+1 (315) 557-6473 

Simulation of ARM Processor and Stack Operation Assignment Solution


ARM Processor Simulation

Your task for this lab is to write a program that extends the simulation of the ARM processor and includes stack operations.

To complete the ARM assignment program will have an array that represents the general registers, and a second multi-dimensional array that represents memory. The program must also have allocated stack space within the memory array.

The program will read content into the memory array from a file, execute the instructions it finds in the array one instruction at a time, and print the contents of the registers (including a program counter), and stack after each instruction. Note, when printing the stack do not print anything below the stack pointer, or above your stack base.

The program will implement the following instructions: ADD, ADDI, SUBI, LDUR, STUR, B, BL, BR. And at least on Conditional Branch instruction.

Instructions in memory will use the assembly mnemonics rather than binary op codes.

Instruction arguments will be register aliases (ex. X0, X1, X2, …) memory locations in the memory array (100, 104, …), or immediate values (#-3, #5, #0, …).

The program should use the specific address - 200, as the default address for the start instructions in memory.

Load and store instructions will only use indirect addressing (ie. [Xn, #nn] ).

Branch instructions will use immediate or register addressing.

The program can limit the number of registers available for use in the program. At least 8 must be available, including X0, X1, X9, X10, SP (X28), LR (X30). Additional registers may be implemented.

Data will be signed integers.

The Stack:

The stack must be at least 100 words deep. Make sure your stack will not grow over your program/data area.

The program must have a check and a “Ran out of Stack Space/Stack Overflow” error message.

Choose a high memory area for the stack.

The stack should grow down.

Stack memory locations should be on double word boundaries (increment/decrement by 8).

Remember to initiate the stack pointer (SP) to the top of the stack.

Use BR XZR to halt the program.

Test Files:

Students will create an input file to test their emulator. The program must be able to read the name of the file as an argument

The instructions in the file to be executed must include:

At least one example of each instruction implemented.

At least 12 instructions.

Instructions to complete at least one loop.

At least one push and pop on the stack.

At least one function call.

Output

The output of the program will be a listing showing:

The current instruction

The values of any registers used in that instruction. Including the Program Counter.

The portion of the stack which is in use – (note: this may be limited to the current stack frame).

Additional information, such as the value of all registers, or other useful items may be shown as well.

SAMPLE OUTPUT

Instruction executed: LDUR X0, XZR, #100

Registers: X0 = 512, SP=1000, PC=204*

Stack: 1000: 98 ***

Hit enter for next instruction: >

Instruction executed: SUBI SP, SP, #8

Registers: SP=992, PC=208

Stack: 1000: 98

                992: **

Hit enter for next instruction:>

Instruction executed: STUR X0, [SP, #0]

Registers: X0 = 512, SP=992, PC=212

Stack: 1000: 4A

                992: 512

Hit enter for next instruction: >

Solution:

#include #include #include #define MEMORY_SIZE 1024 /* by default memory has size 1024 positions */ #define STACK_SIZE 256 /* size of stack */ #define FIELD_LEN 10 /* maximum length for an instruction field string */ #define MAX_LINE_LEN 100 /* maximum length in a line */ /* definition of the register numbers */ enum { X0 = 0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, SP = 28, FP, LR, XZR }; /** * Returns the last character in the string */ char lastchar(char *str) { return (str[strlen(str) - 1]); } /** * Returns 1 if the given character represents a decimal digit, 0 otherwise */ int is_digit(char c) { return (c >= '0' && c <='9'); } /** * Returns 1 if the given character represents a whitespace char, 0 otherwise */ int is_space(char c) { return (c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\v' || c == '\f'); } /** * Copies the string str2 to str1 removing the first char if fst=1 and * removing the last char is lst = 1 */ void copytrim(char *str1, char *str2, int fst, int lst) { if (fst == 1) str2++; strcpy(str1, str2); if (lst == 1) str1[strlen(str1) - 1] = 0; } /** * Check if a string represents a valid int */ int valid_int(char *str) { int i = 0; if (str[0] == '-' || str[0] == '+') i++; for (; i < strlen(str); i++) if (!is_digit(str[i])) /* if not a digit, is not a number */ return 0; return (1); } /** * Split the instruction line by spaces, save the instruction in memory */ int save_instruction(char *inst,char mem[MEMORY_SIZE][FIELD_LEN], int nline) { char *ptr; int nparts; int address; ptr = strtok(inst, " "); /* split string using spaces */ if (ptr == NULL) /* if can't split */ { fprintf(stderr, "Error: line %d: invalid instruction format\n", nline + 1); return (-1); /* return error */ } /* try to get the address */ if (sscanf(ptr, "%d", &address) != 1 || address <0 || address >= MEMORY_SIZE || (address & 3)) /* if error or invalid address */ { fprintf(stderr, "Error: line %d: invalid address\n", nline + 1); return (-1); /* return error */ } ptr = strtok(NULL, " "); /* split next part using spaces */ nparts = 0; /* no parts initially */ while (ptr != NULL) { if (nparts >= 4) /* if too many parts */ { fprintf(stderr, "Error: line %d: invalid instruction format\n", nline + 1); return (-1); } if (strlen(ptr) > FIELD_LEN) /* if field is too long */ { fprintf(stderr, "Error: line %d: instruction field %d is too long\n", nline + 1, nparts + 1); return (-1); } strcpy(mem[address + nparts], ptr); /* save field in memory */ nparts++; /* we have one more part */ ptr = strtok(NULL, " "); /* split next part of string using spaces */ } if (nparts == 0) { fprintf(stderr, "Error: line %d: invalid instruction format\n", nline + 1); return (-1); } if (address < 200) /*if data address*/ { /* check for valid number */ if (!valid_int(mem[address])) { fprintf(stderr, "Error: line %d: invalid data\n", nline + 1); return (-1); } } return (0); } /** * Load program code from a file and saves it in memory in the addresses given * as the first field in the line */ void load_program(char *filename, char mem[MEMORY_SIZE][FIELD_LEN]) { FILE *f; char buffer[MAX_LINE_LEN]; int nline; f = fopen(filename, "rt"); /* open file for reading */ if (f == NULL) /* error opening */ { fprintf(stderr, "Error: unable to open file \"%s\"\n", filename); exit(EXIT_FAILURE); } printf("Loading program from \"%s\"\n", filename); /* read all lines in the file */ nline = 0; /* start at line 0 */ while (fgets(buffer, MAX_LINE_LEN, f) != NULL) { /* remove ending spaces and newline */ while (is_space(lastchar(buffer))) buffer[strlen(buffer) - 1] = 0; /* skip empty lines */ if (buffer[0] != 0) { /* save instruction in memory */ if (save_instruction(buffer, mem, nline) == -1) /* if error */ { fclose(f); /* close file */ exit(EXIT_FAILURE); } } nline++; /* advance line number */ } fclose(f); /* close the file */ } /** * Shows the contents of all changed registers */ void print_registers(int *regs, int *affected, int PC) { int i; int first; printf("Registers:\t"); first = 1; for (i = 0; i < 31; i++) { if (affected[i]) { if (!first) printf(", "); switch (i) { case SP: printf("SP"); break; case FP: printf("FP"); break; case LR: printf("LR"); break; default: printf("X%d", i); } printf("=%d",regs[i]); first = 0; } } if (!first) printf(", "); printf("PC=%d\n", PC); } /** * Prints the stack contents */ void print_stack(int sp, char memory[MEMORY_SIZE][FIELD_LEN], int memused[MEMORY_SIZE]) { int addr; int first; printf("Stack:\t"); first = 1; for (addr = MEMORY_SIZE; addr >= sp; addr-= 8) { if (!first) printf(" \t"); printf("%6d: ", addr); if (memused[addr]) printf("%d", atoi(memory[addr])); printf("\n"); first = 0; } if (first) printf("\n"); } /** * Prints the memory */ void print_memory(char memory[MEMORY_SIZE][FIELD_LEN], int memused[MEMORY_SIZE]) { int addr; int first; printf("Memory:\t"); first = 1; for (addr = 0; addr < MEMORY_SIZE - STACK_SIZE; addr += 4) { if (memused[addr]) { if (!first) printf(" \t"); printf("%6d: ", addr); printf("%d", atoi(memory[addr])); printf("\n"); first = 0; } } if (first) printf("\n"); } /** * Gets the register number from the string if its a valid register Xn * If not valid returns -1 */ int get_register(char *reg) { int nreg; if (!strcmp(reg, "SP")) nreg = SP; else if (!strcmp(reg, "FP")) nreg = FP; else if (!strcmp(reg, "LR")) nreg = LR; else if (!strcmp(reg, "XZR")) nreg = XZR; else if (sscanf(reg, "X%d", &nreg) != 1) return (-1); if (nreg < 0 || nreg > 31) return (-1); return (nreg); } /** * Simulates the program in memory starting at address 200 * */ void simulate_program(char memory[MEMORY_SIZE][FIELD_LEN]) { int PC; int regs[32]; /* registers */ int affected[32]; /* registers affected */ int flagZ; int memused[MEMORY_SIZE]; char *opcode, *op1, *op2, *op3; char t1[FIELD_LEN]; char t2[FIELD_LEN]; char t3[FIELD_LEN]; int i; int r1, r2, r3, r; int halt; PC = 200; /* start simulating at address 200 */ /* initialize registers */ for (i = 0; i < 32; i++) { regs[i] = 0; affected[i] = 0; } regs[SP] = MEMORY_SIZE; /* stack top at end of memory */ /* initialize memory used */ for (i = 0; i < MEMORY_SIZE; i++) { memused[i] = 0; } flagZ = 0; /* initialize flags */ printf("Starting simulation...\n"); halt = 0; while (!halt) /* repeat until halt is found */ { opcode = memory[PC]; /* fetch instruction */ op1 = memory[PC + 1]; /* fetch operands */ op2 = memory[PC + 2]; op3 = memory[PC + 3]; PC+=4; /* advance to next instruction */ /* clear affected registers */ for (i = 0; i < 31; i++) affected[i] = 0; if (!strcmp(opcode, "ADD") || !strcmp(opcode, "ADDS")) { if (lastchar(op1) != ',' || lastchar(op2) != ',') { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { copytrim(t1, op1, 0, 1); copytrim(t2, op2, 0, 1); r1 = get_register(t1); r2 = get_register(t2); r3 = get_register(op3); if (r1 == -1 || r2 == -1 || r3 == -1) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r = regs[r2] + regs[r3]; /* if S instruction, update flag */ if (lastchar(opcode) == 'S') flagZ = (r == 0); if (r1 != XZR) { regs[r1] = r; } affected[r1] = 1; affected[r2] = 1; affected[r3] = 1; } } } else if (!strcmp(opcode, "ADDI") || !strcmp(opcode, "ADDIS")) { if (lastchar(op1) != ',' || lastchar(op2) != ',' || op3[0] != '#') { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { copytrim(t1, op1, 0, 1); copytrim(t2, op2, 0, 1); copytrim(t3, op3, 1, 0); r1 = get_register(t1); r2 = get_register(t2); if (r1 == -1 || r2 == -1 || !valid_int(t3)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r3 = atoi(t3); r = regs[r2] + r3; /* if S instruction, update flag */ if (lastchar(opcode) == 'S') flagZ = (r == 0); if (r1 != XZR) regs[r1] = r; affected[r1] = 1; affected[r2] = 1; } } } else if (!strcmp(opcode, "SUBI") || !strcmp(opcode, "SUBIS")) { if (lastchar(op1) != ',' || lastchar(op2) != ',' || op3[0] != '#') { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { copytrim(t1, op1, 0, 1); copytrim(t2, op2, 0, 1); copytrim(t3, op3, 1, 0); r1 = get_register(t1); r2 = get_register(t2); if (r1 == -1 || r2 == -1 || !valid_int(t3)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r3 = atoi(t3); r = regs[r2] - r3; /* if S instruction, update flag */ if (lastchar(opcode) == 'S') flagZ = (r == 0); if (r1 != XZR) regs[r1] = r; affected[r1] = 1; affected[r2] = 1; } } } else if (!strcmp(opcode, "LDUR")) { if (lastchar(op1) != ',' || lastchar(op2) != ',' || op2[0] != '[' || op3[0] != '#' || lastchar(op3) != ']') { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { copytrim(t1, op1, 0, 1); copytrim(t2, op2, 1, 1); copytrim(t3, op3, 1, 1); r1 = get_register(t1); r2 = get_register(t2); if (r1 == -1 || r2 == -1 || !valid_int(t3)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r3 = atoi(t3); if (r2 == SP && (regs[r2] + r3 < MEMORY_SIZE - STACK_SIZE)) { printf("\n<< Stack overflow found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else if (r2 == SP && ((regs[r2] + r3) & 7) != 0) { printf("\n<< Misaligned stack access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else if (regs[r2] + r3 < 0 || regs[r2] + r3 >= MEMORY_SIZE || ((regs[r2] + r3) & 3) != 0 /* if not aligned */ || !valid_int(memory[regs[r2] + r3])) { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { memused[regs[r2] + r3] = 1; if (r1 != XZR) regs[r1] = atoi(memory[regs[r2] + r3]); affected[r1] = 1; affected[r2] = 1; } } } } else if (!strcmp(opcode, "STUR")) { if (lastchar(op1) != ',' || lastchar(op2) != ',' || op2[0] != '[' || op3[0] != '#' || lastchar(op3) != ']') { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { copytrim(t1, op1, 0, 1); copytrim(t2, op2, 1, 1); copytrim(t3, op3, 1, 1); r1 = get_register(t1); r2 = get_register(t2); if (r1 == -1 || r2 == -1 || !valid_int(t3)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r3 = atoi(t3); if (r2 == SP && (regs[r2] + r3 < MEMORY_SIZE - STACK_SIZE)) { printf("\n<< Stack overflow found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else if (r2 == SP && ((regs[r2] + r3) & 7) != 0) { printf("\n<< Misaligned stack access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else if (regs[r2] + r3 < 0 || regs[r2] + r3 >= MEMORY_SIZE || ((regs[r2] + r3) & 3) != 0 /* if not aligned */ || !valid_int(memory[regs[r2] + r3])) { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { memused[regs[r2] + r3] = 1; sprintf(memory[regs[r2] + r3], "%d", regs[r1]); affected[r1] = 1; affected[r2] = 1; } } } } else if (!strcmp(opcode, "B")) { if (!valid_int(op1)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r1 = atoi(op1); if (r1 < 0 || r1 >= MEMORY_SIZE || (r1 & 3) != 0) /* if not aligned */ { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { PC = r1; } } } else if (!strcmp(opcode, "BL")) { if (!valid_int(op1)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r1 = atoi(op1); if (r1 < 0 || r1 >= MEMORY_SIZE || (r1 & 3) != 0) /* if not aligned */ { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { regs[LR] = PC; PC = r1; affected[LR] = 1; } } } else if (!strcmp(opcode, "BR")) { r1 = get_register(op1); if (r1 == -1) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { if (regs[r1] < 0 || regs[r1] >= MEMORY_SIZE || (regs[r1] & 3) != 0) /* if not aligned */ { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { PC = regs[r1]; affected[r1] = 1; if (r1 == XZR) halt = 1; /* BR XZR halts the program */ } } } else if (!strcmp(opcode, "B.EQ")) { if (!valid_int(op1)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r1 = atoi(op1); if (r1 < 0 || r1 >= MEMORY_SIZE || (r1 & 3) != 0) /* if not aligned */ { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { if (flagZ) PC = r1; } } } else if (!strcmp(opcode, "B.NE")) { if (!valid_int(op1)) { printf("\n<< Invalid instruction operand at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { r1 = atoi(op1); if (r1 < 0 || r1 >= MEMORY_SIZE || (r1 & 3) != 0) /* if not aligned */ { printf("\n<< Invalid memory access found at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } else { if (!flagZ) PC = r1; } } } else /*if invalid opcode */ { printf("\n<< Invalid instruction at address %d >>\n", PC - 4); halt = -1; /* halt simulation */ } if (halt != -1) { printf("\nInstruction executed:\t%s %s %s %s\n",opcode, op1, op2, op3); print_registers(regs, affected, PC); print_stack(regs[SP], memory, memused); print_memory(memory, memused); printf("\nHit enter for next instruction: >"); fflush(stdout); getchar(); } } printf("\nEnd of simulation\n"); } int main(int argc, char **argv) { int i; char memory[MEMORY_SIZE][FIELD_LEN]; /* memory array */ /* by default use code.txt as the input file */ char *filename = "code.txt"; if (argc >= 2) /* if an input file was given */ filename = argv[1]; /* use it as the input filename */ /* clear memory */ for (i = 0; i < MEMORY_SIZE; i++) strcpy(memory[i], "0"); /* load program from file */ load_program(filename, memory); /* simulate the program in memory */ simulate_program(memory); return (EXIT_SUCCESS); }