;       DTC LOADER
;
TBASE:  EQU     100H
       ORG     TBASE
BOOT:   EQU     0
BDOS:   EQU     5
MEMTOP: EQU     BDOS+1  ;TOP OF AVAILABLE MEMORY
;       BDOS FUNCTIONS
CONOUT: EQU     9       ;CONSOLE OUTPUT
CONIN:  EQU     10      ;CONSOLE INPUT
OPENF:  EQU     15      ;OPEN FILE
CLOSEF: EQU     16      ;CLOSE FILE
SEARCF: EQU     17      ;SEARCH FIRST
READF:  EQU     20      ;READ SECTOR
INTDSK: EQU     25      ;INTERROGATE DISK
;
TFCB:   EQU     05CH    ;DEFAULT FILE CONTROL BLOCK
TET:    EQU     TFCB    ;ENTRY TYPE
TFN:    EQU     TET+1   ;FILE NAME
TFT:    EQU     TFN+8   ;FILE TYPE
TEX:    EQU     TFT+3   ;FILE EXTENT
TRC:    EQU     TEX+3   ;RECORD COUNT
TDM:    EQU     TRC+1   ;DISK ALLOCATION MAP
TNR:    EQU     TDM+16  ;NEXT RECORD NUMBER
;
TBUFF:  EQU     080H    ;DEFAULT BUFFER
;
SECSIZ: EQU     128     ;SECTOR SIZE
;
CR:     EQU     0DH
LF:     EQU     0AH
;
;
LOAD:   LXI     H,0             ;SAVE CP/M STACK POINTER
       DAD     SP
       SHLD    SAVESP
       LXI     SP,STACK        ;SET STACK POINTER
       CALL    INIT            ;INITIALIZE FILE PARAMETERS
;
       MVI     C,0             ;CLEAR BUFFER COUNT
LOAD1:  CALL    GETCHR          ;SCAN TO BEGIN OF LINE
       CPI     ':'
       JNZ     LOAD1
       LXI     D,COUNT         ;GET LOAD COUNT
       CALL    HEXBYT
       LDA     COUNT           ;DONE?
       ORA     A
       JZ      EXIT
       LXI     D,LADDR         ;GET LOAD ADDRESS
       CALL    GETADR
       PUSH    H               ;ADD OFFSET
       LHLD    LADDR
       XCHG
       LHLD    OFFSET
       DAD     D
       LXI     D,LOW           ;IS ADDRESS IN LOADER?
       CALL    COMPAR
       XCHG
       JP      LOAD2
       LXI     D,ERMES         ;YES
       MVI     C,CONOUT
       CALL    BDOS
       JMP     EXIT
ERMES:  DB      'LOAD ADDRESS WITHIN LOADER.',CR,LF,'$'
;
LOAD2:  POP     H               ;RESTORE BUFFER POINTER
       CALL    GETCHR          ;SKIP SEPARATOR BYTE
       CALL    GETCHR
LOAD3:  CALL    HEXBYT          ;LOAD DATA
       PUSH    H
       LXI     H,COUNT         ;DECREMENT COUNT
       DCR     M
       POP     H
       JNZ     LOAD3
       JMP     LOAD1           ;END OF LINE
;
;
;
;       GET CHARACTER FROM RECORD
;       IN - HL=BUFFER POINTER
;             C=BUFFER COUNT
;       OUT-A=CHARACTER
;
GETCHR: XRA     A       ;BUFFER EMPTY?
       CMP     C
       JNZ     GETCH2
       MVI     C,READF ;YES. GET NEXT RECORD
       PUSH    B
       PUSH    D
       CALL    FILEOP
       POP     D
       POP     B
       JZ      GETCH1
EXIT:   CALL    CLOSE
       MVI     C,0     ;RESTORE LOGGED DRIVE
DRIVE:  EQU     $-1
       CALL    $
SELDSK: EQU     $-2
       LHLD    SAVESP  ;RESTORE CP/M STACK POINTER
       SPHL
       RET
;
GETCH1: LXI     H,TBUFF
       MVI     C,SECSIZ
GETCH2: MOV     A,M
       INX     H
       DCR     C
       RET
;
;
;
;
CLOSE:  MVI     C,CLOSEF
FILEOP: LXI     D,TFCB
       CALL    BDOS
       CPI     0
       RET
;       GET ADDRESS
;       IN - HL=4 DIGIT ASCII HEX ADDRESS
;            DE=ADDRESS OF RESULT
;
GETADR: INX     D       ;POINT TO MOST SIGN. BYTE
       CALL    HEXBYT
       DCX     D
       DCX     D
       CALL    HEXBYT
       INX     D
       XRA     A
       RET
HEXBYT: CALL    ASCHEX
       RLC
       RLC
       RLC
       RLC
       MOV     B,A
       CALL    ASCHEX
       ADD     B
       STAX    D
       INX     D
       RET
ASCHEX: CALL    GETCHR
       SUI     '0'
       CPI     10
       RM
       SUI     7
       RET
;       COMPARE HL & DE
;       OUT - FLAGS ARE SET TO RESULT OF HL - DE
;
COMPAR: MOV     A,H
       CMP     D
       RNZ
       MOV     A,L
       CMP     E
       RET
