📄 ide.s
字号:
;*
;* C-callable version of SeekSector
;*
;* Prototype is:
;*
;* u08 SeekSectorC (u32 startsector, u08 numsectors);
;*
;* The C compiler passes in numsectors in r20, and startsector in r25:r22
;* It expects a result in r24. Call it a bunch of times and it'll return SeekSect_Done
;* when it's finished.
;*
;**********************************************************************************
SeekSectorC:
push temp
push param1
push param2
push ZL
push ZH ; preserve registers used by SeekSector
mov param1,r20 ; numsectors in param1
push r25
push r24
push r23
push r22 ; starting sector on the stack
rcall SeekSector
pop r22
pop r23
pop r24
pop r25 ; restore the stack
mov r24,param1 ; move result into r24 for the C compiler
pop ZH
pop ZL
pop param2
pop param1
pop temp
ret
;**********************************************************************************
;*
;* SeekSector
;*
;* This routine seeks to the specified sector. The sector number is a 32-bit
;* number passed in via the STACK. The top of the stack contains the low byte;
;* the 4th byte read off the stack is the high byte. Param1 contains the
;* number of sectors required.
;*
;* Because we have to wait for the drive to seek, this routine is a state machine.
;* The first state initiates the seek. The second state returns "busy" to the calling
;* function until the seek is complete. The calling function must call SeekSect
;* repeatedly until it receives a "done". At that time the drive is ready for
;* data reads.
;*
;* It is possible for the first Read Sectors command after drive power-up to fail.
;* This is most likely due to the drive not being properly up to speed yet. Should
;* the drive report an error at the completion of the command, simply tell it to
;* do it again.
;*
;* This routine functions as follows:
;*
;* InitSeek state (SEEKSECT_ST == SeekSect_ISS)
;* If (drive busy OR drive not ready) then return with param1=SeekSect_Busy
;* Write param1 to the SECTOR COUNT register (tell drive how many sectors to get)
;* Write the sector number (4 bytes) into the command block registers and enable LBA
;* Issue the READ SECTORS WITH RETRY ($20) command
;* Change state to WaitSeek
;* Return with param1=SeekSect_Busy
;*
;* WaitSeek state (SEEKSECT_ST == SeekSect_WSS)
;* If drive busy or DriveSeek not complete yet then return with param1=SeekSect_Busy
;* If drive reports no error then
;* Change state to InitSeek
;* Write SeekSect_Done into param1
;* Return
;* Else (drive had an error)
;* Do a dummy read of the drive error register
;* Change state to InitSeek
;* Return with param1=SeekSect_Busy (to redo the whole seek)
;*
;*
;* When reading data off the drive, the first byte on the drive is the low byte read
;* by the AVR; the next byte is the high byte.
;*
;*
;* Accepts: sector number on stack, number of sectors in param1
;* Returns: Busy or Done in param1
;* Uses: temp, param1, param2, ZL, ZH, flags.
;*
;**********************************************************************************
SeekSector:
; Jump to the correct state (this is the state dispatcher code)
ldi ZL,lo8(SEEKSECT_ST)
ldi ZH,hi8(SEEKSECT_ST) ; point Z to SeekSector state variable
ld temp,Z ; get the state
cpi temp,SeekSect_WSS
breq SeekSector_WSS ; branch if state is WaitSeek
;* InitSeek state
;* Execute here for the idle state, the InitSeek state.
; First we check for drive busy or ready, and return if required.
push param1 ; preserve the number of sectors
ldi param1,IDERD_ST
rcall IDE_read8 ; read drive status register
sbrc param1,IDEST_BSY_BIT ; skip forward if BSY bit clear
rjmp SeekSector_ISS_1 ; jump if drive is busy
sbrc param1,IDEST_RDY_BIT ; skip forward if RDY bit clear
rjmp SeekSector_ISS_2 ; jump if drive is ready
; execute here if drive busy OR drive not ready
SeekSector_ISS_1:
; ldi param1,IDERD_ST
; call IDE_read8 ; read drive register
; call UART_PutHexWait ; debug only
; ldi param1,13
; call UART_TxCharWait ; debug only
; ldi param1,10
; call UART_TxCharWait ; debug only
pop param1 ; clean the stack
ldi param1,SeekSect_Busy
ret ; return with "SeekSector routine busy"
; execute here if drive not busy AND drive is ready
SeekSector_ISS_2:
pop param2 ; restore number of sectors now in param2
; write number of sectors into sector count register
SeekSector_ISS_3:
ldi param1,IDEWR_SC ; sector count register address in param1
rcall IDE_write8 ; write number to SECTOR COUNT register
; write the sector number to the LBA registers and enable LBA
in ZL,SPL
in ZH,SPH
adiw ZL,3 ; Z points to low byte parameter on stack
ldi param1,IDEWR_LBA0
ld param2,Z+
rcall IDE_write8 ; write low byte to LBA byte 0
ldi param1,IDEWR_LBA1
ld param2,Z+
rcall IDE_write8 ; write byte 1 to LBA byte 1
ldi param1,IDEWR_LBA2
ld param2,Z+
rcall IDE_write8 ; write byte 2 to LBA byte 2
ldi param1,IDEWR_LBA3
ld param2,Z+ ; get high byte (sector number) off stack
ori param2,0b11100000 ; enable LBA in LBA byte 3 register
rcall IDE_write8 ; and give it to the drive
; issue command READ SECTORS WITH RETRY ($20) to the drive
ldi param1,IDEWR_CMD ; param1: command register address
ldi param2,0x20 ; param2: $20
rcall IDE_write8 ; write command to drive
; change state to WaitSeek and return with param1 saying "busy"
ldi ZL,lo8(SEEKSECT_ST)
ldi ZH,hi8(SEEKSECT_ST) ; point Z to SeekSector state variable
ldi temp,SeekSect_WSS
st Z,temp ; next state is WaitSeek
ldi param1,SeekSect_Busy
ret ; return with Busy param1 code
;* WaitSeek state
;* Execute here if the required state is WaitSeek
SeekSector_WSS:
mov param2,param1 ; keep a copy of number of sectors in param2
; if drive busy or DriveSeek not complete yet then return with Busy code in param1
ldi param1,IDERD_ST
rcall IDE_read8 ; read drive status register
sbrc param1,IDEST_BSY_BIT ; skip forward if BSY bit clear
rjmp SeekSector_WSS_3 ; jump if busy bit set - drive is busy
sbrc param1,IDEST_ERR_BIT ; skip forward if error bit clear
rjmp SeekSector_WSS_2 ; jump if error bit set
sbrs param1,IDEST_DSC_BIT ; skip forward if DriveSeekComplete bit set
rjmp SeekSector_WSS_3 ; jump if DriveSeekComplete bit clear (drive not seeked yet)
sbrs param1,IDEST_RDY_BIT ; skip forward if Ready bit set
rjmp SeekSector_WSS_3 ; jump if Ready bit clear (drive not ready yet)
sbrc param1,IDEST_COR_BIT ; skip forward if Corrected Data bit clear
rjmp SeekSector_WSS_2 ; jump if Corrected Data bit set
rjmp SeekSector_WSS_1 ; if here, everything good, we're green to go...
; execute here if drive busy (BSY bit set) or DriveSeekComplete bit clear
SeekSector_WSS_3:
ldi param1,SeekSect_Busy
ret ; return with Busy param1 code
; execute here if drive not busy (BSY bit clear) AND drive reports no errors (ERR bit clear)
; change state to InitSeek and tell calling function we're done; drive is seeked
SeekSector_WSS_1:
ldi ZL,lo8(SEEKSECT_ST)
ldi ZH,hi8(SEEKSECT_ST) ; point Z to SeekSector state variable
ldi temp,SeekSect_ISS
st Z,temp ; next state is InitSeek
ldi param1,SeekSect_Done
ret ; return with Done; drive is seeked
; execute here if drive reported an error (error bit in status register set)
; do a dummy read from the drive error register, change the state to InitSeek,
; and return saying busy. This way we'll redo the entire seek again.
SeekSector_WSS_2:
; debug - print out the parameters which resulted in the error
call UART_PutHexWait ; print drive status register value
ldi param1,' '
call UART_TxCharWait
mov param1,param2
call UART_PutHexWait ; print number of sectors
ldi param1,' '
call UART_TxCharWait
in ZL,SPL
in ZH,SPH
adiw ZL,7 ; Z points to high byte parameter on stack
ld param1,-Z
call UART_PutHexWait ; print high sector number byte
ld param1,-Z
call UART_PutHexWait
ld param1,-Z
call UART_PutHexWait
ld param1,-Z
call UART_PutHexWait ; print low byte of sector number
ldi param1,' '
call UART_TxCharWait
ldi param1,'S'
call UART_TxCharWait
ldi param1,'e'
call UART_TxCharWait
ldi param1,'e'
call UART_TxCharWait
ldi param1,'k'
call UART_TxCharWait
ldi param1,'S'
call UART_TxCharWait
ldi param1,'e'
call UART_TxCharWait
ldi param1,'c'
call UART_TxCharWait
ldi param1,'t'
call UART_TxCharWait
ldi param1,'o'
call UART_TxCharWait
ldi param1,'r'
call UART_TxCharWait
ldi param1,13
call UART_TxCharWait ; CR
ldi param1,10
call UART_TxCharWait ; LF
; end of debug code
ldi param1,IDERD_ERR
rcall IDE_read8 ; dummy read of drive error register
ldi temp,SeekSect_ISS
sts SEEKSECT_ST,temp ; next state is InitSeek
ldi param1,SeekSect_Busy
ret ; return with busy
;**********************************************************************************
;*
;* PrintDriveData
;*
;* This routine prints out 16 lines of 16 bytes of hex data
;* from the drive data buffer.
;*
;* This routine is just used for debug purposes.
;*
;* R14 is the outer loop counter (line counter); R15 is the inner loop (byte) counter.
;*
;* Accepts: Nothing
;* Returns: Nothing
;* Uses: param1, param2, temp, r14, r15, r20, r21, r23, r24, r26 (X low), flags
;* XL, XH, ZL, ZH
;*
;**********************************************************************************
PrintDriveData:
; wait for drive to clear busy flag
ldi param1,IDERD_ST
rcall IDE_read8 ; read status register
sbrc param1,IDEST_BSY_BIT ; skip forward if BSY bit clear
rjmp PrintDriveData
ldi param1,13
rcall UART_TxCharWait ; print a CR
ldi param1,10
rcall UART_TxCharWait ; print a LF
ldi param1,10
rcall UART_TxCharWait ; print a LF
ldi temp,20
mov r14,temp ; 16 iterations of the outer (line) loop
PrintDriveData100_o:
ldi temp,8
mov r15,temp ; 8 iterations of the inner (byte) loop
PrintDriveData100_i: ; with 2 bytes output per iteration
ldi param1,IDERD_DATA ; read 16-bit data word from the drive
rcall IDE_read16 ; low byte returned in param1; high in param2
rcall UART_PutHexWait ; put low byte out the uart
ldi param1,' '
rcall UART_TxCharWait ; print a space
mov param1,param2
rcall UART_PutHexWait ; put high byte out the uart
ldi param1,' '
rcall UART_TxCharWait ; print a space
dec r15
brne PrintDriveData100_i ; repeat inner loop until r15==0
ldi param1,13
rcall UART_TxCharWait ; print a CR
ldi param1,10
rcall UART_TxCharWait ; print a LF
dec r14
brne PrintDriveData100_o ; repeat outer loop until r14==0
ret
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -