<?php
    
    /*
        GB Disassembler by Kak
        To be used on scripts.
    */
    
    // Sanity checks
    if (substr(php_sapi_name(), 0, 3) != 'cli') {
        die("This program can only be run under the PHP console.");
    }
    
    if (!isset($argv[2])) {
        echo "Not enough arguments.\n";
        echo "php dis.php <path> <type> [<initial offset> [<ending offset>]] [<vars> [...]]\n\n";
        echo "<path>           - Path to the GB ROM\n";
        echo "<type>           - Handling the disasm.\n";
        echo "                   CODE - Disasm mode. Also shows commented bytes.\n";
        echo "                   DATA - Will only print out bytes in .db \$XX,... format.\n";
        echo "                   VARS - Prints out variable information\n";
        echo "<initial offset> - Initial offset to start disassembling\n";
        echo "<ending offset>  - Offset to end dissasembling\n";
        echo "<vars>           - Path to file containing variable info.\n\n";
        
        echo "Example:\n";
        echo "dis.php wl.gb 0x00001F2 0x0000219 wl.txt alt.txt\n";
        echo "Will disassemble wl.gb from 0x00001F2 to 0x0000219, using symbols from wl.txt and alt.txt";
        die;
    }
    
    // Here we go
    
    define('ROM', $argv[1]);
    
    define('CODE_PADDING', 30);
    define('DATA_PADDING', 20);
    define('OFFSET_PADDING', 5);
    define('MAX_DB_ON_LINE', 15); // (offset by 1, ie: 15 will print 16 bytes on one line)
    define('LINE_PADDING', '    '); // Used at the beggining of the line
    
    
    /*
        Nonconfigurable stuff below
        Edit at your own risk.
    */
    
    
    $strcount = 1; // Required
    
    $h = fopen(ROM, 'r');
    if (!$h) die;
    
    switch ($argv[2]) {
        case 'CODE': break; // Do nothing!
        case 'DATA':
            $onlydata = true;
            break;
        case 'VARS':
            $dumpvars = true;
            break;
        default: die("Invalid type selected.");
    }
    
    // Have we set an initial offset for disassembling?
    $endoffset = 0;
    if (isset($argv[3])) {
        if (fseek($h, $argv[3]) < 0) {
            die("Invalid initial offset specified.");
        }
        // Or the ending offset?
        if (isset($argv[4])) {
            if ($argv[3] > $argv[4]) {
                die("The ending offset can't be greater than the initial.");
            }
            $endoffset = $argv[4];
        }
    }
    
    $replacements = array();
    // Do we have files with defined variables?
    for($a = 5; isset($argv[$a]); $a++) {
        if (isset($argv[$a])) {
            $s = fopen($argv[$a], 'r');
            if (!$s) die;
            
            if (!isset($dumpvars)) {
                while (($v = fgetcsv($s, 128, "=")) !== false) {
                    if ($v[0][0] == ";") continue;
                    $replacements[trim($v[0])] = trim($v[1]);
                }
            } else {
                while (($v = fgetcsv($s, 128, "=")) !== false) {
                    if ($v[0][0] == ";") {
                        echo implode("=", $v)."\n";
                        continue;
                    }
                    echo ".def ".str_pad($v[1], CODE_PADDING, " ", STR_PAD_RIGHT).$v[0]."\n";
                }
                
            }
            fclose($s);
        }
    }
    if (isset($dumpvars)) die;
    $opcodes = array (
    // Opcode => Instruction, Length, [Extra format], [Jump type]

    // Jump type
    // 1 - Relative Jump
    // 2 - Absolute Jump
        
        // Misc instructions (1)
        '00' => ['NOP', 1],
        '01' => ['LD BC,', 3, '$!2!1'],
        '02' => ['LD (BC), A', 1],
        '03' => ['INC BC', 1],
        '04' => ['INC B', 1],
        '05' => ['DEC B', 1],
        '06' => ['LD B,', 2, '$!1'],
        '07' => ['RLCA', 1],
        '08' => ['LD', 3, '($!2!1), SP'],
        '09' => ['ADD HL, BC', 1],
        '0A' => ['LD A, (BC)', 1],
        '0B' => ['DEC BC', 1],
        '0C' => ['INC C', 1],
        '0D' => ['DEC C', 1],
        '0E' => ['LD C,', 2, '$!1'],
        '0F' => ['RRCA', 1],

        '10' => ['STOP', 2, ''], // 2 BYTE STOP
        '11' => ['LD DE,', 3, '$!2!1'],
        '12' => ['LD (DE), A', 1],
        '13' => ['INC DE', 1],
        '14' => ['INC D', 1],
        '15' => ['DEC D', 1],
        '16' => ['LD D,', 2, '$!1'],
        '17' => ['RLA', 1],
        '18' => ['JR', 2, '$!1', true],
        '19' => ['ADD HL, DE', 1],
        '1A' => ['LD A, (DE)', 1],
        '1B' => ['DEC DE', 1],
        '1C' => ['INC E', 1],
        '1D' => ['DEC E', 1],
        '1E' => ['LD E,', 2, '$!1'],
        '1F' => ['RRA', 1],

        '20' => ['JR NZ,', 2, '$!1', true],
        '21' => ['LD HL,', 3, '$!2!1'],
        '22' => ['LDI (HL), A', 1],
        '23' => ['INC HL', 1],
        '24' => ['INC H', 1],
        '25' => ['DEC H', 1],
        '26' => ['LD H,', 2, '$!1'],
        '27' => ['DAA', 1],
        '28' => ['JR Z,', 2, '$!1', true],
        '29' => ['ADD HL, HL', 1],
        '2A' => ['LDI A, (HL)', 1],
        '2B' => ['DEC HL', 1],
        '2C' => ['INC L', 1],
        '2D' => ['DEC L', 1],
        '2E' => ['LD L,', 2, '$!1'],
        '2F' => ['CPL', 1],

        '30' => ['JR NC,', 2, '$!1', true],
        '31' => ['LD SP,', 3, '$!2!1'],
        '32' => ['LDD (HL), A', 1],
        '33' => ['INC SP', 1],
        '34' => ['INC (HL)', 1],
        '35' => ['DEC (HL)', 1],
        '36' => ['LD (HL),', 2, '$!1'],
        '37' => ['SCF', 1],
        '38' => ['JR C,', 2, '$!1', true],
        '39' => ['ADD HL, SP', 1],
        '3A' => ['LDD A, (HL)', 1],
        '3B' => ['DEC SP', 1],
        '3C' => ['INC A', 1],
        '3D' => ['DEC A', 1],
        '3E' => ['LD A,', 2, '$!1'],
        '3F' => ['CCF', 1],
        
        // Main LD block
        '40' => ['LD B, B', 1],
        '41' => ['LD B, C', 1],
        '42' => ['LD B, D', 1],
        '43' => ['LD B, E', 1],
        '44' => ['LD B, H', 1],
        '45' => ['LD B, L', 1],
        '46' => ['LD B, (HL)', 1],
        '47' => ['LD B, A', 1],
        
        '48' => ['LD C, B', 1],
        '49' => ['LD C, C', 1],
        '4A' => ['LD C, D', 1],
        '4B' => ['LD C, E', 1],
        '4C' => ['LD C, H', 1],
        '4D' => ['LD C, L', 1],
        '4E' => ['LD C, (HL)', 1],
        '4F' => ['LD C, A', 1],
        //
        '50' => ['LD D, B', 1],
        '51' => ['LD D, C', 1],
        '52' => ['LD D, D', 1],
        '53' => ['LD D, E', 1],
        '54' => ['LD D, H', 1],
        '55' => ['LD D, L', 1],
        '56' => ['LD D, (HL)', 1],
        '57' => ['LD D, A', 1],
        
        '58' => ['LD E, B', 1],
        '59' => ['LD E, C', 1],
        '5A' => ['LD E, D', 1],
        '5B' => ['LD E, E', 1],
        '5C' => ['LD E, H', 1],
        '5D' => ['LD E, L', 1],
        '5E' => ['LD E, (HL)', 1],
        '5F' => ['LD E, A', 1],
        //
        '60' => ['LD H, B', 1],
        '61' => ['LD H, C', 1],
        '62' => ['LD H, D', 1],
        '63' => ['LD H, E', 1],
        '64' => ['LD H, H', 1],
        '65' => ['LD H, L', 1],
        '66' => ['LD H, (HL)', 1],
        '67' => ['LD H, A', 1],
        
        '68' => ['LD L, B', 1],
        '69' => ['LD L, C', 1],
        '6A' => ['LD L, D', 1],
        '6B' => ['LD L, E', 1],
        '6C' => ['LD L, H', 1],
        '6D' => ['LD L, L', 1],
        '6E' => ['LD L, (HL)', 1],
        '6F' => ['LD L, A', 1],
        //
        '70' => ['LD (HL), B', 1],
        '71' => ['LD (HL), C', 1],
        '72' => ['LD (HL), D', 1],
        '73' => ['LD (HL), E', 1],
        '74' => ['LD (HL), H', 1],
        '75' => ['LD (HL), L', 1],
        '76' => ['HALT', 1],
        '77' => ['LD (HL), A', 1],
        
        '78' => ['LD A, B', 1],
        '79' => ['LD A, C', 1],
        '7A' => ['LD A, D', 1],
        '7B' => ['LD A, E', 1],
        '7C' => ['LD A, H', 1],
        '7D' => ['LD A, L', 1],
        '7E' => ['LD A, (HL)', 1],
        '7F' => ['LD A, A', 1],
        
        // Main 8bit arithmetic block
        '80' => ['ADD A, B', 1],
        '81' => ['ADD A, C', 1],
        '82' => ['ADD A, D', 1],
        '83' => ['ADD A, E', 1],
        '84' => ['ADD A, H', 1],
        '85' => ['ADD A, L', 1],
        '86' => ['ADD A, (HL)', 1],
        '87' => ['ADD A, A', 1],
        
        '88' => ['ADC A, B', 1],
        '89' => ['ADC A, C', 1],
        '8A' => ['ADC A, D', 1],
        '8B' => ['ADC A, E', 1],
        '8C' => ['ADC A, H', 1],
        '8D' => ['ADC A, L', 1],
        '8E' => ['ADC A, (HL)', 1],
        '8F' => ['ADC A, A', 1],
        //
        '90' => ['SUB B', 1],
        '91' => ['SUB C', 1],
        '92' => ['SUB D', 1],
        '93' => ['SUB E', 1],
        '94' => ['SUB H', 1],
        '95' => ['SUB L', 1],
        '96' => ['SUB (HL)', 1],
        '97' => ['SUB A', 1],
        
        '98' => ['SBC A, B', 1],
        '99' => ['SBC A, C', 1],
        '9A' => ['SBC A, D', 1],
        '9B' => ['SBC A, E', 1],
        '9C' => ['SBC A, H', 1],
        '9D' => ['SBC A, L', 1],
        '9E' => ['SBC A, (HL)', 1],
        '9F' => ['SBC A, A', 1],
        //
        'A0' => ['AND B', 1],
        'A1' => ['AND C', 1],
        'A2' => ['AND D', 1],
        'A3' => ['AND E', 1],
        'A4' => ['AND H', 1],
        'A5' => ['AND L', 1],
        'A6' => ['AND (HL)', 1],
        'A7' => ['AND A', 1],
        
        'A8' => ['XOR B', 1],
        'A9' => ['XOR C', 1],
        'AA' => ['XOR D', 1],
        'AB' => ['XOR E', 1],
        'AC' => ['XOR H', 1],
        'AD' => ['XOR L', 1],
        'AE' => ['XOR (HL)', 1],
        'AF' => ['XOR A', 1],        
        //
        'B0' => ['OR B', 1],
        'B1' => ['OR C', 1],
        'B2' => ['OR D', 1],
        'B3' => ['OR E', 1],
        'B4' => ['OR H', 1],
        'B5' => ['OR L', 1],
        'B6' => ['OR (HL)', 1],
        'B7' => ['OR A', 1],
        
        'B8' => ['CP B', 1],
        'B9' => ['CP C', 1],
        'BA' => ['CP D', 1],
        'BB' => ['CP E', 1],
        'BC' => ['CP H', 1],
        'BD' => ['CP L', 1],
        'BE' => ['CP (HL)', 1],
        'BF' => ['CP A', 1],    
    
        // Misc instructions (2)
        'C0' => ['RET NZ', 1],
        'C1' => ['POP BC', 1],
        'C2' => ['JP NZ,', 3, '$!2!1', 2],
        'C3' => ['JP', 3, '$!2!1', 2],
        'C4' => ['CALL NZ,', 3, '$!2!1', 2],
        'C5' => ['PUSH BC', 1],
        'C6' => ['ADD A,', 2, '$!1'],
        'C7' => ['RST 00', 1],
        'C8' => ['RET Z', 1],
        'C9' => ['RET', 1],
        'CA' => ['JP Z,', 3, '$!2!1', 2],
        'CB' => ['x', 2, '$!1'], // Bit instruction
        'CC' => ['CALL Z,', 3, '$!2!1', 2],
        'CD' => ['CALL', 3, '$!2!1', 2],
        'CE' => ['ADC A,', 2, '$!1'],
        'CF' => ['RST 08', 1],
        
        'D0' => ['RET NC', 1],
        'D1' => ['POP DE', 1],
        'D2' => ['JP NC,', 3, '$!2!1', 2],
        'D3' => ['-'],
        'D4' => ['CALL NZ,', 3, '$!2!1', 2],
        'D5' => ['PUSH DE', 1],
        'D6' => ['SUB', 2, '$!1'],
        'D7' => ['RST 10', 1],
        'D8' => ['RET C', 1],
        'D9' => ['RETI', 1],
        'DA' => ['JP C,', 3, '$!2!1', 2],
        'DB' => ['-'],
        'DC' => ['CALL C,', 3, '$!2!1', 2],
        'DD' => ['-'],
        'DE' => ['SBC A,', 2, '$!1'],
        'DF' => ['RST 18', 1],
        
        'E0' => ['LD', 2, '($FF00+$!1), A'], // $FF00+$!1
        'E1' => ['POP HL', 1],
        'E2' => ['LD ($FF00+C), A', 1],
        'E3' => ['-'],
        'E4' => ['-'],
        'E5' => ['PUSH HL', 1],
        'E6' => ['AND', 2, '$!1'],
        'E7' => ['RST 20', 1],
        'E8' => ['ADD SP,', 2, '$!1'],
        'E9' => ['JP (HL)', 1, '', 2],
        'EA' => ['LD', 3, '($!2!1), A'],
        'EB' => ['-'],
        'EC' => ['-'],
        'ED' => ['-'],
        'EE' => ['XOR', 2, '$!1'],
        'EF' => ['RST 28', 1],
        
        'F0' => ['LD A,', 2, '($FF00+$!1)'], // $FF00+$!1
        'F1' => ['POP AF', 1],
        'F2' => ['LD A, ($FF00+C)', 1],
        'F3' => ['DI', 1],
        'F4' => ['-'],
        'F5' => ['PUSH AF', 1],
        'F6' => ['OR', 2, '$!1'],
        'F7' => ['RST 30', 1],
        'F8' => ['LD HL, SP+', 2, '$!1'],
        'F9' => ['LD SP, HL', 1],
        'FA' => ['LD A,', 3, '($!2!1)'],
        'FB' => ['EI', 1],
        'FC' => ['-'],
        'FD' => ['-'],
        'FE' => ['CP', 2, '$!1'],
        'FF' => ['RST 38', 1]
        
    );
    
    $bitop = array (
        '00' => 'RLC B',
        '01' => 'RLC C',
        '02' => 'RLC D',
        '03' => 'RLC E',
        '04' => 'RLC H',
        '05' => 'RLC L',
        '06' => 'RLC (HL)',
        '07' => 'RLC A',
        '08' => 'RRC B',
        '09' => 'RRC C',
        '0A' => 'RRC D',
        '0B' => 'RRC E',
        '0C' => 'RRC H',
        '0D' => 'RRC L',
        '0E' => 'RRC (HL)',
        '0F' => 'RRC A',
        '10' => 'RL B',
        '11' => 'RL C',
        '12' => 'RL D',
        '13' => 'RL E',
        '14' => 'RL H',
        '15' => 'RL L',
        '16' => 'RL (HL)',
        '17' => 'RL A',
        '18' => 'RR B',
        '19' => 'RR C',
        '1A' => 'RR D',
        '1B' => 'RR E',
        '1C' => 'RR H',
        '1D' => 'RR L',
        '1E' => 'RR (HL)',
        '1F' => 'RR A',
        '20' => 'SLA B',
        '21' => 'SLA C',
        '22' => 'SLA D',
        '23' => 'SLA E',
        '24' => 'SLA H',
        '25' => 'SLA L',
        '26' => 'SLA (HL)',
        '27' => 'SLA A',
        '28' => 'SRA B',
        '29' => 'SRA C',
        '2A' => 'SRA D',
        '2B' => 'SRA E',
        '2C' => 'SRA H',
        '2D' => 'SRA L',
        '2E' => 'SRA (HL)',
        '2F' => 'SRA A',
        '30' => 'SWAP B',
        '31' => 'SWAP C',
        '32' => 'SWAP D',
        '33' => 'SWAP E',
        '34' => 'SWAP H',
        '35' => 'SWAP L',
        '36' => 'SWAP (HL)',
        '37' => 'SWAP A',
        '38' => 'SRL B',
        '39' => 'SRL C',
        '3A' => 'SRL D',
        '3B' => 'SRL E',
        '3C' => 'SRL H',
        '3D' => 'SRL L',
        '3E' => 'SRL (HL)',
        '3F' => 'SRL A',
        '40' => 'BIT 0, B',
        '41' => 'BIT 0, C',
        '42' => 'BIT 0, D',
        '43' => 'BIT 0, E',
        '44' => 'BIT 0, H',
        '45' => 'BIT 0, L',
        '46' => 'BIT 0, (HL)',
        '47' => 'BIT 0, A',
        '48' => 'BIT 1, B',
        '49' => 'BIT 1, C',
        '4A' => 'BIT 1, D',
        '4B' => 'BIT 1, E',
        '4C' => 'BIT 1, H',
        '4D' => 'BIT 1, L',
        '4E' => 'BIT 1, (HL)',
        '4F' => 'BIT 1, A',
        '50' => 'BIT 2, B',
        '51' => 'BIT 2, C',
        '52' => 'BIT 2, D',
        '53' => 'BIT 2, E',
        '54' => 'BIT 2, H',
        '55' => 'BIT 2, L',
        '56' => 'BIT 2, (HL)',
        '57' => 'BIT 2, A',
        '58' => 'BIT 3, B',
        '59' => 'BIT 3, C',
        '5A' => 'BIT 3, D',
        '5B' => 'BIT 3, E',
        '5C' => 'BIT 3, H',
        '5D' => 'BIT 3, L',
        '5E' => 'BIT 3, (HL)',
        '5F' => 'BIT 3, A',
        '60' => 'BIT 4, B',
        '61' => 'BIT 4, C',
        '62' => 'BIT 4, D',
        '63' => 'BIT 4, E',
        '64' => 'BIT 4, H',
        '65' => 'BIT 4, L',
        '66' => 'BIT 4, (HL)',
        '67' => 'BIT 4, A',
        '68' => 'BIT 5, B',
        '69' => 'BIT 5, C',
        '6A' => 'BIT 5, D',
        '6B' => 'BIT 5, E',
        '6C' => 'BIT 5, H',
        '6D' => 'BIT 5, L',
        '6E' => 'BIT 5, (HL)',
        '6F' => 'BIT 5, A',
        '70' => 'BIT 6, B',
        '71' => 'BIT 6, C',
        '72' => 'BIT 6, D',
        '73' => 'BIT 6, E',
        '74' => 'BIT 6, H',
        '75' => 'BIT 6, L',
        '76' => 'BIT 6, (HL)',
        '77' => 'BIT 6, A',
        '78' => 'BIT 7, B',
        '79' => 'BIT 7, C',
        '7A' => 'BIT 7, D',
        '7B' => 'BIT 7, E',
        '7C' => 'BIT 7, H',
        '7D' => 'BIT 7, L',
        '7E' => 'BIT 7, (HL)',
        '7F' => 'BIT 7, A',
        '80' => 'RES 0, B',
        '81' => 'RES 0, C',
        '82' => 'RES 0, D',
        '83' => 'RES 0, E',
        '84' => 'RES 0, H',
        '85' => 'RES 0, L',
        '86' => 'RES 0, (HL)',
        '87' => 'RES 0, A',
        '88' => 'RES 1, B',
        '89' => 'RES 1, C',
        '8A' => 'RES 1, D',
        '8B' => 'RES 1, E',
        '8C' => 'RES 1, H',
        '8D' => 'RES 1, L',
        '8E' => 'RES 1, (HL)',
        '8F' => 'RES 1, A',
        '90' => 'RES 2, B',
        '91' => 'RES 2, C',
        '92' => 'RES 2, D',
        '93' => 'RES 2, E',
        '94' => 'RES 2, H',
        '95' => 'RES 2, L',
        '96' => 'RES 2, (HL)',
        '97' => 'RES 2, A',
        '98' => 'RES 3, B',
        '99' => 'RES 3, C',
        '9A' => 'RES 3, D',
        '9B' => 'RES 3, E',
        '9C' => 'RES 3, H',
        '9D' => 'RES 3, L',
        '9E' => 'RES 3, (HL)',
        '9F' => 'RES 3, A',
        'A0' => 'RES 4, B',
        'A1' => 'RES 4, C',
        'A2' => 'RES 4, D',
        'A3' => 'RES 4, E',
        'A4' => 'RES 4, H',
        'A5' => 'RES 4, L',
        'A6' => 'RES 4, (HL)',
        'A7' => 'RES 4, A',
        'A8' => 'RES 5, B',
        'A9' => 'RES 5, C',
        'AA' => 'RES 5, D',
        'AB' => 'RES 5, E',
        'AC' => 'RES 5, H',
        'AD' => 'RES 5, L',
        'AE' => 'RES 5, (HL)',
        'AF' => 'RES 5, A',
        'B0' => 'RES 6, B',
        'B1' => 'RES 6, C',
        'B2' => 'RES 6, D',
        'B3' => 'RES 6, E',
        'B4' => 'RES 6, H',
        'B5' => 'RES 6, L',
        'B6' => 'RES 6, (HL)',
        'B7' => 'RES 6, A',
        'B8' => 'RES 7, B',
        'B9' => 'RES 7, C',
        'BA' => 'RES 7, D',
        'BB' => 'RES 7, E',
        'BC' => 'RES 7, H',
        'BD' => 'RES 7, L',
        'BE' => 'RES 7, (HL)',
        'BF' => 'RES 7, A',
        'C0' => 'SET 0, B',
        'C1' => 'SET 0, C',
        'C2' => 'SET 0, D',
        'C3' => 'SET 0, E',
        'C4' => 'SET 0, H',
        'C5' => 'SET 0, L',
        'C6' => 'SET 0, (HL)',
        'C7' => 'SET 0, A',
        'C8' => 'SET 1, B',
        'C9' => 'SET 1, C',
        'CA' => 'SET 1, D',
        'CB' => 'SET 1, E',
        'CC' => 'SET 1, H',
        'CD' => 'SET 1, L',
        'CE' => 'SET 1, (HL)',
        'CF' => 'SET 1, A',
        'D0' => 'SET 2, B',
        'D1' => 'SET 2, C',
        'D2' => 'SET 2, D',
        'D3' => 'SET 2, E',
        'D4' => 'SET 2, H',
        'D5' => 'SET 2, L',
        'D6' => 'SET 2, (HL)',
        'D7' => 'SET 2, A',
        'D8' => 'SET 3, B',
        'D9' => 'SET 3, C',
        'DA' => 'SET 3, D',
        'DB' => 'SET 3, E',
        'DC' => 'SET 3, H',
        'DD' => 'SET 3, L',
        'DE' => 'SET 3, (HL)',
        'DF' => 'SET 3, A',
        'E0' => 'SET 4, B',
        'E1' => 'SET 4, C',
        'E2' => 'SET 4, D',
        'E3' => 'SET 4, E',
        'E4' => 'SET 4, H',
        'E5' => 'SET 4, L',
        'E6' => 'SET 4, (HL)',
        'E7' => 'SET 4, A',
        'E8' => 'SET 5, B',
        'E9' => 'SET 5, C',
        'EA' => 'SET 5, D',
        'EB' => 'SET 5, E',
        'EC' => 'SET 5, H',
        'ED' => 'SET 5, L',
        'EE' => 'SET 5, (HL)',
        'EF' => 'SET 5, A',
        'F0' => 'SET 6, B',
        'F1' => 'SET 6, C',
        'F2' => 'SET 6, D',
        'F3' => 'SET 6, E',
        'F4' => 'SET 6, H',
        'F5' => 'SET 6, L',
        'F6' => 'SET 6, (HL)',
        'F7' => 'SET 6, A',
        'F8' => 'SET 7, B',
        'F9' => 'SET 7, C',
        'FA' => 'SET 7, D',
        'FB' => 'SET 7, E',
        'FC' => 'SET 7, H',
        'FD' => 'SET 7, L',
        'FE' => 'SET 7, (HL)',
        'FF' => 'SET 7, A'
    );
    
    // Analyzing jump labels
    $jumps = array();
    
    if (!isset($onlydata)) {
        while(!feof($h)) {
            $p = ftell($h); // Pointer
            // Reached the ending offset?
            if ($endoffset && $p > $endoffset) break;
            
            $c = gethexval($h); // Opcode
            
            // Jump / Call instruction?
            if (isset($opcodes[$c][3])) {
                if ($opcodes[$c][3] == 1) {
                    // Is this a relative jump instruction?
                    $c = hexdec(gethexval($h));
                    $oper = $c >> 7;
                    $val = $c & 0x7F;
                    $res = $oper ? ($p - (0x7E - $val)) : ($p + $val);
                } else {
                    for ($i = 1, $res = ''; $i < $opcodes[$c][1]; $i++) {
                        $res .= hexdec(gethexval($h));
                    }                
                }
                $jumps[$res] = true;
            }
        }
    }
    
    // Restore original file pointer
    if (isset($argv[3])) {
        fseek($h, $argv[3]);
    } else {
        fseek($h, 0);
    }
    
    $p = 0;
    $cont = 0; // Successive bytes
    $strcont = "";
    $origoff = ""; // Original offset
    while (!feof($h)) {
        $p = ftell($h);                             // Pointer
        $clabel = "LABEL_".strtoupper(dechex($p));    // Current label
        // Does a jump for this point exist?
        // NOTE: We also check in the $replacements in case the jump is defined but the code block doesn't exist
        if (isset($jumps[$p]) || isset($replacements[$clabel])) {
            echo LINE_PADDING . strtr("\n$clabel:\n", $replacements);
        }
        
        // Reached the ending offset?
        if ($endoffset && $p > $endoffset) break;
        
        $c = gethexval($h); // Opcode
        $b = ".db " . $c; // Bytes
        
        if (!isset($opcodes[$c])) {
            $instr = "[UNKNOWN]";
        } else if (isset($onlydata) || $opcodes[$c][0] == '-') {
            // Allow multiple bytes on .db declarations.
            if (!$cont) {
                $strcont = $b;
                $origoffset = $p;
                $cont++;
            } else if ($cont < MAX_DB_ON_LINE) {
                $strcont .= ", $c";
                $cont++;
            } else {
                $strcont .= ", $c";
                echo LINE_PADDING . str_pad($strcont, DATA_PADDING, " ", STR_PAD_RIGHT) . "; 0x" . str_pad(dechex($origoffset), OFFSET_PADDING, "0", STR_PAD_LEFT) . "\n";
                $strcont = "";
                $cont = 0;
            }
            
            continue;
        } else if ($c == 'cb') {
            // Bit instruction
            $c = gethexval($h);
            $b .= ", " . $c;
            $instr = $bitop[$c];
        } else {
            // Is the length of the opcode > 1?
            if ($opcodes[$c][1] > 1) {
                if (!isset($opcodes[$c][3])) {
                    // Most of the instructions
                    $instr = $opcodes[$c][0]." ".$opcodes[$c][2];
                    for ($i = 1; $i < $opcodes[$c][1]; $i++) {
                        $v = gethexval($h);    // Ascii code number
                        $b .= ", $v";
                        $instr = str_replace("!$i", $v, $instr, $strcount);
                    }
                } else {

                    if ($opcodes[$c][3] == 1) {
                        // Relative jump
                        $v = gethexval($h);
                        $b .= ", $v";
                        $v = hexdec($v);
                        $oper = $v >> 7;
                        $val = $v & 0x7F;
                        $res = $oper ? ($p - (0x7E - $val)) : ($p + $val);                    
                    } else {
                        // Absolute jump
                        for ($i = 1, $res = ''; $i < $opcodes[$c][1]; $i++) {
                            $v = gethexval($h);
                            $b .= ", $v";
                            $v = hexdec($v);
                            $res .= $v;
                        }                
                    }
                    $instr = $opcodes[$c][0]." LABEL_".strtoupper(dechex($res));
                }
            } else {
                $instr = $opcodes[$c][0];
            }
        }
        
        // If we didn't reach the 16th data byte, print the result here
        if ($cont != 0) {
            echo LINE_PADDING . str_pad($strcont, DATA_PADDING, " ", STR_PAD_RIGHT) . "; 0x" . str_pad(dechex($origoffset), OFFSET_PADDING, "0", STR_PAD_LEFT) . "\n";
            $strcont = "";
            $cont = 0;
        }
        
        echo LINE_PADDING . str_pad(strtr($instr, $replacements), CODE_PADDING, " ", STR_PAD_RIGHT) . " ; " . str_pad($b, DATA_PADDING, " ", STR_PAD_RIGHT) . "; 0x" . str_pad(dechex($p), OFFSET_PADDING, "0", STR_PAD_LEFT) . "\n";
        
    }
    
    
    function gethexval($handle) { return str_pad(strtoupper(dechex(ord(fgetc($handle)))), 2, "0", STR_PAD_LEFT); }