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 gengen frs erste:
	include	prolog.asm
	[DEBUG]			;Debug-Header fr 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. fr 0dh,0ah
ParaRes	Gleichung fr residente Paragrafen
by	Abk. fr "byte"
wo	Abk. fr "word"
ofs	Abk. fr "offset"
len	Abk. fr "length"
ResizeM	[x1]	Komplettes Programmstck zum Ver„ndern der Speicherblockgr”áe.
	Erfordert die Marke ResEnd bei Angabe ohne Parameter oder der Speicher
	wird ab <x1> freigegeben.
PRINT	<^str>	Programmstck 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. fr JMP SHORT label)
CALLF	[seg:ofs]	erzeugt ohne Parameter den Code 9Ah fr Call Far.
	Mit Parametern anschlieáend die Bytes
JMPF	[seg:ofs]	siehe CALLF
EX	<x1>,<x2>	Austausch von Registern oder Speicherzellen.
	Speziell fr den Austausch der Segmentregister ES und DS
LD	<x1>,<x2>	Laden via Push und Pop, spez. fr 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 fr Int10
KBD	Dasselbe fr Int16
INTR	Master-Makro fr DOS, VID, KBD mit vorangestellter IntNo (ein Muá!)
ALIGNV	<ausr>,[fllb]	Erzeugt ein Alignment in angegebener Ausrichtung,
	Fllbyte defaultm„áig "?".
SDS )	<dest>,<reg>	Fllt 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 fhrendem 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 fr 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 gewnscht 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		;Vornullunterdrckung
	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	;fhrt 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 fhrt 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 zurck
	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: UTF-80