A .gitignore => .gitignore +1 -0
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,