2022-10-23 16:02:19 +00:00
|
|
|
package izapple2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/ivanizag/izapple2/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
2022-10-24 21:09:06 +00:00
|
|
|
To implement a smartPort hard drive we have to support the smartPort commands.
|
2022-10-23 16:02:19 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2022-10-24 21:09:06 +00:00
|
|
|
// SmartPortHardDisk represents a hard disk
|
2022-10-23 16:02:19 +00:00
|
|
|
type SmartPortHardDisk struct {
|
2022-10-24 21:09:06 +00:00
|
|
|
host *CardSmartPort // For DMA
|
2022-10-23 16:02:19 +00:00
|
|
|
filename string
|
|
|
|
trace bool
|
|
|
|
disk *storage.BlockDisk
|
|
|
|
}
|
|
|
|
|
2022-10-24 21:09:06 +00:00
|
|
|
// NewSmartPortHardDisk creates a new hard disk with the smartPort interface
|
|
|
|
func NewSmartPortHardDisk(host *CardSmartPort, filename string) (*SmartPortHardDisk, error) {
|
2022-10-23 16:02:19 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-10-24 21:09:06 +00:00
|
|
|
func (d *SmartPortHardDisk) exec(call *smartPortCall) uint8 {
|
2022-10-23 16:02:19 +00:00
|
|
|
var result uint8
|
|
|
|
|
2022-10-24 21:09:06 +00:00
|
|
|
switch call.command {
|
2022-10-31 21:57:18 +00:00
|
|
|
case smartPortCommandStatus:
|
2022-10-24 21:09:06 +00:00
|
|
|
address := call.param16(2)
|
2022-10-23 16:02:19 +00:00
|
|
|
result = d.status(address)
|
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
case smartPortCommandReadBlock:
|
2022-10-24 21:09:06 +00:00
|
|
|
address := call.param16(2)
|
|
|
|
block := call.param24(4)
|
2022-10-23 16:02:19 +00:00
|
|
|
result = d.readBlock(block, address)
|
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
case smartPortCommandWriteBlock:
|
2022-10-24 21:09:06 +00:00
|
|
|
address := call.param16(2)
|
|
|
|
block := call.param24(4)
|
2022-10-23 16:02:19 +00:00
|
|
|
result = d.writeBlock(block, address)
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Prodos device command not supported
|
2022-10-31 21:57:18 +00:00
|
|
|
result = smartPortErrorIO
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if d.trace {
|
2022-10-31 21:57:18 +00:00
|
|
|
fmt.Printf("[SmartPortHardDisk] Command %v, return %s \n",
|
2022-10-24 21:09:06 +00:00
|
|
|
call, smartPortErrorMessage(result))
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-10-31 21:57:18 +00:00
|
|
|
return smartPortErrorIO
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
2022-10-24 21:09:06 +00:00
|
|
|
|
2022-10-23 16:02:19 +00:00
|
|
|
// 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])
|
|
|
|
}
|
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
return smartPortNoError
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2022-10-31 21:57:18 +00:00
|
|
|
return smartPortErrorWriteProtected
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2022-10-31 21:57:18 +00:00
|
|
|
return smartPortErrorIO
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
return smartPortNoError
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2022-10-24 21:09:06 +00:00
|
|
|
d.host.a.mmu.Poke(dest+0, 0x01) // One device
|
2022-10-23 16:02:19 +00:00
|
|
|
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
|
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
return smartPortNoError
|
2022-10-23 16:02:19 +00:00
|
|
|
}
|