izapple2/video.go

181 lines
5.0 KiB
Go
Raw Normal View History

2020-10-16 18:41:34 +00:00
package izapple2
import (
"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)
)
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
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
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
var mode uint32
2020-10-16 18:41:34 +00:00
if isSuperHighResMode {
mode = screen.VideoSHR
isMixMode = false
} 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
}
if isRGBCard {
mode |= screen.VideoRGBCard
}
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
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
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
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
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
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
}
pixel = !v.a.cg.getPixel(char, rowInChar, colInChar)
2020-10-16 18:41:34 +00:00
} else {
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
}
// GetCardImage returns an image provided by a card, like the videx card
func (v *video) GetCardImage(light color.Color) *image.RGBA {
return v.a.softVideoSwitch.BuildAlternateImage(light)
}
// SupportsLowercase returns true if the video source supports lowercase
func (v *video) SupportsLowercase() bool {
return v.a.hasLowerCase
}
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)
supportsLowercase := a.hasLowerCase
return screen.RenderTextModeAnsi(a.video, is80Columns, isSecondPage, isAltText, supportsLowercase, false)
2020-10-16 18:41:34 +00:00
}