📄 ide.asm
字号:
; IDE functions
; Chris Ward, 24/12/1999
_IDE ;remember start address for code
ORG IDE
;IDE registers, with equivalent PC I/O addresses listed
IDEDATA .ds 1 ;1F0: Data Port
IDEERR .ds 1 ;1F1: Error Register (read)
IDEPRE EQU IDEERR ;1F1: Write Precompensation (write)
IDESECC .ds 1 ;1F2: Sector Count
IDESECT .ds 1 ;1F3: Sector Number
IDECYLL .ds 1 ;1F4: Cylinder Low
IDECYLH .ds 1 ;1F5: Cylinder High
IDEDRHD .ds 1 ;1F6: Drive / Head
IDESTS .ds 1 ;1F7: Status (read)
IDECMD EQU IDESTS ;1F7: Command (write)
ORG *+6 ;3F0 - 3F5: unused
IDEASTS .ds 1 ;3F6: Alternate Status (read)
IDECTL EQU IDEASTS ;3F6: Device Control (write)
IDEDADD .ds 1 ;3F7: Drive Address (read)
IDEHIGH .ds 1 ;latch for high 8 bits
ORG _IDE
; *** Initialise IDE bus
; A,X,Y preserved
IDEINIT PHA
JSR IDEDRDY
LDA #$A0
STA IDEDRHD ;select drive 0, head 0
LDA #$10
STA IDECMD ;send recalibrate command
JSR IDEBUSY
PLA
RTS
; *** Convert LBA address to CHS (Cylinder/Head/Sector) values
; input: LBA address at ZP locations IDELBA0,IDELBA1,IDELBA2,IDELBA3
; Sectors per cylinder (HxS) at ZP location IDESPC
; Sectors per track (S) at ZP location IDESPT
; output: CHS values loaded into IDE registers
; Y preserved
;cylinder = LBA / SPC
LBA2CHS LDA IDELBA0
STA ZTMP0
LDA IDELBA1
STA ZTMP1
LDA IDELBA2
STA ZTMP2
LDA IDELBA3
STA ZTMP3
LDA IDESPC
STA ZTMP4
LDA IDESPC+1
STA ZTMP5
LDA #0
STA ZTMP6
STA ZTMP7
JSR UDIV32 ;do the division
LDA ZTMP0 ;cylinder low byte
STA IDECYLL
LDA ZTMP1 ;cylinder high byte
STA IDECYLH
;head = (LBA % SPC) / SPT
;sector = (LBA % SPT) + 1
;will do division by repeated subtraction
LDX #0
LBA2CH0 SEC
LDA ZTMP8
SBC IDESPT ;subtract low byte
BCS LBA2CH1
DEC ZTMP9 ;carry=0 -> borrow from high byte
BCC LBA2CH2 ;high byte rollover -> finished
LBA2CH1 STA ZTMP8
INX
JMP LBA2CH0
;we now have X=head, A=sector-1
LBA2CH2 ADC IDESPT
CLC
ADC #1
STA IDESECT ;write to sector register
TXA
ORA #$A0 ;set LBA=0, DEV=0
STA IDEDRHD ;write to drive/head register
RTS
; *** Test the LBA2CHS routine with data sent from PC on serial port
LBATEST JSR RXBYTE
STA IDESPC
JSR RXBYTE
STA IDESPC+1
JSR RXBYTE
STA IDESPT
JSR RXBYTE
STA IDELBA0
JSR RXBYTE
STA IDELBA1
JSR RXBYTE
STA IDELBA2
JSR RXBYTE
STA IDELBA3
JSR LBA2CHS
LDA IDECYLL
JSR TXBYTE
LDA IDECYLH
JSR TXBYTE
LDA IDEDRHD
AND #$0F
JSR TXBYTE
LDA IDESECT
JSR TXBYTE
JMP LBATEST
; *** Wait until drive is not busy
; A,X,Y preserved
IDEBUSY PHA
IDEBUSY0 LDA IDESTS ;read status register
AND #$80 ;select bit 7 (BUSY)
BNE IDEBUSY0 ;loop if set
PLA
RTS
; *** Wait until drive is ready to accept commands
; A,X,Y preserved
IDEDRDY PHA
IDEDRDY0 LDA IDESTS ;read status register
AND #$40 ;select bit 6 (DRDY)
BEQ IDEDRDY0 ;loop if not set
PLA
RTS
; *** Wait until drive is ready to transfer data
; A,X,Y preserved
IDEDRQ PHA
IDEDRQ0 LDA IDESTS ;read status register
AND #$08 ;select bit 3 (DRQ)
BEQ IDEDRQ0 ;loop if not set
PLA
RTS
; *** Byte-swap the data in the IDE buffer (512 bytes)
; Input: destination address stored at IDEBUFP
; A,X,Y preserved
IDESWAP PHA
TXA
PHA
TYA
PHA
LDA IDEBUFP ;make copy of IDEBUFP
STA IDEBUFP1
LDA IDEBUFP+1
STA IDEBUFP1+1
LDA #$00 ;initialise counter
STA ZTMP0
IDESWAP0 LDY #$00
LDA (IDEBUFP1),Y ;get byte 1
TAX ;store byte 1 in X
LDY #$01
LDA (IDEBUFP1),Y ;get byte 2
LDY #$00
STA (IDEBUFP1),Y ;write byte 2 over byte 1
LDY #$01
TXA ;retrieve byte 1 from X
STA (IDEBUFP1),Y ;write byte 1 over byte 2
INC ZTMP0 ;increment count
BEQ IDESWAP1 ;quit when swapped all bytes
LDA IDEBUFP1
CLC
ADC #$02 ;advance pointer by two bytes
STA IDEBUFP1
BCC IDESWAP0 ;check for high-byte rollover
INC IDEBUFP1+1
JMP IDESWAP0
IDESWAP1 PLA
TAY
PLA
TAX
PLA
RTS
; *** Read block of data from device (512 bytes / 256 words)
; Input: destination address stored at IDEBUFP
; A,X,Y preserved
IDERBLK PHA
TXA
PHA
TYA
PHA
LDA IDEBUFP ;make copy of IDEBUFP
STA IDEBUFP1
LDA IDEBUFP+1
STA IDEBUFP1+1
LDX #$00
LDY #$00
IDERBLK0 LDA IDEDATA ;read low byte
STA (IDEBUFP1),Y ;store low byte
INC IDEBUFP1 ;advance pointer
BNE IDERBLK1
INC IDEBUFP1+1
IDERBLK1 LDA IDEHIGH ;read high byte from latch
STA (IDEBUFP1),Y ;store high byte
INC IDEBUFP1 ;advance pointer
BNE IDERBLK2
INC IDEBUFP1+1
IDERBLK2 INX ;increment counter
BNE IDERBLK0 ;loop
PLA
TAY
PLA
TAX
PLA
RTS
; *** Write block of data to device (512 bytes / 256 words)
; Input: source address stored at IDEBUFP
; A,X,Y preserved
IDEWBLK JSR IDESWAP ;swap bytes (interface needs hi-byte 1st)
PHA
TXA
PHA
TYA
PHA
LDA IDEBUFP ;make copy of IDEBUFP
STA IDEBUFP1
LDA IDEBUFP+1
STA IDEBUFP1+1
LDX #$00
LDY #$00
IDEWBLK0 LDA (IDEBUFP1),Y ;read high byte
STA IDEHIGH ;place high byte in latch
INC IDEBUFP1 ;advance pointer
BNE IDEWBLK1
INC IDEBUFP1+1
IDEWBLK1 LDA (IDEBUFP1),Y ;read low byte
STA IDEDATA ;write word to device
INC IDEBUFP1 ;advance pointer
BNE IDEWBLK2
INC IDEBUFP1+1
IDEWBLK2 INX ;increment counter
BNE IDEWBLK0 ;loop
PLA
TAY
PLA
TAX
PLA
; *** Read sector
; Input: LBA at IDELBA0-IDELBA3
; destination address at IDEBUFP
IDERSEC PHA
JSR IDEBUSY ;make sure drive is ready
LDA IDELBAF ;does drive support LBA?
BEQ IDERSEC0 ;no - jump
LDA IDELBA0 ;yes - load LBA values into registers
STA IDESECT
LDA IDELBA1
STA IDECYLL
LDA IDELBA2
STA IDECYLH
LDA IDELBA3
ORA #$E0
STA IDEDRHD
JMP IDERSEC1
IDERSEC0 JSR LBA2CHS ;drive doesn't support LBA - convert to CHS
IDERSEC1 LDA #1
STA IDESECC ;set sector count: 1 sector
LDA #$20
STA IDECMD ;set command: $20 = read sector(s)
JSR IDEBUSY ;wait for command to finish
JSR IDERBLK ;read 512 bytes into memory
PLA
RTS
; *** Write sector
; Input: LBA at IDELBA0-IDELBA3
; source address at IDEBUFP
IDEWSEC PHA
JSR IDEBUSY ;make sure drive is ready
LDA IDELBAF ;does drive support LBA?
BEQ IDEWSEC0 ;no - jump
LDA IDELBA0 ;yes - load LBA values into registers
STA IDESECT
LDA IDELBA1
STA IDECYLL
LDA IDELBA2
STA IDECYLH
LDA IDELBA3
ORA #$E0
STA IDEDRHD
JMP IDEWSEC1
IDEWSEC0 JSR LBA2CHS ;drive doesn't support LBA - convert to CHS
IDEWSEC1 LDA #1
STA IDESECC ;set sector count: 1 sector
LDA #$30
STA IDECMD ;set command: $30 = write sector(s)
JSR IDEDRQ ;wait for drive to request data
JSR IDEWBLK ;send 512 bytes
PLA
RTS
; *** Get drive identification
; A,X,Y preserved
IDEID PHA
TXA
PHA
TYA
PHA
JSR IDEBUSY
LDA #$EC
STA IDECMD ;send command $EC (identify drive)
JSR IDEBUSY
LDA #IDEBUF0 ;set low byte of buffer address
STA IDEBUFP
LDA #IDEBUF0/256 ;set high byte of buffer address
STA IDEBUFP+1
JSR IDERBLK ;read data into buffer
JSR IDESWAP ;swap byte order (for some reason, ASCII strings
; are sent with opposite-to-normal byte order)
LDA #54 ;word 27 = model number (40 chars)
STA MSGBASE
LDA #IDEBUF0/256
STA MSGBASE+1
JSR LCDCLR
LDX #40
JSR LCDSTRN ;print model number
JSR IDESWAP ;swap byte order again
JSR WAITKEY ;wait for keypress
JSR LCDCLR
LDA #'C'
JSR LCDCHAR
LDA #':'
JSR LCDCHAR
LDA IDEBUF0+3 ;word 1 = cylinders
JSR LCDHEX
LDA IDEBUF0+2
JSR LCDHEX
LDA #' '
JSR LCDCHAR
LDA #'H'
JSR LCDCHAR
LDA #':'
JSR LCDCHAR
LDA IDEBUF0+6 ;word 3 = heads
STA ZTMP0 ;store for SPC calculation
JSR LCDHEX
LDA #' '
JSR LCDCHAR
LDA #'S'
JSR LCDCHAR
LDA #':'
JSR LCDCHAR
LDA IDEBUF0+12 ;word 6 = sectors
STA ZTMP2 ;store for SPC calculation
STA IDESPT ;also store as SPT
JSR LCDHEX
LDA #' '
JSR LCDCHAR
JSR LCDCHAR
LDA #0 ;setup high bytes
STA ZTMP1
STA ZTMP3
JSR MUL16 ;calculate SPC = H * S
LDA ZTMP1
STA IDESPC+1
JSR LCDHEX
LDA ZTMP0
STA IDESPC
JSR LCDHEX
LDA #0
STA ZTMP2
STA ZTMP3
STA ZTMP6
STA ZTMP7
LDA IDEBUF0+2
STA ZTMP4
LDA IDEBUF0+3
STA ZTMP5
JSR MUL32 ;calculate total number of sectors = SPC * C
LDA #' '
JSR LCDCHAR
JSR LCDCHAR
LDA ZTMP3
STA IDESIZE+3
JSR LCDHEX
LDA ZTMP2
STA IDESIZE+2
JSR LCDHEX
LDA ZTMP1
STA IDESIZE+1
JSR LCDHEX
LDA ZTMP0
STA IDESIZE
JSR LCDHEX
;does drive support LBA? (word 49 bit 9)
LDY #0
LDA #TEXTCHS
STA MSGBASE
LDA #TEXTCHS/256
STA MSGBASE+1
LDA IDEBUF0+99
AND #$02
BEQ IDEID1
LDY #1
LDA #TEXTLBA
STA MSGBASE
LDA #TEXTLBA/256
STA MSGBASE+1
IDEID1 STY IDELBAF
LDA #' '
JSR LCDCHAR
JSR LCDCHAR
JSR LCDSTR
JSR WAITKEY ;wait for keypress
PLA
TAY
PLA
TAX
PLA
RTS
TEXTCHS fcs "CHS"
.byte $00
TEXTLBA fcs "LBA"
.byte $00
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -