Source file: /~heha/hs/dos/mrec_src.zip/PROLOG.ASM

	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
Wrong umlauts? - Assume file is ANSI (CP1252) encoded