use std::ops::{Index, IndexMut};
use std::fmt;
use std::fmt::Debug;
use std::io::{Write, BufReader, BufRead};
use std::fs::File;
use std::convert::TryInto;
use derive_more::{Add, AddAssign, Sub, SubAssign};
#[derive(Default, Copy, Clone, Add, AddAssign, Sub, SubAssign)]
pub struct Addr(std::num::Wrapping<u16>);
impl Addr {
pub const fn from_u16(a: u16) -> Self {
Addr(std::num::Wrapping(a))
}
}
impl From<Addr> for u16 {
fn from(addr: Addr) -> u16 {
addr.0.0
}
}
impl From<Addr> for usize {
fn from(addr: Addr) -> usize {
addr.0.0 as usize
}
}
impl From<u16> for Addr {
fn from(a: u16) -> Self {
Addr::from_u16(a)
}
}
impl Debug for Addr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:#04x}", self.0.0))
}
}
#[derive(Clone,Copy,Debug)]
pub enum Port{
A,
B,
C,
D,
}
impl Port {
pub fn to_addr(self) -> Addr {
use Port::*;
Addr::from(match self{
A => 0xFFFC,
B => 0xFFFD,
C => 0xFFFE,
D => 0xFFFF,
})
}
}
pub const PSW: Addr = Addr::from_u16(0xFFFB);
pub const FLAG_NONE: u8 = 0x00;
pub const FLAG_N: u8 = 0x40;
pub const FLAG_Z: u8 = 0x80;
const MEMORY_LENGTH: usize = u16::MAX as usize + 1;
#[derive(Clone)]
pub struct Memory(pub Box<[u8; MEMORY_LENGTH]>);
impl Memory {
pub fn new() -> Self {
Memory(Box::new([0; MEMORY_LENGTH]))
}
pub fn get(&self, address: Addr) -> u8 {
*self.0.index(usize::from(address))
}
pub fn get_mut(&mut self, address: Addr) -> &mut u8 {
self.0.index_mut(usize::from(address))
}
pub fn set(&mut self, address: Addr, value: u8) {
*self.get_mut(address) = value;
}
}
impl Default for Memory {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "signedmagnitude_sub")]
fn sm_to_tc(v: u8) -> u8 {
if v>=128 {
let ones = v ^ 0b0111_1111;
ones.wrapping_add(1)
} else {
v
}
}
#[cfg(feature = "signedmagnitude_sub")]
fn tc_to_sm(v: u8) -> u8 {
if v>=128 {
let ones = v.wrapping_sub(1);
ones ^ 0b0111_1111
} else {
v
}
}
#[derive(Clone, Default)]
pub struct Ssbc {
memory: Memory,
pc: Addr,
sp: Addr,
fault: bool,
halt: bool,
}
impl Ssbc {
pub fn get_psw(&self) -> u8 {
self.memory.get(PSW)
}
pub fn reset(&mut self) {
self.pc = 0x0000.into();
self.sp = 0xFFFA.into();
self.fault = false;
self.halt = false;
}
fn read_ir(&mut self) -> u8 {
let ir = self.memory.get(self.pc);
self.pc += 1.into();
ir
}
fn read_ext(&mut self) -> u16 {
let hi = self.memory.get(self.pc) as u16;
let lo = self.memory.get(self.pc+1.into()) as u16;
self.pc += 2.into();
hi*0x100+lo
}
fn update_psw(&mut self, val: u8) {
self.memory.set(
PSW,
if val>128{ FLAG_N } else if val==0 { FLAG_Z } else { FLAG_NONE }
);
}
pub fn step(&mut self) {
if self.fault || self.halt {
return;
}
match self.read_ir() {
0 => (),
1 => self.halt = true,
2 => {
let ir = self.read_ir();
self.memory.set(self.sp, ir);
self.sp -= 1.into();
},
3 => {
let ext = self.read_ext().into();
self.memory.set(self.sp, self.memory.get(ext));
self.sp -= 1.into();
},
4 => {
self.sp += 1.into();
},
5 => {
let ext = self.read_ext();
let pop = self.memory.get(self.sp+1.into());
self.memory.set(ext.into(), pop);
self.sp += 1.into();
},
6 => {
let ext = self.read_ext();
if self.memory.get(PSW) != FLAG_Z {
self.pc = ext.into();
}
},
7 => {
let ext = self.read_ext();
if self.memory.get(PSW) != FLAG_N {
self.pc = ext.into();
}
},
8 => {
let result = self.memory.get(self.sp+2.into()).wrapping_add(self.memory.get(self.sp+1.into()));
self.memory.set(self.sp+2.into(), result);
self.update_psw(result);
self.sp += 1.into();
},
#[cfg(feature = "signedmagnitude_sub")]
9 => {
let lhs = self.memory.get(self.sp+1.into());
let rhs = self.memory.get(self.sp+2.into());
let lhs = sm_to_tc(lhs);
let rhs = sm_to_tc(rhs);
let result = lhs.wrapping_sub(rhs);
let result = tc_to_sm(result);
self.memory.set(self.sp+2.into(), result);
self.update_psw(result);
self.sp += 1.into();
},
#[cfg(not(feature = "signedmagnitude_sub"))]
9 => {
let result = self.memory.get(self.sp+1.into()).wrapping_sub(self.memory.get(self.sp+2.into()));
self.memory.set(self.sp+2.into(), result);
self.update_psw(result);
self.sp += 1.into();
},
10 => {
let bw_or = self.memory.get(self.sp+2.into()) | self.memory.get(self.sp+1.into());
self.memory.set(self.sp+2.into(), !bw_or);
self.sp+=1.into();
},
_ => self.fault = true,
}
}
pub fn run(&mut self) {
while !self.fault && !self.halt {
self.step();
}
}
}
#[derive(Default)]
pub struct SsbcCli {
ssbc: Ssbc,
}
impl SsbcCli {
pub fn new() -> Self {
Self::default()
}
pub fn repl(&mut self) {
loop {
Self::prompt();
let mut command = String::new();
std::io::stdin().read_line(&mut command)
.expect("Couldn't read operator's command");
match command.chars().next().unwrap_or(' ') {
'R' => self.reset(),
'b' => self.ssbc.step(),
'r' => self.ssbc.run(),
'A' => self.read_port(Port::A),
'B' => self.write_port(Port::B),
'C' => self.read_port(Port::C),
'D' => self.write_port(Port::D),
's' => self.status(),
't' => self.top(),
'p' => self.psw(),
'q' => return,
_ => println!("WARNING: Unknown command")
}
}
}
fn prompt() {
let stdout = std::io::stdout();
let mut out = stdout.lock();
writeln!(out, "+------------------------+ ").ok();
writeln!(out, "| R: RESET | ").ok();
writeln!(out, "| b: BREAK | ").ok();
writeln!(out, "| r: RUN | ").ok();
writeln!(out, "| A: READ PORT A | ").ok();
writeln!(out, "| B: WRITE PORT B | ").ok();
writeln!(out, "| C: READ PORT C | ").ok();
writeln!(out, "| D: WRITE PORT D | ").ok();
writeln!(out, "| s: STATUS | ").ok();
writeln!(out, "| t: TOP | ").ok();
writeln!(out, "| p: PSW | ").ok();
writeln!(out, "| q: QUIT | ").ok();
writeln!(out, "| | ").ok();
writeln!(out, "| Enter menu selection: | ").ok();
writeln!(out, "+------------------------+ ").ok();
}
fn reset(&mut self) {
self.ssbc.reset();
let mac = BufReader::new(File::open("mac").expect("Couldn't open `mac` machine code file!"));
for (x, line) in mac.lines().filter_map(Result::ok).enumerate() {
let x: u16 = match x.try_into() { Ok(x) => x, Err(_) => {println!("WARNING: Machine code exceeds memory size!"); return} };
if line.len() >= 8 {
let value = u8::from_str_radix(&line[0..8], 2).expect("Couldn't parse user input");
self.ssbc.memory.set(x.into(), value);
}
}
}
fn read_port(&self, port: Port) {
let value = self.ssbc.memory.get(port.to_addr());
println!("Port {:?} value: {:08b} ", port, value);
}
fn write_port(&mut self, port: Port) {
print!("Enter Port {:?} value in binary (8 bits) ", port);
std::io::stdout().flush().expect("couldn't flush stdout");
let mut buffer = String::new();
std::io::stdin().read_line(&mut buffer).expect("Couldn't read value for port");
buffer.pop();
let value = u8::from_str_radix(&buffer, 2).expect("Couldn't parse user input");
self.ssbc.memory.set(port.to_addr(), value);
}
fn status(&self) {
println!("Fault: {} ", if self.ssbc.fault {1}else{0} );
println!(" Halt: {} ", if self.ssbc.halt {1}else{0} );
}
fn top(&self) {
println!("Top of stack: {:08b} ", self.ssbc.memory.get(self.ssbc.sp+1.into()));
}
fn psw(&self) {
println!("PSW: {:08b} ", self.ssbc.get_psw());
}
}
fn main() {
let mut ssbc_cli = SsbcCli::default();
ssbc_cli.repl();
}