Finish/fix remaining VM instructions
x1phosura x1phosura@x1phosura.zone
Tue, 07 Nov 2023 18:46:17 -0800
3 files changed,
190 insertions(+),
64 deletions(-)
M
projects/07/src/codewriter.h
→
projects/07/src/codewriter.h
@@ -23,21 +23,14 @@ #define SP (0) // points to word ahead of top of stack
#define LCL (1) // points to local segment #define ARG (2) // points to argument segment #define POINTER (3) -#define THIS (3) // -#define THAT (4) // -#define TEMP (5) // +#define THIS (3) +#define THAT (4) +#define TEMP (5) #define R13 (13) // R13-R15 are scratch space that -#define R14 (14) // VM-generated assembly can use -#define R15 (15) // for whatever. +#define R14 (14) // VM-generated assembly can use +#define R15 (15) // for whatever. #define STATIC (16) // start of static variables segment (240 words, 16-255) -char vm_init[] = "@256\n" // starting address of stack (nothing pushed yet) - "D=A\n" // D = 256 - "@SP\n" // A = <constant representing address of SP> - "M=D\n" // <memory pointed to by SP> = 256 - "\n"; -// TODO: add initializers for argument, local, static, constant, this, that - char *static_sym_name; // static segment index indexes into map, retrieves hack assembly symbol offset@@ -46,20 +39,158 @@ uint16_t static_symbol_map[MAX_STATIC_SYMBOLS];
uint8_t g_symbol_offset_bump = 0; // holds current largest symbol offset, bumps +char comp_vm_funcs[] = "@__comp_funcs_end\n" + "0;JMP\n" + "\n" + "(__test_eq)\n" + "@SP\n" + "AM=M-1\n" // RAM[SP]--, A = RAM[SP] + "D=M\n" // D = RAM[SP] (top stack val) + "A=A-1\n" // --A (--> bottom stack val) + "D=M-D\n" // if D == 0, equal + "M=0\n" // prematurely push false + "@__test_eq_neq\n" // if D == 0, equal + "D;JNE\n" // if D != 0, not equal, jump + "@SP\n" // get SP + "A=M-1\n" // get bottom stack val address + "M=-1\n" // push 0xffff (true) + "(__test_eq_neq)\n" + "@R13\n" // return address in RAM[R13] + "A=M\n" // A = return address + "0;JMP\n" // return + "\n" + + "(__test_gt)\n" + "@SP\n" + "AM=M-1\n" // RAM[SP]--, A = RAM[SP] + "D=M\n" // D = RAM[SP] (top stack val) + "A=A-1\n" // --A (--> bottom stack val) + "D=M-D\n" // if (D - M) > 0; push true + "M=0\n" // prematurely push false + "@__test_gt_neq\n" // if D == 0, equal + "D;JLE\n" // if D != 0, not equal, jump + "@SP\n" // get SP + "A=M-1\n" // get bottom stack val address + "M=-1\n" // push 0xffff (true) + "(__test_gt_neq)\n" + "@R13\n" // return address in RAM[R13] + "A=M\n" // A = return address + "0;JMP\n" // return + "\n" + + "(__test_lt)\n" + "@SP\n" + "AM=M-1\n" // RAM[SP]--, A = RAM[SP] + "D=M\n" // D = RAM[SP] (top stack val) + "A=A-1\n" // --A (--> bottom stack val) + "D=M-D\n" // if (D - M) < 0; push true + "M=0\n" // prematurely push false + "@__test_lt_neq\n" // if D == 0, equal + "D;JGE\n" // if D != 0, not equal, jump + "@SP\n" // get SP + "A=M-1\n" // get bottom stack val address + "M=-1\n" // push 0xffff (true) + "(__test_lt_neq)\n" + "@R13\n" // return address in RAM[R13] + "A=M\n" // A = return address + "0;JMP\n" // return + "\n" + "(__comp_funcs_end)\n"; + +char vm_init[] = "//@256\n" // starting address of stack (nothing pushed yet) + "//D=A\n" // D = 256 + "//@SP\n" // A = <constant representing address of SP> + "//M=D\n" // <memory pointed to by SP> = 256 + "\n%s\n"; // <- comp_vm_funcs +// TODO: add initializers for argument, local, static, constant, this, that +char vm_stop[] = "(END)\n" // starting address of stack (nothing pushed yet) + "@END\n" // D = 256 + "0;JMP\n"; // A = <constant representing address of SP> + + void write_vm_init(FILE *fp) { - //fprintf(fp, vm_init); - fprintf(fp, "// TODO eventually output VM initialization assembly\n"); + fprintf(fp, vm_init, comp_vm_funcs); +} + +void write_vm_stop(FILE *fp) +{ + fprintf(fp, "\n%s", vm_stop); } static bool write_arithmetic(struct vm_instruction_t *vm_instr, FILE *fp) { - // TODO: write assembly code for 'add' and 'neg' instructions, turn into - // codegen (depend on if unary/binary operation) - // Binary: load RAM[SP], D=M, RAM[SP]--, M=D<op>M (I _think_ this is good) - // Unary: load RAM[SP], M=<op>M (I _think_ this is good) - fprintf(fp, "ARITHMETIC INSTRUCTION: "); - print_vm_instruction(vm_instr); + char binary_op_template[] = "@SP\n" + "AM=M-1\n" // RAM[SP]--, A = RAM[SP] + "D=M\n" // D = RAM[SP] (top stack val) + "A=A-1\n" // --A (--> bottom stack val) + "%s"; // arithmetic op goes here + char unary_op_template[] = "@SP\n" + "A=M-1\n" // A = SP - 1 + "%s"; // arithmetic op goes here + + char op_add[] = "M=D+M\n"; + char op_sub[] = "M=M-D\n"; + + char op_eq[] = "@%s_%lu_eq\n" // <- static_sym_name, file_line_number + "D=A\n" + "@R13\n" + "M=D\n" + "@__test_eq\n" + "0;JMP\n" + "(%s_%lu_eq)\n" // return here + "\n"; + char op_gt[] = "@%s_%lu_gt\n" // <- static_sym_name, file_line_number + "D=A\n" + "@R13\n" + "M=D\n" + "@__test_gt\n" + "0;JMP\n" + "(%s_%lu_gt)\n" // return here + "\n"; + char op_lt[] = "@%s_%lu_lt\n" // <- static_sym_name, file_line_number + "D=A\n" + "@R13\n" + "M=D\n" + "@__test_lt\n" + "0;JMP\n" + "(%s_%lu_lt)\n" // return here + "\n"; + + char op_and[] = "M=D&M\n"; + char op_or[] = "M=D|M\n"; + + char op_neg[] = "M=-M\n"; + char op_not[] = "M=!M\n"; + + // binary operations + if (!strncmp(vm_instr->arg1, "add", CMD_STR_MAX_LEN)) { + fprintf(fp, binary_op_template, op_add); + } else if (!strncmp(vm_instr->arg1, "sub", CMD_STR_MAX_LEN)) { + fprintf(fp, binary_op_template, op_sub); + } else if (!strncmp(vm_instr->arg1, "eq", CMD_STR_MAX_LEN)) { + fprintf(fp, op_eq, static_sym_name, file_line_no, + static_sym_name, file_line_no); + } else if (!strncmp(vm_instr->arg1, "gt", CMD_STR_MAX_LEN)) { + fprintf(fp, op_gt, static_sym_name, file_line_no, + static_sym_name, file_line_no); + } else if (!strncmp(vm_instr->arg1, "lt", CMD_STR_MAX_LEN)) { + fprintf(fp, op_lt, static_sym_name, file_line_no, + static_sym_name, file_line_no); + } else if (!strncmp(vm_instr->arg1, "and", CMD_STR_MAX_LEN)) { + fprintf(fp, binary_op_template, op_and); + } else if (!strncmp(vm_instr->arg1, "or", CMD_STR_MAX_LEN)) { + fprintf(fp, binary_op_template, op_or); + // unary operations + } else if (!strncmp(vm_instr->arg1, "neg", CMD_STR_MAX_LEN)) { + fprintf(fp, unary_op_template, op_neg); + } else if (!strncmp(vm_instr->arg1, "not", CMD_STR_MAX_LEN)) { + fprintf(fp, unary_op_template, op_not); + } else { + err("error: invalid arithmetic op \"%s\"\n", vm_instr->arg1); + return false; + } + return true; }@@ -83,32 +214,19 @@ static_symbol_map[vm_instr->arg2] = g_symbol_offset_bump;
*addr = g_symbol_offset_bump; ++g_symbol_offset_bump; // bump global symbol offset } else { - // offset was found in map, return symbol value/index + // offset found in map, return symbol value/index *addr = symbol_offset; } return true; } -// returns addr -static bool resolve_segment_offset(struct vm_instruction_t *vm_instr, - uint16_t *addr) -{ - // TODO implement - // resolve base (address) of segment - // check index is valid for that segment - // add index vm_instr->arg2 to address - // set to *addr - return true; -} - // push 16-bit value from segment offset onto top of stack static bool write_push(struct vm_instruction_t *vm_instr, FILE *fp) { uint16_t addr, arg2 = vm_instr->arg2; - // TODO: maybe add SP counter/check to catch overflows - // TODO: if vm_instr->arg1 == "constant", push_constant, else push_segment + // TODO: could add SP counter/check to catch overflows char const_template[] = "@%hu\n" // A = constant "D=A\n%s"; // D = constant char addr_template[] = "@%hu\n" // A = segment + index@@ -116,7 +234,7 @@ "D=M\n%s"; // D = RAM[segment + index]
char static_template[] = "@%s.%hu\n" // A = segment + index "D=M\n%s"; // D = RAM[segment + index] char indirect_template[] = "@%hu\n" // A = segment - "D=A\n" // D = segment + "D=M\n" // D = RAM[segment] "@%hu\n" // A = index "A=A+D\n" // A = segment + index "D=M\n%s"; // D = RAM[segment + index]@@ -125,10 +243,9 @@ "M=M+1\n" // RAM[SP]++ // inc SP
"A=M-1\n" // A = RAM[SP] - 1 // prev top "M=D\n"; // RAM[SP] = constant - // TODO: move segment resolution to separate function if (!strcmp(vm_instr->arg1, "constant")) { // TODO: check size of constant (allowed to be > 32,767?) - // TODO: look up in nand2tetris forums in case issue already noted + // TODO: look in nand2tetris forums in case issue already noted fprintf(fp, const_template, arg2, push_boilerplate); } else if (!strcmp(vm_instr->arg1, "argument")) { fprintf(fp, indirect_template, ARG, arg2, push_boilerplate);@@ -162,43 +279,50 @@
// pop 16-bit value from top of stack into segment offset static bool write_pop(struct vm_instruction_t *vm_instr, FILE *fp) { - // TODO: maybe add SP counter/check to catch overflows - // can use R13-R15 for scratch space + // TODO: could add SP counter/check to catch overflows uint16_t addr, arg2 = vm_instr->arg2; - char addr_template[] = "@%hu\n" // A = segment + index - "D=M\n%s"; // D = RAM[segment + index] - char static_template[] = "@%s.%hu\n" // A = segment + index - "D=M\n%s"; // D = RAM[segment + index] - char indirect_template[] = "@%hu\n" // @segment - "D=A\n" // D = segment - "@%hu\n" // @index - "A=A+D\n" // A = segment + index - "D=M\n%s"; // D = RAM[segment + index] - char pop_boilerplate[] = "@SP\n" - "AM=M-1\n" // RAM[SP]--, A = RAM[SP] - "M=D\n"; // RAM[SP] = D + char pop_indirect_template[] = "@%hu\n" // @segment + "D=M\n" // D = segment + "@%hu\n" // @index + "D=A+D\n" // A = segment + index + "@R13\n" + "M=D\n" // RAM[13] = segment + index + "@SP\n" // + "AM=M-1\n" // + "D=M\n" // + "@R13\n" // + "A=M\n" // + "M=D\n"; // + char pop_addr_template[] = "@SP\n" + "AM=M-1\n" // decrement SP + "D=M\n" // "pop" (read) value into D + "@%hu\n" // load address + "M=D\n"; // "pop" (write) value to RAM + char pop_static_template[] = "@SP\n" + "AM=M-1\n" // decrement SP + "D=M\n" // "pop" (read) value into D + "@%s.%hu\n" // A = segment + index + "M=D\n"; // RAM[segment + index] = D - // TODO: move segment resolution to separate function if (!strcmp(vm_instr->arg1, "argument")) { - fprintf(fp, indirect_template, ARG, arg2, pop_boilerplate); + fprintf(fp, pop_indirect_template, ARG, arg2); } else if (!strcmp(vm_instr->arg1, "local")) { - fprintf(fp, indirect_template, LCL, arg2, pop_boilerplate); + fprintf(fp, pop_indirect_template, LCL, arg2); } else if (!strcmp(vm_instr->arg1, "static")) { if (!resolve_static_address(vm_instr, &addr)) { return false; } - fprintf(fp, static_template, static_sym_name, addr, - pop_boilerplate); + fprintf(fp, pop_static_template, static_sym_name, addr); } else if (!strcmp(vm_instr->arg1, "this")) { - fprintf(fp, indirect_template, THIS, arg2, pop_boilerplate); + fprintf(fp, pop_indirect_template, THIS, arg2); } else if (!strcmp(vm_instr->arg1, "that")) { - fprintf(fp, indirect_template, THAT, arg2, pop_boilerplate); + fprintf(fp, pop_indirect_template, THAT, arg2); } else if (!strcmp(vm_instr->arg1, "pointer")) { addr = POINTER + vm_instr->arg2; - fprintf(fp, addr_template, addr, pop_boilerplate); + fprintf(fp, pop_addr_template, addr); } else if (!strcmp(vm_instr->arg1, "temp")) { addr = TEMP + vm_instr->arg2; - fprintf(fp, addr_template, addr, pop_boilerplate); + fprintf(fp, pop_addr_template, addr); } else { err("error: invalid segment name \"%s\"\n", vm_instr->arg1);@@ -218,8 +342,8 @@ write_push(vm_instr, fp);
} else if (vm_instr->cmd == C_POP) { write_pop(vm_instr, fp); } else { - // TODO: eventually error if unrecognized instruction - print_vm_instruction(vm_instr); + err("error: unrecognized instruction (%u)\n", vm_instr->cmd); + return false; } return true; }
M
projects/07/src/util.h
→
projects/07/src/util.h
@@ -22,7 +22,7 @@ extern char *file_line; // reference to currently-read line (for convenience)
extern size_t file_line_no; // line number, regardless of line content #define err(...) (fprintf(stdout, __VA_ARGS__), \ - fprintf(stdout, "%lu | %s\n", file_line_no, file_line), false) + fprintf(stdout, "%lu | %s\n", file_line_no, file_line)) #define die(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) #define is_symbol_char(c) (('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') \ || ('a' <= c && c <= 'z') || c == '_' || c == '.' \
M
projects/07/src/vmtranslator.c
→
projects/07/src/vmtranslator.c
@@ -32,6 +32,7 @@ for (i = 0; i < base_name_len && base_name[i] != '.'; ++i) {
if (!is_symbol_char(base_name[i])) { err("found bad character '0x%hhx' in filename \"%s\"\n", base_name[i], filename); + free(tmp); return false; } static_sym_name[i] = base_name[i];@@ -86,6 +87,7 @@ }
} } + write_vm_stop(out_file); return true; }