Improved memory manager to support Apple2e shadow ROM
This commit is contained in:
parent
a28536d561
commit
d2c897f7ea
|
@ -1,110 +0,0 @@
|
||||||
package apple2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// See https://i.stack.imgur.com/yn21s.gif
|
|
||||||
|
|
||||||
type addressSpace struct {
|
|
||||||
activeMemory *pagedMemory
|
|
||||||
physicalRAM [256]ramPage // up to 64 Kb
|
|
||||||
physicalROM [48]romPage // up to 12 Kb
|
|
||||||
ioPage ioC0Page
|
|
||||||
textPages1 *textPages
|
|
||||||
activeSlow int // Slot that has the addressing 0xc800 to 0ccfff
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
ioAreaMask uint16 = 0xFF80
|
|
||||||
ioAreaValue uint16 = 0xC000
|
|
||||||
ioC8Off uint16 = 0xCFFF
|
|
||||||
)
|
|
||||||
|
|
||||||
// Peek returns the data on the given address
|
|
||||||
func (a *addressSpace) Peek(address uint16) uint8 {
|
|
||||||
if address == ioC8Off {
|
|
||||||
a.resetSlotRoms()
|
|
||||||
}
|
|
||||||
if (address & ioAreaMask) == ioAreaValue {
|
|
||||||
return a.ioPage.Peek(uint8(address))
|
|
||||||
}
|
|
||||||
return a.activeMemory.Peek(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poke sets the data at the given address
|
|
||||||
func (a *addressSpace) Poke(address uint16, value uint8) {
|
|
||||||
if address == ioC8Off {
|
|
||||||
a.resetSlotRoms()
|
|
||||||
}
|
|
||||||
if (address & ioAreaMask) == ioAreaValue {
|
|
||||||
a.ioPage.Poke(uint8(address), value)
|
|
||||||
}
|
|
||||||
a.activeMemory.Poke(address, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *addressSpace) resetSlotRoms() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAddressSpace() *addressSpace {
|
|
||||||
var a addressSpace
|
|
||||||
|
|
||||||
var m pagedMemory
|
|
||||||
a.activeMemory = &m
|
|
||||||
|
|
||||||
// Assign RAM from 0x0000 to 0xbfff, 48kb
|
|
||||||
for i := 0; i <= 0xbf; i++ {
|
|
||||||
m.SetPage(uint8(i), &(a.physicalRAM[i]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign ROM from 0xd000 to 0xfff, 12 kb. The ROM is empty
|
|
||||||
for i := 0xd0; i <= 0xff; i++ {
|
|
||||||
m.SetPage(uint8(i), &(a.physicalROM[i-0xd0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the 0xc000 to 0xcfff as unasigned, 4kb. It wil be taken by slot cards.
|
|
||||||
for i := uint8(0xc0); i <= 0xcf; i++ {
|
|
||||||
var p unassignedPage
|
|
||||||
p.page = i
|
|
||||||
m.SetPage(i, &p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace RAM in the TEXT1 area.
|
|
||||||
// TODO: treat as normal ram. Add is dirty in all RAM pages
|
|
||||||
var t textPages
|
|
||||||
a.textPages1 = &t
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
m.SetPage(uint8(4+i), &(t.pages[i]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &a
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRom loads a binary file to the top of the memory.
|
|
||||||
func (a *addressSpace) loadRom(filename string) {
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
stats, statsErr := f.Stat()
|
|
||||||
if statsErr != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
size := stats.Size()
|
|
||||||
if size != 12288 {
|
|
||||||
panic("Rom size not supported")
|
|
||||||
}
|
|
||||||
bytes := make([]byte, size)
|
|
||||||
|
|
||||||
buf := bufio.NewReader(f)
|
|
||||||
buf.Read(bytes)
|
|
||||||
|
|
||||||
for i, v := range bytes {
|
|
||||||
a.physicalROM[i>>8].burn(uint8(i), uint8(v))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,15 +4,17 @@ import "go6502/core6502"
|
||||||
|
|
||||||
// Run instantiates an apple2 and start emulation
|
// Run instantiates an apple2 and start emulation
|
||||||
func Run(romFile string, log bool) {
|
func Run(romFile string, log bool) {
|
||||||
a := newAddressSpace()
|
mmu := newAddressSpace(romFile)
|
||||||
a.loadRom(romFile)
|
if mmu.isApple2e {
|
||||||
|
addApple2ESoftSwitches(mmu)
|
||||||
|
}
|
||||||
|
|
||||||
var s core6502.State
|
var s core6502.State
|
||||||
s.Mem = a
|
s.Mem = mmu
|
||||||
|
|
||||||
var fe ansiConsoleFrontend
|
var fe ansiConsoleFrontend
|
||||||
a.ioPage.setKeyboardProvider(&fe)
|
mmu.ioPage.setKeyboardProvider(&fe)
|
||||||
go fe.textModeGoRoutine(a.textPages1)
|
go fe.textModeGoRoutine(mmu.textPages1)
|
||||||
|
|
||||||
// Start the processor
|
// Start the processor
|
||||||
core6502.Reset(&s)
|
core6502.Reset(&s)
|
||||||
|
|
|
@ -5,12 +5,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ioC0Page struct {
|
type ioC0Page struct {
|
||||||
ioFlags uint64
|
softSwitches [128]softSwitch
|
||||||
data [1]uint8
|
softSwitchesData [128]uint8
|
||||||
keyboard keyboardProvider
|
keyboard keyboardProvider
|
||||||
addressSpace *addressSpace
|
mmu *memoryManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type softSwitch func(io *ioC0Page, isWrite bool, value uint8) uint8
|
||||||
|
|
||||||
|
// TODO: change interface to func
|
||||||
type keyboardProvider interface {
|
type keyboardProvider interface {
|
||||||
getKey() (key uint8, ok bool)
|
getKey() (key uint8, ok bool)
|
||||||
}
|
}
|
||||||
|
@ -19,44 +22,52 @@ type keyboardProvider interface {
|
||||||
// See https://stason.org/TULARC/pc/apple2/programmer/004-I-d-like-to-do-some-serious-Apple-II-programming-Whe.html
|
// See https://stason.org/TULARC/pc/apple2/programmer/004-I-d-like-to-do-some-serious-Apple-II-programming-Whe.html
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ioFlagNone uint8 = 0
|
ioDataKeyboard uint8 = 0x10
|
||||||
ioFlagGraphics uint8 = 3
|
ioFlagGraphics uint8 = 0x50
|
||||||
ioFlagMixed uint8 = 8
|
ioFlagMixed uint8 = 0x52
|
||||||
ioFlagSecondPage uint8 = 1
|
ioFlagSecondPage uint8 = 0x54
|
||||||
ioFlagHiRes uint8 = 2
|
ioFlagHiRes uint8 = 0x56
|
||||||
ioFlagAnnunciator0 uint8 = 4
|
ioFlagAnnunciator0 uint8 = 0x58
|
||||||
ioFlagAnnunciator1 uint8 = 5
|
ioFlagAnnunciator1 uint8 = 0x5a
|
||||||
ioFlagAnnunciator2 uint8 = 6
|
ioFlagAnnunciator2 uint8 = 0x5c
|
||||||
ioFlagAnnunciator3 uint8 = 7
|
ioFlagAnnunciator3 uint8 = 0x5e
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func (p *ioC0Page) isSoftSwitchExtActive(ioFlag uint8) bool {
|
||||||
ioDataKeyboard uint8 = 0
|
return (p.softSwitchesData[ioFlag] & 0x08) == 0x80
|
||||||
)
|
|
||||||
|
|
||||||
type softSwitch struct {
|
|
||||||
ioFlag uint8
|
|
||||||
value bool
|
|
||||||
onWriteOnly bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var softSwitches = [256]softSwitch{
|
func newIoC0Page(mmu *memoryManager) *ioC0Page {
|
||||||
0x50: softSwitch{ioFlagGraphics, false, false},
|
var p ioC0Page
|
||||||
0x51: softSwitch{ioFlagGraphics, true, false},
|
p.mmu = mmu
|
||||||
0x52: softSwitch{ioFlagMixed, false, false},
|
ss := &p.softSwitches
|
||||||
0x53: softSwitch{ioFlagMixed, true, false},
|
|
||||||
0x54: softSwitch{ioFlagSecondPage, false, false},
|
ss[0x00] = getKeySoftSwitch // Keyboard
|
||||||
0x55: softSwitch{ioFlagSecondPage, true, false},
|
ss[0x10] = strobeKeyboardSoftSwitch // Keyboard Strobe
|
||||||
0x56: softSwitch{ioFlagHiRes, false, false},
|
ss[0x30] = notImplementedSoftSwitch // Speaker
|
||||||
0x57: softSwitch{ioFlagHiRes, true, false},
|
|
||||||
0x58: softSwitch{ioFlagAnnunciator0, false, false},
|
ss[0x50] = getSoftSwitch(ioFlagGraphics, false)
|
||||||
0x59: softSwitch{ioFlagAnnunciator0, true, false},
|
ss[0x51] = getSoftSwitch(ioFlagGraphics, true)
|
||||||
0x5a: softSwitch{ioFlagAnnunciator1, false, false},
|
ss[0x52] = getSoftSwitch(ioFlagMixed, false)
|
||||||
0x5b: softSwitch{ioFlagAnnunciator1, true, false},
|
ss[0x53] = getSoftSwitch(ioFlagMixed, true)
|
||||||
0x5c: softSwitch{ioFlagAnnunciator2, false, false},
|
ss[0x54] = getSoftSwitch(ioFlagSecondPage, false)
|
||||||
0x5d: softSwitch{ioFlagAnnunciator2, true, false},
|
ss[0x55] = getSoftSwitch(ioFlagSecondPage, true)
|
||||||
0x5e: softSwitch{ioFlagAnnunciator3, false, false},
|
ss[0x56] = getSoftSwitch(ioFlagHiRes, false)
|
||||||
0x5f: softSwitch{ioFlagAnnunciator3, true, false},
|
ss[0x57] = getSoftSwitch(ioFlagHiRes, true)
|
||||||
|
ss[0x58] = getSoftSwitch(ioFlagAnnunciator0, false)
|
||||||
|
ss[0x59] = getSoftSwitch(ioFlagAnnunciator0, true)
|
||||||
|
ss[0x5a] = getSoftSwitch(ioFlagAnnunciator1, false)
|
||||||
|
ss[0x5b] = getSoftSwitch(ioFlagAnnunciator1, true)
|
||||||
|
ss[0x5c] = getSoftSwitch(ioFlagAnnunciator2, false)
|
||||||
|
ss[0x5d] = getSoftSwitch(ioFlagAnnunciator2, true)
|
||||||
|
ss[0x5e] = getSoftSwitch(ioFlagAnnunciator3, false)
|
||||||
|
ss[0x5f] = getSoftSwitch(ioFlagAnnunciator3, true)
|
||||||
|
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) setKeyboardProvider(kb keyboardProvider) {
|
||||||
|
p.keyboard = kb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) Peek(address uint8) uint8 {
|
func (p *ioC0Page) Peek(address uint8) uint8 {
|
||||||
|
@ -70,47 +81,47 @@ func (p *ioC0Page) Poke(address uint8, value uint8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) access(address uint8, isWrite bool, value uint8) uint8 {
|
func (p *ioC0Page) access(address uint8, isWrite bool, value uint8) uint8 {
|
||||||
|
// The second hals of the pages is reserved for slots
|
||||||
ss := softSwitches[address]
|
if address >= 0x80 {
|
||||||
if ss.ioFlag != ioFlagNone {
|
// TODO reserved slots data
|
||||||
if !isWrite || !!ss.onWriteOnly {
|
return 0
|
||||||
if ss.value {
|
|
||||||
p.ioFlags |= 1 << ss.ioFlag
|
|
||||||
} else {
|
|
||||||
p.ioFlags &^= 1 << ss.ioFlag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch address {
|
|
||||||
case 0x00: // keyboard (Is this the full range 0x0?)
|
|
||||||
return p.getKey()
|
|
||||||
case 0x10: // strobe (Is this the full range 0x1?)
|
|
||||||
return p.strobeKeyboard()
|
|
||||||
case 0x30: // spkr
|
|
||||||
// TODO: Support sound
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("Unknown softswitch 0xC0%02x", address))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss := p.softSwitches[address]
|
||||||
|
if ss == nil {
|
||||||
|
panic(fmt.Sprintf("Unknown softswitch 0xC0%02x", address))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss(p, isWrite, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSoftSwitch(ioFlag uint8, isSet bool) softSwitch {
|
||||||
|
return func(io *ioC0Page, isWrite bool, value uint8) uint8 {
|
||||||
|
if isSet {
|
||||||
|
io.softSwitchesData[ioFlag] = 0x80
|
||||||
|
} else {
|
||||||
|
io.softSwitchesData[ioFlag] = 0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func notImplementedSoftSwitch(*ioC0Page, bool, uint8) uint8 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) setKeyboardProvider(kb keyboardProvider) {
|
func getKeySoftSwitch(p *ioC0Page, _ bool, _ uint8) uint8 {
|
||||||
p.keyboard = kb
|
strobed := (p.softSwitchesData[ioDataKeyboard] & (1 << 7)) == 0
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ioC0Page) getKey() uint8 {
|
|
||||||
strobed := (p.data[ioDataKeyboard] & (1 << 7)) == 0
|
|
||||||
if strobed && p.keyboard != nil {
|
if strobed && p.keyboard != nil {
|
||||||
if key, ok := p.keyboard.getKey(); ok {
|
if key, ok := p.keyboard.getKey(); ok {
|
||||||
p.data[ioDataKeyboard] = key + (1 << 7)
|
p.softSwitchesData[ioDataKeyboard] = key + (1 << 7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.data[ioDataKeyboard]
|
return p.softSwitchesData[ioDataKeyboard]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) strobeKeyboard() uint8 {
|
func strobeKeyboardSoftSwitch(p *ioC0Page, _ bool, _ uint8) uint8 {
|
||||||
result := p.data[ioDataKeyboard]
|
result := p.softSwitchesData[ioDataKeyboard]
|
||||||
p.data[ioDataKeyboard] &^= 1 << 7
|
p.softSwitchesData[ioDataKeyboard] &^= 1 << 7
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
package apple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://fabiensanglard.net/fd_proxy/prince_of_persia/Inside%20the%20Apple%20IIe.pdf
|
||||||
|
// See https://i.stack.imgur.com/yn21s.gif
|
||||||
|
|
||||||
|
type memoryManager struct {
|
||||||
|
// Map of assigned pages
|
||||||
|
activeMemory *pagedMemory
|
||||||
|
// Pages prepared to be paged in and out
|
||||||
|
physicalMainRAM []ramPage // 0x0000 to 0xbfff, Up to 48 Kb
|
||||||
|
physicalROM []romPage // 0xd000 to 0xffff, 12 Kb
|
||||||
|
physicalROMe []romPage // 0xc000 to 0xcfff, Zero or 4bk in the Apple2e
|
||||||
|
unassignedExpansionROM []unassignedPage // 0xc000 to 0xcfff
|
||||||
|
ioPage *ioC0Page // 0xc000 to 0xc080
|
||||||
|
isApple2e bool
|
||||||
|
textPages1 *textPages // 0x0400 to 0x07ff
|
||||||
|
activeSlow int // Slot that has the addressing 0xc800 to 0ccfff
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ioAreaMask uint16 = 0xFF80
|
||||||
|
ioAreaValue uint16 = 0xC000
|
||||||
|
ioC8Off uint16 = 0xCFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peek returns the data on the given address
|
||||||
|
func (mmu *memoryManager) Peek(address uint16) uint8 {
|
||||||
|
if address == ioC8Off {
|
||||||
|
mmu.resetSlotExpansionRoms()
|
||||||
|
}
|
||||||
|
return mmu.activeMemory.Peek(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poke sets the data at the given address
|
||||||
|
func (mmu *memoryManager) Poke(address uint16, value uint8) {
|
||||||
|
if address == ioC8Off {
|
||||||
|
mmu.resetSlotExpansionRoms()
|
||||||
|
}
|
||||||
|
mmu.activeMemory.Poke(address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When 0xcfff is accessed the card expansion rom is unassigned
|
||||||
|
func (mmu *memoryManager) resetSlotExpansionRoms() {
|
||||||
|
if mmu.ioPage.isSoftSwitchExtActive(ioFlagIntCxRom) {
|
||||||
|
// Ignore if the Apple2 shadow ROM is active
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 8; i < 16; i++ {
|
||||||
|
p := mmu.unassignedExpansionROM[i]
|
||||||
|
mmu.activeMemory.SetPage(uint8(i+0xc0), &p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAddressSpace(romImage string) *memoryManager {
|
||||||
|
var mmu memoryManager
|
||||||
|
|
||||||
|
var m pagedMemory
|
||||||
|
mmu.activeMemory = &m
|
||||||
|
|
||||||
|
// Assign RAM from 0x0000 to 0xbfff, 48kb
|
||||||
|
mmu.physicalMainRAM = make([]ramPage, 0xc0)
|
||||||
|
for i := 0; i <= 0xbf; i++ {
|
||||||
|
m.SetPage(uint8(i), &(mmu.physicalMainRAM[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
mmu.loadRom(romImage)
|
||||||
|
// Assign the first 12kb of ROM from 0xd000 to 0xfff
|
||||||
|
for i := 0xd0; i <= 0xff; i++ {
|
||||||
|
m.SetPage(uint8(i), &(mmu.physicalROM[i-0xd0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the io in 0xc000
|
||||||
|
mmu.ioPage = newIoC0Page(&mmu)
|
||||||
|
m.SetPage(0xc0, mmu.ioPage)
|
||||||
|
|
||||||
|
// Set the 0xc100 to 0xcfff as unasigned, 4kb. It wil be taken by slot cards.
|
||||||
|
mmu.unassignedExpansionROM = make([]unassignedPage, 0x10)
|
||||||
|
for i := 1; i < 0x10; i++ {
|
||||||
|
page := uint8(i + 0xc0)
|
||||||
|
p := &mmu.unassignedExpansionROM[i]
|
||||||
|
p.page = page
|
||||||
|
m.SetPage(page, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace RAM in the TEXT1 area.
|
||||||
|
// TODO: treat as normal ram. Add is dirty in all RAM pages
|
||||||
|
var t textPages
|
||||||
|
mmu.textPages1 = &t
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
m.SetPage(uint8(4+i), &(t.pages[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mmu
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadRom loads a binary file to the top of the memory.
|
||||||
|
const (
|
||||||
|
apple2RomSize = 12 * 1024
|
||||||
|
apple2eRomSize = 16 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
func (mmu *memoryManager) loadRom(filename string) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
stats, statsErr := f.Stat()
|
||||||
|
if statsErr != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := stats.Size()
|
||||||
|
if size != apple2RomSize && size != apple2eRomSize {
|
||||||
|
panic("Rom size not supported")
|
||||||
|
}
|
||||||
|
bytes := make([]byte, size)
|
||||||
|
buf := bufio.NewReader(f)
|
||||||
|
buf.Read(bytes)
|
||||||
|
|
||||||
|
romStart := 0
|
||||||
|
if size == apple2eRomSize {
|
||||||
|
// The extra 4kb ROM is first in the rom file.
|
||||||
|
// It starts with 256 unused bytes not mapped to 0xc000.
|
||||||
|
mmu.isApple2e = true
|
||||||
|
extraRomSize := apple2eRomSize - apple2RomSize
|
||||||
|
mmu.physicalROMe = make([]romPage, extraRomSize>>8)
|
||||||
|
for i := 0; i < extraRomSize; i++ {
|
||||||
|
mmu.physicalROMe[i>>8].burn(uint8(i), bytes[i])
|
||||||
|
}
|
||||||
|
romStart = extraRomSize
|
||||||
|
}
|
||||||
|
|
||||||
|
mmu.physicalROM = make([]romPage, apple2RomSize>>8)
|
||||||
|
for i := 0; i < apple2RomSize; i++ {
|
||||||
|
mmu.physicalROM[i>>8].burn(uint8(i), bytes[i+romStart])
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package apple2
|
package apple2
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
// memoryPage is a data page of 256 bytes
|
// memoryPage is a data page of 256 bytes
|
||||||
type memoryPage interface {
|
type memoryPage interface {
|
||||||
|
@ -24,12 +27,11 @@ func (m *pagedMemory) Peek(address uint16) uint8 {
|
||||||
func (m *pagedMemory) Poke(address uint16, value uint8) {
|
func (m *pagedMemory) Poke(address uint16, value uint8) {
|
||||||
hi := uint8(address >> 8)
|
hi := uint8(address >> 8)
|
||||||
lo := uint8(address)
|
lo := uint8(address)
|
||||||
//fmt.Println(hi)
|
|
||||||
m.data[hi].Poke(lo, value)
|
m.data[hi].Poke(lo, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPage assigns a MemoryPage implementation on the page given
|
// SetPage assigns a MemoryPage implementation on the page given
|
||||||
func (m *pagedMemory) SetPage(index uint8, page memoryPage) {
|
func (m *pagedMemory) SetPage(index uint8, page memoryPage) {
|
||||||
fmt.Printf("Seeting page 0x%02x\n", index)
|
fmt.Printf("Assigning page 0x%02x type %s\n", index, reflect.TypeOf(page))
|
||||||
m.data[index] = page
|
m.data[index] = page
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,74 @@
|
||||||
|
package apple2
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ioFlagIntCxRom uint8 = 0x15
|
||||||
|
ioFlagSlotC3Rom uint8 = 0x17
|
||||||
|
)
|
||||||
|
|
||||||
|
func addApple2ESoftSwitches(mmu *memoryManager) {
|
||||||
|
ss := &mmu.ioPage.softSwitches
|
||||||
|
ss[0x06] = getSoftSwitchExt(ioFlagIntCxRom, 0x00, softSwitchIntCxRomOff)
|
||||||
|
ss[0x07] = getSoftSwitchExt(ioFlagIntCxRom, 0x80, softSwitchIntCxRomOn)
|
||||||
|
ss[0x15] = getStatusSoftSwitchExt(ioFlagIntCxRom)
|
||||||
|
|
||||||
|
ss[0x0A] = getSoftSwitchExt(ioFlagSlotC3Rom, 0x00, softSwitchSlotC3RomOff)
|
||||||
|
ss[0x0B] = getSoftSwitchExt(ioFlagSlotC3Rom, 0x80, softSwitchSlotC3RomOn)
|
||||||
|
ss[0x17] = getStatusSoftSwitchExt(ioFlagSlotC3Rom)
|
||||||
|
|
||||||
|
ss[0x1c] = getStatusSoftSwitchExt(ioFlagSecondPage)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type softSwitchExtAction func(io *ioC0Page)
|
||||||
|
|
||||||
|
func getStatusSoftSwitchExt(ioFlag uint8) softSwitch {
|
||||||
|
return func(io *ioC0Page, isWrite bool, value uint8) uint8 {
|
||||||
|
return io.softSwitchesData[ioFlag]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSoftSwitchExt(ioFlag uint8, dstValue uint8, action softSwitchExtAction) softSwitch {
|
||||||
|
return func(io *ioC0Page, isWrite bool, value uint8) uint8 {
|
||||||
|
fmt.Printf("Softswitch 0x%02x %v %v\n", ioFlag, isWrite, dstValue)
|
||||||
|
if !isWrite {
|
||||||
|
return 0 // Ignore reads
|
||||||
|
}
|
||||||
|
currentValue := io.softSwitchesData[ioFlag]
|
||||||
|
if currentValue == dstValue {
|
||||||
|
return 0 // Already switched, ignore
|
||||||
|
}
|
||||||
|
action(io)
|
||||||
|
io.softSwitchesData[ioFlag] = value
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func softSwitchIntCxRomOn(io *ioC0Page) {
|
||||||
|
for i := uint8(1); i < 16; i++ {
|
||||||
|
io.mmu.activeMemory.SetPage(uint8(0xc0+i), &io.mmu.physicalROMe[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func softSwitchIntCxRomOff(io *ioC0Page) {
|
||||||
|
// TODO restore all the ROM from the slot for 0xc1 to 0xc7
|
||||||
|
for i := 1; i <= 16; i++ {
|
||||||
|
io.mmu.activeMemory.SetPage(uint8(0xc0+i), &io.mmu.unassignedExpansionROM[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func softSwitchSlotC3RomOn(io *ioC0Page) {
|
||||||
|
if io.isSoftSwitchExtActive(ioFlagIntCxRom) {
|
||||||
|
return // Ignore if allt the Apple2 shadow ROM is active
|
||||||
|
}
|
||||||
|
// TODO restore the slot 3 ROM
|
||||||
|
io.mmu.activeMemory.SetPage(0xC3, &io.mmu.unassignedExpansionROM[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func softSwitchSlotC3RomOff(io *ioC0Page) {
|
||||||
|
if io.isSoftSwitchExtActive(ioFlagIntCxRom) {
|
||||||
|
return // Ignore if allt the Apple2 shadow ROM is active
|
||||||
|
}
|
||||||
|
io.mmu.activeMemory.SetPage(0xC3, &io.mmu.physicalROMe[3])
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import "fmt"
|
||||||
|
|
||||||
// State represents the state of the simulated device
|
// State represents the state of the simulated device
|
||||||
type State struct {
|
type State struct {
|
||||||
Reg Registers
|
Reg registers
|
||||||
Mem Memory
|
Mem Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,25 +41,46 @@ func getWordInLine(line []uint8) uint16 {
|
||||||
return uint16(line[1]) + 0x100*uint16(line[2])
|
return uint16(line[1]) + 0x100*uint16(line[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolve(s *State, line []uint8, opcode opcode) (value uint8, address uint16, setValue func(uint8)) {
|
func resolveValue(s *State, line []uint8, opcode opcode) uint8 {
|
||||||
|
getValue, _, _ := resolve(s, line, opcode)
|
||||||
|
return getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveGetSetValue(s *State, line []uint8, opcode opcode) (value uint8, setValue func(uint8)) {
|
||||||
|
getValue, setValue, _ := resolve(s, line, opcode)
|
||||||
|
value = getValue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveSetValue(s *State, line []uint8, opcode opcode) func(uint8) {
|
||||||
|
_, setValue, _ := resolve(s, line, opcode)
|
||||||
|
return setValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
||||||
|
_, _, address := resolve(s, line, opcode)
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolve(s *State, line []uint8, opcode opcode) (getValue func() uint8, setValue func(uint8), address uint16) {
|
||||||
hasAddress := true
|
hasAddress := true
|
||||||
register := regNone
|
register := regNone
|
||||||
|
|
||||||
switch opcode.addressMode {
|
switch opcode.addressMode {
|
||||||
case modeAccumulator:
|
case modeAccumulator:
|
||||||
value = s.Reg.getA()
|
getValue = func() uint8 { return s.Reg.getA() }
|
||||||
hasAddress = false
|
hasAddress = false
|
||||||
register = regA
|
register = regA
|
||||||
case modeImplicitX:
|
case modeImplicitX:
|
||||||
value = s.Reg.getX()
|
getValue = func() uint8 { return s.Reg.getX() }
|
||||||
hasAddress = false
|
hasAddress = false
|
||||||
register = regX
|
register = regX
|
||||||
case modeImplicitY:
|
case modeImplicitY:
|
||||||
value = s.Reg.getY()
|
getValue = func() uint8 { return s.Reg.getY() }
|
||||||
hasAddress = false
|
hasAddress = false
|
||||||
register = regY
|
register = regY
|
||||||
case modeImmediate:
|
case modeImmediate:
|
||||||
value = line[1]
|
getValue = func() uint8 { return line[1] }
|
||||||
hasAddress = false
|
hasAddress = false
|
||||||
case modeZeroPage:
|
case modeZeroPage:
|
||||||
address = uint16(line[1])
|
address = uint16(line[1])
|
||||||
|
@ -85,7 +106,7 @@ func resolve(s *State, line []uint8, opcode opcode) (value uint8, address uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasAddress {
|
if hasAddress {
|
||||||
value = s.Mem.Peek(address)
|
getValue = func() uint8 { return s.Mem.Peek(address) }
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue = func(value uint8) {
|
setValue = func(value uint8) {
|
||||||
|
@ -122,7 +143,7 @@ func buildOpTransfer(regSrc int, regDst int) opFunc {
|
||||||
|
|
||||||
func buildOpIncDec(inc bool) opFunc {
|
func buildOpIncDec(inc bool) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, setValue := resolve(s, line, opcode)
|
value, setValue := resolveGetSetValue(s, line, opcode)
|
||||||
if inc {
|
if inc {
|
||||||
value++
|
value++
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,7 +156,7 @@ func buildOpIncDec(inc bool) opFunc {
|
||||||
|
|
||||||
func buildOpShift(isLeft bool, isRotate bool) opFunc {
|
func buildOpShift(isLeft bool, isRotate bool) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, setValue := resolve(s, line, opcode)
|
value, setValue := resolveGetSetValue(s, line, opcode)
|
||||||
|
|
||||||
oldCarry := s.Reg.getFlagBit(flagC)
|
oldCarry := s.Reg.getFlagBit(flagC)
|
||||||
var carry bool
|
var carry bool
|
||||||
|
@ -160,7 +181,7 @@ func buildOpShift(isLeft bool, isRotate bool) opFunc {
|
||||||
|
|
||||||
func buildOpLoad(regDst int) opFunc {
|
func buildOpLoad(regDst int) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, _ := resolve(s, line, opcode)
|
value := resolveValue(s, line, opcode)
|
||||||
s.Reg.setRegister(regDst, value)
|
s.Reg.setRegister(regDst, value)
|
||||||
s.Reg.updateFlagZN(value)
|
s.Reg.updateFlagZN(value)
|
||||||
}
|
}
|
||||||
|
@ -168,7 +189,7 @@ func buildOpLoad(regDst int) opFunc {
|
||||||
|
|
||||||
func buildOpStore(regSrc int) opFunc {
|
func buildOpStore(regSrc int) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
_, _, setValue := resolve(s, line, opcode)
|
setValue := resolveSetValue(s, line, opcode)
|
||||||
value := s.Reg.getRegister(regSrc)
|
value := s.Reg.getRegister(regSrc)
|
||||||
setValue(value)
|
setValue(value)
|
||||||
}
|
}
|
||||||
|
@ -192,7 +213,7 @@ func buildOpBranch(flag uint8, value bool) opFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opBIT(s *State, line []uint8, opcode opcode) {
|
func opBIT(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, _ := resolve(s, line, opcode)
|
value := resolveValue(s, line, opcode)
|
||||||
acc := s.Reg.getA()
|
acc := s.Reg.getA()
|
||||||
// Future note: The immediate addressing mode (65C02 or 65816 only) does not affect V.
|
// Future note: The immediate addressing mode (65C02 or 65816 only) does not affect V.
|
||||||
s.Reg.updateFlag(flagZ, value&acc == 0)
|
s.Reg.updateFlag(flagZ, value&acc == 0)
|
||||||
|
@ -202,7 +223,7 @@ func opBIT(s *State, line []uint8, opcode opcode) {
|
||||||
|
|
||||||
func buildOpCompare(reg int) opFunc {
|
func buildOpCompare(reg int) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, _ := resolve(s, line, opcode)
|
value := resolveValue(s, line, opcode)
|
||||||
reference := s.Reg.getRegister(reg)
|
reference := s.Reg.getRegister(reg)
|
||||||
s.Reg.updateFlagZN(reference - value)
|
s.Reg.updateFlagZN(reference - value)
|
||||||
s.Reg.updateFlag(flagC, reference >= value)
|
s.Reg.updateFlag(flagC, reference >= value)
|
||||||
|
@ -215,7 +236,7 @@ func operationXor(a uint8, b uint8) uint8 { return a ^ b }
|
||||||
|
|
||||||
func buildOpLogic(operation func(uint8, uint8) uint8) opFunc {
|
func buildOpLogic(operation func(uint8, uint8) uint8) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, _ := resolve(s, line, opcode)
|
value := resolveValue(s, line, opcode)
|
||||||
result := operation(value, s.Reg.getA())
|
result := operation(value, s.Reg.getA())
|
||||||
s.Reg.setA(result)
|
s.Reg.setA(result)
|
||||||
s.Reg.updateFlagZN(result)
|
s.Reg.updateFlagZN(result)
|
||||||
|
@ -223,7 +244,7 @@ func buildOpLogic(operation func(uint8, uint8) uint8) opFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opADC(s *State, line []uint8, opcode opcode) {
|
func opADC(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, _ := resolve(s, line, opcode)
|
value := resolveValue(s, line, opcode)
|
||||||
aValue := s.Reg.getA()
|
aValue := s.Reg.getA()
|
||||||
carry := s.Reg.getFlagBit(flagC)
|
carry := s.Reg.getFlagBit(flagC)
|
||||||
|
|
||||||
|
@ -251,7 +272,7 @@ func opADC(s *State, line []uint8, opcode opcode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSBC(s *State, line []uint8, opcode opcode) {
|
func opSBC(s *State, line []uint8, opcode opcode) {
|
||||||
value, _, _ := resolve(s, line, opcode)
|
value := resolveValue(s, line, opcode)
|
||||||
aValue := s.Reg.getA()
|
aValue := s.Reg.getA()
|
||||||
carry := s.Reg.getFlagBit(flagC)
|
carry := s.Reg.getFlagBit(flagC)
|
||||||
|
|
||||||
|
@ -323,7 +344,7 @@ func opPHP(s *State, line []uint8, opcode opcode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opJMP(s *State, line []uint8, opcode opcode) {
|
func opJMP(s *State, line []uint8, opcode opcode) {
|
||||||
_, address, _ := resolve(s, line, opcode)
|
address := resolveAddress(s, line, opcode)
|
||||||
s.Reg.setPC(address)
|
s.Reg.setPC(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +352,7 @@ func opNOP(s *State, line []uint8, opcode opcode) {}
|
||||||
|
|
||||||
func opJSR(s *State, line []uint8, opcode opcode) {
|
func opJSR(s *State, line []uint8, opcode opcode) {
|
||||||
pushWord(s, s.Reg.getPC()-1)
|
pushWord(s, s.Reg.getPC()-1)
|
||||||
_, address, _ := resolve(s, line, opcode)
|
address := resolveAddress(s, line, opcode)
|
||||||
s.Reg.setPC(address)
|
s.Reg.setPC(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,9 +577,6 @@ func ExecuteInstruction(s *State, log bool) {
|
||||||
}
|
}
|
||||||
opcode.action(s, line, opcode)
|
opcode.action(s, line, opcode)
|
||||||
if log {
|
if log {
|
||||||
// Warning: this create double accesses and can interfere on memory mapped I/O
|
|
||||||
//value, address, _ := resolve(s, line, opcode)
|
|
||||||
//fmt.Printf("%v, [%04x:%02x], [%02x]\n", s.Reg, address, value, line)
|
|
||||||
fmt.Printf("%v, [%02x]\n", s.Reg, line)
|
fmt.Printf("%v, [%02x]\n", s.Reg, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,56 +23,56 @@ const (
|
||||||
flagC uint8 = 1 << 0
|
flagC uint8 = 1 << 0
|
||||||
)
|
)
|
||||||
|
|
||||||
type Registers struct {
|
type registers struct {
|
||||||
data [8]uint8
|
data [8]uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) getRegister(i int) uint8 { return r.data[i] }
|
func (r *registers) getRegister(i int) uint8 { return r.data[i] }
|
||||||
|
|
||||||
func (r *Registers) getA() uint8 { return r.data[regA] }
|
func (r *registers) getA() uint8 { return r.data[regA] }
|
||||||
func (r *Registers) getX() uint8 { return r.data[regX] }
|
func (r *registers) getX() uint8 { return r.data[regX] }
|
||||||
func (r *Registers) getY() uint8 { return r.data[regY] }
|
func (r *registers) getY() uint8 { return r.data[regY] }
|
||||||
func (r *Registers) getP() uint8 { return r.data[regP] }
|
func (r *registers) getP() uint8 { return r.data[regP] }
|
||||||
func (r *Registers) getSP() uint8 { return r.data[regSP] }
|
func (r *registers) getSP() uint8 { return r.data[regSP] }
|
||||||
|
|
||||||
func (r *Registers) setRegister(i int, v uint8) {
|
func (r *registers) setRegister(i int, v uint8) {
|
||||||
r.data[i] = v
|
r.data[i] = v
|
||||||
}
|
}
|
||||||
func (r *Registers) setA(v uint8) { r.setRegister(regA, v) }
|
func (r *registers) setA(v uint8) { r.setRegister(regA, v) }
|
||||||
func (r *Registers) setX(v uint8) { r.setRegister(regX, v) }
|
func (r *registers) setX(v uint8) { r.setRegister(regX, v) }
|
||||||
func (r *Registers) setY(v uint8) { r.setRegister(regY, v) }
|
func (r *registers) setY(v uint8) { r.setRegister(regY, v) }
|
||||||
func (r *Registers) setP(v uint8) { r.setRegister(regP, v) }
|
func (r *registers) setP(v uint8) { r.setRegister(regP, v) }
|
||||||
func (r *Registers) setSP(v uint8) { r.setRegister(regSP, v) }
|
func (r *registers) setSP(v uint8) { r.setRegister(regSP, v) }
|
||||||
|
|
||||||
func (r *Registers) getPC() uint16 {
|
func (r *registers) getPC() uint16 {
|
||||||
return uint16(r.data[regPC])*256 + uint16(r.data[regPC+1])
|
return uint16(r.data[regPC])*256 + uint16(r.data[regPC+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) setPC(v uint16) {
|
func (r *registers) setPC(v uint16) {
|
||||||
r.data[regPC] = uint8(v >> 8)
|
r.data[regPC] = uint8(v >> 8)
|
||||||
r.data[regPC+1] = uint8(v)
|
r.data[regPC+1] = uint8(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) getFlagBit(i uint8) uint8 {
|
func (r *registers) getFlagBit(i uint8) uint8 {
|
||||||
if r.getFlag(i) {
|
if r.getFlag(i) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) getFlag(i uint8) bool {
|
func (r *registers) getFlag(i uint8) bool {
|
||||||
return (r.data[regP] & i) != 0
|
return (r.data[regP] & i) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) setFlag(i uint8) {
|
func (r *registers) setFlag(i uint8) {
|
||||||
r.data[regP] |= i
|
r.data[regP] |= i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) clearFlag(i uint8) {
|
func (r *registers) clearFlag(i uint8) {
|
||||||
r.data[regP] &^= i
|
r.data[regP] &^= i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) updateFlag(i uint8, v bool) {
|
func (r *registers) updateFlag(i uint8, v bool) {
|
||||||
if v {
|
if v {
|
||||||
r.setFlag(i)
|
r.setFlag(i)
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,12 +80,12 @@ func (r *Registers) updateFlag(i uint8, v bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) updateFlagZN(t uint8) {
|
func (r *registers) updateFlagZN(t uint8) {
|
||||||
r.updateFlag(flagZ, t == 0)
|
r.updateFlag(flagZ, t == 0)
|
||||||
r.updateFlag(flagN, t >= (1<<7))
|
r.updateFlag(flagN, t >= (1<<7))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Registers) String() string {
|
func (r registers) String() string {
|
||||||
return fmt.Sprintf("A: %#02x, X: %#02x, Y: %#02x, SP: %#02x, PC: %#04x, P: %#02x, (NV-BDIZC): %08b",
|
return fmt.Sprintf("A: %#02x, X: %#02x, Y: %#02x, SP: %#02x, PC: %#04x, P: %#02x, (NV-BDIZC): %08b",
|
||||||
r.getA(), r.getX(), r.getY(), r.getSP(), r.getPC(), r.getP(), r.getP())
|
r.getA(), r.getX(), r.getY(), r.getSP(), r.getPC(), r.getP(), r.getP())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package core6502
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestRegA(t *testing.T) {
|
func TestRegA(t *testing.T) {
|
||||||
var r Registers
|
var r registers
|
||||||
var data uint8
|
var data uint8
|
||||||
data = 200
|
data = 200
|
||||||
r.setA(data)
|
r.setA(data)
|
||||||
|
@ -12,7 +12,7 @@ func TestRegA(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestRegPC(t *testing.T) {
|
func TestRegPC(t *testing.T) {
|
||||||
var r Registers
|
var r registers
|
||||||
var data uint16
|
var data uint16
|
||||||
data = 0xc600
|
data = 0xc600
|
||||||
r.setPC(data)
|
r.setPC(data)
|
||||||
|
@ -22,7 +22,7 @@ func TestRegPC(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFlags(t *testing.T) {
|
func TestFlags(t *testing.T) {
|
||||||
var r Registers
|
var r registers
|
||||||
r.setP(0x23)
|
r.setP(0x23)
|
||||||
if r.getP() != 0x23 {
|
if r.getP() != 0x23 {
|
||||||
t.Error("Error storing and loading P")
|
t.Error("Error storing and loading P")
|
||||||
|
@ -51,7 +51,7 @@ func TestFlags(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateFlagZN(t *testing.T) {
|
func TestUpdateFlagZN(t *testing.T) {
|
||||||
var r Registers
|
var r registers
|
||||||
r.updateFlagZN(0)
|
r.updateFlagZN(0)
|
||||||
if r.getP() != flagZ {
|
if r.getP() != flagZ {
|
||||||
t.Error("Error update flags ZN with 0")
|
t.Error("Error update flags ZN with 0")
|
||||||
|
|
8
main.go
8
main.go
|
@ -3,10 +3,10 @@ package main
|
||||||
import "go6502/apple2"
|
import "go6502/apple2"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
romFile := "../roms/apple.rom"
|
//romFile := "apple2/romdumps/Apple2.rom"
|
||||||
//romFile := "../roms/APPLE2.ROM"
|
romFile := "apple2/romdumps/Apple2_Plus.rom"
|
||||||
|
//romFile := "apple2/romdumps/Apple2e.rom"
|
||||||
log := true
|
|
||||||
|
|
||||||
|
log := false
|
||||||
apple2.Run(romFile, log)
|
apple2.Run(romFile, log)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue