izapple2/smartPortHardDisk.go
2022-10-31 22:57:18 +01:00

130 lines
3.2 KiB
Go

package izapple2
import (
"fmt"
"github.com/ivanizag/izapple2/storage"
)
/*
To implement a smartPort hard drive we have to support the smartPort commands.
See:
Beneath Prodos, section 6-6, 7-13 and 5-8. (http://www.apple-iigs.info/doc/fichiers/beneathprodos.pdf)
Apple IIc Technical Reference, 2nd Edition. Chapter 8. https://ia800207.us.archive.org/19/items/AppleIIcTechnicalReference2ndEd/Apple%20IIc%20Technical%20Reference%202nd%20ed.pdf
*/
// SmartPortHardDisk represents a hard disk
type SmartPortHardDisk struct {
host *CardSmartPort // For DMA
filename string
trace bool
disk *storage.BlockDisk
}
// NewSmartPortHardDisk creates a new hard disk with the smartPort interface
func NewSmartPortHardDisk(host *CardSmartPort, filename string) (*SmartPortHardDisk, error) {
var d SmartPortHardDisk
d.host = host
d.filename = filename
hd, err := storage.OpenBlockDisk(filename)
if err != nil {
return nil, err
}
d.disk = hd
return &d, nil
}
func (d *SmartPortHardDisk) exec(call *smartPortCall) uint8 {
var result uint8
switch call.command {
case smartPortCommandStatus:
address := call.param16(2)
result = d.status(address)
case smartPortCommandReadBlock:
address := call.param16(2)
block := call.param24(4)
result = d.readBlock(block, address)
case smartPortCommandWriteBlock:
address := call.param16(2)
block := call.param24(4)
result = d.writeBlock(block, address)
default:
// Prodos device command not supported
result = smartPortErrorIO
}
if d.trace {
fmt.Printf("[SmartPortHardDisk] Command %v, return %s \n",
call, smartPortErrorMessage(result))
}
return result
}
func (d *SmartPortHardDisk) readBlock(block uint32, dest uint16) uint8 {
if d.trace {
fmt.Printf("[SmartPortHardDisk] Read block %v into $%x.\n", block, dest)
}
data, err := d.disk.Read(block)
if err != nil {
return smartPortErrorIO
}
// Byte by byte transfer to memory using the full Poke code path
for i := uint16(0); i < uint16(len(data)); i++ {
d.host.a.mmu.Poke(dest+i, data[i])
}
return smartPortNoError
}
func (d *SmartPortHardDisk) writeBlock(block uint32, source uint16) uint8 {
if d.trace {
fmt.Printf("[SmartPortHardDisk] Write block %v from $%x.\n", block, source)
}
if d.disk.IsReadOnly() {
return smartPortErrorWriteProtected
}
// Byte by byte transfer from memory using the full Peek code path
buf := make([]uint8, storage.ProDosBlockSize)
for i := uint16(0); i < uint16(len(buf)); i++ {
buf[i] = d.host.a.mmu.Peek(source + i)
}
err := d.disk.Write(block, buf)
if err != nil {
return smartPortErrorIO
}
return smartPortNoError
}
func (d *SmartPortHardDisk) status(dest uint16) uint8 {
if d.trace {
fmt.Printf("[SmartPortHardDisk] Status into $%x.\n", dest)
}
// See http://www.1000bit.it/support/manuali/apple/technotes/smpt/tn.smpt.2.html
d.host.a.mmu.Poke(dest+0, 0x01) // One device
d.host.a.mmu.Poke(dest+1, 0xff) // No interrupt
d.host.a.mmu.Poke(dest+2, 0x00)
d.host.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer
d.host.a.mmu.Poke(dest+4, 0x01)
d.host.a.mmu.Poke(dest+5, 0x00) // Version 1.0 final
d.host.a.mmu.Poke(dest+6, 0x00)
d.host.a.mmu.Poke(dest+7, 0x00) // Reserved
return smartPortNoError
}