Navigation
« 

Anonymous




Register
Login
« 
« 

Amiga Future

« 

Community

« 

Knowledge

« 

Last Magazine

The Amiga Future 167 was released on the March 5th.

The Amiga Future 167 was released on the March 5th.
The Amiga Future 167 was released on the March 5th.

The Amiga Future 167 was released on the March 5th.
More informations

« 

Service

« 

Search




Advanced search

Unanswered topics
Active topics
« 

Social Media

Twitter Amigafuture Facebook Amigafuture RSS-Feed [german] Amigafuture RSS-Feed [english] Instagram YouTube Patreon
« 

Advertisement

Amazon

Patreon

« 

Partnerlinks

Assembler Kurs

Description: von Jeff Kandle

Categories: [DE] Workshops

Link to this article: Select all

[url=https://amigafuture.de/app.php/kb/viewarticle?a=3409&sid=5cc340442ca77d2943c4aada5d533f77]Artikeldatenbank - Assembler Kurs[/url]

A S S E M B L E R - K U R S (c) Jeff Kandle 1990

Einleitung....

So, leute

jetzt geht`s rund hier. Es ist der Erste Assemblerkurs der zugibt das man
mit ihm auf keinen Fall `Gut` programmieren lernt, allenfalls lernt man
durch geschicktes umgehen der Betriebssystemroutinen unheimlich schnell zu
werden.
Er ist einzig und allein dazu geschrieben, damit die Leute die ihn lesen, nach
dem Kurs im Stande sind
eine Intro oder sogar ein Gutes Demo zu schreiben. Denn das ist es was die
meisten erstmal koennen wollen...was danach kommt kann ja jeder selber
entscheiden.

Auch werdet ihr nicht stundenlang BefehlsTabellen lesen muessen, oder sogar
auswendig lernen muessen. Vielmehr, ihr werdet ja noch nicht mal alle
Assembler-Befehle kennenlernen, eben halt nur die wichtigsten (glaubt mir
die reichen schon)
Falls einer jedoch an den Befehlen interessiert ist, soll er mir nur
schreiben, ich habe ein paar brauchbare listen hier rumzufliegen.

Da ich die Kursteile ja nacheinander schreibe, bin ich in der lage,
ausfuehrlich auf bestimmte sachen einzugehen. Ich halte nicht viel von
pers. briefen an mich, wie diesen...Schreib mir doch bitte mal ein kleines
Intro oder so...das koennt ihr mit sicherheit selber, wenn ihr
einigermassen intelligent seit, und etwas lust mitbringt.

Nun, allerdings will ich mich nicht in erklaerungen fuer dieses und jenes
programm verstricken, deshalb werde ich erstmal eine liste von den Sachen
abschiessen, mit denen ich waehrend dieses Kurses arbeite. Also waere es
guenstig wenn ihr euch ebenfalls diese Tools besorgt. Wenn es auch leute
gibt die ein Modem haben, und dazu noch in der Gluecklichen lage sind
eingetragene User der Titanic zu sein, muesst ihr mir einfach da nur einen
brief schreiben und ihr kriegt zugriff auf ein brett, indem der ganze
Klamauk liegt....also hier erstmal die liste vom noetigsten....

Seka V3.2
Dpaint 1,2 oder 3 (3.21)
Soundtracker 2.3 oder 2.5
Noiseripper V1.0
Kefrens Iff Converter
Bytekiller (dazu spaeter mehr)

So, den meisten krempel kennt ihr ja schon, oder ihr wisst bestimmt wie man
da dran kommt.

Naja, eigentlich ist es ja mehr ein kurs zum Intro schreiben, den ich zeige
auch sachen wie das packen oder konvertieren. Viele Kurse nehmen das als
Selbstverstaendlich hin, oder gehen gar nicht in dieses Thema ein.
Auch braucht ihr keinen Super-Duper getunten Amiga zu diesem Kurs, da ich
selber auch nur mit 1 MB und drei Laufwerken arbeite, also auch keine
Astro-aufgeruestete Maschine...
Ausserdem ist es bei der Programmierung sowieso Aegerlich wenn man da 2.3
MB hat und man sie nicht ausnutzen kann, den so ein Intro muss auch auf
`nem normalen Amiga laufen. Es ist allerdings mit 1 MB besser zu
realisieren, also waere ein Megabyte schon guenstig, ich werde allerdings
auch auf die `Kleinen` eingehen, falls es Probleme bei einer Sachen geben
koennte...

Ich werde auch fuer die Minderbegabten im Zeichnen oder Komponieren 1 oder
2 Bilder und Soundtracker module hierher schicken.
Allerdings erst wenn das dran ist. Auch um eine Abspielroutine (was`n das)
braucht ihr euch nicht zu kuemmern, ich werde die demnaechst irgendwann
liefern.

Tja, was gibt es noch zu sagen (ich weiss ich rede soviel, aber ich finde
das gehoert dazu, schliesslich haben wir ja auch eine menge vor)...achja,
ihr braucht bei diesem Kurs nicht Tage oder Wochenlang vor der Kiste zu
sitzen, denn das mache ich auch nicht. Eine Stunde am Tag reicht voellig
aus. Am besten waere es ihr habt auch einen Drucker, oder einen Freund der
einen Drucker hat, oder einen Freund der einen Kennt dessen Bruder einen
Drucker hat, denn dann koenntet ihr euch das ausdrucken lassen, was ich mir
hier so zusammen schreibsele. Und wenn ihr das dann unter euer Kopfkissen
legt, und darauf eine Nacht schlaft, was meint ihr was...Quatsch, aber ich
werde im Laufe des kurses immer wieder auf die Intern Buecher von Data
Becker zurueckkommen, dort ist es zwar nicht unbedingt besser erklaert,
aber die haben die ganzen adressen die man so braucht, schoen kompakt auf 5
bis 6 seiten. Allerdings werde ich auch immer bezeichnung und wert einer
Adresse oder eines Wichtigen registers nennen, wenn ich damit arbeite, wenn
ihr euch dann noch mit einem Edding bewaffnet, und euch die sachen Markiert
dann koennt ihr euch schon nach kurzer zeit die Einzelnen Adressen, aus
vorherigen kursteilen ziehen, und euch vielleicht eine kleine Kartei
anlegen, wo dann der wichtigste Krempel drin steht.

Ja, ansonsten braucht ihr ausser ein paar leerer disketten eigentlich
nichts mehr, und deshalb koennte ich jetzt loslegen mit dem Kurs...

In 273 Tagen zum Programmierer werden....

Ach Quatsch, ich weiss nicht wielange der Kurs geht, aber solange bestimmt
nicht, aber es ist die genaue Zahl meiner Zeit in Amiga Assembler...

Auch wenn der Kurs etwas anders als die anderen sein soll, ich weiss nicht
ob er es wird, kann ich doch nicht um einige Themen rum. Obwohl ich nicht
glaube das ihr ohne Jegliche unterstuetzende Literatur, oder ohne Sonstige
sachen, wie zum Beispiel andere Assembler-kurse (wehe, wenn ich das
erfahre) ins `rennen` geht, muss ich als erstes eine Frage beantworten, die
da lautet....

1) Was ist eigentlich Maschinensprache ?

Ja, das ist leicht erklaert, naehmlich einen Haufen Transistoren die
entweder leiten oder nicht leiten, also bloss zwei zustaende. Wie ihr die
nennt ist Egal...ob Schwarz oder Weiss...Heiss oder kalt..An oder Aus,
alles was gegensaetzlich ist stimmt. Der einfachheit halber bleiben wir
aber bei 1101001010010100101

Das war Maschinensprache...wenn ihr mich jetzt fragt was das heisst dann
wuesste ich es nicht, denn dieses Wissen uebernimmt fuer mich der Assembler,
ich meine diesmal das Programm. Ich gebe ihm, eine fuer ihn (und
hoffentlich bald auch fuer euch) sinnvolle kombination von Woertern und
zahlen ein, der er dann Uebersetzt und im Speicher zusammenbaut
(Assembliert).
Diese Kombination aus Zahlen und worten, stellen ein Sinnfaelliges Wort
dar, welches in etwa die Funktion beschreibt die die darauss entstehende Bit
kombination, ich meine dieses 1101001001, dann bei aufruf und abarbeitung
durchfuehrt wie zum beispiel

Move fuer Moven oder zu deutsch schieben (schiebe wert von nach)

oder

Add oder Sub fuer addieren oder Subtrahieren

noch eins

Cmp fuer Compare oder vergleichen.

Diese Worte, man nennt sie Uebrigens Mnemonics (ich weiss - Scheisswort)
ergeben meisstens in irgendwelchen Listings ein ausfuehbares programm.
Alleine hingegen, sind die Befehle, bis auf ein paar ausnahmen nicht in der
lage irgendetwas sinnvolles zu bewirken. Die meisten Sachen in Assembler
laufen sowieso durch vergleichen, kopieren und nochmals vergleichen ab,
deshalb kommen die meisten befehle die wir lernen werden auch aus diesem
gebiet.

Wie der Prozessor das Ganze verwaltet ist zu schwierig um es euch zu
erklaeren (um ehrlich zu sein weiss ich es selber nicht so ganz genau) aber
das wuerde auch den rahmen eine Kurses Sprengen. Wichtig ist nur das ihr
wisst Wie der Prozessor was und warum macht, und wie man ihm das sagt.

Nur halt soviel.....
Der Prozessor hat ein Register in sich in der die Augenblickliche Adresse
befindet an der der Prozessor knabbert. Wenn wir ein Programm starten,
machen wir nichts anderes als wie dem Prozessor einfach eine neue Adresse
dahin zu schreiben, wenn er dann denn Aktuellen Befehlabgearbeitet hat,
nimmt er sich die Adresse und macht da weiter. Das ist genauso als wenn ich
ein Aufgezogenes Spielzeug Auto das an mir vorbeifaehrt, hochhebe, die
Raeder Blockiere, und es irgendwo anders wieder hinsetze und die raeder
loslasse...
Dieses angesprochene Register ist der sogenannte Prozessor Counter (PC)
Wichtig wird es erst, wenn man damit programmiert, aber erstmal lassen wir
die Finger davon, also wenn ich mich mal vertippen sollte, und da steht was
von `Schreiben wir den Wert nach A7, dann macht da eine A6 daraus, denn der
PC befindet sich eben in diesem Register.
Bei anderen Prozessoren war dieses wichtige register immer gut verstckt,
bei Amiga bzw. bei MC 68000 oder MC 68010 ist es halt einfacher.

Der Prozessor schnappt sich also nach der Abarbeitung eines Befehls den PC
und holt sich das erste Word aus dem Speicher....

Haeh, word...eben hat der doch was von Zahlen gesagt...
Klar um nicht von Halbbyte, zwei bytes und vierbytes reden zu muessen,
wurden die Synonyme Nibble, Byte, Word, Longword erfunden, sie heissen im
einzelnen.

Nibble =
Vier Bits, stellt werte von 0 bis 15 oder von -8 bis +7 dar

Byte =
Acht Bits, stellt werte von 0 bis 255 oder von -128 bis +127 dar

Word =
Zwei Bytes, stellt werte von 0 bis 65535 oder von -32768 bis +32767 dar

Longword =
Vier Bytes, stellt werte von 0 bis 4294967295 oder von
-2147483648 bis +2147483647 dar

So jetzt wisst ihr dass schon...

Also weiter im Text...wie gesagt, er holt sich das naechste wort aus dem
Speicher, da alle befehle des MC 68000 ein wort lang sind reicht das
erstmal. Nachdem er erkannt hat was es fuer ein Befehl ist weiss er ob er
noch etwas, und wenn wieviel er aus dem Speicher holen muss damit der
Befehl richtig laeuft. Manche Befehle brauchen auch keine weiteren Daten,
die sind natuerlich sehr schnell.
Natuerlich, wird bei jedem weiteren Element was er sich holt auch der PC
weitergesetzt, waere dies nicht der falls wuerde es sehr schnell zum Guru
Fuehren, den Obwohl es unheimlich viele Kombinationen gibt die fuer den
Prozessor ein brauchbare Kombination abgeben, gibt es immer wieder welche
die das Teil halt unheimlich ins Husten bringt weil er damit nicht anfangen
kann. In vielen Faellen merkt er allerdings vorher einen Kleinen
Hustenreiz, dann weiss er das das was er sich da geholt hat nicht gut war,
und gibt dann die Guru-Meditation aus. Aber manchmal schafft er noch nicht
mal mehr das, und dann weiss man noch nicht mal was man da Falsch gemacht
hat.
Naja, da wollen wir ja nicht hoffen das euch das oft passiert, aber es
passiert halt immer wieder....mir taeglich.

Nun noch mal zu den Zahlensystemen mit denen Ihr zutun bekommt..
Diese Zahlen die ich da oben genannt habe, sehen mehr oder weniger Wild
ausgesucht aus, das kommt daher das sie dezimal System dargestellt sind, da
man aber auf einem Computer mit einen Zahlen System rechnet das 16 als
basis hat sind es halt dezimal so schraege zahlen, Hexadezimal heisst das
16 ner System, und da sehen die werte so aus

Nibble $0 bis $f
Byte $00 bis $ff
Word $0000 bis $ffff
und
Longword $00000000 bis $ffffffff

Ha, das sieht doch viel einfacher aus...
Es waere also Gunestig wenn ihr einen Taschenrechner habt der Hexadezimal
umrechnen kann. Da ich davon ausgehe das alle Computerfreaks auch einen
Fimmel fuer taschenrechner haben, schaetze ich mal das so ein ding doch
aufzutreiben ist.
Falls nicht, der Seka nimmt alle werte auch dezimal an, und kann auch um
rechnen.

So, das wars es fuers erste...noch nichts zum lernen dabei, naja ein
bisschen, aber lest es euch trotzdem gut durch.....

Es ist eigentlich nicht der erste oder soundsovielte teil, sondern der 1
abschnitt, weil ich immer soviel tippe wie ich lust habe.

O.K....weiter mit den Zahlen.
Wenn euch das mit dem -128 und +127 gewundert hat, wundert mich das nicht,
denn ich hatte auch etwas probleme das zu verstehen.
Also, es verhaelt sich bei der Zahlenverwaltung der Computer so das, man
den Bereich in dem man rechnet vorher auswaehlt. Das heisst, wenn ich eine
schleife mit 8000 durchlaeufen programmieren will, kann ich kein Byte als
Zaehler nehmen, da es nur bis 255 Zaehlen kann. Ich muss schon ein Wort
reservieren. Genauso verhaelt es sich umgekehrt, warum soll ich bei einer
Schleife mit 6 durchlaeufen gleich ein Langwort reservieren, das ist doch
platzverschwendung.
Nun aber zum Pudels des kerns des Problems...Wenn ein Wort in einer Adresse
dauernd um eins weiter gezaehlt wird, dann erreicht es irgendwann 65535.
Das ist der letzte darstellbare wert den das wort darstellen kann. Wenn ich
jetzt noch eins dazuzaehle, dann laeuft das wort sozusagen ueber. Der
Uebertrag wird aber vernachlaessigt, und faellt links raus. Also steht
wieder null in dem Wort. Also ist es irgendwie endlos mit den Zahlen, und
da hat man sich gedacht, `Machen wir es wie ein Wanderer` der um die Erde
spaziert. Er faengt irgendwo an zu gehen (tolles deutsch), und entfernt
sich immer weiter von dem Punkt an dem er gestartet war...aber irgendwann
hat er den punkt erreicht der genau unter dem liegt von dem er aus
gestartet war. Wenn er nun weiter geht kommt er unweigerlich dem Punkt
wieder naeher...und irgendwann steht er wieder dort. Da ihr die ihr das
lest das nicht ausprobieren koennen muesst ihr mir das schon glauben.

Also, so ist es auch bei den Zahlen, wenn man die haelfte ueberschritten
hat kommt man der null immer wieder naeher. Um das auch sichtbar zu machen
zaehlt man von 0 bis zur Haelfte positiv also 1, 2, 3 usw. und ab der
haelfte so bei 127 schlaegt man dann noch eins darauf um nennt es dann -128
und man zaehlt jetzt -127, -126.......
und wenn ihr das jetzt zusammen zaehlt 127 + -128 dann kommt da -1 bei
raus. Und -1 ist dasselbe wie 255, wobei wir dann wieder bei dem Byte
waeren. Womit auch bewiesen waere das man mit einem Byte nur bis 255
zaehlen kann. Trotzdem kann man mit einem Byte 256 Werte darstellen, denn
die 0 ist eine Vollwertige zahl, findet euch damit ab.

So da ihr jetzt ungefaehr wisst wie das mit dem Zaehlen auf dem Compi geht,
koennen wir so langsam anfangen etwas in richtung Assembler zu lernen.
Dazu gehen wir in das naechste Kapitel

2. Die Ersten Assembler Befehle (Mnemonics)

Tja, bevor wir allerdings damit anfangen muesst ihr noch wissen womit und
wie man das eingibt, damit auch irgendetwas passiert, denn auf die Backe
Taetowieren hat so glaube ich nicht viel sinn, habe es zwar nocht nicht
ausprobiert, aber ich gehe mal nicht davon aus.
Also, schnappen wir uns den Seka. Starten, und wie folgt vorgehen.
Wenn ihr die gepachte version benutzt, sie heisst V3.2 dann fragt er
erstmal was ihr fuer einen Arbeitsspeicher benutzen wollt...

Ihr habt drei moeglichkeiten, die ich mal kurz erklaeren will.

(C)hip = Chipram, sind die ersten 512 Kbyte im Amiga, also die
Grundkonfiguration, wenn ihr keine Speicher erweiterung habt
dann solltet ihr das eingeben.

(F)ast = Alles was rein oder dran gesteckt wird, zaehlt als Fastram.
Allerdings wurde ich davon abraten, da der Amiga meisten alle
Sachen in Vorhandenes fastram legt, somit auch den Seka, und
den wuerdet ihr ggf. ueberschreiben und damit loeschen.

(A)llocate = Einen ganz betimmten bereich auswaehlen. (braucht man nie !)


Habt ihr euch entschieden C fuer Chiop zu druecken, dann fragt der
neugierige Seka dann noch wieviel Speicher ihr braucht bzw. haben wollt.
Wenn ihr mit `nur` 512 KB arbeitet solltet ihr nicht mehr als 150 eingeben,
obwohl fuer die normalen sachen schon 20 oder 30 reichen. Wenn ihr
allerdings `nen ganzen Megabyte habt koennt ihr bis dreihundert eingeben,
hat aber nicht viel sinn, da meistens hundert reichen.

Wenn ihr das alles eingegeben habt dann kann es losgehen, die weiteren
kommandos des Seka Assemblers lernt ihr wenn man sie braucht, so kann man
sich das besser merken.
Also der Modus in dem ihr euch jetzt befindet ist der kommando modus, indem
alle sachen die irgendwie mit programmieren mit dem Seka zu tun haben
eingeben kann, als da das laden und abspeichern von SourceCodes waeren.

Ein Teil von euch, hat warscheinlich noch keinen Schimmer von Assembler,
ich meine bis auf das was ich bisher geschrieben habe, und sich deshalb
auch noch nicht um irgendwelche SourceCode gekuemmert, also muessen wir
erstmal etwas lauffaehiges schreiben damit wir es abspeicher koennen.

Also schreiben wir das Kuerzeste programm was es auf dem Amiga gibt,
naehmlich.....
Moment, erstmal muessen wir in den Editor, und zwar gelangen wir durch
druecken von Escape dort hin wo wir SourceCodes eingeben koennen.
Also wenn ihr Escape gedrueckt habt muesste der bildschirm sich jetzt in
zwei teile teilen. Mit dem Unteren habt ihr im Moment nichts mehr zu tun,
ihr befindet euch jetzt im Editor.
Der Editor gibt euch die zahlen vor die am anfang der zeile stehen, also
braucht ihr euch darum nicht zu kuemmern.

Das (End) was da steht kann man nicht loeschen, es ist wichtig, und liegt
automatisch am Ende des SourceCodes.

So, jetzt kommt dieses Kuerzeste lauffaehige programm. Und zwar muesst ihr
dazu dieses Woertchen eingeben.

rts

Es heisst ausgesprochen `Return from Subroutine`
zu deutsch `Kehre vom Unterprogramm zurueck`
Jetzt sage ich euch erstmal wie es lauffaehig gemacht wird, und dann was
passiert.

Also, ihr habt das jetzt eingegeben, drueckt nun wieder Escape um aus dem
Editor zu fluechten. Nun seit ihr wieder im Kommando Modus, und koennt das
programm jetzt Assemblieren.
Dazu muesst ihr `A` druecken und return. Jetzt fragt der Seka noch nach
options. Da muesst ihr im Prinzip auch return druecken, da die einzige
sache die man da eingeben kann und die fuer euch sinnvoll waere `VH` ist.
Damit koennt ihr dem Seka bei der Arbeit zugucken.

So, nachdem er es assembliert hat, und `No Errors` ausgegeben hat koennt
ihr das programm mit `J` starten.

Boooh, was ist das, nichts passiert...ganz klar das ist richtig.

Was ist passiert....Nach dem Start wird der PC wieder auf die neue Adresse
verbogen, das heisst dahin wo der Seka das Prograemmchen geschrieben hat.
Allerdings merkt sich der Prozessor von wo aus er gesprungen ist..das sieht
dann so aus....

Stellt euch vor das der Prozessor gerade mit dem Abareiten des J-befehls
des Seka zu tun hat...wenn er soweit ist, weiss er das er das programm was
er irgendwo hingeschrieben hat jetzt als unterroutine abarbeiten soll.
Dazu setzt er den PC auf die Adresse des Programms, allerdings merkt er
sich vorher die adresse wo er im Moment arbeitet

Sagen wir mal das J-befehls-programm liegt bei $10000, und das programm mit
dem RTS - befehl liegt bei $50000, beobachten wir den PC mal

. Normale bearbeitung von sachen PC
.
. Bearbeitung des J befehls $10000
. Jetzt kommt der aufruf des Prograemmchens
. der Prozessor merkt sich die position an der er im Moment arbeitet, indem
. den wert einfach auf einen Stapel legt wo er sich alle moeglichen sachen
. merkt, dieser Stapel ist auch wie ein Stapel, das was man zuletzt darauf
. gelegt hat bekommt man wieder. Also nachdem er sich die $10000 `gemerkt`
. hat schreibt er die $50000 in den PC, und arbeitet da weiter.
. Der Befehl der jetzt kommt macht das alles wieder rueckgaengig, den er
. sagt das der prozessor sich die oberste adresse von Stapel nehmen soll,
. und in den PC schreiben soll.
. Jetzt arbeitet der Prozessor am J-befehl weiter. Der nur noch die
. Kontrolle an den Seka zurueckgibt, weil er ebenfalls mit RTS endet.

Wie ihr seht tut dieses Kleine prograemmchen doch schon eine ganze menge.
Nur halt nichts das man sieht.
Trotzdem dieser Befehl `MUSS` hinter jedem programm stehen was zurueck
kehren soll. Sonst wuerde der Prozessor weiterarbeiten, und wie ihr schon
wisst, bei einer ungueltigen Bit-Kombination abstuerzen.

So ich hoffe das ihr das verstanden habt, den das war alles was man dazu
sagen kann.

Jetzt wollen wir mal etwas Programmieren, wobei man auch etwas sehen kann.
Es wird zwar nicht atemberaubendes, aber immerhin.

Vielleicht habt ihr schon bemerkt das der Seka nach abarbeitung eines
programms eine kleine tabelle ausgibt. Die ist eigentlich nur zur fehler
erkennung gedacht, allerdings gibt sie auch alle arbeitregister des
Prozessor`s aus, und die koennen wir natuerlich gut gebrauchen...
Was, ihr wisst nicht was Arbeitsregister sind, nun....achja hatte ich ja
noch nicht erklaert. Nun gut

Es gibt in einem Prozessor einen haufen von register der er so benutzt, und
wo wir nicht drankommen, allerdings gibt es beim MC 68000 16(15) stueck wo
wir mit arbeiten koennen, als da waeren

Datenregister D0 - D7
Adressregister A0 - A7

A7 ist ja wie ihr wisst nicht zu benutzen da der MC 68000 seinen PC da rein
schreibt.
Da diese register direkt im Prozessor sind koennt ihr euch vorstellen das
er damit viel schneller arbeiten kann als wie mit anderen.
Eine Muenze die in eurer Hosentasche steckt habt ihr auch schneller in der
Hand als wie eine die im Schrank liegt.

Weil wir damit arbeiten koennen, koennen wir diese Register auch direkt
benutzen, und uns das ergebnis, nach der wiederkehr ueber `RTS` in der
tabelle die der Seka ausgibt anschauen.

Also, loeschen wir den SourceCode im Speicher erstmal. Dazu gehen wir in
den kommando Modus, und tippe `KS` ein, was heissen soll Kill Source.
Nach einer sicherheits abfrage, die wir natuerlich mit `J` beantworten
loescht der Seka den Source mit dem `RTS`.
Gehen wir in den Editor (Escape)....

So jetzt kommt der maechtigste und meist benutzten Befehl der MC 68000
maschinesprache, er tritt auch auf sehr vielen anderen dialekten auf, also
kennt ihr den auch wenn ihr mal einen anderen Compi besitzen werdet.
Allerdings glaube ich nicht das er dort soviele Adressierungarten drauf hat
wie bei unserer Freundin.

Also, dieser tolle Befehl heisst schlicht und ergreifend Move, und das ist
auch genau das was er macht, er schiebt werte hin und her. Das ist an sich
nichts tolles, aber wenn ihr wisst auf wieviele arten er das kann, dann
werdet ihr schon merken wie toll dieser befehl ist.
Wir geben uns aber erstmal nur mit der einfachsten art und weise ab,
naemlich der Direkten.
Das heisst....Einen ganz bestimmten wert in eine ganz bestimmte Adresse
oder in ein ganz bestimmtes register zu schrieben.
Es ist die einfachste sache, und sieht so aus.....halt, erinnert euch was
ich gesagt habe, das wir vor jeder sache mit zahlen gucken muessen in
welchem bereich wir arbeiten, je nach zahl muessen wir das zeichen hinter
dem punkt veraendern. Und zwar in b, w oder l
Wenn ich den bereichviel groesserangebe nimmt uns das der 68000 nicht krum
aber bei unterschaetzung macht er manchmal zicken.

Move.w #$4000,$40000

wuerde das Word #$4000 nach der Adresse $40000 schreiben.
Wir wollen das aber etwas einfacher zum kontrollieren machen, und deshalb
nehmen wir einfach ein Datenregister, und zwar das mit der nummer null.

Move.w #$4000,d0

so, tippt das mal ein, und setzt noch ein RTS drunter.

Verlasst jetzt den Editor (Esc..) und assembliert das, ihr wisst ja wie.

So, jetzt koennt ihr das ding mit `J` starten.
Ha, kaum gedrueckt ist der auch schon wieder da, denn Maschinensprache ist
sehr schnell. Ich kann zwar nicht die genaue Zeit sagen die er dafuer
gebraucht hat aber ich gehe mal von 2 oder 3 Hunderttausendstel Sekunden
aus.
Schnell wa.

So wenn ihr euch jetzt die tabelle anguckt, findet ihr irgendwo `D0` stehen
und dahinter muesste dann eine 00004000 stehen,toll ne ?
So das kann man Naturlich mit allen registern und werten machen.
Jetzt kommt auch der beweis, fuer meine Aussage zum thema Zahlen.

Geht mal in den Editor und macht aus dem `w` hinter dem Move ein `l`.
Und loescht mit del die `4000` und schreibt da `ffffffff` hin.
Starten, und `D0` angucken...
Jetzt steht da auch dieses `ffffffff`.
Aber da man rueckwaerts und Negativ viel leichter an die `ffffffff` kommt
kann man auch die ff... loeschen und dort einfach `-1` hinschreiben.
Wenn ihr das jetzt startet kommt eben wieder dieses `ffffffff` raus.

Jetzt seht ihr auch wie wichtig die richtige bereichswahl ist.
Denn -1 im Bytebereich ist $ff oder 255, und -1 im Langwortbereich ist
nunmal $ffffffff oder 4294967295
Also, immer daran denken.

So leute, nachdem wir gemoved haben machen wir noch ein paar andere
spielereien in der Art.
Geben wir mal ein programm ein, das folgendes macht
`Einen Wert in ein register schreiben, einen anderen in ein zweites, und
die beiden addieren`
Wie muesste das aussehen ?
Zuerst mal ein Move-befehl mit dem Ziel D0, O.K

Move.w #$4000,d0

Die direkte Adressierung fuer einen Additions befehl wuerde jetzt verlangen
das wir einen direkten wert addieren.
Da es aber sehr langsam ist, und viel zu unflexibel werde wir erst einen
wert in ein register schreiben, und dann die beiden register miteinander
Addieren.

also...

Move.w #$2000,d1

So, der Additions befehl heisst wie ihr wisst Add, und ist sehr einfach zu
benutzen, naemlich so

Add.w d0,d1

Es ist mathematisch egal was ich zu was addiere, das ergebnis ist immer
dasselbe, das weiss man. Allerdings macht der MC 68000 einen Unterschied,
denn das Register zu dem dazu Addiert wird ist gleichzeitig auch das Ziel
der Operation. Also muesste das programm so aussehen

Move.w #$4000,d0
Move.w #$2000,d1
Add.w d0,d1
Rts

In der Tabelle die der Seka ausgibt koennt ihr ueberpruefen ob der MC 68000
auch richtig gerechnet hat, es muesste jetzt 00006000 in d1 stehen.

Naja, genug der spielerei...weitere einfache Befehle in dieser Ebene sind
halt Subtrahieren (Sub), Multiplizieren (Muls) und Divdieren (Divs)
Ihr koennt ja damit ein Bisschen rum Experimentieren. Aber das muesste
eigentlich jedem klar sein, der Rechnen kann.

Ich werde in diesem Kurs einen Grundstamm von befehlen erklaeren der fuer
Sichtbare Effekte wichtig ist, damit man nicht die Lust verliert, den
soviel man auch ueber hunderttausend moeglichkeiten erfaehrt etwas zu
kopieren, wenn man dabei nicht sieht, oder vorzeigen kann ist es schnell
langweilig.

Also machen wir bei etwas weiter was sich schwer anhoert, aber ziemlich
einfach ist.

3.Manipulation und Bedingte verzweigung durch die Bitebene

Hoert sich ja schwierig an, ist es aber nicht. Ein Computer arbeitet den
groessten Teil seiner Zeit mit bytes, words und langwords, er Kopiert,
addiert rotiert diese werte und macht alles moegliche. Es gibt aber auch
viele sachen die werden einfach nur angestellt oder nicht, da gibt es keine
grossen auswahlmoeglichkeite wie zB. bei der farbgebung wo man halt 4096
moeglichkeiten hat. Sondern da reicht es ob an oder aus, genau wie beim
Lichtschalter, entweder an oder aus.

Also Bits sind ja nichts anderes als diese Schalter. Im normalen
arbeitsspeicher des Amigas werden bits garnicht mehr so benutzt sondern
immer nur zusammen gefasst zum Byte oder Word. Aber die ganzen
zusaetzlichen Chip`s die so im Amiga arbeiten werden viel nur durch
Schalter gesteuert. Entweder ist der Bildschirm an oder aus, ich meine
damit nicht dem Schalter den du vor dir hast und der Vielleicht rot
leuchtet wie bei mir, sondern halt der Softwaremaessige an und ausschalter
den es im Amiga gibt, genauso verhaelt es sich mit Sprites, entweder ja
oder nein. Das waeren Beispiele fuer das Setzen oder das loeschen von Bits,
aber was ist mit der Abfrage, was sollte das fuer einen Sinn haben, und was
koennte das schon fuer einen Sinn, ob Sprites da sind sehe ich ja, oder ob
der Bildschirm an ist merke ich ja auch. Was koennte man also Abfragen.

Nun, ich schwoere das du die beiden im Moment wichtigsten schalter schon
benutzt hast, naemlich die Maustasten. Gut, ne ?
Es soll uns aber jetzt nur eine Interresieren, und zwar die Linke.

Dazu eine Kurze erklaerung....
Der ganze Computer hat mit der Maus direkt relativ wenig zu tun, sondern
nur ein Ganz bestimmter Chip, und zwar einer vom typ CIA, keine Bange es
ist kein Bewaffneter Ami, sondern nur ein Normaler Chip. Er heisst
richtig Computer Interface Adapter, was soviel bedeutet wie `interne
Schnittstelle zwischen Hard und Software`. Und ihr koennt ihn direkt ohne
Aufschrauben des Amigas beruehren, naemliche wenn ihr den Mausstecker zieht
und die Pins Beruehrt, den die gehen direkt in diesen Chip rein, der wertet
sie dann aus, und legt das ergebnis dann an seinen I/O port. Dort kann der
user das dann Abfragen, oder dem CIA anweisungen geben.
Also der Port des CIA`s der uns Interresiert liegt bei $BFE001, das solltet
ihr euch merken, man nennt es CIAapra.
Also wenn ihr Irgendwo dieses Wort lest dann wisst ihr welches es ist.

Naja, wo waren wir doch gleich, achja bei der Linken Maustaste.
Tja, die kann man halt bei Bit 6 im CIAapra abfragen.
Wie das erklaere ich jetzt, aber erstmal alle Befehle zur direkten
Bitmanipulation

Bset
Bclr
Bchg

und die Bitabfrage..

Btst

So, also Bset ist Bitsetzen

Bset #6,$40000 , wuerde Bit 6 in der adresse $40000 setzen

Bclr loescht das Bit.

Ein schoener Befehl ist auch noch der Bchg befehl, den er wechselt den
Momentanen zustand des Bits das man nennt,war es gesetzt wird es geloescht.

Ein Kleines Beispiel waere stark verwandt mit unserem Ziel mit der Maus,
denn Bit 1 des CIAapra registers ist die Power Led des Amiga. Abfragen hat
nicht viel Sinn, aber an und ausmachen koennen wir sie, wobei noch ein
Problem ist, und zwar das alle CIA-schalter die man setzen oder loeschen
kann, Lowaktiv sind, das heisst, wenn sie an sind, sind sie aus. Also wenn
die lampe des Amigas an ist, wie jetzt zum Beispiel bei mir, dann ist Bit 1
vom CIAapra geloescht, ist die lampe aus, zb. beim Guru-Blinken wird sie
gesetzt.

Wenn ihr also folgendes kurzprogramm eingebt, und es startet, mueste die
Lampe ausgehen.

Bset #1,$BFE001
rts

Wie ihr wisst, setzen des Bits ist eigentlich loeschen, somit geht die
lampe aus. Um sie wieder anzukriegen muesst ihr einfach das Bset in ein
Bclr umwandeln, starten, und schon geht sie an.
Das kann man aber schneller haben, indem man einfach den bchg-befehl
benutzt, denn der aendert halt immer nur das Bit, an aus an aus.

Schoene Spielerei, werden wir aber erst gleich weiter spielen, nachdem wir
die Maus und warteschleifen beherschen.

O.K weiter mit der Maus....
Wir Fragen ein Bit mit den Btst-befehl ab. Und was macht der ?
Nun er kopiert das Bit einfach in des Z-flag des Prozessors...

Ach, die Flags kennt ihr noch nicht..Nun in den Flags merkt sich der Prozessor
die eigenschaften der letzen operation, wie zum Beispiel, ob es null war,
oder ob das ergebnis im Negativen bereich war. Und das Z-Flag sagt eben aus
ob die operation Null war oder nicht.
Beim Btst befehl uebernimmt der Befehl das setzen des Bits wir muessen es
nur noch abfragen, naja das hoert sich jetzt leicht an aber es kommt nun
ein ganze menge auf euch zu, naemlich

4.Bedingtes abzweigen mittels Branchen.

So, wo fangen wir den jetzt an....najut erstmal die Abfrage abhandeln.
Die beiden befehle zum abfrage ob null oder nicht heisse

Bne und Beq

Bne (Branch if Not Equal) verzweigt wenn nicht null, und Beq (Branch if
Equal) verzweigt wenn null. Das ist klar, was dann passiert kommt spaeter.

Um euch das Branchen zu erklaeren muss ich nochmal zum normalen
Sprungbefehl zurueck kommen.
Wie war das noch, er verbiegt den PC auf eine Neue Adresse, und macht dann
da weiter. Beim branchen geschieht das auch, aber erst spaeter, denn er
muss den neuen wert erstmal ausrechnen, und das macht er anhand des wertes
der hinter den Branch befehl steht, der sagt naehmlich die Distanz zum
neuen ziel aus, diese distanz wird dann dazu oder abgerechnet, und dann
gehts weiter, hier mal ein prograemmchen welches das ungefaehr macht was
dann passiert.

Move.l Distanz,d0 ;Distanz nach d0
Move.l A7,d1 ;Adresse im PC nach d1
Add.l (Sub.l) d0,d1 ;Distanz wert Addieren oder Subtrahieren
Move.l d1,a7 ;Neuen PC schreiben (diesmal ist der zugriff
;erlaubt.

Tippt dieses programm nicht ab, denn es ruf einen absturz hervor.

Um das ausrechnen des distanzwertes brauchen wir uns nicht zu kuemmern,
denn das uebernimmt der Seka fuer uns (hoffentlich).
Wenn wir etwas mit einer solche bedingten verzweigung programmierung,
bedienen wir uns einer Guenstigen einrichtung, die manche schon vom
Amiga-basic her kennen, die Labels.
Das sind Stellen im SourceCode die durch freiwaehlbaere namen genannt
werden. Das sieht ungefaehr so aus....ich zeige es mal an einer Maustasten
abrfrage...


Wait: ;Label `wait`
Btst #6,$BFE001 ;Testet mausbit
bne.s wait ;Falls nicht null, war nicht gedrueckt,
;also wieder nach wait, um weiter zu
;warten.

Die Distanz die der Assembler berechnet, bildet sich aus der Anzahl der
byte die Zwischen dem Ende des bne-befehls und dem Anfang des Labels liegt.
Bei diesem Beispiel waere es...rechnen wir das mal aus.

1 word fuer die Distanz
1 word fuer den Bne befehl
1 langword fuer die $BFE001
1 word fuer die sechs
1 word fuer den Btst-befehl

gleich 12, da wir zurueck springen ist der wert -12. Klar ?

Wenn ein Branch befehl eingesetzt wird verzweigt er nur dann wenn seine
bedingung erfuellt ist, das heisst - der Bne befehl verzweigt nur wenn
nicht null, in unserem Beispiel also nur wenn die maustaste nicht gedrueckt
ist. Andernfalls macht er nach dem befehl weiter. um also eine lauffaehige
Maustasten abfrage zu programmieren, muessten wir nur noch ein RTS
dahitnersetzen. Das Ganze saehe dann so aus.

Wait: Btst #6,$BFE001
Bne wait
rts

Gebt das mal ein, und assembliert und startet das.
Scheisse nichts passiert, schon wieder nichts. Doch er wartet auf die
maustaste. Wird sie Gedrueckt, kommt er wieder. Toll, ne ?

So, damit sind wir bei ersten Programmwas sich lohnt abzuspeichern...
Wir gehen raus aus dem Editor und Tippen `W`
Nun fragt der Seka nach dem Filenamen, geben wir da mal WaitMaus ein.
Krrrkk, Krrrk...faedich, der Seka hat das jetzt abgespeichert, und an den
namen ein .S drangehaengt damit du auch Spaeter erkennst was SourceCode ist
und was nicht.
Denn wir koennen jetzt mal einen Cli-befehl aus dem ding machen, wie ? ganz
einfach.
Zur sicherheit Nochmal assemblieren, aber nicht starten.
Da wir uns im Kommando modus befinden, tippen wir `WO` fuer Write Object.
Nach dem Filenamen fragt der Seka natuerlich auch, und dann generiert er
ein laufaehiges programm auf der Diskette. ihr koennt es Natuerlich wieder
WaitMaus nennen den der SourceCode hat ein .S dran, beim ObjectCode macht
der Seka das nicht.
So um auszuprobieren was ihr jetzt schon gemacht habt, verlasst ihr jetzt
mal den Seka mit `!` und `Y`, und tippt im Cli den namen des eben
generierten Programms ein. Es passiert garnichts, nur das der Prompt nicht
wieder zurueck kommt, denn das Programm ist so kurz das noch nicht mal die
Drive-Led angeht. Drueckt ihr jetzt die Maus geht es weiter. Kopiert ihr
das jetzt noch in den C-ordner habt ihr einen Sinnvollen Cli Befehl
programmiert.

So, nachdem wir mal etwas sinnvolles gemacht haben, geht es mit neuem
schwung weiter..
Wollen wir mal bei periodisch ablaufenden vorgaengen weitermachen, und zwar
wollen wir mal die Powerled blinken lassen, solange bis die Maustaste
gedrueckt wird.
Wie man die Maustaste abfragt wissen wir ja schon, an und aus der Lampe
kennen wir ja schon...also

Ich zeige euch noch zwei sachen die man bei allen SourceCodes benutzen
sollte...


CIAapra = $BFE001 ; So kann man Labels auch benutzen
; Ueberall wo dieses CIAapra auftaucht
; setzt der Seka $BFE001 ein


Start: ; Label bei Start des programms, erklaere
; ich gleich !
Bchg #1,CIAapra ; Bit umdrehen
Btst #6,CIAapra ; Bit testen
Bne.s Start ; Nicht gedrueckt ? Von Vorn
Rts ; Raus ->

So die Funktion muesste eigentlich klar sein, nur das mit den Labels
vielleicht nicht.
Ein Definiertes Label hat immer ein Wert in sich, es kommt aber auf die
Definitionsart an. Die erste Defi... , dieses CIAapra = $BFE001 gibt dem
label CIAapra den Wert $BFE001, ueberall wo dieses Wort auftaucht setzt der
Assembler den Wert ein. Die andere Art und weise ist die mit dem
Doppelpunkt mitten im Listing. Dabei nimmt das label den Wert des PC an.
Dies ist sehr sinnvoll, 1.Weil wir uns die Woerter leichter merken koennen,
und zweitens weil wir nicht wissen wo der Seka das programm hinschreibt,
also koennten wir nie sagen wie die Adresse heisst, 3.Muessten wir jedesmal
die Differenz fuer den Bne befehl neu ausrechnen wenn wir noch etwas dazu
programmieren.
Also leute, benutzt Label wo es nur geht, bei dem einem wert kommt es noch
nicht sinnvoll vor, aber wartet mal bis wir anfangen Bilder anzuschalten
und so, da geht es ab mit Adressen, und da ist es besser man hat ein Wort
das man einprogrammieren lkann, was sich so anhoert wie das was wir machen
wollen.

Habt ihr das Programm eigentlich schon abgetippt und gestartet...nehh, dann
macht das mal.
Es gibt jetzt zwei moeglichkeiten, entweder seht ihr die lampe einwenig
oder noch weniger Flackern, ihr muesst aber genau hingucken, dann seht ihr
es.
Woran liegt das, das man das nicht sieht, hmmmmh. Erinnern wir uns mal
daran wie schnell der MC 68000 ist, und jetzt gucken wir uns mal das
listing an, und gehen es mal in gedanken durch ...lampe aus, maustest,
lampe an, maustest, lampe aus....
Tja, das ist alles, und das macht er natuerlich sehr schnell, also muessen
wir den Amiga bremsen..wie koennte man das machen. Wir Menschen benutzen
diese methode auch, wenn wir Musizieren, naehmlich die Takte, wenn wir zb.
im 1/4 takt spielen machen wir es so, Trommelschlag 2 3 4
Trommelschlag 2 3 4
Genau so machen wir es jetzt mit dem Amiga, wir lassen ihn zwischendurch
etwas Zaehlen.
Wie koennten wir das realisieren, nun den Move befehl kennen wir um einen
wert zu schreiben, wir koennten diesen wert einfach bis auf Null
runterzaehlen, und mit Bne das ende abfragen, ganz einfach machen wir das
doch....

CIAapra = $BFE001
Counter = $ffff

Start: Bchg #1,CIAapra

Move.l Counter,d0
Schleife: Sub.l #1,d0
Bne.s Schleife

Btst #6,CIAapra
Bne.s Start
Rts

So, das mit dem Zaehlen duerfte kein problemm sein, ihr kent ja alle
befehle, die benutzt werden.
Damit ihr nicht durch`s ganze listing fahren muesst, habe ich den Zaehl
wert im Label Counter unterbracht.
So, wenn ihr das jetzt startet koennt ihr mal ein Bisschen sehen wie
schnell der kleine kasten vor euch ist, er macht die lampe aus, zaehlt bis
65535 und macht sie dann wieder an, zaehlt bis 65535....

Schnell is er ja, ihr koennt jetzt ein bisschen mit dem Werten
rumprobieren, allerdings $ffffffff wuerde ich nicht eingeben, den es dauert
sehr lange...

Hmmmmh, warteschleifen kennt ihr jetzt...so, dann werde ich mit euch jetzt
etwas machen, was nicht so funktionieren wird wie man sich es denkt, und
dann werden wir uns dem 1.Special Chip im Amiga widmen. Ich weiss, es
scheint was zu frueh, aber glaubt mir, es lohnt sich das Teil von Anfang an
zu beherrschen, denn ohne den Copper ! geht nicht viel im Amiga, was mit
grafik und so zu tun hat....

Also wollen wir mal versuchen, die Bildschirm Farbe zu aendern..
Die Leute die das Intern-Buch I haben, haben es gut sie koennen jetzt die
seite mit den Registern aufschlagen, und nach Color00 suchen, die Leute die
das nicht haben, muessen mir Glauben das es bei $DFF180 liegt.

Um die Bildschirm farbe zu aendern, muessten wir Theoretisch nur einen
anderen wert als wie den der jetzt da drin steht.

Also....

Color00 = $DFF180

Start: Move.w #$0fb0,Color00 ;$0fb0 = farb wert fuer Gold
Rts

muesste dann eigentlich reichen.
Naja starten wir es doch mal....hmmmh, blitzt nur kurz auf der bildschirm..
Komisch was koennte das sein, muesen wir halt ein Endlos programm mit
Maustest schreiben...

Color00 = $DFF180
CIAapra = $BFE001

Start: Move.w #$0fb0,Color00
Btst #6,CIAapra
Bne.s Start
Rts

Tja, das Flackert immer noch, komisch...naja, um das zu erklaeren muss ich
etwas mehr ausholen, also

5. Der Coprozessor Copper

So, der Copper...nun wie soll ich anfangen..
Zuerst einmal ist es der Wichtigtse Chip im Amiga...ach Quatsch alle sind
wichtig, am besten ich sage was er macht.
Nun er setzt farben zum richtigen zeitpunkt, verwaltet die Bildschirme,
gibt Sprites ihr Positions, kurz er kann fast alles machen, was mit grafik
uns so zutun. Dass heisst er kann alle Register des Amigas benutzen, also
beschreiben, und da hat er einen Ganze menge moeglichkeiten, und dazu noch
zu ganz bestimmten festlegbaren zeiten.
Er kann zum beispiel ganz einfach, in der mitte des bildschirm eine andere
farbe darstellen, und zwei zeilen spaeter wieder auf die alte farben
umstellen, er kann unter gewissen umstaenden jeden Pixel auf dem
Bildschirm abfragen. Nun, wie er das macht, dazu geht es in die Struktur
des Amiga.
Die tollen Moeglichkeiten des Amigas ruehren daher das er nicht alles was
er macht, alleine machen muss, er kann viele Sachen von den SpecialChips um
ihn herum erledigen lassen, er muss ihnen nur noch sagen wo, und wann.
Dazu ist es aber noetig das sich die Chip der sogenannten DMA technik
bedienen, das heisst um effektiv arbeiten zu wollen muessen die einzelnen
Chips zugriff auf den Ramspeicher haben, dieses geht aber nur in gewissen
grenzen, naemlich im Chipram, dem Festeingebauten speicher den man von
anfang an hat. Das ist zwar eine einschraenkung, stoert uns aber weiter
nicht.
Beim Copper sieht es so aus, das er wenn der Bildschirm aufgebaut wird ein
ganz bestimmtes programm durch geht, in dem Steht was er wann zu machen
hat, wann der bildschirm schwarz wird, wann ein neuer bildschirm kommt.
zb. Das rauf und runterziehen der AmigaScreens waere ohne den copper nicht
moeglich, denn er ist sauschnell, das ruehrt daher das er nicht auf andere
warten muss, und das er nur drei befehle hat, von denen uns aber erstmal
nur zwei interressieren. Naemlich Move und Wait

Der Move befehl des Coppers ist verwandt mit dem des MC 68000, es gibt ihn
aber nur in einer Version, der Direkten.
Mit dem Wait befehl, kann man auf jede beliebige position auf dem
Bildschirm warten, um dann eine ganz bestimmte sache durchzufuehren.

Um dem Copper also fuer unsere zwecke arbeiten zu lassen, muessen wir im
ein solches programm schreiben, man nennt es Copperliste, und es im dann
nur noch uebergeben, dann wird er es abarbeiten. Wenn wir das programm
verlassen muessen wir ihm allerdings die alte copperliste die er benutzt
wenn er im CLI oder wo auch immer ist, zurueckgeben, da er sonst unsere
alte weiter abarbeitet, und wir wuerden nichts sehen.

Desweiteren muessen wir auch noch einen schritt gehen den die meisten
profisionellen Programmierer nur in absoluten Notfaellen gehen.
Naemlich, dem Amiga die Kontrolle entziehen, das heisst Multitasking
abschalten, und alle interrupts verbieten. das hat folgenden Sinn, der
Amiga Pfuscht uns jetzt nicht mehr rein, egal was wir jetzt machen, aber
auch diesen Zustand muessen wir bei verlassen wieder in den Ausgangs-
Zustand bringen.

Bei der Copperprogrammierung muessen wir aber auch auf Funktionen
Zurueckgreifen, die nur der Amiga selbst kann, das heisst, ein programm zu
schreiben das das Multitasking abschaltet wuerde unheimlich lange dauern,
und wuerde nur aufhalten, also warum das Rad neu erfinden, wenn der Amiga
schon eine Solche Routine schon da ist. Genauso verhaelt es sich mit der
Adresse 1.Copperliste, die der Amiga fuers CLI errechnet hat, die register
wo das drin steht koennen wir nicht auslesen, aber zum Glueck kann der
Amiga sich die auslesen, und merkt sich die an einer ganz bestimmten
stelle.

Das geheimnis sind die Library`s des Amiga. Jeder von euch hat bestimmt
schon mal davon gehoert, aber weis nicht ganz genau was es damit auf sich
hat.

Nun....

6.Oeffnen und benutzen von Library`s

Library`s sind zu deutsch Bibliotheken, und zwar voll von kleinen oder
groesseren programmen die der Amiga, so im Taeglichen alltag braucht.
Durch die offene Struktur des Amigas koennen wir diese tolle sachen
mitbenutzen. Tja, mit den tollen sachen, ist das aber eine sache, es gibt
fuer alles irgendwelchen routinen, aber sie sind alle in C der sprache des
Amigas programmiert, ich persoenlich habe nichts gegen C, aber es ist
einfach zu langsam, nicht fuer alles, aber sobald es um grafik und alles
was mit echtzeit zu tun hat da muss man es schnell vergessen.
Natuerlich funktionen die man nur einmal in Situation aufruft in denen es
nicht so auf zeit ankommt kann man die library`s ruhig benutzen.

Uns interressieren jetzt erstmal nur 2 library`s naemlich die Exec und die
Graphics library.

Um Libraryfunktionen zu benutzen muess man erstmal die library oeffen, dazu
dient eine Funktion der Exec-Library, naemlich Openlibrary, sie oeffnet die
Library die man will.
Moment, wie soll ich den daran kommen, wenn ich die Library`s erst oeffnen
muss, und der Oeffne -befehl in einer Library ist.
Das habe sich die Programmieren wohl auch ueberlegt, und deshalb ist die
Exec-library auch immer offen, deshalb ist der zugriif auf funktionen
daraus ziemlich einfach. Man muss nur wissen, wo die Position der funktion
in der library ist, und man muss wissen wo die Library liegt.
Also bei der Execbase ist es so das die Adresse in der Speicher stelle
$00000004 liegt. Zu dieser adresse muss man dann nur noch den `Offset` der
gewuenschten Funktion zaehlen, und schon kann man sie aufrufen. Ich
schreibe mal ein programm was Multitasking abschaltet, und dann wieder
anschaltet, die beiden Funktionen heissen Forbid und Permit

CIAapra = $BFE001
Forbid = -132 ;Offsets der funktionen
Permit = -138
Execbase = 4 ;in 4 liegt die Basis der Execlibrary


Start: Move.l Execbase,a6
Jsr Forbid(a6)
Wait: Btst #6,CIAapra
Bne.s Wait
Jsr Permit(a6)
Rts

So, dieses Programm schaltet erstmal das Multitasking aus, wartet auf die
maus, und schaltet es dann wieder an. Ihr braucht es nicht abzutippen, denn
man sieht sowieso nichts.
Natuerlich muss man manchen sachen auch noch etwas auf den Weg geben, damit
die funktion weiss was sie machen muss, zum beispiel die Openlibrary
funktion, sie muss ja wissen welche Library sie denn oeffnen soll...

So, O.K ich wollte euch ja erklaeren wie das mit dem Openlibrary geht.
Es ist eine Funktion wie Forbid oder Permit, nur unterscheidet man sie,
weil sie noch parameter zum arbeiten braucht, und zwar...

In D0 die Versionsnummer der Library. Es koennte im laufe der zeit einer
auf die Idee kommen, mal eine neue graphics.library zu schreiben,
natuerlich kann man dann die alte nicht einfach weglassen, sondern sie muss
weiter bestandteil des Amigas sein. Um sich nicht einen Neuen namen
einfallen lassen zu muessen, gibt man einfach eine hoehere Versionsnummer
an, und bekommt automatisch die Library die man will
gibt man keine versionsnummer an, sucht der Amiga automatisch die erste aus
und oeffnet sie.

In A1 die Adresse, des Namens der Library. Man hat die Library`s nicht
durchnummeriert weil ja immer wieder neue dazu kommen, deshalb gibt man ihr
den namen, des gebietes in dem sie Eingesetzt wird. Findet der befehl diese
Library nicht im Speicher dann sucht er im `LIBS` verzeichnis auf der Disk,
erst wenn sie da nicht ist, gibt er einen FehlerCode zurueck. Falls das
Oeffnen der Library erfolgreich war gibt der Befehl in D0 die Adresse
zurueck an der die Library jetzt Betriebsbereit liegt.
Diese Adresse, muessen wir uns merken, denn mit ihr muessen wir die
Funktionen die wir aufrufen, aufrufen.

Um z.b die Graphics.library zu oeffnen muesste dieses Programm geschrieben
werden.


Execbase = 4
Openlibrary = -552

Start: Move.l Execbase,a6 ; Execbase als grundstock der Execlibrary
Clr.l d0 ; Version Egal
Move.l #gfxname,a1 ; Adresse wo der name der zu oeffnenden
; Library steht nach a1, muss mit 0 enden
Jsr Openlibrary(a6) ; Oeffnen
Move.l d0,gfxbase ; Basis der Graphics-library merken.
Rts

Gfxname: dc.b "graphics.library",0
even
Gfxbase: dc.l 0

Puuuhh, eine Menge neues, naja, das mit dem Oeffnen der Library muesste
jetzt klar sein.....Also was haben wir neues. Erstmal der neue Assembler
befehl `Clr`, duerfte eigentlich klar sein, Clr heisst Clear. Er loescht
das Byte, Word oder Langword in der/dem angegebenen Adresse/Register, mehr
nicht.

So aber was da unter kommt ist Vielleicht etwas Schwieriger, aber sehr
wichtig. Wie ihr in der zeile wo ich a1 die adresse des names gebe, sage
ich #gfxname. Das heisst, die Adresse von dem Wort uebergeben. Der
Unterschied zu einem Label besteht darin, das dieses graphics.library
wirklich im Speicher steht, wollt ihr es sehen. Gut dann tippt das ab und
assembliert es. Dann geht ihr in den kommando modus, und tippt `q start`
und drueckt return, jetzt gibt der Seka einen Speicher auszug aus, und zwar
genau 128 bytes nach dem label start. da unser programm aber nicht so lang
ist koennen wir das wort graphics.library gut sehen. Alles O.K...

Was noch neu ist, ist das reservieren, von langwoertern oder so, fuer einen
ganz bestimmten Zweck. Bei meinem Beispiel wurde ein Langword reserviert,
damit ich da die Adresse der graphics.library hineinschreiben. So muss ich
kein kostbares register verschwenden, und da hat es einen sicheren platz.

Ihr muesst allerdings zwischen Seka- und Assembler befehlen unterscheiden

Dieses Dc.(b,w,l) ist ein Seka befehl, und tritt nicht im fertigen Programm
auf. Genauso verhaelt es sich mit dem `Even` zwischen Gfxname und Gfxbase.

Der Amiga kann keine Worter oder Langwoerter in Ungerade Adressen
schreiben. Im normalfall interrssiert uns das auch nicht, da alle
Befehle `Rund` sind. Aber der name `graphics.library` ist gerade, aber die
null hinten dran macht es ungerade. dem Seka waere es Egal, er reserviert
nur den Platz fuer ein langwort, da er aber weiss das der prozessor darauf
allergisch reagieren wird, gibt er die fehlermeldung `word at Odd adress`
aus. Dies verhindern wir indem wir mit dem Even befehl. ein fuellbyte
einschieben, das den PC wieder gerade biegt.

So alles verstanden...

Nun dann wollenb wir mal dazu kommen was wir eigentlich von der
graphicslibrary wollen.
Funktionen wollen wir keine benutzen, sondern nur einen Wert.
Der Amiga, so hatte ich euch schon erzaehlt merkt sich die Adresse der
Ersten copperliste die er fuer das CLI errechnet hat, in einem Stueck
speicher das vor den funktioen der graphicslibrary liegt, diese Tabelle ist
vor vielen Library`s, und wir koennen sie einfach auslesen, wir muessen nur
wissen was wir wollen, und wo das liegt.
Also, wir wollen uns ja die adresse der ersten Copperliste holen...

Und zwar steht dieser wert an der 38.sten stelle in dieser tabelle, da
diese tabelle am anfang der library steht, brauchen wir nur 38 zu dem wert
in d0 dazuzuzaehlen, und schon haben wir die adresse.

Also, das war doch nicht schwer, oder ?

O.k, jetzt kommt nur nur eins, bis wir Theoretisch schon eine Copperliste
Kreieren koennen....Wie sage ichs dem Copper das er eine andere liste
benutzen soll.
Grossartig sagen muss ich im da nichts, denn der Copper ist so vergesslich
das er sich jedesmal vor einem Bildschirmaufbau, die adresse der
Copperliste die er zu bearbeiten hat aus den Registern Cop1lch und Cop1lcl
holt.
Sie liegen bei $dff080 und $dff082, man kann sie aber gemeinsam
ansprechen, indem man einfach die adresse als langword nach $dff080
schreibt, er verteilt dann die Bits schon richtig.
Sobald er das naechste mal dieses Register ausliesst kriegt er unseren
Wert, und arbeitet Anstandslos unsere Liste ab, dull wa ?

Also, jetzt zum verstaendnis der letzen 100 zeilen mal ein kleines
prograemchen, ist zwar noch nicht lauffaehig, ist ja auch nur
anschauungsmodell, also nicht abtippen.

Execbase = 4
Openlibrary = -552
CIAapra = $BFE001
Cop1lc = $DFF080
Forbid = -132
Permit = -138

Start: Move.l Execbase,a6 ; Vorbereitungen fuer Forbid und
; Openlibrary.
Jsr Forbid(a6) ; Forbid
Clr.l d0 ; Versionsnummer Egal
Lea gfxname,a1 ; Name der zu oeffnenden Lib nach A1
Jsr Openlibrary(a6) ; Graphics.library oeffnen
Move.l d0,gfxbase ; Adresse der Lib merken

Move.l #$50000,Cop1lc ; Adresse der neuen Copperliste nach Cop1lc

Wait: Btst #6,CIAapra ; Kennt ihr schon
Bne.s wait

Move.l gfxbase,a0 ; Basisadresse nach a0
Move.l 38(a0),Cop1lc ; 38. sten wert einfach nach Cop1lc
; schreiben
Jsr Permit(a6) ; Multitasking erlauben
Clr.l d0 ; kein returncode
Rts

Gfxname: dc.b "graphics.library",0
Gfxbase: dc.l 0

Ha, die Programme wachsen so langsam, viel sinn haben sie zwar noch nicht,
aber das kommt doch.

Das einzige was jetzt dazu kam war der Lea-befehl, er macht das selbe wie
das was da vorher stand `Move.l #Gfxname,a1` nur halt kuerzer, denn er
weiss das man ein Langwort braucht um eine Adresse zu benutzen, also muss
man es nicht dahinter schreiben.

Achja, den Jsr-befehl kennt ihr auch nicht, er ist das gegenstueck zu Rts,
er merkt sich den aktuellen PC bevor er den neuen schreibt. Und da alle
library funktionen mit `Rts` enden, knn ich ihn gut benutzen.

So, da wir jetzt wissen wie das drum herum bei einer Coppeliste ist,
koennen wir eigentlich mit der genauen beschreibung der befehle, und ihrer
funktion, und wie man sie dem Copper gibt.

7.Erstellen von Copperliste

Eine kompletter befehl des Coppers besteht immer aus zwei woertern.
Beim Move-befehl sieht es so aus das zuerst das register kommt was
angesprochen werden soll, kommt. Und dann der Wert der da rein soll.

Da der Copper nur die Register des Amigas ansprechen kann, muss kein
komplettes langword als adresse benutzt werden es reicht der hintere teil.

Fuer uns liegt Color00 in $DFF180, fuer den Copper liegt es in $0180.

Also, ein Befehl der Copperliste der die farbe in Color00 auf gold setzt
saehe im speicher so aus

$01800fb0

Die erste vier nibbles geben an welches register, und die zweiten welcher
wert.

Etwas anders ist es beim Wait befehl, der hat ein richtiges befehls Wort,
naemlich $fffe. Er hat den wert den er bearbeitet vor sich stehen, wobei
das erste Byte die Zeile, und das Zweite Byte der pixel in der Zeile.

Der normale Wait befehl sieht also im Speicher so aus...

$8054fffe

Dieser Befehl wuerde bis zeile $80, und dem Pixel $54 warten und dann weiter
machen.

So, dann koennten wir ja jetzt etwas Copperlistenmaessiges auf die Beine
stellen.
Halt, eines noch...Das ende einer Copperliste markiert man mit $fffffffe.
es stellt einen Wait befehl dar, dessen zeile niemals erreicht werden kann,
weil es sie nicht gibt, also wartet der Copper unendlich. Solange er das
Signal kriegt das ihn veranlasst die liste von neuem zu durchlaufen.

Ich werde die Copperliste, auch wieder mit der reservierung funktion des
Seka`s im Listing unterbringen, so das ihr da nach herzenlust dran
rumprobieren koennt.....

Doch hier erstmal, das erste vernuenftige Listing..


Execbase = 4
Openlibrary = -552
CIAapra = $BFE001
Cop1lc = $DFF080
Forbid = -132
Permit = -138

Start: Move.l Execbase,a6 ; Vorbereitungen fuer Forbid und
; Openlibrary.
Jsr Forbid(a6) ; Forbid
Clr.l d0 ; Versionsnummer Egal
Lea gfxname,a1 ; Name der zu oeffnenden Lib nach A1
Jsr Openlibrary(a6) ; Graphics.library oeffnen
Move.l d0,gfxbase ; Adresse der Lib merken

Move.l #copperliste,Cop1lc ; Adresse der Copperliste nach Cop1lc

Wait: Btst #6,CIAapra ; Kennt ihr schon
Bne.s wait

Move.l gfxbase,a0 ; Basisadresse nach a0
Move.l 38(a0),Cop1lc ; 38. sten wert einfach nach Cop1lc
; schreiben
Jsr Permit(a6) ; Multitasking erlauben
Clr.l d0 ; kein returncode
Rts

Gfxname: dc.b "graphics.library",0
even
Gfxbase: dc.l 0
Copperliste:

dc.w $0180,$0000
dc.w $8001,$fffe
dc.w $0180,$0f00
dc.w $a00f,$fffe
dc.w $0180,$0000

copperlistenende:
dc.l $-2 ; ende der copperliste , -2 = $fffffffe

End:


Es waere besser wenn ihr euch das rausschneidet, mit dem Editor und dann
direkt in den Seka einladet, vergesst aber nicht das Suffix (.S)
dranzuhaengen, sonst koennt ihr es nicht mit `R` einladen.

So, zum listing...Eure eigenen ideen koennt ihr zwischen Copperliste und
copperlistenende einfuegen.
Dei $8001 bei dem Ersten Wait befehl habe ich gewaehlt damit ihr seht warum
man sie normal nicht nimmt, da diese $8001 schon am rechten rand erscheint,
um wirklich erst am Linken rand auf die neue farbe umzuschalten muesst ihr
als Pixel position immer 0f benutzen, so wie beim Zweiten Wait befehl, da
ist es ja $a00f...

Eigentlich muesste das jetzt klar sein, falls nicht kann es nicht viel
sein, also schreibt mir ruhig, ich habe immer ein offenes ohr...

Beim Naechsten mal malen wir ein Bild mit Dpaint, und bringen es auf den
Bildschirm, O.K ?

Ne, Leute ich hab mirs ueberlegt, wir arbeiten erstmal ein bisschen mit dem
Listing was wir schon haben. Wir widmen uns erst einmal einfachen effekten
mit farben und so. Wir lernen dabei auch erstmal ein paar tolle neue
Sachen, die man in Assembler machen kann. Kopierroutinen, einfachere
Zaehlroutinen, und wie man leichte schwingungen (sinusse) von Hand eingibt.

So, jetzt verbessern wir erstmal die Zaehlroutine die wir bei der Sache mit
der Powerled benutzt hatten, wir machen sie einfacher, und gestalten sie so
das man sie Wunderbar auch als Zaehlroutine fuer eine Kopieraktion benutzen
kann...erinnern wir uns, wie hatten wir die Erste Routine gemacht.

Move.w #$5000,d0
Loop: Sub.w #1,d0
Bne.s Loop
Rts

Ungefaehr so sah das doch aus oder...Jetzt habe ich aber einen befehl der
Das `Sub` und das `Bne` in sich vereinigt

Dbra oder Dbf

Dasselbe programm saehe so aus...

Move.w #$5000,d0
Loop: Dbf d0,loop
Rts

So, was macht der Dbf (Dbra)-befehl. Er zaehlt das hinter ihm angegebene
Rgister eins runter, und verzweigt immer wenn -1 noch nicht erreicht ist,
das ist der Unterschied zum `Bne` Befehl, denn der Verzweigt ja bekanntlich
bei 0.

So, jetzt zum Kopieren...das A und O der Intro und Demoschreiberei.
Allerdings muessen wir noch eine Adressierungsart des Move-befehl`s
kennenlernen, besser gesagt zwei...aber das seht ihr ja dann.

8.Indirekte Adressierung

Also, eine weitere schoene Sache ist die Indirekte Adressierung. Es sieht
so aus, das in der Quell oder Ziel Spalte, nicht die adresse direkt steht,
sondern nur wo die Adresse steht. Erstmal ein Beispiel mit dem Jmp befehl

Stellt euch vor bei $20000 sieht es so aus

00 02 a0 00

stellt euch weiter vor irgendwo im SourceCode steht eben jenes Mnemonic

...
...
...
Jmp ($20000)
...
...
...

Was passiert wenn der Prozessor auf diesen Befehl trifft. Er Analysiert den
Jmp Befehl, sieht das er Indirekt ist. Und holt sich den Wert aus der
Adresse die bei dem Jmp befehl steht, und legt das Langwort das an dieser
Adresse zu finden ist in den PC...den rest kennt ihr ja, vonwegen das er da
jetzt weiterarbeitet und so. Er wuerde also nach $2a000 springen.

Was das mit Kopieren zutun hat ist ganz einfach. Sehen wir uns mal ein
Routine an die mit unseren Mitteln geschrieben ist, und die 20 Langwoerter
ab $20000 nach $30000 Kopiert

Move.l $20000,$30000
Move.l $20004,$30004
Move.l $20008,$30008
Move.l $2000c,$3000c
Move.l $20010,$30010
Move.l $20014,$30014
Move.l $20018,$30018
Move.l $2001c,$3001c
Move.l $20020,$30020
Move.l $20024,$30024
Move.l $20028,$30028
Move.l $2002c,$3002c
Move.l $20030,$30030
Move.l $20034,$30034
Move.l $20038,$30038
Move.l $2003c,$3003c


Das geht ja an sich noch, und wenn es auf schnelligkeit ankommt, und es
wirklich `Nur` um 20 Langwoerter handelt kann man es so machen.
Aber was ist wenn man 128000 bytes Kopieren will, 32000 Zeilen Sourcecode
ist ein bisschen viel...Also nehmen wir mal die Vorzuege der indirekten
Adressierung, erst das beispiel mit kurzen Saetzen, und dann die ganze
sache peinlich genau erklaert, den jetzt ist es sehr wichtig, da die
indirekte Adressierung sehr wichtig.

Folgendes programm Kopiert ebenfalls 20 langwoerter von $20000 nach $30000

Start: Lea $20000,a0 ; Quell Adresse nach A0
Lea $30000,a1 ; Ziel Adresse nach A1
Move.w #19,d0 ; Laenge-1 nach d0, da Dbf bis -1 zaehlt
Loop: Move.l (a0),(a1) ; Kopiert wert von $20000 nach $30000,
; allerdings inidirekt, da ja in A0 und A1
; die Zeiger auf die stellen stehen.
Add.l #4,a0 ; Zeiger in A0 auf naechstes Langwort
; stellen.
Add.l #4,a1 ; A1 ebenfalls
Dbf d0,loop ; Schon 20 mal, nein -> zurueck
Rts

So mehr nicht, das prinzip ist klar, oder ?
Der Vorteil liegt in der Kuerze, denn das programm wird nicht laenger egal
wieviel ich damit kopiere. Ob 30 langwoerter oder 64 Kilobyte, es bleibt
dasselbe programm.

Es holt sich erst die Zeiger auf Quell und Ziel bereich in die
Adressregister 0 und 1, den mit denen kann man auch indirekt arbeiten...
Dann wird der Counter nach D0 geholt, wie ich gesagt hatte zaehlt der Dbf
befehl bis minus 1, deshalb muss ich das vorher beruecksichtigen, und die
laenge direkt -1 nehmen. Nachdem er dann von der Quelle die in A0 steht zum
Ziel das in A1 steht ein Langwort kopiert erhoeht er die beiden Zeiger so
dass sie jetzt aus $20004 und $30004 zeigen...usw..nach dem 20.sten
Durchlauf schmeisst der dbf befehl uns raus indem er ueberlesen wird, und
dann kommt der Rts befehl......->

Schoen das ist ja recht kurz..aber es geht noch kuerzer

Und zwar mit der Indirekten Postinkrementalen Adressierung, oder
Rueckwaerts mit der Indirekten Predekrementalen Adressierung.

Hoert sich schwer an, is ess aber nicht...Denn dies Adressierungs art
uebernimmt nur dieses Add.l #4 im SourceCode, indem sie die Zeiger
automatisch erhoeht nachdem gelesen und geschrieben wurde. Besser noch, Wir
muessen gucken was wir dazu addieren, falls wir Woerter kopieren koennen
wir ja nich plus 4 nehmen, dann wuerden fehler auftreten is ja klar, und
bei Byte waere es schlimm...Diese Adressierung schaut auch nach was ich
Kopiert habe, und setzt dementsprechend die Zeiger weiter, das heiss
Kopiert man Bytes setzt sie die Zeiger auf um 1 (byte) weiter, Kopiert man
langwoerter wird das entsprechende Register auch um 4 Byte`s (langwort)
weiter gestellt...Das listing von Eben saehe jetzt so aus

Start: Lea $20000,a0
Lea $30000,a1
Move.w #19,d0
Loop: Move.l (a0)+,(a1)+
Dbf d0,loop
Rts

Wird ja immer kuerzer....

So noch schnell die erklaerung der beiden dollen woerter

Post bedeutet `Nachher`, und Inkremental bedeutet erhoehen...Also es wird
Nachher erhoeht

Pre bedeutet `Vorher`, und Dekremental bedeutet erniedrigen....also es wird
vorher erniedrigt.

Sinn hat das Vielleicht nicht, aber es ist nuetzlich, besonders wenn sich
Quell und ziel bereich ueberlappen. Aber hier erstmnal das listing mit den
Aenderungen wenn es Predekremantal arbeiten soll

Start: Lea $20040,a0 ; Natuerlich muessen wir dann auch ganz
; hinten anfangen.
Lea $30040,a1
Move.w #19,d0
Loop: Move.l -(a0),-(a1)
Dbf d0,loop
Rts

Alles klor...

So noch eine Kleine Spielerei wenn ihr mal Speicher bereiche Spiegeln
muesst, bei bilder geht es zwar nicht, da die aus Bitplanes bestehen, aber
um texte zu spiegeln oder so reicht sie... Man laesst einfach einen zeiger
ab und den anderen auflaufen, schaut her !

Start: Lea $20000,a0
Lea $30040,a1
Move.w #19,d0
Loop: Move.l (a0)+,-(a1)
Dbf d0,loop
Rts

Mit ein bisschen logischem denken koennt ihr euch die funktion ja erklaeren,
falls nicht muesst ihr euch das kapitel noch mal durchlesen...

Natuerlich kann man mit dieser Geilen Adressierungsart auch noch toll
loeschen...

Start: Lea $20000,a0
Clr.l d0
Move.l #$20000,d0
Loop: Move.l d0,(a0)+
Dbf d0,loop
Rts

...loescht 128 Kbyte ab $20000.

9.Weiter mit dem Copper

So, will ich noch ein bisschen mit sogenannten billig effekte aufhalten,
weil man sie halt immer wieder braucht, zum Beispiel einen balken der von
oden nach unten zieht.
Als Grundlage nehme ich unser Ersten Copperlisten Programm damit ihr nicht
wieder alles neuabtippen muss. Wir werden zwar bald aus diesen SourceCode
rauswachsen aber im Moment reicht er noch eine Zeit.

Also, gehen wir das erstmal Theoretisch durch, wenn ihr es versteht einfach
nicken. Dann werden wir das Programm schritt fuer schritt modifiziern..

Nun, wir muessten eigentlich nur die Wait befehle bearbeiten, und zwar nur
die positionen worauf sie warten, das koennten wir mit add machen, und zwar
mit add #$100, damit wir die Zeilen Nummer und nicht den Pixel treffen.
Desweiteren muessen wir natuerlich auch eine Warteschleife einbauen damit
das auch klappt, und nicht zu schnell geht. Also jetzt kommt das listing un
alles was Ergaenzt wurde ist gross geschrieben.


Execbase = 4
Openlibrary = -552
CIAapra = $BFE001
Cop1lc = $DFF080
Forbid = -132
Permit = -138

Start: move.l Execbase,a6 ; Vorbereitungen fuer Forbid und
; Openlibrary.
jsr Forbid(a6) ; Forbid
clr.l d0 ; Versionsnummer Egal
lea gfxname,a1 ; Name der zu oeffnenden Lib nach A1
jsr Openlibrary(a6) ; Graphics.library oeffnen
move.l d0,gfxbase ; Adresse der Lib merken

move.l #copperliste,Cop1lc ; Adresse der Copperliste nach Cop1lc


LEA COPPERLISTE,A0
MOVE.W #$5000,D0
Wait: DBF D0,WAIT
ADD.W #$100,4(a0)
ADD.W #$100,12(a0)


Btst #6,CIAapra ; Kennt ihr schon
Bne.s wait

Move.l gfxbase,a0 ; Basisadresse nach a0
Move.l 38(a0),Cop1lc ; 38. sten wert einfach nach Cop1lc
; schreiben
Jsr Permit(a6) ; Multitasking erlauben
Clr.l d0 ; kein returncode
Rts

Gfxname: dc.b "graphics.library",0
even
Gfxbase: dc.l 0
Copperliste:

dc.w $0180,$0000
dc.w $200f,$fffe
dc.w $0180,$0f00
dc.w $220f,$fffe
dc.w $0180,$0000

copperlistenende:
dc.l $-2 ; ende der copperliste , -2 = $fffffffe

End:

Erstmal die naechste dazugekommene Adressierungsart, es ist die Indirekte
mit Adressdistanz. Das Heisst, es wird der Angegebene Zeiger genommen, und
die Adressdistanz dazu gezaehlt. Beispiel

Lea $40000,a0
Move.l #$23452345,$28(a0)
Rts

Es wird erst der zeiger $40000 nach a0 geholt, und dann der Wert $234523345
nach A0 + Distanz geschrieben, das waere dann $40028.
Warum ich das gemacht habe ?..Nun ich habe in A0 die adresse meiner
Copperliste gehabt, und dann habe ich nur noch abgezaehlt anwievielter
stelle die Wait befehle stehen, um sie zu Modifizieren, geschickt, ne?

Modifizieren geschieht ja durch Add.w #$100 wie ihr gesehen habt.

Das mit der Warte schleife ist ansich ja gut so aber wenn man was auf dem
Bildschirm macht sollte man zu einer anderen loesung greifen, aber dazu das
Naechste mal...

So,leute...ich habe mal ein ganz klein bisschen pause gemacht..damit ich
nicht verroste, und ein paar effekte `gebaut`, die werde ich euch
warscheinlich beibringen.

Hmmmh, weiter mit dem raster....Wie gesagt werden wir erstmal das Ding von
demletzt bearbeiten. Und zwar in der Form, das es erstmal weicher wird, und
nicht so Flackert, und zweitens das wir mal ein Schwingen in die Sache
bringen.

Wie gesagt, es ist besser wenn ihr das ausgedruckt vor den Augen habt. Ich
kann zwar aus dem Kopf erzaehlen was geaendert werden muss, schliesslich
habe ich das ding ja Programmiert, aber ihr muesstet es schon sehen, denn
einfach nur tippen und starten is scheisse. Wenn einer aber absolut keine
Moeglichkeit hat, dem werde ich es zuschicken, ich glaube aber das jeder
irgendeinen kennt der Druckern kann.

Ja, pack mers Buam

So, das erste was wir aendern wollen ist das es weicher zieht. Das mit der
Verzoegerungsschleife is` wohl jedem Klar..Aber wir koennen auch auf etwas
anderes warten. Wir sollten da etwas nehmen was zuverlaessig immer an der
gleichen position abfragbar ist, und was immer in gleichen abstaeden
Passiert....
Die uhr...noe geht nicht...die schalten wir bei der Interrupt sperre direkt
mit aus..da tut sich nichts mehr.
Aber den Bildschirm, wir sind mit dem Prozessoer zwar nicht so gut dran wie
mit dem Copper, aber fuer eine Abfrage reicht es allemal.
Vorher muesste ihr aber noch wissen das es im oberen bildschirmbereich ein
paar Zeilen gibt indenen der Amiga und der Copper usw. nichts machen, dort
koennen wir uns hervorragend `Einklinken`, man nennt es den Vertikal
Blanking bereich....Wie fragen wir das aber ab ?
Nun wir haben im Amiga ein Schoenes register..es nennt sich VHposr und
liegt in $DFF006, wir koennen dort X- und Y-position des Rasterstrahls
abfragen, dazu brauchen wir aber einen neuen befehl.

Compare Kurz CMP

Um uns die sache leicht zu machen, vergleichen wir Direkt, das heisst Wert
mit Speicherstelle, und sieht dann so aus

Cmpi.B #$00,VHposr

Das i hinter dem Cmp befehl muss bei allen Befehlen eingegeben werden wenn
sie so Direct arbeiten. z.b. Addi, Subi usw.
Also koennen wir das schon mal anstelle der #$5000 wartreschhleife im
Source einfuegen ....
So, jetzt wollen wir uns mal mit der Routine fuer die Bewegung befassen.
Was macht sie Eigentlich...hmmm nicht viel, sie addiert die Positionen der
Wait befehle in der Copperliste nur das das naechste mal bis eine zeile
tiefer gewartet wird. Das macht sie mit `Add`.
Es waere aber nicht viel schwerer eine Tabelle anzufertigen, in der halt
eine menge positionen stehen, die man dann einfach ausliesst, und jeden
durchlauf ein weiteres element nimmt.
Is wohl klar oder ?
Also zur Technik...keine bange, ich mache das mit den Mitteln die euch
schon zur verfuegung stehen.
Wir machen uns erstmal eine Tabelle, anhand dieser tabelle kann ich die
weiteren Sachen die noetig sind viel besser erklaeren. Bei dem
Programmierstil ist es egal wieviele Elemente die tabelle hat, das
erleichter die sache ungemein.
Erstmal die Tabelle, sie besteht der einfachheit halber erstmal aus dem
Ganzen Wait-befehl Datum...Hinterher werden wir dann mit Bytes arbeiten,
und durch manipulation in der Bitebene, die sache auf die richtige position
bringen...Also, here she comes


Dc.w $500f,$500f,$500f,$500f,$510f,$510f,$510f,$520f,$520f,$530f,$540f
Dc.w $550f,$570f,$590f,$570f,$550f,$540f,$530f,$520f,$520f,$510f,$510f
Dc.w $510f,$500f,$500f,$500f,$500f,$500f


Wie ihr vielleicht seht hoert die tabelle so auf wie sie angefangen hat,
sie ist eine Cyklus-tabelle.
Sie soll ein Titschbewegung des Balkens hervorrufen.

So, um diese Bewegung zu ermoeglichen muessen wir jetzt nur noch jeden
bildschirm durchlauf eine neue position aus der tabelle nehmen..und schon
titscht das ding.
Also, wie koennten wir das machen...Wir nehmen am besten einen Indirekten
zeiger, setzen ihn auf das erste Element der Tabelle, und lassen ihn
durchlaufen, er holt sich dan das erste element..sobald das passiert ist
setzen wir den Zeiger auf das naechtse Element...u.s.w. sobald das letzte
Element erreicht ist setzen wir den zeiger einfach wieder auf das naechste
Element....Ja, das ist relativ einfach...aber Programmtechnisch schwerer
als mein Zweiter Vorschlag zu diesem thema.

Wir lesen `Bei Jedem` durchlauf immer der erste Element der Tabelle, und
bearbeiten es. Dann lassen wir die Tabelle Rotieren, indem wir das erste
Element irgendwo zwischen speichern, und das zweite Element an die Stelle
des Ersten setzen, dann das dritte an die Stelle des Zweiten usw. sobald
wir dann das letzte an die Stelle des vorletzten kopiert haben schreiben
wir das gerettete erste an die letzte position geschrieben haben, haben wir
die Tabelle rotieren lassen...Das hat den Vorteil wenn wir die tabelle von
mehreren stellen auslesen lassen, das wir nicht Hunderttausend zeiger
Stellen und abfragen muessen, denn das ist eine Menge aufwand...Jetzt kommt
das geaenderte listing....Die tabelle koennt ihr auch selber eingeben, ihr
muesst dann nur den Counter-wert im Programmsource auf Tabellenlaenge in
worten -2 worten stellen....


Execbase = 4
Openlibrary = -552
CIAapra = $BFE001
Cop1lc = $DFF080
Forbid = -132
Permit = -138
VHposr = $DFF006

Start: move.l Execbase,a6
jsr Forbid(a6)
clr.l d0
lea gfxname,a1
jsr Openlibrary(a6)
move.l d0,gfxbase
move.l #copperliste,Cop1lc

Vonvorne:
LEA COPPERLISTE,A0
LEA SINTABLE,a1

Wait: CMPI.B #$80,VHPOSR
BNE.S WAIT
MOVE.W 18(A1),d0
MOVE.W D0,4(a0)
ADD.W #$200,D0
MOVE.W D0,12(a0)
BSR.S TABLEROTATE

Btst #6,CIAapra
Bne.s vonvorne

Move.l gfxbase,a0
Move.l 38(a0),Cop1lc

Jsr Permit(a6)
Clr.l d0
Rts

Tablerotate:
Move.l #sintable,a2 ; Holt sich tabellenzeiger
Move.l a2,a3 ; zweiter zeiger nach a3
Add.l #2,a3 ; zweiter zeiger auf naechstes Element
Move.w #25,d0 ; Counter
Move.w (a2),a4 ; Erstes wort retten
roloop:
Move.w (a3)+,(a2)+
dbf d0,roloop
move.w a4,(a2) ; Erstes wort-> unten dran
rts

Gfxname: dc.b "graphics.library",0
even
Gfxbase: dc.l 0
Copperliste:
dc.w $0180,$0000
dc.w $200f,$fffe
dc.w $0180,$0f00
dc.w $220f,$fffe
dc.w $0180,$0000

copperlistenende:
dc.l $-2 ; ende der copperliste , -2 = $fffffffe

Sintable:
Dc.w $500f,$500f,$500f,$500f,$510f,$510f,$510f,$520f,$520f,$530f,$540f
Dc.w $550f,$570f,$590f,$570f,$550f,$540f,$530f,$520f,$520f,$510f,$510f
Dc.w $510f,$500f,$500f,$500f,$500f,$500f


Natuerlich koennt ihr das listing ausschneiden, oder ihr tippt einfach die
aenderungen in euer Grundprogramm ein, wie ihr es macht is egal..hauptsache
ihr habt es verstanden.

So, nachdem wir Copperlistenverwaltungen und Copperlisten manipulation
gelernt haben, koennten wir eigentlich weitergehen oder....
Wir verlassen jetzt mal wieder das kapitel `Manipulationen an der Laufenden
Copperliste`, und gehen weiter mit dem Copper....Wir werden jetzt ein paar
tolle sachen machen, und dann machen wir weiter mit den Effekten...Aber
glaubt mir....Mit ! Bild! im hintergrund sehen die Effekte nochmal so doll
aus....

10.Konvertierung von IFF-ILBM Dateien ins RAW-Format

Endlich...jetzt kommen wir mal zu einer Sache die nicht so viel mit
Maschinensprache zutun hat, dafuer aber um so mehr mit Intros und Demos.

Wir wollen naehmlich mal ein Bild auf den Bildschirm bringen...
Ich ueberlege mir gerade ob wir erst Malen oder erst das programm entwerfen
sollen......Hmmmm, ich wuerde sagen - malen wir erstmal.

O.k, Deluxepaint 3.0 ist schnell zur hand, oder wer Dpaint 3.21 hat ist
natuerlich besser dran, denn es hat eine Funktion die ich sehr zu schaetzen
weiss, naehmlich die Umschaltung PAL->NTSC.. Naja wir malen auf jeden Fall
im PAL-modus, und leider erstmal nur mit einer (2) farben, und im LO-RES
modus...Also Stellt im Dpaint Menu bitte alles dafuer ein...

Also, was ihr jetzt malt ist mir Wurscht...Hauptsache es gefaellt euch.
Wenn es fertig ist...abspeichern...Dpaint verlassen und rein in den
Kefrens-IFF-converter...was dahinten hat den noch einer nicht, kann ich mir
garnicht vorstellen...wenn das einer von sich auch sagen kann, schreibe er
mir bitte einen kleinen brief, ich werde ihm das teil dann in den
Briefkasten pumpen...

O.k...gehen wir mal davon aus das jeder den hat...

Starten..So jetzt seht ihr viele Knoepfe und so..ihr geht erstmal auf den,
auf dem `CD DF0:` und drueckt solange drauf bis dort das laufwerk steht auf
dem die Diskette mit `EUREM` bild liegt. Dann lasst ihr diesen in ruh.
Nun druecken auf `READ DIR`, ratter...ratter... jetzt liegt die Directory
in dem Klitzekleinen Fenster ober Rechts.
Die bedienung kennt ihr ja...klickt euer bild an, und geht auf `LOAD`
Nach kurzer Zeit muesste euer Bild auf dem Bildschirm erscheinen...Was ? es
ist nicht ganz drauf, fehlt ein stueck...dann benutzt mal die
Cursortasten..Schoen nicht.?

So jetzt gehen wir zum Konvertieren.
Ihr seht oben ein Kleines Knoepfchen `IFF-ILBM`..das ist ja das was ihr
eingeladen habt, drueckt da jetzt einmal drauf. Jetzt muesste da RAW-NORM`
stehen..gut !

Jetzt geht ihr auf das ding Daneben, da drueckt ihr drauf bis da `BEFORE`
steht. Es bezieht sich auf die Colormap, sie ist dafuer da damit man die
genauen farben des Bildes hat und sie nicht aus der Palette von Dpaint
abschreiben muss, toll was ?

So jetzt muesst ihr dem Bild, das jetzt schon konvertiert ist noch einen
neuen Namen geben, dazu geht ihr in das laengliche Fenster unter den
Koordinaten, Drueckt mit der Maus drauf, und gebt einen Namen ein.
Dann geht ihr nur noch auf `SAVE` und fertig seid ihr mit dem Konvertieren.

Ich wuerde euch gerne die ganzen funktionen dieses ganz fantastischen
konverters erklaeren, aber die sind alle sehr schwer zu verstehen, und es
hat erst sinn wenn ihr den Blitter kennt, und das dauert noch etwas.

So nachdem wir das Bild abgespeicher haben, muesste es genau 10244 bytes
lang sein, falls nicht habt ihr etwas falsch gemacht.....

Probiert es nochmal....

So nachdem das Bild die richtige laenge hat, koennen wir die noetigen
Schritte zur darstellung durch den Computer besprechen...

So, gehen wir mal durch was der Amiga wissen muss...
1. Modus des dargestellten Bildes

Unter Modus versteht man ob Ham, Hi-Res, Lo-Res oder Interlace, zwischen
den 4 Modi koennen wir waehlen. Dazu dient das BPLcon0-register bei
$DFF100.

2. Anzahl der Planes
Zur einstellung der Planesanzahl benutzt man ebenfalls das BPLcon0
register.

Erstmal ein kleiner einschub zum Thema Raw-format und darstellung der
Farben und so.

Manche Computer haben Tolle Grafik Modi mit 32 oder 16 farben.
Man waehlt dann diesen modus aus und arbeitet darin, der noetige Platz im
speicher ist immer derselbe, egal ob ich jetzt alle 16 oder nur Zwei farben
brauche.
Beim Amiga ist es anders...wir koennen in Jedem der Obengenannten Modi,
noch die Anzahl der Planes eingeben, diese bestimmt dann die Anzahl der
Farben.

1 Plane = 2 Farben
2 Planes = 4 Farben
3 Planes = 8 Farben
4 Planes = 16 Farben
5 Planes = 32 Farben
6 Planes = 64 Farben (allerdings Extra-Half-Bright modus)

Man kann sich so sein Bild nach bedarf reservieren, nach dem Motto, Warum
soll ich fuer 32 Farben platz machen wenn ich nur 4 brauche.

Also, das mit den Planes sieht so aus..es sind die Vollen 320*256 Bits, mit
denen wir auch gemalt haben (ich beziehe das jetzt erstmal auf das Bild
was ihr malen solltet). Da gibt es nur Punkt gesetzt oder nicht gesetzt.
Also zwei kombinationen. Wenn ich von Oben auf so eine Plane gucke dann
sehe ich nur 0 oder 1, also zwei Farben.
Jetzt kommt eine Zweite Plane ins spiel. Der Amiga stellt diese dann genau
unter der Ersten dar. Wenn ich jetzt auf ein Bit gucke,gibt es ja 4
kombinationen, also auch vier Farben, 00 01 10 11, sind die Kombinationen.
Wenn ihr jetzt halt 5 Planes habt dann habt ihr auch 32 Kombinationen.
Die Kombinationen, geben als Bitkombination immer einen Wert, und der wert
stellt dann die farben dar. Also bei 2 Planes wuerde die Kombi.. 10, die
Zweite farbe darstellen, welche das ist muss man dem Programmierer
Ueberlassen, den Farben kann man setzen wie man will, wenn man erstmal den
Copper in seiner Hand hat.

Nach dem Wir jetzt Modus und farbenanzahl gewaehlt haben, muessen wir dem
Amiga noch sagen wie gross unser Bild ist. Da der Amiga den Ganzen
Bildschirm als Grafikbildschirm benutzen kann muessen wir das Fenster, auch
genannt `Playfield` fuer das bild ermitteln.
Dazu geben wir ecken oben Links, und unten rechts an, und wieviel vom
linken rand die darstellung anfaengt und wieviel vom Rechten Rand sie
aufhoert.

Die werte fuer unser Bild...Pal sind

DiwStrt = $3081
DiwStop = $30c1
Ddfstrt = $0038
Ddfstop = $00d0

Und ins Bitplane-Controll-registern (BPLcon0) kommt $1200

1 fuer eine Plane, und die 2 steht fuer Lores...

So, weiter mit den Bildern....Ausser Groesse farben, anzahl und
laenge, muss der Amiga noch wissen wo er die Plane findet die er darstellen
soll. Dazu dienen die sogenannten Bitplanepointer. Sie liegen fuer die
erste Plane bei $dff0e0 und $dff0e2.

In dem nun folgenden listing habe ich die ganzen einstellungen in die
Copperliste gesteckt..Obwohl man groesse und anzahl der Planes auch einmal
einstellen kann ist es besser es immer wieder in der Copperliste macht.
Erstmal das listing, dann werde ich die neue sache erklaeren..


start:
execbase= 4
openlibrary= -408
vhposr= $dff006
forbid= -30-102
permit= -30-108
dmacon= $dff096
intena= $dff09a
dmaconr= $dff002
cop1lc = $dff080


move.w #$0020,dmacon
move.w #$4000,intena
move.l execbase,a6
jsr forbid(a6)

Lea $5fffc,a0
Lea $dff180,a1
Move.w #1,d0
Colorloop:
Move.w (a0)+,(a1)+
dbf d0,colorloop

move.l #copperliste,cop1lc

wait:
btst #6,$bfe001
bne wait

move.l execbase,a6
jsr permit(a6)
lea gfxname(pc),a1
jsr openlibrary(a6)
move.l d0,a6
move.w #$83e0,dmacon
move.w #$c000,intena
move.l 38(a6),cop1lc
moveq #0,d0
rts

gfxname: dc.b "graphics.library",0

>Extern "bild1.raw",$5fffc
even

copperliste:
dc.w $00e0,$0006 ;Hi und Lo pointer der planeadresse im ram
dc.w $00e2,$0000
dc.w $0100,$1200 ;Bplcon0 1 Plane, Lores
dc.w $008e,$3081 ;Diwstrt
dc.w $0090,$30c1 ;Diwstop
dc.w $0092,$0038 ;Ddfstrt
dc.w $0094,$00d0 ;Ddfstop
dc.l $-2


So das ist es, ihr koenntet es euch rausschneiden..es ist einen etwas
verbesserte Version des alten Copperliste programm. Geaendert wurden sachen
die hinterher beim Ausbauen des Listing, stoeren wuerden...und vielleicht
sogar Guru`s veranstalten.

Wichtig ist das ihr nach dem Assemblieren `Y` drueckt und return bevor ihr
das mit `J` startet, damit das Bild nachgeladen wird.

So die erste aenderung die euch aufallen wird, sind zwei neue register,
naemlich DMACON und INTENA.
Das register INTENA heißt INterupts ENAble, was soviel bedeutet Erlaubte
Interupts. Mit ihm schaltet man alle moeglichen interrupts des Amiga an
oder aus...wir schalten sie aus....das gekoppelt mit Forbid, gibt uns die
ganze zeit des Amigas, er macht jetzt nur noch das was wir wollen.

Das zweite neue register ist DMACON. Es oeffnet die Tore fuer die einzelnen
DMA kanaele. Was DMA (direct Memory Access) ist wisst ihr schon. Es gibt
fuer alles, was der Amiga kann DmA kanaele, vom und zum Ram. Diese kanaele
werden solange sie angeschaltet sind immer wieder auf inhalt ueberprueft,
auch wenn man ueberhaupt nichts z.b mit sprites macht, wird dauernd
ueberprueft. Das kostet natuerlich zeit, deshalb schalten wir direkt mal
die Sprite DMA aus, da wir die im moment nicht brauchen.

So, jetzt muss ich euch aber noch etwas ueber das DMACON und INTENA
register erklaeren.

In den 16 Bits der Register, sind 15 schalter der die DMA kanaele oder die
Interrupts eingebaut. Der 16 schalter sagt wie der Zugriff ausssieht.

Den Wert den ich in diese Register schreibe ist eigentlich garnicht der der
hinterher drin steht. Den das 16 Bit sagt aus was gemacht wird. Ist es
gesetzt, werden alle bits die im Wort was ich dort hinschreibe gesetzt sind
auch im DMACON register gesetzt. Ist es geloescht, werden alle im Wert
gesetzten Bits, im DMACON register geloescht. Mit dem Wort das ich am
anfang in das DMACON register schreibe, schalte ich den Sprite Kanal aus.

Der wert ist #$0020, in Bits 0000000000100000, das heisst das das Fuenfte
Bit geloescht wird. Bit 5 ist fuer den SpriteKanal.
Wenn ich wie jetzt vom Cli aus komme, sind Sprite, Bitplane und Blitter DMA
angeschaltet. Die bitplane DMA muss ich an lassen, damit wir uberhaupt ein
Bild sehen, die Blitter dma kann ich ruhig anlassen, weil es sich mit ihm
etwas anders verhaelt, da dort ein Chip dahinter steht der den Kanal
ueberprueft, und darum kostet es uns keine Zeit, deshalb lassen wir ihn
einfach an.
Allerdings werden sich einige jetzt fragen warum ich den Sprite kanal
ausschalten, wir brauchen doch kaum Zeit, ein Voellig einfaches Programm,
wieso muss man denn da schon Zeit sparen ? Ganz einfach...Die Sprites im
Amiga haben eine Sehr schlechte angewohnheit. Sobald ihr Kanal angeschaltet
wird sind sie auch da. Da sie aber kein Aussehen und keine Bildschirm
Position zugewiesen bekommen haben, schnappen sie sich immer die werte die
sie gerade auf dem datenBus finden. Deshalb sieht man nur Truemmer.
Probierts es doch mal aus, loescht einfach die erste Zeile mit dem DMACON
ganz, dann koennt ihr das sehen, wie das aussieht....

So wenn ihr weiter guckt faellt euch die Colorcopy-routine auf, sie setzt
die Farben aus der Colormap, die ja vor der Bitplane liegt (habt ihr
eingestellt).
Wenn ihr bald mit mehr farben arbeitet dann muesst ihr natuerlich auch
diese Routine umstellen, wie das wisst ihr schon.

Das Oeffnen der Graphics.Library habe ich jetzt nach unten verlegt, es kann
schwierigkeiten geben wenn man wie ich das vorher gemacht habe. Erst die
Graphics.library oeffnen, zeiger retten und dann am ende auslesen. Es kommt
schon mal vor das man dort arbeitet wo dann die geoeffnete Library liegt.
Und dann ueberschreibt man sie und kriegt nicht mehr den wert den man
braucht. Das ist mir schon passiert, und dann wundert man sich... aber
sowas wird euch bestimmt auch noch oft genug passieren.

So, jetzt kommen wir zu einem Neue Seka befehl, de das Einbinden von
Dateien erlaubt...bzw. er laedt dateien in den Amiga Speicher ein. Das
koennen Bilder, Musiken oder alles moegliche sein. Seine Syntax lautet

>Extern "Name",Ziel (label oder Adresse), Laenge (Label oder fester wert)

also ein beispiel aufruf, der die ertsen 5000 Bytes vom Bild mit dem Namen
Auto nach $40000 einlaedt saehe dann so aus - >Extern "Auto",$40000,5000

Die option laenge braucht man eigentlich nie, deshalb lasse ich die immer
weg.
Aktiviert wird das ganze nach dem Assemblieren mit `Y`, dann schaut der
Seka das Listing nochmal nach Externen Kommandos durch und fuehrt diese
dann aus.

Noch was...wenn ihr die Funktion Openlibrary benutzt, achtet darauf das der
name der Library genauso geschrieben ist wie es im Buch oder in meinem Kurs
steht...naemlich klein und mit punkt getrennt, sonst erkennt der Befehl
diese nich und bricht ab...

graphics.library ist der absolut richtige name dafuer...also denkt dran.

Was man machen muss um ein Bild mit 32 farben darzustellen ist ja wohl
klar..aber ich erklaere es doch lieber mal.

Also erstmal das Bild ueberhaupt malen. Konvertieren (denkt an die
Colormap, sie sollte davor liegen).

Dann muesst ihr das >Extern kommando um schreiben 1. Den neuen namen
einsetzen 2. muesst ihr den Ladebeginn vorverlegen, da die Colormap jetzt
ja groesser ist. Wieviel ihr das jetzt vorverlegen muesst rechnet man so
aus....Farben*2 , bei 32 farben sind das 64 bytes...64 in Hexdezimal sin
$40, jetzt muesst ihr nur noch Bildanfang -$40 rechnen. Bei $60000 weare
das dann $5ffc0..also die adresse im Extern kommando muss also $5ffc0
lauten. Genau diese Adresse muesst ihr dann natuerlich auch in der
Farbenroutine einsetzen, und den Counter auf 32-1 setzen.

Dann muesst ihr noch in der Copperliste das BPLCON0 register auf $5200
setzen, da es 5 planes sind. Dann muesst ihr natuerlich noch die
BitplanePointer fuer die weiteren planes setzen. Da die Planes im
RAW-Format alle hintereinander liegen, koennen wir die weiteren Adresse
einfach ausrechnen. Also, unser Bild hat die maáe 320*256, das sind 256 mal
320 bits. Erstmal brauchen wir die Bytes..also 320/8 gleich 40. 40 mal 256
gleich 10240...in Hex $2800. Also lauten die Bitplane Startadressen so

1. $60000
2. $62800
3. $65000
4. $67800
5. $6a000

Einsetzen duerfte kein Problem mehr sein oder...

Falls das zu schwierig war muesst ihr leider die Letzten zwei Teile nochmal
durchlesen, denn dann ist etwas Falsch gelaufen.

Natuerlich waere es besser ihr wuerdet das jetzt mal selber versuchen, um
zu sehen was man schon kann. Das fertige teil kommt natuerlich bei dem
Naechsten Effekt als listing rueber, aber das ist ja nicht der Sinn der
Sache, oder ?

So jetzt koennt ihr erstmal einen Zeit mit dem Krempel rumspielen, und alle
moeglichen Bilder Konvertieren. Ihr solltet auch mal versuchen mit
Interlace, oder Hires zu arbeiten..wir werden das in diesem Kurs zwar nicht
benutzen, aber vielleicht findet einer seine leidenschaft darin und
arbeitet fortan nur dadrin. Es ist relativ einfach den Kurs auf Hires
umzusetzen. Naja..ich persoenlich halte nicht soviel von dem Flackern was
der Amiga uns da praesentiert. Die einzige schoene sache ist der HAM modus,
er ist allerdings ziemlich speicherverbrauchend (heisst doch so, oder ?)

Jetzt mal eine kleine erklaerung....Eine sache die ihr euch vielleicht auch
schon mal gefragt habt.

Ein Freund fragte mich letztens "Hoermal Sascha, warum arbeiten wir den im
RAW-format wenn wir Bilder darstellen, und nicht im IFF format wie die
ganzen Malprogramme es tun. Die werden doch sicher einen Grund haben das zu
tun. Und wenn ich mir den Umstand angucke, vonwegen das mit dem
Konvertieren und so, verstehe ich das nicht so recht"

Is Klar was er meint.....Aber der Amiga kann halt nur im RAW formt
arbeiten...das IFF-ILBM format, wie es fuer bilder heisst. Ist ein Pack
verfahren. Die bilder werden auf eine ganze bestimmte art und weise
Zusammen gepackt, gleichzeitig werden auch alle wichtigen daten des Bildes
mit abgespeichtert, als da waeren ..Farben, Groesse...Anzahl der Planes,
modus und so. Das IFF-Format gibt es auch fuer
TextFiles..Samples..Musikstuecke. Es soll den Daten Transfer verschiedener
Programm untereinander ermoeglichen...ne tolle Idee...aber wir muessen das
dann erstmal Konvertieren...Es ist keine Atemberaubende Sache die Dateien
zu entpacken, allerdings dauert es ein Parr sekunden, und das waere Extrem
langweilig finde ich, und deshalb Konvertieren wir das Vorher, O.K ?

So, nachdem wir auch mal Bilder dargestellt haben koennen wir schon
anfangen etwas ansehnliches zu Bauen. Dazu muessen wir aber das Thema
Bilder wieder kurz verlassen, um dann mit einem Sagenhaften effekt wieder
zu kommen.

Wer kennt nicht Stripes, dieses Schicke Cli farbspielerei, bei der es
aussieht als wenn der Bildschirm von einem Dicken Rohr durchzogen wuerde,
oder andere sachen, bei denen der ganze bildschirm von Rohren (die richtige
bezeichnung ist Tubes (sprich : Tjubs) ueberzogen ist. Nette sache..habt
ihr denn schon mal versucht eine selbstgemachte diashow mit einen der
Zahlreichen IFF anzeige Routinen zu machen...Hah, das war wohl nichts, in
dem meisten faellen jedenfalls. Warum ist klar, die eine Copperliste Stellt
die andere ab. Deshalb werden wir diesen Effekt zusammen mit einem Bild.

Erstmal die Beschriebung der Technik dieser `Tubes`.
Jede Zeile wird von Copper abgewartet, und dann ein farbwert aus einer
Tabelle reingschrieben. Die Tabelle ist so angelegt das die einzelnen
Farbtoenne immer nur einige Nouancen unterscheiden. Wenn man diese Tabelle
von Dunkel bis nach hell und dann wieder bis dunkel macht, und das auf
einem Kleinen raum dann kommt eine Tubes zustande.

Wenn wir allerdings den Ganzen bildschirm mit Tubes ueberziehen wollten.
dann wir schon im NTSC bereich 256 zeilen. Auf die Art und weise wie wir
das jetzt machen, vonwegen die Copperliste direkt im SourceCode ablegen,
das hieáe, wir haetten 512 zeilen Text nur fuer die Copperliste. Das waere
wohl doch ein Bisschen sehr viel arbeit, oder ?

Deshalb arbeiten wir mit einer Routine von 10 bis 12 Zeilen die uns diese
Copperliste erstellt.
Das heisst wir legen die einzelnen Elemente die so eine Copperliste hat,
wie z.b der move-befehl, oder die Farbadresse $180 in die Datenregister,
dann legen wir eine Tabelle an fuer die Farben (das machen wir hinterher)
und wir setzen das erste wort fuer den Wait befehl in ein Register.

Dann durchlaufen wir eine Schleife, indem alle Bausteine die noetig sind
immer wieder auf die Copperliste gesetzt werden, das ist wie ein Haus
bauen.
Ein durchlauf muesste dann so ausehen das erstmal die Position fuer den
Waitbefehl geschrieben wird, dann der Wait befehl selber...dann kaeme das
farbregisert was ich ansprechen will (in unserem fall waere das $0180 oder
$0182) und dann der Farbwert der in das Register geschrieben werden soll.

Dann muesste das Positions wort fuer den Wait befehl, so bearbeitet werden
das es auf die naechste Zeile zeigt, und das wort fuer die Farbe muesst
geaendert werden. Ob das durch auslesen einer Tabelle oder durch normales
Addieren geschieht (was uebrigens auch schoene Effekte zustande bringt) ist
erstmal gleich. Jetzt kommt erstmal so eine Routine, die eine Solche
Copperliste erstellt. Wie man sie Einbaut das zeige ich dannach.

Lea $50000,a0 ; Adresse ab der die Copperliste liegen
; soll.
Move.w #$-2,d0 ; Wait-befehl ($fffe) nach d0
Move.w #$0180,d1 ; Farbregister wert nach d1
Move.w #$200f,d2 ; Erstes Positions wort fuer den Waitbefehl
; nach d2
Move.w #$0000,d3 ; Erstes farbwort nach d3
Move.w #$80,d4 ; Counter nach d4
Copperloop:
Move.w d2,(a0)+ ; Durch die Postinkrementale adressierung
; koennen wir die Copperliste stueck fuer
; stueck zusammen bauen. Erstes Positions
; Wort schreiben.
Move.w d0,(a0)+ ; Wait-Befehl schreiben
Move.w d1,(a0)+ ; Erste farbregisterwort setzen
Move.w d3,(a0)+ ; Erstes Farbwort setzen

; Jetzt ist der erste Durchlauf geschafft.
; Wir beginnen mit dem Modifizeren der
; Ausgangswerte.
Add.w #$0100,d2 ; Waitpostionswort auf die naechste zeile
; setzen.
Add.w #$0003,d3 ; Farbwert addieren
/
Dbf d4,copperloop ; waren es schon $80 zeilen, wenn nicht _]
; Zueruck
Move.l #$01800000,(a0)+ ; Ganz unten wieder auf schwarz schalten

Move.l #$-2,(a0)+ ; $fffffffe als endemarkierung fuer die
; Copperliste.
Move.l #$50000,cop1lc ; Erstellte Copperliste auch aktivieren
Das prinzip duerfte klar sein, oder...

In der Naechsten Folge bauen wir das ding ein...

C.U.


So, wenn ihr dieses Modul einbaut, muessen natuerlich einige Aenderung an
dem hauptprogramm gemacht werden, alles moegliche was die Copperliste
betrifft, ich werde das listing liefern. Allerdings werden wir vorher noch
in gedanken mit der Routine arbeiten.
Sie erstellt ja eine Copperliste, die halt aber nur Farbenspielerei macht.

Wenn wir dazu jetzt noch ein Bild darstellen wollen muessen wir irgendwo
die sachen fuer das Bild einfuegen.
Da waere die Kopierroutine fuer die farben, und die ganzen sachen inder
Copperliste.
Da die Copperliste nicht mehr wie vorher schon fertig im Source liegt,
sondern wir sie erstmal erzeugen muessen die Einstellungen fuer den Bild
Modus auch so dort eingebaut werden.
Wir muessen das Allerdings vor der erstellungs routine, direkt nachdem die
Adresse fuer die Neue Copperliste festgelegt ist. Da die Werte ja jedem
bekant sind brauche ich wohl keine Erklaerung abzugeben, oder ?
Ich erklaere das am Beispiel einer Plane, weil mir Fuenf zuviel zu
schreiben ist.
Es saehe ungefaehr so aus...

...
...
...
Lea $50000,a0
Move.l #$00e00006,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$01001200,(a0)+

Erst jetzt kommt die Erstellungsroutine...

Nu, kommt das listing...und dann werde ich mal gucken ob ich wieder mal was
neues einbauen kann...sonst wirds langweilig, gell ?

start:
execbase= 4
openlibrary= -408
vhposr= $dff006
forbid= -30-102
permit= -30-108
dmacon= $dff096
intena= $dff09a
cop1lc= $dff080

move.w #$0020,dmacon
move.w #$4000,intena
move.l execbase,a6
jsr forbid(a6)

lea $5ffc0,a0
lea $dff180,a1
move.w #31,d0
colorcopy:
move.w (a0)+,(a1)+
dbf d0,colorcopy

Bsr.s makecopperliste

wait:
btst #6,$bfe001
bne wait

move.l execbase,a6
jsr permit(a6)
lea gfxname(pc),a1
jsr openlibrary(a6)
move.l d0,a6
move.w #$83e0,dmacon
move.w #$c000,intena
move.l 38(a6),$dff080
moveq #0,d0
rts

gfxname: dc.b "graphics.library",0
even

makecopperliste:
Lea $50000,a0
; Jetzt muessen die definitionen
; Fuer die Bildersachen eingesetzt
; werden
Move.l #$00e00006,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$00e40006,(a0)+
Move.l #$00e62800,(a0)+
Move.l #$00e80006,(a0)+
Move.l #$00ea5000,(a0)+
Move.l #$00ec0006,(a0)+
Move.l #$00ee7800,(a0)+
Move.l #$00f00006,(a0)+
Move.l #$00f2a000,(a0)+
Move.l #$01005200,(a0)+
Move.l #$008e3081,(a0)+
Move.l #$009030c1,(a0)+
Move.l #$00920038,(a0)+
Move.l #$009400d0,(a0)+
; weiter mit der erstellungsroutine

Move.w #$-2,d0
Move.w #$0180,d1 ; Hier koennt ihr mal andere
; werte eingeben. z.b $0182
; Oder $0184...
; Dann seht ihr wie sich teile
; grafik verfaerben.
Move.w #$200f,d2
Move.w #$0000,d3
Move.w #$00e0,d4
Copperloop:
Move.w d2,(a0)+
Move.w d0,(a0)+
Move.w d1,(a0)+
Move.w d3,(a0)+
Add.w #$0100,d2
Add.w #$0101,d3 ; Mit den Add wert koennt ihr
; ein bisschen spielen
; da kommen manchmal schoene
; effekte bei rum.
Dbf d4,copperloop
Move.l #$01800000,(a0)+
Move.l #$-2,(a0)+
Move.l #$50000,cop1lc
rts

>extern "pic32.raw",$5ffc0

So, ich habe beigeschrieben was man veraendern kann. Den Effekt mit den
Anderen Registern sieht man am besten, wenn ihr euch ein Bild mit 32
Vierecken malt, alle in verschiedenen Farben.

Nun, jetzt wollen wir uns mal etwas zuwenden, das jeder Introschreiber oder
Demomensch aus dem FF und in allen moeglichen Variationen Beherschen !MUSS!

11.Horizontales und Vertikales Softscrolling.

Was damit gemeint ist...Ganz Klar, am ende dieses grossen Kapitals steht
eine Laufschrift (Horizontal) und ein Schwingendes Logo (vertikal)
Mit der Vertikalen Laufschrift will ich noch was warten, da dies mit dem
Blitter am allereinfachsten geht, und ohne eine Scheissarbeitet ist.

Fangen wir erstmal mit dem Horizontalen Scrolling an...

Wie koennte man das realisieren ?

Hmmm, eigentlich keine Sache, wir setzen die Bitplane Pointers der planes
einfach eine Zeile rauf oder runter. Das bild verhaelt sich allerdings
umgekehrt. Setzen wir also den Pointer eine Zeile Tiefer, rutscht das Bild
eins Hoeher, da ja mit der darstellung, etwas tiefer im Bild begonnen
wird, geht das Bild Hoeher.

Falls ihr das nicht versteht, mal ein kleine Erklaerung wie der Amiga das
mit den Planes und den Pointern macht.

...Wenn der Amiga am Anfang der Ersten Zeile ist nimmt er sich den Pointer
fuer die Plane (ich rede wieder von einem Bild mit einer Plane), und
stellt die Zeile dar. Wenn die Zeile dargstellt ist, schaut er wielang die
Zeile jetzt war, und setzt den Pointer genau auf den Anfang der Naechsten
Zeile. Wenn er jetzt mit der Naechsten zeile beginnt, kriegt er Automatisch
den Pointer fuer die naechste Zeile im Bild und das ganze geht weiter.

Wie lange jetzt die Zeile ist haengt von den Diw und Ddf werten ab, in
unserem Fall sind es 40 bytes. Wir koennen uns allerdings bei diesen doch
sehr internen vorgang einmischen..und das durch die Modulo register.
Sie sagen dem Amiga, wieviel er die Pointer zusaetzlich zu der Zeilenlaenge
weiter stellen soll. Im normal fall sind diese 0 so das nur die
Zeilenlaenge gesetzt wird. Nehmen wir dort 40, wird immer eine Zeile
Uebersrungen. Nehmen wir -40 wird immer diesselbe Zeile gezeigt. Nehmen wir
-80 koennen wir, wenn wir den Bitplane pointer am anfang der Copperlisten
auf das ende der plane stellen, das Bild sogar auf dem Kopf darstellen.

Wofuer man diese Modulo register braucht werdet ihr sicherlich wissen
wollen, den Spiegeln ist auf diese Art und weise sicherlich keine
Ernsthafte anwendung.
Nun, man kann damit Spielfelder benutzen die Groesser als das Playfield
sind, ja die sogar groesser als der Bildschirm sind.
Mal im Klar text....Das Ram, der Speicher besteht ja aus lauter stellen,
wir fassen sie zu Bytes zusammen, das erleichtert das verstaendnis. Man
stellt sich im Allgemeinen den Speicher so vor

$40000
$40001
$40002
$40003

oder so...
Das heisst man denkt er besteht aus einem Byte nachdem anderen. Das ist
richtig so..allerdings koennen wir ihn auch rechteckig
darstellen...Monitore Machen das, da sieht es so aus

$40000 $40001 $40002 $40003
$40004 $40005 $40006 $40007

Im endeffekt ist das dasselbe, allerdings denken wir es uns anders.
Bei den Bitplane wird auch ein solch rechteckige Speicher bereich
unterstuetzt. Da liegen dann aber immer (wie in unserem beispiel) 40 bytes
nebeneinander, und dann kommt erst die Neue Zeile. Eine neue Zeile ist es
ja nicht den die Sachen liegen ja hintereinander, aber man stellt sich das
halt so vor.
Kommen wir nun zum Einsatz der Modulo register.
Stellen wir uns mal vor wir haetten ein Malprogramm mit dem man bilder von
800*800 malen koennte. Das bild waere im Speicher dann 100*800 Bytes, das
heisst 80000 Bytes lang. Wenn wir jetzt einen Ausschnitt auf den Bildschirm
bringen wollten, muessten wir ja Theoretisch nur die Bitplanepointer
setzen.
Aber schauen wir mal was er dann macht. Stellen wir uns vor wir haben das
Bild bei $40000 mit dem >Extern kommando deponiert.
Er zeigt die ertse Zeile, gut jetzt kommt er ans Ende...Aha 40 bytes,
er stellt den Pointer eine zeile vor...denkt er...Denn die zeilen vom
Ausschnitt stimmen ja nicht mit den wahren zeilenlaengen ueberein. Der
richtige Zeilen anfang liegt ja noch 60 Bytes weiter...Also genau dafuer
sind die Modulo werte...Da kann man jetzt genau diese 60 eintragen, und
schon stimmt die Sache...

Wir werden diese Uebergroáen bildschirme Allerdings erst benutzen wenn wir
den Blitter beherrschen, da wir uns mangels eines Malprogramms was diese
Grossen Bilder packt, die sachen selber zusammenschustern muessen.
Aber fuer ein paar nette effekte werden die Moduloregister schon hinhalten.
Ich denke dabei an einen Wasser effekt oder so aehnlich....

Na wird der Mund langsam waessrig.....

Aber wir wollen erstmal langsam anfangen indem wir ein Logo von unten nach
oben ueber den Bildschirm fahren lassen, O.K ?

Wie schon gesagt ist das sehr einfach, wir muessen einfach nur die Bitplane
pointer einens Bildes in geregelten Zeitlichen abstaenden hochzaehlen.
Das geht sehr einfach mit Add...is ja klar, ne ?
Muessen wir das einfarbige Logo malen...ihr koennt natuerlich auch das
erste Bild benutzen was ihr gemalt habt..

Dann machen wir vor und hinter der Plane noch eine Plane platz. Damit wir
das Bild wirklich von ganz unten in den Bildschirm reinkommen lassen
koennen, um es ganz oben wieder rauszuschieben.

Die sachen fuer die Abfragen ob schon ganz unten und so erklaere ich
nachher.


start:
execbase= 4
openlibrary= -408
vhposr= $dff006
forbid= -30-102
permit= -30-108
dmacon= $dff096
intena= $dff09a
cop1lc = $dff080


move.w #$0020,dmacon
move.w #$4000,intena
move.l execbase,a6
jsr forbid(a6)

Lea $5fffc,a0
Lea $dff180,a1
Move.w #1,d0
Colorloop:
Move.w (a0)+,(a1)+
dbf d0,colorloop
move.l #copperliste,cop1lc

; jetzt werden gleichzeitig
; zwei planes geloescht und
; unser Bild mittenreinkopiert

Lea $50000,a0 ; loeschzeiger 1.Plane
Lea $60000,a1 ; Quelle 2.plane
Lea $52800,a2 ; Ziel 2.plane
Lea $55000,a3 ; Loeschzeiger 3.plane
Move.w #$27ff,d0 ; Counter
ClearCopyloop:
Clr.b (a0)+
move.b (a1)+,(a2)+
Clr.b (a3)+
dbf d0,Clearcopyloop

wait:
cmpi.b #$80,vhposr
bne.s wait

Move.l #copperliste,a0
Add.w #$28,6(a0)

cmp.w #$5000,6(a0)
beq.s raus

btst #6,$bfe001
bne.s wait
raus:
move.l execbase,a6
jsr permit(a6)
lea gfxname(pc),a1
jsr openlibrary(a6)
move.l d0,a6
move.w #$83e0,dmacon
move.w #$c000,intena
move.l 38(a6),$dff080
moveq #0,d0
rts

gfxname: dc.b "graphics.library",0

>Extern "bild1.raw",$5fffc
even

copperliste:
dc.w $00e0,$0005 ;Hi und Lo pointer der olaneadresse im ram
dc.w $00e2,$0000
dc.w $0100,$1200 ;Bplcon0 1 Plane, Lores
dc.w $008e,$3081 ;Diwstrt
dc.w $0090,$30c1 ;Diwstop
dc.w $0092,$0038 ;Ddfstrt
dc.w $0094,$00d0 ;Ddfstop


coperlistenende:
dc.l $-2


So das waers..das programm kopiert die geladene Bitplane nach $52800,
loescht davor und dahinter jeweils eine Plane, und setzt den Pointer auf
die erste Leere plane...

Dann kommt die Modifikation an der laufenden Copperliste (fuer euch ja kein
Problem mehr, oder.

Die Verzoegerungsschleife wartet erstmal bis zeile #$80, dann geht es
weiter. Danach stellt sie den Pointer um eine zeile weiter. Dann
ueberprueft sie ob $5000, aslo die Letzte Plane schon komplett auf dem
Bildschirm ist. Dazu benutze ich einen Befehl `Beq` . Er stellt das genaue
gegenstueck zum Bne befehl dar. Er verzweigt dann wenn das ergebnis null
war. Er springt dann sofort raus. Dann wird noch mal die Maus abgefragt,
ihr koennt also auch schon waehrend das Programm noch laeuft wieder zurueck
zum Seka gelangen.

Achja, ich sollte nochmal schnell erklaeren wie Bne, Beq und der Cmp-befehl
zusammen passen.
Prozessoren Vergleichen alles durch abziehen. Das heisst wenn ich die
Speicher stelle $40000 mit #$5000 vergleichen soll, zieht der prozessor vom
Wert in $40000 die #$5000 ab. Das ergebnis merkt er sich nicht, sondern er
setzt nur die Flags, War es die Zahl ist das Z-Flag 0 war sie es nicht ist
es 1.
Da setze ich mit den befehle an. Es gibt zwei arten von Compareschleife

Wahrschleife und Nichtwahrschleife

Nichtwahrschleife..es ist die erste in unserem Programm...Sie heiát

"Wenn Zeile #$80 noch nicht erreicht, dann weiter mit..."

Wahrschleife

"Wenn schon bei $5000 dann raus zum..."

Alles Klor...

Es gibt noch eine Ganze menge von diesen Abfragen wie z.b

Groesser oder Gleich
Kleiner oder Gleich
Ergebnis im Negative Bereich
Ergebnis im Positiven Bereich

aber das ist ein Bisschen Viel fuer heute...Beim naechsten mal mehr

So, arbeiten wir noch ein bisschen mit dem listing von Eben.
Fuegt doch mal vor dem Ende der Copperliste folgende zeilen ein..


dc.w $0182,$0000
dc.w $000f,-2
dc.w $0182,$0222
dc.w $100f,-2
dc.w $0182,$0444
dc.w $200f,-2
dc.w $0182,$0666
dc.w $200f,-2
dc.w $0182,$0888
dc.w $200f,-2
dc.w $0182,$0aaa
dc.w $500f,-2
dc.w $0182,$0ccc
dc.w $600f,-2
dc.w $0182,$0eee
dc.w $700f,-2
dc.w $0182,$0ccc
dc.w $800f,-2
dc.w $0182,$0aaa
dc.w $900f,-2
dc.w $0182,$0888
dc.w $a00f,-2
dc.w $0182,$0666
dc.w $b00f,-2
dc.w $0182,$0444
dc.w $c00f,-2
dc.w $0182,$0222
dc.w $d00f,-2
dc.w $0182,$0000


Nett, nicht wahr...schoener Effekt.

Natuerlich koennte man auf diese art und weise auf ein logo Titschen oder
schwingen lassen. Dazu muessten wir wie am anfang mit dem balken der
Springt, die Positionen nicht mehr durch ein Popeliges `Add` erschaffen,
sondern die positionen aus einer Tabelle lesen. Aber das machen wir
zusammen mit dem dem Naechsten Effekte, der ein wahrer Super effekt ist,
wenn man sich muehe gibt...Aber ich denke es wird mal zeit etwas zu machen
was schon mal etwas Profisioneller aussieht.
Dazu muessen wir allerdings Einen Schritt weiter gehen, naemlich in...

12.Animation durch zyklische Copperlistenneuerstellung

Hoert sich etwas Komisch an, ist aber schnell erklaert.
Die veraenderungen die wird bis jetzt an den Copperlisten durchgefuehrt
haben bezogen sich immer nur auf ein oder zwei Elemente.
Jetzt aber werden die veraenderungen so Zahlreich das es einfacher und
schneller ist wenn wir jedesmal eine neue copperliste berechnen lassen, und
sie ueber die alte Kopieren.

Anwendung wuerde es finden, wenn wir wie im Vorletzten Listing eine
Copperliste erstellen, die eine Farbspielerei durch veraendern einer
Tabelle immer wieder neue Bilder Bringt, evtl. koennte auch bei jedem
Durchlauf eine Neue Tabelle initialisiert werden.

Tja das ist alles moeglich. Wir nehmen also die Technik die wir schon
kennen. Das erste element lesen und die Tabelle rotieren lassen.

Ich ueberlege mir gerade was wir den zusammenschustern...Entweder Stripes
(tubes) oder einen Waessrigen Wassereffekt, der waere natuerlich schoener.

Also, machen wir einen Kombinierten Logo-Titsch und Wasser effekt.

Das mit dem Titschen is ja wohl Null Probleme wie Alf jetzt sagen wuerde.
Denn das erstzen der Add funktion durch das auslesen einer Tabelle ist ja
schon etwas laenger bekannt. Jetzt muss ich nur noch die technik des
Wassereffektes erklaeren...Naja dazu gehen wir zwar nicht an den Fuehlinger
See, sonder in den Ascii see. Stellt euch bitte vor am ufer steht eine 4,
auf deren schulter eine drei, darauf eine 2 und ganz oben ist eine 1.
Desweiteren Stellt euch vor ihr kommt aus dem Wasser, es sind wellen da,
und ihr seht nach vorne. Dann seht ihr etwas verzerrt die Vier auch im
wasser wieder, allerdings auf dem Kopf...Ich versuche das mal
aufzuzeichnen. Bitte entschuldigt, den meine AscII zeichen faehigkeiten
sind doch sehr begrenzt.
Ich beschreibe die Situation von der seite, wobei die Zahlen auf den Wasser
die Stellen sind wo ihr die Spiegeln bilder der Zahlen am Ufer seht.


Du 1
2
3
4
111 ________________
*** ***2 4*** *** *** /
* * * *333* * * * * */ Strand
*** *** *** *** /

Wenn ihr euch vorstellt von Punkt `Du` zu gucken seht ihr die 1 ein paar
mal, also verzerrt laenger, die 2 garnicht, von der drei, zwei abschnitte,
allerdings wieder ins groessere verzerrt, und die 4 ganz. Das ganze
verzieht sich natuerlich sobald sich das wasser bewegung. Da wasser die
bewandnis hat niemals in wellen zu stehen, ist es klar das es sich standig
aendert.

So auch wenn es schwerfaellt, erinnert euch mal an das was ich geschrieben
habe zu den Einfachen effekten mit den Modulo werte, vonwegen Zeile
doppelt, oder Zeile garnicht oder so.
Mit dieser Technik koennten wir das machen, also frisch ans Werk.
Das wir jetzt fuer laenger zeit erstmal das letzte komplette listing. Da
wir ein laengere zeit nur noch mit Austauschen der Erstellungsroutinen
auskommen.
Achja fuer die Leute die sich die Register markieren.
Die Modulo register fuer Grade und ungrade Planes sind
$DFF108 ungrade ($0108 in dem Copperlistenerstellungsprogramm)
$DFF10A grade ($010a in dem Copperlistenerstellungsprogramm)

Falls ihr mir den Worten grade und ungrade noch nicht anfangen koennt, oder
warum es zwei getrennte gibt ist einfach.
Es ist zwar laestige bei einem bild ueber einer Plane (also ueber 2 Farben)
immer alle beide Modulo Werte zu setzen. Allerdings gibt es den
DualPlayfield-Modus, in dem Zwei voellig voneinander unabhaegige Playfields
gezeigt werden. Diese Koennen allerdings nicht mehr als 8 farben (3 planes)
darstellen, da der Amiga insgesamt nur 6 Planes verwaltet. Die beiden
Playfields teilen sich dann die graden und ungraden auf...

Playfield 1 - Playfield 2
1 2
3 4
5 6

Und dann ist es natuerlich schon besser wenn man getrennte Modulo werte
hat. Denn das eine Koennte ja ein Titschendes Logo sein, und das andere
ein Playfield mit uebergroesse, oder ?


Hier erstmal das Programm, ein paar erklaerungen hinterher.


start:
execbase= 4
openlibrary= -408
vhposr= $dff006
intena= $dff09a
dmacon= $dff096
cop1lc= $dff080
ciaapra= $bfe001

move.w #$0020,dmacon
move.w #$4000,intena
move.w #7,d0
lea $40000,a0
lea $dff180,a1
coloop:
move.w (a0)+,(a1)+
dbf d0,coloop
wait: cmpi.b #$80,vhposr
bne.s wait
bsr.s makecl
bsr.L tablescroll

btst #6,ciaapra
bne wait

move.l execbase,a6
lea gfxname(pc),a1
jsr openlibrary(a6)
move.l d0,a6
move.w #$83e0,dmacon
move.w #$c000,intena
move.l 38(a6),cop1lc
rts

makecl:
lea $03f000,a0
move.l a0,cop1lc
move.l #$01003200,(a0)+
move.l #$00e00004,(a0)+
move.l #$00e20010,(a0)+
move.l #$00e40004,(a0)+
move.l #$00e62810,(a0)+
move.l #$00e80004,(a0)+
move.l #$00ea5010,(a0)+
move.l #$01003200,(a0)+
move.l #$008e3081,(a0)+ ;diwstrt
move.l #$009030c1,(a0)+ ;diwstop
move.l #$00920038,(a0)+ ;ddfstrt
move.l #$009400d0,(a0)+ ;ddfstop

;----------------------------------------------------------------
; Wasser Effekt
;----------------------------------------------------------------
bsr.s makewasser

move.l #$b10ffffe,(a0)+
move.l #$01000200,(a0)+
move.l #$01081780,(a0)+
move.l #$010a1780,(a0)+
move.l #$01020000,(a0)+
move.l #$b20ffffe,(a0)+
move.l #$01080000,(a0)+
move.l #$010a0000,(a0)+
move.l #$-2,(a0)+
rts

gfxname: dc.b "graphics.library",0
>Extern "wasserpic.raw",$40000
even
Makewasser:
move.w #$0108,d0 ;Modulo fuer ungerade planes
move.w #$6c0f,d2 ;Waitadressen
move.w #$fffe,d3 ;Waitbefehl
move.w #$0043,d4 ;Counter
move.l #wassertable,a1 ;Wasserdatentabelle

wasserloop:
move.w d2,(a0)+ ;Warte auf ..
move.w d3,(a0)+ ;Wartebefehl
move.w d0,(a0)+
add.w #2,d0
move.w (a1),(a0)+ ;Modulo wert aus tabelle
move.w d0,(a0)+ ;Modulo gerade
sub.w #2,d0
move.w (a1)+,(a0)+ ;Modulo wert aus tabelle
add.w #$100,d2
dbf d4,wasserloop
rts
Tablescroll:
move.w #$63,d0
move.l #wassertable,a0
move.l a0,a1
add.l #2,a1
move.w (a0),a2
scrollloop:
move.w (a1)+,(a0)+
dbf d0,scrollloop
move.w a2,(a0)
rts
wassertable:
dc.w -80,-80,-120,-80,-120,-120,-120,-120,-80,-120,-80,-80
dc.w -80,-80,-40,-80,-40,-40,-40,-40,-80,-40,-80,-80
dc.w -80,-80,-120,-80,-120,-120,-120,-120,-80,-120,-80,-80
dc.w -80,-80,-40,-80,-40,-40,-40,-40,-80,-40,-80,-80
dc.w -80,-80,-120,-80,-120,-120,-120,-120,-80,-120,-80,-80
dc.w -80,-80,-40,-80,-40,-40,-40,-40,-80,-40,-80,-80
dc.w -80,-80,-120,-80,-120,-120,-120,-120,-80,-120,-80,-80
dc.w -80,-80,-40,-80,-40,-40,-40,-40,-80,-40,-80,-80
dc.w -80,-80,-120,-80,-120,-120,-120,-120,-80,-120,-80,-80
dc.w -80,-80,-40,-80,-40,-40,-40,-40,-80,-40,-80,-80
dc.w -80,-80,-120,-80,-120,-120,-120,-120,-80,-120,-80,-80
dc.w -80,-80,-40,-80,-40,-40,-40,-40,-80,-40,-80,-80


Den namen muesst ihr selber einsetzen. Ich habe den Extern befehl frueher
immer irgendwo im Listing positioniert, also viel spass beim suchen !

So fleissig wie ihr denkt bin ich allerdings nicht, ich habe das nicht
eben jetzt gemacht, sondern....

Tratarata, das ist mein erster versuch mit dem Wasser-effekt, es ist
ungefaehr 4 monate alt...Und wie ihr seht habe ich meine Programmierstil
seitdem nicht grossartig veraendert. Aber das heisst nichts, so habe ich
auch schon auf dem 64`er gewurschtelt.
Wobei ich hiermit alle Kursteilnehmer gruessen moechte die vom guten alten
64`er umgestiegen sind, um die klasse hoeher zu fahren..

Ich habe nichts grossartiges daran geaendert, deshalb laeuft es mit 8
Farben, waere guenstig wenn ihr noch ein Bild malt, welches nur aus drei
Planes besteht, oder besser versucht doch mal es umzuschreiben so das es
auf 32 farben laeuft. Eins ist allerdings von Noeten, ihr muesst in der
Erstellungs routine die da Heisst `Makewasser`, das erste wait wort so
einstellen so das es auf das ende Eures Logos zeigt. Es waere ganz gut wenn
euer Logo 70 Zeilen nicht ueberschreitet. Ansonsten koennt ihr machen was
ihr wollt.

So jetzt mal kurz zu der Funktion des Prograemmchens...
Es triftt alle noetigen vorbereitungen, ausserhalb der Endlosschleife.
Naja, was heisst `Alle`...sie kopiert nur die Farben nach $dff180 und so
weiter. Copperliste wird noch keine Erstellt.
Das passiert in der Endlosschleife, dort erstellen wir eine Copperliste,
die jeder zeile in einem bestimmten bereich einen Modulo wert gibt.
Diese Modulowerte, entsprechen dem was ich von dem Blick auf das wasser
gesagt habe. Manches sieht man nicht, manches ja...manches sogar doppelt
und dreifach..
Nachdem die Copperliste erstellt ist, wird die Tabelle der Modulo werte
Rotiert. Dann wird wieder einmal die Maus abgefragt...und schliesslich
falls nicht gedrueckt...springt der Pozessor wieder in die Endlos schleife.

Allerdings muss dieser Effekt, wenn er in ein Intro/Demo eingebaut wird
etwas veraendert werden, aber dazu (Viel) spaeter.

Versucht mal eine Andere Tabelle zu erstellen, ihr muesst allerdings dann
auch die Routine achten die die Tabelle Rotieren laesst, ihr muesst sie
dann auf eine neue Laenge einstellen.

Bei diesem Listing ist halt sehr deutlich zu sehen wie das mit dem
Zyklischen veraendern der Copperliste geht. Sobald die Zeile #$80 erreicht
ist, wird die neue Liste errechnet, und gleichzeitg aufgebaut. Nachdem die
steht wir noch die Tabelle rotiert, und dann kommt eine Maus abfrage. Und
dann wieder von Vorne.
Falls ihr, was ich nicht hoffen will, das ganze noch nicht so ganz
verstanden habt, es ging auch etwas schnell, dann ist das nicht ganz so
schlimm, das kommt...da wir jetzt schon zu den Fortgeschrittenen zaehlen,
koennen wir uns kleine schnitzer erlauben. Da ich nicht annehme das ihr
euch alles nur einmal durchlest schaetze ich mal das es irgendwann auch der
letzte kapiert.

Ihr koenntet ja mal versuchen, diesen Wassereffekte so mit der
Horizontalen laufschrift (ich nenne sie Kino Schrift) zu verbinden das es
aussieht als wenn die Schrift aus dem Wasser Kaem, gute idee ne ?

So, nachdem wir das mit diser zyklischen veraenderung verstanden habe
machen wir uns weiter, wieder weg davon und hin zur einfachen veraenderung
von einzelnen Elementen der laufenden Copperliste.
Im naechsten Teil wagen wir uns an ein Schwingendes Logo...

So, was wollten wir machen...achja das Schwingende Logo...mmmh, da habt ihr
euch aber was vorgenommen. Es ist ganz im gegentum zu den anderen sache
superleicht zu verstehen, allerdings mit der realisation ist es so eine
sache, wir werden dazu Mininum 5 - 6 voellig neue Befehle, mit bisher
noch nie dagewesener Funktion. Naemlich die Logischen Verknuepfungen, mit
And und Oder, Not und Eor. Alles boemische Doerfer fuer euch, ich will ich
allerdings jetzt noch nichts davon erzaehlen, denn ich will es so wie
bisjetzt halten das ich den Neuen Befehl erst Benutze und dann erklaere was
ich mit ihm gemacht habe. So versteht man sehr schnell. Oder versteht ihr
jetzt schon was `Ausmaskieren des Oberen Kontrollwortes` heisst, seht ihr !

Also, los gehts !!!

Ein teil des Problems koennen wir schon anhand des Kinoscrollers erklaeren.
Man verstellt die Pointer beim Kinoteil immer um eine Zeile. Beim
Titschenden Logo stellen wir die Pointer immer um die kleinste einheit die
ein Wort betraegt, nach links oder rechts.
Das in feinen abstaenden wird sogenanntes Hardscrolling ergeben.
Das aller ding saehe mit verlaub `ein bisschen Scheisse` aus, deshalb
benutzen wir ein wahrhaft fantastisches register..naemlich
BPLCON1 = $DFF102.

Ihr erinnert euch..mit BPLCON0 konnte man schon eine menge machen.
Dieses register beherbergt neben anderen tollen sachen auch noch die
Scrollwerte fuer die geraden und die ungerade planes, und mit
diesen scrollwerten koennen wir die Planes 0 bis 15 Pixel
nach rechts schieben, ist doch toll. So koennen wir durch geschicktes
Kombinieren von Hard- und Softscrolling jede Position auf dem Bildschirm
vom logo einnehmen lassen.

Vergegenwaertigen wir uns mal wie das errechnen der hard- und
softscrollwerte von statten gehen muesste.

...Wir schauen uns erstmal die Position an, an der das Teil Stehen soll.
Dann versuchen wir mit hardscrolling so nah wie moeglich ranzukommen.
So nah wie moeglich heisst, innerhalb der naechsten 15 Pixel. Dann stellen
wir den rest noch mit den Scrollwerten fuer das Softscrolling ein. So
einfach ist das.

Mal konkret...Wir haben ein Normales Playfield wie wir es dauernd benutzen,
mit den Massen 320*256. Die 256 wollen wir mal jetzt weglassen, wir gehen
mal von einem Einzeilenlogo aus, welches genau ein Wort breit ist.
Der Bildschirm hat 320 position, das entspricht 20 woertern a 16 Pixel.

Wir wollen unser `Logo` an der position 78 darstellen. Jetzt kommt der
Vorzug der hexdezimalen schriebweise. Bei dezimal muss man jetzt erst
grossartig durch divideren herausfinden wieviel mal die 16 darin ist,
dammit man die Hardscrollposition heraus findet, dann muesste mann das
nochmal machen damit man den Genauen rest, zum einstellen des
Softscrollings ermitteln.

In Hexadezimaler darstellung heisst 78 aber $4e, und da haben wir alle
beide positionen schon fertig. Man muss nur noch die 4 nehmen muss und als
Hardscrollzeiger nehmen. Die softscrollpositioen habe wir automatisch in
dem rest stehen, diese E bytes koennen wir direkt in das BPLCON1 register
schreiben wo wir ja die Soften werte einstellen.

So koennen wir relativ einfach eine bewegung erzeugen. Schrieben wir nun
eine Routine, die Anhand dieses `Steuerwertes` die Position fuer das Logo
errechnet. Der plane begin ist $40000, die Breite wie gehabt 320, das logo
ist ein wort breit. Damit wir ohne groessere Multiplikationen auskommen
muessen, benutzen wir einige neue Befehle. Fangen wir mal an ich erklaeren
jetzt mal jeden einzelnen schritt.

Der kontroll wert ist am anfang der Routine schon in D0....und es ist eben
diese $4e von eben, so kann ich beschreiben was nach jedem Schritt passiert
mit dem Kontrollwort.


Move.w d0,d7 ; Erstmal sichern da zwei aktionen mit
; mit dem Kontrollwert gemacht werden.

Divs #16,d0 ; Dividiert durch 16 ergibt 4, somit habe
; ich den wert fuer das Hardscrollregister.
; Da Hexadezimal alles abgerundet wird,
; weil es keine kommastellen gibt.

Add.w d0,d0 ; Wert verdoppeln, da eine Position also
; ein wort ja Zwei Bytes in der Plane
; bedeuten.

Move.l #$00040000,d1 ; Grundadresse der Bitplane setzen.

Add.w d0,d1 ; Errechnete Position in der Bitplane
; zu der Grundadresse dazu addieren. So
; erhaelt man die endgueltige Position !

Move.l d1,Bpl1pt ; Und ab in den Bitplanepointer.


So jetzt kommt der Teil mit dem Softscrolling...


Move.w d7,d0 ; Wert neu nach D0 schreiben.


Da wir die $4 vor dem E nicht brauchen koennen, muessen wir sie irgendwie
loswerden. In unserem ganz speziellen fall koennten wir natuerlich -$40
schreiben, aber wenn andere wert auf kreuzen kommen wir nich an das
gewuenschte Ziel oder wir schiessen in den Minus Bereich. Das wollen wir ja
nicht. Also setzen wir dem Prozessor eine Augenklappe auf. Das ist
natuerlich nur sinnbildlich zuverstehen. Aber es ist in der tat so das wir
dem Prozessor die Bits Markieren die er sehen kann. Nach dem Prinzip
koennten wir einfach dieses Kontroll wort nehmen, und dem Prozessor sagen
das er nur die letzten 4 bits sehen kann. Denn die sind ja im endeffekt nur
noch interresant fuer uns. Also wollen wir sehen wie man das macht.

And.w #%0000000000001111,d0

Merke: Dieses And hat nichts mit der Addition von Zahlen zu tun, sondern es
ist wie ein Filter. Das funktioniert so...

Der Prozessor nimmt beide werte, und verknuepft sie miteinander. Das
ergebnis haengt von der Funktion ab..Bei And ist das Ergebnis-Bit gesetzt
wenn auch das Quell-Bit `UND` das Ziel Bit gesetzt sind. Bei unserem
Beispiel koennen das nur die ersten 4 Bits sein. Jetzt mal Bildlich

Quelle %0000000001001110 = $4e
And %0000000000001111 = $0f
-------------------------------------
Ziel %0000000000001110 = $0e

Tolle sache ne..


Man koennte das And wort auch so sehen...

And %------------````

Wobei - sperrt, und ` durchlaesst.


Jetzt haben wir in D0 nur noch die SoftPosition stehen, und die koennen wir
direkt ind das BPLCON1 register setzen..

Move.l d0,BPLCON1

Rts

So, das war die Routine, sehr ausfuehrlich beschrieben oder ?


Diese Routine koennte die Steuerung fuer dieses Einwort Logo uebernehmen.
Allerdings nimmt man fuer Logo halt immer groessere Objekte. Und damit man
die nicht jedesmal soo ein riesiges Paket Daten kopieren muss. Setzen wir
einfach das Logo schon auf das Bild, Und verschieben nur noch das ganze
bild.


Ich will jetzt aber nicht wieder ein Listing hiereinpumpen sondern wir
werden Horizontal und VertikalScrolling erstmal ruhen lassen um uns etwas
anderem Widmen, naemlich...

13.Der Vertikal Blanking Interrupt

Bevor ich mit den Groesseren Demoreifen sachen Anfangen solltet ihr diesen
doch sehr Wichtigen teil des Amigas kennenlernen.

Ich habe euch doch schon von dem Vertikal Blanking bereich erzaehlt. Das
war doch der bereich der so gut fuer manipulationen an dem Bildschirm und
an der Copperliste ist weil dort vom Amiga noch nichts gemacht wird und
noch keine Copperliste Aktiv ist. Dieser VB bereich liegt zwischen 0 und
$3f. Wir haben ihn bisjetzt ja immer mit unserer einfachen Warteschleife

Wait: Cmpi.b #$00,VHposr
Bne.s Wait

abgefragt.
Das ist Theoretisch richtig, aber der Computer macht dann alles Zweimal pro
bildschirm aufbau. Denn die 0 tritt zweimal auf einmal bei $00 und einmal
bei $100, denn die $1 vor den Nullen testen wir ja nicht, also passiert
alles zweimal pro durchlauf. Verhindern koennten wir dies indem wir diese 1
auch noch abfragen. Die Routine dafuer lautet...


Wait: move.l $dff004,d0 ; Rasterpos holen
and.l #$1ff00,d0 ; alles unwichtige ausmaskieren
cmp.l #$1000,d0 ; Zeile 16 ($10) erreicht ?
bne.s Wait ; noe...

Das waere die Abfrage fuer die Wirkliche Position $10.
Allerdings ist das mit den Abfragen so eine Sache. Man kann ohne ihren
Rythmus zu beeinflussen nicht allzuviel dazwischen setzen, und das ist bei
manchen sache ja noetig. Stellen wir uns nur mal vor ihr bastelt ein Musik
demo zusammen. Ihr habt 10 musikstuecke a 30 KB, und ihr habt eine normale
Soundtrackerroutine. Diese muss in regelmaessigen Abstaenden, und zwar alle
50 stel Sekunde, aufgerufen werden. Der Bildschirm wird alle 50 stel
sekunde aufgebaut, und den Anfang fragen wir ja ab. Wenn wir dann aber noch
einbauen das er, damit es schnell geht, immer wenn die gewuenschte position
noch nicht erreicht ist, mal schnell die tasten F1 bis F10 fuer die
einzelnen musikstuecke abfragen, und falls eine gedrueckt war wird das Flag
fuer die neue Musik gesetzt. So falls der Prozessor ganz knapp vor der
Gewueschten Zeile abfragt kriegt er ja nur gesagt das sie es noch nicht
ist. Er fragt dann wieder alles ab...Und solche sachen Koennen mitunter
sehr lange dauern. Und dann faengt es an zu Flackern, dieses Flackern sieht
man bei schlecht programmierten Intros und Demos. Und weil wir das nicht
wollen, muessen wir uns etwas ausdenken oder etwas benutzen was ganz
bestimmt diese Sachen die Flackern koennten zu einem ganz bestimmten
Zeitpunkt durchfuehrt egal was passiert.

Dazu muessen wir aber nichts neues programmieren sondern etwas vom Amiga
benutzen, denn er hat die selben schwierigkeiten wie wir. Er behilft sich
mit einem Interrupt, naemlich dem VBI, Vertikal Blanking Interrupt.

Erst muss ich erklaeren was ueberhaupt ein Interrupt ist. Interrupt heisst
abbrechen, wie bei Coitus Interruptus, was viele ja schon kennen duerften.

Beim Computer ist es aber mehr eine Unterbrechung da die arbeit nach der
unterbrechung wieder aufgenommen wird.
Es ist ungefaehr wie wenn ihr jetzt diese Saetze lest und das Teflon
bimmelt, ihr nehmt ab, Quatscht kurz oder lang, legt wieder auf, denkt nach
wo ihr dran wart, und lest weiter.

Nicht anderes macht ein Computer, sobald er das Signal `IRQ` kriegt macht
er einen Generalinterrupt. Zuerst rettet er alle Arbeitsregisterinhalte
D0-D7 und A0-A7, auf den Stack. Dann analysiert er was fuer ein Interrupt
das war, das steht in einem ganz bestimmten register, dann springt er
indirekt je nach Interruptart ueber einen ganz bestimmten Zeiger zum
Programm das im Falle eines Interrupts ausgefuehrt werden soll.

Es gibt alle moeglichen Interrupts, uns interresiert aber im Moment nur
einer, halt der VBI, der Vertikal Blanking Interrupt.

Er wird ausgeloest wenn der Computer beim Bildschirm aufbau auf diesen
Bereich trifft. Nachdem der Prozessor weiss das es der VBI war, springt er
in die Adresse die in $0000006c steht, und fuehrt das Programm aus was dort
steht. Da liegt unsere Chance auf einen wirklich regelmaessigen Zeitpunkt
zum ausfuehren unserer Routinen zu kommen. Wir muessen nur die alte adresse
nehmen und irgend wo sichern, und dann unsere eigene hinschreiben. So
einfach ist das. Natuerlich muessen wir vor verlassen des Intros oder der
Routine den Alten VBI wieder aktivieren. Und ganz wichtig ist halt das wir
den VBI nicht mit all dem anderen Interrupts ausschalten, dazu kommen wir
noch.
Wenn ich eine Ganz bestimmte Tabelle hier finde werde ich euch ein paar
befehle geben die man nur waehrend eines Interrupts benutzen kann, warum ?

...Es gibt zwei modi in denen der Prozessor betrieben werden kann

1.User Modus
2.Supervisor Modus

Wir arbeiteten jetzt immer nur im User modus, das wird auch im grossen und
ganzen so bleiben, aber erstmal die erklaerung.

Im Supervisor modus ist es als wenn ein Zweiter Prozessor arbeiten koennte,
das ist noetig damit z.b Fehler noch fuer die Guru meldung analysiert
werden kann. Sobald eine Exception (ausnahmesituation) auftritt, der
Interupt gehoert auch dazu, wechselt der Prozessor in den Supervisor Modus.

Dies ist auch gleichzeitig die Einzige moeglichkeit in diesen Zustand zu
gelangen. Wir wollen aber nur wissen was das ist und deshalb werden wir
auch nur indirekt in diesem Modus arbeiten. Wiegesagt es kommen ein paar
befehle dazu, aber alle alten sind noch vorhanden.

Sobald der Prozessor dann auf den Rueckkehr-befehl trifft, holt er die
alten Daten vom Stapel und macht weiter als waere nichts geschehen.

Dieser Rueckkehr Befehl heisst `RTE` Return from Exception.

Der Vorteil liegt klar auf der Hand..wir initialisieren einmal diesen
Interrupt, und koennen ein Endlosschleife gemuetlich auf alle moeglichen
sachen warten lassen. Wir koennen Tausende Tasten abfragen, neue Flags fuer
die Musiken setzen, der Prozessor unterbricht uns dann zwar immer, aber das
ist ja das was wir wollen, das die Routine die Zeitkritisch sind immer zu
einem Ganz bestimmten Zeitpunkt abgearbeitet wird. Verloren geht uns auch
nichts, weil ja alle sachen die wichtig sind vor der eigentlichen Routine
gerettet werden.
ein kleines Beispiel fuer die Noetigen sachen fuer den VBI....Die
ernsthafte anwendung folgt spaeter.

Zuerst alle Interrupts loeschen.

Move.w #$7fff,intena

Dann alten interupt wert an einer bestimmten stelle im Source retten...

Move.l #$006c.w,Oldirq

Und nun den neuen wert schrieben..

Move.l #Newirq,$006c.w

Nun koennen wir den VBI anschalten..

Move.w #$c020,Intena

und unser programm irgendwas machen lassen...Wichtig ist nur das das
Programm mit dem Label `Newirq` steht, es wird direkt beim naechsten VBI
abgearbeitet.

Newirq: blabla #55,97
Blablabla soundso

und am enden der Routine muss dann dieses

Rte

stehen damit der Prozessor wieder an unserem normalen programm arbeiten
kann.

So einfach ist das. Arbeiten werden wir allerdings erst ein bisschen
spaeter damit. Da ich jetzt erstmal ein paar Effekte Programmieren muss,
damit ich die euch praesentieren kann, koennte es also drei vier Tage
dauern bis ich mich wieder melde.

So, direkt im Anschluss kommt die zweite versprochene Abspielroutine,
diesaml die, die 2.3 Sounds abspielt.
Tips am ende...

;+++++++++++++++++++++++++++++++
;+++ Soundtracker 2.3 Player +++
;+++++++++++++++++++++++++++++++

mt_init:lea mt_data(pc),a0
add.l #$01d8,a0
move.l #$0080,d0
moveq #$00,d1
mt_init1:
move.l d1,d2
subq.w #1,d0
mt_init2:
move.b (a0)+,d1
cmp.b d2,d1
bgt.s mt_init1
dbf d0,mt_init2
addq.b #1,d2

mt_init3:
lea mt_data(pc),a0
lea mt_sample1(pc),a1
asl.l #$08,d2
asl.l #$02,d2
add.l #$0258,d2
add.l a0,d2
moveq #$0e,d0
mt_init4:
move.l d2,(a1)+
moveq #$00,d1
move.w 42(a0),d1
asl.l #1,d1
add.l d1,d2
add.l #$1e,a0
dbf d0,mt_init4

lea mt_sample1(pc),a0
moveq #$00,d0
mt_clear:
move.l (a0,d0),a1
clr.l (a1)
addq.l #4,d0
cmp.l #$3c,d0
bne.s mt_clear

clr.w $dff0a8
clr.w $dff0b8
clr.w $dff0c8
clr.w $dff0d8
clr.l mt_partnrplay
clr.l mt_partnote
clr.l mt_partpoint

move.b mt_data+$1d6,mt_maxpart+1
rts

mt_end: clr.w $dff0a8
clr.w $dff0b8
clr.w $dff0c8
clr.w $dff0d8
move.w #$000f,$dff096
rts

mt_music:
addq.l #1,mt_counter
mt_cool:cmp.l #6,mt_counter
bne.s mt_notsix
clr.l mt_counter
bra mt_rout2

mt_notsix:
lea mt_aud1temp(pc),a6
tst.b 3(a6)
beq.s mt_arp1
lea $dff0a0,a5
bsr.s mt_arprout
mt_arp1:lea mt_aud2temp(pc),a6
tst.b 3(a6)
beq.s mt_arp2
lea $dff0b0,a5
bsr.s mt_arprout
mt_arp2:lea mt_aud3temp(pc),a6
tst.b 3(a6)
beq.s mt_arp3
lea $dff0c0,a5
bsr.s mt_arprout
mt_arp3:lea mt_aud4temp(pc),a6
tst.b 3(a6)
beq.s mt_arp4
lea $dff0d0,a5
bra.s mt_arprout
mt_arp4:rts

mt_arprout:
move.b 2(a6),d0
and.b #$0f,d0
tst.b d0
beq.s mt_arpegrt
cmp.b #1,d0
beq.s mt_portup
cmp.b #2,d0
beq.s mt_portdwn
rts

mt_portup:
moveq #$00,d0
move.b 3(a6),d0
sub.w d0,22(a6)
cmp.w #$71,22(a6)
bpl.s mt_ok1
move.w #$71,22(a6)
mt_ok1: move.w 22(a6),6(a5)
rts

mt_portdwn:
moveq #$00,d0
move.b 3(a6),d0
add.w d0,22(a6)
cmp.w #$358,22(a6)
bmi.s mt_ok2
move.w #$358,22(a6)
mt_ok2: move.w 22(a6),6(a5)
rts

mt_arpegrt:
cmp.l #1,mt_counter
beq.s mt_loop2
cmp.l #2,mt_counter
beq.s mt_loop3
cmp.l #3,mt_counter
beq.s mt_loop4
cmp.l #4,mt_counter
beq.s mt_loop2
cmp.l #5,mt_counter
beq.s mt_loop3
rts

mt_loop2:
moveq #$00,d0
move.b 3(a6),d0
lsr.b #4,d0
bra.s mt_cont
mt_loop3:
moveq #$00,d0
move.b 3(a6),d0
and.b #$0f,d0
bra.s mt_cont
mt_loop4:
move.w 16(a6),d2
bra.s mt_endpart
mt_cont:
asl.w #1,d0
moveq #$00,d1
move.w 16(a6),d1
lea mt_arpeggio(pc),a0
mt_loop5:
move.w (a0,d0),d2
cmp.w (a0),d1
beq.s mt_endpart
addq.l #2,a0
bra.s mt_loop5
mt_endpart:
move.w d2,6(a5)
rts

mt_rout2:
lea mt_data(pc),a0
move.l a0,a3
add.l #$0c,a3
move.l a0,a2
add.l #$1d8,a2
add.l #$258,a0
move.l mt_partnrplay,d0
moveq #$00,d1
move.b (a2,d0),d1
asl.l #$08,d1
asl.l #$02,d1
add.l mt_partnote,d1
move.l d1,mt_partpoint
clr.w mt_dmacon

lea $dff0a0,a5
lea mt_aud1temp(pc),a6
bsr mt_playit
lea $dff0b0,a5
lea mt_aud2temp(pc),a6
bsr mt_playit
lea $dff0c0,a5
lea mt_aud3temp(pc),a6
bsr mt_playit
lea $dff0d0,a5
lea mt_aud4temp(pc),a6
bsr mt_playit
move.w #$01f4,d0
mt_rls: dbf d0,mt_rls

move.w #$8000,d0
or.w mt_dmacon,d0
move.w d0,$dff096

lea mt_aud4temp(pc),a6
cmp.w #1,14(a6)
bne.s mt_voice3
move.l 10(a6),$dff0d0
move.w #1,$dff0d4
mt_voice3:
lea mt_aud3temp(pc),a6
cmp.w #1,14(a6)
bne.s mt_voice2
move.l 10(a6),$dff0c0
move.w #1,$dff0c4
mt_voice2:
lea mt_aud2temp(pc),a6
cmp.w #1,14(a6)
bne.s mt_voice1
move.l 10(a6),$dff0b0
move.w #1,$dff0b4
mt_voice1:
lea mt_aud1temp(pc),a6
cmp.w #1,14(a6)
bne.s mt_voice0
move.l 10(a6),$dff0a0
move.w #1,$dff0a4
mt_voice0:
move.l mt_partnote,d0
add.l #$10,d0
move.l d0,mt_partnote
cmp.l #$400,d0
bne.s mt_stop
mt_higher:
clr.l mt_partnote
addq.l #1,mt_partnrplay
moveq #$00,d0
move.w mt_maxpart,d0
move.l mt_partnrplay,d1
cmp.l d0,d1
bne.s mt_stop
clr.l mt_partnrplay

mt_stop:tst.w mt_status
beq.s mt_stop2
clr.w mt_status
bra.s mt_higher
mt_stop2:
rts

mt_playit:
move.l (a0,d1),(a6)
addq.l #4,d1
moveq #$00,d2
move.b 2(a6),d2
and.b #$f0,d2
lsr.b #4,d2
tst.b d2
beq.s mt_nosamplechange

moveq #$00,d3
lea mt_samples(pc),a1
move.l d2,d4
asl.l #2,d2
mulu #$1e,d4
move.l (a1,d2),4(a6)
move.w (a3,d4),8(a6)
move.w 2(a3,d4),18(a6)
move.w 4(a3,d4),d3
tst.w d3
beq.s mt_displace
move.l 4(a6),d2
add.l d3,d2
move.l d2,4(a6)
move.l d2,10(a6)
move.w 6(a3,d4),8(a6)
move.w 6(a3,d4),14(a6)
move.w 18(a6),8(a5)
bra.s mt_nosamplechange

mt_displace:
move.l 4(a6),d2
add.l d3,d2
move.l d2,10(a6)
move.w 6(a3,d4),14(a6)
move.w 18(a6),8(a5)
mt_nosamplechange:
tst.w (a6)
beq.s mt_retrout
move.w (a6),16(a6)
move.w 20(a6),$dff096
move.l 4(a6),(a5)
move.w 8(a6),4(a5)
move.w (a6),6(a5)
move.w 20(a6),d0
or.w d0,mt_dmacon

mt_retrout:
tst.w (a6)
beq.s mt_nonewper
move.w (a6),22(a6)

mt_nonewper:
move.b 2(a6),d0
and.b #$0f,d0
cmp.b #11,d0
beq.s mt_posjmp
cmp.b #12,d0
beq.s mt_setvol
cmp.b #13,d0
beq.s mt_break
cmp.b #14,d0
beq.s mt_setfil
cmp.b #15,d0
beq.s mt_setspeed
rts

mt_posjmp:
not.w mt_status
moveq #$00,d0
move.b 3(a6),d0
subq.b #$01,d0
move.l d0,mt_partnrplay
rts

mt_setvol:
move.b 3(a6),8(a5)
rts

mt_break:
not.w mt_status
rts

mt_setfil:
moveq #$00,d0
move.b 3(a6),d0
and.b #$01,d0
rol.b #$01,d0
and.b #$fd,$bfe001
or.b d0,$bfe001
rts

mt_setspeed:
move.b 3(a6),d0
and.b #$0f,d0
beq.s mt_back
clr.l mt_counter
move.b d0,mt_cool+5
mt_back:rts

mt_aud1temp:
blk.w 10,0
dc.w $0001
blk.w 2,0
mt_aud2temp:
blk.w 10,0
dc.w $0002
blk.w 2,0
mt_aud3temp:
blk.w 10,0
dc.w $0004
blk.w 2,0
mt_aud4temp:
blk.w 10,0
dc.w $0008
blk.w 2,0
mt_partnote: dc.l 0
mt_partnrplay: dc.l 0
mt_counter: dc.l 0
mt_partpoint: dc.l 0
mt_samples:dc.l 0
mt_sample1:blk.l 15,0
mt_maxpart:dc.w $0000
mt_dmacon:dc.w $0000
mt_status:dc.w $0000

mt_arpeggio:
dc.w $0358,$0328,$02fa,$02d0,$02a6,$0280,$025c
dc.w $023a,$021a,$01fc,$01e0,$01c5,$01ac,$0194,$017d
dc.w $0168,$0153,$0140,$012e,$011d,$010d,$00fe,$00f0
dc.w $00e2,$00d6,$00ca,$00be,$00b4,$00aa,$00a0,$0097
dc.w $008f,$0087,$007f,$0078,$0071,$0000,$0000,$0000

>extern "name des Moduls",mt_data

mt_data:blk.b laenge des moduls,0


Einbauen muesst ihr die so wie die erste...das duerfte kein Problem sein,
oder ?

Tip...Baut die Soundroutine nur ganz am Anfang eines Intros ein...dann
ueberpueft ihr ob sie laeuft...Dann setzt ihr im oberen teil des Sources,
vor die 3 MT_... aufrufe Semikolons, dann wird es vom Seka als
Kommentarezeile gesehen und nicht assembliert, die Komplette
Abspielroutine, ladet ihr immer nur mal kurz ein, wenn ihr einen Effekt
lauffaehig habt..das erspart euch viel zeit...

Ich will kein Kapitel mehr dranhaengen, deshalb ist schon schluss fuer
jetzt, werde aber sofort den naechsten Teil schreiben. Versprochen !!

So Leute, nachdem wir einige tolle sachen kennengelernt haben die unser
Demo/Intro schmuecken, wollen wir uns mal wieder etwas einfachem widmen.

15.Darstellung von Sprites

So, erstmal was sind sprites....Orginaltext Data Becker (treffender koennte
selbst ich es nicht ausdruecken)

`Sprites sind kleine Grafik Elemente, die voellig unabhaengig von den
Playfields verwendet werden koennen`

Also ganz klar es sind kleine Objekte....Ich weiss zwar nicht wie Data
Becker das gemeint hat, aber die sind wirklich schrecklich klein, naemlich
maximal nur 16 Pixel breit. Dafuer aber so lang wie wir wollen, schwacher
trost.

So, wozu braucht man Sprites....Wenn ich da an frueher denke auf dem 64`er,
da hatten die Dinger echt ne` Bedeutung.
Aber jetzt..wenn der Mauszeiger nicht waere wuerde man sie nie benutzen.
Es ist unheimlich kompliziert mit ihnen zu hantieren, das einzige wofuer
ich sie einsetze sind Sternenhintergruende....ja dafuer kann man sie
gebrauchen.

Erstmal zum aufbau von Sprites...Wie gesagt maximal 16 Pixel breit, dafuer
so lang wie wir wollen. Es gibt 8 Stueck von ihnen, von denen jeder 4
Farben annehmen kann. Man kann sie aber auch kombinieren, zu insgesamt 4
Stueck, allerdings dann jeder mit 16 Farben..das ist doch schon mal was.

Wie kriegt man sie zu sehen ?, und was muss man beachten.

Tja, da hat sich der Erfinder wirklich gedanken gemacht, wie man soetwas
gut verstecken kann. Erstmal grob die Punkte und dann die erklaerungen.

1.Sprite DMA Kanaele anschalten (oder nicht ausschalten)
2.Bitplane DMA an
3.Playfield erzeugen
4.Farben geben
5.Sprite Datenliste erzeugen
6.Zeiger der unbenutzten Sprites auf leere Listen verbiegen

Puuuh, ist ja eine Ganze menge fuer so kleine Dinge. Also fangen wir mal
an.

1. Sprite DMA ist ja Klar..wiegesagt muss fuer solche Sachen erst der
Zustaendige Kanal geoeffnet werden, sonst kommen die Zustaendigen
Spezialchips, in diesen Fall sind es Agnus und Denise, nicht an die Daten
ran.!

2. Tja das ist einer der Haken, man muss ebenfalls die Bitplanes anhaben,
da sonst nichts kommt.

3. Es kommt noch schlimmer, man muss sogar ein Komplettes Playfield
erstellen, mit allen drum und dran. Den an den Diwstrt und Diwstop werten
orientieren sich die sprites, das heisst wir ein Sprites teilweise oder
ganz ausserhalb des playfields gesetzt werden, wird das Stueck einfach ganz
dreist abgeschnitten, frechheit.

4. Naja farben muss man den dingern nicht unbedingt geben, aber es sieht
halt nich besonders gut aus, wenn es Schwarz auf schwarz ist. Und da das
die Default einstellung ist, muessen wir um sie zu sehen auch etwas farbe
ins spiel bringen.

5. Diese Datenliste enthaelt alle sonstigen informationen ueber das Sprite,
naja was heisst alle, nur zwei..aber die wichtigsten...Aussehen und
Position.

Wie sieht diese Datenliste aus ?

Als erstes kommen zwei kontrollworte..In den Ersten steht die X-und Y
Koordinate des Sprites, in dem Zweiten bis wo er auf dem Bildschirm geht,
danach richtet sich auch die laenge der Daten liste richtet, und die beiden
9.ten Bits fuer die x und y koordinaten, weil mehr als 256 positionen
vorhanden sind, sind die Zaehler auf 256 angesetzt. Desweiteren steht bei
den ungeraden noch das sogenannte Attach Kontroll Bit. Das sagt aus ob
dieser sprite mit dem Darauffolgenden grade zu einem 16 Farbigen gekoppelt
ist. Wie ihr seht knn man auch nur mal eben zwei verkoppeln, man muss nicht
direkt alle nehmen.

6. Das was dann noch kommt ist nicht notwendig da die Sprites schon stehen,
und ebenfalls nicht wenn man sowieso alle sprites benutzt. Aber falls
einige nicht benutzt werden, dann muss man es machen.

Die unangenehme eigenschaft des Sprite DMA Kanals ist das sofort alle
sprites angeschaltet werden. Da wir aber den nichtbenutzten Sprites keine
Datenliste zuweisen, steht in den registern nur 0. Die DMA nimmt das aber
als Adresse..und da im vorderen Teil des Speichers, so bei 0 immer was los
ist, erscheinen mal hier mal da immer wieder Truemmer, sowas wie ihr es
seht wenn ihr vergesst die Sprite-DMA auszuschalten. Also nur eine
Schoenheitskorrektur.

Naja, wie gesagt benutze ich nur Sprites wenn ich Sternen hintergruende
mache, denn dafuer eignet sich das Prinzip dieser Datenliste eigentlich
ganz gut. So jetzt wollen wir aber erstmal ueberhaupt etwas darstellen. Das
mit den Sternen bau ich irgendwann spaeter mal in einen laengere Effekt ein
oder so..denn es ist nicht besonders schwierig..oder mal gucken Vielleicht
kommt es noch als eigenstaendiger Effekt...ich weiss es noch nicht..ich
muesste mal gucken wie kurz ich es machen kann, denn diese scheiss langen
Source Codes, zerren doch ganz schoen an eurer Telefon rechnung oder Dem
Kleingeld fuer das Druckerpapier.

Naja, bis jetzt war es ja noch leicht, wenn es aber um die Definition der
Form und farbe geht dann wird es Haarig. Obwohl so schlimm ist es wiederrum
nicht..das Grundprinzip kennt ihr schon.

Bei der Sprites Form und Fargebung, funktioniert es aehnlich wie bei den
Bitplanes der Bilder. Hier wird auch wieder aus der Kombinatioen von 2 Bits
die Farbe errechnet. Diese Beiden SpritePlanes die so ein 4 Farben sprite
dann braucht, werden aber nicht wie bei den Bitplanes fuer die Raw Bilder,
untereinander, Plane fuer Plane...sondern immer eine Plane zeile nach der
ander. Naja Bildlich gesehen sieht es dann so aus. Stellt euch bitte mal
ein Sprite vor das nur eine Zeile hat, aber mit allen 4 farben drin, da ich
hier nicht die Farben malen kann, muessen wir und leider mit den Nummern
zufriedengeben, obwohl da vielleicht besser ist...also die Farben der Zeile
sind so

0011223333221100

Also auf und ab, die Bit Kombination fue die Zahlen muessten eigentlich
klar sein, falls nicht...

Wert Bit 0 Bit 1

0 = 0 0
1 = 0 1
2 = 1 0
3 = 1 1

und so sieht das dann fuer eine Zeile sprite daten liste aus, es werden die
gesetzten Bits 0 und 1, getrennten in zwei worten gehandelt.

gesetzte bits 0 gesetzte bits 1

dc.w %0011001111001100,%0000001111000000

gehen wir mal die zeile durch, wir lesen immer das erste Bit des ersten
wortes, und das erste bit des zweiten wortes.

Zweimal 0 ergibt 0, wir schaun oben, da steht auch null, das naechste Bit
dasselbe.
Doch dann Bit 0 ist gesetzt, und in der zweiten zeile nicht, wir muessen
uns das dann etwas anders vorstellen, denn normaler weise schreibt man
zahlen von rechts nach links, und so liest man sie auch, Binaer geht es
ebenso, die Kleinste einheit steht immer Rechts, wenn wir das hier aber so
machen, kommen wir an falsche ergebnisse, wir muessen also um richtig zu
liegen die Bitkombination die wir aus den Beiden Worten kriegen spiegel
verkehrt sehen. Also sehen wir das erste Bit ist gesetzt, und das zweite
nicht. Das ergibt 01, und das ist 1...also farbe 1.

Als naechte kombination erhalten erhalten wir erst eine 0 und dann eine 1,
das ergibt 10, das ist laut Tabelle 2. Also farbe zwei...Die letzte
Kombination ist die fuer die Farbe drei, dort sind beide gesetzt, das
ergibt ja 3.

Die Defintionsadressen tragt ihr fuer die Sprites bei den Registern

SPR0PTH und SPR0PTL bis SPR7PTH und SPR7PTL ein.
Sie liegen bei $DFF0120 bis $DFF13E...Schaut es euch im Listing an.

So eigentlich duerfte das Klar sein..aber ihr muesstet gemerkt haben
wieviel arbeit es ist so kleine Objekte zu erhalten. Also am ergebnis ist
ja zusehen was fuer ein Aufwand das ist..Aber arbeitet ruhig mal ein
bisschen damit, wenigstens so lange bis ihr Bobs beherscht, denn die sind
mit viel weniger aufwand zu machen und die Moeglichkeiten ungleich hoeher.

Naja genug der Laberei, nun wollen wir mal meinen wohlklingenden Vornamen
auf den Bildschirm Spriten.

Org $3e000
Load $3e000

execbase= 4
openlibrary= -408
vposr= $dff004
intena= $dff09a
intenar= $dff01c
forbid= -30-102
permit= -30-108
cop1lc= $dff080
ciaapra= $bfe001
dmacon= $dff096

start:
move.l execbase,a6 ; Multitasking aus
jsr forbid(a6)
move.w #$a000,intena ; Interrupts sperren

bsr.l initpointers
move.l #copperliste,cop1lc ; copperliste erstellen

wait: move.l vposr,d0 ; Rasterpos holen
and.l #$1ff00,d0 ; alles unwichtige ausmaskieren
cmp.l #$1000,d0 ; Zeile 16 ($10) erreicht ?
bne.s wait ; noe...

btst #6,ciaapra
bne.s wait

ende: move.l execbase,a6 ; Multitasking an
jsr permit(a6)
lea gfxname(pc),a1
jsr openlibrary(a6)
move.l d0,a6
move.w #$83e0,dmacon
move.w #$C000,intena
move.l 38(a6),cop1lc
moveq #0,d0 ; Keinen Dos-Returncode
rts

copperliste:
dc.l $00e00004
dc.l $00e20000
dc.l $008e1a64
dc.l $009039d1
dc.l $00920030
dc.l $009400d8
First:
dc.l $01200003
dc.l $01220000
Second:
dc.l $01240003
dc.l $01260000
Third:
dc.l $01280003
dc.l $012a0000
Fourth:
dc.l $012c0003
dc.l $012e0000
Fifth:
dc.l $01300004
dc.l $01320000
Sixth:
dc.l $01340004
dc.l $01360000
Seventh:
dc.l $01380004
dc.l $013a0000
Last:
dc.l $013c0004
dc.l $013e0000

dc.l $01a20fff
dc.l $01a40888
dc.l $01a60ccc
dc.l $01aa0fff
dc.l $01ac0888
dc.l $01ae0ccc
dc.l $01001200
dc.l $01800000
dc.l $0182000a
dc.l $fffffffe

gfxname:
dc.b "graphics.library",0

even

sprite1:
dc.l $50506000
dc.w %1111111111111110,%0000000000000001
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %0000000000111110,%0111111111111111
dc.w %0000000000111110,%0000000000011111
dc.w %0000000000111110,%0000000000011111
dc.w %0000000000111110,%0000000000011111
dc.w %1111100000111110,%0000000000011111
dc.w %1111111111111110,%0111100000011111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %0111111111111100,%0011111111111110
dc.w %0011111111111000,%0001111111111100
dc.w %0000000000000000,%0001111111111000
dc.l 0
sprite2:
dc.l $50596000
dc.w %1111111111111111,%0000000000000000
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111100000000000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111111111,%0111100000000000
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1000000000000000,%0111111111111111
dc.l 0
sprite3:
dc.l $50626000
dc.w %1111111111111111,%0000000000000000
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111100000000000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111100000000000,%0111111111110000
dc.w %1111100000000000,%0111110000000000
dc.w %1111100000000000,%0111110000000000
dc.w %1111100000000000,%0111110000000000
dc.w %1111100000000000,%0111110000000000
dc.w %1000000000000000,%0111110000000000
dc.l 0
sprite4:
dc.l $506b6000
dc.w %1111111111111111,%0000000000000000
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111111111111110,%0111111111111111
dc.w %1111100000000000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111111111100000,%0111111111110000
dc.w %1111100000000000,%0111111111110000
dc.w %1111100000000000,%0111110000000000
dc.w %1111100000000000,%0111110000000000
dc.w %1111100000000000,%0111110000000000
dc.w %1111100000000000,%0111110000000000
dc.w %1000000000000000,%0111110000000000
dc.l 0

initpointers:
move.l #sprite1,d0
move.w d0,first+6
move.l #sprite2,d0
move.w d0,second+6
move.l #sprite3,d0
move.w d0,third+6
move.l #sprite4,d0
move.w d0,fourth+6
rts

Nun, wie ihr am anfang seht, benutze ich auch die Org/Load kombination. Das
mache ich deshalb, weil ich dann die initpointersroutine am ende Kuerzer
halten kann, und sie nur die Low adressen eintragen muss, weil ich die
Highwerte der adressen der Datenliste schon in der Copperliste die im
Source enthalten schon eingetragen habe.

Wundert euch nicht ueber die Spritedefinitionen, sie sehen etwas komisch
aus, aber das ergenbnis muesste doch ueberzeugen.
Ich wuerde vorschlagen ihr definiert mal selber ein Logo, damit ihr etwas
Uebung in sachen Spritedatenliste bekommt. Ihr koennt allerdings auch mehr
als 4 sprites benutzten, ihr muesst dann nur, in der copperliste aus der
4 im High werte in der Copperliste eine Drei machen, und die Initpointer
routine am ende erweitern...alles Klor ?

So, nachdem wir wissen wie man sprites darstellt, wollen wir mal gucken ob
wir sie auch bewegt kriegen. Das listing von letztens ist schon dafuer
ausgelegt. So brauchen wir uns nur um die Routinen zu kuemmern, die die von
uns erstellten Sinusdaten als Koordinaten in die Spritedatenlisten
schreibt.

Gleichzeitig werde ich auch noch das mit den Sternen durchgehen, denn es
ist nicht besonders schwer, nur vielleicht ein bisschen viel zu schreiben.

Also, was brauchen wir nur noch fuer unseren Sprite Flieger

1.Eine Zeitbedingte endlosschleife, die im VB-Bereich, die neue
Koordinaten in die Spritedatenlisten schreibt.

2.Ein paar vernuenftige Sprites, obwohl ich meine garnicht mal so schlecht
fand. Aber da von euch nur sehr wenige Jeff heissen solltet ihr euch schon
mal drangeben und eigene Sprites zu entwerfen.

3.Und dann brauchen wir nur noch eine Vernuenftige Kurve...Und da liegt
dann auch das problem. Den Sinusmaker den ich benutzt habe, habe ich
geloescht. Ich werde aber mal sehen ob ich ihn nochmals besorgen kann,
werde ihn dann in die Tools oder Downloadbretter in den Jeweiligen Boxen
pumpen. O.K

Jut, das waere es, da mit der kurve kriegen wir schon irgendwie hin. Wir
geben irgenwas ein, und dann schauen wir das sich die Sprites einigermassen
sauber bewegen. Also versucht euch mal dran...es duerfte eigentlich kein
Problem mehr fuer euch sein, deshalb werde ich mal dazu kein Listing
schreiben....!

Das einzige Problem ist das mit den Sternen....

Wie kriegen wir einen ganzen Bildschirm voll Sterne, wenn wir nur acht zur
verfuegung haben, acht Sterne sind nicht gerade viel, deshalb muessen wir
da etwas anderes in augenschein nehmen. Schauen wir uns erstmal die beiden
Kontrollworte am anfang der Datenliste an.

z.b $a080,$a800

Da waere zuerst mal die $a0, sie ist die Vertikale position des Sprites. Die
$80 ist die Horizontale Position, und die $a8 sagt aus wo der Sprite auf
dem Bildschirm aufhoert.
Dieses aufhoeren ist auch gleichzeitig die laenge der liste. Die DMA findet
dann am ende der liste zwei Nullen, die ihr sagt das dieses Sprite zuende
ist. Von der DMA wird in diesen Bildschirm aufbau nichts mehr aus dieser
daten liste gelesen. So einfach ist das. Nun wenn wir aber nicht diese
beiden Nullen schreiben liesst die DMA weiter aus dieser Datenliste, sie
will aber nun neue Definitionen haben. Das ist unsere chance eine Neue
Horizontale position einzugeben. Wenn wir also jede Zeile ende der
Datenliste ohne nullen machen koennen wir sehr einfach mit einem Sprite
einen Kompletten Sternen Himmel Zaubern. Wenn wir dann noch eine Kleine
Routine Schreiben die die koordinaten noch unterschiedlich erhoeht, dann
haben wir einen Dreidimensionalen Sternen Hintergrund gebaut.

Ist doch eigentlich ganz einfach.
Damit ihr die nicht die Komplette Datenliste erfinden muesst setze ich
einen Sternenlisteneditor ins Tools Brett...Er ist relativ einfach zu
benutzen, und erklaert sich selber.
Ihr muesst dann nur noch die Abgespeicherte Datenliste in den SourceCode
uebernehmen, alles Klor.

So dann werde ich mal ein Kleines Listing bauen das eben diese sachen kann.

Org $3e000
Load $3e000

execbase= 4
openlibrary= -408
vposr= $dff004
intena= $dff09a
intenar= $dff01c
forbid= -30-102
permit= -30-108
cop1lc= $dff080
ciaapra= $bfe001
dmacon= $dff096
spr_anz= 128

start:
move.l execbase,a6 ; Multitasking aus
jsr forbid(a6)
move.w #$a000,intena ; Interrupts sperren

bsr.l initpointers
move.l #copperliste,cop1lc ; copperliste erstellen

wait: move.l vposr,d0 ; Rasterpos holen
and.l #$1ff00,d0 ; alles unwichtige ausmaskieren
cmp.l #$1000,d0 ; Zeile 16 ($10) erreicht ?
bne.s wait ; noe...
bsr.l Move_stars
btst #6,ciaapra
bne.s wait

ende: move.l execbase,a6 ; Multitasking an
jsr permit(a6)
lea gfxname(pc),a1
jsr openlibrary(a6)
move.l d0,a6
move.w #$83e0,dmacon
move.w #$C000,intena
move.l 38(a6),cop1lc
moveq #0,d0 ; Keinen Dos-Returncode
rts

copperliste:
dc.l $00e00004
dc.l $00e20000
dc.l $008e1a64
dc.l $009039d1
dc.l $00920030
dc.l $009400d8
First:
dc.l $01200003
dc.l $01220000
Second:
dc.l $01240004
dc.l $01260000
Third:
dc.l $01280004
dc.l $012a0000
Fourth:
dc.l $012c0004
dc.l $012e0000
Fifth:
dc.l $01300004
dc.l $01320000
Sixth:
dc.l $01340004
dc.l $01360000
Seventh:
dc.l $01380004
dc.l $013a0000
Last:
dc.l $013c0004
dc.l $013e0000

dc.l $01a20888
dc.l $01a40ccc
dc.l $01a60fff
dc.l $01001200
dc.l $01800000
dc.l $0182000a
dc.l $fffffffe

gfxname:
dc.b "graphics.library",0

even

sprite1:
dc.l $2A8C2B00,$80000000,$2CB02D00,$00008000
dc.l $2E352F00,$80008000,$30973100,$80000000
dc.l $32003300,$00008000,$34EA3500,$80008000
dc.l $36A93700,$80000000,$38CA3900,$00008000
dc.l $3ABD3B00,$80008000,$3C5F3D00,$80000000
dc.l $3ED43F00,$00008000,$400E4100,$80008000
dc.l $42C74300,$80000000,$44594500,$00008000
dc.l $46564700,$80008000,$48964900,$80000000
dc.l $4AD94B00,$00008000,$4CEF4D00,$80008000
dc.l $4E484F00,$80000000,$50855100,$00008000
dc.l $525B5300,$80008000,$545C5500,$80000000
dc.l $563A5700,$00008000,$580B5900,$80008000
dc.l $5A5B5B00,$80000000,$5C495D00,$00008000
dc.l $5EE05F00,$80008000,$60746100,$80000000
dc.l $62D36300,$00008000,$64D76500,$80008000
dc.l $66BB6700,$80000000,$68B46900,$00008000
dc.l $6A676B00,$80008000,$6C4B6D00,$80000000
dc.l $6E576F00,$00008000,$70327100,$80008000
dc.l $72A87300,$80000000,$74447500,$00008000
dc.l $760F7700,$80008000,$78917900,$80000000
dc.l $7AE07B00,$00008000,$7C847D00,$80008000
dc.l $7E047F00,$80000000,$80A28100,$00008000
dc.l $82F58300,$80008000,$84FE8500,$80000000
dc.l $86FD8700,$00008000,$88EF8900,$80008000
dc.l $8ACB8B00,$80000000,$8C998D00,$00008000
dc.l $8E708F00,$80008000,$906F9100,$80000000
dc.l $927E9300,$00008000,$94449500,$80008000
dc.l $965D9700,$80000000,$98A09900,$00008000
dc.l $9A539B00,$80008000,$9C459D00,$80000000
dc.l $9EAD9F00,$00008000,$A068A100,$80008000
dc.l $A257A300,$80000000,$A4ADA500,$00008000
dc.l $A673A700,$80008000,$A891A900,$80000000
dc.l $AA33AB00,$00008000,$AC9FAD00,$80008000
dc.l $AEE9AF00,$80000000,$B0C0B100,$00008000
dc.l $B2BDB300,$80008000,$B4A9B500,$80000000
dc.l $B6DBB700,$00008000,$B8C3B900,$80008000
dc.l $BAA9BB00,$80000000,$BCF8BD00,$00008000
dc.l $BEC9BF00,$80008000,$C08AC100,$80000000
dc.l $C21FC300,$00008000,$C407C500,$80008000
dc.l $C65FC700,$80000000,$C852C900,$00008000
dc.l $CA21CB00,$80008000,$CC95CD00,$80000000
dc.l $CE7ECF00,$00008000,$D02DD100,$80008000
dc.l $D29BD300,$80000000,$D4EBD500,$00008000
dc.l $D649D700,$80008000,$D80AD900,$80000000
dc.l $DAC4DB00,$00008000,$DCC2DD00,$80008000
dc.l $DE33DF00,$80000000,$E0C5E100,$00008000
dc.l $E276E300,$80008000,$E4DEE500,$80000000
dc.l $E65EE700,$00008000,$E85BE900,$80008000
dc.l $EA9EEB00,$80000000,$ECB1ED00,$00008000
dc.l $EE6FEF00,$80008000,$F07AF100,$80000000
dc.l $F295F300,$00008000,$F43DF500,$80008000
dc.l $F676F700,$80000000,$F853F900,$00008000
dc.l $FA25FB00,$80008000,$FC6BFD00,$80000000
dc.l $FE79FF00,$00008000,$00320106,$80008000
dc.l $027E0306,$80000000,$04E70506,$00008000
dc.l $06B20706,$80008000,$08D60906,$80000000
dc.l $0AB70B06,$00008000,$0CA40D06,$80008000
dc.l $0ECA0F06,$80000000,$108B1106,$00008000
dc.l $12711306,$80008000,$144A1506,$80000000
dc.l $16751706,$00008000,$18AB1906,$80008000
dc.l $1A5A1B06,$80000000,$1C4A1D06,$00008000
dc.l $1E931F06,$80008000,$205B2106,$80000000
dc.l $22322306,$00008000,$24712506,$80008000
dc.l $00000000 ; Stars made with the TTST-STAR-ED !

initpointers:
move.l #sprite1,d0
move.w d0,first+6
rts

move_stars:
lea sprite1,a0
move.w #spr_anz,d7
moveq #0,d0
ms_loop:
addq #1,d0
and.b #$3,d0 ;3 Ebenen
bne.s ms_cont
addq #1,d0 ;um nulladdition zu verhindern
ms_cont:
add.b d0,1(a0)
addq #8,a0 ;zum naechsten spr
dbra d7,ms_loop
rts

So, das ist es..Die Basis, das erste Sprite Listing ist nicht zu uebersehen,
aber ist klar, das Grundprinzip ist bei allem was die Sprites angeht
gleich, deshalb auch hier das grundschema.

Fuer die Move_Stars routine moechte ich hiermit `Amadeus` danken, er hat sie
mir zur verfuegung gestellt.

Wichtig ist das ihr im Label `Spr_anz` die anzahl der sprites angebt, da
die Moveroutine sich nach ihr richtet und nicht nach den Endmarkierungen.

Falls Muell auf dem Bildschirm erscheint, daa tippt doch im
Seka Kommandomodus `F` fuer Fill, und dann $40000 und $50000, dann fuellt
er den Bereich der angezeigt wird. Den wiegesagt muss ich ja ein Komplettes
Playfield eroeffnen damit man die Sprites sieht.

Ein weiteres Schmankerl dieses Listings sehe ich gerade, es sind die
bildschirmmasse fuer das Playfield. Sie entsprechen den Massen eines
DeluxePaint Overscan Bildes..also wer so lieber arbeitet, ich arbeite nur
in dem Modus, der kann das tun, es ist bis auf ganz bestimmte werte, auf
die man aber kommen muss wenn man ein bisschen ueberlegt, fast dasselbe.
Hier nochmal die Werte zum mitschreiben.

Diwstrt $1a64
Diwstop $39d1
Ddfstrt $0030
Ddfstop $00d8

So, das war eigentlich alles was man zu den Sprites sagen kann, wer alles
verstanden hat wird sicherlich einsehen, das sie sich eigentlich nur fuer
Sterne eignen, deshalb werde ich sie auch nur noch dafuer benutzen. Also
alles Klar.

Wenn ihr meint das ich zuviel geschrieben habe fuer eine Sache die so
schlecht zu benutzen ist wie die Sprites, dann will ich euch nur sagen das
es halt auch dazu gehoert. Denn was meint ihr wenn ihr laut wie ein
Rohrspatz irgendwo ueber sprites schimpft, und dann irgendeiner fragt
`Warum sind die denn so scheisse`, dann koennt ihr nicht sagen...Tja, das
hat der Jeff gesagt`, deshalb ist es schon ganz gut wenn ihr ein Bisschen
hintergrundwissen habt.

Den Star-ed lege ich gleich in die Bretter, ehrlich.

Versucht doch mal alle bisher erklaerten effekte zu kombinieren. Also ein
32 Farben Logo, das im Wasser bei Nacht im Wasser gespiegelt wird, so
schwer ist es nicht. Wenn man sich ein Bisschen muehe gibt dann, sieht es
schon einigermassen gut aus.

Also machen wir fuer heute mal wieder schluss...Ich muss mir noch das
Naechste Thema ausdenken. Ich schaetze aber es wird mal wieder etwas was
nicht direkt mit Assembler zu tun hat...vielleicht `Packen`, und lauffaehig
machen von effekten, ohne den Seka,oder so !
Auf jeden Fall wird es wieder eine Interressante sache. Falls ihr sie noch
nicht habt solltet ihr euch schonmal die Packer besorgen die ich
vorgestellt habe...Ihr solltet euch auch schon mal den Bytekiller genau
angucken, mit dem Arbeiten wir...so tolle Packer wie den Powerpacker
koennen wir leider fuer unsere Zwecke nicht gebrauchen, also falls ihr den
habt...vergesst ihn...er hat mit dem Was wir machen nicht das geringste zu
tun....

Tja, wie ich es versprochen habe, werden wir heute lernen wie wir unsere
Prograemmchen packen, und ausserhalb vom Seka starten.

16.Umgang mit Packern

Wie man programme ausserhalb vom Seka lauffaehig macht, das wissen wir ja
schon...mit `WO`, dann wird ein lauffaehiges programm mit allen dazu
gehoerigen Hunks uns so erzeugt..Der source muss dann allerdings von Seka
in den Speicher gelegt worden sein, also ohne Org/Load, da er sonst nicht
weiss wo das programm liegt und wie lang es ist. Also ohne Org und Load
geht ja noch, aber wir wissen ja auch nicht wo dann unsere Bilderchen und
Sounds liegen, dann koennten wir nicht damit arbeiten, das waere dann ja
nicht so gut, oder ?

Also es muss da was anderes geben, was uns als Programmieren mit festen
Adressen bei unserer arbeit unterstuetzt...

Keine Bange, das gibt es im Seka auch..aber wir muessen erstmal lernen wie
man damit umgeht.

Erstmal muss man wissen wo alles liegt, das heisst, man muss den totalen
ueberblick ueber sein Werk haben...

Das ist ja nicht so schwer...es koennte so aussehen

Hauptprogramm mit Abspielroutinen und so, wird nach $30000 Assembliert und
ist $1267 Bytes lang. Wir notieren uns..

`Mainprog. $30000 - $31267`

Die Musik liegt wenn sie nicht direkt mit >extern in das Mainprog
eingebunden wurde bei $40000. Ein groesseres Modul belegt dann z.b 75 KB.
Das waeren dann 75*1024 = 76800 in Hexa = $12c00

`Modul $40000 - $52c00`

Als letztes, weil wir nichts besonderes gemacht haben kommen noch die
Planes. Nehmen wir ein 32 Farben Pal bild, also 5*10240 = 51200 Hexa $c800.

`Plane $53000 - $5f800`

O.K, das waere mal ein Schnoedes geruest. Wir haben die einzelnen Adresse
der Sachen die wir so brauchen.
Wenn wir also das listing assemblieren, legt es erstmal den Source ab. Um
den rest den wir natuerlich auf Diskette haben auch noch in den Speicher zu
kriegen druecken wir, wie bekannt, `Y`...dann wird es an die
Vorgeschriebenen adressen geladen.

Im groben kann man jetzt ja sagen das unser `Intro` den bereich von $30000
bis $60000 belegt, natuerlich mit zwischenraeumen, aber so ungefaehr. Und
genau das alles muss man natuerlich noch abspeichern. Aber vorher muessen
wir mal gucken was denn dort noch steht wo wir nichts hingeladen haben. Da
koennte allesmoegliche stehen, gehen wir mal davon aus das irgendwelcher
muell von irgendwelche programmen dort liegt die schon lange nicht mehr
laufen, vielleicht noch ein rest vom Soundtracker oder Cygnus ED...

Wenn wir das abgespeichert haben und das dem Packer zum packen geben, dann
wird es nicht viel kuerzer, weil sich dieses zeug unheimlich schlecht
packen laesst, also fuellen wir vor dem Assemblieren, unseren Arbeits
speicher mit Nullen, das wuerde in unserem Beispiel so aussehen...

Seka>F
Start)$30000
End)$60000
Data>0

Dann wird alles gefuellt. Jetzt koennen wir assemblieren und die Sachen
einbinden.
Nachdem das passiert ist muessen wir das Abspeichern. Dazu benutzen wir den
Befehl `WI`, was ausgeschrieben `Write Image` heisst. Es bedeutet nicht
anderes das man einen speicherbereich den man selber eingeben kann
abspeichert. Allerdings wird wirklich nur der Speicherbereich
abgespeichert, sonst nicht...Also ist das auch nicht Lauffaehig. Um es ans
laufen zu kriegen muessten wir es mit einem Monitor nach $30000 laden und
mit dem Sprung Kommando den PC auf das Intro verbiegen..dann wuerde es
laufen.

Allerdings, wenn wir die Hunks selber davorsetzen, wer noch nicht weiss was
Hunks sind, wartet bis ich diesen Satz fertig habe, dann schreibe ich was
dazu, dann belegt das Intro satte 198 KB, und das ist ein Bisschen Viel
fuer ein Intro.

Die Hunks von denen ich dauernd spreche, sind vor jedem Ausfuehrbarem
Programm das der Amiga laden kann. Sie sagen ihm wo er das Programm
hinlegen soll wenn es eine feste Adressen hat, oder sie sagen ihm wie er
das Programm umschrieben soll wenn er es irgendwoanders hinlegen will, weil
er halt da wo es hin soll keinen Platz mehr hat. Je nach programm gibt es
mehr oder weniger Hunks, und sie von Hand ausrechnen, das ist einigen
wenigen Programmierern vergoennt, uns nicht..also lassen wir auch die
finger davon, O.K.

So, reden wir weiter ueber das vermeintliche Intro. Wiegesagt 198 Kilobyte
ist ne ganze menge naemlich 202752. Was meint ihr wie lange es dauert bis
er das eingeladen hat. Und die Hunks muessten woir ja auch davorsetzen, und
die einzige moeglichkeiten die ich kennen waere das mit WO vom Seka
abzuspeichern, aber ich hatte ja schon gesagt, das das nichts ist.

Also, wir muessen es kuerzer kriegen und startbar machen.

Jetzt will ich mal kurz sagen was ein Packer macht...wie er das macht kommt
hinterher !

Ein packer verkuerzt fertige Programme oder Dateien, und erzeugt
Lauffaehige Programme daraus. Aha, das hoert sich ja schonmal ganz gut aus.
Wenn wir das Ding durch einen Packer jagen, haetten wir zwei Fliegen mit
einer Klappe geschlagen.

Packen wir erstmal dieses Teil, dann werde ich euch noch erklaeren was es
mit dem Packen ueberhaupt auf sich hat.

Also wenn ihr alles vorbereitet habt..ich meine das mit dem Fuellen und Wi
und so, dann muesstet ihr jetzt das Image von dem Intro auf Diskette haben.
Starten wir also den Packer...Ihr koennt im Prinzip alle packer nehmen die
Images packen koennen, das es aber nicht soviele gibt, habe ich euch einen
in die Box gepumpt. Bytekiller heisst das Ding.

Starten wir also den Bytekiller 1.3, den koennt ihr von mir bekommen.

Als erstes fragt er wie der Seka nach arbeitsspeicher...

Allocate work space (KBbyte)

Er will wissen wie lang die zu Packende, wir von Fach nennen das
Komprimieren, Datei ist.
Da muessten wir in unserem Fall 200 eingeben, ist aufgerundet, aber es ist
besser ein bisschen zuviel als zuwenig.
Nachdem ihr das eingeben habt Allociert er den Platz.

Dann kommt...

Filename to load

Hier muesst ihr den Filenname eingeben. Falls der Pfad nicht auf dem
Directory liegt muss er auch noch eigegeben werden, also z.b

Df1:Images/Intro.ima

dann laedt er es...

Dann gibt er die Orginal laenge an. Hier koennt ihr nochmal sehen ob ihr
alles richtig abgespeichert habt.

Jetzt fragt er

Offset (max. $0800) :$

Er will die Suchtiefe wissen, was das ist kommt hinterher bei der
erklaerung der Funktionsweise von Packalgorhytmen.
Auf jedenfall soviel..Je hoeher die Suchtiefe ist desto effenzienter
arbeitet der Packer, desto kuerzer wird das Programm.

Dann verschwindet der Bytekiller fuer eine kurze oder laengere Zeit, der
Bildschirm aendert staendig die Farbe....Der Meister arbeitet jetzt..

Irgendwann kommt er dann wieder, mit einer Neuen Frage

create Executable File or Data File ? (e/d) :

Jetzt will er wissen was er da gepackt hat, und als was das behandelt und
abgespeichert werden muss. Hier muessen wir natuerlich Executable also `e`
druecken, denn es soll ja lauffaehig sein.

Nu, nachdem er das weiss hat er im speicher die noetigen Hunks erzeugt, es
fehlen allerdings noch zwei werte. Naemlich..

Locate File at :$

Da muessen wir eingeben wohin er das File entpacken soll, das heisst in
unseren speziellen Fall, $30000, und das geben wir auch ein.

Nun kommt noch eine Besonderheit des Bytekillers, die Viele andere von
diesen Imagepackern nicht haben. Er fragt noch wo er das entpackte
anspringen soll. Denn es koennte ja sein das ein Bild bei $20000 liegt und
das Mainprogramm bei $30000, dann muss man ja bei $30000 anspringen und
nicht bei $20000. Viele Packer nehmen aber die Locateadresse auch als
einsprungadresse, der Bytekiller nicht, er fragt

Jmp in :$

Dort geben wir die Startadresse ein. In unserem Fall auch $30000

Jetzt fragt er nur noch nach dem namen unter dem er das gepackte zeug
abspeichern soll. Haben wir den eingegeben macht er das auch ganz brav.
Dann kehrt er wieder ins CLI zurueck, dort kann man dann das ergebnis laden
und anschauen. Es kommt allerdings vor das ein packer mal ein Programm
nicht richtig entpackt kriegt oder so. Deshalb haben die meisten packer
einen Decrunch effekt, der soll anzeigen wann der Packer arbeitet und wann
nicht mehr, und wann das Programm dran ist. So kann man feststellen ob der
Amiga schon abgestuerzt ist, oder ob er nocht entpackt. Alles Klor ?

So, dann haben wir auch schon was gepackt...jetzt will ich auch noch etwas
ueber den Packvorgang selbst sagen. Ich werde das packen anhand von zwei
sehr einfachen Methoden erklaeren.

Die erste Methode des Packen ist die einfachste und eignet sich
hervorragend fuer bilder.

Der Packalgorhytmus geht den zu packenden speicher bereich durch,
und,vergleicht ein Byte immer mit dem davor. Sobald er auf zwei gleiche
trifft, schaut er sich noch das naechste an. Findet er nach dieser methode
drei gleiche aufeinander folgende Bytes, dann schreibt er anstatt

$31 $31 $31

jetzt nur noch $03 $31, dann weiss er beim entpacken das jetzt 3 mal die
$31 kommt. Das bringt zwar nur 1 Byte, aber was sagt ihr zum beispiel bei
unserem versuch. Da sind hinter dem Mainprog auch ungefaehr 63488 Nullen,
weil wir das ja gefuellt haben, die verschwinden dann auch in drei bytes,
naemlich so

$00 $f8 $00

Das sagt der Entpackeroutine `Jetzt kommt $f800 mal die 0, und das ist doch
eine Grossartige ersparnis, findet ihr nicht ?

Jetzt kommt die zweite einfache methode.

Der packalgorhytmus ueberprueft den Speicher bereich auf gleiche Byte
folgen, wie zum beispiel $31 $45 $37 $37, findet er davon mehrere im
Speicher, dann gibt er einer solche Kombination einen Namen, und dann steht
nur noch der Name dieser Kombination dort, die erlaubte laenge der
kombinationen ist von Packer zu Packer verschieden. Hier tritt auch der
offset vom Bytekiller in aktion, er sagt dem Packalgorhytmus in welchem
Umkreis er nach diesen Kombinatioenen suchen soll, wenn ihr da zum beispiel
einen Wert eingebt, der ihn im bereich von einem KB suchen laesst ist diese
methode halt nicht so effizient wie wenn man sie in einem Bereich von
Hundert KB, oder dem ganzen listing suchen laesst. Aber wenn wir wirklich
das ganze listing untersuchen wuerden, dann wuerde ein normales DemoImage,
von 100 Kilobyte, Tage dauern, deshalb der Offset !

Es gibt natuerlich viele dutzend Packverfahren, aber die sind teilweise so
kompliziert das nur der Programmierer weiss was da passiert, wer mehr
darueber wissen schroiebt mir einen Kleinen brief, dann werde ich noch ein
Paar erklaeren, aber ich glaube nicht das daran einer interresse hat.

Packer arbeiten aber nicht immer nur mit einem der verfahren, sondern haben
mehrere drauf, manche gucken sich erstmal den Speicher bereich an, und
entscheiden dann nachwelchem Prinzip sie das machen. Bei manchen kann man
es waehlen, dann muss man aber schon ein bisschen bescheid wissen, denn ein
Routine mit der man immer bombig Intros packen kann, versagt
hoechstwarscheinlich an einer Textdatei, genauso umgekehrt. Die letzte
gruppe von Packern benutzt halt mehrere packalgorhytmen. Sie gehen auch
zwei oder drei mal ueber den Speicher, und holen mit jedem Weiter
verfahren, immer mehr raus.

Naja, wenn ihr jetzt ein bisschen uebung mit dem Bytekiller habt dann
faellt es euch auch nicht schwer die anderen zu verstehen, also viel spass
bei der Suche nach eurem Lieblings packer.

Achja, probiert doch ein bisschen mit dem Offset rum, schaut euch dochmal
an was der bytekiller mit Offset $0050 aus einer datei macht, und was mit
Offset $0800. Ist Vielleicht mal ganz interressant, zu wissen wo er
aufhoert effektiv zu werden, oder ab wann er einfach zu langsam wird !

So, nachdem wir wissen wie wir unsere sachen auch weitergeben und damit
Strunzen koennen, macht es doch gleich wieder spass, oder ?

In diesem Teil werde ich am anfang erstmal ein paar neue Befehle
vorstellen, sie aber nicht in irgendein programm verpacken. Ich bin sicher
das ihr diese Befehle irgendwann gut gebrauchen koennt, und dann erinnert
ihr euch bestimmt daran.

Als ersten nette befehl kommt der Swap-befehl. Er kann auf die Adress und
Datenregister zugreifen, und vertauscht oberes Wort mit dem unteren.
Aus...

$40981546

wird...

$15464098

Naja, hoert sich ja nicht besonders an. Aber den grund warum ich den Befehl
vorstelle, ich hatte ja gesagt ich stelle nur die Befehle vor die man
wirklich zum Intro schreiben braucht, ist, das er sich ganz besonders gut
fuer manipulationen an Copperlisten eignet, wenn es darum geht Adressen
einzutragen.
Wie ihr wisst sind die Adresse in einer Copperliste immer getrennt in
Highwort und Lowwort.

Um die adresse $40000 als adresse fuer die erste Bitplane festzulegen, muss
man das ja die beiden befehle..

$00e0 $0004
$00e2 $0000

schreiben. Naja wenn wir wissen wo das liegt koennen wir das ja relativ
einfach eintragen. Aber was ist wenn wir zum Beispiel die Adresse der
Sprite datenliste in die Copperliste eintragen wollen. Dann gibt es schon
schwierigkeiten. Wir koennten das zwar ermitteln, weil wir ja das mainprog
mit Org/Load fixieren. Aber die Adresse der Datenliste aendert sich ja
jedesmal wenn wir erweiterungen am listing vornehmen, also das ist es nicht.
Die moeglichkeit ist also, sich mit Move.l # die Adresse in ein Datenregister
zu holen, um dann die Zwei worte in die Copperliste einzutragen.
In der Praxis sieht das dann so aus...

Move.l #Sprite1,d0
Move.w d0,Copperlist+2
Swap d0
Move.w d0,Copperlist+6
Rts

Copperlist:
dc.w $0120,$0000
dc.w $0122,$0000
dc.l $-2

Sprite1:
..Hier liegt dann die Spritedefinition

Die Funktion muesste eigentlich Klar sein...

Dann kommt ein Kuerzel das man z.b hinter die Befehle Move, Add und Sub
klemmen kann.

Naemlich `q`...

Es bedeutet dann soviel wie Quick, man kann damit allerdings nur
vorzeichenbehaftete 4 bit werte uebertragen. Also von -8 bis +7.
Quick wird es genannt weil es sehr schnell geht, denn der wert wird direkt
in den Befehl mit eingebaut. Es kommt nur ein Wort bei raus, und das der
Prozessor das Schneller abarbeiten kann ist ja wohl jedem Klar..ne.

Wenn irgendwie moeglich solltet ihr wenn irgendwie moeglich bei Sprung
operationen immer die Branch befehle vor ziehen. Also anstatt

Jmp und Jsr...immer Bra und Bsr...

Denn dort ist sowieso der Befehl kuerzer. Man kann ihn aber auch von hand
noch kuerzer machen. Indem man einfach .S fuer kurze Distanz, oder .L fuer
lange Distanz eingebt. Wenn ihr euch vertut und Kurz statt lang eingebt ist
das nicht so schlimm, der Seka merkt das und verbessert euch. Falls aber
umgekehrt, wenn ihr also .L schreibt, aber .S moeglich waere, tja dann habt
ihr pech gehabt, da mischt sich der Seka nicht ein.
Dieses Kuerzel habt ihr aber auch schon in meinen Listings gesehen. Es
klebt immer an den Bne`s, Beq`s, und an den Bsr`s dran. Naja jetzt wisst
ihr auch was das bedeutet.

Eine weiteres Kuerzel kann man noch in den Daten fuer den befehl
unterbringen. Und zwar wenn man mit Adresse arbeitet die eigentlich nur ein
Wort lang sind, dann waere es unsinnig ein ganzes langwort dafuer zu
verschwenden. Es koennte dann so aussehen...

Vorher
Move.l $00000040,$00000050

schiebt das langwort von $40 nach $50
Das ergibt nach dem Assemblieren

2 Bytes fuer den Move befehl
4 Bytes fuer die Adresse $00000040
4 Bytes fuer die Adresse $00000050

----------------------------------
10 Bytes

Wenn ihr aber die Kuerzel benutzt, sieht es so aus.

Move.l $0040.w,$0050.w

belegt..

2 Bytes fuer den Move befehl
2 Bytes fuer die Adresse $00000040
2 Bytes fuer die Adresse $00000050

----------------------------------
6 Bytes

Denkt daran, die schreibweise $0040 aendert nichts, das .w muss dahinter,
denn ich kann den Befehl mit den Langwoertern auch so schreiben.

Move.l $40,$50

Es bleiben langwoerter, und als solche werden sie eingesetzt.

Naja, wir haben also im Beispiel 4 Bytes gespart, was das ist nicht viel,
das meint ihr aber auch nur, wenn ihr mal euren ersten Bootblock
beschreibt, dann werdet ihr noch an mich denken, den dort zaehlt jedes
Byte.
Leider gibt es kein Kuerzel .B, denn dann waere die ersparnis noch
groesser.

Jetzt kommt ein Befehl den ich manchmal benutze, um schnell irgend was
auszutauschen.
Stellt euch vor ihr habt irgendwann mal eine tolle Routine geschrieben, die
einen Haufen sachen ausrechnet, und die ergebnisse in allen Datenregistern,
das heisst von 0 bis 7. Und irgendwann schreibt ihr mal ein programm das
halt solche werte braucht, und ihr erinnert euch an die alte
Routine...Jetzt merkt ihr aber das die routine etwas anders ist, und zwar
so das das Ergebnis was die neue Routinen in d6 braucht, in d7 liegt, und
das ergebnis was sie in d7 braucht, aber in d6 liegt.
Jetzt gibt es drei moeglichkeiten dieser Situation Herr zu werden.

Wir schreiben eine der beiden Routinen um...was aber bei 20 Kilobyte
SourceCode nicht gerade leicht ist. Und da Programmierer grundsaetzlich
faule Menschen sind, faellt das also weg.

Also muessen wir gucken wie wir da sachen vertauscht kriegen, so viel ist
klar. Man kann es mit dem Stack, oder mit einer X-beliebigen adresse
machen. Hier die zwei moeglichkeiten.

Move.l d6,$40000
Move.l d7,d6
Move.l $40000,d7

Oder

Move.l d6,-(a7)
Move.l d7,d6
Move.l (a7)+,d7

Die zweite moeglichkeit ist etwas komplizierter, aber wird im Source
kuerzer als die erste. Der effekt ist derselbe, was vorher in d6 stand
steht jetzt in d7, und umgekehrt.

Aber jetzt tritt der neue Befehl auf die Buehne. Er heisst schlicht und
ergreifend `Exchange` kurz `EXG`, und mit ihm passiert dann die ganze
sachen in einem Wort.

Exg d6,d7

Das wars...Im prinzip ist es dasselbe wie die kuerzere Variante von eben,
denn der Prozessor macht genau das auch. Nur wenn er es macht geht es
natuerlich viel schneller. Alles Klor ?

Es kommt immer mal wieder vor das man, wenn man ein Intro programmiert, die
einzelnen Effekte in Modulen abarbeiten laesst. Dabei kann es vorkommen das
man die module an verschiedenen Tagen programmiert, und nicht darauf achtet
welche Arbeits und datenregister man den nun noch frei hat. Und wenn man
ein Intro mit 20 Effektmodulen hat, dann wird es eng mit den registern, mit
denen man doch so hervorragend arbeiten kann, oder.

So, da gibt es eine ganz einfache sache. Wir legen uns jeden die inhalte der
Datenregister und der Adressregister jedes Modules, einen Sicherungsplatz
an, indem wir nach abarbeitung des Modul alle sachen abspeichern, und bevor
wir das naechste mal mit dem modul anfangen, holen wir uns einfach die
sachen wieder rein. Das saehe, wenn wir es jetzt machen muessten allerdings
etwas lang, naemlich so aus.

Move.l d0,$40000
Move.l d1,$40004
....
....
Move.l a0,$40020
Move.l a1,$40024

naja das waere ja ein bisschen lang. Das haben sich die Leute die den 68000
gebaut haben auch gedacht, und deshalb fuer den move befehl noch ein Kurzel
ausgedacht. naemlich `m`. Das `m` steht fuer viele oder mehrere, das heisst
wir koennen mit einem Befehl direkt mehrere register retten. Wir koennen
sie dann auf den Stack oder einfach in den Speicher schreiben. Der Befehl
der den Kompletten registerinhalt nach $40000 rettet sehae dann so aus.

Movem.l d0-d7/a0-a6,$40000

Er erhoeht auch selbsttaetig die adresse wo er das hinschreibt.
Wir muessen allerdings nicht alles retten, sondern wir koennen auch ganz
bestimmt retten. Wenn wir nur d0,d1,d4,d5,d7,a0,a1,a2 und a6 retten wollen
schreiben wir einfach

Movem.l d0-d1/d4-d5/d7/a0-a2/a6,$40000

Also ihr seht..ziemlich cool der Befehl. Natuerlich geht es zurueck
genauso. Der Befehl der den ersten Movem befehl von mir rueckgaengig macht
heisst

Movem.l $40000,d0-d7/a0-a6

Mehr nicht...

Nach moeglichkeit den Clr befehl in langen schleifen und auch sonst meiden.
Denn er dauert einfach zu lange. Ein Ersatz fuer diese Schleife

Lea $40000,a0
Move.w #$ffff,d0
Clearloop:
Clr.l (a0)+
Dbf d0,clearloop
Rts

waere ohne Clr.l, und sehr viel schneller...

Lea $40000,a0
Move.l #$ffff,d0
Clearloop:
Moveq.l #0,(a0)+
Dbf d0,clearloop
Rts

All right ?

Wenn ihr mal etwas groessere sachen programmiert, und ihr kleine Zeitliche
abstaende braucht, dann koennt ihr euch ja mit der einfachen Warteschleife
helfen die ihr schon kennt. Aber gerade bei sachen in Echtzeit, muss man
manchmal kurze abstaende warten. Je nach dem sind die Abstaende sogar so
kurz, das man noch nicht mal eine kleine Routine dazwischensetzen kann.
Dazu gibt es einen netten Befehl, der eigentlich nichts macht. Und so
heisst er auch..

`Nop` - No OPeration

Der Prozessor braucht Vier Taktzyklen um zu merken das der Befehl garkeinen
gueltigen Code ergibt, dann springt er weiter. Solche Codes gibt es noch
viele andere. Die Erbauer von Prozessoren, nehmen immer einen davon der
dann den Namen `Nop` kriegt, und dann ist das so.

So jetzt kommt die Aufstellung der Sprungkriterien fuer die Befehle aus der
Gruppe der Bedingten Branch Befehle. Zwei davon kennt ihr ja schon, Bne und
Beq, ich zaehle sie aber der komplettheit wegen nochmal auf.

Das B davor muesst ihr euch immer denken, da ich nur die bedingung
beschreibe.

CC = Carry Flag nicht gesetzt (Carry Flag wird spaeter erklaert)

CS = Carry Flag gesetzt

EQ = Zero Flag gesetzt, oder letzte Operation gleich 0

GE = Groesser oder Gleich > =

GT = Groesser, aber mit vorzeichen >

HI = Groesser >

LE = Kleiner oder 0, aber mit vorzeichen <

LS = Kleiner oder Gleich < =

LT = Kleiner, aber mit vorzeichen <

MI = Wert im Minus Bereich -

NE = Zero Flag nicht gesetzt, oder letzte operation nicht 0 Extern "test.iff",Quelle,Dateilaenge

Execbase = 4
Allocmem = -$c6
Freemem = -$d2

; Makros

FINDCHUNK MACRO

Fc?0:
cmp.l #?1,(a0) ; ist parameter gefunden ?
nop
adda.l #2,a0 ; adda veraendert keine Flags
beq fc1?0 ; ja
dbra d0,fc?0 ; nein weiter
sub.l d0,d0 ; nicht gefunden Chunk laenge
bra.s fc2?0
fc1?0:
adda.l #2,a0
move.l (a0)+,d0
fc2?0:
ENDM


; Programm

Start:
lea.l quelle,a0
move.l #dateilaenge2-1,d0
FINDCHUNK "FORM"
beq stop
cmp.l #"ILBM",(a0)
bne stop
FINDCHUNK "BMHD"
move.b 10(a0),d0
cmp.b #1,d0
bhi stop
move.l a0,bmhdptr
move.w (a0),breite
move.w 2(a0),hoehe
move.b 8(a0),tiefe
move.l Execbase,a6
sub.l d0,d0
move.w breite,d0
add.w #15,d0
asr.w #3,d0
andi.w #$fffe,d0
mulu hoehe,d0
sub.l d1,d1
move.b tiefe,d1
mulu d1,d0
move.l d0,bodysize
moveq #1,d2
asl.l d2,d1
asl.l #1,d2
add.l d2,d0
move.l d0,filesize
move.l #$10003,d1
jsr allocmem(a6)
move.l d0,outfile
beq stop
lea quelle,a0
move.l #dateilaenge-1,d0
FINDCHUNK "CMAP"
beq nocmap
move.l outfile,a1
add.l bodysize,a1
move.l a1,colormap
CMLOOP:
sub.w d1,d1
move.b (a0)+,d1
lsl.w #4,d1
andi.w #$0f00,d1
move.b (a0)+,d2
andi.w #$00f0,d2
ori.w d2,d1
move.b (a0)+,d2
andi.w #$000f,d2
ori.w d2,d1
move.w d1,(a1)+
sub.l #3,d0
bgt cmloop
nocmap:
lea quelle,a0
move.l #dateilaenge-1,d0
FINDCHUNK "BODY"
beq stop
subq.l #1,d0
move.l bmhdptr,a1
move.b 10(a1),d1
move.l outfile,a1
beq copy
Dekompress:
clr.w d1
move.b (a0)+,d1
cmp.b #128,d1
beq noop
bhi double
copybytes:
move.b (a0)+,(a1)+
dbra d1,copybytes
bra.s noop
double:
neq.b d1
move.b (a0)+,d2
doubleloop:
move.w #$000f,$dff180
move.b d2,(a1)+
dbra d1,doubleloop
noop:
dbra d0,dekompress
bra.s stop
copy:
move.w #$00f0,$dff180
move.b (a0)+,(a1)+
dbra d0,copy
stop:
sub.l d0,d0
sub.l d1,d1
sub.l d2,d2
sub.l d3,d3
move.w breite,d0
move.w hoehe,d1
move.b tiefe,d2
move.l outfile,a0
move.l filesize,a1
adda.l a0,a1
ILLEGAL

clean:
move.l execbase,a6
move.l filesize,d0
move.l outfile,a1
jsr freemem(a6)
ILLEGAL


BMHDptr: dc.l 0
bodysize: dc.l 0
colormap: dc.l 0
outfile: dc.l 0
filesize: dc.l 0
breite: dc.w 0
hoehe: dc.w 0
tiefe: dc.b 0
even
Quelle: blk.b dateilaenge,0
Quelle1:

Wie gesagt, ich habe sie abgetippt, aus dem Copperkurs der Amiga.
(Ich muss das dabeischreiben, wegen dem Copyrights und so)
Wundert euch also nicht ueber den Programmierstil.

Da ihr aber jetzt, wenigstens von Sinn her viele Befehle kennt, dann
koenntet ihr euch ja mal an das Konverterlisting setzen und es
Anaylisieren.
Viel Spass dabei....

So jetzt erstmal wieder was zum Bueffeln, ein etwas schwieriges
unterfangen. Wenn wir es aber beherrschen dann ist die grosse Lernphase der
Kunst des Introprogrammirens leider schon vorbei.
Kommen wir zu einem Chip des Amigas dem er einen grossen teil seiner
Grafischen moeglichkeiten verdankt.

17.Der Blitter

Vielleicht habt ihr schon irgendwelche Schauermaerchen ueber den Blitter
gehoert, und ich gebt auch zu das es ziemlich Haarig ist in erstmal zu
verstehen. Aber wenn man das gefressen hat dann ist er relativ einfach. Und
die wirklich schweren Sachen machen wir ja jetzt noch nicht. Ich meine
jetzt VectorenGrafik und solche Tollen sachen Wie Filled Bobs, oder Sogar
Stencil Bobs. Auch werden wir nicht versuchen neue rekorde zu brechen,
obwohl das jeder der ihn beherscht versucht, wir wollen einfach nur ein
bisschen Kopieren und so...

Erstmal: Wofuer ist der Blitter eigentlich da ?

Nun im Amiga ist er fuer die Textausgabe da, er zeichnet die Fenster und
schaufelt beim laden von Programmen die Daten aus den puffern in den
Arbeitsspeicher, und decodiert sie gleichzeitig. Ganz schoen wichtig das
Ding, oder ?

Ja wichtig ist er, und schnell - Er kopiert mit einer Geschwindigkeit von

16 Millionen Punkten in der Sekunde

Das ist verdammt schnell, ich glaube der Prozessor liegt knapp unter einer
Millionen. Also liegt es doch auf der Hand das wir den fuer uns Arbeiten
lassen.

Wie sprechen ich den Blitter an ?
Ganz einfach ueber die Hardwareregister, von denen wir schon soviele kennen
und schaetzen gelernt haben. Fuer alles moegliche gibt es Register. Die
Aufgabe von uns ist nur diese register richtig zu initialisieren, den rest
macht der Blitter ganz alleine.

Was uns am Blitter interressiert, ist das kopieren mit ihm.

Nun dafuer stellt und der Blitter 3 Quellbereiche und 1 Zielbereich zur
verfuegung.
Man kann immer nur von Quellbereich in Zielbereich Schreiben, deswegen
heissen sie auch so.
Das Heisst von A,B,C, so heissen die Quellen, nach D, so heisst das Ziel.

Allerding kann man auch die Quellen miteinander verknuepfen, aber das
Spaeter. Uns Interresiert einfaches kopieren.

Der Blitter braucht zum Kopieren einige Angaben, die Operation und das
Ausmass betreffend.

Erstmal muss er wissen, welche seiner Kanaele daran beteiligt ist. D ist
meistens dabei, und A wenn es ums einfaches kopieren geht auch. Sagen wir
also A und D.
Dann muss man eingeben ob irgendwelche verknuepfungen waehrend der Aktion
durchgefuehrt werden sollen. Das ist in unserem Fall ja nicht der Fall.

Dann muessen wir dem Blitter sagen wo der bereich ist von dem wir die
Daten abholen, das ist der Quellbereich. Da setzen wir mal $50000 ein.

Nun kommt der Zielbereich, D genannt, dort setzen wir mal $60000 ein.

Als letztes muss der Blitter noch wissen wieviel er von dort nach da
kopieren soll. Sobald wir das eingegeben haben startet er auch die Aktion,
denn das register indem die Angabe ueber die Groesse liegt ist ein
Stroberegister (Sprich: Strohp) Das heisst, bei Schreibzugriff wird
gleichzeitig eine Aktion gestartet. In dem Fall ist es der Blitter.

Mit diesem Register fuer die laenge ist es aber etwas schwerer da man nicht
einfach eingibt `So 100000 Bytes`, sondern man muss in gedanken aus der
Anzahl der Bytes ein Rechteck bauen, und dem Register die Seitenlaenge A
und B uebergeben. In manchen Faellen reicht es wenn wir die Quadratwurzel
ziehen, und eine Grade zahl rauskommt. Bei 100 oder 10000 waere das zum
Beispiel der fall. Aber das sind halt die Ausnahmen, also schauen wir mal
wie wir das machen koennen.

Bis auf eine Ausnahme arbeitet der Blitter nur mir Woertern, also muessen
wir den bereich den wir kopieren wollen in Woertern angeben.
Also, wenn wir z.b 30000 Bytes von $50000 nach $60000 kopieren wollen dann
muessen wir den Bereich ungefaehr so definieren

300 Zeilen zu 100 Bytes bzw. 50 Woertern

das heisst 300*50, sind 30000, so einfach ist das.
Der Bereich ist aber Beschraenkt, die maximale Zeilenzahl betraegt 1024
Zeilen, wobei man da 0 als Zeilen zahl angeben muss, die nimmt er dann als
1024, da 0 ja eine Unsinnige angabe waere. Die maximale Breite betraegt 64
Woerter, wobei wieder das mit der 0 ist. Wir koennen also mit einem Schlag
128 KB durch den Speicher Schleudern, anders kann man es nicht mehr
ausdruecken. Diese laengen und breiten angaben muessen wir aber auch
irgendwie unterbringen in dem Registers unterbringen. Die ersten 6 Bits
sind die Breite, und die restlichen 10 sind fuer die laenge.

Wichtig ist aber wie wir die Masse da rein kriegen, einfach 300 * 50 kann
man ja nicht schreiben, dann waere das System mit dem rechteckigen bereich
wieder hin. Also muessen wir die LaengenBits auch noch oben schieben.
Stellen wir uns das Leere BLTSIZE register , so heisst es naemlich, mal
bildlich vor.

I Zeilen I Breite
0000000000000000
I I

Schreiben wir einfach mal die Zeilen zahl 9 rein. Mit Move.w #$9,Bltsize

I Zeilen I Breite
0000000000001001
I I

So jetzt steht die laenge aber in den Registern fuer die Breite, wie
kriegen wir die rueber. Ganz einfach, mal 64, dann wird naehmlich alles nur
um 6 Bit nach oben gesetzt. Also es sieht dann so aus

I Zeilen I Breite
0000001001000000
I Zeilen I

Naja, und weiter...Hmmm ich will euch nicht damit Qualen, es waeren
ziemlich aufwendige rechnungen um die Sachen vom Programm da reinzusetzen.
Erstmal reicht es wenn wir das vom Seka machen lassen. Bei unserem Beispiel
saehe die Initialisierung des BLTSIZE registers ungefaehr so aus.

Move.w #300*64+50,BLTSIZE

So einfach ist das....

Damit haetten wir schon alle schritte die fuer ein Kopieraktion noetig
waeren beisammen. Schreiben wir sie mal auf, mit SourceCode-beispiel.

Quelle A und Ziel D angeben, keine Verknuepfung.

Move.w #%0000100111110000,BLTCON0

Adresse der Quelle A angeben

Move.l #$50000,BLTAPT

Adresse des Ziels D angeben

Move.l #$60000,BLTDPT

Laenge und Breite angeben, und gleichzeitig Aktion starten

Move.w #300*64+50,BLTSIZE

Damit waere die Sache dann fertig...Komplett ist der Aufruf allerdings noch
nicht. Es fehlen noch ein paar sachen die sehr viele moeglichkeiten
mitsichbringen, aber beim normalen Kopieren nur hindern.

Da waeren erstmal die Modulowerte fuer die Quelle und das Ziel.
Ja, die Modulos mit denen wir bei den Bitplanes schon so toll arbeiten
konnten, hat der Blitter auch. Sie geben an wieviel Bytes, hier ist die
Ausnahme von der ich Sprach, nach jeder zeile uebersprungen werden sollen.
Es gibt sie fuer die Quellbereiche A,B und C sowie fuer den Zielbereich D.
Wir muessen diese Register natuerlich auf 0 setzen, damit es wirklich eine
Kopie gibt.

Desweiteren sind da BLTAFWM und BLTALWM. Sie sind fuer das erste und das
letzte worte jeder zeile zustaendig. Mit ihnen kann man eine And Maske
ueber eben diese beiden Woerter legen. Die And funktion kennt ihr ja schon,
deshalb will ich das hier auch niocht nochmal erklaeren.
Also, die muessen wir natuerlich vollschreiben, alles gesetzt brauchen wir.

Und dann als letztes muessen wir natuerlich auf den Blitter warten bis das
er fertig ist, denn sonst waere jede weitere Initialisierung sinnlos, denn
wenn er arbeitet nimmt er keine Befehle entgegen.

So wie warten wir den auf den Blitter ?
Ganz einfach, im DMACON register gibt ja auch den Blitterkanal, solange der
Blitter arbeitet wird der kanal geschlossen. Wir koennen das zwar nicht im
DMACON register erfahren, da es ja kein Leseregister ist. Aber wofuer gibt
es das DMACONR register. Dort fragen wir einfach das Bit 6 ab. Es gibt noch
eine Zweite moeglichkeit, aber die ich hier vorgestellt habe benutze ich
selbst schon immer und habe da nie Schwierigkeiten mit gehabt. Also eine
Komplette Kopierroutine sieht dann so aus.

Move.w #$ffff,BLTAFWM ; Maske fuer erstes Wort
Move.w #$ffff,BLTALWM ; Maske fuer letztes Wort
Move.w #$0000,BLTAMOD ; Modulo Register A auf Null
Move.w #$0000,BLTDMOD ; Modulo Register D auf Null
Move.w #%0000100111110000,BLTCON0
; Bereiche anschalten, und
; moegliche Verknuepfungen anmelden
Move.l #$50000,BLTAPT ; Quelladresse angeben
Move.l #$60000,BLTDPT ; Zieladresse angeben
Move.w #300*64+50,BLTSIZE ; Groesse der Operation, und Los
WaitBlitter:
Btst #6,DMACONR ; Bit vom DMA.Kanal testen.
Bne.s WaitBlitter ; Nicht gedrueckt -> Zurueck
Rts ; Gedrueckt -> Raus

So, das ist ein Kompletter aufruf zu einer Kopieraktion des Blitters.

Allerdings im loeschen ist er auch einsame Spitze. Man muss im da nur sagen
wo der zu loeschende liegt, und das er nur mit dem Zielbereich arbeiten
soll. Ein Aufruf der den Blitter veranlasst das ebenkopierte jetzt zu
loeschen saehe dann so aus.

Move.w #%0000000100000000,BLTCON0
Move.l #$50000,BLTDPT
Move.w #300*64+50,BLTSIZE
WaitBlitter:
Btst #6,DMACONR
Bne.s WaitBlitter
Rts

So, das sind die ersten anwendungsgebiete des Blitter, es gibt aber noch
sehr viele mehr.

Bevor ich mich im naechsten Teil so langsam an die Bobs, und an das Thema
Laufschrift heranwage, zeige ich euch gerade noch wie man mit dem Blitter
invertiert.

Move.w #%0000100100001111,BLTCON0
Move.l #$60000,BLTAPT
Move.l #$60000,BLTDPT
move.w #300*64+50,BLTSIZE
WaitBlitter:
Btst #6,DMACONR
Bne.s WaitBlitter
Rts

Zu beachten ist das Quelle und Ziel dieselbe Adresse erhalten, da die
Operation sehr kompliziert ist.

Was noch wichtig ist das ihr die Interrupts und das Taskswitching
ausschaltet, denn der Blitter wird halt immer benutzt, und dann muesste man
immer warten. Wenn wir allerdings den Rest abschalten macht der Blitter nor
noch seine Aufgabe zuende, und gehoert dann sofort uns. Deshalb muessen wir
bei einen Programm das den Blitter benutzt, immer ganz am anfang auf den
Blitter warten, damit wir dann richtig loslegen koennen.
Allerdings habe ich das jetzt etwas schlecht angefangen, da man die
WaitBlitterschleife immer vor den Aufruf setzt, damit das Programm was den
Blitter braucht schonmal weiter arbeitet, und erst auf den Blitter wartet
wenn es ihn wieder braucht...Alles Klor ?

Nun denn, jetzt kommen die vorbereitenden Sachen zur Laufschrift.
Wir haetten das auch schon viel frueher machen koennen, mit dem Rol oder
Asl befehl...Aber das ist echt Arbeit. Und mit dem Blitter ist es ein
Klacks einen Text ueber den Screen zu scrollen.
Kommen wir nun zu einer moeglichkeit die uns der Blitter bietet, und die
wir fuer Laufschriften sehr gut gebrauchen koennen. Aber erstmal will ich
es ohne erklaeren, dann erkennt ihr den Nutzen wirklich.

18.Realisation einer Laufschrift mit Hilfe des Blitters

Also wie sieht eine Laufschrift den theoretisch aus.

...Ein Buchstabe wird auf den Bildschirm geschrieben. Er wird stetig nach
Links verschoben, auf den Linken rand zu. Sobald neben ihm Platz fuer einen
neuen ist, wird der naechste Buchstaben dargestellt. Das geht immer weiter
so..Sobald einer der Buchstaben den linken rand erreicht hat, wird er immer
mehr herrausgeschoben, bis er nicht mehr zu sehen ist. So einfach ist das.

Damit die Buchstaben sich aber nicht ruckartig weiterbewegen, mueesen wir
erstmal softscrolling mit dem Blitter verstehen und anwenden.
Denn Hardscrolling hiess mit dem Blitter immer 16 Pixel weiter, weil er ja
als kleinste Einheit, nur das Wort kennt. Und das waere ja ein bisschen
viel, gell ?

Zum Glueck haben die Erbauer des Blitter uns da etwas eingebaut was uns
dabei hilft.

Der Blitter ist in der Lage, die von Quelle A oder B kommenden Daten Pixel-
weise zu verschieben, und verschoben zu schreiben. Man muss ihm den wert
nur in eines der Kontrollworte BLTCON0 oder BLTCON1 zu schreiben. Dann wird
der Wert beim lesen verschoben und dann in das Ziel geschrieben.

Den Verschiebungswert fuer die Quelle A, schreiben wir in die obersten 4
Bits des BLTCON0 registers. Den Verschiebungswert fuer die Quelle B,
schreiben wir in die obersten 4 Bit des BLTCON1 registers.

Man setzt die anzahl der Pixels die nach rechts geschoben werden soll in
das register, mit dessen Quelle wir Arbeiten. Ich nehme bei einfachen
sachen sowieso immer A, also bleiben wir dabei.

Verschieben ist allerdings nicht das richtige Wort fuer die Sache. Es
muesste eigentlich `Rotieren` heissen. Denn was rechts rausfaellt, wird
links wieder reingesetzt.

Es gibt allerdings noch andere Probleme. Wie gesagt, man gibt den Wert der
Pixelverschiebung nach rechts an. Aber so eine Laufschrift laeuft aber
immer von Rechts nach Links...Es sei den wir Schreiben in Arabisches intro,
aber das wollen wir ja nicht, oder. Wie kriegen wir also ein Verschiebung
nach links hin. Es ist nicht schwer, aber ihr muesst jetzt genau lesen.

Eine verschiebung um einen Pixel nach links, ist moeglich wenn wir als
verschiebungs wert 15 eingeben, und als Ziel 1 Wort vorher Waehlen.

Bildlich...


1 Wort 2 Wort 3 Wort
I I I
00000000000000001111111111111110000000000000000

Wenn wir nun die ganzen Einsen, eins nach Links kopieren wollen, kopieren
wir einfach alles in das Erste wort.


1 Wort 2 Wort 3 Wort
I I I
11111111111111110000000000000000000000000000000

Und jetzt verschieben wir es einfach 15 Pixel nach rechts..

1 Wort 2 Wort 3 Wort
I I I
0000000000000001111111111111110000000000000000

Das ist dasselbe als wenn wir es direkt eins nach links schieben, oder ?

Wenn wir also einen Teil des Bildschirms Softscrollen wollen, muessen wir
einfach das gesamte rechteck ein Wort frueher wieder hinschreiben und die
Verschiebung auf 15 Pixel stellen.

Allerdings muessen wir noch einen Sicherheits bereich erschaffen, in dem
die sachen die Links rausfallen wieder reingeschoben werden koennen ohne
das sie auf dem Bildschirm sichtbar werden. Dazu koennen wir wieder die
Modulowerte der Bitplanes gebrauchen. Mit ihnen koennen wir uns diesen
bereich schaffen. Normalerweise waehlt man die groesse eines Buchstaben als
sicherheits abstand. Gehen wir mal von einen 16*16 Zeichensatz aus, das
saehe dann ungefaehr so aus.

I I I
I Normaler Sichtbarer I I Sicherheitsbereich
I Bildschirm I I /
I I I/
I I /
I I /I
I I / I
I I I
I I I

Bei dem erwaehnten 16 mal 16 Font, muessten wir also Modulowert 2, fuer
zwei Bytes eingeben, dann haetten wir die Zone erstellt.
Hier koennen dann die Truemmer von Links hereingeschoben werden. Sobald
aber der Buchstabe den ich waehrend des Scrollens dort rausgeschoben habe,
ich setzte die Buchstaben ebenfalls in diese Zone, damit sie in den
Bildschirm hereingeschoben werden, muss ich allerdings direkt den neuen
dort reinsetzen, da sonst der muell von eben zwischen den buchstaben
landet, und das wollen wir ja nicht.

Wie muss ich jetzt diese beiden bereiche verwalten ?
Der gesamte rechteckige bereich ist samt Sicherheitszone 21*17 Worte gross.
Eine Zeile muss ich oben auch als Sicherheitszone eingeben.

Zuerst muessen wir uns mal um die routine Kuemmern die den ganzen bereich
verschiebt. Wenn wir davon ausgehen das die Plane bei $40000 anfaengt saehe
das dann so aus.

Move.w #$ffff,BLTAFWM ; Masken setzen
Move.w #$ffff,BLTALWM
Move.w #$0000,BLTAMOD ; Modulowerte noch nicht von
; bedeutung
Move.w #$0000,BLTDMOD
Move.w #%1111100111110000,BLTCON0
; Quelle und Ziel festlegen, und
; Verschiebungswert fuer A angeben
Move.l #$40000,BLTAPT ; Quelle Normal
Move.l #$3fffe,BLTDPT ; Ziel 2 Bytes frueher
Move.w #21*64+17,BLTSIZE ; Groesse und LOS!

Ich schreibe die WaitBlitter routine und das RTS nicht mehr dabei. Es
muesste jedem klar sein das das kommen muss wenn man die Routine selbst
benutzt.

So mit der Routine haetten wir alles um einen Pixel nach Links gesetzt.

Sobald das aber 16 mal passiert ist, muss der Puffer wieder mit dem
Naechsten zeichen gefuellt werden, damit wenn wir weiterschieben der neue
Buchstabe reingeschoben werden kann.
Das Aussehen des Buchstaben koennen wir im Source unterbringen, mit der
DC.w anweisung geht das ziemlich einfach.

Jetzt kommen auch die Modulo werte des Blitter auf die Buehne. Wenn ich das
aussehen nun in den Puffer kopiere, kann die Quelle ohne Modulowert sein,
weil die Daten ja alle hintereinander liegen. Aber in der Quelle kann ich
sie nicht hintereinander schreiben, sonst wuerden wir sie ja auf den
bildschirm setzen. Wir muessen also gucken wo die Linke obere Ecke der
Sicherheitszone ist, als Breite die Breite des Puffers angeben, und als
Modulo wert fuer das Ziel, die Breite des Sichtbaren bereiches angeben,
damit wenn eine Zeile des neuen Buchstaben geschrieben ist, der anfang der
Zeile, der ja sichtbar ist, uebersprungen wird, und erst weitergeschrieben
wird wenn der Puffer wieder erreicht ist. Das saehe dann so aus...

Move.w #$ffff,BLTAFWM ; Immer noch keine Maske
Move.w #$ffff,BLTALWM
Move.w #$0000,BLTAMOD ; Modulo A ist null
Move.w #$0028,BLTDMOD ; Modulo D ist 40 ($28)
Move.w #%0000100111110000,BLTCON0
; Keine Verschiebung, A und D an
Move.l #Definiton im Source,BLTAPT
Move.l #$40028,BLTDPT ; Linke obere Ecke des Puffers
Move.w #1*64+16,BLTSIZE ; Groesse und los...

Das waren die beiden Wichtigsten routinen die der Blitter bei der
Programmierung einer Laufschrift ausfuehren muss.

Das schwierige an einer laufschrift ist die verwaltung der Buchstaben
definitionen, und das setzen der neue Buchstaben, die auswahl der
richtigen Buchstaben anhand eines Ascii wertes und so weiter...

Schreiben wir aber erstmal ein Prograemmchen das einen bestimmten bereich
immer weiter nach links Scrollt, und wenn ein zeichen draussen ist, ein
neues reinsetzt...

Dort setze ich diese beiden routinen ein....Achtet aber mehr auf die art
und weise wie ich auf den Zeitpunkt warte an dem ich den Puffer mit dem
Neuen zeichen fuellen muss...

Hier das Listing..

Execbase= 4
Openlibrary= -408
Vhposr= $dff006
Forbid= -30-102
Permit= -30-108
Bltafwm= $dff044
Bltalwm= $dff046
Bltcon0= $dff040
Bltcon1= $dff042
Bltamod= $dff064
Bltdmod= $dff066
Bltapt= $dff050
Bltdpt= $dff054
Dmacon= $dff096
Intena= $dff09a
Dmaconr= $dff002
Bltsize= $dff058


Move.w #$0020,dmacon
Move.w #$4000,intena
Move.l execbase,a6
Jsr forbid(a6)


Move.l #$40000,a0
Move.l #$2800/4,d0
clearloop:
Move.l #0,(a0)+
Dbf d0,clearloop

Bsr.l Set_letter
Bsr.l Makecl

Wait:
Cmpi.b #$80,vhposr
Bne.s Wait
Bsr.l Scroll
Sub.w #1,Lettercount
Bne.s Weiter
Bsr.l Set_letter
Weiter: Btst #6,$bfe001
Bne Wait

Move.l Execbase,a6
Jsr Permit(a6)
Lea Gfxname(pc),a1
Jsr Openlibrary(a6)
Move.l d0,a6
Move.w #$83e0,dmacon
Move.w #$c000,intena
Move.l 38(a6),$dff080
Moveq #0,d0
Rts
Makecl:
Lea $05f000,a0
Move.l a0,$dff080

Move.l #$00e00004,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$008e3081,(a0)+
Move.l #$009030c1,(a0)+
Move.l #$00920038,(a0)+
Move.l #$009400d0,(a0)+
Move.l #$01001200,(a0)+
Move.l #$01080002,(a0)+
Move.l #$01800000,(a0)+
Move.l #$018200ff,(a0)+
Move.l #$fffffffe,(a0)+
Rts

Waitblit:
Btst #6,dmaconr
Bne.s Waitblit
Rts

Set_lettEr:
Bsr.s Waitblit
Move.w #$ffff,bltafwm
Move.w #$ffff,bltalwm
Move.w #%0000100111110000,bltcon0
Move.w #0,bltamod
Move.w #40,bltdmod
Move.l #Smiledefi,bltapt
Move.l #$40028,bltdpt
Move.w #15*64+1,bltsize
Move.w #$10,lettercount
Rts
Scroll:
Bsr.s Waitblit
Move.w #$ffff,bltafwm
Move.w #$ffff,bltalwm
Move.w #%1111100111110000,bltcon0
Move.w #0,bltamod
Move.w #0,bltdmod
Move.l #$40000,bltapt
Move.l #$3fffe,bltdpt
Move.w #16*64+21,bltsize
Rts

Gfxname: dc.b "graphics.library",0

even

Lettercount: dc.w 0

Smiledefi:

dc.w %0000111111110000
dc.w %0011000000001100
dc.w %0100000000000010
dc.w %0100000000000010
dc.w %1001110000111001
dc.w %1001010000101001
dc.w %1001110110111001
dc.w %1000000110000001
dc.w %1000001111000001
dc.w %1000000110000001
dc.w %1001100000011001
dc.w %0100011111100010
dc.w %0100000000000010
dc.w %0011000000001100
dc.w %0000111111110000


So, kurz mal zu der Routine die den Zeitpunkt fuer das Neu-Fuellen des
Puffers bestimmt. Sie Arbeitet sehr einfach...Bevor ich in die
Endlosschleife springe, setze ich das Zeichen schon einmal in den Puffer.
Gleichzeitg mit diesem Aufruf wird das Wort Lettercount auf 16 gesetzt.
Sobald ich mit der Endlosschleife die Zeile $80 erreicht habe, wird der
Bereich einmal nach links geSoftscrollt. Dann wird der Zaehler in
Lettercount eins runtergesetzt, war das ergebnis nicht null, sind die 16
noch nicht abgezaehlt und er springt direkt zur mausabfrage, und von da
wieder in die Endlosschleife. Sobald die 0 aber erreicht ist, verzweigt der
bne befehl nicht und arbeitet weiter. Nach dem Bne befehl steht dann der
Aufruf der PufferFuellroutine, sie setzt auch den Zaehler in lettercount
wieder auf 16.

Wie ihr seht, ist es alles sehr einfach...

Naja, das laeuft ja ganz nett vor sich hin...Haben wir fast schon eine
Laufschrift gebaut. Was noch fehlt, ist schnell erzaehlt (Huch das Reimt
sich) naemlich nur noch eine Routine die anstatt immer den Smile in den
Puffer zu setzen, ein Tabelle mit Buchstaben ausliest und immer den
Naechsten Buchstaben aus der Tabelle zieht, und in den Puffer Kopiert.

Eine Endmarkierung fuer den Text muessen wir auch noch einbauen, aber das
sollte nicht die Schwierigkeit sein.

Eine Sache will ich aber noch erklaeren..Man kann den Blitter auch ueber
den Copper steuern, da der Copper die Register des Blitters auch setzen
kann. Allerdings muessen wir dem Copper das erlauben. Das tun wir indem wir
das Register COPCON $DFF02E auf 1 setzen.
Man ist dann aber bei weitem nicht so Variabel als wenn wir es vom
Prozessor machen lassen. Ohne groesseren Aufwand koennen wir nur immer
wiederkehrende Vorgaenge steuern. So ein Vorgang waere das Scrollen des
Bildschirms, da es ja jeden Bildschirmaufbau geschieht. Wenn wir allerdings
ein bisschen weiterdenken dann ist das nicht das wahre. Denn wenn die
Laufschrift Komplexer wird, ich denke an verschiedene Geschwindigkeiten,
oder einen Stop, dann muessten wir jedesmal die Copperliste wieder
umschreiben. Und dieser Aufwand wuerde dann das Gesparte wieder aufheben.

Also wer interresse daran hat, kann es ja mal ausprobieren aber es lohnt
sich erst ab Komplexeren Aufgaben. Man kann sich zum Beispiel vorstellen
das sich Copper und Blitter gegenseitg steuern. Das koennte Praktisch so
aussehen das in der Copperliste die ganzen Blitteraufrufe eingegeben sind,
und allerhand auf dem Bildschirm passiert. Der letzte Blitteraufruf der
Copperliste muesste dann sein das der Blitter einfach eine neue Copperliste
ueber die Alte schreibt...u.s.w...

Viel ist denkbar..aber zurueck zu unseren Problemen..

Jetzt kommt eine Sache die Klappen muss, da ihr sonst mit euren
Laufschriften nicht viel Spass haben werdet..

Jedem muesste klar sein das wir jetzt kurz vor der Laufschrift stehen, und
desweiteren muesste auch klar sein das wir mindestens 26 Buchstaben
brauchen um damit etwas zu schreiben. Wenn wir die aber wie beim ersten
Blitterlisting im Source unterbringen, Gute Nacht !

Das muss auch anders gehen, aber wie ?
Ganz einfach, wir malen mit Dpaint den Kompletten Zeichensatz auf ein NTSC
Bild, und holen uns die Daten dann da raus. Mit den Modulowerten koennen
wir uns ja Rechteckige bereiche rausschneiden. Und in einer Tabelle
verewigen wir die Adressen der Buchstaben, die wir dann bei bedarf als
Quelle angeben und in den puffer setzen lassen.

Ihr muesst nur halt ein 2 Farben NTSC Bild anschalten (Bei Dpaint) und dort
macht ihr 26 rechtecke mit den maáen 16*16. Die setzt ihr dann bis zum rand
nebeneinander, und dann fangt ihr eine Zeile tiefer an. Wichtig ist nur das
ihr euch an die Maáe haltet, denn sonst koennte es ein wenig Bloed
aussehen.
Diese Plane koennen wir dann nach der Umwandlung (Konvertierung) einfach
mit >Extern nach z.B $50000 laden, und uns die Buchstaben dort rausholen.

Das Festlegen der Adressen ist auch sehr einfach. Der Erste liegt
natuerlich bei $50000, der zweite dann bei $50002. Etwas schwierig wird es
dann wenn wir in die naechste zeile gehen. Dort muesste der Buchstaben dann
bei $50000 + 16*40 liegen, das ist $50280, und dann geht es wie gehabt
weiter.

Jetzt kommt noch wie wir erfahren an welcher stelle der Tabelle wir den
Wert fuer den Buchstaben den wir darstellen wollen den nun finden ?

Auch das ist kein Problem, wir nehmen einfach den ASCII wert des
Buchstabens als Zeiger.

Im Klartext. Gehen wir davon aus das die Laufschrift nur die 26 Buchstaben
des Alphabets kennt. Davon hat das `A` den niedrigsten naemlich $41. Also
muessten wir alle ankommenden ASCII-werte -$41 nehmen, dann haetten wir den
Zeiger auf das Element der Tabelle das auf das Aussehen des Buchstabens
zeigt. Mit der Methode koennten wir einfach alle Adressen der Definitionen
in eine Tabelle setzen und das gewuenschte rausziehen, das ist doch schoen.

Bei unserer Laufschrift machen wir es dann auch so. Nur das ihr vor dem
Ersten Buchstaben noch ein ganz leeres Feld setzen muesst, da wir zumindest
ein `Space` brauchen.

Nachdem ihr jetzt wisst ist eurer Kreativitaet nichts mehr im Weg. Also
malt was das Zeug haelt.

Jetzt mal etwas zusammenfassend zum Kurs bis jetzt.

So ein Kurs ist anfuersich ja eine Tolle sache. Wenn er gut geschrieben ist
dann lernt man in kurzer Zeit sehr viel. Man beherscht dann eine Menge
Sachen. Das einzige was man aber nicht hat, das ist erfahrung mit fehlern.
So komisch sich das anhoert sind fehler eine Ganz wichtige sache. Denn man
merkt sich Fehler relativ gut. Und wenn ich zum beispiel Heute eine
komplette Laufschrift programmiere, und dort gibt es etwas was nicht tut
wie es tun sollte, dann kann ich mich in 90% der Faelle an ein Aehnliches
Ereigniss erinnern, und anhand dieser Erfahrung faellt es mir leichter den
Fehler zu lokalisieren. Also das beste was man einem der mit Asembler
anfaengt, wuenschen kann ist : Allzeit viele Fehler...
Naja das ist Vielleicht ein bisschen uebertrieben, aber man kann es so sehen.
Der Zweite nachteil ist, das man nicht denken lernt. Denn ich nehme euch ja
alle denkarbeit ab. Es waere besser fuer euch wenn ich sagen wuerde, `Dann
versucht mal eine Routine zu schreiben die das und das kann`. Aber wofuer
mache ich dann einen Kurs, dann haette ich auch im Ersten teil schreiben
koennen, `So Leute, dies ist ein introKurs ---- Schreibt ein intro`.

Wiegesagt das nachdenken ueber die Sachen die man machen will, und wie man
das Programmtechnisch realisieren koennte, ist es was ihr noch ueben
muesst. Die Grundlagen fuer einen Intro Programmierer habt ihr schon, was
ihr letztendlich daraus macht ist euch ueberlassen.

Diese Worte gehoeren eigentlich an den Schluss des Kurses, aber ich denke
das musste mal gesagt werden....!!!!

So, zurueck zur Laufschrift...Hmmm, was gibt es denn da noch zu sagen.
Nichts glaube ich..Vielleicht faellt mir beim malen oder beim programmieren
der Laufschrift noch was ein...

Execbase= 4
Openlibrary= -408
Vhposr= $dff006
Forbid= -30-102
Permit= -30-108
Bltafwm= $dff044
Bltalwm= $dff046
Bltcon0= $dff040
Bltcon1= $dff042
Bltamod= $dff064
Bltdmod= $dff066
Bltapt= $dff050
Bltdpt= $dff054
Dmacon= $dff096
Intena= $dff09a
Dmaconr= $dff002
Bltsize= $dff058


Move.w #$0020,dmacon
Move.w #$4000,intena
Move.l execbase,a6
Jsr forbid(a6)

Move.l #$40000,a0
Move.l #$2800/4,d0
clearloop:
Move.l #0,(a0)+
Dbf d0,clearloop

Bsr.l Makecl

Wait:
Cmpi.b #$80,vhposr
Bne.s Wait
Bsr.l Scroll
Sub.w #1,Lettercount
Bne.s Weiter
Bsr.l New_Char
Weiter: Btst #6,$bfe001
Bne Wait

Move.l Execbase,a6
Jsr Permit(a6)
Lea Gfxname(pc),a1
Jsr Openlibrary(a6)
Move.l d0,a6
Move.w #$83e0,dmacon
Move.w #$c000,intena
Move.l 38(a6),$dff080
Moveq #0,d0
Rts
Makecl:
Lea $03f000,a0
Move.l a0,$dff080

Move.l #$00e00004,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$008e3081,(a0)+
Move.l #$009030c1,(a0)+
Move.l #$00920038,(a0)+
Move.l #$009400d0,(a0)+
Move.l #$01001200,(a0)+
Move.l #$01080002,(a0)+
Move.l #$01800000,(a0)+
Move.l #$018200ff,(a0)+
Move.l #$fffffffe,(a0)+
Rts

Waitblit:
Btst #6,dmaconr
Bne.s Waitblit
Rts

Set_lettEr:
Bsr.s Waitblit
Move.w #$ffff,bltafwm
Move.w #$ffff,bltalwm
Move.w #%0000100111110000,bltcon0
Move.w #38,bltamod
Move.w #40,bltdmod
Move.l d0,bltapt
Move.l #$40028,bltdpt
Move.w #16*64+1,bltsize
Move.w #$10,lettercount
Rts
Scroll:
Bsr.s Waitblit
Move.w #$ffff,bltafwm
Move.w #$ffff,bltalwm
Move.w #%1111100111110000,bltcon0
Move.w #0,bltamod
Move.w #0,bltdmod
Move.l #$40000,bltapt
Move.l #$3fffe,bltdpt
Move.w #16*64+21,bltsize
Rts

New_char:
Clr.l d0
Move.l Textcounter,a0
Move.b (a0)+,d0
Move.l a0,Textcounter
Lea Letterpostable,a0
Sub.w #$20,d0
Muls #4,d0
Add.l d0,a0
Move.l (a0),d0
Bsr.l Set_Letter
Move.l Textcounter,a0
Cmpi.b #$-1,(a0)
Beq.s Meu
Rts
Neu:
Move.l #Text,Textcounter
Rts

Lettercount: dc.w 1
Textcounter: dc.l text

text: dc.b "JEFF KANDLE IS SPEAKING ",$ff

Letterpostable:
dc.l $50000 ; Space
blk.l $20,0 ; Platzhalter fuer die Differenz
; Zwischen Ascii Space und A


dc.l $50002,$50004,$50006,$50008,$5000a,$5000c,$5000e,$50010,$50012
dc.l $50014,$50016,$50018,$5001a,$5001c,$5001e,$50020,$50022,$50024
dc.l $50026,$50280,$50282,$50284,$50286,$50288,$5028a,$5028c

Gfxname: dc.b "graphics.library",0

>Extern "Name des Fonts",$50000

Wenn ihr den Zeichensatz woanders hinlegen wollt muesst ihr natuerlich auch
die `Letterpostable` aendern.

Wollen wir uns mal die Routine New_Char mal genauer Schritt fuer Schritt
reinziehen.

New_char:

Clr.l d0

D0, wird komplett geloescht, weil wir, wenn wir weiter an der laufschrift
arbeiten, D0 bestimmt noch oefter brauchen werden, ind deshalb noch reste
drinstehen koennten.

Move.l Textcounter,a0

Der Zeiger auf das Aktuelle Element im text wir nach A0 geholt.

Move.b (a0)+,d0

Das Aktuelle Zeichen wird nach D0 geMoved. Gleichzeitg wird durch die
Postinkrementale Adressierung der Zeiger auf das naechste Element gesetzt.

Move.l a0,Textcounter

Der erhoehte Zeiger wird wieder zurueckgeschrieben..

Lea Letterpostable,a0

Zeiger auf die Letterpostable wird nach A0 geholt.

Sub.w #$20,d0

Von dem ASCII wert des Zeichens in D0 wird das kleinste moegliche zeichen
abgezogen, damit wir in der tabelle beim Kleinsten Zeichen anfangen
koennen.

Muls #4,d0

Da die Adressen der tabelle Langworte sind, wird der wert Mal 4 genommen.

Add.l d0,a0

Jetzt wird die Adresse des zeichens in der tabelle, zu der Grundadresse der
tabelle dazuaddiert. in a0 steht jetzt der Zeiger auf den Zeiger der fuer
den Buchstaben noetig ist.

Move.l (a0),d0

Jetzt wird die Adresse nach d0 geholt,

Bsr.l Set_Letter

und die Buchstabensetzroutine aufgerufen

Move.l Textcounter,a0

Erneut wird der Zeiger auf das Element im Text geholt..

Cmpi.b #$-1,(a0)

Und auf die Endmarkierung ueberprueft.

Beq.s Neu

Falls sie es ist, neuinitialisierung des Textcounters

Rts

Falls nicht -> Rueckkehr in die Endlos schleife

Neu:
Move.l #Text,Textcounter

In den Textcounter wird die Adresse des Textes geschrieben, beim Naechsten
durchlauf faengt der text dann von Vorne an.

Rts

Und zurueck !


So, das war doch nicht so schwer oder. Es gibt natuerlich auch andere
methoden eine Laufschrift zu machen, ich spach es schon an, aber wir haben
den Blitter und benutzen ihn auch..

Seht euch die Erklaerung zur New_Char Routine gut an. Ihr koennt lernen wie
man eine Bestimmte sache programmmaessig auf die Beine stellt.
Man muss bei solchen Sachen das gehirn ein bisschen unterfordern, und
Schritt fuer Schritt denken, Halt wie ein Prozessor "Denkt", dann ist es
relativ einfach sowas zu basteln.

Wie waere es denn wenn ihr mal die Effekte die ihr jetzt koennt mit der
Laufschrift kombiniert. Zum Beispiel den Wasser Effekt..Das saehe doch
bestimmt stark aus.

Achja, wer das mit dem Malen des Zeichensatzes nicht hinkriegt der
schreibe mir einen kurzen Brief, und ich werde ihm meinen zukommen lassen.

So, das mit der Laufschrift ist ja ziemlich einfach, oder ?
Naja, mit einer Plane ist das ja auch kein Problem. Was ist aber wenn wir
Zeichensaetze machen die mehr als 2 Farben haben, weil sie halt schoener
sind. Ganz einfach, dann muessten wir den ganzen Aufwand den wir fuer eine
Plane gemacht haben, auf fuer die anderen machen. Ich spreche von dem
Sicherheits bereich und so. Auch das Aussehen der Buchstaben muessten wir
dann mehrmals kopieren, weil so ein z.B 8 Farbiger Buchstabe auch aus 3
Planes besteht. Das ist zwar kein grosses hinderniss, aber es haelt doch
eben auf.
Deshalb werde ich ein Technik erklaeren, die man Normalerweise bei
mehrfarbigen Laufschriften und bei Bobs, zwar nicht immer, aber oft anwendet.

19.Verschachtelte Planes

Hmmm, was heisst das denn ?

Es ist im Prinzip logisch. Normalerweise liegt die Definition fuer einen 3
Plane Buchstaben so im Speicher.

Plane 1, Zeile 1
Plane 1, Zeile 2
...
...
Plane 2, Zeile 1
Plane 2, Zeile 2
...
...
Plane 3, Zeile 1
Plane 3, Zeile 2

Das heisst auch gleichzeitig `Um den Kompletten Buchstaben auf den Screen
zu bringen, muessen wir fuer jede Plane einen Blitteraufruf machen, der
dann die Plane an die Richtige Adresse setzt.
Um die Anzahl der Aufrufe aber wieder auf eins zu setzen, verschachtelt man
die Planes des leeren Playfields wo die Bobs (oder die Laufschrift) rein
soll, und man verschachtelt die Bobs selber. Verschachtelt sieht eine
Bobdefinition dann so aus.

Plane 1, Zeile 1
Plane 2, Zeile 1
Plane 3, Zeile 1
Plane 1, Zeile 2
Plane 2, Zeile 2
Plane 3, Zeile 2
Plane 1, Zeile 3
Plane 2, Zeile 3
...
...

Jetzt kann ich die komplette Definition mit einem Schlag kopieren.
Allerdings muessen dann die Bitplanepointer und die Modulo werte stimmen.

Die Bitplanepointer muessen auf die ersten drei Zeilen der Bitplane zeigen.
Bei dem Playfield wie wir es immer benutzen (normal PAL) saehen sie Pointer
so aus.

BPL1PT = $40000
BPL2PT = $40028
BPL3PT = $40050

Natuerlich in der Copperliste getrennt geschrieben.
Und die Modulowerte muessten bei 3 Planes 80 ($50) betragen, damit die
Naechsten zwei Zeilen im Speicher uebersprungen werden, weil dort ja die
Daten fuer die anderen beiden Planes stehen. Die zweite Zeile der Ersten
Plane steht dann erst bei $40078. Das hoert sich vielleicht kompliziert an,
ist es aber nicht.

Das leere Playfield so zu initialisieren ist ja Null Problemo, aber wie
machen wir das mit den Buchstaben fuer eine Laufschrift.
Um das auszuprobieren muesst ihr einfach euer Bild mit dem Zeichensatz fuer
die Laufschrift auf 4 Farben erweitern, und damit ihr es sehen koennt malt
ihr noch, sofern es geht einen Bunten rand um die Buchstaben, ihr koennt
natuerlich auch einen neuen Zeichensatz malen, ich lasse euch da freie
Hand.
Wichtig ist nur das es mehr als eine Plane ist, denn sonst laesst es sich
schlecht verschachteln.

Frueher war es ein Grosses Problem mit dem Verschachteln, es war fast
unmoeglich, oder man musste grossartige Routinen fuerden blitter schreiben,
um seine Bobs oder Laufschrift Buchstaben zu verschachteln. Bis der
Kefrens-IFF-Converter kam. Er konnte das auf Knopfdruck.

Ihr muesst einfach vor dem Abspeicher der RAW-Datei, wenn ihr aus dem
IFF-ILBM ein RAW-NORM macht, nochmal drauf druecken, dann erscheint
naemlich RAW-BLIT, das ist dieses Verschachtelte Format, das man benutzt
wenn man mit dem Blitter arbeiten.
So einfach ist das....

Wenn man dann noch die Richtigen werte einstellt laeuft die Laufschrift wie
gehabt mit einem Aufruf, obwohl sie aus 2 Planes besteht. Natuerlich muesst
ihr bei den Blitteraufrufen die zu kopierende Zeilenzahl verdoppeln, denn
die veraendert sich ja.

So nebenbei habe ich mal eine Liste mit den Wichtigsten Registern und
Adressen getippt, sie ist fertig, und ich setze sie dann natuerlich
hierein.

Einfach ausschneiden und in den Source kleben...

Register.....

Bpl1Mod= $Dff108 ;Bitplane modulo ungerade Planes (1,3,5)
Bpl2Mod= $Dff10a ;Bitplane modulo gerade Planes (2,4,6)
BplCon0= $Dff100 ;Bitplane Kontroll Register 0
BplCon1= $Dff102 ;Bitplane Kontroll Register 1
BplCon2= $Dff104 ;Bitplane Kontroll Register 2
Bpl1Pth= $Dff0e0 ;Bitplane Kointer 1-6 (Msb)
Bpl2Pth= $Dff0e4 ;Werden jeden VBI neu eingesetzt
Bpl3Pth= $Dff0e8
Bpl4Pth= $Dff0ec
Bpl5Pth= $Dff0f0
Bpl6Pth= $Dff0f4
Bpl1Ptl= $Dff0e2 ;Bitplane Pointer 1-6 (Lsb)
Bpl2Ptl= $Dff0e6 ;Werden jeden VBI neu eingesetzt
Bpl3Ptl= $Dff0ea
Bpl4Ptl= $Dff0ee
Bpl5Ptl= $Dff0f2
Bpl6Ptl= $Dff0f6
BltCon0= $Dff040 ;Blitter controll register 0
BltCon1= $Dff042
BltSize= $Dff058 ;Blitter size (Start) Register
BltAFWM= $Dff044 ;Blitter erstes wort maske A
BltALWM= $Dff046 ;Blitter letztes wort maske A
BltAMod= $Dff064 ;Blitter A Modulo
BltAPth= $Dff050 ;Blitter A Pointer (19bits)
BltADat= $Dff074
BltBPth= $Dff04c
BltCPth= $Dff048
BltBMod= $Dff062
BltCMod= $Dff060
BltDMod= $Dff066 ;Blitter D Modulo
BltDPth= $Dff054 ;Blitter D Pointer (19bits)
Color0= $Dff180 ;Start der Hardwarefarbentabelle 0-31
Color1= $Dff182
Color2= $Dff184
Color7= $Dff18e
Color8= $Dff190
Color9= $Dff192
Color10= $Dff194
Color11= $Dff196
Color12= $Dff198
Color13= $Dff19a
Color14= $Dff19c
Color15= $Dff19e
Color16= $Dff1a0 ;Hier liegen die Spritefarben {16-31)
Color17= $Dff1a2
Color18= $Dff1a4
Color19= $Dff1a6
Color20= $Dff1a8
Color21= $Dff1aa
Color22= $Dff1ac
Color23= $Dff1ae
Color24= $Dff1b0
Color25= $Dff1b2
Color26= $Dff1b4
Color27= $Dff1b6
Color28= $Dff1b8
Color29= $Dff1ba
Color30= $Dff1bc
Color31= $Dff1be
Cop1Lc= $Dff080 ;Copper Startadresse 1
Cop2Lc= $Dff084 ;Copper Startadresse 2
CopJmp1= $Dff088 ;Copper ausloeser (Strobe) 1
CopJmp2= $Dff08a ;Cooper ausloeser (Strobe) 2
DDFStop= $Dff094 ;Display Data Fetch Stop
DDFStrt= $Dff092 ;Display Data Ftech Start
DiwStrt= $Dff08e ;Display Window Start x,y
DiwStop= $Dff090 ;Display WIndow Stop x,y
DmaCon= $Dff096 ;Direct Memory Access Controll Register
DmaConR= $Dff002 ;DMA controll and blitter status read
Intena= $Dff09a ;Interrupt Enable Register
Intreqr= $Dff01e ;Interrupt Request Register
PotGoR= $Dff016 ;Pot Data (rechte Maus taste)
Vposr= $Dff006 ;Position of the Video beam on screen
VPos= $Dff004 ;Holds highbits of Video beam positon
Spr0pt= $Dff120 ;SpriteDma Pointer
Spr1pt= $Dff124
Spr2pt= $Dff128
Spr3pt= $Dff12c
Spr4pt= $Dff130
Spr5pt= $Dff134
Spr6pt= $Dff138
Spr7pt= $Dff13c
CIAapra= $Bfe001

Adressen...

Execbase= 4
Openlibrary= -552
Forbid= -132
Permit= -138

Ihr koennt es auch ausdrucken und an die Wand haengen. Das werde ich glaube
ich auch jetzt mal tun...ist doch hilfreich so eine Liste neben sich zu
haben.

So, dann mal weiter...Aber eigentlich brauche ich ja garnichts mehr zu
erklaeren zu den Bobs, denn die Technik kennt ihr ja schon von der Routine
die die Buchstaben in den Puffer kopiert.

So bevor es mit dem Blitter weitergeht, habe ich hier noch ein nette kleine
Routine. Eigentlich 2...Die erste kann Punkte auf den Bildschirm setzen,
und die zweite kann punkte auf dem Bildschirm loeschen.

Wofuer ihr sie gebrauchen koennt weiss ich nicht. Ich jedenfalls habe ein
Feuerwerk, und ein Pixelweise verschwindendes Bild daraus gemacht.

Set:
Divu #8,d0
Mulu #40,d1 ; Hier den Wert der Plane breite
; einsetzen, da die Routine sonst
; nicht richtig arbeitet
; Normal 40, Overscan 44.
Add.w d0,d1
Swap d0
Move.b #,d2
Sub.b d0,d2
Bset d2,(a5,d1)
Rts
Clear:
Divu #8,d0
Mulu #40,d1 ; Ebenso
Add.w d0,d1
Swap d0
Move.b #,d2
Sub.b d0,d2
Bclr d2,(a5,d1)
Rts

Man muss die Routinen mit der Basisadresse der Plane in A5, der
X-Koordinate des Punktes in D0, und der Y-Koordinate in D1 anspringen.
Achtet bitte auf den Kommentar, er ist fuer das Funktionieren der Routine
wichtig.

Ha, ich koennte mich Totschlagen...Habe ich mir die Scheissarbeit gemacht,
und die Befehlsliste getippt. Und als ich eben in einer alten Diskette nach
einem Noch aelteren SourceCode gesucht habe, ist mir ein Viel bessere
Befehlsliste unter die Finger gekommen. Sie enthaelt gleichzeitig auch die
Adressierungsarten die es gibt, und wie man den Befehl aufruft.
Ich will die euch nicht vorenthalten, verzeit mir bitte, Schade um das
Druckerpapier was fuer die Erste Liste draufgegangen ist. Naja, Nobody is
Perfekt, ich hatte die Wirklich vergessen.

ABCD Quelle,Ziel Addition zweier BCD Zahlen
ADD Quelle,Ziel Binaere Addition
ADDA Quelle,An Binaere Addition zu einem Adressregister
ADDI #n, Addition mit einer Konstanten
ADDQ #n, Schnelle Addition mit 3-Bit Konstanten
ADDX Quelle,Ziel Addition mit Uebertrag im X-Flag
AND Quelle,Ziel Logisches Und
ANDI #n, Addition mit einer Konstanten
ASL n, Arithmetische Verschiebung nach links
ASR n, Arithmetische Verschiebung nach rechts
B Label Verzweigt bedingt, ja nach Bedingung
BCHG #n, Veraendert Bit n (0 -> 1 / 1 -> 0)
BCLR #n, Setzt Bit n auf 0
BSET #n, Setzt Bit n auf 1
BRA Label Verzweigt (JMP-aehnlich)
BSR Label Verzweigt in ein Unterprogramm (wie JSR)
BTST #n, Testet Bit n (Ergebnis -> Z-Flag)
CHK ,Dx Prueft ein Datenregister auf Grenzen,
loest ggf. CHK-Instruction-Exception aus
CLR Loeschen eines Operanden
CMP Quelle,Ziel Vergleich zweier Operanden
CMPA ,An Vergleich mit Adressregister
CMPI #n, Vergleich mit einer Konstanten
CMPM Quelle,Ziel Vergleich zweier Speicheroperanden
DB(bed) Reg,Label Prueft die Bedingung, dekrementiert Reg
und verzweigt ggf.
DIVS Quelle,Ziel vorzeichenrichtige Division des 32-Bit Ziel- durch
16-Bit Quelloperanden.Das Ergebnis liegt im Lo-Wort
von Ziel, der Rest der Division im Hi-Wort.
DIVU Quelle,Ziel wie DIVS, nur ohne Vorzeichen
EOR Quelle,Ziel Exclusiv ODER
EORI #n, Exclusiv Oder mit einer Konstanten
EXG Rn,Rn Austausch zweier Registerinhalte
EXT Dn Vorzeichenrichtige Erweiterung auf
doppelte Breite
JMP Label Springe zur Adresse
JSR Label Rufe Unterprogramm auf;Die Ruecksprungadresse wird
auf den Stack gelegt;
Beendigung des Unterprogramms mit RTS
LEA ,An Lade effektive Adresse in Adressregister
LINK An,#n Baue Stackbereich auf
LSL n, Logische Verschiebung links
LSR n, Logische Verschiebung rechts
MOVE Quelle,Ziel Uebertrage einen Wert von Quelle nach Ziel
MOVE SR, Uebertrage den Statusregister-Inhalt
MOVE ,SR Uebertrage den Statusregister-Inhalt
MOVE ,CCR Flags laden
MOVE USP, Uebertrage den User-Stackpointer
MOVE ,USP Uebertrage den User-Stackpointer
MOVEA ,An Uebertrage einen Wert in ein Adressregister
MOVEM Regs, Uebertrage mehrere Register auf einmal
MOVEP Quelle,Ziel Uebertrage Daten zur Peripherie
MOVEQ #n,Dn Uebertrage schnell eine 8-Bit Konstante in ein
Datenregister
MULS Quelle,Ziel vorzeichenrichtige Multiplikation zweier Worte zu
einem Langwort
MULU Quelle,Ziel wie MULS nur ohne Vorzeichen
NBCD Quelle,Ziel Negation einer BCD-Zahl (9er Komplement)
NEG Negation eines Operanden (2er Komplement)
NEGX Negation eines Operanden mit Uebertrag
NOP Keine Funktion (no Operation)
NOT Inversion eines Operanden
OR Quelle,Ziel Logisches ODER
ORI #n, Logisches oder mit einer Konstanten
PEA Lege eine Adresse auf den Stapel
RESET Peripherie zuruecksetzen
ROL n, Rotation nach links
ROR n, Rotation nach rechts
ROXL n, Rotation nach links (Uebertrag -> X-Flag)
ROXR n, Rotation nach rechts (Uebertrag -> X-Flag)
RTE Rueckkehr aus Exeption
RTR Rueckkehr mit laden der Flags
RTS Rueckkehr aus Unterprogramm (nach BSR/JSR)
SBCD Quelle,Ziel Subtraktion zweiter BCD-Zahlen
S Setze Byte auf -1, wenn Bedingung erfuellt
STOP Verarbeitung anhalten,evtl. TRAP Exception
SUB Quelle,Ziel Binaere Subtraktion
SUBA ,An Binare Subtraktion von einem Adressregister
SUBI #n, Subtraktion einer Konstanten
SUBQ #n, Schnelle Subtraktion einer 3Bit Konstanten
SUBX Quelle,Ziel Subtraktion mit Uebertrag -> X-Flag
SWAP Dn Vertauscht beide Registerworte
TAS Pruefe ein Byte und setze Bit 7
TRAP #n springe in eine Exception
TRAPV Ist Ueberlauf Flag (V-Flag) gesetzt, dann Exception
TST Testen eines Operanden und ggf. Setzen von N/Z-Flag
UNLK An Baue Stackbereich ab

------------------------------------------------------------------------------
An Adressregister (A0-A7)
Dn Datenregister (D0-D7)
Label Adresse / Label
Reg Register (A0-A7 / D0-D7)
Quelle Quelloperand
Ziel Zieloperand
#n Direktwert
Adresse oder Register
------------------------------------------------------------------------------

Bedingungen ( )

Bedingung Bedeutung Bits
T True Immer
F False Nie
HI higher hoeher als C' ^ Z'
LS lower or same kleiner gleich C v Z
HS higher or same groesser gleich C'
CC Carry clear Carry-Flag geloescht C'
NE not equal ungleich Z'
EQ equal gleich Z
VC Overflow clear kein Ueberlauf V'
VS Overflow set Ueberlauf V
PL plus positiv N'
MI minus negativ N
GE greater or equal groesser gleich N ^ V v N' ^ V'
LT less than weniger als N ^ V' v N' ^ V'
GT greater than groesser als N^V^Z' v N'^V'^Z'
LE less or equal weniger oder gleich Z v N^V' v N'^V

^= AND / v=OR / '= NOT
------------------------------------------------------------------------------

Die ist auch viel besser zum an die Wand haengen...

Und hier noch die Adressierungsarten..


Adressierungsarten:

Datenregister direkt Dn
Adressregister direkt An
" indirekt (An)
" ind. mit Postinkrement (An)+
" ind. mit Predekrement -(An)
" ind. mit 16 Bit Offset O16(An)
" ind. mit 8 Bit Offset O8(An,Rn)
Absolut kurz xxxx.W
ABsolut lang xxxxxxxx.L
Unmittelbar #`Daten`
Programmzaehler ind. mit O16(PC)
16 Bit Offset
" ind. mit 8 Bit Offset O8(PC,Rn)

Legende:

An Adressregister (A0-A7)
Dn Datenregister (D0-D7)
O16 16 Bit Wert
O8 8 Bit Wert
Rn Register (A0-A7 / D0-D7)
`Daten` bel. Wert (bis 16 Bit)

Anhand der Legende kann man sich die noetigen Parameter zusammendenken.

Naja, da habe ich diesen teil leider wieder voll gestopft. Das reicht fuer
Heute erstmal..
Ich fang jetzt mit dem Intro an was wir dann zusammen machen wollen. Ein
paar Routinen werde ich wohl aus dem Kurs nehmen. Aber nicht alle..sie sind
zum teil sehr schlecht geeignet fuer den Einsatz in einem Intro.
Wie ihr merkt halte ich aber mein Versprechen vom 1.Teil das ich am Ende so
ein Paar Listen rueberruecke.

Viel Spass damit...Ich werde nu Duschen gehen, und mir das Intro ausdenken.
Unter der Dusche habe ich naemlich immer die Besten Ideen

Ich hab mir ueberlegt was man noch zum Thema Intro`s machen koennte.
Bis jetzt haben wir grosse Routinen, tolle Effekte usw. kennengelernt.

Ich muss jetzt aufpassen was ich sage, nicht das ihr denkt ich wuerde das
machen, aber beschaeftigt habe ich mich natuerlich schon damit.

`Stellt euch vor ihr habt ein Spiel gecrackt (Gott bewahre), und es belegt
auf Diskette 98%. Dann bleiben fuer das Intro, das natuerlich noch drauf
muss nicht mehr als 16 Kilobyte. Aber drauf muss es das ist doch Ehrensache.`

Naja wie kriege ich da noch ein Intro drauf ?...

Ganz einfach: Ich verrate euch sicher kein grosses geheimnis wenn ich euch
sage das man den Bootblock auch mit eigenen Programmen vollstopfen kann.
Deshalb werde ich hier sofort zeigen wie man es macht, ohne das ich es auf
die geheimnisvolle Tour mancher Buecher beschreibe.
Wie gesagt kann ich allesmoegliche in de Bootblock setzen.
Es darf allerdings nicht groesser als 1 Kilobyte = 1024 Byte sein.
Davon ab geht allerdings der Boot Header, der Die DOS Kennung, die
Checksumme des Bootblocks, und die Adresse des Rootblocks enthaelt.

Jetzt kann man sein Programm reinschreiben, und wenn man sich mit dem
Trackdisk-Device auskennt, kann man den Rest von Ihm erledigen lassen.
Falls aber nicht, muss man noch eine kleine Routine hinter unser Intro
Pflanzen, die dann den Boot vorgang weiter fuehrt.

Wenn wir davon ausgehen das wir nicht mit dem Trackdisk Device arbeiten,
sondern die Diskette nach dem Intro ganz einfach weiterdurchlaufen lassen,
so mit Startup-Sequence und dem Ganzen Zeug. Dann belegt die Kennung und
das noetige Programm genau 54 ($36) Bytes. Bleiben uns also `970` Bytes
fuer uns...Da kriegt man schon was mit hin... Also rein in den Bootblock.

Erstmal muesst ihr aber ein Paar sachen ueber den Bootblock wissen.

Das Programm was sich auf dem Bootblock befindet, wird vom Betriebssystem
praktisch als kleines unterprogramm ausfuehrt, deshalb muessen wir bevor
wir den Prozessor in Anspruch nehmen, alle Register retten. Wie wir das
machen kennt ihr ja schon...kommt aber Spaeter.

Das Betriebssystem geht nach einem Reset ein paar Tests durch und verzweigt
dann in die Bootroutine. Diese holt sich dann den Bootblock in den
Speicher, und prueft die die DOS-Markierung am Anfang. Ist die nicht
vorhanden, so wird die Meldung `No DOS Disk` ausgegeben. Ist die aber in
Ordnung, so prueft das Betriebssystem noch die Checksumme des Bootblocks.
Die ist noetig damit klar ist das das programm auf dem bootblock auch O.K
ist. Ist diese Checksumme nicht In Ordnung, dann stellt sich das
Betriebssystem dumm und wartet weiter auf eine Diskette. Andernfalls wird
das Programm, egal was es macht ausgefuehrt....Das ist der Punkt warum
Viren oft in Bootbloecken liegen, sie werden dort sofort gestartet, sobald
die DOS kennung und die Checksumme (Pruefsumme, auf Deutsch) In ordnung
ist.

Wenn man jetzt denkt das sich die Herren Programmierer eine besonders
Heimtueckische Berechnung der Checksumme ausgedacht haben, weil man halt
doch auch sehr viel unfug damit treiben kann, dann denkt man falsch.

`Die Pruefsumme berechnet man wie folgt`

Alle 256 Langwoerter die der Bootblock hat werden addiert, und was dann
noch zur 0 fehlt, also 0 - (Summe der langwoerteraddition) ist die
Pruefsumme.
Beispiel: Haben wir alle Langwoerter addiert, ung kommen auf das Ergebnis

$3ef6547a

Dann ziehen wir das nur von Null ab..

$00000000 - $3ef6547a = $c109ab86

So einfach bekommt man die Checksumme. Kein Besonderer Schutz, wenn man
ueberlegt was sich eben diese leute ausgedacht haben um an die Pruefsumme
des Kickstarts zu kommen. Naja die Grundlagen habt ihr jetzt, dann will ich
mal das Orginal Bootblock listing des Install-Befehls rausruecken..


Kennung: dc.b "DOS",0 ; Dos-kennung, endet mit 0
Chksum: dc.l $00000000 ; Platz fuer die Checksumme
Rootblock: dc.l $00000370 ; Adresse des Rootblocks

Bootprg:
Lea Resname(PC),a1 ; Zeiger auf Dos.library
Jsr -96(a6) ; Resident Routine von Exec
Tst.l d0 ; Auf Fehler testen
Beq.s Error ; Falls fehler Error Routine anspringen
Move.l d0,a0 ; Falls nicht Adresse als Zeiger nach A0
Move.l 22(a0),a0 ; Langwort das an 22. Stelle steht nach A0
Moveq #$00,d0 ; Kein fehler !
Ende:
Rts ; Raus
Error:
Move.l #$ff,d0 ; Fehlercode nach d0
Bra.s Ende ; Raus

Resname: dc.b "dos.library",0

Die .S bei dem Beq und dem Bra-Befehl habe ich dran gehaengt..ist dann
etwas kuerzer !
Achja noch ein paar sachen zum Dos und so. Wenn ein Programm ins CLI
zurueckkehrt dann wird in D0 der Status mitgeschickt. 0 wenn alles in
Ordnung ist, und etwas anderes wenn was falsch gelaufen ist. Bei unseren
Programmen muessen wir das natuerlich von Hand machen. Hab ich auch immer,
schaut euch die Listings mal an, dort steht vor dem RTS immer das
Moveq #0,d0 welches dem CLI sagt `Kein Fehler`.

Desweiteren hab ich ja schon Erzaehlt das die Exec-Library immer offen ist,
und man nur noch den zeiger aus $00000004 lesen muss um damit zu arbeiten.
Ihr werdet euch also wundern wie ich eine Routine von Exec benutzen kann,
wenn ich das nicht Initialisiert habe. Das ist auch einfach. Der Grund warum
ich die meisten register Retten muss war doch das in ihnen wichtige Daten
und Adressen sind. Nun zwei davon sind sehr wichtig. In A6 steht der Zeiger
auf Exec, und in A1 steht der zeiger auf das Trackdisk-Device. Was wir mit
dem Trackdisk-Device anstellen koennen, kommt spaeter.

Da das Prograemchen gut Erklaert ist, muesste man eigentlich keine
Probleme haben es nachzuvollziehen. falls nicht ist es auch nicht so
schlimm, denn das einzige was wir mit dieser Routine machen ist sie immer
wieder ein unsere Bootblock Intro`s zu setzen....mehr nicht !

So, jetzt wollen wir mal den Bootblock irgendeiner Disk von Hand
`Installieren`. Naja, was heisst von Hand, wir bedienen uns eines
programmes das man im allgemeinen Monitor nennt. Das das nicht das Ding ist
auf das ihr oefters mal schaut ist klar. Ich benutzen den Amon, den Ihr im
Tools oder Do-Ami Brett findet. ich setze euch die neuste Version rein.

Als erstes muessen wir aber mal den Bootblock erzeugen.
Dazu setzen wir vor das eigentliche Listing die Org/Load Kombination, und
lassen uns das teil nach $40000 schreiben.

Bevor wir das Ding assemblieren, muessen wir den Bereich den wir
abspeichern noch Saeubern, damit wir keinen Muell im Bootblock haben. Das
machen wir mit dem Fill kommando des Seka.
Wir muessen 1 KiloByte loeschen, also von $40000 bis $40400.

Dann assemblieren wir den Krempel, starten ihn aber nicht (haette auch
nicht viel Sinn). Jetzt kommt das WI kommando, das den Speicherauszug in
dem der Bootblock steht auf Diskette speichert..Wir nennen es mal einfach
Bootblock (Sprich: BuhtBlock). Jetzt verlassen wir den Seka, und laden den
Amon...Hier erstmal eine Befehlsuebersicht der wichtigsten kommandos mit
kleinen Kommentaren..

? Help Seite, alle Kommandos mit benoetigten
Parametern und Syntax
x Exit, Raus, Fin, Sense, Schluss...usw.
dir Gibt die Aktuelle Directory aus
cd Drive Wechselt das Verzeichnis oder die Disk
cls Loescht den Bildschirm
r Zeigt alle register an
a Addr Schaltet einen Assembler an ab Adresse Addr
d Addr1 Addr2 Diassembliert den Bereich von Addr1 bis Addr2
m Addr1 Addr2 Zeigt den Bereich von Addr1 bis Addr2, Addr2 kann
weggelassen werden
: Addr Bytes Zum veraendern von `Bytes` Bytes ab Adresse Addr
f Addr1 Addr2 Bytes Fuellt den Speicher von Addr1 bis Addr2 mit Bytes
[ Addr Name Laedt File nach Addr von Aktueller Disk
] Addr Lenght Name Schreibt File ab Addr auf Aktuelle Disk
Addr Drive Block Cnt Schreibt Blocks nach demselben Schema wie
gelesen wird
del name Loescht Programm auf der Aktuellen Disk
= Addr Diskblock Checksumme
# Addr Bootblock Checksumme
g Addr Programm ab Addr ausfuehren
c Addr1 Addr2 Dest Vergleicht den bereich Dest mit dem bereich Addr1
bis Addr2
t Addr1 Addr2 Dest Copiert bereich von Addr1 bis Addr2 nach Dest
h Addr1 Addr2 Bytes Sucht von Addr1 bis Addr2 nach bytes
n num rechnet zahlen in die verschiedenen Systeme um.

Also, da ihr jetzt die wichtigsten Befehle des Amon kennt koennen wir ja
weiter machen.
Ihr habt jetzt das Image des Bootblocks auf als normales File auf Diskette.
Jetzt den Amon laden und mit cd drive, auf die Diskette gehen wo das Image
drauf ist. Da der Bootblock noch ein normales File ist, muessen wir ihn
auch normales File einladen. Das geschieht mit [ - und die Adresse wo wir
es hinhaben wollen dahinter, wir sollten uns die merken, sie ist fuer das
weitere arbeiten wichtig. Also gehen wir mal davon aus das wir das
Bootblock Image nach $40000 geladen haben. Der Amon gibt aus von wo bis wo
das Zeug liegt.
Das was ich bis jetzt gemacht habe koennten wir aber auch mit dem Seka
machen, ohne dabei ins Schwitzen zu geraten. Allerdings kommen jetzt die
Sachen die den Amon so wertvoll fuer uns machen.

Vielleicht ist euch die Funktion # Bootblock Checksumme schon aufgefallen.
Ist atuerlich super das man das mit dem Amon machen kann. Besser ist
allerdings noch das er sie auch gleichzeitig in die richtige Adresse
schreibt. Bei dem Aufruf `# 40000` rechnet er die Checksumme aus und setzt
sie an den dafuer reservierten Platz. Der Bootblock steht jetzt also
lauffaehig im Speicher. Das problem ist jetzt nur noch wie wir ihn auf die
Disk kriegen.

Der Bootblock ist kein File, eigentlich sind es auch 2 Bloecke. Er wird
einfach in Block 0 und 1 auf die Disk geschrieben. Das koennen wir mit dem
Amon auch, naemlich mit > 40000 1 0 2. Wobei die werte klar sind

40000 Adresse ab der die Daten im Speicher liegen
1 Drive Df1:
0 Bei Block null beginnen
2 Insgesamt zwei Blocks schreiben

Sobald ihr das gemacht habt, koennt ihr die Diskette aus dem Drive Df1:
entnehmen und in Df0: schieben. Nach Reset muesste der Amiga normal Booten.
Es muss keine Lauffaehige Diskette sein um zu sehen das es tut. Sobald die
anseren Laufwerke anfangen zu initialisieren oder spaetestens wenn das
Amiga-Dos fenster steht wisst ihr das es geklappt hat.

Ihr koennt euch ja schonmal was fuer euer Bootintro ueberlegen. Die
Programmierung des Bootblocks unterscheidet sich nur in zwei Punkten von der
der normalen Intros.

1. Alles sollte moeglichst kurz gehalten werden.
2. Adressen im Programm sollten immer PC-Relativ ermittelt werden.

Hmmm, PC-Relativ...komisches Wort, was ist das ?
Tja, das ist eine weitere Art Adressen zu erhalten. Dabei nimmt man wie bei
den Branch-befehle nicht die Adresse des Ziels selber sondern die Distanz
vom Augenblicklichen PC.
Der Sinn ist einfach - Man kann dadurch ein Programm schreiben das ueberall
im Speicher laufen kann. Es ist egal wo es liegt, da die Adressen innerhalb
des Programms eben alle vom PC aus gerechnet werden. Warum wir das so
machen, ist auch klar. Der Amiga laedt den Bootblock nicht an eine feste
Adresse sondern da wo er gerade Platz hat. Und auch waehrend der Amiga
laeuft und ein Bootblock ist noch Aktiv, muss er ihn hinsetzen koennen wo
er will. Deshalb diese Adressierung.
Also beispiel mal einen Bootblock der eine klein Copperliste Aktiviert, und
auf die Maustaste wartet, um dann die alte Copperliste zu initialisieren
und den Bootvorgang weiterzufuehren.

Org $40000
Load $40000

Kennung: dc.b "DOS",0
Chksum: dc.l $00000000
Rootblock: dc.l $00000370

Bootprg:
Movem.l d0-d7/a0-a6,$70000
Lea Copperliste(PC),a0 ; Adresse der Copperliste wird
Move.l a0,$Dff080 ; PC-Relativ geholt
Wait: Btst #6,$Bfe001
bne.s Wait
Move.l a1,a2
Lea Gfxname(PC),a1
Move.l #$00,d0
Jsr -552(a6)
Move.l d0,a4
Move.l 38(a4),$dff080
move.l a2,a1
movem.l $70000,d0-d7/a0-a6
Orgroutine: ; Jetzt kommt erst die Orginal
; Bootroutine
Lea Resname(PC),a1
Jsr -96(a6)
Tst.l d0
Beq.s Error
Move.l d0,a0
Move.l 22(a0),a0
Moveq #$00,d0
Ende:
Rts
Error:
Move.l #$ff,d0
Bra.s Ende

Resname: dc.b "dos.library",0
Gfxname: dc.b "graphics.library",0

even

Copperliste:
dc.l $800ffffe ; Copperliste
dc.l $01800f00
dc.l $a00ffffe
dc.l $01800000
dc.l $-2


Wenn ihr diesen Bootblock genauso behandelt wie den ersten, dann muesste er
laufen. Ihr koennt an dem Listing sehr schoen sehen wie ich den Zeiger auf
die Copperliste ermittle.
Den `Movem` befehl benutze ich zum retten der Register, so koennt ihr den
auch mal im einsatz sehen. Schaut euch bitte beide Befehle, also den zum
Retten und den zum Zurueckholen gut an, falls ihr den etwas falsch macht
stuerzt der ganze Krempel ab.
Ihr muesst natuerlich nicht alle Register retten, sondern nur die die ihr
braucht. Der Movem befehl wird dadurch aber nicht kuerzer, und genauso
wenig weiss man welche Register man letztendlich benutzt, deshalb rette ich
immer alles, und damit hat sich die sache.

Mehr muesst ihr bei der Bootblock Programmierung nicht beachten.
Also los...schreibt eure Intro`s.

Ich geb ja zu des es viel Aufwand ist um so einen bloeden Bootblock zu
kopieren. Deshalb hab ich hier ein Programm das alles alleine macht.

Bootblock lesen
Checksumme errechnen
Neuen Bootblock schreiben

Allerdings nur auf DF0:, da ich mich mit dem Trackdisk.device noch nicht so
auskenne.
Aber ich denke das werde ich noch nachreichen, ausserdem kann man sich auch
sehr viel kaputt machen. Deshalb nur die Einfache Version.

Dieses Programm ist vollgestopft mit Betriebssystem-Routinen, die ihr nicht
in diesem Kurs lernen werdet. Ihr koennt euch aber in der Ueblichen
Literatur darueber schlau machen.
Die Stelle wo ihr euer Bootintro reinsetzen koennt hab ich angekreuzt.

Execbase = 4
Findtask = -294
Addport = -354
Remport = -360
Openlib = -408
Closelib = -414
Opendev = -444
Closedev = -450
Doio = -456

Start:
Move.l Execbase.w,a6 ; Vorbereitungen zum oeffnen des
Sub.l a1,a1 ; `trackdisk.device`
Jsr Findtask(a6) ; Eigenen Task suchen
Lea Readreply(pc),a0
Move.l d0,$10(a0)

Lea Readreply(pc),a1 ; Port holen
Jsr Addport(a6)

Lea Diskio(pc),a1 ; IO_Struktur in a1
Move.l #0,d0
Clr.l d1
Lea Trddevice(pc),a0 ; oeffne Trackdisk.device
Jsr Opendev(a6)
Tst.l d0
Bne Error

Lea Diskio(pc),a1
Lea Readreply(pc),a0
Move.l a0,14(a1)
Move.w #2,28(a1) ; Lese Orginal Boot-track
Move.l #$40000,40(a1)
Move.l #22*512,36(a1)
Move.l #0*512,44(a1)
Move.l Execbase.w,a6
Jsr Doio(a6)

Bsr Checksum ; Routine zum errechnen der neuen
; neuen Checksumme anspringen
Bsr Copy ; Eigene Routine Kopieren

Lea Ciskio,a1 ; Neuen Boot-track schreiben
Move.w #3,28(a1)
Move.l #$40000,40(a1)
Move.l #22*512,36(a1)
Move.l #0*512,44(a1)
Move.l Execbase.w,a6
Jsr Doio(a6)

Lea Diskio,a1
Move.w #9,28(a1) ; Laufwerk Stoppen
Move.l #0,36(a1)
Jsr Doio(a6)

Lea Readreply(pc),a1 ; Port loeschen
Jsr Remport(a6)

Lea Diskio(pc),a1
Jsr Closedev(a6) ; Trackdisk.device schliessen
Error:
Rts ; Ende

Trddevice: dc.b 'trackdisk.device',0
even
Diskio: blk.l 20,0
Readreply: blk.l 8,0

Checksum:
Lea BootBlockIntro+8(pc),a0 ; Adresse an der die neue Checksumme
; liegen muss
Lea $3f8(a0),a1 ; Ende des Bootblock (1 KB)
Clr.l d0
Clr.l d1
Move.w #$444f,d0 ; Routine die die Checksumme errechnet
Move.w #$5300,d1
L1: Add.w (a0)+,d0
Bcs M1
W1: Add.w (a0)+,d1
Bcs M2
W2: Cmp.l a0,a1
Bne L1
Not.w d0
Not.w d1
Swap d0
Move.w d1,d0
Move.l d0,BootBlockIntro+4
Rts
M1: Add.w #1,d1
Bra W1
M2: Add.w #1,d0
Bra W2

Copy: Lea BootBlockIntro(pc),a0 ; Vorbereitung zum Kopieren an eine
Lea $40000,a1 ; Feste Adresse ($40000) um es vom
; Trackdisk.Device auf Disk schreiben
; zu lassen
Move.w #$3ff,d0 ; Laenge 1024 byte
Loopy: Move.b (a0)+,(a1)+
Dbf d0,Loopy
Rts
; Jetzt kommt das eigentlich Bootintro
; Was auf Diskette geschrieben wird
BootBlockIntro:

Kennung: dc.b "DOS",0 ; DOS-Kennung
Chksum: dc.l 0 ; Platz fuer die Checksumme
Rootblock: dc.l $00000370 ; Adresse des Rootblocks

Movem.l d0-d7/a0-a6,-(a7) ; Register Retten


; / Hier koennt ihr euer Intro reinpflanzen
; / Achtet nur auf die Laenge !


Movem.l (a7)+,d0-d7/a0-a6 ; Register holen
Lea Dosname(pc),a1 ; Normale Bootroutine
Jsr -$60(a6)
Move.l d0,a0
Move.l $16(a0),a0
Moveq #00,d0
Rts ; Ende des Intros

Dosname:dc.b "dos.library",0

Achja, ich habe das Orginal-Boot-Programm noch etwas gekuerzt, somit habt
ihr noch ein bisschen mehr platz, ihr werdet das sicher schon gemerkt
haben.

Um nach dem Assemblieren die laenge des Intro`s zu ermitteln setzt ihr
einfach unter das Dosname noch ein Label, zum Beispiel mit dem Namen
`EndeIntro`

Dann lasst ihr es assemblieren, und schreibt danach bloss

? EndeIntro-Bootblockintro

Und schon gibt der Seka die Laenge in Bytes an.
Da bei dieser ausrechnug schon der Header und das Orginalprogramm drin ist,
kann der wert die vollen 1024 Bytes erreichen.

So, jetzt will ich nochmal ein paar Worte ueber die PC-Relative
Adressierung verlieren, denn es ist wichtige das man sie verteht.
Denn man kann ein bisschen ins Schleudern damit kommen, wenn man einerseits
im Listing diese Adressierung sieht, aber gleichzeitig eine Org/Load
Kombination auf $40000 sieht.

Vergleichen wir die PCR (Schreibe ich ab jetzt fuer PC-Relativ) mal mit der
Normalen.
In unseren Intro schreiben wir wenn wir die Adresse einer im Source
abgelegten Copperliste zu ermitteln ungefaehr das..

Move.l #Copperliste,a1
oder Lea Copperliste,a1

...
...
...

Copperliste:

blabla
bla bla
blablabla

Der Assembler traegt dann direkt beim Assemblieren die Adresse der
Copperliste hinter den Move.l oder Lea Befehlen ein.
Wenn das Programm wie ein Intro immer an derselben Stelle laeuft ist das ja
auch in ordnung. Aber wenn das Programm verschoben wird, muss es sich die
Adresse Selber errechnen. Das macht es mit der PCR.
Dabei wir hinter dem Move.l oder Lea Befehl nur noch der Wert geschrieben
der vom Augenblichlichen PC abgezogen oder dazuaddiert werden muss, damit
man die Adresse erhaelt.

Wen also der Prozessor bei $40040 auf ein Move.l #$20(PC),a1 trifft dann
schreibt er in A1 $40060. Is doch einfach, oder.

Und da die Adressen in Programmen die verschoben werden immer
unterschiedlich sind, die Distanzen der Befehle und Tabellen aber immer
gleich, nimmt man sie.
Ihr koennt die PCR aber auch in den Intros benutzen...kuerzer als die
Absolute ist sie naehmlich auch, das sie normal fuer die Distanz immer nur
ein Wort braucht, und der Absolute ja die Adresse in einem Langwort
speichert.

Nunja, euren Ideen sind da nur Grenzen durch die laenge gesetzt. Es gibt ne
menge sinnvolles was man da machen kann. Aber auch unsinniges faellt mir da
ein.

Installiert doch mal den Bootblock hier.

Org $40000
Load $40000

Kennung: dc.b "DOS",0
Chksum: dc.l $00000000
Rootblock: dc.l $00000370

Bootprg:
Movem.l d0-d7/a0-a6,$70000

Execbase =4
DisplayAlert =-90
OldOpenLib =-408
CloseLib =-414

Start:
Move.l Execbase.w,a6
Lea Libname(PC),a1
Jsr OldOpenLib(a6)
Move.l d0,a6 ; IntuitionBase in A6
Moveq #0,d0 ; Alert_Number
Move.l #50,d1 ; Alert_Height
Lea Alert_text(PC),a0 ; Alert_String
Jsr DisplayAlert(a6)
Move.l a6,a1
Move.l Execbase.w,a6
Jsr CloseLib(a6)

Movem.l $70000,d0-d7/a0-a6
Lea Resname(PC),a1
Jsr -96(a6)
Tst.l d0
Beq.s Error
Move.l d0,a0
Move.l 22(a0),a0
Moveq #$00,d0
Ende:
Rts
Error:
Move.l #$ff,d0
Bra.s Ende


Intuitionbase: dc.l 0

Alert_text:
Dc.w 48 ; X-Coord
Dc.b 16 ; Y-Coord

Dc.b "Software Failure - Press A,D,á,5,P,F,E,R,W,I,2 to Leave",0
Dc.b 0

Copperliste:
dc.l $800ffffe
dc.l $01800f00
dc.l $a00ffffe
dc.l $01800000
dc.l $-2

Resname: dc.b "dos.library",0
Libname: dc.b "intuition.library",0
Gfxname: dc.b "graphics.library",0


Nett, ne...

Das ist der Aufruf eines Alerts mit eigenem Text...Ihr koennt aber auch mit
ganz bestimmten Fehlercodes in D0 einen der Standart Guru-Meditation Alerts
aufrufen. Natuerlich ist das nur als Gag mit den vielen Tasten. Ihr koennt
das Ding ganz leicht mit Maus verlassen. Falls ihr die Maus Routine sucht,
die ist in der Display_Alert Funktion der Intuition.library schon
eingebaut.

So, jetzt wisst ihr ja schon eine ganze Menge ueber den Bootblock. Aber
etwas das wisst ihr noch nicht.
Obwohl ich wiegesagt nicht viel von dem Ganzen Betriebssystem-Krempel halte
gibt es da doch etwas was sich unheimlich toll anhoert und auch toll ist.

Obwohl ich, wie ich gerade gemerkt habe, das Kapitel Bootblock nicht
nummerriert habe will ich doch mit der Nummerierung weitermachen.

20.Programmierung des Trackdisk.Device

Die ganze Zeit in der wir mit dem Bootblock gearbeitet haben hab ich immer von
Trackdisk.Device geredet. Jetzt will ich mal rausruecken was das tolle an
ihm ist.

Erstmal will ich erklaeren was ueberhaupt Devices sind. Es gibt sie fuer
sehr viele Sachen...Tastatur, Drucker...Musik usw.

Was ist ein Device ueberhaupt ?

Devices sind Programme die wie Librarys geoeffnet werden muessen. Sie haben
dann direkten Draht zu dem Geraet fuer das sie da sind. Sie sind die
verbindungsstelle zum Laufwerk oder zum Drucker. Das Trackdisk Device ist
die unterste ebene bei der Arbeit mit dem Diskettenlaufwerk.

Da wie ich gesagt habe das TDD (in zukunft fuer Trackdis...) schon offen
ist, entfaellt das oeffnen komplett. Daran muesst ihr denken falls ihr
dieses Device mal ausserhalb des Bootblocks benutzen wollt.

Wenn wir das TDD benutzen so werden wir sehr viel schneller sein als wie
der das AmigaDos selbst, weil wir die sachen erstmal von Hand auf die
Diskette schmeissen, und da natuerlich die optimale Sache einsetzen. Und
die ist nunmal alles hintereinander und in einem durch.

Das Betriebssystem macht es etwas umstaendlicher.
Um den Komfort und einfachere Handhabung fuer die Operation mit Disketten
zu gewaehrleisten arbeitet das AmigaDos etwas anders mit dem TDD, als wie
es wir tun. Das Betriebssystem laedt ein File nach dieser Technik ein..

Sobald der Name im AmigaDos eingeben wurde sucht es den dafuer zu
staendigen eintrag im Directory.
Sobald es den hat, zieht es sich wichtige Daten aus dem Header. Eine Sache
ist die laenge des Programms, damit es weiss wo es das hinladen kann, und
keine vorhandenen Programme zerstoert.
Desweiteren findet das Dos auch die Adresse des ersten Blocks des Programms
auf der Diskette. Wenn es den hat, veranlasst es das TDD diesen Block zu
lesen. Die letzten beiden Worte dieses Blocks sind die Adressen des
naechsten. So hangelt sich das AmigaDos durch die Disk, bis es das
Komplette Programm drin hat. Dies ist noetig weil das AmigaDos Programme
nicht immer hintereinander auf die Diskette schreibt, sondern immer so nahe
wie moeglich an den Directory Bloecken ran, damit der Schreib/Lese Kopf
schnell das Ziel erreicht.

Ihr stimmt mir doch zu das diese Methode sehr umstaendlich ist. Aber sie
hat sich bewaehrt wenn es um geordnete Diskette und Zugriffszeit geht.

Aber das TDD kann mehr !

Denn das TDD verwaltet die Diskette nicht in Tracks und Sectoren wie das
AmigaDos und die Laufwerke. Fuer das TDD ist die Diskette in Bloecke a 512
Bytes unterteilt, und die sind von Null bis 1760 oder so durchnummeriert.

Wenn wir also mit den Amon ein Programm, welches an Festen Adressen, wie
zum Beispiel unsere Intro`s liegt, einfach Block fuer Block hintereinander
auf Disk schreiben, dann kann es das TDD mit einem Schlag einladen, ohne
das man dafuer ein Directory braucht. Den Effekt kennt ihr bestimmt von
manchen Orginalspielen oder MegaDemos kennt, das obwohl sehr viele
Programme auf der Diskette sein muessen, keine Directory vorhanden ist.

Und das macht es sehr viel schneller.

Ein vergleich..

Ich habe mit dem Seka ein Testfile auf Diskette installiert. Es ist 64
Kilobyte lang. Ansonsten war die Diskette leer.
Um dieses File mit Read Image wieder einzuladen braucht der Seka 7
Sekunden.

Danach habe ich dasselbe File mit dem Amon auf Diskette geschriebn, und
zwar so wie ich es mit dem TDD laden kann.
Ich habe den Bootblock dafuer geschrieben, indem der Aufruf steht, und ihn
auf die Diskette gelegt.
Da ich das Ende der Bootblock identifikation und der Anfang des Ladens
durch das TDD nicht ermitteln kann, sage ich halt wieviel das Laufwerk
gebraucht vom Booten bis zum drinhaben des Files.

Ganze 3.5 Sekunden, fuer alles beide..Das ist doch sehr schnell finde ich.

Desweiteren eignet sich das TDD fuer unsere Zwecke, wenn wir mal von
nachladen waehrend ein Programm laeuft reden. Da das TDD einen eigenen task
belegt koennen wir es Synchron zu anderen Sachen laufen lassen. Es ist
naemlich ein toller Effekt wenn nachgeladen wird und ein sogenanntes
`Loading Demo` laeuft.

Und da es so toll ist, will ich euch mal zeigen wie man damit umgeht.

Erstmal muessen wir uns bevor wir anfangen zu Experimentieren ein Testfile
anlegen. Wir nehmen wie ich ein 64 Kilobyte-File. Damit wir auch sehen das
es ueberhaupt laedt, und wenn, dann wann es fertig ist, schreiben wir noch
ein kleines Prograemmchen davor welches die Bildschirm farbe aendert.

Also wir schreiben im Seka folgendes....

Org $40000
Load $40000

Start:
Move.w #$0f00,$dff180
Bra.s Start

So nun legen ihr eine Formatierte Diskette ein die aber ansonsten leer ist.
Nunja, sie muss nicht leer sein, aber ein Teil der Daten die sich auf ihr
befinden werden Floeten gehen.
Also nachdem die Diskette eingelegt ist, Assemblieren wir das Programm.
Sobald das geschehen ist, schreiben wir das fertige Image auf Disk.
Wir muessen 64 Kilobyte abspeichern, dazu geben wir den Bereich von $40000
bis $50000 an. Sobald sich das Image auf Diskette befindet, muessen wir es
TDD-gerecht auf Disk schreiben.

Dazu benutzen wir den Amon...

Erstmal laden wir das Image in den Arbeitsspeicher. Das geschieht mit

[ 40000 Testfile

Jetzt liegen die 64 Kilobyte ab $40000

Nun muessen wir sie auf Diskette kriegen. Den Write-Blocks befehl vom Amon
kennt ihr ja schon. Nun muessen wir uns nur noch einen Standpunkt auf
Diskette ausdenken. Dazu nehmen wir den Teil direkt hinter den Bootblocks.

Der Befehl lautet

> 40000 1 2 80

40000 Von $40000 an
1 Df1:
2 ab Block 2
80 80 Blocks

Damit duerfte das File Korrekt geschrieben werden.

Jetzt muessen wir uns um den Bootblock kuemmern der das dann wieder
einlaedt.
Wiegesagt muss man sich wenn man mit dem TDD arbeitet nicht mehr um die
Routinen kuemmern die das Ordnungsgemaesse abarbeiten der Bootroutine
organisieren. Wir koennen uns also nur mit dem Aufruf fuer das TDD
kuemmern.
Der Aufruf sieht so aus, das wir dem TDD folgende Daten geben

Den Jobcode, ob laden oder speichern
Die Laenge in Bytes
Das Ziel an das es im Speicher geschrieben werden soll
und ab wo das File auf Diskette anfaengt.

Nachdem ich das alles geschrieben haben muss ich das TDD nur noch
veranlassen diese Operation auszufuehren. Der Bootblock saehe dann so aus..

Da ich die Werte nicht in irgendwelchen Register uebergebe, sondern an ganz
bestimmten stellen im TDD, schreibe ich sie Direkt dort hin, und zwar so.

Jobcode nach $1c(a1), also an die 28($1c) Stelle des TDD
Die Laenge in Bytes nach $24(a1)
Ziel im Speicher nach $28(a1)
Ab dem Wievielten Byte auf Disk es anfaengt, dort kann man aber nur
anfaenge von Blocks angeben.

Zum Schluss noch eine Routine im Exec, die alles veranlasst. Diese Funktion
heisst DoIO fuer Do Input/Output

Danach nur noch der Jmp-Befehl der das geladene ausfuehrt.

Das Bootblock-listing sieht dann so aus..

Org $40000
Load $40000

Kennung: dc.b "DOS",0
Chksum: dc.l 0
Rootblock: dc.l 0 ; Wiegesagt, der Rootblock ist nur
; bei Diskette mit Directory noetig
; Deshalb lasse ich ihn weg.

Move.w #$02,$1c(a1) ; Jobcode `Lesen` nach $1c
Move.l #$10000,$24(a1) ; Laenge in Bytes nach $24
Move.l #$40000,$28(a1) ; Ziel im Speicher
Move.l #$400,$2c(a1) ; Ab Byte $0400 auf Disk
Jsr -$1c8(a6) ; DoIO
jmp $40000 ; Geladenes anspringen.

Das ist doch garnicht so schwer, oder ?

Wenn man mehrere Programme, wie zum Beispiel bei einem Megademo, laedt kann
man das Programm auch mit JSR anspringen, nachdem das Intro dann mir RTS
verlassen wird, wird der Bootblock, sofern er nicht zerstoert ist weiter
abgearbeitet.
Deshalb arbeiten die meisten Demosteuernden Bootbloecke so, das sie erstmal
den Teil der das Demo steuert an einen Feste Adresse Kopieren, damit sie
genau wissen wo er liegt, und sicher ist das er Intakt ist.

Die Eintragungen in den Registern des TDD sind einfach. Sie entsprechen den
Werten die man auch mit dem Seka hat. Ich rede von der Laenge und der
Adresse.
Mit dem Amon ist das aber etwas anders. Dort werden Anfang auf Diskette und
die Laenge mit der Anzahl der Blocks auf Diskette angegeben. Um diese Werte
zu erhalten, muessen wir die Werte des Bootblocks nur durch 512($200)
teilen.

Damit waere der Anfang auf Diskette $400/$200 = 2
Und die Laenge $10000/$200 = 80

Somit wisst ihr auch wie ihr den Aufruf

> 40000 1 2 80

zu verstehen habt...

Es stehen bei der Arbeit mit dem Trackdisk.device genau 1760 Blocks zur
verfuegung. Das sind die vollen 880 Kilobyte, die die Diskette hergibt.
Und das ist eine ganze menge, da kriegt man ne` menge Demo rein.

Man kann natuerlich auch die beiden Modi kombinieren. Das ist noetig wenn
man zum Beispiel eine Diskette mit vielen Programmen rumgibt die sich die
Leute aber Kopieren sollen koennen, und man ein etwas laengeres Bootintro
hat, dann kann man die Blocks die das laengere Bootintro braucht als Belegt
kennzeichnen. Sie werden dann nicht von AmigaDos benutzt.

Man kann, wie ihr gesehen habt, die direkte Adressierung zu benutzen, wie wir
es mit den Intros und so gewoehnt sind. Denn geben ja die Adresse an wo es
hingeladen werden soll. Man kann aber nur wenn man aus dem Bootblock
herauslaedt mit den Werten um sich schmeissen. Sobald aber andere
Programme Laufen sollte man sich entweder vergewissern ob der Bereich frei
ist, oder sich halt von Betriebssystem eine Adresse geben lassen wo es hin
kann. Den sonst stuerzt der Amiga sofort ab wenn ein wichtiges Programm
ueberschrieben wird.

Mehr muss eigentlich nicht beachtet werden wenn man mit den TDD arbeitet.
Ich benutze es sehr selten. Es ist auch nur interresant wenn man viel
Demos, MusikShow oder Diashows Produziert und herumreicht. Die Vorteile
liegen dort klar auf der Hand.

1. Die sachen sind alle sehr schnell eingeladen.
2. Irgendwelche Lamer`s koennen die Diskette nicht zerpfluecken koennen und
als ihr Werk herumreichen koennen.
3. Macht es natuerlich noch einen sehr guten eindruck.

Ich schaetze die Gruende sprechen fuer sich. Experimentiert noch ein
Bisschen damit rum, und macht mal ein paar Test, von wegen den Zeiten die
Zum laden gebraucht werden, damit ihr sicherer in der Handhabung des TDD
werdet. Denn nicht ist aergelicher als wie wenn man `mal eben` einen TDD
Bootblock schreiben will, alles wieder neu lernen muss.

So, noch ein paar Tips zum TDD und dann gehts weiter.

Ihr koennt natuerlich auch gepackte Files auf die Disk `Tracken`, und sie
dann von dem TDD einladen lassen. Ich will mal kurz erklaeren wie da mit
Bytekiller-Files gemacht wird.

1. Demo/Intro packen.
2. Gepacktes File in den Speicher laden.
3. Ohne Hunks wieder abspeichern
4. Mit Amon auf die Disk Tracken.
5. TDD Bootblock schreiben.

Beim Bytekiller ist es so das die Decrunchroutine PC-Relativ geschrieben
ist, und deshalb ueberall laeuft. Ihr koennt sie deshalb ueberall hinladen.
Ausser halt dahin wo das entpackte liegt.
Ich habe mir angewoehnt meine Intro`s immer erst ab $38000 zu beginnen.
Dann habe ich immer knapp 100 KB zur verfuegung um darin das gepackte unter
zu kriegen.
Beim Bytekiller speziell sieht es so aus das die ersten 34 Bytes,
Hunk_Codes sind. Deshalb muesst ihr wenn ihr das File geladen habt, mit dem
Abspeichern um 34 Bytes spaeter beginnen.

Beispiel:
Ihr habt das vom CLI lauffaehige Intro nach $40000 geladen, dann muestt ihr
es ab $40022 abspeichern. Dann faengt es genau mit dem ersten Befehl der
Entpackroutine an.
Das mit dem Tracken muesst ihr dann selber ausrechnen. Bitte denkt daran,
das ihr auch bei der Laenge auf die naechste 512($200) Byte aufrunden
muesst. Bei anderen Werten kann es schwierigkeiten geben.

Wenn ihr euch jetzt ein bisschen mit der Bootblock-Programmierung
beschaeftigt, dann empfehle ich euch das Studium der Librarys. Die
Funktionen sind zwar sehr langsam, aber da wir ja nicht grosses machen ist
es ganz nuetzlich sie zu kennen. Denn so ein Aufruf zu einer bestimmten
Funktion ist sicherlich kuerzer als wie wenn man diese Funktion selber in
den Bootblock schreibt.

Ich habe auf einer Diske noch einen Bootblock-Source gefunden. Er zeigt was
man wenn man ein bisschen geschickt programmiert, alles mit dem Bootblock
anstellen kann.

Ich habe den Source ein bisschen Editiert, aber nur die Befehle selber
verrueckt und das ganze der lesbarkeit halber etwas auseinander gezogen.
Den Rest habe ich so gelassen, denn wer einen solche Bootblock schreiben
kann, der verdient es das man ihn erwaehnt.

Hier erstmal der Source...

ORG $40000
LOAD $40000

Bootblock:
dc.b "DOS",0
dc.l 0


dc.l $00000370

; Library Addressen und Offsets

Execbase= 4

OldOpenLibrary= -408
InitBitMap= -390
InitRastPort= -198
ClearScreen= -48
RectFill= -306
FindResident= -96
ScrollRaster= -396
Text= -60

; ChipAddressen

OffsetBase= $dff000
VPOSR= $6
DMACON= $96
BPL1MOD= $108
BPL2MOD= $10a
DIWSTRT= $8E
DIWSTOP= $90

; Konstanten

SETCLR= $8000
DMAEN= $100
DIWSTRTV= 44
DIWSTRTH= 129
DIWSTOPV= 244
DIWSTOPH= 193

; Hardware Register

LeftMouseButton= $bfe001

; Tabellen

RastPort= $70000
BitMap= $70200
Copperlist= $7a000
JumpAddr= $6c

Start: movem.l d0-d7/a0-a6,-(sp) ; Alle Register auf Stack retten

; graphics.library oeffnen

Move.l execbase,A6
Lea Gfxlibname(PC),A1
Jsr OldOpenLibrary(A6) ;Graphics Library oeffnen
Move.l D0,A6 ;Gfxbase nach A6

; Bildschirm Variable initialisieren

Lea OffsetBase,A0
Move.w #SETCLR+DMAEN,DMACON(A0)
Move.w #6,BPL1MOD(A0)
Move.w #6,BPL2MOD(A0)
Move.w #DIWSTRTV*$100+DIWSTRTH,DIWSTRT(A0)
Move.w #DIWSTOPV*$100+DIWSTOPH,DIWSTOP(A0)

; Init Copperlist

Lea Copperlist,A0
Move.l #$00e00005,(A0)+ ; MOVE $5 -> BPL1PTH
Move.l #$00e29000,(A0)+ ; MOVE $9000 -> BPL1PTL
Move.l #$01001200,(A0)+ ; MOVE 1 -> BPU0 & 1 -> COLOR
Move.l #$01800000,(A0)+ ; MOVE 0 -> COLOR0
Move.l #$01820000,(A0)+ ; MOVE 0 -> COLOR1
Lea Copperlist,A0
Move.l A0,50(A6) ; GfxBase+50

; BitMap initialisieren

Lea BitMap,A0 ; BitMap
Move.w #1,D0 ; depth
Move.w #368,D1 ; width
Move.w #220,D2 ; height
Jsr InitBitMap(A6)
Move.l #$58e62,BitMap+8 ; erstes BitPlane

; RastPort initialisieren

Lea RastPort,A1 ; rastport
Jsr InitRastPort(A6)
Move.l #BitMap,RastPort+4 ; BitMap Addresse -> RastPort
Jsr ClearScreen(A6) ; Bildschirm loeschen

; Kaestchen in die Copperliste schreiben

Lea Copperlist+20,A0 ; Ende der Copperliste -> A0
Move.w #175,D7
Move.l #$2c09fffe,D5 ; WAIT 8, 44
EinsVertikal:
Move.l D5,D0
Move.w #15,D6
Horizontalline:
Move.l D0,(A0)+
Move.l #$1800000,(A0)+ ; MOVE $000 -> COLOR0
Addi.l #$e0000,D0 ; WAIT Oldx+15, Oldy
Dbf D6,Horizontalline
Addi.l #$1000000,D5 ; WAIT 8, Oldy+1
Dbf D7,EinsVertikal
Move.l #28,D7 ; Kaestchenhoehe = 10
Lea Textscrollfeld(PC),A4
AppendCopList:
Move.l (A4)+,(A0)+ ; Scrollfeld malen anhaengen
Dbf D7,AppendCopList

; The P.R.O.F.I. auf BitMap malen

Lea BobTab(PC),A5
Move.w #30,D7 ; The PROFI = 31 Vierecke
InitBob:
Lea RastPort,A1 ; RastPort
Clr.l D0 ; xl
Clr.l D1 ; yl
Clr.l D2 ; xu
Clr.l D3 ; yu
Move.b (A5)+,D0 ; xl
Addi.w #25,D0 ; xl+25
Move.b (A5)+,D1 ; yl
Move.b (A5)+,D2 ; xu
Addi.w #25,D2 ; xu+25
Move.b (A5)+,D3 ; yu
Jsr RectFill(A6)
Dbf D7,InitBob

; Farbtabelle spiegeln auf 44 Farben

Lea Farbtabelle(PC),A0
Lea Farbtabelle+80(PC),A1
Move.w #20,D7
SpiegelFarbtabelleLoop3:
Move.w (A0)+,-(A1)
Dbf D7,SpiegelFarbtabelleLoop3

Lea Dummy(PC),A0
Move.l JumpAddr,2(A0) ; -> jmp ($6c)
Lea TextScrollen(PC),A0
Move.l A0,JumpAddr ; -> jmp Textscrollen

Mainloop:

; Farben in die Copperliste eintragen

Move.w #21,D7
Lea Copperlist+26,A0
Lea Farbtabelle(PC),A5
Move.w (A5),-8(A0)
FCLop0: Move.w #7,D4
FCLop1: Move.l A5,A1
Move.w #15,D6
FCLop2: Move.w (A1)+,(A0)
Addq.l #8,A0
Dbf D6,FCLop2
Dbf D4,FCLop1
Addq.l #2,A5
Dbf D7,FCLop0

; Farbtabelle rotieren

Lea Farbtabelle(PC),A0
Lea Farbtabelle+2(PC),A1
Move.w #38,D0
Move.w (A0),D1 ; farbe0 -> dummy
RotateFarbtabelle:
Move.w (A1)+,(A0)+
Dbf D0,RotateFarbtabelle
Move.w D1,-(A1) ; dummy -> farbe44

; Warten bis Elektronenstrahl in Zeile 64 ist (WaitTOF)

WaitTOF:
Cmpi.b #64,OffsetBase+VPOSR
Bls.s WaitTOF

Btst #6,LeftMouseButton ; Mousebutton gedrueckt
Bne.s MainLoop ; falls nein -> Mainloop

; Programm beenden

Lea Dummy+2(PC),A0
Move.l (A0),JumpAddr
Lea Dummy+10(PC),A1
Move.l #$20646f73,(A1) ; " dos"

Movem.l (sp)+,d0-d7/a0-a6
Lea Dummy+11(PC),A1 ; Name = dos.library
Jsr FindResident(A6)
Move.l D0,A0
Move.l 22(A0),A0
Clr.l D0
Rts

TextScrollen:
Movem.l d0-d7/a0-a6,-(sp)
Lea RastPort,A1 ; RastPort
Move.w #1,D0 ; dx
Clr.w D1 ; dy
Clr.w D2 ; minx
Move.w #198,D3 ; miny
Move.w #351,D4 ; maxx
Move.w #206,D5 ; maxy
Jsr ScrollRaster(A6)
Lea Scrolltext-2(PC),A0
Subi.b #1,(A0)
Bne.s Bra2
Move.b #9,(A0)
Lea Scrolltext-1(PC),A0
Eori.b #1,(A0)
Lea RastPort,A1
Move.w #320,36(A1) ; Current Pen Position x
Move.w #205,38(A1) ; Current Pen Position y
Lea AktBuchstabe(PC),A2
Addq.b #1,(A2)
Cmpi.b #Textende-Scrolltext,(A2) ;Anzahl der Buchstaben
Bne.s PrintBuchstabe
Clr.b (A2)
PrintBuchstabe:
Lea Scrolltext(PC),A0
Adda.w -1(A2),A0 ; string
Move.l #1,D0 ; count
Jsr Text(A6) ; A1=RastPort

Bra2: Lea Copperlist+6,A1
Lea Scrolltext-1(PC),A0

; Scrollen des grossen Schriftzueges
; er wird durch veraendern der HintergrundBitmap Startaddresse
; gescrollt.

Tst.b (A0)
Beq.s Scrollrunter
Addi.w #46,(A1) ; Scrollwert * 46
Bra.s Hochgescrollt
Scrollrunter:
Subi.w #46,(A1) ; Scrollwert * 46
Hochgescrollt:
Movem.l (sp)+,d0-d7/a0-a6
Dummy: Jmp $ffffffff

Gfxlibname:
dc.b "graphics.library",0

AktBuchstabe:
dc.b $ff

Farbtabelle:
dc.w $005,$006,$007,$008,$009,$00a,$00b,$00c,$00d,$00e,$00f
dc.w $50f,$60f,$70f,$80f,$90f,$a0f,$b0f,$c0f,$d0f,$e0f,$f0f

TextScrollFeld:
dc.l $dc09fffe ; WAIT 8, 220
dc.l $00e2b058 ; MOVE $b058 -> BPL1PTL
dc.l $01800aaf ; MOVE $aaf -> COLOR0
dc.l $de09fffe ; WAIT 8, 222
dc.l $0180086f ; MOVE $86f -> COLOR0
dc.l $e609fffe ; WAIT 8,230
dc.l $01820fff ; MOVE $fff -> COLOR1
dc.l $e709fffe ; WAIT 8,231
dc.l $01820fdd ; MOVE $fdd -> COLOR1
dc.l $e809fffe ; WAIT 8,232
dc.l $01820fcc ; MOVE $fbb -> COLOR1
dc.l $e909fffe ; WAIT 8,233
dc.l $01820faa ; MOVE $faa -> COLOR1
dc.l $ea09fffe ; WAIT 8,234
dc.l $01820f99 ; MOVE $f99 -> COLOR1
dc.l $eb09fffe ; WAIT 8,235
dc.l $01820f77 ; MOVE $f77 -> COLOR1
dc.l $ec09fffe ; WAIT 8,236
dc.l $01820f66 ; MOVE $f55 -> COLOR1
dc.l $ed09fffe ; WAIT 8,237
dc.l $01820f44 ; MOVE $f33 -> COLOR1
dc.l $ee09fffe ; WAIT 8,238
dc.l $01820f33 ; MOVE $f11 -> COLOR1
dc.l $ef09fffe ; WAIT 8,239
dc.l $01820f00 ; MOVE $f00 -> COLOR1
dc.l $f609fffe ; WAIT 8, 246
dc.l $0180042f ; MOVE $42f -> COLOR0
dc.l $f809fffe ; WAIT 8, 248
dc.l $01800000 ; MOVE $000 -> COLOR0

BobTab:

dc.b 65,80,105,90 ; T 1
dc.b 80,90,90,120 ; T 2

dc.b 110,80,120,120 ; H 3
dc.b 120,95,140,105 ; H 4
dc.b 140,80,150,120 ; H 5

dc.b 155,85,165,115 ; E 6
dc.b 160,80,195,90 ; E 7
dc.b 165,95,185,105 ; E 8
dc.b 160,110,195,120 ; E 9

dc.b 0,130,35,140 ; P 10
dc.b 30,135,40,150 ; P 11
dc.b 10,145,35,155 ; P 12
dc.b 0,145,10,170 ; P 13
dc.b 45,160,55,170 ; Punkt 14

dc.b 60,130,95,140 ; R 15
dc.b 90,135,100,150 ; R 16
dc.b 60,145,95,155 ; R 17
dc.b 60,155,70,170 ; R 18
dc.b 90,155,100,170 ; R 19
dc.b 105,160,115,170 ; Punkt 20

dc.b 120,135,130,165 ; O 21
dc.b 125,130,155,140 ; O 22
dc.b 150,135,160,165 ; O 23
dc.b 125,160,155,170 ; O 24
dc.b 165,160,175,170 ; Punkt 25

dc.b 180,135,190,170 ; F 26
dc.b 185,130,220,140 ; F 27
dc.b 190,145,210,155 ; F 28
dc.b 215,160,225,170 ; Punkt 29

dc.b 230,130,240,170 ; I 30
dc.b 245,160,255,170 ; Punkt 31

dc.b 1
dc.b 0

ScrollText:
dc.b "K-Seka Assembler Diskette von The P.R.O.F.I. "
dc.b "many greetings to ICI "
TextEnde:


Hiermit moechte ich das Thema Bootblock, endgueltig beenden. Hab wieder
viel zu viel geschrieben. Aber es ist halt auch eine interressante Sachen.

Der Bootblock ist etwas woran man Programmierer messen kann. Denn dort ist
der Platz sehr begrenzt, und man muss mit alle Haken und Oesen arbeiten.
Dort wird sich auch noch mehr tun.
Es ist nur schade das es Leute gibt die dieses koennen, kurz zu
Programmieren dafuer benutzen einen Virus zu schreiben.

Ich gebe zu das ich mich auch schon damit beschaeftigt habe. Ich habe auch
schon einen Kleinen Virus selber gemacht, aber eben nur zu testzwecken. Ich
habe dabei gemerkt das es wirklich schwer ist etwas so perfekt zu
programmieren wie die Viren neuerer Generation, wie z.b den Butonic, oder
den Revenge-Lamer. Aber wenn man so etwas kann gibt es einem noch lange
nicht das Recht anderen den Spass zu verderben.

Deshalb bitte ich die Leute unter euch die bald besser programmieren
lernen, und die Finessen des Amigas kennenlernen, doch bitte vom
Virenschreiben abzusehen.

Naja, ich will jetzt keinen Moralischen kriegen, aber das gehoert auch
dazu..

So, das war ein etwas laengerer Einschub einer vollkommen anderen Sache.
Machen wir aber jetzt weiter mit dem Blitter, denn wir sind ja noch lange
nicht fertig mit ihm.

21.Logische Verknuepfungen bei Kopieraktionen mit dem Blitter

Wie der Titel schon sagt kann man beim Kopieren zwei oder drei Quellen
logisch miteinander Verknuepfen.
Den Sinn erfahren wir im Laufe der Zeit...

Machen wir erstmal mit den Bobs weiter, wo wir aufgehoert haben.

Nun, im Grunde genommen habt ihr die einfachste Art und Weise der Bobs
schon praktiziert. Naemlich als wir bei dem einfachen Laufschrift Programm
die neuen Buchstaben in den Puffer kopiert haben.
Genausogut koennten wir auch einen Konvertierten Dpaint-Brush dafuer an
irgendeine Stelle auf dem Playfield schreiben.

Nun verstehen manche von euch unter dem begriff Bobs, kleine oder grosse
Grafiken die Munter auf dem Bildschirm rumflitzen.
Das ist aber falsch, denn fuer das rumflitzen sind ausgekluegelte Bob_Move
routinen zustaendig, die sich Positionen holen und alles selber ausrechnen.

Um einen Bob an eine durch X und Y Koordinate fesgelegte Position muss
diese Routine naemlich drei werte aus diesen Koordinaten errechnen.

1. Hardscrollwert X
2. Softscrollwert X
3. Softscrollwert Y

Sobald sie die errechnet hat muessen die Werte in eine Universale
Kopierroutine eingesetzt werden, damit der Blitter diese Aktion auch
ausfuehrt.

Wir arbeiten, wenn wir mit BOBS (Blitter OBjectS) arbeiten, grundsaetzlich
mit den verschachtelten Planes. Das reduziert die Kopieraktion auf eine Pro
Bob, egal wieviel Farben er hat.
Zeitlich ist die Handhabung von Bobs naemlich schon schwer genug. Wenn man
da noch fuer jeden Bobs 2,3 oder sogar 4 Aufrufe machen muesste, das waere
ja zum Heulen.

Manche werden sich jetzt fragen: `Wieso Zeit-Probleme, ich denk der Blitter
arbeitet mit einer Geschwindigkeit von 16 Millionen Punkte in der Sekunde.`

Das stimmt auch im groben und ganzen....aber !!

Rechnen wir mal aus wieviel Zeit wir pro Bildschirmaufbau haben, und ziehen
daraus schluesse, wieviel Punkte dann noch Kopiert werden.

Also bleiben wir erstmal bei der Sekunde.

16 Millionen Punkte = 2 Millionen Byte

Wir haben eine Bildschirmaufbau Frequenz von 50 Hertz. Das heisst alle
50 stel Sekunde wir ein Neuer Bildschirm aufgebaut. Um Ruckelfrei und ohne
Stoerungen arbeiten zu koennen, muessen die Bobs alle 50 stel Sekunde neu
gesetzt werden.
Dieses neusetzen bedeutet aber -> Alten Screen loeschen
Neue Bobkoordinaten holen
Hard und Soft Adressen errechnen
Bobs setzen

Naja, soviel dazu...Aus den 2 Millionen Byte pro Sekunde sind nun
40000 Byte pro Bildschirm aufbau geworden. Da aber ungefaehr die haelfte
der Zeit fuer das loeschen des Bildschirms und die errechnung der noetigen
Werte drauf gehen, kann der Blitter nur noch eine 100 stel Sekunde genutzt
werden. Und das sind nur 20000 Byte.

Treiben wirs mal weiter..
Gehen wir mal von einem Bobs aus der ungefaehr so gross ist..


---------
[ ]
[ ]
[ ]
[ ]
---------

Und in 5 Farben.

Er ist 48 * 50 von den massen, und das ganze mal 5

(48/8 (fuer die Byteanzahll)) * 50 *3 = 1500 Byte

Nun ist das noch ein sehr kleiner Bob. Wenn man aber bedenkt das diese
niedlichen kleinen Geschoepfe immer in Scharen auftreten, dann kann man
sich vorstellen das man echt in schwierig keiten kommt. Wenn man 10 Stueck
Kopiert hat, dann ist die Zeit Floeten gegangen. Und es ist noch nichts
anderes in unserem Schein-Intro gelaufen. Denn da ist noch eine Musik und
mindestens noch die noetige Laufschrift, an der leider die meisten Demos
gemessen werden. Dann weiss man was da getrickst werden muss damit doch
manchmal bis zu 20 oder mehr Bobs auf den Bildschirm kommen.

Dieser Teil des Assemblerkurses ist etwas anders als wie die anderen.
Bisher habe ich bis auf ein paar Werte alles aus der Hand geschrieben, und
auch die Source Codes zusammen mit dem Text geschrieben und hinterher nur
auf Funktion und Fehler ueberprueft. Jetzt wird es anders, denn ich habe
mich bisjetzt nicht an dem Bob-Rekordversuchen mancher Demo/Intro-Schreiber
beteiligt. Jetzt moechte ich das aber gleichzeitigt mit diesem Kurs tun.
Wie ihr seht hab auch ich noch eine ganze Menge noch nicht gemacht. Aber was
soll es, je mehr man kann desto mehr Routine ist dabei. Und ob ihr es glaubt
oder nicht, Copperlisten und Playfields erroeffnen, Blitter Operationen und
Bootbloecke schreiben wird auf die Dauer so laestig das man sich die Sachen
die man braucht einfach aus eigenen alten Source Codes herausschneidet, und
gegebenenfalls noch ein bisschen Editiert.

Das ist nicht besonders gut fuer das Intro oder Demo, da man sich so sehr
wenig mit der Arbeit an dem eigenen Programmierstil beschaeftigt, und das
ist Mist. Aber so laeuft das...

Wir werden also langsam mit den Einfachen Bobs beginnen, und das ist schon
schwer genug, und dann langsam die Anzahl hochschrauben, O.K !

Also fangen wir erstmal mit dem ganz einfachen Bob an.

Erstmal muessen wir ihn Zeichnen..

Um etwas Zeit vornerein zu sparen malen wir nur mit 8 Farben, stellt das
also ein. Und wir malen diesmal auf einem NTSC-Schirm. Das koennt ihr ja
wenn ihr die 3.21 Version von Dpaint benutzt die ich euch empfohlen habe,
einstellen. Ansonsten muesst ihr das mit Dpaint 1 malen.
Wir brauchen einen 32*32 Bob. Stell euch bei Dpaint die Coords ein und
zeichnet ein Rechteck mit dem massen 31*31. Da es bei 0 anfaengt zu
Zaehlen, ist das genau das richtige.
Dieses Rechteck setzt ihr dann in die Linke obere Ecke. Malt nun mit 8
Farben, irgendwas dort rein. Evtl. einen Buchstaben, oder ein kleines Logo.

Sobald das passiert ist, speichert ihr das GANZE Bild ab und verlasst
Dpaint.
Nun beearbeitet ihr das Bild mit dem Kefrens-Converter.
Laden is klar...Ihr stellt danach das Format auf RAW-Blit..
Jetzt fahrt ihr mit der Maus in den Bereich wo das Bild dargestellt wird.
Ihr seht jetzt ein Fadenkreuz. Nun Fahrt ihr auf die Obere linke Ecke des
Bildes und drueckt den Mausknopf (nich wieder loslassen) Jetzt fahrt ihr in
die Mitte des Screens. Wir ihr seht erscheint ein Netz auf dem Bildschirm.
Es stellt die Wortgrenzen des Blitters dar. Fahrt jetzt auf die unterste
Zeile des Bobs. Wenn ihr bis an die rechte untere grenze fahrt, dann muesst
genau in der Mitte des Bobs ein Strich sein. Gut, jetzt fahrt mit der Maus
weiter nch Rechts, bitte nicht die unterste Linie verlassen. Ihr fahrt
solange bis ein neuer Teilstrich erscheint. Sobald ihr ihn seht, fahrt ihr
soweit zurueck bis der Strich unter dem Y-Achsen Strich der Maus
verschwindet. Jetzt koennt ihr loslassen, endlich !

Ihr habt jetzt aus dem Bild den Bob mit Sicherheitsrand ausgeschnitten.
Jetzt koennt ihr dem Ding den Namen `Bob.Raw` geben, und auf Save gehen.
Das mit dem Sicherheitsrand kommt spaeter. Als Grundlage fuer die
Bobbearbeitung soll uns folgendes Listing dienen.
Mit ihm kann man den Eben erstellten Bob auf einem Overscan Bildschirm
Darstellen.

Start:
Execbase= 4
Openlibrary= -408
Vhposr= $dff006
Forbid= -30-102
Permit= -30-108
Bltafwm= $dff044
Bltalwm= $dff046
Bltcon0= $dff040
Bltcon1= $dff042
Bltamod= $dff064
Bltdmod= $dff066
Bltapt= $dff050
Bltdpt= $dff054
Dmacon= $dff096
Intena= $dff09a
Dmaconr= $dff002
Bltsize= $dff058
Cop1lc= $dff080

Move.w #$0020,Dmacon
Move.w #$4000,Intena
Move.l Execbase.w,a6
Jsr Forbid(a6)


Move.l #$60000,a0 ; Clearloop -> Fuellt bereich mit
Move.l #$9588/4,d0 ; $ff auf.
Clearloop:
Move.l #-1,(a0)+
Dbf d0,Clearloop


Bsr.l Makecl ; Copperliste erstellen
Bsr.l Set_bob ; Bos setzen

Wait:
Btst #6,$bfe001
Bne.s Wait

Move.l Execbase.w,a6
Jsr Permit(a6)
Lea Gfxname(pc),a1
Jsr Openlibrary(a6)
Move.l d0,a6
Move.w #$83e0,Dmacon
Move.w #$c000,Intena
Move.l 38(a6),Cop1lc
Moveq #0,d0
Rts
Makecl:
Lea $05f000,a0
Move.l a0,$dff080

Move.l #$00e00006,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$00e40006,(a0)+
Move.l #$00e6002c,(a0)+
Move.l #$00e80006,(a0)+
Move.l #$00ea0058,(a0)+
Move.l #$008e1a64,(a0)+
Move.l #$009039d1,(a0)+
Move.l #$00920030,(a0)+
Move.l #$009400d8,(a0)+
Move.l #$01003200,(a0)+
Move.l #$01080058,(a0)+
Move.l #$010a0058,(a0)+
;----------------------------------------------
Move.l #$01800000,(a0)+
Move.l #$0182071d,(a0)+
Move.l #$0184088e,(a0)+
Move.l #$0186044f,(a0)+
Move.l #$0188000f,(a0)+
Move.l #$018a000c,(a0)+
Move.l #$018c0008,(a0)+
Move.l #$018e0004,(a0)+
;----------------------------------------------
Move.l #$fffffffe,(a0)+
Rts
Waitblit:
Btst #6,Dmaconr ; Auf Blitter warten
Bne.s Waitblit
Rts
Set_bob:
Bsr.s Waitblit
Move.w #$ffff,Bltafwm
Move.w #$ffff,Bltalwm
Move.w #%0000100111110000,Bltcon0
Move.w #0,Bltamod ; Quellmodulo = 0 (rawblit)
Move.w #38,Bltdmod ; Zielmodulo = 38 (planes = $2c)
Move.l #$5e000,Bltapt ; Quelladr.
Move.l #$61088,Bltdpt ; Zieladr.
Move.w #96*64+3,Bltsize ; Bltsize und Start
Rts

Gfxname: dc.b "graphics.library",0

>Extern "bob.raw",$5e000


Das Programm ist sehr einfach gehalten. Schaut euch aber die Set_Bob
Routine gut an. Mit ihr wird im Laufe des Bobabschnitts eine Ganze Menge
passieren.
Ihr koennt die Farben aendern, damit euer Bobs die Orginal Farben. Ihr
koennt das ausprobieren, oder ihr schreibt euch einfach die Farbwerte aus
den Dpaint ab.
Ich habe zwar eine Clear-Routine drin, sie Cleart aber nicht sondern fuellt
den ganzen Bereich mit $ff, somit erhaelt alles eine Farbe, und man kann
den Sicherheitsabstand sehr gut erkennen, denn er ist auch von grosser
Wichtigkeit.

Ueberlegt euch schonmal was der naechste Schritt waere, wenn wir anhand
zwei fester Koordinaten, die Adresse ausrechnen wollen an der der Bob
dargestellt werden soll, und wie man so etwas mit einfachen Befehlen
ermitteln kann.

Hat mal wieder etwas laenger gedauert, aber ich habe im moment sehr viel zu
tun. Unter anderem muss ich mich um ein Netzteil fuer meine Festplatte
kuemmern die ich mir gekauft habe. Das wird auch noch etwas dauern, und
sobald ich alles fuer die Platte habe muss ich sie mir ja auch noch
einrichten und mich damit bekannt machen. Also wenn die Teile jetzt
Zeitlich ein bisschen mehr auseinander liegen, dann denkt nicht ich haette
euch vergessen, es ist einfach nur die Platte.

Aber weiter gehts...

Das einfache setzen eines Bobs duerfte jetzt ja kein Problem mehr sein,
oder ?
Geben wir uns mal an eine Routine die aus einem Wort die X und Y
Koordinaten errechnet und die Werte fuer den Blitter setzt.
Allerdings treten da ein paar wichtige sachen auf die man beachten muss.
Aber fangen wir mal Theoretisch an. Um das Programm einfach zu halten
arbeiten wir nur mit PAL-Koordinaten. Das heisst das wir zwar einen
Overscan Screen haben, aber nur Koordinaten von 0 bis 255 erlauben.

Ich gehe diese Theoretischen durchlauf einer solchen Routine mit einem
Beispielwert durch. Das erleichtert die sache.

Der Beispielwert ist $5167, also Y = $51 und X = $67.
Die Werte fuer den Bildschirm sind.
Bitplanes bei $60000,$6002c,$60058 (Verschachtelt)
Bytes pro Zeile = 44($2c)

Rechnen wir erstmal den Y-Wert aus...

Dazu muessen wir erstmal den anderen Krempel loswerden der noch in dem
Kontrollwort steht. Das machen wir indem wir es durch $100 Dividieren. Dann
steht nur noch die $0051 da.
Also Zeile $51....Da muessten wir normalerweise zum Anfang der Plane nur
$51 * $2c Addieren. Da wir aber drei verschachtelte Planes haben, muss man
den ganzen Krempel noch mal drei nehmen. Damit wir uns eine Multiplikation
zu ersparen nehmen wir die Zeilenzahl direkt mal $84 (3 * $2c).
Bei Zeile $51 muesste dann $51 * $84 + $60000 gerechnet werden.

Da kommt nach Adam`s Riesen $629c4 bei raus. Das ist also der Zeilenanfang
an dem der Bobs steht.

Gehen wir jetzt weiter zur Komplizierteren Routine. Holen wir uns erneut
das Kontrollwort. Mit der Logischen Funktion AND koennen wir den Oberen
Teil ausmaskieren. Bleibt noch die $67, die die X Koordinate in Pixeln
angibt. Da wir aber immer nur 15 Pixel verschieben koennen muessen wir
erstmal mit Hardscrolling so nah wie moeglich an den Punkt rankommen an dem
der Bob dann hinterher steht. Also brauchen wir erstmal die 6. Teilen wir
den Krempel durch $10. Jetzt steht die $6 im Wort. Da wir nicht Byteweise
sondern Wortweise arbeiten, muessen wir die 6 noch verdoppeln. Dazu
multiplizieren wir es nicht, sondern Addieren das Register zu sich selbst
dazu. Denn die Multiplikation des MC 68000 sind eine sehr langsame sache.
Nachdem wir es verdoppelt haben, koennen wir es zu der Adresse der Zeile
dazuaddieren. Dann stehen wir schon sehr nah am Ziel.

Jetzt muessen wir noch den Wert fuer das Softscrollen errechnen..
Dazu maskieren wir alles ausser den Softwert mit AND aus. Nehmen den * $100
um ihn in das Highbyte des hinteren Wortes zu kriegen, und Addieren dazu
einfach den Rest des BLTCON0 Registers dazu.

Diese Routine macht das eben besprochene. Am Ende der Routine steht in A0
die Adresse an der der Bob dargestellt wird, und in D0 das Komplette Word
fuer das BLTCON0 Register.


; Beim Aufruf der Routine muss das Kontrollwort schon feststehen und
; in D7 stehen.

CoordCalc:

Move.l d7,d1 ; Kontrollwort nach D1
And.l #$ff00,d1 ; Alles ausser Y-Werte aus-
; maskieren !
Divs #$100,d1 ; Nach unten schieben
Muls #$84,d1 ; Wert auf dem Screen ermitteln
Add.l #$60000,d1 ; Plus PlayfieldBase
Move.l d1,a0 ; Nach A0 setzen.

Move.l d7,d1 ; Erneut holen !
And.l #$00f0,d1 ; Unwichtiges ausmaskieren
Lsr.l #3,d1 ; Ersetzt Divs $10 und verdoppeln
Add.l d1,a0

Move.l d7,d1 ; !!!
And.l #$f,d1 ; Ausmaskieren !
Swap d1
Lsr.l #4,d1
Move.w #%0000100111110000,d0 ; BLTCON0-Wert ohne Scrollwert
; nach d0
Add.w d1,d0 ; D1 zu D0 addiert ergibt BLTCON0
Clr.l d1 ; Werte nur in D0 und A0 lassen
Clr.l d7

Bsr.l Set_bob ; Bob setzen.
Rts

So diese koennt ihr direkt hinter die Set_Bob-Routine setzen. Da diese
Routine vor der Set_bob aufgerufen werden muss, muesst ihr in dem oberen
Teil des Listings aus dem Bsr.l Set_Bob ein Bsr.l Coordcalc machen.
Davor setzt ihr zum Probieren die Zeile

Move.l #$5167,d7

Damit es schonmal laeuft.
Dann muesst ihr noch die Set_Bob Routine aendern, das nicht mehr die festen
Werte geschrieben werden, sondern D0 nach BLTCON0 und A0 nach BLTDPT
geschrieben wird, das muesstet ihr finden.
Als letztes koennt ihr noch in der Clearschleife aus der -1 eine 0 machen,
damit der Screen Schwarz wird und man die Raender links und rechts von Bob
sehen, obwohl es ja auch ganz Interressant ist sie zu sehen.

Um diesen Bob auf dem Screen rumflitzen zu lassen, muessen wir eigentlich
nur noch eine Routine schreiben die der Coordcalc-Routine laufend neue
Koordinaten uebergibt. Das sollte dann jeden Bildschirm aufbau passieren.
Also muessen wir die abfrage der Bildschirmposition einbauen, die Tabelle
mit den Positionen und die Routine die die Koordinaten uebergibt und die
Tabelle rotiert.
Allerdings fehlt dann noch etwas...Naemlich eine Routine die den alten Bob
loescht damit die Reste nicht auf dem Bildschirm bleiben. Das ist zwar ein
netter Effekt, aber auch nicht unbedingt das was wir wollen.

So hier erstmal das Prograemmchen. Ich habe keine Koordinatentabelle
benutzt weil sie Platz wegnimmt, und ich ausserdem keine lust habe mir die
Finger an so einer Tabelle wund zu schreiben. Das Position des Bobs wird
durch einen ADD befehl veraendert.

Start:
Execbase= 4
Openlibrary= -408
Vposr= $dff004
Forbid= -30-102
Permit= -30-108
Bltafwm= $dff044
Bltalwm= $dff046
Bltcon0= $dff040
Bltcon1= $dff042
Bltamod= $dff064
Bltdmod= $dff066
Bltapt= $dff050
Bltdpt= $dff054
Dmacon= $dff096
Intena= $dff09a
Dmaconr= $dff002
Bltsize= $dff058
Cop1lc= $dff080

Move.w #$0020,Dmacon
Move.w #$4000,Intena
Move.l Execbase.w,a6
Jsr Forbid(a6)


Move.l #$60000,a0
Move.l #$9588/4,d0
Clearloop:
Move.l #0,(a0)+
Dbf d0,Clearloop

Bsr.l Makecl


Wait: Move.l Vposr,d0
And.l #$1ff00,d0
Cmp.l #$1000,d0
Bne.s Wait
Bsr.l Kill_old
Bsr.l Get_Pos
Btst #6,$bfe001
Bne.s Wait

Move.l Execbase.w,a6
Jsr Permit(a6)
Lea Gfxname(pc),a1
Jsr Openlibrary(a6)
Move.l d0,a6
Move.w #$83e0,Dmacon
Move.w #$c000,Intena
Move.l 38(a6),Cop1lc
Moveq #0,d0
Rts
Makecl:
Lea $05f000,a0
Move.l a0,$dff080

Move.l #$00e00006,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$00e40006,(a0)+
Move.l #$00e6002c,(a0)+
Move.l #$00e80006,(a0)+
Move.l #$00ea0058,(a0)+
Move.l #$008e1a64,(a0)+
Move.l #$009039d1,(a0)+
Move.l #$00920030,(a0)+
Move.l #$009400d8,(a0)+
Move.l #$01003200,(a0)+
Move.l #$01080058,(a0)+
Move.l #$010a0058,(a0)+
;----------------------------------------------
Move.l #$01800000,(a0)+
Move.l #$0182071d,(a0)+
Move.l #$0184088e,(a0)+
Move.l #$0186044f,(a0)+
Move.l #$0188000f,(a0)+
Move.l #$018a000c,(a0)+
Move.l #$018c0008,(a0)+
Move.l #$018e0004,(a0)+
;----------------------------------------------
Move.l #$fffffffe,(a0)+
Rts
Waitblit:
Btst #6,Dmaconr
Bne.s Waitblit
Rts
Set_bob:
Bsr.s Waitblit
Move.w #$ffff,Bltafwm
Move.w #$ffff,Bltalwm
Move.w d0,Bltcon0
Move.w #0,Bltamod ; Quellmodulo = 0 (rawblit)
Move.w #38,Bltdmod ; Zielmodulo = 38 (planes = $2c)
Move.l #$5e000,Bltapt ; Quelladr.
Move.l a0,Bltdpt ; Zieladr.
Move.w #96*64+3,Bltsize ; Bltsize und Start
Rts

; Beim Aufruf der Routine muss das Kontrollwort schon feststehen und
; in D7 stehen.

CoordCalc:

Move.l d7,d1 ; Kontrollwort nach D1
And.l #$ff00,d1 ; Alles ausser Y-Werte aus-
; maskieren !
Divs #$100,d1 ; Nach unten schieben
Muls #$84,d1 ; Wert auf dem Screen ermitteln
Add.l #$60000,d1 ; Plus PlayfieldBase
Move.l d1,a0 ; Nach A0 setzen.

Move.l d7,d1 ; Erneut holen !
And.l #$00f0,d1 ; Unwichtiges ausmaskieren
Lsr.l #3,d1 ; Ersetzt Divs $10 und verdoppeln
Add.l d1,a0

Move.l d7,d1 ; !!!
And.l #$f,d1 ; Ausmaskieren !
Swap d1
Lsr.l #4,d1
Move.w #%0000100111110000,d0 ; BLTCON0-Wert ohne Scrollwert
; nach d0
Add.w d1,d0 ; D1 zu D0 addiert ergibt BLTCON0
Clr.l d1 ; Werte nur in D0 und A0 lassen
Clr.l d7

Bsr.l Set_bob ; Bob setzen.
Move.l a0,Old_Pos ; Position fuer loeschroutine
; merken !
Rts

Kill_Old:
Bsr.l Waitblit
Move.l #$-1,Bltalwm
Move.w #38,Bltdmod
Move.w #%0000000100000000,Bltcon0
Move.l Old_Pos,Bltdpt
Move.w #96*64+3,Bltsize
Rts

Get_Pos:
Move.w Position,d7
Add.w #$101,Position
Bsr.l Coordcalc
Rts

Old_Pos: Dc.l $60000
Position: Dc.w 0
Gfxname: dc.b "graphics.library",0

>Extern "bob.raw",$5e000

Wichtig ist allerdings eines.
Da ich die Kill_Old Routine vor der Get_Pos Routine aufrufe, und sie die
Koordinaten erst beim Ersten Aufruf von Get_Pos kriegt muesst ihr das
reservierte Langwort Old_pos mit irgendeiner Adresse fuellen.
Ich hatte naemlich nur geschrieben

Old_pos: Dc.l 0

Und mein Amiga stuerzte jedesmal nach dem Druck auf die Maustaste ab.

Was war passiert:
Beim ersten Aufruf der Kill_Old Routine hat der Blitter als Destination
fuer seine Loeschaktion die 0 als Adresse gekriegt. Und da bei 0 und $4
sehr wichtige Daten liegen war das nicht so gut. Das Programm lief ohne
Probs weiter. Sobald ich aber das Programm beenden wollte und auf die
Maustaste drueckte stuerzte er ab. Ganz klar - er holte sich die Execbase
nach A6 und sprang Openlibrary an. Da aber nicht mehr die Execbase sondern
Irgendetwas in $4 stand, ging er daran zu grunde.

Solche sachen bringen mich dann immer um denn Verstand. Ich sitze dann
immer wie Apathisch vor der Kiste. Schreibe nach und nach alle Routinen neu
und nicht passiert.
Beim Essen ist es mir dann eingefallen was das schief gelaufen ist.
So kanns gehen...

Da ich wiegesagt keine Tabelle fuer die position dabei gesetzt habe,
solltet ihr das mal selber versuchen. Es duerfte allerdings kein Problem
fuer euch sein. Denn ihr habt das ja schon mehrmals gemacht.
Ihr koennt auch mal versuchen die Routine auf mehrere Bobs um zu schreiben.
Dabei werdet ihr dann auf eine Sache stossen die dem Thema Bobs einen sehr
komplizierten Touch geben. Und wir werden uns damit sehr stark auseinander
setzen muessen.

Naja, ich will euch nicht den Mut nehmen....

Wie ich versprochen habe, kommen wir jetzt an etwas schwierigere
Blitterthemen. Also ran an die logischen Verknuepfungen.

Wer von euch ein bisschen mit dem letzten Bob-Listing gearbeitet hat, der
hat bestimmt einige unangenehme eigenschaften des Programm`s kennengelernt.
Zum Beispiel das wenn mehrere Bob`s sich ueberschneiden, immer nur der
oberste ganz dargestellt wird, und von den anderen immer mehr oder weniger
fehlt.

Die Erklaerung ist einfach. Wir Kopieren ja bloss von A nach D. Alles was
ich A steht geht auch nach D.
Das ist keine Verknupfung. Schalten wir mal eben auf eine Plane, da kann
man die Funktion besser erkennen.

Es soll ja am Ende so Funktionieren das in der Bitplane alles gesetzt wird
was auch im Bob gesetzt ist. Sobald aber mal das aktuelle Bit im Bob nicht
gesetzt ist, soll das gesetzt werden was dort vorher stand.
Da wir aber nicht Logisch mit D arbeiten koennen, sondern in D immer nur
das Ergebnis steht, muessen wir eine weitere Quelle dazu schalten.
Dazu nehmen wir die Quelle B.

Der Blitteraufruf sieht dann so aus das A auf den einplaneBob zeigt, B auf
den bereich zeigt der von dem Bob ueberschrieben wird, und D auf den
gleichen Bereich, allerdings als Ziel....

Im BLTCON0-Register muss man dann nur noch die Verknuepfung

D = A or B

Das heisst auf Deutsch - D ist immer dann 1, wenn A oder B 1 ist. Wichtig
ist dabei das Oder.

Manche werden sich jetzt fragen ob ich noch ganz echt bin. Aber trotzdem
werde ich jetzt wieder mal die Bobs kurz verlassen, da ich ja immer zu dem
jeweiligen Stand des Koennens in Assembler ein paar andere Sachen mache.
Das finde ich naemlich sehr viel besser.

Kommen wir nun zu einer Sache die vor einem Jahr das Markenzeichen fuer
gute Intro/Demo-Programmierer war. Mittlerweile ist es allerdings Standart
geworden. Das ist zwar etwas traurig aber es ist nach wie vor eine tolle
Sache, an der man seine Programmtechnischen Faehigkeiten sehr gut messen
kann.

22.Realisierung eines einfachen Sinusscrollers mit dem Blitter

Als Grundprogramm nehmen wir einfach das Laufschriftprogramm was wir im
Kapitel Laufschrift geschrieben haben.
Es muss auch nicht soviel daran geaendert werden, deshalb bietet es sich
an.

Schauen wir uns mal an was der Prozessor dem Blitter zu tun gibt.
Der Prozessor laesst den Blitter jede 1/50 Sekunde den Laufschrift Bereich
um eins nach Links schieben. Gleichzeitig zaehlt er jedoch ob der Aktuelle
Buchstabe schon aus dem Puffer herausgeschoben ist. Sobald das passiert ist
laesst er den Blitter einen neue Buchstaben in den Puffer kopieren.

Diese Kopierroutine ist einfach Mathematisch darzustellen.

D = A

Also...Ist A = 1 dann ist auch D = 1, ist A = 0 dann....
Somit wird auch der Muell der beim Linksrotieren des Puffer hinten
reingeschoben wird, ueberschrieben. Schoen und gut.

Jetzt wollen wir uns mal ueberlegen wie man die Wellen, oder einfacher,
ueberhaupt einen Vertikalen versatz hinkriegen koennen.
Wenn wir immer mit einem Wort arbeiten, ist das ja nicht schwer. Das
naechste Ziel des Blitters liegt dann eben 3 oder 4 zeilen unter dem
vorherigen. Was ist aber wenn wir die abstufungen Steiler machen wollen ?

Wir muessten dann mehrmals pro Wort um eine Zeile runtergehen. Sagen wir
mal immer 4 Bits, und dann eine Zeile runter.
Aber wie kriegt man nur 4 Bits kopiert, und wie weiss der Blitter welche 4
Bits ich will ?

Dazu gibt es Bltafwm und Bltalwm, die beiden Masken die man ueber das erste
und letzte Word der zu kopierenden Zeilen legen kann. Damit markieren wir
einfach die Bits die wir kopieren wollen. Nachdem das passiert ist schieben
wir die Maske auf die naechsten 4 Bits, und starten die Aktion fuer das
Wort nochmal. Solange bis das Wort kopiert ist. Ihr koennt euch sicherlich
vorstellen was das fuer eine arbeit fuer den Blitter ist. Wiegesagt -
erstes und letztes Wort der Zeile. Falls die Zeile nur ein Wort, wie in
unserem Fall, breit ist, dann werden die Masken uebereinander gelegt. Um
dann sinnvolle angaben zu erhalten, muessen die beiden Masken jeweils
denselben Inhalt haben. Wenn ich also aus dem Wort nur Bit 1,2,3 und 4
kopieren will, dann muessen die Masken fuer die Register ungefaehr so !
aussehen.

BltaFwm = %1111000000000000
BltaLwm = %1111000000000000

So, und was geschieht mit dem Rest ? Der Blitter schreibt an ihre Stelle im
Ziel, egal was in der Quelle steht, immer eine Null. Somit haetten wir dann
nur die 4 Bits die wir gewuenscht haben, kopiert.

Was passiert aber wenn ich die ersten 4 Bits kopiert habe, die Maske
verschoben habe, und nun die Bits 5 - 8 um eine zeile verschoben, Kopieren
will. Da Nullen ja auch Kopiert werden, und die ausmaskierten Bits, als 0
Interpretiert werden, wird der groesste Teil der ersten 4 Bits geloescht.

Das ist dann ehrlichgesagt - Scheisse !, und ganz bestimmt nicht das was
wir wollen.
Deshalb muessen wir es so einrichten, das der Blitter die gesetzten Bits im
Ziel, die durch nullen ueberschrieben werden wuerden, zu beruecksichtigen.

Die Logische verknuepfung die wir dafuer einstellen muessen, ist

D = A ^ B

Was auf Deutsch heisst. D ist auf jedenfall 1 wenn A oder B 1 ist. Wenn
beide 1 sind, was aber in unserem Fall nicht vorkommen wird, ist es
natuerlich auch 1.

Auf diese Art und Weise, werden die davor kopierten Bitspalten, in Ruhe
gelassen, und halten ihr aussehen.

Ich will das ebenbesprochene mal Source-Code-Maessig unterstuetzen.
Es kopiert einen im Source enthaltenen Bob, jeweils um eine Zeile versatz
pro Bit auf den Screen.


Start:
Execbase= 4
Openlibrary= -408
Vhposr= $dff006
Forbid= -30-102
Permit= -30-108
Bltafwm= $dff044
Bltalwm= $dff046
Bltcon0= $dff040
Bltcon1= $dff042
Bltamod= $dff064
Bltbmod= $dff062
Bltdmod= $dff066
Bltapt= $dff050
Bltbpt= $dff04c
Bltdpt= $dff054
Dmacon= $dff096
Intena= $dff09a
Dmaconr= $dff002
Bltsize= $dff058


Move.w #$0020,Dmacon
Move.w #$4000,Intena
Move.l Execbase.w,a6
Jsr Forbid(a6)


Move.l #$60000,a0
Move.l #$9588/4,d0
Clearloop:
Move.l #$0,(a0)+
Dbf d0,Clearloop


Bsr.l Makecl
Bsr.l Bobmaker ; Aufruf der Bobroutine

Wait:
Btst #6,$bfe001
Bne Wait

Move.l Execbase.w,a6
Jsr Permit(a6)
Lea Gfxname(pc),a1
Jsr Openlibrary(a6)
Move.l d0,a6
Move.w #$83e0,Dmacon
Move.w #$c000,Intena
Move.l 38(a6),$dff080
Moveq #0,d0
Rts
Makecl:
Lea $05f000,a0
Move.l a0,$dff080

Move.l #$00e00006,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$008e1a64,(a0)+
Move.l #$009039d1,(a0)+
Move.l #$00920030,(a0)+
Move.l #$009400d8,(a0)+
Move.l #$01001200,(a0)+
;----------------------------------------------
Move.l #$01800000,(a0)+
Move.l #$01820fff,(a0)+
;----------------------------------------------
Move.l #$fffffffe,(a0)+
Rts
Waitblit:
Btst #6,Dmaconr ; Wartet bis Blitter fertig
Bne.s Waitblit
Rts
Bobmaker:
Move.w #$0f,d7 ; Counter fuer die Spalten-
; Anzahl
Move.w #%1000000000000000,d0 ; Erste Maske
Move.l #$61088,d1 ; Erste Adressen
Loop: Bsr.l Set_bob ; Bob setzen
Add.l #$2c,d1 ; Eine Zeile runter
Ror.l #1,d0 ; Maske 1 Bit verschieben
Dbf d7,Loop ; Schleife ob schon ganzer
; Bob ?
Rts


Set_bob:
Bsr.s Waitblit
Move.w d0,Bltafwm
Move.w d0,Bltalwm
Move.w #%0000110100111100,Bltcon0
Move.w #0,Bltamod ; Quellmodulo = 0 (rawblit)
Move.w #42,Bltdmod ; Zielmodulo = 42 (planes = $2c)
Move.w #42,Bltbmod
Move.l #Bob,Bltapt ; Quelladr.
Move.l d1,Bltbpt
Move.l d1,Bltdpt ; Zieladr.
Move.w #16*64+1,Bltsize ; Bltsize und Start
Rts

Gfxname: dc.b "graphics.library",0

even

Bob:
Dc.w %1111111111111111
Dc.w %0000000000110000
Dc.w %0000000001100000
Dc.w %0000000011000000
Dc.w %0000000110000000
Dc.w %0000001100000000
Dc.w %0000111111000000
Dc.w %0001111111100000
Dc.w %0001111111100000
Dc.w %0010000000000000
Dc.w %0001000000000000
Dc.w %0000100011110000
Dc.w %0000010000000000
Dc.w %0000001000000000
Dc.w %0000000100000000
Dc.w %1111111111111111


Das Listing ist ja ein Bisschen Dokumentiert. Ihr duerftet aber keine
Probleme mehr damit haben.
Mir hat ein bekannter gesagt, das es eine Sache gibt die ihn an diesem Kurs
stoert. Das ich viel zu viel zeige, und die Leute nicht selber denken
lasse.
Ich habe aber sonst keine Klagen gehoert, deshalb schaetze ich mal das euch
der Kurs ganz gut gefaellt. Naja, aber trotzdem, ich werde jetzt in den
letzten Teile die Kritik hinnehmen und befolgen.

Um daraus jetzt eine Sinusschrift zu machen muesst ihr folgendes machen.

Diese Kopierroutine in das Laufschrift Programm einsetzen. Auf eine ganze
Zeile erweitern, und die Zeilen in die Kopiert wird aus einer Tabelle lesen
lassen. Die alte Laufschrift muesst ihr dann in einen Puffer ablaufen
lassen, sodas erst diese Kopierroutine die Schrift auf den Screen bringt.

Mehr nicht...

Eigentlich war es das zu der Sinuslaufschrift..Wenn ihr das Prinzip
geschnallt habt, dann ist ja alles O.K...Verschiedene Variationen werdet
ihr ja sicher selber machen. Nur halt soviel. Ueberlegt euch immer vorher
ob es in einem Demo immer eine Sinusschrift sein muss. Denn diese ganzen
Blitteraktionen nehmen auf dem Prozessor viel Zeit weg, und deshalb kann
fuer das restliche Intro nicht mehr soviel Zeit aufgebracht werden. Wenn
ihr das mal durchrechnet was der Blitter da schon an Zeit braucht, dann
wisst ihr warum die eigentlich garnicht so schwierigen Sinusscroller nicht
so oft eingesetzt werden.
Es gibt ja genug andere schoene Effekte die sich fuer Intro`s eignen. Und
nicht der, der am besten andere Ideen nachprogrammiert macht von sich
reden, sondern der, der die schoensten eigenen Ideen hat. Wobei Egal ist
wie einfach sie zu programmieren sind. Hauptsache schon...

Mit diesen Worten moechte ich mich von euch verabschieden...wuensche euch
viel Spass beim Sinussen...

Ich hoffe ihr kommt mit dem SinusscrolleransatzSource klar. Muesstet ihr
aber schon schaffen. Positionen aus Tabellen lesen und bedingte Schleifen
programmieren koennt ihr ja schon.

So, dann will ich mal weiter machen mit den Bobs.
Vielleicht haben einige schon versucht die neue Verknuepfung auf die Bobs
anzusetzen. Duerfte bei einplanebobs auch geklappt haben. Aber sobald
mehrere Planes ins Spiel kamen, sah es etwas bunter aus als wie es haette
aussehen sollen.
Nun das ist eigentlich etwas verwunderlich, denn da wo etwas vom Bobs
gesetzt war, ist auch in der Quelle etwas gesetzt, und da wo nichts ist, da
wurde ein Bit vom Hintergrund eingesetzt.

Warum Funktioniert es nicht ?

Ganz klar...Wenn ein Bit der Quelle Bob nicht gesetzt ist heisst das noch
lange nicht das da nichts gesetzt ist. Es koennte ja eine Plane weiter
etwas gesetzt sein. Und da die Bits die Farbgebung der Bobs sind, macht da
ein dazwischengeschobenes Bit, ne ganze Menge aus.

Was ist also zu tun ?

Nun wir muessten neben dem Normalen Bobs noch eine Plane oder ein Abbild
des Bobs haben in dem alle gesetzten Bits, egal in welcher Plane,
verzeichnet sind. Dieses verzeichnis koennten wir dann als grundlage fuer
die verknuepfung nehmen. Die verknuepfungen saehe dann ungefaehr so aus.

1. D = A wenn C = 1 und B = 0 2. D = B wenn C = 0

Das heisst das C nur noch ausschlaggeber fuer die Aktion ist.

Wir erreichen so ein verzeichnis indem wir einen Bob mit 8 Farben im 16
Farben Modus malen und dabei nur die Farben 8 - 15 benutzen. die 4. Plane
koennen wir dann als verzeichnis benutzen, da in ihr Automatisch alle
gesetzten Bits verzeichnet sind, schlau was ?

Naja, da solche verknuepfungen auch ne menge Zeit kosten, muessen wir auch
ein paar Tricks und Kniffe beherschen die uns schneller werden lassen.

Die Tips sind zwar nicht alle von mir, sind aber trotzdem gut. Ich setze immer
noch eine erklaerung darunter.

1. Man vermeide 32-Bit-Immediate-Werte.

Also, man sollte nicht mit direkten Langwoertern arbeiten.

Beispiel: Wenn man ein Rotierende Schleife programmieren will, dann sind
die Zeiger (A0,A1) um die Laenge des Elements voneinander verschoben. Die
Initialisierung saehe normal dann so aus.

Move.l #$40000,A0
Move.l #$40004,A1

Scheusslich...Wir aber geben der tabelle einen Namen und initialisieren so..

Lea Tabelle(Pc),A0
Move.l A0,A1
Add.l #4.w,A1

Das ist sehr viel kuerzer, und damit auch gleichzeitig schneller.

2. Man vermeide 32-Bit-Operanden zusammen mit Adressregistern.

Solange es irgendwie geht sollte man ueberhaupt Langwoerter nicht benutzen,
denn sind sind fuer den MC 68000 sehr schwer zu holen, da er ja nur ein
Word pro Lungenzug aus dem Speicher ziehen kann.

3. Man vermeide 32-Bit-Absolut Adressen...

Also die Source-Kot zeile

Move.l $40000,$50000

ist Tabu....Sie dauert haarstraeubend lange.

4. Man vermeide die Mul- und Div anweisungen..

Wenn man ein bisschen scharf ueberlegt kommt man oft auf einen anderen Weg
an das gewuenschte Ergebnis zu kommen.

5. Was der Verfassers jetzt meint weiss ich nicht, aber er sagt das man die
Quick anweisung des MC 68000 vergessen sollte. Naja er wird schon seine
gruende haben. Ich kann nur sagen das ich sie gerne benutze, schon alleine
weil sie so kurz ist.

6. Was ich aber schon gesagt habe ist das man die Clr- anweisung komplett
aus den Assemblerbuechern streichen sollte. Ein Befehl mit nur einen
Parameter der ganz genau weiss was er zutun hat, und der dann so lange
dafuer braucht, der gehoert einfach nicht in ein gutes Programm.
Desweiteren fuehrt er, weiss ich warum seine Anweisung zweimal aus. Das ist
bei Strobe Registern nicht gerade das wahre, oder ?

Hier mal ein aufstellung von einfachen Befehlen, und wie man diese durch
andere Adressierungsarten verkuerzt oder verschnellert, oder beides !

L1: Move.l #1,D0 6 Bytes 12 Zyklen

L2: Add.l #2,A0 6 Bytes 16 Zyklen

L3: Move.l #3,Datavariable 10 Bytes 28 Zyklen

L4: Muls.w #10,D0 4 Bytes 70 Zyklen

L5: Clr.l D0 2 Bytes 6 Zyklen


Durch geschickten einsatz der moeglichkeiten, erreicht man dasselbe
schneller und kuerzer so...

L1: Moveq #1,D0 2 Bytes 4 Zyklen

L2: Addq.w #2,A0 2 Bytes 8 Zyklen

L3a: Moveq #3,D0 2 Bytes 4 Zyklen
Move.l D0,Datavariable 6 Bytes 20 Zyklen
------- ---------
8 Bytes 24 Zyklen

L3b: Moveq #3,D0 2 Bytes 4 Zyklen
Move.l D0,(a4) 4 Bytes 16 Zyklen
------- ---------
6 Bytes 20 Zyklen

L4: Move.l D0,D1 2 Bytes 4 Zyklen
Lsl.l #2,D1 2 Bytes 12 Zyklen
Add.l D1,D0 2 Bytes 8 Zyklen
Add.l D0,D0 2 Bytes 8 Zyklen
------- --------
8 Bytes 32 Zyklen

L5: Moveq #0,d0 2 Bytes 4 Zyklen


7. Wenn man fuer eine 32-Bit-Konstante MOVEQ verwenden kann, soll man es
auch tun.

Folgende Routine....

And.l #15,D2 6 Bytes 16 Zyklen
Or.l #2,D3 6 Bytes 16 Zyklen
Sub.l #28,D4 6 Bytes 16 Zyklen
Cmp.l #1,D1 6 Bytes 14 Zyklen
-------- ---------
24 Bytes 62 Zyklen

Werden kuerzer und schneller, so geschrieben

Moveq #15,D0 2 Bytes 4 Zyklen
And.l D0,D2 2 Bytes 8 Zyklen
Moveq #2,D0 2 Bytes 4 Zyklen
Or.l D0,D3 2 Bytes 8 Zyklen
Moveq #28,D0 2 Bytes 4 Zyklen
Sub.l D0,D4 2 Bytes 8 Zyklen
Moveq #1,D0 2 Bytes 4 Zyklen
Cmp.l D0,D1 2 Bytes 8 Zyklen
-------- ---------
16 Bytes 48 Zyklen

Allerdings muss man nicht immer auf Register zurueckgreifen wenn man
groessere Werte Addieren will und man die Grenze der Quick-anweisung
sprengt.

Anstatt

Sub.l #10,D0 6 Bytes 16 Zyklen

Schreibt man genauso schnell, aber im Code kuerzer.

Subq.l #8,D0 2 Btyes 8 Zyklen
Subq.l #2,D0 2 Bytes 8 Zyklen
-------- ---------
4 Zyklen 16 Zyklen

Man kann sich aber auch mit dem Moveq-Befehl behelfen wenn es um Grosse
werte geht. Ein Beispiel waere anstatt

Move.l #$10000,D0 6 Bytes 12 Zyklen

schreibt man

Moveq #1,D0 2 Bytes 4 Zyklen
Swap D0 2 Bytes 4 Zyklen
-------- ---------
4 Bytes 8 Zyklen

8. Man verwende fuer Adressregister die .W form wenn moeglich.

Also Move.l Execbase.w,a6

9. Man fuehre Operationen, wenn moeglich, in Adressregistern aus. Soll
etwa eine Konstante in ein Adressregister geladen werden, koennte dies wie
folgt codiert werden:

Move.l #502,A1 6 Bytes 12 Zyklen

Dasselbe kann man aber auch mit der Lea-Anweisung erreichen.

Lea 502.w,A1 4 Bytes 8 Zyklen


Ein weiterer Tip ist, das man ruhig mehere gleiche Kommandos hintereinander
schreiben kann, wenn man so schneller auf das Ergebnis kommen kann. Also
anstatt..

Exg A0,D0 2 Bytes 6 Zyklen
Lsl.l #2,D0 2 Bytes 12 Zyklen
Exg D0,A0 2 Bytes 6 Zyklen
-------- ---------
6 Bytes 24 Zyklen

Das Exg ist scheinbar noetig, da man ja nur Datenregister `Shiften` kann.

Der Ersatz fuer diese Zeilen waere einfacher....

Add.l A0,A0 2 Bytes 8 Zyklen
Add.l A0,A0 2 Bytes 8 Zyklen
-------- ---------
4 Bytes 16 Zyklen

Und da wir gerade beim Thema Shifts sind, hier ein paar einfache Regeln.

10a: Wenn die Anzahl der Schiebepositionen groesser als 15 ist, fuehre man
eine Swap-anweisung durch und Subtrahiere 16 von der anzahl der
durchzufuehrenden Shifts

10b: Wenn die Zahl 1 ist, und es sich um einen Links-Shift handelt, dann
ist ein Add-anweisung vorzuziehen.

So, diese Tips und Tricks hier, setze ich meistens bei meinen Programmen
ein, und kann euch sagen das sie wirklich gut sind. Allerdings moechte ich
euch auch ein paar Tips geben, die ich beim Codes eines Intro`s benutzen.

Wenn es um Aufwendige Teil-Routinen geht, dann Speichere ich den
Source.Code ab und loeschen den Speicher. Dann fange ich mit der Routine
an. Da ich weiss was als Ergebnis bei einem Ganz bestimmten Wert rauskommen
muss, kann ich jeden neuen Befehl beobachten. Dazu koennt ihr die Register
ausgabe beim Ruecksprung in den Seka benutzen. Lasst euch Zeit damit, denn
so eine Routine die z.B Die Bobkoordinaten, oder Logopositionen anhand
eines Bytes errechnet kommt ohne Muls und Divs anweisungen meist nicht aus.
Wenn man allerdings die Tips beruecksichtigt kommt man noch ziemlich billig
davon.

Wenn ihr allerdings groessere Programme selber schreiben solltet, dann ist
es besser ihr schreibt euch die Gliederung bis in sehr kleine Teilprobleme
auf. Dann koennt ihr jedes kleine Progamm einzeln schreiben, und braucht
sie dann nur noch anzupassen.

Puh, das war aber jetzt eine lange Pause mit diesem Kurs, aber ich hatte
Facharbeiterpruefung, und da muss man ja ein bisschen ueben, oder?
Da ich mir denke das viele schon sehr weit sind mit Assemblern, da koennte
wir mal besprechen wie man ein Intro schreibt. Nicht vom Programm her, das
muesstet ihr schon koennen sondern vom drumherum. Die vorbereitungen und
so.
Als, erstes muss ich sagen das ich mich nicht hinsetzen kann und sagen,
`Jetzt schreibe ich ein Intro`. Ich schreibe meine Intros immer dann wenn
ich irgendeinen guten Effekt programmiert habe, der gut in ein Intro passen
koennte. Dann setze ich mich hin, hoere Musik, und ueberlege mir das drum
herum. Was noch passieren muesste. Der Nachteil liegt darin, das der Effekt
den ich als Grundlage nehme, immer der Hauptteil des Intro`s bleibt, und
man den rest drumherum programmiert. Das ist natuerlich nicht die Optimale
loesung. Deshalb gehe ich so vor:
Sobald ich einen Introreifen Effekt programmiert habe, denke ich mir das
Intro aus, wo er gut reinpassen wuerde, und schreibe mir alles was rein
muss, und alles was noch reinpasst auf.

Muss-
Laufschrift

Kann-
Schwabbeln
Wasser
Sterne
Schwingendes Logo

Laufschrift ist finde ich ein muss fuer ein Intro. Da das was man mitteilen
will ja in nicht zu aufdringlicher form vorhanden sein muss, aber das Ziel
eines Intro ja der ist, zu sagen was sache ist und so. Demo`s, besonders
Teile von Megademos kommen manchmal ohne Laufschrift aus, da die Effekte
schon reichen um den Zuschauer bei dem teil zu lassen.
Dann sollte man sich gedanken machen wie das Demo ungefaehr aussehen soll.
Danach kann ich dann entscheiden was an zusatzeffekte noch eingbaut wird.
Es bringt absolut nichts wenn man in jedes Intro Sterne reinschmeisst, das
ist dann nichts besonderes mehr. Man muss sich ueberlegen was am besten
aussieht.
Nachdem man ungefaehr weiss wie das Intro aussehen soll, kann man sich
gedanken ueber aussehen und groesse der Laufschrift machen. Sie sollte sich
ins gesamtbild des Intro gut einfuegen.
Wenn man zum Beispiel auf dem Introscreen zwei effekte hat, die jeweils ein
drittel des Bildschirms belegen, dann kann man auch die Laufschrift ein
drittel des Screens grossmachen. Wenn man ein Logo das halb so gross ist
wie der Bildschirm ist, hat und einen Viertelscreen Effekt, dann sollte man
auch die Laufschrift nicht groesser machen.
Das ganze zeichnet man sich am Sinnvollste irgendwie auf. Dann beginnt die
Gliederung. Das heisst was ist am wichtigsten, und was muss von Zeitlichen
aufbau an erster und an zweiter Stelle kommen.
Hierbei ist wichtig wie der Screen auszusehen hat. Grundsaetzlich muss ja
die Musik zuerst gespielt werden. Da die Musik aber mit dem Prozessor und
Paula gemacht wird, koennen wir nebenbei noch dem Blitter etwas arbeit
geben. Also lassen wir sofern eine Blitter-Laufschrift vorhanden ist, und
diese in verschachtelten Plane auf den Bildschirm liegt, den Blitter
gleichzeitig mit der Musik, noch die Laufschrift eins oder zwei weiter
schieben. Wenn die Abspielroutine dann ans Intro zurueckgibt, dann ist der
Blitter schon fertig und kann andere aufgaben uebernehmen. Man muss
zwischen sichtbaren und unsichtbare Aktionen unterscheiden. Die sichtbare
sollte vor dem erscheinen auf dem bildschirm erledigt sein. Das heisst
nichts anderes, als das, das ich die Copperliste eines Wasser-Effekts der
bei Rasterzeile $80 beginnt, spaetestens bei $78 fertig haben sollte sonst
sieht es nicht so gut aus, der sich alte und neue Copperliste dann
vermischen. Weiter heisst es das ich sachen wie das Rotieren einer Farb.-
oder um beim Beispiel zu bleiben, einer Wassertabelle erledigen kann wann
ich will. Am sinnvollsten dann, wenn der Blitter gerade zu tun hat. Dabei
darf man naturlich nicht direkt nachdem Blitteraufruf in die Warteschleife
verzweigen, sondern muss erst das erledigen lassen was zu tun ist. Wenn
dann die Warteschleife noch noetig ist, also eine weitere Blitteraktion
gestartet wird, kann man sie einsetzen, falls keine Blitteraktion waehrend
dieses Bildschirmaufbaus stattfindet, kann man die Warte schleife ruhig
weglassen. Dieses Fehler findet man noch sehr oft in Demo`s und Intro`s
auch besserer Programmierer. Ich habe es auch eine Zeit gemacht, gebe ich
offen zu, aber das ist irgendwie Reflex, man Programmiert einen Blitter
aufruf, und wartet dann bis er fertig wird. Naja, beachtet das, und es wird
kuerzer...Zawr nur 12 Bytes, aber das ist ja auch schon was.
Ein Intelligenter ablauf eines Bildschirm aufbaues saehe dann ungefaehr so
aus.

Auf VHbreich warten.

Prozessor Blitter

Daten fuer Laufschrift-
Scroll an den Blitter ueber-
geben.
Blitter verschiebt Laufschrift

Musicroutine anspringen ()

() ()

Blitter daten fuer das rotieren
der groessten Tabelle geben.
Blitter rotiert groesste Tabelle
Prozessor wertet position der
Laufschrift aus, und errechnet die ()
Quellwerte des neuen Buchstabens aus.
Weiteren Laufschrift-Features werden ()
ausgewertet.

Dem Blitter das Kopieren des neue
Buchstabens aufgeben.
Blitter kopiert neuen Buchstaben
Andere aufgabe des Intros erledigen
(Stars, weitere tabellen, Mausabfrage) ()

Zurueck zum Anfang.

Natuerlich ist das nur ein kleines Intro ohne viel effekte, aber wenn man
sich diese Art und Weise an ein Intro macht, kann man sicher sein das man
die Zeit die man hat, und ebenfalls den Amiga, schon sehr gut ausgenutzt
hat. Wie ich schonmal erwaehnt habe, kann man auch den Copper sachen fuer
sich erledigen lassen. Den ersten Blitteraufruf fuer das verschieben der
Laufschrift ist dazu gut. Aber das ist nur sehr selten von noeten, und auch
nicht so Flexibel, da der Aufwand den das Umschreiben der Copperliste,
falls sich was an der Geschwindigkeit aendert, so gross ist, das kein
Geschwindigkeitsvorteil dabei rumkommt.
Sobald die zu erledigenden Aufgaben zahlreicher werden, sollte man sie
ausmessen, um die Ideale paarung zwischen Blitter und Prozessor gleichlauf
zu erreichen.
Mit ausmessen meine ich, die Zeit die eine Routine in Rasterzeilen braucht.
Um das rauszukriegen braucht man keine Taktzyklentabelle sondern laesst
einfach die Copperliste leer. Dann aendert man vor jeder Routine mit
Move.w Farbe,$Dff180, die Bildschirm farbe. Dann kann man mit Augenmass,
verschiedene Tatigkeiten kombinieren. Da die taetigkeiten die der Prozessor
ausfuehrt zwar komplizierter sind, aber nicht soviel Zeit brauchen, koennen
wir mehrere von ihnen in der Zeit einer Blitteraktion erledigen lassen.
Wenn man dann noch die Mischung zwischen sichtbarer und unsichtbarer Aktion
beherscht, dann kommt man auf sehr gute ergebnisse in der ausnutzung der
Zeit.
Natuerlich dauert es seine Zeit bis man das alles beachtet, aber es lohnt
sich wirklich. Den obwohl Intro/Demoprogrammierer ja angeblich schlechte
Programmierer sind, muessen auch von ihnen die Grundreglen des Programmierens
beachtet werden.

Desweiteren sollte man sein Intro/Demo gut strukturieren. Das Strukturieren
geht allerdings nicht nach einem ganz bestimmten Schema, sondern wie es
fuer euch am uerbsichtlichsten ist.
Ein Teil von euch wird meine Struktur uebernehmen, bis sie eine bessere
finden. Diese Strukturen unterscheiden sich nur durch Einzelheiten. Manche
schreiben alle Routinen direkt hintereinander, manche schreiben ein grosse
Aufruftabelle und lassen sie abarbeiten. Wie ihr das macht ist natuerlich
euch ueberlassen. Ihr solltet allerdings nicht jedesmal wenn ihr was neues
schreibt die Struktur wechseln, da sich das Gehirn ja auch daran gewoehnen
muss, wo es etwas zu suchen hat, und wo nicht.

Desweiteren ist es wichtig das ihr nach jedem Teilschritt das ganz wieder
abspeichert, und zwar jedesmal unter neuem Namen, denn nur so kann man sehr
genau festlegen was zum Fehler in der Routine fuehrte, und so braucht man
nicht zu grosse Stuecke neu zu programmieren. Da sich das aber schlecht auf
die uebersichtlichkeit der Disk auswirkt, koennt ihr sobald wieder zehn
Sources fertig sind, die alle in einen Order kopieren. So liegen sie euch
nicht im Weg und sind aber trotzdem da. Desweiteren solltet ihr der Zeit
wegen, Ueberlegen, ob ihr, falls ihr Grafiken oder Music einbindet, die
nach jedem Assemblieren wieder mit `y` reinzuholen. Meistens ist das
garnicht von noeten, da das Zeug ja nicht ueberschrieben wird. Das einzige
was eigentlich jedesmal eingebunden werden muss, ist die Musik. Aber da
kann man ja eingreifen in dem man sie nur abundzu in den Source einbaut, um
zu schauen wie es so laeuft. Desweiteren kann man ja auch die
Abspielroutine umschreiben, so das sie auf einen Feste Adresse zugreift,
anstatt einen PC-Relativen Wert zu holen.

Also aus Move.l Mt-Data(PC),A0
wird Lea $60000,A0

oder so aehnlich.
Falls aber Manipulationen am Bild stattfinden, solltet ihr wenigstens die
sachen aus dem Ram: holen lassen.
Desweiteren kann man bei sehr viel Grafik oder Musik, auch noch den vom
Seka zu reservierenden Bereich ins Fastmem legen, dann stoert und der
Source nicht mehr. Allerdings muesst ihr dann immer mit Org/Load arbeiten,
da die Sachen mit Blitter/Copper ja im Fastmem nicht laufen.
So, gibt es noch mehrere Tricks um sich Platz zu machen. Aber man muss sie
nicht oft anwenden bei der Intro/Demo Programmierung. Bei Spielen ist es da
schon etwas anderes, aber soweit sind wir ja noch nicht.

Den letzen Tip den ich euch noch geben kann, werden manche von euch schon
selber heraus gefunden haben. Setzt euch niemals mit schlechter Laune an
ein Programm...Denn wenn dann was falsch laeuft regt man sich nur unnoetig
auf, und dann klappt ueberhaupt nichts mehr. Dann sollte man eine Pause
miteinem netten Spiel einlegen, und dann sieht alles schon wieder viel
besser aus.

So, vor einigen Kapiteln habe ich euch ja schon vom VBI erzaehlt, und
seinen Aufbau und seine Aufgaben erzaehlt. Damit habe ich es dann belassen,
weil ich meinte das man ihn zu dem Zeitpunkt noch nicht brauchen wuerde.

Das ist jetzt anders - Wir stehen vor dem Ersten Intro, und da koennen wir
den VBI schon sehr gut gebrauchen, finde ich.

Nochmal kurz zur Erinnerung:
Den VBI fuehrt der Amiga jedesmal auf, wenn der Prozessor das entsprechende
Signal auf seine Leitung bekommt. Das bekommt er jedesmal wenn der
Elektronenstrahl das Vertical Blanking erreicht. Das Vertical Blanking
heisst so, weil dann wenn der Elektronenstrahl diesen Bereich durchquert
nciths auf dem Bildschirm dargestellt wird. Die Bitplane und Sprite DMA ist
dort gesperrt, was dem Prozessor schneller macht. Schon deshalb bietet sich
der VBI gut fuer Zeitaufwendige Routinen an.
Desweiteren spielt das Timing eine Rolle. Der Vorteil der Timingschleife
die wir bis jetzt benutzt haben liegt eindeutig in ihrer kuerze, deswegen
werde ich sie auch weiterhin als beste loesung benutzen. Aber wir gehen die
Schleife ja mit dem Prozessor durch. Wenn wir also in der Schleife noch
eine Menge andere taetigkeiten ausfuehren lassen, dann kann es sehr schnell
vorkommen das diese taetigkeiten, in der Prozessorzeit laenger brauchen als
wie der Elektronenstrahl eine Rasterzeile aufbaut. Das heisst weiter das
der Prozessor abundzu mal eine Zeile nicht `Mitbekommt`. Die Konsequenz
waere dann das es vorkomen koennte das er mal fuer eine 50stel sekunde mit
dem intro nicht weiter macht, und das merkt man schon sehr wohl im
Introgenuss. Beim VBI ist das ja etwas anders, dort kann der Prozessor
seiner aufgetragenen Arbeit folgen, und wird dann von VBI-Signal
unterbrochen.
Er springt dann ueber den Vector der bei $0000006c indirekt an seine
VBI-IRQ-Routine.
Das ist dann der Punkt an den wir uns `Einhaengen` koennen. Das koennen wir
in zwei arten tun.
Wenn wir Taetigkeiten ausfuehren wollen die neben dem normalen
`Amiga-Arbeitstag` erledigt werden sollen, dann muessen wir uns den Wert
der bei $6c liegt merken, den Vector auf unsere Routine verbiegen, und nach
abarbeitung unserer Routine das IRQ-Programm durch einen Sprung, dahin
wohin der Vector zeigte, fortsetzen.
Die zweite Art ist die, die uns interessiert. Dabei merken wir uns
naemlich den Wert der in $6c steht, schreiben die Adresse unserer Routine
dort hin, und lassen es so. Nachdem das Intro/Demo beendet wird, stellen
wir einfach den Vectoor wieder auf den alten Wert, und alles ist wie
vorher.

Hier jetzt mal alle schritte die zur Initialisierung eines eigenen VBI und
der Re-Initialisierung des alten VBI noetig waeren.

Diese Routine ist fuer beide beschriebene Verwendungen tauglich, das sie
die Eingangswerte Dmacon, Intena und den VBI-Vector ($6c) Buffert, und nach
dem Mausclick umrechnen und wieder zurueckschreiben.


Dmacon= $DFF096
Dmaconr= $DFF002
Intreq= $DFF09C
Intena= $DFF09A
Intenar= $DFF01C
Ciaapra= $BFE001

Move.w Intenar,Intenabuffer ; Inhalt von Intena sichern
Move.w Dmaconr,Dmaconbuffer ; Inhalt von Dmacon sichern
Move.w #$7fff,Intena ; Intena loeschen
Move.w #$7fff,Dmacon ; Dmacon loeschen
Move.l $006c.w,Oldirq ; Alten IRQ Vector nach Oldirq
; retten
Move.l #Newirq,$006c.w ; Adresse von Newirq aus dem Source
; in den Zeiger schreiben.
Move.w #$87e0,Dmacon ; Alle DMA-Kanaele wieder oeffnen
Move.w #$c020,Intena ; VBI anschalten

; Der VBI ist jetzt Initialisiert und laeuft sofort nach dem Zugriff auf
; Intena.
; Jetzt koennen die Restlichen einstellungen die nichts mit dem VBI zu tun
; haben, erledigt werden.

Wait: ; Auf Maustaste warten (gaehn)
Btst #6,Ciaapra
Bne.s Wait

; Bevor alle anderen Einstellungen des Intro/Demo geloescht oder auf
; Default zurueckgestellt werden, sollte mit dieser Routine der VBI wieder
; auf den Urspruenglichen Wert zurueckgesetzt werden

Or.w #$8000,Intenabuffer ; Wert im Intenabuffer auf
; schreibzugriff umformatieren
Or.w #$8000,dmaconbuffer ; Dasselbe !
Move.w #$7fff,Intena ; Intena loeschen
Move.w #$07e0,Dmacon ; Dmacon loeschen
Move.l Oldirq,$006c.w ; Alten Vector von Oldirq nach $6c
Move.w Dmaconbuffer,Dmacon ; Alten Dmaconwert setzen
Move.w Intenabuffer,Intena ; Alten Intenawert setzen
Moveq #0,D0 ; Zur sicherheit (Cli)
Rts ; Auf Wiedersehen!

; Ab hier beginnt die Routine die im VBI ausgefuehrt werden soll
; Die vier Zeilen die dort schon stehen, sollten in jede Routine eingebaut
; werden. Sie garantieren einen Reibungslosen ablauf.

Newirq:
Move.w #$0020,Intreq ; VBI bestaetigen
Movem.l d0-d7/a0-a6,-(sp) ; Register auf den Stack retten

; So, hier kann das eigene Programm ablaufen.

Movem.l (sp)+,d0-d7/a0-a6 ; Register wieder vom Stack holen
Rte ; RTE - Return from Exeption

Oldirq: dc.l 0
Dmaconbuffer: dc.w 0
Intenabuffer: dc.w 0


So, so schwer ist das doch wirklich nicht, oder?
Programmiermaessig kann man damit genauso arbeiten wie mit der
Endlosschleife die wir bis jetzt immer benutzt haben. Allerdings ist sie
halt bei groesseren Intro/Demo-projekten vorzuziehen, da halt alles ein
ganz kleines bisschen schneller geht (nich viel!).
Wichtig ist auch das ihr das RTE am ende nicht mit RTS vertauscht, denn
sonst laeuft das ganze nicht.
Das letzte was ich noch sagen koennte, wisst ihr aber schon. Waehrend des
VBI, der ja eine normale Exeption (Ausnahmezustand) ist, seit ihr im
Supervisor-Modus des Prozessors...Bringt aber bei der
Intro/Demo-Programmiererei nicht viele Vorteile..Ehrlich gesagt kenne ich
gar keine...Hmmh, ich kenn ja auch den Supervisor-Modus nicht so gut..Aber
das ginge auch etwas weit glaube ich.

Am besten probiert ihr mal ein bisschen damit rum, damit ihr ein bisschen
gefuehl fuer die Sache bekommt. Ihr muesst allerdings darauf achten das
ihre den Wert der fuer diese Routine nach Intena und Dmacon geschrieben
wird, eurer Anwendung gerecht sein sollte, da er nicht nur fuer den VBI
gilt, sondern fuer den ganzen Amiga.
Desweiteren muesst ihr aufpassen, damit meine Ich jetzt die, die sich noch
weiter mit anderen Interrupts(Exeptions) befassen wollen, das diese
Routine, da nur fuer den vBI, sehr kurz gehalten werden kann. Eine Routine
die mehrere Interrupts auswerten und durchfuehren kann, wird ungleich
groesser, da jeder Interrupt seine besonderheiten hat, und auch die Zeiten
in denen er auftritt meisst unterschiedlich sind. Ich denke da nur an die
Sprite-Sprite Kollision oder den Diskready Interrupt, sie brauchen genau
wie der VBI die richtigen adressen um ordnungsgemaess arbeiten zu koenen,
sonst laeuft nichts.

Ihr koennt diese Routine auch fuer die Zeitmessung einiger Routinen
benutzen, von der ich im zusammenhang der Optimalen Zeitausnutzung im
letzen Teil gesprochen habe. Ihr muesst dazu einfach die Auszumessende(n)
Routine(n) in den VBI einsetzen, und vor jeder Routine die Bildschirmfarbe
aendern oder eine bestimmte Farbe zuweisen. Dann koennt ihr mit dem
Millimetermass die Zeit in Millimeter (toll, wa) messen. Falls einige von
euch noch mit einem Fernseher oder mit einen schlechten Monitor mit grossem
Bildschirmpumpen habt, dann solltet ihr nicht zu hellen farben verwenden,
oder die Kontraste zwischen den Farben nicht zu gross waehlen, da sonst das
sowieso schon ungenaue Ergebniss nicht noch weiter verschlechtert wird.

So, da dieser Kursteil zwar schon fertig ist, aber noch nicht lang genug
ist zum abschicken, setze ich noch eine Beschreibung des MC 68000 in den
Teil. Sie erklaert die Pin-belegung und eroertert einige Fragen.

Der 68000 er

---------------------
D4 |1 | | 64| D5
D3 |2 -------- 63| D6
D2 |3 62| D7
D1 |4 61| D8
D0 |5 60| D9
AS |6 59| D10
-UDS |7 58| D11
-LDS |8 57| D12
R/-W |9 56| D13
-DTACK |10 55| D14
-BG |11 54| D15
-BGACK |12 53| GND
-BR |13 52| A23
VCC |14 51| A22
CLK |15 50| A21
-GND |16 49| VCC
-HALT |17 48| A20
-RESET |18 47| A19
-VMA |19 46| A18
E |20 45| A17
-VPA |21 44| A16
-BERR |22 43| A15
-IPL2 |23 42| A14
-IPL1 |24 41| A13
-IPL0 |25 40| A12
FC2 |26 39| A11
FC1 |27 38| A10
FC0 |28 37| A9
A1 |29 36| A8
A2 |30 35| A7
A3 |31 34| A6
A4 |32 33| A5
---------------------

Die Stromversorgung: VCC + GND

Der 68000 arbeitet mit einer einfachen Versorgungssnung von 5 Volt.
Die Anschluesse sind doppelt ausgelegt und zentral gelegen,um durch kurze
Leitungswege im Gehaeuse die Stromverluste minimal zu halten.

Der Takteingang: CLK

Der 68000 benoetigt lediglich einen einfachen Takt.
Die Frequenz haengt von der gewaehlten Prozessorversion ab.
Im Amiga betraegt die Taktfrequenz 7.16 Mhz.

Der Datenbuss: D0 - D15

Der Datenbuss ist als 16-Bit Buss ausgelegt und kann somit ein Wort (16 Bit)
auf einmal uebertragen.Beim Transfer einzelner Bytes ist jeweils nur eine
Haelfte der Leitungen an der Uebertragung beteiligt.
Entweder wird das Byte ueber die unteren 8 Bit oder ueber die oberen 8 Bit
gelesen bzw. geschrieben.

Der Adressbuss: A1 - A23

Der Adressbus kann mit seinen 23 Leitungen 8 Megaworte Speicher ansprechen.
Da er kein A0 besitzt kann er diesen Speicher nur wortweise adressieren.

Bussteuerleitungen im asynchronen Modus: AS,R/-W,UDS,LDS,DTACK

Der 68000 kann seine Speicherzugriffe grundsaetzlich in zwei verschiedenen
Modi ausfuehren. Im asynchronen Modus signalisiert der Prozessor mit AS
(Adress Strobe/Adresse gueltig) dass eine gueltige Adresse am
Adressbus anliegt.Gleichzeitig bestimmt er mit der R/-W (Read-Write/
Lesen-Schreiben) ob ein Byte/Wort gelesen oder geschrieben werden soll.
Die Wahl zwischen Wort oder Byte treffen die Leitungen UDS und LDS.
Da der Speicher immer wortweise adressiert wird,uebertraegt der Prozessor
bei einem Bytezugriff einfach nur die oberen oder die unteren 8 Bits des
Datenbusses.
Dies signalisiert er durch UDS und LDS.Bei einem Wortzugriff legt er beide
Leitungen auf 0.Will er dagegen auf ein Byte zugreifen,legt er entweder
UDS oder LDS auf 0,die andere Leitung auf 1.
Hat der Prozessor jetzt mit AS,R/-W,UDS und LDS den ihm gewuenschten Zugriff
signalisiert,wartet er,bis der Speicher ihm mitteilt,dass die gewuenschten
Daten bereit sind. Dazu verwendet er die Leitung DTACK,die er auf 0 legt
sobald er die Daten bereitgelegt hat.Schreibt der Prozessor die Daten,teilt
ihm der Speicher durch DTACK mit,dass er die Daten uebernommen hat.
Im asynchronen Modus passt sich der Prozessor also immer an die Geschwindigkeit

des Speichers an.

Bussteuersignale im asynchronen Modus: E,VPA,VMA

Um den Sinn dieser Signale besser verstehen zu koennen, muss man die Situation
zum Zeitpunkt der Markteinfuehrung des 68000 kennen. Es waren damals keine
eigenen Periepheriechips fuer den 68000 verfuegbar. Die vorhandenen Chips
von Motorola die ja fuer die 6800-Serie(von der auch der 6502 abstammt)
gedacht waren konnten nicht ohne zusaetzliche Schaltungen in die
asynchrone Bussteuerung eingepasst werden.Also versah man den 68000 bei
Motorola noch mit einem Synchronen Bussmodus,wie man ihn von den 8-Bit
Prozessoren wie dem 6800 oder 6502 kennt.
An der Leitung E liegt staendig ein durch den Faktor 10 geteilter Prozessortakt
an,der den Peripheriechips als Takt dient.

Er wird mit dem Phi-2-Eingang des Chips verbunden.
Die Umschaltung von asynchronem Modus in den synchronen erfolgt ueber den
Eingang VPA (Valid Periphial Adress/Gueltige Peripherieadresse).
Dieser Eingang muss von einem externen Adressdcoder auf 0 gelegt werden,
sobald dieser die Adresse eines Peripheriechips erkennt.Der Prozessor
antwortet darauf indem er die Leitung VMA (Valid Memory Adress/Gueltige
Speiher Adresse) ebenfalls auf 0 legt. Der entsprechende Peripherie-
chip muss jetzt innerhalb eines Taktzyklus von E die Daten uebernehmen
bzw. bereitstellen. Danach verlaesst der 68000 automatisch wieder den
synchronen Modus,bis das VPA Signal erneut aktiv wird.

Dies bedeutet also,dass der Peripheriechip im synchronen Modus
gezwungen ist,die Daten an einem bestimmten Zeitpunkt bereitzustellen bzw. zu
uebernehmen.

Wollen wir mal die in den letzten paar Teilen vorgekommenen Programmierkniffe
anwenden, und eine in einem fruehere Kursteil vorgekommene Routine ansehen,
und verstehen.
Es handelt sich um eine Punkt Setz/Loesch-routine, die den Punkt anhand der
X und Y Koordinate, und der Playfieldbase setzt.
Ich habe die Routine damals einfach so reingesetzt, ohne ihr besonders viel
beachtung zu schenken. Das hat sich aber jetzt etwas geaendert. Ich habe
die letzten paar Tage an einem Dots(Punkte) Demo gearbeitet und da ist mir
diese Routine wieder eingefallen die ich damals mal irgendwo abgetippt
hatte.
Frueher war sehr viel noetig um ein Bit zu setzen, ich meine frueher auf
dem 64er. Naja, viel auch nicht, aber mit dem 68000 ist halt alles
einfacher, und der Bset oder Bclr-befehl nimmt uns mehrere Logische
Operationen ab.
Also, ueberlegen wir uns mal was alles fuer das setzen eines Punktes mit
uebergabe der X und Y Koordinate noetig ist.

Als erstes kommt da erstmal die X-Koordinate. Wir muessen dort erstmal die
Anzahl der Bytes errechnen die wir ueberbruecken koennen. Mit Words waere
das natuerlich einfacher, aber der Bset/clr Befehl kann im normalen
Speicher nur Bytemaessig setzen und loeschen, also rechnen wir das aus.
Normalerweise, wuerde man jetzt den Divs oder Divu-Befehl benutzen, weil er
halt einfach teilt. Die letzten Teile des Kurses haben aber genau davon
abgeraten, weil er ja so langsam ist. Also, shiften wir das Wort in D0
dreimal nach rechts, was ja /8 entspricht. So, jetzt haben wir ja die
Anzahl der Bytes, vom Linken Rand des Ausgabe Fensters, die wir einfach zur
Byteanzahl die sich aus
Y-Koordinate * (Bytes pro Zeile) + Playfieldbase
errechnet.
Dann haben wir das genaue Byte in den die Aktion stattfinden soll.
Aber, wie kommen wir an das zu setzende Bit in dem Byte.
Wieder durch 8 Teilen ?. Solange 8 abziehen bis ein wert >8 rauskommt. Das
dauert viel zu lange. Man benutzt einfach am anfang den Divu befehl, denn
er teilt und gibt das ergebnis und den Rest aus. Der rest steht dann im
oberen Wort des Ergebnisses. Da der Rest aber die anzahl der Bits vom
linken Byterand angeben wuerde, und der Bset/Bclr Befehl aber die anzahl
der Bytes von rechten Byterand braucht, muessen wir das ergebnis noch von
-1 abziehen, und wir haben das zusetzende Bit ausgerechnet.
Damit haben wir dann auch die Nummer des Bits errechnet. Zusammen mit der
Playfield_Base und den zu addierenden Werte aus X und Y Koordinate koennen
wir den Punkt setzen/loeschen.

Die fertige Routine hat allerdings noch ein kleines Schmankerl, wie es nur
von sehr wenigen Programmierern genutzt wird, naemlich selbstmodifikation
des Programms. Was nichts anderes heisst als wie das sich das Programm
selber umschreiben kann. Im Programm wird das so genutzt, das wenn es von
Anfang an ab dem Label `Clear` gestartet wird, es nur den Bset Befehl am
ende des Programmes in eine Bclr Befehl umwandelt, die berechnung bleibt
dieselbe. Am Ende des Programms wir der Bclr-befehl dann wieder in Bset
umgewandelt. Beim Aufruf ab `Set` wird einfach alles beim alten gelassen.

Hier mal die Routine, aber Dokumentiert...

Clear: Move.b #$B5,Label+1 ; Aendert Bset in Bclr
Set: Divu #8,D0 ; Teilt durch 8, um Byteanzanzahl zu
; erhalten - Rest im oberen Word
Mulu #40,D1 ; Y-Koordinate * Bytes pro zeile
Add.w D0,D1 ; Bytes fuer X plus Y Bytes
Swap D0 ; holt oberes Wort nach vorne,
Moveq #-1,D2 ; um es von -1 in D2 abzuziehen
Sub.b D0,D2
Label: Bset D2,(A5,D1) ; Zu setzendes Bit in D2 vermerkt, setzt
; Bit in Adresse die sich aus Playfieldbase
; in A5 und +(Bytes) in D1 zusammensetzt
Move.b #$F5,Label+1 ; setzt eventl. veraenderung durch die
; selbstmodifikation zurueck
Rts

Natuerlich sollte, wenn die Routine fuer einen Dots-Effekt benutzt wird,
der Teil fuer die Selbstmodifaktion geloescht werden, da man den Bildschirm
ja sowieso mit dem Blitter loescht. Desweiteren, sollte man die Mulu
Funktion durch eine Tabelle ersetzen, aus der man die Werte die fuer eine
bestimmt Y-Position uebernehmen kann, ersetzt werden. Den Divu befehl
sollte man lassen, da das ersetzen durch schnellere Befehle nichts bringt,
im gegentum, es ist die schnellste loesung die mir im moment einfaellt.

Ich schaetze das es sehr viel schneller nicht mehr geht so etwas zu
berechnen. Schneller kann man nur noch werden, wenn man den Gedanken mit
der X und Y Koordinate fallen laesst, und fuer den Y Wert schon die
Komplette Byteanzahl in der tabelle hat, und somit das ausrechnen durch
Mulu, oder das auslesen der Tabelle von der ich sprach wegfaellt.

Mit dieser Routine, wie sie dort steht schafft man ohne Double-Buffering
(dazu Spaeter mehr) knapp 400 Punkte mit Bewegung und Loeschen durch den
Blitter. Dabei sollte das Rotieren der Koordinaten und das loeschen des
Bildschirmausschnitts im VBI gestartet werden. Die Punktsetz-Routine sollte
dann sofort unter dem VBI beginnen, wobei man daran denken muss, das sich
darstellung und bearbeitung nicht ueberschneiden, sonst bekomt man nicht so
gute ergebnisse.
Die 400 Punkte koennen natuerlich auch nicht auf dem ganzen Bildschirm
verteilt sein, mit einem 64 * 64 Feld kann man auch schon ganz schoene
Effekte erzielen. Alles was groesser wird geht auf kosten der Dots anzahl
pro Zentimeter auf dem Bildschirm. Natuerlich sieht ein 64*64 Sinus mit 3
Punkten pro Zentimeter besser aus als einen mit nur 1. Grosse Sinuskurven,
sind mit 3 Dots/cm gar nicht realisierbar, jedenfalls nicht mit unseren
mitteln, aber sind sehen auch bei 1Dot/cm, noch sehr gut aus.

Also, macht euch ein kleine Sinustabelle, und lasst die Routine drueber
laufen, und durch veraenderung einige Parameter im auslesen lassen sich
schoene Effekte erzielen.

Allerdings ist die Zeit die man fuer die Punkte braucht bei der selben
anzahl Punkte immer dieselbe, so das man auch groessere Sinen ablaufen
lassen kann. Tja, da gibt es aber ein Problem, naemlich die Zeit.
Wenn ich einen 64*64 Sinus als Kreis auf den Bildschirm bringe, und durch
Rotation der Tabelle einen Effekt zaubere, dann darf ich in dem Bereich wo
die Punkte sind nichts machen. Ich darf den Blitter nicht loeschen lassen,
ich darf die Punktesetzroutine nicht laufen lassen und das Rotieren der
Tabelle geht auch nicht, da alles sichtbar waere, weil die veraenderungen
ja sofort auf dem Bildschirm zu sehen waeren.
Also kommen wir zum oben erwaehnten Doublebuffering.
Die Technik ist folgende: Das erste Bild wird vor dem Start der
Endlosroutine ausgerechnet. Es liegt z.B. bei $30000 Der Rasterstrahl baut
dieses Bild auf. Gleichzeitig arbeitet der Prozessor und der Blitter an dem
zweiten Bild, welches bei z.B $40000 liegt. Sobald der Amiga mit der
darstellung des ersten Bildes fertig ist, setzt man die Bitplanepointer
fuer den naechsten durchlauf auf die Plane bei $40000. Wenn diese
dargestellt wir koennen wir die alte bei $30000 loeschen, und die neuen
Punkte setzen. Dann geht wieder alles von vorne los. Somit haben wir den
Kompletten Bildschirm zeit fuer den Aufbau des Bildschirm, und muessen
nicht auf ueberschneidungen mit dem Rasterstrahl achten. Das laeuft ganz
gut, solange man nicht den ganzen Bildschirm fuer Dots benutzt. Sobald das
aber gemacht wird, muessen wir diese Technik noch eine Stufe weitertreiben.
Treblebuffering - Waehrend Bild A angezeigt wird, instruiert der Prozessor
den Blitter Bild B zu loeschen und baut Bild C, waehrend der Blitter
arbeitet, auf.
Ich schaetze ihr koennt das selber realisieren, und werde deshalb dazu kein
Programm liefern. Allerdings ein kleine Dots Routine werde ich mal
dabeipacken. An ihr koennt ihr dann schon mal sehen, wie ich die Routine
eingebaut habe, und wie man Zeit spart. Ich habe die einzelen Aktionen
farblich auf dem Bildschirm gekennzeichnet.

Rot - Blitter loescht altes Bild
Gruen- Prozessor baut neues Bild auf
Gelb - Tabelle wird rotiert

Baut mal die von mir angeprochenen verbesserungen ein, und versucht die
Zeit auf ein Minimun zu reduzieren.

Start:
Execbase= 4
Openlibrary= -408
Vhposr= $dff006
Forbid= -30-102
Permit= -30-108
Dmacon= $dff096
Intena= $dff09a
Dmaconr= $dff002
Bltdmod= $dff066
Bltafwm= $dff044
Bltcon0= $dff040
Bltcon1= $dff042
Bltdpt= $dff054
Bltapt= $dff050
Bltsize= $dff058


Move.w #$0020,dmacon
Move.w #$4000,intena
Move.l execbase,a6
Jsr forbid(a6)
Bsr.l makecl
Bsr.l clearscreen
Bsr.l kreis
Wait: Cmpi.b #$72,$dff006
Bne.s wait
Move.w #$0400,$dff180
Bsr.l blitclear
Move.w #$0040,$dff180
Bsr.l kreis
Move.w #$0440,$dff180
Bsr.l rotate
Move.w #$0000,$dff180
Maus: Btst #6,$bfe001
Bne wait
Move.l execbase,a6
Jsr permit(a6)
Lea gfxname(pc),a1
Jsr openlibrary(a6)
Move.l d0,a6
Move.w #$83e0,dmacon
Move.w #$c000,intena
Move.l 38(a6),$dff080
Moveq #0,d0
Rts
Makecl:
Lea $07f000,a0
Move.l a0,$dff080
Move.l #$008e3081,(a0)+
Move.l #$009030c1,(a0)+
Move.l #$00920038,(a0)+
Move.l #$009400d0,(a0)+
Move.l #$01001200,(a0)+
Move.l #$00e00003,(a0)+
Move.l #$00e20000,(a0)+
Move.l #$01080000,(a0)+
Move.l #$01800000,(a0)+
Move.l #$01820fb0,(a0)+
Move.l #$fffffffe,(a0)+
Rts
Set:
Divu #8,d0
Mulu #40,d1
Add.w d0,d1
Swap d0
Move.b #-1,d2
Sub.b d0,d2
Bset d2,(a5,d1)
Rts
Kreis:
Move.w #238,d3
Lea sinus,a0
Lea sinus+120,a1
Lea $3000e,a5
Move.w (a0)+,d0
Addq #6,a0
Move.w (a1)+,d1
Addq #4,a1
Bsr.s set
Moveq #0,d0
Moveq #0,d1
Dbf d3,loop
Rts
Rotate:
Lea Sinus,a0
Move.l a0,a1
Add.l #8,a1
Move.w #476,d0
Move.l (a0),a2
Move.l 4(a0),a3
Roloop:
Move.l (a1)+,(a0)+
Dbf d0,roloop
Move.l a2,(a0)
Move.l a3,4(a0)
Rts
Sinus:
Dc.w $0020,$0020,$0021,$0022,$0023,$0023,$0024,$0025
Dc.w $0026,$0027,$0027,$0028,$0029,$002A,$002A,$002B
Dc.w $002C,$002C,$002D,$002E,$002F,$002F,$0030,$0031
Dc.w $0031,$0032,$0033,$0033,$0034,$0034,$0035,$0036
Dc.w $0036,$0037,$0037,$0038,$0038,$0039,$0039,$003A
Dc.w $003A,$003B,$003B,$003B,$003C,$003C,$003C,$003D
Dc.w $003D,$003D,$003E,$003E,$003E,$003E,$003F,$003F
Dc.w $003F,$003F,$003F,$003F,$003F,$003F,$003F,$003F
Dc.w $003F,$003F,$003F,$003E,$003E,$003E,$003E,$003D
Dc.w $003D,$003D,$003C,$003C,$003C,$003B,$003B,$003A
Dc.w $003A,$003A,$0039,$0039,$0038,$0038,$0037,$0037
Dc.w $0036,$0035,$0035,$0034,$0034,$0033,$0032,$0032
Dc.w $0031,$0030,$0030,$002F,$002E,$002E,$002D,$002C
Dc.w $002C,$002B,$002A,$0029,$0029,$0028,$0027,$0026
Dc.w $0026,$0025,$0024,$0023,$0022,$0022,$0021,$0020
Dc.w $0020,$0020,$001F,$001E,$001D,$001C,$001C,$001B
Dc.w $001A,$0019,$0019,$0018,$0017,$0016,$0016,$0015
Dc.w $0014,$0013,$0013,$0012,$0011,$0011,$0010,$000F
Dc.w $000F,$000E,$000D,$000D,$000C,$000B,$000B,$000A
Dc.w $000A,$0009,$0009,$0008,$0008,$0007,$0007,$0006
Dc.w $0006,$0005,$0005,$0005,$0004,$0004,$0003,$0003
Dc.w $0003,$0003,$0002,$0002,$0002,$0002,$0001,$0001
Dc.w $0001,$0001,$0001,$0001,$0001,$0001,$0001,$0001
Dc.w $0001,$0001,$0002,$0002,$0002,$0002,$0002,$0003
Dc.w $0003,$0003,$0004,$0004,$0004,$0005,$0005,$0006
Dc.w $0006,$0007,$0007,$0007,$0008,$0008,$0009,$000A
Dc.w $000A,$000B,$000B,$000C,$000C,$000D,$000E,$000E
Dc.w $000F,$0010,$0010,$0011,$0012,$0012,$0013,$0014
Dc.w $0015,$0015,$0016,$0017,$0018,$0018,$0019,$001A
Dc.w $001B,$001B,$001C,$001D,$001E,$001F,$001F,$0020
Dc.w $0020,$0021,$0021,$0022,$0023,$0024,$0025,$0025
Dc.w $0026,$0027,$0028,$0028,$0029,$002A,$002B,$002B
Dc.w $002C,$002D,$002E,$002E,$002F,$0030,$0030,$0031
Dc.w $0032,$0032,$0033,$0034,$0034,$0035,$0035,$0036
Dc.w $0036,$0037,$0038,$0038,$0039,$0039,$0039,$003A
Dc.w $003A,$003B,$003B,$003C,$003C,$003C,$003D,$003D
Dc.w $003D,$003E,$003E,$003E,$003E,$003E,$003F,$003F
Dc.w $003F,$003F,$003F,$003F,$003F,$003F,$003F,$003F
Dc.w $003F,$003F,$003E,$003E,$003E,$003E,$003D,$003D
Dc.w $003D,$003D,$003C,$003C,$003B,$003B,$003B,$003A
Dc.w $003A,$0039,$0039,$0038,$0038,$0037,$0037,$0036
Dc.w $0036,$0035,$0035,$0034,$0033,$0033,$0032,$0031
Dc.w $0031,$0030,$002F,$002F,$002E,$002D,$002D,$002C
Dc.w $002B,$002A,$002A,$0029,$0028,$0027,$0027,$0026
Dc.w $0025,$0024,$0024,$0023,$0022,$0021,$0020,$0020
Dc.w $0020,$001F,$001E,$001E,$001D,$001C,$001B,$001A
Dc.w $001A,$0019,$0018,$0017,$0017,$0016,$0015,$0014
Dc.w $0014,$0013,$0012,$0012,$0011,$0010,$0010,$000F
Dc.w $000E,$000E,$000D,$000C,$000C,$000B,$000B,$000A
Dc.w $0009,$0009,$0008,$0008,$0007,$0007,$0006,$0006
Dc.w $0006,$0005,$0005,$0004,$0004,$0004,$0003,$0003
Dc.w $0003,$0002,$0002,$0002,$0002,$0001,$0001,$0001
Dc.w $0001,$0001,$0001,$0001,$0001,$0001,$0001,$0001
Dc.w $0001,$0001,$0002,$0002,$0002,$0002,$0003,$0003
Dc.w $0003,$0004,$0004,$0004,$0005,$0005,$0005,$0006
Dc.w $0006,$0007,$0007,$0008,$0008,$0009,$0009,$000A
Dc.w $000A,$000B,$000C,$000C,$000D,$000D,$000E,$000F
Dc.w $000F,$0010,$0011,$0011,$0012,$0013,$0014,$0014
Dc.w $0015,$0016,$0016,$0017,$0018,$0019,$0019,$001A
Dc.w $001B,$001C,$001D,$001D,$001E,$001F,$0020,$0020
Dc.w $0020,$0020,$0021,$0022,$0023,$0023,$0024,$0025
Dc.w $0026,$0027,$0027,$0028,$0029,$002A,$002A,$002B
Dc.w $002C,$002C,$002D,$002E,$002F,$002F,$0030,$0031
Dc.w $0031,$0032,$0033,$0033,$0034,$0034,$0035,$0036
Dc.w $0036,$0037,$0037,$0038,$0038,$0039,$0039,$003A
Dc.w $003A,$003B,$003B,$003B,$003C,$003C,$003C,$003D
Dc.w $003D,$003D,$003E,$003E,$003E,$003E,$003F,$003F
Dc.w $003F,$003F,$003F,$003F,$003F,$003F,$003F,$003F
Dc.w $003F,$003F,$003F,$003E,$003E,$003E,$003E,$003D
Dc.w $003D,$003D,$003C,$003C,$003C,$003B,$003B,$003A
Dc.w $003A,$003A,$0039,$0039,$0038,$0038,$0037,$0037
Dc.w $0036,$0035,$0035,$0034,$0034,$0033,$0032,$0032
Dc.w $0031,$0030,$0030,$002F,$002E,$002E,$002D,$002C
Dc.w $002C,$002B,$002A,$0029,$0029,$0028,$0027,$0026
Dc.w $0026,$0025,$0024,$0023,$0022,$0022,$0021,$0020
Dc.w $0020,$0020,$001F,$001E,$001D,$001C,$001C,$001B
Dc.w $001A,$0019,$0019,$0018,$0017,$0016,$0016,$0015
Dc.w $0014,$0013,$0013,$0012,$0011,$0011,$0010,$000F
Dc.w $000F,$000E,$000D,$000D,$000C,$000B,$000B,$000A
Dc.w $000A,$0009,$0009,$0008,$0008,$0007,$0007,$0006
Dc.w $0006,$0005,$0005,$0005,$0004,$0004,$0003,$0003
Dc.w $0003,$0003,$0002,$0002,$0002,$0002,$0001,$0001
Dc.w $0001,$0001,$0001,$0001,$0001,$0001,$0001,$0001
Dc.w $0001,$0001,$0002,$0002,$0002,$0002,$0002,$0003
Dc.w $0003,$0003,$0004,$0004,$0004,$0005,$0005,$0006
Dc.w $0006,$0007,$0007,$0007,$0008,$0008,$0009,$000A
Dc.w $000A,$000B,$000B,$000C,$000C,$000D,$000E,$000E
Dc.w $000F,$0010,$0010,$0011,$0012,$0012,$0013,$0014
Dc.w $0015,$0015,$0016,$0017,$0018,$0018,$0019,$001A
Dc.w $001B,$001B,$001C,$001D,$001E,$001F,$001F,$0020
Dc.w $0020,$0021,$0021,$0022,$0023,$0024,$0025,$0025
Dc.w $0026,$0027,$0028,$0028,$0029,$002A,$002B,$002B
Dc.w $002C,$002D,$002E,$002E,$002F,$0030,$0030,$0031
Dc.w $0032,$0032,$0033,$0034,$0034,$0035,$0035,$0036
Dc.w $0036,$0037,$0038,$0038,$0039,$0039,$0039,$003A
Dc.w $003A,$003B,$003B,$003C,$003C,$003C,$003D,$003D
Dc.w $003D,$003E,$003E,$003E,$003E,$003E,$003F,$003F
Dc.w $003F,$003F,$003F,$003F,$003F,$003F,$003F,$003F
Dc.w $003F,$003F,$003E,$003E,$003E,$003E,$003D,$003D
Dc.w $003D,$003D,$003C,$003C,$003B,$003B,$003B,$003A
Dc.w $003A,$0039,$0039,$0038,$0038,$0037,$0037,$0036
Dc.w $0036,$0035,$0035,$0034,$0033,$0033,$0032,$0031
Dc.w $0031,$0030,$002F,$002F,$002E,$002D,$002D,$002C
Dc.w $002B,$002A,$002A,$0029,$0028,$0027,$0027,$0026
Dc.w $0025,$0024,$0024,$0023,$0022,$0021,$0020,$0020
Dc.w $0020,$001F,$001E,$001E,$001D,$001C,$001B,$001A
Dc.w $001A,$0019,$0018,$0017,$0017,$0016,$0015,$0014
Dc.w $0014,$0013,$0012,$0012,$0011,$0010,$0010,$000F
Dc.w $000E,$000E,$000D,$000C,$000C,$000B,$000B,$000A
Dc.w $0009,$0009,$0008,$0008,$0007,$0007,$0006,$0006
Dc.w $0006,$0005,$0005,$0004,$0004,$0004,$0003,$0003
Dc.w $0003,$0002,$0002,$0002,$0002,$0001,$0001,$0001
Dc.w $0001,$0001,$0001,$0001,$0001,$0001,$0001,$0001
Dc.w $0001,$0001,$0002,$0002,$0002,$0002,$0003,$0003
Dc.w $0003,$0004,$0004,$0004,$0005,$0005,$0005,$0006
Dc.w $0006,$0007,$0007,$0008,$0008,$0009,$0009,$000A
Dc.w $000A,$000B,$000C,$000C,$000D,$000D,$000E,$000F
Dc.w $000F,$0010,$0011,$0011,$0012,$0013,$0014,$0014
Dc.w $0015,$0016,$0016,$0017,$0018,$0019,$0019,$001A
Dc.w $001B,$001C,$001D,$001D,$001E,$001F,$0020,$0020
Dc.w $20,$20,$20,$20,$20,$20,$20,$20

Clearscreen:
Move.w #10239/2,d0
Lea $30000,a0
Clloop:
Clr.w (a0)+
Dbf d0,clloop
Rts
Blitclear:
Move.w #%0000000100000000,bltcon0
Move.w #32,bltdmod
Move.l #$3000e,bltdpt
Move.w #$40*64+4,bltsize
Blwait:
Btst #14,Dmaconr
Bne.s blwait
Rts

Gfxname: dc.b "graphics.library",0

So, das war mal wieder genug fuer Heute...Wieder viel zu viel Source drin,
aber das ist halt etwas handfestes..

Wollen wir das Listing aus dem Letzten Kursteil doch mal ueberarbeiten und
versuchen es schneller zu machen.
Suchen wir erstmal logisch ab, und versuchen eine Loesung zu finden.

1. Die hin und her springerei im Programm tut der Geschwindigkeit bestimmt
nicht so gut. Also setzen wir anstelle des

Bsr.s Set

in der Kreisroutine, direkt die Routine `Set` in die Kreisroutine.
Das anspringen von Programme dauert zwar nicht solange, ebensowenig das
Rts`en, aber bei 239 anspruengen macht sich das schon bemerkbar.

2. Das rotieren der Sinustabelle, koennte ja auch der Blitter machen. Man
muss sich nur mit dem Prozessor das oberste Wort merken, damit man es
hinterher wieder hintendran Pflanzen kann.

Das Rotieren mit dem Blitter saehe dann etwas so aus.

Blitrotate:
Move.w Sinus,Sinbuffer ; Erstes Wort retten
Move.w #%0000100111110000,Bltcon0
Move.l #-1,Bltafwm
Move.l #Sinus+2,Bltapt
Move.l #Sinus,Bltdpt
Move.w #Anzahl der Zeile der Sintab * 64+8,Bltsize
Rts

Gleichzeitig koennten wir noch die groesse der Elemente aendern. Wir
koennten Bytes nehmen. Da der Blitter aber nur mit Worten arbeitet, wird
immer eine Koordinate uebersprungen, was aber nicht so schlimm ist, da die
bewegung eh noch etwas langsam ist. Die grosse Vorteil ist dann, das die
Zeit die der Blitter braucht halbiert wird.
Achtet allerdings auf die veraenderungen im Listing, da es der 68000 sehr
krumm nimmt wenn man etwas an eine ungerade stelle schreiben, oder von eben
eine solchen lesen, will wenn der Befehl nicht durch `.b` dafuer ausgelegt
ist. Also, vorsicht!

Wir warten allerdings nicht auf den Blitter, sondern starten die Aktion vor
der Kreisroutine. Da sich die Punkte dann nur Max. um eine Koordinate
verschieben, macht das nichts aus. Aber wir haben wieder ne menge Zeit
gespart.

3. Machen wir uns mal an die beiden Zeitkiller Nummer eins, in der
Set-routine ran.

Divu und Mulu.

3.1 Mulu ist schnell ersetzt, indem wir einfach eine Tabelle erschaffen, in
der die zu den verschiedene Koordinaten gehoerenden Werte fuer die Additon
zur Playfieldbase stehen. die noetigen aenderungen waeren -

3.1.1 Tabelle mit 64 ($3f) Elemente, die immer um 40 ($28) hochgezaehlt
wird.

3.1.2 Den Mulu #40,d1 befehl durch

Add.w d1,d1 ; Um auf Worte zu kommen
Move.w (a4,d1.w),d1 ; Indirekt aus der Tabelle lesen.

ersetzen. Die Addertabelle fuer d1, muss ihn A4 stehen. Wagt es nicht das
jedesmal in der Schleife mit Lea zu erledigen, sondern stellt es vor dem
Aufruf der Kreisroutine, oder vor der Schleife ein.

3.2 Bei Divu kriegen wir da schon mehr schwierigkeiten. Obwohl ich gesagt
habe das ich keine schnellere loesung kenne, habe ich mich doch mal auf die
suche gemacht, und habe selbst etwas rumprobiert. Allerdings suchte ich
immer nach dem Kompletten Divu-ersatz. Den werde ich wohl auch noch laenger
suchen. Aber ich habe mir ueberlegt, das uns ja ein Divu #8 ersatz genuegt.

Setzt also anstelle des Divu`s, bitte folgende Zeilen ein -

Ror.l #3,D0
Swap D0
Rol.w #3,D0
Swap D0

Dauert im gegensatz zum echten Divu #8 mit 140 Taktzyklen, nur um die 30
Tz, und das ist doch schon eine ganze Menge.

Es gibt noch mehrere kleine Extra die man einbauen koennte, aber das sind
die Groebsten Zeitfresser in dem Listing, ohne sie wird der Dots Effekt
langsam Konkurrenzfaehig.

Kommen wir jetzt mal zu den Manipulationsmoeglichkeiten fuer die
Sinus-Kurve selbst.
Dort koennen wir durch einfaches weiterstellen der Zeiger auf die
Sintabelle, sehr schoenen effekte erzeugen.
Die erste Form ist im Listing schon enthalten.

Move.w (a0)+,d0 ; X-Koordinate nach D0 mit gleichzeitigem
; weiterstellen auf das naechste Element
Addq #6,a0 ; Den Zeiger auf das naechste Element, fuer
; den naechsten durchlauf weiter nach
; hinten setzen
Move.w (a1)+,d1
Addq #4,a1

Dadurch wird ereicht das mehrere Element einfach uebersprungen werden, und
somit die X-Kurve schneller durchlauf wird, was eine Elipsen-Form des Sinus
hervorruft.

Desweiteren waere noch ein Addieren eines Beliebigen Wertes aus der Sintab
moeglich. Er musste dann vor der bearbeitung der X-Koordinate erfolgen. Man
kann diese Werte auf dieselbe Art und Weise aus der Tabelle holen.
Probiert ruhig ein bisschen mit den Add-Werten rum, die Muster die manchmal
dabei entstehen, sind ein wahrer genuss. Wenn man jetzt noch ueberlegt, wie
begrenzt die moeglichkeiten bei so einen kleine Sinus bzw. bei so einer
kleinen Tabelle sind, da kann man sich nur dasselbe mit einem 200*200 Sinus
freuen.

Bleiben wir mal bei dem Thema `Manipulationsmoeglichkeiten`. Wir koennen
allerdings auch von aussen in das geschehen eingreifen, indem wir die Werte
waehrend das Programm laeuft veraendern. Es staenden dafuer die beiden
Add-werte die die Zeiger auf die naechste X- oder Y-Koordinate veraendern,
der Wert der zur ermittelten X-Koordinate zugezogen wird, und die Anzahl
der Punkte zur auswahl.
Davon sucht sich am besten jeder selbst das aus was er am schoensten
findet, oder erweitert die Abfrage nach seinem Geschmack.

Dann waere da noch die Frage wie wir in das geschehen eingreifen. Da
staenden wieder drei moeglichkeiten offen. Als da waeren Joystick2, Maus
oder die Tastatur.
Nun, die Maus laesst sich zwar einfach abfragen, ist aber sehr schwer zu
dosieren. Der Joystick waere da schon besser, allerdings artet das so
langsam in Arbeit aus. Die Tastatur zum assemblieren, dann schnell den
Joystick hinter dem Bildschirm her kramen, um das ganze zu veraendern und
zum schluss wieder die Maus betatscht, damit der ganze Spuk ein Ende hat.
Also bleiben wir doch einfach mal bei der Tastatur.
Suchen wir uns noch ein paar schoene Tasten aus..O.K nehmen wir die
Cursortasten, den mehr als Vier brauchen wir ja sowieso nicht. Damit
koennen wir 2 Parameter im laufenden Programm veraendern..Das reicht
erstmal zum kennenlernen.

Tasten koennen wir ueber die Seriellen Datenregister der Cia (Computer
Interface Adapter) erhalten. Die Cia im Amiga, und auch in allen anderen
Computer sind zur Kommunikation mit anderen Geraten da, erledigen aber
auch andere Sachen.
Jedenfalls liegt das Register das wir suchen bei $Bfec01 und heisst Ciaaspr
(Cia-A-Seriell Port Register) und ist ein Byte lang, und wie meistens bei
den Cia`s ist es Lowaktiv, daran muessen wir denken bei der Arbeit. Den
Wert den wir dort lesen koennen muessen wir vor der abfrage noch
bearbeiten damit die Orginal-Tasten Codes verwendet werden koennen,
ansonsten kann man die Werte natuerlich auch so uebernehmen, allerdings
muesste man dann immer bevor man einen Buchstaben abfragen will, von hand
nachschauen, welchen Wert er im Ciaaspr hinterlaesst.
Sobald wir diese aufbereitung des Codes haben, koennen wir mit der
Auswertung beginnen. Das folgende Prograemmchen veraendert bei Tastendruck
die Bildschirmfarbe, und ist mit Mausbutton zu verlassen. Das Programm ist
sehr einfach gehalten, und es duerfte nicht schwer sein, es in die
Endlos-Wait schleife des Programm einzusetzen. Ihr koenntet es sofort nach
dem Start der Blitclear-Routine aufrufen, dann waere es auch noch Optimal
in das Listing eingefuegt, und belastet die Gesamtzeit der Routinen nicht.

Color00= $DFF180
Ciaapra= $BFE001
Ciaaspr= $BFEC01

Holdkey:Move.b Ciaaspr,D0 ; Wert vom Cia abholen
Move.b #$ff,D1 ; Maske fuer Lowaktiv
Sub.b D0,D1 ; Lowaktiv umdrehen
Ror.b #1,D1 ; Eins nach Rechts
Cmp.b #$4c,D1 ; Vergleich `Oben`
Beq.s Hoch ; Ja? Nein-> Weiter
Cmp.b #$4d,D1 ; Vergleich `Runter`
Beq.s Runter ; Ja? Nein-> Weiter
Cmp.b #$4f,D1 ; Vergleich `Links`
Beq.s Links ; Ja? Nein-> Weiter
Cmp.b #$4e,D1 ; Vergleich `Rechts`
Beq.s Rechts ; Ja? Nein-> Weiter
Btst #6,Ciaapra ; Maustest
Beq.s Raus ; Ja?-> Raus, Nein-> Weiter
Bra.s Holdkey ; Zurueck zum Anfang
Raus: Rts ; Raus!
Hoch: Move.w #$f00,Color00
Bra.s holdkey
Runter:
Move.w #$0f0,Color00
Bra.s Holdkey
Links:
Move.w #$00f,Color00
Bra.s Holdkey
Rechts:
Move.w #$fff,Color00
Bra.s Holdkey


Denkt dran das dieses ein eigenstaendig laufaehiges Proggy ist..Ihr muesst
es umschreiben, um es einbauen zu koennen.

Fuer die Farbveraenderung muesst ihr dann eben nur Parameter ihm Listing
aendern. Ihr koennt dazu die letztens kennengelernte `Selbstmodifikation`
benutzen, oder mit Dc.w oder so, abgelegte Parameter aendern. Denkt daran
das ihr je nach dem wie ihr euch entschieden habt, das listing nochmals
durchzugehen, da man beim einbauen von Routinen oft schlampt, und
manchmal viel vom neuen weglassen kann.

Auf diesem Weg der Tastatur abfrage, geht man ja nicht ueber irgendwelche
Betriebssystemroutine (Hatte ich ja versprochen, das wir diese umgehen)
Sondern fragen direkt an der Tastatur nach welche Taste gedrueckt ist.
Da die Tasten durchnummeriert sind, kriegen wir die Werte nicht nach dem
Alphabet geordnet wieder, sondern so wie sie auf der Tastatur geordnet
sind. Deswegen sind die Werte die ich mal hier zusammen gestellt habe, auch
ziemlich chaotisch.

F1 - $50 F2 - $51 F3 - $52 F4 - $53 F5 - $54
F6 - $55 F7 - $56 F8 - $57 F9 - $58 F10 - $59
ESC $45 DEL $46 HELP $5f Space $40 ` - $00

Alt Links $64 Alt Rechts $65

Amiga Links $66 Amiga Rechts $67

Shift Links $60 Shift Rechts $61


Cursor Oben $4c
Unten $4d
Rechts $4e
Links $4f

Return $44

Backspace $41

TAB $42

CTRL $63

1.Tastenreihe

1 - $01 2 - $02 3 - $03 4 - $04 5 - $05
6 - $06 7 - $07 8 - $08 9 - $09 0 - $0a
á - $0b , - $0c - $0d

2.Tastenreihe

q - $10 w - $11 e - $12 r - $13 t - $14
z - $15 u - $16 i - $17 o - $18 p - $19
 - $1a + - $1b

3.Tastenreihe

a - $20 s - $21 d - $22 f - $23 g - $24
h - $25 j - $26 k - $27 l - $28 ” - $29
„ - $2a # - $2b

4.Tastenreihe

< - $30 y - $31 x - $32 c - $33 v - $34
b - $35 n - $36 m - $37 , - $38 . - $39
- - $3a

Zehnerblock

[ - $5a ] - $5b / - $5c * - $5d
7 - $3d 8 - $3e 9 - $3f - - $4a
4 - $2d 5 - $2e 6 - $2f + - $5e
1 - $1d 2 - $1e 3 - $1f
0 - $f . - $3c

Enter $43


Das das nur Tastennummern sind koennt ihr daran sehen, das Shift links und
rechts, obwohl dieselbe wirkung einen unterschiedlichen Tastencode haben.

Wenn ihr diese Werte selber mal nachpruefen wollt, koennt ihr folgendes
kleine Prograemmchen benutzen. Mit dem habe ich die ganzen Code auch
geholt.
Die benutzung ist relativ einfach. Einfach starten und dann die Taste
druecken, dessen Code man habe will. Taste festhalten, und gleichzeitig die
Maus zum verlassen druecken. Der Gueltige Code steht dann in D1 bei der
Seka-Register ausgabe. Fuer Tasten die den Seka veranlassen den Bildschirm
zu loeschen, habe ich das Ergebnis nochmal im Buffer abgelegt, den man mit
`QBuffer` abfragen kann.

Ciaapra= $bfe001
Ciaaspr= $bfec01

Holdkey:Move.b Ciaaspr,D0
Move.b #$ff,D1
Sub.b D0,D1
Ror.b #1,D1
Move.w d1,Buffer
Btst #6,ciaapra
Bne.s holdkey
Rts
Buffer: Dc.w 0

Ihr seit ja schon relativ weit mit der Programmiererei, und habt euch
sicherlich schon mal an andere Sachen gewagt. Ich rede von eigenen Cli
befehlen, oder Sachen mit Betriebssystem und benutzen von library`s.
Allerdings werdet ihr da etliche schwierigkeiten gehabt haben, denn das
habt ihr noch nicht gelernt. Was ihr in dem Kurs hier lernt ist nur fuer
Demos/Intros geeignet. Bei der Amiga-Programmierung mit multitasking
unterstuetzung fallt ihr auf die nase mit dem was ihr koennt.
Allen Leute die das bemaekelt haben sei hier gesagt. `Ich kann richtig mit
dem betriebssystem umgehen, habe den Kurs aber nur fuer die
Demoprogrammierung geschrieben`...Wer mit System an die Sache gehen will,
sollte die Finger von diesem Kurs lassen. Denn er ist Gift fuer den Amiga.
Zu Multitasking kan ich nur sagen: Tolle Sache, allerdings lohnt sich das
nicht, da unsere sachen die wir so basteln, min. 90% des Prozessors in
anspruch nehmen, da laeuft eh nicht mehr viel mit Cli und so.

So fuer alle die jetzt noch weiterlesen habe ich hier eine kleine Routine
die Coolcapture, das ist einer von den Resetvectoren, verbiegt, um ihn auf
eigene Programme zu setzen.
Man kann damit seine Demo/Intro vor Rippern schuetzen, die nur Bilder und
Sounds Klauen wollen, und dann damit zu prahlen.
Auch sie ist in der Form nicht erlaubt, aber sehr kurz. Der Amiga weiss von
nichts dabei...deshalb ist sie Ideal.


Move.l $04,a6
Move.l #Prog,$2e(a6) ; Adresse nach CoolCapture
Clr.l d0
Lea $22(a6),a0
Move.l #$17,d1
Chkloop:Add.w (a0)+,d0
Dbf d1,chkloop
Not.w d0
Move.w d0,(a0)
Rts
Prog:

Ab dem Label prog kann dann eure eigene Reset-routine stehen...es sind euch
da keine grenzen gesetzt...allerdings muesst ihr hier aufpassen, und
solltest, falls ihr bilder benutzt, die ganze sache ins Chipmem legen, und
das moeglichst weit hinten. So bei $70000, dann kommt der Amiga dann nicht
mehr so leicht dran.
Der Wert bleibt bis er geloescht ist dort stehen, und das Programm wird
jeden Reset durchgefuehrt. Allerdings kann es natuerlich probleme geben,
falls das Prograemchen ueberschrieben wird, dann duerfte es einen Guru
geben, Dead Ends sind aber auch schon vorgekommen, naja! Wenn man es aber
nur bei seine Intros/Demos dabeipackt, dann duerfte diese Routine reichen.

So, Leute....

Hiermit habt ihr das Ende dieses Kurses erreicht. Das was ich mit diesem
Kurs erreichen wollte habe ich erreicht. Viele Leute, unter anderem auch
ein paar aus der Dfue-Szene haben gelernt wie man den Amiga Programmiert.
Ich selber bin vor einem halben Jahr umgestiegen und habe mich nur noch mit
der Systemprogrammierung beschaeftigt, und kaum noch an Intros gearbeitet.

Jetzt bin ich wieder in eine Gruppe eingestiegen, und werde wohl wieder
Intros schreiben. Etwas eingerostet bin ich schon, aber das kommt wieder
(Hoffe ich).

Naja, ich danke all jenen Leuten die mich lobten, oder kritisierten. Und
natuerlich allen die sich mit dem Kurs beschaeftigt haben.

Aber auch in Zukunft koennt ihr mich natuerlich gerne zu dem Thema Demos
und Intros fragen, ich werde da auch weiterhin frage und antwort stehen.

Ein Grund dafuer das ich jetzt aufhoeren ist das es jetzt, wenn ich
weiterschreiben wuerde, an die sachen geht die wirklich Coden bedeuten, und
das veraet keiner der Coder, wie Flash, Coconut oder andere, so gerne. Das
muesst ihr schon selber lernen. Aber mit der Grundlage und dem verstaendnis
was ihr hoffentlich aus diesem Kurs bekommen habt, koennt ihr wirklich was
anfangen. Es ist sehr Praxis nah, weil es zum teil auch Routinen aus meinen
Intros sind.

O.K, genug der Worte....

Und Tschuess...

Jeff Kandle