8-Bit-Breadboard-Computer auf Basis einer 6502-CPU - Assembler-Programmierung: weitere Funktionen des CoProzessors auf Arduino-Basis

Bisherige Artikel dieser Serie - hier könnt ihr nochmal alle Grundlagen nachlesen, falls ihr jetzt erst einsteigt: Nachdem wir uns das letzte mal um die Hardware-Integration des CoProzessors auf Arduino-Basis gekümmert haben und die Software-Seite nur mit der Zufallszahlen-Funktion angeschnitten, soll es heute um die weiteren Funktionen gehen, die der CoProzessor zu bieten hat: Mit der Hardware für die Arduino CoProzessor-Sensoren haben sich die insgesamt 10 Breadboards unseres Breadboard-Computers schon ordentlich gefüllt:



Ich habe links unten noch einen kleinen Schuber für die 433 MHz-Fernbedienung mit meinem Anycubic i3 Mega rausgelassen und per Klebeknete befestigt. So kann sie nicht mehr verloren gehen und ich habe alles beisammen.

Viel Platz ist jetzt nicht mehr, vielleicht noch für ein paar Taster. Aber alles in einem hat der Breadboard Computer ja auch alles, was man sich wünschen kann: Eingabe, Ausgabe, Sensoren. Ich habe das Gefühl, hardwaretechnisch haben wir das Projekt jetzt so ziemlich ausgereizt.

Die Software-Schnittstelle zum CoProzessor

Aber heute soll es ja auch mehr um die Software gehen, und dort im speziellen um die noch nicht behandelten Sensoren und das dazu gehörige Bit Banging, denn gerade für die Echtzeituhr musste ich die Bits schon ein bisschen zusammenrücken lassen, damit ich alles in den 16 zur Verfügung stehenden Funktionen unterzubringen.

Funktionen: 0 0b0000 = Zufallswert (4 Bit) 1 0b0001 = 433 MHz Fernbedienung DCBA (4 Bits, gedrücktes gesetzt) DHT11 Temp (Messbereich: 0...50°C, Auflösung 1°C (+/- 2°) 2 0b0010 = DHT11 Temp High (0...50) = 6 Bits 3 0b0011 = Temp Low DHT11 Luftfeuchte (Messbereich: 20...90% RH, Auflösung 1% (+/- 5%) 4 0b0100 = DHT11 Fcht High (0...90) = 7 Bits 5 0b0101 = Fcht Low RTC DS1307 6 0b0110 = YYYY Jahr High 1900 + 0...255 7 0b0111 = YYYY Low 8 0b1000 = MMMM Monat (0...12) = 4 Bits 9 0b1001 = DDDD Tag (0...31) = 5 Bits 10 0b1010 = DHHH Stunde (0...23) = 5 Bits 11 0b1011 = HHMM Minute (0...59) = 6 Bits 12 0b1100 = MMMM 13 0b1101 = xxSS Sekunde (0...59) = 6 Bits 14 0b1110 = SSSS 15 0b1111 = idle (Normalzustand 6522 Port)

Abfrage der Fernbedienung

Nachdem wir anfangs den 6522 im ioInit verklickert habe, wie die Pins von Port B behandelt werden sollen, nämlich welche Eingang und welche Ausgang sind (nachzulesen im letzten Teil), sagen wir ihm zur Abfrage einfach, welche Funktion wir aufrufen wollen.

Für die Verbedienung ist dies die Funktion 1. Der Funktionsaufruf erfolgt in den oberen 4 Bits, die Rückgabe in den unteren 4 Bits.

Wie praktisch, dass die Fernbedienung ebenfalls 4 Knöpfe hat, da können wir jeden Tastendruck auf ein Bit abbilden.

Ich habe die Abfrage in eine Funktion namens getFernbedienung im Include-File sensors.inc gepackt, wegen der Wiederverwendbarkeit und ÜBersicht:

getFernbedienung: ; liefert das Bitmuster der 433 MHz-Funkfernbedienung ; VIA Port B, Funktion 1 (oberen 4 Bits) ; -> 433 MHz Fernbedienung DCBA (4 Bits, gedrücktes gesetzt) lda #$10 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden sta fernbd ; sichern ; fertig, der CoProzessor darf wieder faulenzen lda #$F0 sta VIA_PORTB lda fernbd ; Rückgabe im Akku rts nach einem Aufruf der Funktion 1 lassen wir dem CoProzessor 2 Millisekunden Zeit, zu antworten und schauen uns dann das Ergebnis in den unteren 4 Bits von Port B an. Dieses speichern wir im RAM unter der Adresse $21E, die wir uns aber dann Assembler nicht merken müssen. Wir verwenden einfach immer den Variablennamen fernbd für Fernbedienung. Dies ist ein Byte und hat folgenden Aufbau Bit 76543210 0000DCBA Wird auf der Fernbedienung die Taste A gedrückt, sorgt das also dafür, dass das Bit 0 ganz rechts gesetzt wird. Die Bits lassen sich wunderbar über unsere LED-Ausgabe anzeigen und so werden wir das auch in unserem Test- und Beispielprogramm implementieren.

Abfrage von Temperatur und Luftfeuchtigkeit

Die Temperatur und die Luftfeuchtigkeit brauchen jeweils ein ganzes Byte als Rückgabe. Das heißt, jedes von beiden braucht zwei Nibbles (Halbbytes) und damit zwei Funktionen.

Für die Temperatur sind das 2 und 3; für die Luftfeuchtigkeit 4 und 5. Hier werden einfach die zwei Funktionen nacheinander aufgerufen und dann die zwei Paare zu 4 Bits zu 8 Bits kombiniert, simsalabim fertig ist der 8 Byte Wert, indem schon fertig die richtige Temperatur in Grad Celsius bzw. die Luftfeuchtigkeit in Prozent RH (RH = Relative Humidity = relative Luftfeuchtigkeit).

Hier als Beispiel der Code für die Temperatur: getTemperature: ; liefert die Temperatur in Grad Celsius in tempC phy ldy #sensorTries tryTemperature: ; VIA Port B, Funktion 2 (oberen 4 Bits) -> ; High-Nibble Temp. lda #$20 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #10 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden asl A; ins High-Nibble verschieben asl A asl A asl A sta tempC ; High-Nibble sichern ; VIA Port B, Funktion 3 (oberen 4 Bits) -> ; Low-Nibble Temp. lda #$30 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #10 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden ora tempC ; das high-Nibble dazu sta tempC ; Gesamt-Temperatur speichern ; fertig, der CoProzessor darf wieder faulenzen lda #$F0 sta VIA_PORTB lda tempC ; Rückgabe im Akku bne doneTemperature ; temperatur gültig: zurückgeben ;temperatur = 0 = ungültig, warten und neuer versuch dey beq doneTemperature ; alle versuche durch, mit 0 zurückgeben lda #sensorWait jsr delayMs bra tryTemperature doneTemperature: ply rts getHumidity funktioniert ganz analog und liefert in der Variablen humidity zurück.

Leider habe ich immer noch das Problem mit den Aussetzern in der Datenkommunikation zwischen ATmega328P und 6522, den ich bisher noch nicht ausfindig machen konnte.

Darum gibt es eine Abfrage, ob nicht eine Null zurückgeliefert wird, was auf einen Aussetzer hindeutet. Dann wartet das Programm ein paar Hundert Millisekunden (einstellbar in der Konstanten sensorWait) und versucht es dann erneut. Ist nach einer bestimmten Anzahl Versuche (sensorTries) immer noch kein Erfolg zu verbuchen, dann wird trotzdem die Null zurückgeliefert, damit das Programm nicht in einer Endlosschleife hängenbleibt. So gibt es zwar Kommunikationsaussetzer, aber die falschen Ergebnisse werde nicht angezeigt.

Abfrage von Datum / Zeit

Wir steigern uns in der Schwierigkeit. Die Werte für Datum und Zeit, die ich aufgeteilt habe auf die Variablen habe ich aufgeteilt auf die verbleibenden 9 Funktionen 6 bis 14 (bzw. $6...$E). 9 Funktionen bedeuten 9 / 2 Bytes = 4 1/2 Bytes. Wir haben aber 6 Werte, also musste ich teils mehrere Werte in einem Byte speichern. Das funktioniert, weil nicht jede Information 8 Bits benötigt. Der Monat ist kleiner/gleich 15 und braucht nur 4 Bits. Die Monat ist kleiner/gleich 31 und ihm reichten 5 Bits. Und Minute und Sekunde brauchen auch nur 6 Bits (>=63).

Die folgende Übersicht zeigt die Bitkodierung von Datum und Zeit vielleicht noch einmal übersichtlicher:



Für den Tag zum Beispiel muss ich Funktion $9 für die Bits 4, 3, 2, 1 und Funktion $10 für das Bit 0 des Tages aufrufen. $10 enthält aber außerdem Infos über die Stunde. Möchte ich den Tag errechnen, muss ich die Bits ausblenden, die nicht zum Tag gehören (AND) und an den richtigen Platz verschieben (ASL und LSR) und mir den Tag so wieder zusammenbasteln.

So bekomme ich alles in den verbleibenden 9 Funktionen unter und habe am Ende sogar noch 2 Bits übrig, die ich vor die Sekunden gesetzt habe, um dort einfacher zu rechnen.

Das ganze Bitgeschubse mit OR, AND, ASL und LSR ergibt sich durch die Schnittstellendefinition und steht in der Funktion getDateAndTime von sensors.inc. Ich gehe auf die Programmierung im Video weiter unten näher darauf ein.

sensors.inc (klicken, um diesen Abschnitt aufzuklappen)
; Unter-Routinen für Sensoren (Temperatur, Luftfeuchtigkeit, 433 MHz-Fb., RTC) ; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-10-06 ; Funktionen: ; getFernbedienung: ; liefert das Bitmuster der 433 MHz-Funkfernbedienung ; getTemperature: ; liefert die Temperatur in Grad Celsius ; getHumidity: ; liefert die Luftfeuchtigkeit in % RH ; getDateAndTime: ; liefert Datum und Zeit in year, month, day, hour, minute, second sensorWait: equ 200 ; Wie lange auf den Sensor warten ? sensorTries: equ 5 ; Wie oft versuchen getFernbedienung: ; liefert das Bitmuster der 433 MHz-Funkfernbedienung ; VIA Port B, Funktion 1 (oberen 4 Bits) ; -> 433 MHz Fernbedienung DCBA (4 Bits, gedrücktes gesetzt) lda #$10 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden sta fernbd ; sichern ; fertig, der CoProzessor darf wieder faulenzen lda #$F0 sta VIA_PORTB lda fernbd ; Rückgabe im Akku rts getTemperature: ; liefert die Temperatur in Grad Celsius in tempC phy ldy #sensorTries tryTemperature: ; VIA Port B, Funktion 2 (oberen 4 Bits) -> ; High-Nibble Temp. lda #$20 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #10 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden asl A; ins High-Nibble verschieben asl A asl A asl A sta tempC ; High-Nibble sichern ; VIA Port B, Funktion 3 (oberen 4 Bits) -> ; Low-Nibble Temp. lda #$30 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #10 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden ora tempC ; das high-Nibble dazu sta tempC ; Gesamt-Temperatur speichern ; fertig, der CoProzessor darf wieder faulenzen lda #$F0 sta VIA_PORTB lda tempC ; Rückgabe im Akku bne doneTemperature ; temperatur gültig: zurückgeben ;temperatur = 0 = ungültig, warten und neuer versuch dey beq doneTemperature ; alle versuche durch, mit 0 zurückgeben lda #sensorWait jsr delayMs bra tryTemperature doneTemperature: ply rts getHumidity: ; liefert die Luftfeuchtigkeit in % RH in humidity phy ldy #sensorTries tryHumidity: ; VIA Port B, Funktion 4 (oberen 4 Bits) -> ; High-Nibble Humidity lda #$40 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #10 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden asl A; ins High-Nibble verschieben asl A asl A asl A sta humidity; High-Nibble sichern ; VIA Port B, Funktion 5 (oberen 4 Bits) -> ; Low-Nibble Humidity lda #$50 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #10 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden ora humidity ; das high-Nibble dazu sta humidity ; Gesamt-Temperatur speichern ; fertig, der CoProzessor darf wieder faulenzen lda #$F0 sta VIA_PORTB lda humidity ; Rückgabe im Akku bne doneHumidity ; temperatur gültig: zurückgeben ; Humidity = 0 = ungültig, warten und neuer versuch dey beq doneHumidity ; alle versuche durch, mit 0 zurückgeben lda #sensorWait jsr delayMs bra tryHumidity doneHumidity: ply rts getDateAndTime: ; liefert Datum und Zeit in year, month, day, hour, minute, second phy ldy #sensorTries tryDateAndTime: ; --- JAHR --- ; VIA Port B, Funktion 6 (oberen 4 Bits) -> ; High-Nibble Jahr (seit 1900) lda #$60 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden asl A ; ins High-Nibble verschieben asl A asl A asl A sta year; High-Nibble sichern ; VIA Port B, Funktion 7 (oberen 4 Bits) -> ; Low-Nibble Jahr lda #$70 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden ora year ; das high-Nibble dazu sta year ; Gesamtwert speichern lda #20 sta century lda year ; test, ob 0 (=ungültig, dann warten und neuer versuch) bne doneYear ; weiter dey jmp doneDateAndTime ; alle versuche durch, mit 0 zurückgeben lda #sensorWait jsr delayMs bra tryDateAndTime doneYear: ; --- MONAT --- ; VIA Port B, Funktion 8 (oberen 4 Bits) -> ; High-Nibble Monat lda #$80 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden sta month ; Gesamtwert speichern lda month ; test, ob 0 (=ungültig, dann warten und neuer versuch) bne doneMonth ; weiter dey jmp doneDateAndTime ; alle versuche durch, mit 0 zurückgeben lda #sensorWait jsr delayMs bra tryDateAndTime doneMonth: ; --- TAG --- ; VIA Port B, Funktion 9: DDDD (10: DHHH) lda #$90 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$0F ; Function ausblenden asl A ; um 1 nach links verschieben sta day; High-Nibble sichern ; VIA Port B, Funktion 10: DHHH lda #$A0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #$08 ; wir brauchen nur das bit 4 (wert=8) lsr A ; und zwar an der 0. Pos. (= 3x rechts schieben) lsr A lsr A ora day ; das high-Nibble dazu sta day ; Gesamtwert speichern lda day ; test, ob 0 (=ungültig, dann warten und neuer versuch) bne doneDay ; weiter dey jmp doneDateAndTime ; alle versuche durch, mit 0 zurückgeben lda #sensorWait jsr delayMs jmp tryDateAndTime doneDay: ; --- STUNDE --- ; VIA Port B, Funktion 10: DHHH) lda #$A0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #%00000111 ; nur die unteren 3 Bits behalten asl A ; um 2 nach links verschieben (da kommt noch was rechts) asl A sta hour; High-Nibble sichern ; VIA Port B, Funktion 11: HHMM lda #$B0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #%00001100 ; wir brauchen nur bit 3 und 2 lsr A ; und zwar an der 0. Pos. (= 2x rechts schieben) lsr A ora hour ; das high-Nibble dazu sta hour ; Gesamtwert speichern ; --- MINUTE --- ; VIA Port B, Funktion 11: HHmm lda #$B0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #%00000011 ; nur die unteren 2 Bits behalten asl A ; um 4 nach links verschieben (da kommt noch was rechts) asl A asl A asl A sta minute; High-Nibble sichern ; VIA Port B, Funktion 12: mmmm lda #$C0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #%00001111 ; wir brauchen alle 4 bits ora minute ; das high-Nibble dazu sta minute ; Gesamtwert speichern ; --- SEKUNDE --- ; VIA Port B, Funktion 13: xxSS lda #$D0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #%00000011 ; nur die unteren 2 Bits behalten asl A ; um 4 nach links verschieben (da kommt noch was rechts) asl A asl A asl A sta second; High-Nibble sichern ; VIA Port B, Funktion 12: SSSS lda #$E0 sta VIA_PORTB ; dem CoProzessor ein bisschen Zeit gönnen zum Antworten lda #2 jsr delayMs ; Akkuwert lang in Millsekunden warten ; und Ergebnis lesen (in die 4 unteren Bits) lda VIA_PORTB and #%00001111 ; wir brauchen alle 4 bits ora second ; das high-Nibble dazu sta second ; Gesamtwert speichern ; fertig, der CoProzessor darf wieder faulenzen lda #$F0 sta VIA_PORTB doneDateAndTime: ply rts

Testprogramm

Die frisch entwickelten Unterroutinen müssen natürlich noch getestet werden. Dazu brauchen wir ein Test- und Beispielprogramm. Diese soll In Programmbank 10 wird also das Programm 10-sensor-test.asm abgelegt:

10-sensor-test.asm (klicken, um diesen Abschnitt aufzuklappen)
; Test der Sensoren / Abfrage über den CoProzessor ; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-10-06 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 --- ; 40 KHz ; 200 nF Kondensator von 7 Hz bis 2 KHz ; Ein Takt sind 0.5 ms, ein NOP sind zwei Takte sind 1 ms ; 2 KHz NopsPerMs: equ 1 ; wieviele NOPs braucht es, um eine Millisek. voll zu machen? ; 40 KHz / 7 = TaktfrequenzKHz: equ 40 ; KHz ; wieviele NOPs braucht es, um eine Millisek. voll zu machen? NopsPerMs: equ 5 ; TaktfrequenzKHz / 8 ; --- 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 INCLUDE math.inc ; getRandom etc. INCLUDE sensors.inc ; get Temperature etc. ; --- Hauptprogramm --- reset: jsr ioInit start: LCD_CLEAR ; LCD löschen (Makro) LCD_PRINT msgAnzeige ; Makro-Aufruf, siehe macros.inc werteAusgeben: ldx #$FF werteAusgeben2: jsr getFernbedienung lda fernbd sta LED ; auf LED ausgeben dex bne werteAusgeben2 ; der Fernbedienung mehr Zeit einräumen jsr getTemperature LCD_POS #32 ; LCD-Cursor positionieren (Makro) LCD_VAL tempC, 2 jsr getHumidity LCD_POS #39 ; LCD-Cursor positionieren (Makro) LCD_VAL humidity, 2 jsr getDateAndTime ; Echtzeit holen LCD_POS #0 LCD_VAL day, 2 LCD_POS #2 LCD_VAL month, 2 LCD_POS #7 LCD_VAL year, 2 LCD_POS #15 LCD_VAL hour, 2 LCD_POS #18 LCD_VAL minute, 2 LCD_POS #21 LCD_VAL second, 2 ; dem CoProzessor eine kleine Pause gönnen ; lda #50 ; jsr delayMs jmp werteAusgeben stop: STP ; Hält die CPU an ; --- Unterprogramme --- ; --- im ROM abgelegte Konstanten --- msgAnzeige: ; 1234567890123456 ASCII __.__.20__ ASCII __:__:__ ASCII __ byte lcd_grad ASCII C, __ %RH 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

Das meiste daraus kennen wir ja schon und wäre Wiederholung, darum hier nur der interessante Abschnitt im Detail: start: LCD_CLEAR ; LCD löschen (Makro) LCD_PRINT msgAnzeige ; Makro-Aufruf, siehe macros.inc werteAusgeben: ldx #$FF werteAusgeben2: jsr getFernbedienung lda fernbd sta LED ; auf LED ausgeben dex bne werteAusgeben2 ; der Fernbedienung mehr Zeit einräumen jsr getTemperature LCD_POS #32 ; LCD-Cursor positionieren (Makro) LCD_VAL tempC, 2 jsr getHumidity LCD_POS #39 ; LCD-Cursor positionieren (Makro) LCD_VAL humidity, 2 jsr getDateAndTime ; Echtzeit holen LCD_POS #0 LCD_VAL day, 2 LCD_POS #2 LCD_VAL month, 2 LCD_POS #7 LCD_VAL year, 2 LCD_POS #15 LCD_VAL hour, 2 LCD_POS #18 LCD_VAL minute, 2 LCD_POS #21 LCD_VAL second, 2 ; dem CoProzessor eine kleine Pause gönnen ; lda #50 ; jsr delayMs jmp werteAusgeben stop: STP ; Hält die CPU an ; --- im ROM abgelegte Konstanten --- msgAnzeige: ; 1234567890123456 ASCII __.__.20__ ASCII __:__:__ ASCII __ byte lcd_grad ASCII C, __ %RH BYTE 0 Ganz zu Anfang wird das Gerüst der Meldung auf dem LCD angezeigt. Hier sind alle auszufüllenden Variablen noch mit Unterstrichen belegt. Das Gerüst bleibt die ganze Zeit stehen. Die variablen Werte werden jeweils geholft und per Positionierungsbefehl an die richtige Stelle im Gerüst geschrieben. Das spart Zeit und erspart einem ein Flackern, würde man alles immer wieder löschen und neu schreiben.

Danach startet eine Endlosschleife, in der die Daten immer und immer wieder ausgegeben werden.

Der LED-Ausgabe der Fernbedienung (getFernbedienung) wird dabei mehr Zeit eingeräumt, damit man den Tastendruck auch gut erkennen kann. Darum wird dieser 255 mal durchlaufen, bevor danach mit getTemperature, getHumidity und getDateAndTime die restlichen Werte vom CoProzessor geholt und dann an der richtigen Position auf dem LCD angezeigt werden.

Durch Kapselung und Verwendung von Unterroutinen und Makros erhalten wir hier einen übersichtlichen Source-Code für das Hauptprogramm. Assembler muss kein Spaghetti-Code sein.

Vorführung des fertigen Programms und Erklärung

In folgendem Video zeige ich zuerst das fertige Programm. So kann man sich schon mal ein Bild dessen machen, was wir programmieren wollen. Im Anschluss erkläre ich den Source-Code in 6502-Assembler, der das leistet:



Ausblick

Im nächsten Teil wird es um das Interrupt-Handling mithilfe des 6522 gehen.