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