oboy/src/core/cartridge.ml

116 lines
3.3 KiB
OCaml

(**
* Copyright (c) 2015, Fabien Freling
* All rights reserved.
*
* This source code is licensed under the BSD 2-clause license found in the
* LICENSE file at the top level directory of this source tree.
*)
(** http://bgb.bircd.org/pandocs.htm#thecartridgeheader *)
type memory_bank_controller =
| ROM_ONLY
| MBC1
type t = {
full_rom : bytes;
nintendo_logo : bytes;
title : string;
mem_type : memory_bank_controller;
rom_size : int;
ram_size : int;
(* header_checksum : bytes;
global_checksum : bytes; *)
}
(** The Nintendo logo is a checksum in the header *)
let check_nintendo_logo cartridge =
let reference = Bytes.of_string
"\xCE\xED\x66\x66\xCC\x0D\x00\x0B\x03\x73\x00\x83\x00\x0C\x00\x0D\
\x00\x08\x11\x1F\x88\x89\x00\x0E\xDC\xCC\x6E\xE6\xDD\xDD\xD9\x99\
\xBB\xBB\x67\x63\x6E\x0E\xEC\xCC\xDD\xDC\x99\x9F\xBB\xB9\x33\x3E" in
(Bytes.compare cartridge.nintendo_logo reference) == 0
let get_cartridge_type = function
| 0x00 -> ROM_ONLY
| 0x01 -> MBC1
| x -> Printf.printf "%02X\n" x; failwith "Invalid cartridge type."
let mem_type_name = function
| ROM_ONLY -> "ROM_ONLY"
| MBC1 -> "MBC1"
(** ROM size is expressed in KB *)
let get_ROM_size = function
| 0x00 -> 32 (* no ROM banking *)
| 0x01 -> 64 (* 4 banks *)
| 0x02 -> 128 (* 8 banks *)
| 0x03 -> 256 (* 16 banks *)
| 0x04 -> 512 (* 32 banks *)
| 0x05 -> 1000 (* 64 banks - only 63 banks used by MBC1 *)
| 0x06 -> 2000 (* 128 banks - only 125 banks used by MBC1 *)
| 0x07 -> 4000 (* 256 banks *)
| 0x52 -> 1100 (* 72 banks *)
| 0x53 -> 1200 (* 80 banks *)
| 0x54 -> 1500 (* 96 banks *)
| _ -> failwith "Invalid ROM size code."
(** RAM size is expressed in KB *)
let get_RAM_size = function
| 0x00 -> 0
| 0x01 -> 2
| 0x02 -> 8
| 0x03 -> 32 (* 4 x 8KB banks *)
| _ -> failwith "Invalid RAM size code."
let read_cartridge file =
try
let ic = open_in_bin file in
let file_size = in_channel_length ic in
let full_rom = Bytes.create file_size in
really_input ic full_rom 0 file_size;
close_in ic;
(* Nintendo logo *)
let nin_logo_offset = 0x0104 in
let nin_logo_size = 48 in
let nintendo_logo = Bytes.sub full_rom nin_logo_offset nin_logo_size in
(* Title *)
let title_offset = 0x0134 in
let title_size = 16 in
let title_b = Bytes.sub full_rom title_offset title_size in
let title = Bytes.to_string title_b in
(* Cartridge type - memory bank *)
let mem_type_code = Bytes.get full_rom 0x0147 |> int_of_char in
let mem_type = get_cartridge_type mem_type_code in
(* ROM size *)
let rom_size_code = Bytes.get full_rom 0x0148 |> int_of_char in
let rom_size = get_ROM_size rom_size_code in
(* RAM size *)
let ram_size_code = Bytes.get full_rom 0x0149 |> int_of_char in
let ram_size = get_RAM_size ram_size_code in
Some { full_rom; nintendo_logo; title; mem_type; rom_size; ram_size }
with
| Sys_error msg
| Invalid_argument msg (* This is triggered by Bytes.sub *)
| Failure msg -> prerr_endline msg; None
let print_info cartridge =
let open Printf in
printf "Title: %s\n" cartridge.title;
printf "Mem. type: %s\n" (cartridge.mem_type |> mem_type_name);
printf "ROM size: %iKB\n" cartridge.rom_size;
printf "RAM size: %iKB\n" cartridge.ram_size