From 819c1589ce3408e0a3a906cf98bf614d49085ce2 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 7 Aug 2022 16:28:47 +0200 Subject: [PATCH] Rig up I/O subsystem I am not in love with this design --- cmd/hdmg/main.ha | 6 ++-- dmg/dmg.ha | 33 ++++++++++++++----- dmg/io.ha | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ dmg/mmu.ha | 10 +++--- 4 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 dmg/io.ha diff --git a/cmd/hdmg/main.ha b/cmd/hdmg/main.ha index 024bb18..eb9620e 100644 --- a/cmd/hdmg/main.ha +++ b/cmd/hdmg/main.ha @@ -31,12 +31,12 @@ export fn main() void = { fmt::println(cart.title)!; const dmg = dmg::init(cart); - defer dmg::finish(&dmg); + defer dmg::dmg_free(dmg); sdl2::SDL_Init(sdl2::SDL_INIT_VIDEO)!; defer sdl2::SDL_Quit(); - const win = sdl2::SDL_CreateWindow("hdmg", + const win = sdl2::SDL_CreateWindow(cart.title, sdl2::SDL_WINDOWPOS_UNDEFINED, sdl2::SDL_WINDOWPOS_UNDEFINED, 640, 576, SDL_WindowFlags::NONE)!; defer sdl2::SDL_DestroyWindow(win); @@ -51,7 +51,7 @@ export fn main() void = { let state = state { quit = false, - dmg = &dmg, + dmg = dmg, prev = time::now(time::clock::MONOTONIC), window = win, render = render, diff --git a/dmg/dmg.ha b/dmg/dmg.ha index 6de5ab8..dfb83a0 100644 --- a/dmg/dmg.ha +++ b/dmg/dmg.ha @@ -6,30 +6,47 @@ export def CLOCK_HZ: i64 = 4194304; export type dmg = struct { cart: *cart::cart, - cpu: sm83::sm83, + cpu: *sm83::sm83, mmu: *mmu, + io: *io, + + // I/O devices + cpu_ie: cpudev, + cpu_if: cpudev, }; // Initializes a new DMG emulator. -export fn init(cart: *cart::cart) dmg = { - let mmu = new_mmu(cart); - let cpu = sm83::init(mmu); +export fn init(cart: *cart::cart) *dmg = { + let io = io_new(); + let mmu = new_mmu(cart, io); + let cpu = alloc(sm83::init(mmu)); // XXX: Other power-on registers? From which BIOS model? cpu.regs.PC = 0x100; cpu.regs.SP = 0xFFFE; - return dmg { + + let dmg = alloc(dmg { cart = cart, cpu = cpu, mmu = mmu, - }; + io = io, + cpu_ie = cpudev_ie_new(cpu), + cpu_if = cpudev_if_new(cpu), + }); + + io_mount(dmg.io, 0xFF0F, &dmg.cpu_if); + io_mount(dmg.io, 0xFFFF, &dmg.cpu_ie); + return dmg; }; // Frees state associated with a [[dmg]]. -export fn finish(dmg: *dmg) void = { +export fn dmg_free(dmg: *dmg) void = { mmu_free(dmg.mmu); + free(dmg.cpu); + free(dmg.io); + free(dmg); }; // Executes the [[dmg]] CPU for a given number of cycles. export fn exec(dmg: *dmg, cycles: uint) void = { - sm83::exec(&dmg.cpu, cycles); + sm83::exec(dmg.cpu, cycles); }; diff --git a/dmg/io.ha b/dmg/io.ha new file mode 100644 index 0000000..6293643 --- /dev/null +++ b/dmg/io.ha @@ -0,0 +1,82 @@ +use sm83; + +export type io = struct { + tab: [0x100]*io_device, +}; + +export type io_device = struct { + readb: *fn(iodev: *io_device, addr: u16) u8, + writeb: *fn(iodev: *io_device, addr: u16, b: u8) void, +}; + +const nulldev: io_device = io_device { + readb = &nulldev_readb, + writeb = &nulldev_writeb, +}; + +fn nulldev_readb(iodev: *io_device, addr: u16) u8 = 0; +fn nulldev_writeb(iodev: *io_device, addr: u16, b: u8) void = void; + +// Creates a new MMIO controller. +export fn io_new() *io = { + return alloc(io { + tab = [&nulldev...], + }); +}; + +// Mounts an I/O device at the given address. +export fn io_mount(io: *io, addr: u16, dev: *io_device) void = { + assert(addr & 0xFF00 == 0xFF00); + io.tab[addr & 0xFF] = dev; +}; + +fn io_readb(io: *io, addr: u16) u8 = { + const dev = io.tab[addr >> 8]; + return dev.readb(dev, addr); +}; + +fn io_writeb(io: *io, addr: u16, b: u8) void = { + const dev = io.tab[addr >> 8]; + dev.writeb(dev, addr, b); +}; + +export type cpudev = struct { + io_device, + cpu: *sm83::sm83, +}; + +fn cpudev_ie_new(cpu: *sm83::sm83) cpudev = { + return cpudev { + readb = &ie_readb, + writeb = &ie_writeb, + cpu = cpu, + }; +}; + +fn ie_readb(iodev: *io_device, addr: u16) u8 = { + const iodev = iodev: *cpudev; + return iodev.cpu.int_ie; +}; + +fn ie_writeb(iodev: *io_device, addr: u16, b: u8) void = { + const iodev = iodev: *cpudev; + iodev.cpu.int_ie = b; +}; + +fn cpudev_if_new(cpu: *sm83::sm83) cpudev = { + return cpudev { + readb = &if_readb, + writeb = &if_writeb, + cpu = cpu, + }; +}; + +fn if_readb(iodev: *io_device, addr: u16) u8 = { + const iodev = iodev: *cpudev; + return iodev.cpu.int_if; +}; + +fn if_writeb(iodev: *io_device, addr: u16, b: u8) void = { + const iodev = iodev: *cpudev; + iodev.cpu.int_if = b; +}; diff --git a/dmg/mmu.ha b/dmg/mmu.ha index 6eb469c..f7ba16f 100644 --- a/dmg/mmu.ha +++ b/dmg/mmu.ha @@ -2,11 +2,11 @@ // or something. Dunno. use dmg::cart; use sm83; -use fmt; // XXX TEMP export type mmu = struct { sm83::mmu, cart: *cart::cart, + io: *io, vram: [0x2000]u8, ram0: [0x1000]u8, ram1: [0x1000]u8, @@ -15,13 +15,14 @@ export type mmu = struct { }; // Creates a new DMG MMU. -export fn new_mmu(cart: *cart::cart) *mmu = { +export fn new_mmu(cart: *cart::cart, io: *io) *mmu = { let mmu = alloc(mmu { readb = &mmu_readb, readw = &mmu_readw, writeb = &mmu_writeb, writew = &mmu_writew, cart = cart, + io = io, ... }); return mmu; @@ -60,8 +61,7 @@ fn mmu_readb(mem: *sm83::mmu, addr: u16) u8 = { }; if (addr >= 0xFF00) { - // TODO: I/O - return 0; + return io_readb(mem.io, addr); }; // "Unusable" memory @@ -94,7 +94,7 @@ fn mmu_writeb(mem: *sm83::mmu, addr: u16, b: u8) void = { } else if (addr >= 0xFF80 && addr < 0xFFFF) { mem.ram2[addr - 0xFF80] = b; } else if (addr >= 0xFF00) { - void; // TODO: I/O + io_writeb(mem.io, addr, b); }; }; -- 2.45.2