M examples/hello_world_mini.bf => examples/hello_world_mini.bf +5 -1
@@ 1,1 1,5 @@
-++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
+++++++++[>++++[>++>+++> 20
++++>+<<<<-]>+>+>->>+[<] 56
+<-]>>.>---.+++++++..+++
+.>>.<-.<.+++.------.
+--------.>>+.>++.
M src/lc3.rs => src/lc3.rs +16 -14
@@ 19,6 19,7 @@ impl LC3CompiledBrainfuck {
const ORIGIN: Word = 0x3000;
// TODO: improve this to make use of negative bound being -16 and not -15
+ // TODO: move sign un-extension into primitives crate on emit function
fn compile_add(r: Register, n: i16) -> impl Iterator<Item = LC3Instruction> {
let base = if n >= 0 { 0xF } else { 0x1F };
let repeats = n / 0xF;
@@ 32,12 33,15 @@ impl LC3CompiledBrainfuck {
)))
}
+ fn compile_br(offset: i16) {}
+
// Curent register usage:
// R0 -> temporary register for I/O and other operations
// R1 -> data pointer
// TODO: fix expected single-byte overflow not happening because LC3 words are 2-bytes wide.
// TODO: improve naive instruction emition to make use of registers for hot paths.
// TODO: fix ADDs due to immediate mode only bearing 5 bits
+ // TODO: replace panics with result
pub fn compile(ir: &BrainfuckIR) -> Self {
let mut ir_iter = ir.instructions.iter().peekable();
let mut lc3_insts = Vec::new();
@@ 46,9 50,6 @@ impl LC3CompiledBrainfuck {
let mut value_mod = 0i16;
let mut pos_mod = 0i16;
- // Placeholder instruction to then be replaced by R1 init to tape beginning
- lc3_insts.push(LC3Instruction::RES);
-
// One pass to do small optimizations and produce LC3 instructions
while let Some(i) = ir_iter.next() {
match i {
@@ 90,15 91,19 @@ impl LC3CompiledBrainfuck {
}
BfInstruction::RET => {
let opening_pc = jump_stack.pop().expect("Invalid IR");
- let closing_pc = lc3_insts.len() as u16;
+ let closing_pc = lc3_insts.len() as i16;
+ let offset = closing_pc - opening_pc as i16;
+ if offset >= 1 << 9 {
+ panic!("Too big of a jump");
+ }
lc3_insts.push(LC3Instruction::LDR(Register::R0, Register::R1, 0));
lc3_insts.push(LC3Instruction::BR(
ConditionCode::NEG as u16 | ConditionCode::POS as u16,
- closing_pc - opening_pc as u16 + 2, // +2 because we wanna land on the inst after the [
+ (-offset) as u16, // + 2, // +2 because we wanna land on the inst after the [
));
// and fill in the jump instruction
lc3_insts[opening_pc + 1] =
- LC3Instruction::BR(ConditionCode::ZER.into(), closing_pc + 2);
+ LC3Instruction::BR(ConditionCode::ZER.into(), offset as u16);
}
BfInstruction::INP => {
lc3_insts.push(LC3Instruction::TRAP(TrapVector::GETC));
@@ 113,15 118,12 @@ impl LC3CompiledBrainfuck {
lc3_insts.push(LC3Instruction::TRAP(TrapVector::HALT));
- // Complete R1 init
- lc3_insts[0] = LC3Instruction::ADD(
- Register::R1,
- Register::R1,
- AddMode::Immediate(lc3_insts.len() as Word + 1),
- );
-
LC3CompiledBrainfuck {
- instructions: lc3_insts,
+ instructions: [
+ Self::compile_add(Register::R1, lc3_insts.len() as i16 + 1).collect(),
+ lc3_insts,
+ ]
+ .concat(),
}
}
M src/main.rs => src/main.rs +3 -3
@@ 20,13 20,13 @@ fn main() -> Result<()> {
// crate::interpreter::execute_default(&ir);
let lc3_compiled = crate::lc3::LC3CompiledBrainfuck::compile(&ir);
eprintln!("LC3 compiled:");
- for i in lc3_compiled.instructions.iter() {
- eprintln!("{:?}", i);
+ for (i, inst) in lc3_compiled.instructions.iter().enumerate() {
+ eprintln!("{i} {inst:?}");
}
// Write LC3 image
out_filename.push_str(".lc3.obj");
- println!("Emitting to {}", out_filename);
+ println!("Emitting to {out_filename}");
let mut out_file = File::create(out_filename)?;
// TODO: implement output buffering
for c in lc3_compiled.emit() {