initial commit
This commit is contained in:
commit
ffbb823d6e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
zig-cache
|
||||
zig-out
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# CHIP-8 Emulator
|
||||
|
||||
Techinal reference: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
|
BIN
ROMs/flightrunner.ch8
Normal file
BIN
ROMs/flightrunner.ch8
Normal file
Binary file not shown.
BIN
ROMs/morse_demo.ch8
Normal file
BIN
ROMs/morse_demo.ch8
Normal file
Binary file not shown.
BIN
ROMs/octojam1title.ch8
Normal file
BIN
ROMs/octojam1title.ch8
Normal file
Binary file not shown.
BIN
ROMs/test_opcode.ch8
Normal file
BIN
ROMs/test_opcode.ch8
Normal file
Binary file not shown.
37
build.zig
Normal file
37
build.zig
Normal file
@ -0,0 +1,37 @@
|
||||
const std = @import("std");
|
||||
const rl = @import("raylib-zig/build.zig");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
var raylib = rl.getModule(b, "raylib-zig");
|
||||
var raylib_math = rl.math.getModule(b, "raylib-zig");
|
||||
//web exports are completely separate
|
||||
if (target.getOsTag() == .emscripten) {
|
||||
const exe_lib = rl.compileForEmscripten(b, "chip8-zig", "src/main.zig", target, optimize);
|
||||
exe_lib.addModule("raylib", raylib);
|
||||
exe_lib.addModule("raylib-math", raylib_math);
|
||||
const raylib_artifact = rl.getArtifact(b, target, optimize);
|
||||
// Note that raylib itself is not actually added to the exe_lib output file, so it also needs to be linked with emscripten.
|
||||
exe_lib.linkLibrary(raylib_artifact);
|
||||
const link_step = try rl.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact });
|
||||
b.getInstallStep().dependOn(&link_step.step);
|
||||
const run_step = try rl.emscriptenRunStep(b);
|
||||
run_step.step.dependOn(&link_step.step);
|
||||
const run_option = b.step("run", "Run chip8-zig");
|
||||
run_option.dependOn(&run_step.step);
|
||||
return;
|
||||
}
|
||||
|
||||
const exe = b.addExecutable(.{ .name = "chip8-zig", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target });
|
||||
|
||||
rl.link(b, exe, target, optimize);
|
||||
exe.addModule("raylib", raylib);
|
||||
exe.addModule("raylib-math", raylib_math);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
const run_step = b.step("run", "Run chip8-zig");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
b.installArtifact(exe);
|
||||
}
|
10
build.zig.zon
Normal file
10
build.zig.zon
Normal file
@ -0,0 +1,10 @@
|
||||
.{
|
||||
.name = "zig-raylib",
|
||||
.version = "0.1.0",
|
||||
.dependencies = .{
|
||||
.raylib = .{
|
||||
.url = "https://github.com/raysan5/raylib/archive/6094869e3e845e90e1e8ae41b98e889fb3e13e78.tar.gz",
|
||||
.hash = "12203b7a16bcf8d7fe4c9990a46d92b6f2e35531a4b82eb3bdf8ba4a0dbcc5f21415",
|
||||
},
|
||||
},
|
||||
}
|
1
raylib-zig
Submodule
1
raylib-zig
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a106b9968fab5eae97a8f9fc85e0379358642c8d
|
379
src/chip.zig
Normal file
379
src/chip.zig
Normal file
@ -0,0 +1,379 @@
|
||||
const std = @import("std");
|
||||
|
||||
const print = std.debug.print;
|
||||
|
||||
fn get_inst_x(inst: u16) u4 {
|
||||
return @truncate((inst & 0x0F00) >> 8);
|
||||
}
|
||||
|
||||
fn get_inst_kk(inst: u16) u8 {
|
||||
return @truncate(inst & 0x00FF);
|
||||
}
|
||||
|
||||
fn get_inst_y(inst: u16) u4 {
|
||||
return @truncate((inst & 0x00F0) >> 4);
|
||||
}
|
||||
|
||||
fn get_inst_nnn(inst: u16) u12 {
|
||||
return @truncate(inst & 0x0FFF);
|
||||
}
|
||||
|
||||
fn get_inst_n(inst: u16) u4 {
|
||||
return @truncate(inst & 0x000F);
|
||||
}
|
||||
|
||||
const ChipErrors = error { UnknownInstruction };
|
||||
|
||||
pub const ChipContext = struct {
|
||||
pub const MEMORY_SIZE: u16 = 4096;
|
||||
pub const DISPLAY_WIDTH: u16 = 64;
|
||||
pub const DISPLAY_HEIGHT: u16 = 32;
|
||||
pub const DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT;
|
||||
|
||||
display: [DISPLAY_SIZE]bool,
|
||||
memory: [MEMORY_SIZE]u8,
|
||||
stack: [16]u16,
|
||||
rng: std.rand.DefaultPrng,
|
||||
input: [16]bool,
|
||||
|
||||
V: [16]u8,
|
||||
|
||||
I: u16, // Address pointer
|
||||
PC: u16, // Program counter
|
||||
SP: u8, // Stack pointer
|
||||
DT: u8, // Delay timer
|
||||
ST: u8, // Sound timer
|
||||
|
||||
pub fn init() ChipContext {
|
||||
const seed_bits: u128 = @bitCast(std.time.nanoTimestamp());
|
||||
const seed: u64 = @truncate(seed_bits);
|
||||
|
||||
return ChipContext{
|
||||
.display = [1]bool{false} ** DISPLAY_SIZE,
|
||||
.memory = [1]u8{0} ** MEMORY_SIZE,
|
||||
.stack = [1]u16{0} ** 16,
|
||||
.V = [1]u8{0} ** 16,
|
||||
.I = 0,
|
||||
.PC = 0x200,
|
||||
.SP = 0,
|
||||
.DT = 0,
|
||||
.ST = 0,
|
||||
.rng = std.rand.DefaultPrng.init(seed),
|
||||
.input = [1]bool{false} ** 16
|
||||
};
|
||||
}
|
||||
|
||||
pub fn display_get(self: *ChipContext, x: u8, y: u8) bool {
|
||||
return self.display[y * ChipContext.DISPLAY_WIDTH + x];
|
||||
}
|
||||
|
||||
pub fn display_set(self: *ChipContext, x: u8, y: u8, value: bool) void {
|
||||
self.display[y * ChipContext.DISPLAY_WIDTH + x] = value;
|
||||
}
|
||||
|
||||
pub fn draw(self: *ChipContext, x: u8, y: u8, n: u4) bool {
|
||||
var result = false;
|
||||
for (0..n) |i| {
|
||||
const sprite_row = self.memory[self.I + i];
|
||||
for (0..8) |ix| {
|
||||
const disp_x: u8 = @intCast((x+ix) % ChipContext.DISPLAY_WIDTH);
|
||||
const disp_y: u8 = @intCast((y+i) % ChipContext.DISPLAY_HEIGHT);
|
||||
const sprite_pixel = ((sprite_row >> @intCast(7-ix)) & 1) == 1;
|
||||
|
||||
const display_pixel = self.display_get(disp_x, disp_y);
|
||||
const new_pixel = (display_pixel != sprite_pixel);
|
||||
self.display_set(disp_x, disp_y, new_pixel);
|
||||
if (display_pixel and display_pixel != new_pixel) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn is_input_pressed(self: *ChipContext, key: u4) bool {
|
||||
return self.input[key];
|
||||
}
|
||||
|
||||
pub fn set_memory(self: *ChipContext, base_address: u16, memory: []const u8) void {
|
||||
for (0.., memory) |offset, byte| {
|
||||
self.memory[base_address + offset] = byte;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_memory_from_file(self: *ChipContext, base_address: u16, file: std.fs.File) !void {
|
||||
const stat = try file.stat();
|
||||
|
||||
var buffer: [512]u8 = undefined;
|
||||
var offset: usize = 0;
|
||||
var bytes_left: i64 = @intCast(stat.size);
|
||||
while (bytes_left > 0) : (bytes_left -= buffer.len) {
|
||||
const bytes_read = try file.read(&buffer);
|
||||
for (0..bytes_read) |byte_index| {
|
||||
self.memory[base_address + offset + byte_index] = buffer[byte_index];
|
||||
}
|
||||
offset += buffer.len;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(self: *ChipContext) !void {
|
||||
const high_byte: u16 = self.memory[self.PC];
|
||||
const low_byte: u16 = self.memory[self.PC+1];
|
||||
const instruction: u16 = (high_byte << 8) | low_byte;
|
||||
self.PC += 2;
|
||||
try self.run_instruction(instruction);
|
||||
}
|
||||
|
||||
pub fn update_timer(self: *ChipContext) void {
|
||||
if (self.DT > 0) {
|
||||
self.DT -= 1;
|
||||
}
|
||||
if (self.ST > 0) {
|
||||
self.ST -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_display(self: *ChipContext) void {
|
||||
for (0..DISPLAY_SIZE) |i| {
|
||||
self.display[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_instruction(self: *ChipContext, inst: u16) !void {
|
||||
print("[{x:0>4}] ({x:0>4}) ", .{ self.PC, inst });
|
||||
|
||||
if (inst & 0xFFFF == 0x00E0) { // 00E0 - CLS
|
||||
print("CLS\n", .{});
|
||||
self.clear_display();
|
||||
|
||||
} else if (inst & 0xFFFF == 0x00EE) { // 00EE - RET
|
||||
self.PC = self.stack[self.SP];
|
||||
self.SP -= 1;
|
||||
print("RET\n", .{});
|
||||
|
||||
} else if (inst & 0xF000 == 0x0000) { // 0nnn - SYS addr
|
||||
const addr = inst & 0x0FFF;
|
||||
print("SYS 0x{x}\n", .{ addr });
|
||||
|
||||
} else if (inst & 0xF000 == 0x1000) { // 1nnn - JP addr
|
||||
const addr = inst & 0x0FFF;
|
||||
self.PC = addr;
|
||||
print("JP 0x{x}\n", .{ addr });
|
||||
|
||||
} else if (inst & 0xF000 == 0x2000) { // 2nnn - CALL addr
|
||||
const addr = inst & 0x0FFF;
|
||||
self.SP += 1;
|
||||
self.stack[self.SP] = self.PC;
|
||||
self.PC = addr;
|
||||
print("CALL 0x{x}\n", .{ addr });
|
||||
|
||||
} else if (inst & 0xF000 == 0x3000) { // 3xkk - SE Vx, byte
|
||||
const Vx = get_inst_x(inst);
|
||||
const byte = get_inst_kk(inst);
|
||||
if (self.V[Vx] == byte) {
|
||||
self.PC += 2;
|
||||
}
|
||||
print("SE V{x}, 0x{x}\n", .{ Vx, byte });
|
||||
|
||||
} else if (inst & 0xF000 == 0x4000) { // 4xkk - SNE Vx, byte
|
||||
const x = get_inst_x(inst);
|
||||
const byte = get_inst_kk(inst);
|
||||
if (self.V[x] != byte) {
|
||||
self.PC += 2;
|
||||
}
|
||||
print("SNE V{x}, 0x{x}\n", .{ x, byte });
|
||||
|
||||
} else if (inst & 0xF00F == 0x5000) { // 5xy0 - SE Vx, Vy
|
||||
const Vx = get_inst_x(inst);
|
||||
const Vy = get_inst_y(inst);
|
||||
if (self.V[Vx] == self.V[Vy]) {
|
||||
self.PC += 2;
|
||||
}
|
||||
print("SE V{x}, V{x}\n", .{ Vx, Vy });
|
||||
|
||||
} else if (inst & 0xF000 == 0x6000) { // 6xkk - LD Vx, byte
|
||||
const Vx = get_inst_x(inst);
|
||||
const byte = get_inst_kk(inst);
|
||||
self.V[Vx] = byte;
|
||||
print("LD V{x}, 0x{x}\n", .{ Vx, byte });
|
||||
|
||||
} else if (inst & 0xF000 == 0x7000) { // 7xkk - ADD Vx, byte
|
||||
const x = get_inst_x(inst);
|
||||
const byte = get_inst_kk(inst);
|
||||
self.V[x] +%= byte;
|
||||
print("ADD V{x}, 0x{x}\n", .{ x, byte });
|
||||
|
||||
} else if (inst & 0xF000 == 0x8000) {
|
||||
if (inst & 0x000F == 0x0) { // 8xy0 - LD Vx, Vy
|
||||
const x = get_inst_x(inst);
|
||||
const y = get_inst_y(inst);
|
||||
self.V[x] = self.V[y];
|
||||
print("LD V{x}, V{x}\n", .{ x, y });
|
||||
|
||||
} else if (inst & 0x000F == 0x1) { // 8xy1 - OR Vx, Vy
|
||||
const x = get_inst_x(inst);
|
||||
const y = get_inst_y(inst);
|
||||
self.V[x] |= self.V[y];
|
||||
print("OR V{x}, V{x}\n", .{ x, y });
|
||||
|
||||
} else if (inst & 0x000F == 0x2) { // 8xy2 - AND Vx, Vy
|
||||
const x = get_inst_x(inst);
|
||||
const y = get_inst_y(inst);
|
||||
self.V[x] &= self.V[y];
|
||||
print("AND V{x}, V{x}\n", .{ x, y });
|
||||
|
||||
} else if (inst & 0x000F == 0x3) { // 8xy3 - XOR Vx, Vy
|
||||
const Vx = get_inst_x(inst);
|
||||
const Vy = get_inst_y(inst);
|
||||
self.V[Vx] ^= self.V[Vy];
|
||||
print("XOR V{x}, V{x}\n", .{ Vx, Vy });
|
||||
|
||||
} else if (inst & 0x000F == 0x4) { // 8xy4 - ADD Vx, Vy, set VF = carry
|
||||
const x = get_inst_x(inst);
|
||||
const y = get_inst_y(inst);
|
||||
const result = @addWithOverflow(self.V[x], self.V[y]);
|
||||
self.V[x] = result[0];
|
||||
self.V[0xF] = (1 - result[1]);
|
||||
print("ADD V{x}, V{x}\n", .{ x, y });
|
||||
|
||||
} else if (inst & 0x000F == 0x5) { // 8xy5 - SUB Vx, Vy, set VF = NOT borrow
|
||||
const x = get_inst_x(inst);
|
||||
const y = get_inst_y(inst);
|
||||
const result = @subWithOverflow(self.V[x], self.V[y]);
|
||||
self.V[x] = result[0];
|
||||
self.V[0xF] = (1 - result[1]);
|
||||
print("SUB V{x}, V{x}\n", .{ x, y });
|
||||
|
||||
} else if (inst & 0x000F == 0x6) { // 8xy6 - Vx SHR 1
|
||||
const x = get_inst_x(inst);
|
||||
self.V[0xF] = self.V[x] & 1;
|
||||
self.V[x] = (self.V[x] >> 1);
|
||||
print("SHR V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN Vx, Vy
|
||||
const Vx = get_inst_x(inst);
|
||||
const Vy = get_inst_y(inst);
|
||||
const sub: i16 = self.V[Vy] - self.V[Vx];
|
||||
const not_borrow = @intFromBool(sub > 0);
|
||||
self.V[Vx] = @intCast(sub & 0x00FF);
|
||||
self.V[0xF] = not_borrow;
|
||||
print("SUBN V{x}, V{x}\n", .{ Vx, Vy });
|
||||
|
||||
} else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1
|
||||
const Vx = get_inst_x(inst);
|
||||
self.V[0xF] = self.V[Vx] & 128;
|
||||
self.V[Vx] = (self.V[Vx] << 1);
|
||||
print("SHL V{x}\n", .{ Vx });
|
||||
|
||||
} else {
|
||||
print("UNKNOWN\n", .{ });
|
||||
return ChipErrors.UnknownInstruction;
|
||||
}
|
||||
|
||||
} else if (inst & 0xF00F == 0x9000) { // 9xy0 - SNE Vx, Vy
|
||||
const Vx = get_inst_x(inst);
|
||||
const Vy = get_inst_y(inst);
|
||||
if (self.V[Vx] != self.V[Vy]) {
|
||||
self.PC += 2;
|
||||
}
|
||||
print("SNE V{x}, V{x}\n", .{ Vx, Vy });
|
||||
|
||||
} else if (inst & 0xF000 == 0xA000) { // Annn - LD I, addr
|
||||
const addr = get_inst_nnn(inst);
|
||||
self.I = addr;
|
||||
print("LD I, 0x{x}\n", .{ addr });
|
||||
|
||||
} else if (inst & 0xF000 == 0xB000) { // Bnnn - JP V0, addr
|
||||
const addr = get_inst_nnn(inst);
|
||||
self.PC = self.V[0] + addr;
|
||||
print("JP V0, 0x{x}\n", .{ addr });
|
||||
|
||||
} else if (inst & 0xF000 == 0xC000) { // Cxkk - RNG Vx, byte
|
||||
const Vx = get_inst_x(inst);
|
||||
const mask = get_inst_kk(inst);
|
||||
self.V[Vx] = self.rng.random().int(u8) & mask;
|
||||
print("RNG V{x}, 0x{x}\n", .{ Vx, mask });
|
||||
|
||||
} else if (inst & 0xF000 == 0xD000) { // Dxyn - DRW Vx, Vy, nibble
|
||||
const Vx = get_inst_x(inst);
|
||||
const Vy = get_inst_y(inst);
|
||||
const n = get_inst_n(inst);
|
||||
self.V[0xF] = @intFromBool(self.draw(self.V[Vx], self.V[Vy], n));
|
||||
print("DRW V{x}, V{x}, 0x{x}\n", .{ Vx, Vy, n });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xE09E) { // Ex9E - SKP Vx
|
||||
const x = get_inst_x(inst);
|
||||
if (self.is_input_pressed(@truncate(self.V[x]))) {
|
||||
self.PC += 2;
|
||||
}
|
||||
print("SKP V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xE0A1) { // ExA1 - SKNP Vx
|
||||
const x = get_inst_x(inst);
|
||||
if (!self.is_input_pressed(@truncate(self.V[x]))) {
|
||||
self.PC += 2;
|
||||
}
|
||||
print("SKNP V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF007) { // Fx07 - LD Vx, DT
|
||||
const x = get_inst_x(inst);
|
||||
self.V[x] = self.DT;
|
||||
print("LD V{x}, DT\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF00A) { // Fx0A - LD Vx, K
|
||||
const x = get_inst_x(inst);
|
||||
if (!self.is_input_pressed(@truncate(self.V[x]))) {
|
||||
self.PC -= 2;
|
||||
}
|
||||
print("LD V{x}, K\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF015) { // Fx15 - LD DT, Vx
|
||||
const x = get_inst_x(inst);
|
||||
self.DT = self.V[x];
|
||||
print("LD DT, V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF018) { // Fx1E - LD ST, Vx
|
||||
const x = get_inst_x(inst);
|
||||
self.ST = self.V[x];
|
||||
print("LD ST, V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF01E) { // Fx1E - ADD I, Vx
|
||||
const x = get_inst_x(inst);
|
||||
self.I += self.V[x];
|
||||
print("ADD I, V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF029) { // Fx29 - LD F, Vx
|
||||
const x = get_inst_x(inst);
|
||||
self.I = self.V[x] * 5;
|
||||
print("LD F, V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF033) { // Fx33 - LD B, Vx
|
||||
const x = get_inst_x(inst);
|
||||
const Vx = self.V[x];
|
||||
self.memory[self.I+0] = @divFloor(Vx, 100);
|
||||
self.memory[self.I+1] = @divFloor(Vx, 10) % 10;
|
||||
self.memory[self.I+2] = Vx % 10;
|
||||
print("LD B, V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF055) { // Fx55 - LD [I], Vx
|
||||
const x = get_inst_x(inst);
|
||||
var i: u4 = 0;
|
||||
while (i <= x) : (i += 1) {
|
||||
self.memory[self.I + i] = self.V[i];
|
||||
}
|
||||
print("LD [I], V{x}\n", .{ x });
|
||||
|
||||
} else if (inst & 0xF0FF == 0xF065) { // Fx65 - LD Vx, [I]
|
||||
const x = get_inst_x(inst);
|
||||
var i: u4 = 0;
|
||||
while (i <= x) : (i += 1) {
|
||||
self.V[i] = self.memory[self.I + i];
|
||||
}
|
||||
print("LD V{x}, [I]\n", .{ x });
|
||||
|
||||
} else {
|
||||
print("UNKNOWN\n", .{ });
|
||||
return ChipErrors.UnknownInstruction;
|
||||
}
|
||||
}
|
||||
};
|
147
src/main.zig
Executable file
147
src/main.zig
Executable file
@ -0,0 +1,147 @@
|
||||
const rl = @import("raylib");
|
||||
const std = @import("std");
|
||||
const ChipContext = @import("chip.zig").ChipContext;
|
||||
|
||||
pub fn render_display(chip: *ChipContext, x: i32, y: i32, pixel_size: i32, on_color: rl.Color, off_color: rl.Color) void {
|
||||
for (0..ChipContext.DISPLAY_HEIGHT) |oy| {
|
||||
for (0..ChipContext.DISPLAY_WIDTH) |ox| {
|
||||
const ix = x + @as(i32, @intCast(ox)) * pixel_size;
|
||||
const iy = y + @as(i32, @intCast(oy)) * pixel_size;
|
||||
const pixel = chip.display[oy * ChipContext.DISPLAY_WIDTH + ox];
|
||||
const color = if (pixel) on_color else off_color;
|
||||
rl.drawRectangle(ix, iy, pixel_size, pixel_size, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_keyboard_input(chip: *ChipContext) void {
|
||||
const keys = [16]rl.KeyboardKey{
|
||||
.key_x,
|
||||
.key_one,
|
||||
.key_two,
|
||||
.key_three,
|
||||
.key_q,
|
||||
.key_w,
|
||||
.key_e,
|
||||
.key_a,
|
||||
.key_s,
|
||||
.key_d,
|
||||
.key_z,
|
||||
.key_c,
|
||||
.key_four,
|
||||
.key_r,
|
||||
.key_f,
|
||||
.key_v,
|
||||
};
|
||||
for (0.., keys) |i, key| {
|
||||
chip.input[i] = rl.isKeyDown(key);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_sin_wave(samples: []i16, sample_rate: u32, duration: u32, frequency: u32, volume: f32) void {
|
||||
const sample_rate_float: f32 = @floatFromInt(sample_rate);
|
||||
const frequency_float: f32 = @floatFromInt(frequency);
|
||||
|
||||
const sample_size = 16;
|
||||
const max_sample_value: f32 = @floatFromInt((1 << (sample_size-1)) - 1);
|
||||
|
||||
const frame_count = sample_rate * duration;
|
||||
for (1..frame_count) |i| {
|
||||
const i_float: f32 = @floatFromInt(i);
|
||||
const wave: f32 = @sin(std.math.pi*2*frequency_float/sample_rate_float*i_float);
|
||||
samples[i] = @intFromFloat(wave*max_sample_value * volume);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
var chip = ChipContext.init();
|
||||
|
||||
chip.set_memory(0x000, &[_]u8{
|
||||
0xF0, 0x90, 0x90, 0x90, 0xF0, // "0"
|
||||
0x20, 0x60, 0x20, 0x20, 0x70, // "1"
|
||||
0xF0, 0x10, 0xF0, 0x80, 0xF0, // "2"
|
||||
0xF0, 0x10, 0xF0, 0x10, 0xF0, // "3"
|
||||
0x90, 0x90, 0xF0, 0x10, 0x10, // "4"
|
||||
0xF0, 0x80, 0xF0, 0x10, 0xF0, // "5"
|
||||
0xF0, 0x80, 0xF0, 0x90, 0xF0, // "6"
|
||||
0xF0, 0x10, 0x20, 0x40, 0x40, // "7"
|
||||
0xF0, 0x90, 0xF0, 0x90, 0xF0, // "8"
|
||||
0xF0, 0x90, 0xF0, 0x10, 0xF0, // "9"
|
||||
0xF0, 0x90, 0xF0, 0x90, 0x90, // "A"
|
||||
0xE0, 0x90, 0xE0, 0x90, 0xE0, // "B"
|
||||
0xF0, 0x80, 0x80, 0x80, 0xF0, // "C"
|
||||
0xE0, 0x90, 0x90, 0x90, 0xE0, // "D"
|
||||
0xF0, 0x80, 0xF0, 0x80, 0xF0, // "E"
|
||||
0xF0, 0x80, 0xF0, 0x80, 0x80 // "F"
|
||||
});
|
||||
|
||||
{
|
||||
const file = try std.fs.cwd().openFile("morse_demo.ch8", .{ .mode = .read_only });
|
||||
defer file.close();
|
||||
try chip.set_memory_from_file(0x200, file);
|
||||
}
|
||||
|
||||
|
||||
const pixel_size = 20;
|
||||
const screen_width = ChipContext.DISPLAY_WIDTH * pixel_size;
|
||||
const screen_height = ChipContext.DISPLAY_HEIGHT * pixel_size;
|
||||
|
||||
rl.initWindow(screen_width, screen_height, "CHIP-8");
|
||||
defer rl.closeWindow();
|
||||
|
||||
rl.initAudioDevice();
|
||||
defer rl.closeAudioDevice();
|
||||
|
||||
rl.setTargetFPS(60);
|
||||
|
||||
var chip_speed: f32 = 500;
|
||||
var timer_speed: f32 = 60;
|
||||
|
||||
const sample_rate = 44100;
|
||||
var data = [1]i16{0} ** sample_rate;
|
||||
create_sin_wave(&data, sample_rate, 1.0, 440, 0.2);
|
||||
var chip_wave = rl.Wave{
|
||||
.frameCount = sample_rate,
|
||||
.sampleRate = sample_rate,
|
||||
.sampleSize = 16,
|
||||
.channels = 1,
|
||||
.data = @ptrCast(&data),
|
||||
};
|
||||
var chip_sound = rl.loadSoundFromWave(chip_wave);
|
||||
defer rl.unloadSound(chip_sound);
|
||||
|
||||
var chip_clock_time: f32 = 0;
|
||||
var chip_timer_timer: f32 = 0;
|
||||
while (!rl.windowShouldClose()) {
|
||||
var dt = rl.getFrameTime();
|
||||
chip_clock_time += dt;
|
||||
chip_timer_timer += dt;
|
||||
read_keyboard_input(&chip);
|
||||
|
||||
while (chip_clock_time > 1/chip_speed) {
|
||||
chip.step() catch {};
|
||||
chip_clock_time -= 1/chip_speed;
|
||||
}
|
||||
|
||||
while (chip_timer_timer > 1/timer_speed) {
|
||||
chip.update_timer();
|
||||
chip_timer_timer -= 1/timer_speed;
|
||||
}
|
||||
|
||||
if (chip.ST > 0) {
|
||||
if (!rl.isSoundPlaying(chip_sound)) {
|
||||
rl.playSound(chip_sound);
|
||||
}
|
||||
} else {
|
||||
rl.stopSound(chip_sound);
|
||||
}
|
||||
|
||||
{
|
||||
rl.beginDrawing();
|
||||
defer rl.endDrawing();
|
||||
|
||||
rl.clearBackground(rl.Color.white);
|
||||
render_display(&chip, 0, 0, pixel_size, rl.Color.black, rl.Color.ray_white);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user