chore: general update
This commit is contained in:
parent
01fb7833f9
commit
c296dce683
|
@ -37,7 +37,6 @@ pub fn draw_sprite(disp: *Display, pos: *const Point, data: *const []u8) bool {
|
||||||
|
|
||||||
const temp = (byte >> x_bit_offset) & 0x01;
|
const temp = (byte >> x_bit_offset) & 0x01;
|
||||||
const bit = @intCast(u1, temp);
|
const bit = @intCast(u1, temp);
|
||||||
// const bit = @as(u1, temp);
|
|
||||||
|
|
||||||
const i = DISPLAY_WIDTH * y + x;
|
const i = DISPLAY_WIDTH * y + x;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub const Cpu = struct {
|
||||||
sp: u16,
|
sp: u16,
|
||||||
v: [16]u8,
|
v: [16]u8,
|
||||||
stack: [16]u16,
|
stack: [16]u16,
|
||||||
memory: [4096]u8,
|
memory: [0x1000]u8,
|
||||||
disp: Display,
|
disp: Display,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ pub fn decode(cpu: *Cpu, word: u16) ?Instruction {
|
||||||
const nib3: u4 = @intCast(u4, (word & 0x00F0) >> 4);
|
const nib3: u4 = @intCast(u4, (word & 0x00F0) >> 4);
|
||||||
const nib4: u4 = @intCast(u4, (word & 0x000F) >> 0);
|
const nib4: u4 = @intCast(u4, (word & 0x000F) >> 0);
|
||||||
|
|
||||||
|
|
||||||
if (nib1 == 0x0) {
|
if (nib1 == 0x0) {
|
||||||
// nib2 is 0x0, nib3 is 0xE
|
// nib2 is 0x0, nib3 is 0xE
|
||||||
|
|
||||||
|
@ -84,20 +83,20 @@ pub fn decode(cpu: *Cpu, word: u16) ?Instruction {
|
||||||
|
|
||||||
pub fn execute(cpu: *Cpu, instr: Instruction) void {
|
pub fn execute(cpu: *Cpu, instr: Instruction) void {
|
||||||
switch (instr) {
|
switch (instr) {
|
||||||
.CLS => std.debug.print("CLS\n", .{}),
|
.CLS => std.log.debug("CLS", .{}),
|
||||||
.RET => {
|
.RET => {
|
||||||
std.debug.print("RET\n", .{});
|
std.log.debug("RET", .{});
|
||||||
|
|
||||||
cpu.pc = cpu.stack[cpu.sp];
|
cpu.pc = cpu.stack[cpu.sp];
|
||||||
cpu.sp -= 1;
|
cpu.sp -= 1;
|
||||||
},
|
},
|
||||||
.JP => |addr| {
|
.JP => |addr| {
|
||||||
std.debug.print("JP 0x{X:}\n", .{ addr });
|
std.log.debug("JP 0x{X:}", .{ addr });
|
||||||
|
|
||||||
cpu.pc = addr;
|
cpu.pc = addr;
|
||||||
},
|
},
|
||||||
.CALL => |addr| {
|
.CALL => |addr| {
|
||||||
std.debug.print("CALL 0x{X:}\n", .{ addr });
|
std.log.debug("CALL 0x{X:}", .{ addr });
|
||||||
|
|
||||||
cpu.sp += 1;
|
cpu.sp += 1;
|
||||||
cpu.stack[cpu.sp] = cpu.pc;
|
cpu.stack[cpu.sp] = cpu.pc;
|
||||||
|
@ -106,7 +105,7 @@ pub fn execute(cpu: *Cpu, instr: Instruction) void {
|
||||||
.SE_3 => |args| {
|
.SE_3 => |args| {
|
||||||
const x = args.x;
|
const x = args.x;
|
||||||
const kk = args.kk;
|
const kk = args.kk;
|
||||||
std.debug.print("SE V{}, 0x{X:}\n", .{ x, kk });
|
std.log.debug("SE V{}, 0x{X:}", .{ x, kk });
|
||||||
|
|
||||||
if (cpu.v[x] != kk) {
|
if (cpu.v[x] != kk) {
|
||||||
cpu.pc += 2;
|
cpu.pc += 2;
|
||||||
|
@ -115,19 +114,19 @@ pub fn execute(cpu: *Cpu, instr: Instruction) void {
|
||||||
.LD_6 => |args| {
|
.LD_6 => |args| {
|
||||||
const x = args.x;
|
const x = args.x;
|
||||||
const kk = args.kk;
|
const kk = args.kk;
|
||||||
std.debug.print("LD V{} 0x{X:}\n", .{ x, kk });
|
std.log.debug("LD V{} 0x{X:}", .{ x, kk });
|
||||||
|
|
||||||
cpu.v[x] = kk;
|
cpu.v[x] = kk;
|
||||||
},
|
},
|
||||||
.LD_I => |addr| {
|
.LD_I => |addr| {
|
||||||
std.debug.print("LD I, 0x{X:}\n", .{ addr });
|
std.log.debug("LD I, 0x{X:}", .{ addr });
|
||||||
cpu.i = addr;
|
cpu.i = addr;
|
||||||
},
|
},
|
||||||
.DRW => |args| {
|
.DRW => |args| {
|
||||||
const x = args.x;
|
const x = args.x;
|
||||||
const y = args.y;
|
const y = args.y;
|
||||||
const n = args.n;
|
const n = args.n;
|
||||||
std.debug.print("DRW V{}, V{}, 0x{X:}\n", .{ x, y, n });
|
std.log.debug("DRW V{}, V{}, 0x{X:}", .{ x, y, n });
|
||||||
|
|
||||||
const draw_pos: Point = .{ .x = cpu.v[x], .y = cpu.v[y] };
|
const draw_pos: Point = .{ .x = cpu.v[x], .y = cpu.v[y] };
|
||||||
const sprite_data = cpu.memory[cpu.i..(cpu.i + n)];
|
const sprite_data = cpu.memory[cpu.i..(cpu.i + n)];
|
||||||
|
@ -141,7 +140,7 @@ pub fn execute(cpu: *Cpu, instr: Instruction) void {
|
||||||
.ADD_7 => |args| {
|
.ADD_7 => |args| {
|
||||||
const x = args.x;
|
const x = args.x;
|
||||||
const kk = args.kk;
|
const kk = args.kk;
|
||||||
std.debug.print("ADD V{}, 0x{X:}\n", .{ x, kk });
|
std.log.debug("ADD V{}, 0x{X:}", .{ x, kk });
|
||||||
|
|
||||||
cpu.v[x] = cpu.v[x] + kk;
|
cpu.v[x] = cpu.v[x] + kk;
|
||||||
}
|
}
|
||||||
|
@ -158,18 +157,19 @@ fn load_rom(cpu: *Cpu, buf: []u8) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_rom_path(cpu: *Cpu, path: []const u8) !void {
|
pub fn load_rom_path(cpu: *Cpu, path: []const u8) !void {
|
||||||
var alloc = std.heap.page_allocator;
|
var heap = std.heap.page_allocator;
|
||||||
|
|
||||||
const file = try std.fs.cwd().openFile(path, .{ .read = true });
|
const file = try std.fs.cwd().openFile(path, .{ .read = true });
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const file_size = try file.getEndPos();
|
const file_size = try file.getEndPos();
|
||||||
const rom_buf = try file.readToEndAlloc(alloc, file_size);
|
const rom_buf = try file.readToEndAlloc(heap, file_size);
|
||||||
|
defer heap.free(rom_buf);
|
||||||
|
|
||||||
load_rom(cpu, rom_buf);
|
load_rom(cpu, rom_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(cpu: *Cpu) void {
|
pub fn step(cpu: *Cpu) void {
|
||||||
const opcode = fetch(cpu);
|
const opcode = fetch(cpu);
|
||||||
const maybe_instr = decode(cpu, opcode);
|
const maybe_instr = decode(cpu, opcode);
|
||||||
|
|
|
@ -1,107 +1,77 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const cpu = @import("cpu.zig");
|
const emu = @import("emu.zig");
|
||||||
const Display = @import("display.zig").Display;
|
const Display = @import("display.zig").Display;
|
||||||
const c = @cImport({
|
const c = @cImport(@cInclude("SDL.h"));
|
||||||
@cInclude("SDL2/SDL.h");
|
|
||||||
});
|
|
||||||
const assert = @import("std").debug.assert;
|
|
||||||
|
|
||||||
// See https://github.com/zig-lang/zig/issues/565
|
const Cpu = emu.Cpu;
|
||||||
// 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;
|
const WIDTH = 64;
|
||||||
|
const HEIGHT = 32;
|
||||||
|
const WINDOW_WIDTH = WIDTH * 10;
|
||||||
|
const WINDOW_HEIGHT = HEIGHT * 10;
|
||||||
|
const PIXELBUF_LEN = WIDTH * HEIGHT * @sizeOf(u32);
|
||||||
|
|
||||||
// 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 {
|
pub fn main() !void {
|
||||||
// Initialize SDL2
|
_ = c.SDL_Init(c.SDL_INIT_VIDEO);
|
||||||
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();
|
defer c.SDL_Quit();
|
||||||
|
|
||||||
// Create Screen (Maybe introduce scaling somehow?)
|
const window = c.SDL_CreateWindow("CHIP-8", c.SDL_WINDOWPOS_CENTERED, c.SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, c.SDL_WINDOW_OPENGL);
|
||||||
const screen = c.SDL_CreateWindow("Zig8", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 320, c.SDL_WINDOW_OPENGL) orelse
|
defer c.SDL_DestroyWindow(window);
|
||||||
{
|
|
||||||
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(window, -1, c.SDL_RENDERER_ACCELERATED);
|
||||||
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);
|
defer c.SDL_DestroyRenderer(renderer);
|
||||||
|
|
||||||
// Create RGBA Texture
|
const texture = c.SDL_CreateTexture(renderer, c.SDL_PIXELFORMAT_RGBA8888, c.SDL_TEXTUREACCESS_STATIC, WIDTH, HEIGHT);
|
||||||
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);
|
defer c.SDL_DestroyTexture(texture);
|
||||||
|
|
||||||
// Initialize CHIP-8 Emulator
|
var zig8 = emu.Cpu {
|
||||||
var chip8 = cpu.Cpu {
|
|
||||||
.i = 0x00,
|
.i = 0x00,
|
||||||
.pc = 0x0200,
|
.pc = 0x200,
|
||||||
.sp = 0x00,
|
.sp = 0x00,
|
||||||
.v = [_]u8{0x0} ** 16,
|
.v = [_]u8{0x00} ** 16,
|
||||||
.stack = [_]u16{0x0} ** 16,
|
.stack = [_]u16{0x00} ** 16,
|
||||||
.memory = [_]u8{0x0} ** 4096,
|
.memory = [_]u8{0x0000} ** 0x1000,
|
||||||
.disp = Display.new(),
|
.disp = Display.new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var path = "./bin/IBM Logo.ch8";
|
try emu.load_rom_path(&zig8, "./bin/IBM Logo.ch8");
|
||||||
|
var pixel_buf: [PIXELBUF_LEN]u8 = [_]u8 {0x00} ** PIXELBUF_LEN;
|
||||||
|
|
||||||
// Load Game ROM into CHIP-8 Emulator
|
emuloop: while(true) {
|
||||||
try cpu.load_rom_path(&chip8, path);
|
_ = c.SDL_UpdateTexture(texture, null, @ptrCast(*const c_void, &pixel_buf), WIDTH * @sizeOf(u32));
|
||||||
|
_ = c.SDL_RenderCopy(renderer, texture, null, null);
|
||||||
|
_ = c.SDL_RenderPresent(renderer);
|
||||||
|
|
||||||
|
|
||||||
// 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;
|
var event: c.SDL_Event = undefined;
|
||||||
while (SDL_PollEvent(&event) != 0) {
|
while(c.SDL_PollEvent(&event) != 0) {
|
||||||
switch (event.@"type") {
|
switch(event.type) {
|
||||||
c.SDL_QUIT => {
|
c.SDL_QUIT => break :emuloop,
|
||||||
quit = true;
|
else => {}
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = c.SDL_RenderClear(renderer);
|
emu.step(&zig8);
|
||||||
_ = c.SDL_RenderCopy(renderer, texture, null, null);
|
draw(&zig8.disp, &pixel_buf);
|
||||||
c.SDL_RenderPresent(renderer);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cpu.cycle(&chip8);
|
fn draw(display: *const Display, pixel_buf: *[8192]u8) void {
|
||||||
|
var emu_i: usize = 0;
|
||||||
|
|
||||||
c.SDL_Delay(17);
|
while (emu_i < display.buf.len) : (emu_i += 1) {
|
||||||
|
var pixel_i = emu_i * 4;
|
||||||
|
|
||||||
|
if (display.buf[emu_i] == 0x01) {
|
||||||
|
pixel_buf[pixel_i] = 0xFF;
|
||||||
|
pixel_buf[pixel_i + 1] = 0xFF;
|
||||||
|
pixel_buf[pixel_i + 2] = 0xFF;
|
||||||
|
pixel_buf[pixel_i + 3] = 0xFF;
|
||||||
|
} else {
|
||||||
|
pixel_buf[pixel_i] = 0xFF;
|
||||||
|
pixel_buf[pixel_i + 1] = 0x00;
|
||||||
|
pixel_buf[pixel_i + 2] = 0x00;
|
||||||
|
pixel_buf[pixel_i + 3] = 0x00;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue