2022-09-02 09:33:46 +00:00
|
|
|
use anyhow::{bail, Result};
|
2022-07-17 18:24:49 +00:00
|
|
|
use itertools::Itertools;
|
|
|
|
use std::cmp::Ordering;
|
2022-08-23 14:20:38 +00:00
|
|
|
use std::io::{self, Write};
|
2023-03-09 16:05:33 +00:00
|
|
|
use std::time::Instant;
|
2022-07-17 18:24:49 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2023-03-23 16:59:10 +00:00
|
|
|
pub struct Emulator {
|
2022-07-17 18:24:49 +00:00
|
|
|
program: [u8; 32 * 1024],
|
|
|
|
pub tmp: u16,
|
|
|
|
pub reg_pc: u16,
|
|
|
|
pub reg_opc: u8,
|
|
|
|
pub reg_arg: u16,
|
|
|
|
pub reg_s: u8,
|
|
|
|
pub reg_sp: u16,
|
|
|
|
pub reg_a: u16,
|
|
|
|
pub reg_b: u16,
|
|
|
|
pub reg_c: u16,
|
|
|
|
pub reg_d: u16,
|
2023-03-09 16:05:33 +00:00
|
|
|
pub time: u16,
|
|
|
|
pub last_time: Instant,
|
2022-07-17 18:24:49 +00:00
|
|
|
memory: [u16; 16 * 1024],
|
2022-08-23 14:20:38 +00:00
|
|
|
term: console::Term,
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
impl Emulator {
|
2022-07-17 18:24:49 +00:00
|
|
|
pub fn new(program: [u8; 32 * 1024]) -> Self {
|
2022-08-26 13:02:48 +00:00
|
|
|
Self {
|
2022-07-17 18:24:49 +00:00
|
|
|
program,
|
|
|
|
tmp: 0,
|
|
|
|
reg_pc: 0,
|
|
|
|
reg_opc: 0,
|
|
|
|
reg_arg: 0,
|
|
|
|
reg_s: 0,
|
|
|
|
reg_sp: 0,
|
|
|
|
reg_a: 0,
|
|
|
|
reg_b: 0,
|
|
|
|
reg_c: 0,
|
|
|
|
reg_d: 0,
|
2023-03-09 16:05:33 +00:00
|
|
|
time: 0,
|
|
|
|
last_time: Instant::now(),
|
2022-07-17 18:24:49 +00:00
|
|
|
memory: [0; 16 * 1024],
|
2022-08-23 14:20:38 +00:00
|
|
|
term: console::Term::stdout(),
|
2022-08-26 13:02:48 +00:00
|
|
|
}
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
|
2022-09-02 09:33:46 +00:00
|
|
|
pub fn get_memory(&self, address: u16) -> Result<u16> {
|
2022-07-17 18:24:49 +00:00
|
|
|
if address < (32 * 1024) {
|
|
|
|
match self.program.get(address as usize) {
|
2022-09-02 09:33:46 +00:00
|
|
|
Some(val) => Ok(*val as u16),
|
|
|
|
None => Ok(0),
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2022-09-09 12:17:37 +00:00
|
|
|
// } else if address < (40 * 1024) {
|
|
|
|
// Ok(self.stack[(address - (32 * 1024)) as usize])
|
|
|
|
} else if address < (48 * 1024) {
|
2022-09-09 12:52:07 +00:00
|
|
|
Ok(self.memory[(address - (32 * 1024)) as usize])
|
2022-09-09 12:17:37 +00:00
|
|
|
} else if address == (48 * 1024 + 2) {
|
2022-09-02 09:33:46 +00:00
|
|
|
Ok(self.term.read_char()? as u16)
|
2022-07-17 18:24:49 +00:00
|
|
|
} else {
|
2022-09-02 09:33:46 +00:00
|
|
|
Ok(0)
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 09:33:46 +00:00
|
|
|
pub fn set_memory(&mut self, address: u16, value: u16) -> Result<()> {
|
2022-09-09 12:17:37 +00:00
|
|
|
// if ((32 * 1024)..(40 * 1024)).contains(&address) {
|
|
|
|
// self.stack[(address - (32 * 1024)) as usize] = value;
|
|
|
|
if ((32 * 1024)..(48 * 1024)).contains(&address) {
|
|
|
|
self.memory[(address - (32 * 1024)) as usize] = value;
|
|
|
|
} else if address == (48 * 1024) {
|
|
|
|
print!("{}", value);
|
2022-09-02 09:33:46 +00:00
|
|
|
io::stdout().flush()?;
|
2022-09-09 12:17:37 +00:00
|
|
|
} else if address == (48 * 1024 + 1) {
|
2022-07-17 18:24:49 +00:00
|
|
|
print!("{}", char::from(value as u8));
|
2022-09-02 09:33:46 +00:00
|
|
|
io::stdout().flush()?;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2022-09-02 09:33:46 +00:00
|
|
|
|
|
|
|
Ok(())
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_register(&self, register: u8) -> u16 {
|
|
|
|
match register {
|
|
|
|
0x0 => self.reg_pc,
|
|
|
|
0x1 => self.reg_opc as u16,
|
|
|
|
0x2 => self.reg_arg,
|
|
|
|
0x3 => self.reg_s as u16,
|
|
|
|
0x4 => self.reg_sp,
|
|
|
|
0x5 => self.reg_a,
|
|
|
|
0x6 => self.reg_b,
|
|
|
|
0x7 => self.reg_c,
|
|
|
|
0x8 => self.reg_d,
|
|
|
|
_ => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_register(&mut self, register: u8, value: u16) -> u16 {
|
|
|
|
match register {
|
|
|
|
0x0 => {
|
|
|
|
self.reg_pc = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
0x1 => {
|
|
|
|
self.reg_opc = value as u8;
|
|
|
|
value & 0xff
|
|
|
|
}
|
|
|
|
0x2 => {
|
|
|
|
self.reg_arg = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
0x3 => {
|
|
|
|
self.reg_s = value as u8;
|
|
|
|
value & 0xff
|
|
|
|
}
|
|
|
|
0x4 => {
|
|
|
|
self.reg_sp = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
0x5 => {
|
|
|
|
self.reg_a = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
0x6 => {
|
|
|
|
self.reg_b = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
0x7 => {
|
|
|
|
self.reg_c = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
0x8 => {
|
|
|
|
self.reg_d = value;
|
|
|
|
value
|
|
|
|
}
|
|
|
|
_ => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
pub fn alu(&mut self, operation: u8) -> Result<()> {
|
2022-07-17 18:24:49 +00:00
|
|
|
match operation {
|
|
|
|
0x00 => {
|
|
|
|
self.tmp = !self.reg_a;
|
|
|
|
}
|
|
|
|
0x01 => {
|
|
|
|
self.tmp = self.reg_a & self.reg_b;
|
|
|
|
}
|
|
|
|
0x02 => {
|
|
|
|
self.tmp = self.reg_a | self.reg_b;
|
|
|
|
}
|
|
|
|
0x03 => {
|
|
|
|
self.tmp = self.reg_a ^ self.reg_b;
|
|
|
|
}
|
|
|
|
0x04 => {
|
|
|
|
self.tmp = self.reg_a << self.reg_b;
|
|
|
|
}
|
|
|
|
0x05 => {
|
|
|
|
self.tmp = self.reg_a >> self.reg_b;
|
|
|
|
}
|
|
|
|
0x06 => {
|
|
|
|
self.tmp = self
|
|
|
|
.reg_a
|
|
|
|
.wrapping_add(self.reg_b)
|
|
|
|
.wrapping_add((self.reg_s & 0b00000001) as u16);
|
|
|
|
self.reg_s &= 0b11111110;
|
|
|
|
if self.tmp < self.reg_a {
|
|
|
|
self.tmp |= 0b00000001;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0x07 => {
|
|
|
|
self.tmp = self
|
|
|
|
.reg_a
|
|
|
|
.wrapping_sub(self.reg_b)
|
|
|
|
.wrapping_sub((self.reg_s & 0b00000001) as u16);
|
|
|
|
self.reg_s &= 0b11111110;
|
|
|
|
if self.tmp > self.reg_a {
|
|
|
|
self.tmp |= 0b00000001;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0x08 => {
|
|
|
|
self.tmp = self.reg_a.wrapping_mul(self.reg_b);
|
|
|
|
self.reg_s &= 0b11111110;
|
|
|
|
}
|
|
|
|
0x09 => {
|
|
|
|
self.tmp = self.reg_a / self.reg_b;
|
|
|
|
self.reg_s &= 0b11111110;
|
|
|
|
}
|
|
|
|
0x0a => {
|
|
|
|
self.tmp = match self.reg_a.cmp(&self.reg_b) {
|
2022-08-23 14:20:38 +00:00
|
|
|
Ordering::Equal => 0,
|
2022-07-17 18:24:49 +00:00
|
|
|
Ordering::Less => 1,
|
|
|
|
Ordering::Greater => 2,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0x0b => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a == self.reg_b) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x0c => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a < self.reg_b) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x0d => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a > self.reg_b) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x0e => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a <= self.reg_b) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x0f => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a >= self.reg_b) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x10 => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a & 1 == 1) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x11 => {
|
2022-09-02 09:33:46 +00:00
|
|
|
self.tmp = (self.reg_a & 1 != 1) as u16;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x12 => {
|
|
|
|
self.tmp = rand::random();
|
|
|
|
}
|
2023-03-09 16:05:33 +00:00
|
|
|
0x13 => {
|
|
|
|
let time = Instant::now();
|
|
|
|
self.tmp = self.last_time.elapsed().as_millis() as u16;
|
|
|
|
self.last_time = time;
|
|
|
|
}
|
2023-03-24 20:13:40 +00:00
|
|
|
_ => bail!("Invalid ALU operation: 0x{:0>4x}", operation),
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
pub fn cycle(&mut self) -> Result<()> {
|
|
|
|
self.reg_opc = self.get_memory(self.reg_pc)? as u8;
|
|
|
|
self.reg_pc = self.reg_pc.wrapping_add(1);
|
2022-07-17 18:24:49 +00:00
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
if self.reg_opc >> 7 == 1 {
|
|
|
|
self.reg_opc &= 0b0111111;
|
|
|
|
self.reg_arg = 0;
|
2022-07-17 18:24:49 +00:00
|
|
|
} else {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.reg_arg = self.get_memory(self.reg_pc)? << 8;
|
|
|
|
self.reg_pc = self.reg_pc.wrapping_add(1);
|
2022-07-17 18:24:49 +00:00
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
self.reg_arg |= self.get_memory(self.reg_pc)?;
|
|
|
|
self.reg_pc = self.reg_pc.wrapping_add(1);
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
match self.reg_opc {
|
2022-07-17 18:24:49 +00:00
|
|
|
0x00 => {}
|
|
|
|
0x01 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.tmp = self.reg_arg;
|
|
|
|
self.memory[self.reg_sp as usize] = self.reg_arg;
|
|
|
|
self.reg_sp = self.reg_sp.wrapping_add(1);
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x02 => {
|
2023-03-31 12:36:00 +00:00
|
|
|
let before = self.reg_sp;
|
2023-03-23 16:59:10 +00:00
|
|
|
self.reg_sp = self.reg_sp.wrapping_sub(1);
|
2023-03-31 12:36:00 +00:00
|
|
|
if before < self.reg_sp {
|
|
|
|
self.reg_sp = 0;
|
|
|
|
}
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x03 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.tmp = self.reg_arg;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x04 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.tmp = self.get_register(self.reg_arg as u8);
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x05 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.tmp = self.memory[self.reg_sp.wrapping_sub(1) as usize];
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x06 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.set_register(self.reg_arg as u8, self.tmp);
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
0x07 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
if self.reg_a & 1 == 1 {
|
|
|
|
self.set_register(self.reg_arg as u8, self.tmp);
|
2022-08-23 14:20:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
0x08 => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.memory[self.reg_sp as usize] = self.tmp;
|
|
|
|
self.reg_sp = self.reg_sp.wrapping_add(1);
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2022-08-23 14:20:38 +00:00
|
|
|
0x09 => {
|
2022-07-17 18:24:49 +00:00
|
|
|
println!(
|
|
|
|
"[DEBUG]: [{}]",
|
2023-03-23 16:59:10 +00:00
|
|
|
self.memory.iter().take(self.reg_sp as usize).join(", ")
|
2022-07-17 18:24:49 +00:00
|
|
|
);
|
2022-08-23 14:20:38 +00:00
|
|
|
print!("Press enter to continue execution...",);
|
2022-09-02 09:33:46 +00:00
|
|
|
io::stdout().flush()?;
|
2023-03-23 16:59:10 +00:00
|
|
|
self.term.read_line()?;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2022-09-06 09:54:57 +00:00
|
|
|
0x0a => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.alu(self.tmp as u8)?;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2022-09-06 09:54:57 +00:00
|
|
|
0x0b => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.tmp = self.get_memory(self.tmp)?;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2022-09-06 09:54:57 +00:00
|
|
|
0x0c => {
|
2023-03-23 16:59:10 +00:00
|
|
|
self.set_memory(self.tmp, self.reg_a)?;
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2023-04-04 12:07:21 +00:00
|
|
|
_ => bail!("Invalid opcode: 0x{:0>2x}", self.reg_opc),
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
2023-03-23 16:59:10 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_stopped(&self) -> bool {
|
|
|
|
self.reg_pc == 0xffff
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|
|
|
|
|
2023-03-23 16:59:10 +00:00
|
|
|
pub fn run(&mut self) -> Result<()> {
|
|
|
|
while !self.is_stopped() {
|
|
|
|
self.cycle()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-07-17 18:24:49 +00:00
|
|
|
}
|