1
0
mirror of https://github.com/rzzzwilson/pymlac.git synced 2025-06-10 09:32:41 +00:00

Almost working

This commit is contained in:
Ross Wilson
2016-01-28 20:34:53 +07:00
parent 31c7828581
commit a9dbc0c5e6

View File

@@ -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."""