~jpl8/lorthc3

001577bdbf1f92e7dd768789aa2398fd4bcd1761 — jpl 1 year, 1 month ago fc71356
Add fill instruction and finish line reading
5 files changed, 99 insertions(+), 115 deletions(-)

M Cargo.lock
M Cargo.toml
M src/compiler.rs
A src/lorth_instruction.rs
M src/main.rs
M Cargo.lock => Cargo.lock +7 -0
@@ 3,9 3,16 @@
version = 3

[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"

[[package]]
name = "lorthc3"
version = "0.1.0"
dependencies = [
 "anyhow",
 "toasty_lc3_primitives",
]


M Cargo.toml => Cargo.toml +1 -0
@@ 6,4 6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.68"
toasty_lc3_primitives = { git = "https://git.sr.ht/~tmpod/toasty-lc3-vm"}

M src/compiler.rs => src/compiler.rs +69 -27
@@ 1,87 1,109 @@
use toasty_lc3_primitives::condition_codes::ConditionCode;
use toasty_lc3_primitives::instructions::{AddMode, AndMode, TrapVector::*};
use toasty_lc3_primitives::instructions::{AddMode, AndMode, JsrMode, TrapVector::*};
use toasty_lc3_primitives::instructions::{Instruction, Instruction::*};
use toasty_lc3_primitives::types::Word;

use toasty_lc3_primitives::registers::{Register, Register::*, Registers};

use anyhow::{bail, Result};

use std::iter;

use crate::lorth_instruction::LorthInstruction::{self, *};

pub type SignedWord = i16;

pub struct ForthCompiler {
    // BIG TODO: remove pub
    pub instructions: Vec<Instruction>,
    instructions: Vec<LorthInstruction>,
    registers: Registers,
    tib: Option<Word>,
    toin: Option<Word>,
}

impl ForthCompiler {
    pub const ORIGIN: u16 = 0x3000;
    pub const TIB: u16 = 0x3090;
    pub const TOIN: u16 = 0x3100;
    pub const TIB: u16 = 0x4000;
    pub const TOIN: u16 = 0x4100;

    pub fn new() -> Self {
        Self {
            instructions: vec![],
            registers: Registers::new(),
            tib: None,
            toin: None,
        }
    }
    pub fn load(&mut self, r: Register, addr: Word) -> &mut Self {
        let offset: SignedWord = (addr as SignedWord) - (self.pc() as SignedWord) - 1;
        eprintln!("Load Offset: {:#02X}", offset);
        self.instr(LD(r, offset as Word))
        self.instr(LC3(LD(r, offset as Word)))
    }

    pub fn load_addr(&mut self, r: Register, addr: Word) -> &mut Self {
        let offset: SignedWord = (addr as SignedWord) - (self.pc() as SignedWord) - 1;
        eprintln!("Load Offset: {:#02X}", offset);
        self.instr(LEA(r, offset as Word))
        self.instr(LC3(LEA(r, offset as Word)))
    }

    pub fn get_char(&mut self) -> &mut Self {
        self.instr(TRAP(GETC))
        self.instr(LC3(TRAP(GETC)))
    }

    pub fn out_char(&mut self) -> &mut Self {
        self.instr(TRAP(OUT))
        self.instr(LC3(TRAP(OUT)))
    }

    pub fn add(&mut self, dest: Register, src: Register, arg: AddMode) -> &mut Self {
        self.instr(ADD(dest, src, arg))
        self.instr(LC3(ADD(dest, src, arg)))
    }
    pub fn and(&mut self, dest: Register, src: Register, arg: AndMode) -> &mut Self {
        self.instr(AND(dest, src, arg))
        self.instr(LC3(AND(dest, src, arg)))
    }

    pub fn not(&mut self, dest: Register, src: Register) -> &mut Self {
        self.instr(NOT(dest, src))
        self.instr(LC3(NOT(dest, src)))
    }

    pub fn branch(&mut self, cond: Word, addr: Word) -> &mut Self {
        eprintln!("Addr: {:#02X}", addr);
        let offset: SignedWord = (addr as SignedWord) - (self.pc() as SignedWord) - 1;
        self.instr(BR(cond, offset as Word))
        self.instr(LC3(BR(cond, offset as Word)))
    }

    // Optimizations

    fn num_diff_bits(a: Word, b: Word) -> u32 {
        return (a ^ b).count_ones();
    }

    /// Move 5 bit word
    pub fn mov(&mut self, r: Register, v: Word) -> &mut Self {
        self.and(r, r, AndMode::Immediate(0))
            .add(r, r, AddMode::Immediate(v))
        self.and(r, r, AndMode::Immediate(0));
        const MAX_POS_5_BIT: Word = 0x0F;
        let div = v / MAX_POS_5_BIT;
        let rem = v % MAX_POS_5_BIT;
        for _ in 0..div {
            self.add(r, r, AddMode::Immediate(MAX_POS_5_BIT));
        }
        self.add(r, r, AddMode::Immediate(rem))
    }

    pub fn store(&mut self, src: Register, base: Register, offset: Word) -> &mut Self {
        self.instr(STR(src, base, offset))
        self.instr(LC3(STR(src, base, offset)))
    }

    pub fn fill(&mut self, w: Word) -> &mut Self {
        self.instr(FILL(w))
    }

    pub fn halt(&mut self) -> &mut Self {
        self.instr(TRAP(HALT))
        self.instr(LC3(TRAP(HALT)))
    }

    pub fn instr(&mut self, instruction: Instruction) -> &mut Self {
    pub fn instr(&mut self, instruction: LorthInstruction) -> &mut Self {
        self.instructions.push(instruction);
        self.inc_pc()
    }
    pub fn instrs(&mut self, instructions: Vec<Instruction>) -> &mut Self {
    pub fn instrs(&mut self, instructions: Vec<LorthInstruction>) -> &mut Self {
        instructions.into_iter().for_each(|i| {
            self.instr(i);
        });


@@ 105,11 127,27 @@ impl ForthCompiler {
            .flat_map(|w| w.to_be_bytes())
    }

    pub fn read_input(&mut self) -> &mut Self {
        let tib_cell = self.pc();
    pub fn jmp_subroutine(&mut self, offset: Word) -> &mut Self {
        self.instr(LC3(JSR(JsrMode::Offset(offset))))
    }

    fn load_intermediate_cells(&mut self) -> &mut Self {
        self.jmp_subroutine(2);
        self.fill(Self::TIB);
        self.tib = Some(self.pc());
        self.fill(Self::TOIN);
        self.toin = Some(self.pc());

        self
    }

    pub fn read_input(&mut self) -> Result<&mut Self> {
        if let Some(tib) = self.tib {
            self.load(R1, tib).load_addr(R0, ForthCompiler::TOIN);
        } else {
            bail!("TIB not allocated!")
        }

        self.load_addr(R1, ForthCompiler::TIB)
            .load_addr(R0, ForthCompiler::TOIN);
        let loop_top = self.pc();
        self.get_char()
            .out_char()


@@ 122,10 160,14 @@ impl ForthCompiler {
                loop_top,
            )
            .add(R1, R1, AddMode::Immediate(-1i16 as Word))
            .store(R0, R1, 0)
            .store(R0, R1, 0);

        Ok(self)
    }

    pub fn compile(&mut self) -> &mut Self {
        self.read_input().halt()
    pub fn compile(&mut self) -> Result<&mut Self> {
        self.load_intermediate_cells().read_input()?.halt();

        Ok(self)
    }
}

A src/lorth_instruction.rs => src/lorth_instruction.rs +17 -0
@@ 0,0 1,17 @@
use toasty_lc3_primitives::instructions::{Instruction, Instruction::*};
use toasty_lc3_primitives::types::Word;

#[derive(Debug, PartialEq, Eq)]
pub enum LorthInstruction {
    LC3(Instruction),
    FILL(Word),
}

impl LorthInstruction {
    pub fn emit(&self) -> Word {
        match self {
            Self::LC3(instr) => instr.emit(),
            Self::FILL(w) => *w,
        }
    }
}

M src/main.rs => src/main.rs +5 -88
@@ 1,103 1,20 @@
mod compiler;
mod lorth_instruction;

use compiler::ForthCompiler;
use toasty_lc3_primitives::condition_codes::ConditionCode;
use toasty_lc3_primitives::instructions::{AddMode, AndMode};
use toasty_lc3_primitives::registers::{Register, Register::*, Registers};
use toasty_lc3_primitives::types::Word;

use std::fs::File;
use std::io::{Result, Write};
use std::io::Write;

// struct ForthCompiler {
//     instructions: Vec<Instruction>,
//     registers: Registers,
// }

// impl ForthCompiler {
//     /// Terminal Input Buffer
//     const TIB: u16 = 0x4000;
//     const TOIN: u16 = 0x4100;

//     fn new() -> Self {
//         Self {
//             instructions: vec![],
//             registers: Registers::new(),
//         }
//     }

//     fn test_compile(&mut self) {
//         let test = vec![
//             TRAP(GETC),
//             TRAP(OUT),
//             ADD(R0, R0, AddMode::Immediate(-1i16 as Word)), // -1
//             TRAP(OUT),
//             TRAP(HALT),
//         ];

//         self.add_instr(&test);
//     }

//     fn add_instr(&mut self, instructions: &Vec<Instruction>) -> &mut Self {
//         self.instructions.extend(instructions);
//         self
//     }

//     fn compile_readln(&mut self) {
//         // let readln_pc: Word = Registers[PC];

//         let load_addresses = vec![LD(R1, Self::TIB), LD(R0, Self::TOIN), STR(R1, R0, 0)];
//         let loop_character_reading = vec![
//             TRAP(GETC),
//             TRAP(OUT),
//             STR(R0, R1, 0),
//             ADD(R1, R1, AddMode::Immediate(1 as Word)),
//             NOT(R0, R0),
//             AND(R0, R0, AndMode::Immediate('\n' as Word)),
//         ];
//         let branching = vec![
//             BR(
//                 ConditionCode::NEG as Word | ConditionCode::POS as Word,
//                 -((loop_character_reading.len() + 1) as i16) as Word,
//             ),
//             ADD(R1, R1, AddMode::Immediate(-1i16 as Word)), // -1
//             STR(R0, R1, 0),
//         ];

//         let print_chars = vec![
//             LD(R0, Self::TIB),
//             TRAP(OUT),
//             LD(R0, Self::TIB + 1),
//             TRAP(OUT),
//             LD(R0, Self::TIB + 2),
//             TRAP(OUT),
//         ];

//         let halt = vec![TRAP(HALT)];

//         self.add_instr(&load_addresses)
//             .add_instr(&loop_character_reading)
//             .add_instr(&branching)
//             .add_instr(&print_chars)
//             // TODO: remove halt and print
//             .add_instr(&halt);
//     }

//     fn emit(&self) -> impl Iterator<Item = u8> + '_ {
//         let inst_words = self.instructions.iter().map(|i| i.emit());
//         iter::once(Self::ORIGIN)
//             .chain(inst_words)
//             .flat_map(|w| w.to_be_bytes())
//     }
// }
use anyhow::Result;

fn main() -> Result<()> {
    let mut out = File::create("test.obj")?;

    let mut compiler = ForthCompiler::new();
    compiler.compile();
    compiler.compile()?;

    println!("{:?}", compiler.instructions);
    // println!("{:?}", compiler.instructions);

    for c in compiler.emit() {
        out.write(&[c])?;