Begin work on common code for better sharing between the assembler, disassembler, and emulator

main
John Zacarias Jekel 1 year ago
parent efef8f8889
commit 674509d817
  1. 6
      README.md
  2. 36
      lib/asm.rs
  3. 36
      lib/decode.rs
  4. 36
      lib/disasm.rs
  5. 203
      lib/encode.rs
  6. 94
      lib/lib.rs
  7. 47
      src/legv8assemble.rs

@ -59,7 +59,7 @@ You can now type in a LEGv8 instruction you'd like to assemble, for example:
```
legv8assemble> movz x1, 1234, lsl 48
The instruction "MOVZ X1, 1234, LSL 48" is IM-type
The instruction "MOVZ X1, 1234, LSL 48" is IW-type
________________________________________________
| 9 | 2 | 16 | 5 | <- Field length in bits
|----------------------------------------------|
@ -123,11 +123,11 @@ legv8assemble> ^C
$
```
## Disassembler
## Using the Disassembler
Coming soon!
## Emulator
## Using the Emulator
Coming soon!

@ -0,0 +1,36 @@
/* asm.rs
* By: John Jekel
* Copyright (C) 2023 John Jekel
* See the LICENSE file at the root of the project for licensing info.
*
* Human-readable text -> DecodedInstruction
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
//TODO
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -0,0 +1,36 @@
/* encode.rs
* By: John Jekel
* Copyright (C) 2023 John Jekel
* See the LICENSE file at the root of the project for licensing info.
*
* Raw 32-bit LEGv8 instruction -> DecodedInstruction
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
//TODO
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -0,0 +1,36 @@
/* disasm.rs
* By: John Jekel
* Copyright (C) 2023 John Jekel
* See the LICENSE file at the root of the project for licensing info.
*
* DecodedInstruction -> human readable text
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
//TODO
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -0,0 +1,203 @@
/* encode.rs
* By: John Jekel
* Copyright (C) 2023 John Jekel
* See the LICENSE file at the root of the project for licensing info.
*
* Decoded LEGv8 instruction -> actual 32-bit raw instruction
*
*/
/* Imports */
use crate::{DecodedOpcode, DecodedInstruction, ConvenientlyBitAccessible};
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
//TODO
/* Associated Functions and Methods */
//TODO
/* Functions */
pub fn encode(decoded_instruction: DecodedInstruction) -> Option<u32> {
//Decode the opcode first
let raw_opcode = encode_opcode(decoded_instruction)?;
let mut raw_instruction = 0u32;
match decoded_instruction {
DecodedInstruction::R{rm, shamt, rn, rd, ..} => {
debug_assert!(raw_opcode <= 0b11111111111);
raw_instruction.set_bits(raw_opcode.into(), 31, 21);
if rm > 31 { return None; }
raw_instruction.set_bits(rm.into(), 20, 16);
if shamt > 63 { return None; }
raw_instruction.set_bits(shamt.into(), 15, 10);
if rn > 31 { return None; }
raw_instruction.set_bits(rn.into(), 9, 5);
if rd > 31 { return None; }
raw_instruction.set_bits(rd.into(), 4, 0);
},
DecodedInstruction::I{imm12, rn, rd, ..} => {
debug_assert!(raw_opcode <= 0b1111111111);
raw_instruction.set_bits(raw_opcode.into(), 31, 22);
if imm12 > 0b111111111111 { return None; }
raw_instruction.set_bits(imm12.into(), 21, 10);
if rn > 31 { return None; }
raw_instruction.set_bits(rn.into(), 9, 5);
if rd > 31 { return None; }
raw_instruction.set_bits(rd.into(), 4, 0);
},
DecodedInstruction::D{addr9, op2, rn, rt, ..} => {
debug_assert!(raw_opcode <= 0b11111111111);
raw_instruction.set_bits(raw_opcode.into(), 31, 21);
if addr9 > 0b111111111 { return None; }
raw_instruction.set_bits(addr9.into(), 20, 12);
if op2 > 0b11 { return None; }
raw_instruction.set_bits(op2.into(), 11, 10);
if rn > 31 { return None; }
raw_instruction.set_bits(rn.into(), 9, 5);
if rt > 31 { return None; }
raw_instruction.set_bits(rt.into(), 4, 0);
},
DecodedInstruction::B{addr26, ..} => {
debug_assert!(raw_opcode <= 0b111111);
raw_instruction.set_bits(raw_opcode.into(), 31, 26);
if addr26 > 0b11111111111111111111111111 { return None; }
raw_instruction.set_bits(addr26.into(), 25, 0);
},
DecodedInstruction::CB{addr19, rt, ..} => {
debug_assert!(raw_opcode <= 0b11111111);
raw_instruction.set_bits(raw_opcode.into(), 31, 24);
if addr19 > 0b1111111111111111111 { return None; }
raw_instruction.set_bits(addr19.into(), 23, 5);
if rt > 31 { return None; }
raw_instruction.set_bits(rt.into(), 4, 0);
},
DecodedInstruction::IW{lsl, imm16, rd, ..} => {
debug_assert!(raw_opcode <= 0b111111111);
raw_instruction.set_bits(raw_opcode.into(), 31, 23);
raw_instruction.set_bits(
match lsl {
0 => { 0 },
16 => { 1 },
32 => { 2 },
48 => { 3 }
_ => { return None; }
},
22, 21
);
raw_instruction.set_bits(imm16.into(), 20, 5);
if rd > 31 { return None; }
raw_instruction.set_bits(rd.into(), 4, 0);
}
}
return Some(raw_instruction);
}
pub fn encode_opcode(decoded_instruction: DecodedInstruction) -> Option<u16> {
match decoded_instruction {
DecodedInstruction::R{opcode, ..} => {
//Opcode is 11 bits
match opcode {
DecodedOpcode::ADD => { return Some(0b10001011000); },
DecodedOpcode::SUB => { return Some(0b11001011000); },
DecodedOpcode::ADDS => { return Some(0b10101011000); },
DecodedOpcode::SUBS => { return Some(0b11101011000); },
DecodedOpcode::AND => { return Some(0b10001010000); },
DecodedOpcode::ORR => { return Some(0b10101010000); },
DecodedOpcode::EOR => { return Some(0b11001010000); },
DecodedOpcode::LSL => { return Some(0b11010011011); },
DecodedOpcode::LSR => { return Some(0b11010011010); },
DecodedOpcode::BR => { return Some(0b11010110000); }
_ => { return None; }
}
},
DecodedInstruction::D{opcode, ..} => {
//Opcode is 11 bits
match opcode {
DecodedOpcode::LDUR => { return Some(0b11111000010); },
DecodedOpcode::STUR => { return Some(0b11111000000); },
DecodedOpcode::LDURSW => { return Some(0b10111000100); },
DecodedOpcode::STURW => { return Some(0b10111000000); },
DecodedOpcode::LDURH => { return Some(0b01111000010); },
DecodedOpcode::STURH => { return Some(0b01111000000); },
DecodedOpcode::LDURB => { return Some(0b00111000010); },
DecodedOpcode::STURB => { return Some(0b00111000000); },
DecodedOpcode::LDXR => { return Some(0b11001000010); },
DecodedOpcode::STXR => { return Some(0b11001000000); },
_ => { return None; }
}
},
DecodedInstruction::I{opcode, ..} => {
//Opcode is 10 bits
match opcode {
DecodedOpcode::ADDI => { return Some(0b1001000100); },
DecodedOpcode::SUBI => { return Some(0b1101000100); },
DecodedOpcode::ADDIS => { return Some(0b1011000100); },
DecodedOpcode::SUBIS => { return Some(0b1111000100); },
DecodedOpcode::ANDI => { return Some(0b1001001000); },
DecodedOpcode::ORRI => { return Some(0b1011001000); },
DecodedOpcode::EORI => { return Some(0b1101001000); },
_ => { return None; }
}
},
DecodedInstruction::IW{opcode, ..} => {
//Opcode is 9 bits
match opcode {
DecodedOpcode::MOVZ => { return Some(0b110100101); },
DecodedOpcode::MOVK => { return Some(0b111100101); },
_ => { return None; }
}
},
DecodedInstruction::CB{opcode, ..} => {
//Opcode is 8 bits
match opcode {
DecodedOpcode::CBZ => { return Some(0b10110100); },
DecodedOpcode::CBNZ => { return Some(0b10110101); },
DecodedOpcode::B_cond => { return Some(0b01010100); },
_ => { return None; }
}
},
DecodedInstruction::B{opcode, ..} => {
//Opcode is 6 bits
match opcode {
DecodedOpcode::B => { return Some(0b000101); },
DecodedOpcode::BL => { return Some(0b100101); },
_ => { return None; }
}
}
}
}

@ -9,7 +9,10 @@
/* Imports */
//TODO (include "use" and "mod" here)
pub mod asm;
pub mod disasm;
pub mod decode;
pub mod encode;
/* Constants */
@ -25,18 +28,93 @@
/* Types */
pub enum InstructionType {
R,
I,
D,
#[derive(Copy, Clone, Debug)]
pub enum DecodedInstruction {
R{opcode: DecodedOpcode, rm: u8, shamt: u8, rn: u8, rd: u8},
I{opcode: DecodedOpcode, imm12: u16, rn: u8, rd: u8},
D{opcode: DecodedOpcode, addr9: u16, op2: u8, rn: u8, rt: u8},
B{opcode: DecodedOpcode, addr26: u32},
CB{opcode: DecodedOpcode, addr19: u32, rt: u8},
IW{opcode: DecodedOpcode, lsl: u8, imm16: u16, rd: u8}//NOTE lsl is not 0, 1, 2, or 3, it is 0, 16, 32, or 64 (since that is faster than having to translate it each time, at the expense of slowing down encode)
}
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum DecodedOpcode {
ADD,
SUB,
ADDI,
SUBI,
ADDS,
SUBS,
ADDIS,
SUBIS,
LDUR,
STUR,
LDURSW,
STURW,
LDURH,
STURH,
LDURB,
STURB,
LDXR,
STXR,
MOVZ,
MOVK,
AND,
ORR,
EOR,
ANDI,
ORRI,
EORI,
LSL,
LSR,
CBZ,
CBNZ,
B_cond,
B,
CB,
IM
BR,
BL
}
pub trait ConvenientlyBitAccessible: Sized {
fn get_bit(self: Self, index: u8) -> Self {
return self.get_bits(index, index);
}
fn get_bits(self: Self, high: u8, low: u8) -> Self;
fn set_bits(self: &mut Self, value: Self, high: u8, low: u8);
}
/* Associated Functions and Methods */
//TODO
impl ConvenientlyBitAccessible for u32 {
fn get_bits(self: Self, high: u8, low: u8) -> u32 {
debug_assert!(high < 32);
debug_assert!(low < 32);
debug_assert!(low <= high);
let num_bits_to_keep = high - low + 1;
let mask = (1 << num_bits_to_keep) - 1;
let unmasked_value = self >> low;
return unmasked_value & mask;
}
fn set_bits(self: &mut Self, value: Self, high: u8, low: u8) {
debug_assert!(high < 32);
debug_assert!(low < 32);
debug_assert!(low <= high);
let num_bits_to_change = high - low + 1;
let mask_unshifted = (1 << num_bits_to_change) - 1;
let mask = mask_unshifted << low;
debug_assert!(value <= mask_unshifted);
let shifted_value = value << low;
*self = (*self & !mask) | shifted_value;
}
}
/* Functions */

@ -11,7 +11,7 @@
/* Imports */
use legv8::InstructionType;
use legv8::ConvenientlyBitAccessible;
/* Constants */
@ -27,44 +27,19 @@ use legv8::InstructionType;
/* Types */
trait ConvenientlyBitAccessible: Sized {
fn get_bit(self: Self, index: u8) -> Self {
return self.get_bits(index, index);
}
fn get_bits(self: Self, high: u8, low: u8) -> Self;
fn set_bits(self: &mut Self, value: Self, high: u8, low: u8);
#[derive(Copy, Clone, Debug)]
pub enum InstructionType {//TODO remove this
R,
I,
D,
B,
CB,
IM
}
/* Associated Functions and Methods */
impl ConvenientlyBitAccessible for u32 {
fn get_bits(self: Self, high: u8, low: u8) -> u32 {
debug_assert!(high < 32);
debug_assert!(low < 32);
debug_assert!(low <= high);
let num_bits_to_keep = high - low + 1;
let mask = (1 << num_bits_to_keep) - 1;
let unmasked_value = self >> low;
return unmasked_value & mask;
}
fn set_bits(self: &mut Self, value: Self, high: u8, low: u8) {
debug_assert!(high < 32);
debug_assert!(low < 32);
debug_assert!(low <= high);
let num_bits_to_change = high - low + 1;
let mask_unshifted = (1 << num_bits_to_change) - 1;
let mask = mask_unshifted << low;
debug_assert!(value <= mask_unshifted);
let shifted_value = value << low;
*self = (*self & !mask) | shifted_value;
}
}
//TODO
/* Functions */
@ -169,7 +144,7 @@ fn main() {
eprintln!(" ------------------------------------------");
},
InstructionType::IM => {
eprintln!(" The instruction \"\x1b[1m{}\x1b[0m\" is \x1b[94mIM\x1b[0m-type", nice_line);
eprintln!(" The instruction \"\x1b[1m{}\x1b[0m\" is \x1b[94mIW\x1b[0m-type", nice_line);
eprintln!(" ________________________________________________");
eprintln!(" | \x1b[93m9\x1b[0m | \x1b[93m2\x1b[0m | \x1b[93m16\x1b[0m | \x1b[93m5\x1b[0m | \x1b[90m<- Field length in bits\x1b[0m");
eprintln!(" |----------------------------------------------|");