龙空技术网

让我们用Rust构建一个 LC-3 虚拟机 (完整代码部分)

心飞xin 198

前言:

目前兄弟们对“c语言编写虚拟机程序代码”大约比较注意,朋友们都想要学习一些“c语言编写虚拟机程序代码”的相关资讯。那么小编也在网络上收集了一些对于“c语言编写虚拟机程序代码””的相关内容,希望小伙伴们能喜欢,同学们快快来学习一下吧!

Address

I/O 寄存器名称

I/O Register Function

xFE00

键盘状态寄存器(KBSR)

状态位指示键盘是否接收到新字符。(位 [15])

xFE02

键盘数据寄存器(KBDR)

包含在键盘上键入的最后一个字符。(位 [7:0])

xFE04

显示状态寄存器(DSR)

状态位指示是否在屏幕显示数据。。(位 [15])

xFE06

显示数据寄存器(DDR)

写入该寄存器低字节的字符将显示在屏幕上。

xFFFE

机器控制寄存器(MCR)

是时钟使能位(位 [15]) 清零后指令处理停止。

// register/mod.rsconst PC_START: u16 = 0x3000;/// LC-3 has 10 registers/// -- 8 general purpose registers,/// -- 1 program counter, and/// -- 1 condition flag./// The program counter stores an uint that is the memory/// address of the next instruction to be executed.pub struct Registers {    pub r0: u16,    pub r1: u16,    pub r2: u16,    pub r3: u16,    pub r4: u16,    pub r5: u16,    pub r6: u16,    pub r7: u16,    pub pc: u16,    pub cond: u16,}
pub fn new() -> Registers {    Registers {        r0: 0,        // 通用寄存器        r1: 0,        // 通用寄存器        r2: 0,        // 通用寄存器        r3: 0,        // 通用寄存器        r4: 0,        // 通用寄存器        r5: 0,        // 通用寄存器        r6: 0,        // 通用寄存器        r7: 0,        // 通用寄存器        pc: PC_START, // 程序寄存器 program counter        cond: 0,      // 条件寄存器 condition flag    }}
pub fn update(&mut self, index: u16, value: u16) {    match index {        0 => self.r0 = value,        1 => self.r1 = value,        2 => self.r2 = value,        3 => self.r3 = value,        4 => self.r4 = value,        5 => self.r5 = value,        6 => self.r6 = value,        7 => self.r7 = value,        8 => self.pc = value,        9 => self.cond = value,        _ => panic!("Index out of bound"),    }}
pub fn get(&self, index: u16) -> u16 {    match index {        0 => self.r0,        1 => self.r1,        2 => self.r2,        3 => self.r3,        4 => self.r4,        5 => self.r5,        6 => self.r6,        7 => self.r7,        8 => self.pc,        9 => self.cond,        _ => panic!("Index out of bound. "),    }}
/// 根据寄存器内的值 `r` 更新条件寄存器。pub fn update_cond_code(&mut self, r: u16) {    if self.get(r) == 0 {        // 注意`9`是寄存器编号9,也就是 cond 寄存器        self.update(9, ConditionFlag::ZRO as u16);    } else if (self.get(r) >> 15) != 0 {        // 最左边位的 1 表示负数        self.update(9, ConditionFlag::NEG as u16);    } else {        self.update(9, ConditionFlag::POS as u16);    }}
/// The RCOND register stores condition flags that represents information about/// the most recent computation. It's used for checking logical conditions./// The LC-3 uses only 3 condition flags which indicate the sign of the previous calculation.enum ConditionFlag {    POS = 1 << 0,   // Positive    ZRO = 1 << 1,   // Zero    NEG = 1 << 2,   // Negative}
// vm/mod.rs//! The LC-3 has 65_536 memory locations, which is the max addressable by u16, 2^16.const MEMORY_SIZE: usize = u16::MAX as usize;use super::register::Registers;use std::io::Read;pub struct VM {    pub memory: [u16; MEMORY_SIZE],    pub registers: Registers,}
/// 执行指令是关于:/// 1.0 检测给定的 u16 值是哪条指令,即 OpCode 是什么?/// 2.0 将其与所有可能的操作码匹配/// 3.0 运行匹配的 OpCode 并从整个指令中提取操作数。
impl VM {    pub fn new() -> VM {        VM {            memory: [0; MEMORY_SIZE],            registers: Registers::new(),        }    }    pub fn read_memory(&mut self, address: u16) -> u16 {        if address == MemoryMappedReg::Kbsr as u16 {            self.handle_keyboard();        }        self.memory[address as usize]    }    fn handle_keyboard(&mut self) {        let mut buffer = [0; 1];        std::io::stdin().read_exact(&mut buffer).unwrap();        if buffer[0] != 0 {            self.write_memory(MemoryMappedReg::Kbsr as usize, 1 << 15);            self.write_memory(MemoryMappedReg::Kbdr as usize, buffer[0] as u16);        } else {            self.write_memory(MemoryMappedReg::Kbsr as usize, 0)        }    }    pub fn write_memory(&mut self, address: usize, value: u16) {        self.memory[address] = value;    }}
///  enum MemoryMappedReg {    // 键盘状态:KBSR 指示是否按下了某个键    Kbsr = 0xFE00,    // 键盘数据:KBDR 识别按下了具体哪个键    Kbdr = 0xFE02,}
// hardware/mod.rspub(crate) mod vm;pub(crate) mod register;pub(crate) mod instruction;use vm::VM;pub const MEMORY_SIZE: usize = std::u16::MAX as usize;pub fn execute_program(vm: &mut VM) {    //initialize Registers    while vm.registers.pc < MEMORY_SIZE as u16 {        //read instruction        let instruction = vm.read_memory(vm.registers.pc);        //increment program counter        vm.registers.pc += 1;        //extract op_code and execute operation...        instruction::execute_instruction(instruction, vm)    }}
// 下面大量的代码都在 instruction/mod.rs/// 操作码是 CPU 的基本操作,LC-3 只有 16 个操作码。/// 每条指令长 16 位,前 4 位存储操作码(OpCode),/// 其余部分保留给参数。use super::vm::VM;use std::io;use std::io::Read;use std::io::Write;use std::process;
#[derive(Debug)]pub enum OpCode {    BR = 0, // branch    ADD,    // add    LD,     // load    ST,     // store    JSR,    // jump register    AND,    // bitwise and    LDR,    // load register    STR,    // store register    RTI,     // unused    NOT,     // bitwise not    LDI,    // load indirect    STI,     // store indirect    JMP,    // jump    RES,    // reserved (unused)    LEA,     // load effective address    TRAP,   // execute trap}
pub enum TrapCode {    // get character from keyboard    Getc = 0x20,    // output a character    Out = 0x21,    // output a word string    Puts = 0x22,    // input a string    In = 0x23,    // output a byte string    Putsp = 0x24,    // halt the program    Halt = 0x25,}
pub enum TrapCode {    Getc = 0x20,    // get character from keyboard    Out = 0x21,     // output a character    Puts = 0x22,    // output a word string    In = 0x23,      // input a string    Putsp = 0x24,   // output a byte string    Halt = 0x25,    // halt the program}
pub fn execute_instruction(instruction: u16, vm: &mut VM) {    let op_code: Option<OpCode> = get_op_code(&instruction);    match op_code {        Some(OpCode::ADD) => add(instruction, vm),        Some(OpCode::AND) => and(instruction, vm),        Some(OpCode::NOT) => not(instruction, vm),        Some(OpCode::BR) => br(instruction, vm),        Some(OpCode::JMP) => jmp(instruction, vm),        Some(OpCode::JSR) => jsr(instruction, vm),        Some(OpCode::LD) => ld(instruction, vm),        Some(OpCode::LDI) => ldi(instruction, vm),        Some(OpCode::LDR) => ldr(instruction, vm),        Some(OpCode::LEA) => lea(instruction, vm),        Some(OpCode::ST) => st(instruction, vm),        Some(OpCode::STI) => sti(instruction, vm),        Some(OpCode::STR) => str(instruction, vm),        Some(OpCode::TRAP) => trap(instruction, vm),        _ => {}    }}
pub fn get_op_code(instruction: &u16) -> Option<OpCode> {    match instruction >> 12 {        0 => Some(OpCode::BR),        1 => Some(OpCode::ADD),        2 => Some(OpCode::LD),        3 => Some(OpCode::ST),        4 => Some(OpCode::JSR),        5 => Some(OpCode::AND),        6 => Some(OpCode::LDR),        7 => Some(OpCode::STR),        8 => Some(OpCode::RTI),        9 => Some(OpCode::NOT),        10 => Some(OpCode::LDI),        11 => Some(OpCode::STI),        12 => Some(OpCode::JMP),        13 => Some(OpCode::RES),        14 => Some(OpCode::LEA),        15 => Some(OpCode::TRAP),        _ => None,    }}
////// 用来模拟硬件CPU与门电路。 两种操作模式,立即或传递寄存器。////// 15           12 │11        9│8         6│ 5 │4     3│2         0/// ┌───────────────┼───────────┼───────────┼───┼───────┼───────────┐/// │      0101     │     DR    │  SR1      │ 0 │  00   │    SR2    │/// └───────────────┴───────────┴───────────┴───┴───────┴───────────┘//////  15           12│11        9│8         6│ 5 │4                 0/// ┌───────────────┼───────────┼───────────┼───┼───────────────────┐/// │      0101     │     DR    │  SR1      │ 1 │       IMM5        │/// └───────────────┴───────────┴───────────┴───┴───────────────────┘pub fn and(instruction: u16, vm: &mut VM) {    let dr = (instruction >> 9) & 0x7;    let sr1 = (instruction >> 6) & 0x7;    let imm_flag = (instruction >> 5) & 0x1;    if imm_flag == 1 {        let imm5 = sign_extend(instruction & 0x1F, 5);        vm.registers.update(dr, vm.registers.get(sr1) & imm5);    } else {        let sr2 = instruction & 0x7;        vm.registers.update(dr, vm.registers.get(sr1) & vm.registers.get(sr2));    }    vm.registers.update_cond_code(dr);}
/// 用来模拟硬件CPU非门电路。/// 15           12 │11        9│8         6│ 5 │4                 0/// ┌───────────────┼───────────┼───────────┼───┼───────────────────┐/// │      1001     │     DR    │     SR    │ 1 │       1111        │/// └───────────────┴───────────┴───────────┴───┴───────────────────┘pub fn not(instruction: u16, vm: &mut VM) {    let dr = (instruction >> 9) & 0x7;    let sr1 = (instruction >> 6) & 0x7;    vm.registers.update(dr, !vm.registers.get(sr1));    vm.registers.update_cond_code(dr);}
/// The branching operation; means to go somewhere else in the assembly code/// depending on whether some conditions are met./// The condition codes specified by the state of bits [11:9] are tested./// If bit [11] is set, N is tested; if bit [11] is clear, N is not tested./// If bit [10] is set, Z is tested. If any of the condition codes tested is set,/// the program branches to the location specified by/// adding the sign-extended PCOffset9 field to the incremented PC.////// 15           12 │11 │10 │ 9 │8                                 0/// ┌───────────────┼───┼───┼───┼───────────────────────────────────┐/// │      0000     │ N │ Z │ P │             PCOffset9             │/// └───────────────┴───┴───┴───┴───────────────────────────────────┘pub fn br(instruction: u16, vm: &mut VM) {    let pc_offset: u16 = sign_extend((instruction) & 0x1ff, 9);    let cond_flag: u16 = (instruction >> 9) & 0x7;    if cond_flag & vm.registers.cond != 0 {        let val: u32 = vm.registers.pc as u32 + pc_offset as u32;        vm.registers.pc = val as u16;    }    // 如果没有进行分支(不满足条件),PC 不会改变,PC 将只指向下一个顺序指令。}
/// The program unconditionally jumps to the location specified by the contents of the base register./// Bits [8:6] identify the base register. `RET` is listed as a separate instruction/// in the specification, since it is a different keyword in assembly./// However, it is actually a special case of JMP. RET happens whenever R1 is 7.//////  15           12│11        9│8         6│ 5                    0/// ┌───────────────┼───────────┼───────────┼───────────────────────┐/// │      1100     │    000    │   BaseR   │       00000           │/// └───────────────┴───────────┴───────────┴───────────────────────┘///  15           12│11        9│8         6│ 5                    0/// ┌───────────────┼───────────┼───────────┼───────────────────────┐/// │      1100     │    000    │    111    │       00000           │/// └───────────────┴───────────┴───────────┴───────────────────────┘pub fn jmp(instruction: u16, vm: &mut VM) {    let base_reg = (instruction >> 6) & 0x7;    vm.registers.pc = vm.registers.get(base_reg);}
/// First, the incremented PC is saved in R7./// This is the linkage back to the calling routine./// Then the PC is loaded with the address of the first instruction of the subroutine,/// causing an unconditional jump to that address./// The address of the subroutine is obtained from the base register (if bit [11] is 0),/// or the address is computed by sign-extending bits [10:0] and adding this value to the incremented PC (if bit [11] is 1).//////  15           12│11 │10/// ┌───────────────┼───┼───────────────────────────────────────────┐/// │      0100     │ 1 │                PCOffset11                 │/// └───────────────┴───┴───────────────────────────────────────────┘///  15           12│11 │10    9│8     6│5                         0/// ┌───────────────┼───┼───────┼───────┼───────────────────────────┐/// │      0100     │ 0 │   00  │ BaseR │           00000           │/// └───────────────┴───┴───────┴───────┴───────────────────────────┘pub fn jsr(instruction: u16, vm: &mut VM) {    let base_reg = (instruction >> 6) & 0x7;    let long_pc_offset = sign_extend(instruction & 0x7ff, 11);    let long_flag = (instruction >> 11) & 1;    vm.registers.r7 = vm.registers.pc;    if long_flag != 0 {        let val: u32 = vm.registers.pc as u32 + long_pc_offset as u32;        vm.registers.pc = val as u16;    } else {        vm.registers.pc = vm.registers.get(base_reg);    }}
/// An address is computed by sign-extending bits [8:0] to 16 bits and/// adding this value to the incremented PC./// The contents of memory at this address are loaded into DR./// The condition codes are set, based on whether the value loaded is negative, zero, or positive.//////  15           12│11        9│8                                 0/// ┌───────────────┼───────────┼───────────────────────────────────┐/// │      0010     │     DR    │            PCOffset9              │/// └───────────────┴───────────┴───────────────────────────────────┘pub fn ld(instruction: u16, vm: &mut VM) {    let dr: u16 = (instruction >> 9) & 0x7;    let pc_offset: u16  = sign_extend(instruction & 0x1ff, 9);    let mem: u32 = pc_offset as u32 + vm.registers.pc as u32;    let value: u16  = vm.read_memory(mem as u16);    vm.registers.update(dr, value);    vm.registers.update_cond_code(dr);}
/// Load base + offset/// An address is computed by sign-extending bits [5:0] to 16 bits/// and adding this value to the contents of the register specified by bits [8:6]./// The contents of memory at this address are loaded into DR./// The condition codes are set, based on whether the value loaded is negative, zero, or positive.//////  15           12│11        9│8             6│5                 0/// ┌───────────────┼───────────┼───────────────┼───────────────────┐/// │      1010     │     DR    │     BaseR     │     PCOffset6     │/// └───────────────┴───────────┴───────────────┴───────────────────┘pub fn ldr(instruction: u16, vm: &mut VM) {    let dr: u16 = (instruction >> 9) & 0x7;    let base_reg: u16 = (instruction >> 6) & 0x7;    let offset: u16 = sign_extend(instruction & 0x3F, 6);    let val: u32 = vm.registers.get(base_reg) as u32 + offset as u32;    let mem_value: u16 = vm.read_memory(val as u16).clone();    vm.registers.update(dr, mem_value);    vm.registers.update_cond_code(dr);}
/// An address is computed by sign-extending bits [8:0] to 16 bits and adding/// this value to the incremented PC./// This address is loaded into DR. The condition codes are set, based on whether the/// value loaded is negative, zero, or positive.//////  15           12│11        9│8                                 0/// ┌───────────────┼───────────┼───────────────────────────────────┐/// │      1110     │     DR    │            PCOffset9              │/// └───────────────┴───────────┴───────────────────────────────────┘pub fn lea(instruction: u16, vm: &mut VM) {    let dr: u16 = (instruction >> 9) & 0x7;    let pc_offset: u16 = sign_extend(instruction & 0x1ff, 9);    let val: u32 = vm.registers.pc as u32 + pc_offset as u32;    vm.registers.update(dr, val as u16);    vm.registers.update_cond_code(dr);}
/// ST: Store////// The contents of the register specified by SR are stored in the memory location/// whose address is computed by sign-extending bits [8:0] to 16 bits and adding this/// value to the incremented PC.//////  15           12│11        9│8                                 0/// ┌───────────────┼───────────┼───────────────────────────────────┐/// │      0011     │     SR    │            PCOffset9              │/// └───────────────┴───────────┴───────────────────────────────────┘pub fn st(instruction: u16, vm: &mut VM) {    let sr: u16 = (instruction >> 9) & 0x7;    let pc_offset: u16 = sign_extend(instruction & 0x1ff, 9);    let val: u32 = vm.registers.pc as u32 + pc_offset as u32;    let val: u16 = val as u16;    vm.write_memory(val as usize, vm.registers.get(sr));}
/// STI: Store Indirect////// The contents of the register specified by SR are stored in the memory location/// whose address is obtained as follows: Bits [8:0] are sign-extended to 16 bits and added to the incremented PC./// What is in memory at this address is the address of the location to which the data in SR is stored.//////  15           12│11        9│8                                 0/// ┌───────────────┼───────────┼───────────────────────────────────┐/// │      1011     │     SR    │            PCOffset9              │/// └───────────────┴───────────┴───────────────────────────────────┘pub fn sti(instruction: u16, vm: &mut VM) {    let sr: u16 = (instruction >> 9) & 0x7;    let pc_offset: u16 = sign_extend(instruction & 0x1ff, 9);    let val: u32 = vm.registers.pc as u32 + pc_offset as u32;    let val: u16 = val as u16;    //这就是STI和ST的区别    let address: usize = vm.read_memory(val) as usize;    vm.write_memory(address, vm.registers.get(sr));}
/// STR: Store Base+offset////// The contents of the register specified by SR are stored in the memory location/// whose address is computed by sign-extending bits [5:0] to 16 bits/// and adding this value to the contents of the register specified by bits [8:6].//////  15           12│11        9│8         6│                      0/// ┌───────────────┼───────────┼───────────┼───────────────────────┐/// │      0111     │     SR    │   BaseR   │        PCOffset6      │/// └───────────────┴───────────┴───────────┴───────────────────────┘pub fn str(instruction: u16, vm: &mut VM) {    let dr: u16 = (instruction >> 9) & 0x7;    let base_reg: u16 = (instruction >> 6) & 0x7;    let offset: u16 = sign_extend(instruction & 0x3F, 6);    let val: u32 = vm.registers.get(base_reg) as u32 + offset as u32;    let val: u16 = val as u16;    vm.write_memory(val as usize, vm.registers.get(dr));}
/// TRAP: System Call/// trap 函数允许与 I/O 设备交互/// First R7 is loaded with the incremented PC./// (This enables a return to the instruction physically following the TRAP instruction in the original program/// after the service routine has completed execution.)/// Then the PC is loaded with the starting address of the system call specified by trap vector8./// The starting address is contained in the memory location whose address is obtained by zero-extending trap vector8 to 16 bits.pub fn trap(instruction: u16, vm: &mut VM) {    match instruction & 0xFF {        0x20 => {            // Get character            let mut buffer = [0; 1];            std::io::stdin().read_exact(&mut buffer).unwrap();            vm.registers.r0 = buffer[0] as u16;        }        0x21 => {            // Write out character            let c = vm.registers.r0 as u8;            print!("{}", c as char);            // println!("[*] OUT");        }        0x22 => {            // Puts            let mut index = vm.registers.r0;            let mut c = vm.read_memory(index);            while c != 0x0000 {                print!("{}", (c as u8) as char);                index += 1;                c = vm.read_memory(index);            }            io::stdout().flush().expect("failed to flush");        }        0x23 => {            // In, Print a prompt on the screen and read a single character from the keyboard.            // The character is echoed onto the console monitor, and its ASCII code is copied into R0.The high eight bits of R0 are cleared.            print!("Enter a  character : ");            io::stdout().flush().expect("failed to flush");            let char = std::io::stdin()                .bytes()                .next()                .and_then(|result| result.ok())                .map(|byte| byte as u16)                .unwrap();            vm.registers.update(0, char);        }        0x24 => {            // Putsp            let mut index = vm.registers.r0;            let mut c = vm.read_memory(index);            while c != 0x0000 {                let c1 = ((c & 0xFF) as u8) as char;                print!("{}", c1);                let c2 = ((c >> 8) as u8) as char;                if c2 != '\0' {                    print!("{}", c2);                }                index += 1;                c = vm.read_memory(index);            }            io::stdout().flush().expect("failed to flush");        }        0x25 => {            println!("HALT detected");            io::stdout().flush().expect("failed to flush");            process::exit(1);        }        _ => {            process::exit(1);        }    }}
pub fn sign_extend(mut x: u16, bit_count: u8) -> u16 {    if (x >> (bit_count - 1)) & 1 != 0 {        x |= 0xFFFF << bit_count;    }    x}
extern crate termios;pub mod hardware;use hardware::vm::VM;use termios::*;use byteorder::{BigEndian, ReadBytesExt};use std::{fs::File, io::BufReader};use structopt::StructOpt;#[derive(StructOpt)]struct Cli {    /// 要读取的文件的路径    #[structopt(parse(from_os_str))]    path: std::path::PathBuf,    #[structopt(long)]    print_asm: bool, // Future feature}/// 一旦我们将程序加载到 VM 的内存中,运行它就是使用存储在程序计数器 (PC) 寄存器中的值遍历内存位置的问题。fn main() {    // 使 VM 的终端具有交互性的一些技巧    let stdin = 0;    let termios = termios::Termios::from_fd(stdin).unwrap();    // 创建我们将修改的 termios 的可变副本    let mut new_termios = termios.clone();    //     new_termios.c_iflag &= IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON;    new_termios.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode    tcsetattr(stdin, TCSANOW, &mut new_termios).unwrap();    // 实际 VM 逻辑代码    let mut vm = VM::new();    let cli = Cli::from_args();    let f = File::open(cli.path).expect("couldn't open file");    let mut f = BufReader::new(f);    // 请注意我们如何使用 `read_u16` 和 BigEndian 来读取二进制文件。    let base_address = f.read_u16::<BigEndian>().expect("error");    // 这里我们在内存中加载程序    let mut address = base_address as usize;    loop {        match f.read_u16::<BigEndian>() {            Ok(instruction) => {                vm.write_memory(address, instruction);                address += 1;            }            Err(e) => {                if e.kind() == std::io::ErrorKind::UnexpectedEof {                    println!("OK")                } else {                    println!("failed: {}", e);                }                break;            }        }    }    hardware::execute_program(&mut vm);    // 将标准输入重置为原始 termios 数据    tcsetattr(stdin, TCSANOW, &termios).unwrap();}// 备份历史代码// let args: Vec<String> = env::args().collect::<Vec<String>>();// println!("文件路径:{:?}", args[1]);// let data = fs::read(args[1].clone()).expect("Unable to read file");// println!("文件数据: {:?}\n", data);

标签: #c语言编写虚拟机程序代码