5. Musik machen mit Assembler

 

Bis jetzt können Sie Musik nur mit BASIC und vielen, vielen POKE-Befehlen programmieren. Ihre Noten müssen Sie aus separaten DATA-Zeilen auslesen. Mit dem „Saver“ können Sie Ihre Maschinenroutinen und Noten in einer schnellen Weise nachladen, allerdings zerfällt dann Ihre Anwendung oder Ihr Spiel in viele kleine Dateien, und kann unter Umständen nur als ganze Diskette weitergegeben werden. Irgendwann wird die Sache trotzdem zu langsam, besonders, wenn Sie ein Musikstück mit mehreren Stimmen flüssig abspielen wollen - hier stößt BASIC an die Grenzen des Machbaren. In diesem Kapitel geht es vor Allem um Interrupt-Programmierung, und zwar in der Weise, dass Ihr Songplayer an den Timer-Interrupt gebunden wird. Dies ist nicht sehr schwierig, Sie müssen aber trotzdem die richtigen Programmiertechniken beherrschen.

 

5.1 Der Timer-Interrupt

 

Wenn das Kernal korrekt läuft, dann gibt die Uhr den Takt an. Diese wird 60-mal in der Sekunde aktualisiert, und zwar über Unterprogramme, die der Kernal auch periodisch aufruft. Bei den Kernal-Funktionen in Kapitel 4 haben Sie eine dieser Funktionen schon kennengelernt, nämlich UDTIM (update timer). Es geht hier aber nicht um UDTIM, sondern darum, dass normalerweise 60-mal in der Sekunde ein Interrupt ausgelöst wird, der den C64 dazu veranlasst, periodisch einige Arbeiten auszuführen. Z.B. wird dann die Tastatur abgefragt, und eventuell wird dann auch der Tastaturpuffer mit einem neuen Zeichen beschrieben. Außerdem wird die serielle Schnittstelle oder der Commodore-Bus abgefragt, und wenn dies nötig ist, werden auch die zugehörigen Dateien aktualisiert. Aber auch dies ist an dieser Stelle nicht so wichtig, denn die meisten Dinge, die sich mit der Ein- und Ausgabe von Daten befassen, laufen automatisch ab. Wichtig ist jedoch, dass die Arbeiten, die normalerweise bei einem Timer-Interrupt ausgelöst werden, dem folgenden Befehl entsprechen:

 

JMP $EA31

 

Aber wer oder was führt diesen JMP-Befehl aus? Die Antwort ist, dass auch auf die Adresse $EA31 ein Zeiger verweist, und dieser steht als Lo- und Hi-Byte in den Adressen 788 und 789. $EA31 entspricht nun den Dezimalzahlen 49 und 234, und genau diese Zahlen stehen auch direkt nach dem Einschalten in den Adressen 788 und 789. Denken Sie an dieser Stelle bitte daran, dass indirekte Sprünge erlaubt sind, und dass deshalb das Kommando

 

JMP (788)

 

erlaubt ist. Aber was haben Sie nun davon? Ganz einfach: Wenn Sie den Zeiger in den Adressen 788 und 789 „umbiegen“, dann können Sie dem C64 eigene Interrupt-Routinen unterschieben, die dann auch alle 1/60 Sekunde ausgeführt werden. Das Einzige, das Sie an dieser Stelle beachten müssen, sind die folgenden Dinge:

 

·       Ihre eigene Routine darf zusammen mit den danach noch anstehenden Aufräumarbeiten nicht länger als 1/60 Sekunde dauern

·       Ihre eigene Interrupt-Routine darf nicht mit RTS abgeschlossen werden, sondern muss mit JMP $EA31 enden

 

Ein sehr einfaches Programm, das nur das erste Zeichen auf dem Bildschirm alle 1/60 Sekunde um 1 erhöht, wäre das folgende:

 

10    -                  .BA 49152

20    -                  INC 1024

30    -                  JMP $EA31

 

Aber wie erreichen Sie nun, dass Ihre Interrupt-Routine auch periodisch ausgeführt wird? Vielleicht mit folgendem Kommando?

 

POKE 788,0:POKE 789,192

 

Leider geht dies schief, denn BASIC ist viel zu langsam, um den Zeiger in den Adressen 788 und 789 zu ändern, noch bevor ein neuer Timer-Interrupt ausgelöst wird. Die letzte BASIC-Zeile führt also zum Absturz des Systems. Sie können natürlich Ihre Routine so umschreiben, dass Sie nur das Hi-Byte verändern müssen:

 

10    -                  .BA 49201

20    -                  INC 1024

30    -                  JMP $EA31

RUN

POKE 789,192

 

Wenn sich nun Ihr Zeichen an der Adresse 1024 automatisch dauernd ändert, dann haben Sie alles richtig gemacht. Leider können Sie Ihre Interrupt-Routine nicht mit SYS 49152 starten bzw. stoppen, sondern müssen stattdessen die folgenden Zeilen benutzen:

 

POKE 789,192:REM ROUTINE AN

POKE 789,234:REM ROUTINE AUS

 

Es geht aber auch einfacher, nämlich mit schnellem Assembler, der die Inhalte der Adressen 788 und 789 so ändert, dass Ihr Timer dies nicht mitbekommt. Dies geht auf die folgende Weise:

 

10    -                  .BA 49152

20    -                  SEI

30    -                  LDA #<(IRQROUTINE)

40    -                  STA 788

50    -                  LDA #>(IRQROUTINE)

60    -                  STA 789

70    -                  CLI

80    -                  RTS

90    -IRQROUTINE        INC 1024

100   -                  JMP $EA31

 

Der Trick ist hier (zumindest bei Hypra-Ass), dass das Lo-Byte bzw. Hi-Byte eines Labels ermittelt werden kann, und dann auch in der korrekten Weise als Immidiate an den Akkumulator weitergereicht werden kann. Und nicht nur das: Mit SEI können Sie während der Veränderungen des Zeigers in den Adressen 788 und 789 zusätzlich verhindern, dass Ihnen die Uhr dabei mit einem neuen Interrupt dazwischenfunkt. Sie dürfen nur nicht vergessen, die Interrupts mit CLI wieder zuzulassen, bevor Sie mit RTS zu BASIC zurückkehren.

 

5.1.1 Songs im Hintergrund abspielen

 

Nun geht es ja um Musik, und nicht um Interrupt-Programmierung. Die Interrupt-Programmierung, die ja im vorigen Kapitel schon für die Sprite-Steuerung benutzt wurde, ist hier deshalb wieder nur ein Werkzeug, um Songs im Hintergrund stimmensynchron abzuspielen, während z.B. BASIC weiterläuft. Aber wie genau können Sie Ihre Noten der einzelnen Stimmen verwalten? Ganz einfach: Sie benutzen für jede der 3 SID-Stimmen ein 2-Byte-Tupel. Das erste Byte enthält einfach einen Zähler, der bei jedem Aufruf Ihrer Interrupt-Routine um 1 erhöht wird (mit INC), das zweite Byte enthält dann einen Wert, der angibt, wie groß Ihr Zähler maximal sein darf. Wenn dann Ihr Zähler über diesem Wert liegt, dann wird Ihr Zähler auf 0 zurückgesetzt, und die nächste Note für Ihre Stimme wird aus einem Array eingelesen, das die Notenwerte und die dazugehörigen Längen enthält. Nun benötigen wir noch einige Zeiger, um mit dem Songplayer auch korrekt arbeiten zu können. Hierfür belege ich nun die folgenden Speicheradressen in der folgenden Weise:

 

·       900/901: Zeiger auf die aktuelle Note des Noten-Arrays von SID-Stimme 0

·       902: Aktueller Zeitzähler für SID-Stimme 0

·       903: Länge der aktuellen Note für SID-Stimme 0

·       904/905: Frequenzwerte für die SID-Register 0 und 1 für Stimme 0

·       906/907: Zeiger auf den Anfang des Noten-Arrays von SID-Stimme 0 (für Repeat-Funktion)

·       908: Wellenform für Stimme 0

·       909: Frei für Erweiterungen oder zusätzliche Flags für Stimme 0

·       910: Attack/Decay für Stimme 0

·       911: Sustain/Release für Stimme 0

·       912/913: Zeiger auf die aktuelle Note des Noten-Arrays von SID-Stimme 1

·       914: Aktueller Zeitzähler für SID-Stimme 1

·       915: Länge der aktuellen Note für SID-Stimme 1 in 1/60 Sekunden

·       916/917: Frequenzwerte für die SID-Register 7 und 8 für Stimme 1

·       918/919: Zeiger auf den Anfang des Noten-Arrays von SID-Stimme 1 (für Repeat-Funktion)

·       920: Wellenform für Stimme 1

·       921: Frei für Erweiterungen oder zusätzliche Flags für Stimme 1

·       922: Attack/Decay für Stimme 1

·       923: Sustain/Release für Stimme 1

·       924/925: Zeiger auf die aktuelle Note des Noten-Arrays von SID-Stimme 2

·       926: Zeitzähler für SID-Stimme 2

·       927: Länge der aktuellen Note für SID-Stimme 2

·       928/929: Frequenzwerte für die SID-Register 14 und 15 für Stimme 2

·       930/931: Zeiger auf den Anfang des Noten-Arrays von SID-Stimme 2 (für Repeat-Funktion)

·       932: Wellenform für Stimme 2

·       933: Frei für Erweiterungen oder zusätzliche Flags für Stimme 2

·       934: Attack/Decay für Stimme 2

·       935: Sustain/Release für Stimme 2

 

Ich habe hier bewusst dafür gesorgt, dass die Größe der Datenstruktur für die einzelnen Stimmen ein Vielfaches von 2 ist, denn in diesem Fall können an vielen Stellen für die Zeigeroperationen einfache ASL-Befehle anstatt eines aufwendigen Multiplikationsverfahrens benutzt werden.  Außerdem enthalten die 12 Bytes großen Datenpakete mehr Bytes, als nötig ist, deshalb ist hier noch genug Platz für spätere Erweiterungen. Ferner ist die Verarbeitung der eigentlichen Daten für jede Stimme identisch, das heißt, dass die Hauptschleife, die Stimme 0 verarbeitet einfach nur dreimal hintereinander ausgeführt werden muss. Setzen wir nun den Songplayer Stück für Stück zusammen. Der Ausgangspunkt ist die folgende Grundstruktur:

 

10                       .EQ SI=54272

20                       .BA 49152

30                       SEI

40                       LDA 999

50                       CMP #0

60                       BEQ INIT

70                       CMP #1

80                       BEQ DEINIT

90                       RTS

10    INIT               LDA #<(PLAYER)

110                      STA 788

120                      LDA #>(PLAYER)

130                      STA 789

140                      LDA #1

150                      STA 999

160                      LDA 910

170                      STA SI+5

180                      LDA 911

190                      STA SI+6

200                      LDA 922

210                      STA SI+12

220                      LDA 923

230                      STA SI+13

240                      LDA 934

250                      STA SI+19

260                      LDA 935

270                      STA SI+20

280                      CLI

290                      RTS

300   DEINIT             LDA #49

310                      STA 788

320                      LDA #234

330                      STA 789

340                      LDA #0

350                      STA 999

360                      CLI

370                      RTS

 

Wenn Sie den C64 einschalten, dann steht normalerweise in den Adressen 900-1023 der Wert 0, also auch in der Adresse 999. Dies ist dann das Zeichen dafür, dass der Songplayer noch nicht initialisiert ist. Deshalb liest das Maschinenprogramm, das Sie mit SYS 49152 aufrufen, zunächst die Adresse 999 aus. Wenn dort der Wert 0 steht, dann wird das Unterprogramm INIT aufgerufen, das den Timer-Interrupt auf den Songplayer umbiegt, und an die Adresse 999 den Wert 1 schreibt. Zusätzlich werden noch die Hüllkurven für die einzelnen Stimmen initialisiert, indem die entsprechenden Daten aus der entsprechenden Datenstruktur ab der Adresse 900 ausgelesen werden.

Wenn allerdings an Adresse 999 schon eine 1 seht, dann wird das Unterprogramm DEINIT aufgerufen, das den alten Timer-Interrupt und den alten Wert in Adresse 999 wiederherstellt. Die Zeiger auf die Notenarrays werden dadurch aber nicht geändert, deshalb können Sie den Songplayer auch beliebig starten, anhalten und (bei Neubeschreiben der Zeiger auf die Notenarrays) auch vor und zurückspulen.

 

Der Songplayer

 

Kommen wir nun zum Songplayer selbst. Dieser ist wie gesagt sehr einfach gestrickt, das heißt, dass hier vorausgesetzt wird, dass sich schon die richtigen Noten im Speicher befinden, und Sie die Lautstärke schon richtig mit SID-Register Nr. 24 eingestellt haben. Auch die Tabelle mit den Frequenzwerten der einzelnen Noten muss sich bereits im Speicher befinden, und zwar an der Adresse 50000. Die Noten werden vom Player also nur „angestoßen“, wie dies etwa bei einem Klavier der Fall ist. Natürlich können Sie auf diese Weise nicht alle Möglichkeiten des SID ausreizen, dafür ist der Player aber sehr schnell programmiert. Nun sollten Sie sich die einfache Variante in Ruhe anschauen, und den Songplayer mit einem Spiel vergleichen, das Sie gerade erst mit dem 1. Level begonnen haben. Schauen Sie sich nun das komplette Listing für den einfachen Songplayer an.

 

14-SONGPLAYER

10                       .EQ SI=54272

20                       .BA 49152

30                       SEI

40                       LDA 999

50                       CMP #0

60                       BEQ INIT

70                       CMP #1

80                       BEQ DEINIT

90                       RTS

10    INIT               LDA #<(PLAYER)

110                      STA 788

120                      LDA #>(PLAYER)

130                      STA 789

140                      LDA #1

150                      STA 999

160                      LDA 910

170                      STA SI+5

180                      LDA 911

190                      STA SI+6

200                      LDA 922

210                      STA SI+12

220                      LDA 923

230                      STA SI+13

240                      LDA 934

250                      STA SI+19

260                      LDA 935

270                      STA SI+20

280                      CLI

290                      RTS

300   DEINIT             LDA #49

310                      STA 788

320                      LDA #234

330                      STA 789

340                      LDA #0

350                      STA 999

360                      CLI

370                      RTS

380   PLAYER             LDA248

390                      PHA

400                      LDA 249

410                      PHA

420                      LDA 250

430                      PHA

440                      LDA 251

450                      PHA

460                      LDA #$00

470                      STA 250

480                      LDA #$D4

490                      STA 251

500                      LDX #0

510   DOVOICE            LDA 903,X

520                      CMP 902,X

530                      BNE TIMECOUNT

540                      LDA 900,X

550                      STA 248

560                      LDA 901,X

570                      STA 249

580                      LDA #0

590                      STA 902,X

600                      LDY #1

610                      LDA (248),Y

620                      CMP #$FF

630                      BNE NOREPEAT

640                      LDA 906,X

650                      STA 900,X

660                      LDA 907,X

670                      STA 901,X

680                      LDA 900,X

690                      STA 248

700                      LDA 901,X

710                      STA 249

720                      LDY #1

730                      LDA (248),Y

740   NOREPEAT           STA 903,X

750                      LDY #0

760                      LDA (248),Y

770                      ASL

780                      TAY

790                      LDA 50000,Y

800                      STA 904,X

810                      LDA 50001,Y

820                      STA 905,X

830                      LDY #0

840                      LDA 904,X

