Observers on memory pages. Text memory is now regular RAM with an observer

This commit is contained in:
Ivan Izaguirre 2019-02-25 23:28:26 +01:00
parent 590dfb5cec
commit ac73b8823c
7 changed files with 47 additions and 100 deletions

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)