Last active
June 14, 2020 13:08
-
-
Save mizukmb/ddb641cdf04e7e9eb71c299c9ff44da3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CPU EMULATOR | |
// CPUの作り方10講3章 | |
#![allow(unused)] | |
use std::num::Wrapping; | |
// 命令コードのインデックス | |
const MOV: u16 = 0; | |
const ADD: u16 = 1; | |
const SUB: u16 = 2; | |
const AND: u16 = 3; | |
const OR: u16 = 4; | |
const SL: u16 = 5; | |
const SR: u16 = 6; | |
const SRA: u16 = 7; | |
const LDL: u16 = 8; | |
const LDH: u16 = 9; | |
const CMP: u16 = 10; | |
const JE: u16 = 11; | |
const JMP: u16 = 12; | |
const LD: u16 = 13; | |
const ST: u16 = 14; | |
const HLT: u16 = 15; | |
// 汎用レジスタのインデックス | |
const REG0: u16 = 0; | |
const REG1: u16 = 1; | |
const REG2: u16 = 2; | |
const REG3: u16 = 3; | |
const REG4: u16 = 4; | |
const REG5: u16 = 5; | |
const REG6: u16 = 6; | |
const REG7: u16 = 7; | |
fn main() { | |
// 配列の宣言 | |
let mut reg: [u16; 8] = [0; 8]; | |
let mut rom: [u16; 256] = [0; 256]; | |
let mut ram: [u16; 256] = [0; 256]; | |
let mut pc = 0; // プログラムカウンタ | |
let mut ir = 0; // インストラクションレジスタ (命令レジスタ。現在実行中の命令を格納する) | |
let mut flag_eq = 0; // 比較用フラグ | |
rom = assembler(rom); | |
println!("START"); | |
loop { | |
ir = rom[pc]; // romのPC番地にある命令ビット列をインストラクションレジスタに転送 | |
println!("PC: {}, REG0: {}, REG1: {}, REG2: {}, REG3: {}", pc, reg[0], reg[1], reg[2], reg[3]); | |
pc += 1; // PCの更新 | |
println!("ir: {}", ir); | |
println!("flag_eq: {}", flag_eq); | |
match op_code(ir) { | |
MOV => reg[op_reg_a(ir)] = reg[op_reg_b(ir)], | |
ADD => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] + reg[op_reg_b(ir)], | |
SUB => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] - reg[op_reg_b(ir)], | |
AND => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] & reg[op_reg_b(ir)], | |
OR => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] | reg[op_reg_b(ir)], | |
SL => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] << 1, | |
SR => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] >> 1, | |
SRA => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] & 0b1000_0000_0000_0000 | reg[op_reg_a(ir)] >> 1, | |
LDL => reg[op_reg_a(ir)] = reg[op_reg_a(ir)] & 0b1111_1111_0000_0000 | op_data(ir) & 0b1111_1111, | |
LDH => reg[op_reg_a(ir)] = op_data(ir) << 8 | reg[op_reg_a(ir)] & 0b1111_1111, | |
CMP => flag_eq = if reg[op_reg_a(ir)] == reg[op_reg_b(ir)] { | |
1 | |
} else { | |
0 | |
}, | |
JE => if flag_eq == 1 { | |
pc = op_addr(ir) | |
}, | |
JMP=> pc = op_addr(ir), | |
LD => reg[op_reg_a(ir)] = ram[op_addr(ir)], | |
ST => ram[op_addr(ir)] = reg[op_reg_a(ir)], | |
HLT => break, | |
_ => println!("OP CODE '{}' IS NOT FOUND", op_code(ir)), | |
} | |
} | |
println!("ram[64] = {}", ram[64]); | |
println!("FINISHED!!"); | |
} | |
// 1+2+3+...+9+10の計算例 | |
fn assembler(mut rom: [u16; 256]) -> [u16; 256] { | |
// レジスタ0 に値 `0` を設定 | |
rom[0] = ldh(REG0, 0); | |
rom[1] = ldl(REG0, 0); | |
// レジスタ1 に値 `1` を設定 | |
rom[2] = ldh(REG1, 0); | |
rom[3] = ldl(REG1, 1); | |
// レジスタ2 に値 `0` を設定 | |
// REG2 に 0, 1, 2, 3... と次に足す数を格納していく | |
rom[4] = ldh(REG2, 0); | |
rom[5] = ldl(REG2, 0); | |
// レジスタ3に値 `10` を設定 | |
// 終了条件みたいなもの。REG2がREG3の数値になるまで足し続ける | |
rom[6] = ldh(REG3, 0); | |
rom[7] = ldl(REG3, 10); | |
// REG0に加算する数をREG2に設定する | |
rom[8] = add(REG2, REG1); | |
// 加算 | |
rom[9] = add(REG0, REG2); | |
// メモリ番地64にREG0の内容を転送 | |
rom[10] = st(REG0, 64); | |
// 10まで足したか? | |
rom[11] = cmp(REG2, REG3); | |
// Flagが1 (つまり10まで足した) ならば rom[14] にジャンプするようにPCを設定 | |
rom[12] = je(14); | |
// Flagが0だったら rom[8] にジャンプするようにPCを設定 | |
rom[13] = jmp(8); | |
rom[14] = hlt(); | |
rom | |
} | |
// 各命令の命令コードの機械語(16bit)を作成し返す | |
// 上位4ビット (11~14)→命令コード | |
// 下位11ビット(0~10)→オペランド | |
// MOV: move命令 | |
// 0~4: 不使用 | |
// 5~7: 第2オペランド | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn mov(ra: u16, rb: u16) -> u16 { | |
(Wrapping(MOV) << 11).0 | (Wrapping(ra) << 8).0 | rb << 5 | |
} | |
// add: 加算命令 | |
// 0~4: 不使用 | |
// 5~7: 第2オペランド | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn add(ra: u16, rb: u16) -> u16 { | |
(Wrapping(ADD) << 11).0 | (Wrapping(ra) << 8).0 | rb << 5 | |
} | |
// sub: 減算命令 | |
// 0~4: 不使用 | |
// 5~7: 第2オペランド | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn sub(ra: u16, rb: u16) -> u16 { | |
(Wrapping(SUB) << 11).0 | (Wrapping(ra) << 8).0 | rb << 5 | |
} | |
// and: 論理積命令 | |
// 0~4: 不使用 | |
// 5~7: 第2オペランド | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn and(ra: u16, rb: u16) -> u16 { | |
(Wrapping(AND) << 11).0 | (Wrapping(ra) << 8).0 | rb << 5 | |
} | |
// or: 論理和命令 | |
// 0~4: 不使用 | |
// 5~7: 第2オペランド | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn or(ra: u16, rb: u16) -> u16 { | |
(Wrapping(OR) << 11).0 | (Wrapping(ra) << 8).0 | rb << 5 | |
} | |
// sl: 1bit左シフト命令 | |
// 0~7: 不使用 | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn sl(ra: u16) -> u16 { | |
(Wrapping(SL) << 11).0 | (Wrapping(ra) << 8).0 | |
} | |
// sr: 1bit右シフト命令 | |
// 0~7: 不使用 | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn sr(ra: u16) -> u16 { | |
(Wrapping(SR) << 11).0 | (Wrapping(ra) << 8).0 | |
} | |
// sra: shift right arithmetic | |
// 1bit右シフト命令。シフトする前の最上位ビットが0のときは0, 1のときは1が最上位に書き込まれる | |
// 0~7: 不使用 | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn sra(ra: u16) -> u16 { | |
(Wrapping(SRA) << 11).0 | (Wrapping(ra) << 8).0 | |
} | |
// ldl: load immediate value low | |
// 第1オペランドで指定したレジスタのデータの **下位** 8ビットに第2オペランドの8ビットデータを直接書き込む | |
// 0~7: 第2オペランド | |
// 8~10: 第1オペランド | |
// 11~14: 命令コード | |
fn ldl(ra: u16, rb: u16) -> u16 { | |
(Wrapping(LDL) << 11).0 | (Wrapping(ra) << 8).0 | rb | |
} | |
// ldh: load immediate value high | |
// 第1オペランドで指定したレジスタのデータの **上位** 8ビットに第2オペランドの8ビットデータを直接書き込む | |
// 0~7: 第2オペランド | |
// 8~10: 第1オペランド | |
// 11~14: 命令コード | |
fn ldh(ra: u16, rb: u16) -> u16 { | |
(Wrapping(LDH) << 11).0 | (Wrapping(ra) << 8).0 | rb | |
} | |
// cmp: compare | |
// 第1オペランドと第2オペランドで指定したレジスタを比較し、完全一致の場合はFlagを1にして、そうでない場合はFlagを0にする | |
// 0~4: 不使用 | |
// 5~7: 第2オペランド | |
// 8~10 第1オペランド | |
// 11~14: 命令コード | |
fn cmp(ra: u16, addr: u16) -> u16 { | |
(Wrapping(CMP) << 11).0 | (Wrapping(ra) << 8).0 | (Wrapping(addr) << 5).0 | |
} | |
// je: jump equal | |
// Flagが1にセットされている場合にのみ、第2オペランドの8ビットをプログラムカウンタ(PC)に代入する。0の場合はなにもしない | |
// 0~7: 第2オペランド | |
// 8~10: 使わない | |
// 11~14: 命令コード | |
fn je(addr: u16) -> u16 { | |
(Wrapping(JE) << 11).0 | addr | |
} | |
// jmp: jump | |
// Flagの内容に影響されず、第2オペランドの8ビットをプログラムカウンタ(PC)に代入する。 | |
// 0~7: 第2オペランド | |
// 8~10: 使わない | |
// 11~14: 命令コード | |
fn jmp(addr: u16) -> u16 { | |
(Wrapping(JMP) << 11).0 | addr | |
} | |
// ld: load memory | |
// 第2オペランドの8ビットをアドレスとするメインメモリの内容を第1オペランドで指定したレジスタに転送する | |
// 0~7: メインメモリアドレス | |
// 8~10: レジスタアドレス | |
// 11~14: 命令コード | |
fn ld(ra: u16, addr: u16) -> u16 { | |
(Wrapping(LD) << 11).0 | (Wrapping(ra) << 8).0 | addr | |
} | |
// st: store memory | |
// 第1オペランドで指定したレジスタの内容を第2オペランドの8ビットをアドレスとするメインメモリに転送する | |
// 0~7: メインメモリアドレス | |
// 8~10: レジスタアドレス | |
// 11~14: 命令コード | |
fn st(ra: u16, addr: u16) -> u16 { | |
(Wrapping(ST) << 11).0 | (Wrapping(ra) << 8).0 | addr | |
} | |
// hlt: halt | |
// プログラムカウンタの更新を停止する | |
// 0~10: 不使用 | |
// 11~14: 命令コード | |
fn hlt() -> u16 { | |
(Wrapping(HLT) << 11).0 | |
} | |
// 命令コード・オペランドの抽出 | |
// 命令コードの抽出 | |
// 命令コードのビット列は4ビット固定で必ず上位4ビットが命令となっている | |
fn op_code(ir: u16) -> u16 { | |
ir >> 11 | |
} | |
// オペランド(REG_A)の抽出 | |
fn op_reg_a(ir: u16) -> usize { | |
(ir >> 8 & 0b111) as usize | |
} | |
// オペランド(REG_B)の抽出 | |
fn op_reg_b(ir: u16) -> usize { | |
(ir >> 5 & 0b111) as usize | |
} | |
// オペランド(data)の抽出 | |
// 下位8ビットの抽出なのでビットシフトの必要はない | |
fn op_data(ir: u16) -> u16 { | |
ir & 0b1111_1111 | |
} | |
// オペランド(addr)の抽出 | |
// 下位8ビットの抽出なのでビットシフトの必要はない | |
fn op_addr(ir: u16) -> usize { | |
(ir & 0b1111_1111) as usize | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment