215 lines
6.6 KiB
Python
Executable File
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()
|
|
|
|
|