Nachdem ich bei einem Freund eine Maschinen-Routine sah, die es aus GFA-Basic heraus ermöglicht [mit dem Befehl CALL adr()], schnell pixelweise horizontal auf dem Bildschirm âum die Uhrâ zu scrollen, wurde ich neugierig: so etwas suchte ich, und eigene Versuche brachten bisher nicht das gewĂŒnschte Ergebnis. Allerdings ist diese Routine ziemlich âtrickyâ programmiert, und es ist auch nur möglich, nach links zu scrollen.
Das Trickreiche an der Routine ist, daĂ, um Ăbergabeparameter zu sparen, die den Aufruf ja auch verlangsamen, in einer Initialisierungsroutine die Adresse der Scroll-Routine geladen wurde, und mittels Offset die fĂŒr das Scrollen nötigen Parameter an die Stelle in der Scroll-Routine âgeschobenâ wurden, an der sie benötigt werden. Werden nun in der Scroll-Routine Zeilen geĂ€ndert, hinzugefĂŒgt oder gelöscht, kann sich der Offset Ă€ndern, was schöne AbstĂŒrze mit sich bringt. Genau die Probleme hatte ich bei dem Versuch, die Assembler-Routine in C einzubinden, da LASER an den Anfang jeder Funktion den LINK-Befehl des 68000 und eventuell den MOVEM-Befehl zum Registerretten setzt. Daher ĂŒberlegte ich mir eine elegantere und auch nicht langsamere Methode. Die zum Scrollen nötigen Variablen werden in einer Struktur zusammengefaĂt, und ein Zeiger darauf wird den Scroll-Routinen ĂŒbergeben. Dadurch entfĂ€llt eine weitere EinschrĂ€nkung; werden nĂ€mlich direkt in der Routine Werte geĂ€ndert, braucht man fĂŒr jeden zu scrollenden Block eine eigene Routine, bzw. es mĂŒĂte jedes Mal die Initialisierungsroutine aufgerufen werden, wenn ein anderer Block gescrollt werden soll, was nicht besonders zur Geschwindigkeits-Steigerung beitrĂ€gt.
Die Struktur namens SCROLLER besteht aus folgenden Komponenten: words enthĂ€lt die Wörter (16 Bit) pro Zeile, zeilen die Zeilenzahl, beide um 1 erniedrigt (spart im Assembler je eine Zeile), offset enthĂ€lt die Breite in Bytes, wird zur Korrektur des an der abzuarbeitenden Zeile langlaufenden Zeigers benötigt, adr ist die Adresse auf dem Monitor, pixel die Breite in Pixeln, wird nur fĂŒr Links-rechts-Scrollen benötigt.
Den Scroll-Routinen wird neben dem Zeiger auf die Struktur ein Wert (long warten) fĂŒr die Pause ĂŒbergeben, die nach jeder Zeile beim vertikalen bzw. nach jedem Pixel beim horizontalen Scrollen gemacht werden soll, die Zahl der Zeilen bzw. Pixel (int pixel), die pro Aufruf der Routine gescrollt werden sollen, und ein Flag fĂŒr die Richtung (int richtung, 1 = links/oben, 0 = rechts/unten). warten verlangsamt das Scrollen absolut, auch fĂŒr die anderen scrollenden Bereiche, wĂ€hrend pixel das Scrollen des einen relativ zu den anderen Ă€ndert. So kann z.B ein Block bei jedem Durchlauf 4 Pixel scrollen, wĂ€hrend die anderen nur 1 Pixel pro Durchlauf scrollen dĂŒrfen.
Die Routinen etwas nĂ€her betrachtet: scroll_lr(): Als erstes wird dem Compiler das Ăbertragen der Strukturkomponenten in die Register abgenommen! LASER hat nĂ€mlich die unangenehme Eigenschaft, vor jedem Zugriff auf eine Komponente die Adresse der Struktur in A0 zu laden und dann relativ zu adressieren, statt die Adresse nur einmal zu laden. Da die Scroll-Routinen nun etwas öfters aufgerufen werden, machen sich 3 zusĂ€tzliche MOVEA-Zeilen bemerkbar. Aber dafĂŒr gibt es ja den praktischen Inline-Assembler. Die erste Schleife zĂ€hlt die Zahl der Zeilen pro Durchgang herunter (die relative Geschwindigkeit), als nĂ€chstes kommt die Pause. Adresse und Zeilenzahl werden in die Register geladen, auf die Richtung geprĂŒft und zum entsprechenden Teil verzweigt. Das ganz linke bzw. rechte Wort wird mit dem roxr/roxl-Befehl gerollt und der Zeiger auf den Anfang bzw. das Ende der Zeile gesetzt, danach wird der Rest der Zeile wortweise gerollt.
scroll_ou(): Es werden 3 Zeiger verwendet, in Al liegt die Adresse der ersten Zeile (beim Hoch-Scrollen) bzw. der letzten Zeile, last_line zeigt auf die letzte bzw. erste Zeile. Dadurch lassen sich die erste und die letzte Zeile nach jedem Durchlauf (wenn also alle Zeilen 1 Zeile verschoben wurden) einfach tauschen, lauf ist ein Zeiger auf die jeweils aktuelle Zeile, die verschoben wird. Da A1 und last_line nach jedem Durchgang um Bytes verschoben sind, werden sie in den Variablen anfang und ende gebuffert, um Rechenzeit zu sparen. Vor jedem Durchgang (label z1) werden die Adressen neu geladen, es folgen wie beim horizontalen Scrollen die Zeilenschleife und die absolute Pause. Die wortweise Verschiebeschleife fĂŒr die Zeilen (wo bzw. wu) kann durch Verwendung eines festen Offsets sehr kurz ausfallen (lĂ€uft daher auch nur mit 640 Pixeln Breite). Nun wird der laufende Zeiger korrigiert und auf die nĂ€chste Zeile gesetzt. Nach Verschieben aller Zeilen wird noch die erste Zeile gegen die letzte getauscht (labels ll bzw. fl), das war es dann auch schon.
Die Funktion scrollinit() errechnet die fĂŒr das Scrollen nötigen Parameter, wobei fĂŒr die Richtungen links und oben der Zeiger auf den Anfang des Blocks, fĂŒr rechts und unten auf das Ende gesetzt wird. Zu beachten ist, daĂ das Flag, das die Richtung bestimmt, den gleichen Wert haben muĂ wie beim Aufruf der Scroll-Routinen, da sonst falsche Bereiche gescrollt werden. Es findet auch keine ĂberprĂŒfung auf negative Werte statt, die entstehen, wenn man (x2/y2) kleiner als (x1/y1) ĂŒbergibt, also Vorsicht!
Im Source sind an einigen Stellen Pfeile (==>), die deutlich machen, wo die Routinen an die Bildschirmbreite angepaĂt werden sollten, damit wenigstens monochrom auf gröĂeren Bildschirmen gescrollt werden kann.
Die Routinen sind so geschrieben, daĂ sie ohne globale Zugriffe auskommen, es mĂŒssen nur die beiden Strukturen vereinbart werden, was ja in einer Header-Datei geschehen kann. Extra compiliert braucht man das File dann nur noch hinzuzulinken. NatĂŒrlich sind die Routinen weniger geeignet, wenn es darum geht, den ganzen Bildschirm zu verschieben. Die wortweise Schleife ist dann einfach zu langsam. FĂŒr kleinere Bereiche (und dann auch gerne mehrere) lassen sich aber doch recht flĂŒssige Bewegungen erzielen.
Besondere Effekte erzielt man beim Ăbereinanderlegen von scrollenden Blöcken. Rechts + Oben = Diagonal, das ist ja wohl klar; ĂŒberkreuzen sich z.B. 2 Blöcke, die in die gleiche Richtung scrollen. wird der ĂŒberlappende Bereich auseinandergezogen, scrollen sie entgegengesetzt, scheint der Schnittpunkt stillzustehen. Weisen die beiden auch noch unterschiedliche relative Geschwindigkeiten auf, lĂ€uft der Schnittpunkt plötzlich spiegelverkehrt! Ein nettes Feld zum Experimentieren ergibt sich auch, wenn 2 senkrechte und 2 waagerechte âStĂ€beâ so eingestellt sind, daĂ die Pixel im Kreis (bzw. im Rechteck) laufen.
Eine âernsthaftereâ Anwendung ist z.B. eine Laufschrift, wie im Beispiel-Source. Vielleicht kommt ja mal jemand auf die Idee, wichtige Alert-Boxen mit scrollenden Stopschildem auszustatten? Die werden dann bestimmt nicht mehr ĂŒbersehen!
Bei dem Demoprogramm sind einige Tasten des Zehnerblocks belegt: â+â und â-â erhöhen bzw. erniedrigen die relative Geschwindigkeit der horizontalen Schrift, â*â und â/â die der vertikal scrollenden Schrift. â(â und ')â erhöhen/erniedrigen die absolute Geschwindigkeit (Pause), Enter setzt die Parameter zurĂŒck. Mit âESCâ wird das Programm verlassen.
/* SCROLL - ROUTINEN fĂŒr MEGAMAX LASER C */
/* von Ulrich Witte */
/* (c) 1991 MAXON Computer */
#include <osbind.h>
#include "scroll.h"
scroll_lr(scroll,warten,pixel,rxchtung)
int pixel;
register int richtung;
long warten;
SCROLLER *scroll;
{
register char *quelle;
register int zeilen;
register int words;
register long offset;
asm
{
movea.l scroll(A6),A0 ;Adr der Struktur
move.w (A0),words ;Vars aus Struktur in
;in Register schieben
move.l 2(A0),offset
move.w 6(A0),zeilen
movea.l 8(A0),quelle ;Adresse des Blocks
move.w pixel(A6),D2
subq.w #1,D2
anzahl: move.l warten(A6),D3
pause1: dbf D3,pause1 ;Pause
; Scrollgeschwxndxgkext absolut
movea.l quelle,A0
move.w zeilen,D1
cmpi.w #1,richtung ;TRUE = links herum
beq.s links1
rechts1:move.w -2(A0),D0
roxr.w #1,D0
move.w words,D0 ;Worte pro Zeile
suba.l offset,A0 ;Ptr auf Zeilenanf.
rechts2:roxr.w (A0)+ ;alle Bytes 1
;Pixel weiterrollen
dbf D0,rechts2
suba.l #80,A0 ;==> 1 Zeile höher
dbf D1,rechts1
dbf D2,anzahl
bra.s ende ;das war's schon
links1: move.w (A0),D0
roxl.w #1,D0
move.w words,D0
adda.l offset,A0
links2: roxl.w -(A0)
dbf D0,links2
adda.l #80,A0 ;=> nÀchste Zeile
dbf D1,links1
dbf D2,anzahl
ende:
}
}
scroll_ou(scroll,warten,anzahl,richtung)
int anzahl;
register int richtung;
long warten;
SCROLLER *scroll;
{
register int zeilen;
register int words,
register long offset;
register char *anfang,*ende,*lauf,*last_line;
asm
{
movea.l scroll(A6),A0 ;Adr. der Struktur
move.w (A0),words
move.l 2(A0),offset
move.w 6(A0),zeilen
movea.l 8(A0),A1 ;Adresse des Blocks
movea.l A1,last_line
move.w zeilen,D0 ;==> Zeilenzahl * 80
mulu.w #80,D0 ;==> anpassen
cmpi.w #1,richtung
beq.s add
suba.l D0,last_line ;Adr der 1. Zeile
bra.s cont
add: adda.l D0,last_line ;Adr der letzten
;Zeile fĂŒr Hochsrollen
cont: movea.l A1,anfang ;Anfangs-Adr buffern
movea.l last_line,ende ;last_line buffern
move.w anzahl(A6),D3 ;GesamtdurchlÀufe
subq.w #1,03
z1: move.w zeilen,D1 ;1. Schleife
;GesamtdurchlÀufe
movea.l anfang,lauf ;Laufptr auf Anfang
movea.l anfang,A1 ;Adr. der ersten
;und letzten Zeile laden
movea.l ende,last_line
z: move.l warten(A6),D0 ;2. Schleife
;zeilenweise
pause: dbf D0,pause ;Pause...
move.w words,D2 ;Worte pro Zeile
cmpi.w #1,richtung
beq.s wo ; nach unten.
wu: move.w -82(lauf),-(lauf) ;==> 3.Schleife
;Jede Zeile wortweise eine Zeile tiefer schieben
dbf D2,wu
adda.l offset,lauf
suba.l #80,lauf ;==> nÀchste Zeile
dbf D1,z
move.w words,D2 ;letzte Zeile in erste
ll: move.w -(A1),-(last_line)
dbf D2,ll
dbf D3,zl
bra.s end ;fertig
;nach oben:
wo: move.w 80(lauf),(lauf)+ ;==>
;jede Zeile wortweise eine Zeile hoher schieben
dbf D2,wo
suba.l offset,lauf
adda.l #80,lauf ;==> nÀchste Zeile
dbf D1,z
move.w words,D2
fl: move.w (A1)+,(last_line)+
;erste Zeile in die letzte Zeile
dbf D2,fl
dbf D3,zl
end:
}
}
scrollinit(k,scroll,links_oder_oben)
register SCROLLER *scroll;
register RECT *k;
int links_oder_oben;
{
register int help;
/* x-Koordinaten geteilt durch 16 ergibt */
/* Anzahl der Worte pro Zeile */
/* Die x-Koordinaten der Struktur */
/* dĂŒrfen nicht geĂ€ndert werden */
register int x1 = k->x1 » 4;
register int x2 = k->x2 » 4;
/* Monitor-Adresse holen */
scroll->adr = (char *)Physbase();
/* Differenz merken */
help = x2 - x1;
if (links_oder_oben)
/* ==> Adresse auf linke obere Ecke setzen */
scroll->adr += (x1 «1) + (k->y1 * 80);
else
/* ==> oder rechts unten als Adresse eintragen */
scroll->adr += (x2 « 1) + ((k->y2 - 1) * 80);
scroll->words = help - 1;
/* Bytes = Worte * 2 */
scroll->offset = (long)(help « 1);
/* Seilen - 1 */
scroll->zeilen = k->y2 - k->y1 - 1;
/* Pixelzahl = Differenz * 16 */
scroll->pixel = help « 4,
}
/* Header fĂŒr Scrollroutinen */
/* Struktur fĂŒr die 4 Eckpunkte des zu */
/* scrollenden Bereichs, wird */
/* fĂŒr die Initialisierung der Parameter */
/* der SCROLLER-Struktur benötigt */
typedef struct
{
int x1;
int y1;
int x2;
int y2;
}RECT;
/* Struktur, die zum Scrollen */
/* nötigen Parameter enthalt */
typedef struct
{
int words; /* Wortbreite des Bereichs */
long offset /* Breite in Bytes */
int zeilen; /* Höhe in Pixelzeilen */
char *adr; /* Adresse auf dem Monitor */
int pixel; /* Breite in Pixel,wird fĂŒr */
/* Links-Rechts benötigt */
}SCROLLER;
/* Demo-Programm fĂŒr die Scrollroutinen */
/* von Ulrich Witte */
/* (c) 1991 MAXON Computer */
#include <osbind.h>
#include "scroll.h"
int contrl[12]; /* Globale VDI-Variablen */
int intin[256], ptsin[256];
int intout [256], ptsout[256];
SCROLLER s[10];
main()
{
int taste;
/* Flag fĂŒr Richtung */
int links_oder_oben = 1;
/* Scrollgeschwindigkeit relativ , zu */
/* scrollende Pixel pro Durchlauf */
int x = 1,y = 1;
/* Scrollgeschwindigkeit absolut, */
/* Pause pro Durchlauf */
long pause = 0L;
/* Nummer der Workstation */
int handle;
/* GEM Bescheid sagen, daĂ wir da sind */
appl_init();
/* VDI-Handle besorgen */
handle = open_workstation();
/* schnell die Maus ausmachen */
asm {dc.w 0xa00a}
/* und den Bildschirm sÀubern */
v_clrwk(handle);
/* ein bisschen Text und Grafik */
mach_was_auf_den_bildschirm(handle);
scroll_bereiche_festlegen(links_oder_oben);
while ((taste = Crawio(0xff) & 0xff ) != 27)
{ /* solange nicht ESC gedrĂŒckt */
switch (taste)
{
case 32: /* Space */
links_oder_oben ^= 1;
/* Flag umdrehen, neue Initialisierung */
scroll_bereiche_festlegen(links_oder_oben);
break;
case 0x2b: /* '+' auf Zehnertastatur */
if (++x >= 10)
x = 10;
break;
case 0x2d: /* '-' */
if (--x <= 1)
x = 1;
break;
case 0x2a: /* '*' */
if (++y >= 10)
y = 10;
break;
case 0x2f; /* '/' */
if (â-y <= 1)
y = 1;
break;
case 0x28: /* '(' */
if (++pause >= 1000L)
pause = 1000L;
break;
case 0x29: /* ')' */
if (â-pause <= 0L)
pause = 0L;
break;
case 0xd: /* 'Enter' */
/* Parameter zurucksetzen */
pause = 0L;
x = y = 1;
break;
}
/* scroll_lr(&s[3],pause,x,!links_oder_oben);*/
scroll_ou(&s[7],pause,y,links_oder_oben);
scroll_lr(&s[0],pause,x,links_oder_oben);
/* scroll_ou(&s[4],pause,y,!links_oder_oben);*/
scroll_lr(&s[2],pause,1,!links_oder_oben);
/* scroll_ou(&s[6],pause,1,!links_oder_oben);*/
/* Diagonal */
scroll_lr(&s[1],pause,1,!links_oder_oben);
scroll_ou(&s[5],pause,1,links_oder_oben);
/* scroll_ou(&s[8],pause,1,1);*/
}
asm {dc.w 0xa009} /* Maus wieder an */
y_clsvwk(handle); /* Workstation schlieĂen */
appl_exit(); /* Bei GEM abmelden */
Pterm0(); /* und raus; nur nötig, wenn in */
/* der INIT-C des LASER der Sprung zur Exit- */
/* Routine entfernt wird (spart ca 3 Kb !) */
}
open_workstation()
{
register int x;
int work_in[11],work_out[57],handle,dummy;
/* work_in Array fur GEM initialisieren */
for(x=0; x < 10; work_in[x++] = 1);
work_in[10] = 2;
handle = graf_handle(&dummy,&dummy,&dummy,&dummy);
v_opnvwk(work_in, &handle, work_out);
return handle;
}
mach_was_auf_den_bildschirm(handle)
register int handle,
{
int dummy, rect[4];
vst_height(handle,26,&dummy,&dummy,&dummy,&dummy);
v_gtext(handle, 128,100,"LEA.L _LAUFSCHRIFT,A0");
vst_height(handle,13,&dummy,&dummy,&dummy,&dummy);
v_gtext(handle,100, 150, "Dieser String scrollt in die andere Richtung ");
vswr_mode(handle,2);
vsf_color(handle, 1); /* Farbe schwarz */
vsf_interior(handle,3); /* Strichmuster */
vsf_style(handle,8);
rect_set(rect,159,199,208,380);
v_bar(handle,rect);
vsf_interior(handle, 2) ; /* Punktmuster */
vsf_style(handle,4);
rect_set(rect,319,299,560,330);
v_bar(handle,rect);
v_gtext(handle, 330,320,"Z W E I BOXEN, die Scrollen!");
vst_rotation(handle, 900);
vst_height(handle,6,&dummy,&dummy,&dummy,&dummy);
/* v_gtext(handle,40,350,"Das kommt doch auch nicht schlecht, oder?");*/
}
scroll_bereiche_festlegen(links_oder_oben)
register int links_oder_oben;
{
RECT koor;
/* 2 Bereiche mit gleichen Koordinaten */
/* erlauben Diagonal Scrolling, wenn */
/* beide nacheinander gescrollt werden */
rect_set(&koor,128,73,500,106);
scrollinit(&koor,&s[0],links_oder_oben);
scrollinit(&koor,&s[4],!links_oder_oben);
rect_set(&koor,320,300,560,330);
scrollinit(&koor,&s[1],!links_oder_oben);
scrollinit(&koor,&s[5],links_oder_oben);
rect_set(&koor,160,200,208,380);
scrollinit(&koor,&s[2],!links_oder_oben);
scrollinit(&koor,&s[6],!links_oder_oben);
rect_set(&koor,100,133,474,154);
scrollinit(&koor,&s[3],!links_oder_oben);
scrollinit(&koor,&s[7],links_oder_oben);
rect_set(&koor,32,20,48,350);
scrollinit(&koor,&s[8],links_oder_oben);
}