;               -- NEW MACRO LIBRARY --
;
;       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
;
;
;       . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;       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: SAVE    PSW
       RRC
       RRC
       RRC
       RRC                     ;;SHIFT RIGHT 4
       CALL    OUTCHR
       RESTORE 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 ERROR. CONVERSION STOPS WHEN ZERO IS
;                       FOUND IN HEX STRING.
;
HEXIN   MACRO   ADDR
       LOCAL   IN1,IN2,OVERSUB
       JMP     OVERSUB
@HEXIN  LXI     H,0             ;;ZERO NUMBER
IN1:    LDAX    D               ;;GET A CHAR
       ORA     A               ;;CHECK FOR END OF BUFFER
       RZ
       SUI     '0'             ;;CHECK < 0 AND CONVERT TO HEX
       RC
       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:SAVE                    ;;PUSH STACK
       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
       RESTORE                 ;;POP STACK
       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 HL
;
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:     SAVE
       MATCH
       RESTORE
       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      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:
       ENDM
       INSTR   STRING,LENGTH,SUBSTR
       ENDM
;
;       . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;       SCAN MACRO      SCAN A STRING UNTIL A CHAR IS FOUND, SKIP BLANKS
;                       AND CONTROL CHARACTERS
;
;                       CARRY SET IF NUMERIC, CARRY OFF IF ALPHABETIC
;
;
SCAN    MACRO   ADDR
       LOCAL   OVERSUB
       JMP     OVERSUB
@SCAN:  MOV     A,M             ;;GET A BYTE
       CPI     21H             ;;SPACE OR LESS?
       RP
       INX     H               ;;INCR POINTER
       JMP     @SCAN           ;;KEEP SEARCHING
OVERSUB:
SCAN    MACRO   ?ADDR
       IF      NOT NUL ?ADDR
       LXI     H,?ADDR
       ENDIF
       CALL    @SCAN           ;;CALL SUBROUTINE
       CPI     3AH             ;;NUMBER OR ALPHA
       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
       SAVE    H
       LHLD    INDX
       XCHG
       LXI     H,ADDR
       DAD     D
       RESTORE D
       MOV     M,E
       INX     H
       MOV     M,D
       ENDIF
       ENDM
;
;       . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;       INDEX MACRO     INDEX AN ADDRESS POINTER BY A CONSTANT
;
;       INDEX   POINTER,INCR
;
INDEX   MACRO   POINTER,INCR
       LHLD    POINTER
       LXI     D,INCR
       DAD     D               ;;DOUBLE ADD
       SHLD    POINTER
       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 > 26
;
SETSEC  MACRO   SECNO
       LOCAL   ENDSEC
       IF      NOT NUL SECNO
       LDA     SECNO
       ENDIF
       ORA     A               ;CHECK ZERO
       STC
       JZ      ENDSEC
       CPI     27              ;CHECK > 26
       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