Show alt text character map

This commit is contained in:
Iván Izaguirre 2021-03-02 00:19:18 +01:00
parent e49bef397b
commit 9d040fa58b
9 changed files with 41 additions and 28 deletions

View File

@ -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. - 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. - 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. - 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 - F11: Toggle on and off the trace to console of the CPU execution
- F12: Save a screen snapshot to a file `snapshot.png` - F12: Save a screen snapshot to a file `snapshot.png`
- Pause: Pause the emulation - Pause: Pause the emulation

View File

@ -91,7 +91,7 @@ func sdlRun(a *izapple2.Apple2) {
if !a.IsPaused() { if !a.IsPaused() {
var img *image.RGBA var img *image.RGBA
if kp.showCharGen { if kp.showCharGen {
img = screen.SnapshotCharacterGenerator(a) img = screen.SnapshotCharacterGenerator(a, kp.showAltText)
window.SetTitle(fmt.Sprintf("%v character map", a.Name)) window.SetTitle(fmt.Sprintf("%v character map", a.Name))
} else if kp.showPages { } else if kp.showPages {
img = screen.SnapshotParts(a, screen.ScreenModeNTSC) img = screen.SnapshotParts(a, screen.ScreenModeNTSC)

View File

@ -14,6 +14,7 @@ type sdlKeyboard struct {
showPages bool showPages bool
showCharGen bool showCharGen bool
showAltText bool
} }
func newSDLKeyBoard(a *izapple2.Apple2) *sdlKeyboard { func newSDLKeyBoard(a *izapple2.Apple2) *sdlKeyboard {
@ -43,6 +44,7 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) {
key := keyEvent.Keysym key := keyEvent.Keysym
ctrl := key.Mod&sdl.KMOD_CTRL != 0 ctrl := key.Mod&sdl.KMOD_CTRL != 0
shift := key.Mod&sdl.KMOD_SHIFT != 0
if ctrl { if ctrl {
if key.Sym >= 'a' && key.Sym <= 'z' { if key.Sym >= 'a' && key.Sym <= 'z' {
@ -103,6 +105,8 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) {
case sdl.K_F10: case sdl.K_F10:
if ctrl { if ctrl {
k.showCharGen = !k.showCharGen k.showCharGen = !k.showCharGen
} else if shift {
k.showAltText = !k.showAltText
} else { } else {
k.a.SendCommand(izapple2.CommandNextCharGenPage) k.a.SendCommand(izapple2.CommandNextCharGenPage)
} }

View File

@ -40,6 +40,7 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB
videoBase := videoMode & VideoBaseMask videoBase := videoMode & VideoBaseMask
mixMode := videoMode & VideoMixTextMask mixMode := videoMode & VideoMixTextMask
isSecondPage := (videoMode & VideoSecondPage) != 0 isSecondPage := (videoMode & VideoSecondPage) != 0
isAltText := (videoMode & VideoAltText) != 0
var lightColor color.Color = color.White var lightColor color.Color = color.White
if screenMode == ScreenModeGreen { if screenMode == ScreenModeGreen {
@ -53,13 +54,13 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB
var ntscMask *image.Alpha var ntscMask *image.Alpha
switch videoBase { switch videoBase {
case VideoText40: case VideoText40:
snap = snapshotText40(vs, isSecondPage, lightColor) snap = snapshotText40(vs, isSecondPage, isAltText, lightColor)
applyNTSCFilter = false applyNTSCFilter = false
case VideoText80: case VideoText80:
snap = snapshotText80(vs, isSecondPage, lightColor) snap = snapshotText80(vs, isSecondPage, isAltText, lightColor)
applyNTSCFilter = false applyNTSCFilter = false
case VideoText40RGB: case VideoText40RGB:
snap = snapshotText40RGB(vs, isSecondPage) snap = snapshotText40RGB(vs, isSecondPage, isAltText)
applyNTSCFilter = false applyNTSCFilter = false
case VideoGR: case VideoGR:
snap = snapshotLoRes(vs, isSecondPage, lightColor) snap = snapshotLoRes(vs, isSecondPage, lightColor)
@ -90,11 +91,11 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB
applyNTSCFilter := screenMode != ScreenModeGreen applyNTSCFilter := screenMode != ScreenModeGreen
switch mixMode { switch mixMode {
case VideoMixText40: case VideoMixText40:
bottom = snapshotText40(vs, isSecondPage, lightColor) bottom = snapshotText40(vs, isSecondPage, isAltText, lightColor)
case VideoMixText80: case VideoMixText80:
bottom = snapshotText80(vs, isSecondPage, lightColor) bottom = snapshotText80(vs, isSecondPage, isAltText, lightColor)
case VideoMixText40RGB: case VideoMixText40RGB:
bottom = snapshotText40RGB(vs, isSecondPage) bottom = snapshotText40RGB(vs, isSecondPage, isAltText)
applyNTSCFilter = false applyNTSCFilter = false
} }
if applyNTSCFilter { if applyNTSCFilter {

View File

@ -79,6 +79,10 @@ func VideoModeName(vs VideoSource) string {
name += "-PAGE2" name += "-PAGE2"
} }
if (videoMode & VideoAltText) != 0 {
name += "-ALT"
}
switch mixMode { switch mixMode {
case VideoMixText40: case VideoMixText40:
name += "-MIX40" name += "-MIX40"
@ -130,7 +134,7 @@ func doubleWidthFilter(in *image.RGBA) *image.RGBA {
} }
// SnapshotCharacterGenerator shows the current character set // 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) text := make([]uint8, textLines*text40Columns)
for l := 0; l < textLines; l++ { for l := 0; l < textLines; l++ {
for c := 0; c < text40Columns; c++ { 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)
} }

View File

@ -114,7 +114,7 @@ func (ts *TestScenario) GetVideoMemory(secondPage bool, ext bool) []uint8 {
} }
// GetCharacterPixel returns the pixel as output by the character generator // 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 // We don't have a character generator. We will return a square or blank for spaces
if char&0x3f == 0x20 { if char&0x3f == 0x20 {
return false // Space char return false // Space char

View File

@ -3,6 +3,7 @@ package screen
import ( import (
"image" "image"
"image/color" "image/color"
"time"
) )
const ( const (
@ -12,25 +13,25 @@ const (
textLines = 24 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) 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) 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) text := getTextFromMemory(vs, isSecondPage, false)
colorMap := getTextFromMemory(vs, isSecondPage, true) 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 { func snapshotText40RGBColors(vs VideoSource, isSecondPage bool) *image.RGBA {
colorMap := getTextFromMemory(vs, isSecondPage, true) 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 { 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 columns := len(text) / textLines
if text == nil { if text == nil {
columns = text40Columns 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 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 var colour color.Color
if colorMap != nil { if colorMap != nil {

View File

@ -28,6 +28,7 @@ const (
const ( const (
VideoModifiersMask uint16 = 0xf000 VideoModifiersMask uint16 = 0xf000
VideoSecondPage uint16 = 0x1000 VideoSecondPage uint16 = 0x1000
VideoAltText uint16 = 0x2000
) )
// VideoSource provides the info to build the video output // 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 returns a slice to the video memory pages
GetVideoMemory(secondPage bool, ext bool) []uint8 GetVideoMemory(secondPage bool, ext bool) []uint8
// GetCharactePixel returns the pixel as output by the character generator // 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 returns a slice to the SHR video memory
GetSuperVideoMemory() []uint8 GetSuperVideoMemory() []uint8
} }

View File

@ -1,8 +1,6 @@
package izapple2 package izapple2
import ( import (
"time"
"github.com/ivanizag/izapple2/screen" "github.com/ivanizag/izapple2/screen"
) )
@ -34,6 +32,8 @@ func (a *Apple2) GetCurrentVideoMode() uint16 {
isRGB160Mode := isDoubleResMode && rgbFlag1 && !rgbFlag2 isRGB160Mode := isDoubleResMode && rgbFlag1 && !rgbFlag2
isMixMode := a.io.isSoftSwitchActive(ioFlagMixed) isMixMode := a.io.isSoftSwitchActive(ioFlagMixed)
isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active
isAltText := a.isApple2e && a.io.isSoftSwitchActive(ioFlagAltChar)
var mode uint16 var mode uint16
if isSuperHighResMode { if isSuperHighResMode {
@ -76,10 +76,12 @@ func (a *Apple2) GetCurrentVideoMode() uint16 {
mode |= screen.VideoMixText40 mode |= screen.VideoMixText40
} }
} }
isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active
if isSecondPage { if isSecondPage {
mode |= screen.VideoSecondPage mode |= screen.VideoSecondPage
} }
if isAltText {
mode |= screen.VideoAltText
}
return mode return mode
} }
@ -111,11 +113,7 @@ func (a *Apple2) GetSuperVideoMemory() []uint8 {
} }
// GetCharacterPixel returns the pixel as output by the character generator // GetCharacterPixel returns the pixel as output by the character generator
func (a *Apple2) GetCharacterPixel(char uint8, rowInChar int, colInChar int) bool { func (a *Apple2) GetCharacterPixel(char uint8, rowInChar int, colInChar int, isAltText bool, isFlashedFrame bool) bool {
// Flash mode is 2Hz (host time)
isFlashedFrame := time.Now().Nanosecond() > (1 * 1000 * 1000 * 1000 / 2)
isAltText := a.io.isSoftSwitchActive(ioFlagAltChar)
var pixel bool var pixel bool
if a.isApple2e { if a.isApple2e {
vid6 := (char & 0x40) != 0 vid6 := (char & 0x40) != 0