~sircmpwn/hdmg

819c1589ce3408e0a3a906cf98bf614d49085ce2 — Drew DeVault 1 year, 4 months ago c3847ed master
Rig up I/O subsystem

I am not in love with this design
4 files changed, 115 insertions(+), 16 deletions(-)

M cmd/hdmg/main.ha
M dmg/dmg.ha
A dmg/io.ha
M dmg/mmu.ha
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);
	};
};