Erstellt: 1. Februar 2013 (zuletzt geändert: 29. Juni 2020)

Ladebefehle XY-Gelöst

Die XY-Ladebefehle

C64 Studio, AMCE & TASM

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????

Da fehlt doch was…
Da fehlt doch was…

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-ForceinfoInfoEin 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 verzichteninfoLö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.

Für ein Beispiel mit LDX:

 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.


Schrott!!Naja...Geht so...Ganz gut...SUPER! (18 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


ZurückWeiter

16 Gedanken zu „Ladebefehle XY-Gelöst“

  1. 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 😉

  2. 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!

    1. 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

  3. 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

  4. 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.

    1. Hi,
      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.

  5. “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.

    1. 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.

  6. “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

    1. 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

  7. 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

    1. 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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Protected by WP Anti Spam