/*************************************************************************** * file name : asm_parser.c * * author : * * description : the functions are declared in asm_parser.h * * The intention of this library is to parse a .ASM file * * * * * *************************************************************************** * */ #include #include #include #include "asm_parser.h" #include int read_asm_file(char *filename, char program[ROWS][COLS]) { FILE *file = fopen(filename, "r"); if (!file) { printf("error: read_asm_file failed\n"); return 2; } int line_num = 0; while (fgets(program[line_num], COLS, file) != NULL) { // Remove trailing newline size_t len = strlen(program[line_num]); if (len > 0 && program[line_num][len - 1] == '\n') { program[line_num][len - 1] = '\0'; len--; } trim(program[line_num]); if (strlen(program[line_num]) == 0) { continue; } // Remove comments starting with ';' or '#' char *comment = strchr(program[line_num], ';'); if (comment != NULL) { *comment = '\0'; len = comment - program[line_num]; } // /* Here, we remove this code, because it may cause immediate value cannot be detected */ // comment = strchr(program[line_num], '#'); // if (comment != NULL) { // *comment = '\0'; // len = comment - program[line_num]; // } while (len > 0 && isspace(program[line_num][len - 1])) { program[line_num][--len] = '\0'; } // Skip empty lines if (len == 0) { continue; } line_num++; if (line_num >= ROWS) { printf("error: read_asm_file failed - file too large\n"); fclose(file); return 2; } } if (!feof(file)) { printf("error: read_asm_file failed - could not read the file completely\n"); fclose(file); return 2; } fclose(file); return 0; } int parse_instruction(char *instr, char *instr_bin_str) { // Remove leading whitespace while (isspace(*instr)) instr++; if (*instr == '\0') { // Empty line return 0; } trim(instr); // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces while (isspace(*p)) p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; if (len > 0) { tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } // Skip commas int comma_count = 0; while (*p != '\0' && (isspace(*p) || *p == ',')) { if (*p == ',') { comma_count++; } p++; } if (comma_count > 1) { printf("error: parse_instruction failed - too many commas\n"); // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 3; } } if (token_count == 0) { printf("error: parse_instruction failed\n"); return 3; } // Convert opcode to uppercase to_uppercase(tokens[0]); // Now tokens[0] is opcode, tokens[1..] are operands if (strcmp(tokens[0], "ADD") == 0) { int ret = parse_add(instr, instr_bin_str); // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "MUL") == 0) { int ret = parse_mul(instr, instr_bin_str); // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } // Handle other opcodes similarly else if (strcmp(tokens[0], "SUB") == 0) { int ret = parse_sub(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "DIV") == 0) { int ret = parse_div(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "AND") == 0) { int ret = parse_and(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "OR") == 0) { int ret = parse_or(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "XOR") == 0) { int ret = parse_xor(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "NOP") == 0) { int ret = parse_nop(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "NOT") == 0) { int ret = parse_not(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "CONST") == 0) { int ret = parse_const(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "SLL") == 0) { int ret = parse_sll(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "SRA") == 0) { int ret = parse_sra(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "SRL") == 0) { int ret = parse_srl(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "MOD") == 0) { int ret = parse_mod(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "JMPR") == 0) { int ret = parse_jmpr(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "TRAP") == 0) { int ret = parse_trap(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "RTI") == 0) { int ret = parse_rti(instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else if (strcmp(tokens[0], "RET") == 0) { // RET is a pseudo-instruction equivalent to JMPR R7 char expanded_instr[COLS]; strcpy(expanded_instr, "JMPR R7"); int ret = parse_jmpr(expanded_instr, instr_bin_str); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return ret; } else { printf("error: parse_instruction failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 3; } } void trim(char *str) { // Remove leading spaces char *start = str; while (isspace(*start)) start++; memmove(str, start, strlen(start) + 1); // Remove trailing spaces char *end = str + strlen(str) - 1; while (end > str && isspace(*end)) end--; *(end + 1) = '\0'; } int parse_reg(char reg_num, char *instr_bin_str) { if (reg_num < '0' || reg_num > '7') { printf("error: parse_reg failed\n"); return 5; } int reg = reg_num - '0'; char bin_str[4]; int_to_bin_str(reg, 3, bin_str); strcat(instr_bin_str, bin_str); return 0; } void int_to_bin_str(int num, int bits, char *bin_str) { bin_str[bits] = '\0'; for (int i = bits - 1; i >= 0; i--) { bin_str[i] = ((num >> (bits - 1 - i)) & 1) + '0'; } } void to_uppercase(char *str) { for (; *str; ++str) { *str = toupper(*str); } } int parse_add(char *instr, char *instr_bin_str) { // Opcode: 0001 strcpy(instr_bin_str, "0001"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_add() failed, token_count = %d\n", token_count); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_add() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_add() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_add() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_add() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rt or Immediate if (tokens[3][0] == 'R') { // Register strcat(instr_bin_str, "000"); // Bit[5] = 0 char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_add() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } } else { // Immediate int imm_value; ret = parse_imm5(tokens[3], &imm_value); if (ret != 0) { printf("error: parse_add() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } strcat(instr_bin_str, "1"); // Bit[5] = 1 // Append imm5 bits char imm_bits[6]; int_to_bin_str(imm_value & 0x1F, 5, imm_bits); strcat(instr_bin_str, imm_bits); } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_mul(char *instr, char *instr_bin_str) { // Similar to parse_add, but sub-opcode is '001' // Opcode: 0001 strcpy(instr_bin_str, "0001"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode for MUL strcat(instr_bin_str, "001"); // Parse Rt if (tokens[3][0] != 'R') { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_mul() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_imm5(char *imm_str, int *imm_value) { int value = 0; if (imm_str[0] == '#') { // Decimal immediate value = atoi(&imm_str[1]); } else if (imm_str[0] == 'x') { // Hex immediate sscanf(&imm_str[1], "%x", &value); } else if (imm_str[0] == '0' && imm_str[1] == 'x') { // Hex immediate sscanf(&imm_str[2], "%x", &value); } else { printf("error: invalid immediate value\n"); return 4; } // Check if value fits in signed 5-bit if (value < -16 || value > 15) { printf("error: immediate value out of range\n"); return 4; } *imm_value = value; return 0; } // Function to parse SUB instruction int parse_sub(char *instr, char *instr_bin_str) { // Opcode: 0001 strcpy(instr_bin_str, "0001"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode for SUB strcat(instr_bin_str, "010"); // Parse Rt if (tokens[3][0] != 'R') { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_sub() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } // Function to parse DIV instruction int parse_div(char *instr, char *instr_bin_str) { // Opcode: 0001 strcpy(instr_bin_str, "0001"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode for DIV strcat(instr_bin_str, "011"); // Parse Rt if (tokens[3][0] != 'R') { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_div() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } // Function to parse AND instruction int parse_and(char *instr, char *instr_bin_str) { // Opcode: 0101 strcpy(instr_bin_str, "0101"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rt or Immediate if (tokens[3][0] == 'R') { // Register strcat(instr_bin_str, "000"); // Bit[5] = 0 char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } } else { // Immediate int imm_value; ret = parse_imm5(tokens[3], &imm_value); if (ret != 0) { printf("error: parse_and() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } strcat(instr_bin_str, "1"); // Bit[5] = 1 // Append imm5 bits char imm_bits[6]; int_to_bin_str(imm_value & 0x1F, 5, imm_bits); strcat(instr_bin_str, imm_bits); } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } // Function to parse OR instruction int parse_or(char *instr, char *instr_bin_str) { // Opcode: 0101 strcpy(instr_bin_str, "0101"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode for OR strcat(instr_bin_str, "010"); // Parse Rt if (tokens[3][0] != 'R') { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_or() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } // Function to parse XOR instruction int parse_xor(char *instr, char *instr_bin_str) { // Opcode: 0101 strcpy(instr_bin_str, "0101"); // Opcode // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode for XOR strcat(instr_bin_str, "011"); // Parse Rt if (tokens[3][0] != 'R') { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_xor() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } void write_uint16_big_endian(FILE *file, uint16_t value) { uint8_t bytes[2]; bytes[0] = (value >> 8) & 0xFF; // High byte bytes[1] = value & 0xFF; // Low byte fwrite(bytes, sizeof(uint8_t), 2, file); } unsigned short int str_to_bin(char *instr_bin_str) { if (strlen(instr_bin_str) != 16) { printf("error: str_to_bin failed, invalid length: %s\n", instr_bin_str); return 6; } unsigned short int result = 0; for (int i = 0; i < 16; i++) { result <<= 1; if (instr_bin_str[i] == '1') { result |= 1; } else if (instr_bin_str[i] != '0') { // Invalid character printf("error: str_to_bin failed\n"); return 6; } } return result; } int write_obj_file(char *filename, unsigned short int program_bin[ROWS], int instr_count) { FILE *file = fopen(filename, "wb"); if (!file) { printf("error: write_obj_file failed\n"); return 7; } // Write the code header: xCADE, address (start at 0), n (instr_count) unsigned short int header[3]; header[0] = 0xCADE; header[1] = 0x0000; // Starting address, assume 0 for now header[2] = instr_count; // Write header in big-endian order write_uint16_big_endian(file, header[0]); write_uint16_big_endian(file, header[1]); write_uint16_big_endian(file, header[2]); // Write the instructions in big-endian order for (int i = 0; i < instr_count; i++) { write_uint16_big_endian(file, program_bin[i]); } fclose(file); return 0; } int parse_nop(char *instr, char *instr_bin_str) { // NOP: Opcode 0000000--------- strcpy(instr_bin_str, "0000000"); // strcat(instr_bin_str, "000000000"); // use instr to prevent warning instr[0] = instr[0]; return 0; } int parse_not(char *instr, char *instr_bin_str) { // Opcode: 0101 strcpy(instr_bin_str, "0101"); // Opcode char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 3) { printf("error: parse_not() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } to_uppercase(tokens[1]); to_uppercase(tokens[2]); if (tokens[1][0] != 'R') { printf("error: parse_not() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); if (ret != 0) { printf("error: parse_not() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_not() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_not() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } strcat(instr_bin_str, "001"); strcat(instr_bin_str, "000"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_const(char *instr, char *instr_bin_str) { // Opcode: 1001 strcpy(instr_bin_str, "1001"); char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { while (isspace(*p) || *p == ',') p++; if (*p == '\0') break; char *start = p; while (*p != '\0' && !isspace(*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 3) { printf("error: parse_const() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } to_uppercase(tokens[1]); to_uppercase(tokens[2]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_const() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_const() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse IMM9 int imm_value; ret = parse_imm9(tokens[2], &imm_value); if (ret != 0) { printf("error: parse_const() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char imm_bits[10]; int_to_bin_str(imm_value & 0x1FF, 9, imm_bits); strcat(instr_bin_str, imm_bits); // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_imm9(char *imm_str, int *imm_value) { int value = 0; if (imm_str[0] == '#') { value = atoi(&imm_str[1]); } else if (imm_str[0] == 'x') { sscanf(&imm_str[1], "%x", &value); } else if (imm_str[0] == '0' && imm_str[1] == 'x') { sscanf(&imm_str[2], "%x", &value); } else { printf("error: invalid immediate value\n"); return 4; } if (value < -256 || value > 255) { printf("error: immediate value out of range\n"); return 4; } *imm_value = value; return 0; } int parse_sll(char *instr, char *instr_bin_str) { // Opcode: 1010 strcpy(instr_bin_str, "1010"); // Opcode bits [15:12] // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { // Skip spaces and commas while (isspace((unsigned char)*p) || *p == ',') p++; if (*p == '\0') break; // Collect token char *start = p; while (*p != '\0' && !isspace((unsigned char)*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_sll() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Enforce uppercase for operands to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); // Parse Rd if (tokens[1][0] != 'R') { printf("error: parse_sll() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_sll() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Parse Rs if (tokens[2][0] != 'R') { printf("error: parse_sll() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_sll() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode bits[5:4] = "00" strcat(instr_bin_str, "00"); // Parse UIMM4 int uimm_value; ret = parse_uimm4(tokens[3], &uimm_value); if (ret != 0) { printf("error: parse_sll() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Append UIMM4 bits char uimm_bits[5]; int_to_bin_str(uimm_value & 0xF, 4, uimm_bits); strcat(instr_bin_str, uimm_bits); // Free allocated memory for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_sra(char *instr, char *instr_bin_str) { // Opcode: 1010 strcpy(instr_bin_str, "1010"); // Opcode bits [15:12] // Tokenize and parse similarly to parse_sll, but with sub-opcode "01" // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { while (isspace((unsigned char)*p) || *p == ',') p++; if (*p == '\0') break; char *start = p; while (*p != '\0' && !isspace((unsigned char)*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_sra() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); if (tokens[1][0] != 'R') { printf("error: parse_sra() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_sra() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } if (tokens[2][0] != 'R') { printf("error: parse_sra() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_sra() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode bits[5:4] = "01" strcat(instr_bin_str, "01"); int uimm_value; ret = parse_uimm4(tokens[3], &uimm_value); if (ret != 0) { printf("error: parse_sra() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char uimm_bits[5]; int_to_bin_str(uimm_value & 0xF, 4, uimm_bits); strcat(instr_bin_str, uimm_bits); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_srl(char *instr, char *instr_bin_str) { // Opcode: 1010 strcpy(instr_bin_str, "1010"); // Opcode bits [15:12] // Tokenize and parse similarly to parse_sll, but with sub-opcode "10" char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { while (isspace((unsigned char)*p) || *p == ',') p++; if (*p == '\0') break; char *start = p; while (*p != '\0' && !isspace((unsigned char)*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_srl() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); if (tokens[1][0] != 'R') { printf("error: parse_srl() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_srl() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } if (tokens[2][0] != 'R') { printf("error: parse_srl() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_srl() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode bits[5:4] = "10" strcat(instr_bin_str, "10"); int uimm_value; ret = parse_uimm4(tokens[3], &uimm_value); if (ret != 0) { printf("error: parse_srl() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char uimm_bits[5]; int_to_bin_str(uimm_value & 0xF, 4, uimm_bits); strcat(instr_bin_str, uimm_bits); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_uimm4(char *imm_str, int *imm_value) { int value = 0; if (imm_str[0] == '#') { // Decimal immediate value = atoi(&imm_str[1]); } else if (imm_str[0] == 'x') { // Hex immediate sscanf(&imm_str[1], "%x", &value); } else if (imm_str[0] == '0' && imm_str[1] == 'x') { // Hex immediate sscanf(&imm_str[2], "%x", &value); } else { printf("error: invalid immediate value\n"); return 4; } // Check if value fits in unsigned 4-bit if (value < 0 || value > 15) { printf("error: immediate value out of range\n"); return 4; } *imm_value = value; return 0; } int parse_mod(char *instr, char *instr_bin_str) { // Opcode: 1010 strcpy(instr_bin_str, "1010"); // Opcode bits [15:12] // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { while (isspace((unsigned char)*p) || *p == ',') p++; if (*p == '\0') break; char *start = p; while (*p != '\0' && !isspace((unsigned char)*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 4) { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } to_uppercase(tokens[1]); to_uppercase(tokens[2]); to_uppercase(tokens[3]); if (tokens[1][0] != 'R') { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rd_num = tokens[1][1]; int ret = parse_reg(rd_num, instr_bin_str); // Rd if (ret != 0) { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } if (tokens[2][0] != 'R') { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[2][1]; ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } // Sub-opcode bits[5:4] = "11", bit[3]: don't care, set to '0' strcat(instr_bin_str, "110"); if (tokens[3][0] != 'R') { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rt_num = tokens[3][1]; ret = parse_reg(rt_num, instr_bin_str); // Rt if (ret != 0) { printf("error: parse_mod() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_jmpr(char *instr, char *instr_bin_str) { // Opcode: 11000 strcpy(instr_bin_str, "11000"); // Opcode bits [15:11] // bits[10:9]: don't cares, set to "00" strcat(instr_bin_str, "00"); // Tokenize the instruction line char instr_copy[COLS]; strcpy(instr_copy, instr); char *tokens[10]; int token_count = 0; char *p = instr_copy; while (*p != '\0') { while (isspace((unsigned char)*p) || *p == ',') p++; if (*p == '\0') break; char *start = p; while (*p != '\0' && !isspace((unsigned char)*p) && *p != ',') p++; size_t len = p - start; tokens[token_count] = (char *)malloc(len + 1); strncpy(tokens[token_count], start, len); tokens[token_count][len] = '\0'; token_count++; } if (token_count != 2) { printf("error: parse_jmpr() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } to_uppercase(tokens[1]); if (tokens[1][0] != 'R') { printf("error: parse_jmpr() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } char rs_num = tokens[1][1]; int ret = parse_reg(rs_num, instr_bin_str); // Rs if (ret != 0) { printf("error: parse_jmpr() failed\n"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 4; } strcat(instr_bin_str, "000000"); for (int i = 0; i < token_count; i++) { free(tokens[i]); } return 0; } int parse_trap(char *instr, char *instr_bin_str) { // Opcode: 1111 strcpy(instr_bin_str, "1111"); // Opcode // Bits[11:8] are don't cares, set to "0000" strcat(instr_bin_str, "0000"); // Parse UIMM8 int uimm_value; int ret = parse_uimm8("#0x80", &uimm_value); if (ret != 0) { printf("error: parse_trap() failed\n"); return 4; } // Append UIMM8 bits char uimm_bits[9]; int_to_bin_str(uimm_value & 0xFF, 8, uimm_bits); strcat(instr_bin_str, uimm_bits); // use instr to avoid unused parameter warning instr[0] = instr[0]; return 0; } int parse_uimm8(char *imm_str, int *imm_value) { int value = 0; if (imm_str[0] == '#') { // Decimal immediate value = atoi(&imm_str[1]); } else if (imm_str[0] == 'x') { // Hex immediate sscanf(&imm_str[1], "%x", &value); } else if (imm_str[0] == '0' && imm_str[1] == 'x') { // Hex immediate sscanf(&imm_str[2], "%x", &value); } else { printf("error: invalid immediate value\n"); return 4; } // Check if value fits in unsigned 8-bit if (value < 0 || value > 255) { printf("error: immediate value out of range\n"); return 4; } *imm_value = value; return 0; } int parse_rti(char * instr, char *instr_bin_str) { strcpy(instr_bin_str, "1000"); // Opcode strcat(instr_bin_str, "000000000000"); // use instr to avoid unused parameter warning instr[0] = instr[0]; return 0; }