You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
404 lines
9.5 KiB
404 lines
9.5 KiB
/// xgayuh instruction set |
|
/// fuck it we ball |
|
/// |
|
/// |
|
/// 32 bit instruction width |
|
/// all instructions optional via state flag comparison |
|
/// mmapped i/o |
|
/// 16 registers |
|
/// 32bit address space |
|
/// |
|
use std::{ |
|
fmt::Display, |
|
ops::{Index, IndexMut}, |
|
}; |
|
|
|
#[derive(Clone, Copy, Debug)] |
|
pub enum Register { |
|
R0, |
|
R1, |
|
R2, |
|
R3, |
|
R4, |
|
R5, |
|
R6, |
|
R7, |
|
R8, |
|
R9, |
|
CS, |
|
CA, |
|
ST, |
|
IA, |
|
RA, |
|
SA, |
|
} |
|
|
|
const ALL_REGISTERS: [Register; 16] = [ |
|
Register::R0, |
|
Register::R1, |
|
Register::R2, |
|
Register::R3, |
|
Register::R4, |
|
Register::R5, |
|
Register::R6, |
|
Register::R7, |
|
Register::R8, |
|
Register::R9, |
|
Register::CS, |
|
Register::CA, |
|
Register::ST, |
|
Register::IA, |
|
Register::RA, |
|
Register::SA, |
|
]; |
|
|
|
impl TryFrom<u8> for Register { |
|
type Error = (); |
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> { |
|
use Register::*; |
|
|
|
match value { |
|
0b0000 => Ok(R0), |
|
0b0001 => Ok(R1), |
|
0b0010 => Ok(R2), |
|
0b0011 => Ok(R3), |
|
0b0100 => Ok(R4), |
|
0b0101 => Ok(R5), |
|
0b0110 => Ok(R6), |
|
0b0111 => Ok(R7), |
|
0b1000 => Ok(R8), |
|
0b1001 => Ok(R9), |
|
0b1010 => Ok(CS), |
|
0b1011 => Ok(CA), |
|
0b1100 => Ok(ST), |
|
0b1101 => Ok(IA), |
|
0b1110 => Ok(RA), |
|
0b1111 => Ok(SA), |
|
_ => Err(()), |
|
} |
|
} |
|
} |
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
|
pub struct State(pub u32); |
|
|
|
bitflags::bitflags! { |
|
impl State: u32 { |
|
const CMP_GT = 1; |
|
const CMP_EQ = 1 << 1; |
|
const CMP_LT = 1 << 2; |
|
const CMP_EN = 1 << 3; |
|
const RET_ST = 1 << 4; |
|
} |
|
} |
|
|
|
#[derive(Clone, Copy, Default, Debug)] |
|
pub struct CmpFlags { |
|
pub gt: bool, |
|
pub eq: bool, |
|
pub lt: bool, |
|
} |
|
|
|
impl CmpFlags { |
|
pub fn new<T: Ord>(a: T, b: T) -> Self { |
|
CmpFlags { |
|
gt: a > b, |
|
eq: a == b, |
|
lt: a < b, |
|
} |
|
} |
|
} |
|
|
|
impl Display for CmpFlags { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
write!( |
|
f, |
|
"CmpFlags({})", |
|
match (self.gt, self.eq, self.lt) { |
|
(true, true, true) => "ALL", |
|
(true, true, false) => "GE", |
|
(true, false, true) => "NE", |
|
(false, true, true) => "LE", |
|
(true, false, false) => "GT", |
|
(false, true, false) => "EQ", |
|
(false, false, true) => "LT", |
|
(false, false, false) => "NONE", |
|
}, |
|
) |
|
} |
|
} |
|
|
|
impl Into<State> for CmpFlags { |
|
fn into(self) -> State { |
|
self.gt.then_some(State::CMP_GT).unwrap_or(State(0)) |
|
| self.eq.then_some(State::CMP_EQ).unwrap_or(State(0)) |
|
| self.lt.then_some(State::CMP_LT).unwrap_or(State(0)) |
|
} |
|
} |
|
|
|
impl Default for State { |
|
fn default() -> Self { |
|
Self::CMP_EQ | Self::CMP_GT | Self::CMP_LT |
|
} |
|
} |
|
|
|
impl State { |
|
pub fn set_cmp(&mut self, new: CmpFlags) { |
|
self.set(Self::CMP_GT, new.gt); |
|
self.set(Self::CMP_EQ, new.eq); |
|
self.set(Self::CMP_LT, new.lt); |
|
} |
|
|
|
pub fn get_cmp(&self) -> CmpFlags { |
|
CmpFlags { |
|
gt: self.contains(Self::CMP_GT), |
|
eq: self.contains(Self::CMP_EQ), |
|
lt: self.contains(Self::CMP_LT), |
|
} |
|
} |
|
} |
|
|
|
#[repr(C)] |
|
#[derive(Default)] |
|
pub struct RegisterFile { |
|
pub r0: u32, |
|
pub r1: u32, |
|
pub r2: u32, |
|
pub r3: u32, |
|
pub r4: u32, |
|
pub r5: u32, |
|
pub r6: u32, |
|
pub r7: u32, |
|
pub r8: u32, |
|
pub r9: u32, |
|
/// call stack |
|
pub cs: u32, |
|
/// "carry", used by the alu for extended results |
|
pub ca: u32, |
|
/// "state", internal state and alu flags |
|
pub st: State, |
|
/// instruction address |
|
pub ia: u32, |
|
/// return address |
|
pub ra: u32, |
|
/// stack address |
|
pub sa: u32, |
|
} |
|
|
|
impl Index<u8> for RegisterFile { |
|
type Output = u32; |
|
|
|
fn index(&self, index: u8) -> &Self::Output { |
|
assert!(index < 16, "there's only 16 registers"); |
|
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.r8, |
|
9 => &self.r9, |
|
10 => &self.cs, |
|
11 => &self.ca, |
|
12 => &self.st.0, |
|
13 => &self.ia, |
|
14 => &self.ra, |
|
15 => &self.sa, |
|
_ => unreachable!(), |
|
} |
|
} |
|
} |
|
|
|
impl IndexMut<u8> for RegisterFile { |
|
fn index_mut(&mut self, index: u8) -> &mut Self::Output { |
|
assert!(index < 16, "there's only 16 registers"); |
|
match index { |
|
0 => &mut self.r0, |
|
1 => &mut self.r1, |
|
2 => &mut self.r2, |
|
3 => &mut self.r3, |
|
4 => &mut self.r4, |
|
5 => &mut self.r5, |
|
6 => &mut self.r6, |
|
7 => &mut self.r7, |
|
8 => &mut self.r8, |
|
9 => &mut self.r9, |
|
10 => &mut self.cs, |
|
11 => &mut self.ca, |
|
12 => &mut self.st.0, |
|
13 => &mut self.ia, |
|
14 => &mut self.ra, |
|
15 => &mut self.sa, |
|
_ => unreachable!(), |
|
} |
|
} |
|
} |
|
|
|
impl Index<Register> for RegisterFile { |
|
type Output = u32; |
|
|
|
fn index(&self, index: Register) -> &Self::Output { |
|
use Register::*; |
|
|
|
match index { |
|
R0 => &self.r0, |
|
R1 => &self.r1, |
|
R2 => &self.r2, |
|
R3 => &self.r3, |
|
R4 => &self.r4, |
|
R5 => &self.r5, |
|
R6 => &self.r6, |
|
R7 => &self.r7, |
|
R8 => &self.r8, |
|
R9 => &self.r9, |
|
CS => &self.cs, |
|
CA => &self.ca, |
|
ST => &self.st.0, |
|
IA => &self.ia, |
|
RA => &self.ra, |
|
SA => &self.sa, |
|
} |
|
} |
|
} |
|
|
|
impl IndexMut<Register> for RegisterFile { |
|
fn index_mut(&mut self, index: Register) -> &mut Self::Output { |
|
use Register::*; |
|
|
|
match index { |
|
R0 => &mut self.r0, |
|
R1 => &mut self.r1, |
|
R2 => &mut self.r2, |
|
R3 => &mut self.r3, |
|
R4 => &mut self.r4, |
|
R5 => &mut self.r5, |
|
R6 => &mut self.r6, |
|
R7 => &mut self.r7, |
|
R8 => &mut self.r8, |
|
R9 => &mut self.r9, |
|
CS => &mut self.cs, |
|
CA => &mut self.ca, |
|
ST => &mut self.st.0, |
|
IA => &mut self.ia, |
|
RA => &mut self.ra, |
|
SA => &mut self.sa, |
|
} |
|
} |
|
} |
|
|
|
impl Display for RegisterFile { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
let mut out = String::new(); |
|
out += "RegisterFile {\n"; |
|
for regs in ALL_REGISTERS.chunks(4) { |
|
for r in regs { |
|
out += format!(" {:?}: {:#010x}", r, self[*r]).as_str(); |
|
} |
|
out += "\n"; |
|
} |
|
out += "};"; |
|
write!(f, "{}", out) |
|
} |
|
} |
|
|
|
#[derive(Clone, Copy, Debug)] |
|
pub struct Instruction { |
|
pub cmp: CmpFlags, |
|
pub sign: bool, |
|
pub var: InstVariant, |
|
} |
|
|
|
impl Instruction { |
|
pub fn has_upcoming_value(&self) -> bool { |
|
use InstVariant::*; |
|
use InstructionData::*; |
|
match self.var { |
|
// this might be the worst match i've ever written |
|
Set | Rst | Ret | Nop | Halt => false, |
|
Add(id) | Mul(id) | And(id) | Or(id) | Xor(id) | Not(id) | Cmp(id) | Lsl(id) |
|
| Lsr(id) | Asr(id) | Ror(id) | Ld(id) | Sto(id) | Push(id) | Pop(id) | Mov(id) |
|
| Call(id) | Jmp(id) | JmpAbs(id) => match id { |
|
RegisterAndU32(_, _) | TwoRegistersAndU32(_, _, _) | ImmediateOnly(_) => true, |
|
_ => false, |
|
}, |
|
} |
|
} |
|
} |
|
|
|
impl Display for Instruction { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
write!( |
|
f, |
|
"Instruction({} {} {:?})", |
|
self.cmp, |
|
if self.sign { "Neg" } else { "Pos" }, |
|
self.var |
|
) |
|
} |
|
} |
|
|
|
#[derive(Default, Clone, Copy, Debug)] |
|
/// first 5 bits: instruction variant |
|
/// last 16 bits: decode into instruction data |
|
/// bits 6-8: cmp bits, gt eq lt |
|
/// 9-12: 4 bits of cozy padding |
|
/// 13: sign bit |
|
/// 14-16: instruction data variant |
|
pub enum InstVariant { |
|
// data processing |
|
Add(InstructionData), |
|
Mul(InstructionData), |
|
And(InstructionData), |
|
Or(InstructionData), |
|
Xor(InstructionData), |
|
Not(InstructionData), |
|
|
|
// comparison, branch control |
|
Cmp(InstructionData), |
|
Set, |
|
Rst, |
|
|
|
// shifts |
|
Lsl(InstructionData), |
|
Lsr(InstructionData), |
|
Asr(InstructionData), |
|
Ror(InstructionData), |
|
|
|
// Load and store and move |
|
Ld(InstructionData), |
|
Sto(InstructionData), |
|
Push(InstructionData), |
|
Pop(InstructionData), |
|
Mov(InstructionData), |
|
|
|
// subroutine and jumps |
|
Call(InstructionData), |
|
Ret, |
|
Jmp(InstructionData), |
|
JmpAbs(InstructionData), |
|
|
|
// misc |
|
#[default] |
|
Nop, |
|
Halt, |
|
} |
|
|
|
/// a marker to make the cpu read the next 4 bytes from the program code as a literal value |
|
pub type UpcomingU32 = (); |
|
|
|
// up to 16 bits large |
|
#[derive(Clone, Copy, Debug)] |
|
pub enum InstructionData { |
|
OneRegister(Register), |
|
TwoRegisters(Register, Register), |
|
ThreeRegisters(Register, Register, Register), |
|
RegisterAndU8(Register, u8), |
|
RegisterAndU32(Register, UpcomingU32), |
|
TwoRegistersAndU8(Register, Register, u8), |
|
TwoRegistersAndU32(Register, Register, UpcomingU32), |
|
ImmediateOnly(UpcomingU32), |
|
}
|
|
|