Compare commits

...

6 Commits

Author SHA1 Message Date
Richard
7365bda40a removetest 2025-08-30 21:19:44 +02:00
Richard
c9dafbcb72 debugging 2025-08-30 21:05:56 +02:00
Richard
8d48c4bc89 featurecomplete 2025-08-30 16:50:47 +02:00
Richard
35b0b5972f ops 2025-08-29 22:20:12 +02:00
Richard
1ea7908fca ops 2025-08-28 19:34:25 +02:00
Richard
65598cf86e cmov 2025-08-26 10:50:05 +02:00
2 changed files with 642 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
extern crate um;
use um::cpu::{Cpu};
use um::cpu::{Cpu,Console};
use std::io;
use std::io::prelude::*;
use std::fs::File;
@@ -9,8 +9,44 @@ use clap::Parser;
/// Run a um program
#[derive(Parser)]
struct Cli {
// debug level
#[clap(short, long, default_value_t = 0)]
debug: u32,
/// The path to the program to load
program: std::path::PathBuf,
program: std::path::PathBuf
}
struct IoConsole {
inptr: usize,
inbuf: String
}
impl IoConsole {
pub fn make() -> IoConsole {
IoConsole {
inptr: 0,
inbuf: String::from("")
}
}
}
impl Console for IoConsole {
fn read(&mut self) -> Option<u8> {
if self.inptr >= self.inbuf.len() {
if let Err(_) = std::io::stdin().read_line(&mut self.inbuf) {
return None;
}
}
let r = self.inbuf.bytes().nth(self.inptr).unwrap();
self.inptr +=1;
Some(r)
}
fn write(&mut self, c: u8) {
let buf = [c;1];
let _ = std::io::stdout().write(&buf);
let _ = std::io::stdout().flush();
}
}
@@ -36,21 +72,11 @@ fn main() -> io::Result<()> {
}
let mut c = Cpu::new(pgm);
cont = true;
println!("start\n{}", c.state());
while cont {
let r = c.step();
cont = match r {
Ok(_) => {
println!("ok\n{}", c.state());
true
},
Err(e) => {
println!("Error {:?}", e);
false
}
};
c.set_debug(args.debug);
let mut cons = IoConsole::make();
match c.run(&mut cons) {
Err(e) => println!("Error {:?}", e),
Ok(_) => ()
}
Ok(())
}

View File

