aboutsummaryrefslogtreecommitdiff
path: root/cpu/addressing.go
diff options
context:
space:
mode:
authorcrecher <crecher@crecher.net>2026-10-12 03:44:25 +0200
committercrecher <crecher@crecher.net>2026-10-12 03:44:25 +0200
commit7775f9ad5407f2be4b03c300d1db4c745fda2f69 (patch)
tree857bed544f0c3161aa5d561c54f0a112feb0b178 /cpu/addressing.go
downloadgones-7775f9ad5407f2be4b03c300d1db4c745fda2f69.tar.gz
gones-7775f9ad5407f2be4b03c300d1db4c745fda2f69.zip
initial commit
Diffstat (limited to 'cpu/addressing.go')
-rw-r--r--cpu/addressing.go138
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)
+ }
+}