Example #1
0
int bcopy_01()
{
	BFILE* in;
	BFILE* out;
	int i = 0;

	in = bfopen("test.txt", "rb");
	TEST_EXPECT(in != NULL);
	out = bfopen("copy.txt", "wb");
	TEST_EXPECT(out != NULL);

	while (!bfeof(in))
	{
		bfputc(bfgetc(in), out);
		i++;
	}

	bfclose(in);
	bfclose(out);

	if (i == LENGTH_OF_TEST_FILE)
	{
		TEST_SUCCESS;
	}
	else
	{
		TEST_FAILURE("didn't copy all expected data (total bytes copied incorrect).");
	}
}
Example #2
0
void micro_write_file(BFILE* out, BFILE* in, freed_bstring name)
{
    int written = 0;
    uint16_t store;
    size_t pos;

    // Check to make sure we don't have too many entries.
    if (micro_table_count >= MICRO_MAX_ENTRIES)
    {
        printd(LEVEL_WARNING, "ignoring file %s; maximum limit of %u files hit.\n", name.ref->data, MICRO_MAX_ENTRIES);
        exit(1);
    }

    // Copy data.
    store = (uint16_t)(bftell(out) / 2);
    while (!bfeof(in))
    {
        bfputc(bfgetc(in), out);
        written++;
    }

    // Store the position so we can jump back to
    // this position.
    pos = bftell(out);

    // Write the entry.
    bfseek(out, micro_table_addr + micro_table_count * 2, SEEK_SET);
    bfiwrite(&store, out);
    store = written / 2;
    bfiwrite(&store, out);
    printd(LEVEL_VERBOSE, "wrote file %s at index %u (0x%04X words).\n", name.ref->data, micro_table_count, written / 2);
    micro_table_count++;

    // Seek back again.
    bfseek(out, pos, SEEK_SET);

    // Clear memory.
    bautodestroy(name);
}
Example #3
0
void micro_write_kernel(BFILE* out, BFILE* in, int count)
{
    int written = 0, i;
    uint16_t store;
    size_t pos;

    // Copy data.
    while (!bfeof(in))
    {
        bfputc(bfgetc(in), out);
        written++;
    }

    // Go back and write the filesystem
    // information if permitted.
    pos = bftell(out);
    bfseek(out, sizeof(uint16_t) * 2, SEEK_SET);
    bfiread(&store, out);
    bfseek(out, sizeof(uint16_t) * 2, SEEK_SET);
    if (store == 0xDEDE)
    {
        store = written / 2;
        bfiwrite(&store, out);
    }
    else
        printd(LEVEL_VERBOSE, "not writing end-of-kernel to 0x0002 (0x0002 is not 0xDEDE).\n");
    bfseek(out, pos, SEEK_SET);
    printd(LEVEL_VERBOSE, "wrote kernel (0x%04X words).\n", written / 2);

    // Store the position and count of the micro table.
    micro_table_addr = pos;
    micro_table_count = 0;

    // Write out 20 NULL entries (1 addr and 1 size for each entry).
    store = 0x0000;
    for (i = 0; i < MICRO_MAX_ENTRIES * 2; i++)
        bfiwrite(&store, out);
}
Example #4
0
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(&current_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);
}
Example #5
0
int main(int argc, char* argv[])
{
    bstring ldargs = bfromcstr("");
    int i, result;
    unsigned int match = 0, unmatch = 0;
    char ca, ce;
    BFILE* expect;
    BFILE* actual;

    // Define arguments.
    struct arg_lit* show_help = arg_lit0("h", "help", "Show this help.");
    struct arg_lit* gen_relocatable = arg_lit0("r", "relocatable", "Generate relocatable code.");
    struct arg_lit* gen_intermediate = arg_lit0("i", "intermediate", "Generate intermediate code for use with the linker.");
    struct arg_lit* little_endian_mode = arg_lit0(NULL, "little-endian", "Use little endian serialization.");
    struct arg_file* input_file = arg_file1(NULL, NULL, "<file>", "The input assembly file.");
    struct arg_file* expect_file = arg_file0("e", "expect", "<file>", "The output file that contains expected output.");
    struct arg_file* actual_file = arg_file1("a", "actual", "<file>", "The output file where actual output will be placed.");
    struct arg_file* symbols_file = arg_file0("s", "debug-symbols", "<file>", "The debugging symbol output file.");
    struct arg_lit* fail_opt = arg_lit0("f", "fail", "The assembler is expected to fail and the actual output file should not exist on completion.");
    struct arg_file* path = arg_file1("p", NULL, "<path>", "The path to the assembler.");
    struct arg_lit* verbose = arg_litn("v", NULL, 0, LEVEL_EVERYTHING - LEVEL_DEFAULT, "Increase verbosity.");
    struct arg_lit* quiet = arg_litn("q", NULL,  0, LEVEL_DEFAULT - LEVEL_SILENT, "Decrease verbosity.");
    struct arg_end* end = arg_end(20);
    void* argtable[] = { show_help, gen_relocatable, gen_intermediate, little_endian_mode, symbols_file, input_file, expect_file, actual_file, fail_opt, path, verbose, quiet, end };

    // Parse arguments.
    int nerrors = arg_parse(argc, argv, argtable);

    version_print(bautofree(bfromcstr("Assembler Test Driver")));
    if (nerrors != 0 || show_help->count != 0 || (fail_opt->count == 0 && (expect_file->count == 0 || actual_file->count == 0)))
    {
        if (show_help->count != 0 && fail_opt->count == 0 && (expect_file->count == 0 || actual_file->count == 0))
            printd(LEVEL_ERROR, "error: you must provide either -f or -e and -a.\n");
        if (show_help->count != 0)
            arg_print_errors(stderr, end, "testasm");

        printd(LEVEL_DEFAULT, "syntax:\n    testasm");
        arg_print_syntax(stderr, argtable, "\n");
        printd(LEVEL_DEFAULT, "options:\n");
        arg_print_glossary(stderr, argtable, "    %-25s %s\n");
        arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
        return 1;
    }

    // Set verbosity level.
    debug_setlevel(LEVEL_DEFAULT + verbose->count - quiet->count);

    // Set global path variable.
    osutil_setarg0(bautofree(bfromcstr(argv[0])));

    // Generate the argument list for the assembler.
    ldargs = bfromcstr(path->filename[0]);
    binsertch(ldargs, 0, 1, '"');
    bconchar(ldargs, '"');
    bconchar(ldargs, ' ');

    // Verbosity options.
    if (verbose->count > 0)
    {
        bconchar(ldargs, '-');
        for (i = 0; i < verbose->count; i++) bconchar(ldargs, 'v');
        bconchar(ldargs, ' ');
    }
    if (quiet->count > 0)
    {
        bconchar(ldargs, '-');
        for (i = 0; i < quiet->count; i++) bconchar(ldargs, 'q');
        bconchar(ldargs, ' ');
    }

    // Literal options.
    if (gen_relocatable->count > 0)
    {
        bconchar(ldargs, '-');
        for (i = 0; i < gen_relocatable->count; i++) bconchar(ldargs, 'r');
        bconchar(ldargs, ' ');
    }
    if (gen_intermediate->count > 0)
    {
        bconchar(ldargs, '-');
        for (i = 0; i < gen_intermediate->count; i++) bconchar(ldargs, 'i');
        bconchar(ldargs, ' ');
    }
    if (little_endian_mode->count > 0)
    {
        for (i = 0; i < little_endian_mode->count; i++)
            bcatcstr(ldargs, "--little-endian ");
    }

    // Unlink the actual file so that if we are expecting
    // failure, we won't return incorrectly.
    unlink(actual_file->filename[0]);

    // Output file.
    bcatcstr(ldargs, "-o \"");
    bcatcstr(ldargs, actual_file->filename[0]);
    bcatcstr(ldargs, "\" ");

    // Input file.
    bcatcstr(ldargs, "\"");
    bcatcstr(ldargs, input_file->filename[0]);
    bcatcstr(ldargs, "\" ");

    // Windows needs the whole command wrapped in quotes and slashes to be correct.
    // See http://stackoverflow.com/questions/2642551/windows-c-system-call-with-spaces-in-command.
#ifdef _WIN32
    binsertch(ldargs, 0, 1, '"');
    bconchar(ldargs, '"');
#endif

    // Now run the assembler!
    result = system(ldargs->data);
    if (result != 0 && fail_opt->count == 0)
    {
        // Assembler returned error exit code.
        printd(LEVEL_ERROR, "error: expected success but assembler returned non-zero exit code (%i).\n", result);
        return 1;
    }
    else if (result == 0 && fail_opt->count >= 1)
    {
        // Assembler returned zero when failure was expected.
        printd(LEVEL_ERROR, "error: expected failure but assembler returned zero exit code.\n");
        return 1;
    }
    else if (result != 0 && fail_opt->count >= 1)
    {
        // Assembler failed and we expected it to.  Return success only
        // if the output file does not exist.
        actual = bfopen(actual_file->filename[0], "rb");
        if (actual != NULL)
        {
            printd(LEVEL_ERROR, "error: expected failure but actual output file exists.\n");
            bfclose(actual);
            return 1;
        }
        return 0;
    }

    // Open expect data.
    expect = bfopen(expect_file->filename[0], "rb");
    if (expect == NULL)
    {
        // The expect file was not provided.
        printd(LEVEL_ERROR, "error: path to expect file does not exist.\n");
        return 1;
    }

    // Open actual data.
    actual = bfopen(actual_file->filename[0], "rb");
    if (actual == NULL)
    {
        // The expect file was not provided.
        bfclose(expect);
        printd(LEVEL_ERROR, "error: expected data but actual output file does not exist after running assembler.\n");
        return 1;
    }

    // Now compare raw bytes.
    while (true)
    {
        if (!bfeof(actual) && !bfeof(expect))
        {
            ca = bfgetc(actual);
            ce = bfgetc(expect);
            if (ca == ce)
                match++;
            else
            {
                printd(LEVEL_WARNING, "warning: byte at 0x%04X is different (got 0x%02X, expected 0x%02X)!\n", bftell(actual), ca, ce);
                unmatch++;
            }
        }
        else if (!bfeof(actual))
        {
            ca = bfgetc(actual);
            printd(LEVEL_ERROR, "error: actual output contained trailing byte 0x%02X.\n", (unsigned char)ca);
            unmatch++;
        }
        else if (!bfeof(expect))
        {
            ce = bfgetc(expect);
            printd(LEVEL_ERROR, "error: expected actual output to contain 0x%02X.\n", (unsigned char)ce);
            unmatch++;
        }
        else
            break;
    }
    if (unmatch > 0)
    {
        printd(LEVEL_ERROR, "error: actual output differs from expected output in content (%f%%, %i bytes different).\n", 100.f / (unmatch + match) * unmatch, unmatch);
        if (bftell(actual) != bftell(expect))
            printd(LEVEL_ERROR, "error: actual output differs from expected output in length (%i bytes larger).\n", bftell(actual) - bftell(expect));
        bfclose(actual);
        bfclose(expect);
        return 1;
    }

    // Close files and delete actual because we have
    // succeeded.
    bfclose(actual);
    bfclose(expect);
    unlink(actual_file->filename[0]);

    return 0;
}