173 lines
4.1 KiB
Rust
173 lines
4.1 KiB
Rust
use rodio::{source::SineWave, Sink};
|
|
use std::time::{Duration, Instant};
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct Display {
|
|
pub buf: [u8; Self::WIDTH as usize * Self::HEIGHT as usize],
|
|
}
|
|
|
|
impl Display {
|
|
const WIDTH: u8 = 64;
|
|
const HEIGHT: u8 = 32;
|
|
|
|
pub fn clear(&mut self) {
|
|
self.buf = [0x0; Self::WIDTH as usize * Self::HEIGHT as usize]
|
|
}
|
|
|
|
pub fn draw_sprite(&mut self, coords: (u8, u8), data: &[u8]) -> bool {
|
|
let x = coords.0 % Self::WIDTH;
|
|
let y = coords.1 % Self::HEIGHT;
|
|
let mut set_vf = false;
|
|
|
|
// Each byte in data is a row, with the column being data.len() tall
|
|
// This means when writing this sprite, x will change for each bit in a byte and y
|
|
// should be incremented by 1 for every new byte we draw to the graphics buffer
|
|
for (y_offset, byte) in data.iter().enumerate() {
|
|
// Access every bit of the byte
|
|
for x_bit_offset in 0..8 {
|
|
// The Coordinates of this bit in the gfx buffer
|
|
let gfx_x = x + (7 - x_bit_offset);
|
|
let gfx_y = y + y_offset as u8;
|
|
|
|
let bit = (byte >> x_bit_offset) & 0x01;
|
|
|
|
// Translate the gfx_x and gfx_y to an index in the 1D buffer
|
|
let gfx_i = (Self::WIDTH as usize * gfx_y as usize) + gfx_x as usize;
|
|
|
|
if gfx_i >= self.buf.len() {
|
|
break; // Stop Drawing this spite and move on to the next
|
|
}
|
|
|
|
if bit == 0x1 && self.buf[gfx_i] == 0x01 {
|
|
set_vf = true;
|
|
}
|
|
|
|
self.buf[gfx_i] ^= bit;
|
|
}
|
|
}
|
|
set_vf
|
|
}
|
|
}
|
|
|
|
impl Default for Display {
|
|
fn default() -> Self {
|
|
Display {
|
|
buf: [0x0; Self::WIDTH as usize * Self::HEIGHT as usize],
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct Timer {
|
|
remaining: u8,
|
|
enabled: bool,
|
|
beep_enabled: bool,
|
|
}
|
|
|
|
impl Default for Timer {
|
|
fn default() -> Self {
|
|
Timer {
|
|
remaining: 0,
|
|
enabled: false,
|
|
beep_enabled: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Timer {
|
|
pub fn with_beep(mut self) -> Self {
|
|
self.beep_enabled = true;
|
|
self
|
|
}
|
|
|
|
pub fn set(&mut self, secs: u8) {
|
|
self.remaining = secs;
|
|
self.enabled = true;
|
|
}
|
|
|
|
pub fn get(&self) -> u8 {
|
|
self.remaining
|
|
}
|
|
|
|
pub fn tick(&mut self) {
|
|
if self.enabled {
|
|
self.remaining -= 1;
|
|
|
|
if self.remaining == 0 {
|
|
self.enabled = false;
|
|
|
|
if self.beep_enabled {
|
|
Self::beep();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn beep() {
|
|
let maybe_device = rodio::default_output_device();
|
|
let beep = SineWave::new(440);
|
|
|
|
// TODO: Fix Pop when sound ends?
|
|
if let Some(device) = maybe_device {
|
|
std::thread::spawn(move || {
|
|
let sink = Sink::new(&device);
|
|
let duration = Duration::from_millis(100);
|
|
let start = Instant::now();
|
|
sink.append(beep);
|
|
|
|
loop {
|
|
if Instant::now().duration_since(start) > duration {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct Keypad {
|
|
keys: [bool; 16],
|
|
}
|
|
|
|
impl Default for Keypad {
|
|
fn default() -> Self {
|
|
Self { keys: [false; 16] }
|
|
}
|
|
}
|
|
|
|
impl Keypad {
|
|
pub fn get_any_pressed(&self) -> Option<u8> {
|
|
for (i, key) in self.keys.iter().enumerate() {
|
|
if *key {
|
|
return Some(i as u8);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.keys = [false; 16];
|
|
}
|
|
|
|
pub fn get_overview(&self) -> [bool; 16] {
|
|
self.keys
|
|
}
|
|
|
|
pub fn is_pressed(&self, key: u8) -> bool {
|
|
self.keys[key as usize]
|
|
}
|
|
|
|
pub fn set_all(&mut self, keys: [bool; 16]) {
|
|
self.keys = keys;
|
|
}
|
|
|
|
pub fn set_key(&mut self, index: usize) {
|
|
self.keys[index] = true;
|
|
}
|
|
|
|
pub fn unset_key(&mut self, index: usize) {
|
|
self.keys[index] = false;
|
|
}
|
|
}
|