forked from MirrorRepos/RomWBW
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1371 lines
26 KiB
1371 lines
26 KiB
; N8VEM PPI IDE test program for checkout of IDE drive connected to the 8255 PPI
|
|
;
|
|
; Written by Max Scane July 2009
|
|
; Based on work by Paul Stoffregen (www.pjrc.com)
|
|
;
|
|
; Note: due to a known anomaly in the 8255, some signals ( all active low signals) on the IDE bus require an inverter (74LS04 or 74LS14)
|
|
; between the 8255 and the IDE drive.
|
|
; This is due to the 8255 returning all signals to 0 (low) when a mode change is performed (for read and write to IDE data bus).
|
|
;
|
|
; This test program will allow you to check out an attached IDE drive using the basic commands:
|
|
;
|
|
; u - Spin up the drive
|
|
; d - Spin down the drive
|
|
; s - Read and print out drive status
|
|
; i - Execute drive ID command and print result correctly
|
|
; r - Read the current LBA into the sector buffer and print status
|
|
; w - Write the sector buffer to the current LBA and print status
|
|
; l - Change the current LBA
|
|
; h - Dump the current sector buffer in hexdump format
|
|
; f - format drive for CP/M use (fill with 0xE5)
|
|
; e - Display drive error information
|
|
; x - Return to CP/M
|
|
; n - read and hexdump next LBA
|
|
; ? - Display command menu help
|
|
; p - set PPI port
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; - Updated December 2014 MS - changed IO routines to support different PPI ports.
|
|
; - Updated July 2021 Andrew Lynch - Minor cosmetic updates
|
|
|
|
;********************* HARDWARE IO ADR ************************************
|
|
|
|
DEFBASE: .EQU 60H ; PPI base I/O address default
|
|
|
|
;
|
|
; Offsets to the various PPI registers
|
|
|
|
IDELSB: .EQU 0 ; LSB
|
|
IDEMSB: .EQU 1 ; MSB
|
|
IDECTL: .EQU 2 ; Control Signals
|
|
PIOCONT: .EQU 3 ; CONTROL BYTE PIO 82C55
|
|
|
|
; PPI control bytes for read and write to IDE drive
|
|
|
|
rd_ide_8255: .EQU 10010010b ; ide_8255_ctl out, ide_8255_lsb/msb input
|
|
wr_ide_8255: .EQU 10000000b ; all three ports output
|
|
|
|
;IDE control lines for use with ide_8255_ctl. Change these 8
|
|
;constants to reflect where each signal of the 8255 each of the
|
|
;IDE control signals is connected. All the control signals must
|
|
;be on the same port, but these 8 lines let you connect them to
|
|
;whichever pins on that port.
|
|
|
|
ide_a0_line: .EQU 01H ; direct from 8255 to IDE interface
|
|
ide_a1_line: .EQU 02H ; direct from 8255 to IDE interface
|
|
ide_a2_line: .EQU 04H ; direct from 8255 to IDE interface
|
|
ide_cs0_line: .EQU 08H ; inverter between 8255 and IDE interface
|
|
ide_cs1_line: .EQU 10H ; inverter between 8255 and IDE interface
|
|
ide_wr_line: .EQU 20H ; inverter between 8255 and IDE interface
|
|
ide_rd_line: .EQU 40H ; inverter between 8255 and IDE interface
|
|
ide_rst_line: .EQU 80H ; inverter between 8255 and IDE interface
|
|
|
|
|
|
;------------------------------------------------------------------
|
|
; More symbolic constants... these should not be changed, unless of
|
|
; course the IDE drive interface changes, perhaps when drives get
|
|
; to 128G and the PC industry will do yet another kludge.
|
|
|
|
;some symbolic constants for the ide registers, which makes the
|
|
;code more readable than always specifying the address pins
|
|
|
|
ide_data: .EQU ide_cs0_line
|
|
ide_err: .EQU ide_cs0_line + ide_a0_line
|
|
ide_sec_cnt: .EQU ide_cs0_line + ide_a1_line
|
|
ide_sector: .EQU ide_cs0_line + ide_a1_line + ide_a0_line
|
|
ide_cyl_lsb: .EQU ide_cs0_line + ide_a2_line
|
|
ide_cyl_msb: .EQU ide_cs0_line + ide_a2_line + ide_a0_line
|
|
ide_head: .EQU ide_cs0_line + ide_a2_line + ide_a1_line
|
|
ide_command: .EQU ide_cs0_line + ide_a2_line + ide_a1_line + ide_a0_line
|
|
ide_status: .EQU ide_cs0_line + ide_a2_line + ide_a1_line + ide_a0_line
|
|
ide_control: .EQU ide_cs1_line + ide_a2_line + ide_a1_line
|
|
ide_astatus: .EQU ide_cs1_line + ide_a2_line + ide_a1_line + ide_a0_line
|
|
|
|
;IDE Command Constants. These should never change.
|
|
ide_cmd_recal: .EQU 10H
|
|
ide_cmd_read: .EQU 20H
|
|
ide_cmd_write: .EQU 30H
|
|
ide_cmd_init: .EQU 91H
|
|
ide_cmd_id: .EQU 0ECH
|
|
ide_cmd_spindown: .EQU 0E0H
|
|
ide_cmd_spinup: .EQU 0E1H
|
|
|
|
CR: .EQU 0Dh
|
|
LF: .EQU 0Ah
|
|
BELL: .EQU 07H
|
|
|
|
|
|
.org 100H
|
|
|
|
start:
|
|
|
|
; save stack pointer so that we can return to calling program
|
|
|
|
ld (savsp),sp
|
|
ld sp,stack
|
|
|
|
call set_ppi_rd ; setup PPI chip to known state
|
|
|
|
ld hl,lba1 ; zero LBA variables
|
|
call clrlba
|
|
|
|
ld hl,lba2
|
|
call clrlba
|
|
|
|
ld hl,lba3
|
|
call clrlba
|
|
|
|
|
|
call print
|
|
.db "PPI IDE test program v0.6b",CR,LF,0
|
|
|
|
call prport
|
|
call prstatus
|
|
call prlba
|
|
|
|
call ide_init
|
|
call prstatus
|
|
|
|
call crlf
|
|
|
|
menu:
|
|
call print ; display prompt
|
|
.db "Enter command (u,d,s,i,r,w,l,h,f,e,n,p,?,x) > ",0
|
|
|
|
call cin ; get command from console in reg A
|
|
push af
|
|
call crlf
|
|
pop af
|
|
|
|
mnu1:
|
|
cp 'd' ; spin down command
|
|
jr nz,mnu2
|
|
call spindown
|
|
jr menu
|
|
mnu2:
|
|
cp 'u' ; spinup command
|
|
jr nz,mnu3
|
|
call spinup
|
|
jr menu
|
|
|
|
mnu3:
|
|
cp 's' ; print IDE status reg contents
|
|
jr nz,mnu4
|
|
call prstatus
|
|
jr menu
|
|
|
|
mnu4:
|
|
cp 'i'
|
|
jr nz,mnu5 ; drive ID command
|
|
call drive_id
|
|
jr menu
|
|
|
|
mnu5:
|
|
cp 'r'
|
|
jr nz,mnu6 ; read command
|
|
call prlba ; print out the current LBA
|
|
call read_sector ; read current LBA
|
|
call prstatus
|
|
jr menu
|
|
|
|
mnu6:
|
|
cp 'w'
|
|
jr nz,mnu7 ; write command
|
|
call prlba ; print out the current LBA
|
|
call write_sector ; write current LBA
|
|
call prstatus
|
|
jr menu
|
|
|
|
mnu7:
|
|
cp 'l'
|
|
jr nz,mnu8 ; LBA command
|
|
call prlba ; print out the current LBA
|
|
mnu7a:
|
|
call print
|
|
.db "Enter new LBA: ",0
|
|
|
|
ld de,lba1 ; get LBA in lba1
|
|
call getlba
|
|
jp nc,menu ; valid, finished
|
|
|
|
call print
|
|
.db "Invalid LBA",CR,LF,0
|
|
|
|
jr mnu7a ; try again
|
|
|
|
|
|
mnu8:
|
|
cp 'h'
|
|
jr nz,mnu9 ; hexdump command
|
|
call hexdump ; hexdump the current sector buffer
|
|
jp menu
|
|
|
|
mnu9:
|
|
cp 'f'
|
|
jr nz,mnua ; drive format
|
|
call format
|
|
jp menu
|
|
|
|
mnua:
|
|
cp 'e'
|
|
jr nz,mnub ; get error register
|
|
call get_err
|
|
push af
|
|
call print
|
|
.db "Error register is: ",0
|
|
|
|
pop af
|
|
call prhex
|
|
call crlf
|
|
|
|
|
|
ld a,ide_head
|
|
call ide_read
|
|
ld a,c
|
|
call prhex
|
|
|
|
ld a,ide_cyl_msb
|
|
call ide_read
|
|
ld a,c
|
|
call prhex
|
|
|
|
ld a,ide_cyl_lsb
|
|
call ide_read
|
|
ld a,c
|
|
call prhex
|
|
|
|
|
|
ld a,ide_sector
|
|
call ide_read
|
|
ld a,c
|
|
call prhex
|
|
|
|
call crlf
|
|
|
|
ld a,ide_sec_cnt
|
|
call ide_read
|
|
ld a,c
|
|
call prhex
|
|
call crlf
|
|
|
|
jp menu
|
|
|
|
|
|
mnub:
|
|
cp 'n'
|
|
jr nz,mnuc
|
|
ld hl,lba1
|
|
call inclba
|
|
call prlba
|
|
call read_sector
|
|
call hexdump
|
|
jp menu
|
|
|
|
mnuc:
|
|
cp 'p'
|
|
jr nz,mnux
|
|
call prport
|
|
mnuca:
|
|
call print
|
|
.db "Enter new PPI base port: ",0
|
|
|
|
ld a,(ppibase)
|
|
call gethexbyte
|
|
jr c,mnucb
|
|
ld (ppibase),a ; save it
|
|
call prport
|
|
jp menu
|
|
|
|
mnucb:
|
|
call print
|
|
.db "Invalid PPI base port value",CR,LF,0
|
|
jr mnuca
|
|
|
|
mnux:
|
|
cp 'x' ; exit command
|
|
jp nz,mnuhlp
|
|
ld sp,(savsp)
|
|
ret
|
|
|
|
|
|
mnuhlp:
|
|
call print
|
|
.db "Commands available:",CR,LF,LF
|
|
.db "u - Spin Up drive",CR,LF
|
|
.db "d - Spin Down drive",CR,LF
|
|
.db "s - Print drive Status",CR,LF
|
|
.db "i - Query drive using ID command",CR,LF
|
|
.db "r - Read a sector addressed by the lba variable",CR,LF
|
|
.db "w - Write a sector adresses by the lba variable",CR,LF
|
|
.db "l - Change the current LBA variable",CR,LF
|
|
.db "h - Hexdump the current buffer",CR,LF
|
|
.db "f - Format the drive for CP/M use (fill with 0xE5)",CR,LF
|
|
.db "e - Display drive Error information",CR,LF
|
|
.db "p - Change base IO port",CR,LF
|
|
.db "? - Display command menu help",CR,LF
|
|
.db "x - eXit from this utility",CR,LF,LF,0
|
|
jp menu
|
|
|
|
|
|
format:
|
|
call print
|
|
.db "Warning - this command will write data to the drive",CR,LF,LF
|
|
.db "All existing data will be over written",CR,LF,LF
|
|
.db "Is that what you want to do ? ",0
|
|
|
|
call cin ; get answer
|
|
cp 'y'
|
|
jr z,fmt1 ; if yes then continue
|
|
call print
|
|
.db " Command aborted",CR,LF,0
|
|
ret
|
|
|
|
fmt1:
|
|
ld a,0E5h
|
|
call fillbuf ; setup sector buffer
|
|
fmt2:
|
|
|
|
call print
|
|
.db CR,LF,"Enter starting LBA: ",0
|
|
|
|
ld de,lba2 ; starting LBA
|
|
call getlba
|
|
jr nc,fmt3
|
|
|
|
call print
|
|
.db "Invalid LBA",CR,LF,0
|
|
jr fmt2 ; try again
|
|
|
|
fmt3:
|
|
|
|
call crlf
|
|
|
|
fmt4:
|
|
call print
|
|
.db "Enter ending LBA: ",0
|
|
|
|
ld de,lba3 ; ending LBA
|
|
call getlba
|
|
jr nc,fmt5
|
|
|
|
call print
|
|
.db "Invalid LBA",CR,LF,0
|
|
|
|
jr fmt4 ; try again
|
|
|
|
fmt5:
|
|
call crlf
|
|
|
|
call print ; say what is going to happen
|
|
.db "Format will start at LBA ",0
|
|
|
|
ld a,(lba2+3)
|
|
call prhex
|
|
ld a,(lba2+2)
|
|
call prhex
|
|
ld a,(lba2+1)
|
|
call prhex
|
|
ld a,(lba2)
|
|
call prhex
|
|
|
|
call print
|
|
.db " and finish at LBA ",0
|
|
ld a,(lba3+3)
|
|
call prhex
|
|
ld a,(lba3+2)
|
|
call prhex
|
|
ld a,(lba3+1)
|
|
call prhex
|
|
ld a,(lba3)
|
|
call prhex
|
|
|
|
call crlf
|
|
call print
|
|
.db "Type y to continue or any other key to abort ",0
|
|
|
|
call cin
|
|
cp 'y'
|
|
jp nz,fmtx
|
|
call crlf
|
|
|
|
; add the actual format code here
|
|
; get starting LBA
|
|
; get ending LBA
|
|
; fill buffer with E5
|
|
|
|
ld hl,lba2
|
|
ld de,lba1
|
|
call cpylba ; copy start LBA to LBA
|
|
call inclba
|
|
|
|
|
|
fmt6:
|
|
push hl
|
|
call print ; display progress
|
|
.db "Writing LBN: ",0
|
|
ld a,(lba1+3)
|
|
call prhex
|
|
ld a,(lba1+2)
|
|
call prhex
|
|
ld a,(lba1+1)
|
|
call prhex
|
|
ld a,(lba1)
|
|
call prhex
|
|
ld a,CR
|
|
call cout
|
|
pop hl
|
|
|
|
; do some stuff to format here
|
|
call write_sector
|
|
|
|
; need to check status after each call and check for errors
|
|
;
|
|
ld hl,lba1 ; LBA for disk operation
|
|
call inclba
|
|
|
|
ld hl,lba1
|
|
ld de,lba3
|
|
call cplba
|
|
|
|
jp nz,fmt6
|
|
|
|
call crlf
|
|
|
|
fmtx:
|
|
call print ; finished
|
|
.db "Format complete",CR,LF,0
|
|
ret
|
|
|
|
|
|
getlba: ; get an LBA value from the console and validate it
|
|
push de ; save LBA variable
|
|
ld c,0ah ; bdos read console buffer
|
|
ld de,conbuf
|
|
call 5 ; get edited string
|
|
|
|
call crlf
|
|
|
|
; ok we now have an ascii string representing the LBA. now we have to validate it
|
|
|
|
pop de
|
|
ld hl,conbuf
|
|
inc hl
|
|
ld b,(hl) ; get character count
|
|
glba1:
|
|
inc hl ; HL = address of buffer
|
|
ld a,(hl) ; get next character
|
|
|
|
call ishex
|
|
ret c ; return with carry set if any char is invalid
|
|
|
|
; ok we are here when we have a valid character (0-9,A-F,a-f) need to convert to binary
|
|
; character is still in A
|
|
|
|
cp 3AH ; test for 0-9
|
|
jp m,glba4
|
|
cp 47H ; test for A-F
|
|
jp m,glba3
|
|
cp 67H ; test for a-f
|
|
jp m,glba2
|
|
|
|
glba2:
|
|
sub 20H ; character is a-f
|
|
glba3:
|
|
sub 07h ; character is A-F
|
|
glba4:
|
|
sub 030H ; character is 0-9
|
|
|
|
ld (hl),a ; save back in buffer as binary
|
|
djnz glba1 ; continue checking the buffer
|
|
|
|
; need to pack bytes into the destination LBA which is in de and points to the LSB
|
|
|
|
glba5:
|
|
|
|
; - need to change the endian-ness
|
|
push de
|
|
ex de,hl ; clear LBA ready
|
|
; push de ; address of LBA
|
|
; pop hl ; address of input buffer
|
|
ld a,0 ; zero existing LBA variable
|
|
ld (hl),a
|
|
inc hl
|
|
ld (hl),a
|
|
inc hl
|
|
ld (hl),a
|
|
inc hl
|
|
ld (hl),a
|
|
|
|
ex de,hl ; de now positioned at end of LBA
|
|
pop de ; restore LBA
|
|
; de still contains dest address
|
|
; now pack and store LBA
|
|
|
|
ld hl,conbuf+1 ; get character count
|
|
ld b,(hl)
|
|
inc hl ; point to first character in buffer
|
|
|
|
glba6:
|
|
push de ; save starting address for next subsequent rotations
|
|
ld a,(hl) ; get next char from buffer
|
|
inc hl ; next character
|
|
ex de,hl ; switch to LBA
|
|
rld ; shift nibble into LBA
|
|
inc hl
|
|
rld
|
|
inc hl
|
|
rld
|
|
inc hl
|
|
rld
|
|
ex de,hl ; back to console buffer
|
|
pop de ; restore address of LBA
|
|
djnz glba6 ; process next character
|
|
|
|
scf
|
|
ccf ; exit with carry clear = success
|
|
ret
|
|
|
|
|
|
gethexbyte:
|
|
push af ; save incoming value
|
|
ld c,0ah ; bdos read console buffer
|
|
ld de,conbuf
|
|
call 5 ; get edited string
|
|
call crlf
|
|
pop de ; restore incoming to d
|
|
|
|
; ok we should now have a string with a hex number
|
|
ld hl,conbuf
|
|
inc hl
|
|
ld a,(hl) ; get character count
|
|
inc hl
|
|
cp 3
|
|
jr c,ghb0 ; ok if <= 2 chars
|
|
scf ; signal error
|
|
ret ; and return
|
|
|
|
ghb0:
|
|
or a ; set flags
|
|
jr nz,ghb1 ; got chars, go ahead
|
|
ld a,d ; restore incoming value
|
|
or a ; signal success
|
|
ret ; and done
|
|
|
|
ghb1:
|
|
ld b,a ; count to b
|
|
ld c,0 ; initial value
|
|
|
|
ghb2:
|
|
ld a,(hl) ; get next char
|
|
inc hl
|
|
call ishex
|
|
ret c ; abort on non-hex char
|
|
|
|
; ok we are here when we have a valid character (0-9,A-F,a-f) need to convert to binary
|
|
; character is still in A
|
|
|
|
cp 3AH ; test for 0-9
|
|
jp m,ghb2c
|
|
cp 47H ; test for A-F
|
|
jp m,ghb2b
|
|
cp 67H ; test for a-f
|
|
jp m,ghb2a
|
|
ghb2a: sub 20H ; character is a-f
|
|
ghb2b: sub 07H ; character is A-F
|
|
ghb2c: sub 30H ; character is 0-9
|
|
|
|
rlc c ; multiply cur value by 16
|
|
rlc c
|
|
rlc c
|
|
rlc c
|
|
add a,c ; add to accum
|
|
ld c,a ; put back in c
|
|
|
|
djnz ghb2 ; loop thru all chars
|
|
|
|
ld a,c ; into a for return
|
|
or a ; signal success
|
|
ret ; done
|
|
|
|
ishex:
|
|
cp 30h ; check if less than character 0
|
|
jp m,nothex
|
|
cp 3Ah ; check for > 9
|
|
jp m,ishx1 ; ok, character is 1-9
|
|
|
|
cp 41h ; check for character less than A
|
|
jp m,nothex
|
|
cp 47H ; check for characters > F
|
|
jp m,ishx1
|
|
|
|
cp 61H ; check for characters < a
|
|
jp m,nothex
|
|
|
|
cp 67H ; check for character > f
|
|
jp m,ishx1
|
|
nothex:
|
|
scf ; set carry to indicate fail
|
|
ret
|
|
|
|
ishx1:
|
|
scf
|
|
ccf
|
|
ret
|
|
|
|
|
|
|
|
fillbuf:
|
|
; fill sector buffer with character specified in A
|
|
ld hl,buffer
|
|
ld b,0
|
|
fb1:
|
|
ld (hl),a ; store character in buffer
|
|
inc hl
|
|
ld (hl),a
|
|
inc hl
|
|
djnz fb1
|
|
ret
|
|
|
|
|
|
hexdump:
|
|
|
|
call print ; print heading
|
|
.db "Current sector buffer contents:",CR,LF,LF,0
|
|
|
|
ld b,32 ; line counter
|
|
ld hl,buffer ; address of buffer to dump
|
|
hxd1:
|
|
|
|
push bc ; save loop counter
|
|
push hl ; save address pointer
|
|
|
|
push hl
|
|
ld a,h
|
|
call prhex ; print hi byte of address
|
|
pop hl
|
|
|
|
push hl
|
|
ld a,l
|
|
call prhex ; print lo byte of address
|
|
ld a,' '
|
|
call cout
|
|
pop hl
|
|
|
|
|
|
ld b,16 ; how many characters do we display
|
|
hxd2:
|
|
push bc
|
|
|
|
ld a,(hl) ; get byte from buffer
|
|
inc hl
|
|
push hl
|
|
|
|
call prhex ; display it in hex
|
|
ld a,' '
|
|
call cout
|
|
|
|
pop hl
|
|
pop bc
|
|
djnz hxd2
|
|
|
|
pop hl
|
|
|
|
ld b,16 ; how many characters do we display
|
|
hxd3:
|
|
push bc
|
|
|
|
ld a,(hl) ; get byte from buffer
|
|
inc hl
|
|
push hl
|
|
|
|
call prascii ; display it in ASCII
|
|
|
|
pop hl
|
|
pop bc
|
|
djnz hxd3
|
|
|
|
push hl
|
|
call crlf
|
|
|
|
pop hl
|
|
pop bc
|
|
|
|
ld a,b ; check for screen pause
|
|
cp 16
|
|
jp nz,hxd4
|
|
|
|
push hl
|
|
push bc
|
|
call cin ; wait for a character
|
|
pop bc
|
|
pop hl
|
|
|
|
hxd4:
|
|
djnz hxd1 ; continue if not at end of buffer
|
|
|
|
call crlf
|
|
call crlf
|
|
ret
|
|
|
|
prascii:
|
|
|
|
cp 20H
|
|
jp m,pra1 ; anything less than 20H is non-printable
|
|
cp 7fH ; anything greater than 7E is non-printable
|
|
jp m,pra2
|
|
pra1:
|
|
ld a,'.'
|
|
pra2:
|
|
call cout
|
|
ret
|
|
;
|
|
;
|
|
;
|
|
; -------------------------------------------------------------------------
|
|
;
|
|
; LBA manipulation routines;
|
|
;
|
|
cpylba:
|
|
; copy LBA to LBA
|
|
; source = HL, Destination = DE
|
|
ld bc,04H
|
|
ldir
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
inclba:
|
|
ld a,(hl) ; first byte
|
|
add a,1
|
|
ld (hl),a
|
|
ret nc
|
|
|
|
inc hl ;second byte
|
|
ld a,(hl)
|
|
add a,1
|
|
ld (hl),a
|
|
ret nc
|
|
|
|
inc hl ; third byte
|
|
ld a,(hl)
|
|
add a,1
|
|
ld (hl),a
|
|
ret nc
|
|
|
|
inc hl ; fourth byte (MSB)
|
|
ld a,(hl)
|
|
add a,1
|
|
ld (hl),a
|
|
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
cplba: ; compare LBA
|
|
; addresses by HL and DE
|
|
|
|
ld a,(hl) ; start at LSB
|
|
inc hl
|
|
ex de,hl
|
|
cp (hl)
|
|
ret nz
|
|
inc hl
|
|
ex de,hl
|
|
|
|
ld a,(hl)
|
|
inc hl
|
|
ex de,hl
|
|
cp (hl)
|
|
ret nz
|
|
inc hl
|
|
ex de,hl
|
|
|
|
ld a,(hl)
|
|
inc hl
|
|
ex de,hl
|
|
cp (hl)
|
|
ret nz
|
|
inc hl
|
|
ex de,hl
|
|
|
|
ld a,(hl)
|
|
inc hl
|
|
ex de,hl
|
|
cp (hl)
|
|
ret
|
|
|
|
ret nz
|
|
inc hl
|
|
ex de,hl
|
|
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
prlba:
|
|
|
|
call print
|
|
.db "Current LBA = ",0
|
|
ld a,(lba1+3)
|
|
call prhex
|
|
ld a,(lba1+2)
|
|
call prhex
|
|
ld a,(lba1+1)
|
|
call prhex
|
|
ld a,(lba1)
|
|
call prhex
|
|
call crlf
|
|
call crlf
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
prport:
|
|
call print
|
|
.db "Current PPI base port: 0x",0
|
|
ld a,(ppibase)
|
|
call prhex
|
|
call crlf
|
|
ret
|
|
|
|
|
|
clrlba:
|
|
ld a,0
|
|
ld b,4
|
|
clr32b1:
|
|
ld (hl),a
|
|
inc hl
|
|
djnz clr32b1
|
|
ret
|
|
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
prstatus:
|
|
call print
|
|
.db "status = ",0
|
|
|
|
ld a,ide_status ; read IDE status register
|
|
call ide_read
|
|
ld a,c ; returned value
|
|
call prhex
|
|
|
|
call crlf
|
|
ret
|
|
|
|
|
|
|
|
|
|
print:
|
|
pop hl ; get address of text
|
|
ld a,(hl) ; get next character
|
|
inc hl
|
|
push hl
|
|
cp 0
|
|
ret z ; end of text found
|
|
call cout ; output character
|
|
jp print
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
prhex: ; print hexadecimal digit in A
|
|
push af
|
|
srl a ; move high nibble to low
|
|
srl a
|
|
srl a
|
|
srl a
|
|
call hexnib ; convert to ASCII Hex
|
|
call cout ; send character to output device
|
|
pop af
|
|
call hexnib
|
|
call cout ; send character to output device
|
|
ret
|
|
|
|
|
|
|
|
hexnib:
|
|
and 0fh ; strip high order nibble
|
|
add a,30H ; add ASCII ofset
|
|
cp 3ah ; correction necessary?
|
|
ret m
|
|
add a,7 ; correction for A to F
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
cout:
|
|
ld e,a
|
|
ld c,02h ; Console output byte call
|
|
call 5
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
cin:
|
|
ld c,01h ; BDOS console function
|
|
call 5
|
|
ret
|
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
crlf:
|
|
ld a,CR
|
|
call cout
|
|
ld a,LF
|
|
call cout
|
|
ret
|
|
|
|
;------------------------------------------------------------------
|
|
; Routines that talk with the IDE drive, these should be called by
|
|
; the main program.
|
|
|
|
|
|
; read a sector, specified by the 4 bytes in "lba",
|
|
; Return, acc is zero on success, non-zero for an error
|
|
read_sector:
|
|
call ide_wait_not_busy ;make sure drive is ready
|
|
call wr_lba ;tell it which sector we want
|
|
|
|
ld a, ide_command
|
|
ld c, ide_cmd_read
|
|
call ide_write ; ask the drive to read it
|
|
|
|
call ide_wait_drq ;wait until it's got the data
|
|
|
|
bit 0,a
|
|
jp nz, get_err
|
|
ld hl, buffer
|
|
call read_data ;grab the data
|
|
ld a,0
|
|
ret
|
|
|
|
|
|
; when an error occurs, we get acc.0 set from a call to ide_drq
|
|
; or ide_wait_not_busy (which read the drive's status register). If
|
|
; that error bit is set, we should jump here to read the drive's
|
|
; explanation of the error, to be returned to the user. If for
|
|
; some reason the error code is zero (shouldn't happen), we'll
|
|
; return 255, so that the main program can always depend on a
|
|
; return of zero to indicate success.
|
|
get_err:
|
|
ld a,ide_err
|
|
call ide_read
|
|
ld a,c
|
|
jp z,gerr2
|
|
ret
|
|
gerr2:
|
|
ld a, 255
|
|
ret
|
|
|
|
|
|
;write a sector, specified by the 4 bytes in "lba",
|
|
;whatever is in the buffer gets written to the drive!
|
|
;Return, acc is zero on success, non-zero for an error
|
|
write_sector:
|
|
call ide_wait_not_busy ; make sure drive is ready
|
|
call wr_lba ; tell it which sector we want
|
|
ld a, ide_command
|
|
ld c, ide_cmd_write
|
|
|
|
call ide_write ;tell drive to write a sector
|
|
call ide_wait_drq ;wait unit it wants the data
|
|
bit 0,a ; check for error returned
|
|
jp nz,get_err
|
|
|
|
ld hl, buffer
|
|
call write_data ;give the data to the drive
|
|
call ide_wait_not_busy ;wait until the write is complete
|
|
|
|
bit 0,a
|
|
jp nz,get_err
|
|
|
|
ld a,0
|
|
ret
|
|
|
|
|
|
; do the identify drive command, and return with the buffer
|
|
; filled with info about the drive
|
|
drive_id:
|
|
call ide_wait_not_busy
|
|
ld a,ide_head
|
|
ld c,10100000b
|
|
call ide_write ;select the master device
|
|
call ide_wait_ready
|
|
ld a,ide_command
|
|
ld c,0ech
|
|
call ide_write ;issue the command
|
|
call ide_wait_drq
|
|
ld hl, buffer
|
|
call read_data
|
|
ret
|
|
|
|
|
|
; tell the drive to spin up
|
|
spinup:
|
|
ld c,ide_cmd_spinup
|
|
ld a,ide_command
|
|
call ide_write
|
|
call ide_wait_not_busy
|
|
ret
|
|
|
|
; tell the drive to spin down
|
|
spindown:
|
|
call ide_wait_not_busy
|
|
ld c,ide_cmd_spindown
|
|
ld a,ide_command
|
|
call ide_write
|
|
call ide_wait_not_busy
|
|
ret
|
|
|
|
; initialize the IDE drive
|
|
ide_init:
|
|
ld a, ide_head
|
|
ld b, 0
|
|
ld c, 10100000b ; select the master device
|
|
call ide_write
|
|
init1:
|
|
ld a, ide_status
|
|
call ide_read
|
|
ld a, c
|
|
|
|
; should probably check for a timeout here
|
|
bit 6,a ; wait for RDY bit to be set
|
|
jp z,init1
|
|
bit 7,a
|
|
jp nz,init1 ; wait for BSY bit to be clear
|
|
|
|
ret
|
|
|
|
|
|
|
|
; IDE Status Register:
|
|
; bit 7: Busy 1=busy, 0=not busy
|
|
; bit 6: Ready 1=ready for command, 0=not ready yet
|
|
; bit 5: DF 1=fault occured inside drive
|
|
; bit 4: DSC 1=seek complete
|
|
; bit 3: DRQ 1=data request ready, 0=not ready to xfer yet
|
|
; bit 2: CORR 1=correctable error occured
|
|
; bit 1: IDX vendor specific
|
|
; bit 0: ERR 1=error occured
|
|
|
|
|
|
|
|
|
|
|
|
;------------------------------------------------------------------
|
|
; Not quite as low, low level I/O. These routines talk to the drive,
|
|
; using the low level I/O. Normally a main program should not call
|
|
; directly to these.
|
|
|
|
|
|
;Read a block of 512 bytes (one sector) from the drive
|
|
;and store it in memory @ HL
|
|
read_data:
|
|
ld b, 0
|
|
rdblk2:
|
|
push bc
|
|
push hl
|
|
ld a, ide_data
|
|
call ide_read
|
|
pop hl
|
|
ld a, c
|
|
ld (hl), a
|
|
inc hl
|
|
ld a, b
|
|
ld (hl), a
|
|
inc hl
|
|
pop bc
|
|
djnz rdblk2
|
|
ret
|
|
|
|
|
|
; Write a block of 512 bytes (at HL) to the drive
|
|
write_data:
|
|
ld b,0
|
|
wrblk2:
|
|
push bc
|
|
ld a,(hl)
|
|
ld c, a ; LSB
|
|
inc hl
|
|
ld a,(hl)
|
|
ld b, a ; MSB
|
|
inc hl
|
|
push hl
|
|
|
|
ld a, ide_data
|
|
call ide_write
|
|
pop hl
|
|
pop bc
|
|
djnz wrblk2
|
|
ret
|
|
|
|
|
|
|
|
; write the logical block address to the drive's registers
|
|
wr_lba:
|
|
ld a,(lba1+3) ; MSB
|
|
and 0fh
|
|
or 0e0h
|
|
ld c,a
|
|
ld a,ide_head
|
|
call ide_write
|
|
|
|
ld a,(lba1+2)
|
|
ld c,a
|
|
ld a,ide_cyl_msb
|
|
call ide_write
|
|
|
|
ld a,(lba1+1)
|
|
ld c,a
|
|
ld a,ide_cyl_lsb
|
|
call ide_write
|
|
|
|
ld a,(lba1+0) ; LSB
|
|
ld c,a
|
|
ld a,ide_sector
|
|
call ide_write
|
|
|
|
ld c,1
|
|
ld a,ide_sec_cnt
|
|
call ide_write
|
|
|
|
|
|
ret
|
|
|
|
|
|
ide_wait_not_busy:
|
|
ld a,ide_status ;wait for RDY bit to be set
|
|
call ide_read
|
|
bit 7,c
|
|
jp nz,ide_wait_not_busy
|
|
; should probably check for a timeout here
|
|
|
|
ret
|
|
|
|
|
|
ide_wait_ready:
|
|
ld a,ide_status ; wait for RDY bit to be set
|
|
call ide_read
|
|
bit 6,c ; test for XXX
|
|
jp z,ide_wait_ready
|
|
bit 7,c
|
|
jp nz,ide_wait_ready
|
|
|
|
;should probably check for a timeout here
|
|
ret
|
|
|
|
|
|
|
|
; Wait for the drive to be ready to transfer data.
|
|
; Returns the drive's status in Acc
|
|
ide_wait_drq:
|
|
ld a,ide_status ;wait for DRQ bit to be set
|
|
call ide_read
|
|
bit 7,c
|
|
jp nz,ide_wait_drq ; check for busy
|
|
bit 3,c ; wait for DRQ
|
|
jp z,ide_wait_drq
|
|
|
|
; should probably check for a timeout here
|
|
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------------------
|
|
; Low Level I/O to the drive. These are the routines that talk
|
|
; directly to the drive, via the 8255 chip. Normally a main
|
|
; program would not call to these.
|
|
|
|
; Do a read bus cycle to the drive, using the 8255.
|
|
; input acc = IDE register address
|
|
; output C = lower byte read from IDE drive
|
|
; output B = upper byte read from IDE drive
|
|
|
|
|
|
|
|
|
|
ide_read:
|
|
push af ; save register value
|
|
push bc
|
|
call set_ppi_rd ; setup for a read cycle
|
|
pop bc
|
|
|
|
pop af ; restore register value
|
|
call wrppictl ; write to control sigs
|
|
|
|
or ide_rd_line ; assert RD pin
|
|
call wrppictl ; write to control sigs
|
|
|
|
push af ; save register value
|
|
call rdppilsb ; read LSB register into A
|
|
ld c,a ; save in reg C
|
|
|
|
call rdppimsb ; read MSB register into A
|
|
ld b,a ; save in reg C
|
|
|
|
|
|
pop af ; restore register value
|
|
xor ide_rd_line ; de-assert RD signal
|
|
call wrppictl ; write to control sigs
|
|
|
|
ld a,0
|
|
call wrppictl ; write to control sigs
|
|
ret
|
|
|
|
|
|
|
|
; Do a write bus cycle to the drive, via the 8255
|
|
; input acc = IDE register address
|
|
; input register C = LSB to write
|
|
; input register B = MSB to write
|
|
;
|
|
|
|
ide_write:
|
|
push af ; save IDE register value
|
|
|
|
push bc
|
|
call set_ppi_wr ; setup for a write cycle
|
|
pop bc
|
|
|
|
ld a,c ; get value to be written
|
|
call wrppilsb
|
|
|
|
ld a,b ; get value to be written
|
|
call wrppimsb
|
|
|
|
pop af ; get saved IDE register
|
|
call wrppictl ; write to control sigs
|
|
|
|
or ide_wr_line ; assert write pin
|
|
call wrppictl ; write to control sigs
|
|
|
|
xor ide_wr_line ; de assert WR pin
|
|
call wrppictl ; write to control sigs
|
|
|
|
ld a,0
|
|
call wrppictl ; write to control sigs
|
|
ret
|
|
|
|
|
|
;-------------------------------------------------------------------------------------------
|
|
|
|
ide_hard_reset:
|
|
call set_ppi_rd
|
|
ld a,ide_rst_line
|
|
call wrppictl ; write to control register
|
|
ld bc,0
|
|
rstdly:
|
|
djnz rstdly
|
|
ld a,0
|
|
call wrppictl ; write to control registers
|
|
ret
|
|
|
|
;-----------------------------------------------------------------------------------
|
|
; PPI setup routine to configure the appropriate PPI mode
|
|
;
|
|
;------------------------------------------------------------------------------------
|
|
|
|
set_ppi_rd:
|
|
ld a,(ppibase)
|
|
add a,PIOCONT ; select Control register
|
|
ld c,a
|
|
ld a,rd_ide_8255 ; configure 8255 chip, read mode
|
|
out (c),a
|
|
ret
|
|
|
|
set_ppi_wr:
|
|
ld a,(ppibase)
|
|
add a,PIOCONT ; select Control register
|
|
ld c,a
|
|
ld a,wr_ide_8255 ; configure 8255 chip, write mode
|
|
out (c),a
|
|
ret
|
|
|
|
;------------------------------------------------------------------------------------
|
|
|
|
rdppilsb: ; read LSB
|
|
; returns data in A
|
|
push bc
|
|
ld a,(ppibase)
|
|
add a,IDELSB ; select Control register
|
|
ld c,a
|
|
in a,(c)
|
|
pop bc
|
|
ret
|
|
|
|
|
|
wrppilsb: ; write LSB
|
|
; data to be written in A
|
|
push bc
|
|
push af
|
|
ld a,(ppibase)
|
|
add a,IDELSB ; select Control register
|
|
ld c,a
|
|
pop af
|
|
out (c),a
|
|
pop bc
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
|
|
rdppimsb: ; read MSB
|
|
; returns data in A
|
|
push bc
|
|
ld a,(ppibase)
|
|
add a,IDEMSB ; select MSB Register
|
|
ld c,a
|
|
in a,(c)
|
|
pop bc
|
|
ret
|
|
|
|
|
|
wrppimsb: ; write LSB
|
|
; data to be written in A
|
|
push bc
|
|
push af
|
|
ld a,(ppibase)
|
|
add a,IDEMSB ; select MSB Register
|
|
ld c,a
|
|
pop af
|
|
out (c),a
|
|
pop bc
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
|
|
wrppictl: ; write to control signals
|
|
; data to be written in A
|
|
push bc
|
|
push af
|
|
ld a,(ppibase)
|
|
add a,IDECTL ; select CTL Register
|
|
ld c,a
|
|
pop af
|
|
out (c),a
|
|
pop bc
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Storage area follows
|
|
|
|
savsp: .dw 0 ; saved stack pointer
|
|
lba1: .dw 0,0 ; LBA used for read/write operations
|
|
lba2: .dw 0,0 ; Start LBA for format
|
|
lba3: .dw 0,0 ; End LBA for format
|
|
ppibase: .db DEFBASE ; base address of PPI chip
|
|
|
|
.fill 0C00H - $
|
|
|
|
buffer: .fill 512 ; sector buffer for IDE transfers
|
|
conbuf: .db 8 ; maximum chars
|
|
.db 0 ; count
|
|
.fill 8 ; size of buffer
|
|
|
|
.fill 100
|
|
stack:
|
|
|
|
.end
|
|
|
|
|