This anaylizes a ROM dump of a Gameboy cartridge and extracts various information. The information is based on the not-quite-official GB Spec, which covers the orginal Gb, Super GB, Color GB, and Pocket GB.

#!/usr/bin/perl -l use strict; use warnings; package My::GbCart; sub new { my ($class, $cart) = @_; bless $cart => $class; } { my $START = 0x0104; my $END = 0x0133; sub nintendo_graphic { my $self = shift; my @cart = @$self; return join '', @cart[$START .. $END]; } } { my $TITLE_START = 0x0134; my $TITLE_END = 0x0142; sub title { my $self = shift; my @cart = @$self; return join '', map( chr, @cart[ $TITLE_START .. $TITLE_END ] ); } } { my $TYPE = 0x0143; my $GBC = 0x0080; sub gbc { my $self = shift; return $self->[$TYPE] == $GBC ? 1 : 0; } } { my $HIGH = 0x0144; my $LOW = 0x0145; sub license_code_new { my $self = shift; return $self->[$HIGH] . $self->[$LOW]; } } { my $IND = 0x0146; my $SGB = 0x0003; sub sbg { my $self = shift; return $self->[$IND] == $SGB ? 1 : 0; } } { my $IND = 0x0147; my %ROM_TYPES = ( 0x00 => 'ROM', 0x01 => 'ROM+MBC1', 0x02 => 'ROM+MBC1+RAM', 0x03 => 'ROM+MBC1+RAM+BATT', 0x05 => 'ROM+MBC2', 0x06 => 'ROM+MBC2+BATT', 0x08 => 'ROM+RAM', 0x09 => 'ROM+RAM+BATT', 0x0B => 'ROM+MMM01', 0x0C => 'ROM+MMM01+SRAM', 0x0D => 'ROM+MMM01+SRAM+BATT', 0x0F => 'ROM+MBC3+TIMER+BATT', 0x10 => 'ROM+MBC3+TIMER+RAM+BATT', 0x11 => 'ROM+MBC3', 0x12 => 'ROM+MBC3+RAM', 0x13 => 'ROM+MBC3+RAM+BATT', 0x19 => 'ROM+MBC5', 0x1A => 'ROM+MBC5+RAM', 0x1B => 'ROM+MBC5+RAM+BATT', 0x1C => 'ROM+MBC5+RUMBLE', 0x1D => 'ROM+MBC5+RUMBLE+SRAM', 0x1E => 'ROM+MBC5+RUMBLE+SRAM+BATT', 0x1F => 'Pocket Camera', 0xFD => 'Bandai TAMA5', 0xFE => 'Hudson HuC-3', 0xFF => 'Hudson HuC-1', ); sub cart_type { my $self = shift; return $ROM_TYPES{ $self->[$IND] }; } } { my $IND = 0x0148; my %SIZE = ( 0 => (32 * 1024), 1 => (64 * 1024), 2 => (128 * 1024), 3 => (256 * 1024), 4 => (512 * 1024), 5 => (1 * 1024 * 1024), 6 => (2 * 1024 * 1024), 0x52 => (1.1 * 1024 * 1024), 0x53 => (1.2 * 1024 * 1024), 0x54 => (1.5 * 1024 * 1024), ); sub rom_size { my $self = shift; return $SIZE{ $self->[$IND] }; } } { my $IND = 0x0149; my %SIZE = ( 0 => 0, 1 => 2 * 1024, 2 => 8 * 1024, 3 => 32 * 1024, 4 => 128 * 1024, ); sub ram_size { my $self = shift; return $SIZE{ $self->[$IND] }; } } { my $IND = 0x014A; sub destination_code { my $self = shift; return $self->[$IND]; } } { my $IND = 0x014B; my %LICENSESE = ( 0x33 => '', 0x79 => 'Accolade', 0xA4 => 'Konami', ); sub license_code_old { my $self = shift; return $LICENSESE{ $self->[$IND] } || ''; } } { my $IND = 0x014C; sub mask_version_number { my $self = shift; return $self->[$IND]; } } { my $IND = 0x014D; sub complement_check { my $self = shift; return $self->[$IND]; } } { my $START = 0x014E; my $END = 0x014F; sub checksum { my $self = shift; return ( $self->[$START] << 8 ) | $self->[$END]; } sub checksum_ok { my $self = shift; my @self = @$self; my @to_check = ( @self[ 0 .. $START - 1 ], @self[ $END + 1 .. $#self ], ); use List::Util 'sum'; my $checksum = 0xFFFF & sum @to_check; return $checksum == $self->checksum ? 1 : 0; } } { my $START = 0x0134; my $END = 0x014D; my $ADD = 25; sub powerup_check { my $self = shift; my @self = @$self; use List::Util 'sum'; my $check = 0xFF & ( sum ( @self[ $START .. $END ]) + $ADD ); return $check == 0 ? 1 : 0; } } package main; die "Need a cartridge to read\n" unless @ARGV; my $CART = join '', <>; sub build_array { my $data = shift; return [ unpack "C*", $data ]; } { my $cart = My::GbCart->new( build_array( $CART ) ); print "Nintendo Graphic: " . $cart->nintendo_graphic; print "Title: " . $cart->title; print "GameBoy Color: " . ($cart->gbc ? "Yes" : "No"); print "Licensee Code (old): " . ($cart->license_code_old eq '' ? "Check new license code" : $cart->license_code_old); print "Licensee Code (new): " . $cart->license_code_new; print "Super GameBoy: " . ($cart->sbg ? "Yes" : "No"); print "Cartridge Type: " . $cart->cart_type; print "ROM Size (bytes): " . $cart->rom_size; print "RAM Size (bytes): " . $cart->ram_size; print "Destination Code: " . ($cart->destination_code ? "Japanese" : "Non-Japanese"); print "Mask: " . $cart->mask_version_number; print "Complement Check: " . $cart->complement_check; print "Checksum: " . $cart->checksum . ' (' . ( $cart->checksum_ok ? 'OK' : 'NOT OK') . ')'; print "Powerup Check: " . ( $cart->powerup_check ? 'OK' : 'NOT OK' ); }

"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.


In reply to View Gameboy Cartridge Information by hardburn

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.