idle on hlt instruction

This commit is contained in:
Fabian 2022-01-30 13:28:27 -06:00
parent e5adfc93c2
commit 74dca95e23
7 changed files with 159 additions and 176 deletions

View file

@ -144,6 +144,7 @@ ACPI.prototype.timer = function(now)
}
this.last_timer = timer;
return 100; // TODO
};
ACPI.prototype.get_timer = function(now)

View file

@ -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)

View file

@ -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();
}
};

View file

@ -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()

View file

@ -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()
{

View file

@ -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);

View file

@ -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)