mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-12-21 02:32:06 +00:00
SuperHiRes video mode
This commit is contained in:
parent
fce719deb8
commit
37e6f3024f
15
cardVidHD.go
15
cardVidHD.go
@ -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)
|
||||
}
|
||||
|
@ -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
47
screenDoubleHiRes.go
Normal 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
|
||||
}
|
@ -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
97
screenSuperHiRes.go
Normal 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
|
||||
}
|
@ -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]
|
||||
|
@ -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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user