#!/usr/local/bin/python class dis: def __init__(my, rom): my.rom = rom my.pc = 0 my.data = [] my.data16 = [] my.symbols = {} my.comments = {} def next8(my): return my.rom[my.pc + 1] def next8s(my): v = my.next8() if v < 128: return v else: return v - 256 def next16(my): return my.rom[my.pc + 1] + 256 * my.rom[my.pc + 2] def aluname(my, r): return ["add", "adc", "sub", "sbc", "and", "xor", "or", "cp"][r] def rotname(my, reg): return { 0: "rlc", 1: "rrc", 2: "rl", 3: "rr", 4: "sla", 5: "sra", 6: "swap", 7: "srl"}[reg] def condname(my, reg): return { 0: "nz", 1: "z", 2: "nc", 3: "c"}[reg] def rname(my, reg): return { 0: "b", 1: "c", 2: "d", 3: "e", 4: "h", 5: "l", 6: "(hl)", 7: "a"}[reg]; def rpname(my, reg): return { 0: "bc", 1: "de", 2: "hl", 3: "sp"}[reg]; def rp2name(my, reg): return { 0: "bc", 1: "de", 2: "hl", 3: "af"}[reg]; def sym(my, n): if 0x4000 <= n and n <= 0x7fff: page = my.pc / 0x4000 if page == 0: page = 1 n = page * 0x4000 + (n % 0x4000) if not my.symbols.has_key(n): my.symbols[n] = "L" + ("%04x" % n) return my.symbols[n] def msym(my, n): if my.symbols.has_key(n): return my.symbols[n] else: return "%04x" % n return my.symbols[n] def insn(my): op = my.rom[my.pc] if op != 0xcb: cbprefix = False else: cbprefix = True op = my.rom[my.pc+1] x = op >> 6 y = 7 & (op >> 3) z = 7 & op p = 3 & (op >> 4) q = 1 & (op >> 3) i = None if cbprefix: if x == 0: i = (2, "%s %s" % (my.rotname(y), my.rname(z))) elif x == 1: i = (2, "bit %d,%s" % (y, my.rname(z))) elif x == 2: i = (2, "res %d,%s" % (y, my.rname(z))) elif x == 3: i = (2, "set %d,%s" % (y, my.rname(z))) elif x == 0: if z == 0: if 4 <= y: i = (2, "jr %s,%s" % (my.condname(y - 4), my.sym(2 + my.pc + my.next8s()))) if z == 1: if q == 0: i = (3, "ld %s,%s" % (my.rpname(p), my.msym(my.next16()))) else: i = (1, "add hl,%s" % my.rpname(p)) elif z == 2: if y <= 3: i = (1, { 0: "ld (bc),a", 1: "ld a,(bc)", 2: "ld (de),a", 3: "ld a,(de)"}[y]) else: #i = (3, { 4: "ld (%s),hl", 5: "ld hl,(%s)", 6: "ld (%s),a", 7: "ld a,(%s)"}[y] % my.msym(my.next16())) pass elif z == 3: if q == 0: i = (1, "inc %s" % my.rpname(p)) else: i = (1, "dec %s" % my.rpname(p)) elif z == 4: i = (1, "inc %s" % my.rname(y)) elif z == 5: i = (1, "dec %s" % my.rname(y)) elif z == 6: i = (2, "ld %s,%s" % (my.rname(y), my.next8())) elif x == 1: if op == 0x76: i = (1, "halt") else: i = (1, "ld %s,%s" % (my.rname(y), my.rname(z))) elif x == 2: i = (1, "%-3s %s" % (my.aluname(y), my.rname(z))) elif x == 3: if z == 0: if y < 4: i = (1, "ret %s" % my.condname(y)) elif z == 1: if q == 0: i = (1, "pop %s" % my.rp2name(p)) elif z == 2: if y < 4: i = (3, "jump %s,%s" % (my.condname(y), my.sym(my.next16()))) elif z == 3: if y == 0: i = (3, "jump %s" % my.sym(my.next16())) elif z == 4: if y < 4: i = (3, "call %s,%s" % (my.condname(y), my.sym(my.next16()))) elif z == 5: if q == 0: i = (1, "push %s" % my.rp2name(p)) elif p == 0: i = (3, "call %s" % my.sym(my.next16())) elif z == 6: i = (2, "%-3s %02x" % (my.aluname(y), my.next8())) elif z == 7: i = (1, "rst %d ; %s" % (y, ["dot", "", "", "", "setframe", "", "", ""][y])) if i == None: decode = { 0000 : lambda: (1, "nop"), 0007 : lambda: (1, "rlca"), 0017 : lambda: (1, "rrca"), 0027 : lambda: (1, "rla"), 0037 : lambda: (1, "rra"), 0047 : lambda: (1, "daa"), 0057 : lambda: (1, "cpl"), 0067 : lambda: (1, "scf"), 0077 : lambda: (1, "ccf"), 0x08 : lambda: (3, "ld (%04x),sp" % my.next16()), 0x10 : lambda: (1, "stop"), 0x18 : lambda: (2, "jr %s" % my.sym(2 + my.pc + my.next8s())), 0x22 : lambda: (1, "ld (hli),a"), 0x2a : lambda: (1, "ld a,(hli)"), 0x32 : lambda: (1, "ld (hld),a"), 0x3a : lambda: (1, "ld a,(hld)"), 0xc9 : lambda: (1, "ret"), 0xd9 : lambda: (1, "reti"), 0xe0 : lambda: (2, "ld (%s),a" % my.msym(0xff00 + my.next8())), 0xe2 : lambda: (1, "ld (ff00+c),a"), 0xe9 : lambda: (1, "jp (hl)"), 0xea : lambda: (3, "ld (%s),a" % my.msym(my.next16())), 0xeb : lambda: (1, "ex de,hl"), 0xc3 : lambda: (3, "jmp %s" % my.sym(my.next16())), 0xf0 : lambda: (2, "ld a,(%s)" % my.msym(0xff00 + my.next8())), 0xf2 : lambda: (1, "ld a,(ff00+c)"), 0xf3 : lambda: (1, "di"), 0xf4 : lambda: (1, "?MYSTERY"), 0xf8 : lambda: (2, "ldhl sp,%02x" % my.next8()), 0xf9 : lambda: (1, "ld sp,hl"), 0xfa : lambda: (3, "ld a,(%s)" % my.msym(my.next16())), 0xfb : lambda: (1, "ei"), 0xe4 : lambda: (1, "data 0xe4"), } i = decode.get(my.rom[my.pc], lambda: None)() if i == None: print "--------------------------------------" print "op %02x" % op print "x", x, "y", y, "z", z return i # return (1,"?") # if ((op & 0300) == 0100): # i = (1, "ld %s,%s" % (my.r1(), my.r2())) # else: # return i def pass1(my): if my.pc in my.data: my.pc += 1 elif my.pc in my.data16: my.pc += 2 else: (Count, Op) = my.insn() my.pc += Count def step(my): if my.pc in my.data: print "%04x " % my.pc, for i in range(3): print " ", print " " * 8, print " " * 16, print " ", "byte %02x ; %s" % (my.rom[my.pc], chr(my.rom[my.pc])) my.pc += 1 elif my.pc in my.data16: print "%04x " % my.pc, for i in range(3): print " ", print " " * 8, print " " * 16, print " ", "word %04x" % (my.rom[my.pc] + 256 * my.rom[my.pc + 1]) my.pc += 2 else: (Count, Op) = my.insn() print "%04x " % my.pc, for i in range(3): if (i < Count): print "%02x" % my.rom[my.pc + i], else: print " ", print " " * 8, if (my.symbols.has_key(my.pc)): print "%-16s" % my.symbols[my.pc], else: print " " * 16, print " ", Op, if my.comments.has_key(my.pc): print " ;", my.comments[my.pc] else: print my.pc += Count F = open("jellyboy.gb", "r") S = F.read() D = dis([ord(C) for C in S]) D.data += [0xba] D.data += range(0x104, 0x150) D.data += range(0x530, 0x53c) D.data += range(0x090, 0x09a) D.data += range(0x0a5, 0x0b9) D.data += range(0x08d5, 0x08db) D.data += range(0x0d33, 0x0d4b) D.data += range(0x0d66, 0x0d66 + 10) D.data16 += range(0x3008a, 0x30096, 2) D.data16 += range(0x30155, 0x30161, 2) Jellyboy = [ (0x0003, "maybeincb"), (0x0059, "coldstart"), (0x00dc, "memzero"), (0x00dd, "memset"), (0x34b2, "memcpy"), (0x00bb, "lcdcsomething"), (0x0929, "waitvbi"), (0x0b25, "copytile"), (0x38e4, "zeroC000_C1FF"), (0x0c57, "clamp_hlde"), (0x0c60, "min_hlde"), (0x0c6a, "min_hlbc"), (0x091b, "switch"), (0xde2e, "cheat"), (0x1542, "bcd_dec16"), (0x15d0, "setlevel"), (0x1c31, "copy16"), (0x0907, "memswap"), (0x6e0, "loadscreen"), (0x33a2, "decompress"), (0x0a53, "addrcalc"), (0x30e2, "newproc"), (0x30aa, "initprocs"), (0x30d2, "allocproc"), (0x3162, "endproc"), (0x2bb4, "proc_ld_1b"), (0x2c4e, "proc_sgna_a"), (0x1d1a, "getattr_bcde"), (0x0a78, "drawscr"), (0x31b0, "runprocs"), (0x323e, "fork"), (0x31d6, "dot"), (0x263b, "ld_bcde"), (0x1d47, "isattr_0c"), (0x214e, "addr_1a_to_1c"), (0x2845, "getframe"), (0x284b, "setframe"), (0x2855, "newframe"), (0x286c, "frameinc_mod8"), (0x287c, "frameinc"), (0x2bc8, "framexor1"), (0x2428, "procmove"), (0x335e, "moveprocs"), (0x7fff, "BANKID"), (0xc200, "map"), (0xd200, "maplines"), (0xd670, "procram"), (0xde32, "proccount"), (0x3d894, "dot2"), (0x3d893, "dot3"), (0x3d892, "dot4"), (0x3d891, "dot5"), (0x3d890, "dot6"), (0x3d88f, "dot7"), (0x3d88e, "dot8"), (0x3d882, "dot128"), (0x3d87c, "framedec"), (0xff93, "maxXscroll"), (0xff94, "maxXscroll+1"), (0xff95, "maxYscroll"), (0xff96, "maxYscroll+1"), (0xff97, "mapwidth"), (0xff98, "mapheight"), (0xff9d, "screenX"), (0xff9e, "screenX+1"), (0xff9f, "tileX"), (0xffa0, "screenY"), (0xffa1, "screenY+1"), (0xffa2, "tileY"), (0xffb1, "lives"), (0xffb7, "notes"), (0xffd3, "level"), (0xffd4, "tilebank"), (0xffd5, "tilebase"), (0xffe2, "freeprocs"), (0xffe3, "freeprocs+1"), (0xffe4, "procs"), (0xffe5, "procs+1"), ] for (a,n) in Jellyboy: D.symbols[a] = n D.symbols[0x2150] = "BANKSEL" D.symbols[0xff00] = "P1" D.symbols[0xff0f] = "IF" D.symbols[0xff40] = "LCDC" D.symbols[0xff41] = "STAT" D.symbols[0xff42] = "SCY" D.symbols[0xff43] = "SCX" D.symbols[0xff44] = "LY" D.symbols[0xff45] = "LYC" D.symbols[0xff46] = "DMA" D.symbols[0xff47] = "BGP" D.symbols[0xff48] = "OBP0" D.symbols[0xff49] = "OBP1" D.symbols[0xff4a] = "WY" D.symbols[0xff4b] = "WX" D.symbols[0xffff] = "IE" D.comments[0x32c3] = "ff99 <= 0x10 - ff9d" D.comments[0x32da] = "ff9b <= 0x10 - ffa0" D.comments[0x0bad] = "ff9d <= min(ff9d,ff93)" D.comments[0x0bc2] = "ffa0 <= min(ffa0,ff95)" D.comments[0x0a53] = "Input (H,L), DE points into video at 9800, HL is the map char address" D.comments[0x2ac8] = "count procs?" D.comments[0x313e] = "set up the new proc's stack" D.comments[0x30e2] = "HL is program in page 15" coderanges = [ (0x0000, 0x4000), (12 * 0x4000, 12 * 0x4000 + 0x02df), (15 * 0x4000 + 0, 15 * 0x4000 + 0x3fff), ] coderanges = [] for (a, b) in coderanges: D.pc = a while D.pc < b: D.pass1() for (a, b) in coderanges: D.pc = a while D.pc < b: D.step() print # 04 Xpos # 06 Ypos # 08 Xvel # 09 Yvel # 0c flags: 2:moves # L3c34a import array import struct import os mem = array.array('B', S) def ptr24(a): (hl,a) = struct.unpack(">= 1 return b def getbyte(self): r = self.mem[self.a] self.a += 1 return r def gethuff(self, d): code = "" while not code in d: code += str(self.getbit()) assert len(code) < 8, 'bad code %s' % code return d[code] # http://segaretro.org/Rob_Northen_compression class Expander(object): def __init__(self, mem, a): assert mem[a] == ord('R') self.br = BitReader(mem, a + 18) self.dst = [] self.verbose = 0 def expand(self): self.br.getbit() self.br.getbit() maintab = { "0" : self.handle_lit, "10" : self.GETLEN, "110" : self.COPY2, "1110" : self.COPY3, "1111" : self.handle_copy } self.eof = False while not self.eof: self.br.gethuff(maintab)() if self.verbose: print len(self.dst), self.dst return self.dst def handle_lit(self): self.dst.append(self.br.getbyte()) distancetab = { "10" : 1, "000" : 2, "001" : 3, "0101" : 4, "0111" : 5, "1101" : 6, "1111" : 7, "01000" : 8, "01001" : 9, "01100" : 10, "01101" : 11, "11000" : 12, "11001" : 13, "11100" : 14, "11101" : 15 } def handle_copy(self): # 1111 # byte+8 -> C # if (bit ==0 -> bytedisp # # copy X+8 # + Distance X Y If X != 0, Get Y + 8 Bytes from (Distance * 256) + X + 1 b = self.br.getbyte() if b == 0: # Show's over self.eof = True return count = b + 8 self.bytedisp(count) def bytedisp(self, count): if self.br.getbit() == 0: offset = self.br.getbyte() + 1 if self.verbose: print 'offset', offset, 'count', count else: dist256 = self.br.gethuff(self.distancetab) if self.verbose: print 'dist256', dist256 offset = (256 * dist256) + self.br.getbyte() + 1 while count: self.dst.append(self.dst[-offset]) count -= 1 def GETLEN(self): lengthtab = { "00" : 4, "010" : 6, "011" : 7, "10" : 5, "110" : 8, "111" : 9 }; if self.verbose: print 'GETLEN' c = [4,5][self.br.getbit()] if self.br.getbit(): c -= 1 c = (2 * c) + self.br.getbit() if self.verbose: print 'c', c if c == 9: l = self.br.getbit() l = (l * 2) + self.br.getbit() l = (l * 2) + self.br.getbit() l = (l * 2) + self.br.getbit() l += 3 l *= 4 if self.verbose: print 'RAW bytes', l while l: self.dst.append(self.br.getbyte()) l -= 1 else: self.bytedisp(c) def COPY2(self): if self.verbose: print 'COPY2' offset = self.br.getbyte() + 1 self.dst.append(self.dst[-offset]) self.dst.append(self.dst[-offset]) def COPY3(self): if self.verbose: print 'COPY3' self.bytedisp(3) def decompress(a): e = Expander(mem, a) return e.expand() e = Expander(mem, ptr24(0x4395)) assert e.expand() == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 11, 11, 11, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] import Image # See http://www.semis.demon.co.uk/Gameboy/Gbspec.txt def pullchar(a): e = Expander(mem, a) dchr = array.array('B', e.expand()) chars = [] for i in range(len(dchr) / 16): src = dchr[16*i:16*i+16] im = Image.new("L", (8,8)) for y in range(8): for x in range(8): v = (0x80 & (src[2 * y] << x)) >> 7 v += (0x80 & (src[2 * y + 1] << x)) >> 6 im.putpixel((x,y), [0,64,128,255][v]) # im.save("g%03d.png" % i) chars.append(im) return chars def pullimage(a, file): chars = pullchar(ptr24(a + 3)) e = Expander(mem, ptr24(a)) pic = e.expand() im = Image.new("L", (160,144)) for y in range(18): for x in range(20): im.paste(chars[pic[20*y+x]], (8*x, 8*y)) im.save(file) pullimage(0x4395 + 6 * 2, "preview.png") rnc2sig = array.array('B', "RNC" + chr(2)) for a in range(len(mem)): if mem[a:a+4] == rnc2sig: print "%6x" % a, struct.unpack(">LLL", mem[a:a+12]) # print struct.unpack("LLL", mem[a:a+12]) exp = decompress(a) print " " + " ".join(["%02x" % x for x in exp[:16]]) if 0: if 3000 < psize <= 4096: pad = Image.new("L", (128,128)) for i,im in enumerate(pullchar(a)): pad.paste(im, (8*(i%16),8*(i/16))) pad.save("p_%x.png" % a) # World table at 0x1dec0, 9 bytes each # Each world has: charset(rnc), tileset(1k), gapset(rnc) # 0 world (1) # 1 ptr to levelmap # Level table at 0x1deff, 9 bytes each # Each level has: dims, map if 0: srch = 149270 a = srch/0x4000 hl = 0x4000 + (srch & 0x3fff) pat = array.array('B', struct.pack("LLL", snes[a:a+12]) (s,psize,csize) = struct.unpack(">LLL", snes[a:a+12]) if (psize % 32) == 0: ex = decompress(a) im = Image.new("L", (32, len(ex) / 16 + 32)) for c in range(len(ex) / 32): for y in range(8): pix = [0,0,0,0,0,0,0,0] bb0 = ex[32 * c + y * 2] bb1 = ex[32 * c + y * 2 + 1] bb2 = ex[32 * c + y * 2 + 16] bb3 = ex[32 * c + y * 2 + 17] for x in range(8): if bb0 & (0x80 >> x): pix[x] |= 16 if bb1 & (0x80 >> x): pix[x] |= 32 if bb2 & (0x80 >> x): pix[x] |= 64 if bb3 & (0x80 >> x): pix[x] |= 128 dstx = 8 * (c % 4) dsty = 8 * (c / 4) for x in range(8): im.putpixel((dstx+x,dsty+y), pix[x]) im.save("snes%x.png" % a)