Skip to content

Commit

Permalink
Implement basic arith opcodes and add example rom with a moving sprite (
Browse files Browse the repository at this point in the history
#9)

* Implement DEI opcode

* Add DUP2 opcode

* Add DUP opcode

* Add INC opcode

* Add SUB opcode

* Add MUL opcode

* Add DIV opcode

* Add ADD opcode

* Implement video vector callback and add example rom
  • Loading branch information
belen-albeza authored Jul 18, 2024
1 parent 28d4058 commit d174d1f
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 5 deletions.
172 changes: 172 additions & 0 deletions coco-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,16 @@ impl Cpu {
let op = self.read_byte();
match op {
opcodes::BRK => break,
opcodes::INC => self.op_inc(),
opcodes::DUP => self.op_dup(),
opcodes::DUP2 => self.op_dup2(),
opcodes::DEI => self.op_dei(machine),
opcodes::DEO => self.op_deo(machine),
opcodes::DEO2 => self.op_deo2(machine),
opcodes::ADD => self.op_add(),
opcodes::SUB => self.op_sub(),
opcodes::MUL => self.op_mul(),
opcodes::DIV => self.op_div(),
opcodes::PUSH => self.op_push(),
opcodes::PUSH2 => self.op_push2(),
_ => {}
Expand Down Expand Up @@ -103,6 +111,26 @@ impl Cpu {
u16::from_be_bytes([hi, lo])
}

#[inline]
fn op_inc(&mut self) {
let value = self.stack.pop_byte();
self.stack.push_byte(value.wrapping_add(1));
}

#[inline]
fn op_dup(&mut self) {
let value = self.stack.pop_byte();
self.stack.push_byte(value);
self.stack.push_byte(value);
}

#[inline]
fn op_dup2(&mut self) {
let value = self.stack.pop_short();
self.stack.push_short(value);
self.stack.push_short(value);
}

#[inline]
fn op_push(&mut self) {
let value = self.read_byte();
Expand All @@ -115,6 +143,15 @@ impl Cpu {
self.stack.push_short(value);
}

#[inline]
fn op_dei(&mut self, machine: &mut impl Machine) {
let target = self.stack.pop_byte();
self.stack.push_byte(self.devices[target as usize]);

// callback for I/O
machine.dei(self, target);
}

#[inline]
fn op_deo(&mut self, machine: &mut impl Machine) {
let target = self.stack.pop_byte();
Expand All @@ -140,6 +177,38 @@ impl Cpu {
// callback for I/0
machine.deo(self, target);
}

#[inline]
fn op_add(&mut self) {
let b = self.stack.pop_byte();
let a = self.stack.pop_byte();
let value = a.wrapping_add(b);
self.stack.push_byte(value);
}

#[inline]
fn op_sub(&mut self) {
let b = self.stack.pop_byte();
let a = self.stack.pop_byte();
let value = a.wrapping_sub(b);
self.stack.push_byte(value);
}

#[inline]
fn op_mul(&mut self) {
let b = self.stack.pop_byte();
let a = self.stack.pop_byte();
let value = a.wrapping_mul(b);
self.stack.push_byte(value);
}

#[inline]
fn op_div(&mut self) {
let b = self.stack.pop_byte();
let a = self.stack.pop_byte();
let value = a.wrapping_div(b);
self.stack.push_byte(value);
}
}

impl fmt::Display for Cpu {
Expand Down Expand Up @@ -201,12 +270,51 @@ mod tests {
assert_eq!(pc, cpu.pc);
}

#[test]
fn inc_opcode() {
let rom = rom_from(&[PUSH, 0xff, INC, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x104);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0x00);
}

#[test]
fn dup_opcode() {
let rom = rom_from(&[PUSH, 0xab, DUP, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x104);
assert_eq!(cpu.stack.len(), 2);
assert_eq!(cpu.stack.byte_at(0), 0xab);
assert_eq!(cpu.stack.byte_at(1), 0xab);
}

#[test]
fn dup2_opcode() {
let rom = rom_from(&[PUSH2, 0xab, 0xcb, DUP2, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x105);
assert_eq!(cpu.stack.len(), 4);
assert_eq!(cpu.stack.short_at(0), 0xabcb);
assert_eq!(cpu.stack.short_at(2), 0xabcb);
}

#[test]
fn push_opcode() {
let rom = rom_from(&[PUSH, 0xab, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x103);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0xab);
Expand All @@ -218,17 +326,32 @@ mod tests {
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x104);
assert_eq!(cpu.stack.len(), 2);
assert_eq!(cpu.stack.short_at(0), 0xabcd);
}

#[test]
fn dei_opcode() {
let rom = rom_from(&[PUSH, 0x10, DEI, BRK]);
let mut cpu = Cpu::new(&rom);
cpu.devices[0x10] = 0xab;

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x104);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0xab);
}

#[test]
fn deo_opcode() {
let rom = rom_from(&[PUSH, 0xab, PUSH, 0x02, DEO, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x106);
assert_eq!(cpu.stack.len(), 0);
assert_eq!(cpu.devices[0x02], 0xab);
Expand All @@ -241,10 +364,59 @@ mod tests {
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x107);
assert_eq!(cpu.stack.len(), 0);
assert_eq!(cpu.devices[0x00], 0xab);
assert_eq!(cpu.devices[0x01], 0xcd);
// TODO: check AnyMachine.deo has been called with 0xab as target arg
}

#[test]
fn add_opcode() {
let rom = rom_from(&[PUSH, 0xab, PUSH, 0x02, ADD, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x106);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0xad);
}

#[test]
fn sub_opcode() {
let rom = rom_from(&[PUSH, 0xab, PUSH, 0x02, SUB, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x106);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0xa9);
}

#[test]
fn mul_opcode() {
let rom = rom_from(&[PUSH, 0x03, PUSH, 0x02, MUL, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x106);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0x06);
}

#[test]
fn div_opcode() {
let rom = rom_from(&[PUSH, 0x07, PUSH, 0x02, DIV, BRK]);
let mut cpu = Cpu::new(&rom);

let pc = cpu.run(0x100, &mut AnyMachine {});

assert_eq!(pc, 0x106);
assert_eq!(cpu.stack.len(), 1);
assert_eq!(cpu.stack.byte_at(0), 0x03);
}
}
8 changes: 8 additions & 0 deletions coco-core/src/opcodes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
pub const BRK: u8 = 0x00;
pub const INC: u8 = 0x01;
pub const DUP: u8 = 0x06;
pub const DUP2: u8 = 0x26;
pub const DEI: u8 = 0x16;
pub const DEO: u8 = 0x17;
pub const ADD: u8 = 0x18;
pub const SUB: u8 = 0x19;
pub const MUL: u8 = 0x1a;
pub const DIV: u8 = 0x1b;
pub const DEO2: u8 = 0x37;
pub const PUSH: u8 = 0x80;
pub const PUSH2: u8 = 0xa0;
1 change: 1 addition & 0 deletions coco-ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ <h1>👻-8</h1>
<option value="put_pixel.rom">Pixel (single)</option>
<option value="pixel_fill.rom">Pixel (fill)</option>
<option value="sprite.rom">Sprite</option>
<option value="sprite_move.rom">Moving Sprite</option>
</select>

<div>
Expand Down
2 changes: 1 addition & 1 deletion coco-ui/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function main() {
const _ = await initWasm("./vendor/coco_ui_bg.wasm");
const romSelector = document.querySelector("#coco-rom-selector");

const defaultRom = "sprite.rom";
const defaultRom = "sprite_move.rom";
setupRomSelector(romSelector, defaultRom);
setupControls();

Expand Down
Binary file added coco-ui/roms/sprite_move.rom
Binary file not shown.
3 changes: 1 addition & 2 deletions coco-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ impl Vm {
}

pub fn on_video(&mut self, cpu: &mut Cpu) -> DeviceOutput {
// TODO: call video vector
cpu.run(0x200, self);
cpu.run(self.video.vector(), self);
self.output()
}

Expand Down
17 changes: 16 additions & 1 deletion coco-vm/src/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ impl Ports for VideoPorts {
}

impl VideoPorts {
#[allow(dead_code)]
const VECTOR: u8 = 0x00;
const X: u8 = 0x02;
const Y: u8 = 0x03;
Expand All @@ -32,6 +31,7 @@ pub struct VideoDevice {
pub layers: [VideoBuffer; 2],
is_dirty: bool,
buffer: VideoBuffer,
vector: u16,
}

impl VideoDevice {
Expand All @@ -40,9 +40,14 @@ impl VideoDevice {
layers: [[0x00; VIDEO_BUFFER_LEN]; 2],
is_dirty: true,
buffer: [0x00; VIDEO_BUFFER_LEN],
vector: 0,
}
}

pub fn vector(&self) -> u16 {
self.vector
}

pub fn pixels(&mut self) -> &VideoBuffer {
if std::mem::take(&mut self.is_dirty) {
self.refresh_buffer();
Expand Down Expand Up @@ -76,6 +81,15 @@ impl VideoDevice {
u16::from_be_bytes([hi, lo])
}

#[inline]
fn deo_vector(&mut self, cpu: &mut Cpu) {
let ports = cpu.device_page::<VideoPorts>();
let hi = ports[VideoPorts::VECTOR as usize];
let lo = ports[VideoPorts::VECTOR as usize + 1];

self.vector = u16::from_be_bytes([hi, lo]);
}

fn deo_pixel(&mut self, cpu: &mut Cpu) {
self.is_dirty = true;

Expand Down Expand Up @@ -162,6 +176,7 @@ impl VideoDevice {
impl Device for VideoDevice {
fn deo(&mut self, cpu: &mut Cpu, target: u8) {
match target {
VideoPorts::VECTOR => self.deo_vector(cpu),
VideoPorts::X => {}
VideoPorts::Y => {}
VideoPorts::PIXEL => self.deo_pixel(cpu),
Expand Down
1 change: 0 additions & 1 deletion coco-vm/tests/vm_cpu_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ fn test_deo_sprite() {

let _ = vm.on_reset(&mut cpu);
let buffer = vm.pixels();
// println!("{:?}", buffer);

assert_eq!(buffer[0..8], [0x01; 8]);
}

0 comments on commit d174d1f

Please sign in to comment.