;SAM IDE BIOS ;(C) 1996 Stefan Drissen aka Solar Flare of ENTROPY ;this code can be included in programs which want to access the ;SAM IDE harddrive interface. DEFM "+------------------------------+" DEFM "! !" DEFM "! SAM IDE BIOS version 1.00 !" DEFM "! (C) 1996 Stefan Drissen !" DEFM "! !" DEFM "+------------------------------+" ;ROUTINES: ; init ; size ; select ; read ; write ; diagnostic ;--------------------------------------------------------------- ;VARIABLES used to SELECT the sector sect: DEFB 0 trlo: DEFB 0 trhi: DEFB 0 head: DEFB 0 ;--------------------------------------------------------------- ;DEFINE the SIZE of the drive - set by size routine maxsect: DEFB 17 ;(1-255) maxtrlo: DEFB 1027\256 maxtrhi: DEFB 1027/256 ;(0-65535) maxhead: DEFB 5 ;(0-15) ;--------------------------------------------------------------- ;IDE interface ports reg.port: EQU 191 ;select harddrive register dat.port: EQU 189 ;send data to register ;Data is always 16 bits! When sending a command or setting a ;register, always also send a dummy byte (0). ;When reading a port, only the first byte read has a meaning, ;the second byte is always 255 (except when reading data!). ;--------------------------------------------------------------- ;AT Attachment VARIABLES ;if bits 3 & 4 are set then the register is invalid! dummy: EQU %11100000 ;does this matter? device.ctrl: EQU dummy+%01110 ;out data: EQU dummy+%10000 ;in/out error.reg: EQU dummy+%10001 ;in sector.count: EQU dummy+%10010 ;in/out sector.number: EQU dummy+%10011 ;in/out (1-255/max) cylinder.low: EQU dummy+%10100 ;in/out (0-255) cylinder.high: EQU dummy+%10101 ;in/out (0-255/max) drive.head: EQU dummy+%10110 ;in/out (0-15/max) status: EQU dummy+%10111 ;in command: EQU dummy+%10111 ;out ;COMMANDS (O=optional, M=mandatory) drv.diagnose: EQU &90 ;execute drive diagnostic M identify: EQU &EC ;identify drive O read.sector: EQU &21 ;read sector(s) without retry M write.sector: EQU &31 ;write sector(s) without retry M ;--------------------------------------------------------------- ;=============================================================== ;initialise harddrive init: ; LD A,223 ; OUT (191),A ; IN A,(189) LD A,device.ctrl OUT (reg.port),A LD A,%1110 ;reset drive + irq ;x compulsory 1 ; x host software reset ; x interrupt via high impedance ; x compulsory 0 OUT (dat.port),A LD A,0 OUT (dat.port),A LD B,40 ;is this pause necessary? DJNZ $ LD A,%1010 ;unreset drive + irq OUT (dat.port),A LD A,0 OUT (dat.port),A ; LD A,255 ; OUT (191),A ; IN A,(189) ; IN A,(189) LD A,status ;wait for drive not busy OUT (reg.port),A waitbusy: IN A,(dat.port) LD C,A IN A,(dat.port) ;dummy BIT 7,C ;bit 7 = 1 -> busy JR NZ,waitbusy BIT 6,C ;bit 6 = 0 -> drive not ready JR Z,waitbusy RET ;=============================================================== ;set up the head, track, sector registers of the drive for the ;next read/write select: LD A,sector.number OUT (reg.port),A LD A,(sect) INC A OUT (dat.port),A XOR A OUT (dat.port),A LD A,cylinder.low OUT (reg.port),A LD A,(trlo) OUT (dat.port),A XOR A OUT (dat.port),A LD A,cylinder.high OUT (reg.port),A LD A,(trhi) OUT (dat.port),A XOR A OUT (dat.port),A LD A,drive.head OUT (reg.port),A LD A,(head) AND %00001111 ADD %10100000 ;drive 0, CHS (-> not LBA) ;x compulsory 1 ; x address type: 0=CHS, 1=LBA ; x compulsory 1 ; x drive (0 or 1) ; xxxx head number (0-15) OUT (dat.port),A XOR A OUT (dat.port),A LD A,sector.count OUT (reg.port),A LD A,1 ;only want to read 1 sector OUT (dat.port),A XOR A OUT (dat.port),A RET ;=============================================================== ;read the sector as addressed by select at buffer read: LD A,command OUT (reg.port),A LD A,read.sector OUT (dat.port),A XOR A OUT (dat.port),A read.wait: IN A,(dat.port) ;wait for sector to be read LD C,A ;into harddrive's buffer IN A,(dat.port) BIT 7,C ;busy JR NZ,read.wait BIT 0,C ;error (invalid sector) RET NZ LD A,data OUT (reg.port),A LD HL,buffer LD BC,dat.port INIR ;read 512 bytes INIR LD A,status ;read status OUT (reg.port),A IN A,(dat.port) LD C,A IN A,(dat.port) RET ;=============================================================== ;write data stored in buffer to the sector addressed by select write: LD A,command OUT (reg.port),A LD A,write.sector OUT (dat.port),A XOR A OUT (dat.port),A write.wait: IN A,(dat.port) LD C,A IN A,(dat.port) BIT 7,C ;busy JR NZ,write.wait BIT 3,C ;data request JR Z,write.wait LD A,data OUT (reg.port),A LD HL,buffer LD BC,dat.port OTIR ;write 512 bytes OTIR LD A,status OUT (reg.port),A write.wait2: IN A,(dat.port) LD C,A IN A,(dat.port) BIT 7,C ;busy JR NZ,write.wait2 RET ;=============================================================== size: XOR A LD (sect),A LD (trlo),A LD (trhi),A LD (head),A size.sector: CALL select CALL read LD HL,sect INC (HL) BIT 0,C ;error JR Z,size.sector DEC (HL) DEC (HL) size.head: LD A,(head) INC A LD (head),A CP 16 JR Z,got.max.head CALL select CALL read BIT 0,C JR Z,size.head got.max.head: LD HL,head DEC (HL) size.cyl: LD DE,32768 LD H,D LD L,E added.cyl: LD (trlo),HL PUSH DE CALL select CALL read POP DE SRL D RR E LD A,D OR E JR Z,got.max.cyl BIT 0,C JR Z,add.cyl LD HL,(trlo) SBC HL,DE JR added.cyl add.cyl: LD HL,(trlo) ADD HL,DE JR added.cyl got.max.cyl: LD A,(sect) INC A LD (maxsect),A LD HL,(trlo) LD (maxtrlo),HL LD A,(head) INC A LD (maxhead),A XOR A LD (sect),A LD (trlo),A LD (trhi),A LD (head),A RET ;=============================================================== ;excecute drive diagnostic, return error code in C: ; &01 = no error detected ; &02 = formater devic error ; &03 = sector buffer error ; &04 = ECC circuitry failure ; &05 = controlling microprocessor error ; &8x = error on second drive diagnostic: LD A,command OUT (reg.port),A LD A,drv.diagnose OUT (dat.port),A XOR A OUT (dat.port),A diag.wait: IN A,(dat.port) BIT 7,A IN A,(dat.port) JR NZ,diag.wait LD A,error.reg OUT (reg.port),A IN A,(dat.port) LD C,A IN A,(dat.port) LD B,0 RET ;=============================================================== ;next sector - sets up sector variables for next sector next.sector: LD HL,sect INC (HL) LD A,(maxsect) CP (HL) RET NZ LD (HL),0 LD HL,head INC (HL) LD A,(maxhead) CP (HL) RET NZ LD (HL),0 LD HL,(trlo) INC HL LD (trlo),HL LD DE,(maxtrlo) OR A SBC HL,DE RET NZ LD (trlo),HL RET ;=============================================================== ;call this routine to see the values received in the status port trap: LD HL,buffer LD BC,dat.port INIR INIR LD C,80 RET DEFB 0 ;space used to store sector buffer: DEFS 512