Fix some instructions
This commit is contained in:
parent
028a21ed7f
commit
a56d787878
43
index.html
43
index.html
@ -23,33 +23,40 @@
|
||||
|
||||
<h1>Chip 8 emulator</h1>
|
||||
<!-- Original screen is 64x32 -->
|
||||
<canvas id="emulator-screen" width="256" height="128"></canvas>
|
||||
<canvas id="emulator-screen" width="512" height="256"></canvas>
|
||||
|
||||
<script>
|
||||
// Assuming your library exposes an Emulator class
|
||||
console.log(Revuelto8ts);
|
||||
console.log(Revuelto8ts.Chip8Emulator);
|
||||
const emulator = new Revuelto8ts.Chip8Emulator();
|
||||
|
||||
// Load ROM (you'll need to implement this method in your emulator)
|
||||
|
||||
fetch('./roms/1-chip8-logo.ch8')
|
||||
let fullDirRoms = [
|
||||
'./roms/1-chip8-logo.ch8',
|
||||
'./roms/2-ibm-logo.ch8',
|
||||
'./roms/3-corax+.ch8',
|
||||
'./roms/4-flags.ch8',
|
||||
'./roms/5-quirks.ch8',
|
||||
'./roms/6-keypad.ch8',
|
||||
'./roms/7-beep.ch8',
|
||||
'./roms/8-scrolling.ch8',
|
||||
'./roms/Maze[David Winter, 199x].ch8',
|
||||
];
|
||||
|
||||
// Load ROM
|
||||
fetch(fullDirRoms[3])
|
||||
.then(response => response.arrayBuffer())
|
||||
.then(buffer => {
|
||||
//emulator.loadROM(new Uint8Array(buffer));
|
||||
// Load and start
|
||||
emulator.loadRom(buffer);
|
||||
.then(arrayBufferRom => {
|
||||
let romData = new Uint8Array(arrayBufferRom);
|
||||
emulator.loadRom(romData);
|
||||
});
|
||||
|
||||
const emulatorCanvas = document.getElementById('emulator-screen');
|
||||
|
||||
// Loop
|
||||
while(true) {
|
||||
emulator.emulateCycle();
|
||||
|
||||
emulator.drawGraphics();
|
||||
emulator.drawToCanvas(emulatorCanvas, 4);
|
||||
}
|
||||
setInterval(function() {
|
||||
if(emulator.isRomLoaded()) {
|
||||
//console.log(emulator.getCPUStatus());
|
||||
emulator.emulateCycle();
|
||||
emulator.drawToCanvas(emulatorCanvas, 8);
|
||||
}
|
||||
}, 5);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -7,27 +7,32 @@ class Chip8Emulator {
|
||||
keyboard: Keyboard;
|
||||
rom: Uint8Array;
|
||||
|
||||
romLoaded: boolean;
|
||||
|
||||
constructor() {
|
||||
this.keyboard = new Keyboard();
|
||||
this.cpu = new CPU(this.keyboard);
|
||||
|
||||
this.rom = new Uint8Array();
|
||||
this.romLoaded = false;
|
||||
}
|
||||
|
||||
loadRom(rom: Uint8Array) {
|
||||
this.rom = rom;
|
||||
this.cpu.loadRom(this.rom);
|
||||
this.romLoaded = true;
|
||||
}
|
||||
|
||||
isRomLoaded() {
|
||||
return this.romLoaded;
|
||||
}
|
||||
|
||||
emulateCycle() {
|
||||
// Fetch opcode
|
||||
// Decode opcode
|
||||
// Execute opcode
|
||||
|
||||
// Update timers
|
||||
this.cpu.cycle();
|
||||
}
|
||||
|
||||
drawGraphics() {
|
||||
|
||||
getCPUStatus() : string {
|
||||
return this.cpu.getCPUStatus();
|
||||
}
|
||||
|
||||
drawToCanvas(canvas: HTMLCanvasElement, size: number) {
|
||||
@ -43,11 +48,11 @@ class Chip8Emulator {
|
||||
for(let i = 0; i < this.cpu.displayMemory.length; i++) {
|
||||
let isOn: number = this.cpu.displayMemory[i];
|
||||
if(isOn === 1) {
|
||||
let y = Math.floor(i/width);
|
||||
let x = i - (y*width);
|
||||
let y = Math.floor(i/64);
|
||||
let x = i - (y*64);
|
||||
|
||||
ctx.fillStyle = "WHITE";
|
||||
ctx.fillRect(x, y, 1, 1);
|
||||
ctx.fillRect(x*size, y*size, size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
109
src/cpu.ts
109
src/cpu.ts
@ -18,7 +18,7 @@ class CPU {
|
||||
soundTimer: number; // 8b
|
||||
|
||||
stackPointer: number; // 16b
|
||||
stack: Array<number>;
|
||||
stack: Uint16Array;
|
||||
|
||||
memory: Uint8Array;
|
||||
displayMemory: Uint8Array;
|
||||
@ -34,7 +34,7 @@ class CPU {
|
||||
this.pc = 0x200;
|
||||
this.indexReg = 0;
|
||||
this.stackPointer = 0;
|
||||
this.stack = new Array<number>(STACK_SIZE);
|
||||
this.stack = new Uint16Array(STACK_SIZE);
|
||||
this.delayTimer = 0;
|
||||
this.soundTimer = 0;
|
||||
this.regs = new Uint8Array(REGISTERS);
|
||||
@ -99,11 +99,40 @@ class CPU {
|
||||
this.memory.set(characters, addressFont);
|
||||
}
|
||||
|
||||
public loadRom(rom: Uint8Array) {
|
||||
// Loads a ROM into starting PC addres??
|
||||
console.log(rom);
|
||||
for(let i = 0; i < rom.length; i++) {
|
||||
this.memory[this.pc+i] = rom[i];
|
||||
}
|
||||
this.memory.set(rom, this.pc);
|
||||
}
|
||||
|
||||
public getCPUStatus() : string{
|
||||
let status = "";
|
||||
status += "PC: " + this.pc + "\n";
|
||||
status += "I: " + this.indexReg.toString(16) + "\n";
|
||||
for(let i = 0; i < REGISTERS; i++) {
|
||||
status += "V" + i + ": " + this.regs[i].toString(16).toUpperCase().padStart(2, '0') + "\n";
|
||||
}
|
||||
status += "SP: " + this.stackPointer.toString(16) + "\n";
|
||||
for(let i = 0; i < this.stackPointer; i++) {
|
||||
status += "ST" + i + ": " + this.stack[i] + "\n";
|
||||
}
|
||||
status += "\n";
|
||||
let firstOpcode: number = this.memory[this.pc];
|
||||
let secondOpcode: number = this.memory[this.pc + 1];
|
||||
let opcode: number = firstOpcode << 8 | secondOpcode;
|
||||
status += "OPCODE FOR PC: " + opcode.toString(16).toUpperCase().padStart(4, '0') + "\n";
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private incrementPC() {
|
||||
this.pc += 2;
|
||||
}
|
||||
|
||||
private cycle() {
|
||||
public cycle() {
|
||||
// Fetch opcode
|
||||
let firstOpcode: number = this.memory[this.pc];
|
||||
let secondOpcode: number = this.memory[this.pc + 1];
|
||||
@ -111,38 +140,47 @@ class CPU {
|
||||
|
||||
// Decode opcode
|
||||
let nib1 = firstOpcode >> 4; // X000
|
||||
let nib2 = firstOpcode & 0xF; // 0X00
|
||||
let nib2 = firstOpcode & 0x0F; // 0X00
|
||||
let nib3 = secondOpcode >> 4; // 00X0
|
||||
let nib4 = secondOpcode & 0xF; // 000X
|
||||
let nib4 = secondOpcode & 0x0F; // 000X
|
||||
|
||||
switch(nib1) {
|
||||
case 0x0:
|
||||
if(opcode == 0x00E0) {
|
||||
// 00E0
|
||||
// Clear the screen
|
||||
this.clearDisplay();
|
||||
this.incrementPC();
|
||||
}else if(opcode == 0x00EE) {
|
||||
// 00EE
|
||||
// Returns from a subroutine
|
||||
this.stackPointer--;
|
||||
this.pc = this.stack[this.pc];
|
||||
if(this.stackPointer < 0) {
|
||||
throw new Error("Stack pointer cant go to -1");
|
||||
}
|
||||
this.pc = this.stack[this.stackPointer];
|
||||
}
|
||||
this.incrementPC();
|
||||
break;
|
||||
case 0x1:
|
||||
case 0x1: {
|
||||
// 1NNN
|
||||
// goto NNN;
|
||||
let threeNib = opcode & 0x0FFF;
|
||||
this.pc = threeNib;
|
||||
let address = opcode & 0x0FFF;
|
||||
this.pc = address;
|
||||
}
|
||||
break;
|
||||
case 0x2:
|
||||
case 0x2: {
|
||||
// 2NNN
|
||||
// Calls subroutine at NNN
|
||||
this.stack[this.stackPointer] = this.pc; // Store PC at stack
|
||||
this.stack[this.stackPointer] = this.pc + 2; // Store PC at stack
|
||||
this.stackPointer++; // Increment it
|
||||
this.pc = opcode & 0x0FFF; // Go to that address to execute code
|
||||
let address = opcode & 0x0FFF;
|
||||
this.pc = address; // Go to that address to execute code
|
||||
}
|
||||
break;
|
||||
case 0x3: {
|
||||
// 3XNN
|
||||
// Skips the next instruction if VX equals NN
|
||||
let val = opcode & 0x00FF; //0x00NN
|
||||
if(this.regs[nib2] == val){
|
||||
if(this.regs[nib2] == secondOpcode){
|
||||
this.incrementPC();
|
||||
}
|
||||
this.incrementPC();
|
||||
@ -151,8 +189,7 @@ class CPU {
|
||||
case 0x4: {
|
||||
// 4XNN
|
||||
// Skips the next instruction if VX NOT equals NN
|
||||
let val = opcode & 0x00FF; //0x00NN
|
||||
if(this.regs[nib2] != val){
|
||||
if(this.regs[nib2] != secondOpcode){
|
||||
this.incrementPC();
|
||||
}
|
||||
this.incrementPC();
|
||||
@ -177,7 +214,7 @@ class CPU {
|
||||
case 0x7: {
|
||||
// 7XNN
|
||||
// Adds NN to VX
|
||||
this.regs[nib2] = secondOpcode;
|
||||
this.regs[nib2] += secondOpcode;
|
||||
this.incrementPC();
|
||||
}
|
||||
break;
|
||||
@ -204,37 +241,40 @@ class CPU {
|
||||
this.incrementPC();
|
||||
}else if(nib4 == 0x4) {
|
||||
// 8XY4
|
||||
// Vx += Vy
|
||||
// Vx += Vy, Set VF to carry
|
||||
let sum = this.regs[nib2] + this.regs[nib3];
|
||||
this.regs[0xF] = sum > 255 ? 1 : 0; // Set carry flag
|
||||
this.regs[nib2] = sum & 0xFF; // Get only the first byte
|
||||
this.regs[0xF] = sum > 255 ? 1 : 0; // Set carry flag
|
||||
this.incrementPC();
|
||||
}else if(nib4 == 0x5) {
|
||||
// 8XY5
|
||||
// Vx -= Vy
|
||||
this.regs[0xF] = this.regs[nib2] > this.regs[nib3] ? 1 : 0;
|
||||
let subs = this.regs[nib2] - this.regs[nib3];
|
||||
this.regs[nib2] = subs;
|
||||
this.regs[0xF] = this.regs[nib2] > this.regs[nib3] ? 1 : 0;
|
||||
this.regs[nib2] = subs & 0xFF;
|
||||
this.incrementPC();
|
||||
}else if(nib4 == 0x6) {
|
||||
// 8XY6
|
||||
// Vx >>= 1
|
||||
this.regs[0xF] = this.regs[0xF] & 1; // ???? // Store the least significant bit of VX prior
|
||||
this.regs[nib2] >>= 1;
|
||||
// Stores the least significant bit of VX prior to the shift in VF
|
||||
// Then. Shift Vx by 1 to the right.
|
||||
this.regs[0xF] = this.regs[nib2] & 1; // Get the least significat bit
|
||||
this.regs[nib2] = this.regs[nib2] >> 1;
|
||||
this.incrementPC();
|
||||
}else if(nib4 == 0x7) {
|
||||
// 8XY7
|
||||
// Vx = Vy - Vx
|
||||
this.regs[0xF] = this.regs[nib3] > this.regs[nib2] ? 1 : 0;
|
||||
let subs = this.regs[nib3] - this.regs[nib2];
|
||||
this.regs[nib2] = subs;
|
||||
this.regs[0xF] = this.regs[nib3] > this.regs[nib2] ? 1 : 0;
|
||||
this.incrementPC();
|
||||
}else if(nib4 == 0xE) {
|
||||
// 8XYE
|
||||
// Vx <<= 1
|
||||
// Set VF to 1 if the most significant bit of VX prior to that shift was set
|
||||
this.regs[0xF] = this.regs[0xF] >> 3;
|
||||
this.regs[nib2] <<= 1;
|
||||
// Set VF to 1 if the most significant bit of VX.
|
||||
// Then shift to the left Vx
|
||||
this.regs[0xF] = this.regs[nib2] >> 7;
|
||||
this.regs[nib2] = (this.regs[nib2] << 1) & 0xFF;
|
||||
this.incrementPC();
|
||||
}
|
||||
|
||||
@ -253,14 +293,16 @@ class CPU {
|
||||
case 0xA: {
|
||||
// ANNN
|
||||
// Sets I to the address NNN
|
||||
this.indexReg = opcode & 0x0FFF;
|
||||
let address = opcode & 0x0FFF;
|
||||
this.indexReg = address;
|
||||
this.incrementPC();
|
||||
}
|
||||
break;
|
||||
case 0xB:{
|
||||
// BNNN
|
||||
// Jumps to the address NNN plus V0
|
||||
this.pc = this.regs[0] + opcode & 0x0FFF;
|
||||
let address = opcode & 0x0FFF;
|
||||
this.pc = this.regs[0] + address;
|
||||
}
|
||||
break;
|
||||
case 0xC: {
|
||||
@ -380,7 +422,7 @@ class CPU {
|
||||
// FX55
|
||||
// reg_dump(Vx, &I)
|
||||
// Stores from V0 to VX in memory starting at address I.
|
||||
for(let i = 0; i < nib2; i++) {
|
||||
for(let i = 0; i <= nib2; i++) {
|
||||
this.memory[this.indexReg + i] = this.regs[i];
|
||||
}
|
||||
this.incrementPC();
|
||||
@ -388,7 +430,7 @@ class CPU {
|
||||
// FX65
|
||||
// reg_load(Vx, &I)
|
||||
// Fills fomr V0 to VX with values from memory staring at address I.
|
||||
for(let i = 0; i < nib2; i++) {
|
||||
for(let i = 0; i <= nib2; i++) {
|
||||
this.regs[i] = this.memory[this.indexReg + i];
|
||||
}
|
||||
this.incrementPC();
|
||||
@ -396,6 +438,9 @@ class CPU {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(this.delayTimer > 0)this.delayTimer--;
|
||||
if(this.soundTimer > 0)this.soundTimer--; // Should I play a sound???
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user