-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add disassembler and listing generator
- Loading branch information
Showing
6 changed files
with
307 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use tracing_chrome::ChromeLayerBuilder; | ||
use tracing_subscriber::prelude::*; | ||
|
||
use mos6502::disassembler::{listing::Listing, Disassembler}; | ||
|
||
#[tracing::instrument] | ||
fn main() { | ||
let (chrome_layer, _guard) = ChromeLayerBuilder::new().build(); | ||
tracing_subscriber::registry().with(chrome_layer).init(); | ||
|
||
let input = std::env::args().nth(1).expect("Filename missing"); | ||
let bytes = std::fs::read(input).expect("Unable to read file"); | ||
|
||
let mut disassembler = Disassembler::default(bytes); | ||
let ast = disassembler.disassemble_code(); | ||
|
||
let mut listing = Listing::default(ast); | ||
println!("{}", listing.generate()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
use crate::ast::{ASTAddressingMode, ASTInstruction, ASTInstructionNode, ASTNode, ASTOperand}; | ||
|
||
pub mod listing; | ||
|
||
#[derive(Debug)] | ||
pub struct Disassembler { | ||
input: Vec<u8>, | ||
starting_address: usize, | ||
curr_ix: usize, | ||
} | ||
|
||
impl Disassembler { | ||
#[tracing::instrument] | ||
pub fn new(input: Vec<u8>, starting_address: usize) -> Self { | ||
Self { | ||
input, | ||
starting_address, | ||
curr_ix: 0, | ||
} | ||
} | ||
|
||
#[tracing::instrument] | ||
pub fn default(input: Vec<u8>) -> Self { | ||
Self::new(input, 0x0600) | ||
} | ||
|
||
#[tracing::instrument] | ||
fn decode_operand(&mut self, addressing_mode: ASTAddressingMode) -> ASTOperand { | ||
let ix = self.curr_ix; | ||
|
||
macro_rules! byte { | ||
() => {{ | ||
self.input[ix] | ||
}}; | ||
} | ||
macro_rules! word { | ||
() => {{ | ||
((self.input[ix + 1] as u16) << 8) | self.input[ix] as u16 | ||
}}; | ||
} | ||
|
||
match addressing_mode { | ||
ASTAddressingMode::Absolute => ASTOperand::Absolute(word!()), | ||
ASTAddressingMode::ZeroPage => ASTOperand::ZeroPage(byte!()), | ||
ASTAddressingMode::ZeroPageX => ASTOperand::ZeroPage(byte!()), | ||
ASTAddressingMode::ZeroPageY => ASTOperand::ZeroPage(byte!()), | ||
ASTAddressingMode::AbsoluteX => ASTOperand::Absolute(word!()), | ||
ASTAddressingMode::AbsoluteY => ASTOperand::Absolute(word!()), | ||
ASTAddressingMode::Relative => ASTOperand::Relative(byte!() as i8), | ||
ASTAddressingMode::Indirect => ASTOperand::Absolute(word!()), | ||
ASTAddressingMode::IndirectIndexedX => ASTOperand::ZeroPage(byte!()), | ||
ASTAddressingMode::IndirectIndexedY => ASTOperand::ZeroPage(byte!()), | ||
ASTAddressingMode::Immediate => ASTOperand::Immediate(byte!()), | ||
ASTAddressingMode::Accumulator => ASTOperand::Implied, | ||
ASTAddressingMode::Implied => ASTOperand::Implied, | ||
_ => panic!("Invalid addressing mode: '{:#?}'", addressing_mode), | ||
} | ||
} | ||
|
||
#[tracing::instrument] | ||
fn decode_opcode(&mut self, opcode: u8) -> ASTInstruction { | ||
crate::assembler::compiler::opcode::OPCODE_MAPPING | ||
.find_instruction(opcode) | ||
.unwrap_or_else(|| panic!("Invalid opcode: '{:#04x}'", opcode)) | ||
} | ||
|
||
#[tracing::instrument] | ||
fn decode_instruction(&mut self) -> ASTNode { | ||
let ins = self.decode_opcode(self.input[self.curr_ix]); | ||
self.curr_ix += 1; // Step over opcode | ||
|
||
let operand = self.decode_operand(ins.addr_mode); | ||
let instruction = ASTInstructionNode { ins, operand }; | ||
self.curr_ix += instruction.size() - 1; // Step over operand | ||
|
||
ASTNode::Instruction(instruction) | ||
} | ||
|
||
#[tracing::instrument] | ||
pub fn disassemble_code(&mut self) -> Vec<ASTNode> { | ||
let mut ast = vec![]; | ||
|
||
while self.curr_ix < self.input.len() { | ||
ast.push(self.decode_instruction()); | ||
} | ||
|
||
ast | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::ast::ASTMnemonic; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_dissasemble_code() { | ||
let tests = vec![ | ||
( | ||
vec![0xA9, 0x01], | ||
vec![ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Immediate, | ||
ASTOperand::Immediate(0x01), | ||
)], | ||
), | ||
( | ||
vec![0xAD, 0x00, 0x02], | ||
vec![ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Absolute, | ||
ASTOperand::Absolute(0x0200), | ||
)], | ||
), | ||
( | ||
vec![0xBD, 0x00, 0x02], | ||
vec![ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::AbsoluteX, | ||
ASTOperand::Absolute(0x0200), | ||
)], | ||
), | ||
( | ||
vec![0xA9, 0x01, 0xAD, 0x00, 0x02, 0xBD, 0x00, 0x02], | ||
vec![ | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Immediate, | ||
ASTOperand::Immediate(0x01), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Absolute, | ||
ASTOperand::Absolute(0x0200), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::AbsoluteX, | ||
ASTOperand::Absolute(0x0200), | ||
), | ||
], | ||
), | ||
( | ||
vec![ | ||
/* LDX */ 0xA2, 0x08, /* LDA */ 0xA9, 0x01, /* JMP */ 0x4C, | ||
0x0C, 0x00, /* STA */ 0x8D, 0x00, 0x02, /* BNE */ 0xD0, 0xF6, | ||
/* BRK */ 0x00, | ||
], | ||
vec![ | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDX, | ||
ASTAddressingMode::Immediate, | ||
ASTOperand::Immediate(0x08), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Immediate, | ||
ASTOperand::Immediate(0x01), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::JMP, | ||
ASTAddressingMode::Absolute, | ||
ASTOperand::Absolute(0x000C), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::STA, | ||
ASTAddressingMode::Absolute, | ||
ASTOperand::Absolute(0x0200), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::BNE, | ||
ASTAddressingMode::Relative, | ||
ASTOperand::Relative(-10), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::BRK, | ||
ASTAddressingMode::Implied, | ||
ASTOperand::Implied, | ||
), | ||
], | ||
), | ||
]; | ||
|
||
for (input, expected) in tests { | ||
let mut disassembler = Disassembler::default(input); | ||
let actual = disassembler.disassemble_code(); | ||
assert_eq!(actual, expected); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use crate::ast::{ASTInstructionNode, ASTNode}; | ||
|
||
#[derive(Debug)] | ||
pub struct Listing { | ||
ast: Vec<ASTNode>, | ||
start_address: usize, | ||
current_address: usize, | ||
str: String, | ||
} | ||
|
||
impl Listing { | ||
#[tracing::instrument] | ||
pub fn new(ast: Vec<ASTNode>, start_address: usize) -> Self { | ||
Self { | ||
ast, | ||
start_address, | ||
current_address: start_address, | ||
str: String::new(), | ||
} | ||
} | ||
|
||
#[tracing::instrument] | ||
pub fn default(ast: Vec<ASTNode>) -> Self { | ||
Self::new(ast, 0x0600) | ||
} | ||
|
||
#[tracing::instrument] | ||
fn generate_line(&self, node: &ASTInstructionNode) -> String { | ||
format!("{:04x}: {}\n", self.current_address, node) | ||
} | ||
|
||
#[tracing::instrument] | ||
pub fn generate(&mut self) -> String { | ||
self.str.push_str("Addr Ins\n"); | ||
self.str.push_str("---------------\n"); | ||
// 0600: JSR $0606 | ||
|
||
for node in &self.ast { | ||
// TODO: Add support for other AST nodes | ||
if let ASTNode::Instruction(ins_node) = node { | ||
self.str.push_str(self.generate_line(ins_node).as_str()); | ||
self.current_address += ins_node.size(); | ||
} | ||
} | ||
|
||
self.str.clone() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::ast::ASTOperand; | ||
|
||
#[test] | ||
fn test_listing() { | ||
use crate::ast::{ASTAddressingMode, ASTMnemonic, ASTNode}; | ||
use crate::disassembler::listing::Listing; | ||
let ast = vec![ | ||
ASTNode::new_instruction( | ||
ASTMnemonic::JSR, | ||
ASTAddressingMode::Absolute, | ||
ASTOperand::Absolute(0x0606), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Immediate, | ||
ASTOperand::Immediate(0x01), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::Absolute, | ||
ASTOperand::Absolute(0x0200), | ||
), | ||
ASTNode::new_instruction( | ||
ASTMnemonic::LDA, | ||
ASTAddressingMode::AbsoluteX, | ||
ASTOperand::Absolute(0x0200), | ||
), | ||
]; | ||
let mut listing = Listing::default(ast); | ||
let expected = "Addr Ins | ||
--------------- | ||
0600: JSR $0606 | ||
0603: LDA #$01 | ||
0605: LDA $0200 | ||
0608: LDA $0200,X | ||
"; | ||
|
||
assert_eq!(listing.generate(), expected); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters