Skip to content

Commit

Permalink
kc85 file loading wip
Browse files Browse the repository at this point in the history
  • Loading branch information
floooh committed Aug 8, 2024
1 parent 9b50199 commit c5da083
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 7 deletions.
22 changes: 22 additions & 0 deletions emus/host/time.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,25 @@ pub fn after(micro_seconds: u64) bool {
const elapsed_us: u64 = @intFromFloat(stm.us(stm.since(state.start_time)));
return elapsed_us > micro_seconds;
}

// a helper which triggers an action once after a delay
pub const Once = struct {
delay_us: u64,
triggered: bool,

pub fn init(delay_us: u64) Once {
return .{
.delay_us = delay_us,
.triggered = false,
};
}

pub fn once(self: *Once) bool {
if (!self.triggered and after(self.delay_us)) {
self.triggered = true;
return true;
} else {
return false;
}
}
};
14 changes: 14 additions & 0 deletions emus/kc85/kc85.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ const name = switch (model) {
};
const KC85 = kc85.Type(model);

// a once-trigger for loading a file after booting has finished
var file_loaded = host.time.Once.init(switch (model) {
.KC852, .KC853 => 8 * 1000 * 1000,
.KC854 => 3 * 1000 * 1000,
});

var sys: KC85 = undefined;
var gpa = GeneralPurposeAllocator(.{}){};
var args: Args = undefined;
Expand Down Expand Up @@ -79,6 +85,14 @@ export fn frame() void {
.emu_stats = host.prof.stats(.EMU),
},
});
if (file_loaded.once()) {
if (args.file_data) |file_data| {
// FIXME: patch-func
sys.load(.{ .data = file_data, .start = true, .patch = null }) catch |err| {
print("Failed to load file into emulator with {}", .{err});
};
}
}
}

export fn cleanup() void {
Expand Down
Binary file added media/kc85/demo.kcc
Binary file not shown.
6 changes: 6 additions & 0 deletions src/common/memory.zig
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ pub fn Type(comptime cfg: TypeConfig) type {
self.pages[addr >> PAGE_SHIFT].write[addr & PAGE_MASK] = data;
}

// write 16-bit value to memory
pub inline fn wr16(self: *Self, addr: u16, data: u16) void {
self.wr(addr, @truncate(data >> 8));
self.wr(addr +% 1, @truncate(data));
}

/// map address range as RAM to host memory
pub fn mapRAM(self: *Self, addr: u16, size: u17, ram: []u8) void {
assert(ram.len == size);
Expand Down
98 changes: 91 additions & 7 deletions src/systems/kc85.zig
Original file line number Diff line number Diff line change
Expand Up @@ -983,13 +983,97 @@ pub fn Type(comptime model: Model) type {
kcc: KCCHeader, // from here on identical with KCC
};

pub fn load(data: []const u8, start: bool) !void {
_ = start; // autofix
if (isKCTAPMagic(data)) {
@panic("FIXME");
pub const LoadOptions = struct {
data: []const u8,
start: bool,
patch: ?struct {
func: *const fn (snapshot_name: []const u8, userdata: usize) void,
userdata: usize = 0,
},
};

pub fn load(self: *Self, opts: LoadOptions) !void {
if (isKCTAPMagic(opts.data)) {
try self.loadKCTAP(opts);
} else {
@panic("FIXME");
try self.loadKCC(opts);
}
}

pub fn loadKCTAP(self: *Self, opts: LoadOptions) !void {
try ensureValidKCTAP(opts.data);
const hdr: *const KCTAPHeader = @ptrCast(opts.data);
var addr = asU16(hdr.kcc.load_addr_h, hdr.kcc.load_addr_l);
const end_addr = asU16(hdr.kcc.end_addr_h, hdr.kcc.end_addr_l);
var pos: usize = @sizeOf(KCTAPHeader);
while (addr < end_addr) {
// each block is 1 lead byte + 128 bytes data
pos += 1;
for (pos..pos + 128) |i| {
if (addr < end_addr) {
self.mem.wr(addr, opts.data[i]);
addr +%= 1;
}
}
}
if (opts.patch) |patch| {
patch.func(&hdr.kcc.name, patch.userdata);
}
if (opts.start) {
const exec_addr = asU16(hdr.kcc.exec_addr_h, hdr.kcc.exec_addr_l);
self.loadStart(exec_addr);
}
}

pub fn loadKCC(self: *Self, opts: LoadOptions) !void {
try ensureValidKCC(opts.data);
const hdr: *const KCCHeader = @ptrCast(opts.data);
var addr = asU16(hdr.load_addr_h, hdr.load_addr_l);
const end_addr = asU16(hdr.end_addr_h, hdr.end_addr_l);
for (opts.data[@sizeOf(KCCHeader)..]) |byte| {
if (addr < end_addr) {
self.mem.wr(addr, byte);
addr += 1;
}
}
if (opts.patch) |patch| {
patch.func(&hdr.name, patch.userdata);
}
if (opts.start) {
const exec_addr = asU16(hdr.exec_addr_h, hdr.exec_addr_l);
self.loadStart(exec_addr);
}
}

fn loadReturnAddr() u16 {
return switch (model) {
.KC852 => @panic("FIXME: find return address for loaded files"),
.KC853 => 0xF15C,
.KC854 => 0xF17E,
};
}

fn loadStart(self: *Self, exec_addr: u16) void {
self.cpu.r[Z80.A] = 0;
self.cpu.r[Z80.F] = 0x10;
self.cpu.af2 = 0;
self.cpu.setBC(0);
self.cpu.bc2 = 0;
self.cpu.setDE(0);
self.cpu.de2 = 0;
self.cpu.setHL(0);
self.cpu.hl2 = 0;
self.cpu.setSP(0x01C2);
// delete ASCII buffer
for (0xB200..0xB700) |addr| {
self.mem.wr(@truncate(addr), 0);
}
self.mem.wr(0xB7A0, 0);
// FIXME: do we actually need to reset PIO-B and memory mapping here?
// write return address
self.mem.wr16(self.cpu.SP(), exec_addr);
// start execution at new address
self.cpu.prefetch(exec_addr);
}

fn isKCTAPMagic(data: []const u8) bool {
Expand All @@ -998,7 +1082,7 @@ pub fn Type(comptime model: Model) type {
}
const hdr: *const KCTAPHeader = @ptrCast(data);
const magic = [16]u8{ 0xC3, 'K', 'C', '-', 'T', 'A', 'P', 'E', 0x20, 'b', 'y', 0x20, 'A', 'F', '.', 0x20 };
return std.mem.eql(u8, &magic, &hdr.magic);
return std.mem.eql(u8, &magic, &hdr.sig);
}

fn asU16(hi: u8, lo: u8) u16 {
Expand Down Expand Up @@ -1032,7 +1116,7 @@ pub fn Type(comptime model: Model) type {

fn ensureValidKCC(data: []const u8) !void {
if (data.len <= @sizeOf(KCCHeader)) {
return false;
return error.KCCNotEnoughData;
}
const hdr: *const KCCHeader = @ptrCast(data);
if (hdr.num_addr > 3) {
Expand Down

0 comments on commit c5da083

Please sign in to comment.