Fix color bug
This commit is contained in:
+234
-106
@@ -1,31 +1,35 @@
|
||||
use crate::opcodes::Opcode;
|
||||
use crate::opcodes::InvalidOpcode;
|
||||
use crate::memory::Memory;
|
||||
use crate::word::Word;
|
||||
use crate::memory::PeekPoke;
|
||||
use crate::opcodes::InvalidOpcode;
|
||||
use crate::opcodes::Opcode;
|
||||
use crate::word::Word;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct CPU {
|
||||
memory: Memory, // Main memory, all of it
|
||||
pc: Word, // program counter, address of the low byte of the instruction
|
||||
dp: Word, // data pointer, address of the low byte of one cell above the data stack
|
||||
sp: Word, // stack pointer, address of the low byte of the return stack
|
||||
iv: Word, // interrupt vector
|
||||
memory: Memory, // Main memory, all of it
|
||||
pc: Word, // program counter, address of the low byte of the instruction
|
||||
dp: Word, // data pointer, address of the low byte of one cell above the data stack
|
||||
sp: Word, // stack pointer, address of the low byte of the return stack
|
||||
iv: Word, // interrupt vector
|
||||
int_enabled: bool, // interrupt enable bit
|
||||
halted: bool, // Whether the CPU is halted
|
||||
halted: bool, // Whether the CPU is halted
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
struct Instruction {
|
||||
opcode: Opcode,
|
||||
arg: Option<u32>,
|
||||
length: u8
|
||||
length: u8,
|
||||
}
|
||||
|
||||
impl PeekPoke for CPU {
|
||||
fn peek(&self, addr: Word) -> u8 { self.memory.peek(addr) }
|
||||
fn poke(&mut self, addr: Word, val: u8) { self.memory.poke(addr, val) }
|
||||
fn peek(&self, addr: Word) -> u8 {
|
||||
self.memory.peek(addr)
|
||||
}
|
||||
fn poke(&mut self, addr: Word, val: u8) {
|
||||
self.memory.poke(addr, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
@@ -88,7 +92,7 @@ impl CPU {
|
||||
Ok(Instruction {
|
||||
opcode,
|
||||
arg: None,
|
||||
length: 1
|
||||
length: 1,
|
||||
})
|
||||
} else {
|
||||
let mut arg = 0u32;
|
||||
@@ -100,11 +104,11 @@ impl CPU {
|
||||
Ok(Instruction {
|
||||
opcode,
|
||||
arg: Some(arg),
|
||||
length: arg_length + 1
|
||||
length: arg_length + 1,
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,20 +122,20 @@ impl CPU {
|
||||
let y = self.pop_data();
|
||||
|
||||
match instruction.opcode {
|
||||
Opcode::Add => { self.push_data(x + y) }
|
||||
Opcode::Sub => { self.push_data(y - x) }
|
||||
Opcode::Mul => { self.push_data(y * x) }
|
||||
Opcode::Div => { self.push_data(y / x) }
|
||||
Opcode::Mod => { self.push_data(y % x) }
|
||||
Opcode::And => { self.push_data(y & x) }
|
||||
Opcode::Or => { self.push_data(y | x) }
|
||||
Opcode::Xor => { self.push_data(y ^ x) }
|
||||
Opcode::Gt => { self.push_data(bool_as_word(y > x)) }
|
||||
Opcode::Lt => { self.push_data(bool_as_word(y < x)) }
|
||||
Opcode::Agt => { self.push_data(bool_as_word(word_as_signed(y) > word_as_signed(x))) }
|
||||
Opcode::Alt => { self.push_data(bool_as_word(word_as_signed(y) < word_as_signed(x))) }
|
||||
Opcode::Lshift => { self.push_data(y << x) }
|
||||
Opcode::Rshift => { self.push_data(y >> x) }
|
||||
Opcode::Add => self.push_data(x + y),
|
||||
Opcode::Sub => self.push_data(y - x),
|
||||
Opcode::Mul => self.push_data(y * x),
|
||||
Opcode::Div => self.push_data(y / x),
|
||||
Opcode::Mod => self.push_data(y % x),
|
||||
Opcode::And => self.push_data(y & x),
|
||||
Opcode::Or => self.push_data(y | x),
|
||||
Opcode::Xor => self.push_data(y ^ x),
|
||||
Opcode::Gt => self.push_data(bool_as_word(y > x)),
|
||||
Opcode::Lt => self.push_data(bool_as_word(y < x)),
|
||||
Opcode::Agt => self.push_data(bool_as_word(word_as_signed(y) > word_as_signed(x))),
|
||||
Opcode::Alt => self.push_data(bool_as_word(word_as_signed(y) < word_as_signed(x))),
|
||||
Opcode::Lshift => self.push_data(y << x),
|
||||
Opcode::Rshift => self.push_data(y >> x),
|
||||
Opcode::Arshift => {
|
||||
if y & 0x800000 != 0 {
|
||||
let mut shifted = y;
|
||||
@@ -147,14 +151,22 @@ impl CPU {
|
||||
self.push_data(x);
|
||||
self.push_data(y)
|
||||
}
|
||||
Opcode::Store => { self.memory.poke(x.into(), y as u8) }
|
||||
Opcode::Storew => { self.memory.poke24(x.into(), y) }
|
||||
Opcode::Store => self.memory.poke(x.into(), y as u8),
|
||||
Opcode::Storew => self.memory.poke24(x.into(), y),
|
||||
Opcode::Setsdp => {
|
||||
self.dp = x.into();
|
||||
self.sp = y.into()
|
||||
}
|
||||
Opcode::Brz => { if y == 0 { return self.pc + word_as_signed(x) } }
|
||||
Opcode::Brnz => { if y != 0 { return self.pc + word_as_signed(x) } }
|
||||
Opcode::Brz => {
|
||||
if y == 0 {
|
||||
return self.pc + word_as_signed(x);
|
||||
}
|
||||
}
|
||||
Opcode::Brnz => {
|
||||
if y != 0 {
|
||||
return self.pc + word_as_signed(x);
|
||||
}
|
||||
}
|
||||
_ => {} // This can never happen
|
||||
}
|
||||
self.pc + instruction.length as i32
|
||||
@@ -166,8 +178,10 @@ impl CPU {
|
||||
let x = self.pop_data();
|
||||
self.push_data(bool_as_word(x == 0))
|
||||
}
|
||||
Opcode::Pop => { self.pop_data(); }
|
||||
Opcode::Dup => { self.push_data(self.peek_data()) }
|
||||
Opcode::Pop => {
|
||||
self.pop_data();
|
||||
}
|
||||
Opcode::Dup => self.push_data(self.peek_data()),
|
||||
Opcode::Pick => {
|
||||
let index = self.pop_data();
|
||||
let val = self.memory.peek24(self.dp - (index as i32 + 1) * 3);
|
||||
@@ -181,18 +195,18 @@ impl CPU {
|
||||
self.push_data(x);
|
||||
self.push_data(z)
|
||||
}
|
||||
Opcode::Jmp => { return self.pop_data().into() }
|
||||
Opcode::Jmp => return self.pop_data().into(),
|
||||
Opcode::Jmpr => {
|
||||
let x = word_as_signed(self.pop_data());
|
||||
return self.pc + x
|
||||
return self.pc + x;
|
||||
}
|
||||
Opcode::Call => {
|
||||
let x = self.pop_data();
|
||||
self.push_call(self.pc + instruction.length as i32);
|
||||
return x.into()
|
||||
return x.into();
|
||||
}
|
||||
Opcode::Ret => { return self.pop_call().into() }
|
||||
Opcode::Hlt => { self.halted = true }
|
||||
Opcode::Ret => return self.pop_call().into(),
|
||||
Opcode::Hlt => self.halted = true,
|
||||
Opcode::Load => {
|
||||
let x = self.pop_data();
|
||||
self.push_data(self.memory.peek(x.into()) as u32)
|
||||
@@ -201,9 +215,9 @@ impl CPU {
|
||||
let x = self.pop_data();
|
||||
self.push_data(self.memory.peek24(x.into()))
|
||||
}
|
||||
Opcode::Inton => { self.int_enabled = true }
|
||||
Opcode::Intoff => { self.int_enabled = false }
|
||||
Opcode::Setiv => { self.iv = self.pop_data().into() }
|
||||
Opcode::Inton => self.int_enabled = true,
|
||||
Opcode::Intoff => self.int_enabled = false,
|
||||
Opcode::Setiv => self.iv = self.pop_data().into(),
|
||||
Opcode::Sdp => {
|
||||
self.push_data(self.sp);
|
||||
self.push_data(self.dp + 3) // The +3 accounts for the word we're about to push
|
||||
@@ -231,11 +245,28 @@ impl CPU {
|
||||
impl Opcode {
|
||||
fn is_binary(self) -> bool {
|
||||
use Opcode::*;
|
||||
self != Nop && self != Not && self != Rand && self != Pop && self != Dup && self != Pick &&
|
||||
self != Rot && self != Jmp && self != Jmpr && self != Call && self != Ret &&
|
||||
self != Hlt && self != Load && self != Loadw && self != Inton && self != Intoff &&
|
||||
self != Setiv && self != Sdp && self != Pushr && self != Popr && self != Peekr &&
|
||||
self != Debug
|
||||
self != Nop
|
||||
&& self != Not
|
||||
&& self != Rand
|
||||
&& self != Pop
|
||||
&& self != Dup
|
||||
&& self != Pick
|
||||
&& self != Rot
|
||||
&& self != Jmp
|
||||
&& self != Jmpr
|
||||
&& self != Call
|
||||
&& self != Ret
|
||||
&& self != Hlt
|
||||
&& self != Load
|
||||
&& self != Loadw
|
||||
&& self != Inton
|
||||
&& self != Intoff
|
||||
&& self != Setiv
|
||||
&& self != Sdp
|
||||
&& self != Pushr
|
||||
&& self != Popr
|
||||
&& self != Peekr
|
||||
&& self != Debug
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +278,13 @@ fn word_as_signed(word: u32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn bool_as_word(flag: bool) -> u32 { if flag { 1 } else { 0 } }
|
||||
fn bool_as_word(flag: bool) -> u32 {
|
||||
if flag {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -276,59 +313,102 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn predicate_opcode_test<P, Q>(opcode: Opcode, given: P, pred: Q) where P: FnOnce(&mut CPU), Q: FnOnce(&CPU)
|
||||
fn predicate_opcode_test<P, Q>(opcode: Opcode, given: P, pred: Q)
|
||||
where
|
||||
P: FnOnce(&mut CPU),
|
||||
Q: FnOnce(&CPU),
|
||||
{
|
||||
let mut cpu = CPU::new(Memory::default());
|
||||
given(&mut cpu);
|
||||
let new_pc = cpu.execute(Instruction{ opcode: opcode, arg: None, length: 1 });
|
||||
let new_pc = cpu.execute(Instruction {
|
||||
opcode: opcode,
|
||||
arg: None,
|
||||
length: 1,
|
||||
});
|
||||
cpu.pc = new_pc;
|
||||
pred(&mut cpu)
|
||||
}
|
||||
|
||||
fn simple_opcode_test(given: Vec<u32>, opcode: Opcode, expected: Vec<u32>) {
|
||||
predicate_opcode_test(opcode, |cpu| {
|
||||
for i in given.into_iter() { cpu.push_data(i) }
|
||||
}, |cpu| {
|
||||
assert_eq!(cpu.get_stack(), expected)
|
||||
})
|
||||
predicate_opcode_test(
|
||||
opcode,
|
||||
|cpu| {
|
||||
for i in given.into_iter() {
|
||||
cpu.push_data(i)
|
||||
}
|
||||
},
|
||||
|cpu| assert_eq!(cpu.get_stack(), expected),
|
||||
)
|
||||
}
|
||||
|
||||
fn call_stack_opcode_test(given: Vec<u32>, given_r: Vec<u32>, opcode: Opcode, expected: Vec<u32>, expected_r: Vec<u32>, pc: Word) {
|
||||
predicate_opcode_test(opcode, |cpu| {
|
||||
for i in given.into_iter() { cpu.push_data(i) }
|
||||
for i in given_r.into_iter() { cpu.push_call(i) }
|
||||
}, |cpu| {
|
||||
assert_eq!(cpu.get_stack(), expected);
|
||||
assert_eq!(cpu.get_call(), expected_r);
|
||||
assert_eq!(pc, cpu.pc)
|
||||
})
|
||||
fn call_stack_opcode_test(
|
||||
given: Vec<u32>,
|
||||
given_r: Vec<u32>,
|
||||
opcode: Opcode,
|
||||
expected: Vec<u32>,
|
||||
expected_r: Vec<u32>,
|
||||
pc: Word,
|
||||
) {
|
||||
predicate_opcode_test(
|
||||
opcode,
|
||||
|cpu| {
|
||||
for i in given.into_iter() {
|
||||
cpu.push_data(i)
|
||||
}
|
||||
for i in given_r.into_iter() {
|
||||
cpu.push_call(i)
|
||||
}
|
||||
},
|
||||
|cpu| {
|
||||
assert_eq!(cpu.get_stack(), expected);
|
||||
assert_eq!(cpu.get_call(), expected_r);
|
||||
assert_eq!(pc, cpu.pc)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn control_flow_opcode_test<A>(given: Vec<u32>, opcode: Opcode, expected_pc: A) where A: Into<Word> {
|
||||
predicate_opcode_test(opcode, |cpu| {
|
||||
for i in given.into_iter() { cpu.push_data(i) }
|
||||
}, |cpu| {
|
||||
assert_eq!(cpu.pc, expected_pc.into())
|
||||
})
|
||||
fn control_flow_opcode_test<A>(given: Vec<u32>, opcode: Opcode, expected_pc: A)
|
||||
where
|
||||
A: Into<Word>,
|
||||
{
|
||||
predicate_opcode_test(
|
||||
opcode,
|
||||
|cpu| {
|
||||
for i in given.into_iter() {
|
||||
cpu.push_data(i)
|
||||
}
|
||||
},
|
||||
|cpu| assert_eq!(cpu.pc, expected_pc.into()),
|
||||
)
|
||||
}
|
||||
|
||||
fn memory_opcode_test(given: Vec<u32>, given_memory: Vec<u8>, opcode: Opcode, expected: Vec<u32>, expected_memory: Option<Vec<u8>>) {
|
||||
predicate_opcode_test(opcode,
|
||||
|cpu| {
|
||||
for i in given.into_iter() { cpu.push_data(i) }
|
||||
for (offset, byte) in given_memory.into_iter().enumerate() {
|
||||
cpu.memory.poke(Word::from(2048 + offset as u32), byte)
|
||||
}
|
||||
},
|
||||
|cpu| {
|
||||
if let Some(expected_memory) = expected_memory {
|
||||
for (offset, byte) in expected_memory.into_iter().enumerate() {
|
||||
let actual = cpu.memory.peek(Word::from(2048 + offset as u32));
|
||||
assert_eq!(byte, actual, "At address 2048 + {}", offset)
|
||||
}
|
||||
assert_eq!(cpu.get_stack(), expected)
|
||||
}
|
||||
})
|
||||
fn memory_opcode_test(
|
||||
given: Vec<u32>,
|
||||
given_memory: Vec<u8>,
|
||||
opcode: Opcode,
|
||||
expected: Vec<u32>,
|
||||
expected_memory: Option<Vec<u8>>,
|
||||
) {
|
||||
predicate_opcode_test(
|
||||
opcode,
|
||||
|cpu| {
|
||||
for i in given.into_iter() {
|
||||
cpu.push_data(i)
|
||||
}
|
||||
for (offset, byte) in given_memory.into_iter().enumerate() {
|
||||
cpu.memory.poke(Word::from(2048 + offset as u32), byte)
|
||||
}
|
||||
},
|
||||
|cpu| {
|
||||
if let Some(expected_memory) = expected_memory {
|
||||
for (offset, byte) in expected_memory.into_iter().enumerate() {
|
||||
let actual = cpu.memory.peek(Word::from(2048 + offset as u32));
|
||||
assert_eq!(byte, actual, "At address 2048 + {}", offset)
|
||||
}
|
||||
assert_eq!(cpu.get_stack(), expected)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn to_word(val: i32) -> u32 {
|
||||
@@ -361,7 +441,7 @@ mod tests {
|
||||
fn test_basic_ops() {
|
||||
control_flow_opcode_test(vec![], Nop, 1025);
|
||||
simple_opcode_test(vec![2], Nop, vec![2]);
|
||||
predicate_opcode_test(Hlt, |_| { }, |cpu| { assert!(cpu.halted) })
|
||||
predicate_opcode_test(Hlt, |_| {}, |cpu| assert!(cpu.halted))
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -378,9 +458,27 @@ mod tests {
|
||||
#[test]
|
||||
fn test_memory() {
|
||||
memory_opcode_test(vec![2048], vec![123], Load, vec![123], None);
|
||||
memory_opcode_test(vec![2048], vec![0x12, 0x34, 0x56], Loadw, vec![0x123456], None);
|
||||
memory_opcode_test(vec![100, 2048], vec![0x12, 0x34, 0x56], Store, vec![], Some(vec![100, 0x34, 0x56]));
|
||||
memory_opcode_test(vec![0x112233, 2048], vec![0x12, 0x34, 0x56], Storew, vec![], Some(vec![0x33, 0x22, 0x11]));
|
||||
memory_opcode_test(
|
||||
vec![2048],
|
||||
vec![0x12, 0x34, 0x56],
|
||||
Loadw,
|
||||
vec![0x123456],
|
||||
None,
|
||||
);
|
||||
memory_opcode_test(
|
||||
vec![100, 2048],
|
||||
vec![0x12, 0x34, 0x56],
|
||||
Store,
|
||||
vec![],
|
||||
Some(vec![100, 0x34, 0x56]),
|
||||
);
|
||||
memory_opcode_test(
|
||||
vec![0x112233, 2048],
|
||||
vec![0x12, 0x34, 0x56],
|
||||
Storew,
|
||||
vec![],
|
||||
Some(vec![0x33, 0x22, 0x11]),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -407,16 +505,25 @@ mod tests {
|
||||
fn test_cpu_call_stack() {
|
||||
call_stack_opcode_test(vec![5000], vec![], Call, vec![], vec![1025], 5000.into());
|
||||
call_stack_opcode_test(vec![], vec![5000], Ret, vec![], vec![], 5000.into());
|
||||
call_stack_opcode_test(vec![], vec![], Sdp, vec![1024, 256 + 6], vec![], 1025.into());
|
||||
predicate_opcode_test(Setsdp,
|
||||
|cpu| {
|
||||
cpu.push_data(1000u32);
|
||||
cpu.push_data(2000u32)
|
||||
},
|
||||
|cpu| {
|
||||
assert_eq!(cpu.sp, 1000.into());
|
||||
assert_eq!(cpu.dp, 2000.into())
|
||||
});
|
||||
call_stack_opcode_test(
|
||||
vec![],
|
||||
vec![],
|
||||
Sdp,
|
||||
vec![1024, 256 + 6],
|
||||
vec![],
|
||||
1025.into(),
|
||||
);
|
||||
predicate_opcode_test(
|
||||
Setsdp,
|
||||
|cpu| {
|
||||
cpu.push_data(1000u32);
|
||||
cpu.push_data(2000u32)
|
||||
},
|
||||
|cpu| {
|
||||
assert_eq!(cpu.sp, 1000.into());
|
||||
assert_eq!(cpu.dp, 2000.into())
|
||||
},
|
||||
);
|
||||
call_stack_opcode_test(vec![123], vec![], Pushr, vec![], vec![123], 1025.into());
|
||||
call_stack_opcode_test(vec![], vec![123], Popr, vec![123], vec![], 1025.into());
|
||||
call_stack_opcode_test(vec![], vec![123], Peekr, vec![123], vec![123], 1025.into());
|
||||
@@ -471,13 +578,34 @@ mod tests {
|
||||
cpu.memory.poke_u32(0x406, 29 << 2); // hlt
|
||||
cpu.memory.poke_u32(0x407, 0xfc); // gibberish
|
||||
|
||||
assert_eq!(cpu.fetch(), Ok(Instruction { opcode: Opcode::Nop, arg: Some(2), length: 2 }));
|
||||
assert_eq!(
|
||||
cpu.fetch(),
|
||||
Ok(Instruction {
|
||||
opcode: Opcode::Nop,
|
||||
arg: Some(2),
|
||||
length: 2
|
||||
})
|
||||
);
|
||||
|
||||
cpu.pc = 0x402.into();
|
||||
assert_eq!(cpu.fetch(), Ok(Instruction { opcode: Opcode::Add, arg: Some(0x123456), length: 4 }));
|
||||
assert_eq!(
|
||||
cpu.fetch(),
|
||||
Ok(Instruction {
|
||||
opcode: Opcode::Add,
|
||||
arg: Some(0x123456),
|
||||
length: 4
|
||||
})
|
||||
);
|
||||
|
||||
cpu.pc = 0x406.into();
|
||||
assert_eq!(cpu.fetch(), Ok(Instruction { opcode: Opcode::Hlt, arg: None, length: 1 }));
|
||||
assert_eq!(
|
||||
cpu.fetch(),
|
||||
Ok(Instruction {
|
||||
opcode: Opcode::Hlt,
|
||||
arg: None,
|
||||
length: 1
|
||||
})
|
||||
);
|
||||
|
||||
cpu.pc = 0x407.into();
|
||||
assert_eq!(cpu.fetch(), Err(InvalidOpcode(0x3f)));
|
||||
|
||||
Reference in New Issue
Block a user