/* First pass of the assembler. You should implement pass_two() first. This function should read each line, strip all comments, scan for labels, and pass instructions to write_pass_one(). The input file may or may not be valid. Here are some guidelines: 1. Only one label may be present per line. It must be the first token present. Once you see a label, regardless of whether it is a valid label or invalid label, treat the NEXT token as the beginning of an instruction. 2. If the first token is not a label, treat it as the name of an instruction. 3. Everything after the instruction name should be treated as arguments to that instruction. If there are more than MAX_ARGS arguments, call raise_extra_arg_error() and pass in the first extra argument. Do not write that instruction to memory. 4. Only one instruction should be present per line. You do not need to do anything extra to detect this - it should be handled by guideline 3. 5. A line containing only a label is valid. The address of the label should be the byte offset of the next instruction, regardless of whether there is a next instruction or not. Just like in pass_two(), if the function encounters an error it should NOT exit, but process the entire file and return -1. If no errors were encountered, it should return 0. */ int pass_one(FILE* input, FILE* output, SymbolTable* symtbl) { /* YOUR CODE HERE */ char buf[BUF_SIZE]; uint32_t input_line = 0, byte_offset = 0; int ret_code = 0; // Read lines and add to instructions while(fgets(buf, BUF_SIZE, input)) { input_line++; // Ignore comments skip_comment(buf); // Scan for the instruction name char* token = strtok(buf, IGNORE_CHARS); // Scan for arguments char* args[MAX_ARGS]; int num_args = 0; // Checks to see if there were any errors when writing instructions unsigned int lines_written = write_pass_one(output, token, args, num_args); if (lines_written == 0) { raise_inst_error(input_line, token, args, num_args); ret_code = -1; } byte_offset += lines_written * 4; } return -1; }
/* Reads an intermediate file and translates it into machine code. You may assume: 1. The input file contains no comments 2. The input file contains no labels 3. The input file contains at maximum one instruction per line 4. All instructions have at maximum MAX_ARGS arguments 5. The symbol table has been filled out already If an error is reached, DO NOT EXIT the function. Keep translating the rest of the document, and at the end, return -1. Return 0 if no errors were encountered. */ int pass_two(FILE *input, FILE* output, SymbolTable* symtbl, SymbolTable* reltbl) { // Since we pass this buffer to strtok(), the chars here will GET CLOBBERED. char buf[BUF_SIZE]; // Store input line number / byte offset below. When should each be incremented? uint32_t byte_offset = 0; int line_number = 1; // First, read the next line into a buffer. fgets(buf, BUF_SIZE, input); // Next, use strtok() to scan for next character. If there's nothing, // go to the next line. strtok(buf, "\n"); while (strcmp(buf, "") == 0) { line_number++; strtok(buf, "\n"); } // Parse for instruction arguments. You should use strtok() to tokenize // the rest of the line. Extra arguments should be filtered out in pass_one(), // so you don't need to worry about that here. int errored = 0; while (buf != NULL) { char* args[MAX_ARGS]; int num_args = 0; *args = strtok(buf, " ,\n"); num_args = sizeof(args)/sizeof(args[0]); // Use translate_inst() to translate the instruction and write to output file. // If an error occurs, the instruction will not be written and you should call // raise_inst_error(). if (strcmp(*args, "") == 0) { line_number++; int boolean = translate_inst(output, args[0], args, num_args, byte_offset, symtbl, reltbl); if (!boolean) { raise_inst_error(line_number, args[0], args, num_args); errored++; } else { byte_offset += 4; } } // Repeat until no more characters are left, and the return the correct return val line_number++; strtok(buf, "\n"); } if (errored != 0) { //my own: errors... in the output function? return -1; } else { //my own:if there are no errors return 0; } }
int pass_two(FILE *input, FILE* output, SymbolTable* symtbl, SymbolTable* reltbl) { /* YOUR CODE HERE */ // Since we pass this buffer to strtok(), the chars here will GET CLOBBERED. char buf[BUF_SIZE]; // Store input line number / byte offset below. When should each be incremented? // First, read the next line into a buffer. // Next, use strtok() to scan for next character. If there's nothing, // go to the next line. // Parse for instruction arguments. You should use strtok() to tokenize // the rest of the line. Extra arguments should be filtered out in pass_one(), // so you don't need to worry about that here. int err = 0; // Use translate_inst() to translate the instruction and write to output file. // If an error occurs, the instruction will not be written and you should call // raise_inst_error(). // Repeat until no more characters are left, and the return the correct return val uint32_t line_no = 0; uint32_t addr = 0; while(fgets(buf, BUF_SIZE, input) != NULL) { line_no++; if(strlen(buf) == 0) continue; char* args[MAX_ARGS]; int num_args = 0; char instr[BUF_SIZE]; // instruction string instr[0] = '\0'; char * pch; pch = strtok (buf, IGNORE_CHARS); if(pch != NULL) { strcpy(instr, pch); // first is instruction string pch = strtok (NULL, IGNORE_CHARS); while (pch != NULL) { // following is arguments args[num_args++] = strdup(pch); pch = strtok (NULL, IGNORE_CHARS); } #if 0 printf("instr: %s\n", instr); printf("num_args: %d\n", num_args); printf("args:\n"); for(int i = 0; i < num_args; i++) printf("\t %s", args[i]); printf("addr: %d\n", addr); #endif if(translate_inst(output, instr, args, num_args, addr, symtbl, reltbl) == -1) { raise_inst_error(line_no, instr, args, num_args); err = -1; } else { addr += 4; } } else { raise_inst_error(line_no, instr, args, num_args); err = -1; } // free memeory in args for(int i = 0; i < num_args; i++) free(args[i]); } return err; }
/* First pass of the assembler. You should implement pass_two() first. This function should read each line, strip all comments, scan for labels, and pass instructions to write_pass_one(). The input file may or may not be valid. Here are some guidelines: 1. Only one label may be present per line. It must be the first token present. Once you see a label, regardless of whether it is a valid label or invalid label, treat the NEXT token as the beginning of an instruction. 2. If the first token is not a label, treat it as the name of an instruction. 3. Everything after the instruction name should be treated as arguments to that instruction. If there are more than MAX_ARGS arguments, call raise_extra_arg_error() and pass in the first extra argument. Do not write that instruction to the output file (eg. don't call write_pass_one()) 4. Only one instruction should be present per line. You do not need to do anything extra to detect this - it should be handled by guideline 3. 5. A line containing only a label is valid. The address of the label should be the byte offset of the next instruction, regardless of whether there is a next instruction or not. Just like in pass_two(), if the function encounters an error it should NOT exit, but process the entire file and return -1. If no errors were encountered, it should return 0. */ int pass_one(FILE* input, FILE* output, SymbolTable* symtbl) { /* YOUR CODE HERE */ int err = 0; char buf[BUF_SIZE]; uint32_t line_no = 0; uint32_t addr = 0; // label addr while(fgets(buf, BUF_SIZE, input) != NULL) { line_no++; // strip comments skip_comment(buf); if(strlen(buf) == 0) continue; char* args[MAX_ARGS]; int num_args = 0; char nbuf[BUF_SIZE]; // buf for line with no label char instr[BUF_SIZE]; // instruction string char label[BUF_SIZE]; // label string instr[0] = '\0'; label[0] = '\0'; strcpy(nbuf, buf); char * pch; pch = strtok (buf, IGNORE_CHARS); // buf is clobbered if(pch == NULL) continue; strcpy(label, pch); // first pch maybe label int res = add_if_label(line_no, label, addr, symtbl); printf("Label1: %s\n", label); if( res == 0) { label[0] = '\0'; } else { if(res == -1) { err = -2; raise_label_error(line_no, label); } char* tmp = strstr(nbuf, label); strcpy(nbuf, tmp + strlen(label) + 1); printf("Label2: %s\n", label); } printf("nbuf (for instruction) : %s\n", nbuf); pch = strtok (nbuf, IGNORE_CHARS); if(pch != NULL) { strcpy(instr, pch); // first is instruction string pch = strtok (NULL, IGNORE_CHARS); while (pch != NULL) { // following is arguments args[num_args++] = strdup(pch); pch = strtok (NULL, IGNORE_CHARS); if(num_args > MAX_ARGS ) { raise_extra_arg_error(line_no, args[num_args]); err = -3; break; } } #if 0 printf("instr: %s\n", instr); printf("num_args: %d\n", num_args); printf("args:\n"); for(int i = 0; i < num_args; i++) printf("\t %s", args[i]); printf("addr: %d\n", addr); #endif if(err != -3) { int num_instr = write_pass_one(output, instr, args, num_args); addr += 4 * num_instr; printf("addr: %d\n", addr); if( num_instr == 0) { raise_inst_error(line_no, instr, args, num_args); err = -1; } } // free memeory in args for(int i = 0; i < num_args; i++) free(args[i]); } } return err < 0 ? -1 : 0; }