SuperHiRes video mode

This commit is contained in:
Ivan Izaguirre 2019-11-11 22:58:42 +01:00 committed by Iván Izaguirre
parent fce719deb8
commit 37e6f3024f
7 changed files with 190 additions and 43 deletions

View File

@ -5,6 +5,7 @@ Simulates just what is needed to make Total Replay use the GS modes if the VidHD
See:
https://github.com/a2-4am/4cade/blob/master/src/hw.vidhd.a
http://www.applelogic.org/files/GSHARDWAREREF.pdf, page 89
*/
type cardVidHD struct {
@ -21,6 +22,20 @@ func buildVidHDRom() []uint8 {
return data
}
const (
ioDataNewVideo uint8 = 0x29
)
func (c *cardVidHD) assign(a *Apple2, slot int) {
// The softswitches are outside the card reverded ss
a.io.addSoftSwitchR(0x22, notImplementedSoftSwitchR, "VIDHD-TBCOLOR")
a.io.addSoftSwitchW(0x22, notImplementedSoftSwitchW, "VIDHD-TBCOLOR")
a.io.addSoftSwitchR(0x29, getStatusSoftSwitch(ioDataNewVideo), "VIDHD-NEWVIDEO")
a.io.addSoftSwitchW(0x29, setStatusSoftSwitch(ioDataNewVideo), "VIDHD-NEWVIDEO")
a.io.addSoftSwitchR(0x34, notImplementedSoftSwitchR, "VIDHD-CLOCKCTL")
a.io.addSoftSwitchW(0x34, notImplementedSoftSwitchW, "VIDHD-CLOCKCTL")
a.io.addSoftSwitchR(0x35, notImplementedSoftSwitchR, "VIDHD-SHADOW")
a.io.addSoftSwitchW(0x35, notImplementedSoftSwitchW, "VIDHD-SHADOW")
c.cardBase.assign(a, slot)
}

View File

@ -24,6 +24,7 @@ func Snapshot(a *Apple2) *image.RGBA {
is80Columns := a.io.isSoftSwitchActive(ioFlag80Col)
isDoubleResMode := !isTextMode && is80Columns && !a.io.isSoftSwitchActive(ioFlagAnnunciator3)
isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active
isSuperHighResMode := a.io.isSoftSwitchActive(ioDataNewVideo)
var lightColor color.Color
if isColor {
@ -36,7 +37,9 @@ func Snapshot(a *Apple2) *image.RGBA {
}
var snap *image.RGBA
if isTextMode {
if isSuperHighResMode { // Has to be first and disables the rest
snap = snapshotSuperHiResMode(a)
} else if isTextMode {
snap = snapshotTextMode(a, is80Columns, isSecondPage, false /*isMixMode*/, lightColor)
} else {
if isHiResMode {
@ -58,7 +61,9 @@ func Snapshot(a *Apple2) *image.RGBA {
}
}
snap = linesSeparatedFilter(snap)
if !isSuperHighResMode {
snap = linesSeparatedFilter(snap)
}
return snap
}

47
screenDoubleHiRes.go Normal file
View File

@ -0,0 +1,47 @@
package apple2
import (
"image"
"image/color"
)
const (
doubleHiResWidth = 2 * hiResWidth
)
func snapshotDoubleHiResModeMono(a *Apple2, isSecondPage bool, mixedMode bool, light color.Color) *image.RGBA {
// As described in "Inside the Apple IIe"
height := hiResHeight
if mixedMode {
height = hiResHeightMixed
}
size := image.Rect(0, 0, doubleHiResWidth, height)
img := image.NewRGBA(size)
for y := 0; y < height; y++ {
lineParts := [][]uint8{
getHiResLine(a, y, isSecondPage, true),
getHiResLine(a, y, isSecondPage, false),
}
x := 0
// For the NTSC filter to work we have to insert an initial black pixel and skip the last one
img.Set(x, y, color.Black)
x++
for iByte := 0; iByte < hiResLineBytes-1; iByte++ {
for iPart := 0; iPart < 2; iPart++ {
b := lineParts[iPart][iByte]
for j := uint(0); j < 7; j++ {
bit := (b >> j) & 1
colour := light
if bit == 0 {
colour = color.Black
}
img.Set(x, y, colour)
x++
}
}
}
}
return img
}

View File

@ -8,7 +8,6 @@ import (
const (
hiResWidth = 280
hiResLineBytes = hiResWidth / 7
doubleHiResWidth = 2 * hiResWidth
hiResHeight = 192
hiResHeightMixed = 160
hiResPage1Address = uint16(0x2000)
@ -72,40 +71,3 @@ func snapshotHiResModeMono(a *Apple2, isSecondPage bool, mixedMode bool, light c
}
return img
}
func snapshotDoubleHiResModeMono(a *Apple2, isSecondPage bool, mixedMode bool, light color.Color) *image.RGBA {
// As described in "Inside the Apple IIe"
height := hiResHeight
if mixedMode {
height = hiResHeightMixed
}
size := image.Rect(0, 0, doubleHiResWidth, height)
img := image.NewRGBA(size)
for y := 0; y < height; y++ {
lineParts := [][]uint8{
getHiResLine(a, y, isSecondPage, true),
getHiResLine(a, y, isSecondPage, false),
}
x := 0
// For the NTSC filter to work we have to insert an initial black pixel and skip the last one
img.Set(x, y, color.Black)
x++
for iByte := 0; iByte < hiResLineBytes-1; iByte++ {
for iPart := 0; iPart < 2; iPart++ {
b := lineParts[iPart][iByte]
for j := uint(0); j < 7; j++ {
bit := (b >> j) & 1
colour := light
if bit == 0 {
colour = color.Black
}
img.Set(x, y, colour)
x++
}
}
}
}
return img
}

97
screenSuperHiRes.go Normal file
View File

@ -0,0 +1,97 @@
package apple2
import (
"image"
"image/color"
)
const (
shrWidth = 640
shrWidthBytes = 640 / 4
shrHeight = 200
palettesCount = 256
shrPixelDataAddress = uint16(0x2000)
shrScanLineControlAddress = uint16(0x9d00)
shrColorPalettesAddress = uint16(0x9e00)
shrColorPalettesAddressEnd = uint16(0xa000)
)
func snapshotSuperHiResMode(a *Apple2) *image.RGBA {
// See "Apple IIGS Hardware Reference", chapter 4, page 91
// http://www.applelogic.org/files/GSHARDWAREREF.pdf
size := image.Rect(0, 0, shrWidth, shrHeight)
img := image.NewRGBA(size)
// Load the palletes
palleteMem := a.mmu.physicalMainRAMAlt.subRange(shrColorPalettesAddress, shrColorPalettesAddressEnd)
colors := make([]color.Color, palettesCount)
iMem := 0
for i := 0; i < palettesCount; i++ {
b0 := palleteMem[iMem]
iMem++
b1 := palleteMem[iMem]
iMem++
red := (b0 & 0x0f) << 4
green := b1 & 0xf0
blue := (b1 & 0x0f) << 4
colors[i] = color.RGBA{red, green, blue, 255}
}
// See "Apple IIGS Hardware Reference", table 4-21
palettesSelectionTable := []uint8{0x4, 0x0, 0xc, 0x8}
// Build the lines
for y := 0; y < shrHeight; y++ {
controlByte := a.mmu.physicalMainRAMAlt.peek(shrScanLineControlAddress + uint16(y))
is640Wide := (controlByte & 0x80) != 0
isColorFill := (controlByte & 0x20) != 0
palleteIndex := (controlByte & 0x0f) << 4
lineAddress := shrPixelDataAddress + uint16(shrWidthBytes*y)
lineBytes := a.mmu.physicalMainRAMAlt.subRange(lineAddress, uint16(lineAddress+shrWidthBytes))
if is640Wide {
// Line is 640 pixels, two bits per pixel
x := 0
for i := 0; i < shrWidthBytes; i++ {
b := lineBytes[i]
for j := 3; j >= 0; j-- {
p := (b >> (uint(j) * 2)) & 0x03
offset := palettesSelectionTable[j]
color := colors[palleteIndex+offset+p]
img.Set(x, y, color)
x++
}
}
} else {
// Line is 320 pixels, two pixels per byte
x := 0
previousColor := uint8(0)
for i := 0; i < shrWidthBytes; i++ {
p0 := (lineBytes[i] & 0xf0) >> 4
if isColorFill && p0 == 0 {
p0 = previousColor
} else {
previousColor = p0
}
p1 := lineBytes[i] & 0x0f
if isColorFill && p1 == 0 {
p1 = previousColor
} else {
previousColor = p1
}
img.Set(x, y, colors[palleteIndex+p0])
img.Set(x+1, y, colors[palleteIndex+p0])
img.Set(x+2, y, colors[palleteIndex+p1])
img.Set(x+3, y, colors[palleteIndex+p1])
x += 4
}
}
}
return img
}

View File

@ -80,6 +80,12 @@ func notImplementedSoftSwitchR(*ioC0Page) uint8 {
func notImplementedSoftSwitchW(*ioC0Page, uint8) {
}
func setStatusSoftSwitch(ioFlag uint8) softSwitchW {
return func(io *ioC0Page, value uint8) {
io.softSwitchesData[ioFlag] = value
}
}
func getStatusSoftSwitch(ioFlag uint8) softSwitchR {
return func(io *ioC0Page) uint8 {
return io.softSwitchesData[ioFlag]

View File

@ -8,7 +8,11 @@ package apple2
const (
ioFlagAltChar uint8 = 0x1E
ioFlag80Col uint8 = 0x1F
// ??? ioVertBlank uin8 = 0x19
)
const (
screenDrawCycles = uint64(12480 + 4550)
screenVertBlankingCycles = uint64(4550)
)
func addApple2ESoftSwitches(io *ioC0Page) {
@ -38,8 +42,19 @@ func addApple2ESoftSwitches(io *ioC0Page) {
return ssFromBool(mmu.lcActiveRead)
}, "BSRREADRAM")
// TOOD:
// VBL or VERTBLANK read on 0x19
io.addSoftSwitchR(0x19, func(_ *ioC0Page) uint8 {
// See "Inside Apple IIe", page 268
// See http://rich12345.tripod.com/aiivideo/vbl.html
// For each screen draw:
// 12480 cycles drawing lines, VERTBLANK = $00
// 4550 cycles doing the return to position (0,0), VERTBLANK = $80
// Vert blank takes 12480 cycles every page redraw
cycles := io.apple2.cpu.GetCycles() % screenDrawCycles
if cycles <= screenVertBlankingCycles {
return ssOn
}
return ssOff
}, "VERTBLANK")
//io.softSwitchesData[ioFlagAltChar] = ssOn // Not sure about this.