diff --git a/apple2/ansiConsoleFrontend.go b/apple2/ansiConsoleFrontend.go index 03d359d..5373f25 100644 --- a/apple2/ansiConsoleFrontend.go +++ b/apple2/ansiConsoleFrontend.go @@ -21,12 +21,23 @@ type ansiConsoleFrontend struct { mmu *memoryManager keyChannel chan uint8 extraLineFeeds chan int + textUpdated bool } -func newAnsiConsoleFrontend(mmu *memoryManager) ansiConsoleFrontend { +func newAnsiConsoleFrontend(mmu *memoryManager) *ansiConsoleFrontend { var fe ansiConsoleFrontend fe.mmu = mmu - return fe + fe.subscribeToTextPages() + return &fe +} + +func (fe *ansiConsoleFrontend) subscribeToTextPages() { + observer := func(_ uint8, _ bool) { + fe.textUpdated = true + } + for i := 0x04; i < 0x08; i++ { + fe.mmu.physicalMainRAM[i].observer = observer + } } const refreshDelayMs = 100 @@ -37,7 +48,8 @@ func (fe *ansiConsoleFrontend) getKey() (key uint8, ok bool) { for { byte, err := reader.ReadByte() if err != nil { - panic(err) + fmt.Println(err) + return } c <- byte } @@ -67,12 +79,13 @@ func ansiCursorUp(steps int) { fmt.Printf("\033[%vA", steps) } -func (fe *ansiConsoleFrontend) textModeGoRoutine(tp *textPages) { +func (fe *ansiConsoleFrontend) textModeGoRoutine() { fe.extraLineFeeds = make(chan int, 100) fmt.Printf(strings.Repeat("\n", 26)) for { - if tp.strobe() { + if fe.textUpdated { + fe.textUpdated = false // Go up ansiCursorUp(26) done := false @@ -90,16 +103,17 @@ func (fe *ansiConsoleFrontend) textModeGoRoutine(tp *textPages) { // See "Understand the Apple II", page 5-10 // http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf isAltText := fe.mmu.isApple2e && fe.mmu.ioPage.isSoftSwitchExtActive(ioFlagAltChar) - var i, j, h uint8 + var i, j, h, c uint8 // Top, middle and botton screen for i = 0; i < 120; i = i + 40 { // Memory pages - for _, p := range tp.pages { + for j = 0x04; j < 0x08; j++ { + p := fe.mmu.physicalMainRAM[j] // The two half pages for _, h = range []uint8{0, 128} { line := "" - for j = i + h; j < i+h+40; j++ { - line += textMemoryByteToString(p.Peek(j), isAltText) + for c = i + h; c < i+h+40; c++ { + line += textMemoryByteToString(p.internalPeek(c), isAltText) } fmt.Printf("# %v #\n", line) } diff --git a/apple2/apple2.go b/apple2/apple2.go index b9239c0..fe4694a 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -10,8 +10,8 @@ func Run(romFile string, log bool) { s.Mem = mmu fe := newAnsiConsoleFrontend(mmu) - mmu.ioPage.setKeyboardProvider(&fe) - go fe.textModeGoRoutine(mmu.textPages1) + mmu.ioPage.setKeyboardProvider(fe) + go fe.textModeGoRoutine() // Start the processor core6502.Reset(&s) diff --git a/apple2/memoryManager.go b/apple2/memoryManager.go index 92eb18b..a587f39 100644 --- a/apple2/memoryManager.go +++ b/apple2/memoryManager.go @@ -18,8 +18,7 @@ type memoryManager struct { unassignedExpansionROM []unassignedPage // 0xc000 to 0xcfff ioPage *ioC0Page // 0xc000 to 0xc080 isApple2e bool - textPages1 *textPages // 0x0400 to 0x07ff - activeSlow int // Slot that has the addressing 0xc800 to 0ccfff + activeSlot int // Slot that has the addressing 0xc800 to 0ccfff } const ( @@ -87,14 +86,6 @@ func newAddressSpace(romImage string) *memoryManager { m.SetPage(page, p) } - // Replace RAM in the TEXT1 area. - // TODO: treat as normal ram. Add is dirty in all RAM pages - var t textPages - mmu.textPages1 = &t - for i := 0; i < 4; i++ { - m.SetPage(uint8(4+i), &(t.pages[i])) - } - return &mmu } diff --git a/apple2/rxmPage.go b/apple2/rxmPage.go index fbb3748..3c3bee3 100644 --- a/apple2/rxmPage.go +++ b/apple2/rxmPage.go @@ -1,23 +1,36 @@ package apple2 +type rxmPage struct { + data [256]uint8 + observer func(address uint8, isWrite bool) +} + type ramPage struct { - data [256]uint8 + rxmPage } type romPage struct { - data [256]uint8 + rxmPage } -func (p *ramPage) Peek(address uint8) uint8 { +func (p *rxmPage) Peek(address uint8) uint8 { + p.touch(address, false) return p.data[address] } -func (p *ramPage) Poke(address uint8, value uint8) { +func (p *rxmPage) internalPeek(address uint8) uint8 { + return p.data[address] +} + +func (p *rxmPage) Poke(address uint8, value uint8) { + p.touch(address, true) p.data[address] = value } -func (p *romPage) Peek(address uint8) uint8 { - return p.data[address] +func (p *rxmPage) touch(address uint8, isWrite bool) { + if p.observer != nil { + p.observer(address, isWrite) + } } func (p *romPage) Poke(address uint8, value uint8) { diff --git a/apple2/textPages.go b/apple2/textPages.go deleted file mode 100644 index a730448..0000000 --- a/apple2/textPages.go +++ /dev/null @@ -1,48 +0,0 @@ -package apple2 - -type textPages struct { - pages [4]textPage -} - -type textPage struct { - dirty bool - data [256]uint8 -} - -func (p *textPage) Peek(address uint8) uint8 { - return p.data[address] -} - -func (p *textPage) Poke(address uint8, value uint8) { - p.data[address] = value - // Note: we could avoid setting dirty on the 16 blocks of 8 hidden bytes - p.dirty = true -} - -func (tp *textPages) read(column uint8, line uint8) uint8 { - page, address := tp.charAddress(column, line) - return tp.pages[page].Peek(address) -} - -func (tp *textPages) write(column uint8, line uint8, value uint8) { - page, address := tp.charAddress(column, line) - tp.pages[page].Poke(address, value) -} - -func (tp *textPages) charAddress(column uint8, line uint8) (page uint8, address uint8) { - page = (line % 8) / 2 - address = column + (line/8)*40 + (line%2)*128 - return -} - -func (tp *textPages) strobe() bool { - // Thread safe. May just mark more dirties than needed. - dirty := false - for i := 0; i < 4; i++ { - if tp.pages[i].dirty { - dirty = true - tp.pages[i].dirty = false - } - } - return dirty -} diff --git a/apple2/textPages_test.go b/apple2/textPages_test.go deleted file mode 100644 index 2e5ccad..0000000 --- a/apple2/textPages_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package apple2 - -import "testing" - -func TestCharAddress(t *testing.T) { - var tp textPages - - mappings := [][]uint8{ - // column, line, page, address - {0, 0, 0, 0}, - {0, 1, 0, 0x80}, - {0, 2, 1, 0x00}, - {0, 23, 3, 0xD0}, - } - - for _, v := range mappings { - page, address := tp.charAddress(v[0], v[1]) - if page != v[2] || address != v[3] { - t.Errorf("Error on charAddress for (%v, %v) (%v:%02x) <> (%v:%02x)", - v[0], v[1], v[2], v[3], page, address) - } - } -} diff --git a/main.go b/main.go index 6f2d87d..9bc1d61 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,8 @@ import "go6502/apple2" func main() { //romFile := "apple2/romdumps/Apple2.rom" - //romFile := "apple2/romdumps/Apple2_Plus.rom" - romFile := "apple2/romdumps/Apple2e.rom" + romFile := "apple2/romdumps/Apple2_Plus.rom" + //romFile := "apple2/romdumps/Apple2e.rom" log := false apple2.Run(romFile, log)