diff --git a/asm/Rect.hack b/asm/Rect.hack index e69de29..a9c09d1 100644 --- a/asm/Rect.hack +++ b/asm/Rect.hack @@ -0,0 +1,25 @@ +0000000000000000 +1111111111111111 +0000000000010111 +1111111111111111 +0000000000010000 +1111111111111111 +0100000000000000 +1111111111111111 +0000000000010001 +1111111111111111 +0000000000010001 +1111111111111111 +1111111111111111 +0000000000010001 +1111111111111111 +0000000000100000 +1111111111111111 +0000000000010001 +1111111111111111 +0000000000010000 +1111111111111111 +0000000000001010 +1111111111111111 +0000000000010111 +1111111111111111 diff --git a/asm/Rect2.hack b/asm/Rect2.hack new file mode 100644 index 0000000..8965041 --- /dev/null +++ b/asm/Rect2.hack @@ -0,0 +1,25 @@ +0000000000000000 +1111110000010000 +0000000000010111 +1110001100000110 +0000000000010000 +1110001100001000 +0100000000000000 +1110110000010000 +0000000000010001 +1110001100001000 +0000000000010001 +1111110000100000 +1110111010001000 +0000000000010001 +1111110000010000 +0000000000100000 +1110000010010000 +0000000000010001 +1110001100001000 +0000000000010000 +1111110010011000 +0000000000001010 +1110001100000001 +0000000000010111 +1110101010000111 \ No newline at end of file diff --git a/asm/asm02.py b/asm/asm02.py index 6c57623..fdd91b8 100644 --- a/asm/asm02.py +++ b/asm/asm02.py @@ -7,7 +7,7 @@ d. HACK-Code erzeugen ''' -import sys +import sys, os from pathlib import Path import re from icecream import ic @@ -90,26 +90,48 @@ def searchSymbols(asm): ist Variable @x ein int, dann ohne @ in die Symboltable übernehmen ''' #ic(asm) - i = 0 + i = 16 for line in asm: if line.startswith('@'): - ic (i, " ",line[1:]) + #ic (i, " ",line[1:]) if line[1:].isdigit(): - ic(line[1:], " ist ein int") - - + if line not in symboltable: + addtoSymboltable(line, int(line[1:])) + elif line not in symboltable: + #ic("Symbol nicht in Table") + addtoSymboltable(line, i) + i = i+1 + +def ciinstruction(): + + +def addtoSymboltable(key,value): + ic(key, value) + symboltable[key]=value +def createAsmFile(): + datename = os.path.splitext(os.path.basename(filename))[0]+".hack" + ic(datename) + f = open(datename,"w") + for line in asm: + if line in symboltable: + f.write('{0:016b}'.format(symboltable[line]) + "\n") + else: + #M=D + f.write('1111111111111111\n') + f.close() if __name__ == "__main__": - #filename = sys.argv[1] - filename = "Rect.asm" + filename = sys.argv[1] + #filename = "Pong.asm" symboltable = createSymboltable() asm = load_asm_file(filename) label_to_symboltable(asm) searchSymbols(asm) + createAsmFile() #print("Symtable = ",symboltable) diff --git a/asm/assembler02.py b/asm/assembler02.py index e58751a..1ea2138 100644 --- a/asm/assembler02.py +++ b/asm/assembler02.py @@ -168,8 +168,8 @@ def load_asm_file(): Carriage returns are removed. """ - ic(Path(sys.argv[1]).expanduser().read_text().replace( - '\r', '').split('\n')) + #ic(Path(sys.argv[1]).expanduser().read_text().replace( + # '\r', '').split('\n')) return Path(sys.argv[1]).expanduser().read_text().replace( '\r', '').split('\n') @@ -179,18 +179,18 @@ def load_asm_file(): Anything inside a line, after a "//" sequence is a comment. """ def filter_comment_and_blank_in_line(l): - ic(re.sub('\s', '', l).split('//')[0]) + #ic(re.sub('\s', '', l).split('//')[0]) return re.sub('\s', '', l).split('//')[0] - ic([filter_comment_and_blank_in_line(l) for l in lines]) + #ic([filter_comment_and_blank_in_line(l) for l in lines]) return [filter_comment_and_blank_in_line(l) for l in lines] def remove_empty_lines(file): - ic("remove_empty_lines ",[ l for l in file if len(l) > 0]) + #ic("remove_empty_lines ",[ l for l in file if len(l) > 0]) return [l for l in file if len(l) > 0] #ic("vor remove empty lines") - ic(remove_empty_lines(filter_comment_and_blank_in_lines(read_lines()))) + #ic(remove_empty_lines(filter_comment_and_blank_in_lines(read_lines()))) return remove_empty_lines( filter_comment_and_blank_in_lines( @@ -198,17 +198,17 @@ def load_asm_file(): def is_label(line): - ic(line) + #ic(line) """Recognise "label" declarations A label is an line in the form `"(" LABEL_NAME ")"` """ - ic(line.startswith('(') and line.endswith(')')) + #ic(line.startswith('(') and line.endswith(')')) return line.startswith('(') and line.endswith(')') def extract_label_name(label_declaration): - ic("extract_label_name ", label_declaration.strip('()')) + #ic("extract_label_name ", label_declaration.strip('()')) """Extract the label name from a label instruction""" return label_declaration.strip('()') @@ -218,7 +218,7 @@ def is_a_instruction(line): An A-instruction starts with "@" """ - ic("is_a_instruction ",line.startswith('@')) + #ic("is_a_instruction ",line.startswith('@')) return line.startswith('@') @@ -256,7 +256,7 @@ def default_symbol_table(): def inc_p_c(line, program_counter): - ic("inc_p_c ", line, program_counter ) + #ic("inc_p_c ", line, program_counter ) """Increment `program_counter` if `line` is an instruction""" if is_label(line): return program_counter @@ -429,7 +429,7 @@ def get_op_code(c_instruction): def assemble_op_code_no_M(op_code): - ic(op_code) + #ic(op_code) """Convert an assembly op code to its binary counterpart Note that this method assumes that the A/M switch is made. It @@ -492,14 +492,14 @@ def assemble_op_code_no_M(op_code): def assemble_op_code(op_code): - ic("assemble_op_code ", op_code) + #ic("assemble_op_code ", op_code) """Assemble the A/M switch and the op-code""" return ('1' if 'M' in op_code else '0') + \ assemble_op_code_no_M(op_code.replace('M', 'A')) def assemble_c_instruction(c_instruction): - ic("assemble_c_instruction ", c_instruction) + #ic("assemble_c_instruction ", c_instruction) """Assemble a c-instruction The binary representation of a c-instruction is diff --git a/asm/assembler04.py b/asm/assembler04.py new file mode 100644 index 0000000..d083350 --- /dev/null +++ b/asm/assembler04.py @@ -0,0 +1,156 @@ +#import re +#import sys +import argparse + +def convert_assembly_to_binary_file(asm_file, binary_file): + with open(asm_file, "r") as f: + result = translate_lines(f.readlines()) + output = "\n".join([l for l in result if l]) + with open(binary_file, "w") as f: + f.write(output) + +def translate_lines(lines): + lines = strip_whitespace_and_comments(lines) + symbol_table = build_symbol_table(lines) + translate_instruction = build_instruction_translator(symbol_table) + return [translate_instruction(x) for x in lines] + +def strip_whitespace_and_comments(lines): + instructions = [] + for line in lines: + stripped_line = line.strip() + if stripped_line: + if not stripped_line.startswith("//"): + if "//" in stripped_line: + instructions.append(stripped_line.split("//")[0].strip()) + else: + instructions.append(stripped_line) + return instructions + +def build_symbol_table(lines): + symbols = { + "R0": "0000000000000000", + "R1": "0000000000000001", + "R2": "0000000000000010", + "R3": "0000000000000011", + "R4": "0000000000000100", + "R5": "0000000000000101", + "R6": "0000000000000110", + "R7": "0000000000000111", + "R8": "0000000000001000", + "R9": "0000000000001001", + "R10": "0000000000001010", + "R11": "0000000000001011", + "R12": "0000000000001100", + "R13": "0000000000001101", + "R14": "0000000000001110", + "R15": "0000000000001111", + "SP": "0000000000000000", + "ARG": "0000000000000010", + "LCL": "0000000000000001", + "THIS": "0000000000000011", + "THAT": "0000000000000100", + "KBD": "0110000000000000", + "SCREEN": "0100000000000000" + } + is_address_instruction = lambda x: x.startswith("@") + is_compute_instruction = lambda x: "=" in x or ";" in x + label_value = lambda x: x.replace("(", "").replace(")", "").strip() + current_line_num = 0 + for line in lines: + if is_address_instruction(line) or is_compute_instruction(line): + current_line_num +=1 + elif is_label(line): + symbols[label_value(line)] = decimal_to_binary(current_line_num) + base_address = 16 + for line in lines: + if line.startswith("@"): + value = line[1:] + if value not in symbols and not value.isnumeric(): + symbols[value] = decimal_to_binary(base_address) + base_address += 1 + return symbols + +def build_instruction_translator(symbol_table): + COMPUTATIONS = { + "0": "0101010", + "1": "0111111", + "-1": "0111010", + "D": "0001100", + "A": "0110000", + "!D": "0001101", + "!A": "0110001", + "-D": "0001111", + "-A": "0110011", + "D+1": "0011111", + "A+1": "0110111", + "D-1": "0001110", + "A-1": "0110010", + "D+A": "0000010", + "D-A": "0010011", + "A-D": "0000111", + "D&A": "0000000", + "D|A": "0010101", + "M": "1110000", + "!M": "1110001", + "-M": "1110011", + "M+1": "1110111", + "M-1": "1110010", + "D+M": "1000010", + "D-M": "1010011", + "M-D": "1000111", + "D&M": "1000000", + "D|M": "1010101" + } + DESTINATIONS = { + "": "000", + "M": "001", + "D": "010", + "MD": "011", + "A": "100", + "AM": "101", + "AD": "110", + "AMD": "111" + } + JUMPS = { + "": "000", + "JGT": "001", + "JEQ": "010", + "JGE": "011", + "JLT": "100", + "JNE": "101", + "JLE": "110", + "JMP": "111" + } + def fn(line): + if is_label(line): + return + if line.startswith("@"): + value = line[1:] + if value in symbol_table: + return symbol_table[value] + return decimal_to_binary(int(value)) + dest, jump = "", "" + comp = line.split("=").pop().split(";")[0] + if "=" in line: + dest = line.split("=")[0] + if ";" in line: + jump = line.split(";").pop() + return f"111{COMPUTATIONS.get(comp, '0000000')}{DESTINATIONS.get(dest, '000')}{JUMPS.get(jump, '000')}" + return fn + +def is_label(line): + return line.startswith("(") and line.endswith(")") + +def decimal_to_binary(decimal_value): + return f"{decimal_value:0>16b}" + +if __name__ == "__main__": + #parser = argparse.ArgumentParser(description="Generates a hack binary file from assembly") + #parser.add_argument("Rect.asm") + #parser.add_argument("Rect.hack") + #parser.add_argument("asm_file", help="name of a HACK assembly file, i.e input.asm") + #parser.add_argument("binary_file", help="name of the HACK file, i.e output.hack") + #args = parser.parse_args() + #convert_assembly_to_binary_file(args.asm_file, args.binary_file) + convert_assembly_to_binary_file('Rect.asm', 'Rect.hack') \ No newline at end of file diff --git a/asm/assembler05.py b/asm/assembler05.py new file mode 100644 index 0000000..a83aca9 --- /dev/null +++ b/asm/assembler05.py @@ -0,0 +1,76 @@ +import re + +acommands=[] +asm=[] + +dest={'':'000','M=':'001','D=':'010','MD=':'011', + 'A=':'100','AM=':'101','AD=':'110','AMD=':'111'} +jump={'':'000',';JGT':'001',';JEQ':'010',';JGE':'011', + ';JLT':'100',';JNE':'101',';JLE':'110',';JMP':'111'} +comp={'0':'0101010','1':'0111111','-1':'0111010','D':'0001100', + 'A':'0110000','M':'1110000','!D':'0001101','!A':'0110001', + '!M':'1110001','-D':'0001111','-A':'0110011','-M':'1110011', + 'D+1':'0011111','A+1':'0110111','M+1':'1110111','D-1':'0001110', + 'A-1':'0110010','M-1':'1110010','D+A':'0000010','D+M':'1000010', + 'D-A':'0010011','D-M':'1010011','A-D':'0000111','M-D':'1000111', + 'D&A':'00000000','D&M':'1000000','D|A':'0010101','D|M':'1010101'} +symbols={'SP':0,'LCL':1,'ARG':2,'THIS':3,'THAT':4,'SCREEN':16384,'KBD':24576, + 'R0':0,'R1':1,'R1':1,'R2':2,'R3':3,'R4':4,'R5':5,'R6':6,'R7':7, + 'R8':8,'R9':9,'R10':10,'R11':11,'R12':12,'R13':13,'R14':14,'R15':15} + +f=input('File:') +asmfile=open(f+'.asm','r') +for line in asmfile: + ln=re.sub(r'\/+.*\n|\n| *','',line) + if ln!='': + acommands.append(ln) +asmfile.close() + +lineno=0 +for command in acommands: + symbol=re.findall(r'\(.+\)',command) + if symbol!=[]: + if symbol[0][1:-1] not in symbols: + symbols[symbol[0][1:-1]] = lineno + lineno-=1 + lineno+=1 + +for line in acommands: + ln=re.sub(r'\(.+\)','',line) + if ln!='': + asm.append(ln) + +variableno=16 +for command in asm: + symbol=re.findall(r'@[a-zA-Z]+.*',command) + if symbol!=[]: + if symbol[0][1:] not in symbols: + symbols[symbol[0][1:]] = variableno + variableno+=1 + +hackfile=open(f+'1.hack','w') +for command in asm: + if command[0]=='@': + address=0 + if command[1:] in symbols: + address=symbols[command[1:]]+32768 + else: + address=int(command[1:])+32768 + hackfile.write('0'+bin(address)[3:]+'\n') + else: + de=re.findall(r'.+=',command) + if de!=[]: + d=dest[str(de[0])] + else: + d=dest[''] + + je=re.findall(r';.+',command) + if je!=[]: + j=jump[str(je[0])] + else: + j=jump[''] + + c=comp[re.sub(r'.+=|;.+','',command)] + hackfile.write('111'+c+d+j+'\n') + +hackfile.close()