ROM-Routinen in eigenen Programmen
Das Rad ist schon erfunden! Ähnlich verhält es sich mit verschiedenen Routinen, die ein Assembler-Programmierer immer wieder benötigt. Aber warum soll man sich die Arbeit des Programmierens machen, wenn das Betriebssystem viele ständig benötigte Routinen schon enthält und man nur noch zu wissen braucht, ab welcher Adresse sie stehen?
Angenommen, Sie möchten in Assembler einige komplexe Dinge programmieren wie beispielsweise eine neue mathematische Funktion (wie wäre es mit dem Kotangens) und diese auf dem Bildschirm ausgeben. Das ist eine große Aufgabe, zu der zunächst einmal die Übernahme des Arguments in das Maschinenprogramm, dann einige Fließkomma-Rechenoperationen und schließlich die Ausgabe auf dem Bildschirm geschrieben werden müßten, wenn da nicht schon fast alles an verborgener Stelle als fertige Programm-Module im Computer vorhanden wäre!
Sowohl im unteren (von $A000 bis $BFFF) als auch im oberen ROM-Bereich (von $E000 bis $FFFF) liegt die Firmware fest verschachtelt vor. Der untere ROM-Abschnitt wird manchmal auch Basic-Interpreter, der obere ROM-Bereich Betriebssystem genannt, wobei diese Einteilung aber den Kern der Sache nicht genau trifft, denn Interpreter, Editor und Betriebssystem führen ein gemischtes Dasein quer durch alle genannten ROM-Bereiche hindurch.
Mindestens fünf Informationen braucht ein Assembler-Programmierer, wenn er das breite Programmangebot des ROMs nutzen möchte:
- Einsprungadresse
- Format der Eingabeparameter
- Adressen der Eingabeparameter
- Adressen der Ausgabeparameter
- Format der Ausgabeparameter
Nicht alle Routinen, die man benutzen kann, erfordern alle fünf Informationen, manche weniger, einige auch mehr und schließlich gibt es noch Programmroutinen, die noch den Aufruf einer oder sogar mehrerer anderer Routinen nötig machen.
In der beigefügten Tabelle sind - nach Anwendungen sortiert - die wichtigsten Firmware-Möglichkeiten mit den erforderlichen Ein- und Ausgabeparametern aufgeführt. Das sind natürlich beileibenichtalle. Die Auswahl erfolgte subjektiv! Es sind einfach diejenigen, die mir bislang am häufigsten untergekommen sind. Außerdem wurde auf die Kernel-Routinen verzichtet: Man findet diese sehr gut dokumentiert bereits in einer Reihe von Büchern und im Assembler-Kurs.
Die Tabelle nennt den Label-Namen, die Einsprungadresse und gibt eine Kurzbeschreibung der Funktion. Das Ein- und auch das Ausgabeformat ist ebenso angegeben wie auch die Adressen, an denen diese Parameter übergeben werden. Die verwendeten Bezeichnungen halten sich eng an die im Assembler-Kurs kennengelernten. Sie sind allgemein üblich:
| FAC | Fließkomma-Akku 1 |
| ARG | Fließkomma-Akku 2 |
| A | Akkumulator |
| X,Y | X-, Y-Register |
| A/Y | 2-Byte-Angabe im Format LSB/MSB im Akku/Y-Register |
| FLPT | Fließkommazahl im Normalformat |
| MFLPT | gepacktes Fließkommaformat |
Damit das alles nicht so trocken abläuft, soll noch ein kleines Beispiel vorgestellt werden! Die oben schon erwähnte Kotangens-Funktion wird in einem Maschinenprogramm erzeugt, das durch USR anzuspringen ist. In Bild 1 finden Sie ein Flußdiagramm zu dem Programm, welches hier als Hypra-Ass-Listing abgebildet ist (Listing 1). Ein kurzes Testprogramm liefert Listing 2.

Der Einsprung mittels USR bietet den Vorteil, daß der Übergabewert gleich im FLPT-Fbrmat im FAC »landet«. Es ist aber sinnvoll, den Übergabeparameter mittels der MOVMF-Routine zu »retten«, weil durch die Kosinus-Funktion der FAC verändert wird. Wenn auch das Ergebnis der Kosinus-Funktion mittels MOVMF beiseite gelegt wurde, holen wir durch MOVFM den Anfangswert wieder in den FAC und bilden mittels SIN den Sinus davon. Schließlich teilen wir den im Speicher stehenden Kosinuswert durch den im FAC befindlichen Sinuswert (unter Verwendung von FDIV). Das Ergebnis ist der Kotangens:
COTX = (COSX/SIN X)
Dieser Wert befindet sich nun im FAC und wird mit dem RTS an das Basic-Programm zurückgeliefert. Im Testprogramm weisen wir ihm dann die Variable E zu.
Dieses kurze Beispiel soll Ihnen den Mund wässrig machen. Sehr viel detaillierter werden die ROM-Routinen im Kurs »Von Basic zu Assembler« im 64’er behandelt werden.
(Heino Ponnath/hm)Literatur:
1. Kassera/Kassera, Programmieren in Maschinensprache, München 1985: Markt & Technik Verlag, MT 830
2. West, C64 Computerhandbuch, München 1984, Te-wi
3. Babel/Krause/Dripke, Das Interface Age Systemhandbuch zum C 64, München 1983: Interface Age Verlag
4. Ponnath, C 64 Wunderland der Grafik, München 1985: Markt & Technik Verlag MT 756.
1. Routinen, die die Kooperation von Basic und Assembler erleichtern: |
||||||
| Label | Adresse | Funktion | Eingabe | Ausgabe | ||
| Format | Adresse | Format | Adresse | |||
| CHRGET | 0073 | Holt nächstes Byte | 1 Byte | Basic-Text | 1 Byte | A |
| CHRGOT | 0073 | Holt aktuelles Byte | 1 Byte | Basic-Text | 1 Byte | A |
| READY | A474 | Erzeugt READY-Status | - | - | - | - |
| LINGET | A96B | Holt Integerwert (0-63999) |
ASCII-Zahl | Basic-Text | 2-Byte Integer | 14/15 |
| FRMNUM | AD8A | Holt beliebigen nume- rischen Ausdruck |
Basic- Ausdruck |
Basic-Text | FLPT | FAC |
| FRMEVL | AD9E | Holt beliebigen Ausdruck |
Basic- Ausdruck |
Basic-Text | a) bei Fließkomma: | |
| FLPT | FAC | |||||
| b) bei Integer: | ||||||
| FLPT | FAC | |||||
| c) bei String: | ||||||
| Zeiger auf | FAC+3, | |||||
| Descriptor | FAC+4 | |||||
|
Diese Routine setzt außerdem eine Reihe von Flaggen: VALTYP($0D) 0=Zahl FF=String INTFLAG ($0E) 0=Fließkomma 80=integer War Ausdruck einfache Variable, dann zeigt VARNAM ($45/6) das 1. Byte des Variablen-Namens |
||||||
| CHKCLS | AEF7 | Prüft auf » ) « | ASCII | Basic-Text | - | - |
| CHKOPN | AEFA | Prüft auf » ( « | ASCII | Basic-Text | - | - |
| CHKCOM | AEFD | Prüft auf »,« | ASCII | Basic-Text | - | - |
| SYNCHR | AEFF | Prüft auf Zeichen im Akkumulator |
ASCII | Basic-Text A |
- | - |
|
Diese 4 Routinen überlesen das Zeichen, wenn vorhanden. Wenn nicht vorhanden, folgt SYNTAX ERROR |
||||||
| ISVAR | AF28 | Sucht Variablenwert | Name + Kennung |
$45/46 | a) Zahl: | |
| FLPT | FAC | |||||
| b) String: | ||||||
| Descriptor-FAC+3 | ||||||
| ORDVAR | B0E7 | Sucht Variablennamen | Name+ Kennung |
$45/46 | Adresse | $47/48 |
| GTBYTC | B79B | Holt Zahl (0-255) | ASCII | Basic-Text | 1 Byte | X |
| GETNUM | B7EB | Liest 2 Integerzahlen (Trennung durch Komma) 1. Zahl: 0 bis 65535 2. Zahl: 0 bis 255 |
ASCII | Basic-Text | 2Byte-Int. 1Byte-Int. |
$14/15 X |
| COMBYT | E200 | Prüft auf »,« und holt folgende Zahl |
ASCII | Basic-Text | 1 Byte | X |
2. Routinen, die die Verschiebungen im Speicher durchführen: |
||||||
| BLTUC | A3BF | Verschiebt Blöcke | Adressen: | |||
| Quelle | ||||||
| Start | $5F/60 | |||||
| Ende+1 | $5A/5B | |||||
| Ziel | ||||||
| Ende+1 | $58/59 | - | - | |||
| PUTINT | A9C4 | Schiebt FAC als Integer | FLPT | FAC | 2Byte- | angegebene |
| in Variable | Adresse | $49/50 | Integer | Variable | ||
| PTFLPT | A9D6 | Schiebt FAC | FLPT | FAC | MFLPT | angegebene |
| in Variable | Adresse | $49/50 | Variable | |||
| GETSPT | AA2C | Schiebt String- | Zeiger | FAC+3 | Descriptor | angegebene |
| descriptor in Variable | Adresse | $49/50 | Variable | |||
| STRVAL | B7B5 | Zahlenstring in | ASCII | ab $22 | FLPT | FAC |
| FAC einlesen | Länge | A | ||||
| CONUPK | BA8C | Lädt ARG aus Speicher | MFLPT | A/Y | FLPT | ARG |
| MOVFM | BBA2 | Lädt FAC aus Speicher | MFLPT | A/Y | FLPT | ARG |
| MOVMF | BBD4 | Schiebt FAC | FLPT | MFLPT | ange- | |
| in Speicher | Adresse | FAC X/Y | gebener | |||
| Speicher | ||||||
| MOVFA | BBFC | ARG in FAC kopieren | FLPT | ARG | FLPT | FAC |
| MOVAF | BCOC | FAC in ARG kopieren | FLPT | FAC | FLPT | ARG |
| ACTOFC | BC3C | Akku in FAC schieben | 1Byte | A | FLPT | FAC |
3. Routinen zur Arithmetik:
| ASCADD | AA27 | Addiert ASCII-Ziffer zu FAC |
ASCII Ziffer |
A | FLPT | FAC |
| OROP | AFE6 | FAC=(FAC)OR(ARG) | FLPT | FAC,ARG | FLPT | FAC |
| ANDOP | AFE9 | FAC=(FAC)AND(ARG) | FLPT 0 |
FAC,ARG Y |
FLPT | FAC |
| FACINX | B1AA | FAC wird als Integer in A/Y abgelegt |
FLPT | FAC | 2Byte- Integer |
A/Y |
| UMULT | B357 | 16-Bit-Multiplikation | 2-Byte-Integer Zahl1 Zahl2 |
$28/29 $71/72 |
2Byte- Integer |
X/Y |
| CIVAYF | B391 | Integer (-32768 bis 32767) in FAC |
2Byte- Integer |
A/Y | FLPT | FAC |
| SGNFT | B3A2 | Integer (0 bis 255) in FAC |
1Byte | y | FLPT | FAC |
| GETADR | B7F7 | Wandelt FAC zu Integer (0-65535) |
FLPT | FAC | 2Byte- Integer |
Y/A +$14/15 |
| FADDH | B849 | FAC = FAC + 0,5 | FLPT | FAC | FLPT | FAC |
| FSUB | B850 | FAC=Speicherzahl-FAC | MFLPT | Zeiger A/Y | FLPT | FAC |
| FSUBT | B853 | FAC = ARG - FAC | FLPT | FAC | FLPT | FAC |
| FADD | B867 | FAC=Speicherzahl +FAC | MFLPT MFLPT |
Zeiger A/Y FAC |
FLPT | FAC |
| FADDT | B86A | FAC = ARG + FAC | FLPT | ARG,FAC | FLPT | FAC |
| COMPLT | B947 | Erzeugt Zweierkomplement von FAC | FLPT | FAC | FLPT | FAC |
| LOG | B9EA | FAC = In(FAC) | FLPT | FAC | FLPT | FAC |
| FMULT | BA28 | FAC=Speicherwert*FAC | MFLPT | Zeiger A/Y | FLPT | FAC |
| FLPT | FAC | |||||
| FMULTT | BA30 | FAC = ARG * FAC | FLPT | ARG,FAC | FLPT | FAC |
| MUL10 | BAE2 | FAC = 10 * FAC | FLPT | FAC | FLPT | FAC |
| DIV10 | BAFE | FAC = FAC/10 | FLPT | FAC | FLPT | FAC |
| FDIVF | BB07 | FAC=ARG/Speicherzahl | MFLPT | Zeiger A/Y | FLPT | FAC |
| FLPT | ARG | |||||
| FDIV | BB0F | FAC=Speicherzahl/FAC | MFLPT | Zeiger A/Y | FLPT | FAC |
| FLPT | FAC | |||||
| FDIVT | BB14 | FAC = ARG/FAC | FLPT | FAC,ARG | FLPT | FAC |
| SIGN | BC28 | Ermittelt Vorzeichen von FAC |
FLPT | FAC | 1Byte | A 1 ← + 0 → 0 FF ← - |
| ABS | BC58 | FAC = ABS(FAC) | FLPT | FAC | FLPT | FAC |
| FCOMP | BC5B | Vergleicht FAC mit Speicherzahl |
MFLPT | Zeiger A/Y | 1Byte: | A 1: FAC > Speicher 0: FAC = Speicher FF: FAC < Speicher |
| FLPT | FAC | |||||
| INT | BCCC | FAC = INT(FAC) | FLPT | FAC | FLPT | FAC |
| AADD | BD7E | Addiert A zu FAC | FLPT | FAC | FLPT | FAC |
| 1Byte | A | |||||
| SQR | BF71 | FAC = SQR(FAC) | FLPT | FAC | FLPT | FAC |
| MPOT | BF78 | FAC=Speicherwert ↑ FAC |
FLPT | FAC | FLPT | FAC |
| MFLPT | Zeiger A/Y | |||||
| FPWRT | BF7B | FAC = ARG ↑ FAC | FLPT | ARG,FAC | FLPT | FAC |
| NEGOP | BFB4 | FAC = -FAC | FLPT | FAC | FLPT | FAC |
| EXP | BFED | FAC = e↑FAC | FLPT | FAC | FLPT | FAC |
| POLYX | E059 | Polynomberechnung FAC=a0+a1x+a2x²+... |
Adresse | Zeiger A/Y | FLPT | FAC |
|
Zeiger weist auf Start der Konstantentabelle. 1. Byte = Polynomgrad Weitere Bytes sind die Koeffizienten des Polynoms in der Reihenfolge an,.....,a0 im MFLPT-Format. |
||||||
| COS | E264 | FAC = COS(FAC) | FLPT | FAC | FLPT | FAC |
| SIN | E26B | FAC = SIN(FAC) | FLPT | FAC | FLPT | FAC |
| TAN | E2B4 | FAC = TAN(FAC) | FLPT | FAC | FLPT | FAC |
| ATN | E30E | FAC = ATN(FAC) | FLPT | FAC | FLPT | FAC |
4. Auswahl von Ein-/Ausgabe-Routinen:
| ERROR | A437 | Fehlermeldung ausgeben und READY | Fehlernummer | X | ASCII | Bildschirm |
| LIST | A69C | Listet Basic-Programm | - | - | - | - |
| NUMDON | AABC | Druckt FAC auf Bildschirm aus | FLPT | FAC | ASCII | Bildschirm |
| STROUT | AB1E | Gibt String auf Bildschirm aus. Ende=0 | Adresse | Zeiger A/Y | ASCII | Bildschirm |
| SYNERR | AF08 | Ausgabe SYNTAX ERROR | - | - | ASCII | Bildschirm |
| OVERR | B97E | Ausgabe OVERFLOW ERR. | - | - | ASCII | Bildschirm |
| LINPRT | BDCD | Druckt Integerzahl (0 bis 65535) aus. | 2Byte-Integer | X/A | ASCII | Bildschirm |
| FACOUT | BDD7 | Druckt FAC auf Bildschirm aus | FLPT | FAC | ASCII | Bildschirm |
| FOUT | BDDD | FAC wird zu ASCII-String (Ende=0). Kann direkt mit STROUT ausgegeben werden. | FLPT | FAC | ASCII (Ende=0) Startadr. |
ab $100 A/Y |
| SAVET | E156 | Save | Parameter aus Basic-Text | |||
| VERFYT | E165 | Verify | Parameter aus Basic-Text | |||
| LOADT | E168 | Load | Parameter aus Basic-Text | |||
| SLPARA | E1D4 | Holt Parameter für Save, Verify, Load aus dem Basic-Text | ||||
| PLOTK | E50A | Setzt Cursorposition | Zeile Spalte |
X Y |
||
| HOME | E566 | Cursor in Home-Position | ||||
| PLOTR | E56C | Setzt Cursor-Position | Zeile Spalte |
$D6 $D3 |
||
| GETKBC | E5B4 | Holt Zeichen aus Tastaturpuffer | - | - | 1Byte | A |
| PRT | E716 | Gibt Zeichen in A auf Bildschirm aus | 1Byte | A | ASCII | Bildschirm |
| CLRLN | E9FF | Löscht xte Bildschirmzeile | Zeilennummer | X | - | - |
10 rem***test fuer cotangens*** 20 poke785,0:poke786,96:rem usr-vektor 30 input"winkel";w:w=w*~/180:rem auf bogenmass 40 e=usr(w):rem aufruf des programmes 50 printw,e:rem ergebnis in e 60 end
10 - .LI 1,4 20 - .BA $6000 30 -;******************************** 40 -;* * 50 -;* BEISPIELPROGRAMM ZU DEN * 60 -;* R O M - ROUTINEN * 70 -;* * 80 -;* COTANGENS-FUNKTION * 90 -;* HEIMO PONNATH HAMBURG 1985 * 100 -;* * 110 -;******************************** 120 -; 130 -;EINSPRUNG MITTELS USR 140 -;ZUVOR USR-VEKTOR EINSTELLEN! 150 -; 160 - .EQ COS=$E264 165 - .EQ MOVFM=$BBA2 170 - .EQ MOVMF=$BBD4 180 - .EQ SIN=$E26B 190 - .EQ FDIV=$BB0F 200 - .EQ WERT=$7000 205 - .EQ WERT1=$7010 210 -; 212 -START LDX #<(WERT1) 214 - LDY #>(WERT1) 216 - JSR MOVMF 220 - JSR COS 230 - LDX #<(WERT) 240 - LDY #>(WERT) 250 - JSR MOVMF 252 - LDA #<(WERT1) 254 - LDY #>(WERT1) 256 - JSR MOVFM 260 - JSR SIN 270 - LDA #<(WERT) 280 - LDY #>(WERT) 290 - JSR FDIV 300 - RTS 310 -; 320 - .SY 1,4 330 - .ST