Ich liebe meine REU, kann es ihr aber nicht sagen oder Die REU-Programmierung im Detail Eine kleine Ausschweifung von Willcox Diesen Text veröffentlichte ich 1996 in der Tiger Disk 1) Das übliche Bla Bla Vieles wurde gesagt über die REU, vieles hat auch gestimmt, vieles war aber auch nur halbwahr. In diesem Text wird alles schonungslos offengelegt, auch die Details, die sich keiner zu erzählen traut. Wenn Du dies bis zum Ende liest bist Du ein REU-Profi der, für sich selbst oder andere, dieses Modul bis zum Letzten ausreizen kann. Und Du wirst staunen wie nützlich eine REU sein kann, mit nur ein wenig Fantasie. Nicht nur bei bereits fertigen Programmen, sondern auch bei denen, die Dir in den Fingern jucken und die Du schon immer mal schreiben wolltest. Die noch kommenden Beispiele sind ein bunter Mix aus Basic und Assembler, wobei letztere im Assblaster Format verfaßt wurden. Um einen sanften Einstieg sicherzustellen beginne ich mit den allgemeinen Dingen, die jeder wissen sollte, und erst später schauen wir den Bits unter den Rock. Allgemein ist zum Beispiel, daß Commodore drei verschiedenen REU's das Leben schenkte, als da wären: REU 1700: 128 KB. War nicht lange am Markt. REU 1764: 256 KB. Geplant für den Einsatz am C64. REU 1750: 512 KB. Für den C128. Das Handbuch führt dazu aus, daß eine 1764 nur am C64 funktioniert und eine 1750 nur am C128, nicht umgekehrt. Dies ist ein wenig unerklärlich, in der Praxis haben sich mit unterschiedlichen Konfigurationen noch nie Probleme ergeben. Gemeint war wahrscheinlich, daß 512 KB mehr Strom brauchen als 256 und nur das C128-Netzteil diesen liefern kann. Dies ist Quatsch. Wahr ist aber, daß der C64 ein neues, stärkeres Netzteil benötigt, welches der REU allerdings beigelegt wurde. Der Vollständigkeit halber erwähne ich noch, daß sich zumindest die 1764 mit ein wenig Löterei problemlos auf 512 KB aufrüsten läßt. Auch 1 MB und 2 MB Aufrüstungen sind möglich, dies erfordert aber tiefschneidende Eingriffe in die Hardware und geht oft schief. Für den, der auf viel Speicher nicht verzichten kann empfiehlt sich ein Komplettkauf. (Preis und Bezugsquelle am Ende des Textes.) Das neue, starke Netzteil erkennst Du an der Backstein-Form (nicht abgeflacht) und an dem roten Aufkleber HiPot tested, sofern er denn noch da ist. 2) Speicher, ach, wo bist du geblieben? Nun haben wir ein starkes Netzteil und die REU steckt auch schon im Expansionport. Der Computer sagt aber immer noch 38911 BASIC BYTES FREE.Hadere nicht, das ist schon richtig so. Immer noch ist der C64 (und der C128) ein 8-Bit Computer und kann nur 64 KB direkt ansprechen. Drum haben sich die Commodore-Entwickler einen kleinen Kniff einfallen lassen: Die REU besitzt den sogenannten REC, den Ram Expansion Controller, der sich im Bereich IO 2 ab $DF00 eingeblendet hat. Dort sagen wir ihm, durch einfaches Poken, was wir von ihm wollen. Dies kann folgendes sein: - einen Bereich vom Computer in die REU kopieren (STASH) - einen Bereich von der REU in den Computer kopieren. (FETCH) - einen Bereich im Computer mit einem Bereich in der REU tauschen (SWAP) - einen Bereich im Computer mit einem Bereich in der REU vergleichen. (VERIFY) Speicher direkt in der REU können wir also nicht ansprechen, wir können aber der REU sagen, daß sie uns diesen Speicher in den C64 kopieren soll. Wo und wieviel ist dabei völlig frei wählbar. 3) Paß auf! Das oben gesagte macht die REU ganz alleine, die CPU im Computer hat damit nichts zu tun. Dieses Verfahren des direkten Speicherzugriffs nennt sich DMA (Direct Memory Access) und ist eine feine, schnelle Sache. Doch Vorsicht! Die CPU wird für diese Zeit nicht nur nicht gebraucht, sondern komplett deaktiviert. Im einzelnen: Die REU legt den Prozessorpin AEC (Pin 5) auf low, worauf die CPU vermeint, der VIC würde den Datenbus benötigen und stellt jede Aktivität ein. Diesen Trick benutzen auch die Flash 8/Super CPU und diverse "Prozessorstop-Schaltungen". Die anderen Chips laufen jedoch weiter und darin liegt die Gefahr! So ist ein eventuell gestarteter Timer in einer CIA immer noch am heraufzählen, will sagen: Eine gestartete RS232-Übertragung geht daneben und irgendwelche "abgezählten Zyklen" stimmen nicht mehr. In der Praxis mag das kaum vorkommen, aber der Teufel ist ein Eichhörnchen und schon war's schade gewesen. 4) Wie erkenne ich eine REU? Nun, sie ist groß, hellfarben und es steht 17.. drauf. :-) Spaß beiseite. Wie bereits gesagt hat der REC seine Register ab $DF00 (57088) eingeblendet, 11 an der Zahl. Register Null ($DF00) ist ein Read-Only-Register, kann also nicht beschrieben werden. Die anderen aber wohl. Und dies machen wir uns zunutze! Ohne etwas im Expansion-Port ist der IO-Bereich leer, man kann hineinschreiben was man will. Immer kommt etwas anderes dabei heraus. Module, die sich dort auch einblenden, wie z.B. das Action Replay, haben dort nur ROM, auch nicht beschreibbar. Also müssen wir nur folgendes Programm eingeben: 10 REC=57088 20 POKE REC,0:IFPEEK(REC)=0 THEN 99 30 FOR X=2 TO 5:POKE REC+X,X:NEXT 40 FOR X=2 TO 5 45 IFPEEK(REC+X)<>X THEN 99 48 NEXT 50 PRINT "REU GEFUNDEN!":END 99 PRINT "KEINE REU IM PORT!" In Zeile 20 wird das Read-Only Register auf Null gesetzt. Eine REU schert sich nicht darum (da Read-Only) und eine Null kann von sich aus bei einer REU in diesem Register nie stehen. Im weiteren Verlauf werden die Register 2 bis 5 mit aufsteigenden Zahlenwerten beschrieben. Da wir in Zeile 20 schon sichergestellt haben, daß sich bei $DF00 kein RAM befindet und die geschriebenen Werte bleiben stabil (d.h. dort ist RAM), dann ist eine REU schon recht treffsicher gefunden. Die Register 6-10 lassen wir aus Geschwindigkeitsgründen außen vor und das Register Eins ($DF01) beziehen wir in unsere Prüfung nicht mit ein, da dies das Kommandoregister darstellt und durch Beschreiben die REU mit einem Datentransfer beginnen könnte. Und das wollen wir ja (noch) nicht. Hier das ganze nocheinmal in Assembler: £ba=$c000 ;Startadresse lda #$00 ;Null sta $df00 ;nach Register 0 cmp $df00 ;noch drin? beq noreu ;dann keine REU! ldx #$02 ;Zähler nach X loop1: txa ;als Wert nach Akku sta $df00,x ;speichern inx ;Zähler erhöhen cpx #$0b ;schon alle Register? bne loop1 ;nein, dann zurück ldx #$02 ;Zähler nach X loop2: txa ;als Wert nach Akku cmp $df00,x ;vergleichen bne noreu ;ungleich? Keine REU! inx ;Zähler erhöhen cpx #$0b ;alles verglichen? bne loop2 ;nein, dann zurück lda #jatxt ;... jmp $ab1e ;ausgeben und Ende. noreu: lda #notxt ;... jmp $ab1e ;ausgeben und Ende. jatxt: £tx "jawoll, reu gefunden!" £by 0 notxt: £tx "keine reu im port. schade!" £by 0 5) Die Praxis - Ein Beginn Um die REU zu programmieren braucht man genaue Kenntnis der REC-Register, und ich laß keins davon aus, versprochen. Für unser erstes kleines Experiment sind allerdings nur die folgenden wichtig: $DF01 (57088) Kommandoregister $DF02/03 Computeradresse (Lo/Hi) $DF04/05 REU-Adresse (Lo/Hi) $DF06 REU-Bank (siehe unten) $DF07/08 Anzahl Bytes die wir übertragen wollen (Lo/hi) Der Speicher in der REU ist in Bänken zu je 64 KB organisiert. Maximal sind also ansprechbar: REU 1700: 2 Bänke (0 bis 1) REU 1764: 4 Bänke (0 bis 3) REU 1750: 8 Bänke (0 bis 7) REU mit 1 MB: 16 Bänke (0 bis 16) REU mit 2 MB: 32 Bänke (0 bis 31) Für unser erstes kleines Beispiel kopieren wir den Bildschirm in die Bank 0 und holen ihn dort auch wieder zurück. 10 REC=50788 20 PRINTCHR$(147)"BILDSCHIRM 1" 30 POKE REC+2,0:POKE REC+3,4 40 POKE REC+4,0:POKE REC+5,0 50 POKE REC+6,0 60 POKE REC+7,0:POKE REC+8,4 70 POKE REC+9,0:POKE REC+10,0 80 POKE REC+1,252 100 PRINTCHR$(147)"BILDSCHIRM 2" 101 PRINT"TASTE FUER BILDSCHIRM 1" 110 POKE198,0:WAIT198,1 120 POKE REC+2,0:POKE REC+3,4 130 POKE REC+4,0:POKE REC+5,0 140 POKE REC+6,0 150 POKE REC+7,0:POKE REC+8,4 160 POKE REC+1,253 Das sieht jetzt sehr, sehr schlimm aus, ist es aber nicht. Wie Du weißt, liegt der Bildschirmspeicher gemeinhin bei 1024 ($0400) und ist einschließlich der Spritepointer 1024 ($0400) Bytes lang. Das Low-Byte von $0400 ist 00 und das Hi-Byte 04. Und damit kann man nun prima rechnen. In Zeile 30 wird die Adresse im C64 festgelegt. Low-Byte=0 und Hi-Byte=4. Zeile 40 bestimmt die Adresse in der REU. Wir beginnen am Anfang der Bank, also Adresse 0000 (Lo=0, Hi=0) In Zeile 50 steht die Bank, Null ist Null. In Zeile 60 legen wir die Anzahl der zu übertragenen Bytes fest. Wir übertragen 1024 Bytes, dies sind 400 Bytes in Hexadezimalschreibweise. Low-Byte ist Null und Hi-Byte ist 4. Register 8 und 9 brauchen wir nicht, also setzen wir sie auf Null. Zeile 80 startet nun die Übertragung. Dafür sorgt der Wert 252 im Kommandoregister 1. Ab Zeile 120 geht's andersrum: Die Adressen in der REU und im Computer bleiben natürlich gleich, die Register 9 und 10 brauchen wir auch nicht mehr zurückzusetzen. Die einzige Veränderung: Ins Kontrollregister kommt der Wert 253, der eine Übertragung von der REU zum Computer startet. Et voila! Am besten, ihr tippt das mal ab und probiert es selbst aus. Ansonsten fehlt euch das Erfolgserlebnis und die trockene Terrorie hier steigt euch über den Kopf. Der Übersichtlichkeit wegen hier die wichtigsten Werte für das Kontrollregister 1 ($DF01): 252 STASH (C64 -> REU) 253 FETCH (REU -> C64) 254 SWAP (C64 <> REU) Es gibt natürlich noch einige mehr, aber seien wir geduldig. Hier noch ein universelles Basic-Listing, das ihr in eure eigenen Programme einbauen könnt. Ihr müßt nur die Variablen an das Unterprogramm übergeben. 0 RE=57088 10 REM *** HAUPTPROGRAMM *** 20 CS=1024 : REM STARTADRESSE COMPUTER 21 RS=0 : REM STARTADRESSE REU 22 RB=0 : REM BANK IN DER REU 23 AB=1024 : REM ANZAHL DER BYTES 24 CO=252 : REM KOMMANDO STASH 28 GOSUB 10000 30 REM *** WEITER IM PROGRAMM *** 999 REM *** UNSERE SUBROUTINE *** 1000 POKE RE+3,CS/256 1001 POKE RE+2,CS-PEEK(RE+3)*256 1002 POKE RE+5,RS/256 1003 POKE RE+4,RS-PEEK(RE+5)*256 1004 POKE RE+6,RB 1005 POKE RE+8,AB/256 1006 POKE RE+7,AB-PEEK(RE+8)*256 1007 POKE RE+9,0:POKE RE+10,0 1008 POKE RE+1,CO 1009 RETURN Dieses Beispiel kopiert, wie gehabt, den Bildschirm in die REU-Bank 0. Durch Übergabe anderer Werte an das Unterprogramm habt ihr die REU voll im Griff. 6) Eine weitere Überlegung Wie wär's denn damit: Ihr kennt das Problem, ihr habt ein Basic-Programm gecodet, groß und schön, es werden auch viele DATAs eingelesen. Das Programm nimmt seinen Verlauf und am Ende ist ein Neustart erforderlich (z.B. Game Over bei einem Spiel), damit die Variablen wieder die Anfangswerte haben. Also wieder alle DATAs einlesen. Und warten. Wir jedoch brauchen dies nicht, denn wir sind klug. Am Programmstart, nachdem alle Variablen ihren Wert besitzen, kopieren wir die ganzen Variablen einfach in die REU und bei einem Neustart holen wir alles wieder zurück. Wir übergeben an die obige Subroutine dazu folgende Werte: 20 CS=PEEK(45)+256*PEEK(46) 21 RS=0 22 RB=1 : REM ZUR ABWECHSLUNG BANK 1 23 AB=40960-(PEEK(45)+256*PEEK(46)) 24 CO=252 25 GOSUB 10000 26 CS=45:RS=40960:RB=1:AB=8:CO=252 27 GOSUB 10000 Es wird der gesamte Speicher vom Beginn der Variablen bis zum Ende des Basic-Speichers in REU-Bank 1 übertragen. In Zeile 26 werden die Zeropageadressen der Variablengrenzen in die REU-Bank 1 ab 40960 gebracht. Um alle diese Werte zurückzubekommen ist an das Unterprogramm genau das gleiche abzuliefern, nur mit dem Kommandocode CO=253. Einfacher wäre es, den gesamten Speicher in die REU zu bringen, einschließlich der Zeropage, dann würde unser Programm aber gehörig durcheinandergeraten, da dann z.B. der Zeiger auf das nächste Element im Basic-Text nicht mehr stimmen würde, ebenso der Stack. Und das ist nicht gut. Habt ihr es schon bemerkt? Mit dieser Routine ist noch SEHR VIEL MEHR möglich als nur dies. Denkt mal einen Schritt weiter. 7) Einen Schritt weitergedacht Ihr schreibt an einem Dateiprogramm, z.B. um eure vielen Freundinnen zu verwalten. Und plötzlich reicht das RAM nicht! Dann teilt es euch doch folgendermaßen auf: Freundinnen A-L laden und String- (bzw. Array-)Speicher in Bank 0 klopfen. Freundinnen M-Z laden und ab dafür in Bank 1. Je nach Notwendigkeit kommt nun Bank Null oder Bank Eins in den Compi, wird dort begutachtet, eventuell verändert und dann wieder in die REU kopiert. Und zum Speichern auf Diskette alles wieder zurück. Und wenn euch 2 Bänke (128 KB) für euer Vorhaben nicht reichen, dann teilt es doch in drei Häppchen, oder vier,... Das alles in Basic und blitzschnell! Beachtet aber unbedingt dies: Alle jemals im Programm vorkommenden Variablen, und sei es nur eine pupsige Schleifenvariable, muß vor der ersten REU-Aktion schon definiert sein, zur Not halt mit einem Dummy, z.B. X=0. Ansonsten verschieben sich die Variablen im Speicher und die Pointer zeigen auf ganz falsche Werte. Die Variablen im Computer werden von denen in der REU natürlich überschrieben. Werte, die sich verändert haben und wichtig sind, z.B. AZ für die Anzahl der Freundinnen sollten vorher in Sicherheit gepoket werden, z.B. in den Kassettenpuffer. Wenn ihr dies beachtet, dann könnt ihr Programme schreiben, wie noch nie jemand zuvor. Und jetzt denkt noch einmal einen Schritt weiter. Seht euch in der Rolle des Anwenders, der verschiedene Programme in verschiedenen Bänken der REU haben kann und im Direktmodus zwischen diesen hin und herwechselt. Eine Textverarbeitung, ein Sprite-Editor, ein Assembler, alles zusammen in der REU... Günstigerweise befindet sich irgendwo ein Assemblerprogramm, das alles übernimmt und nur noch aufgerufen werden braucht. Und das geht so: £ba $010b ;Diese Startadresse ist $010b=SYS 267 lda #252 £by $2c ;Diese Startadresse ist $010e=SYS 270 lda #253 gemeinsam: sta werte jsr $aefd ;prüft auf Komma jsr $b79e ;holt Bank nach x stx werte+5 ;ab in Tabelle sei ;kein Interrupt lda 1 ;Prozessorport pha ;merken lda #$35 ;BASIC & Kernal aus sta 1 ldx #$09 ;neun Werte loop1: lda werte,x ;laden und sta $df01,x ;in REC speichern dex bpl loop1 pla ;alte Speicherkonfig. sta 1 ;wiederherstellen cli ;Interrupt freigeben rts ;Programmende Werte: £by 252,1,8,0,0,0,$ff,$c7,0,0 Vorteil dieses Programms gegenüber RAMDOS von der Test/Demo-Disk: Es ist viel kürzer, es ist nicht nur schneller geladen (1 Block) sondern auch schneller in der Ausführung. Und es überlebt einen Reset völlig schadlos. Ein Nachteil ist, daß gepackte Programme denselben Speicherbereich beanspruchen, unser Programm also überschreiben. Aber dagegen ist RAMDOS auch nicht gefeit. Die Verfahrensweise ist folgende: Zuerst dieses Programm absolut (,8,1) laden. Danach die gewünschten Programme laden und nach jedem Programm SYS267,Bank eingeben. Soll das Programm in Bank 3, ist die Eingabe also: SYS267,3 Um ein Programm zurückzuholen genügt ein: SYS270,Bank also zum Beispiel: SYS270,3 Wird ein gepacktes Programm gestartet, so ist unser Programm bei Bedarf erneut zu laden. Die Daten in der REU bleiben davon unberührt! Mit dem was wir gelernt haben verstehen wir auch bereits, was geschieht: Basic & Kernal werden ausgeblendet. (Adresse 1 auf #$35) Dem REC wird mitgeteilt, daß ab $0801, dem Basic-Start, $C7FF Bytes in die gewünschte Bank zu übertragen bzw. von dieser in den C64 zu bringen sind. Die $C7FF Bytes umfassen den Bereich bis $D000. Wozu soll das nur alles gut sein? Denkt doch mal nach. Ihr habt die REU im Port also kein Action Replay, Final Cartridge o.ä. Nun codet ihr ein Basic-Programm und wollt euch ein Directory ansehen. Und ihr habt kein Supra-Dos oder Jiffy-Dos. Schade. Doch die Qualen sind nun vorbei. Mit SYS267,0 das Programm in die REU geschaufelt, Directory geladen und begutachtet, und mit SYS270,0 die Mühen vieler Stunden wieder zurückgeholt. Für Basic-Coder ist dies ideal, ABER erkennt ihr's? Auf diese Art kann man nur 202 Blocks große Programme in die REU bringen. Schalten wir den IO-Bereich auf RAM, können wir dem REC ja keine Mitteilungen mehr machen. Oder doch? Damit beschäftigen wir uns jetzt! 8) Ein tieferes Eindringen Ich erwähnte es bereits: Man kann dem REC noch viel mehr Kommandos geben als offensichtlich ist. Eine Gruppe von Kommandos sind die (ich nenne sie mal so) Total-RAM-Commands. Man erkennt sie unschwer an ihrem gelöschten Bit 4. Ihre Namen sind: 128 STASH (C64->REU) 129 FETCH (REU->C64) 130 SWAP (C64<>REU) 131 VERIFY (C64--REU) Mit diesen Kommandos ist es möglich auch im Bereich von $D000 bis $DFFF das RAM zu lesen und zu schreiben. Aber wie soll das gehen? Nun, durch die obigen Kommandos führt die REU die gewünschte Aktion nicht sofort aus, sondern wartet auf ein bestimmtes Ereignis. Dadurch hat man Gelegenheit den IO-Bereich (nach Übermittlung der Werte an den REC!) auf RAM zu schalten. Und das bestimmte Ereignis ist ein Schreibzugriff auf $FF00. (Ich sage es nochmal: $FF00, nicht $DF00 oder irgend etwas anderes). Der Wert in $FF00 wird dadurch nicht verändert. Der Ablauf in der Übersicht: Aktion Wert $01 ------ -------- a) Parameter an den REC $37,36,35 übermitteln ($DF01-$DF0A) b) spätestens jetzt: SEI c) IO auf RAM schalten $34,30 d) etwas in $FF00 schreiben $34,30 um REU zu starten e) bei Bedarf: ROM an $37 f) bei Bedarf: CLI Das SEI ist natürlich für die REU unwichtig. Blenden wir aber Basic u/o Kernal aus, täte es schon Not. Und an das RAM unter IO kommen wir ohne ausgeblendetes Basic & Kernal nicht heran! So, und nun können wir unser Programm aus dem vorigen Kapitel verbessern: £ba $010b ;Diese Startadresse ist $010b=SYS 267 lda #128 £by $2c ;Diese Startadresse ist $010e=SYS 270 lda #129 gemeinsam: sta werte jsr $aefd ;prüft auf Komma jsr $b79e ;holt Bank nach x stx werte+5 ;ab in Tabelle ldx #$09 ;neun Werte loop1: lda werte,x ;laden und sta $df01,x ;in REC speichern dex bpl loop1 lda 1 ;Prozessorport pha ;merken lda #$30 ;ganzer Computer: RAM sei ;Interrupt sperren sta 1 ;RAM-Konfig. einstellen sta $ff00 ;Transfer starten pla ;alte Speicherkonfig. sta 1 ;wiederherstellen cli ;Interrupt freigeben rts ;Programmende werte: £by 128,1,8,0,0,0,$ff,$f7,0,0 Ich finde, dies ist recht selbsterklärend. Beachtet die Änderungen in der Tabelle (nun $F7FF Bytes statt $C7FF) und die Änderung der Adresse 1, die nun nach der Werteübertragung an den REC stattfindet. Selbstredend haben sich auch die Kommandocodes geändert. Denn diese erst sagen dem REC, daß er auf einen Schreibzugriff bei $FF00 warten soll. 9) Das Eingemachte Wir wissen jetzt bereits eine Menge über die REU, nicht? Wir können schon Daten an die REU übertragen und diese auch wieder zurückholen. Es gibt jedoch noch einige Spezialfunktionen, die selbst den Profis oft unbekannt sind. Und für diese ist die genaue Kenntnis der REC-Register unerläßlich. ADRESSE FUNKTION ======================================= $DF00 Status-Register, nur Lesen -------------------------- Bit 7: 1=noch kein Interrupt Bit 6: 1=Transfer beendet Bit 5: 1=Fehler bei Verify Bit 4: Ramgröße Bit 3-0: Version (1764=0,0,0) DIE BITS 7-5 WERDEN DURCH EINEN LESEZUGRIFF GELÖSCHT. --------------------------------------- $DF01 Kommandoregister ---------------- Bit 7: 1=Aktion beginnen Bit 6: <> Bit 5: 1=Autoload zugelassen Bit 4: 1=Warte auf $FF00 Bit 3: <> Bit 2: <> Bit 1-0: Art der Aktion: 00: STASH C64->REU 01: FETCH REU->C64 10: SWAP C64<>REU 11: VERIFY C64--REU --------------------------------------- $DF02 Adresse im C64 Low-Byte --------------------------------------- $DF03 High-Byte --------------------------------------- $DF04 Adresse in der REU Low-Byte --------------------------------------- $DF05 High-Byte --------------------------------------- $DF06 Bank in der REU --------------------------------------- $DF07 Anzahl der Bytes Low-Byte --------------------------------------- $DF08 High-Byte --------------------------------------- $DF09 Interrupt Mask Register ----------------------- Bit 7: 1=Interrupts erlauben Bit 6: 1=Interrupt nach Ende der Aktion Bit 5: 1=Interrupt bei Verify- Error Bit 4-0: <> --------------------------------------- $DF0A Adress Control Register ----------------------- Bit 7-6: 00=beide Adressen hochzählen 01=REU-Adresse nicht erhöhen 10=C64-Adresse nicht erhöhen 11=beide Adressen n. erhöhen Bit 5-0: <> --------------------------------------- Ich hoffe,ihr habt einen guten Drucker, damit ihr den Überblick nicht verliert. Denn was ihr da überflogen habt ist manchmal schwer verständlich, aber nur bis man es erklärt bekommt. Und anstatt hier eine weitere Tabelle zu schreiben führe ich die neuen Möglichkeiten besser anhand von Beispielen auf. a) Am Ende einer Aktion (STASH, FETCH, (SWAP, VERIFY) ist Bit 6 des Statusregisters gesetzt. Durch Lesen wird dieses (wie auch Bit 7 und 5) wieder gelöscht. Wir kennen das ja schon vom Sprite-Kollisionsregister des VIC. Bit 4 ist bei einer 1764 (AUCH EINER AUFGERÜSTETEN!) als einziges im Urzustand gesetzt. Der Wert ist also 16. Nach einer Aktion ist er dann beim ersten Lesen des Registers 80, danach wieder 16. Bei einer 1700 ist Bit 4 gelöscht. Die Bits 3-0 sollen die Version angeben. Tun sie aber wohl nicht. (1764=0,0,0) b) Wir vergleichen einen Speicherbereich im C64 mit einem in der REU. DAZU LESEN WIR ZUERST DAS STATUSREGISTER $DF00, UM ES ZURÜCKZUSETZEN! Dann setzen wir die Bits 0 und 1 des Kommandoregisters ($DF01) auf 1. Also statt z.B. 252 für STASH schreiben wir 223. Die anderen Register beschreiben wir wie gewohnt. Das Ergebnis erfahren wir in Bit 5 der Adresse $DF00: 0=Speicherinhalt ist gleich. 1=Speicherinhalt ist nicht gleich. Also 10 IFPEEK(57088)AND223 THEN 20 15 PRINT "VERIFY OK":END 20 PRINT "VERIFY ERROR BEI:"; 21 PRINTPEEK((57090)+256*PEEK(57091))-1 Eine weitere wichtige Erkenntnis: Tritt ein Fehler auf, vermindern wir die Adresse in $DF02/03 um 1. So erhalten wir die Computeradresse, an der die REU bei VERIFY einen Unterschied fand. Und klar ist: Dasselbe mit $DF04-06 (wieder minus 1) ergibt die Adresse in der REU. DAS ALLES FUNKTIONIERT NICHT BEI AUTOLOAD! c) Wir AUTOLOADen uns einen. Gemeinhin verlieren unsere Register bei einer Aktion ihren Wert. D.h., wir müssen jedesmal aufs neue alle Register beschreiben, auch wenn sich einige Werte gar nicht geändert haben (z.B. Adresse im Computer ist immer noch der Bildschirmspeicher). Hier hilft uns die AUTOLOAD-Funktion aus der Patsche. Setzen wir Bit 5 im Kommandoregister bleiben die Werte der Register $DF02-08 am Leben, auch nach der gewünschten Aktion. Also: Bei 252 für STASH ist Bit 5 bereits gesetzt. Wollen wir keinen AUTOLOAD, schreiben wir 220. Wie wir wissen, ist bei aktivierter AUTOLOAD-Funktion die Ermittlung der Fehler-Adresse bei VERIFY nicht möglich. d) Schreiben wir ein Kommando ins Kommandoregister ($DF01), so muß Bit 7 dabei gesetzt sein, ansonsten das Kommando nicht ausgeführt wird. Also keine Kommandos kleiner 128! e) Interrupt - steh' ich drauf. Wir können die REU am Ende einer Aktion und/oder bei Eintreten eines Fehlers einen Interrupt generieren lassen. Dazu beschreiben wir unsere Register wie gewohnt. Nur $DF09 bleibt diesmal nicht Null. Wir können schreiben: POKE 57097,192 : Interrupt nach Ende der Aktion POKE 57097,160 : Interrupt NUR bei VERIFY-Error BITTE BEACHTET, DAß $DF00 VOR JEDER AKTION EINMAL AUSGELESEN WERDEN SOLLTE, UM ES ZURÜCKZUSETZEN! Nur so können wir sicher feststellen ob ein Interrupt von der REU generiert wurde. Unsere Interruptroutine ($0314/15) haben wir natürlich bereits verbogen bit $df00 ;Kommt Interrupt von REU? bmi vonreu ;Ja, dann vonreu jmp $ea31 ;sonst Systeminterrupt vonreu: bvs error ;Bit 6=1 Verify Error! ... ;Fehlerlose Aktion ist ;zu Ende error: ... ;Bei VERIFY trat ein ;Fehler auf f) Adress Control, eine fesselnde Versuchung. Das letzte Register bietet noch einmal ein Schmankerl. Normalerweise erhöht der REC beim Datentransfer die Zähler in $DF07/08 (Die Anzahl der zu übertragenden Bytes, die wir ja vorher setzten). Wir können nun folgendes festlegen: belasse Adresse im C64 belasse Adresse in der REU belasse beide Adressen. Der Sinn? Nun, bei gleicher Adresse im C64 können wir die REU löschen, ohne gleich ewig viel RAM im Computer vollzuschreiben. Wir bringen den Wert, der in die REU soll in eine einzige C64-Adresse, setzen die Register darauf, Anzahl der zu übertragenden Bytes auf $FFFF (eine Bank), aktivieren Autoload, fixieren den Byte-Counter in $DF0A (Moment, gleich...) und starten die Übertragung mit dem STASH-Kommando. Nun erhöhen wir den Bank-Zeiger ($DF06) so lange bis die gesamte REU gefüllt ist. Dank AUTOLOAD brauchen wir die übrigen Register nicht neu zu beschreiben. Umgekehrt läßt sich natürlich auch (blitzschnell!) ein beliebiger Bereich im C64 mit einem Wert aus der REU füllen. Und eine feste Adresse nur im C64? Commodore sagt dazu, daß man damit den Inhalt der REU an eine IO-Adresse übergeben kann. Z.B. einen CIA-Port, an dem eine Floppy hängt, oder irgendein Gerät am Userport. Die Anwendungsmöglichkeiten sind groß. Man muß nur ein Gerät finden, daß 1 MB pro Sekunde Datendurchsatz verkraften kann. :) Wir schreiben nach $DF0A: POKE 57098,0 alles bewegt sich POKE 57098,64 REU-Counter steht POKE 57098,128 C64-Counter steht POKE 57098,192 nichts bewegt sich g) Zum Abschluß (Ja, wir sind fertig!) noch eine Tabelle der möglichen Kommandos für $DF01. Bedenkt, daß es durch die drei reservierten, nutzlosen Bits (Bit 6,3,2) noch viel mehr Kombinationen gibt, allerdings mit gleicher Funktionalität. (A+ mit AUTOLOAD, A- ohne AUTOLOAD) (F+ mit $FF00-Verzögerung, F- ohne) KOMMANDO A+F+ A+F- A-F+ A-F- ------------------------------- STASH 236 252 204 220 FETCH 237 253 205 221 SWAP 238 254 206 222 VERIFY 239 255 207 223 h) Und ganz zum Schluß die Adresse, bei der man noch ungebrauchte REUs 1764 bekommen kann (Dies ist keine Werbung und ich verdiene nichts daran): (Hier stand die Adresse. Es war die des GEOS User Clubs. Aber die haben schon lange keine REUs mehr) Angebote (Preise Stand August 1996) REU 1764 256 KB 70,- DM REU 1764 512 KB 120,- DM REU 1764 1 MB 300,- DM REU 1764 2 MB 700,- DM 10) Ade Das war alles und noch mehr über die REU. Die Beispiele aus diesem Artikel sollen zu eigenen Experimenten anregen. Viel Freude dabei wünscht euch Willcox