goapple2/cards/disk.go

232 lines
4.6 KiB
Go

package cards
import (
"fmt"
"github.com/zellyn/goapple2/disk"
)
type Disk interface {
Read() byte
Skip(int)
Write(byte)
SetHalfTrack(byte)
HalfTrack() byte
Writeable() bool
}
const (
MODE_READ = 0
MODE_WRITE = 1
MODE_SHIFT = 0
MODE_LOAD = 2
)
type DiskCard struct {
rom [256]byte
cm CardManager
slot byte
slotbit byte
disks [2]Disk
active int
phases byte
mode byte
onOff bool
dataRegister byte
lastAccess int
}
func NewDiskCard(rom []byte, slot byte, cm CardManager) (*DiskCard, error) {
if len(rom) != 256 {
return nil, fmt.Errorf("Wrong size ROM: expected 256, got %d", len(rom))
}
dc := &DiskCard{
cm: cm,
slot: slot,
slotbit: 1 << slot,
disks: [2]Disk{disk.NewDummy(disk.DEFAULT_VOLUME), disk.NewDummy(disk.DEFAULT_VOLUME)},
}
copy(dc.rom[:], rom)
return dc, nil
}
func (dc *DiskCard) String() string {
return fmt.Sprintf(
"Disk Card (slot %d): halfTrack=%d, active=%d, phases=$%02X, mode=$%02X, onoff=%v, dr=$%02X, la=%d",
dc.slot, dc.disks[0].HalfTrack(), dc.active, dc.phases, dc.mode, dc.onOff, dc.dataRegister,
dc.lastAccess)
}
func (dc *DiskCard) Slot() byte {
return dc.slot
}
func (dc *DiskCard) ROMDisabled() {
// Disk card doesn't have a $C(8-F)xx ROM
}
func (dc *DiskCard) handlePhase(phase byte, onOff bool) {
if !dc.onOff {
return
}
phaseBit := byte(1 << phase)
if onOff {
dc.phases |= phaseBit
} else {
dc.phases &^= phaseBit
}
disk := dc.disks[dc.active]
newTrack := int(disk.HalfTrack())
switch dc.phases {
case 1:
newTrack = (newTrack + 1) / 4 * 4
case 2:
newTrack = (newTrack/4)*4 + 1
case 4:
if newTrack != 0 {
newTrack = (newTrack-1)/4*4 + 2
}
case 8:
if newTrack < 2 {
newTrack = 0
} else {
newTrack = (newTrack-2)/4*4 + 3
}
default:
return
}
if newTrack < 0 {
newTrack = 0
}
if newTrack > 68 {
newTrack = 68
}
if disk.HalfTrack() != byte(newTrack) {
disk.SetHalfTrack(byte(newTrack))
}
}
func (dc *DiskCard) handleAccess(address byte) {
if address < 8 {
phase := address / 2
onOff := (address & 1) == 1
dc.handlePhase(phase, onOff)
return
}
switch address {
case 0x8:
dc.onOff = false
case 0x9:
dc.onOff = true
case 0xA, 0xB:
which := int(address & 1)
if dc.active != which {
dc.active = which
dc.handlePhase(0, dc.phases&1 == 1) // No change: force update
}
case 0xC, 0xD:
dc.mode = dc.mode&^2 | address&1<<1
case 0xE, 0xF:
dc.mode = dc.mode&^1 | address&1
}
}
func (dc *DiskCard) Read16(address byte) byte {
dc.handleAccess(address)
if address != 0xC && address != 0xE {
return 0xFF
}
if dc.onOff {
switch dc.mode {
case MODE_READ | MODE_SHIFT:
// Normal read
return dc.readOne()
case MODE_READ | MODE_LOAD:
// Check write-protect
if dc.disks[dc.active].Writeable() {
return 0x00
} else {
return 0xFF
}
case MODE_WRITE | MODE_SHIFT:
// Doesn't do anything in our simulation: just return last data
return dc.dataRegister
case MODE_WRITE | MODE_LOAD:
// Nonsense for reading: just return last data
return dc.dataRegister
default:
panic(fmt.Sprintf("Unexpected disk card mode: %d", dc.mode))
}
}
return 0xFF
}
func (dc *DiskCard) Write16(address byte, value byte) {
dc.handleAccess(address)
if dc.onOff {
switch dc.mode {
case MODE_READ | MODE_SHIFT:
// Normal read
// panic("Write while in read mode")
case MODE_READ | MODE_LOAD:
// Check write-protect
// panic("Write while in check-write-protect mode")
case MODE_WRITE | MODE_SHIFT:
// Shifting data to disk
// panic("Write while in shift mode")
case MODE_WRITE | MODE_LOAD:
if dc.disks[dc.active].Writeable() {
dc.writeOne(value)
}
default:
panic(fmt.Sprintf("Unexpected disk card mode: %d", dc.mode))
}
}
}
func (dc *DiskCard) readOne() byte {
if dc.lastAccess < 4 {
return dc.dataRegister
}
disk := dc.disks[dc.active]
dc.lastAccess = 0
dc.dataRegister = disk.Read()
return dc.dataRegister
}
func (dc *DiskCard) writeOne(value byte) {
disk := dc.disks[dc.active]
dc.dataRegister = value
disk.Write(value)
}
func (dc *DiskCard) Read(address uint16) byte {
panic(fmt.Sprintf("%s got read to $%04X", dc.String(), address))
}
func (dc *DiskCard) Write(address uint16, value byte) {
panic(fmt.Sprintf("%s got write to $%04X", dc.String(), address))
}
func (dc *DiskCard) Read256(address byte) byte {
return dc.rom[address]
}
func (dc *DiskCard) Write256(address byte, value byte) {
// Firmware is ROM: do nothing
}
func (dc *DiskCard) LoadDisk(d Disk, which int) {
dc.disks[which] = d
}
func (dc *DiskCard) WantTicker() bool {
return true
}
func (dc *DiskCard) Tick() {
dc.lastAccess++
}