From f82bb114912f6d0443343192e08fc6b7344dda81 Mon Sep 17 00:00:00 2001 From: Ross Wilson Date: Sat, 30 Jan 2016 15:27:29 +0700 Subject: [PATCH] Passes 1 & 2 now seem OK, worry about code generation --- pyasm/pyasm | 243 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 161 insertions(+), 82 deletions(-) diff --git a/pyasm/pyasm b/pyasm/pyasm index b49acac..aa39f1e 100755 --- a/pyasm/pyasm +++ b/pyasm/pyasm @@ -71,13 +71,17 @@ CurrentLineNumber = None Undefined = None # buffer for blocked code -BlockBuffer = [] +BlockMaxSize = 255 +BlockBuffer = bytearray() BlockBufferStart = None ###### # Mostly constant stuff ###### +# mask for 16bit values +WordMask = 0xFFFF + # the output PTP filename extension PTPExtension = '.ptp' @@ -88,7 +92,7 @@ ListFileExtension = '.lst' WordBits = 16 # number of bytes in the 'zero' leader -ZeroLeaderSize = 16 +ZeroLeaderSize = 32 # address states, AYES = address required, # ANO = address NOT required @@ -341,79 +345,109 @@ def usage(msg=None): def error(msg): """Print a syntax error and abort.""" + lnum = CurrentLineNumber if CurrentLineNumber else 1 + line = CurrentLine if CurrentLine else '' + print('-' * 80) - print("%04d: %s" % (CurrentLineNumber, CurrentLine)) + print("%04d: %s" % (lnum, line)) print(msg) print('-' * 80) sys.exit(10) -def emit_byte(byte): - """Emit one byte to the output file. +def write_byte(byte): + """Write one byte into the output code file. Write only the low 8 bits of 'byte'. """ + print('write_byte: byte=%04o' % byte) OutputFileHandle.write(chr(byte & 0xFF)) -def emit_word(word): +def write_word(word): """Emit a 16-bit word to the output file.""" - emit_byte(word >> 8) - emit_byte(word) + write_byte(word >> 8) + write_byte(word) -def emit_start(address): - """Emit the start block.""" +def write_start(address): + """Write the start block.""" - emit_byte(1) # one word block - emit_word(address) + write_block() # emit any code accumulated + start_block(address) + write_block() -def emit_loader(): +def write_leader(): + """Write the papertape leader.""" + + for _ in range(ZeroLeaderSize): + write_byte(0) + +def write_block_loader(): """Emit the block loader prefix code.""" - for _ in range(ZeroLeaderSize): - emit_byte(0) + write_leader() for word in BlockLoader: - emit_word(word) + write_word(word) - for _ in range(ZeroLeaderSize): - emit_byte(0) + write_leader() def start_block(addr): """Prepare next block to start at 'addr'""" - BlockBufferStart = addr + global BlockBuffer, BlockBufferStart -def emit_block(): - """Emit the current code block and reset the buffer.""" + BlockBuffer = [] + BlockBufferStart = addr + print('start_block: BlockBufferStart set to %s' % str(BlockBufferStart)) + +def emit_word(word): + """Put a word into the code block buffer. + + Write buffer out if full. + """ + + code_block_size = len(BlockBuffer) + if code_block_size >= BlockMaxSize: + write_block() + start_block(Dot) + + BlockBuffer.append(word) + +def write_block(): + """Write the current code block and reset the buffer.""" global BlockBuffer, BlockBufferStart - # emit the block size and load address code_block_size = len(BlockBuffer) - emit_byte(code_block_size) - for v in BlockBuffer: - emit_word(v) + print('write_block: code_block_size=%d, BlockBufferStart=%s' + % (code_block_size, str(BlockBufferStart))) + if code_block_size == 0: + # block buffer is empty, do nothing + return - # emit the block & calculate checksum as we go + # emit the block size and load address + write_byte(code_block_size) + write_word(BlockBufferStart) + for word in BlockBuffer: + write_word(word) + + # calculate checksum checksum = 0 for word in BlockBuffer: checksum += word if checksum and ~WordMask: ++checksum checksum &= WordMask - emit_word(word) + write_word(word) # emit the block checksum - emit_word(checksum) + write_word(checksum) # reset the code buffer BlockBuffer = [] - if BlockBufferStart: - BlockBufferStart += code_block_size - else: - BlockBufferStart = None + BlockBufferStart = None def write_list(code, addr, lnum, line): """Generate one line of listing file. @@ -441,8 +475,6 @@ def eval_expr(expr): Returns 'None' if there is no valid expression. """ - print('eval_expr: expr=%s' % str(expr)) - global Undefined # if no expression, do nothing @@ -457,15 +489,17 @@ def eval_expr(expr): # evaluate the expression try: result = eval(expr, globs) - except TypeError as e: - error("ORG pseudo-opcode expression contains unsatisfied references") - return None - except NameError as e: +# except TypeError as e: +# error("ORG pseudo-opcode expression contains unsatisfied references") +# return None + except (TypeError, NameError) as e: Undefined = e.message if 'is not defined' in e.message: Undefined = e.message[len("name '"):-len("' is not defined")] - error("ORG pseudo-opcode expression has '%s' undefined" % Undefined) - error("ORG pseudo-opcode expression has an error") + raise NameError("ORG pseudo-opcode expression has '%s' undefined" % Undefined) +# error("ORG pseudo-opcode expression has '%s' undefined" % Undefined) + raise NameError("ORG pseudo-opcode expression has an error") +# error("ORG pseudo-opcode expression has an error") return result @@ -487,8 +521,9 @@ def pass_1(lines): Returns False if there was an error. """ - global StartAddress - global Dot, CurrentLineNumber, CurrentLine, SymTable, SymTableLine + global Dot, StartAddress + global CurrentLineNumber, CurrentLine + global SymTable, SymTableLine # initialize things Dot = None @@ -508,9 +543,11 @@ def pass_1(lines): # we have an opcode, so code might be generated if opcode == 'ORG': if not addr or eval_expr(addr) is None: - error("ORG pseudo-op has bad address") + error("ORG pseudo-op has a bad address") return False Dot = eval_expr(addr) + if label: + error("ORG pseudo-op must not have a label") elif opcode == 'EQU': # no code, but we define a label @@ -518,14 +555,14 @@ def pass_1(lines): error("EQU pseudo-op must have a label") return False if not addr or eval_expr(addr) is None: - error("EQU pseudo-op has bad value") + error("EQU pseudo-op has a 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: - error("BSS pseudo-op has bad value") + error("BSS pseudo-op has a bad value") return False if label: define_label(label, Dot, lnum) @@ -534,14 +571,16 @@ def pass_1(lines): elif opcode == 'DATA': # a single data word if not addr or eval_expr(addr) is None: - error("BSS pseudo-op has bad value") + error("BSS pseudo-op has a bad value") return False if label: define_label(label, Dot, lnum) Dot += 1 elif opcode == 'ASCII': - # an ASCII string, packed two bytes/word + # ASCII string, pack two bytes/word, maybe zero byte fill at end + print('ASCII: addr=%' % str(addr)) + # TODO worry if string has delimiting quotes ascii_len = len(addr) ascii_words = ascii_len / 2 if ascii_len % 2: @@ -550,7 +589,12 @@ def pass_1(lines): elif opcode == 'END': # get the (optional) start address - StartAddress = eval_expr(addr) + StartAddress = None + if addr: + StartAddress = eval_expr(addr) + if StartAddress is None: + error("END pseudo-op has a bad address") + return False return True else: @@ -569,12 +613,17 @@ def pass_1(lines): return True def pass_2(lines): - """Perform the second pass of the assembly.""" + """Perform the second pass of the assembly. - global Dot, CurrentLineNumber, CurrentLine + Very similar to pass_1(), but we: + . don't define labels, that was done in pass_1() + . check that any pass_2 labels haven't changed value + """ + + global Dot, StartAddress, CurrentLineNumber, CurrentLine # punch the zero leader and ptr/tty loader - emit_loader() + write_block_loader() # for each line in the file Dot = None @@ -585,45 +634,60 @@ def pass_2(lines): # get line fields (label, opcode, indirect, addr) = split_fields(line) - print('#####: label=%s, opcode=%s, indirect=%s, addr=%s' + print('pass_2: 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: - error("ORG pseudo-op has bad address") + if label: + error("ORG pseudo-op may not have a label") return False - emit_block() # punch any accumulated code + if not addr or eval_expr(addr) is None: + error("ORG pseudo-op has a bad address") + return False + write_block() # write any code accumulated so far Dot = eval_expr(addr) + print('ORG: Dot set to %06o' % Dot) start_block(Dot) elif opcode == 'EQU': - # no code, but we define a label + # no code, but we must have a label if not label: error("EQU pseudo-op must have a label") return False if not addr or eval_expr(addr) is None: - error("EQU pseudo-op has bad value") + error("EQU pseudo-op has a bad value") return False + value = eval_expr(addr) # check EQU value unchanged try: old_value = SymTable[label] - value = eval_expr(addr) if value != old_value: error("EQU value in '%s' has changed,\n" "was %06o, is now %06o" % (old_value, value)) except KeyError: - error("EQU label '%s' not defined in second pass!?" + error("EQU label '%s' wasn't defined in first pass!?" % label) elif opcode == 'BSS': # no code, but Dot moves if not addr or eval_expr(addr) is None: error("BSS pseudo-op has bad value") return False - emit_block() # punch any accumalated code - Dot += eval_expr(addr) + value = eval_expr(addr) + if label: + try: + old_value = SymTable[label] + if value != old_value: + error("BSS value in has changed, " + "was %06o, is now %06o" + % (old_value, value)) + except KeyError: + error("BSS label '%s' wasn't defined in first pass!?" + % label) + write_block() # write any code accumulated so far + Dot += value start_block(Dot) elif opcode == 'DATA': @@ -631,7 +695,18 @@ def pass_2(lines): if not addr or eval_expr(addr) is None: error("BSS pseudo-op has bad value") return False - emit_word(eval_expr(addr)) + value = eval_expr(addr) + if label: + try: + old_value = SymTable[label] + if value != old_value: + error("BSS value has changed, " + "was %06o, is now %06o" + % (old_value, value)) + except KeyError: + error("DATA label '%s' wasn't defined in first pass!?" + % label) + emit_word(value) Dot += 1 elif opcode == 'ASCII': @@ -645,27 +720,27 @@ def pass_2(lines): elif opcode == 'END': # get optional start address - global StartAddress - - 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 - + StartAddress = None + if addr: + # we have the optional start address + start_address = eval_expr(addr) + if start_address != StartAddress: + error("Pass 2 start address is different from pass 1, " + "was %06o but now %06o" + % (StartAddress, start_address)) + StartAddress = start_address + break # end of pass else: # actual machine instruction! # if label, check value unchanged if label: if not label in SymTable: - error("Label '%s' not defined in pass 2!?" % label) + error("Label '%s' not defined in pass 1!?" % label) old_dot = SymTable[label] if old_dot != Dot: - error("Label '%s' has different value in pass 2.\n" + error("Start address has different value in pass 2.\n" "Was %06o, now %06o" - % (label. old_dot, Dot)) + % (old_dot, Dot)) gen_code(lnum, line, label, label, opcode, indirect, addr) Dot += 1 @@ -674,13 +749,15 @@ def pass_2(lines): if label in SymTable: dot = SymTable[label] if dot != Dot: - error("Second pass, line '%s'\n" - "Label '%s' has value %06o, was %06o in first pass" - % (line, label, Dot, dor)) + error("Label '%s' has value %06o, was %06o in first pass" + % (label, Dot, dot)) - # punch the final block of code and optional start address - emit_block() - emit_start() + # write the final block of code and optional start address + write_block() + if StartAddress is not None: + print('write_start(%06o)' % StartAddress) + write_start(StartAddress) + write_leader() # check nothing after END if lnum - 1 > len(lines): @@ -862,8 +939,9 @@ def assemble_file(): print('asm_lines=\n%s' % '\n'.join(asm_lines)) if pass_1(asm_lines): - print('SymTable=%s' % str(SymTable)) + print('After pass_1(), SymTable=%s' % str(SymTable)) pass_2(asm_lines) + print('After pass_2(), SymTable=%s' % str(SymTable)) def main(): """The assembler.""" @@ -922,6 +1000,7 @@ def main(): if __name__ == '__main__': main() + #/****************************************************************************** # Name : emitblock() #Description : Emit the code for the current block.