Concatenate this section with the previous to make the full program.
Code: Select all
;----------------------------- SUPPORT FUNCTIONS ------------------------------
driveid:CALL IDEwaitnotbusy ;Retrieve drive info
RC
MVI D, COMMANDid
MVI E, REGcommand
CALL IDEwr8D ;Issue the ID command
IF VERBOSE
LXI D, READING$ID
CALL PSTRING
LXI D, DISKSTATUS ;Print status message
CALL PSTRING
ENDIF
MVI E, REGstatus ;Get status after ID command
CALL IDErd8D ;Check Status (info in [D])
IF VERBOSE
MOV A, D
CALL PHEX ;Print status
CALL ZPERCRLF
ENDIF
CALL IDEwaitdrq ;Wait for Busy=0, DRQ=1
JC SHOWerrors
CALL CLEAR$ID$BUFFER ;Clear ID Buffer
IF VERBOSE
LXI D, GETTING$ID
CALL PSTRING
ENDIF
MVI B, 0 ;256 words
LXI H, IDbuffer ;Store data here
CALL MoreRD16 ;Get 256 words of data from REGdata port to [HL]
RET
spinup: ;Start the drive
MVI D, COMMANDspinup
spup2: MVI E, REGcommand
CALL IDEwr8D
CALL IDEwaitnotbusy
JC SHOWerrors
ORA A ;Clear carry
RET
spindown: ;Tell the drive to spin down
CALL IDEwaitnotbusy
JC SHOWerrors
MVI D,COMMANDspindown
JMP spup2
SequentialReads: ;Sequentially read sectors from current position
CALL IDEwaitnotbusy
JC SHOWerrors
CALL ZCRLF
NEXTSEC:
LXI H, buffer ;Point to buffer
SHLD mDMA
CALL READSECTOR ;Errors will show in READSECTOR
JZ SEQOK
LXI D, CONTINUE$MSG
CALL PSTRING
CALL ZCI
CPI ESC ;Abort if ESC
RZ
SEQOK: CALL ZCR ;Return to beginning of line
CALL DISPLAYposition ;Display current track, sector and head
LXI H, buffer ;Point to buffer
SHLD mDMA
LDA mDisplayFlag ;Do we have display flag on or off
ORA A ;NZ = on
CNZ HEXDUMP
CALL ZCRLF
CALL ZCRLF
CALL ZCRLF
CALL ZCSTS ;Any keyboard character will stop display
CPI 01H ;CP/M says something there
JNZ NEXTSEC1
CALL ZCI ;Flush character
LXI D, CONTINUE$MSG
CALL PSTRING
CALL ZCI
CPI ESC
RZ
CALL ZCRLF
NEXTSEC1:
LHLD mSEC
MOV A, L ;Current sector to A
CALL CHK$SEC ;Are we already at max sector?
JZ NextSecZero ;Yes - back to sector 0
INX H ;No - bump to next sector
SHLD mSEC
JMP NEXTSEC
NextSecZero:
LXI H, 0 ;Back to CP/M sector 0
SHLD mSEC
LHLD mTRK ;Bump to next track
INX H
SHLD mTRK
JMP NEXTSEC ;Note will go to last track on disk unless stopped
PRN$0$INFO: ;Print Drive 0 identification info
LXI D, DRIVE0$INFO
CALL PSTRING
CALL REM$DRV ;Remember current drive position
CALL SELECT0
CALL PRN$DRV$INFO
CALL IDEinit
CALL RET$DRV ;Return to original drive and position
JMP MAINLOOP
PRN$1$INFO: ;Print Drive 1 identification info
LXI D, DRIVE1$INFO
CALL PSTRING
CALL REM$DRV ;Remember current drive position
CALL SELECT1
CALL PRN$DRV$INFO
CALL IDEinit
CALL RET$DRV ;Return to original drive and position
JMP MAINLOOP
PRN$DRV$INFO: ;Print drive identification info
CALL driveid
JZ PRN$DETAILS
LXI D, ID$ERROR ;On error, display message
CALL PSTRING
RET
PRN$DETAILS: ;Get Sector Count
LXI H, IDbuffer + 12
MOV A, M ;(High Byte)
ORA A
JNZ PRN$DET2
INX H
MOV A, M ;(Low Byte)
ORA A
JNZ PRN$DET2 ;Looks like we have a valid IDE drive
LXI D, BAD$DRIVE ;Zero sectors means something's wrong
CALL PSTRING
RET
PRN$DET2: ;Print drive info
LXI D, msgmdl ;Drive name
CALL PSTRING
LXI H, IDbuffer + 54
MVI B, 20 ;Character count in words
CALL printSwap ;Print [HL], [B] X 2 characters
CALL ZCRLF
LXI D, msgsn ;Serial number
CALL PSTRING
LXI H, IDbuffer + 20
MVI B, 10 ;Character count in words
CALL printText
CALL ZCRLF
LXI D, msgrev ;Firmware revision string
CALL PSTRING
LXI H, IDbuffer + 46
MVI B, 4 ;Character count in words
CALL printSwap
CALL ZCRLF
LXI D, msgcy ;Drive specs (cyl/hd/sect)
CALL PSTRING
LXI H, IDbuffer + 2
CALL printparm
LXI D, msghd
CALL PSTRING
LXI H, IDbuffer + 6
CALL printparm
LXI D, msgsc
CALL PSTRING
LXI H, IDbuffer + 12
CALL printparm
CALL ZCRLF
LXI D, msgLBAsup1 ;First part of LBA support message
CALL PSTRING
LDA IDbuffer+98+1 ;Bits 15-10 reserved, 9 LBA, 8 DMA
ANI 02H
JNZ PRN$SUP ;LBA is supported
LXI D, msgLBAnot ;LBA is not supported
CALL PSTRING
PRN$SUP:
LXI D, msgLBAsup2
CALL PSTRING
RET
DISPLAYposition: ;Display current track, sector & head position
LXI D, msgCPMTRK ;Display in LBA format
CALL PSTRING ;---- CP/M FORMAT ----
LDA mTRK+1 ;High TRK byte
CALL PHEX
LDA mTRK ;Low TRK byte
CALL PHEX
LXI D, msgCPMSEC
CALL PSTRING ;SEC = (16 bits)
LDA mSEC+1 ;High Sec
CALL PHEX
LDA mSEC ;Low sec
CALL PHEX
;---- LBA FORMAT ----
LXI D, msgLBA
CALL PSTRING ;LBA = 00 ("Heads" = 0 for these drives)
LDA mDRIVE$TRK+1 ;High "cylinder" byte
CALL PHEX
LDA mDRIVE$TRK ;Low "cylinder" byte
CALL PHEX
LDA mDRIVE$SEC
CALL PHEX
LXI D, MSGBracket
CALL PSTRING
RET
printText: ;Print text up to [B] (16-bit word) byte-pairs
MOV C, M ;Text is contiguous byte array
CALL ZCO
INX H
MOV C, M
CALL ZCO
INX H
DCR B
JNZ printText
RET
printSwap: ;Print text up to [B] (16-bit word) byte-pairs
INX H ;Swap byte pairs - low byte, high byte
MOV C, M
CALL ZCO
DCX H
MOV C, M
CALL ZCO
INX H
INX H
DCR B
JNZ printSwap
RET
ZCRLF: ;Print CRLF
PUSH PSW
MVI C, CR
CALL ZCO
MVI C, LF
CALL ZCO
POP PSW
RET
ZPERCRLF: ;Print period and then CRLF
PUSH PSW
MVI C, PERIOD
CALL ZCO
MVI C, CR
CALL ZCO
MVI C, LF
CALL ZCO
POP PSW
RET
ZCR: ;Return to beginning of line
MVI C, CR
CALL ZCO
RET
ZERA: ;Return to beginning of line and erase [B] characters
MVI C, CR
CALL ZCO
MVI C, SPACE
ERAX: CALL ZCO
DCR B
JNZ ERAX
MVI C, CR
CALL ZCO
RET
ZCSTS:
IF CPM
PUSH B
PUSH D
PUSH H
MVI C, CONST
CALL BDOS ;Returns with 1 in [A] if character at keyboard
POP H
POP D
POP B
CPI 1
RET
ENDIF
IF NOT CPM
IN CONI ;Get Character in [A]
ANI 02H
RZ
MVI A, 01H
ORA A
RET
ENDIF
ZCO: ;Write character that is in [C]
IF CPM
PUSH PSW
PUSH B
PUSH D
PUSH H
MOV E, C
MVI C, WRCON
CALL BDOS
POP H
POP D
POP B
POP PSW
RET
ENDIF
IF NOT CPM
PUSH PSW
ZCO1: IN CONI ;Show Character
ANI 04H
JZ ZCO1
MOV A, C
OUT CONO
POP PSW
RET
ENDIF
ZCI: ;Return keyboard character in [A]
IF CPM
PUSH B
PUSH D
PUSH H
MVI C, RDCON
CALL BDOS
POP H
POP D
POP B
RET
ENDIF
IF NOT CPM
ZCI1: IN CONI ;Get Character in [A]
ANI 02H
JZ ZCI1
IN CONO
RET
ENDIF
;------------------------------------------------------------------------------
;Print a string in [DE] up to '$'
;------------------------------------------------------------------------------
PSTRING:
IF CPM
MVI C, PRINT
JMP BDOS ;PRINT MESSAGE
ENDIF
IF NOT CPM
PUSH B
PUSH D
PUSH H
XCHG
PSTRX: MOV A, M
CPI '$'
JZ DONEP
MOV C, A
CALL ZCO
INX H
JMP PSTRX
DONEP: POP H
POP D
POP B
RET
ENDIF
SHOWerrors:
IF NOT DEBUG
ORA A ;Set NZ flag
STC ;Set Carry Flag
RET
ENDIF
IF DEBUG
CALL ZCRLF
MVI E, REGstatus ;Get status in status register
CALL IDErd8D
MOV A, D
ANI 1H
JNZ MoreError ;Go to REGerr register for more info
;All OK if 01000000
PUSH PSW ;Save for return below
ANI 80H
JZ NOT7
LXI D, DRIVE$BUSY ;Drive Busy (bit 7) stuck high
CALL PSTRING
JMP DONEERR
NOT7: ANI 40H
JNZ NOT6
LXI D, DRIVE$NOT$READY ;Drive Not Ready (bit 6) stuck low
CALL PSTRING
JMP DONEERR
NOT6: ANI 20H
JNZ NOT5
LXI D, DRIVE$WR$FAULT ;Drive write fault
CALL PSTRING
JMP DONEERR
NOT5 LXI D, UNKNOWN$ERROR
CALL PSTRING
JMP DONEERR
MoreError: ;Bit 0 of the status register indicates problem
MVI E, REGerr ;Get error code in REGerr
CALL IDErd8D
MOV A, D
PUSH PSW
ANI 10H
JZ NOTE4
LXI D, SEC$NOT$FOUND
CALL PSTRING
JMP DONEERR
NOTE4: ANI 80H
JZ NOTE7
LXI D, BAD$BLOCK
CALL PSTRING
JMP DONEERR
NOTE7: ANI 40H
JZ NOTE6
LXI D, UNRECOVER$ERR
CALL PSTRING
JMP DONEERR
NOTE6: ANI 4H
JZ NOTE2
LXI D, INVALID$CMD
CALL PSTRING
JMP DONEERR
NOTE2: ANI 2H
JZ NOTE1
LXI D, TRK0$ERR
CALL PSTRING
JMP DONEERR
NOTE1: LXI D, UNKNOWN$ERROR1
CALL PSTRING
JMP DONEERR
DONEERR:POP PSW
PUSH PSW
CALL ZBITS
CALL ZCRLF
POP PSW
ORA A ;Set Z flag
STC ;Set Carry flag
RET
ENDIF
;------------------------------------------------------------------------------
;Print a 16-bit number in RAM located @ [HL], low-byte first for Drive ID
;------------------------------------------------------------------------------
printparm:
INX H ;Index to high byte first
MOV A, M
CALL PHEX
DCX H ;Now low byte
MOV A, M
CALL PHEX
RET
;------------------------------------------------------------------------------
;Print an 8 bit number located in [A]
;------------------------------------------------------------------------------
PHEX: PUSH PSW
PUSH B
PUSH PSW
RRC
RRC
RRC
RRC
CALL ZCONV
POP PSW
CALL ZCONV
POP B
POP PSW
RET
ZCONV: ANI 0FH ;HEX to ASCII and print it
ADI 90H
DAA
ACI 40H
DAA
MOV C, A
CALL ZCO
RET
;------------------------------------------------------------------------------
;Display binary in [A]
;------------------------------------------------------------------------------
ZBITS: PUSH PSW
PUSH B
PUSH D
MOV E, A
MVI B, 8
BQ2: DB 0CBH, 23H ;SLA A, E
MVI A, 18H
ADC A
MOV C, A
CALL ZCO
DCR B
JNZ BQ2
POP D
POP B
POP PSW
RET
ghex32lba: ;Convert CP/M Track & Sector to LBA format
LXI D,ENTER$SECH
CALL PSTRING
CALL GETHEX ;Enter high byte sector number
RC
STA mSEC+1
CALL ZCRLF
LXI D,ENTER$SECL
CALL PSTRING
CALL GETHEX ;Enter low byte sector number
RC
STA mSEC
CALL ZCRLF
LXI D, ENTER$TRKH
CALL PSTRING
CALL GETHEX ;Enter high byte track number
RC
STA mTRK+1
CALL ZCRLF
LXI D, ENTER$TRKL
CALL PSTRING
CALL GETHEX ;Enter low byte track number
RC
STA mTRK
CALL ZCRLF
XRA A
ORA A ;Clear Accumulator and Carry bit
RET
;------------------------------------------------------------------------------
;Get a HEX character from the keyboard and echo it
;------------------------------------------------------------------------------
GETHEX:
CALL GETCMD ;Get character
CPI ESC
JZ HEXABORT
CPI '/' ;check 0-9, A-F
JC HEXABORT
CPI 'F'+1
JNC HEXABORT
CALL ASBIN ;Convert to binary
RLC ;Shift to high nibble
RLC
RLC
RLC
MOV B, A ;Store it
CALL GETCMD ;Get 2nd character from keyboard & ECHO
CPI ESC
JZ HEXABORT
CPI '/' ;check 0-9, A-F
JC HEXABORT
CPI 'F'+1
JNC HEXABORT
CALL ASBIN ;Convert to binary
ORA B ;add in the first digit
ORA A ;To return NC
RET
HEXABORT:
STC ;Set Carry flag
RET
;------------------------------------------------------------------------------
;Get a character from the keyboard, convert to uppercase and echo it
;------------------------------------------------------------------------------
GETCMD: CALL ZCI ;Get character
CALL UPPER
CPI ESC
RZ ;Don't echo an ESC
IF NOT CPM
PUSH PSW ;Save state of registers
PUSH B
MOV C, A
CALL ZCO ;Echo it
POP B
POP PSW ;Retrieve original state
ENDIF
RET
;------------------------------------------------------------------------------
;Convert lowercase to uppercase
;------------------------------------------------------------------------------
UPPER: CPI 'a' ;Must be >= lowercase a
RC ;else return as-is
CPI 'z'+1 ;Must be <= lowercase z
RNC ;else return as-is
SUI 'a'-'A' ;Subtract lowercase bias
RET
ASBIN: SUI 30H ;ASCII to binary conversion
CPI 0AH
RM
SUI 07H
RET
;------------------------------------------------------------------------------
;Print a hexdump of the data in the 512 byte buffer starting at [HL]
;------------------------------------------------------------------------------
HEXDUMP:
PUSH PSW ;Save everything
PUSH B
PUSH D
PUSH H
CALL ZCRLF ;CR/LF first
MVI D, 32 ;Print 32 lines total
MVI B, 16 ;16 characters across
SHLD mStartLineHex ;Save buffer location for ASCII display below
LXI H, 0
SHLD mBYTE$COUNT
SF172: CALL ZCRLF
LHLD mBYTE$COUNT
MOV A, H
CALL PHEX ;Print byte count in sector
MOV A, L
CALL PHEX
PUSH D
LXI D, 16
DAD D
POP D
SHLD mBYTE$COUNT ;Store for next time
CALL BLANK
LHLD mStartLineHex
SHLD mStartLineASCII ;Store for ASCII display below
SF175: MOV A, M
CALL LBYTE ;Display [A] on CRT/LCD
INX H
DCR B
JNZ SF175
SHLD mStartLineHex ;Save for next line later
CALL ShowAscii ;Now translate to ASCII and display
MVI B, 16 ;16 characters across for next line
DCR D
JNZ SF172 ;Have we done all 32 lines
CALL ZCRLF
POP H ;Get back original registers
POP D
POP B
POP PSW
RET
ShowAscii: ;Show as ASCII info
LHLD mStartLineASCII
MVI B, 16 ;16 ASCII characters across
XF172: CALL BLANK ;Send a space character
CALL BLANK
XF175: MOV A, M
ANI 7FH
CPI ' ' ;Filter out control characters
JNC XT33
XT22: MVI A, '.'
XT33: CPI 07CH
JNC XT22
MOV C, A ;Setup to send
PUSH B
CALL ZCO
POP B
INX H ;Next position in buffer
DCR B
JNZ XF175
RET
BLANK: PUSH B
PUSH H
MVI C, ' '
CALL ZCO
POP H
POP B
RET
LBYTE: PUSH PSW
RRC
RRC
RRC
RRC
CALL SF598
POP PSW
SF598: CALL ZCONV
RET
CHK$SEC: ;Compare current CP/M sector to max CP/M sector
PUSH B ;Save
MOV C, A ;C <- Current Sector
MVI B, MAXSEC ;Retrieve max sector number
MOV A, C ;Get current sector back in A for compare (and return with it in A)
CMP B ;Current : Max
POP B
RET ;Return with compare status. (Carry => Max > Current)
GET$BkPt$NUM: ;Ask user for backup partition number (01-FF)
LXI D, Enter$BkupPart
CALL PSTRING
CALL GETHEX ;Get 2 HEX digits
RC
STA mPART$NUM
CALL ZCRLF
RET
GET$SrcPt$NUM: ;Ask user for source partition number (00-FF)
LXI D, Enter$SrcPartn
CALL PSTRING
CALL GETHEX ;Get 2 HEX digits
RC
STA mSrc$Partn
CALL ZCRLF
RET
GET$TgtPt$NUM: ;Ask user for target partition number (00-FF)
LXI D, Enter$Tgt$Partn
CALL PSTRING
CALL GETHEX ;Get 2 HEX digits
RC
STA mTgt$Partn
CALL ZCRLF
RET
GET$Src$Drive: ;Ask user for source drive (00 or 01)
LXI D, Enter$SrcDrive
CALL PSTRING
CALL GETHEX ;Get 2 HEX digits
JNC GdSrDin
CPI ESC ;Return if ESC key pressed
STC
RZ
LXI D, INVALID$MSG ;Re-prompt if input is invalid
CALL PSTRING
JMP GET$Src$Drive
GdSrDin:
CALL Val$Drive ;Verify that drive is valid
RC
STA mSrc$Drive
CALL ZCRLF
RET
GET$Tgt$Drive: ;Ask user for target drive (00 or 01)
LXI D, Enter$TgtDrive
CALL PSTRING
CALL GETHEX ;Get 2 HEX digits
JNC GdTgDin
CPI ESC ;Return if ESC key pressed
STC
RZ
LXI D, INVALID$MSG ;Re-prompt if input is invalid
CALL PSTRING
JMP GET$Tgt$Drive
GdTgDin:
CALL Val$Drive ;Is drive valid?
RC
STA mTgt$Drive
CALL ZCRLF
RET
Val$Drive: ;Check if drive [A] is valid
LHLD mLast$Drive
INX H
CMP L ;Is drive valid?
JC Vdone
LXI D, DRV$NOT$FOUND
CALL PSTRING
Vdone: CMC
RET
;------------------------------------------------------------------------------
;IDE Drive BIOS Routines written in a format that can be used directly with CPM
;------------------------------------------------------------------------------
IDEinit: ;Initialize the 8255 and drive then do a hard reset
PUSH B
PUSH D
IF VERBOSE
LXI D, INITDRIVE
CALL PSTRING
ENDIF
MVI A, READcfg8255 ;Config 8255 chip (10010010B)
OUT IDEportCtrl ;for READ mode
MVI A, IDErstline ;Hard reset the disk drive
OUT IDEportC ;Some CF cards are sensitive to reset pulse width
MVI B, 20H ;Symptom is incorrect data back from a sector read
ResetDelay:
DCR B
JNZ ResetDelay ;Delay (reset pulse width)
XRA A
OUT IDEportC ;No control lines asserted (just bit 7 of port C)
CALL DELAY$SHORT ;Short Delay
CALL IDEwaitnotbusy ;Wait for drive
JC WaitInitErr
MVI D, 11100000b ;Data for IDE SDH reg (512byte, LBA, single drive, hd 0)
;For Trk, Sec, Head (non LBA) use 10100000
MVI E, REGshd ;00001110,(0EH) for CS0,A2,A1,
CALL IDEwr8D ;Write byte to select the MASTER device
MVI B, 02H ;Delay time for hard disks to get up to speed (2s)
WaitInit:
IF VERBOSE
LXI D, DISKSTATUS ;Print initialization status message
CALL PSTRING
ENDIF
MVI E, REGstatus ;Get status after initilization
CALL IDErd8D ;Check Status (info in [D])
MOV A, D
IF VERBOSE
CALL PHEX ;Print drive initialization status
CALL ZPERCRLF
ENDIF
ANI 80H
JNZ WaitInitL ;Need a longer wait...
POP D ;Restore registers
POP B
RET ;Return. We'll check for errors when we get back
WaitInitL:
MVI A, 2
CALL DELAY$LONG ;Long delay, drive has to get up to speed
DCR B
JNZ WaitInit
XRA A
DCR A
POP D
POP B
RET ;Return NZ. We'll check for errors when we get back
WaitInitErr:
XRA A
DCR A ;Return NZ (error)
POP D ;Restore Registers
POP B
RET ;Return and check for errors there
DELAY$LONG: ;Long delay (Seconds)
STA mDELAYStore
PUSH B
LXI B, 0FFFFH
DELAY2: LDA mDELAYStore
DELAY1: DCR A
JNZ DELAY1
DCX B
MOV A, C
ORA B
JNZ DELAY2
POP B
RET
DELAY$SHORT: ;Short delay (32ms)
MVI A, 40
DELAY3: MVI B, 0
M0: DCR B
JNZ M0
DCR A
JNZ DELAY3
RET
SELECT0: ;Select drive 0
XRA A
JMP SELECTdrive
SELECT1: ;Select drive 1
MVI A, 1
SELECTdrive: ;Select drive [A]
STA mCURRENT$DRIVE
OUT IDEDrive
RET
REM$DRV: ;Remember drive and position
LDA mCURRENT$DRIVE
STA mREM$DRIVE
LHLD mSEC
SHLD mREM$SEC
LHLD mTRK
SHLD mREM$TRK
RET
RET$DRV: ;Return to last drive and position
LDA mREM$DRIVE
STA mCURRENT$DRIVE
OUT IDEDrive
LHLD mREM$SEC
SHLD mSEC
LHLD mREM$TRK
SHLD mTRK
RET
;------------------------------------------------------------------------------
;Sector Read
;------------------------------------------------------------------------------
READSECTOR: ;Read a sector, specified by the 3 bytes in LBA
;Z on success, NZ call error routine if problem
CALL wrlba ;Tell which sector we want to read from.
;Translate first in case of an error, otherewise
;we will get stuck on bad sector
CALL IDEwaitnotbusy ;Make sure drive is ready
JC SHOWerrors ;Returned with NZ set if error
MVI D, COMMANDread
MVI E, REGcommand
CALL IDEwr8D ;Send sec read command to drive.
CALL IDEwaitdrq ;Wait until it's got the data
JC SHOWerrors
LHLD mDMA ;DMA address
MVI B, 0 ;Read 512 bytes to [HL]
MoreRD16:
MVI A, REGdata ;REG register address
OUT IDEportC
ORI IDErdline ;08H+40H, Pulse RD line
OUT IDEportC
IN IDEportA ;Read the lower byte first
MOV M, A
INX H
IN IDEportB ;Then read the upper byte
MOV M, A
INX H
MVI A, REGdata ;Deassert RD line
OUT IDEportC
DCR B
JNZ MoreRD16
MVI E, REGstatus
CALL IDErd8D
MOV A, D
ANI 1H
CNZ SHOWerrors ;If error display status
RET
;------------------------------------------------------------------------------
;Sector Write
;------------------------------------------------------------------------------
WRITESECTOR: ;Write a sector, specified by the 3 bytes in LBA
;Z on success, NZ to error routine if problem
CALL wrlba ;Tell which sector we want to read from.
;Translate first in case of an error, otherewise
;we will get stuck on bad sector
CALL IDEwaitnotbusy ;Make sure drive is ready
JC SHOWerrors
MVI D, COMMANDwrite
MVI E, REGcommand
CALL IDEwr8D ;Tell drive to write a sector
CALL IDEwaitdrq ;Wait unit it wants the data
JC SHOWerrors
LHLD mDMA
MVI B, 0
MVI A, WRITEcfg8255
OUT IDEportCtrl
WRSEC1: MOV A, M
INX H
OUT IDEportA ;Write the lower byte first
MOV A, M
INX H
OUT IDEportB ;Then high byte on B
MVI A, REGdata
PUSH PSW
OUT IDEportC ;Send write command
ORI IDEwrline ;Send WR pulse
OUT IDEportC
POP PSW
OUT IDEportC
DCR B
JNZ WRSEC1
MVI A, READcfg8255 ;Set 8255 back to read mode
OUT IDEportCtrl
MVI E, REGstatus
CALL IDErd8D
MOV A, D
ANI 1H
CNZ SHOWerrors ;If error display status
RET
;------------------------------------------------------------------------------
;Write Logical Block Address (LBA) mode
;------------------------------------------------------------------------------
wrlba:
CALL IDEwaitnotbusy ;Make sure drive isn't busy
JC SHOWErrors ;If error, display status
LHLD mTRK ;Get the CP/M requested track High & Low
MOV A, L ;Get Low byte of track
RRC ;Get bottom two bits in high bits of A
RRC
ANI 0C0H ;Just what were the bottom two bits (now at the top)
MOV C, A ;Save in C
LDA mSEC ;Sector number in A
ANI 03FH ;Take only bottom 6 bits
ORA C ;Add in top 2 bits of track
STA mDRIVE$SEC ;For diagnostic display only
MOV D, A ;Send info to the drive
MVI E, REGsector
CALL IDEwr8D
MOV A, L ;Get low byte of track again
RRC
RRC
ANI 03FH
MOV C, A ;Save in C
MOV A, H ;Get high byte of track.
RRC ;Rotate twice, leaving low 2 bits
RRC ;In upper bits of A
ANI 0C0H ;Mask all but the two bits we want
ORA C ;Add in the top 6 bits of the first track byte
STA mDRIVE$TRK
MOV D, A ;Send Low TRK#
MVI E, REGcylinderLSB
CALL IDEwr8D
MOV A, H ;Get high byte of track
RRC ;Just the top 6 bits
RRC
ANI 03FH
STA mDRIVE$TRK+1
MOV D, A ;Send High TRK#
MVI E, REGcylinderMSB
CALL IDEwr8D
MVI D, 1 ;One sector at a time
MVI E, REGseccnt
CALL IDEwr8D
RET
;------------------------------------------------------------------------------
;Wait for drive to come ready
;------------------------------------------------------------------------------
IDEwaitnotbusy: ;Drive READY if status = 01000000
MVI B, 0FFH
MVI A, 0FFH ;Delay must be above 80H, longer for slow drives
STA mDELAYStore
MoreWait:
MVI E, REGstatus ;Wait for RDY bit to be set
CALL IDErd8D
MOV A, D
ANI 11000000B
XRI 01000000B
JZ DoneNotbusy
DCR B
JNZ MoreWait
LDA mDELAYStore ;Check timeout delay
DCR A
STA mDELAYStore
JNZ MoreWait
STC ;Set carry to indicate an error
ret
DoneNotBusy:
ORA A ;Clear carry it indicate no error
RET
;------------------------------------------------------------------------------
;Wait for drive to assert data request (DRQ) line ready
;------------------------------------------------------------------------------
IDEwaitdrq:
MVI B, 0FFH
MVI A, 0FFH ;Delay must be above 80H, longer for slow drives
STA mDELAYStore
MoreDRQ:
MVI E, REGstatus ;Wait for DRQ bit to be set
CALL IDErd8D
MOV A, D
ANI 10001000B
CPI 00001000B
JZ DoneDRQ
DCR B
JNZ MoreDRQ
LDA mDELAYStore ;Check timeout delay
DCR A
STA mDELAYStore
JNZ MoreDRQ
STC ;Set carry to indicate error
RET
DoneDRQ:
ORA A ;Clear carry
RET ;Return drive status in A
;------------------------------------------------------------------------------
;Clear the ID buffer
;------------------------------------------------------------------------------
CLEAR$ID$BUFFER:
LXI H, IDBuffer
LXI B, 512
CLEAR2: MVI A, ' '
MOV M, A
INX H
DCX B
MOV A, C
ORA B
JNZ CLEAR2
LXI H, IDBuffer ;Zero for cylinder, heads, sectors
LXI B, 14
CLEAR3: MVI A, 0
MOV M, A
INX H
DCX B
MOV A, C
ORA B
JNZ CLEAR3
RET
;------------------------------------------------------------------------------
; Low Level 8 bit R/W to the drive controller. These are the routines that talk
; directly to the drive controller registers, via the 8255 chip.
; Note the 16 bit I/O to the drive (which is only for SEC R/W) is done directly
; in the routines READSECTOR & WRITESECTOR for speed reasons.
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
;Read One Byte
;------------------------------------------------------------------------------
IDErd8D: ;Read 8 bits from IDE register in [E],
MOV A, E ;and return info in [D]
OUT IDEportC ;Drive address onto control lines
ORI IDErdline ;RD pulse pin (40H)
OUT IDEportC ;Assert read pin
IN IDEportA
MOV D, A ;Return with data in [D]
MOV A, E
OUT IDEportC ;Deassert RD pin
XRA A
OUT IDEportC ;Zero all port C lines
RET
;------------------------------------------------------------------------------
;Write One Byte
;------------------------------------------------------------------------------
IDEwr8D: ;Write Data in [D] to IDE register [E]
MVI A, WRITEcfg8255 ;Set 8255 to write mode
OUT IDEportCtrl
MOV A, D ;Get data put it in 8255 A port
OUT IDEportA
MOV A, E ;Select IDE register
OUT IDEportC
ORI IDEwrline ;Lower WR line
OUT IDEportC
MOV A, E ;Raise WR line
OUT IDEportC ;Deassert RD pin
XRA A ;Deselect all lines including WR line
OUT IDEportC
MVI A, READcfg8255 ;Config 8255 chip, read mode on return
OUT IDEportCtrl
RET
;------------------------------------------------------------------------------
;This code is written to reside and run from 0H. To re-introduce the CPMLDR,
;it must be copied from where it is stored in high memory and relocated to 100H
;in RAM, which overwrites this program.
;------------------------------------------------------------------------------
CPM$MOVE$CODE
LXI H, BUFFER
LXI D, 100H
LXI B, (12*512)
LDIR
JMP 100H
CPM$MOVE$CODE$END:
;------------------------------------------------------------------------------
;
;String constants - Messages generated by this program
;
;------------------------------------------------------------------------------
SIGN$ON: DB CR,LF,'IDE Disk Drive Utility Program v3.0 12-21-2022',CR,LF,LF,'$'
SEL0MSG DB 'Selecting first IDE drive.',CR,LF,'$'
SEL1MSG DB 'Selecting second IDE drive.',CR,LF,'$'
INITDRIVE DB 'Initializing drive. $'
READING$ID DB 'Reading drive ID. $'
GETTING$ID DB 'Getting drive ID...',CR,LF,'$'
DISKSTATUS DB 'Status is $'
INIT$0$ERROR: DB 'Initialization of first drive failed. Aborting program.',BELL,CR,LF,LF,'$'
INIT$1$ERROR DB 'Initialization of second drive failed. (Possibly not present).',BELL,CR,LF,LF,'$'
ID$ERROR: DB 'Error obtaining drive ID.',BELL,CR,LF,'$'
INIT$DR$OK: DB 'Drive initialized OK.',CR,LF,LF,'$'
BAD$DRIVE: DB CR,LF,'First Drive ID Information appears invalid.',CR,LF
DB 'Aborting program.',BELL,CR,LF,LF,'$'
DRIVE0$INFO: DB '------------ Drive 0 -------------',CR,LF,'$'
DRIVE1$INFO: DB '------------ Drive 1 -------------',CR,LF,'$'
msgmdl: DB 'Model: $'
msgsn: DB 'S/N: $'
msgrev: DB 'Rev: $'
msgcy: DB 'Cyl: $'
msghd: DB ', Hd: $'
msgsc: DB ', Sec: $'
msgCPMTRK: DB 'CPM TRK = $'
msgCPMSEC: DB ' CPM SEC = $'
msgLBA: DB ' (LBA = 00$'
MSGBracket DB ')$'
msgLBAsup1: DB 'LBA is $'
msgLBAnot: DB 'NOT $'
msgLBAsup2 DB 'supported',CR,LF,'$'
DRIVE$0$MSG DB CR,LF,LF,' >>> DRIVE #0 <<<$'
DRIVE$1$MSG DB CR,LF,LF,' >>> DRIVE #1 <<<$'
CMD$STRING1: DB ' IDE Board Diagnostic MAIN MENU',CR,LF,LF
DB '(A) Select Drive 0 (O) Drive 0 Information '
DB '(H) Backup Disk',CR,LF
DB '(B) Select Drive 1 (I) Drive 1 Information '
DB '(G) Restore Backup',CR,LF
DB '(K) Set LBA by Partition (M) Show Buffer w/o Read '
DB '(E) Clear Buffer',CR,LF
DB '(L) Set LBA Track, Sector (R) Read Sector to Buffer '
DB '(W) Write Buffer to Sector',CR,LF
DB '(N) Next Sector (V) Read N Sectors '
DB '(X) Write N Sectors',CR,LF
DB '(P) Previous Sector (S) Sequental Sector Read '
DB '(C) Copy Partition',CR,LF
DB '(U) Power Up (T) Power Down '
DB '(Y) Verify Partition',CR,LF
DB '(F) Format Disk (D) Set Display ON '
DB '(ESC) Quit',CR,LF
DB LF,'Current settings: $'
CMD$STRING2: DB ' IDE Board Diagnostic MAIN MENU',CR,LF,LF
DB '(A) Select Drive 0 (O) Drive 0 Information '
DB '(H) Backup Disk',CR,LF
DB '(B) Select Drive 1 (I) Drive 1 Information '
DB '(G) Restore Backup',CR,LF
DB '(K) Set LBA by Partition (M) Show Buffer w/o Read '
DB '(E) Clear Buffer',CR,LF
DB '(L) Set LBA Track, Sector (R) Read Sector to Buffer '
DB '(W) Write Buffer to Sector',CR,LF
DB '(N) Next Sector (V) Read N Sectors '
DB '(X) Write N Sectors',CR,LF
DB '(P) Previous Sector (S) Sequental Sector Read '
DB '(C) Copy Partition',CR,LF
DB '(U) Power Up (T) Power Down '
DB '(Y) Verify Partition',CR,LF
DB '(F) Format Disk (D) Set Display OFF '
DB '(ESC) Quit',CR,LF
DB LF,'Current settings: $'
Prompt: DB CR,LF,LF,'Please enter command > $'
Response: DB CR,LF,'Command received: $'
msgsure: DB CR,LF,'Warning: this will change data on the drive, '
DB 'are you sure? $'
AreYouSure DB CR,LF,'Are you sure? $'
DoYouWant DB CR,LF,'Is that what you want to do? $'
msgrd: DB CR,LF,'Sector Read OK',CR,LF,'$'
msgwr: DB CR,LF,'Sector Write OK',CR,LF,'$'
GET$LBA: DB 'Enter CPM style TRK & SEC values (in hex).',CR,LF,'$'
SEC$RW$ERROR DB 'Drive Error, Status Register = $'
ERR$REG$DATA DB 'Drive Error, Error Register = $'
ENTER$SECL DB 'Sector number (LOW byte, xxH) = $'
ENTER$SECH DB 'Sector number (HIGH byte, xxH) = $'
ENTER$TRKL DB 'Track number (LOW byte, xxH) = $'
ENTER$TRKH DB 'Track number (HIGH byte, xxH) = $'
ENTER$HEAD DB 'Head number (01-0F) = $'
ENTER$COUNT DB 'Number of sectors to R/W = $'
DRIVE$BUSY DB 'Drive Busy (bit 7) stuck high. Status = $'
DRIVE$NOT$READY DB 'Drive Ready (bit 6) stuck low. Status = $'
DRIVE$WR$FAULT DB 'Drive write fault. Status = $'
UNKNOWN$ERROR DB 'Unknown error in status register. Status = $'
BAD$BLOCK DB 'Bad Sector ID. Error Register = $'
UNRECOVER$ERR DB 'Uncorrectable data error. Error Register = $'
READ$ID$ERROR DB 'Error setting up to read Drive ID',CR,LF,'$'
SEC$NOT$FOUND DB 'Sector not found. Error Register = $'
INVALID$CMD DB 'Invalid Command. Error Register = $'
TRK0$ERR DB 'Track Zero not found. Error Register = $'
UNKNOWN$ERROR1 DB 'Unknown Error. Error Register = $'
CONTINUE$MSG DB CR,LF,'ESC to abort. Any other key to continue. $'
FORMAT$MSG DB 'FORMAT DISK. Fill all sectors with E5'
DB 60H,'s on the current drive.$'
ReadN$MSG DB CR,LF,'Read multiple sectors from current drive to RAM buffer.'
DB CR,LF,'How many 512 byte sectors (xx HEX):$'
WriteN$MSG DB CR,LF,'Write multiple sectors from RAM buffer to current drive.'
DB CR,LF,'How many 512 byte sectors (xx HEX):$'
ReadingN$MSG DB CR,LF,'Reading Sector at: $'
WritingN$MSG DB CR,LF,'Writing Sector at: $'
msgErr DB CR,LF,'Sorry, that was not a valid menu option!$'
FormatDone DB CR,LF,'Disk Format Complete.',CR,LF,'$'
BackupDone DB CR,LF,'Disk partition copy complete.',CR,LF,'$'
PartnExpln DB CR,LF,'Each 2Gb physical disk is structured as 256'
DB ' "partitions" of 8Mb each. The CP/M'
DB CR,LF,'operating system can directly access only'
DB ' partition 00, but all the others can'
DB CR,LF,'be used as backups or archives. The backup'
DB ' partitions are numbered 00 - FF.',CR,LF,'$'
BackupMsg DB CR,LF,'This will copy data from the main CP/M'
DB ' partition on the current drive to a'
DB CR,LF,'backup partition.',CR,LF,'$'
RestoreMsg DB CR,LF,'This will restore data from a backup'
DB ' partition to the main CP/M partition on'
DB CR,LF,'the current drive.',CR,LF,'$'
CopyMsg DB CR,LF,'This will copy data from any partition to'
DB ' any other partition on either drive.',CR,LF,'$'
Enter$Partition DB CR,LF,LF,'Choose a partition number (00-FF) $'
Enter$Bkup$Part DB CR,LF,LF,'Choose a backup partition (01-FF) $'
Enter$Src$Partn DB CR,LF,'Choose source partition (00-FF) $'
Enter$Tgt$Partn DB CR,LF,'Choose target partition (00-FF) $'
Enter$Src$Drive DB CR,LF,'Choose source drive (00 or 01) $'
Enter$Tgt$Drive DB CR,LF,'Choose target drive (00 or 01) $'
ConfirmCopy DB CR,LF,'This will copy drive $'
ConfirmCmp DB CR,LF,'This will compare drive $'
Partition DB ' partition $'
ToDrive DB ' to drive $'
AtEnd DB CR,LF,'At end of disk partition!',CR,LF,'$'
RBackup$MSG DB 'Reading track: $'
WBackup$MSG DB 'H. Writing track: $'
H$Msg DB 'H$'
RestoreDone DB CR,LF,'Restore of disk data from backup partition complete.',CR,LF,'$'
DRV$NOT$FOUND DB CR,LF,LF,'Drive not connected.',CR,LF,'$'
RANGE$MSG DB CR,LF,LF,'Value out of range.',CR,LF,'$'
INVALID$MSG DB CR,LF,LF,'Invalid input.',CR,LF,'$'
CPM$ERROR DB CR,LF,'Error reading CPMLDR.',CR,LF,'$'
CPM$ERROR1 DB CR,LF,'Data error reading CPMLDR. (The first byte loaded was not 31H).',CR,LF,'$'
MOVE$REQUEST DB CR,LF,'The CPMLDR image is now at 3000H in RAM. '
DB 'To boot CPM you will have to'
DB CR,LF,'overwrite this program at 100H. Do you wish to do so? $'
SET0$MSG DB CR,LF,'Current drive is now #0 (Yellow LED)$'
SET1$MSG DB CR,LF,'Current drive is now #1 (Green LED)$'
FILL$MSG DB CR,LF,'Sector buffer in RAM filled with 0',27H,'s$'
CopyDone DB CR,LF,LF,'Partition copy complete.',CR,LF,'$'
CopyTrk$MSG DB 'Copying track $'
OnDrive$MSG DB ' on drive $'
ToTrack$MSG DB ' to track $'
VerifyMsg DB CR,LF,'This will compare any two partitions on either drive'
DB ' and will report any',CR,LF,'differences.',CR,LF,'$'
VerifyTrk$MSG DB 'Comparing track $'
VerifyDone DB CR,LF,LF,'Partition verification complete.',CR,LF,'$'
Verify$ERR DB CR,LF,BELL,'Verify error on track $'
SEC$Msg DB 'H Sector $'
;------------------------------------------------------------------------------
;RAM usage
;------------------------------------------------------------------------------
RAMAREA DB ' RAM STORE AREA -------->'
mDMA DW buffer
mDRIVE$SEC DB 0H
mDRIVE$TRK DW 0H
mDisplayFlag DB 0FFH ;Display of sector data initially ON
mSEC DW 0H
mTRK DW 0H
mSEC1 DW 0H ;For disk partition copy
mTRK1 DW 0H
mSEC2 DW 0H
mTRK2 DW 0H
mSrc$Drive DB 0H ;User-inputs for copy and restore commands
mSrc$Partn DB 0H
mTgt$Drive DB 0H
mTgt$Partn DB 0H
mPART$NUM DB 0H ;Backup partition (01-FF)
mStartLineHex DW 0H
mStartLineASCII DW 0H
mBYTE$COUNT DW 0H
mSECTOR$COUNT DW 0H
mDELAYStore DB 0H
mCURRENT$DRIVE DB 0H
mREM$DRIVE DB 0H
mREM$SEC DW 0H
mREM$TRK DW 0H
mLast$Drive DB 0H ;0 or 1
DS 100H ;Stack is 256 bytes, just before buffers
STACK: DW 0H
DB ' Start of ID buffer-->'
IDbuffer: DS 512 ;IDbuffer is 512 bytes with text before and after
DB '<--End of ID buffer '
ORG BUFFER$ORG
BUFFER: DB '>--Start buffer'
DS 481 ;buffer is 512 bytes total
DB 'End of buffer--<'
BUFFER2: DB '>--Start buffer2'
DS 479 ;buffer2 is 512 bytes total
DB 'End of buffer2--<'
END