Fix V8 emulator bugs

This commit is contained in:
Asraelite 2024-01-09 21:05:38 +01:00
parent c45ad79440
commit 476972f85a
29 changed files with 281 additions and 18 deletions

View file

@ -39,6 +39,10 @@
<label for="config-raw-output-input">raw output: </label>
<input type="checkbox" id="config-raw-output-input">
</div>
<div id="config-focus-emulator">
<label for="config-focus-emulator-input">focus emulator: </label>
<input type="checkbox" id="config-focus-emulator-input">
</div>
<!-- <div id="config-line-numbers">
<label for="config-line-numbers-input">line numbers: </label>
<input type="checkbox" id="config-labels-input">

View file

@ -8,7 +8,7 @@ import { ArchName, ArchSpecification, AssemblyInput, AssemblyOutput, Instruction
import { toNote } from './util.js';
const CONFIG_VERSION: string = '1';
const EMULATOR_SPEED = 60;
const EMULATOR_SPEED = 30;
window.addEventListener('load', init);
@ -51,6 +51,7 @@ type ConfigurationSpecification = {
syntaxHighlighting: string;
inlineSourceFormat: string;
rawOutput: boolean;
focusEmulator: boolean;
sendDelay: number;
};
@ -62,6 +63,7 @@ class Configuration {
syntaxHighlighting: string;
inlineSourceFormat: string;
rawOutput: boolean;
focusEmulator: boolean;
sendDelay: number;
private formElement: HTMLFormElement;
@ -73,6 +75,7 @@ class Configuration {
private targetArchDocsButton: HTMLButtonElement;
private syntaxHighlightingSelect: HTMLSelectElement;
private rawOutputCheckbox: HTMLInputElement;
private focusEmulatorCheckbox: HTMLInputElement;
private sendDelayInput: HTMLInputElement;
constructor() {
@ -85,6 +88,7 @@ class Configuration {
this.targetArchDocsButton = document.getElementById('config-target-arch-docs-button') as HTMLButtonElement;
this.syntaxHighlightingSelect = document.getElementById('config-syntax-highlighting-select') as HTMLSelectElement;
this.rawOutputCheckbox = document.getElementById('config-raw-output-input') as HTMLInputElement;
this.focusEmulatorCheckbox = document.getElementById('config-focus-emulator-input') as HTMLInputElement;
this.sendDelayInput = document.getElementById('control-send-speed') as HTMLInputElement;
this.formElement.addEventListener('change', () => {
@ -115,10 +119,17 @@ class Configuration {
this.machineCodeShowLabels = this.labelsCheckbox.checked;
this.inlineSourceFormat = this.inlineSourceSelect.value;
this.rawOutput = this.rawOutputCheckbox.checked;
this.focusEmulator = this.focusEmulatorCheckbox.checked;
this.sendDelay = parseInt(this.sendDelayInput.value);
document.getElementById('assembly').className = `theme-${this.syntaxHighlighting}`;
if (this.focusEmulator) {
document.getElementById('page').classList.add('emulator');
} else {
document.getElementById('page').classList.remove('emulator');
}
this.saveToLocalStorage();
}
@ -139,6 +150,7 @@ class Configuration {
configuration.targetArch = parsed.targetArch;
configuration.syntaxHighlighting = parsed.syntaxHighlighting;
configuration.inlineSourceFormat = parsed.inlineSourceFormat;
configuration.focusEmulator = parsed.focusEmulator;
configuration.rawOutput = parsed.rawOutput;
configuration.sendDelay = parsed.sendDelay;
@ -149,6 +161,7 @@ class Configuration {
configuration.syntaxHighlightingSelect.value = configuration.syntaxHighlighting;
configuration.inlineSourceSelect.value = configuration.inlineSourceFormat;
configuration.rawOutputCheckbox.checked = configuration.rawOutput;
configuration.focusEmulatorCheckbox.checked = configuration.focusEmulator;
configuration.sendDelayInput.value = configuration.sendDelay.toString();
}
}
@ -169,6 +182,7 @@ class Configuration {
inlineSourceFormat: this.inlineSourceFormat,
syntaxHighlighting: this.syntaxHighlighting,
rawOutput: this.rawOutput,
focusEmulator: this.focusEmulator,
sendDelay: this.sendDelay,
};
localStorage.setItem('configuration', JSON.stringify(values));

View file

@ -35,6 +35,10 @@ input[type="checkbox"] {
"assembly-status machine-code-status";
}
#page.emulator {
grid-template-rows: 0fr 1fr 300px;
}
@media (max-width: 900px) {
#page {
grid-template-columns: 1fr;
@ -62,7 +66,7 @@ input[type="checkbox"] {
grid-template-areas:
"target-arch syntax-highlighting labels"
"output-format source raw-output"
"address-format none none";
"address-format breakpoints focus-emulator";
}
#controls>form>div {
@ -81,6 +85,14 @@ input[type="checkbox"] {
grid-area: raw-output;
}
#config-focus-emulator {
grid-area: focus-emulator;
}
#config-breakpoints {
grid-area: breakpoints;
}
#config-line-numbers {
grid-area: config-line-numbers;
}

View file

@ -618,6 +618,7 @@ class ParvaEmulator implements Emulator {
screen: [] as Array<Array<number>>,
screenBuffer: [] as Array<Array<number>>,
};
previousFrameAccessedMemory: boolean = false;
constructor() { }
@ -632,6 +633,12 @@ class ParvaEmulator implements Emulator {
}
step() {
}
stepCore(): boolean {
let coreTerminates = false;
const instruction = this.memory[this.pc] ?? 0;
const bits = instruction.toString(2).padStart(24, '0');
@ -715,6 +722,8 @@ class ParvaEmulator implements Emulator {
this.memory[address + 1] = valueD1;
}
}
this.previousFrameAccessedMemory = true;
} else if (operationType === '11') {
// branching
const special = condition.startsWith('11');
@ -747,6 +756,7 @@ class ParvaEmulator implements Emulator {
result = !result;
}
if (result) {
coreTerminates = true;
this.pc = targetAddress - 1;
}
}
@ -755,6 +765,7 @@ class ParvaEmulator implements Emulator {
this.pc &= 0xffffff;
this.cycle += 1;
this.cycle &= 0xffffff;
return coreTerminates;
}
ioOut(device: number, command: number, valueA: number, valueB: number) {

View file

@ -440,16 +440,16 @@ class V8Emulator implements Emulator {
this.pc = this.memory[immediate] ?? 0;
} else if (bits === '00000100') {
// jz imm
this.pc = this.registers[0] === 0 ? immediate : this.pc + 1;
this.pc = this.zeroFlag ? immediate : this.pc + 2;
} else if (bits === '00000101') {
// jnz imm
this.pc = this.registers[0] !== 0 ? immediate : this.pc + 1;
this.pc = !this.zeroFlag ? immediate : this.pc + 2;
} else if (bits === '00000110') {
// jc imm
this.pc = this.carryFlag ? immediate : this.pc + 1;
this.pc = this.carryFlag ? immediate : this.pc + 2;
} else if (bits === '00000111') {
// jnc imm
this.pc = !this.carryFlag ? immediate : this.pc + 1;
this.pc = !this.carryFlag ? immediate : this.pc + 2;
} else if (bits === '00001000') {
// jsr imm
this.push(this.pc + 2);
@ -486,16 +486,16 @@ class V8Emulator implements Emulator {
this.aluOp(operand, (x, a) => x + a + (this.carryFlag ? 1 : 0));
} else if (bits.startsWith('00011')) {
// inc
this.aluOp(operand, (x, a) => x + 1);
this.aluOp(operand, (x, a) => x + 1, false);
} else if (bits.startsWith('00100')) {
// sbc
this.aluOp(operand, (x, a) => x - a - (this.carryFlag ? 1 : 0));
} else if (bits.startsWith('00101')) {
// dec
this.aluOp(operand, (x, a) => x - 1);
this.aluOp(operand, (x, a) => x - 1, false);
} else if (bits.startsWith('00110')) {
// not
this.aluOp(operand, (x, a) => (~x) & 0xff);
this.aluOp(operand, (x, a) => (~x) & 0xff, false);
} else if (bits.startsWith('00111')) {
// xor
this.aluOp(operand, (x, a) => x ^ a);
@ -519,11 +519,11 @@ class V8Emulator implements Emulator {
this.pc += 1;
} else if (bits.startsWith('01100')) {
// rol
this.aluOp(operand, (x, a) => (x << 1) + (x >>> 7));
this.aluOp(operand, (x, a) => (x << 1) + (x >>> 7), false);
} else if (bits.startsWith('01101')) {
// ror
const initialValue = this.getRegister(operand);
this.aluOp(operand, (x, a) => (x >>> 1) + ((x & 1) << 7));
this.aluOp(operand, (x, a) => (x >>> 1) + ((x & 1) << 7), false);
this.carryFlag = (initialValue & 1) === 1;
} else if (bits.startsWith('01110')) {
// cmp
@ -575,13 +575,13 @@ class V8Emulator implements Emulator {
}
}
aluOp(register: string, fn: (x: number, a: number) => number) {
aluOp(register: string, fn: (x: number, a: number) => number, destinationIsA = true) {
const operandValue = this.getRegister(register);
const aValue = this.registers[0];
const result = fn(operandValue, aValue);
this.zeroFlag = result === 0;
this.carryFlag = result > 0xff;
this.registers[0] = result & 0xff;
this.carryFlag = (result & 0xff) !== result;
this.setRegister(destinationIsA ? '0' : register, result & 0xff);
this.pc += 1;
}
@ -634,8 +634,12 @@ class V8Emulator implements Emulator {
memoryString += ' ';
}
}
const controlRegistersString = `<span style="background-color: #fcb55b">pc: ${this.pc.toString(16).padStart(2, '0')}</span>, `
+ `<span style="background-color: cyan">stack pointer: ${this.memory[STACK_POINTER_ADDRESS].toString(16).padStart(2, '0')}</span>, `
+ `carry flag: ${this.carryFlag ? 1 : 0}, `
+ `zero flag: ${this.zeroFlag ? 1 : 0}`;
return `
<pre><span style="background-color: #fcb55b">pc: ${this.pc.toString(16).padStart(2, '0')}</span>,<span style="background-color: cyan"> stack pointer: ${this.memory[STACK_POINTER_ADDRESS].toString(16).padStart(2, '0')}</span></pre>
<pre>${controlRegistersString}</pre>
<pre>${registersString}</pre>
<pre>memory:\n${memoryString}</pre>
`;