Ein wenig über die erforderlichen Anpassungen steht auf der Seite „Assemblieren und Linken für .COM-Dateien beim MASM 4.00 und beim ML - Assembler”.
Die erzeugten MS-DOS-Programme sind sehr kurz. Das fertige Beispielprogramm (frage.com) ist nur 216 Bytes lang!
Die Verfahren funktionieren unter Windows 98, Windows XP und Windows VISTA. Ab Windows 7 ist es mit der Assemblerprogrammierung, wie sie auf den ersten der folgenden Webseiten beschrieben ist, vorbei. Zur Assemblerprogrammierung ab Windows 7 sind auf den Seiten ab „MASM32-SDK (MASM32 Software Development Kit)” einige Beiträge enthalten - siehe Auswahlliste weiter unten.
Segmentanteil * 16d + Offsetadresse = physikalische Adresse [16 bit]*16d->[20 bit] [16 bit] [20 bit] Segmentanteil *16 1011 0000 1111 1100 + Offsetanteil 0000 0001 1100 1101 ------------------------------------------------- = physikalische Adresse 1011 0001 0001 1000 1101
Standardmäßige Segmentzuordnung: (EA=effektive Adresse) | |||
---|---|---|---|
Zugriffsart | Standardsegment | Alternativen | Offset |
Befehl lesen | CS | keine | IP |
Datenoperation | DS | CS,ES,SS | EA |
Stapeloperation | SS | keine | SP |
BP als Basis | SS | CS,DS,ES | EA |
BX als Basis | DS | CS,ES,SS | EA |
Immer, wenn auf den Speicher zugegriffen wird, liefert das Programm eine 16 bit lange Adresse. Weitere 16 bit der Adressbildung werden aus dem Segmentregister bezogen. Diese werden um 4 Bits (1 Halbbyte bzw. 1 Nibble) nach links verschoben und auf die 16 Bits Adressanteil des Programmes aufaddiert, um die wirkliche Speicheradresse zu bilden. Der Inhalt der Segmentregister kann verändert werden, so dass ein Zugriff auf jede Speicheradresse möglich ist. Die Segementregister sind Spezialregister. Es gibt je eines für
Man tendiert dazu, den Inhalt der Segmentregister am Beginn des Programmes mit Daten zu versorgen und sie im Programmablauf möglichst nicht zu verändern.
MOV AX,DATASEG MOV DS,AX ;Set value of Data segment ASSUME DS:DATASEG ;Tell assembler DS is usable ....... MOV AX,PLACE ;Access storage symbolically by 16 bit address
Im vorstehenden Programmbeispiel weiß der Assembler, dass keine Besonderheiten vorliegen, da die Maschine von sich aus das DS-Register zur Adressbildung für den normalen Zugriff auf Daten verwendet.
Falls man im obigen Beispiel ES statt DS für den Datenzugriff verwenden möchte, müsste man es gesondert angeben. Vor der MOV-Anweisung, die auf die symbolische Adresse SPACE zugreift, würde eine vorgestelltes ES der Maschine mitteilen, dass hier ES anstelle von DS zur Adressbildung zu verwenden ist.
Einige Konventionen erleichtern den Umgang mit den Segmentregistern. Handelt es sich beispielsweise um eine COM-Programm (und nicht um eine EXE-Programm), haben alle vier Segmentregister den gleichen Inhalt. Das Programm läuft vollständig in
einem Bereich von 64 kB ab. Wenn erforderlich, kann man diesen Adressraum verlassen, muss es aber nicht.
Direkte Registeradressierung | ADD AX,CX JMP AX | Verwendet die direkten Inhalte der Register |
Indirekte Speicheradressierung | ADD AX,[BX] ADD [BX],AX JMP [CX] | Verwendet den Inhalt von BP, BX, DI oder SI um den Offset der Speicheradresse zu bestimmen. Bei einigen Befehlen sind alle allgemeine Register verwendbar. |
Unmittelbare Adressierung | ADD AX,123 | Verwendet unmittelbar den Wert des 2. Argumentes. |
Relative Speicheradressierung | ADD AX,[123] | Das 2.Argument ist der Offset der Speicheradresse. |
Basisadressierung, basisrelative Adressierung | MOV FELD[BX],AL MOV AX,[BX]+6 | 1. Beispiel: die Adresse ist die Summe aus der Offsetadresse FELD plus Inhalt von BX. 2. Beispiel: die Adresse entsteht aus dem Inhalt von BX plus dem Wert 6. Beide Beispiel: Anstelle von BX kann auch BP verwendet werden. |
Indizierte Adressierung, direktindizierte Adressierung | MOV FELD[DI],AL | wie Basisadressierung,nur DI und SI als Indexregister. |
Basisindizierte Adr. | MOV AX,[BX][SI]100 | Die Adresse ist die Summe aus den Inhalten von BX oder BP + DI oder SI + einer Offsetadresse. |
Annahmen bei Auslassung von Angaben zur Verwendung der Segment-Register (default rules for which segment registers are used) zur Adressbildung:
Bei einer Adressbildung für NEAR INDIRECT wird DS verwendet, um die Adresse aus dem Speicher zu lesen. Anschließen wird CS zur Adressvervollständigung verwendet. Für Verzweigungen im FAR-Bereich wird CS selbst geändert.
Der Adresszeiger (instruction counter) zeigt implizit immer auf das Codesegment.
Datenregister | Zeiger- und Indexregister | |||||
---|---|---|---|---|---|---|
EAX32 | Erweitertes AX | ESP32 | Erweiterter SP | |||
AX16 | Akkumulator | SP16 | Stackpointer | |||
AH8 | AL8 | EBP32 | Erweiterter BP | |||
EBX32 | Erweitertes BX | BP16 | Basepointer | |||
BX16 | Base Adressierung | ESI32 | Erweiterter SI | |||
BH8 | BL8 | SI16 | Sourceindex | |||
ECX32 | Erweitertes CX | EDI32 | Erweiterter DI | |||
CX16 | Counter (Schleifen) | DI16 | Destinationindex | |||
CH8 | CL8 | |||||
EDX32 | Erweitertes DX | |||||
DX16 | Data | Segmentregister | ||||
DH8 | DL8 | (Portadressierung) | CS16 | Codesegment | ||
Kontrollregister | DS16 | Datasegment | ||||
EIP32 | Erweiterter IP | SS16 | Stacksegment | |||
IP16 | Instruction Pointer | ES16 | Extrasegment | |||
Eflags32 | Erweitertes PSW | FS16 | Extrasegment | |||
Flags | Process Status Word | GS16 | Extrasegment |
Process Status Word beim 8086 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
OF | DF | IF | TF | SF | ZF | - | AF | - | PF | - | CF | ||||
Process Status Word beim 80286 | |||||||||||||||
NT | I/O | OF | DF | IF | TF | SF | ZF | - | AF | - | PF | - | CF |
CF | Carry Flag | Übertragskennzeichen (Statusflag) Wird auch als Borrow bezeichnet. |
PF | Parity Flag | Paritätskennzeichen (Statusflag) 1=gerade Anzahl 1er-bits im Ergebnis. |
AF | Auxiliary Flag | Hilfsübertragungskennzeichen (Statusflag) für AL, bezieht sich auf den Übertrag vom niederwertigen Halbbyte. |
ZF | Zero Flag | Nullkennzeichen (Statusflag) |
SF | Sign Flag | Vorzeichenbit (Statusflag) 1=Ergebnis der Integerberechnung ist negativ. |
TF | Trap Flag | Einzelschrittkennzeichen (Steuerungsflag) für Fehlersuche (Debugging). |
IF | Interrupt enable Flag | Unterbrechungsfreigabekennzeichen (Steuerungsflag) 1=Interrupt enabled |
DF | Direction Flag | Richtungskennzeichen (Steuerungsflag) 1=Stringbefehl arbeitet von hoher Adresse zur niedrigen. 0=Stringbefehl arbeitet von niedriger Adresse zur hohen. |
OF | Overflow Flag | Überlaufkennzeichen (Statusflag) für vorzeichenbehaftete Operationen. 1=es trat Überlauf (Carry) oder Ausleihen (Borrow) auf. Das Ergebnis ist zu groß oder zu klein. |
80286 im Protected Mode: | ||
---|---|---|
I/O | I/O Privilege Level | Maximale Zugriffsrechte für E/A Operationen |
NT | Nested Task | Kennzeichen für verschachtelte Tasks |
BX ist ein Register für allgemeine Aufgaben. Es hat eine Sonderfunktion bei der Adressierung.
CX ist ein Register für allgemeine Aufgaben. Es hat bei einigen Befehlen die Sonderfunktion des Zählers (z.B. als Schleifen- oder Wiederholungszähler).
DX ist ein Register für allgemeine Aufgaben. Es verlängert bei einige arithmetischen Operationen das AX-Register. Bei den Ein-/Ausgabeoperationen IN und OUT kann es zur Angabe der Portadressen verwendet werden.
AX, BX, CX und DX können in die Hälften AH, AL, BH, BL, CH, CL, DH und DL unterteilt und als solche verwendet werden.
SI und DI sind nur als 16-bit-Register verwendbar . Sie können (wie auch BX) zur Indexadressierung genutzt werden. Hauptsächlich werden sie als Stringpointer (Zeiger auf Zeichenketten) genutzt.
SP dient als Zeiger auf den Stack.
BP ist manipulierbar, und somit ein Verwandter von SP. Er hilft, auf Daten zuzugreifen, die im Stack abgelegt sind.
Die meisten 16-bit-Befehle können auf die Register SI, DI, SP oder BP angewendet werden.
Um System-Funktionen aufzurufen, wird der INT-Befehl benutzt. Man muss nicht wissen, wie dies genau funktioniert, sondern man kann hier "kochbuchartig" vorgehen.
Die Übergabe von Steuerungsanweisungen (CALL, RET, JMP) erfordert ein gewisses Verständnis. Sie können so klassifiziert werden:
JZ bedeutet was es sagt: verzweige bei gesetztem Zero-bit JNZ bedeutet was es sagt: verzweige, wenn das Zero-bit nicht gesetzt ist JG größer: bedeutet "wenn die vorzeichenbehaftete Differenz positiv ist" JA über (above): bedeutet "wenn die nicht vorzeichenbehaftete Differenz positiv ist" JL kleiner (less): bedeutet "wenn die vorzeichenbehaftete Differenz negativ ist" JB unter (below): bedeutet "wenn die nicht vorzeichenbehaftete Differenz negativ ist" JC carry: ist das Gleiche wie JB JP verzweige bei gesetztem Paritätsflag (even) JPE parity even: ist das Gleiche wie JP JNP verzweige bei nicht gesetztem Paritätsflag (odd) JPO parity odd: ist das Gleiche wie JNP JCXZ verzweige wenn CX den Inhalt 0 hat. JECXZ verzweige wenn ECX den Inhalt 0 hat. Der Befehl wurde mit dem 386er Prozessor eingeführt.
Für .COM-Programme bis einschließlich 80286er Prozessor gilt, dass alle anwendbaren bedingten Verzweigungen automatisch DIRECT, NEAR und SHORT sind. Das bedeutet: Man kann nicht weiter als 128 Bytes entfernt vor oder zurück verzweigen. Ab dem 80386er Prozessor entfällt die genannte Entfernungsbeschränkung. Für „Verzweige wenn” Jcc kann „Jcc” sein:
Ohne Vorzeichen (unsigned):
(Zu den Flagbezeichnungen wie CF, OF, PF, SF, ZF siehe hier.
JA größer als CF=0 und ZF=0 (wie JNBE) JAE größer oder gleich CF=0 (wie JNB und JNC) JB kleiner als CF=1 (wie JNAE und JC) JBE kleiner als oder gleich CF=1 und ZF=1 (wie JNA) JC Carry Flag gesetzt CF=1 JNA nicht größer als CF=1 und ZF=1 (wie JBE) JNAE nicht größer/gleich CF=1 (wie JB und JC) JNB nicht kleiner CF=0 (wie JAE und JNC) JNBE nicht drunter/gleich CF=0 und ZF=0 (wie JA) JNC kein Übertrag CF=0Mit Vorzeichen (signed):
JG größer ZF=0 und SF=OF (wie JNLE) JGE größer/gleich SF=OF (wie JNL) JL kleiner SF<>OF (wie JNGE) JLE kleiner/gleich ZF=1 und SF<>OF (wie JNG) JNG nicht größer ZF=1 oder SF<>OF (wie JLE) JNGE nicht größer/gleich SF<>OF (wie JL) JNL nicht kleiner SF=OF (wie JGE) JNLE nicht kleiner/gleich ZF=0 und SF=0 (wie JG) JNO kein Überlauf OF=0 JNS positv SF=0 JO Überlauf OF=1 JS negativ SF=1Sonstige:
JCXZ CX-Register gleich 0 JE gleich ZF=1 (wie JZ) JNE nicht gleich ZF=0 (wie JNZ) JNZ nicht null ZF=0 JNP keine Parität PF=0 (wie JPO) JP Parität PF=1 (wie JPE) JPE gerade Parität PF=1 (wie JP) JPO ungerade Parität PF=0 (wie JNP) JZ null ZF=0 (wie JE)
allgemeine Syntax:
LOOPcc <Sprungziel>
Bei jedem Durchgang wird das CX-Register um 1 verringert. Danach erfolgt eine SHORT-Verzweigung zum angegebenen Sprungziel, oder der nächstfolgende Befehl wird ausgeführt. LOOPcc setzt keine Flags.
cc siehe bei Jcc. Die Bedingung bezieht sich auf die Dekrementierung von CX.
BATch Control Program Vers.:29.10.95 batc ? Eingabe J/N im Dialog Errorlevel bei Ausgang: 0 nach J od.Y, 1 nach N, 3 nach ^C batc W prompted mit <-' batc B Warmboot batc Cx setzt Randfarben (EGA/VGA) x=0...F, vergl. batc F batc E... Ausgabe ESC... zum Bildschirm (z.B. für ANSI-Treiber) batc F Anzeige Farbpalette auf Bildschirm. batc J setzt Errorlevel auf 0 batc K Keycode anzeigen batc M Verwendung als more-Filter, Aufruf hinter | batc N setzt Errorlevel auf 1 batc P... Textausgabe auf Drucker LPT1. Anleitung: batc P batc R Resetbefehl fuer Drucker an LPT1 (BIOS) batc S Zeichenvorrat zum Bildschirm batc V Zeigt den Inhalt des Video-State-Buffers batc xx VGA auf xx Zeilen/Seite, 25 / 28 / 43 batc Y Anzeige diverser Systeminformationen
Die dafür zuständige Programmierung wird nachfolgend gezeigt. Mit den ersten vier Befehlen
mov al,byte ptr cs:fcb+1 lea di,btab ;befehlstabelle mov cx,lbtab ;deren laenge repne scasb
wird festgestellt, ob der erste Buchstabe des beim Programmaufruf mitgegebenen Parameters in der Tabelle 'MYWV42BCDEFPRS?NJK ' enthalten ist. Ist er enthalten, erfolgt eine Verzweigung zur Marke w1, andernfalls geht es zur Marke hilfe- dort wird die oben dargestellte kurze Bedienungsanleitung am Bildschirm gezeigt.
Ganz wichtig: Das Registerpaar CX enthält die Position des übereinstimmenden Zeichens in der Tabelle, und zwar von hinten gezählt. Ab dem Merkmal wohin beginnt eine Verzweigungsadresstabelle. Sie enthält alle in Frage kommenden Zieladressen, ist jedoch genau andersherum angeordnet wie die Tabelle btab der möglichen Aufrufparameter.
Beim Merkmal w1 erhält das DI-Registerpaar die Adresse des Sprungzielen und der Sprung wird ausgeführt.
Das verwendeten Unterprogramm prmsg stellt den nachfolgenden Text auf dem Bildschirm dar. Das Macro dbl definiert eine Textzeile mit abschließendem Zeilenwechsel.
mov al,byte ptr cs:fcb+1 lea di,btab ;befehlstabelle mov cx,lbtab ;deren laenge repne scasb je w1 jmp hilfe ;ungueltiger aufrufparameter btab db 'MYWV42BCDEFPRS?NJK ';mgl.Parameter lbtab equ $-btab wohin dw hilfe ;hilfeanzeige dw keycode ;tastencode zeigen dw ret0 ;errorlevel auf 0 dw ret1 ;errorlevel auf 1 dw inkjn ;eingabe j/n dw chrshow ;zeichensatz dw resetp ;reset printer dw list_p ;druckausgabe dw farbe ;farbpalette zum bildschirm dw scrn_esc ;bildchirmausgabe esc-string dw datetest ;datumtest und Blockcursor ein dw crand ;setzt farbigen rand (ega) dw boot ;warmboot dw ega25 ;vga auf 25 / 28 Zeilen / Seite dw ega43 ;vga auf 43 Zeilen/Seite dw vgainfo ;anzeige video state buffer dw weiter ;Abwarten Eingabe der CR-Taste dw pcstat ;PC-Status anzeigen dw more ;Verwendung als More-Filter w1: add cx,cx ;*2->tabellenoffset mov di,cx jmp wohin[di] ;hilfstextanzeige wenn aufruf ohne parameter hilfe: call prmsg dbl 'BATch Control Program Vers.:29.10.95' dbl dbl 'batc ? Eingabe J/N im Dialog'