Die XY-Ladebefehle
Wir haben ja schon die beiden unmittelbaren Ladebefehle für das X- und Y-Register verwendet. Kommen wir hier zu den restlichen.
Dieser Beitrag baut auf dem vorherigen Die Akku-Ladebefehle auf und führt das dort entwickelte Programm weiter.
Unter Verwendung des bisher gelernten verändern wir zunächst unser Programm (farbige ♠-Symbole ausgeben) so, dass jetzt der komplette BS gefüllt wird.
Schleifen, die 255 Zeichen auf dem BS ausgeben, haben wir ja schon einige verwendet, aber wie können wir nun den gesamten BS mit Pik-Zeichen füllen? Der BS besteht aus 25 Zeilen mit je 40 Zeichen, also müssen wir 1000 Zeichen anzeigen. Da das X- / Y-Register nur 8-Bit breit ist, werden wir nur mit einem Register keine Schleife bauen können, die 1000mal durchlaufen wird. Also muss eine andere Lösung her. Bekanntlich führen viele Wege ins ROM nach Rom, wir werden die Zero-Page, verwenden um diese Schleife zu realisieren.
Nun aber zum ersten Code-Abschnitt.
;*** Variablen SCREENRAM = $0400 ;Start des Bildschirmspeichers CHAR = $41 ;Pik-Zeichen für die Ausgabe COLORRAM = $d800 ;Start des Farb-RAMs COLORNO = $00 ;Schwarz ($00) als Zeichenfarbe SCREENZEROADR = $fb ;Zero-Page Adr. für BS-Speicher Adr. COLORZEROADR = $fd ;Zero-Page Adr. für Farb-RAM Adresse ;*** Startadresse BASIC-Zeile *=$0801 !byte $0c,$08,$e2,$07,$9e,$20,$32,$30,$36,$32,$00,$00,$00 ;*** Start des Assemblerprogrammes startadr
Neu sind hier eigentlich nur die beiden Variablen:
SCREENZEROADR = $fb ;Zero-Page Adr. für BS-Speicher Adr. COLORZEROADR = $fd ;Zero-Page Adr. für Farb-RAM Adresse
Diese beiden Variablen werden wir verwenden, um die Basisadresse des BS-Speichers und des Farb-RAMs in der Zero-Page abzulegen. Machen wir das doch nun einfach:
;*** Bildschirmspeicher in die Zero-Page lda #<SCREENRAM
Hier laden wir das LSB der Adresse des BS-Speichers in den Akku. Das kennen wir schon vom letzten Beitrag, dort haben wir nur statt der Variablen SCREENRAM das Label farbadr verwendet. Man kann also auch von Variablen das LSB und MSB bilden lassen.
sta SCREENZEROADR
Nun legen wir das LSB aus dem Akku an der in SCREENZEROADR hinterlegten Adresse $fb ab.
lda #>(SCREENRAM+$0300) ;auf der letzten Seite beginnen
Da unsere Schleife wieder rückwärts laufen soll, müssen wir zur Startadresse des BS-Speichers noch den Speicherbedarf für drei weitere Pages addieren. Um den korrekten Ausgangswert zu erhalten, setzen wir vor der Ermittlung des MSB (würde natürlich auch fürs LSB gelten, wenn wir dort etwas Rechnen) die Rechen-Operationen in Klammern (SCREENRAM+$0300). Da jede Page, wie wir bereits wissen, 256 Bytes groß ist, müssen wir also 3*256 = 3*$100 = $0300 addieren. Anschließend holt sich der Assembler das MSB ($07, da $0400 + $0300 = $0700).
sta SCREENZEROADR+1
Als nächstes speichern wir das MSB vom Akku an die nächste Adresse auf der Zero-Page. Dazu addieren wir einfach 1 zur Variablen SCREENZEROADR und landen somit bei $fc.
Da das Farb-RAM nahezu identisch angesprochen wird, machen wir das ganze nochmal, nur mit anderen Variablen.
;*** Farb-RAM in die Zero-Page lda #<COLORRAM sta COLORZEROADR lda #>(COLORRAM+$0300) sta COLORZEROADR+1
Jetzt müssen wir unseren Schleifen-Zähler initialisieren.
ldx #$04 ;Page-Anzahl pageloop ldy #$ff ;Schleifenanzahl
Im Source nichts Neues. Wir merken uns im X-Register die benötigte Page-Anzahl. Dann brauchen wir ein Label pageloop zu dem wir für jede Page zurückspringen und im Y-Register merken wir uns die Anzahl der auszugebenen Zeichen für die jeweilige Page. Dies steht hinter dem Label pageloop, da wir bei jeder Page wieder dieselbe Anzahl an Zeichen ausgeben möchten.
loop lda #CHAR ;Zeichen in den Akku laden
Nachdem wir unser Zeichen in den Akku geholt haben, müssen wir dieses jetzt ausgeben.
sta (SCREENZEROADR),y ;Akku auf dem BS ausgeben
STA indirekt Y-nach-indiziert ($91, 2B, 6T, <keine>)
Die indirekte Adressierung mit Y-Register kennen wir schon vom LDA-Befehl. Wie wir hier sehen gibt es auch eine STA-Variante. Sie funktioniert genauso: Es wird von der angegebenen Zero-Page-Adresse (hier $FB aus der Variablen SCRENNZEROADR) die dort zufindende Adresse (hier $0700, beachtet dass wir rückwärts gehen und oben auf die Adresse $0400 noch den Wert $0300 addiert haben) geholt, dazu wird der Inhalt des Y-Registers addiert und dann an der so ermittelten Adresse der Inhalt des Akkus abgelegt. Da wir das Y-Register bei jedem Durchlauf verringern, füllen wir somit unsere letzte Seite von hinten nach vorne.
lda #COLORNO ;Farbe in den Akku laden sta (COLORZEROADR),y ;Akku ins Farb-RAM schreiben
Statt wie bisher zwei getrennte Schleifen für Zeichen und Farbe zu verwenden, packen wir jetzt beides in eine. Daher laden wir oben unsere Farbe in den Akku und geben diese dann wieder mit dem eben gelernten STA-Befehl (indirekt Y-nach-indiziert) aus. Diesmal schreiben wir den Akku-Inhalt aber natürlich ins Farb-RAM.
Anschließend muss das Y-Register um eins verringert werden.
dey
DEY: DEcrement Y-Register (den Inhalt vom Y-Register um 1 verringern)
DEY implizit ($88, 1B, 2T, NZ)
Der Befehl macht exakt dasselbe, wie der DEX-Befehl. Nur eben mit dem Y-Register. Wir zählen hier also das Y-Register ums eins herunter, um im nächsten Durchlauf unser Zeichen und unsere Farbe an der nächst niedriegeren BS-Position auszugeben.
bne loop
Wenn das Y-Register nicht 0 ist, dann springen wir zum Label loop und geben das nächste Zeichen aus.
;*** MSB für SCREENRAM & COLORRAM um 1 verringern
Kommen wir zum letzten Decrement-Befehl.
dec SCREENZEROADR+1
DEC: DECrement (Byte an der angegebenen Adresse um 1 verringern)
DEC Zero-Page ($C6, 2B, 5T, NZ)
Im Gegensatz zum DEX– und DEY-Befehl benötigt DEC allerdings eine Adresse, da wir hier direkt auf den Speicher zugreifen. Daher stimmt die obige Aussage auch nicht ganz, denn es gibt insgesamt vier Varianten des DEC-Befehls (die restlichen drei sehen wir uns später an). Hier verringern wir das MSB unserer Bildschirmspeicher-Adresse um 1.
Wie schon weiter oben beim STA-Befehl erklärt, finden wir an der Zero-Page Adresse $fb die in SCREENZEROADR gespeichert ist, die Adresse der letzten Page des Bildschirmspeichers $0700. Da wir mittlerweile gute Fortschritte gemacht haben, wissen wir auch, dass im Speicher erst das LSB (an $fb) und anschließend das MSB (an $fc, daher SCREENZEROADR+1) gespeichert wird. Also bezieht sich der DEC-Befehl auf die Zero-Page-Adresse $fc. Durch das DEC wird daher aus der Adresse $0700 jetzt $0600 und wir haben unsere nächste Page im Bildschirmspeicher.
dec COLORZEROADR+1 ;*** wenn es noch eine gibt, dann die nächste Page abarbeiten dex bne pageloop rts
Um die nächste Page im Farb-RAM anzusteuern, benutzen wir den DEC-Befehl erneut, diesmal natürlich mit der Position des MSB der Farb-RAM-Adresse auf der Zero-Page. Danach verringern wir das X-Register und springen solange dies noch nicht Null ist wieder zum Label pageloop. Sind alle Pages verarbeitet worden, springen wir mit RTS zurück zum BASIC.
Bevor wir endlich zu den Lade-Befehlen kommen, nochmal das komplette Listing:
;*** Variablen SCREENRAM = $0400 ;Start des Bildschirmspeichers CHAR = $41 ;Pik-Zeichen für die Ausgabe COLORRAM = $d800 ;Start des Farb-RAMs COLORNO = $00 ;Schwarz ($00) als Zeichenfarbe SCREENZEROADR = $fb ;Zero-Page Adr. für BS-Speicher Adr. COLORZEROADR = $fd ;Zero-Page Adr. für Farb-RAM Adresse ;*** Startadresse BASIC-Zeile *=$0801 !byte $0c,$08,$e2,$07,$9e,$20,$32,$30,$36,$32,$00,$00,$00 ;*** Start des Assemblerprogrammes startadr ;*** Bildschirmspeicher in die Zero-Page lda #<SCREENRAM sta SCREENZEROADR lda #>(SCREENRAM+$0300) ;auf der letzten Seite beginnen sta SCREENZEROADR+1 ;*** Farb-RAM in die Zero-Page lda #<COLORRAM sta COLORZEROADR lda #>(COLORRAM+$0300) sta COLORZEROADR+1 ldx #$04 ;Page-Anzahl pageloop ldy #$ff ;Schleifenanzahl loop lda #CHAR ;Zeichen in den Akku laden sta (SCREENZEROADR),y ;Akku auf dem BS ausgeben lda #COLORNO ;Farbe in den Akku laden sta (COLORZEROADR),y ;Akku ins Farb-RAM schreiben dey bne loop ;*** MSB für SCREENRAM & COLORRAM um 1 verringern dec SCREENZEROADR+1 dec COLORZEROADR+1 ;*** wenn es noch eine gibt, dann die nächste Page abarbeiten dex bne pageloop rts
Außer den bereits bekannten Punkten (z. B. Zeilenumbrüche, Hintergrundfarbe, Kleinbuchstaben usw.), ist beim Turbo Assembler nichts weiter zu beachten.
Endlich haben wir es geschafft, unser Programm läuft!
Oder????
Hmmm, da scheint etwas nicht zu stimmen.
Das READY. ist klar, das kommt daher, dass unser Programm am Schluß zurück zum BASIC springt. Aber die anderen vier Lücken, sollten nicht sein.
Habt ihr das Problem bereits erkannt?
Unser Programm soll ja nacheinander vier Pages füllen. Wir füllen jede Page, indem wir ins Y-Register #$ff schreiben und herunterzählen, bis das Y-Register Null ist. Somit wird unsere Schleife 255 mal durchlaufen, es fehlt also das letzte Byte.
Jetzt könnten wir natürlich zum Schluß, direkt hinter dem bne loop das fehlenden Zeichen in die jeweilige Page schreiben:
dey bne loop ;*** 256. Zeichen mit Y=0 speichern! ; lda #CHAR ; sta (SCREENZEROADR),y ; lda #COLORNO ; sta (COLORZEROADR),y
Das wäre aber mit Kanonen auf Spatzen schießen!
Die Lösung ist viel einfacher. Wir schreiben zu Beginn jeder Seite einfach #$00 statt #$ff ins Y-Register. Die Prüfung, ob das Y-Register Null ist, findet ja erst nach dem DEY (s. oben) statt. Wenn jetzt beim ersten Mal #$00 um eins verringert wird, erhalten wir #$ff (s. „Wie der Rechner rechnet“) und unsere Schleife wird wie vorgesehen insgesamt 256 mal durchlaufen. Das hierdurch zuerst das erste Zeichen der Page (da das Y-Register ja den Wert #$00 hat) und anschließen alle restlichen (nach #$00 folgt, wie beschrieben durchs DEY ja #$ff) rückwärts gesetzt werden, stellt für uns kein Problem dar.
pageloop ;ldy #$FF <- löschen ldy #$00 <- NEU
Wenn ihr das Programm jetzt startet läuft alles wie es soll.
2. ODER???
Unsere BS-Ausgabe sieht doch wie geplant aus. Also, was will der Typ dann noch?
Wer sich alles nochmal genau anschaut und überdenkt, kommt evtl. auf das noch offene Problem.
Es liegt im nicht sichtbaren Bereich hinter dem Bildschirmspeicher. Wir beschreiben 4 Pages mit je 256 Bytes (also 1024 Bytes) der BS-Speicher benötigt aber nur 1000Bytes, somit überschreiben wir 24 Bytes zu viel. Für unser Beispiel ist das kein Problem, aber hinter dem BS-Speicher folgen z. B. acht Register in denen der Speicherblock eingetragen wird, an dem die Spritedaten für den VIC-II zu finden sind. Da das Farb-RAM von uns identisch beschrieben wird, tritt dort natürlich ein ähnliches Problem auf.
Lösen wir das Problem einfach nach guter alter Brute-ForceInfoEin Problem lösen, ohne auf die Effizienz zu achten. Es klappt dann zwar, ist aber evtl. nicht so schön.-Manier.
Beim ersten Lauf (dort wird ja die 4. Page gefüllt), schreiben wir einfach 24 Bytes weniger weg. Dafür ziehen wir von $ff $18 (also 24 dezimal) ab. Außerdem müssen wir, da nun das Zeichen an Y-Register = 0 nicht mehr geschrieben wird, dieses noch manuell machen und schließlich muss das Y-Register für alle anderen Seiten wieder auf #$00 gesetzt werden. Hier der dazugehöriger Code-Abschnitt (die Änderungen sind farbig hervorgehoben):
lda #>(COLORRAM+$0300) sta COLORZEROADR+1 ;*** 1. Zeichen auf der 4. Page setzen ldy #$00 ;Position 0 fürs 1. Zeichen lda #CHAR sta (SCREENZEROADR),y lda #COLORNO sta (COLORZEROADR),y ldx #$04 ;Page-Anzahl ldy #$ff-$18 ;Schleifenanzahl (1. Durchlauf) pageloop loop lda #CHAR ;Zeichen in den Akku laden sta (SCREENZEROADR),y ;Akku auf dem BS ausgeben lda #COLORNO ;Farbe in den Akku laden sta (COLORZEROADR),y ;Akku ins Farb-RAM schreiben dey bne loop ;*** MSB für SCREENRAM & COLORRAM um 1 verringern dec SCREENZEROADR+1 dec COLORZEROADR+1 ldy #$00 ;Schleifenanzahl (x. Durchlauf) ;*** wenn es noch eine gibt, dann die nächste Page abarbeiten dex bne pageloop rts pagecount !byte $03, $04, $00, $01, $02 charcount !byte $ff, $00, $e7, $80
Unser Label pageloop ist nun eigentlich überflüssig, aber da es nur ein Platzhalter für den Assembler ist, hat es auf unser fertiges Programm keine negativen Auswirkungen.
Wer kann mir denn jetzt noch sagen, warum wir auf das ldy #$00 verzichtenLösungBei jedem Durchlauf verringert dey den Ihnhalt des Y-Registers, erreicht es 0 springt bne loop: nicht mehr nach oben. Somit behält das Y-Register seinen Wert von 0 und wir können auf das erneute Setzen verzichten. können?
Die beiden neuen Label (pagecount und charcount) mit den !byte-Anweisungen, verwenden wir gleich für unsere Übungen.
Soviel (OK es war schon sehr viel) zu den Vorbereitungen. Der Bildschirm sollte jetzt voller Piks sein und es werden nur 1000 Bytes beschrieben.
Ihr könnt bis auf weiteres, bei jeder Trennlinie, das Programm einmal erstellen. Es sollten immer 1000 ♠-Symbole erscheinen, aber jedesmal werden die benötigten Werte auf eine andere Art ermittelt. Bis auf die bekannten Punkte, gibt es beim Turbo Assembler auch hier nichts zu beachten.
Da die LDX– und LDY-Befehle nahezu identisch sind, stelle ich sie jeweils im Doppelpack vor.
Wir ändern nun ldx #$04 ldy #$ff-#$18
Statt die #$04 direkt ins X-Register zu laden, verwenden wir:
ldx pagecount+1
LDX absolut ($AE, 3B, 4T, NZ)
Wir laden das X-Register mit dem Byte, dass an der angegebenen Adresse zu finden ist. Genau so, wie beim LDA mit absoluter Adressierung. Um unsere #$04 zu erhalten, müssen wir noch eins zur Adresse hinzuzählen.
Um unser Y-Register für den ersten Durchlauf zu füllen:
ldy charcount+2
LDY absolut ($AC, 3B, 4T, NZ)
Macht das Selbe, wie der LDX-Befehl von eben, nur mit dem Y-Register. Auch hier müssen wir etwas zur Adresse hinzuaddieren (+2) um auf unseren bisherigen Wert #$E7 = #$FF-#$18 zu kommen.
Auch so ist unser Bildschirm wieder voller ♠-Symbole.
Noch eine Variante:
Neben der absoluten Adressierung gibt es, wie beim LDA, auch eine indizierte Adressierung. Dabei kann das X-Register mit Hilfe des Y-Registers geladen werden und umgekehrt.
Ein Laden mit Hilfe des Akkumulators ist nicht möglich!
; ldx pagecount+1 <- LÖSCHEN ldy #$01
ldx pagecount,y
LDX absolut Y-indiziert ($BE, 3B, 4-5T, NZ)
Füllt das X-Register mit dem Wert, der an der angegebenen Adresse zzgl. dem Inhalt des Y-Registers zu finden ist. Auch das kennen wir schon vom LDA-Befehl. Normal werden 4 Taktzyklen benötigt, bei Überschreitung der Page-Grenze ist es einer mehr.
Um das Y-Register mit X-indiziert zu füllen müssen wir uns den Inhalt des X-Registers merken, schließlich haben wir uns eben mühevoll den richtigen Wert herausgesucht.
--> hinter! ldy #$01 ldx pagecount,y --> kommt...
txa
TXA: Transfer X-Register to Akku (Inhalt des X-Registers in den Akku kopieren)
TXA implizit ($8A, 1B, 2T, NZ)
Wir merken uns unseren im X-Register gespeicherten Wert, indem wir ihn in den Akku kopieren (OK, wir hätten oben auch direkt den Akku füllen können, aber schließlich ist das hier eine Übung). Es gibt insg. sechs dieser Transferbefehle. Sie dienen dazu, den Inhalt verschiedener Register untereinander auszutauschen.
Nun können wir das X-Register verwenden, um mit seiner Hilfe das Y-Register zu setzen.
; ldy charcount:+2 <- LÖSCHEN ldx #$02
ldy charcount,x
LDY absolut X-indiziert ($BC, 3B, 4-5T, NZ)
Macht das Gleiche wie sein LDX Gegenstück von eben. Es wird das Y-Register mit dem Wert, der an der angegebenen Adresse plus Inhalt des X-Registers zufinden ist, gefüllt. Auch hier gilt, beim Überschreiten der Page-Grenze sind es 5 Taktzyklen, sonst nur 4.
Damit unser X-Register wieder den gewünschten Wert enthält, müssen wir jetzt noch den Akku-Inhalt zurück ins X-Register kopieren.
tax
TAX: Transfer Akku to X-Register (Inhalt des Akkus ins X-Register kopieren)
TAX implizit ($AA, 1B, 2T, NZ)
Alles wie beim TXA-Befehl, nur dieses Mal wird der Inhalt vom Akku ins X-Register kopiert.
Nach all der Mühe, sieht wieder alles, wie bisher aus, aber wir haben einige neue Befehle kennengelernt.
Auch für die Index-Register besteht die Möglichkeit direkt den Inhalt einer Zero-Page-Adresse zu laden.
; ldy #$01 <- Kurz auskommentieren lda #$01 ;gewünschter Wert in den Akku sta $ff ;Akku-Inhalt in letztes Byte der Zero-Page
ldy $ff
LDY Zero-Page ($A4, 2B, 3T, NZ)
Wir laden das Y-Register mit dem Wert (hier #$01), der an der angegebenen Zero-Page-Adresse (bei uns $ff) steht. Zum Vergleich evtl. noch mal beim LDA-Befehl nachschauen.
ldy #$01 <- wieder einkommentieren lda #$02 ;gewünschter Wert in den Akku <- von #$01 in #$02 ändern ; ldy $ff <- LÖSCHEN ; ldx #$02 <- auskommentieren
ldx $ff
LDX Zero-Page ($A6, 2B, 3T, NZ)
Wir laden das X-Register mit dem Wert (hier #$02), der an der angegebenen Zero-Page-Adresse (bei uns $ff) steht. Also exakt das Gleiche, wie eben beim LDY-Befehl.
Kommen wir zu den letzten beiden Befehlen, es handelt sich sich um die indizierte Zero-Page-Adressierung.
; ldx $ff <- LÖSCHEN ldy #$0f
ldx $f0,y
LDX Zero-Page Y-indiziert ($B6, 2B, 4T, NZ)
Wir laden das X-Register mit dem Wert (hier #$02), der an der angegebenen Zero-Page-Adresse (bei uns $f0) plus dem Inhalt des Y-Registers #$0f steht, also wieder von der Zero-Page-Adresse $ff.
Das abschließende Beispiel für die LDY-Variante spare ich mir, oder? Wer mag, kann ja an die Zero-Page-Adresse $ff eine #$01 schreiben, das X-Register mit #$0f füllen und dann den folgenden Befehl verwenden:
ldy $f0,x
LDY Zero-Page X-indiziert ($B4, 2B, 4T, NZ)
Der macht wieder exakt das Gleiche, wie eben.
Lasst uns abschließend noch eine Tastaturabfrage einbauen, damit wir unsere Ausgabe in Ruhe geniessen können. Wir werden aufs Drücken von F1 warten.
Fügt die nächsten Zeilen bitte am Programmende nach dem BNE pageloop und vor dem RTS-Befehl ein.
sei ; Interrupts sperren
SEI: SEt Interrupt-Flag (Interrupt-Flag setzen, auf 1 setzen)
SEI implizit ($78, 1B, 2T, I)
Wir setzen das Interrupt-Flag, um zu verhindern, dass unsere Tastaturabfrage evtl. durch eine andere, per Interrupt ausgelöste, gestört wird. Durch Interrupts können Programmfunktionen abhängig von Systemereignissen angesprungen werden. Sobald wir das Interrupt-Flag mit SEI setzen, wird uns kein Interrupt mehr unterbrechen.
lda #%11111110 ; Spalte 0 (Col 0) der Tastatur-Matrix testen
Mit dem bekannten LDA-Befehl laden wir die zu prüfende Spalte der Tastaturmatrix in den Akku. Die gewünschte Spalte muss auf 0 gesetzt werden. Durch die binäre Schreibweise können wir im Source besser erkennen, welche Spalte wir gewählt haben. Da unsere F1-Taste in der Spalte 0 liegt, müssen wir diese auswählen.
sta $dc00 ; Spaltenregister schreiben
STA absolut ($8D, 3B, 4T, <keine>)
Wir legen den Akku-Inhalt (unsere zu prüfende Spalte der Tastaturmatrix) an der Adresse $dc00 ab. Dies ist das erste Register des CIA-1, der Teil der Ein-/Ausgabe ist.
getkeyloop lda $dc01 ; Zeilenregister auslesen
Der CIA-1 legt an der Adresse $dc01 die Info ab, welche Taste (der in $dc00 gespeicherten Spalte) gedrückt ist. Wir laden diesen Wert in den Akku, um gleich zu kontrollieren, ob unsere F1-Taste dabei ist.
and #%00010000 ; Zeile 1 (Row 4) ausmaskieren
AND: boolean AND (bitweises UND)
AND unmittelbar ($29, 2B, 2T, NZ)
Eine bitweise UND-Verknüpfung (s. Boolsche Algebra) mit dem Akku und dem übergebenen Wert durchführen. Ist das Ergebnis null oder negativ, so wird das entsprechende Flag gesetzt.
Im Akku steht jetzt, in welcher Zeile, der zu prüfenden Spalte, eine Taste gedrückt wurde. Da die F1-Taste in der vierten Zeile (die Zählung beginnt bei 0) liegt, prüfen wir mit #%00010000. Wurde keine Taste gedrückt, so steht das entsprechende Bit auf 1, wurde eine gedrückt, nimmt das Bit Null an und unser AND-Befehl ergibt dann auch 0.
im Akku: %11101111 <- Taste in der 4. Zeile gedrückt vom AND: %00010000 --------- %00000000 -> Z-Flag ist gesetzt und BNE springt nicht!
bne getkeyloop ; Warte bis Taste gedrückt (Akku = 0)
So lange das AND kein Ergebnis von 0 im Akku hinterlässt springen wir wieder zum Label getkeyloop und warten weiter auf das Drücken der F1-Taste.
cli ; Interrupts wieder zulassen
CLI: CLear Interrupt-Flag (Interrupt-Flag löschen, auf 0 setzen)
CLI implizit ($58, 1B, 2T, I)
Wir müssen natürlich die Interrupts wieder aktivieren. Dazu löschen wir mit CLI das Interrupt-Flag und schon sind die Interrupts wieder möglich.
Startet das Programm und ihr könnt unsere Ausgabe ungestört betrachten, bis ihr F1 drückt.
;*** Variablen SCREENRAM = $0400 ;Start des Bildschirmspeichers CHAR = $41 ;Pik-Zeichen für die Ausgabe COLORRAM = $d800 ;Start des Farb-RAMs COLORNO = $00 ;Schwarz ($00) als Zeichenfarbe SCREENZEROADR = $fb ;Zero-Page Adr. für BS-Speicher Adr. COLORZEROADR = $fd ;Zero-Page Adr. für Farb-RAM Adresse ;*** Startadresse BASIC-Zeile *=$0801 !byte $0c,$08,$e2,$07,$9e,$20,$32,$30,$36,$32,$00,$00,$00 ;*** Start des Assemblerprogrammes startadr ;*** Bildschirmspeicher in die Zero-Page lda #<SCREENRAM sta SCREENZEROADR lda #>(SCREENRAM+$0300) ;auf der letzten Seite beginnen sta SCREENZEROADR+1 ;*** Farb-RAM in die Zero-Page lda #<COLORRAM sta COLORZEROADR lda #>(COLORRAM+$0300) sta COLORZEROADR+1 ;*** 1. Zeichen auf der 4. Page setzen ldy #$00 ;Position 0 fürs 1. Zeichen lda #CHAR sta (SCREENZEROADR),y lda #COLORNO sta (COLORZEROADR),y ; ldy #$01 lda #$01 ;gewünschter Wert in den Akku sta $ff ;Akku-Inhalt in letztes Byte der Zero-Page ldy $ff ldx pagecount,y txa ldx #$02 ldy charcount,x tax pageloop loop lda #CHAR ;Zeichen in den Akku laden sta (SCREENZEROADR),y ;Akku auf dem BS ausgeben lda #COLORNO ;Farbe in den Akku laden sta (COLORZEROADR),y ;Akku ins Farb-RAM schreiben dey bne loop ;*** MSB für SCREENRAM & COLORRAM um 1 verringern dec SCREENZEROADR+1 dec COLORZEROADR+1 ldy #$00 ;Schleifenanzahl (x. Durchlauf) ;*** wenn es noch eine gibt, dann die nächste Page abarbeiten dex bne pageloop sei ; Interrupts sperren lda #%11111110 ; Spalte 0 (Col 0) der Tastatur-Matrix testen sta $dc00 ; Spaltenregister schreiben getkeyloop lda $dc01 ; Zeilenregister auslesen and #%00010000 ; Zeile 1 (Row 4) ausmaskieren bne getkeyloop ; Warte bis Taste gedrückt (Akku = 0) cli ; Interrupts wieder zulassen rts pagecount !byte $03, $04, $00, $01, $02 charcount !byte $ff, $00, $e7, $80
Wenn ihr bis hier durchgehalten habt, danke!
Ich weiß nicht wie es euch geht, aber ich kann keine Pik-Symbole mehr sehen! Daher werden die nächsten Befehle mit etwas anspruchsvolleren Beispielen erklärt.
Einfach toll hier. Ein Super Tutorial wurde mit weiterem Feedback nochmals verbessert. Ich bin jetzt Jahre später dran und die Fehler sind raus und mit C64Studio und der dazu passenden Einführung funktioniert dass Super.
Einfach grandios, sowas tolles hätte ich mir früher mal gewünscht und natürlich für den Amstrad 😉
Bissl spät dran, aber ich wurschtel mich gerade durch den Kurs und hab meinen Spaß.
Vielen Dank für Deine Mühe Jörn!
An der Stelle “Wer mag, kann ja…”, muss es nicht statt “ldy #$f0,x” besser “ldy $f0,x” heißen? Denn wir wollen ja #$01 aus Zero Page $ff laden (mit $f0 + $0f aus dem X-Register) und nicht #$ff in das Y-Register (was mit dem Befehl auch gar nicht geht wie C64 Studio mir freundlicherweise mitgeteilt hat).
Alternativ schließe ich einen Denkfehler meinerseits nicht aus und lerne gern dazu.
Ich arbeite brav alles durch, mache mir meine eigenen Notizen und komme so ganz langsam in das Thema Assembler rein, was ich vor 30 Jahren nicht angerührt habe und bei Basic geblieben bin 😉
Tolles Tutorial, wirklich! Bin gespannt was mich noch erwartet!
Hallo Tobi,
da hast du vollkommen recht, die Raute # hatte dort absolut nichts zu suchen.
Ich habe die Stelle eben korrigiert, vielen Dank für den Hinweis.
Gruß,
Jörn
Hi Jörn,
in dem Codeschnipsel
> dex
> bne loop
>
>;*** 256. Zeichen mit Y=0 speichern!
>; lda #CHAR
>; sta (SCREENZEROADR),y
>; lda #COLORNO
>; sta (COLORZEROADR),y
>
>Das wäre aber mit Kanonen auf Spatzen schießen!
muß es in der ersten Codezeile ‘dey’ anstelle von ‘dex’ heißen.
Nur ein kleiner Rechtschreibfehler; nichts schlimmes. Wollte es Dir trotzdem mitteilen. 🙂
Viele Grüße,
Achim
vielen Dank für den Hinweis, ich habe es eben korrigiert.
Gruß,
Jörn
Es wäre schön wenn man zum Schluss, dass gesamte fertige Programm noch einmal sehen könnte. Das Springen von hinzufügen und wieder löschen von Programmzeilen ist manchmal schwer nachzuvollziehen (geht das nur mir so?)
Ich bekomme immer die Meldung beim Start “Main CPU :JAM at $01FF
Ich kann zwar das Programm weiter laufen lassen, aber er fragt die Tastatur Eingabe nicht ab.
Dennoch vielen Dank für die Mühe, die Du dir hier gibst.
ich habe eben nochmal alles durchgespielt und prinzipiell geht es, irgendwo hat sich bei dir wohl ein Tippfehler eingeschlichen.
Im Beitrag gibt es jetzt aber zwei weitere Male, das komplette Listing. In der Mitte und ganz am Ende.
Außerdem habe ich den Text mit der RETURN-Taste noch etwas erweitert.
So solltest du eigentlich das Problem mit dem CPU: JAM finden.
Super danke Dir. Werde noch einmal alles überprüfen.
“Wenn ihr bis hier durchgehalten habt, danke!”
Ich hab zu danken, ich kapier es 🙂 Ok, ich habe auch etwas Erfahrung im x86 Assembler. Aber das Ganze hier verdeutlicht, wie dämlich die Idee ist einen 8 Bit Prozessor zu bauen, der nicht mit seinen Registern den eigenen Addressbus abdecken kann 😀 Ich mache die Beispiele immer schön mit. Diese Zerstückelung von wegen: “Addresse in den Ram legen um mit dem Ram den Ram zu addressieren”, … ich sags mal so, ich hoffe mal, dass die Verwirrung mit der Zeit nachlässt.
Freut mich, wenn es etwas geholfen hat.
Geringfügig besser wird es mit dem 65816 aus der SuperCPU, der verwendet alle 256 möglichen BYTES für Befehle und bietet dann eine Reihe neuer Adressierungsarten.
“Endlich haben wir es geschafft, unser Programm läuft!
Oder????”
Leider nein. Ich bekomme die folgenden Errors und weiß nicht, was ich dagegen tun kann:
[Error ] Line 19:Invalid operand, label or variable “lda #>SCREENRAM+$0300 ;auf der letzten Seite beginnen” – D:\projekte\games\C64\NewProject\test2.asm
[Error ] Line 25:Invalid operand, label or variable “lda #>COLORRAM+$0300” – D:\projekte\games\C64\NewProject\test2.asm
Ich bin gerade ein bisschen ratlos. Hoffentlich kannst du mir weiterhelfen, ich will nämlich mit diesem großartigen Tutorial fortfahren. 😉
Cyber-Magische Grüße
Jacob
Hallo Jacob,
danke für das ‘Großartig’.
Sehr wahrscheinlich verwendest du das CBM prg Studio 2.7.0 oder höher.
Dort wurde leider das Vorgehen für die Berechnung der Hi-/Lo-Werte geändert (s. CBM prg Studio 2.7.0).
Ab 2.8.0 kannst du das Problem mit der Assemblerdirektive ‘Operator Calc’ umgehen, sonst hilft nur das Umstellen der Optionen (s. Link).
Ich habe oben auch noch einen Hinweis hinzugefügt.
Gruß,
Jörn
Hey, danke für die schnelle Antwort! Jetzt funktioniert es super.
Gern geschehen.
Hallo
Ich habe grossen Spass daran, diese Seiten durchzulesen und mich in die Assemblerprogrammierung des C64 einzuarbeiten.
Ich habe dabei einen kleinen Fehler gefunden: Zwischen dem “Oder????” und dem “2. Oder????” wird einige Male auf das X-Register verwiesen, das angeblich von $FF auf Null zählt. Tatsächlich übernimmt das Y-Register diese Aufgabe.
Gruss
Thomas
Hallo Thomas,
da hast du recht, im X-Register wird die Page gezählt, Y zählt die einzelnen Zeichen.
Vielen Dank für den Hinweis, das wurde eben korrigiert.
Gruß,
Jörn