REU Programmierung

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

CBM prg StudioMit Assembler auf den Speicher der REU zugreifen

Da ich mittlerweile selbst im Besitz zweier REUs 1764 bin, wollte ich natürlich auch gleich mit einem eigenen Programm deren Funktion testen. Wer selbst etwas über die Programmierung der REU erfahren möchte, ist hier also an der richtigen Stelle.

 
Habt ihr bisher noch keine REU, so könnt ihr euch dank Emulatoren oder dem TC64 aber trotzdem mit der Programmierung dieser Erweiterung beschäftigen:

VICE
Startet VICE und wählt den Menüpunkt „Einstellungen -> Erweiterungsmodul I/O Einstellungen -> REU Einstellungen…“ aus. Setzt den Haken bei „RAM Speichererweiterung aktivieren“ und wählt 256KB (entspricht der 1764) als Größe. Am besten speichert ihr die Einstellungen erstmal. Damit wäre auch alles vorbereitet. Ich selbst habe meine Programme auch mit WinVICE und aktivierter REU entwickelt. Anschließend aber auch auf dem Turbo Chameleon 64 und einem echten C64 getestet. Wie sich rausstellte war das auch notwendig (s. ganz unten).

Turbo Chameleon 64
Ruft das Menü auf und wählt „Options“ an, hier etwas nach unten scrollen und unter „REU size“ 256KB einstellen. Im Hauptmenü wirkt sich der Punkt [F2] Clear ALL Memory nun auch auf den REU-Speicher aus. Auch hier solltet ihr die Einstellungen erstmal speichern.
Wer sich keine echte REU anschaffen möchte, der ist mit der simulierten REU des TC64 sehr gut bedient. Sie funktioniert bisher einwandfrei und ihr kommt so in den Genuß von bis zu 16MB zusätzlichen Speicher! Modifizierte REUs mit mehr als 2MB sind da schon schwerer zu finden.

 

Funktionsweise
Die REU ist, ähnlich wie das GeoRAM, in Speicherbänke (banks) unterteilt. Allerdings hat bei der REU eine Bank 64KB (beim GeoRAM waren es 16KB). Da wir wieder ein BYTE zur Auswahl einer Bank haben, kommen wir so (in der Theorie) auf einen max. Speicher von 64KB * 256 = 16384KB = 16MB.
Wir können (im Gegensatz zum GeoRAM) nicht direkt auf den Speicher der REU zugreifen! Für die Kommunikation verfügt die REU über den RAM Expansion Controller (REC). Diesen blendet die REU, wie von anderen Modulen gewohnt, ab der Adresse  $DF00  ein.

Funktionsprinzip der REU (Grafik aus dem 1764-Handbuch)
Funktionsprinzip (Grafik aus dem 1764-Handbuch)

 

Wie bei „RAM Expansion Unit (REU)“ beschrieben, werden die Daten eigenständig und schnell per DMA kopiert, ausgetauscht und verglichen, aber wo Licht ist, ist auch Schatten (jedenfalls etwas).

Während des DMA Zugriffs wird die CPU des C64 angehalten! Die anderen Chips laufen allerdings weiter, d. h. dass z. B. der TIMER unerkannt über einen Punkt hinweg laufen kann, den ihr eigentlich abfangen möchtet. Dies mag in der Praxis eher selten ein Problem sein, aber behaltet dies im Hinterkopf.
Außerdem kann man, im Gegensatz zum GeoRAM, kein Programm direkt im „neuen“ Speicher ausführen. Bei der REU müssen die BYTEs immer per DMA zum C64 übertragen werden. Das GeoRAM blendet dagegen je 256 BYTEs im Speicher des C64 ein, somit kann der C64 auch direkt auf dort befindliche Programmteile zugreifen. Ihr müsst also beim GeoRAM nicht zwingend kopieren.

 

Der RECinfoInfo(R)AM (E)xpansion (C)ontroller bietet uns vier einfache Befehle mit denen wir auf die Daten zugreifen können:

  • STASH: Daten vom C64 zur REU kopieren
  • FETCH: Daten aus der REU zum C64 kopieren
  • SWAP: Daten zwischen C64 und REU austauschen
  • VERIFY: Daten im C64 und in der REU vergleichen

Um diese Befehle nun zu nutzen, finden wir, wie eben erwähnt, ab der Adresse $DF00 die insg. 11 Register des RECinfoInfo(R)AM (E)xpansion (C)ontroller:

AdresseBezeichnungFunktion
$DF00Status-RegisterDer Status kann nur gelesen werden!
BIT
7 : Interrupt aufgetreten (= 1)
6 : Transfer beendet (= 1)
5 : Verify-Fehler (= 1)
4 : Größe (0 = 128KB / 1=ab 256KB)
3-0 : Version (???)

Die BITs 7-5 werden beim Lesen gelöscht!
Die Größe und Version wurde nie richtig implementiert und ist somit kaum zu gebrauchen.
$DF01Kommando-RegisterHier erwartet die REU den Befehl, der ausgeführt werden soll. Dieses Register sollte (in der Regel) erst ganz am Ende geschrieben werden, da die REU sofort mit der Ausführung beginnt (einzige Ausnahme, wenn BIT 4 gesetzt ist). Das Verhalten der Hauptbefehle (BIT 1-0) kann durch die anderen BITs beeinflusst werden.
BIT
7 : Befehl ausführen (immer 1)
6 : <<< unbenutzt >>>
5 : AUTOLOAD
(= 1 -> Register $DF02-$DF08 NACH der
Befehlsausführung wiederherstellen)
4 : aufs Schreiben nach $FF00 warten
(0=warten / 1=sofort ausführen)
3-2 : <<< unbenutzt >>>
1-0 : Der gewünschte Befehl:
00 = STASH (C64 --> REU)
01 = FETCH (REU --> C64)
10 = SWAP (C64 <-> REU)
11 = VERIFY (C64 ??? REU)

Damit die REU das Kommando akzeptiert, muss immer das höchste BIT gesetzt sein!
$DF02C64 AdresseLSB
$DF03C64 AdresseMSB
$DF04REU AdresseLSB
$DF05REU AdresseMSB
$DF06REU Bank64KB Bank festlegen (je nach Speichergröße $00 - $FF)
$DF07Anzahl der BYTESinfoInfoWieviele BYTES sollen kopiert, getauscht oder verglichen werden? LSB
$DF08Anzahl der BYTESMSB
$DF09Interrupt MaskeDer REU ermöglichen Interrupts auszulösen
BIT
7: Interrupts aktivieren (= 1)
6: Interrupt, wenn Aktion beendet (= 1)
5: Interrupt, wenn beim VERIFY ein Unterschied
gefunden wurde (= 1)
4-0: <<< unbenutzt >>>
$DF0AAdress-Kontroll-RegisterMan kann das Hochzählen der Adressen bei der Befehlsausführung verhindern.
BIT
7-6: 00 - beide hochzählen (Standard)
01 - REU Adresse nicht erhöhen
10 - C64 Adresse nicht erhöhen
11 - REU & C64 Adresse nicht erhöhen
5-0: <<< unbenutzt >>>

 

Ein Blick auf die Register
Bevor wir nun tatsächlich loslegen, noch ein abschließender Blick auf die Register. Dabei bedürfen  $DF02 –  $DF08 wohl keiner weiteren Erklärung. Dort stehen ja nur BYTEs für Speicheradressen, Bank und Anzahl der BYTEs.
Die ersten und letzten beiden sollten wir aber nochmal etwas genauer betrachten.

 

$DF00Statusregister
Hier können verschiedene Infomationen zur REU und unseren Aktionen ausgelesen werden. Das Register kann nur gelesen werden, dabei werden die BITs 7-5 gelöscht! Daher solltet ihr das Statusregister vor einer Aktion einmal lesen, wenn ihr es anschließend prüfen möchtet.

  • BIT 7: Steht ein Interrupt von der REU an und wurde noch nicht bearbeitet, dann ist dieses BIT auf 1 gesetzt.
     
  • BIT 6: Dieses BIT wird am Ende eines Befehls (STASH, FETCH, SWAP, VERIFY) auf 1 gesetzt. Da die CPU aber angehalten wird, wissen wir auch so, wann der Transfer beendet ist, nämlich sobald unser Programm weiterläuft. 😉
     
  • BIT 5: Vergleichen wir mit dem VERIFY-Befehl zwei Speicherbereiche, dann können wir hier ablesen, ob diese identisch (0) oder unterschiedlich (1) sind.
     
  • BIT 4: Mit einem BIT kann die Größe natürlich nicht korrekt angezeigt werden, bei 128KB steht hier eine 0, ab 256KB eine 1infoInfoWir können also nur feststellen, ob 128KB oder mehr Speicher vorhanden ist. Die exakte Größe ab 256KB können wir hier nicht auslesen..
     
  • BIT 3-0: Hier soll eigentlich eine Version stehen, aber sowohl die original Anleitung als auch „das Internet“ schweigen sich zu diesen BITs aus. Ich nehme daher an, dass die Version nie wirklich implementiert wurde.


$DF01Kommandoregister
Über dieses Register bringen wir die REU dazu, den von uns gewünschten Befehl auszuführen.

  • BIT 7: Mit dem höchsten BIT teilen wir dem RECinfoInfo(R)AM (E)xpansion (C)ontroller mit, dass er unseren Befehl, jetzt ausführen soll. Normalerweise wird der Befehl sofort verarbeitet, eine Ausnahme gibt es aber, wenn BIT-4 gelöscht ist.
     
  • BIT 6: << unbenutzt >>
     
  • BIT 5: Ist dieses BIT gesetzt, dann wird das sog. AUTOLOAD ausgeführt. Hierbei werden nach Ausführung eines Befehls die Register $DF02 bis $DF08 wieder mit ihren Ausgangswerten gefüllt. Das spart Zeit und Arbeitsspeicher (wir benötigen ja keine weiteren Befehle, um die Werte erneut zu setzen), wenn man häufig die selben Adressen verwendet (so wie gleich in unserem Beispiel) ist das durchaus sinnvoll. Normalerweise werden diese Register während der Verarbeitung hochgezählt und müssten so erneut initialisiert werden.
    ACHTUNG: Verwendet man AUTOLOAD beim VERIFY, dann geht bei einem gefundenen Unterschied die Adresse verloren, in der dieser aufgetreten ist!
     
  • BIT 4: Steht dieses BIT auf 1, dann wird der Befehl sofort ausgeführt. Mit einer 0 weisen wir den REC an, solange zu warten, bis ein Schreibzugriff auf die Adresse $FF00 stattfindet. Richtig $FF00!
    Wer sich nach dem Sinn fragt:
    Stellt euch vor, ihr möchtet auf den RAM-Speicher von $DF00-$DFFF zugreifen. Dann muss der IO-Bereich ausgeblendet werden. Nur befinden sich dort leider auch unsere REC-Register und ein Schreiben nach $DF01 landet dann natürlich nicht mehr beim REC!! Mit diesem BIT habt ihr nun die Möglichkeit den IO-Bereich zu deaktivieren und sobald ihr etwas nach  $FF00 schreibt, startet der REC den Befehl.
     
  • BIT 3-2: << unbenutzt >>
     
  • BIT 1-0: Die letzten beiden BITs bestimmen den eigentlichen Befehl.
    • 00: STASH (C64 –> REU)
      vom C64 zur REU kopieren
       
    • 01: FETCH (REU –> C64)
      von der REU zum C64 kopieren
       
    • 10: SWAP (C64 <-> REU)
      Speicher zwischen C64 und REU austauschen
       
    • 11: VERIFY (C64 ??? REU)
      Speicherbereich im C64 mit dem in der REU vergleichen. Im Statusregister $DF00 zeigt BIT-5 an ob die Speicherbereiche identisch (0) oder verschieden (1) sind. Außerdem wird der DMA-Zugriff bei einem Unterschied beendet und man kann in den Adress- und Bank-Registern die Speicherstelle finden, an der der erste Unterschied auftrat. Die Adressen findet man wie eben erwähnt nur, wenn man aufs AUTOLOAD verzichtet!

 

$DF07Interruptmaske
Auf Wunsch kann die REU einen IRQ auslösen, wenn eine Aktion beendet oder ein Unterschied beim VERIFY gefunden wurde. Dies ist ein ganz normaler Interrupt, der über $0314/$0315 verarbeitet werden kann. Natürlich müsst ihr dann noch mit  $DF00 kontrollieren, ob der IRQ wirklich von der REU kam.

  • BIT 7: Um überhaupt Interrupts von der REU zu erlauben, muss hier eine 1 stehen.
     
  • BIT 6: = 1, falls ein IRQ am Ende einer Aktion gewünscht wird
     
  • BIT 5: = 1, um einen Interrupt auszulösen, wenn beim VERIFY ein Unterschied vorliegt
     
  • BIT 4-0: << unbenutzt >>
     

$DF08Adresskontrollregister
Wie beim AUTOLOAD erklärt, werden normalerweise die Adressen, die wir dem REC mitgeteilt haben, hochgezählt. Hier könnt ihr festlegen, was höchgezählt werden soll.

  • BIT 7-6: Mit diesen beiden BITs bestimmt ihr, das Hochzählen.
    • 00: beide Adressen hochzählen (dies ist der Standard)
       
    • 01: Adresse für die REU festsetzen, nur die für den C64 hochzählen. Dies macht Sinn, wenn man z. B. einen Speicherbereich im C64 löschen möchte. Dann benötigt man nur ein BYTE in der REU.
       
    • 10: Adresse für den C64 festsetzen, nur die für die REU hochzählen. Das Handbuch meint, man können so z. B. Daten über IO ausgeben. Fraglich ist nur welches C64 Gerät 1MB/Sek. verarbeiten kann. Man kann so aber auch den Speicher der REU schnell löschen und benötigt (wie eben) wieder nur ein BYTE.
       
    • 11: Beide fixieren!?? Was man damit wohl anfangen soll???
       
  • BIT 5-0: << unbenutzt >>

 

Ein kleines Testprogramm
Wie schon beim GeoRAM besteht die erste Herausforderung darin festzustellen, ob überhaupt eine REU vorhanden ist. Man kann nun entweder den User danach fragen oder versucht es per Programm herauszufinden. Wir wollen es per Programm ermitteln. Dazu schreiben wir einfach in die Register der REU Testwerte und prüfen, ob diese auch wieder gelesen werden konnten. Der Bereich ab  $DF00  ist ja für Erweiterungen gedacht. Steckt kein Modul im C64, dann gehen unsere Daten, die wir dorthin schreiben, verloren. Ist ein Modul vorhanden, dann wird es am häufigsten wohl ein ROM-Modul sein, auch da bleibt unser Schreiben erfolglos. Durch dieses Vorgehen, kann es allerdings durchaus zu Problemen kommen. Angefangen bei einer fälschlich erkannten REU bis hin zum Absturz kann vieles passieren. Da es aber kein eindeutiges Erkennungsmerkmal gibt, bleiben uns wenige Alternativen, um automatisch eine REU zu erkennen.

 
Prüfen wir also mal, ob eine REU vorhanden ist…

Am Anfang gibt es wieder jede Menge Variablen / Konstanten. In  main: springen wir aktuell nur nach checkForREU:, um festzustellen, ob überhaupt eine REU angeschlossen ist. Falls ja, wird der Rahmen grün, sonst rot eingefärbt. Da endet das Programm auch schon wieder.

Hinter dem  rts  kommt nun unsere Prüfroutine. Diese funktioniert NICHT so wie die im BASIC-Programm von der originalen Commodore REU-1764-Demo-Disk. Da ich selbst keine REU 1700 (128KB) besitze konnte ich das Verhalten zwar nicht mit echter Hardware überprüfen, aber unter WinVICE liefert das Statusregister $DF00 den Wert 0, wenn man 128KB einstellt. Das ergibt sich auch aus der Doku, BIT-4 ist erst ab 256KB gesetzt. Die Prüfung der Demo-Disk baut aber darauf auf, dass das Register niemals null sein kann (bei größeren REUs stimmt das auch). Also habe ich meine Prüfung etwas anders angelegt.

Im Y-Register geben wir eine 0 zurück, wenn eine REU gefunden wurde. Zu Beginn gehen wir aber erstmal davon aus, dass es keine gibt und füllen Y mit #$FF. Dann lesen wir das Statusregister, dadurch werden die BITs 7-5 gelöscht. Da der Status nicht geschrieben werden kann, muss das anschließende Speichern von #$FF im Statusregister wirkungslos bleiben. Falls das erneute Auslesen des Status aber wieder den selben Wert (#$FF) zurückgibt, wissen wir, dass keine REU vorhanden sein kann. Um jetzt noch etwas sicherer zu sein, schreiben wir in einige schreib/lese Register der REU Testwerte und prüfen, ob diese erhalten bleiben.

Wichtig ist, dass wir nicht alle 11 Register der REU zum Testen verwenden können.  $DF01 lassen wir z. B. gleich mal aus, wir wollen ja nicht versehentlich einen Befehl abschicken.
Hier könnt ihr jetzt erkennen, warum solche Prüfungen durchaus gefährlich sind. Würde ein Programm diese Stellen, so wie wir, zum Testen auf ein anderes Modul füllen, könnte es passieren, dass ein Rechner mit einer REU abstürzt. Bei einer REU würde ein Schreiben nach  $DF01 evtl. einen Kopiervorgang auslösen und könnte dabei wichtige Speicherbereiche „zerstören“ (überschreiben).
Wir prüfen jetzt nur noch $DF02 - $DF05. Die restlichen Register (mit Außnahme von  $DF07 & $DF08) liefern nach dem Schreiben nicht unbedingt den hineingeschriebenen Werte wieder zurück.

Also schreiben wir in einer Schleife einfach unseren Schleifenzähler (im X-Register) in das jeweilige Register. Dann vergleichen wir es mit unserem X-Register, wenn der Wert übereinstimmt ist alles OK und wir prüfen das nächste Register, sonst liegt keine REU vor und wir brechen ab. Wurden alle vier Register erfolgreich geprüft, laden wir eine Null ins Y-Register und kehren zum Aufrufer zurück.

Ein Start sollte mit einem grünen Rahmen anzeigen, dass eine REU gefunden wurde, sonst ist der Rahmen rot.

 

Die REU in Aktion
Achtung: Es wird ein Interrupt verwendet, schaut euch bei Bedarf bitte zumindest den Beitrag „Interrupts“ vorher einmal an.

Da wir jetzt wissen, dass eine REU vorhanden ist, was können wir nun mit ihr anfangen? Eine REU ist natürlich perfekt für einen schnellen Datenaustausch geeignet. Ihr könnt z. B. alle einmal geladenen Teile eures Spiels oder einer Demo dort ablegen. Diese können dann bei Bedarf blitzschnell erneut geladen werden. Daher ist auch ein häufiges Einsatzgebiet eine RAM-Disk. Stellt euch vor, ihr möchtet am C64 entwickeln. Nun braucht ihr einen Assembler, Sprite-Editor, Character-Editor, ein Grafik- und Soundprogramm. All dies könntet ihr in die REU laden und dann per Tastendruck oder Befehl das jeweilige Programm laden. Es ist dann praktisch sofort verfügbar.
Wir machen dies nur in einer vereinfachten Form. Wir werden ein Programm schreiben, dass den aktuellen Bildschirm speichern, wiederherstellen, austauschen und vergleichen kann. Das Programm läuft neben dem BASIC und wird über die Funktions-Tasten bedient.

Wie ihr am Anfang bereits erfahren habt und auch der Tabelle sowie dem ersten Listing entnehmen könnt, bietet uns die REU im Grunde nur vier einfache Kommandos: STASH, FETCH, SWAP und VERIFY. Wie es der Zufall mal wieder so will, entsprechen diese vier Befehle exakt unseren vier Funktionen von eben. Also Zufälle gibt es… 😉

Fügt doch bitte noch diese Konstante zum Programm hinzu:

 

Dann ersetzt alles zwischen  *=$0801 und dem  rts mit unserem neuen Programmbeginn.

Außer der veränderten BASIC-Zeile ist der Anfang nahezu identisch geblieben. Wir prüfen ob eine REU vorhanden ist. Falls nicht springt das Programm zu @error:, setzt die Rahmenfarbe auf rot und wird beendet. Ist aber eine REU vorhanden, dann wird als Erstes der IRQ-RAM-Vektor auf unsere Interruptroutine MyIRQ: umgebogen. Dann können wir schon mal einige REC-Register füllen. Da wir ausschließlich AUTOLOAD verwenden, bleiben unsere Werte in den Registern erhalten. Außerdem verwenden wir nur einen bestimmten Bereich, mit immer der selben Länge, es reicht diese Werte hier einmalig zu setzen. Also teilen wir der REU erstmal mit, auf welchen Speicherbereich des C64 wir zugreifen wollen. Da wir den Bildschim retten möchten, beginnt der für uns relevante Speicher ab $0400 / SCREENRAM (wir beschränken uns auf den Standard). Danach müssen wir die Anzahl der BYTEs setzen, die wir benötigen. Der Bildschirm hat 25 Zeilen zu je 40 Zeichen, also 1000 (oder $03E8) Zeichen. Jetzt müssen wir noch bestimmen, welchen Speicherbereich in der REU wir verwenden möchten. Dies geschieht einmal über die Bank, wir nehmen die erste mit der Nr. 0 und über die Adresse innerhalb der gewünschten Bank. Diese Adresse setzen wir einfach auf $0000 und beginnen somit mit dem ersten BYTE der REU. Genug der Vorbereitungen, jetzt noch den Rahmen grün einfärben und dann zurück zum BASIC.

Kommen wir jetzt zu unserer Interrupt-Routine. Diese kümmert sich um die Tastaturabfrage (werft ggf. einen Blick auf den Beitrag „Tastatusmatrix“), löst evtl. die REU-Funktion aus und springt schließlich zur Systemroutine nach $EA31. Fügt sie einfach hinter die hoffentlich nicht gelöschte Funktion checkForREU: ein.

Um Platz für ein BASIC-Programm zu lassen, beginnt unsere Interruptroutine erst bei  *=$C000. Da wir bei einem Tastendruck nicht dutzendfach eine Funktion auslösen wollen, merken wir uns den letzten Wert in lastInput:, diesen holen wir zu Beginn des Interrupts ins X-Register. Dann prüfen wir, ob eine Tastatureingabe vorliegt. Da uns nur die F-Tasten (F1-F7) interessieren maskieren wir PA0 aus. Dann schauen wir nach, ob dort eine Taste betätigt wurde. Da die Werte low aktiv sind (0 = Taste gedrückt) kehren wir die zum besseren Verständnis einfach um und maskieren die vier von uns gewünschten Tasten (BIT 6-3) aus. Diesen Wert speichern wir bei lastInput:, sollte er null sein, ist aktuell keine Taste gedrückt und die Funktion kann beendet werden. Sonst prüfen wir noch, ob zuletzt eine Taste gedrückt wurde. Ihr erinnert euch, dass wir im X-Register den Wert der letzten Tastatureingabe gespeichert haben? Ist die letzte Eingabe null, können wir die aktuelle verarbeiten, sonst geht es wieder direkt zum Ende. Das machen wir, da sonst mit einem Tastendruck eine Funktion mehrfach ausgeführt wird. Der IRQ wird immerhin 60 mal in der Sekunde gestartet! Jetzt wird per links-shift das höchste BIT verworfen, bevor durch weitere Shift-Befehle auf die einzelnen Funktionstasten geprüft wird. Wir prüfen hier übrigens nur auf die Taste als solches. Ob F1 oder F2 betätigt wurde merkt das Programm nicht! Stellt das Programm fest, das eine mögliche Taste gedrückt ist, dann wird zur entsprechenden Funktion gesprungen. Falls nicht, läuft das Programm zum Ende durch und springt schließlich zur Systemroutine nach $EA31.
Das war auch schon unsere Interrupt-Routine.

Vergesst nicht die Variable für die letzte Tastatureingabe. Fügt die einfach direkt hinter der obigen Funktion ein:

 

Kommen wir endlich zu den vier Befehlen. Da wir die meisten REC-Register schon beim Programmstart gefüllt haben, bleibt jetzt nur noch wenig Arbeit. Fügt die folgenden Abschnitte auch einfach am Ende ein.

Wie ihr seht, müssen wir nur noch den Befehl (hier fürs Kopieren vom C64 zur REU) ins Register $DF01 schreiben. Da die REU (bei unserem Befehl) sofort loslegt, muss dieser natürlich ganz zum Schluß erfolgen. Wie erwähnt wurden die anderen Register bereits gefüllt, also kann hier einfach kopiert werden. Anschließend wird die Rahmen- und Hintergrundfarbe verändert, bevor es zur Systemroutine geht.

Das Kopieren von der REU zum C64 und der Austausch der Speicherbereiche sind ebenso simpel.

Es gibt nur andere Befehle und Farben.

Einzig beim Vergleichen müssen wir etwas mehr Arbeit investieren, es bleibt aber pipifax.

