Compare commits
26 Commits
49e4914c00
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0695c23a78 | |||
| c3df8672c8 | |||
| 47cce19940 | |||
| d8b3bcae19 | |||
| 7ede5aeafa | |||
| 08a2ec4459 | |||
| 58901521e5 | |||
| fed6762288 | |||
| 342eb4557d | |||
| 63fe937481 | |||
| 73422b4072 | |||
| efba92b8c7 | |||
| 25c6d9beaa | |||
| 38ec89813d | |||
| 596e60abeb | |||
| ad50485d37 | |||
| ce5ff7ee24 | |||
| 124493fd11 | |||
| 0ff0b6c596 | |||
| 253095bc6d | |||
| 063ca15b37 | |||
| 266c685d1b | |||
| 82323846ec | |||
| b3eed758cc | |||
| d6aeda49ca | |||
| 877ee5de9a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/programs
|
||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -177,7 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chip-8-em"
|
name = "chip8-em"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "chip-8-em"
|
name = "chip8-em"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
|||||||
204
README.md
204
README.md
@@ -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>
|
<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.
|
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
|
||||||
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
|
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
|
||||||
-->
|
-->
|
||||||
[![Contributors][contributors-shield]][contributors-url]
|
[![MIT][license-shield]][license-url]
|
||||||
[![Forks][forks-shield]][forks-url]
|
<!-- [![LinkedIn][linkedin-shield]][linkedin-url] -->
|
||||||
[![Stargazers][stars-shield]][stars-url]
|
|
||||||
[![Issues][issues-shield]][issues-url]
|
|
||||||
[![project_license][license-shield]][license-url]
|
|
||||||
[![LinkedIn][linkedin-shield]][linkedin-url]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- PROJECT LOGO -->
|
<!-- PROJECT LOGO -->
|
||||||
<br />
|
<br />
|
||||||
<div align="center">
|
<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">
|
<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">
|
<p align="center">
|
||||||
project_description
|
Chip8 Emulator built in Rust.
|
||||||
<br />
|
<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 />
|
||||||
<br />
|
<a href="https://git.colelanders.dev/jlanders/chip8-em">View Demo</a>
|
||||||
<a href="https://github.com/github_username/repo_name">View Demo</a>
|
|
||||||
·
|
|
||||||
<a href="https://github.com/github_username/repo_name/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
|
|
||||||
·
|
|
||||||
<a href="https://github.com/github_username/repo_name/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -67,6 +49,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="#prerequisites">Prerequisites</a></li>
|
<li><a href="#prerequisites">Prerequisites</a></li>
|
||||||
<li><a href="#installation">Installation</a></li>
|
<li><a href="#installation">Installation</a></li>
|
||||||
|
<li><a href="#running">Running</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#usage">Usage</a></li>
|
<li><a href="#usage">Usage</a></li>
|
||||||
@@ -83,74 +66,78 @@
|
|||||||
<!-- ABOUT THE PROJECT -->
|
<!-- ABOUT THE PROJECT -->
|
||||||
## About The Project
|
## About The Project
|
||||||
|
|
||||||
[![Product Name Screen Shot][product-screenshot]](https://example.com)
|
![Chip8 Emulator Screen Shot][product-screenshot]
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
||||||
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Built With
|
### Built With
|
||||||
|
|
||||||
* [![Next][Next.js]][Next-url]
|
* [![Rust][Rust]][Rust-url]
|
||||||
* [![React][React.js]][React-url]
|
* [![Raylib][Raylib]][Raylib-url]
|
||||||
* [![Vue][Vue.js]][Vue-url]
|
* [![Clap][Clap]][Clap-url]
|
||||||
* [![Angular][Angular.io]][Angular-url]
|
* [![Rodio][Rodio]][Rodio-url]
|
||||||
* [![Svelte][Svelte.dev]][Svelte-url]
|
|
||||||
* [![Laravel][Laravel.com]][Laravel-url]
|
|
||||||
* [![Bootstrap][Bootstrap.com]][Bootstrap-url]
|
|
||||||
* [![JQuery][JQuery.com]][JQuery-url]
|
|
||||||
|
|
||||||
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- GETTING STARTED -->
|
<!-- GETTING STARTED -->
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
This is an example of how you may give instructions on setting up your project locally.
|
You can find pre-built binaries for Windows/MacOS/Linux -> [releases](https://git.colelanders.dev/jlanders/chip8-em/releases)
|
||||||
To get a local copy up and running follow these simple example steps.
|
|
||||||
|
To run the code get a local copy up and running follow these simple example steps.
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
This is an example of how to list things you need to use the software and how to install them.
|
This is an example of how to list things you need to use the software and how to install them.
|
||||||
* npm
|
* rust - Follow the official install guide [here](https://rust-lang.org/tools/install/)
|
||||||
```sh
|
<!-- ```sh
|
||||||
npm install npm@latest -g
|
npm install npm@latest -g
|
||||||
```
|
``` -->
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. Get a free API Key at [https://example.com](https://example.com)
|
1. Clone the repo
|
||||||
2. Clone the repo
|
|
||||||
```sh
|
```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
|
```sh
|
||||||
npm install
|
cd chip8-em
|
||||||
```
|
```
|
||||||
4. Enter your API in `config.js`
|
3. Build the project
|
||||||
```js
|
|
||||||
const API_KEY = 'ENTER YOUR API';
|
|
||||||
```
|
|
||||||
5. Change git remote url to avoid accidental pushes to base project
|
|
||||||
```sh
|
```sh
|
||||||
git remote set-url origin github_username/repo_name
|
cargo build
|
||||||
git remote -v # confirm the changes
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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>
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- USAGE EXAMPLES -->
|
<!-- USAGE EXAMPLES -->
|
||||||
## Usage
|
## 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>
|
<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 -->
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- [ ] Feature 1
|
- [ ] Super-Chip8 extension
|
||||||
- [ ] Feature 2
|
- [ ] XO-CHIP extension
|
||||||
- [ ] Feature 3
|
- [ ] More Configuration
|
||||||
- [ ] Nested Feature
|
- [ ] Window Resizing
|
||||||
|
- [ ] Allow rebinding keys via GUI
|
||||||
See the [open issues](https://github.com/github_username/repo_name/issues) for a full list of proposed features (and known issues).
|
- [ ] Replacing default sound via GUI
|
||||||
|
|
||||||
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- CONTRIBUTING -->
|
<!-- CONTRIBUTING -->
|
||||||
## 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**.
|
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!
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- LICENSE -->
|
<!-- LICENSE -->
|
||||||
## 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>
|
<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 -->
|
||||||
## 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>
|
<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 -->
|
||||||
## 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>
|
<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 -->
|
<!-- MARKDOWN LINKS & IMAGES -->
|
||||||
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
||||||
[contributors-shield]: https://img.shields.io/git.colelanders.dev/jlanders/chip8-em.svg?style=for-the-badge
|
<!-- [license-shield]: https://img.shields.io/github/license/github_username/repo_name.svg?style=for-the-badge
|
||||||
[contributors-url]: https://github.com/github_username/repo_name/graphs/contributors
|
[license-url]: https://git.colelanders.dev/jlanders/chip8-em/blob/master/LICENSE.txt
|
||||||
[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
|
|
||||||
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
|
[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-->
|
||||||
[product-screenshot]: images/screenshot.png
|
[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 -->
|
<!-- 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
|
[Rust]: https://img.shields.io/badge/rust-%23E32F26.svg?style=for-the-badge&logo=rust&logoColor=white
|
||||||
[Next-url]: https://nextjs.org/
|
[Rust-url]: https://www.rust-lang.org/
|
||||||
[React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB
|
|
||||||
[React-url]: https://reactjs.org/
|
[Raylib]: https://img.shields.io/badge/raylib-white?style=for-the-badge&logo=target&logoColor=black
|
||||||
[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D
|
[Raylib-url]: https://www.raylib.com/
|
||||||
[Vue-url]: https://vuejs.org/
|
|
||||||
[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white
|
[Clap]: https://img.shields.io/badge/clap-blue?style=for-the-badge&logo=command-line&logoColor=white
|
||||||
[Angular-url]: https://angular.io/
|
[Clap-url]: https://github.com/clap-rs/clap
|
||||||
[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00
|
|
||||||
[Svelte-url]: https://svelte.dev/
|
[Rodio]: https://img.shields.io/badge/rodio-orange?style=for-the-badge&logo=audio-bus&logoColor=white
|
||||||
[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white
|
[Rodio-url]: https://github.com/RustAudio/rodio
|
||||||
[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
|
|
||||||
BIN
images/screenshot.png
Normal file
BIN
images/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
70
src/chip8.rs
70
src/chip8.rs
@@ -1,5 +1,4 @@
|
|||||||
mod cpu;
|
mod cpu;
|
||||||
mod debug;
|
|
||||||
mod gpu;
|
mod gpu;
|
||||||
mod input;
|
mod input;
|
||||||
mod memory;
|
mod memory;
|
||||||
@@ -7,14 +6,15 @@ mod renderer;
|
|||||||
|
|
||||||
use rodio::source::{SineWave, Source};
|
use rodio::source::{SineWave, Source};
|
||||||
|
|
||||||
static MEMORY_LIMIT: i32 = 4096;
|
static MEMORY_LIMIT: u16 = 4096;
|
||||||
static STACK_LIMIT: i32 = 16;
|
static STACK_LIMIT: u8 = 16;
|
||||||
static VARIABLE_REGISTER_COUNT: i32 = 16;
|
static VARIABLE_REGISTER_COUNT: u8 = 16;
|
||||||
static TIMER_TICK_RATE: u32 = 60;
|
static TIMER_TICK_RATE: f32 = 60.0;
|
||||||
static DESIRED_FPS: u32 = 60;
|
static DESIRED_FPS: f32 = 60.0;
|
||||||
static CYCLES_PER_FRAME: u32 = 10;
|
static CYCLES_PER_FRAME: f32 = 10.0;
|
||||||
|
static INSTRUCTION_BYTE_SIZE: usize = 2;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Chip8State {
|
struct Chip8State {
|
||||||
// Flags
|
// Flags
|
||||||
eti_600_flag: bool,
|
eti_600_flag: bool,
|
||||||
@@ -39,6 +39,8 @@ struct Chip8State {
|
|||||||
input: [bool; 16],
|
input: [bool; 16],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Chip8Quirks {
|
pub struct Chip8Quirks {
|
||||||
pub vf_reset: bool,
|
pub vf_reset: bool,
|
||||||
pub memory: bool,
|
pub memory: bool,
|
||||||
@@ -48,7 +50,7 @@ pub struct Chip8Quirks {
|
|||||||
pub jumping: bool,
|
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 {
|
let mut state = Chip8State {
|
||||||
eti_600_flag: false,
|
eti_600_flag: false,
|
||||||
vblank_waiting: false,
|
vblank_waiting: false,
|
||||||
@@ -64,42 +66,44 @@ pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, quirks: &Chip8Quirks, de
|
|||||||
input: [false; 16],
|
input: [false; 16],
|
||||||
};
|
};
|
||||||
|
|
||||||
if !state.eti_600_flag {
|
if state.eti_600_flag {
|
||||||
state.r_pc = 0x200;
|
|
||||||
} else {
|
|
||||||
state.r_pc = 0x600;
|
state.r_pc = 0x600;
|
||||||
|
} else {
|
||||||
|
state.r_pc = 0x200;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load Program
|
// Load Program
|
||||||
let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref());
|
let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref());
|
||||||
|
|
||||||
// Run Program
|
// 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
|
// TODO rip out as much RL stuff from here and put into renderer
|
||||||
// Init Rendering Pipeline
|
// Init Rendering Pipeline
|
||||||
let (mut rl, thread) = raylib::init()
|
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")
|
.title("Chip8 Emu")
|
||||||
.build();
|
.build();
|
||||||
rl.set_target_fps(DESIRED_FPS); // Should see if i can get the users hz
|
#[allow(clippy::cast_sign_loss)]
|
||||||
if !debug_mode {
|
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);
|
rl.set_trace_log(raylib::ffi::TraceLogLevel::LOG_NONE);
|
||||||
}
|
|
||||||
|
|
||||||
// initialize timer
|
// initialize timer
|
||||||
let mut timer_accumulator: f32 = 0.0f32;
|
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
|
// initialize builtin sprites
|
||||||
gpu::load_builtin_sprites(state);
|
gpu::load_builtin_sprites(&mut state);
|
||||||
|
|
||||||
// initialize sound system look into struct and impl for functions
|
// initialize sound system look into struct and impl for functions
|
||||||
let stream_handle =
|
let stream_handle =
|
||||||
rodio::OutputStreamBuilder::open_default_stream().expect("open default audio stream");
|
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)
|
let source = SineWave::new(440.0)
|
||||||
.amplify(0.2) // Volume (0.0 to 1.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);
|
sink.set_volume(0.0f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
input::handle_input(state, &mut rl);
|
input::handle_input(&mut state, &mut rl);
|
||||||
|
|
||||||
state.vblank_waiting = false;
|
state.vblank_waiting = false;
|
||||||
for _ in 0..CYCLES_PER_FRAME {
|
for _ in 0..CYCLES_PER_FRAME as i32 {
|
||||||
let instruction_bytes =
|
let instruction_bytes = memory::read_n_bytes(
|
||||||
memory::read_n_bytes(&state.mem, state.mem.len(), state.r_pc as usize, 2);
|
&state.mem,
|
||||||
|
state.mem.len(),
|
||||||
|
state.r_pc as usize,
|
||||||
|
INSTRUCTION_BYTE_SIZE,
|
||||||
|
);
|
||||||
let instruction: u16 =
|
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;
|
state.r_pc += 2;
|
||||||
|
|
||||||
if debug_mode {
|
cpu::execute_instruction(&mut state, instruction, quirks);
|
||||||
debug::print_debug(state, instruction);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu::execute_instruction(state, instruction, &quirks);
|
|
||||||
|
|
||||||
if state.vblank_waiting {
|
if state.vblank_waiting {
|
||||||
break;
|
break;
|
||||||
@@ -150,7 +154,7 @@ fn start(state: &mut Chip8State, quirks: &Chip8Quirks, debug_mode: bool) {
|
|||||||
while timer_accumulator >= 1.0f32 {
|
while timer_accumulator >= 1.0f32 {
|
||||||
if state.r_dt > 0 {
|
if state.r_dt > 0 {
|
||||||
state.r_dt -= 1;
|
state.r_dt -= 1;
|
||||||
};
|
}
|
||||||
if state.r_st > 0 {
|
if state.r_st > 0 {
|
||||||
state.r_st -= 1;
|
state.r_st -= 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
|
#![allow(clippy::many_single_char_names)]
|
||||||
use crate::chip8::{Chip8Quirks, gpu};
|
use crate::chip8::{Chip8Quirks, gpu};
|
||||||
|
|
||||||
use super::Chip8State;
|
use super::Chip8State;
|
||||||
use super::memory::read_n_bytes;
|
use super::memory::read_n_bytes;
|
||||||
|
|
||||||
// Need to come back and add good comments for each of the match patterns
|
// 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 c = ((instruction & 0xF000) >> 12) as u8;
|
||||||
let x = ((instruction & 0x0F00) >> 8) as u8;
|
let x = ((instruction & 0x0F00) >> 8) as u8;
|
||||||
let y = ((instruction & 0x00F0) >> 4) as u8;
|
let y = ((instruction & 0x00F0) >> 4) as u8;
|
||||||
let d = (instruction & 0x000F) as u8;
|
let d = (instruction & 0x000F) as u8;
|
||||||
let n = d;
|
let n = d;
|
||||||
let kk = (instruction & 0x00FF) as u8;
|
let kk = (instruction & 0x00FF) as u8;
|
||||||
let nnn = (instruction & 0x0FFF) as u16;
|
let nnn = instruction & 0x0FFF;
|
||||||
|
|
||||||
match (c, x, y, d) {
|
match (c, x, y, d) {
|
||||||
(0x0, _, 0xE, 0x0) => {
|
(0x0, _, 0xE, 0x0) => {
|
||||||
for row in state.display.iter_mut() {
|
for row in &mut state.display {
|
||||||
for col in row {
|
for col in row {
|
||||||
*col = false;
|
*col = false;
|
||||||
}
|
}
|
||||||
@@ -68,9 +69,9 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(0x8, _, _, 0x4) => {
|
(0x8, _, _, 0x4) => {
|
||||||
let val: u16 = state.r_v[x as usize] as u16 + state.r_v[y as usize] as u16;
|
let val: u16 = u16::from(state.r_v[x as usize]) + u16::from(state.r_v[y as usize]);
|
||||||
if val > u8::MAX as u16 {
|
if val > u16::from(u8::MAX) {
|
||||||
state.r_v[x as usize] = (val & 0xFFFF) as u8;
|
state.r_v[x as usize] = val as u8;
|
||||||
state.r_v[0xF] = 1;
|
state.r_v[0xF] = 1;
|
||||||
} else {
|
} else {
|
||||||
state.r_v[x as usize] = val as u8;
|
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) => {
|
(0x8, _, _, 0x5) => {
|
||||||
let flag = state.r_v[x as usize] >= state.r_v[y as usize];
|
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[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) => {
|
(0x8, _, _, 0x6) => {
|
||||||
if !quirks.shifting {
|
if !quirks.shifting {
|
||||||
state.r_v[x as usize] = state.r_v[y as usize];
|
state.r_v[x as usize] = state.r_v[y as usize];
|
||||||
}
|
}
|
||||||
let flag = (state.r_v[x as usize] & 0b00000001) == 1;
|
let flag = (state.r_v[x as usize] & 0b0000_0001) == 1;
|
||||||
state.r_v[x as usize] = state.r_v[x as usize] / 2;
|
state.r_v[x as usize] /= 2;
|
||||||
state.r_v[0xF] = if flag { 1 } else { 0 };
|
state.r_v[0xF] = u8::from(flag);
|
||||||
}
|
}
|
||||||
(0x8, _, _, 0x7) => {
|
(0x8, _, _, 0x7) => {
|
||||||
let flag = state.r_v[x as usize] <= state.r_v[y as usize];
|
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[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) => {
|
(0x8, _, _, 0xE) => {
|
||||||
if !quirks.shifting {
|
if !quirks.shifting {
|
||||||
state.r_v[x as usize] = state.r_v[y as usize];
|
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[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, _, _, _) => {
|
(0x9, _, _, _) => {
|
||||||
if state.r_v[x as usize] != state.r_v[y as usize] {
|
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,
|
(0xA, _, _, _) => state.r_i = nnn,
|
||||||
(0xB, _, _, _) => {
|
(0xB, _, _, _) => {
|
||||||
if quirks.jumping {
|
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 {
|
} else {
|
||||||
state.r_pc = nnn + state.r_v[0] as u16;
|
state.r_pc = nnn + u16::from(state.r_v[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(0xC, _, _, _) => {
|
(0xC, _, _, _) => {
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
let rng = rand::random_range(0..256) as u8;
|
let rng = rand::random_range(0..256) as u8;
|
||||||
let result = rng & kk;
|
let result = rng & kk;
|
||||||
state.r_v[x as usize] = result;
|
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, 0x5) => state.r_dt = state.r_v[x as usize],
|
||||||
(0xF, _, 0x1, 0x8) => state.r_st = 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, _, 0x1, 0xE) => state.r_i += u16::from(state.r_v[x as usize]),
|
||||||
(0xF, _, 0x2, 0x9) => state.r_i = gpu::get_builtin_sprite_addr(x) as u16,
|
(0xF, _, 0x2, 0x9) => state.r_i = u16::from(gpu::get_builtin_sprite_addr(x)),
|
||||||
(0xF, _, 0x3, 0x3) => {
|
(0xF, _, 0x3, 0x3) => {
|
||||||
let mut decimal = state.r_v[x as usize];
|
let mut decimal = state.r_v[x as usize];
|
||||||
let mut i = 3;
|
let mut i = 3;
|
||||||
loop {
|
loop {
|
||||||
i = i - 1;
|
i -= 1;
|
||||||
state.mem[(state.r_i + i) as usize] = decimal % 10;
|
state.mem[(state.r_i + i) as usize] = decimal % 10;
|
||||||
decimal = decimal / 10;
|
decimal /= 10;
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
break;
|
break;
|
||||||
@@ -180,15 +182,15 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Ch
|
|||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i <= x {
|
while i <= x {
|
||||||
match y {
|
match y {
|
||||||
0x5 => state.mem[(state.r_i + i as u16) as usize] = state.r_v[i as usize],
|
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 + i as u16) as usize],
|
0x6 => state.r_v[i as usize] = state.mem[(state.r_i + u16::from(i)) as usize],
|
||||||
_ => panic!("Unmatched OPCODE 0xFx{}5", y),
|
_ => panic!("Unmatched OPCODE 0xFx{y}5"),
|
||||||
}
|
}
|
||||||
|
|
||||||
i = i + 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
if quirks.memory {
|
if quirks.memory {
|
||||||
state.r_i += i as u16;
|
state.r_i += u16::from(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -2,25 +2,25 @@ use crate::chip8::memory;
|
|||||||
|
|
||||||
use super::Chip8State;
|
use super::Chip8State;
|
||||||
|
|
||||||
pub static CHIP8_DISPLAY_WIDTH: i32 = 64;
|
pub static CHIP8_DISPLAY_WIDTH: u8 = 64;
|
||||||
pub static CHIP8_DISPLAY_HEIGHT: i32 = 32;
|
pub static CHIP8_DISPLAY_HEIGHT: u8 = 32;
|
||||||
pub static SPRITE_WIDTH: u8 = 8;
|
pub static SPRITE_WIDTH: u8 = 8;
|
||||||
|
|
||||||
// I probably don't need state here
|
// I probably don't need state here
|
||||||
// refactor to just receive a mutatable buffer
|
// 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) {
|
pub fn draw(state: &mut Chip8State, vx: u8, vy: u8, bytes_to_draw: &[u8], bytes_to_draw_len: u8) {
|
||||||
state.r_v[0xF] = 0;
|
state.r_v[0xF] = 0;
|
||||||
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH 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 as u8;
|
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT;
|
||||||
|
|
||||||
let mut bytes_idx = 0;
|
let mut bytes_idx = 0;
|
||||||
while bytes_idx < bytes_to_draw_len {
|
while bytes_idx < bytes_to_draw_len {
|
||||||
// TODO: this should be a u8 for safety
|
// 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;
|
let mut bit_idx = 0;
|
||||||
while bit_idx < SPRITE_WIDTH {
|
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;
|
let sprite_pixel = ((bytes_to_draw[bytes_idx as usize] >> (7 - bit_idx)) & 1) == 1;
|
||||||
if sprite_pixel {
|
if sprite_pixel {
|
||||||
@@ -48,21 +48,21 @@ pub fn clipping_draw(
|
|||||||
bytes_to_draw_len: u8,
|
bytes_to_draw_len: u8,
|
||||||
) {
|
) {
|
||||||
state.r_v[0xF] = 0;
|
state.r_v[0xF] = 0;
|
||||||
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH 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 as u8;
|
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT;
|
||||||
|
|
||||||
let mut bytes_idx = 0;
|
let mut bytes_idx = 0;
|
||||||
while bytes_idx < bytes_to_draw_len {
|
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
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bit_idx = 0;
|
let mut bit_idx = 0;
|
||||||
while bit_idx < SPRITE_WIDTH {
|
while bit_idx < SPRITE_WIDTH {
|
||||||
let x = (start_x + bit_idx) as u16;
|
let x = u16::from(start_x + bit_idx);
|
||||||
if x >= CHIP8_DISPLAY_WIDTH as u16 {
|
if x >= u16::from(CHIP8_DISPLAY_WIDTH) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ pub fn load_builtin_sprites(state: &mut Chip8State) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_builtin_sprite_addr(sprite_index: u8) -> u8 {
|
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;
|
static BUILTIN_SPRITES_ADDR: u8 = 0;
|
||||||
|
|||||||
@@ -23,56 +23,31 @@ static RL_KEY_LAYOUT: [KeyboardKey; 16] = [
|
|||||||
|
|
||||||
pub fn handle_input(state: &mut Chip8State, rl_hdl: &mut RaylibHandle) {
|
pub fn handle_input(state: &mut Chip8State, rl_hdl: &mut RaylibHandle) {
|
||||||
for key in RL_KEY_LAYOUT {
|
for key in RL_KEY_LAYOUT {
|
||||||
let mapped_key = qwerty_to_chip8(key);
|
let Ok(mapped_key) = qwerty_to_chip8(key) else {
|
||||||
// let chip8_input_index = chip8_to_index(mapped_key);
|
continue;
|
||||||
if rl_hdl.is_key_down(key) {
|
};
|
||||||
state.input[mapped_key as usize] = true;
|
state.input[mapped_key as usize] = rl_hdl.is_key_down(key);
|
||||||
} else {
|
|
||||||
state.input[mapped_key as usize] = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn qwerty_to_chip8(keycode: KeyboardKey) -> u8 {
|
fn qwerty_to_chip8(keycode: KeyboardKey) -> Result<u8, String> {
|
||||||
match keycode {
|
match keycode {
|
||||||
KeyboardKey::KEY_ONE => return 0x1,
|
KeyboardKey::KEY_ONE => Ok(0x1),
|
||||||
KeyboardKey::KEY_TWO => return 0x2,
|
KeyboardKey::KEY_TWO => Ok(0x2),
|
||||||
KeyboardKey::KEY_THREE => return 0x3,
|
KeyboardKey::KEY_THREE => Ok(0x3),
|
||||||
KeyboardKey::KEY_FOUR => return 0xC,
|
KeyboardKey::KEY_FOUR => Ok(0xC),
|
||||||
KeyboardKey::KEY_Q => return 0x4,
|
KeyboardKey::KEY_Q => Ok(0x4),
|
||||||
KeyboardKey::KEY_W => return 0x5,
|
KeyboardKey::KEY_W => Ok(0x5),
|
||||||
KeyboardKey::KEY_E => return 0x6,
|
KeyboardKey::KEY_E => Ok(0x6),
|
||||||
KeyboardKey::KEY_R => return 0xD,
|
KeyboardKey::KEY_R => Ok(0xD),
|
||||||
KeyboardKey::KEY_A => return 0x7,
|
KeyboardKey::KEY_A => Ok(0x7),
|
||||||
KeyboardKey::KEY_S => return 0x8,
|
KeyboardKey::KEY_S => Ok(0x8),
|
||||||
KeyboardKey::KEY_D => return 0x9,
|
KeyboardKey::KEY_D => Ok(0x9),
|
||||||
KeyboardKey::KEY_F => return 0xE,
|
KeyboardKey::KEY_F => Ok(0xE),
|
||||||
KeyboardKey::KEY_Z => return 0xA,
|
KeyboardKey::KEY_Z => Ok(0xA),
|
||||||
KeyboardKey::KEY_X => return 0x0,
|
KeyboardKey::KEY_X => Ok(0x0),
|
||||||
KeyboardKey::KEY_C => return 0xB,
|
KeyboardKey::KEY_C => Ok(0xB),
|
||||||
KeyboardKey::KEY_V => return 0xF,
|
KeyboardKey::KEY_V => Ok(0xF),
|
||||||
_ => return 0,
|
_ => 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),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -10,34 +10,30 @@ pub fn read_n_bytes(
|
|||||||
let mut addr = start_addr;
|
let mut addr = start_addr;
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
while addr != start_addr + n_bytes {
|
while addr != start_addr + n_bytes {
|
||||||
if addr >= buffer_len {
|
assert!(
|
||||||
panic!(
|
addr < buffer_len,
|
||||||
"Couldn't read from Address {} exceeds buffer length {}",
|
"Couldn't read from Address {addr} exceeds buffer length {buffer_len}"
|
||||||
addr, buffer_len
|
);
|
||||||
) // nice error handling
|
|
||||||
}
|
|
||||||
bytes.push(buffer[addr]);
|
bytes.push(buffer[addr]);
|
||||||
|
|
||||||
addr += 1;
|
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) {
|
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..(data_len + start_addr)].copy_from_slice(&data[..data_len]);
|
||||||
state.mem[start_addr + i] = data[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file_to_memory<P: AsRef<Path>>(state: &mut Chip8State, filepath: P) -> io::Result<()> {
|
pub fn load_file_to_memory<P: AsRef<Path>>(state: &mut Chip8State, filepath: P) -> io::Result<()> {
|
||||||
let fp = filepath.as_ref();
|
let fp = filepath.as_ref();
|
||||||
|
|
||||||
// read file to Vec(u8)
|
// read file to Vec(u8)
|
||||||
let program = std::fs::read(fp)?;
|
let bytes = std::fs::read(fp)?;
|
||||||
|
|
||||||
for i in 0..program.len() {
|
for (i, byte) in bytes.into_iter().enumerate() {
|
||||||
state.mem[state.r_pc as usize + i] = program[i];
|
state.mem[state.r_pc as usize + i] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should return Ok or Err
|
// Should return Ok or Err
|
||||||
|
|||||||
@@ -2,27 +2,32 @@ use super::Chip8State;
|
|||||||
use super::gpu::{CHIP8_DISPLAY_HEIGHT, CHIP8_DISPLAY_WIDTH};
|
use super::gpu::{CHIP8_DISPLAY_HEIGHT, CHIP8_DISPLAY_WIDTH};
|
||||||
use raylib::{RaylibHandle, RaylibThread, color::Color, prelude::RaylibDraw};
|
use raylib::{RaylibHandle, RaylibThread, color::Color, prelude::RaylibDraw};
|
||||||
|
|
||||||
pub static DISPLAY_WIDTH: i32 = 640;
|
pub static DISPLAY_WIDTH: u16 = 640;
|
||||||
pub static DISPLAY_HEIGHT: i32 = 480;
|
pub static DISPLAY_HEIGHT: u16 = 480;
|
||||||
|
|
||||||
pub fn render(state: &Chip8State, rl_handle: &mut RaylibHandle, rl_thread: &RaylibThread) {
|
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);
|
// d.clear_background(Color::BLACK);
|
||||||
|
|
||||||
let scale_x = DISPLAY_WIDTH / CHIP8_DISPLAY_WIDTH;
|
let scale_x = DISPLAY_WIDTH / u16::from(CHIP8_DISPLAY_WIDTH);
|
||||||
let scale_y = DISPLAY_HEIGHT / CHIP8_DISPLAY_HEIGHT;
|
let scale_y = DISPLAY_HEIGHT / u16::from(CHIP8_DISPLAY_HEIGHT);
|
||||||
|
|
||||||
for y in 0..CHIP8_DISPLAY_HEIGHT {
|
for y in 0..CHIP8_DISPLAY_HEIGHT {
|
||||||
for x in 0..CHIP8_DISPLAY_WIDTH {
|
for x in 0..CHIP8_DISPLAY_WIDTH {
|
||||||
// fix to render color based on exact bit for pixels
|
// fix to render color based on exact bit for pixels
|
||||||
let color: Color;
|
let color: Color = if state.display[y as usize][x as usize] {
|
||||||
if state.display[y as usize][x as usize] {
|
Color::WHITE
|
||||||
color = Color::WHITE;
|
|
||||||
} else {
|
} else {
|
||||||
color = Color::BLACK;
|
Color::BLACK
|
||||||
}
|
};
|
||||||
d.draw_rectangle(x * scale_x, y * scale_y, scale_x, scale_y, color);
|
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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/main.rs
12
src/main.rs
@@ -1,3 +1,6 @@
|
|||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
#![allow(clippy::cast_possible_truncation)]
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
mod chip8;
|
mod chip8;
|
||||||
@@ -5,9 +8,6 @@ mod chip8;
|
|||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about = "Chip 8 Emulator")]
|
#[command(version, about = "Chip 8 Emulator")]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Turn on debug mode
|
|
||||||
#[arg(short, long, action = clap::ArgAction::SetTrue)]
|
|
||||||
debug: bool,
|
|
||||||
/// Set path for chip8 binary file to run
|
/// Set path for chip8 binary file to run
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
file: Option<String>,
|
file: Option<String>,
|
||||||
@@ -31,10 +31,6 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(filepath) = args.file {
|
if let Some(filepath) = args.file {
|
||||||
if args.debug {
|
chip8::run(filepath, quirks);
|
||||||
chip8::run(filepath, &quirks, true);
|
|
||||||
} else {
|
|
||||||
chip8::run(filepath, &quirks, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user