diff options
| author | crecher <crecher@crecher.net> | 2026-10-12 03:44:25 +0200 |
|---|---|---|
| committer | crecher <crecher@crecher.net> | 2026-10-12 03:44:25 +0200 |
| commit | 7775f9ad5407f2be4b03c300d1db4c745fda2f69 (patch) | |
| tree | 857bed544f0c3161aa5d561c54f0a112feb0b178 /cpu/addressing.go | |
| download | gones-7775f9ad5407f2be4b03c300d1db4c745fda2f69.tar.gz gones-7775f9ad5407f2be4b03c300d1db4c745fda2f69.zip | |
initial commit
Diffstat (limited to 'cpu/addressing.go')
| -rw-r--r-- | cpu/addressing.go | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/cpu/addressing.go b/cpu/addressing.go new file mode 100644 index 0000000..f1b612a --- /dev/null +++ b/cpu/addressing.go @@ -0,0 +1,138 @@ +package cpu + +type AddrMode int + +const ( + ModeImplied AddrMode = iota + ModeAccumulator + ModeImmediate + ModeZeroPage + ModeZeroPageX + ModeZeroPageY + ModeAbsolute + ModeAbsoluteX + ModeAbsoluteY + ModeIndirect + ModeIndirectX + ModeIndirectY + ModeRelative +) + +type operand struct { + addr uint16 + accumulator bool + pageCrossed bool +} + +func samePage(a, b uint16) bool { + return a&0xFF00 == b&0xFF00 +} + +func (c *CPU) resolveOperand(mode AddrMode) operand { + switch mode { + case ModeImplied: + return operand{} + + case ModeAccumulator: + return operand{accumulator: true} + + case ModeImmediate: + addr := c.PC + c.PC++ + return operand{addr: addr} + + case ModeZeroPage: + addr := uint16(c.read(c.PC)) + c.PC++ + return operand{addr: addr} + + case ModeZeroPageX: + addr := uint16(uint8(c.read(c.PC) + c.X)) + c.PC++ + return operand{addr: addr} + + case ModeZeroPageY: + addr := uint16(uint8(c.read(c.PC) + c.Y)) + c.PC++ + return operand{addr: addr} + + case ModeAbsolute: + addr := c.read16(c.PC) + c.PC += 2 + return operand{addr: addr} + + case ModeAbsoluteX: + base := c.read16(c.PC) + c.PC += 2 + addr := base + uint16(c.X) + return operand{addr: addr, pageCrossed: !samePage(base, addr)} + + case ModeAbsoluteY: + base := c.read16(c.PC) + c.PC += 2 + addr := base + uint16(c.Y) + return operand{addr: addr, pageCrossed: !samePage(base, addr)} + + case ModeIndirect: + ptr := c.read16(c.PC) + c.PC += 2 + return operand{addr: c.readIndirectBug(ptr)} + + case ModeIndirectX: + zp := uint8(c.read(c.PC) + c.X) + c.PC++ + addr := c.read16zp(zp) + return operand{addr: addr} + + case ModeIndirectY: + zp := c.read(c.PC) + c.PC++ + base := c.read16zp(zp) + addr := base + uint16(c.Y) + return operand{addr: addr, pageCrossed: !samePage(base, addr)} + + case ModeRelative: + offset := int8(c.read(c.PC)) + c.PC++ + addr := uint16(int32(c.PC) + int32(offset)) + return operand{addr: addr} + + default: + panic("cpu: unhandled addressing mode") + } +} + +func (c *CPU) read16zp(addr uint8) uint16 { + lo := uint16(c.read(uint16(addr))) + hi := uint16(c.read(uint16(addr + 1))) + return hi<<8 | lo +} + +// jmp (ind) hardware bug: if pointers low byte is 0xff, the high byte is +// fetched from the start of the same page instead of the next page +func (c *CPU) readIndirectBug(ptr uint16) uint16 { + lo := uint16(c.read(ptr)) + var hiAddr uint16 + if ptr&0x00FF == 0x00FF { + hiAddr = ptr & 0xFF00 + } else { + hiAddr = ptr + 1 + } + hi := uint16(c.read(hiAddr)) + return hi<<8 | lo +} + +func (c *CPU) operandValue(op operand) uint8 { + if op.accumulator { + return c.A + } + return c.read(op.addr) +} + +func (c *CPU) writeOperand(op operand, v uint8) { + if op.accumulator { + c.A = v + } else { + c.write(op.addr, v) + } +} |