;		-- NEW MACRO LIBRARY --
;
;						BY S. J. SINGER
;						REVISION - OCT 1978
;
;
;	SAVE MACRO	SAVE SPECIFIED REGISTERS
;
;	SAVE	R1,R2,R3,R4
;
;		R1-R4 MAY BE B,D,H OR PSW  SAVED IN ORDER SPECIFIED
;		IF REGS ARE OMITTED SAVE B,D AND H
;
SAVE	MACRO	R1,R2,R3,R4
	IF NOT NUL R1&R2&R3&R4
	IRP	R,<<R1>,<R2>,<R3>,<R4>>
	IF	NUL R
	EXITM
	ENDIF
	PUSH	R
	ENDM
	ELSE
	IRPC	REG,BDH
	PUSH	REG
	ENDM
	ENDIF
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	RESTORE MACRO	RESTORE REGISTERS  (INVERSE OF SAVE)
;
;	RESTORE	R1,R2,R3,R4
;
;		R1-R4 MAY BE B,D,H OR PSW  RESTORED IN ORDER SPECIFIED
;		IF REGS OMITTED RESTORE H,D AND B
;
RESTORE	MACRO	R1,R2,R3,R4
	IF	NOT NUL R1&R2&R3&R4
	IRP	R,<<R1>,<R2>,<R3>,<R4>>
	IF	NUL R
	EXITM
	ENDIF
	POP	R
	ENDM
	ELSE
	IRPC	REG,HDB
	POP	REG
	ENDM
	ENDIF
	ENDM
;
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	SAV MACRO	SAVE REGISTERS B,D AND H
;
SAV	MACRO
	PUSH	B
	PUSH	D
	PUSH	H
	ENDM
;
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;
;	RES MACRO RESTORE REGISTERS H,D, AND B (INVERSE OF SAV)
;
RES	MACRO
	POP	H
	POP	D
	POP	B
	ENDM
;
;
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	CHARIN MACRO	CONSOLE INPUT TO A
;
;	CHARIN	ADDR
;
CHARIN	MACRO	ADDR
	MVI	C,1		;;CONSOLE INPUT
	CALL	5		;;CALL BDOS
	IF	NOT NUL ADDR
	STA	ADDR
	ENDIF
	ENDM
;
;
;	. . . . . . . . . . . . . . ... ... . .. . . . . . . . .
;
;	CHAROUT MACRO	CONSOLE OUTPUT FROM A
;
;	CHAROUT	ADDR
;
CHAROUT	MACRO	ADDR
	IF	NOT NUL ADDR
	LDA	ADDR
	ENDIF
	MVI	C,2		;;CONOUT
	MOV	E,A		;;CHAR TO E
	CALL	5		;;CALL BDOS
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	CHARSTAT MACRO	CHECK CONSOLE STATUS
;
;			RETURN TRUE (FF) IF CHAR READY FALSE (0) IF NOT
;
CHARSTAT MACRO
	LOCAL	EXIT
	MVI	C,11
	CALL	5
	ORA	A
	JZ	EXIT
	MVI	A,0FFH
EXIT:	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	INPUT MACRO	INPUT CHARACTER STRING FROM CONSOLE
;
;	INPUT	ADDR,BUFLEN
;
;		ADDR	START OF TEXT BUFFER
;		BUFLEN	LENGTH OF BUFFER  (DEFAULT IS 127)
;
INPUT	MACRO	ADDR,BUFLEN
	MVI	C,10
	IF	NOT NUL ADDR
	LXI	D,ADDR		;;SET BUFFER ADDRESS
	ENDIF
	IF	NOT NUL BUFLEN
	MVI	A,BUFLEN	;;SET BUFFER LENGTH
	STAX	D
	ELSE
	MVI	A,127
	STAX	D		;;SET BUFFER DEFAULT MAXIMUM
	ENDIF
	CALL	5		;;BDOS ENTRY
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	PRINT MACRO	PRINT A STRING ON CONSOLE
;
;	PRINT				(CARRIAGE RETURN, LINE FEED)
;	PRINT	'LITERAL'
;	PRINT	<'LITERAL',CR,LF,'SECOND LITERAL'>
;
;	PRINT	ADDR,$			(ASCII OUTPUT UNTIL $)
;	PRINT	ADDR,L,H		(HEX OUTPUT L CHARACTERS)
;	PRINT	ADDR,L,A		(ASCII OUTPUT L CHARACTERS)
;
;		LITERALS MUST BE IN SINGLE QUOTES  'LIT'
;		IF LITERAL CONTAINS CONTROL CODES ENTIRE STRING IN <> BRACKETS
;		MACRO ALSO ASSEMBLES
;			CR = CARRIAGE RETURN
;			LF = LINE FEED
;			BEL = BELL CODE
;
;		MACRO ASSUMES ADDR ALREADY LOADED TO HL IF ARGUMENT OMITTED
;
PRINT	MACRO	?STRING,LEN,TC
	LOCAL	@OVER,@MESS,PLOOP,PASTCR,@CRLF
CR	SET	0DH
LF	SET	0AH
BEL	SET	07H
	IF	NUL ?STRING&LEN&TC
	JMP	PASTCR
@CRLF:	DB	CR
	DB	LF
	DB	'$'
PASTCR:	LXI	D,@CRLF
	MVI	C,9
	CALL	5
	ELSE
	IF	NUL LEN&TC
	JMP	@OVER
@MESS:	DB	?STRING
	DB	'$'
@OVER:	LXI	D,@MESS
	MVI	C,9
	CALL	5		;;BDOS ENTRY
	ELSE
	IF	NUL TC
	IF	NOT NUL ?STRING
	LXI	D,?STRING	;;POINTER TO STRING
	ENDIF
	MVI	C,9
	CALL	5		;;BDOS ENTRY
	ELSE
	IF	NOT NUL ?STRING
	LXI	H,?STRING	;;POINTER TO STRING
	ENDIF
	MVI	C,LEN		;;LENGTH OF STRING
PLOOP:	PUSH	B
	PUSH	H
	IF	TC=H
	MOV	A,M		;;GET A BYTE
	HEXOUT			;;CONV TO HEX & OUTPUT
	ELSE
	MOV	E,M		;;GET A BYTE
	MVI	C,2		;;OUT FROM E
	CALL	5
	ENDIF
	POP	H
	POP	B
	INX	H
	DCR	C		;;DECR LENGTH
	JNZ	PLOOP		;;CONTINUE TILL LEN 0
	ENDIF
	ENDIF
	ENDIF
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	HEXOUT MACRO	CONVERT BINARY NO AND OUTPUT TO CONSOLE
;
;	HEXOUT	ADDR
;
;		NUMBER ASSUMED IN A IF NO ARGUMENT
;
HEXOUT	MACRO	ADDR
	LOCAL	OUTCHR,HEXEND
	JMP	HEXEND
HEXPRN:	PUSH	PSW
	RRC
	RRC
	RRC
	RRC			;;SHIFT RIGHT 4
	CALL	OUTCHR
	POP	PSW
OUTCHR: ANI	0FH		;;MASK 4 BITS
	ADI	90H		;;ADD OFFSET
	DAA			;;DEC ADJUST
	ACI	40H		;;ADD OFFSET
	DAA			;;DEC ADJUST
	MOV	E,A		;;TO E FOR OUTPUT
	MVI	C,2		;;CONOUT
	JMP	5		;;CALL BDOS
HEXEND:
HEXOUT	MACRO	?ADDR
	IF	NOT NUL ?ADDR
	LDA	?ADDR
	ENDIF
	CALL	HEXPRN
	ENDM
	HEXOUT	ADDR
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	HEXIN MACRO	CONVERT A NUMBER IN MEMORY FROM HEX TO BINARY
;
;			IF NO ARGUMENT MACRO ASSUMES ADDR OF HEX STRING IN HL
;			ANSWER LEFT IN HL WITH LEAST SIGNIFICANT 8 BITS IN A
;			CARRY SET ON NON HEX DIGIT OTHER THAN SPACE OR ZERO.
;			CONVERSION STOPS ON SPACE OR ZERO.
;
HEXIN	MACRO	ADDR
	LOCAL	IN1,IN2,OVERSUB
	JMP	OVERSUB
@HEXIN	LXI	H,0		;;ZERO NUMBER
IN1:	LDAX	D		;;GET A CHAR
	ORA	A
	RZ			;;RETURN IF ZERO IN FIELD
	CPI	20H		;;SPACE WORKS TOO
	RZ
	SUI	'0'		;;CHECK < 0 AND CONVERT TO HEX
	RC			;;RETURN, CHAR < 0 FOUND
	ADI	'0'-'G'		;;CHECK > F
	RC
	ADI	6
	JP	IN2		;;NO BETWEEN A AND F
	ADI	7
	RC
IN2:	ADI	10
	ORA	A		;;CLEAR CARRY
	MOV	C,A		;;HEX DIGIT TO C
	MVI	B,0		;;ZERO TO B
	DAD	H
	DAD	H
	DAD	H
	DAD	H		;;SHIFT LEFT 4
	DAD	B		;;ADD IN NEW DIGIT
	INX	D		;;INCR BUFFER POINTER
	JMP	IN1		;;RETURN FOR MORE INPUT
OVERSUB:
HEXIN	MACRO	?ADDR
	IF NOT NUL ?ADDR
	LXI	D,?ADDR		;;LOAD BUFFER ADDR
	ELSE
	XCHG
	ENDIF
	CALL	@HEXIN
	MOV	A,L		;;LEAST SIGNIFICANT 8 BITS TO A
	ENDM
	HEXIN	ADDR
	ENDM
;
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	DECOUT MACRO	CONVERT A POSITIVE INTEGER TO DECIMAL AND OUTPUT 
;			TO THE CONSOLE.
;
;	DECOUT	ADDR
;
;		IF ADDR OMITTED, NUMBER ASSUMED TO BE IN HL, ELSE LOADED TO HL
;		LEADING ZEROS SUPRESSED. MAXIMUM NUMBER 65,767
;
DECOUT	MACRO	ADDR
	LOCAL	ENDDEC,DX
	JMP	ENDDEC
@DECOUT: PUSH	B		;PUSH STACK
	PUSH	D
	PUSH	H
	LXI	B,-10		;;RADIX FOR CONVERSION
	LXI	D,-1		;;THIS BECOMES NO DIVIDED BY RADIX
DX:	DAD	B		;;SUBTRACT 10
	INX	D
	JC	DX
	LXI	B,10
	DAD	B		;;ADD RADIX BACK IN ONCE
	XCHG
	MOV	A,H
	ORA	L		;;TEST FOR ZERO
	CNZ	@DECOUT		;;RECURSIVE CALL
	MOV	A,E
	ADI	'0'		;;CONVERT FROM BCD TO HEX
	MOV	E,A		;;TO E FOR OUTPUT
	CHAROUT			;;CONSOLE OUTPUT
	POP	H		;;POP STACK
	POP	D
	POP	B
	RET
ENDDEC:
DECOUT	MACRO	?ADDR
	IF	NOT NUL ?ADDR
	LHLD	?ADDR
	ENDIF
	CALL	@DECOUT		;;CALL THE SUBROUTINE
	ENDM
	DECOUT	ADDR
	ENDM
;
;
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	DECIN MACRO	CONVERT A NUMBER IN MEMORY FROM ASCII TO BINARY
;
;	DECIN	ADDR
;
;		ADDR POINTS TO MEMORY LOCATION OF START OF NO, IF
;		ARG OMITTED POINTER ASSUMED LOADED TO HL
;		MACRO RETURNS WITH CARRY SET IF ALPHABETIC CHAR FOUND
;		CONVERSION STOPS WHEN CHAR LESS THAN ZERO IS FOUND.
;		BINARY NUMBER IS LEFT IN HL, MAXIMUM 65,767
;		LEAST SIGNIFICANT 8 BITS OF NUMBER IN A.
;
DECIN	MACRO	ADDR
	LOCAL	DLOOP,OVERSUB
	JMP	OVERSUB
@DECIN:	LXI	D,0		;;ZERO DE
	XCHG			;;ADDR POINTER TO DE, ZERO TO HL
DLOOP:	LDAX	D		;;GET A ASCII DIGIT
	SUI	'0'		;;CONVERT TO BCD AND TEST
	ANA	A		;;RESET CARRY
	RM			;;TERMINATE CONVERSION IF < ZERO
	CPI	10		;;CHECK LEGITIMATE DIGIT (0-9)
	CMC			;;COMPLEMENT CARRY
	RC			;;RET WITH CARRY SET IF ERROR
	INX	D		;;INCR ADDR POINTER
	DAD	H		;;SHIFT LEFT 1
	PUSH	H		;;SAVE RESULT
	DAD	H
	DAD	H		;;SHIFT LEFT 2
	POP	B		;;NO * 2 TO B
	DAD	B		;;HL NOW CONTAINS 10*NO
	MOV	C,A		;;ADD PRODUCT TO DIGIT
	MVI	B,0
	DAD	B
	JMP	DLOOP		;;BACK FOR ANOTHER DIGIT
OVERSUB:
DECIN	MACRO	?ADDR
	IF	NOT NUL	?ADDR
	LXI	H,?ADDR
	ENDIF
	CALL	@DECIN		;;CALL THE SUBROUTINE
	MOV	A,L		;;LEAST SIGNIFICANT HALF OF NO TO A
	ENDM
	DECIN	ADDR
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	MOVE MACRO	MOVE A BLOCK FROM SOURCE TO DEST
;
;	MOVE	SOURCE,DEST,COUNT
;
;		SOURCE TO HL	MACRO ASSUMES REGISTERS ALREADY
;		DEST TO DE	LOADED IF ARG OMITTED
;		COUNT TO BC
;
MOVE	MACRO	SOURCE,DEST,COUNT
	LOCAL	OVERSUB
	JMP	OVERSUB
@MOVE:	MOV	A,B
	ORA	C
	RZ			;;EXIT COUNT ZERO
	MOV	A,M		;;GET A BYTE
	STAX	D		;;STORE IT
	INX	H
	INX	D
	DCX	B
	JMP	@MOVE		;;BACK TO MOVE LOOP
OVERSUB:
MOVE	MACRO	SRC,?D,?C
	IF	NOT NUL SRC
	LXI	H,SRC
	ENDIF
	IF	NOT NUL ?D
	LXI	D,?D
	ENDIF
	IF	NOT NUL ?C
	LXI	B,?C
	ENDIF
	CALL	@MOVE		;;CALL THE MOVE SUBROUTINE
	ENDM
	MOVE	SOURCE,DEST,COUNT
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	FILL MACRO - FILL A BLOCK OF MEMORY WITH A CONSTANT
;
;	FILL	START,STOP,CONSTANT
;
;		CONSTANT OMITTED, FILL WITH 0
;		END OMITTED, FILL ONE BYTE
;
FILL	MACRO	START,STOP,CONST
	LOCAL	@FILL,BLKLEN
BLKLEN	SET	STOP-START+1
	LXI	H,START		;;LOAD START ADDR
	IF	NOT NUL STOP
	IF	BLKLEN > 255
	LXI	B,BLKLEN	;;LOAD BLOCK LENGTH
	ELSE
	MVI	C,BLKLEN
	ENDIF
	IF	NOT NUL CONST
	MVI	E,CONST		;;LOAD CONST IF NOT NULL
	ELSE
	MVI	E,0
	ENDIF
