ASCALM.DOK CALM-ASSEMBLER Bedienungsanleitung Inhaltsverzeichnis Seite CALM-Dateien 1 Minimale Konfiguration 1 Befehlszeile 1 Arbeitsweise 2 Querverweisliste 2 Objektformat 2 Pseudobefehle 4 Unterschiede Assembler - CALM-Norm 5 Fehlermeldungen 5 Inline mit dem CALM-Assembler 6 Erzeugung von .EXE-Programmen mit dem CALM-Assembler 9 Einsatz von lokalen Marken 10 Automatische Optimisierung von Sprungbefehlen 10 Erweiterungen des CALM-Assemblers 11 (c) Copyright Mai 1994 Patrick Fäh, La Colombière, CH-1783 Pensier, Schweiz CALM-Assembler Bedienungsanleitung Seite: 1 CALM-Dateien Folgende Dateien sollten sich auf Ihrem Betriebssystem befinden: ASCALM.* CALM-Assembler (Annahme: für Z80) MUFBIN.* wandelt das erzeugte Binärformat um (MUFOM) Z80.PRO Prozessormodul für den Mikroprozessor Z80 TZ80.ASM Testdatei mit Z80-Befehlen ASCALMER.TXT Fehlerliste für den Assembler Minimale Konfiguration Atari ST: 680x0, 256 KByte frei, 1 Floppy-Laufwerk PC/MS-DOS >= 2.x: iAPX86-kompatibler Prozessor, 256 KByte, 1 LW. nur PC/MS-DOS: setzen Sie in CONFIG.SYS mindestens FILES=16. DOS, TOS: ASCALMER.TXT, *.PRO und *.REF: PATH gilt; anderer Pfad mit SET CALM=Pfad. Befehlszeile Der Assembler wird wie folgt gestartet: ASCALM [/Option] Die Optionen sind fakultativ. Folgende Optionen sind verfügbar: /Apath1;path2; ASCALM definiert weitere "paths" für ASCALM, für die Dateien: ASCALMER.TXT, *.PRO und *.REF. Beispiel: DOS, TOS: PATH C:\ASCALM;C:\BIN; Befehlszeile: /AA:\PRODEF; Befehl: .PROC Z80 Versuche mit: 1) Z80.PRO 2) A:\PRODEF\Z80.PRO 3) C:\ASCALM\Z80.PRO 4) C:\BIN\Z80.PRO Auch für ASCALMER.TXT und alle *.REF. Maximimum für alle "paths": 80 Zeichen. Bei .PROC A:Z80 wird nur nach A:Z80.PRO gesucht. Datei/B .MUF generiert ein Objekt (MUFOM Format) Datei/C .ASC erzeugt Datei.ASC in der alle Makros und .IF/.ELSE/.ENDIF aufgelöst sind (nicht wenn /E) /DName=Wert definiert Symbol mit Name und Wert (+ oder -) Datei/E .ERR alle Fehlermeldungen auch in diese Datei /Ipath1;path2; Insert definiert zusätzliche "paths" für .INS. Beispiel: Befehlszeile: /IA:\SOURCE;B:\DEF\;C:\PROJET; Befehl: .INS IO_PART Versuche mit: 1) A:\SOURCE\IO_PART.ASM 2) B:\DEF\IO_PART.ASM 3) C:\PROJET\IO_PART.ASM 4) IO_PART.ASM Maximum für alle "paths": 80 Zeichen. Bei .INS B:IO_PART bedeutet nur B:IO_PART.ASM. /F Fix unbenutzte Symbole nicht entfernen (.REF). Datei/L .LST generiert eine Auflistung /R Read Quelldateien werden nicht verändert Datei/S .REF enthält alle Symbole als Anweisungen. Datei/SA... .REF wie /S, doch Adressen sind mit ":"; & TRUE usw. Datei/Si... .REF gewisse Symbole nicht in Symboldatei schreiben: Werte (i=0), Adressen (1), == (2), .SYSCALL (3) /V Verify generiert kein Objekt (hebt /B auf) /W Wait wartet bei jeder Zeile mit einem Fehler Datei/X .XRF generiert auch Querverweisangaben Wird keine Option angegeben, generiert der Assembler nur das Objekt. Die Option /B ist nur notwendig, wenn der Objektdatei .MUF ein anderer Name gegeben werden soll oder auf einen anderen Plattenspeicher kopiert werden soll. Die Quelldatei kann mit PFED oder ähnlichen Editoren erstellt werden. Eine Zeile darf nicht länger als 127 Zeichen sein (ohne ). CALM-Assembler Bedienungsanleitung Seite: 2 Als Beispiel wollen wir den Assembler und sein Modul testen, indem wir die entsprechende Testdatei assemblieren und ein Objekt und eine Auflistung generieren: ASCALM TZ80/L Der Assembler sucht die Datei TZ80.ASM und generiert das Objekt TZ80.MUF und die Auflistung TZ80.LST. Um eine Datei im Plattenspeicher B: zu assemblieren, und das Objekt und die Auflistung in A: abzulegen, wird folgendes eingegeben: ASCALM B:TZ80 A:TZ80/L/B oder ASCALM B:TZ80 A:TZ80/L A:TZ80/B Arbeitsweise Der Assembler wird durch folgenden Befehl gestartet: ASCALM /L Auch eine Auflistung wird verlangt. Der Assembler öffnet die Quelldatei und generiert die Objekt- (.MUF) und die Auflistungsdatei (.LST). Die Quelldatei wird zweimal gelesen. Die Objektgenerierung erfolgt im zweiten Durchlauf. Wenn ein Fehler im Programm gefunden wird, wird die Fehlerzeile mit der Fehlerangabe in der Auflistung, in der Fehlerdatei (falls /E), am Bildschirm und in der Quelldatei (falls nicht /R) angegeben. Beispiel: MOVE B,ALPHA ; Zeile im Z80-Programm ^ 31 Hierzu sucht der Assembler zu Beginn einer Assemblierung nach der Datei ASCALMER.TXT. Ist diese im aktuellen Laufwerk nicht vorhanden, werden alle Fehler wie im obigen Beispiel durch Nummern angegeben. Ansonsten erscheint das obige Beispiel wie folgt: MOVE B,ALPHA ; Zeile im Z80-Programm ^ symbol value undefined Bei Fehlermeldungen am Bildschirm werden folgende Tasten interpretiert: "D" nicht mehr nach Fehler warten "S" Assemblierung abbrechen (kann jederzeit eingegeben werden) "W" nach Fehler warten Tritt ein fataler Fehler auf (Datei existiert nicht, Plattenspeicher voll, usw.), dann hält der Assembler an und zeigt am Bildschirm entsprechende Informationen an (ASCALM gibt 4 ans System zurück, ansonsten 0). Der Benutzer muss die angegebenen Fehler beseitigen und kann dann den Assembler erneut starten. Die E/A-Dateien CON:, AUX:, usw. werden unterstützt. Folgende Befehlszeile leitet die Auflistung auf den Bildschirm um und generiert kein Objekt: ASCALM TZ80 CON:/L/V Wichtig: Um allfällige Fehler in die Quelldatei einzufügen, macht der Assembler eine Kopie der Quelldatei. Daher genug Plattenkapazität vorsehen!. Diese Kopie (.AST) mit den Fehlermeldungen ersetzt dann die Originaldatei. Mit der Option /R kann man verhindern, dass der Assembler die Quelldatei(en) ändert. Diese Kopie erfolgt auch für die eingefügten Dateien (.INS). Erlaubte Zeichen in Dateinamen: "0".."9","A".."Z","a".."z","_","?","-",":","\",".". Querverweisliste Um die Querverweisliste eines Programmes zu erhalten, muss die Option /X angegeben werden: ASCALM TZ80 B:TZ80/X Der Assembler erzeugt die Datei TZ80.XRF im Laufwerk B:. Je mehr Symbole verwendet werden, desto umfangreicher wird diese Datei. Die Querverweisliste wird in TZ80.LST geschrieben (angehängt falls /L). Die Zwischendatei TZ80.XRF wird gelöscht. Objektformat Der CALM-Assembler generiert Objektdateien mit der Dateinamen- erweiterung .MUF. Dieses Objektformat ist ein ASCII-Format. Es werden nur absolute Formate generiert. Um beispielsweise das MUFOM-Format in eine .COM-Datei umzuwandeln, wird das Programm MUFBIN benötigt: CALM-Assembler Bedienungsanleitung Seite: 3 MUFBIN /Optionen MUFBIN begrenzt die Länge der Ausgangsdatei zur Zeit auf 64 KByte (Ausnahme: keine Begrenzung für /B/N, /H/N, /I/U, /M/U keine Begrenzung (/N: falls aufeinanderfolgend). Die folgenden Optionen bestimmen das Ausgangsformat (keine Vorgabe): /B .BIN, binär, zusätzlich muss /N oder /Y angegeben werden i/E .EXE, für PC/MS-DOS, i (1..3) definiert die Speichereinteilung /H .BIN, sedezimal, ASCII, zusätzlich auch /N oder /Y angeben /I .HEX, Intel's Hex-Format /M oder i/M.FRS, Motorola's S-Format (Vorgabe: S0, S3 und S7; 1/M: S0, S1 und S9; 2/M: S0, S2 und S8; 3/M: S0, S3 und S7) /T .TOS, für Atari ST Bei den Ausgangsformaten /B und /H kann eine Präambel vor die eigentliche Datei einfügt werden. Diese Dateien setzen sich dann aus einer Präambel (bestehend aus 256 Bytes), gefolgt von den Binärdaten zusammen. Die Präambel enthält folgende Informationen: Abstand Inhalt [festgelegt durch] 0 Ladeadresse [tiefstes .LOC mit generiertem Code] 2 Länge [Code] 4 Startadresse [.START Start] Die Bytefolge der drei 16 Bitangaben ist: LSB-MSB. Wenn Sie keine Präambel einfügen möchten, geben Sie /N ein. Die generierte Datei ist dann kompatibel mit einer .COM Datei, wenn die Start- und die Ladeaddresse 16'100 betragen. Und mit /Y kann eine Präambel eingefügt werden. Weitere Optionen sind möglich: /A verschiebt das Objekt um den angegebenen Wert (Sedezimalzahl/A). /D Datenlänge (mit /E verwendet; Sedezimalzahl/D). /F führt eine UND-Operation zwischen dem angegebenen Filterwert und den Adressen aus (Sedezimalzahl/F). /J hh/J: Wert der nicht definierten Speicherplätze, Vorgabe: 00/J. Benutzen Sie FF/J für EPROMs. /L legt die Anzahl Datenbytes pro Zeile in /H, /I oder /M fest (Bereich: 1 bis 250; Vorgabewerte: 39, 32, 32; Wert/L). Bei /H 0/L werden keine erzeugt. /O Name der Ausgangsdatei ändern (Dateiname/O). /S Stapellänge (mit /E verwendet; Sedezimalzahl/S). /U undefinierte Bereiche nicht auffüllen (mit /I oder /M). /V zeigt alle Informationen (ausser Daten) der MUFOM-Datei. /W Wortaustausch: Austausch von LSB - MSB in 16-Bit-Wort. Beispiele: Ausgangsdateinamen ändern (Intel-Format soll generiert werden): MUFBIN ObjektEin/I ObjektAus/O. Die E/A Dateien (z.B. CON: oder AUX:) für ObjektAus sind möglich. Die beiden Adressbereiche 16'0 bis 16'FFF und 16'F000 bis 16'FFFF sollen in einem 8-KByte-EPROM untergebracht werden: MUFBIN ObjektEin/B/N 1FFF/F. Es wird eine 8 KByte lange Binärdatei erzeugt, die direkt zur Programmierung des EPROM's verwendet werden kann. Ansonsten wäre die Datei 64 KByte lang geworden. Das Objekt um einen beliebigen Wert verschieben: MUFBIN ObjektEin/I 200/A. Wurde beispielsweise die Datei mit .LOC 16'0 assembliert, dann generiert MUFBIN eine Intel .HEX kompatible Datei, die mit 16'200 beginnt. Der Objektcode wird jedoch nicht verändert. Falls ebenfalls /F angegeben wurde, wird zuerst die Filteroperation ausgeführt. CALM-Assembler Bedienungsanleitung Seite: 4 Pseudobefehle Folgende Pseudobefehle werden vom CALM-Assembler unterstützt: .ALIGN, .APC, .ASCII, .ASCIZ, .ASCIZE, .BASE, .BLK.n, .CHAP, .DATA.n, .ELSE, .END, .ENDIF, .ENDLIST, .ENDMACRO, .ERROR, .EVEN, .EXITMACRO, .FILL.n, .IF, .INS, .LAYOUT, .LAYOUTMACRO, .LIST, .LISTIF, .LOC, .LOCALMACRO, .MACRO, .MESSAGE, .ODD, .PAGE, .PROC, .PROCSET, .PROCVAL, .RANGE, .REF, .START, .STRING, .SYSCALL, .TITLE, .8, .16 und .32 . Bemerkungen zu einigen Pseudobefehlen (siehe auch UPDATESD.*): .ASCIZE - entspricht .ASCIZ gefolgt von .EVEN. .IF/.ELSE/.ENDIF - IF ist wahr, wenn <> Null ist. - IF..ENDIF kann bis zu 32mal geschachtelt werden. - IF und das entsprechende ENDIF müssen in der gleichen Datei sein. - IF..ELSE..ELSE..ENDIF ist möglich. .INS - mit .INS Datei,READONLY wird die einzufügende Datei nur gelesen. Gefundene Fehler werden nicht in die Datei eingefügt. Sie werden in die Hauptdatei (falls erlaubt) oder die Fehlerdatei (falls /E) eingefügt. - Vorgabe für Dateierweiterung: .ASM. - .INS kann nur einmal geschachtelt werden. .LAYOUT - Bei .LAYOUT sind folgende Parameter möglich: HEX (Adressen und Daten in sedezimaler Darstellung) LENGTH n (n Zeilen pro Auflistungsseite, n=0: unendlich) Beispiel: .LAYOUT HEX, LENGTH 60 ; Assemblerwerte - Fest eingestellt sind folgende Layoutwerte (nicht änderbar): HEX (OCT ist nicht möglich) WIDTH 127 (Zeilenlänge) TAB 8 (ein Tabulator entspricht 8 Leerzeichen) .LIST/.ENDLIST - LIST ist wahr, wenn <> null. - LIST..ENDLIST kann bis zu 255mal geschachtelt werden. - LIST und das entsprechende ENDLIST: in der gleichen Datei sein. .LISTIF - zeigt alle .IF/.ELSE/.ENDIF-Pseudobefehle in der Auflistung. - .LISTIF ist aktiv wenn <> null ist oder fehlt. .REF Datei - Datei.REF ist eine Textdatei, die Anweisungen, .SYSCALLs und Kommentare enthalten kann. Datei wird nur ein Mal gelesen. Datei wird nie verändert. - der aktuelle PATH wird berücksichtigt. .SYSCALL.n Name (n = 8, 16 oder 32) - definiert eine spezielle Makro: .MACRO Name; .n Name%1; .ENDMACRO. .SYSCALLs sind in .REF-Dateien erlaubt. Beispiel: INTDOS = 16'CD21; .SYSCALL.16 INT; Aufruf: INT DOS; erzeugt: .16 INTDOS. Folgende Pseudobefehle werden nicht unterstützt: .ENDTEXT, .EXPORT, .IMPORT, .TEXT. CALM-Assembler Bedienungsanleitung Seite: 5 Unterschiede Assembler - CALM-Norm Der CALM-Assembler unterstützt nicht die volle CALM-Norm. Die Unterschiede: Symbole: - Name: 32 (lokale Marken: 29) Zeichen werden berücksichtigt; Zeichen: "A".."Z", "a".."z", "_", "?" und "0".."9" (<> 1. Position). Umlaute/Akzente werden in Großbuchstaben umgewandelt. - Wert: 32 Bit mit Vorzeichen. Ausdruck: - Wortlänge: 32 Bit mit Vorzeichen. - die maximale Anzahl der offenen Operationen beträgt 15. - Verschiebeamplitude (.SR., .SL. und .ASR.): Amplitude wird auf 8 Bit begrenzt (-256..+255). Eine negative Amplitude dreht die Verschieberichtung um. allgemein: - Länge einer Eingangsassemblerzeile beträgt 127. - der APC hat eine Länge von 32 Bits. - einige Pseudobefehle werden nicht behandelt. - die \-Zeichenfolgen werden nicht unterstützt. - mehrere .PROC sind nicht möglich Fehlermeldungen Siehe ASCALMER.TXT. Fehlermeldungen in deutsch befinden sich in ASCALMED.TXT. Diese Datei kann in ASCALMER.TXT kopiert werden. Bemerkungen zu fatalen Fehlern: 101: .PROC Fehler (In der Prozessorbeschreibung ist etwas falsch. Mit der Option /D kann der Befehl festgestellt werden, der diesen Fehler verursacht.) 102: .PROC ist zu lang (zuwenig Speicherplatz) 103: Datei nicht vorhanden (Bei den Pseudobefehlen PROC oder INS ist die angegebene Datei nicht vorhanden.) 104: Eingangsdatei nicht vorhanden (Der Assembler findet die Eingangsdatei nicht.) 105: Datei kann nicht kreiert werden (Die Objekt- oder/und Auflistungsdatei kann nicht geschaffen werden.) 106: kein .PROC (Keine Prozessorbeschreibung: .PROC zu Beginn der Datei angeben.) 107: Eingangsdatei nicht zurücksetzbar (Vor dem zweiten Durchlauf wird die Eingangsdatei zurückgesetzt. Computersystem prüfen.) 108: falsche .PROC Version (Der Assembler und die Prozessorbeschreibung sind inkompatibel.) 109: leere Befehlszeile (Eingangsdatei und Optionen in der Befehlszeile angegeben.) 110: neues Symbol im 2. Durchlauf (In einem Netzwerk sollte erneut assembliert werden. Ansonsten kann die entsprechende Zeile mit der Option /D gefunden werden.) 111: Stapelfehler in .PRO (Fataler Fehler beim Interpretieren der Prozessorbeschreibung.) 112: zu viele Symbole 113: Dateiende: es fehlt .ENDMACRO 114: Makropuffer zu klein 115: zu viele geschachtelte .INS 116: Abgebrochen (Mit "S" von der Tastatur.) 117: .PROC/.REF: muss vor Kodeerzeugung sein (.PROC und .REF immer zu Beginn einer Datei einfügen; nach .TITLE.) CALM-Assembler Bedienungsanleitung Seite: 6 Inline mit dem CALM-Assembler TurboPascal und Pascal/MT+ erlauben mittels INLINE Anweisungen Maschinenbefehle direkt in das Pascalprogramm einzuschieben. Genauere Angaben zur Syntax und zu den Einschränkungen von INLINE, finden Sie in den entsprechenden Bedienungsanleitungen zu diesen Pascalcompilern. Der CALM-Assembler kann zum Erzeugen von Maschinenbefehlen eingesetzt werden. Das folgende Beispiel zeigt die Schritte auf, die zum Erzeugen von INLINE Anweisungen für die Prozessoren iAPX86 (PC/MS-DOS) und Z80 (CP/M-80) notwendig sind. Die Funktion HEXNIBBLE testet und wandelt das Eingangszeichen in eine Zahl um, wenn das Zeichen eine gültige hexadezimale Zahl ist ('0'..'9','A'..'F', 'a'..'f'). FUNCTION HEXNIBBLE(VAR H:INTEGER):BOOLEAN; {in: ActCh, out: H (value), HEXNIBBLE (true or false)} VAR C:CHAR; BEGIN C:=UpCase(ActCh); HEXNIBBLE:=TRUE; IF (C >= '0') AND (C <= '9') THEN BEGIN H:=ORD(C)-ORD('0'); END ELSE IF (C >= 'A') AND (C <= 'F') THEN BEGIN H:=ORD(C)-ORD('A')+10; END ELSE BEGIN H:=0; HEXNIBBLE:=FALSE; END; END; Nun muss die Pascalprozedur in Assembler umgeschrieben werden. Die folgenden beiden Seiten zeigen die iAPX86 und die Z80 Version der Funktion HEXNIBBLE. Dazu werden die Assemblerbefehle in ganz normaler Assemblerform in eine Assemblerquelldatei geschrieben und assembliert (mit Auflistung). Dann werden in der Funktion HEXNIBBLE die Pascalzeilen zwischen BEGIN und END gelöscht und die Assemblerauflistungsdatei nach BEGIN eingefügt. Sie löschen alle überflüssigen Assemblerauflistungszeilen und die Adressen (jeweils 4 Zeichen zu Beginn jeder Zeile). Nun müssen Sie die erzeugten Auflistungsbytes in die INLINE Form setzen: mit INLINE( beginnen, $ und / zwischen die Auflistungsbytes einfügen, usw. Setzen Sie die Assemblerbefehle in die Pascalkommentarform mit (* und *). Schliesslich müssen Sie alle Variablen durch deren Namen ersetzten (RESULT, HEX). Hierbei ist es wichtig, dass Sie die interne Darstellungsform der verschiedenen Datentypen genaustens kennen. Somit können Sie die Assemblerversion oft erheblich verbessern (Geschwindigkeit, Programmlänge). Zum Beispiel ist der Booleanwert hier 1 für TRUE und 0 für FALSE. Auch muss man wissen, wie auf die einzelnen Variablen zugegriffen wird. Die Bedienungsanleitung zu Ihrem Pascalcompiler gibt Ihnen hierzu weitere Informationen. Um die Geschwindigkeit der Pascal- und der Assemblerversion zu messen, wurde folgendes kleine Programm verwendet: PROGRAM THEX; {Testet die Assembler- und die Pascalversion von HEXNIBBLE} VAR ACTCH: CHAR; RESULT: BOOLEAN; I, VALUE: INTEGER; {$I P_HEX} { P_HEX: Pascal, A_HEX: Assembler } BEGIN WRITELN('START'); FOR I:=1 TO 1000 DO BEGIN {1000 x} FOR ACTCH:=' ' TO '~' DO {95 Zeichen} RESULT:=HEXNIBBLE(VALUE); END; WRITELN('END'); END. CALM-Assembler Bedienungsanleitung Seite: 7 Es ergaben sich folgende Resultate: Programmlänge Sekunden (für THEX) Pascalversion 192 bytes 26,3 Assemblerversion 80 bytes 20,6 Unterschied -58 % -22 % (XT-kompatibel, Takt 4.77 MHz, TurboPascal 3.0 für PC-DOS) Diese Zahlen sollen Ihnen nur eine erste Grössenordnung vermitteln. Oft ist die Assemblerversion erheblich schneller (2..4) als die Pascalversion. Ausserdem ist die Programmlänge der Assemblerversion immer wesentlich kürzer. Die iAPX86 (PC/MS-DOS) Version (Auflistung): 0000 .TITLE HEXNIBBLE 0000 .PROC IAPX86 2710 00002710 .LOC 10000 2710 RESULT: ; address > 8 bit: 2710 HEX: ; assembler gets 16 bit 0000 00000000 .LOC 0 0000 HEXNIBBLE: 0000 8A861027 MOVE.8 [SS]+{BP}+RESULT,AL 0004 31C9 XOR.16 CX,CX ; CL = FALSE (=0), 0006 2C30 SUB.8 #"0",AL ; CH = VALUE 0008 7211 JUMP,LO END$ 000A 3C09 COMP.8 #9,AL 000C 760A JUMP,LS OK$ 000E 2C07 SUB.8 #"A"-"0"-10,AL 0010 3C0A COMP.8 #10,AL 0012 7207 JUMP,LO END$ 0014 3C0F COMP.8 #15,AL 0016 7703 JUMP,HI END$ 0018 88C5 OK$: MOVE.8 AL,CH ; nibble 001A 41 INC.16 CX ; CL = TRUE (=1) 001B 88AE1027 END$: MOVE.8 CH,[SS]+{BP}+RESULT 001F 888E1027 MOVE.8 CL,[SS]+{BP}+HEX Prozedur: FUNCTION HEXNIBBLE(VAR H:INTEGER):BOOLEAN; {in: ActCh, out: H (value), HEXNIBBLE (true or false)} VAR RESULT:INTEGER; HEX:BOOLEAN; BEGIN RESULT:=ORD(UpCase(ActCh)); INLINE( $8A/$86/RESULT/ (* MOVE.8 [SS]+{BP}+RESULT,AL *) $31/$C9/ (* XOR.16 CX,CX;CL=FALSE (=0), CH=VALUE*) $2C/$30/ (* SUB.8 #"0",AL *) $72/$11/ (* JUMP,LO END$ *) $3C/$09/ (* COMP.8 #9,AL *) $76/$0A/ (* JUMP,LS OK$ *) $2C/$07/ (* SUB.8 #"A"-"0"-10,AL *) $3C/$0A/ (* COMP.8 #10,AL *) $72/$07/ (* JUMP,LO END$ *) $3C/$0F/ (* COMP.8 #15,AL *) $77/$03/ (* JUMP,HI END$ *) $88/$C5/ (*OK$: MOVE.8 AL,CH ; nibble *) $41/ (* INC.16 CX ; CL = TRUE (=1) *) $88/$AE/RESULT/ (*END$: MOVE.8 CH,[SS]+{BP}+RESULT *) $88/$8E/HEX); (* MOVE.8 CL,[SS]+{BP}+HEX *) HEXNIBBLE:=HEX; H:=RESULT; END; CALM-Assembler Bedienungsanleitung Seite: 8 Die Z80 (CP/M-80) Version (Auflistung): 0000 .TITLE HEXNIBBLE 0000 .PROC Z80 2710 00002710 .LOC 10000 2710 RESULT: 2710 HEX: 0000 00000000 .LOC 0 0000 HEXNIBBLE: 0000 3A1027 MOVE RESULT,A 0003 010000 MOVE #0,BC ; C = FALSE (=0), 0006 D630 SUB #"0",A ; B = VALUE 0008 3810 JUMP,LO R8^END$ 000A FE0A COMP #9+1,A 000C 380A JUMP,LO R8^OK$ 000E D607 SUB #"A"-"0"-10,A 0010 FE0A COMP #10,A 0012 3806 JUMP,LO R8^END$ 0014 FE10 COMP #15+1,A 0016 3002 JUMP,HS R8^END$ 0018 47 OK$: MOVE A,B ; nibble value 0019 0C INC C ; TRUE (=1) 001A 78 END$: MOVE B,A 001B 321027 MOVE A,RESULT 001E 79 MOVE C,A 001F 321027 MOVE A,HEX Prozedur: FUNCTION HEXNIBBLE(VAR H:INTEGER):BOOLEAN; {in: ActCh, out: H (value), HEXNIBBLE (true or false)} VAR RESULT:INTEGER; HEX:BOOLEAN; BEGIN RESULT:=ORD(UpCase(ActCh)); INLINE( $3A/RESULT/ (* MOVE RESULT,A *) $01/$00/$00/ (* MOVE #0,BC ;C=FALSE (=0), B=VALUE*) $D6/$30/ (* SUB #"0",A *) $38/$10/ (* JUMP,LO R8^END$ *) $FE/$0A/ (* COMP #9+1,A *) $38/$0A/ (* JUMP,LO R8^OK$ *) $D6/$07/ (* SUB #"A"-"0"-10,A *) $FE/$0A/ (* COMP #10,A *) $38/$06/ (* JUMP,LO R8^END$ *) $FE/$10/ (* COMP #15+1,A *) $30/$02/ (* JUMP,HS R8^END$ *) $47/ (*OK$: MOVE A,B ; nibble value *) $0C/ (* INC C ; TRUE (=1) *) $78/ (*END$: MOVE B,A *) $32/RESULT/ (* MOVE A,RESULT *) $79/ (* MOVE C,A *) $32/HEX); (* MOVE A,HEX *) HEXNIBBLE:=HEX; H:=RESULT; END; CALM-Assembler Bedienungsanleitung Seite: 9 Erzeugung von .EXE-Programmen mit dem CALM-Assembler Das PC/MS-DOS Betriebssystem kennt zwei Programmtypen: .COM und .EXE. Die .COM-Programme sind Überbleibsel von CP/M-80. Programm-, Daten- und Stapelsegmente sind in einem 64K Speicherbereich. Die Programmausführung beginnt bei 16'100. Und die vier Segmentregisters des iAPX86 (CS, DS, ES und SS) haben alle den gleichen Inhalt und zeigen auf den Beginn eines 64K Segments. Aber wenn ein .COM-Programm ausgeführt wird, werden nicht nur die 64K belegt, sondern auch der ganze freie Speicher. Die .EXE-Programme sind komplizierter. Sie haben eine Vorspann, der Informationen über die Programmlänge, die Anfangswerte des Befehlszählers (CS:IP) und den Stapelzeiger (SS:SP), usw., enthält. Ein .EXE-Programm kann getrennte Programm-, Daten- und Stapelsegmente haben. Die Beschränkung von 64K ist damit aufgehoben. Und ein .EXE-Programm belegt nur den notwendigen Speicherplatz. Es ist möglich, .EXE-Programme mit dem CALM-Assembler zu erzeugen. Dazu muss der Programmierer wissen, wo die Segmente (Programm, Daten, Stapel) sind und wie sie festgelegt werden und wie auf sie zugegriffen wird. Die Programmsegmentlänge ist jedoch auf 64K beschränkt (ohne Tricks). Und die Stapel- und Datensegmente können jeweils auch 64K haben (und mit wenig Aufwand unbegrenzte Länge). Die Programmierung von .EXE-Programmen mit dem CALM-Assembler benötigt einige Sorgfalt. So ist es nicht möglich, ein Segmentregister (DS oder ES) mit einer Konstanten (z.B. mit einer Marke über AX) zu laden, da das Programm an einer anderen (unbekannten) Adresse ausgeführt wird. Die Verschiebbarkeit (Relokation) wird durch den Programmierer gewährleistet. Er legt die Segmentregister korrekt fest und benützt diese als Basisregister. Übrigens ist dieser Programmierstil mit jedem Assembler möglich. Viele .EXE Dateien im PC/MS-DOS System benötigen vor der Ausführung keine Relokation (sie haben keine Relokationseinträge in deren .EXE Vorspann). Um .EXE-Programme zu generieren, benötigen Sie den CALM-Assembler (ASCALM und MUFBIN) und das Prozessormodul iAPX86 (oder -186, -286). Der CALM-Assembler wird ebenfalls zur Erzeugung von .COM-Programmen verwendet. In .COM-Programmen verändern Sie nie die Segmentregister und das Programm beginnt mit .LOC 16'100. In .EXE-Programmen müssen Sie das Datensegment festlegen. Vor der Ausführung legt das System die Segmente CS (Programm) und SS (Stapel) fest. Die Segmente DS und ES (Daten) zeigen auf den PSP (Programmsegmentpräambel). Das .EXE-Programm beginnt in der Assemblerquelldatei immer mit .LOC 0. Ausserdem können Sie eine Startadresse mit .START angeben. Sie können schliesslich die Quelldatei assemblieren und mit MUFBIN Datei i/E in die spezifische .EXE-Binärdatei umwandeln. Die Stapel- und Datenlängen sind jedoch nicht bekannt und müssen eingegeben werden. Zur Zeit unterstützt MUFBIN drei .EXE Speichereinteilungen. Diese drei Möglichkeiten hängen von Ihrer Programmierung ab. Drei entsprechende Beispiele (TESTEXE1.ASM bis TESTEXE3.ASM) liegen dem Modul iAPX86 bei. In diesen Beispielen finden Sie auch weitere Erläuterungen. Die Befehlszeile von MUFBIN sieht wie folgt aus: MUFBIN Eingangsdatei{.MUF} i/E {Stapellaenge/S} {Datenlaenge/D} Die Angaben zwischen den geschweiften Klammern sind fakultativ. i wählt eine der drei .EXE Speichereinteilungen. /S und /D legen die Länge des Stapels und der Daten (falls notwendig) fest. Beispiel (Fall 2): MUFBIN TESTEXE2 2/i 80/S 104/D CALM-Assembler Bedienungsanleitung Seite: 10 Einsatz von lokalen Marken Lokale Marken (z.B. LOOP$) unterscheiden sich eigentlich nicht von globalen Marken (z.B. START). Aber lokale Marken erscheinen nicht in der Querverweisliste, da ihre Bedeutung - entsprechend ihrer Namensgebung - nur lokal ist. Ausserdem sind lokale Marken nur zwischen zwei globalen Marken gültig/aufrufbar. Deshalb werden lokale Marken vorzugsweise in Unterprogrammen eingesetzt, wo sie die Punkte für Schlaufen, Bedingungen und Abschluss markieren. Beispiel: TEXTHL: PRINTC$ = 2 ; lokale Zuweisung PUSH HL ; in HL ^.ASCIZ LOOP$: MOVE {HL},A OR A,A JUMP,EQ R8^END$ ; erreicht ? PUSH HL MOVE A,E MOVE #PRINTC$,C ; Zeichen am Bildschirm ausgeben CALL BDOS POP HL INC HL JUMP LOOP$ END$: POP HL RET LOOP$ und END$ können ebenfalls in anderen Unterprogrammen verwendet werden. Man muss also nicht dauernd neue Namen erfinden wie z.B. LOOP1, LOOP2, usw. Ausserdem erscheint nur TEXTHL, der Name und Einstiegspunkt des Unterprogramms, in der Querverweisliste. Automatische Optimisierung von Sprungbefehlen Bei vielen 8-Bit-Mikroprozessoren gibt es zwei Adressierungsmöglichkeiten für Sprungbefehle: 1) JUMP R8^Marke (relat. Adressierung, APC-128..APC+127, 2 Bytes) 2) JUMP 16^Marke (absolute Adressierung, 0..16'FFFF, 3 Bytes) Wenn die Adressenangaben R8^ und 16^ fehlen, wählt der CALM-Assembler automatisch entweder Fall 1) oder 2). Doch wann wird Fall 1) erzeugt? Fall 1) hat ja zwei Vorteile gegenüber Fall 2): relative Adressierung (adressenunabhängig) und kürzerer Maschinenkode (2 statt 3 Bytes). Betrachten wir folgende Situation: ... VORHER: ... JUMP VORHER ; (a) ... JUMP NACHHER ; (b) ... NACHHER: Halten wir fest: JUMP VORHER (a) springt zu einer Marke, die vor dem Sprungbefehl definiert wird. JUMP NACHHER (b) springt zu einer Marke, die nach dem Sprungbefehl definiert wird. Der CALM-Assembler wird nur bei (a) vielleicht die relative Adressierung erzeugen. Dazu muss der Abstand vom APC des JUMP-VORHER-Befehls zur Marke VORHER kleiner 129 sein. Ist der Abstand grösser, wählt der CALM-Assembler die absolute Adressierung. CALM-Assembler Bedienungsanleitung Seite: 11 Bei (b) wird der CALM-Assembler immer die absolute Adressierung wählen, auch wenn NACHHER nicht weiter als 127 Bytes von (b) entfernt ist. Dies hat folgenden Grund: Der CALM-Assembler liest ein Quellenprogramm zwei Mal (zwei Durchläufe), um das Maschinenprogramm zu erzeugen. Im ersten Durchlauf trifft der CALM-Assembler bei (b) auf die noch unbekannt Marke NACHHER. Da der CALM-Assembler den Wert nicht abschätzen kann, nimmt er den schlechtesten Fall an (d.h. die Adresse NACHHER ist weit entfernt) und erzeugt einen absoluten Sprungbefehl (3 Bytes). Der erste Durchlauf dient ja nur dazu, die richtigen Adressen zu berechnen. Im zweiten Durchlauf trifft der CALM-Assembler bei (b) auf die jetzt bekannte Marke NACHHER. In manchen Fällen wäre der Abstand tatsächlich kleiner als 128, d.h. eine relative 8-Bit-Adressierung wäre möglich. Aber jetzt ist dies unmöglich: Würde der CALM-Assembler hier einen relativen Sprungbefehl einsetzen (2 Bytes), dann würden sich auch alle nachfolgenden Adressen (NACHHER,...) um ein Byte verschieben! Deshalb muss der CALM-Assembler bei (b) den gleichen Befehl wie im ersten Durchlauf einsetzen, d.h. einen absoluten Sprungbefehl. Will man dagegen Fall 1) oder Fall 2) erzwingen, so muss man die Adressenangabe R8^ oder 16^ hinzufügen. Das Gesagte gilt sinngemäss für andere automatische Optimisierungen. Erweiterungen des CALM-Assemblers Der CALM-Assembler ist ein Makroassembler, d.h. es werden auch Makros behandelt. Mehrfachdefinitionen bei Zuweisungen und Marken sind erlaubt, wenn die Symbole den gleichen Namen und den gleichen Wert haben (CR = 13; CR = 16'D). Lokale Zuweisungen (A$ = 10) sind erlaubt und haben die gleichen Möglichkeiten wie die lokalen Marken (A$:). Mehrfache Zuweisungen (A == 10) sind möglich und der Wert kann geändert werden (A == 20). Entspricht SET bei anderen Assemblern. Normale Zuweisungen (A = 10) können nicht mit mehrfachen Zuweisungen (A == 20) gemischt werden. Die Angabe der Zahlenbasis ist auch mit Buchstaben möglich: Basis mit Zahl oder Buchstabe Beispiel 16 16'nnnn H'nnnn X'nnnn H'AF 10 10'nnnn D'nnnn D'100 8 8'nnnn O'nnnn Q'nnnn Q'377 2 2'nnnn B'nnnn B'110