tpu.c (15160B)
1#include "tpu.h" 2#include "util.h" 3 4#include <ctype.h> 5#include <stdio.h> 6#include <stdbool.h> 7#include <stddef.h> 8#include <string.h> 9 10const char *dir_reprs[] = { 11 "LEFT", "RIGHT", "UP", "DOWN" 12}; 13 14const char *status_reprs[] = { 15 "IDLE", "RUN", "READ", "WRITE" 16}; 17 18const char *inst_reprs[] = { 19 "NOP", "MOV", "SWP", "SAV", "ADD", 20 "SUB", "NEG", "XOR", "AND", "JMP", 21 "JEQ", "JNE", "JRO", "SHL", "SHR" 22}; 23 24const char *op_reprs[] = { 25 "ACC", "NIL", "LEFT", "RIGHT", "UP", "DOWN", 26 "ANY", "LAST", "LIT", "<NAME>" 27}; 28 29static bool 30op_is_dir(enum tpu_inst_op_type op) 31{ 32 return op >= OP_LEFT && op <= OP_DOWN; 33} 34 35static enum tpu_port_dir 36op_to_dir(enum tpu_inst_op_type op) 37{ 38 return op - OP_LEFT; 39} 40 41static enum tpu_port_dir 42opposite_dir(enum tpu_port_dir dir) 43{ 44 switch (dir) { 45 case DIR_UP: 46 return DIR_DOWN; 47 case DIR_DOWN: 48 return DIR_UP; 49 case DIR_LEFT: 50 return DIR_RIGHT; 51 case DIR_RIGHT: 52 return DIR_LEFT; 53 } 54} 55 56void 57tpu_port_init(struct tpu_port *port) 58{ 59 port->attached = false; 60 port->dst_port = NULL; 61 port->dst_tpu = NULL; 62 port->type = 0; 63 port->clr_post_run = false; 64 port->reading = false; 65 port->writing = false; 66 port->could_read = false; 67 port->could_write = false; 68 port->empty = true; 69 port->in = -1; 70 port->out = -1; 71 port->out_pending = -1; 72} 73 74void 75tpu_port_deinit(struct tpu_port *port) 76{ 77 /* empty */ 78} 79 80void 81tpu_init(struct tpu *tpu) 82{ 83 size_t i; 84 85 tpu->status = STATUS_IDLE; 86 87 tpu->x = 0; 88 tpu->y = 0; 89 90 tpu->steps = 0; 91 tpu->idle_steps = 0; 92 93 tpu->user = NULL; 94 95 tpu->pc = 0; 96 tpu->acc = 0; 97 tpu->bak = 0; 98 tpu->inst_cnt = 0; 99 memset(tpu->labels, 0, sizeof(char *) * TPU_MAX_INST); 100 memset(tpu->jmpdst, 0xff, sizeof(int) * TPU_MAX_INST); 101 102 tpu->last = -1; 103 tpu->io_port = -1; 104 for (i = 0; i < 4; i++) 105 tpu_port_init(&tpu->ports[i]); 106} 107 108void 109tpu_deinit(struct tpu *tpu) 110{ 111 int i; 112 113 for (i = 0; i < tpu->inst_cnt; i++) { 114 if (tpu->insts[i].opcnt >= 1 115 && tpu->insts[i].ops[0].type == OP_LABEL) 116 free(tpu->insts[i].ops[0].val.label); 117 } 118 for (i = 0; i < TPU_MAX_INST; i++) 119 free(tpu->labels[i]); 120} 121 122bool 123tpu_label_add(struct tpu *tpu, const char *name, size_t pc) 124{ 125 if (tpu->labels[pc]) return false; 126 if (tpu_label_get(tpu, name) != TPU_MAX_INST) return false; 127 128 tpu->labels[pc] = strdup(name); 129 if (!tpu->labels[pc]) die("strdup"); 130 131 return true; 132} 133 134size_t 135tpu_label_get(struct tpu *tpu, const char *name) 136{ 137 size_t i; 138 139 for (i = 0; i < TPU_MAX_INST; i++) { 140 if (tpu->labels[i] && !strcasecmp(tpu->labels[i], name)) 141 return i; 142 } 143 144 return TPU_MAX_INST; 145} 146 147void 148tpu_init_ports(struct tpu *tpu, struct tpu_map *map) 149{ 150 const int dx[4] = { -1, 1, 0, 0 }; 151 const int dy[4] = { 0, 0, -1, 1 }; 152 enum tpu_port_dir dir, odir; 153 enum tpu_inst_op_type optype; 154 struct tpu *neighbor; 155 int x, y, i, k, l; 156 157 for (i = 0; i < tpu->inst_cnt; i++) { 158 for (k = 0; k < tpu->insts[i].opcnt; k++) { 159 optype = tpu->insts[i].ops[k].type; 160 if (op_is_dir(optype)) { 161 dir = op_to_dir(tpu->insts[i].ops[k].type); 162 if (k == 0) 163 tpu->ports[dir].could_read = true; 164 else 165 tpu->ports[dir].could_write = true; 166 } else if (optype == OP_ANY) { 167 for (l = 0; l < 4; l++) { 168 if (k == 0) 169 tpu->ports[l].could_read = true; 170 else 171 tpu->ports[l].could_write = true; 172 } 173 } else if (optype == OP_LAST && k == 1) { 174 for (l = 0; l < 4; l++) { 175 if (tpu->ports[l].could_read) 176 tpu->ports[l].could_write = true; 177 } 178 } 179 } 180 } 181 182 for (i = 0; i < 4; i++) { 183 x = tpu->x + dx[i]; 184 y = tpu->y + dy[i]; 185 neighbor = tpu_map_get(map, x, y); 186 tpu->ports[i].dst_tpu = neighbor; 187 tpu->ports[i].type = tpu->ports[i].could_read * PORT_IN 188 | tpu->ports[i].could_write * PORT_OUT; 189 if (neighbor && tpu->ports[i].type != 0) { 190 odir = opposite_dir((enum tpu_port_dir) i); 191 tpu->ports[i].dst_port = &neighbor->ports[odir]; 192 } 193 } 194} 195 196void 197tpu_attach_ports(struct tpu *tpu) 198{ 199 struct tpu *neighbor; 200 enum tpu_port_dir i, o; 201 202 for (i = 0; i < 4; i++) { 203 neighbor = tpu->ports[i].dst_tpu; 204 if (!neighbor) continue; 205 o = opposite_dir(i); 206 if (tpu->ports[i].type & PORT_IN && neighbor->ports[o].type & PORT_OUT) 207 tpu->ports[i].attached = true; 208 if (tpu->ports[i].type & PORT_OUT && neighbor->ports[o].type & PORT_IN) 209 tpu->ports[i].attached = true; 210 } 211} 212 213static void 214tpu_update_ports(struct tpu *tpu) 215{ 216 struct tpu_port *port; 217 int i; 218 219 for (i = 0; i < 4; i++) { 220 port = &tpu->ports[i]; 221 if (!port->attached) continue; 222 if (port->out >= 0 && port->dst_port->in < 0) { 223 port->dst_port->reading = false; 224 port->dst_port->in = port->out; 225 port->writing = false; 226 port->out = -1; 227 } 228 if (port->dst_port->out >= 0 && port->in < 0) { 229 port->reading = false; 230 port->in = port->dst_port->out; 231 port->dst_port->writing = false; 232 port->dst_port->out = -1; 233 } 234 if (tpu->status == STATUS_RUN && port->clr_post_run) { 235 port->in = -1; 236 port->clr_post_run = false; 237 } 238 port->empty = port->in < 0; 239 } 240} 241 242bool 243tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_inst_type inst_type, 244 unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2) 245{ 246 struct tpu_inst *inst; 247 248 inst = &tpu->insts[pc]; 249 inst->type = inst_type; 250 inst->opcnt = opcnt; 251 inst->ops[0] = op1; 252 inst->ops[1] = op2; 253 254 switch (inst->type) { 255 case INST_NOP: case INST_SAV: 256 case INST_SWP: case INST_NEG: 257 if (inst->opcnt != 0) return false; 258 break; 259 case INST_ADD: case INST_SUB: case INST_XOR: 260 case INST_AND: case INST_SHL: case INST_SHR: 261 if (inst->opcnt != 1) return false; 262 if (inst->ops[0].type == OP_LABEL) return false; 263 break; 264 case INST_JRO: 265 if (inst->opcnt != 1) return false; 266 if (inst->ops[0].type != OP_LIT) return false; 267 break; 268 case INST_JMP: 269 if (inst->opcnt != 1) return false; 270 if (inst->ops[0].type != OP_LABEL) return false; 271 break; 272 case INST_JEQ: case INST_JNE: 273 if (inst->opcnt != 2) return false; 274 if (inst->ops[0].type == OP_LABEL) return false; 275 if (inst->ops[1].type != OP_LABEL) return false; 276 break; 277 case INST_MOV: 278 if (inst->opcnt != 2) return false; 279 if (inst->ops[0].type == OP_LABEL) return false; 280 if (inst->ops[1].type == OP_LABEL) return false; 281 if (inst->ops[1].type == OP_LIT) return false; 282 break; 283 } 284 285 return true; 286} 287 288bool 289tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type, 290 unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2) 291{ 292 if (tpu->inst_cnt >= TPU_MAX_INST) 293 die("tpu_add_inst: tpu X%i Y%i, >= max %i instructions", 294 tpu->x, tpu->y, TPU_MAX_INST); 295 return tpu_set_inst(tpu, (uint8_t) tpu->inst_cnt++, 296 inst_type, opcnt, op1, op2); 297} 298 299static int 300tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir) 301{ 302 struct tpu_port *port; 303 304 port = &tpu->ports[dir]; 305 if (port->attached && tpu->tis->stdin 306 && port->dst_port == &tpu->tis->stdin_port && port->in < 0) { 307 port->in = getc(tpu->tis->stdin); 308 port->in = port->in == EOF ? -1 : (uint8_t) port->in; 309 } 310 if (port->in < 0) { 311 port->reading = true; 312 return -1; 313 } 314 port->clr_post_run = true; 315 return port->in; 316} 317 318static bool 319tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, uint8_t lit) 320{ 321 struct tpu_port *port; 322 323 port = &tpu->ports[dir]; 324 if (port->attached && port->dst_port == &tpu->tis->stdout_port) { 325 if (tpu->tis->stdout) putc(lit, tpu->tis->stdout); 326 return true; 327 } 328 if (!port->attached || !port->dst_port->empty) { 329 port->out_pending = lit; 330 port->writing = true; 331 return false; 332 } 333 port->out = lit; 334 port->out_pending = -1; 335 return true; 336} 337 338static void 339tpu_jmp_label(struct tpu *tpu, const char *label) 340{ 341 size_t pc; 342 343 if (tpu->jmpdst[tpu->pc] >= 0) { 344 tpu->pc = (uint8_t) tpu->jmpdst[tpu->pc]; 345 } else { 346 pc = tpu_label_get(tpu, label); 347 if (pc >= TPU_MAX_INST) abort(); 348 tpu->jmpdst[tpu->pc] = (uint8_t) pc; 349 tpu->pc = (uint8_t) pc; 350 } 351} 352 353static int 354tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op) 355{ 356 int i, v; 357 358 switch (op->type) { 359 case OP_ACC: 360 return tpu->acc; 361 case OP_NIL: 362 return -1; 363 case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN: 364 return tpu_port_read(tpu, op_to_dir(op->type)); 365 case OP_ANY: 366 for (i = 0; i < 4; i++) { 367 v = tpu_port_read(tpu, (enum tpu_port_dir) i); 368 if (v >= 0) { 369 tpu->last = i; 370 return (uint8_t) v; 371 } 372 } 373 return -1; 374 case OP_LAST: 375 if (tpu->last < 0) return 0; 376 return tpu_port_read(tpu, (enum tpu_port_dir) tpu->last); 377 case OP_LIT: 378 return op->val.lit; 379 case OP_LABEL: 380 abort(); 381 } 382} 383 384static bool 385tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, uint8_t lit) 386{ 387 int i; 388 389 switch (op->type) { 390 case OP_ACC: 391 tpu->acc = lit; 392 return true; 393 case OP_NIL: 394 return true; 395 case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN: 396 return tpu_port_write(tpu, op_to_dir(op->type), lit); 397 case OP_ANY: 398 for (i = 0; i < 4; i++) { 399 if (tpu_port_write(tpu, (enum tpu_port_dir) i, lit)) { 400 tpu->last = i; 401 return true; 402 } 403 } 404 return false; 405 case OP_LAST: 406 if (tpu->last < 0) return false; 407 return tpu_port_write(tpu, (enum tpu_port_dir) tpu->last, lit); 408 case OP_LABEL: 409 case OP_LIT: 410 abort(); 411 } 412} 413 414static enum tpu_status 415tpu_exec_mov(struct tpu *tpu, struct tpu_inst *inst) 416{ 417 int lit; 418 419 if (inst->type != INST_MOV) abort(); 420 421 lit = tpu_exec_get(tpu, &inst->ops[0]); 422 if (lit < 0) return STATUS_READ; 423 424 if (!tpu_exec_put(tpu, &inst->ops[1], (uint8_t) lit)) 425 return STATUS_WRITE; 426 427 return STATUS_RUN; 428} 429 430static enum tpu_status 431tpu_exec(struct tpu *tpu, struct tpu_inst *inst) 432{ 433 enum tpu_status status; 434 uint8_t lit; 435 int val; 436 437 switch (inst->type) { 438 case INST_NOP: 439 tpu->pc += 1; 440 return STATUS_RUN; 441 case INST_MOV: 442 status = tpu_exec_mov(tpu, inst); 443 if (status == STATUS_RUN) 444 tpu->pc += 1; 445 return status; 446 case INST_SWP: 447 lit = tpu->acc; 448 tpu->acc = tpu->bak; 449 tpu->bak = lit; 450 tpu->pc += 1; 451 return STATUS_RUN; 452 case INST_SAV: 453 tpu->bak = tpu->acc; 454 tpu->pc += 1; 455 return STATUS_RUN; 456 case INST_ADD: 457 val = tpu_exec_get(tpu, &inst->ops[0]); 458 if (val < 0) return STATUS_READ; 459 tpu->acc += (uint8_t) val; 460 tpu->pc += 1; 461 return STATUS_RUN; 462 case INST_SUB: 463 val = tpu_exec_get(tpu, &inst->ops[0]); 464 if (val < 0) return STATUS_READ; 465 tpu->acc -= (uint8_t) val; 466 tpu->pc += 1; 467 return STATUS_RUN; 468 case INST_NEG: 469 tpu->acc = -tpu->acc; 470 tpu->pc += 1; 471 return STATUS_RUN; 472 case INST_XOR: 473 val = tpu_exec_get(tpu, &inst->ops[0]); 474 if (val < 0) return STATUS_READ; 475 tpu->acc ^= (uint8_t) val; 476 tpu->pc += 1; 477 return STATUS_RUN; 478 case INST_AND: 479 val = tpu_exec_get(tpu, &inst->ops[0]); 480 if (val < 0) return STATUS_READ; 481 tpu->acc &= (uint8_t) val; 482 tpu->pc += 1; 483 return STATUS_RUN; 484 case INST_JMP: 485 tpu_jmp_label(tpu, inst->ops[0].val.label); 486 return STATUS_RUN; 487 case INST_JEQ: 488 val = tpu_exec_get(tpu, &inst->ops[0]); 489 if (val < 0) return STATUS_READ; 490 if (tpu->acc == val) 491 tpu_jmp_label(tpu, inst->ops[1].val.label); 492 else 493 tpu->pc += 1; 494 return STATUS_RUN; 495 case INST_JNE: 496 val = tpu_exec_get(tpu, &inst->ops[0]); 497 if (val < 0) return STATUS_READ; 498 if (tpu->acc != val) 499 tpu_jmp_label(tpu, inst->ops[1].val.label); 500 else 501 tpu->pc += 1; 502 return STATUS_RUN; 503 case INST_JRO: 504 tpu->pc += inst->ops[0].val.lit; 505 if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; 506 return STATUS_RUN; 507 case INST_SHL: 508 val = tpu_exec_get(tpu, &inst->ops[0]); 509 if (val < 0) abort(); 510 tpu->acc <<= val; 511 tpu->pc += 1; 512 return STATUS_RUN; 513 case INST_SHR: 514 val = tpu_exec_get(tpu, &inst->ops[0]); 515 if (val < 0) abort(); 516 tpu->acc >>= val; 517 tpu->pc += 1; 518 return STATUS_RUN; 519 } 520} 521 522enum tpu_status 523tpu_step(struct tpu *tpu) 524{ 525 if (tpu->pc < tpu->inst_cnt) { 526 tpu->status = tpu_exec(tpu, &tpu->insts[tpu->pc]); 527 if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; 528 } else { 529 tpu->status = STATUS_IDLE; 530 } 531 532 tpu->steps += 1; 533 if (tpu->status != STATUS_RUN) 534 tpu->idle_steps += 1; 535 536 return tpu->status; 537} 538 539void 540tpu_vec_init(struct tpu_vec *vec) 541{ 542 vec->cnt = 0; 543 vec->cap = 256; 544 vec->tpus = malloc(vec->cap * sizeof(struct tpu)); 545 if (!vec->tpus) die("malloc:"); 546} 547 548void 549tpu_vec_deinit(struct tpu_vec *vec) 550{ 551 struct tpu *tpu; 552 size_t i; 553 554 for (tpu = vec->tpus, i = 0; i < vec->cnt; i++, tpu++) 555 tpu_deinit(tpu); 556 free(vec->tpus); 557} 558 559void 560tpu_map_init(struct tpu_map *map) 561{ 562 memset(map->buckets, 0, sizeof(void *) * TPU_MAP_BUCKETS); 563} 564 565void 566tpu_map_deinit(struct tpu_map *map) 567{ 568 struct tpu_map_link *link, *next; 569 size_t i; 570 571 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 572 link = map->buckets[i]; 573 while (link) { 574 next = link->next; 575 free(link); 576 link = next; 577 } 578 } 579} 580 581static struct tpu_map_link ** 582tpu_map_link_pos(struct tpu_map *map, int x, int y) 583{ 584 struct tpu_map_link **link; 585 size_t i; 586 587 i = (size_t) (x + y) % TPU_MAP_BUCKETS; 588 link = &map->buckets[i]; 589 while (*link && !((*link)->x == x && (*link)->y == y)) 590 link = &(*link)->next; 591 592 return link; 593} 594 595bool 596tpu_map_add(struct tpu_map *map, struct tpu *tpu) 597{ 598 struct tpu_map_link **pos, *link; 599 600 pos = tpu_map_link_pos(map, tpu->x, tpu->y); 601 if (*pos) return false; 602 *pos = link = malloc(sizeof(struct tpu_map_link)); 603 if (!link) die("malloc:"); 604 link->tpu = tpu; 605 link->x = tpu->x; 606 link->y = tpu->y; 607 link->next = NULL; 608 return true; 609} 610 611struct tpu * 612tpu_map_get(struct tpu_map *map, int x, int y) 613{ 614 struct tpu_map_link **link; 615 616 link = tpu_map_link_pos(map, x, y); 617 if (!*link) return NULL; 618 619 return (*link)->tpu; 620} 621 622void 623tis_init(struct tis *tis, FILE *tis_stdin, FILE *tis_stdout) 624{ 625 tis->steps = 0; 626 tis->alive = true; 627 tis->idle = tis->prev_idle = false; 628 tpu_vec_init(&tis->tpu_vec); 629 tpu_map_init(&tis->tpu_map); 630 tis->stdin = tis_stdin; 631 tpu_port_init(&tis->stdin_port); 632 tis->stdin_port.type = PORT_OUT; 633 tis->stdout = tis_stdout; 634 tpu_port_init(&tis->stdout_port); 635 tis->stdout_port.type = PORT_IN; 636} 637 638void 639tis_deinit(struct tis *tis) 640{ 641 tpu_vec_deinit(&tis->tpu_vec); 642 tpu_map_deinit(&tis->tpu_map); 643 if (tis->stdin) fclose(tis->stdin); 644 tpu_port_deinit(&tis->stdin_port); 645 if (tis->stdout) fclose(tis->stdout); 646 tpu_port_deinit(&tis->stdout_port); 647} 648 649bool 650tis_step(struct tis *tis) 651{ 652 struct tpu *tpu; 653 bool running; 654 size_t i; 655 656 running = false; 657 for (tpu = tis->tpu_vec.tpus, i = 0; i < tis->tpu_vec.cnt; i++, tpu++) 658 running |= (tpu_step(tpu) == STATUS_RUN); 659 660 for (tpu = tis->tpu_vec.tpus, i = 0; i < tis->tpu_vec.cnt; i++, tpu++) 661 tpu_update_ports(tpu); 662 663 tis->prev_idle = tis->idle; 664 tis->idle = !running; 665 666 tis->alive = !tis->idle || !tis->prev_idle 667 || tis->stdin_port.reading && !feof(tis->stdin); 668 669 tis->steps++; 670 671 return tis->alive; 672} 673 674struct tpu * 675tis_add_tpu(struct tis *tis) 676{ 677 struct tpu_port *port; 678 struct tpu_vec *vec; 679 struct tpu_map_link *link; 680 struct tpu *tpu; 681 void *old_arena; 682 size_t i, k; 683 ssize_t off; 684 685 vec = &tis->tpu_vec; 686 if (vec->cnt >= vec->cap) { 687 vec->cap *= 2; 688 old_arena = vec->tpus; 689 vec->tpus = realloc(vec->tpus, vec->cap * sizeof(struct tpu)); 690 if (!vec->tpus) die("realloc:"); 691 /* AAAAH, dont looook >.< */ 692 off = (void *) vec->tpus - old_arena; 693 for (tpu = vec->tpus, i = 0; i < vec->cnt; i++, tpu++) { 694 for (port = tpu->ports, k = 0; k < 4; k++, port++) { 695 if (port->dst_tpu) 696 port->dst_tpu = (void *)port->dst_tpu + off; 697 if (port->dst_port) 698 port->dst_port = (void *)port->dst_port + off; 699 } 700 } 701 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 702 link = tis->tpu_map.buckets[i]; 703 for (; link; link = link->next) 704 link->tpu = (void *)link->tpu + off; 705 } 706 } 707 708 return &vec->tpus[vec->cnt++]; 709}