├── .gitignore ├── README.md ├── bin └── .gitignore ├── doc └── DB001-110412-F18A.pdf ├── src ├── .gitignore ├── Makefile ├── README.txt ├── f18.c ├── f18.h └── f18_emu.c └── test ├── add.f18 ├── add_stream.f18 ├── add_stream2.f18 ├── example1.f18 ├── example2.f18 ├── example3.f18 ├── message.f18 └── mult.f18 /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # F18 2 | 3 | A simulator of the Green Array F18A chip. 4 | 5 | The simulator is a bit different than the chip since it 6 | interprets the input from the io registers rather than 7 | just expect a binary stream of 18 bit input. 8 | 9 | This makes it super simple to write programs for the 10 | F18A simulator since you can feed it straight in. 11 | 12 | ## Build 13 | 14 | cd src 15 | make 16 | 17 | ## Run 18 | 19 | Have a look in the test directory and run the examples by 20 | 21 | cd test 22 | ../bin/f18 < add.f18 23 | 24 | or 25 | cd test 26 | ../bin/f18 < mult.f18 27 | 28 | ... 29 | 30 | ## Options 31 | 32 | -v verbose (if debug compiled) 33 | -t trace (if debug comipled) 34 | -d delay Set delay between instructions 35 | -l VxH processor layout (max 8x18) default is 1x1!!! 36 | 37 | Specially the last flag is worth mentioning, -l 3x4 starts 12 thread 38 | with about 1 page of node data and 4 pages of stack each. 39 | On a mac 8x18 open a bit too many socket pairs, simulating the io 40 | registers used for communication between nodes. 41 | That is memory consumption is 2.8M when running all 8x18 (144) nodes. 42 | 43 | ## Remarks 44 | 45 | The processor is interesting in a number of ways, but the way 46 | you can just bootstrap the thing from basically nothing is 47 | spectacular. 48 | Worth mentioning is also the selective receive, by using various 49 | io registers. 50 | 51 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | f18 2 | -------------------------------------------------------------------------------- /doc/DB001-110412-F18A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyrog/f18/cc776021131229028ab41fabe352858b04b07578/doc/DB001-110412-F18A.pdf -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *~ 3 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | OBJS = f18.o f18_emu.o 2 | CFLAGS = -g -DDEBUG 3 | LDFLAGS = -g -lpthread 4 | 5 | ../bin/f18: $(OBJS) 6 | $(CC) -o $@ $(OBJS) $(LDFLAGS) 7 | 8 | f18_emu.o: f18_emu.c f18.h 9 | $(CC) $(CFLAGS) -c f18_emu.c 10 | 11 | f18.o: f18.c f18.h 12 | $(CC) $(CFLAGS) -c f18.c 13 | -------------------------------------------------------------------------------- /src/README.txt: -------------------------------------------------------------------------------- 1 | 2 | Some cool tricks: 3 | 4 | - computed jump when address on stack: 5 | 6 | push ; 7 | 8 | - same as above but with ex: co-routine jump 9 | 10 | push ex 11 | 12 | - 13 | call:xxxx ; => jump:xxxx 14 | 15 | Direction port ioreg numbers 16 | 17 | 8 7654 3210 18 | _D_U 1 0000 0101 0 19 | _D__ 1 0001 0101 1 20 | _DLU 1 0010 0101 2 21 | _DL_ 1 0011 0101 3 22 | ___U 1 0100 0101 4 23 | 24 | __LU 1 0110 0101 6 25 | __L_ 1 0111 0101 7 26 | RD_U 1 1000 0101 8 27 | RD__ 1 1001 0101 9 28 | RDLU 1 1010 0101 A 29 | RDL_ 1 1011 0101 B 30 | R__U 1 1100 0101 C 31 | R___ 1 1101 0101 D 32 | R_LU 1 1110 0101 E 33 | R_L_ 1 1111 0101 F 34 | 35 | : zero ( -- 0 ) dup dup or ; 36 | 37 | : nowhere 155 ; 38 | : right ( x -- x ) 80 + ; 39 | : down ( x -- x ) -40 + ; 40 | : left ( x -- x ) 20 + ; 41 | : up ( x -- x ) -10 + ; 42 | 43 | route codes: 44 | with 2 bits we can get upto 9 hops of data transfers in a word 45 | 46 | 00 = R 47 | 01 = D 48 | 10 = L 49 | 11 = U 50 | 51 | This code segment is normall y first in a stream: 52 | 53 | \ ------- BOOT LOADER ------ 54 | @p push . . 55 | 56 | @p !+ unext . 57 | \ -------------------------- 58 | 59 | Or it could be a router like this one: 60 | 61 | \ ------- BOOT/ROUTER ------ 62 | @p push @p . 63 | 64 | 65 | a! . . . 66 | @p !+ unext . 67 | \ -------------------------- 68 | 69 | -------------------------------------------------------------------------------- /src/f18.c: -------------------------------------------------------------------------------- 1 | // 2 | // F18 emulator 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "f18.h" 23 | 24 | #ifndef SIGRETTYPE /* allow override via Makefile */ 25 | #define SIGRETTYPE void 26 | #endif 27 | 28 | // NOTE! STACK_SIZE is the smallest possible page number I found working 29 | #define PAGE_SIZE g_page_size 30 | #define PAGE(x) ((((x)+g_page_size-1)/g_page_size)*g_page_size) 31 | #define NODE_SIZE sizeof(node_t) 32 | #define STACK_SIZE (2*PAGE_SIZE+PTHREAD_STACK_MIN) 33 | 34 | 35 | static int tty_fd = -1; 36 | static struct termios tty_smode; 37 | static struct termios tty_rmode; 38 | static size_t g_page_size = 0; 39 | static uint18_t g_flags = 0; 40 | 41 | static SIGRETTYPE ctl_c(int); 42 | static SIGRETTYPE suspend(int); 43 | static SIGRETTYPE (*orig_ctl_c)(int); 44 | 45 | typedef struct { 46 | int i; 47 | int j; 48 | node_t* np; 49 | pthread_t thread; 50 | pthread_attr_t attr; 51 | } node_data_t; 52 | 53 | node_data_t node[8][18]; 54 | 55 | int select_ports(node_t* np, uint18_t ioreg, int is_input, 56 | int* fds, uint18_t* io_rd, uint18_t* io_wr) 57 | { 58 | if ((ioreg < IOREG_START) || (ioreg > IOREG_END)) 59 | return 0; 60 | if (io_rd) io_rd[0] = 0; 61 | if (io_wr) io_wr[0] = 0; 62 | switch(ioreg) { 63 | case IOREG_STDIO: 64 | if ((fds[0] = is_input ? np->stdin_fd : np->stdout_fd) < 0) 65 | return 0; 66 | return 1; 67 | case IOREG_STDIN: 68 | if (!is_input || (np->stdin_fd < 0)) 69 | return 0; 70 | fds[0] = np->stdin_fd; 71 | return 1; 72 | case IOREG_STDOUT: 73 | if (is_input || (np->stdout_fd < 0)) 74 | return 0; 75 | fds[0] = np->stdout_fd; 76 | return 1; 77 | case IOREG_TTY: 78 | if (np->tty_fd < 0) 79 | return 0; 80 | fds[0] = np->tty_fd; 81 | return 1; 82 | default: { 83 | int i = 0; 84 | if ((ioreg & F18_DIR_MASK) == F18_DIR_BITS) { 85 | if ((ioreg & F18_RIGHT_BIT) && (np->right_fd >= 0)) { 86 | fds[i] = np->right_fd; 87 | if (io_rd) io_rd[i] = F18_IO_RIGHT_RD; 88 | if (io_wr) io_wr[i] = F18_IO_RIGHT_WR; 89 | i++; 90 | } 91 | if (!(ioreg & F18_DOWN_BIT) && (np->down_fd >= 0)) { 92 | fds[i] = np->down_fd; 93 | if (io_rd) io_rd[i] = F18_IO_DOWN_RD; 94 | if (io_wr) io_wr[i] = F18_IO_DOWN_WR; 95 | i++; 96 | } 97 | if ((ioreg & F18_LEFT_BIT) && (np->left_fd >= 0)) { 98 | fds[i] = np->left_fd; 99 | if (io_rd) io_rd[i] = F18_IO_LEFT_RD; 100 | if (io_wr) io_wr[i] = F18_IO_LEFT_WR; 101 | i++; 102 | } 103 | if (!(ioreg & F18_UP_BIT) && (np->up_fd >= 0)) { 104 | fds[i] = np->up_fd; 105 | if (io_rd) io_rd[i] = F18_IO_UP_RD; 106 | if (io_wr) io_wr[i] = F18_IO_UP_WR; 107 | i++; 108 | } 109 | } 110 | return i; 111 | } 112 | } 113 | } 114 | 115 | static void f18_write_ioreg(node_t* np, uint18_t ioreg, uint18_t value) 116 | { 117 | uint18_t io_wr[4]; 118 | int fds[4]; 119 | int nports; 120 | int i, r; 121 | 122 | if ((nports = select_ports(np, ioreg, 0, fds, NULL, io_wr)) == 0) { 123 | fprintf(stderr, "io error when writing ioreg=%x, not mapped\n", ioreg); 124 | return; 125 | } 126 | 127 | if (ioreg == IOREG_TTY) { // special for character i/o 128 | char c = value; 129 | if (write(fds[0], &c, 1) < 0) 130 | goto error; 131 | return; 132 | } 133 | 134 | for (i = 0; i < nports; i++) { 135 | char buf[8]; 136 | int len; 137 | uint18_t dd = io_wr[i]; 138 | int fd = fds[i]; 139 | 140 | len = snprintf(buf, sizeof(buf), "%05x\n", value); 141 | if (len >= sizeof(buf)) { 142 | fprintf(stderr, "warning: output truncated\n"); 143 | len = sizeof(buf)-1; 144 | } 145 | 146 | switch(dd) { 147 | case 0: 148 | r = write(fd, buf, len); 149 | break; 150 | case F18_IO_RIGHT_WR: 151 | if (np->flags & FLAG_WR_BIN_RIGHT) 152 | r = write(fd, &value, sizeof(value)); 153 | else 154 | r = write(fd, buf, len); 155 | break; 156 | case F18_IO_DOWN_WR: 157 | if (np->flags & FLAG_WR_BIN_DOWN) 158 | r = write(fd, &value, sizeof(value)); 159 | else 160 | r = write(fd, buf, len); 161 | break; 162 | case F18_IO_LEFT_WR: 163 | if (np->flags & FLAG_WR_BIN_LEFT) 164 | r = write(fd, &value, sizeof(value)); 165 | else 166 | r = write(fd, buf, len); 167 | break; 168 | case F18_IO_UP_WR: 169 | if (np->flags & FLAG_WR_BIN_UP) 170 | r = write(fd, &value, sizeof(value)); 171 | else 172 | r = write(fd, buf, len); 173 | break; 174 | default: 175 | break; 176 | } 177 | if (r < 0) 178 | goto error; 179 | } 180 | return; 181 | 182 | error: 183 | fprintf(stderr, "io error when writing ioreg=%x, error=%s\n", 184 | ioreg, strerror(errno)); 185 | } 186 | 187 | 188 | 189 | int parse_mnemonic(char* word, int n) 190 | { 191 | int i; 192 | for (i=0; i<32; i++) { 193 | int len = strlen(f18_ins_name[i]); 194 | if ((n == len) && (memcmp(word, f18_ins_name[i], n) == 0)) 195 | return i; 196 | } 197 | return -1; 198 | } 199 | 200 | struct { 201 | char* name; 202 | uint18_t value; 203 | } symbol[] = { 204 | { "stdio", IOREG_STDIO }, 205 | { "stdin", IOREG_STDIN }, 206 | { "stdout", IOREG_STDOUT }, 207 | { "tty", IOREG_TTY }, 208 | 209 | { "io", IOREG_IO }, 210 | { "data", IOREG_DATA }, 211 | { "---u", IOREG____U }, 212 | { "--l-", IOREG___L_ }, 213 | { "--lu", IOREG___LU }, 214 | { "-d--", IOREG__D__ }, 215 | { "-d--u", IOREG__D_U }, 216 | { "-dl-", IOREG__DL_ }, 217 | { "-dlu", IOREG__DLU }, 218 | { "r---", IOREG_R___ }, 219 | { "r--u", IOREG_R__U }, 220 | { "r-l-", IOREG_R_L_ }, 221 | { "r-lu", IOREG_R_LU }, 222 | { "rd--", IOREG_RD__ }, 223 | { "rd-u", IOREG_RD_U }, 224 | { "rdl-", IOREG_RDL_ }, 225 | { "rdlu", IOREG_RDLU }, 226 | { NULL, 0 } 227 | }; 228 | 229 | int parse_symbol(char** pptr, uint18_t* valuep) 230 | { 231 | int i = 0; 232 | char*ptr = *pptr; 233 | 234 | while(symbol[i].name != NULL) { 235 | int n = strlen(symbol[i].name); 236 | if (strncmp(ptr, symbol[i].name, n) == 0) { 237 | if ((ptr[n] == '\0') || isblank(ptr[n])) { 238 | *pptr = ptr + n; 239 | *valuep = symbol[i].value; 240 | return 1; 241 | } 242 | } 243 | i++; 244 | } 245 | return 0; 246 | } 247 | 248 | #define TOKEN_ERROR -1 249 | #define TOKEN_EMPTY 0 250 | #define TOKEN_MNEMONIC1 1 251 | #define TOKEN_MNEMONIC2 2 252 | #define TOKEN_VALUE 3 253 | 254 | // 255 | // parse: 256 | // 257 | // ( '(' .* ')' )* 258 | // ( '(' .* ')' )* ':' 259 | // ( '(' .* ')' )* 260 | // ( '(' .* ')' )* \ .* 261 | // 262 | 263 | int parse_ins(char** pptr, uint18_t* insp, uint18_t* dstp) 264 | { 265 | char* ptr = *pptr; 266 | char* word; 267 | uint18_t value = 0; 268 | int n = 0; 269 | int ins; 270 | int has_dest = 0; 271 | 272 | while(isblank(*ptr) || (*ptr == '(')) { 273 | while(isblank(*ptr)) ptr++; 274 | if (*ptr == '(') { 275 | ptr++; 276 | while(*ptr && (*ptr != ')')) 277 | ptr++; 278 | if (*ptr) ptr++; 279 | } 280 | } 281 | 282 | if ( (*ptr == '\\') && (isblank(*(ptr+1)) || (*(ptr+1)=='\0')) ) { 283 | while(*ptr != '\0') ptr++; // skip rest 284 | } 285 | word = ptr; 286 | // fprintf(stderr, "WORD [%s]", word); 287 | while (*ptr && !isblank(*ptr) && (*ptr != ':')) { ptr++; n++; } 288 | if (n == 0) return TOKEN_EMPTY; 289 | // first check mnemonic 290 | ins = parse_mnemonic(word, n); 291 | // check reset is destination 292 | switch(ins) { 293 | case -1: 294 | has_dest = 0; 295 | ptr = word; 296 | break; 297 | case INS_PJUMP: 298 | case INS_PCALL: 299 | case INS_NEXT: 300 | case INS_IF: 301 | case INS_MINUS_IF: 302 | if (*ptr == ':') { // force? 303 | has_dest = 1; 304 | ptr++; 305 | } 306 | break; 307 | default: 308 | has_dest = 0; 309 | break; 310 | } 311 | 312 | // parse number or dest 313 | if (parse_symbol(&ptr, &value)) 314 | n = 1; 315 | else { 316 | n = 0; 317 | while(isxdigit(*ptr)) { 318 | value <<= 4; 319 | if ((*ptr >= '0') && (*ptr <= '9')) 320 | value += (*ptr-'0'); 321 | else if ((*ptr >= 'A') && (*ptr <= 'F')) 322 | value += ((*ptr-'A')+10); 323 | else 324 | value += ((*ptr-'a')+10); 325 | ptr++; 326 | n++; 327 | } 328 | } 329 | *pptr = ptr; 330 | if (ins >= 0) { 331 | *insp = ins; 332 | *dstp = value; 333 | if (has_dest && (n > 0)) 334 | return TOKEN_MNEMONIC2; 335 | return TOKEN_MNEMONIC1; 336 | } 337 | if ((n == 0) || !(isblank(*ptr) || (*ptr=='\0')) ) 338 | return TOKEN_ERROR; 339 | *insp = value; 340 | return TOKEN_VALUE; 341 | } 342 | 343 | 344 | 345 | static uint18_t f18_read_ioreg(node_t* np, uint18_t ioreg) 346 | { 347 | uint18_t io_rd[4]; 348 | uint18_t dd = 0; 349 | int fds[4]; 350 | int fd; 351 | fd_set readfds; 352 | int nfds; 353 | char buf[256]; 354 | char* ptr; 355 | uint18_t ins; 356 | uint18_t insx; 357 | uint18_t dest; 358 | uint18_t value; 359 | int nports; 360 | int i; 361 | int r; 362 | 363 | if (ioreg == IOREG_IO) { 364 | uint18_t io_wr[4]; 365 | fd_set writefds; 366 | struct timeval tm = {0, 0}; 367 | 368 | if ((nports = select_ports(np, IOREG_RDLU, 1, fds, io_rd, io_wr))==0) 369 | return 0; 370 | FD_ZERO(&readfds); 371 | FD_ZERO(&writefds); 372 | nfds = 0; 373 | for (i = 0; i < nports; i++) { 374 | FD_SET(fds[i], &readfds); 375 | FD_SET(fds[i], &writefds); 376 | if (fds[i] >= nfds) 377 | nfds = fds[i]+1; 378 | } 379 | if (select(nfds, &readfds, &writefds, NULL, &tm) < 0) 380 | goto error; 381 | value = 0; 382 | for (i = 0; i < nports; i++) { 383 | if (FD_ISSET(fds[i], &readfds)) // otherside is writing 384 | value |= io_wr[i]; 385 | if (!FD_ISSET(fds[i], &writefds)) // other side is reading 386 | value |= io_rd[i]; 387 | } 388 | return value; 389 | } 390 | 391 | if ((nports = select_ports(np, ioreg, 1, fds, io_rd, NULL)) == 0) { 392 | fprintf(stderr, "io error when writing ioreg=%x, not mapped\n", ioreg); 393 | return 0; 394 | } 395 | VERBOSE(np, "node %p selected %d ports\n", np, nports); 396 | 397 | if (ioreg == IOREG_TTY) { // special for character i/o 398 | uint8_t c; 399 | if (read(fds[0], (char*) &c, 1) <= 0) 400 | goto error; 401 | return c; 402 | } 403 | 404 | FD_ZERO(&readfds); 405 | nfds = 0; 406 | for (i = 0; i < nports; i++) { 407 | FD_SET(fds[i], &readfds); 408 | if (fds[i] >= nfds) 409 | nfds = fds[i]+1; 410 | } 411 | 412 | // VERBOSE(np,"select nfds=%d\n", nfds); 413 | if (select(nfds, &readfds, NULL, NULL, NULL) <= 0) 414 | goto error; 415 | 416 | fd = -1; 417 | for (i = 0; i < nports; i++) { 418 | if (FD_ISSET(fds[i], &readfds)) { 419 | // just grab the first one that is ready 420 | fd = fds[i]; 421 | dd = io_rd[i]; 422 | goto again; 423 | } 424 | } 425 | fprintf(stderr, "error: no fd is set?\n"); 426 | return 0; 427 | 428 | again: 429 | // read line from fd until '\n' is found or buffer overflow 430 | // VERBOSE(np,"read line fd=%d\n", fd); 431 | if ((fd == 0) && (np->flags & FLAG_TERMINATE)) 432 | return 0; 433 | 434 | // Check binary mode channel 435 | if (dd) { 436 | if ( ((dd == F18_IO_RIGHT_RD) && (np->flags & FLAG_RD_BIN_RIGHT)) || 437 | ((dd == F18_IO_LEFT_RD) && (np->flags & FLAG_RD_BIN_LEFT)) || 438 | ((dd == F18_IO_UP_RD) && (np->flags & FLAG_RD_BIN_UP)) || 439 | ((dd == F18_IO_DOWN_RD) && (np->flags & FLAG_RD_BIN_DOWN))) { 440 | r = read(fd, &value, sizeof(value)); 441 | if (r == 0) { // input stream has closed 442 | if (fd == 0) // it was stdin ! 443 | np->flags |= FLAG_TERMINATE; // lets terminate 444 | } 445 | else if (r < 0) goto error; 446 | return value; 447 | } 448 | } 449 | 450 | // Interpreter mode channel 451 | i = 0; 452 | while(i < (sizeof(buf)-1)) { 453 | r = read(fd, &buf[i], 1); 454 | if (r == 0) { // input stream has closed 455 | if (fd == 0) // it was stdin ! 456 | np->flags |= FLAG_TERMINATE; // lets terminate 457 | break; 458 | } 459 | else if (r < 0) 460 | goto error; 461 | else if (buf[i] == '\n') 462 | break; 463 | i++; 464 | } 465 | buf[i] = '\0'; 466 | VERBOSE(np,"read fd=%d [%s]\n", fd, buf); 467 | ptr = buf; 468 | i = parse_ins(&ptr, &insx, &dest); 469 | switch(i) { 470 | case TOKEN_EMPTY: goto again; 471 | case TOKEN_MNEMONIC1: ins = (insx << 13); break; 472 | case TOKEN_MNEMONIC2: 473 | // instruction part is encoded (^IMASK) but dest is not (why) 474 | ins = (insx << 13) ^ IMASK; // encode instruction 475 | ins = (ins & ~MASK10) | (dest & MASK10); // set address bits 476 | return ins; 477 | case TOKEN_VALUE: 478 | return (insx & MASK18); // value not encoded 479 | default: goto error; 480 | } 481 | i = parse_ins(&ptr, &insx, &dest); 482 | switch(i) { 483 | case TOKEN_EMPTY: // assume rest of opcode are nops (warn?) 484 | ins = (ins | (INS_NOP<<8) | (INS_NOP<<3) | (INS_NOP>>2)) ^ IMASK; 485 | return ins; 486 | case TOKEN_MNEMONIC1: ins |= (insx << 8); break; 487 | case TOKEN_MNEMONIC2: 488 | ins = (ins | (insx << 8)) ^ IMASK; // encode instruction 489 | ins = (ins & ~MASK8) | (dest & MASK8); // set address bits 490 | return ins; 491 | default: goto error; 492 | } 493 | i = parse_ins(&ptr, &insx, &dest); 494 | switch(i) { 495 | case TOKEN_EMPTY: 496 | ins = (ins | (INS_NOP<<3) | (INS_NOP>>2)) ^ IMASK; 497 | return ins; 498 | case TOKEN_MNEMONIC1: ins |= (insx << 3); break; 499 | case TOKEN_MNEMONIC2: 500 | ins = (ins | (insx << 3)) ^ IMASK; // encode instruction 501 | ins = (ins & ~MASK3) | (dest & MASK3); // set address bits 502 | return ins; 503 | default: goto error; 504 | } 505 | i = parse_ins(&ptr, &insx, &dest); 506 | switch(i) { 507 | case TOKEN_EMPTY: 508 | ins = (ins | (INS_NOP>>2)) ^ IMASK; 509 | return ins; 510 | case TOKEN_MNEMONIC1: 511 | if ((insx & 3) != 0) 512 | fprintf(stderr, "scan error: bad slot3 instruction used %s\n", 513 | f18_ins_name[insx]); 514 | ins = (ins | (insx >> 2)) ^ IMASK; // add op and encode 515 | return ins; 516 | default: goto error; 517 | } 518 | 519 | error: 520 | fprintf(stderr, "io error when reading ioreg=%x, error=%s\n", 521 | ioreg, strerror(errno)); 522 | return 0; 523 | } 524 | 525 | 526 | SIGRETTYPE (*sys_sigset(int sig, SIGRETTYPE (*func)(int)))(int) 527 | { 528 | struct sigaction act, oact; 529 | 530 | sigemptyset(&act.sa_mask); 531 | act.sa_flags = 0; 532 | act.sa_handler = func; 533 | sigaction(sig, &act, &oact); 534 | return(oact.sa_handler); 535 | } 536 | 537 | void sys_sigblock(int sig) 538 | { 539 | sigset_t mask; 540 | 541 | sigemptyset(&mask); 542 | sigaddset(&mask, sig); 543 | sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL); 544 | } 545 | 546 | void sys_sigrelease(int sig) 547 | { 548 | sigset_t mask; 549 | 550 | sigemptyset(&mask); 551 | sigaddset(&mask, sig); 552 | sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); 553 | } 554 | 555 | static int tty_set(int fd) 556 | { 557 | if (tcsetattr(fd, TCSANOW, &tty_smode) < 0) 558 | return -1; 559 | return 0; 560 | } 561 | 562 | static int tty_reset(int fd) 563 | { 564 | if (tcsetattr(fd, TCSANOW, &tty_rmode) < 0) 565 | return -1; 566 | return 0; 567 | } 568 | 569 | static SIGRETTYPE suspend(int sig) 570 | { 571 | tty_reset(tty_fd); 572 | 573 | sys_sigset(sig, SIG_DFL); /* Set signal handler to default */ 574 | sys_sigrelease(sig); /* Allow 'sig' to come through */ 575 | kill(getpid(), sig); /* Send ourselves the signal */ 576 | sys_sigblock(sig); /* Reset to old mask */ 577 | sys_sigset(sig, suspend); /* Reset signal handler */ 578 | 579 | tty_set(tty_fd); 580 | } 581 | 582 | 583 | static SIGRETTYPE ctl_c(int sig) 584 | { 585 | tty_reset(tty_fd); 586 | 587 | sys_sigset(sig, orig_ctl_c); /* Set ctl_c break handler to original */ 588 | sys_sigrelease(sig); /* Allow 'sig' to come through */ 589 | kill(getpid(), sig); /* Send ourselves the signal */ 590 | sys_sigblock(sig); /* Reset to old mask */ 591 | sys_sigset(sig, ctl_c); /* Reset signal handler */ 592 | 593 | tty_set(tty_fd); 594 | } 595 | 596 | 597 | // Setup termial file descriptor "io port" 598 | 599 | int tty_init(int fd) 600 | { 601 | if (tcgetattr(fd, &tty_rmode) < 0) 602 | return -1; 603 | tty_smode = tty_rmode; 604 | 605 | // Default characteristics for all usage including termcap output. 606 | tty_smode.c_iflag &= ~ISTRIP; 607 | 608 | tty_smode.c_iflag |= ICRNL; // cr -> nl on input 609 | tty_smode.c_lflag &= ~ICANON; 610 | tty_smode.c_oflag |= OPOST; // nl -> cr-nl .. 611 | // Must get these really right or funny effects can occur. 612 | tty_smode.c_cc[VMIN] = 1; 613 | tty_smode.c_cc[VTIME] = 0; 614 | #ifdef VDSUSP 615 | tty_smode.c_cc[VDSUSP] = 0; 616 | #endif 617 | tty_smode.c_cflag &= ~(CSIZE | PARENB); // clear char-size,disable parity 618 | tty_smode.c_cflag |= CS8; // 8-bit 619 | tty_smode.c_lflag &= ~ECHO; // no echo 620 | 621 | if (tty_set(fd) < 0) { 622 | tty_fd = -1; 623 | tty_reset(fd); 624 | return -1; 625 | } 626 | tty_fd = fd; 627 | 628 | sys_sigset(SIGTSTP, suspend); 629 | sys_sigset(SIGTTIN, suspend); 630 | sys_sigset(SIGTTOU, suspend); 631 | orig_ctl_c = sys_sigset(SIGINT, ctl_c); 632 | return 0; 633 | } 634 | 635 | 636 | void usage(char* prog) 637 | { 638 | fprintf(stderr, "usage: %s [options]\n", prog); 639 | fprintf(stderr, " options:\n" 640 | " -v Enable verbose (if debug compiled)\n" 641 | " -t Enable trace (if debug compiled)\n" 642 | " -D regs,ram,r,s Dump registers,ram,return-stack,data-stack\n" 643 | " -d Set delay between instructions (in usecs)\n" 644 | " -l VxH Set processor mesh layout (max 8x18)\n"); 645 | exit(1); 646 | } 647 | 648 | void* f18_emu_start(void *arg) 649 | { 650 | node_data_t* dp = (node_data_t*) arg; 651 | 652 | VERBOSE(dp->np, "node %p [%d,%d] started\r\n", dp->np, dp->i, dp->j); 653 | f18_emu(dp->np); 654 | return NULL; 655 | } 656 | 657 | // 658 | // Start F18 emulator 659 | // -v verbose (if debug compiled) 660 | // -t trace (if debug comipled) 661 | // -d delay Set delay between instructions 662 | // -l VxH processor layout (max 8x18) 663 | // 664 | 665 | int main(int argc, char** argv) 666 | { 667 | node_t n; 668 | int fd; 669 | int c; 670 | int i,j; 671 | useconds_t delay = 0; 672 | char* opt_layout = NULL; 673 | uint32_t h=1, v=1; 674 | void* node_mem; 675 | uint8_t* np_mem; 676 | size_t alloc_size; 677 | 678 | g_page_size = sysconf(_SC_PAGESIZE); // must be first! 679 | g_flags = 0; 680 | 681 | while((c=getopt(argc, argv, "vtl:d:D:")) != -1) { 682 | switch(c) { 683 | case 'v': 684 | g_flags |= FLAG_VERBOSE; 685 | break; 686 | case 't': 687 | g_flags |= FLAG_TRACE; 688 | break; 689 | case 'l': 690 | opt_layout = optarg; 691 | break; 692 | case 'd': { 693 | char* endptr = NULL; 694 | if ((delay = strtol(optarg, &endptr,0)) == 0) { 695 | if (endptr && (*endptr != '\0')) 696 | usage(basename(argv[0])); 697 | } 698 | break; 699 | } 700 | case 'D': { 701 | char* ptr = optarg; 702 | while(*ptr) { 703 | if (strncmp(ptr, "reg", 3) == 0) { 704 | if ((ptr[3] == '\0') || (ptr[3] == ',')) { 705 | g_flags |= FLAG_DUMP_REG; 706 | ptr += (ptr[3] == ',') ? 4 : 3; 707 | } 708 | else 709 | usage(basename(argv[0])); 710 | } 711 | else if (strncmp(ptr, "ram", 3) == 0) { 712 | if ((ptr[3] == '\0') || (ptr[3] == ',')) { 713 | g_flags |= FLAG_DUMP_RAM; 714 | ptr += (ptr[3] == ',') ? 4 : 3; 715 | } 716 | else 717 | usage(basename(argv[0])); 718 | } 719 | else if (strncmp(ptr, "rs", 2) == 0) { 720 | if ((ptr[2] == '\0') || (ptr[2] == ',')) { 721 | g_flags |= FLAG_DUMP_RS; 722 | ptr += (ptr[2] == ',') ? 3 : 2; 723 | } 724 | else 725 | usage(basename(argv[0])); 726 | } 727 | else if (strncmp(ptr, "ds", 2) == 0) { 728 | if ((ptr[2] == '\0') || (ptr[2] == ',')) { 729 | g_flags |= FLAG_DUMP_DS; 730 | ptr += (ptr[2] == ',') ? 3 : 2; 731 | } 732 | else 733 | usage(basename(argv[0])); 734 | } 735 | else 736 | usage(basename(argv[0])); 737 | } 738 | break; 739 | } 740 | case '?': 741 | default: 742 | usage(basename(argv[0])); 743 | } 744 | } 745 | 746 | argc -= optind; 747 | argv += optind; 748 | 749 | if ((fd = open("/dev/tty", O_RDWR)) < 0) { 750 | fprintf(stderr, "unabled to open tty, error=%s\n", 751 | strerror(errno)); 752 | exit(1); 753 | } 754 | if (tty_init(fd) < 0) { 755 | fprintf(stderr, "unabled to setup tty, error=%s\n", 756 | strerror(errno)); 757 | exit(1); 758 | } 759 | 760 | if (opt_layout) { 761 | char* ptr = opt_layout; 762 | v = strtol(ptr,&ptr,0); 763 | if ((v != 0) && (*ptr == 'x')) { 764 | ptr++; 765 | h = strtol(ptr,&ptr,0); 766 | if ((h == 0) || (ptr && (*ptr != '\0'))) 767 | usage(basename(argv[0])); 768 | } 769 | else if ((v != 0) && (*ptr == '\0')) { 770 | // when only one numer is given assume it is 1xH 771 | h = v; v = 1; 772 | } 773 | else if ((v == 0) && (*ptr != '\0')) 774 | usage(basename(argv[0])); 775 | if ((v > 8) || (h > 18)) 776 | usage(basename(argv[0])); 777 | } 778 | 779 | // allocate HxV nodes threads (example 3x3 ) 780 | // A - B - C 781 | // | | | 782 | // D - E - F 783 | // | | | 784 | // G - H - I 785 | // 786 | // connect the lines with socket pairs 787 | // 788 | alloc_size = v*h*(PAGE(NODE_SIZE)); 789 | if (g_flags & FLAG_VERBOSE) { 790 | fprintf(stderr, "page size %ld\n", g_page_size); 791 | fprintf(stderr, "alloc size: %ld\n", alloc_size); 792 | fprintf(stderr, "stack size: %ld bytes\n", PAGE(STACK_SIZE)); 793 | fprintf(stderr, "node size: %ld bytes\n", PAGE(NODE_SIZE)); 794 | fprintf(stderr, "sizeof(node_t): %lu\n", sizeof(node_t)); 795 | fprintf(stderr, "layout: %d x %d\n", v, h); 796 | } 797 | 798 | if (posix_memalign(&node_mem, g_page_size, alloc_size)) { 799 | perror("posix_memalign (node_mem) failed"); 800 | exit(1); 801 | } 802 | 803 | // reset global node connection 8x18 array 804 | memset(node, 0, sizeof(node)); 805 | 806 | // start moving memory into the node data 807 | np_mem = (uint8_t*) node_mem; 808 | for (i = 0; i < v; i++) { 809 | for (j = 0; j < h; j++) { 810 | node_data_t* dp = &node[i][j]; 811 | node_t* np = (node_t*) np_mem; 812 | dp->i = i; 813 | dp->j = j; 814 | dp->np = np; 815 | // dp->stack = (void*) (np_mem + PAGE(NODE_SIZE)); 816 | 817 | memset(np, 0, sizeof(node_t)); 818 | np->flags = g_flags; // specific for node? 819 | np->delay = delay; // specific for node? 820 | np->up_fd = -1; 821 | np->left_fd = -1; 822 | np->down_fd = -1; 823 | np->right_fd = -1; 824 | np->tty_fd = tty_fd; 825 | np->stdin_fd = 0; 826 | np->stdout_fd = 1; 827 | if ((i == 0) && (j == 0)) 828 | np->p = IOREG_STDIO; 829 | else 830 | np->p = IOREG_RDLU; 831 | np->b = IOREG_TTY; 832 | np->read_ioreg = f18_read_ioreg; 833 | np->write_ioreg = f18_write_ioreg; 834 | 835 | np_mem += (PAGE(NODE_SIZE)); 836 | } 837 | } 838 | // create all the socket pairs needed 839 | for (i = 0; i < v; i++) { 840 | for (j = 0; j < h; j++) { 841 | if (j > 0) { // create left/right 842 | int lr[2]; 843 | if (socketpair(PF_LOCAL, SOCK_STREAM, 0, lr) < 0) { 844 | perror("socketpair lr"); 845 | exit(1); 846 | } 847 | node[i][j-1].np->right_fd = lr[0]; 848 | node[i][j].np->left_fd = lr[1]; 849 | } 850 | if (i > 0) { // create up/down 851 | int ud[2]; 852 | if (socketpair(PF_LOCAL, SOCK_STREAM, 0, ud) < 0) { 853 | perror("socketpair ud"); 854 | exit(1); 855 | } 856 | node[i-1][j].np->down_fd = ud[0]; 857 | node[i][j].np->up_fd = ud[1]; 858 | } 859 | } 860 | } 861 | 862 | for (i=0; i < v; i++) { 863 | for (j = 0; j < h; j++) { 864 | node_data_t* dp = &node[i][j]; 865 | pthread_attr_init(&dp->attr); 866 | pthread_attr_setstacksize(&dp->attr, PAGE(STACK_SIZE)); 867 | if (g_flags & FLAG_VERBOSE) { 868 | size_t size; 869 | pthread_attr_getstacksize(&dp->attr, &size); 870 | fprintf(stderr, "thread stack size: %ld\n", size); 871 | } 872 | VERBOSE(dp->np, "about to start node %p [%d,%d]\r\n", 873 | dp->np, dp->i, dp->j); 874 | if (pthread_create(&dp->thread,&dp->attr,f18_emu_start,(void*) dp) <0) { 875 | perror("pthread_create"); 876 | exit(1); 877 | } 878 | } 879 | } 880 | 881 | while(1) { 882 | // printf("Z\n"); 883 | sleep(10); 884 | } 885 | tty_reset(tty_fd); // atexit? 886 | exit(0); 887 | } 888 | -------------------------------------------------------------------------------- /src/f18.h: -------------------------------------------------------------------------------- 1 | #ifndef __F18_H__ 2 | #define __F18_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define INS_RETURN 0x00 // ; 10 | #define INS_EXECUTE 0x01 // ex 11 | #define INS_PJUMP 0x02 // jump <10-bit> 12 | #define INS_PCALL 0x03 // call <10-bit> 13 | #define INS_UNEXT 0x04 // micronext 14 | #define INS_NEXT 0x05 // next <10-bit> 15 | #define INS_IF 0x06 // if <10-bit> 16 | #define INS_MINUS_IF 0x07 // -if <10-bit> 17 | #define INS_FETCH_P 0x08 // @p 18 | #define INS_FETCH_PLUS 0x09 // @+ 19 | #define INS_FETCH_B 0x0a // @b 20 | #define INS_FETCH 0x0b // @ 21 | #define INS_STORE_P 0x0c // !p 22 | #define INS_STORE_PLUS 0x0d // !+ 23 | #define INS_STORE_B 0x0e // !b 24 | #define INS_STORE 0x0f // ! 25 | #define INS_MULT_STEP 0x10 // +* 26 | #define INS_TWO_STAR 0x11 // 2* 27 | #define INS_TWO_SLASH 0x12 // 2/ 28 | #define INS_NOT 0x13 // - 29 | #define INS_PLUS 0x14 // + 30 | #define INS_AND 0x15 // and 31 | #define INS_OR 0x16 // or ALU 1.5 (exclusive or) 32 | #define INS_DROP 0x17 // drop ALU 1.5 33 | #define INS_DUP 0x18 // dup ALU 1.5 34 | #define INS_POP 0x19 // pop ALU 1.5 35 | #define INS_OVER 0x1a // over ALU 1.5 36 | #define INS_A 0x1b // a ALU 1.5 (A to T) 37 | #define INS_NOP 0x1c // . ALU 1.5 “nop” 38 | #define INS_PUSH 0x1d // push ALU 1.5 (from T to R) 39 | #define INS_B_STORE 0x1e // b! ALU 1.5 “b-store” (store into B) 40 | #define INS_A_STORE 0x1f // a! ALU 1.5 “a-store” (store into A) 41 | 42 | 43 | typedef uint32_t uint18_t; // 18 bits packed into 32 bits 44 | typedef uint16_t uint9_t; // 9 bits packed into 16 bits 45 | typedef uint16_t uint10_t; // 10 bits packed into 16 bits 46 | typedef uint8_t uint5_t; // 5 bits packed into 8 bits 47 | typedef uint8_t uint3_t; // 3 bits packed into 8 bits 48 | 49 | // address layout in binary 50 | // 000000000 - 000111111 RAM 51 | // 001000000 - 001111111 RAM* (repeat) 52 | // 010000000 - 010111111 ROM 53 | // 011000000 - 011111111 ROM* (repeat) 54 | // 100000000 - 111111111 IOREG 55 | 56 | #define RAM_START 0x000 57 | #define RAM_END 0x03f 58 | #define RAM_END2 0x07f 59 | #define ROM_START 0x080 60 | #define ROM_END 0x0BF 61 | #define ROM_END2 0x0FF 62 | #define IOREG_START 0x100 63 | #define IOREG_END 0x1FF 64 | 65 | #define IOREG_STDIN 0x100 // test/debug 66 | #define IOREG_STDOUT 0x101 // test/debug 67 | #define IOREG_STDIO 0x102 // test/debug 68 | #define IOREG_TTY 0x103 // test/debug 69 | 70 | #define IOREG_IO 0x15D // i/o control and status 71 | #define IOREG_DATA 0x141 72 | #define IOREG____U 0x145 // up 73 | #define IOREG___L_ 0x175 // left 74 | #define IOREG___LU 0x165 // left or up 75 | #define IOREG__D__ 0x115 // down 76 | #define IOREG__D_U 0x105 77 | #define IOREG__DL_ 0x135 78 | #define IOREG__DLU 0x125 79 | #define IOREG_R___ 0x1D5 // right 80 | #define IOREG_R__U 0x1C5 81 | #define IOREG_R_L_ 0x1F5 82 | #define IOREG_R_LU 0x1E5 83 | #define IOREG_RD__ 0x195 84 | #define IOREG_RD_U 0x185 85 | #define IOREG_RDL_ 0x1B5 86 | #define IOREG_RDLU 0x1A5 87 | 88 | // IO register number coding 89 | #define F18_DIR_BITS 0x105 // Direction pattern 90 | #define F18_DIR_MASK 0x10F // Direction pattern 91 | #define F18_RIGHT_BIT 0x080 // right when 1 92 | #define F18_DOWN_BIT 0x040 // down when 0 93 | #define F18_LEFT_BIT 0x020 // left when 1 94 | #define F18_UP_BIT 0x010 // up when 0 95 | 96 | #define F18_IO_PIN17 0x20000 97 | // port status bits in io register 98 | #define F18_IO_RIGHT_RD 0x10000 99 | #define F18_IO_RIGHT_WR 0x08000 100 | #define F18_IO_DOWN_RD 0x04000 101 | #define F18_IO_DOWN_WR 0x02000 102 | #define F18_IO_LEFT_RD 0x01000 103 | #define F18_IO_LEFT_WR 0x00800 104 | #define F18_IO_UP_RD 0x00400 105 | #define F18_IO_UP_WR 0x00200 106 | // ... 107 | #define F18_IO_PIN5 0x00020 108 | #define F18_IO_PIN3 0x00008 109 | #define F18_IO_PIN1 0x00002 110 | 111 | #define P9 0x200 // P9 bit 112 | #define SIGN_BIT 0x20000 // 18 bit sign bit 113 | #define MASK3 0x7 114 | #define MASK8 0xff 115 | #define MASK9 0x1ff 116 | #define MASK10 0x3ff 117 | #define MASK18 0x3ffff 118 | #define MASK19 0x7ffff 119 | #define IMASK 0x15555 // exeucte/compiler mask 120 | 121 | #define SIGNED18(v) (((int32_t)((v)<<14))>>14) 122 | 123 | #define FLAG_VERBOSE 0x00001 124 | #define FLAG_TRACE 0x00002 125 | #define FLAG_TERMINATE 0x00004 126 | #define FLAG_DUMP_REG 0x00010 127 | #define FLAG_DUMP_RAM 0x00020 128 | #define FLAG_DUMP_RS 0x00040 129 | #define FLAG_DUMP_DS 0x00080 130 | #define FLAG_RD_BIN_RIGHT 0x00800 131 | #define FLAG_RD_BIN_DOWN 0x00400 132 | #define FLAG_RD_BIN_LEFT 0x00200 133 | #define FLAG_RD_BIN_UP 0x00100 134 | #define FLAG_WR_BIN_RIGHT 0x08000 135 | #define FLAG_WR_BIN_LEFT 0x04000 136 | #define FLAG_WR_BIN_DOWN 0x02000 137 | #define FLAG_WR_BIN_UP 0x01000 138 | 139 | // 140 | // sizeof(node_t) = 652 bytes (update me now and then) 141 | // total ram usage for threads 93888 bytes. 142 | // with page size of 4096*144 = 589824 bytes 143 | // 144 | typedef struct _node_t { 145 | uint18_t ram[64]; 146 | const uint18_t rom[64]; 147 | uint18_t io; // io status register (read) 148 | 149 | useconds_t delay; // delay between instructions 150 | uint18_t flags; // flags,debug,trace... 151 | 152 | // FILE/PIPE/SOCKETS 153 | int up_fd; 154 | int left_fd; 155 | int down_fd; 156 | int right_fd; 157 | 158 | int tty_fd; 159 | int stdin_fd; 160 | int stdout_fd; 161 | 162 | // System dependent functions 163 | uint18_t (*read_ioreg)(struct _node_t* np, uint18_t reg); 164 | void (*write_ioreg)(struct _node_t* np, uint18_t reg, uint18_t val); 165 | 166 | uint18_t t; // top of data stack 167 | uint18_t s; // second of data stack 168 | uint18_t ds[8]; // data stack 169 | uint3_t sp; // data stack pointer 170 | 171 | uint18_t r; // return top of return stack 172 | uint18_t rs[8]; // return stack 173 | uint3_t rp; // return stack pointer 174 | 175 | uint18_t i; // instruction register 176 | uint10_t p; // program counter 177 | uint18_t a; // address register 178 | uint9_t b; // 179 | uint8_t c; // carry flag 180 | } node_t; 181 | 182 | 183 | #ifdef DEBUG 184 | #define VERBOSE(np,fmt,...) do { \ 185 | if ((np)->flags & FLAG_VERBOSE) \ 186 | fprintf(stdout, fmt, __VA_ARGS__); \ 187 | } while(0) 188 | #define TRACE(np,fmt, ...) do { \ 189 | if ((np)->flags & FLAG_TRACE) \ 190 | fprintf(stdout, fmt, __VA_ARGS__); \ 191 | } while(0) 192 | #define DELAY(np) do { \ 193 | if ((np)->delay) \ 194 | usleep((np)->delay); \ 195 | } while(0) 196 | #else 197 | #define VERBOSE(np,fmt, ...) 198 | #define TRACE(np,fmt, ...) 199 | #define DELAY(np) 200 | #endif 201 | 202 | extern char* f18_ins_name[]; 203 | 204 | extern void f18_emu(node_t* p); 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /src/f18_emu.c: -------------------------------------------------------------------------------- 1 | // 2 | // Simple F18 emulator 3 | // 4 | #include "f18.h" 5 | 6 | char* f18_ins_name[] = { 7 | ";", "ex", "jump", "call", "unext", "next", "if", "-if", 8 | "@p", "@+", "@b", "@", "!p", "!+", "!b", "!", 9 | "+*","2*","2/","-","+","and","or", "drop", 10 | "dup","pop","over","a",".","push","b!","a!" 11 | }; 12 | 13 | // I do not think it matters in the emulator which way the posh and pop goes 14 | #define PUSH_ds(np,val) (np)->ds[(np)->sp++ & 0x7] = (val) 15 | #define POP_ds(np) (np)->ds[--(np)->sp & 0x7] 16 | 17 | #define PUSH_s(np, val) do { \ 18 | PUSH_ds((np), (np)->s); \ 19 | (np)->s = (np)->t; \ 20 | (np)->t = (val); \ 21 | } while(0) 22 | 23 | #define POP_s(np) do { \ 24 | (np)->t = (np)->s; \ 25 | (np)->s = POP_ds(np); \ 26 | } while(0) 27 | 28 | #define PUSH_rs(np,val) (np)->rs[(np)->rp++ & 0x7] = (val) 29 | #define POP_rs(np) (np)->rs[--(np)->rp & 0x7] 30 | 31 | #define PUSH_r(np, val) do { \ 32 | PUSH_rs((np), (np)->r); \ 33 | (np)->r = (val); \ 34 | } while(0) 35 | 36 | #define POP_r(np) do { \ 37 | (np)->r = POP_rs(np); \ 38 | } while(0) 39 | 40 | #define swap18(a,b) do { \ 41 | uint18_t _swap18_t1 = (a); \ 42 | (a) = (b); \ 43 | (b) = _swap18_t1; \ 44 | } while(0) 45 | 46 | #ifdef DEBUG 47 | #define DUMP(np) do { \ 48 | if ((np)->flags & FLAG_DUMP_REG) dump_reg((np)); \ 49 | if ((np)->flags & FLAG_DUMP_DS) dump_ds((np)); \ 50 | if ((np)->flags & FLAG_DUMP_RS) dump_rs((np)); \ 51 | if ((np)->flags & FLAG_DUMP_RAM) dump_ram((np)); \ 52 | } while(0) 53 | #else 54 | #define DUMP(np) 55 | #endif 56 | 57 | 58 | 59 | static void dump_ram(node_t* np) 60 | { 61 | int i; 62 | for (i = RAM_START; i<=RAM_END; i++) 63 | fprintf(stdout, "ram[%d]=%05x\n", i, np->ram[i]); 64 | } 65 | 66 | static void dump_ds(node_t* np) 67 | { 68 | int i; 69 | fprintf(stdout, "t=%05x,s=%05x",np->t,np->s); 70 | for (i=0; i<8; i++) 71 | fprintf(stdout, ",%05x", np->ds[(np->sp+i-1)&0x7]); 72 | fprintf(stdout, "\n"); 73 | } 74 | 75 | 76 | static void dump_rs(node_t* np) 77 | { 78 | int i; 79 | fprintf(stdout, "r=%05x",np->r); 80 | for (i=0; i<8; i++) 81 | fprintf(stdout, ",%05x", np->rs[(np->rp+i-1)&0x7]); 82 | fprintf(stdout, "\n"); 83 | } 84 | 85 | static void dump_reg(node_t* np) 86 | { 87 | fprintf(stdout, "t=%05x,a=%05x,b=%03x,c=%x,p=%x,i=%x,s=%05x,r=%05x\n", 88 | np->t, np->a, np->b, np->c, np->p, np->i, np->s, np->r); 89 | } 90 | 91 | // read value of P return the current value and 92 | // perform auto increment if needed. 93 | 94 | static uint9_t p_auto(node_t* np) 95 | { 96 | uint9_t p = np->p & MASK9; // strip P(9) 97 | if ((p >= RAM_START) && (p <= RAM_END2)) 98 | np->p = ((p + 1) & 0x7f) | (np->p & P9); 99 | else if ((p >= ROM_START) && (p <= ROM_END2)) 100 | np->p = (ROM_START + (((p - ROM_START) + 1) & 0x7f)) | (np->p & P9); 101 | return p; 102 | } 103 | 104 | static uint9_t a_auto(node_t* np) 105 | { 106 | uint9_t a = np->a & MASK9; 107 | 108 | if ((a >= RAM_START) && (a <= RAM_END2)) 109 | np->a = ((a + 1) & 0x7f); 110 | else if ((a >= ROM_START) && (a <= ROM_END2)) 111 | np->a = (ROM_START + (((a - ROM_START) + 1) & 0x7f)); 112 | return a; 113 | } 114 | 115 | static uint18_t read_mem(node_t* np, uint18_t addr) 116 | { 117 | uint18_t value; 118 | if (addr <= RAM_END2) { 119 | value = np->ram[addr & 0x3f]; 120 | VERBOSE(np,"read ram[%x] = %x\n", addr, value); 121 | } 122 | else if (addr <= ROM_END2) { 123 | value = np->rom[(addr-ROM_START) & 0x3f]; 124 | VERBOSE(np,"read rom[%x] = %x\n", addr, value); 125 | } 126 | else { 127 | value = (*np->read_ioreg)(np, addr); 128 | VERBOSE(np,"read ioreg[%x] = %x\n", addr, value); 129 | } 130 | return value; 131 | } 132 | 133 | static void write_mem(node_t* np, uint18_t addr, uint18_t val) 134 | { 135 | if (addr <= RAM_END2) { 136 | np->ram[addr & 0x3f] = val; 137 | VERBOSE(np,"write ram[%04x] = %02x %02x %02x %02x = %x\n", 138 | addr & 0x3f, 139 | (val >> 13) & 0x1f, 140 | (val >> 8) & 0x1f, 141 | (val >> 3) & 0x1f, 142 | (val << 2) & 0x1f, 143 | val); 144 | } 145 | else if (addr <= ROM_END2) { 146 | fprintf(stderr, "warning: try to write in ROM area %x, value=%d\n", 147 | addr, val); 148 | // np->rom[(addr-ROM_START) & 0x3f] = val; 149 | } 150 | else { 151 | (*np->write_ioreg)(np, addr, val); 152 | VERBOSE(np,"write ioreg[%04x] = %x\n", addr, val); 153 | } 154 | } 155 | 156 | void f18_emu(node_t* np) 157 | { 158 | uint18_t I; 159 | uint32_t II; 160 | int n; 161 | uint5_t ins; 162 | 163 | next: 164 | DUMP(np); 165 | np->i = read_mem(np, p_auto(np)); 166 | if (np->flags & FLAG_TERMINATE) 167 | return; 168 | I = np->i ^ IMASK; // "decode" instruction (why?) 169 | 170 | restart: 171 | II = I << 2; 172 | n = 4; 173 | unext: 174 | DUMP(np); 175 | ins = (II >> 15) & 0x1f; 176 | TRACE(np, "execute %s\n", f18_ins_name[ins]); 177 | DELAY(np); 178 | 179 | switch(ins) { 180 | case INS_RETURN: 181 | np->p = np->r; 182 | POP_r(np); 183 | goto next; 184 | 185 | case INS_EXECUTE: 186 | swap18(np->p, np->r); 187 | np->p &= MASK10; // maske sure P is 10 bits 188 | goto next; 189 | 190 | case INS_PJUMP: 191 | goto load_p; 192 | 193 | case INS_PCALL: 194 | PUSH_r(np, np->p); 195 | goto load_p; 196 | 197 | case INS_UNEXT: 198 | if (np->r == 0) 199 | POP_r(np); 200 | else { 201 | np->r--; 202 | goto restart; 203 | } 204 | break; 205 | 206 | case INS_NEXT: 207 | if (np->r == 0) 208 | POP_r(np); 209 | else { 210 | np->r--; 211 | goto load_p; 212 | } 213 | goto next; 214 | 215 | case INS_IF: // if ( x -- x ) jump if x == 0 216 | if (np->t == 0) 217 | goto load_p; 218 | goto next; 219 | 220 | case INS_MINUS_IF: // -if ( x -- x ) jump if x >= 0 221 | if (SIGNED18(np->t) >= 0) 222 | goto load_p; 223 | goto next; 224 | 225 | case INS_FETCH_P: // @p ( -- x ) fetch via P auto-increament 226 | PUSH_s(np, read_mem(np, p_auto(np))); 227 | break; 228 | 229 | case INS_FETCH_PLUS: // @+ ( -- x ) fetch via A auto-increament 230 | PUSH_s(np, read_mem(np, a_auto(np))); 231 | break; 232 | 233 | case INS_FETCH_B: // @b ( -- x ) fetch via B 234 | PUSH_s(np, read_mem(np, np->b)); 235 | break; 236 | 237 | case INS_FETCH: // @ ( -- x ) fetch via A 238 | PUSH_s(np, read_mem(np, np->a)); 239 | break; 240 | 241 | case INS_STORE_P: // !p ( x -- ) store via P auto increment 242 | write_mem(np, p_auto(np), np->t); 243 | POP_s(np); 244 | break; 245 | 246 | case INS_STORE_PLUS: // !+ ( x -- ) \ write T in [A] pop data stack, inc A 247 | write_mem(np, a_auto(np), np->t); 248 | POP_s(np); 249 | break; 250 | 251 | case INS_STORE_B: // !b ( x -- ) \ store T into [B], pop data stack 252 | write_mem(np, np->b, np->t); 253 | POP_s(np); 254 | break; 255 | 256 | case INS_STORE: // ! ( x -- ) \ store T info [A], pop data stack 257 | write_mem(np, np->a, np->t); 258 | POP_s(np); 259 | break; 260 | 261 | case INS_MULT_STEP: { // t:a * s 262 | int32_t t = SIGNED18(np->t); 263 | if (np->a & 1) { // sign-extend and add s and t 264 | t += SIGNED18(np->s); 265 | if (np->p & P9) { 266 | t += np->c; 267 | np->c = (t >> 18) & 1; 268 | } 269 | } 270 | np->a = (np->a >> 1) | ((t & 1) << 17); 271 | np->t = ((t >> 1) | (t & SIGN_BIT)) & MASK18; 272 | break; 273 | } 274 | 275 | case INS_TWO_STAR: np->t = (np->t << 1) & MASK18; break; 276 | 277 | case INS_TWO_SLASH: np->t = (np->t >> 1) | (np->t & SIGN_BIT); break; 278 | 279 | case INS_NOT: np->t = (~np->t) & MASK18; break; 280 | 281 | case INS_PLUS: { // + or +c ( x y -- (x+y) ) | ( x y -- (x+y+c) ) 282 | int32_t t = SIGNED18(np->t) + SIGNED18(np->s); 283 | if (np->p & P9) { 284 | t += np->c; 285 | np->c = (t >> 18) & 1; 286 | } 287 | np->t = t & MASK18; 288 | np->s = POP_ds(np); 289 | break; 290 | } 291 | 292 | case INS_AND: // ( x y -- ( x & y) ) 293 | np->t &= np->s; 294 | np->s = POP_ds(np); 295 | break; 296 | 297 | case INS_OR: // ( x y -- ( x ^ y) ) why not named XOR???? 298 | np->t ^= np->s; 299 | np->s = POP_ds(np); 300 | break; 301 | 302 | case INS_DROP: 303 | np->t = np->s; 304 | np->s = POP_ds(np); 305 | break; 306 | 307 | case INS_DUP: // ( x -- x x ) 308 | PUSH_ds(np, np->s); 309 | np->s = np->t; 310 | break; 311 | 312 | case INS_POP: // push R onto data stack and pop return stack 313 | PUSH_s(np, np->r); 314 | np->r = POP_rs(np); 315 | break; 316 | 317 | case INS_OVER: // ( x y -- x y x ) 318 | PUSH_ds(np, np->s); 319 | swap18(np->t, np->s); 320 | break; 321 | 322 | case INS_A: // ( -- A ) push? A onto data stack 323 | PUSH_s(np, np->a); 324 | break; 325 | 326 | case INS_NOP: 327 | break; 328 | 329 | case INS_PUSH: // push T onto return stack and pop data stack 330 | PUSH_r(np, np->t); 331 | POP_s(np); 332 | break; 333 | 334 | case INS_B_STORE: // b! ( x -- ) store into B 335 | np->b = np->t; 336 | POP_s(np); 337 | break; 338 | 339 | case INS_A_STORE: // a! ( x -- ) store into A 340 | np->a = np->t; 341 | POP_s(np); 342 | break; 343 | } 344 | if (--n == 0) 345 | goto next; 346 | II <<= 5; 347 | goto unext; 348 | 349 | load_p: 350 | // destination addresses are unencoded and must are retrieved 351 | // from the "original" i register 352 | switch(n) { 353 | case 4: np->p = np->i & MASK10; break; 354 | case 3: np->p = np->i & MASK8; break; 355 | case 2: np->p = np->i & MASK3; break; 356 | } 357 | goto next; 358 | } 359 | -------------------------------------------------------------------------------- /test/add.f18: -------------------------------------------------------------------------------- 1 | ( Read two number add them and emit result ) 2 | 3 | @p @p + . 4 | 100 5 | 200 6 | !p . . . 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/add_stream.f18: -------------------------------------------------------------------------------- 1 | \ 2 | \ Example that send a loader that load code 3 | \ The will pass a header and then compute on 4 | \ the streamed data (add one to every word) 5 | \ 6 | 7 | @p push . . ( read following length and put on return stack ) 8 | 0d 9 | @p !+ unext . ( read the code into RAM from 0 and up ) 10 | 11 | ( set A to stdio stream ) 12 | ( 0000 ) @p a! . . 13 | ( 0001 ) stdio 14 | 15 | ( set B to stdio stream ) 16 | ( 0002 ) @p b! . . 17 | ( 0003 ) stdio 18 | 19 | ( --- copy header straight from input to output --- ) 20 | ( 0004 ) @p push . . 21 | ( 0005 ) 0 22 | ( write header to stdio ) 23 | ( 0006 ) @ !b unext . 24 | 25 | ( --- process each data and send to output --- ) 26 | ( push data length from stream ) 27 | ( 0007 ) @ push . . 28 | 29 | ( push 1 onto stack ) 30 | ( 0008 ) @p . . . 31 | ( 0009 ) 1 32 | ( 000a ) dup @ + . ( add 1 to data ) 33 | ( 000b ) !b . . . ( write to output ) 34 | ( 000c ) next:000a ( loop over data length ) 35 | ( 000d ) ; ( restart processing! ) 36 | ( start execute the loaded code ) 37 | call:0000 38 | 1234 ( header ) 39 | 0b ( data length-1 ) 40 | 03 41 | 05 42 | 07 43 | 09 44 | 0b 45 | 0d 46 | 01 47 | 02 48 | 04 49 | 06 50 | 08 51 | 0a 52 | -------------------------------------------------------------------------------- /test/add_stream2.f18: -------------------------------------------------------------------------------- 1 | \ 2 | \ Flavour of add_stream, but this will 3 | \ pass the code to from [0,0] to [0,1] (right) 4 | \ after adding 1 [0,1] will shift data to the left 1 step 5 | \ 6 | 7 | @p push . . ( boot-start ) 8 | 0c 9 | @p !+ unext . ( boot-end ) 10 | 11 | ( 0000 ) @p a! . . 12 | ( 0001 ) stdin 13 | ( 0002 ) @p b! . . 14 | ( 0003 ) r--- 15 | ( 0004 ) @ push . . ( read header length from stream ) 16 | ( 0005 ) @ !b unext . ( write header to right ) 17 | ( 0006 ) @ dup push . ( get data length to return stack ) 18 | ( 0007 ) !b . . . ( send a copy of length ) 19 | 20 | ( 0008 ) dup dup or . ( push 1 on to the data stack ) 21 | ( 0009 ) dup @ + . ( add 1 to data ) 22 | ( 000a ) !b . . . ( write to output ) 23 | ( 000b ) next:0008 ( loop over data length ) 24 | ( 000c ) ; ( restart processing! ) 25 | ( start execute the loaded code ) 26 | call:0000 27 | 28 | ( HEADER 1 - code for next node ) 29 | 10 ( header length -1 for all following headers! ) 30 | @p push . . ( boot ) 31 | 0a 32 | @p !+ unext ( boot-end ) 33 | ( 0000 ) @p a! . . 34 | ( 0001 ) --l- ( read from left ) 35 | ( 0002 ) @p b! . . 36 | ( 0003 ) stdout ( write to stdout ) 37 | ( 0004 ) @ push . . ( read header length from stream ) 38 | ( 0005 ) @ !b unext . ( write header to right ) 39 | ( 0006 ) @ push . . ( data length onto return stack ) 40 | ( 0007 ) @ 2* . . ( multiply data with 2 ) 41 | ( 0008 ) !b . . . ( write to output ) 42 | ( 0009 ) next:0007 ( loop over data length ) 43 | ( 000a ) ; ( restart processing! ) 44 | call:0000 45 | ( END-OF-HEADER 1 ) 46 | 47 | ( HEADER 2 - empty ) 48 | 00 49 | dead 50 | ( END-OF-HEADER 2) 51 | 52 | ( DATA ) 53 | 0b ( data length-1 ) 54 | 03 55 | 05 56 | 07 57 | 09 58 | 0b 59 | 0d 60 | 01 61 | 02 62 | 04 63 | 06 64 | 08 65 | 0a 66 | -------------------------------------------------------------------------------- /test/example1.f18: -------------------------------------------------------------------------------- 1 | - push . . 2 | @b !b unext 3 | -------------------------------------------------------------------------------- /test/example2.f18: -------------------------------------------------------------------------------- 1 | @p ! . . 2 | @b !b jump:0000 3 | jump:0000 4 | -------------------------------------------------------------------------------- /test/example3.f18: -------------------------------------------------------------------------------- 1 | @p push . . 2 | 7 3 | @p !+ unext . 4 | ( 0000 ) @p @p push . 5 | ( 0001 ) 60 6 | ( 0002 ) 19 7 | ( 0003 ) @p + dup . 8 | ( 0004 ) 1 9 | ( 0005 ) !b . . . 10 | ( 0006 ) next:0003 11 | ( 0007 ) ; 12 | call:0000 13 | -------------------------------------------------------------------------------- /test/message.f18: -------------------------------------------------------------------------------- 1 | ( LOAD ) 2 | @p push . . 3 | 20 4 | @p !+ unext . 5 | 6 | ( EMIT & SHIFT ) 7 | ( 0000 ) dup . . . 8 | ( 0001 ) @p and !b . 9 | ( 0002 ) 0ff 10 | ( 0003) @p push . . 11 | ( 0004 ) 7 12 | ( 0005 ) 2/ unext ; 13 | 14 | ( 2CHAR ) 15 | ( 0006 ) @p push . . 16 | ( 0007 ) call:0000 ( CHAR ) 17 | ( 0008 ) call:0000 ( CHAR ) 18 | ( 0009 ) drop ; 19 | 20 | ( 4*2CHAR : 2x 2y 2z 2t -- ) 21 | ( 000a ) @p push . . 22 | ( 000b ) 3 23 | ( 000c ) call:0006 ( 2CHAR ) 24 | ( 000d ) next:000c 25 | ( 000e ) ; 26 | 27 | ( MESSAGE ) 28 | ( 000f ) @p @p @p @p 29 | ( 0010 ) 2121 30 | ( 0011 ) 2021 31 | ( 0012 ) 636f 32 | ( 0013 ) 736b 33 | ( 0014 ) call:000a ( 4*2CHAR ) 34 | ( 0015 ) @p @p @p @p 35 | ( 0016 ) 6874 36 | ( 0017 ) 7220 37 | ( 0018 ) 6672 38 | ( 0019 ) 726f 39 | ( 001a ) call:000a ( 4*2CHAR ) 40 | ( 001b ) @p @p . . 41 | ( 001c ) 6f63 42 | ( 001d ) 6f6c 43 | ( 001e ) call:0006 ( 2CHAR ) 44 | ( 001f ) call:0006 ( 2CHAR ) 45 | ( 0020 ) ; 46 | ( START ) 47 | call:000f 48 | -------------------------------------------------------------------------------- /test/mult.f18: -------------------------------------------------------------------------------- 1 | ( Test F18 multiplication ) 2 | ( calculate t:a * s ) 3 | @p a! @p @p ( a=2 s=3 t=0 ) 4 | babe 5 | cafe 6 | 0 7 | @p push . . 8 | 11 9 | +* unext . . 10 | !p . . . 11 | a !p . . 12 | 13 | ( Result: t=02504 a=33484 ) 14 | --------------------------------------------------------------------------------