;Format - Data Technology Corporation CP/M 2.2 Format.
;
; +-----------------------+
; | |
; | F O R M A T |
; | |
; +-----------------------+
;
;
; Version number: 2.2B
; Version date: October 24, 1980
;
; UPDATE JANUARY 6,1981
; ADD MRX101D DISK SIZE CONDITIONALS
;
; Update March 31, 1981
; Modified for assembly with ASM.
; Parameterized for hard disk.
;
; Update April 27, 1981
; Modified for controller timeout.
;
; Update date: June 15, 1981
; Modified for new configuration parameters.
; Formats full hard disk only.
; The following code is supplied to customers who
; purchase a hard/floppy disk system from DTC.
; The following code formats floppy disks and the
; hard disk contained within the DTC hard disk unit.
CALL INITBF ;intialize data buffer
FORM1: CALL INITL ;Intialize for formatting
LDA DTYPE
MOV B,A
ANI TYPEDRV
JZ FORM2 ;If floppy disk
MOV A,B
ANI TYPEN48+TYPEN96
JNZ FORM2
CALL FORMH ;Format hard disk
JNZ FORM1 ;If new disk requested
JMP SYSRET
FORM2: CALL FORMF ;Format floppy disk
JNZ FORM1 ;If new disk requested
JMP SYSRET
; Return to system gracefully.
SYSRET: LHLD SYSTK
SPHL
POP H
POP D
POP B
POP PSW
RET ;return to system
SMESSG: DB CR,LF,LF,LF
DB 'Data Technology Disk Format Program'
DB ' Version ',VERS/10+'0','.',VERS mod 10 + '0'
DB CR,LF,EOS
; INITBF - Intialize data buffer with constant pettern.
INITBF:
MVI C,0E5h
LHLD BDOSV+1 ;Get buffer end address
XCHG
LXI H,BUFFER
IBF1: MOV M,C ;Set a byte
INX H
MOV A,D
CMP H
JNZ IBF1
MOV A,E
CMP L
JNZ IBF1 ;If entire buffer not set
RET
; INITL - Get parameters from operator.
INITL:
XRA A ;Reset E5FLAG
STA E5FLAG
INIL1: LXI D,DMESSG ;Output disk selection message
CALL INFCRT
STA LETTER
SUI '0'
JM INIL1 ;If invalid entry
CPI 4
JP INIL1 ;If invalid entry
RRC
RRC
RRC
STA DRVNO
CALL DDTYPE ;Determine Disk type
JNZ INIL4
LDA DTYPE
MOV B,A
ANI TYPEDRV
JZ INILf ;If floppy disk unit
MOV A,B ; or mini floppy
ANI 18h
JNZ INILf
XRA A
INIL2: LXI D,HDMESG
CALL INFCRT ;Get hard disk response
CPI 'Y'
RNZ
INIL3: LXI D,INTMSG ;Get interleave code
CALL INFCRT
SUI '1'
JM INIL3 ;If invalid interleave code
CPI 16
JP INIL3 ;If invlaid interleave code
INR A
STA HDILC ;Save interleave code
INILf: LXI D,DENMSG ;Output density seclection
CALL INFCRT
SUI '1'
JM INILf ;If invalid density
CPI 2
JP INILf ;If invalid density
STA FPDEN
INILg: LXI D,SIDMSG ;Get number of sides
CALL INFCRT
SUI '1'
JM INILg ;If invalid number of sides
CPI 2
JP INILg ;If invalid number of sides
MOV C,A
LDA FPDEN
ADD A
ADD C
STA FPSLV ;Save format selection value
RET
DMESSG: DB CR,LF
DB 'Select Drive (0, 1, 2, 3): ',EOS
DENMSG: DB CR,LF
DB 'Select a sector size:'
DB CR,LF
DB ' 1) 128 Byte Single density.'
DB CR,LF
DB ' 2) 256 Byte Double density.'
DB CR,LF
DB 'Sector Size: ',EOS
HDMESG: DB CR,LF
DB 'This program will format the entire hard disk.',CR,LF
DB 'Do you wish to continue (Y or N)? ',EOS
ADTMSG: DB CR,LF,'Assign Drive Type error.',EOS
INTMSG: DB CR,LF
DB 'Enter interleave value (1--16): '
DB EOS
SIDMSG: DB CR,LF
DB 'Enter number of sides (1 or 2): '
DB EOS
; FORMH - Format a hard disk unit.
FORMH:
CALL SCMND ;Set command buffers
LDA HDILC ;Set interleave code
STA CIOFDC+4
LXI H,CIOSTC
CALL EXEC ;Output command buffer
CZ WAITF ;Wait completion
MOV A,C
ANI FERR
JZ FRMH1 ;If unit is ready
LXI D,UNDMSG ;Unit not available
MVI C,PRTSTR
CALL BDOSV
ORI 0FFh
RET
LXI H,CIOFDC
CALL EXEC ;Output Command
CZ WAITF ;Wait for completion
MOV A,C
ANI FERR
JZ FRMH2 ;If no errors
LXI H,CIOFDC
CALL ERROR ;Print error message
JMP FORMH
FRMH2:
CALL FRME5 ;Fill disk with E5s
JC FORMH ;Error
RET
FRME5:
LDA E5FLAG ;Is data E5?
CPI 0E5h
JZ FRMH7
ORA A ;No. Has data been checked?
JNZ FRMH12
LXI H,0
SHLD CIORED+2
LXI H,CIORED ;No. Read a sector.
LXI D,BUFFER
CALL RDISK
MOV A,C
ANI FERR
JZ FRMH3
LXI H,CIORED
CALL ERROR
STC
RET
FRMH3:
LDA BUFFER ;Set E5FLAG
ORA A
JNZ FRMH4
INR A
FRMH4:
STA E5FLAG
CPI 0E5h ;Is data E5?
JZ FRMH12
CALL INITBF ;No. Set buffer to E5s
LHLD BDOSV+1 ;Compute # sectors in buffer
LXI D,-BUFFER
DAD D
LDA DTYPE
ANI TYPESEC
RRC
MOV B,A
MVI D,3
CALL DBLSHR
FRMH5:
DCR B
JM FRMH6
MVI D,1
CALL DBLSHR
JMP FRMH5
FRMH6:
MOV A,L
STA CIOWRT+4 ;# SECTORS
XCHG
LXI H,0
SHLD CIOWRT+2
FRMH7:
PUSH D
LXI H,CIOWRT
LXI D,BUFFER
CALL WDISK ;Output buffer
POP D
MOV A,C
ANI FERR
JZ FRMH8 ;If no errors
LXI H,CIOWRT
CALL ERROR
STC
RET
FRMH8:
PUSH D ;Increment logical address
LXI H,CIOWRT+2
MOV D,M ;It's in reverse order
INX H
MOV E,M
POP H
XCHG
DAD D
PUSH D
XCHG
LXI H,CIOWRT+1
JNC FRMH9
INR M
FRMH9:
INX H
MOV M,D
INX H
MOV M,E
LXI D,ESECTOR ;Done?
LXI H,CIOWRT+1
MOV A,M
ANI 1Fh
XCHG
FRMH10:
CMP M
JZ FRMH11
POP D
JMP FRMH8
FRMH11:
INX D
INX H
DCR B
LDAX D
JNZ FRMH10
FRMH12:
LXI D,FCMSG
CALL INFCRT
CPI CR
RET
UNDMSG: DB CR,LF,'Unit is not present.',CR,LF,EOS
BFDMSG: DB CR,LF,'Begin formatting hard disk.'
DB CR,LF,EOS
FNAMSG: DB CR,LF,'Function is not presently available.'
DB CR,LF,EOS
; FORMF - Format a floppy disk unit.
FORMF:
CALL SCMND ;Set command buffers
MVI A,1 ;Set interleave code
STA CIOFDC+4
LXI H,CIOSTC
CALL EXEC ;Output command buffer
CZ WAITF ;Wait completion
MOV A,C
ANI FERR
JZ FRMF1 ;If unit is ready
LXI D,NRYMSG ;Output not ready message
CALL INFCRT
JMP FORMF ;retry on any character
FRMF1:
LDA FPSLV ;Get selection value
MOV E,A
MVI D,0
LXI H,FPYTFC
LDA DTYPE
MOV B,A
ANI TYPEDRV
JZ FRMF2
LXI H,M48TFC
MOV A,B
ANI TYPEN48
JNZ FRMF2
LXI H,M96TFC
FRMF2:
DAD D
MOV A,M
STA CIOFSC+5
LXI H,CIOFSC
CALL EXEC ;Output Command
CZ WAITF ;Wait function complete
LXI D,FDKMSG
MVI C,PRTSTR
CALL BDOSV ;Output format disk
LXI H,CIOFDC
CALL EXEC ;Output Command
CZ WAITF ;Wait for completion
MOV A,C
ANI FERR
JZ FRMF3 ;If no errors
LXI H,CIOFDC
CALL ERROR ;PRINT ERROR MESSAGE
JMP FORMF
FRMF3:
LHLD ESECTOR ;Fill 1/4th of disk with E5s
MVI D,2
CALL DBLSHR
SHLD ESECTOR
CALL FRME5
JC FORMF
RZ ;If request to return to CP/M
CPI 'F'
JZ FORMF ;If request for another
RET
; Track format code tables (indexed by density*2+# sides)
;
FPYTFC: DB 0,1,6,7
M48TFC: DB 0,1,4,5
M96TFC: DB 2,3,6,7
;
FCMSG:
DB CR,LF,'Function Complete.'
DB CR,LF,LF
DB 'Type RETURN to return to CP/M, '
DB 'or F to Format another: '
DB EOS
NRYMSG: DB CR,LF
DB 'Disk unit is not ready. ',CR,LF
DB 'Please ready unit and hit RETURN. '
DB EOS
FDKMSG: DB CR,LF,'Formatting the entire disk.'
DB CR,LF,EOS
; 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
; DDTYPE - determine disk type (hard or floppy).
DDTYPE:
XRA A
STA CIOFSC+5
LDA DRVNO
STA CIOFSC+1
STA CIOADT+1
LXI H,DT
LXI D,6
MVI B,NLUN
DDTYP1:
CMP M
JZ DDTYP2
DAD D
DCR B
JNZ DDTYP1
ORI 0FFH ;Configuration error.
RET
DDTYP2:
INX H
MOV A,M
STA DTYPE
INX H
MOV A,M
STA CIOADT+4 ;ASSIGN DRIVE TYPE
INX H ;Compute end sector
MOV C,M
INX H
MOV E,M
INX H
MOV D,M
CALL MUL
XCHG
LXI H,ESECTOR
MOV M,B
INX H
MOV M,D
INX H
MOV M,E
LDA CIOADT+4 ;Does controller have Class 6,
CPI 0FFh ; op code 1?
RZ
LXI H,CIOADT
CALL EXEC ;Output Command
CZ WAITF ;Wait function complete
MOV A,C
ANI FERR ;Mask for errors
RET
;
; Drive Type table (indexed by LUN)
;
DT:
IF LUN0
DB 0 SHL 5
DB LUN0TYPE+TYPEN48*N48M0+TYPEN96*N96M0
DB LUN0DAT,LUN0NLD
DW LUN0SEC
ENDIF
IF LUN1
DB 1 SHL 5
DB LUN1TYPE+TYPEN48*N48M1+TYPEN96*N96M1
DB LUN1DAT,LUN1NLD
DW LUN1SEC
ENDIF
IF LUN2
DB 2 SHL 5
DB LUN2TYPE+TYPEN48*N48M2+TYPEN96*N96M2
DB LUN2DAT,LUN2NLD
DW LUN2SEC
ENDIF
IF LUN3
DB 3 SHL 5
DB LUN3TYPE+TYPEN48*N48M3+TYPEN96*N96M3
DB LUN3DAT,LUN3NLD
DW LUN3SEC
ENDIF
;
; SCMND - Set drive into command buffers.
SCMND:
LDA DRVNO
LXI H,CIOFSC+1
LXI D,6
MVI B,CIONUM
SCMD1: MOV M,A
DAD D
DCR B
JNZ SCMD1 ;If not all buffer set
RET
; MOVDTA - Move data utility program.
;
; ENTRY H = 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
MOV A,C
ORA A
RZ
MVI B,0
MUL1: DAD D
JNC MUL2
INR B
MUL2:
DCR A
JNZ MUL1
RET
;
;
;Shift HL right by (D)
;
DBLSHR:
XRA A
MOV A,H
RAR
MOV H,A
MOV A,L
RAR
MOV L,A
DCR D
JNZ DBLSHR
RET
;
;
;
CIOBGN:
CIOFSC: DB FSCMD,0,0,0,0,0
CIOWRT: DB WTCMD,0,0,0,1,0C0h
CIORED: DB RDCMD,0,0,0,1,0
CIOESC: DB ESCMD,0,0,0,0,0
CIOSTC: DB STCMD,0,0,0,0,0
CIOFDC: DB FDCMD,0,0,0,0,0
CIOADT: DB ADCMD,0,0,0,0,0
CIONUM: EQU ($-CIOBGN)/6 ;Number of entries above
SYSTK: DW 0 ;Hold stack
DS 50
STACK: DS 1
LETTER: DB 0,EOS
STRACK: DW 0 ;Starting track number
CTRACK: DW 0 ;Current track number
ETRACK: DW 0 ;Ending track number
ESECTOR: DS 3 ;Ending sector
DRVNO: DB 0
DTYPE: DB 0
HDFTP: DB 0 ;Hard disk format type
HDILC: DB 0 ;Hard disk interleave code
FPDEN: DB 0 ;floppy Density selection
FPSLV: DB 0 ;floppy format selection value
E5FLAG: DB 0 ;Format data value
; 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: EQU $
;
END