| Crates.io | dismael |
| lib.rs | dismael |
| version | 0.1.1 |
| created_at | 2025-06-30 18:39:42.289764+00 |
| updated_at | 2025-06-30 18:48:25.665669+00 |
| description | Disassembler for Asmodeus binary files with intelligent analysis |
| homepage | |
| repository | https://github.com/szymonwilczek/asmodeus |
| max_upload_size | |
| id | 1732197 |
| size | 57,074 |
Disassembler for Asmodeus Binary Code
┌──────────────────────────────────────────────────────────┐
│ │
│ ██████╗ ██╗███████╗███╗ ███╗ █████╗ ███████╗██╗ │
│ ██╔══██╗██║██╔════╝████╗ ████║██╔══██╗██╔════╝██║ │
│ ██║ ██║██║███████╗██╔████╔██║███████║█████╗ ██║ │
│ ██║ ██║██║╚════██║██║╚██╔╝██║██╔══██║██╔══╝ ██║ │
│ ██████╔╝██║███████║██║ ╚═╝ ██║██║ ██║███████╗███████╗ │
│ ╚═════╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ │
│ │
│ Binary Machine Code Converter for Asmodeus Language │
└──────────────────────────────────────────────────────────┘
Dismael is the disassembler component of the Asmodeus toolchain. It performs reverse engineering of Asmodeus code, converting 16-bit machine instructions back into human-readable assembly language. Features intelligent code analysis, automatic label generation, and data/code separation.
use dismael::{disassemble, disassemble_to_string};
// Machine code for: POB #42, WYJSCIE, STP
let machine_code = vec![
0x212A, // POB #42 (immediate addressing)
0x7800, // WYJSCIE (or alias: WYJ)
0x3800, // STP
];
// Disassemble to vector of lines
let lines = disassemble(&machine_code)?;
for line in lines {
println!("{}", line);
}
// Or disassemble to single string
let assembly = disassemble_to_string(&machine_code)?;
println!("{}", assembly);
Output:
POB #42
WYJSCIE
STP
use dismael::Disassembler;
let machine_code = vec![
0x2004, // POB 4 (load from address 4)
0x090A, // DOD #10 (add immediate 10)
0x7800, // WYJSCIE (output)
0x3800, // STP (stop)
0x002A, // Data: 42
];
let mut disassembler = Disassembler::new();
let lines = disassembler.disassemble(&machine_code)?;
// Expected output with labels:
// L_0000:
// POB DATA_0004
// DOD #10
// WYJSCIE
// STP
// DATA_0004:
// RST 42
Dismael automatically generates labels for:
L_0010, L_0025, etc.DATA_0004, DATA_0015, etc.let program_with_jumps = vec![
0x2005, // POB 5 (load counter)
0x5800, // SOZ 0 (jump to 0 if zero)
0x1005, // ODE 5 (decrement)
0x2800, // SOB 0 (jump back to start)
0x3800, // STP (stop)
0x0005, // Data: 5 (counter value)
];
let assembly = disassemble_to_string(&program_with_jumps)?;
println!("{}", assembly);
Output:
L_0000:
POB DATA_0005
SOZ L_0004
ODE DATA_0005
SOB L_0000
L_0004:
STP
DATA_0005:
RST 5
Dismael intelligently separates code from data:
let mixed_program = vec![
0x2006, // POB 6 (instruction)
0x7800, // WYJSCIE (instruction)
0x3800, // STP (instruction)
0x0000, // Padding (data)
0x0000, // Padding (data)
0x0000, // Padding (data)
0x002A, // Value: 42 (data)
];
let mut disassembler = Disassembler::new();
let analyzer = disassembler.get_analyzer_mut();
// The analyzer identifies what's code vs data
let lines = disassembler.disassemble(&mixed_program)?;
All addressing modes are properly decoded:
let addressing_examples = vec![
0x2042, // POB #42 (immediate)
0x2004, // POB 4 (direct)
0x2204, // POB [4] (indirect)
0x2304, // POB R4 (register)
// ... etc
];
let assembly = disassemble_to_string(&addressing_examples)?;
// Output shows correct addressing syntax
// Simple disassembly functions
pub fn disassemble(machine_code: &[u16]) -> Result<Vec<String>, DisassemblerError>;
pub fn disassemble_to_string(machine_code: &[u16]) -> Result<String, DisassemblerError>;
For advanced control:
use dismael::Disassembler;
let mut disassembler = Disassembler::new();
let lines = disassembler.disassemble(machine_code)?;
// Access internal analyzer for more control
let analyzer = disassembler.get_analyzer();
let code_regions = analyzer.get_code_regions();
let data_addresses = analyzer.get_data_addresses();
#[derive(Debug, thiserror::Error)]
pub enum DisassemblerError {
#[error("Empty machine code provided")]
EmptyCode,
#[error("Invalid instruction at address {address}: 0x{instruction:04X}")]
InvalidInstruction { address: u16, instruction: u16 },
#[error("Address out of bounds: {address}")]
AddressOutOfBounds { address: u16 },
#[error("Invalid opcode: {opcode:05b}")]
InvalidOpcode { opcode: u8 },
}
#[derive(Debug, Clone)]
pub struct DisassembledInstruction {
pub address: u16,
pub raw_instruction: u16,
pub mnemonic: String,
pub operand: Option<String>,
pub addressing_mode: AddressingMode,
pub is_data: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AddressingMode {
Direct,
Immediate,
Indirect,
Register,
None, // For instructions without operands
}
use dismael::disassemble_to_string;
// Simple addition program
let machine_code = vec![
0x2006, // POB 6 - load first number
0x0807, // DOD 7 - add second number
0x7800, // WYJSCIE - output result
0x3800, // STP - stop
0x0000, // (padding)
0x0000, // (padding)
0x0019, // 25 - first number
0x0011, // 17 - second number
];
let assembly = disassemble_to_string(&machine_code)?;
println!("{}", assembly);
Output:
POB DATA_0006
DOD DATA_0007
WYJSCIE
STP
DATA_0006:
RST 25
DATA_0007:
RST 17
// Countdown loop
let countdown_program = vec![
0x2005, // POB 5 - load counter
0x7800, // WYJSCIE - output current value
0x1006, // ODE 6 - subtract 1
0x5800, // SOZ 4 - jump to end if zero
0x2800, // SOB 0 - jump back to start
0x3800, // STP - stop
0x0001, // 1 - decrement value
];
let assembly = disassemble_to_string(&countdown_program)?;
println!("{}", assembly);
Output:
L_0000:
POB DATA_0005
WYJSCIE
ODE DATA_0006
SOZ L_0005
SOB L_0000
L_0005:
STP
DATA_0005:
RST 5 ; Initial counter value
DATA_0006:
RST 1 ; Decrement value
// Program using extended arithmetic
let extended_program = vec![
0x210F, // POB #15
0x8903, // MNO #3 (extended: multiply by 3)
0x9105, // DZI #5 (extended: divide by 5)
0x7800, // WYJSCIE
0x3800, // STP
];
let assembly = disassemble_to_string(&extended_program)?;
println!("{}", assembly);
Output:
POB #15
MNO #3 ; Extended instruction
DZI #5 ; Extended instruction
WYJSCIE
STP
use dismael::Disassembler;
// Larger program with multiple jump targets
let complex_program = vec![
0x200A, // POB 10 - main entry
0x5807, // SOZ 7 - conditional jump
0x080B, // DOD 11 - add operation
0x7800, // WYJSCIE - output
0x1810, // ODE 16 - subtract
0x2806, // SOB 6 - loop back
0x3800, // STP - end
0x200C, // POB 12 - subroutine
0x0001, // (padding)
0x2800, // SOB 0 - return to main
0x0005, // Data: 5
0x0003, // Data: 3
0x0002, // Data: 2
];
let mut disassembler = Disassembler::new();
let lines = disassembler.disassemble(&complex_program)?;
// Analyzer provides additional insights
let analyzer = disassembler.get_analyzer();
println!("Found {} jump targets", analyzer.get_jump_targets().len());
println!("Found {} data addresses", analyzer.get_data_addresses().len());
use std::fs;
use dismael::disassemble_to_string;
// Read binary file (16-bit words, little-endian)
let binary_data = fs::read("program.bin")?;
// Convert bytes to u16 words
let machine_code: Vec<u16> = binary_data
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
let assembly = disassemble_to_string(&machine_code)?;
// Write disassembled output
fs::write("program_disassembled.asmod", assembly)?;
use dismael::{disassemble, DisassemblerError};
// Invalid machine code
let bad_code = vec![
0xFFFF, // Invalid instruction
0x7800, // Valid instruction
];
match disassemble(&bad_code) {
Ok(lines) => {
println!("Disassembled {} lines", lines.len());
}
Err(DisassemblerError::InvalidInstruction { address, instruction }) => {
println!("Invalid instruction 0x{:04X} at address {}", instruction, address);
}
Err(DisassemblerError::EmptyCode) => {
println!("No code to disassemble");
}
Err(e) => {
println!("Disassembly error: {}", e);
}
}
cargo test -p dismael
# Test basic instruction disassembly
cargo test -p dismael basic_disassembly_tests
# Test label generation
cargo test -p dismael label_tests
# Test data recognition
cargo test -p dismael data_recognition_tests
# Test addressing modes
cargo test -p dismael addressing_tests
# Test error handling
cargo test -p dismael error_tests
cargo test -p dismael --test integration_tests
use dismael::disassemble;
use std::time::Instant;
let large_program = vec![0x7800; 10000]; // 10K identical instructions
let start = Instant::now();
let lines = disassemble(&large_program)?;
let duration = start.elapsed();
println!("Disassembled {} instructions in {:?}",
large_program.len(), duration);
// The disassembler uses standard prefixes:
// L_xxxx for code labels
// DATA_xxxx for data labels
// Future versions might allow customization
use dismael::Disassembler;
let mut disassembler = Disassembler::new();
let lines = disassembler.disassemble(&machine_code)?;
// Get detailed analysis
let analyzer = disassembler.get_analyzer();
// Check if address contains code or data
for addr in 0..machine_code.len() {
if analyzer.is_code_address(addr as u16) {
println!("Address {} contains code", addr);
} else if analyzer.is_data_address(addr as u16) {
println!("Address {} contains data", addr);
}
}
Dismael is designed for perfect round-trip compatibility with Hephasm:
use hephasm::assemble_source;
use dismael::disassemble_to_string;
let original_source = r#"
start:
POB #42
WYJSCIE
STP
"#;
// Assemble to machine code
let machine_code = assemble_source(original_source)?;
// Disassemble back to assembly
let disassembled = disassemble_to_string(&machine_code)?;
// Re-assemble the disassembled code
let machine_code2 = assemble_source(&disassembled)?;
// Should be identical
assert_eq!(machine_code, machine_code2);
Dismael provides the reverse path in the compilation pipeline:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Hephasm │───▶│ Machine │───▶│ Dismael │
│ (Assembler) │ │ Code │ │(Disassembly)│
│ │ │ (.bin) │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ ▼
│ ┌─────────────┐ ┌─────────────┐
└───────────│ Re-assemble │◀───│ Assembly │
│ │ │ Source │
└─────────────┘ └─────────────┘
use dismael::disassemble_to_string;
use std::fs;
// Load binary file
let binary = fs::read("unknown_program.bin")?;
let machine_code: Vec<u16> = binary
.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();
// Disassemble to readable source
let assembly_source = disassemble_to_string(&machine_code)?;
// Save reconstructed source
fs::write("reconstructed.asmod", assembly_source)?;
println!("Successfully reverse engineered binary to assembly source");
| Binary Pattern | Assembly | Description |
|---|---|---|
0001_000_aaaaaaaa |
DOD addr |
Add direct |
0001_001_vvvvvvvv |
DOD #val |
Add immediate |
0010_000_aaaaaaaa |
ODE addr |
Subtract direct |
0011_000_aaaaaaaa |
LAD addr |
Store direct |
0100_000_aaaaaaaa |
POB addr |
Load direct |
0100_001_vvvvvvvv |
POB #val |
Load immediate |
0101_000_aaaaaaaa |
SOB addr |
Jump unconditional |
0110_000_aaaaaaaa |
SOM addr |
Jump if negative |
0111_000_00000000 |
STP |
Stop |
| Binary Pattern | Assembly | Description |
|---|---|---|
10001_000_aaaaaaa |
MNO addr |
Multiply direct |
10001_001_vvvvvvv |
MNO #val |
Multiply immediate |
10010_000_aaaaaaa |
DZI addr |
Divide direct |
10010_001_vvvvvvv |
DZI #val |
Divide immediate |
10011_000_aaaaaaa |
MOD addr |
Modulo direct |
10011_001_vvvvvvv |
MOD #val |
Modulo immediate |
This crate is part of the Asmodeus project and is licensed under the MIT License.