feat: add disassembler that is aware of immediate values

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-12-16 12:16:58 -04:00
parent 3fb7edd333
commit cf2357f917
1 changed files with 127 additions and 12 deletions

View File

@ -2155,8 +2155,11 @@ mod table {
}
pub(crate) mod dbg {
use std::borrow::Cow;
use super::add::{Source as AddSource, Target as AddTarget};
use super::jump::JpCond;
use super::alu::Source as AluSource;
use super::jump::{JpCond, JpLoc};
use super::load::{Source as LDSource, Target as LDTarget};
use super::{AllRegisters, BusIo, Cpu, Instruction, RegisterPair};
@ -2190,28 +2193,140 @@ pub(crate) mod dbg {
sm83_asm
}
fn disasm(cpu: &Cpu, pc: u16, instr: Instruction) -> String {
pub(crate) fn new_disasm(cpu: &Cpu, limit: u8) -> String {
let mut assembly = String::new();
let mut pc = cpu.register_pair(RegisterPair::PC);
for _ in 0..limit {
let opcode = cpu.read_byte(pc);
pc += 1;
let maybe_instr = if opcode == 0xCB {
let opcode = cpu.read_byte(pc);
pc += 1;
Instruction::prefixed(opcode)
} else {
Instruction::unprefixed(opcode)
};
match maybe_instr {
Instruction::Invalid => {}
instr => {
let output = format!("${:04X} {}\n", pc - 1, disasm(cpu, pc, instr));
assembly.push_str(&output);
pc += delta::pc_inc_count(instr);
}
}
}
assembly
}
// TODO: It might be better if I pass in a mutable writer instead of usnig a String
fn disasm(cpu: &Cpu, pc: u16, instr: Instruction) -> Cow<str> {
use Instruction::*;
let imm_byte = cpu.read_byte(pc + 1);
let imm_word = (cpu.read_byte(pc + 2) as u16) << 8 | imm_byte as u16;
match instr {
NOP => "NOP".to_string(),
// Unprefixed Instructions
NOP => "NOP".into(),
LD(LDTarget::IndirectImmediateWord, LDSource::SP) => {
format!("LD ({:#06X}), SP", imm_word)
format!("LD ({:#06X}), SP", imm_word).into()
}
STOP => "STOP".to_string(),
JR(JpCond::Always) => format!("JR {}", imm_byte as i8),
JR(cond) => format!("JR {:?} {}", cond, imm_byte as i8),
STOP => "STOP".into(),
JR(JpCond::Always) => format!("JR {}", imm_byte as i8).into(),
JR(cond) => format!("JR {:?} {}", cond, imm_byte as i8).into(),
LD(LDTarget::Group1(rp), LDSource::ImmediateWord) => {
format!("LD {:?} {:#06X}", rp, imm_word)
format!("LD {:?} {:#06X}", rp, imm_word).into()
}
ADD(AddTarget::HL, AddSource::Group1(rp)) => format!("ADD HL, {:?}", rp),
LD(LDTarget::IndirectGroup2(rp), LDSource::A) => format!("LD ({:?}), A", rp),
LD(LDTarget::A, LDSource::IndirectGroup2(rp)) => format!("LD A, ({:?})", rp),
ADD(AddTarget::HL, AddSource::Group1(rp)) => format!("ADD HL, {:?}", rp).into(),
LD(LDTarget::IndirectGroup2(rp), LDSource::A) => format!("LD ({:?}), A", rp).into(),
LD(LDTarget::A, LDSource::IndirectGroup2(rp)) => format!("LD A, ({:?})", rp).into(),
INC(AllRegisters::Group1(rp)) => format!("INC {:?}", rp).into(),
DEC(AllRegisters::Group1(rp)) => format!("DEC {:?}", rp).into(),
INC(AllRegisters::Register(reg)) => format!("INC {:?}", reg).into(),
DEC(AllRegisters::Register(reg)) => format!("DEC {:?}", reg).into(),
LD(LDTarget::Register(reg), LDSource::ImmediateByte) => {
format!("LD {:?}, {:#04X}", reg, imm_byte).into()
}
RLCA => "RLCA".into(),
RRCA => "RRCA".into(),
RLA => "RLA".into(),
RRA => "RRA".into(),
DAA => "DAA".into(),
CPL => "CPL".into(),
SCF => "SCF".into(),
CCF => "CCF".into(),
HALT => "HALT".into(),
LD(LDTarget::Register(left), LDSource::Register(right)) => {
format!("LD {:?}, {:?}", left, right).into()
}
ADD(AddTarget::A, AddSource::Register(reg)) => format!("ADD A, {:?}", reg).into(),
ADC(AluSource::Register(reg)) => format!("ADC {:?}", reg).into(),
SUB(AluSource::Register(reg)) => format!("SUB {:?}", reg).into(),
SBC(AluSource::Register(reg)) => format!("SBC {:?}", reg).into(),
AND(AluSource::Register(reg)) => format!("AND {:?}", reg).into(),
XOR(AluSource::Register(reg)) => format!("XOR {:?}", reg).into(),
OR(AluSource::Register(reg)) => format!("OR {:?}", reg).into(),
CP(AluSource::Register(reg)) => format!("CP {:?}", reg).into(),
RET(JpCond::Always) => "RET".into(),
RET(cond) => format!("RET {:?}", cond).into(),
LD(LDTarget::IoWithImmediateOffset, LDSource::A) => {
format!("LD ({:#06X}) , A", 0xFF00 + imm_byte as u16).into()
}
ADD(AddTarget::SP, AddSource::ImmediateSignedByte) => {
format!("ADD SP, {}", imm_byte as i8).into()
}
LD(LDTarget::A, LDSource::IoWithImmediateOffset) => {
format!("LD A, ({:#06X})", 0xFF00 + imm_byte as u16).into()
}
LDHL => format!("LD HL, SP + {}", imm_byte as i8).into(),
POP(rp) => format!("POP {:?}", rp).into(),
RETI => "RETI".into(),
JP(JpCond::Always, JpLoc::HL) => "JP HL".into(),
LD(LDTarget::SP, LDSource::HL) => "LD SP, HL".into(),
JP(JpCond::Always, JpLoc::ImmediateWord) => format!("JP {:#06X}", imm_word).into(),
JP(cond, JpLoc::ImmediateWord) => format!("JP {:?} {:#06X}", cond, imm_word).into(),
LD(LDTarget::IoWithC, LDSource::A) => "LD (0xFF00 + C), A".into(),
LD(LDTarget::IndirectImmediateWord, LDSource::A) => {
format!("LD ({:#06X}), A", imm_byte).into()
}
LD(LDTarget::A, LDSource::IoWithC) => "LD A, (0xFF00 + C)".into(),
LD(LDTarget::A, LDSource::IndirectImmediateWord) => {
format!("LD A, ({:#06X})", imm_word).into()
}
DI => "DI".into(),
EI => "EI".into(),
CALL(cond) => format!("CALL {:?} {:#06X}", cond, imm_word).into(),
PUSH(rp) => format!("PUSH {:?}", rp).into(),
ADD(AddTarget::A, AddSource::ImmediateByte) => {
format!("ADD A, {:#04X}", imm_byte).into()
}
ADC(AluSource::ImmediateByte) => format!("ADC {:#04X}", imm_byte).into(),
SUB(AluSource::ImmediateByte) => format!("SUB {:#04X}", imm_byte).into(),
SBC(AluSource::ImmediateByte) => format!("SBC {:#04X}", imm_byte).into(),
AND(AluSource::ImmediateByte) => format!("AND {:#04X}", imm_byte).into(),
XOR(AluSource::ImmediateByte) => format!("XOR {:#04X}", imm_byte).into(),
OR(AluSource::ImmediateByte) => format!("OR {:#04X}", imm_byte).into(),
CP(AluSource::ImmediateByte) => format!("CP {:#04X}", imm_byte).into(),
RST(v) => format!("RST {:#04X}", v).into(),
_ => todo!(),
// Prefixed Instructions
RLC(reg) => format!("RLC {:?}", reg).into(),
RRC(reg) => format!("RRC {:?}", reg).into(),
RL(reg) => format!("RL {:?}", reg).into(),
RR(reg) => format!("RR {:?}", reg).into(),
SLA(reg) => format!("SLA {:?}", reg).into(),
SRA(reg) => format!("SRA {:?}", reg).into(),
SWAP(reg) => format!("SWAP {:?}", reg).into(),
SRL(reg) => format!("SRL {:?}", reg).into(),
BIT(bit, reg) => format!("BIT {}, {:?}", bit, reg).into(),
RES(bit, reg) => format!("RES {}, {:?}", bit, reg).into(),
SET(bit, reg) => format!("SET {}, {:?}", bit, reg).into(),
_ => unreachable!("{:?} is an illegal instruction", instr),
}
}