;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