#! /usr/bin/env python

import sys
from optparse import OptionParser
import random
import math

def mustbepowerof2(bits, size, msg):
    if math.pow(2,bits) != size:
        print 'Error in argument: %s' % msg
        sys.exit(1)

def mustbemultipleof(bignum, num, msg):
    if (int(float(bignum)/float(num)) != (int(bignum) / int(num))):
        print 'Error in argument: %s' % msg
        sys.exit(1)

def convert(size):
    length = len(size)
    lastchar = size[length-1]
    if (lastchar == 'k') or (lastchar == 'K'):
        m = 1024
        nsize = int(size[0:length-1]) * m
    elif (lastchar == 'm') or (lastchar == 'M'):
        m = 1024*1024
        nsize = int(size[0:length-1]) * m
    elif (lastchar == 'g') or (lastchar == 'G'):
        m = 1024*1024*1024
        nsize = int(size[0:length-1]) * m
    else:
        nsize = int(size)
    return nsize


#
# main program
#
parser = OptionParser()
parser.add_option('-A', '--addresses', default='-1',
                  help='a set of comma-separated pages to access; -1 means randomly generate', 
                  action='store', type='string', dest='addresses')
parser.add_option('-s', '--seed',    default=0,     help='the random seed',                               action='store', type='int', dest='seed')
parser.add_option('-a', '--asize',   default='16k', help='address space size (e.g., 16, 64k, 32m, 1g)',   action='store', type='string', dest='asize')
parser.add_option('-p', '--physmem', default='64k', help='physical memory size (e.g., 16, 64k, 32m, 1g)', action='store', type='string', dest='psize')
parser.add_option('-P', '--pagesize', default='4k', help='page size (e.g., 4k, 8k, whatever)',            action='store', type='string', dest='pagesize')
parser.add_option('-n', '--numaddrs',  default=5,  help='number of virtual addresses to generate',       action='store', type='int', dest='num')
parser.add_option('-u', '--used',       default=50, help='percent of virtual address space that is used', action='store', type='int', dest='used')
parser.add_option('-v',                             help='verbose mode',                                  action='store_true', default=False, dest='verbose')
parser.add_option('-c',                             help='compute answers for me',                        action='store_true', default=False, dest='solve')


(options, args) = parser.parse_args()

print 'ARG seed',               options.seed
print 'ARG address space size', options.asize
print 'ARG phys mem size',      options.psize
print 'ARG page size',          options.pagesize
print 'ARG verbose',            options.verbose
print 'ARG addresses',          options.addresses
print ''

random.seed(options.seed)

asize    = convert(options.asize)
psize    = convert(options.psize)
pagesize = convert(options.pagesize)
addresses = str(options.addresses)

if psize <= 1:
    print 'Error: must specify a non-zero physical memory size.'
    exit(1)

if asize < 1:
    print 'Error: must specify a non-zero address-space size.'
    exit(1)

if psize <= asize:
    print 'Error: physical memory size must be GREATER than address space size (for this simulation)'
    exit(1)

if psize >= convert('1g') or asize >= convert('1g'):
    print 'Error: must use smaller sizes (less than 1 GB) for this simulation.'
    exit(1)

mustbemultipleof(asize, pagesize, 'address space must be a multiple of the pagesize')
mustbemultipleof(psize, pagesize, 'physical memory must be a multiple of the pagesize')

# print some useful info, like the darn page table 
pages = psize / pagesize;
import array
used = array.array('i')
pt   = array.array('i')
for i in range(0,pages):
    used.insert(i,0)
vpages = asize / pagesize

# now, assign some pages of the VA
vabits   = int(math.log(float(asize))/math.log(2.0))
mustbepowerof2(vabits, asize, 'address space must be a power of 2')
pagebits = int(math.log(float(pagesize))/math.log(2.0))
mustbepowerof2(pagebits, pagesize, 'page size must be a power of 2')
vpnbits  = vabits - pagebits
pagemask = (1 << pagebits) - 1

# import ctypes
# vpnmask  = ctypes.c_uint32(~pagemask).value
vpnmask = 0xFFFFFFFF & ~pagemask
#if vpnmask2 != vpnmask:
#    print 'ERROR'
#    exit(1)
# print 'va:%d page:%d vpn:%d -- %08x %08x' % (vabits, pagebits, vpnbits, vpnmask, pagemask)

print ''
print 'The format of the page table is simple:'
print 'The high-order (left-most) bit is the VALID bit.'
print '  If the bit is 1, the rest of the entry is the PFN.'
print '  If the bit is 0, the page is not valid.'
print 'Use verbose mode (-v) if you want to print the VPN # by'
print 'each entry of the page table.'
print ''

print 'Page Table (from entry 0 down to the max size)'
for v in range(0,vpages):
    done = 0
    while done == 0:
        if ((random.random() * 100.0) > (100.0 - float(options.used))):
            u = int(pages * random.random())
            if used[u] == 0:
                done = 1
                # print '%8d - %d' % (v, u)
                if options.verbose == True:
                    print '  [%8d]  ' % v,
                else:
                    print '  ',
                print '0x%08x' % (0x80000000 | u)
                pt.insert(v,u)
        else:
            # print '%8d - not valid' % v
            if options.verbose == True:
                print '  [%8d]  ' % v,
            else:
                print '  ',
            print '0x%08x' % 0
            pt.insert(v,-1)
            done = 1
print ''            


#
# now, need to generate virtual address trace
#

addrList = []
if addresses == '-1':
    # need to generate addresses
    for i in range(0, options.num):
        n = int(asize * random.random())
        addrList.append(n)
else:
    addrList = addresses.split(',')


print 'Virtual Address Trace'
for vStr in addrList:
    # vaddr = int(asize * random.random())
    vaddr = int(vStr)
    if options.solve == False:
        print '  VA 0x%08x (decimal: %8d) --> PA or invalid address?' % (vaddr, vaddr)
    else:
        paddr = 0
        # split vaddr into VPN | offset
        vpn = (vaddr & vpnmask) >> pagebits
        if pt[vpn] < 0:
            print '  VA 0x%08x (decimal: %8d) -->  Invalid (VPN %d not valid)' % (vaddr, vaddr, vpn)
        else:
            pfn    = pt[vpn]
            offset = vaddr & pagemask
            paddr  = (pfn << pagebits) | offset
            print '  VA 0x%08x (decimal: %8d) --> %08x (decimal %8d) [VPN %d]' % (vaddr, vaddr, paddr, paddr, vpn)
print ''

if options.solve == False:
    print 'For each virtual address, write down the physical address it translates to'
    print 'OR write down that it is an out-of-bounds address (e.g., segfault).'
    print ''