← ST-Computer 07 / 1988

Schwarz-Weiß-Emulator

Programmierpraxis

Mit diesem Programm läßt sich der Monochrom-Modus des ATARI ST auf einem Farbmonitor darstellen. Eine Vielzahl von Programmen (leider nicht alle) laufen mit diesem Schwarzweiß-Emulator. Natürlich erreicht man nicht die Qualität eines SM124, doch das Programm zeigt doch eine interessante Möglichkeit, wie man den ST manipulieren kann. Es ist allerdings notwendig, einige direkte Sprungadressen zu benutzen, so daß man einige Zeilen abändern muß, damit es auch auf dem Blitter-TOS läuft. Die Änderungen werden am Ende des Artikels angegeben.

Das Prinzip

Zuerst wird die TOS-Variable phystop ($42E) heruntergesetzt und dadurch Raum für einen zweiten Bildschirmspeicher und einige Routinen geschaffen, die nach einem Reset erhalten bleiben. Der Wert von phystop ändert sich bei einem Warmstart nicht.

Hinter phystop werden folgende Routinen abgelegt:

Eine veränderte vbl-Interrupt-Routine, bei der erstens der Monitor-Detect abgeschaltet ist (normalerweise führt der Computer ein Reset aus, wenn er merkt, daß im Schwarzweiß-Modus ein Farbmonitor angeschlossen ist oder andersherum) und die zweitens dafür sorgt, daß der Schwarzweiß-Bildschirmspeicher in den 2. Bildschirmspeicher hinter phystop übertragen und dabei auch gleich an die verschiedenen Modi angepaßt wird. Aus zeitkritischen Gründen wird dabei bei jedem vbl-Interrupt jeweils nur ein Viertel des Bildschirms übertragen, sonst würde der Computer extrem langsam werden.

Außerdem wird geprüft, ob sich vielleicht das Invers-Bit geändert hat. Die Farben paßt man dann entsprechend daran an. Auch eine Veränderung der Adresse des Schwarzweiß-Bildschirmspeichers wird erkannt und sofort gespeichert. Bei aktiven Floppies wird auch bei eingeschaltetem Emulator der Bildschirmspeicher nicht übertragen, da es sonst zu Fehlern kommen kann.

Eine veränderte dumpvec-Routine

Über dump_vec springt der Computer jedesmal, wenn ALT/HELP gedrückt wird. Normalerweise erzeugt man damit eine Hardcopy auf dem Drucker. Hier dient dieser Vektor dazu, den Emulator ein- und auszuschalten (MIT Shift-Taste), bzw. zwischen den verschiedenen Modi hin- und herzuschalten (OHNE Shift-Taste). Beim Ausschalten des Emulator setzt diese Routine wieder die originale Bildschirmadresse.

Ein veränderter Xbios-Aufruf

Folgende Xbios-Routinen werden verändert:

Xbios(4)=Getrez liefert immer den Wert 2 = High-Res zurück.
Xbios(2)=Physbase liefert bei eingeschaltetem Emulator die Adresse des Schwarzweiß-Bildschirmspeichers zurück.
Xbios(5)=Setscreen verändert bei eingeschaltetem Emulator nicht die wirkliche Bildschirmadresse (ab der der Videoprozessor abbildet), sondern die Adresse des Bildschirmspeichers, aus dem übertragen wird. Ein Ändern der Auflösung mit dieser Routine wird unterdrückt. Sonst verhält sich diese Routine wie gewöhnlich.

Bei allen anderen Xbios-Aufrufen werden die Original-Routinen angesprungen.

Eine geänderte Keyboard-Interrupt-Routine

Diese sorgt nur dafür, daß während der Abarbeitung eines Keyboard-Interrupts der vbl-Interrupt eingeschaltet ist, da es sonst zu unschönen Erscheinungen kommen kann.

Außerdem werden hinter phystop noch einige Variablen, die der Emulator benötigt, gespeichert:

In alt_adr speichert er die Adresse des Schwarzweiß-Bildschirmspeichers, aus dem bei eingeschaltetem Emulator übertragen wird.

In neu_adr speichert er die Adresse des 2. Bildschirmspeichers hinter phystop, auf den bei eingeschaltetem Emulator der Videoprozessor gesetzt ist. Diese Adresse wird nur am Anfang gemerkt und dann nicht mehr verändert.

In aufl merkt er sich, ob er ein- oder ausgeschaltet ist. 1=An, 2=Aus.

In modus merkt er sich, in welchem der 4 Modi er sich befindet:

modus=0 : Jede 2. Zeile wird abgebildet (alle ungeraden Zeilen).
modus=1 : Jede 2. Zeile wird abgebildet (alle geraden Zeilen).
modus=2 : Der obere Teil des Bildschirms wird abgebildet.
modus=3 : Der untere Teil des Bildschirms wird abgebildet.

Nachdem das Programm die obigen 4 Routinen und die Variablen hinter phystop abgelegt hat, wird eine Routine erzeugt, die nach einem Reset automatisch aufgerufen wird. Wer Genaueres wissen will, sollte sich das ROM ab $fc0d20 ansehen. Eine Reset-Routine muß unterhalb von phystop an einer Adresse stehen, die ein Vielfaches von 512 ist. Das erste Langwort muß das “Magic” $12123456 sein, das zweite Langwort die Adresse des Magics enthalten und die Prüfsumme über 256 Worte = $5678 sein. Die Routine wird nach dem Laden des Boot-Sektors, aber noch vor dem Ausführen des Auto-Ordners, ausgeführt. In dieser Routine werden nur die Adressen der vier veränderten Aufrufe und die Auflösung = High gesetzt und dann die Bildschirmausgabe neu initialisiert.

Nach dem Laden des Programms prüft dieses zuerst, ob sich der Computer im Schwarzweiß-Modus befindet. Wenn ja, wird das Programm sofort wieder verlassen und nichts geschieht. Im anderen Fall wird die Kurzanleitung ausgegeben und dann die Routine “supexec” im Supervisor-Modus angesprungen. Diese macht nun nichts anderes, als phystop herunterzusetzen, die verschiedenen Routinen zu verschieben und die Reset-Routine zu erzeugen. Danach wird in einer Endlos-Schleife solange gewartet, bis der Resetknopf gedrückt wird.

Änderungen für Blitter-TOS

Für all diejenigen, die mit dem Blitter-TOS arbeiten, müssen folgende Zeilen im Listing abgeändert werden:

Zeile 88 in: btst #1,$e61
Zeile 180 in: jmp $fc0748
Zeile 182 in: jmp $fc071a
Zeile 214 in: jmp $fc07f2
Zeile 235 in: jmp $fc29ce
Zeile 252 in: jmp $fca96e

