前言:
目前兄弟们对“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语言编写虚拟机程序代码