feat: ITS ALIVE
This commit is contained in:
parent
40223a179d
commit
c70756069b
12 changed files with 395 additions and 38 deletions
50
src/main.zig
50
src/main.zig
|
|
@ -1,7 +1,51 @@
|
||||||
const std = @import("std");
|
|
||||||
const octo = @import("octochip");
|
const octo = @import("octochip");
|
||||||
|
const rl = @import("raylib");
|
||||||
|
|
||||||
|
const PIXEL_SCALE: i32 = 20;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const ins = try octo.machine.Instruction.from_bytes(0xabcd);
|
var machine = octo.machine.Machine.new();
|
||||||
std.log.info("instruction: {f}", .{ins});
|
try machine.loadRom(@embedFile("./rom.ch8"));
|
||||||
|
|
||||||
|
rl.initWindow(64 * PIXEL_SCALE, 32 * PIXEL_SCALE, "octochip");
|
||||||
|
defer rl.closeWindow();
|
||||||
|
|
||||||
|
while (!rl.windowShouldClose()) {
|
||||||
|
machine.keyboard.press_or_release_if(0x1, rl.isKeyDown(.one));
|
||||||
|
machine.keyboard.press_or_release_if(0x2, rl.isKeyDown(.two));
|
||||||
|
machine.keyboard.press_or_release_if(0x3, rl.isKeyDown(.three));
|
||||||
|
machine.keyboard.press_or_release_if(0xC, rl.isKeyDown(.four));
|
||||||
|
|
||||||
|
machine.keyboard.press_or_release_if(0x4, rl.isKeyDown(.q));
|
||||||
|
machine.keyboard.press_or_release_if(0x5, rl.isKeyDown(.w));
|
||||||
|
machine.keyboard.press_or_release_if(0x6, rl.isKeyDown(.e));
|
||||||
|
machine.keyboard.press_or_release_if(0xD, rl.isKeyDown(.r));
|
||||||
|
|
||||||
|
machine.keyboard.press_or_release_if(0x7, rl.isKeyDown(.a));
|
||||||
|
machine.keyboard.press_or_release_if(0x8, rl.isKeyDown(.s));
|
||||||
|
machine.keyboard.press_or_release_if(0x9, rl.isKeyDown(.d));
|
||||||
|
machine.keyboard.press_or_release_if(0xE, rl.isKeyDown(.f));
|
||||||
|
|
||||||
|
machine.keyboard.press_or_release_if(0xA, rl.isKeyDown(.z));
|
||||||
|
machine.keyboard.press_or_release_if(0x0, rl.isKeyDown(.x));
|
||||||
|
machine.keyboard.press_or_release_if(0xB, rl.isKeyDown(.c));
|
||||||
|
machine.keyboard.press_or_release_if(0xF, rl.isKeyDown(.v));
|
||||||
|
|
||||||
|
machine.tick();
|
||||||
|
|
||||||
|
rl.beginDrawing();
|
||||||
|
defer rl.endDrawing();
|
||||||
|
|
||||||
|
rl.clearBackground(.black);
|
||||||
|
|
||||||
|
for (0..32) |y| {
|
||||||
|
for (0..64) |x| {
|
||||||
|
if (machine.display.get(@intCast(x), @intCast(y)) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.drawRectangle(@intCast(x * PIXEL_SCALE), @intCast(y * PIXEL_SCALE), PIXEL_SCALE, PIXEL_SCALE, .white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,19 @@
|
||||||
pub const Display = struct {
|
pub const Display = struct {
|
||||||
pixels: [32][64]bool = .{},
|
pixels: [32][64]u1 = [_][64]u1{[_]u1{0} ** 64} ** 32,
|
||||||
|
|
||||||
fn new() @This() {
|
pub fn new() @This() {
|
||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear(self: *@This()) void {
|
||||||
|
self.pixels = [_][64]u1{[_]u1{0} ** 64} ** 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: *const @This(), x: u8, y: u8) u1 {
|
||||||
|
return self.pixels[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(self: *@This(), x: u8, y: u8, value: u1) void {
|
||||||
|
self.pixels[y][x] = value;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,28 @@
|
||||||
pub const Keyboard = struct {
|
pub const Keyboard = struct {
|
||||||
// FEDCBA9876543210
|
// We track each key by its index (0..15)
|
||||||
pressed: u16 = 0,
|
pressed: [16]bool = [_]bool{false} ** 16,
|
||||||
|
|
||||||
fn new() @This() {
|
pub fn new() @This() {
|
||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn released(self: *const @This()) u16 {
|
pub fn press(self: *@This(), key: u4) void {
|
||||||
return ~self.pressed;
|
self.pressed[@intCast(key)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mask(key: u4) u16 {
|
pub fn release(self: *@This(), key: u4) void {
|
||||||
return (@as(u16, 1) << key);
|
self.pressed[@intCast(key)] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn press(self: *@This(), key: u4) void {
|
pub fn press_or_release_if(self: *@This(), key: u4, cond: bool) void {
|
||||||
self.pressed |= mask(key);
|
self.pressed[@intCast(key)] = cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release(self: *@This(), key: u4) void {
|
pub fn is_pressed(self: *const @This(), key: u4) bool {
|
||||||
self.pressed &= ~mask(key);
|
return self.pressed[@intCast(key)];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn press_or_release_if(self: *@This(), key: u4, cond: bool) void {
|
pub fn is_released(self: *const @This(), key: u4) bool {
|
||||||
if (cond) {
|
|
||||||
self.press(key);
|
|
||||||
} else {
|
|
||||||
self.release(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_pressed(self: *const @This(), key: u4) bool {
|
|
||||||
return (self.pressed & mask(key)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_released(self: *const @This(), key: u4) bool {
|
|
||||||
return !self.is_pressed(key);
|
return !self.is_pressed(key);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pub const Memory = struct {
|
pub const Memory = struct {
|
||||||
memory: [4096]u8 = .{},
|
memory: [4096]u8 = [_]u8{0} ** 4096,
|
||||||
|
|
||||||
pub fn new() @This() {
|
pub fn new() @This() {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -12,4 +12,15 @@ pub const Memory = struct {
|
||||||
pub fn peek(self: *const @This(), addr: usize) u8 {
|
pub fn peek(self: *const @This(), addr: usize) u8 {
|
||||||
return self.memory[addr];
|
return self.memory[addr];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn put16(self: *@This(), addr: usize, value: u16) void {
|
||||||
|
self.memory[addr] = @intCast((value >> 8) & 0xFF);
|
||||||
|
self.memory[addr + 1] = @intCast(value & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek16(self: *const @This(), addr: usize) u16 {
|
||||||
|
const hi: u16 = @intCast(self.memory[addr]);
|
||||||
|
const lo: u16 = @intCast(self.memory[addr + 1]);
|
||||||
|
return (hi << 8) | lo;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
pub const Registers = struct {
|
pub const Registers = struct {
|
||||||
V: [16]u8 = .{},
|
V: [16]u8 = [_]u8{0} ** 16,
|
||||||
DT: u8 = 0,
|
DT: u8 = 0,
|
||||||
ST: u8 = 0,
|
ST: u8 = 0,
|
||||||
I: u16 = 0,
|
I: u16 = 0,
|
||||||
PC: u16 = 0x200,
|
PC: u16 = 0x200,
|
||||||
SP: u8 = 0,
|
|
||||||
|
|
||||||
pub fn new() Registers {
|
pub fn new() Registers {
|
||||||
return Registers{};
|
return Registers{};
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,4 @@ pub const Memory = @import("memory.zig").Memory;
|
||||||
pub const Registers = @import("registers.zig").Registers;
|
pub const Registers = @import("registers.zig").Registers;
|
||||||
pub const Keyboard = @import("keyboard.zig").Keyboard;
|
pub const Keyboard = @import("keyboard.zig").Keyboard;
|
||||||
pub const Display = @import("display.zig").Display;
|
pub const Display = @import("display.zig").Display;
|
||||||
|
pub const WrappingStack = @import("wrapping_stack.zig").WrappingStack;
|
||||||
|
|
|
||||||
25
src/octochip/components/wrapping_stack.zig
Normal file
25
src/octochip/components/wrapping_stack.zig
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// NOTE: used for the machine callstack.
|
||||||
|
// via WrappingStack(u16, 16)
|
||||||
|
pub fn WrappingStack(comptime T: type, comptime size: usize) type {
|
||||||
|
return struct {
|
||||||
|
stack: [size]T = undefined,
|
||||||
|
pointer: usize = 0,
|
||||||
|
|
||||||
|
pub fn new() @This() {
|
||||||
|
return .{
|
||||||
|
.stack = [_]T{0} ** size,
|
||||||
|
.pointer = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: *@This(), value: T) void {
|
||||||
|
self.stack[self.pointer] = value;
|
||||||
|
self.pointer = (self.pointer + 1) % size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(self: *@This()) T {
|
||||||
|
self.pointer = (self.pointer + size - 1) % size;
|
||||||
|
return self.stack[self.pointer];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
1
src/octochip/machine/digits.bin
Normal file
1
src/octochip/machine/digits.bin
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ð<EFBFBD><EFBFBD><EFBFBD>ð ` pðð€ðððð<><C3B0>ðð€ððð€ð<E282AC>ðð @@ð<>ð<EFBFBD>ðð<C3B0>ððð<C3B0>ð<EFBFBD><C3B0>à<EFBFBD>à<EFBFBD>àð€€€ðà<C3B0><C3A0><EFBFBD>àð€ð€ðð€ð€€
|
||||||
|
|
@ -129,11 +129,11 @@ pub const Instruction = union(enum) {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
.CLS => try writer.writeAll("CLS"),
|
.CLS => try writer.writeAll("CLS"),
|
||||||
.RET => try writer.writeAll("RET"),
|
.RET => try writer.writeAll("RET"),
|
||||||
.SYS => |v| try writer.print("SYS {x}", .{v.location}),
|
.SYS => |v| try writer.print("SYS {X}", .{v.location}),
|
||||||
.JP => |v| try writer.print("JP {x}", .{v.location}),
|
.JP => |v| try writer.print("JP {X}", .{v.location}),
|
||||||
.CALL => |v| try writer.print("CALL {x}", .{v.subroutine}),
|
.CALL => |v| try writer.print("CALL {X}", .{v.subroutine}),
|
||||||
.SE => |v| try writer.print("SE V{X}, {x}", .{ v.register, v.value }),
|
.SE => |v| try writer.print("SE V{X}, {X}", .{ v.register, v.value }),
|
||||||
.SNE => |v| try writer.print("SNE V{X}, {x}", .{ v.register, v.value }),
|
.SNE => |v| try writer.print("SNE V{X}, {X}", .{ v.register, v.value }),
|
||||||
.SER => |v| try writer.print("SE V{X}, V{X}", .{ v.first, v.second }),
|
.SER => |v| try writer.print("SE V{X}, V{X}", .{ v.first, v.second }),
|
||||||
.LD => |v| try writer.print("LD V{X}, {x}", .{ v.register, v.value }),
|
.LD => |v| try writer.print("LD V{X}, {x}", .{ v.register, v.value }),
|
||||||
.ADD => |v| try writer.print("ADD V{X}, {x}", .{ v.register, v.value }),
|
.ADD => |v| try writer.print("ADD V{X}, {x}", .{ v.register, v.value }),
|
||||||
|
|
|
||||||
|
|
@ -1 +1,276 @@
|
||||||
// the part that actually executes chip8
|
const std = @import("std");
|
||||||
|
|
||||||
|
const c = @import("../components/root.zig");
|
||||||
|
const Instruction = @import("./instruction.zig").Instruction;
|
||||||
|
|
||||||
|
pub const Machine = struct {
|
||||||
|
display: c.Display = c.Display.new(),
|
||||||
|
keyboard: c.Keyboard = c.Keyboard.new(),
|
||||||
|
memory: c.Memory = c.Memory.new(),
|
||||||
|
registers: c.Registers = c.Registers.new(),
|
||||||
|
stack: c.WrappingStack(u16, 16) = c.WrappingStack(u16, 16).new(),
|
||||||
|
|
||||||
|
const log = std.log.scoped(.machine);
|
||||||
|
|
||||||
|
pub fn new() @This() {
|
||||||
|
var self: @This() = .{};
|
||||||
|
|
||||||
|
const digits = @embedFile("./digits.bin");
|
||||||
|
@memcpy(self.memory.memory[0x0..digits.len], digits);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadRom(self: *@This(), rom: []const u8) !void {
|
||||||
|
if (rom.len > self.memory.memory.len) return error.RomTooBig;
|
||||||
|
@memcpy(self.memory.memory[0x200..(0x200 + rom.len)], rom);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(self: *@This()) void {
|
||||||
|
const instruction_raw = self.memory.peek16(self.registers.PC);
|
||||||
|
const instruction = Instruction.from_bytes(instruction_raw) catch {
|
||||||
|
log.warn("Invalid instruction: 0x{X}", .{instruction_raw});
|
||||||
|
log.warn(" skipping...", .{});
|
||||||
|
self.registers.PC += 2;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
log.debug("pc=0x{X} ins={f}", .{ self.registers.PC, instruction });
|
||||||
|
|
||||||
|
switch (instruction) {
|
||||||
|
.SYS => {
|
||||||
|
log.warn("The ROM you are currently running tried to execute a SYS instruction! ({f})", .{instruction});
|
||||||
|
log.warn("These are ignored by modern emulators, and this emulator is no exception.", .{});
|
||||||
|
log.warn("Take heed: the ROM may now misbehave!", .{});
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.CLS => {
|
||||||
|
self.display.clear();
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.RET => {
|
||||||
|
self.registers.PC = self.stack.pop() + 2;
|
||||||
|
},
|
||||||
|
.JP => |jp| {
|
||||||
|
self.registers.PC = jp.location;
|
||||||
|
},
|
||||||
|
.CALL => |call| {
|
||||||
|
self.stack.push(self.registers.PC);
|
||||||
|
self.registers.PC = call.subroutine;
|
||||||
|
},
|
||||||
|
.SE => |se| {
|
||||||
|
if (self.registers.get(se.register) == se.value) {
|
||||||
|
self.registers.PC += 4;
|
||||||
|
} else {
|
||||||
|
self.registers.PC += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.SNE => |sne| {
|
||||||
|
if (self.registers.get(sne.register) != sne.value) {
|
||||||
|
self.registers.PC += 4;
|
||||||
|
} else {
|
||||||
|
self.registers.PC += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.SER => |ser| {
|
||||||
|
if (self.registers.get(ser.first) == self.registers.get(ser.second)) {
|
||||||
|
self.registers.PC += 4;
|
||||||
|
} else {
|
||||||
|
self.registers.PC += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.LD => |ld| {
|
||||||
|
self.registers.set(ld.register, ld.value);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.ADD => |add| {
|
||||||
|
const old = self.registers.get(add.register);
|
||||||
|
self.registers.set(add.register, old +% add.value);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDR => |ldr| {
|
||||||
|
self.registers.set(ldr.first, self.registers.get(ldr.second));
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.OR => |orr| {
|
||||||
|
const first = self.registers.get(orr.first);
|
||||||
|
const second = self.registers.get(orr.second);
|
||||||
|
const result = first | second;
|
||||||
|
self.registers.set(orr.first, result);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.AND => |andd| {
|
||||||
|
const first = self.registers.get(andd.first);
|
||||||
|
const second = self.registers.get(andd.second);
|
||||||
|
const result = first & second;
|
||||||
|
self.registers.set(andd.first, result);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.XOR => |xor| {
|
||||||
|
const first = self.registers.get(xor.first);
|
||||||
|
const second = self.registers.get(xor.second);
|
||||||
|
const result = first ^ second;
|
||||||
|
self.registers.set(xor.first, result);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.ADDR => |addr| {
|
||||||
|
const first = self.registers.get(addr.first);
|
||||||
|
const second = self.registers.get(addr.second);
|
||||||
|
const result = @addWithOverflow(first, second);
|
||||||
|
|
||||||
|
const sum = result[0];
|
||||||
|
const carry = result[1];
|
||||||
|
|
||||||
|
self.registers.set(addr.first, sum);
|
||||||
|
self.registers.set(0xF, carry);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.SUB => |sub| {
|
||||||
|
const first = self.registers.get(sub.first);
|
||||||
|
const second = self.registers.get(sub.second);
|
||||||
|
self.registers.set(0xF, if (first > second) 1 else 0);
|
||||||
|
self.registers.set(sub.first, first -% second);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.SHR => |shr| {
|
||||||
|
const value = self.registers.get(shr.register);
|
||||||
|
const lsb = value & 0x01;
|
||||||
|
self.registers.set(0xF, lsb);
|
||||||
|
self.registers.set(shr.register, value >> 1);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.SUBN => |subn| {
|
||||||
|
const vy = self.registers.get(subn.second);
|
||||||
|
const vx = self.registers.get(subn.first);
|
||||||
|
self.registers.set(0xF, if (vy > vx) 1 else 0);
|
||||||
|
self.registers.set(subn.first, vy -% vx);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.SHL => |shl| {
|
||||||
|
const value = self.registers.get(shl.register);
|
||||||
|
const msb = (value & 0x80) >> 7;
|
||||||
|
self.registers.set(0xF, msb);
|
||||||
|
self.registers.set(shl.register, value << 1);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.SNER => |sner| {
|
||||||
|
if (self.registers.get(sner.first) != self.registers.get(sner.second)) {
|
||||||
|
self.registers.PC += 4;
|
||||||
|
} else {
|
||||||
|
self.registers.PC += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.LDI => |ldi| {
|
||||||
|
self.registers.I = ldi.value;
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.JPV0 => |jpv0| {
|
||||||
|
self.registers.PC = self.registers.get(0x0) + jpv0.add;
|
||||||
|
},
|
||||||
|
.RND => |rnd| {
|
||||||
|
const r: u8 = std.crypto.random.int(u8);
|
||||||
|
const value = r & rnd.value;
|
||||||
|
self.registers.set(rnd.register, value);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.DRW => |drw| {
|
||||||
|
var VF: u8 = 0;
|
||||||
|
|
||||||
|
const x0: u8 = self.registers.get(drw.x_register);
|
||||||
|
const y0: u8 = self.registers.get(drw.y_register);
|
||||||
|
const h: u8 = drw.bytes;
|
||||||
|
|
||||||
|
for (0..h) |row| {
|
||||||
|
const sprite: u8 = self.memory.peek(self.registers.I + row);
|
||||||
|
|
||||||
|
for (0..8) |col| {
|
||||||
|
const sprite_pixel: u1 = @intCast((sprite >> @intCast(7 - col)) & 1);
|
||||||
|
|
||||||
|
if (sprite_pixel == 1) {
|
||||||
|
const x: u8 = @intCast((x0 + col) % 64);
|
||||||
|
const y: u8 = @intCast((y0 + row) % 32);
|
||||||
|
|
||||||
|
const old_pixel = self.display.get(x, y);
|
||||||
|
if (old_pixel == 1) {
|
||||||
|
VF = 1;
|
||||||
|
}
|
||||||
|
self.display.pixels[y][x] ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.registers.set(0xF, VF);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.SKP => |skp| {
|
||||||
|
if (self.keyboard.is_pressed(@truncate(self.registers.get(skp.key)))) {
|
||||||
|
self.registers.PC += 4;
|
||||||
|
} else {
|
||||||
|
self.registers.PC += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.SKNP => |sknp| {
|
||||||
|
if (!self.keyboard.is_pressed(@truncate(self.registers.get(sknp.key)))) {
|
||||||
|
self.registers.PC += 4;
|
||||||
|
} else {
|
||||||
|
self.registers.PC += 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.LDDTIN => |lddtin| {
|
||||||
|
self.registers.set(lddtin.register, self.registers.DT);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDK => |ldk| {
|
||||||
|
for (0..16) |key| {
|
||||||
|
if (self.keyboard.is_pressed(@intCast(key))) {
|
||||||
|
self.registers.set(ldk.register, @intCast(key));
|
||||||
|
self.registers.PC += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If no key is currently pressed, PC is not advanced.
|
||||||
|
// tick() will re-execute LDK on the next frame until a key is held.
|
||||||
|
},
|
||||||
|
.LDDT => |lddt| {
|
||||||
|
self.registers.DT = self.registers.get(lddt.register);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDST => |ldst| {
|
||||||
|
self.registers.ST = self.registers.get(ldst.register);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.ADDI => |addi| {
|
||||||
|
self.registers.I += self.registers.get(addi.register);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDF => |ldf| {
|
||||||
|
self.registers.I = self.registers.get(ldf.register) * 5;
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDB => |ldb| {
|
||||||
|
const value: u8 = self.registers.get(ldb.register);
|
||||||
|
self.memory.put(self.registers.I + 0, value / 100);
|
||||||
|
self.memory.put(self.registers.I + 1, (value / 10) % 10);
|
||||||
|
self.memory.put(self.registers.I + 2, value % 10);
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDRO => |ldro| {
|
||||||
|
const count = @as(usize, ldro.until_register) + 1;
|
||||||
|
for (0..count) |register| {
|
||||||
|
self.memory.put(self.registers.I + register, self.registers.get(@intCast(register)));
|
||||||
|
}
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
.LDRI => |ldri| {
|
||||||
|
const count = @as(usize, ldri.until_register) + 1;
|
||||||
|
for (0..count) |register| {
|
||||||
|
self.registers.set(@intCast(register), self.memory.peek(self.registers.I + register));
|
||||||
|
}
|
||||||
|
self.registers.PC += 2;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.registers.DT > 0) self.registers.DT -= 1;
|
||||||
|
if (self.registers.ST > 0) self.registers.ST -= 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub const Instruction = @import("instruction.zig").Instruction;
|
pub const Instruction = @import("instruction.zig").Instruction;
|
||||||
|
pub const Machine = @import("machine.zig").Machine;
|
||||||
|
|
|
||||||
BIN
src/rom.ch8
Normal file
BIN
src/rom.ch8
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue