From d6a48c54755a6f1bde076f4bbdd2cc218a092f60 Mon Sep 17 00:00:00 2001 From: Frederick Yin Date: Tue, 23 Aug 2022 20:30:57 +0800 Subject: hack-vm: function commands and verbose mode --- projects/hack-vm/__main__.py | 18 ++++-- projects/hack-vm/arith_logic.py | 6 +- projects/hack-vm/branching.py | 6 +- projects/hack-vm/compare.py | 2 +- projects/hack-vm/function.py | 125 ++++++++++++++++++++++++++++++++++++++++ projects/hack-vm/memory.py | 16 ++--- 6 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 projects/hack-vm/function.py (limited to 'projects') diff --git a/projects/hack-vm/__main__.py b/projects/hack-vm/__main__.py index 5373fe9..33df369 100644 --- a/projects/hack-vm/__main__.py +++ b/projects/hack-vm/__main__.py @@ -3,10 +3,11 @@ from .memory import translate_memory from .arith_logic import translate_arith_logic from .compare import translate_compare from .branching import translate_branching +from .function import translate_function, translate_return from .utils import * -def vm_translate(input_path): +def vm_translate(input_path, verbose): # program name, for static variable labels input_fn = input_path.split("/")[-1] prog = input_fn[:-3] if input_fn.endswith(".vm") else input_fn @@ -34,13 +35,17 @@ def vm_translate(input_path): case []: continue case [("push" | "pop") as action, segment, index]: - asm += translate_memory(action, segment, index, prog) + asm += translate_memory(action, segment, index, prog, verbose) case [("add" | "sub" | "neg" | "and" | "or" | "not") as command]: - asm += translate_arith_logic(command) + asm += translate_arith_logic(command, verbose) case [("lt" | "eq" | "gt") as command]: - asm += translate_compare(command) + asm += translate_compare(command, verbose) case [("label" | "goto" | "if-goto") as action, label]: - asm += translate_branching(action, label) + asm += translate_branching(action, label, verbose) + case [("call" | "function") as action, function, n]: + asm += translate_function(action, function, n, prog, verbose) + case ["return"]: + asm += translate_return(verbose) case _: exit_on_error( f"Syntax error: invalid command: {cmd}", EXIT_CODE_SYNTAX_ERROR @@ -61,6 +66,7 @@ def vm_translate(input_path): if __name__ == "__main__": parser = ArgumentParser("hack-vm") + parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode") parser.add_argument("input_path", help="input file in HACK VM") args = parser.parse_args() - vm_translate(args.input_path) + vm_translate(args.input_path, args.verbose) diff --git a/projects/hack-vm/arith_logic.py b/projects/hack-vm/arith_logic.py index 6716ad9..a184067 100644 --- a/projects/hack-vm/arith_logic.py +++ b/projects/hack-vm/arith_logic.py @@ -22,5 +22,7 @@ ARITH_LOGIC_ASM = { } -def translate_arith_logic(command): - return ARITH_LOGIC_ASM[command] +def translate_arith_logic(command, verbose=False): + asm = f"// {command}\n" if verbose else "" + asm += ARITH_LOGIC_ASM[command] + return asm diff --git a/projects/hack-vm/branching.py b/projects/hack-vm/branching.py index b73ed94..9f94db6 100644 --- a/projects/hack-vm/branching.py +++ b/projects/hack-vm/branching.py @@ -20,5 +20,7 @@ BRANCHING_ASM = { } -def translate_branching(action, label): - return BRANCHING_ASM[action].format(label=label) +def translate_branching(action, label, verbose=False): + asm = f"// {action} {label}\n" if verbose else "" + asm += BRANCHING_ASM[action].format(label=label) + return asm diff --git a/projects/hack-vm/compare.py b/projects/hack-vm/compare.py index 1565b11..029236a 100644 --- a/projects/hack-vm/compare.py +++ b/projects/hack-vm/compare.py @@ -25,7 +25,7 @@ JMP = { tag_idx = 0 -def translate_compare(command): +def translate_compare(command, verbose=False): global tag_idx asm = COMPARE_ASM.format(tag=f"{command.upper()}_{tag_idx}", jmp=JMP[command]) tag_idx += 1 diff --git a/projects/hack-vm/function.py b/projects/hack-vm/function.py new file mode 100644 index 0000000..596c9cf --- /dev/null +++ b/projects/hack-vm/function.py @@ -0,0 +1,125 @@ +CALL_ASM = """@{return_address} +D=A +@SP +A=M +M=D +@LCL +D=A +@SP +AM=M+1 +M=D +@ARG +D=A +@SP +AM=M+1 +M=D +@THIS +D=A +@SP +AM=M+1 +M=D +@THAT +D=A +@SP +AM=M+1 +M=D +@SP +DM=M+1 +@LCL +M=D +@{arg_lcl_offset} +D=D-A +@ARG +M=D +@{function_label} +0;JMP +({return_address}) +""" + +FUNCTION_ASM = """({function_label}) +@SP +A=M +M=0 +{init_vars}@SP +M=M+1 +""" + +# repeated for each local variable of function +INIT_VAR_ASM = """@SP +AM=M+1 +M=0 +""" + +RETURN_ASM = """@ARG +A=M +D=M +@{tmp} +M=D +@SP +A=M-1 +D=M +@ARG +A=M +M=D +@ARG +D=M+1 +@SP +M=D +@LCL +AM=M-1 +D=M +@THAT +M=D +@LCL +AM=M-1 +D=M +@THIS +M=D +@LCL +AM=M-1 +D=M +@ARG +M=D +@LCL +AM=M-1 +D=M +@LCL +M=D +@{tmp} +A=M +0;JMP +""" + +ret_idx = {} + + +def translate_function(action, function, n, prog, verbose=False): + global ret_idx + try: + n = int(n) + except ValueError: + exit_on_error(f"Syntax error: not an integer: {n}", EXIT_CODE_SYNTAX_ERROR) + + asm = f"// {action} {function} {n}\n" if verbose else "" + if action == "call": + if prog not in ret_idx: + ret_idx[prog] = 0 + else: + ret_idx[prog] += 1 + asm += CALL_ASM.format( + return_address=f"{prog}$ret.{ret_idx[prog]}", + arg_lcl_offset=(5 + n), + function_label=f"{function}", + ) + else: + asm += FUNCTION_ASM.format( + function_label=f"{function}", init_vars=INIT_VAR_ASM * (n - 1) + ) + + return asm + + +def translate_return(verbose=False): + asm = "// return\n" if verbose else "" + asm += RETURN_ASM.format(tmp="R13") + return asm diff --git a/projects/hack-vm/memory.py b/projects/hack-vm/memory.py index 84f4a53..a8e2765 100644 --- a/projects/hack-vm/memory.py +++ b/projects/hack-vm/memory.py @@ -77,7 +77,7 @@ SEGMENT_SIZE = { } -def translate_memory(action, segment, index, prog): +def translate_memory(action, segment, index, prog, verbose=False): try: index = int(index) except ValueError: @@ -90,11 +90,11 @@ def translate_memory(action, segment, index, prog): f"Address error: negative segment index {index}", EXIT_CODE_ADDR_ERROR ) - asm = "" + asm = f"// {action} {segment} {index}\n" if verbose else "" if segment in ["local", "argument", "this", "that"]: # these segments are dynamic - asm = PUSH_ASM if action == "push" else POP_ASM - return asm.format(segment=SEGMENT_PTR[segment], index=index) + asm += PUSH_ASM if action == "push" else POP_ASM + asm = asm.format(segment=SEGMENT_PTR[segment], index=index) elif segment in ["static", "temp", "pointer"]: # these segments have a fixed location and size on memory if index >= SEGMENT_SIZE[segment]: @@ -113,15 +113,17 @@ def translate_memory(action, segment, index, prog): # "pointer 0" is THIS, "pointer 1" is THAT addr = "THIS" if index == 0 else "THAT" - asm = PUSH_FIXED_ASM if action == "push" else POP_FIXED_ASM - return asm.format(addr=addr) + asm += PUSH_FIXED_ASM if action == "push" else POP_FIXED_ASM + asm = asm.format(addr=addr) elif segment == "constant": if action == "pop": exit_on_error( f"Syntax error: popping to constant not allowed", EXIT_CODE_SYNTAX_ERROR ) - return PUSH_CONSTANT_ASM.format(constant=index) + asm += PUSH_CONSTANT_ASM.format(constant=index) else: exit_on_error( f"Syntax error: invalid memory segment {segment}", EXIT_CODE_SYNTAX_ERROR ) + + return asm -- cgit v1.2.3