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 * ;* *