Und oder nicht?
Die boolschen Befehle werden häufig genutzt, um einzelne Bits zu setzen oder zu löschen. Schaut euch am besten nochmal die Boolsche Algebra an, falls ihr nicht mehr genau vor Augen habt, wie UND und ODER funktionieren.
Wir testen die Boolschen-Befehle in einem neuen, kleinen Beispiel.
Wir werden nun bei den ersten 240 Zeichen auf dem BS jedes zweite invertieren. Wer möchte kann als kleine Übung das Programm ja später so ändern, dass das Programm sich auf den gesamten Bildschirm bezieht. Invertieren bedeutet nichts Anderes, als Zeichen- und Hingrundfarbe zu vertauschen. Wer sich mal Bild-1 und Bild-2 beim Char-ROM ansieht, dem wird jetzt bestimmt etwas auffallen. Das zweite Bild zeigt exakt die gleichen Zeichen, die auch schon auf dem ersten zu sehen sind, nur invertiert. Uns als Binär-Profis sollte bei einem Blick auf die Nummern der einzelnen Zeichen noch etwas ins Auge stechen (Autsch!).
Nehmen wir als Beispiel mal das Zeichen für die Ziffer 7, dies hat in der normalen Darstellung die Nr. 55 / $37 und invertiert die Nr. 183 / $b7 im Zeichensatz.
normal....: 55 = $37 = %00110111 invertiert: 183 = $b7 = %10110111
Jetzt sollte allen klar sein, dass man zum invertierten Zeichen kommt, wenn man einfach das höchste Bit setzt. Das seht ihr ja auch in den beiden Bildern beim Char-ROM, da alle normalen Zeichen im 1. Bild von Nr. 0-127 zu finden sind, die invertierten aber von Nr. 128 bis 255 im zweiten Bild.
Oder
Wir brauchen nun also ein Programm, dass für jedes Zeichen das höchste Bit setzt.
;*** Variablen SCREENRAM = $03ff ;Start des Bildschirmspeichers ORAMASK = %10000000 ;*** Startadresse BASIC-Zeile *=$0801 !byte $0c,$08,$e2,$07,$9e,$20,$32,$30,$36,$32,$00,$00,$00 ;*** Start des Assemblerprogrammes ldy #$f0 ;die ersten 240 Zeichen loop_ora lda SCREENRAM,y ;Zeichen in den Akku
Beim Turbo Assembler könnt ihr keine Unterstriche wie bei loop_ora verwenden, lasst diese einfach weg und schreibt die Labelbezeichnungen direkt zusammen. Denkt auch weiterhin daran, dass die Label dort max. 15 Zeichen lang sein dürfen.
Hier ist nichts Spektakuläres zu sehen, einzig die neue Variable ORAMASK. In unserer Schleife direkt hinter loop_ora laden wir erstmal das Zeichen in den Akku, dass sich an der Adresse SCRENNRAM plus Y-Register (absolute Y-indizierte Adressierung) befindet.
ora #ORAMASK ;ODER-Verknüpfung des Akkus mit mit unserer Maske
ORA: OR with Accumulator (bitweises ODER mit dem Akku)
ORA Akku ($09, 2B, 2T, NZ)
Hier führen wir jetzt mit ORA eine bitweise ODER-Verknüpfung mit dem Akku und unserer ORAMASK durch. Das Ergebnis landet dann wieder im Akkumulator. Ist das Ergebnis 0, dann wird das Z-Flag gesetzt, sollte Bit-7 auf eins stehen, dann wird das N-Flag gesetzt.
Beim ODER wird ein Bit immer dann zu 1, wenn entweder das erste oder das zweite oder beide beteiligten Bits eine 1 aufweisen:
%0101 ORA %0011 ===== %0111
ORA wird daher verwendet, um gezielt einzelne Bits zu setzen, dazu läßt man in seiner Maske alle Bits, die man nicht ändern möchte auf 0 und setzt nur die Bits auf 1 die man auf jeden Fall auf 1 setzen will. Deshalb ist in unserer Maske nur Bit-7 auf 1 gesetzt worden, alle anderen sind 0.
Im Detail sieht das für unser Zeichen 7, wenn es im Akku liegt, so aus:
Akku: %00110111 ORA Maske: %10000000 ========= Akku: %10110111
Voiala, wir haben das Zeichen 183, das der invertierten Darstellung der entspricht.
sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern und gleich nochmal, da dey ;wir nur jedes 2. Zeichen invertieren wollen bne loop_ora ;solange nicht 0 wieder zum loop_ora springen rts ;Zurück zum BASIC
Anschließend schreiben wir den geänderten Akku-Inhalt wieder zurück an unsere Position auf dem BS, verringern das Y-Register zweimal fürs übernächste Zeichen und springen solange zu loop_ora zurück, bis das Z-Flag gesetzt ist. Zum Ende gehts zurück ins Basic.
Nach einem Start sollte euer BS ungefähr so aussehen:
Nun wollen wir unsere Invertierung wieder rückgängig machen. Dazu verwenden wir diesmal die Möglichkeit aus Basic heraus direkt an die gewünschte Stelle in unserem Programm zuspringen. Vom Prinzip her machen wir das schon bei jedem Programmstart. Unsere Programme beginnen bekanntlich mit der Zeile: 2018 SYS 2062
Diesen SYS-Befehl können wir natürlich auch direkt eingeben, also statt RUN einfach SYS 2062. Ändern wir unser Programm und probieren es mal.
Und
Gebt bitte am Sourcebeginn, hinter ORAMASK , eine neue Variable ein:
ANDMASK = %01111111
Fügt dann hinter unser bisheriges Programm (Ausschnitt ist unten zur Orientierung markiert), die nächsten Zeilen ein:
... bne loop_ora ;solange nicht 0 wieder zum loop_ora springen rts ;Zurück zum BASIC *=$1000 ;(4096) ldy #$f0 ;wieder nur die ersten 240 Zeichen loop_and lda SCREENRAM,y ;Zeichen in den Akku
Etwas irritiert könntet ihr jetzt durch *=$1000 sein. Eigentlich benutzen wir dies ja, um die Startadresse für unser Programm festzulegen (bisher fast immer *=$0801). Auch wenn es nicht wirklich benötigt wird, teilen wir hier dem Assembler mit, dass die nächsten Befehle an der Speicherstelle $1000 oder 4096 dezimal beginnen sollen. Falls ihr neue Adressen angebt, passt besser auf, dass sich diese nicht überschneiden. Das C64 Studio wirft einen Fehler, sollte dies der Fall sein. Bei ACME gibt es eine Warnung, die ja unterdrückt werden können. Der Turbo Assembler nimmt eure Angabe ohne weitere Hinweise / Warnungen an. Wir benutzen dies nur um eine schönere Einsprungadresse für unsere neue Funktion zu erhalten.
and #ANDMASK ;UND-Verknüpfung des Akkus mit mit unserer Maske
AND: AND with Accumulator (bitweises UND mit dem Akku)
AND Akku ($29, 2B, 2T, NZ)
Auch wenn wir das AND schon hatten, wiederhole das hier der Vollständigkeit nochmal. Wir führen jetzt mit AND eine bitweise UND-Verknüpfung mit dem Akku und unserer ANDMASK durch. Das Ergebnis landet dann wieder im Akkumulator. Ist das Ergebnis Null, dann wird das Zero-Flag gesetzt, sollte Bit-7 auf 1 stehen, dann wird das N-Flag gesetzt.
Beim AND wird ein Bit immer dann zu 1, wenn das erste und das zweite Bit eine 1 aufweist:
%0101 AND %0011 ===== %0001
AND verwenden wir, um gezielt einzelne Bits zu löschen, dazu läßt man in seiner Maske alle Bits, die man nicht ändern möchte auf 1 und setzt nur die Bits auf 0, die man auf jeden Fall löschen will. Daher ist in unserer Maske nur Bit-7 auf 0 gesetzt worden, alle Anderen sind 1.
Und jetzt nochmal für unser invertiertes Zeichen , wenn es im Akku liegt:
Akku: %10110111 AND Maske: %01111111 ========= Akku: %00110111
Schon haben wir wieder das Zeichen 55, das der normalen Darstellung der 7 entspricht.
sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern (jetzt nur einmal) bne loop_and ;solange nicht 0 wieder zum loop_and springen rts ;Zurück zum BASIC
Der Rest entspricht dem Block von eben, nur das wir hier einfach mal alle 240 Zeichen anfassen. Es gibt absichtlich nur ein dey, um zu zeigen, dass sich wirklich nur unsere invertierten Zeilen ändern.
Wenn ihr das Progamm nun startet, gibt es zunächst keinen Unterschied zu eben. Gebt jetzt aber mal SYS 4096 ein, drückt RETURN und schon ist die Invertierung verschwunden. Wir springen damit direkt zum Beginn unserer neuen Routine, die ja hinter *=$1000 beginnt. Mit RUN könnt ihr natürlich wieder die Invertierung vornehmen.
Exklusives Oder
Abschließend möchten wir jetzt unsere Invertierung invertieren 😉 . Wir wollen also innerhalb der ersten 240 Zeichen alle normalen invertiert darstellen und alle invertierten normal. Dazu könnten wir in unserer Schleife, das in den Akku geladene Zeichen prüfen und dann zur jeweiligen Verknüpfung springen.
Die Routine würde dann so aussehen (bitte am Programmende einfügen!):
*=$2000 ;(8192) ;*** Start des Assemblerprogrammes ldy #$f0 ;die ersten 240 Zeichen loop_onoff lda SCREENRAM,y ;Zeichen in den Akku und
Bis auf unsere neue Startadresse *=$2000 ist alles wie gehabt.
bmi donormal ;wenn Bit-7 (Invers) gesetzt ist, zu donormal: springen
BMI: Branch if MInus (springe wenn Minus)
BMI relativ ($30, 2B, 2-4T, <keine>)
Mit BMI verzweigen wir zur angegebenen Adresse, wenn nach der letzten Aktion das „Minuszeichen“ gesetzt ist. Es wird also einfach geprüft, ob das Negativ-Flag = 1 ist. Wie von den anderen bedingten Sprüngen gewohnt, werden 2 Taktzyklen benötigt, wenn nicht gesprungen wird, 3TZ beim Sprung und sogar 4TZ falls der Sprung über eine Pagegrenze geht.
Wir überprüfen hier unser eben in den Akku geladenes Zeichen. Ist es bereits invertiert, dann ist das Bit-7 ja gesetzt und wir springen zu donormal, um die Invertierung aufzuheben, wenn es aktuell nicht invertiert ist invertieren wir es mit dem nächsten Befehl…
ora #ORAMASK ;sonst ODER-Verknüpfung für Invers jmp charout ;weiter zur Ausgabe donormal and #ANDMASK ;UND-Verknüpfung für 'normal' charout sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern bne loop_onoff ;solange nicht 0 wieder zum loop_onoff springen rts ;Zurück zum BASIC
Wenn wir invertieren mussten, springen wir nach dem ORA direkt zu charout, müssen wir die Invertierung aufheben, landen wir nach dem BMI bei donormal und löschen mit dem AND unsere Invertierung. Anschließen wird das Zeichen wieder ausgegeben usw. (alles wieder wie eben).
Startet das Programm und es erscheint wieder unsere gestreifte Darstellung. Mit SYS 4096 könnt ihr diese wieder rückgängig machen. Gebt ihr nach dem RUN aber die Startadresse SYS 8192 ein, wird der obere Bereich des Bildschrims inveriert. Bei jedem Aufruf (einfach mit dem Cursor wieder auf den SYS-Befehl gehen und RETURN drücken) ändert sich unsere Anzeige. Probiert das ruhig mal mit der gestreiften und der blanken Darstellung (nach SYS 4096) aus.
OK, dass habe ich jetzt nur eingebaut, um mal den BMI-Befehl zu verwenden und nochmal zu zeigen, dass es viele Lösungsmöglichkeiten gibt. Wir können das Problem auch viel eleganter lösen. Es gibt nämlich einen Befehl, der es uns ermöglicht die Umschaltung zwischen invertiert und normal automatisch vorzunehmen. Gebt die nächsten Zeilen direkt hinter dem Block mit dem BMI-Befehl von eben, also wieder am Ende unseres Sourcecodes ein.
;(8213) start_eor ldy #$f0 ;die ersten 240 Zeichen durchgehen loop_eor lda SCREENRAM,y ;Zeichen in den Akku
Dieses Mal verzichten wir auf eine neue Startadresse, daher ist es sehr wichtig, dass die vorherige Routine unverändert von euch übernommen wurde, sonst stimmt die im Kommentar ;(8213) angezeigte Startadresse nicht! Der Rest ist mittlerweile kalter Kaffee für uns.
eor #ORAMASK ;Exklusiv-ODER-Verknüpfung Akku mit mit unserer Maske
EOR: Exclusiv OR with Accumulator (bitweises exklusives ODER mit dem Akku)
EOR Akku ($49, 2B, 2T, NZ)
Wow, etwas ganz exklusives… EOR arbeitet fast wie ORA. Wir verwenden hier auch unsere ORAMASK und das Ergebnis landet wieder im Akku. Ist das Ergebnis negativ (also Bit-7 gesetzt), dann wird das Negativ-Flag gesetzt, sollte das Ergebnis Null sein, wird das Z-Flag gesetzt.
Beim EOR wird ein Bit nur dann zu 1, wenn ausschließlich das erste oder das zweite Bit eine 1 aufweist:
%0101 EOR %0011 ===== %0110
EOR wird häufig zum Umschalten von einzelnen Bits verwendet. Dazu setzen wir in unserer Maske alle Bits, die Umgeschaltet werden sollen auf 1 und die anderen auf 0.
Für unser Zeichen 7 im Akku, sähe das dann so aus:
Akku: %00110111 EOR Maske: %10000000 ========= Akku: %10110111 nächstes EOR Maske: %10000000 ========= Akku: %00110111
Wie ihr seht, schalten wir mit jedem EOR immer das 7-Bit um, die anderen bleiben unverändert.
sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern bne loop_eor ;solange nicht 0 wieder zum loop_eor springen jmp start_eor ;für Endlosschleife zu start_eor springen
Wir fabrizieren hier eine Endlosschleife (also obacht beim Start, ggf. vorher speichern), um mal wieder unsere Anfälligkeit für schnelle Bildwechsel zu prüfen.
Zur Sicherheit hier wieder das komplette Programm:
;*** Variablen SCREENRAM = $03ff ;Start des Bildschirmspeichers ORAMASK = %10000000 ANDMASK = %01111111 ;*** Startadresse BASIC-Zeile *=$0801 !byte $0c,$08,$e2,$07,$9e,$20,$32,$30,$36,$32,$00,$00,$00 ;*** Start des Assemblerprogrammes ldy #$f0 ;die ersten 240 Zeichen loop_ora lda SCREENRAM,y ;Zeichen in den Akku ora #ORAMASK ;ODER-Verknüpfung des Akkus mit mit unserer Maske sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern und gleich nochmal, da dey ;wir nur jedes 2. Zeichen invertieren wollen bne loop_ora ;solange nicht 0 wieder zum loop_ora springen rts ;Zurück zum BASIC *=$1000 ;(4096) ldy #$f0 ;wieder nur die ersten 240 Zeichen loop_and lda SCREENRAM,y ;Zeichen in den Akku and #ANDMASK ;UND-Verknüpfung des Akkus mit mit unserer Maske sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern (jetzt nur einmal) bne loop_and ;solange nicht 0 wieder zum loop_and springen rts ;Zurück zum BASIC *=$2000 ;(8192) ;*** Start des Assemblerprogrammes ldy #$f0 ;die ersten 240 Zeichen loop_onoff lda SCREENRAM,y ;Zeichen in den Akku und bmi donormal ;wenn Bit-7 (Invers) gesetzt ist, zu donormal: springen ora #ORAMASK ;sonst ODER-Verknüpfung für Invers jmp charout ;weiter zur Ausgabe donormal and #ANDMASK ;UND-Verknüpfung für 'normal' charout sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern bne loop_onoff ;solange nicht 0 wieder zum loop_onoff springen rts ;Zurück zum BASIC ;(8213) start_eor ldy #$f0 ;die ersten 240 Zeichen durchgehen loop_eor lda SCREENRAM,y ;Zeichen in den Akku eor #ORAMASK ;Exklusiv-ODER-Verknüpfung Akku mit mit unserer Maske sta SCREENRAM,y ;Zeichen auf der 1. Page des BS-Speichers ausgeben dey ;Y-Register verringern bne loop_eor ;solange nicht 0 wieder zum loop_eor springen jmp start_eor ;für Endlosschleife zu start_eor springen
Ein Start zeigt unser Streifenmuster, mit SYS 8213 springt ihr aber in die Endlosschleife mit dem EOR-Befehl und alles flackert…
Eine Bemerkung am Rande, wenn ihr jetzt einen RESET (z. B. mit ALT+R bei Vice) auslöst und dann SYS 8213 eingebt. Läuft unser Programm wieder an. Also merke, der Speicher wird bei einem RESET nicht gelöscht / initialisiert (jedenfalls nicht alles, Teilbereiche werden schon verändert!).
Wer sich die Boolsche Algebra angesehen hat, der fragt sich jetzt bestimmt wo denn der NOT-Befehl bleibt. Den gibt es zwar unter BASIC, aber nicht im Assembler. Macht noch ein paar Übungen mit den drei boolschen Befehlen und ihr kommt sicher auf die Lösung. Hilfreich könnte es evtl. sein, unser Programm aus dem letzten Beitrag Mit dem Rechner rechnen so anzupassen, dass auch die boolschen Befehle getestet werden können.
Weitere Adressierungsarten
Es folgt wieder die endlos erscheinende Liste, der alternativen Adressierungsarten. Da AND, ORA und EOR identische Adressierungsarten besitzen, werden die jeweils am Stück behandelt.
ora $0821
ORA absolut ($0D, 3B, 4T, NZ)
and $0821
AND absolut ($2D, 3B, 4T, NZ)
eor $0821
EOR absolut ($4D, 3B, 4T, NZ)
Eine Verknüpfung mit dem Akku und dem Byte, dass an der absoluten Adresse zu finden ist.
ora $0821,x
ORA absolut X-indiziert ($1D, 3B, 4-5T, NZ)
and $0821,x
AND absolut X-indiziert ($3D, 3B, 4-5T, NZ)
eor $0821,x
EOR absolut X-indiziert ($5D, 3B, 4-5T, NZ)
Eine Verknüpfung mit dem Akku und dem Byte, dass an der absoluten Adresse plus dem X-Register zu finden ist. Beim überschreiten einer Page-Grenze wird ein extra Taktzyklus fällig.
ora $0821,y
ORA absolut Y-indiziert ($19, 3B, 4-5T, NZ)
and $0821,y
AND absolut Y-indiziert ($39, 3B, 4-5T, NZ)
eor $0821,y
EOR absolut Y-indiziert ($59, 3B, 4-5T, NZ)
Wie eben, nur mit dem Y-Register.
ora $fb
ORA Zero-Page ($05, 2B, 3T, NZ)
and $fb
AND Zero-Page ($25, 2B, 3T, NZ)
eor $fb
EOR Zero-Page ($45, 2B, 3T, NZ)
ora $fb,x
ORA Zero-Page X-indiziert ($15, 2B, 4T, NZ)
and $fb,x
AND Zero-Page X-indiziert ($35, 2B, 4T, NZ)
eor $fb,x
EOR Zero-Page X-indiziert ($55, 2B, 4T, NZ)
ora ($fb,x)
ORA indirekt X-indiziert ($01, 2B, 6T, NZ)
and ($fb,x)
AND indirekt X-indiziert ($21, 2B, 6T, NZ)
eor ($fb,x)
EOR indirekt X-indiziert ($41, 2B, 6T, NZ)
ora ($fb),y
ORA indirekt Y-nach-indiziert ($11, 2B, 5-6T, NZ)
and ($fb),y
AND indirekt Y-nach-indiziert ($31, 2B, 5-6T, NZ)
eor ($fb),y
EOR indirekt Y-nach-indiziert ($51, 2B, 5-6T, NZ)
Bei Überschreitung einer Page-Grenze wird wieder ein extra Takt benötigt.
Abschließend für alle, die immer noch über den fehlenden NOT-Befehl grübeln, hier ist die Lösung (in unser Programm aus Mit dem Rechner rechnen eingebettet):
CHROUT = $ffd2 ;Jump-Table Adr.: Zeichenausgabe SETCURSOR = $fff0 ;Jump-Table Adr.: get/set cursor pos TEXTADR = $fb ;Zero-Page-Adr. für den Text BINPOS = $05 ;Spalte in der unsere Ausgabe beginnt N1 = %10101010 ;erste Zahl / Maske N2 = %11111111 ;zweite Zahl / Maske ;*** Startadresse BASIC-Zeile *=$0801 !byte $0b,$08,$e2,$07,$9e,$20,$32,$30,$36,$32,$00,$00, $00 ;*** Start des Assemblerprogrammes cld ;D-Flag zur Sicherheit löschen jsr showmask ;Hauptmaske anzeigen lda #$05 ;#$01 = Addition | #$02 = Subtraktion | ;#$03 = AND | #$04 = ORA | #$05 = EOR jsr showoperator ;Operant anzeigen ;*** rechnen lda #N1 ;1. Zahl in den Akku eor #N2 ;Durch eine Maske mit lauter einsen, erreichen wir ;beim eor dass, was sonst mit NOT geschieht: ;1 wird 0 und 0 wird 1 jsr result ;Ergebnis ausgeben rts ;zurück zum BASIC ;************************************************************ ;*** Das Ergebnis ausgeben ;************************************************************ ;*** Übergabe: A = Ergebnis der Operation ;************************************************************ ;*** Rückgabe: - ;************************************************************ ;*** ändert: A,X,Y,SR ;************************************************************ result php ;Flags für Anzeige auf den Stack ;*** Ergebnis im Akku ausgeben ldx #$0b ldy #BINPOS jsr binaryout ;*** Flags ausgeben pla ldx #$0b ldy #BINPOS+10 jsr binaryout rts ;************************************************************ ;*** Operator ausgeben ;************************************************************ ;*** Übergabe: A = Nr. des Operanten ;************************************************************ ;*** Rückgabe: - ;************************************************************ ;*** ändert: X,Y,SR und A (durch Unterprogramme) ;************************************************************ showoperator cmp #$01 bne showoperatornext_1 lda #<textadd sta TEXTADR lda #>textadd sta TEXTADR+1 jmp showoperatortextout showoperatornext_1 cmp #$02 bne showoperatornext_2 lda #<textsub sta TEXTADR lda #>textsub sta TEXTADR+1 jmp showoperatortextout showoperatornext_2 cmp #$03 bne showoperatornext_3 lda #<textand sta TEXTADR lda #>textand sta TEXTADR+1 jmp showoperatortextout showoperatornext_3 cmp #$04 bne showoperatornext_4 lda #<textora sta TEXTADR lda #>textora sta TEXTADR+1 jmp showoperatortextout showoperatornext_4 cmp #$05 bne showoperatornext_5 lda #<texteor sta TEXTADR lda #>texteor sta TEXTADR+1 jmp showoperatortextout showoperatornext_5 rts showoperatortextout ldx #$09 ldy #$01 jsr textout rts ;************************************************************ ;*** Unsere Maske ausgeben ;************************************************************ ;*** Übergabe: - ;************************************************************ ;*** Rückgabe: - ;************************************************************ ;*** ändert: A,X,Y,SR ;************************************************************ showmask ;BS löschen lda #$93 jsr CHROUT ;1. Zahl ausgeben lda #N1 ldx #$08 ldy #BINPOS jsr binaryout ;2. Zahl ausgeben lda #N2 ldx #$09 ldy #BINPOS jsr binaryout ;Überschrift für Flags lda #<textflags sta TEXTADR lda #>textflags sta TEXTADR+1 ldx #$0a ldy #BINPOS+11 jsr textout ;========= ausgeben lda #<textline sta TEXTADR lda #>textline sta TEXTADR+1 ldx #$0a ldy #BINPOS jsr textout rts ;************************************************************ ;*** Beliebigen Text ausgeben (null-terminiert) ;************************************************************ ;*** Übergabe: X = Zeile in der die Ausgabe beginnt ;*** Y = Spalte in der die Ausgabe beginnt ;*** TEXTADR = An dieser Zero-Page-Adresse wird die ;*** Adresse des Textes erwartet. ;*** Das Textende wird durch $00 gekennzeichnet! ;************************************************************ ;*** Rückgabe: - ;************************************************************ ;*** ändert: A,X,Y,SR ;************************************************************ textout clc ;C=0 für set / C=1 für get Cursor jsr SETCURSOR ;Jump-Table: get/set cursor ldy #$00 ;Pos. im Text textcharin lda (TEXTADR),Y ;Aktuelles Zeichen in den Akku laden beq textdone ;wenn 0 dann sind wir fertig jsr CHROUT ;Jump-Table: Zeichenausgeben iny ;X erhöhen, für nächstes Zeichen jmp textcharin ;und wieder hochspringen textdone ;Ziel, sobald wir eine 0 haben rts ;zurück zum Aufrufer (jsr textout) ;*** unsere Texte textadd !text " +" !byte $00 textsub !text " -" !byte $00 textand !text "AND" !byte $00 textora !text "ORA" !byte $00 texteor !text "EOR" !byte $00 textflags !text "NV-BDIZC" !byte $00 textline !text "=========" !byte $00 ;************************************************************ ;*** Den Inhalt des Akkus als Binärzahl auf dem BS ausgeben ;************************************************************ ;*** Übergabe: A = Zahl, die ausgegeben wird ;*** X = Zeile in der die Ausgabe beginnt ;*** Y = Spalte in der die Ausgabe beginnt ;************************************************************ ;*** Rückgabe: - ;************************************************************ ;*** ändert: X,Y,SR ;************************************************************ binaryout pha ;Akku auf dem Stack merken (wg. SETCURSOR) clc ;C=0 für set / C=1 für get Cursor jsr SETCURSOR ;Jump-Table: get/set cursor lda #"%" ;Prozent-Zeichen in den Akku jsr CHROUT ;und ausgeben pla ;Akku wieder vom Stack holen pha ;und direkt nochmal merken (für Rücksprung) ldy #$07 ;Schleife rückwärts von Bit 7-0 binoutloop ldx #"0" ;Zeichen "0" ins X-Register asl ;Akku nach <-links verschieben bcc out ;Wenn es eine 0 ist, direkt zur Ausgabe, inx ;sonst erhöhen, damit wir eine 1 haben. out ;Sprungziel, wenn wir eine 0 haben pha ;Akku merken, wird überschrieben (CHROUT) txa ;X -> Akku; Zeichen in den Akku jsr CHROUT ;Jump-Table: Zeichenausgeben pla ;Akku fürs nächste Bit wiederholen dey ;Schleife runterzählen bpl binoutloop ;bis 8-Bit verarbeitet sind -> binoutloop pla ;sonst, den Akku wiederherstellen rts ;und zurück zum Aufrufer (jsr binaryout) ;************************************************************ ;*** Den Inhalt des Akkus als Hexzahl auf dem BS ausgeben ;************************************************************ ;*** Übergabe: A = Zahl, die ausgegeben wird ;*** X = Zeile in der die Ausgabe beginnt ;*** Y = Spalte in der die Ausgabe beginnt ;************************************************************ ;*** Rückgabe: - ;************************************************************ ;*** ändert: X,Y,SR ;************************************************************ hexout pha ;Akku auf dem Stack merken (wg. SETCURSOR) clc ;C=0 für set / C=1 für get Cursor jsr SETCURSOR ;Jump-Table: get/set cursor lda #"$" ;Dollar-Zeichen in den Akku jsr CHROUT ;und ausgeben pla ;Akku wiederherstellen pha ;und merken bis zum Rücksprung pha ;gleich nochmal wg. LSR und CHROUT lsr ;jetzt msb -> ins lsb verschieben lsr ;dafür 4* LSR lsr lsr tax ;Akku ins X-Register, lda possiblehexchars,x ;das Zeichen fürs obere Nibble holen jsr CHROUT ;und auszugeben pla ;Akku vom Stack holen and #$0F ;oberes Nibble ausmaskieren tax ;Akku wieder nach X lda possiblehexchars,x ;das Zeichen fürs untere Nibble holen jsr CHROUT ;und wieder ausgeben pla ;Ursprünglichen Akkuwert vom Stack holen rts ;und zurück zum Aufrufer (jsr hexout) ;*** die Hex-Ziffern possiblehexchars !text "0123456789ABCDEF"
Beachtet beim Turbo Assembler wieder, die Besonderheiten, z. B. max. 15 Zeichen für die Label und keine Unterstriche!