diff --git a/README.md b/README.md index dee5c12..fdfc676 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ Line: - F5: Toggle speed between real and fastest - Ctrl-F5: Show current speed in Mhz - F6: Toggle between NTSC color TV and green phosphor monochrome monitor +- Ctrl-F6: Show a split screen with the views for NTSC color TV, monochrome monitor, HGR page 1 and HGR page 2. - F7: Save current state to disk (incomplete) - F8: Restore state from disk (incomplete) - F10: Cycle character generator codepages. Only if the character generator ROM has more than one 2Kb page. diff --git a/apple2sdl/main.go b/apple2sdl/main.go index 051eb68..f3a7acc 100644 --- a/apple2sdl/main.go +++ b/apple2sdl/main.go @@ -1,6 +1,7 @@ package main import ( + "image" "unsafe" "github.com/ivanizag/apple2" @@ -77,7 +78,12 @@ func SDLRun(a *apple2.Apple2) { } if !a.IsPaused() { - img := apple2.Snapshot(a) + var img *image.RGBA + if kp.showPages { + img = apple2.SnapshotHGRModes(a) + } else { + img = apple2.Snapshot(a) + } if img != nil { surface, err := sdl.CreateRGBSurfaceFrom(unsafe.Pointer(&img.Pix[0]), int32(img.Bounds().Dx()), int32(img.Bounds().Dy()), diff --git a/apple2sdl/sdlKeyboard.go b/apple2sdl/sdlKeyboard.go index 6aca3eb..2a9bf46 100644 --- a/apple2sdl/sdlKeyboard.go +++ b/apple2sdl/sdlKeyboard.go @@ -11,6 +11,8 @@ import ( type sdlKeyboard struct { keyChannel chan uint8 a *apple2.Apple2 + + showPages bool } func newSDLKeyBoard(a *apple2.Apple2) *sdlKeyboard { @@ -105,7 +107,11 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) { k.a.SendCommand(apple2.CommandToggleSpeed) } case sdl.K_F6: - k.a.SendCommand(apple2.CommandToggleColor) + if ctrl { + k.showPages = !k.showPages + } else { + k.a.SendCommand(apple2.CommandToggleColor) + } case sdl.K_F7: k.a.SendCommand(apple2.CommandSaveState) case sdl.K_F8: diff --git a/screen.go b/screen.go index 8ce8547..103be25 100644 --- a/screen.go +++ b/screen.go @@ -17,6 +17,10 @@ References: // Snapshot the currently visible screen func Snapshot(a *Apple2) *image.RGBA { + return activeSnapshot(a, false) +} + +func activeSnapshot(a *Apple2, raw bool) *image.RGBA { isColor := a.isColor isTextMode := a.io.isSoftSwitchActive(ioFlagText) isHiResMode := a.io.isSoftSwitchActive(ioFlagHiRes) @@ -56,17 +60,42 @@ func Snapshot(a *Apple2) *image.RGBA { snapText := snapshotTextMode(a, is80Columns, false /*isSecondPage*/, true /*isMixMode*/, lightColor) snap = mixSnapshots(snap, snapText) } - if isColor { + if isColor && !raw { snap = filterNTSCColor(false /*blacker*/, snap) } } - if !isSuperHighResMode { + if !raw && !isSuperHighResMode { snap = linesSeparatedFilter(snap) } return snap } +// SnapshotAllModes to get all modes mixed +func SnapshotHGRModes(a *Apple2) *image.RGBA { + bwSnap := activeSnapshot(a, true) + if bwSnap.Bounds().Dx() == hiResWidth { + bwSnap = doubleWidthFilter(bwSnap) + } + colorSnap := filterNTSCColor(false, bwSnap) + page1Snap := filterNTSCColor(false /*blacker*/, snapshotHiResModeMono(a, false /*2nd page*/, false /*mix*/, color.White)) // HGR 1 + page2Snap := filterNTSCColor(false /*blacker*/, snapshotHiResModeMono(a, true /*2nd page*/, false /*mix*/, color.White)) // HGR 2 + + size := image.Rect(0, 0, hiResWidth*4, hiResHeight*2) + out := image.NewRGBA(size) + + for y := 0; y < hiResHeight; y++ { + for x := 0; x < hiResWidth*2; x++ { + out.Set(x, y, colorSnap.At(x, y)) + out.Set(x+hiResWidth*2, y, bwSnap.At(x, y)) + out.Set(x, y+hiResHeight, page1Snap.At(x, y)) + out.Set(x+hiResWidth*2, y+hiResHeight, page2Snap.At(x, y)) + } + } + + return out +} + func mixSnapshots(top, bottom *image.RGBA) *image.RGBA { topBounds := top.Bounds() topWidth := topBounds.Dx() @@ -89,7 +118,7 @@ func mixSnapshots(top, bottom *image.RGBA) *image.RGBA { } } - // Copy bottom, applyng the factor + // Copy bottom, applying the factor for y := bottomBounds.Min.Y; y < bottomBounds.Max.Y; y++ { for x := bottomBounds.Min.X; x < bottomBounds.Max.X; x++ { c := bottom.At(x, y) @@ -149,3 +178,17 @@ func linesSeparatedFilter(in *image.RGBA) *image.RGBA { } return out } + +func doubleWidthFilter(in *image.RGBA) *image.RGBA { + b := in.Bounds() + size := image.Rect(0, 0, 2*b.Dx(), b.Dy()) + out := image.NewRGBA(size) + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + c := in.At(x, y) + out.Set(2*x, y, c) + out.Set(2*x+1, y, c) + } + } + return out +}