do the do, this is unfinished don't touch
This commit is contained in:
commit
a6c5af3382
5 changed files with 883 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
program.ram
|
||||
/target
|
248
Cargo.lock
generated
Normal file
248
Cargo.lock
generated
Normal file
|
@ -0,0 +1,248 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47582c09be7c8b32c0ab3a6181825ababb713fde6fff20fc573a3870dd45c6a0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foodie"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "foodie"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1.17", features = ["derive"] }
|
||||
termios = "0.3.3"
|
604
src/food8.rs
Normal file
604
src/food8.rs
Normal file
|
@ -0,0 +1,604 @@
|
|||
use std::io::{Read, stdin};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
|
||||
use std::time::Duration;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicU8};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::io::Write;
|
||||
use std::io::stdout;
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::thread;
|
||||
|
||||
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
|
||||
|
||||
struct StdinPoll {
|
||||
exit: Arc<AtomicBool>,
|
||||
byte: Arc<AtomicU16>
|
||||
}
|
||||
|
||||
impl StdinPoll {
|
||||
pub fn new() -> Self {
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let byte = Arc::new(AtomicU16::new(256));
|
||||
|
||||
let exit2 = exit.clone();
|
||||
let byte2 = byte.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut sin = stdin();
|
||||
let mut lock = sin.lock();
|
||||
let mut buf = [0u8; 1];
|
||||
|
||||
loop {
|
||||
if exit2.load(Ordering::Relaxed) { break }
|
||||
lock.read_exact(buf.as_mut_slice()).unwrap();
|
||||
while byte2.compare_exchange(256, buf[0] as u16, Ordering::Relaxed, Ordering::Relaxed).is_err() {}
|
||||
}
|
||||
});
|
||||
|
||||
StdinPoll {
|
||||
exit,
|
||||
byte
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_byte(&self) -> Option<u8> {
|
||||
u8::try_from(self.byte.swap(256, Ordering::Relaxed)).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StdinPoll {
|
||||
fn drop(&mut self) {
|
||||
self.exit.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
static KEEBIMP: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
struct Pointer {
|
||||
bank: u8,
|
||||
address: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MemoryBank {
|
||||
content: Vec<u8>,
|
||||
}
|
||||
|
||||
struct Cpu {
|
||||
debug: bool,
|
||||
memory: Vec<MemoryBank>,
|
||||
pc: u16,
|
||||
stack: Vec<Pointer>,
|
||||
registers: Vec<u8>,
|
||||
peripherals: Vec<u8>,
|
||||
cache: MemoryBank,
|
||||
cachebank: u8,
|
||||
bank: u8,
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
fn ramread(&self, index: usize) -> &u8 {
|
||||
&(self.memory[self.bank as usize]).content[index]
|
||||
}
|
||||
fn ramwrite(&mut self, index: usize, value: u8) {
|
||||
(self.memory[self.bank as usize]).content[index] = value;
|
||||
}
|
||||
fn regread(&self, index: usize) -> &u8 {
|
||||
&self.registers[index]
|
||||
}
|
||||
fn regwrite(&mut self, index: usize, value: u8) {
|
||||
self.registers[index] = value;
|
||||
}
|
||||
fn refreshcache(&mut self) {
|
||||
self.cache = self.memory[self.bank as usize].to_owned();
|
||||
self.cachebank = self.bank;
|
||||
}
|
||||
fn incrementpc(&mut self) {
|
||||
if self.pc >= 0xFFF9 {
|
||||
let bank = self.bank;
|
||||
self.setbank(self.cachebank);
|
||||
self.incbank();
|
||||
self.pc = 0;
|
||||
self.refreshcache();
|
||||
self.setbank(bank);
|
||||
} else {
|
||||
self.pc = self.pc + 4;
|
||||
}
|
||||
}
|
||||
#[allow(unused_parens)]
|
||||
fn fetch(&mut self) -> u32 {
|
||||
return ((self.cache.content[self.pc as usize] as u32) << 24 | (self.cache.content[self.pc as usize + 1 as usize] as u32) << 16 | (self.cache.content[self.pc as usize + 2 as usize] as u32) << 8 | self.cache.content[self.pc as usize + 3 as usize] as u32)
|
||||
}
|
||||
fn setpc(&mut self, prc: u16) {
|
||||
self.pc = prc;
|
||||
}
|
||||
fn stackpush(&mut self) {
|
||||
if self.stack.len() == 4 {
|
||||
self.dump("Stack overflow");
|
||||
std::process::exit(0);
|
||||
}
|
||||
let example = Pointer { bank: self.bank, address: self.pc };
|
||||
self.stack.push(example);
|
||||
}
|
||||
fn stackpop(&mut self) -> Pointer {
|
||||
if self.stack.len() == 0 {
|
||||
self.dump("Stack underflow");
|
||||
std::process::exit(0);
|
||||
}
|
||||
let stacker = self.stack.pop().unwrap();
|
||||
return stacker
|
||||
}
|
||||
fn getperph(&self, perph: u8) -> u8 {
|
||||
self.peripherals[perph as usize]
|
||||
}
|
||||
fn setperph(&mut self, perph: u8, value: u8) {
|
||||
self.peripherals[perph as usize] = value;
|
||||
}
|
||||
fn setbank(&mut self, bank: u8) {
|
||||
self.bank = bank;
|
||||
}
|
||||
fn incbank(&mut self) {
|
||||
self.bank = self.bank + 1;
|
||||
}
|
||||
fn dump(&self, msg: &str) {
|
||||
if self.debug {
|
||||
println!("{} at 0x{:x?}, bank 0x{:x?}", msg, self.pc, self.cachebank);
|
||||
println!("Stack Length: {}", self.stack.len());
|
||||
println!("Current working memory bank: {}", self.bank);
|
||||
let mut i = 0;
|
||||
for x in &self.registers {
|
||||
if *x != 0 {
|
||||
println!("Register 0x{:x?}: 0x{:x?}", i, x);
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Instruction {
|
||||
name: &'static str,
|
||||
mask: u32,
|
||||
pattern: u32,
|
||||
masks: &'static [u32],
|
||||
shifts: &'static [u8],
|
||||
}
|
||||
|
||||
const ISET: [Instruction; 32] = [
|
||||
Instruction { name: "NOP",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x00000000,
|
||||
masks: &[], shifts: &[] },
|
||||
Instruction { name: "LOAD_MEM_REG",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x01000000,
|
||||
masks: &[0x00ffff00, 0x000000ff], shifts: &[8, 0] },
|
||||
Instruction { name: "STOR_REG_MEM",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x02000000,
|
||||
masks: &[0x00ff0000, 0x0000ffff], shifts: &[16, 0] },
|
||||
Instruction { name: "LOAD_REG_REG",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x03000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "STOR_BYTE_REG",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x04000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "SWAP_MEM_REG",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x05000000,
|
||||
masks: &[0x00ffff00, 0x000000ff], shifts: &[8, 0] },
|
||||
Instruction { name: "SWAP_REG_REG",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x06000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "INP",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x0c000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "OUT",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x0d000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "BANK",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x0e000000,
|
||||
masks: &[0x00ff0000], shifts: &[16] },
|
||||
Instruction { name: "REF",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x0f000000,
|
||||
masks: &[], shifts: &[] },
|
||||
Instruction { name: "ADD",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x10000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "SUB",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x11000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "MULT",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x12000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "DIVD",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x13000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "INC",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x14000000,
|
||||
masks: &[0x00ff0000], shifts: &[16] },
|
||||
Instruction { name: "DEC",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x15000000,
|
||||
masks: &[0x00ff0000], shifts: &[16] },
|
||||
Instruction { name: "NOT",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x16000000,
|
||||
masks: &[0x00ff0000], shifts: &[16] },
|
||||
Instruction { name: "AND",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x17000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "OR",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x18000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "XOR",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x19000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00], shifts: &[16, 8] },
|
||||
Instruction { name: "LESS",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x1a000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00, 0x000000ff], shifts: &[16, 8, 0] },
|
||||
Instruction { name: "EQU",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x1b000000,
|
||||
masks: &[0x00ff0000, 0x0000ff00, 0x000000ff], shifts: &[16, 8, 0] },
|
||||
Instruction { name: "JMP",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x20000000,
|
||||
masks: &[0x00ffff00], shifts: &[8] },
|
||||
Instruction { name: "JMR",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x21000000,
|
||||
masks: &[0x00ffff00], shifts: &[8] },
|
||||
Instruction { name: "JCZ",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x22000000,
|
||||
masks: &[0x00ffff00, 0x000000ff], shifts: &[8, 0] },
|
||||
Instruction { name: "JCZR",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x23000000,
|
||||
masks: &[0x00ffff00, 0x000000ff], shifts: &[8, 0] },
|
||||
Instruction { name: "CALL",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x24000000,
|
||||
masks: &[0x00ffff00], shifts: &[8] },
|
||||
Instruction { name: "RET",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x25000000,
|
||||
masks: &[], shifts: &[] },
|
||||
Instruction { name: "RET",
|
||||
mask: 0xff000000,
|
||||
pattern: 0x25000000,
|
||||
masks: &[], shifts: &[] },
|
||||
Instruction { name: "DUMP",
|
||||
mask: 0xff000000,
|
||||
pattern: 0xfe000000,
|
||||
masks: &[], shifts: &[] },
|
||||
Instruction { name: "HALT",
|
||||
mask: 0xff000000,
|
||||
pattern: 0xff000000,
|
||||
masks: &[], shifts: &[] }
|
||||
];
|
||||
|
||||
fn disassemble(opcode: u32) -> Instruction {
|
||||
let mut instruction;
|
||||
for instr in ISET {
|
||||
if (opcode & instr.mask) == instr.pattern {
|
||||
instruction = instr;
|
||||
return instruction;
|
||||
}
|
||||
}
|
||||
|
||||
instruction = Instruction { name: "NOP", mask: 0xff000000, pattern: 0x00000000, masks: &[], shifts: &[] };
|
||||
return instruction;
|
||||
}
|
||||
|
||||
fn decodetest(opcode: u32, expected: &str) {
|
||||
let instruction = disassemble(opcode);
|
||||
if instruction.name != expected {
|
||||
println!("Opcode 0x{} disassembled as instruction {}. Expected {}.", format!("{:x}", opcode), instruction.name, expected);
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(opcode: u32, instruction: Instruction, mut cpu: Cpu) -> Cpu {
|
||||
let mut args: Vec<u32> = vec![];
|
||||
let mut i = 0;
|
||||
if instruction.masks.len() != 0 {
|
||||
for mask in instruction.masks {
|
||||
let shift = instruction.shifts[i];
|
||||
args.push((opcode & mask) >> shift);
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if instruction.name == "NOP" {
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "LOAD_MEM_REG" {
|
||||
let value = cpu.ramread(args[0] as usize).to_owned();
|
||||
cpu.regwrite(args[1] as usize, value);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "STOR_REG_MEM" {
|
||||
let value = cpu.regread(args[0] as usize).to_owned();
|
||||
cpu.ramwrite(args[1] as usize, value);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "LOAD_REG_REG" {
|
||||
let value = cpu.ramread(args[0] as usize).to_owned();
|
||||
cpu.regwrite(args[1] as usize, value);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "STOR_BYTE_REG" {
|
||||
cpu.regwrite(args[1] as usize, args[0] as u8);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "SWAP_MEM_REG" {
|
||||
let v1 = cpu.ramread(args[0] as usize).to_owned();
|
||||
let v2 = cpu.regread(args[1] as usize).to_owned();
|
||||
cpu.ramwrite(args[0] as usize, v2);
|
||||
cpu.regwrite(args[1] as usize, v1);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "SWAP_REG_REG" {
|
||||
let mut v1 = cpu.regread(args[0] as usize).to_owned();
|
||||
let mut v2 = cpu.regread(args[1] as usize).to_owned();
|
||||
cpu.regwrite(args[0] as usize, v2);
|
||||
cpu.regwrite(args[1] as usize, v1);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "OUT" {
|
||||
let v1 = cpu.regread(args[1] as usize).to_owned();
|
||||
cpu.setperph(args[0] as u8, v1);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "INP" {
|
||||
let v1 = cpu.getperph(args[0] as u8);
|
||||
cpu.regwrite(args[1] as usize, v1);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "BANK" {
|
||||
cpu.setbank(args[0] as u8);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "REF" {
|
||||
cpu.refreshcache();
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "ADD" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a;
|
||||
if *y as u16 + *z as u16 > 0xFF {
|
||||
a = 0;
|
||||
} else {
|
||||
a = y + z;
|
||||
}
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "SUB" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a;
|
||||
if (*y as i16 - *z as i16) < 0 {
|
||||
a = 255;
|
||||
} else {
|
||||
a = y - z;
|
||||
}
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "MULT" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a;
|
||||
if *y as i16 * *z as i16 > 256 {
|
||||
a = 255;
|
||||
} else {
|
||||
a = y * z;
|
||||
}
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "DIVD" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a;
|
||||
if *z == 0 {
|
||||
a = 0;
|
||||
} else {
|
||||
a = y / z;
|
||||
}
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "INC" {
|
||||
let y = cpu.regread(args[0] as usize).to_owned();
|
||||
if y == 0xFF {
|
||||
cpu.regwrite(args[0] as usize, 0);
|
||||
} else {
|
||||
cpu.regwrite(args[0] as usize, y+1);
|
||||
}
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "NOT" {
|
||||
let y = cpu.regread(args[0] as usize).to_owned();
|
||||
cpu.regwrite(args[0] as usize, !y);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "AND" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a = y & z;
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "OR" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a = y | z;
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "XOR" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let a = y ^ z;
|
||||
cpu.regwrite(args[0] as usize, a);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "LESS" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let b = (y < z) as u8;
|
||||
cpu.regwrite(args[2] as usize, b);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "EQU" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
let z = cpu.regread(args[1] as usize);
|
||||
let b = (y == z) as u8;
|
||||
cpu.regwrite(args[2] as usize, b);
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "JMP" {
|
||||
cpu.setpc(args[0] as u16);
|
||||
} else if instruction.name == "JMR" {
|
||||
cpu.refreshcache();
|
||||
cpu.setpc(args[0] as u16);
|
||||
} else if instruction.name == "JCZ" {
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
if *y == 0 {
|
||||
cpu.setpc(args[0] as u16);
|
||||
} else {
|
||||
cpu.incrementpc();
|
||||
}
|
||||
} else if instruction.name == "JCZR" {
|
||||
cpu.refreshcache();
|
||||
let y = cpu.regread(args[0] as usize);
|
||||
if *y == 0 {
|
||||
cpu.setpc(args[0] as u16);
|
||||
} else {
|
||||
cpu.incrementpc();
|
||||
}
|
||||
} else if instruction.name == "CALL" {
|
||||
cpu.stackpush();
|
||||
cpu.refreshcache();
|
||||
cpu.setpc(args[0] as u16);
|
||||
} else if instruction.name == "RET" {
|
||||
let pointer = cpu.stackpop();
|
||||
cpu.setbank(pointer.bank);
|
||||
cpu.refreshcache();
|
||||
cpu.setpc(pointer.address + 4);
|
||||
} else if instruction.name == "DUMP" {
|
||||
cpu.dump("Program executed dump");
|
||||
cpu.incrementpc();
|
||||
} else if instruction.name == "HALT" {
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
println!("Unimplemented instruction {}. Skipping.", instruction.name);
|
||||
cpu.incrementpc();
|
||||
}
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
fn peripherals(mut cpu: Cpu, stdpoll: &StdinPoll) -> Cpu {
|
||||
let byte = stdpoll.poll_byte().unwrap_or(0x00);
|
||||
cpu.setperph(0x02, byte);
|
||||
let consoleout = cpu.getperph(0x01);
|
||||
if consoleout != 0 {
|
||||
let b = &[consoleout];
|
||||
let string = std::str::from_utf8(b).unwrap_or("B");
|
||||
print!("{}", string);
|
||||
stdout().flush().unwrap();
|
||||
cpu.setperph(0x01, 0);
|
||||
}
|
||||
|
||||
|
||||
return cpu
|
||||
}
|
||||
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
pub fn emulate(debug: bool) -> io::Result<()> {
|
||||
let stdin = 0;
|
||||
let mut stdout = io::stdout();
|
||||
|
||||
let termios = Termios::from_fd(stdin).unwrap();
|
||||
let mut new_termios = termios.clone();
|
||||
new_termios.c_lflag &= !(ICANON | ECHO);
|
||||
tcsetattr(stdin, TCSANOW, &mut new_termios).unwrap();
|
||||
|
||||
if debug {
|
||||
torture();
|
||||
println!("If you see anything about opcodes above, there's a bug!");
|
||||
}
|
||||
|
||||
let mut f = File::open("program.rom")?;
|
||||
let mut buffer: Vec<u8> = vec![];
|
||||
if debug { println!("Buffer initialised!"); }
|
||||
|
||||
f.read_to_end(&mut buffer)?;
|
||||
|
||||
let mut cpu = Cpu { memory: vec![ MemoryBank { content: vec![0; 65536] }; 256], pc: 0x0, registers: vec![0; 256], stack: vec![], peripherals: vec![0; 256], cache: MemoryBank { content: vec![0; 65536] }, bank: 0, cachebank: 0, debug: debug };
|
||||
if debug { println!("CPU object initialised!"); }
|
||||
cpu.setbank(0);
|
||||
|
||||
let mut i = 0;
|
||||
let mut x = 0;
|
||||
for byte in buffer {
|
||||
cpu.ramwrite(i, byte);
|
||||
i = i + 1;
|
||||
if i == 65536 {
|
||||
i = 0;
|
||||
x = x + 1;
|
||||
cpu.setbank(x);
|
||||
}
|
||||
}
|
||||
cpu.setbank(0);
|
||||
cpu.refreshcache();
|
||||
let stdpoll = StdinPoll::new();
|
||||
if debug {
|
||||
println!("Program loaded into memory!");
|
||||
}
|
||||
loop {
|
||||
let mut opcode = cpu.fetch();
|
||||
let mut instruction = disassemble(opcode);
|
||||
cpu = execute(opcode, instruction, cpu);
|
||||
cpu = peripherals(cpu, &stdpoll);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn torture() {
|
||||
decodetest(0x00000000, "NOP");
|
||||
decodetest(0x01000000, "LOAD_MEM_REG");
|
||||
decodetest(0x02000000, "STOR_REG_MEM");
|
||||
decodetest(0x03000000, "LOAD_REG_REG");
|
||||
decodetest(0x04000000, "STOR_BYTE_REG");
|
||||
decodetest(0x05000000, "SWAP_MEM_REG");
|
||||
decodetest(0x06000000, "SWAP_REG_REG");
|
||||
decodetest(0x0c000000, "INP");
|
||||
decodetest(0x0d000000, "OUT");
|
||||
decodetest(0x0e000000, "BANK");
|
||||
decodetest(0x0f000000, "REF");
|
||||
decodetest(0x10000000, "ADD");
|
||||
decodetest(0x11000000, "SUB");
|
||||
decodetest(0x12000000, "MULT");
|
||||
decodetest(0x13000000, "DIVD");
|
||||
decodetest(0x14000000, "INC");
|
||||
decodetest(0x15000000, "DEC");
|
||||
decodetest(0x16000000, "NOT");
|
||||
decodetest(0x17000000, "AND");
|
||||
decodetest(0x18000000, "OR");
|
||||
decodetest(0x19000000, "XOR");
|
||||
decodetest(0x1a000000, "LESS");
|
||||
decodetest(0x1b000000, "EQU");
|
||||
decodetest(0x20000000, "JMP");
|
||||
decodetest(0x21000000, "JMR");
|
||||
decodetest(0x22000000, "JCZ");
|
||||
decodetest(0x23000000, "JCZR");
|
||||
decodetest(0x24000000, "CALL");
|
||||
decodetest(0x25000000, "RET");
|
||||
decodetest(0xfe000000, "DUMP");
|
||||
decodetest(0xff000000, "HALT");
|
||||
}
|
19
src/main.rs
Normal file
19
src/main.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
mod food8;
|
||||
|
||||
use self::food8::emulate;
|
||||
use clap::Parser;
|
||||
|
||||
/// Field Of Operational Development Integral Emulator.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Shows dump information when a DUMP opcode is executed or when an erroneous operation occurs.
|
||||
#[clap(short, long)]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
emulate(args.debug);
|
||||
}
|
Reference in a new issue