850                      STA (250),Y

860                      LDY #1

870                      LDA 905,X

880                      STA (250),Y

890                      LDY #4

900                      LDA 908,X

910                      STA (250),Y

920                      ORA #1

930                      STA (250),Y

940                      CLC

950                      LDA 900,X

960                      ADC #2

970                      STA 900,X

980                      LDA 901,X

990                      ADC #0

1000                     STA 901,X

1010                     JMP NEXTVOICE

1020  TIMECOUNT          INC 902,X

1030  NEXTVOICE          CLC

1040                     TXA

1050                     ADC #12

1060                     TAX

1070                     CLC

1080                     LDA 250

1090                     ADC #7

1100                     STA 250

1110                     CPX #36

1120                     BEQ EXIT

1130                     JMP DOVOICE

1140  EXIT               PLA

1150                     STA 251

1160                     PLA

1170                     STA 250

1180                     PLA

1190                     STA 249

1200                     PLA

1210                     STA 248

1220                     JMP $EA31

 

Zunächst müssen in Zeile 380 - 450 die Inhalte der Speicheradressen 248-251 auf dem Stack abgelegt werden. Dies ist wichtig, da innerhalb einer Interrupt-Routine der Inhalt der Zeropage nicht verändert werden darf.

Die Adressen 248 und 249 werden nun vom Song-Player dazu benutzt, auf das Noten-Array der einzelnen Stimmen per indirekter Adressierung zuzugreifen, die Adressen 250 und 251 enthalten die Startadresse der SID-Register. Die eigentliche Arbeit leistet aber nicht die Zeigerinitialisierung in Zeile 380 - 500, sondern die Routine DOVOICE, die in Zeile 510 beginnt. DOVOICE liest nun zunächst den Teil der Datenstruktur in den Adressen 900-936 für die aktuelle Stimme aus, für die der Address-Offset im X-Register steht. Die Datenstruktur selbst startet an der Adresse 900, und DOVOICE muss nun zunächst den Zeitzählerstand für die aktuelle Stimme aus der Adresse 903,X auslesen, die maximal erreicht werden darf. In Zeile 520 wird dieser Zeitzählerstand nun mit dem Zeitzählerstand verglichen, der aktuell vorliegt. Ist dieser Zählerstand noch nicht beim maximalen Wert angekommen, springt der bedingte Sprungbefehl in Zeile 530 zum Label TIMECOUNT- das Einlesen der nächsten Note aus dem Notenarray der aktuellen Stimme wird dadurch übersprungen. Wenn allerdings der Zähler in der Adresse 902,X den maximalen Wert erreicht hat, dann muss eine neue Note ausgewählt und angeschlagen werden.

Für das Abspielen einer neuen Note muss zunächst der aktuelle Notenzeiger aus den Adressen 900,X und 901,X in die Adressen 248 und 249 kopiert werden. Dies ist nötig, weil die Y-indizierte indirekte Adressierung nur mit Zeropage-Zeigern funktioniert. Außerdem muss in Zeile 580 und 590 noch der aktuelle Zeitzähler auf 0 gesetzt werden (Adresse 902,X). Die Notenlänge, die in die Adresse 903,X geschrieben werden soll, steht an der 2. Stelle des entsprechenden Wertepaares im Noten-Array , und wird in den Zeilen 600 und 610 erst einmal in den Akkumulator geladen.

An dieser Stelle habe ich nun eine Funktion eingefügt, die sehr oft in Spielen benutzt wird: Wenn die Längenangabe den Wert $FF enthält, dann wird die entsprechende Stimme von vorn wiederholt. Auf diese Weise können Sie einen Song in einfacher Weise endlos abspielen. Normalerweise springt der bedingte Sprungbefehl in Zeile 630 zum Label NOREPEAT, und die Stimme wird nicht von vorn gestartet. Wenn allerdings die Längenangabe den Wert 255 hat, werden in Zeile 640 - 730 die Werte von den Adressen 906,X und 907,X in die Adressen 900,X und 901,X kopiert. Dadurch wird der Zeiger für die aktuelle Note wieder durch den Zeiger auf den Beginn des Notenarrays für die aktuelle Stimme zurückgesetzt. Natürlich wird dann auch die aktuelle Notenlänge neu ermittelt.

