📄 ide.asm
字号:
.title Low Level IDE I/O Routines
.sbttl Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
;++
; IDE.ASM
;
; Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
;
; This file is part of the Spare Time Gizmos' MP3 Player firmware.
;
; Thisfirmware is free software; you can redistribute it and/or modify it under
; the terms of the GNU General Public License as published by the Free Software
; Foundation; either version 2 of the License, or (at your option) any later
; version.
;
; This program is distributed in the hope that it will be useful, but WITHOUT
; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
; more details.
;
; You should have received a copy of the GNU General Public License along with
; this program; if not, write to the Free Software Foundation, Inc., 59 Temple
; Place, Suite 330, Boston, MA 02111-1307 USA
;
; DESCRIPTION:
; This module contains several low level routines for talking to the Compact
; Flash card in TrueIDE interface mode. There are functions to read and write
; individual IDE registers, and another one to transfer the 512 byte sector
; buffer. There's no reason why they couldn't have been written in C (in fact,
; the original prototype versions were) but the C versions are too slow to play
; high bit rate MP3s...
;
; REVISION HISTORY:
; dd-mmm-yy who description
; 21-May-05 RLA New File
; 07-Jul-05 DJA ported to SDCC/ASX8051
; 20-Jul-05 DJA fixed error with 2nd parameter on readIdeBuffer
;--
.module IDE
.optsdcc -mmcs51 --model-small
.radix d
.include "player.inc"
.globl _WriteIDE, _ReadIDE, _ReadIDEBuffer
; DN: Unlike Keil, SDCC passes all but the first parameter via global
; variables. Only the first parameter is passed in a register. Public
; (global) declarations below are for said parameters.
.globl _WriteIDE_PARM_2, _ReadIDEBuffer_PARM_2
.area OSEG(OVR,DATA)
_WriteIDE_PARM_2::
.ds 1
_ReadIDEBuffer_PARM_2::
.ds 2
; This retarded asx8051 assembler needs us to jump thru a few hoops just
; to get the standard 8051 registers defined. Note that the .area and .ds
; declarations are probably superfluous, but they're reproduced here in the
; interest of correctness. [Just between you and me, I've got to wonder
; about declaring the register bank segment as "Overlayable" and especially
; "RELocatable", but that's the way the SDCC compiler does it. I assume
; they know what they're doing!]
.area REG_BANK_0 (REL,OVR,DATA)
.ds 8
AR0=0x00
AR1=0x01
AR2=0x02
AR3=0x03
AR4=0x04
AR5=0x05
AR6=0x06
AR7=0x07
.area CSEG (CODE)
;
;++
; SelectRegister
;
; This local routine will set up the IDE RS0, RS1, RS2, CS1FX and CS3FX bits
; based on the value currently in A. It's used by both the IDE Read and Write
; routines to set up the register select before any I/O takes place...
;--
SelectRegister:
; Before we change any of the register select bit, and especially before we
; assert either CS1FX or CS3FX, be sure that both IOWR and IORD are inactive.
; Remember that these signals are shared with other peripherals (e.g. the LCD)
; and we can't depend on their current state!
SETB CARD_IORD ; (remember that they're active HIGH!)
SETB CARD_IOWR ; ...
MOV C, ACC.0 ; set up RS0
MOV CARD_RS0, C ; ...
MOV C, ACC.1 ; RS1 ...
MOV CARD_RS1, C ; ...
MOV C, ACC.2 ; and RS2...
MOV CARD_RS2, C ; ...
MOV C, ACC.3 ; get the primary/secondary register set bit
MOV CARD_CS1FX, C ; if ACC.3 == 0, select CS1
CPL C ; ...
MOV CARD_CS3FX, C ; and if ACC.3 == 1, select CS3
RET ; and all done
;
;++
; PRIVATE void WriteIDE (BYTE bReg, BYTE bValue)
;
; This routine will write a byte to an IDE register. The IDE interface
; defines 8 primary registers (addressed 0..7) selected by CS1FX and eight
; alternate registers (although most of these are not implemented) selected
; by CS3FX. In our case, if bReg is 0..7 a primary register is selected and
; if bReg is 8..15 a secondary register is selected instead.
;
; Note that the CS1FX and CS3FX names actually come from IDE's PC legacy -
; CS1FX refers to the registers that were originally addressed in the PC's
; I/O space as 0x1F0..0x1F7. On a traditional PC the secondary registers
; were addressed by I/O ports 0x3F0..0x3F7 (hence, CS3FX).
;--
_WriteIDE:
;---- Variable 'bReg' assigned to Register 'DPL' ----
;---- Variable 'bValue' assigned to location '_WriteIde_PARM_2'
MOV A, DPL ; get the bReg parameter
LCALL SelectRegister ; and select the right register
MOV CARD_DATA, _WriteIDE_PARM_2; put the data on the bus
CLR CARD_IOWR ; strobe IOWR low
NOP ; wait a moment or two
SETB CARD_IOWR ; and back to high
; Before leaving, be sure that CS1FX and CS3FX are both de-asserted and that
; the data bus is floating, just in case some other device wants to use it.
SETB CARD_CS1FX ; clear both selects
SETB CARD_CS3FX ; ...
MOV CARD_DATA, #0x0FF ; and float the data bus
RET ; all done!
;
;++
; PRIVATE BYTE ReadIDE (BYTE bReg)
;
; This routine is pretty much the same as WriteIDE except that it (what
; else??) reads an IDE register. The byte read is returned as the function
; value...
;--
_ReadIDE:
;---- Variable 'bReg' assigned to Register 'DPL' ----
MOV A, DPL ; get the bReg parameter
LCALL SelectRegister ; and select the right register
MOV CARD_DATA, #0x0FF ; make sure the port pins are floating
CLR CARD_IORD ; and then strobe IORD low
MOV DPL, CARD_DATA ; capture the card data
SETB CARD_IORD ; and release IORD
SETB CARD_CS1FX ; make sure both selection bits are
SETB CARD_CS3FX ; ... cleared before returning
RET ; and we're done
;
;++
; PRIVATE WORD ReadIDEBuffer (PXBYTE pxBuffer, WORD cbMaxBuf)
;
; This routine will read data from the IDE drive's data register and store it
; in the XDATA buffer. It stops whenever either the drive's DRQ bit (in the status
; register is no longer set (indicating that the drive has no more data to transfer)
; or whenver the drive's ERR bit _is_ set (indicating that something bad happened!).
; Since this routine never waits for DRQ and returns as soon as DRQ is clear, it's
; up to the caller to wait for DRQ to initially set after issuing a command to the
; drive. If the caller issues a command and then calls this function without
; first waiting for the command to complete, it's pretty sure that zero bytes will
; be transferred!
;
; Normally a transfer is always exactly one sector (512 bytes), however the
; cbMaxBuf parameter can be used to specify the buffer size. If the drive sends
; fewer bytes than the buffer size, then the remainder of the buffer will be
; untouched. If the drive transfers more bytes than will fit in the buffer, then
; the extra bytes are discarded. This routine always transfers exactly as many
; bytes as the drive wants and stops when the drive runs out of data, regardless
; of the actual buffer size. The return value for this function is the number of
; bytes actually read from the drive, so a buffer over/underrun can be detected
; by the return value != cbMaxBuf.
;
; This routine returns the number of bytes actually read from the drive even
; if the ERR bit sets, however in that case the drive may have still more data
; to be transferred (and the DRQ bit may still be set). Normally the caller will
; reset the drive in case of an error, and so any left over data won't matter.
;
;--
;--
_ReadIDEBuffer:
;---- Parameter 'pxBuffer' assigned to Register 'DPTR'
;---- Parameter 'cbMaxBuf' assigned to location '_ReadIDEBuffer_PARM_2' ----
;---- Variable 'wCount' assigned to Register 'R6/R7' ----
MOV R6, #0 ; First, clear the word count
MOV R7, #0 ; ...
MOV R5, _ReadIDEBuffer_PARM_2 ; cbMaxBuf low byte
MOV R4, _ReadIDEBuffer_PARM_2+1 ; ... high byte
; Ensure that IORD is clear until we need it, as well as IOWR (which we never
; need!). Clear CS3FX and set CS1FX since we'll always be using the primary
; IDE register set...
SETB CARD_IORD ; all these signals are active high!
SETB CARD_IOWR ; ...
SETB CARD_CS3FX ; ...
CLR CARD_CS1FX ; ...
MOV CARD_DATA, #0xFF ; allow the card to drive the data bus
; Read the status register and ensure that DRQ is set and ERR is clear. If
; either of those conditions is false, then return now with the current word
; count. Remember that the IDE status register is register 7, where as the
; IDE data register is register zero!
;
; Note that this code takes a few liberties (especially the use of P2
; instead of the pre-defined constants for RS0/1/2) in the interest of speed!
; Sorry about that...
RDBUF1: ORL P2, #7 ; set RS0=RS1=RS2=1 (register 7)
CLR CARD_IORD ; and then strobe IORD low
MOV A, CARD_DATA ; read the card status
SETB CARD_IORD ; and release IORD
JB ACC.0, RDBUF9 ; return if ERR is set
JNB ACC.3, RDBUF9 ; .... or if DRQ is clear
; Read two bytes from the data register (temporarily) into R0 and R1...
ANL P2, #0xF8 ; set RS0=RS1=RS2=0 (register 0)
CLR CARD_IORD ; strobe data low
MOV R0, CARD_DATA ; read the high byte
SETB CARD_IORD ; release the strobe
NOP ; just for symmetry
CLR CARD_IORD ; and then do it again
MOV R1, CARD_DATA ; ... with the low byte
SETB CARD_IORD ; ...
; If the remaining buffer size has reached zero, then just loop back to the
; status read and discard these two bytes entirely. Otherwise, decrement the
; buffer size by two.
MOV A, R4 ; get cbMaxBuf
ORL A, AR5 ; are both bytes zero?
JZ RDBUF1 ; if yes, just skip everything else
MOV A, #0xFE ; add -2 (0FFFEH) to cbMaxBuf (R4/R5)
ADD A, R5 ; first the low byte
MOV R5, A ; ...
MOV A, #0xFF ; and then the high byte
ADDC A, R4 ; ...
MOV R4, A ; ...
; Store the two bytes in memory and increment the actual count of the bytes
; read (kept in R6/R7)...
MOV A, R0 ; get the first byte
MOVX @DPTR, A ; and store that in XRAM
INC DPTR ; ...
MOV A, R1 ; then the next byte, too
MOVX @DPTR, A ; ...
INC DPTR ; (for the next time around)
MOV A, #2 ; add 2 to wCount (R6/R7)
ADD A, R7 ; first the low byte
MOV R7, A ; ...
MOV A, #0 ; and then the high byte
ADDC A, R6 ; ...
MOV R6, A ; ...
SJMP RDBUF1 ; then go check for more data
; Land Here when we're done. Make sure that CS1FX is cleared (since we share the
; IOWR and IORD pins with other peripherals!) and return the actual count of
; the bytes read.
RDBUF9: SETB CARD_CS1FX ; make sure nothing is selected
MOV DPL, R7 ; first the count's low byte
MOV DPH, R6 ; and then the high byte
RET ; and that's all we need
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -