1 /** 2 * DCPU-16 CPU 3 * 4 * See_Also: 5 * http://pastebin.com/raw.php?i=Q4JvQvnM 6 */ 7 module dcpu.cpu; 8 9 /+ 10 import std.array, std.random; 11 //import std.string, std.conv, std.stdio; 12 import dcpu.microcode, dcpu.machine, dcpu.hardware; 13 14 /** 15 * CPU State 16 */ 17 enum CpuState { 18 DECO, /// Decoding an instrucction 19 OPA, /// Get Operator A value 20 OPB, /// Get Operator B value 21 EXECUTE /// Execute the OpCode 22 } 23 24 /** 25 * CPU public information of his actual state 26 */ 27 struct CpuInfo { 28 union { 29 struct {ushort a, b, c, x, y, z, i, j;} 30 ushort[8] registers; /// General registers 31 } 32 33 ushort pc; /// Program Counter register 34 ushort sp; /// Stack Pointer register 35 ushort ex; /// Excess register 36 ushort ia; /// Interrupt Address register 37 38 bool read_queue = true; /// FrontPop interrupt queue ? 39 bool wait_hwd; /// Waiting because to end an Interrup to Hardware 40 41 bool f_fire; /// CPU cath fire 42 43 bool skip; /// Skip next instrucction 44 CpuState state; /// Actual state of CPU 45 ushort word; /// Value of [PC] when begin ready state 46 int cycles; /// Cycles to do in execute 47 48 } 49 50 final class DCpu { 51 private: 52 Random gen; // Used when get fire 53 /** 54 * Operator 55 * Params: 56 * opt = Type of operator (OpA or OpB) 57 */ 58 final class Operator(string opt) { 59 static assert (opt == "OpA" || opt == "OpB", "Invalid operator"); 60 61 private: 62 ushort val; // Value read 63 ushort op; // Operand Type 64 ushort ptr; // Where are pointing the pointer 65 bool _next_word; // Uses the next word and so need a extra cycle 66 67 public: 68 this(ubyte op) { 69 this.op = op; 70 switch (op) { // Need to read the next word ? 71 case Operand.Aptr_word: 72 case Operand.Bptr_word: 73 case Operand.Cptr_word: 74 case Operand.Xptr_word: 75 case Operand.Yptr_word: 76 case Operand.Zptr_word: 77 case Operand.Iptr_word: 78 case Operand.Jptr_word: 79 case Operand.PICK_word: 80 case Operand.NWord_ptr: 81 case Operand.NWord: 82 _next_word = true; 83 break; 84 default: 85 _next_word = false; 86 } 87 88 if (!info.skip) 89 switch (op) { // Read it 90 case Operand.A: // General Registers 91 case Operand.B: 92 case Operand.C: 93 case Operand.X: 94 case Operand.Y: 95 case Operand.Z: 96 case Operand.I: 97 case Operand.J: 98 val = info.registers[op]; 99 break; 100 101 case Operand.Aptr: // General Registers Pointer 102 case Operand.Bptr: 103 case Operand.Cptr: 104 case Operand.Xptr: 105 case Operand.Yptr: 106 case Operand.Zptr: 107 case Operand.Iptr: 108 case Operand.Jptr: 109 ptr = info.registers[op- Operand.Aptr]; 110 //synchronized (machine) { 111 val = machine.ram[ptr]; 112 //} 113 break; 114 115 case Operand.Aptr_word: // [Reg + next word litreal] 116 case Operand.Bptr_word: 117 case Operand.Cptr_word: 118 case Operand.Xptr_word: 119 case Operand.Yptr_word: 120 case Operand.Zptr_word: 121 case Operand.Iptr_word: 122 case Operand.Jptr_word: 123 //synchronized (machine) { 124 ptr = cast(ushort)(info.registers[op- Operand.Aptr_word] + machine.ram[info.pc +1]); 125 val = machine.ram[ptr]; 126 //} 127 break; 128 129 case Operand.POP_PUSH: // a Pop [SP++] | b PUSH [--SP] 130 static if (opt == "OpA") { 131 //synchronized (machine) { // To read the value 132 val = machine.ram[cast(ushort)(info.sp++)]; 133 //} 134 } else { // TODO Need confirmation if this is correct 135 //synchronized (machine) { 136 val = machine.ram[cast(ushort)(info.sp-1)]; 137 //} 138 } 139 break; 140 141 case Operand.PEEK: // [SP] 142 //synchronized (machine) { 143 ptr = info.sp; 144 val = machine.ram[info.sp]; 145 //} 146 break; 147 148 case Operand.PICK_word: // [SP + next word litreal] 149 //synchronized (machine) { 150 ptr = cast(ushort)(info.sp + machine.ram[info.pc +1]); 151 val = machine.ram[ptr]; 152 //} 153 break; 154 155 case Operand.SP: // SP 156 val = info.sp; 157 break; 158 159 case Operand.PC: // PC 160 val = info.pc; 161 break; 162 163 case Operand.EX: // EXcess 164 val = info.ex; 165 break; 166 167 case Operand.NWord_ptr: // Ptr [next word literal ] 168 //synchronized (machine) { 169 ptr = machine.ram[info.pc +1]; 170 val = machine.ram[ptr]; 171 //} 172 break; 173 174 case Operand.NWord: // next word literal 175 //synchronized (machine) { 176 ptr = cast(ushort)(info.pc +1); 177 val = machine.ram[ptr]; 178 //} 179 break; 180 181 default: // Literal 182 static if (opt == "OpA") { 183 val = cast(ushort)((op -1) - Operand.Literal); //(op - (Operand.Literal +1)); 184 } else { 185 assert(false, "This code never should be executed. Operator B can't have literals"); 186 } 187 } 188 189 //writeln(opt, "=> ", format("0x%04X",val), " ", val, " op:", format("0x%04X",op)); 190 } 191 192 /** 193 * Uses next word, so need to increase PC value ? 194 */ 195 @property bool next_word() { 196 return _next_word; 197 } 198 /** 199 * Returns: Value of the operator 200 */ 201 ushort read () @property { 202 return val; 203 } 204 205 /** 206 * Writes the new value to his place 207 */ 208 void write(ushort v) @property { 209 if (!info.skip) 210 switch (op) { // Read it 211 case Operand.A: // General Registers 212 case Operand.B: 213 case Operand.C: 214 case Operand.X: 215 case Operand.Y: 216 case Operand.Z: 217 case Operand.I: 218 case Operand.J: 219 info.registers[op] = v; 220 break; 221 222 case Operand.Aptr: // General Registers Pointer 223 case Operand.Bptr: 224 case Operand.Cptr: 225 case Operand.Xptr: 226 case Operand.Yptr: 227 case Operand.Zptr: 228 case Operand.Iptr: 229 case Operand.Jptr: 230 case Operand.Aptr_word: // [Reg + next word litreal] 231 case Operand.Bptr_word: 232 case Operand.Cptr_word: 233 case Operand.Xptr_word: 234 case Operand.Yptr_word: 235 case Operand.Zptr_word: 236 case Operand.Iptr_word: 237 case Operand.Jptr_word: 238 case Operand.NWord_ptr: // Ptr [next word literal ] 239 case Operand.PEEK: // [SP] 240 case Operand.PICK_word: // [SP + next word litreal] 241 //synchronized (machine) { 242 machine.ram[ptr] = v; 243 //} 244 break; 245 246 case Operand.POP_PUSH: // a Pop [SP++] | b PUSH [--SP] 247 static if (opt == "OpB") { 248 //synchronized (machine) { // To read the value 249 machine.ram[cast(ushort)(--info.sp)] = v; 250 //} 251 break; 252 } else { 253 assert (false, "This code must never executed. OpA PUSH can be writted"); 254 } 255 256 257 case Operand.SP: // SP 258 info.sp = v; 259 break; 260 261 case Operand.PC: // PC 262 info.pc = cast(ushort)(v -1); // Compesate pc++ of execute 263 break; 264 265 case Operand.EX: // EXcess 266 info.ex = v; 267 break; 268 269 default: // Literal and Next_Word literal or pointer 270 assert(false, "This code never should be executed. Literasl can be writed"); 271 } 272 } 273 274 } 275 276 Machine machine; /// Self-reference to the machine 277 278 ushort[] int_queue; /// Interrupt queue 279 bool new_skip; /// New value of skip 280 281 // Stores state between clock cycles 282 ubyte opcode; /// OpCode 283 ubyte ext_opcode; /// Extendend Opcode if OpCode == 0 284 285 bool do_inmediate = true; /// Do an inmediate operand or next word operand 286 ubyte opa; /// Operand A 287 ubyte opb; /// Operand B 288 289 Operator!"OpA" val_a; /// Value of operand A 290 Operator!"OpB" val_b; /// Value of operand B 291 ushort val; /// Result of an operation 292 bool write_val; /// Must write val to a register or ram 293 294 CpuInfo info; /// CPU actual state 295 296 public: 297 298 this(ref Machine machine) { 299 this.machine = machine; 300 gen = Random(unpredictableSeed); 301 } 302 303 /** 304 * Returns CPU actual mutable state (used by hardware to alter CPU registers and state) 305 */ 306 package ref CpuInfo state() @property { 307 return info; 308 } 309 310 /** 311 * Returns a no muttable copy of CPU actual state 312 */ 313 auto actual_state() @property { 314 immutable(CpuInfo) i = info; 315 return i; 316 } 317 318 /** 319 * Steps one cycle 320 * Returns: True if executed an instrucction. False if not ended the execution of a instrucction 321 */ 322 bool step() { 323 //writeln(to!string(state)); 324 if (info.f_fire) { // Swap a random bit of a random address 325 enum bits = [ 1, 2^^1, 2^^2, 2^^3, 2^^4, 2^^5, 2^^6, 2^^7, 2^^8, 2^^9, 2^^10, 2^^11, 2^^12, 2^^13, 2^^14, 2^^15 ]; 326 auto rbit = randomCover(bits, gen); 327 ushort pos = cast(ushort)uniform(0, ushort.max, gen); 328 machine.ram[pos] = cast(ushort)(machine.ram[pos] ^ rbit.front); 329 } 330 331 if (info.state == CpuState.DECO) { // Feth [PC] and extract operands and opcodes 332 if (int_queue.length > 255) { // Catch fire 333 info.f_fire = true; 334 } 335 if (info.read_queue && !int_queue.empty) { // Try to do a int in the queue 336 if (info.ia != 0 ) { 337 info.read_queue = false; 338 machine.ram[--info.sp] = info.pc; // Push PC and A 339 machine.ram[--info.sp] = info.a; 340 info.pc = info.ia; 341 info.a = int_queue[0]; 342 int_queue = int_queue[1..$]; 343 } else { 344 // If IA is set to 0, a triggered interrupt does nothing. A queued interrupt is considered triggered when it leaves the queue, not when it enters it. 345 int_queue = int_queue[1..$]; 346 } 347 } 348 349 //synchronized (machine) { 350 info.word = machine.ram[info.pc]; 351 //} 352 353 opcode = decode!"OpCode"(info.word); 354 opa = decode!"OpA"(info.word); 355 opb = decode!"OpB"(info.word); 356 ext_opcode = decode!"ExtOpCode"(info.word); 357 358 info.state = CpuState.OPA; 359 return step(); // Jump to OPA to try to get a not "next word" operand 360 361 } else if (info.state == CpuState.OPA) { // Get Operand A 362 if (do_inmediate) { 363 val_a = new Operator!"OpA"(opa); 364 if (val_a.next_word && !info.skip) { // Take a extra cycle 365 do_inmediate = false; 366 return false; 367 } else if (val_a.next_word) { 368 info.pc++; 369 } 370 } else { 371 do_inmediate = true; 372 info.pc++; 373 } 374 375 if (opcode == 0) { 376 info.state = CpuState.EXECUTE; 377 info.cycles = -1; // Say to Execute to calc it 378 return step(); // Jump to Execute state 379 } else { 380 info.state = CpuState.OPB; 381 return step(); // Jump to OPB to try to get a not "next word" operand 382 } 383 384 } else if (info.state == CpuState.OPB) { // Get Operand B 385 if (do_inmediate) { 386 val_b = new Operator!"OpB"(opb); 387 if (val_b.next_word && !info.skip) { // Take a extra cycle 388 do_inmediate = false; 389 return false; 390 } else if (val_b.next_word) { 391 info.pc++; 392 } 393 } else { 394 do_inmediate = true; 395 info.pc++; 396 } 397 398 info.state = CpuState.EXECUTE; 399 info.cycles = -1; // It will be calculated in Execute mode 400 return step(); // Jump to Execute state 401 402 } else { // Execute the OpCode 403 return execute_op(); // I will increase pc when the last cycle is made 404 } 405 406 } 407 408 /** 409 * Send to the CPU a hardware interrupt 410 */ 411 void hardware_int(ushort msg) { 412 // Asumes that when IA == 0, incoming hardware interrupts are ignored 413 if (info.ia != 0 && int_queue.length < 256) { 414 int_queue ~= msg; 415 } 416 } 417 418 private: 419 420 /** 421 * Execute a OpCode 422 */ 423 bool execute_op() { 424 if (opcode != 0 && info.cycles < 0) { // Execute Not extended opcode 425 if (!info.skip) { 426 write_val = true; 427 switch (opcode) { 428 case OpCode.SET: 429 val = val_a.read; 430 info.cycles = 1; 431 break; 432 433 case OpCode.ADD: 434 uint tmp = val_b.read + val_a.read; 435 val = cast(ushort)(tmp & 0xFFFF); 436 info.ex = tmp > 0xFFFF; // Overflow 437 info.cycles = 2; 438 break; 439 440 case OpCode.SUB: 441 int tmp = val_b.read - val_a.read; 442 val = cast(ushort)(tmp & 0xFFFF); 443 if ( val & 0x800 ) { // val < 0 444 info.ex = 0xFFFF; // Underflow 445 } else { 446 info.ex = 0; 447 } 448 info.cycles = 2; 449 break; 450 451 case OpCode.MUL: 452 uint tmp = val_b.read * val_a.read; 453 val = cast(ushort)(tmp & 0xFFFF); 454 info.ex = cast(ushort)(tmp >> 16); 455 info.cycles = 2; 456 break; 457 458 case OpCode.MLI: // Mul with sign 459 int tmp = cast(short)val_b.read * cast(short)val_a.read; 460 val = cast(ushort)(tmp & 0xFFFF); 461 info.ex = cast(ushort)(tmp >> 16); 462 info.cycles = 2; 463 break; 464 465 case OpCode.DIV: 466 if (val_a.read == 0) { 467 val = 0; 468 } else { 469 uint tmp = val_b.read / val_a.read; 470 uint tmp2 = (val_b.read << 16) / val_a.read; 471 val = cast(ushort)(tmp & 0xFFFF); 472 info.ex = cast(ushort)(tmp2 & 0xFFFF); 473 } 474 info.cycles = 3; 475 break; 476 477 case OpCode.DVI: // Div with sign 478 if (val_a.read == 0) { 479 val = 0; 480 } else { 481 int tmp = cast(short)val_b.read / cast(short)val_a.read; 482 int tmp2 = (cast(short)val_b.read << 16) / cast(short)val_a.read; 483 val = cast(ushort)(tmp & 0xFFFF); 484 info.ex = cast(ushort)(tmp2 & 0xFFFF); 485 } 486 info.cycles = 3; 487 break; 488 489 case OpCode.MOD: 490 if (val_a.read == 0) { 491 val = 0; 492 } else { 493 val = val_b.read % val_a.read; 494 } 495 info.cycles = 3; 496 break; 497 498 case OpCode.MDI: // Mod with sign 499 if (val_a.read == 0) { 500 val = 0; 501 } else { 502 val = cast(short)val_b.read % cast(short)val_a.read; 503 } 504 info.cycles = 3; 505 break; 506 507 case OpCode.AND: 508 val = val_b.read & val_a.read; 509 info.cycles = 1; 510 break; 511 512 case OpCode.BOR: 513 val = val_b.read | val_a.read; 514 info.cycles = 1; 515 break; 516 517 case OpCode.XOR: 518 val = val_b.read ^ val_a.read; 519 info.cycles = 1; 520 break; 521 522 case OpCode.SHR: // Logical Shift 523 uint tmp = val_b.read >>> val_a.read; 524 auto tmp2 = (val_b.read << 16) >> val_a.read; 525 val = cast(ushort)(tmp & 0xFFFF); 526 info.ex = cast(ushort)(tmp2 & 0xFFFF); 527 info.cycles = 1; 528 break; 529 530 case OpCode.ASR: // Arthmetic shift 531 int tmp2 = ((cast(short)val_b.read <<16) >>> cast(short)val_a.read); 532 auto tmp = cast(short)val_b.read >> cast(short)val_a.read; 533 val = cast(ushort)(tmp & 0xFFFF); 534 info.ex = cast(ushort)(tmp2 & 0xFFFF); 535 info.cycles = 1; 536 break; 537 538 case OpCode.SHL: 539 uint tmp = (val_b.read << val_a.read) >> 16; 540 val = cast(ushort)(val_b.read << val_a.read); 541 info.ex = cast(ushort)(tmp & 0xFFFF); 542 info.cycles = 1; 543 break; 544 545 case OpCode.IFB: 546 info.cycles = 2; 547 write_val = false; 548 new_skip = !((val_b.read & val_a.read) != 0); // Skip next instrucction 549 break; 550 551 case OpCode.IFC: 552 info.cycles = 2; 553 write_val = false; 554 new_skip = !((val_b.read & val_a.read) == 0); 555 break; 556 557 case OpCode.IFE: 558 new_skip = !(val_b.read == val_a.read); 559 write_val = false; 560 info.cycles = 2; 561 break; 562 563 case OpCode.IFN: 564 info.cycles = 2; 565 write_val = false; 566 new_skip = !(val_b.read != val_a.read); 567 break; 568 569 case OpCode.IFG: 570 info.cycles = 2; 571 write_val = false; 572 new_skip = !(val_b.read > val_a.read); 573 break; 574 575 case OpCode.IFA: 576 info.cycles = 2; 577 write_val = false; 578 new_skip = !(cast(short)val_b.read > cast(short)val_a.read); 579 break; 580 581 case OpCode.IFL: 582 info.cycles = 2; 583 write_val = false; 584 new_skip = !(val_b.read < val_a.read); 585 break; 586 587 case OpCode.IFU: 588 info.cycles = 2; 589 write_val = false; 590 new_skip = !(cast(short)val_b.read < cast(short)val_a.read); 591 break; 592 593 case OpCode.ADX: 594 uint tmp = val_b.read + val_a.read + info.ex; 595 val = cast(ushort)(tmp & 0xFFFF); 596 info.ex = tmp > 0xFFFF; // Overflow 597 info.cycles = 3; 598 break; 599 600 case OpCode.SBX: 601 int tmp = val_b.read - val_a.read + info.ex; 602 val = cast(ushort)(tmp & 0xFFFF); 603 if ( val & 0x800 ) { // val < 0 604 info.ex = 0xFFFF; // Underflow 605 } else { 606 info.ex = 0; 607 } 608 info.cycles = 3; 609 break; 610 611 case OpCode.STI: 612 val = val_a.read; 613 info.i++; 614 info.j++; 615 info.cycles = 2; 616 break; 617 618 case OpCode.STD: 619 val = val_a.read; 620 info.i--; 621 info.j--; 622 info.cycles = 2; 623 break; 624 625 default: // Unknow OpCode 626 // Do Nothing (I should do a random OpCode ?) 627 write_val = false; 628 info.cycles = 1; 629 } 630 631 } else { // Skip next basic OpCode instrucction 632 info.cycles = 1; 633 write_val = false; 634 switch (opcode) { // Skipe chained branchs 635 case OpCode.IFB: 636 case OpCode.IFC: 637 case OpCode.IFE: 638 case OpCode.IFN: 639 case OpCode.IFG: 640 case OpCode.IFA: 641 case OpCode.IFL: 642 case OpCode.IFU: 643 new_skip = true; 644 break; 645 646 default: 647 new_skip = false; 648 } 649 } 650 651 } else if (info.cycles < 0) { // Extended OpCode 652 write_val = false; 653 new_skip = false; 654 if (!info.skip) { 655 switch (ext_opcode) { 656 case ExtOpCode.JSR: 657 //synchronized (machine) { 658 machine.ram[--info.sp] = cast(ushort)(info.pc +1); 659 //} 660 info.pc = cast(ushort)(val_a.read -1); // Compesate later pc++ 661 info.cycles = 3; 662 break; 663 664 case ExtOpCode.HCF: 665 info.cycles = 9; 666 info.f_fire = true; 667 break; 668 669 case ExtOpCode.INT: // Software Interruption 670 info.cycles = 4; 671 // NOTE This implementations asumes that INTs bypass the queue 672 if (info.ia != 0) { 673 info.read_queue = false; 674 machine.ram[--info.sp] = cast(ushort)(info.pc +1); // Push PC and A 675 machine.ram[--info.sp] = info.a; 676 info.pc = cast(ushort)(info.ia -1); 677 } 678 break; 679 680 case ExtOpCode.IAG: // Get IA 681 write_val = true; 682 val = info.ia; 683 info.cycles = 1; 684 break; 685 686 case ExtOpCode.IAS: // Set IA 687 info.ia = val_a.read; 688 info.cycles = 1; 689 break; 690 691 case ExtOpCode.RFI: // Return From Interrupt 692 info.read_queue = true; 693 //synchronized (machine) { 694 info.a = machine.ram[info.sp++]; 695 info.pc = cast(ushort)(machine.ram[info.sp++] -1); 696 //} 697 info.cycles = 3; 698 break; 699 700 case ExtOpCode.IAQ: // Enable pop front of interrupt queue 701 info.read_queue = val_a.read == 0; // if val_a != 0 Not read the interrupt queue 702 info.cycles = 2; 703 break; 704 705 case ExtOpCode.HWN: // Number of devices 706 write_val = true; 707 val = cast(ushort)machine.dev.length; 708 info.cycles = 2; 709 break; 710 711 case ExtOpCode.HWQ: // Get Hardware IDs 712 info.cycles = 4; 713 if (val_a.read in machine.dev) { 714 auto dev = machine.dev[val_a.read]; 715 info.a = dev.id_lsb; 716 info.b = dev.id_msb; 717 info.c = dev.dev_ver; 718 info.x = dev.vendor_lsb; 719 info.y = dev.vendor_msb; 720 } else { // Unspecific behaviour 721 info.a = info.b = info.c = info.x = info.y = 0xFFFF; 722 } 723 break; 724 725 case ExtOpCode.HWI: // Send a hardware interrupt to device A 726 info.cycles = 4; // Or more 727 if (val_a.read in machine.dev) { 728 machine.dev[val_a.read].interrupt(info, machine.ram); 729 } 730 break; 731 732 default: // Unknow OpCode 733 // Do Nothing (I should do a random OpCode ?) 734 info.cycles = 1; 735 } 736 } else { 737 info.cycles = 1; 738 new_skip = false; 739 } 740 741 } 742 743 if (!info.wait_hwd) // Some hardware when receive a HWI can make to wait more cycles 744 info.cycles--; 745 746 if (info.cycles == 0) { // Only increment PC and set Ready when cycle count == 0 747 if (!info.skip) { 748 if (opcode != 0 && write_val) { // Basic OpCode 749 // OpB <= OpA Operation OpA = val 750 val_b.write = val; 751 } else if (write_val) { // Extended OpCode 752 // OpA <= val 753 val_a.write = val; 754 } 755 } 756 info.skip = new_skip; 757 info.state = CpuState.DECO; 758 info.pc++; 759 return true; 760 } 761 return false; 762 } 763 } 764 +/ 765