2019-02-16 19:15:41 +00:00
|
|
|
package apple2
|
2019-02-16 16:32:06 +00:00
|
|
|
|
2019-02-17 23:01:48 +00:00
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
)
|
2019-02-16 16:32:06 +00:00
|
|
|
|
|
|
|
type ioC0Page struct {
|
2019-02-17 23:01:48 +00:00
|
|
|
ioFlags uint64
|
|
|
|
data [1]uint8
|
|
|
|
keyChannel chan uint8
|
2019-02-16 16:32:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 (
|
|
|
|
ioFlagNone uint8 = 0
|
|
|
|
ioFlagGraphics uint8 = 3
|
|
|
|
ioFlagMixed uint8 = 8
|
|
|
|
ioFlagSecondPage uint8 = 1
|
|
|
|
ioFlagHiRes uint8 = 2
|
|
|
|
ioFlagAnnunciator0 uint8 = 4
|
|
|
|
ioFlagAnnunciator1 uint8 = 5
|
|
|
|
ioFlagAnnunciator2 uint8 = 6
|
|
|
|
ioFlagAnnunciator3 uint8 = 7
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
ioDataKeyboard uint8 = 0
|
|
|
|
)
|
|
|
|
|
|
|
|
type softSwitch struct {
|
|
|
|
ioFlag uint8
|
|
|
|
value bool
|
|
|
|
onWriteOnly bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var softSwitches = [256]softSwitch{
|
|
|
|
0x50: softSwitch{ioFlagGraphics, false, false},
|
|
|
|
0x51: softSwitch{ioFlagGraphics, true, false},
|
|
|
|
0x52: softSwitch{ioFlagMixed, false, false},
|
|
|
|
0x53: softSwitch{ioFlagMixed, true, false},
|
|
|
|
0x54: softSwitch{ioFlagSecondPage, false, false},
|
|
|
|
0x55: softSwitch{ioFlagSecondPage, true, false},
|
|
|
|
0x56: softSwitch{ioFlagHiRes, false, false},
|
|
|
|
0x57: softSwitch{ioFlagHiRes, true, false},
|
|
|
|
0x58: softSwitch{ioFlagAnnunciator0, false, false},
|
|
|
|
0x59: softSwitch{ioFlagAnnunciator0, true, false},
|
|
|
|
0x5a: softSwitch{ioFlagAnnunciator1, false, false},
|
|
|
|
0x5b: softSwitch{ioFlagAnnunciator1, true, false},
|
|
|
|
0x5c: softSwitch{ioFlagAnnunciator2, false, false},
|
|
|
|
0x5d: softSwitch{ioFlagAnnunciator2, true, false},
|
|
|
|
0x5e: softSwitch{ioFlagAnnunciator3, false, false},
|
|
|
|
0x5f: softSwitch{ioFlagAnnunciator3, true, false},
|
|
|
|
}
|
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
func (p *ioC0Page) Peek(address uint8) uint8 {
|
2019-02-16 16:32:06 +00:00
|
|
|
//fmt.Printf("Peek on $C0%02x ", address)
|
|
|
|
return p.access(address, false, 0)
|
|
|
|
}
|
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
func (p *ioC0Page) Poke(address uint8, value uint8) {
|
2019-02-16 16:32:06 +00:00
|
|
|
//fmt.Printf("Poke on $C0%02x with %02x ", address, value)
|
|
|
|
p.access(address, true, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *ioC0Page) access(address uint8, isWrite bool, value uint8) uint8 {
|
|
|
|
|
|
|
|
ss := softSwitches[address]
|
|
|
|
if ss.ioFlag != ioFlagNone {
|
|
|
|
if !isWrite || !!ss.onWriteOnly {
|
|
|
|
if ss.value {
|
|
|
|
p.ioFlags |= 1 << ss.ioFlag
|
|
|
|
} else {
|
|
|
|
p.ioFlags &^= 1 << ss.ioFlag
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch address {
|
2019-02-17 23:01:48 +00:00
|
|
|
case 0x00: // keyboard (Is this the full range 0x0?)
|
|
|
|
return p.getKey()
|
|
|
|
case 0x10: // strobe (Is this the full range 0x1?)
|
|
|
|
return p.strobeKeyboard()
|
2019-02-16 16:32:06 +00:00
|
|
|
case 0x30: // spkr
|
|
|
|
// TODO: Support sound
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unknown softswitch 0xC0%02x", address))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
2019-02-17 23:01:48 +00:00
|
|
|
|
|
|
|
func (p *ioC0Page) initKeyboard() {
|
|
|
|
if p.keyChannel == nil {
|
|
|
|
p.keyChannel = make(chan uint8)
|
|
|
|
go func(c chan uint8) {
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
for {
|
|
|
|
ch, err := reader.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
c <- ch
|
|
|
|
}
|
|
|
|
}(p.keyChannel)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *ioC0Page) getKey() uint8 {
|
|
|
|
p.initKeyboard()
|
|
|
|
strobed := (p.data[ioDataKeyboard] & (1 << 7)) == 0
|
|
|
|
if strobed {
|
|
|
|
select {
|
|
|
|
case ch := <-p.keyChannel:
|
|
|
|
if ch == 10 {
|
|
|
|
ch = 13
|
|
|
|
}
|
|
|
|
p.data[ioDataKeyboard] = ch + (1 << 7)
|
|
|
|
default:
|
|
|
|
// Nothing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p.data[ioDataKeyboard]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *ioC0Page) strobeKeyboard() uint8 {
|
|
|
|
p.initKeyboard()
|
|
|
|
result := p.data[ioDataKeyboard]
|
|
|
|
p.data[ioDataKeyboard] &^= 1 << 7
|
|
|
|
return result
|
|
|
|
}
|