ASSEMBLERKURS - LEKTION 5 In dieser Lektion werden wir den horizontalen und vertikalen Scroll von Bildern behandeln, weiters noch einige andere Spezialeffekte. Beginnen wir mit dem horizontalen Scroll (Bild links oder rechts schieben): der Amiga besitzt ein spezielles Register, das für diese Aufgabe zugeschnitten ist, das BPLCON1 ($dff102), das ein Bild jeweils um ein Pixel nach links oder rechts verschieben kann, bis zu einem Maximum von 15 Pixel. Das wird durch den Copper erreicht, indem der Datentransfer der Bitplanes um ein Pixel oder mehr verspätet. Die geraden und die ungeraden Bitplanes können auch separat voneinander verschoben werden. Die ungeraden Bitplanes werden PLAYFIELD1 genannt (1,3,5), die geraden PLAYFIELD2 (2,4,6). Das $dff102 ist ein Word lang, und in zwei Bytes unterteilt: das hochwertige, also das links ($xx00), das aus den Bits von 15 bis 8 besteht. Es wird nicht verwendet, es muß auf 0 gelassen werden. Das niederwertige Byte ($00xx) kontrolliert den Scroll: $dff102, BPLCON1 - Bit Plane Control Register 1 BITS NAME-FUNKTION 15 - X 14 - X 13 - X 12 - X 11 - X 10 - X 09 - X 08 - X 07 - PF2H3\ 06 - PF2H2 \4 Bit zum Scroll der GERADEN PLANES 05 - PF2H1 / (Playfield 2) 04 - PF2H0/ 03 - PF1H3\ 02 - PF1H2 \4 Bit zum Scroll der UNGERADEN PLANES 01 - PF1H1 / (Playfield 1) 00 - PF1H0/ Auf dieses Word muß man ähnlich zugreifen wie bei den Farbregistern: während bei den Farbregistern auf drei Komponenten agiert werden muß, also auf RGB, so wird hier nur auf zwei zugegriffen, die jeweils von $0 bis $f gehen, wie in etwa Grün und Blau des $dff180 (COLOR0): dc.w $102,$00xy ; BPLCON1 - wobei: X Scroll GERADE BITPLANES Y Scroll UNGERADE BITPLANES Einige Beispiele: (für die Copperlist) dc.w $102,$0000 ; BPLCON1 - Scroll NULL, normale Position dc.w $102,$0011 ; BPLCON1 - Scroll = 1 in beiden Playfield, ; ich bewege also das ganze Bild dc.w $102,$0055 ; BPLCON1 - Scroll = 5 für das ganze Bild dc.w $102,$00FF ; "" Scroll auf Maximalwert (15) für das ganze Bild dc.w $102,$0030 ; "" Scroll = 3 nur für gerade Bitplanes dc.w $102,$00b0 ; "" Scroll = $B nur für ungerade Bitplanes dc.w $102,$003e ; "" Scroll = 3 für die geraden Bitplanes, $e ; für die ungeraden Bitplanes Nicht Leichteres! Einfach den Wert des Scrolls bei jedem Frame wechseln, und man hat einen tollen Scroll des ganzen Bildschirmes mit einem MOVE erzeugt!! Ladet Listing5a.s um in der Praxis zu sehen, was passiert. In diesem Beispiel wird das BPLCON1 - $dff102 - am Anfang der Copperlist verändert, deswegen bewegt sich das ganze Bild. Es ist auch möglich, mehrere $dff102 in verschiedenen Zeilen der Copperlist zu verwenden, kombiniert mit einigen Waits. Was daraus entstehen kann, seht ihr in Listing 5b.s Dort werden zwei Scrolls verwendet, die die Schriftzüge "COMMODORE" und "AMIGA" unabhängig verschieben. Wird ein $dff102 pro Zeile gegeben, dann erzielt man die allbekannten Welleneffekte der Bilder. Schauen wir uns nun den vertikalen Scroll an. Die einfachste Art ist jene, einfach höher oder tiefen in die Bitplane-Pointers in der Copperlist zu pointen. Somit erscheint das Bild höher oder tiefer. Stellen wir uns vor, wir sehen das Bild durch ein quadratisches Loch, einer Art Fenster (Monitor): --------------- | | 1 | | 2 | AMIGA | 3 | | 4 | | 5 --------------- In diesem Fall sehen wir die Schrift AMIGA in der Mitte des Fensters, und wir haben die Bitplane-Pointers auf 1 zeigen lassen, also dort, wo der Bildschirm beginnt. Da er also bei Zeile 1 anfängt, steht AMIGA auf Zeile 3. Wenn wir nun aber auf 2 pointen, was passiert dann?? --------------- | | 2 | AMIGA | 3 | | 4 | | 5 | | 6 --------------- Das passiert: AMIGA "steigt" um eine Zeile, weil das Fenster (Monitor) sinkt, oder anders ausgedrückt, der Pointer um eine Zeile tiefer angesetzt ist. Da Bewegungen relativ sind, wird ein Baum, den wir aus dem Zugfenster vorbeiziehen sehen, sich in Wirklichkeit ja nicht bewegen, diejenigen, die das tun sind wir im Zug. Hier geschieht das Gleiche. Aber wieviel müssen wir dazuzählen, um ein Bild rauf- oder runterzuscrollen? Um wieviel müssen die Bitplanepointer erhöht bzw. erniedrigt werden? Die Bytes einer Zeile! Also 40, wenn das Pic in LOW RES 320x200 ist, oder 80 für ein Bild in HIGH RES 640x256. Schauen wir uns das Beispiel an: 1234567890 .......... ....++.... ...+..+... ...++++... ...+..+... ...+..+... .......... Wir haben ein hypotetisches Bitplane mit 10 Bytes pro Zeile, das jeweils auf 0 (.) oder 1 (+) sein kann. Hier wird ein "A" dargestellt. Um dieses A nach oben zu scrollen, werden wir eine Zeile "tiefer" pointen, also 10 Bytes weiter unten. Um tiefer zu zeigen, müssen 10 BYTES DAZUGEZÄHLT werden (ADD.L #10,Pointer): 1234567890 ....++.... ...+..+... ...++++... ...+..+... ...+..+... .......... .......... Auf die gleiche Art und Weise, um die Pic zum sinken zu bringen, werden wir eine Zeile weiter oben beginnen, sie zu zeichnen, also 10 Bytes weniger in der Adresse. (SUB.L #10,Pointer): 1234567890 .......... .......... ....++.... ...+..+... ...++++... ...+..+... ...+..+... Um das in der Praxis zu erreichen, müssen wir uns erinnern, daß die Pointer in der Copperlist die Adresse der Planes beinhalten (und wir dann dementsprechned ändern), und diese Adresse in zwei Words aufgeteilt ist. Dieses Problem ist recht einfach zu bewältigen, wenn man einige kleine Änderungen in der Routine zum Anpointen der Bitplanes anbringt. Wir müssen die Adresse der Bitplanes aus der Copperlist "holen" (umgekehrte Operation), 40 dazu- oder wegzählen (für den Scroll, 80 bei High-Res...), und dann diese neue Adresse wieder in die Copperlist einsetzen. Für diesen letzten Schritt kann auch die alte Routine dienen. Schaut euch Listing5c.s an, dort wird dieses System verwendet. Nun ladet Listing5d.s, in dem die beiden Routinen für vertikalen und horizontalen Scroll gleichzeitig laufen. In Listing5d2.s werdet ihr eine weitere Anwendung des Horizontalscrolls zusammen mit dem $dff102 (BPLCON1) finden, die Verzerrung während einer Bewegung. Nun werden wir die wichtigsten Register für Video-Spezialeffekte des Amigas kennenlernen, und zwar die Modulo: $dff108 und $dff10a (BPL1MOD und BPL2MOD). Es gibt zwei Modulo-Register, damit sie unabhängig für gerade und ungerade Planes geändert werden könne. Um auf unserem Bild mit 3 Bitplanes operierne zu können, müssen wir beide Register zu Verwendung ziehen. Ihr werdet bemerkt haben, daß bei einem Bild in LowRes 320x256 der Beam alle 40 Bytes eine neue Zeile nimmt, die Daten selbst aber alle hintereinander stehen. Genauso geht der Beam alle 80 in die nächste Zeile, wenn es sich um eine Pic in High-Res handelt. In der Tat wird dieses "Modul" automatisch zugewiesen, wenn das $dff100 (BPLCON0) gesetzt wird: wird Low Res ausgewählt, dann weiß der Copper, daß eine Zeile 40 Bytes lang ist, er nimmt also alle 40 Bytes eine neue. Er beginnt also links oben am Bildschirm, liest 40 Bytes und malt sie mit dem Beam hinauf. Die erste Zeile steht. Dann beginnt das Spiel von vorne, aber eine Zeile tiefer. Die nächsten 40 Bytes kommen an die Reihe. Und so weiter, bis alle 256 Zeilen durch sind. Im Speicher aber liegen diese Daten klarerweise alle nacheinander, da gibt´s kein quadratisches Bild! Der Speicher ist wie eine Schnur, auf der die Bytes wie Perlen aufgereiht sind. Keine quadratischen Felder mit Bitplanes etc. Das stellen wir uns nur so vor. Stellt euch vor, ihr zerlegt die einzelnen Zeilen des Bildes und reiht dann alle 256 aneinander: genau so sieht´s aus! Wenn wir nun das Modulo auf 0 lassen, dann verändern wir nichts, und es bleiben die Werte, wie sie sich der Copper vorstellt: alle 40 bzw. 80 Bytes eine neue Zeile. Den Wert, den wir in das Modulo geben, wird zu den Bitplanepointers am ENDE DER ZEILE DAZUGEZÄHLT, also wenn wir die 40 Byte erreicht haben. Somit können wir Bytes "überspringen", die nicht angezeigt werden. Wenn wir z.B. 40 an jedes Ende dazurechnen, dann wird nach jeder Zeile eine weitere übersprungen, es wird also eine alle zwei Zeilen angezeigt: - NORMALES BILD - .................... ; am Ende dieser Zeile überspringe ich 40 Bytes .........+.......... ........+++......... ; und zeige diese Zeile an, dann "springe" ich... .......+++++........ ......+++++++....... ; und zeige diese Zeile an, dann "springe" ich... .......+++++........ ........+++......... ; und zeige diese Zeile an, dann "springe" ich... .........+.......... .................... ; und zeige diese Zeile an, dann "springe" ich... Das Ergebnis wird eine Anzeige jeder zweiten Zeile sein: - BILD Modulo 40 - .................... ; am Ende dieser Zeile überspringe ich 40 Bytes ........+++......... ; und zeige diese Zeile an, dann "springe" ich... ......+++++++....... ; und zeige diese Zeile an, dann "springe" ich... ........+++......... ; und zeige diese Zeile an, dann "springe" ich... .................... ; und zeige diese Zeile an, dann "springe" ich... .................... .................... .................... .................... Das Bild wird zerquetscht erscheinen, nur die Hälfte lang. Unter anderem werden wir auch Bytes "unter" unserem Bild anzeigen, da der Bildschirm ja immer bei Zeile 256 endet: praktisch werden immer nur 256 Zeilen angezeigt, aber da wir nur jede zweite anzeigen, wird die Gesamtzahl auf 512 Zeilen kommen. Ladet nochmal Listing2b.s und modifiziert die Modulo in der Copperlist: dc.w $108,40 ; Bpl1Mod dc.w $10a,40 ; Bpl2Mod Ihr wedet bemerken, daß das Bild wie erwartet nur die Hälfte so groß ist, und der untere Teil des Bildschirmes mit Bitplanes gefüllt ist, die "übrig" sind: es wird das zweiten Bitplane unter dem Ersten angezeigt, das Dritte unter dem Zweiten, während nach dem dritten Bitplane der Speicher angezeigt wird, wie er unter dem letzten Bitplane ist. Es werden einfach mehrere angezeigt. Probiert zwei Zeilen zu überspringen, indem ihr 80 Bytes überspringt und 40 anzeigt,...: dc.w $108,40*2 ; Bpl1Mod dc.w $10a,40*2 ; Bpl2Mod Die Pic ist nochmal halbiert worden, und darunter werden weitere Bytes erscheinen. Ihr werdet eine Halbierung der Länge des Bildes alle Modulo 40*x feststellen. Wenn ihr ein Modulo wählt, das nicht 40 ist, dann werdet ihr eine Art "fransen" verursachen, denn der Copper wird die Zeilen nicht mehr ab deren Anfang darstellen, sondern ab einem Punkt, der von Zeile zu Zeile verschieden sein wird. Haut euch Listing5e.s rein, um eine schnelle Routine zu sehen, die 40 zum Modulo dazuzählt, um die Pic zu halbieren. Die Moduli können außer positiv auch negativ sein. In diesem Fall wird die Zahl am Ende der angezeigten Zeile abgezogen. Somit können komische Effekte erzielt werden: stellt euch vor, ihr setzt das Modulo auf -40. Der Copper wird also 40 Bytes lesen, die erste Zeile, sie anzeigen, dann 40 Bytes zurückgehe, und nochmal dieselben anzeigen. Er wird also über die ersten 40 Bytes nie darüber hinaus kommen. Wenn z.B. die erste Zeile vollständig schwarz ist, dann werden alle folgendne Zeilen auch schwarz werden, weil sie die erste Zeile "kopieren". Wenn ein Punkt inmitten der ersten Zeile war, dann werden alle Zeilen diesen Punkt haben: ..........+........ ; Zeile 1 (immer neu gezeichnet: ..........+........ ; Zeile 2 Modulo -40!) ..........+........ ; Zeile 3 ..........+........ ; Zeile 4 ..........+........ ; Zeile 5 ..........+........ ; Zeile 6 ..........+........ ; Zeile 7 ..........+........ ; Zeile 8 ..........+........ ; Zeile 9 ..........+........ ; Zeile 10 So wird jede Farbe eine Art von "Schmelzeffekt" erzeugen, der bis zum Ende des Screens reichen wird. Dieser Effekt wurde viel in Spielen wie Full Contact, dem Demomaker von Red-Sector und vielen anderen Programmen eingesetzt. Sehen wir uns an, wie er in der Praxis funktioniert. Listing5f.s. Sehr eindrucksvoll und einfach zu erstellen, stimmt´s oder hab ich recht? Er wird auch FLOOD-Effekt genannt. Das Modulo wird am Ende jeder Zeile zu den Bitplane-Pointers dazugezählt, diese "wandern" im Speicher herum, um das ganze Bild anzuzeigen. Wir addieren also eine negative Zahl, das einer Subtraktion entspricht. In diesem gegebenen Fall werden die Pointer nach dem Transfer einer jeden Zeile mit dem Wert X+40 bzw. mit X-40 geladen, und starten wieder ab diesem Wert X. +---->->->--------+ | | |BPL POINTER= X+ 0......................................39 | | | |ANFANG ZEILE-+---xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx---+- LETZTES BYTE -> | (X) | | | | (X+39) | +---+ +---+ | | | NÄCHST. Z.-+----xxxx[...] ^ | | | +-X+ 40 (Der Pointer ist nach dem Transfer die ganze Länge einer ^ | Zeile durchgegangen (40 Bytes), und ist am 40sten | | stehengeblieben, das im Grunde nichts anderes ist als ^ | das erste Byte der nächsten Zeile). | +-> (Hier wird dem Pointer eines jeden Plane ein Wert | | ADDIERT, in unserem Fall "-40") | +-> X=X+(-40) => X=X-40 => X=0 >-+ | | | +----------<-<-<--+------------<-<-<---------------+ Gesehen? Grade dann, wenn der Pointer am Ende angekommen ist, zählen wir ihm 40 Bytes weg, und er muß von vorne beginnen. Er zeigt die gleiche Zeile nochmals an. Wir haben in Listing5f.s auch den "Spiegel-Effekt" gesehen, also Modulo -80. Schauen wir ihn uns alleine in Listing5g.s an. Nun sehen wir, wie die Verwendung von vielen $dff102 (BPLCON1) nacheinander in der Copperlist einen Welleneffekt erzeugen können: ladet Listing5h.s. Sehen wir uns nun eine spezielle Verwendung des Scrolls mit den Bitplanes an: Listing5i.s ist ein sogenannter GRAPHIC-SCANNER, ein Vorfahre der GFX-RIPPER, also der Programme, die Bilder aus dem Speicher "stehlen". Dieses kurze Programm dient dazu, den Inhat der Chip-Ram anzuzeigen, mit allen sichtbaren Bildern, die sie enthält. Noch ein Beispiel mit den Modulo in Listing5l.s, diemal um ein Bild in die Länge zu ziehen, anstatt es zu kürzen. In Listing5m.s sehen wir eine andere Methode, um Bilder nach oben oder nach unten zu verschieben, diesmal durch verändern von DIWSTART ($dff08e). Die Register DIWSTART und DIWSTOP bestimmen den Anfang und das Ende des "VideoFensters", also dem rechteckigen Teil des Bildschirmes, der angezeigt wird. DIWSTART enthält die YYXX-Koordinaten der linken oberen Ecke dieses "Fensters", DIWSTOP die Koordinaten res rechten, unteren: DIWSTART o---------------- | | | | | | | | | | ----------------o DIWSTOP In diesen Registern kann man aber nicht alle beliebigen Werte einsetzen, denn XX und YY sind Bytes, und bekanntlich könne Bytes nur 256 verschiedene Werte darstellen ($00-$FF). Schauen wir also, wo wir das Video-Fenster mit DiwStart beginnen und es mit DiwStop beenden können. dc.w $8e,$2c81 ; DiwStrt YY=$2c, XX=$81 dc.w $90,$2cc1 ; DiwStop YY=$2c(+$ff), XX=$c1(+$ff) Das normale Videofenster hat diese Werte als Standart für DIWSTART und DIWSTOP; die vertikale Position YY funktioniert genau so wie beim Wait des Copper: wenn wir mit dem Copper eine Zeile über $2c abwarten, und dort Farbverläufe herstellen, dann werden sie nicht sichtbar sein, weil sie zu hoch oben liegen. Das Gleiche gilt für Waits nach der Zeile $FF, die dann bei $00 wieder starten werden, also $FF+1. Der Bildschirm beginnt bei $2c und endet bei $2c nach $FF. Dadurch werden wie erwartet insgesamt 256 Zeilen angezeigt. Für einen Bildschirm, der nur 200 Zeilen hoch ist, müßten wir folgendes DIWSTOP setzen: dc.w $90,$f4c1 ; DiwStop YY=$2c(+$ff), XX=$f4 In der Tat ist $f4-$2c = 200. Wenn wir $00, $01... setzen, werden wir die Zeile nach $FF meinen. Die Limits sind folgende: das DiwStart kann sich vertikal zwischen $00 und $FF bewegen, also bis zur Zeile 200. Das Video-Fenster kann also nicht ab Zeile 201 oder mehr starten, immer früher. Für das DiwStop haben sich die Ingeneure eine Strategie ausgedacht: wenn der Wert unter $80 (128) ist, dann warte Zeile $FF ab, $2c bezieht sich also auf $2c+$FF, also Zeile 256. Wenn die Zahl größer als $80 ist, dann wird sie so wie sie ist genommen (auch weil es keine Zeile $80+$ff=383 gibt!), und es wird wirklich die Zeile 129, 130, etc. abgewartet. DiwStart kann also bis maximal Zeile $FF gehen, bei NULL startend, das DiwStop hingegen kann Zeile $FF überschreiten und über das Limit des Bildschirmes hinausgehen, es kann aber nicht unter Zeile $80 gehen. Dieser Trick wurde angewandt, indem die Zahlen mit Bit 7 auf NULL (also bis $80) so angesehen hat, also ob sie ein hypotetisches Bit 8 gesetzt hätten ( die Zahlen nach $80 haben es gesetzt), das dann alles um $FF erhöht. Ist dieses Bit aber nicht gesetzt, dann wird unser hypotetisches Geisterbit gelöscht und die Zahlen werden genommen wir sie kommen. Was die horizontalen Zeilen angeht, sie können jeden belienigen Wert zwischen $00 und $FF annehmen, also bis zur Position 256 (erinnert euch aber, daß der Bildschirm bei $81 und nicht bei $00 beginnt, also bei 126!). DiwStop hingegen interpretiert ein $00 als ein 127, und das geht so weiter bis zum rechten Rand, denn es hat das "Geisterbit" immer auf 1, es weren also immer $FF zu seinem XX-Wert dazugezählt. Schlußendlich kann man sagen, daß das DiwStart sich in jeder Position XX und YY positionieren kann, mit jedem Wert zwischen $00 und $FF. DiwStop hingegen kann sich horizontal nach der Zeile $FF, vertikal von der Zeile $80 bis $FF positionieren, danach starten die Zeilen wieder bei $00, $01 usw., wie beim Wait nach $FF, deswegen ist ein $2c eigentlich $2c+$FF. In Listing5m2.s, wird dieses Argument behandelt. Als Abschluß von LEKTION5 ladet Listing5n.s, das eine Zusammenfassung der vorherigen Listings ist, und dazu spielt es auch noch ein Lied. Einmal dieses Listing verstanden, bleibt euch nichts anderes übrig, als LEKTION6.TXT zu laden!