@FILL:	MOV	M,E		;;STORE A BYTE
	INX	H		;;INCR MEMORY POINTER
	IF	BLKLEN > 255
	DCX	B		;;DECR COUNT
	MOV	A,C		;;TEST LIMIT
	ORA	B
	JNZ	@FILL		;;CONTINUE
	ELSE
	DCR	C
	JNZ	@FILL
	ENDIF
	ELSE
	IF	NUL CONST
	MVI	M,0		;;STORE A ZERO
	ELSE
	MVI	M,CONST		;;STORE SINGLE BYTE
	ENDIF
	ENDIF
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;
;	MATCH MACRO	COMPARE 2 STRINGS OF SAME LENGTH SET CARRY IF EQUAL
;
;	MATCH	STR1,'LITERAL STRING'
;	MATCH	STR1,STR2,LENGTH
;	MATCH
;
;		DE POINTS TO STR1	MACRO WILL LOAD REG IF ARG
;		HL POINTS TO STR2	PRESENT
;		C CONTAINS LENGTH
;
;		SUBTRACT STR2 FROM STR1 AND SET FLAGS, ZERO INDICATES MATCH.
;		NORMALLY THE SECOND ARG IS A LITERAL STRING AND THE LENGTH
;		IS OMITTED. IF THE LEN ARG IS PRESENT THE SECOND STRING
;		ARG IS ASSUMED TO BE A MEMORY ADDR. IF ALL ARGUMENTS OMITTED
;		REGISTERS ASSUMED ALREADY LOADED.
;
MATCH	MACRO	STR1,STR2,LEN
	LOCAL	OVERSUB,M1
	JMP	OVERSUB
@MATCH:	INR	C		;;PRE INCREMENT COUNT (IT MIGHT BE ZERO)
M1:	DCR	C		;;DECR LENGTH COUNT
	RZ			;;RETURN IF MATCH FOUND
	LDAX	D		;;GET A BYTE FROM ONE STRING
	SUB	M		;;COMPARE WITH OTHER
	RNZ			;;RETURN
	INX	H
	INX	D		;;INCR STRING POINTERS
	JMP	M1		;;TRY SOME MORE
OVERSUB:
MATCH	MACRO	?STR1,?STR2,?LEN
	LOCAL	LITSTR,ENDLIT
	IF	NUL ?STR1&?STR2&?LEN
	CALL	@MATCH
	ELSE
	IF	NOT NUL ?STR1
	LXI	D,?STR1		;;LOAD STRING1 POINTER
	ENDIF
	IF	NUL ?LEN	;;TEST FOR LITERAL
	MVI	C,ENDLIT-LITSTR	;;LENGTH OF LITERAL STRING
	LXI	H,LITSTR	;;POINTER TO LITERAL
	CALL	@MATCH
	JMP	ENDLIT
LITSTR:	DB	?STR2		;;LITERAL STRING
ENDLIT:				;;END OF STRING
	ELSE
	IF	NOT NUL ?STR2
	LXI	H,?STR2		;;LOAD POINTER TO STRING2
	ENDIF
	MVI	C,?LEN		;;LOAD STRING LENGTH
	CALL	@MATCH		;;CALL MATCH SUBROUTINE
	ENDIF
	ENDIF
	ENDM
	MATCH	STR1,STR2,LEN
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	INSTR MACRO	SEARCH STRING FOR SUBSTRING AND SET CARRY IF FOUND
;
;	INSTR	STRING,LENGTH,SUBSTR
;
;		HL	POINTS TO STRING
;		DE	POINTS TO SUBSTRING
;		B	CONTAINS STRING LENGTH
;		C	CONTAINS SUBSTRING LENGTH
;
;		MACRO RETURNS POINTER TO END OF SUBSTRING IN NL
;
INSTR	MACRO	STRING,LENGTH,SUBSTR
	LOCAL	OVERSUB,S1,SSX
	JMP	OVERSUB
@INSTR:	MOV	A,B		;;STRING LENGTH
	SUB	C		;;SUBTRACT SUBSTR LENGTH
	CMC			;;COMP CARRY
	RNC			;;ERROR RETURN SUBSTR > STRING
	MOV	B,A		;;NEW STRING LIMIT TO B
S1:	PUSH	B
	PUSH	D
	PUSH	H
	MATCH
	POP	H
	POP	D
	POP	B
	JZ	SSX		;;MATCH IF ZERO ON RET
	ANA	A		;;RESET CARRY
	DCR	B		;;BYTES LEFT
	RM			;;FINISHED IF MINUS, NO MATCH
	INX	H		;;INCR STRING POINTER
	JMP	S1		;;TRY AGAIN
SSX:	MVI	B,0		;;SET D TO 0
	DAD	B
	STC			;;SET CARRY
	RET
OVERSUB:
INSTR	MACRO	?STR,?LEN,?SUBSTR
	LOCAL	LITSTR,ENDLIT
	IF	NUL ?STR&?LEN&?SUBSTR
	CALL	@INSTR
	ELSE
	IF	NOT NUL ?STR
	LXI	H,?STR
	ENDIF
	MVI	B,?LEN
	MVI	C,ENDLIT-LITSTR
	LXI	D,LITSTR
	CALL	@INSTR
	JMP	ENDLIT
LITSTR:	DB	?SUBSTR
ENDLIT:
	ENDIF
	ENDM
	INSTR	STRING,LENGTH,SUBSTR
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	SCAN MACRO	SCAN A STRING UNTIL A CHAR IS FOUND, SKIP BLANKS
;			AND CONTROL CHARACTERS
;
;			ZERO SET IF NO DATA FOUND IN SPECIFIED FIELD
;
;
SCAN	MACRO	ADDR,LENGTH
	LOCAL	OVERSUB
	JMP	OVERSUB
@SCAN:	MOV	A,M		;;GET A BYTE
	CPI	21H		;;SPACE OR LESS?
	RP
	INX	H		;;INCR POINTER
	DCR	C		;;DECR SCAN COUNT
	RZ			;;RETURN IF COUNT ZERO
	JMP	@SCAN		;;KEEP SEARCHING
OVERSUB:
SCAN	MACRO	?ADDR,?LEN
	IF	NOT NUL ?ADDR
	LXI	H,?ADDR
	ENDIF
	IF	NOT NUL ?LEN
	MVI	C,?LEN
	ELSE
	MVI	C,127		;;DEFAULT SCAN LENGTH 127 BYTES
	ENDIF
	CALL	@SCAN		;;CALL SUBROUTINE
	ENDM
	SCAN	ADDR
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	DISKIO MACRO	EXECUTE BDOS DISK ACCESS PRIMITIVES
;
;	DISKIO	FUNCTION,PARAMETER
;
;		NO	FUNCTION	ENTRY PARAM
;
;		12	LIFTHEAD
;		13	INITIAL
;		14	LOGIN		DISK NO 0 - 1
;		15	OPEN		FCB
;		16	CLOSE		FCB
;		17	SEARCH		FCB
;		18	SERNXT		FCB
;		19	DELETE		FCB
;		20	READ		FCB
;		21	WRITE		FCB
;		22	MAKE		FCB
;		23	RENAME		FCB
;		24	?LOGIN
;		25	?DRIVE
;		26	SETDMA		BUFFER
;		27	?ALLOC
;		SEE CP/M INTERFACE GUIDE FOR DETAILED INFORMATION ON THE
;		DISK ACCESS PRIMITIVES
;
;	DISKIO	READ,FCB	(TYPICAL MACRO CALL)
;
DISKIO	MACRO	FUNCTION,PARAMETER
LIFTHEAD	SET	12
INITIAL		SET	13
LOGIN		SET	14
OPEN		SET	15
CLOSE		SET	16
SEARCH		SET	17
SERNXT		SET	18
DELETE		SET	19
READ		SET	20
WRITE		SET	21
MAKE		SET	22
RENAME		SET	23
?LOGIN		SET	24
?DRIVE		SET	25
SETDMA		SET	26
?ALLOC		SET	27
;
?C	SET	FUNCTION
	IF	NOT NUL PARAMETER
	LXI	D,PARAMETER
	ENDIF
	MVI	C,?C
	CALL	5		;;BDOS ENTRY
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	CALLBIOS MACRO	CALL BIOS ROUTINES DIRECTLY
;
;	CALLBIOS	FUNCTION,PARAM
;
CALLBIOS	MACRO	FUNCT,PARAM
	LOCAL	@CALL
