diff --git a/emus/kc85/kc85.zig b/emus/kc85/kc85.zig index 43ddfea..4983da7 100644 --- a/emus/kc85/kc85.zig +++ b/emus/kc85/kc85.zig @@ -1,5 +1,8 @@ const build_options = @import("build_options"); const std = @import("std"); +const print = std.debug.print; +const fs = std.fs; +const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator; const sokol = @import("sokol"); const sapp = sokol.app; const slog = sokol.log; @@ -19,7 +22,21 @@ const name = switch (model) { }; const KC85 = kc85.Type(model); +// command line args +const Args = struct { + const Module = struct { + mod_type: KC85.ModuleType = .NONE, + rom_dump: ?[]const u8 = null, + }; + // optional file to load (.KCC or .TAP format) + file: ?[]const u8 = null, + // modules to insert into slot 08 and 0C + slot8: Module = .{}, + slotc: Module = .{}, +}; + var sys: KC85 = undefined; +var gpa = GeneralPurposeAllocator(.{}){}; export fn init() void { host.audio.init(.{}); @@ -47,6 +64,10 @@ export fn init() void { }, }); host.gfx.init(.{ .display = sys.displayInfo() }); + const args = parseArgs(gpa.allocator()) catch { + std.process.exit(10); + }; + print("args: {}\n", .{args}); } export fn frame() void { @@ -70,6 +91,9 @@ export fn cleanup() void { host.gfx.shutdown(); host.prof.shutdown(); host.audio.shutdown(); + if (gpa.deinit() != .ok) { + @panic("Memory leaks detected"); + } } export fn input(ev: ?*const sapp.Event) void { @@ -154,3 +178,92 @@ pub fn main() void { .logger = .{ .func = slog.func }, }); } + +fn isArg(arg: []const u8, comptime strings: []const []const u8) bool { + for (strings) |str| { + if (std.mem.eql(u8, arg, str)) { + return true; + } + } + return false; +} + +fn parseArgs(alloc: std.mem.Allocator) !Args { + var args = Args{}; + var arg_iter = try std.process.argsWithAllocator(alloc); + defer arg_iter.deinit(); + _ = arg_iter.skip(); + while (arg_iter.next()) |arg| { + if (isArg(arg, &.{ "-h", "-help", "--help" })) { + const help = .{ + " -file path -- load .KCC or .TAP file", + " -slot8 name [rom dump path] -- load module into slot 08", + " -slotc name [rom dump path] -- load module into slot 0C", + "", + " Valid module names are:", + " m006 - KC85/2 BASIC ROM", + " m011 - 64 KByte RAM", + " m022 - 16 KByte RAM", + " m012 - TEXOR text processor", + " m026 - FORTH development", + " m027 - assembly development", + }; + inline for (help) |s| { + print(s ++ "\n", .{}); + } + } else if (isArg(arg, &.{"-file"})) { + const next = arg_iter.next() orelse { + print("Expected path to .KCC or .TAP file after '{s}'\n", .{arg}); + return error.InvalidArgs; + }; + args.file = try alloc.dupe(u8, next); + } else if (isArg(arg, &.{"-slot8"})) { + args.slot8 = try parseModuleArgs(alloc, arg, &arg_iter); + } else if (isArg(arg, &.{"-slotc"})) { + args.slotc = try parseModuleArgs(alloc, arg, &arg_iter); + } else { + print("Unknown argument: {s} (run '-help' to show valid args)\n", .{arg}); + return error.InvalidArgs; + } + } + return args; +} + +fn parseModuleArgs(alloc: std.mem.Allocator, arg: []const u8, arg_iter: *std.process.ArgIterator) !Args.Module { + var mod = Args.Module{}; + const mod_name = arg_iter.next() orelse { + print("Expected module name after '{s}'\n", .{arg}); + return error.InvalidArgs; + }; + const mod_table = .{ + .{ .name = "m006", .mod_type = .M006_BASIC, .rom = true }, + .{ .name = "m011", .mod_type = .M011_64KBYTE, .rom = false }, + .{ .name = "m012", .mod_type = .M012_TEXOR, .rom = true }, + .{ .name = "m022", .mod_type = .M022_16KBYTE, .rom = false }, + .{ .name = "m026", .mod_type = .M026_FORTH, .rom = true }, + .{ .name = "m026", .mod_type = .M027_DEV, .rom = true }, + }; + var is_rom_module = false; + inline for (mod_table) |item| { + if (std.mem.eql(u8, mod_name, item.name)) { + mod.mod_type = item.mod_type; + is_rom_module = item.rom; + break; + } + } + if (mod.mod_type == .NONE) { + print("Invalid module name '{s} (see -help for list of valid module names)\n", .{mod_name}); + return error.InvalidArgs; + } + if (is_rom_module) { + const rom_dump_path = arg_iter.next() orelse { + print("Expect ROM dump file path after '{s} {s}\n", .{ arg, mod_name }); + return error.InvalidArgs; + }; + mod.rom_dump = fs.cwd().readFileAlloc(alloc, rom_dump_path, 64 * 1024) catch |err| { + print("Failed to load module rom dump file '{s}'\n", .{rom_dump_path}); + return err; + }; + } + return mod; +} diff --git a/zig_notes.md b/zig_notes.md index fb04fb6..12bfcb9 100644 --- a/zig_notes.md +++ b/zig_notes.md @@ -1,5 +1,5 @@ - Low debug performance may become a problem => investigate! - => release=safe is fast + => release=safe is fast => PS: not for the KC85 though! => inline functions are also inlined in debug mode, but still push/pop parameters on stack