diff options
author | Frederick Yin <fkfd@fkfd.me> | 2022-08-22 17:04:19 +0800 |
---|---|---|
committer | Frederick Yin <fkfd@fkfd.me> | 2022-08-22 17:04:19 +0800 |
commit | 10c9f64f592198d78aad11ebc676629d92ca6576 (patch) | |
tree | 1b4f8a21c72dae1700d92892a619ad9ddaf25cbf /projects/hack-vm/memory.py | |
parent | 900bd8fe1934f83ea35ab8673dda5239ae2dfca8 (diff) |
Project 07: VM translator part 1
Diffstat (limited to 'projects/hack-vm/memory.py')
-rw-r--r-- | projects/hack-vm/memory.py | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/projects/hack-vm/memory.py b/projects/hack-vm/memory.py new file mode 100644 index 0000000..ad22482 --- /dev/null +++ b/projects/hack-vm/memory.py @@ -0,0 +1,129 @@ +from .utils import * + +# common instructions all push commands end with +PUSH_COMMON_ASM = """@SP +A=M +M=D +@SP +M=M+1 +""" + +# push <segment> <index> +# when <segment> is one of local, argument, this and that +PUSH_ASM = ( + """@{segment} +D=M +@{index} +A=D+A +D=M +""" + + PUSH_COMMON_ASM +) + +# when <segment> is one of static, temp and pointer +PUSH_FIXED_ASM = ( + """@{addr} +D=M +""" + + PUSH_COMMON_ASM +) + +# push constant <constant> +PUSH_CONSTANT_ASM = ( + """@{constant} +D=A +""" + + PUSH_COMMON_ASM +) + + +# pop <segment> <index> +# when <segment> is one of local, argument, this and that +POP_ASM = """@{index} +D=A +@{segment} +M=M+D +@SP +M=M-1 +A=M +D=M +@{segment} +A=M +M=D +@{index} +D=A +@{segment} +M=M-D +""" + +# when <segment> is one of static, temp and pointer +POP_FIXED_ASM = """@SP +M=M-1 +A=M +D=M +@{addr} +M=D +""" + +SEGMENT_PTR = { + "local": "LCL", + "argument": "ARG", + "this": "THIS", + "that": "THAT", +} + +SEGMENT_SIZE = { + "pointer": 2, # 3..4, i.e. THIS and THAT + "temp": 8, # 5..12 + "static": 240, # 16..255 +} + + +def translate_memory(action, segment, index, prog): + try: + index = int(index) + except ValueError: + exit_on_error( + f"Syntax error: invalid segment index {index}", EXIT_CODE_SYNTAX_ERROR + ) + + if index < 0: + exit_on_error( + f"Address error: negative segment index {index}", EXIT_CODE_ADDR_ERROR + ) + + asm = "" + 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) + elif segment in ["static", "temp", "pointer"]: + # these segments have a fixed location and size on memory + if index >= SEGMENT_SIZE[segment]: + exit_on_error( + f"Address error: segment index {index} exceeds size of {segment}", + EXIT_CODE_ADDR_ERROR, + ) + + addr = "" + if segment == "static": + # translate "static i" in "prog.vm" into "@prog.i" + addr = f"{prog}.{index}" + elif segment == "temp": + addr = str(5 + index) + else: + # "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) + 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) + else: + exit_on_error( + f"Syntax error: invalid memory segment {segment}", EXIT_CODE_SYNTAX_ERROR + ) |