masm
COMMENT ` (haftmann#software) +++FREEWARE+++
MIT HILFE DIESER DATEI KANN MAN GANZ EINFACH UND SCHNELL KÜRZESTE
ASSEMBLER-PROGRAMME SCHREIBEN! Folgende Zeilen genügen fürs erste:
include prolog.asm
[DEBUG] ;Debug-Header für Pseudo-COM's
ret ;1 Byte; DIESES PROGRAMM LÄUFT !!!
endc
Assemblieren mit TASM /zl name[.asm] ;Wozu gibt's den Norton Commander?
Linken mit TLINK /t name[.obj] ;Am besten diese Jobs auf ".ASM" legen!
Ausprobieren mit NAME[.COM] ;fertig!!!
Beschreibung der Funktionen der PROLOG.ASM:
(<>: Pflichtparameter, [] freie Parameter)
PROLOG.ASM schaltet auf folgende Standards:
<IDEAL, MODEL tiny, %TRUNC, %CONDS, %NOMACS, %NOSYMS, CODESEG>
nl NewLine, Abk. für 0dh,0ah
ParaRes Gleichung für residente Paragrafen
by Abk. für "byte"
wo Abk. für "word"
ofs Abk. für "offset"
len Abk. für "length"
ResizeM [x1] Komplettes Programmstück zum Verändern der Speicherblockgröße.
Erfordert die Marke ResEnd bei Angabe ohne Parameter oder der Speicher
wird ab <x1> freigegeben.
PRINT <^str> Programmstück zur Ausgabe eines $-terminierten Strings per
DOS-Funktion.
JN <cc>,<label> Ersetzt fehlenden Jump Near Conditional der 80x86-CPU
bzw. die %JUMPS-Anweisung. Erzegt den 5-Byte-Ersatzcode. Zugelassene
Bedingungen sind z.Z. nur: "z","nz","c","nc" (Klein!!)
JR <label> Erzeugt Short-Sprung (Abk. für JMP SHORT label)
CALLF [seg:ofs] erzeugt ohne Parameter den Code 9Ah für Call Far.
Mit Parametern anschließend die Bytes
JMPF [seg:ofs] siehe CALLF
EX <x1>,<x2> Austausch von Registern oder Speicherzellen.
Speziell für den Austausch der Segmentregister ES und DS
LD <x1>,<x2> Laden via Push und Pop, spez. für Segregs!
XLD <x1>,<x2>,[x3]..[x8] Kettenladebefehl von rechts nach links
DOS [x1],[x2] DOS-Funktionsaufruf; x1=AH, x2=AL. Wenn x1 bzw.
x2 fehlt, wird nur 1 8-Bit-Register geladen. Sind beide angegeben,
wird AX geladen. Fehlen beide, dann nur Aufruf des Int21
Ist x1≥100h wird AX mit dem Wert von x1 geladen und x2 ignoriert!
VID Dasselbe für Int10
KBD Dasselbe für Int16
INTR Master-Makro für DOS, VID, KBD mit vorangestellter IntNo (ein Muß!)
ALIGNV <ausr>,[füllb] Erzeugt ein Alignment in angegebener Ausrichtung,
Füllbyte defaultmäßig "?".
SDS ) <dest>,<reg> Füllt das Doppelwort "dest" mit DS:reg auf. Gegen-
SES ) funktion zu LDS, LES, LSS
SSS )
SCS )
LSSx <reg>,<src> Fehlender LoadSS des 8086
DPS <string> Definiert PASCAL-String mit führendem Längen-Byte.
DCL <string> Definiert DOS-Kommandozeilenstring, wie DPS, jedoch
mit abschließendem 0Dh
DXS <xorbyte>,<char-chain> Definiert gescrambelte Zeichenkette. (Zeichen-
kette in spitzen Klammern ohne '' angeben!! Keine CRLF's u.ä.)
DCD <val> Definiert "val" als numerische Zeichenkette im Speicher.
(zur Ausgabe der [festen] Programmgröße o.ä.) Keine Vorwärtsreferenzen
bitte!
DVT Definiere 1 Programmverteilertabellen-Eintrag: 1 Byte und 1 Word
DZ Definiere nullterminierten String, notfalls nur eine Null
DEBUG Debug-Code kopiert PSP an den COM-Anfang und korrigiert Segmentregs.
Im Turbo Debugger den CALL-Befehl mit F8 übergehen!
ENDC Komfortable END-Anweisung. Mit Beachtung einer evtl. Einbindung
des Makros DEBUG
_OCHR Komplett-Routine zur Zeichenausgabe (Zeichen in AL, VR:-)
Ansprungmarke: OCHR
_AXHX Komplett-Routine zur Zeichen- und Hexzahlausgabe, folgende Marken:
AXHX: AX hex ausgeben
AHEX: AL hex ausgeben
AHEX1: AL-Low-Nibble hex ausgeben
OCHR: AL ASCII ausgeben
Alle folgenden Prozeduren definieren 1 gleichnamiges Label ohne "_"
_ALDEZ Sternis geniale DOS-Uhrzeitkonvertierung, nur für AL=0..99!
_AXDEZ AX dezimal ausgeben 1..5 Zeichen
_UPCASE Upcase-Routine AL->AL
_TOUP Komfort-Upcase mit Umlauten, zieht _UPCASE nach sich
_TOLOW Lowcase-Rountine AL->AL
_ANUM Wandelt ASCII in AL in numerischen Wert <36 um, bei Fehler AL>=36
_INW 1 Word ab [SI] einlesen, BL=Zahlenbasis. Zieht _ANUM nach sich
PA: AX: Zahl, CY=1: Fehler: Gar keine Ziffern oder Zahl zu groß
SI zeigt auf erste Nicht-Ziffer
_INW2 1 Word ab [SI] einlesen, Zahlenbasis mit Präfixen # (dez) und $ (hex)
überschreibbar; PA: s. _INW, dazu BL=Neue Basis. Zieht INW nach sich!
_INW3 wie INW2, jedoch dezimale Vorgabe und C-mäßige Präfixe dazu.
Zieht _INW2 nach sich!
_CHKWS Check AL auf WhiteSpace 0,9,0a,0d,20 PA: Z=1 A ist Whitespace
_CRLF Na was wohl? Aber ohne Register einzusauen! Benötigt _OCHR!
_CASE CASE-Anweisung via Tabelle! Wandelt Byte zu Wort wie UPV
_UPV Unterprogrammverteiler [si]=Optionsbuchstabe, di=Tabelle, s.a. DVT
_IS286 Programm testet ob mindestens 80286; CY=0 wenn ja
_IS386 Programm testet ob 80386; CY=0 wenn ja; schließt _IS286 ein
PSPOrg eine Marke am PSP-Anfang
COMentry hier geht das COM-Programm los!
(Assembler-Programm also mit END COMentry beenden!)
Sollte ausnahmsweise eine .EXE gewünscht werden, empfiehlt sich der
Befehl ORG 0 direkt nach der Include-Anweisung. Dann ist aber ein *anderer*
Eintrittspunkt zu wählen!
`
IDEAL
%NOINCL
MODEL tiny
%TRUNC ;Begrnzen von Strings im Object-Code
%CONDS ;Auch nicht übersetzte IF's listen
%NOMACS ;Keine Makros expandieren (Papier sparen)
%NOSYMS ;Keine "Symboltabelle" bitte!
NOMULTERRS ;Nie mehrere Fehler pro Quellzeile bitte!
nl equ <13,10> ;Zeilenende
ParaRes equ <(ResEnd-PSPOrg+15)/16>
;Residente Paragrafen eines .COM-Programms
by equ <byte>
wo equ <word>
ofs equ <offset>
len equ <length>
macro ResizeM r1
ifb <r1>
mov bx,ParaRes
else
mov bx,(r1-PSPOrg+15) / 16
endif
DOS 4ah
endm
macro PRINT str ;handhabbares Ausgabekommando
mov dx,offset str
mov ah,9
int 21h
endm
macro JN cc,lab ;Jump Near conditional
ifidn <cc>,<c>
jnc $+5
jmp lab
elseifidn <cc>,<nc>
jc $+5
jmp lab
elseifidn <cc>,<z>
jnz $+5
jmp lab
elseifidn <cc>,<nz>
jz $+5
jmp lab
else
err <Unknown condition code>
endif
endm
macro JR lab
jmp short lab ;KC-like
endm
macro SEGOFS r1 ;Internes Makro!
ifnb <r1>
@cxxf1 instr <r1>,<:>
if @cxxf1 lt 2
err <Wrong colon>
endif
@cxxf2 substr <r1>,1,@cxxf1-1
@cxxf3 substr <r1>,@cxxf1+1
dw @cxxf3,@cxxf2
endif
endm
macro CALLF r1 ;r1 Seg:Ofs
db 9ah
segofs <r1>
endm
macro JMPF r1 ;r1: Seg:Ofs
db 0eah
segofs <r1>
endm
macro EX r1,r2 ;zum Vertauschen von Segmentregistern
errifb <r1> <Operand expected>
errifb <r2> <Operand expected>
push r1 r2
pop r1 r2
endm
macro LD r1,r2 ;zum Laden von Segmentregistern
errifb <r1> <Operand expected>
errifb <r2> <Operand expected>
push r2
pop r1
endm
macro XLD r1,r2,r3,r4,r5,r6,r7,r8
ifnb <r8>
mov r7,r8
endif
ifnb <r7>
mov r6,r7
endif
ifnb <r6>
mov r5,r6
endif
ifnb <r5>
mov r4,r5
endif
ifnb <r4>
mov r3,r4
endif
ifnb <r3>
mov r2,r3
endif
mov r1,r2
endm
macro DOS r1,r2
INTR 21h,<r1>,<r2>
endm
macro VID r1,r2
INTR 10h,<r1>,<r2>
endm
macro KBD r1,r2
INTR 16h,<r1>,<r2>
endm
macro LoadAX r1,r2
ifb <r2>
ifnb <r1>
if r1 ge 256
mov ax,r1
else
mov ah,r1
endif
endif
else
ifb <r1>
mov al,r2
else
mov ax,r1*256+r2
endif
endif
endm
macro INTR intno,r1,r2 ;r1=ah, r2=al
LoadAX r1,r2
int intno
endm
;Alignment with Value
macro ALIGNV w1,w2
local w3
ife w1 GT 0
err <False Operand>
endif
; errife (w1 GT 0) <False Operand>
w3 = w1- (($-PSPOrg) MOD w1)
if w3 NE w1
ifnb <w2> ;;Existiert w2?
db w3 dup (w2)
else ;;wenn nicht
db w3 dup (?)
endif
endif
endm
macro SDS dest,reg ;Store DS:reg into dest
mov [wo LOW dest],reg
mov [wo HIGH dest],ds
endm
macro SES dest,reg ;Store ES:reg into dest
mov [wo LOW dest],reg
mov [wo HIGH dest],es
endm
macro SSS dest,reg ;Store SS:reg into dest
mov [wo LOW dest],reg
mov [wo HIGH dest],ss
endm
macro SCS dest,reg ;Store CS:reg into dest
mov [wo LOW dest],reg
mov [wo HIGH dest],ss
endm
macro LSSx reg,src ;Load SS:reg from src
mov reg,[wo LOW src]
mov ss,[wo HIGH src]
endm
;; Aufrufen mit DPS <'Hallo!',13,10>
macro dps w1
local dpsa,dpse
;;Define Pascal String (with length byte)
db LOW (dpse-dpsa)
dpsa: db w1
dpse:
endm
macro dcl w1 ;;Definiere Kommandozeilen-String
dps <w1>
db 13
endm
macro dxs x1,w1 ;Define Xored String
irpc c,<w1>
db '&c' xor x1
endm
endm
;;Note that x MUST BE a value, create it using %-Operator
macro dcd x ;Define Constant String Of A Decimal Word
local deno
deno = 10000
rept 5
if (x/deno GT 0) OR (deno EQ 1)
db ((x/deno) MOD 10)+'0'
endif
deno = deno/10
endm
endm
macro DVT c,w ;;Definiere Verteilertabelleneintrag
db c
dw ofs w
endm
macro DZ str ;;Definiere nullterminierten String
ifnb <str>
db str
endif
db 0
endm
macro entr w1 ;wie ENTER beim 286
if @CPU and 2
enter w1,0
else
push bp
mov bp,sp
ifnb <w1>
sub sp,w1 ;;lokale Variablen
endif
endif
endm
macro leav ;wie LEAVE beim 286
if @CPU and 2
leave
else
mov sp,bp
pop bp
endif
endm
macro _ochr ;Zeichenausgabe aus AL
ochr: push ax dx
mov dl,al
DOS 2
pop dx ax
ret
endm
macro _axhx ;Hexzahlausgabe, VR: AX,F
axhx: xchg al,ah
call ahex
xchg al,ah
ahex: push ax cx ;Werte erhalten
mov cl,4 ;oberes Nibble auf Bit 3...0
shr al,cl ; schieben
pop cx
call ahex1
pop ax
ahex1: and al,0fh
add al,90h ;Hex -> ASCII
daa
adc al,40h
daa
_ochr
endm
macro _ALDEZ ;AL zu Dezimal-String in AX wandeln
aldez:
xor ah,ah
aam ;dividiert AL durch 10
xchg al,ah ;AH=Rest, Low-Teil, AL=High-Teil
add ax,'00' ;fertig zum Einpoken
ret
endm
macro _AXDEZ ;AX dezimal ausgeben
proc axdez
push ax cx dx
xor cx,cx ;Vornullunterdrückung
mov bx,10000
call @@1 ;hinterläßt in ax den Rest!
mov bx,1000
call @@1
mov bx,100
call @@1
mov bx,10
call @@1
add al,'0'
call ochr
pop dx cx ax
ret
@@1: ;Ziffernausgabe, ax=Zahl, bx=Teiler, cx=Vornull-Flag
xor dx,dx ;High-Teil=0
div bx ;ax:=ax/bx, Rest dx (bx Dezimalzahl?!)
push dx
or cx,ax ;Evtl. Ziffer anmelden
or cx,cx ;Immer noch Vornull?
jz @@3 ;Ziffer
add al,'0'
call ochr
@@3: pop ax ;Rest
ret
endp
endm
macro _UPCASE
proc UpCase
cmp al,'a'
jb @@1
cmp al,'z'
ja @@1
and al,not 20h
@@1: ret
endp
endm
macro _TOUP ;Komfort-Upcase mit länderspezifischer Umsetzung (?)
proc ToUp
cmp al,80h
jc @@e
entr 20h
push bx cx dx ds ss
pop ds
lea dx,bp-20h
push ax ;Zeichencode
DOS 3800h
pop ax
jc @@e1
call [dword bp-20h+12h]
@@e1: pop ds dx cx bx
leav
@@e: endp
_UPCASE
endm
macro _TOLOW
proc ToLow
cmp al,'A'
jb UpCas1
cmp al,'Z'
ja UpCas1
or al,20h
@@1 ret
endp
endm
macro _ANUM ;stellt fest, ob AL eine "Ziffer" ist
;Zulässig: 0..9, A..Z, a..z
proc Anum ;gemopst von CAOS NT
;A numerisch wandeln
;PE: A-ASCII-Code
;PA: A: Zahl, die A repräsentierte
;A>=36 wenn nicht im zulässigen Bereich
;VR: AF
SUB al,30H
jc @@e
CMP al,10
jc @@e
SUB al,11H
AND al,not 20h
ADD al,10
@@e: RET
endp
endm
macro _INW ;Liest Word ein PE: BL: Zahlenbasis
;PA: CY=1: Gar keine Ziffern zum Einlesen oder Zahl zu groß
;SI zeigt aufs erste falsche Zeichen
;(Auswertung desselben ist Sache des Hauptprogramms!)
proc InW
push bx cx dx
xor cx,cx
mov bh,ch ;Null
mov al,[si]
call Anum
cmp al,bl
cmc
jc @@e ;;Fehler
@@1: mov ah,0
xchg ax,cx ;;bisherige Zahl nach AX, neue nach CX
mul bx ;;DXAX=BX*AX
add dx,-1
jc @@e ;;Fehler: Zahl zu groß
add cx,ax ;;Zur neuen Ziffer bl*bisherige dazu
jc @@e
inc si
mov al,[si]
call Anum
cmp al,bl
jc @@1 ;;wenn Ziffer klein genug
@@e: xchg ax,cx
pop dx cx bx
ret
endp
_ANUM
endm
macro _INW3 ;;C-mäßige Zahlenauswertung dazu,
;;Dezimalvorgabe! VR:BL
InW3: mov bl,10 ;;Immer dezimal!
cmp [by si],'0'
jnz inw3e
mov bl,8
inc si
cmp [by si],'x'
jnz inw3e
cmp [by si],'X'
jnz inw3e
mov bl,16
inc si
_INW2
endm
macro _INW2 ;wie oben, jedoch Präfixauswertung wie folgt:
;BL: Default-Basis (meist 10 oder 16)
;am Anfang #: Immer dezimal
; $: Immer hex
InW2: cmp [by si],'#'
jnz inw2b
mov bl,10
jr inw2a
inw2b: cmp [by si],'$'
jnz inw2e
mov bl,16
inw2a: inc si
inw2e: _INW
endm
macro _CHKWS
proc ChkWs ;Check for WhiteSpace, Z=1: Es ist welcher
or al,al
jz @@e
cmp al,9
jz @@e
cmp al,' '
jz @@e
cmp al,13
jz @@e
cmp al,10
@@e: ret
endp
endm
macro _CRLF
crlf: push ax
mov al,13
call ochr
mov al,10
call ochr
pop ax
ret
endm
macro _CASE ;führt Case-Anveisung via Tabelle durch
proc case ;PE: DI: Tabelle, AL: Zeichen (Byte); CY=1: Zeichen nicht
;enthalten; dann zeigt DI auf ELSE-Zweig
;Endekennung der Tabelle: Null-Byte! (leichte Einschränkung)
@@r: cmp [by es:di],1
jc @@3
scasb
jz @@2 ;CY=0!
scasw ;Nächstes Wort übergehen
jr @@r
@@3: inc di
@@2: ret
endp
endm
macro _UPV ;zieht nicht mehr _CASE nach sich!
; _CASE
proc upv ;Unterprogrammverteiler nach Tabelle ES:DI
;Holt sich ein Zeichen ab [si] und führt Programm nach
;Tabelle aus
cld
lodsb
call UpCase
call Case ;nach DI
jc @@2
jmp [wo es:di] ;UP rufen; CY bedeutet Abbruchs-Erzwingung,
;z.B. Fehler oder Hilfeseite, AX=Code!
;AX=0: Nur Abbruch, keine zentrale Fehlermeldung
;AX=1: Fehler in Kommandozeile, SI zeigt auf
;unpassendes Zeichen
@@2: mov ax,1
dec si ;Pointer zurück
ret
endp
endm
macro _IS286 ;Is at least 80286?
push sp ;;continue with: jc WrongProcessor
pop ax
cmp ax,sp ;;ax less sp?
endm
macro _IS386
local isn286
_IS286 ;Is at least 80386?
jc isn286
mov ax,7000h
push ax
popf
pushf
pop ax
and ax,7000h
sub ax,1 ;Z->CY
isn286:
endm
macro DEBUG ;Debug-Hilfe, kopiert PSP vornan
local deb01,deb02
org 0
int 20h ;das Original hier
deb01: push cs
pop es
mov si,ofs deb02
mov di,si
mov cx,COMentry-deb02
cld
rep movsb
push cs
pop ds
mov sp,cx ;Null
push cx ;Return zum Int20
jmp si
deb02:
org 0fdh
DEBentry:
call deb01
endm
macro ENDC str ;;komfortable End-Anweisung
local ende1 ;;Wenn str=debug dann eingebaute Debug-Utility
ifb <str>
ifdef DEBentry
ende1 = DEBentry
else
ende1 = COMentry
endif
else
ende1 = str
endif
end ende1 ;;Diese Konstruktion vermeidet Warnings
endm
CODESEG
PSPOrg: ;eine Marke Wert 0 aber verschieblich weil im Codesegment definiert
org 100h
COMentry:
; ENDC ;Semikolon nur zu Testzwecken entfernen!
Detected encoding: OEM (CP437) | 1
|
|