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 = {}));