Saturday, March 15, 2008

processor.py

In my Digital Design course, we did quite a bit involving the MIPS2000 CPU, such as designing its controller. It seemed interesting, so I decided to implement a pseudo-processor using Python and its higher-level data types.

processor.py

from copy import deepcopy
from math import pow
from sys import stdin, stdout, stderr, argv
BITS = 9;
class processor(object):
class executor(object):
def __init__(self, cache, memories):
self.cache = cache
self.memories = memories
self.pc = 0
self.initial = 16
self.halt = False
self.instructions = {
'stoi' : self.stoi,
'sto' : self.sto,
'or' : self.lor,
'and' : self.land,
'add' : self.add,
'addi' : self.addi,
'sub' : self.sub,
'lw' : self.lw,
'lwi' : self.lwi,
'sw' : self.sw,
'swi' : self.swi,
'jump' : self.jump,
'jumpo' : self.jumpo,
'cjump' : self.cjump,
'cjumpi' : self.cjumpi,
'cjumpo' : self.cjumpo,
'imc' : self.imc,
'pass' : self.ps,
'halt' : self.haltp
}
self.errstring = 'Invalid Instruction at 0x%%.%dx\n' % int(BITS / 4);
def run(self, instruction):
if isinstance(instruction, tuple) and (instruction[0] in self.instructions):
name = instruction[0]
args = instruction[1:len(instruction)]
self.instructions[name](args)
else:
stderr.write(self.errstring % self.pc)
self.pc += 1
def stoi(self, args):
"stoi val out"
self.cache[args[1]] = args[0]
self.pc += 1
def sto(self, args):
"sto in out"
self.cache[args[1]] = self.cache[args[0]]
self.pc += 1
def lor(self, args):
"or in1 in2 out"
self.cache[args[2]] = self.cache[args[0]] | self.cache[args[1]]
self.pc += 1
def land(self, args):
"and in1 in2 out"
self.cache[args[2]] = self.cache[args[0]] & self.cache[args[1]]
self.pc += 1
def add(self, args):
"add in1 in2 out"
self.cache[args[2]] = self.cache[args[0]] + self.cache[args[1]]
self.pc += 1
def addi(self, args):
"addi in1 imm out"
self.cache[args[2]] = self.cache[args[0]] + args[1]
self.pc += 1
def sub(self, args):
"sub in1 in2 out"
self.cache[args[2]] = self.cache[args[0]] - self.cache[args[1]]
self.pc += 1
def lw(self, args):
"lw memlocreg out"
memloc = args[0]
out = args[1]
if isinstance(self.memories[self.cache[memloc]], list) and len(self.memories[self.cache[memloc]]) == 2:
self.cache[out] = deepcopy(self.memories[self.cache[memloc]][0])
self.memories[self.cache[memloc]][1] = True
else:
self.cache[out] = deepcopy(self.memories[self.cache[memloc]])
self.pc += 1
def lwi(self, args):
"lwi memloc out"
memloc = args[0]
out = args[1]
if isinstance(self.memories[memloc], list) and len(self.memories[memloc]) == 2:
self.cache[out] = deepcopy(self.memories[memloc][0])
self.memories[memloc][1] = True
else:
self.cache[out] = deepcopy(self.memories[memloc])
self.pc += 1
def sw(self, args):
"sw in memlocreg"
inp = args[0]
memloc = args[1]
if isinstance(self.memories[self.cache[memloc]], list) and len(self.memories[self.cache[memloc]]) == 2:
self.memories[self.cache[memloc]][0] = deepcopy(self.cache[inp])
self.memories[self.cache[memloc]][1] = True
else:
self.memories[self.cache[memloc]] = deepcopy(self.cache[inp])
self.pc += 1
def swi(self, args):
"sw in memlocreg"
inp = args[0]
memloc = args[1]
if isinstance(self.memories[memloc], list) and len(self.memories[memloc]) == 2:
self.memories[memloc][0] = deepcopy(self.cache[inp])
self.memories[memloc][1] = True
else:
self.memories[memloc] = deepcopy(self.cache[inp])
self.pc += 1
def jump(self, args):
"jump address"
self.pc = args[0]
def jumpo(self, args):
"jump addressoffsetreg"
self.pc += args[0]
def cjump(self, args):
"cjump cond addressreg, will jump when condition is FALSE"
if not self.cache[args[0]]:
self.pc = self.cache[args[1]]
else:
self.pc += 1
def cjumpi(self, args):
"cjump cond address, will jump when condition is FALSE"
if not self.cache[args[0]]:
self.pc = args[1]
else:
self.pc += 1
def cjumpo(self, args):
"cjumpo cond address, will jump when condition is FALSE"
if not self.cache[args[0]]:
self.pc += self.cache[args[1]]
else:
self.pc += 1
def imc(self, args):
"pass"
self.initial = self.cache[args[0]]
def ps(self, args):
"pass"
self.pc += 1
def haltp(self, args):
"halt"
self.halt = True
def __init__(self):
self.pc = 0
self.cache = [0] * 16
self.memories = [0] * int(pow(2, BITS))
self.asiptr = 0
self.initial = 16 # defaults to loading at 16
self.io = {
'pread' : (int(pow(2, BITS) - 5), 'r'), # Reads instruction file
'fread' : (int(pow(2, BITS) - 4), 'r'), # Reads byte of any file as integer
'read' : (int(pow(2, BITS) - 3), 'r'),
'write' : (int(pow(2, BITS) - 2), 'w'), # Writes integer value with spaces
'cwrite' : (int(pow(2, BITS) - 1), 'w') # Writes character
}
for i in self.io.values():
self.memories[i[0]] = [0, False]
self.file = None
if len(argv) >= 2:
self.file = open(argv[1], 'rb')
self.asmd = self.asi(self.file.readlines())
self.file.seek(0)
for i in ['pread', 'fread']:
#print 'INIT: %s: %s' % (i, str(self.io[i]))
#print 'INIT:', self.memories[self.io[i][0]]
self.memories[self.io[i][0]][0] = self.handler(i)
#print "\t-> %d: %s" % (self.io[i][0], self.memories[self.io[i][0]])
self.memories[self.io[i][0]][1] = False
else:
self.asmd, self.file = None, None
self.boot()
def boot(self):
asm = [
('stoi', 0, 0), # Gives us a workable zero
('stoi', 16, 1), # Gives us the initial address for program execution
('sto', 1, 2), # Initializes the counter for loading the program
('pass',), # A label to go to
('lwi', self.io['pread'][0], 3), # Loads instruction word from I/O
('cjump', 3, 1), # Jumps to beginning of program when no operator returned, will start program
('sw', 3, 2), # Loads instruction word into memory
('addi', 2, 1, 2), # Increments the address to copy to
('jump', 3), # Jumps to the label address
('halt',) # Halt at failure
]
self.memories[0:len(asm)] = asm
#for i in range(508, 512):
#print '%d: %s' % (i, self.memories[i])
self.execute = self.executor(self.cache, self.memories)
#for i in range(508, 512):
#print '%d: %s' % (i, self.memories[i])
#print "BOOT:", self.memories
while not self.execute.halt:
if self.pc < len(self.memories):
self.execute.run(self.memories[self.pc])
self.pc = self.execute.pc
#print self.cache
#print '%d, %s' % (self.pc, str(self.memories))
self.initial = self.execute.initial
self.check()
else:
self.execute.run(('halt',))
print '\n',self.cache
print '%d, %s' % (self.pc, str(self.memories))
def check(self):
#for i in range(508, 512):
#print '%d: %s' % (i, self.memories[i])
for i in self.io.keys():
#print "CHECK:", self.memories
#print '%s: %s' % (i, str(self.io[i]))
#print self.memories[self.io[i][0]]
if self.io[i][1] == 'r' and self.memories[self.io[i][0]][1]:
#print "\tBEFORE:", self.memories[self.io[i][0]]
self.memories[self.io[i][0]][0] = self.handler(i)
self.memories[self.io[i][0]][1] = False
#print "\tAFTER:", self.memories[self.io[i][0]]
elif self.io[i][1] == 'w' and self.memories[self.io[i][0]][1]:
self.handler(i, self.memories[self.io[i][0]][0])
self.memories[self.io[i][0]][0] = 0
self.memories[self.io[i][0]][1] = False
#print '%s: %s' % (i, str(self.io[i]))
#print self.memories[self.io[i][0]]

def handler(self, handle, value = ''):
if handle == 'read':
return ord(stdin.read(1))
elif handle == 'write':
stdout.write('%d ' % value)
elif handle == 'cwrite':
stdout.write(chr(value))
elif handle == 'fread':
if self.file:
ret = self.file.read(1)
if ret:
return ord(ret)
else:
return 0
else:
stderr.write('No file open!')
return 0
elif handle == 'pread':
#print 'LOADING FROM PROGRAM'
if self.asmd and self.asiptr < len(self.asmd):
ret = self.asmd[self.asiptr]
self.asiptr += 1
else:
ret = 0
return ret
def asi(self, lines):
vars = {}
outlines = []
ctr = 1
linectr = self.initial
print 'INITIAL:', linectr
for i in lines:
if len(i) and i[0:1] != '#' :
if i[len(i) - 2] == ':':
print '%s -> %d' % (i[0:len(i) - 2], linectr)
vars[i[0:len(i) - 2]] = linectr
linectr += 1
elif len(i.split()):
linectr += 1
linctr = self.initial
for i in lines:
if len(i) and i[0:1] != '#':
if i[len(i) - 2] == ':':
#print '%s -> %d' % (i[0:len(i) - 2], linectr)
#vars[i[0:len(i) - 2]] = linectr
#outlines += [('pass',)]
outlines += [('pass',)]
linectr += 1
else:
data = i.split()
inst = False
tmp = ()
for d in data:
if len(d):
if not inst:
tmp = (d,)
inst = True
else:
if not(d[0] == '(' and d[-1] == ')'):
if not d in vars:
vars[d] = ctr
ctr += 1
tmp += (vars[d],)
else:
tmp += (int(d[1:len(d) - 1]),)
if len(tmp):
outlines += (tmp,)
linectr += 1
print outlines
return outlines
test = processor()

asm.test

A test assembly file.

#stoi (10) A
#stoi (20) B
#stoi (50) D
#add A B C
#sub C D Q
#swi Q (510)

stoi (55) B
foo:
lwi (508) A
cjumpi A end1
swi A (511)
sw A B
addi B (1) B
jump foo
end1:
lwi (508) A
cjumpi A end2
swi A (511)
sw A B
addi B (1) B
jump end1
end2:

halt

Sample run

% python processor.py test.asm
INITIAL: 16
foo -> 17
end1 -> 24
end2 -> 31
[('stoi', 55, 1), ('pass',), ('lwi', 508, 2), ('cjumpi', 2, 24), ('swi', 2, 511), ('sw', 2, 1), ('addi', 1, 1, 1), ('jump', 17), ('pass',), ('lwi', 508, 2), ('cjumpi', 2, 31), ('swi', 2, 511), ('sw', 2, 1), ('addi', 1, 1, 1), ('jump', 24), ('pass',), ('halt',)]
#stoi (10) A
#stoi (20) B
#stoi (50) D
#add A B C
#sub C D Q
#swi Q (510)

stoi (55) B
foo:
lwi (508) A
cjumpi A end1
swi A (511)
sw A B
addi B (1) B
jump foo
end1:
lwi (508) A
cjumpi A end2
swi A (511)
sw A B
addi B (1) B
jump end1
end2:

halt

[0, 300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
32, [('stoi', 0, 0), ('stoi', 16, 1), ('sto', 1, 2), ('pass',), ('lwi', 507, 3), ('cjump', 3, 1), ('sw', 3, 2), ('addi', 2, 1, 2), ('jump', 3), ('halt',), 0, 0, 0, 0, 0, 0, ('stoi', 55, 1), ('pass',), ('lwi', 508, 2), ('cjumpi', 2, 24), ('swi', 2, 511), ('sw', 2, 1), ('addi', 1, 1, 1), ('jump', 17), ('pass',), ('lwi', 508, 2), ('cjumpi', 2, 31), ('swi', 2, 511), ('sw', 2, 1), ('addi', 1, 1, 1), ('jump', 24), ('pass',), ('halt',), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 115, 116, 111, 105, 32, 40, 49, 48, 41, 32, 65, 10, 35, 115, 116, 111, 105, 32, 40, 50, 48, 41, 32, 66, 10, 35, 115, 116, 111, 105, 32, 40, 53, 48, 41, 32, 68, 10, 35, 97, 100, 100, 32, 65, 32, 66, 32, 67, 10, 35, 115, 117, 98, 32, 67, 32, 68, 32, 81, 10, 35, 115, 119, 105, 32, 81, 32, 40, 53, 49, 48, 41, 10, 10, 115, 116, 111, 105, 32, 40, 53, 53, 41, 32, 66, 10, 102, 111, 111, 58, 10, 108, 119, 105, 32, 40, 53, 48, 56, 41, 32, 65, 10, 99, 106, 117, 109, 112, 105, 32, 65, 32, 101, 110, 100, 49, 10, 115, 119, 105, 32, 65, 32, 40, 53, 49, 49, 41, 10, 115, 119, 32, 65, 32, 66, 10, 97, 100, 100, 105, 32, 66, 32, 40, 49, 41, 32, 66, 10, 106, 117, 109, 112, 32, 102, 111, 111, 10, 101, 110, 100, 49, 58, 10, 108, 119, 105, 32, 40, 53, 48, 56, 41, 32, 65, 10, 99, 106, 117, 109, 112, 105, 32, 65, 32, 101, 110, 100, 50, 10, 115, 119, 105, 32, 65, 32, 40, 53, 49, 49, 41, 10, 115, 119, 32, 65, 32, 66, 10, 97, 100, 100, 105, 32, 66, 32, 40, 49, 41, 32, 66, 10, 106, 117, 109, 112, 32, 101, 110, 100, 49, 10, 101, 110, 100, 50, 58, 10, 10, 104, 97, 108, 116, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, [0, False], [0, False], [0, False], [0, False], [0, False]]

No comments: