# CROSS ASSEMBLER FOR THE LITTLE MAN COMPUTER MK. V # # INPUT FILE POSSIBLE LINE FORMATS: # LABEL: OPCODE TARGET IGNORED # OPCODE TARGET IGNORED # LABEL: "EQU" HEXBYTE IGNORED # "*" IGNORED import sys ##### ##### DECLARATIONS ##### i = 0 _Start = 0x0100 # base address for the assembled machine code _Line = '' _Address = _Start _Inc = 0x00 # Offset of next OpCode _ThreeBytes = ['ADD', 0x10, 'SUB', 0x20, 'STA', 0x30, 'AND', 0x40, 'LDA', 0x50, 'JMP', 0x60, 'BRZ', 0x70, 'BRB', 0x7b, 'BRF', 0x7f] _SingleByte = ['HLT', 0x00, 'INP', 0x93, 'OUT', 0x94, 'RDT', 0x95, 'LSR', 0xf2, 'LSL', 0xf3, 'INC', 0xf4, 'DEC', 0xf5, 'NOP', 0xff] _Labels = [ ] ##### ##### ACCESS COMMANDD LINE ARGUMENT ##### if len(sys.argv) != 2: raise SystemExit('Error: Incorrect Number of Arguments.') _AsmFile = (str(sys.argv[1])) # _AsmFile now holds name of .asm source file ##### ##### OPEN FILES ##### _Target = open('.scratch.pad', 'w') # scratch.pad is the intermediate 'object file' _Source = open(_AsmFile, 'r') # The source code file from CL Argument ##### ##### FIRST PASS: TOKENIZE, NUMBER LINES, LIST LABEL ADDRESSES ##### for _Line in _Source: _Line = _Line.replace('\t', ' ') # Clean out tabs _Line = _Line.replace('\n', '') # Remove new lines _Tokens = _Line.split() # Tokenize for _Token in _Tokens: # Examine each token if _Token.find(':') >= 1: # If this _Token is a 'Label:'... _Labels.append(_Token[:-1]) # then add it to the list of labels _Labels.append(_Address) # followed by its address continue if _Token in _ThreeBytes: # Token denotes Three Byte Opcode i = _ThreeBytes.index(_Token) _Target.write(hex(_ThreeBytes[i+1])) _Target.write(' ') _Address = _Address + 3 i = _Tokens.index(_Token) _Target.write(_Tokens[i+1]) continue if _Token in _SingleByte: # Token denotes Single Byte Opcode i = _SingleByte.index(_Token) _Target.write(hex(_SingleByte[i+1])) _Target.write(' ') _Address = _Address + 1 continue if _Token == 'EQU': # EQU is a Compiler Symbol i = _Tokens.index(_Token) _Target.write(_Tokens[i+1]) _Address = _Address + 1 continue if _Token == '*': # Denotes this line is a comment continue _Target.write('\n') _Target.close() _Source.close() ##### ##### SECOND PASS - RAPLACE TARGETS WITH HEX ADDRESSES ##### _Object = open('.object.obj', 'w') _Scratch = open('.scratch.pad', 'r') _Byte = 0x00 _MaskFF00 = 0xFF00 _Mask00FF = 0x00FF for _Line in _Scratch: # READ INTEREM CODE FROM SCRATCH _Object.write(_Line[0:4]) # PASS OPCODE BYTE TO OUTPUT FILE _Found = False # NO LABEL FOUND YET for _Label in _Labels: # SEARCH THROUGH ALL LABELS if str(_Label) in _Line: # IF THE CURRENT LABEL IS IN CURRENT LINE... i = _Labels.index(_Label) # NOTE ITS INDEX j = i + 1 # AND THE INDEX OF THE ASSOCIATED ADDRESS if len(_Labels) > j: # IF WE'RE NOT AT THE END OF LABELS _Labels[i+1] = int(_Labels[i+1]) # HEXIFY IT _Byte = str(int(((_Labels[i+1]) & (_MaskFF00)) / 256)) # ISOLATE HI ORDER BYTE _Object.write(' 0x') _Object.write(_Byte.rjust(2, '0')) _Byte = str(int(((_Labels[i+1]) & (_Mask00FF)) / 1)) # ISOLATE LO ORDER BYTE _Object.write(' 0x') _Object.write(_Byte.rjust(2, '0')) _Found = True # NOTE THAT LABEL FOUND _Object.write('\n') _Object.close() _Scratch.close() ##### ##### THIRD PASS - TURN OBJECT FILE INTO BINARY EXECUTABLE ##### _Object = open('.object.obj', 'r') _Tape = open('tape', 'w') for _Line in _Object: for _Byte in _Line.split(): _Num = int(_Byte, 16) _Tape.write(chr(_Num))