M cmd/hdmg/main.ha => cmd/hdmg/main.ha +3 -3
@@ 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,
M dmg/dmg.ha => dmg/dmg.ha +25 -8
@@ 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);
};
A dmg/io.ha => dmg/io.ha +82 -0
@@ 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;
+};
M dmg/mmu.ha => dmg/mmu.ha +5 -5
@@ 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);
};
};