Ohne gutes Werkzeug geht es nicht: SMON – Teil 1
In mehreren Teilen möchten wir Ihnen einen Maschinensprachmonitor vorstellen. Parallel zum Kursus über Assembler-Programmierung wird Schritt für Schritt ein Programm entstehen, das sich durchaus mit kommerziellen Monitoren messen kann.
Ich kann mich noch gut an unsere ersten Schritte in die Maschinensprache erinnern. Ausgerüstet mit einer Befehlsliste für den 6502 und einem in Basic geschriebenen »Mini-Monitor« entstanden Programme, die 3 und 5 addieren und das Ergebnis im Speicher ablegen konnten. Dazu mußten wir die Befehlcodes aus der Liste heraussuchen und dann in den Speicher »POKEn«. Jeder Sprung mußte von Hand ausgerechnet werden, jeder falsch herausgesuchte Befehl führte zum Programmabsturz. Der erste Disassembler — ein Programm zur Anzeige der Maschinenbefehle in Assemblersprache — war für uns die Offenbarung. Von nun an konnten wir Maschinenprogramme analysieren und daraus lernen. Zum Verständnis der Maschinensprache ist es nämlich noch weit mehr als bei anderen Sprachen wichtig, vorhandene Programme zu verstehen und sich dabei die wichtigsten Techniken anzueignen.
Mit der Zeit wuchsen unsere Ansprüche, ein Assembler mußte her, um die neugewonnenen Erkenntnisse auch auszuprobieren. Das war zuerst wieder ein Basic-Programm, langsam und wenig komfortabel, aber immerhin. Wir schrieben unsere ersten kleinen Routinen, vor allem, um vorhandene Maschinenprogramme unseren eigenen Wünschen anzupassen. Mit dem AMON für den VC 20 bekamen wir dann einen Monitor, der (fast) alle unsere Wünsche erfüllte. Als wir jedoch auf den C 64 umstiegen, mußten wir feststellen, daß es für diesen Computer nichts gab, das uns zufriedenstellen konnte. Der einzige Ausweg: Selbst programmieren. So entstand im Laufe eines Jahres SMON. Ursprünglich hatten wir nur vor, die Funktionen von AMON für den C 64 zu programmieren, aber dabei blieb es nicht. Immer neue Befehle und Routinen kamen hinzu, bis wir endlich zufrieden waren.
Was bietet SMON?
Zunächst ist alles enthalten, was zum »Standard« gehört: Memory-Dump, also die Anzeige des Speicherinhalts in Hexbytes, mit Änderungsmöglichkeiten, ein Disassembler mit Änderungsmöglichkeit sowie Routinen zum Laden, Abspeichern und Starten von Maschinenprogrammen. Darüber hinaus gibt es einen kleinen Direktassembler, der sogar Labels verarbeitet, Befehle zum Verschieben im Speicher mit und ohne Umrechnen der Adressen und Routinen zum Umrechnen von Hex-, Dezimal- und Binärzahlen. Der besondere Clou von SMON liegt aber zweifellos in seinen leistungsfähigen Suchroutinen und vor allem im Trace-Modus. Damit lassen sich Maschinenprogramme Schritt für Schritt abarbeiten und kontrollieren.
Dieser erste Teil umfaßt sämtliche Eingabe- und Ausgaberoutinen, die Registeranzeige, den Memory-Dump sowie Disassembler und Assembler. Damit steht Ihnen bereits ein lauffähiges Monitorprogramm mit den unten aufgeführten Befehlen zur Verfügung.
Der Monitor benötigt für alle Eingaben die hexadezimale Schreibweise, das heißt zu den Zahlen 1 bis 9 kommen noch die Buchstaben A (für dez. 10) bis F (für dez. 15) hinzu.
Bei der Eingabe von Adressen ist folgendes zu beachten: [ANFADR] bedeutet exakt die Startadresse, [ENDADR] bedeutet hierbei die erste Adresse hinter dem gewählten Bereich. Im Normalfall ist die Eingabe mit und ohne Leerzeichen zulässig. Beim Abweichen von dieser Regel wird darauf besonders verwiesen.
Assemblieren
A [ANFADR]
Assemblierung beginnt bei angegebener Adresse
Beispiel:
A 4000 Beginn bei Startadresse $4000
Nach Eingabe von »RETURN« erscheint auf dem Bildschirm die gewählte Adresse mit einem blinkenden Cursor. Die Befehle werden so eingegeben, wie sie der Disassembler zeigt: LDY #00 oder LDA 400E,Y und so weiter. »RETURN« schließt die Eingabe der Zeile ab. Bei fehlerhafter Eingabe springt der Cursor wieder in die Anfangsposition zurück. Ansonsten wird der Befehl disassembliert und nach Ausgabe der Hex-Bytes gelistet. Zur Korrektur vorhergehender Zeilen gehen Sie mit dem Cursor zur Anfangsposition (hinter die Adresse) zurück, schreiben den Befehl neu und gehen nach »RETURN« mit dem Cursor wieder in die letzte Zeile. Falls Ihnen bei Sprüngen (Branch-Befehl, JSR und JMP) die Zieladressen noch nicht bekannt sind, geben Sie einfach sogenannte »Label« ein.
Ein Label besteht aus dem Buchstaben »M« (für Marke) und einer zweistelligen Hex-Zahl von 01 bis 30.
Zum Beispiel: BCC M01
Wenn Sie die Zieladresse für diesen Sprung erreicht haben, dann kennzeichnen Sie diese mit eben dieser »Marke«.
Zum Beispiel: M01 LDY #00
Einzelne Bytes nimmt der Assembler an, indem Sie diese mit einem Punkt kennzeichnen: .00 oder .AB. In diesem Modus werden die Eingaben natürlich nicht disassembliert.
Nach Beendigung des Assemblierens geben Sie »F« ein. Danach sehen Sie alle Ihre Eingaben noch einmal aufgelistet und korrigieren bei Bedarf wie beim Disassembler (!) angegeben.
Probieren Sie einmal das folgende Beispiel:
A4000
Der Assembler meldet sich mit: »4000« und einem blinkenden Cursor. Geben Sie nun ein (die Adressen erscheinen automatisch):
4000 LDY #00
4002 LDA 400E,Y
4005 JSR FFD2
4008 INY
4009 CPY #12
400B BCC 4002
400D BRK
Die folgenden Bytes werden wie beschrieben mit einem Punkt eingegeben. Sie werden nicht disassembliert.
400E .0D
400F .0D
4010 .53
4011 .4D
4012 .4F
4013 .4E
4014 .20
4015 .49
4016 .53
4017 .54
4018 .20
4019 .53
401A .55
401B .50
401C .45
401D .52
401E .0D
401F .0D
Drücken Sie anschließend »F«. Ihr Programm wird nochmal aufgelistet. Starten Sie es nun mit »G 4000«. Es erscheint ein Text auf dem Bildschirm — lassen Sie sich überraschen.
Disassemblieren
D [ANFADR,ENDADR]
disassembliert den Bereich von ANFADR bis ENDADR, wobei ENDADR nicht eingegeben werden muß. Wird keine Endadresse eingegeben, erscheint zunächst nur eine Zeile:
| ADR | HEXBYTES | BEFEHL |
| 4000 | A000 | LDY #00 |
Mit der SPACE-Taste wird der jeweils nächste Befehl in der gleichen Art und Weise gezeigt. Wünschen Sie eine fortlaufende Ausgabe, drücken Sie »RETURN«. Die Ausgabe wird dann so lange fortgesetzt, bis eine weitere Taste gedrückt wird oder bis ENDADR erreicht ist. Mit »RUN/STOP« springen Sie jederzeit in den Eingabemodus zurück.
Das Komma, das vor der Adresse auf dem Bildschirm erscheint, ist ein »hidden command« (verstecktes Kommando). Es braucht nicht eingegeben zu werden, da es automatisch beim Disassemblieren angezeigt wird. So ermöglicht es ein einfaches Ändern des Programms. Fahren Sie mit dem Cursor auf den zu ändernden Befehl und überschreiben Sie ihn mit dem neuen. Wenn Sie jetzt »RETURN« drücken, erkennt SMON das Komma als Befehl und führt ihn im Speicher aus. Achten Sie aber darauf, daß der neue Befehl die gleiche Länge (in Bytes) hat und füllen Sie gegebenenfalls mit »NOPs« auf. Zur Kontrolle können Sie den geänderten Bereich noch einmal disassemblieren.
Lassen Sie als Beispiel einmal das Programm (siehe Befehl »A«) ab 4000 disassemblieren (»D 4000 4011«). Ändern Sie nun den ersten Befehl auf LDY #01. Die Änderung zeigt sich daran, daß die HEX-Bytes automatisch den neuen Wert annehmen. Starten Sie nun das Programm nochmals mit »G 4000«. Jetzt erscheint der Text mit nur einer Zeile Abstand auf dem Bildschirm.
Starten eines Maschinenprogramms (Go)
G [ADRESSE]
startet ein Maschinenprogramm, das bei ADRESSE beginnt. Das Programm muß mit einem BRK-Befehl abgeschlossen werden, damit ein Rücksprung in SMON erfolgen kann. Wird nach »G« keine Adresse eingegeben, benutzt SMON die, die mit dem letzten BRK erreicht worden ist und bei der Register-Ausgabe als PC auftaucht. Mit dem »R«-Befehl (siehe unten) werden die Register vorher auf gewünschte Werte gesetzt.
Memory-Dump
M [ANFADR ENDADR]
gibt die HEX-Werte des Speichers sowie die zugehörigen ASCII-Zeichen aus. Auch hier kann auf die Eingabe einer Endadresse verzichtet werden. Die Steuerung der Ausgabe entspricht der beim Disassemblieren.
Beispiel:
M 4000 gibt die Inhalte der Speicherstellen $4000 bis $4007 aus. Weiter geht es wie beim Disassemblieren mit SPACE oder RETURN. Die Bytes können ebenfalls durch Überschreiben geändert werden, allerdings nicht die ASCII-Zeichen. Verantwortlich dafür ist der Doppelpunkt, der am Anfang jeder Zeile ausgegeben wird, ein weiterer »hidden command«. Wenn Ihre Änderung nicht durchgeführt werden kann, weil Sie zum Beispiel versuchen, ins ROM zu schreiben, wird ein »?« als Fehlermeldung ausgegeben.
Registeranzeige
R zeigt den gegenwärtigen Stand der wichtigsten 6510-Register an: Programmzähler (PC), Status-Register (SR), Akkumulator (AC), X-Register (XR), Y-Register (YR), Stackpointer (SP). Außerdem werden die einzelnen Flags des Status-Registers mit 1 für »gesetzt« und 0 für »nicht gesetzt« angezeigt. Durch Überschreiben werden die Inhalte auf einen gewünschten Wert gesetzt. Die Flags können allerdings nicht einzeln verändert werden, sondern nur durch Überschreiben des Wertes von SR.
Exit
X springt ins Basic zurück. Alle Basic-Pointer bleiben erhalten. Sie können also zum Beispiel direkt im Programm fortfahren, wenn Sie zwischendurch mit SMON einige Speicherstellen kontrolliert haben.
Probieren Sie alle bisher beschriebenen Befehle in Ruhe aus und machen Sie sich mit SMON vertraut. Arbeiten Sie auch parallel den Kurs über Assemblerprogrammierung in dieser Ausgabe durch. Alle Beispiele dort sind auf SMON abgestimmt.
Wir wollen jetzt einen Blick auf das Programm selbst werfen. Natürlich ist es unmöglich, den gesamten Quelltext umfassend zu beschreiben. Andererseits enthält SMON aber eine Reihe von Routinen, die in jedem Maschinenprogramm vorkommen. Wir werden im Rahmen dieser Serie versuchen, die wichtigsten zu erklären, damit Sie sie später in eigene Programme einbauen können.
Zum besseren Verständnis werden solche Routinen so abgedruckt, wie wir sie im Assembler-Quelltext geschrieben haben. Sie enthalten daher anstelle absoluter Adressen Labels, deren Name — hoffentlich — etwas über den Sinn und Zweck aussagt. Parallel dazu sollten Sie sich diese Routinen von SMON disassemblieren lassen, damit Sie sehen, wie es denn nun fertig im Speicher aussieht.
Beginnen wir mit der Routine GETCHRERR. Das soll soviel bedeuten wie »Hole ein Zeichen und erzeuge eine Fehlermeldung, wenn keins eingegeben wurde«. Leider wäre so ein Label auch für den geduldigsten Assembler viel zu lang, daher die merkwürdige Abkürzung. Mit dieser Routine holen wir ein Zeichen von der Tastatur. Das erledigt die Betriebssystemroutine CHRIN. Um zu prüfen, ob überhaupt etwas eingegeben wurde, untersuchen wir das Zeichen. Handelt es sich um die »RETURN«-Taste ($0D), hat der Benutzer gar kein Zeichen eingegeben. Dies quittiert SMON mit einem »?« und dem Rücksprung in den Eingabemodus. So läßt sich — in gewissen Grenzen — kontrollieren, ob zu einem Befehl die richtigen Eingaben gemacht wurden. Geben Sie einmal den »D«-Befehl ohne Angabe einer Adresse ein, dann sehen Sie, was gemeint ist.
Alle Eingaberoutinen benutzen GETCHRERR, um Falscheingaben zu prüfen. Nehmen wir als Beispiel GETBYT. Diese soll ein Byte, also zwei ASCII-Zeichen 0 - F von der Tastatur holen und in ein Byte umwandeln. Das erste Zeichen wird darauf überprüft, ob es sich um ein »Space« oder ein Komma handelt. Trifft das zu, wird es einfach übergangen und das nächste Zeichen geholt. Der Benutzer kann also Leerzeichen und Komma benutzen, um seine Eingaben übersichtlicher zu machen, er muß aber nicht! Ist das Zeichen aber gültig, wird es von ASCHEX in eine Hexzahl gewandelt.
Dazu ein Beispiel:
Auf der Tastatur wurde 5B eingetippt. Zuerst wird jetzt die 5 (ASCII $35) mit $3A verglichen, um festzustellen, ob es sich um eine Zahl (0 - 9) oder einen Buchstaben (A - F) handelt. ASCII $35 ist eine Zahl, also wird nur die linke Hälfte ausmaskiert (AND #$0F). Ergebnis ist $05. Jetzt wird viermal nach links geschoben und das Ergebnis ($50) in $B4 zwischengespeichert. Nun ist das B (ASCII $42) an der Reihe. Da $42 größer ist als $3A werden diesmal 8 und das gesetzte Carry-Flag, also 9 addiert. Ergebnis ist $5B. Linke Hälfte ausmaskieren wie gehabt und eine OR-Verknüpfung mit dem gemerkten $50 ergibt $5B. Das war’s.
Meistens aber braucht SMON zwei Bytes als Eingabe, zum Beispiel für Adressen. Mit dem, was wir schon haben, kein Problem: GETADR ruft einfach GETBYT zweimal hintereinander auf und legt das Ergebnis in zwei Speicherstellen in der Zeropage ab, die mit dem X-Register ausgewählt werden können. Brauchen wir mehr als eine Adreßeingabe, rufen wir einfach GETADR mehrmals auf. So etwas machen GET3ADR und GET2ADR. Bisweilen aber, zum Beispiel beim G-Befehl, darf eine Adresse eingegeben werden, es muß aber nicht sein. Deswegen prüft GETSTART, ob direkt nach dem »G« »RETURN« gedrückt wurde. Dies erledigt GETRET. Wenn ja, wird die Adresse benutzt, die in PCL und PCH steht. Das sind SMONs interne Programm-Counter. Ansonsten wird die eingetippte Adresse benutzt.
Sie sehen, wie aus einfachen Routinen immer kompliziertere Befehle zusammengesetzt werden. Und das ist das ganze Geheimnis, wenn Sie umfangreiche Programme schreiben: Gliedern Sie sich das Problem (hier eine benutzerfreundliche Eingabe) in kleine und kleinste Schritte auf, die Sie dann jeden für sich programmieren und austesten.
Werfen wir noch einen Blick auf die Art und Weise, wie SMON Befehle verarbeitet. In EXECUTE setzen wir zunächst den Stackpointer auf den Wert, den er beim letzten BRK erreicht hatte. Dann werden als erstes die »hidden commands« abgeprüft. Wir lesen dazu direkt vom Bildschirm. D3 enthält die Anfangsadresse der aktuellen Zeile im Speicher. Übrigens gibt es neben den bereits erwähnten noch weitere »hidden commands«, die in den späteren Folgen noch auftauchen werden. Liegt kein verstecktes Kommando vor, holen wir mit GETCHRERR ein Zeichen und merken es uns in COMMAND. Jetzt untersuchen wir, ob dieses Zeichen in der Befehlsliste (CMDTBL) steht. CMDTBL steht übrigens ab $C00B ganz oben im Speicher. Sie endet mit fünf Nullen für spätere Erweiterungen. Direkt dahinter stehen die Anfangsadressen der zugehörigen Routinen in der für den 6502 typischen Reihenfolge, Low-Byte zuerst, dann High-Byte. Sehen Sie sich das mit M C00B einmal an. Am Ende dieser Tabelle stehen nochmals 10 Nullen, denn zu jedem Byte in CMDTBL gehören ja zwei Adreßbytes in der Liste (CMDS). Wenn nun ein Kommando in CMDSEARCH gefunden wurde, wird CMDEXEC als Subroutine aufgerufen. CMDEXEC legt nun die zugehörigen Adreßbytes auf den Stack und führt dann einen RTS aus, der jetzt — nach der Stackmanipulation — zu dem gewünschten Befehl führt. Beachten Sie, daß RTS immer auf die um eins erhöhte Adresse springt, daher müssen Sie zu den Adressen in CMDS immer 1 addieren, wenn Sie den Anfang einer Routine suchen.
Alle Befehle in SMON enden mit einem RTS, springen also auf den JMP EXECUTE hinter CMDFOUND. Damit ist eine Endlosschleife geschlossen, die immer einen Befehl ausführt und anschließend wieder in die Eingabe zurückspringt. Beim nächsten Mal erfahren Sie etwas über LOAD, SAVE und die Umrechnung verschiedener Zahlensysteme.
(Dietrich Weineck/N. Mann/gk)10 rem ************************** 20 rem * * 30 rem * smon teil 1 * 40 rem * von n.mann & d.weineck * 50 rem * fleetrade 40 * 60 rem * 2800 bremen * 70 rem * tel. 0421 / 493090 * 80 rem * * 90 rem ************************** 100 fori=0to8:reada:pr(i)=a:next 110 sa=49152:i=0 120 pa=sa+256*i:ch=0 130 forj=0to255:reada:pokepa+j,a:ch=ch+a:next 140 ifch<>pr(i)then190 150 i=i+1:ifi<8then120 160 pa=pa+256:ch=0 170 forj=0to60:reada:pokepa+j,a:ch=ch+a:next 180 ifch=pr(i)thenend 190 print"fehler in block"i+1:end 191 rem 192 rem *** blockpruefsummen *** 195 data20921,25604,31944,33700,36302,34378,34305,34639,7819 200 rem 210 rem *** block 1 *** 220 rem 230 data169,20,141,22,3,169,194,141,23,3,0,39,35,36,37,44,58,59,61,63,65,66 240 data67,68,70,71,73,75,76,77,79,80,82,83,84,86,87,88,0,0,0,0,0,218,202,45 250 data201,7,201,27,201,251,198,28,196,181,195,244,202,153,200,208,198,107 260 data201,60,202,92,197,16,203,226,195,67,200,182,202,77,200,248,195,192 270 data201,60,200,133,195,77,200,240,203,66,202,210,201,109,195,0,0,0,0,0 280 data0,0,0,0,0,255,255,1,0,65,90,73,82,84,128,32,64,16,0,2,1,1,2,0,145,145 290 data13,83,217,49,55,50,13,0,125,76,125,201,13,13,32,32,80,67,32,32,83,82 300 data32,65,67,32,88,82,32,89,82,32,83,80,32,32,78,86,45,66,68,73,90,67,0 310 data2,4,1,44,0,44,89,41,88,157,31,255,28,28,31,31,31,28,223,28,31,223,255 320 data255,3,31,128,9,32,12,4,16,1,17,20,150,28,25,148,190,108,3,19,1,2,2 330 data3,3,2,2,2,2,2,2,3,3,2,3,3,3,2,0,64,64,128,128,32,16,37,38,33,34,129 340 data130,33,130,132,8,8,231,231,231,231 350 rem 360 rem *** block 2 *** 370 rem 380 data227,227,227,227,227,227,227,227,227,227,231,167,231,231,243,243,247 390 data223,38,70,6,102,65,129,225,1,160,162,161,193,33,97,132,134,230,198 400 data224,192,36,76,32,144,176,240,48,208,16,80,112,120,0,24,216,88,184,202 410 data136,232,200,234,72,8,104,40,64,96,170,168,186,138,154,152,56,248,137 420 data156,158,178,42,74,10,106,79,35,147,179,243,51,211,19,83,115,82,76,65 430 data82,69,83,83,79,76,76,76,67,65,65,83,83,73,68,67,67,66,74,74,66,66,66 440 data66,66,66,66,66,83,66,67,67,67,67,68,68,73,73,78,80,80,80,80,82,82,84 450 data84,84,84,84,84,83,83,79,83,83,79,79,84,66,82,68,68,68,77,78,68,84,84 460 data78,69,80,80,73,77,83,67,67,69,77,78,80,86,86,69,82,76,76,76,76,69,69 470 data78,78,79,72,72,76,76,84,84,65,65,83,88,88,89,69,69,76,82,76,82,82,65 480 data67,65,89,88,65,80,68,67,89,88,67,67,88,89,84,80,82,67,83,81,73,69,76 490 data67,83,73,75,67,68,73,86,88,89,88,89,80,65,80,65,80,73,83,88,89,88,65 500 rem 510 rem *** block 3 *** 520 rem 530 data83,65,67,68,8,132,129,34,33,38,32,128,3,32,28,20,20,16,4,12,216,169 540 data8,141,176,2,169,4,141,175,2,169,6,141,32,208,141,33,208,169,3,141,134 550 data2,162,5,104,157,168,2,202,16,249,173,169,2,208,3,206,168,2,206,169 560 data2,186,142,174,2,169,82,76,255,194,32,194,194,240,11,32,126,194,141 570 data169,2,165,252,141,168,2,96,162,164,32,128,194,32,128,194,208,28,32 580 data126,194,169,254,133,253,169,255,133,254,32,194,194,208,12,141,119,2 590 data230,198,96,32,126,194,44,162,251,32,141,194,149,1,32,154,194,149,0 600 data232,232,96,32,202,194,201,32,240,249,201,44,240,245,208,3,32,202,194 610 data32,175,194,10,10,10,10,133,180,32,202,194,32,175,194,5,180,96,201,58 620 data144,2,105,8,41,15,96,32,202,194,201,32,240,249,198,211,96,32,207,255 630 data198,211,201,13,96,32,207,255,201,13,208,248,169,63,32,210,255,174,174 640 data2,154,162,0,134,198,32,81,195,161,209,201,39,240,17,201,58,240,13,201 650 data59,240,9,201,44,240,5,169,46,32,210,255,32,202,194,201,46,240,249,133 660 rem 670 rem *** block 4 *** 680 rem 690 data172,41,127,162,32,221,10,192,240,5,202,208,248,240,194,32,21,195,76 700 data214,194,138,10,170,232,189,41,192,72,202,189,41,192,72,96,165,252,32 710 data42,195,165,251,72,74,74,74,74,32,53,195,104,41,15,201,10,144,2,105 720 data6,105,48,76,210,255,169,13,32,210,255,138,76,210,255,32,76,195,169 730 data32,76,210,255,169,13,76,210,255,133,187,132,188,160,0,177,187,240,6 740 data32,210,255,200,208,246,96,230,251,208,2,230,252,96,169,14,141,134,2 750 data141,32,208,169,6,141,33,208,169,55,133,1,174,174,2,154,76,116,164,160 760 data192,169,140,32,86,195,162,59,32,64,195,173,168,2,133,252,173,169,2 770 data133,251,32,35,195,32,76,195,162,251,189,175,1,32,42,195,32,76,195,232 780 data208,244,173,170,2,76,208,195,32,78,194,162,251,32,202,194,32,154,194 790 data157,175,1,232,208,244,32,76,195,189,170,2,76,208,195,133,170,169,32 800 data160,9,32,210,255,6,170,169,48,105,0,136,208,244,96,32,73,194,174,174 810 data2,154,162,250,189,174,1,72,232,208,249,104,168,104,170,104,64,32,100 820 data194,162,58,32,64 830 rem 840 rem *** block 5 *** 850 rem 860 data195,32,35,195,160,32,162,0,32,76,195,161,251,32,42,195,161,251,32,57 870 data196,208,241,32,93,196,144,224,96,32,126,194,160,32,162,0,32,202,194 880 data32,154,194,129,251,193,251,240,3,76,209,194,32,57,196,208,236,96,201 890 data32,144,12,201,96,144,10,201,192,144,4,201,219,144,4,169,46,41,63,41 900 data127,145,209,173,134,2,145,243,32,103,195,200,192,40,96,32,111,196,76 910 data102,196,32,103,195,165,251,197,253,165,252,229,254,96,32,148,196,32 920 data134,196,240,14,32,134,196,240,251,201,32,208,5,141,119,2,230,198,96 930 data32,228,255,72,32,225,255,240,2,104,96,76,214,194,160,40,36,172,16,246 940 data132,200,132,208,169,255,32,195,255,169,255,133,184,133,185,173,175 950 data2,133,186,32,192,255,162,0,134,211,202,32,201,255,32,207,255,32,210 960 data255,201,13,208,246,32,204,255,169,145,76,210,255,160,0,177,251,36,170 970 data48,2,80,12,162,31,221,60,193,240,47,202,224,21,208,246,162,4,221,73 980 data193,240,33,221,77,193,240,30,202,208,243,162,56,221,17,193,240,20,202 990 data224,22,208,246,177,251,61,251 1000 rem 1010 rem *** block 6 *** 1020 rem 1030 data192,93,17,193,240,5,202,208,243,162,0,134,173,138,240,15,162,17,177 1040 data251,61,181,192,93,198,192,240,3,202,208,243,189,234,192,133,171,189 1050 data216,192,133,182,166,173,96,160,1,177,251,170,200,177,251,160,16,196 1060 data171,208,7,32,74,197,160,3,208,2,164,182,142,174,0,141,175,0,96,160 1070 data1,177,251,16,1,136,56,101,251,170,232,240,1,136,152,101,252,96,162 1080 data0,134,170,32,100,194,32,140,197,165,173,201,22,240,12,201,47,240,8 1090 data201,33,240,4,201,48,208,13,32,81,195,162,35,169,45,32,210,255,202,208 1100 data250,32,93,196,144,217,96,162,44,32,64,195,32,35,195,32,76,195,32,117 1110 data198,32,203,196,32,76,195,177,251,32,42,195,32,76,195,200,196,182,208 1120 data243,169,3,56,229,182,170,240,9,32,73,195,32,76,195,202,208,247,169 1130 data32,32,210,255,160,0,166,173,208,17,162,3,169,42,32,210,255,202,208 1140 data248,36,170,48,133,76,106,198,36,170,80,41,169,8,36,171,240,35,177,251 1150 data41,252,133,173,200,177,251,10,168,185,60,3,141,174,0,200,185,60,3,141 1160 data175,0,32,190,198,164 1170 rem 1180 rem *** block 7 *** 1190 rem 1200 data182,32,147,198,32,203,196,189,91,193,32,210,255,189,147,193,32,210 1210 data255,189,203,193,32,210,255,169,32,36,171,240,3,32,73,195,162,32,169 1220 data4,36,171,240,2,162,40,138,32,210,255,36,171,80,5,169,35,32,210,255 1230 data32,44,197,136,240,22,169,8,36,171,240,7,169,77,32,210,255,160,1,185 1240 data173,0,32,42,195,136,208,247,160,3,185,172,192,36,171,240,9,185,175 1250 data192,190,178,192,32,66,195,136,208,237,165,182,32,103,195,56,233,1,208 1260 data248,96,164,211,169,32,145,209,200,192,40,144,249,96,228,171,208,4,5 1270 data173,133,173,96,185,173,0,145,251,209,251,208,4,136,16,244,96,104,104 1280 data96,208,28,138,5,171,133,171,169,4,133,181,32,207,255,201,32,240,13 1290 data201,36,240,9,201,40,240,5,201,44,240,1,96,198,181,208,232,96,224,24 1300 data48,14,173,174,0,56,233,2,56,229,251,141,174,0,160,64,96,32,126,194 1310 data133,253,165,252,133,254,32,81,195,32,228,198,48,251,16,246,169,0,133 1320 data211,32,76,195,32,35,195,32,76,195,32,207,255,169,1,133,211,162,128 1330 data208,5,162,128,142,177 1340 rem 1350 rem *** block 8 *** 1360 rem 1370 data2,134,170,32,126,194,169,37,133,200,44,177,2,16,8,162,10,32,207,255 1380 data202,208,250,169,0,141,177,2,32,161,198,201,70,208,22,70,170,104,104 1390 data162,2,181,250,72,181,252,149,250,104,149,252,202,208,243,76,100,197 1400 data201,46,208,17,32,154,194,160,0,145,251,209,251,208,4,32,103,195,200 1410 data136,96,162,253,201,77,208,25,32,154,194,160,0,201,63,176,239,10,168 1420 data165,251,153,60,3,165,252,200,153,60,3,32,161,198,149,169,224,253,208 1430 data4,169,7,133,183,232,208,240,162,56,165,166,221,91,193,240,5,202,208 1440 data246,202,96,165,167,221,147,193,208,244,165,168,221,203,193,208,237 1450 data189,17,193,133,173,32,161,198,160,0,224,32,16,9,201,32,208,8,189,77 1460 data193,133,173,76,49,200,160,8,201,77,240,32,160,64,201,35,240,26,32,157 1470 data194,141,174,0,141,175,0,32,161,198,160,32,201,48,144,27,201,71,176 1480 data23,160,128,198,211,32,161,198,32,157,194,141,174,0,32,161,198,192,8 1490 data240,3,32,190,198,132,171,162,1,201,88,32,154,198,162,4,201,41,32,154 1500 data198,162,2,201,89,32,154,198 1510 rem 1520 rem *** block 9 *** 1530 rem 1540 data165,173,41,13,240,10,162,64,169,8,32,129,198,169,24,44,169,28,162,130 1550 data32,129,198,160,8,165,173,201,32,240,9,190,3,194,185,11,194,32,129,198 1560 data136,208,244,165,171,16,1,200,200,32,138,198,198,183,165,183,133,211 1570 data76,151,197
;***************************
;* *
;* S M O N *
;* MASCHINENSPRACH-MONITOR *
;* *
;* T E I L 1 *
;* *
;* August 1984 *
;* *
;* BY N.MANN & D.WEINECK *
;* TEL. 0421 / 493090 *
;* *
;***************************
;
;
.BA $C000
.OS
.CE
PCL .DE $FB
PCH .DE PCL+1
FLAG .DE $AA
COMMAND .DE $AC
;
BEFCODE .DE $AD
ADRCODE .DE $AB
BEFLEN .DE $B6
LOPER .DE $AE
HOPER .DE $AF
;
PCHSAVE .DE $02A8
PCLSAVE .DE PCHSAVE+1
SRSAVE .DE PCHSAVE+2
AKSAVE .DE PCHSAVE+3
XRSAVE .DE PCHSAVE+4
YRSAVE .DE PCHSAVE+5
SPSAVE .DE PCHSAVE+6
;
PRINTNR .DE PCHSAVE+7
IO.NR .DE PCHSAVE+8
MEM .DE PCHSAVE+9
;
TASTBUF .DE $0277
COLOR .DE $0286
BUF1 .DE $033C
BUF2 .DE BUF1+$30
BUF3 .DE BUF2+$30
BUF4 .DE BUF3+$30
;
READY .DE $A474
BORDER .DE $D020
BKGRND .DE $D021
OPEN .DE $FFC0
CLOSE .DE $FFC3
CHKOUT .DE $FFC9
CLRCHN .DE $FFCC
CHRIN .DE $FFCF
CHROUT .DE $FFD2
STOPT .DE $FFE1
GETIN .DE $FFE4
;
SETBRK LDA #L,BREAK
STA $0316
LDA #H,BREAK
STA $0317
BRK
;
CMDTBL .BY $27
.BY '#$%,:;=?A'
.BY 'BCDFGIKL'
.BY 'MOPRSTVWX'
.BY $00,$00,$00,$00,$00
;
CMDS .SE TICK-1
.SE BEFDEC-1
.SE BEFHEX-1
.SE BEFBIN-1
.SE KOMMA-1
.SE COLON-1
.SE SEMIS-1
.SE COMP-1
.SE ADDSUB-1
.SE ASSEMBLER-1
.SE BCDATA-1
.SE CONVERT-1
.SE DISASSEM-1
.SE FIND-1
.SE GO-1
.SE IO.SET-1
.SE KONTROLLE-1
.SE LOADSAVE-1
.SE MEMDUMP-1
.SE OCCUPY-1
.SE SETPRINTER-1
.SE REGISTER-1
.SE LOADSAVE-1
.SE TRACE-1
.SE VERSCHIEB-1
.SE WRITE-1
.SE EXIT-1
.DS 10
;
OFFSET .BY $FF,$FF,$01,$00
;
FINDTAB .BY 'AZIRT'
FINDFLG .BY $80,$20,$40,$10,$00
FINDFLG1 .BY $02,$01,$01,$02,$00
;
SYS172 .BY $91,$91,$0D,$53
.BY $D9,$31,$37,$32,$0D
;
DATATAB .BY $00,$7D,$4C
.SE DATALOOP
;
REGHEAD .BY $0D,$0D,$20,$20
.BY 'PC SR AC XR YR SP'
.BY ' NV-BDIZC' $00
;
.BA $C214
;
BREAK CLD
LDA #$08
STA IO.NR
LDA #04
STA PRINTNR
LDA #06
STA BORDER
STA BKGRND
BREAK1 LDA #03
STA COLOR
LDX *$05
BREAK2 PLA
STA PCHSAVE,X
DEX
BPL BREAK2
LDA PCLSAVE
BNE BREAK3
DEC PCHSAVE
BREAK3 DEC PCLSAVE
TSX
STX SPSAVE
LDA #'R'
JMP CMDSTORE
;
GETSTART JSR GETRET
BEQ GETSTRTS
GETSTART1 JSR GETADR1
STA PCLSAVE
LDA *PCH
STA PCHSAVE
GETSTRTS RTS
;
GET3ADR LDX *$A4
JSR GETADR
JSR GETADR
BNE GETADR
;
GET1.2ADR JSR GETADR1
LDA #$FE
STA *$FD
LDA #$FF
STA *$FE
JSR GETRET
BNE GETADR
STA TASTBUF
INC *$C6
RTS
;
GET2ADR JSR GETADR1
.BY $2C
GETADR1 LDX #$FB
;
GETADR JSR GETBYT
STA *$01,X
JSR GETBYT1
STA *$00,X
INX
INX
RTS
;
GETBYT JSR GETCHRERR
CMP #$20
BEQ GETBYT
CMP #$2C
BEQ GETBYT
BNE ASCHEX
;
GETBYT1 JSR GETCHRERR
ASCHEX JSR ASCHEX1
ASL A
ASL A
ASL A
ASL A
STA *$B4
JSR GETCHRERR
JSR ASCHEX1
ORA *$B4
RTS
ASCHEX1 CMP #$3A
BCC ASCHEX2
ADC #$08
ASCHEX2 AND #$0F
RTS
;
SKIPSPACE JSR GETCHRERR
CMP #$20
BEQ SKIPSPACE
DEC *$D3
RTS
;
GETRET
JSR CHRIN
DEC *$D3
CMP #$0D
GETBRTS RTS
;
GETCHRERR
JSR CHRIN
CMP #$0D
BNE GETBRTS
;
ERROR LDA #'?'
JSR CHROUT
EXECUTE LDX SPSAVE
TXS
LDX #$00
STX *$C6
JSR RETURN
LDA ($D1,X)
CMP #'''
BEQ EXEC1
CMP #':'
BEQ EXEC1
CMP #';'
BEQ EXEC1
CMP #','
BEQ EXEC1
LDA #'.'
JSR CHROUT
EXEC1 JSR GETCHRERR
CMP #'.'
BEQ EXEC1
CMDSTORE STA *COMMAND
AND #$7F
LDX *CMDS-CMDTBL
CMDSEARCH CMP CMDTBL-1,X
BEQ CMDFOUND
DEX
BNE CMDSEARCH
BEQ ERROR
CMDFOUND JSR CMDEXEC
JMP EXECUTE
;
CMDEXEC TXA
ASL A
TAX
INX
LDA CMDS-2,X
PHA
DEX
LDA CMDS-2,X
PHA
RTS
;
HEXOUT LDA *PCH
JSR HEXOUT1
LDA *PCL
;
HEXOUT1 PHA
LSR A
LSR A
LSR A
LSR A
JSR HEXOUT2
PLA
AND #$0F
HEXOUT2 CMP #$0A
BCC HEXOUT3
ADC #$06
HEXOUT3 ADC #$30
JMP CHROUT
;
CHARRET LDA #$0D
CHARR1 JSR CHROUT
TXA
JMP CHROUT
;
SPACE2 JSR SPACE
SPACE LDA #$20
JMP CHROUT
;
RETURN LDA #$0D
JMP CHROUT
;
PRINT STA *$BB
STY *$BC
LDY #$00
PRINT1 LDA ($BB),Y
BEQ PRINT2
JSR CHROUT
INY
BNE PRINT1
PRINT2 RTS
;
PCINC INC *PCL
BNE PCRTS
INC *PCH
PCRTS RTS
;
;**********
EXIT
LDA #$0E
STA COLOR
STA BORDER
LDA #$06
STA BKGRND
LDA #$37
STA *$01
LDX SPSAVE
TXS
JMP READY
;
;**********
REGISTER LDY #H,REGHEAD
LDA #L,REGHEAD
JSR PRINT
LDX #';'
JSR CHARRET
REGISTER1 LDA PCHSAVE
STA *PCH
LDA PCLSAVE
STA *PCL
JSR HEXOUT
JSR SPACE
LDX #$FB
REGISTER2 LDA SRSAVE-$FB,X
JSR HEXOUT1
JSR SPACE
INX
BNE REGISTER2
LDA SRSAVE
JMP CHANGBIN
;
;**********
SEMIS JSR GETSTART1
LDX #$FB
SEMIS1 JSR GETCHRERR
JSR GETBYT1
STA SRSAVE-$FB,X
INX
BNE SEMIS1
JSR SPACE
LDA SRSAVE,X
JMP CHANGBIN
;
CHANGBIN STA *FLAG
LDA #$20
LDY #$09
CHANGB1 JSR CHROUT
ASL *FLAG
LDA #$30
ADC #$00
DEY
BNE CHANGB1
RTS
;
;**********
GO JSR GETSTART
LDX SPSAVE
TXS
LDX #$FA
GO2 LDA PCHSAVE-$FA,X
PHA
INX
BNE GO2
PLA
TAY
PLA
TAX
PLA
RTI
;
;**********
MEMDUMP JSR GET1.2ADR
MEMDUMP1 LDX #':'
JSR CHARRET
JSR HEXOUT
LDY #32
LDX #$00
MEMDUMP2 JSR SPACE
LDA (PCL,X)
JSR HEXOUT1
LDA (PCL,X)
JSR ASCII
BNE MEMDUMP2
JSR CONTIN
BCC MEMDUMP1
RTS
;
;**********
COLON JSR GETADR1
LDY #32
LDX #$00
COLON1 JSR GETCHRERR
JSR GETBYT1
STA (PCL,X)
CMP (PCL,X)
BEQ COLON2
JMP ERROR
COLON2 JSR ASCII
BNE COLON1
RTS
;
ASCII CMP #$20
BCC ASCII1
CMP #$60
BCC ASCII2
CMP #$C0
BCC ASCII1
CMP #$DB
BCC ASCII3
ASCII1 LDA #'.'
ASCII2 AND #$3F
ASCII3 AND #$7F
STA ($D1),Y
LDA COLOR
STA ($F3),Y
JSR PCINC
INY
CPY #40
RTS
;
CONTIN JSR TASTE
JMP CMPEND1
;
CMPEND JSR PCINC
CMPEND1 LDA *PCL
CMP *$FD
LDA *PCH
SBC *$FE
RTS
;
TASTE JSR PRINTER1
TASTE1 JSR SCANKEY
BEQ TASTRTS
TASTE2 JSR SCANKEY
BEQ TASTE2
CMP #$20
BNE TASTRTS
STA TASTBUF
INC *$C6
TASTRTS RTS
;
SCANKEY
JSR GETIN
PHA
JSR STOPT
BEQ STOP
PLA
SCANRTS RTS
STOP JMP EXECUTE
;
PRINTER1 LDY #40
PRINTER BIT COMMAND
BPL SCANRTS
STY *$C8
STY *$D0
LDA #$FF
JSR CLOSE
LDA #$FF
STA *$B8
STA *$B9
LDA PRINTNR
STA *$BA
JSR OPEN
LDX #$00
STX *$D3
DEX
JSR CHKOUT
PRLOOP JSR CHRIN
JSR CHROUT
CMP #$0D
BNE PRLOOP
JSR CLRCHN
LDA #$91
JMP CHROUT
;
;
;
;
;
;
;***************************
;* *
;* S M O N *
;* MASCHINENSPRACH-MONITOR *
;* *