All pastes #631656 Raw Edit

ploximeth

public text v1 · immutable
#631656 ·published 2007-07-24 05:12 UTC
rendered paste body
#!/usr/bin/perl 
#use SDL; 
#use SDL::App;
#use SDL::Event;
use File::Basename;
my $ticks;
$SIG{TSTP} = "dump_regs";
our @RAM;     # an array of 8192 bytes, 1d
our @VRAM;    # see above
our @SPRITES; # 40 maximum sprites at one time
our @SNDBUF;  # 4 8192*4 audio buffers
our $CLK_SGB = "4.194304"; # MHz 
our $CLK_GBC = "8.388";    # MHz
our $NINTENDO_LOGO  = chr(hex(0xCE)).chr(hex(0xED)).chr(hex(0x66)).chr(hex(0x66)).chr(hex(0xCC)).chr(hex(0x0D)).chr(hex(0x00)).chr(hex(0x0B)).chr(hex(0x03)).chr(hex(0x73)).chr(hex(0x00)).chr(hex(0x83)).chr(hex(0x00)).chr(hex(0x0C)).chr(hex(0x00)).chr(hex(0x0D));
$NINTENDO_LOGO .= chr(hex(0x00)).chr(hex(0x08)).chr(hex(0x11)).chr(hex(0x1F)).chr(hex(0x88)).chr(hex(0x89)).chr(hex(0x00)).chr(hex(0x0E)).chr(hex(0xDC)).chr(hex(0xCC)).chr(hex(0x6E)).chr(hex(0xE6)).chr(hex(0xDD)).chr(hex(0xDD)).chr(hex(0xD9)).chr(hex(0x99));
$NINTENDO_LOGO .= chr(hex(0xBB)).chr(hex(0xBB)).chr(hex(0x67)).chr(hex(0x63)).chr(hex(0x6E)).chr(hex(0x0E)).chr(hex(0xEC)).chr(hex(0xCC)).chr(hex(0xDD)).chr(hex(0xDC)).chr(hex(0x99)).chr(hex(0x9F)).chr(hex(0xBB)).chr(hex(0xB9)).chr(hex(0x33)).chr(hex(0x3E));

# Generate the opcode table.

our %OPCODES;

# The gameboy color's screen is 160x144.
#my $app = SDL::App->new(-width  => 160, -height => 144, -depth  => 16) or die("Failed to create screen\n");
#my $event = SDL::Event->new();
my $filename = shift || die("Parameters: ".basename($0)." <rom file>\n");
open(ROM, $filename) or die("Failed to open $filename: $!\n");
my @rom_array = <ROM>;
my $rom = join('', @rom_array);
close(ROM);
my $rom_size = length($rom)/1024;
my $rom_raw_size = length($rom);
print "*** ROM Size: ".$rom_size."kb\n";
my $title = substr($rom, 0x134, 16); # title is at 0x134-0x142 (16 chars)
print "*** Game: $title\n";
our %cart_types;
$cart_types{0x0}  = "ROM only";
$cart_types{0x1}  = "ROM and MBC1";
$cart_types{0x2}  = "ROM and MBC1 with RAM";
$cart_types{0x3}  = "ROM and MBC1 with RAM and BATTERY";
$cart_types{0x5}  = "ROM and MBC2";
$cart_types{0x6}  = "ROM and MBC2 with BATTERY";
$cart_types{0x8}  = "ROM with RAM";
$cart_types{0x9}  = "ROM with RAM and BATTERY";
$cart_types{0xB}  = "ROM with MMM01";
$cart_types{0xC}  = "ROM with MMM01 and SRAM";
$cart_types{0xD}  = "ROM with MMM01 and SRAM with BATTERY";
$cart_types{0xF}  = "ROM with MBC3 and TIMER with BATTERY";
$cart_types{0x10} = "ROM with MBC3 and TIMER with RAM and BATTERY";
$cart_types{0x11} = "ROM with MBC3";
$cart_types{0x12} = "ROM with MBC3 and RAM";
$cart_types{0x13} = "ROM with MBC3 and RAM and BATTERY";
$cart_types{0x19} = "ROM with MBC5";
$cart_types{0x1A} = "ROM with MBC5 and RAM";
$cart_types{0x1B} = "ROM with MBC5 and RAM with BATTERY";
$cart_types{0x1C} = "ROM with MBC5 and RUMBLE";
$cart_types{0x1D} = "ROM with MBC5 and RUMBLE with SRAM";
$cart_types{0x1E} = "ROM with MBC5 and RUMBLE with SRAM and BATTERY";
$cart_types{0x1F} = "Pocket Camera ROM";
$cart_types{0xFD} = "Bandai TAMA-5";
$cart_types{0xFE} = "Hudson HuC-3";
$cart_types{0xFF} = "Hudson HuC-1";

my $cart_type = substr($rom, 0x147, 1);
print "*** Cart type: ".$cart_types{ord($cart_type)}."\n";

my $rom_size_ = substr($rom, 0x148, 1);
my $ram_size_ = substr($rom, 0x149, 1);

our @stack;
our %regs;

# 8-bit registers
$regs{A} = 0x0;
$regs{B} = 0x0;
$regs{C} = 0x0;
$regs{D} = 0x0;
$regs{E} = 0x0;
$regs{F} = 0x0;
$regs{H} = 0x0;
$regs{L} = 0x0;
# 16-bit registers
$regs{AF} = 0x0;
$regs{BC} = 0x0;
$regs{DE} = 0x0;
$regs{HL} = 0x0;
# Special registers
$regs{PC} = 0x0; # Program counter
$regs{SP} = 0x100; # Stack pointer

sub is_spacebar_down {
	while ($event->wait()) {
		my $key = $event->type();
		if ($key == SDLK_SPACE) { return(1); }
		if ($key == SDLK_q) { exit(0); }
		else { return(0); }	
	}
}
sub RCLA {
	$regs{F} = (($regs{A} & 0x80) >> 3); 
	$regs{A} = (($regs{A} << 1) | ($regs{A} >> 7));
}


sub RLA  {
	$regs{F} |= $regs{A} >> 7;
	$regs{A} <<= 1;
	$regs{A} |= ($regs{F} & 0x10) >> 4;
	$regs{F} = ($regs{F} & 0x01) << 4; 
}

sub RRA { 
	$regs{F} |= $regs{A} & 0x01;
	$regs{A} >>= 1;
        $regs{A} |= ($regs{F} & 0x10) << 3;
	$regs{F} = ($regs{F} & 0x01) << 4;
}

sub DAA { }

for ($x=0;$x<length($rom);$x++) {
	$rom_array[$x]=substr($rom, $x, 1);
}
print "*** ROM Array creation complete, beginning exec\n";

$starttime = time()-1;
sub DAA { print "DAA: BROKEN\n"; }

sub ADD {
	my $reg = shift;
	$regs{F} = (((($regs{A} & 0x0F) + ($reg & 0x0F)) & 0x10) << 1) | ((($regs{A} + $$reg) & 0x100) >> 4) |  (($regs{A} = ($regs{A} + $$reg)) == 0) ? 0x80 : 0;
}

sub ADC {
	my $reg = shift;
	my $DUMMY_F = ($regs{F} & 0x10) > 0 ? 1 :0;
	$regs{F}    = (((($regs{A} & 0x0F) + ($$reg & 0x0F) + $DUMMY_F) & 0x10) << 1) | 
	((($regs{A} + $$reg + $DUMMY_F) & 0x100 ) >> 4) | (($regs{A} = ($regs{A} + $$reg + $DUMMY_F))) == 0 ? 0x80 : 0; 
}

while (1) {
	$ticks++;
	my $opcode = substr($rom, $regs{PC}, 1);
	if ($opcode == 0x00) {  }                                              # NOP
#	elsif ($opcode != 0x00) { printf(" 0x%.2lX", $opcode); }
	elsif ($opcode == 0x01) { $regs{BC} = $regs{PC}; $regs{PC}+=2; }
	elsif ($opcode == 0x02) { $regs{BC} = $regs{A}; } # LD (BC),A
	elsif ($opcode == 0x03) { $regs{BC}++; }          # INC BC
	elsif ($opcode == 0x04) { $regs{B}++; }           # INC B
	elsif ($opcode == 0x05) { $regs{B}--; }           # DEC B
	elsif ($opcode == 0x06) { $regs{B} = substr($rom, ++$regs{PC}, 2); $regs{PC}++; } # LD B,N
	elsif ($opcode == 0x07) { &RCLA(); }                                   # RLCA
	elsif ($opcode == 0x08) { 					       #  LD (NN),SP
		$rom[$regs{PC}] = $regs{SP};
		$rom[++$regs{PC}] = $regs{SP};
	} 
	elsif ($opcode == 0x09) { $regs{HL} += $regs{BC}; }                    #  ADD HL,BC 
	elsif ($opcode == 0x0A) { $regs{A}  =  $regs{BC}; }                    #  LD A,(BC) 
	elsif ($opcode == 0x0B) { $regs{BC}--; }                               #  DEC BC 
	elsif ($opcode == 0x0C) { $regs{C}++;  }                               #  INC C 
	elsif ($opcode == 0x0D) { $regs{C}--;  }                               #  DEC C 
	elsif ($opcode == 0x0E) { $regs{C} = substr($rom, ++$regs{PC}, 1); }   #  LD C,N 
	elsif ($opcode == 0x0F) { &RRCA(); }                                   #  RRCA
	elsif ($opcode == 0x10) { print "0x10 (STOP) unimplimented\n"; }       #  STOP
	elsif ($opcode == 0x11) {                                              #  LD DE,NN
		$regs{DE} = substr($rom, ++$regs{PC}, 1);
		$regs{PE} += 2;
		if ($regs{DE} == 0x1B58) {
			if (substr($rom, $regs{PC}, 1).substr($rom, $regs{PC}+2, 1) == 0) {
				$regs{DE} = 0x0001;
			}
		}
	}
	elsif ($opcode == 0x12) { $regs{DE} = $regs{A}; }                      #  LD (DE),A
	elsif ($opcode == 0x13) { $regs{DE}++; }                               #  INC DE 
	elsif ($opcode == 0x14) { $regs{D}++;  }                               #  INC D 
	elsif ($opcode == 0x15) { $regs{D}--;  }                               #  DEC D 
	elsif ($opcode == 0x16) { $regs{D} = substr($rom, ++$regs{PC}, 1); }   #  LD D,N 
	elsif ($opcode == 0x17) { &RLA(); }                                    #  RLA 
	elsif ($opcode == 0x18) { $regs{PC} = substr($rom, ++$regs{PC}, 1); }  #  JR N 
	elsif ($opcode == 0x19) { $regs{HL} = $regs{DE}; }                     #  ADD HL,DE 
	elsif ($opcode == 0x1A) { $regs{A}  = $regs{DE}; }                     #  LD A,(DE) 
	elsif ($opcode == 0x1B) { $regs{DE}--; }                               #  DEC DE 
	elsif ($opcode == 0x1C) { $regs{E}++; }                                #  INC E 
	elsif ($opcode == 0x1D) { $regs{E}--; }                                #  DEC E 
	elsif ($opcode == 0x1E) { $regs{E} = substr($rom, ++$regs{PC}, 1); }   #  LD E,N
	elsif ($opcode == 0x1F) { &RRA(); }                                    #  RRA 
	elsif ($opcode == 0x20) {                                              #  JR NZ,N 
		if (($regs{F} & 0x80) == 0) { 
			$regs{PC} = substr($rom, ++$regs{PC}, 1);
		}
		else { 
			$regs{PC}++ 
		}
	}
	elsif ($opcode == 0x21) { $regs{HL} = substr($rom, ++$regs{PC}, 2); $regs{PC}++; } #  LD HL,NN 
	elsif ($opcode == 0x22) { $regs{HL}++; $regs{HL} = $regs{A}; }         #  LDI (HL),A 
	elsif ($opcode == 0x23) { $regs{HL}++; }                               #  INC HL 
	elsif ($opcode == 0x24) { $regs{H}++; }                                #  INC H 
	elsif ($opcode == 0x25) { $regs{H}--; }                                #  DEC H 
	elsif ($opcode == 0x26) { $regs{H} = substr($rom, ++$regs{PC}, 1); }   #  LD H,N 
	elsif ($opcode == 0x27) { &DAA(); }                                    #  DAA 
	elsif ($opcode == 0x28) {                                              #  JR Z,N 
		if (($regs{F} & 0x80) != 0) { print "JR Z,N\n"; } # impliment
		else { $regs{PC}++; }
	}
	elsif ($opcode == 0x29) { $regs{HL} += $regs{HL}; }                    #  ADD HL,HL 
	elsif ($opcode == 0x2A) { $regs{A}=++$regs{HL}; }                      #  LDI A,(HL) 
	elsif ($opcode == 0x2B) { $regs{HL}--; }                               #  DEC HL 
	elsif ($opcode == 0x2C) { $regs{L}++; }                                #  INC L 
	elsif ($opcode == 0x2D) { $regs{L}--; }                                #  DEC L 
	elsif ($opcode == 0x2E) { $regs{L} = substr($rom, ++$regs{PC}, 1); }   #  LD L,N 
	elsif ($opcode == 0x2F) {                                              #  CPL 
		$regs{A} ^= 0xFF;
		$regs{F}  = ($reg{F} & (0x80 | 0x10)) | 0x40 | 0x20;
		print "CPL: possibly misimpliented\n";
	}
	elsif ($opcode == 0x30) {                                              #  JR NC,N 
		if (($regs{F} & 0x10) == 0) { print "JR NC, N\n"; }
		else { $regs{PC}++; }
	}
	elsif ($opcode == 0x31) { 
		$regs{SP} = substr($rom, ++$regs{PC}, 2);                   #  LD SP,NN 
		$regs{PC}++;
	}
	elsif ($opcode == 0x32) { $regs{HL}--; $regs{HL}=$regs{A}; }           #  LDD (HL),A 
	elsif ($opcode == 0x33) { $regs{SP}++; }                               #  INC SP 
	elsif ($opcode == 0x34) { $regs{HL}++; }                               #  INC (HL)                       
	elsif ($opcode == 0x35) { $regs{HL}--; }                               #  DEC (HL) 
	elsif ($opcode == 0x36) { $regs{HL} = substr($rom, ++$regs{PC}, 1); }  #  LD (HL),N 
	elsif ($opcode == 0x37) { $regs{F} = ($regs{F} & 0x80) | 0x10; }
	elsif ($opcode == 0x38) {                                              #  JR C,N 
		if (($regs{F} & 0x10) != 0) { print "JR C,N\n"; }
		else { $regs{SP}++; }
	}	
	elsif ($opcode == 0x39) { $regs{HL} += $regs{SP}; }                    #  ADD HL,SP 
	elsif ($opcode == 0x3A) { $regs{A} = substr($rom, --$regs{HL}, 1); }   #  LDD A,(HL) 
	elsif ($opcode == 0x3B) { $regs{SP}--; }                               #  DEC SP 
	elsif ($opcode == 0x3C) { $regs{A}++; }                                #  INC A 
	elsif ($opcode == 0x3D) { $regs{A}--; }                                #  DEC A 
	elsif ($opcode == 0x3E) { $regs{A} = substr($rom, ++$regs{PC}, 1); }   #  LD A,N 
	elsif ($opcode == 0x3F) {                                              #  CCF 
		$regs{F} = ($regs{F} ^ 0x10) & (0x10 | 0x80);
		print "CCF: possibly misplimented\n";
	}
	elsif ($opcode == 0x40) { $regs{B} = $regs{B}; }                       #  LD B,B 
	elsif ($opcode == 0x41) { $regs{B} = $regs{C}; }                       #  LD B,C 
	elsif ($opcode == 0x42) { $regs{B} = $regs{D}; }                       #  LD B,D 
	elsif ($opcode == 0x43) { $regs{B} = $regs{E}; }                       #  LD B,E 
	elsif ($opcode == 0x44) { $regs{B} = $regs{H}; }                       #  LD B,H 
	elsif ($opcode == 0x45) { $regs{B} = $regs{L}; }                       #  LD B,L 
	elsif ($opcode == 0x46) { $regs{B} = substr($rom, $regs{HL}, 1); }     #  LD B,(HL) 
	elsif ($opcode == 0x47) { $regs{B} = $regs{A}; }                       #  LD B,A 
	elsif ($opcode == 0x48) { $regs{C} = $regs{B}; }                       #  LD C,B 
	elsif ($opcode == 0x49) { $regs{C} = $regs{C}; }                       #  LD C,C 
	elsif ($opcode == 0x4A) { $regs{C} = $regs{D}; }                       #  LD C,D 
	elsif ($opcode == 0x4B) { $regs{C} = $regs{E}; }                       #  LD C,E 
	elsif ($opcode == 0x4C) { $regs{C} = $regs{H}; }                       #  LD C,H 
	elsif ($opcode == 0x4D) { $regs{C} = $regs{L}; }                       #  LD C,L 
	elsif ($opcode == 0x4E) { $regs{C} = substr($rom, $regs{HL}, 1); }     #  LD C,(HL) 
	elsif ($opcode == 0x4F) { $regs{C} = $regs{A}; }                       #  LD C,A 
	elsif ($opcode == 0x50) { $regs{D} = $regs{B}; }                       #  LD D,B 
	elsif ($opcode == 0x51) { $regs{D} = $regs{C}; }                       #  LD D,C 
	elsif ($opcode == 0x52) { $regs{D} = $regs{D}; }                       #  LD D,D 
	elsif ($opcode == 0x53) { $regs{D} = $regs{E}; }                       #  LD D,E 
	elsif ($opcode == 0x54) { $regs{D} = $regs{H}; }                       #  LD D,H 
	elsif ($opcode == 0x55) { $regs{D} = $regs{L}; }                       #  LD D,L 
	elsif ($opcode == 0x56) { $regs{D} = substr($rom, $regs{HL}, 1); }     #  LD D,(HL) 
	elsif ($opcode == 0x57) { $regs{D} = $regs{A}; }                       #  LD D,A 
	elsif ($opcode == 0x58) { $regs{E} = $regs{B}; }                       #  LD E,B 
	elsif ($opcode == 0x59) { $regs{E} = $regs{C}; }                       #  LD E,C 
	elsif ($opcode == 0x5A) { $regs{E} = $regs{D}; }                       #  LD E,D 
	elsif ($opcode == 0x5B) { $regs{E} = $regs{E}; }                       #  LD E,E 
	elsif ($opcode == 0x5C) { $regs{E} = $regs{H}; }                       #  LD E,H 
	elsif ($opcode == 0x5D) { $regs{E} = $regs{L}; }                       #  LD E,L 
	elsif ($opcode == 0x5E) { $regs{E} = substr($rom, $regs{HL}, 1); }     #  LD E,(HL) 
	elsif ($opcode == 0x5F) { $regs{E} = $regs{A}; }                       #  LD E,A 
	elsif ($opcode == 0x60) { $regs{H} = $regs{B}; }                       #  LD H,B 
	elsif ($opcode == 0x61) { $regs{H} = $regs{C}; }                       #  LD H,C 
	elsif ($opcode == 0x62) { $regs{H} = $regs{D}; }                       #  LD H,D 
	elsif ($opcode == 0x63) { $regs{H} = $regs{E}; }                       #  LD H,E 
	elsif ($opcode == 0x64) { $regs{H} = $regs{H}; }                       #  LD H,H 
	elsif ($opcode == 0x65) { $regs{H} = $regs{L}; }                       #  LD H,L 
	elsif ($opcode == 0x66) { $regs{H} = substr($rom, $regs{HL}, 1); }     #  LD H,(HL) 
	elsif ($opcode == 0x67) { $regs{H} = $regs{A}; }                       #  LD H,A 
	elsif ($opcode == 0x68) { $regs{L} = $regs{B}; }                       #  LD L,B 
	elsif ($opcode == 0x69) { $regs{L} = $regs{C}; }                       #  LD L,C 
	elsif ($opcode == 0x6A) { $regs{L} = $regs{D}; }                       #  LD L,D 
	elsif ($opcode == 0x6B) { $regs{L} = $regs{E}; }                       #  LD L,E 
	elsif ($opcode == 0x6C) { $regs{L} = $regs{H}; }                       #  LD L,H 
	elsif ($opcode == 0x6D) { $regs{L} = $regs{L}; }                       #  LD L,L 
	elsif ($opcode == 0x6E) { $regs{L} = substr($rom, $regs{HL}, 1); }     #  LD L,(HL) 
	elsif ($opcode == 0x6F) { $regs{L} = $regs{A}; }                       #  LD L,A 
	elsif ($opcode == 0x70) { 					       #  LD (HL),B 
		$rom[$regs{HL}] = $regs{B};
		$rom[++$regs{HL}] = $regs{B};
	} 
	elsif ($opcode == 0x71) { 					       #  LD (HL),C
		$rom[$regs{HL}] = $regs{C};
		$rom[++$regs{HL}] = $regs{C};
	} 
	elsif ($opcode == 0x72) { 					       #  LD (HL),D
		$rom[$regs{HL}] = $regs{D};
		$rom[++$regs{HL}] = $regs{D};
	} 
	elsif ($opcode == 0x73) { 					       #  LD (HL),E
		$rom[$regs{HL}] = $regs{E};
		$rom[++$regs{HL}] = $regs{E};
	} 
	elsif ($opcode == 0x74) { 					       #  LD (HL),H
		$rom[$regs{HL}] = $regs{H};
		$rom[++$regs{HL}] = $regs{H};
	} 
	elsif ($opcode == 0x75) { 					       #  LD (HL),L
		$rom[$regs{HL}] = $regs{L};
		$rom[++$regs{HL}] = $regs{L};
	} 
	elsif ($opcode == 0x76) { $cpu_running = 0; safe_reg_dump(); }         #  HALT 
	elsif ($opcode == 0x77) { 					       #  LD (HL),A
		$rom[$regs{HL}] = $regs{A};
		$rom[++$regs{HL}] = $regs{A};
	} 
	elsif ($opcode == 0x78) { $regs{A} = $regs{B}; }                       #  LD A,B 
	elsif ($opcode == 0x79) { $regs{A} = $regs{C}; }                       #  LD A,C 
	elsif ($opcode == 0x7A) { $regs{A} = $regs{D}; }                       #  LD A,D 
	elsif ($opcode == 0x7B) { $regs{A} = $regs{E}; }                       #  LD A,E 
	elsif ($opcode == 0x7C) { $regs{A} = $regs{H}; }                       #  LD A,H 
	elsif ($opcode == 0x7D) { $regs{A} = $regs{L}; }                       #  LD A,L 
	elsif ($opcode == 0x7E) { $regs{A} = substr($rom, $regs{HL}, 1); }     #  LD A,(HL) 
	elsif ($opcode == 0x7F) { $regs{A} = $regs{A}; }                       #  LD A,A 
	elsif ($opcode == 0x80) { $regs{A} += $regs{B} }                       #  ADD A,B 
	elsif ($opcode == 0x81) { $regs{A} += $regs{C}; }                      #  ADD A,C 
	elsif ($opcode == 0x82) { $regs{A} += $regs{D}; }                      #  ADD A,D 
	elsif ($opcode == 0x83) { $regs{A} += $regs{E}; }                      #  ADD A,E 
	elsif ($opcode == 0x84) { $regs{A} += $regs{H}; }                      #  ADD A,H 
	elsif ($opcode == 0x85) { $regs{A} += $regs{L}; }                      #  ADD A,L 
	elsif ($opcode == 0x86) { $regs{A} += substr($rom, ++$regs{HL}, 1); }  #  ADD A,(HL) 
	elsif ($opcode == 0x87) { $regs{A} += $regs{A}; }                      #  ADD A,A 
	elsif ($opcode == 0x88) { ADC(\$regs{B}); }                             #  ADC A,B 
	elsif ($opcode == 0x89) { ADC(\$regs{C}); }                             #  ADC A,C 
	elsif ($opcode == 0x8A) { ADC(\$regs{D}); }                             #  ADC A,D 
	elsif ($opcode == 0x8B) { ADC(\$regs{E}); }                             #  ADC A,E 
	elsif ($opcode == 0x8C) { ADC(\$regs{H}); }                             #  ADC A,H 
	elsif ($opcode == 0x8D) { ADC(\$regs{L}); }                             #  ADC A,L 
	elsif ($opcode == 0x8E) { ADC(\$regs{HL}); }                            #  ADC A,(HL) 
	elsif ($opcode == 0x8F) { ADC(\$regs{A}); }                             #  ADC A,A 
	else { 
		printf("Invalid opcode 0x%.2lX at $regs{PC}\n", $opcode);
		dump_regs();
	}
	$regs{PC}++;
	$secs  = time()-$starttime;
	$clock = ($ticks/$secs)/256;
#	print "\rMHz: ".sprintf("%.3f", $clock)."/$CLK_GBC ($ticks)";
	#sleep(1) if ($clock > $CLK_GBC && is_spacebar_down());
}


