Support for character generator pages. F10 to change page.

This commit is contained in:
Ivan Izaguirre 2019-06-09 23:54:27 +02:00
parent 68d68eadfc
commit 047233f16b
10 changed files with 98 additions and 23 deletions

View File

@ -84,6 +84,7 @@ Line:
- F6: Toggle between NTSC color TV and green phosphor monochrome monitor
- F7: Save current state to 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`
Only valid on SDL mode
@ -91,6 +92,8 @@ Only valid on SDL mode
### Command line options
```
-base64a
setup a Base64A clone
-charRom string
rom file for the character generator (default "<internal>/Apple2rev7CharGen.rom")
-disk string

View File

@ -13,6 +13,7 @@ import (
// Apple2 represents all the components and state of the emulated machine
type Apple2 struct {
Name string
cpu *core6502.State
mmu *memoryManager
io *ioC0Page
@ -84,6 +85,8 @@ const (
CommandLoadState
// CommandDumpDebugInfo dumps usefull info
CommandDumpDebugInfo
// CommandNextCharGenPage cycles the CharGen page if several
CommandNextCharGenPage
)
// SendCommand enqueues a command to the emulator thread
@ -111,6 +114,9 @@ func (a *Apple2) executeCommand(command int) {
a.load("apple2.state")
case CommandDumpDebugInfo:
a.dumpDebugInfo()
case CommandNextCharGenPage:
a.cg.nextPage()
fmt.Printf("Chargen page %v\n", a.cg.page)
}
}

View File

@ -7,6 +7,7 @@ func NewApple2(charRomFile string, clockMhz float64,
isColor bool, fastMode bool, panicSS bool) *Apple2 {
var a Apple2
a.Name = "Apple ][+"
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu)
if charRomFile != "" {

View File

@ -66,13 +66,6 @@ func MainApple() *Apple2 {
)
flag.Parse()
if *dumpChars {
cg := NewCharacterGenerator(*charRomFile)
cg.Dump()
os.Exit(0)
return nil
}
a := NewApple2(*charRomFile, *cpuClock, !*mono, *fastDisk, *panicSS)
if *base64a {
NewBase64a(a)
@ -92,5 +85,11 @@ func MainApple() *Apple2 {
//a.AddCardInOut(2)
//a.AddCardLogger(4)
if *dumpChars {
a.cg.Dump()
os.Exit(0)
return nil
}
return a
}

View File

@ -26,7 +26,7 @@ func SDLRun(a *apple2.Apple2) {
defer window.Destroy()
defer renderer.Destroy()
window.SetTitle("Apple2")
window.SetTitle(a.Name)
kp := newSDLKeyBoard(a)
a.SetKeyboardProvider(kp)

View File

@ -101,6 +101,8 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) {
k.a.SendCommand(apple2.CommandLoadState)
case sdl.K_F9:
k.a.SendCommand(apple2.CommandDumpDebugInfo)
case sdl.K_F10:
k.a.SendCommand(apple2.CommandNextCharGenPage)
case sdl.K_F12:
apple2.SaveSnapshot(k.a, "snapshot.png")
}

View File

@ -15,14 +15,32 @@ type Base64a struct {
// NewBase64a instantiates an apple2
func NewBase64a(a *Apple2) *Base64a {
var b Base64a
b.a = a
a.Name = "Base 64A"
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
}
func base64aCharGenColumnsMap(column int) int {
bit := column + 2
// Weird positions
if column == 6 {
bit = 2
} else if column == 0 {
bit = 1
}
return bit
}
const (
// There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill
// 2 or 4 banks with 2kb windows.
@ -87,7 +105,7 @@ func (b *Base64a) loadRom() {
func (b *Base64a) changeRomBank(bank uint8) {
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.resetRomPaging() // If rom was not active. This is going to far?
}

View File

@ -11,11 +11,17 @@ import (
// CharacterGenerator represents the ROM wth the characters bitmaps
type CharacterGenerator struct {
data []uint8
data []uint8
customRom bool
columnMap charColumnMap
page int
}
type charColumnMap func(column int) int
const (
rev7CharGenSize = 2048
rev7CharGenSize = 2048
defaultCharGenROM = "<internal>/Apple2rev7CharGen.rom"
)
// 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) {
cg.customRom = !isInternalResource(filename)
bytes := loadResource(filename)
size := len(bytes)
if size != rev7CharGenSize {
if size < rev7CharGenSize {
panic("Character ROM size not supported")
}
cg.data = bytes
}
func (cg *CharacterGenerator) getPixel(char uint8, row int, column int) bool {
bits := cg.data[int(char)*8+row]
bit := bits >> (uint(6 - column)) & 1
return bit == 1
func (cg *CharacterGenerator) setColumnMap(columnMap charColumnMap) {
// Regular Apple II uses bits 6 to 0 but some clones have other mappings
cg.columnMap = columnMap
}
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
fmt.Printf("Char: %v\n---------\n", char)
for i := 0; i < 8; i++ {
fmt.Print("|")
b := cg.data[base+i]
for j := 6; j >= 0; j-- {
for j := 0; j < 8; j++ {
if (b>>uint(j))&1 == 1 {
fmt.Print("#")
} else {
@ -76,7 +105,12 @@ func (cg *CharacterGenerator) dumpChar(char uint8) {
// Dump to sdtout all the character maps
func (cg *CharacterGenerator) Dump() {
for i := 0; i < 256; i++ {
cg.dumpChar(uint8(i))
pages := len(cg.data) / rev7CharGenSize
for p := 0; p < pages; p++ {
cg.setPage(p)
for i := 0; i < 256; i++ {
cg.dumpChar(uint8(i))
//cg.dumpCharRaw(int(i))
}
}
}

View File

@ -13,9 +13,13 @@ const (
internalPrefix = "<internal>/"
)
func isInternalResource(filename string) bool {
return strings.HasPrefix(filename, internalPrefix)
}
func loadResource(filename string) []uint8 {
var file io.Reader
if strings.HasPrefix(filename, internalPrefix) {
if isInternalResource(filename) {
// load from embedded resource
resource := strings.TrimPrefix(filename, internalPrefix)
resourceFile, err := romdumps.Assets.Open(resource)

File diff suppressed because one or more lines are too long