(** * 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