You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
5.7 KiB
157 lines
5.7 KiB
pub mod cpu; |
|
pub mod opcodes; |
|
use cpu::Mem; |
|
use cpu::CPU; |
|
use rand::Rng; |
|
use sdl2::event::Event; |
|
use sdl2::keyboard::Keycode; |
|
use sdl2::pixels::Color; |
|
use sdl2::pixels::PixelFormatEnum; |
|
use sdl2::EventPump; |
|
mod cartridge; |
|
|
|
extern crate lazy_static; |
|
|
|
extern crate bitflags; |
|
|
|
/// Translate byte values to colors |
|
fn color(byte: u8) -> Color { |
|
match byte { |
|
0 => sdl2::pixels::Color::BLACK, |
|
1 => sdl2::pixels::Color::WHITE, |
|
2 | 9 => sdl2::pixels::Color::GREY, |
|
3 | 10 => sdl2::pixels::Color::RED, |
|
4 | 11 => sdl2::pixels::Color::GREEN, |
|
5 | 12 => sdl2::pixels::Color::BLUE, |
|
6 | 13 => sdl2::pixels::Color::MAGENTA, |
|
7 | 14 => sdl2::pixels::Color::YELLOW, |
|
_ => sdl2::pixels::Color::CYAN, |
|
} |
|
} |
|
|
|
/// Read a block of memory and update frame buffer with the corresponding RGB value |
|
/// If returns true, then there were changes to the frame |
|
fn read_screen_state(cpu: &CPU, frame: &mut [u8; 32 * 3 * 32]) -> bool { |
|
let mut frame_idx = 0; |
|
let mut update = false; |
|
for i in 0x0200..0x600 { |
|
let color_idx = cpu.mem_read(i as u16); |
|
let (b1, b2, b3) = color(color_idx).rgb(); |
|
if frame[frame_idx] != b1 || frame[frame_idx + 1] != b2 || frame[frame_idx + 2] != b3 { |
|
frame[frame_idx] = b1; |
|
frame[frame_idx + 1] = b2; |
|
frame[frame_idx + 2] = b3; |
|
update = true; |
|
} |
|
frame_idx += 3; |
|
} |
|
update |
|
} |
|
|
|
fn handle_user_input(cpu: &mut CPU, event_pump: &mut EventPump) { |
|
for event in event_pump.poll_iter() { |
|
match event { |
|
Event::Quit { .. } |
|
| Event::KeyDown { |
|
keycode: Some(Keycode::Escape), |
|
.. |
|
} => std::process::exit(0), |
|
Event::KeyDown { |
|
keycode: Some(Keycode::W), |
|
.. |
|
} => { |
|
cpu.mem_write(0xff, 0x77); |
|
} |
|
Event::KeyDown { |
|
keycode: Some(Keycode::S), |
|
.. |
|
} => { |
|
cpu.mem_write(0xff, 0x73); |
|
} |
|
Event::KeyDown { |
|
keycode: Some(Keycode::A), |
|
.. |
|
} => { |
|
cpu.mem_write(0xff, 0x61); |
|
} |
|
Event::KeyDown { |
|
keycode: Some(Keycode::D), |
|
.. |
|
} => { |
|
cpu.mem_write(0xff, 0x64); |
|
} |
|
_ => { /* do nothing */ } |
|
} |
|
} |
|
} |
|
|
|
fn main() { |
|
// init sdl2 |
|
let sdl_context = sdl2::init().unwrap(); |
|
let video_subsystem = sdl_context.video().unwrap(); |
|
let window = video_subsystem |
|
.window("Snake game", (32.0 * 10.0) as u32, (32.0 * 10.0) as u32) |
|
.position_centered() |
|
.build() |
|
.unwrap(); |
|
|
|
let mut canvas = window.into_canvas().present_vsync().build().unwrap(); |
|
let mut event_pump = sdl_context.event_pump().unwrap(); |
|
canvas.set_scale(20.0, 20.0).unwrap(); |
|
|
|
let creator = canvas.texture_creator(); |
|
let mut texture = creator |
|
.create_texture_target(PixelFormatEnum::RGB24, 32, 32) |
|
.unwrap(); |
|
|
|
let game_code = vec![ |
|
0x20, 0x06, 0x06, 0x20, 0x38, 0x06, 0x20, 0x0d, 0x06, 0x20, 0x2a, 0x06, 0x60, 0xa9, 0x02, |
|
0x85, 0x02, 0xa9, 0x04, 0x85, 0x03, 0xa9, 0x11, 0x85, 0x10, 0xa9, 0x10, 0x85, 0x12, 0xa9, |
|
0x0f, 0x85, 0x14, 0xa9, 0x04, 0x85, 0x11, 0x85, 0x13, 0x85, 0x15, 0x60, 0xa5, 0xfe, 0x85, |
|
0x00, 0xa5, 0xfe, 0x29, 0x03, 0x18, 0x69, 0x02, 0x85, 0x01, 0x60, 0x20, 0x4d, 0x06, 0x20, |
|
0x8d, 0x06, 0x20, 0xc3, 0x06, 0x20, 0x19, 0x07, 0x20, 0x20, 0x07, 0x20, 0x2d, 0x07, 0x4c, |
|
0x38, 0x06, 0xa5, 0xff, 0xc9, 0x77, 0xf0, 0x0d, 0xc9, 0x64, 0xf0, 0x14, 0xc9, 0x73, 0xf0, |
|
0x1b, 0xc9, 0x61, 0xf0, 0x22, 0x60, 0xa9, 0x04, 0x24, 0x02, 0xd0, 0x26, 0xa9, 0x01, 0x85, |
|
0x02, 0x60, 0xa9, 0x08, 0x24, 0x02, 0xd0, 0x1b, 0xa9, 0x02, 0x85, 0x02, 0x60, 0xa9, 0x01, |
|
0x24, 0x02, 0xd0, 0x10, 0xa9, 0x04, 0x85, 0x02, 0x60, 0xa9, 0x02, 0x24, 0x02, 0xd0, 0x05, |
|
0xa9, 0x08, 0x85, 0x02, 0x60, 0x60, 0x20, 0x94, 0x06, 0x20, 0xa8, 0x06, 0x60, 0xa5, 0x00, |
|
0xc5, 0x10, 0xd0, 0x0d, 0xa5, 0x01, 0xc5, 0x11, 0xd0, 0x07, 0xe6, 0x03, 0xe6, 0x03, 0x20, |
|
0x2a, 0x06, 0x60, 0xa2, 0x02, 0xb5, 0x10, 0xc5, 0x10, 0xd0, 0x06, 0xb5, 0x11, 0xc5, 0x11, |
|
0xf0, 0x09, 0xe8, 0xe8, 0xe4, 0x03, 0xf0, 0x06, 0x4c, 0xaa, 0x06, 0x4c, 0x35, 0x07, 0x60, |
|
0xa6, 0x03, 0xca, 0x8a, 0xb5, 0x10, 0x95, 0x12, 0xca, 0x10, 0xf9, 0xa5, 0x02, 0x4a, 0xb0, |
|
0x09, 0x4a, 0xb0, 0x19, 0x4a, 0xb0, 0x1f, 0x4a, 0xb0, 0x2f, 0xa5, 0x10, 0x38, 0xe9, 0x20, |
|
0x85, 0x10, 0x90, 0x01, 0x60, 0xc6, 0x11, 0xa9, 0x01, 0xc5, 0x11, 0xf0, 0x28, 0x60, 0xe6, |
|
0x10, 0xa9, 0x1f, 0x24, 0x10, 0xf0, 0x1f, 0x60, 0xa5, 0x10, 0x18, 0x69, 0x20, 0x85, 0x10, |
|
0xb0, 0x01, 0x60, 0xe6, 0x11, 0xa9, 0x06, 0xc5, 0x11, 0xf0, 0x0c, 0x60, 0xc6, 0x10, 0xa5, |
|
0x10, 0x29, 0x1f, 0xc9, 0x1f, 0xf0, 0x01, 0x60, 0x4c, 0x35, 0x07, 0xa0, 0x00, 0xa5, 0xfe, |
|
0x91, 0x00, 0x60, 0xa6, 0x03, 0xa9, 0x00, 0x81, 0x10, 0xa2, 0x00, 0xa9, 0x01, 0x81, 0x10, |
|
0x60, 0xa6, 0xff, 0xea, 0xea, 0xca, 0xd0, 0xfb, 0x60, |
|
]; |
|
|
|
//load the game |
|
let mut cpu = CPU::new(); |
|
cpu.load(game_code); |
|
cpu.reset(); |
|
|
|
println!("Program has started!"); |
|
|
|
let mut screen_state = [0 as u8; 32 * 3 * 32]; |
|
let mut rng = rand::thread_rng(); |
|
|
|
// run the game cycle |
|
cpu.run_with_callback(move |cpu| { |
|
handle_user_input(cpu, &mut event_pump); |
|
|
|
cpu.mem_write(0xfe, rng.gen_range(1..16)); |
|
|
|
if read_screen_state(cpu, &mut screen_state) { |
|
texture.update(None, &screen_state, 32 * 3).unwrap(); |
|
|
|
canvas.copy(&texture, None, None).unwrap(); |
|
|
|
canvas.present(); |
|
} |
|
|
|
std::thread::sleep(std::time::Duration::new(0, 70_000)); |
|
}); |
|
}
|
|
|