Der ROM-Port führte bisher leider ein Schattendasein unter den Schnittstellen des ATARI-Computers. Doch an Anwendungsmöglichkeiten fehlt es ihm bestimmt nicht. Dies zeigen wohl auch die zwei bekanntesten Cartridges. Dabei handelt es sich zum einen um einen Macintosh-Emulator, der dem ST Kompatibilität zu diesem Rechner verschafft, und zum anderen um das in der Industrie bewährte Echtzeitbetriebssystem „RTOS-UH“. Auch der „Omikron-BASIC“-Compiler war zumindest zeitweise als Cartridge verfügbar.
Durch den Anschluß am ROM-Port entfällt die für Anwendungsprogramme lästige Ladezeit. Außerdem belegen sie keinen Platz im RAM, was 520ST-Besitzern, aber auch anderen zugute kommt. Heute kann jeder, der eine Möglichkeit hat, sich EPROMS selbst zu brennen, diese Vorteile des ROM-Ports nutzen. Cartridges, in denen Sie die selbst-gebrannten EPROMS nur noch einsetzen müssen, sind mittlerweile schon von vielen Drittanbietem zu günstigen Preisen zu haben.
Es gibt zwei Modultypen, und zwar Diagnose- und Programm-Module. Der erste Typ findet normalerweise nur bei Gerätetests des Reparaturservices Anwendung. Sie werden direkt nach einem Reset gestartet. Zu diesem Zeitpunkt hat der ST noch gar nichts initialisiert (noch nicht einmal die Speicherkonfiguration). Man ist somit gezwungen, dies selbst durchzuführen. Dadurch läßt sich jeder Teil des Systems prüfen und auf Funktionstüchtigkeit checken. Das Betriebssystem erkennt solche Module an der magischen Zahl $FA52235F in der Speicherzelle $FA0000 (1. Langwort des Speicherbereichs für das Modul).
Programm-Module sind Cartridges, die nach einer teilweisen oder vollständigen Initialisierung des Systems gestartet werden und die magische Zahl $ABCDEF42 besitzen. Beide Arten von Modulen sind nicht nur auf ein Programm beschränkt, sondern können auch mehrere beinhalten. Unterstützung durch das Betriebssystem Das Betriebssystem ist schon darauf vorbereitet, Cartridges am ROM-Port zu empfangen, und stellt dafür den Speicherbereich von $FA0000 bis $FBFFFF zur Verfügung. Die Schnittstelle läßt sich als eigenes Laufwerk verwalten und ist im Desktop als Laufwerk ,c:‘ anzumelden. Die Programme lassen sich daraus wie gewöhnlich starten.
Vorsicht für eigene Experimente! Am ROM-Port stehen die Leitungen für den Daten- und Adreßbus ungepuffert zur Verfügung! Es sind folgende Signale an der Schnittstelle anzutreffen:
+5V Betriebsspannung für das Modul
GND Masseleitung des Moduls
A1-A15 die unteren 15 Adreßleitungen des 68000ers
D0-D15 die 16 Datenleitungen der CPU
LDS (Low-Aktiv) Low an LDS zeigt an, daß das niederwertige Byte des Datenbusses gültig ist.
UDS (Low-Aktiv) Low an UDS zeigt an, daß das höherwertige Byte des Datenbusses gültig ist.
Sind LDS und UDS Low, handelt es sich um einen Wortzugriff.
ROM3 (Low-Aktiv) wird Low, wenn die oberen 64 KB des Speichers angesprochen werden ($FB0000-$FBFFFF).
ROM4 (Low_Aktiv) wird Low, wenn die unteren 64 KB des Speichers angesprochen werden ($FA0000-$FAFFFF).
Die Pin-Belegung ist in Bild 1 angegeben. Mit einem gewöhnlichen Aufbau des Cartridges lassen sich dem Rechner also 128 KB zusätzliches ROM verpassen. Doch einigen Tüftlern reichte dies nicht aus, sie entwickelten deshalb Platinen, mit denen mittels Bank-Switching-Technik mehr als 128 KB, z. B. 512 KB oder 1 MB, angesprochen werden können. Unter Bank-Switching versteht man eine Vorgehensweise, bei der ein großer Speicher angelegt wird, der Computer aber nur ein „Fenster“ davon zu sehen bekommt. Dazu kann man die ROMs beispielsweise in 64-KB-Blöcke aufteilen, eine Decodierschaltung sorgt dann dafür, daß der Rechner stets auf den jeweils gewünschten Block zugreift. Für Hardware-Freaks sei dabei auf Bild 2 verwiesen, in dem das Prinzip einer erweiterten EPROM-Bank dargestellt ist. Mit Hilfe des Latches und den Adreßleitungen A1, A2, A15 wird der jeweils angesprochene Speicherblock selektiert und mit einem Decoder der CS-Eingang des ausgewählten Blockes aktiviert (Low-Aktiv). Das Betriebssystem ist natürlich auf eine solche Konfiguration nicht vorbereitet, weshalb ein neuer Treiber für die EPROM-Bank geschrieben werden muß. Hierbei verweisen wir vor allem auf [2].
Etwas genauer werden wir jetzt auf die 128-KB-Karte eingehen, da diese für jeden Elektronik-Freak zu verwirklichen ist. Sehen Sie sich dazu bitte Bild 3 an. Für die Karte werden vier ,27256‘-EPROMs zu je 32 KB verwendet. Es läßt sich erkennen, daß sämtliche Daten- und Adreßleitungen mit den entsprechenden Anschlüssen an den EPROMs zu verbinden sind. Ebenso die Versorgungsspannung, die mit einem Kondensator noch zusätzlich etwas stabilisiert wird. IC1 und IC2 werden für die unteren und IC3 und IC4 für die oberen 64 KB benutzt. Aus diesem Grund ist die CS-Leitung von IC 1 und 2 mit ROM4 und die von IC3 und 4 mit ROM3 verbunden. Jetzt fehlen nur noch die LDS- und UDS-Anschlüsse. LDS legen wir an OE von IC1 und 3, UDS an OE von IC2 und 4, was bedeutet, daß in IC1 und 3 alle geraden und in IC2 und 4 alle ungeraden Adressen liegen. Bitte berücksichtigen Sie das beim Brennen der EPROMs. Damit ist die Cartridge anschlußfertig.
Noch ein Hinweis: Leider ist es nicht möglich, in eine Cartridge zu schreiben, da der GLUE-Chip für diesen Adreßbereich einen Bus-Error liefert.
Damit im EPROM kein Chaos entsteht, existiert zu jedem Programm ein Vorspann, der Cartridge-Application-Header. Der Aufbau ist in Bild 4 ersichtlich. Die Inhalte der Komponenten des Vorspanns haben folgende Bedeutung:
CA_NEXT
Sollten sich mehrere Programme auf dem Modul befinden, ist hier ein Zeiger auf den nächsten Programmvorspann vorzufinden, im letzten Header der Wert 0.
CA_INIT
In diesem Long-Wert steht in den untersten 3 Bytes eine Adresse, die das Betriebssystem aufruft, um das entsprechende Programm zu initialisieren. Die verbleibenden obersten 8 Bits geben an, wann dies geschieht.
Bit 0: Aufruf von Adresse in CA_INIT nach der Initialisierung der Hardware; die Bildschirmauflösung ist festgestellt, Betriebssystemvariablen und Interrupt-Vektoren sind gesetzt, lediglich noch nicht freigegeben (Interrupt-Priority-Level: 7).
Bit 1: wie bei Bit 0, jedoch sind die Interrupts freigegeben (Interrupt-Priority-Level: 3); vorder GEMDOS-Initialisierung
Bit 2: wie bei Bit 0, jedoch ist die Bildschirmauflösung noch nicht festgestellt
Bit 3: Initialisierung direkt vor Disk- bzw. DMA-Boot-Routine
Die folgenden 3 Bits geben den Programmtyp an:
Bit 5: Accessory
Bit 6: TOS-Programm
Bit 7: TTP-Programm
CA_RUN
Adresse, die beim Programmstart, z.B. aus dem Desktop, angesprungen wird.
CA_TIME
Uhrzeit im GEMDOS-Format (HHHHHMMMMMMSSSSS)
CA_DATE
Datum im GEMDOS-Format (YYYYYYYMMMMDDDDD)
CA_SIZE
Länge des Programms
CA_NAME
Programmname als C-String (Abschluß durch $0-Byte) Format wie gewöhnlich: FILENAME.EXT
Wie man sieht, haben die Application-Header durch CA_NAME keine konstante Länge. Deshalb kann die Anfangsadresse eines Headers nicht berechnet werden, sondern der Programmierer muß sich durch die CA_NEXT-Zeiger hangeln. Wollen Sie Programme für eine Cartridge schreiben, müssen Sie darauf achten, daß sie später auch im Adreßbereich ab $FA0000 laufen. Wie das geschieht, sehen wir noch.
Mit dem abgedruckten Listing haben Sie die Möglichkeit, Ihre für die EPROMs geschriebenen Programme auf Lauffähigkeit im ROM-Modul zu testen. Dazu muß man eigentlich nur die Routine des Betriebssystems simulieren, welche testet, ob sich eine Cartridge im ROM-Port befindet oder nicht. Im Startup-Code wird sie Routine mehrmals aufgerufen, so daß es kein Problem darstellte, diese zu disassemblieren (siehe Listing 1).
Sie überprüft zunächst das Vorhandensein der magischen Zahl $ABCDEF42 in der Speicherzelle $FA0000. Danach erfolgt ein Bit-Test, mit dem die Routine feststellt, ob das zum aktuellen Header gehörende Programm das gewünschte Init-Bit (Bits 24-31 in CA_INIT) gesetzt hat. Wenn nicht, wird, falls es überhaupt noch einen gibt, der nächste Programm-Header untersucht. Ist das entsprechende Bit jedoch gesetzt, kann man die Adresse des Init-Teils holen und anspringen.
Wie Sie vielleicht gemerkt haben, ist der einzige ROM-Modul-spezifische Befehl LEA $FA0000,A0. Das heißt, wenn wir die Programme des Moduls im RAM unterbringen, müssen wir nur $F A0000 durch unsere RAM-Adresse ersetzen, und schon arbeitet die Routine nach unseren Wünschen. Dies haben wir in Listing 2 verwirklicht.
Das Programm fragt zunächst nach dem Init-Bit, welches gesetzt sein soll. Es sind nur Eingaben von 0-3 sinnvoll, da sich lediglich die Bits 0-3 auf die Initialisierung beziehen, die Bits 4-7 geben ja den Programmtyp an. Nun folgt der Aufruf der schon oben besprochenen Routine. In ihr werden alle Header auf das entsprechende Bit hin durchsucht und gegebenenfalls das Init-Programm aufgerufen. Danach beginnt der Teil des Listings, den Sie später ins EPROM brennen. Wie wir anfangs erwähnten, muß er dann im Bereich $FA0000 laufen. Dies erreichen wir durch den Befehl ORG $FA0000, der dafür sorgt, daß der Modulteil bei dieser Adresse beginnt. Bevor Sie jedoch das EPROM brennen, müssen Sie noch alles löschen, was zum Programmteil oberhalb des ORG-Befehles gehört. Natürlich ist auch das Kommentarzeichen der ORG-Zeile zu entfernen.
Zum Schluß noch eine Anregung. Der ROM-Port eignet sich ja nicht nur für ROM-Module, sondern auch für viele andere Erweiterungen, z.B. A/D-Wandler usw., denn an ihm liegen viele Signale der CPU an. Aber Achtung! Die meisten sind ungepuffert.
Christian Roth/Matthias Brust
Literatur:
[1] ATARI Profibuch, Jankowski/Reschke/ Rabich, Sybex Verlag
[2] Scheibenkleister II - Massenspeicher am ST, Claus Brod/Anton Stepper, MAXON Computer
OFFSET | BEDEUTUNG |
---|---|
+$00 | Zeiger auf nächsten Header |
+$04 | Zeiger auf PGM-Init-Routine |
+$08 | Zeiger auf PGM-Beginn |
+$0C | GEMDOS-Zeit |
+$0E | GEMDOS-Datum |
+$10 | Programmlänge |
+$14 | Programnnane |
Bild 4: Struktur der Cartridge-Application-Header
ROM_TEST LEA $FA0000,A0 ;Hole 1.Langwort aus EPROM
CMP.L #$ABCDEF42,(A0)+ ;Magic Zahl?
BNE.S QUIT ;sonst Tschüß
TEST_INIT BTST D0,4(A0) ;Init-Bit testen
BEQ.S NO_INIT ;nicht gesetzt?
MOVEM.L D0-A6,-(A7) ;Register retten
MOVE.L 4(A0), A0 ;Startadresse des
;PRG's holen
JSR (A0) ;und anspringen
MOVEM.L (A7)+,D0-A6 ;Register holen
NO_INIT TST.L (A0) ;noch ein PGM
;vorhanden?
MOVE.L (A0),A0 ;ja annehmen
BNE.S TEST_INIT ;wenn wirklich ja
QUIT RTS ;sonst Tschüß
; ***************************
; * Cartridge-Test-Programm *
; * geschrieben von *
; * Christian Roth u. *
; * Matthias Brust *
; * Assembler: Profimat *
; * (c) 20.6.92 by MAXON *
; ***************************
loop: move.l #ask,d0 ;Eingabestring
jsr print ;printen
move.w #1,-(sp) ;Auf Taste
trap #1 ;warten
addq.l #2,sp
cmp.w #$1B,d0 ;bei ESC
beq.s tschau ;Tschüss...
sub.l #'0',d0 ;ASCII->Zahl
and.l #7,d0 ;nur 3 Bits
jsr test ;Programme testen
bra loop ;wieder von vorn
tschau: clr.w -(sp) ;Auf
trap #1 ;Wiedersehen
print: move.l d0,-(sp) ;Stringadresse
move.w #9,-(sp) ;holen und
trap #1 ;String
addq.l #6,sp ;printen
rts ;zurück
; Hier folgt das eigentliche Cartridge-
; Test-Programm, es entspricht weit-
; gehend dem TOS-Test-Programm
test: lea base,a0 ;magische Zahl
cmp.l #$ABCDEF42,(a0)+ ;vergleichen
bne.s back ;Tschüss...
initbit: btst d0,4(a0) ;Init-Bit gesetzt?
beq.s no_init ;sonst => no_init
movem.l d0-a6,-(sp) ;Register retten
move.l 4(a0),a0 ;PGM-Adr. holen
jsr (a0) ;PGM initialisieren
movem.l (sp)+,d0-a6 ;Regs zurückholen
no_init: tst.l (a0) ;gibts noch ein PGM
move.l (a0),a0 ;Adr. eintragen
bne initbit ;das Spiel wiederholen
back: rts ;sonst Tschüss...
; Wenn das Programm getestet wurde und einwand-
; frei funktioniert, müssen Sie alles, was nicht
; zum unteren Programmteil gehört löschen und bei
; folgender Zeile das Kommentarzeichen ent-
; fernen, bevor Sie das PGM brennen.
; org $FA0000
base: dc.l $ABCDEF42 ;magische Zahl
head_1: dc.l head_2 ;Zeiger auf 2. Header
dc.l init_1+$8000000 ;Zeiger auf Init +
;Bit 27 setzen
dc.l begin_1 ;Startadresse PGM
dc.w 0 ;Zeit
dc.w 0 ;Datum
dc.l end_1-begin_1 ;Länge des PGM
dc.b 'ZUM_BSP1.PRG',0 ;Name des PGM
align.l ;auf gerade
;Adresse bringen
head_2: dc.l head_3 ;s.o
dc.l init_2+$4000000 ;Bit 26 setzen
dc.l begin_2 ;...
dc.w 0
dc.w 0
dc.l end_2-begin_2
dc.b 'ZUM_BSP2.PRG',0
align.l
head_3: dc.l 0 ;kein Header mehr
dc.l init_3+$8000000 ;Bit 27 setzen +
dc.l begin_3 ;gleiches Init-PGM
dc.w 0 ;wie bei PGM 1
dc.w 0 ;...
dc.l end_3-begin_3
dc.b 'ZUM_BSP3.PRG',0
align.l
init_1: move.l #init_txt,d0 ;Init-Text
jsr print ;printen
move.l #head_1+20,d0 ;PGM-Name
jsr print ;printen
rts
init_2: move.l #init_txt,d0 ;...
jsr print
move.l #head_2+20,d0
jsr print
rts
init_3: move.l #init_txt,d0
jsr print
move.l #head_3+20,d0
jsr print
rts
begin_1: rts ;Hier steht das richtige PGM
end_1:
begin_2: rts
end_2:
begin_3: rts
end_3:
ask: dc.b 13,10
dc.b 'Welches Init-Bit soll gesetzt sein? (0-7, ESC=Exit) ',0
init_txt: dc.b 13,10,'Initialisiere Programm',0
end