From: slack Date: Sat, 20 Sep 2008 22:09:56 +0000 (+0200) Subject: Added graph output (via dot) to wendi X-Git-Url: http://slack.codemaniacs.com/git/?a=commitdiff_plain;h=3470442c303feb910c55f29655c41120f62a591e;p=wenboi.git Added graph output (via dot) to wendi --- diff --git a/Makefile b/Makefile index 475c335..61f2172 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ #CXXFLAGS=-pg -O3 -g -Wall -Weffc++ -Wstrict-null-sentinel -Wold-style-cast -CXXFLAGS=-pg -O3 -g -Wall -Weffc++ -Wold-style-cast \ +CXXFLAGS=-pg -g -Wall -Weffc++ -Wold-style-cast \ -Woverloaded-virtual $(shell sdl-config --cflags) LDFLAGS=-pg -g $(shell sdl-config --libs) @@ -42,7 +42,14 @@ wendi/CodeBlock.o: wendi/CodeBlock.cc wendi/CodeBlock.h wendi/disasm.o: wendi/disasm.cc wendi/disasm.h wendi/Instruction.h g++ $(CXXFLAGS) -c -o $@ $< -wendi/wendi: wendi/wendi.cc wendi/CodeBlock.o wendi/disasm.o gbcore.o MBC.o \ +wendi/output_txt.o: wendi/output_txt.cc wendi/output_txt.h wendi/disassembly_output.h + g++ $(CXXFLAGS) -c -o $@ $< + +wendi/output_graph.o: wendi/output_graph.cc wendi/output_graph.h wendi/disassembly_output.h Logger.h + g++ $(CXXFLAGS) -c -o $@ $< + +wendi/wendi: wendi/wendi.cc wendi/CodeBlock.o wendi/disasm.o \ + wendi/output_txt.o wendi/output_graph.o gbcore.o MBC.o \ GBMemory.o GBRom.o GBVideo.o util.o NoMBC.o MBC1.o g++ $(CXXFLAGS) -o $@ $^ $(LDFLAGS) diff --git a/wendi/CodeBlock.cc b/wendi/CodeBlock.cc index 091417d..5c99570 100644 --- a/wendi/CodeBlock.cc +++ b/wendi/CodeBlock.cc @@ -30,7 +30,10 @@ CodeBlock::CodeBlock(CodeBlock &block, address addr): //< removes [addr,end[ fro block.disassembly.end(), compose1(bind2nd(equal_to
(), addr), select1st())); + DisassemblyIterator tmp = first; + this->add_xref((--tmp)->first, Instruction::OTHER); DisassemblyIterator last = block.disassembly.end(); + disassembly.splice(disassembly.end(), block.disassembly, first, last); } diff --git a/wendi/disassembly_output.h b/wendi/disassembly_output.h new file mode 100644 index 0000000..a4123eb --- /dev/null +++ b/wendi/disassembly_output.h @@ -0,0 +1,16 @@ +#ifndef DISASSEMBLY_OUTPUT_H +#define DISASSEMBLY_OUTPUT_H + +#include "../gbcore.h" +#include "CodeBlock.h" +#include + +class DisassemblyOutput +{ + public: + virtual void generate_output(GameBoy &gb, std::vector &v)=0; + virtual ~DisassemblyOutput() {} +}; + +#endif + diff --git a/wendi/output_graph.cc b/wendi/output_graph.cc new file mode 100644 index 0000000..b67556b --- /dev/null +++ b/wendi/output_graph.cc @@ -0,0 +1,132 @@ +#include "output_graph.h" +#include +#include +#include "../Logger.h" + +using std::vector; +using std::string; +using std::endl; + +void GraphDisassemblyOutput::show_disassembly_block(const CodeBlock& b) +{ + std::ostringstream addrstr, inststr; + + // Generate an input port "" for the node at the label + out << "node [label=\"{{"; + + // Print header to stdout + if (b.type & CodeBlock::FUNCTION) + out << ";-- FUNCTION --------------------------\\l"; + if (b.type & CodeBlock::VBLANK_HANDLER) + out << ";-- VBLANK INTERRUPT HANDLER ----------\\l"; + if (b.type & CodeBlock::LCD_STAT_HANDLER) + out << ";-- LCD_STAT INTERRUPT HANDLER --------\\l"; + if (b.type & CodeBlock::TIMER_HANDLER) + out << ";-- TIMER INTERRUPT HANDLER -----------\\l"; + if (b.type & CodeBlock::SERIAL_HANDLER) + out << ";-- SERIAL INTERRUPT HANDLER ----------\\l"; + if (b.type & CodeBlock::JOYPAD_HANDLER) + out << ";-- JOYPAD INTERRUPT HANDLER ----------\\l"; + + if (b.type & CodeBlock::ENTRYPOINT) + out << ";-- ENTRYPOINT ------------------------\\l"; + + out << b.name << ":\\l}|{"; + + + // Print disassembly + for(CodeBlock::DisassemblyConstIterator i=b.disassembly.begin(); + i!=b.disassembly.end(); + i++) + { + addrstr << std::hex << "0x" << + std::setw(4) << std::setfill('0') << i->first << "\\l|"; + + CodeBlock::DisassemblyConstIterator j=i; + // Generate an output port "" for the node at the last instruction + if (++j == b.disassembly.end()) + inststr << ""; + inststr << i->second << "\\l|"; + } + string addr=addrstr.str(); + string inst=inststr.str(); + addr[addr.size()-1]=' '; + inst[inst.size()-1]=' '; + out << "{" << addr << "}|{" << inst << "}}}\"] " << b.name << ";" << endl; +} + +void GraphDisassemblyOutput::show_xrefs(vector &v) +{ + for (vector::iterator i=v.begin(); + i != v.end(); i++) + { + for (CodeBlock::XrefsIterator x = i->xrefs.begin(); + x != i->xrefs.end(); + x++) + { + int src = x->first; + vector::iterator src_block=v.end(); + + for (vector::iterator j=v.begin(); + j!=v.end(); j++) + { + if (src >= j->start && src < j->end) src_block = j; + } + + if (src_block != v.end()) + { + string color = (i->start < src) ? "red" : "green"; + out << src_block->name << ":out:sw ->" << i->name << ":in:n" << + "[color=" << color << "];" << endl; + } + else + { + logger.error("Dangling xref src=", std::hex, src, " dst=", i->start); + } + } + } +} + + +void GraphDisassemblyOutput::show_jump_table_block(GameBoy &gb, const CodeBlock &b) +{ + /* + int n = (b.end - b.start)/2; + + out << ";-- JUMP TABLE ------------------------" << endl; + for (int i=0; i &v) +{ + //std::for_each(tmp.begin(), tmp.end(), show_block); + + out << "digraph disassembly {" << endl << + "splines=polyline;" << endl << + "node [shape=record, fontname=fixed];" << endl; + + for (vector::iterator i=v.begin(); + i != v.end(); i++) + { + switch (i->type) + { + case CodeBlock::JUMP_TABLE: + show_jump_table_block(gb, *i); + break; + default: + show_disassembly_block(*i); + } + } + + show_xrefs(v); + + out << "}" << endl; +} + + diff --git a/wendi/output_graph.h b/wendi/output_graph.h new file mode 100644 index 0000000..8a69664 --- /dev/null +++ b/wendi/output_graph.h @@ -0,0 +1,23 @@ +#ifndef OUTPUT_GRAPH +#define OUTPUT_GRAPH +#include "disassembly_output.h" +#include "CodeBlock.h" +#include +#include + +class GraphDisassemblyOutput: public DisassemblyOutput +{ + std::ostream &out; + + void show_disassembly_block(const CodeBlock& b); + void show_jump_table_block(GameBoy &gb, const CodeBlock &b); + void show_xrefs(std::vector &v); + + public: + GraphDisassemblyOutput(std::ostream &ofs): out(ofs) {} + void generate_output(GameBoy &gb, std::vector &v); +}; + +#endif + + diff --git a/wendi/output_txt.cc b/wendi/output_txt.cc new file mode 100644 index 0000000..08bb1cf --- /dev/null +++ b/wendi/output_txt.cc @@ -0,0 +1,128 @@ +#include "output_txt.h" +#include +#include + +using std::vector; +using std::string; + +void TextDisassemblyOutput::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 << ","; + } + + xrefsstr << std::endl; + + // Print header to stdout + if (b.type & CodeBlock::FUNCTION) + out << std::endl << ";-- FUNCTION --------------------------" << std::endl; + if (b.type & CodeBlock::VBLANK_HANDLER) + out << std::endl << ";-- VBLANK INTERRUPT HANDLER ----------" << std::endl; + if (b.type & CodeBlock::LCD_STAT_HANDLER) + out << std::endl << ";-- LCD_STAT INTERRUPT HANDLER --------" << std::endl; + if (b.type & CodeBlock::TIMER_HANDLER) + out << std::endl << ";-- TIMER INTERRUPT HANDLER -----------" << std::endl; + if (b.type & CodeBlock::SERIAL_HANDLER) + out << std::endl << ";-- SERIAL INTERRUPT HANDLER ----------" << std::endl; + if (b.type & CodeBlock::JOYPAD_HANDLER) + out << std::endl << ";-- JOYPAD INTERRUPT HANDLER ----------" << std::endl; + + if (b.type & CodeBlock::ENTRYPOINT) + out << std::endl << ";-- ENTRYPOINT ------------------------" << std::endl; + + out << b.name << ":"; + + out << xrefsstr.str(); + + // Print disassembly + for(CodeBlock::DisassemblyConstIterator i=b.disassembly.begin(); + i!=b.disassembly.end(); + i++) + { + out << std::hex << "\t0x" << + std::setw(4) << std::setfill('0') << i->first << + "\t" << i->second << std::endl; + } +} + +void TextDisassemblyOutput::show_jump_table_block(GameBoy &gb, const CodeBlock &b) +{ + int n = (b.end - b.start)/2; + + out << ";-- JUMP TABLE ------------------------" << std::endl; + for (int i=0; i start) + { + out << std::endl << ";-- HEXDUMP ---------------------------"; + address i=start - (start%0x10); + if (i &v) +{ + //std::for_each(tmp.begin(), tmp.end(), show_block); + + const address MAX_ADDRESS = 0xFFFF; + address last = 0; + for (vector::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); +} + diff --git a/wendi/output_txt.h b/wendi/output_txt.h new file mode 100644 index 0000000..dc129d3 --- /dev/null +++ b/wendi/output_txt.h @@ -0,0 +1,22 @@ +#ifndef OUTPUT_TXT +#define OUTPUT_TXT +#include "disassembly_output.h" +#include "CodeBlock.h" +#include +#include + +class TextDisassemblyOutput: public DisassemblyOutput +{ + std::ostream &out; + + void show_disassembly_block(const CodeBlock& b); + void show_jump_table_block(GameBoy &gb, const CodeBlock &b); + void hexdump(GameBoy &gb, address start, address end); + + public: + TextDisassemblyOutput(std::ostream &ofs): out(ofs) {} + void generate_output(GameBoy &gb, std::vector &v); +}; + +#endif + diff --git a/wendi/wendi.cc b/wendi/wendi.cc index 8145b40..b3c6741 100644 --- a/wendi/wendi.cc +++ b/wendi/wendi.cc @@ -1,5 +1,8 @@ #include "../gbcore.h" #include "../Logger.h" +#include "disassembly_output.h" +#include "output_txt.h" +#include "output_graph.h" #include "CodeBlock.h" #include "disasm.h" @@ -91,96 +94,40 @@ void classify_block(CodeBlock &b) } else { - block_name << "block"; + block_name << "loc"; } - block_name << "_0x" << std::hex << std::setw(4) << std::setfill('0') << b.start; + block_name << "_" << 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) +bool does_fall_through(const Instruction &ins) { - int n = (b.end - b.start)/2; - - std::cout << ";-- JUMP TABLE ------------------------" << std::endl; - for (int i=0; ifirst << ","; - } - - xrefsstr << std::endl; - - // Print header to stdout - 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.type & CodeBlock::ENTRYPOINT) - std::cout << std::endl << ";-- ENTRYPOINT ------------------------" << std::endl; - - std::cout << b.name << ":"; - - std::cout << xrefsstr.str(); - - // Print disassembly - for(CodeBlock::DisassemblyConstIterator i=b.disassembly.begin(); - i!=b.disassembly.end(); - i++) - { - 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) +// true if it's a jump with known destination address +bool is_jump(const Instruction &ins) { - if (ins.type == Instruction::UNCONDITIONAL_JUMP || - ins.type == Instruction::UNCONDITIONAL_RET || + if (ins.type == Instruction::UNCONDITIONAL_JUMP || + ins.type == Instruction::CONDITIONAL_JUMP || + ins.type == Instruction::CALL || ins.type == Instruction::RESET) return true; - return false; } -bool is_jump(const Instruction &ins) +bool is_block_end(const Instruction &ins) { - if (ins.type == Instruction::UNCONDITIONAL_JUMP || - ins.type == Instruction::CONDITIONAL_JUMP || - ins.type == Instruction::CALL || - ins.type == Instruction::RESET) + if (is_jump(ins) || + ins.type == Instruction::CONDITIONAL_RET || + ins.type == Instruction::UNCONDITIONAL_RET) return true; return false; } @@ -233,64 +180,58 @@ list::iterator find_block(list &l, address addr) //logger.trace("<-- find_block()"); return i; } + -void hexdump(GameBoy &gb, address start, address end) +void new_block_start(address dst, address src, Instruction::InstructionType type, CodeBlock ¤t, + list &blocks, list &pending) { - if (end > start) + if (dst == current.start) // Check if dst is this block's beginning + { + current.add_xref(src, type); + } + else if (dst > current.start && dst < current.end) // Check if dst is inside this block { - std::cout << std::endl << ";-- HEXDUMP ---------------------------"; - address i=start - (start%0x10); - if (i::iterator i = find_block(blocks, dst); + if (i != blocks.end()) { - std::cout << std::endl << ";" << std::hex << "0x" << - std::setw(4) << std::setfill('0') << start << " "; - while(i < start) + if (dst == i->start) { - if (i % 0x10 == 8) - std::cout << "- "; - std::cout << " "; - i++; + i->add_xref(src, type); + } + else + { + logger.info("Splitting block 0x", std::hex, i->start, " at 0x", dst); + blocks.push_back(CodeBlock(*i, dst)); + blocks.back().add_xref(src, type); } - } - - while(i &v) -{ - //std::for_each(tmp.begin(), tmp.end(), show_block); - - const address MAX_ADDRESS = 0xFFFF; - address last = 0; - for (vector::iterator i=v.begin(); - i != v.end(); i++) - { - hexdump(gb, last, i->start); - switch (i->type) + else { - case CodeBlock::JUMP_TABLE: - show_jump_table_block(gb, *i); - break; - default: - show_disassembly_block(*i); + // Check if dst is a pending block + i = find_block(pending, dst); + if (i != pending.end()) + { + logger.info("Adding xref to pending block 0x", std::hex, i->start); + i->add_xref(src, type); + } + else + { + // dst is a new block + pending.push_back(CodeBlock(dst)); + pending.back().add_xref(src, type); + logger.info("new block at ", std::hex, "0x", dst); + } } - last = i->end; } - - hexdump(gb, v.back().end, MAX_ADDRESS); } int main(int argc, char **argv) @@ -378,67 +319,25 @@ int main(int argc, char **argv) { address dst = jump_address(ins); logger.info("Found jump. dst address = 0x", std::hex, dst); - if (dst == block.start) // Check if dst is this block's beginning - { - block.add_xref(addr, ins.type); - } - else if (dst > block.start && dst < block.end) // Check if dst is inside this block - { - logger.info("Splitting current block 0x", std::hex, block.start, " at 0x", dst); - CodeBlock newblock(block, dst); - blocks.push_back(block); - block = newblock; - } - else - { - // Check if dst is inside a known block - list::iterator i = find_block(blocks, dst); - if (i != blocks.end()) - { - if (dst == i->start) - { - i->add_xref(addr, ins.type); - } - else - { - logger.info("Splitting block 0x", std::hex, i->start, " at 0x", dst); - blocks.push_back(CodeBlock(*i, dst)); - blocks.back().add_xref(addr, ins.type); - } - - } - else - { - // Check if dst is a pending block - i = find_block(pending, dst); - if (i != pending.end()) - { - logger.info("Adding xref to pending block 0x", std::hex, i->start); - i->add_xref(addr, ins.type); - } - else - { - // dst is a new block - pending.push_back(CodeBlock(dst)); - pending.back().add_xref(addr, ins.type); - logger.info("new block at ", std::hex, "0x", dst); - } - } - } + new_block_start(dst, addr, ins.type, block, blocks, pending); } - addr += ins.length; + address new_addr = 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() || + if (find_block(blocks, new_addr) != blocks.end() || + find_block(pending, new_addr) != pending.end() || is_block_end(ins)) { block_end=true; + if (does_fall_through(ins)) + { + new_block_start(new_addr, addr, Instruction::OTHER, + block, blocks, pending); + } } - - + addr = new_addr; } blocks.push_back(block); @@ -450,7 +349,9 @@ int main(int argc, char **argv) std::sort(tmp.begin(), tmp.end()); std::for_each(tmp.begin(), tmp.end(), classify_block); - show_disassembly(gb, tmp); + GraphDisassemblyOutput output(std::cout); + //TextDisassemblyOutput output(std::cout); + output.generate_output(gb, tmp); return 0; }