tisc

tiny instruction set computer
Log | Files | Refs | README

tisc.c (18251B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <stdint.h>
      4 #include <string.h>
      5 
      6 #define VERSION_STRING "v2.3"
      7 #define TOT_INSTRUCTIONS 33
      8 #define MAX_SYMBOLS    1000
      9 #define MAX_SYMBOL_LEN 100
     10 #define MAX_LINE_LEN   100
     11 #define MAX_PGR_SIZE   0xFFFF
     12 #define MAX_LNI_SIZE   17
     13 
     14 #define NR          0x0
     15 #define NR_STRING   "NIL"
     16 #define GR_A        0x1
     17 #define GR_A_STRING "GRA"
     18 #define GR_B        0x2
     19 #define GR_B_STRING "GRB"
     20 #define GR_C        0x3
     21 #define GR_C_STRING "GRC"
     22 
     23 #define TOKENIZER " \t\n"
     24 
     25 int  validSymbols = 0;
     26 int  addresses[MAX_SYMBOLS];
     27 char symbols[MAX_SYMBOLS][MAX_SYMBOL_LEN];
     28 
     29 typedef struct InstructionDefintion
     30 {
     31 	char* instructionLabel;
     32 	int arguements;
     33 	int instructionLength;
     34 	uint8_t opcode_mask;
     35 	int (*assemble)(struct InstructionDefintion*, char* arg[3], uint8_t*, int);
     36 } InstructionDefinition_t;
     37 
     38 /* Checks if instruction is valid
     39  *
     40  *  return -1 -> too few arguements
     41  *  return  0 -> instruction is valid
     42  *  return  1 -> too many arguements
     43  */
     44 int validins(InstructionDefinition_t *ins, const char *opcode, char *arg[])
     45 {
     46 	int status = 0;
     47 
     48 	int supp_args;
     49 
     50 	for (supp_args = 0; supp_args < 3; supp_args++)
     51 	{
     52 		if (arg[supp_args] == NULL || arg[supp_args][0] == '#')
     53 		{
     54 			break;
     55 		}
     56 	}
     57 
     58 	if (ins->arguements > supp_args)
     59 	{
     60 		status = -1; // Too few args, fail
     61 	}
     62 	else
     63 	if (ins->arguements < supp_args)
     64 	{
     65 		status = 1; // Too many args, fail
     66 	}
     67 
     68 	return status;
     69 }
     70 
     71 void add_label(const char* label, int address)
     72 {
     73 	//add label to table 'o' label
     74 	strcpy(symbols[validSymbols], label);
     75 	addresses[validSymbols] = address;
     76 	validSymbols++;
     77 }
     78 
     79 int longest_label()
     80 {
     81 	int i, labelLen = 0;
     82 
     83 	for (i = 0; i < validSymbols; i++)
     84 	{
     85 		int len = strlen(symbols[i]);
     86 		if (len > labelLen)
     87 		{
     88 			labelLen = len;
     89 		}
     90 	}
     91 
     92 	return (labelLen > 5) ? labelLen : 5;
     93 }
     94 
     95 int label_address(const char *label)
     96 {
     97 	int i, address = -1;
     98 
     99 	for (i = 0; i < validSymbols; i++)
    100 	{
    101 		if (strcmp(symbols[i], label) == 0)
    102 		{
    103 			address = addresses[i];
    104 			break;
    105 		}
    106 	}
    107 
    108 	return address;
    109 }
    110 
    111 void update_label(const char* label, int address)
    112 {
    113 	int i;
    114 
    115 	for (i = 0; i < validSymbols; i++)
    116 	{
    117 		if (strcmp(symbols[i], label) == 0)
    118 		{
    119 			addresses[i] = address & 0xFF;
    120 			break;
    121 		}
    122 	}
    123 }
    124 
    125 int labelexists(const char *label)
    126 {
    127 	int i, status = 0;
    128 
    129 	for (i = 0; i < validSymbols; i++)
    130 	{
    131 		if (strcmp(symbols[i], label) == 0)
    132 		{
    133 			status = 1;
    134 		}
    135 	}
    136 
    137 	return status;
    138 }
    139 
    140 int process_label_initial(char *label, int address)
    141 {
    142 	int status = 0;
    143 
    144 	if (label != NULL)
    145 	{
    146 		//duplicate label check
    147 		if (labelexists(label) == 0)
    148 		{
    149 			add_label(label, address);
    150 
    151 			status = 1;
    152 		}
    153 	}
    154 	else
    155 	{
    156 		status = 1;
    157 	}
    158 
    159 	return status;
    160 }
    161 
    162 int process_label_final(char *label, int address)
    163 {
    164 	int status = 0;
    165 
    166 	if (label != NULL)
    167 	{
    168 		//make sure the label exists 
    169 		if (labelexists(label) == 1)
    170 		{
    171 			update_label(label, address);
    172 			
    173 			status = 1;
    174 		}
    175 	}
    176 	else
    177 	{
    178 		status = 1;
    179 	}
    180 
    181 	return status;
    182 }
    183 
    184 uint8_t getRegisterEnumeration(char* string)
    185 {
    186 	if (string == NULL) {
    187 		return 0xFF;
    188 	}
    189 	if (strncmp(string, GR_A_STRING, strlen(GR_A_STRING)) == 0)
    190 	{
    191 		return GR_A;
    192 	}
    193 	if (strncmp(string, GR_B_STRING, strlen(GR_B_STRING)) == 0)
    194 	{
    195 		return GR_B;
    196 	}
    197 	if (strncmp(string, GR_C_STRING, strlen(GR_C_STRING)) == 0)
    198 	{
    199 		return GR_C;
    200 	}
    201 	if (strncmp(string, NR_STRING, strlen(NR_STRING)) == 0)
    202 	{
    203 		return NR;
    204 	}
    205 
    206 	return 0xFF;
    207 }
    208 
    209 int stringToInteger(char* str)
    210 {
    211 	int return_value = -1;
    212 
    213 	if (strncmp("0x", str, 2) == 0)
    214 	{
    215 		return_value = (int)strtol(str+2, NULL, 16);
    216 	}
    217 	else if (strncmp("0b", str, 2) == 0)
    218 	{
    219 		return_value = (int)strtol(str+2, NULL, 2);
    220 	}
    221 	else
    222 	{
    223 		return_value = atoi(str);
    224 	}
    225 
    226 	return return_value;
    227 }
    228 
    229 int assemble_0arg(
    230 	InstructionDefinition_t *definition,
    231 	char* arg[3], uint8_t* write_buffer,
    232 	int address)
    233 {
    234 	write_buffer[0] = definition->opcode_mask;
    235 
    236 	return 1;
    237 }
    238 
    239 int assemble_1arg(
    240 	InstructionDefinition_t *definition,
    241 	char* arg[3], uint8_t* write_buffer,
    242 	int address)
    243 {
    244 	uint8_t argB = getRegisterEnumeration(arg[0]);
    245 	
    246 	write_buffer[0] = definition->opcode_mask | (argB << 4);
    247 
    248 	return 1;
    249 }
    250 
    251 int assemble_2arg(
    252 	InstructionDefinition_t *definition,
    253 	char* arg[3], uint8_t* write_buffer,
    254 	int address)
    255 {
    256 	uint8_t argA, argB;
    257 
    258 	argA = getRegisterEnumeration(arg[0]);
    259 	argB = getRegisterEnumeration(arg[1]);
    260 
    261 	write_buffer[0] = definition->opcode_mask;
    262 
    263 	write_buffer[0] |= ( argA << 2 ) | ( argB << 4);
    264 	
    265 	return 1;
    266 }
    267 
    268 int assemble_cin(
    269 	InstructionDefinition_t *definition,
    270 	char* arg[3], uint8_t* write_buffer,
    271 	int address)
    272 {
    273 	uint8_t argB, argC;
    274 
    275 	argB = getRegisterEnumeration(arg[0]);
    276 	argC = getRegisterEnumeration(arg[1]);
    277 
    278 	write_buffer[0] = definition->opcode_mask;
    279 
    280 	write_buffer[0] |= ( argB << 4) | ( argC << 6);
    281 	
    282 	return 1;
    283 }
    284 
    285 int assemble_mov(
    286 	InstructionDefinition_t *definition,
    287 	char* arg[3], uint8_t* write_buffer,
    288 	int address)
    289 {
    290 	uint8_t argA, argC;
    291 
    292 	argA = getRegisterEnumeration(arg[0]);
    293 	argC = getRegisterEnumeration(arg[1]);
    294 
    295 	write_buffer[0] = definition->opcode_mask;
    296 
    297 	write_buffer[0] |= ( argA << 2) | ( argC << 6);
    298 	
    299 	return 1;
    300 }
    301 
    302 int assemble_3arg(
    303 	InstructionDefinition_t *definition,
    304 	char* arg[3], uint8_t* write_buffer,
    305 	int address)
    306 {
    307 	uint8_t argA, argB, argC;
    308 
    309 	argA = getRegisterEnumeration(arg[0]);
    310 	argB = getRegisterEnumeration(arg[1]);
    311 	argC = getRegisterEnumeration(arg[2]);
    312 
    313 	write_buffer[0] = definition->opcode_mask;
    314 
    315 	write_buffer[0] |= ( argA << 2 ) | ( argB << 4) | ( argC << 6);
    316 	
    317 	return 1;
    318 }
    319 
    320 int assemble_lli(
    321 	InstructionDefinition_t *definition,
    322 	char* arg[3], uint8_t* write_buffer,
    323 	int address)
    324 {
    325 	int return_value = 0;
    326 	
    327 	int immediate_value = stringToInteger(arg[0]);
    328 
    329 	if ((immediate_value & 0xF0) == 0)
    330 	{
    331 		write_buffer[0] = definition->opcode_mask | immediate_value << 2;
    332 
    333 		return_value = 1;
    334 	}
    335 	else
    336 	{
    337 		printf("FATAL: Value provided is invalid: (%i)", immediate_value);
    338 		printf("FATAL: Value must not be > 15");
    339 	}
    340 
    341 	return return_value;
    342 }
    343 
    344 int assemble_li(
    345 	InstructionDefinition_t *definition,
    346 	char* arg[3], uint8_t* write_buffer,
    347 	int address)
    348 {
    349 	int return_value = 0;
    350 	
    351 	int immediate_value = stringToInteger(arg[0]);
    352 
    353 	if (immediate_value < 256 && immediate_value >= 0)
    354 	{
    355 		write_buffer[0] = definition->opcode_mask;
    356 		write_buffer[1] = immediate_value;
    357 
    358 		return_value = 1;
    359 	}
    360 	else
    361 	{
    362 		printf("FATAL: value provided is invalid %i\n", immediate_value);
    363 		printf("FATAL: value must be < 256 && >= 0\n");
    364 	}
    365 
    366 	return return_value;
    367 }
    368 
    369 int assemble_lni(
    370 	InstructionDefinition_t *definition,
    371 	char* arg[3], uint8_t* write_buffer,
    372 	int address)
    373 {
    374 	int return_value = 0;
    375 
    376 	int byte_len = 0;
    377 	int bytes[MAX_LNI_SIZE];
    378 	if (arg[0][0] == '"')
    379 	{
    380 		for (int i = 1; arg[0][i] != '\0'; i++)
    381 		{
    382 			
    383 		}
    384 	}
    385 	else
    386 	{
    387 		char* arg_copy = (char*)malloc(sizeof(char) * strlen(arg[0]));
    388 		strcpy(arg_copy, arg[0]);
    389 		char* byte_str = strtok(arg_copy, ",");
    390 	
    391 		while (byte_str != NULL)
    392 		{
    393 			int value = stringToInteger(byte_str);
    394 	
    395 			if (value > 255 || value < 0)
    396 			{
    397 				byte_len = 0;
    398 				printf("FATAL: value provided is invalid %i\n", value);
    399 				break;
    400 			}
    401 	
    402 			byte_str = strtok(NULL, ",");
    403 			if (byte_len < MAX_LNI_SIZE)
    404 			{
    405 				bytes[byte_len] = value;
    406 			}
    407 			byte_len++;
    408 		}
    409 	
    410 		free(arg_copy);
    411 	}
    412 
    413 	if (byte_len <= 1)
    414 	{
    415 		printf("FATAL: must provide byte array length > 1\n");
    416 	}
    417 
    418 	if (byte_len > 1 && byte_len < MAX_LNI_SIZE)
    419 	{
    420 		definition->instructionLength = byte_len + 1;
    421 
    422 		write_buffer[0] = definition->opcode_mask | (byte_len - 1) << 2;
    423 
    424 		for (int i = 1; (i-1) < byte_len; i++)
    425 		{
    426 			write_buffer[i] = bytes[i - 1];
    427 		}
    428 
    429 		return_value = 1;
    430 	}
    431 	else
    432 	{
    433 		printf("FATAL: byte array has too many elements. length must be <= %i\n", MAX_LNI_SIZE);
    434 		printf("FATAL:                                            length = %i\n", byte_len);
    435 	}
    436 
    437 	return return_value;
    438 }
    439 
    440 int assemble_jmp(
    441 	InstructionDefinition_t *definition,
    442 	char* arg[3], uint8_t* write_buffer,
    443 	int address)
    444 {
    445 	int return_value = 0;
    446 
    447 	int jump_address = label_address(arg[0]);
    448 
    449 	if (jump_address != -1)
    450 	{
    451 		write_buffer[0] = definition->opcode_mask;
    452 		write_buffer[1] = jump_address;
    453 
    454 		return_value = 1;
    455 	}
    456 	else
    457 	{
    458 		printf("Could not find label %s\n", arg[0]);
    459 	}
    460 
    461 	return return_value;
    462 }
    463 
    464 int segment(
    465 	InstructionDefinition_t *definition,
    466 	char* arg[3], uint8_t* write_buffer,
    467 	int address)
    468 {
    469 	int return_value = 0;
    470 	int target_address = stringToInteger(arg[0]);
    471 
    472 	if (target_address < MAX_PGR_SIZE && (target_address > address))
    473 	{
    474 		definition->instructionLength = target_address - address;
    475 		return_value = 1;
    476 	}
    477 	else
    478 	{
    479 		printf("Segment address must be less than %i\n", MAX_PGR_SIZE);
    480 	}
    481 
    482 	return return_value;
    483 }
    484 
    485 int getlabel(
    486 	InstructionDefinition_t *definition,
    487 	char* arg[3], uint8_t* write_buffer,
    488 	int address)
    489 {
    490 	int return_value = 0;
    491 
    492 	int label = label_address(arg[0]);
    493 
    494 	if (label != -1)
    495 	{
    496 		write_buffer[0] = definition->opcode_mask;
    497 		write_buffer[1] = label;
    498 
    499 		return_value = 1;
    500 	}
    501 	else
    502 	{
    503 		printf("Could not find label %s\n", arg[0]);
    504 	}
    505 
    506 	return return_value;
    507 }
    508 
    509 InstructionDefinition_t definitions[TOT_INSTRUCTIONS] =
    510 {
    511 	//opcode,args,size,byte,func 
    512 	{ "getlabel",  1, 2, 0x43, getlabel      },
    513 	{ "segment",   1, 0, 0x00, segment       },
    514 	{ "nop",       0, 1, 0x00, assemble_0arg },
    515 	{ "push",      0, 1, 0x93, assemble_0arg },
    516 	{ "pop",       0, 1, 0xA3, assemble_0arg },
    517 	{ "peek",      0, 1, 0xB3, assemble_0arg },
    518 	{ "lbs",       0, 1, 0x87, assemble_0arg },
    519 	{ "sbs",       0, 1, 0x8B, assemble_0arg },
    520 	{ "sps",       0, 1, 0x8F, assemble_0arg },
    521 	{ "sop_add",   0, 1, 0x00, assemble_0arg },
    522 	{ "sop_sub",   0, 1, 0x10, assemble_0arg },
    523 	{ "sop_and",   0, 1, 0x20, assemble_0arg },
    524 	{ "sop_xor",   0, 1, 0x30, assemble_0arg },
    525 	{ "sop_xnor",  0, 1, 0x40, assemble_0arg },
    526 	{ "sop_cin",   0, 1, 0x50, assemble_0arg },
    527 	{ "sop_lsh",   0, 1, 0x60, assemble_0arg },
    528 	{ "sop_rsh",   0, 1, 0x70, assemble_0arg },
    529 	{ "goto",      0, 1, 0x90, assemble_0arg },
    530 	{ "pcr",       0, 1, 0xA0, assemble_0arg },
    531 	{ "ptrinc",    0, 1, 0xF0, assemble_0arg },
    532 	{ "lli",       1, 1, 0x03, assemble_lli  },
    533 	{ "lni",       1, 1, 0x43, assemble_lni  },
    534 	{ "li",        1, 2, 0x43, assemble_li   },
    535 	{ "jmp",       1, 2, 0x83, assemble_jmp  },
    536 	{ "lb",        1, 1, 0x87, assemble_1arg },
    537 	{ "sb",        1, 1, 0x8B, assemble_1arg },
    538 	{ "sp",        1, 1, 0x8F, assemble_1arg },
    539 	{ "cin",       2, 1, 0x02, assemble_cin  },
    540 	{ "mov",       2, 1, 0x00, assemble_mov  },
    541 	{ "cmp",       2, 1, 0xC3, assemble_2arg },
    542 	{ "or",        3, 1, 0x00, assemble_3arg },
    543 	{ "nand",      3, 1, 0x01, assemble_3arg },
    544 	{ "op",        3, 1, 0x02, assemble_3arg }
    545 };
    546 
    547 InstructionDefinition_t* getInstructionFromOpcode(const char *opcode)
    548 {
    549 	InstructionDefinition_t* return_value = NULL;
    550 
    551 	int i;
    552 	for (i = 0; i < TOT_INSTRUCTIONS; i++)
    553 	{
    554 		char *label = definitions[i].instructionLabel;
    555 		if (strlen(label) == strlen(opcode) &&
    556 		    strncmp(label, opcode, strlen(label)) == 0)
    557 		{
    558 			return_value = &definitions[i];
    559 			break;
    560 		}
    561 	}
    562 
    563 	return return_value;
    564 }
    565 
    566 /* char *parse -> parses file and sets label, opcode, and args
    567    accordingly */
    568 int parse(int* line_number, FILE *file, char *line, char **label, char **opcode, char *arg[3])
    569 {
    570 	int success = 0;
    571 
    572 	char *str, *first;
    573 	str = fgets(line, MAX_LINE_LEN, file);
    574 
    575 	if (str != NULL)
    576 	{
    577 		first = strtok(line, TOKENIZER);
    578 
    579 		if (first == NULL || first[0] == '#')
    580 		{
    581 			*line_number = *line_number + 1;
    582 			return parse(line_number, file, line, label, opcode, arg);
    583 		}
    584 		else if (first[strlen(first) - 1] == ':')
    585 		{
    586 			*label = first;
    587 			*opcode = strtok(NULL, TOKENIZER);
    588 			if (*opcode != NULL)
    589 			{
    590 				first[strlen(first) - 1] = '\0';
    591 			}
    592 
    593 			success = 1;
    594 		}
    595 		else
    596 		{
    597 			*label = NULL;
    598 			*opcode = first;
    599 
    600 			success = 1;
    601 		}
    602 
    603 		if (success)
    604 		{
    605 			arg[0] = strtok(NULL, TOKENIZER);
    606 			arg[1] = strtok(NULL, TOKENIZER);
    607 			arg[2] = strtok(NULL, TOKENIZER);
    608 		}
    609 
    610 		*line_number = *line_number + 1;
    611 	}
    612 
    613 	return success;
    614 }
    615 
    616 /* int labelprocess -> 1st pass over file, defines all labels
    617  */
    618 int labelprocess(int line, int *address, char *label, char *opcode, char *arg[3])
    619 {
    620 
    621 	int status = 1;
    622 
    623 	if (process_label_initial(label, 0) == 0)
    624 	{
    625 		printf(
    626 			"Error:%i: re-use of existing label '%s'\n",
    627 				   line,                         label );
    628 
    629 		status = 0;
    630 	}
    631 
    632 	return status;
    633 }
    634 
    635 /* int preprocess -> 2nd pass over file, links symbols to address
    636    and reports syntax errors, fails if returns -1 */
    637 int preprocess(int line, int *address, char *label, char *opcode, char *arg[3])
    638 {
    639 	int status = 0;
    640 
    641 	if (opcode != NULL)
    642 	{
    643 		InstructionDefinition_t* ins = getInstructionFromOpcode(opcode);
    644 
    645 		if (ins != NULL)
    646 		{
    647 			switch (validins(ins, opcode, arg))
    648 			{
    649 			case -1:
    650 				printf("Error:%i: too few arguements for '%s'\n",
    651 				              line,                       opcode);
    652 				status = 0;
    653 				break;
    654 			case 1:
    655 				printf("Error:%i: too many arguements for '%s'\n",
    656 				              line,                        opcode);
    657 				status = 0;
    658 				break;
    659 			case 0:
    660 				status = 1;
    661 				break;
    662 			default:
    663 				status = 0;
    664 				printf("Something Weird!\n");
    665 				break;
    666 			}
    667 		}
    668 		else
    669 		{
    670 			printf("Error:%i: instruction '%s' does not exist\n",
    671 			              line,            opcode);
    672 			status = 0;
    673 		}
    674 
    675 		process_label_final(label, *address);
    676 
    677 		if (status)
    678 		{
    679 			uint8_t test_buffer[32];
    680 			status = ins->assemble(ins, arg, test_buffer, *address);
    681 
    682 			*address = *address + ins->instructionLength;
    683 		}
    684 	}
    685 	else
    686 	{
    687 		printf("Error:%i: Unspecified opcode\n", line);
    688 		status = 0;
    689 	}
    690 
    691 	return status;
    692 }
    693 
    694 /* int process -> 3rd pass over file, instructions are turned into
    695    machine code with symbols filled in as adresses,
    696    fails if returns -1 */
    697 int process(
    698 	int line, int* address, uint8_t *buffer, char *label, char *opcode, char *arg[3]) 
    699 {
    700 	int status = 0;
    701 
    702 	InstructionDefinition_t* ins = getInstructionFromOpcode(opcode);
    703 
    704 	if (ins != NULL && ins->assemble != NULL)
    705 	{
    706 		status = ins->assemble(ins, arg, buffer + *address, *address);
    707 	}
    708 
    709 	if (status)
    710 	{
    711 		*address = *address + ins->instructionLength;
    712 	}
    713 	
    714 	return status;
    715 }
    716 
    717 void print_instruction_header(int label_width)
    718 {
    719 	char* label_str = (char*)malloc(sizeof(char) * (label_width + 1));
    720 	for (int i = 0; i < label_width; i++)
    721 	{
    722 		label_str[i] = ' ';
    723 	}
    724 	label_str[label_width] = '\0';
    725 	strncpy(label_str, "label", 5);
    726 
    727 	printf("%s line  addr:out>op \targs\n", label_str);
    728 	free(label_str);
    729 }
    730 
    731 /* void print_instruction -> prints the instruction with some helpful information
    732    about the source line number, the physical address, the label the instruction has,
    733    and the args for that instruction
    734 */
    735 void print_instruction(int line, int begin_address, int end_address, uint8_t *buffer, char *label, char *opcode, char *arg[3], int label_width)
    736 {
    737 	int len = 0;
    738 	for (int i = 0; arg[i] != NULL && i < 3; i++)
    739 	{
    740 		len += strlen(arg[i]) + 1;
    741 	}
    742 	char* arg_str = "";
    743 	if (len > 0)
    744 	{
    745 		arg_str = (char*)malloc(sizeof(char) * len);
    746 
    747 		for (int i = 0; i < len; i++)
    748 		{
    749 			arg_str[i] = '\0';
    750 		}
    751 
    752 		for (int i = 0; i < 3; i++)
    753 		{
    754 			if (arg[i] == NULL || arg[i][0] == '#')
    755 			{
    756 				break;
    757 			}
    758 			sprintf(arg_str, "%s%s\t", arg_str, arg[i]);
    759 		}
    760 	}
    761 
    762 	char* label_str = (char*)malloc(sizeof(char) * (label_width + 1));
    763 	for (int i = 0; i < label_width; i++)
    764 	{
    765 		label_str[i] = ' ';
    766 	}
    767 	label_str[label_width] = '\0';
    768 
    769 	if (label != NULL)
    770 	{
    771 		strncpy(label_str, label, strlen(label));
    772 	}
    773 
    774 	int this_size = (int)(end_address - begin_address);
    775 	uint8_t *this_buffer = buffer + begin_address;
    776 
    777 	printf("%s %04i  %04x:%02x| %s\t%s\n", label_str, line + 1, begin_address, this_buffer[0], opcode, arg_str);
    778 	for (int i = 1; i < this_size; i++)
    779 	{
    780 		printf("%s       %04x:%02x| +\n", label_str, begin_address + i, this_buffer[i]);
    781 	}
    782 	free(label_str);
    783 	if (strlen(arg_str) != 0)
    784 	{
    785 		free(arg_str);
    786 	}
    787 }
    788 
    789 int output_file(FILE* output, uint8_t* bytes, int size)
    790 {
    791 	fprintf(output, "v2.0 raw");
    792 
    793 	for (int i = 0; i < size; i++)
    794 	{
    795 		fprintf(output, "%s%x", (i % 8 == 0) ? "\n" : " ", bytes[i]);
    796 	}
    797 	return 1;
    798 }
    799 
    800 int main(int argc, char *argv[])
    801 {
    802 	if (argc != 3)
    803 	{
    804 		fprintf(stderr,
    805 			"do: %s <assembly-code-file> <machine-code-file> \n",
    806 				argv[0]);
    807 
    808 		exit(1);
    809 	}
    810 
    811 	char *input,  *output;
    812 	FILE *inputf = NULL, *outputf = NULL;
    813 	char *label, *opcodes, *args[3];
    814 	char line[MAX_LINE_LEN + 1];
    815 	int address = 0, line_number = 0;
    816 
    817 	input = argv[1];
    818 	inputf = fopen(input, "r");
    819 
    820 	if (inputf == NULL || ferror(inputf))
    821 	{
    822 		printf("Error opening file '%s'\n", input);
    823 		goto DITCH;
    824 	}
    825 
    826 	output = argv[2];
    827 	outputf = fopen(output, "w");
    828 
    829 	if (outputf == NULL || ferror(outputf))
    830 	{
    831 		printf("Error opening file '%s'\n", output);
    832 		goto DITCH;
    833 	}
    834 	printf("Assembling tac file: '%s' TISC %s\n", input, VERSION_STRING);
    835 
    836 	while (parse(&line_number, inputf, line, &label, &opcodes, args))
    837 	{
    838 		if (labelprocess(line_number, &address, label, opcodes, args) == 0)
    839 		{
    840 			printf("Labelprocess: Error on line #%i\n", line_number);
    841 
    842 			goto CLOSEFILES;
    843 		}
    844 	}
    845 
    846 	address = 0;
    847 	line_number = 0;
    848 
    849 	rewind(inputf);
    850 
    851 	while (parse(&line_number, inputf, line, &label, &opcodes, args))
    852 	{
    853 		if (preprocess(line_number, &address, label, opcodes, args) == 0)
    854 		{
    855 			printf("Preprocess: Error on line #%i\n", line_number);
    856 
    857 			goto CLOSEFILES;
    858 		}
    859 	}
    860 
    861 	rewind(inputf);
    862 
    863 	int full_size = address;
    864 
    865 	if (full_size > MAX_PGR_SIZE)
    866 	{
    867 		printf("FATAL: Program exceeds maximum size!\n");
    868 		printf("FATAL: program size=%i max=%i\n", full_size, MAX_PGR_SIZE);
    869 		goto CLOSEFILES;
    870 	}
    871 	
    872 	uint8_t* w_buffer = (uint8_t*)malloc(sizeof(uint8_t) * full_size);
    873 
    874 	if (w_buffer != NULL)
    875 	{
    876 		address = 0;
    877 		line_number = 0;
    878 
    879 		int label_width = longest_label();
    880 
    881 		print_instruction_header(label_width);
    882 
    883 		while (parse(&line_number, inputf, line, &label, &opcodes, args))
    884 		{
    885 			int begin_address = address;
    886 			int begin_line = line_number;
    887 			if (process(line_number, &address, w_buffer, label, opcodes, args) == 0)
    888 			{
    889 				printf("Process: Error on line #%i\n", line_number);
    890 				free(w_buffer);
    891 				goto CLOSEFILES;
    892 			}
    893 
    894 			print_instruction(begin_line - 1, begin_address, address, w_buffer, label, opcodes, args, label_width);
    895 		}
    896 
    897 		// Output Logisim raw v2.0 format
    898 
    899 		printf("Finished assembling tac file: '%s', program size: %i byte%s\n", input, full_size, (full_size > 1) ? "s":"");
    900 
    901 		output_file(outputf, w_buffer, full_size);
    902 
    903 		free(w_buffer);
    904 	}
    905 
    906 CLOSEFILES:
    907 	if (inputf != NULL) fclose(inputf);
    908 	if (outputf != NULL) fclose(outputf);
    909 
    910 DITCH:
    911 	return 1;
    912 }