Wenn Sie sich einige Demos genauer ansehen, werden Sie sicherlich schon bemerkt haben, daß sie mit einer höheren Grafikauflösung arbeiten, als normalerweise möglich ist. Mit Screen plus können Sie auch unter GEM die Auflösung erhöhen. Diese Lösung ist kein Ersatz für Hardware-Erweiterungen (z.B. AutoSwitch-OVERSCAN) oder Grafikkarten, aber bietet immerhin 45 zusätzliche Pixel-Zeilen und kostet (fast) nichts.
Wie geht das? Wenn das Videosignal mit Hilfe eines Standard-Controllers (u.a. möglich. Bei diesen Video-Controllern sind nämlich fast alle Parameter frei programmierbar wie z.B. Anzahl der Pixel jeZeile oder Anzahl der Pixel-Zeilen. Im Atari ST kommen dagegen spezielle Custom-ICs zum Einsatz. Diese sind nur für die 3 möglichen Betriebsarten (320200 / 16 Farben; 640200 / 4 Farben; 640*400 s/w) des Videosystems ausgelegt. Andere Betriebsarten sind ohne zusätzliche Hardware (normalerweise) nicht möglich.
Wie Sie schon ahnen, gibt es doch eine Möglichkeit, eine andere Auflösung als die 3 gängigen zu realisieren. Ansatzpunkt hierfür ist die Timing-Logik für das Videosignal. Diese Logik befindet sich im GLUE und läßt sich über 2 Register steuern (Shift-Mode- und Sync-Mode-Register). Wenn man in diesen Registern zum richtigen Zeitpunkt bestimmte Bits kippelt, wird die Timing-Logik so ‘durcheinander’ gebracht, daß der ST ein Bild mit einer höheren Auflösung erzeugt. Das Erhöhen der Anzahl der Pixel je Zeile ist aber sehr aufwendig, da innerhalb jeder Pixel-Zeile mehrmals, zu genau definierten Zeitpunkten, auf den GLUE zugegriffen werden muß. Das gesamte Programm muß dann unter Echtzeitbedingungen ablaufen, und es dürfen keine Interrupts auftreten. Außerdem wird bei dieser Aktion die CPU zeitlich so stark belastet, daß das Hauptprogramm wesentlich langsamer wird.
Einfacher dagegen ist die Erhöhung der Anzahl der Pixel-Zeilen. Hier muß nur, nachdem die ‘normalen’ 200 Pixel-Zeilen geschrieben sind, das Vertikalfrequenz-Bit im Sync-Mode-Register gekippelt werden. Dadurch wird die Dunkeltastung für die nächsten Pixel-Zeilen aufgehoben. Die weiteren Einzelheiten werden zusammen mit dem Programm erläutert.
Im ST gibt es ein Display-Enable Signal, welches immer dann high wird, wenn eine Pixel-Zeile geschrieben wird. Dieses Signal liegt auch am TBI-Eingang des Timers B an. Wenn der Timer B auf die Betriebsart ‘Zähler / H-L-Flanke’ programmiert wird, kann man einen Interrupt nach dem Schreiben einer beliebigen Pixel-Zeile auslösen. Da die Interrupt-Reaktionszeit der CPU nicht immer konstant ist, wird schon nach der 190. Zeile ein Interrupt ausgelöst. In der Interrupt-Routine werden alle Interrupts gesperrt. Das Erreichen der 200. Pixel-Zeile wird durch ständiges Abfragen von Timer B festgestellt. Dann wird kurzzeitig die Vertikalfrequenz auf 60 Hz geschaltet. Danach wird Timer B bis zum nächsten VBIank angehalten, und die Interrupts werden wiederfreigegeben. Die VBIank-Routine tut weiter nichts, als den Timer B für das nächste Bild vorzubereiten (Betriebsart: Zähler, Interrupt bei der 190. Zeile) (siehe Bild). Störungen des Bildes während des Zugriffs auf Disk oder Festplatte lassen sich leider nicht vermeiden. Diese treten immer dann auf, wenn der Timer-B-lnterrupt während eines DMA-Transfers ausgelöst wird. Der Start der Interrupt-Routine verzögert sich bis zum Ende des DMA-Transfers, und der Zeitpunkt zum Kippeln des Sync-Bits wird verpaßt.
Ein Bild besteht jetzt aus 245 Pixel-Zeilen. Im Monochrommodus funktioniert dieses Verfahren nicht, da dort mit 71 Hz Vertikalfrequenz gearbeitet wird und das Sync-Bit somit ohne Bedeutung ist. An der Organisation des Bildspeichers ändert sich nichts, er ist nur um 7200 Bytes größer (insgesamt 10400 Bytes). Deshalb ist es auch notwendig, eine neue Bildspeicheranfangsadresse mit der XBIOS-Funktion Setscreen festzulegen. Und schon haben wir das nächste Problem: Wie bringt man GEM die neue Auflösung bei?
Während der Initialisierung von GEM wird mit Hilfe von OPEN WORKSTATION die Grafikauflösung des Bildschirms ermittelt. Dieser VDI-Aufruf wird von einer neuen Trap-2-Routine abgefangen. Hier wird die Return-Adresse auf ein Programm umgeleitet, das nach dem Aufruf der Funktion OPEN WORKSTATION im Intout-Array (work-out[1]) und im negative Line-A-Bereich (dev_tab[45]) die neue Rasterhöhe einträgt (244 für 245 Pixel-Zeilen). Gleichzeitig wird die neue maximale Cursor-Zeilenposition (30) festgelegt. Nach dieser Initialisierung werden alle VDI-Aufrufe wieder über den normalen Trap-2-Dispatcher abgewickelt. Da der Trap-2-Vektor vor der Initialisierung von GEM verbogen werden muß, funktioniert das Programm nur, wenn es in einem AUTO-Ordner abgelegt ist.
Alle Grafikroutinen (Line-A) machen die neue Auflösung ohne weitere Änderungen mit, da sich die neuen 45 Pixel-Zeilen nahtlos an die alten anschließen. Nur die Mausroutine macht hier eine Ausnahme.
Ohne Änderung der Mausroutine verschwindet die Maus in dem neuen Bildbereich. Die Ursache dafür ist die Berechnung der Bildschirmadresse, an der die Maus gezeichnet werden soll. Diese Adresse wird mit dem Befehl adda.w d1,a1 ermittelt, wobei sich in dl die relative Adresse des Mauszeigers und in al die physikalische Anfangsadresse des Bildspeichers befindet. Wenn die relative Mauszeiger-Adresse größer als 32767 ist, wird diese als negative Zahl interpretiert, und es ergibt sich eine falsche Adresse. Demzufolge muß an dieser Stelle adda.l d1,a1 stehen.
Bei der Initialisierung der neuen Grafikauflösung wird der erste Eintrag der VBL-Queue auf den neuen Maustreiber gesetzt. Dieser Maustreiber ist fast original aus dem ROM übernommen. Leider ergeben sich je nach TOS-Version für 3 Adressen unterschiedliche Werte, Diese werden vom Programm selbständig ermittelt und eingetragen. Wer sein TOS auf EPROM hat, kann diese Änderung gleich in die Original-Mausroutine eintragen.
Alle Programme, die wirklich auflösungsunabhängig programmiert sind, können Sie unter Screen plus benutzen, wie zum Beispiel 1st Word oder Turbo-C. Auch TOS-Programme profitieren von der neuen Auflösung. Sie können jetzt 30 Cursor-Zeilen nutzen.
;************************************************
;* *
;* SCREEN plus *
;* Steffen Scharfe *
;* *
;* (c) 1992 MAXON Computer *
;************************************************
v_bas_ad equ $044e
nvbls equ $0454
vblqueue equ $0456
sysbase equ $04f2
mfp equ $fffa01 ;MFP 68901
aer equ $fffa03 ;Aktive-Edge-Register
iera equ $fffa07 ;Interrupt-Enable-Register A
isra equ $fffa0f ;Interrupt-in-Service-Register A
imra equ $fffa13 ;Interrupt-Mask-Register A
tbcr equ $fffa1b ;Timer-B-Control-Reg±ster
tbdr equ $fffa21 ;Timer-B-Data-Register
sync_mod equ $ff820a ;Sync-Mode-Register
zeile equ 190
;Das hintere Programmstuck wird nur zur
;Initialisierung benötigt
bra init
;hier folgt nun das residente Programmstück
;diese Trap #2-Routine wird nur einmal
;angesprungen, um nach der Initialisierung des
;VDI die Parameter der neuen Bildschirmhöhe zu
;setzen (245 Pixelzeilen)
;neuer Trap #2 Routine
trap_2: move.l d1,vdipb ;vdipb merken
move.w d0,vdi_aes ;VDI oder AES
move.l 2(sp),return ;Return-Adresse merken
move.l #new_return,2(sp) ;neue Return-Adresse
movea.l vblqueue,a1
move.l #new_maus,(a1) ;teilweise neue Mausroutine
movea.l old_vektor,a1
jmp (a1) ;ab zum Trap 2-Dispatcher
new_return:
cmpi.w #$73,vdi_aes ;war dies ein VDI-Aufruf?
beq.s vdi ;ja
zurueck:movea.l return,a1 ;alte Return-Adresse
jmp (a1) ;zurück ins Hauptprogramm
vdi: movem.l d0-d2/a0-a2,-(sp)
movea.l vdipb,a0
movea.l (a0),a1 ;Zeiger auf Control-Array
cmpi.w #1,(a1) ;Open Workstation?
bne.s no_open ;nein
movea.l 12(a0),a1 ;Zeiger auf Intout-Array
move.w #244,2(a1) ;neue Rasterhöhe
movea.l linea_param,a0
move.w #244,-$02b2(a0) ;neue Werte in die negativen
addi.w #5,-$2a(a0) ;Line-A-Variablen eintragen
addi.w #45,-4(a0) ;45 Pixelzeilen mehr
move.l old_yektor,-(sp) ;ausklinken
move.w #34,-(sp)
move.w #5,-(sp)
trap #13 ;Setexc
addq.l #8,sp
no_open:movem.l (sp)+,d0-d2/a0-a2
bra.s zurueck
;leider notwendig ein neuer Maustreiber
; (wegen 1 Befehl !!)
new_maus:movea.l linea_param,a5 ;wegen verschiedener BS-Varianten
tst.b -$0153(a5) ;Maus-Flag
bne.s maus_end
bclr #0,-$0154(a5) ;Cur-Flag
beq.s maus_end
move.l -$0158(a5),d1 ;x/y-Position
move.l d1,d0
swap d0
movem.w d0-d1,-(sp)
lea -$014a(a5),a2 ;Save-Len
maus_adr1:
jsr 0
movem.w (sp)+,d0-dl
movea.l linea_param,a5
lea -$0358(a5),a0 ;x-Hot-Spot
lea -$014a(a5),a2 ;Save-Len
bsr.s new_maus1
maus_end:
rts
new_maus1:
move.w 6(a0),-(sp) ;Hintergrundfarbe 4(sp)
move w 8(a0),-(sp) ;Vordergrundfarbe 2(sp)
clr.w d2
tst.w 4(a0) ;Replace/Xor
bge.s replace ;Replace-Modus
moveq #$10,d2 ;XOR-Modus
replace:move.w d2,-(sp) ;Modus merken 0(sp)
clr.w d2
bclr #1,6(a2) ;Words gebuffert
sub.w (a0),d0 ;x:=x-hotspot
bcs.s x_hotspot
move.w -$02b4(a5),d3 ;maximale Rasterbreite
subi.w #15,d3 ;minus 15
cmp.w d3,d0 ;Mauszeiger vollständig sichtbar?
bhi.s teilweise_x ;nein
bset #1,6(a2) ;Longs gebuffert
bra.s maus_y
x_hotspot:
addx.w #16,d0
moveq #8,d2 ;anmerken: x-hotspot
bra.s maus_y
teilweise_x:
moveq #16,d2
maus_y: sub.w 2(a0),d1 ;y:=y-hotspot
lea $0a(a0),a0 ;Tabelle für Vordergrund und Maske
bcs.s y_hotspot
move.w -$02b2(a5),d3 ;Anzahl der Pixelzeilen
subi.w #15,d3
cmp.w d3,d1 ;Mauszeiger vollständig sichtbar?
bhi.s teilweise_y ;nein
moveq #16,d5 ;ja -> Höhe:=16 Pixelzeilen
bra.s maus1
y_hotspot:
move.w d1,d5
addi.w #16,d5 ;Höhe berechnen
asl.w #2,d1 ;y:=y*4
suba.w d1,a0 ;ersten Pixelzeilen des Mauszeigers übergehen
clr.w d1
bra.s maus1
teilweise_y:
move.w -$02b2(a5),d5 ;Anzahl der Pixelzeilen
sub.w d1,d5
addq.w #1,d5 ;Höhe berechnen
maus1: jsr 0 ;rel Adresse im Bildspeicher
;ab der die Maus gezeichnet wird
movea.l v_bas_ad,a1 ;Zeiger auf Anfang des Bildspeichers
;und nun der entscheidende Befehl
;aus adda.w d1,a1 wird...
adda.l d1,a1 ;absolute Adresse
;aha, die Maus funktioniert jetzt auch mit über
;32767 Bytes Bildspeicher
maus_adr3:jmp 0 ;weiter im Original-Programm
;Interrupt-Routinen
new_vbl:move.b #zeile,tbdr ;Timer B
move.b #8,tbcr ;Start
rts
timer_b:move.l d0,-(sp)
move #$2700,sr ;alle Ints gesperrt
w200: cmpi.b #180,tbdr ;auf 200
bne.s w200 ;Pixelzeile warten
move.b #0,sync_mod ;Sync-Bit
move w #2,d0 ;kippeln
w: nop
dbra d0,w ;etwas warten
move.b #2,sync_mod
move #$2300,sr
move.b #0,tbcr ;Zähler stop
move.l (sp)+,d0
bclr #0,isra ;Interrupt-Service fertig
rte
even
old_vektor: ds.l 1
return: ds.l 1
vdipb: ds.l 1
vdi_aes: ds.w 1
linea_param:ds.l 1
old_stack: ds.l 1
;bis hier ist das Programm resident !!
init: movea.l 4(sp),a6 ;Adresse der Basepage
move.w #$0100,d7 ;Länge der Basepage
add.l 12(a6),d7 ;Länge des Text-Segments
add.l 20(a6),d7 ;Länge des Daten-Segments
add.l 28(a6),d7 ;Länge des Speicher-/Segments
move.w #4,-(sp) ;Bildschirmauflösung holen
trap #14 ;Getrez
addq.l #2,sp
cmp.w #2,d0
beq error1 ;monochrom kann ich nicht !
dc.w $a000 ;Line-A Init
move.l a0,linea_param ;merken
clr.l -(sp) ;Supermodus
move.w #32,-(sp)
trap #1
addq.l #6,sp
move.l d0,old_stack ;alten Stack merken
;ermittelt je nach TOS-Version bestimmte Adressen
movea.l sysbase,a0
move.w 2(a0),d0 ;Versionsnummer holen
lea tos_1_2,a1
cmp.w #$0102,d0 ;TOS 1.2 ?
beq.s tos1 ;ja
lea tos_1_4,a1
emp.w #$0104,d0 ;TOS 1.4 ?
bne error3 ;nein
tos1: move.l (a1)+,maus_adr1+2 ;TOS-abhängige Adressen
move.l (a1)+,maus1+2 ;initialisieren
move.l (a1)+,maus_adr3+2
sub.l #init_end-init,d7 ;Initialisierungsteil wegwerfen
move.l a6,d6 ;Adresse Base-Page
add.l d7,d6
cmp.w #0,d6 ;unterste Byte 0 ?
beq.s byte0 ;ja
and.l #$ffff00,d6
add.l #$0100,d6 ;wegen Videoprozessor
;neue physikalische und logische
;Bildanfangsadresse setzen
byte0: move.w #-1,(sp)
move.l d6,-(sp) ;neue Bildanfangsadresse
move.l d6,-(sp) ;logische Bildanfangsadresse
move.w #5,-(sp) ;Setscreen
trap #14
lea 12(sp),sp
;VBL-Interrupt in Queue eintragen
movea.l vblqueue,a0
addq.l #4,a0 ;1.Eintrag freilassen (für GEM)
move.w nvbls,d0
subq.w #2,d0
such: move.l (a0)+,d1
beq.s eintrag ;leeren Eintrag gefunden
dbra d0,such ;weitersuchen
bra error2
eintrag:move.l #new_vbl,-(a0)
;Timer-B-Interrupt initialisieren
move.l #timer_b,$0120 ;Beginn der Interruptroutine
move.b #0,tbcr ;Timer gestoppt
;MFP Timer B initialisieren
andi.b #$f7,aer ;H-L Flanke wirksam
move.b #zeile,tbdr ;Int. bei zeile
ori.b #1,imra ;Interruptmaske freigeben
ori.b #1,iera ;Interrupt Timer B einschalten
move.w #37,-(sp) ;Wait Vbl
trap #14
addq.l #2,sp
move.b #8,tbcr ;Ereigniszählung
;Trap #2-Vektor verbiegen
pea trap_2
move.w #34,-(sp) ;Trap #2
move.w #5,-(sp) ;neuen Vektor setzen
trap #13 ;Setexc
addq.l #8,sp
move.l d0,old_vektor ;alten Vektor merken
move.l old_stack,-(sp) ;User-Modus
move.w #32,-(sp)
trap #1
addq.l #6,sp
;Programm resident hinterlassen
add.l #32000+7200,d6 ;Große Bildspeicher
sub.l a6,d6 ;benötigter Speicherplatz
clr.w -(sp) ;Status ok
move.l d6,-(sp) ;Länge
move.w #$31,-(sp)
trap #1 ;Ptermres
;Error-Meldung
error1: pea err_txt1
bra.s error
error2: pea err_txt2
bra.s error
error3: pea err_txt3
error: move.w #9,-(sp) ;Cconws
trap #1
addq.l #6, sp
move.w #-1,d0
warte: dbra d0,warte
clr.w -(sp)
trap #1
;Tabelle für die verschiedenen TOS-Versionen
;dc.l maus_ad1,maus_ad2,maus_ad3
even
tos_1_2:dc.l $fd01de,$fca212,$fd008c
tos_1_4:dc.l 0,0,0
err_txt1:dc.b "nur niedrige oder mittlere"
dc.b " Auflösung !",0
err_txt2:dc.b "alle VBL-Slots belegt !",0
err_txt3:dc.b "unbekannte TOS-Version '",0
even
init_end:
end