"""This is some stuff to experiment with MMIX, Knuth's new assembly
language for his new versions of The Art Of Computer Programming.

Author: Danny Yoo (dyoo@hkn.eecs.berkeley.edu)

See http://www-cs-faculty.stanford.edu/~knuth/mmix.html for more
details about the MMIX language.

This program will take a compiled MMIX program and show different
representations of the tetrabytes.  The columns will contain
tetrabytes, opcodes, and character representations.
"""

from string import split, join
from string import letters, digits
symbols = " !@#$%^&*()-_=+[]{}\\|;:'\",./<>?`~"
##  Is there a definition of all ascii symbols somewhere else?

"""This defines all of the instructions for MMIX."""
##  Note: I will need to double check this, since I'm sure I made
##  some typing mistake somewhere...  Holy moley, there are a lot of
##  opcodes in MMIX!
OPCODES = split("""

    TRAP	FCMP	FUN	FEQL	FADD	FIX	FSUB	FIXU
    FLOT	FLOTI	FLOTU	FLOTUI	SFLOT	SFLOTI	SFLOTU	SFLOTUI
    FMUL	FCMPE	FUNE	FEQLE	FDIV	FSQRT	FREM	FINT
    MUL		MULI	MULU	MULUI	DIV	DIVI	DIVU	DIVUI
    ADD		ADDI	ADDU	ADDUI	SUB	SUBI	SUBU	SUBUI
    2ADDU	2ADDUI	4ADDU	4ADDUI	8ADDU	8ADDUI	16ADDU	16ADDUI
    CMP		CMPI	CMPU	CMPUI	NEG	NEGI	NEGU	NEGUI
    SL		SLI	SLU	SLUI	SR	SRI	SRU	SRUI
    BN		BNB	BZ	BZB	BP	BPB	BOD	BODB
    BNN		BNNB	BNZ	BNB	BNP	NBPB	BEV	BEVB
    PBN		PBNB	PBZ	PBZB	PBP	PBPB	PBOD	PBODB
    PBNN	PBNNB	PBNZ	PBNZB	PBNP	PBNPB	PBEV	PBEVB
    CSN		CSNI	CSZ	CSZI	CSP	CSPI	CSOD	CSODI
    CSN		CSNNI	CSNZ	CSNZI	CSNP	CSNPI	CSEV	CSEVI
    ZSN		ZSNI	ZSZ	ZSZI	ZSP	ZSPI	ZSOD	ZSODI
    ZSNN	ZNI	ZSNZ	ZSNZI	ZSNP	ZSNPI	ZSEV	ZSEVI
    LDB		LDBI	LDBU	LDBUI	LDW	LDWI	LDWU	LDWUI
    LDT		LDTI	LDTU	LDTUI	LDO	LDOI	LDOU	LDOUI
    LDSF	LDSFI	LDHT	LDHTI	CSWAP	CSWAPI	LDUNC	LDUNCI
    LDVTS	LDVTSI	PRELD	PRELDI PREGO	PREGOI	GO	GOI
    STB		STBI	STBU	STBUI	STW	STWI	STWU	STWUI
    STT		STTI	STTU	STTUI	STO	STOI	STOU	STOUI
    STSF	STSFI	STHT	STHTI	STCO	STCOI	STUNC	STUNCI
    SYNCD	SYNCDI	PREST	PRESTI	SYNCI	SYNCII	PUSHGO	PUSHGOI
    OR		ORI	ORN	ORNI	NOR	NORI	XOR	XORI
    AND		ANDI	ANDN	ANDNI	NAND	NANDI	NXOR	NXORI
    BDIF	BDIFI	WDIF	WDIFI	TDIF	TDIFI	ODIF	ODIFI
    MUX		MUXI	SADD	SADDI	MOR	MORI	MXOR	MXORI
    SETH	SETMH	SETML	SETL	INCH	INCMH	INCML	INCL
    ORH		ORMH	ORML	ORL	ANDNH	ANDNMH	ANDNML	ANDNL
    JMP		JMPB	PUSHJ	PUSHJB	GETA	GETAB	PUT	PUTI
    POP		RESUME	SAVE	UNSAVE	SYNC	SWYM	GET	TRIP

    """)

######################################################################
##  Each "view" is responsible for returning a clean string, given a
##  list of bytes.

def bytes_view(bytes):
    return ('%2x ' * len(bytes)) % tuple(bytes)

def mmix_view(bytes):
    code = '%7s' % OPCODES[bytes[0]]
    args = map(lambda x: '%3d' % x, bytes[1:])
    return "%19s" % join([code] + args, ' ')

def char_view(bytes):
    return '|' + join(map(asciifilter, bytes), '') + '|'

def asciifilter(byte):
    """Convert anything non-ascii-printable into blank space."""
    if chr(byte) in letters + digits + symbols: return chr(byte)
    return ' '
######################################################################
from sys import exit
from sys import argv
from getopt import getopt

def _help():
    print """
NAME
    mmixviewer.py: a program to look at different representations of a
    compiled MIX program.

SYNOPSIS
    mmixviewer.py [OPTIONS] file

OPTIONS:
    -l: don't show line numbers.
    -b: don't show bytes.
    -m: don't show mmix.
    -c: don't show chars.
    --help: what you're looking at right now.

AUTHOR:
    Danny Yoo (dyoo@hkn.eecs.berkeley.edu)

SEE ALSO:
    Donald Knuth's MMIX web page:
    http://www-cs-faculty.stanford.edu/~knuth/mmix.html
    """
    exit()

def _doit(bytes, views, linenumbering):
    for i in range(0, len(bytes), 4):
        tetra = bytes[i:i+4]
        ## a little tricky... mapping on functions!
        output = map(lambda func, tetra=tetra: func(tetra), views)
        if linenumbering: print '%3x:  ' % i,
        print join(output, '\t')

if __name__ == '__main__':
    options, args = getopt(argv[1:], 'bmcl', 'help')
    op_views = { '-b' : bytes_view, '-m' : mmix_view, '-c' : char_view}
    linenumbering = 1
    views = [bytes_view, mmix_view, char_view]
    try:
        for o, a in options:
            if op_views.has_key(o): views.remove(op_views[o])
            if o == '--help': _help()
            if o == '-l': linenumbering = 0
        bytes = map(ord, open(args[0]).read())
    except: _help()
    _doit(bytes, views, linenumbering)

## Here's what we'd expect to see:
## dyoo@einfall:~$ python mmixviewer.py hello.mmo
##   0:   98  9  1  1 	  LDVTS   9   1   1	|    |
##   4:   3a 86 a5 19 	    SLU 134 165  25	|:   |
##  [some stuff omitted]
##  20:   8f ff  1  0 	  LDOUI 255   1   0	|    |
##  24:    0  0  7  1 	   TRAP   0   7   1	|    |
##  28:   f4 ff  0  0 	   GETA 255   0   0	|    |
##  2c:    0  0  7  1 	   TRAP   0   7   1	|    |
##  30:    0  0  0  0 	   TRAP   0   0   0	|    |
## ... etc.
## So this is a program that helps to study MMIX code.
