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) } }