;
;
;       VARIABLES
LADDR:  DW      0       ;LOAD ADDRESS
OFFSET: DW      0       ;LOAD OFFSET
SAVESP: DW      0       ;CP/M STACK POINTER
COUNT:  DW      0       ;LOAD COUNT OF LINE
       DS      26
STACK:
LOW:    EQU     $       ;LOW LOAD ADDRESS
;
;
INIT:   MVI     C,INTDSK        ;SAVE CURRENTLY LOGGED DISK
       CALL    BDOS
       STA     DRIVE
       LHLD    BOOT+1          ;GET SELECT DISK ADDRESS
       MVI     L,3*9
       SHLD    SELDSK
       SHLD    INISEL
       LXI     H,0             ;CLEAR OFFSET
       SHLD    OFFSET
       MVI     B,22            ;SET MAX CHAR SCAN
       LDA     TFN             ;WAS FILE SPECIFIED?
       CPI     ' '
       JNZ     INIT1
       MVI     A,20            ;NO. SET MAX BUFFER LENGTH
       STA     TBUFF
INIT0:  LXI     D,FNMES         ;OUTPUT FILENAME MESSAGE
       MVI     C,CONOUT
       CALL    BDOS
       LXI     D,TBUFF         ;INPUT FILE NAME
       MVI     C,CONIN
       CALL    BDOS
       CALL    CRLF
       LDA     TBUFF+1         ;# CHARS IN BUFFER
       CPI     0
       JZ      INIT0
       MOV     B,A             ;B:=# CHARS IN BUFFER
INIT1:  LXI     H,TBUFF+1
       LDA     TBUFF+3         ;DRIVE SPECIFIED?
       CPI     ':'
       JNZ     INITD3
       INX     H
       MOV     A,M
       SUI     'A'             ;YES. LEGAL DRIVE #
       JM      INITD1
       CPI     16
       JM      INITD2
INITD1: LXI     D,DRVMES        ;NO
       MVI     C,CONOUT
       CALL    BDOS
       JMP     EXIT
DRVMES: DB      'INVALID DRIVE.',CR,LF,'$'
INITD2: PUSH    B
       MOV     C,A             ;SELECT DRIVE
       CALL    $
INISEL: EQU     $-2
       POP     B
       LXI     H,TBUFF+3
INITD3:
       LXI     D,TFCB
INIT2:  INX     H
       INX     D
       MOV     A,M
       CPI     '.'             ;END OF NAME?
       JZ      INIT4
       CPI     ' '
       JZ      INIT7
       CPI     CR
       JZ      INIT9
       CPI     060H            ;NO. LOWER CASE?
       JC      INIT3
       SUI     020H            ;YES. CONVERT TO UPPER CASE
INIT3:  STAX    D
       DCR     B
       JNZ     INIT2
       JMP     INIT9
INIT4:  LXI     D,TFT-1         ;GET FILE TYPE
INIT5:  INX     H
       INX     D
       MOV     A,M
       CPI     ' '
       JZ      INIT7
       CPI     CR
       JZ      INIT9
       CPI     060H
       JC      INIT6
       SUI     020H
INIT6:  STAX    D
       DCR     B
       JNZ     INIT5
       JP      INIT9
INIT7:  INX     H               ;SCAN FOR OFFSET
       MOV     A,M
       CPI     '='
       JZ      INIT8
       CPI     CR
       JZ      INIT9
       DCR     B
       JNZ     INIT7
       JMP     INIT9
INIT8:  INX     H               ;GET OFFSET
       LXI     D,OFFSET
       CALL    GETADR
INIT9:
       LDA     TFT             ;WAS FILE TYPE SPECIFIED?
       CPI     ' '
       JNZ     INIT11
       LXI     H,HEXMES        ;SET FILE TYPE TO HEX
       LXI     D,TFT
       MVI     B,3
INIT10: MOV     A,M
       STAX    D
       INX     H
       INX     D
       DCR     B
       JNZ     INIT10
INIT11:
       LXI     D,TEX           ;CLEAR EXTENT AND
       MVI     B,4             ; RECORD COUNT
       XRA     A
INIT12: STAX    D
       INX     D
       DCR     B
       JNZ     INIT12
       LXI     D,TNR           ;CLEAR NEXT RECORD NUMBER
       STAX    D
       MVI     C,SEARCF
       CALL    FILEOP          ;LOOKUP HEX FILE
       CPI     0FFH
       JNZ     INIT13
       LXI     D,EXIMES        ;OUTPUT ERROR MESSAGE
       MVI     C,CONOUT
       CALL    BDOS
       JMP     EXIT
EXIMES: DB      'HEX FILE DOES NOT EXIST.',CR,LF,'$'
INIT13:
       MVI     C,OPENF         ;OPEN HEX FILE
       CALL    FILEOP
       RET
;
;
CRLF:   LXI     D,CRLFM
       MVI     C,CONOUT
       CALL    BDOS
       RET
CRLFM:  DB       CR,LF,'$'
FNMES:  DB      'FILENAME ... $'
HEXMES: DB      'HEX'
       END