#!/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("<HB", mem[a:a+3])
    return 0x4000 * a + (hl - 0x4000)

print struct.unpack("<HB", mem[0x4395:0x4398])
print hex(ptr24(0x4395))
class BitReader(object):
    def __init__(self, mem, a):
        self.mem = mem
        self.a = a
        self.refill()

    def refill(self):
        self.mask = 128
        self.byte = self.mem[self.a]
        self.a += 1

    def getbit(self):
        if self.mask == 0:
            self.refill()
        b = int((self.byte & self.mask) != 0)
        self.mask >>= 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("<BB", array.array('B', decompress(a))[:3].tostring())
        (s,psize,csize) = 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("<HB", hl, a))
    print pat
    for a in range(len(mem)):
        if mem[a:a+3] == pat:
            print 'found ptr at', "%6x" % a
            print " ".join(["%02x" % x for x in mem[a-16:a+16]])
            for b in range(a-60,a+60,3):
                print "%6x" % b, "%6x" % ptr24(b)
srch = 0x2038c 
a = srch/0x4000
hl = 0x4000 + (srch & 0x3fff)
pat = array.array('B', struct.pack("<HB", hl, a))
print pat
for a in range(len(mem)):
    if mem[a:a+3] == pat:
        print 'found ptr at', "%6x" % a
        print " ".join(["%02x" % x for x in mem[a-16:a+16]])

# print struct.unpack("<HH", array.array('B', decompress(235660))[:4].tostring())

def worlddata(n):
    baseaddr = 0x1dec0 + 9 * n
    charset = pullchar(ptr24(baseaddr))
    tileset = mem[ptr24(baseaddr+3):][:1024]
    return (charset, tileset)

def loadworld(n):
    baseaddr = 0x1dec0 + 9 * n
    charset = pullchar(ptr24(baseaddr))
    tileset = mem[ptr24(baseaddr+3):][:1024]
    # print 'tileset at %x' % ptr24(0x1dec0+3)
    im = Image.new("L", (256,256))
    tiles = []
    for i in range(256):
        tile = tileset[4*i:4*i+4]
        x = 16 * (i % 16)
        y = 16 * (i / 16)
        if max(tile) < len(charset):
            im.paste(charset[tile[0]], (x, y))
            im.paste(charset[tile[1]], (x + 8, y))
            im.paste(charset[tile[2]], (x, y + 8))
            im.paste(charset[tile[3]], (x + 8, y + 8))
            tim = Image.new("L", (16,16))
            tim.paste(charset[tile[0]], (0, 0))
            tim.paste(charset[tile[1]], (8, 0))
            tim.paste(charset[tile[2]], (0, 8))
            tim.paste(charset[tile[3]], (8, 8))
            tiles.append(tim)
    return tiles

def dump(a):
    print "%6x" % a,
    print " ".join(["%02x" % x for x in mem[a:a+16]])

def loadlevel(n):
    levels = 0x1deff
    baseaddr = levels + 9 * n
    tiles = loadworld(mem[baseaddr])
    leveldata = ptr24(baseaddr + 1)

    def m256(x):
        if x:
            return x
        else:
            return 256
    w = m256(mem[leveldata])
    h = m256(mem[leveldata+1])
    map = decompress(leveldata + 2)
    assert len(map) == (w * h)

    mim = Image.new("L", (w * 16, h * 16))
    for y in range(h):
        for x in range(w):
            mim.paste(tiles[map[y * w + x]], (16 * x, 16 * y))
    return mim

# to display a level need to know:
#  charset
#  tileset
#  map
#  (w,h)

def dumplevel(n):
    levels = 0x1deff
    baseaddr = levels + 9 * n
    (charset, tileset) = worlddata(mem[baseaddr])
    leveldata = ptr24(baseaddr + 1)

    def m256(x):
        if x:
            return x
        else:
            return 256
    w = m256(mem[leveldata])
    h = m256(mem[leveldata+1])
    map = decompress(leveldata + 2)
    assert len(map) == (w * h)

    pad = Image.new("L", (128,128))
    for i,im in enumerate(charset):
        pad.paste(im, (8*(i%16),8*(i/16)))
    pad.save("chr.png")
    open("tiles", "w").write(tileset.tostring())
    open("map", "w").write(array.array('B', map).tostring())

dumplevel(32)

if 0:
    for i in range(50):
        print i
        loadlevel(i).save("level%02d.png" % i)

if 0:
    snes = array.array('B', open("snes.rom").read())

    def decompress(a):
        e = Expander(snes, a)
        return e.expand()

    print "SNES dump"
    for a in range(len(snes)):
        if snes[a:a+4] == rnc2sig:
            print "%6x" % a, struct.unpack(">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)
