From ca38abd3d7987ae15db43c84c8fd5db55e918b11 Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Sun, 12 May 2019 19:22:32 +0200 Subject: [PATCH] Support for lores graphics, per specs, no artifcats --- apple2/screen.go | 7 +- apple2/{screenHgr.go => screenHiRes.go} | 44 +++---- ...{screenHgr_test.go => screenHiRes_test.go} | 4 +- apple2/screenLoRes.go | 119 ++++++++++++++++++ apple2/screenNtscFilter.go | 1 + 5 files changed, 148 insertions(+), 27 deletions(-) rename apple2/{screenHgr.go => screenHiRes.go} (82%) rename apple2/{screenHgr_test.go => screenHiRes_test.go} (78%) create mode 100644 apple2/screenLoRes.go diff --git a/apple2/screen.go b/apple2/screen.go index 48e8685..8e145b2 100644 --- a/apple2/screen.go +++ b/apple2/screen.go @@ -21,7 +21,7 @@ func Snapshot(a *Apple2) *image.RGBA { isTextMode := a.io.isSoftSwitchActive(ioFlagText) isHiResMode := a.io.isSoftSwitchActive(ioFlagHiRes) isMixMode := a.io.isSoftSwitchActive(ioFlagMixed) - // Todo: isMixMode + pageIndex := 0 if a.io.isSoftSwitchActive(ioFlagSecondPage) { pageIndex = 1 @@ -44,8 +44,9 @@ func Snapshot(a *Apple2) *image.RGBA { if isHiResMode { snap = snapshotHiResModeMonoShift(a, pageIndex, isMixMode, lightColor) } else { - // Lo res mode not supported - return nil + snap = snapshotLoResModeReferenceColor(a, pageIndex, isMixMode) + isColor = false + isMixMode = false } if isMixMode { snapText := snapshotTextMode(a, pageIndex, isHiResMode, lightColor) diff --git a/apple2/screenHgr.go b/apple2/screenHiRes.go similarity index 82% rename from apple2/screenHgr.go rename to apple2/screenHiRes.go index ad44c5f..8529b77 100644 --- a/apple2/screenHgr.go +++ b/apple2/screenHiRes.go @@ -6,14 +6,14 @@ import ( ) const ( - graphWidth = 280 - graphHeight = 192 - graphHeightMixed = 160 - graphPage1Address = uint16(0x2000) - graphPage2Address = uint16(0x4000) + hiResWidth = 280 + hiResHeight = 192 + hiResHeightMixed = 160 + hiResPage1Address = uint16(0x2000) + hiResPage2Address = uint16(0x4000) ) -func getGraphLineOffset(line int) uint16 { +func getHiResLineOffset(line int) uint16 { // See "Understanding the Apple II", page 5-14 // http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf @@ -23,13 +23,13 @@ func getGraphLineOffset(line int) uint16 { return uint16(section*40 + outerEigth*0x80 + innerEigth*0x400) } -func getGraphLine(a *Apple2, line int, page int) []uint8 { - address := graphPage1Address +func getHiResLine(a *Apple2, line int, page int) []uint8 { + address := hiResPage1Address if page == 1 { - address = graphPage2Address + address = hiResPage2Address } - address += getGraphLineOffset(line) + address += getHiResLineOffset(line) hi := uint8(address >> 8) lo := uint8(address) @@ -40,16 +40,16 @@ func getGraphLine(a *Apple2, line int, page int) []uint8 { func snapshotHiResModeMonoShift(a *Apple2, page int, mixedMode bool, light color.Color) *image.RGBA { // As described in "Undertanding the Apple II", with half pixel shifts - height := graphHeight + height := hiResHeight if mixedMode { - height = graphHeightMixed + height = hiResHeightMixed } - size := image.Rect(0, 0, 2*graphWidth, height) + size := image.Rect(0, 0, 2*hiResWidth, height) img := image.NewRGBA(size) for y := 0; y < height; y++ { - bytes := getGraphLine(a, y, page) + bytes := getHiResLine(a, y, page) x := 0 var previousColour color.Color = color.Black for _, b := range bytes { @@ -78,12 +78,12 @@ func snapshotHiResModeMonoShift(a *Apple2, page int, mixedMode bool, light color func snapshotHiResModeReferenceColor(a *Apple2, page int, mixedMode bool) *image.RGBA { // As defined on "Apple II Reference Manual", page 19 - height := graphHeight + height := hiResHeight if mixedMode { - height = graphHeightMixed + height = hiResHeightMixed } - size := image.Rect(0, 0, graphWidth, height) + size := image.Rect(0, 0, hiResWidth, height) img := image.NewRGBA(size) // RGB values from https://mrob.com/pub/xapple2/colors.html @@ -109,7 +109,7 @@ func snapshotHiResModeReferenceColor(a *Apple2, page int, mixedMode bool) *image } for y := 0; y < height; y++ { - bytes := getGraphLine(a, y, page) + bytes := getHiResLine(a, y, page) x := 0 previous := uint8(0) for _, b := range bytes { @@ -136,12 +136,12 @@ func snapshotHiResModeReferenceColorSolid(a *Apple2, page int, mixedMode bool) * // As defined on "Apple II Reference Manual", page 19 // but with more solid colors and half the resolution - height := graphHeight + height := hiResHeight if mixedMode { - height = graphHeightMixed + height = hiResHeightMixed } - size := image.Rect(0, 0, graphWidth/2, height) + size := image.Rect(0, 0, hiResWidth/2, height) img := image.NewRGBA(size) // RGB values from https://mrob.com/pub/xapple2/colors.html @@ -167,7 +167,7 @@ func snapshotHiResModeReferenceColorSolid(a *Apple2, page int, mixedMode bool) * } for y := 0; y < height; y++ { - bytes := getGraphLine(a, y, page) + bytes := getHiResLine(a, y, page) x := 0 previous := uint8(0) for _, b := range bytes { diff --git a/apple2/screenHgr_test.go b/apple2/screenHiRes_test.go similarity index 78% rename from apple2/screenHgr_test.go rename to apple2/screenHiRes_test.go index fbf6fc8..8d7e6ab 100644 --- a/apple2/screenHgr_test.go +++ b/apple2/screenHiRes_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestGetGraphLineOffest(t *testing.T) { +func TestGetHiResLineOffest(t *testing.T) { scenarios := map[int]uint16{ 0: 0x2000, 1: 0x2400, @@ -16,7 +16,7 @@ func TestGetGraphLineOffest(t *testing.T) { } for in, want := range scenarios { - got := 0x2000 + getGraphLineOffset(in) + got := 0x2000 + getHiResLineOffset(in) if want != got { t.Errorf("expected %x but got %x for line %v", want, got, in) } diff --git a/apple2/screenLoRes.go b/apple2/screenLoRes.go new file mode 100644 index 0000000..4c6dabf --- /dev/null +++ b/apple2/screenLoRes.go @@ -0,0 +1,119 @@ +package apple2 + +import ( + "image" +) + +const ( + loResPixelWidth = charWidth + loResPixelHeight = charHeight / 2 + + loResWidth = textColumns + loResHeight = textLines * 2 + loResHeightMixed = (textLines - textLinesMix) * 2 + loRes + loResPage1Address = textPage1Address + loResPage2Address = textPage2Address +) + +func snapshotLoResModeReferenceColor(a *Apple2, page int, mixedMode bool) *image.RGBA { + // As defined on "Apple II Reference Manual" + + height := loResHeight + if mixedMode { + height = loResHeightMixed + } + + size := image.Rect(0, 0, loResWidth, height) + img := image.NewRGBA(size) + + // Lores colors correspond to the NTSC 4 bit patterns reversed + colorMap := getNTSCColorMap() + reversedNibble := []uint8{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15} + + for y := 0; y < height; y = y + 2 { + for x := 0; x < loResWidth; x++ { + // Each text mode char encodes two pixels + char := getTextChar(a, x, y/2, page) + bottom := char >> 4 + top := char & 0xf + img.Set(x, y, colorMap[reversedNibble[top]]) + img.Set(x, y+1, colorMap[reversedNibble[bottom]]) + } + } + + return img +} + +/* +func getLoResLine(a *Apple2, line int, page int) []uint8 { + address := loResPage1Address + if page == 1 { + address = loResPage2Address + } + + // Every text line encodes two lores lines + address += getTextCharOffset(0, line/2) + data := make([]uint8, 0, textColumns) + lower := (line % 2) == 1 + for i := uint16(0); i < textColumns; i++ { + // Two pixels are encoded on each text page char position + v := a.mmu.internalPeek(address + i) + if lower { + // The four nost significant bits store the odd lines + v >>= 4 + } else { + // The four least significant bits store the even lines + v &= 0xf + } + data = append(data) + } + return data +} + +func snapshotLoResModeMonoShift(a *Apple2, page int, mixedMode bool, light color.Color) *image.RGBA { + // As described in "Undertanding the Apple II", with half pixel shifts + + height := loResHeight + if mixedMode { + height = loResHeightMixed + } + + size := image.Rect(0, 0, 2*loResWidth*loResPixelWidth, height*loResPixelHeight) + img := image.NewRGBA(size) + + for y := 0; y < height; y++ { + bytes := getLoResLine(a, y, page) + x := 0 + for i, v := range bytes { + // For each loRes 4bit pixel we have to complete 7*2 half mono pixels + for j := 0; j < 14; i++ { + + } + } + + x := 0 + var previousColour color.Color = color.Black + for _, b := range bytes { + shifted := b>>7 == 1 + for j := uint(0); j < 7; j++ { + bit := (b >> j) & 1 + colour := light + if bit == 0 { + colour = color.Black + } + + if shifted { + img.Set(x, y, previousColour) + } else { + img.Set(x, y, colour) + } + img.Set(x+1, y, colour) + previousColour = colour + x += 2 + } + } + } + return img +} +*/ diff --git a/apple2/screenNtscFilter.go b/apple2/screenNtscFilter.go index 488d7d8..fe4b1c4 100644 --- a/apple2/screenNtscFilter.go +++ b/apple2/screenNtscFilter.go @@ -50,6 +50,7 @@ func getNTSCColorMap() []color.Color { /* 1110 */ ltBlue, /* 1111 */ white, } + return colorMap }