## A program to decode bitfields into human readable output
## Copyright (C) 2005  Ian Wienand <ianw@ieee.org>

## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import readline, os, sys

class BitmapList:
    # A list of bitmaps.  Each entry consists of a dictionary
    # with keys
    #  ] name   : the short name (no spaces)
    #  ] descr  : a longer description
    #  ] ranges : a list of ranges, consisting of
    #           ( (start,end) , ("name", "specifier") )
    #         where
    #           o start = start bit
    #           o end   = end bit (can be ommited on a single bit, but leave the comma)
    #           o name  = name of bitfield
    #           o specifier = type of field where
    #                         b   = Boolean (True/False)
    #                         x   = Hexadecimal
    #                         d   = Decimal
    #                         B   = Binary
    #                         rwx = Read/Write/Execute (two bits)

    bitmap_list = ({"name" : "to_64bit_binary",
                    "descr" : "Convert to a 64 bit binary",
                    "ranges" : ( ((0,63)     , ("Bits", "B")), )
                    },
                   {"name" : "to_32bit_binary",
                    "descr" : "Convert to a 32 bit binary",
                    "ranges" : ( ((0,31)     , ("Bits", "B")), )
                    },
                   { "name" : "ia64_itir" ,
                     "descr" : "IA64 Instruction Translation Insertion Register" ,
                     "ranges" :  ( ((0,1)    , ("Reserved", "B")) ,
                                   ((2,7)    , ("Page Size", "x")),
                                   ((8,31)   , ("Protection Key", "x")),
                                   ((32,63)  , ("Reserved", "b"))
                                   ) 
                     },
                   { "name" : "ia64_ifa" ,
                     "descr" : "IA64 Instruction Fault Address Register" ,
                     "ranges" :  ( ((0,11)    , ("Ignored", "B")) ,
                                   ((12,63)   , ("VPN", "x"))
                                   )
                     },
                   { "name" : "ia64_rr" ,
                     "descr" : "IA64 Region Register" ,
                     "ranges" :  ( ((0,)    , ("Ignored", "B")) ,
                                   ((1,)    , ("Reserved", "B")),
                                   ((2,7)   , ("Ignored", "B")),
                                   ((8,31)  , ("RID", "x")),
                                   ((32,63) , ("Reserved", "B"))
                                   ) 
                     },
                   { "name" : "ia64_pkr" ,
                     "descr" : "IA64 Protection Key Register" ,
                     "ranges" :  ( ((0,)   , ("Valid", "b")) ,
                                   ((1,)   , ("Write Disable", "b")),
                                   ((2,)   , ("Read Disable", "b")),
                                   ((3,)   , ("Execute Disable", "b")),
                                   ((4,7)  , ("Reserved", "B")),
                                   ((8,31) , ("Protection Key", "x")),
                                   ((32,63), ("Reserved", "B"))
                                   ) 
                     },
                   { "name" : "ia64_tlb_gr" ,
                     "descr" : "IA64 TLB GR Insertion" ,
                     "ranges" :  ( ((0,)    , ("Present", "b")) ,
                                   ((1,)    , ("Reserved", "B")) ,
                                   ((2,4)   , ("Memmory Attribute", "x")),
                                   ((5,)    , ("Accessed", "b")),
                                   ((6,)    , ("Dirty", "b")),
                                   ((7,8)   , ("Privilege Level", "x")),
                                   ((9,11)  , ("Access Rights", "rwx")),
                                   ((12,49) , ("Physical Page Number", "x")),
                                   ((50,51) , ("Reserved", "B")),
                                   ((52,)   , ("Exception Deferred", "b")),
                                   ((53,63) , ("Ignored", "B"))
                                   ) 
                     },
                   { "name" : "ia64_pte" ,
                     "descr" : "IA64 PTE Entry" ,
                     "ranges" :  ( ((0,)    , ("Present", "b")) ,
                                   ((1,)    , ("Reserved", "B")) ,
                                   ((2,3)   , ("Memory Attributes", "x")),
                                   ((5,)    , ("Page Accessed", "b")),
                                   ((6,)    , ("Page Dirty", "b")),
                                   ((7,9)   , ("Privilege Level", "x")),
                                   ((9,11)  , ("Access Rights", "rwx")),
                                   ((12,50) , ("PFN", "x")),
                                   ((52,)   , ("Exception Deferral", "b")),
                                   ((53,63) , ("Ignored", "x"))
                                   ) 
                     },
                   { "name" : "ia64_psr" ,
                     "descr" : "IA64 Processor Status Register" ,
                     "ranges" :  ( ((0,)    , ("Reserved", "B")) ,
                                   ((1,)    , ("Big Endian", "b")) ,
                                   ((2,)    , ("User Performance Monitor", "b")),
                                   ((3,)    , ("Alignment Check", "b")),
                                   ((4,)    , ("Lower Floating Point Registers", "b")),
                                   ((5,)    , ("Upper Floating Point Registers", "b")),
                                   ((6,12)  , ("Reserved", "B")),
                                   ((13,)   , ("Interrupt Collection", "b")),
                                   ((14,)   , ("Interrupt", "b")),
                                   ((15,)   , ("Protection Key Enabled", "b")),
                                   ((16,)   , ("Reserved", "b")),
                                   ((17,)   , ("Data Address Translation", "b")),
                                   ((18,)   , ("Disable Floating Point Low", "b")),
                                   ((19,)   , ("Disable Floating Point Upper", "b")),
                                   ((19,)   , ("Disable Floating Point Upper", "b")),
                                   ((20,)   , ("Secure Performance Monitors", "b")),
                                   ((21,)   , ("Priv Performance Monitors Enable", "b")),
                                   ((22,)   , ("Disable Instruction Translation", "b")),
                                   ((23,)   , ("Secure Interval Timer", "b")),
                                   ((24,)   , ("Debug Breakpoint Fault", "b")),
                                   ((25,)   , ("Lower Privilege Transfer Trap", "b")),
                                   ((26,)   , ("Taken Branch Trap", "b")),
                                   ((27,)   , ("Register Stack Translation", "b")),
                                   ((28,31) , ("Reserved", "B")),
                                   ((32,33) , ("Current Privilege Level", "B")),
                                   ((34,)   , ("386 Instruction Set", "b")),
                                   ((35,)   , ("Machine Check abort mask", "b")),
                                   ((36,)   , ("Instruction Address Translation", "b")),
                                   ((37,)   , ("Instruction Debug Fault Disable", "b")),
                                   ((38,)   , ("Disable Data Access/Dirty bit Fault", "b")),
                                   ((39,)   , ("Data debug fault disable", "b")),
                                   ((40,)   , ("Single Step Enable", "b")),
                                   ((41,42) , ("Restart Instruction", "d")),
                                   ((43,)   , ("Exception Deferral", "b")),
                                   ((44,)   , ("Register Bank", "x")),
                                   ((45,)   , ("Disable Instruction Access", "b")),
                                   ((46,63) , ("Reserved", "B"))
                                   )
                     },
                   { "name" : "386_pte" ,
                     "descr" : "386 Page Table Entry" ,
                     "ranges" :  ( ((0,)   , ("Present", "b")) ,
                                   ((1,)   , ("Read only", "b")) ,
                                   ((2,)   , ("User Page", "b")) ,
                                   ((3,4)  , ("Reserved", "B")) ,
                                   ((5,)   , ("Accessed", "b")) ,
                                   ((6,)   , ("Dirty", "b")) ,
                                   ((7,8)  , ("Reserved", "B")) ,
                                   ((9,11) , ("Ignored", "B")) ,
                                   ((12,31), ("PFN", "x"))
                                )
                     },
                   { "name" : "unix_permissions" ,
                     "descr" : "Unix file permissions" ,
                     "ranges" :  ( ((0,2)   , ("User", "rwx")) ,
                                   ((3,5)   , ("Group", "rwx")) ,
                                   ((6,8)   , ("World", "rwx")) ,
                                )
                     }
                   )


class AnsiColors:
    def __init__(self):
        self.colors = {
            "black" : '\033[30m',
            "red" : '\033[31m',
            "green" : '\033[32m',
            "yellow" : '\033[33m',
            "blue" :  '\033[34m',
            "magenta" : '\033[35m',
            "cyan" : '\033[36m',
            "white" : '\033[37m'
            }

        self.modifiers = {
            "reset" : '\033[0;0m',
            "bold" : '\033[1m',
            "reverse" : '\033[2m'
            }
          
    def color(self, color, string):
        return self.colors[color.lower()] + string + self.modifiers["reset"]

    def bold_color(self, color, string):
        return self.modifiers["bold"] + self.color(color, string)

class Bitmap:
    def __init__(self, bitmap):
        self.bitmap = long(bitmap)

    def return_bits(self, start, end):
        start = long(start)
        end = long(end)
        rlen = end - start + 1
        return ((self.bitmap >> start) & ~(-1L << rlen))
   
class BinaryBits:
    def __init__(self, specifier):
        self.name = specifier["name"]
        self.descr = specifier["descr"]
        self.ranges = specifier["ranges"]
        self.color = AnsiColors()

    # convert number into len binary bits
    def to_binary(self, number, bits):
        str = ""
        while bits > 0:
            if (number >> (bits-1)) & 1:
                str = str + "1"
            else:
                str = str + "0"
            bits = bits - 1
            if not (bits % 4) : str = str + " "
        return str
    
    def print_decoded(self, bitmap):
        bitmap = Bitmap(bitmap)

        print self.color.bold_color("green", "Decoded output for a " + self.descr)

        #find the maximum length of specifiers for printout
        maxlen = 0
        for attribute in self.ranges:
            (descr, rangetype) = attribute[1]
            maxlen = max(maxlen, len(descr))

        #find bitmap size
        bitmaplen = 0
        for attribute in self.ranges:
            (bits, info) = attribute
            if len(bits) > 1:
                (start, end) = bits
            else:
                start = end = bits[0]
            bitmaplen = max(end, bitmaplen)
        bitmaplen = bitmaplen + 1

        if long(bitmap.bitmap) > 2L**long(bitmaplen) :
            print self.color.color("red", " Warning: input overflows bitmap specifier")

        #print all attributes
        for attribute in self.ranges:
            (bits, info) = attribute
            if len(bits) > 1:
                (start, end) = bits
            else:
                start = end = bits[0]
            
            (descr, rangetype) = attribute[1]

            specifier = "%"+`maxlen`+"s |"
            print specifier % descr,
            val = bitmap.return_bits(start, end)
            if rangetype == "b" :
                print "%s" % (bool(val))
            if rangetype == "d" :
                print "%d" % val
            if rangetype == "x" :
                print "0x%x" % val
            if rangetype == "rwx" :
                if val & 4 :
                    print "R",
                else: print ".",
                if val & 2 :
                    print "W",
                else: print ".",
                if val & 1 :
                    print "X"
                else: print "."
            if rangetype == "B" :
                print self.to_binary(val, end - start + 1)
                                    
               
class Completer:
    def __init__(self, list_of_bitmaps):
        self.cache = []
        self.list_of_bitmaps = list_of_bitmaps
        for entry in list_of_bitmaps:
            self.cache.append(entry["name"])

    def bitmap_completer(self, text, state):
        returns = []
        for entry in self.cache:
            if entry.startswith(text):
                returns.append(entry)
        try:
            return returns[state]
        except:
            return None

    def check_value(self, text):
        for entry in self.cache:
            if entry == text:
                return True
        return False

    def get_entry(self, text):
        for entry in self.list_of_bitmaps:
            if entry["name"] == text:
                return entry
        return None
            

def runBitmapDecode():
    color = AnsiColors()

    help_string = """
    First select the type of bitmap (tab shows list).
    q or quit exits the program
    
    Then enter your bitmap.  You can prefix the input with

    ] 0x : hexadecimal
    ] h  : hexadecimal
    ] o  : octal
    ] b  : binary (note output is big-endian; first bit is the MSB)

    No prefix is a base-10 integer.

    q or quit returns to the bitmap selection menu
    """

    r = readline
    completer = Completer(BitmapList.bitmap_list)
    
    print color.bold_color("black", "\nBitmap Decoder")
    print """--------------
Tab shows bitmaps to complete, quit or Ctrl-D to exit
    """

    try:
        while True:

            #setup the completer
            r.parse_and_bind("tab: complete")
            r.set_completer(completer.bitmap_completer)
            
            bitmap_type = raw_input(color.bold_color("red", "Input Bitmap Type > "))
            bitmap_type = bitmap_type.lower()

            if bitmap_type.lower() == "help" or bitmap_type.lower() == "h" :
                print help_string
                continue
            
            if bitmap_type.lower() == "quit" or bitmap_type.lower() == "q" :
                sys.exit(0)
                
            if not completer.check_value(bitmap_type):
                print "Invalid bitmap name (tab shows valid names)"
                continue

            entry = completer.get_entry(bitmap_type)
            if entry == None:
                print "Error!"
                sys.exit(1)

            b = BinaryBits(entry)

            while True:

                #reset so the completer doesn't work with binary input
                r.parse_and_bind("")
                r.set_completer(None)
                
                bitmap = raw_input(color.bold_color("blue", b.name + " value > "))
                bits = 0
                
                if bitmap.lower() == "help" or bitmap_type.lower() == "h" :
                    print help_string
                    continue
            
                try:
                    if bitmap.lower() == "q":
                        break
                    if bitmap.startswith("0x") or bitmap.startswith("h") :
                        bitmap = long(bitmap[2:], 16)
                    elif bitmap.startswith("o") :
                        bitmap = long(bitmap[1:], 8)
                    elif bitmap.startswith("b") :
                        bitmap = long(bitmap[1:], 2)
                    else :
                        bitmap = long(bitmap)
                except ValueError:
                    print "Error: Can't convert %s (did you forget a specifier?)" % bitmap
                    continue
            
                b.print_decoded(bitmap)

    except EOFError:
        print
        sys.exit(0)
    else:
        print
        sys.exit(1)

if __name__ == '__main__':
    runBitmapDecode()
