diff --git a/README.md b/README.md index 3f6a9f7..c31196c 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@ Portable emulator of an Apple II+. Written in Go. - Apple //e enhanced with 128Kb of RAM - Base64A clone with 48Kb of base RAM and paginated ROM - Sound -- 16 Sector diskettes in DSK format -- ProDos hard disk +- Storage + - 16 Sector diskettes in DSK format + - ProDos hard disk - Emulated extension cards: - DiskII controller - 16Kb Language Card @@ -27,6 +28,7 @@ Portable emulator of an Apple II+. Written in Go. - Low-Resolution graphics - Double-Width Low-Resolution graphics (Apple //e only) - High-Resolution graphics + - Double-Width High-Resolution graphics (Apple //e only) - Mixed mode - Displays: - Green monochrome monitor with half width pixel support diff --git a/screen.go b/screen.go index 8b06c63..f584065 100644 --- a/screen.go +++ b/screen.go @@ -22,14 +22,9 @@ func Snapshot(a *Apple2) *image.RGBA { isHiResMode := a.io.isSoftSwitchActive(ioFlagHiRes) isMixMode := a.io.isSoftSwitchActive(ioFlagMixed) is80Columns := a.io.isSoftSwitchActive(ioFlag80Col) - isDoubleResMode := !isTextMode && is80Columns && a.io.isSoftSwitchActive(ioFlagAnnunciator3) + isDoubleResMode := !isTextMode && is80Columns && !a.io.isSoftSwitchActive(ioFlagAnnunciator3) isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active - pageIndex := 0 - if isSecondPage { - pageIndex = 1 - } - var lightColor color.Color if isColor { lightColor = color.White @@ -45,7 +40,11 @@ func Snapshot(a *Apple2) *image.RGBA { snap = snapshotTextMode(a, is80Columns, isSecondPage, false /*isMixMode*/, lightColor) } else { if isHiResMode { - snap = snapshotHiResModeMonoShift(a, pageIndex, isMixMode, lightColor) + if isDoubleResMode { + snap = snapshotDoubleHiResModeMono(a, isSecondPage, isMixMode, lightColor) + } else { + snap = snapshotHiResModeMono(a, isSecondPage, isMixMode, lightColor) + } } else { snap = snapshotLoResModeMono(a, isDoubleResMode, isSecondPage, isMixMode, lightColor) } diff --git a/screenHiRes.go b/screenHiRes.go index d19b770..f4c733d 100644 --- a/screenHiRes.go +++ b/screenHiRes.go @@ -7,6 +7,8 @@ import ( const ( hiResWidth = 280 + hiResLineBytes = hiResWidth / 7 + doubleHiResWidth = 2 * hiResWidth hiResHeight = 192 hiResHeightMixed = 160 hiResPage1Address = uint16(0x2000) @@ -23,17 +25,17 @@ func getHiResLineOffset(line int) uint16 { return uint16(section*40 + outerEigth*0x80 + innerEigth*0x400) } -func getHiResLine(a *Apple2, line int, page int) []uint8 { +func getHiResLine(a *Apple2, line int, isSecondPage bool, auxMem bool) []uint8 { address := hiResPage1Address - if page == 1 { + if isSecondPage { address = hiResPage2Address } address += getHiResLineOffset(line) - return a.mmu.physicalMainRAM.subRange(address, address+40) + return a.mmu.getPhysicalMainRAM(auxMem).subRange(address, address+hiResLineBytes) } -func snapshotHiResModeMonoShift(a *Apple2, page int, mixedMode bool, light color.Color) *image.RGBA { +func snapshotHiResModeMono(a *Apple2, isSecondPage bool, mixedMode bool, light color.Color) *image.RGBA { // As described in "Undertanding the Apple II", with half pixel shifts height := hiResHeight @@ -45,7 +47,7 @@ func snapshotHiResModeMonoShift(a *Apple2, page int, mixedMode bool, light color img := image.NewRGBA(size) for y := 0; y < height; y++ { - bytes := getHiResLine(a, y, page) + bytes := getHiResLine(a, y, isSecondPage, false /*auxMem*/) x := 0 var previousColour color.Color = color.Black for _, b := range bytes { @@ -71,116 +73,39 @@ func snapshotHiResModeMonoShift(a *Apple2, page int, mixedMode bool, light color return img } -func snapshotHiResModeReferenceColor(a *Apple2, page int, mixedMode bool) *image.RGBA { - // As defined on "Apple II Reference Manual", page 19 +func snapshotDoubleHiResModeMono(a *Apple2, isSecondPage bool, mixedMode bool, light color.Color) *image.RGBA { + // As described in "Inside the Apple IIe" height := hiResHeight if mixedMode { height = hiResHeightMixed } - size := image.Rect(0, 0, hiResWidth, height) + size := image.Rect(0, 0, doubleHiResWidth, height) img := image.NewRGBA(size) - - // RGB values from https://mrob.com/pub/xapple2/colors.html - black := color.RGBA{0, 0, 0, 255} - violet := color.RGBA{255, 68, 253, 255} - red := color.RGBA{255, 106, 60, 255} - green := color.RGBA{20, 246, 60, 255} - blue := color.RGBA{20, 207, 253, 255} - white := color.RGBA{255, 255, 255, 255} - colorMap := [][][]color.Color{ - { - /* 00 */ {black, black}, - /* 01 */ {black, green}, - /* 10 */ {violet, black}, - /* 11 */ {white, white}, - }, - { - /* 00 */ {black, black}, - /* 01 */ {black, red}, - /* 10 */ {blue, black}, - /* 11 */ {white, white}, - }, - } - for y := 0; y < height; y++ { - bytes := getHiResLine(a, y, page) + lineParts := [][]uint8{ + getHiResLine(a, y, isSecondPage, true), + getHiResLine(a, y, isSecondPage, false), + } x := 0 - previous := uint8(0) - for _, b := range bytes { - shift := b >> 7 - for j := uint(0); j < 7; j++ { - bit := (b >> j) & 1 - even := x%2 == 0 - if even { - previous = bit - } else { - pair := colorMap[shift][(previous<<1)+bit] - img.Set(x-1, y, pair[0]) - img.Set(x, y, pair[1]) + // For the NTSC filter to work we have to insert an initial black pixel and skip the last one + img.Set(x, y, color.Black) + x++ + for iByte := 0; iByte < hiResLineBytes-1; iByte++ { + for iPart := 0; iPart < 2; iPart++ { + b := lineParts[iPart][iByte] + for j := uint(0); j < 7; j++ { + bit := (b >> j) & 1 + colour := light + if bit == 0 { + colour = color.Black + } + img.Set(x, y, colour) + x++ } - x++ } } } - - return img -} - -func snapshotHiResModeReferenceColorSolid(a *Apple2, page int, mixedMode bool) *image.RGBA { - // As defined on "Apple II Reference Manual", page 19 - // but with more solid colors and half the resolution - - height := hiResHeight - if mixedMode { - height = hiResHeightMixed - } - - size := image.Rect(0, 0, hiResWidth/2, height) - img := image.NewRGBA(size) - - // RGB values from https://mrob.com/pub/xapple2/colors.html - black := color.RGBA{0, 0, 0, 255} - violet := color.RGBA{255, 68, 253, 255} - red := color.RGBA{255, 106, 60, 255} - green := color.RGBA{20, 246, 60, 255} - blue := color.RGBA{20, 207, 253, 255} - white := color.RGBA{255, 255, 255, 255} - colorMap := [][]color.Color{ - { - /* 00 */ black, - /* 01 */ green, - /* 10 */ violet, - /* 11 */ white, - }, - { - /* 00 */ black, - /* 01 */ red, - /* 10 */ blue, - /* 11 */ white, - }, - } - - for y := 0; y < height; y++ { - bytes := getHiResLine(a, y, page) - x := 0 - previous := uint8(0) - for _, b := range bytes { - shift := b >> 7 - for j := uint(0); j < 7; j++ { - bit := (b >> j) & 1 - even := x%2 == 0 - if even { - previous = bit - } else { - colour := colorMap[shift][(previous<<1)+bit] - img.Set(x/2, y, colour) - } - x++ - } - } - } - return img } diff --git a/screenNtscFilter.go b/screenNtscFilter.go index fe4b1c4..b2d969d 100644 --- a/screenNtscFilter.go +++ b/screenNtscFilter.go @@ -62,7 +62,7 @@ func filterNTSCColor(blacker bool, in *image.RGBA) *image.RGBA { out := image.NewRGBA(size) for y := b.Min.Y; y < b.Max.Y; y++ { - // We store the last four bits. We start will 0000 + // We store the last four bits. We start with 0000 v := 0 for x := b.Min.X; x < b.Dx(); x++ { cIn := in.At(x, y)