#include "lc4_loader.h"
#include "lc4_memory.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}