const std = @import("std"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var uxn = Uxn{ .mem = try allocator.alloc(u8, 0xFFFF), .ws = try allocator.alloc(u8, 0xFF), .rs = try allocator.alloc(u8, 0xFF), .wsp = 0, .rsp = 0, .pc = 0x0100, }; defer allocator.free(uxn.mem); defer allocator.free(uxn.ws); defer allocator.free(uxn.rs); while (true) uxn.loop(); } pub const Uxn = struct { mem: [0xFFFF]u8, // ws: [0xFF]u8, // rs: [0xFF]u8, // wsp: u8, // rsp: u8, ws: Stack, rs: Stack, pc: u16, const Stack = struct { s: *[0xFF]u8, sp: *u8, pub fn peek(self: *Stack, comptime T: type) T { return switch (T) { u8 => self.s[self.sp], u16 => @intCast(self.s[self.sp]), // but actually do the conversion else => unreachable, }; } pub fn poke(self: *Stack, comptime T: type, v: T) void { switch (T) { u8 => self.s[self.sp] = v, u16 => {}, else => unreachable, } } pub fn pop(self: *Stack, comptime T: type) T { switch (T) { u8 => { self.sp -%= 1; return self.s[self.sp +% 1]; }, u16 => { self.sp -%= 2; return self.s[self.sp +% 1]; }, else => unreachable, } } pub fn push(self: *Stack, comptime T: type, v: T) void { switch (T) { u8 => { self.sp +%= 1; self.s[self.sp] = v; }, u16 => { self.sp +%= 2; self.s[self.sp] = v; }, else => unreachable, } } }; pub fn loop(self: *Uxn) void { switch (self.mem[self.pc]) { 0x00 => {}, // BRK 0x01 => inc(&self.ws, false, false), // INC 0x02 => pop(&self.ws, false, false), // POP 0x03 => nip(&self.ws, false, false), // NIP 0x04 => swp(&self.ws, false, false), // SWP 0x05 => rot(&self.ws, false, false), // ROT 0x06 => dup(&self.ws, false, false), // DUP 0x07 => ovr(&self.ws, false, false), // OVR 0x08 => equ(&self.ws, false, false), // EQU 0x09 => neq(&self.ws, false, false), // NEQ 0x0A => gth(&self.ws, false, false), // GTH 0x0B => lth(&self.ws, false, false), // LTH 0x0C => self.jmp(&self.ws, u8, false), // JMP 0x0D => self.jcn(&self.ws, u8, false), // JCN 0x0E => self.jsr(&self.ws, u8, false), // JSR 0x0F => self.sth(false, u8, false), // STH 0x10 => {}, // LDZ 0x11 => {}, // STZ 0x12 => {}, // LDR 0x13 => {}, // STR 0x14 => {}, // LDA 0x15 => {}, // STA 0x16 => {}, // DEI 0x17 => {}, // DEO 0x18 => {}, // ADD 0x19 => {}, // SUB 0x1A => {}, // MUL 0x1B => {}, // DIV 0x1C => {}, // AND 0x1D => {}, // ORA 0x1E => {}, // EOR 0x1F => {}, // SFT } } fn brk() void {} fn inc(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); stack.push(T, a +% 1); // if (keep) { // stack.s[stack.sp +% 1] = stack.s[stack.sp] +% 1; // stack.sp +%= 1; // } else { // stack.s[stack.sp] +%= 1; // } } fn pop(stack: *Stack, comptime T: type, comptime keep: bool) void { if (keep) return; _ = stack.pop(T); // if (!keep) { // if (short) stack.sp -%= 2 else stack.sp -%= 1; // } } fn nip(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); stack.push(T, b); // if (!keep) { // stack.sp -%= 1; // stack.s[stack.sp] = stack.s[stack.sp +% 1]; // } } fn swp(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); stack.push(T, b); stack.push(T, a); // if (keep) { // stack.s[stack.sp +% 1] = stack.s[stack.sp]; // stack.s[stack.sp +% 2] = stack.s[stack.sp -% 1]; // } else { // const a = stack.s[stack.sp -% 1]; // stack.s[stack.sp -% 1] = stack.s[stack.sp]; // stack.sp = a; // } } fn rot(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); const c = if (keep) stack.peek(T) else stack.pop(T); stack.push(T, b); stack.push(T, c); stack.push(T, a); } fn dup(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); stack.push(T, a); // stack.s[stack.sp +% 1] = stack.s[stack.sp]; // if (keep) { // stack.s[stack.sp +% 2] = stack.s[stack.sp]; // stack.sp +%= 2; // } else stack.sp +%= 1; } fn ovr(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); stack.push(T, a); stack.push(T, b); stack.push(T, a); // if (keep) { // stack.s[stack.sp +% 1] = stack.s[stack.sp -% 1]; // stack.s[stack.sp +% 2] = stack.s[stack.sp]; // stack.s[stack.sp +% 3] = stack.s[stack.sp -% 1]; // stack.sp +%= 3; // } else { // stack.s[stack.sp +% 1] = stack.s[stack.sp -% 1]; // stack.sp +%= 1; // } } fn equ(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); if (a == b) stack.push(T, 1) else stack.push(T, 0); // if (keep) {} else { // if (stack.s[stack.sp -% 1] == stack.s[stack.sp]) // stack.s[stack.sp +% 1] = 1 // else // stack.s[stack.sp +% 1] = 0; // stack.sp +%= 1; // } } fn neq(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); if (a != b) stack.push(T, 1) else stack.push(T, 0); } fn gth(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); if (a > b) stack.push(T, 1) else stack.push(T, 0); } fn lth(stack: *Stack, comptime T: type, comptime keep: bool) void { const a = if (keep) stack.peek(T) else stack.pop(T); const b = if (keep) stack.peek(T) else stack.pop(T); if (a < b) stack.push(T, 1) else stack.push(T, 0); } fn jmp(self: *Uxn, stack: *Stack, comptime T: type, comptime keep: bool) void { const addr = if (keep) stack.peek(T) else stack.pop(T); switch (T) { u8 => self.pc +%= @as(i8, @bitCast(addr)), u16 => self.pc = addr, else => unreachable, } } fn jcn(self: *Uxn, stack: *Stack, comptime T: type, comptime keep: bool) void { const cond = if (keep) stack.peek(T) else stack.pop(T); const addr = if (keep) stack.peek(T) else stack.pop(T); if (cond == 0) return; switch (T) { u8 => self.pc +%= @as(i8, @bitCast(addr)), u16 => self.pc = addr, else => unreachable, } } fn jsr(self: *Uxn, stack: *Stack, comptime T: type, comptime keep: bool) void { self.rs.push(u16, self.pc); const addr = if (keep) stack.peek(T) else stack.pop(T); switch (T) { u8 => self.pc +%= @as(i8, @bitCast(addr)), u16 => self.pc = addr, else => unreachable, } } fn sth(self: *Uxn, comptime swap: bool, comptime T: type, comptime keep: bool) void { const src = if (swap) self.rs else self.ws; const dst = if (swap) self.ws else self.rs; const a = if (keep) src.peek(T) else src.pop(T); dst.push(T, a); } fn ldz(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn stz(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn ldr(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn str(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn lda(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn sta(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn dei(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn deo(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn add(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn sub(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn mul(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn div(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn @"and"(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn ora(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn eor(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } fn sft(stack: *Stack, comptime T: type, comptime keep: bool) void { _ = short; } };