Skip to content

Commit

Permalink
Scaffold COCO vm and UI app (#2)
Browse files Browse the repository at this point in the history
* Create coco-vm crate

* Init coco-ui web app

* Load ROM from web-land and run it in Rust
  • Loading branch information
belen-albeza authored Jul 15, 2024
1 parent aa6eddc commit 5d13a11
Show file tree
Hide file tree
Showing 13 changed files with 417 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = ["coco-core"]
members = ["coco-core", "coco-vm"]
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,33 @@ COCO-8 is [made of these parts](./docs/architecture.md):
- Devices, which interface with the CPU and provide input / output with peripherals or other systems (e.g. video, audio, controller input, etc.).

- A web-based GUI, for users to load and run COCO-8 roms.

## Build and run

### Requirements

You need Rust and Cargo in your system. You can get them with [`rustup`](https://www.rust-lang.org/tools/install).

You will also need the `wasm-bindgen` CLI tool.

### Build

```zsh
cargo install wasm-bindgen-cli
```

The `build.sh` shell script builds the Rust code and generates ESM modules inside `coco-ui/vendor`.

```zsh
./build.sh
```

### Run

To run COCO-8, you just need to start a local web server in `coco-ui`.

If you have npm in your system:

```zsh
npx http-server coco-ui
```
6 changes: 6 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Use cargo to build the wasm module made in Rust
cargo build --release --target wasm32-unknown-unknown

# Build ESM modules in coco-ui
wasm-bindgen target/wasm32-unknown-unknown/release/coco_vm.wasm --out-dir coco-ui/vendor --target web --no-typescript

2 changes: 0 additions & 2 deletions coco-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@
name = "coco-core"
version = "0.1.0"
edition = "2021"

[dependencies]
19 changes: 16 additions & 3 deletions coco-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<'a> Cpu<'a> {

/// Runs the code starting the PC in the given address until
/// it finds a BRK opcode
pub fn run(&mut self, addr: u16) -> u16 {
pub fn run<M: Machine>(&mut self, addr: u16, _: &mut M) -> u16 {
self.pc = addr;
loop {
let op = self.read_byte();
Expand All @@ -65,6 +65,11 @@ impl<'a> Cpu<'a> {
self.pc
}

/// Returns the current value for the program counter (PC)
pub fn pc(&self) -> u16 {
self.pc
}

#[inline]
fn read_byte(&mut self) -> u8 {
let res = self.ram[self.pc as usize];
Expand All @@ -77,6 +82,14 @@ impl<'a> Cpu<'a> {
mod tests {
use super::*;

pub struct AnyMachine {}
impl Machine for AnyMachine {
fn deo(&mut self, _: &mut Cpu, _: u8) -> bool {
false
}
fn dei(&mut self, _: &mut Cpu, _: u8) {}
}

fn zeroed_memory() -> [u8; 0x10000] {
[0_u8; 0x10000]
}
Expand All @@ -100,7 +113,7 @@ mod tests {
let mut rom = rom_from(&[0x01, 0x01, 0x00]);
let mut cpu = Cpu::new(&mut rom);

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

assert_eq!(pc, 0x03);
assert_eq!(pc, cpu.pc);
Expand All @@ -112,7 +125,7 @@ mod tests {
rom[0xffff] = 0x01;
let mut cpu = Cpu::new(&mut rom);

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

assert_eq!(pc, 0x01);
assert_eq!(pc, cpu.pc);
Expand Down
28 changes: 28 additions & 0 deletions coco-ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>👻 COCO-8</title>
<script type="module" defer src="./index.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css"
</head>
<body>
<header>
<h1>👻-8</h1>
</header>
<main>
<canvas id="coco-video" class="coco-video" width="192" height="144"></canvas>
<p>
<select name="rom" id="coco-rom-selector">
<option value="empty.rom">Empty</option>
</select>
</p>
</main>
<footer>
<p>Crafted with 🖤 by <a href="https://www.ladybenko.net">ladybenko</a>.</p>
<p>
<a href="https://github.com/PIWEEK/coco-8">Source code</a>
</p>
</footer>
</body>
</html>
47 changes: 47 additions & 0 deletions coco-ui/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import initWasm, { runRom } from "./vendor/coco_vm.js";

async function handleFile(file) {
const buffer = file.arrayBuffer();
const rom = new Uint8Array(buffer);

const output = runRom(rom);
console.debug(`PC: 0x${output.pc.toString(16)}`);
}

async function fetchRom(path) {
try {
const response = await fetch(path);
return response;
} catch (err) {
console.error(err);
}
return null;
}

function setupRomSelector(selectEl, defaultRom) {
const defaultOption = selectEl.querySelector(`option[value="${defaultRom}"]`);
defaultOption.selected = true;

selectEl.addEventListener("change", async (event) => {
const romUrl = `/roms/${event.target.value}`;
const rom = await fetchRom(romUrl);
if (rom) {
await handleFile(rom);
}
});
}

async function main() {
const _ = await initWasm("./vendor/coco_vm_bg.wasm");
const romSelector = document.querySelector("#coco-rom-selector");

const defaultRom = "empty.rom";
await setupRomSelector(romSelector, defaultRom);

const rom = await fetchRom(`/roms/${defaultRom}`);
if (rom) {
await handleFile(rom);
}
}

main();
Binary file added coco-ui/roms/empty.rom
Binary file not shown.
17 changes: 17 additions & 0 deletions coco-ui/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
body {
display: grid;
justify-content: center;
font-family: monospace;
}

select, button {
font-family: monospace;
padding: 0.25rem 1rem;
}

.coco-video {
image-rendering: pixelated;
image-rendering: crisp-edges;
width: 768px; /* x4 */
background: black;
}
Loading

0 comments on commit 5d13a11

Please sign in to comment.