#include "lc4_loader.h" #include "lc4_memory.h" #include #include #include FILE *open_file(char *file_name) { FILE *file = fopen(file_name, "rb"); if (!file) { fprintf(stderr, "Error: could not open file %s\n", file_name); return NULL; } return file; } int parse_file(FILE *my_obj_file, row_of_memory **memory) { unsigned short int directive; unsigned short int address; unsigned short int n; unsigned short int contents; char *label; row_of_memory *new_memory_row; // read through the file until no more data can be read while (fread(&directive, sizeof(unsigned short int), 1, my_obj_file) == 1) { // convert the endianess of the header convert_endian(&directive, 8); if (fread(&address, sizeof(unsigned short int), 1, my_obj_file) != 1 || fread(&n, sizeof(unsigned short int), 1, my_obj_file) != 1) { close_file(my_obj_file, memory); return -1; } convert_endian(&address, 8); convert_endian(&n, 8); // handle cade/dada headers if (directive == 0xcade || directive == 0xdada) { // read and convert memory contents unsigned short int i = 0; for (i = 0; i < n; i++) { if (fread(&contents, sizeof(unsigned short int), 1, my_obj_file) != 1) { close_file(my_obj_file, memory); return -1; } convert_endian(&contents, 8); add_to_list(memory, address++, contents); } } // handle c3b7 header else if (directive == 0xc3b7) { label = NULL; // allocate memory for the label (n bytes + null terminator) label = malloc((sizeof(char) * n) + 1); if (!label) { close_file(my_obj_file, memory); return -1; } // read the label char by char unsigned short int i = 0; for (i = 0; i < n; i++) { if (fread(&label[i], sizeof(char), 1, my_obj_file) != 1) { free(label); close_file(my_obj_file, memory); return -1; } } // null-terminate the label string label[n] = '\0'; // search for the address in the memory list new_memory_row = search_address(*memory, address); // if the address is not found, add it to the list // and assign the label to the address if (!new_memory_row) { int result = add_to_list(memory, address, 0); if (result != 0) { free(label); close_file(my_obj_file, memory); return -1; } new_memory_row = search_address(*memory, address); if (!new_memory_row) { free(label); close_file(my_obj_file, memory); return -1; } } // handle case where memory row might already have a label if (new_memory_row->label) { free(new_memory_row->label); } new_memory_row->label = label; } else { close_file(my_obj_file, memory); return -1; } } if (close_file(my_obj_file, memory) != 0) { return -1; } return 0; } void convert_endian(unsigned short int *value, const short int endian) { *value = ((*value << endian) | (*value >> endian)) & 0xFFFF; } int output_file(char *file_name, row_of_memory *memory) { if (!memory) { return -1; } char output_file_name[110]; strncpy(output_file_name, file_name, sizeof(output_file_name) - 1); replace_file_ext(output_file_name, ".obj", ".asm"); FILE *file = fopen(output_file_name, "w"); if (!file) { fprintf(stderr, "Error: could not open/create file %s\n", file_name); return -1; } row_of_memory *curr = memory; unsigned short int addr = -1; unsigned short int first_directive = 0; while (curr) { char fout[256] = ""; unsigned short int width = 0; if (curr->assembly) { width = strlen(curr->assembly) + 4; } // USER .CODE if (curr->address >= 0x0000 && curr->address <= 0x1FFF) { if (addr > 0x1fff || first_directive == 0) { sprintf(fout, "\n\n%9s\n%9s x%.04X", ".CODE", ".ADDR", curr->address); first_directive = 1; } else if (curr->address - addr != 1) { // address may not be continuous, set the new // starting address if there's a leap from the last ending address sprintf(fout, "\n\n%9s x%.04X\n", ".ADDR", curr->address); } char code_line[256]; if (curr->label && strlen(curr->label) > 0) { // Label exists, include both newlines sprintf(code_line, "\n%s\n%*s", curr->label ? curr->label : "", width, curr->assembly == 0 ? "NOP" : curr->assembly ? curr->assembly : ""); } else { sprintf(code_line, "\n%*s", width, curr->assembly == 0 ? "NOP" : curr->assembly ? curr->assembly : ""); } strcat(fout, code_line); } // USER .DATA else if (curr->address >= 0x2000 && curr->address <= 0x7FFF) { // so we don't pad .DATA everytime if (addr < 0x2000 || first_directive == 0) { sprintf(fout, "\n\n%9s\n%9s x%.04X", ".DATA", ".ADDR", curr->address); first_directive = 1; } else if (curr->address - addr != 1) { sprintf(fout, "\n\n%9s x%.04X\n", ".ADDR", curr->address); } // Append the data directive to fout char directive[256]; if (curr->label && strlen(curr->label) > 0) { // Label exists, include both newlines sprintf(directive, "\n%s\n%s x%.04X", curr->label ? curr->label : "", ".FILL", curr->contents); } else { sprintf(directive, "\n%s x%.04X", ".FILL", curr->contents); } strcat(fout, directive); } // .OS CODE else if (curr->address >= 0x8000 && curr->address <= 0x9FFF) { if (addr < 0x8000 || addr > 0x9FFF || first_directive == 0) { sprintf(fout, "\n\n%7s\n%9s\n%9s x%.04X", ".OS", ".CODE", ".ADDR", curr->address); first_directive = 1; } else if (curr->address - addr != 1) { sprintf(fout, "\n\n%9s x%.04X\n", ".ADDR", curr->address); } char os_line[256]; if (curr->label && strlen(curr->label) > 0) { sprintf(os_line, "\n\n%s\n%s\n%*s", ".CODE", curr->label ? curr->label : "", width, curr->assembly == 0 ? "NOP" : curr->assembly ? curr->assembly : ""); } else { sprintf(os_line, "\n%*s", width, curr->assembly == 0 ? "NOP" : curr->assembly ? curr->assembly : ""); } strcat(fout, os_line); } // .OS DATA + Device Memory else if (curr->address >= 0xA000 && curr->address <= 0xFFFF) { if (addr < 0xA000 || first_directive == 0) { sprintf(fout, "\n\n%9s\n%9s x%.04X", ".DATA", ".ADDR", curr->address); first_directive = 1; } else if (curr->address - addr != 1) { sprintf(fout, "\n\n%9s x%.04X\n", ".ADDR", curr->address); } char os_data_line[256]; // when reserving addresses using .BLKW, we decided to use NOP // as .BLKW doesn't do anything at those addresses the same way as NOP // doing so also catches both empty lines if (curr->label && strlen(curr->label) > 0) { sprintf(os_data_line, "\n%s\n%*s", curr->label ? curr->label : "", width, curr->assembly == 0 ? "NOP" : curr->assembly ? curr->assembly : ""); } else { sprintf(os_data_line, "\n%*s", width, curr->assembly == 0 ? "NOP" : curr->assembly ? curr->assembly : ""); } strcat(fout, os_data_line); } else { printf("Error: outputting invalid address 0x%04X\n", curr->address); return -1; } addr = curr->address; // Write only if we have content if (strlen(fout) > 0) { fwrite(fout, sizeof(char), strlen(fout), file); } // go to the next node curr = curr->next; } close_file(file, &memory); return 0; } void replace_file_ext(char *file_name, char *curr_ext, char *new_ext) { // replace .obj with .asm char *dot = strrchr(file_name, '.'); // check if the last '.' matches the current extension if (dot && strcmp(dot, curr_ext) == 0) { *dot = '\0'; // null-terminate where the current extension starts } // concatenate the new extension strcat(file_name, new_ext); } int close_file(FILE *file, row_of_memory **memory) { if (file && fclose(file) != 0) { printf("error: unable to close the input .obj file\n"); delete_list(memory); return -1; } return 0; }