diff --git a/ROMs/flightrunner.ch8 b/src/ROMs/flightrunner.ch8 similarity index 100% rename from ROMs/flightrunner.ch8 rename to src/ROMs/flightrunner.ch8 diff --git a/ROMs/morse_demo.ch8 b/src/ROMs/morse_demo.ch8 similarity index 100% rename from ROMs/morse_demo.ch8 rename to src/ROMs/morse_demo.ch8 diff --git a/ROMs/octojam1title.ch8 b/src/ROMs/octojam1title.ch8 similarity index 100% rename from ROMs/octojam1title.ch8 rename to src/ROMs/octojam1title.ch8 diff --git a/ROMs/test_opcode.ch8 b/src/ROMs/test_opcode.ch8 similarity index 100% rename from ROMs/test_opcode.ch8 rename to src/ROMs/test_opcode.ch8 diff --git a/src/chip.zig b/src/chip.zig index 7a86035..0b5013b 100644 --- a/src/chip.zig +++ b/src/chip.zig @@ -1,6 +1,27 @@ +const Self = @This(); 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 { return @truncate((inst & 0x0F00) >> 8); @@ -22,379 +43,456 @@ fn get_inst_n(inst: u16) u4 { 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 { - 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; + const display_width = 64; + const display_height = 32; + const memory_size = 4096; - display: [DISPLAY_SIZE]bool, - memory: [MEMORY_SIZE]u8, - stack: [16]u16, - rng: std.rand.DefaultPrng, - input: [16]bool, + var self = Self { + .allocator = allocator, + .display = try allocator.alloc(bool, display_width * display_height), + .display_width = display_width, + .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 - PC: u16, // Program counter - SP: u8, // Stack pointer - DT: u8, // Delay timer - ST: u8, // Sound timer +pub fn deinit(self: *Self) void { + self.allocator.free(self.display); + self.allocator.free(self.memory); +} - pub fn init() ChipContext { - const seed_bits: u128 = @bitCast(std.time.nanoTimestamp()); - const seed: u64 = @truncate(seed_bits); +pub fn init_default_sprites(self: *Self) void { + self.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" + }); +} - 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: *Self, x: u8, y: u8) bool { + const idx: u16 = @as(u16, y) * self.display_width + x; + return self.display[idx]; +} - pub fn init_default_sprites(self: *ChipContext) void { - self.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" - }); - } +pub fn display_set(self: *Self, x: u8, y: u8, value: bool) void { + const idx: u16 = @as(u16, y) * self.display_width + x; + self.display[idx] = value; +} - pub fn display_get(self: *ChipContext, x: u8, y: u8) bool { - return self.display[y * ChipContext.DISPLAY_WIDTH + x]; - } +pub fn draw(self: *Self, 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) % 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 { - 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; - } + 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 { - return self.input[key]; +pub fn is_input_pressed(self: *Self, key: u4) bool { + 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 { - for (0.., memory) |offset, byte| { - self.memory[base_address + offset] = byte; +pub fn set_memory_from_file(self: *Self, 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 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 { - const stat = try file.stat(); + return ""; +} - 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 run_instruction(self: *Self, inst: u16) !void { + if (inst & 0xFFFF == 0x00E0) { // 00E0 - CLS + self.clear_display(); + + } else if (inst & 0xFFFF == 0x00EE) { // 00EE - RET + self.PC = self.stack[self.SP]; + self.SP -= 1; + + } 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 { - 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; + } 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; } - 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 { - for (0..DISPLAY_SIZE) |i| { - self.display[i] = false; - } - } + } 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; - pub fn run_instruction(self: *ChipContext, inst: u16) !void { - print("[{x:0>4}] ({x:0>4}) ", .{ self.PC, inst }); + } 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; - 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 + } else if (inst & 0xF000 == 0x8000) { + if (inst & 0x000F == 0x0) { // 8xy0 - LD Vx, Vy 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 }); + const y = get_inst_y(inst); + self.V[x] = self.V[y]; - } 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 Vy = get_inst_y(inst); - if (self.V[Vx] == self.V[Vy]) { - self.PC += 2; - } - print("SE V{x}, V{x}\n", .{ Vx, Vy }); + self.V[Vx] ^= self.V[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 + } else if (inst & 0x000F == 0x4) { // 8xy4 - ADD Vx, Vy, set VF = carry 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 }); + 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]); - } 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 == 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]); - } 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 == 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); - } 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 + } else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN 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 }); + 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; - } 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 + } else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1 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 }); + self.V[0xF] = self.V[Vx] & 128; + self.V[Vx] = (self.V[Vx] << 1); - } 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 { + return Errors.UnknownInstruction; + } - } 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 & 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; + } - } 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 & 0xF000 == 0xA000) { // Annn - LD I, addr + const addr = get_inst_nnn(inst); + self.I = addr; - } 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); 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); 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 + } else if (inst & 0x00FF == 0x15) { // 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 + } else if (inst & 0x00FF == 0x18) { // 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 + } else if (inst & 0x00FF == 0x1E) { // 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 + } else if (inst & 0x00FF == 0x29) { // 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 + } else if (inst & 0x00FF == 0x33) { // 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 + } else if (inst & 0x00FF == 0x55) { // 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] + } else if (inst & 0x00FF == 0x65) { // 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; + return Errors.UnknownInstruction; } + } else { + return Errors.UnknownInstruction; } -}; +} diff --git a/src/main.zig b/src/main.zig index 0b295ca..e62d524 100755 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,7 @@ const rl = @import("raylib"); const std = @import("std"); const assert = @import("std").debug.assert; -const ChipContext = @import("chip.zig").ChipContext; +const ChipContext = @import("chip.zig"); const RaylibChip = @import("raylib-chip.zig"); 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 { - var chip = ChipContext.init(); - chip.init_default_sprites(); + var buffer: [8192]u8 = undefined; + 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 }); - defer file.close(); - try chip.set_memory_from_file(0x200, file); + // const file = try std.fs.cwd().openFile("ROMs/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; + const screen_width: i32 = @as(i32, chip.display_width) * pixel_size; + const screen_height: i32 = @as(i32, chip.display_height) * pixel_size; rl.initWindow(screen_width, screen_height, "CHIP-8"); defer rl.closeWindow(); diff --git a/src/raylib-chip.zig b/src/raylib-chip.zig index 1867179..02cc77e 100644 --- a/src/raylib-chip.zig +++ b/src/raylib-chip.zig @@ -1,6 +1,7 @@ const Self = @This(); const rl = @import("raylib"); -const ChipContext = @import("chip.zig").ChipContext; +const ChipContext = @import("chip.zig"); +const print = @import("std").debug.print; chip: *ChipContext, on_color: rl.Color, @@ -55,6 +56,10 @@ pub fn update(self: *Self, dt: f32) void { self.tick_time += dt; 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.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 { - const pixel_width = @divFloor(width, ChipContext.DISPLAY_WIDTH); - const pixel_height = @divFloor(height, ChipContext.DISPLAY_HEIGHT); + const pixel_width = @divFloor(width, self.chip.display_width); + const pixel_height = @divFloor(height, self.chip.display_height); rl.drawRectangle(0, 0, width, height, self.off_color); - for (0..ChipContext.DISPLAY_HEIGHT) |oy| { - for (0..ChipContext.DISPLAY_WIDTH) |ox| { - const pixel = self.chip.display[oy * ChipContext.DISPLAY_WIDTH + ox]; - if (pixel) { + for (0..self.chip.display_height) |oy| { + for (0..self.chip.display_width) |ox| { + if (self.chip.display_get(@intCast(ox), @intCast(oy))) { const ix = x + @as(i32, @intCast(ox)) * pixel_width; const iy = y + @as(i32, @intCast(oy)) * pixel_height; rl.drawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);