add simple assembler

This commit is contained in:
krolxon 2026-01-04 23:49:04 +05:30
parent a566c1737b
commit debe2ecbc4
4 changed files with 108 additions and 49 deletions

73
src/assembler.rs Normal file
View File

@ -0,0 +1,73 @@
use crate::instructions::Instruction;
fn tokenize(line: &str) -> Vec<String> {
line.split(|c| c == ' ' || c == ',' || c == '\t')
.filter(|s| !s.is_empty())
.map(|s| s.to_lowercase())
.collect()
}
fn parse_reg(s: &str) -> u8 {
match s {
"a" => 0,
"b" => 1,
"c" => 2,
"d" => 3,
_ => panic!("Unknown register {}", s),
}
}
pub fn assembler(source: &str) -> Vec<u8> {
let mut bytes = Vec::new();
for (line_no, line) in source.lines().enumerate() {
let line = line.trim();
// Comments in assembly start with ";"
if line.is_empty() || line.starts_with(';') {
continue;
}
let tokens = tokenize(line);
match tokens[0].as_str() {
"mov" => {
// mov reg, imm
let reg = parse_reg(&tokens[1]);
let imm: u8 = tokens[2].parse().unwrap();
bytes.push(Instruction::MOV as u8);
bytes.push(reg);
bytes.push(imm);
}
"add" => {
// add a, b
let r1 = parse_reg(&tokens[1]);
let r2 = parse_reg(&tokens[2]);
bytes.push(Instruction::ADD as u8);
bytes.push(r1);
bytes.push(r2);
}
"sub" => {
// sub a, b
let r1 = parse_reg(&tokens[1]);
let r2 = parse_reg(&tokens[2]);
bytes.push(Instruction::SUB as u8);
bytes.push(r1);
bytes.push(r2);
}
"hlt" => {
bytes.push(Instruction::HLT as u8);
}
_ => panic!("Line {}: unknown instruction", line_no + 1),
}
}
bytes
}

View File

@ -1,4 +1,6 @@
use crate::instructions::Instruction;
use crate::memory::Memory;
#[derive(Default, Debug)]
pub struct CPU {
pub a: u8,
@ -16,6 +18,22 @@ pub struct CPU {
}
impl CPU {
pub fn step(&mut self, mem: &mut Memory) {
let opcode = mem.read(self.pc);
self.inc_pc();
match opcode {
x if x == Instruction::MOV as u8 => self.mov(mem),
x if x == Instruction::ADD as u8 => self.add(mem),
x if x == Instruction::SUB as u8 => self.sub(mem),
x if x == Instruction::JMP as u8 => self.jmp(mem),
x if x == Instruction::JZ as u8 => self.jz(mem),
x if x == Instruction::JNZ as u8 => self.jnz(mem),
x if x == Instruction::HLT as u8 => self.halt(),
_ => panic!("Unknown opcode {:02X}", opcode),
}
}
pub fn inc_pc(&mut self) {
self.pc += 1;
}
@ -24,7 +42,7 @@ impl CPU {
self.halted = true;
}
pub fn mov(&mut self, mem: &mut Memory) {
pub fn mov(&mut self, mem: &Memory) {
let reg = mem.read(self.pc);
self.inc_pc();
let val = mem.read(self.pc);
@ -41,7 +59,7 @@ impl CPU {
self.zero = val == 0;
}
pub fn add(&mut self, mem: &mut Memory) {
pub fn add(&mut self, mem: &Memory) {
let dest = mem.read(self.pc);
self.pc += 1;
let src = mem.read(self.pc);
@ -83,7 +101,7 @@ impl CPU {
self.carry = carry;
}
pub fn sub(&mut self, mem: &mut Memory) {
pub fn sub(&mut self, mem: &Memory) {
let dest = mem.read(self.pc);
self.pc += 1;
let src = mem.read(self.pc);
@ -125,7 +143,7 @@ impl CPU {
self.carry = borrow;
}
pub fn jmp(&mut self, mem: &mut Memory) {
pub fn jmp(&mut self, mem: &Memory) {
let low = mem.read(self.pc) as u16;
self.inc_pc();
let high = mem.read(self.pc) as u16;
@ -136,7 +154,7 @@ impl CPU {
self.pc = addrs;
}
pub fn jz(&mut self, mem: &mut Memory) {
pub fn jz(&mut self, mem: &Memory) {
let low = mem.read(self.pc) as u16;
self.inc_pc();
let high = mem.read(self.pc) as u16;
@ -149,7 +167,7 @@ impl CPU {
}
}
pub fn jnz(&mut self, mem: &mut Memory) {
pub fn jnz(&mut self, mem: &Memory) {
let low = mem.read(self.pc) as u16;
self.inc_pc();
let high = mem.read(self.pc) as u16;

View File

@ -1,58 +1,26 @@
mod assembler;
mod cpu;
mod instructions;
mod memory;
use cpu::CPU;
use instructions::Instruction;
use memory::Memory;
use crate::assembler::assembler;
fn main() {
let mut cpu = CPU::default();
let mut mem = Memory::new();
// a = 10
mem.write(0x0000, Instruction::MOV as u8);
mem.write(0x0001, 0);
mem.write(0x0002, 5);
let asm = std::fs::read_to_string("program.asm").unwrap();
let program = assembler(&asm);
// b = 2
mem.write(0x0003, Instruction::MOV as u8);
mem.write(0x0004, 1);
mem.write(0x0005, 3);
// a = a + b
mem.write(0x0006, Instruction::SUB as u8);
mem.write(0x0007, 0);
mem.write(0x0008, 1);
// JMP to halt
mem.write(0x0009, Instruction::JNZ as u8);
mem.write(0x000a, 0x0f); // Low
mem.write(0x000b, 0x00); // High
// set b = 0
mem.write(0x000c, Instruction::MOV as u8);
mem.write(0x000d, 1);
mem.write(0x000e, 0);
// halt
mem.write(0x000f, Instruction::HLT as u8);
while !cpu.halted {
let opcode = mem.read(cpu.pc);
cpu.inc_pc();
match opcode {
x if x == Instruction::MOV as u8 => cpu.mov(&mut mem),
x if x == Instruction::ADD as u8 => cpu.add(&mut mem),
x if x == Instruction::SUB as u8 => cpu.sub(&mut mem),
x if x == Instruction::JMP as u8 => cpu.jmp(&mut mem),
x if x == Instruction::JZ as u8 => cpu.jz(&mut mem),
x if x == Instruction::JNZ as u8 => cpu.jnz(&mut mem),
x if x == Instruction::HLT as u8 => cpu.halt(),
_ => panic!("Unknown opcode {:02X}", opcode),
}
for (i, byte) in program.iter().enumerate() {
mem.write(i as u16, *byte);
}
println!("{:#?}", cpu);
while !cpu.halted {
cpu.step(&mut mem);
println!("{:?}", cpu);
}
}