Commit eabb0b16 authored by Arthur Carlsson's avatar Arthur Carlsson

Refactored code, removed unused code and implemented some basic instruction decode

parent 2b8187e7
Pipeline #7509620 passed with stage
in 2 minutes and 7 seconds
......@@ -6,4 +6,4 @@ authors = ["Arthur Carlsson <arthur@kiron.net>"]
[dependencies]
clap = "2.10"
byteorder = "0.5"
bitflags = "0.7"
bitflags = "0.8"
use types::Word;
use mem::{VirtualAddr, PhysicalAddr};
use super::reg;
use super::instruction;
use super::mem;
pub struct Cpu {
regs: reg::Regs
}
impl Cpu {
pub fn new() -> Cpu {
Cpu {
regs: reg::Regs::new()
}
}
pub fn power_on_reset(&mut self) {
self.regs.pc = 0xFFFF_FFFF_BFC0_0000;
self.regs.status -= reg::STATUS_DS_TS | reg::STATUS_DS_SR | reg::STATUS_RP;
self.regs.config -= reg::CONFIG_EP;
self.regs.status |= reg::STATUS_ERL | reg::STATUS_DS_BEV;
self.regs.config |= reg::CONFIG_BE;
}
pub fn fetch(&self, mem: &mem::Reader) -> u32 {
instruction::fetch(self.regs.pc, mem)
}
pub fn decode(&self, word: u32) -> Result<instruction::Instruction, instruction::InstructionError> {
instruction::decode(word)
}
pub fn execute(&mut self, instr: &instruction::Instruction) {
println!("{:?}", instr);
self.regs.pc += 4;
}
pub fn virt_addr_to_phys_addr(&self, vaddr: u64) -> u32 {
if self.regs.status.is_kernel_mode_address_segment() {
// Kernel mode segment as per "Table 5-4 64-Bit Kernel Mode Segments"
match vaddr >> 62 {
0b11 if (vaddr >> 31) == 0x1_ffff_ffff => match vaddr {
// ckseg0
0xFFFF_FFFF_8000_0000...0xFFFF_FFFF_9FFF_FFFF => (vaddr - 0xFFFF_FFFF_8000_0000) as u32,
// ckseg1
0xFFFF_FFFF_A000_0000...0xFFFF_FFFF_BFFF_FFFF => (vaddr - 0xFFFF_FFFF_A000_0000) as u32,
_ => panic!("Unmapped 0b11 address: 0x{:X}", vaddr)
},
_ => panic!("Unmapped address: 0x{:X}", vaddr)
}
} else if self.regs.status.is_supervisor_mode_address_segments() {
// Supervisor mode segment as per "Table 5-2 32-Bit and 64-Bit Supervisor Mode Segments"
panic!("Supervisor mode not implemented")
} else {
panic!("Unknown address mode")
}
}
}
#[cfg(test)]
mod tests {
}
use super::mem::Mem;
use super::mem;
// NAMN, HANDLER, DISASSEMBER, OPCODE
macro_rules! oper {
($t:ident; $e:expr; $ub:expr, $lb:expr) => ((((0xffffffffu32 >> (31 - $ub)) & $e) >> $lb) as $t)
}
macro_rules! special_instr {
($e:expr; $n:ident) => (
$n { rs: oper![u8; $e; 25, 21], rt: oper![u8; $e; 20, 16], rd: oper![u8; $e; 15, 11] }
)
}
#[derive(Debug, PartialEq)]
pub enum InstructionError {
UnknownInstruction(u8),
UnknownSpecialInstruction(u8)
}
struct Opcode(u32, Box<Fn(u32, &Mem)>);
#[derive(Debug, PartialEq)]
pub enum Instruction {
ADD { rs: u8, rt: u8, rd: u8 },
ADDU { rs: u8, rt: u8, rd: u8 },
ADDI { rs: u8, rt: u8, immediate: u16 },
ADDIU { rs: u8, rt: u8, immediate: u16 },
DMTC0 { rt: u8, rd: u8 },
LUI { rt: u8, immediate: u16 }
}
struct OpcodeTable {
opcodes: Vec<Opcode>
pub fn fetch(pc: u64, mem: &mem::Reader) -> u32 {
mem.readw(pc)
}
impl OpcodeTable {
fn new() -> OpcodeTable {
OpcodeTable {
opcodes: vec![
Opcode(0x0000, Box::new(|opcode, mem| println!("asd")))
]
}
pub fn decode(instr: u32) -> Result<Instruction, InstructionError> {
use self::Instruction::*;
use self::InstructionError::*;
match instr >> 26 {
// SPECIAL
0b000000 => {
match instr & 0b111111 {
0b100000 => Ok(special_instr!(instr; ADD)),
0b100001 => Ok(special_instr!(instr; ADDU)),
i => Err(UnknownSpecialInstruction(i as u8))
}
},
0b001000 => Ok(ADDI { rs: oper![u8; instr; 25, 21], rt: oper![u8; instr; 20, 16], immediate: oper![u16; instr; 15, 0] }),
0b001001 => Ok(ADDIU { rs: oper![u8; instr; 25, 21], rt: oper![u8; instr; 20, 16], immediate: oper![u16; instr; 15, 0] }),
0b010000 => Ok(DMTC0 { rt: oper![u8; instr; 20, 16], rd: oper![u8; instr; 15, 11] }),
0b001111 => Ok(LUI { rt: oper![u8; instr; 20, 16], immediate: oper![u16; instr; 15, 0] }),
i => Err(UnknownInstruction(i as u8))
}
}
#[cfg(test)]
mod tests {
use super::*;
use self::Instruction::*;
use self::InstructionError::*;
#[test]
fn test_oper() {
assert_eq!(0b001001, oper!(u8; 0x24ffffff; 31, 26));
assert_eq!(0xdead, oper!(u16; 0x0000dead; 15, 0));
}
#[test]
fn test_decode_instr() {
assert_eq!(Ok(ADDIU { rs: 0b10101, rt: 0b00110, immediate: 0b0000111100001111 }), decode(0b001001_10101_00110_0000111100001111));
}
#[test]
fn test_decode_special_instr() {
assert_eq!(Ok(ADD { rs: 0b10101, rt: 0b00110, rd: 0b11001 }), decode(0b000000_10101_00110_11001_00000_100000));
}
fn decode(&self, instr: u32) -> &Opcode {
&self.opcodes.iter().find(|opcode| (opcode.0 & instr) == opcode.0).unwrap()
#[test]
fn test_decode_no_match() {
assert_eq!(Err(UnknownInstruction(0b111111)), decode(0xffffffff));
}
}
pub trait Mem {
fn read_word(&self, addr: u32) -> u32;
pub trait Reader {
fn readw(&self, addr: u64) -> u32;
}
pub trait Writer {
fn write(&mut self, addr: u64, byte: u8);
fn write_byte(&mut self, addr: u32, data: u8);
}
mod cpu;
mod instruction;
mod reg;
mod mem;
pub mod mem;
pub use self::cpu::Cpu;
//pub use mem::Mem;
pub enum GPR {
T1 = 9
bitflags! {
pub flags Status: u32 {
const STATUS_IE = 1 << 0,
const STATUS_EXL = 1 << 1,
const STATUS_ERL = 1 << 2,
const STATUS_KSU = 0b11 << 3,
const STATUS_UX = 1 << 5,
const STATUS_SX = 1 << 6,
const STATUS_KX = 1 << 7,
const STATUS_IM = 0b1111_1111 << 8,
const STATUS_DS_DE = 1 << 15,
const STATUS_DS_CE = 1 << 16,
const STATUS_DS_CH = 1 << 17,
// ... 0
const STATUS_DS_SR = 1 << 19,
const STATUS_DS_TS = 1 << 20,
const STATUS_DS_BEV = 1 << 21,
// ... 0
const STATUS_DS_ITS = 1 << 23,
const STATUS_RE = 1 << 24,
const STATUS_FR = 1 << 25,
const STATUS_RP = 1 << 26,
const STATUS_CU = 0b1111 << 27,
const STATUS_DS = STATUS_DS_DE.bits
| STATUS_DS_CE.bits
| STATUS_DS_CH.bits
| STATUS_DS_SR.bits
| STATUS_DS_TS.bits
| STATUS_DS_BEV.bits
| STATUS_DS_ITS.bits,
}
}
impl Status {
// Kernel mode segment as per "Table 5-4 64-Bit Kernel Mode Segments"
pub fn is_kernel_mode_address_segment(&self) -> bool {
(!self.contains(STATUS_KSU) || self.intersects(STATUS_EXL | STATUS_ERL)) && self.contains(STATUS_KX)
}
// Supervisor mode segment as per "Table 5-2 32-Bit and 64-Bit Supervisor Mode Segments"
pub fn is_supervisor_mode_address_segments(&self) -> bool {
(*self & STATUS_KSU).bits == 0b01000 && !self.intersects(STATUS_EXL | STATUS_ERL)
}
}
bitflags! {
pub flags Config: u32 {
const CONFIG_K0 = 0b111 << 0,
const CONFIG_CU = 1 << 3,
// ... 1100100110
const CONFIG_BE = 1 << 15,
// ... 00000110
const CONFIG_EP = 0b1111 << 24,
const CONFIG_EC = 0b111 << 28,
// ... 0
}
}
pub struct Regs {
pub pc: u64,
pub status: Status,
pub config: Config
}
impl Regs {
pub fn new() -> Regs {
Regs {
pc: 0xcafebabe,
status: Status::from_bits_truncate(0xcafebabe),
config: Config::from_bits_truncate(0xcafebabe)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_kernel_mode_address_segment() {
assert!((STATUS_EXL | STATUS_ERL | STATUS_KX).is_kernel_mode_address_segment());
}
#[test]
fn test_is_kernel_mode_address_segment_not_matching() {
assert!(!Status::empty().is_kernel_mode_address_segment());
}
#[test]
fn test_is_supervisor_mode_segments_sx_0() {
assert!(Status::from_bits_truncate(0b1000).is_supervisor_mode_address_segments());
}
#[test]
fn test_is_supervisor_mode_segments_sx_1() {
assert!(Status::from_bits_truncate(0b1001000).is_supervisor_mode_address_segments());
}
}
......@@ -8,7 +8,5 @@ mod rom;
mod pif_rom;
pub mod n64;
pub mod cpu;
pub mod mem;
pub use types::Word;
pub use pif_rom::PifRom;
......@@ -37,12 +37,9 @@ fn main() {
println!("Running {}", cart.title());
let mut n64 = n64::N64::new(pif_rom);
n64.insert_cart(&cart);
n64.insert_cart(cart);
n64.power_on();
loop {
n64.step();
}
n64.start()
}
fn read_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
......
use std::ops::Range;
use mem::PhysicalAddr;
pub struct MMap<T> {
entries: Vec<Entry<T>>
}
struct Entry<T> {
addr_space: Range<PhysicalAddr>,
func: Box<Fn(PhysicalAddr) -> T>
}
impl<T> MMap<T> {
pub fn new(entries: Vec<(Range<PhysicalAddr>, Box<Fn(PhysicalAddr) -> T>)>) -> MMap<T> {
MMap {
entries: entries.into_iter().map(|t| Entry { addr_space: t.0, func: t.1 }).collect()
}
}
pub fn map_addr(&self, addr: PhysicalAddr) -> Result<T, String> {
match self.entries.iter().map(|e| e.map_addr(addr)).find(|e| e.is_some()).map(|e| e.unwrap()) {
Some(addr) => Ok(addr),
None => Err(format!("Could not match address: {}", addr))
}
}
}
impl<T> Entry<T> {
fn map_addr(&self, addr: PhysicalAddr) -> Option<T> {
if self.addr_space.start <= addr && addr < self.addr_space.end {
Some((self.func)(addr - self.addr_space.start))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use mem::PhysicalAddr;
#[derive(Debug, PartialEq)]
enum MyAddr {
A(PhysicalAddr)
}
fn mmap() -> MMap<MyAddr> {
MMap::new(vec![
(1234..5678, Box::new(MyAddr::A))
])
}
#[test]
fn test_read_word() {
assert_eq!(Ok(MyAddr::A(6)), mmap().map_addr(1240));
}
#[test]
fn test_read_word_no_match() {
assert!(mmap().map_addr(11000).is_err());
}
#[test]
fn test_read_word_address_map() {
assert_eq!(Ok(MyAddr::A(1)), mmap().map_addr(1235));
}
#[test]
fn test_read_word_edge() {
assert!(mmap().map_addr(5678).is_err());
assert_eq!(Ok(MyAddr::A(0)), mmap().map_addr(1234));
}
}
mod mmap;
pub use self::mmap::MMap;
pub type PhysicalAddr = u64;
pub type VirtualAddr = u64;
use mem::{PhysicalAddr, MMap};
pub enum Addr {
PifRom(PhysicalAddr),
CartDom1(PhysicalAddr)
}
pub struct Bus {
mem_map: MMap<Addr>
}
impl Bus {
pub fn new() -> Bus {
Bus {
mem_map: MMap::new(vec![
(0x1fc0_0000..0x1fc0_07bf, Box::new(Addr::PifRom)),
(0x1000_0000..0x1fc0_0000, Box::new(Addr::CartDom1))
])
}
}
pub fn map_addr(&self, addr: PhysicalAddr) -> Result<Addr, String> {
self.mem_map.map_addr(addr)
}
}
use byteorder::BigEndian;
use rom::Rom;
use types::Word;
use mem::{VirtualAddr};
pub struct Cart {
rom: Rom<BigEndian>
......@@ -28,7 +26,7 @@ impl Cart {
}
impl Cart {
pub fn read_word(&self, addr: VirtualAddr) -> Word {
self.rom.read_word(addr as usize)
pub fn read_word(&self, addr: u32) -> u32 {
self.rom.read_word(addr)
}
}
trait Memory {
}
mod cart;
mod n64;
mod bus;
pub use self::cart::Cart;
pub use self::n64::N64;
use cpu::Cpu;
use mem::PhysicalAddr;
use pif_rom::PifRom;
use types::Word;
use cpu;
use super::bus::{Bus, Addr};
use super::cart::Cart;
pub struct N64<'a> {
pub struct N64 {
cpu: Cpu,
bus: Bus,
pif_rom: PifRom,
cart: Option<&'a Cart>
cart: Option<Box<Cart>>
}
impl<'a> N64<'a> {
pub fn new(pif_rom: PifRom) -> N64<'a> {
impl N64 {
pub fn new(pif_rom: PifRom) -> N64 {
N64 {
cpu: Cpu::new(),
bus: Bus::new(),
pif_rom: pif_rom,
cart: None
}
}
pub fn insert_cart(&mut self, cart: &'a Cart) {
self.cart = Some(cart)
pub fn insert_cart(&mut self, cart: Cart) {
self.cart = Some(Box::new(cart))
}
pub fn power_on(&mut self) {
self.cpu.power_on_reset()
}
pub fn step(&mut self) {
pub fn start(&mut self) {
loop {
let word = self.cpu.fetch(self);
let instr = self.cpu.decode(word).unwrap();
self.cpu.execute(&instr);
}
}
}
fn read_word(&self, addr: PhysicalAddr) -> Result<Word, String> {
match self.bus.map_addr(addr)? {
Addr::PifRom(pa) => Ok(self.pif_rom.read_word(pa)),
Addr::CartDom1(pa) => Ok(self.cart.unwrap().read_word(pa))
impl cpu::mem::Reader for N64 {
fn readw(&self, addr: u64) -> u32 {
match self.cpu.virt_addr_to_phys_addr(addr) {
paddr @ 0x1FC0_0000...0x1FC0_07BF => self.pif_rom.readw(paddr - 0x1FC0_0000),
paddr @ _ => panic!("Unknown address: 0x{:X}", paddr)
}
}
}
impl cpu::mem::Writer for N64 {
fn write(&mut self, addr: u64, byte: u8) {
}
}
use byteorder::BigEndian;
use rom::Rom;
use types::Word;
use mem::{VirtualAddr};
pub struct PifRom {
rom: Rom<BigEndian>
......@@ -17,7 +15,7 @@ impl PifRom {
}
impl PifRom {
pub fn read_word(&self, addr: VirtualAddr) -> Word {
self.rom.read_word(addr as usize)
pub fn readw(&self, addr: u32) -> u32 {
self.rom.read_word(addr)
}
}
......@@ -17,12 +17,12 @@ impl<T: ByteOrder> Rom<T> {
}
}
pub fn read_word(&self, addr: usize) -> Word {
T::read_u32(&self.buf[addr..])
pub fn read_word(&self, addr: u32) -> Word {
T::read_u32(&self.buf[addr as usize..])
}
pub fn read_ascii_string(&self, addr: usize, count: usize) -> String {
String::from_utf8_lossy(&self.buf[addr..addr+count]).trim_right_matches('\0').to_string()
pub fn read_ascii_string(&self, addr: u32, count: usize) -> String {
String::from_utf8_lossy(&self.buf[addr as usize..addr as usize+count]).trim_right_matches('\0').to_string()
}
pub fn byte_swap(&mut self) {
......@@ -48,11 +48,11 @@ impl <T: ByteOrder> Index<RangeFull> for Rom<T> {
}
}
impl <T: ByteOrder> Index<usize> for Rom<T> {
impl <T: ByteOrder> Index<u32> for Rom<T> {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
&self.buf[index]
fn index(&self, index: u32) -> &u8 {
&self.buf[index as usize]
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment