;Install - Data Technology Corporation CP/M 2.2 Install.
;
;       +-----------------------+
;       |                       |
;       |     I N S T A L L     |
;       |                       |
;       +-----------------------+
;
;
;       Version number: 2.2B
;       Version date:   December 3, 1980
;
;       Update date:    March 31, 1981
;               Source modified for assembly with ASM.
;
;       Update date:    April 27, 1981
;               Source modified for controller timeout.

;       Update date:    June 16, 1981
;               Source modified for new CONFIG parameters.
;
;       The following code is supplied to customers who
;       purchase a hard/floppy disk system from DTC.
;       The following code reads the user created CPMxx.com
;       file and writes out onto the Hard disk boot sectors.
;
;       The format of the Hard Disk Boot sectors are as follows:
;
;       Logical         Routine
;       Sector          Name
;
;         0             Boot program
;
;       1 thru 8        CCP
;
;       9 thru 22       BDOS
;
;       23 thru 32      DTC CBIOS
;
;       The format of the CPMxx.com file is as follows:
;
;       CP/M            Routine
;       Sector          Name
;
;       0 thru 14       Do not care.
;
;         15,16         Boot program
;
;       17 thru 32      CCP
;
;       33 thru 60      BDOS
;
;       61 thru 88      DTC CBIOS





VERS:   EQU     22

CR:     EQU     0Dh             ;ASCII carrriage return
LF:     EQU     0Ah             ;ASCII line feed
TAB:    EQU     9               ;ASCII HORIZONTAL TAB
EOS:    EQU     '$'             ;BDOS End of string
ERRCD:  EQU     0FFh            ;BDOS error code



;       BDOS function equates.

PRTSTR: EQU     09      ;Print String    DE = buffer address.
RDCB:   EQU     10      ;Read console buffer   DE = buffer address.
OPEN:   EQU     15      ;Open file   DE = FCB address.
READS:  EQU     20      ;Read sequential file    DE = FCB.
SETDMA: EQU     26      ;Set DMA address    DE = DMA address.

;       Page zero locations.

BDOSV:  EQU     5       ;BDOS jump address.
DFCB:   EQU     005Ch   ;Default FCB address.
DBUF:   EQU     0080h   ;Default DMA buffer.








       ORG     100h

INSTAL:
       PUSH    PSW
       PUSH    B
       PUSH    D
       PUSH    H
       LXI     H,0
       DAD     SP
       SHLD    SYSTK           ;save system stack


       LXI     SP,STACK
       CALL    GFILEN          ;Get file name
       CALL    SKIPF           ;Skip do not care sectors
       CALL    RBOOT           ;Read Boot
       CALL    RCPM            ;Read CP/M
       CALL    RBIOS           ;Read BIOS
       CALL    WOUT            ;Write out onto hard disk

       LXI     D,ENDMSG
       MVI     C,PRTSTR
       CALL    BDOSV           ;Output successful message

;       Return to system gracefully.

SYSRET: LHLD    SYSTK
       SPHL
       POP     H
       POP     D
       POP     B
       POP     PSW
       RET                     ;return to system

ENDMSG: DB      CR,LF,'Disk Boot Successfully Updated.'
       DB      CR,LF,EOS




;       GFILEN - Get file name and opent the file.
;
;       ENTRY   loaction 5C = filename.

GFILEN:
       LXI     H,5Ch
       LXI     D,FCB
       LXI     B,12
       CALL    MOVDTA          ;Move file name to FCB

       LXI     D,FCB
       MVI     C,OPEN
       CALL    BDOSV           ;Open the file
       CPI     ERRCD
       RNZ                     ;If no error

       LXI     D,OPNERR
       MVI     C,PRTSTR
       CALL    BDOSV
       JMP     SYSRET

OPNERR: DB      CR,LF,'Error on file open.',CR,LF,EOS



;       SKIPF - Skip meaningless sectors on file.

SKIPF:
       LXI     D,DBUF
       MVI     C,SETDMA
       CALL    BDOSV           ;Set DMA address to default buffer

       MVI     A,15            ;Skip 15 sectors
SKPF1:  PUSH    PSW
       LXI     D,FCB
       MVI     C,READS
       CALL    BDOSV           ;Read a sector
       CPI     ERRCD
       JZ      SKPF2           ;If error on read
       POP     PSW
       DCR     A
       JNZ     SKPF1           ;If 16 sectors not read
       RET

SKPF2:  LXI     D,SKERR
       MVI     C,PRTSTR
       CALL    BDOSV           ;Output error message
       JMP     SYSRET

SKERR:  DB      CR,LF,'File read error sectors 0 thru 15.'
       DB      CR,LF,EOS


;       RBOOT - Read the boot sector.

RBOOT:
       LXI     H,BUFFER
       LXI     D,BUFFER+1
       LXI     B,255
       XRA     A
       MOV     M,A
       CALL    MOVDTA          ;Clear 256 bytes of buffer

       LXI     D,BUFFER
       MVI     A,2
RBOOT1:
       PUSH    PSW
       PUSH    D
       MVI     C,SETDMA
       CALL    BDOSV           ;Set DMA address

       LXI     D,FCB
       MVI     C,READS
       CALL    BDOSV           ;Read the boot sector
       CPI     ERRCD
       JZ      RBOOT2
       POP     D
       LXI     H,128
       DAD     D
       XCHG
       POP     PSW
       DCR     A
       JNZ     RBOOT1
       RET

RBOOT2:
       LXI     D,BTERR
       MVI     C,PRTSTR
       CALL    BDOSV           ;Output error message
       JMP     SYSRET

BTERR:  DB      CR,LF,'File read error on boot sector.'
       DB      CR,LF,EOS




;       RCPM - Read CPM sectors.

RCPM:
       MVI     A,22*2
       LXI     D,BUFFER+256
RCPM1:  PUSH    PSW
       PUSH    D               ;Save buffer address
       MVI     C,SETDMA
       CALL    BDOSV           ;Set DMA address to default buffer

       LXI     D,FCB
       MVI     C,READS
       CALL    BDOSV           ;Read a sector
       CPI     ERRCD
       JZ      RCPM2           ;If error on read
       POP     D
       LXI     H,128
       DAD     D
       XCHG
       POP     PSW
       DCR     A
       JNZ     RCPM1           ;If 22*2 sectors not read
       RET

RCPM2:  LXI     D,RCERR
       MVI     C,PRTSTR
       CALL    BDOSV           ;Output error message
       JMP     SYSRET

RCERR:  DB      CR,LF,'File read error in CPM sectors (17 thru 60).'
       DB      CR,LF,EOS
       RET




;       RBIOS - Read BIOS sectors.

RBIOS:
       LXI     H,BUFFER+22*256+256
       LXI     D,BUFFER+22*256+256+1
       LXI     B,255
       XRA     A
       MOV     M,A
       CALL    MOVDTA          ;Clear the rest of the buffer

       LXI     D,BUFFER+22*256+256
RBIOS1: PUSH    D               ;Save buffer address
       MVI     C,SETDMA
       CALL    BDOSV           ;Set DMA address to default buffer

       LXI     D,FCB
       MVI     C,READS
       CALL    BDOSV           ;Read a sector
       POP     D
       LXI     H,128
       DAD     D
       XCHG
       ORA     A
       JZ      RBIOS1          ;If no errors

       RET




;       WOUT - Write out boot sectors onto hard disk.

WOUT:
       LXI     D,DMESSG        ;Output disk selection message
       CALL    INFCRT
       SUI     'A'
       JM      WOUT            ;If invalid entry
       MOV     B,A             ;Save CP/M drive #
       XRA     A               ;Determine LUN
       LXI     H,CONTBL
       LXI     D,CONELN
       MVI     C,CONLEN
WOUT1:
       CMP     B
       JP      WOUT2
       ADD     M
       DAD     D
       DCR     C
       JP      WOUT1
       LXI     D,INVMSG        ;Invalid drive select
       MVI     C,PRTSTR
       CALL    BDOSV
       JMP     WOUT
WOUT2:
       JZ      WOUT3           ;This table?
       LXI     D,-CONELN       ;No. Went too far.
       DAD     D               ;HL = table entry address
       SUB     M
WOUT3:
       MOV     C,A             ;Compute relative logical disk
       INX     H               ;Get LUNiTYPE
       MOV     A,M
       STA     TYPE
       MOV     D,A
       ANI     TYPEDRV         ;Floppy?
       JZ      WOUT4
       MOV     A,D
       ANI     TYPEN48+TYPEN96
       MVI     A,0
       JNZ     WOUT4
       MOV     A,B
       SUB     C
WOUT4:
       MOV     C,A
       INX     H               ;Set drive type
       MOV     A,M
       STA     CIOADT+4
       INX     H               ;Set track format code
       MOV     A,M
       STA     CIOFS+5
       INX     H               ;Set LUN
       MOV     A,M
       STA     CIOESC+1
       STA     CIOPB+1
       STA     CIOADT+1
       STA     CIOFS+1
       INX     H               ;Set # sectors
       MOV     A,M
       STA     LOGNSEC
       INX     H               ;Compute logical address
       MOV     E,M
       INX     H
       MOV     D,M
       CALL    MUL
       XCHG
       LXI     H,CIOPB+1
       MOV     A,M
       ADD     B
       MOV     M,A
       INX     H
       MOV     M,D
       INX     H
       MOV     M,E
       LDA     CIOADT+4        ;Does controller have Class 6,
       CPI     0FFh            ;  op code?
       JZ      WOUTA
       LXI     H,CIOADT
       CALL    EXEC
       CZ      WAITF
       MOV     A,C
       ANI     FERR
       JZ      WOUT5
       LXI     D,ADTMSG
       MVI     C,PRTSTR
       CALL    BDOSV
       JMP     SYSRET
WOUT5:
       LDA     CIOFS+5         ;Floppy?
       CPI     0FFh
       JZ      WOUTA
       LXI     H,CIOFS
       CALL    EXEC            ; diskettes
       CZ      WAITF
       MOV     A,C
       ANI     FERR
       JZ      WOUTA
       LXI     H,CIOFS
       CALL    ERROR
       JMP     SYSRET
WOUTA:

       LXI     H,CIOPB         ;Set Command buffer address
       LXI     D,BUFFER        ;Set data buffer address
       CALL    WDISK           ;Write the data
       MOV     A,C
       ANI     FERR
       RZ
       LXI     H,CIOPB         ;Report errors
       CALL    ERROR
       JMP     WOUT

TYPE:   DS      1
CIOESC: DB      ESCMD,0,0,0,0,0
CIOADT: DB      ADCMD,0,0,0,0,0
CIOFS:  DB      FSCMD,0,0,0,0,0
CIOPB:  DB      WTCMD
       DB      0
LOGSEC: DB      0,0                     ;Write logical sector 0
LOGNSEC:
       DB      0               ;Write two or three tracks
       DB      0                       ;Perform no retries

DMESSG: DB      CR,LF
       DB      'Select Drive : ',EOS
INVMSG: DB      CR,LF,'Invalid drive selection.',EOS
ADTMSG: DB      CR,LF,'Drive Assignment error.',EOS

