diff --git a/apple2/addressSpace.go b/apple2/addressSpace.go deleted file mode 100644 index 7bfe6d5..0000000 --- a/apple2/addressSpace.go +++ /dev/null @@ -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)) - } -} diff --git a/apple2/apple2.go b/apple2/apple2.go index c93e683..cbda391 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -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) diff --git a/apple2/ioC0Page.go b/apple2/ioC0Page.go index 3eafce0..6e2587c 100644 --- a/apple2/ioC0Page.go +++ b/apple2/ioC0Page.go @@ -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 } diff --git a/apple2/memoryManager.go b/apple2/memoryManager.go new file mode 100644 index 0000000..92eb18b --- /dev/null +++ b/apple2/memoryManager.go @@ -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]) + } +} diff --git a/apple2/pagedMemory.go b/apple2/pagedMemory.go index c977b3d..e3f6903 100644 --- a/apple2/pagedMemory.go +++ b/apple2/pagedMemory.go @@ -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 } diff --git a/apple2/romdumps/Apple2.rom b/apple2/romdumps/Apple2.rom new file mode 100644 index 0000000..e53fa17 Binary files /dev/null and b/apple2/romdumps/Apple2.rom differ diff --git a/apple2/romdumps/Apple2_Plus.rom b/apple2/romdumps/Apple2_Plus.rom new file mode 100644 index 0000000..b0641c3 Binary files /dev/null and b/apple2/romdumps/Apple2_Plus.rom differ diff --git a/apple2/romdumps/Apple2e.rom b/apple2/romdumps/Apple2e.rom new file mode 100644 index 0000000..916b318 Binary files /dev/null and b/apple2/romdumps/Apple2e.rom differ diff --git a/apple2/romdumps/Apple2e_Enhanced.rom b/apple2/romdumps/Apple2e_Enhanced.rom new file mode 100644 index 0000000..65bb648 Binary files /dev/null and b/apple2/romdumps/Apple2e_Enhanced.rom differ diff --git a/apple2/softSwitches2e.go b/apple2/softSwitches2e.go new file mode 100644 index 0000000..9805fbe --- /dev/null +++ b/apple2/softSwitches2e.go @@ -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]) +} diff --git a/core6502/execute.go b/core6502/execute.go index 6d736e0..84b66e1 100644 --- a/core6502/execute.go +++ b/core6502/execute.go @@ -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) } } diff --git a/core6502/registers.go b/core6502/registers.go index 494a698..b207578 100644 --- a/core6502/registers.go +++ b/core6502/registers.go @@ -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()) } diff --git a/core6502/registers_test.go b/core6502/registers_test.go index 47e5079..403c1d7 100644 --- a/core6502/registers_test.go +++ b/core6502/registers_test.go @@ -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") diff --git a/main.go b/main.go index ed3d729..9bc1d61 100644 --- a/main.go +++ b/main.go @@ -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) }