2020-10-16 18:41:34 +00:00
|
|
|
package izapple2
|
|
|
|
|
|
|
|
import (
|
2021-03-14 17:46:52 +00:00
|
|
|
"image"
|
|
|
|
"image/color"
|
|
|
|
|
2020-10-16 18:41:34 +00:00
|
|
|
"github.com/ivanizag/izapple2/screen"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
textPage1Address = uint16(0x0400)
|
|
|
|
textPage2Address = uint16(0x0800)
|
|
|
|
textPageSize = uint16(0x0400)
|
|
|
|
hiResPage1Address = uint16(0x2000)
|
|
|
|
hiResPage2Address = uint16(0x4000)
|
|
|
|
hiResPageSize = uint16(0x2000)
|
|
|
|
shResPageAddress = uint16(0x2000)
|
|
|
|
shResPageSize = uint16(0x8000)
|
|
|
|
)
|
|
|
|
|
2024-07-28 20:37:48 +00:00
|
|
|
type video struct {
|
|
|
|
a *Apple2
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ screen.VideoSource = (*video)(nil)
|
|
|
|
|
|
|
|
func newVideo(a *Apple2) *video {
|
|
|
|
return &video{a}
|
|
|
|
}
|
|
|
|
|
2020-10-16 18:41:34 +00:00
|
|
|
// GetCurrentVideoMode returns the active video mode
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) GetCurrentVideoMode() uint32 {
|
|
|
|
isTextMode := v.a.io.isSoftSwitchActive(ioFlagText)
|
|
|
|
isHiResMode := v.a.io.isSoftSwitchActive(ioFlagHiRes)
|
|
|
|
is80Columns := v.a.io.isSoftSwitchActive(ioFlag80Col)
|
|
|
|
isStore80Active := v.a.mmu.store80Active
|
|
|
|
isDoubleResMode := !isTextMode && is80Columns && !v.a.io.isSoftSwitchActive(ioFlagAnnunciator3)
|
|
|
|
isSuperHighResMode := v.a.io.isSoftSwitchActive(ioDataNewVideo)
|
|
|
|
isVidex := v.a.softVideoSwitch.isActive()
|
|
|
|
|
|
|
|
isRGBCard := v.a.io.isSoftSwitchActive(ioFlagRGBCardActive)
|
|
|
|
rgbFlag1 := v.a.io.isSoftSwitchActive(ioFlag1RGBCard)
|
|
|
|
rgbFlag2 := v.a.io.isSoftSwitchActive(ioFlag2RGBCard)
|
2020-10-16 18:41:34 +00:00
|
|
|
isMono560 := isDoubleResMode && !rgbFlag1 && !rgbFlag2
|
|
|
|
isRGBMixMode := isDoubleResMode && !rgbFlag1 && rgbFlag2
|
|
|
|
isRGB160Mode := isDoubleResMode && rgbFlag1 && !rgbFlag2
|
|
|
|
|
2024-07-28 20:37:48 +00:00
|
|
|
isMixMode := v.a.io.isSoftSwitchActive(ioFlagMixed)
|
|
|
|
isSecondPage := v.a.io.isSoftSwitchActive(ioFlagSecondPage) && !v.a.mmu.store80Active
|
|
|
|
isAltText := v.a.isApple2e && v.a.io.isSoftSwitchActive(ioFlagAltChar)
|
2020-10-16 18:41:34 +00:00
|
|
|
|
2024-07-28 20:37:48 +00:00
|
|
|
var mode uint32
|
2020-10-16 18:41:34 +00:00
|
|
|
if isSuperHighResMode {
|
|
|
|
mode = screen.VideoSHR
|
|
|
|
isMixMode = false
|
2021-03-14 17:46:52 +00:00
|
|
|
} else if isVidex {
|
|
|
|
mode = screen.VideoVidex
|
|
|
|
isMixMode = false
|
2020-10-16 18:41:34 +00:00
|
|
|
} else if isTextMode {
|
|
|
|
if is80Columns {
|
|
|
|
mode = screen.VideoText80
|
|
|
|
} else if isRGBCard && isStore80Active {
|
|
|
|
mode = screen.VideoText40RGB
|
|
|
|
} else {
|
|
|
|
mode = screen.VideoText40
|
|
|
|
}
|
|
|
|
isMixMode = false
|
|
|
|
} else if isHiResMode {
|
|
|
|
if !isDoubleResMode {
|
|
|
|
mode = screen.VideoHGR
|
|
|
|
} else if isMono560 {
|
|
|
|
mode = screen.VideoMono560
|
|
|
|
} else if isRGBMixMode {
|
|
|
|
mode = screen.VideoRGBMix
|
|
|
|
} else if isRGB160Mode {
|
|
|
|
mode = screen.VideoRGB160
|
|
|
|
} else {
|
|
|
|
mode = screen.VideoDHGR
|
|
|
|
}
|
|
|
|
} else if isDoubleResMode {
|
|
|
|
mode = screen.VideoDGR
|
|
|
|
} else {
|
|
|
|
mode = screen.VideoGR
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modifiers
|
|
|
|
if isMixMode {
|
|
|
|
if is80Columns {
|
|
|
|
mode |= screen.VideoMixText80
|
|
|
|
} else /* if isStore80Active {
|
|
|
|
mode |= screen.VideoMixText40RGB
|
|
|
|
} else */{
|
|
|
|
mode |= screen.VideoMixText40
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isSecondPage {
|
|
|
|
mode |= screen.VideoSecondPage
|
|
|
|
}
|
2021-03-01 23:19:18 +00:00
|
|
|
if isAltText {
|
|
|
|
mode |= screen.VideoAltText
|
|
|
|
}
|
2024-01-25 17:42:43 +00:00
|
|
|
if isRGBCard {
|
|
|
|
mode |= screen.VideoRGBCard
|
|
|
|
}
|
2024-07-28 20:37:48 +00:00
|
|
|
if v.a.isFourColors {
|
2024-02-08 19:24:37 +00:00
|
|
|
mode |= screen.VideoFourColors
|
|
|
|
}
|
2020-10-16 18:41:34 +00:00
|
|
|
|
|
|
|
return mode
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTextMemory returns a slice to the text memory pages
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) GetTextMemory(secondPage bool, ext bool) []uint8 {
|
|
|
|
mem := v.a.mmu.getVideoRAM(ext)
|
2020-10-16 18:41:34 +00:00
|
|
|
addressStart := textPage1Address
|
|
|
|
if secondPage {
|
|
|
|
addressStart = textPage2Address
|
|
|
|
}
|
|
|
|
return mem.subRange(addressStart, addressStart+textPageSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetVideoMemory returns a slice to the video memory pages
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) GetVideoMemory(secondPage bool, ext bool) []uint8 {
|
|
|
|
mem := v.a.mmu.getVideoRAM(ext)
|
2020-10-16 18:41:34 +00:00
|
|
|
addressStart := hiResPage1Address
|
|
|
|
if secondPage {
|
|
|
|
addressStart = hiResPage2Address
|
|
|
|
}
|
|
|
|
return mem.subRange(addressStart, addressStart+hiResPageSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSuperVideoMemory returns a slice to the SHR video memory
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) GetSuperVideoMemory() []uint8 {
|
|
|
|
mem := v.a.mmu.getVideoRAM(true)
|
2020-10-16 18:41:34 +00:00
|
|
|
return mem.subRange(shResPageAddress, shResPageAddress+shResPageSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCharacterPixel returns the pixel as output by the character generator
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) GetCharacterPixel(char uint8, rowInChar int, colInChar int, isAltText bool, isFlashedFrame bool) bool {
|
2020-10-16 18:41:34 +00:00
|
|
|
var pixel bool
|
2024-07-28 20:37:48 +00:00
|
|
|
if v.a.isApple2e {
|
2020-10-16 18:41:34 +00:00
|
|
|
vid6 := (char & 0x40) != 0
|
|
|
|
vid7 := (char & 0x80) != 0
|
|
|
|
char := char & 0x3f
|
|
|
|
if vid6 && (vid7 || isAltText) {
|
|
|
|
char += 0x40
|
|
|
|
}
|
|
|
|
if vid7 || (vid6 && isFlashedFrame && !isAltText) {
|
|
|
|
char += 0x80
|
|
|
|
}
|
2024-07-28 20:37:48 +00:00
|
|
|
pixel = !v.a.cg.getPixel(char, rowInChar, colInChar)
|
2020-10-16 18:41:34 +00:00
|
|
|
} else {
|
2024-07-28 20:37:48 +00:00
|
|
|
pixel = v.a.cg.getPixel(char, rowInChar, colInChar)
|
2020-10-16 18:41:34 +00:00
|
|
|
topBits := char >> 6
|
|
|
|
isInverse := topBits == 0
|
|
|
|
isFlash := topBits == 1
|
|
|
|
|
|
|
|
pixel = pixel != (isInverse || (isFlash && isFlashedFrame))
|
|
|
|
}
|
|
|
|
return pixel
|
|
|
|
}
|
|
|
|
|
2021-03-14 17:46:52 +00:00
|
|
|
// GetCardImage returns an image provided by a card, like the videx card
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) GetCardImage(light color.Color) *image.RGBA {
|
|
|
|
return v.a.softVideoSwitch.BuildAlternateImage(light)
|
2021-03-14 17:46:52 +00:00
|
|
|
}
|
|
|
|
|
2024-07-21 21:28:31 +00:00
|
|
|
// SupportsLowercase returns true if the video source supports lowercase
|
2024-07-28 20:37:48 +00:00
|
|
|
func (v *video) SupportsLowercase() bool {
|
|
|
|
return v.a.hasLowerCase
|
2024-07-21 21:28:31 +00:00
|
|
|
}
|
|
|
|
|
2020-10-16 18:41:34 +00:00
|
|
|
// DumpTextModeAnsi returns the text mode contents using ANSI escape codes for reverse and flash
|
|
|
|
func DumpTextModeAnsi(a *Apple2) string {
|
|
|
|
is80Columns := a.io.isSoftSwitchActive(ioFlag80Col)
|
|
|
|
isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active
|
|
|
|
isAltText := a.isApple2e && a.io.isSoftSwitchActive(ioFlagAltChar)
|
2024-07-28 20:37:48 +00:00
|
|
|
supportsLowercase := a.hasLowerCase
|
|
|
|
return screen.RenderTextModeAnsi(a.video, is80Columns, isSecondPage, isAltText, supportsLowercase, false)
|
2020-10-16 18:41:34 +00:00
|
|
|
}
|