izapple2/cardHardDisk.go
2019-11-09 18:13:19 +01:00

166 lines
4.6 KiB
Go

package apple2
import "fmt"
/*
To implement a hard drive we just have to support boot from #PR7 and the PRODOS expextations.
See Beneath Prodos, section 6-6, 7-13 and 5-8. (http://www.apple-iigs.info/doc/fichiers/beneathprodos.pdf)
See:
https://prodos8.com/docs/technote/21/
*/
type cardHardDisk struct {
cardBase
disk *hardDisk
trace bool
}
func buildHardDiskRom(slot int) []uint8 {
data := make([]uint8, 256)
ssBase := 0x80 + uint8(slot<<4)
copy(data, []uint8{
// Preamble bytes to comply with the expectation in $Cn01, 3, 5 and 7
0xa9, 0x20, // LDA #$20
0xa9, 0x00, // LDA #$20
0xa9, 0x03, // LDA #$20
0xa9, 0x3c, // LDA #$3c
// Alternate: 0xa9, 0x00, // LDA #$00 ; Not a Smartport device, but won't boot on ii+ ROM
// Boot code: SS will load block 0 in address $0800. The jump there.
// Note: on execution the first block expects $42 to $47 to have
// valid values to read block 0. At least Total Replay expects that.
0xa9, 0x01, // LDA·#$01
0x85, 0x42, // STA $42 ; Command READ(1)
0xa9, 0x00, // LDA·#$00
0x85, 0x43, // STA $43 ; Unit 0
0x85, 0x44, // STA $44 ; Dest LO($0800)
0x85, 0x46, // STA $46 ; Block LO(0)
0x85, 0x47, // STA $47 ; Block HI(0)
0xa9, 0x08, // LDA·#$08
0x85, 0x45, // STA $45 ; Dest HI($0800)
0xad, ssBase, 0xc0, // LDA $C0n1 ;Call to softswitch 0.
0xa2, uint8(slot << 4), // LDX $s7 ;Slot on hign nibble of X
0x4c, 0x01, 0x08, // JMP $801
})
copy(data[0x80:], []uint8{
0xad, ssBase + 0, 0xc0, // LDA $C0n0 ; Softswitch 0, execute command. Error code in reg A.
0x48, // PHA
0xae, ssBase + 1, 0xc0, // LDX $C0n1 ; Softswitch 2, LO(Blocks), STATUS needs that in reg X.
0xac, ssBase + 2, 0xc0, // LDY $C0n2 ; Softswitch 3, HI(Blocks). STATUS needs that in reg Y.
0x18, // CLC ; Clear carry for no errors.
0x68, // PLA ; Sets Z if no error
0xF0, 0x01, // BEQ $01 ; Skips the SEC if reg A is zero
0x38, // SEC ; Set carry on errors
0x60, // RTS
})
data[0xfc] = 0
data[0xfd] = 0
data[0xfe] = 3 // Status and Read. No write, no format. Single volume
data[0xff] = 0x80 // Driver entry point
return data
}
const (
proDosDeviceCommandStatus = 0
proDosDeviceCommandRead = 1
proDosDeviceCommandWrite = 2
proDosDeviceCommandFormat = 3
)
const (
proDosDeviceNoError = uint8(0)
proDosDeviceErrorIO = uint8(0x27)
proDosDeviceErrorNoDevice = uint8(0x28)
proDosDeviceErrorWriteProtected = uint8(0x2b)
)
func (c *cardHardDisk) assign(a *Apple2, slot int) {
c.addCardSoftSwitchR(0, func(*ioC0Page) uint8 {
// Prodos entry point
command := a.mmu.Peek(0x42)
unit := a.mmu.Peek(0x43)
address := uint16(a.mmu.Peek(0x44)) + uint16(a.mmu.Peek(0x45))<<8
block := uint16(a.mmu.Peek(0x46)) + uint16(a.mmu.Peek(0x47))<<8
if c.trace {
fmt.Printf("[CardHardDisk] Command %v on unit $%x, block %v to $%x.\n", command, unit, block, address)
}
switch command {
case proDosDeviceCommandStatus:
return proDosDeviceNoError
case proDosDeviceCommandRead:
return c.readBlock(block, address)
case proDosDeviceCommandWrite:
return c.writeBlock(block, address)
default:
// Prodos device command not supported
return proDosDeviceErrorIO
}
}, "HDCOMMAND")
c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 {
// Blocks available, low byte
return uint8(c.disk.header.Blocks)
}, "HDBLOCKSLO")
c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 {
// Blocks available, high byte
return uint8(c.disk.header.Blocks >> 8)
}, "HDBLOCKHI")
c.cardBase.assign(a, slot)
}
func (c *cardHardDisk) readBlock(block uint16, dest uint16) uint8 {
if c.trace {
fmt.Printf("[CardHardDisk] Read block %v into $%x.\n", block, dest)
}
data, err := c.disk.read(uint32(block))
if err != nil {
return proDosDeviceErrorIO
}
// Byte by byte transfer to memory using the full Poke code path
for i := uint16(0); i < uint16(proDosBlockSize); i++ {
c.a.mmu.Poke(dest+i, data[i])
}
return proDosDeviceNoError
}
func (c *cardHardDisk) writeBlock(block uint16, source uint16) uint8 {
if c.trace {
fmt.Printf("[CardHardDisk] Write block %v from $%x.\n", block, source)
}
if c.disk.readOnly {
return proDosDeviceErrorWriteProtected
}
// Byte by byte transfer from memory using the full Peek code path
buf := make([]uint8, proDosBlockSize)
for i := uint16(0); i < uint16(proDosBlockSize); i++ {
buf[i] = c.a.mmu.Peek(source + i)
}
err := c.disk.write(uint32(block), buf)
if err != nil {
return proDosDeviceErrorIO
}
return proDosDeviceNoError
}
func (c *cardHardDisk) addDisk(disk *hardDisk) {
c.disk = disk
}
func (c *cardHardDisk) setTrace(trace bool) {
c.trace = trace
}