Skip to content

Commit 9938c7e

Browse files
committed
Added documentation and a few comments to the VM code.
1 parent eecb907 commit 9938c7e

File tree

7 files changed

+83
-21
lines changed

7 files changed

+83
-21
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ Pop the top two stack elements and push the result onto the stack. **multu** tre
221221

222222
All versions pop the top two stack elements and divide the top - 1 stack element by the top. This is integer division as done in C so values that would result in fractions are rounded toward `0`. That means `-1.23` will be `-1` **NOT** `-2`. Use the unsigned versions if the values are being treated as unsigned. For example `0x10 / 0xff(-1)` is `0xf0(-16)` when signed, but it is just `0x00` when unsigned as `0xff` is much larger than `0x10`.
223223

224-
### mod, dmod, dmod, dmodu
224+
### mod, dmod, modu, dmodu
225225

226226
Pops the top two stack elements and takes top - 1 stack element mod by the top.
227227

include/ltconst.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ extern const bool DEBUGGING;
1919
extern const ADDRESS END_MEMORY;
2020
extern const ADDRESS END_RETURN;
2121
extern const ADDRESS END_STACK;
22-
extern const WORD WORD_SIZE;
23-
extern const WORD BYTE_SIZE;
24-
extern const WORD BUFFER_SIZE;
25-
extern const WORD DEFAULT_SCALE;
26-
extern const WORDU SCALE_MAX;
27-
extern const DWORD SCALES[];
22+
23+
extern const WORDU WORD_SIZE; // number of bytes in a word
24+
extern const WORDU BYTE_SIZE; // number of bits in a byte
25+
extern const WORDU BUFFER_SIZE; // number of words in the buffer
26+
27+
extern const WORDU DEFAULT_SCALE; // index of default scale in scales
28+
extern const WORDU SCALE_MAX; // length of the scales array
29+
extern const DWORDU SCALES[]; // scaling factors i.e. 10, 100 ...
2830

2931
// Exit codes //
3032
extern const size_t EXIT_MEM;

include/ltio.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,42 @@
44
#include "ltconst.h"
55
#include "stddef.h"
66

7+
/* Read the bytes of the program from the given file into the given memory
8+
* array.
9+
* On success returns the length of the program in words. On errors prints a
10+
* message to stderr and returns 0.
11+
*/
712
size_t read_program(WORD* mem, const char* filename);
13+
14+
/* Display a range of words from memory as space separated hex values.
15+
* The range goes up to but not includeing end. I.e. [start, end).
16+
* If debug is true it prints the range to stderr instead of stdout.
17+
* I.e. display_range(memory, 1, 3);
18+
* (out) 00aa bbcc
19+
*/
820
void display_range(WORD* mem, ADDRESS start, ADDRESS end, bool debug);
21+
22+
/* Print a null terminated string from memory.
23+
* Assumes each word holds two characters, one in the top byte
24+
* and one in the bottom byte of a 16 bit word. I.e. If a word is 0x6566
25+
* then AB would be printed for that word.
26+
* The given max is the maximum number of words to traverse if a null byte is
27+
* not found. Null bytes can be in either position to stop the printing.
28+
*/
929
void print_string(WORD* mem, ADDRESS start, ADDRESS max);
30+
31+
/* Reads characters from stdin and stores them in memory starting from start.
32+
* Will read until a newline is discovered or the max number of characters
33+
* has been read. Every two characters will be packed into a word with the
34+
* first character read being in the words top byte. Always leaves at least one
35+
* null byte after the final read character. For an even number of characters
36+
* the next word is set to 0. For an uneven number of characters the top byte
37+
* of the last word is set to 0x00.
38+
*
39+
* TODO push a flag value onto the stack to indicate if max was reached before
40+
* all input characters were read. This way it is possible to know that more
41+
* input needs to be read.
42+
*/
1043
void read_string(WORD* mem, ADDRESS start, ADDRESS max);
1144

1245
#endif

include/ltrun.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
#include "ltconst.h"
55
#include "stddef.h"
66

7+
/* All op codes for the VM.
8+
* If new codes are added at this point they MUST be added to the end. The
9+
* VM will still run fine if they are rearanged, but the tests depend on the
10+
* current code assignment.
11+
*/
712
enum op_codes { HALT=0,
813
PUSH, POP, LOAD, STORE, // 04
914
FST, SEC, NTH, // 07
@@ -45,9 +50,16 @@ enum op_codes { HALT=0,
4550
FMULT, FDIV, FMULTSC, FDIVSC, // 63
4651
};
4752

53+
// Some additional codes to track the direction of memory copies
4854
enum copy_codes { MEM_BUF = 0, BUF_MEM };
4955

50-
56+
/* Runs a program starting at position 0 in the given program memory.
57+
* The length is the length of the program that has been loaded into memory
58+
* and is used to calculate the positions of buffer and free memory pointers.
59+
* The data and return stacks are expected to be empty. Any memory loaded into
60+
* them before running this function will be lost.
61+
* Returns an exit code based on the result of the program execution.
62+
*/
5163
size_t execute(WORD* memory, size_t length, WORD* data_stack, WORD* return_stack);
5264

5365
#endif

src/ltconst.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ const ADDRESS END_MEMORY = 0xffff;
2020
const ADDRESS END_RETURN = 0x1000;
2121
const ADDRESS END_STACK = 0x1000;
2222

23-
const WORD WORD_SIZE = 16;
24-
const WORD BYTE_SIZE = 8;
25-
const WORD BUFFER_SIZE = 0x0400;
23+
const WORDU WORD_SIZE = 16;
24+
const WORDU BYTE_SIZE = 8;
25+
const WORDU BUFFER_SIZE = 0x0400;
2626

27-
const WORD DEFAULT_SCALE = 3;
27+
const WORDU DEFAULT_SCALE = 3;
2828
const WORDU SCALE_MAX = 10;
29-
const DWORD SCALES[ 10 ] = {
29+
const DWORDU SCALES[ 10 ] = {
3030
1, 10, 100, 1000, 10000, 100000,
3131
1000000, 10000000, 100000000,
3232
1000000000

src/ltio.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ void read_string(WORD* mem, ADDRESS start, ADDRESS max) {
8181
}
8282
mem[atemp] = two_chars;
8383
mem[atemp + 1] = 0;
84-
break;
84+
return;
8585

8686
} else {
8787
if (first) {
@@ -94,5 +94,6 @@ void read_string(WORD* mem, ADDRESS start, ADDRESS max) {
9494
}
9595
}
9696
}
97+
mem[atemp + 1] = 0;
9798
}
9899

src/ltrun.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,60 @@
77
#include "string.h"
88

99
size_t execute(WORD* memory, size_t length, WORD* data_stack, WORD* return_stack) {
10+
// Declare and initialize memory pointer "registers"
1011
ADDRESS dsp, rsp, pc, bfp, fmp;
1112
dsp = 0;
1213
rsp = 0;
1314
pc = 0;
1415
bfp = length;
1516
fmp = length + BUFFER_SIZE;
1617

18+
// Declare some temporary "registers" for working with intermediate values
1719
ADDRESS atemp;
1820
WORD temp;
1921
WORDU utemp;
2022
DWORD dtemp;
2123
DWORDU udtemp;
2224

25+
// Run the program in memory
2326
bool run = true;
2427
while (run) {
28+
// Print stack, op code, and pc before every execution
2529
if (DEBUGGING) {
2630
fprintf(stderr, "Stack: ");
2731
display_range(data_stack, 0x0001, dsp + 1, DEBUGGING);
2832
fprintf(stderr, "OP: %hx (%hu)\nPC: %hx (%hu)\n\n",
2933
memory[pc], memory[pc], pc, pc);
3034
}
3135

36+
// Catch some common pointer/address errors
3237
if (pc >= bfp) {
3338
fprintf(stderr,
3439
"Error: program counter out of bounds, pc: %hx, bfp: %hx\n",
3540
pc, bfp);
36-
exit (EXIT_POB);
41+
return EXIT_POB;
3742
} else if (dsp > 0x8000) { // i.e. it has wrapped around into negatives
3843
fprintf(stderr, "Error: stack underflow, sp: %hx (%hd)\n", dsp, dsp);
39-
exit (EXIT_SUF);
44+
return EXIT_SUF;
4045
} else if (dsp > END_STACK) {
4146
fprintf(stderr, "Error: stack overflow, sp: %hx (%hd)\n", dsp, dsp);
42-
exit (EXIT_SOF);
47+
return EXIT_SOF;
4348
} else if (rsp > 0x8000) { // i.e. it has wrapped around into negatives
4449
fprintf(stderr, "Error: return stack underflow, sp: %hx (%hd)\n",
4550
rsp, rsp);
46-
exit (EXIT_RSUF);
51+
return EXIT_RSUF;
4752
} else if (rsp > END_RETURN) {
4853
fprintf(stderr, "Error: return stack overflow, sp: %hx (%hd)\n",
4954
rsp, rsp);
50-
exit (EXIT_RSOF);
55+
return EXIT_RSOF;
5156
}
5257

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.
5364
switch (memory[pc] & 0xff) {
5465
case HALT:
5566
run = false;
@@ -649,12 +660,15 @@ size_t execute(WORD* memory, size_t length, WORD* data_stack, WORD* return_stack
649660
/// BAD OP CODE ///
650661
default:
651662
fprintf(stderr, "Error: Unknown OP code: 0x%hx\n", memory[pc]);
652-
exit(EXIT_OP);
663+
return EXIT_OP;
653664
}
654665
pc++;
655666
}
656667

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.
658672
if (TESTING) {
659673
display_range(data_stack, 0x0001, dsp + 1, false);
660674
}

0 commit comments

Comments
 (0)