Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ jobs:
- name: Install cargo-docs-rs
uses: dtolnay/install@cargo-docs-rs
- name: cargo docs-rs
run: cargo docs-rs
run: cargo docs-rs -p chip8-interpreter
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,19 @@ jobs:
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test_gleam:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "27.1.2"
gleam-version: "1.11.1"
rebar3-version: "3"
# elixir-version: "1"
- run: gleam deps download
working-directory: ./ui
- run: gleam test
working-directory: ./ui
- run: gleam format --check src test
working-directory: ./ui
62 changes: 60 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 10 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
[package]
name = "chip8"
version = "0.1.0"
edition = "2024"
categories = ["no-std"]

[dependencies]
clap = { version = "4.5.30", features = ["derive"] }
fastrand = "2.3.0"
itertools = "0.14.0"
ratatui = "0.29.0"
[workspace]
members = ["crates/chip8-interpreter", "crates/chip8-cli"]
resolver = "2"


[[bin]]
name = "chip8"
[workspace.package]
authors = ["Rob Hand <[email protected]>"]
version = "0.0.1"
edition = "2024"
license = "MIT"
homepage = "https://github.com/sinon/chip8"
repository = "https://github.com/sinon/chip8"
15 changes: 10 additions & 5 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ set dotenv-load
build:
CARGO_PROFILE_DEV_CODEGEN_BACKEND=cranelift cargo +nightly build -Zcodegen-backend
format:
@cargo fmt --version
cargo fmt
@cargo fmt --version
cargo fmt
lint:
@cargo clippy --version
cargo clippy -- -D warnings -W clippy::pedantic -W clippy::nursery
cargo doc
@cargo clippy --version
cargo clippy -- -D warnings -W clippy::pedantic -W clippy::nursery
cargo doc
test:
cargo nextest run --all-targets --no-fail-fast

t:test

build_lib:
@cargo --version
cargo build -p chip8-interpreter --release --features="rustler"
cp ./target/release/libchip8_interpreter.dylib ./priv/libchip8_interpreter.so
23 changes: 23 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# TODO Notes

Want to implement a UI using gleam but have the core chip8 interpreter still be driven by the rust lib.

To achieve this:
- [x] broken rust code up into seperate crates (lib and original clap CLI)
- [x] Added `rustler` feature to core lib behind feature flag
- [x] Build the dynamic lib for us in `gleam`
- [ ] Figure out how to expose the relevant parts of the Chip8Emulator to erlang, blog did the nix plumbing manually but `rustler` on erl side might handle this instead?
- [ ] Call the needed methods in gleam to verify:
- `tick`
- `tick_timers`
- `keypress`
- `get_display`
- [ ] Build a UI in gleam ???
- Potential big gap here, I had hoped to use `lustre` but that of course leverages the javascript side of `gleam` not the erlang side where I have the rust FFI available. I can't seem to find anything about WASM on gleam/lustre which would be the obvious replacement to `rustler`


## Resources

- https://www.jonashietala.se/blog/2024/01/11/exploring_the_gleam_ffi/
- https://docs.rs/rustler/latest/rustler/attr.resource_impl.html
- https://hexdocs.pm/rustler/readme.html
10 changes: 10 additions & 0 deletions crates/chip8-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "chip8-cli"
version = "0.1.0"
edition = "2024"

[dependencies]
clap = { version = "4.5.30", features = ["derive"] }
itertools = "0.14.0"
ratatui = "0.29.0"
chip8-interpreter = { path = "../chip8-interpreter" }
8 changes: 4 additions & 4 deletions src/main.rs → crates/chip8-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::{io, time::Duration};

use chip8::{Chip8Emulator, SCREEN_WIDTH};
use chip8_interpreter::{Chip8Emulator, SCREEN_WIDTH};
use clap::Parser;
use clap::Subcommand;
use itertools::Itertools;
Expand Down Expand Up @@ -55,9 +55,9 @@ fn main() -> io::Result<()> {
impl App {
#[must_use]
pub fn new(command: &Commands) -> Self {
let pong = include_bytes!("./roms/PONG");
let guess = include_bytes!("./roms/GUESS");
let maze = include_bytes!("./roms/MAZE");
let pong = include_bytes!("../../roms/PONG");
let guess = include_bytes!("../../roms/GUESS");
let maze = include_bytes!("../../roms/MAZE");
let mut emulator = Chip8Emulator::new();
match command {
Commands::Pong => emulator.load_data(pong),
Expand Down
16 changes: 16 additions & 0 deletions crates/chip8-interpreter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "chip8-interpreter"
version = "0.1.0"
edition = "2024"
categories = ["no-std"]

[features]
default = []
rustler = ["dep:rustler"]

[dependencies]
fastrand = "2.3.0"
rustler = { version = "0.36.2", optional = true }

[lib]
crate-type = ["lib", "dylib"]
13 changes: 10 additions & 3 deletions src/lib.rs → crates/chip8-interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ impl Default for Chip8Emulator {
}
}

#[cfg(feature = "rustler")]
impl rustler::Resource for Chip8Emulator {}

#[cfg(feature = "rustler")]
#[rustler::resource_impl]
impl Chip8Emulator {}

impl Chip8Emulator {
#[must_use]
pub fn new() -> Self {
Expand Down Expand Up @@ -584,7 +591,7 @@ mod tests {
#[test]
fn load_rom_pong() {
let mut cpu = Chip8Emulator::new();
let bytes = include_bytes!("./roms/PONG");
let bytes = include_bytes!("../../roms/PONG");
cpu.load_data(bytes);
let mut counter = 0;
while counter < 10000 {
Expand All @@ -599,7 +606,7 @@ mod tests {
#[test]
fn load_rom_guess() {
let mut cpu = Chip8Emulator::new();
let bytes = include_bytes!("./roms/GUESS");
let bytes = include_bytes!("../../roms/GUESS");
cpu.load_data(bytes);
let mut counter = 0;
while counter < 10000 {
Expand All @@ -614,7 +621,7 @@ mod tests {
#[test]
fn load_rom_maze() {
let mut cpu = Chip8Emulator::new();
let bytes = include_bytes!("./roms/MAZE");
let bytes = include_bytes!("../../roms/MAZE");
cpu.load_data(bytes);
let mut counter = 0;
while counter < 10000 {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added priv/libchip8_interpreter.so
Binary file not shown.
4 changes: 4 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump
24 changes: 24 additions & 0 deletions ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frontend

[![Package Version](https://img.shields.io/hexpm/v/frontend)](https://hex.pm/packages/frontend)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/frontend/)

```sh
gleam add frontend@1
```
```gleam
import frontend

pub fn main() -> Nil {
// TODO: An example of the project in use
}
```

Further documentation can be found at <https://hexdocs.pm/frontend>.

## Development

```sh
gleam run # Run the project
gleam test # Run the tests
```
19 changes: 19 additions & 0 deletions ui/gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name = "ui"
version = "1.0.0"

# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# description = ""
# licences = ["Apache-2.0"]
# repository = { type = "github", user = "", repo = "" }
# links = [{ title = "Website", href = "" }]
#
# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.

[dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
11 changes: 11 additions & 0 deletions ui/manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
{ name = "gleam_stdlib", version = "0.62.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "DC8872BC0B8550F6E22F0F698CFE7F1E4BDA7312FDEB40D6C3F44C5B706C8310" },
{ name = "gleeunit", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "63022D81C12C17B7F1A60E029964E830A4CBD846BBC6740004FC1F1031AE0326" },
]

[requirements]
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
10 changes: 10 additions & 0 deletions ui/src/libchip8_interpreter.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-module(libchip8_interpreter).
-export([Chip8Emulator/0]).
-nifs([Chip8Emulator/0]).
-on_load(init/0).

init() ->
ok = erlang:load_nif("priv/libchip8_interpreter", 0).

truly_random() ->
exit(nif_library_not_loaded).
5 changes: 5 additions & 0 deletions ui/src/ui.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import gleam/io

pub fn main() -> Nil {
io.println("Hello from frontend!")
}
13 changes: 13 additions & 0 deletions ui/test/ui_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import gleeunit

pub fn main() -> Nil {
gleeunit.main()
}

// gleeunit test functions end in `_test`
pub fn hello_world_test() {
let name = "Joe"
let greeting = "Hello, " <> name <> "!"

assert greeting == "Hello, Joe!"
}