idle on hlt instruction
This commit is contained in:
parent
e5adfc93c2
commit
74dca95e23
|
@ -144,6 +144,7 @@ ACPI.prototype.timer = function(now)
|
|||
}
|
||||
|
||||
this.last_timer = timer;
|
||||
return 100; // TODO
|
||||
};
|
||||
|
||||
ACPI.prototype.get_timer = function(now)
|
||||
|
|
17
src/apic.js
17
src/apic.js
|
@ -373,19 +373,14 @@ APIC.prototype.timer = function(now)
|
|||
{
|
||||
if(this.timer_current_count === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//dbg_log(now + " " + this.next_tick, LOG_APIC);
|
||||
|
||||
var steps = (now - this.next_tick) * APIC_TIMER_FREQ / (1 << this.timer_divider_shift) >>> 0;
|
||||
|
||||
if(steps === 0)
|
||||
{
|
||||
return;
|
||||
return 100;
|
||||
}
|
||||
|
||||
this.next_tick += steps / APIC_TIMER_FREQ * (1 << this.timer_divider_shift);
|
||||
const freq = APIC_TIMER_FREQ / (1 << this.timer_divider_shift);
|
||||
|
||||
const steps = (now - this.next_tick) * freq >>> 0;
|
||||
|
||||
this.next_tick += steps / freq;
|
||||
this.timer_current_count -= steps;
|
||||
|
||||
if(this.timer_current_count <= 0)
|
||||
|
@ -418,6 +413,8 @@ APIC.prototype.timer = function(now)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(0, this.timer_current_count / freq);
|
||||
};
|
||||
|
||||
APIC.prototype.route = function(vector, mode, is_level, destination, destination_mode)
|
||||
|
|
76
src/cpu.js
76
src/cpu.js
|
@ -11,8 +11,9 @@ var CPU_LOG_VERBOSE = false;
|
|||
|
||||
|
||||
/** @constructor */
|
||||
function CPU(bus, wm)
|
||||
function CPU(bus, wm, next_tick_immediately)
|
||||
{
|
||||
this.next_tick_immediately = next_tick_immediately;
|
||||
this.wm = wm;
|
||||
this.wasm_patch();
|
||||
this.create_jit_imports();
|
||||
|
@ -562,15 +563,7 @@ CPU.prototype.main_run = function()
|
|||
{
|
||||
if(this.in_hlt[0])
|
||||
{
|
||||
//if(false)
|
||||
//{
|
||||
// var _t = this.hlt_loop();
|
||||
// var t = 0;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
var t = this.hlt_loop();
|
||||
//}
|
||||
const t = this.hlt_loop();
|
||||
|
||||
if(this.in_hlt[0])
|
||||
{
|
||||
|
@ -578,7 +571,23 @@ CPU.prototype.main_run = function()
|
|||
}
|
||||
}
|
||||
|
||||
this.do_run();
|
||||
const start = v86.microtick();
|
||||
let now = start;
|
||||
|
||||
for(; now - start < TIME_PER_FRAME;)
|
||||
{
|
||||
this.do_many_cycles();
|
||||
|
||||
now = v86.microtick();
|
||||
|
||||
const t = this.run_hardware_timers(now);
|
||||
this.handle_irqs();
|
||||
|
||||
if(this.in_hlt[0])
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
@ -1196,32 +1205,6 @@ CPU.prototype.load_bios = function()
|
|||
}.bind(this));
|
||||
};
|
||||
|
||||
CPU.prototype.do_run = function()
|
||||
{
|
||||
/** @type {number} */
|
||||
var start = v86.microtick();
|
||||
|
||||
/** @type {number} */
|
||||
var now = start;
|
||||
|
||||
// outer loop:
|
||||
// runs cycles + timers
|
||||
for(; now - start < TIME_PER_FRAME;)
|
||||
{
|
||||
this.run_hardware_timers(now);
|
||||
this.handle_irqs();
|
||||
|
||||
this.do_many_cycles();
|
||||
|
||||
if(this.in_hlt[0])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
now = v86.microtick();
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.do_many_cycles = function()
|
||||
{
|
||||
if(DEBUG)
|
||||
|
@ -1409,12 +1392,9 @@ CPU.prototype.hlt_loop = function()
|
|||
{
|
||||
if(this.get_eflags_no_arith() & FLAG_INTERRUPT)
|
||||
{
|
||||
//dbg_log("In HLT loop", LOG_CPU);
|
||||
|
||||
this.run_hardware_timers(v86.microtick());
|
||||
const t = this.run_hardware_timers(v86.microtick());
|
||||
this.handle_irqs();
|
||||
|
||||
return 0;
|
||||
return t;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1428,19 +1408,24 @@ CPU.prototype.run_hardware_timers = function(now)
|
|||
{
|
||||
var pit_time = this.devices.pit.timer(now, this.devices.hpet.legacy_mode);
|
||||
var rtc_time = this.devices.rtc.timer(now, this.devices.hpet.legacy_mode);
|
||||
this.devices.hpet.timer(now);
|
||||
var hpet_time = this.devices.hpet.timer(now);
|
||||
}
|
||||
else
|
||||
{
|
||||
var pit_time = this.devices.pit.timer(now, false);
|
||||
var rtc_time = this.devices.rtc.timer(now, false);
|
||||
var hpet_time = 100;
|
||||
}
|
||||
|
||||
let acpi_time = 100;
|
||||
let apic_time = 100;
|
||||
if(this.acpi_enabled[0])
|
||||
{
|
||||
this.devices.acpi.timer(now);
|
||||
this.devices.apic.timer(now);
|
||||
acpi_time = this.devices.acpi.timer(now);
|
||||
apic_time = this.devices.apic.timer(now);
|
||||
}
|
||||
|
||||
return Math.min(pit_time, rtc_time, hpet_time, acpi_time, apic_time);
|
||||
};
|
||||
|
||||
CPU.prototype.hlt_op = function()
|
||||
|
@ -1467,6 +1452,7 @@ CPU.prototype.handle_irqs = function()
|
|||
if(this.get_eflags_no_arith() & FLAG_INTERRUPT)
|
||||
{
|
||||
this.pic_acknowledge();
|
||||
this.next_tick_immediately();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ function HPET(cpu)
|
|||
{
|
||||
if(!hpet_enabled)
|
||||
{
|
||||
return;
|
||||
return 100;
|
||||
}
|
||||
|
||||
var
|
||||
|
@ -107,6 +107,8 @@ function HPET(cpu)
|
|||
}
|
||||
|
||||
last_check = counter_value;
|
||||
|
||||
return 100; // TODO
|
||||
};
|
||||
|
||||
function get_counter()
|
||||
|
|
209
src/main.js
209
src/main.js
|
@ -12,8 +12,11 @@ function v86(bus, wasm)
|
|||
/** @type {boolean} */
|
||||
this.stopped = false;
|
||||
|
||||
this.tick_counter = 0;
|
||||
this.worker = null;
|
||||
|
||||
/** @type {CPU} */
|
||||
this.cpu = new CPU(bus, wasm);
|
||||
this.cpu = new CPU(bus, wasm, () => { this.idle && this.next_tick(0); });
|
||||
|
||||
this.bus = bus;
|
||||
bus.register("cpu-init", this.init, this);
|
||||
|
@ -21,7 +24,7 @@ function v86(bus, wasm)
|
|||
bus.register("cpu-stop", this.stop, this);
|
||||
bus.register("cpu-restart", this.restart, this);
|
||||
|
||||
this.register_tick();
|
||||
this.register_yield();
|
||||
}
|
||||
|
||||
v86.prototype.run = function()
|
||||
|
@ -30,30 +33,40 @@ v86.prototype.run = function()
|
|||
|
||||
if(!this.running)
|
||||
{
|
||||
this.running = true;
|
||||
this.bus.send("emulator-started");
|
||||
this.fast_next_tick();
|
||||
}
|
||||
|
||||
this.next_tick(0);
|
||||
};
|
||||
|
||||
v86.prototype.do_tick = function()
|
||||
{
|
||||
if(this.stopped)
|
||||
if(this.stopped || !this.running)
|
||||
{
|
||||
this.stopped = this.running = false;
|
||||
this.bus.send("emulator-stopped");
|
||||
return;
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
var dt = this.cpu.main_run();
|
||||
this.idle = false;
|
||||
const t = this.cpu.main_run();
|
||||
|
||||
if(dt <= 0)
|
||||
this.next_tick(t);
|
||||
};
|
||||
|
||||
v86.prototype.next_tick = function(t)
|
||||
{
|
||||
const tick = ++this.tick_counter;
|
||||
this.idle = true;
|
||||
this.yield(t, tick);
|
||||
};
|
||||
|
||||
v86.prototype.yield_callback = function(tick)
|
||||
{
|
||||
if(tick === this.tick_counter)
|
||||
{
|
||||
this.fast_next_tick();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.next_tick(dt);
|
||||
this.do_tick();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -67,7 +80,7 @@ v86.prototype.stop = function()
|
|||
|
||||
v86.prototype.destroy = function()
|
||||
{
|
||||
this.unregister_tick();
|
||||
this.unregister_yield();
|
||||
};
|
||||
|
||||
v86.prototype.restart = function()
|
||||
|
@ -82,132 +95,102 @@ v86.prototype.init = function(settings)
|
|||
this.bus.send("emulator-ready");
|
||||
};
|
||||
|
||||
|
||||
if(typeof importScripts === "function" && typeof queueMicrotask === "function")
|
||||
if(typeof process !== "undefined")
|
||||
{
|
||||
let tick_counter = 0;
|
||||
|
||||
/** @this {v86} */
|
||||
var fast_next_tick = function()
|
||||
v86.prototype.yield = function(t, tick)
|
||||
{
|
||||
if(tick_counter === 256)
|
||||
if(t < 1)
|
||||
{
|
||||
tick_counter = 0;
|
||||
setTimeout(() => { this.do_tick(); }, 0);
|
||||
global.setImmediate(tick => this.yield_callback(tick), tick);
|
||||
}
|
||||
else
|
||||
{
|
||||
tick_counter++;
|
||||
queueMicrotask(() => { this.do_tick(); });
|
||||
setTimeout(tick => this.yield_callback(tick), t, tick);
|
||||
}
|
||||
};
|
||||
|
||||
/** @this {v86} */
|
||||
var register_tick = function() {};
|
||||
|
||||
/** @this {v86} */
|
||||
var unregister_tick = function() {};
|
||||
v86.prototype.register_yield = function() {};
|
||||
v86.prototype.unregister_yield = function() {};
|
||||
}
|
||||
else if(typeof setImmediate !== "undefined")
|
||||
else if(typeof Worker !== "undefined")
|
||||
{
|
||||
/** @this {v86} */
|
||||
fast_next_tick = function()
|
||||
// XXX: This has a slightly lower throughput compared to window.postMessage
|
||||
|
||||
function the_worker()
|
||||
{
|
||||
setImmediate(() => { this.do_tick(); });
|
||||
};
|
||||
|
||||
/** @this {v86} */
|
||||
register_tick = function() {};
|
||||
|
||||
/** @this {v86} */
|
||||
unregister_tick = function() {};
|
||||
}
|
||||
else if(typeof window !== "undefined" && typeof postMessage !== "undefined")
|
||||
{
|
||||
// setImmediate shim for the browser.
|
||||
// TODO: Make this deactivatable, for other applications
|
||||
// using postMessage
|
||||
|
||||
/** @const */
|
||||
let MAGIC_POST_MESSAGE = 0xAA55;
|
||||
|
||||
/** @this {v86} */
|
||||
fast_next_tick = function()
|
||||
{
|
||||
window.postMessage(MAGIC_POST_MESSAGE, "*");
|
||||
};
|
||||
|
||||
let tick;
|
||||
|
||||
/** @this {v86} */
|
||||
register_tick = function()
|
||||
{
|
||||
tick = e =>
|
||||
globalThis.onmessage = function(e)
|
||||
{
|
||||
if(e.source === window && e.data === MAGIC_POST_MESSAGE)
|
||||
{
|
||||
this.do_tick();
|
||||
}
|
||||
const t = e.data.t;
|
||||
if(t < 1) postMessage(e.data.tick);
|
||||
else setTimeout(() => postMessage(e.data.tick), t);
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener("message", tick, false);
|
||||
v86.prototype.register_yield = function()
|
||||
{
|
||||
const url = URL.createObjectURL(new Blob(["(" + the_worker.toString() + ")()"], { type: "text/javascript" }));
|
||||
this.worker = new Worker(url);
|
||||
this.worker.onmessage = e => this.yield_callback(e.data);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
/** @this {v86} */
|
||||
unregister_tick = function()
|
||||
v86.prototype.yield = function(t, tick)
|
||||
{
|
||||
window.removeEventListener("message", tick);
|
||||
tick = null;
|
||||
this.worker.postMessage({ t, tick });
|
||||
};
|
||||
|
||||
v86.prototype.unregister_yield = function()
|
||||
{
|
||||
this.worker.terminate();
|
||||
this.worker = null;
|
||||
};
|
||||
}
|
||||
//else if(typeof window !== "undefined" && typeof postMessage !== "undefined")
|
||||
//{
|
||||
// // setImmediate shim for the browser.
|
||||
// // TODO: Make this deactivatable, for other applications
|
||||
// // using postMessage
|
||||
//
|
||||
// /** @const */
|
||||
// let MAGIC_POST_MESSAGE = 0xAA55;
|
||||
//
|
||||
// v86.prototype.yield = function(t)
|
||||
// {
|
||||
// // XXX: Use t
|
||||
// window.postMessage(MAGIC_POST_MESSAGE, "*");
|
||||
// };
|
||||
//
|
||||
// let tick;
|
||||
//
|
||||
// v86.prototype.register_yield = function()
|
||||
// {
|
||||
// tick = e =>
|
||||
// {
|
||||
// if(e.source === window && e.data === MAGIC_POST_MESSAGE)
|
||||
// {
|
||||
// this.do_tick();
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// window.addEventListener("message", tick, false);
|
||||
// };
|
||||
//
|
||||
// v86.prototype.unregister_yield = function()
|
||||
// {
|
||||
// window.removeEventListener("message", tick);
|
||||
// tick = null;
|
||||
// };
|
||||
//}
|
||||
else
|
||||
{
|
||||
/** @this {v86} */
|
||||
fast_next_tick = function()
|
||||
{
|
||||
setTimeout(() => { this.do_tick(); }, 0);
|
||||
};
|
||||
|
||||
/** @this {v86} */
|
||||
register_tick = function() {};
|
||||
|
||||
/** @this {v86} */
|
||||
unregister_tick = function() {};
|
||||
}
|
||||
|
||||
v86.prototype.fast_next_tick = fast_next_tick;
|
||||
v86.prototype.register_tick = register_tick;
|
||||
v86.prototype.unregister_tick = unregister_tick;
|
||||
|
||||
if(typeof document !== "undefined" && typeof document.hidden === "boolean")
|
||||
{
|
||||
/** @this {v86} */
|
||||
var next_tick = function(t)
|
||||
{
|
||||
if(t < 4 || document.hidden)
|
||||
{
|
||||
// Avoid sleeping for 1 second (happens if page is not
|
||||
// visible), it can break boot processes. Also don't try to
|
||||
// sleep for less than 4ms, since the value is clamped up
|
||||
this.fast_next_tick();
|
||||
}
|
||||
else
|
||||
{
|
||||
setTimeout(() => { this.do_tick(); }, t);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// In environments that aren't browsers, we might as well use setTimeout
|
||||
/** @this {v86} */
|
||||
next_tick = function(t)
|
||||
v86.prototype.yield = function(t)
|
||||
{
|
||||
setTimeout(() => { this.do_tick(); }, t);
|
||||
};
|
||||
}
|
||||
|
||||
v86.prototype.next_tick = next_tick;
|
||||
v86.prototype.register_yield = function() {};
|
||||
v86.prototype.unregister_yield = function() {};
|
||||
}
|
||||
|
||||
v86.prototype.save_state = function()
|
||||
{
|
||||
|
|
13
src/pit.js
13
src/pit.js
|
@ -106,8 +106,6 @@ PIT.prototype.timer = function(now, no_irq)
|
|||
{
|
||||
if(this.counter_enabled[0] && this.did_rollover(0, now))
|
||||
{
|
||||
time_to_next_interrupt = 0;
|
||||
|
||||
this.counter_start_value[0] = this.get_counter_value(0, now);
|
||||
this.counter_start_time[0] = now;
|
||||
|
||||
|
@ -130,8 +128,15 @@ PIT.prototype.timer = function(now, no_irq)
|
|||
{
|
||||
this.cpu.device_lower_irq(0);
|
||||
}
|
||||
|
||||
if(this.counter_enabled[0])
|
||||
{
|
||||
const diff = now - this.counter_start_time[0];
|
||||
const diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
|
||||
const ticks_missing = this.counter_start_value[0] - diff_in_ticks; // XXX: to simplify
|
||||
time_to_next_interrupt = ticks_missing / OSCILLATOR_FREQ;
|
||||
}
|
||||
}
|
||||
time_to_next_interrupt = 0;
|
||||
|
||||
return time_to_next_interrupt;
|
||||
};
|
||||
|
@ -172,7 +177,7 @@ PIT.prototype.did_rollover = function(i, now)
|
|||
if(diff < 0)
|
||||
{
|
||||
// should only happen after restore_state
|
||||
dbg_log("Warning: PIT timer difference is negative, resetting");
|
||||
dbg_log("Warning: PIT timer difference is negative, resetting (timer " + i + ")");
|
||||
return true;
|
||||
}
|
||||
var diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
|
||||
|
|
15
src/rtc.js
15
src/rtc.js
|
@ -134,8 +134,6 @@ RTC.prototype.timer = function(time, legacy_mode)
|
|||
|
||||
this.next_interrupt += this.periodic_interrupt_time *
|
||||
Math.ceil((time - this.next_interrupt) / this.periodic_interrupt_time);
|
||||
|
||||
return Math.max(0, time - this.next_interrupt);
|
||||
}
|
||||
else if(this.next_interrupt_alarm && this.next_interrupt_alarm < time)
|
||||
{
|
||||
|
@ -145,7 +143,18 @@ RTC.prototype.timer = function(time, legacy_mode)
|
|||
this.next_interrupt_alarm = 0;
|
||||
}
|
||||
|
||||
return 100;
|
||||
let t = 100;
|
||||
|
||||
if(this.periodic_interrupt && this.next_interrupt)
|
||||
{
|
||||
t = Math.min(t, Math.max(0, this.next_interrupt - time));
|
||||
}
|
||||
if(this.next_interrupt_alarm)
|
||||
{
|
||||
t = Math.min(t, Math.max(0, this.next_interrupt_alarm - time));
|
||||
}
|
||||
|
||||
return t;
|
||||
};
|
||||
|
||||
RTC.prototype.bcd_pack = function(n)
|
||||
|
|
Loading…
Reference in a new issue