diff --git a/README.md b/README.md index 134f3eb..ebbbbe4 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Line: - F7: Show the video mode and a split screen with the views for NTSC color TV, page 1, page 2 and extra info. - F10: Cycle character generator code pages. Only if the character generator ROM has more than one 2Kb page. - Ctrl-F10: Show the charater map for the current character generator page. +- Shift-F10: When showing the character map, use altText. - F11: Toggle on and off the trace to console of the CPU execution - F12: Save a screen snapshot to a file `snapshot.png` - Pause: Pause the emulation diff --git a/frontend/a2sdl/main.go b/frontend/a2sdl/main.go index f51d88a..c5b6de6 100644 --- a/frontend/a2sdl/main.go +++ b/frontend/a2sdl/main.go @@ -91,7 +91,7 @@ func sdlRun(a *izapple2.Apple2) { if !a.IsPaused() { var img *image.RGBA if kp.showCharGen { - img = screen.SnapshotCharacterGenerator(a) + img = screen.SnapshotCharacterGenerator(a, kp.showAltText) window.SetTitle(fmt.Sprintf("%v character map", a.Name)) } else if kp.showPages { img = screen.SnapshotParts(a, screen.ScreenModeNTSC) diff --git a/frontend/a2sdl/sdlKeyboard.go b/frontend/a2sdl/sdlKeyboard.go index 7aca842..d9129c6 100644 --- a/frontend/a2sdl/sdlKeyboard.go +++ b/frontend/a2sdl/sdlKeyboard.go @@ -14,6 +14,7 @@ type sdlKeyboard struct { showPages bool showCharGen bool + showAltText bool } func newSDLKeyBoard(a *izapple2.Apple2) *sdlKeyboard { @@ -43,6 +44,7 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) { key := keyEvent.Keysym ctrl := key.Mod&sdl.KMOD_CTRL != 0 + shift := key.Mod&sdl.KMOD_SHIFT != 0 if ctrl { if key.Sym >= 'a' && key.Sym <= 'z' { @@ -103,6 +105,8 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) { case sdl.K_F10: if ctrl { k.showCharGen = !k.showCharGen + } else if shift { + k.showAltText = !k.showAltText } else { k.a.SendCommand(izapple2.CommandNextCharGenPage) } diff --git a/screen/snapshots.go b/screen/snapshots.go index 25041fb..e6dd26e 100644 --- a/screen/snapshots.go +++ b/screen/snapshots.go @@ -40,6 +40,7 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB videoBase := videoMode & VideoBaseMask mixMode := videoMode & VideoMixTextMask isSecondPage := (videoMode & VideoSecondPage) != 0 + isAltText := (videoMode & VideoAltText) != 0 var lightColor color.Color = color.White if screenMode == ScreenModeGreen { @@ -53,13 +54,13 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB var ntscMask *image.Alpha switch videoBase { case VideoText40: - snap = snapshotText40(vs, isSecondPage, lightColor) + snap = snapshotText40(vs, isSecondPage, isAltText, lightColor) applyNTSCFilter = false case VideoText80: - snap = snapshotText80(vs, isSecondPage, lightColor) + snap = snapshotText80(vs, isSecondPage, isAltText, lightColor) applyNTSCFilter = false case VideoText40RGB: - snap = snapshotText40RGB(vs, isSecondPage) + snap = snapshotText40RGB(vs, isSecondPage, isAltText) applyNTSCFilter = false case VideoGR: snap = snapshotLoRes(vs, isSecondPage, lightColor) @@ -90,11 +91,11 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB applyNTSCFilter := screenMode != ScreenModeGreen switch mixMode { case VideoMixText40: - bottom = snapshotText40(vs, isSecondPage, lightColor) + bottom = snapshotText40(vs, isSecondPage, isAltText, lightColor) case VideoMixText80: - bottom = snapshotText80(vs, isSecondPage, lightColor) + bottom = snapshotText80(vs, isSecondPage, isAltText, lightColor) case VideoMixText40RGB: - bottom = snapshotText40RGB(vs, isSecondPage) + bottom = snapshotText40RGB(vs, isSecondPage, isAltText) applyNTSCFilter = false } if applyNTSCFilter { diff --git a/screen/snapshotsDebug.go b/screen/snapshotsDebug.go index d095770..8ecd7e6 100644 --- a/screen/snapshotsDebug.go +++ b/screen/snapshotsDebug.go @@ -79,6 +79,10 @@ func VideoModeName(vs VideoSource) string { name += "-PAGE2" } + if (videoMode & VideoAltText) != 0 { + name += "-ALT" + } + switch mixMode { case VideoMixText40: name += "-MIX40" @@ -130,7 +134,7 @@ func doubleWidthFilter(in *image.RGBA) *image.RGBA { } // SnapshotCharacterGenerator shows the current character set -func SnapshotCharacterGenerator(vs VideoSource) *image.RGBA { +func SnapshotCharacterGenerator(vs VideoSource, isAltText bool) *image.RGBA { text := make([]uint8, textLines*text40Columns) for l := 0; l < textLines; l++ { for c := 0; c < text40Columns; c++ { @@ -144,5 +148,5 @@ func SnapshotCharacterGenerator(vs VideoSource) *image.RGBA { } } - return renderText(vs, text, nil, color.White) + return renderText(vs, text, isAltText, nil, color.White) } diff --git a/screen/testScenarios.go b/screen/testScenarios.go index 42a7185..0f3c011 100644 --- a/screen/testScenarios.go +++ b/screen/testScenarios.go @@ -114,7 +114,7 @@ func (ts *TestScenario) GetVideoMemory(secondPage bool, ext bool) []uint8 { } // GetCharacterPixel returns the pixel as output by the character generator -func (ts *TestScenario) GetCharacterPixel(char uint8, rowInChar int, colInChar int) bool { +func (ts *TestScenario) GetCharacterPixel(char uint8, rowInChar int, colInChar int, isAltText bool, isFlashedFrame bool) bool { // We don't have a character generator. We will return a square or blank for spaces if char&0x3f == 0x20 { return false // Space char diff --git a/screen/text.go b/screen/text.go index 4163e27..dd888d0 100644 --- a/screen/text.go +++ b/screen/text.go @@ -3,6 +3,7 @@ package screen import ( "image" "image/color" + "time" ) const ( @@ -12,25 +13,25 @@ const ( textLines = 24 ) -func snapshotText40(vs VideoSource, isSecondPage bool, light color.Color) *image.RGBA { +func snapshotText40(vs VideoSource, isSecondPage bool, isAltText bool, light color.Color) *image.RGBA { text := getTextFromMemory(vs, isSecondPage, false) - return renderText(vs, text, nil /*colorMap*/, light) + return renderText(vs, text, isAltText, nil /*colorMap*/, light) } -func snapshotText80(vs VideoSource, isSecondPage bool, light color.Color) *image.RGBA { +func snapshotText80(vs VideoSource, isSecondPage bool, isAltText bool, light color.Color) *image.RGBA { text := getText80FromMemory(vs, isSecondPage) - return renderText(vs, text, nil /*colorMap*/, light) + return renderText(vs, text, isAltText, nil /*colorMap*/, light) } -func snapshotText40RGB(vs VideoSource, isSecondPage bool) *image.RGBA { +func snapshotText40RGB(vs VideoSource, isSecondPage bool, isAltText bool) *image.RGBA { text := getTextFromMemory(vs, isSecondPage, false) colorMap := getTextFromMemory(vs, isSecondPage, true) - return renderText(vs, text, colorMap, nil) + return renderText(vs, text, isAltText, colorMap, nil) } func snapshotText40RGBColors(vs VideoSource, isSecondPage bool) *image.RGBA { colorMap := getTextFromMemory(vs, isSecondPage, true) - return renderText(vs, nil /*text*/, colorMap, nil) + return renderText(vs, nil /*text*/, false, colorMap, nil) } func getText80FromMemory(vs VideoSource, isSecondPage bool) []uint8 { @@ -77,7 +78,10 @@ func getRGBTextColor(pixel bool, colorKey uint8) color.Color { } -func renderText(vs VideoSource, text []uint8, colorMap []uint8, light color.Color) *image.RGBA { +func renderText(vs VideoSource, text []uint8, isAltText bool, colorMap []uint8, light color.Color) *image.RGBA { + // Flash mode is 2Hz (host time) + isFlashedFrame := time.Now().Nanosecond() > (1 * 1000 * 1000 * 1000 / 2) + columns := len(text) / textLines if text == nil { columns = text40Columns @@ -102,7 +106,7 @@ func renderText(vs VideoSource, text []uint8, colorMap []uint8, light color.Colo char = 79 + 128 // Debug screen filed with O } - pixel := vs.GetCharacterPixel(char, rowInChar, colInChar) + pixel := vs.GetCharacterPixel(char, rowInChar, colInChar, isAltText, isFlashedFrame) var colour color.Color if colorMap != nil { diff --git a/screen/videoSource.go b/screen/videoSource.go index a772d42..c423b63 100644 --- a/screen/videoSource.go +++ b/screen/videoSource.go @@ -28,6 +28,7 @@ const ( const ( VideoModifiersMask uint16 = 0xf000 VideoSecondPage uint16 = 0x1000 + VideoAltText uint16 = 0x2000 ) // VideoSource provides the info to build the video output @@ -39,7 +40,7 @@ type VideoSource interface { // GetVideoMemory returns a slice to the video memory pages GetVideoMemory(secondPage bool, ext bool) []uint8 // GetCharactePixel returns the pixel as output by the character generator - GetCharacterPixel(char uint8, rowInChar int, colInChar int) bool + GetCharacterPixel(char uint8, rowInChar int, colInChar int, isAltText bool, isFlashedFrame bool) bool // GetSuperVideoMemory returns a slice to the SHR video memory GetSuperVideoMemory() []uint8 } diff --git a/videoSourceImpl.go b/videoSourceImpl.go index a2e4545..58bb6ae 100644 --- a/videoSourceImpl.go +++ b/videoSourceImpl.go @@ -1,8 +1,6 @@ package izapple2 import ( - "time" - "github.com/ivanizag/izapple2/screen" ) @@ -34,6 +32,8 @@ func (a *Apple2) GetCurrentVideoMode() uint16 { isRGB160Mode := isDoubleResMode && rgbFlag1 && !rgbFlag2 isMixMode := a.io.isSoftSwitchActive(ioFlagMixed) + isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active + isAltText := a.isApple2e && a.io.isSoftSwitchActive(ioFlagAltChar) var mode uint16 if isSuperHighResMode { @@ -76,10 +76,12 @@ func (a *Apple2) GetCurrentVideoMode() uint16 { mode |= screen.VideoMixText40 } } - isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active if isSecondPage { mode |= screen.VideoSecondPage } + if isAltText { + mode |= screen.VideoAltText + } return mode } @@ -111,11 +113,7 @@ func (a *Apple2) GetSuperVideoMemory() []uint8 { } // GetCharacterPixel returns the pixel as output by the character generator -func (a *Apple2) GetCharacterPixel(char uint8, rowInChar int, colInChar int) bool { - // Flash mode is 2Hz (host time) - isFlashedFrame := time.Now().Nanosecond() > (1 * 1000 * 1000 * 1000 / 2) - isAltText := a.io.isSoftSwitchActive(ioFlagAltChar) - +func (a *Apple2) GetCharacterPixel(char uint8, rowInChar int, colInChar int, isAltText bool, isFlashedFrame bool) bool { var pixel bool if a.isApple2e { vid6 := (char & 0x40) != 0