1
0

embed example rom into program

This commit is contained in:
Rokas Puzonas 2023-09-02 16:28:42 +03:00
parent 90ae28d4d0
commit cf8fec2353
7 changed files with 417 additions and 309 deletions

View File

@ -1,6 +1,27 @@
const Self = @This();
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const print = std.debug.print; const Errors = error { UnknownInstruction };
allocator: *const Allocator,
display: []bool,
display_width: u8,
display_height: u8,
memory: []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
fn get_inst_x(inst: u16) u4 { fn get_inst_x(inst: u16) u4 {
return @truncate((inst & 0x0F00) >> 8); return @truncate((inst & 0x0F00) >> 8);
@ -22,379 +43,456 @@ fn get_inst_n(inst: u16) u4 {
return @truncate(inst & 0x000F); return @truncate(inst & 0x000F);
} }
const ChipErrors = error { UnknownInstruction }; pub fn init(allocator: *const Allocator) !Self {
const seed_bits: u128 = @bitCast(std.time.nanoTimestamp());
const seed: u64 = @truncate(seed_bits);
pub const ChipContext = struct { const display_width = 64;
pub const MEMORY_SIZE: u16 = 4096; const display_height = 32;
pub const DISPLAY_WIDTH: u16 = 64; const memory_size = 4096;
pub const DISPLAY_HEIGHT: u16 = 32;
pub const DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT;
display: [DISPLAY_SIZE]bool, var self = Self {
memory: [MEMORY_SIZE]u8, .allocator = allocator,
stack: [16]u16, .display = try allocator.alloc(bool, display_width * display_height),
rng: std.rand.DefaultPrng, .display_width = display_width,
input: [16]bool, .display_height = display_height,
.memory = try allocator.alloc(u8, 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
};
V: [16]u8, self.init_default_sprites();
return self;
}
I: u16, // Address pointer pub fn deinit(self: *Self) void {
PC: u16, // Program counter self.allocator.free(self.display);
SP: u8, // Stack pointer self.allocator.free(self.memory);
DT: u8, // Delay timer }
ST: u8, // Sound timer
pub fn init() ChipContext { pub fn init_default_sprites(self: *Self) void {
const seed_bits: u128 = @bitCast(std.time.nanoTimestamp()); self.set_memory(0x000, &[_]u8{
const seed: u64 = @truncate(seed_bits); 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"
});
}
return ChipContext{ pub fn display_get(self: *Self, x: u8, y: u8) bool {
.display = [1]bool{false} ** DISPLAY_SIZE, const idx: u16 = @as(u16, y) * self.display_width + x;
.memory = [1]u8{0} ** MEMORY_SIZE, return self.display[idx];
.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 init_default_sprites(self: *ChipContext) void { pub fn display_set(self: *Self, x: u8, y: u8, value: bool) void {
self.set_memory(0x000, &[_]u8{ const idx: u16 = @as(u16, y) * self.display_width + x;
0xF0, 0x90, 0x90, 0x90, 0xF0, // "0" self.display[idx] = value;
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"
});
}
pub fn display_get(self: *ChipContext, x: u8, y: u8) bool { pub fn draw(self: *Self, x: u8, y: u8, n: u4) bool {
return self.display[y * ChipContext.DISPLAY_WIDTH + x]; 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) % self.display_width);
const disp_y: u8 = @intCast((y+i) % self.display_height);
const sprite_pixel = ((sprite_row >> @intCast(7-ix)) & 1) == 1;
pub fn display_set(self: *ChipContext, x: u8, y: u8, value: bool) void { const display_pixel = self.display_get(disp_x, disp_y);
self.display[y * ChipContext.DISPLAY_WIDTH + x] = value; const new_pixel = (display_pixel != sprite_pixel);
} self.display_set(disp_x, disp_y, new_pixel);
if (display_pixel and display_pixel != new_pixel) {
pub fn draw(self: *ChipContext, x: u8, y: u8, n: u4) bool { result = true;
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;
} }
return result;
}
pub fn is_input_pressed(self: *ChipContext, key: u4) bool { pub fn is_input_pressed(self: *Self, key: u4) bool {
return self.input[key]; return self.input[key];
}
pub fn set_memory(self: *Self, base_address: u16, memory: []const u8) void {
for (0.., memory) |offset, byte| {
self.memory[base_address + offset] = byte;
} }
}
pub fn set_memory(self: *ChipContext, base_address: u16, memory: []const u8) void { pub fn set_memory_from_file(self: *Self, base_address: u16, file: std.fs.File) !void {
for (0.., memory) |offset, byte| { const stat = try file.stat();
self.memory[base_address + offset] = byte;
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 current_instruction(self: *Self) u16 {
const high_byte: u16 = self.memory[self.PC];
const low_byte: u16 = self.memory[self.PC+1];
return (high_byte << 8) | low_byte;
}
pub fn step(self: *Self) !void {
const instruction: u16 = self.current_instruction();
self.PC += 2;
try self.run_instruction(instruction);
}
pub fn update_timer(self: *Self) void {
if (self.DT > 0) {
self.DT -= 1;
}
if (self.ST > 0) {
self.ST -= 1;
}
}
pub fn clear_display(self: *Self) void {
const display_size = @as(u16, self.display_width) * self.display_height;
for (0..display_size) |i| {
self.display[i] = false;
}
}
pub fn decode_instruction_fmt(inst: u16) [*:0]const u8 {
if (inst & 0xFFFF == 0x00E0) { // 00E0 - CLS
return "CLS";
} else if (inst & 0xFFFF == 0x00EE) { // 00EE - RET
return "RET";
} else if (inst & 0xF000 == 0x0000) { // 0nnn - SYS addr
return "SYS {nnn}";
} else if (inst & 0xF000 == 0x1000) { // 1nnn - JP addr
return "JP {nnn}";
} else if (inst & 0xF000 == 0x2000) { // 2nnn - CALL addr
return "CALL {nnn}";
} else if (inst & 0xF000 == 0x3000) { // 3xkk - SE Vx, byte
return "SE V{x}, {kk}";
} else if (inst & 0xF000 == 0x4000) { // 4xkk - SNE Vx, byte
return "SNE V{x}, {kk}";
} else if (inst & 0xF00F == 0x5000) { // 5xy0 - SE Vx, Vy
return "SE V{x}, V{y}";
} else if (inst & 0xF000 == 0x6000) { // 6xkk - LD Vx, byte
return "LD V{x}, {kk}";
} else if (inst & 0xF000 == 0x7000) { // 7xkk - ADD Vx, byte
return "ADD V{x}, {kk}";
} else if (inst & 0xF000 == 0x8000) {
if (inst & 0x000F == 0x0) { // 8xy0 - LD Vx, Vy
return "LD V{x}, V{y}";
} else if (inst & 0x000F == 0x1) { // 8xy1 - OR Vx, Vy
return "OR V{x}, V{y}";
} else if (inst & 0x000F == 0x2) { // 8xy2 - AND Vx, Vy
return "AND V{x}, V{y}";
} else if (inst & 0x000F == 0x3) { // 8xy3 - XOR Vx, Vy
return "XOR V{x}, V{y}";
} else if (inst & 0x000F == 0x4) { // 8xy4 - ADD Vx, Vy, set VF = carry
return "ADD V{x}, V{y}";
} else if (inst & 0x000F == 0x5) { // 8xy5 - SUB Vx, Vy, set VF = NOT borrow
return "SUB V{x}, V{y}";
} else if (inst & 0x000F == 0x6) { // 8xy6 - Vx SHR 1
return "SHR V{x}";
} else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN Vx, Vy
return "SUBN V{x}, V{y}";
} else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1
return "SHL V{x}";
}
} else if (inst & 0xF00F == 0x9000) { // 9xy0 - SNE Vx, Vy
return "SNE V{x}, V{y}";
} else if (inst & 0xF000 == 0xA000) { // Annn - LD I, addr
return "LD I, {nnn}";
} else if (inst & 0xF000 == 0xB000) { // Bnnn - JP V0, addr
return "JP V0, {nnn}";
} else if (inst & 0xF000 == 0xC000) { // Cxkk - RNG Vx, byte
return "RNG V{x}, {kk}";
} else if (inst & 0xF000 == 0xD000) { // Dxyn - DRW Vx, Vy, nibble
return "DRW V{x}, V{x}, {n}\n";
} else if (inst & 0xF0FF == 0xE09E) { // Ex9E - SKP Vx
return "SKP V{x}";
} else if (inst & 0xF0FF == 0xE0A1) { // ExA1 - SKNP Vx
return "SKNP V{x}";
} else if (inst & 0xF000 == 0xF000) {
if (inst & 0x00FF == 0x07) { // Fx07 - LD Vx, DT
return "LD V{x}, DT";
} else if (inst & 0x00FF == 0x0A) { // Fx0A - LD Vx, K
return "LD V{x}, K";
} else if (inst & 0x00FF == 0x15) { // Fx15 - LD DT, Vx
return "LD DT, V{x}";
} else if (inst & 0x00FF == 0x18) { // Fx1E - LD ST, Vx
return "LD ST, V{x}";
} else if (inst & 0x00FF == 0x1E) { // Fx1E - ADD I, Vx
return "ADD I, V{x}";
} else if (inst & 0x00FF == 0x29) { // Fx29 - LD F, Vx
return "LD F, V{x}";
} else if (inst & 0x00FF == 0x33) { // Fx33 - LD B, Vx
return "LD B, V{x}";
} else if (inst & 0x00FF == 0x55) { // Fx55 - LD [I], Vx
return "LD [I], V{x}";
} else if (inst & 0x00FF == 0x65) { // Fx65 - LD Vx, [I]
return "LD V{x}, [I]";
} }
} }
pub fn set_memory_from_file(self: *ChipContext, base_address: u16, file: std.fs.File) !void { return "";
const stat = try file.stat(); }
var buffer: [512]u8 = undefined; pub fn run_instruction(self: *Self, inst: u16) !void {
var offset: usize = 0; if (inst & 0xFFFF == 0x00E0) { // 00E0 - CLS
var bytes_left: i64 = @intCast(stat.size); self.clear_display();
while (bytes_left > 0) : (bytes_left -= buffer.len) {
const bytes_read = try file.read(&buffer); } else if (inst & 0xFFFF == 0x00EE) { // 00EE - RET
for (0..bytes_read) |byte_index| { self.PC = self.stack[self.SP];
self.memory[base_address + offset + byte_index] = buffer[byte_index]; self.SP -= 1;
}
offset += buffer.len; } else if (inst & 0xF000 == 0x0000) { // 0nnn - SYS addr
// Ignore
} else if (inst & 0xF000 == 0x1000) { // 1nnn - JP addr
const addr = inst & 0x0FFF;
self.PC = 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;
} 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;
} }
}
pub fn step(self: *ChipContext) !void { } else if (inst & 0xF000 == 0x4000) { // 4xkk - SNE Vx, byte
const high_byte: u16 = self.memory[self.PC]; const x = get_inst_x(inst);
const low_byte: u16 = self.memory[self.PC+1]; const byte = get_inst_kk(inst);
const instruction: u16 = (high_byte << 8) | low_byte; if (self.V[x] != byte) {
self.PC += 2; 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; } 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;
} }
}
pub fn clear_display(self: *ChipContext) void { } else if (inst & 0xF000 == 0x6000) { // 6xkk - LD Vx, byte
for (0..DISPLAY_SIZE) |i| { const Vx = get_inst_x(inst);
self.display[i] = false; const byte = get_inst_kk(inst);
} self.V[Vx] = byte;
}
pub fn run_instruction(self: *ChipContext, inst: u16) !void { } else if (inst & 0xF000 == 0x7000) { // 7xkk - ADD Vx, byte
print("[{x:0>4}] ({x:0>4}) ", .{ self.PC, inst }); const x = get_inst_x(inst);
const byte = get_inst_kk(inst);
self.V[x] +%= byte;
if (inst & 0xFFFF == 0x00E0) { // 00E0 - CLS } else if (inst & 0xF000 == 0x8000) {
print("CLS\n", .{}); if (inst & 0x000F == 0x0) { // 8xy0 - LD Vx, Vy
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 x = get_inst_x(inst);
const byte = get_inst_kk(inst); const y = get_inst_y(inst);
if (self.V[x] != byte) { self.V[x] = self.V[y];
self.PC += 2;
}
print("SNE V{x}, 0x{x}\n", .{ x, byte });
} else if (inst & 0xF00F == 0x5000) { // 5xy0 - SE Vx, Vy } 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];
} 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];
} else if (inst & 0x000F == 0x3) { // 8xy3 - XOR Vx, Vy
const Vx = get_inst_x(inst); const Vx = get_inst_x(inst);
const Vy = get_inst_y(inst); const Vy = get_inst_y(inst);
if (self.V[Vx] == self.V[Vy]) { 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 } else if (inst & 0x000F == 0x4) { // 8xy4 - ADD Vx, Vy, set VF = carry
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 x = get_inst_x(inst);
const byte = get_inst_kk(inst); const y = get_inst_y(inst);
self.V[x] +%= byte; const result = @addWithOverflow(self.V[x], self.V[y]);
print("ADD V{x}, 0x{x}\n", .{ x, byte }); self.V[x] = result[0];
self.V[0xF] = (1 - result[1]);
} else if (inst & 0xF000 == 0x8000) { } else if (inst & 0x000F == 0x5) { // 8xy5 - SUB Vx, Vy, set VF = NOT borrow
if (inst & 0x000F == 0x0) { // 8xy0 - LD Vx, Vy const x = get_inst_x(inst);
const x = get_inst_x(inst); const y = get_inst_y(inst);
const y = get_inst_y(inst); const result = @subWithOverflow(self.V[x], self.V[y]);
self.V[x] = self.V[y]; self.V[x] = result[0];
print("LD V{x}, V{x}\n", .{ x, y }); self.V[0xF] = (1 - result[1]);
} else if (inst & 0x000F == 0x1) { // 8xy1 - OR Vx, Vy } else if (inst & 0x000F == 0x6) { // 8xy6 - Vx SHR 1
const x = get_inst_x(inst); const x = get_inst_x(inst);
const y = get_inst_y(inst); self.V[0xF] = self.V[x] & 1;
self.V[x] |= self.V[y]; self.V[x] = (self.V[x] >> 1);
print("OR V{x}, V{x}\n", .{ x, y });
} else if (inst & 0x000F == 0x2) { // 8xy2 - AND Vx, Vy } else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN 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 Vx = get_inst_x(inst);
const Vy = get_inst_y(inst); const Vy = get_inst_y(inst);
if (self.V[Vx] != self.V[Vy]) { const sub: i16 = self.V[Vy] - self.V[Vx];
self.PC += 2; const not_borrow = @intFromBool(sub > 0);
} self.V[Vx] = @intCast(sub & 0x00FF);
print("SNE V{x}, V{x}\n", .{ Vx, Vy }); self.V[0xF] = not_borrow;
} else if (inst & 0xF000 == 0xA000) { // Annn - LD I, addr } else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1
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 Vx = get_inst_x(inst);
const mask = get_inst_kk(inst); self.V[0xF] = self.V[Vx] & 128;
self.V[Vx] = self.rng.random().int(u8) & mask; self.V[Vx] = (self.V[Vx] << 1);
print("RNG V{x}, 0x{x}\n", .{ Vx, mask });
} else if (inst & 0xF000 == 0xD000) { // Dxyn - DRW Vx, Vy, nibble } else {
const Vx = get_inst_x(inst); return Errors.UnknownInstruction;
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 } else if (inst & 0xF00F == 0x9000) { // 9xy0 - SNE Vx, Vy
const x = get_inst_x(inst); const Vx = get_inst_x(inst);
if (self.is_input_pressed(@truncate(self.V[x]))) { const Vy = get_inst_y(inst);
self.PC += 2; if (self.V[Vx] != self.V[Vy]) {
} self.PC += 2;
print("SKP V{x}\n", .{ x }); }
} else if (inst & 0xF0FF == 0xE0A1) { // ExA1 - SKNP Vx } else if (inst & 0xF000 == 0xA000) { // Annn - LD I, addr
const x = get_inst_x(inst); const addr = get_inst_nnn(inst);
if (!self.is_input_pressed(@truncate(self.V[x]))) { self.I = addr;
self.PC += 2;
}
print("SKNP V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF007) { // Fx07 - LD Vx, DT } else if (inst & 0xF000 == 0xB000) { // Bnnn - JP V0, addr
const addr = get_inst_nnn(inst);
self.PC = self.V[0] + 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;
} 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));
} 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;
}
} 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;
}
} else if (inst & 0xF000 == 0xF000) {
if (inst & 0x00FF == 0x07) { // Fx07 - LD Vx, DT
const x = get_inst_x(inst); const x = get_inst_x(inst);
self.V[x] = self.DT; self.V[x] = self.DT;
print("LD V{x}, DT\n", .{ x });
} else if (inst & 0xF0FF == 0xF00A) { // Fx0A - LD Vx, K } else if (inst & 0x00FF == 0x0A) { // Fx0A - LD Vx, K
const x = get_inst_x(inst); const x = get_inst_x(inst);
if (!self.is_input_pressed(@truncate(self.V[x]))) { if (!self.is_input_pressed(@truncate(self.V[x]))) {
self.PC -= 2; self.PC -= 2;
} }
print("LD V{x}, K\n", .{ x });
} else if (inst & 0xF0FF == 0xF015) { // Fx15 - LD DT, Vx } else if (inst & 0x00FF == 0x15) { // Fx15 - LD DT, Vx
const x = get_inst_x(inst); const x = get_inst_x(inst);
self.DT = self.V[x]; self.DT = self.V[x];
print("LD DT, V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF018) { // Fx1E - LD ST, Vx } else if (inst & 0x00FF == 0x18) { // Fx1E - LD ST, Vx
const x = get_inst_x(inst); const x = get_inst_x(inst);
self.ST = self.V[x]; self.ST = self.V[x];
print("LD ST, V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF01E) { // Fx1E - ADD I, Vx } else if (inst & 0x00FF == 0x1E) { // Fx1E - ADD I, Vx
const x = get_inst_x(inst); const x = get_inst_x(inst);
self.I += self.V[x]; self.I += self.V[x];
print("ADD I, V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF029) { // Fx29 - LD F, Vx } else if (inst & 0x00FF == 0x29) { // Fx29 - LD F, Vx
const x = get_inst_x(inst); const x = get_inst_x(inst);
self.I = self.V[x] * 5; self.I = self.V[x] * 5;
print("LD F, V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF033) { // Fx33 - LD B, Vx } else if (inst & 0x00FF == 0x33) { // Fx33 - LD B, Vx
const x = get_inst_x(inst); const x = get_inst_x(inst);
const Vx = self.V[x]; const Vx = self.V[x];
self.memory[self.I+0] = @divFloor(Vx, 100); self.memory[self.I+0] = @divFloor(Vx, 100);
self.memory[self.I+1] = @divFloor(Vx, 10) % 10; self.memory[self.I+1] = @divFloor(Vx, 10) % 10;
self.memory[self.I+2] = Vx % 10; self.memory[self.I+2] = Vx % 10;
print("LD B, V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF055) { // Fx55 - LD [I], Vx } else if (inst & 0x00FF == 0x55) { // Fx55 - LD [I], Vx
const x = get_inst_x(inst); const x = get_inst_x(inst);
var i: u4 = 0; var i: u4 = 0;
while (i <= x) : (i += 1) { while (i <= x) : (i += 1) {
self.memory[self.I + i] = self.V[i]; self.memory[self.I + i] = self.V[i];
} }
print("LD [I], V{x}\n", .{ x });
} else if (inst & 0xF0FF == 0xF065) { // Fx65 - LD Vx, [I] } else if (inst & 0x00FF == 0x65) { // Fx65 - LD Vx, [I]
const x = get_inst_x(inst); const x = get_inst_x(inst);
var i: u4 = 0; var i: u4 = 0;
while (i <= x) : (i += 1) { while (i <= x) : (i += 1) {
self.V[i] = self.memory[self.I + i]; self.V[i] = self.memory[self.I + i];
} }
print("LD V{x}, [I]\n", .{ x });
} else { } else {
print("UNKNOWN\n", .{ }); return Errors.UnknownInstruction;
return ChipErrors.UnknownInstruction;
} }
} else {
return Errors.UnknownInstruction;
} }
}; }

View File

@ -1,7 +1,7 @@
const rl = @import("raylib"); const rl = @import("raylib");
const std = @import("std"); const std = @import("std");
const assert = @import("std").debug.assert; const assert = @import("std").debug.assert;
const ChipContext = @import("chip.zig").ChipContext; const ChipContext = @import("chip.zig");
const RaylibChip = @import("raylib-chip.zig"); const RaylibChip = @import("raylib-chip.zig");
pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void { pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
@ -20,18 +20,24 @@ pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
} }
pub fn main() anyerror!void { pub fn main() anyerror!void {
var chip = ChipContext.init(); var buffer: [8192]u8 = undefined;
chip.init_default_sprites(); var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
var chip = try ChipContext.init(&allocator);
defer chip.deinit();
chip.set_memory(0x200, @embedFile("ROMs/morse_demo.ch8"));
{ {
const file = try std.fs.cwd().openFile("ROMs/morse_demo.ch8", .{ .mode = .read_only }); // const file = try std.fs.cwd().openFile("ROMs/morse_demo.ch8", .{ .mode = .read_only });
defer file.close(); // defer file.close();
try chip.set_memory_from_file(0x200, file); // try chip.set_memory_from_file(0x200, file);
} }
const pixel_size = 20; const pixel_size = 20;
const screen_width = ChipContext.DISPLAY_WIDTH * pixel_size; const screen_width: i32 = @as(i32, chip.display_width) * pixel_size;
const screen_height = ChipContext.DISPLAY_HEIGHT * pixel_size; const screen_height: i32 = @as(i32, chip.display_height) * pixel_size;
rl.initWindow(screen_width, screen_height, "CHIP-8"); rl.initWindow(screen_width, screen_height, "CHIP-8");
defer rl.closeWindow(); defer rl.closeWindow();

View File

@ -1,6 +1,7 @@
const Self = @This(); const Self = @This();
const rl = @import("raylib"); const rl = @import("raylib");
const ChipContext = @import("chip.zig").ChipContext; const ChipContext = @import("chip.zig");
const print = @import("std").debug.print;
chip: *ChipContext, chip: *ChipContext,
on_color: rl.Color, on_color: rl.Color,
@ -55,6 +56,10 @@ pub fn update(self: *Self, dt: f32) void {
self.tick_time += dt; self.tick_time += dt;
while (self.tick_time > 1/self.tick_speed) { while (self.tick_time > 1/self.tick_speed) {
// const inst = self.chip.current_instruction();
// const inst_fmt = ChipContext.decode_instruction_fmt(inst);
// print("[{x:0>4}] ({x:0>4}) {s}\n", .{ self.chip.PC, inst, inst_fmt });
self.chip.step() catch {}; self.chip.step() catch {};
self.tick_time -= 1/self.tick_speed; self.tick_time -= 1/self.tick_speed;
} }
@ -78,15 +83,14 @@ pub fn update(self: *Self, dt: f32) void {
} }
pub fn render(self: *Self, x: i32, y: i32, width: i32, height: i32) void { pub fn render(self: *Self, x: i32, y: i32, width: i32, height: i32) void {
const pixel_width = @divFloor(width, ChipContext.DISPLAY_WIDTH); const pixel_width = @divFloor(width, self.chip.display_width);
const pixel_height = @divFloor(height, ChipContext.DISPLAY_HEIGHT); const pixel_height = @divFloor(height, self.chip.display_height);
rl.drawRectangle(0, 0, width, height, self.off_color); rl.drawRectangle(0, 0, width, height, self.off_color);
for (0..ChipContext.DISPLAY_HEIGHT) |oy| { for (0..self.chip.display_height) |oy| {
for (0..ChipContext.DISPLAY_WIDTH) |ox| { for (0..self.chip.display_width) |ox| {
const pixel = self.chip.display[oy * ChipContext.DISPLAY_WIDTH + ox]; if (self.chip.display_get(@intCast(ox), @intCast(oy))) {
if (pixel) {
const ix = x + @as(i32, @intCast(ox)) * pixel_width; const ix = x + @as(i32, @intCast(ox)) * pixel_width;
const iy = y + @as(i32, @intCast(oy)) * pixel_height; const iy = y + @as(i32, @intCast(oy)) * pixel_height;
rl.drawRectangle(ix, iy, pixel_width, pixel_height, self.on_color); rl.drawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);