; Schwarzweiß Emulator ; ; (C) Volker Ullrich 1987 ; Postfach 1646 ; 7770 Überlingen ; ; bitte relozierbar anklicken phystop equ $42e swv_vec equ $46e dump_vec equ $502 sshiftmod equ $44c move #4,-(a7) trap #14 ; xbios 4, Getrez cmpi #2,d0 ; Schon Highres? beq.s term ; ja = term pea message move #9,-(a7) trap #1 ; Gemdos 9. Print Line pea supexec move.w #38,-(a7) trap #14 ; xbios 38. Supexec term: clr (a7) trap #1 ; gemdos 0, Term supexec: ; Hier nachfolgende Routinen verschieben, Prüfsumme ; erzeugen, phystop anpassen etc. move.l phystop,a0 suba.l #$8000,a0 ; a0 = Adresse 2. Bildschirmbereich move.l a0,neu_adr suba.l #$200,a0 ; a0 = Adresse neues phystop move.l a0,phystop move.l a0,al ; phystop retten suba.l #$8200,a1 ; a1 = Adresse Reset-Routine lea -4(a1),a7 ; Stack heruntersetzen move.l #vbl_sprung,a2 move #255,d0 ; Länge change_aufl + ubl2 : ca. 256 Worte verschieben: move.w (a2)+,(a0)+ ; change_aufl hinter phystop kopieren dbra d0,verschieben move.l al,a0 ; a0 = Anfang Reset-Routine move #127,d0 ; 128 Langworte loeschen: clr.l (a0)+ ; Platz für Reset-Routine loschen dbra d0,loeschen move.l a1,a0 ; a0 = Anfang Reset-Routine move.l #reset_resident,a2 move #39,d0 ; Länge: ca. 40 Worte verschieben2: move.w (a2)+,(a0)+ ; Reset-Routine verschieben dbra d0,verschieben2 move.l a1,4(a1) ; Adresse der Routine speichern move #255,d0 ; 256 Worte für Prüfsumme clr d1 ; Prüfsumme löschen pruefsumme: add (a1)+ ,d1 ; Prüfsumme erzeugen dbra d0,pruefsumme move #$5678,d0 sub d1,d0 move d0,-(a1) ; Prüfsumme korrigieren endlos: bra.s endlos ; Auf Reset warten ; Die folgenden Routinen werden hinter phystop gespeichert: vbl_sprung: bra vbl2 ; geänderter vbl Interrupt Aufruf bra xbios2 ; geänderter xbios Aufruf bra keyb_int ; geänderter mfp 68901 Level 6-Interrupt bra.s change_aufl ; geänderter dump_vec alt_adr: dc.l 0 neu_adr: dc.l 0 aufl: dc.b 2 ; default: High=Emulator aus modus: dc.b 0 ; Modus : Jede 2. Zeile a) change_aufl: ; Aufruf dieser Routine mit Alt/Help lea alt_adr(pc),a0 ; Anfang Uariablen in a0 btst #1,$elb ; linke Shifttaste? bne.s shift ; ja addq.b #1,9(a0) ; wenn nicht gedrückt: modus erhöhen andi.b #3,9(a0) ; falls 4 erreicht, wieder modus=0 rts shift: eori.b #3.8(a0) ; Hier Auflösung Umschalten cmpi.b #l,8(a0) ; Neue Auflösung = Mid ? bne.s aufl_high ; Nein moveq.l #0,d0 ; Hier wenn neue Auflösung Mid move.b $ff8201,d0 lsl.w #8,d0 move.b $ff8203,d0 lsl.l #8,d0 ; physikalische Uideoadr. in d0 move.l d0,(a0) ; in alt_adr merken move.l 4(a0),-(a7) ; Neue Videoadresse auf Stack video_setzen: move.b 1(a7),$ff8201; und setzen move.b 2(a7),$ff8203 clr.l (a7)+ ; Stack löschen rts aufl_high: move.l (a0),-(a7) ; Alte Videoadresse auf Stack bra.s video_setzen ; und setzen vbl2: ; veränderte vbl-Routine ohne Monitor-Detect movem.l d0-d7/a0-a6,-(a7) ; Register retten lea alt_adr(pc),a0 ; Anfang Uariablen in a0 move.b 8(a0),d0 ; Auflösung in dB move.b d0,$ff8260 ; und setzen cmpi.b #1,d0 ; Mid Auflösung ? bne nix ; Nein : nichts verschieben move.l #$ffff,d0 ; Hier wenn Auflösung Mid btst #0,$ff8241 ; Bit Null Farbpalettenregister 0 gesetzt ? bne.s invers ; ja swap d0 ; sonst : Maske vertauschen invers: move d0,$ff8240 ; schwarze und weiße Farbe setzen swap d0 move d0,$ff8246 moveq.l #0,d0 ; aktuelle Videoadresse ausrechnen move.b $ff8201, d0 lsl.w #8,d0 move.b $ff8203,d0 lsl.l #8,d0 cmp.l 4(a0),d0 ; muß bei Mid = neu_adr sein beq.s gleich move.l d0,(a0) ; sonst: alt_adr neu setzen move.l 4(a0),-(a7) ; neu_adr auf Stack move.b 1(a7),$ff8201 ; und setzen move.b 2(a7),$ff8203 clr.l (a7)+ ; Stack löschen gleich: tst.w $43e ; flock, Floppies aktiu ? bne.s nix ; Ja : nichts verschieben move.w $468,d0 ; vbl-Zähler andi.w #3,d0 mulu #8000,d0 ; Jeden vbl ein Viertel übertragen move.l 4(a0),a1 ; Neue Videoadresse in a1 move.l (a0),a2 ; Alte Videoadresse in a2 move.b 9(a0),d1 ; Modus in d1 bne.s modus1 lea 80(a2),a2 ; 2. Zeilensatz modus1: cmpi.b #3,d1 bne.s modus2 adda.l #16000,a2 ; untere Bildschirmhälfte modus2: adda.l d0,al moveq.l #80,d2 ; Jede 2. Zeile uerschlucken btst #1,d1 beq.s modus3 clr.l d2 ; Modus 2,3: Jede 2. Zeile nicht verschlucken asr.l #1,d0 ; d0/2 modus3: adda.l d0,a2 move #49,d1 ; 50 Zeilen schleife1: move #39,d0 ; 80 Bytes je Zeile schleife2: move (a2),(a1)+ move (a2)+,(a1)+ dbra d0,schleife2 adda.l d2,a2 ; Jede 2. Zeile verschlucken dbra d1,schleife1 nix: addq.l #1,$466 ; wie Original subq.w #1,$452 ; wie Original bmi.s gesperrt ; wie Original addq.l #1,$462 ; sub.l a5,a5 ; jmp $fc069e ; und Sprung in Original Routine (aber hinter der kritischen Stelle) gesperrt: jmp $fc071a ; vbl gesperrt: Sprung an Ende der Routine ; Obige zwei Sprungadressen gelten nur für ROM-TOS !! xbios2: lea 6(a7),a0 ; Adresse Funktionsnummer in a0 btst #5,(a7) ; Aufruf aus Supervisor ? bne.s supervisor ; ja move.l usp,a0 ; Adresse Funktionsnummer in a0 supervisor: lea alt_adr(pc),a1 ;Adresse Variablen in a1 cmpi #4,(a0) ;=4? (Getrez) bne.s weiter ; nein moveq.l #2,d0 ; Getrez = 2 rte weiter: cmpi #2, (a0) ; = 2? (Physbase) bne.s weiter2 ; nein move.l alt_adr(pc),d0 ; Physbase = alt_adr cmpi.b #1,8(a1) ; Emulator an? beq.s physb_aus ; Ja moveq.l #0,d0 ; sonst: aktuelle Videoadresse ausrechnen move.b $ff8201,d0 lsl.w #8,d0 move.b $ff8203,d0 lsl.l #8,d0 physb_aus: rte weiter2: cmpi #5,(a0) ; = 5 ? (Setscreen) beq.s setscreen jmp $fc0748 ; Original xbios, nur für R0M-T05 setscreen: tst.l 2(a0) bmi.s setscreen2 move.l 2(a0),$44e ; _v_bs_ad setzen setscreen2: tst.l 6(a0) bmi.s setscr_aus move.l 6(a0),(a1) ; physbase in alt_adr speichern cmpi.b #1,8(a1) ; Emulator an ? beq.s setscr_aus ; Ja move.l 6(a0),-(a7) ; Sonst: auch Videoadresse setzen move.b 1(a7),$ff8201 move.b 2(a7),$ff8203 clr.l (a7)+ ; Stack löschen setscr_aus: ; Auflösung ändern wird unterdrückt rte keyb_int: andi.b 8$fb,(a7) ; Interruptmaske 0-3, vbl enable jmp $fc281c ; Sprung in Original Routine ; obige Adresse gilt nur für ROM-TOS ! reset_resident: ; Wird an 512 Grenze kopiert und nach Reset ausgeführt de.1 $12123456 ; Magic dc.l 0 ; Hier kommt Adresse des Magic hinein move.l phystop,a0 move.l a0,$70 ; Level 4 Interrupt : Auf vbl2 lea 4(a0),a0 ; a0 += 4 move.l a0,$b8 ; Trap 814 : Auf xbios2 lea 4(a0),a0 move.l a0,$118 ; mfp 68901, Level 6 auf keyb_int lea 4(a0),a0 move.l a0,$502 ; dumpvec auf change_aufl move.b 82,sshiftmod ; Auflösung setzen jmp $fca7c4 ; Bildschirmausgabe initialisieren + rts ; Adresse gilt nur für ROM-TOS ! data message: dc.b 27,"u",27,"E" ; Überlauf ein & Cls dc.b "Schwarz-Weiß Emulator ® 1987 Volker Ullrich", 13,10 dc.b "Dieses Programm simuliert auf einem Farbmonitor einen Schwarzweiß Monitor.", 13,10,13,10 dc.b "Bedienung:", 13,10,13,10 dc.b "Shift/Alternate/Help —> Emulator An/Aus",13,10 dc.b "Alternate/Help —> Modus umschalten",13,10 dc.b "Da ein Farbmonitor nur die Hälfte der Zeilenanzahl eines Schwarz-Weiß Monitors",13,10 dc.b "darstellen kann (640x206 anstatt 640x400 Punkte), wird mit Alt/Help zwischen",13,10 dc.b "4 Möglichkeiten umgeschaltet:",13,10 dc.b "1.) Nur jede 2. Zeile wird abgebildet (1. , 3., 5., 7. usw.)",13,10 dc.b "2.) Wie oben, jedoch 2.. 4., 6. usw. Zeile",13,10 dc.b "3.) Nur die obere hälfte des Bildschirms wird abgebildet",13,10 dc.b "4.) Nur die untere Hälfte des Bildschirms wird abgebildet",13,10,13,10 dc.b "Funktioniert nur mit ROM-TOS ! ", 13,10 dc.b "Kann auch in den Auto-Ordner kopiert werden.”,13,10,13,10 dc.b "Jetzt bitte eine Diskette einlegen und den Resetknopf drücken. Der Computer geht" dc.b "dann automatisch nach kurzer Zeit in den Hochauflösenden Modus. Nach Beendigung".13,10 dc.b "des Ladevorgangs Shift/Alt/Help drücken, um den Emulator einzuschalten.",13,10,13,10,0
Volker Ullrich