add simple assembler
This commit is contained in:
parent
a566c1737b
commit
debe2ecbc4
|
|
@ -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
|
||||||
|
}
|
||||||
30
src/cpu.rs
30
src/cpu.rs
|
|
@ -1,4 +1,6 @@
|
||||||
|
use crate::instructions::Instruction;
|
||||||
use crate::memory::Memory;
|
use crate::memory::Memory;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
pub a: u8,
|
pub a: u8,
|
||||||
|
|
@ -16,6 +18,22 @@ pub struct CPU {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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) {
|
pub fn inc_pc(&mut self) {
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +42,7 @@ impl CPU {
|
||||||
self.halted = true;
|
self.halted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mov(&mut self, mem: &mut Memory) {
|
pub fn mov(&mut self, mem: &Memory) {
|
||||||
let reg = mem.read(self.pc);
|
let reg = mem.read(self.pc);
|
||||||
self.inc_pc();
|
self.inc_pc();
|
||||||
let val = mem.read(self.pc);
|
let val = mem.read(self.pc);
|
||||||
|
|
@ -41,7 +59,7 @@ impl CPU {
|
||||||
self.zero = val == 0;
|
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);
|
let dest = mem.read(self.pc);
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
let src = mem.read(self.pc);
|
let src = mem.read(self.pc);
|
||||||
|
|
@ -83,7 +101,7 @@ impl CPU {
|
||||||
self.carry = carry;
|
self.carry = carry;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub(&mut self, mem: &mut Memory) {
|
pub fn sub(&mut self, mem: &Memory) {
|
||||||
let dest = mem.read(self.pc);
|
let dest = mem.read(self.pc);
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
let src = mem.read(self.pc);
|
let src = mem.read(self.pc);
|
||||||
|
|
@ -125,7 +143,7 @@ impl CPU {
|
||||||
self.carry = borrow;
|
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;
|
let low = mem.read(self.pc) as u16;
|
||||||
self.inc_pc();
|
self.inc_pc();
|
||||||
let high = mem.read(self.pc) as u16;
|
let high = mem.read(self.pc) as u16;
|
||||||
|
|
@ -136,7 +154,7 @@ impl CPU {
|
||||||
self.pc = addrs;
|
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;
|
let low = mem.read(self.pc) as u16;
|
||||||
self.inc_pc();
|
self.inc_pc();
|
||||||
let high = mem.read(self.pc) as u16;
|
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;
|
let low = mem.read(self.pc) as u16;
|
||||||
self.inc_pc();
|
self.inc_pc();
|
||||||
let high = mem.read(self.pc) as u16;
|
let high = mem.read(self.pc) as u16;
|
||||||
|
|
|
||||||
52
src/main.rs
52
src/main.rs
|
|
@ -1,58 +1,26 @@
|
||||||
|
mod assembler;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
use cpu::CPU;
|
use cpu::CPU;
|
||||||
use instructions::Instruction;
|
|
||||||
use memory::Memory;
|
use memory::Memory;
|
||||||
|
|
||||||
|
use crate::assembler::assembler;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut cpu = CPU::default();
|
let mut cpu = CPU::default();
|
||||||
let mut mem = Memory::new();
|
let mut mem = Memory::new();
|
||||||
|
|
||||||
// a = 10
|
let asm = std::fs::read_to_string("program.asm").unwrap();
|
||||||
mem.write(0x0000, Instruction::MOV as u8);
|
let program = assembler(&asm);
|
||||||
mem.write(0x0001, 0);
|
|
||||||
mem.write(0x0002, 5);
|
|
||||||
|
|
||||||
// b = 2
|
for (i, byte) in program.iter().enumerate() {
|
||||||
mem.write(0x0003, Instruction::MOV as u8);
|
mem.write(i as u16, *byte);
|
||||||
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 {
|
while !cpu.halted {
|
||||||
let opcode = mem.read(cpu.pc);
|
cpu.step(&mut mem);
|
||||||
cpu.inc_pc();
|
println!("{:?}", cpu);
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{:#?}", cpu);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue