~sircmpwn/hdmg

76de8348704bbb1606c9857fb739cd6ade7c055b — Drew DeVault 1 year, 6 months ago e6f5293
dmg::cart: initial commit
7 files changed, 159 insertions(+), 7 deletions(-)

A .gitignore
A cmd/hdmg/main.ha
D dmg/cart.ha
A dmg/cart/cart.ha
A dmg/cart/mbc0.ha
A dmg/cart/ram.ha
M dmg/mmu.ha
A .gitignore => .gitignore +1 -0
@@ 0,0 1,1 @@
*.gb

A cmd/hdmg/main.ha => cmd/hdmg/main.ha +12 -0
@@ 0,0 1,12 @@
use dmg;
use dmg::cart;
use fmt;
use io;
use os;

export fn main() void = {
	const file = os::open(os::args[1])!;
	const cart = cart::load(file)!;
	defer cart::cart_free(cart);
	fmt::println(cart.title)!;
};

D dmg/cart.ha => dmg/cart.ha +0 -5
@@ 1,5 0,0 @@
use sm83;

export type cart = struct {
	sm83::mmu,
};

A dmg/cart/cart.ha => dmg/cart/cart.ha +96 -0
@@ 0,0 1,96 @@
use io;
use sm83;
use strings;

// Returned when we are unable to load this cartridge.
export type invalid_cart = !void;

def CARTHDR_SIZE: size = 0x150;

// Cartridge type.
export type cart_type = enum u8 {
	MBC0 = 0x00,
	MBC1 = 0x01,
	MBC1_RAM = 0x02,
	MBC1_RAM_BATTERY = 0x03,
	MBC2 = 0x05,
	MBC2_BATTERY = 0x06,
	MBC0_RAM = 0x08,
	MBC0_RAM_BATTERY = 0x09,
	MMM01 = 0x0B,
	MMM01_RAM = 0x0C,
	MMM01_RAM_BATTERY = 0x0D,
	MBC3_TIMER_BATTERY = 0x0F,
	MBC3_TIMER_RAM_BATTERY = 0x10,
	MBC3 = 0x11,
	MBC3_RAM = 0x12,
	MBC3_RAM_BATTERY = 0x13,
	MBC5 = 0x19,
	MBC5_RAM = 0x1A,
	MBC5_RAM_BATTERY = 0x1B,
	MBC5_RUMBLE = 0x1C,
	MBC5_RUMBLE_RAM = 0x1D,
	MBC5_RUMBLE_RAM_BATTERY = 0x1E,
	MBC6 = 0x20,
	MBC7_SENSOR_RUMBLE_RAM_BATTERY = 0x22,
	POCKET_CAMERA = 0xFC,
	BANDAI_TAMA5 = 0xFD,
	HUC3 = 0xFE,
	HUC1_RAM_BATTERY = 0xFF,
};

export type cart = struct {
	sm83::mmu,
	load: *fn(cart: *cart, in: io::file) (void | io::error),

	title: str,
	cart_type: cart_type,
	rom_size: u32,
	ram_size: u32,

	ram: [][16384]u8,
};

// Loads a [[cart]] from a file.
export fn load(in: io::file) (*cart | invalid_cart | io::error) = {
	let header: [CARTHDR_SIZE]u8 = [0...];
	io::readall(in, header)?;

	const ctype = header[0x147];
	let cart = cart_init(ctype: cart_type)?;
	cart.rom_size = (1u32 << header[0x148]) * 32768;
	cart.ram_size = 16384 * (switch (header[0x149]) {
	case 0x00 =>
		yield 0u32;
	case 0x02 =>
		yield 1u32;
	case 0x03 =>
		yield 4u32;
	case 0x04 =>
		yield 16u32;
	case 0x05 =>
		yield 8u32;
	case =>
		return invalid_cart;
	});
	const title = header[0x134..0x144];
	cart.title = strings::dup(strings::rtrim(strings::fromutf8(title), '\0'));

	io::seek(in, 0, io::whence::SET)?;
	cart.load(cart, in)?;
	return cart;
};

fn cart_init(ctype: cart_type) (*cart | invalid_cart) = {
	switch (ctype) {
	case cart_type::MBC0 =>
		return new_mbc0();
	case =>
		return invalid_cart;
	};
};

// Frees state associated with a cartridge.
export fn cart_free(cart: *cart) void = {
	free(cart);
};

A dmg/cart/mbc0.ha => dmg/cart/mbc0.ha +44 -0
@@ 0,0 1,44 @@
use io;
use sm83;

export type mbc0 = struct {
	cart,
	rom0: [32768]u8,
};

// Initializes a new MBC0 ROM (32 KiB ROM, 0 KiB RAM).
export fn new_mbc0() *cart = {
	return alloc(mbc0 {
		readb = &mbc0_readb,
		readw = &mbc0_readw,
		writeb = &mbc0_writeb,
		writew = &mbc0_writew,
		load = &mbc0_load,
		...
	});
};

fn mbc0_load(cart: *cart, in: io::file) (void | io::error) = {
	const mbc0 = cart: *mbc0;
	io::readall(in, mbc0.rom0)?;
};

fn mbc0_readb(mmu: *sm83::mmu, addr: u16) u8 = {
	const mbc0 = mmu: *mbc0;
	return mbc0.rom0[addr];
};

fn mbc0_readw(mmu: *sm83::mmu, addr: u16) u16 = {
	const mbc0 = mmu: *mbc0;
	const low: u16 = mbc0.rom0[addr];
	const high: u16 = mbc0.rom0[addr + 1];
	return low | (high << 8);
};

fn mbc0_writeb(mmu: *sm83::mmu, addr: u16, b: u8) void = {
	void;
};

fn mbc0_writew(mmu: *sm83::mmu, addr: u16, w: u16) void = {
	void;
};

A dmg/cart/ram.ha => dmg/cart/ram.ha +3 -0
@@ 0,0 1,3 @@
export type ram = struct {
	banks: [][16384]u8,
};

M dmg/mmu.ha => dmg/mmu.ha +3 -2
@@ 1,10 1,11 @@
// XXX: These if statement chains might be better implemented as a mapping table
// or something. Dunno.
use dmg::cart;
use sm83;

export type mmu = struct {
	sm83::mmu,
	cart: *cart,
	cart: *cart::cart,
	vram: [0x2000]u8,
	ram0: [0x1000]u8,
	ram1: [0x1000]u8,


@@ 13,7 14,7 @@ export type mmu = struct {
};

// Creates a new DMG MMU.
export fn new_mmu(cart: *cart) *mmu = {
export fn new_mmu(cart: *cart::cart) *mmu = {
	let mmu = alloc(mmu {
		readb = &mmu_readb,
		readw = &mmu_readw,