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 }