chore: limpopo update
This commit is contained in:
parent
7eda2d71ff
commit
e9770053dd
|
@ -0,0 +1,6 @@
|
||||||
|
**/target
|
||||||
|
**/zig-cache
|
||||||
|
**/.vscode
|
||||||
|
**/bin
|
||||||
|
**/rom
|
||||||
|
**/.gitignore
|
|
@ -1 +0,0 @@
|
||||||
/target
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "pw313"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Rekai Musuka <rekai@musuka.dev>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
uvc = "0.1"
|
||||||
|
winit = "0.23"
|
||||||
|
sdl2 = { version = "0.34" }
|
||||||
|
anyhow = "1.0"
|
||||||
|
image = "0.23"
|
|
@ -0,0 +1,172 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use sdl2::{
|
||||||
|
event::Event,
|
||||||
|
keyboard::Keycode,
|
||||||
|
pixels::PixelFormatEnum,
|
||||||
|
render::{Texture, TextureCreator},
|
||||||
|
surface::Surface,
|
||||||
|
video::WindowContext,
|
||||||
|
};
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
};
|
||||||
|
use uvc::{Context, Device, DeviceHandle};
|
||||||
|
|
||||||
|
fn log_found_device(device: &Device) -> Result<()> {
|
||||||
|
let info = device.description()?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Found device: Bus {:03} Device {:03} : ID {:04x}:{:04x} {} ({})",
|
||||||
|
device.bus_number(),
|
||||||
|
device.device_address(),
|
||||||
|
info.vendor_id,
|
||||||
|
info.product_id,
|
||||||
|
info.product.unwrap_or_else(|| "Unknown".to_string()),
|
||||||
|
info.manufacturer.unwrap_or_else(|| "Unknown".to_string()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_device_settings(device: &DeviceHandle) {
|
||||||
|
println!(
|
||||||
|
"Scanning mode: {:?}\nAuto-exposure mode: {:?}\nAuto-exposure priority: {:?}\nAbsolute exposure: {:?}\nRelative exposure: {:?}\nAboslute focus: {:?}\nRelative focus: {:?}",
|
||||||
|
device.scanning_mode(),
|
||||||
|
device.ae_mode(),
|
||||||
|
device.ae_priority(),
|
||||||
|
device.exposure_abs(),
|
||||||
|
device.exposure_rel(),
|
||||||
|
device.focus_abs(),
|
||||||
|
device.focus_rel(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let uvc_ctx = Context::new().expect("Could not create context");
|
||||||
|
let device = uvc_ctx
|
||||||
|
.find_device(Some(0x07ca), Some(0x313a), None)
|
||||||
|
.expect("Unable to find the AVerMedia PW313");
|
||||||
|
|
||||||
|
log_found_device(&device).unwrap();
|
||||||
|
|
||||||
|
let device = device.open().expect("Unable to open webcam.");
|
||||||
|
let format = device
|
||||||
|
.get_preferred_format(|x, y| {
|
||||||
|
if x.fps >= y.fps && x.width * x.height >= y.width * y.height {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Chosen format: {:?}", format);
|
||||||
|
|
||||||
|
let mut stream = device.get_stream_handle_with_format(format).unwrap();
|
||||||
|
log_device_settings(&device);
|
||||||
|
|
||||||
|
let frame_guard = Arc::new(Mutex::new(MyFrame::default()));
|
||||||
|
let frame_cpy = frame_guard.clone();
|
||||||
|
|
||||||
|
let counter = Arc::new(AtomicUsize::new(0));
|
||||||
|
let stream = stream
|
||||||
|
.start_stream(
|
||||||
|
move |frame_ref, count| {
|
||||||
|
let mut frame = frame_cpy.lock().unwrap();
|
||||||
|
|
||||||
|
*frame = MyFrame {
|
||||||
|
data: frame_ref.to_rgb().unwrap().to_bytes().to_owned(),
|
||||||
|
width: frame_ref.width(),
|
||||||
|
height: frame_ref.height(),
|
||||||
|
};
|
||||||
|
|
||||||
|
count.fetch_add(1, Ordering::SeqCst);
|
||||||
|
},
|
||||||
|
counter.clone(),
|
||||||
|
)
|
||||||
|
.expect("Could not start stream.");
|
||||||
|
|
||||||
|
let sdl_ctx = sdl2::init().unwrap();
|
||||||
|
let video_subsystem = sdl_ctx.video().unwrap();
|
||||||
|
|
||||||
|
let window = video_subsystem
|
||||||
|
.window("AVerMedia PW313 Web Camera", format.width, format.height)
|
||||||
|
.position_centered()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut canvas = window
|
||||||
|
.into_canvas()
|
||||||
|
.present_vsync()
|
||||||
|
.accelerated()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
for event in sdl_ctx.event_pump().unwrap().poll_iter() {
|
||||||
|
match event {
|
||||||
|
Event::Quit { .. }
|
||||||
|
| Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Escape),
|
||||||
|
..
|
||||||
|
} => break 'main,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let texture = do_thing(&frame_guard, &texture_creator);
|
||||||
|
match texture {
|
||||||
|
Some(thing) => {
|
||||||
|
canvas.copy(&thing, None, None).unwrap();
|
||||||
|
canvas.present();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_thing<'a>(
|
||||||
|
guard: &'a Arc<Mutex<MyFrame>>,
|
||||||
|
creator: &'a TextureCreator<WindowContext>,
|
||||||
|
) -> Option<Texture<'a>> {
|
||||||
|
let mut frame = guard.lock().ok()?;
|
||||||
|
let width = frame.width;
|
||||||
|
let height = frame.height;
|
||||||
|
|
||||||
|
if !frame.data.is_empty() {
|
||||||
|
// Flip Image
|
||||||
|
let surface = Surface::from_data(
|
||||||
|
frame.data.as_mut_slice(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
width * (24 / 8),
|
||||||
|
PixelFormatEnum::RGB24,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Some(Texture::from_surface(&surface, creator).ok()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// let surface = Surface::new(600, 800, PixelFormatEnum::RGB24).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct MyFrame {
|
||||||
|
data: Vec<u8>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MyFrame {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
data: vec![],
|
||||||
|
width: 600,
|
||||||
|
height: 800,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
use glium::{implement_vertex, uniform};
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use glium::Surface;
|
||||||
|
use uvc::{Context, Frame};
|
||||||
|
|
||||||
|
fn frame_to_raw_image(
|
||||||
|
frame: &Frame,
|
||||||
|
) -> Result<glium::texture::RawImage2d<'static, u8>, Box<dyn Error>> {
|
||||||
|
let new_frame = frame.to_rgb()?;
|
||||||
|
let data = new_frame.to_bytes();
|
||||||
|
|
||||||
|
let image = glium::texture::RawImage2d::from_raw_rgb(
|
||||||
|
data.to_vec(),
|
||||||
|
(new_frame.width(), new_frame.height()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn callback_frame_to_image(
|
||||||
|
frame: &Frame,
|
||||||
|
data: &mut Arc<Mutex<Option<glium::texture::RawImage2d<u8>>>>,
|
||||||
|
) {
|
||||||
|
let image = frame_to_raw_image(frame);
|
||||||
|
match image {
|
||||||
|
Err(x) => println!("{:#?}", x),
|
||||||
|
Ok(x) => {
|
||||||
|
let mut data = Mutex::lock(&data).unwrap();
|
||||||
|
*data = Some(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ctx = Context::new().expect("Could not create context");
|
||||||
|
let dev = ctx
|
||||||
|
.find_device(None, None, None)
|
||||||
|
.expect("Could not find device");
|
||||||
|
|
||||||
|
let description = dev.description().unwrap();
|
||||||
|
println!(
|
||||||
|
"Found device: Bus {:03} Device {:03} : ID {:04x}:{:04x} {} ({})",
|
||||||
|
dev.bus_number(),
|
||||||
|
dev.device_address(),
|
||||||
|
description.vendor_id,
|
||||||
|
description.product_id,
|
||||||
|
description.product.unwrap_or_else(|| "Unknown".to_owned()),
|
||||||
|
description
|
||||||
|
.manufacturer
|
||||||
|
.unwrap_or_else(|| "Unknown".to_owned())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open multiple devices by enumerating:
|
||||||
|
// let mut list = ctx.devices().expect("Could not get devices");
|
||||||
|
// let dev = list.next().expect("No device available");
|
||||||
|
|
||||||
|
let devh = dev.open().expect("Could not open device");
|
||||||
|
|
||||||
|
let format = devh
|
||||||
|
.get_preferred_format(|x, y| {
|
||||||
|
if x.fps >= y.fps && x.width * x.height >= y.width * y.height {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Best format found: {:?}", format);
|
||||||
|
let mut streamh = devh.get_stream_handle_with_format(format).unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Scanning mode: {:?}\nAuto-exposure mode: {:?}\nAuto-exposure priority: {:?}\nAbsolute exposure: {:?}\nRelative exposure: {:?}\nAboslute focus: {:?}\nRelative focus: {:?}",
|
||||||
|
devh.scanning_mode(),
|
||||||
|
devh.ae_mode(),
|
||||||
|
devh.ae_priority(),
|
||||||
|
devh.exposure_abs(),
|
||||||
|
devh.exposure_rel(),
|
||||||
|
devh.focus_abs(),
|
||||||
|
devh.focus_rel(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let frame = Arc::new(Mutex::new(None));
|
||||||
|
let _stream = streamh
|
||||||
|
.start_stream(callback_frame_to_image, frame.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
use glium::glutin;
|
||||||
|
let events_loop = glutin::event_loop::EventLoop::new();
|
||||||
|
let window = glutin::window::WindowBuilder::new().with_title("Mirror");
|
||||||
|
let context = glutin::ContextBuilder::new();
|
||||||
|
let display = glium::Display::new(window, context, &events_loop).unwrap();
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct QuadVertex {
|
||||||
|
pos: (f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
implement_vertex!(QuadVertex, pos);
|
||||||
|
|
||||||
|
let vertices: [QuadVertex; 4] = [
|
||||||
|
QuadVertex { pos: (-1.0, -1.0) },
|
||||||
|
QuadVertex { pos: (-1.0, 1.0) },
|
||||||
|
QuadVertex { pos: (1.0, -1.0) },
|
||||||
|
QuadVertex { pos: (1.0, 1.0) },
|
||||||
|
];
|
||||||
|
|
||||||
|
let indices: [u8; 6] = [0, 1, 2, 1, 3, 2];
|
||||||
|
|
||||||
|
let vertex_shader_source = r#"
|
||||||
|
#version 140
|
||||||
|
in vec2 pos;
|
||||||
|
out vec2 v_position;
|
||||||
|
void main() {
|
||||||
|
v_position = (pos + 1.0)/2.0;
|
||||||
|
gl_Position = vec4(-pos.x, -pos.y, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let fragment_shader_source = r#"
|
||||||
|
#version 140
|
||||||
|
in vec2 v_position;
|
||||||
|
out vec4 colour;
|
||||||
|
uniform sampler2D u_image;
|
||||||
|
void main() {
|
||||||
|
vec2 pos = v_position;
|
||||||
|
colour = texture(u_image, pos);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let vertices = glium::VertexBuffer::new(&display, &vertices).unwrap();
|
||||||
|
let indices = glium::IndexBuffer::new(
|
||||||
|
&display,
|
||||||
|
glium::index::PrimitiveType::TrianglesList,
|
||||||
|
&indices,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let program =
|
||||||
|
glium::Program::from_source(&display, vertex_shader_source, fragment_shader_source, None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut buffer: Option<glium::texture::SrgbTexture2d> = None;
|
||||||
|
events_loop.run(move |event, _, control_flow| {
|
||||||
|
if let glutin::event::Event::WindowEvent { event, .. } = event {
|
||||||
|
if let glutin::event::WindowEvent::CloseRequested = event {
|
||||||
|
*control_flow = glutin::event_loop::ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut target = display.draw();
|
||||||
|
target.clear_color(0.0, 0.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
let mut mutex = Mutex::lock(&frame).unwrap();
|
||||||
|
|
||||||
|
match mutex.take() {
|
||||||
|
None => {
|
||||||
|
// No new frame to render
|
||||||
|
}
|
||||||
|
Some(image) => {
|
||||||
|
let image = glium::texture::SrgbTexture2d::new(&display, image)
|
||||||
|
.expect("Could not use image");
|
||||||
|
buffer = Some(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref b) = buffer {
|
||||||
|
let uniforms = uniform! { u_image: b };
|
||||||
|
target
|
||||||
|
.draw(
|
||||||
|
&vertices,
|
||||||
|
&indices,
|
||||||
|
&program,
|
||||||
|
&uniforms,
|
||||||
|
&Default::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
target.finish().unwrap();
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
# Max's Original Solution
|
||||||
|
# password = input("Enter password: ")
|
||||||
|
# contain_digit = False
|
||||||
|
# length = len(password)
|
||||||
|
|
||||||
|
|
||||||
|
# while contain_digit == False and length < 8:
|
||||||
|
# for char in password:
|
||||||
|
# if char.isnumeric():
|
||||||
|
# contain_digit = True
|
||||||
|
# break
|
||||||
|
# else:
|
||||||
|
# contain_digit = False
|
||||||
|
# password = input("Enter valid password: ")
|
||||||
|
# length = len(password)
|
||||||
|
|
||||||
|
|
||||||
|
password = input("Enter password: ")
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
while not valid:
|
||||||
|
# Check whether there's a digit
|
||||||
|
contains_digit = False
|
||||||
|
|
||||||
|
for char in password:
|
||||||
|
if not contains_digit and char.isnumeric():
|
||||||
|
contains_digit = True
|
||||||
|
|
||||||
|
valid = contains_digit and len(password) > 8
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
password = input("Enter valid password: ")
|
||||||
|
|
||||||
|
|
||||||
|
print("Valid Password")
|
|
@ -0,0 +1,17 @@
|
||||||
|
string = input("Gimme string: ")
|
||||||
|
valid = False
|
||||||
|
start = False
|
||||||
|
end = False
|
||||||
|
|
||||||
|
while not valid:
|
||||||
|
if string[0:6] == "http://":
|
||||||
|
start = True
|
||||||
|
else:
|
||||||
|
start = False
|
||||||
|
if string[-4:] == ".com":
|
||||||
|
end = True
|
||||||
|
else:
|
||||||
|
end = False
|
||||||
|
valid = start and end
|
||||||
|
if not valid:
|
||||||
|
string = input("gimme good string: ")
|
|
@ -0,0 +1,27 @@
|
||||||
|
const Builder = @import("std").build.Builder;
|
||||||
|
|
||||||
|
pub fn build(b: *Builder) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard release options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
const exe = b.addExecutable("zig-hello", "src/main.zig");
|
||||||
|
exe.setTarget(target);
|
||||||
|
exe.setBuildMode(mode);
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn main() anyerror!void {
|
||||||
|
const test_num = 5;
|
||||||
|
std.log.info("All your codebase are belong to us.", .{});
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap');
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Roboto', Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.container {
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
background-color: #e5e5e5;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.card-container {
|
||||||
|
flex: 1 0 calc(100vw / 6);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.card {
|
||||||
|
background-color: white;
|
||||||
|
height: 500px;
|
||||||
|
width: 300px;
|
||||||
|
max-width: 350px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.card-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: calc(500px / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.card-content > p {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.card-content > footer {
|
||||||
|
height: 2em;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
color: #858585
|
||||||
|
}
|
||||||
|
|
||||||
|
div.card-content > footer > hr {
|
||||||
|
margin: 0;
|
||||||
|
color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.card > img {
|
||||||
|
border-radius: inherit;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
width: inherit;
|
||||||
|
height: calc(500px / 2);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#card-content {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#card-content > h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#card-content > p {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
div.card-container {
|
||||||
|
flex: 1 0 calc(100vw / 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
div.card-container {
|
||||||
|
flex: 1 0 100vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
<title>Nichol Assignment</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<img src="./img/Zimbabwe-Harare.jpg" alt="A View of Harare from a nearby hill">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2>My Heading</h2>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||||
|
labore
|
||||||
|
et dolore magna al</p>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<span>TWITTER</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,29 @@
|
||||||
|
const Builder = @import("std").build.Builder;
|
||||||
|
|
||||||
|
pub fn build(b: *Builder) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard release options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
const exe = b.addExecutable("zig8", "src/main.zig");
|
||||||
|
exe.setTarget(target);
|
||||||
|
exe.setBuildMode(mode);
|
||||||
|
exe.linkSystemLibrary("SDL2");
|
||||||
|
exe.linkSystemLibrary("c");
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const expect = @import("std").testing.expect;
|
||||||
|
const panic = @import("std").debug.panic;
|
||||||
|
const Dir = @import("std").fs.Dir;
|
||||||
|
const Instruction = @import("instructions.zig").Instruction;
|
||||||
|
const InstrType = @import("instructions.zig").Type;
|
||||||
|
const draw_sprite = @import("display.zig").draw_sprite;
|
||||||
|
const Display = @import("display.zig").Display;
|
||||||
|
const Point = @import("display.zig").Point;
|
||||||
|
|
||||||
|
pub const Cpu = struct {
|
||||||
|
i: u16,
|
||||||
|
pc: u16,
|
||||||
|
sp: u16,
|
||||||
|
v: [16]u8,
|
||||||
|
stack: [16]u16,
|
||||||
|
memory: [4096]u8,
|
||||||
|
disp: Display,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn fetch(cpu: *Cpu) u16 {
|
||||||
|
const high: u16 = cpu.memory[cpu.pc];
|
||||||
|
const low: u16 = cpu.memory[cpu.pc + 1];
|
||||||
|
|
||||||
|
cpu.pc += 2;
|
||||||
|
|
||||||
|
return high << 8 | low;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(cpu: *Cpu, word: u16) ?Instruction {
|
||||||
|
const nib1: u4 = @intCast(u4, (word & 0xF000) >> 12);
|
||||||
|
const nib2: u4 = @intCast(u4, (word & 0x0F00) >> 8);
|
||||||
|
const nib3: u4 = @intCast(u4, (word & 0x00F0) >> 4);
|
||||||
|
const nib4: u4 = @intCast(u4, (word & 0x000F) >> 0);
|
||||||
|
|
||||||
|
|
||||||
|
if (nib1 == 0x0) {
|
||||||
|
// nib2 is 0x0, nib3 is 0xE
|
||||||
|
|
||||||
|
if (nib4 == 0) {
|
||||||
|
// 0x00E0 | CLS
|
||||||
|
return Instruction.CLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nib4 == 0xE) {
|
||||||
|
// 0x00EE | RET
|
||||||
|
return Instruction.RET;
|
||||||
|
}
|
||||||
|
} else if (nib1 == 0x1) {
|
||||||
|
// 0x1nnn | JP addr
|
||||||
|
|
||||||
|
const addr = calc_u12(nib2, nib3, nib4);
|
||||||
|
return Instruction { .JP = addr };
|
||||||
|
} else if (nib1 == 0x2) {
|
||||||
|
// 0x2nnn | CALL addr
|
||||||
|
|
||||||
|
const addr = calc_u12(nib2, nib3, nib4);
|
||||||
|
return Instruction { .CALL = addr };
|
||||||
|
} else if (nib1 == 0x3) {
|
||||||
|
// 0x3xkk | SE Vx, u8
|
||||||
|
|
||||||
|
return Instruction { .SE_3 = .{ .x = nib2, .kk = calc_u8(nib3, nib4) }};
|
||||||
|
} else if (nib1 == 0x6) {
|
||||||
|
// 0x6xkk | LD Vx, u8
|
||||||
|
|
||||||
|
return Instruction { .LD_6 = .{ .x = nib2, .kk = calc_u8(nib3, nib4) }};
|
||||||
|
} else if(nib1 == 0x7) {
|
||||||
|
// 0x7xkk | LD Vx, kk
|
||||||
|
|
||||||
|
return Instruction { .ADD_7 = .{ .x = nib2, .kk = calc_u8(nib3, nib4) }};
|
||||||
|
} else if (nib1 == 0xA) {
|
||||||
|
// 0xAnnn | LD I, addr
|
||||||
|
const addr = calc_u12(nib2, nib3, nib4);
|
||||||
|
return Instruction { .LD_I = addr };
|
||||||
|
} else if (nib1 == 0xD) {
|
||||||
|
// 0xDxyn | DRW Vx, Vy, n
|
||||||
|
return Instruction {.DRW = .{ .x = nib2, .y = nib3, .n = nib4 }};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(cpu: *Cpu, instr: Instruction) void {
|
||||||
|
switch (instr) {
|
||||||
|
.CLS => std.debug.print("CLS\n", .{}),
|
||||||
|
.RET => {
|
||||||
|
std.debug.print("RET\n", .{});
|
||||||
|
|
||||||
|
cpu.pc = cpu.stack[cpu.sp];
|
||||||
|
cpu.sp -= 1;
|
||||||
|
},
|
||||||
|
.JP => |addr| {
|
||||||
|
std.debug.print("JP 0x{X:}\n", .{ addr });
|
||||||
|
|
||||||
|
cpu.pc = addr;
|
||||||
|
},
|
||||||
|
.CALL => |addr| {
|
||||||
|
std.debug.print("CALL 0x{X:}\n", .{ addr });
|
||||||
|
|
||||||
|
cpu.sp += 1;
|
||||||
|
cpu.stack[cpu.sp] = cpu.pc;
|
||||||
|
cpu.pc = addr;
|
||||||
|
},
|
||||||
|
.SE_3 => |args| {
|
||||||
|
const x = args.x;
|
||||||
|
const kk = args.kk;
|
||||||
|
std.debug.print("SE V{}, 0x{X:}\n", .{ x, kk });
|
||||||
|
|
||||||
|
if (cpu.v[x] != kk) {
|
||||||
|
cpu.pc += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.LD_6 => |args| {
|
||||||
|
const x = args.x;
|
||||||
|
const kk = args.kk;
|
||||||
|
std.debug.print("LD V{} 0x{X:}\n", .{ x, kk });
|
||||||
|
|
||||||
|
cpu.v[x] = kk;
|
||||||
|
},
|
||||||
|
.LD_I => |addr| {
|
||||||
|
std.debug.print("LD I, 0x{X:}\n", .{ addr });
|
||||||
|
cpu.i = addr;
|
||||||
|
},
|
||||||
|
.DRW => |args| {
|
||||||
|
const x = args.x;
|
||||||
|
const y = args.y;
|
||||||
|
const n = args.n;
|
||||||
|
std.debug.print("DRW V{}, V{}, 0x{X:}\n", .{ x, y, n });
|
||||||
|
|
||||||
|
const draw_pos: Point = .{ .x = cpu.v[x], .y = cpu.v[y] };
|
||||||
|
const sprite_data = cpu.memory[cpu.i..(cpu.i + n)];
|
||||||
|
|
||||||
|
// Draw Sprite
|
||||||
|
const collision = draw_sprite(&cpu.disp, &draw_pos, &sprite_data);
|
||||||
|
|
||||||
|
cpu.v[0xF] = if (collision) 1 else 0;
|
||||||
|
// Request Redraw
|
||||||
|
},
|
||||||
|
.ADD_7 => |args| {
|
||||||
|
const x = args.x;
|
||||||
|
const kk = args.kk;
|
||||||
|
std.debug.print("ADD V{}, 0x{X:}\n", .{ x, kk });
|
||||||
|
|
||||||
|
cpu.v[x] = cpu.v[x] + kk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_rom(cpu: *Cpu, buf: []u8) void {
|
||||||
|
var mem_index: usize = 0x200;
|
||||||
|
|
||||||
|
for (buf) |byte| {
|
||||||
|
cpu.memory[mem_index] = byte;
|
||||||
|
mem_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_rom_path(cpu: *Cpu, path: []const u8) !void {
|
||||||
|
var alloc = std.heap.page_allocator;
|
||||||
|
|
||||||
|
const file = try std.fs.cwd().openFile(path, .{ .read = true });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const file_size = try file.getEndPos();
|
||||||
|
const rom_buf = try file.readToEndAlloc(alloc, file_size);
|
||||||
|
|
||||||
|
load_rom(cpu, rom_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle(cpu: *Cpu) void {
|
||||||
|
const opcode = fetch(cpu);
|
||||||
|
const maybe_instr = decode(cpu, opcode);
|
||||||
|
|
||||||
|
if (maybe_instr) |instr| {
|
||||||
|
execute(cpu, instr);
|
||||||
|
} else {
|
||||||
|
panic("Unknown Opcode: 0x{X:}", . { opcode });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_u12(nib2: u4, nib3: u4, nib4: u4) u12 {
|
||||||
|
return @as(u12, nib2) << 8 | @as(u12, nib3) << 4 | @as(u12, nib4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_u8(nib_a: u4, nib_b: u4) u8 {
|
||||||
|
return @as(u8, nib_a) << 4 | @as(u8, nib_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "calc_u12 works" {
|
||||||
|
const left: u12 = 0xABC;
|
||||||
|
const right: u12 = calc_u12(0xA, 0xB, 0xC);
|
||||||
|
|
||||||
|
expect(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "calc_u8 works" {
|
||||||
|
const left: u8 = 0xAB;
|
||||||
|
const right: u12 = calc_u8(0xA, 0xB);
|
||||||
|
|
||||||
|
expect(left == right);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
const DISPLAY_WIDTH: usize = 64;
|
||||||
|
const DISPLAY_HEIGHT: usize = 32;
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Point = struct {
|
||||||
|
x: u16,
|
||||||
|
y: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
pub const Display = struct {
|
||||||
|
buf: [DISPLAY_HEIGHT * DISPLAY_WIDTH]u8,
|
||||||
|
|
||||||
|
|
||||||
|
pub fn new() Display {
|
||||||
|
return .{
|
||||||
|
.buf = [_]u8{0x00} ** (DISPLAY_HEIGHT * DISPLAY_WIDTH)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn clear(disp: *Display) void {
|
||||||
|
disp.buf = u8[DISPLAY_WIDTH * DISPLAY_HEIGHT]{0x00};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_sprite(disp: *Display, pos: *const Point, data: *const []u8) bool {
|
||||||
|
var set_vf: bool = false;
|
||||||
|
|
||||||
|
for (data.*) |byte, y_offset| {
|
||||||
|
var offset_count: u8 = 0;
|
||||||
|
|
||||||
|
while (offset_count < 8) : (offset_count += 1) {
|
||||||
|
const x_bit_offset = @intCast(u3, offset_count);
|
||||||
|
const x = @intCast(u8, pos.x + (7 - x_bit_offset));
|
||||||
|
const y = @intCast(u8, pos.y + y_offset);
|
||||||
|
|
||||||
|
|
||||||
|
const temp = (byte >> x_bit_offset) & 0x01;
|
||||||
|
const bit = @intCast(u1, temp);
|
||||||
|
// const bit = @as(u1, temp);
|
||||||
|
|
||||||
|
const i = DISPLAY_WIDTH * y + x;
|
||||||
|
|
||||||
|
if (i >= disp.buf.len) break;
|
||||||
|
|
||||||
|
if (bit == 0x1 and disp.buf[i] == 0x01) {
|
||||||
|
set_vf = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
disp.buf[i] ^= bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return set_vf;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
pub const Instruction = union(enum) {
|
||||||
|
CLS: void, // 0x00E0
|
||||||
|
RET: void, // 0x00EE
|
||||||
|
JP: u12, // 0x1nnn
|
||||||
|
CALL: u12, // 0x2nnn
|
||||||
|
SE_3: struct { x: u4, kk: u8 }, // 0x3xkk
|
||||||
|
LD_6: struct {x: u4, kk: u8}, // 0x6xkk
|
||||||
|
LD_I: u12, // 0xAnnn
|
||||||
|
DRW: struct { x: u4, y: u4, n: u4 }, // 0xDxyn
|
||||||
|
ADD_7: struct { x: u4, kk: u8 } // 0x7xkk
|
||||||
|
};
|
|
@ -0,0 +1,107 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const cpu = @import("cpu.zig");
|
||||||
|
const Display = @import("display.zig").Display;
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("SDL2/SDL.h");
|
||||||
|
});
|
||||||
|
const assert = @import("std").debug.assert;
|
||||||
|
|
||||||
|
// See https://github.com/zig-lang/zig/issues/565
|
||||||
|
// SDL_video.h:#define SDL_WINDOWPOS_UNDEFINED SDL_WINDOWPOS_UNDEFINED_DISPLAY(0)
|
||||||
|
// SDL_video.h:#define SDL_WINDOWPOS_UNDEFINED_DISPLAY(X) (SDL_WINDOWPOS_UNDEFINED_MASK|(X))
|
||||||
|
// SDL_video.h:#define SDL_WINDOWPOS_UNDEFINED_MASK 0x1FFF0000u
|
||||||
|
const SDL_WINDOWPOS_UNDEFINED = @bitCast(c_int, c.SDL_WINDOWPOS_UNDEFINED_MASK);
|
||||||
|
|
||||||
|
extern fn SDL_PollEvent(event: *c.SDL_Event) c_int;
|
||||||
|
|
||||||
|
// SDL_RWclose is fundamentally unrepresentable in Zig, because `ctx` is
|
||||||
|
// evaluated twice. One could make the case that this is a bug in SDL,
|
||||||
|
// especially since the docs list a real function prototype that would not
|
||||||
|
// have this double-evaluation of the parameter.
|
||||||
|
// If SDL would instead of a macro use a static inline function,
|
||||||
|
// it would resolve the SDL bug as well as make the function visible to Zig
|
||||||
|
// and to debuggers.
|
||||||
|
// SDL_rwops.h:#define SDL_RWclose(ctx) (ctx)->close(ctx)
|
||||||
|
inline fn SDL_RWclose(ctx: [*]c.SDL_RWops) c_int {
|
||||||
|
return ctx[0].close.?(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
// Initialize SDL2
|
||||||
|
if (c.SDL_Init(c.SDL_INIT_VIDEO) != 0) {
|
||||||
|
c.SDL_Log("Unable to initialize SDL: %s", c.SDL_GetError());
|
||||||
|
return error.SDLInitializationFailed;
|
||||||
|
}
|
||||||
|
defer c.SDL_Quit();
|
||||||
|
|
||||||
|
// Create Screen (Maybe introduce scaling somehow?)
|
||||||
|
const screen = c.SDL_CreateWindow("Zig8", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 320, c.SDL_WINDOW_OPENGL) orelse
|
||||||
|
{
|
||||||
|
c.SDL_Log("Unable to create window: %s", c.SDL_GetError());
|
||||||
|
return error.SDLInitializationFailed;
|
||||||
|
};
|
||||||
|
defer c.SDL_DestroyWindow(screen);
|
||||||
|
|
||||||
|
// Create Renderer
|
||||||
|
const renderer = c.SDL_CreateRenderer(screen, -1, c.SDL_RENDERER_ACCELERATED) orelse {
|
||||||
|
c.SDL_Log("Unable to create renderer: %s", c.SDL_GetError());
|
||||||
|
return error.SDLInitializationFailed;
|
||||||
|
};
|
||||||
|
defer c.SDL_DestroyRenderer(renderer);
|
||||||
|
|
||||||
|
// Create RGBA Texture
|
||||||
|
const texture = c.SDL_CreateTexture(renderer, c.SDL_PIXELFORMAT_RGBA8888, c.SDL_TEXTUREACCESS_STREAMING, 64, 32) orelse {
|
||||||
|
c.SDL_Log("Unable to create texture: %s", c.SDL_GetError());
|
||||||
|
return error.SDLInitializationFailed;
|
||||||
|
};
|
||||||
|
defer c.SDL_DestroyTexture(texture);
|
||||||
|
|
||||||
|
// Initialize CHIP-8 Emulator
|
||||||
|
var chip8 = cpu.Cpu {
|
||||||
|
.i = 0x00,
|
||||||
|
.pc = 0x0200,
|
||||||
|
.sp = 0x00,
|
||||||
|
.v = [_]u8{0x0} ** 16,
|
||||||
|
.stack = [_]u16{0x0} ** 16,
|
||||||
|
.memory = [_]u8{0x0} ** 4096,
|
||||||
|
.disp = Display.new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var path = "./bin/IBM Logo.ch8";
|
||||||
|
|
||||||
|
// Load Game ROM into CHIP-8 Emulator
|
||||||
|
try cpu.load_rom_path(&chip8, path);
|
||||||
|
|
||||||
|
|
||||||
|
// Create Pixel Buffer (Temporary)
|
||||||
|
const buf_size = 64 * 32 * @sizeOf(u32);
|
||||||
|
var pixels: [buf_size]u8 = [_]u8{255} ** buf_size;
|
||||||
|
|
||||||
|
// Render
|
||||||
|
var quit = false;
|
||||||
|
while (!quit) {
|
||||||
|
|
||||||
|
var buf_ptr: [*c]?*c_void = &@ptrCast(?*c_void, &pixels[0]);
|
||||||
|
|
||||||
|
_ = c.SDL_LockTexture(texture, null, buf_ptr, 64 * @sizeOf(u32));
|
||||||
|
c.SDL_UnlockTexture(texture);
|
||||||
|
|
||||||
|
var event: c.SDL_Event = undefined;
|
||||||
|
while (SDL_PollEvent(&event) != 0) {
|
||||||
|
switch (event.@"type") {
|
||||||
|
c.SDL_QUIT => {
|
||||||
|
quit = true;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = c.SDL_RenderClear(renderer);
|
||||||
|
_ = c.SDL_RenderCopy(renderer, texture, null, null);
|
||||||
|
c.SDL_RenderPresent(renderer);
|
||||||
|
|
||||||
|
cpu.cycle(&chip8);
|
||||||
|
|
||||||
|
c.SDL_Delay(17);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue