Nand2Tetris — Capítulo 7
Traductor de VM I: operaciones de pila y segmentos de memoria
# VM Translator 1
# Entrada: archivo .vm
# Salida: archivo .asm
# Alcance: comandos push/pop/aritméticos
# movimiento:
# El puntero de la pila está apuntado por SP,
# push es *SP=valor y luego SP++,
# pop almacena el valor después de SP-- en el segmento correspondiente.
import sys
SEGMENT_BASE = {
"local": "LCL",
"argument": "ARG",
"this": "THIS",
"that": "THAT",
}
class VMTranslator:
def __init__(self):
self.label_id = 0
def push_d(self):
return [
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
]
def pop_to_d(self):
return [
"@SP",
"AM=M-1",
"D=M",
]
def write_arithmetic(self, command):
if command == "add":
return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M+D"]
if command == "sub":
return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M-D"]
if command == "and":
return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M&D"]
if command == "or":
return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M|D"]
if command == "neg":
return ["@SP", "A=M-1", "M=-M"]
if command == "not":
return ["@SP", "A=M-1", "M=!M"]
# eq, gt, lt guarda el resultado de la comparación como verdadero (-1) o falso (0) en la parte superior de la pila.
jump = {"eq": "JEQ", "gt": "JGT", "lt": "JLT"}[command]
true_label = f"TRUE_{self.label_id}"
end_label = f"END_{self.label_id}"
self.label_id += 1
return [
"@SP",
"AM=M-1",
"D=M",
"A=A-1",
"D=M-D",
f"@{true_label}",
f"D;{jump}",
"@SP",
"A=M-1",
"M=0",
f"@{end_label}",
"0;JMP",
f"({true_label})",
"@SP",
"A=M-1",
"M=-1",
f"({end_label})",
]
def write_push(self, segment, index, file_name):
if segment == "constant":
return [f"@{index}", "D=A"] + self.push_d()
if segment in SEGMENT_BASE:
return [
f"@{SEGMENT_BASE[segment]}",
"D=M",
f"@{index}",
"A=D+A",
"D=M",
] + self.push_d()
if segment == "temp":
return [f"@{5 + int(index)}", "D=M"] + self.push_d()
if segment == "pointer":
return [f"@{'THIS' if index == '0' else 'THAT'}", "D=M"] + self.push_d()
if segment == "static":
return [f"@{file_name}.{index}", "D=M"] + self.push_d()
raise ValueError(segment)
def write_pop(self, segment, index, file_name):
if segment in SEGMENT_BASE:
return [
f"@{SEGMENT_BASE[segment]}",
"D=M",
f"@{index}",
"D=D+A",
"@R13",
"M=D",
] + self.pop_to_d() + [
"@R13",
"A=M",
"M=D",
]
if segment == "temp":
return self.pop_to_d() + [f"@{5 + int(index)}", "M=D"]
if segment == "pointer":
return self.pop_to_d() + [f"@{'THIS' if index == '0' else 'THAT'}", "M=D"]
if segment == "static":
return self.pop_to_d() + [f"@{file_name}.{index}", "M=D"]
raise ValueError(segment)