diff --git a/apple2/addressSpace.go b/apple2/addressSpace.go new file mode 100644 index 0000000..7bfe6d5 --- /dev/null +++ b/apple2/addressSpace.go @@ -0,0 +1,110 @@ +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 5b28f5c..c93e683 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -4,30 +4,15 @@ import "go6502/core6502" // Run instantiates an apple2 and start emulation func Run(romFile string, log bool) { - - // Setup the Apple ][ address space - var m core6502.PagedMemory - m.InitWithRAM() - m.LoadRom(romFile) - var io ioC0Page - m.SetPage(0xc0, &io) - var t textPages - for j := 0; j < 4; j++ { - m.SetPage(uint8(4+j), &(t.pages[j])) - } - - for j := uint8(0xc1); j < 0xd0; j++ { - var p tracePage - p.page = j - m.SetPage(j, &p) - } + a := newAddressSpace() + a.loadRom(romFile) var s core6502.State - s.Mem = &m + s.Mem = a var fe ansiConsoleFrontend - io.setKeyboardProvider(&fe) - go fe.textModeGoRoutine(&t) + a.ioPage.setKeyboardProvider(&fe) + go fe.textModeGoRoutine(a.textPages1) // Start the processor core6502.Reset(&s) diff --git a/apple2/ioC0Page.go b/apple2/ioC0Page.go index 162a3ae..3eafce0 100644 --- a/apple2/ioC0Page.go +++ b/apple2/ioC0Page.go @@ -5,9 +5,10 @@ import ( ) type ioC0Page struct { - ioFlags uint64 - data [1]uint8 - keyboard keyboardProvider + ioFlags uint64 + data [1]uint8 + keyboard keyboardProvider + addressSpace *addressSpace } type keyboardProvider interface { diff --git a/apple2/pagedMemory.go b/apple2/pagedMemory.go new file mode 100644 index 0000000..c977b3d --- /dev/null +++ b/apple2/pagedMemory.go @@ -0,0 +1,35 @@ +package apple2 + +import "fmt" + +// memoryPage is a data page of 256 bytes +type memoryPage interface { + Peek(uint8) uint8 + Poke(uint8, uint8) +} + +// pagedMemory represents the addressable space of the processor +type pagedMemory struct { + data [256]memoryPage +} + +// Peek returns the data on the given address +func (m *pagedMemory) Peek(address uint16) uint8 { + hi := uint8(address >> 8) + lo := uint8(address) + return m.data[hi].Peek(lo) +} + +// Poke sets the data at the given address +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) + m.data[index] = page +} diff --git a/apple2/rxmPage.go b/apple2/rxmPage.go new file mode 100644 index 0000000..fbb3748 --- /dev/null +++ b/apple2/rxmPage.go @@ -0,0 +1,29 @@ +package apple2 + +type ramPage struct { + data [256]uint8 +} + +type romPage struct { + data [256]uint8 +} + +func (p *ramPage) Peek(address uint8) uint8 { + return p.data[address] +} + +func (p *ramPage) Poke(address uint8, value uint8) { + p.data[address] = value +} + +func (p *romPage) Peek(address uint8) uint8 { + return p.data[address] +} + +func (p *romPage) Poke(address uint8, value uint8) { + // Do nothing +} + +func (p *romPage) burn(address uint8, value uint8) { + p.data[address] = value +} diff --git a/apple2/tracePage.go b/apple2/unassignedPage.go similarity index 51% rename from apple2/tracePage.go rename to apple2/unassignedPage.go index a8bda4d..6142807 100644 --- a/apple2/tracePage.go +++ b/apple2/unassignedPage.go @@ -2,17 +2,17 @@ package apple2 import "fmt" -type tracePage struct { +type unassignedPage struct { page uint8 } -func (p *tracePage) Peek(address uint8) uint8 { +func (p *unassignedPage) Peek(address uint8) uint8 { fmt.Printf("Read on address 0x%02x%02x\n", p.page, address) - panic(address) + //panic(address) return 0xcc } -func (p *tracePage) Poke(address uint8, value uint8) { +func (p *unassignedPage) Poke(address uint8, value uint8) { fmt.Printf("Write on address 0x%02x%02x\n", p.page, address) - panic(address) + //panic(address) } diff --git a/core6502/execute.go b/core6502/execute.go index d64c624..6d736e0 100644 --- a/core6502/execute.go +++ b/core6502/execute.go @@ -27,6 +27,7 @@ const ( ) const ( + vectorNMI uint16 = 0xfffa vectorReset uint16 = 0xfffc vectorBreak uint16 = 0xfffe ) @@ -527,13 +528,21 @@ var opcodes = [256]opcode{ func executeLine(s *State, line []uint8) { opcode := opcodes[line[0]] + if opcode.cycles == 0 { + panic(fmt.Sprintf("Unknown opcode 0x%02x\n", line[0])) + } opcode.action(s, line, opcode) } // ExecuteInstruction transforms the state given after a single instruction is executed. func ExecuteInstruction(s *State, log bool) { pc := s.Reg.getPC() - opcode := opcodes[s.Mem.Peek(pc)] + opcodeID := s.Mem.Peek(pc) + opcode := opcodes[opcodeID] + + if opcode.cycles == 0 { + panic(fmt.Sprintf("Unknown opcode 0x%02x\n", opcodeID)) + } line := make([]uint8, opcode.bytes) for i := uint8(0); i < opcode.bytes; i++ { @@ -548,8 +557,9 @@ 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) + //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/pagedMemory.go b/core6502/pagedMemory.go deleted file mode 100644 index 896c5a5..0000000 --- a/core6502/pagedMemory.go +++ /dev/null @@ -1,32 +0,0 @@ -package core6502 - -// MemoryPage is a data page of 256 bytes -type MemoryPage interface { - Peek(uint8) uint8 - Poke(uint8, uint8) -} - -// PagedMemory represents the addressable space of the processor -type PagedMemory struct { - data [256]MemoryPage -} - -// Peek returns the data on the given address -func (m *PagedMemory) Peek(address uint16) uint8 { - hi := uint8(address >> 8) - lo := uint8(address) - return m.data[hi].Peek(lo) -} - -// Poke sets the data at the given address -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) { - m.data[index] = page -} diff --git a/core6502/rxmPage.go b/core6502/rxmPage.go deleted file mode 100644 index 879da01..0000000 --- a/core6502/rxmPage.go +++ /dev/null @@ -1,84 +0,0 @@ -package core6502 - -import ( - "bufio" - "fmt" - "os" -) - -type ramPage struct { - data [256]uint8 -} - -type romPage struct { - data [256]uint8 -} - -func (p *ramPage) Peek(address uint8) uint8 { - return p.data[address] -} - -func (p *ramPage) Poke(address uint8, value uint8) { - p.data[address] = value -} - -func (p *romPage) Peek(address uint8) uint8 { - return p.data[address] -} - -func (p *romPage) Poke(address uint8, value uint8) { - // Do nothing -} - -func (p *romPage) burn(address uint8, value uint8) { - p.data[address] = value -} - -// InitWithRAM adds RAM memory to all the memory pages -func (m *PagedMemory) InitWithRAM() { - var ramPages [256]ramPage - for i := 0; i < 256; i++ { - m.SetPage(uint8(i), &ramPages[i]) - } -} - -func (m *PagedMemory) transformToRom(page uint8) { - var romPage romPage - address := uint16(page) << 8 - for i := 0; i < 256; i++ { - romPage.burn(uint8(i), m.Peek(address)) - address++ - } - m.SetPage(page, &romPage) -} - -// LoadRom loads a binary file to the top of the memory and makes those pages read only. -func (m *PagedMemory) 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() - bytes := make([]byte, size) - - buf := bufio.NewReader(f) - buf.Read(bytes) - - romStart := uint16(0xFFFF - size + 1) - fmt.Printf("ROM start in in 0x%04x\n", romStart) - - for i, v := range bytes { - m.Poke(uint16(i)+romStart, uint8(v)) - } - - for i := uint8(romStart >> 8); i != 0; i++ { - m.transformToRom(i) - } -} diff --git a/main.go b/main.go index e0b27c2..ed3d729 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ func main() { romFile := "../roms/apple.rom" //romFile := "../roms/APPLE2.ROM" - log := false + log := true apple2.Run(romFile, log) }