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));
}