|
7 | 7 | #include "string.h" |
8 | 8 |
|
9 | 9 | size_t execute(WORD* memory, size_t length, WORD* data_stack, WORD* return_stack) { |
| 10 | + // Declare and initialize memory pointer "registers" |
10 | 11 | ADDRESS dsp, rsp, pc, bfp, fmp; |
11 | 12 | dsp = 0; |
12 | 13 | rsp = 0; |
13 | 14 | pc = 0; |
14 | 15 | bfp = length; |
15 | 16 | fmp = length + BUFFER_SIZE; |
16 | 17 |
|
| 18 | + // Declare some temporary "registers" for working with intermediate values |
17 | 19 | ADDRESS atemp; |
18 | 20 | WORD temp; |
19 | 21 | WORDU utemp; |
20 | 22 | DWORD dtemp; |
21 | 23 | DWORDU udtemp; |
22 | 24 |
|
| 25 | + // Run the program in memory |
23 | 26 | bool run = true; |
24 | 27 | while (run) { |
| 28 | + // Print stack, op code, and pc before every execution |
25 | 29 | if (DEBUGGING) { |
26 | 30 | fprintf(stderr, "Stack: "); |
27 | 31 | display_range(data_stack, 0x0001, dsp + 1, DEBUGGING); |
28 | 32 | fprintf(stderr, "OP: %hx (%hu)\nPC: %hx (%hu)\n\n", |
29 | 33 | memory[pc], memory[pc], pc, pc); |
30 | 34 | } |
31 | 35 |
|
| 36 | + // Catch some common pointer/address errors |
32 | 37 | if (pc >= bfp) { |
33 | 38 | fprintf(stderr, |
34 | 39 | "Error: program counter out of bounds, pc: %hx, bfp: %hx\n", |
35 | 40 | pc, bfp); |
36 | | - exit (EXIT_POB); |
| 41 | + return EXIT_POB; |
37 | 42 | } else if (dsp > 0x8000) { // i.e. it has wrapped around into negatives |
38 | 43 | fprintf(stderr, "Error: stack underflow, sp: %hx (%hd)\n", dsp, dsp); |
39 | | - exit (EXIT_SUF); |
| 44 | + return EXIT_SUF; |
40 | 45 | } else if (dsp > END_STACK) { |
41 | 46 | fprintf(stderr, "Error: stack overflow, sp: %hx (%hd)\n", dsp, dsp); |
42 | | - exit (EXIT_SOF); |
| 47 | + return EXIT_SOF; |
43 | 48 | } else if (rsp > 0x8000) { // i.e. it has wrapped around into negatives |
44 | 49 | fprintf(stderr, "Error: return stack underflow, sp: %hx (%hd)\n", |
45 | 50 | rsp, rsp); |
46 | | - exit (EXIT_RSUF); |
| 51 | + return EXIT_RSUF; |
47 | 52 | } else if (rsp > END_RETURN) { |
48 | 53 | fprintf(stderr, "Error: return stack overflow, sp: %hx (%hd)\n", |
49 | 54 | rsp, rsp); |
50 | | - exit (EXIT_RSOF); |
| 55 | + return EXIT_RSOF; |
51 | 56 | } |
52 | 57 |
|
| 58 | + // Switch to cover each opcode. It is too long, but for simplicity and |
| 59 | + // efficiency it is kept this way, with larger operations calling |
| 60 | + // functions. The functions for things like unpacking and packing |
| 61 | + // double words are declared as inline so they will be more efficient. |
| 62 | + // Larger functions for things like io operations are regular functions |
| 63 | + // because they are not really hurt by the function call. |
53 | 64 | switch (memory[pc] & 0xff) { |
54 | 65 | case HALT: |
55 | 66 | run = false; |
@@ -649,12 +660,15 @@ size_t execute(WORD* memory, size_t length, WORD* data_stack, WORD* return_stack |
649 | 660 | /// BAD OP CODE /// |
650 | 661 | default: |
651 | 662 | fprintf(stderr, "Error: Unknown OP code: 0x%hx\n", memory[pc]); |
652 | | - exit(EXIT_OP); |
| 663 | + return EXIT_OP; |
653 | 664 | } |
654 | 665 | pc++; |
655 | 666 | } |
656 | 667 |
|
657 | | - // Test output |
| 668 | + // When program is run for tests we print out the contents of the stack |
| 669 | + // to stdout to check that the program ended in the expected state. |
| 670 | + // Because we always increment dsp before pushing a value the true start of |
| 671 | + // the stack is index 1 and not 0. |
658 | 672 | if (TESTING) { |
659 | 673 | display_range(data_stack, 0x0001, dsp + 1, false); |
660 | 674 | } |
|
0 commit comments