Auf jeden Fall landet die aktuelle Notenlänge immer in der Adresse 903,X. Die Note selbst dagegen befindet sich immer im Notenarray vor der Längenangabe, und ist die erste Zahl des Notenpaars {Note,Länge}. Die Note selbst wird in den Zeilen 750 und 760 eingelesen. In der Notenfrequenztabelle ab Adresse 50000 befinden sich nun stets 2 Werte, und zwar in der Form, wie sie auch in das entsprechende SID-Frequenzregister geschrieben werden muss. Deshalb muss hier der Akkumulator-Inhalt zunächst um 1 Bit nach links verschoben werden, was einer Multiplikation mit 2 entspricht. Da im X-Register schon eine wichtige Information steht (nämlich der Zeiger auf die aktuelle Stimme in der Datenstruktur ab Adresse 900), wird nun das Y-Register benutzt, um den korrekten Frequenzwert aus der Notentabelle auszuwählen. Deshalb ist dann auch in Zeile 780 ein TAY-Befehl nötig, der den Inhalt des Akkumulators in das Y-Register kopiert.

In den Zeilen 790 - 820 werden nun die korrekten Werte, die für die SID-Register benutzt werden sollen, in den Adressen 904,X und 905,X zwischengespeichert. Anschließend werden mit diesen Werten in Zeile 830 - 930 die SID-Register für die aktuelle Stimme in der richtigen Weise befüllt. Dies geschieht wie folgt: Zunächst wird die Frequenz ausgewählt. Anschließend wird das Gate-Bit für die aktuelle Stimme zuerst gelöscht, und anschließend wieder gesetzt. Leider können Sie die SID-Register nicht auslesen, sondern nur beschreiben. Genau hierfür benötigt man dann auch den korrekten Wellenform-Wert in der Adresse 908,X: Dieser wird zunächst ohne Gate-Bit, und anschließend mit Gate-Bit (ORA-Befehl in Zeile 920) in das entsprechende SID-Register geschrieben, das die Wellenform auswählt. Am Ende muss dann natürlich noch der Zeiger in Adresse 900,X und 901,X um 2 erhöht werden, damit dieser auf die nächste Note im Noten-Array für die aktuelle Stimme zeigt. Am Ende landen Sie dann nach der Auswahl und dem „Anstoßen“ der Note beim Label NEXTVOICE in Zeile 1030. Die Routine NEXTVOICE erhöht den Zeiger in den Adressen 250 und 250 um 7 und wählt dadurch den Bereich der SID-Register für die nächste Stimme aus. Vorher wird aber noch das X-Register um 12 erhöht- was aber nur durch einen Umweg über den Akkumulator möglich ist. Anschließend landen Sie dann wieder bei DOVIOICE, aber nur dann, wenn im X-Register nicht der Wert 36 steht. Wenn dies dennoch der Fall ist, dann verlassen Sie durch den Befehl JMP EXIT die IRQ-Player-Routine. In diesem Fall ist ein separater Label nötig, um vor der Rückkehr zu BASIC den veränderten Inhalt der Adressen 248-251 wiederherzustellen und den tack aufzuräumen.

Wenn Sie jedoch zuvor keine neue Note ausgewählt haben, dann landen Sie in Zeile 1020 beim Label TIMECOUNT. Im Endeffekt wird hier nur der aktuelle Zeitzähler in der Adresse 902,X um 1 erhöht, und anschließend springen Sie zurück zu DOVOICE.

 

5.1.2 Noten für den Songplayer erstellen

 

Der Songplayer ist einfach gehalten, soll aber trotzdem flexibel sein. Dies hat leider den Nachteil, dass die Tabelle mit den Notenfrequenzen an der Adresse 50000 nicht fest vorgegeben ist, und erst einmal erstellt werden muss. So schwer ist dies aber auch wieder nicht, denn Sie können dazu folgendes BASIC-Programm verwenden:

 

15-NOTETAB

10 PRINT"[SHIFT+CLR/HOME]BITTE WARTEN..."

20 FOR I=1 TO 80

30 F=16*(INT(27.5*2^(I/12)))

40 F=F*0.9825:REM PAL-MASCHINE

50 HI=INT(F/256):LO=F-(256*HI)

60 POKE 50000+(2*I),LO:POKE 50001+(2*I)+1,HI

90 NEXT I

 

In dem letzten Beispiel-Listing ist der tiefste Ton der Ton A0 mit einer Frequenz von 27,5 HZ. Um eine Note, die I Halbtonschritte über A0 liegt, in eine Frequenz umzurechnen, wird die entsprechende Formel benutzt:

 

F=27,5*2(I/12)

 

Wenn der 6510-Prozessor mit genau 1 MHz laufen würde, dann wären die in die SID-Register einzutragenden Frequenzwerte genau 1 Schritt pro 1/16 Hz, und die Formel in Zeile 30 wäre auch so korrekt. Da aber z.B. auf meiner PAL-Maschine der 6510 nur mit 982,5 kHz läuft, muss der Wert F, den die Formel in Zeile 30 liefert, in Zeile 40 noch einmal mit dem Korrekturfaktor 0,9825 multipliziert werden. Zeile 50 und 60 machen nun nichts anderes, als F in ein Lo- und ein Hi-Byte zu zerlegen und in dieser Reihenfolge in die Frequenztabelle ab Adresse 50000 einzutragen. Das letzte Beispiel benutzt 80 mögliche Notenwerte (Wert 1 bis 80), der Wert 0 ist für Pausen reserviert.

 

Nun fehlt natürlich noch die wichtigste Sache, nämlich die Noten selbst. An dieser Stelle ist nun guter Rat teuer, denn woher wollen Sie Noten nehmen? Die einfachste Variante ist die, sich einfach Notenbücher in einem Musikfachgeschäft Ihrer Wahl zu besorgen, und diese Note für Note per Hand in entsprechende Zahlen für den Songplayer umzusetzen. Am Ende haben Sie dann nach mehren Tagen ein BASIC-Programm mit zahlreichen DATA-Zeilen, die Sie irgendwann (wenn alles einwandfrei läuft) mit dem „Saver“ in eine Datei auslagern können. In der Tat sind die ersten Spieleentwickler, die auch meist gute Musiker waren, so vorgegangen, und haben auch sehr erstaunliche Resultate erzielt. Wenn Sie z.B. das Spiel „trolls and tribulations“ kennen, dann wissen Sie wahrscheinlich, dass Sie mit sehr einfachen Möglichkeiten beeindruckende Mozart-Stücke in Ihr Spiel einbauen können.

Zugegebenermaßen kann ich Ihnen an dieser Stelle auch keine einfache Lösung bieten, denn ich bin hauptberuflich Buchautor, und kann nicht meine gesamte Zeit am C64 verbringen. Auch, wenn ich mich mit Musik auskenne, weil ich 10 Jahre lang Bands abgemischt habe, kann ich Ihnen kein Patentrezept für gute C64-Songs anbieten. Deshalb ist auch mein Noteneditor, an dem ich von Zeit zu Zeit arbeite, noch lange nicht fertig- ich werde diesen aber in den Download-Bereich stellen, sobald ich sämtliche Bugs, von denen es leider noch zahlreiche gibt, entfernt habe. Wie Sie sehen, ist das Musikmachen auch auf dem C64 keine einfache Sache, denn Musik ist leider das Komplexeste, das der Mensch neben der Mathematik erfunden hat. Und nicht nur das: Musik und Mathematik haben sehr viele Berührungspunkte, deshalb können viele Bereiche der Musik auch mathematisch beschrieben werden.