;
DCOLD	SET	00H
DWBOOT	SET	03H
DSTAT	SET	06H
DCONIN	SET	09H
DCONOUT	SET	0CH		;;CHAR IN C
DLIST	SET	0FH		;;CHAR IN C
DPUNCH	SET	12H
DREADER	SET	15H
DHOME	SET	18H
DSELDSK	SET	1BH
DSETTRK	SET	1EH
DSETSEC	SET	21H		;;SECTOR NO IN C
DSETDMA	SET	24H		;;DMA ADDR IN BC
DREAD	SET	27H
DWRITE	SET	2AH
;
?F	SET	FUNCT
	IF	NOT NUL PARAM
	MVI	C,PARAM
	ENDIF
	LHLD	1		;;ADDR OF BIOS
	MVI	L,?F		;;JUMP OFFSET
	SHLD	@CALL+1		;;MODIFY CALL ADDR
@CALL:	CALL	0
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	DLOAD MACRO	DOUBLE PRECISION INDEXED LOAD HL
;
;		LOAD (ADDR + INDX) TO HL
;
DLOAD	MACRO	ADDR,INDX
	IF	NUL INDX
	LHLD	ADDR
	ELSE
	LHLD	INDX
	LXI	D,ADDR
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	ENDIF
	ENDM
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	CPHL MACRO	SUBTRACT DE FROM HL AND SET FLAGS
;
CPHL	MACRO
	LOCAL	@END
	MOV	A,H
	CMP	D		;;COMPARE HIGH BYTES
	JNZ	@END
	MOV	A,L
	CMP	E		;;COMPARE LOW BYTES
@END:	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	DJZ MACRO	DOUBLE PRECISION TEST HL AND JUMP ON ZERO
;
DJZ	MACRO	ADDR
	MOV	A,H
	ORA	L
	JZ	ADDR
	ENDM
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	DSTORE MACRO	DOUBLE PRECISION INDEXED STORE HL
;
;		STORE (HL) IN (ADDR + INDX)
;
DSTORE	MACRO	ADDR,INDX
	IF	NUL INDX
	SHLD	ADDR
	ELSE
	PUSH	H
	LHLD	INDX
	XCHG
	LXI	H,ADDR
	DAD	D
	POP	D
	MOV	M,E
	INX	H
	MOV	M,D
	ENDIF
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	INDEX MACRO	INDEX AN ADDRESS POINTER BY A CONSTANT
;			MOD MODULUS
;
;	INDEX	POINTER,INCR,MODULUS
;
INDEX	MACRO	POINTER,INCR,MOD
	LOCAL	IND1
	LHLD	POINTER
	LXI	D,INCR
	DAD	D		;;DOUBLE ADD
	IF	NUL MOD
	SHLD	POINTER
	ELSE
	PUSH	H
	LXI	D,-MOD
	DAD	D		;;SUBTRACT MODULUS
	JNC	IND1
	POP	H		;;GET BACK NO
IND1:	SHLD	POINTER
	ENDIF
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	FILFCB	MACRO	FILL IN THE ID FIELDS OF FCB
;
;	FILFCB	FCB,IDSTRING
;
;		IDSTRING CONTAINS FILE NAME AND TYPE  (FILNAM.TYP)
;		CARRY SET IF ERROR  (NAME TOO LONG)
;
FILFCB	MACRO	FCB,IDSTRING
	LOCAL	OVERSUB,F1,F2,F3,F4,F5,F6
	JMP	OVERSUB
@FLFCB: MVI	M,0		;;CLEAR FIRST BYTE OF FCB
	INX	H
	PUSH	H		;;SAVE POINTER TO NAME
	MVI	C,11		;;SIZE OF ID FIELD
	MVI	A,' '		;;SPACE TO A
F1:	MOV	M,A		;;FILL NAME WITH SPACES
	INX	H
	DCR	C
	JNZ	F1
	POP	H		;;RESTORE NAME POINTER
	MVI	C,8		;;MAXIMUM SIZE OF NAME
F2:	LDAX	D		;;GET BYTE FROM ID FIELD
	CPI	' '		;;LEADING SPACES?
	JNZ	F3
	INX	D		;;SKIP LEADING SPACES
	JMP	F2
F3:	LDAX	D		;;GET ID BYTE
	CPI	0		;;ZERO END OF FIELD
	RZ
	CPI	' '		;;SPACE END OF FIELD
	RZ
	CPI	'.'		;;PERIOD TYPE SEPARATOR
	JZ	F4		;;DO TYPE
	MOV	M,A		;;STORE NAME BYTE
	INX	H
	INX	D		;;INCR POINTERS
	DCR	C		;;DECR MAXIMUM COUNT
	JP	F3		;;LOOP BACK
	STC			;;SET CARRY NAME TOO LARGE
	RET
F4:	INX	D		;;SKIP THE PERIOD
	MOV	A,C
	ORA	A
	JZ	F6		;;TEST C FOR ZERO
F5:	INX	H
	DCR	C
	JNZ	F5		;;INDEX TO TYPE FIELD
F6:	MVI	C,3		;;SIZE OF TYPE FIELD
F7:	LDAX	D		;;GET ID BYTE
	CPI	0		;;ZERO?
	RZ			;;FINISHED
	CPI	' '		;;SPACE?
	RZ
	MOV	M,A		;;STORE TYPE BYTE
	INX	H
	INX	D		;INCR POINTERS
	DCR	C		;;DECR MAX COUNT
	JNZ	F7		;;LOOP BACK
	RET
OVERSUB:
FILFCB	MACRO	?FCB,?ID
	IF	NOT NUL	?ID
	LXI	D,?ID
	ENDIF
	IF	NOT NUL ?FCB
	LXI	H,?FCB
	ENDIF
	CALL	@FLFCB
	XCHG
	ENDM
	FILFCB	FCB,IDSTRING
	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	SETTRK MACRO	SET AND TEST TRACK NUMBER
;
;			CARRY SET IF > 76
;
SETTRK	MACRO	TRKNO
	LOCAL	ENDTRK
	IF	NOT NUL TRKNO
	LDA	TRKNO
	ENDIF
	CPI	77
	CMC
	JC	ENDTRK
	MOV	C,A		;;TRACK NO TO C
	CALLBIOS DSETTRK
ENDTRK:	ENDM
;
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	SETSEC MACRO	SET AND TEST SECTOR NUMBER
;
;		RETURN WITH CARRY SET < 1 OR > 52
;
SETSEC	MACRO	SECNO
	LOCAL	ENDSEC
	IF	NOT NUL SECNO
	LDA	SECNO
	ENDIF
	ORA	A		;CHECK ZERO
	STC
	JZ	ENDSEC
	CPI	53		;CHECK > 52
	CMC
	JC	ENDSEC
	MOV	C,A		;MOVE TO C
	CALLBIOS DSETSEC
ENDSEC:	ENDM
;	. . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;	HALF MACRO	DIVIDES A 16 BIT NUMBER BY 2
;
HALF	MACRO	I
	LOCAL	OVER
	JMP	OVER
@HALF:	XRA	A		;;CLEAR CARRY
	MOV	A,H
	RAR			;;SHIFT UPPER HALF
	MOV	H,A
	MOV	A,L
	RAR			;;SHIFT LOWER HALF
	MOV	L,A
	RET
OVER:
HALF	MACRO	?I
	IF	NOT NUL ?I
	LHLD	?I
	ENDIF
	CALL	@HALF
	ENDM
	HALF	I
	ENDM
