aboutsummaryrefslogtreecommitdiff
path: root/cpu/instructions.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/instructions.go
downloadgones-7775f9ad5407f2be4b03c300d1db4c745fda2f69.tar.gz
gones-7775f9ad5407f2be4b03c300d1db4c745fda2f69.zip
initial commit
Diffstat (limited to 'cpu/instructions.go')
-rw-r--r--cpu/instructions.go223
1 files changed, 223 insertions, 0 deletions
diff --git a/cpu/instructions.go b/cpu/instructions.go
new file mode 100644
index 0000000..b390f5c
--- /dev/null
+++ b/cpu/instructions.go
@@ -0,0 +1,223 @@
+package cpu
+
+func ldaExec(c *CPU, op operand) { c.A = c.operandValue(op); c.setZN(c.A) }
+func ldxExec(c *CPU, op operand) { c.X = c.operandValue(op); c.setZN(c.X) }
+func ldyExec(c *CPU, op operand) { c.Y = c.operandValue(op); c.setZN(c.Y) }
+
+func staExec(c *CPU, op operand) { c.write(op.addr, c.A) }
+func stxExec(c *CPU, op operand) { c.write(op.addr, c.X) }
+func styExec(c *CPU, op operand) { c.write(op.addr, c.Y) }
+
+func taxExec(c *CPU, op operand) { c.X = c.A; c.setZN(c.X) }
+func txaExec(c *CPU, op operand) { c.A = c.X; c.setZN(c.A) }
+func tayExec(c *CPU, op operand) { c.Y = c.A; c.setZN(c.Y) }
+func tyaExec(c *CPU, op operand) { c.A = c.Y; c.setZN(c.A) }
+func tsxExec(c *CPU, op operand) { c.X = c.SP; c.setZN(c.X) }
+func txsExec(c *CPU, op operand) { c.SP = c.X } // no flags affected
+
+func phaExec(c *CPU, op operand) { c.push(c.A) }
+func plaExec(c *CPU, op operand) { c.A = c.pop(); c.setZN(c.A) }
+func phpExec(c *CPU, op operand) { c.push(c.Status | FlagB | FlagU) }
+func plpExec(c *CPU, op operand) {
+ pulled := c.pop()
+ c.Status = (pulled &^ FlagB) | FlagU
+}
+
+func (c *CPU) addWithCarry(v uint8) {
+ var carryIn uint16
+ if c.flag(FlagC) {
+ carryIn = 1
+ }
+ sum := uint16(c.A) + uint16(v) + carryIn
+ result := uint8(sum)
+
+ c.setFlag(FlagC, sum > 0xFF)
+ c.setFlag(FlagV, (c.A^v)&0x80 == 0 && (c.A^result)&0x80 != 0)
+ c.A = result
+ c.setZN(c.A)
+}
+
+func adcExec(c *CPU, op operand) { c.addWithCarry(c.operandValue(op)) }
+func sbcExec(c *CPU, op operand) { c.addWithCarry(^c.operandValue(op)) }
+
+func andExec(c *CPU, op operand) { c.A &= c.operandValue(op); c.setZN(c.A) }
+func oraExec(c *CPU, op operand) { c.A |= c.operandValue(op); c.setZN(c.A) }
+func eorExec(c *CPU, op operand) { c.A ^= c.operandValue(op); c.setZN(c.A) }
+
+func bitExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ c.setFlag(FlagZ, c.A&v == 0)
+ c.setFlag(FlagV, v&0x40 != 0)
+ c.setFlag(FlagN, v&0x80 != 0)
+}
+
+func aslExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ c.setFlag(FlagC, v&0x80 != 0)
+ result := v << 1
+ c.writeOperand(op, result)
+ c.setZN(result)
+}
+
+func lsrExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ c.setFlag(FlagC, v&0x01 != 0)
+ result := v >> 1
+ c.writeOperand(op, result)
+ c.setZN(result)
+}
+
+func rolExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ var carryIn uint8
+ if c.flag(FlagC) {
+ carryIn = 1
+ }
+ c.setFlag(FlagC, v&0x80 != 0)
+ result := v<<1 | carryIn
+ c.writeOperand(op, result)
+ c.setZN(result)
+}
+
+func rorExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ var carryIn uint8
+ if c.flag(FlagC) {
+ carryIn = 0x80
+ }
+ c.setFlag(FlagC, v&0x01 != 0)
+ result := v>>1 | carryIn
+ c.writeOperand(op, result)
+ c.setZN(result)
+}
+
+func incExec(c *CPU, op operand) { v := c.operandValue(op) + 1; c.writeOperand(op, v); c.setZN(v) }
+func decExec(c *CPU, op operand) { v := c.operandValue(op) - 1; c.writeOperand(op, v); c.setZN(v) }
+
+func inxExec(c *CPU, op operand) { c.X++; c.setZN(c.X) }
+func dexExec(c *CPU, op operand) { c.X--; c.setZN(c.X) }
+func inyExec(c *CPU, op operand) { c.Y++; c.setZN(c.Y) }
+func deyExec(c *CPU, op operand) { c.Y--; c.setZN(c.Y) }
+
+func (c *CPU) compare(reg, v uint8) {
+ result := reg - v
+ c.setFlag(FlagC, reg >= v)
+ c.setZN(result)
+}
+
+func cmpExec(c *CPU, op operand) { c.compare(c.A, c.operandValue(op)) }
+func cpxExec(c *CPU, op operand) { c.compare(c.X, c.operandValue(op)) }
+func cpyExec(c *CPU, op operand) { c.compare(c.Y, c.operandValue(op)) }
+
+func (c *CPU) branch(op operand, cond bool) {
+ if !cond {
+ return
+ }
+ c.extraCycles++
+ if !samePage(c.PC, op.addr) {
+ c.extraCycles++
+ }
+ c.PC = op.addr
+}
+
+func bccExec(c *CPU, op operand) { c.branch(op, !c.flag(FlagC)) }
+func bcsExec(c *CPU, op operand) { c.branch(op, c.flag(FlagC)) }
+func bneExec(c *CPU, op operand) { c.branch(op, !c.flag(FlagZ)) }
+func beqExec(c *CPU, op operand) { c.branch(op, c.flag(FlagZ)) }
+func bplExec(c *CPU, op operand) { c.branch(op, !c.flag(FlagN)) }
+func bmiExec(c *CPU, op operand) { c.branch(op, c.flag(FlagN)) }
+func bvcExec(c *CPU, op operand) { c.branch(op, !c.flag(FlagV)) }
+func bvsExec(c *CPU, op operand) { c.branch(op, c.flag(FlagV)) }
+
+func jmpExec(c *CPU, op operand) { c.PC = op.addr }
+
+func jsrExec(c *CPU, op operand) {
+ c.push16(c.PC - 1) // address of the last byte of the jsr instruction
+ c.PC = op.addr
+}
+
+func rtsExec(c *CPU, op operand) { c.PC = c.pop16() + 1 }
+
+func rtiExec(c *CPU, op operand) {
+ pulled := c.pop()
+ c.Status = (pulled &^ FlagB) | FlagU
+ c.PC = c.pop16()
+}
+
+func clcExec(c *CPU, op operand) { c.setFlag(FlagC, false) }
+func secExec(c *CPU, op operand) { c.setFlag(FlagC, true) }
+func cliExec(c *CPU, op operand) { c.setFlag(FlagI, false) }
+func seiExec(c *CPU, op operand) { c.setFlag(FlagI, true) }
+func cldExec(c *CPU, op operand) { c.setFlag(FlagD, false) }
+func sedExec(c *CPU, op operand) { c.setFlag(FlagD, true) }
+func clvExec(c *CPU, op operand) { c.setFlag(FlagV, false) }
+
+func nopExec(c *CPU, op operand) {}
+
+func brkExec(c *CPU, op operand) {
+ c.PC++ // brk is logically 2 bytes: the byte after the opcode is skipped
+ c.interrupt(vectorIRQ, true)
+}
+
+func laxExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ c.A, c.X = v, v
+ c.setZN(v)
+}
+
+func saxExec(c *CPU, op operand) { c.write(op.addr, c.A&c.X) }
+
+func dcpExec(c *CPU, op operand) {
+ v := c.operandValue(op) - 1
+ c.writeOperand(op, v)
+ c.compare(c.A, v)
+}
+
+func sloExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ c.setFlag(FlagC, v&0x80 != 0)
+ result := v << 1
+ c.writeOperand(op, result)
+ c.A |= result
+ c.setZN(c.A)
+}
+
+func rlaExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ var carryIn uint8
+ if c.flag(FlagC) {
+ carryIn = 1
+ }
+ c.setFlag(FlagC, v&0x80 != 0)
+ result := v<<1 | carryIn
+ c.writeOperand(op, result)
+ c.A &= result
+ c.setZN(c.A)
+}
+
+func sreExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ c.setFlag(FlagC, v&0x01 != 0)
+ result := v >> 1
+ c.writeOperand(op, result)
+ c.A ^= result
+ c.setZN(c.A)
+}
+
+func rraExec(c *CPU, op operand) {
+ v := c.operandValue(op)
+ var carryIn uint8
+ if c.flag(FlagC) {
+ carryIn = 0x80
+ }
+ c.setFlag(FlagC, v&0x01 != 0)
+ result := v>>1 | carryIn
+ c.writeOperand(op, result)
+ c.addWithCarry(result)
+}
+
+func iscExec(c *CPU, op operand) {
+ v := c.operandValue(op) + 1
+ c.writeOperand(op, v)
+ c.addWithCarry(^v)
+}