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