go6502/cpu/opcodeinstructions.go

564 lines
9.9 KiB
Go

package cpu
// Helpers and instruction-builders
// setNZ uses the given value to set the N and Z flags.
func (c *cpu) setNZ(value byte) {
c.r.P = (c.r.P &^ FLAG_N) | (value & FLAG_N)
if value == 0 {
c.r.P |= FLAG_Z
} else {
c.r.P &^= FLAG_Z
}
}
// samePage is a helper that returns true if two memory addresses
// refer to the same page.
func samePage(a1 uint16, a2 uint16) bool {
return (a1^a2)&0xFF00 == 0
}
// clearFlag builds instructions that clear the flag specified by the
// given mask.
func clearFlag(flag byte) func(*cpu) {
return func(c *cpu) {
c.r.P &^= flag
c.m.Read(c.r.PC)
c.t()
}
}
// setFlag builds instructions that set the flag specified by the
// given mask.
func setFlag(flag byte) func(*cpu) {
return func(c *cpu) {
c.r.P |= flag
c.m.Read(c.r.PC)
c.t()
}
}
// branch builds instructions that perform branches if the status
// register masks to a given value.
func branch(mask, value byte) func(*cpu) {
return func(c *cpu) {
// T1
offset := c.m.Read(c.r.PC)
c.r.PC++
c.t()
// T2
oldPC := c.r.PC
if c.r.P&mask == value {
c.m.Read(oldPC)
c.t()
// T3
c.r.PC = c.r.PC + uint16(offset)
if offset >= 128 {
c.r.PC = c.r.PC - 256
}
if !samePage(c.r.PC, oldPC) {
c.m.Read((oldPC & 0xFF00) | (c.r.PC & 0x00FF))
c.t()
}
}
}
}
// Individual opcodes
func adc(c *cpu, value byte) {
if c.r.P&FLAG_D > 0 {
adc_d(c, value)
return
}
result16 := uint16(c.r.A) + uint16(value) + uint16(c.r.P&FLAG_C)
result := byte(result16)
c.r.P &^= (FLAG_C | FLAG_V)
c.r.P |= uint8(result16 >> 8)
if (c.r.A^result)&(value^result)&0x80 > 0 {
c.r.P |= FLAG_V
}
c.r.A = result
c.setNZ(result)
}
// adc_d performs decimal-mode add-with-carry.
func adc_d(c *cpu, value byte) {
// See http://www.6502.org/tutorials/decimal_mode.html#A
// fmt.Printf("adc_d: $%04X: A=$%02X value=$%02X carry=%d\n",
// c.oldPC, c.r.A, value, c.r.P & FLAG_C)
bin := c.r.A + value + (c.r.P & FLAG_C)
al := (c.r.A & 0x0F) + (value & 0x0F) + (c.r.P & FLAG_C)
if al >= 0x0A {
al = ((al + 0x06) & 0x0F) + 0x10
}
// fmt.Printf(" al=$%04X\n", al)
a := uint16(c.r.A&0xF0) + uint16(value&0xF0) + uint16(al)
if a >= 0xA0 {
a += 0x60
}
// fmt.Printf(" a=$%04X\n", a)
a_nv := int16(int8(c.r.A&0xF0)) + int16(int8(value&0xF0)) + int16(int8(al))
c.r.P &^= (FLAG_V | FLAG_N | FLAG_Z)
if byte(a_nv&0xFF)&FLAG_N > 0 {
c.r.P |= FLAG_N
}
if a_nv < -128 || a_nv > 127 {
c.r.P |= FLAG_V
}
c.r.A = byte(a & 0xFF)
// fmt.Printf(" A=$%02X\n", c.r.A)
c.r.P &^= FLAG_C
if a >= 0x100 {
c.r.P |= FLAG_C
}
switch c.version {
case VERSION_6502:
if bin == 0 {
c.r.P |= FLAG_Z
}
case VERSION_65C02:
c.t()
c.setNZ(byte(a & 0xFF))
default:
panic("Unknown chip version")
}
}
func and(c *cpu, value byte) {
c.r.A &= value
c.setNZ(c.r.A)
}
func asl(c *cpu, value byte) byte {
result := value << 1
c.r.P = (c.r.P &^ FLAG_C) | (value >> 7)
c.setNZ(result)
return result
}
func bit(c *cpu, value byte) {
if t := c.r.A & value; t == 0 {
c.r.P |= FLAG_Z
} else {
c.r.P &^= FLAG_Z
}
c.r.P = (c.r.P &^ FLAG_NV) | (value & FLAG_NV)
}
// Note that BRK skips the next instruction:
// http://en.wikipedia.org/wiki/Interrupts_in_65xx_processors#Using_BRK_and_COP
func brk(c *cpu) {
// T1
c.m.Read(c.r.PC)
c.r.PC++
c.t()
// T2
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC>>8))
c.r.SP--
c.t()
// T3
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC&0xff))
c.r.SP--
c.t()
// T4
c.m.Write(0x100+uint16(c.r.SP), c.r.P|FLAG_B) // Set B flag
c.r.SP--
c.r.P |= FLAG_I // Disable interrupts
c.t()
// T5
addr := uint16(c.m.Read(IRQ_VECTOR))
c.t()
// T6
addr |= (uint16(c.m.Read(IRQ_VECTOR+1)) << 8)
c.r.PC = addr
c.t()
}
func cmp(c *cpu, value byte) {
v := c.r.A - value
c.r.P &^= FLAG_C
if c.r.A >= value {
c.r.P |= FLAG_C
}
c.setNZ(v)
}
func cpx(c *cpu, value byte) {
v := c.r.X - value
c.r.P &^= FLAG_C
if c.r.X >= value {
c.r.P |= FLAG_C
}
c.setNZ(v)
}
func cpy(c *cpu, value byte) {
v := c.r.Y - value
c.r.P &^= FLAG_C
if c.r.Y >= value {
c.r.P |= FLAG_C
}
c.setNZ(v)
}
func dec(c *cpu, value byte) byte {
result := value - 1
c.setNZ(result)
return result
}
func dex(c *cpu) {
c.r.X--
c.setNZ(c.r.X)
c.m.Read(c.r.PC)
c.t()
}
func dey(c *cpu) {
c.r.Y--
c.setNZ(c.r.Y)
c.m.Read(c.r.PC)
c.t()
}
func eor(c *cpu, value byte) {
c.r.A ^= value
c.setNZ(c.r.A)
}
func inc(c *cpu, value byte) byte {
result := value + 1
c.setNZ(result)
return result
}
func inx(c *cpu) {
c.r.X++
c.setNZ(c.r.X)
c.m.Read(c.r.PC)
c.t()
}
func iny(c *cpu) {
c.r.Y++
c.setNZ(c.r.Y)
c.m.Read(c.r.PC)
c.t()
}
func jmpAbsolute(c *cpu) {
// T1
addr := uint16(c.m.Read(c.r.PC))
c.r.PC++
c.t()
// T2
addr |= (uint16(c.m.Read(c.r.PC)) << 8)
c.r.PC++
c.r.PC = addr
c.t()
}
func jmpIndirect(c *cpu) {
// T1
iAddr := uint16(c.m.Read(c.r.PC))
c.r.PC++
c.t()
// T2
iAddr |= (uint16(c.m.Read(c.r.PC)) << 8)
c.r.PC++
c.t()
// T3
addr := uint16(c.m.Read(iAddr))
c.t()
// T4
// 6502 jumps to (xxFF,xx00) instead of (xxFF,xxFF+1).
// See http://en.wikipedia.org/wiki/MOS_Technology_6502#Bugs_and_quirks
switch c.version {
case VERSION_6502:
if iAddr&0xff == 0xff {
addr |= (uint16(c.m.Read(iAddr&0xff00)) << 8)
} else {
addr |= (uint16(c.m.Read(iAddr+1)) << 8)
}
case VERSION_65C02:
addr |= (uint16(c.m.Read(iAddr+1)) << 8)
default:
panic("Unknown chip version")
}
c.r.PC = addr
c.t()
}
func jsr(c *cpu) {
// T1
addr := uint16(c.m.Read(c.r.PC)) // We actually push PC(next) - 1
c.r.PC++
c.t()
// T2
c.m.Read(0x100 + uint16(c.r.SP)) // Ignored read on stack
c.t()
// T3
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC>>8)) // Write PC|hi to stack
c.r.SP--
c.t()
// T4
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC&0xff)) // Write PC|lo to stack
c.r.SP--
c.t()
// T5
addr |= (uint16(c.m.Read(c.r.PC)) << 8)
c.r.PC = addr
c.t()
}
func lda(c *cpu, value byte) {
c.r.A = value
c.setNZ(value)
}
func ldx(c *cpu, value byte) {
c.r.X = value
c.setNZ(value)
}
func ldy(c *cpu, value byte) {
c.r.Y = value
c.setNZ(value)
}
func lsr(c *cpu, value byte) byte {
result := (value >> 1)
c.r.P = (c.r.P &^ FLAG_C) | (value & FLAG_C)
c.setNZ(result)
return result
}
func ora(c *cpu, value byte) {
c.r.A |= value
c.setNZ(c.r.A)
}
func nop(c *cpu) {
c.m.Read(c.r.PC)
c.t()
}
func pha(c *cpu) {
c.m.Read(c.r.PC)
c.t()
c.m.Write(0x100+uint16(c.r.SP), c.r.A)
c.r.SP--
c.t()
}
func pla(c *cpu) {
c.m.Read(c.r.PC)
c.t()
c.m.Read(0x100 + uint16(c.r.SP))
c.r.SP++
c.t()
c.r.A = c.m.Read(0x100 + uint16(c.r.SP))
c.setNZ(c.r.A)
c.t()
}
func php(c *cpu) {
c.m.Read(c.r.PC)
c.t()
c.m.Write(0x100+uint16(c.r.SP), c.r.P)
c.r.SP--
c.t()
}
func plp(c *cpu) {
c.m.Read(c.r.PC)
c.t()
c.m.Read(0x100 + uint16(c.r.SP))
c.r.SP++
c.t()
c.r.P = c.m.Read(0x100+uint16(c.r.SP)) | FLAG_UNUSED | FLAG_B
c.t()
}
func rol(c *cpu, value byte) byte {
result := value<<1 | (c.r.P & FLAG_C)
c.r.P = (c.r.P &^ FLAG_C) | (value >> 7)
c.setNZ(result)
return result
}
func ror(c *cpu, value byte) byte {
result := (value >> 1) | (c.r.P << 7)
c.r.P = (c.r.P &^ FLAG_C) | (value & FLAG_C)
c.setNZ(result)
return result
}
func rts(c *cpu) {
// T1
c.m.Read(c.r.PC)
c.t()
// T2
c.m.Read(0x100 + uint16(c.r.SP))
c.r.SP++
c.t()
// T3
addr := uint16(c.m.Read(0x100 + uint16(c.r.SP)))
c.r.SP++
c.t()
// T4
addr |= (uint16(c.m.Read(0x100+uint16(c.r.SP))) << 8)
c.t()
// T5
c.m.Read(addr)
c.r.PC = addr + 1 // Since we pushed PC(next) - 1
c.t()
}
func rti(c *cpu) {
// T1
c.m.Read(c.r.PC)
c.t()
// T2
c.m.Read(0x100 + uint16(c.r.SP))
c.r.SP++
c.t()
// T3
c.r.P = c.m.Read(0x100+uint16(c.r.SP)) | FLAG_UNUSED
c.r.SP++
// T4
addr := uint16(c.m.Read(0x100 + uint16(c.r.SP)))
c.r.SP++
c.t()
// T5
addr |= (uint16(c.m.Read(0x100+uint16(c.r.SP))) << 8)
c.r.PC = addr
c.t()
}
func sbc(c *cpu, value byte) {
if c.r.P&FLAG_D > 0 {
sbc_d(c, value)
return
} else {
c.r.A = sbc_bin(c, value)
}
}
// sbc_bin performs binary-mode subtract with carry. Broken out into a
// separate routine so sbc_d can call it to determine flag values.
func sbc_bin(c *cpu, value byte) byte {
// Same as adc, except we take the ones complement of value
value = ^value
result16 := uint16(c.r.A) + uint16(value) + uint16(c.r.P&FLAG_C)
result := byte(result16)
c.r.P &^= (FLAG_C | FLAG_V)
c.r.P |= uint8(result16 >> 8)
if (c.r.A^result)&(value^result)&0x80 > 0 {
c.r.P |= FLAG_V
}
c.setNZ(result)
return result
}
// sdc_d performs decimal-mode subtract-with-carry.
func sbc_d(c *cpu, value byte) {
// See http://www.6502.org/tutorials/decimal_mode.html#A
carry := c.r.P & FLAG_C
// fmt.Printf("sbc_d: $%04X: A=$%02X value=$%02X carry=%d\n",
// c.oldPC, c.r.A, value, carry)
// Compute normal sbc, and set all flags accordingly
sbc_bin(c, value)
switch c.version {
case VERSION_6502:
al := int16(c.r.A&0x0F) - int16(value&0x0F) + int16(carry) - 1
if al < 0 {
al = ((al - 0x06) & 0x0F) - 0x10
}
// fmt.Printf(" al=$%04X\n", al)
a := int16(c.r.A&0xF0) - int16(value&0xF0) + al
if a < 0 {
a = a - 0x60
}
// fmt.Printf(" a=$%04X\n", a)
c.r.A = byte(a)
case VERSION_65C02:
al := int16(c.r.A&0x0F) - int16(value&0x0F) + int16(carry) - 1
a := int16(c.r.A) - int16(value) + int16(carry) - 1
// fmt.Printf(" al=$%04X\n", al)
if a < 0 {
a = a - 0x60
}
if al < 0 {
a = a - 0x06
}
// fmt.Printf(" a=$%04X ($%02X)\n", a, byte(a))
c.r.A = byte(a)
c.t()
c.setNZ(c.r.A)
default:
panic("Unknown chip version")
}
}
func sta(c *cpu) byte {
return c.r.A
}
func stx(c *cpu) byte {
return c.r.X
}
func sty(c *cpu) byte {
return c.r.Y
}
func tax(c *cpu) {
c.r.X = c.r.A
c.setNZ(c.r.X)
c.m.Read(c.r.PC)
c.t()
}
func tay(c *cpu) {
c.r.Y = c.r.A
c.setNZ(c.r.Y)
c.m.Read(c.r.PC)
c.t()
}
func tsx(c *cpu) {
c.r.X = c.r.SP
c.setNZ(c.r.X)
c.m.Read(c.r.PC)
c.t()
}
func txa(c *cpu) {
c.r.A = c.r.X
c.setNZ(c.r.A)
c.m.Read(c.r.PC)
c.t()
}
func txs(c *cpu) {
c.r.SP = c.r.X
c.m.Read(c.r.PC)
c.t()
}
func tya(c *cpu) {
c.r.A = c.r.Y
c.setNZ(c.r.A)
c.m.Read(c.r.PC)
c.t()
}