Modified QuestRunner's execution flow.

Now it goes like this:
1. Advance instruction pointer.
2. Check for breakpoints.
3. Execute instruction.

Also fixed some bugs.
This commit is contained in:
jtuu 2019-11-15 19:30:51 +02:00
parent 6e41b6fb79
commit 24abf3ae7f
2 changed files with 121 additions and 53 deletions

View File

@ -45,6 +45,11 @@ export class QuestRunner {
* A quest is running but the execution is currently paused.
*/
public readonly paused: Property<boolean> = this._paused;
/**
* Have we executed since last advancing the instruction pointer?
*/
private executed_since_advance = false;
private first_frame = true;
constructor() {
this.vm = new VirtualMachine(this.create_vm_io());
@ -73,6 +78,9 @@ export class QuestRunner {
this.vm.start_thread(0);
this._running.val = true;
this._paused.val = false;
this.executed_since_advance = false;
this.first_frame = true;
this.schedule_frame();
}
@ -110,6 +118,8 @@ export class QuestRunner {
this.stepping_breakpoints.push(dst_instr.asm.mnemonic.line_no);
}
}
this.schedule_frame();
}
public step_in(): void {
@ -139,6 +149,12 @@ export class QuestRunner {
}
public stop(): void {
this._running.val = false;
this._paused.val = false;
asm_editor_store?.unset_execution_location();
}
private schedule_frame(): void {
this.animation_frame = requestAnimationFrame(this.execution_loop);
}
@ -149,30 +165,45 @@ export class QuestRunner {
let need_emit_unpause = this.paused.val;
exec_loop: while (true) {
result = this.vm.execute();
if (this.first_frame || this.executed_since_advance) {
if (!this.first_frame) {
this.vm.advance();
if (this.vm.halted) {
this.stop();
break exec_loop;
}
}
const srcloc = this.vm.get_current_source_location();
if (srcloc) {
// check if need to break
const hit_breakpoint =
this.break_on_next ||
asm_editor_store?.breakpoints.val.includes(srcloc.line_no) ||
this.stepping_breakpoints.includes(srcloc.line_no);
this.executed_since_advance = false;
this.break_on_next = false;
const srcloc = this.vm.get_current_source_location();
if (hit_breakpoint) {
this.stepping_breakpoints.length = 0;
asm_editor_store?.set_execution_location(srcloc.line_no);
break exec_loop;
if (srcloc) {
// check if need to break
const hit_breakpoint =
this.break_on_next ||
asm_editor_store?.breakpoints.val.includes(srcloc.line_no) ||
this.stepping_breakpoints.includes(srcloc.line_no);
this.break_on_next = false;
if (hit_breakpoint) {
this.stepping_breakpoints.length = 0;
asm_editor_store?.set_execution_location(srcloc.line_no);
break exec_loop;
}
}
// first instruction after pause did not break, set state to unpaused
if (need_emit_unpause) {
need_emit_unpause = false;
this._paused.val = false;
}
}
// first instruction after pause did not break, set state to unpaused
if (need_emit_unpause) {
need_emit_unpause = false;
this._paused.val = false;
}
result = this.vm.execute(false);
this.executed_since_advance = true;
switch (result) {
case ExecutionResult.WaitingVsync:
@ -189,12 +220,12 @@ export class QuestRunner {
this.schedule_frame();
break exec_loop;
case ExecutionResult.Halted:
this._running.val = false;
asm_editor_store?.unset_execution_location();
this.stop();
break exec_loop;
}
}
this.first_frame = false;
this._paused.val = true;
};

View File

@ -159,6 +159,7 @@ export class VirtualMachine {
private list_open = false;
private selection_reg = 0;
private cur_srcloc?: AsmToken;
private _halted = true;
constructor(private io: VirtualMachineIO = new VMIOStub()) {
srand(GetTickCount());
@ -216,29 +217,81 @@ export class VirtualMachine {
);
}
this.thread.push(
new Thread(
this.io,
new ExecutionLocation(seg_idx!, 0),
this.memory.allocate(ARG_STACK_SLOT_SIZE * ARG_STACK_LENGTH),
true,
),
const thread = new Thread(
this.io,
new ExecutionLocation(seg_idx!, 0),
this.memory.allocate(ARG_STACK_SLOT_SIZE * ARG_STACK_LENGTH),
true,
);
this.thread.push(thread);
this._halted = false;
this.update_source_location(thread);
}
private dispose_thread(thread_idx: number): void {
this.thread[thread_idx].dispose();
this.thread.splice(thread_idx, 1);
if (this.thread.length === 0) {
this.halt();
}
}
private update_source_location(exec: Thread) {
const inst = this.get_next_instruction_from_thread(exec);
if (inst.asm && inst.asm.mnemonic) {
this.cur_srcloc = inst.asm.mnemonic;
}
}
/**
* Move to next instruction.
*/
public advance(): void {
if (this._halted) {
return;
}
const exec = this.thread[this.thread_idx];
if (exec.call_stack.length) {
const top = exec.call_stack_top();
const segment = this.object_code[top.seg_idx] as InstructionSegment;
// move to next instruction
if (++top.inst_idx >= segment.instructions.length) {
// segment ended, move to next segment
if (++top.seg_idx >= this.object_code.length) {
// eof
this.dispose_thread(this.thread_idx);
} else {
top.inst_idx = 0;
}
}
}
if (this.thread.length === 0) {
this.halt();
} else {
this.update_source_location(exec);
}
}
public get halted(): boolean {
return this._halted;
}
/**
* Executes the next instruction if one is scheduled.
* @param auto_advance Should the instruction pointer be automatically advanced.
*/
execute(): ExecutionResult {
execute(auto_advance: boolean = true): ExecutionResult {
let srcloc: AsmToken | undefined;
if (this.thread.length === 0) {
this.cur_srcloc = undefined;
if (this._halted) {
return ExecutionResult.Halted;
}
@ -246,13 +299,7 @@ export class VirtualMachine {
const exec = this.thread[this.thread_idx];
const inst = this.get_next_instruction_from_thread(exec);
if (inst.asm && inst.asm.mnemonic) {
srcloc = inst.asm.mnemonic;
}
this.cur_srcloc = srcloc;
return this.execute_instruction(exec, inst, srcloc);
return this.execute_instruction(auto_advance, exec, inst, srcloc);
} catch (err) {
if (!(err instanceof Error)) {
err = new Error(String(err));
@ -266,6 +313,7 @@ export class VirtualMachine {
}
private execute_instruction(
auto_advance: boolean,
exec: Thread,
inst: Instruction,
srcloc?: AsmToken,
@ -689,21 +737,8 @@ export class VirtualMachine {
break;
}
// advance instruction "pointer"
if (exec.call_stack.length) {
const top = exec.call_stack_top();
const segment = this.object_code[top.seg_idx] as InstructionSegment;
// move to next instruction
if (++top.inst_idx >= segment.instructions.length) {
// segment ended, move to next segment
if (++top.seg_idx >= this.object_code.length) {
// eof
this.dispose_thread(this.thread_idx);
} else {
top.inst_idx = 0;
}
}
if (auto_advance) {
this.advance();
}
return result;
@ -726,6 +761,8 @@ export class VirtualMachine {
thread.dispose();
}
this.cur_srcloc = undefined;
this._halted = true;
this.window_msg_open = false;
this.thread = [];
@ -839,7 +876,7 @@ export class VirtualMachine {
// popped off the last return address
// which means this is the end of the function this thread was started on
// which means this is the end of this thread
this.thread.splice(idx, 1);
this.dispose_thread(idx);
}
}