;       Configuration table
;
CONTBL:
       IF      LUN0
       DB      LUN0NLD
       DB      LUN0TYPE+TYPEN48*N48M0+TYPEN96*N96M0
       DB      LUN0DAT,B0
       DB      0 SHL 5
       DB      2*26*(NH0+NF0)+3*16*(N48M0+N96M0)
       DW      LUN0SEC
       ENDIF
       IF      LUN1
       DB      LUN1NLD
       DB      LUN1TYPE+TYPEN48*N48M1+TYPEN96+N96M1
       DB      LUN1DAT,B1
       DB      1 SHL 5
       DB      2*26*(NH1+NF1)+3*16*(N48M1+N96M1)
       DW      LUN1SEC
       ENDIF
       IF      LUN2
       DB      LUN2NLD
       DB      LUN2TYPE+TYPEN48*N48M2+TYPEN96*N96M2
       DB      LUN2DAT,B2
       DB      2 SHL 5
       DB      2*26*(NH2+NF2)+3*16*(N48M2+N96M2)
       DW      LUN2SEC
       ENDIF
       IF      LUN3
       DB      LUN3NLD
       DB      LUN3TYPE+TYPEN48*N48M3+TYPEN96*N96M3
       DB      LUN3DAT,B3
       DB      3 SHL 5
       DB      2*26*(NH3+NF3)+3*16*(N48M3+N96M3)
       DW      LUN3SEC
       ENDIF
CONELN: EQU     8               ;Entry length
CONLEN: EQU     ($-CONTBL)/CONELN



;       INFCRT - Output message and input from console.
;
;       ENTRY   DE = message address.
;
;       EXIT    A = First character entered (upper case).

INFCRT:
       MVI     C,PRTSTR
       CALL    BDOSV           ;Output message
       LXI     D,INBUFX
       MVI     C,RDCB
       CALL    BDOSV
       LDA     INBUFX+1
       ANA     A
       MVI     A,CR
       RZ
       LDA     INBUF
       CPI     'A'+20h
       RC                      ;If upper case
       CPI     'Z'+20h+1
       RNC                     ;If upper case
       SUI     20h             ;Fold to uppercase
       RET

INBUFX  DB      10,0
INBUF   DB      0,0,0,0,0,0,0,0,0,0






;       MOVDTA - Move data utility program.
;
;       ENTRY   HL = Source field.
;               DE = Destination field.
;               BC = number of bytes.

MOVDTA:
       MOV     A,M
       STAX    D
       INX     H
       INX     D
       DCX     B
       MOV     A,B
       ANA     C
       CPI     0FFh            ;-1
       JNZ     MOVDTA
       RET

;       Multiply single X double
;
;       Entry:  DE = Multiplicand
;                C = Multiplier
;       Exit:   HL = Product (least significant)
;                B = Product (most significant)
;
MUL:
       LXI     H,0
       MVI     B,0
       MOV     A,C
       ORA     A
       RZ
MUL1:
       DAD     D
       JNC     MUL2
       INR     B
MUL2:
       DCR     A
       JNZ     MUL1
       RET




SYSTK:  DW      0               ;Hold stack

FCB:    DS      12              ;file name
       DB      0,0,0,0,0
       DB      0,0,0,0,0
       DB      0,0,0,0,0
       DB      0,0,0,0,0
       DB      0,0,0,0



       DS      50
STACK:  DS      1




;       Disk I/O Routines
;
;
       IF      I696
;       E X E C

EXEC:   MVI     B,BUSY          ;Wait for not busy.
       MVI     C,BUSY and (not BUSY)
       CALL    WAITM
       RNZ


       MVI     A,SLCT          ;Alert controller
       OUT     DIO+1
EXEC1:
       MOV     C,B             ;Wait for controller busy
       CALL    WAITM
       RNZ

       MVI     A,DODTA         ;Enable data in
       OUT     DIO+1

EXEC2:  IN      DIO+2           ;Get status
       XRI     0FFh
       JM      EXEC2           ;If not requesting next byte
       ANI     CMND+DIROUT
       JNZ     EXEC3           ;If CMND or DIROUT false
       MOV     A,M
       INX     H
       OUT     DIO             ;Send byte from command buffer
       JMP     EXEC2

EXEC3:  CMP     A               ;Z:=1
       RET
