Nachlauffreies Scrolling

Bei dieser Routine handelt es sich um ein Assembler-Progrämmchen, das es erlaubt, auch aus Hochsprachen (BASIC, C, Pascal, Modula) ein absolut nachlauffreies Text-Scrolling zu realisieren. Um ein großes Maß an Flexibilität zu gewährleisten, teilt sich die Routine in drei verschiedene Teile ein.

Als erstes gibt es einen Programmteil, der einen ganzen Bildschirm (25 Zeilen) auf einmal ausgibt.

An dieser Stelle kommt natürlich kein Text-Scrolling zum Tragen, vielmehr macht sich hier die Geschwindigkeit der Routine beim Ausgeben von Strings bemerkbar. Ca. 0.1 Sekunde muß für einen ganzen Schirm einkalkuliert werden. Diese Zeit ist etwa um den Faktor 4.5 gegenüber der normalen Textausgabe über das Betriebssystem schneller. Des weiteren erledigen zwei andere Programmteile das Scrolling nach oben und unten. Auch hier sorgen die Routinen für einen richtigen Turbo-Effekt, und störendes Flimmern, wie etwa bei den Betriebssystem-Routinen, kann man lange suchen.

Nun aber zum Aufruf des Assembler-Bröckchens. Wie aus dem Beispiel-Programm zu ersehen, verwende ich die Routinen in Verbindung mit GFA-BASIC.

Deshalb werde ich auch meine Erklärungen in der Syntax dieser Sprache halten. Um jedoch auch den Freunden anderer Sprachen die Möglichkeit zum Verständnis zu geben, möchte ich zunächst den hier verwendeten Befehl “C:adr%(L:string%,W:mode%)” erläutern. Mit seiner Hilfe lassen sich von GFA-BASIC aus Maschinen-Routinen aufrufen. Dazu wird in der Variablen adr% die Adresse der entsprechenden Routine abgelegt. In string% findet sich beim Aufruf der beiden Scroll-Prozeduren die Adresse des auszugebenden Strings wieder. Dieser String sollte normalerweise die Länge von 80 Zeichen = einer Bildschirmzeile besitzen. Um jedoch auch kürzere Strings ausgeben zu können, läßt sich das Ende des Strings mit einem Null-Byte markieren. Der Rest des Bildschirms hinter dem Null-Byte wird dann gelöscht.

Der Parameter mode% (Wortlänge) bestimmt dabei die aufzurufende Funktion. Bei einer negativen Zahl (-1) wird der Bildschirm nach unten verschoben und der String in der obersten Zeile dargestellt.

Es wird also nach oben gescrollt. Genau die umgekehrte Richtung, nämlich nach unten, nimmt die Routine, wenn mode% auf einen positiven Wert gesetzt wird. Der Schirm wird dann nämlich nach oben verschoben und der String erscheint am unteren Rand Ihres Monitors.

Wenn Sie nun einen ganzen Bildschirm darstellen möchten, müssen Sie zuerst eine Tabelle erstellen, in der die Adressen aller auszugebender Strings aufgeführt sind. Wie man das in GFA-BASIC erledigt, kann man sich in Listing 1 ansehen.

An die Scroll-Routine übergeben Sie dann nicht mehr die Adressen der einzelnen Strings, sondern die Adresse dieser Tabelle. Für die Länge der Strings gelten die gleichen Bedingungen wie bei den eigentlichen Scroll-Routinen. Wie Sie bei dem Aufruf der Routine schon sehen können, ist der Parameter mode% in diesem Falle auf Null zu setzen.

Es ist nicht nötig, vor dem Aufruf den Mauszeiger oder den Cursor auszuschalten. Diese Aufgaben erledigt die Routine ohne Ihr Zutun.

Nach diesen Erläuterungen zur Benutzung nun aber weiter zur Arbeitsweise.

Als erstes werden der Maus- und der Textcursor ausgeschaltet, danach die Adressen des Font-Images und des logischen Bildschirmes ermittelt. Das Font-Image enthält die Daten, aus denen sich die einzelnen Zeichen zusammensetzen. Ein Zeichen belegt dort 16 Byte. Da es im Zeichensatz des ATARI 256 verschiedene Zeichen des 8*16 Systemfonts gibt, ist diese Tabelle folglich 4096 Bytes lang. Die einzelnen Bytes des Zeichens liegen dabei immer um 256 Bytes verschoben in diesem Image. Will man also z.B. ein “A” ausgeben (ASCII-Code 65), muß zur Adresse des Font-Images noch diese 65 addiert werden, und schon ergibt sich die Adresse der ersten Pixelzeile des Zeichens. Die weiteren Pixelzeilen folgen um je- weils 256 Bytes versetzt. Da direkt auf den Zeichensatz zugegriffen wird, ist es möglich, alle Zeichen aus dem Zeichensatz darzustellen. Somit lassen sich auch die Steuercodes kleiner als 32 in Form von Symbolen ausgeben.

Danach wird, in Abhängigkeit vom Parameter mode%, in die einzelnen Teile der Scroll-Routine verzweigt. Diese Programmteile rufen dann, eventuell nach dem Scrollen des Bildschirms, die Routine “AUSGABE” auf. Dort wird ein String auf den Bildschirm geschrieben. Schließlich werden dann der Maus- und Textcursor wieder sichtbar gemacht und die Routine verlassen.

Um das Prinzip besser zu verstehen und um diese Routinen in der Anwendung zu sehen, habe ich ein kleines Programm geschrieben, das ein Gerüst für einen Text-Editor darstellt (s. Listing 2).

Einige Benchmarks: (Zeiten in Sekunden)

- Betriebssystem SCROLL
Darstellen von 10 kompletten Bildschirm-Seiten
4.8 1.1
Scrollen in einem Text, 1000 Zeilen
Blitter-TOS, mit Blitter
Nach oben 37.8 22.9
Nach unten 37.4 22.8
Blitter-TOS, ohne Blitter
Nach oben 49.8 22.9
Nach unten 46.3 22.8

Benchmarks

a$=""   ! a$ löschen
FOR a%=anfang% to anfang%2  ! Zählschleife
    a$=a$+MKL$(VARPTR(feld$(a%))) ! Tab.d.Adr.erstellen 
NEXT a% ! nächst. Feldelement
tab%=VARPTR(a$) ! Adr.d.Tab.ermitteln
a%=C:adr%(L:tab%,0) ! Routine aufrufen

Listing 1: Ermitteln der Adressen der auszugebenden Strings

' © 1988 Martin Fangmeyer 
'   Wilmeresch  60
'   4430 Steinfurt 1
'
RESTORE scroll  ! Datazeiger setzen
READ len%       ! Länge der Routine lesen
scroll$=SPACE$(len%*4)  ! Speicher für die Routine reservieren
scroll%=VARPTR(scroll$) ! Adr.d.Speichers holen 
FOR a%=1 TO len%
    READ wert%
    LPOKE scroll%,wert% ! Routine i.d.Speicher (L)poken
    ADD scroll%, 4      ! Nächstes Langwort
NEXT a%
'
DIM text$(1000)         ! Stringfeld dimensionieren
FOR a%=0 TO 1000        ! Dieses Feld mit zufälligem Inhalt füllen 
    text$(a%)=LEFT$(STR$(a%)+" "+STRING$(78,RANDOM (223)+32),80)
NEXT a%
'
a$=SPACE$(100)  ! Platz für 25*4 Byte (eine Bildschirmseite) 
adr%=VARPTR(a$) ! Adresse des Strings ermitteln

FOR a%=0 TO 24  ! Die Adressen der Text-Strings festhalten 
    LPOKE adr%+a%*4,VARPTR(text$(a%))
NEXT a%
'
scroll%=VARPTR(scroll$) ! Die Adresse der Scroll-Routine holen 
VOID C:scroll%(L:adr%,0)    ! Die erste Textseite ausgeben
PRINT CHR$(27);CHR$(101);   ! Cursor einschalten
REPEAT
    a%=INP(2)               ! Auf Tastendruck warten
    IF a%=200               ! Hoch (scrollen)
        DEC zeile%
        IF zeile%<0             ! Eventuell bei 1000 neu anfangen
            zeile%=1000
        ENDIF
        IF CRSLIN=1             ! Scrollen notwendig
            scroll%=VARPTR(scroll$)
            VOID C:scroll%(L:VARPTR(text$(zeile%)),-1)
            ! Scroll-Routine aufrufen
        ELSE
            PRINT CHR$(27);CHR$(65);    ! Andernfalls Cursor eins höher
        ENDIF
    ELSE
        IF a%=208           ! Runter (scrollen)
            INC zeile%
            IF zeile%>1000  ! Evtl, bei 0 neu anfangen
                CLR zeile%
            ENDIF
            IF CRSLIN=25    ! Scrollen  notwendig
                scroll%=VARPTR(scroll$)
                VOID C:scroll%(L:VARPTR(text$(zeile%)),1)
                ! Scrollroutine aufrufen
            ELSE
                PRINT CHR$(27);CHR$(66);    !Sonst nur Cursor höher
            ENDIF
        ELSE
            IF a%=20    ! Links
                PRINT CHR$(27);CHR$(68);    ! Cursor eine Pos nach links
            ELSE
                IF a%=205   ! Rechts
                    PRINT CHR$(27);CHR$(67);    ! Cursor eine nach rechts
                ELSE    ! Taste ist kein Steuerzei-
                    OUT 2,a%    ! Daher das Zeichen auf dem Schirm ausgeben
                ENDIF
            ENDIF
        ENDIF
    ENDIF
UNTIL a%=27 ! Solange,  bis ESC gedrückt
'
scroll:
DATA 152
DATA 809238555,1627390504,809238630, 1627390496, -1610595334,37364392 
DATA -1400822,1060896771,1313756303,1140458028, 578833007,551680 
DATA 9725696,18620480,-772014080,83914815, 1289240318,1223180030 
DATA -87012136,1056852200,1056897744,1289240318, 1223180030,-87012136 
DATA 1056852200,1056897744,1289240318,1223180030, -87012136,1056852200
DATA 1056897744,1289240318,1223180030,-87012136, 1056852200,1056897744 
DATA 1289240318,1223180030,-87012136,1056852200, 1056897744,1372127130 
DATA 745472004,611975588,-704905216,2013275196,79, 1627390136,1610613116 
DATA 541118972,30240,1883196632,1056852200, 1056834768,1289240318 
DATA 1223180030,80760024,1056852200,1056834768, 1289240318,1223180030 
DATA 80760024,1056852200,1056834768,1289240318, 1223180030,80760024 
DATA 1056852200,1056834768,1289240318,1223180030, 80760024,1056852200 
DATA 1056834768,1289240318,1223180030,80777724,960, 1372127124
DATA 745472004,611975440,607911936,5202218, 1610612976,574357504 
DATA 1584128,544145412,608578648,607911936,5202190, -591659008
DATA 83907017,-1286144,13247098,14041728,270420480, 1734792128
DATA 345052521,16777296,359203328,10491241, 50331888,359203840 
DATA 20977001,83886480,359204352,31462761, 117441072,359204864 
DATA 41948521,150995664,359205376,52434281, 184550256,359205888 
DATA 62920041,218104848,359206400,73405801, 251659440,1380602314 
DATA -7188875,1108492842,5259818,10502698,15745578, 20988458,26231338 
DATA 31474218,36717098,41959978,47202858,52445738, 57688618,62931498 
DATA 68174378,73417258,78664330,1372258238, 1316306688,1060896770 
DATA 1060896771,1313692815,1316302908,1794538, 809238629,1642373129 
DATA 1316290560,0,0,0,0

Listing 2: Kleines Grundgerüst für einen Text-Editor

    MOVE #27,D0 
    BSR BCONOUT
    MOVE #102,D0    ; Cursor ausschalten
    BSR BCONOUT
    DC.W $A000      ; LINE-A installieren
    LEA CHARS(PC),A1 ; Umständlich, aber PC-relativ
    MOVE.L -$16(A0),(A1) ; Adresse des Zeichensatzes speichern
    DC.W $A00A      ; Maus ausschalten

    MOVE #3,-(SP)
    TRAP #14 
    ADDQ.L #2,SP
                ; In D0 findet man nun die Bildschirm-Adresse
    LEA LOGBASE(PC),A1
    MOVE.L D0,(A1)  ; Bildschirmadresse speichern 
    TST 8 (SP)      ; Flag:
    BMI RAUF        ; Kleiner als 0: Raufscrollen
    BEQ SEITE       ; Gleich 0: Ganze Seite darstellen
    MOVE.L D0,A0    ; Ansonsten: Runterscrollen
    ADD.L #1280,A0  ; 1. Zeile löschen
    MOVEQ.L #63,D0  ; 64 Durchläufe => 24 Zeilen bewegen
SCROLL_DOWN:
    MOVEM.L (a0)+,D1-D7/A1-A5 ; mit 2 Zeilen werden 48 Byte kopiert 
    MOVEM.L D1-D7/A1-A5,-1328(A0) ; 1 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 2 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 3 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 4 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 5 
    MOVEM.L (a0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 6 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 7 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 8 
    MOVEM.L (a0)+,D1-D7/A1-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 9 
    MOVEM.L (a0)+,D1-D7/Al-A5 
    MOVEM.L D1-D7/A1-A5,-1328(a0) ; 10 
    DBRA D0,SCROLL_DOWN     ; insgesamt: 64*10*48=30720 Byte kopieren
    MOVE.L 4(SP),A6         ; Adresse des Strings vom Stack holen
    MOVE.L LOGBASE(PC),A2   ; Bildschirmadresse in A2
    ADD.L #30720,A2         ; Nur unterste Zeile schreiben 
    MOVE.L #79,D2           ; 80 Zeichen
    BSR AUSGABE             ; Zeile ausgeben
    BRA ZURÜCK              ; Und zurück zum aufrufenden Programm
RAUF:                       ; Raufscrollen
    MOVE.L D0,A0            ; Bildschirmadresse in  A0
    ADD.L #30240,A0         ; Unterste Zeile löschen
    MOVEQ #63,D0            ; 64 Durchläufe (0 zählt mit)

SCROLL_UP:
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   1
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   2
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   3
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   4
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   5
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   6
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   7
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   8
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   9
    MOVEM.L (A0)+,D1-D7/A1-A5
    MOVEM.L D1-D7/A1-A5,1232(A0)    ;   10
    SUB.L #960,A0
    DBRA D0,SCROLL_UP       ; 64*10*48=30720 Byte kop.
    MOVE.L 4(SP),A6         ; Adresse des Strings
    MOVE.L LOGBASE(PC),A2   ; Bildschirmadresse
    MOVE.L #79,D2           ; 80 Zeichen
    BSR.S AUSGABE           ; Oberste Zeile schreiben
    BRA ZURÜCK              ; Zurück zum aufrufenden Programm

SEITE:  ; Eine ganze Seite schreiben
    MOVE.L #24,D1           ; 25 Bildschirmzeilen 
    MOVE.L D0,D6            ; Bildschirmadr.in D6 speichern 
    MOVE.L 4(SP),A0         ; Adresse der Stringtabelle 25*4 Byte

NÄCHSTE_ZEILE:
    MOVE.L D6,A2    ;   Bildschirmadresse
    MOVE.L (A0)+,A6 ;   eine Zeile holen
    MOVE.L #79,D2   ;   80 Buchstaben
    BSR.S AUSGABE   ;   eine Zeile ausgeben
    ADD.L #1280,D6  ; eine Cursorzeile tiefer
    DBRA D1,NÄCHSTE_ZEILE ; nächste Zeile
    BRA ZURÜCK      ; Zurück zum aufrufenden Programm

AUSGABE:
    MOVE.L CHARS(PC),A1 ; Adr.d.Zeichensatzdaten 
    CLR.L D0
    MOVE.B (A6)+,D0     ; Ein Byte holen
    TST.B D0            ; Byte testen
    BEQ.S ZEILE_ZU_ENDE ; Zeile zu Ende (Byte =0)? 
    ADD.L D0,A1         ; + Offset
    MOVE.B (A1),(A2)    ; Zeichen auf den Bildschirm bringen
    MOVE.B 256(A1),80(A2)
    MOVE.B 512(A1),160(A2)
    MOVE.B 768(A1),240(A2)
    MOVE.B 1024(A1),320(A2)
    MOVE.B 1280(A1),400(A2)
    MOVE.B 1536(A1),480(A2)
    MOVE.B 1792(A1),560(A2)
    MOVE.B 2048(A1),640(A2)
    MOVE.B 2304(A1),720(A2)
    MOVE.B 2560(A1),800(A2)
    MOVE.B 2816(A1),880(A2)
    MOVE.B 3072(A1),960(A2)
    MOVE.B 3328(A1),1040(A2)
    MOVE.B 3584(A1),1120(A2)
    MOVE.B 3840(A1),1200(A2)    ; 16 MOVE-Befehle

NÄCHSTES_ZEICHEN:
    ADDQ #1,A2          ;   eine Zeichenposition weiter
    DBRA D2,AUSGABE     ; und nächstes Zeichen ausgeben 
    RTS

ZEILE_ZU_ENDE:          ; Nach einem Null-Byte wird die

    CLR.B (A2)  ; Zeile bis zum rechten Rand gelöscht
    CLR.B 80(A2)
    CLR.B 160(A2)
    CLR.B 240(A2)
    CLR.B 320(A2)
    CLR.B 400(A2)
    CLR.B 480(A2)
    CLR.B 560(A2)
    CLR.B 640(A2)
    CLR.B 720(A2)
    CLR.B 800(A2)
    CLR.B 880(A2)
    CLR.B 960(A2)
    CLR.B 1040(A2)
    CLR.B 1120(A2)
    CLR.B 1200(A2)
    ADDQ.L #1,A2
    DBRA D2,ZEILE ZU_ENDE ; Weiter bis zum rechten Rand
    RTS

BCONOUT:    ; ein Zeichen 'normal' ausgeben, z.B.
            ; um den Cursor ein- und auszuschalten          
    MOVE D0,-(SP)   ; Zeichen auf den Stack
    MOVE #2,-(SP)   ; 'Zielgerät' Bildschirm (Console)
    MOVE #3,-(SP)   ; Funktionsnummer
    TRAP #13        ; Bios aufrufen
    ADDQ.L #6,SP    ; Stack reparieren
    RTS

ZURÜCK: ;   Hier kehrt die Routine zu Basic zurück
    MOVE #27,D0     ; Escape-Sequenz einläuten
    BSR.S BCONOUT
    MOVE #101,D0    ; Cursor einschalten
    BSR.S BCONOUT
    DC.W $A009      ; Maus wieder einschalten
    RTS

CHARS:  DS.L    1   ; Speicher für Adresse des Zeichensatzes 
LOGBASE:    DS.L 1  ; Speicher für Bildschirmadr.

END

Listing 3: Die Assembler-Routine
Martin Fangmeyer



Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]