duskos

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

vm.c (29620B) - raw


      1 /* Dusk VM for POSIX platforms
      2 
      3 The VM has the same endianness as the host. This code was written on a little
      4 endian machine. It's very probably broken on big endian hosts, but in
      5 principle, it's only a matter of fixing kinks here and there.
      6 
      7 It also has a read-only file system and is pretty slow. It's enough, however,
      8 to generate native binary images for any supported target.
      9 
     10 HAL argument structure:
     11 b2:0   type  0=W 1=A 2=PSP 3=RSP 4=memory/immediate
     12 b3	   has disp? (type "memory" always has disp)
     13 b4     direct register mode (AREG, WREG)?
     14 b5     A dst?
     15 b6     invert?
     16 b7     unused
     17 b8	   16b?
     18 b9	   8b?
     19 b11:10 unused
     20 b15:12 number bank id
     21 */
     22 #include <inttypes.h>
     23 #include <stdio.h>
     24 #include <unistd.h>
     25 #include <string.h>
     26 #include <sys/stat.h>
     27 #include <fcntl.h>
     28 #include <libgen.h>
     29 #include <dirent.h>
     30 #include <stdlib.h>
     31 
     32 #define MEMSZ 0x1000000 // 16MB
     33 #define STACKSZ 0x800
     34 #define RSTOP MEMSZ
     35 #define PSTOP (RSTOP-STACKSZ)
     36 #define SYSVARSSZ 0x80
     37 #define SYSVARS ((PSTOP-STACKSZ)-SYSVARSSZ)
     38 #define HERE SYSVARS
     39 #define HEREMAX (HERE+4)
     40 #define SYSDICT (HEREMAX+12) // 12b to have a whole dict entry with null name
     41 #define NEXTMETA (SYSDICT+4)
     42 #define _RCNT_ (NEXTMETA+4)
     43 #define NEXTWORD (_RCNT_+4)
     44 #define MOD (NEXTWORD+4)
     45 #define CURWORD (MOD+4)
     46 #define HBANKCNT 0x10
     47 #define HBANKSZ (HBANKCNT*4)
     48 #define HBANK (SYSVARS-HBANKSZ)
     49 #define IOBUFSZ 0x200
     50 #define IOBUF (HBANK-IOBUFSZ)
     51 
     52 #define OPW 0
     53 #define OPA 1
     54 #define OPPSP 2
     55 #define OPRSP 3
     56 #define OPMEM 4
     57 #define OPHASDISP 0x08
     58 #define OPDEREF 0x10
     59 #define OPADEST 0x20
     60 #define OPINVERT 0x40
     61 #define OP16B 0x100
     62 #define OP8B 0x200
     63 // Condition structure: b6:0 condition ID b7 invert
     64 #define CONDZ 0x00
     65 #define CONDNZ 0x80
     66 #define CONDLT 0x01 // unsigned lesser than
     67 #define CONDNLT 0x81
     68 #define CONDGT 0x02
     69 #define CONDNGT 0x82
     70 #define CONDSLT 0x03 // signed
     71 #define CONDNSLT 0x83
     72 #define CONDSGT 0x04 // signed
     73 #define CONSNSGT 0x84
     74 #define EMETA_8B 0x10
     75 #define EMETA_16B 0x11
     76 
     77 typedef uint8_t byte;
     78 typedef uint16_t word;
     79 typedef uint32_t dword;
     80 
     81 struct VM {
     82 	dword PSP;
     83 	dword RSP;
     84 	dword PC; // when PC >= MEMSZ, machine is halted
     85 	dword W;  // W is PS top
     86 	dword A;
     87 	dword T; // tmp register. not directly addressable
     88 	dword mprotect; // halt when protected memory is changed
     89 	dword mprotectsz;
     90 	byte Z;
     91 	byte C;
     92 	byte SC; // signed C
     93 	byte compiling;
     94 	byte hbankidx;
     95 	byte mem[MEMSZ];
     96 };
     97 
     98 static struct VM vm = {0};
     99 static FILE *fp;
    100 
    101 // Addresses of sysaliases
    102 static dword abortaddr;
    103 static dword inrdaddr;
    104 static dword mainaddr;
    105 
    106 // Utilities
    107 static void vmabort() { vm.PC = MEMSZ + 1; }
    108 static dword memchk(dword a) {
    109 	if (a < MEMSZ) return a;
    110 	printf("Out of bounds memory access! Halting\n");
    111 	vmabort();
    112 	return 0;
    113 }
    114 static dword memchkmut(dword a) {
    115 	if ((a >= vm.mprotect) && (a < vm.mprotect + vm.mprotectsz)) {
    116 		printf("Access to protected memory! Halting\n");
    117 		vmabort();
    118 		return 0;
    119 	} else { return a; }
    120 }
    121 static byte gb(dword addr) { return vm.mem[memchk(addr)]; }
    122 static dword gd(dword addr) { return *(dword*)&vm.mem[memchk(addr)]; }
    123 static dword gpc() { dword n = gd(vm.PC); vm.PC += 4; return n; }
    124 static byte gpcb() { dword n = gb(vm.PC); vm.PC++; return n; }
    125 static void sb(dword addr, byte b) { vm.mem[memchkmut(addr)] = b; }
    126 static void sd(dword addr, dword d) {
    127 	dword *a = (dword*)&vm.mem[memchkmut(addr)]; *a = d; }
    128 static dword ppop() { dword n = vm.W; vm.W = gd(vm.PSP); vm.PSP += 4; return n; }
    129 static void ppush(dword n) { vm.PSP -= 4; sd(vm.PSP, vm.W); vm.W = n; }
    130 static dword rpeek() { return gd(vm.RSP); }
    131 static dword rpop() { dword n = rpeek(); vm.RSP += 4; return n; }
    132 static void rpush(dword d) { vm.RSP -= 4; sd(vm.RSP, d); }
    133 static dword here() { return gd(HERE); }
    134 static void allot(dword n) { sd(HERE, here()+n); }
    135 static dword sysdict() { return gd(SYSDICT); }
    136 static dword findmeta(dword id, dword ll) {
    137 	while (ll && gd(ll+4) != id) ll = gd(ll);
    138 	return ll;
    139 }
    140 static dword _find(dword dict, byte *name, byte slen) {
    141 	dword a = dict;
    142 	byte len;
    143 	while (memchk(a)) {
    144 		len = gb(a-5) & 0x3f;
    145 		if ((len == slen) && (memcmp(name, &vm.mem[a-5-len], len)==0)) {
    146 			return a+4;
    147 		}
    148 		a = gd(a);
    149 	}
    150 	return 0;
    151 }
    152 static dword find(char *name) {
    153 	return _find(sysdict(), (byte*)name, strlen(name));
    154 }
    155 static void cwrite(byte b) {
    156 	sb(here(), b);
    157 	allot(1);
    158 }
    159 static void dwrite(dword d) {
    160 	sd(here(), d);
    161 	allot(4);
    162 }
    163 static dword hbankaddr(byte idx) { return HBANK+((idx%HBANKCNT)*4); }
    164 static dword hbankget(dword operand) {
    165 	return operand&OPHASDISP ? gd(hbankaddr((operand >> 12)&0xf)) : 0; }
    166 static dword _hbankset(dword val) {
    167 	byte idx = (++vm.hbankidx) % HBANKCNT;
    168 	sd(hbankaddr(idx), val);
    169 	return idx;
    170 }
    171 static dword hbankset(dword operand, dword val) {
    172 	return (operand & 0xfff) | (_hbankset(val) << 12);
    173 }
    174 
    175 static void retwr() { cwrite(0x02); }
    176 static void psaddwr(dword n) { cwrite(0x08); dwrite(n); }
    177 static void wopwr(byte opcode, dword operand) {
    178 	opcode += (operand&(OP8B|OP16B)) >> 5;
    179 	cwrite(opcode); cwrite(operand);
    180 	if (operand&OPHASDISP) dwrite(hbankget(operand));
    181 }
    182 static void binopwr(byte binopidx, dword operand) {
    183 	byte opcode = 0x48 + ((operand&(OP8B|OP16B)) >> 8);
    184 	cwrite(opcode); cwrite(binopidx); cwrite(operand);
    185 	if (operand&OPHASDISP) dwrite(hbankget(operand));
    186 }
    187 static void wfetchwr(dword op) { wopwr(0x10, op); }
    188 static void wstorewr(dword op) { wopwr(0x10, op^OPINVERT); }
    189 static void addnwr(dword op, dword n) { wopwr(0x12, op); dwrite(n); }
    190 static void dupwr() { psaddwr(0xfffffffc); wstorewr(OPPSP); }
    191 static void nipwr() { psaddwr(4); }
    192 static void dropwr() { wfetchwr(OPPSP); nipwr(); }
    193 static void wlitwr(dword n) { wfetchwr(hbankset(OPHASDISP|OPMEM|OPDEREF, n)); }
    194 static void litwr(dword n) { dupwr(); wlitwr(n); }
    195 static void callwr(dword a) { cwrite(0x01); dwrite(a); }
    196 static void brwr(dword a) { cwrite(0x00); dwrite(a); }
    197 static void writewr() {
    198 	dword op = hbankset(OPHASDISP|OPMEM, HERE);
    199 	wfetchwr(op|OPADEST); addnwr(op, 4); wstorewr(OPA); dropwr(); }
    200 static void cwritewr() {
    201 	dword op = hbankset(OPHASDISP|OPMEM, HERE);
    202 	wfetchwr(op|OPADEST); addnwr(op, 1); wstorewr(OPA|OP8B); dropwr(); }
    203 static void compopwr(byte opcode) { litwr(opcode); cwrite(0x3f); }
    204 static void compbinopwr(byte binopidx) { litwr(binopidx); cwrite(0x45); }
    205 static void storewr() { wstorewr(OPA|OPDEREF); dropwr(); wstorewr(OPA); dropwr(); }
    206 
    207 static void callword(dword addr); // forward declaration
    208 static void _entry(dword dict, byte *name, byte slen) {
    209 	dword n = (here() + slen + 1) % 4;
    210 	if (n) { allot(4-n); }
    211 	memcpy(&vm.mem[here()], name, slen);
    212 	allot(slen);
    213 	cwrite(slen);
    214 	dwrite(gd(NEXTMETA));
    215 	dwrite(gd(dict));
    216 	sd(dict, here()-4);
    217 	sd(NEXTMETA, 0);
    218 }
    219 static void entry(char *name) {
    220 	_entry(SYSDICT, (byte*)name, strlen(name));
    221 }
    222 
    223 /* Operations */
    224 static dword *opsrcptr;
    225 static dword *opsrc;
    226 static dword *opdst;
    227 static dword opsz;
    228 static dword (*mget)(dword *a);
    229 static void (*mset)(dword *a, dword val);
    230 static dword mget32(dword *a) { return *a; }
    231 static dword mget16(dword *a) { return (dword)*(word *)a; }
    232 static dword mget8(dword *a) { return (dword)*(byte *)a; }
    233 static void mset32(dword *a, dword val) { *a = val; }
    234 static void mset16(dword *a, dword val) { *(word *)a = (word)val; }
    235 static void mset8(dword *a, dword val) { *(byte *)a = (byte)val; }
    236 #define M32B opsz = 4; mget = mget32; mset = mset32
    237 #define M16B opsz = 2; mget = mget16; mset = mset16
    238 #define M8B opsz = 1; mget = mget8; mset = mset8
    239 static dword def_opdget() { return *opdst; }
    240 static void def_opdset(dword val) { *opdst = val; }
    241 static dword def_opsget() { return mget(opsrc); }
    242 static void def_opsset(dword val) { mset(opsrc, val); }
    243 static dword (*opdget)();         // dst get
    244 static void (*opdset)(dword val); // dst set
    245 static dword (*opsget)();         // src get
    246 static void (*opsset)(dword val); // src set
    247 
    248 static void BR() { vm.PC = gpc(); } // 0x00
    249 static void CALL() { dword n = gpc(); rpush(vm.PC); vm.PC = n; }
    250 static void RET() { vm.PC = rpop(); }
    251 // ( a -- a )
    252 static void BRWR() { dwrite(vm.W); vm.W = here()-4; }
    253 static void BRA() { vm.PC = vm.A; }
    254 static int checkcond(byte cond) {
    255 	int r = 0;
    256 	switch (cond&0x7f) {
    257 		case CONDZ: r = vm.Z; break;
    258 		case CONDLT: r = vm.C; break;
    259 		case CONDGT: r = !vm.C && !vm.Z; break;
    260 		case CONDSLT: r = vm.SC; break;
    261 		case CONDSGT: r = !vm.SC && !vm.Z; break;
    262 	}
    263 	if (cond&0x80) r = !r;
    264 	return r;
    265 }
    266 static void BRC() { byte cond = gpcb(); dword a = gpc(); if (checkcond(cond)) vm.PC = a; }
    267 static void YIELD() { dword pc = vm.PC; vm.PC = rpop(); rpush(pc); }
    268 
    269 static void PSADD() { vm.PSP += gpc(); } // 0x08
    270 static void RSADD() { vm.RSP += gpc(); }
    271 
    272 static void readop() {
    273 	byte op = gpcb();
    274 	if (op & OPINVERT) {
    275 		opdget = def_opsget; opdset = def_opsset;
    276 		opsget = def_opdget; opsset = def_opdset;
    277 	} else {
    278 		opdget = def_opdget; opdset = def_opdset;
    279 		opsget = def_opsget; opsset = def_opsset;
    280 	}
    281 	opdst = op & OPADEST ? &vm.A : &vm.W;
    282 	switch (op & 0x7) {
    283 		case OPW: opsrcptr = &vm.W; break;
    284 		case OPA: opsrcptr = &vm.A; break;
    285 		case OPPSP: opsrcptr = &vm.PSP; break;
    286 		case OPRSP: opsrcptr = &vm.RSP; break;
    287 		case OPMEM: opsrcptr = (dword*)&vm.mem[vm.PC]; vm.PC += 4; break;
    288 		default:
    289 			printf("Invalid HAL operand %x\n", op);
    290 			vmabort();
    291 	}
    292 	if (op & OPDEREF) {
    293 		opsrc = opsrcptr;
    294 	} else {
    295 		opsrc = (dword*)&vm.mem[*opsrcptr];
    296 	}
    297 	if ((op & OPHASDISP) && ((op & 0x7) != OPMEM)) {
    298 		if (op & OPDEREF) {
    299 			vm.T = *opsrc + gpc(); opsrc = opsrcptr = &vm.T;
    300 		} else {
    301 			// why (int32_t)? because if it's negative, we want to subtract!
    302 			opsrc = (dword*)((byte*)opsrc + (int32_t)gpc());
    303 		}
    304 	}
    305 }
    306 static void _wfetch() { readop(); opdset(opsget()); }
    307 static void _wswap() { readop(); dword n; n = opsget(); opsset(opdget()); opdset(n); }
    308 static void _maddn() { readop(); dword n=gpc(); n += opsget(); opsset(n); vm.Z = n == 0; }
    309 static void _wcmp() {
    310 	readop(); dword n = opsget(); dword ref = opdget();
    311 	vm.Z = n == ref; vm.C = ref < n;
    312 	vm.SC = (ref+0x80000000) < (n+0x80000000); }
    313 static void _wfetchinc() { _wfetch(); *opsrcptr += opsz; }
    314 static void _wdecfetch() {
    315 	readop();
    316 	*opsrcptr -= opsz; opsrc = (dword*)((byte*)opsrc - opsz);
    317 	opdset(opsget()); }
    318 
    319 static void WFETCH() { M32B; _wfetch(); } // 0x10
    320 static void WSWAP() { M32B; _wswap(); }
    321 static void ADDN() { M32B; _maddn(); }
    322 static void WCMP() { M32B; _wcmp(); }
    323 static void WFETCHINC() { M32B; _wfetchinc(); }
    324 static void WDECFETCH() { M32B; _wdecfetch(); }
    325 
    326 static void WFETCH16() { M16B; _wfetch(); } // 0x18
    327 static void WSWAP16() { M16B; _wswap(); }
    328 static void ADDN16() { M16B; _maddn(); }
    329 static void WCMP16() { M16B; _wcmp(); }
    330 static void WFETCHINC16() { M16B; _wfetchinc(); }
    331 static void WDECFETCH16() { M16B; _wdecfetch(); }
    332 
    333 static void WFETCH8() { M8B; _wfetch(); } // 0x20
    334 static void WSWAP8() { M8B; _wswap(); }
    335 static void ADDN8() { M8B; _maddn(); }
    336 static void WCMP8() { M8B; _wcmp(); }
    337 static void WFETCHINC8() { M8B; _wfetchinc(); }
    338 static void WDECFETCH8() { M8B; _wdecfetch(); }
    339 
    340 // 0x28
    341 static void MOVE() {
    342 	dword u = ppop();
    343 	dword dst = ppop();
    344 	dword src = ppop();
    345 	if (u && memchk(dst+u) && memchk(src+u)) {
    346 		if (u && (dst >= src) && (dst < src+u)) {
    347 			fprintf(stderr, "overlapping MOVE! %x %x %d\n", src, dst, u);
    348 			vmabort();
    349 			return;
    350 		}
    351 		memmove(&vm.mem[dst], &vm.mem[src], u);
    352 	}
    353 }
    354 
    355 static void BOOTRD() { ppush(fgetc(fp)); }
    356 static void STDOUT() { dword c = ppop(); write(STDOUT_FILENO, &c, 1); }
    357 // ( -- c? f )
    358 static void MAYBEKEY() {
    359 	char c;
    360 	if (read(STDIN_FILENO, &c, 1) == 1) {
    361 		ppush(c); ppush(1);
    362 	} else { ppush(0); }
    363 }
    364 // ( a1 a2 u -- f )
    365 static void RANGEEQ() {
    366 	dword u = ppop();
    367 	dword a2 = ppop();
    368 	dword a1 = ppop();
    369 	if (u && memchk(a1+u) && memchk(a2+u)) {
    370 		ppush(memcmp(&vm.mem[a1], &vm.mem[a2], u) == 0);
    371 	} else {
    372 		ppush(1);
    373 	}
    374 }
    375 
    376 // n -- operand
    377 static void MAKEMEM() { vm.W = hbankset(OPHASDISP|OPMEM, vm.W); }
    378 // operand n -- operand
    379 static void ADDDISP() {
    380 	dword by = ppop(); if (by) {
    381 		vm.W = hbankset(vm.W, hbankget(vm.W)+by)|OPHASDISP;
    382 	}
    383 }
    384 static void CIDX() { // ( c a u -- ?idx f )
    385 	dword u = ppop();
    386 	dword a = ppop();
    387 	dword c = ppop();
    388 	for (dword i=0; i<u; i++) {
    389 		if ((dword)vm.mem[a++] == c) {
    390 			ppush(i); ppush(1); return;
    391 		}
    392 	}
    393 	ppush(0);
    394 }
    395 
    396 static void MAYBEWORD() { // 0x30
    397 	dword c, a;
    398 	if ((a = gd(NEXTWORD))) {
    399 		sd(NEXTWORD, 0);
    400 		memchk(a+gb(a));
    401 		memcpy(&vm.mem[CURWORD], &vm.mem[a], gb(a)+1);
    402 		ppush(CURWORD);
    403 		return;
    404 	}
    405 	do {
    406 		callword(inrdaddr);
    407 		c = ppop();
    408 		if (c >> 31) { // EOF
    409 			ppush(0);
    410 			return;
    411 		}
    412 	} while (c <= ' ');
    413 	a = CURWORD+1;
    414 	do {
    415 		sb(a++, c);
    416 		callword(inrdaddr);
    417 		c = ppop();
    418 	} while (!(c >> 31) && (c > ' '));
    419 	sb(CURWORD, a-CURWORD-1); // len
    420 	ppush(CURWORD);
    421 }
    422 
    423 static void WORD() {
    424 	MAYBEWORD();
    425 	if (!vm.W) {
    426 		write(STDOUT_FILENO, "word expected", 13);
    427 		vm.PC = abortaddr;
    428 	}
    429 }
    430 
    431 static void PARSE() {
    432 	dword s = ppop();
    433 	byte len = gb(s++);
    434 	dword n = 0;
    435 	byte neg = 0;
    436 	byte c;
    437 	if (!len) goto err;
    438 	switch (gb(s)) {
    439 		case '\'':
    440 			if (len != 3) goto err;
    441 			if (gb(s+2) != '\'') goto err;
    442 			n = gb(s+1);
    443 			break;
    444 		case '$':
    445 			s++; len--;
    446 			if (!len) goto err;
    447 			while (len--) {
    448 				c = (gb(s++) | 0x20) - '0';
    449 				if (c >= 10) {
    450 					c -= ('a'-'0');
    451 					if (c >= 6) goto err;
    452 					c += 10;
    453 				}
    454 				n *= 16;
    455 				n += c;
    456 			}
    457 			break;
    458 		case '-':
    459 			neg = 1;
    460 			s++; len--;
    461 			if (!len) goto err;
    462 		default: // decimal
    463 			while (len--) {
    464 				c = gb(s++) - '0';
    465 				if (c >= 10) goto err;
    466 				n *= 10;
    467 				n += c;
    468 			}
    469 			if (neg) n *= -1;
    470 	}
    471 	ppush(n); ppush(1);
    472 	return;
    473 err:
    474 	ppush(0);
    475 }
    476 
    477 // ( name 'dict -- entry-or-0 )
    478 static void FIND() {
    479 	dword dict = ppop();
    480 	dword s = ppop();
    481 	byte len = gb(s++);
    482 	ppush(_find(dict, &vm.mem[s], len));
    483 }
    484 
    485 static void WNF() {
    486 	write(STDOUT_FILENO, &vm.mem[CURWORD+1], vm.mem[CURWORD]);
    487 	write(STDOUT_FILENO, " word not found", 15);
    488 	vm.PC = abortaddr;
    489 }
    490 static void FINDMOD() {
    491 	dword a;
    492 	if (gd(MOD)) {
    493 		if ((a = findmeta(gd(MOD), gd(vm.W-8)))) {
    494 			vm.W = a + 8;
    495 			sd(MOD, 0);
    496 		}
    497 	}
    498 }
    499 
    500 static void STACKCHK() { // 0x38
    501 	if (vm.PSP > PSTOP) {
    502 		write(STDOUT_FILENO, "stack underflow", 15);
    503 		vm.PC = abortaddr;
    504 	}
    505 }
    506 
    507 static void COMPWORD() {
    508 	PARSE();
    509 	if (ppop()) {
    510 		litwr(ppop());
    511 	} else {
    512 		ppush(CURWORD);
    513 		ppush(sysdict());
    514 		FIND();
    515 		if (!vm.W) { WNF(); return; }
    516 		if ((gb(vm.W-9) & 0x80) /* immediate */) {
    517 			FINDMOD();
    518 			callword(ppop());
    519 			STACKCHK();
    520 		} else {
    521 			FINDMOD();
    522 			callwr(ppop());
    523 		}
    524 	}
    525 }
    526 static void RUNWORD() {
    527 	PARSE();
    528 	if (!ppop()) {
    529 		ppush(CURWORD);
    530 		ppush(sysdict());
    531 		FIND();
    532 		if (!vm.W) { WNF(); return; }
    533 		FINDMOD();
    534 		callword(ppop());
    535 		STACKCHK();
    536 	}
    537 }
    538 static void COMPILING() { ppush(vm.compiling); }
    539 static void STARTCOMP() {
    540 	vm.compiling = 1;
    541 	while (vm.compiling) {
    542 		WORD();
    543 		COMPWORD();
    544 	}
    545 }
    546 static void STOPCOMP() { vm.compiling = 0; }
    547 static void RSADDWR() {
    548 	dword n = ppop();
    549 	cwrite(0x09); // RSADD
    550 	dwrite(n);
    551 	sd(_RCNT_, gd(_RCNT_)+n);
    552 }
    553 // ( operand opcode -- )
    554 static void COMPOP() {
    555 	dword opcode = ppop(); dword operand = ppop(); wopwr(opcode, operand); }
    556 
    557 // 0x40
    558 // ( 'dict s -- )
    559 static void ENTRY() {
    560 	dword s = ppop();
    561 	dword dict = ppop();
    562 	byte len = gb(s++);
    563 	_entry(dict, &vm.mem[s], len);
    564 	sd(_RCNT_, 0);
    565 }
    566 static void CODE() { ppush(SYSDICT); WORD(); ENTRY(); }
    567 static void addmeta(dword id) {
    568 	dword a = gd(sysdict()-4);
    569 	sd(sysdict()-4, here());
    570 	dwrite(a); dwrite(id);
    571 }
    572 static void CODE16() { addmeta(EMETA_16B); }
    573 static void CODE8() { addmeta(EMETA_8B); }
    574 static void COMPBINOP() {
    575 	dword binopidx = ppop(); dword operand = ppop();
    576 	binopwr(binopidx, operand); }
    577 
    578 typedef dword (*BinOp)(dword, dword);
    579 #define BINOP(name, op) static dword name(dword dst, dword src) { return dst op src; }
    580 BINOP(BADD, +) BINOP(BSUB, -) BINOP(BMUL, *)
    581 BINOP(BSHL, <<) BINOP(BSHR, >>)
    582 BINOP(BAND, &) BINOP(BOR, |) BINOP(BXOR, ^)
    583 static dword BDIVMOD(dword dst, dword src) { vm.A = dst % src; return dst / src; }
    584 #define BINOPCNT 0x10
    585 static BinOp binops[BINOPCNT] = {
    586 	BADD, BSUB, BMUL, BDIVMOD, NULL, BSHL, BSHR, NULL,
    587 	BAND, BOR, BXOR, NULL, NULL, NULL, NULL, NULL,
    588 };
    589 static void _binop() {
    590 	byte binopidx = gpcb(); readop();
    591 	dword n = binops[binopidx](opdget(), opsget());
    592 	opdset(n); vm.Z = n == 0; }
    593 static void WBINOP() { M32B; _binop(); } // 0x48
    594 static void WBINOP16() { M16B; _binop(); }
    595 static void WBINOP8() { M8B; _binop(); }
    596 static void HBANKADDR() { ppush(hbankaddr(ppop())); }
    597 static void HBANKGET() { ppush(gd(hbankaddr(ppop()))); }
    598 static void HBANKSET() { ppush(_hbankset(ppop())); }
    599 static void NEG() { vm.W = -vm.W; }
    600 
    601 static void BYE() { vm.PC = MEMSZ; } // 0x50
    602 static void BYEFAIL() { vmabort(); }
    603 static void QUIT() {
    604 	sd(MOD, 0);
    605 	vm.RSP = RSTOP;
    606 	vm.compiling = 0;
    607 	vm.PC = mainaddr;
    608 }
    609 static void ABORT_() {
    610 	vm.PSP = PSTOP;
    611 	sd(NEXTMETA, 0);
    612 	QUIT();
    613 }
    614 static void _stackdump(dword a, dword top) {
    615 	while (a < top) { printf("%08x ", gd(a)); a += 4; } printf("\n");
    616 }
    617 static void DBG() {
    618 	printf("W %08x A %08x PC %08x PSP %08x RSP %08x MOD %08x HERE %08x\n",
    619 		vm.W, vm.A, vm.PC, vm.PSP, vm.RSP, gd(MOD), gd(HERE));
    620 	printf("PS "); _stackdump(vm.PSP, PSTOP-4);
    621 	printf("RS "); _stackdump(vm.RSP, RSTOP);
    622 }
    623 static void USLEEP() { usleep(ppop()); }
    624 // ( a u -- )
    625 static void MPROTECT() { vm.mprotectsz = ppop(); vm.mprotect = ppop(); }
    626 
    627 // 0x60
    628 // ( cond -- )
    629 static void STOREC() { vm.W = checkcond(gpcb()); }
    630 
    631 /* Filesystem
    632 
    633 At POSIX level, we don't have access to the underlying FS structure such as
    634 inodes, making Dusk's 32-bit "ID" system a bit awkward. What we're going to do
    635 here is have a buffer of all requested paths, and the index in that buffer will
    636 be our ID.
    637 
    638 To simplify cursor management, we have a single IO buffer that we rotate our
    639 reads into.
    640 */
    641 #define MAXPATHSZ 0x100
    642 #define FSIDCNT 0x200
    643 #define FILEPOSOFF 20
    644 #define FILEDESCOFF 36
    645 static char fsids[FSIDCNT][MAXPATHSZ] = {0};
    646 
    647 static dword findpath(char *path) {
    648 	for (dword i=0; i<FSIDCNT; i++) {
    649 		if (strcmp(path, fsids[i]) == 0) return i;
    650 	}
    651 	return 0;
    652 }
    653 
    654 // Return the "high" part of fd. On a 32-bit system, it's always zero. On a
    655 // 64-bit system, it's fd >> 32.
    656 static dword fdhi(int fd) {
    657 	if (sizeof(int) > sizeof(dword)) {
    658 		return (int64_t)fd >> 32;
    659 	} else {
    660 		return 0;
    661 	}
    662 }
    663 
    664 static int fdjoin(dword lo, dword hi) {
    665 	if (sizeof(int) > sizeof(dword)) {
    666 		return ((int64_t)hi<<32)|lo;
    667 	} else {
    668 		return lo;
    669 	}
    670 }
    671 
    672 static int getfiledesc(dword hdl) {
    673 	return fdjoin(gd(hdl+FILEDESCOFF), gd(hdl+FILEDESCOFF+4));
    674 }
    675 
    676 static char* getpathfromid(dword fsid) {
    677 	if ((fsid >= FSIDCNT) || !fsids[fsid][0]) {
    678 		printf("Out of bounds FSID %x\n", fsid);
    679 		vm.PC = abortaddr;
    680 		return NULL;
    681 	}
    682 	return fsids[fsid];
    683 }
    684 
    685 static char* pathcat(char *p1, dword s) {
    686 	static char buf1[MAXPATHSZ] = {0};
    687 	static char buf2[MAXPATHSZ] = {0};
    688 	byte len = gb(s++);
    689 	if (!memchk(s+len)) return NULL;
    690 	strcpy(buf1, p1);
    691 	strcat(buf1, "/");
    692 	strncat(buf1, (char*)&vm.mem[s], len);
    693 	if (!realpath(buf1, buf2)) return "/NoSuchFile";
    694 	return buf2;
    695 }
    696 
    697 static dword _newfsid(char *path) {
    698 	dword res = 0;
    699 	while (fsids[++res][0]);
    700 	strcpy(fsids[res], path);
    701 	return res;
    702 }
    703 
    704 static void FCHILD () { // 0x60
    705 	dword s = ppop();
    706 	dword parent = ppop();
    707 	char *path;
    708 	dword res;
    709 
    710 	path = pathcat(getpathfromid(parent), s);
    711 	if (!path) return;
    712 	res = findpath(path);
    713 	if (!res) {
    714 		// Verify if path actually exists in FS, then create a new ID
    715 		struct stat sb;
    716 		if (stat(path, &sb) != 0) { // does not exist
    717 			ppush(0);
    718 			return;
    719 		}
    720 		res = _newfsid(path);
    721 	}
    722 	ppush(res);
    723 }
    724 
    725 // Our handles are simply the regular IO/File preludes followed by the POSIX
    726 // file descriptor, a 64bit int.
    727 
    728 #undef FOPEN
    729 static void FOPEN () {
    730 	dword fsid = ppop();
    731 	char *path;
    732 	int fd;
    733 	int filesize;
    734 	path = getpathfromid(fsid);
    735 	fd = open(path, O_RDONLY);
    736 	if (fd < 0) {
    737 		printf("Can't open %s\n", path);
    738 		vm.PC = abortaddr;
    739 		return;
    740 	}
    741 	filesize = lseek(fd, 0, SEEK_END);
    742 	lseek(fd, 0, SEEK_SET);
    743 	ppush(here()); // File cursor we're about to create
    744 	dwrite(0); // putback
    745 	dwrite(find("_freadbuf"));
    746 	dwrite(find("abort")); // writebuf
    747 	dwrite(find("drop")); // flush
    748 	dwrite(find("_fclose")); // close
    749 	dwrite(0); // pos
    750 	dwrite(filesize);
    751 	dwrite(find("_fseek")); // seek
    752 	dwrite(find("abort")); // resize
    753 	dwrite(fd);
    754 	dwrite(fdhi(fd));
    755 }
    756 
    757 static void FREADBUF() {
    758 	dword hdl = ppop();
    759 	dword n = ppop();
    760 	int res;
    761 	int fd = getfiledesc(hdl);
    762 	if (!fd) {
    763 		ppush(0);
    764 		return;
    765 	}
    766 	if (n > IOBUFSZ) n = IOBUFSZ;
    767 	res = read(fd, &vm.mem[IOBUF], n);
    768 	if (res < 0) {
    769 		printf("I/O readbuf error\n");
    770 		vm.PC = abortaddr;
    771 		return;
    772 	}
    773 	sd(hdl+FILEPOSOFF, lseek(fd, 0, SEEK_CUR));
    774 	if (res) ppush(IOBUF);
    775 	ppush((dword)res);
    776 }
    777 
    778 static void FCLOSE() {
    779 	dword hdl = ppop();
    780 	int fd = getfiledesc(hdl);
    781 	if (fd) {
    782 		close(fd);
    783 		sd(hdl+FILEDESCOFF, 0);
    784 		sd(hdl+FILEDESCOFF+4, 0);
    785 	}
    786 }
    787 
    788 // ( id -- info )
    789 static void FINFO() {
    790 	dword fsid = ppop();
    791 	dword dst = find("_fsinfobuf");
    792 	char *path = getpathfromid(fsid);
    793 	char *name = basename(path);
    794 	struct stat s;
    795 	if (stat(path, &s) != 0) { // does not exist
    796 		printf("Can't stat %s\n", path);
    797 		vm.PC = abortaddr;
    798 		return;
    799 	}
    800 	ppush(dst);
    801 	sd(dst, dst+12); // where we'll put the name
    802 	sd(dst+4, s.st_size);
    803 	sd(dst+8, S_ISDIR(s.st_mode));
    804 	sb(dst+12, strlen(name));
    805 	memcpy(&vm.mem[dst+13], name, strlen(name));
    806 }
    807 
    808 static struct dirent* _next(DIR *dirp) {
    809 	struct dirent *d;
    810 	do {
    811 		d = readdir(dirp);
    812 	} while (d && ((strcmp(d->d_name, ".") == 0) || (strcmp(d->d_name, "..") == 0)));
    813 	return d;
    814 }
    815 
    816 // ( dirid previd -- id-or-0 )
    817 static void FITER() {
    818 	DIR *dirp;
    819 	struct dirent *d;
    820 	char path[MAXPATHSZ] = {0};
    821 	int baselen;
    822 	dword previd = ppop();
    823 	dword dirid = ppop();
    824 	dword curid = 0;
    825 	int matched = 0;
    826 	strcpy(path, getpathfromid(dirid));
    827 	dirp = opendir(path);
    828 	if (!dirp) {
    829 		printf("Couldn't open dir %s\n", path);
    830 		vm.PC = abortaddr;
    831 		return;
    832 	}
    833 	strcat(path, "/");
    834 	baselen = strlen(path);
    835 	do {
    836 		if (curid == previd) matched = 1;
    837 		d = _next(dirp);
    838 		if (d) {
    839 			strcpy(&path[baselen], d->d_name);
    840 			curid = findpath(path);
    841 			if (!curid) curid = _newfsid(path);
    842 		} else { curid = 0; }
    843 	} while (curid && !matched);
    844 	ppush(curid);
    845 }
    846 
    847 static void FSEEK() {
    848 	dword hdl = ppop();
    849 	dword pos = ppop();
    850 	int res;
    851 	int fd = getfiledesc(hdl);
    852 	if (!fd) {
    853 		vm.PC = abortaddr;
    854 		return;
    855 	}
    856 	res = lseek(fd, pos, SEEK_SET);
    857 	if (res < 0) {
    858 		printf("I/O lseek error\n");
    859 		vm.PC = abortaddr;
    860 		return;
    861 	}
    862 	sd(hdl+FILEPOSOFF, res);
    863 }
    864 
    865 // ( imgname -- size-in-bytes )
    866 static void MOUNTDRV() {
    867 	char buf[64] = {0};
    868 	dword str = ppop();
    869 	byte slen = gb(str++);
    870 	memcpy(buf, &vm.mem[str], slen);
    871 	if (fp) {
    872 		fclose(fp);
    873 		fp = NULL;
    874 	}
    875 	fp = fopen(buf, "r+");
    876 	fseek(fp, 0, SEEK_END);
    877 	ppush(ftell(fp));
    878 	fseek(fp, 0, SEEK_SET);
    879 	if (!fp) {
    880 		printf("Can't open %s.\n", buf);
    881 		BYEFAIL();
    882 	}
    883 }
    884 
    885 // ( -- )
    886 static void UNMOUNTDRV() {
    887 		if (fp) {
    888 		fclose(fp);
    889 		fp = NULL;
    890 	}
    891 }
    892 
    893 #define SECSZ 512
    894 // ( sec dst drv -- )
    895 static void DRVRD() {
    896 	ppop();
    897 	dword dst = ppop();
    898 	dword sec = ppop();
    899 	fseek(fp, SECSZ * sec, SEEK_SET);
    900 	fread(&vm.mem[dst], SECSZ, 1, fp);
    901 }
    902 
    903 // ( sec src drv -- )
    904 static void DRVWR() {
    905 	ppop();
    906 	dword src = ppop();
    907 	dword sec = ppop();
    908 	fseek(fp, SECSZ * sec, SEEK_SET);
    909 	fwrite(&vm.mem[src], SECSZ, 1, fp);
    910 }
    911 
    912 #define OPCNT 0x70
    913 static void (*ops[OPCNT])() = {
    914 	BR, CALL, RET, BRWR, BRA, BRC, NULL, YIELD,
    915 	PSADD, RSADD, NULL, NULL, NULL, NULL, NULL, NULL,
    916 	WFETCH, WSWAP, ADDN, WCMP, WFETCHINC, WDECFETCH, NULL, NULL,
    917 	WFETCH16, WSWAP16, ADDN16, WCMP16, WFETCHINC16, WDECFETCH16, NULL, NULL,
    918 	WFETCH8, WSWAP8, ADDN8, WCMP8, WFETCHINC8, WDECFETCH8, NULL, NULL,
    919 	MOVE, BOOTRD, STDOUT, MAYBEKEY, RANGEEQ, MAKEMEM, ADDDISP, CIDX,
    920 	MAYBEWORD, WORD, PARSE, FIND, WNF, FINDMOD, NULL, NULL,
    921 	STACKCHK, COMPWORD, RUNWORD, COMPILING, STARTCOMP, STOPCOMP, RSADDWR, COMPOP,
    922 	NULL, ENTRY, CODE, CODE16, CODE8, COMPBINOP, NULL, NULL,
    923 	WBINOP, WBINOP16, WBINOP8, HBANKADDR, HBANKSET, HBANKGET, NEG, NULL,
    924 	BYE, BYEFAIL, QUIT, ABORT_, DBG, USLEEP, MPROTECT, NULL,
    925 	NULL, NULL, NULL, NULL, NULL, NULL, STOREC, NULL,
    926 	FCHILD, FOPEN, FREADBUF, FCLOSE, FINFO, FITER, NULL, FSEEK,
    927 	MOUNTDRV, UNMOUNTDRV, DRVRD, DRVWR, NULL, NULL, NULL, NULL,
    928 };
    929 
    930 static void oprun1() { // run next op
    931 	if (!memchk(vm.PC)) return;
    932 	byte opcode = vm.mem[vm.PC++];
    933 	if ((opcode >= OPCNT) || (!ops[opcode])) {
    934 		printf("Illegal opcode %02x at PC %08x\n", opcode, vm.PC-1);
    935 		BYEFAIL();
    936 	} else {
    937 		ops[opcode]();
    938 	}
    939 }
    940 
    941 static void callword(dword addr) {
    942 	dword oldPC;
    943 	// We push a special "0" marker to RS. This way, when PC is 0, we know that
    944 	// it's time to return from our callword().
    945 	rpush(0);
    946 	oldPC = vm.PC;
    947 	vm.PC = addr;
    948 	while (vm.PC && (vm.PC < MEMSZ)) oprun1();
    949 	if (vm.PC < MEMSZ) {
    950 		vm.PC = oldPC;
    951 	}
    952 }
    953 
    954 static void wentry(char *name, byte op) { entry(name); cwrite(op); retwr(); }
    955 static void sysconst(char *name, dword val) { entry(name); litwr(val); retwr(); }
    956 static void sysalias(char *name, char *target) { entry(name); brwr(find(target)); }
    957 static void makeimm() { dword a = sysdict()-5; sb(a, gb(a)|0x80); }
    958 static void compileop(byte op) { litwr(op); cwritewr(); }
    959 
    960 // Names for simple word-to-code mappings
    961 static char *opnames[OPCNT-0x28] = {
    962 	"move", "boot<", "(emit)", "(key?)", "[]=", "m)", "+)", "cidx",
    963 	"maybeword", "word", "parse", "find", "(wnf)", "findmod", NULL, NULL,
    964 	"stack?", "compword", "runword", "compiling", "]", NULL, "rs+,", NULL,
    965 	NULL, "entry", "code", "code16b", "code8b", NULL, NULL, NULL,
    966 	NULL, NULL, NULL, "hbank'", "hbank!", "hbank@",NULL, NULL,
    967 	"bye", "byefail", "quit", "(abort)", "dbg", "_usleep", "mprotect", NULL,
    968 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    969 	"_fchild", "_fopen", "_freadbuf", "_fclose", "_finfo", "_fiter", NULL, "_fseek",
    970 	"_mountdrv", "_unmountdrv", "_drv@", "_drv!", NULL, NULL, NULL, NULL
    971 };
    972 
    973 static void buildsysdict() {
    974 	sd(HERE, 0);
    975 	sd(HEREMAX, IOBUF);
    976 	sd(NEXTWORD, 0);
    977 	sd(MOD, 0);
    978 	sd(SYSDICT, 0);
    979 	sd(SYSDICT+4, 0); // set 0 len byte. See doc/impl
    980 	sysconst("HERE", HERE);
    981 	sysconst("NEXTWORD", NEXTWORD);
    982 	sysconst("HEREMAX", HEREMAX);
    983 	sysconst("MOD", MOD);
    984 	sysconst("DOESSZ", 9);
    985 	sysconst("BRSZ", 5);
    986 	sysconst("PSTOP", PSTOP);
    987 	sysconst("RSTOP", RSTOP);
    988 	sysconst("curword", CURWORD);
    989 	sysconst("sysdict", SYSDICT);
    990 	sysconst("nextmeta", NEXTMETA);
    991 	sysconst("[rcnt]", _RCNT_);
    992 	sysconst("W)", OPW); sysconst("A)", OPA);
    993 	sysconst("PSP)", OPPSP); sysconst("RSP)", OPRSP);
    994 	sysconst("Z)", CONDZ); sysconst("NZ)", CONDNZ);
    995 	sysconst("<)", CONDLT); sysconst(">=)", CONDNLT);
    996 	sysconst(">)", CONDGT); sysconst("<=)", CONDNGT);
    997 	sysconst("s<)", CONDSLT); sysconst("s>=)", CONDNSLT);
    998 	sysconst("s>)", CONDSGT); sysconst("s<=)", CONSNSGT);
    999 	for (int i=0; i<OPCNT-0x28; i++) {
   1000 		if (opnames[i]) wentry(opnames[i], i+0x28);
   1001 	}
   1002 	wentry("[", 0x3d); makeimm();
   1003 	entry("pushret,"); retwr();
   1004 	entry("popret,"); retwr();
   1005 	entry("ps+,"); compileop(0x08); writewr(); retwr();
   1006 	entry("@,"); compopwr(0x10); retwr();
   1007 	entry("@!,"); compopwr(0x11); retwr();
   1008 	// ( n operand -- )
   1009 	entry("+n,"); compopwr(0x12); writewr(); retwr();
   1010 	entry("compare,"); compopwr(0x13); retwr();
   1011 	entry("@+,"); compopwr(0x14); retwr();
   1012 	entry("-@,"); compopwr(0x15); retwr();
   1013 	entry("+,"); compbinopwr(0x00); retwr();
   1014 	entry("-,"); compbinopwr(0x01); retwr();
   1015 	entry("*,"); compbinopwr(0x02); retwr();
   1016 	entry("/mod,"); compbinopwr(0x03); retwr();
   1017 	entry("<<,"); compbinopwr(0x05); retwr();
   1018 	entry(">>,"); compbinopwr(0x06); retwr();
   1019 	entry("&,"); compbinopwr(0x08); retwr();
   1020 	entry("|,"); compbinopwr(0x09); retwr();
   1021 	entry("^,"); compbinopwr(0x0a); retwr();
   1022 	entry("-W,"); compileop(0x4e); retwr();
   1023 	entry("C>W,"); compileop(0x5e); cwritewr(); retwr();
   1024 	entry("exit,"); compileop(0x02); retwr();
   1025 	entry("branchR,"); compileop(0x01); writewr(); retwr();
   1026 	entry("branchA,"); compileop(0x04); retwr();
   1027 	entry("branch,"); litwr(0x00); cwritewr(); cwrite(0x03); retwr();
   1028 	entry("branchC,"); litwr(0x05); cwritewr(); cwritewr(); cwrite(0x03); retwr();
   1029 	entry("branch!"); storewr(); retwr();
   1030 	entry("yield"); compileop(0x07); retwr(); makeimm();
   1031 	entry(";"); compileop(0x02); cwrite(0x3d); retwr(); makeimm();
   1032 	entry("16b)"); binopwr(0x09 /* |, */, hbankset(OPHASDISP|OPMEM|OPDEREF, OP16B)); retwr();
   1033 	entry("8b)"); binopwr(0x09 /* |, */, hbankset(OPHASDISP|OPMEM|OPDEREF, OP8B)); retwr();
   1034 	entry("32b)"); binopwr(0x08 /* &, */, hbankset(OPHASDISP|OPMEM|OPDEREF, (OP8B|OP16B)^0xffffffff)); retwr();
   1035 	entry("A>)"); binopwr(0x09 /* |, */, hbankset(OPHASDISP|OPMEM|OPDEREF, OPADEST)); retwr();
   1036 	entry("<>)"); binopwr(0x0a /* |, */, hbankset(OPHASDISP|OPMEM|OPDEREF, OPINVERT)); retwr();
   1037 	entry("&)"); binopwr(0x09 /* |, */, hbankset(OPHASDISP|OPMEM|OPDEREF, OPDEREF)); retwr();
   1038 	entry("i)"); callwr(find("m)")); callwr(find("&)")); retwr();
   1039 	entry("!,"); callwr(find("<>)")); callwr(find("@,")); retwr();
   1040 	entry("!+,"); callwr(find("<>)")); callwr(find("@+,")); retwr();
   1041 	entry("-!,"); callwr(find("<>)")); callwr(find("-@,")); retwr();
   1042 	entry("dup,");
   1043 	litwr(0xfffffffc); callwr(find("ps+,"));
   1044 	callwr(find("PSP)")); callwr(find("!,")); retwr();
   1045 	entry("litn");
   1046 	callwr(find("dup,")); callwr(find("i)")); callwr(find("@,")); retwr();
   1047 	sysalias("in<", "boot<");
   1048 	sysalias("rtype", "byefail");
   1049 	sysalias("abort", "byefail");
   1050 	abortaddr = find("abort");
   1051 	inrdaddr = find("in<");
   1052 	entry("_fsinfobuf"); allot(0x112); // used in FINFO
   1053 	entry("main");
   1054 	mainaddr = here();
   1055 	callwr(find("word"));
   1056 	callwr(find("runword"));
   1057 	brwr(mainaddr);
   1058 }
   1059 
   1060 // Interpret loop
   1061 int main() {
   1062 	strcpy(fsids[0], "fs"); // Set FS root path
   1063 	fp = fopen("posix/boot.fs", "r");
   1064 	if (!fp) {
   1065 		printf("Can't open boot file.\n");
   1066 		return 1;
   1067 	}
   1068 	buildsysdict();
   1069 	vm.mprotectsz = 0;
   1070 	vm.PC = find("(abort)");
   1071 	while (vm.PC < MEMSZ) oprun1();
   1072 	if (fp) fclose(fp);
   1073 	if (vm.PC > MEMSZ) {
   1074 		DBG();
   1075 		fprintf(stderr, "Dumping memory to memdump.\n");
   1076 		FILE *fp = fopen("memdump", "w");
   1077 		fwrite(vm.mem, MEMSZ, 1, fp);
   1078 		fclose(fp);
   1079 	}
   1080 	return MEMSZ - vm.PC;
   1081 }