From 469e118f14c8ffa800ece3bd238185f8d7b213fe Mon Sep 17 00:00:00 2001 From: Romain Beauxis Date: Tue, 6 Jan 2026 09:37:33 -0600 Subject: [PATCH] Implement Memory Bank Controller 2 (MBC2). --- lib/cartridge/detect_cartridge.ml | 5 +- lib/cartridge/mbc2.ml | 84 +++++++++++++++++ lib/cartridge/mbc2.mli | 1 + resource/test_roms/mooneye/mbc2/bits_ramg.gb | Bin 0 -> 32768 bytes resource/test_roms/mooneye/mbc2/bits_romb.gb | Bin 0 -> 65536 bytes .../test_roms/mooneye/mbc2/bits_unused.gb | Bin 0 -> 262144 bytes resource/test_roms/mooneye/mbc2/ram.gb | Bin 0 -> 32768 bytes resource/test_roms/mooneye/mbc2/rom_1Mb.gb | Bin 0 -> 131072 bytes resource/test_roms/mooneye/mbc2/rom_2Mb.gb | Bin 0 -> 262144 bytes resource/test_roms/mooneye/mbc2/rom_512kb.gb | Bin 0 -> 65536 bytes test/rom_test/mooneye/mooneye_utils.ml | 41 ++++----- test/rom_test/mooneye/test_mooneye_mbc2.ml | 86 ++++++++++++++++++ 12 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 lib/cartridge/mbc2.ml create mode 100644 lib/cartridge/mbc2.mli create mode 100644 resource/test_roms/mooneye/mbc2/bits_ramg.gb create mode 100644 resource/test_roms/mooneye/mbc2/bits_romb.gb create mode 100644 resource/test_roms/mooneye/mbc2/bits_unused.gb create mode 100644 resource/test_roms/mooneye/mbc2/ram.gb create mode 100644 resource/test_roms/mooneye/mbc2/rom_1Mb.gb create mode 100644 resource/test_roms/mooneye/mbc2/rom_2Mb.gb create mode 100644 resource/test_roms/mooneye/mbc2/rom_512kb.gb create mode 100644 test/rom_test/mooneye/test_mooneye_mbc2.ml diff --git a/lib/cartridge/detect_cartridge.ml b/lib/cartridge/detect_cartridge.ml index e61bbb5..83b53f8 100644 --- a/lib/cartridge/detect_cartridge.ml +++ b/lib/cartridge/detect_cartridge.ml @@ -8,4 +8,7 @@ let f ~rom_bytes = | MBC1 | MBC1_RAM | MBC1_RAM_BATTERY -> (module Mbc1 : Cartridge_intf.S) - | _ -> assert false + | MBC2 + | MBC2_BATTERY -> (module Mbc2 : Cartridge_intf.S) + | other -> + failwith (Printf.sprintf "Unsupported cartridge type: %s" (Cartridge_type.show other)) diff --git a/lib/cartridge/mbc2.ml b/lib/cartridge/mbc2.ml new file mode 100644 index 0000000..f8d61fe --- /dev/null +++ b/lib/cartridge/mbc2.ml @@ -0,0 +1,84 @@ +(* MBC2 - Memory Bank Controller 2 + + Features: + - Up to 256KB ROM (16 banks) + - Built-in 512×4 bits RAM (only lower nibble used) + - RAM at 0xA000-0xA1FF (mirrored in 0xA200-0xBFFF) + + Register writes: + - 0x0000-0x3FFF with bit 8 = 0: RAM enable (0x0A enables) + - 0x0000-0x3FFF with bit 8 = 1: ROM bank number (4 bits, 0→1) + + Reference: https://gbdev.io/pandocs/MBC2.html *) + +open Uints + +type t = { + rom_bytes : Bigstringaf.t; + ram_bytes : Bigstringaf.t; + rom_bank_count : int; + mutable ram_enabled : bool; + mutable rom_bank_num : int; +} + +let create ~rom_bytes = + let h = Cartridge_header.create ~rom_bytes in + let rom_bank_count = Cartridge_header.get_rom_bank_count h in + (* MBC2 has built-in 512×4 bit RAM *) + let ram_bytes = Bigstringaf.create 512 in + { + rom_bytes; + ram_bytes; + rom_bank_count; + ram_enabled = false; + rom_bank_num = 1; + } + +let read_byte t addr = + let addr = Uint16.to_int addr in + match addr with + | _ when addr <= 0x3FFF -> + Bigstringaf.unsafe_get t.rom_bytes addr + |> Uint8.of_char + | _ when addr <= 0x7FFF -> + let offset = 0x4000 * t.rom_bank_num + (addr - 0x4000) in + Bigstringaf.unsafe_get t.rom_bytes offset + |> Uint8.of_char + | _ when 0xA000 <= addr && addr <= 0xBFFF -> + (* RAM - 512 bytes mirrored, only lower 4 bits valid + This memory is at A000–A1FF and mirrored at + A200–BFFF hence the 0x1FF below. *) + if t.ram_enabled then + let ram_addr = (addr - 0xA000) land 0x1FF in + let value = Bigstringaf.unsafe_get t.ram_bytes ram_addr |> Char.code in + Uint8.of_int (value lor 0xF0) (* Upper 4 bits read as 1 *) + else + Uint8.of_int 0xFF + | _ -> assert false + +let write_byte t ~addr ~data = + let addr = Uint16.to_int addr in + let data = Uint8.to_int data in + match addr with + | _ when addr <= 0x3FFF -> + if addr land 0x100 = 0 then + t.ram_enabled <- (data land 0x0F) = 0x0A + else begin + let bank = data land 0x0F in + let bank = if bank = 0 then 1 else bank in + t.rom_bank_num <- bank mod t.rom_bank_count + end + | _ when 0x4000 <= addr && addr <= 0x7FFF -> + (* Writes to this area do nothing on MBC2 *) + () + | _ when 0xA000 <= addr && addr <= 0xBFFF -> + (* RAM - only lower 4 bits written *) + if t.ram_enabled then begin + let ram_addr = (addr - 0xA000) land 0x1FF in + Bigstringaf.unsafe_set t.ram_bytes ram_addr (Char.unsafe_chr (data land 0x0F)) + end + | _ -> assert false + +let accepts _ addr = + let addr = Uint16.to_int addr in + (addr <= 0x7FFF) || (0xA000 <= addr && addr <= 0xBFFF) diff --git a/lib/cartridge/mbc2.mli b/lib/cartridge/mbc2.mli new file mode 100644 index 0000000..c3c85ec --- /dev/null +++ b/lib/cartridge/mbc2.mli @@ -0,0 +1 @@ +include Cartridge_intf.S diff --git a/resource/test_roms/mooneye/mbc2/bits_ramg.gb b/resource/test_roms/mooneye/mbc2/bits_ramg.gb new file mode 100644 index 0000000000000000000000000000000000000000..887786c8bd848594d5c7269708335045f8be72b2 GIT binary patch literal 32768 zcmeI4U1$_n6vyw(ZsNH4a8_-cq=U|Gmvl{2iEA)xSjWk9NOsn=hR`5{K;y=&3T+b5 zhc!;tO@h#mhxQ?b3IPQld<;_hPz6C2C5vv+f|eGA@xf4vE!&!43O3t&_Ga8Q^rdg2 z=MOV`&pqd!d+zVv8}c-T!s{)P>EV9(J(0LoL#kZI$uy}ZHAL`k`}8wXd;iwR!h;7t zpS^PB$k0gb<695zpFMZw`(5fMqoX6kCx_oX5?4+PA3s6*`$;7s+%EF=fr_2PRz1HF z;v?rdk$8XQa=w^vy5RlMWkP@%IznyZDg=c@S-E~%PX1egiC-Kh{^zLj|ib)cOj(t3Qm_XXk zd6vAl<9%gh^n`Nw*zrSgaMtPBC%(S_H+XpD`PlqqZc8kmrZ1Xiwk%DkUHR|1uDuwv zw$ME>I;?9i$xqRow79iPMjHtr0VIF~kN^@u0!RP}AOR$R1dsp{Kmter2_OL^fCP{L z5MTj6ke~_x&4?UK}9_SZ@ty_aZS@w7$ z5g0KzI5TtfXkFd*?UnQ!2!zP8LRDYi*jQiB;#pp3g9JeoAwm>cKm59>pdb8AO|q=2 zvfR`J76=MdRd_Soc{?M(jzpZ^(l3kWd6rvIG!1-`6bR^4fq*1|EeMhXxdlO%!Hz_9 z-R+hnMM)LUzhmn(=3bU9e*SOHq5}{Q>ho{hkWoje%4l1o-eI| zWiGAPu^FMtN=X_VlqAL*YKBT#wO}zH#DEnJ8wRY05H=cCXc)7zGcyANiXzJrRUp8s zfcRw6FyMi=Gd@ja7~!zv2e0F|EKN%$H7d&jf5~RNVg7V_cGejWD^I21t?Llab{6=n zD%i3Nr@`%Z;teAj?dcgCi$)Ct{4f&}TX9Fn&`@8WX?9S7U)SUD-riWOvyRw!Ux}ZVQO<*EDu_+-|5A_P028{rW2A7sa*r7otEcpL6e6 zAG{+Go>$e*&h~b&fDD}9czj?0Y!JKCZ2dfys_MEE&u$Fd0_Xfnk|>75A{84=NNa0% zw_&uly4`Fw;V|$C1i%kxdlhH#euM4YNI@7IgOvvY!C*^Eb2G$4UM9vD`Vxu$exMF* z(2gCU5NtZLScT61K;OVXJg)0-QZ)_i_I5Zea0mG!)8^(-2<8PJTfHPf?aUU%NCYY< z%@1=mHa0XcKb*nR-xuhcr0VZaBuaib1LZb51ER?8Ec|7KSs-4PMX`lS6d8Yqm2+ZX z|EsA4!Kn{Mg+krk@C&hYIlZ8H}g41}dZ2G+(uED(iD7PyX zUZ2Z%<-Ra;k4+!>CVLmU-UMDN>X(#Octa6dv0ZH48NaC1R=R2{>V&N|jc@GmOMjdqs5=~iD@sAdGo!Zy zSszJrM48Jp#pY~2&o4~Qw`G@Zw&iT|&x~zCI{&6B>FGh*8uwR8%X#D8`F+`Ixo^$u z_)VRc>YsTm1_>YmB!C2v01`j~NB{{S0VIF~kN^@u0!RP}AOR$R1dsp{Kmter2_OL^ tfCP{L5P0&&Wko23)pV_PxVB)y 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 z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U vAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7e?e;4={kcWca literal 0 HcmV?d00001 diff --git a/resource/test_roms/mooneye/mbc2/ram.gb b/resource/test_roms/mooneye/mbc2/ram.gb new file mode 100644 index 0000000000000000000000000000000000000000..e570e1c0a4f9356557376c65f9ef7cdd0106f0b5 GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/test/rom_test/mooneye/mooneye_utils.ml b/test/rom_test/mooneye/mooneye_utils.ml index d5b6bd5..d7d1257 100644 --- a/test/rom_test/mooneye/mooneye_utils.ml +++ b/test/rom_test/mooneye/mooneye_utils.ml @@ -12,28 +12,25 @@ module Make (Cartridge : Cartridge_intf.S) = struct let run_result = Camlboy.run_instruction camlboy in let prev_instr = Camlboy.For_tests.prev_inst camlboy in match prev_instr, run_result with - | JR (None, i8), Frame_ended famebuffer -> - if Int8.to_int i8 <> -3 then - loop () - else begin - Printf.printf "%s\n" @@ Camlboy.show camlboy; - famebuffer - |> Array.iteri (fun i row -> - if row |> Array.for_all (fun color -> color = `White) then - () - else begin - let show_color = function - | `Black -> '#' - | `Dark_gray -> '@' - | `Light_gray -> 'x' - | `White -> '-' - in - Printf.printf "%03d:" i; - row |> Array.iter (fun color -> show_color color |> print_char); - print_newline () - end - ) - end + | JR (None, i8), Frame_ended famebuffer + when List.mem (Int8.to_int i8) [-2; -3] -> + Printf.printf "%s\n" @@ Camlboy.show camlboy; + famebuffer + |> Array.iteri (fun i row -> + if row |> Array.for_all (fun color -> color = `White) then + () + else begin + let show_color = function + | `Black -> '#' + | `Dark_gray -> '@' + | `Light_gray -> 'x' + | `White -> '-' + in + Printf.printf "%03d:" i; + row |> Array.iter (fun color -> show_color color |> print_char); + print_newline () + end + ) | _, _ -> loop () in loop () diff --git a/test/rom_test/mooneye/test_mooneye_mbc2.ml b/test/rom_test/mooneye/test_mooneye_mbc2.ml new file mode 100644 index 0000000..6810268 --- /dev/null +++ b/test/rom_test/mooneye/test_mooneye_mbc2.ml @@ -0,0 +1,86 @@ +open Camlboy_lib +module M = Mooneye_utils.Make(Mbc2) + +let%expect_test "bits_ramg.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/bits_ramg.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$4989 + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}] + +let%expect_test "bits_romb.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/bits_romb.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$4879 + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}] + +let%expect_test "bits_unused.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/bits_unused.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$4089 + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}] + +let%expect_test "ram.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/ram.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$4879 + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}] + +let%expect_test "rom_512kb.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/rom_512kb.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$487A + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}] + +let%expect_test "rom_1Mb.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/rom_1Mb.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$487A + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}] + +let%expect_test "rom_2Mb.gb" = + M.run_test_rom_and_print_framebuffer "mbc2/rom_2Mb.gb"; + + [%expect{| + A:$00 F:ZN-C BC:$0305 DE:$080D HL:$1522 SP:$E000 PC:$487A + 008:-#######-----------------------------------###---#---#---------------------------------------------------------------------------------------------------------- + 009:----#-----####-----###-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 010:----#----#----#---#-------####------------#---#--#-#------------------------------------------------------------------------------------------------------------ + 011:----#----######----##------#--------------#---#--###------------------------------------------------------------------------------------------------------------ + 012:----#----#-----------#-----#--------------#---#--#--#----------------------------------------------------------------------------------------------------------- + 013:----#-----####----###------##--------------###---#---#---------------------------------------------------------------------------------------------------------- |}]