Vor dem Vergleich löschen wir, durch einfaches Auslesen, das Statusregister. Dann starten wir den Vergleich durch den entsprechenden Befehl im Kommando-Register. Anschließend gehen wir erstmal davon aus, dass die Speicherbereiche identisch sind und laden die Farbe grün ins X-Register. Dann holen wir den Status der REU in den Akku und kontrollieren, ob BIT 5 gesetzt ist. Falls nein, sind die Speicherbereiche identisch und es wird zum Ende gesprungen. Steht das BIT auf 1, dann haben wir unterschiedliche Bereiche und laden rot ins ins X-Reg. Am Ende setzen wir die Hintergrundfarbe auf die Farbe aus dem X-Register und springen zur Systemroutine.

Schon ist unser Programm fertig. Wenn ihr es startet könnt ihr mit F1 den aktuellen Bildschirminhalt retten, F3 holt ihn zurück, F5 tauscht den aktuellen Bildschirm mit dem in der REU und F7 prüft ob der aktuelle BS und der in der REU identisch sind.
Spielt ein wenig damit rum und es wird euch sicher ein Problem auffallen. Der Cursor stört! Er wird mit gerettet, zurückgeholt und verglichen. Um das Programm zu verbessern, sollten wir den Cursor ggf. ausblenden. Wie das geht überlasse ich euch. Eine Möglichkeit ist, sich die Systemroutine bei $EA31 anzuschauen. Diese ist ja auch für das Blinken des Cursors zuständig. Schnappt euch ein kommentiertes ROM-Listing und ihr werdet schnell fündig.


Der Java-Emulator bietet leider keine REU-Emulation an. Es gibt aber ein D64-Image als Download. Dort findet ihr obiges Testprogramm (inkl. der Cursor-Bereinigung).

Als Bonus gibt es zusätzlich den…

REU-Checker V1.0

Der REU-Checker V1.0 prüft REUs von 128KB bis 16MB.
Der REU-Checker V1.0 prüft REUs von 128KB bis 16MB.

Das Programm prüft, ob eine REU vorhanden ist. Falls ja, wird die Größe der REU ermittelt. Da mir noch keine anderen Größen untergekommen sind, erkennt das Programm 128KB, 256KB, 512KB, 1MB, 2MB, 4MB, 8MB und 16MB. Bei anderen Größen wird ein Fehler angezeigt. Im Speicher des C64 wird dann ein 8KB großes Testmuster (immer BYTE $00 – $FF) erzeugt. Am schönsten wären natürlich 64KB, so groß ist ja eine Bank, aber dazu würden wir den kompletten Speicher des C64 benötigen. Deshalb habe ich mich als Mittelweg für 8KB entschieden. Ein Zeichen im Statusbildschirm entspricht einer 64KB-Bank in der REU. Jede Bank wird komplett mit dem 8KB Muster (also acht Stück davon) gefüllt. Anschließend wird jede Bank wieder in 8KB-Schritten mit dem Muster im Speicher des C64 verglichen. Wie schon beim GeoRAM-Checker, ist der Source wieder sehr gruselig. Daher gibt es nur das ausführbare Programm, es steht euch aber frei (seht es als Übung) das Programm zu disassemblieren. Bei normal großen REUs (128KB – 512KB) ist der Test so schnell durchgelaufen, dass man kaum etwas mitbekommt. Bei größeren REUs (z. B. mit VICE oder dem Turbo Chameleon) dauert der Test auch nicht sehr lange, man sieht dann aber, was geschieht.

 

 

Traue keiner Emulation
Ich habe das Programm entwickelt, ohne im Besitz einer echten REU zu sein und mich dabei an der WinVICE-Emulation orientiert. Anschließend testete ich den REU-Checker für alle Größen von 128KB bis 16MB mit WinVICE und dem Turbo Chameleon 64. Alles lief wunderbar. Als ich dann eine umgebaute REU 1764 (512KB / 1MB) testete, klappte auch hier im 512KB-Modus zunächst alles wie erwartet. Bei 1MB versagte das Programm allerdings. Es zeigte nur 128KB statt 1MB an. Meine Befürchtung, dass die REU evtl. defekt ist, konnte ich mit einem kleinen BASIC-Testprogramm zum Glück wiederlegen. Nach einigem Suchen bin ich dann hinter das Problem gekommen. Um die Größe zu ermitteln habe ich in das erste BYTE der jeweils höchsten Bank für eine Speichergröße eine laufende Nr. geschrieben. Da es acht mögliche Speicherkonfigurationen gibt, waren es somit die Zahlen 1 (128KB) bis 8 (16MB). Danach habe ich rückwärts kontrolliert, was auf der jeweils höchsten Bank im ersten BYTE steht. Wurde vom REU-Checker versucht auf eine ungültige Bank zuzugreifen, dann liefert WinVICE ein #$FF, sonst kam das von mir gespeicherte BYTE zurück. Nun habe ich einfach solange gesucht, bis etwas anderes als #$FF zurück kam und hier liegt das Problem! WinVICE verhält sich anders als das Turbo Chameleon 64 oder eine echte REU (wobei meine umgebaute noch weitere Probleme veruracht)!! Mit einem kleinen BASIC-Programm habe ich die Funktionsweise der REUs getestet. Dazu habe ich in alle 256 theoretisch möglichen Bänke die Nr. der Bank (0-255) geschrieben und anschließend wieder ausgelesen. Das Turbo Chameleon und die unmodifizierte REU verhalten sich gleich. Wie beim GeoRAM werden bei der Bankwahl nur die BITs beachtet, die zur Speichergröße passen. Greift man z. B. bei 512KB auf die nicht vorhandene Bank 11 zu (es gibt ja nur acht Bänke zu je 64KB), dann wird die 4. Bank verwendet (11 = %00001011 da nur drei BITs für 512KB nötig sind, wird auf %00000011 = 3 zugegriffen, die Zählung beginnt bei 0!). Also liefert mein BASIC-Programm hier eine 3 beim TC64 und einer originalen REU, unter WinVICE aber #$FF. Starte ich das BASIC-Programm mit der umgebauten REU klappt es mit 512KB auch wie eben beschrieben, nur 1MB machen Probleme. Bei 1MB kommen total wirre Werte zurück. Beim Zugriff auf Bank 255 liefert die REU eine 1 und somit zeigte der REU-Checker fälschlich 128KB an. Dieses Verhalten liegt daran, dass 1MB (und mehr) nur durch einen massiven Eingriff in die Hardware der REU möglich sind. Sie war nie für soviel Speicher gedacht!

Ende gut, alles gut?!?
Nach dieser kleinen Odyssee habe ich das Programm dann umgebaut, so dass es jetzt sowohl mit WinVICE als auch mit meinen beiden echten REUs und dem Turbo Chameleon 64 funktioniert.
Falls jemand mal eine echte REU 1700 oder 1750 (oder größere Umbauten) testet, wäre eine Rückmeldung, ob das Programm auch dort funktioniert, sehr nett.

Umgebaute REUs (über 512KB) sind also nicht 100%ig kompatibel!

 

Update vom 20.03.2014
Wie in den Kommentaren gewünscht, folgt hier meine Routine, um die Speichergröße der REU zu ermitteln.

Das Programm geht davon aus, dass eine REU vorhanden ist! Die Routine  checkREUSize ermittelt die Größe, indem sie in das erste BYTE der jeweils höchsten Page eine laufenden Nummer schreibt (1 = 128KB, 2 = 256KB, 3 = 512KB … 8 = 16MB).

REU: Übersicht der Speicherseiten (pages)Anschließend werden diese BYTES wieder ausgelesen und mit dem erwarteten Ergebnis verglichen. Stimmen die Werte nicht oder sind wir am Ende (bei max. 16MB) angelangt, dann wissen wir, wie groß die REU ist. Zur Kontrolle gibt das Beispiel noch obenlinks die ermittelte REU-Größe als Zahl von 1 bis 8 aus.

Da ein einzelnes BYTE als Erkennung evtl. nicht soooo sicher ist, könnt ihr die Routine natürlich auch auf ein größeres Muster umstellen und dann mit VERIFY prüfen, ob das richtige Ergebnis gefunden wurde. Hier wird z. B. immer mit [Nr.]REUSIZE geprüft…

 

 


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

Loading...


 

<<< zurück |

 

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

2 Gedanken zu „REU Programmierung“

Schreibe einen Kommentar

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

Protected by WP Anti Spam