summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Yin <fkfd@fkfd.me>2022-08-23 20:30:57 +0800
committerFrederick Yin <fkfd@fkfd.me>2022-08-23 20:30:57 +0800
commitd6a48c54755a6f1bde076f4bbdd2cc218a092f60 (patch)
treea392515eacc99e9ddf891ecc905d5811a63e01b2
parentd8ad916c80ce1e1dcfeabb8441896e94f7f00568 (diff)
hack-vm: function commands and verbose mode
-rw-r--r--projects/hack-vm/__main__.py18
-rw-r--r--projects/hack-vm/arith_logic.py6
-rw-r--r--projects/hack-vm/branching.py6
-rw-r--r--projects/hack-vm/compare.py2
-rw-r--r--projects/hack-vm/function.py125
-rw-r--r--projects/hack-vm/memory.py16
6 files changed, 155 insertions, 18 deletions
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