Assembler erweitert

This commit is contained in:
Sven Riwoldt
2024-04-04 19:37:49 +02:00
parent c7bc862c6f
commit efc4bd08a5
6 changed files with 326 additions and 22 deletions

View File

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

25
asm/Rect2.hack Normal file
View File

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

View File

@@ -7,7 +7,7 @@
d. HACK-Code erzeugen d. HACK-Code erzeugen
''' '''
import sys import sys, os
from pathlib import Path from pathlib import Path
import re import re
from icecream import ic from icecream import ic
@@ -90,26 +90,48 @@ def searchSymbols(asm):
ist Variable @x ein int, dann ohne @ in die Symboltable übernehmen ist Variable @x ein int, dann ohne @ in die Symboltable übernehmen
''' '''
#ic(asm) #ic(asm)
i = 0 i = 16
for line in asm: for line in asm:
if line.startswith('@'): if line.startswith('@'):
ic (i, " ",line[1:]) #ic (i, " ",line[1:])
if line[1:].isdigit(): 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__": if __name__ == "__main__":
#filename = sys.argv[1] filename = sys.argv[1]
filename = "Rect.asm" #filename = "Pong.asm"
symboltable = createSymboltable() symboltable = createSymboltable()
asm = load_asm_file(filename) asm = load_asm_file(filename)
label_to_symboltable(asm) label_to_symboltable(asm)
searchSymbols(asm) searchSymbols(asm)
createAsmFile()
#print("Symtable = ",symboltable) #print("Symtable = ",symboltable)

View File

@@ -168,8 +168,8 @@ def load_asm_file():
Carriage returns are removed. Carriage returns are removed.
""" """
ic(Path(sys.argv[1]).expanduser().read_text().replace( #ic(Path(sys.argv[1]).expanduser().read_text().replace(
'\r', '').split('\n')) # '\r', '').split('\n'))
return Path(sys.argv[1]).expanduser().read_text().replace( return Path(sys.argv[1]).expanduser().read_text().replace(
'\r', '').split('\n') '\r', '').split('\n')
@@ -179,18 +179,18 @@ def load_asm_file():
Anything inside a line, after a "//" sequence is a comment. Anything inside a line, after a "//" sequence is a comment.
""" """
def filter_comment_and_blank_in_line(l): 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] 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] return [filter_comment_and_blank_in_line(l) for l in lines]
def remove_empty_lines(file): 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] return [l for l in file if len(l) > 0]
#ic("vor remove empty lines") #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( return remove_empty_lines(
filter_comment_and_blank_in_lines( filter_comment_and_blank_in_lines(
@@ -198,17 +198,17 @@ def load_asm_file():
def is_label(line): def is_label(line):
ic(line) #ic(line)
"""Recognise "label" declarations """Recognise "label" declarations
A label is an line in the form `"(" LABEL_NAME ")"` 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(')') return line.startswith('(') and line.endswith(')')
def extract_label_name(label_declaration): 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""" """Extract the label name from a label instruction"""
return label_declaration.strip('()') return label_declaration.strip('()')
@@ -218,7 +218,7 @@ def is_a_instruction(line):
An A-instruction starts with "@" An A-instruction starts with "@"
""" """
ic("is_a_instruction ",line.startswith('@')) #ic("is_a_instruction ",line.startswith('@'))
return line.startswith('@') return line.startswith('@')
@@ -256,7 +256,7 @@ def default_symbol_table():
def inc_p_c(line, program_counter): 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""" """Increment `program_counter` if `line` is an instruction"""
if is_label(line): if is_label(line):
return program_counter return program_counter
@@ -429,7 +429,7 @@ def get_op_code(c_instruction):
def assemble_op_code_no_M(op_code): def assemble_op_code_no_M(op_code):
ic(op_code) #ic(op_code)
"""Convert an assembly op code to its binary counterpart """Convert an assembly op code to its binary counterpart
Note that this method assumes that the A/M switch is made. It 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): 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""" """Assemble the A/M switch and the op-code"""
return ('1' if 'M' in op_code else '0') + \ return ('1' if 'M' in op_code else '0') + \
assemble_op_code_no_M(op_code.replace('M', 'A')) assemble_op_code_no_M(op_code.replace('M', 'A'))
def assemble_c_instruction(c_instruction): def assemble_c_instruction(c_instruction):
ic("assemble_c_instruction ", c_instruction) #ic("assemble_c_instruction ", c_instruction)
"""Assemble a c-instruction """Assemble a c-instruction
The binary representation of a c-instruction is The binary representation of a c-instruction is

156
asm/assembler04.py Normal file
View File

@@ -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')

76
asm/assembler05.py Normal file
View File

@@ -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()