ive/src/cpu.rs

418 lines
11 KiB
Rust

use crate::gpu::{Gpu, Primitive};
use crate::{elf, Memory, Store};
use byteorder::{ByteOrder, LittleEndian};
pub const RAM_BASE: u32 = 0x80000000;
const RAM_SIZE: u32 = RAM_BASE + 0x100000;
const GPU_BASE: u32 = 0x60000000;
const GPU_SIZE: u32 = GPU_BASE + 0x1;
#[derive(Debug)]
enum Format {
R,
I,
IL,
S,
B,
J,
JI,
U,
UI,
E,
}
enum Opcode {
Add,
Sub,
Xor,
Or,
And,
Sll,
Srl,
Sra,
Slt,
Sltu,
Addi,
Xori,
Ori,
Andi,
Slli,
Srli,
Srai,
Slti,
Sltiu,
Lb,
Lh,
Lw,
Lbu,
Lhu,
Sb,
Sh,
Sw,
Beq,
Bne,
Blt,
Bge,
Bltu,
Bgeu,
Jal,
Jalr,
Lui,
Auipc,
Ecall,
Ebreak,
}
struct Ram {
mem: Vec<u8>,
}
struct Bus {
ram: Box<dyn Memory>,
gpu: Gpu,
}
pub struct Cpu {
pub reg: [u32; 31],
pub pc: u32,
bus: Bus,
}
struct Instruction {
op: Format,
rd: u8,
f3: u8,
f7: u8,
rs1: u8,
rs2: u8,
imm: u32,
simm: u32,
uimm: u32,
bimm: u32,
jimm: u32,
}
impl Ram {
fn new() -> Self {
let mut mem = Vec::new();
mem.resize((RAM_SIZE - RAM_BASE) as usize, 0);
Self { mem }
}
}
impl Memory for Ram {
fn read(&self, addr: u32) -> u32 {
let addr = addr as usize;
// should check for unaligned reads
LittleEndian::read_u32(&self.mem[addr..addr + 4])
}
fn write(&mut self, addr: u32, val: Store) {
let addr = addr as usize;
match val {
Store::Byte(b) => self.mem[addr] = b,
Store::Half(h) => LittleEndian::write_u16(&mut self.mem[addr..addr + 2], h),
Store::Word(w) => LittleEndian::write_u32(&mut self.mem[addr..addr + 4], w),
}
}
}
impl Format {
fn new(ins: u32) -> Self {
match ins & 0x7f {
0x33 => Self::R,
0x13 => Self::I,
0x03 => Self::IL,
0x23 => Self::S,
0x63 => Self::B,
0x6f => Self::J,
0x67 => Self::JI,
0x37 => Self::U,
0x17 => Self::UI,
0x73 => Self::E,
_ => panic!(),
}
}
}
macro_rules! op {
($op: ident) => {
Instruction {
op: Format::$op,
..
}
};
}
macro_rules! f3f7 {
($op: ident, $f3: expr, $f7: expr) => {
Instruction {
op: Format::$op,
f3: $f3,
f7: $f7,
..
}
};
}
macro_rules! f3 {
($op: ident, $f3: expr) => {
Instruction {
op: Format::$op,
f3: $f3,
..
}
};
}
impl Opcode {
fn new(ins: &Instruction) -> Self {
match ins {
f3f7!(R, 0x00, 0x00) => Self::Add,
f3f7!(R, 0x00, 0x20) => Self::Sub,
f3f7!(R, 0x04, 0x00) => Self::Xor,
f3f7!(R, 0x06, 0x00) => Self::Or,
f3f7!(R, 0x07, 0x00) => Self::And,
f3f7!(R, 0x01, 0x00) => Self::Sll,
f3f7!(R, 0x05, 0x00) => Self::Srl,
f3f7!(R, 0x05, 0x20) => Self::Sra,
f3f7!(R, 0x02, 0x00) => Self::Slt,
f3f7!(R, 0x03, 0x00) => Self::Sltu,
f3!(I, 0x00) => Self::Addi,
f3!(I, 0x04) => Self::Xori,
f3!(I, 0x06) => Self::Ori,
f3!(I, 0x07) => Self::Andi,
f3!(I, 0x01) => Self::Slli,
f3f7!(I, 0x05, 0x00) => Self::Srli,
f3f7!(I, 0x05, 0x20) => Self::Srai,
f3!(I, 0x02) => Self::Slti,
f3!(I, 0x03) => Self::Sltiu,
f3!(IL, 0x00) => Self::Lb,
f3!(IL, 0x01) => Self::Lh,
f3!(IL, 0x02) => Self::Lw,
f3!(IL, 0x04) => Self::Lbu,
f3!(IL, 0x05) => Self::Lhu,
f3!(S, 0x00) => Self::Sb,
f3!(S, 0x01) => Self::Sh,
f3!(S, 0x02) => Self::Sw,
f3!(B, 0x00) => Self::Beq,
f3!(B, 0x01) => Self::Bne,
f3!(B, 0x04) => Self::Blt,
f3!(B, 0x05) => Self::Bge,
f3!(B, 0x06) => Self::Bltu,
f3!(B, 0x07) => Self::Bgeu,
op!(J) => Self::Jal,
f3!(JI, 0x0) => Self::Jalr,
op!(U) => Self::Lui,
op!(UI) => Self::Auipc,
op!(E) => match ins.imm {
0x0 => Self::Ecall,
_ => Self::Ebreak,
},
_ => panic!("bad instruction"),
}
}
}
impl Instruction {
fn new(ins: u32) -> Self {
Self {
op: Format::new(ins),
rd: ((ins >> 7) & 0x1f) as u8,
f3: ((ins >> 12) & 0x7) as u8,
f7: ((ins >> 25) & 0x7f) as u8,
rs1: ((ins >> 15) & 0x1f) as u8,
rs2: ((ins >> 20) & 0x1f) as u8,
imm: ((ins & 0xfff00000) as i64 as i32 >> 20) as u32,
simm: (((ins & 0xfe000000) as i64 as i32 >> 20) as u32 | ((ins >> 7) & 0x1f)),
uimm: ins >> 12,
bimm: ((ins & 0x80000000) as i64 as i32 >> 19) as u32
| ((ins & 0x80) << 4)
| ((ins >> 20) & 0x7e0)
| ((ins >> 7) & 0x1e),
jimm: ((ins & 0x80000000) as i64 as i32 >> 11) as u32
| (ins & 0xff000)
| ((ins >> 9) & 0x800)
| ((ins >> 20) & 0x7fe),
}
}
fn exec_r(&self, cpu: &mut Cpu, op: fn(u32, u32) -> u32) {
let rs1 = cpu.reg_read(self.rs1);
let rs2 = cpu.reg_read(self.rs2);
cpu.reg_write(self.rd, op(rs1, rs2));
}
fn exec_i(&self, cpu: &mut Cpu, op: fn(u32, u32) -> u32) {
let rs1 = cpu.reg_read(self.rs1);
cpu.reg_write(self.rd, op(rs1, self.imm));
}
fn exec_il(&self, cpu: &mut Cpu, op: fn(u32) -> u32) {
let rs1 = cpu.reg_read(self.rs1);
let load = cpu.read(rs1.wrapping_add_signed(self.imm as i32));
cpu.reg_write(self.rd, op(load));
}
fn exec_s(&self, cpu: &mut Cpu, op: fn(u32) -> Store) {
let rs1 = cpu.reg_read(self.rs1);
let rs2 = cpu.reg_read(self.rs2);
cpu.write(rs1.wrapping_add_signed(self.simm as i32), op(rs2))
}
fn exec_b(&self, cpu: &mut Cpu, op: fn(u32, u32) -> bool) {
let rs1 = cpu.reg_read(self.rs1);
let rs2 = cpu.reg_read(self.rs2);
if op(rs1, rs2) {
cpu.pc = cpu.pc.wrapping_add_signed(self.bimm as i32) - 4;
}
}
fn execute(&self, cpu: &mut Cpu) {
match Opcode::new(self) {
Opcode::Add => self.exec_r(cpu, |r1, r2| r1.wrapping_add_signed(r2 as i32)),
Opcode::Sub => self.exec_r(cpu, |r1, r2| r1.wrapping_sub(r2)),
Opcode::Xor => self.exec_r(cpu, |r1, r2| r1 ^ r2),
Opcode::Or => self.exec_r(cpu, |r1, r2| r1 | r2),
Opcode::And => self.exec_r(cpu, |r1, r2| r1 & r2),
Opcode::Sll => self.exec_r(cpu, |r1, r2| r1.wrapping_shl(r2)),
Opcode::Srl => self.exec_r(cpu, |r1, r2| r1.wrapping_shr(r2)),
Opcode::Sra => self.exec_r(cpu, |r1, r2| r1.wrapping_shr(r2)),
Opcode::Slt => self.exec_r(cpu, |r1, r2| (r1 < (r2 as i32) as u32) as u32),
Opcode::Sltu => self.exec_r(cpu, |r1, r2| (r1 < r2) as u32),
Opcode::Addi => self.exec_i(cpu, |r1, imm| r1.wrapping_add_signed(imm as i32)),
Opcode::Xori => self.exec_i(cpu, |r1, imm| r1 ^ imm),
Opcode::Ori => self.exec_i(cpu, |r1, imm| r1 | imm),
Opcode::Andi => self.exec_i(cpu, |r1, imm| r1 & imm),
Opcode::Slli => self.exec_i(cpu, |r1, imm| r1.wrapping_shl(imm & 0x1f)),
Opcode::Srli => self.exec_i(cpu, |r1, imm| r1.wrapping_shr(imm & 0x1f)),
Opcode::Srai => self.exec_i(cpu, |r1, imm| r1.wrapping_shr(imm & 0x1f) as i32 as u32),
Opcode::Slti => self.exec_i(cpu, |r1, imm| (r1 < (imm as i32) as u32) as u32),
Opcode::Sltiu => self.exec_i(cpu, |r1, imm| (r1 < imm) as u32),
Opcode::Lb => self.exec_il(cpu, |imm| imm as u8 as u32),
Opcode::Lh => self.exec_il(cpu, |imm| imm as u16 as u32),
Opcode::Lw => self.exec_il(cpu, |imm| imm),
Opcode::Lbu => self.exec_il(cpu, |imm| imm as u8 as u32),
Opcode::Lhu => self.exec_il(cpu, |imm| imm as u16 as u32),
Opcode::Sb => self.exec_s(cpu, |r1| Store::Byte(r1 as u8)),
Opcode::Sh => self.exec_s(cpu, |r1| Store::Half(r1 as u16)),
Opcode::Sw => self.exec_s(cpu, Store::Word),
Opcode::Beq => self.exec_b(cpu, |r1, r2| (r1 as i32) == (r2 as i32)),
Opcode::Bne => self.exec_b(cpu, |r1, r2| (r1 as i32) != (r2 as i32)),
Opcode::Blt => self.exec_b(cpu, |r1, r2| (r1 as i32) < (r2 as i32)),
Opcode::Bge => self.exec_b(cpu, |r1, r2| (r1 as i32) >= (r2 as i32)),
Opcode::Bltu => self.exec_b(cpu, |r1, r2| r1 < r2),
Opcode::Bgeu => self.exec_b(cpu, |r1, r2| r1 >= r2),
Opcode::Jal => {
cpu.reg_write(self.rd, cpu.pc);
cpu.pc = cpu.pc.wrapping_add_signed(self.jimm as i32) - 4;
}
Opcode::Jalr => {
let pc = cpu.pc;
let rs1 = cpu.reg_read(self.rs1);
cpu.pc = rs1.wrapping_add_signed(self.imm as i32);
cpu.reg_write(self.rd, pc);
}
Opcode::Lui => cpu.reg_write(self.rd, self.uimm << 12),
Opcode::Auipc => cpu.reg_write(self.rd, cpu.pc.wrapping_add(self.uimm << 12) - 4),
Opcode::Ecall | Opcode::Ebreak => (),
}
}
}
impl Memory for Bus {
fn read(&self, addr: u32) -> u32 {
match addr {
RAM_BASE..=RAM_SIZE => self.ram.read(addr - RAM_BASE),
GPU_BASE..=GPU_SIZE => self.gpu.read(addr - GPU_BASE),
_ => panic!(),
}
}
fn write(&mut self, addr: u32, val: Store) {
match addr {
RAM_BASE..=RAM_SIZE => self.ram.write(addr - RAM_BASE, val),
GPU_BASE..=GPU_SIZE => self.gpu.write(addr - GPU_BASE, val),
_ => panic!(),
}
}
}
impl Cpu {
pub fn new(mem: Vec<u8>) -> Result<Self, elf::Error> {
let mut reg: [u32; 31] = [0; 31];
let mut ram = Ram::new();
// set sp to point to top of ram
reg[2] = RAM_SIZE;
let pc = elf::load(mem, &mut ram.mem)?;
Ok(Self {
reg,
pc,
bus: Bus {
ram: Box::new(ram),
gpu: Gpu::new(),
},
})
}
pub fn read(&self, addr: u32) -> u32 {
self.bus.read(addr)
}
fn write(&mut self, addr: u32, val: Store) {
self.bus.write(addr, val);
}
fn reg_read(&mut self, reg: u8) -> u32 {
let reg = reg as usize;
if reg >= self.reg.len() {
panic!("invalid register read");
}
self.reg[reg]
}
fn reg_write(&mut self, reg: u8, val: u32) {
let reg = reg as usize;
if reg >= self.reg.len() {
panic!("invalid register write");
}
self.reg[reg] = val;
self.reg[0] = 0
}
pub fn gpu_queue(&self) -> &Vec<Primitive> {
&self.bus.gpu.queue
}
pub fn gpu_clear(&mut self) {
self.bus.gpu.update = false;
self.bus.gpu.queue.clear();
}
pub fn update(&mut self) -> bool {
self.bus.gpu.update
}
pub fn step(&mut self) -> bool {
let inst = Instruction::new(self.read(self.pc));
self.pc += 4;
inst.execute(self);
if self.pc == 0 {
return false;
}
true
}
}