disassembler works now :D
authorslack <slack@codemaniacs.com>
Sun, 17 Aug 2008 01:30:43 +0000 (03:30 +0200)
committerslack <slack@codemaniacs.com>
Sun, 17 Aug 2008 01:30:43 +0000 (03:30 +0200)
wendi/CodeBlock.cc
wendi/CodeBlock.h
wendi/Instruction.h
wendi/disasm.cc
wendi/wendi.cc

index 06f4698d2de07cb6e134a3d22840cc6e92fe22de..091417dc8105467195472ffa9eff9fe0a1d45aae 100644 (file)
@@ -4,17 +4,21 @@
 #include <ext/functional>
 
 CodeBlock::CodeBlock(address start): //< creates an empty CodeBlock 
+       type(BLOCK),
        start(start),
        end(start),
        disassembly(),
-       xrefs()
+       xrefs(),
+       name()
 {}
 
 CodeBlock::CodeBlock(CodeBlock &block, address addr): //< removes [addr,end[ from the block creating a new one
+       type(BLOCK),
        start(addr),
        end(block.end),
        disassembly(),
-       xrefs()
+       xrefs(),
+       name()
 {
        using std::bind2nd;
        using __gnu_cxx::select1st;
@@ -30,6 +34,15 @@ CodeBlock::CodeBlock(CodeBlock &block, address addr): //< removes [addr,end[ fro
        disassembly.splice(disassembly.end(), block.disassembly, first, last);
 }
 
+CodeBlock::CodeBlock(CodeBlockType type, address start, address end):
+       type(type),
+       start(start),
+       end(end),
+       disassembly(),
+       xrefs(),
+       name()
+{
+}
 
 void CodeBlock::add_instruction(std::string ins, int nbytes) // appends an instruction to the end of the block
 {
index acffda57cf066abbf254bb972a4ee34c2337f05d..4d4b8df14888ba1b133f52ddb6e4690f7fa4042f 100644 (file)
@@ -13,6 +13,20 @@ typedef u16 address;
 class CodeBlock
 {
        public:
+       enum CodeBlockType
+       {
+               BLOCK            = 0x000,
+               FUNCTION         = 0x001,
+               VBLANK_HANDLER   = 0x002,
+               LCD_STAT_HANDLER = 0x004,
+               TIMER_HANDLER    = 0x008,
+               SERIAL_HANDLER   = 0x010,
+               JOYPAD_HANDLER   = 0x020,
+               ENTRYPOINT       = 0x040,
+               JUMP_TABLE       = 0x080,
+               JUMP_TABLE_DEST  = 0x100,
+       };
+
        typedef std::pair<address, std::string> DisassemblyItem;
        typedef std::pair<address, Instruction::InstructionType> XrefsItem;
        typedef std::list<DisassemblyItem>      DisassemblyList;
@@ -23,13 +37,17 @@ class CodeBlock
        typedef XrefsVector::iterator           XrefsIterator;
        typedef XrefsVector::const_iterator     XrefsConstIterator;
 
+       int type;
+
        address start, end;  // block is [start, end[
        DisassemblyList disassembly;
        XrefsVector xrefs;
 
+       std::string name;
 
        CodeBlock(address start); //< creates an empty CodeBlock 
        CodeBlock(CodeBlock &block, address addr); //< removes [addr,end[ from the block creating a new one
+       CodeBlock(CodeBlockType type, address start, address end); // Creates a "raw" block
 
        int length() { return end-start; }
        void add_instruction(std::string ins, int nbytes); // appends an instruction to the end of the block
index 41aa0aabe4dea9a17309413203948b4a947dd6ac..8698f42323171076da17ae96bfaebfdc19d09f41 100644 (file)
@@ -9,19 +9,21 @@ struct Instruction
        {
                UNCONDITIONAL_JUMP=0,
                CONDITIONAL_JUMP,
+               UNCONDITIONAL_RET,
+               CONDITIONAL_RET,
                CALL,
-               RET,
                RESET,
                ALU,
                LOAD,
-               OTHER
+               OTHER,
+               JUMP_TABLE_JUMP,
        };
 
        enum InstructionSubType
        {
                JP,
                JR,
-       }
+       };
 
        enum Register { A=0,B,C,D,E,H,L,AF,BC,DE,HL,SP,PC };
 
index ddb87d09436a242374ead0bfcb02d10af047f96e..11f201fae46bcf0bcdd0688dc24190664044a2af 100644 (file)
@@ -503,14 +503,14 @@ Instruction disassemble_opcode(GameBoy &gb, u16 addr)
                dis(0xFF, "RST 0x38", Instruction::RESET)
 
                // Returns
-               dis(0xC9, "RET", Instruction::RET)
+               dis(0xC9, "RET", Instruction::UNCONDITIONAL_RET)
                // RET cc
-               dis(0xC0, "RET NZ", Instruction::RET)
-               dis(0xC8, "RET Z",  Instruction::RET)
-               dis(0xD0, "RET NC", Instruction::RET)
-               dis(0xD8, "RET C",  Instruction::RET)
+               dis(0xC0, "RET NZ", Instruction::CONDITIONAL_RET)
+               dis(0xC8, "RET Z",  Instruction::CONDITIONAL_RET)
+               dis(0xD0, "RET NC", Instruction::CONDITIONAL_RET)
+               dis(0xD8, "RET C",  Instruction::CONDITIONAL_RET)
 
-               dis(0xD9, "RETI", Instruction::RET)
+               dis(0xD9, "RETI", Instruction::UNCONDITIONAL_RET)
 
                default:
                        std::ostringstream errmsg;
index e7ea524343e5fad0a63190df161ed7e0653f2bc8..421f27efb2eb3ce975c43a318046a2af8cd6be0a 100644 (file)
@@ -8,9 +8,12 @@
 #include <string>
 #include <list>
 #include <iostream>
+#include <fstream>
+#include <iomanip>
 #include <sstream>
 #include <algorithm>
 #include <functional>
+#include <cstdlib>
 //#include <tr1/unordered_map>
 
 using std::vector;
@@ -24,72 +27,147 @@ typedef u16 address;
 list<CodeBlock> blocks;
 list<CodeBlock> pending;
 
-void show_block(const CodeBlock& b)
+
+void classify_block(CodeBlock &b)
 {
-       bool is_CALLed = false;
-       bool is_VBLANK_interrupt_handler   = false;
-       bool is_LCD_STAT_interrupt_handler = false;
-       bool is_TIMER_interrupt_handler    = false;
-       bool is_SERIAL_interrupt_handler   = false;
-       bool is_JOYPAD_interrupt_handler   = false;
-       std::ostringstream headerstr;
-       
-       // Create header, check for CALL xrefs
-       headerstr << "block_0x" << std::hex << b.start << ":";
-       if (!b.xrefs.empty())
-               headerstr << "\t;xrefs ";
+       std::ostringstream block_name;
 
+       if (b.start == 0x100)
+               b.type |= CodeBlock::ENTRYPOINT;
+
+       // check for CALL xrefs; build xrefs list string
+       std::sort(b.xrefs.begin(), b.xrefs.end());
        for(CodeBlock::XrefsConstIterator i=b.xrefs.begin();
                        i!=b.xrefs.end();
                        i++)
        {
-               headerstr << std::hex << "0x" << i->first << ",";
                if (i->second == Instruction::CALL)
-                       is_CALLed = true;
+                       b.type |= CodeBlock::FUNCTION;
+               if (i->second == Instruction::JUMP_TABLE_JUMP)
+                       b.type |= CodeBlock::JUMP_TABLE_DEST;
                if (i->first == 0x40)
-                       is_VBLANK_interrupt_handler   = true;
+                       b.type |= CodeBlock::VBLANK_HANDLER;
                if (i->first == 0x48)
-                       is_LCD_STAT_interrupt_handler = true;
+                       b.type |= CodeBlock::LCD_STAT_HANDLER;
                if (i->first == 0x50)
-                       is_TIMER_interrupt_handler    = true;
+                       b.type |= CodeBlock::TIMER_HANDLER;
                if (i->first == 0x58)
-                       is_SERIAL_interrupt_handler   = true;
+                       b.type |= CodeBlock::SERIAL_HANDLER;
                if (i->first == 0x60)
-                       is_JOYPAD_interrupt_handler   = true;
+                       b.type |= CodeBlock::JOYPAD_HANDLER;
+       }
+
+       if (b.type & CodeBlock::ENTRYPOINT)
+               block_name << "entrypoint";
+       else if (b.start == 0x40)
+               block_name << "vblank_interrupt";
+       else if (b.start == 0x48)
+               block_name << "lcd_stat_interrupt";
+       else if (b.start == 0x50)
+               block_name << "timer_interrupt";
+       else if (b.start == 0x58)
+               block_name << "serial_interrupt";
+       else if (b.start == 0x60)
+               block_name << "joypad_interrupt";
+       else
+       {
+               if (b.type & 
+                               (CodeBlock::VBLANK_HANDLER |
+                                CodeBlock::LCD_STAT_HANDLER |
+                                CodeBlock::TIMER_HANDLER |
+                                CodeBlock::SERIAL_HANDLER | 
+                                CodeBlock::JOYPAD_HANDLER))
+               {
+                       block_name << "handler";
+               }
+               else if (b.type & CodeBlock::FUNCTION)
+               {
+                       block_name << "function";
+               }
+               else if (b.type & CodeBlock::JUMP_TABLE_DEST)
+               {
+                       block_name << "jump";
+               }
+               else
+               {
+                       block_name << "block";
+               }
+
+               block_name << "_0x" << std::hex << std::setw(4) << std::setfill('0') << b.start;
+       }
+
+       b.name = block_name.str();
+}
+
+
+void show_jump_table_block(GameBoy &gb, const CodeBlock &b)
+{
+       int n = (b.end - b.start)/2;
+
+       std::cout << ";-- JUMP TABLE ------------------------" << std::endl;
+       for (int i=0; i<n; i++)
+       {
+               address addr = b.start+2*i;
+               std::cout << "\t0x" << std::hex << std::setw(4) <<  std::setfill('0') << addr <<
+                       "\t" <<  i << "\t0x" << std::setw(2) << gb.memory.read16(addr) << std::endl;;
+       }
+}
+
+
+
+void show_disassembly_block(const CodeBlock& b)
+{
+       std::ostringstream xrefsstr;
+       
+       if (!b.xrefs.empty())
+               xrefsstr << "\t;xrefs ";
+
+       // check for CALL xrefs; build xrefs list string
+       for(CodeBlock::XrefsConstIterator i=b.xrefs.begin();
+                       i!=b.xrefs.end();
+                       i++)
+       {
+               xrefsstr << std::hex << "0x" << std::setw(4) << std::setfill('0') << i->first << ",";
        }
        
-       headerstr << std::endl;
+       xrefsstr << std::endl;
        
        // Print header to stdout
-       if (is_CALLed)
-               std::cout << std::endl << "-- FUNCTION --------------------------" << std::endl;
-       if (is_VBLANK_interrupt_handler)
-               std::cout << std::endl << "-- VBLANK INTERRUPT HANDLER ----------" << std::endl;
-       if (is_LCD_STAT_interrupt_handler)
-               std::cout << std::endl << "-- LCD_STAT INTERRUPT HANDLER --------" << std::endl;
-       if (is_TIMER_interrupt_handler)
-               std::cout << std::endl << "-- TIMER INTERRUPT HANDLER -----------" << std::endl;
-       if (is_SERIAL_interrupt_handler)
-               std::cout << std::endl << "-- SERIAL INTERRUPT HANDLER ----------" << std::endl;
-       if (is_JOYPAD_interrupt_handler)
-               std::cout << std::endl << "-- JOYPAD INTERRUPT HANDLER ----------" << std::endl;
+       if (b.type & CodeBlock::FUNCTION)
+               std::cout << std::endl << ";-- FUNCTION --------------------------" << std::endl;
+       if (b.type & CodeBlock::VBLANK_HANDLER)
+               std::cout << std::endl << ";-- VBLANK INTERRUPT HANDLER ----------" << std::endl;
+       if (b.type & CodeBlock::LCD_STAT_HANDLER)
+               std::cout << std::endl << ";-- LCD_STAT INTERRUPT HANDLER --------" << std::endl;
+       if (b.type & CodeBlock::TIMER_HANDLER)
+               std::cout << std::endl << ";-- TIMER INTERRUPT HANDLER -----------" << std::endl;
+       if (b.type & CodeBlock::SERIAL_HANDLER)
+               std::cout << std::endl << ";-- SERIAL INTERRUPT HANDLER ----------" << std::endl;
+       if (b.type & CodeBlock::JOYPAD_HANDLER)
+               std::cout << std::endl << ";-- JOYPAD INTERRUPT HANDLER ----------" << std::endl;
 
-       if (b.start == 0x100)
-               std::cout << std::endl << "-- ENTRYPOINT -----------------" << std::endl;
+       if (b.type & CodeBlock::ENTRYPOINT)
+               std::cout << std::endl << ";-- ENTRYPOINT ------------------------" << std::endl;
+       
+       std::cout << b.name << ":";
 
-       std::cout << headerstr.str();
+       std::cout << xrefsstr.str();
 
        // Print disassembly
        for(CodeBlock::DisassemblyConstIterator i=b.disassembly.begin();
                        i!=b.disassembly.end();
                        i++)
-               std::cout << std::hex << "0x" << i->first << "\t" << i->second << std::endl;
+       {
+               std::cout << std::hex << "\t0x" << 
+                       std::setw(4) << std::setfill('0') << i->first << 
+                       "\t" << i->second << std::endl;
+       }
 }
 
 bool is_block_end(const Instruction &ins)
 {
        if (ins.type == Instruction::UNCONDITIONAL_JUMP || 
-                       ins.type == Instruction::RET ||
+                       ins.type == Instruction::UNCONDITIONAL_RET ||
                        ins.type == Instruction::RESET)
                return true;
 
@@ -155,6 +233,65 @@ list<CodeBlock>::iterator find_block(list<CodeBlock> &l, address addr)
        return i;
 }
 
+void hexdump(GameBoy &gb, address start, address end)
+{
+       if (end > start)
+       {
+               std::cout << std::endl << ";-- HEXDUMP ---------------------------";
+               address i=start - (start%0x10);
+               if (i<start)
+               {
+                       std::cout << std::endl << ";" << std::hex << "0x" << 
+                               std::setw(4) << std::setfill('0') << start << " ";
+                       while(i < start)
+                       {
+                               if (i % 0x10 == 8)
+                                       std::cout << "- ";
+                               std::cout << "   ";
+                               i++;
+                       }
+               }
+
+               while(i<end)
+               {
+                       if (i % 0x10 == 0)
+                               std::cout << std::endl << ";" << std::hex << "0x" << i << " ";
+
+                       if (i % 0x10 == 8)
+                               std::cout << "- ";
+
+                       std::cout << std::hex << std::setw(2) << std::setfill('0') << int(gb.memory.read(i)) << " ";
+                       i++;
+               }
+
+               std::cout << std::endl << std::endl;
+       }
+}
+
+void show_disassembly(GameBoy &gb, vector<CodeBlock> &v)
+{
+       //std::for_each(tmp.begin(), tmp.end(), show_block);
+       
+       const address MAX_ADDRESS = 0xFFFF;
+       address last = 0;
+       for (vector<CodeBlock>::iterator i=v.begin();
+                       i != v.end(); i++)
+       {
+               hexdump(gb, last, i->start);
+               switch (i->type)
+               {
+                       case CodeBlock::JUMP_TABLE:
+                               show_jump_table_block(gb, *i);
+                               break;
+                       default:
+                               show_disassembly_block(*i);
+               }
+               last = i->end;
+       }
+
+       hexdump(gb, v.back().end, MAX_ADDRESS);
+}
+
 int main(int argc, char **argv)
 {
        logger.set_log_level(Logger::TRACE);
@@ -169,6 +306,47 @@ int main(int argc, char **argv)
        pending.push_back(CodeBlock(0x58));
        pending.push_back(CodeBlock(0x60));
 
+       if (argc > 2)
+       {
+               std::ifstream config(argv[2]);
+               while (!config.eof())
+               {
+                       std::string cmd;
+                       config >> cmd;
+                       if (cmd == "block")
+                       {
+                               address a;
+                               config >> std::hex >> a;
+                               pending.push_back(CodeBlock(a));
+                       }
+                       else if (cmd == "jump_table")
+                       {
+                               address start, end;
+                               config >> std::hex >> start >> end;
+                               logger.trace("jump table block at 0x",std::hex, start);
+                               blocks.push_back(CodeBlock(CodeBlock::JUMP_TABLE, start, end));
+                               // add all destinations to pending
+                               int n = (end - start)/2;
+                               for (int i=0; i<n; i++)
+                               {
+                                       address src = start+2*i;
+                                       address dst = gb.memory.read16(src);
+                                       list<CodeBlock>::iterator i = find_block(pending, dst);
+                                       if (i == pending.end())
+                                       {
+                                               logger.trace("jump table dst block at 0x",std::hex, dst);
+                                               pending.push_back(CodeBlock(dst));
+                                               pending.back().add_xref(src, Instruction::JUMP_TABLE_JUMP);
+                                       }
+                                       else
+                                       {
+                                               i->add_xref(src, Instruction::JUMP_TABLE_JUMP);
+                                       }
+                               }
+                       }
+               }
+       }
+
        while(!pending.empty())
        {
                // Disassemble a block
@@ -237,12 +415,18 @@ int main(int argc, char **argv)
                                }
                        }
 
-                       if (is_block_end(ins))
+                       addr += ins.length;
+
+                       // If new addr is in another block, this block is over
+                       if (find_block(blocks, addr) != blocks.end() ||
+                               find_block(pending, addr) != pending.end() ||
+                               is_block_end(ins))
                        {
                                block_end=true;
                        }
+                               
+
 
-                       addr += ins.length;
                }
 
                blocks.push_back(block);
@@ -252,7 +436,9 @@ int main(int argc, char **argv)
        for (list<CodeBlock>::iterator i = blocks.begin(); i != blocks.end(); i++)
                tmp.push_back(*i);
        std::sort(tmp.begin(), tmp.end());      
-       std::for_each(tmp.begin(), tmp.end(), show_block);
-       
+       std::for_each(tmp.begin(), tmp.end(), classify_block);
+
+       show_disassembly(gb, tmp);
+
        return 0;
 }