sub dump_regs {
	$endttime = time();
	printf("\n8-bit registers\n");
	printf("A: 0x%.2lX B: 0x%.2lX C: 0x%.2lX D: 0x%.2lX\n", $regs{A}, $regs{B}, $regs{C}, $regs{D});
	printf("E: 0x%.2lX F: 0x%.2lX H: 0x%.2lX L: 0x%.2lX\n", $regs{E}, $regs{F}, $regs{H}, $regs{L});
	print "16-bit registers\n";
	printf("AF: 0x%.4lX BC: 0x%.4lX DE: 0x%.4lX HL: 0x%.4lX\n", $regs{AF}, $regs{BC}, $regs{DE}, $regs{HL});
	printf("Stack pointer: 0x%.4lX Program counter: 0x%.4lX Cycles: %d\n", $regs{SP}, $regs{PC}, $ticks);
	$secs = $endttime-$starttime;
	$mhz  = ($ticks/$secs)/256;
	printf("Average CPU speed: %dMHz (%dGHz)\n", int($mhz), int($mhz/1024));
	exit(1);

	# Reset registers in case we want to restart
	$regs{A} = 0x00;
	$regs{B} = 0x00;
	$regs{C} = 0x00;
	$regs{D} = 0x00;
	$regs{E} = 0x00;
	$regs{F} = 0x00;
	$regs{H} = 0x00;
	$regs{L} = 0x00;
	# 16-bit registers
	$regs{AF} = 0x0000;
	$regs{BC} = 0x0000;
	$regs{DE} = 0x0000;
	$regs{HL} = 0x0000;
	# Special registers
	$regs{PC} = 0x100;  # Program counter
	$regs{SP} = 0xFFFE; # Stack pointer
}

sub safe_reg_dump {
	$endttime = time();
	printf("\n8-bit registers\n");
	printf("A: 0x%.2lX B: 0x%.2lX C: 0x%.2lX D: 0x%.2lX\n", $regs{A}, $regs{B}, $regs{C}, $regs{D});
	printf("E: 0x%.2lX F: 0x%.2lX H: 0x%.2lX L: 0x%.2lX\n", $regs{E}, $regs{F}, $regs{H}, $regs{L});
	print "16-bit registers\n";
	printf("AF: 0x%.4lX BC: 0x%.4lX DE: 0x%.4lX HL: 0x%.4lX\n", $regs{AF}, $regs{BC}, $regs{DE}, $regs{HL});
	printf("Stack pointer: 0x%.4lX Program counter: 0x%.4lX Cycles: %d\n", $regs{SP}, $regs{PC}, $ticks);
	$secs = $endttime-$starttime;
	$mhz  = ($ticks/$secs);
	printf("Average CPU speed: %dMHz (%dGHz)\n", int($mhz), int($mhz/1024));
}