mirror of
https://github.com/zellyn/go6502.git
synced 2024-12-28 10:29:53 +00:00
443 lines
8.5 KiB
Go
443 lines
8.5 KiB
Go
package cpu
|
|
|
|
// BUG(zellyn): 6502 should do invalid reads when doing indexed
|
|
// addressing across page boundaries. See
|
|
// http://en.wikipedia.org/wiki/MOS_Technology_6502#Bugs_and_quirks.
|
|
|
|
// BUG(zellyn): rmw instructions should write old data back on 6502,
|
|
// read twice on 65C02. See
|
|
// http://en.wikipedia.org/wiki/MOS_Technology_6502#Bugs_and_quirks.
|
|
|
|
// BUG(zellyn): Instructions should do many more reads. See
|
|
// http://users.telenet.be/kim1-6502/6502/hwman.html#AA and/or table
|
|
// 4.1 of "Understanding the Apple II".
|
|
|
|
// immediate2 performs 2-opcode, 2-cycle immediate mode instructions.
|
|
func immediate2(f func(*cpu, byte)) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
value := c.m.Read(c.r.PC)
|
|
c.r.PC++
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absolute4r performs 3-opcode, 4-cycle absolute mode read instructions.
|
|
func absolute4r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(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.t()
|
|
// T3
|
|
value := c.m.Read(addr)
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absolute4w performs 3-opcode, 4-cycle absolute mode write instructions.
|
|
func absolute4w(f func(*cpu) byte) func(*cpu) {
|
|
return func(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.t()
|
|
// T3
|
|
c.m.Write(addr, f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zp3r performs 2-opcode, 3-cycle zero page read instructions.
|
|
func zp3r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := uint16(c.m.Read(c.r.PC))
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
value := c.m.Read(addr)
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zp3w performs 2-opcode, 3-cycle zero page write instructions.
|
|
func zp3w(f func(*cpu) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := uint16(c.m.Read(c.r.PC))
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Write(addr, f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absx4r performs 3-opcode, 4*-cycle abs,X read instructions.
|
|
func absx4r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(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)
|
|
addrX := addr + uint16(c.r.X)
|
|
c.r.PC++
|
|
c.t()
|
|
// T3
|
|
if !samePage(addr, addrX) {
|
|
c.m.Read(addrX - 0x100)
|
|
c.t()
|
|
}
|
|
// T3(cotd.) or T4
|
|
value := c.m.Read(addrX)
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absy4r performs 3-opcode, 4*-cycle abs,Y read instructions.
|
|
func absy4r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(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)
|
|
addrY := addr + uint16(c.r.Y)
|
|
c.r.PC++
|
|
c.t()
|
|
// T3
|
|
if !samePage(addr, addrY) {
|
|
c.m.Read(addrY - 0x100)
|
|
c.t()
|
|
}
|
|
// T3(cotd.) or T4
|
|
value := c.m.Read(addrY)
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absx5w performs 3-opcode, 5-cycle abs,X write instructions.
|
|
func absx5w(f func(*cpu) byte) func(*cpu) {
|
|
return func(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)
|
|
addrX := addr + uint16(c.r.X)
|
|
c.r.PC++
|
|
c.t()
|
|
// T3
|
|
c.m.Read((addr & 0xFF00) | (addrX & 0x00FF))
|
|
c.t()
|
|
// T4
|
|
c.m.Write(addrX, f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absy5w performs 3-opcode, 5-cycle abs,Y write instructions.
|
|
func absy5w(f func(*cpu) byte) func(*cpu) {
|
|
return func(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)
|
|
addrY := addr + uint16(c.r.Y)
|
|
c.r.PC++
|
|
c.t()
|
|
// T3
|
|
c.m.Read((addr & 0xFF00) | (addrY & 0x00FF))
|
|
c.t()
|
|
// T4
|
|
c.m.Write(addrY, f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpx4r performs 2-opcode, 4-cycle zp,X read instructions.
|
|
func zpx4r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := c.m.Read(c.r.PC)
|
|
addrX := uint16(addr + c.r.X)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(addr))
|
|
c.t()
|
|
// T3
|
|
value := c.m.Read(addrX)
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpx4w performs 2-opcode, 4-cycle zp,X write instructions.
|
|
func zpx4w(f func(*cpu) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := c.m.Read(c.r.PC)
|
|
addrX := uint16(addr + c.r.X)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(addr))
|
|
c.t()
|
|
// T3
|
|
c.m.Write(uint16(addrX), f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpy4r performs 2-opcode, 4-cycle zp,Y instructions.
|
|
func zpy4r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := c.m.Read(c.r.PC)
|
|
addrY := uint16(addr + c.r.Y)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(addr))
|
|
c.t()
|
|
// T3
|
|
value := c.m.Read(uint16(addrY))
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpy4w performs 2-opcode, 4-cycle zp,Y write instructions.
|
|
func zpy4w(f func(*cpu) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := c.m.Read(c.r.PC)
|
|
addrY := uint16(addr + c.r.Y)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(addr))
|
|
c.t()
|
|
// T3
|
|
c.m.Write(addrY, f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpiy5r performs 2-opcode, 5*-cycle zero-page indirect Y read instructions.
|
|
func zpiy5r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
iAddr := c.m.Read(c.r.PC)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
addr := uint16(c.m.Read(uint16(iAddr)))
|
|
c.t()
|
|
// T3
|
|
addr |= (uint16(c.m.Read(uint16(iAddr+1))) << 8)
|
|
addrY := addr + uint16(c.r.Y)
|
|
c.t()
|
|
// T4
|
|
if !samePage(addr, addrY) {
|
|
c.m.Read((addr & 0xFF00) | (addrY & 0x00FF))
|
|
c.t()
|
|
}
|
|
// T4(cotd.) or T5
|
|
value := c.m.Read(addr + uint16(c.r.Y))
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpiy6w performs 2-opcode, 6-cycle zero-page indirect Y write instructions.
|
|
func zpiy6w(f func(*cpu) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
iAddr := c.m.Read(c.r.PC)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
addr := uint16(uint16(c.m.Read(uint16(iAddr))))
|
|
c.t()
|
|
// T3
|
|
addr |= (uint16(c.m.Read(uint16(iAddr+1))) << 8)
|
|
addrY := addr + uint16(c.r.Y)
|
|
c.t()
|
|
// T4
|
|
c.m.Read((addr & 0xFF00) | (addrY & 0x00FF))
|
|
c.t()
|
|
// T5
|
|
c.m.Write(addr+uint16(c.r.Y), f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpxi6r performs 2-opcode, 6-cycle zero-page X indirect read instructions.
|
|
func zpxi6r(f func(*cpu, byte)) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
iAddr := c.m.Read(c.r.PC)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(iAddr))
|
|
c.t()
|
|
// T3
|
|
addr := uint16(uint16(c.m.Read(uint16(iAddr + c.r.X))))
|
|
c.t()
|
|
// T4
|
|
addr |= (uint16(c.m.Read(uint16(iAddr+c.r.X+1))) << 8)
|
|
c.t()
|
|
// T5
|
|
value := c.m.Read(addr)
|
|
f(c, value)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpxi6w performs 2-opcode, 6-cycle zero-page X indirect write instructions.
|
|
func zpxi6w(f func(*cpu) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
iAddr := c.m.Read(c.r.PC)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(iAddr))
|
|
c.t()
|
|
// T3
|
|
addr := uint16(uint16(c.m.Read(uint16(iAddr + c.r.X))))
|
|
c.t()
|
|
// T4
|
|
addr |= (uint16(c.m.Read(uint16(iAddr+c.r.X+1))) << 8)
|
|
c.t()
|
|
// T5
|
|
c.m.Write(addr, f(c))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// acc2rmw performs 1-opcode, 2-cycle, accumulator rmw instructions. eg. ASL
|
|
func acc2rmw(f func(*cpu, byte) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
c.m.Read(c.r.PC)
|
|
c.r.A = f(c, c.r.A)
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zp5rmw performs 2-opcode, 5-cycle, zp rmw instructions. eg. ASL $70
|
|
func zp5rmw(f func(*cpu, byte) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr := uint16(c.m.Read(c.r.PC))
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
value := c.m.Read(addr)
|
|
c.t()
|
|
// T3
|
|
c.m.Write(addr, value)
|
|
c.t()
|
|
// T4
|
|
c.m.Write(addr, f(c, value))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// abs6rmw performs 3-opcode, 6-cycle, abs rmw instructions. eg. ASL $5F72
|
|
func abs6rmw(f func(*cpu, byte) byte) func(*cpu) {
|
|
return func(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.t()
|
|
// T3
|
|
value := c.m.Read(addr)
|
|
c.t()
|
|
// T4
|
|
c.m.Write(addr, value) // Spurious write...
|
|
c.t()
|
|
// T5
|
|
c.m.Write(addr, f(c, value))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// zpx6rmw performs 2-opcode, 6-cycle, zp,X rmw instructions. eg. ASL $70,X
|
|
func zpx6rmw(f func(*cpu, byte) byte) func(*cpu) {
|
|
return func(c *cpu) {
|
|
// T1
|
|
addr8 := c.m.Read(c.r.PC)
|
|
c.r.PC++
|
|
c.t()
|
|
// T2
|
|
c.m.Read(uint16(addr8))
|
|
c.t()
|
|
// T3
|
|
addr := uint16(addr8 + c.r.X)
|
|
value := c.m.Read(addr)
|
|
c.t()
|
|
// T4
|
|
c.m.Write(addr, value)
|
|
c.t()
|
|
// T5
|
|
c.m.Write(addr, f(c, value))
|
|
c.t()
|
|
}
|
|
}
|
|
|
|
// absx7rmw performs 3-opcode, 7-cycle, abs,X rmw instructions. eg. ASL $5F72,X
|
|
func absx7rmw(f func(*cpu, byte) byte) func(*cpu) {
|
|
return func(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)
|
|
addr += uint16(c.r.X)
|
|
c.r.PC++
|
|
c.t()
|
|
// T3
|
|
c.m.Read(addr)
|
|
c.t()
|
|
// T4
|
|
value := c.m.Read(addr)
|
|
c.t()
|
|
// T5
|
|
c.m.Write(addr, value) // Spurious write
|
|
c.t()
|
|
// T6
|
|
c.m.Write(addr, f(c, value))
|
|
c.t()
|
|
}
|
|
}
|