feat: implement IPCSYNC irqs
This commit is contained in:
		@@ -8,13 +8,16 @@ const Allocator = std.mem.Allocator;
 | 
			
		||||
/// Load a NDS Cartridge
 | 
			
		||||
///
 | 
			
		||||
/// intended to be used immediately after Emulator initialization
 | 
			
		||||
pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8 {
 | 
			
		||||
pub fn load(allocator: Allocator, system: System, rom_path: []const u8) ![12]u8 {
 | 
			
		||||
    const log = std.log.scoped(.load_rom);
 | 
			
		||||
 | 
			
		||||
    const rom_buf = try rom_file.readToEndAlloc(allocator, try rom_file.getEndPos());
 | 
			
		||||
    defer allocator.free(rom_buf);
 | 
			
		||||
    const file = try std.fs.cwd().openFile(rom_path, .{});
 | 
			
		||||
    defer file.close();
 | 
			
		||||
 | 
			
		||||
    var stream = std.io.fixedBufferStream(rom_buf);
 | 
			
		||||
    const buf = try file.readToEndAlloc(allocator, try file.getEndPos());
 | 
			
		||||
    defer allocator.free(buf);
 | 
			
		||||
 | 
			
		||||
    var stream = std.io.fixedBufferStream(buf);
 | 
			
		||||
    const header = try stream.reader().readStruct(Header);
 | 
			
		||||
 | 
			
		||||
    log.info("Title: \"{s}\"", .{std.mem.sliceTo(&header.title, 0)});
 | 
			
		||||
@@ -29,7 +32,7 @@ pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8
 | 
			
		||||
        log.debug("ARM9 Size: 0x{X:0>8}", .{header.arm9_size});
 | 
			
		||||
 | 
			
		||||
        // Copy ARM9 Code into Main Memory
 | 
			
		||||
        for (rom_buf[header.arm9_rom_offset..][0..header.arm9_size], 0..) |value, i| {
 | 
			
		||||
        for (buf[header.arm9_rom_offset..][0..header.arm9_size], 0..) |value, i| {
 | 
			
		||||
            const address = header.arm9_ram_address + @as(u32, @intCast(i));
 | 
			
		||||
            system.bus9.dbgWrite(u8, address, value);
 | 
			
		||||
        }
 | 
			
		||||
@@ -45,7 +48,7 @@ pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8
 | 
			
		||||
        log.debug("ARM7 Size: 0x{X:0>8}", .{header.arm7_size});
 | 
			
		||||
 | 
			
		||||
        // Copy ARM7 Code into Main Memory
 | 
			
		||||
        for (rom_buf[header.arm7_rom_offset..][0..header.arm7_size], 0..) |value, i| {
 | 
			
		||||
        for (buf[header.arm7_rom_offset..][0..header.arm7_size], 0..) |value, i| {
 | 
			
		||||
            const address = header.arm7_ram_address + @as(u32, @intCast(i));
 | 
			
		||||
            system.bus7.dbgWrite(u8, address, value);
 | 
			
		||||
        }
 | 
			
		||||
@@ -56,6 +59,39 @@ pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8
 | 
			
		||||
    return header.title;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Load NDS Firmware
 | 
			
		||||
pub fn loadFirm(allocator: Allocator, system: System, firm_path: []const u8) !void {
 | 
			
		||||
    const log = std.log.scoped(.load_firm);
 | 
			
		||||
 | 
			
		||||
    { // NDS7 BIOS
 | 
			
		||||
        const path = try std.mem.join(allocator, "/", &.{ firm_path, "bios7.bin" });
 | 
			
		||||
        defer allocator.free(path);
 | 
			
		||||
 | 
			
		||||
        log.debug("bios7 path: {s}", .{path});
 | 
			
		||||
 | 
			
		||||
        const file = try std.fs.cwd().openFile(path, .{});
 | 
			
		||||
        defer file.close();
 | 
			
		||||
 | 
			
		||||
        const buf = try file.readToEndAlloc(allocator, try file.getEndPos());
 | 
			
		||||
        defer allocator.free(buf);
 | 
			
		||||
 | 
			
		||||
        @memcpy(system.bus7.bios[0..buf.len], buf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    { // NDS9 BIOS
 | 
			
		||||
        const path = try std.mem.join(allocator, "/", &.{ firm_path, "bios9.bin" });
 | 
			
		||||
        defer allocator.free(path);
 | 
			
		||||
 | 
			
		||||
        const file = try std.fs.cwd().openFile(path, .{});
 | 
			
		||||
        defer file.close();
 | 
			
		||||
 | 
			
		||||
        const buf = try file.readToEndAlloc(allocator, try file.getEndPos());
 | 
			
		||||
        defer allocator.free(buf);
 | 
			
		||||
 | 
			
		||||
        @memcpy(system.bus9.bios[0..buf.len], buf);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const bus_clock = 33513982; // 33.513982 Hz
 | 
			
		||||
const dot_clock = 5585664; //   5.585664 Hz
 | 
			
		||||
const arm7_clock = bus_clock;
 | 
			
		||||
@@ -273,3 +309,27 @@ pub const System = struct {
 | 
			
		||||
        self.bus9.deinit(allocator);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// FIXME: Using Wram.Device here is jank. System should probably carry an Enum + some Generic Type Fns
 | 
			
		||||
pub fn handleInterrupt(comptime dev: Wram.Device, cpu: if (dev == .nds9) *System.Arm946es else *System.Arm7tdmi) void {
 | 
			
		||||
    const Bus = if (dev == .nds9) System.Bus9 else System.Bus7;
 | 
			
		||||
    const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
 | 
			
		||||
 | 
			
		||||
    if (!bus_ptr.io.ime or cpu.cpsr.i.read()) return; // ensure irqs are enabled
 | 
			
		||||
    if ((bus_ptr.io.ie.raw & bus_ptr.io.irq.raw) == 0) return; // ensure there is an irq to handle
 | 
			
		||||
 | 
			
		||||
    // TODO: Handle HALT
 | 
			
		||||
    // HALTCNG (NDS7) and CP15 (NDS9)
 | 
			
		||||
 | 
			
		||||
    const ret_addr = cpu.r[15] - if (cpu.cpsr.t.read()) 0 else @as(u32, 4);
 | 
			
		||||
    const spsr = cpu.cpsr;
 | 
			
		||||
 | 
			
		||||
    cpu.changeMode(.Irq);
 | 
			
		||||
    cpu.cpsr.t.unset();
 | 
			
		||||
    cpu.cpsr.i.set();
 | 
			
		||||
 | 
			
		||||
    cpu.r[14] = ret_addr;
 | 
			
		||||
    cpu.spsr.raw = spsr.raw;
 | 
			
		||||
    cpu.r[15] = if (dev == .nds9) 0xFFFF_0018 else 0x0000_0018;
 | 
			
		||||
    cpu.pipe.reload(cpu);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@ const std = @import("std");
 | 
			
		||||
 | 
			
		||||
const Bitfield = @import("bitfield").Bitfield;
 | 
			
		||||
const Bit = @import("bitfield").Bit;
 | 
			
		||||
const System = @import("emu.zig").System;
 | 
			
		||||
const handleInterrupt = @import("emu.zig").handleInterrupt;
 | 
			
		||||
 | 
			
		||||
const log = std.log.scoped(.shared_io);
 | 
			
		||||
 | 
			
		||||
@@ -10,34 +12,10 @@ const log = std.log.scoped(.shared_io);
 | 
			
		||||
// every other "shared I/O register" is just duplicated on both CPUs. So they shouldn't be here
 | 
			
		||||
 | 
			
		||||
pub const Io = struct {
 | 
			
		||||
    /// Interrupt Master Enable
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ime: bool = false,
 | 
			
		||||
 | 
			
		||||
    /// Interrupt Enable
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable`
 | 
			
		||||
    ie: u32 = 0x0000_0000,
 | 
			
		||||
 | 
			
		||||
    /// IF - Interrupt Request
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest`
 | 
			
		||||
    irq: u32 = 0x0000_0000,
 | 
			
		||||
 | 
			
		||||
    /// Inter Process Communication FIFO
 | 
			
		||||
    ipc_fifo: IpcFifo = .{},
 | 
			
		||||
 | 
			
		||||
    /// Post Boot Flag
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg`
 | 
			
		||||
    post_flg: u8 = @intFromEnum(nds7.PostFlag.in_progress),
 | 
			
		||||
    ipc: Ipc = .{},
 | 
			
		||||
 | 
			
		||||
    wramcnt: WramCnt = .{ .raw = 0x00 },
 | 
			
		||||
 | 
			
		||||
    // TODO: DS Cartridge I/O Ports
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn warn(comptime format: []const u8, args: anytype) u0 {
 | 
			
		||||
@@ -45,13 +23,20 @@ fn warn(comptime format: []const u8, args: anytype) u0 {
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const IpcFifo = struct {
 | 
			
		||||
/// Inter-Process Communication
 | 
			
		||||
const Ipc = struct {
 | 
			
		||||
    const Sync = IpcSync;
 | 
			
		||||
    const Control = IpcFifoCnt;
 | 
			
		||||
 | 
			
		||||
    _nds7: Impl = .{},
 | 
			
		||||
    _nds9: Impl = .{},
 | 
			
		||||
 | 
			
		||||
    // we need access to the CPUs to handle IPC IRQs
 | 
			
		||||
    arm7tdmi: ?*System.Arm7tdmi = null,
 | 
			
		||||
    arm946es: ?*System.Arm946es = null,
 | 
			
		||||
 | 
			
		||||
    // TODO: DS Cartridge I/O Ports
 | 
			
		||||
 | 
			
		||||
    const Source = enum { nds7, nds9 };
 | 
			
		||||
 | 
			
		||||
    const Impl = struct {
 | 
			
		||||
@@ -69,6 +54,11 @@ const IpcFifo = struct {
 | 
			
		||||
        last_read: ?u32 = null,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub fn configure(self: *@This(), system: System) void {
 | 
			
		||||
        self.arm7tdmi = system.arm7tdmi;
 | 
			
		||||
        self.arm946es = system.arm946es;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// IPCSYNC
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    pub fn setIpcSync(self: *@This(), comptime src: Source, value: anytype) void {
 | 
			
		||||
@@ -77,6 +67,13 @@ const IpcFifo = struct {
 | 
			
		||||
                self._nds7.sync.raw = masks.ipcFifoSync(self._nds7.sync.raw, value);
 | 
			
		||||
                self._nds9.sync.raw = masks.mask(self._nds9.sync.raw, (self._nds7.sync.raw >> 8) & 0xF, 0xF);
 | 
			
		||||
 | 
			
		||||
                if (value >> 13 & 1 == 1 and self._nds9.sync.recv_irq.read()) {
 | 
			
		||||
                    const bus: *System.Bus9 = @ptrCast(@alignCast(self.arm946es.?.bus.ptr));
 | 
			
		||||
 | 
			
		||||
                    bus.io.irq.ipcsync.set();
 | 
			
		||||
                    handleInterrupt(.nds9, self.arm946es.?);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (value >> 3 & 1 == 1) {
 | 
			
		||||
                    self._nds7.fifo.reset();
 | 
			
		||||
 | 
			
		||||
@@ -91,6 +88,13 @@ const IpcFifo = struct {
 | 
			
		||||
                self._nds9.sync.raw = masks.ipcFifoSync(self._nds9.sync.raw, value);
 | 
			
		||||
                self._nds7.sync.raw = masks.mask(self._nds7.sync.raw, (self._nds9.sync.raw >> 8) & 0xF, 0xF);
 | 
			
		||||
 | 
			
		||||
                if (value >> 13 & 1 == 1 and self._nds7.sync.recv_irq.read()) {
 | 
			
		||||
                    const bus: *System.Bus7 = @ptrCast(@alignCast(self.arm7tdmi.?.bus.ptr));
 | 
			
		||||
 | 
			
		||||
                    bus.io.irq.ipcsync.set();
 | 
			
		||||
                    handleInterrupt(.nds7, self.arm7tdmi.?);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (value >> 3 & 1 == 1) {
 | 
			
		||||
                    self._nds9.fifo.reset();
 | 
			
		||||
 | 
			
		||||
@@ -271,23 +275,10 @@ pub const masks = struct {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub const nds7 = struct {
 | 
			
		||||
    pub const IntEnable = extern union {
 | 
			
		||||
        raw: u32,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub const IntRequest = IntEnable;
 | 
			
		||||
    pub const PostFlag = enum(u8) { in_progress = 0, completed };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub const nds9 = struct {
 | 
			
		||||
    pub const IntEnable = extern union {
 | 
			
		||||
        raw: u32,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub const IntRequest = IntEnable;
 | 
			
		||||
 | 
			
		||||
    pub const PostFlag = enum(u8) { in_progress = 0, completed };
 | 
			
		||||
// FIXME: bitfields depends on NDS9 / NDS7
 | 
			
		||||
pub const IntEnable = extern union {
 | 
			
		||||
    ipcsync: Bit(u32, 16),
 | 
			
		||||
    raw: u32,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Fifo = struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,16 @@ wram: *[64 * KiB]u8,
 | 
			
		||||
vram: *Vram,
 | 
			
		||||
io: io.Io,
 | 
			
		||||
 | 
			
		||||
bios: *[16 * KiB]u8,
 | 
			
		||||
 | 
			
		||||
pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() {
 | 
			
		||||
    const wram = try allocator.create([64 * KiB]u8);
 | 
			
		||||
    errdefer allocator.destroy(wram);
 | 
			
		||||
    @memset(wram, 0);
 | 
			
		||||
    errdefer allocator.destroy(wram);
 | 
			
		||||
 | 
			
		||||
    const bios = try allocator.create([16 * KiB]u8);
 | 
			
		||||
    @memset(bios, 0);
 | 
			
		||||
    errdefer allocator.destroy(bios);
 | 
			
		||||
 | 
			
		||||
    return .{
 | 
			
		||||
        .main = ctx.main,
 | 
			
		||||
@@ -34,11 +40,14 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This(
 | 
			
		||||
        .wram = wram,
 | 
			
		||||
        .scheduler = scheduler,
 | 
			
		||||
        .io = io.Io.init(ctx.io),
 | 
			
		||||
 | 
			
		||||
        .bios = bios,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
 | 
			
		||||
    allocator.destroy(self.wram);
 | 
			
		||||
    allocator.destroy(self.bios);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn reset(_: *@This()) void {}
 | 
			
		||||
@@ -64,12 +73,13 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return switch (aligned_addr) {
 | 
			
		||||
        0x0000_0000...0x01FF_FFFF => readInt(T, self.bios[address & 0x3FFF ..][0..byte_count]),
 | 
			
		||||
        0x0200_0000...0x02FF_FFFF => readInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count]),
 | 
			
		||||
        0x0300_0000...0x037F_FFFF => switch (self.io.shr.wramcnt.mode.read()) {
 | 
			
		||||
            0b00 => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
 | 
			
		||||
            else => self.shr_wram.read(T, .nds7, aligned_addr),
 | 
			
		||||
        },
 | 
			
		||||
        0x0380_0000...0x0380_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
 | 
			
		||||
        0x0380_0000...0x03FF_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
 | 
			
		||||
        0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
 | 
			
		||||
        0x0600_0000...0x06FF_FFFF => self.vram.read(T, .nds7, aligned_addr),
 | 
			
		||||
        else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }),
 | 
			
		||||
@@ -97,6 +107,7 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (aligned_addr) {
 | 
			
		||||
        0x0000_0000...0x01FF_FFFF => log.err("tried to read from NDS7 BIOS: 0x{X:0>8}", .{aligned_addr}),
 | 
			
		||||
        0x0200_0000...0x02FF_FFFF => writeInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count], value),
 | 
			
		||||
        0x0300_0000...0x037F_FFFF => switch (self.io.shr.wramcnt.mode.read()) {
 | 
			
		||||
            0b00 => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,36 @@ const Bus = @import("Bus.zig");
 | 
			
		||||
const SharedCtx = @import("../emu.zig").SharedCtx;
 | 
			
		||||
const masks = @import("../io.zig").masks;
 | 
			
		||||
 | 
			
		||||
const IntEnable = @import("../io.zig").IntEnable;
 | 
			
		||||
const IntRequest = @import("../io.zig").IntEnable;
 | 
			
		||||
 | 
			
		||||
const log = std.log.scoped(.nds7_io);
 | 
			
		||||
 | 
			
		||||
pub const Io = struct {
 | 
			
		||||
    shr: *SharedCtx.Io,
 | 
			
		||||
 | 
			
		||||
    /// Interrupt Master Enable
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ime: bool = false,
 | 
			
		||||
 | 
			
		||||
    /// Interrupt Enable
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable`
 | 
			
		||||
    ie: IntEnable = .{ .raw = 0x0000_0000 },
 | 
			
		||||
 | 
			
		||||
    /// IF - Interrupt Request
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest`
 | 
			
		||||
    irq: IntRequest = .{ .raw = 0x0000_0000 },
 | 
			
		||||
 | 
			
		||||
    /// Post Boot Flag
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg`
 | 
			
		||||
    postflg: PostFlag = .in_progress,
 | 
			
		||||
 | 
			
		||||
    pub fn init(io: *SharedCtx.Io) @This() {
 | 
			
		||||
        return .{ .shr = io };
 | 
			
		||||
    }
 | 
			
		||||
@@ -20,21 +45,23 @@ pub const Io = struct {
 | 
			
		||||
pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
 | 
			
		||||
    return switch (T) {
 | 
			
		||||
        u32 => switch (address) {
 | 
			
		||||
            0x0400_0208 => @intFromBool(bus.io.shr.ime),
 | 
			
		||||
            0x0400_0210 => bus.io.shr.ie,
 | 
			
		||||
            0x0400_0214 => bus.io.shr.irq,
 | 
			
		||||
            0x0400_0208 => @intFromBool(bus.io.ime),
 | 
			
		||||
            0x0400_0210 => bus.io.ie.raw,
 | 
			
		||||
            0x0400_0214 => bus.io.irq.raw,
 | 
			
		||||
 | 
			
		||||
            0x0410_0000 => bus.io.shr.ipc_fifo.recv(.nds7),
 | 
			
		||||
            0x0410_0000 => bus.io.shr.ipc.recv(.nds7),
 | 
			
		||||
            else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
 | 
			
		||||
        },
 | 
			
		||||
        u16 => switch (address) {
 | 
			
		||||
            0x0400_0180 => @truncate(bus.io.shr.ipc_fifo._nds7.sync.raw),
 | 
			
		||||
            0x0400_0184 => @truncate(bus.io.shr.ipc_fifo._nds7.cnt.raw),
 | 
			
		||||
            0x0400_0180 => @truncate(bus.io.shr.ipc._nds7.sync.raw),
 | 
			
		||||
            0x0400_0184 => @truncate(bus.io.shr.ipc._nds7.cnt.raw),
 | 
			
		||||
            else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
 | 
			
		||||
        },
 | 
			
		||||
        u8 => switch (address) {
 | 
			
		||||
            0x0400_0240 => bus.vram.stat().raw,
 | 
			
		||||
            0x0400_0241 => bus.io.shr.wramcnt.raw,
 | 
			
		||||
 | 
			
		||||
            0x0400_0300 => @intFromEnum(bus.io.postflg),
 | 
			
		||||
            else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
 | 
			
		||||
        },
 | 
			
		||||
        else => @compileError(T ++ " is an unsupported bus read type"),
 | 
			
		||||
@@ -44,19 +71,20 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
 | 
			
		||||
pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
 | 
			
		||||
    switch (T) {
 | 
			
		||||
        u32 => switch (address) {
 | 
			
		||||
            0x0400_0208 => bus.io.shr.ime = value & 1 == 1,
 | 
			
		||||
            0x0400_0210 => bus.io.shr.ie = value,
 | 
			
		||||
            0x0400_0214 => bus.io.shr.irq = value,
 | 
			
		||||
            0x0400_0208 => bus.io.ime = value & 1 == 1,
 | 
			
		||||
            0x0400_0210 => bus.io.ie.raw = value,
 | 
			
		||||
            0x0400_0214 => bus.io.irq.raw &= ~value,
 | 
			
		||||
 | 
			
		||||
            0x0400_0188 => bus.io.shr.ipc_fifo.send(.nds7, value) catch |e| std.debug.panic("FIFO error: {}", .{e}),
 | 
			
		||||
            0x0400_0188 => bus.io.shr.ipc.send(.nds7, value) catch |e| std.debug.panic("FIFO error: {}", .{e}),
 | 
			
		||||
            else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
 | 
			
		||||
        },
 | 
			
		||||
        u16 => switch (address) {
 | 
			
		||||
            0x0400_0180 => bus.io.shr.ipc_fifo.setIpcSync(.nds7, value),
 | 
			
		||||
            0x0400_0184 => bus.io.shr.ipc_fifo.setIpcFifoCnt(.nds7, value),
 | 
			
		||||
            0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds7, value),
 | 
			
		||||
            0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds7, value),
 | 
			
		||||
            else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
 | 
			
		||||
        },
 | 
			
		||||
        u8 => switch (address) {
 | 
			
		||||
            0x0400_0208 => bus.io.ime = value & 1 == 1,
 | 
			
		||||
            else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
 | 
			
		||||
        },
 | 
			
		||||
        else => @compileError(T ++ " is an unsupported bus write type"),
 | 
			
		||||
@@ -73,3 +101,5 @@ pub const Vramstat = extern union {
 | 
			
		||||
    vramd_enabled: Bit(u8, 1),
 | 
			
		||||
    raw: u8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const PostFlag = enum(u8) { in_progress = 0, completed };
 | 
			
		||||
 
 | 
			
		||||
@@ -20,23 +20,32 @@ wram: *Wram,
 | 
			
		||||
io: io.Io,
 | 
			
		||||
ppu: Ppu,
 | 
			
		||||
 | 
			
		||||
bios: *[32 * KiB]u8,
 | 
			
		||||
 | 
			
		||||
scheduler: *Scheduler,
 | 
			
		||||
 | 
			
		||||
pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() {
 | 
			
		||||
    const dots_per_cycle = 3; // ARM946E-S runs twice as fast as the ARM7TDMI
 | 
			
		||||
    scheduler.push(.{ .nds9 = .draw }, 256 * dots_per_cycle);
 | 
			
		||||
 | 
			
		||||
    const bios = try allocator.create([32 * KiB]u8);
 | 
			
		||||
    @memset(bios, 0);
 | 
			
		||||
    errdefer allocator.destroy(bios);
 | 
			
		||||
 | 
			
		||||
    return .{
 | 
			
		||||
        .main = ctx.main,
 | 
			
		||||
        .wram = ctx.wram,
 | 
			
		||||
        .ppu = try Ppu.init(allocator, ctx.vram),
 | 
			
		||||
        .scheduler = scheduler,
 | 
			
		||||
        .io = io.Io.init(ctx.io),
 | 
			
		||||
 | 
			
		||||
        .bios = bios,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
 | 
			
		||||
    self.ppu.deinit(allocator);
 | 
			
		||||
    allocator.destroy(self.bios);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn reset(_: *@This()) void {
 | 
			
		||||
@@ -68,6 +77,7 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T
 | 
			
		||||
        0x0300_0000...0x03FF_FFFF => self.wram.read(T, .nds9, aligned_addr),
 | 
			
		||||
        0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
 | 
			
		||||
        0x0600_0000...0x06FF_FFFF => self.ppu.vram.read(T, .nds9, aligned_addr),
 | 
			
		||||
        0xFFFF_0000...0xFFFF_FFFF => readInt(T, self.bios[address & 0x0000_7FFF ..][0..byte_count]),
 | 
			
		||||
        else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }),
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -97,6 +107,7 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v
 | 
			
		||||
        0x0300_0000...0x03FF_FFFF => self.wram.write(T, .nds9, aligned_addr, value),
 | 
			
		||||
        0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value),
 | 
			
		||||
        0x0600_0000...0x06FF_FFFF => self.ppu.vram.write(T, .nds9, aligned_addr, value),
 | 
			
		||||
        0xFFFF_0000...0xFFFF_FFFF => log.err("tried to read from NDS9 BIOS: 0x{X:0>8}", .{aligned_addr}),
 | 
			
		||||
        else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, aligned_addr }),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,9 @@ const Bus = @import("Bus.zig");
 | 
			
		||||
const SharedCtx = @import("../emu.zig").SharedCtx;
 | 
			
		||||
const masks = @import("../io.zig").masks;
 | 
			
		||||
 | 
			
		||||
const IntEnable = @import("../io.zig").IntEnable;
 | 
			
		||||
const IntRequest = @import("../io.zig").IntEnable;
 | 
			
		||||
 | 
			
		||||
const sext = @import("../../util.zig").sext;
 | 
			
		||||
 | 
			
		||||
const log = std.log.scoped(.nds9_io);
 | 
			
		||||
@@ -14,6 +17,22 @@ const log = std.log.scoped(.nds9_io);
 | 
			
		||||
pub const Io = struct {
 | 
			
		||||
    shr: *SharedCtx.Io,
 | 
			
		||||
 | 
			
		||||
    /// Interrupt Master Enable
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ime: bool = false,
 | 
			
		||||
 | 
			
		||||
    /// Interrupt Enable
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable`
 | 
			
		||||
    ie: IntEnable = .{ .raw = 0x0000_0000 },
 | 
			
		||||
 | 
			
		||||
    /// IF - Interrupt Request
 | 
			
		||||
    /// Read/Write
 | 
			
		||||
    ///
 | 
			
		||||
    /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest`
 | 
			
		||||
    irq: IntRequest = .{ .raw = 0x0000_0000 },
 | 
			
		||||
 | 
			
		||||
    /// POWCNT1 - Graphics Power Control
 | 
			
		||||
    /// Read / Write
 | 
			
		||||
    powcnt: PowCnt = .{ .raw = 0x0000_0000 },
 | 
			
		||||
@@ -33,9 +52,9 @@ pub const Io = struct {
 | 
			
		||||
pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
 | 
			
		||||
    return switch (T) {
 | 
			
		||||
        u32 => switch (address) {
 | 
			
		||||
            0x0400_0208 => @intFromBool(bus.io.shr.ime),
 | 
			
		||||
            0x0400_0210 => bus.io.shr.ie,
 | 
			
		||||
            0x0400_0214 => bus.io.shr.irq,
 | 
			
		||||
            0x0400_0208 => @intFromBool(bus.io.ime),
 | 
			
		||||
            0x0400_0210 => bus.io.ie.raw,
 | 
			
		||||
            0x0400_0214 => bus.io.irq.raw,
 | 
			
		||||
 | 
			
		||||
            0x0400_02A0 => @truncate(bus.io.div.result),
 | 
			
		||||
            0x0400_02A4 => @truncate(bus.io.div.result >> 32),
 | 
			
		||||
@@ -43,15 +62,15 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
 | 
			
		||||
            0x0400_02AC => @truncate(bus.io.div.remainder >> 32),
 | 
			
		||||
            0x0400_02B4 => @truncate(bus.io.sqrt.result),
 | 
			
		||||
 | 
			
		||||
            0x0410_0000 => bus.io.shr.ipc_fifo.recv(.nds9),
 | 
			
		||||
            0x0410_0000 => bus.io.shr.ipc.recv(.nds9),
 | 
			
		||||
            else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
 | 
			
		||||
        },
 | 
			
		||||
        u16 => switch (address) {
 | 
			
		||||
            0x0400_0004 => bus.ppu.io.dispstat.raw,
 | 
			
		||||
            0x0400_0130 => bus.io.keyinput.load(.Monotonic),
 | 
			
		||||
 | 
			
		||||
            0x0400_0180 => @truncate(bus.io.shr.ipc_fifo._nds9.sync.raw),
 | 
			
		||||
            0x0400_0184 => @truncate(bus.io.shr.ipc_fifo._nds9.cnt.raw),
 | 
			
		||||
            0x0400_0180 => @truncate(bus.io.shr.ipc._nds9.sync.raw),
 | 
			
		||||
            0x0400_0184 => @truncate(bus.io.shr.ipc._nds9.cnt.raw),
 | 
			
		||||
 | 
			
		||||
            0x0400_0280 => @truncate(bus.io.div.cnt.raw),
 | 
			
		||||
            0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw),
 | 
			
		||||
@@ -69,9 +88,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
 | 
			
		||||
    switch (T) {
 | 
			
		||||
        u32 => switch (address) {
 | 
			
		||||
            0x0400_0000 => bus.ppu.io.dispcnt_a.raw = value,
 | 
			
		||||
            0x0400_0180 => bus.io.shr.ipc_fifo.setIpcSync(.nds9, value),
 | 
			
		||||
            0x0400_0184 => bus.io.shr.ipc_fifo.setIpcFifoCnt(.nds9, value),
 | 
			
		||||
            0x0400_0188 => bus.io.shr.ipc_fifo.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}),
 | 
			
		||||
            0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds9, value),
 | 
			
		||||
            0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds9, value),
 | 
			
		||||
            0x0400_0188 => bus.io.shr.ipc.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}),
 | 
			
		||||
 | 
			
		||||
            0x0400_0240 => {
 | 
			
		||||
                bus.ppu.vram.io.cnt_a.raw = @truncate(value >> 0); // 0x0400_0240
 | 
			
		||||
@@ -80,9 +99,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
 | 
			
		||||
                bus.ppu.vram.io.cnt_d.raw = @truncate(value >> 24); // 0x0400_0243
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            0x0400_0208 => bus.io.shr.ime = value & 1 == 1,
 | 
			
		||||
            0x0400_0210 => bus.io.shr.ie = value,
 | 
			
		||||
            0x0400_0214 => bus.io.shr.irq = value,
 | 
			
		||||
            0x0400_0208 => bus.io.ime = value & 1 == 1,
 | 
			
		||||
            0x0400_0210 => bus.io.ie.raw = value,
 | 
			
		||||
            0x0400_0214 => bus.io.irq.raw &= ~value,
 | 
			
		||||
 | 
			
		||||
            0x0400_0290 => {
 | 
			
		||||
                bus.io.div.numerator = masks.mask(bus.io.div.numerator, value, 0xFFFF_FFFF);
 | 
			
		||||
@@ -114,9 +133,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
 | 
			
		||||
            else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
 | 
			
		||||
        },
 | 
			
		||||
        u16 => switch (address) {
 | 
			
		||||
            0x0400_0180 => bus.io.shr.ipc_fifo.setIpcSync(.nds9, value),
 | 
			
		||||
            0x0400_0184 => bus.io.shr.ipc_fifo.setIpcFifoCnt(.nds9, value),
 | 
			
		||||
            0x0400_0208 => bus.io.shr.ime = value & 1 == 1,
 | 
			
		||||
            0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds9, value),
 | 
			
		||||
            0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds9, value),
 | 
			
		||||
            0x0400_0208 => bus.io.ime = value & 1 == 1,
 | 
			
		||||
 | 
			
		||||
            0x0400_0280 => {
 | 
			
		||||
                bus.io.div.cnt.raw = value;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user