Videx Videoterm 80 columns card support. Videx Soft Video Switch support

This commit is contained in:
Ivan Izaguirre 2021-03-14 18:46:52 +01:00
parent f34b3da510
commit dfb8b1ffb2
20 changed files with 427 additions and 37 deletions

View File

@ -26,6 +26,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
- ThunderClock Plus real time clock
- Apple //e 80 columns with 64Kb extra RAM and optional RGB modes
- No Slot Clock based on the DS1216
- Videx Videoterm 80 column card with the Videx Soft Video Switch (Apple ][+ only)
- Useful cards not emulating a real card
- Bootable Smartport / ProDOS card
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
@ -35,7 +36,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
- Graphic modes:
- Text 40 columns
- Text 80 columns (Apple //e only)
- Text 80 columns (Apple //e and Videx VideoTerm)
- Low-Resolution graphics
- Double-Width Low-Resolution graphics (Apple //e only)
- High-Resolution graphics
@ -231,6 +232,8 @@ Only valid on SDL mode
dump to the console the sofswitch registrations
-vidHDSlot int
slot for the VidHD card, only for //e models. -1 for none (default 2)
-videxCardSlot int
slot for the Videx Videoterm 80 columns card. For pre-2e models. -1 for none (default 3)
```

View File

@ -15,6 +15,7 @@ type Apple2 struct {
io *ioC0Page
cg *CharacterGenerator
cards [8]Card
softVideoSwitch *SoftVideoSwitch
isApple2e bool
commandChannel chan int
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
@ -53,7 +54,7 @@ func (a *Apple2) Run() {
for i := 0; i < cpuSpinLoops; i++ {
// Conditional tracing
//pc, _ := a.cpu.GetPCAndSP()
//a.cpu.SetTrace(pc >= 0x300 && pc <= 0x400)
//a.cpu.SetTrace((pc >= 0xc300 && pc <= 0xc400) || (pc >= 0xc800 && pc <= 0xce00))
// Execution
a.cpu.ExecuteInstruction()

View File

@ -158,6 +158,13 @@ func (a *Apple2) AddMouseCard(slot int) {
a.insertCard(NewCardMouse(), slot)
}
// AddVidexCard inserts a Videx card
func (a *Apple2) AddVidexCard(slot int) {
c := NewCardVidex()
a.insertCard(c, slot)
a.softVideoSwitch = NewSoftVideoSwitch(c)
}
// AddRGBCard inserts an RBG option to the Apple IIe 80 col 64KB card
func (a *Apple2) AddRGBCard() {
setupRGBCard(a)

View File

@ -82,6 +82,10 @@ func MainApple() *Apple2 {
"mouseCardSlot",
4,
"slot for the Mouse card. -1 for none")
videxCardSlot := flag.Int(
"videxCardSlot",
3,
"slot for the Videx Videoterm 80 columns card. For pre-2e models. -1 for none")
nsc := flag.Int(
"nsc",
-1,
@ -204,11 +208,17 @@ func MainApple() *Apple2 {
}
charGenMap = charGenColumnsMapBase64a
*vidHDCardSlot = -1
*videxCardSlot = -1 // The videx firmware crashes the BASE64A, probably by use of ANN0
default:
panic("Model not supported")
}
if a.isApple2e {
// Videx not used on Apple IIe
*videxCardSlot = -1
}
a.cpu.SetTrace(*traceCPU)
// Load ROM if not loaded already
@ -249,6 +259,9 @@ func MainApple() *Apple2 {
if *mouseCardSlot > 0 {
a.AddMouseCard(*mouseCardSlot)
}
if *videxCardSlot > 0 {
a.AddVidexCard(*videxCardSlot)
}
if *smartPortImage != "" {
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)

View File

@ -1,6 +1,8 @@
package izapple2
import "github.com/ivanizag/izapple2/storage"
import (
"github.com/ivanizag/izapple2/storage"
)
// Card represents an Apple II card to be inserted in a slot
type Card interface {
@ -15,9 +17,9 @@ type Card interface {
type cardBase struct {
a *Apple2
name string
romCsxx *memoryRangeROM
romC8xx *memoryRangeROM
romCxxx *memoryRangeROM
romCsxx memoryHandler
romC8xx memoryHandler
romCxxx memoryHandler
slot int
_ssr [16]softSwitchR
@ -54,8 +56,14 @@ func (c *cardBase) loadRom(data []uint8) {
if len(data) == 0x100 {
// Just 256 bytes in Cs00
c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM")
} else if len(data) == 0x400 {
// The file has C800 to CBFF for ROM
// The 256 bytes in Cx00 are copied from the last page in C800-CBFF
// Used on the Videx 80 columns card
c.romCsxx = newMemoryRangeROM(0, data[0x300:], "Slot ROM")
c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM")
} else if len(data) == 0x800 {
// The file has C800 to C8FF
// The file has C800 to CFFF
// The 256 bytes in Cx00 are copied from the first page in C800
c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM")
c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM")

View File

@ -6,6 +6,10 @@ from controlled speed to max speed the emulator can do.
Note: It ends up not being useful for Total Replay as loading from HD is already
very fast. HD blocks are loaded directly on the emulated RAM.
Note that it doesn't intefere with the Apple IIe 80 columns in slot 3. It doesn't
have ROM or slot specific sofswitches.
See:
https://github.com/a2-4am/4cade/blob/master/src/hw.accel.a
http://www.a2heaven.com/webshop/resources/pdf_document/18/82/c.pdf
@ -26,15 +30,9 @@ type CardFastChip struct {
func NewCardFastChip() *CardFastChip {
var c CardFastChip
c.name = "FASTChip IIe Card - limited"
c.loadRom(buildFastChipRom())
return &c
}
func buildFastChipRom() []uint8 {
data := make([]uint8, 256)
return data
}
const (
fastChipUnlockToken = 0x6a
fastChipUnlockRepeats = 4

View File

@ -28,7 +28,7 @@ func (c *CardLogger) assign(a *Apple2, slot int) {
return 0
}, "LOGGERR")
c.addCardSoftSwitchW(i, func(_ *ioC0Page, value uint8) {
fmt.Printf("[cardLogger] Write access to softswith 0x%x for slot %v, value 0x%v.\n", iCopy, slot, value)
fmt.Printf("[cardLogger] Write access to softswith 0x%x for slot %v, value 0x%02x.\n", iCopy, slot, value)
}, "LOGGERW")
}

163
cardVidex.go Normal file
View File

@ -0,0 +1,163 @@
package izapple2
import (
"errors"
"fmt"
"image"
"image/color"
"github.com/ivanizag/izapple2/component"
"github.com/ivanizag/izapple2/storage"
)
/*
Videx 80 columns card for the Apple II+
See:
https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/80%20Column%20Cards/Videx%20Videoterm/Manuals/Videx%20Videoterm%20-%20Installation%20and%20Operation%20Manual.pdf
http://bitsavers.trailing-edge.com/components/motorola/_dataSheets/6845.pdf
https://glasstty.com/?p=660
*/
// CardVidex represents a Videx compatible 80 column card
type CardVidex struct {
cardBase
mc6845 component.MC6845
sramPage uint8
sram [0x800]uint8
upperROM memoryHandler
charGen []uint8
}
// NewCardVidex creates a new CardVidex
func NewCardVidex() *CardVidex {
var c CardVidex
c.name = "Videx 80 col Card"
// The C800 area has ROM and RAM
c.loadRomFromResource("<internal>/Videx Videoterm ROM 2.4.bin")
c.upperROM = c.romC8xx
c.romC8xx = &c
// The resource should be internal and never fail
c.loadCharacterMap("<internal>/80ColumnP110.BIN")
return &c
}
func (c *CardVidex) loadCharacterMap(filename string) error {
bytes, _, err := storage.LoadResource(filename)
if err != nil {
return err
}
size := len(bytes)
if size < 0x800 {
return errors.New("Character ROM size not supported for Videx")
}
c.charGen = bytes
return nil
}
func (c *CardVidex) assign(a *Apple2, slot int) {
for i := uint8(0x0); i <= 0xf; i++ {
// Bit 0 goes to the RS pin of the MC6548. It controls
// whether a register is being accesed or the contents
// of the register is being accessed
rsPin := (i & 1) == 1
// Bits 2 and 3 determine which page will be selected
sramPage := i >> 2
ssName := fmt.Sprintf("VIDEXPAGE%v", sramPage)
if rsPin {
ssName += "REG"
} else {
ssName += "ADDRESS"
}
c.addCardSoftSwitchR(i, func(*ioC0Page) uint8 {
c.sramPage = sramPage
return c.mc6845.Read(rsPin)
}, ssName+"R")
c.addCardSoftSwitchW(i, func(_ *ioC0Page, value uint8) {
c.sramPage = sramPage
c.mc6845.Write(rsPin, value)
}, ssName+"W")
}
c.cardBase.assign(a, slot)
}
const videxRomLimit = uint16(0xcc00)
const videxSramLimit = uint16(0xce00)
const videxSramMask = uint16(0x01ff)
func (c *CardVidex) peek(address uint16) uint8 {
if address < videxRomLimit {
return c.upperROM.peek(address)
} else if address < videxSramLimit {
return c.sram[address&videxSramMask+uint16(c.sramPage)*0x200]
}
return 0
}
func (c *CardVidex) poke(address uint16, value uint8) {
if address >= videxRomLimit && address < videxSramLimit {
c.sram[address&videxSramMask+uint16(c.sramPage)*0x200] = value
}
}
func (c *CardVidex) setBase(base uint16) {
// Nothing
}
const (
videxCharWidth = uint8(8)
)
func (c *CardVidex) buildImage(light color.Color) *image.RGBA {
params := c.mc6845.ImageData()
width, height := params.DisplayedWidthHeight(videxCharWidth)
if (width == 0) || (height == 0) {
// No image available
size := image.Rect(0, 0, 3, 3)
img := image.NewRGBA(size)
img.Set(1, 1, color.White)
return img
}
size := image.Rect(0, 0, width, height)
img := image.NewRGBA(size)
params.IterateScreen(func(address uint16, charLine uint8,
cursor bool, displayEnable bool,
column uint8, y int) {
bits := uint8(0)
if displayEnable {
char := c.sram[address&0x7ff]
bits = c.charGen[(uint16(char&0x7f)<<4)+uint16(charLine)]
if cursor {
bits = ^bits
}
if char >= 128 {
bits = ^bits
}
}
x := int(column) * int(videxCharWidth)
for i := 0; i < int(videxCharWidth); i++ {
pixel := (bits & 0x80) != 0
if pixel {
img.Set(x, y, light)
} else {
img.Set(x, y, color.Black)
}
bits <<= 1
x++
}
})
return img
}

105
component/mc6845.go Normal file
View File

@ -0,0 +1,105 @@
package component
/*
MC6845 CRT Controller
See:
Motorola MC6845 datasheet
Pins:
RW, RS, D0-D7: Read() and Write()
MA0-13, RA04, CURSOR, DE: MC6845RasterCallBack()
*/
type MC6845 struct {
reg [18]uint8 // Internal registers R0 to R17
sel uint8 // Selected address register AR
}
func (m *MC6845) Read(rs bool) uint8 {
if !rs {
// AR is not readable
return 0x00
} else if m.sel >= 14 && m.sel <= 17 {
// Only R14 to R17 are readable
// Should we mask R14 and R16?
return m.reg[m.sel]
}
return 0x00
}
func (m *MC6845) Write(rs bool, value uint8) {
if !rs {
// AR is 5 bits
// What happens if AR > 17 ?
m.sel = value & 0x1f
} else if m.sel <= 15 {
// R0 to R15 are writable
m.reg[m.sel] = value
//fmt.Printf("Set %v to %v\n", m.sel, value)
}
}
func (m *MC6845) ImageData() MC6845ImageData {
var data MC6845ImageData
data.firstChar = uint16(m.reg[12]&0x3f)<<8 + uint16(m.reg[13])
data.charLines = (m.reg[9] + 1) & 0x1f
data.columns = m.reg[1]
data.lines = m.reg[6] & 0x7f
data.adjustLines = m.reg[5] & 0x1f
data.cursorPos = uint16(m.reg[14]&0x3f)<<8 + uint16(m.reg[15])
data.cursorStart = m.reg[10] & 0x1f
data.cursorEnd = m.reg[11] & 0x1f
// cursor mode is on bits 6 and 5 of R10
return data
}
type MC6845ImageData struct {
firstChar uint16 // 14 bits, address of the firt char on the first line
charLines uint8 // 5 bits, lines par character
columns uint8 // 8 bits, chars per line
lines uint8 // 7 bits, char lines per screen
adjustLines uint8 // 5 bits, extra blank lines
cursorPos uint16 // 14 bits, address? of the cursor position
cursorStart uint8 // 5 bits, cursor starting char row
cursorEnd uint8 // 5 bits, cursos ending char row
// cursor mode
}
func (data *MC6845ImageData) DisplayedWidthHeight(charWidth uint8) (int, int) {
return int(data.columns) * int(charWidth),
int(data.lines)*int(data.charLines) + int(data.adjustLines)
}
type MC6845RasterCallBack func(address uint16, charLine uint8, // Lookup in char ROM
cursor bool, displayEnable bool, // Modifiers
column uint8, y int) // Position in screen
func (data *MC6845ImageData) IterateScreen(callBack MC6845RasterCallBack) {
lineAddress := data.firstChar
y := 0
var address uint16
for line := uint8(0); line < data.lines; line++ {
for charLine := uint8(0); charLine < data.charLines; charLine++ {
address = lineAddress // Back to the first char of the line
for column := uint8(0); column < data.columns; column++ {
isCursor := (address == data.cursorPos) &&
(charLine >= data.cursorStart) &&
(charLine <= data.cursorEnd)
callBack(address, charLine, isCursor, true, column, y)
address = (address + 1) & 0x3fff // 14 bits
}
y++
}
lineAddress = address
}
for adjust := uint8(0); adjust <= data.adjustLines; adjust++ {
for column := uint8(0); column < data.columns; column++ {
callBack(0, 0, false, false, column, y) // lines with display not enabled
}
y++
}
}

View File

@ -149,6 +149,10 @@ func (p *ioC0Page) poke(address uint16, value uint8) {
ss(p, value)
}
func (p *ioC0Page) setBase(_ uint16) {
// Ignore
}
func ssFromBool(value bool) uint8 {
if value {
return ssOn

View File

@ -70,6 +70,7 @@ const (
type memoryHandler interface {
peek(uint16) uint8
poke(uint16, uint8)
setBase(uint16)
}
func newMemoryManager(a *Apple2) *memoryManager {

View File

@ -5,11 +5,9 @@ import (
)
type memoryRange struct {
base uint16
data []uint8
name string
address string
//basePtr uintptr
base uint16
data []uint8
name string
}
type memoryRangeROM struct {
@ -23,7 +21,6 @@ func newMemoryRange(base uint16, data []uint8, name string) *memoryRange {
m.setBase(base)
m.name = name
m.address = fmt.Sprintf("%p", &m)
return &m
}
@ -31,30 +28,16 @@ func newMemoryRangeROM(base uint16, data []uint8, name string) *memoryRangeROM {
var m memoryRangeROM
m.base = base
m.data = data
m.setBase(base)
m.name = name
m.address = fmt.Sprintf("%p", &m)
return &m
}
func (m *memoryRange) setBase(base uint16) {
m.base = base
//p := unsafe.Pointer(&m.data[0])
//m.basePtr = (uintptr)(p) - (uintptr)(base)
}
func (m *memoryRange) peek(address uint16) uint8 {
// Safe version:
return m.data[address-m.base]
// Really overkill
// go-vet warns the caching of basePtr
// This wouldn't have a warning
// indexp := unsafe.Pointer((uintptr)(unsafe.Pointer(&m.data[0])) - (uintptr)(m.base) + uintptr(address))
// But it makes sense to precalculate that
//indexp := unsafe.Pointer(m.basePtr + uintptr(address))
//return *(*uint8)(indexp)
}
func (m *memoryRange) poke(address uint16, value uint8) {
@ -72,12 +55,12 @@ func (m *memoryRangeROM) poke(address uint16, value uint8) {
func identifyMemory(m memoryHandler) string {
ram, ok := m.(*memoryRange)
if ok {
return fmt.Sprintf("RAM 0x%04x %s at %s", ram.base, ram.name, ram.address)
return fmt.Sprintf("RAM 0x%04x %s", ram.base, ram.name)
}
rom, ok := m.(*memoryRangeROM)
if ok {
return fmt.Sprintf("ROM 0x%04x %s at %s", rom.base, ram.name, rom.address)
return fmt.Sprintf("ROM 0x%04x %s", rom.base, ram.name)
}
return ("Unknown memory")

View File

@ -138,6 +138,10 @@ func (nsc *noSlotClockDS1216) poke(address uint16, value uint8) {
nsc.memory.poke(address, value)
}
func (nsc *noSlotClockDS1216) setBase(base uint16) {
nsc.memory.setBase(base)
}
func (nsc *noSlotClockDS1216) loadTime() {
now := time.Now()

View File

@ -19,7 +19,21 @@ var Assets = func() http.FileSystem {
fs := vfsgen۰FS{
"/": &vfsgen۰DirInfo{
name: "/",
modTime: time.Date(2021, 1, 25, 17, 41, 15, 529030569, time.UTC),
modTime: time.Date(2021, 3, 14, 16, 59, 4, 46831383, time.UTC),
},
"/80ColumnP109.BIN": &vfsgen۰CompressedFileInfo{
name: "80ColumnP109.BIN",
modTime: time.Date(2021, 3, 7, 21, 7, 53, 641037891, time.UTC),
uncompressedSize: 1024,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x93\x5f\x68\x1c\xd5\x17\xc7\xef\xee\xce\xbd\xb3\x3b\x6d\x93\x25\x69\x7e\x6c\xfb\x53\x7a\x71\xd3\x92\x95\x88\x41\x8c\x5e\x1f\x0c\x42\x1f\x9c\xac\x0b\x0d\xf8\x2e\x83\x4f\x77\x8a\x82\xbe\xc8\x80\xc4\x6c\x68\x37\x86\xd8\x95\x2b\x52\xd8\x55\x06\x02\x4d\x21\x37\x64\xc3\xa8\x2f\xd7\x92\x81\x79\x49\x99\x3b\xb1\xf4\xa2\x4f\x45\x1b\xfa\xe0\x9f\xc4\x87\x7a\xfb\x52\x06\x75\x53\x99\xf1\xd5\xb7\xf3\x3d\x1c\xee\x39\xe7\x73\xbe\x37\x14\xa8\x91\xca\x19\x3d\xc7\x67\x7c\x81\x7c\x62\x62\x12\xaf\xd9\x21\x31\x1b\xd5\xad\xb5\xc6\x68\xbf\x1d\x85\x57\xe2\xfe\x72\x74\xb8\xd6\x18\x55\x0f\xe9\x16\x07\x3e\x41\xad\x1b\x32\xaf\xd0\x06\xaf\xa9\x22\x1f\xeb\xd4\x1d\x7c\x3b\xfe\xf5\x12\x26\xf1\x00\x44\xd5\xc7\xd3\xd5\x48\x7e\xa2\x8a\xfc\x47\xb9\xac\xac\x90\x98\xf3\xaf\xf9\xc4\x0c\x7e\xca\x9e\xb5\x2c\xca\x90\x8c\x58\x71\x1e\xdb\x8d\x25\x5f\x40\xea\x7c\xb8\xf0\xf6\x99\xd3\xe5\xb1\x71\x70\x22\x3a\x01\x00\x00\x99\xfe\x7f\xb9\x76\x06\x94\xa3\x72\xa6\x7d\x01\x6f\x9e\xbf\x4f\xa0\x46\x3e\x81\xb8\x9e\xdc\x9c\xbc\x2f\x0c\x56\xf2\x85\x81\x49\x1c\x0a\x88\xaf\x26\xdb\x29\xe2\xf9\xac\xc2\xa8\x2d\x8a\x52\x7f\x39\xe2\x23\xfd\x76\xc4\xc1\x22\x31\xfa\xcb\x91\x13\x0a\x54\x29\xfa\x02\x39\x3c\xb2\x79\xfb\xa8\xa0\xfe\x1e\xcc\x44\xf4\xa8\xa0\x1e\x39\xbb\xc2\xc0\x6e\x72\x2c\x8c\x50\x18\x72\x81\x35\x79\x57\x9e\x52\x88\x03\x5f\x18\x4e\xa5\x2d\xd7\x83\x03\xf9\x31\x7b\x51\xdf\x76\x3f\x60\xcf\xeb\x69\x77\xc8\xde\xd0\x6f\xba\x43\x76\x59\x7f\x24\x4f\xb2\xa7\xf4\x3b\xee\x23\x36\xa2\x4f\xbb\x43\xb6\x9b\xe7\xaf\x07\xe3\x59\x33\x23\x6b\x76\x20\x8c\xea\x9f\xfc\x92\x2f\x8c\x90\x40\xad\x0f\x08\x6c\xd5\x13\x32\x1f\xe9\xe7\xdc\x21\xeb\xea\x07\xee\x90\xcd\xe9\x9f\xdd\x21\x3b\xab\x7e\xd8\xcd\x8b\x6c\x7c\x21\xc1\xb5\x64\x1d\x50\x17\xec\xd5\x99\x0e\xfe\xd7\x9a\x48\xf0\x40\xf6\xf4\x2f\xbf\xb7\xc2\x38\x9f\xca\x27\x70\xb3\x55\x4f\xf2\xf8\x98\xc0\x90\xc0\xbd\x3a\x7b\xeb\x80\xc0\xda\x76\x8a\x42\x82\x5c\xd8\x58\xf2\x09\xc2\x2f\x24\xfc\x54\x8e\x24\xc7\x71\x32\x0b\x73\x18\x1b\xf5\xd5\x1e\xbe\x90\xac\x03\x5c\x4b\x82\xfa\xf4\xfe\x93\xf7\x0c\xc2\x6a\xa2\xfb\xe4\x2e\x9e\xd8\x77\x3e\x9b\x22\x81\xde\x15\x06\x5f\xc7\x6e\x12\x47\x0b\x2c\x75\x42\x02\xbb\xa9\x61\x59\xb5\x77\x53\x63\x91\x20\xbb\xd9\x6c\x36\x7d\x62\x50\xcb\xb2\x2c\x5f\x94\x9c\x4f\x3d\xb3\x97\x51\xef\x7a\x30\x07\xde\x2c\x37\x4a\x96\x75\x2a\x45\x57\x67\x37\xbf\x6d\x47\x1b\xb3\x53\x3b\x1e\x74\xac\xec\xfa\x4d\x7a\xd9\xc6\x17\x13\x1a\x40\x1f\x7c\xc7\x4a\x3e\xb8\xb3\xe3\x99\x8e\xbd\x9d\x22\xfc\xdb\x7e\x66\x45\x53\x19\xb4\x35\x2e\x1b\x86\x7a\x96\x1e\xe1\xc6\x92\x7d\x20\x50\x96\x2f\xa9\x09\xba\x57\x0f\x4a\x3e\x81\x83\x14\xca\x85\x20\x3b\x7f\xab\x9e\xd0\x6e\x0a\x9d\xdc\xba\xb4\xd2\x96\x81\x32\x43\x62\x56\xaa\xaa\x22\xbf\x52\xe5\xcc\xa2\x7f\xb4\x46\x62\xf9\xb5\xaa\xf0\xe3\x57\x89\xe9\x13\xd3\x91\xdf\xa8\x0a\x2f\x3c\xfd\xaf\x68\x15\xe4\x7f\xee\x6f\xaf\xd9\x3d\xbb\xbc\x32\x7b\x8b\x52\x4a\x37\x29\xfd\x62\xab\x67\xaf\x65\xeb\x6e\x5e\x4b\x11\x9d\xb2\x17\xaa\xbc\xda\x21\x2b\xaf\x70\xb3\xf3\xd2\xca\xcb\x18\xc4\x35\xe6\xd1\x8d\x59\xfd\xcc\xea\xc0\x43\x72\x55\x9f\xbd\x07\x8a\x7a\x62\x1e\xdf\x03\x45\x35\x37\xf0\x50\x1f\x14\x03\x84\xed\x18\xbf\x2f\x79\x1b\x3f\x4e\xb0\x1d\xcb\x2f\xf5\x43\xd9\x55\xd0\xc6\x13\x09\x95\xd7\xd5\x58\xe6\xc3\x8b\x49\x00\xc3\x0c\x4c\x08\xee\x54\xda\x5d\x0f\xa9\x32\xb6\xe3\x75\x70\xcd\x43\xb7\x0e\x0f\x0f\x7d\x50\xd8\xf1\x4c\x0e\x3a\x93\x21\x81\x9d\xf3\x74\x93\x6e\x51\x87\xee\x12\xb3\x5a\xde\xf6\x50\xf4\x80\x15\x7a\xf8\x46\xcc\x97\xf0\x51\x12\x0a\x43\xa3\xa3\xd7\xd9\xf7\xee\xb9\x5a\x67\x92\xdd\x1d\x80\x48\x7e\xae\x46\xa7\xab\x51\xf6\x41\xff\x92\x57\x74\x69\xba\x1a\x11\xe7\xdc\x3f\x01\x00\x00\xff\xff\x98\xef\x4f\xd7\x00\x04\x00\x00"),
},
"/80ColumnP110.BIN": &vfsgen۰CompressedFileInfo{
name: "80ColumnP110.BIN",
modTime: time.Date(2021, 3, 14, 15, 10, 13, 458446698, time.UTC),
uncompressedSize: 2048,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x95\x3f\x6b\xdb\x4e\x18\xc7\x0f\x71\x98\x1b\x44\xf2\x60\x32\xdc\x20\xc2\x21\x34\x98\x1f\xbf\xc1\x53\x30\x45\x88\xe3\xe1\x08\xa6\x43\x31\x21\x43\x86\x0c\xc6\x53\xc6\x4e\xc5\x14\x23\x84\xe8\x10\x42\x07\x23\x32\x15\x9a\xa1\x63\x5e\x44\xc9\xd4\xd7\x91\xa1\x63\x06\x8f\x9e\xac\xa2\xab\x7d\xf7\xe8\x48\xfa\xe0\x21\x1f\x7d\xef\xbe\x77\xcf\x1f\x29\x2c\x88\xb6\x6d\xff\xf9\xa8\xb5\xf1\xf6\x92\xbf\x7f\xbd\xbd\xa5\x3d\xc4\x9e\x9f\xd7\xcf\x0f\x9b\x38\x4a\x0e\x8b\xd7\xeb\xcd\xfa\xa7\x10\xe2\xc0\x55\x55\x3d\x6c\x24\xc0\x81\x6f\x6f\x67\xaa\x20\xfa\xa6\x7a\x7a\x58\x13\xfd\x63\x55\xad\x86\xc9\xc9\xd0\xf3\x1c\x5e\x86\xc3\x98\xf0\xe9\x6f\xce\x8f\xc2\x0c\x0e\x21\x04\x31\xdf\xc7\x71\xa0\x1f\xbf\xb6\xe7\x8d\xb0\xfb\x83\x67\xc7\x81\xbe\x0d\xbc\xb6\x81\x7f\x1b\xe8\x61\xf9\xb7\x81\xff\x36\xf0\x6f\x03\xbd\x0d\x74\x1a\xb2\x0b\x26\x1d\x9f\x9d\x0d\x33\xaa\x67\x59\x99\x95\x99\x7f\x24\x8a\x69\x3e\x5c\xb9\xf2\xb3\xc5\xfd\x0d\xfc\x7f\xf1\xd5\xb1\x9a\xcd\x16\xf7\xb7\x9f\x88\xbf\x00\xea\x17\x83\x52\x0a\x5c\x7b\xd8\x58\x70\xce\xc5\xd8\x31\x34\x97\x93\xcb\x86\x6c\x01\xd8\x01\x84\x77\xee\x79\x96\x61\x4e\x4c\xfa\x7c\x22\x2e\x40\xe9\xca\xf1\xc4\xdc\x35\x3f\xcc\xc4\xe7\x33\xe9\xaa\x52\x38\xce\x31\x4a\x94\xf6\x96\x25\x17\x49\x84\xb9\x63\x1e\x9f\x64\x25\xe7\x5e\xd7\xab\x88\xea\xa7\x4a\xaf\x90\x70\x69\x2f\xa0\x88\x3f\xe6\x54\xcf\x11\x8b\x88\x2f\x7b\x77\x27\xd7\xdf\x33\xc9\x77\x20\xe7\x72\xc0\x7a\xe9\xf7\x0a\x30\x97\x03\x39\x67\x34\x9f\x18\x18\x10\xfe\xf6\x78\xaf\xfd\xf9\x32\x43\x2c\x11\x1d\xaf\x10\xbb\x9f\xe3\x24\xd5\x5a\xa7\xee\x75\x65\x4b\x83\x88\x66\x49\xf2\xd7\x4b\x4d\xeb\x65\x59\xd3\xfd\x1f\x30\x3d\x75\x8c\xf6\x38\x7f\x5e\x62\xa7\xd2\xfb\x1f\x71\xce\x39\xe9\x0f\x9a\xe9\xec\xc6\xf8\xf5\xda\x86\x3f\xaf\xfe\xf5\xd8\xd4\x75\xed\xd7\x2f\x2e\xde\x9f\x13\xff\x89\xa9\xeb\x9a\xf8\x75\xc9\xd1\xfb\x75\xfa\x9d\x79\xd7\xd3\xa7\xe4\xbc\x1c\x75\x4e\xfb\xbb\x03\x1b\x34\x1f\xa4\xfd\xac\x6b\x63\x46\x23\x20\x5c\xd7\xcd\xa3\xf1\x6c\x46\x30\x32\x75\x8f\xa9\xdf\xce\xce\xeb\xce\x71\x31\xee\xc2\xcf\x67\xa5\x15\x08\x1e\x79\x7d\xd0\x85\xd7\x45\xf2\xdf\x6b\xaf\xb9\x33\x94\x12\x02\x71\xcb\x57\x5f\x3e\x3b\xd2\xdd\xf8\x92\xfe\xdb\x0a\x68\xef\x1f\x45\x05\x22\x16\x3d\xbd\x24\xf3\x94\xa4\x6a\xa9\xc8\xbc\xb3\xc8\x4e\xb8\xab\x90\xd6\xd7\x0b\xda\x7f\x11\x7c\x81\x39\xeb\x06\x80\x67\xd2\xad\x3f\xbf\x9a\x5f\x9d\x3b\x5d\x06\xf3\xc2\xd8\x4b\xd3\x34\x0d\xe1\xeb\x34\x4d\xd3\xde\xfd\x68\x7f\x6c\x87\xbb\x11\x70\x6c\xf3\x89\x7c\x45\xaf\x17\x74\x3e\x18\x2b\x74\x1e\x91\x7a\x40\x01\x00\xf4\xdf\x89\x31\x86\xcc\x8f\x9d\x80\x11\xf4\x98\xf6\x9f\x31\xcc\x64\x86\xe1\x7a\xf5\xe4\xb8\xe4\x52\xf9\xf9\x8e\x41\x8c\x05\xf9\x5e\x76\xb5\xa2\xf5\xea\x44\x20\xdf\xcf\x79\x13\x33\x1a\xd9\xb4\xb1\xbf\x3d\x7e\xff\x13\x00\x00\xff\xff\xf1\x70\x46\x09\x00\x08\x00\x00"),
},
"/Apple IIe Video Enhanced - 342-0265-A - 2732.bin": &vfsgen۰CompressedFileInfo{
name: "Apple IIe Video Enhanced - 342-0265-A - 2732.bin",
@ -133,6 +147,13 @@ var Assets = func() http.FileSystem {
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x52\xc1\x6b\x23\x55\x1c\x9e\x4e\x67\x3a\xcd\xd8\x6e\x97\x76\x29\x51\x56\xfc\x95\xee\x4a\xb3\x14\x9b\x83\xae\x03\x96\xc5\xb8\x4d\x7d\x29\xd3\x74\x48\x13\xdd\x95\x25\xe4\xe0\xe1\x99\x93\x07\x0f\xb9\xa8\x39\xb8\xb0\x04\x02\x3d\x78\x48\x0a\x81\x61\x5c\xcb\xbc\x92\x66\x83\x2b\xec\xa4\x18\x8c\x20\xd8\xf7\xe6\x90\x77\xf0\x20\x82\xcb\x1e\xf6\x60\x7b\x90\x57\xb5\xec\x20\x48\x65\xd2\x52\x2f\xfe\x07\xf6\x3b\x7c\x6f\xbe\x99\xef\xf7\xfd\x3e\x86\x37\x5a\x9a\x9b\xbf\x75\xfc\xa1\x6a\xb4\x87\xa2\xde\x68\x09\x55\x50\x0d\xed\x1c\xf7\xe1\x85\x3d\x8c\x77\xab\x81\xa6\xeb\xba\x5e\x2d\x69\x18\x63\x17\x6f\x46\x14\x34\x57\x6b\x96\xb4\x56\xa0\xc5\x3e\x45\xd6\x05\xef\xcb\xeb\x5c\x79\xfa\xba\xd0\xc9\xe8\x5d\xc3\xd8\x98\x36\x2d\x4a\x9e\xbb\x7b\x9d\xe4\x1b\x9e\x42\xf4\x86\xa1\x10\xa9\x61\xa8\x98\x5e\x15\x77\xe8\xcb\xe2\x16\x5d\x14\x59\x7a\x43\x58\x74\x56\x98\x34\x2f\xde\xa3\x33\x62\x85\x5e\x13\x19\x07\xe8\xbc\xc8\x38\x73\xf4\x15\x61\x3a\x71\xba\x20\x96\x5b\x81\x86\xba\x9e\x42\x67\x44\x82\xe6\xc5\x05\x1c\x7b\x49\x37\xf6\x87\xc2\xc0\x3c\x9f\xc5\xc6\x7e\x22\x0e\x74\xfa\xe2\xe5\xed\x6e\xd5\x37\x15\xbf\x61\xa8\xe6\x02\x25\x00\x8f\x18\x19\x0d\x69\xa6\xe1\x29\xe6\x02\xad\xc0\x23\xc6\x9f\x61\x3a\x2e\xa6\x28\x88\xa5\xd8\x04\xf4\x99\x90\xc3\x62\x5a\x74\xe3\x35\xe8\xb3\x8f\x0d\x0d\x91\x48\xc3\x18\x81\x3e\x43\x8f\x8d\x11\xfe\x2c\xfc\x38\x82\xa1\xcf\x4e\xd4\xc5\x30\xe1\xab\x5d\x8c\x6b\x08\x63\x52\x45\x9b\xad\x40\x6b\x96\xb4\xae\x37\xfc\x8b\xa1\xf1\xb1\x17\x0d\x4d\x68\xf4\x4d\x3e\x5c\x2f\xf7\xb0\x8b\xb7\xf1\x5c\x01\x93\xe8\x59\x8d\xd3\x64\xaa\xc7\x65\x22\x0d\xf2\x0f\x43\x39\x3e\x90\x5d\x43\x15\x0a\x9d\xe5\x79\x5b\xc2\x54\x8f\x2b\x7b\xc6\xbe\x8e\x6a\x8e\x04\x5b\x0c\xc3\x16\x6b\x06\x5a\xc7\x50\xf9\x35\xf2\x00\x5c\x86\x6d\x05\x9f\xbe\xbf\x27\x34\xf2\x06\xb8\x8c\xff\x46\xec\xaa\x2a\x77\xfe\xf0\xc5\xf3\xe0\xb2\x3d\x7e\x44\x24\x64\xab\xf0\x80\x9d\x39\xf9\xef\xe6\x26\xc3\xdb\x58\xd7\xdd\x8a\xae\xa3\x56\xa0\x75\x0d\xd5\xb1\xe9\x62\x5b\x76\x9c\x8a\x23\x81\xcb\x3a\x5f\xf8\x7b\xe0\x32\x6a\xf3\x23\xec\x76\xbe\xfd\x57\xc0\xfd\x41\x06\x3c\x64\xf6\x70\x28\xf8\xf0\xd9\xfe\x08\xd9\x0d\x2b\x99\xb1\xd3\x8e\xf4\xaa\x50\xe8\x0d\x6e\xd9\x89\x9d\x31\x99\xda\x5c\x26\xf1\xc1\xa5\x01\x79\x67\x5c\x8e\x4d\x8c\x83\x4c\x27\xe3\xb2\x6d\x51\x89\x2b\x64\x92\x47\xe8\x54\x7c\x3a\x30\xf6\x27\x7f\x72\xc6\x10\x1c\xb3\xd8\x44\xb8\x2b\x36\x01\x5b\xcc\xb9\x04\x0f\x59\x0d\x5c\x46\x56\xc1\x65\x15\x97\x54\xc1\x65\xe6\x15\x2a\xc6\x23\xf1\x48\xb9\x21\xc9\xbf\x16\x48\x93\x1f\x11\x9b\x1f\xd6\xcb\xbd\x88\x52\x2f\xf7\xe0\x07\x96\x1a\x9c\x8c\x21\x84\x71\x01\x11\xa5\xe1\x8d\x10\xa9\xe1\xa9\x9d\x72\x4f\xff\xc4\x53\x31\x8a\x0d\x85\x6e\xb9\x5e\xee\xa5\x42\xc2\x45\xf4\xd8\x1b\xe1\x4f\x70\xd7\x53\xa3\xc5\x62\xb1\x58\x38\xf9\x35\xe1\x20\x58\x3e\x46\x60\xfa\x18\xa5\x02\x30\x7d\xc7\x86\x0f\x4e\xc8\x8e\x42\xc1\x6f\x05\x5a\x38\xf9\x33\xb6\xbf\x61\xfc\xef\x7b\xfc\x2f\x73\x81\xa2\x0d\x15\x2c\x5f\x0c\xc3\x6d\x1f\xeb\xfc\xb0\xe0\x58\xa1\x7b\x8e\x6b\x4e\x34\x7c\x58\x72\xa6\x06\xd6\xa0\xd0\x2c\x69\x04\xea\xe5\xde\xc1\xc1\x41\xea\xbf\x4e\xc6\x9f\x14\xde\x7f\x7a\xe5\xfe\xa5\xcf\xb3\x3f\x7e\xf4\xe7\xab\x5f\x2b\x9f\x2d\x7d\x3f\xbf\x78\xc7\xbc\x3c\xb6\x9e\x4b\xc3\xea\x5a\x1a\xb2\xb9\x24\xbc\x9b\x5c\x82\x2c\xca\xc1\x72\x26\x05\xeb\x89\x2c\x24\x33\x19\x58\x49\xa4\x61\x39\xf9\x16\xac\x26\x32\x90\xb0\x32\xb0\x9a\xb8\x0d\x2b\xb9\x34\xac\xe4\x4c\x48\xe4\xde\x86\xf5\xa4\x05\x6b\x37\xb3\x90\x5e\x7b\x07\x96\x92\x37\xa1\xd5\x6e\xb7\xa5\x01\xd9\xf6\x77\xc7\xe7\x38\xc7\x39\xfe\xb7\xf8\x27\x00\x00\xff\xff\xe3\xc4\x99\x1b\x00\x08\x00\x00"),
},
"/Videx Videoterm ROM 2.4.bin": &vfsgen۰CompressedFileInfo{
name: "Videx Videoterm ROM 2.4.bin",
modTime: time.Date(2021, 3, 7, 20, 59, 17, 80032721, time.UTC),
uncompressedSize: 1024,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x92\x51\x68\x1c\x45\x1c\xc6\x27\x77\x3b\xbb\x77\x4b\x93\x9e\x49\xa4\x07\x5a\x32\xa6\x2d\xf5\xf0\xd4\xb4\x18\x3d\xf1\xc5\x87\x82\x9b\x63\x69\xcf\x37\x83\x45\x46\x84\x76\x23\x04\xcd\xdb\xe1\x3c\x24\x2a\x49\xd3\xea\xc8\x4a\x28\x44\x34\x70\x1e\x06\x33\x21\x8b\x9b\xf8\xd2\x08\x81\xd1\xfa\xb0\xff\x4d\x73\x99\x82\x2f\xa5\x08\x79\xaa\x69\xc0\xb2\x29\x04\x0f\x46\x13\xd9\x13\xf7\xe9\xbf\xcc\x37\xcc\xff\xfb\x7d\x5f\xc0\xac\x52\x1b\x86\x92\x67\xc4\x10\x67\x16\xd7\x96\x40\x5c\x9b\xe4\x5d\x68\xa2\x1b\x3c\x94\x1b\xdf\x46\x7c\x55\xee\xee\x14\xd4\x3e\x1f\x95\x34\xd0\x56\x29\x97\xe4\xc9\xdc\x21\x19\xfc\x9b\x5c\x3f\xf4\x96\xbc\x65\x8f\x92\xbb\xd1\x83\x8b\x2a\xf3\xe0\x52\x80\x64\xe1\x80\x5c\x8e\xfc\xa4\x5c\x90\x45\x0a\x9f\xa9\x8c\xb8\x0f\x1f\x2b\x3b\xd0\xd6\xc8\xeb\x5c\x5b\xe1\x1f\x4e\xa0\x2d\xdb\xf6\xfc\x01\x08\xfd\xa7\xca\xef\xc9\xa1\x7e\x08\x93\x1e\x90\x2a\x23\x14\xdc\xf7\x73\xa5\x6d\x65\x88\xdf\xf3\xc4\x29\x4d\x72\x66\x7a\x15\xfa\x51\xed\x9d\x17\x07\x51\xf1\x24\xca\xed\xe4\x10\x42\x88\x33\x73\xf1\xcc\x96\xc6\x89\xc9\x35\x26\x46\xbc\x78\x7a\x8b\x61\x3f\xcb\x19\x0e\x98\x49\xae\xc7\xe2\x38\x0f\x65\xc0\x30\xd4\xc2\xbe\x71\x66\xf0\x55\x29\x7a\x78\x28\x05\x1a\xd7\xe9\x0f\x1d\x91\x90\x0b\x4f\x2e\x09\x70\xd6\x1f\x6f\x3a\x74\x6f\x85\x61\xd1\x20\x13\x71\x24\x6b\x7e\x9b\x8a\x97\x38\xb3\xa8\x38\xaf\xda\x0d\xd9\x9c\x8a\xd5\x3f\xc1\x90\x9c\x55\x07\x74\x85\x61\x59\xf3\xb1\x43\x42\xf0\x56\x18\x26\x13\xf1\x23\x86\xcb\x75\xa3\x60\xfd\xf7\x94\x97\x2a\x02\x8d\x1d\x62\xc5\xc4\x80\x06\xf2\xc6\x10\x14\xfd\x24\x3c\x45\xae\xc2\x7c\xb2\x2b\x10\x67\x98\x6b\xbc\x94\xf4\xb6\x18\x2e\x2c\x88\x4b\xbc\xa3\x4f\xe6\x5a\x1a\xbb\x46\xdc\xa1\xef\xe5\xa7\x60\x55\x5d\x15\x39\xfe\x96\x54\x6f\xc3\x9a\x7a\x53\x1c\x3e\xaf\xd3\x50\x28\x67\xe6\xc5\xba\xe1\x6e\x46\xe4\x6c\xfc\x48\xa7\x37\xa1\xe8\x57\x5b\xe9\x64\x8e\x19\xa5\xc9\x34\xb3\xde\x58\x74\x77\xbc\x77\x4c\x1f\x4b\xc7\x8e\x65\x71\x82\x58\x71\x03\x11\x03\xc2\x9b\xf0\xa3\xea\x11\x5d\xdd\xda\x52\x02\x42\xf5\x8d\x9b\x8f\xdc\xb3\x10\x68\xcc\xdb\x86\x6d\x8f\xb7\x8d\x71\x6d\x3a\xd5\x6a\xb5\xca\xb5\xe1\xd9\xb6\x6d\x73\x66\x50\xe8\x56\x66\x67\x7f\x9a\x9f\x82\x46\xd8\x82\x6b\x7e\xae\xc3\x6f\x1d\x1c\x5a\x9c\xe8\x5b\x73\xe8\x0f\x0b\x8f\xfb\xfe\xff\xae\xf4\xf4\x55\x50\x3f\x2b\xce\x8f\x33\xc3\xe9\x60\x77\xec\xd2\xb1\xe5\x8d\x50\x7a\x55\x6f\x99\xda\x69\x03\xaa\xde\xfb\x0e\x19\x8d\xbd\x10\x2f\xa0\x3b\x7e\x76\x01\x6d\x51\x47\xfc\x45\x1a\xc0\x47\x65\xda\x48\x4b\x19\x9e\x7b\x2a\x2e\x19\x49\xd6\xbd\x06\x5e\xe5\x21\x29\x4d\x3a\x2d\x66\xa5\x67\x59\xf5\xa4\x07\xc5\x30\xcb\x35\x0e\xda\x29\xfb\x34\x7b\xd7\x88\x3d\xde\xc6\x34\x40\x12\xe6\xd4\xf1\x72\x41\xa6\x5d\xd4\xf0\x69\x92\x2d\x17\x24\x5d\x5a\x3f\xb7\x49\xf6\x23\x72\x21\x82\x56\x98\x03\xf0\x0d\xb8\xa3\xf6\xdc\xfd\x68\xaf\xbc\x79\xf4\xe1\xb9\x8a\x5f\xbc\x55\x7b\xae\xeb\x93\x27\xfa\x9f\x1e\x74\x51\x2a\x2b\x4d\x36\x11\x25\xdf\x43\x13\x51\x40\x49\x3e\x40\xd2\xf6\xb3\xe4\x72\xd4\x44\xf4\xcb\x67\x2b\xb7\xf8\xd1\xb6\x33\x3d\x7c\xc3\x99\x77\x16\x87\x67\x86\x9b\xb7\xbf\xa8\x1b\x4e\xad\x20\xce\x4f\x57\x66\x5e\x15\xd6\xf4\xcb\x33\xaf\x10\x14\x15\xfd\x0f\xbc\xef\x86\x93\x81\xd9\xa0\x6e\xc2\x6c\x72\xe2\x1e\xca\x24\xbd\x23\xe4\x1e\xca\xa8\xd7\x82\xba\xf9\x15\xca\x84\x59\xf2\x67\x2c\xa6\xc8\x01\xa4\x9b\x7d\x9d\xec\x03\x57\xd8\x21\x5d\xe0\xc1\x4d\xd5\x9b\xf6\x6c\x34\x0e\xf1\x46\x8a\x68\x03\x6d\xe5\xa7\x78\xdd\x54\x39\x72\x21\x6a\xa0\xcf\xeb\xe6\x4f\xbb\xbb\xbb\x0b\xa8\x4b\xa0\xe9\xd3\x81\xc6\xd3\x67\xdc\x17\x22\x6f\x45\x5b\x85\xdc\x4a\xdd\x94\x3b\x7e\xd7\x3c\x59\x8d\xc8\x76\x2c\x26\x49\x03\x02\x86\x1f\xbe\xe1\xff\x36\x36\x50\xf4\xef\xd2\xca\xc4\x1a\x43\xce\x95\x5f\x7f\xf9\xf9\xe8\xf6\xde\xbf\x01\x00\x00\xff\xff\x28\xbb\xe3\xbb\x00\x04\x00\x00"),
},
"/dos33.dsk": &vfsgen۰CompressedFileInfo{
name: "dos33.dsk",
modTime: time.Date(2021, 1, 23, 23, 12, 20, 701418936, time.UTC),
@ -142,6 +163,8 @@ var Assets = func() http.FileSystem {
},
}
fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
fs["/80ColumnP109.BIN"].(os.FileInfo),
fs["/80ColumnP110.BIN"].(os.FileInfo),
fs["/Apple IIe Video Enhanced - 342-0265-A - 2732.bin"].(os.FileInfo),
fs["/Apple IIe Video Unenhanced - 342-0133-A - 2732.bin"].(os.FileInfo),
fs["/Apple2_Plus.rom"].(os.FileInfo),
@ -158,6 +181,7 @@ var Assets = func() http.FileSystem {
fs["/DISK2.rom"].(os.FileInfo),
fs["/MemoryExpansionCard-341-0344a.bin"].(os.FileInfo),
fs["/ThunderclockPlusROM.bin"].(os.FileInfo),
fs["/Videx Videoterm ROM 2.4.bin"].(os.FileInfo),
fs["/dos33.dsk"].(os.FileInfo),
}

View File

@ -80,6 +80,9 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB
case VideoSHR:
snap = snapshotSuperHiRes(vs)
applyNTSCFilter = false
case VideoVidex:
snap = vs.GetCardImage(lightColor)
applyNTSCFilter = false
}
if applyNTSCFilter {

View File

@ -71,6 +71,8 @@ func VideoModeName(vs VideoSource) string {
name = "RGB160"
case VideoSHR:
name = "SHR"
case VideoVidex:
name = "VIDEX"
default:
name = "Unknown video mode"
}

View File

@ -3,6 +3,8 @@ package screen
import (
"encoding/json"
"fmt"
"image"
"image/color"
"image/png"
"io/ioutil"
"os"
@ -128,6 +130,11 @@ func (ts *TestScenario) GetSuperVideoMemory() []uint8 {
return ts.SVideoPage
}
// GetCardImage returns an image provided by a card, like the videx card
func (ts *TestScenario) GetCardImage(light color.Color) *image.RGBA {
return nil
}
func buildImageName(name string, screenMode int, altSet bool) string {
var screenName string
switch screenMode {

View File

@ -1,5 +1,10 @@
package screen
import (
"image"
"image/color"
)
// Base Video Modes
const (
VideoBaseMask uint16 = 0x1f
@ -14,6 +19,7 @@ const (
VideoRGBMix uint16 = 0x12
VideoRGB160 uint16 = 0x13
VideoSHR uint16 = 0x14
VideoVidex uint16 = 0x15
)
// Mix text video mdes modifiers
@ -43,4 +49,6 @@ type VideoSource interface {
GetCharacterPixel(char uint8, rowInChar int, colInChar int, isAltText bool, isFlashedFrame bool) bool
// GetSuperVideoMemory returns a slice to the SHR video memory
GetSuperVideoMemory() []uint8
// GetCardImage returns an image provided by a card, like the videx card
GetCardImage(light color.Color) *image.RGBA
}

44
softVideoSwitch.go Normal file
View File

@ -0,0 +1,44 @@
package izapple2
import (
"image"
"image/color"
)
/*
Videx Soft Video Switch
See:
https://archive.org/details/videx-soft-video-switch
*/
// SoftVideoSwitch represents a Videx soft video switch
type SoftVideoSwitch struct {
card *CardVidex
}
// NewSoftVideoSwitch creates a new SoftVideoSwitch
func NewSoftVideoSwitch(card *CardVidex) *SoftVideoSwitch {
var vs SoftVideoSwitch
vs.card = card
return &vs
}
func (vs *SoftVideoSwitch) isActive() bool {
if vs == nil {
return false
}
isTextMode := vs.card.a.io.isSoftSwitchActive(ioFlagText)
ann0 := vs.card.a.io.isSoftSwitchActive(ioFlagAnnunciator0)
return isTextMode && ann0
}
func (vs *SoftVideoSwitch) BuildAlternateImage(light color.Color) *image.RGBA {
return vs.card.buildImage(light)
}
func (a *Apple2) SoftVideoSwitch() *SoftVideoSwitch {
return a.softVideoSwitch
}

View File

@ -1,6 +1,9 @@
package izapple2
import (
"image"
"image/color"
"github.com/ivanizag/izapple2/screen"
)
@ -23,6 +26,7 @@ func (a *Apple2) GetCurrentVideoMode() uint16 {
isStore80Active := a.mmu.store80Active
isDoubleResMode := !isTextMode && is80Columns && !a.io.isSoftSwitchActive(ioFlagAnnunciator3)
isSuperHighResMode := a.io.isSoftSwitchActive(ioDataNewVideo)
isVidex := a.softVideoSwitch.isActive()
isRGBCard := a.io.isSoftSwitchActive(ioFlagRGBCardActive)
rgbFlag1 := a.io.isSoftSwitchActive(ioFlag1RGBCard)
@ -39,6 +43,9 @@ func (a *Apple2) GetCurrentVideoMode() uint16 {
if isSuperHighResMode {
mode = screen.VideoSHR
isMixMode = false
} else if isVidex {
mode = screen.VideoVidex
isMixMode = false
} else if isTextMode {
if is80Columns {
mode = screen.VideoText80
@ -137,6 +144,11 @@ func (a *Apple2) GetCharacterPixel(char uint8, rowInChar int, colInChar int, isA
return pixel
}
// GetCardImage returns an image provided by a card, like the videx card
func (a *Apple2) GetCardImage(light color.Color) *image.RGBA {
return a.softVideoSwitch.BuildAlternateImage(light)
}
// DumpTextModeAnsi returns the text mode contents using ANSI escape codes for reverse and flash
func DumpTextModeAnsi(a *Apple2) string {
is80Columns := a.io.isSoftSwitchActive(ioFlag80Col)