use super::{CPU, System, AddressType, AddressingMode, State, ByteRef, Byte, TaggedByte};
pub type InstructionFn = fn(&mut CPU, &mut dyn System, AddressingMode) -> ();
fn todo(cpu: &mut CPU, _sys: &mut dyn System, _am: AddressingMode) {
todo!("{:02x}{:04x} {:02x}", cpu.pbr, cpu.pc, cpu.ir);
}
#[inline(always)]
fn io(cpu: &mut CPU, sys: &mut dyn System) {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let _ = sys.read(effective, AddressType::Invalid, &cpu.signals);
}
#[inline(always)]
fn implied(cpu: &mut CPU, sys: &mut dyn System) {
io(cpu, sys);
if cpu.tcu == 1 {
cpu.state = State::Fetch;
}
}
fn brk_cop(_cpu: &mut CPU, _sys: &mut dyn System, _am: AddressingMode) {}
fn stp(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
io(cpu, sys);
if cpu.tcu == 2 {
if !cpu.aborted {
cpu.stp = true;
}
cpu.state = State::Fetch;
}
}
fn wai(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
io(cpu, sys);
if cpu.tcu == 2 {
if !cpu.aborted {
cpu.wai = true;
}
cpu.state = State::Fetch;
}
}
fn nop(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
}
fn clc(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.carry = false;
}
}
fn sec(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.carry = true;
}
}
fn cli(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.interrupt_disable = false;
}
}
fn sei(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.interrupt_disable = true;
}
}
fn cld(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.decimal = false;
}
}
fn sed(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.decimal = true;
}
}
fn clv(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if !cpu.aborted {
cpu.flags.overflow = false;
}
}
fn xce(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if cpu.tcu == 1 && !cpu.aborted {
let old_e = cpu.flags.emulation;
cpu.flags.emulation = cpu.flags.carry;
cpu.flags.carry = old_e;
cpu.set_e(cpu.flags.emulation);
}
}
fn lda(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
if !cpu.aborted {
ByteRef::Low(&mut cpu.a).set(l);
cpu.flags.zero = l == 0;
}
if cpu.a8() {
cpu.state = State::Fetch;
if !cpu.aborted {
cpu.flags.negative = l >> 7 != 0;
}
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.state = State::Fetch;
if !cpu.aborted {
ByteRef::High(&mut cpu.a).set(h);
cpu.flags.negative = h >> 7 != 0;
cpu.flags.zero = cpu.flags.zero && h == 0;
}
}
_ => (),
}
}
fn ldx(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
if !cpu.aborted {
ByteRef::Low(&mut cpu.x).set(l);
cpu.flags.zero = l == 0;
}
if cpu.m8() {
cpu.state = State::Fetch;
if !cpu.aborted {
cpu.flags.negative = l >> 7 != 0;
}
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.state = State::Fetch;
if !cpu.aborted {
ByteRef::High(&mut cpu.x).set(h);
cpu.flags.negative = h >> 7 != 0;
cpu.flags.zero = cpu.flags.zero && h == 0;
}
}
_ => (),
}
}
fn ldy(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
if !cpu.aborted {
ByteRef::Low(&mut cpu.y).set(l);
cpu.flags.zero = l == 0;
}
if cpu.m8() {
cpu.state = State::Fetch;
if !cpu.aborted {
cpu.flags.negative = l >> 7 != 0;
}
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.state = State::Fetch;
if !cpu.aborted {
ByteRef::High(&mut cpu.y).set(h);
cpu.flags.negative = h >> 7 != 0;
cpu.flags.zero = cpu.flags.zero && h == 0;
}
}
_ => (),
}
}
fn sta(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.write(cpu, sys, cpu.a) {
Some(TaggedByte::Data(Byte::Low(_))) if cpu.a8() => {
cpu.state = State::Fetch;
}
Some(TaggedByte::Data(Byte::High(_))) => {
cpu.state = State::Fetch;
}
_ => (),
}
}
fn stx(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.write(cpu, sys, cpu.x) {
Some(TaggedByte::Data(Byte::Low(_))) if cpu.m8() => {
cpu.state = State::Fetch;
}
Some(TaggedByte::Data(Byte::High(_))) => {
cpu.state = State::Fetch;
}
_ => (),
}
}
fn sty(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.write(cpu, sys, cpu.y) {
Some(TaggedByte::Data(Byte::Low(_))) if cpu.m8() => {
cpu.state = State::Fetch;
}
Some(TaggedByte::Data(Byte::High(_))) => {
cpu.state = State::Fetch;
}
_ => (),
}
}
fn stz(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.write(cpu, sys, 0) {
Some(TaggedByte::Data(Byte::Low(_))) if cpu.a8() => {
cpu.state = State::Fetch;
}
Some(TaggedByte::Data(Byte::High(_))) => {
cpu.state = State::Fetch;
}
_ => (),
}
}
#[inline]
fn jsr_al(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
2 => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::High(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
3 => {
cpu.stack_push(sys, cpu.pbr, false);
}
4 => {
let mut s = cpu.s.wrapping_add(1);
if cpu.flags.emulation {
ByteRef::High(&mut s).set(0x01);
}
sys.read(s as u32, AddressType::Invalid, &cpu.signals);
}
5 => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
cpu.temp_bank = data;
}
6 => {
let pc = ByteRef::High(&mut cpu.pc).get();
cpu.stack_push(sys, pc, false);
}
7 => {
let pc = ByteRef::Low(&mut cpu.pc).get();
cpu.stack_push(sys, pc, false);
if !cpu.aborted {
cpu.pc = cpu.temp_addr;
cpu.pbr = cpu.temp_bank;
}
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn jsr(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
const ABS: AddressingMode = AddressingMode::Absolute;
const IDX_IN: AddressingMode = AddressingMode::IndexedIndirectX;
match (cpu.tcu, am) {
(1, ABS) | (1, IDX_IN) => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
(2, ABS) | (4, IDX_IN) => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::High(&mut cpu.temp_addr).set(data);
}
(3, ABS) | (5, IDX_IN) => {
io(cpu, sys)
}
(4, ABS) | (2, IDX_IN) => {
let pc = ByteRef::High(&mut cpu.pc).get();
cpu.stack_push(sys, pc, false);
}
(5, ABS) | (3, IDX_IN) => {
let pc = ByteRef::Low(&mut cpu.pc).get();
cpu.stack_push(sys, pc, false);
if am == ABS {
if !cpu.aborted {
cpu.pc = cpu.temp_addr;
}
cpu.state = State::Fetch;
}
}
(6, IDX_IN) => {
let pc = ((cpu.pbr as u32) << 16) | ((cpu.temp_addr.wrapping_add(cpu.x)) as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_data).set(data);
}
(7, IDX_IN) => {
let pc = ((cpu.pbr as u32) << 16) | ((cpu.temp_addr.wrapping_add(cpu.x).wrapping_add(1)) as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::High(&mut cpu.temp_data).set(data);
if !cpu.aborted {
cpu.pc = cpu.temp_data;
}
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn jml(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
2 => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
ByteRef::High(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
3 => {
let pc = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(pc, AddressType::Program, &cpu.signals);
if !cpu.aborted {
cpu.pbr = data;
cpu.pc = cpu.temp_addr;
}
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn jmp(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am {
AddressingMode::IndirectLong => {
todo!("jmp [abs]")
}
_ => (),
}
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => cpu.temp_data = l as u16,
Some(TaggedByte::Data(Byte::High(h))) => {
ByteRef::High(&mut cpu.temp_data).set(h);
cpu.pc = cpu.temp_data;
cpu.state = State::Fetch;
}
_ => (),
}
}
fn sep(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = match cpu.tcu {
1 => {
cpu.pc = cpu.pc.wrapping_add(1);
sys.read(effective, AddressType::Program, &cpu.signals)
}
2 => sys.read(effective, AddressType::Invalid, &cpu.signals),
_ => unreachable!(),
};
if cpu.tcu == 1 && !cpu.aborted {
cpu.flags.set_mask(data, true);
cpu.signals.m = cpu.flags.mem_sel;
cpu.signals.x = cpu.flags.index_sel;
if cpu.flags.index_sel {
ByteRef::High(&mut cpu.x).set(0);
ByteRef::High(&mut cpu.y).set(0);
}
}
if cpu.tcu == 2 {
cpu.state = State::Fetch;
}
}
fn rep(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = match cpu.tcu {
1 => {
cpu.pc = cpu.pc.wrapping_add(1);
sys.read(effective, AddressType::Program, &cpu.signals)
}
2 => sys.read(effective, AddressType::Invalid, &cpu.signals),
_ => unreachable!(),
};
if cpu.tcu == 1 && !cpu.aborted {
cpu.flags.set_mask(data, false);
cpu.signals.m = cpu.flags.mem_sel;
cpu.signals.x = cpu.flags.index_sel;
if cpu.flags.index_sel {
ByteRef::High(&mut cpu.x).set(0);
ByteRef::High(&mut cpu.y).set(0);
}
}
if cpu.tcu == 2 {
cpu.state = State::Fetch;
}
}
fn rts(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
const RTS: AddressingMode = AddressingMode::Absolute;
const RTL: AddressingMode = AddressingMode::AbsoluteLong;
match (am, cpu.tcu) {
(_, 1 | 2) => io(cpu, sys),
(_, 3) => {
let data = cpu.stack_pop(sys);
ByteRef::Low(&mut cpu.pc).set(data);
}
(_, 4) => {
let data = cpu.stack_pop(sys);
ByteRef::High(&mut cpu.pc).set(data);
}
(RTS, 5) => {
let mut s = cpu.s;
if cpu.flags.emulation {
ByteRef::High(&mut s).set(0x01);
}
sys.read(s as u32, AddressType::Invalid, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
cpu.state = State::Fetch;
}
(RTL, 5) => {
let data = cpu.stack_pop(sys);
cpu.pbr = data;
cpu.pc = cpu.pc.wrapping_add(1);
cpu.state = State::Fetch;
}
_ => todo!()
}
}
fn rti(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 | 2 => io(cpu, sys),
3 => {
let data = cpu.stack_pop(sys);
cpu.set_p(data);
}
4 => {
let data = cpu.stack_pop(sys);
ByteRef::Low(&mut cpu.pc).set(data);
}
5 => {
let data = cpu.stack_pop(sys);
ByteRef::High(&mut cpu.pc).set(data);
}
6 => {
let data = cpu.stack_pop(sys);
cpu.pbr = data;
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
macro_rules! transfer {
($name:ident, $cpu:ident, $cond8:expr, $r0:expr, $r1:expr) => {
fn $name($cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied($cpu, sys);
if $cpu.tcu == 2 {
if !$cpu.aborted {
if $cond8 {
let data = ByteRef::Low(&mut $r0).get();
ByteRef::Low(&mut $r1).set(data);
} else {
$r1 = $r0;
}
}
$cpu.state = State::Fetch;
}
}
}
}
transfer!(tax, cpu, cpu.m8(), cpu.a, cpu.x);
transfer!(tay, cpu, cpu.m8(), cpu.a, cpu.y);
transfer!(tsx, cpu, cpu.flags.emulation, cpu.s, cpu.x);
transfer!(txa, cpu, cpu.a8(), cpu.x, cpu.a);
transfer!(txs, cpu, cpu.flags.emulation, cpu.x, cpu.s);
transfer!(txy, cpu, cpu.flags.emulation, cpu.x, cpu.y);
transfer!(tya, cpu, cpu.a8(), cpu.y, cpu.a);
transfer!(tyx, cpu, cpu.flags.emulation, cpu.y, cpu.x);
macro_rules! transfer16 {
($name:ident, $cpu:ident, $r0:expr, $r1:expr) => {
fn $name($cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied($cpu, sys);
if !$cpu.aborted {
$r1 = $r0;
}
$cpu.state = State::Fetch;
}
}
}
transfer16!(tcd, cpu, cpu.a, cpu.d);
transfer16!(tcs, cpu, cpu.a, cpu.s);
transfer16!(tdc, cpu, cpu.d, cpu.a);
transfer16!(tsc, cpu, cpu.s, cpu.a);
fn adc(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let l = l as u16;
let c = if cpu.flags.carry { 1 } else { 0 };
let a = ByteRef::Low(&mut cpu.a).get() as u16;
let mut r = a.wrapping_add(l).wrapping_add(c);
if cpu.flags.decimal {
if (a ^ l ^ r) & 0x10 != 0 {
r = r.wrapping_add(6);
}
if (r & 0xf0) > 0x90 {
r = r.wrapping_add(0x60);
}
}
ByteRef::Low(&mut cpu.a).set(r as u8);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = r == 0;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.overflow = (a ^ r) & (l ^ r) & 0x80 != 0;
if cpu.a8() {
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let h = h as u16;
let c = if cpu.flags.carry { 1 } else { 0 };
let a = ByteRef::High(&mut cpu.a).get() as u16;
let mut r = a.wrapping_add(h).wrapping_add(c);
if cpu.flags.decimal {
if (a ^ h ^ r) & 0x10 != 0 {
r = r.wrapping_add(6);
}
if (r & 0xf0) > 0x90 {
r = r.wrapping_add(0x60);
}
}
ByteRef::High(&mut cpu.a).set(r as u8);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = cpu.flags.zero && r == 0;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.overflow = (a ^ r) & (h ^ r) & 0x80 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
fn sbc(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let l = (!l) as u16;
let c = if cpu.flags.carry { 1 } else { 0 };
let a = ByteRef::Low(&mut cpu.a).get() as u16;
let mut r = a.wrapping_add(l).wrapping_add(c);
if cpu.flags.decimal {
if (a ^ l ^ r) & 0x10 != 0 {
r = r.wrapping_add(6);
}
if (r & 0xf0) > 0x90 {
r = r.wrapping_add(0x60);
}
}
ByteRef::Low(&mut cpu.a).set(r as u8);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = r == 0;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.overflow = (a ^ r) & (l ^ r) & 0x80 != 0;
if cpu.a8() {
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let h = (!h) as u16;
let c = if cpu.flags.carry { 1 } else { 0 };
let a = ByteRef::High(&mut cpu.a).get() as u16;
let mut r = a.wrapping_add(h).wrapping_add(c);
if cpu.flags.decimal {
if (a ^ h ^ r) & 0x10 != 0 {
r = r.wrapping_add(6);
}
if (r & 0xf0) > 0x90 {
r = r.wrapping_add(0x60);
}
}
ByteRef::High(&mut cpu.a).set(r as u8);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = cpu.flags.zero && r == 0;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.overflow = (a ^ r) & (h ^ r) & 0x80 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
fn and(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let a = ByteRef::Low(&mut cpu.a).get() & l;
ByteRef::Low(&mut cpu.a).set(a);
cpu.flags.zero = a == 0;
if cpu.a8() {
cpu.flags.negative = a >> 7 != 0;
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let a = ByteRef::High(&mut cpu.a).get() & h;
ByteRef::High(&mut cpu.a).set(a);
cpu.flags.zero = cpu.flags.zero && a == 0;
cpu.flags.negative = a >> 7 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
fn ora(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let a = ByteRef::Low(&mut cpu.a).get() | l;
ByteRef::Low(&mut cpu.a).set(a);
cpu.flags.zero = a == 0;
if cpu.a8() {
cpu.flags.negative = a >> 7 != 0;
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let a = ByteRef::High(&mut cpu.a).get() | h;
ByteRef::High(&mut cpu.a).set(a);
cpu.flags.zero = cpu.flags.zero && a == 0;
cpu.flags.negative = a >> 7 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
fn eor(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let a = ByteRef::Low(&mut cpu.a).get() ^ l;
ByteRef::Low(&mut cpu.a).set(a);
cpu.flags.zero = a == 0;
if cpu.a8() {
cpu.flags.negative = a >> 7 != 0;
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let a = ByteRef::High(&mut cpu.a).get() ^ h;
ByteRef::High(&mut cpu.a).set(a);
cpu.flags.zero = cpu.flags.zero && a == 0;
cpu.flags.negative = a >> 7 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
macro_rules! branch {
($name:ident, $cpu:ident, $expr:expr) => {
fn $name($cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match $cpu.tcu {
1 => {
let effective = (($cpu.pbr as u32) << 16) | ($cpu.pc as u32);
let offset = sys.read(effective, AddressType::Program, &$cpu.signals);
$cpu.temp_addr = offset as u16;
$cpu.pc = $cpu.pc.wrapping_add(1);
$cpu.temp_data = $cpu.pc;
if !($expr) {
$cpu.state = State::Fetch;
}
}
2 => {
io($cpu, sys);
let ba = $cpu.pc.wrapping_add_signed(($cpu.temp_addr as i8).into());
$cpu.pc = ba;
if !$cpu.flags.emulation || $cpu.pc & 0xff00 == ba & 0xff00 {
$cpu.state = State::Fetch;
}
}
3 => {
let effective = (($cpu.pbr as u32) << 16) | ($cpu.temp_data as u32);
sys.read(effective, AddressType::Invalid, &$cpu.signals);
$cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
}
}
branch!(bcc, cpu, !cpu.flags.carry);
branch!(bcs, cpu, cpu.flags.carry);
branch!(bne, cpu, !cpu.flags.zero);
branch!(beq, cpu, cpu.flags.zero);
branch!(bpl, cpu, !cpu.flags.negative);
branch!(bmi, cpu, cpu.flags.negative);
branch!(bvc, cpu, !cpu.flags.overflow);
branch!(bvs, cpu, cpu.flags.overflow);
branch!(bra, cpu, true);
macro_rules! push {
($name:ident, $cpu:ident, $r:expr, $b16:expr) => {
fn $name($cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
let mut r: u16 = $r;
match $cpu.tcu {
1 if $b16 => {
let b = ByteRef::High(&mut r).get();
$cpu.stack_push(sys, b, false);
}
1 | 2 => {
let b = ByteRef::Low(&mut r).get();
$cpu.stack_push(sys, b, false);
$cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
}
}
macro_rules! pull8 {
($name:ident, $cpu:ident, $v:ident, $set:stmt) => {
fn $name($cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match $cpu.tcu {
1 => {
let $v = $cpu.stack_pop(sys);
$set;
$cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
}
}
macro_rules! pull16 {
($name:ident, $cpu:ident, $r:expr, $b16:expr) => {
fn $name($cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match $cpu.tcu {
1 => {
let b = $cpu.stack_pop(sys);
ByteRef::Low(&mut $r).set(b);
if !($b16) {
$cpu.state = State::Fetch;
}
}
2 => {
let b = $cpu.stack_pop(sys);
ByteRef::High(&mut $r).set(b);
$cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
}
}
push!(pha, cpu, cpu.a, cpu.a16());
push!(phb, cpu, cpu.dbr as u16, false);
push!(phd, cpu, cpu.d, true);
push!(phk, cpu, cpu.pbr as u16, false);
push!(php, cpu, cpu.flags.as_byte() as u16, false);
push!(phx, cpu, cpu.x, cpu.m16());
push!(phy, cpu, cpu.y, cpu.m16());
pull16!(pla, cpu, cpu.a, cpu.a16());
pull8!(plb, cpu, b, cpu.dbr = b);
pull16!(pld, cpu, cpu.d, true);
pull8!(plp, cpu, p, cpu.set_p(p));
pull16!(plx, cpu, cpu.x, cpu.m16());
pull16!(ply, cpu, cpu.y, cpu.m16());
fn asl(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let b16 = cpu.a16();
let res = am.rwb(cpu, sys, |cpu, mut v, b16| if b16 {
cpu.flags.carry = v >> 15 != 0;
v <<= 1;
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 15 != 0;
v
} else {
let mut v = v as u8;
cpu.flags.carry = v >> 7 != 0;
v <<= 1;
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 7 != 0;
v as u16
}, b16);
if let Some(TaggedByte::Data(Byte::Low(_))) = res {
cpu.state = State::Fetch;
}
}
fn inc(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let b16 = cpu.a16();
let res = am.rwb(cpu, sys, |cpu, mut v, b16| if b16 {
v = v.wrapping_add(1);
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 15 != 0;
v
} else {
let mut v = v as u8;
v = v.wrapping_add(1);
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 7 != 0;
v as u16
}, b16);
if let Some(TaggedByte::Data(Byte::Low(_))) = res {
cpu.state = State::Fetch;
}
}
fn dec(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let b16 = cpu.a16();
let res = am.rwb(cpu, sys, |cpu, mut v, b16| if b16 {
v = v.wrapping_sub(1);
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 15 != 0;
v
} else {
let mut v = v as u8;
v = v.wrapping_sub(1);
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 7 != 0;
v as u16
}, b16);
if let Some(TaggedByte::Data(Byte::Low(_))) = res {
cpu.state = State::Fetch;
}
}
macro_rules! push_effective {
($name:ident) => {
fn $name(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
if cpu.tcu == 1 {
cpu.temp_bank = 0;
}
match cpu.temp_bank {
1 => {
cpu.stack_push(sys, (cpu.temp_data >> 8) as u8, false);
cpu.temp_bank += 1;
}
2 => {
cpu.stack_push(sys, cpu.temp_data as u8, false);
cpu.state = State::Fetch;
}
_ => match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => cpu.temp_data = l as u16,
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.temp_data |= (h as u16) << 8;
cpu.temp_bank = 1;
}
_ => (),
}
}
}
}
}
push_effective!(pea);
push_effective!(pei);
fn per(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(effective, AddressType::Program, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
ByteRef::Low(&mut cpu.temp_addr).set(data);
}
2 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(effective, AddressType::Program, &cpu.signals);
ByteRef::High(&mut cpu.temp_addr).set(data);
}
3 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let _ = sys.read(effective, AddressType::Invalid, &cpu.signals);
cpu.temp_addr = cpu.temp_addr.wrapping_add(cpu.pc);
}
4 => {
let data = ByteRef::High(&mut cpu.temp_addr).get();
cpu.stack_push(sys, data, false);
}
5 => {
let data = ByteRef::Low(&mut cpu.temp_addr).get();
cpu.stack_push(sys, data, false);
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn bit(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let a = ByteRef::Low(&mut cpu.a).get();
cpu.flags.zero = l & a == 0;
if cpu.a8() {
cpu.flags.negative = l >> 7 != 0;
cpu.flags.overflow = (l >> 6) & 1 != 0;
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let a = ByteRef::High(&mut cpu.a).get();
cpu.flags.zero = cpu.flags.zero && h & a == 0;
cpu.flags.negative = h >> 7 != 0;
cpu.flags.overflow = (h >> 6) & 1 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
pub const INSTRUCTIONS: [(InstructionFn, AddressingMode); 0x100] = [
(brk_cop, AddressingMode::Implied), (ora, AddressingMode::DirectIndirectX), (brk_cop, AddressingMode::Implied), (ora, AddressingMode::StackRel), (todo, AddressingMode::Implied), (ora, AddressingMode::Direct), (asl, AddressingMode::Direct), (ora, AddressingMode::DirectIndirectLong), (php, AddressingMode::Implied), (ora, AddressingMode::Immediate), (asl, AddressingMode::Accumulator), (phd, AddressingMode::Implied), (todo, AddressingMode::Implied), (ora, AddressingMode::Absolute), (asl, AddressingMode::Absolute), (ora, AddressingMode::AbsoluteLong), (bpl, AddressingMode::Immediate), (ora, AddressingMode::DirectIndirectIndexedY), (ora, AddressingMode::DirectIndirect), (ora, AddressingMode::StackRelIndirectIndexedY), (todo, AddressingMode::Implied), (ora, AddressingMode::DirectIndexedX), (asl, AddressingMode::DirectIndexedX), (ora, AddressingMode::DirectIndirectLongIndexedY), (clc, AddressingMode::Implied), (ora, AddressingMode::AbsoluteIndexedY), (inc, AddressingMode::Accumulator), (tcs, AddressingMode::Implied), (todo, AddressingMode::Implied), (ora, AddressingMode::AbsoluteIndexedX), (asl, AddressingMode::AbsoluteIndexedX), (ora, AddressingMode::AbsoluteLongIndexedX), (jsr, AddressingMode::Absolute), (and, AddressingMode::DirectIndirectX), (jsr_al, AddressingMode::AbsoluteLong), (and, AddressingMode::StackRel), (bit, AddressingMode::Direct), (and, AddressingMode::Direct), (todo, AddressingMode::Implied), (and, AddressingMode::DirectIndirectLong), (plp, AddressingMode::Implied), (and, AddressingMode::Immediate), (todo, AddressingMode::Implied), (pld, AddressingMode::Implied), (bit, AddressingMode::Absolute), (and, AddressingMode::Absolute), (todo, AddressingMode::Implied), (and, AddressingMode::AbsoluteLong), (bmi, AddressingMode::Immediate), (and, AddressingMode::DirectIndirectIndexedY), (and, AddressingMode::DirectIndirect), (and, AddressingMode::StackRelIndirectIndexedY), (bit, AddressingMode::DirectIndexedX), (and, AddressingMode::DirectIndexedX), (todo, AddressingMode::Implied), (and, AddressingMode::DirectIndirectLongIndexedY), (sec, AddressingMode::Implied), (and, AddressingMode::AbsoluteIndexedY), (dec, AddressingMode::Accumulator), (tsc, AddressingMode::Implied), (todo, AddressingMode::Implied), (and, AddressingMode::AbsoluteIndexedX), (todo, AddressingMode::Implied), (and, AddressingMode::AbsoluteLongIndexedX), (rti, AddressingMode::Implied), (eor, AddressingMode::DirectIndirectX), (todo, AddressingMode::Implied), (eor, AddressingMode::StackRel), (todo, AddressingMode::Implied), (eor, AddressingMode::Direct), (todo, AddressingMode::Implied), (eor, AddressingMode::DirectIndirectLong), (pha, AddressingMode::Implied), (eor, AddressingMode::Immediate), (todo, AddressingMode::Implied), (phk, AddressingMode::Implied), (jmp, AddressingMode::Immediate), (eor, AddressingMode::Absolute), (todo, AddressingMode::Implied), (eor, AddressingMode::AbsoluteLong), (bvc, AddressingMode::Immediate), (eor, AddressingMode::DirectIndirectIndexedY), (eor, AddressingMode::DirectIndirect), (eor, AddressingMode::StackRelIndirectIndexedY), (todo, AddressingMode::Implied), (eor, AddressingMode::DirectIndexedX), (todo, AddressingMode::Implied), (eor, AddressingMode::DirectIndirectLongIndexedY), (cli, AddressingMode::Implied), (eor, AddressingMode::AbsoluteIndexedY), (phy, AddressingMode::Implied), (tcd, AddressingMode::Implied), (jml, AddressingMode::AbsoluteLong), (eor, AddressingMode::AbsoluteIndexedX), (todo, AddressingMode::Implied), (eor, AddressingMode::AbsoluteLongIndexedX), (rts, AddressingMode::Absolute), (adc, AddressingMode::DirectIndirectX), (todo, AddressingMode::Implied), (adc, AddressingMode::StackRel), (stz, AddressingMode::Direct), (adc, AddressingMode::Direct), (todo, AddressingMode::Implied), (adc, AddressingMode::DirectIndirectLong), (pla, AddressingMode::Implied), (adc, AddressingMode::Immediate), (todo, AddressingMode::Implied), (rts, AddressingMode::AbsoluteLong), (jmp, AddressingMode::Indirect), (adc, AddressingMode::Absolute), (todo, AddressingMode::Implied), (adc, AddressingMode::AbsoluteLong), (bvs, AddressingMode::Immediate), (adc, AddressingMode::DirectIndirectIndexedY), (adc, AddressingMode::DirectIndirect), (adc, AddressingMode::StackRelIndirectIndexedY), (stz, AddressingMode::DirectIndexedX), (adc, AddressingMode::DirectIndexedX), (todo, AddressingMode::Implied), (adc, AddressingMode::DirectIndirectLongIndexedY), (sei, AddressingMode::Implied), (adc, AddressingMode::AbsoluteIndexedY), (ply, AddressingMode::Implied), (tdc, AddressingMode::Implied), (jmp, AddressingMode::IndexedIndirectX), (adc, AddressingMode::AbsoluteIndexedX), (todo, AddressingMode::Implied), (adc, AddressingMode::AbsoluteLongIndexedX), (bra, AddressingMode::Immediate), (sta, AddressingMode::DirectIndirectX), (todo, AddressingMode::Implied), (sta, AddressingMode::StackRel), (sty, AddressingMode::Direct), (sta, AddressingMode::Direct), (stx, AddressingMode::Direct), (sta, AddressingMode::DirectIndirectLong), (todo, AddressingMode::Implied), (bit, AddressingMode::Immediate), (txa, AddressingMode::Implied), (phb, AddressingMode::Implied), (sty, AddressingMode::Absolute), (sta, AddressingMode::Absolute), (stx, AddressingMode::Absolute), (sta, AddressingMode::AbsoluteLong), (bcc, AddressingMode::Immediate), (sta, AddressingMode::DirectIndirectIndexedY), (sta, AddressingMode::DirectIndirect), (sta, AddressingMode::StackRelIndirectIndexedY), (sty, AddressingMode::DirectIndexedX), (sta, AddressingMode::DirectIndexedX), (stx, AddressingMode::DirectIndexedY), (sta, AddressingMode::DirectIndirectLongIndexedY), (tya, AddressingMode::Implied), (sta, AddressingMode::AbsoluteIndexedY), (txs, AddressingMode::Implied), (txy, AddressingMode::Implied), (stz, AddressingMode::Absolute), (sta, AddressingMode::AbsoluteIndexedX), (stz, AddressingMode::AbsoluteIndexedX), (sta, AddressingMode::AbsoluteLongIndexedX), (ldy, AddressingMode::Immediate), (lda, AddressingMode::DirectIndirectX), (ldx, AddressingMode::Immediate), (lda, AddressingMode::StackRel), (ldy, AddressingMode::Direct), (lda, AddressingMode::Direct), (ldx, AddressingMode::Direct), (lda, AddressingMode::DirectIndirectLong), (tay, AddressingMode::Implied), (lda, AddressingMode::Immediate), (tax, AddressingMode::Implied), (plb, AddressingMode::Implied), (ldy, AddressingMode::Absolute), (lda, AddressingMode::Absolute), (ldx, AddressingMode::Absolute), (lda, AddressingMode::AbsoluteLong), (bcs, AddressingMode::Immediate), (lda, AddressingMode::DirectIndirectIndexedY), (lda, AddressingMode::DirectIndirect), (lda, AddressingMode::StackRelIndirectIndexedY), (ldy, AddressingMode::DirectIndexedX), (lda, AddressingMode::DirectIndexedX), (ldx, AddressingMode::DirectIndexedY), (lda, AddressingMode::DirectIndirectLongIndexedY), (clv, AddressingMode::Implied), (lda, AddressingMode::AbsoluteIndexedY), (tsx, AddressingMode::Implied), (tyx, AddressingMode::Implied), (ldy, AddressingMode::AbsoluteIndexedX), (lda, AddressingMode::AbsoluteIndexedX), (ldx, AddressingMode::AbsoluteIndexedY), (lda, AddressingMode::AbsoluteLongIndexedX), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (rep, AddressingMode::Immediate), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (dec, AddressingMode::Direct), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (wai, AddressingMode::Implied), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (dec, AddressingMode::Absolute), (todo, AddressingMode::Implied), (bne, AddressingMode::Immediate), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (pei, AddressingMode::Direct), (todo, AddressingMode::Implied), (dec, AddressingMode::DirectIndexedX), (todo, AddressingMode::Implied), (cld, AddressingMode::Implied), (todo, AddressingMode::Implied), (phx, AddressingMode::Implied), (stp, AddressingMode::Implied), (jmp, AddressingMode::IndirectLong), (todo, AddressingMode::Implied), (dec, AddressingMode::AbsoluteIndexedX), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (sbc, AddressingMode::DirectIndirectX), (sep, AddressingMode::Immediate), (sbc, AddressingMode::StackRel), (todo, AddressingMode::Implied), (sbc, AddressingMode::Direct), (inc, AddressingMode::Direct), (sbc, AddressingMode::DirectIndirectLong), (todo, AddressingMode::Implied), (sbc, AddressingMode::Immediate), (nop, AddressingMode::Implied), (todo, AddressingMode::Implied), (todo, AddressingMode::Implied), (sbc, AddressingMode::Absolute), (inc, AddressingMode::Absolute), (sbc, AddressingMode::AbsoluteLong), (beq, AddressingMode::Immediate), (sbc, AddressingMode::DirectIndirectIndexedY), (sbc, AddressingMode::DirectIndirect), (sbc, AddressingMode::StackRelIndirectIndexedY), (pea, AddressingMode::Immediate), (sbc, AddressingMode::DirectIndexedX), (inc, AddressingMode::DirectIndexedX), (sbc, AddressingMode::DirectIndirectLongIndexedY), (sed, AddressingMode::Implied), (sbc, AddressingMode::AbsoluteIndexedY), (plx, AddressingMode::Implied), (xce, AddressingMode::Implied), (jsr, AddressingMode::IndexedIndirectX), (sbc, AddressingMode::AbsoluteIndexedX), (inc, AddressingMode::AbsoluteIndexedX), (sbc, AddressingMode::AbsoluteLongIndexedX), ];