#!/usr/bin/python # -*- coding: utf-8 -*- ############## IMPORTS ################## from __future__ import print_function from os import path from sys import argv, exit from colorama import init as initColorama from colorama import Fore, Style from struct import pack from binascii import unhexlify as unHex ######################################### ######### CROSS-PYTHON HACK ############# try: input = raw_input # For Python 2 except NameError: pass # For Python 3 ######################################### # Init colorama module initColorama() ######### CONSTANTS ######### # Instruction table dictionary instructionTable = { 'nop': 0, 'stax b': 2, 'inx b': 3, 'inr b': 4, 'dcr b': 5, 'rlc': 7, 'dad b': 9, 'ldax b': 10, 'dcx b': 11, 'inr c': 12, 'dcr c': 13, 'rrc': 15, 'stax d': 18, 'inx d': 19, 'inr d': 20, 'dcr d': 21, 'ral': 23, 'dad d': 25, 'ldax d': 26, 'dcx d': 27, 'inr e': 28, 'dcr e': 29, 'rar': 31, 'rim': 32, 'inx h': 35, 'inr h': 36, 'dcr h': 37, 'daa': 39, 'dad h': 41, 'dcx h': 43, 'inr l': 44, 'dcr l': 45, 'cma': 47, 'sim': 48, 'inx sp': 51, 'inr m': 52, 'dcr m': 53, 'stc': 55, 'dad sp': 57, 'dcx sp': 59, 'inr a': 60, 'dcr a': 61, 'push b': 197, 'rst 0': 199, 'rz': 200, 'ret': 201, 'rst 1': 207, 'rnc': 208, 'pop d': 209, 'push d': 213, 'rst 2': 215, 'rc': 216, 'rst 3': 223, 'rpo': 224, 'pop h': 225, 'xthl': 227, 'push h': 229, 'rst 4': 231, 'rpe': 232, 'pchl': 233, 'xchg': 235, 'rst 5': 239, 'rp': 240, 'pop psw': 241, 'di': 243, 'push psw': 245, 'rst 6': 247, 'rm': 248, 'sphl': 249, 'ei': 251, 'rst 7': 255, 'cmc': 63, 'mov b,b': 64, 'mov b,c': 65, 'mov b,d': 66, 'mov b,e': 67, 'mov b,h': 68, 'mov b,l': 69, 'mov b,m': 70, 'mov b,a': 71, 'mov c,b': 72, 'mov c,c': 73, 'mov c,d': 74, 'mov c,e': 75, 'mov c,h': 76, 'mov c,l': 77, 'mov c,m': 78, 'mov c,a': 79, 'mov d,b': 80, 'mov d,c': 81, 'mov d,d': 82, 'mov d,e': 83, 'mov d,h': 84, 'mov d,l': 85, 'mov d,m': 86, 'mov d,a': 87, 'mov e,b': 88, 'mov e,c': 89, 'mov e,d': 90, 'mov e,e': 91, 'mov e,h': 92, 'mov e,l': 93, 'mov e,m': 94, 'mov e,a': 95, 'mov h,b': 96, 'mov h,c': 97, 'mov h,d': 98, 'mov h,e': 99, 'mov h,h': 100, 'mov h,l': 101, 'mov h,m': 102, 'mov h,a': 103, 'mov l,b': 104, 'mov l,c': 105, 'mov l,d': 106, 'mov l,e': 107, 'mov l,h': 108, 'mov l,l': 109, 'mov l,m': 110, 'mov l,a': 111, 'mov m,b': 112, 'mov m,c': 113, 'mov m,d': 114, 'mov m,e': 115, 'mov m,h': 116, 'mov m,l': 117, 'hlt': 118, 'mov m,a': 119, 'mov a,b': 120, 'mov a,c': 121, 'mov a,d': 122, 'mov a,e': 123, 'mov a,h': 124, 'mov a,l': 125, 'mov a,m': 126, 'mov a,a': 127, 'add b': 128, 'add c': 129, 'add d': 130, 'add e': 131, 'add h': 132, 'add l': 133, 'add m': 134, 'add a': 135, 'adc b': 136, 'adc c': 137, 'adc d': 138, 'adc e': 139, 'adc h': 140, 'adc l': 141, 'adc m': 142, 'adc a': 143, 'sub b': 144, 'sub c': 145, 'sub d': 146, 'sub e': 147, 'sub h': 148, 'sub l': 149, 'sub m': 150, 'sub a': 151, 'sbb b': 152, 'sbb c': 153, 'sbb d': 154, 'sbb e': 155, 'sbb h': 156, 'sbb l': 157, 'sbb m': 158, 'sbb a': 159, 'ana b': 160, 'ana c': 161, 'ana d': 162, 'ana e': 163, 'ana h': 164, 'ana l': 165, 'ana m': 166, 'ana a': 167, 'xra b': 168, 'xra c': 169, 'xra d': 170, 'xra e': 171, 'xra h': 172, 'xra l': 173, 'xra m': 174, 'xra a': 175, 'ora b': 176, 'ora c': 177, 'ora d': 178, 'ora e': 179, 'ora h': 180, 'ora l': 181, 'ora m': 182, 'ora a': 183, 'cmp b': 184, 'cmp c': 185, 'cmp d': 186, 'cmp e': 187, 'cmp h': 188, 'cmp l': 189, 'cmp m': 190, 'cmp a': 191, 'rnz': 192, 'pop b': 193, } # Instruction table dictionary that expects a secondary parameter (8-bit) varInstructionTable_EigthBit = { 'mvi b,': 6, 'mvi c,': 14, 'mvi d,': 22, 'mvi e,': 30, 'mvi h,': 38, 'mvi l,': 46, 'sta': 50, 'mvi m,': 54, 'lda': 58, 'mvi a,': 62, 'adi': 198, 'aci': 206, 'out': 211, 'sui': 214, 'in': 219, 'sbi': 222, 'ani': 230, 'xri': 238, 'ori': 246, 'cpi': 254 } # Instruction table dictionary that expects a secondary parameter (16-bit) varInstructionTable_SixteenBit = { 'lxi b,': 1, 'lxi d,': 17, 'lxi h,': 33, 'shld': 34, 'lhld': 42, 'lxi sp,': 49, 'jnz': 194, 'jmp': 195, 'cnz': 196, 'jz': 202, 'cz': 204, 'call': 205, 'jnc': 210, 'cnc': 212, 'cc': 220, 'jc': 218, 'jpo': 226, 'cpo': 228, 'jpe': 234, 'cpe': 236, 'jp': 242, 'cp': 244, 'jm': 250, 'cm': 252 } helpArgVariants = ['-h', '--h', '-help', '--help'] ######### FUNCTIONS ######### # Print a small ASCII art banner def banner(): print(Style.DIM) print(' ___________________________') print(' / /\\') print(' / sadboyzvone\'s _/ /\\') print(' / Intel 8080 / \/') print(' / Assembler /\\') print('/___________________________/ /') print('\___________________________\/') print(' \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\' + Style.RESET_ALL + Style.BRIGHT) print(Fore.WHITE + '\nPowered by ' + Fore.BLUE + 'Pyt' + Fore.YELLOW + 'hon' + Fore.WHITE + '\nCopyright (C) 2017, Zvonimir Rudinski') # Print usage information def printHelp(): print('\nThis ' + Fore.BLUE + 'Intel' + Fore.WHITE + ' 8080 assembler was made for ' + Fore.BLUE + 'Project ' + Fore.YELLOW + 'Week' + Fore.WHITE + ' at my school.') print('It is written in ' + Fore.BLUE + 'Pyt' + Fore.YELLOW + 'hon' + Fore.WHITE) print('Modules: ' + Fore.RED + 'Co' + Fore.BLUE + 'lo' + Fore.YELLOW + 'ra' + Fore.GREEN + 'ma' + Fore.WHITE) print('\nPass a file path as an arguement.') # Main function def run(fileNameArg): banner() # Print banner # File name fileName = None # Variable and label info labelMap = {} variableMap = {} # Program counter programCounter = 0 try: if fileNameArg in helpArgVariants: printHelp() # Print help then exit exit(0) else: fileName = fileNameArg # Argument is provided print('Trying to open ' + Fore.YELLOW + '\'' + fileName + '\'' + Fore.WHITE) if path.isfile(fileName) is False: # Check if the file exists print(Fore.RED + 'Fatal error: ' + Fore.WHITE + 'File not found: ' + Fore.YELLOW + '\'' + fileName + '\'') raise IOError # It doesn't raise an exception # Read in the source code from the file with open(fileName, 'r') as sourceFile: sourceCode = sourceFile.readlines() # Strip the newlines sourceCode = map(lambda sc: sc.strip(), sourceCode) # Start compiling the code with open(fileName + '.rom', 'wb+') as romFile: # Check the line for (i, scLine) in enumerate(sourceCode): scLine = scLine.lower() # Turn it to lower case for easier lookup # Check for ORG if scLine.split(' ')[0] == "org": programCounter = int(scLine.split(' ')[1].zfill(4)) print("ORG set to " + str(programCounter)) romFile.seek(0,0) for x in range(0,programCounter): romFile.write(pack('B', instructionTable['nop'])) romFile.seek(programCounter,0) continue # Check if it's a label if len(scLine.split(':')) > 1: print('Updating labels') labelMap[scLine.split(':')[0]] = unHex( str(programCounter).zfill(4)) continue # Check if it's in the instruction table if scLine in instructionTable: # Write the opcode romFile.write(pack('B', instructionTable[scLine])) programCounter += 1 # 1 byte continue elif scLine.split(' ')[1] == 'equ': # Check if it's a variable declaration if int(scLine.split(' ')[2]) >= 2 ** 8: # Number is out of bounds for Intel 8080 print(Fore.RED + 'Variable too large: ' + scLine + ' : Line ' + str(i + 1)) raise SyntaxError variableMap[scLine.split(' ')[0]] = unHex(scLine.split( ' ')[2].ljust(4, '0')) # It is, save it to a dictionary print('Updating variables') continue else: # Check if it's in a instruction table (8-bit) for tableKey in varInstructionTable_EigthBit.keys(): if scLine.startswith(tableKey): # Write the opcode romFile.write( pack('B', varInstructionTable_EigthBit[tableKey])) try: # Check if it's a variable variable = (scLine.split(',')[1].strip( ) if ',' in scLine else scLine.split()[1].strip()) if variable in variableMap.keys(): # If it is get it's value from the dict romFile.write(variableMap[variable][:1]) # Python3 had an error if I tried `variableMap[variable][0]` so I'll just start from the beginning and stop before the end...it's a hack I know ;) elif variable in labelMap.keys(): # It it is get it's value from the dict romFile.write(labelMap[variable]) else: # Else write it down romFile.write(unHex(variable)) programCounter += 2 # 2 bytes break except (ValueError, TypeError): # That's not even a number...or a variable name print(Fore.RED + 'Invalid variable use: ' + scLine + ' : Line ' + str(i + 1)) raise SyntaxError else: # Check if it's in a instruction table (16-bit) for tableKey in varInstructionTable_SixteenBit.keys(): if scLine.startswith(tableKey): # Write the opcode romFile.write( pack('B', varInstructionTable_SixteenBit[tableKey])) try: # Check if it's a variable variable = (scLine.split(',')[1].strip( ) if ',' in scLine else scLine.split()[1].strip()) if variable in variableMap.keys(): # If it is get it's value from the dict romFile.write(variableMap[variable]) elif variable in labelMap.keys(): # It is, so get it's value from the dict romFile.write(labelMap[variable]) else: # Else write it down romFile.write(unHex(variable)) programCounter += 3 # 3 bytes break except (ValueError, TypeError): # That's not even a number...or a variable name print(Fore.RED + 'Invalid variable use: ' + scLine + ' : Line ' + str(i + 1)) raise SyntaxError else: print(Fore.RED + 'Syntax error: ' + scLine + ' : Line ' + str(i + 1)) raise SyntaxError # All was good print(Fore.WHITE + 'Closing down... ' + Fore.YELLOW + '\'' + fileName + Fore.WHITE + '\'\nEverything went ' + Fore.GREEN + 'fine') except (KeyboardInterrupt, EOFError, IOError, SyntaxError): # Universal exception handler print(Fore.RED + '\nExiting...') exit(1) # A peaceful exit to be honest ;) # Call main if __name__ == '__main__': if len(argv) is not 2: printHelp() else: run(argv[1])