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);
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 {
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 {
cpu.wai = true;
cpu.state = State::Fetch;
}
}
fn nop(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
}
fn xba(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 => { io(cpu, sys); }
2 => {
io(cpu, sys);
let lo = ByteRef::Low(&mut cpu.a).get();
let hi = ByteRef::High(&mut cpu.a).get();
ByteRef::Low(&mut cpu.a).set(hi);
ByteRef::High(&mut cpu.a).set(lo);
cpu.flags.zero = hi == 0;
cpu.flags.negative = hi >> 7 != 0;
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn brl(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);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
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.pc = cpu.pc.wrapping_add(1).wrapping_add_signed(cpu.temp_addr as i16);
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn mvn(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
0 => { let ea = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let _ = sys.read(ea, AddressType::Opcode, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
}
1 => { let ea = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
cpu.temp_bank = sys.read(ea, AddressType::Program, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
}
2 => { let ea = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let sba = sys.read(ea, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(sba);
}
3 => { let sba = ByteRef::Low(&mut cpu.temp_addr).get() as u32;
let x = if cpu.flags.index_sel { cpu.x & 0xFF } else { cpu.x } as u32;
let data = sys.read((sba << 16) | x, AddressType::Data, &cpu.signals);
ByteRef::Low(&mut cpu.temp_data).set(data);
}
4 => { let dba = cpu.temp_bank as u32;
let y = if cpu.flags.index_sel { cpu.y & 0xFF } else { cpu.y } as u32;
let data = ByteRef::Low(&mut cpu.temp_data).get();
sys.write((dba << 16) | y, data, AddressType::Data, &cpu.signals);
cpu.dbr = cpu.temp_bank;
}
5 => { let y = if cpu.flags.index_sel { cpu.y & 0xFF } else { cpu.y } as u32;
let ea = ((cpu.temp_bank as u32) << 16) | y;
let _ = sys.read(ea, AddressType::Invalid, &cpu.signals);
}
6 => { let y = if cpu.flags.index_sel { cpu.y & 0xFF } else { cpu.y } as u32;
let ea = ((cpu.temp_bank as u32) << 16) | y;
let _ = sys.read(ea, AddressType::Invalid, &cpu.signals);
cpu.x = cpu.x.wrapping_add(1);
cpu.y = cpu.y.wrapping_add(1);
if cpu.flags.index_sel {
ByteRef::High(&mut cpu.x).set(0);
ByteRef::High(&mut cpu.y).set(0);
}
cpu.a = cpu.a.wrapping_sub(1);
if cpu.a == 0xFFFF {
cpu.pc = cpu.pc.wrapping_add(1);
cpu.state = State::Fetch;
} else {
cpu.pc = cpu.pc.wrapping_sub(2);
cpu.tcu = 0xFF;
}
}
_ => unreachable!(),
}
}
fn mvp(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
0 => { let ea = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let _ = sys.read(ea, AddressType::Opcode, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
}
1 => { let ea = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
cpu.temp_bank = sys.read(ea, AddressType::Program, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
}
2 => { let ea = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let sba = sys.read(ea, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(sba);
}
3 => { let sba = ByteRef::Low(&mut cpu.temp_addr).get() as u32;
let x = if cpu.flags.index_sel { cpu.x & 0xFF } else { cpu.x } as u32;
let data = sys.read((sba << 16) | x, AddressType::Data, &cpu.signals);
ByteRef::Low(&mut cpu.temp_data).set(data);
}
4 => { let dba = cpu.temp_bank as u32;
let y = if cpu.flags.index_sel { cpu.y & 0xFF } else { cpu.y } as u32;
let data = ByteRef::Low(&mut cpu.temp_data).get();
sys.write((dba << 16) | y, data, AddressType::Data, &cpu.signals);
cpu.dbr = cpu.temp_bank;
}
5 => { let y = if cpu.flags.index_sel { cpu.y & 0xFF } else { cpu.y } as u32;
let ea = ((cpu.temp_bank as u32) << 16) | y;
let _ = sys.read(ea, AddressType::Invalid, &cpu.signals);
}
6 => { let y = if cpu.flags.index_sel { cpu.y & 0xFF } else { cpu.y } as u32;
let ea = ((cpu.temp_bank as u32) << 16) | y;
let _ = sys.read(ea, AddressType::Invalid, &cpu.signals);
cpu.x = cpu.x.wrapping_sub(1);
cpu.y = cpu.y.wrapping_sub(1);
if cpu.flags.index_sel {
ByteRef::High(&mut cpu.x).set(0);
ByteRef::High(&mut cpu.y).set(0);
}
cpu.a = cpu.a.wrapping_sub(1);
if cpu.a == 0xFFFF {
cpu.pc = cpu.pc.wrapping_add(1);
cpu.state = State::Fetch;
} else {
cpu.pc = cpu.pc.wrapping_sub(2);
cpu.tcu = 0xFF;
}
}
_ => unreachable!(),
}
}
fn wdm(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let _ = sys.read(effective, AddressType::Program, &cpu.signals);
cpu.pc = cpu.pc.wrapping_add(1);
cpu.state = State::Fetch;
}
fn clc(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.carry = false;
}
fn sec(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.carry = true;
}
fn cli(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.interrupt_disable = false;
}
fn sei(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.interrupt_disable = true;
}
fn cld(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.decimal = false;
}
fn sed(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.decimal = true;
}
fn clv(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.flags.overflow = false;
}
fn xce(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if cpu.tcu == 1 {
core::mem::swap(&mut cpu.flags.emulation, &mut cpu.flags.carry);
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))) => {
ByteRef::Low(&mut cpu.a).set(l);
cpu.flags.zero = l == 0;
if cpu.a8() {
cpu.state = State::Fetch;
cpu.flags.negative = l >> 7 != 0;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.state = State::Fetch;
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))) => {
ByteRef::Low(&mut cpu.x).set(l);
cpu.flags.zero = l == 0;
if cpu.m8() {
cpu.state = State::Fetch;
cpu.flags.negative = l >> 7 != 0;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.state = State::Fetch;
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))) => {
ByteRef::Low(&mut cpu.y).set(l);
cpu.flags.zero = l == 0;
if cpu.m8() {
cpu.state = State::Fetch;
cpu.flags.negative = l >> 7 != 0;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
cpu.state = State::Fetch;
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_raw(sys, cpu.pbr, false);
}
4 => {
let s = cpu.s.wrapping_add(1);
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_raw(sys, pc, false);
}
7 => {
let pc = ByteRef::Low(&mut cpu.pc).get();
cpu.stack_push_raw(sys, pc, false);
cpu.pc = cpu.temp_addr;
cpu.pbr = cpu.temp_bank;
cpu.enforce_emulation_stack();
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 {
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);
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);
cpu.pbr = data;
cpu.pc = cpu.temp_addr;
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn jmp(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
if let AddressingMode::IndirectLong = am {
match cpu.tcu {
1 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(effective, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
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);
cpu.pc = cpu.pc.wrapping_add(1);
}
3 => {
let data = sys.read(cpu.temp_addr as u32, AddressType::Data, &cpu.signals);
ByteRef::Low(&mut cpu.temp_data).set(data);
}
4 => {
let data = sys.read((cpu.temp_addr as u32 + 1) & 0xFFFF, AddressType::Data, &cpu.signals);
ByteRef::High(&mut cpu.temp_data).set(data);
}
5 => {
let data = sys.read((cpu.temp_addr as u32 + 2) & 0xFFFF, AddressType::Data, &cpu.signals);
cpu.pc = cpu.temp_data;
cpu.pbr = data;
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
return;
}
if let AddressingMode::Indirect = am {
match cpu.tcu {
1 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(effective, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
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);
cpu.pc = cpu.pc.wrapping_add(1);
}
3 => {
let data = sys.read(cpu.temp_addr as u32, AddressType::Data, &cpu.signals);
ByteRef::Low(&mut cpu.temp_data).set(data);
}
4 => {
let data = sys.read((cpu.temp_addr as u32 + 1) & 0xFFFF, AddressType::Data, &cpu.signals);
ByteRef::High(&mut cpu.temp_data).set(data);
cpu.pc = cpu.temp_data;
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
return;
}
if let AddressingMode::IndexedIndirectX = am {
match cpu.tcu {
1 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc as u32);
let data = sys.read(effective, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_addr).set(data);
cpu.pc = cpu.pc.wrapping_add(1);
}
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);
cpu.pc = cpu.pc.wrapping_add(1);
}
3 => {
let effective = ((cpu.pbr as u32) << 16) | (cpu.pc.wrapping_sub(1) as u32);
let _ = sys.read(effective, AddressType::Invalid, &cpu.signals);
}
4 => {
let ptr = ((cpu.pbr as u32) << 16) | (cpu.temp_addr.wrapping_add(cpu.x) as u32);
let data = sys.read(ptr, AddressType::Program, &cpu.signals);
ByteRef::Low(&mut cpu.temp_data).set(data);
}
5 => {
let ptr = ((cpu.pbr as u32) << 16)
| (cpu.temp_addr.wrapping_add(cpu.x).wrapping_add(1) as u32);
let data = sys.read(ptr, AddressType::Program, &cpu.signals);
ByteRef::High(&mut cpu.temp_data).set(data);
cpu.pc = cpu.temp_data;
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
return;
}
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.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.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),
(RTL, 3) => {
if cpu.flags.emulation {
ByteRef::High(&mut cpu.s).set(0x01);
}
cpu.s = cpu.s.wrapping_add(1);
let data = sys.read(cpu.s as u32, AddressType::Data, &cpu.signals);
ByteRef::Low(&mut cpu.pc).set(data);
}
(RTL, 4) => {
cpu.s = cpu.s.wrapping_add(1);
let data = sys.read(cpu.s as u32, AddressType::Data, &cpu.signals);
ByteRef::High(&mut cpu.pc).set(data);
}
(RTL, 5) => {
cpu.s = cpu.s.wrapping_add(1);
let data = sys.read(cpu.s as u32, AddressType::Data, &cpu.signals);
cpu.pbr = data;
if cpu.flags.emulation {
ByteRef::High(&mut cpu.s).set(0x01);
}
cpu.pc = cpu.pc.wrapping_add(1);
cpu.state = State::Fetch;
}
(_, 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;
}
_ => 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_nf {
($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 $cond8 {
let data = ByteRef::Low(&mut $r0).get();
ByteRef::Low(&mut $r1).set(data);
} else {
$r1 = $r0;
}
}
}
}
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 $cond8 {
let data = ByteRef::Low(&mut $r0).get();
ByteRef::Low(&mut $r1).set(data);
$cpu.flags.negative = data >> 7 != 0;
$cpu.flags.zero = data == 0;
} else {
$r1 = $r0;
$cpu.flags.negative = $r1 >> 15 != 0;
$cpu.flags.zero = $r1 == 0;
}
}
}
}
transfer!(tax, cpu, cpu.m8(), cpu.a, cpu.x);
transfer!(tay, cpu, cpu.m8(), cpu.a, cpu.y);
transfer!(tsx, cpu, cpu.m8(), cpu.s, cpu.x);
transfer!(txa, cpu, cpu.a8(), cpu.x, cpu.a);
transfer_nf!(txs, cpu, cpu.flags.emulation, cpu.x, cpu.s);
transfer!(txy, cpu, cpu.m8(), cpu.x, cpu.y);
transfer!(tya, cpu, cpu.a8(), cpu.y, cpu.a);
transfer!(tyx, cpu, cpu.m8(), 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);
$r1 = $r0;
$cpu.state = State::Fetch;
}
}
}
fn tcd(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.d = cpu.a;
cpu.flags.negative = cpu.d >> 15 != 0;
cpu.flags.zero = cpu.d == 0;
cpu.state = State::Fetch;
}
transfer16!(tcs, cpu, cpu.a, cpu.s);
fn tdc(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.a = cpu.d;
cpu.flags.negative = cpu.a >> 15 != 0;
cpu.flags.zero = cpu.a == 0;
cpu.state = State::Fetch;
}
fn tsc(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
cpu.a = cpu.s;
cpu.flags.negative = cpu.a >> 15 != 0;
cpu.flags.zero = cpu.a == 0;
cpu.state = State::Fetch;
}
macro_rules! inc_index {
($name:ident, $reg:ident, $op:ident) => {
fn $name(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
implied(cpu, sys);
if cpu.m8() {
let v = ByteRef::Low(&mut cpu.$reg).get().$op(1);
ByteRef::Low(&mut cpu.$reg).set(v);
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 7 != 0;
} else {
cpu.$reg = cpu.$reg.$op(1);
cpu.flags.zero = cpu.$reg == 0;
cpu.flags.negative = cpu.$reg >> 15 != 0;
}
}
}
}
inc_index!(dey, y, wrapping_sub);
inc_index!(iny, y, wrapping_add);
inc_index!(dex, x, wrapping_sub);
inc_index!(inx, x, wrapping_add);
fn adc_bcd(a: u16, l: u16, r: u16) -> (u16, u16) {
let h_carry = (a ^ l ^ r) & 0x10 != 0;
let low_invalid = (r & 0x0f) > 9;
let mut r = r;
if h_carry && low_invalid {
r = (r & 0xfff0) | ((r + 6) & 0x0f);
} else if h_carry || low_invalid {
r = r.wrapping_add(6);
}
let r_after_low = r;
if r > 0xff || (r & 0xf0) > 0x90 {
r = r.wrapping_add(0x60);
}
(r, r_after_low)
}
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 binary_r = a.wrapping_add(l).wrapping_add(c);
let (r, v_r) = if cpu.flags.decimal { adc_bcd(a, l, binary_r) } else { (binary_r, binary_r) };
ByteRef::Low(&mut cpu.a).set(r as u8);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = r as u8 == 0;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.overflow = (a ^ v_r) & (l ^ v_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 binary_r = a.wrapping_add(h).wrapping_add(c);
let (r, v_r) = if cpu.flags.decimal { adc_bcd(a, h, binary_r) } else { (binary_r, binary_r) };
ByteRef::High(&mut cpu.a).set(r as u8);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = cpu.flags.zero && r as u8 == 0;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.overflow = (a ^ v_r) & (h ^ v_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);
let binary_carry = r > 0xff;
let r_binary = r;
if cpu.flags.decimal {
if (a ^ l ^ r) & 0x10 == 0 {
r = (r & !0x0Fu16) | (r.wrapping_sub(6) & 0x0F);
}
if !binary_carry {
r = (r & 0x0F) | (r.wrapping_sub(0x60) & 0xF0);
}
}
ByteRef::Low(&mut cpu.a).set(r as u8);
cpu.flags.carry = binary_carry;
cpu.flags.zero = r as u8 == 0;
cpu.flags.negative = r as u8 & 0x80 != 0;
cpu.flags.overflow = (a ^ r_binary) & (l ^ r_binary) & 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);
let binary_carry = r > 0xff;
let r_binary = r;
if cpu.flags.decimal {
if (a ^ h ^ r) & 0x10 == 0 {
r = (r & !0x0Fu16) | (r.wrapping_sub(6) & 0x0F);
}
if !binary_carry {
r = (r & 0x0F) | (r.wrapping_sub(0x60) & 0xF0);
}
}
ByteRef::High(&mut cpu.a).set(r as u8);
cpu.flags.carry = binary_carry;
cpu.flags.zero = cpu.flags.zero && r as u8 == 0;
cpu.flags.negative = r as u8 & 0x80 != 0;
cpu.flags.overflow = (a ^ r_binary) & (h ^ r_binary) & 0x80 != 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
fn cmp(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let m = (!l) as u16;
let a = ByteRef::Low(&mut cpu.a).get() as u16;
let r = a.wrapping_add(m).wrapping_add(1);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = r as u8 == 0;
if cpu.a8() {
cpu.flags.negative = r & 0x80 != 0;
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let m = (!h) as u16;
let c = if cpu.flags.carry { 1 } else { 0 };
let a = ByteRef::High(&mut cpu.a).get() as u16;
let r = a.wrapping_add(m).wrapping_add(c);
cpu.flags.carry = r > 0xff;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.zero = cpu.flags.zero && r as u8 == 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
macro_rules! cmp_index {
($name:ident, $reg:ident) => {
fn $name(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
match am.read(cpu, sys) {
Some(TaggedByte::Data(Byte::Low(l))) => {
let m = (!l) as u16;
let a = ByteRef::Low(&mut cpu.$reg).get() as u16;
let r = a.wrapping_add(m).wrapping_add(1);
cpu.flags.carry = r > 0xff;
cpu.flags.zero = r as u8 == 0;
if cpu.m8() {
cpu.flags.negative = r & 0x80 != 0;
cpu.state = State::Fetch;
}
}
Some(TaggedByte::Data(Byte::High(h))) => {
let m = (!h) as u16;
let c = if cpu.flags.carry { 1 } else { 0 };
let a = ByteRef::High(&mut cpu.$reg).get() as u16;
let r = a.wrapping_add(m).wrapping_add(c);
cpu.flags.carry = r > 0xff;
cpu.flags.negative = r & 0x80 != 0;
cpu.flags.zero = cpu.flags.zero && r as u8 == 0;
cpu.state = State::Fetch;
}
_ => (),
}
}
}
}
cmp_index!(cpx, x);
cmp_index!(cpy, y);
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 old_pc = $cpu.pc;
let ba = old_pc.wrapping_add_signed(($cpu.temp_addr as i8).into());
$cpu.pc = ba;
if !$cpu.flags.emulation || old_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 => {
io($cpu, sys);
}
2 if $b16 => {
let b = ByteRef::High(&mut r).get();
sys.write($cpu.s as u32, b, super::AddressType::Data, &$cpu.signals);
if !$cpu.aborted {
$cpu.s = $cpu.s.wrapping_sub(1);
}
}
2 | 3 => {
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 | 2 => {
io($cpu, sys);
}
3 => {
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 | 2 => {
io($cpu, sys);
}
3 => {
let b = $cpu.stack_pop(sys);
ByteRef::Low(&mut $r).set(b);
if !($b16) {
$cpu.flags.negative = b >> 7 != 0;
$cpu.flags.zero = b == 0;
$cpu.state = State::Fetch;
}
}
4 => {
let b = $cpu.stack_pop(sys);
ByteRef::High(&mut $r).set(b);
$cpu.flags.negative = $r >> 15 != 0;
$cpu.flags.zero = $r == 0;
$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());
fn plb(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 | 2 => {
io(cpu, sys);
}
3 => {
let b = cpu.stack_pop_raw(sys);
cpu.dbr = b;
cpu.flags.negative = b >> 7 != 0;
cpu.flags.zero = b == 0;
cpu.enforce_emulation_stack();
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn pld(cpu: &mut CPU, sys: &mut dyn System, _am: AddressingMode) {
match cpu.tcu {
1 | 2 => {
io(cpu, sys);
}
3 => {
let b = cpu.stack_pop_raw(sys);
ByteRef::Low(&mut cpu.d).set(b);
}
4 => {
let b = cpu.stack_pop_raw(sys);
ByteRef::High(&mut cpu.d).set(b);
cpu.flags.negative = cpu.d >> 15 != 0;
cpu.flags.zero = cpu.d == 0;
cpu.enforce_emulation_stack();
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
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 lsr(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let b16 = cpu.a16();
let res = am.rwb(cpu, sys, |cpu, v, b16| if b16 {
cpu.flags.carry = v & 1 != 0;
let v = v >> 1;
cpu.flags.zero = v == 0;
cpu.flags.negative = false;
v
} else {
let v = v as u8;
cpu.flags.carry = v & 1 != 0;
let v = v >> 1;
cpu.flags.zero = v == 0;
cpu.flags.negative = false;
v as u16
}, b16);
if let Some(TaggedByte::Data(Byte::Low(_))) = res {
cpu.state = State::Fetch;
}
}
fn tsb(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let b16 = cpu.a16();
let res = am.rwb(cpu, sys, |cpu, v, b16| if b16 {
cpu.flags.zero = (cpu.a & v) == 0;
v | cpu.a
} else {
let a = ByteRef::Low(&mut cpu.a).get();
let v = v as u8;
cpu.flags.zero = (a & v) == 0;
(v | a) as u16
}, b16);
if let Some(TaggedByte::Data(Byte::Low(_))) = res {
cpu.state = State::Fetch;
}
}
fn trb(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let b16 = cpu.a16();
let res = am.rwb(cpu, sys, |cpu, v, b16| if b16 {
cpu.flags.zero = (cpu.a & v) == 0;
v & !cpu.a
} else {
let a = ByteRef::Low(&mut cpu.a).get();
let v = v as u8;
cpu.flags.zero = (a & v) == 0;
(v & !a) as u16
}, b16);
if let Some(TaggedByte::Data(Byte::Low(_))) = res {
cpu.state = State::Fetch;
}
}
fn rol(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 {
let old_carry = cpu.flags.carry as u16;
cpu.flags.carry = v >> 15 != 0;
v = (v << 1) | old_carry;
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 15 != 0;
v
} else {
let mut v = v as u8;
let old_carry = cpu.flags.carry as u8;
cpu.flags.carry = v >> 7 != 0;
v = (v << 1) | old_carry;
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 ror(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 {
let old_carry = (cpu.flags.carry as u16) << 15;
cpu.flags.carry = v & 1 != 0;
v = (v >> 1) | old_carry;
cpu.flags.zero = v == 0;
cpu.flags.negative = v >> 15 != 0;
v
} else {
let mut v = v as u8;
let old_carry = (cpu.flags.carry as u8) << 7;
cpu.flags.carry = v & 1 != 0;
v = (v >> 1) | old_carry;
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_raw(sys, (cpu.temp_data >> 8) as u8, false);
cpu.temp_bank += 1;
}
2 => {
cpu.stack_push_raw(sys, cpu.temp_data as u8, false);
cpu.enforce_emulation_stack();
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);
cpu.pc = cpu.pc.wrapping_add(1);
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.pc.wrapping_add_signed(cpu.temp_addr as i16);
}
4 => {
let data = ByteRef::High(&mut cpu.temp_addr).get();
cpu.stack_push_raw(sys, data, false);
}
5 => {
let data = ByteRef::Low(&mut cpu.temp_addr).get();
cpu.stack_push_raw(sys, data, false);
cpu.enforce_emulation_stack();
cpu.state = State::Fetch;
}
_ => unreachable!(),
}
}
fn bit(cpu: &mut CPU, sys: &mut dyn System, am: AddressingMode) {
let immediate = matches!(am, AddressingMode::Immediate);
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() {
if !immediate {
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;
if !immediate {
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), (tsb, AddressingMode::Direct), (ora, AddressingMode::Direct), (asl, AddressingMode::Direct), (ora, AddressingMode::DirectIndirectLong), (php, AddressingMode::Implied), (ora, AddressingMode::Immediate), (asl, AddressingMode::Accumulator), (phd, AddressingMode::Implied), (tsb, AddressingMode::Absolute), (ora, AddressingMode::Absolute), (asl, AddressingMode::Absolute), (ora, AddressingMode::AbsoluteLong), (bpl, AddressingMode::Immediate), (ora, AddressingMode::DirectIndirectIndexedY), (ora, AddressingMode::DirectIndirect), (ora, AddressingMode::StackRelIndirectIndexedY), (trb, AddressingMode::Direct), (ora, AddressingMode::DirectIndexedX), (asl, AddressingMode::DirectIndexedX), (ora, AddressingMode::DirectIndirectLongIndexedY), (clc, AddressingMode::Implied), (ora, AddressingMode::AbsoluteIndexedY), (inc, AddressingMode::Accumulator), (tcs, AddressingMode::Implied), (trb, AddressingMode::Absolute), (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), (rol, AddressingMode::Direct), (and, AddressingMode::DirectIndirectLong), (plp, AddressingMode::Implied), (and, AddressingMode::Immediate), (rol, AddressingMode::Accumulator), (pld, AddressingMode::Implied), (bit, AddressingMode::Absolute), (and, AddressingMode::Absolute), (rol, AddressingMode::Absolute), (and, AddressingMode::AbsoluteLong), (bmi, AddressingMode::Immediate), (and, AddressingMode::DirectIndirectIndexedY), (and, AddressingMode::DirectIndirect), (and, AddressingMode::StackRelIndirectIndexedY), (bit, AddressingMode::DirectIndexedX), (and, AddressingMode::DirectIndexedX), (rol, AddressingMode::DirectIndexedX), (and, AddressingMode::DirectIndirectLongIndexedY), (sec, AddressingMode::Implied), (and, AddressingMode::AbsoluteIndexedY), (dec, AddressingMode::Accumulator), (tsc, AddressingMode::Implied), (bit, AddressingMode::AbsoluteIndexedX), (and, AddressingMode::AbsoluteIndexedX), (rol, AddressingMode::AbsoluteIndexedX), (and, AddressingMode::AbsoluteLongIndexedX), (rti, AddressingMode::Implied), (eor, AddressingMode::DirectIndirectX), (wdm, AddressingMode::Implied), (eor, AddressingMode::StackRel), (mvp, AddressingMode::Implied), (eor, AddressingMode::Direct), (lsr, AddressingMode::Direct), (eor, AddressingMode::DirectIndirectLong), (pha, AddressingMode::Implied), (eor, AddressingMode::Immediate), (lsr, AddressingMode::Accumulator), (phk, AddressingMode::Implied), (jmp, AddressingMode::Immediate), (eor, AddressingMode::Absolute), (lsr, AddressingMode::Absolute), (eor, AddressingMode::AbsoluteLong), (bvc, AddressingMode::Immediate), (eor, AddressingMode::DirectIndirectIndexedY), (eor, AddressingMode::DirectIndirect), (eor, AddressingMode::StackRelIndirectIndexedY), (mvn, AddressingMode::Implied), (eor, AddressingMode::DirectIndexedX), (lsr, AddressingMode::DirectIndexedX), (eor, AddressingMode::DirectIndirectLongIndexedY), (cli, AddressingMode::Implied), (eor, AddressingMode::AbsoluteIndexedY), (phy, AddressingMode::Implied), (tcd, AddressingMode::Implied), (jml, AddressingMode::AbsoluteLong), (eor, AddressingMode::AbsoluteIndexedX), (lsr, AddressingMode::AbsoluteIndexedX), (eor, AddressingMode::AbsoluteLongIndexedX), (rts, AddressingMode::Absolute), (adc, AddressingMode::DirectIndirectX), (per, AddressingMode::Implied), (adc, AddressingMode::StackRel), (stz, AddressingMode::Direct), (adc, AddressingMode::Direct), (ror, AddressingMode::Direct), (adc, AddressingMode::DirectIndirectLong), (pla, AddressingMode::Implied), (adc, AddressingMode::Immediate), (ror, AddressingMode::Accumulator), (rts, AddressingMode::AbsoluteLong), (jmp, AddressingMode::Indirect), (adc, AddressingMode::Absolute), (ror, AddressingMode::Absolute), (adc, AddressingMode::AbsoluteLong), (bvs, AddressingMode::Immediate), (adc, AddressingMode::DirectIndirectIndexedY), (adc, AddressingMode::DirectIndirect), (adc, AddressingMode::StackRelIndirectIndexedY), (stz, AddressingMode::DirectIndexedX), (adc, AddressingMode::DirectIndexedX), (ror, AddressingMode::DirectIndexedX), (adc, AddressingMode::DirectIndirectLongIndexedY), (sei, AddressingMode::Implied), (adc, AddressingMode::AbsoluteIndexedY), (ply, AddressingMode::Implied), (tdc, AddressingMode::Implied), (jmp, AddressingMode::IndexedIndirectX), (adc, AddressingMode::AbsoluteIndexedX), (ror, AddressingMode::AbsoluteIndexedX), (adc, AddressingMode::AbsoluteLongIndexedX), (bra, AddressingMode::Immediate), (sta, AddressingMode::DirectIndirectX), (brl, AddressingMode::Implied), (sta, AddressingMode::StackRel), (sty, AddressingMode::Direct), (sta, AddressingMode::Direct), (stx, AddressingMode::Direct), (sta, AddressingMode::DirectIndirectLong), (dey, 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), (cpy, AddressingMode::Immediate), (cmp, AddressingMode::DirectIndirectX), (rep, AddressingMode::Immediate), (cmp, AddressingMode::StackRel), (cpy, AddressingMode::Direct), (cmp, AddressingMode::Direct), (dec, AddressingMode::Direct), (cmp, AddressingMode::DirectIndirectLong), (iny, AddressingMode::Implied), (cmp, AddressingMode::Immediate), (dex, AddressingMode::Implied), (wai, AddressingMode::Implied), (cpy, AddressingMode::Absolute), (cmp, AddressingMode::Absolute), (dec, AddressingMode::Absolute), (cmp, AddressingMode::AbsoluteLong), (bne, AddressingMode::Immediate), (cmp, AddressingMode::DirectIndirectIndexedY), (cmp, AddressingMode::DirectIndirect), (cmp, AddressingMode::StackRelIndirectIndexedY), (pei, AddressingMode::Direct), (cmp, AddressingMode::DirectIndexedX), (dec, AddressingMode::DirectIndexedX), (cmp, AddressingMode::DirectIndirectLongIndexedY), (cld, AddressingMode::Implied), (cmp, AddressingMode::AbsoluteIndexedY), (phx, AddressingMode::Implied), (stp, AddressingMode::Implied), (jmp, AddressingMode::IndirectLong), (cmp, AddressingMode::AbsoluteIndexedX), (dec, AddressingMode::AbsoluteIndexedX), (cmp, AddressingMode::AbsoluteLongIndexedX), (cpx, AddressingMode::Immediate), (sbc, AddressingMode::DirectIndirectX), (sep, AddressingMode::Immediate), (sbc, AddressingMode::StackRel), (cpx, AddressingMode::Direct), (sbc, AddressingMode::Direct), (inc, AddressingMode::Direct), (sbc, AddressingMode::DirectIndirectLong), (inx, AddressingMode::Implied), (sbc, AddressingMode::Immediate), (nop, AddressingMode::Implied), (xba, AddressingMode::Implied), (cpx, AddressingMode::Absolute), (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), ];