;
;
;
;
;       WDISK - Output from memory buffer.
;       ENTRY:  HL = COMMAND BUFFER ADDRESS
;               DE = DATA BUFFER ADDRESS
;

WDISK:  CALL    EXEC            ;Output command
       RNZ                     ;Return if timeout
WDISK1: IN      DIO+2           ;Read status
       ORA     A
       JP      WDISK1          ;If request is present
       ANI     CMND
       JNZ     GCMPS           ;If done with transfer
       LDAX    D               ;Get the data byte
       OUT     DIO
       INX     D               ;Advance buffer address
       JMP     WDISK1
;
;
;
;
;       RDISK - Input to memory buffer.
;
;       Entry:  HL = command buffer address
;               DE = data buffer address

RDISK:  CALL    EXEC
       RNZ                     ;Return if timeout
RDISK1: IN      DIO+2           ;Read status
       ORA     A
       JP      RDISK1          ;If request is present
       ANI     CMND
       JNZ     GCMPS
       IN      DIO
       STAX    D
       INX     D
       JMP     RDISK1
;
;
;
;
;       WAITF - Wait for function to complete.

WAITF:  MVI     B,REQ+CMND      ;Wait for both REQ and CMND
       MOV     C,B
       CALL    WAITM
       RNZ
;
;       Get completion status.

GCMPS:  IN      DIO             ;Get completion status
       MOV     C,A

GCMP1:  IN      DIO+2
       ORA     A
       JP      GCMP1           ;If REQ not set

       MOV     B,A
       IN      DIO             ;Get message byte
       RET
       ENDIF
;

;
;
;
       IF      I796
;       EXEC - Output the command
;
;       Enter:  HL is the command buffer address
;               DE - data transfer address.

EXEC:
       MOV     A,E             ;Output DMA address
       OUT     DIO+2
       MOV     A,D
       OUT     DIO+3
       MOV     A,L
       OUT     DIO+4
       MOV     A,H
       OUT     DIO+5
       MVI     A,0
       OUT     DIO+6
       OUT     DIO+7
       OUT     DIO
       CMP     A               ;Z:=1
       RET


;       Disk read/write
;
;       Entry:  same as EXEC
;
RDISK:
WDISK:  CALL    EXEC
       RNZ                     ;Return if timeout

;       WAITF - Wait until transfer done
;
;       Enter:  none
;       Exit:   when transfer completed

WAITF:  MVI     B,CMDDON        ;Wait for CMDDON
       MOV     C,B
       CALL    WAITM
       RNZ                     ;Return if timeout
;

;       GCMPS - Get completion status
;
;       Enter:  none
;       Exit:   Status in C
GCMPS:  IN      DIO+1
       MOV     C,A
       RET
       ENDIF

;       WAITM - Wait for controller with timeout
;
;       Entry:  B=Status mask
;               C=Status value
;       Exit:   Z=1 if OK, else timeout with A=C=TERR
;
WAITM:
       PUSH    D               ;Save D
       PUSH    H
       LXI     H,138           ;Two minute timeout
       LXI     D,0             ;Max wait @4MHZ is 868 ms
WAITML:
       IF      I696
       IN      DIO+2
       ENDIF
       IF      I796
       IN      DIO
       ENDIF
       ANA     B               ;Mask wait bits
       CMP     C               ;Check value
       JZ      WAITM1
       DCX     D               ;Not ready.  Decrement time
       MOV     A,D
       ORA     E
       JNZ     WAITML
       DCX     H
       MOV     A,H
       ORA     L
       JNZ     WAITML
       MVI     B,0             ;Timeout
       MVI     A,TERR
       ORA     A
WAITM1:
       POP     H
       POP     D               ;Restore D
       MOV     C,A             ;Return status in C
       RET
;       DTC Error Print Routine
;
;Called at completion of disk command when error status is returned.
;
;       Entry:  HL = Address of Command Descriptor Block
;               C = Status byte
;
ERROR:
       PUSH    B               ;Save status
       PUSH    H               ;SAVE ADDRESS OF CDB
       MOV     A,M             ;GET CLASS CODE
       ANI     0E0H
       RAL                     ;MAKE CDB LENGTH INDEX
       RAL
       RAL
       MOV     E,A             ;GET CDB LENGTH
       MVI     D,0
       LXI     H,CDBLEN
       DAD     D
       MOV     C,M
       POP     H               ;RESTORE CDB ADDRESS
       PUSH    H
       LXI     D,ERRCDB
       CALL    PUTHEX          ;BUILD CDB FOR PRINT
       LXI     D,EHEAD         ;Print header
       MVI     C,PRTSTR
       CALL    BDOSV
       POP     H               ;Get status
       POP     B
       PUSH    H
       MOV     A,C             ;Timeout?
       ANI     TERR
       LXI     D,TOMSG
       JNZ     ERROR1
       LXI     H,CIOESC        ;No. READ ERROR SENSE
       LXI     D,SENSE
       CALL    RDISK
       LXI     D,ESENSE
       MVI     C,PRTSTR
       CALL    BDOSV
       LXI     D,ESENS1
       LXI     H,SENSE
       MOV     A,M
       MVI     C,PRTSTR
       ORA     A
       CM      BDOSV
       LXI     D,ETYPE
       LXI     H,SENSE         ;BUILD ERROR SENSE MESSAGE
       MOV     A,M
       RAR
       RAR
       RAR
       RAR
       ANI     3
       CALL    HEXASC
       LXI     D,ECODE
       MOV     A,M
       CALL    HEXASC
       LXI     D,ELUN
       INX     H
       MOV     A,M
       RLC
       RLC
       RLC
       ANI     7
       CALL    HEXASC
       MOV     A,M
       ANI     01FH
       MOV     M,A
       LXI     D,ELAD
       MVI     C,3
       CALL    PUTHEX
       LXI     D,ESENS2        ;PRINT MESSAGE
ERROR1:
       MVI     C,PRTSTR
       CALL    BDOSV
       POP     H               ;RESTORE CDB ADDRESS
       RET
;
;

;       PUT HEXADECIMAL STRING
;
;       ENTRY:  HL = ADDRESS OF HEX NUMBER STRING
;               DE = ADDRESS OF HEX ASCII STRING
;                C = NUMBER OF BYTES TO CONVERT
;
PUTHEX: CALL    HEXBYT
       MVI     A,' '
       STAX    D
       INX     D
       DCR     C
       JNZ     PUTHEX
       MVI     A,EOS
       STAX    D
       RET
;
;
HEXBYT: MOV     A,M
       RAR
       RAR
       RAR
       RAR
       CALL    HEXASC
       MOV     A,M
       INX     H
HEXASC: ANI     0FH
       ADI     090H
       DAA
       ACI     040H
       DAA
       STAX    D
       INX     D
       RET
;
;
;       CDB length table (indexed by class)
CDBLEN: DB      6,10,0,0,0,0,6,6
;
EHEAD:  DB      CR,LF,LF,'Disk error:'
       DB      CR,LF,'Command Descriptor:',TAB,TAB
ERRCDB: DS      31
;
TOMSG:  DB      CR,LF,TAB,TAB,TAB,TAB,'Timeout',EOS
ESENSE: DB      CR,LF,'Error Sense:',EOS
ESENS1: DB      CR,LF,TAB,TAB,TAB,TAB,'Block address valid.',EOS
ESENS2: DB      CR,LF,TAB,'Error type:',TAB,TAB
ETYPE:  DS      1
       DB      CR,LF,TAB,'Error code:',TAB,TAB
ECODE:  DS      1
       DB      CR,LF,TAB,'Logical unit:',TAB,TAB
ELUN:   DS      1
       DB      CR,LF,TAB,'Logical address:',TAB
ELAD:   DS      4
SENSE:  DS      4
;
BUFFER: DS      9*1024+256
;
       END