use crate::IterationResult;
use crate::StepError;
#[derive(Copy, Clone, Default, PartialOrd, PartialEq)]
pub struct Insn<V: mos6502::Variant>([u8; 3], std::marker::PhantomData<V>);
impl<V: mos6502::Variant> crate::Step for Insn<V> {
fn first() -> Self {
Self([0, 0, 0], std::marker::PhantomData::<V>)
}
fn next(&mut self) -> IterationResult {
use crate::Encode;
self.incr_at_offset(self.len() - 1);
self.fixup()
}
}
impl<V: mos6502::Variant> crate::subroutine::ShouldReturn for Insn<V> {
fn should_return(&self, offset: usize) -> Result<(), crate::StaticAnalysis<Self>> {
if self.0 == Self::rts().0 {
return Ok(());
}
Err(crate::StaticAnalysis::<Self> {
offset,
advance: Self::skip_to_next_opcode,
reason: "ShouldReturn",
})
}
}
impl<V: mos6502::Variant> Insn<V> {
pub fn skip_to_next_opcode(&mut self) -> IterationResult {
self.0 = [self.0[0] + 1, 0, 0];
self.fixup()
}
}
impl<V: mos6502::Variant> crate::Branch for Insn<V> {}
impl<V: mos6502::Variant> crate::Encode<u8> for Insn<V> {
fn encode(&self) -> std::vec::Vec<u8> {
let mut encoding = self.0.to_vec();
encoding.truncate(self.len());
encoding
}
fn len(&self) -> usize {
use mos6502::instruction::AddressingMode;
match self.decode().1 {
AddressingMode::Accumulator => 1,
AddressingMode::Implied => 1,
AddressingMode::Immediate => 2,
AddressingMode::ZeroPage => 2,
AddressingMode::ZeroPageX => 2,
AddressingMode::ZeroPageY => 2,
AddressingMode::ZeroPageIndirect => 2,
AddressingMode::Relative => 2,
AddressingMode::IndexedIndirectX => 2,
AddressingMode::IndirectIndexedY => 2,
AddressingMode::Absolute => 3,
AddressingMode::AbsoluteX => 3,
AddressingMode::AbsoluteY => 3,
AddressingMode::Indirect => 3,
AddressingMode::BuggyIndirect => 3,
}
}
}
impl<V: mos6502::Variant> Insn<V> {
pub fn rts() -> Self {
Self::new(&[0x60])
}
pub fn new(mc: &[u8]) -> Self {
let mut enc = [0, 0, 0];
enc[..mc.len().min(3)].copy_from_slice(mc);
Self(enc, std::marker::PhantomData::<V>)
}
fn incr_at_offset(&mut self, offset: usize) -> bool {
if let Some(nb) = self.0[offset].checked_add(1) {
self.0[offset] = nb;
true
} else {
self.0[offset] = 0;
if offset == 0 {
false
} else {
self.incr_at_offset(offset - 1)
}
}
}
pub fn decode(
&self,
) -> (
mos6502::instruction::Instruction,
mos6502::instruction::AddressingMode,
) {
use mos6502::Variant;
mos6502::instruction::Cmos6502::decode(self.0[0])
.unwrap_or_else(|| panic!("can't decode opcode {:02x}", self.0[0]))
}
pub fn address(&self) -> Option<u16> {
use mos6502::instruction::AddressingMode;
match self.decode().1 {
AddressingMode::Accumulator
| AddressingMode::Implied
| AddressingMode::Immediate
| AddressingMode::Relative => None,
AddressingMode::ZeroPage
| AddressingMode::ZeroPageX
| AddressingMode::ZeroPageY
| AddressingMode::ZeroPageIndirect
| AddressingMode::IndexedIndirectX
| AddressingMode::IndirectIndexedY => Some(u16::from_le_bytes([self.0[1], 0])),
AddressingMode::Absolute
| AddressingMode::AbsoluteX
| AddressingMode::AbsoluteY
| AddressingMode::Indirect
| AddressingMode::BuggyIndirect => Some(u16::from_le_bytes([self.0[1], self.0[2]])),
}
}
pub fn writes_to(&self) -> Option<u16> {
use mos6502::instruction::Instruction::*;
match self.decode().0 {
ADC | ADCnd | AND | BIT | EOR | CMP | CPX | CPY | ORA | SBC | SBCnd => None,
BVC | BVS | BNE | BPL | BRA | BCC | BCS | BEQ | BMI => None,
BRK | BRKcld | JMP | JSR | RTS | RTI => None,
CLC | CLD | CLI | CLV | SEC | SED | SEI => None,
INC | DEC | ROR | ROL | ASL | LSR | TSB | TRB => self.address(),
DEX | DEY | INX | INY => None,
LDA | LDY | LDX => None,
NOP => None,
PHA | PHX | PHY | PHP | PLA | PLX | PLY | PLP => None,
STA | STX | STY | STZ => self.address(),
TAX | TAY | TXA | TYA | TSX | TXS => None,
}
}
pub fn reads_from(&self) -> Option<u16> {
use mos6502::instruction::Instruction::*;
match self.decode().0 {
ADC | ADCnd | AND | BIT | EOR | CMP | CPX | CPY | ORA | SBC | SBCnd => self.address(),
BVC | BVS | BNE | BPL | BRA | BCC | BCS | BEQ | BMI => None,
BRK | BRKcld | JMP | JSR | RTS | RTI => None,
CLC | CLD | CLI | CLV | SEC | SED | SEI => None,
INC | DEC | ROR | ROL | ASL | LSR | TSB | TRB => self.address(),
DEX | DEY | INX | INY => None,
LDA | LDY | LDX => self.address(),
NOP => None,
PHA | PHX | PHY | PHP | PLA | PLX | PLY | PLP => None,
STA | STX | STY | STZ => None,
TAX | TAY | TXA | TYA | TSX | TXS => None,
}
}
pub fn fixup(&mut self) -> IterationResult {
while V::decode(self.0[0]).is_none() {
if let Some(new_opcode) = self.0[0].checked_add(1) {
self.0[0] = new_opcode;
} else {
return Err(StepError::End);
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
fn check_insn<V>(insn: super::Insn<V>)
where
V: Clone + mos6502::Variant,
{
use crate::Encode;
insn.decode();
let mut copy = insn.clone();
copy.fixup().unwrap();
assert_eq!(copy.encode(), insn.encode());
}
#[test]
fn first_few() {
use super::Insn;
use crate::Step;
use mos6502::instruction::Cmos6502;
let mut i = Insn::<Cmos6502>::first();
println!("{} ; {:?}", i, i);
assert!(i.next().is_ok());
println!("{} ; {:?}", i, i);
assert!(i.next().is_ok());
println!("{} ; {:?}", i, i);
assert!(i.next().is_ok());
println!("{} ; {:?}", i, i);
assert!(i.next().is_ok());
}
fn all_instructions<V>()
where
V: Clone + mos6502::Variant,
{
use super::Insn;
use crate::Step;
let mut i = Insn::<V>::first();
let mut count = 0usize;
while i.next().is_ok() {
check_insn(i.clone());
count += 1;
}
println!("Iterated over {count} instructions");
}
#[test]
fn all_instructions_cmos() {
all_instructions::<mos6502::instruction::Cmos6502>();
}
#[test]
fn all_instructions_reva() {
all_instructions::<mos6502::instruction::RevisionA>();
}
#[test]
fn all_instructions_2a03() {
all_instructions::<mos6502::instruction::Ricoh2a03>();
}
#[test]
fn all_instructions_nmos() {
all_instructions::<mos6502::instruction::Nmos6502>();
}
}