package izapple2 import ( "fmt" ) type ioC0Page struct { softSwitchesR [256]softSwitchR softSwitchesW [256]softSwitchW softSwitchesRName [256]string softSwitchesWName [256]string softSwitchesData [128]uint8 keyboard KeyboardProvider speaker SpeakerProvider paddlesStrobeCycle uint64 joysticks JoysticksProvider mouse MouseProvider apple2 *Apple2 traceMask uint16 // A bit for each 16 softswitches panicMask uint16 // A bit for each 16 softswitches traceRegistrations bool } type softSwitchR func() uint8 type softSwitchW func(value uint8) // SpeakerProvider provides a speaker implementation type SpeakerProvider interface { // Click receives a speaker click. The argument is the CPU cycle when it is generated Click(cycle uint64) } // JoysticksProvider abstracts the joysticks type JoysticksProvider interface { ReadButton(i int) bool ReadPaddle(i int) (uint8, bool) } // MouseProvider abstracts the mouse type MouseProvider interface { ReadMouse() (x uint16, y uint16, pressed bool) } // See https://www.kreativekorp.com/miscpages/a2info/iomemory.shtml // See https://stason.org/TULARC/pc/apple2/programmer/004-I-d-like-to-do-some-serious-Apple-II-programming-Whe.html const ( ssOn uint8 = 0x80 ssOff uint8 = 0x00 ) func newIoC0Page(a *Apple2) *ioC0Page { var io ioC0Page io.apple2 = a return &io } func (p *ioC0Page) setTrace(trace bool) { if trace { p.traceMask = 0xffff } else { p.traceMask = 0x0000 } } func (p *ioC0Page) traceSlot(slot int) { p.traceMask |= 1 << (8 + slot) } func (p *ioC0Page) panicNotImplementedSlot(slot int) { p.panicMask |= 1 << (8 + slot) } func (p *ioC0Page) setTraceRegistrations(traceRegistrations bool) { p.traceRegistrations = traceRegistrations } func (p *ioC0Page) setPanicNotImplemented(value bool) { if value { p.panicMask = 0xffff } else { p.panicMask = 0x0000 } } func (p *ioC0Page) addSoftSwitchRW(address uint8, ss softSwitchR, name string) { p.addSoftSwitchR(address, ss, name) p.addSoftSwitchW(address, func(uint8) { ss() }, name) } func (p *ioC0Page) addSoftSwitchR(address uint8, ss softSwitchR, name string) { if p.traceRegistrations { fmt.Printf("Softswitch registered in $c0%02x for reads as %s\n", address, name) } p.softSwitchesR[address] = ss p.softSwitchesRName[address] = name } func (p *ioC0Page) addSoftSwitchW(address uint8, ss softSwitchW, name string) { if p.traceRegistrations { fmt.Printf("Softswitch registered in $c0%02x for writes as %s\n", address, name) } p.softSwitchesW[address] = ss p.softSwitchesWName[address] = name } func (p *ioC0Page) isSoftSwitchActive(ioFlag uint8) bool { return (p.softSwitchesData[ioFlag] & ssOn) == ssOn } func (p *ioC0Page) setKeyboardProvider(kb KeyboardProvider) { p.keyboard = kb } func (p *ioC0Page) setSpeakerProvider(s SpeakerProvider) { p.speaker = s } func (p *ioC0Page) setJoysticksProvider(j JoysticksProvider) { p.joysticks = j } func (p *ioC0Page) setMouseProvider(m MouseProvider) { p.mouse = m } func (p *ioC0Page) isTraced(address uint16) bool { ss := address & 0xff return address != 0xc000 && // Do not trace the spammy keyboard softswitch (p.traceMask&(1<<(ss>>4))) != 0 } func (p *ioC0Page) isPanicNotImplemented(address uint16) bool { ss := address & 0xff return ss != 0xc068 && // Ignore known IIGS softswitch (p.panicMask&(1<<(ss>>4))) != 0 } func (p *ioC0Page) peek(address uint16) uint8 { pageAddress := uint8(address) ss := p.softSwitchesR[pageAddress] if ss == nil { if p.isTraced(address) { fmt.Printf("Unknown softswitch on read to $%04x\n", address) } if p.isPanicNotImplemented(address) { panic(fmt.Sprintf("Unknown softswitch on read to $%04x", address)) } return 0 } value := ss() if p.isTraced(address) { name := p.softSwitchesRName[pageAddress] fmt.Printf("Softswitch peek on $%04x %v: $%02x\n", address, name, value) } return value } func (p *ioC0Page) poke(address uint16, value uint8) { pageAddress := uint8(address) ss := p.softSwitchesW[pageAddress] if ss == nil { if p.isTraced(address) { fmt.Printf("Unknown softswitch on write $%02x to $%04x\n", value, address) } if p.isPanicNotImplemented(address) { panic(fmt.Sprintf("Unknown softswitch on write to $%04x", address)) } return } if p.isTraced(address) { name := p.softSwitchesWName[pageAddress] fmt.Printf("Softswitch poke on $%04x %v with $%02x\n", address, name, value) } ss(value) } func ssFromBool(value bool) uint8 { if value { return ssOn } return ssOff }