@@ -2,21 +2,33 @@ use std::collections::HashMap;
use std::vec::Vec;
use crate::instruction::{Instruction, NormalInstruction, SpecialInstruction};
pub trait Console {
fn read(&mut self) -> Option<u8>;
fn write(&mut self, c: u8);
}
#[derive(Debug)]
pub enum CpuError {
ProgramEnd,
InvalidInstruction,
UnexpectedInput
UnexpectedInput,
InvalidArray,
ArrayOutOfBounds,
DivideByZero,
Halt,
LargeAlloc,
InvalidOutput
}
#[derive(Debug)]
pub struct Cpu {
reg: [u32; 8],
arr: HashMap<u32, Vec<u32>>,
iarr: u32,
finger: u32,
out: Option<u8>,
in_reg: Option<u8>
in_reg: Option<u8>,
debug: u32
}
@@ -27,12 +39,25 @@ impl Cpu {
Cpu {
reg: [0;8],
arr: arrays,
iarr: 1,
finger: 0,
in_reg: None,
out: None
out: None,
debug: 0
}
}
pub fn set_debug(&mut self, debug: u32) {
self.debug = debug;
}
fn gr(&self, r: u8) -> u32{
self.reg[r as usize]
}
fn sr(&mut self, r: u8, v:u32) {
self.reg[r as usize] = v
}
fn ins(&self) -> Result<Instruction,CpuError> {
if self.finger as usize >= self.arr[&0].len() {
return Err(CpuError::ProgramEnd);
@@ -75,15 +100,164 @@ impl Cpu {
}
fn handle_ortho(&mut self, i:SpecialInstruction) -> Result<(), CpuError>{
self.reg[i.a as usize] = i.v as u32;
self.sr(i.a, i.v);
Ok(())
}
fn handle_cmov(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
if self.gr(i.c) > 0 {
let v = self.gr(i.b);
self.sr(i.a, v);
}
Ok(())
}
fn handle_aix(&mut self, i: NormalInstruction) -> Result<(), CpuError> {
let aidx = self.gr(i.b);
let aoff = self.gr(i.c) as usize;
if let Some(arr) = self.arr.get(&aidx){
if arr.len() > aoff {
self.sr(i.a, arr[aoff]);
Ok(())
} else {
Err(CpuError::ArrayOutOfBounds)
}
} else {
Err(CpuError::InvalidArray)
}
}
fn handle_amd(&mut self, i: NormalInstruction) -> Result<(), CpuError> {
let aidx = self.gr(i.a);
let aoff = self.gr(i.b) as usize;
let v = self.gr(i.c);
// println!("amd {}[{}]={}", aidx, aoff, v);
if let Some(arr) = self.arr.get_mut(&aidx){
if arr.len() > aoff {
arr[aoff] = v;
Ok(())
} else {
Err(CpuError::ArrayOutOfBounds)
}
} else {
Err(CpuError::InvalidArray)
}
}
fn handle_add(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let b = self.gr(i.b);
let c = self.gr(i.c);
let (a, _) = b.overflowing_add(c);
self.sr(i.a, a);
Ok(())
}
fn handle_mul(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let b = self.gr(i.b);
let c = self.gr(i.c);
let (a, _) = b.overflowing_mul(c);
self.sr(i.a, a);
Ok(())
}
fn handle_div(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let b = self.gr(i.b);
let c = self.gr(i.c);
if c == 0 {
return Err(CpuError::DivideByZero);
}
let (a, _) = b.overflowing_div(c);
self.sr(i.a, a);
Ok(())
}
fn handle_nand(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let b = self.gr(i.b);
let c = self.gr(i.c);
let a = !(b & c);
self.sr(i.a, a);
Ok(())
}
fn handle_halt(&mut self, _i:NormalInstruction) -> Result<(), CpuError>{
Err(CpuError::Halt)
}
fn handle_alc(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let c = self.gr(i.c) as usize;
if c > 1048576 {
return Err(CpuError::LargeAlloc);
}
let aid = self.iarr;
self.iarr += 1;
let v = vec![0; c];
self.arr.insert(aid, v);
self.sr(i.b, aid);
//println!("alc {} -> {}", c, aid);
Ok(())
}
fn handle_abn(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let c = self.gr(i.c);
if c == 0 || ! self.arr.contains_key(&c){
return Err(CpuError::InvalidArray);
}
self.arr.remove(&c);
Ok(())
}
fn handle_out(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let c = self.gr(i.c);
if c > 255 {
return Err(CpuError::InvalidOutput);
}
self.out = Some(c as u8);
Ok(())
}
fn handle_inp(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
self.sr(i.c, 0xffffffff);
self.in_reg = Some(i.c);
Ok(())
}
fn handle_lod(&mut self, i:NormalInstruction) -> Result<(), CpuError>{
let aidx = self.gr(i.b);
let aoff = self.gr(i.c);
if let Some(arr) = self.arr.get(&aidx){
if arr.len() > (aoff as usize) {
if aidx > 0 {
// println!("cloning {}", aidx);
self.arr.insert(0, arr.clone());
}
self.finger = aoff;
Ok(())
} else {
Err(CpuError::ArrayOutOfBounds)
}
} else {
Err(CpuError::InvalidArray)
}
}
fn exec(&mut self, instruction: Instruction)-> Result<(), CpuError>{
println!("{:?}", instruction);
//println!("{:?}", instruction);
match instruction {
Instruction::Ortho(i) => self.handle_ortho(i),
_ => Err(CpuError::InvalidInstruction)
Instruction::ConditionalMove(i) => self.handle_cmov(i),
Instruction::ArrayIndex(i) => self.handle_aix(i),
Instruction::ArrayAmend(i) => self.handle_amd(i),
Instruction::Add(i) => self.handle_add(i),
Instruction::Multiply(i) => self.handle_mul(i),
Instruction::Divide(i) => self.handle_div(i),
Instruction::NotAnd(i) => self.handle_nand(i),
Instruction::Halt(i) => self.handle_halt(i),
Instruction::Allocate(i) => self.handle_alc(i),
Instruction::Abandon(i) => self.handle_abn(i),
Instruction::Output(i) => self.handle_out(i),
Instruction::Input(i) => self.handle_inp(i),
Instruction::Load(i) => self.handle_lod(i),
//_ => Err(CpuError::InvalidInstruction)
}
}
@@ -91,15 +265,81 @@ impl Cpu {
self.out = None;
self.in_reg = None;
let i = self.ins()?;
self.exec(i)?;
self.finger += 1;
self.exec(i)?;
Ok(())
}
pub fn run(&mut self, console: &mut impl Console) -> Result<(), CpuError> {
let mut cont = true;
while cont {
if self.debug > 5 {
println!("{:?}", self.ins());
println!("{}", self.state());
}
cont = match self.step() {
Ok(_) => true,
Err(CpuError::ProgramEnd) => false,
Err(e) => return Err(e)
};
if self.expecting_input() {
if let Some(c) = console.read() {
self.set_in(c)?;
}
};
if let Some(c) = self.output() {
console.write(c);
}
};
Ok(())
}
}
#[cfg(test)]
mod tests{
use super::*;
struct NoConsole;
impl Console for NoConsole {
fn read(&mut self) -> Option<u8> {
None
}
fn write(&mut self, _c: u8) {}
}
struct BufConsole {
inb: Vec<u8>,
inptr: usize,
outb: Vec<u8>
}
impl BufConsole {
fn make() -> BufConsole {
BufConsole {
inb: vec![],
inptr: 0,
outb: vec![]
}
}
}
impl Console for BufConsole {
fn read(&mut self) -> Option<u8> {
if self.inptr >= self.inb.len() {
None
} else {
let r = self.inb[self.inptr];
self.inptr += 1;
Some(r)
}
}
fn write(&mut self, c: u8) {
self.outb.push(c);
}
}
fn ezop(op:u32, a:u32, b:u32, c:u32) -> u32 {
match op {
@@ -108,17 +348,361 @@ mod tests{
}
}
#[test]
fn test_ortho() -> Result<(), String> {
let pgm = vec!(ezop(13, 3, 1337, 0));
fn test_ortho() -> Result<(), CpuError> {
let pgm = vec!( ezop(13, 3, 1337, 0) );
let mut c = Cpu::new(pgm);
let result = c.step();
assert!(matches!(result, Ok(_)));
assert_eq!(c.reg[0], 0);
assert_eq!(c.reg[3], 1337);
let exp = [0, 0, 0, 1337, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_cmov() -> Result<(), CpuError> {
let pgm = vec!( ezop(0, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1337, 1, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 1337, 1337, 1, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_cmov_neg() -> Result<(), CpuError> {
let pgm = vec!( ezop(0, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1337, 0, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 0, 1337, 0, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_aix() -> Result<(), CpuError> {
let pgm = vec!( ezop(1, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.arr.insert(1338, vec![0,0,0,1337]);
c.reg = [0, 0, 1338, 3, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 1337, 1338, 3, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_aix_noarr() -> Result<(), String> {
let pgm = vec!( ezop(1, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1338, 3, 0, 0, 0, 0];
match c.run(&mut NoConsole{}) {
Err(CpuError::InvalidArray) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_aix_nooff() -> Result<(), String> {
let pgm = vec!( ezop(1, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1338, 3, 0, 0, 0, 0];
c.arr.insert(1338, vec![0]);
match c.run(&mut NoConsole{}) {
Err(CpuError::ArrayOutOfBounds) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_amd() -> Result<(), CpuError> {
let pgm = vec!( ezop(2, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.arr.insert(1338, vec![0,0,0,0]);
c.reg = [0, 1338, 1, 1336, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 1338, 1, 1336,
0, 0, 0, 0];
assert_eq!(c.reg, exp);
if let Some(arr) = c.arr.get(&1338){
assert_eq!(arr,&vec![0,1336,0,0] );
Ok(())
} else {
Err(CpuError::InvalidArray)
}
}
#[test]
fn test_amd_noarr() -> Result<(), String> {
let pgm = vec!( ezop(2, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 1338, 3, 0, 0, 0, 0, 0];
match c.run(&mut NoConsole{}) {
Err(CpuError::InvalidArray) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_amd_nooff() -> Result<(), String> {
let pgm = vec!( ezop(2, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 1338, 3, 0, 0, 0, 0, 0];
c.arr.insert(1338, vec![0]);
match c.run(&mut NoConsole{}) {
Err(CpuError::ArrayOutOfBounds) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_add() -> Result<(), CpuError> {
let pgm = vec!( ezop(3, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1337, 1, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 1338, 1337, 1, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_add_ovf() -> Result<(), CpuError> {
let pgm = vec!( ezop(3, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1337, 0xffffffff, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 1336, 1337, 0xffffffff, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_mul() -> Result<(), CpuError> {
let pgm = vec!( ezop(4, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1337, 2, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 2674, 1337, 2, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_mul_ovf() -> Result<(), CpuError> {
let pgm = vec!( ezop(4, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 2, 0xffffffff, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 0xfffffffe, 2, 0xffffffff, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_div() -> Result<(), CpuError> {
let pgm = vec!( ezop(5, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 2048, 1024, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 2, 2048, 1024, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_div_zero() -> Result<(), String> {
let pgm = vec!( ezop(5, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 1338, 3, 0, 0, 0, 0, 0];
match c.run(&mut NoConsole{}) {
Err(CpuError::DivideByZero) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_nand() -> Result<(), CpuError> {
let pgm = vec!( ezop(6, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 0xff00ff00, 0xffff0000, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
let exp = [0, 0x00ffff00, 0xff00ff00, 0xffff0000, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_halt() -> Result<(), String> {
let pgm = vec!( ezop(7, 1, 2, 3) );
let mut c = Cpu::new(pgm);
match c.run(&mut NoConsole{}) {
Err(CpuError::Halt) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_alc_large() -> Result<(), String> {
let pgm = vec!( ezop(8, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 1338, 33, 0xffffffff, 0, 0, 0, 0];
match c.run(&mut NoConsole{}) {
Err(CpuError::LargeAlloc) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_alc() -> Result<(), CpuError> {
let pgm = vec!( ezop(8, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 1338, 33, 5, 0, 0, 0, 0];
if let Err(e) = c.run(&mut NoConsole{}) {
return Err(e)
}
let exp = [0, 1338, 1, 5, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
if let Some(arr) = c.arr.get(&1){
assert_eq!(arr,&vec![0,0,0,0,0] );
Ok(())
} else {
Err(CpuError::InvalidArray)
}
}
#[test]
fn test_abn() -> Result<(), CpuError> {
let pgm = vec!( ezop(9, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.arr.insert(1338, vec![0,0,0,0]);
c.reg = [0, 0, 0, 1338, 0, 0, 0, 0];
c.run(&mut NoConsole{})?;
if c.arr.contains_key(&1338) {
Err(CpuError::InvalidArray)
} else {
Ok(())
}
}
#[test]
fn test_abn_zero() -> Result<(), String> {
let pgm = vec!( ezop(9, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 0, 0, 0, 0, 0, 0];
match c.run(&mut NoConsole{}) {
Err(CpuError::InvalidArray) => Ok(()),
Err(_) => Err(String::from("Invalid error")),
Ok(_) => Err(String::from("Expected err got ok"))
}
}
#[test]
fn test_out() -> Result<(), String> {
let pgm = vec!( ezop(10, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 0, 128, 0, 0, 0, 0];
let mut cons = BufConsole::make();
if let Err(e) = c.run(&mut cons) {
return Err(format!("unexpected err {:?}", e));
};
assert_eq!(cons.outb, vec!(128));
Ok(())
}
#[test]
fn test_out_inv() -> Result<(), String> {
let pgm = vec!( ezop(10, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 0, 1337, 0, 0, 0, 0];
let mut cons = BufConsole::make();
match c.run(&mut cons) {
Err(CpuError::InvalidOutput) => Ok(()),
Ok(_) => Err(format!("unexpected ok")),
Err(e) => Err(format!("unexpected err {:?}", e))
}
}
#[test]
fn test_inp() -> Result<(), String> {
let pgm = vec!( ezop(11, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 0, 0, 0, 0, 0, 0];
let mut cons = BufConsole::make();
cons.inb.push(128);
if let Err(e) = c.run(&mut cons) {
return Err(format!("unexpected err {:?}", e));
};
let exp = [0, 0, 0, 128, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_inp_fail() -> Result<(), String> {
let pgm = vec!( ezop(11, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 0, 0, 0, 0, 0, 0];
let mut cons = BufConsole::make();
if let Err(e) = c.run(&mut cons) {
return Err(format!("unexpected err {:?}", e));
};
let exp = [0, 0, 0, 0xffffffff, 0, 0, 0, 0];
assert_eq!(c.reg, exp);
Ok(())
}
#[test]
fn test_lod() -> Result<(), CpuError> {
let pgm = vec!( ezop(12, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.arr.insert(1338, vec![0,0,0,1339]);
c.reg = [0, 0, 1338, 3, 0, 0, 0, 0];
c.step()?;
assert_eq!(c.finger, 3);
if let Some(arr) = c.arr.get(&0){
assert_eq!(arr,&vec![0,0,0,1339] );
Ok(())
} else {
Err(CpuError::InvalidArray)
}
}
#[test]
fn test_lod_inv() -> Result<(), String> {
let pgm = vec!( ezop(12, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1338, 3, 0, 0, 0, 0];
match c.step() {
Err(CpuError::InvalidArray) => Ok(()),
Ok(_) => Err(format!("unexpected ok")),
Err(e) => Err(format!("unexpected err {:?}", e))
}
}
#[test]
fn test_lod_off() -> Result<(), String> {
let pgm = vec!( ezop(12, 1, 2, 3) );
let mut c = Cpu::new(pgm);
c.reg = [0, 0, 1338, 3, 0, 0, 0, 0];
c.arr.insert(1338, vec![0]);
match c.step() {
Err(CpuError::ArrayOutOfBounds) => Ok(()),
Ok(_) => Err(format!("unexpected ok")),
Err(e) => Err(format!("unexpected err {:?}", e))
}
}
}