Mouse: Erste Schritte

hw_setup_2Nachdem ich vor einiger Zeit die W65C02 CPU im Freerun Modus getestet und dabei gleich mein neues Spielzeug, einen 16 Channel Logicanalyzer eingeweiht habe, soll es nun einen Schritt weiter gehen.
Die CPU soll zum ersten Mal „echten“ Code ausführen.

Aber zunächst zurück zum Anfang. Als „Free Run“ bezeichnet man ein Setup, bei dem die CPU nahezu ohne zusätzliche Beschaltung betrieben wird. Dabei nutzt man den Umstand, das ziemlich jede CPU ein „NOOP“ Kommando (No Operation) kennt, ein Befehl bei dem die CPU einfach nichts macht, sondern sofort den nächsten Befehl aus dem Speicher holt. Der Trick ist nun, das es gar keinen Speicher gibt. Der Adressbus der CPU ist komplett unbeschaltet, lediglich der Datenbus ist genau mit der Bitfolge belegt, der dem Opcode des „mache nix“ Kommandos entspricht. Bei der 6502 CPU ist das $EA (NOP). Bei einem RESET liest die CPU zunächst den Reset-Vektor aus $FFFC und $FFFD aus, was bei den hart verdrahteten Datenleitungen $EAEA als Adresse ergibt. Anschließend wird der Programmcounter (PC) auf diese Adresse gesetzt und der dort liegende Code ausgeführt. Da aber egal, was die CPU auf den Adressbus legt, jedesmal ein NOP ($EA) gelesen wird, führt die CPU einfach jedesmal diesen Befehl aus und erhöht den Programmcounter. Das führt dazu, das die CPU den kompletten 64k großen Adressraum durchläuft. Durch das Aufzeichnen der Signale aller 16 Adressleitungen mit dem Logic Analyzer konnte ich das sehr gut beobachten.

Der nächste Schritt ist nun, echten Code ausführen zu lassen. Dazu wird ein AT28C64B EEPROM verwendet, den ich bereits erfolgreich mit dem MEEPROMMER beschreiben konnte. Dieser ist 1:1 mit dem Adress- und dem Datenbus der CPU verbunden. Die Leitungen /CE (Chip enable) und /OE (output enable) sind gegen Masse geschaltet und damit immer aktiv, /WE (write enable) ist dagegen auf +5V gelegt, denn wir wollen ja nur lesen. Als Programm kommt folgendes zum Einsatz:

.org $e000 ; start at $e000
.outfile "romtest.bin" ; name the file

.scope
LDX #$00 ; A2 - 2 cycles
outer1: LDY #$00 ; A0 - 2 cycles
inner1: NOP ; EA - 2 cycles \
INY ; C8 - 2 cycles 7 cycles for inner loop
BNE inner1 ; D0 - 3 cycles /
INX ; E8 - 2 cycles
BNE outer1 ; D0 - 3 cycles -> 2 + 2 +3 cycles outer loop
JMP $f000 ; 4C - 3 cycles
.scend

.advance $f000 ; fill up with $00 to $f000

.scope
LDX #$00 ; set X to 0
outer2: LDY #$00 ; set Y to 0
inner2: NOP ; do nothing
INY ; increment Y
BNE inner2 ; if Y != 0 branch to inner loop
INX ; if Y == 0 increment X
BNE outer2 ; if X != 0 branch to outer loop
JMP $e000 ; jump back to first loop
.scend

.advance $fffc ; fill up to reset vector
.word $e000 ; set reset to $e000
.word $0000 ; fill last vector to $0000

hw_setup_1Es ist zweimal die gleiche Schleife, einmal ab Adresse $E000 und einmal ab Adresse $F000 (damit passt der Code genau in das 8k EEPROM). Beide Adressen unterscheiden sich dadurch, das bei der einen Adresse ($F000 – 1111 0000 0000 0000) das 13te Bit gesetzt ist und bei der anderen Adresse ($E000 – 1110 0000 0000 0000) nicht. Am Ende jedes Codeblocks wird jeweils zum anderen gesprungen. Jeder Codeblock beinhaltet zwei ineinander geschachtelte Schleifen die ingesamt 256 x 256 = 65536 Mal ausgeführt werden. Rechnet man die Taktzyklen zusammen kommt man auf (256 * 7 + 7) * 256 + 3 = 460547 Zyklen. Bei 1 MHz Taktfrequenz müßte dann das 13te Bit des Adressbus alle 0.461 Sekunden umschalten. Das der EEPROM 8 Mal im gesamten Adressraum gespiegelt ist, spielt dabei keine Rolle. Das Bild zeigt den Aufbau inkl. Takterzeugung und notwendiger Minimalbeschaltung (Reset und definierte Signale für div. Leitungen). Zur visuellen Kontrolle kommt wieder mein kleiner „Bus-Sniffer“ zum Einsatz, diesmal jedoch nur mit einer LED.
Der Assemblercode wurde mittels Ophis Assembler in ein genau 8192 Byte (8k) großes binäres Image übersetzt, das ich direkt auf den EEPROM brennen kann. Eine kurze Kontrolle im „Simple JBurn“ zeigt die erwarteten Codefragmente und den Reset-Vektor auf $FFFC an den richtigen Stellen im sonst mit „$00“ gefüllten EEPROM.
Nach dem Verkabeln und überprüfen der Verbindungen wird die Schaltung mit Strom versorgt und wie erwartet, blinkt die LED im geschätzten 0,5 Sekunden-Takt. Was mich doch etwas überraschte war, das es sofort ohne Fehler funktioniert. Das macht Mut auf mehr. …
Zur Kontrolle habe ich dann noch das Oszilloskop an die Adressleitung geklemmt und das Signal gemessen :

osci_1 osci_2

Wie man sieht, stimmt sogar meine Rechnung. Die High- bzw. Low-Phase ist genau 0.461 Sekunden lang.

Schreibe einen Kommentar

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

*