all repos — nand2tetris @ bba9e22274747685dcb9bfcd9170800278efead4

my nand2tetris progress

Support CRLF newlines, some misc. refactors
x1phosura x1phosura@x1phosura.zone
Sun, 25 Dec 2022 22:59:53 -0800
commit

bba9e22274747685dcb9bfcd9170800278efead4

parent

45c796329ee858875dffb68a81155d43a8556c18

3 files changed, 63 insertions(+), 68 deletions(-)

jump to
M projects/06/Makefileprojects/06/Makefile

@@ -2,7 +2,6 @@

CFLAGS = -std=c99 -Wall -Wextra all: bin/assembler1 -#all: bin/assembler1 bin/bin2text # TODO: clean up, make more Makefile-idiomatic
M projects/06/assembler1/assembler1.cprojects/06/assembler1/assembler1.c

@@ -4,21 +4,37 @@ #include <stdio.h>

#include <stdlib.h> #include <string.h> -#include "../bindump.h" - -//#define DEBUG(...) printf(__VA_ARGS__) -#define DEBUG(...) -#define die(err_msg) perror(err_msg); exit(-1) +//#define DBGLOG(...) printf(__VA_ARGS__) +#define DBGLOG(...) #define error(...) fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "%lu | %s\n", \ g_asm_line_number, g_asm_line); #define MAX_LINE_LEN 256 -char *g_asm_line; // copy of currently-read line for easy reference -size_t g_asm_line_number; // current line number +char *g_asm_line; // currently-read line for convenience +size_t g_asm_line_number; // current line number +size_t g_instruction_number = 0; // instruction offset +void print_binary_word16(uint16_t w) +{ + unsigned char i, msb; + char binary_string[17]; + binary_string[16] = '\0'; + + for (i = 0; i < 16; ++i) { + msb = (char)(w >> 15); + + if (msb != 0) + binary_string[i] = '1'; + else + binary_string[i] = '0'; + w <<= 1; + } + printf("%s", binary_string); +} + static uint32_t myatoi(const char *a_field_str) { size_t i;

@@ -33,9 +49,11 @@ }

static bool parse_a_type(const char *line, uint16_t *instruction) { - char c, a_field_str[6]; // TODO: eventually factor out use of array + char c, a_field_str[6]; uint32_t a_field = 0; size_t i, a = 0; + + DBGLOG("line: %s\n", comp_line); if (line[0] != '@') { error("syntax error: A-type instruction doesn't start with @\n");

@@ -64,25 +82,25 @@ return false;

} } - a_field_str[a] = '\0'; // exit + a_field_str[a] = '\0'; - // TODO: extension: support negative numbers - a_field = myatoi(a_field_str); + a_field = myatoi(a_field_str); // TODO: maybe negative number support? if (a_field > 32767) { error("error: %u > 32767, too large\n", a_field); return false; } *instruction = 0x0000 | (uint16_t) a_field; - return true; // STUB, A-type MSB == 0 anyway + return true; } /* returns dest bits 0b00000ddd + * Note: order doesn't matter for multi-register dest (but officially it should) */ static bool parse_c_type_dest(const char *dest_line, uint8_t *dest) { size_t len; - DEBUG("dest_line: %s\n", dest_line); + DBGLOG("dest_line: %s\n", dest_line); for (len = 0; dest_line[len] != '='; ++len) { // read until '=' if (dest_line[len] == 'A') {

@@ -124,12 +142,11 @@ return true;

} /* returns comp bits 0b0acccccc - * TODO: refactor (especially the error cases, which are too repetitive) */ static bool parse_c_type_comp(const char *comp_line, uint8_t *comp) { size_t len; - DEBUG("comp_line: %s\n", comp_line); + DBGLOG("comp_line: %s\n", comp_line); for (len = 0; comp_line[len] == '0' || comp_line[len] == '1' || comp_line[len] == '-' || comp_line[len] == 'D' ||

@@ -240,9 +257,9 @@ */

static bool parse_c_type_jump(const char *jump_line, uint8_t *jump) { size_t len; - char *err_1st_char = "syntax error: jump field doesn't start with 'J'\n"; - char *err_2nd_char = "syntax error: 2nd letter in jump field incorrect\n"; char *err_3rd_char = "syntax error: 3rd letter in jump field incorrect\n"; + + DBGLOG("jump_line: %s\n", dest_line); for (len = 0; jump_line[len] == 'J' || jump_line[len] == 'G' || jump_line[len] == 'T' || jump_line[len] == 'E' ||

@@ -296,11 +313,11 @@ error(err_3rd_char); return false;

} break; default: - error(err_2nd_char); + error("syntax error: 2nd letter in jump field incorrect\n"); return false; } } else { - error(err_1st_char); + error("syntax error: jump field doesn't start with 'J'\n"); return false; }

@@ -313,7 +330,7 @@ * TODO: eventually just replace all wasteful c-instruction parsing w/ strcmp()

*/ static bool parse_c_type(const char *line, uint16_t *instruction) { - bool ret; + bool ret = false; char c; const char *dest_start = NULL; const char *comp_start = NULL;

@@ -384,7 +401,7 @@ // does not care about line line length; exits at first newline or after

// relevant portion parsed (allows for syntactically-incorrect lines, I know) static bool parse_next_instruction(const char *line, uint16_t *instruction) { - bool ret; + bool ret = false; char c; size_t i = 0;

@@ -393,9 +410,11 @@ if (c == ' ' || c == '\t')

; // skip any whitespace at start of line else if (c == '@') { ret = parse_a_type(&line[i], instruction); + ++g_instruction_number; break; } else if (c >= '!' && c < '~') { ret = parse_c_type(&line[i], instruction); + ++g_instruction_number; break; } else { error("syntax error: line '%s' incorrectly formatted\n",

@@ -409,36 +428,29 @@ return ret;

} // return false for comment or invalid assembly instruction -bool parse_line(const char *line, size_t line_len, uint16_t *instruction) +bool parse_line(const char *line, uint16_t *instruction) { char c; bool slash_found = false; size_t i; - if (line_len == 0 || line_len == 1) - return false; - // filter out comment lines - //for (i = 0; (c = line[i]) != NULL; ++i) { - for (i = 0; i < line_len; ++i) { - c = line[i]; - + for (i = 0; (c = line[i]) != '\0'; ++i) { if (c == ' ' || c == '\t') { continue; + } else if (i == 0 && c == '\0') { + return false; } else if (c == '/') { - if (slash_found) { - // second slash means this is a comment + if (slash_found) { // second slash means comment return false; } slash_found = true; continue; } else if (slash_found) { // this char not slash, but previous was: invalid syntax - // TODO: add line, column numbers error("syntax error: found '/', comments need '//'\n"); return false; - } else { - // non-whitespace/slash char discovered + } else { // non-whitespace/slash discovered break; } }

@@ -452,12 +464,10 @@ char *usage_msg = "Usage: assembler1 [path/to/file.asm]\n";

int main(int argc, char *argv[]) { - DEBUG("DEBUG 1 2 3...\n"); - bool result = false; uint16_t instruction; char in_line[MAX_LINE_LEN]; - size_t in_line_len, i, file_line; + size_t i, file_line, in_line_len; char *in_file_path; FILE *fp;

@@ -473,27 +483,34 @@ fprintf(stderr, "failed to open file for reading\n");

exit(-1); } - file_line = 1; + file_line = 0; while (fgets(in_line, MAX_LINE_LEN, fp) != NULL) { // parse loop - in_line_len = strlen(in_line); + ++file_line; - for (i = 0; i < in_line_len; ++i) { // remove newlines - if (in_line[i] == '\n') { // TODO handle DOS '\r\n' + for (i = 0; in_line[i] != '\0'; ++i) { // remove newlines + if (in_line[i] == '\n' || in_line[i] == '\r') { in_line[i] = '\0'; break; } + ++in_line_len; } - DEBUG("%lu|%s | ", file_line, in_line); + if (in_line_len == 0 || in_line_len == 1) + continue; + g_asm_line = in_line; g_asm_line_number = file_line; - result = parse_line(in_line, in_line_len, &instruction); + result = parse_line(in_line, &instruction); + if (result) { - DEBUG("instruction: 0x%x | ", instruction); - bindump_word16(instruction); // output instruction as binary + print_binary_word16(instruction); putchar('\n'); } - ++file_line; + } + + if (fclose(fp)) { + fprintf(stderr, "Failed to close file %s\n", in_file_path); + exit(-1); } return 0;
D projects/06/bindump.h

@@ -1,21 +0,0 @@

-#include <stdio.h> -#include <stdint.h> - -void bindump_word16(uint16_t w) -{ - unsigned char i, msb; - char binary_string[17]; - binary_string[16] = '\0'; - - for (i = 0; i < 16; ++i) { - msb = (char)(w >> 15); - - if (msb != 0) - binary_string[i] = '1'; - else - binary_string[i] = '0'; - w <<= 1; - } - printf("%s", binary_string); -} -