duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

commit acdd1856e9e051422946a7d675dd5b727cc1cece
parent b11bf44c2de78b2b425840a0dc9203420aaf4b07
Author: Virgil Dupras <hsoft@hardcoded.net>
Date:   Wed,  3 Aug 2022 10:14:06 -0400

Dusk CVM: first steps

Diffstat:
M.gitignore | 1+
MMakefile | 3+++
Aposix/vm.c | 580+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 584 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,5 @@ /dusk +/duskvm /fatfs /boot.fs /fs/init.fs diff --git a/Makefile b/Makefile @@ -12,6 +12,9 @@ dusk: dusk.asm boot.fs fatfs nasm -f elf32 dusk.asm -o dusk.o ld -m elf_i386 dusk.o -o $@ +duskvm: posix/vm.c + $(CC) posix/vm.c -Wall -o $@ + boot.fs: $(BOOTFS_SRC) cat $(BOOTFS_SRC) > $@ diff --git a/posix/vm.c b/posix/vm.c @@ -0,0 +1,580 @@ +/* VM for bootstrapping Dusk from POSIX platforms + +This is a Forth VM that has the goal of being able to bootstrap Dusk from any +POSIX platform. It is limited compared to bare-metal builds because it cannot +run CC, but it still has the ability to load assemblers and thus create bare +metal builds. + +The VM is little endian. +*/ +#include <inttypes.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#define MEMSZ 0x100000 // 1MB +#define STACKSZ 0x800 +#define RSTOP MEMSZ +#define PSTOP RSTOP-STACKSZ +#define SYSVARSSZ 0x80 +#define SYSVARS (PSTOP-STACKSZ)-SYSVARSSZ +#define HERE SYSVARS +#define CURRENT HERE+4 +#define COMPILING CURRENT+4 +#define CURWORD COMPILING+4 +#define HEREMAX SYSVARS + + +typedef uint8_t byte; +typedef uint16_t word; +typedef uint32_t dword; + +struct VM { + dword PSP; + dword RSP; + dword PC; // when PC >= MEMSZ, machine is halted + dword toptr; + dword bootptr; + dword areg; + byte mem[MEMSZ]; +}; + +// Various labels set during dict building phase +static dword lblmain = 0; + +static struct VM vm = {0}; + +// Utilities +static byte gb(dword addr) { return vm.mem[addr]; } +static word gw(dword addr) { return gb(addr) | (gb(addr+1)<<8); } +static dword gd(dword addr) { return gw(addr) | (gw(addr+2)<<16); } +static dword gpc() { dword n = gd(vm.PC); vm.PC += 4; return n; } +static void sb(dword addr, byte b) { vm.mem[addr] = b; } +static void sw(dword addr, word w) { sb(addr, w); sb(addr+1, w>>8); } +static void sd(dword addr, dword d) { sw(addr, d); sw(addr+1, d>>16); } +static dword ppeek() { return gd(vm.PSP); } +static dword ppop() { dword n = ppeek(); vm.PSP += 4; return n; } +static void ppush(dword d) { vm.PSP -= 4; sd(vm.PSP, d); } +static dword rpeek() { return gd(vm.RSP); } +static dword rpop() { dword n = rpeek(); vm.RSP += 4; return n; } +static void rpush(dword d) { vm.RSP -= 4; sd(vm.RSP, d); } +static dword here() { return gd(HERE); } +static void allot(dword n) { sd(HERE, here()+n); } +static dword current() { return gd(CURRENT); } +static dword find(char* name) { + byte slen = strlen(name); + dword a = current(); + byte len; + while (a) { + len = gb(a-1) & 0x3f; + if ((len == slen) && (memcmp(name, &vm.mem[a-5-len], len)==0)) { + return a; + } + a = gd(a-5); + } + return 0; +} + +// Operations +/* The VM works in a simple manner. PC starts at 0. The VM reads the byte +where PC points to in memory, increases PC by 1 and executes the corresponding +function in the function list below. As simple as this. */ + +static void BR() { // op: 00 + vm.PC = gpc(); +} + +static void CALL() { // op: 01 + dword n = gpc(); + rpush(vm.PC); + vm.PC = n; +} + +static void RET() { // op: 02 + vm.PC = rpop(); +} + +static void LIT() { // op: 03 + dword n = gpc(); + ppush(n); + // ppush(gpc()); +} + +static void BYE() { // op: 04 + vm.PC = MEMSZ; +} + +static void BYEFAIL() { // op: 05 + vm.PC = MEMSZ + 1; +} + +static void QUIT() { // op: 06 + vm.toptr = 0; + vm.RSP = RSTOP; + vm.PC = lblmain; +} + +static void ABORT() { // op: 07 + vm.PSP = PSTOP; + QUIT(); +} + +static void EXECUTE() { // op: 08 + vm.PC = ppop(); +} + +static void CELL() { // op: 09 + ppush(rpop()); +} + +static void VAL() { // op: 0a + dword a = rpop(); + if (vm.toptr) { + ppush(a); + vm.PC = vm.toptr; + vm.toptr = 0; + } else { + ppush(gd(a)); + } +} + +static void ALIAS() { // op: 0b + dword a = rpop(); + if (vm.toptr) { + ppush(a); + vm.PC = vm.toptr; + vm.toptr = 0; + } else { + vm.PC = gd(a); + } +} + +static void DOES() { // op: 0c + dword a = rpop(); + ppush(a+4); + vm.PC = gd(a); +} + +static void SLIT() { // op: 0d + dword a = rpop(); + ppush(a+1); + ppush(gd(a)); +} + +static void CBR() { // op: 0e + if (ppop()) { + vm.PC += 4; + } else { + BR(); + } +} + +static void NEXT() { // op: 0f + dword n = rpop(); + if (--n) { + rpush(n); + BR(); + } else { + vm.PC += 4; + } +} + +static void BOOTRD() { // op: 10 + ppush(gb(vm.bootptr++)); +} + +static void EMIT() { // op: 11 + putc(ppop(), stdout); +} + +static void STDERR() { // op: 12 + putc(ppop(), stderr); +} + +static void KEY() { // op: 13 + ppush(getc(stdin)); +} + +static void DROP() { // op: 14 + ppop(); +} + +static void DUP() { // op: 15 + ppush(ppeek()); +} + +static void CDUP() { // op: 16 + if (ppeek()) { DUP(); } +} + +static void SWAP() { // op: 17 + dword a = ppop(); + dword b = ppop(); + ppush(a); ppush(b); +} + +static void OVER() { // op: 18 + dword a = gd(vm.PSP+4); + ppush(a); +} + +static void ROT() { // op: 19 + dword a = ppop(); + dword b = ppop(); + dword c = ppop(); + ppush(b); ppush(a); ppush(c); +} + +static void ROTR() { // op: 1a + dword a = ppop(); + dword b = ppop(); + dword c = ppop(); + ppush(a); ppush(c); ppush(b); +} + +static void NIP() { // op: 1b + dword n = ppop(); + sd(vm.PSP, n); +} + +static void TUCK() { // op: 1c + dword a = ppop(); + dword b = ppop(); + ppush(a); ppush(b); ppush(a); +} + +/* Warning: RS routines are all called, which means that we have to work from +the second item from the top rather than the first. */ + +static void RS2PS() { // op: 1d + dword a = rpop(); + ppush(rpop()); + rpush(a); +} + +static void PS2RS() { // op: 1e + dword a = rpop(); + rpush(ppop()); + rpush(a); +} + +static void RSGET() { // op: 1f + ppush(gd(vm.RSP+4)); +} + +static void RDROP() { // op: 20 + dword a = rpop(); + ppop(); + rpush(a); +} + +static void SCNT() { // op: 21 + ppush((PSTOP-vm.PSP)>>2); +} + +static void RCNT() { // op: 22 + ppush(((RSTOP-vm.RSP)>>2)-1); +} + +static void ASET() { // op: 23 + vm.areg = ppop(); +} + +static void AGET() { // op: 24 + ppush(vm.areg); +} + +static void ACFETCH() { // op: 25 + ppush(gb(vm.areg)); +} + +static void ACSTORE() { // op: 26 + sb(vm.areg, ppop()); +} + +static void AINC() { // op: 27 + vm.areg++; +} + +static void ADEC() { // op: 28 + vm.areg--; +} + +static void A2RS() { // op: 29 + rpush(vm.areg); +} + +static void RS2A() { // op: 2a + vm.areg = rpop(); +} + +static void TOSET() { // op: 2b + vm.toptr = ppop(); +} + +static void TOGET() { // op: 2c + ppush(vm.toptr); + vm.toptr = 0; +} + +static void INC() { // op: 2d + ppush(ppop()+1); +} + +static void DEC() { // op: 2e + ppush(ppop()-1); +} + +static void CFETCH() { // op: 2f + ppush(gb(ppop())); +} + +static void CSTORE() { // op: 30 + dword a = ppop(); + sb(a, ppop()); +} + +static void CWRITE() { // op: 31 + sb(here(), ppop()); + allot(1); +} + +static void WFETCH() { // op: 32 + ppush(gw(ppop())); +} + +static void WSTORE() { // op: 33 + dword a = ppop(); + sw(a, ppop()); +} + +static void FETCH() { // op: 34 + ppush(gd(ppop())); +} + +static void STORE() { // op: 35 + dword a = ppop(); + sd(a, ppop()); +} + +static void ADDSTORE() { // op: 36 + dword a = ppop(); + sd(a, gd(a)+ppop()); +} + +static void WRITE() { // op: 37 + sd(here(), ppop()); + allot(4); +} + +static void ADD() { // op: 38 + dword n = ppop(); + ppush(ppop()+n); +} + +static void SUB() { // op: 39 + dword n = ppop(); + ppush(ppop()-n); +} + +static void MUL() { // op: 3a + dword n = ppop(); + ppush(ppop()*n); +} + +// ( a b -- r q ) +static void DIVMOD() { // op: 3b + dword a = ppop(); + dword b = ppop(); + ppush(a%b); + ppush(a/b); +} + +static void AND() { // op: 3c + dword n = ppop(); + ppush(ppop()&n); +} + +static void OR() { // op: 3d + dword n = ppop(); + ppush(ppop()|n); +} + +static void XOR() { // op: 3e + dword n = ppop(); + ppush(ppop()^n); +} + +static void BOOL() { // op: 3f + if (ppop()) { ppush(1); } else { ppush(0); } +} + +static void NOT() { // op: 40 + if (ppop()) { ppush(0); } else { ppush(1); } +} + +static void LT() { // op: 41 + dword n = ppop(); + if (ppop()<n) { ppush(1); } else { ppush(0); } +} + +static void SHLC() { // op: 42 + dword n = ppop(); + ppush(n<<1); + ppush(n>>31); +} + +static void SHRC() { // op: 43 + dword n = ppop(); + ppush(n>>1); + ppush(n&1); +} + +// ( n u -- n ) +static void LSHIFT() { // op: 44 + dword u = ppop(); + dword n = ppop(); + ppush(n<<u); +} + +static void RSHIFT() { // op: 45 + dword u = ppop(); + dword n = ppop(); + ppush(n>>u); +} + +static void LITN() { // op: 46 + ppush(0x03); // LIT + CWRITE(); + WRITE(); +} + +static void EXECUTEWR() { // op: 47 + ppush(0x01); // CALL + CWRITE(); + WRITE(); +} + +static void EXITWR() { // op: 48 + ppush(0x02); // RET + CWRITE(); +} + +static void MOVE() { // op: 49 + dword u = ppop(); + dword dst = ppop(); + dword src = ppop(); + memcpy(&vm.mem[dst], &vm.mem[src], u); +} + +static void MOVEWR() { // op: 4a + dword u = ppop(); + ppush(here()); + allot(u); + ppush(u); + MOVE(); +} + +static void RTYPE() { // op: 4b + dword u = ppop(); + dword a = ppop(); + write(STDOUT_FILENO, &vm.mem[a], u); +} + +static void WNF() { // op: 4c + write(STDOUT_FILENO, &vm.mem[CURWORD+1], vm.mem[CURWORD]); + printf(" word not found"); + ABORT(); +} + +static void STACKCHK() { // op: 4d + if (vm.PSP > PSTOP) { + printf("stack underflow"); + ABORT(); + } +} + +#define OPCNT 0x4e +static void (*ops[OPCNT])() = { + BR, CALL, RET, LIT, BYE, BYEFAIL, QUIT, ABORT, + EXECUTE, CELL, VAL, ALIAS, DOES, SLIT, CBR, NEXT, + BOOTRD, EMIT, STDERR, KEY, DROP, DUP, CDUP, + SWAP, OVER, ROT, ROTR, NIP, TUCK, RS2PS, PS2RS, RSGET, + RDROP, SCNT, RCNT, ASET, AGET, ACFETCH, ACSTORE, AINC, + ADEC, A2RS, RS2A, TOSET, TOGET, INC, DEC, CFETCH, + CSTORE, CWRITE, WFETCH, WSTORE, FETCH, STORE, ADDSTORE, WRITE, + ADD, SUB, MUL, DIVMOD, AND, OR, XOR, BOOL, + NOT, LT, SHLC, SHRC, LSHIFT, RSHIFT, LITN, EXECUTEWR, + EXITWR, MOVE, MOVEWR, RTYPE, WNF, STACKCHK}; + +// Dictionary building +static void litwr(dword n) { + ppush(n); + LITN(); +} + +static void opwr(byte opcode) { + ppush(opcode); + CWRITE(); +} + +static void retwr() { opwr(0x02); } + +static void callwr(dword a) { + opwr(0x01); + ppush(a); + WRITE(); +} + +static void entry(char *name) { + dword len = strlen(name); + memcpy(&vm.mem[here()], name, len); + allot(len); + ppush(current()); + WRITE(); + ppush(len); + CWRITE(); + sd(CURRENT, here()); +} + +static void buildsysdict() { + sd(HERE, 0); + sd(CURRENT, 0); + sd(COMPILING, 0); + opwr(0x00); // BR + allot(4); // forward jump + entry("bye"); opwr(0x04); + entry("byefail"); opwr(0x05); + entry("noop"); retwr(); + entry("quit"); opwr(0x06); + entry("abort"); opwr(0x07); + entry("(emit)"); opwr(0x12); retwr(); + lblmain = here(); + sd(0x01, lblmain); + litwr('X'); + callwr(find("(emit)")); + opwr(0x04); // BYE +} + +// Interpret loop +int main() { + byte opcode; + vm.PC = 0; + vm.PSP = PSTOP; + vm.RSP = RSTOP; + buildsysdict(); + /*for (int i=0; i<here(); i++) { + printf("%02x ", vm.mem[i]); + } + putc('\n', stdout); */ + while (vm.PC < MEMSZ) { + opcode = vm.mem[vm.PC++]; + if (opcode >= OPCNT) { + printf("Illegal opcode %02x at PC %08x\n", opcode, vm.PC-1); + BYEFAIL(); + } else { + ops[opcode](); + } + } + return MEMSZ - vm.PC; +}