;********************************;
;   LOGICAL I/O SYSTEM FOR CP/M  ;
;********************************;

;********************************;
;   MISCELLANEOUS EQUATES        ;
;********************************;

BOOT    EQU     0000H   ;REBOOT ENTRY POINT
CPM     EQU     0005H   ;CPM ENTRY POINT

TBUF    EQU     0080H   ;TRANS. BUFFER
TFCB    EQU     005CH   ;TRANS. FCB

SDISK   EQU     14      ;SET DISK FUNCT. CODE
OPEN    EQU     15      ;OPEN FUNCTION CODE
CLOSE   EQU     16      ;CLOSE FUNCTION CODE
DELET   EQU     19      ;DELETE FUNCTION CODE
READ    EQU     20      ;READ FUNCTION CODE
WRITE   EQU     21      ;WRITE FUNCTION CODE
CREAT   EQU     22      ;CREATE FUNCTION CODE
RENAM   EQU     23      ;RENAME FUNCTION CODE

;********************************;
;          JUMP VECTORS          ;
;********************************;

       ORG     1000H
       JMP     FFCB    ;FILL FCB
       JMP     FMAKE   ;FILE CREATE
       JMP     FOPEN   ;FILE OPEN
       JMP     FCLOS   ;FILE CLOSE
       JMP     FDELE   ;DELETE FILE
       JMP     OUTBT   ;BYTE OUTPUT
       JMP     GETBT   ;BYTE INPUT

;********************************;
;           F F C B              ;
; ROUTINE TO FILL IN ID FIELDS   ;
;            OF FCB              ;
;                                ;
; INPUTS:    HL = A(FCB)         ;
;            DE = A(ID STRING)   ;
; OUTPUTS:   CARRY = ERROR       ;
;********************************;

FFCB:   MVI     M,00    ;CLEAR FIRST BYTE OF FCB
       INX     H       ;SKIP BYTE
       PUSH    H       ;SAVE FCB NAME ADDRESS
       MVI     C,8+3   ;SIZE OF ID FIELD
       MVI     A,' '   ;SPACE
FFCB0:  MOV     M,A     ;CLEAR ID FIELD TO SPACES
       INX     H
       DCR     C
       JNZ     FFCB0
       POP     H       ;GET ID FIELD ADDR.
       MVI     C,8     ;MAZ SIZE OF NAME
FFCB1:  LDAX    D       ;GET ID BYTES
       CPI     ' '     ;LEADING SPACES?
       JNZ     FFCB2   ;NO, CONTINUE
       INX     D       ;SKIP LEADING SPACES
       JMP     FFCB1
FFCB2:  LDAX    D       ;GET ID BYTE
       CPI     0DH     ;CR?
       RZ              ;YES, DONE
       CPI     ' '     ;IMBEDDED SPACE?
       RZ              ;YES, DONE
       CPI     '.'     ;NAME.TYP SEPARATOR?
       JZ      FFCB3   ;YES, PROCESS TYPE
       MOV     M,A     ;STORE NAME BYTE
       INX     D
       INX     H       ;BUMP POINTERS
       DCR     C       ;DECREMENT MAX COUNT
       JNZ     FFCB2   ;LOOP
       JMP     FFCBE   ;ERROR, NAME TOO BIG

FFCB3:  INX     D       ;SKIP OVER '.'
FFCB4:  INX     H       ;SKIP TO TYPE FIELD
       DCR     C
       JNZ     FFCB4
       MVI     C,3     ;SIZE OF TYPE FIELD
FFCB5:  LDAX    D       ;GET ID BYTE
       CPI     0DH     ;CR?
       RZ              ;YES, DONE
       CPI     ' '     ;SPACE?
       RZ              ;YES, DONE
       MOV     M,A     ;STORE TYPE BYTE
       INX     D       ;BUMP POINTER
       INX     H
       DCR     C       ;DECREMENT MAX COUNT
       JNZ     FFCB5   ;LOOP
       RET             ;DONE

FFCBE:  STC             ;SET CARRY
       RET

;********************************;
;           F M A K E            ;
;  ROUTINE TO CREATE A DISK FILE ;
;                                ;
;  INPUT:    DE=A(FCB)           ;
; OUTPUT:    CARRY=ERROR         ;
;********************************;

FMAKE:  MVI     C,CREAT ;CREATE CODE
       CALL    CPM     ;ISSUE CREATE
       CPI     0FFH    ;ERROR?
       JZ      FMERR   ;YES
       XRA     A       ;CLEAR CARRY
       RET
FMERR:  STC             ;SET CARRY
       RET             ;EXIT

;********************************;
;           F O P E N            ;
;  ROUTINE TO OPEN A DISK FILE   ;
;                                ;
;  INPUT:     DE=A(FCB)          ;
; OUTPUT:     CARRY=ERROR        ;
;********************************;

FOPEN:  MVI     C,OPEN  ;OPNE CODE
       CALL    CPM     ;ISSUE OPEN
       CPI     0FFH    ;ERROR?
       JZ      FOERR   ;YES
       XRA     A       ;CLEAR CARRY
       RET
FOERR:  STC
       RET

;********************************;
;           F C L O S            ;
;  ROUTINE TO CLOSE A DISK FILE  ;
;                                ;
;  INPUT:     DE=A(FCB)          ;
; OUTPUT:     CARRY=ERROR        ;
;********************************;

FCLOS:  MVI     C,CLOSE ;CLOSE CODE
       CALL    CPM
       CPI     0FFH    ;ERROR?
       JZ      FCERR   ;YES
       XRA     A       ;CLEAR CARRY
       RET
FCERR:  STC
       RET

;********************************;
;            F D E L E           ;
;  ROUTINE TO DELETE A DISK FILE ;
;                                ;
;  INPUT:     DE=A(FCB)          ;
;********************************;

FDELE:  MVI     C,DELET
       CALL    CPM     ;ISSUE DELETE
       XRA     A       ;RESET CARRY
       RET

;********************************;
;            O U T B T           ;
;   ROUTINE TO OUTPUT A BYTE     ;
;                                ;
;  INPUT:     A=BYTE)            ;
; OUTPUT:     CARRY=ERROR        ;
;********************************;

OUTBT:  STA     TEMP    ;SAVE BYTE
OUT1:   LXI     H,OBUF+128
       XCHG            ;BUFFER END ADDR IN DE
       LHLD    OUTPT   ;CURRENT ADDR IN HL
       CALL    CPHL    ;TEST FOR END OF BUFFER
       JZ      OUT2    ;YES, WRITE
       LDA     TEMP
       MOV     M,A     ;STORE DATA BYTE IN BUFFER
       INX     H       ;BUMP BUFFER POINTER
       SHLD    OUTPT   ;SAVE BUFFER POINTER
       ORA     A
       RET             ;EXIT
OUT2:   LXI     D,OBUF  ;POINT TO OUTPUT BUFFER
       LXI     H,TBUF  ;TEMP BUFFER
       MVI     B,128
       CALL    MOVE    ;COPY BUFFERS
       MVI     C,WRITE ;WRITE CODE
       LXI     D,OFCB  ;OUTPUT FCB
       CALL    CPM     ;ISSUE WRITE
       CPI     00      ;OK?
       JNZ     OERR    ;NO
OUT2A:  LXI     H,OBUF  ;RESET POINTER
       SHLD    OUTPT
       JMP     OUT1    ;CONTINUE
OERR:   STC
       RET

;********************************;
;            G E T B T           ;
;   ROUTINE TO READ A BYTE       ;
;                                ;
;  OUTPUTS:     A=BYTE           ;
;               CARRY=ERROR      ;
;********************************;

GETBT:  LXI     H,IBUF+128
       XCHG            ;BUFFER END ADDR. IN DE
       LHLD    INPTR   ;CURRENT POINTER IN HL
       CALL    CPHL    ;TEST FOR END OF BUFFER
       JZ      GETB2   ;YES, READ
GETB1:  MOV     A,M     ;GET BYTE
       INX     H       ;BUMP POINTER
       SHLD    INPTR   ;SAVE POINTER
       ORA     A       ;RESET CARRY
       RET
GETB2:  MVI     C,READ  ;READ CODE
       LXI     D,IFCB  ;FCB ADDRESS
       CALL    CPM     ;ISSUE READ
       CPI     00      ;ERROR?
       JNZ     IERR    ;YES
       LXI     D,TBUF  ;POINT TO TEMP BUFFER
       LXI     H,IBUF  ;INPUT BUFFER
       MVI     B,128
       CALL    MOVE    ;COPY BUFFER
       LXI     H,IBUF  ;RESET BUFFER POINTER
       SHLD    INPTR
       JMP     GETB1   ;CONTINUE
IERR:   STC
       RET

;********************************;
;   MISCELLANEOUS SUBROUTINES    ;
;********************************;

;********************************;
;            M O V E             ;
;  ROUTINE TO MOVE BLOCKS OF DATA;
;********************************;
MOVE:   LDAX    D       ;GET BYTE
       MOV     M,A     ;STORE BYTE
       INX     H
       INX     D       ;BUMPP POINTERS
       DCR     B       ;DECREMENT COUNT
       JNZ     MOVE    ;LOOP
       RET

;********************************;
;             C P H L            ;
;  ROUTINE TO COMPARE HL VS DE   ;
;********************************;

CPHL:   MOV     A,H
       CMP     D
       RNZ
       MOV     A,L
       CMP     E
       RET

;********************************;
;             D A T A            ;
;********************************;

TEMP:   DS      2       ;TEMP SAVE WORD

IFCB:   DS      33      ;INPUT FCB
OFCB:   DS      33      ;OUTPUT FCB

OUTPT:  DW      OBUF    ;OUTPUT POINTER
INPTR:  DW      IBUF+128;INPUT POINTER

OBUF:   DS      128     ;INPUT BUFFER
IBUF:   DS      128     ;OUTPUT BUFFER
       END