Syncroll: Das Softscroling im Interrupt

Das Thema Scrolling schien sich schon vor Jahren erschöpft zu haben, und die große Mehrheit der ST-Besitzer hat sich inzwischen damit abgefunden, daß andere Computer (insbesondere der Amiga) dem ST mit Hilfe ihrer exzellenten Hardware förmlich „davonscrollten“. Doch auch jetzt zeigt sich wieder, daß sich durch raffinierte Programmierung aus der vorhandenen Hardware erstaunliche, ja sogar für technisch unmöglich gehaltene Effekte herauskitzeln lassen. Eine völlig neue Technik, das „Syncscrolling“, bereitet den bestehenden Problemen zumindest bei einem Scrolling in vertikaler Richtung ein Ende.

Dm Genre der Action-Spiele ist das Scrolling schon nahezu unverzichtbar geworden. Die Qualität eines solchen Spieles wird nicht zuletzt auch an der Qualität des Scrollings gemessen. Da es jedoch zumeist auch der rechenintensivste Teil ist, ergeben sich hierfürden Programmierer große Probleme. Worin diese bestehen, soll erst einmal erklärt werden:

Perfektion

Stellen Sie sich einmal vor, welche Anforderungen man an ein wirklich perfektes (vertikales) Scrolling stellen kann. Dazu gehört wohl in erster Linie die Geschwindigkeit, die wie die Bildfrequenz des Monitors 50 Hertz betragen sollte. Auch sollten alle 16 (oder sogar noch mehr) Farben verschoben werden, statt vier oder nur zwei. Weiterhin hat der zu bewegende Bereich des Bildes möglichst groß zu sein und, wenn möglich, den gesamten Bildschirm zu umfassen.

;------------------------;
;                        ;
;    S Y N C R O L L     ;
;                        ;
;  Das Softscrolling im  ;
;       Interrupt        ;
;                        ;
; von Christoph Hartwich ;
; mit GFA-Assembler V1.1 ; 
; (c) 1992 MAXON Computer;
;                        ;
;     .. LOW RES ..      ;
;------------------------;

VBL_IR_VEC          equ $70
TIMER_C_VEC         equ $114
TIMER_ B_VEC        equ $120

_V_BAS_AD           equ $44e

VIDEO_BASE_H        equ $ff8201
VIDEO_BASE_M        equ $ff8203
VADR_COUNT_H        equ $ff8205
VADR_COUNT_M        equ $ff8207
VADR_COUNT_L        equ $ff8209
SYNC_MODE           equ $ff820a
COLOR_REG           equ $ff8240

IR_ENABLE_A         equ $fffa07
IR_ENABLE_B         equ $fffa09
IR_SERVICE_A        equ $fffa0f
IR_MASK_B           equ $fffa15
TIMER B_CTRL        equ $fffa1b
TIMER_B_DATA        equ $fffa21

ACIA_DATA           equ $fffc02

super:  clr.l       -(sp)     ; ab in den
        move.w      #32,-(sp) ; .
        trap        #1        ; .
        addq.l      #6,sp     ; Supervisormodus

main:   bsr         _action_init
        bsr         _syncroll_init
        bsr         _action
        bsr         _syncroll_end
        bra         _action_end

        ;---------------------------------------
        ;---             action.init         ---
        ;---------------------------------------
_action_init:
        move.w      #2,-(sp)    ; physbase
        trap        #14         ; .
        addq.l      #2,sp       ; .
        move.l      d0,xbios2   ; => xbios2

        move.l      #block,d0   ; nächste nach
        divu        #1280,d0    ; #block kommende
        addq.l      #1,d0       ; durch 1280 teil-
        mulu.w      #1280,d0    ; bare Adresse
        move.l      d0,pic      ; => pic

        ;   Neue VB-Adresse fur Bildschirmaus-
        ;   gaben anmelden :
        move.w      #-1,-(sp)
        move.l      pic(pc),-(sp)
        move.l      pic(pc),-(sp)
        move.w      #5,-(sp)
        trap        #14
        lea.l       12(sp),sp

        ;   Farbregister sichern : 
        movem.l     COLOR_REG,d0-d7 
        movem.l     d0-d7,palsave

        rts

        ; ---------------------------------
        ; ---       syncroll.init       ---
        ; ---------------------------------

Nun drängt sich natürlich die Frage auf, warum ein Spiel, das allen diesen Anforderungen entspricht, nur sehr selten zu entdecken ist. Die Antwort ist einfach: Es ist faktisch unmöglich, mit einem normalen ST ein Langwort (4 Bytes) von einer Speicherstelle in eine andere zu kopieren, ohne dazu nicht mindestens 16 Taktzyklen zu benötigen. Um einen Bildschirm mit 32000 Bytes komplett zu verschieben, werden also mindestens 128000 Taktzyklen benötigt. Da in einer 1/50 Sekunde jedoch nur 160000 zur Verfügung stehen, verbraucht allein das Scrolling schon 80% der zu Verfügung stehenden Kapazität. Daß die verbleibenden 20% oft nicht mehr ausreichen, um Sprite-Darstellung, Joystick-Abfrage, Score-Anzeige etc. zu realisieren, ist offensichtlich.

Eine zweite Möglichkeit, ein „SCreen ROLLING“ zu programmieren, besteht darin, einfach die Anfangsadresse des darzustellenden Bildes zu verändern. Ein Erhöhen dieser Adresse um 160 Bytes hätte zur Folge, daß der Bildschirm um 1 Zeile nach oben gescrollt würde, da sich im Farbmodus eine Zeile eben aus 160 Bytes zusammensetzt. Doch so einfach macht der ST einem die Sache nicht, denn diese Video-Base-Adresse (ab jetzt kurz „VB-Adresse“) kann nur in 256 Byte-Schritten verändert werden. Daraus folgt, daß die kleinste Veränderung 1280 Bytes betragen muß, weil dieses die kleinste Zahl ist, die sowohl durch 256 als auch durch 160 teilbar ist. Das so erzeugte Scrolling ist folglich in dieser Form nicht zu gebrauchen, denn es kann das Bild nur in einem sehr groben Raster von mindestens 8 Zeilen verschieben, es ist nicht mehr „soft“.

Der Video-Controller

Um das Konzept des Syncscrollings durchschauen zu können, ist zuerst eine kurze Schilderung der üblichen Vorgehensweise des Video-Controllers nötig.

Nehmen wir einmal an, der Elektronenstrahl des Monitors befände sich gerade in der linken oberen Ecke. Der Controller überträgt hierauf den Inhalt der Video-Base Register, in denen sich die Anfangsadresse des darzustellenden Bildes befindet, in den eigenen Adreßzähler.

Der Strahl rast nun am oberen Bildschirmrand nach rechts und schreibt dabei die Daten auf den Schirm, die der Controller ihm liefert. Da im Augenblick noch der obere Rand dargestellt werden muß, bekommt der Strahl auch nur dessen Daten, also ständig die Farbe 0, übertragen. Wenn er auf seinem Weg den rechten Monitorrand erreicht hat, sendet der Controller ein Signal zur horizontalen Synchronisation, was für den Elektronenstrahl ein Zeichen ist, in die darunterliegende Zeile links zu springen und sich dort von neuem auf den Weg nach rechts zu machen. Wenn er auf die Zeilen trifft, in denen Bildinformationen stehen, behält er diese Vorgehensweise überwiegend bei, nur mit dem Unterschied, daß er jetzt Daten nach dem Schema linker Rand - Bilddaten -rechter Rand - HSync. für jede Zeile bekommt.

Jedesmal, wenn der Controller Bilddaten schickt, hat er sie zuerst aus dem Speicher geholt und zwar aus der Speicherstelle, auf die sein Adreßzähler zeigt. Danach wird dieser Zähler natürlich erhöht. Sobald der Strahl den Bildteil und den unteren Rand hinter sich hat, sendet der Controller ein Vertikal-Synchronisationssignal, welches den Strahl veranlaßt, in die linke obere Ecke zu springen und wieder die VB-Adresse aus den Video-Base Registern in seinen Adreßzähler zu übertragen.

_syncroll_init:
        ;   MFP-Register sichern und danach nur
        ;   TimerB und Tastatur-IR erlauben :
        move.b      IR_ENABLE_A,old7
        move.b      IR_ENABLE_B,old9
        move.b      #1,IR_ENABLE_A
        move.b      #64,IR_ENABLE_B

        ;   Neuen VBL-Vektor installieren :
        move.l      VBL_IR_VEC,oldvbl
        move.l      #vbl_routine,VBL_IR_VEC

        ;   TimerB vor initialisieren : 
        clr.b       TIMER_B_CTRL
        pea         tb_routine1(pc)
        clr.l       -(sp)
        move.w      #1,-(sp)
        move.w      #$1f,-(sp)
        trap        #14
        lea.l       12(sp),sp

        ;   Damit die gewünschte VB-Adresse zu 
        ;   Anfang nicht auf Null zeigt : 
        move.l      _V_BAS_AD,vbadr
        move.l      _V_BAS_AD,vbadr2

        ;   Die Maus kaltstellen und die beiden
        ;   JMP-Befehle vervollständigen :
        move.b      #$12,ACIA_DATA
        move.l      oldvbl(pc),op_jmp1+2
        move.l      TIMER_C_VEC,op_jmp2+2

        rts

        ; ------------------------------------
        ; ---           action             ---
        ; -------------------------------------
        ;   Der Aktionteil ist relativ uninter-
        ;   issant, aber irgend etwas muß sich 
        ;   ja auf dem Bildschirm tun.

_action:
        ;   Text ausgeben :
        pea         string(pc)
        move.w      #9,-{sp)
        trap        #1
        addq.l      #6,sp

        ;   Die Bildschirmposition verändern
        ;   und beim Erreichen der Grenzwerte
        ;   die Richtung umdrehen :
hz50:   moveq.l     #0,d0
        move.w      dy(pc),d0
        add.w       d0,y
        cmpi.w      #189,y
        bne.s       not189
        move.w      #-1,dy
not189: tst.w       y
        bne.s       not0
        move.w      #1,dy
not0:   move.w      y(pc),d0
        mulu.w      #160,d0

        ;   Die gewünschte VB-Adresse über-
        ;   geben und vbiflag setzen, damit
        ;   man später merkt, wann es vom
        ;   VBL-IR wieder gelöscht wird :
        move.l      pic(pc),vbadr
        add.l       d0,vbadr
        move.w      #1,vbiflag

        ;   Balkenposition = y*2+50
        moveq.l     #0,d0
        move.w      y(pc),d0
        asl.w       #1,d0
        addi.w      #50,d0

        ;   Notfalls begrenzen :
        cmpi.w      #379,d0
        bls.s       nocut
        move.w      #379,d0

Dazwischengefunkt

Im Sync-Mode-Register läßt sich mit dem Bit 0 steuern, ob der Controller die Synchronisationssignale selber erzeugt (Standardeinstellung) oder das einem externen Gerät wie z.B. einem Videorekorder überlassen soll.

Die zweite Einstellung wurde bisher kaum beachtet oder höchstens für die Realisierung von Bildschirmschonern mißbraucht. Wir wollen zwar auch keinen Videorekorder anschließen, nutzen diese Option jedoch auf unsere Weise.

Das entscheidende an der Sache ist, daß sobald ein Synchronisationsimpuls fehlt (unser nicht angeschlossener Videorekorder denkt gar nicht daran, einen solchen zu senden), der Elektronenstrahl ohne dieses Signal in die nächste Zeile links springt, wenn auch geringfügig später. Der Video-Controller hingegen wartet mit der weiteren Übertragung, bis er vom externen Gerät einen solchen Synchronisationsimpuls bekommt. Während dieser Zeit bekommt der Elektronenstrahl keine Daten, und so bleiben alle Bereiche, durch die er kommt, dunkel.

Wird das Bit im Sync-Mode-Register kurz darauf in den alten Zustand versetzt, erzeugt der Controller die Impulse wieder selbst und fährt mit der Übertragung fort, als ob nichts gewesen wäre.

Daß der Monitor vielleicht schon 10 Zeilen weiter ist, die schwarz auf dem Schirm erscheinen, bemerkt der Controller gar nicht und sendet weiter Bilddaten, die 10 Zeilen zu spät kommen und folglich alle 10 Zeilen nach unten verrutscht auf dem Monitor erscheinen (Abb. 1).

Wenn man dem Controller nun kontinuierlich eine Zwangspause von einigen Zeilen auferlegt, sobald er versucht, die ersten Zeilen des oberen Randes zu schreiben, läßt sich damit schon einmal steuern, um wieviele Zeilen sich das gesamte nun folgende Bild nach unten verschiebt.

Abb. 1: Ein normales Bild (a) und ein Bild mit fehlenden HSync-Signalen (b)
        ;   Quell- und Zieladresse ermitteln : 
nocut:  lea.l       data(pc),a0
        movea.l     pic(pc),a1
        mulu.w      #160,d0
        adda.l      d0,a1
        movea.l     a1,a2

        ;   Den Balken darstellen : 
        moveq.l     #18,d3
box:    move.l      (a0)+,d1
        move.l      (a0)+,d2
        moveq.l     #19,d0
line:   move.l      d1,(a1)+
        move.l      d2,(a1)+
        dbra        d0,line
        dbra        d3,box

        ;   Punkte darstellen, solange noch
        ;   Zeit übrig ist :
        moveq.l     #0,d0
        movea.l     pic(pc),a0
        lea.l       8000(a0),a0
        suba.l      pic(pc),a2
        lea.l       -3000(a2),a2
        lea.l       3200(a2),a3
plot:   addi.l      #3717,d0
        add.b       VADR_COUNT_M,d0
        cmpi.l      #53999,d0
        bls.s       modify
        subi.l      #54000,d0
modify: move.b      VADR_COUNT_M,d1
        cmpa.l      d0,a2
        bgt.s       legal
        cmpa.l      d0,a3
        bgt.s       illegal
legal:  bset        d1,0(a0,d0.l)
illegal: tst.w      vblflag
        bne.s       plot

        ;   Solange weiter, bis 'q' gedrückt
        move.w      #$ff,-(sp)
        move.w      #6,-(sp)
        trap        #1
        addq.l      #4,sp
        cmpi.w      #113,d0
        bne         hz50
        rts

        ; -----------------------------------------
        ; ---           syncroll_end            ---
        ; -----------------------------------------

_syncroll_end:
        ; Die Tastatur wiederbeleben und die 
        ; Maus wecken : 
        bset        #6,IR_MASK_B
        move.b      #8,ACIA_DATA

        ;   Den alten VBL-Vektor und die Farb-
        ;   register restaurieren : 
        move.l      oldvbl(pc),VBL_IR_VEC

        ;   Die MFP-Register restaurieren : 
        move.b      old7(pc),IR_ENABLE_A 
        move.b      old9(pc),IR_ENABLE_B

        rts

        ; ---------------------------------------
        ; ---           action_end            ---
        ; ---------------------------------------
_action_end:
        ; alte Auflösung, Physbase und 
        ; Logbase zurück : 
        move.w      #-1,-(sp)
        move.l      xbios2(pc),-(sp)
        move.l      xbios2(pc),-(sp)
        move.w      #5,-(sp)
        trap        #14
        lea.l       12(sp),sp

        ;   Farbregister restaurieren :

Die ersten Zeilen eignen sich für einen solchen Eingriff am besten, weil sie zumeist noch unter dem oberen Lack oder Plastikrand des Monitors verborgen sind.

Diese Zwangspause muß sich allerdings in Grenzen halten, weil der Monitor, der sehnsüchtig auf das VSync-Signal wartet, sonst selbständig an den Bildanfang springt, noch bevor der Controller sein Bild zuende gesendet hat, das Bild finge dann an zu ‘laufen’.

Die Lösung des Problems liegt in einer Verknüpfung dieser Technik mit dem am Anfang beschriebenen 8-Zeilen-Scrolling. Wenn die Grobarbeit mit dem letzteren erledigt wird, muß nur noch ein Weg gefunden werden, mit dem Synchronisations-Trick ein Scrolling von 0 bis 7 Zeilen zu erzeugen. Mit einer Kombination ließe sich dann jede einzelne Zeile ansteuern. Ein Problem bleibt, daß der Rand oben und unten nicht mehr stillsteht wie erwünscht, sondern auch seine Position verändert. Durch ein geschicktes Einfärben der benachbarten Zeilen mit der Farbe Schwarz {alle Farbregister auf $000 setzen) können die Ränder aber bis zu einer festgelegten Marke ergänzt werden, so daß sie scheinbar die selbe Position behalten. Dafür müssen aber leider Bildzeilen hinhalten, auf denen sonst Grafik dargestellt werden könnte.

Konkret

Das Modell in Abb. 2 vereinigt mehrere Vorteile in sich: eine hohe Flexibilität, geringe Rechenzeit (nur 5%!) und einen großen Bildausschnitt. Angenommen, die gewünschte VB-Adresse läge bei 2080 (= 1280+5*160). Die Scroll-Routine muß jetzt die reale VB-Adresse auf den nächsten kleineren sowohl durch 160 als auch durch 256 teilbaren Wert setzen, also auf 1280. N wird jetzt definiert als die Anzahl der Zeilen, die das Bild eigentlich zu früh begänne (hier 5). Das Bild muß nun mit dem Sync-Trick um N Zeilen nach unten verschoben und der obere Rand, der sich mit nach unten verschiebt, um 9-N Zeilen nach unten ergänzt werden. Der Rand bleibt jetzt genau 9 Zeilen unter der Position, die er vor dem Programmstart einnahm, egal welcher Wert für N eingesetzt wird. Eine Ergänzung von 7-N Zeilen würde genügen, der TimerB benötigt jedoch noch 2 Zeilen Pause, bevor er eingreifen kann.

Abb. 2: Das Timing des Syncscrollings

Der untere Rand wird im Gegenzug um N Zeilen nach oben hin ergänzt. Der verbleibende Mittelteil des Bildes, 191 Zeilen, ist der sichtbare Bereich des Scrollings. Wer nachrechnet (und das sollten Sie), wird feststellen, daß das Bild immer noch 2 Zeilen zu spät beginnt, was aber einfach durch ein Vermindern der gewünschten VB-Adresse um 2*160 unmittelbar vor der Rechnung zu korrigieren ist.

Ein Nachteil des Syncscrollings besteht zweifelsfrei in der Tatsache, daß es nur für Farbmonitore geeignet ist. Die Synchronisationssignale für einen Fernseher werden nämlich vom FIF-Modulator und nicht vom Video-Controller erzeugt, und der läßt sich nicht so einfach hintergehen. Der SM124 ist auch disqualifiziert, weil er leider etwas anders auf fehlende HSync-Signale reagiert.

Zur Praxis

Nach langer Theorie wenden wir uns jetzt der Praxis zu. Dieser Teil stellt hohe Anforderungen, weil einerseits die Realisierung ausschließlich in Assembler möglich ist, und andererseits ein gutes Grund wissen bezüglich der Interrupt-und Timer-Programmierung des ST vorausgesetzt wird.

Am Anfang des Bildes wird eine VBL-Routine aktiviert, die HSync-Signale unterdrückt, den TimerB initialisiert und zuletzt in die VBL-Systemroutine springt. Um N HSync-Signale auszulassen, muß das Synchronisations-Bit für N*512 Taktzyklen auf eins (extern) gesetzt werden, da der Video-Controller zum Schreiben einer Zeile genau 512 Taktzyklen benötigt. Der Rest des oberen Randes wird auf den Monitor geschrieben, und nach der Bilddatenzeile Nummer 8-N unterbricht die TimerB-Routine das Hauptprogramm. Diese verbiegt ihren eigenen Vektor auf eine zweite Timer-B-Routine, die 191 Zeilen später aktiv werden soll, setzt in den Farbregistern, die vorher gelöscht waren, die 16 Farben des kommenden Bildes und verabschiedet sich durch eine Verzweigung in die alte 200Hz-Systemroutine. Jetzt folgen 191 „normale“ Zeilen, danach meldet sich die zweite Timer-B-Routine zu Wort, deren Aufgabe es ist, die ganze Palette wieder zu löschen und die Video-Base Register zu aktualisieren, da das Hauptprogramm vielleicht inzwischen die VB-Adresse geändert hat. Sowohl der untere als auch der folgende obere Rand bleiben dunkel, bis die erste Timer-B-Routine wieder Licht in die Sache bringt.

Es wäre sehr umständlich, auf die Systeminterrupts zu verzichten, diese jedoch nach Belieben walten und den empfindlichen Takt stören zu lassen, ist genauso undenkbar. Dabei ist es nur nötig, die bisherigen Routinen auszuklinken und sie selber regelmäßig aufzurufen, wenn genug Rechenzeit vorhanden ist, der nächste „eigene“ Interrupt also erst einige Zeilen später aktiv wird. Da die Darstellung der Maus zu lange dauert und noch dazu den maßlosen Anspruch stellt, auf einem IPL von 7 zu laufen, muß sie leider zu Beginn abgeschaltet werden. Systemtimer, Tastaturklick, Tastenwiederholung, etc. bleiben aber erhalten. Ein geringes Manko bleibt vielleicht, daß die 200Hz-Systemroutine nur in einem 50Hz-Takt aufgerufen wird, bei einer entsprechend angepaßten Tastatur-Wiederholrate fällt das aber kaum auf. Wen die Interrupts stören, der kann sie leicht durch ein Einfügen eines „RTE“ vor jeden der beiden „JMP $0“-Befehle herausnehmen.

Die Einbindung

Ein weiterer kritischer Punkt ist neben der Zusammenarbeit zwischen den Interrupts und dem Hauptprogramm immer die Einbindung einer Routine in das eigene Programm. Ich schlage hier einen anderen Weg vor: Binden Sie doch Ihr Programm in Syncroll ein. Auch ein auf dem Gebiet der Interrupts unerfahrener Assemblerprogrammierer sollte so in der Lage sein, die Routinen für sich zu verwenden, denn es müssen nur die Unterprogramme _action_init, _action und _action_end mit den eigenen Routinen belegt werden.

Das Hauptprogramm darf ab jetzt nicht mehr direkt auf die Farbregister zugreifen, sondern muß die Farbtabelle ab der Adresse prgpal benutzen, die in allen Einzelheiten dem Original entspricht. In der VBL-Routine, also ganz am Anfang, wird das Wort vblflag gelöscht, damit das Hauptprogramm erkennen kann, wann ein VBL stattgefunden hat. Die gewünschte VB-Adresse (durch 160 teilbar) wird jetzt einfach vom Hauptprogramm in das Langwort an der Adresse vbadr geschrieben, den Rest erledigen die Interrupts.


        movem.l     palsave(pc),d0-d7 
        movem.l     d0-d7,COLOR_REG

        clr.w       -(sp)           ; Und damit lebt
        trap        #1              ; wohl ...

        ; -------------------------------------
        ; ---           VBL-Routine         ---
        ; -------------------------------------
vbl_routine:
        ; Register sichern / VBL-Flag löschen 
        movem.l     d0/d1,regs
        clr.w       vblflag

        ;   d0 = 8-N
        move.l      vbadr2(pc),d1
        move.l      d1,d0
        divu        #1280,d1
        mulu.w      #1280,d1
        sub.l       d1,d0
        divu        #160,d0
        addq.w      #1,d0

        ; TimerB-Vektcr auf 1. Routine umbie-
        ; gen und TimerB mit 8-N Seilen vor-
        ; belegen ( die Timer-Routine wartet 
        ; auch noch eine Zeile; = 9-N ) : 
        move.l      #tb_routine1,TIMER_B_VEC 
        move.b      d0,TIMER_B_DATA

        ;   Synchronisation für ca. N*512
        ;   Taktzyklen auf 'extern' stellen :
        moveq.l     #8,d1
        sub.l       d0,d1
        asl.w       #5,d1
        bset        #0,SYNC_MODE
extern: nop
        dbra        d1,extern
        bclr        #0,SYNC_MODE

        ;   TimerB von der Leine lassen, die 
        ;   Register restaurieren und in die 
        ; VBL-Systemroutine springen : 
        move.b      #5,1000,TIMER_B_CTRL
        movem.l     regs(pc),d0/d1
op_jmp1: jmp        $0
        rte                 ; ist überflüssig

        ; ------------------------------------------
        ; ---       erste Timer-B-Routine        --
        ; ------------------------------------------
        tb_routine1:
        ;   die zweite TimerB-Routine 191 
        ;   Zeilen später aufrufen : 
        clr.b       TIMER_B_CTRL
        move.b      #191,TIMER_B_DATA
        move.b      #%1000,TIMER_B_CTRL
        move.l      #tb_routine2,TIMER_B_VEC

        ;   Die Register retten und die Farben 
        ;   schon in die Register laden : 
        movem.l     d0-d7/a0/a1,regs
        movem.l     prgpal(pc),d0-d6/a0

        ;   Auf den rechten Rand warten 
        lea.l       VADR_COUNT_L,a1
rborder1: move.b    (a1),d7
        cmp.b       (a1),d7
        bne.s       rborder1

        ;   Die Pallette 'anknipsen' und die 
        ;   Register restaurieren. 
        movem.l     d1-d6/a0,COLOR_REG+4
        move.l      d0,COLOR_REG
        movem.l     regs(pc),d0-d7/a0/a1

        ;   Interrupts freigeben, Tastatur auch 
        ;   wieder erlauben und in die 200Hz-
        ;   Systemroutine springen :
        bclr        #0,IR_SERVICE_A
        bset        #6,IR_MASK_B
op_jmp2:jmp         $0

        ; -----------------------------------
        ; ---    zweite TimerB-Routine    ---
        ; -----------------------------------
tb_routine2:
        ;   TimerB stoppen und die Tastatur 
        ;   verriegeln. Der Tastatur-IR darf
        ;   nämlich nicht den folgenden VBL-IR
        ;   verzögern oder stören. 
        clr.b       TIMER_B_CTRL
        bclr        #6,IR_MASK_B
        movem.l     d0/a0,regs

        ;   Kurz warten : 
        move.l      $23456,$23456

        ; Auf den rechten Rand warten : 
        lea.l       VADR_COUNT_L,a0
rborder2: move.b    (a0),d0 
        cmp.b       (a0),d0
        bne.s       rborder2

        ;   Bildschirm verdunkeln :
        moveq.l #0,d0
        lea.l       COLOR_REG+4, a0
        move.l      d0,(a0)+
        move.l      d0,(a0)+
        move.l      d0,(a0)+
        move.l      d0,(a0)+
        move.l      d0,(a0)+
        move.l      d0,(a0}+
        move.l      d0,(a0)+
        move.l      d0,COLOR_REG

        ;   Damit vbadr vom Hauptprogramm wie-
        ;   der verändert werden kann, wird es 
        ;   jetzt gesichert.
        ;   Von einem Bild würden normalerweise 
        ;   die ersten 2 Seilen fehlen, deshalb 
        ;   wird vbadr2 um 320 vermindert : 
        move.l      vbadr(pc),d0
        subi.l      #160*2,d0
        move.l      d0,vbadr2

        ;   Die Video-Base Register für das
        ;   nächste Bild belegen :
        divu        #1280,d0
        mulu.w      #1280,d0
        lsr.l       #8,d0
        move.b      d0,VIDEO_BASE_M
        lsr.l       #8,d0
        move.b      d0,VIDEO_BASE_H
        movem.l     regs(pc),d0/a0

        ;   IRs freigeben und raus hier :
        bclr        #0,IR_SERVICE_A
        rte

        ; ------------------------------
        ; ---       Variablen        ---
        ; ------------------------------

        ; von den Scrollingroutinen benutzte 
        ; Variablen : 
old7:   .DC.b 0     ; $fffa07 gesichert
old9:   .DC.b 0     ; $fffa09 gesichert
oldvbl: .DC.l 0     ; alter VBL-Vektor
regs:   .DS.l 10    ; zum Retten der Register
vbadr2: .DC.l 0     ; gew. VB-Adr. intern

        ; von beiden Teilen benutzte Var. 
vbadr:  .DC.l 0     ; gewünschte VB-Adresse
vblflag:.DC.w 0     ; Infoflag für das Hauptprg.
prgpal: .DC.w 0,1,2,3,4,5,6,7
        .DC.w $117,$227,$337,$447 
        .DC.w $557,$667,$777,$777

        ; Variablen des Hauptprogramms :
xbios2: .DC.l 0
string: .DC.b 13,10,'               SYNCROLL',0
        .EVEN
y:      .DC.w 0
dy:     .DC.w 1
pic:    .DC.l 0
data:   .DC.w $0000,$0000,$0000,$0000
        .DC.w $0000,$0000,$0000,$0000 
        .DC.w $ffff,$0000,$0000,$0000 
        .DC.w $0000,$ffff,$0000,$0000 
        .DC.w $ffff,$ffff,$0000,$0000
        .DC.w $0000,$0000,$ffff,$0000 
        .DC.w $ffff,$0000,$ffff,$0000 
        .DC.w $0000,$ffff,$ffff,$0000 
        .DC.w $ffff,$ffff,$ffff,$0000 
        .DC.w $0000,$0000,$0000,$ffff 
        .DC.w $ffff,$0000,$0000,$ffff 
        .DC.w $0000,$ffff,$0000,$ffff 
        .DC.w $ffff,$ffff,$0000,$ffff 
        .DC.w $0000,$0000,$ffff,$ffff 
        .DC.w $ffff,$0000,$ffff,$ffff 
        .DC.w $0000,$ffff,$ffff,$ffff
        .DC.w $ffff,$ffff,$ffff,$ffff 
        .DC.w $0000,$0000,$0000,$0000 
        .DC.w $0000,$0000,$0000,$0000 
        .BSS
palsave:.DS.w 16
block:  .DS.b 64000+1280


Christoph Hartwich
Aus: ST-Computer 05 / 1992, Seite 92

Links

Copyright-Bestimmungen: siehe Über diese Seite