Source: chip8-is.js

var chip8;

(function (chip8) {
    'use strict';

    /**
     * Module which keeps instructions set.
     * @namespace chip8.CPU.IS
     * @memberof chip8.CPU
     */
    chip8.CPU.IS =  /** @lends chip8.CPU.IS */ {
        /**
         * <pre><code>0nnn - SYS addr</code></pre>
         * Jump to a machine code routine at nnn.
         * This instruction is only used on the old computers on which Chip-8 was originally implemented.
         * It is ignored by modern interpreters.
         * @returns {Function}
         */
        SYS: function () {
            return function (cpu) {
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            } ;
        },

        /**
         * <pre><code>00E0 - CLS</code></pre>
         * Clears the display.
         * @returns {Function}
         */
        CLS: function () {
            return function (cpu) {
                cpu.screen.clear();
                cpu.repaint = true;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy0 - LD Vx, Vy</code></pre>
         * Set Vx = Vy
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        LD_Vx_Vy: function (x, y) {
            return function (cpu) {
                cpu.V[x] = cpu.V[y];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },


        /**
         * <pre><code>6xkk - LD Vx, kk</code></pre>
         * Set Vx = kk
         * @param {number} x
         * @param {number} kk
         * @returns {Function}
         */
        LD_Vx_kk: function (x, kk) {
            return function (cpu) {
                cpu.V[x] = kk;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy1 - OR Vx, Vy</code></pre>
         * Set Vx = Vx OR Vy.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        OR_Vx_Vy: function (x, y) {
            return function (cpu) {
                cpu.V[x] = cpu.V[x] | cpu.V[y];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy2 - AND Vx, Vy</code></pre>
         * Set Vx = Vx AND Vy.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        AND_Vx_Vy: function (x, y) {
            return function (cpu) {
                cpu.V[x] = cpu.V[x] & cpu.V[y];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy3 - XOR Vx, Vy</code></pre>
         * Set Vx = Vx XOR Vy.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        XOR_Vx_Vy: function (x, y) {
            return function (cpu) {
                cpu.V[x] = cpu.V[x] ^ cpu.V[y];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy4 - ADD Vx, Vy</code></pre>
         * Set Vx = Vx + Vy, set VF = carry.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        ADD_Vx_Vy: function (x, y) {
            return function (cpu) {
                var sum = cpu.V[x] + cpu.V[y];
                cpu.V[0xF] = (sum > 0xFF ? 1 : 0); // carry
                cpu.V[x] = sum;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy5 - SUB Vx, Vy</code></pre>
         * Set Vx = Vx - Vy, set VF = NOT borrow.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        SUB_Vx_Vy: function (x, y) {
            return function (cpu) {
                cpu.V[0xF] = (cpu.V[x] >= cpu.V[y] ? 1 : 0); // NOT borrow
                cpu.V[x] = cpu.V[x] - cpu.V[y];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy7 - SUBN Vx, Vy</code></pre>
         * Set Vx = Vy - Vx, set VF = NOT borrow.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        SUBN_Vx_Vy: function (x, y) {
            return function (cpu) {
                cpu.V[0xF] = (cpu.V[y] >= cpu.V[x] ? 1 : 0); // NOT borrow
                cpu.V[x] = cpu.V[y] - cpu.V[x];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xy6 - SHR Vx, Vy</code></pre>
         * Set Vx = Vy SHR 1.
         * If shift quirks enabled Vx = Vx SHR 1.
         * If the least-significant bit of shifted value is 1, then VF is set to 1, otherwise 0.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        SHR_Vx_Vy: function (x, y) {
            return function (cpu) {
                if (cpu.quirks.shift) {
                    y = x;
                }
                cpu.V[0xF] = cpu.V[y] & 0x01;
                cpu.V[x] = cpu.V[y] >> 1;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>8xyE - SHL Vx, Vy</code></pre>
         * Set Vx = Vy SHL 1.
         * If shift quirks enabled Vx = Vx SHL 1.
         * If the most-significant bit of shifted value is 1, then VF is set to 1, otherwise to 0.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        SHL_Vx_Vy: function (x, y) {
            return function (cpu) {
                if (cpu.quirks.shift) {
                    y = x;
                }
                cpu.V[0xF] = (cpu.V[y] >> 7) & 0x01;
                cpu.V[x] = cpu.V[y] << 1;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>7xkk - ADD Vx, kk</code></pre>
         * Set Vx = Vx + kk.
         * @param {number} x
         * @param {number} kk
         * @returns {Function}
         */
        ADD_Vx_kk: function (x, kk) {
            return function (cpu) {
                cpu.V[x] = cpu.V[x] + kk;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>9xy0 - SNE Vx, Vy</code></pre>
         * Skip next instruction if Vx != Vy.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        SNE_Vx_Vy: function (x, y) {
            return function (cpu) {
                if (cpu.V[x] !== cpu.V[y]) {
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>4xkk - SNE Vx, kk</code></pre>
         * Skip next instruction if Vx != kk.
         * @param {number} x
         * @param {number} kk
         * @returns {Function}
         */
        SNE_Vx_kk: function (x, kk) {
            return function (cpu) {
                if (cpu.V[x] !== kk) {
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>3xkk - SE Vx, kk</code></pre>
         * Skip next instruction if Vx = kk.
         * @param {number} x
         * @param {number} kk
         * @returns {Function}
         */
        SE_Vx_kk: function (x, kk) {
            return function (cpu) {
                if (cpu.V[x] === kk) {
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>5xy0 - SE Vx, Vy</code></pre>
         * Skip next instruction if Vx = Vy.
         * @param {number} x
         * @param {number} y
         * @returns {Function}
         */
        SE_Vx_Vy: function (x, y) {
            return function (cpu) {
                if (cpu.V[x] === cpu.V[y]) {
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>1nnn - JP nnn</code></pre>
         * Jump to location nnn.
         * @param {number} nnn
         * @returns {Function}
         */
        JP_nnn: function (nnn) {
            return function (cpu) {
                cpu.pc = nnn;
            };
        },

        /**
         * <pre><code>00EE - RET</code></pre>
         * Return from a subroutine.
         * @returns {Function}
         */
        RET: function () {
            return function (cpu) {
                cpu.sp = (cpu.sp - 1) & (cpu.stack.length - 1);
                cpu.pc = cpu.stack[cpu.sp];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>2nnn - CALL nnn</code></pre>
         * Call subroutine at nnn.
         * @param {number} nnn
         * @returns {Function}
         */
        CALL_nnn: function (nnn) {
            return function (cpu) {
                cpu.stack[cpu.sp] = cpu.pc;
                cpu.sp = (cpu.sp + 1) & (cpu.stack.length - 1);
                cpu.pc = nnn;
            };
        },

        /**
         * <pre><code>Annn - LD I, nnn</code></pre>
         * Set I = nnn.
         * @param {number} nnn
         * @returns {Function}
         */
        LD_I_nnn: function (nnn) {
            return function (cpu) {
                cpu.i = nnn;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Bnnn - JP V0, nnn</code></pre>
         * Jump to location nnn + V0.
         * @param {number} nnn
         * @returns {Function}
         */
        JP_V0_nnn: function (nnn) {
            return function (cpu) {
                cpu.pc = (nnn + cpu.V[0]) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Cxkk - RND Vx, kk</code></pre>
         * Set Vx = random byte AND kk.
         * @param {number} x
         * @param {number} kk
         * @returns {Function}
         */
        RND_Vx_kk: function (x, kk) {
            return function (cpu) {
                cpu.V[x] = Math.floor(Math.random() * (0xFF + 1)) & kk;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx33 - LD B, Vx</code></pre>
         * Store BCD representation of Vx in memory locations I, I+1, and I+2.
         * @param {number} x
         * @returns {Function}
         */
        LD_B_Vx : function (x) {
            return function (cpu) {
                cpu.mem[cpu.i]     = parseInt(cpu.V[x] / 100, 10);
                cpu.mem[cpu.i + 1] = parseInt(cpu.V[x] % 100 / 10, 10);
                cpu.mem[cpu.i + 2] = cpu.V[x] % 10;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx07 - LD Vx, DT</code></pre>
         * Set Vx = delay timer value.
         * @param {number} x
         * @returns {Function}
         */
        LD_Vx_DT: function (x) {
            return function (cpu) {
                cpu.V[x] = cpu.dt;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx15 - LD DT, Vx</code></pre>
         * Set delay timer = Vx.
         * @param {number} x
         * @returns {Function}
         */
        LD_DT_Vx: function (x) {
            return function (cpu) {
                cpu.dt = cpu.V[x];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx18 - LD ST, Vx</code></pre>
         * Set sound timer = Vx.
         * @param {number} x
         * @returns {Function}
         */
        LD_ST_Vx: function (x) {
            return function (cpu) {
                cpu.st = cpu.V[x];
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx1E - ADD I, Vx</code></pre>
         * Set I = I + Vx.
         * @param {number} x
         * @returns {Function}
         */
        ADD_I_Vx: function (x) {
            return function (cpu) {
                cpu.i = (cpu.i + cpu.V[x]) & 0xFFF;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx55 - LD [I], Vx</code></pre>
         * Store registers V0 through Vx in memory starting at location I.
         * The value of the I register will be incremented by X + 1, if load/store quirks are disabled.
         * @param {number} x
         * @returns {Function}
         */
        LD_I_Vx: function (x) {
            return function (cpu) {
                for (var i = 0 ; i <= x; ++i) {
                    cpu.mem[cpu.i + i] = cpu.V[i];
                }
                if (!cpu.quirks.loadStore) {
                    cpu.i += x + 1;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx65 - LD Vx, [I]</code></pre>
         * Read registers V0 through Vx from memory starting at location I.
         * The value of the I register will be incremented by X + 1, if load/store quirks are disabled.
         * @param {number} x
         * @returns {Function}
         */
        LD_Vx_I: function (x) {
            return function (cpu) {
                for (var i = 0; i <= x; ++i) {
                    cpu.V[i] = cpu.mem[cpu.i + i];
                }
                if (!cpu.quirks.loadStore) {
                    cpu.i += x + 1;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx0A - LD Vx, K</code></pre>
         * Wait for a key press, store the value of the key in Vx.
         * @param {number} x
         * @returns {Function}
         */
        LD_Vx_K: function (x) {
            return function (cpu) {
                cpu.halted = true;
                cpu.keyboard.onNextKeyPressed = function (key) {
                    cpu.V[x] = key;
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                    cpu.halted = false;
                };
            };
        },

        /**
         * <pre><code>Ex9E - SKP Vx</code></pre>
         * Skip next instruction if key with the value of Vx is pressed.
         * @param {number} x
         * @returns {Function}
         */
        SKP_Vx: function (x) {
            return function (cpu) {
                if (cpu.keyboard.isKeyPressed(cpu.V[x])) {
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>ExA1 - SKNP Vx</code></pre>
         * Skip next instruction if key with the value of Vx is not pressed.
         * @param {number} x
         * @returns {Function}
         */
        SKNP_Vx: function (x) {
            return function (cpu) {
                if (!cpu.keyboard.isKeyPressed(cpu.V[x])) {
                    cpu.pc = (cpu.pc + 2) & 0x0FFF;
                }
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Fx29 - LD F, Vx</code></pre>
         * Set I = location of sprite for digit Vx.
         * @param {number} x
         * @returns {Function}
         */
        LD_F_Vx: function (x) {
            return function (cpu) {
                cpu.i = cpu.V[x] * 5;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        },

        /**
         * <pre><code>Dxyn - DRW Vx, Vy, n</code></pre>
         * Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
         * @param {number} x
         * @param {number} y
         * @param {number} n
         * @returns {Function}
         */
        DRW_Vx_Vy_n: function (x, y, n) {
            return function (cpu) {
                var hline, vline, membyte, coll;

                cpu.V[0xF] = 0;
                for (hline = 0; hline < n; ++hline) {
                    membyte = cpu.mem[cpu.i + hline];

                    for  (vline = 0; vline < 8; ++vline) {
                        if ((membyte & (0x80 >> vline)) !== 0) {
                            coll = cpu.screen.togglePixel(cpu.V[x] + vline, cpu.V[y] + hline);
                            if (coll) {

                                cpu.V[0xF] = 1;
                            }
                        }
                    }
                }

                cpu.repaint = true;
                cpu.pc = (cpu.pc + 2) & 0x0FFF;
            };
        }
    };

})(chip8 || (chip8 = {}));