home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / assembler-kurs / lektionen / lektion2.txt < prev    next >
Text File  |  1977-12-31  |  31KB  |  705 lines

  1.  
  2.     ASSEMBLERKURS - Lektion 2
  3.  
  4. Na, habt ihr Listing1a.s perfekt verstanden? Wenn  nicht,  dann  seit  ihr
  5. reif für die Klapsmühle, und ihr solltet den Kurs hiermit beenden.
  6.  
  7. Nun  wollen  wir  die  68000er-"Sprache"  etwas vertiefen. Ich habe vorhin
  8. schon angedeutet, daß der Prozessor nur als Organisator wirkt, selbst aber
  9. nur  Werte  herumkopiert  und ändert. Indem man gewisse Werte in bestimmte
  10. Zonen im Speicher gibt, z.B. $DFFxxx oder $BFExxx, gibt man den  Pins  der
  11. einzelnen  Chips  Strom,  wie  dem  der  Grafik, des Sound, der Ports, und
  12. folgedessen kann  man,  wie  im  vorgegangenen  Beispiel,  die  Farben  am
  13. Bildschirm ändern, oder durch Auslesen bestimmter Adressen die Information
  14. erhalten, wo sich gerade der Elektronenstrahl des Monitors  befindet  oder
  15. ob  der  Mausknopf  gedrückt ist. Um ein Demo oder ein Spiel zu schreiben,
  16. braucht man einen Haufen solcher Adressen, REGISTER genannt,  und  es  ist
  17. nötig,  sie  zu  kennen  genauso  wie  die Programmiersprache des 68000ers
  18. (MOVE, JSR, ADD, SUB etc.), mit denen man die beschreibt.
  19.  
  20. Für  das  Programmieren  auf  diese  Art  brauchen  wir  die  Bibliotheken
  21. (Libraries)  der  ROM  /  des   Kickstarts   1.2/1.3/2.0/3.0   (also   die
  22. Subroutines,  die  uns erlauben, ein Workbenchfenster zu öffnen oder einen
  23. File zu  lesen...)nicht,  oder  vielmehr  nur  sehr  wenig:  z.B.  um  das
  24. Multitasking abzuschalten, um die Workbench nicht in GURU zu schicken!
  25.  
  26. Ich  halte  es für notwendig, in dieser Lektion 2 die Verwendung des 68000
  27. zu vertiefen, so daß verstanden wird, was sein Zweck ist.
  28.  
  29. Das wichtigste, das es zu lernen gilt, sind  die  Adressierungsarten.  Das
  30. ist  fast  wichtiger  als  die Befehle selbst, denn wenn das einmal sitzt,
  31. kann man die einzelnen Befehle lernen,  sie  funktionieren  alle  mit  der
  32. gleichen Syntax ("Rechtschreibung"), und man muß sich nur noch merken, was
  33. jeder einzelne tut. Wie schon gesagt, arbeitet der Prozessor im  Speicher,
  34. der in Adressen eingeteilt ist, dessen Einheit das Byte ist. Normalerweise
  35. wird die hexadezimale Schreibweise angewandt, das  ist  ein  Zahlensystem,
  36. das  sich  von  gebräuchlichen  Dezimalsystem  darin unterscheidet, daß es
  37. nicht nur die Ziffern 0 bis 9 besitzt, sondern 0 bis 9 und A bis F, es hat
  38. als Basis nicht Zehn sondern Sechzehn. Es ist so, als ob die Ziffern A, B,
  39. C... wie 10, 11, 12, ... dastehen würden. Um die Zahlen von Hexadeziaml in
  40. Dezimale  zu  konvertieren, reicht es, in der Kommandozeile des ASMONE den
  41. Befehl  "?"  einzugeben:  "?10000"  liefert  als   Resultat   $2710,   den
  42. entsprechenden  Wert  als  Hexzahl  (die  Hexzahlen werden immer von einem
  43. $-Zeichen angeführt, die Dezimalzeichen von nichts  und  die  Binärzeichen
  44. von  einem  %).  Das  Hex-system  wird  verwendet,  weil  es  der  Art des
  45. Computers, Zahlen darzustellen am  nächsten  liegt.  Der  Computer  selbst
  46. "denkt" aber klarerweise im Binärsystem, also nur 0 und 1.
  47. Um die verschiedenen Arten der  Adressierung  des  68000ers  verstehen  zu
  48. lernen,   werden   wir  den  Befehl  CLR  verwenden,  der  die  angegebene
  49. Speicherzelle löscht:
  50.  
  51.     CLR.B    $40000      ; Erinnert ihr euch an den Unterschied
  52.                 ; zwichen .B, .W und .L ?
  53.  
  54. Diese Instruktion "säubert" die Speicherzelle $40000, es löscht  also  das
  55. Byte Nr. $40000 im Speicher (setzt es auf 0). Das ist der einfachste Fall,
  56. die sogenannte ABSOLUTE Adressierung; d.h., man gibt  direkt  die  Adresse
  57. an,  auf  die  man  ein  CLR  anwenden  will.  Im Assembler sind LABELS in
  58. Gebrauch, die helfen, einen "Ort" im Programm zu  identifizieren,  in  dem
  59. z.B. ein Byte steht, das es anzusprechen gilt. In diesem Fall reicht statt
  60. der Adresse der Name des Label. Der Assembler kümmert sich dann darum, das
  61. Label  durch  die  effektive Adresse des Bytes zu ersetzen. Wenn wir unser
  62. erstes Listing in etwa so modifizieren:
  63.  
  64. Waitmouse:
  65.     move.w    $dff006,$dff180    ; gibt den Wert von $dff006 in $dff180
  66.                 ; also das VHPOSR in COLOR0
  67.     btst    #6,$bfe001    ; limke Maustaste gedrückt ?
  68.     bne.s    Waitmouse    ; wenn nicht, zurück zu Waitmouse und
  69.                 ; wiederhole
  70.                 ; (das .s ist äquivalent zu .B in diesem
  71.                 ; Typ von Anweisung (bne.s = bne.b)
  72.     clr.b    Wert1        ; Setze Wert1 auf 0
  73.     rts            ; Steige aus
  74.  
  75. Wert1:
  76.     dc.b    $30    ; dc.b bedeutet "Gib folgendes Byte in den
  77.             ; Speicher", in diesem Fall wird $30 unter Wert1:
  78.             ; gesetzt.
  79.  
  80. Vor dem Ausstieg mit dem RTS würde das Byte, das durch  das  Label  Wert1:
  81. gekennzeichnet  wird,  auf NULL gesetzt. Diesem Byte würde in der Fase des
  82. Assemblierens eine ganz bestimmte,  absolute  Adresse  zugewiesen  werden,
  83. z.B.  wenn das Programm mit dem ASMONE assembliert würde, auf eine Adresse
  84. ab $50000, hier würde nach danach ein CLR.B $5001c stehen, also die  reale
  85. Adresse  von  Wert1:  , aber bestimmt nicht CLR.B Wert1, da Wert1: nur ein
  86. Name ist, den der Programmierer dem dc.b $30 gegeben hat. Hier  wird  auch
  87. die  Nützlichkeit  der  Labels  klar,  man  stelle  sich  vor, ein Listing
  88. schreiben zu müssen, bei dem  immer  die  numerischen  Adressen  angegeben
  89. werden  müssen; abgesehen  von der Unbequemlichkeit, wenn man eine Routine
  90. inmitten  der  anderen  einfügt,  müßten  alle  Adressen  neu  geschrieben
  91. werden...  Um zu sehen, auf welche Adressen die Labels gelegt werden, kann
  92. man den Befehl "D" des ASMONE verwenden: z.B. nach  dem  Assemblieren  von
  93. Listing1a.s  kann  man  ein  "D Waitmouse" durchführen, und ihr werdet den
  94. disassemblierten Speicher ab Waitmouse erhalten,  und  im  Listing  werden
  95. nicht die Labels, sondern die reellen Adressen aufscheinen.
  96.  
  97. Ihr werdet bemerken, daß  in  den  Beispielprogrammen  niemals  numerische
  98. Adressen  auftreten,  aber  immer  nur  Labels. Einzige Ausnahmen sind die
  99. Spezialadressen wie $dffxxx oder $bfexxx. Im letzten Beispiel habe ich ein
  100. dc.b verwendet. Dieser Befehl hat die Aufgabe, bestimmte Bytes einzufügen;
  101. z.B. um $12345678 an einem bestimmten  Punkt  des  Programmes  einzufügen,
  102. verwende  ich  ein  DC,  das  in drei Formen auftreten kann: .B (Byte), .W
  103. (Word), .L (Long):
  104.  
  105.     dc.b    $12,$34,$56,$78 ; in bytes
  106.  
  107.     dc.w    $1234,$5678    ; in words
  108.  
  109.     dc.l    $12345678    ; in longwords 
  110.  
  111. Diesen Befehl verwendet man auch, um Sätze in den Speicher zu  geben,  wie
  112. etwa  den  Text,  der am Bildschirm ausgegeben werden soll, wenn z.B. eine
  113. Routine PRINT aufgerufen wird, die das  ausdruckt,  was  unter  dem  Label
  114. TEXT: steht:
  115.  
  116. TEXT:
  117.     dc.b    "Viele schöne Grüße"
  118.  
  119. oder
  120.     dc.b    'Viele schöne Grüße'
  121.  
  122.     dc.b    "Viele schöne Grüße",0
  123.  
  124. Erinnert euch, den Text unter Gänsefüßchen  zu  setzen  und  das  dc.b  zu
  125. verwenden,  nicht  ein  dc.w oder dc.l!! Die Charakter sind ein Byte groß,
  126. und sie entsprechen einem bestimmten Byte: probiert ?"a"  einzugeben,  und
  127. ihr  werdet  merken,  daß es $61 entpricht. Daraus folgt, daß dc.b "a" das
  128. gleiche ist wie  dc.b  $61.  Achtung  aber,  Großbuchstaben  haben  andere
  129. Werte!!  Ein  "A"  hingegen ist $41. Die häufigste Verwendung des dc.b ist
  130. aber jener, Bytes, Words oder noch größere Speicherzonen zu definieren, in
  131. denen unsere Daten festgehalten werden. Wenn man zum Beispiel ein Programm
  132. schreiben möchte, das zählt, wie oft eine Taste gedrückt wird,  müßte  man
  133. ein   Label  definieren, gefolgt von einem - auf NULL gesetzem - Byte, und
  134. jedesmal, wenn ich nun die Taste drücke, 1 dazuzählen. Dafür verwendet man
  135. ein  ADD,  gefolgt  von  dem Label, somit wird das Byte unter dem Label um
  136. eins raufgezählt. Zum Schluß braucht man nur noch den Wert auszulesen:
  137.  
  138.     ; Wenn die Tasate gedrückt wurde, dann ADDQ.B #1,ANZAHL, also
  139.     ; zähle ein Byte unter dem Label ANZAHL dazu.
  140.  
  141. ANZAHL:
  142.     dc.b    0
  143.  
  144. Am Ende des Progammes wird der anfängliche Nuller nicht  mehr  existieren,
  145. statt  dessen  wird die Anzahl der Tastendrucke darinstehen. Ein ähnliches
  146. Beispiel  ist  in  Listing2a.s  enthalten,   es   ist   auch   ausführlich
  147. dokumentiert.  Ich  rate euch, es in einen anderen Textbuffer zu laden: um
  148. einen anderen Buffer auszuwählen müßt ihr nur mit einer F-Taste auswählen,
  149. F1 bis F10. Wenn dieser Text z.B. in Buffer 1 ist, dann drückt F2, und ihr
  150. seid im 2. Um das Listing zu laden, tippt "R" in der Kommandozeile. Danach
  151. Listing2b.s  in  Buffer  3  usw. So habt ihr alles sofort zur Hand. Es ist
  152. aber besser, die LEKTION.TXT zu folgen und dann Schritt  für  Schritt  die
  153. einzelnen   Listings  reinzuholen  und  zu  testen.  Danch  kehrt  ihr  zu
  154. LEKTION.TXT zurück, fahrt fort bis  zum  nächsten  angedeuteten  Beispiel,
  155. ladet  dieses, führt aus... Das ist, glaube ich, die beste Art, zu lernen:
  156. man macht ein bißchen Theorie und verifiziert das gelernte nebenbei.
  157.  
  158. Habt ihr Listing2a.s verstanden?
  159.  
  160. Habt ihr die Wichtigkeit von Byte, Word  und  Longword  bemerkt?  Was  die
  161. Binärzahlen  betrifft,  um Bits zu zählen beginnt man rechts und geht nach
  162. links, also "umgekehrt", und man startet bei 0, nicht bei 1. Ein Byte (das
  163. aus 8 Bit besteht) beginnt bei 0 und geht bis 7. Z.B. diese Zahl:
  164.  
  165.     %000100010000
  166.  
  167. Bit 4 und Bit 8 sind "angeschaltet". Um euch ein bißchen unter die Arme zu
  168. greifen, könnt ihr sie auch numerieren:
  169.  
  170.          ;5432109876543210    <- intelligente Anwendung des ;
  171.     move.w  #%0011000000100100,$dffxxx
  172.  
  173. Hier sind Bit 2,5,12 und 13 der WORD angeknipst. Nochmal zum mitschreiben:
  174. ein Byte hat 8 Bit, ein Word 16 (von 0 bis 15), ein Longword 32 (von 0 bis
  175. 31).
  176.  
  177. In der Anweisung
  178.  
  179.     BTST #6,$bfe001
  180.  
  181. wird kontrolliert, ob Bit 6 des Byte $bfe001 NULL ist. Wenn es
  182.  
  183.     ;76543210
  184.     %01000000
  185.  
  186. wäre, dann ist Bit 6 = 1, also ist der Mausknopf nicht gedrückt!!
  187.  
  188. Nochmals, ein BYTE hat 8 BIT: um sie einzeln angeben zu  können,  ist  das
  189. Bit  ganz  rechts  das Bit 0, auch NIEDERWERTIGSTES BIT genannt, oder LSB,
  190. aus dem Englischen (Least Significant Bit),  während  Bit  Nr.  7  das  am
  191. weitesten  links  ist,  und  HÖCHSTWERTIGSTES  heißt (MSB Most Significant
  192. Bit). Am höchstwertigsten deshalb, weil es am meißten zählt,  genauso  wie
  193. beim 1000-DM Schein, bei dem der Einser ganz links um so mehr zählt, desto
  194. weiter links er ist und umsomehr Nullen rechts von ihm sind. Ein Byte kann
  195. als höchsten Wert 255 annehmen, also %11111111.
  196.  
  197. Ein  WORD hingegen besteht aus 16 Bit, praktisch aus zwei Byte, und analog
  198. zum Byte startet man rechts mit dem Bit 0, immer dem niederwertigsten, und
  199. endet  ganz  links,  bei  Bit 15, dem höchstwertigsten. Ein Word kommt bis
  200. maximal 65536.
  201.  
  202. Ein  Longword  ist  aus  32  Bit  zusammengesetzt, von 0 bis 31, das sind,
  203. Wunder  Wunder,  4  Bytes,  oder,   noch   größeres   Wunder,   2   Words,
  204. zusammengeklebt. Maximun ist hier 4294967299 ( 4 Milliarden!!!).
  205.  
  206. Nun geht´s weiter mit den verschiedenen Adressierungsarten: wie wir sahen,
  207. haben  wir  mit  CLR.W $100 die Speicherplätze $100 und $101 gelöscht, auf
  208. NULL gesetzt, also ein Word beginnend bei $100 (da ein Word  aus  2  Bytes
  209. besteht,  und  der Speicher in Bytes aufgeteilt ist, killen wir 2 Bytes!).
  210. Auf die gleiche Art kopiert ein Move.B $100,$200 den Inhalt von Zelle $100
  211. in Zelle $200. Das kann man auch durch Labels erledigen, ohne die Adressen
  212. spezifizieren zu müssen: MOVE.B LABEL1,LABEL2; also kopiere das Byte unter
  213. LABEL1 nach LABEL2. Es gibt auch Kombinationen davon, so kann ich auch ein
  214. MOVE.B #$50000,LABEL2 machen, das mir einen FIXEN Wert in LABEL2 schreibt.
  215. Wenn  z.B.  LABEL2  auf  Adresse  $60000  steht, dann bewegen wir dem Wert
  216. $00050000 nach $60000. Mit einem M $60000 erhalten wir 00 05 00  00.  Wenn
  217. das Symbol des Lattenzaunes (#) vor einer Zahl oder einem Label auftaucht,
  218. dann bedeutet das, daß diese  Zahl  den  Wert  darstellt,  und  nicht  die
  219. Adresse,  an  der  er  liegt, wie es vorkommt, wenn KEIN # davorsteht. Ein
  220. Beispiel:
  221.  
  222. 1)    MOVE.L    $50000,$60000    ; die Werte in den Speicherzellen
  223.                 ; $50000, $50001, $50002 und $50003
  224.                 ; werden in die Zellen $60000,
  225.                 ; $60001, $60002 und $60003 kopiert.
  226.  
  227. 2)    MOVE.L  #$50000,$60000    ; Diesmal wird in $60000 der Wert nach
  228.                 ; dem # gellegt, also $50000. Zu beachten,
  229.                 ; daß hier $50000 als Adresse absolut
  230.                 ; nichts zu tun hat, die einzige Adresse,
  231.                 ; die vorkommt, ist $60000.
  232.  
  233. Wenn Label verwendet werden, ändert sich nichts:
  234.  
  235. 1)    MOVE.L    HUND,KATZE    ; Der Inhalt der Longword HUND, also
  236.                 ; $00123456 wird in die Longword
  237.                 ; KATZE kopiert ($123456 ist das Erste,
  238.                 ; das unter dem Label HUND steht).
  239.  
  240. HUND:
  241.     dc.l    $123456
  242.  
  243. KATZE:
  244.     dc.l    0
  245.  
  246. Nach ausführen des Programmes:
  247.  
  248. HUND:
  249.     dc.l    $123456
  250.  
  251. KATZE:
  252.     dc.l    $123456
  253.  
  254. 2)    MOVE.L    #HUND,KATZE    ; Diesmal wird die Adresse des Label
  255.                 ; HUND in das Label KATZE kopiert.
  256.  
  257. Vor der Ausführung:    ; Nehmen wir an, daß das Label HUND: auf Adresse
  258.             ; $34500 steht, wenn man also ein M HUND macht,
  259.             ; wird man ein 00034500 00 12 34 56 00 00 00 ...
  260.             ; erhalten.
  261.  
  262. HUND:
  263.     dc.l    $123456
  264. KATZE:
  265.     dc.l    0
  266.  
  267. Nach der Ausführung:
  268.  
  269. HUND:
  270.     dc.l    $123456
  271. KATZE:
  272.     dc.l    34500    ; also wo sich das LABEL im Speicher befindet.
  273.  
  274. Zu beachten ist, daß wenn versucht worden  wäre,  ein  MOVE.W  #HUND,KATZE
  275. oder  ein  MOVE.B  #HUND,KATZE zu tun, der Assembler einen Fehler gemeldet
  276. hätte, da ADRESSEN IMMER ein LONGWORD groß sind. Im Speicher ist an Stelle
  277. eines  MOVE.L  #LABEL,LABEL  immer  ein  Befehl  wie MOVE.L #$12345,$12345
  278. vorzufinden, der Assembler verwandelt das Label  in  ihre  reale  Adresse.
  279. Listing2b.s veranschaulicht das.
  280.  
  281. Nun  wenden  wir  uns an die anderen Adressierungsarten mit Registern (sie
  282. sind schwieriger); wie ich schon andedeutet hatte, gibt es 8 Datenregister
  283. und  8  Adressregister, jeweils D0, D1, D2, D3, D4, D5, D6, D7 und a0, a1,
  284. a2, a3, a4, a5, a6 und a7. Das Adressregister a7 wird auch STACK  POINTER,
  285. oder SP, genannt, wir werden es später näher behandeln. Im Moment lasst es
  286. bitte stehen, verwendet nur jene bis a6.  Diese  Adressen  sind  alle  ein
  287. Longword groß, es sind sowas wie kleine Speicher im 68000er, die als Folge
  288. davon, hurtig schnell sind. Mittels Registern kann man viel anstellen, und
  289. deswegen gibt es eine eigene Syntax dafür. Als erstes vorweg: man kann mit
  290. den A-Registern keine Byte-Arbeiten verrichten. Ein MOVE.B  LABEL,A0  gibt
  291. einen  Fehler.  Mit  den  Adress-Registern  kann  man  also als .W oder .L
  292. arbeiten. Die Datenregister sind da flexibler: sie erlauben  .B,  .W  oder
  293. .L.   Die   A-Register   sind,  wie  man  bemerkt,  für  Adressoperationen
  294. prädestiniert, und deswegen gibt es auch eigene Befehle  dafür,  wie  z.B.
  295. LEA,  was  sovie  wie "LOAD ENTIRE ADRESS" bedeutet, also lade die gesamte
  296. Adresse ins Register. LEA steht alleine, ohne .b oder .w, denn  es  könnte
  297. nur  .L geben, es sind ja Adressen, deswegen wird es weggelassen. Um einen
  298. Wert in ein Adressregister zu laden gibt es zwei Methoden:
  299.  
  300. 1)    MOVE.L    #$50000,A0    ; (oder MOVE.L #LABEL,a0)
  301.  
  302. 2)    LEA    $50000,A0    ; (oder LEA LABEL,A0)
  303.  
  304. Wenn man die erste Methode auf Daten- wie auf Adressregister anwenden kann
  305. (Beispiel:   move.l   #$50000,d0   -   move.l   #$50000,LABEL   -   move.l
  306. #$LABEL,LABEL...), so ist die zweite auf Adressen beschränkt.
  307.  
  308. P.S: Ob man nun move.l #$50000,d0 oder  MOVE.L  #$50000,D0  schreibt,  ist
  309. egal,  auch  MoVe.L  #$50000,d0  ist  identisch,  das Resultat ändert sich
  310. nicht, nur ästhetisch  ist  eine  Version  der  anderen  zu  bevorzugen...
  311. Anderes  gilt für das Label: zwar können sie in einem Punkt des Programmes
  312. groß geschrieben sein, weiter später klein, etc.  aber  nur,  weil  es  im
  313. Preferences  des  TRASH´M-ONE  so  eingetragen  ist.  Diese Option ist die
  314. "UCase = LCase" im Menü "Assembler/Assemble..", was  soviel  bedeutet  wie
  315. "Upper  case  =  Lower  case", praktisch "Groß ist gleich Klein". Wenn ihr
  316. diese Option ausschaltet, dann  wird  bei  der  Erkennung  des  Label  auf
  317. Groß-Kleinschreibung geachtet, HUND: wird also etwas anderes wie Hund oder
  318. HuNd sein...
  319.  
  320. Die zweite Methode kann  nur  auf  Adressregister  angewandt  werden,  und
  321. intuitiv  kann  man  sich  denken,  daß  das die schnellere Art sein wird:
  322. richtig. Erinnert euch deshalb, daß wenn ihr eine Adresse in ein  Register
  323. wie  a0,  a1...  geben müßt, ihr den Befehl LEA verwenden solltet, gefolgt
  324. von der Adresse OHNE vorangehendem  Lattenzaun  (#)  und  dem  anvisierten
  325. Register. Schaut die folgenden zwei Beispiele genau an:
  326.  
  327. 1)    MOVE.L    $50000,a0    ; gib in a0 den ab Adresse $50000
  328.                 ; ($50000 + $50001 + $50002 + $50003,
  329.                 ; ein Longword = 4 Byte!) enthaltenen
  330.                 ; Wert
  331.  
  332. 2)    LEA    $50000,a0    ; gib die Zahl $50000 in a0
  333.  
  334. Passt also gut auf, wenn ihr mit MOVE mit und ohne "#" arbeitet, vor allem
  335. am  Anfang  wird  es  oft  passieren,  daß  ihr die Adresse mit einem Wert
  336. verwechselt, weil das # fehlt, oder umgekehrt.  Den  Unterschied  versucht
  337. auch Listing2c.s nochmals zu vertiefen.
  338.  
  339. Mit  den Adressregistern sind verschiedene Arten der Adressierung möglich:
  340. Zu Beginn analysieren wir diese zwei Instruktionen:
  341.  
  342.     move.l    a0,d0    ; gib die in a0 enthaltene Zahl ins Register d0
  343.     move.l    (a0),d0    ; gib das Longword, das an Adresse a0 zu finden
  344.             ; ist, in d0
  345.  
  346. Die Adressierung mit den Klammern nennt man INDIREKT, da nicht der Wert in
  347. a0 kopiert wird (Direkt...), sondern der, der an der Adresse steht, die in
  348. a0 enthalten ist. Ein praktische sBeispiel ist in Listing2d.s zu finden.
  349.  
  350. Durch Verwendung der indirekten Adressierung kann  man  auf  die  Register
  351. INDIREKT  zugreifen,  z.B. indem die Adresse der Maustaste und der Farbe 0
  352. in  die  Register  gegeben  wird,  kann  man  das  Listing  von   Lektion1
  353. neuschreiben. Das wurde in Listing2e.s gemacht.
  354.  
  355. Machen  wir  die  letzten  Beispiele  zur indirekten Adressierung, um noch
  356. eventuelle Zweifel zu beheben:
  357.  
  358.     move.l    a0,d0    ; kopiert den Wert in a0 ins Register d0 
  359.     move.b    (a0),d0    ; kopiert das Byte, das an Adresse a0 steht, 
  360.             ; in das Register d0 move.w
  361.     (a0),(a1)    ; kopiert das Word, das an Adresse a0 steht,
  362.             ; in die Adresse, die in a1 angegeben ist
  363.             ; (und in die folgende, denn ein Word besteht
  364.             ; ja aus zwei Bytes, also zwei Adressen !)
  365.     clr.w    (a3)    ; Löscht das Word (die zwei Bytes), ab der
  366.             ; Adresse a3, also Adresse a3 und Adresse a3+1
  367.     clr.l    (a3)    ; Wie oben, nur werden Adresse a3, a3+1,
  368.             ; a3+2 und a3+3 auf NULL gesetzt.
  369.     move.l    d0,(a5)    ; der Wert in d0 wird in die Adresse kopiert,
  370.             ; die in a5 steht. D.h. an Adresse a5 und die
  371.             ; drei folgenden wird der Inhalt von d0
  372.             ; geschrieben, insgesamt 4 Bytes, LongWord.
  373.  
  374. Also nochmal, bitte beseitigt alle Zweifel,  die  die  Adressierungsarten,
  375. die bis hierher behandelt wurden, betreffen! Eventuell schaut euch nochmal
  376. die Listings bis Listing2e.s durch, da  die  folgenden  Adressierungsarten
  377. auf die bisherigen aufbauen.
  378.  
  379. Jetzt  ist´s  wieder  mal  Zeit  für  eine  Kundgebung:  das hier wird der
  380. abstrakteste Teil von Lektion2, denn hier werden  die  letzten  Arten  der
  381. Adressierung  aufgezählt,  aber  ich  versichere  euch,  schon ab Lektion3
  382. werdet ihr das angeeignete Wissen einsetzen, und  ihr  werdet  die  ersten
  383. Videoeffekte  mit dem Copper starten!! Also, dieser Teil überstanden, wird
  384. alles sehr viel praktischer: jeder Erklärung wird ein neuer  Spezialeffekt
  385. oder  eine  hypergeile  Farbe zugeordnet sein. Also, haut jetzt noch rein,
  386. und gebt nicht auf, weil es langweilig erscheint, denn ich selbst hatte an
  387. diesem  Punkt  alles  Fallen  gelassen,  als  ich  das erste Mal Assembler
  388. lernte, genauso, weil  auch  ich  total  durcheinander  war  von  all  den
  389. Befehlen und Klammern, die dann alles so ins Chaos stürzten. Aber wenn ihr
  390. mal die Befehle gelernt habt, dann werdet ihr abzischen wie  eine  Rakete,
  391. und  von selbst weiterlernen, indem ihr Listings von hier und da durchlest
  392. und durchstrebt. Die Schritte werden immer größer werden, genauso wie  die
  393. Regeln  bei einer Sprotart lernen: jemand, der das Set der 68000er Befehle
  394. nicht kennt ist gleich mit einem, der z.B. die Regeln beim  Fußball  nicht
  395. kennt:  wenn  er  einem  Spiel zusieht (Listing...), wird er sich wundern,
  396. wieso die Verrückten alle auf den Ball reindreschen, und er wird  sich  zu
  397. Tode   langweilen,   aber   wenn  er  weiß,  wie  die  Spielregeln  lauten
  398. (Adressierung etc), wird er die Fasen im Spiel verstehen  und  die  Tricks
  399. kapieren (z.B. die Programmiertricks bei den Grafikregistern).
  400.  
  401. Schauen wir uns zwei weitere Adressierungsarten an:
  402.  
  403.     lea    OPA,a0     ; gibt in a0 die Adresse von OPA:
  404.     MOVE.L    (a0)+,d0 ; gibt in d0 den .L-Wert (Long), der
  405.              ; an der Adresse a0 enthalten ist, also
  406.              ; $3231020 ( genau das gleiche wie ein
  407.              ; Move.L (a0),d0 )
  408.              ; DANACH aber SUMMIERE 4 ZUM WERT IN a0 DAZU
  409.              ; "POST-INKREMENTAL"
  410.              ; praktisch "zeigen" wir jetzt auf das folgende
  411.              ; Longword im Speicher
  412.              ; Wenn es ein move.w (a0)+,d0 gewesen wäre, dann
  413.              ; würde danach zum a0 nur 2 dazugezählt (ein Word=2)
  414.              ; bei einem Move.b nur eins (ein Byte...)
  415.     MOVE.L    (a0)+,d1 ; das Gleiche: kopiert in d1 den Long-Wert,
  416.              ; der in der Adresse a0 enthalten ist, die
  417.              ; nun die Adresse OPA + ein Longword ist, also
  418.              ; OPA + 4, -> $13478
  419.     rts         ; RAUS!
  420.  
  421. OPA:
  422.     dc.l    $3231020,$13478
  423.  
  424.     END
  425.  
  426. Wir können diesen Typ der Adressierung folgendermaßen übersetzen:
  427.  
  428. 1)    Move.L    (a0)+,LABEL
  429.  
  430. ist äquivalent mit:
  431.  
  432. 1b)    Move.L    (a0),LABEL    ; kopiert ein Longword von der Adresse a0
  433.                 ; in das LABEL.
  434.     ADDQ.W    #4,a0        ; zählt zu a0 4 dazu (.L=4)
  435.                 ; Bemerkung: wenn eine Zahl kleiner als
  436.                 ; 9 dazugezählt wird, verwendet man den
  437.                 ; Befehl ADDQ, weil er für kleine Zahlen
  438.                 ; zurechtgeschnitten ist und daher schneller!
  439.                 ; Weiters, wenn zu Adressregister eine Zahl
  440.                 ; kleiner als $FFFF (Word) summiert wird, kann
  441.                 ; ein .W an Stelle des .L verwendet werden, es
  442.                 ; wird trotzdem immer auf das gesamte Longword
  443.                 ; zugegriffen.
  444.  
  445. Das Selbe:
  446.  
  447. 2)    MOVE.W    (a0)+,LABEL
  448.  
  449. Bedeutet soviel wie:
  450.  
  451. 2b)    Move.W    (a0),LABEL    ; kopiert ein Word von Adresse a0 ins LABEL
  452.     ADDQ    #2,a0        ;summiert 2 zu a0 (.W = 2 Bytes)
  453.  
  454. Nochmal das Geliche:
  455.  
  456. 3)    MOVE.B    (a0)+,LABEL
  457.  
  458. Ist gleich dem:
  459.  
  460. 3b)    MOVE.B    (a0),LABEL    ; kopiert das Byte an der Adresse a0 ins LABEL
  461.     ADDQ    #1,a0        ; Zählt 1 zu a0 dazu (.B = 1 Byte)
  462.  
  463. Also, zusammenfassend kann man sagen, daß die indirekte  Adressierung  mit
  464. Post-Inlkrementierung  mehr  oder  weniger  mit einem Fließband verglichen
  465. werden  kann,  bei  dem  der  Arbeiter  zuerst  die  Arbeit  am  Werkstück
  466. verrichtet  (MOVE),  und  jedesmal,  wenn er fertig ist, das Fließband mit
  467. einem Pedal (dem +) nach vorne weiterfahren läßt (die Adresse, auf die  a0
  468. zeigt). Ein Beispiel anhand einer Schleife wird vielleicht klarer wirken:
  469.  
  470. Anfang:
  471.     lea    $60000,a0    ; Hier beginnt der Putztrupp
  472.     lea    $62000,a1    ; und hier endet er
  473.  
  474. CLELOOP:
  475.     clr.l    (a0)+    ; löscht (setzt auf 0) ein Long an der Adresse, die
  476.             ; in a0 steht, und erhöhe a0 um ein long, also
  477.             ; um 4 Byte, anders ausgedrückt, lösche ein Long
  478.             ; und geh zum Nächsten weiter
  479.     cmp.l    a0,a1    ; ist a0 bei $62000 angekommen; oder: ist a0 gleich
  480.             ; a1?
  481.     bne.s    CLELOOP    ; wenn nicht, mache einen weiteren Durchgang, bei
  482.             ; CLELOOP startend.
  483.     rts
  484.  
  485. Wie man sieht, "putzt" dieses Programm den Speicher von Adresse $60000 bis
  486. $62000, indem es ein CLR (a0)+ verwendet, das wiederholt wird, bis wir
  487. nicht zur gewünschten Adresse angekommen sind. Ein ähnliches Beispiel
  488. ist Listing2f.s.
  489.  
  490. Nun kommen wir zur Adressierung mittels Pre-Decrement, also das Gegenteil
  491. von dem, das ich gerade beschrieben habe, denn anstatt die Adresse im
  492. Register zu erhöhen, nachdem die Operation durchgeführt wurde, wird hier
  493. zuerst dekrementiert (abgezogen), und DANN kommt die Operation. Beispiel:
  494.  
  495.     lea    OPA,a0        ; gibt in a0 die Adresse von OPA:
  496.     MOVE.L    -(a0),d0    ; a0 wird dekrementiert, also um 4 herunter
  497.                 ; gezählt (in diesem Fall um 4, da es sich
  498.                 ; um ein .L handelt, bei einem .W=2, .B=1)
  499.                 ; danach wird in d0 der Wert kopiert, der
  500.                 ; sich an der nun entstandenen Adresse
  501.                 ; befindet, also OPA-4, => $12345678
  502.     rts            ; im Register bleibt nun der anfängliche
  503.                 ; Wert - 4. Bei einem move.w -(a0),d0 würde
  504.                 ; vorher dem a0 2 abgezogen (-> .W!!), DANACH
  505.  
  506.     dc.l    $12345678  ; das Move auf der errechneten Adresse ausgeführt,
  507. OPA:               ; bei einem .B würde dem a0 eins abgezogen, a0
  508.     dc.l    $ffff0f0f  ; würde nun auf die vorhergehende Adresse "zeigen"
  509.  
  510. Wir können diesen Typ von Adressierung mit zwei geteilten Operationen
  511. gleichsetzen:
  512.  
  513. 1)    MOVE.L    -(a0),LABEL
  514.  
  515. entspricht:
  516.  
  517. 1b)    SUBQ.W    #4,a0        ; Ziehe 4 von a0 ab (= .L)
  518.                 ; wichtig: wenn Zahlen kleiner als 9
  519.                 ; abgezogen werden, sollte das SUBQ dem
  520.                 ; SUB vorgezogen werden, es ist schneller
  521.     MOVE.L    (a0),LABEL    ; kopiert den Wert, der an Adresse a0 steht,
  522.                 ; in das LABEL ( an seine Adresse... )
  523. Dementsprechend:
  524.  
  525. 2)    MOVE.W    -(a0),LABEL
  526.  
  527. ist gleich mit:
  528.  
  529. 2b)    SUBQ.W    #2,a0        ; subtrahiere 2 von a0 (.W=2)
  530.     MOVE.W    (a0),LABEL    ; kopiert das Word, das sich an  Adresse a0
  531.                 ; befindet, in das LABEL
  532. Und das Letzte:
  533.  
  534. 3)    MOVE.B    -(a0),LABEL
  535.  
  536. heißt soviel wie:
  537.  
  538. 3b)    SUBQ.W    #1,a0        ; Zieht vom Wert in a0 eins ab (.B=1)
  539.     MOVE.B    (a0),LABEL    ; Kopiert das Byte an Adresse a0 ins LABEL
  540.  
  541. Zusammenfassend,  mit dem Beispiel des Fließbandarbeiters von vorhin, kann
  542. man sich vorstellen, daß die Adressierung mit Pre-Decrement  so  aussieht:
  543. Zuerst  verschiebt  er  das Fließband rückwärts (a0) mit seinem Pedal (-),
  544. DANN   erfolgt   das   MOVE   oder   die   gewünschte  Operation.   Ein
  545. Schleifenbeispiel:
  546.  
  547. Anfang:
  548.     lea    $62000    ; hier beginne ich
  549.     lea    $60000    ; und hier ende ich
  550. CLELOOP:
  551.     clr.l    -(a0)    ; verringere a0 um ein Long (4 Byte) und
  552.             ; lösche dann die daraus resultierende
  553.             ; Speicherzelle, oder anders: geh´ zum
  554.             ; vorherigen Long und lösche es
  555.     cmp.l    a0,a1    ; ist a0 bei $60000 angekommen, ist also a0=a1?
  556.     bne.s    CLELOOP    ; wenn nicht, wiederhole alles ab CLEOLOOP
  557.     rts
  558.  
  559. Wie man sieht, "putzt" dieses Programm den Speicher von der Adresse $62000
  560. bis  zu Adresse $60000, indem es ein clr -(a0) verwendet, bis es nicht zur
  561. gewünschten Adresse heruntergekommen ist (Eben rückwärts, bei einem  (a0)+
  562. begannen  wir  bei  $60000  und kamen in 4er Schritten bis $62000, mit dem
  563. -(a0) hingegen beginnen wir bei $62000 und  zählen  in  4er-Schritten  bis
  564. $62000 herunter).
  565. Schaut  euch  Listing2g.s  und  Listing2h.s  an,  um  die   letzten   zwei
  566. Adressierungen besser zu verstehn.
  567.  
  568. Jetzt lernen wir, wie man die Adressierungsdistanz verwendet:  ein  MOVE.L
  569. $100(a0),d0  kopiert  den Inhalt, der in Adresse a0+$100 enthalten ist, in
  570. d0. Wenn z.B. a0  die  Adresse  $60200  enthält,  dann  kommt  in  d0  das
  571. Longword, das ab Speicherzelle $60300 enthalten ist ($60200+$100). Auf die
  572. gleiche Art und Weise funktioniert ein MOVE.L -$100(a0),d0, in d0 wird der
  573. Wert  von a0-$100 landen, also das Long ab Adresse $60100. Zu beachten ist
  574. aber, daß sich der Wert  in  a0  NICHT  ändert,  der  Prozessor  berechnet
  575. jedesmal  die  Adresse neu, auf der es zu arbeiten gilt, indem er den Wert
  576. vor den Klammern zur  Adresse  in  den  Klammern  summiert.  Die  maximale
  577. "Distanz"  (oder  Offset), die erreicht werden kann, liegt zwischen -32768
  578. und 32768 (-$7FFF, $8000). Ein Beispiel dafür gibt´s in Listing2i.s.
  579.  
  580. Die letzte Adressierungsart ist die folgende:
  581.  
  582.     MOVE.L    50(a0,d0),Label
  583.  
  584. Diese Art hat sowohl eine Adressierungsdistanz (den 50er), wie auch  einen
  585. INDEX  (das  d0):  die  Distanz  und  und  der  Inhalt  von d0 werden alle
  586. summiert, um die Adresse zu  definieren,  von  der  kopiert  werden  soll.
  587. Praktisch gesehen ist es das gleiche wie die Adressierung mit Distanz, nur
  588. daß hier auch noch der Inhalt des Registers d0 hinzugefügt  wird,  das  in
  589. diesem  Fall  aber  von  minimal -128 bis maximal +128 geht. Ich will euch
  590. nicht mit weiteren Beispielen  über  diese  Adressierung  langweilen,  ihr
  591. werdet  damit  besser  vertraut  werden,  wenn  sie später in den Listings
  592. vorkommen.
  593.  
  594. Um die LEKTION2 abzuschließen, die, wenn ihr sie gut durchgekaut hat, euch
  595. in  die  Lage  versetzt,  ASM-Listings  zu  lesen und zu verstehen, ist es
  596. unerläßlich, den DBRA-Zyklus zu erklären.  Er  wird  sehr  oft  verwendet:
  597. durch  Verwenden  eines  Datenregisters  kann  man  gewisse Befehle öfters
  598. ausführen lassen, es reicht, in das Register (d0, d1,...) die Anzahl-1  zu
  599. geben. Z.B. kann die Routine, die den Speicher mit dem CLR.L (a0)+ löschr,
  600. so modifiziert werden, daß sie mit einem DBRA-Loop  funktioniert  und  den
  601. Putzzyklus so oft aufruft, wie wir es wünschen:
  602.  
  603. Anfang:
  604.     lea    $60000,A0    ; Anfang
  605.     move.l    #($2000/4)-1,d0    ; Gib in d0 die Anzahl der notwendigen
  606.                 ; Durchgänge, um $2000 Bytes zu löschen:
  607.                 ; $2000/4 (also dividiert durch 4, da jedes
  608.                 ; CLR.L 4 Bytes löscht, eben ein Long). Alles
  609.                 ; minus 1, weil der Loop im Endeffekt einmal
  610.                 ; mehr ausgeführt wird.
  611. CLEARLOOP:
  612.     CLR.L    (a0)+
  613.     DBRA    d0,CLEARLOOP
  614.     rts
  615.  
  616. Diese Routine löscht den Speicher von $60000 bis $62000, genauso  wie  das
  617. Beispiel  von vorher, das das CMP verwendete, um a0 mit a1 zu vergleichen,
  618. um zu sehen, ob wir angekommen sind, wo wir wollten. In diesem  Fall  wird
  619. das CLR 2047 mal ausgeführt, probiert mal, in der Kommandozeile des ASMONE
  620. ?($2000/4)-1 zu tippen. Das DBRA funktioniert  folgendermaßen:  Das  erste
  621. Mal  kommt in d0 z.B. der Wert 2047, das CLR wird ausgeführt, und dann, am
  622. DBRA angekommen, wird d0 um eins verringert, der Prozessor springt zum CLR
  623. zurück.  Das wiederholt sich solange, bis d0 "verbraucht" ist, bis es also
  624. NULL enthält. Es muß die  Anzahl  der  Durchgänge  minus  eins  eingegeben
  625. werden, weil beim ersten Durchgang das d0 nicht dekrementiert wird.
  626.  
  627. Als  letztes  Beispiel  studiert euch Listing2l.s, das Subroutinen mit BSR
  628. aufruft und DBRA-Schleifen in Action zeigt. Es wird nützlich sein, um  die
  629. Struktur komplexerer Programme zu verstehen.
  630.  
  631. Zum  Abschluß  möchte  ich  euch noch auf den Unterschied zwischen BSR und
  632. BEQ/BNE hinweisen: im Falle  von  BSR  Label  springt  der  Prozessor  zur
  633. Routine,  die  bei Label liegt, und verharrt darin, bis er ein RTS findet,
  634. das ihn veranlasst, zur Instruktion direkt unter dem  BSR  zurückzukehren.
  635. Man  kann  also  sagen,  es  wird  eine UNTERROUTINE ausgeführt, d.h. eine
  636. Routine, die in Mitten einer anderen aufgerufen wird:
  637.  
  638. Hauptprogramm:
  639.     move.l    ding1,d0
  640.     move.l  ding2,d1
  641.  
  642.     bsr.s    Restposten
  643.  
  644.     move.l    ding3,d2
  645.     move.l    ding4,d3
  646.  
  647.     rts    ; ENDE DES HAUPTPROGRAMMES, DER HAUPTROUTINE
  648.         ; ZURÜCK ZUM ASMONE
  649.  
  650. Restposten:
  651.     move.l  nixwert,d4
  652.     move.l  nixwert2,d5
  653.  
  654.     rts    ; ENDE DER UNTERROUTINE, KEHRE ZU "move.l ding3.d2"
  655.         ; ZURÜCK, ALSO UNTER DAS "bsr.s Restposten"
  656.  
  657. Im Falle einer BNE/BEQ - Verzweigung hingegen wird entweder ein Weg oder
  658. der andere eingeschlagen:
  659.  
  660. Hauptprogramm:
  661.     move.l  ding1,d0
  662.     move.l  ding2,a0
  663.  
  664.     cmp.b    d0,a0
  665.     bne.s    weg2
  666.  
  667.     move.l    ding3,d1
  668.  
  669.     cmp.b    d1,a0
  670.     beq.s    weg3
  671.  
  672.     move.l    ding4,d0
  673.  
  674.     rts    ; ENDE DER HAUPTROUTINE , ZURÜCK ZUM ASMONE
  675.  
  676. weg2:
  677.     move.l  nixwert,d5
  678.     move.l  nixwert2,d6
  679.  
  680.     rts    ; ENDE DER ROUTINE; ZURÜCK ZUM ASMONE, NICHT unter das bne!!!
  681.         ; Hier haben wir diesen Weg ausgesucht, und wenn ein RTS
  682.         ; auftaucht, geht´s zurück zum ASMONE!!!
  683.  
  684. weg3:
  685.     move.l  nixwert3,d1
  686.     move.l  nixwert4,d2
  687.  
  688.     rts    ; ENDE DER ROUTINE; ZURÜCK ZUM ASMONE, NICHT unter das beq!!!
  689.         ; Auch hier wurde dieser Weg gewählt, und nach dem RTS
  690.         ; geht´s zurück zum ASMONE!!!
  691.  
  692. Das gleiche gilt für das BRA Label, das  soviel  bedeutet  wie  SPRING  ZU
  693. Label,  äquivalent  zu JMP, es ist wie ein Zug, der zu einer Weiche kommt,
  694. der kommt auch nicht zurück, wenn  das  Gleis  fertig  ist!  Am  Ende  des
  695. Gleises  angekommen ist Schluß, kein beamen wie bei Raumschiff Enterprise,
  696. das uns zurück bringt.
  697.  
  698. Für eine letzte Präzisierung über die Register, schut euch Listing2m.s an.
  699.  
  700. Um LEKTION3.TXT zu laden, könnt ihr zwei  Methoden  wählen:  entweder  "R"
  701. tippen  und  im Requestorfenster einen Text auswählen (in diesem Fall df0:
  702. LEKTIONEN/LEKTION3.TXT), oder, wenn ihr in der richtigen  Directory  seid,
  703. einfach "R LEKTION3.TXT". Um Directory zu wechseln, "V df0:LEKTIONEN".
  704.  
  705.