mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
Almost working
This commit is contained in:
204
pyasm/pyasm
204
pyasm/pyasm
@@ -70,6 +70,10 @@ CurrentLineNumber = None
|
||||
# Any undefined label
|
||||
Undefined = None
|
||||
|
||||
# buffer for blocked code
|
||||
BlockBuffer = []
|
||||
BlockBufferStart = None
|
||||
|
||||
######
|
||||
# Mostly constant stuff
|
||||
######
|
||||
@@ -95,9 +99,13 @@ ZeroLeaderSize = 16
|
||||
# dict mapping opcode to generated word, address opts, address mask & indirect allowed
|
||||
######
|
||||
|
||||
# helper function to generate N-bit mask
|
||||
# helper function to generate N-bit mask, right justified
|
||||
def mask(n):
|
||||
return (0xFFFF << n) & 0xFFFF
|
||||
value = 0
|
||||
for _ in range(n):
|
||||
value = (value << 1) + 1
|
||||
|
||||
return value
|
||||
|
||||
OpcodeData = {
|
||||
'LAW': ( 0004000, AYES, mask(11), False),
|
||||
@@ -201,6 +209,8 @@ OpcodeData = {
|
||||
'DNOP': (0004000, ANO, 0, False),
|
||||
}
|
||||
|
||||
print('OpcodeData=%s' % str(OpcodeData))
|
||||
|
||||
######
|
||||
# The papertape/teletype loader code
|
||||
######
|
||||
@@ -331,12 +341,6 @@ def usage(msg=None):
|
||||
print(__doc__)
|
||||
|
||||
def error(msg):
|
||||
"""Print error message and stop."""
|
||||
|
||||
print(msg)
|
||||
sys.exit(10)
|
||||
|
||||
def syn_error(msg):
|
||||
"""Print a syntax error and abort."""
|
||||
|
||||
print('-' * 80)
|
||||
@@ -378,17 +382,25 @@ def emit_loader():
|
||||
for _ in range(ZeroLeaderSize):
|
||||
emit_byte(0)
|
||||
|
||||
def start_block(addr):
|
||||
"""Prepare next block to start at 'addr'"""
|
||||
|
||||
BlockBufferStart = addr
|
||||
|
||||
def emit_block():
|
||||
"""Emit the current code block and reset the buffer."""
|
||||
|
||||
global BlockBuffer, BlockBufferStart
|
||||
|
||||
# emit the block size and load address
|
||||
code_block_size = len(CodeBlock)
|
||||
code_block_size = len(BlockBuffer)
|
||||
emit_byte(code_block_size)
|
||||
emit_word(CodeBlockStart)
|
||||
for v in BlockBuffer:
|
||||
emit_word(v)
|
||||
|
||||
# emit the block & calculate checksum as we go
|
||||
checksum = 0
|
||||
for word in CodeBlock:
|
||||
for word in BlockBuffer:
|
||||
checksum += word
|
||||
if checksum and ~WordMask:
|
||||
++checksum
|
||||
@@ -399,8 +411,11 @@ def emit_block():
|
||||
emit_word(checksum)
|
||||
|
||||
# reset the code buffer
|
||||
CodeBlock = []
|
||||
CodeBlockStart += code_block_size
|
||||
BlockBuffer = []
|
||||
if BlockBufferStart:
|
||||
BlockBufferStart += code_block_size
|
||||
else:
|
||||
BlockBufferStart = None
|
||||
|
||||
def write_list(code, addr, lnum, line):
|
||||
"""Generate one line of listing file.
|
||||
@@ -445,14 +460,14 @@ def eval_expr(expr):
|
||||
try:
|
||||
result = eval(expr, globs)
|
||||
except TypeError as e:
|
||||
syn_error("ORG pseudo-opcode expression contains unsatisfied references")
|
||||
error("ORG pseudo-opcode expression contains unsatisfied references")
|
||||
return None
|
||||
except NameError as e:
|
||||
Undefined = e.message
|
||||
if 'is not defined' in e.message:
|
||||
Undefined = e.message[len("name '"):-len("' is not defined")]
|
||||
syn_error("ORG pseudo-opcode expression has '%s' undefined" % Undefined)
|
||||
syn_error("ORG pseudo-opcode expression has an error")
|
||||
error("ORG pseudo-opcode expression has '%s' undefined" % Undefined)
|
||||
error("ORG pseudo-opcode expression has an error")
|
||||
|
||||
return result
|
||||
|
||||
@@ -460,19 +475,10 @@ def num_gen_words(opcode, addr):
|
||||
"""Calculate number of words generated by this opcode."""
|
||||
|
||||
if opcode:
|
||||
if opcode == 'EQU':
|
||||
return 0
|
||||
if opcode == 'DATA':
|
||||
return 1
|
||||
# if opcode == 'ASCII':
|
||||
# return ?
|
||||
if opcode == 'END':
|
||||
return 0
|
||||
|
||||
# we assume opcode will return 1
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
return 0
|
||||
|
||||
def pass_1(lines):
|
||||
"""Do pass 1 of the assembly.
|
||||
@@ -483,40 +489,45 @@ def pass_1(lines):
|
||||
Returns False if there was an error.
|
||||
"""
|
||||
|
||||
global Dot, CurrentLineNumber, CurrentLine
|
||||
global StartAddress
|
||||
global Dot, CurrentLineNumber, CurrentLine, SymTable, SymTableLine
|
||||
|
||||
# initialize things
|
||||
Dot = None
|
||||
Symtable = {}
|
||||
SymTableLine = {}
|
||||
|
||||
# for each line in the file
|
||||
Dot = None
|
||||
for (lnum, line) in enumerate(lines):
|
||||
lnum += 1 # line numbers are 1-based
|
||||
CurrentLineNumber = lnum
|
||||
CurrentLine = line
|
||||
|
||||
# get line fields
|
||||
(label, opcode, addr) = split_fields(line)
|
||||
(label, opcode, indirect, addr) = split_fields(line)
|
||||
|
||||
if opcode:
|
||||
# we have an opcode, so code might be generated
|
||||
if opcode == 'ORG':
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("ORG pseudo-op has bad address")
|
||||
error("ORG pseudo-op has bad address")
|
||||
return False
|
||||
Dot = eval_expr(addr)
|
||||
|
||||
elif opcode == 'EQU':
|
||||
# no code, but we define a label
|
||||
if not label:
|
||||
syn_error("EQU pseudo-op must have a label")
|
||||
error("EQU pseudo-op must have a label")
|
||||
return False
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("EQU pseudo-op has bad value")
|
||||
error("EQU pseudo-op has bad value")
|
||||
return False
|
||||
define_label(label, eval_expr(addr), lnum)
|
||||
|
||||
elif opcode == 'BSS':
|
||||
# no code, but Dot moves
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("BSS pseudo-op has bad value")
|
||||
error("BSS pseudo-op has bad value")
|
||||
return False
|
||||
if label:
|
||||
define_label(label, Dot, lnum)
|
||||
@@ -525,18 +536,23 @@ def pass_1(lines):
|
||||
elif opcode == 'DATA':
|
||||
# a single data word
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("BSS pseudo-op has bad value")
|
||||
error("BSS pseudo-op has bad value")
|
||||
return False
|
||||
if label:
|
||||
define_label(label, Dot, lnum)
|
||||
Dot += 1
|
||||
|
||||
# TODO: implement "label ASCII 'Abcde'", producing packed ASCII chars, possible label
|
||||
# elif opcode == 'ASCII':
|
||||
# pass
|
||||
elif opcode == 'ASCII':
|
||||
# an ASCII string, packed two bytes/word
|
||||
ascii_len = len(addr)
|
||||
ascii_words = ascii_len / 2
|
||||
if ascii_len % 2:
|
||||
ascii_words += 1
|
||||
Dot += ascii_words
|
||||
|
||||
elif opcode == 'END':
|
||||
# TODO: get the start address, if any
|
||||
# get the (optional) start address
|
||||
StartAddress = eval_expr(addr)
|
||||
return True
|
||||
|
||||
else:
|
||||
@@ -548,7 +564,7 @@ def pass_1(lines):
|
||||
elif label:
|
||||
# label but no code generated, just set label in symtab
|
||||
if label in SymTable:
|
||||
syn_error("Label '%s' has already been defined" % label)
|
||||
error("Label '%s' has already been defined" % label)
|
||||
return False
|
||||
define_label(label, Dot, lnum)
|
||||
|
||||
@@ -570,24 +586,27 @@ def pass_2(lines):
|
||||
CurrentLine = line
|
||||
|
||||
# get line fields
|
||||
(label, opcode, addr) = split_fields(line)
|
||||
print('#####: label=%s, opcode=%s, addr=%s' % (str(label), str(opcode), str(addr)))
|
||||
(label, opcode, indirect, addr) = split_fields(line)
|
||||
print('#####: label=%s, opcode=%s, indirect=%s, addr=%s'
|
||||
% (str(label), str(opcode), str(indirect), str(addr)))
|
||||
|
||||
if opcode:
|
||||
# we have an opcode, so code might be generated
|
||||
if opcode == 'ORG':
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("ORG pseudo-op has bad address")
|
||||
error("ORG pseudo-op has bad address")
|
||||
return False
|
||||
emit_block() # punch any accumulated code
|
||||
Dot = eval_expr(addr)
|
||||
start_block(Dot)
|
||||
|
||||
elif opcode == 'EQU':
|
||||
# no code, but we define a label
|
||||
if not label:
|
||||
syn_error("EQU pseudo-op must have a label")
|
||||
error("EQU pseudo-op must have a label")
|
||||
return False
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("EQU pseudo-op has bad value")
|
||||
error("EQU pseudo-op has bad value")
|
||||
return False
|
||||
# check EQU value unchanged
|
||||
try:
|
||||
@@ -603,28 +622,39 @@ def pass_2(lines):
|
||||
elif opcode == 'BSS':
|
||||
# no code, but Dot moves
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("BSS pseudo-op has bad value")
|
||||
error("BSS pseudo-op has bad value")
|
||||
return False
|
||||
emit_block() # punch any accumalated code
|
||||
Dot += eval_expr(addr)
|
||||
start_block(Dot)
|
||||
|
||||
elif opcode == 'DATA':
|
||||
# a single data word
|
||||
if not addr or eval_expr(addr) is None:
|
||||
syn_error("BSS pseudo-op has bad value")
|
||||
error("BSS pseudo-op has bad value")
|
||||
return False
|
||||
emit_word(eval_expr(addr))
|
||||
Dot += 1
|
||||
|
||||
# TODO: implement "label ASCII 'Abcde'", producing packed ASCII chars, possible label
|
||||
# elif opcode == 'ASCII':
|
||||
# pass
|
||||
elif opcode == 'ASCII':
|
||||
len_addr = len(addr)
|
||||
for i in range(0, len_addr-1, 2):
|
||||
emit_word(ord((addr[i]) << 8) + ord(addr[i+1]))
|
||||
Dot += 1
|
||||
if len_addr % 2:
|
||||
emit_word((ord(addr[-1]) << 8))
|
||||
Dot += 1
|
||||
|
||||
elif opcode == 'END':
|
||||
# get optional start address
|
||||
global StartAddress
|
||||
|
||||
StartAddress = None
|
||||
if addr:
|
||||
StartAddress = eval_expr(addr)
|
||||
start_address = eval_expr(addr)
|
||||
if start_address != StartAddress:
|
||||
print('start_address=%s, StartAddress=%s' % (str(start_address), str(StartAddress)))
|
||||
error("Pass 2 start address is different from pass 1, was %06o but now %06o"
|
||||
% (StartAddress, start_address))
|
||||
StartAddress = start_address
|
||||
return True
|
||||
|
||||
else:
|
||||
@@ -638,11 +668,11 @@ def pass_2(lines):
|
||||
error("Label '%s' has different value in pass 2.\n"
|
||||
"Was %06o, now %06o"
|
||||
% (label. old_dot, Dot))
|
||||
gen_code(lnum, line, label, label, opcode, addr)
|
||||
gen_code(lnum, line, label, label, opcode, indirect, addr)
|
||||
Dot += 1
|
||||
|
||||
elif label:
|
||||
# label but no code generated, just check Dot dor label unchanged
|
||||
# label but no code generated, just check Dot for label unchanged
|
||||
if label in SymTable:
|
||||
dot = SymTable[label]
|
||||
if dot != Dot:
|
||||
@@ -656,18 +686,18 @@ def pass_2(lines):
|
||||
|
||||
# check nothing after END
|
||||
if lnum - 1 > len(lines):
|
||||
syn_error("Something after the 'END' pseudo-op!?")
|
||||
error("Something after the 'END' pseudo-op!?")
|
||||
|
||||
|
||||
def gen_code(lnum, line, dot, label, opcode, addr):
|
||||
def gen_code(lnum, line, dot, label, opcode, indirect, addr):
|
||||
"""Assemble one line of code.
|
||||
|
||||
lnum source file line number
|
||||
line the actual source line (for error reporting)
|
||||
dot current address in the assembly
|
||||
label optional label
|
||||
opcode opcode, uppercase
|
||||
addr address expression, uppercase
|
||||
lnum source file line number
|
||||
line the actual source line (for error reporting)
|
||||
dot current address in the assembly
|
||||
label optional label
|
||||
opcode opcode, uppercase
|
||||
indirect True if indirect flag found
|
||||
addr address expression, uppercase
|
||||
|
||||
Puts the assembled word into the punch buffer.
|
||||
"""
|
||||
@@ -675,21 +705,36 @@ def gen_code(lnum, line, dot, label, opcode, addr):
|
||||
print('gen_code: lnum=%d, line=%s, label=%s, opcode=%s, addr=%s'
|
||||
% (lnum, line, str(label), str(opcode), str(addr)))
|
||||
|
||||
# get instruction coding details
|
||||
try:
|
||||
(word, aok, mask, ind) = OpcodeData[opcode]
|
||||
except KeyError:
|
||||
error("%d: %s\nUnrecognized opcode '%s'" % (lnum, line, opcode))
|
||||
print('word=%06o, aok=%d, mask=%06o, ind=%s' % (word, aok, mask, str(ind)))
|
||||
|
||||
value = eval_expr(addr)
|
||||
print('addr=%s, value=%s' % (str(addr), str(value)))
|
||||
if aok in (AYES, AOPT):
|
||||
print('addr=%s, value=%s' % (str(addr), str(value)))
|
||||
|
||||
word_s = format(word, '016b') if word else ''
|
||||
word_s = format(word, '016b')
|
||||
mask_s = format(mask, '016b') if mask else ''
|
||||
value_s = format(value, '016b') if value else ''
|
||||
|
||||
print('word=%s, mask=%s, ind=%s, value=%s' % (word_s, mask_s, str(ind), value_s))
|
||||
|
||||
return 1025
|
||||
# check if 'addr' has overflowed. add in if OK
|
||||
if value:
|
||||
if value & mask != value:
|
||||
error("Address field overflow: %06o" % value)
|
||||
word += value
|
||||
|
||||
# if indirect and indirect OK, set high bit
|
||||
if indirect and ind:
|
||||
word += 0100000
|
||||
if not ind and indirect:
|
||||
error("Indirect not allowed here")
|
||||
|
||||
emit_word(word)
|
||||
|
||||
def define_label(label, address, lnum):
|
||||
"""Put 'label' into the symbol tables.
|
||||
@@ -746,20 +791,22 @@ def next_symbol(line):
|
||||
return fields
|
||||
|
||||
def split_fields(line):
|
||||
"""Split one ASM line into fields: label, opcode, address.
|
||||
"""Split one ASM line into fields: label, opcode, indirect, address.
|
||||
|
||||
Returns a tuple: (label, opcode, address).
|
||||
If label and opcode ar not None, uppercase the result string.
|
||||
Returns a tuple: (label, opcode, indirect, address).
|
||||
If label and opcode are not None, uppercase the result string.
|
||||
If address is not None and is not a string, it's uppercased.
|
||||
'indirect' is either True or False.
|
||||
|
||||
If a field is missing, return None for it. If the line is empty, return
|
||||
(None, None, None).
|
||||
(None, None, False, None).
|
||||
|
||||
We take pains not to split the address field if it's something like
|
||||
ALPHA + 100
|
||||
"""
|
||||
|
||||
if not line:
|
||||
return (None, None, None)
|
||||
return (None, None, False, None)
|
||||
|
||||
# check for the label
|
||||
label = None
|
||||
@@ -776,10 +823,11 @@ def split_fields(line):
|
||||
opcode = opcode.upper()
|
||||
|
||||
# get address
|
||||
indirect = False
|
||||
address = None
|
||||
if remainder and remainder[0] != ';':
|
||||
# first, check for a string
|
||||
if remainder[0] in "'\"":
|
||||
# it's a string
|
||||
delim = remainder[0]
|
||||
remainder = remainder[1:]
|
||||
ndx = remainder.find(delim)
|
||||
@@ -789,20 +837,22 @@ def split_fields(line):
|
||||
address = '"' + remainder[:ndx].strip() + '"'
|
||||
remainder = remainder[ndx+1:].strip()
|
||||
else:
|
||||
# strip off any comment
|
||||
# otherwise just an expression, strip off any indirect
|
||||
ndx = remainder.find(';')
|
||||
if ndx != -1:
|
||||
remainder = remainder[:ndx].strip()
|
||||
address = remainder.strip()
|
||||
if remainder[0] == '*':
|
||||
indirect = True
|
||||
remainder = remainder[1:]
|
||||
address = remainder.strip().upper()
|
||||
remainder = None
|
||||
address = address.upper()
|
||||
|
||||
# check that remainder is empty or only a comment
|
||||
if remainder and remainder[0] != ';':
|
||||
error('Badly formed instruction:\n'
|
||||
'%d: %s' % (CurrentLineNumber, CurrentLine))
|
||||
|
||||
return (label, opcode, address)
|
||||
return (label, opcode, indirect, address)
|
||||
|
||||
def assemble_file():
|
||||
"""Assemble the file and produce listing & output files."""
|
||||
|
||||
Reference in New Issue
Block a user