logisim-stuff/TK16/TK16 programs/assembler.py
2016-04-14 13:40:43 -05:00

215 lines
6.6 KiB
Python
Executable File

#Simple assembler for TK16
# CAK 2014
import os
print(os.getcwd())
import re
import sys
SOURCE_FILENAME = raw_input('input file:')
t = SOURCE_FILENAME.strip( '.asm' )
OUTPUT_FILENAME = t+ ".code"
VALID_LABEL = re.compile("[a-zA-Z_][a-zA-Z0-9_]*")
#for TK16
#key is lowercase instruction name
#value is (numeric code, True is needs argument)
INSTRUCTION_TABLE = {
'loada':(0, True),
'loadb':(1, True),
'store':(2, True),
'iloada':(3, True),
'iloadb':(4, True),
'setp':(5, False),
'ploada':(6, False),
'ploadb':(7, False),
'skey':(8, True),
'ldisp':(9, True),
'ktod':(10, False),
'storep':(11, False),
'add':(12, False),
'sub':(13, False),
'mul':(14, False),
'div':(15, False),
'neg':(16, False),
'and':(17, False),
'or':(18, False),
'not':(19, False),
'lshift':(20, False),
'rshift':(21, False),
'copya':(22, False),
'copyb':(23, False),
'jump':(24, True),
'jumpz':(25, True),
'jumpnz':(26, True),
'cleargdisp':(27,False),
'clearpc':(28,False),
'incp':(29,False),
'call':(30,True),
'return':(31,False),
'push':(32,True),
'pop':(33,True),
'clearpop':(34,False),
'clearstack':(35,False)
}
output_file = open(OUTPUT_FILENAME, "w")
#initialize counters for data and code segments
counters = {}
counters['dseg'] = 0
counters['cseg'] = 0
labels = {}
current_segment = 'cseg' #default at start
def convert_number(instring):
if instring.startswith(("0x", "0X")):
return int(instring[2:], 16)
elif instring.startswith(("x", "X")):
return int(instring[1:], 16)
elif instring.startswith(("b", "B")):
return int(instring[1:], 2)
else:
#try good old decimal conversion
return int(instring)
def assert_label(instring):
#throw an AsserionError if the string is not a valid label or
#instruction name
if VALID_LABEL.match(instring) is None:
raise AssertionError(
"'{0}' is not a valid label/token".format(instring))
asm_file = open(SOURCE_FILENAME)
asm_lines = asm_file.readlines()
#first pass works out values for labels
linenum = 1
for line in asm_lines:
try:
#take out any comment
line = line.strip()
line = line.split(";")[0]
tokens = line.split()
print tokens
#check for segment directive
if len(tokens) > 0 and tokens[0].lower() in (".cseg", ".dseg"):
#change of segment code/data
current_segment = tokens[0][1:].lower()
tokens = tokens[1:]
#check for label declaration
if len(tokens) > 0 and tokens[0][-1] == ":":
#a label, put it in the table
label_name = tokens[0][:-1]
assert_label(label_name)
labels[label_name] = counters[current_segment]
tokens = tokens[1:]
#check for a definition
if len(tokens) > 0 and tokens[0] == ".def":
if len(tokens) >= 3:
(name, value) = tokens[1:]
assert_label(name)
labels[name] = convert_number(value)
#remove the three tokens in the .def statement
tokens = tokens[3:]
else:
raise AssertionError("Bad .def statement")
#check for .alloc of memory spaces
if len(tokens) > 0 and tokens[0] == ".alloc":
if len(tokens) >= 2:
size = tokens[1]
if size in labels:
size = labels[size]
else:
size = convert_number(size)
counters[current_segment] += size
#remove the .alloc statement tokens
tokens = tokens[2:]
else:
raise AssertionError("Bad .alloc statement")
#check if there is a token that is a valid instruction
if len(tokens) > 0:
instruction = tokens[0].lower()
if instruction in INSTRUCTION_TABLE:
#increment program counter
counters[current_segment] += 1
tokens = tokens[1:]
else:
raise AssertionError(
"Unknown instruction '{0}'".format(instruction))
except:
#catch in error and include line number
print "Error at .asm file line {0}".format(linenum)
#keep passing the error along
raise
linenum += 1
#second pass generates code
output_code = []
code_list = []
linenum = 1
code_segment = False #need to look for .cseg
for line in asm_lines:
try:
#take out any comment
line = line.strip()
without_comments = line.split(";")[0]
tokens = without_comments.split()
if len(tokens) > 0 and tokens[0][-1] == ":":
#label at start, remove from token list
tokens = tokens[1:]
if len(tokens) > 0 and tokens[0].lower() == ".cseg":
#start of code segment
code_segment = True
tokens = tokens[1:]
if len(tokens) > 0 and tokens[0][0] != ".":
#should be an instruction
instruction = tokens[0].lower()
if instruction in INSTRUCTION_TABLE:
(opcode, needs_argument) = INSTRUCTION_TABLE[instruction]
code = opcode << 16
if needs_argument:
if len(tokens) > 1:
argument = tokens[1]
if argument in labels:
argument = labels[argument]
else:
argument = convert_number(argument)
code += argument
else:
raise AssertionError(
"Argument Missing for {0}".format(instruction))
output_code.append(code)
code_list.append(
"({0}) {1}".format(hex(code),line.strip()))
else:
raise AssertionError(
"{0} is not a valid instruction".format(instruction))
else:
#should still add to code listing
code_list.append(line)
except:
print "Error at .asm file line {0}".format(linenum)
raise
linenum += 1
print labels
print output_code
for line in code_list:
print line
#header for logisim image
output_file.write("v2.0 raw\n")
for code in output_code:
output_file.write("{0:06x}\n".format(code))
asm_file.close()
output_file.close()