Adok's Way to Assembler
Folge 1



Hier ist mein Kurs für alle, die Assembler lernen wollen! Wir fangen praktisch
bei  Null  an,  so  daß  jeder  anfangen  kann,  egal  ob  er  Einsteiger oder
Programmier-Profi  ist.  Voraussetzung für diesen Kurs  ist  es, daß ihr einen
Assembler  habt.  Im  Kurs  selbst beziehe  ich  mich  auf den Turbo Assembler
(TASM),  der den meisten Borland-Programmiersprachen kostenlos beiliegt. Falls
ihr  den Turbo Assembler nicht habt,  könnt ihr auch den kompatiblen Microsoft
Makroassembler  (MASM) verwenden, bei dem allerdings die Kommandozeilenaufrufe
zum Compilieren und Linken anders aussehen.

+++ Unser Komm-Puter +++

So, beginnen wir einmal mit einem kurzen Überblick über das Innere des Compis.
Das  Herz  des Computers ist der Prozessor,  auch CPU genannt. Hier werden die
Assemblerbefehle  verarbeitet.  Das  funktioniert,  einmal  ganz  grob gesagt,
folgendermaßen:  Der  Prozessor ist in zwei  Teile geteilt. Der eine Teil holt
sich die Befehle aus dem Arbeitsspeicher und gibt sie in die Warteschlange des
Prozessors.  Der zweite Teil des Prozessors holt  sich dann der Reihe nach die
Befehle  aus  der Warteschlange, übersetzt  und verarbeitet sie. Dabei spricht
er,  je  nach Befehl, den  Arbeitsspeicher  an, rechnet einfache arithmetische
Operationen aus oder gibt den Befehl an die Peripheriegeräte weiter.

Das  ist  im  Moment alles, was wir  über  den Prozessor wissen müssen. Weiter
geht's  mit dem Arbeitsspeicher, dem RAM. Wie wohl jeder weiß, kann man diesen
Speicher  frei  beschreiben  und lesen, nur  gehen  die Informationen nach dem
Ausschalten wieder verloren. Jede Speicherstelle im RAM wird Adresse genannt.

+++ DOS' Speicheradressierung +++

Bei  dieser  Gelegenheit  können wir  gleich  die Speicheradressierung von DOS
besprechen:  Da in DOS 1 MB Speicher adressiert werden kann, im Real Mode (dem
Prozessormodus,  in welchem DOS normalweise  arbeitet) die Adreßleitung jedoch
nur  16  Bit  breit ist (für 1 MB  Speicher  bräuchte man 20 Bit), muß man ein
bißchen  tricksen.  Der  Speicher wird in  DOS  nicht linear, sondern über ein
Segment und einen Offset adressiert. Das Segment wird mit 16 multipliziert und
der Offset dazuaddiert - dann hat man die "physikalische" Adresse.

Ein Segment ist 64 KByte groß. Folglich kann die Offsetadresse zwischen 0h und
FFFFh  (eine  Hexadezimalzahl, welche der  Dezimalzahl  65535 entspricht) groß
sein.  Das Segment kann ebenfalls 0h bis FFFFh sein. Wenn man mal nachrechnet,
ergibt  sich,  daß manche  Segment-Offsetkombinationen einander überschneiden.
Aber gehen wir vorläufig nicht darauf ein, damit es nicht zu kompliziert wird.

Jau, und die Adresse wird immer in der Form xxxx:xxxx geschrieben, und zwar in
hexadezimalen  Zahlen, da diese die Basis 16  haben und man somit leichter die
physikalische  Adresse  berechnen kann! Falls  jemand nur Bahnhof versteht: In
unserem  dezimalen  Zahlensystem haben wir die  Basis  10. Wir haben auch zehn
Ziffern:  von 0 bis 9. Die Dezimalzahl 10 ist, wie wohl jedes Baby weiß, zehn.
Beim  Hexadezimalsystem  mit  der  Basis 16  haben  wir  auch  16 Ziffern. Man
verwendet  hierbei nach der 9 die Buchstaben A bis F, also geht das Hex-System
von  0  bis F. Nach F folgt dann  10,  und diese Zahl ist im Hexadezimalsystem
nicht zehn, sondern sechzehn! Zur besseren Unterscheidung schreibt man deshalb
nach  jeder  Hexadezimalzahl ein kleines h.  Alles klar? Wenn nicht, schaut in
einem Mathe-Lexikon nach!

+++ E/A-Ports +++

Weiters  finden  wir im PC die  Peripheriegeräte (Drucker, Monitor, Tastatur &
Co.)  und  Zusatzkarten. (Nicht Asse,  Piks und Konsorten, sondern Soundkarte,
Grafikkarte  und  so weiter! ;) ) Diese  Teile  werden alle über die E/A-Ports
angesprochen.

So,  das wäre einmal alles, was wir  über das Innere des Compis wissen müssen!
Bevor  wir  das erste Assembler-Programm  schreiben  können, müssen wir jedoch
noch etwas Theorie lernen...

+++ Register +++

Register  -  genauer gesagt, Prozessorregister,  denn auch die Grafikkarte und
andere  Peripherie kann eigene Register  haben - sind eigentlich 16-Bit-breite
Integer-Variablen,  die  Werte von 0 bis  65535  annehmen können, nur sind sie
schneller  als  normale Variablen (die  ja  im Hauptspeicher abgelegt werden),
weil  die Register ein Bestandteil des  Prozessors sind. Es gibt mehrere Arten
von Registern:

- Allgemeine Register: Diese  sind  frei  verwendbar,  allerdings  müssen  bei
  manchen  ASM-Befehlen  in  ihnen  bestimmte  Werte stehen. Folgende Register
  werden bei folgenden Befehlen zu folgendem Zweck benutzt:

  AX = Meistbenutztes allgemeines Reg. Bei Interrupt-Aufrufen (Erklärung folgt
       später)  steht  hier meistens die Funktionsnummer. Außerdem wird AX für
       manche Rechenoperationen benötigt.
  BX = Parameter für Interrupts, indirekte Adressierung (z.B. beim MOV-Befehl,
       den wir noch SPÄTER besprechen werden).
  CX = Zählreg für Schleifen.
  DX = Wird von Befehlen, die die E/A-Ports  ansprechen,  verwendet.  Außerdem
       wird das DX-Register auch von einigen Rechenoperationen verwendet, wenn
       AX "nicht mehr reicht".

  Jedes allgemeine Reg läßt sich in zwei 8-Bit-Regs teilen, die dann nicht mit
  X,  sondern  mit  H bzw. L enden. Genauer gesagt: Ein 16-Bit-Reg wie AX läßt
  sich als vierstellige Hexadezimalzahl darstellen. Die  oberen  zwei  Stellen
  (Hi-Byte) sind AH, die unteren (Lo-Byte) AL. Somit dürfte auch geklärt sein,
  wozu  Hexadezimalzahlen  gut  sind:  8  Bit  (1 Byte) lassen sich durch zwei
  Hex-Stellen, 16 Bit (1 Word) durch vier  Hex-Stellen  darstellen.  Noch  ein
  Beispiel zur Vertiefung:

  AX = 1234h => AH = 12h, AL = 34h.

- Segmentregister:  Was  die  Segmente  sind,  haben  wir  ja  schon  bei  der
  Adressierung des Arbeitsspeichers in DOS geklärt. In diesen Segmentregistern
  wird nun die Segmentadresse des jeweiligen Segments  (schon  wieder  so  ein
  Wortspiel)  angegeben.  Dabei gibt es mehrere Segmente, die für verschiedene
  Aufgaben GEDACHT sind. Ich sage 'gedacht', weil  man  sie  auch  für  andere
  Zwecke mißbrauchen kann!  So lassen sich mit einem einfachen Trick Variablen
  auch im Codesegment anlegen. Dann kann man gleich ein Segment einsparen, was
  für COM-Dateien essentiell ist - siehe unten.  Doch nun zur Übersicht, damit
  jeder versteht, wovon ich quassle.

  CS = Zeigt auf das Codesegment, in dem (normalerweise, hehe) die ASM-Befehle
       eines Proggys (also das eigentliche Proggy) steh[en/t].
  DS = Zeigt auf das Datensegment, in dem die Variablen des Proggys stehen.
  ES = Kann  auf ein  beliebiges Segment  im RAM  zeigen,  das frei  verwendet
       werden darf!
  SS = Zeigt   auf   das   Stacksegment,   in  dem  meistens   Daten  temporär
       zwischengespeichert werden.

  WICHTIG:  COM-Dateien dürfen nur ein Segment haben, nämlich das Codesegment!
  Deshalb muß man etwas tricksen, wenn man Variablen in COMs  verwenden  will.
  Das werden wir aber noch später besprechen.

- Weitere  Register, die man meistens frei verwenden kann, sind BP, SI und DI.
  BP  wird in Hochsprachen zum Adressieren  von Variablen verwendet, SI und DI
  finden  vor  allem  bei den  Stringbefehlen  ihre  Verwendung. Abzuraten ist
  jedoch  von  IP (zeigt auf die nächste  Anweisung im Programm) und SP (zeigt
  auf die aktuelle Position im Stack)! Wer diese verändert, muß mindestens mit
  einem Absturz rechnen, wenn nicht etwas noch schlimmeren!

So, nun zu den...

+++ Interrupts +++

Interrupts  sind,  mal oberflächlich  betrachtet, stinknormale Unterprogramme.
Doch es gibt zwei Arten von Interrupts:

- Hardware-Interrupts und
- (tatatatamm) Software-Interrupts!

Hardware-Interrupts  werden  immer automatisch  durch  ein bestimmtes Ereignis
ausgelöst.  Zum  Beispiel wird beim  Drücken einer Taste der Tastaturinterrupt
ausgelöst,  der dann herausfindet, welche Taste gedrückt wurde, und sie in den
Tastaturpuffer  speichert. Das geht so schnell,  daß ein normaler User gar nix
davon merkt!

Wenn  man  das  Ganze  genauer betrachtet,  gibt's  zwei  Arten  von Hardware-
Interrupts:  Während die wenigen nicht-maskierbaren Interrupts IMMER ausgelöst
werden,  kann  man  die maskierbaren  Interrupts  mit dem Assembler-Befehl CLI
abschalten und mit STI wieder einschalten.

Das  zweite  sind  die Software-Interrupts, die  keine  Interrupts in dem Sinn
sind,  sondern  wirklich  nur Unterroutinen! Sie  werden  vom BIOS und vom DOS
bereitgestellt und dienen hauptsächlich dazu, dem Coder bei der ohnehin harten
Arbeit  ein  wenig  zu  unterstützen. So  beinhaltet  der  Interrupt 33h viele
Funktionen,   mit   denen   man   ohne   große  Schwierigkeiten  Maussteuerung
programmieren kann.

Die  Software-Interrupts  werden in Assembler  mit  dem Befehl INT aufgerufen.
Danach  folgt  die  Nummer  des  Interrupts.  Viele  Interrupts  haben mehrere
Funktionen.  So kann der BIOS-Interrupt  10h den Bildschirmmodus wechseln, den
Bildschirmmodus  abfragen, Text ausgeben, den  Cursor positionieren und vieles
mehr.  Deshalb  benötigt  man eine Funktionsnummer.  Sie  muß  meistens im AH-
Register (Erinnert ihr euch? AH ist das Hi-Byte von AX!) übergeben werden, nur
beim  Interrupt 33h im "großen" AX. Die Parameter für die Interrupts werden in
den  anderen  Registern  übergeben.  Welche das  sind,  ist  von  Interrupt zu
Interrupt  verschieden. Außerdem haben einige Interrupts sogar Unterfunktionen
und Unter-Unterfunktionen und Unter-Unter-Unter... Diese werden meistens in AL
übergeben.

So,  das  war die Theorie! Mit  diesem Wissen werden wir das Beispielprogramm,
das in der nächsten Folge kommt, viel besser verstehen. Bis dahin noch viiiiel
Spaß wünscht euch Adok!