Improved memory manager to support Apple2e shadow ROM

This commit is contained in:
Ivan Izaguirre 2019-02-24 00:41:32 +01:00
parent a28536d561
commit d2c897f7ea
14 changed files with 380 additions and 239 deletions

View File

@ -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))
}
}

View File

@ -4,15 +4,17 @@ import "go6502/core6502"
// Run instantiates an apple2 and start emulation
func Run(romFile string, log bool) {
a := newAddressSpace()
a.loadRom(romFile)
mmu := newAddressSpace(romFile)
if mmu.isApple2e {
addApple2ESoftSwitches(mmu)
}
var s core6502.State
s.Mem = a
s.Mem = mmu
var fe ansiConsoleFrontend
a.ioPage.setKeyboardProvider(&fe)
go fe.textModeGoRoutine(a.textPages1)
mmu.ioPage.setKeyboardProvider(&fe)
go fe.textModeGoRoutine(mmu.textPages1)
// Start the processor
core6502.Reset(&s)

View File

@ -5,12 +5,15 @@ import (
)
type ioC0Page struct {
ioFlags uint64
data [1]uint8
keyboard keyboardProvider
addressSpace *addressSpace
softSwitches [128]softSwitch
softSwitchesData [128]uint8
keyboard keyboardProvider
mmu *memoryManager
}
type softSwitch func(io *ioC0Page, isWrite bool, value uint8) uint8
// TODO: change interface to func
type keyboardProvider interface {
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
const (
ioFlagNone uint8 = 0
ioFlagGraphics uint8 = 3
ioFlagMixed uint8 = 8
ioFlagSecondPage uint8 = 1
ioFlagHiRes uint8 = 2
ioFlagAnnunciator0 uint8 = 4
ioFlagAnnunciator1 uint8 = 5
ioFlagAnnunciator2 uint8 = 6
ioFlagAnnunciator3 uint8 = 7
ioDataKeyboard uint8 = 0x10
ioFlagGraphics uint8 = 0x50
ioFlagMixed uint8 = 0x52
ioFlagSecondPage uint8 = 0x54
ioFlagHiRes uint8 = 0x56
ioFlagAnnunciator0 uint8 = 0x58
ioFlagAnnunciator1 uint8 = 0x5a
ioFlagAnnunciator2 uint8 = 0x5c
ioFlagAnnunciator3 uint8 = 0x5e
)
const (
ioDataKeyboard uint8 = 0
)
type softSwitch struct {
ioFlag uint8
value bool
onWriteOnly bool
func (p *ioC0Page) isSoftSwitchExtActive(ioFlag uint8) bool {
return (p.softSwitchesData[ioFlag] & 0x08) == 0x80
}
var softSwitches = [256]softSwitch{
0x50: softSwitch{ioFlagGraphics, false, false},
0x51: softSwitch{ioFlagGraphics, true, false},
0x52: softSwitch{ioFlagMixed, false, false},
0x53: softSwitch{ioFlagMixed, true, false},
0x54: softSwitch{ioFlagSecondPage, false, false},
0x55: softSwitch{ioFlagSecondPage, true, false},
0x56: softSwitch{ioFlagHiRes, false, false},
0x57: softSwitch{ioFlagHiRes, true, false},
0x58: softSwitch{ioFlagAnnunciator0, false, false},
0x59: softSwitch{ioFlagAnnunciator0, true, false},
0x5a: softSwitch{ioFlagAnnunciator1, false, false},
0x5b: softSwitch{ioFlagAnnunciator1, true, false},
0x5c: softSwitch{ioFlagAnnunciator2, false, false},
0x5d: softSwitch{ioFlagAnnunciator2, true, false},
0x5e: softSwitch{ioFlagAnnunciator3, false, false},
0x5f: softSwitch{ioFlagAnnunciator3, true, false},
func newIoC0Page(mmu *memoryManager) *ioC0Page {
var p ioC0Page
p.mmu = mmu
ss := &p.softSwitches
ss[0x00] = getKeySoftSwitch // Keyboard
ss[0x10] = strobeKeyboardSoftSwitch // Keyboard Strobe
ss[0x30] = notImplementedSoftSwitch // Speaker
ss[0x50] = getSoftSwitch(ioFlagGraphics, false)
ss[0x51] = getSoftSwitch(ioFlagGraphics, true)
ss[0x52] = getSoftSwitch(ioFlagMixed, false)
ss[0x53] = getSoftSwitch(ioFlagMixed, true)
ss[0x54] = getSoftSwitch(ioFlagSecondPage, false)
ss[0x55] = getSoftSwitch(ioFlagSecondPage, true)
ss[0x56] = getSoftSwitch(ioFlagHiRes, 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 {
@ -70,47 +81,47 @@ func (p *ioC0Page) Poke(address uint8, value uint8) {
}
func (p *ioC0Page) access(address uint8, isWrite bool, value uint8) uint8 {
ss := softSwitches[address]
if ss.ioFlag != ioFlagNone {
if !isWrite || !!ss.onWriteOnly {
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))
}
// The second hals of the pages is reserved for slots
if address >= 0x80 {
// TODO reserved slots data
return 0
}
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
}
func (p *ioC0Page) setKeyboardProvider(kb keyboardProvider) {
p.keyboard = kb
}
func (p *ioC0Page) getKey() uint8 {
strobed := (p.data[ioDataKeyboard] & (1 << 7)) == 0
func getKeySoftSwitch(p *ioC0Page, _ bool, _ uint8) uint8 {
strobed := (p.softSwitchesData[ioDataKeyboard] & (1 << 7)) == 0
if strobed && p.keyboard != nil {
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 {
result := p.data[ioDataKeyboard]
p.data[ioDataKeyboard] &^= 1 << 7
func strobeKeyboardSoftSwitch(p *ioC0Page, _ bool, _ uint8) uint8 {
result := p.softSwitchesData[ioDataKeyboard]
p.softSwitchesData[ioDataKeyboard] &^= 1 << 7
return result
}

144
apple2/memoryManager.go Normal file
View File

@ -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])
}
}

View File

@ -1,6 +1,9 @@
package apple2
import "fmt"
import (
"fmt"
"reflect"
)
// memoryPage is a data page of 256 bytes
type memoryPage interface {
@ -24,12 +27,11 @@ func (m *pagedMemory) Peek(address uint16) uint8 {
func (m *pagedMemory) Poke(address uint16, value uint8) {
hi := uint8(address >> 8)
lo := uint8(address)
//fmt.Println(hi)
m.data[hi].Poke(lo, value)
}
// SetPage assigns a MemoryPage implementation on the page given
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
}

BIN
apple2/romdumps/Apple2.rom Normal file

Binary file not shown.

Binary file not shown.

BIN
apple2/romdumps/Apple2e.rom Normal file

Binary file not shown.

Binary file not shown.

74
apple2/softSwitches2e.go Normal file
View File

@ -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])
}

View File

@ -4,7 +4,7 @@ import "fmt"
// State represents the state of the simulated device
type State struct {
Reg Registers
Reg registers
Mem Memory
}
@ -41,25 +41,46 @@ func getWordInLine(line []uint8) uint16 {
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
register := regNone
switch opcode.addressMode {
case modeAccumulator:
value = s.Reg.getA()
getValue = func() uint8 { return s.Reg.getA() }
hasAddress = false
register = regA
case modeImplicitX:
value = s.Reg.getX()
getValue = func() uint8 { return s.Reg.getX() }
hasAddress = false
register = regX
case modeImplicitY:
value = s.Reg.getY()
getValue = func() uint8 { return s.Reg.getY() }
hasAddress = false
register = regY
case modeImmediate:
value = line[1]
getValue = func() uint8 { return line[1] }
hasAddress = false
case modeZeroPage:
address = uint16(line[1])
@ -85,7 +106,7 @@ func resolve(s *State, line []uint8, opcode opcode) (value uint8, address uint16
}
if hasAddress {
value = s.Mem.Peek(address)
getValue = func() uint8 { return s.Mem.Peek(address) }
}
setValue = func(value uint8) {
@ -122,7 +143,7 @@ func buildOpTransfer(regSrc int, regDst int) opFunc {
func buildOpIncDec(inc bool) opFunc {
return func(s *State, line []uint8, opcode opcode) {
value, _, setValue := resolve(s, line, opcode)
value, setValue := resolveGetSetValue(s, line, opcode)
if inc {
value++
} else {
@ -135,7 +156,7 @@ func buildOpIncDec(inc bool) opFunc {
func buildOpShift(isLeft bool, isRotate bool) opFunc {
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)
var carry bool
@ -160,7 +181,7 @@ func buildOpShift(isLeft bool, isRotate bool) opFunc {
func buildOpLoad(regDst int) opFunc {
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.updateFlagZN(value)
}
@ -168,7 +189,7 @@ func buildOpLoad(regDst int) opFunc {
func buildOpStore(regSrc int) opFunc {
return func(s *State, line []uint8, opcode opcode) {
_, _, setValue := resolve(s, line, opcode)
setValue := resolveSetValue(s, line, opcode)
value := s.Reg.getRegister(regSrc)
setValue(value)
}
@ -192,7 +213,7 @@ func buildOpBranch(flag uint8, value bool) opFunc {
}
func opBIT(s *State, line []uint8, opcode opcode) {
value, _, _ := resolve(s, line, opcode)
value := resolveValue(s, line, opcode)
acc := s.Reg.getA()
// Future note: The immediate addressing mode (65C02 or 65816 only) does not affect V.
s.Reg.updateFlag(flagZ, value&acc == 0)
@ -202,7 +223,7 @@ func opBIT(s *State, line []uint8, opcode opcode) {
func buildOpCompare(reg int) opFunc {
return func(s *State, line []uint8, opcode opcode) {
value, _, _ := resolve(s, line, opcode)
value := resolveValue(s, line, opcode)
reference := s.Reg.getRegister(reg)
s.Reg.updateFlagZN(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 {
return func(s *State, line []uint8, opcode opcode) {
value, _, _ := resolve(s, line, opcode)
value := resolveValue(s, line, opcode)
result := operation(value, s.Reg.getA())
s.Reg.setA(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) {
value, _, _ := resolve(s, line, opcode)
value := resolveValue(s, line, opcode)
aValue := s.Reg.getA()
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) {
value, _, _ := resolve(s, line, opcode)
value := resolveValue(s, line, opcode)
aValue := s.Reg.getA()
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) {
_, address, _ := resolve(s, line, opcode)
address := resolveAddress(s, line, opcode)
s.Reg.setPC(address)
}
@ -331,7 +352,7 @@ func opNOP(s *State, line []uint8, opcode opcode) {}
func opJSR(s *State, line []uint8, opcode opcode) {
pushWord(s, s.Reg.getPC()-1)
_, address, _ := resolve(s, line, opcode)
address := resolveAddress(s, line, opcode)
s.Reg.setPC(address)
}
@ -556,9 +577,6 @@ func ExecuteInstruction(s *State, log bool) {
}
opcode.action(s, line, opcode)
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)
}
}

View File

@ -23,56 +23,56 @@ const (
flagC uint8 = 1 << 0
)
type Registers struct {
type registers struct {
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) getX() uint8 { return r.data[regX] }
func (r *Registers) getY() uint8 { return r.data[regY] }
func (r *Registers) getP() uint8 { return r.data[regP] }
func (r *Registers) getSP() uint8 { return r.data[regSP] }
func (r *registers) getA() uint8 { return r.data[regA] }
func (r *registers) getX() uint8 { return r.data[regX] }
func (r *registers) getY() uint8 { return r.data[regY] }
func (r *registers) getP() uint8 { return r.data[regP] }
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
}
func (r *Registers) setA(v uint8) { r.setRegister(regA, v) }
func (r *Registers) setX(v uint8) { r.setRegister(regX, v) }
func (r *Registers) setY(v uint8) { r.setRegister(regY, v) }
func (r *Registers) setP(v uint8) { r.setRegister(regP, v) }
func (r *Registers) setSP(v uint8) { r.setRegister(regSP, v) }
func (r *registers) setA(v uint8) { r.setRegister(regA, v) }
func (r *registers) setX(v uint8) { r.setRegister(regX, v) }
func (r *registers) setY(v uint8) { r.setRegister(regY, v) }
func (r *registers) setP(v uint8) { r.setRegister(regP, 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])
}
func (r *Registers) setPC(v uint16) {
func (r *registers) setPC(v uint16) {
r.data[regPC] = uint8(v >> 8)
r.data[regPC+1] = uint8(v)
}
func (r *Registers) getFlagBit(i uint8) uint8 {
func (r *registers) getFlagBit(i uint8) uint8 {
if r.getFlag(i) {
return 1
}
return 0
}
func (r *Registers) getFlag(i uint8) bool {
func (r *registers) getFlag(i uint8) bool {
return (r.data[regP] & i) != 0
}
func (r *Registers) setFlag(i uint8) {
func (r *registers) setFlag(i uint8) {
r.data[regP] |= i
}
func (r *Registers) clearFlag(i uint8) {
func (r *registers) clearFlag(i uint8) {
r.data[regP] &^= i
}
func (r *Registers) updateFlag(i uint8, v bool) {
func (r *registers) updateFlag(i uint8, v bool) {
if v {
r.setFlag(i)
} 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(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",
r.getA(), r.getX(), r.getY(), r.getSP(), r.getPC(), r.getP(), r.getP())
}

View File

@ -3,7 +3,7 @@ package core6502
import "testing"
func TestRegA(t *testing.T) {
var r Registers
var r registers
var data uint8
data = 200
r.setA(data)
@ -12,7 +12,7 @@ func TestRegA(t *testing.T) {
}
}
func TestRegPC(t *testing.T) {
var r Registers
var r registers
var data uint16
data = 0xc600
r.setPC(data)
@ -22,7 +22,7 @@ func TestRegPC(t *testing.T) {
}
func TestFlags(t *testing.T) {
var r Registers
var r registers
r.setP(0x23)
if r.getP() != 0x23 {
t.Error("Error storing and loading P")
@ -51,7 +51,7 @@ func TestFlags(t *testing.T) {
}
func TestUpdateFlagZN(t *testing.T) {
var r Registers
var r registers
r.updateFlagZN(0)
if r.getP() != flagZ {
t.Error("Error update flags ZN with 0")

View File

@ -3,10 +3,10 @@ package main
import "go6502/apple2"
func main() {
romFile := "../roms/apple.rom"
//romFile := "../roms/APPLE2.ROM"
log := true
//romFile := "apple2/romdumps/Apple2.rom"
romFile := "apple2/romdumps/Apple2_Plus.rom"
//romFile := "apple2/romdumps/Apple2e.rom"
log := false
apple2.Run(romFile, log)
}