diff --git a/Source/Apps/Build.cmd b/Source/Apps/Build.cmd index d9c389ff..b84b5d21 100644 --- a/Source/Apps/Build.cmd +++ b/Source/Apps/Build.cmd @@ -22,6 +22,7 @@ call :asm Timer || goto :eof call :asm180 IntTest || goto :eof call :asm rtcds7 || goto :eof call :asm rtchb || goto :eof +call :asm ppidetst || goto :eof zx Z80ASM -SYSGEN/F diff --git a/Source/Apps/ppidetst.asm b/Source/Apps/ppidetst.asm index 7d534562..85fcd3f5 100644 --- a/Source/Apps/ppidetst.asm +++ b/Source/Apps/ppidetst.asm @@ -1,1308 +1,1371 @@ -; 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 ************************************ - - -; -; 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.6a",CR,LF,0 - - 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 crlf - call print - db "Please enter new PPI base port: ",0 -; get a hex number - ld (ppibase),a ; save it - call crlf - jp menu - -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 - - - - -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 - - - -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: ; proint 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: ds 2 ; saved stack pointer -lba1: ds 4 ; LBA used for read/write operations -lba2: ds 4 ; Start LBA for format -lba3: ds 4 ; End LBA for format -ppibase: db 60h ; base address of PPI chip to be used, default to Z80 SBC / Z80 MBC -;ppibase db 44h ; base address of PPI chip to be used - - -org 0b00h -buffer: ds 512 ; sector buffer for IDE transfers -conbuf: - db 8 ; maximum chars - db 0 ; count - ds 8 ; size of buffer - - ds 100 -stack: - - - end - +; 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 +