Skip to content

Commit

Permalink
Add disassembler and listing generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Granddave committed Dec 20, 2023
1 parent 5740f9b commit 41f1762
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/assembler/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use self::symbol_resolver::{SymbolTable, SymbolType};
use crate::ast::{ASTAddressingMode, ASTInstructionNode, ASTNode, ASTOperand, AST};

/// Mapping from ASTInstruction to opcode.
mod opcode;
pub mod opcode;

/// Resolves symbols in the AST.
mod symbol_resolver;
Expand Down
3 changes: 2 additions & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ impl ASTMnemonic {

impl fmt::Display for ASTMnemonic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, " {:?}", self)
// TODO: Why did it have spaces in front?
write!(f, "{:?}", self)
}
}

Expand Down
19 changes: 19 additions & 0 deletions src/bin/dism.rs
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());
}
191 changes: 191 additions & 0 deletions src/disassembler.rs
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);
}
}
}
91 changes: 91 additions & 0 deletions src/disassembler/listing.rs
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);
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub mod ast;
/// - Pass 2: Code generation - generating machine code
pub mod assembler;

/// Transforms machine code to assembly code.
pub mod disassembler;

/// 6502 CPU emulator
pub mod emulator;

Expand Down

0 comments on commit 41f1762

Please sign in to comment.