diff --git a/apple2/ioC0Page.go b/apple2/ioC0Page.go index 55958d8..d73cccb 100644 --- a/apple2/ioC0Page.go +++ b/apple2/ioC0Page.go @@ -85,6 +85,10 @@ func (p *ioC0Page) internalPeek(address uint8) uint8 { return 0 } +func (p *ioC0Page) all() []uint8 { + return nil +} + func (p *ioC0Page) Poke(address uint8, value uint8) { //fmt.Printf("Poke on $C0%02x with %02x ", address, value) ss := p.softSwitchesW[address] diff --git a/apple2/memoryManager.go b/apple2/memoryManager.go index 914e22f..d8c0a65 100644 --- a/apple2/memoryManager.go +++ b/apple2/memoryManager.go @@ -20,6 +20,7 @@ type memoryPage interface { Peek(uint8) uint8 Poke(uint8, uint8) internalPeek(uint8) uint8 + all() []uint8 } const ( @@ -43,6 +44,10 @@ func (mmu *memoryManager) internalPeek(address uint16) uint8 { return mmu.activeMemory[hi].internalPeek(lo) } +func (mmu *memoryManager) internalPage(hi uint8) []uint8 { + return mmu.activeMemory[hi].all() +} + // Poke sets the data at the given address func (mmu *memoryManager) Poke(address uint16, value uint8) { if address == ioC8Off { diff --git a/apple2/rxmPage.go b/apple2/rxmPage.go index 3c3bee3..3686dae 100644 --- a/apple2/rxmPage.go +++ b/apple2/rxmPage.go @@ -22,6 +22,10 @@ func (p *rxmPage) internalPeek(address uint8) uint8 { return p.data[address] } +func (p *rxmPage) all() []uint8 { + return p.data[:] +} + func (p *rxmPage) Poke(address uint8, value uint8) { p.touch(address, true) p.data[address] = value diff --git a/apple2/screen.go b/apple2/screen.go index d48c1b0..8188ca2 100644 --- a/apple2/screen.go +++ b/apple2/screen.go @@ -10,18 +10,29 @@ import ( // Snapshot the currently visible screen func Snapshot(a *Apple2) *image.RGBA { - isTextMode := a.io.isSoftSwitchActive(ioFlagGraphics) + isTextMode := a.io.isSoftSwitchActive(ioFlagText) is80ColMode := a.io.isSoftSwitchActive(ioFlag80Col) + isHiResMode := a.io.isSoftSwitchActive(ioFlagHiRes) pageIndex := 0 if a.io.isSoftSwitchActive(ioFlagSecondPage) { pageIndex = 1 } - if isTextMode && !is80ColMode { - //Text mode - return snapshotTextMode(a, pageIndex) + if isTextMode { + if is80ColMode { + // Not supported + } else { + return snapshotTextMode(a, pageIndex) + } + } else { + if isHiResMode { + return snapshotGraphMode(a, pageIndex) + } else { + // Not supported + } } - fmt.Printf("t: %v, 8: %v\n", isTextMode, is80ColMode) + + //fmt.Printf("g: %v, 8: %v, h: %v\n", isTextMode, is80ColMode, isHiResMode) return nil //panic("Screen mode not supported") } @@ -44,29 +55,39 @@ func saveSnapshot(a *Apple2) { } const ( - charWidth = 7 - charHeight = 8 - textColumns = 40 - textLines = 24 - textPage1Address = uint16(0x400) - textPage2Address = uint16(0x400) + charWidth = 7 + charHeight = 8 + textColumns = 40 + textLines = 24 + textPage1Address = uint16(0x0400) + textPage2Address = uint16(0x0800) + graphWidth = 280 + graphHeight = 192 + graphPage1Address = uint16(0x2000) + graphPage2Address = uint16(0x4000) ) +func getTextCharOffset(col int, line int) uint16 { + + // See "Understand the Apple II", page 5-10 + // http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf + section := line / 8 // Top, middle and bottom + eigth := line % 8 + return uint16(section*40 + eigth*0x80 + col) +} + func getTextChar(a *Apple2, col int, line int, page int) uint8 { address := textPage1Address if page == 1 { address = textPage2Address } - - // See "Understand the Apple II", page 5-10 - // http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf - section := line / 8 // Top, middle and bottom - eigth := line % 8 - address += uint16(section*40 + eigth*0x80 + col) + address += getTextCharOffset(col, line) return a.mmu.internalPeek(address) } func snapshotTextMode(a *Apple2, page int) *image.RGBA { + // TODO: Missing inverse and flash modes + width := textColumns * charWidth height := textLines * charHeight size := image.Rect(0, 0, width, height) @@ -90,3 +111,51 @@ func snapshotTextMode(a *Apple2, page int) *image.RGBA { return img } + +func getGraphLineOffset(line int) uint16 { + + // See "Understand the Apple II", page 5-14 + // http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf + section := line >> 6 // Top, middle and bottom + outerEigth := (line >> 3) & 0x07 + innerEigth := line & 0x07 + return uint16(section*40 + outerEigth*0x80 + innerEigth*0x400) +} + +func getGraphLine(a *Apple2, line int, page int) []uint8 { + address := graphPage1Address + if page == 1 { + address = graphPage2Address + } + + address += getGraphLineOffset(line) + hi := uint8(address >> 8) + lo := uint8(address) + + memPage := a.mmu.internalPage(hi) + //fmt.Printf("line: %v, lo: %x\n", line, lo) + return memPage[lo : lo+40] +} + +func snapshotGraphMode(a *Apple2, page int) *image.RGBA { + size := image.Rect(0, 0, graphWidth, graphHeight) + img := image.NewRGBA(size) + + for y := 0; y < graphHeight; y++ { + bytes := getGraphLine(a, y, page) + x := 0 + for _, b := range bytes { + for j := uint(0); j < 7; j++ { + bit := (b >> j) & 1 + colour := color.Black + if bit == 1 { + colour = color.White + } + img.Set(x, y, colour) + x++ + } + } + } + + return img +} diff --git a/apple2/screen_test.go b/apple2/screen_test.go new file mode 100644 index 0000000..fbf6fc8 --- /dev/null +++ b/apple2/screen_test.go @@ -0,0 +1,24 @@ +package apple2 + +import ( + "testing" +) + +func TestGetGraphLineOffest(t *testing.T) { + scenarios := map[int]uint16{ + 0: 0x2000, + 1: 0x2400, + 8: 0x2080, + 63: 0x3f80, + 64: 0x2028, + 128: 0x2050, + 191: 0x3fd0, + } + + for in, want := range scenarios { + got := 0x2000 + getGraphLineOffset(in) + if want != got { + t.Errorf("expected %x but got %x for line %v", want, got, in) + } + } +} diff --git a/apple2/softSwitches2.go b/apple2/softSwitches2.go index 0198e83..a9970a4 100644 --- a/apple2/softSwitches2.go +++ b/apple2/softSwitches2.go @@ -3,7 +3,7 @@ package apple2 const ( ioDataKeyboard uint8 = 0x10 - ioFlagGraphics uint8 = 0x50 + ioFlagText uint8 = 0x50 ioFlagMixed uint8 = 0x52 ioFlagSecondPage uint8 = 0x54 ioFlagHiRes uint8 = 0x56 @@ -33,8 +33,8 @@ func addApple2SoftSwitches(io *ioC0Page) { // for read and write. But the Apple2e take over some of them, with // the prevention on acting only on writes. - io.addSoftSwitchRW(0x50, getSoftSwitch(ioFlagGraphics, false)) - io.addSoftSwitchRW(0x51, getSoftSwitch(ioFlagGraphics, true)) + io.addSoftSwitchRW(0x50, getSoftSwitch(ioFlagText, false)) + io.addSoftSwitchRW(0x51, getSoftSwitch(ioFlagText, true)) io.addSoftSwitchRW(0x52, getSoftSwitch(ioFlagMixed, false)) io.addSoftSwitchRW(0x53, getSoftSwitch(ioFlagMixed, true)) io.addSoftSwitchRW(0x54, getSoftSwitch(ioFlagSecondPage, false)) diff --git a/apple2/unassignedPage.go b/apple2/unassignedPage.go index 8ae87e5..b8e479a 100644 --- a/apple2/unassignedPage.go +++ b/apple2/unassignedPage.go @@ -14,6 +14,10 @@ func (p *unassignedPage) internalPeek(address uint8) uint8 { return 0xdd } +func (p *unassignedPage) all() []uint8 { + return nil +} + func (p *unassignedPage) Poke(address uint8, value uint8) { //fmt.Printf("Write on address 0x%02x%02x\n", p.page, address) //panic(address) diff --git a/apple2sdl/run.go b/apple2sdl/run.go index 7c35953..8c909ce 100644 --- a/apple2sdl/run.go +++ b/apple2sdl/run.go @@ -42,26 +42,27 @@ func SDLRun(a *apple2.Apple2) { } } - img := *apple2.Snapshot(a) - surface, err := sdl.CreateRGBSurfaceFrom(unsafe.Pointer(&img.Pix[0]), 40*7, 24*8, 32, 40*7*4, - 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff) - if err != nil { - panic(err) + img := apple2.Snapshot(a) + if img != nil { + surface, err := sdl.CreateRGBSurfaceFrom(unsafe.Pointer(&img.Pix[0]), 40*7, 24*8, 32, 40*7*4, + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff) + if err != nil { + panic(err) + } + + texture, err := renderer.CreateTextureFromSurface(surface) + if err != nil { + panic(err) + } + + renderer.Clear() + w, h := window.GetSize() + renderer.Copy(texture, nil, &sdl.Rect{X: 0, Y: 0, W: w, H: h}) + renderer.Present() + + surface.Free() + texture.Destroy() } - - texture, err := renderer.CreateTextureFromSurface(surface) - if err != nil { - panic(err) - } - - renderer.Clear() - w, h := window.GetSize() - renderer.Copy(texture, nil, &sdl.Rect{X: 0, Y: 0, W: w, H: h}) - renderer.Present() - - surface.Free() - texture.Destroy() - sdl.Delay(1000 / 60) }