8-Bit-Breadboard-Computer auf Basis einer 6502-CPU - Assembler-Programmierung: Dezimal Hexadezimal Binär Konverter und Spiel
Bisherige Artikel dieser Serie - hier könnt ihr nochmal alle Grundlagen nachlesen, falls ihr jetzt erst einsteigt:- Digitale Logik und Logikgatter einfach erklärt
- Verwendung des 555-Timer als Taktgeber / Clock
- Das Clock-Modul: Taktgeber für unseren Breadboard-Computer
- Speichertypen und Zugriff auf Speicher
- Erste Schritte mit der CPU
- Eine echte WDC W65C02-CPU
- Das Speicher-Modul: Anbindung von RAM und ROM
- Erstes Programm in Maschinensprache: RAM-Test
- Das Sniffer-Modul: Ein Arduino/STM32 zeigt an, was auf dem Bus los ist.
- Erstes Ausgabegerät: Adressierung und Ausgabe auf 8 LEDs
- Programmiersprache-Evolution: von Maschinensprache zu Assembler
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 1: Taktungsprobleme
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 2: 20 Nanosekunden, die nicht sein sollten
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 3: Assembler-Programme
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 4: neue Adressierungsarchitektur mit 74HC688 und 74HC138
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 5: Programmierung in Assembler
- 3x16 Zeichen LCD DOGM163 als Textausgabe-Gerät, Teil 1: Breadboard-Aufbau und erste Programmversion
- 3x16 Zeichen LCD DOGM163 als Textausgabe-Gerät, Teil 2: Debugging
- Clock-Modul upgraden: höherer Takt (2 KHz) und Frequenzgenerator (bis 160 KHz)
- Erstes Eingabe-Gerät: ein 4x4 Keypad als Tastatur (wird mit 65C22 abgefragt)
- Erweiterung um eine Debug-Anzeige des Prozessorstatus und der Register auf dem LCD
- Die Tastatur spinnt: Hardware-Debugging, Fake-6522 aus China und mehr
Was soll die Software leisten?
- 1. Umrechnung von hexadezimal zu dezimal und binär, dies soll der Computer erledigen
- 2. Umrechnung von dezimal zu hexadezimal und binär, dies soll ebenfalls der Computer erledigen
- 3. Umrechnung von binär zu hexadezimal, als kleines Kopfrechenspiel für den Benutzer
Beim hexadezimal zu dezimal Konverter wird eine zweistellige Hexadezimalzahl (gegebenenfalls mit führenden Nullen) eingegeben. Ungültige Tasten sind abzuweisen. Die Hex-Zahl wird intern zu Binär umgerechnet und auf der LED-Ausgabe angezeigt. Außerdem wird sie nach dezimal umgerechnet und die Dezimalzahl auf der 7-Segment-Anzeige angezeigt.
Beim dezimal zu hexadezimal Konverter wird eine dreistellige Dezimalzahl (gegebenenfalls mit führenden Nullen) eingegeben. Ungültige Tasten sind abzuweisen. Die Dezimal-Zahl wird intern zu Binär umgerechnet und auf der LED-Ausgabe angezeigt. Außerdem wird sie nach hexadezimal umgerechnet und die Hex-Zahl auf der 7-Segment-Anzeige angezeigt.
Im dritten Modus werden Pseudozufallszahlen der Reihe nach auf der LED-Ausgabe in binärer Darstellung angezeigt. Der Spieler soll diese im Kopf nach hexadezimal umrechnen (kleiner Tipp: jeweils 4 LEDs zu einem Nibble gruppieren) und dann zweistellig ggf. mit führender Null) eingeben. Intern wird die Binärzahl nach hexadezimal umgerechnet und mit der Eingabe verglichen. Ist die Eingabe richtig, erhält der Spielen eine Rückmeldung und einen Punkt. Der aktuelle Punktestand wird auf der 7-Segment-Anzeige angezeigt. Ist eine Eingabe falsch, erhält der Spieler die Meldung "Game over" und das Spiel endet.
"Abweisen" einer ungültigen Taste meint: Der Tastendruck wird ignoriert und nicht verwertet. Er wird auch nicht angezeigt. Nur gültige Tastendrucke werden duch Ausgabe des eingegebenen Tastenwertes bestätigt. Für die Eingabe einer Dezimalzahl (maximal 255) muss zum Beispiel folgendes sichergestellt werden:
- nur 0, 1, 2 für die erste Ziffer zulassen
- 0...9 für die 2. Ziffer zulassen, es sei denn, die erste Ziffer ist eine 2, dann maximal 5
- 0...9 für die 3. Ziffer zulassen, es sei denn, die erste Ziffer ist eine 2 und die zweite Ziffer ist eine 5, dann maximal 5
Demonstration des Programmes
Ich will einmal mit dem Ergebnis, dem fertigen Programm anfangen und dies in einem Video zeigen. So ist am einfachsten zu erklären und zu sehen, was das Program leisten soll:
Source-Code
8-hex-dec-bin-spiel.asm (klicken, um diesen Abschnitt aufzuklappen)
; bin-dec-hex Umrechner bzw. kleines Spielchen
; 1. hex to dec/bin (errechnet 6502)
; 2. dec to hex/bin (errechnet 6502)
; 3. bin (LED) to hex (Spieleraufgabe)
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-11
CHIP W65C02S ; (default)
ORG $8000 ; Programmstartadresse (von der CPU aus gesehen)
startvektor:
jmp reset
; !!! === Hinweise === !!!!
; - max 5.3V, sonst kippende Bits in der LED-Ausgabe (und woanders?)
; - max 2Khz, damit der Sniffer noch funktioniert (mitkommt)
; - immer noch ein ungelöstes Problem: Spannungsabfall zwischen den oberen und
; unteren Breadboards, besonders, wenn Verbraucher wie Sniffer angeschaltet sind
; --- Konstanten: Timing ---
; 200 nF Kondensator von 7 Hz bis 2 KHz
; Ein Takt sind 0.5 ms, ein NOP sind zwei Takte sind 1 ms
NopsPerMs: equ 1 ; wieviele NOPs braucht es, um eine Millisek. voll zu machen?
; --- Makro-Definitionen ---
INCLUDE macros.inc
; --- Speicherort-Definitionen ---
INCLUDE io-defs.inc
INCLUDE zeropage-defs.inc
INCLUDE ram-defs.inc
; --- Standard-Definitionen für IO-Geräte ---
INCLUDE seg-defs.inc
INCLUDE lcd-defs.inc
INCLUDE key-defs.inc
; --- allgemeine Unterprogramme und Makros ---
INCLUDE seg.inc ; für 7-Segment-Anzeige
INCLUDE lcd.inc ; für LCD
INCLUDE key.inc ; für 4x4 Keypad
INCLUDE io.inc ; io_init etc.
INCLUDE convert.inc ; valToDez
INCLUDE debug.inc ; Tools zum Debugging
; --- Hauptprogramm ---
reset:
jsr ioInit
menu:
LCD_PRINT msgMenu ; Makro-Aufruf, siehe macros.inc
eingabeMenu:
LCD_CURSOR_ON ; Cursor blinken lassen (Makro)
tasteMenu:
jsr getKey ; speichert Tasten-Bitmuster in keyIn
jsr keyToVal ; liefert in Akku zurück
; richtige Taste gedrückt (1...3) ? (wird nicht extra angezeigt), Cursor lassen wir an
cmp #1
beq startHexToDecBin
cmp #2
beq jmpStartDecToHexBin
cmp #3
beq jmpStartBinToHex ; startBinToHex ist zu weit weg für bra
bra tasteMenu ; falsche Taste: nochmal eingeben
jmpStartBinToHex:
jmp startBinToHex
jmpStartDecToHexBin
jmp startDecToHexBin
startHexToDecBin: ; erledigt der Computer --------------------------------------------
; Hex-Wert eingeben, Dec Wert auf 7-Segment, Bin auf LED-Leiste
LCD_CLEAR ; LCD löschen (Makro)
LCD_PRINT msgEingabeHexToDecBin ; Makro-Aufruf, siehe macros.inc
LCD_POS #26 ; LCD-Cursor positionieren (Makro)
eingabeHexToDecBin: ; gültig 0...F
; 1. Nibble
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_ASC,x
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
jsr keyToVal ; liefert in Akku zurück
sta val ; Highbyte speichern
asl val ; 4x shift left
asl val
asl val
asl val
; 2. Nibble
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_ASC,x
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
LCD_CURSOR_OFF ; Cursor blinken aus (Makro)
jsr keyToVal ; liefert in Akku zurück
ora val ; Low-Byte dazu (ORA = Or mit Akku)
sta val ; Ergebnis zurück nach val schreiben
; in val steht jetzt der Wert in Binär
lda val
sta LED ; den schon mal auf die LEDs ausgeben
jsr valToDez ; und dann in dezimal umrechnen und in dez1...dez3 speichern
jsr dezToSeg ; und auf 7-Segment ausgeben
wai
jmp reset
startDecToHexBin: ; erledigt der Computer --------------------------------------------
; Dez-Wert eingeben, Hex Wert auf 7-Segment, Bin auf LED-Leiste
LCD_CLEAR ; LCD löschen (Makro)
LCD_PRINT msgEingabeDecToHexBin ; Makro-Aufruf, siehe macros.inc
LCD_POS #25 ; LCD-Cursor positionieren (Makro)
eingabeDecToHexBin: ; max 255 als Eingabe zulässig
; 1. Ziffer max 2
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_VAL, x ; Wert für Taste holen
cmp #3
bge eingabeDecToHexBin ; Taste >= 3 (bge==bcs)
; Wert für Key-Muster holen ($0...$F bzw. 0 bis 15)
sta dez1
tax
lda nibble_ASC, x
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
; 2. Ziffer max 9 bzw. 5 (wenn 1. Ziffer 2)
lda dez1
cmp #2
bne eingabeDecToHexBin2
lda #6
sta zptmp
bra eingabeDecToHexBin3
eingabeDecToHexBin2:
lda #$A
sta zptmp
eingabeDecToHexBin3:
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_VAL, x ; Wert für Taste holen
ldy zptmp
cmp zptmp
bge eingabeDecToHexBin3 ; taste >= 6 bzw. A
sta dez2
tax
lda nibble_ASC, x ; Wert für Key-Muster holen ($0...$F bzw. 0 bis 15)
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
; 3. Ziffer max 9 bzw. 5 (wenn 1. und 2. Ziffer 2 und 5)
; steht in zptpm noch 5 drin? dann müssen die ersten 2 Ziffern 25 sein
lda zptmp
cmp #6
beq eingabeDecToHexBin4 ; ersten zwei Ziffern = 25
lda #$A
sta zptmp
bra eingabeDecToHexBin5
eingabeDecToHexBin4:
lda #6
sta zptmp
eingabeDecToHexBin5:
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_VAL, x ; Wert für Taste holen
cmp zptmp
bge eingabeDecToHexBin5 ; taste >= 6 bzw. A
sta dez3
tax
lda nibble_ASC, x
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
LCD_CURSOR_OFF ; Cursor blinken aus (Makro)
jsr dezToVal ; wandelt dez1...dez3 in Wert in val um
lda val
sta LED ; schon mal auf LED ausgeben
jsr valToSegInHex ; nimmt Wert aus Val und gibt ihn als 2 Hex-Ziffern aus
wai
jmp reset
; "Zufallszahlen" zwischen 1 und 255
zufall:
BYTE 238, 217, 161, 74, 27, 237, 50, 152, 68, 66, 4, 4, 138, 247, 106, 176, 85, 238, 169
BYTE 189, 61, 55, 42, 12, 170, 155, 12, 43, 164, 192, 241, 43, 111, 183, 34, 41, 155, 203
BYTE 178, 67, 144, 33, 131, 226, 198, 146, 46, 158, 161, 153, 177, 104, 69, 105, 50, 114
BYTE 110, 189, 207, 221, 248, 115, 190, 153, 76, 242, 253, 151, 36, 58, 126, 177, 25, 159
BYTE 96, 29, 162, 17, 11, 201, 110, 195, 126, 146, 18, 147, 15, 240, 75, 60, 172, 24, 197
BYTE 158, 139, 196, 24, 97, 15
BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0
binToHexScore: equ $1000
binToHexRichtig: equ $1001
binToHexEingabe: equ $1002
startBinToHex: ; Spielchen für den User -------------------------------------------
; eine Zufallszahl wird geholt und in LED angezeigt und in HEX umgerechnet
; der User gibt sein Rechenergebnis ein, ist es richtig, gibt es einen
; Punkt, ansonsten endet das Spiel
stz binToHexScore;
startBinToHex2a:
LCD_CLEAR ; LCD löschen (Makro)
LCD_PRINT msgEingabeBinToHex ; Makro-Aufruf, siehe macros.inc
LCD_POS #26 ; LCD-Cursor positionieren (Makro)
startBinToHex2:
ldy binToHexScore
lda zufall, y ; Zufallszahl holen und auf LED anzeigen
sta LED
beq gewonnenBinToHex ; Zufallszahlen sind mir ausgegangen
sta binToHexRichtig
eingabeBinToHex:
; 1. Nibble
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_ASC,x
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
jsr keyToVal ; liefert in Akku zurück
sta val ; Highbyte speichern
asl val ; 4x shift left
asl val
asl val
asl val
; 2. Nibble
jsr getKey ; speichert Tasten-Bitmuster in keyIn
ldx keyIn
lda KEY_ASC,x
LCD_CHAR_AKKU ; gibt das ASCII-Zeichen im Akku aufs LCD aus (Makro)
LCD_CURSOR_OFF ; Cursor blinken aus (Makro)
jsr keyToVal ; liefert in Akku zurück
ora val ; Low-Byte dazu (ORA = Or mit Akku)
sta val ; Ergebnis zurück nach val schreiben
sta binToHexEingabe
cmp binToHexRichtig ; war das richtig?
bne endeBinToHex
LCD_PRINT msgEingabeBinToHexRichtig
SED
lda binToHexScore
adc #1
sta binToHexScore
sta val
CLD
jsr valToSegInHex ; neuen Score auf 7-Segmentanzeige ausgeben
wai ; warten auf Cont.
jmp startBinToHex2a ; nächste Runde
gewonnenBinToHex:
LCD_CLEAR
LCD_PRINT msgBinToHexGewonnen
stp
endeBinToHex:
LCD_PRINT msgEingabeBinToHexFalsch
stp
stop:
STP ; Hält die CPU an
; --- Unterprogramme ---
delay: ; 1 delay ca. 5 ms
NOP ; Zeit verschwenden, NOP = 2 Taktzyklen
NOP
dec A
bne delay
rts
; --- im ROM abgelegte Konstanten ---
msgMenu:
; 1234567890123456 Spaces am Ende gelten auch, auch wenn man sie nicht sieht
ASCII 1. hex
BYTE lcd_pfeil_r
ASCII dec/bin C
ASCII 2. dec
BYTE lcd_pfeil_r
ASCII hex/bin C
ASCII 3. bin
BYTE lcd_pfeil_r
ASCII hex (Du)
BYTE 0
msgEingabeHexToDecBin:
; 1234567890123456
ASCII hex
BYTE lcd_pfeil_r
ASCII dec/bin CPU
ASCII Hex-Wert: $__
BYTE 0
msgEingabeDecToHexBin:
; 1234567890123456
ASCII dec
BYTE lcd_pfeil_r
ASCII hex/bin CPU
ASCII Dez-Wert: ___
BYTE 0
msgEingabeBinToHex:
; 1234567890123456
ASCII bin
BYTE lcd_pfeil_r
ASCII hex Du
ASCII Hex-Wert: $__
BYTE 0
msgEingabeBinToHexRichtig:
BYTE 32,32,32
ASCII RICHTIG! +1 Pkt.
BYTE 0
msgEingabeBinToHexFalsch:
BYTE 32,32,32
ASCII FALSCH,GAME OVER
BYTE 0
msgBinToHexGewonnen:
; 1234567890123456
ASCII ALLE ACHTUNG!
ASCII Du hast alle Auf
ASCII gaben gel
BYTE lcd_oe
ASCII st.
ASCII Gratulation!
BYTE 0
; --- Interrupt-Routinen ---
; bei einkommenden IRQ-Signal (Taster an NMI) springt er kurz hier rein und
; macht dann im Code weiter
ORG $FFD0 ; NMI -> Debug-Ausgabe
php
jsr debug
plp
wai
rti
; spring zurück, ohne etwas zu tun
; so kann ich BRK als Breakpoints in den Source einbauen, bei dem die CPU stoppt
ORG $FFE0 ; BRK/IRQ -> Continue
rti
; --- Sprungvektoren ---
ORG $FFFA
word $FFD0 ; NMI bei $FFFA/B
word $8000 ; Programmstartadresse bei $FFFC/D
word $FFE0 ; BRK/IRQ bei $FFFE/F
convert.inc (klicken, um diesen Abschnitt aufzuklappen)
; bin-dec-hex Umrechen-Routinen
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-11
; --- Funktionen:
;
; valToDez: ; holt sich Wert aus Var. val und liefert in dez1...dez3 zurück
; dezToVal: ; holt sich Ziffern aus Wert aus dez1...dez3 und gibt Wert in Var. val zurück
; -----------------------------------------------------------------------------------------
dezToVal: ; holt sich Ziffern aus Wert aus dez1...dez3 und gibt Wert in Var. val zurück
phx
lda #0 ; Wert im Akku aus dez1...dez3 aufaddieren
ldx dez1
beq dezToVal2a
dezToVal1:
adc #100
dex
bne dezToVal1
dezToVal2a:
ldx dez2
beq dezToVal3
dezToVal2b:
adc #10
dex
bne dezToVal2b
dezToVal3:
adc dez3
sta val
plx
rts
; -----------------------------------------------------------------------------------------
valToDez: ; holt sich Wert aus Var. val und liefert in dez1...dez3 zurück
sed ; Dezimal-Modus einschalten ($10+$10 sind dann z. B. $20!)
; damit passt aber max $99 in ein Byte, dann geschieht ein
; Überlauf und wir brauchen ein weiteres Byte für 100...255,
; in dem dann die führende 1 oder 2 steht
stz dezL ; low und High-Byte der BCD-Darstellung auf 0
stz dezH
lda val ; umzurechnenden Binärwert holen ($00...$FF)
sta zptmp
lda #0 ; Dezimalwert wird im Akku aufaddiert.
convBit0:
bbr0 zptmp, convBit1 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$1 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit1 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit1:
bbr1 zptmp, convBit2 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$2 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit2 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit2:
bbr2 zptmp, convBit3 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$4 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit3 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit3:
bbr3 zptmp, convBit4 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$8 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit4 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit4:
bbr4 zptmp, convBit5 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$16 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit5 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit5:
bbr5 zptmp, convBit6 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$32 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit6 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit6:
bbr6 zptmp, convBit7 ; wenn bit nicht gesetzt --> zum nächsten Bit springen
clc
adc #$64 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBit7 ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBit7:
bbr7 zptmp, convBitDone; wenn bit nicht gesetzt --> fertig
inc dezH
clc
adc #$28 ; in decimal mode sind dezimalzahlen als Hex-Wert anzugeben
bcc convBitDone ; kein Übertrag (<=99) --> zum nächsten Bit springen
inc dezH
convBitDone:
sta dezL ; die addierten Bitwerte nach dezL sichern
cld ; Dezimalmode wieder verlassen
; die BCD-Werte ($01 $23) in 3 Ziffern (1, 2, 3) portieren
lda dezH ; Ziffer 1
sta dez1
lda dezL ; Ziffer 2
lsr A ; 4x rechts shiften
lsr A
lsr A
lsr A
sta dez2
lda dezL ; Ziffer 3
and #%00001111
sta dez3
rts
debug.inc (klicken, um diesen Abschnitt aufzuklappen)
; Unter-Routinen fürs Debugging
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-06
; Aufruf:
; -------
; php ; Prozessor Status sichern
; jsr debug
; plp ; und wieder herstellen
debug:
sta regA ; Register retten
stx regX
sty regY
pla ; Rücksprungadresse (=PC) von jsr-Einsprung holen und retten
sta regPCL
pla
sta regPCH
pla
sta regP ; dann liegt P-Register von PHP oben, holen und retten
debug2:
lda regP ; P-Register wieder auf den Stack
pha
lda regPCH ; und dann die Rücksprungadresse
pha ; wieder auf den Stack
lda regPCL
pha
debug3:
LCD_CLEAR
; --- auf LCD ausgeben: NV1BDIZC A=$xx
LCD_PRINT msgDebug1
LCD_HEX regA
LCD_CHAR #32
; --- auf LCD ausgeben: NV1BDIZC - Registerwerte S, wenn set; leer, wenn reset
lda regP
sta zptmp
pst7:
bbs7 zptmp, pst7set
LCD_CHAR #32 'leer
bra pst6
pst7set:
LCD_CHAR #83 'ASC S
pst6:
bbs6 zptmp, pst6set
LCD_CHAR #32 'leer
bra pst5
pst6set:
LCD_CHAR #83 'ASC S
pst5:
bbs5 zptmp, pst5set
LCD_CHAR #32 'leer
bra pst4
pst5set:
LCD_CHAR #83 'ASC S
pst4:
bbs4 zptmp, pst4set
LCD_CHAR #32 'leer
bra pst3
pst4set:
LCD_CHAR #83 'ASC S
pst3:
bbs3 zptmp, pst3set
LCD_CHAR #32 'leer
bra pst2
pst3set:
LCD_CHAR #83 'ASC S
pst2:
bbs2 zptmp, pst2set
LCD_CHAR #32 'leer
bra pst1
pst2set:
LCD_CHAR #83 'ASC S
pst1:
bbs1 zptmp, pst1set
LCD_CHAR #32 'leer
bra pst0
pst1set:
LCD_CHAR #83 'ASC S
pst0:
bbs0 zptmp, pst0set
LCD_CHAR #32 'leer
bra pstdone
pst0set:
LCD_CHAR #83 'ASC S
pstdone:
LCD_CHAR #32 'leer
LCD_CHAR #32 'leer
; --- auf LCD ausgeben: X=$xx
LCD_PRINT msgDebug2
LCD_HEX regX
LCD_CHAR #32
; --- auf LCD ausgeben: PC=$
LCD_PRINT msgDebug3
LCD_HEX regPCH
LCD_HEX regPCL
LCD_CHAR #32
LCD_CHAR #32
; --- auf LCD ausgeben: Y=$xx
LCD_PRINT msgDebug4
LCD_HEX regY
LCD_CHAR #32
; register wieder herstellen
lda regA
ldx regX
ldy regY
wai ; auf Cont-Taste warten
rts
msgDebug1:
ASCII NV1BDIZC A=$
BYTE 0
msgDebug2:
ASCII X=$
BYTE 0
msgDebug3:
ASCII PC=$
BYTE 0
msgDebug4:
ASCII Y=$
BYTE 0
io-defs.inc (klicken, um diesen Abschnitt aufzuklappen)
; IO-Geräte-Adressen
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-31
; --- Konstanten: IO-Adressen ---
; --- OUTPUT ---
OUTPUT1: equ $4000
LED: equ OUTPUT1+$0 ; 8-LED-Ausgabe
SEGL: equ OUTPUT1+$1 ; linke 7-Segment-Anzeige
SEGM: equ OUTPUT1+$2 ; mittlere 7-Segment-Anzeige
SEGR: equ OUTPUT1+$3 ; rechte 7-Segment-Anzeige
LCD: equ OUTPUT1+$4 ; DOGM163 LCD mit 3*16 Zeichen
OUTPUT2: equ $5000 ; Output #2: für zukünftige Verwendung
; --- 6522er ---
VIA: equ $6000 ; 6522 #1: 4x4 Keypad
VIA_PORTB: equ VIA+$0 ; Port B
VIA_PORTA: equ VIA+$1 ; Port A
VIA_DDRB: equ VIA+$2 ; Data Direction Port B
VIA_DDRA: equ VIA+$3 ; Data Direction Port A
VIA_T1CL: equ VIA+$4 ; Timer 1 Low Order Latches (W) / Counter (R)
VIA_T1CH: equ VIA+$5 ; T1 High-Order Counter
VIA_T1LL: equ VIA+$6 ; T1 Low-Order Latches
VIA_T1LH: equ VIA+$7 ; T1 High-Order Latches
VIA_T2CL: equ VIA+$8 ; Timer 2 Low Order Latches (W) / Counter (R)
VIA_T2CH: equ VIA+$9 ; T2 High-Order Counter
VIA_SR: equ VIA+$A ; Shift Register
VIA_ACR: equ VIA+$B ; Auxiliary Control Register
VIA_PCR: equ VIA+$C ; Peripheral Control Register
VIA_IFR: equ VIA+$D ; Interrupt Flag Register
VIA_IER: equ VIA+$E ; Interrupt Enable Register
VIA_PA_No_SHAKE: equ VIA+$F ; Same as Reg 1 except no "Handshake"
VIA2: equ $7000 ; 6522 #2: für zukünftige Verwendung
io.inc (klicken, um diesen Abschnitt aufzuklappen)
; Funktionen, die IO betreffend
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-03
ioInit:
; LED-Ausgabe löschen
stz LED ; das gleiche wie lda #0, sta LED
; Segment-Ausgabe löschen
lda #$FF
sta SEGL
sta SEGM
sta SEGR
init_LCD:
; Reset an LCD (Bit 0 auf Low für ein paar ms)
stz LCD ; das gleiche wie lda #0, sta LCD
lda #5
jsr delay
; wieder auf high und min 50 ms warten
lda #1
sta LCD
lda #10
jsr delay
jsr lcdInit
jsr test_keypad
rts
key-defs.inc (klicken, um diesen Abschnitt aufzuklappen)
; 4x4 Keypad Definitionen
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-01
KEY_VAL:
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $01, $FF, $FF, $FF, $04, $FF, $07, $0E, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $02, $FF, $FF, $FF, $05, $FF, $08, $00, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $03, $FF, $FF, $FF, $06, $FF, $09, $0F, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $0A, $FF, $FF, $FF, $0B, $FF, $0C, $0D, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
KEY_ASC:
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $31, $FF, $FF, $FF, $34, $FF, $37, $45, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $32, $FF, $FF, $FF, $35, $FF, $38, $30, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $33, $FF, $FF, $FF, $36, $FF, $39, $46, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $41, $FF, $FF, $FF, $42, $FF, $43, $44, $FF
BYTE $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
key.inc (klicken, um diesen Abschnitt aufzuklappen)
; Tastendrücke vom 4x4 Keypad entgegennehmen
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-02
; -----------
; Funktionen:
; -----------
; test_keypad: ; checkt, ob Kurzschluss in Tastatur
; getKey: Wartet auf Tastendruck (+loslassen) und schreibt Tasten-Muster nach keyIn
; in: -
; out: Key-Bitmuster in Var. keyIn
; keyToLCD: gibt die gedrückte Taste als ASCII auf dem LCD aus
; in: - (keyIn)
; out: -
; keyToSeg: gibt die gedrückte Taste auf der 7-Segment-Anzeige aus
; in: - (keyIn)
; out: -
; keyToVal: gibt die gedrückte Taste als Wert im Akku zurück
; in: - (keyIn)
; out: TastenWert ($0...$F) in Akku
; ------------------------------------------------------------------------------------------
test_keypad: ; checkt, ob Kurzschluss in Tastatur
; oberen 4 Bits Port A Output, unteren 4 Bits Port Input
; standardmäßig sind die Leitungen auf High, wir müssen also auf low prüfen
lda #%11110000
sta VIA_DDRA
lda #$0F ; alle Output-Leitungen auf low
sta VIA_PORTA
lda VIA_PORTA ; sollte $0F bleiben, sonst hängt eine Taste
cmp #$0F
beq test_keypad_done ; alles okay
; Eine Taste hängt. Meldung ausgeben
LCD_CLEAR
LCD_PRINT msgTastaturKlemmt
; welche Taste(n) hängt? Durchtesten und ausgeben
lda #%11101111
sta keyOut
ldx #4 ; 4 Output-Leitungen nacheinander mit LOW versorgen...
nextKeyOutTest:
lda keyOut
sta VIA_PORTA
; und schauen, ob das bei einer der 4 Input-Leitungen ankommt...
lda VIA_PORTA
sta keyIn
; ein unteres Bit auf 0? Dann Taste gedrückt
and #%00001111
cmp #$0F ; $0F = Standardwert = nichts gedrückt
beq keinHaengerHier
; Hänger ausgeben
ldy keyIn
lda KEY_ASC, y
LCD_CHAR_AKKU
keinHaengerHier:
dex
beq test_nextKeyOutTest_done; wenn 4x durch, raus
rol keyOut ; bits eins nach links schieben = nächste Leitung
bra nextKeyOutTest
test_nextKeyOutTest_done:
wai ; auf Cont.-Taste warten
LCD_CLEAR ; LCD wieder leeren
test_keypad_done:
rts
msgTastaturKlemmt:
;1234567890123456
ASCII WARNUNG! Key-Pad
BYTE lcd_ue
ASCII berpr
BYTE lcd_ue
ASCII fen! Eine
ASCII Taste h
BYTE lcd_ae
ASCII ngt:
BYTE 0
keyToLCD: ; --- gibt die gedrückte Taste als ASCII auf dem LCD aus
phx ; Register X auf Stack retten
lda #1 ; ist ein zeichen
sta lcdrs
; ASCII-Zeichen für Key-Muster holen ("0"..."9" und "A"..."F")
ldx keyIn
lda KEY_ASC, x
jsr lcdByte
plx ; Register X wieder herstellen
rts
keyToSeg: ; --- gibt die gedrückte Taste auf der 7-Segment-Anzeige aus
phx ; Register X auf Stack retten
lda #1 ; ist ein zeichen
sta lcdrs
; Wert für Key-Muster holen ($0...$F bzw. 0 bis 15)
ldx keyIn
lda KEY_VAL, x ; erst das Key-Bitmuster zuordnen
tax
lda SEG, x ; dann dafür das Segment-Muster holen
sta SEGR
plx ; Register X wieder herstellen
rts
keyToVal: ; --- gibt die gedrückte Taste als Wert im Akku zurück
phx ; Register X auf Stack retten
; Wert für Key-Muster holen ($0...$F bzw. 0 bis 15)
ldx keyIn
lda KEY_VAL, x ; das Key-Bitmuster dem Wert zuuordnen
plx ; Register X wieder herstellen
rts
getKey: ; --- Wartet auf Tastendruck (+loslassen) und schreibt Tasten-Muster nach keyIn
phx ; Register X auf Stack retten
; oberen 4 Bits Port A Output, unteren 4 Bits Port Input
; standardmäßig sind die Leitungen auf High, wir müssen also auf low prüfen
lda #%11110000
sta VIA_DDRA
getKey2:
lda #%11101111
sta keyOut
ldx #4 ; 4 Output-Leitungen nacheinander mit LOW versorgen...
nextKeyOut:
lda keyOut
sta VIA_PORTA
; und schauen, ob das bei einer der 4 Input-Leitungen ankommt...
lda VIA_PORTA
sta keyIn
; ein unteres Bit auf 0? Dann Taste gedrückt
and #%00001111
cmp #$0F ; $0F = Standardwert = nichts gedrückt
bne getKeyDone ; es wurde etwas gedrückt
rol keyOut ; bits eins nach links schieben = nächste Leitung
dex
beq getKey2 ; wenn 4x durch, dann nochmal von vorne
bra nextKeyOut
getKeyDone: ; Ergebnis steht in keyIn
; warten, bis die Taste wieder losgelassen wurde
loslassen:
NOP
NOP
NOP
NOP
NOP
lda VIA_PORTA
and #%00001111
cmp #$0F ; $0F = Standardwert = nichts gedrückt
bne loslassen
plx ; Register X wieder herstellen
rts ; gedrückte Taste steht in keyIn
lcd-defs.inc (klicken, um diesen Abschnitt aufzuklappen)
; LCD Definitionen für DOGM163-LCD
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-30
; --- Konstanten für LCD
opLCD_Clear: equ %00000001
opLCD_Home: equ %00000010
opLCD_DispOn: equ %00001100
opLCD_DispOff: equ %00001000
opLCD_CursorOn: equ %00001111
opLCD_CursorOff: equ %00001100
; --- Kontanten: Bitpositionen ---
bitLCD_Reset: equ 1 ; Bit 0 = Reset (0) / normal (1)
bitLCD_RS: equ 2 ; Bit 1 = RS = Command (0) / Character (1)
bitLCD_RW: equ 4 ; Bit 2 = RW = Write (0, normal) / Read (1, nicht benutzt)
bitLCD_E: equ 8 ; Bit 3 = Ena ble (pulse=1, normal=0)
; bit 4 bis 7 = Datenbits D4 bis D7
; --- Sonderzeichen ---
lcd_ae: equ %10000100
lcd_oe: equ %10010100
lcd_ue: equ %10000001
lcd_AE: equ %10001110
lcd_OE: equ %10011001
lcd_UE: equ %10011010
lcd_grad: equ %11011111
lcd_durchschn: equ %11101110
lcd_wurzel: equ %11111110
lcd_ungleich: equ %11111101
lcd_pfeil_l: equ %01111111
lcd_pfeil_r: equ %01111110
lcd_ecke_lo: equ %00001001
lcd_ecke_ro: equ %00001010
lcd_ecke_lu: equ %00001011
lcd_ecke_ru: equ %00001100
lcd_strich_o: equ %11111111
lcd_strich_u: equ %01011111
lcd_mittelpkt: equ %00001101
lcd_mult: equ %11110111
lcd_div: equ %11111000
lcd_alpha: equ %00011111
lcd_pi: equ %00011001
lcd_sigma: equ %00011010
lcd_phi: equ %00011100
lcd_psi: equ %00011101
lcd_ohm: equ %00011111
lcd_ypsilon: equ %00011011
lcd_xi: equ %00011000
lcd_lambda: equ %00010111
lcd_theta: equ %00010110
lcd_delta: equ %00010101
nibble_ASC:
BYTE $30, $31, $32, $33, $34, $35, $36, $37, $38, $39 ; ASCII Ziffern 0...9
BYTE $41, $42, $43, $44, $45, $46 ; ASCII Bst. A...F
lcd.inc (klicken, um diesen Abschnitt aufzuklappen)
; Unterprogramme für DOGM163-LCD
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-12
; Funktionen / Makros:
; LCD_POS CURSOR_POS ; LCD-Cursor positionieren (0...47)
; LCD_CHAR ; Druckt 1 Zeichen, darf Speicheradresse oder auch #Wert sein
; LCD_CHAR_AKKU ; Druckt 1 Zeichen, ASCII-Wert steht schon im Akku
; LCD_PRINT ADDRMSG ; gibt ein Null-terminierten String aus
; LCD_CLEAR
; LCD_CURSOR_ON
; LCD_CURSOR_OFF
; lcdByte: ; Übergabe Bytewert in Akku, Command (0) / Character (1) in lcdrs
; lcdInit: ; LCD initialisieren
LCD_CHAR MACRO CHAR_OR_ADDR ; darf Speicheradresse oder auch #Wert sein
lda #1 ; ist ein Zeichen
sta lcdrs
lda CHAR_OR_ADDR
jsr lcdByte ; druckt, was im Akku steht
MACEND
LCD_CHAR_AKKU MACRO ; ASCII-Wert steht schon im Akku
ldy #1 ; ist ein Zeichen
sty lcdrs
jsr lcdByte ; druckt, was im Akku steht
MACEND
LCD_HEX MACRO BYTE_OR_ADDR ; darf Speicheradresse oder auch #Wert sein
lda #1 ; ist ein Zeichen
sta lcdrs
lda BYTE_OR_ADDR
lsr A
lsr A
lsr A
lsr A
tax
lda nibble_ASC, x
jsr lcdByte ; druckt, was im Akku steht
lda BYTE_OR_ADDR
and #%00001111
tax
lda nibble_ASC, x
jsr lcdByte ; druckt, was im Akku steht
MACEND
; eine Message (Null-terminierten String) auf dem LCD anzeigen --------------
LCD_PRINT MACRO ADDRMSG
lda #1 ; sind Zeichen
sta lcdrs
ldx #0
print# ; # = für Makro individuelles Label
ldy ADDRMSG, x ; X. Buchstaben aus Message in Y-Register laden
beq print_done# ; Null-Byte = String-Ende
tya
jsr lcdByte
inx
bra print#
print_done# ; Ausgabe fertig, weiter im Takt
MACEND
LCD_CLEAR MACRO
stz lcdrs ; ist ein Befehl (0)
lda #opLCD_Clear
jsr lcdByte
MACEND
LCD_CURSOR_ON MACRO
stz lcdrs ; ist ein Befehl (0)
lda #opLCD_CursorOn
jsr lcdByte
MACEND
LCD_CURSOR_OFF MACRO
stz lcdrs ; ist ein Befehl (0)
lda #opLCD_CursorOff
jsr lcdByte
MACEND
LCD_POS MACRO CURSOR_POS ; LCD-Cursor positionieren
stz lcdrs ; ist ein Befehl (0)
lda CURSOR_POS
adc #$80 ; $80 für Pos.-Command dazu addieren
jsr lcdByte
MACEND
lcdByte: ; Übergabe Bytewert in Akku, Command (0) / Character (1) in lcdrs
sta lcdhb ; Übergabe aus Akku speichern -> Highbyte kommt zuerst dran
sta lcdlb ; Übergabe aus Akku speichern -> schon mal Lowbyte speichern
asl lcdlb ; unteren 4 Bit nach links nach oben schieben
asl lcdlb
asl lcdlb
asl lcdlb
smb0 lcdhb ; Reset aus
smb0 lcdlb
rmb2 lcdhb ; Write
rmb2 lcdlb
rmb3 lcdhb ; Enable erst einmal auf low
rmb3 lcdlb
; command (lcdrs=0) oder Character (lcdrs=1)
lda lcdrs
bne lcdByteCmd
rmb1 lcdhb ; Command (0) / Character (1)
rmb1 lcdlb ; Command (0) / Character (1)
bra lcdByteCmd_after
lcdByteCmd:
smb1 lcdhb ; Command (0) / Character (1)
smb1 lcdlb ; Command (0) / Character (1)
lcdByteCmd_after:
; High-Byte übertragen und pulsen (E-Leitung auf High für ein paar ms, min. 2 ms)
lda lcdhb
sta LCD
;sta LED
nop
smb3 lcdhb ; E-Leitung kurz auf High pulsen
lda lcdhb
sta LCD
;sta LED
nop
rmb3 lcdhb ; E-Leitung wieder auf Low
lda lcdhb
sta LCD
;sta LED
nop
; Low-Byte übertragen und pulsen (E-Leitung auf High für ein paar ms, min. 2 ms)
lda lcdlb
sta LCD
;sta LED
smb3 lcdlb ; E-Leitung kurz auf High pulsen
lda lcdlb
sta LCD
;sta LED
nop
rmb3 lcdlb ; E-Leitung wieder auf Low
lda lcdlb
sta LCD
;sta LED
nop
rts
lcdInit: ; LCD initialisieren --------------------------------------------------------------
; 8-Bit-Mode einschalten; nötig, um nach deinem Reset die Spur wieder zu finden
stz lcdrs ;cmd
lda #%00110000
jsr lcdByte
; nötig, um nach deinem Reset die Spur wieder zu finden
; weiterhin stz lcdrs ;cmd
lda #%00110011
jsr lcdByte
; nötig, um nach deinem Reset die Spur wieder zu finden
; weiterhin stz lcdrs ;cmd
lda #%00110010
jsr lcdByte
; 4-Bit-Mode einschalten; extended instruction table 1 setzen
; weiterhin stz lcdrs ;cmd
lda #%00101001
jsr lcdByte
; bias set
; weiterhin stz lcdrs ;cmd
lda #%00011101
jsr lcdByte
; Power/ICON control/Contrast set
; weiterhin stz lcdrs ;cmd
lda #%01010011
jsr lcdByte
; Follower control
; weiterhin stz lcdrs ;cmd
lda #%01101111
jsr lcdByte
; Contrast set(low byte)
; weiterhin stz lcdrs ;cmd
lda #%01111111
jsr lcdByte
; es folgen nur noch Standard-Befehle: normal instruction table
; weiterhin stz lcdrs ;cmd
lda #%00101000
jsr lcdByte
; Entry Mode Set
; weiterhin stz lcdrs ;cmd
lda #%00000110
jsr lcdByte
; clear
; weiterhin stz lcdrs ;cmd
lda #opLCD_Clear
jsr lcdByte
; display on
; weiterhin stz lcdrs ;cmd
lda #opLCD_DispOn
jsr lcdByte
rts
macros.inc (klicken, um diesen Abschnitt aufzuklappen)
; Makro-Funktionen
; werden vom Assembler ersetzt und belasten so nicht den Stack wie jsr-Aufrufe
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-07
; vorhandene Makros:
; DEBUG ; gibt Prozessorstatus auf LCD aus und wartet auf Cont
DEBUG MACRO ; gibt Prozessorstatus auf LCD aus und wartet auf Cont
php
jsr debug
plp
MACEND
ram-defs.inc (klicken, um diesen Abschnitt aufzuklappen)
; Definition von gemeinsamen RAM-Adressen außerhalb der ZeroPage
; $0200...$0FFF
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-31
keyOut: equ $200;
keyIn: equ $201;
val: equ $202 ; normaler Binärwert
dezL: equ $203 ; BCD Darstellung low Byte
dezH: equ $204 ; high Byte
dez1: equ $205 ; Dezimalziffer 1 (0...2) (max 255)
dez2: equ $206 ; Dezimalziffer 2 (0...9)
dez3: equ $207 ; Dezimalziffer 3 (0...9)
hex1: equ $208 ; Hexziffer 1 (0...F)
hex2: equ $209 ; Hexziffer 2 (0...F)
bitVal: equ $20A ; Hilfs.-Var zur Hex-Umrechnung
testVal: equ $20B ; Hilfs.-Var zur Hex-Umrechnung
regA: equ $20C ; Zwischenspeicher Akku
regX: equ $20D ; Zwischenspeicher X-Register
regY: equ $20E ; Zwischenspeicher Y-Register
regP: equ $20F ; Zwischenspeicher Prozessorstatus-Register
regPCL: equ $210 ; Program counter Low
regPCH: equ $211 ; Program counter High
regP2: equ $212 ; Prozessorstatus-Register Kopie
regPCL2: equ $213 ; Program counter Low Kopie
regPCH2: equ $214 ; Program counter High Kopie
seg-defs.inc (klicken, um diesen Abschnitt aufzuklappen)
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-08
; Definitionen für 7-Segment-Anzeige 5611 B (common Anode)
;
; 2
; __
; 4 | | 1
; -- 8
; 10 |__| 40 .=80
;
; 20
; 0...9 Ziffern, 11...36 Buchstaben
SEG:
BYTE $88, $BE, $C4, $94, $B2, $91, $81, $BC, $80, $90 ; 0 bis 9 = Ziffern 0 bis 9
BYTE $A0, $83, $C9, $86, $C1, $E1, $89, $A2, $BE, $9E ; 11 bis 20 = Buchst. A bis J
BYTE $A1, $CB, $A5, $A8, $88, $E0, $B0, $E9, $91, $BC ; 21 bis 30 = Buchst. K bis T
BYTE $8A, $DA, $D2, $D5, $92, $C4 ; 31 bis 36 = Buchst. U bis Z
; Abbildung der wichtigsten ASCII-Zeichen an der ASCII-Position
SEG_ASC:
; ersten 32 Bytes leer, weil Steuerzeichen
BLKB 32, $FF
; SPC ! " # $ % & ' ( ) * + , - . /
BYTE $FF, $3E, $FA, $AA, $91, $E6, $C0, $FE, $C9, $9C, $B6, $E3, $BF, $F7, $7F, $E6
; 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
BYTE $88, $BE, $C4, $94, $B2, $91, $81, $BC, $80, $90, $3F, $3F, $C7, $D7, $97, $44
; @ A B C D E F G H I J K L M N O
BYTE $84, $A0, $83, $C9, $86, $C1, $E1, $89, $A2, $BE, $9E,$A1, $CB, $A5, $A8, $88
; P Q R S T U V W X Y Z [ \ ] ^ _
BYTE $E0, $B0, $E9, $91, $BC, $8A, $DA, $D2, $D5, $92, $C4, $C9, $B3, $9C, $F8, $DF
; ` a b c d e f g h i j k l m n o
BYTE $FB, $A0, $83, $C9, $86, $C1, $E1, $89, $A2, $BE, $9E,$A1, $CB, $A5, $A8, $88
; p q r s t u v w x y z { | } ~ DEL
BYTE $E0, $B0, $E9, $91, $BC, $8A, $DA, $D2, $D5, $92, $C4, $C9, $B3, $9C, $F8, $FF
; ASCII 128-255 = 128 Bytes leer
BLKB 128, $FF
seg.inc (klicken, um diesen Abschnitt aufzuklappen)
; Unter-Routinen für 7-Segment-Anzeige
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-09-06
; Funktionen:
;
; dezToSeg: ; gibt eine Dezimalzahl auf der 7-Segment-Anzeige aus
valToSegInHex: ; gibt einen Speicherinhalt auf der 7-Segment-Anzeige in Hex aus
phx
; links immer leer
lda #$FF
sta SEGL
; mittig High-Nibble
lda val
lsr
lsr
lsr
lsr
tax
lda SEG, x ; Muster für Wert ($0...$F) holen
sta SEGM
; rechts High-Nibble
lda val
and #%00001111
tax
lda SEG, x ; Muster für Wert ($0...$F) holen
sta SEGR
plx
rts
dezToSeg: ; gibt eine Dezimalzahl auf der 7-Segment-Anzeige aus
phx
; und auf 7-Segment ausgeben
ldx dez1
beq SEGL_leer
lda SEG, x
sta SEGL
bra SEGM_weiter
SEGL_leer:
lda #$FF
sta SEGL
; 2. ziffer auch 0? dann auch leer lassen
lda dez2
bne SEGM_weiter
lda #$FF
sta SEGM
bra SEGR_weiter
SEGM_weiter:
ldx dez2
lda SEG, x
sta SEGM
SEGR_weiter:
ldx dez3
lda SEG, x
sta SEGR
plx
rts
zeropage-defs.inc (klicken, um diesen Abschnitt aufzuklappen)
; Definition von festen ZeroPage- Adressen
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-11
; --- Speicheradressen RAM Zeropage $00...$FF
; --- lcd ---
lcdhb: equ $00
lcdlb: equ $01
lcdrs: equ $02 ; 1= char, 0= cmd, vor jsr lcdByte setzen
; --- allgemeine temporäre Verwendung
zptmp: equ $03
zptmp2: equ $04
zptmp3: equ $05
Der Source-Code ist - wie gewohnt - gut dokumentiert und kommentiert. Eine Erklärung des Programmes und der neuen Funktionen findet sich in folgendem Video. Das ist einfach anschaulicher als mit viel Text.

Ausblick
Wenn man das Spiel häufiger spielt, wird man die angezeigten Zahlen, zumindestens die ersten, bald auswendig kennen, denn es sind immer die gleichen. Und eigentlich soll beim Spiel ums Kopfrechnen und nicht ums Auswendiglernen gehen.Ein Pseudozufallszahlengenerator wäre also etwas tolles, damit immer wieder andere Zahlen kommen. Der wäre auch für zukünftige Programme, insbesondere Spiele nützlich.