1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-27 09:38:06 +11:00

cvm: improve comments

They were a bit terse.
This commit is contained in:
Virgil Dupras 2020-08-07 21:03:28 -04:00
parent fc7971d64f
commit 25f4312523
2 changed files with 28 additions and 11 deletions

View File

@ -21,6 +21,8 @@ static VM vm;
static uint64_t blkop = 0; // 5 bytes static uint64_t blkop = 0; // 5 bytes
static FILE *blkfp; static FILE *blkfp;
// Read single byte from I/O handler, if set. addr is a word only because of
// Forth's cell size, but can't actually address more than a byte-ful of ports.
static byte io_read(word addr) static byte io_read(word addr)
{ {
addr &= 0xff; addr &= 0xff;
@ -44,6 +46,9 @@ static void io_write(word addr, byte val)
} }
} }
// I/O hook to read/write a chunk of 1024 byte to blkfs at specified blkid.
// This is used by EFS@ and EFS! in xcomp.fs.
// See comment above BLK_PORT define for poking convention.
static void iowr_blk(byte val) static void iowr_blk(byte val)
{ {
blkop <<= 8; blkop <<= 8;
@ -62,29 +67,37 @@ static void iowr_blk(byte val)
} }
} }
// get/set word from/to memory
static word gw(word addr) { return vm.mem[addr+1] << 8 | vm.mem[addr]; } static word gw(word addr) { return vm.mem[addr+1] << 8 | vm.mem[addr]; }
static void sw(word addr, word val) { static void sw(word addr, word val) {
vm.mem[addr] = val; vm.mem[addr] = val;
vm.mem[addr+1] = val >> 8; vm.mem[addr+1] = val >> 8;
} }
// pop word from SP
static word pop() { static word pop() {
if (vm.uflw) return 0; if (vm.uflw) return 0;
if (vm.SP >= SP_ADDR) { vm.uflw = true; } if (vm.SP >= SP_ADDR) { vm.uflw = true; }
return vm.mem[vm.SP++] | vm.mem[vm.SP++] << 8; return vm.mem[vm.SP++] | vm.mem[vm.SP++] << 8;
} }
// push word to SP
static void push(word x) { static void push(word x) {
vm.SP -= 2; sw(vm.SP, x); vm.SP -= 2; sw(vm.SP, x);
if (vm.SP < vm.minSP) { vm.minSP = vm.SP; } if (vm.SP < vm.minSP) { vm.minSP = vm.SP; }
} }
// pop word from RS
static word popRS() { static word popRS() {
if (vm.uflw) return 0; if (vm.uflw) return 0;
if (vm.RS <= RS_ADDR) { vm.uflw = true; } if (vm.RS <= RS_ADDR) { vm.uflw = true; }
word x = gw(vm.RS); vm.RS -= 2; return x; word x = gw(vm.RS); vm.RS -= 2; return x;
} }
// push word to RS
static void pushRS(word val) { static void pushRS(word val) {
vm.RS += 2; sw(vm.RS, val); vm.RS += 2; sw(vm.RS, val);
if (vm.RS > vm.maxRS) { vm.maxRS = vm.RS; } if (vm.RS > vm.maxRS) { vm.maxRS = vm.RS; }
} }
// The functions below directly map to native forth words defined in the
// dictionary (B30)
static void execute(word wordref) { static void execute(word wordref) {
byte wtype = vm.mem[wordref]; byte wtype = vm.mem[wordref];
if (wtype == 0) { // native if (wtype == 0) { // native
@ -242,6 +255,7 @@ static void MINUS2() { push(pop()-2); }
static void PLUS2() { push(pop()+2); } static void PLUS2() { push(pop()+2); }
static void RSHIFT() { word u = pop(); push(pop()>>u); } static void RSHIFT() { word u = pop(); push(pop()>>u); }
static void LSHIFT() { word u = pop(); push(pop()<<u); } static void LSHIFT() { word u = pop(); push(pop()<<u); }
static void native(NativeWord func) { static void native(NativeWord func) {
vm.nativew[vm.nativew_count++] = func; vm.nativew[vm.nativew_count++] = func;
} }
@ -284,6 +298,7 @@ VM* VM_init() {
vm.iowr[i] = NULL; vm.iowr[i] = NULL;
} }
vm.iowr[BLK_PORT] = iowr_blk; vm.iowr[BLK_PORT] = iowr_blk;
// Added in the same order as in xcomp.fs
native(EXIT); native(EXIT);
native(_br_); native(_br_);
native(_cbr_); native(_cbr_);

View File

@ -14,28 +14,30 @@ typedef void (*NativeWord) ();
typedef byte (*IORD) (); typedef byte (*IORD) ();
typedef void (*IOWR) (byte data); typedef void (*IOWR) (byte data);
/* Native word placement
Being a C VM, all actual native code is outside the VM's memory. However,
we have a stable ABI to conform to. VM_init() configures the memory by
placing references to stable words at proper offsets, and then add all other
native words next to it. This will result in a "boot binary" that is much
more compact than a real Collapse OS memory layout.
*/
typedef struct { typedef struct {
byte mem[0x10000]; byte mem[0x10000];
word SP; word SP; // parameter Stack Pointer
word RS; word RS; // Return Stack pointer
word IP; word IP; // Interpreter Pointer
// A list of native words' code. This is filled in VM_init() by calls to
// native(). The order is very important because we refer to these elements
// by index. For example, "0x42 CODE FOO" in Forth creates the native word
// FOO which, when executed, will call the code at index 0x42 in this array.
NativeWord nativew[0x100]; NativeWord nativew[0x100];
byte nativew_count; byte nativew_count;
// Array of 0x100 function pointers to IO read and write routines. Leave to // Array of 0x100 function pointers to IO read and write routines. Leave to
// NULL when IO port is unhandled. // NULL when IO port is unhandled.
IORD iord[0x100]; IORD iord[0x100];
IOWR iowr[0x100]; IOWR iowr[0x100];
word xcurrent; // only used during native bootstrap // Used for keeping track of max RS and min SP during the lifetime of the
// program. Useful for debugging.
word maxRS; word maxRS;
word minSP; word minSP;
bool running; bool running;
// Whether we're in stack underflow situation. Alters the behavior of some
// core action, notably popping. Doesn't stay set for more than a single
// execute cycle. The goal is to avoid over-popping in native words that
// pop more than once and thus corrupt memory.
bool uflw; bool uflw;
} VM; } VM;