Advertisement
Guest User

cpu.js

a guest
Feb 21st, 2019
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. "use strict";
  2.  
  3. class CPU {
  4.    
  5.     constructor() {
  6.  
  7.         this.V = new Uint8Array(16);            // V0 to Vf are the 16 8-bit registers
  8.         this.I = 0;                             // Special 16-bit address register I
  9.         this.memory = new Uint8Array(4096);     // 4K (4096) bytes of memory
  10.         this.PC = 0x200;                        // Program counter (16-bit) starts at 0x200 (512)
  11.         this.stack = new Uint16Array(16);       // Array of 16 16-bit values
  12.         this.stackPointer = 0;                  // Points to the top of the stack
  13.  
  14.         this.delayTimer = 0;                    // Delay timer initialized to 0
  15.         this.soundTimer = 0;                    // Sound timer initialized to 0
  16.  
  17.         this.screenWidth = 64;
  18.         this.screenHeight = 32;
  19.         this.display = new Uint8Array(this.screenWidth * this.screenHeight); // Video memory, used to draw frames
  20.         this.drawFlag = false;                  // Tells whether to draw
  21.  
  22.         this.keys = new Uint8Array(16);         // Stores the status of the keys
  23.         this.keyPressed = null;                 // Value of the most recent key pressed
  24.  
  25.         this.isRunning = false;                 // CPU run status
  26.         this.programLoaded = false;             // True when the program has been loaded into memory
  27.  
  28.         this.fontsToStore = [                  
  29.             0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
  30.             0x20, 0x60, 0x20, 0x20, 0x70, // 1
  31.             0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
  32.             0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
  33.             0x90, 0x90, 0xF0, 0x10, 0x10, // 4
  34.             0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
  35.             0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
  36.             0xF0, 0x10, 0x20, 0x40, 0x40, // 7
  37.             0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
  38.             0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
  39.             0xF0, 0x90, 0xF0, 0x90, 0x90, // A
  40.             0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
  41.             0xF0, 0x80, 0x80, 0x80, 0xF0, // C
  42.             0xE0, 0x90, 0x90, 0x90, 0xE0, // D
  43.             0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
  44.             0xF0, 0x80, 0xF0, 0x80, 0x80  // F
  45.         ];
  46.  
  47.     } // End of constructor()
  48.  
  49.     loadFonts() {
  50.  
  51.         let length = this.fontsToStore.length;
  52.  
  53.         for(let i = 0; i < length; i++){
  54.             this.memory[i] = this.fontsToStore[i]
  55.         }
  56.  
  57.     } // End of loadFonts()
  58.  
  59.     reset() {
  60.  
  61.         this.V.fill(0);                                 // Clear registers
  62.         this.memory.fill(0);                            // Clear RAM
  63.         this.display.fill(0);                           // Clear memory
  64.         this.loadFonts();                               // Load font set
  65.         this.I = 0;                                     // Clear register I
  66.         this.PC = 0x200;                                // PC starts at 0x200 (512)
  67.         this.stack.fill(0);                             // Clear program stack
  68.         this.stackPointer = 0;                          // Clear stack pointer
  69.         this.delayTimer = 0;                            // Clear delay timer
  70.         this.soundTimer = 0;                            // Clear sound timer
  71.         this.isRunning = false;                         // Set CPU run status to false
  72.         this.keys.fill(0);                              // Clear keys
  73.         this.keyPressed = null;                         // No key pressed
  74.         this.drawFlag = false;                          // Don't draw anything
  75.  
  76.     } // End of reset()
  77.  
  78.     setTimer() {
  79.  
  80.         if (this.delayTimer > 0) {
  81.             this.delayTimer--;
  82.         }
  83.  
  84.         if (this.soundTimer > 0) {
  85.             this.soundTimer--;
  86.         }
  87.  
  88.     } // End of setTimer()
  89.  
  90.     renderScreen() {
  91.  
  92.         if (this.drawFlag === false)
  93.             return;
  94.  
  95.         const canvas = document.getElementById("screen-area");
  96.         const screen = canvas.getContext("2d");
  97.         screen.clearRect(0, 0, this.screenWidth * 10, this.screenHeight * 10);
  98.  
  99.         for (let y = 0; y < this.screenHeight; y++) {
  100.             for (let x = 0; x < this.screenWidth; x++) {
  101.                 if (this.display[x + (y * this.screenWidth)])
  102.                     screen.fillRect(x * 10, y * 10, 10, 10);
  103.             }
  104.         }
  105.  
  106.         this.drawFlag = false;
  107.     } // End of renderScreen()
  108.  
  109.     setKeyDown(key) {
  110.         this.keys[key] = true;
  111.     }   // End of setKeyDown(key)
  112.  
  113.     setKeyUp(key) {
  114.         this.keys[key] = false;
  115.     }   // End of setKeyUp(key)
  116.  
  117.     loadProgram(rom){
  118.  
  119.         let reader = new FileReader();
  120.  
  121.         reader.addEventListener("loadend", function(){
  122.             let buffer = new Uint8Array(reader.result);
  123.            
  124.             for (let i = 0; i < buffer.length; i++) {
  125.                 CHIP8.memory[CHIP8.PC + i] = buffer[i];
  126.             }
  127.  
  128.         });
  129.  
  130.         reader.readAsArrayBuffer(rom);
  131.  
  132.     } // End of loadProgram()
  133.  
  134.     // Opcode implementations
  135.     emulateOpcode(opcode) {
  136.         let code = opcode;                      // 1111-2222-3333-4444
  137.         let x = (code & 0x0F00) >>> 8;          // 0x00
  138.         let y = (code & 0x00F0) >>> 4;          // 00y0
  139.         let kk = code & 0x00FF;                 // 00kk
  140.         let nnn = code & 0x0FFF;                // 0nnn
  141.  
  142.         switch (code & 0xF000) {
  143.             case 0x0000:
  144.  
  145.                 switch (code & 0x00FF) {
  146.  
  147.                     case 0x00E0:
  148.  
  149.                         // 00E0 - CLS
  150.                         // Clear the display
  151.                         this.display.fill(0);
  152.                         this.PC += 2;
  153.                         this.drawFlag = true;
  154.                         break;
  155.  
  156.                     case 0x00EE:
  157.  
  158.                         // 00EE - RET
  159.                         // Return from a subroutine
  160.                         this.stackPointer--;
  161.                         this.PC = this.stack[this.stackPointer];
  162.                         break;
  163.  
  164.                 }
  165.                
  166.                 break;
  167.  
  168.             case 0x1000:
  169.  
  170.                 // 1nnn - JP addr
  171.                 // Jump to location nnn
  172.                 this.PC = nnn;
  173.                 break;
  174.  
  175.             case 0x2000:
  176.  
  177.                 // 2nnn - CALL addr
  178.                 // Call subroutine at nnn
  179.                 this.stack[this.stackPointer] = this.PC + 2;
  180.                 this.stackPointer++;
  181.                 this.PC = nnn;
  182.                 break;
  183.  
  184.             case 0x3000:
  185.  
  186.                 // 3xkk - SE Vx, byte
  187.                 // Skip next instruction if Vx = kk
  188.                 if (this.V[x] == kk)
  189.                     this.PC += 2;
  190.                 this.PC += 2;
  191.                 break;
  192.  
  193.             case 0x4000:
  194.  
  195.                 // 4xkk - SNE Vx, byte
  196.                 // Skip next instruction if Vx != kk
  197.                 if (this.V[x] != kk)
  198.                     this.PC += 2;
  199.                 this.PC += 2;
  200.                 break;
  201.  
  202.             case 0x5000:
  203.  
  204.                 // 5xy0 - SE Vx, Vy
  205.                 // Skip next instruction if Vx = Vy
  206.                 if (this.V[x] == this.V[y])
  207.                     this.PC += 2;
  208.                 this.PC += 2;
  209.                 break;
  210.  
  211.             case 0x6000:
  212.  
  213.                 // 6xkk - LD Vx, byte
  214.                 // Set Vx = kk
  215.                 this.V[x] = kk;
  216.                 this.PC += 2;
  217.                 break;
  218.  
  219.             case 0x7000:
  220.  
  221.                 // 7xkk - ADD Vx, byte
  222.                 // Set Vx = Vx + kk
  223.                 this.V[x] += kk;
  224.                 this.PC += 2;
  225.                 break;
  226.  
  227.             case 0x8000:
  228.  
  229.                 switch (code & 0x000F) {
  230.  
  231.                     case 0x0000:
  232.  
  233.                         // 8xy0 - LD Vx, Vy
  234.                         // Set Vx = Vy
  235.                         this.V[x] = this.V[y];
  236.                         this.PC += 2;
  237.                         break;
  238.  
  239.                     case 0x0001:
  240.  
  241.                         // 8xy1 - OR Vx, Vy
  242.                         // Set Vx = Vx OR Vy
  243.                         this.V[x] = this.V[x] | this.V[y];
  244.                         this.PC += 2;
  245.                         break;
  246.  
  247.                     case 0x0002:
  248.  
  249.                         // 8xy2 - AND Vx, Vy
  250.                         // Set Vx = Vx AND Vy
  251.                         this.V[x] = this.V[x] & this.V[y];
  252.                         this.PC += 2;
  253.                         break;
  254.  
  255.                     case 0x0003:
  256.  
  257.                         // 8xy3 - XOR Vx, Vy
  258.                         // Set Vx = Vx XOR Vy
  259.                         this.V[x] = this.V[x] ^ this.V[y];
  260.                         this.PC += 2;
  261.                         break;
  262.  
  263.                     case 0x0004:
  264.  
  265.                         // 8xy4 - ADD Vx, Vy
  266.                         // Set Vx = Vx + Vy, set VF = carry
  267.                         let sum = this.V[x] + this.V[y];
  268.                         if (sum > 0xFF)
  269.                             this.V[0xF] = 1;
  270.                         else
  271.                             this.V[0xF] = 0;
  272.                         this.V[x] = sum & 0xFF;
  273.                         break;
  274.  
  275.                     case 0x0005:
  276.  
  277.                         // 8xy5 - SUB Vx, Vy
  278.                         // Set Vx = Vx - Vy, set VF = NOT borrow
  279.                         if (this.V[x] >= this.V[y])
  280.                             this.V[0xF] = 1;                // Vf = Not Borrow
  281.                         else
  282.                             this.V[0xF] = 0;
  283.                         this.V[x] -= this.V[y];
  284.                         this.PC += 2;
  285.                         break;
  286.  
  287.                     case 0x0006:
  288.  
  289.                         // 8xy6 - SHR Vx {, Vy}
  290.                         // Set Vx = Vx SHR 1
  291.                         if (this.V[x] & 0x01)
  292.                             this.V[0xF] = 1;
  293.                         else
  294.                             this.V[0xF] = 0;
  295.                         this.V[x] = (this.V[x] / 2) & 0xFF;         // >> or >>>
  296.                         this.PC += 2;
  297.                         break;
  298.  
  299.                     case 0x0007:
  300.  
  301.                         // 8xy7 - SUBN Vx, Vy
  302.                         // Set Vx = Vy - Vx, set VF = NOT borrow
  303.                         if (this.V[y] >= this.V[x])         // Vf = Not Borrow
  304.                             this.V[0xF] = 1;
  305.                         else
  306.                             this.V[0xF] = 0;
  307.                         this.V[x] = this.V[y] - this.V[x];
  308.                         this.PC += 2;
  309.                         break;
  310.  
  311.                     case 0x000E:
  312.  
  313.                         // 8xyE - SHL Vx {, Vy}
  314.                         // Set Vx = Vx SHL 1
  315.                         if (this.V[x] & 0x80)
  316.                             this.V[0xF] = 1;
  317.                         else
  318.                             this.V[0xF] = 0;
  319.                         this.V[x] = (this.V[x] * 2) & 0xFF;
  320.                         this.PC += 2;
  321.                         break;
  322.                 }
  323.                 break;
  324.  
  325.             case 0x9000:
  326.  
  327.                 // 9xy0 - SNE Vx, Vy
  328.                 // Skip next instruction if Vx != Vy
  329.                 if (this.V[x] != this.V[y])
  330.                     this.PC += 2;
  331.                 this.PC += 2;
  332.                 break;
  333.  
  334.             case 0xA000:
  335.  
  336.                 // Annn - LD I, addr
  337.                 // Set I = nnn
  338.                 this.I = nnn;
  339.                 this.PC += 2;
  340.                 break;
  341.  
  342.             case 0xB000:
  343.  
  344.                 // Bnnn - JP V0, addr
  345.                 // Jump to location nnn + V0
  346.                 this.PC = this.V[0] + nnn;
  347.                 break;
  348.  
  349.             case 0xC000:
  350.  
  351.                 // Cxkk - RND Vx, byte
  352.                 // Set Vx = random byte AND kk
  353.                 let random_byte = Math.floor(Math.random() * 256);      // 0 to 255
  354.                 this.V[x] = random_byte & kk;
  355.                 this.PC += 2;
  356.                 break;
  357.  
  358.             case 0xD000:
  359.  
  360.                 // Dxyn - DRW Vx, Vy, nibble
  361.                 // Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision
  362.                 this.V[0xF] = 0;
  363.                 let n = code & 0x000F;
  364.                 for (let i = 0; i < n; i++) {
  365.                     let byteToDraw = this.memory[this.I + i];
  366.                     for (let j = 0; j < 8; j++) {
  367.                         if ((byteToDraw & (0x80 >> j)) != 0) {
  368.                             if (this.display[(this.V[x] + j) + ((this.V[y] + i) * 64)] == 1)
  369.                                 this.V[0xF] = 1;
  370.                             this.display[(this.V[x] + j) + ((this.V[y] + i) * 64)] ^= 1;
  371.                         }
  372.                     }
  373.                 }
  374.                 this.drawFlag = true;
  375.                 this.PC += 2;
  376.                 break;
  377.  
  378.             case 0xE000:
  379.  
  380.                 switch (code & 0x00FF) {
  381.  
  382.                     case 0x009E:
  383.  
  384.                         // Ex9E - SKP Vx
  385.                         // Skip next instruction if key with the value of Vx is pressed
  386.                         if (this.keys[this.V[x]] == true)
  387.                             this.PC += 2;
  388.                         this.PC += 2;
  389.                         break;
  390.  
  391.                     case 0x00A1:
  392.  
  393.                         // ExA1 - SKNP Vx
  394.                         // Skip next instruction if key with the value of Vx is not pressed
  395.                         if (this.keys[this.V[x]] == false)
  396.                             this.PC += 2;
  397.                         this.PC += 2;
  398.                         break;
  399.                 }
  400.  
  401.             case 0xF000:
  402.  
  403.                 switch (code & 0x00FF) {
  404.  
  405.                     case 0x0007:
  406.  
  407.                         // Fx07 - LD Vx, DT
  408.                         // Set Vx = delay timer value
  409.                         this.V[x] = this.delayTimer;
  410.                         this.PC += 2;
  411.                         break;
  412.  
  413.                     case 0x000A:
  414.  
  415.                         // Fx0A - LD Vx, K
  416.                         // Wait for a key press, store the value of the key in Vx
  417.                         let waitingForKey = true;
  418.                         for (let i = 0; i < this.keys.length; i++) {
  419.                             if (this.keys[i]) {
  420.                                 this.V[x] = i;
  421.                                 waitingForKey = false;
  422.                             }
  423.                         }
  424.                         if (!waitingForKey)             // Move to next instruction only when key recorded
  425.                             this.PC += 2;
  426.                         break;
  427.  
  428.                     case 0x0015:
  429.  
  430.                         // Fx15 - LD DT, Vx
  431.                         // Set delay timer = Vx
  432.                         this.delayTimer = this.V[x];
  433.                         this.PC += 2;
  434.                         break;
  435.  
  436.                     case 0x0018:
  437.  
  438.                         // Fx18 - LD ST, Vx
  439.                         // Set sound timer = Vx
  440.                         this.soundTimer = this.V[x];
  441.                         this.PC += 2;
  442.                         break;
  443.  
  444.                     case 0x001E:
  445.  
  446.                         // Fx1E - ADD I, Vx
  447.                         // Set I = I + Vx
  448.                         this.I += this.V[x];
  449.                         this.PC += 2;
  450.                         break;
  451.  
  452.                     case 0x0029:
  453.  
  454.                         // Fx29 - LD F, Vx
  455.                         // Set I = location of sprite for digit Vx
  456.                         this.I = this.V[x] * 0x5;   // Each sprite is 5 bytes long
  457.                         this.PC += 2;
  458.                         break;
  459.  
  460.                     case 0x0033:
  461.  
  462.                         // Fx33 - LD B, Vx
  463.                         // Store BCD representation of Vx in memory locations I, I+1, and I+2
  464.                         this.memory[this.I] = this.V[x] / 100;
  465.                         this.memory[this.I + 1] = (this.V[x] / 10) % 10;
  466.                         this.memory[this.I + 2] = (this.V[x] % 100) % 10;
  467.                         break;
  468.  
  469.                     case 0x0055:
  470.  
  471.                         // Fx55 - LD [I], Vx
  472.                         // Store registers V0 through Vx in memory starting at location I
  473.                         for (let i = 0; i <= x; i++) {
  474.                             this.memory[this.I + i] = this.V[i];
  475.                         }
  476.                         // this.I += x + 1;
  477.                         this.PC += 2;
  478.                         break;
  479.  
  480.                     case 0x0065:
  481.  
  482.                         // Fx65 - LD Vx, [I]
  483.                         // Read registers V0 through Vx from memory starting at location I
  484.                         for (let i = 0; i <= x; i++) {
  485.                             this.V[i] = this.memory[this.I + i];
  486.                         }
  487.                         // this.I += x + 1;
  488.                         this.PC += 2;
  489.                         break;
  490.  
  491.                 }
  492.  
  493.                 break;
  494.             // no default case (is it needed?)
  495.         }
  496.  
  497.     } // End of  emulateOpcode(opcode)
  498.  
  499. } // End of CPU class
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement