const std = @import("std"); const StackSize = 33; const ReturnStackSize = 257; const MemorySize = 65536; var ip: i32 = 0; // instruction pointer var sp: i32 = 0; // data stack pointer var rp: i32 = 0; // address stack pointer var ds: [StackSize]i32 = [_]i32{0} ** StackSize; // data stack var as: [ReturnStackSize]i32 = [_]i32{0} ** ReturnStackSize; // address stack var m: [MemorySize]i32 = [_]i32{0} ** MemorySize; // memory var blocks: []const u8 = "ilo.blocks"; // name of block file var rom: []const u8 = "ilo.rom"; // name of image var a: i32 = 0; var b: i32 = 0; var s: i32 = 0; var d: i32 = 0; var l: i32 = 0; var io_buf: [1]u8 = .{0}; inline fn t() *i32 { return &ds[@as(usize, @intCast(sp))]; } inline fn n() *i32 { return &ds[@as(usize, @intCast(sp - 1))]; } inline fn r() *i32 { return &as[@as(usize, @intCast(rp))]; } fn push(v: i32) void { ds[@as(usize, @intCast(sp + 1))] = v; sp += 1; } fn pop() i32 { sp -= 1; return ds[@as(usize, @intCast(sp + 1))]; } fn load_image() void { const file = std.fs.cwd().openFile(rom, .{ .mode = .read_only }) catch return; defer file.close(); const bytes = std.mem.sliceAsBytes(m[0..]); _ = file.readAll(bytes) catch {}; ip = 0; sp = 0; rp = 0; } fn save_image() void { const file = std.fs.cwd().openFile(rom, .{ .mode = .write_only }) catch return; defer file.close(); const bytes = std.mem.sliceAsBytes(m[0..]); _ = file.writeAll(bytes) catch {}; } fn block_common(file: *std.fs.File) void { b = pop(); // block buffer a = pop(); // block number const offset: u64 = if (a > 0) @as(u64, @intCast(@as(i64, a) * 4096)) else 0; _ = file.seekTo(offset) catch {}; } fn read_block() void { var file = std.fs.cwd().openFile(blocks, .{ .mode = .read_only }) catch return; defer file.close(); block_common(&file); const start = @as(usize, @intCast(b)); const bytes = std.mem.sliceAsBytes(m[start..]); _ = file.readAll(bytes[0..4096]) catch {}; } fn write_block() void { var file = std.fs.cwd().openFile(blocks, .{ .mode = .write_only }) catch return; defer file.close(); block_common(&file); const start = @as(usize, @intCast(b)); const bytes = std.mem.sliceAsBytes(m[start..]); _ = file.writeAll(bytes[0..4096]) catch {}; } fn save_ip() void { rp += 1; r().* = ip; } fn symmetric() void { if (b >= 0 and n().* < 0) { t().* +%= 1; n().* -%= b; } } fn li() void { ip += 1; push(m[@as(usize, @intCast(ip))]); } fn du() void { push(t().*); } fn dr() void { ds[@as(usize, @intCast(sp))] = 0; sp -= 1; } fn sw() void { a = t().*; t().* = n().*; n().* = a; } fn pu() void { rp += 1; r().* = pop(); } fn po() void { push(r().*); rp -= 1; } fn ju() void { ip = pop() - 1; } fn ca() void { save_ip(); ip = pop() - 1; } fn cc() void { a = pop(); if (pop() != 0) { save_ip(); ip = a - 1; } } fn cj() void { a = pop(); if (pop() != 0) { ip = a - 1; } } fn re() void { ip = r().*; rp -= 1; } fn eq() void { n().* = if (n().* == t().*) -1 else 0; sp -= 1; } fn ne() void { n().* = if (n().* != t().*) -1 else 0; sp -= 1; } fn lt() void { n().* = if (n().* < t().*) -1 else 0; sp -= 1; } fn gt() void { n().* = if (n().* > t().*) -1 else 0; sp -= 1; } fn fe() void { t().* = m[@as(usize, @intCast(t().*))]; } fn st() void { m[@as(usize, @intCast(t().*))] = n().*; sp -= 2; } fn ad() void { n().* +%= t().*; sp -= 1; } fn su() void { n().* -%= t().*; sp -= 1; } fn mu() void { n().* *%= t().*; sp -= 1; } fn di() void { a = t().*; b = n().*; t().* = @divTrunc(b, a); n().* = @rem(b, a); symmetric(); } fn an() void { n().* = t().* & n().*; sp -= 1; } fn or_() void { n().* = t().* | n().*; sp -= 1; } fn xo() void { n().* = t().* ^ n().*; sp -= 1; } fn sl() void { n().* = n().* << @as(u5, @intCast(t().* & 0x1F)); sp -= 1; } fn sr() void { n().* = n().* >> @as(u5, @intCast(t().* & 0x1F)); sp -= 1; } fn cp() void { l = pop(); d = pop(); s = t().*; t().* = -1; while (l != 0) { if (m[@as(usize, @intCast(d))] != m[@as(usize, @intCast(s))]) { t().* = 0; } l -= 1; s += 1; d += 1; } } fn cy() void { l = pop(); d = pop(); s = pop(); while (l != 0) { m[@as(usize, @intCast(d))] = m[@as(usize, @intCast(s))]; l -= 1; s += 1; d += 1; } } fn ioa() void { io_buf[0] = @as(u8, @intCast(pop() & 0xFF)); _ = std.posix.write(std.posix.STDOUT_FILENO, io_buf[0..]) catch {}; } fn iob() void { _ = std.posix.read(std.posix.STDIN_FILENO, io_buf[0..]) catch {}; push(@as(i32, @intCast(io_buf[0]))); } fn ioc() void { read_block(); } fn iod() void { write_block(); } fn ioe() void { save_image(); } fn iof() void { load_image(); ip = -1; } fn iog() void { ip = MemorySize; } fn ioh() void { push(sp); push(rp); } fn io() void { switch (pop()) { 0 => ioa(), 1 => iob(), 2 => ioc(), 3 => iod(), 4 => ioe(), 5 => iof(), 6 => iog(), 7 => ioh(), else => {}, } } fn process(o: i32) void { switch (o) { 0 => {}, 1 => li(), 2 => du(), 3 => dr(), 4 => sw(), 5 => pu(), 6 => po(), 7 => ju(), 8 => ca(), 9 => cc(), 10 => cj(), 11 => re(), 12 => eq(), 13 => ne(), 14 => lt(), 15 => gt(), 16 => fe(), 17 => st(), 18 => ad(), 19 => su(), 20 => mu(), 21 => di(), 22 => an(), 23 => or_(), 24 => xo(), 25 => sl(), 26 => sr(), 27 => cp(), 28 => cy(), 29 => io(), else => {}, } } fn process_bundle(opcode: i32) void { const op: u32 = @bitCast(opcode); process(@as(i32, @intCast(op & 0xFF))); process(@as(i32, @intCast((op >> 8) & 0xFF))); process(@as(i32, @intCast((op >> 16) & 0xFF))); process(@as(i32, @intCast((op >> 24) & 0xFF))); } fn execute() void { while (ip < MemorySize) { process_bundle(m[@as(usize, @intCast(ip))]); ip += 1; } } pub fn main() !void { const args = try std.process.argsAlloc(std.heap.page_allocator); defer std.process.argsFree(std.heap.page_allocator, args); if (args.len > 1) { blocks = args[1]; } if (args.len > 2) { rom = args[2]; } load_image(); execute(); const stdout_file = std.fs.File.stdout(); var buf: [64]u8 = undefined; while (sp > 0) : (sp -= 1) { const slice = try std.fmt.bufPrint(&buf, " {d}", .{ds[@as(usize, @intCast(sp))]}); try stdout_file.writeAll(slice); } try stdout_file.writeAll("\n"); }