Advertisement
TheFastFish

AAAAAAGGGHHHHHHHH

May 4th, 2015
606
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. CHIP-JS8
  3. A CHIP-8 virtual machine/emulator for JavaScript
  4. by slacker' 2015
  5. ---
  6. TODO:
  7. -implement timers
  8. -implement keyboard
  9. -implement sprite drawing
  10. -future plan: write an assembly language instead of raw machine code
  11. */
  12. //globally scoped for debugging purposes
  13. var draw;
  14. var i;
  15. var j;
  16. var x;
  17. var y;
  18. var interval0;
  19. var interval1;
  20. var jumpflag;
  21. var ll;
  22. var vx;
  23. var vy;
  24. var addr;
  25. var l;
  26. var h;
  27. var hh;
  28. var opcode;
  29. var drawColor;
  30.  
  31. function getRandomInt(min, max) {
  32.     return Math.floor(Math.random() * (max - min + 1)) + min;
  33. }
  34.  
  35. //complete virtual machine object
  36. var machine = {
  37.     state: 0,
  38.     memory: new Uint8Array(4096),
  39.     callstack: [],
  40.     callpointer: -1,
  41.     displaywidth: 64,
  42.     displayheight: 32,
  43.     displayscale: 10,
  44.     delaytimer: 0,
  45.     soundtimer: 0,
  46.     pc: 0x200,
  47.     i: 0,
  48.     register: new Uint8Array(16),
  49.     rate: 1000,
  50.     font: [
  51.     0xF0,
  52.     0x90,
  53.     0x90,
  54.     0x90,
  55.     0xF0,
  56.     0x20,
  57.     0x60,
  58.     0x20,
  59.     0x20,
  60.     0x70,
  61.     0xF0,
  62.     0x10,
  63.     0xF0,
  64.     0x80,
  65.     0xF0,
  66.     0xF0,
  67.     0x10,
  68.     0xF0,
  69.     0x10,
  70.     0xF0,
  71.     0x90,
  72.     0x90,
  73.     0xF0,
  74.     0x10,
  75.     0x10,
  76.     0xF0,
  77.     0x80,
  78.     0xF0,
  79.     0x10,
  80.     0xF0,
  81.     0xF0,
  82.     0x80,
  83.     0xF0,
  84.     0x90,
  85.     0xF0,
  86.     0xF0,
  87.     0x10,
  88.     0x20,
  89.     0x40,
  90.     0x40,
  91.     0xF0,
  92.     0x90,
  93.     0xF0,
  94.     0x90,
  95.     0xF0,
  96.     0xF0,
  97.     0x90,
  98.     0xF0,
  99.     0x10,
  100.     0xF0,
  101.     0xF0,
  102.     0x90,
  103.     0xF0,
  104.     0x90,
  105.     0x90,
  106.     0xE0,
  107.     0x90,
  108.     0xE0,
  109.     0x90,
  110.     0xE0,
  111.     0xF0,
  112.     0x80,
  113.     0x80,
  114.     0x80,
  115.     0xF0,
  116.     0xE0,
  117.     0x90,
  118.     0x90,
  119.     0x90,
  120.     0xE0,
  121.     0xF0,
  122.     0x80,
  123.     0xF0,
  124.     0x80,
  125.     0xF0,
  126.     0xF0,
  127.     0x80,
  128.     0xF0,
  129.     0x80,
  130.     0x80],
  131.     display: document.getElementById("display"),
  132.     input: document.getElementById("input"),
  133.     init: function () {
  134.         display.width = this.displaywidth * this.displayscale;
  135.         display.height = this.displayheight * this.displayscale;
  136.         draw = this.display.getContext("2d");
  137.         draw.fillRect(0, 0, this.display.width, this.display.height, "#000");
  138.         for (i = 0; i < this.font.length; i++) {
  139.             this.memory[i] = this.font[i];
  140.         }
  141.     },
  142.     doCycle: function () {
  143.         l = this.memory[this.pc + 1].toString(16);
  144.         h = this.memory[this.pc].toString(16);
  145.         if (h.length == 1) {
  146.             h = "0" + h;
  147.         }
  148.         if (l.length == 1) {
  149.             l = "0" + l;
  150.         }
  151.         opcode = h + l;
  152.         opcode = opcode.toUpperCase();
  153.         hh = opcode.charAt(0);
  154.         switch (hh) {
  155.             case "0":
  156.                 ll = opcode.charAt(3);
  157.                 switch (ll) {
  158.                     case "0":
  159.                         draw.fillRect(0, 0, this.display.width, this.display.height, "#000");
  160.                         break;
  161.                     case "E":
  162.                         this.pc = this.callstack[this.callpointer];
  163.                         this.callpointer--;
  164.                         break;
  165.                     default:
  166.                         throw {
  167.                             name: "opcodeDecodeError",
  168.                             message: "unknown error decoding opcode " + opcode
  169.                         };
  170.                 }
  171.                 break;
  172.             case "1":
  173.                 addr = parseInt(opcode.substring(1, 4), 16);
  174.                 this.pc = addr;
  175.                 jumpflag = true;
  176.                 break;
  177.             case "2":
  178.                 addr = parseInt(opcode.substring(1, 4), 16);
  179.                 this.callpointer++;
  180.                 this.callstack[this.callpointer] = this.pc;
  181.                 this.pc = addr;
  182.                 jumpflag = true;
  183.                 break;
  184.             case "3":
  185.                 vx = parseInt(opcode.charAt(1), 16);
  186.                 l = parseInt(opcode.substring(2, 4), 16);
  187.                 if (this.register[vx] == l) {
  188.                     this.pc += 2;
  189.                 }
  190.                 break;
  191.             case "4":
  192.                 vx = parseInt(opcode.charAt(1), 16);
  193.                 l = parseInt(opcode.substring(2, 4), 16);
  194.                 if (this.register[vx] != l) {
  195.                     this.pc += 2;
  196.                 }
  197.                 break;
  198.             case "5":
  199.                 vx = parseInt(opcode.charAt(1), 16);
  200.                 vy = parseInt(opcode.charAt(2), 16);
  201.                 if (this.register[vx] == this.register[vy]) {
  202.                     this.pc += 2;
  203.                 }
  204.                 break;
  205.             case "6":
  206.                 vx = parseInt(opcode.charAt(1), 16);
  207.                 l = parseInt(opcode.substring(2, 4), 16);
  208.                 this.register[vx] = l;
  209.                 break;
  210.             case "7":
  211.                 vx = parseInt(opcode.charAt(1), 16);
  212.                 l = parseInt(opcode.substring(2, 4), 16);
  213.                 this.register[vx] += l;
  214.                 break;
  215.             case "8":
  216.                 ll = instruction.charAt(3);
  217.                 vx = parseInt(opcode.charAt(1), 16);
  218.                 vy = parseInt(opcode.charAt(2), 16);
  219.                 switch (ll) {
  220.                     case "0":
  221.                         this.register[vx] = this.register[vy];
  222.                         break;
  223.                     case "1":
  224.                         this.register[vx] = this.register[vx] | this.register[vy];
  225.                         break;
  226.                     case "2":
  227.                         this.register[vx] = this.register[vx] & this.register[vy];
  228.                         break;
  229.                     case "3":
  230.                         this.register[vx] = this.register[vx] ^ this.register[vy];
  231.                         break;
  232.                     case "4":
  233.                         this.register[vx] = this.register[vx] + this.register[vy];
  234.                         break;
  235.                     case "5":
  236.                         this.register[vx] = this.register[vx] - this.register[vy];
  237.                         if (vx > vy) {
  238.                             this.register[15] = 1;
  239.                         } else {
  240.                             this.register[15] = 0;
  241.                         }
  242.                         break;
  243.                     case "6":
  244.                         this.register[15] = this.register[vy] & 0xFFFE;
  245.                         this.register[vx] = this.register[vy] >>> 1;
  246.                         break;
  247.                     case "7":
  248.                         this.register[vx] = this.register[vy] - this.register[vx];
  249.                         if (vy > vx) {
  250.                             this.register[15] = 1;
  251.                         } else {
  252.                             this.register[15] = 0;
  253.                         }
  254.                         break;
  255.                     case "E":
  256.                         this.register[15] = (this.register[vy] >> 7) & 0xFFFE;
  257.                         this.register[vx] = this.register[vy] << 1;
  258.                         break;
  259.                     default:
  260.                         throw {
  261.                             name: "opcodeDecodeError",
  262.                             message: "unknown error decoding opcode " + opcode
  263.                         };
  264.                 }
  265.                 break;
  266.             case "9":
  267.                 vx = parseInt(opcode.charAt(1), 16);
  268.                 vy = parseInt(opcode.charAt(2), 16);
  269.                 if (this.register[vx] != this.register[vy]) {
  270.                     this.pc += 2;
  271.                 }
  272.                 break;
  273.             case "A":
  274.                 addr = parseInt(opcode.substring(1, 4), 16);
  275.                 this.i = addr;
  276.                 break;
  277.             case "B":
  278.                 addr = parseInt(opcode.substring(1, 4), 16);
  279.                 this.pc = addr + this.register[0];
  280.                 jumpflag = true;
  281.                 break;
  282.             case "C":
  283.                 vx = parseInt(opcode.charAt(1), 16);
  284.                 l = parseInt(opcode.substring(2, 4), 16);
  285.                 this.register[vx] = getRandomInt(0, 256) & l;
  286.                 break;
  287.             case "D":
  288.                 vx = parseInt(opcode.charAt(1), 16);
  289.                 vy = parseInt(opcode.charAt(2), 16);
  290.                 ll = parseInt(opcode.charAt(3), 16);
  291.                 image = draw.getImageData(0, 0, this.display.width, this.display.height);
  292.                 for (i = 0; i < ll; i++) {
  293.                     linebyte = this.memory[this.i + i];
  294.                     for (j = 0; j < 8; j++) {
  295.                         bit = linebyte & (0x01 << j);
  296.                         cx = this.register[vx] * this.displayscale;
  297.                         cy = this.register[vy] * this.displayscale;
  298.                         data = imageDataReadPixel(cx, cy, image);
  299.                         if (bit){
  300.                             if (JSON.stringify(data) == JSON.stringify([0, 0, 0, 255])) {
  301.                                 drawColor = 255;
  302.                             } else if (JSON.stringify(data) == JSON.stringify([255, 255, 255, 255])) {
  303.                                 drawColor = 0;
  304.                                 this.register[15] = 1;
  305.                             } else {
  306.                                 throw {
  307.                                     name: "imageDataError",
  308.                                     message: "encountered error proccessing image data"
  309.                                 };
  310.                             }
  311.                             draw.fillRect(cx, cy, this.displayscale, this.displayscale, "#" +
  312.  
  313. drawColor.toString(16).repeat(3));
  314.                         }
  315.                     }
  316.                 }
  317.                 break;
  318.             case "E":
  319.                 vx = parseInt(opcode.charAt(1), 16);
  320.                 l = parseInt(opcode.substring(2, 4), 16);
  321.                 switch (l) {
  322.                     case "9E":
  323.                         //EX9E
  324.                         //skip next instruction if key value in VX is pressed
  325.                         break;
  326.                     case "A1":
  327.                         //EXA1
  328.                         //skip next instruction if key value in VX is not pressed
  329.                         break;
  330.                     default:
  331.                         throw {
  332.                             name: "opcodeDecodeError",
  333.                             message: "unknown error decoding opcode " + opcode
  334.                         };
  335.                 }
  336.                 break;
  337.             case "F":
  338.                 vx = parseInt(opcode.charAt(1), 16);
  339.                 l = opcode.substring(2, 4);
  340.                 switch (l) {
  341.                     case "07":
  342.                         //FX07
  343.                         //set VX to current value of delay timer
  344.                         break;
  345.                     case "0A":
  346.                         //FX0A
  347.                         //wait for keypress and store keycode in VX
  348.                         break;
  349.                     case "15":
  350.                         this.delaytimer = this.register[vx];
  351.                         break;
  352.                     case "18":
  353.                         this.delaytimer = this.register[vx];
  354.                         break;
  355.                     case "1E":
  356.                         this.i += this.register[vx];
  357.                         break;
  358.                     case "29":
  359.                         if ((this.register[vx] > 15) | (this.register[vx]) < 0) {
  360.                             throw {
  361.                                 name: "characterFetchError",
  362.                                 message: "Character code out of range"
  363.                             };
  364.                         }
  365.                         this.i = 5 * this.register[vx];
  366.                         break;
  367.                     case "33":
  368.                         var num = this.register[vx].toString(10);
  369.                         for (i = 0; i < 3; i++) {
  370.                             this.memory[this.i + i] = parseInt(num.charAt(i), 10);
  371.                         }
  372.                         break;
  373.                     case "55":
  374.                         for (i = 0; i <= vx; i++) {
  375.                             this.memory[this.i + i] = this.register[i];
  376.                         }
  377.                         this.i += i;
  378.                         break;
  379.                     case "65":
  380.                         for (i = 0; i <= vx; i++) {
  381.                             this.register[i] = this.memory[this.i + i];
  382.                         }
  383.                         this.i += i;
  384.                         break;
  385.                     default:
  386.                         throw {
  387.                             name: "opcodeDecodeError",
  388.                             message: "unknown error decoding opcode " + opcode
  389.                         };
  390.                 }
  391.                 break;
  392.             default:
  393.                 throw {
  394.                     name: "opcodeDecodeError",
  395.                     message: "unknown error decoding opcode " + opcode
  396.                 };
  397.         }
  398.         if (!jumpflag) {this.pc += 2;} else {jumpflag = false;}
  399.         return;
  400.     },
  401.     doTimers: function() {
  402.        
  403.     },
  404.     load: function () {
  405.         var text = this.input.value;
  406.         var code = text.match(/^[0-9A-F]{4}$/gm);
  407.         var h, l;
  408.         for (i = 0; i < code.length; i++) {
  409.             h = code[i].match(/^[0-9A-F]{2}/gm);
  410.             l = code[i].match(/[0-9A-F]{2}$/gm);
  411.             this.memory[i * 2 + 0x200] = parseInt(h, 16);
  412.             this.memory[i * 2 + 0x200 + 1] = parseInt(l, 16);
  413.         }
  414.         return;
  415.     }
  416. };
  417.  
  418. //debugging object
  419. var debug = {
  420.     dumpMemoryToConsole: function() {
  421.         console.log("Memory: ");
  422.         console.log(machine.memory);
  423.         return;
  424.     },
  425.     dumpRegistersToConsole: function() {
  426.         console.log("General registers:");
  427.         for (i = 0; i < 16; i++) {
  428.             console.log(i.toString(16) + ": " + machine.register[i]);
  429.         }
  430.         console.log("I register: " + machine.i);
  431.         console.log("Program counter: " + machine.pc);
  432.         return;
  433.     },
  434.     dumpStackToConsole: function () {
  435.         console.log("Stack pointer: " + machine.callpointer);
  436.         console.log("Call stack: ");
  437.         for (i = machine.callpointer; i <= 0; i++) {
  438.             console.log(i.toString(16) + ": " + machine.callstack[i]);
  439.         }
  440.     }
  441. };
  442.  
  443. machine.init();
  444. loadButton = document.getElementById("loadButton");
  445. loadButton.addEventListener("click", function(){machine.load();});
  446. runButton = document.getElementById("runButton");
  447. runButton.addEventListener("click", function(){
  448. interval0 = setInterval(function(){
  449. try {
  450. machine.doCycle();
  451. } catch(err) {
  452. alert(err.name + "\n" + err.message);
  453. }
  454. }, 1000 / machine.rate);});
  455. pauseButton = document.getElementById("pauseButton");
  456. pauseButton.addEventListener("click", function(){clearInterval(interval0);});
  457. stepButton = document.getElementById("stepButton");
  458. stepButton.addEventListener("click", function(){machine.doCycle();debug.dumpRegistersToConsole();});
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement