Compare commits

..

28 Commits

Author SHA1 Message Date
0695c23a78 move raylib rs to ack 2026-01-31 19:30:02 -06:00
c3df8672c8 update built with to include raylib rust bindings 2026-01-31 19:28:01 -06:00
47cce19940 acknowledge chip8 archive 2026-01-31 16:43:34 -06:00
d8b3bcae19 fix formatting and spelling issues 2026-01-31 16:40:34 -06:00
7ede5aeafa add program aquasition info 2026-01-31 16:38:26 -06:00
08a2ec4459 fix license URL 2026-01-31 16:26:41 -06:00
58901521e5 readme license shield 2026-01-31 16:25:35 -06:00
fed6762288 more readme changes 2026-01-31 16:20:56 -06:00
342eb4557d Merge branch 'main' of git:/jlanders/chip8-em 2026-01-31 16:07:47 -06:00
63fe937481 maybe it's fixed now :) 2026-01-31 16:07:35 -06:00
73422b4072 maybe it's fixed now :) 2026-01-31 16:06:50 -06:00
efba92b8c7 Merge branch 'main' of git:/jlanders/chip8-em 2026-01-31 16:05:24 -06:00
25c6d9beaa fix screenshot readme 2026-01-31 16:05:06 -06:00
38ec89813d fix screenshot readme 2026-01-31 16:04:39 -06:00
596e60abeb Merge branch 'main' of git:/jlanders/chip8-em 2026-01-31 16:04:18 -06:00
ad50485d37 Merge branch 'main' of git:/jlanders/chip8-em 2026-01-31 16:03:53 -06:00
ce5ff7ee24 Merge branch 'main' of git:/jlanders/chip8-em 2026-01-31 16:03:14 -06:00
124493fd11 pushing readme changes to view 2026-01-31 16:02:42 -06:00
0ff0b6c596 pushing readme changes to view 2026-01-31 16:00:54 -06:00
253095bc6d logic changed after changing to assert 2026-01-31 15:37:11 -06:00
063ca15b37 fix most clippy complaints 2026-01-31 15:32:07 -06:00
266c685d1b change package name 2026-01-31 14:44:48 -06:00
82323846ec clippy fixes 1 manual before auto apply 2026-01-31 14:43:36 -06:00
b3eed758cc set clippy level for code cleanup 2026-01-31 13:33:12 -06:00
d6aeda49ca add changes for readme 2026-01-31 01:43:04 -06:00
877ee5de9a ignore programs i add locally 2026-01-31 01:42:57 -06:00
49e4914c00 updated contributors shield 2026-01-31 01:32:52 -06:00
a3b1dcf32d add license 2026-01-31 01:28:46 -06:00
14 changed files with 217 additions and 304 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target
/programs

2
Cargo.lock generated
View File

@@ -177,7 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chip-8-em"
name = "chip8-em"
version = "0.1.0"
dependencies = [
"clap",

View File

@@ -1,5 +1,5 @@
[package]
name = "chip-8-em"
name = "chip8-em"
version = "0.1.0"
edition = "2024"

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2026 Cole Landers
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

202
README.md
View File

@@ -1,12 +1,4 @@
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
<a id="readme-top"></a>
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
*** or simply open an issue with the tag "enhancement".
*** Don't forget to give the project a star!
*** Thanks again! Now go create something AMAZING! :D
-->
@@ -18,35 +10,25 @@
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![project_license][license-shield]][license-url]
[![LinkedIn][linkedin-shield]][linkedin-url]
[![MIT][license-shield]][license-url]
<!-- [![LinkedIn][linkedin-shield]][linkedin-url] -->
<!-- PROJECT LOGO -->
<br />
<div align="center">
<a href="https://github.com/github_username/repo_name">
<!-- <a href="https://git.colelanders.dev/jlanders/chip8-em">
<img src="images/logo.png" alt="Logo" width="80" height="80">
</a>
</a> -->
<h3 align="center">project_title</h3>
<h3 align="center">Chip8 Emulator</h3>
<p align="center">
project_description
Chip8 Emulator built in Rust.
<br />
<a href="https://github.com/github_username/repo_name"><strong>Explore the docs »</strong></a>
<!-- <a href="https://git.colelanders.dev/jlanders/chip8-em"><strong>Explore the docs »</strong></a>
<br /> -->
<br />
<br />
<a href="https://github.com/github_username/repo_name">View Demo</a>
&middot;
<a href="https://github.com/github_username/repo_name/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
&middot;
<a href="https://github.com/github_username/repo_name/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
<a href="https://git.colelanders.dev/jlanders/chip8-em">View Demo</a>
</p>
</div>
@@ -67,6 +49,7 @@
<ul>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#running">Running</a></li>
</ul>
</li>
<li><a href="#usage">Usage</a></li>
@@ -83,74 +66,78 @@
<!-- ABOUT THE PROJECT -->
## About The Project
[![Product Name Screen Shot][product-screenshot]](https://example.com)
Here's a blank template to get started. To avoid retyping too much info, do a search and replace with your text editor for the following: `github_username`, `repo_name`, `twitter_handle`, `linkedin_username`, `email_client`, `email`, `project_title`, `project_description`, `project_license`
![Chip8 Emulator Screen Shot][product-screenshot]
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Built With
* [![Next][Next.js]][Next-url]
* [![React][React.js]][React-url]
* [![Vue][Vue.js]][Vue-url]
* [![Angular][Angular.io]][Angular-url]
* [![Svelte][Svelte.dev]][Svelte-url]
* [![Laravel][Laravel.com]][Laravel-url]
* [![Bootstrap][Bootstrap.com]][Bootstrap-url]
* [![JQuery][JQuery.com]][JQuery-url]
* [![Rust][Rust]][Rust-url]
* [![Raylib][Raylib]][Raylib-url]
* [![Clap][Clap]][Clap-url]
* [![Rodio][Rodio]][Rodio-url]
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- GETTING STARTED -->
## Getting Started
This is an example of how you may give instructions on setting up your project locally.
To get a local copy up and running follow these simple example steps.
You can find pre-built binaries for Windows/MacOS/Linux -> [releases](https://git.colelanders.dev/jlanders/chip8-em/releases)
To run the code get a local copy up and running follow these simple example steps.
### Prerequisites
This is an example of how to list things you need to use the software and how to install them.
* npm
```sh
* rust - Follow the official install guide [here](https://rust-lang.org/tools/install/)
<!-- ```sh
npm install npm@latest -g
```
``` -->
### Installation
1. Get a free API Key at [https://example.com](https://example.com)
2. Clone the repo
1. Clone the repo
```sh
git clone https://github.com/github_username/repo_name.git
git clone https://git.colelanders.dev/jlanders/chip8-em.git
```
3. Install NPM packages
2. Navigate to the repo directory
```sh
npm install
cd chip8-em
```
4. Enter your API in `config.js`
```js
const API_KEY = 'ENTER YOUR API';
```
5. Change git remote url to avoid accidental pushes to base project
3. Build the project
```sh
git remote set-url origin github_username/repo_name
git remote -v # confirm the changes
cargo build
```
### Running
Choose the method that best fits your needs. If you just want to use the emulator, I recommend using the **Pre-built Binaries**.
#### Prerequisite - Obtaining a Chip8 Rom
*Note - as of now the project only supports chip8 programs. Chip8 extensions such as SUPER-Chip or XO-Chip will not work with v1*
This emulator requires you either:
* Create your own ROM using a tool like [Octo](https://johnearnest.github.io/Octo/)
* Download a ROM made by the community
* Navigate to [johnearnest.github.io/chip8Archive/](https://johnearnest.github.io/chip8Archive/?sort=platform)
* Select a chip8 ROM that seems interesting
* Click "Download Rom"
#### 🚀 Option 1: Pre-built Binaries (Fastest)
**WIP**
#### 🛠️ Option 2: Built Locally (From Source)
**WIP**
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- USAGE EXAMPLES -->
## Usage
Use this space to show useful examples of how a project can be used. Additional screenshots, code examples and demos work well in this space. You may also link to more resources.
**WIP**
_For more examples, please refer to the [Documentation](https://example.com)_
<!-- Use this space to show useful examples of how a project can be used. Additional screenshots, code examples and demos work well in this space. You may also link to more resources. -->
<!-- _For more examples, please refer to the [Documentation](https://example.com)_ -->
<p align="right">(<a href="#readme-top">back to top</a>)</p>
@@ -159,45 +146,24 @@ _For more examples, please refer to the [Documentation](https://example.com)_
<!-- ROADMAP -->
## Roadmap
- [ ] Feature 1
- [ ] Feature 2
- [ ] Feature 3
- [ ] Nested Feature
See the [open issues](https://github.com/github_username/repo_name/issues) for a full list of proposed features (and known issues).
- [ ] Super-Chip8 extension
- [ ] XO-CHIP extension
- [ ] More Configuration
- [ ] Window Resizing
- [ ] Allow rebinding keys via GUI
- [ ] Replacing default sound via GUI
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- CONTRIBUTING -->
## Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
Don't forget to give the project a star! Thanks again!
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Top contributors:
<a href="https://github.com/github_username/repo_name/graphs/contributors">
<img src="https://contrib.rocks/image?repo=github_username/repo_name" alt="contrib.rocks image" />
</a>
Since this is a personal project, this specific repo is not open for contributions. However, please feel free to clone the project and use it as you wish!
<!-- LICENSE -->
## License
Distributed under the project_license. See `LICENSE.txt` for more information.
Distributed under the MIT License. See `LICENSE` for more information.
<p align="right">(<a href="#readme-top">back to top</a>)</p>
@@ -206,9 +172,9 @@ Distributed under the project_license. See `LICENSE.txt` for more information.
<!-- CONTACT -->
## Contact
Your Name - [@twitter_handle](https://twitter.com/twitter_handle) - email@email_client.com
Cole Landers - jclanders@pm.me
Project Link: [https://github.com/github_username/repo_name](https://github.com/github_username/repo_name)
Project Link: [https://git.colelanders.dev/jlanders/chip8-em](https://git.colelanders.dev/jlanders/chip8-em)
<p align="right">(<a href="#readme-top">back to top</a>)</p>
@@ -217,9 +183,8 @@ Project Link: [https://github.com/github_username/repo_name](https://github.com/
<!-- ACKNOWLEDGMENTS -->
## Acknowledgments
* []()
* []()
* []()
* [raylib-rs](https://github.com/raylib-rs/raylib-rs) - For raylib rust bindings
* [John Earnest Chip8 Archive](https://github.com/JohnEarnest/chip8Archive) - For amazing tools and support of the Chip8 ecosystem
<p align="right">(<a href="#readme-top">back to top</a>)</p>
@@ -227,33 +192,22 @@ Project Link: [https://github.com/github_username/repo_name](https://github.com/
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/github_username/repo_name.svg?style=for-the-badge
[contributors-url]: https://github.com/github_username/repo_name/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/github_username/repo_name.svg?style=for-the-badge
[forks-url]: https://github.com/github_username/repo_name/network/members
[stars-shield]: https://img.shields.io/github/stars/github_username/repo_name.svg?style=for-the-badge
[stars-url]: https://github.com/github_username/repo_name/stargazers
[issues-shield]: https://img.shields.io/github/issues/github_username/repo_name.svg?style=for-the-badge
[issues-url]: https://github.com/github_username/repo_name/issues
[license-shield]: https://img.shields.io/github/license/github_username/repo_name.svg?style=for-the-badge
[license-url]: https://github.com/github_username/repo_name/blob/master/LICENSE.txt
<!-- [license-shield]: https://img.shields.io/github/license/github_username/repo_name.svg?style=for-the-badge
[license-url]: https://git.colelanders.dev/jlanders/chip8-em/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
[linkedin-url]: https://linkedin.com/in/linkedin_username
[linkedin-url]: https://linkedin.com/in/linkedin_username-->
[license-shield]: https://img.shields.io/badge/license-MIT-blue
[license-url]: https://git.colelanders.dev/jlanders/chip8-em/src/branch/main/LICENSE
[product-screenshot]: images/screenshot.png
<!-- Shields.io badges. You can a comprehensive list with many more badges at: https://github.com/inttter/md-badges -->
[Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white
[Next-url]: https://nextjs.org/
[React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB
[React-url]: https://reactjs.org/
[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D
[Vue-url]: https://vuejs.org/
[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white
[Angular-url]: https://angular.io/
[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00
[Svelte-url]: https://svelte.dev/
[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white
[Laravel-url]: https://laravel.com
[Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white
[Bootstrap-url]: https://getbootstrap.com
[JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white
[JQuery-url]: https://jquery.com
[Rust]: https://img.shields.io/badge/rust-%23E32F26.svg?style=for-the-badge&logo=rust&logoColor=white
[Rust-url]: https://www.rust-lang.org/
[Raylib]: https://img.shields.io/badge/raylib-white?style=for-the-badge&logo=target&logoColor=black
[Raylib-url]: https://www.raylib.com/
[Clap]: https://img.shields.io/badge/clap-blue?style=for-the-badge&logo=command-line&logoColor=white
[Clap-url]: https://github.com/clap-rs/clap
[Rodio]: https://img.shields.io/badge/rodio-orange?style=for-the-badge&logo=audio-bus&logoColor=white
[Rodio-url]: https://github.com/RustAudio/rodio

BIN
images/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,5 +1,4 @@
mod cpu;
mod debug;
mod gpu;
mod input;
mod memory;
@@ -7,14 +6,15 @@ mod renderer;
use rodio::source::{SineWave, Source};
static MEMORY_LIMIT: i32 = 4096;
static STACK_LIMIT: i32 = 16;
static VARIABLE_REGISTER_COUNT: i32 = 16;
static TIMER_TICK_RATE: u32 = 60;
static DESIRED_FPS: u32 = 60;
static CYCLES_PER_FRAME: u32 = 10;
static MEMORY_LIMIT: u16 = 4096;
static STACK_LIMIT: u8 = 16;
static VARIABLE_REGISTER_COUNT: u8 = 16;
static TIMER_TICK_RATE: f32 = 60.0;
static DESIRED_FPS: f32 = 60.0;
static CYCLES_PER_FRAME: f32 = 10.0;
static INSTRUCTION_BYTE_SIZE: usize = 2;
#[derive(Clone)]
#[derive(Debug, Clone)]
struct Chip8State {
// Flags
eti_600_flag: bool,
@@ -39,6 +39,8 @@ struct Chip8State {
input: [bool; 16],
}
#[derive(Debug, Clone, Copy)]
#[allow(clippy::struct_excessive_bools)]
pub struct Chip8Quirks {
pub vf_reset: bool,
pub memory: bool,
@@ -48,7 +50,7 @@ pub struct Chip8Quirks {
pub jumping: bool,
}
pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, quirks: &Chip8Quirks, debug_mode: bool) {
pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, quirks: Chip8Quirks) {
let mut state = Chip8State {
eti_600_flag: false,
vblank_waiting: false,
@@ -64,42 +66,44 @@ pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, quirks: &Chip8Quirks, de
input: [false; 16],
};
if !state.eti_600_flag {
state.r_pc = 0x200;
} else {
if state.eti_600_flag {
state.r_pc = 0x600;
} else {
state.r_pc = 0x200;
}
// Load Program
let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref());
// Run Program
start(&mut state, &quirks, debug_mode);
start(state, quirks);
}
fn start(state: &mut Chip8State, quirks: &Chip8Quirks, debug_mode: bool) {
fn start(mut state: Chip8State, quirks: Chip8Quirks) {
// TODO rip out as much RL stuff from here and put into renderer
// Init Rendering Pipeline
let (mut rl, thread) = raylib::init()
.size(renderer::DISPLAY_WIDTH, renderer::DISPLAY_HEIGHT)
.size(
i32::from(renderer::DISPLAY_WIDTH),
i32::from(renderer::DISPLAY_HEIGHT),
)
.title("Chip8 Emu")
.build();
rl.set_target_fps(DESIRED_FPS); // Should see if i can get the users hz
if !debug_mode {
#[allow(clippy::cast_sign_loss)]
rl.set_target_fps(DESIRED_FPS as u32); // Should see if i can get the users hz
rl.set_trace_log(raylib::ffi::TraceLogLevel::LOG_NONE);
}
// initialize timer
let mut timer_accumulator: f32 = 0.0f32;
let timer_increment: f32 = TIMER_TICK_RATE as f32 / DESIRED_FPS as f32;
let timer_increment: f32 = TIMER_TICK_RATE / DESIRED_FPS;
// initialize builtin sprites
gpu::load_builtin_sprites(state);
gpu::load_builtin_sprites(&mut state);
// initialize sound system look into struct and impl for functions
let stream_handle =
rodio::OutputStreamBuilder::open_default_stream().expect("open default audio stream");
let sink = rodio::Sink::connect_new(&stream_handle.mixer());
let sink = rodio::Sink::connect_new(stream_handle.mixer());
let source = SineWave::new(440.0)
.amplify(0.2) // Volume (0.0 to 1.0)
@@ -122,21 +126,21 @@ fn start(state: &mut Chip8State, quirks: &Chip8Quirks, debug_mode: bool) {
sink.set_volume(0.0f32);
}
input::handle_input(state, &mut rl);
input::handle_input(&mut state, &mut rl);
state.vblank_waiting = false;
for _ in 0..CYCLES_PER_FRAME {
let instruction_bytes =
memory::read_n_bytes(&state.mem, state.mem.len(), state.r_pc as usize, 2);
for _ in 0..CYCLES_PER_FRAME as i32 {
let instruction_bytes = memory::read_n_bytes(
&state.mem,
state.mem.len(),
state.r_pc as usize,
INSTRUCTION_BYTE_SIZE,
);
let instruction: u16 =
((instruction_bytes[0] as u16) << 8) | instruction_bytes[1] as u16;
(u16::from(instruction_bytes[0]) << 8) | u16::from(instruction_bytes[1]);
state.r_pc += 2;
if debug_mode {
debug::print_debug(state, instruction);
}
cpu::execute_instruction(state, instruction, &quirks);
cpu::execute_instruction(&mut state, instruction, quirks);
if state.vblank_waiting {
break;
@@ -150,7 +154,7 @@ fn start(state: &mut Chip8State, quirks: &Chip8Quirks, debug_mode: bool) {
while timer_accumulator >= 1.0f32 {
if state.r_dt > 0 {
state.r_dt -= 1;
};
}
if state.r_st > 0 {
state.r_st -= 1;
}

View File

@@ -1,21 +1,22 @@
#![allow(clippy::many_single_char_names)]
use crate::chip8::{Chip8Quirks, gpu};
use super::Chip8State;
use super::memory::read_n_bytes;
// Need to come back and add good comments for each of the match patterns
pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Chip8Quirks) {
pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: Chip8Quirks) {
let c = ((instruction & 0xF000) >> 12) as u8;
let x = ((instruction & 0x0F00) >> 8) as u8;
let y = ((instruction & 0x00F0) >> 4) as u8;
let d = (instruction & 0x000F) as u8;
let n = d;
let kk = (instruction & 0x00FF) as u8;
let nnn = (instruction & 0x0FFF) as u16;
let nnn = instruction & 0x0FFF;
match (c, x, y, d) {
(0x0, _, 0xE, 0x0) => {
for row in state.display.iter_mut() {
for row in &mut state.display {
for col in row {
*col = false;
}
@@ -68,9 +69,9 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Ch
}
}
(0x8, _, _, 0x4) => {
let val: u16 = state.r_v[x as usize] as u16 + state.r_v[y as usize] as u16;
if val > u8::MAX as u16 {
state.r_v[x as usize] = (val & 0xFFFF) as u8;
let val: u16 = u16::from(state.r_v[x as usize]) + u16::from(state.r_v[y as usize]);
if val > u16::from(u8::MAX) {
state.r_v[x as usize] = val as u8;
state.r_v[0xF] = 1;
} else {
state.r_v[x as usize] = val as u8;
@@ -80,43 +81,44 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Ch
(0x8, _, _, 0x5) => {
let flag = state.r_v[x as usize] >= state.r_v[y as usize];
state.r_v[x as usize] = state.r_v[x as usize].wrapping_sub(state.r_v[y as usize]);
state.r_v[0xF] = if flag { 1 } else { 0 };
state.r_v[0xF] = u8::from(flag);
}
(0x8, _, _, 0x6) => {
if !quirks.shifting {
state.r_v[x as usize] = state.r_v[y as usize];
}
let flag = (state.r_v[x as usize] & 0b00000001) == 1;
state.r_v[x as usize] = state.r_v[x as usize] / 2;
state.r_v[0xF] = if flag { 1 } else { 0 };
let flag = (state.r_v[x as usize] & 0b0000_0001) == 1;
state.r_v[x as usize] /= 2;
state.r_v[0xF] = u8::from(flag);
}
(0x8, _, _, 0x7) => {
let flag = state.r_v[x as usize] <= state.r_v[y as usize];
state.r_v[x as usize] = state.r_v[y as usize].wrapping_sub(state.r_v[x as usize]);
state.r_v[0xF] = if flag { 1 } else { 0 };
state.r_v[0xF] = u8::from(flag);
}
(0x8, _, _, 0xE) => {
if !quirks.shifting {
state.r_v[x as usize] = state.r_v[y as usize];
}
let flag = ((state.r_v[x as usize] & 0b10000000) >> 7) == 1;
let flag = ((state.r_v[x as usize] & 0b1000_0000) >> 7) == 1;
state.r_v[x as usize] = state.r_v[x as usize].wrapping_mul(2);
state.r_v[0xF] = if flag { 1 } else { 0 };
state.r_v[0xF] = u8::from(flag);
}
(0x9, _, _, _) => {
if state.r_v[x as usize] != state.r_v[y as usize] {
state.r_pc += 2
state.r_pc += 2;
}
}
(0xA, _, _, _) => state.r_i = nnn,
(0xB, _, _, _) => {
if quirks.jumping {
state.r_pc = nnn + state.r_v[x as usize] as u16;
state.r_pc = nnn + u16::from(state.r_v[x as usize]);
} else {
state.r_pc = nnn + state.r_v[0] as u16;
state.r_pc = nnn + u16::from(state.r_v[0]);
}
}
(0xC, _, _, _) => {
#[allow(clippy::cast_sign_loss)]
let rng = rand::random_range(0..256) as u8;
let result = rng & kk;
state.r_v[x as usize] = result;
@@ -161,15 +163,15 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Ch
}
(0xF, _, 0x1, 0x5) => state.r_dt = state.r_v[x as usize],
(0xF, _, 0x1, 0x8) => state.r_st = state.r_v[x as usize],
(0xF, _, 0x1, 0xE) => state.r_i = state.r_i + state.r_v[x as usize] as u16,
(0xF, _, 0x2, 0x9) => state.r_i = gpu::get_builtin_sprite_addr(x) as u16,
(0xF, _, 0x1, 0xE) => state.r_i += u16::from(state.r_v[x as usize]),
(0xF, _, 0x2, 0x9) => state.r_i = u16::from(gpu::get_builtin_sprite_addr(x)),
(0xF, _, 0x3, 0x3) => {
let mut decimal = state.r_v[x as usize];
let mut i = 3;
loop {
i = i - 1;
i -= 1;
state.mem[(state.r_i + i) as usize] = decimal % 10;
decimal = decimal / 10;
decimal /= 10;
if i == 0 {
break;
@@ -180,15 +182,15 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Ch
let mut i = 0;
while i <= x {
match y {
0x5 => state.mem[(state.r_i + i as u16) as usize] = state.r_v[i as usize],
0x6 => state.r_v[i as usize] = state.mem[(state.r_i + i as u16) as usize],
_ => panic!("Unmatched OPCODE 0xFx{}5", y),
0x5 => state.mem[(state.r_i + u16::from(i)) as usize] = state.r_v[i as usize],
0x6 => state.r_v[i as usize] = state.mem[(state.r_i + u16::from(i)) as usize],
_ => panic!("Unmatched OPCODE 0xFx{y}5"),
}
i = i + 1;
i += 1;
}
if quirks.memory {
state.r_i += i as u16;
state.r_i += u16::from(i);
}
}
_ => {}

View File

@@ -1,27 +0,0 @@
use std::io::{self, Read};
use super::Chip8State;
pub fn print_debug(state: &Chip8State, current_instruction: u16) {
print!("\x1b[H\x1b[J");
println!("---- DEBUG ----");
println!("PC: {:04X}", state.r_pc - 2); // -2 because of where i put this log in the main loop
println!("SP: {}", state.r_sp);
println!("I: {:04X}", state.r_i);
println!("DT: {}", state.r_dt);
println!("ST: {}", state.r_st);
for x in 0..state.r_v.len() {
if state.r_v[x] > 0 {
println!("V{}: {}", x, state.r_v[x]);
}
}
for x in 0..state.input.len() {
if state.input[x] == true {
println!("Pressed: {}", x);
}
}
println!("Current Instruction: {:04X}", current_instruction);
println!("----------------");
// println!("Press Enter to continue...");
// let _ = io::stdin().read(&mut [0u8]).unwrap();
}

View File

@@ -2,25 +2,25 @@ use crate::chip8::memory;
use super::Chip8State;
pub static CHIP8_DISPLAY_WIDTH: i32 = 64;
pub static CHIP8_DISPLAY_HEIGHT: i32 = 32;
pub static CHIP8_DISPLAY_WIDTH: u8 = 64;
pub static CHIP8_DISPLAY_HEIGHT: u8 = 32;
pub static SPRITE_WIDTH: u8 = 8;
// I probably don't need state here
// refactor to just receive a mutatable buffer
pub fn draw(state: &mut Chip8State, vx: u8, vy: u8, bytes_to_draw: &[u8], bytes_to_draw_len: u8) {
state.r_v[0xF] = 0;
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH as u8;
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT as u8;
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH;
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT;
let mut bytes_idx = 0;
while bytes_idx < bytes_to_draw_len {
// TODO: this should be a u8 for safety
let y: u8 = start_y.wrapping_add(bytes_idx) % CHIP8_DISPLAY_HEIGHT as u8;
let y: u8 = start_y.wrapping_add(bytes_idx) % CHIP8_DISPLAY_HEIGHT;
let mut bit_idx = 0;
while bit_idx < SPRITE_WIDTH {
let x: u8 = start_x.wrapping_add(bit_idx) % CHIP8_DISPLAY_WIDTH as u8;
let x: u8 = start_x.wrapping_add(bit_idx) % CHIP8_DISPLAY_WIDTH;
let sprite_pixel = ((bytes_to_draw[bytes_idx as usize] >> (7 - bit_idx)) & 1) == 1;
if sprite_pixel {
@@ -48,21 +48,21 @@ pub fn clipping_draw(
bytes_to_draw_len: u8,
) {
state.r_v[0xF] = 0;
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH as u8;
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT as u8;
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH;
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT;
let mut bytes_idx = 0;
while bytes_idx < bytes_to_draw_len {
let y: u16 = (start_y + bytes_idx) as u16;
let y: u16 = u16::from(start_y + bytes_idx);
// TODO: general reminder to cleanup type casts to be consistent
if y >= CHIP8_DISPLAY_HEIGHT as u16 {
if y >= u16::from(CHIP8_DISPLAY_HEIGHT) {
break;
}
let mut bit_idx = 0;
while bit_idx < SPRITE_WIDTH {
let x = (start_x + bit_idx) as u16;
if x >= CHIP8_DISPLAY_WIDTH as u16 {
let x = u16::from(start_x + bit_idx);
if x >= u16::from(CHIP8_DISPLAY_WIDTH) {
break;
}
@@ -94,7 +94,7 @@ pub fn load_builtin_sprites(state: &mut Chip8State) {
}
pub fn get_builtin_sprite_addr(sprite_index: u8) -> u8 {
return BUILTIN_SPRITES_ADDR + (sprite_index * BUILTIN_SPRITES_SIZE);
BUILTIN_SPRITES_ADDR + (sprite_index * BUILTIN_SPRITES_SIZE)
}
static BUILTIN_SPRITES_ADDR: u8 = 0;

View File

@@ -23,56 +23,31 @@ static RL_KEY_LAYOUT: [KeyboardKey; 16] = [
pub fn handle_input(state: &mut Chip8State, rl_hdl: &mut RaylibHandle) {
for key in RL_KEY_LAYOUT {
let mapped_key = qwerty_to_chip8(key);
// let chip8_input_index = chip8_to_index(mapped_key);
if rl_hdl.is_key_down(key) {
state.input[mapped_key as usize] = true;
} else {
state.input[mapped_key as usize] = false;
}
let Ok(mapped_key) = qwerty_to_chip8(key) else {
continue;
};
state.input[mapped_key as usize] = rl_hdl.is_key_down(key);
}
}
fn qwerty_to_chip8(keycode: KeyboardKey) -> u8 {
fn qwerty_to_chip8(keycode: KeyboardKey) -> Result<u8, String> {
match keycode {
KeyboardKey::KEY_ONE => return 0x1,
KeyboardKey::KEY_TWO => return 0x2,
KeyboardKey::KEY_THREE => return 0x3,
KeyboardKey::KEY_FOUR => return 0xC,
KeyboardKey::KEY_Q => return 0x4,
KeyboardKey::KEY_W => return 0x5,
KeyboardKey::KEY_E => return 0x6,
KeyboardKey::KEY_R => return 0xD,
KeyboardKey::KEY_A => return 0x7,
KeyboardKey::KEY_S => return 0x8,
KeyboardKey::KEY_D => return 0x9,
KeyboardKey::KEY_F => return 0xE,
KeyboardKey::KEY_Z => return 0xA,
KeyboardKey::KEY_X => return 0x0,
KeyboardKey::KEY_C => return 0xB,
KeyboardKey::KEY_V => return 0xF,
_ => return 0,
KeyboardKey::KEY_ONE => Ok(0x1),
KeyboardKey::KEY_TWO => Ok(0x2),
KeyboardKey::KEY_THREE => Ok(0x3),
KeyboardKey::KEY_FOUR => Ok(0xC),
KeyboardKey::KEY_Q => Ok(0x4),
KeyboardKey::KEY_W => Ok(0x5),
KeyboardKey::KEY_E => Ok(0x6),
KeyboardKey::KEY_R => Ok(0xD),
KeyboardKey::KEY_A => Ok(0x7),
KeyboardKey::KEY_S => Ok(0x8),
KeyboardKey::KEY_D => Ok(0x9),
KeyboardKey::KEY_F => Ok(0xE),
KeyboardKey::KEY_Z => Ok(0xA),
KeyboardKey::KEY_X => Ok(0x0),
KeyboardKey::KEY_C => Ok(0xB),
KeyboardKey::KEY_V => Ok(0xF),
_ => Err(format!("Un-mapped keycode {}", keycode as u8)),
}
}
// fn chip8_to_index(chip8_key: u8) -> u8 {
// match chip8_key {
// 0x1 => return 0x0,
// 0x2 => return 0x1,
// 0x3 => return 0x2,
// 0xC => return 0x3,
// 0x4 => return 0x4,
// 0x5 => return 0x5,
// 0x6 => return 0x6,
// 0xD => return 0x7,
// 0x7 => return 0x8,
// 0x8 => return 0x9,
// 0x9 => return 0xA,
// 0xE => return 0xB,
// 0xA => return 0xC,
// 0x0 => return 0xD,
// 0xB => return 0xE,
// 0xF => return 0xF,
// _ => panic!("Unknown Chip8 Key {}", chip8_key),
// }
// }

View File

@@ -10,34 +10,30 @@ pub fn read_n_bytes(
let mut addr = start_addr;
let mut bytes = Vec::new();
while addr != start_addr + n_bytes {
if addr >= buffer_len {
panic!(
"Couldn't read from Address {} exceeds buffer length {}",
addr, buffer_len
) // nice error handling
}
assert!(
addr < buffer_len,
"Couldn't read from Address {addr} exceeds buffer length {buffer_len}"
);
bytes.push(buffer[addr]);
addr += 1;
}
return bytes as Vec<u8>;
bytes as Vec<u8>
}
pub fn load_bytes(state: &mut Chip8State, data: &[u8], data_len: usize, start_addr: usize) {
for i in 0..data_len {
state.mem[start_addr + i] = data[i];
}
state.mem[start_addr..(data_len + start_addr)].copy_from_slice(&data[..data_len]);
}
pub fn load_file_to_memory<P: AsRef<Path>>(state: &mut Chip8State, filepath: P) -> io::Result<()> {
let fp = filepath.as_ref();
// read file to Vec(u8)
let program = std::fs::read(fp)?;
let bytes = std::fs::read(fp)?;
for i in 0..program.len() {
state.mem[state.r_pc as usize + i] = program[i];
for (i, byte) in bytes.into_iter().enumerate() {
state.mem[state.r_pc as usize + i] = byte;
}
// Should return Ok or Err

View File

@@ -2,27 +2,32 @@ use super::Chip8State;
use super::gpu::{CHIP8_DISPLAY_HEIGHT, CHIP8_DISPLAY_WIDTH};
use raylib::{RaylibHandle, RaylibThread, color::Color, prelude::RaylibDraw};
pub static DISPLAY_WIDTH: i32 = 640;
pub static DISPLAY_HEIGHT: i32 = 480;
pub static DISPLAY_WIDTH: u16 = 640;
pub static DISPLAY_HEIGHT: u16 = 480;
pub fn render(state: &Chip8State, rl_handle: &mut RaylibHandle, rl_thread: &RaylibThread) {
let mut d = rl_handle.begin_drawing(&rl_thread);
let mut d = rl_handle.begin_drawing(rl_thread);
// d.clear_background(Color::BLACK);
let scale_x = DISPLAY_WIDTH / CHIP8_DISPLAY_WIDTH;
let scale_y = DISPLAY_HEIGHT / CHIP8_DISPLAY_HEIGHT;
let scale_x = DISPLAY_WIDTH / u16::from(CHIP8_DISPLAY_WIDTH);
let scale_y = DISPLAY_HEIGHT / u16::from(CHIP8_DISPLAY_HEIGHT);
for y in 0..CHIP8_DISPLAY_HEIGHT {
for x in 0..CHIP8_DISPLAY_WIDTH {
// fix to render color based on exact bit for pixels
let color: Color;
if state.display[y as usize][x as usize] {
color = Color::WHITE;
let color: Color = if state.display[y as usize][x as usize] {
Color::WHITE
} else {
color = Color::BLACK;
}
d.draw_rectangle(x * scale_x, y * scale_y, scale_x, scale_y, color);
Color::BLACK
};
d.draw_rectangle(
i32::from(u16::from(x) * scale_x),
i32::from(u16::from(y) * scale_y),
i32::from(scale_x),
i32::from(scale_y),
color,
);
}
}
}

View File

@@ -1,3 +1,6 @@
#![warn(clippy::pedantic)]
#![allow(clippy::cast_possible_truncation)]
use clap::Parser;
mod chip8;
@@ -5,9 +8,6 @@ mod chip8;
#[derive(Parser, Debug)]
#[command(version, about = "Chip 8 Emulator")]
struct Args {
/// Turn on debug mode
#[arg(short, long, action = clap::ArgAction::SetTrue)]
debug: bool,
/// Set path for chip8 binary file to run
#[arg(short, long)]
file: Option<String>,
@@ -31,10 +31,6 @@ fn main() {
};
if let Some(filepath) = args.file {
if args.debug {
chip8::run(filepath, &quirks, true);
} else {
chip8::run(filepath, &quirks, false);
}
chip8::run(filepath, quirks);
}
}