struct process_parameter_results process_address(struct ast_node_address* param) { struct process_parameter_results result; struct register_mapping* registr; bstring btmp = NULL; result.v_raw = NULL; if (param->value != NULL) btmp = expr_representation(param->value); if (param->bracketed && param->added) { // This is of the form [0x1000+I]. registr = get_register_by_name_next(param->addcmpt); if (registr == NULL) { // Attempt to use a label in brackets that can't be. fprintf(stderr, "\n"); ahalt(ERR_NEXTED_LABEL_UNSUPPORTED, param->addcmpt); } else if (registr->value == VALUE_NEXT_UNSUPPORTED) { // Attempt to use a register in brackets that can't be. fprintf(stderr, "\n"); ahalt(ERR_NEXTED_REGISTER_UNSUPPORTED, param->addcmpt); } fprintf(stderr, "[%s+%s]", btmp->data, registr->name); result.v = registr->value; result.v_extra = param->value; result.v_extra_used = true; result.v_label = NULL; } else { // This is either the form 0x1000 or [0x1000]. if (param->bracketed) { fprintf(stderr, "[%s]", btmp->data); result.v = NXT; } else { fprintf(stderr, "%s", btmp->data); result.v = NXT_LIT; } result.v_extra = param->value; result.v_extra_used = true; result.v_label = NULL; } if (btmp != NULL) bdestroy(btmp); return result; }
void ahalt_expression_exit_handler(int code, void* data) { switch (code) { case EXPR_EXIT_LABEL_NOT_FOUND: ahalt(ERR_LABEL_NOT_FOUND, ((bstring)data)->data); case EXPR_EXIT_DIVIDE_BY_ZERO: ahalt(ERR_EXPRESSION_DIVIDE_BY_ZERO, ((bstring)data)->data); default: ahalt(ERR_GENERIC, NULL); } }
uint16_t aout_get_label_address(bstring name) { struct aout_byte* current = start; uint16_t mem_index = code_offset; bool found_import = false; while (current != NULL) { if (current->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust memory address. mem_index = current->opcode; } else if (current->type == AOUT_TYPE_NORMAL) { // Replace the label position. if (current->label == NULL) mem_index += 1; else if (strcmp(current->label, name->data) == 0) { // Return the address of this label. return mem_index; } } else if (current->type == AOUT_TYPE_METADATA_IMPORT) { // Check to see if the requested label is // actually imported. if (strcmp(current->label, name->data) == 0) { // Set a temporary variable in case we // define the label later (in which case we'll // use that instead of throwing an error). found_import = true; } } // Goto next. current = current->next; } // Error if we get to here. if (found_import) ahalt(ERR_EXPRESSION_NOT_PERMITTED, name->data); else ahalt(ERR_LABEL_NOT_FOUND, name->data); return 0; }
uint16_t treloc_init(struct aout_byte* start) { struct aout_byte* current; uint32_t mem_index; // Write out the table (maximum 2000 entries). reloc_count = 0; current = start; mem_index = 0; while (current != NULL) { if (current->type != AOUT_TYPE_NORMAL) { current = current->next; continue; } if (current->label == NULL) mem_index += 1; if (current->label_replace != NULL) { fprintf(stderr, "RELOC [0x%04X] 0x%04X (points to %s)\n", reloc_count, mem_index, current->label_replace); reloc_data[reloc_count] = mem_index; reloc_count += 1; if (reloc_count == RELOC_MAXIMUM_ENTRIES) ahalt(ERR_RELOCATION_TABLE_TOO_LARGE, NULL); } current = current->next; } // Return how much code should be offset by. return RELOC_OFFSET; }
uint16_t textn_init(struct aout_byte* start) { struct aout_byte* current; uint32_t mem_index; char* ext_name; // Write out the table (maximum 2000 entries). extension_count = 0; extension_offset = 0; current = start; mem_index = 0; while (current != NULL) { if (current->type == AOUT_TYPE_METADATA_EXTENSION) { ext_name = textn_verify_name(current->label); if (ext_name == NULL) ahalt(ERR_EXTENSION_UNKNOWN, current->label); extension_data[extension_count] = ext_name; printd(LEVEL_VERBOSE, "EXTENSION [0x%04X] %s\n", extension_offset, ext_name); extension_count += 1; extension_offset += strlen(ext_name) + 1; if (extension_count == EXTENSION_MAXIMUM_ENTRIES) ahalt(ERR_EXTENSION_TABLE_TOO_LARGE, NULL); } current = current->next; } // Return how much code should be offset by. if (extension_count > 0) return EXTENSION_OFFSET; else return 0; }
struct aout_byte* aout_create_opcode(uint16_t opcode, uint16_t a, uint16_t b) { struct aout_byte* byte = malloc(sizeof(struct aout_byte)); byte->type = AOUT_TYPE_NORMAL; byte->opcode = opcode; byte->a = a; byte->b = b; byte->next = NULL; byte->prev = NULL; byte->expr = NULL; byte->label = NULL; byte->raw_used = false; byte->raw = 0x0; list_init(&byte->symbols); if (opcode == 0 && a == 0) ahalt(ERR_OUTPUT_NULL, NULL); return byte; }
struct process_parameter_results process_register(struct ast_node_register* param) { struct process_parameter_results result; struct register_mapping* registr; if (param->bracketed) fprintf(stderr, "[%s]", param->value); else fprintf(stderr, "%s", param->value); registr = get_register_by_name(param->value, param->bracketed); if (registr == NULL) { // Must be a label. result.v = 0x0; result.v_extra = 0x0; result.v_extra_used = false; result.v_label = param->value; result.v_label_bracketed = param->bracketed; result.v_raw = NULL; } else if (registr->value == BRACKETS_UNSUPPORTED) { // Attempt to use a register in brackets that can't be. fprintf(stderr, "\n"); ahalt(ERR_BRACKETED_REGISTER_UNSUPPORTED, param->value); } else { // Must be a register. result.v = registr->value; result.v_extra = 0x0; result.v_extra_used = false; result.v_label = NULL; result.v_label_bracketed = false; result.v_raw = NULL; } return result; }
uint16_t ahalt_label_resolution_not_permitted(bstring name) { ahalt(ERR_LABEL_RESOLUTION_NOT_PERMITTED, name->data); return 0; }
void process_line(struct ast_node_line* line) { struct instruction_mapping* insttype; struct process_parameters_results ppresults; struct process_parameter_results dparam; struct ast_node_parameter* dcurrent; uint32_t dchrproc; uint16_t i, flimit, fchar, opos; // Change depending on the type of line. switch (line->type) { case type_keyword: switch (line->keyword) { case BOUNDARY: fprintf(stderr, ".BOUNDARY"); // Emit safety boundary of 16 NULL words. for (i = 0; i < 16; i += 1) aout_emit(aout_create_raw(0)); break; case FILL: fprintf(stderr, ".FILL"); // Emit N words with value X flimit = expr_evaluate(line->keyword_data_expr_1, &ahalt_label_resolution_not_permitted, &ahalt_expression_exit_handler); fchar = expr_evaluate(line->keyword_data_expr_2, &ahalt_label_resolution_not_permitted, &ahalt_expression_exit_handler); for (i = 0; i < flimit; i++) aout_emit(aout_create_raw(fchar)); break; case EXTENSION: fprintf(stderr, ".EXTENSION %s", line->keyword_data_string); // Emit extension metadata. aout_emit(aout_create_metadata_extension(bstr2cstr(line->keyword_data_string, '0'))); break; case INCBIN: fprintf(stderr, ".INCBIN %s", line->keyword_data_string); // Emit binary include metadata. aout_emit(aout_create_metadata_incbin(bstr2cstr(line->keyword_data_string, '0'))); break; case ORIGIN: opos = expr_evaluate(line->keyword_data_expr_1, &ahalt_label_resolution_not_permitted, &ahalt_expression_exit_handler); fprintf(stderr, ".ORIGIN 0x%04X", opos); // Emit origin set metadata. aout_emit(aout_create_metadata_origin(opos)); break; case EXPORT: fprintf(stderr, ".EXPORT %s", line->keyword_data_string); // Emit export metadata. aout_emit(aout_create_metadata_export(bstr2cstr(line->keyword_data_string, '0'))); break; case IMPORT: fprintf(stderr, ".IMPORT %s", line->keyword_data_string); // Emit export metadata. aout_emit(aout_create_metadata_import(bstr2cstr(line->keyword_data_string, '0'))); break; default: fprintf(stderr, "\n"); ahalt(ERR_UNSUPPORTED_KEYWORD, NULL); } fprintf(stderr, "\n"); break; case type_instruction: // Check to see if this is DAT. if (strcmp(line->instruction->instruction, "DAT") == 0) { // Handle data. fprintf(stderr, "EMIT DAT"); // Process parameters as data. reverse_parameters(line->instruction->parameters); dcurrent = line->instruction->parameters->last; while (dcurrent != NULL) { // Process parameter normally. dparam = process_parameter(dcurrent); // Output depending on what kind of parameter it was. if (dparam.v_label != NULL) // If this is a label, output something that we need to replace. aout_emit(aout_create_expr(expr_new_label(bautofree(bfromcstr(dparam.v_label))))); else if (dparam.v_raw != NULL) // If the raw field is not null, get each character and output it. { fprintf(stderr, " \"%s\"", dparam.v_raw->data); for (dchrproc = 0; dchrproc < blength(dparam.v_raw); dchrproc++) aout_emit(aout_create_raw(dparam.v_raw->data[dchrproc])); } else if (dparam.v_extra_used == true) // Just a single address. aout_emit(aout_create_expr(dparam.v_extra)); else // Something that isn't handled by DAT. { fprintf(stderr, "\n"); ahalt(ERR_DAT_UNSUPPORTED_PARAMETER, NULL); } dcurrent = dcurrent->prev; } } else { // Handle instruction. insttype = get_instruction_by_name(line->instruction->instruction); if (insttype == NULL) ahalt(ERR_UNKNOWN_OPCODE, line->instruction->instruction); fprintf(stderr, "EMIT %s", insttype->name); // Process parameters normally. ppresults = process_parameters(line->instruction->parameters); // Force the parameter value to be NXT if it's a label. if (ppresults.a_label != NULL) ppresults.a = NXT_LIT; if (ppresults.b_label != NULL) ppresults.b = NXT_LIT; if (ppresults.a_label != NULL && ppresults.a_label_bracketed) ppresults.a = NXT; if (ppresults.b_label != NULL && ppresults.b_label_bracketed) ppresults.b = NXT; // Output the initial opcode. if (insttype->opcode != OP_NONBASIC) aout_emit(aout_create_opcode(insttype->opcode, ppresults.a, ppresults.b)); else aout_emit(aout_create_opcode(insttype->opcode, insttype->nbopcode, ppresults.a)); // If the parameter is a label or requires an extra word, output them. if (ppresults.b_label != NULL) aout_emit(aout_create_expr(expr_new_label(bautofree(bfromcstr(ppresults.b_label))))); else if (ppresults.b_extra_used) aout_emit(aout_create_expr(ppresults.b_extra)); if (ppresults.a_label != NULL) aout_emit(aout_create_expr(expr_new_label(bautofree(bfromcstr(ppresults.a_label))))); else if (ppresults.a_extra_used) aout_emit(aout_create_expr(ppresults.a_extra)); } fprintf(stderr, "\n"); break; case type_label: // Handle label definition. fprintf(stderr, ":%s\n", line->label->name); aout_emit(aout_create_label(line->label->name)); break; } }
struct process_parameters_results process_parameters(struct ast_node_parameters* params) { struct process_parameters_results result; struct process_parameter_results t; reverse_parameters(params); result.raw = NULL; if (params->last != NULL) { t = process_parameter(params->last); if (t.v_raw) { fprintf(stderr, "\n"); ahalt(ERR_GEN_UNSUPPORTED_PARAMETER, NULL); } result.a = t.v; result.a_extra = t.v_extra; result.a_extra_used = t.v_extra_used; result.a_label = t.v_label; result.a_label_bracketed = t.v_label_bracketed; if (params->last->prev != NULL) { t = process_parameter(params->last->prev); if (t.v_raw) { fprintf(stderr, "\n"); ahalt(ERR_GEN_UNSUPPORTED_PARAMETER, NULL); } result.b = t.v; result.b_extra = t.v_extra; result.b_extra_used = t.v_extra_used; result.b_label = t.v_label; result.b_label_bracketed = t.v_label_bracketed; } else { result.b = 0x0; result.b_extra = 0x0; result.b_extra_used = false; result.b_label = NULL; result.b_label_bracketed = false; } } else { result.a = 0x0; result.a_extra = 0x0; result.a_extra_used = false; result.a_label = NULL; result.b_label_bracketed = false; result.b = 0x0; result.b_extra = 0x0; result.b_extra_used = false; result.b_label = NULL; result.b_label_bracketed = false; } return result; }
uint16_t aout_write(FILE* out, bool relocatable, bool intermediate) { struct aout_byte* current_outer; struct aout_byte* current_inner; struct lprov_entry* linker_provided = NULL; struct lprov_entry* linker_required = NULL; struct lprov_entry* linker_adjustment = NULL; struct lprov_entry* linker_section = NULL; struct lprov_entry* linker_output = NULL; struct lprov_entry* linker_temp = NULL; uint32_t mem_index, out_index; uint16_t inst; BFILE* temp = NULL; bstring bname, ename; uint16_t eaddr; bool did_find; bool shown_expr_warning = false; bool has_output = false; // Initialize out our extension table. code_offset += textn_init(start); // If relocatable, initialize out our relocation table. if (relocatable) code_offset += treloc_init(start); // First go through and evaluate all expressions that need to be. current_outer = start; out_index = code_offset; while (current_outer != NULL) { if (current_outer->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust memory address. out_index = current_outer->opcode; } else if (current_outer->type == AOUT_TYPE_METADATA_SECTION) { assert(current_outer->label != NULL); // We're exporting the current address as the beginning // of a section. if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); // Check to make sure outputs haven't previously been emitted. if (has_output) ahalt(ERR_OUTPUT_BEFORE_SECTION, NULL); // Create linker entry. linker_temp = lprov_create(current_outer->label, out_index); linker_temp->next = linker_section; linker_section = linker_temp; printd(LEVEL_VERBOSE, "LINK SECTION %s -> 0x%04X\n", current_outer->label, out_index); } else if (current_outer->type == AOUT_TYPE_METADATA_OUTPUT) { assert(current_outer->label != NULL); // We're exporting the current address as the beginning // of a section. if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); // Create linker entry. has_output = true; linker_temp = lprov_create(current_outer->label, out_index); linker_temp->next = linker_output; linker_output = linker_temp; printd(LEVEL_VERBOSE, "LINK OUTPUT 0x%04X -> %s\n", out_index, current_outer->label); } else if (current_outer->type == AOUT_TYPE_METADATA_EXPORT) { assert(current_outer->label != NULL); // We're exporting the address of this label in the // object table. if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); // Resolve label position. ename = bfromcstr(current_outer->label); eaddr = aout_get_label_address(ename); bdestroy(ename); // Create linker entry. linker_temp = lprov_create(current_outer->label, eaddr); linker_temp->next = linker_provided; linker_provided = linker_temp; printd(LEVEL_VERBOSE, "LINK REPLACE %s -> 0x%04X\n", current_outer->label, eaddr); } else if (current_outer->type == AOUT_TYPE_NORMAL && current_outer->expr != NULL) { if (current_outer->expr->type != EXPR_LABEL) { // This is either just a number or a more complicated expression, so // evaluate it using the preprocessor expression engine. if ((relocatable || intermediate) && !shown_expr_warning) { printd(LEVEL_WARNING, "warning: expressions will not be adjusted at link or relocation time.\n"); printd(LEVEL_WARNING, " ensure labels are not used as part of expressions.\n"); shown_expr_warning = true; } current_outer->raw_used = true; current_outer->raw = expr_evaluate(current_outer->expr, &aout_get_label_address, &ahalt_expression_exit_handler); expr_delete(current_outer->expr); current_outer->expr = NULL; } else { // If this is just a label, we can handle it directly (this allows // us to handle imported labels in intermediate code). current_inner = start; mem_index = code_offset; did_find = false; // Search for .IMPORT directives first. while (current_inner != NULL) { if (current_inner->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust memory address. mem_index = current_inner->opcode; } else if (current_inner->type == AOUT_TYPE_METADATA_IMPORT) { // An imported label (we don't need to adjust // memory index because the existance of this type // of entry doesn't affect executable size). if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); assert(current_outer->expr->data != NULL); if (strcmp(current_inner->label, ((bstring)current_outer->expr->data)->data) == 0) { // We don't actually know our position yet; // that will be handled by the linker! current_outer->raw = 0xFFFF; expr_delete(current_outer->expr); current_outer->expr = NULL; linker_temp = lprov_create(current_inner->label, out_index); linker_temp->next = linker_required; linker_required = linker_temp; printd(LEVEL_VERBOSE, "LINK REPLACE 0x%04X -> %s\n", out_index, current_inner->label); did_find = true; break; } } // Goto next. current_inner = current_inner->next; } // If it wasn't found in the .IMPORT directives, try searching // labels directly using the expression engine. if (!did_find) { // Replace the label position. current_outer->raw_used = true; current_outer->raw = expr_evaluate(current_outer->expr, &aout_get_label_address, &ahalt_expression_exit_handler); expr_delete(current_outer->expr); current_outer->expr = NULL; did_find = true; // We also need to add this entry to the adjustment // table for the linker since it also needs to adjust // internal label jumps in files when it concatenates // all of the object code together. linker_temp = lprov_create(NULL, out_index); linker_temp->next = linker_adjustment; linker_adjustment = linker_temp; printd(LEVEL_VERBOSE, "LINK ADJUST 0x%04X\n", out_index); } } } if (current_outer->type == AOUT_TYPE_NORMAL && current_outer->label == NULL) out_index += 1; current_outer = current_outer->next; } // If intermediate, we need to write out our linker table // as the absolute first thing in the file. if (intermediate) { fwrite(ldata_objfmt, 1, strlen(ldata_objfmt) + 1, out); objfile_save(out, linker_provided, linker_required, linker_adjustment, linker_section, linker_output); // Adjust the "true origin" for .ORIGIN directivies because // the linker table won't exist in the final result when // linked. true_origin = (uint16_t)ftell(out); } // Write out our extension table. textn_write(out); // If relocatable, write out our relocation table. if (relocatable) treloc_write(out); // Now write to the file. current_outer = start; while (current_outer != NULL) { if (current_outer->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust origin. fseek(out, true_origin + current_outer->opcode * 2 /* double because the number is in words, not bytes */, SEEK_SET); } else if (current_outer->type == AOUT_TYPE_METADATA_INCBIN) { // Include binary file. bname = ppfind_locate(bautofree(bfromcstr(current_outer->label))); if (bname == NULL) ahalt(ERR_UNABLE_TO_INCBIN, current_outer->label); temp = bfopen((const char*)(bname->data), "rb"); if (temp == NULL) ahalt(ERR_UNABLE_TO_INCBIN, current_outer->label); // Copy binary data. while (!bfeof(temp)) { // TODO: This could be faster if we didn't do it character // by character. fputc(bfgetc(temp), out); } // Finalize. bfclose(temp); bdestroy(bname); } else if (current_outer->type == AOUT_TYPE_NORMAL) { // Update the debugging symbol. dbgfmt_update_symbol_list(¤t_outer->symbols, (uint16_t)((ftell(out) - true_origin) / 2)); // Normal output. if (current_outer->raw_used == true) { inst = current_outer->raw; iwrite(&inst, out); } else if (current_outer->label == NULL) { inst = INSTRUCTION_CREATE(current_outer->opcode, current_outer->a, current_outer->b); iwrite(&inst, out); } } // Goto next in linked list. current_outer = current_outer->next; } fflush(out); return (uint16_t)((ftell(out) - true_origin) / 2); }