diff --git a/6502functional_test.go b/6502functional_test.go index e6066ed..fe97cee 100644 --- a/6502functional_test.go +++ b/6502functional_test.go @@ -7,11 +7,12 @@ import ( func TestFunctional(t *testing.T) { var s state + // Test suite from https://github.com/Klaus2m5/6502_65C02_functional_tests s.memory.loadBinary("tests/6502_functional_test.bin") s.registers.setPC(0x0400) for true { - testCase := s.memory[0x0200] + testCase := s.memory.peek(0x0200) if testCase >= 240 { break } diff --git a/execute.go b/execute.go index 6b98d4b..4be0966 100644 --- a/execute.go +++ b/execute.go @@ -82,12 +82,12 @@ func resolve(s *state, line []uint8, opcode opcode) (value uint8, address uint16 } if hasAddress { - value = s.memory[address] + value = s.memory.peek(address) } setValue = func(value uint8) { if hasAddress { - s.memory[address] = value + s.memory.poke(address, value) } else if register != regNone { s.registers.setRegister(register, value) } else { @@ -99,7 +99,7 @@ func resolve(s *state, line []uint8, opcode opcode) (value uint8, address uint16 type opcode struct { name string - bytes int8 + bytes uint8 cycles int addressMode int action opFunc @@ -279,14 +279,14 @@ const stackAddress uint16 = 0x0100 func pushByte(s *state, value uint8) { adresss := stackAddress + uint16(s.registers.getSP()) - s.memory[adresss] = value + s.memory.poke(adresss, value) s.registers.setSP(s.registers.getSP() - 1) } func pullByte(s *state) uint8 { s.registers.setSP(s.registers.getSP() + 1) adresss := stackAddress + uint16(s.registers.getSP()) - return s.memory[adresss] + return s.memory.peek(adresss) } func pushWord(s *state, value uint16) { @@ -530,10 +530,15 @@ func executeLine(s *state, line []uint8) { func executeInstruction(s *state, log bool) { pc := s.registers.getPC() - opcode := opcodes[s.memory[pc]] - pcNext := pc + uint16(opcode.bytes) - s.registers.setPC(pcNext) - line := s.memory[pc:pcNext] + opcode := opcodes[s.memory.peek(pc)] + + line := make([]uint8, opcode.bytes) + for i := uint8(0); i < opcode.bytes; i++ { + line[i] = s.memory.peek(pc) + pc++ + } + s.registers.setPC(pc) + if log { fmt.Printf("%#04x %-12s: ", pc, lineString(s, line, opcode)) } diff --git a/execute_test.go b/execute_test.go index 8e4ba50..47a795c 100644 --- a/execute_test.go +++ b/execute_test.go @@ -6,6 +6,7 @@ import ( func TestLoad(t *testing.T) { var s state + s.memory.initWithRam() executeLine(&s, []uint8{0xA9, 0x42}) if s.registers.getA() != 0x42 { @@ -27,59 +28,59 @@ func TestLoad(t *testing.T) { t.Error("Error in LDY #") } - s.memory[0x38] = 0x87 + s.memory.poke(0x38, 0x87) executeLine(&s, []uint8{0xA5, 0x38}) if s.registers.getA() != 0x87 { t.Error("Error in LDA zpg") } - s.memory[0x57] = 0x90 + s.memory.poke(0x57, 0x90) s.registers.setX(0x10) executeLine(&s, []uint8{0xB5, 0x47}) if s.registers.getA() != 0x90 { t.Error("Error in LDA zpg, X") } - s.memory[0x38] = 0x12 + s.memory.poke(0x38, 0x12) s.registers.setX(0x89) executeLine(&s, []uint8{0xB5, 0xAF}) if s.registers.getA() != 0x12 { t.Error("Error in LDA zpgX with sero page overflow") } - s.memory[0x1234] = 0x67 + s.memory.poke(0x1234, 0x67) executeLine(&s, []uint8{0xAD, 0x34, 0x12}) if s.registers.getA() != 0x67 { t.Error("Error in LDA abs") } - s.memory[0xC057] = 0x7E + s.memory.poke(0xC057, 0x7E) s.registers.setX(0x57) executeLine(&s, []uint8{0xBD, 0x00, 0xC0}) if s.registers.getA() != 0x7E { t.Error("Error in LDA abs, X") } - s.memory[0xD059] = 0x7A + s.memory.poke(0xD059, 0x7A) s.registers.setY(0x59) executeLine(&s, []uint8{0xB9, 0x00, 0xD0}) if s.registers.getA() != 0x7A { t.Error("Error in LDA abs, Y") } - s.memory[0x24] = 0x74 - s.memory[0x25] = 0x20 + s.memory.poke(0x24, 0x74) + s.memory.poke(0x25, 0x20) s.registers.setX(0x04) - s.memory[0x2074] = 0x66 + s.memory.poke(0x2074, 0x66) executeLine(&s, []uint8{0xA1, 0x20}) if s.registers.getA() != 0x66 { t.Error("Error in LDA (oper,X)") } - s.memory[0x86] = 0x28 - s.memory[0x87] = 0x40 + s.memory.poke(0x86, 0x28) + s.memory.poke(0x87, 0x40) s.registers.setY(0x10) - s.memory[0x4038] = 0x99 + s.memory.poke(0x4038, 0x99) executeLine(&s, []uint8{0xB1, 0x86}) if s.registers.getA() != 0x99 { t.Error("Error in LDA (oper),Y") @@ -88,32 +89,33 @@ func TestLoad(t *testing.T) { func TestStore(t *testing.T) { var s state + s.memory.initWithRam() s.registers.setA(0x10) s.registers.setX(0x40) s.registers.setY(0x80) executeLine(&s, []uint8{0x85, 0x50}) - if s.memory[0x0050] != 0x10 { + if s.memory.peek(0x0050) != 0x10 { t.Error("Error in STA zpg") } executeLine(&s, []uint8{0x86, 0x51}) - if s.memory[0x0051] != 0x40 { + if s.memory.peek(0x0051) != 0x40 { t.Error("Error in STX zpg") } executeLine(&s, []uint8{0x84, 0x52}) - if s.memory[0x0052] != 0x80 { + if s.memory.peek(0x0052) != 0x80 { t.Error("Error in STY zpg") } executeLine(&s, []uint8{0x8D, 0x20, 0xC0}) - if s.memory[0xC020] != 0x10 { + if s.memory.peek(0xC020) != 0x10 { t.Error("Error in STA abs") } executeLine(&s, []uint8{0x9D, 0x08, 0x10}) - if s.memory[0x1048] != 0x10 { + if s.memory.peek(0x1048) != 0x10 { t.Error("Error in STA abs, X") } } @@ -399,23 +401,24 @@ func TestCompare(t *testing.T) { } func TestBit(t *testing.T) { var s state + s.memory.initWithRam() s.registers.setA(0x0F) - s.memory[0x0040] = 0xF0 + s.memory.poke(0x0040, 0xF0) executeLine(&s, []uint8{0x24, 0x40}) if s.registers.getP() != 0xC2 { t.Errorf("Error in BIT. %v", s.registers) } s.registers.setA(0xF0) - s.memory[0x0040] = 0xF0 + s.memory.poke(0x0040, 0xF0) executeLine(&s, []uint8{0x24, 0x40}) if s.registers.getP() != 0xC0 { t.Errorf("Error in BIT, 2. %v", s.registers) } s.registers.setA(0xF0) - s.memory[0x01240] = 0x80 + s.memory.poke(0x01240, 0x80) executeLine(&s, []uint8{0x2C, 0x40, 0x12}) if s.registers.getP() != 0x80 { t.Errorf("Error in BIT, 2. %v", s.registers) @@ -424,6 +427,7 @@ func TestBit(t *testing.T) { func TestBranch(t *testing.T) { var s state + s.registers.setPC(0xC600) s.registers.setFlag(flagV) executeLine(&s, []uint8{0x50, 0x20}) @@ -446,6 +450,7 @@ func TestBranch(t *testing.T) { func TestStack(t *testing.T) { var s state + s.memory.initWithRam() s.registers.setSP(0xF0) s.registers.setA(0xA0) @@ -454,7 +459,7 @@ func TestStack(t *testing.T) { if s.registers.getSP() != 0xEF { t.Errorf("Error in PHA stack pointer, %v", s.registers) } - if s.memory[0x01F0] != 0xA0 { + if s.memory.peek(0x01F0) != 0xA0 { t.Errorf("Error in PHA, %v", s.registers) } @@ -462,7 +467,7 @@ func TestStack(t *testing.T) { if s.registers.getSP() != 0xEE { t.Errorf("Error in PHP stack pointer, %v", s.registers) } - if s.memory[0x01EF] != 0x3A { + if s.memory.peek(0x01EF) != 0x3A { t.Errorf("Error in PHP, %v", s.registers) } diff --git a/main.go b/main.go index aa8014f..11b0040 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ func main() { s.registers.setPC(0x0400) for true { - testCase := s.memory[0x0200] + testCase := s.memory.peek(0x0200) if testCase >= 240 { break } diff --git a/memory.go b/memory.go index 1d9bc2c..095c4a8 100644 --- a/memory.go +++ b/memory.go @@ -6,18 +6,62 @@ import ( "os" ) -type memory [65536]uint8 +type memoryPage interface { + peek(uint8) uint8 + poke(uint8, uint8) +} + +type ramPage [256]uint8 +type romPage [256]uint8 + +type memory [256]memoryPage + +func (p *ramPage) peek(address uint8) uint8 { + return p[address] +} + +func (p *ramPage) poke(address uint8, value uint8) { + p[address] = value +} + +func (p *romPage) peek(address uint8) uint8 { + return p[address] +} + +func (p *romPage) poke(address uint8, value uint8) { + // Do nothing +} + +func (m *memory) peek(address uint16) uint8 { + hi := uint8(address >> 8) + lo := uint8(address) + return m[hi].peek(lo) +} + +func (m *memory) poke(address uint16, value uint8) { + hi := uint8(address >> 8) + lo := uint8(address) + //fmt.Println(hi) + m[hi].poke(lo, value) +} func (m *memory) getWord(address uint16) uint16 { - return uint16(m[address]) + 0x100*uint16(m[address+1]) + return uint16(m.peek(address)) + 0x100*uint16(m.peek(address+1)) } func (m *memory) getZeroPageWord(address uint8) uint16 { - return uint16(m[address]) + 0x100*uint16(m[address+1]) - // TODO: Does address + 1 wraps around the zero page? + return uint16(m.peek(uint16(address))) + 0x100*uint16(m.peek(uint16(address+1))) +} + +func (m *memory) initWithRam() { + var ramPages [256]ramPage + for i := 0; i < 256; i++ { + m[i] = &ramPages[i] + } } func (m *memory) loadBinary(filename string) { + // Load file f, err := os.Open(filename) if err != nil { panic(err) @@ -35,8 +79,9 @@ func (m *memory) loadBinary(filename string) { buf := bufio.NewReader(f) buf.Read(bytes) + m.initWithRam() for i, v := range bytes { - m[i] = uint8(v) + m.poke(uint16(i), uint8(v)) } } diff --git a/textPages.go b/textPages.go new file mode 100644 index 0000000..2dfc99c --- /dev/null +++ b/textPages.go @@ -0,0 +1,73 @@ +package main + +import "fmt" + +type textPages struct { + dirty bool + pages *[4]textPage +} + +type textPage struct { + textPages *textPages + data [256]uint8 +} + +func (p *textPage) peek(address uint8) uint8 { + return p.data[address] +} + +func (p *textPage) poke(address uint8, value uint8) { + p.data[address] = value + // Note: we could avoid setting dirty on the 16 blocks of 8 hidden bytes + p.textPages.dirty = true +} + +func textMemoryByteToString(value uint8) string { + return string(value) +} + +func (tp *textPages) dump() { + // See "Understand the Apple II", page 5-10 + // http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf + + var i, j, h uint8 + // Top, middle and botton screen + for i = 0; i < 128; i = i + 40 { + // Memory pages + for _, p := range tp.pages { + // The two half pages + for _, h = range []uint8{0, 128} { + line := "" + for j = i + h; j < i+h+40; j++ { + line += string(p.peek(j)) + } + fmt.Println(line) + } + } + } +} + +func (tp *textPages) dumpIfDirty() { + if !tp.dirty { + return + } + + tp.dirty = false + tp.dump() +} + +func (tp *textPages) charAddress(column uint8, line uint8) (page uint8, address uint8) { + page = (line / 3) % 4 + address = column + (line/8)*40 + (line%2)*128 + return +} + +func (tp *textPages) read(column uint8, line uint8) uint8 { + page, address := tp.charAddress(column, line) + return tp.pages[page].peek(address) +} + +func (tp *textPages) write(column uint8, line uint8, value uint8) { + page, address := tp.charAddress(column, line) + tp.pages[page].poke(address, value) +}