Support for character generator pages. F10 to change page.
This commit is contained in:
parent
68d68eadfc
commit
047233f16b
|
@ -84,6 +84,7 @@ Line:
|
||||||
- F6: Toggle between NTSC color TV and green phosphor monochrome monitor
|
- F6: Toggle between NTSC color TV and green phosphor monochrome monitor
|
||||||
- F7: Save current state to disk
|
- F7: Save current state to disk
|
||||||
- F8: Restore state from disk
|
- F8: Restore state from disk
|
||||||
|
- F10: Cycle character generator codepages. Only if the character generator ROM has more than one 2Kb pages.
|
||||||
- F12: Save a screen snapshot to a file `snapshot.png`
|
- F12: Save a screen snapshot to a file `snapshot.png`
|
||||||
|
|
||||||
Only valid on SDL mode
|
Only valid on SDL mode
|
||||||
|
@ -91,6 +92,8 @@ Only valid on SDL mode
|
||||||
### Command line options
|
### Command line options
|
||||||
|
|
||||||
```
|
```
|
||||||
|
-base64a
|
||||||
|
setup a Base64A clone
|
||||||
-charRom string
|
-charRom string
|
||||||
rom file for the character generator (default "<internal>/Apple2rev7CharGen.rom")
|
rom file for the character generator (default "<internal>/Apple2rev7CharGen.rom")
|
||||||
-disk string
|
-disk string
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
// Apple2 represents all the components and state of the emulated machine
|
// Apple2 represents all the components and state of the emulated machine
|
||||||
type Apple2 struct {
|
type Apple2 struct {
|
||||||
|
Name string
|
||||||
cpu *core6502.State
|
cpu *core6502.State
|
||||||
mmu *memoryManager
|
mmu *memoryManager
|
||||||
io *ioC0Page
|
io *ioC0Page
|
||||||
|
@ -84,6 +85,8 @@ const (
|
||||||
CommandLoadState
|
CommandLoadState
|
||||||
// CommandDumpDebugInfo dumps usefull info
|
// CommandDumpDebugInfo dumps usefull info
|
||||||
CommandDumpDebugInfo
|
CommandDumpDebugInfo
|
||||||
|
// CommandNextCharGenPage cycles the CharGen page if several
|
||||||
|
CommandNextCharGenPage
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendCommand enqueues a command to the emulator thread
|
// SendCommand enqueues a command to the emulator thread
|
||||||
|
@ -111,6 +114,9 @@ func (a *Apple2) executeCommand(command int) {
|
||||||
a.load("apple2.state")
|
a.load("apple2.state")
|
||||||
case CommandDumpDebugInfo:
|
case CommandDumpDebugInfo:
|
||||||
a.dumpDebugInfo()
|
a.dumpDebugInfo()
|
||||||
|
case CommandNextCharGenPage:
|
||||||
|
a.cg.nextPage()
|
||||||
|
fmt.Printf("Chargen page %v\n", a.cg.page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ func NewApple2(charRomFile string, clockMhz float64,
|
||||||
isColor bool, fastMode bool, panicSS bool) *Apple2 {
|
isColor bool, fastMode bool, panicSS bool) *Apple2 {
|
||||||
|
|
||||||
var a Apple2
|
var a Apple2
|
||||||
|
a.Name = "Apple ][+"
|
||||||
a.mmu = newMemoryManager(&a)
|
a.mmu = newMemoryManager(&a)
|
||||||
a.cpu = core6502.NewNMOS6502(a.mmu)
|
a.cpu = core6502.NewNMOS6502(a.mmu)
|
||||||
if charRomFile != "" {
|
if charRomFile != "" {
|
||||||
|
|
|
@ -66,13 +66,6 @@ func MainApple() *Apple2 {
|
||||||
)
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *dumpChars {
|
|
||||||
cg := NewCharacterGenerator(*charRomFile)
|
|
||||||
cg.Dump()
|
|
||||||
os.Exit(0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
a := NewApple2(*charRomFile, *cpuClock, !*mono, *fastDisk, *panicSS)
|
a := NewApple2(*charRomFile, *cpuClock, !*mono, *fastDisk, *panicSS)
|
||||||
if *base64a {
|
if *base64a {
|
||||||
NewBase64a(a)
|
NewBase64a(a)
|
||||||
|
@ -92,5 +85,11 @@ func MainApple() *Apple2 {
|
||||||
//a.AddCardInOut(2)
|
//a.AddCardInOut(2)
|
||||||
//a.AddCardLogger(4)
|
//a.AddCardLogger(4)
|
||||||
|
|
||||||
|
if *dumpChars {
|
||||||
|
a.cg.Dump()
|
||||||
|
os.Exit(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func SDLRun(a *apple2.Apple2) {
|
||||||
|
|
||||||
defer window.Destroy()
|
defer window.Destroy()
|
||||||
defer renderer.Destroy()
|
defer renderer.Destroy()
|
||||||
window.SetTitle("Apple2")
|
window.SetTitle(a.Name)
|
||||||
|
|
||||||
kp := newSDLKeyBoard(a)
|
kp := newSDLKeyBoard(a)
|
||||||
a.SetKeyboardProvider(kp)
|
a.SetKeyboardProvider(kp)
|
||||||
|
|
|
@ -101,6 +101,8 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) {
|
||||||
k.a.SendCommand(apple2.CommandLoadState)
|
k.a.SendCommand(apple2.CommandLoadState)
|
||||||
case sdl.K_F9:
|
case sdl.K_F9:
|
||||||
k.a.SendCommand(apple2.CommandDumpDebugInfo)
|
k.a.SendCommand(apple2.CommandDumpDebugInfo)
|
||||||
|
case sdl.K_F10:
|
||||||
|
k.a.SendCommand(apple2.CommandNextCharGenPage)
|
||||||
case sdl.K_F12:
|
case sdl.K_F12:
|
||||||
apple2.SaveSnapshot(k.a, "snapshot.png")
|
apple2.SaveSnapshot(k.a, "snapshot.png")
|
||||||
}
|
}
|
||||||
|
|
22
base64a.go
22
base64a.go
|
@ -15,14 +15,32 @@ type Base64a struct {
|
||||||
|
|
||||||
// NewBase64a instantiates an apple2
|
// NewBase64a instantiates an apple2
|
||||||
func NewBase64a(a *Apple2) *Base64a {
|
func NewBase64a(a *Apple2) *Base64a {
|
||||||
|
|
||||||
var b Base64a
|
var b Base64a
|
||||||
b.a = a
|
b.a = a
|
||||||
|
a.Name = "Base 64A"
|
||||||
b.loadRom()
|
b.loadRom()
|
||||||
|
|
||||||
|
// Configure the character generator
|
||||||
|
if !a.cg.customRom {
|
||||||
|
a.cg.load("<internal>/BASE64A_ROM7_CharGen.BIN")
|
||||||
|
}
|
||||||
|
a.cg.setColumnMap(base64aCharGenColumnsMap)
|
||||||
|
a.cg.setPage(1)
|
||||||
|
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func base64aCharGenColumnsMap(column int) int {
|
||||||
|
bit := column + 2
|
||||||
|
// Weird positions
|
||||||
|
if column == 6 {
|
||||||
|
bit = 2
|
||||||
|
} else if column == 0 {
|
||||||
|
bit = 1
|
||||||
|
}
|
||||||
|
return bit
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill
|
// There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill
|
||||||
// 2 or 4 banks with 2kb windows.
|
// 2 or 4 banks with 2kb windows.
|
||||||
|
@ -87,7 +105,7 @@ func (b *Base64a) loadRom() {
|
||||||
|
|
||||||
func (b *Base64a) changeRomBank(bank uint8) {
|
func (b *Base64a) changeRomBank(bank uint8) {
|
||||||
b.romBank = bank
|
b.romBank = bank
|
||||||
fmt.Printf("Change to ROM bank #%v\n", b.romBank)
|
//fmt.Printf("Change to ROM bank #%v\n", b.romBank)
|
||||||
b.a.mmu.physicalROM = b.romBanks[b.romBank]
|
b.a.mmu.physicalROM = b.romBanks[b.romBank]
|
||||||
b.a.mmu.resetRomPaging() // If rom was not active. This is going to far?
|
b.a.mmu.resetRomPaging() // If rom was not active. This is going to far?
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,16 @@ import (
|
||||||
// CharacterGenerator represents the ROM wth the characters bitmaps
|
// CharacterGenerator represents the ROM wth the characters bitmaps
|
||||||
type CharacterGenerator struct {
|
type CharacterGenerator struct {
|
||||||
data []uint8
|
data []uint8
|
||||||
|
customRom bool
|
||||||
|
columnMap charColumnMap
|
||||||
|
page int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type charColumnMap func(column int) int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rev7CharGenSize = 2048
|
rev7CharGenSize = 2048
|
||||||
|
defaultCharGenROM = "<internal>/Apple2rev7CharGen.rom"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCharacterGenerator instantiates a new Character Generator with the rom on the file given
|
// NewCharacterGenerator instantiates a new Character Generator with the rom on the file given
|
||||||
|
@ -26,27 +32,50 @@ func NewCharacterGenerator(filename string) *CharacterGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cg *CharacterGenerator) load(filename string) {
|
func (cg *CharacterGenerator) load(filename string) {
|
||||||
|
cg.customRom = !isInternalResource(filename)
|
||||||
bytes := loadResource(filename)
|
bytes := loadResource(filename)
|
||||||
size := len(bytes)
|
size := len(bytes)
|
||||||
if size != rev7CharGenSize {
|
if size < rev7CharGenSize {
|
||||||
panic("Character ROM size not supported")
|
panic("Character ROM size not supported")
|
||||||
}
|
}
|
||||||
cg.data = bytes
|
cg.data = bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cg *CharacterGenerator) getPixel(char uint8, row int, column int) bool {
|
func (cg *CharacterGenerator) setColumnMap(columnMap charColumnMap) {
|
||||||
bits := cg.data[int(char)*8+row]
|
// Regular Apple II uses bits 6 to 0 but some clones have other mappings
|
||||||
bit := bits >> (uint(6 - column)) & 1
|
cg.columnMap = columnMap
|
||||||
return bit == 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cg *CharacterGenerator) dumpCharFast(char uint8) {
|
func (cg *CharacterGenerator) setPage(page int) {
|
||||||
|
// Some clones had a switch to change codepage with extra characters
|
||||||
|
pages := len(cg.data) / rev7CharGenSize
|
||||||
|
cg.page = page % pages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cg *CharacterGenerator) nextPage() {
|
||||||
|
cg.setPage(cg.page + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cg *CharacterGenerator) getPixel(char uint8, row int, column int) bool {
|
||||||
|
bits := cg.data[int(char)*8+row+cg.page*rev7CharGenSize]
|
||||||
|
var bit int
|
||||||
|
if cg.columnMap != nil {
|
||||||
|
bit = cg.columnMap(column)
|
||||||
|
} else {
|
||||||
|
// Standard Apple 2 mapping
|
||||||
|
bit = 6 - column
|
||||||
|
}
|
||||||
|
value := bits >> uint(bit) & 1
|
||||||
|
return value == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cg *CharacterGenerator) dumpCharRaw(char int) {
|
||||||
base := int(char) * 8
|
base := int(char) * 8
|
||||||
fmt.Printf("Char: %v\n---------\n", char)
|
fmt.Printf("Char: %v\n---------\n", char)
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
fmt.Print("|")
|
fmt.Print("|")
|
||||||
b := cg.data[base+i]
|
b := cg.data[base+i]
|
||||||
for j := 6; j >= 0; j-- {
|
for j := 0; j < 8; j++ {
|
||||||
if (b>>uint(j))&1 == 1 {
|
if (b>>uint(j))&1 == 1 {
|
||||||
fmt.Print("#")
|
fmt.Print("#")
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,7 +105,12 @@ func (cg *CharacterGenerator) dumpChar(char uint8) {
|
||||||
|
|
||||||
// Dump to sdtout all the character maps
|
// Dump to sdtout all the character maps
|
||||||
func (cg *CharacterGenerator) Dump() {
|
func (cg *CharacterGenerator) Dump() {
|
||||||
|
pages := len(cg.data) / rev7CharGenSize
|
||||||
|
for p := 0; p < pages; p++ {
|
||||||
|
cg.setPage(p)
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
cg.dumpChar(uint8(i))
|
cg.dumpChar(uint8(i))
|
||||||
|
//cg.dumpCharRaw(int(i))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,13 @@ const (
|
||||||
internalPrefix = "<internal>/"
|
internalPrefix = "<internal>/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func isInternalResource(filename string) bool {
|
||||||
|
return strings.HasPrefix(filename, internalPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
func loadResource(filename string) []uint8 {
|
func loadResource(filename string) []uint8 {
|
||||||
var file io.Reader
|
var file io.Reader
|
||||||
if strings.HasPrefix(filename, internalPrefix) {
|
if isInternalResource(filename) {
|
||||||
// load from embedded resource
|
// load from embedded resource
|
||||||
resource := strings.TrimPrefix(filename, internalPrefix)
|
resource := strings.TrimPrefix(filename, internalPrefix)
|
||||||
resourceFile, err := romdumps.Assets.Open(resource)
|
resourceFile, err := romdumps.Assets.Open(resource)
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue