Skip to content

Commit 83cbba1

Browse files
committed
Add a "speed governor" to control the execution speed for Basilisk II-based machines
Uses the instruction count reported by mihaip/macemu@683bc64 to implement support for Mini vMac's speed setting in Basilisk II too. We set a target instructions per second (IPS) count, and then add sleep time to try to hit it. Fixes #372
1 parent 6169681 commit 83cbba1

13 files changed

Lines changed: 185 additions & 71 deletions

src/Mac.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ export default function Mac({
490490
cdroms,
491491
machine,
492492
ethernetProvider,
493+
screenSizeProp,
493494
initialScreenWidth,
494495
initialScreenHeight,
495496
customDate,

src/MacSettings.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
margin-right: 4px;
1010
}
1111

12+
.MacSettings-Row label {
13+
display: inline;
14+
}
15+
1216
.MacSettings-Row .Dialog-Description {
1317
margin-left: 104px;
1418
}

src/MacSettings.tsx

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -69,48 +69,54 @@ export function MacSettings({
6969

7070
return (
7171
<Dialog title="Settings" onDone={onDone}>
72-
<label>
73-
<Checkbox
74-
checked={emulatorSettings.swapControlAndCommand}
75-
onChange={() =>
76-
setEmulatorSettings({
77-
...emulatorSettings,
78-
swapControlAndCommand:
79-
!emulatorSettings.swapControlAndCommand,
80-
})
81-
}
82-
/>
83-
Swap Control and Command Keys
72+
<div className="MacSettings-Row">
73+
<div className="MacSettings-Row-Label">Input:</div>
74+
<label>
75+
<Checkbox
76+
checked={emulatorSettings.swapControlAndCommand}
77+
onChange={() =>
78+
setEmulatorSettings({
79+
...emulatorSettings,
80+
swapControlAndCommand:
81+
!emulatorSettings.swapControlAndCommand,
82+
})
83+
}
84+
/>
85+
Swap Control and Command Keys
86+
</label>
8487
<div className="Dialog-Description">
8588
Makes it easier to use shortcuts like Command-W, Command-Q
8689
or Command-Shift-3 (which are otherwise handled by the host
8790
browser or OS).
8891
</div>
89-
</label>
92+
</div>
9093
{emulatorSupportsMouseDeltas(emulatorType) &&
9194
!emulatorSettings.trackpadMode && (
92-
<label>
93-
<Checkbox
94-
checked={emulatorSettings.useMouseDeltas}
95-
onChange={() =>
96-
setEmulatorSettings({
97-
...emulatorSettings,
98-
useMouseDeltas:
99-
!emulatorSettings.useMouseDeltas,
100-
})
101-
}
102-
/>
103-
Use relative mouse movements
95+
<div className="MacSettings-Row">
96+
<div className="MacSettings-Row-Label" />
97+
<label>
98+
<Checkbox
99+
checked={emulatorSettings.useMouseDeltas}
100+
onChange={() =>
101+
setEmulatorSettings({
102+
...emulatorSettings,
103+
useMouseDeltas:
104+
!emulatorSettings.useMouseDeltas,
105+
})
106+
}
107+
/>
108+
Use relative mouse movements
109+
</label>
104110
<div className="Dialog-Description">
105111
Send relative mouse movements to the emulator
106112
instead of absolute positions. This can help with
107113
compatibility of games such as Apeiron.
108114
</div>
109-
</label>
115+
</div>
110116
)}
111117
{emulatorSupportsSpeedSetting(emulatorType) && (
112-
<label>
113-
Speed:
118+
<div className="MacSettings-Row">
119+
<div className="MacSettings-Row-Label">Speed:</div>
114120
<Select
115121
value={emulatorSettings.speed}
116122
onChange={event =>
@@ -131,10 +137,9 @@ export function MacSettings({
131137
)}
132138
</Select>
133139
<div className="Dialog-Description">
134-
Very old software may be timing dependent and thus only
135-
work at 1x speeds.
140+
Very old software may be timing dependent.
136141
</div>
137-
</label>
142+
</div>
138143
)}
139144
<div className="MacSettings-Row">
140145
<div className="MacSettings-Row-Label">Scaling:</div>

src/emulator/BasiliskII.js

Lines changed: 41 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/emulator/BasiliskII.wasm

172 Bytes
Binary file not shown.

src/emulator/emulator-common-emulators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function emulatorUsesPlaceholderDisks(type: EmulatorType): boolean {
4141
}
4242

4343
export function emulatorSupportsSpeedSetting(type: EmulatorType): boolean {
44-
return type === "Mini vMac";
44+
return type === "Mini vMac" || type === "BasiliskII";
4545
}
4646

4747
// True if the emulator can optionally use mouse deltas instead of absolute

src/emulator/emulator-common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export type EmulatorWorkerConfig = EmulatorDef & {
165165
ethernet: EmulatorWorkerEthernetConfig;
166166
clipboard: EmulatorWorkerClipboardConfig;
167167
dateOffset: number;
168+
speedGovernorTargetIPS?: number;
168169
};
169170

170171
export type EmulatorWorkerVideoConfig =

src/emulator/emulator-ui.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,8 @@ export class Emulator {
370370

371371
let args: string[] = [];
372372
let configDebugStr;
373-
const {emulatorType, emulatorSubtype} = this.#config.machine;
373+
const {emulatorType, emulatorSubtype, speedGovernorTargetIPS} =
374+
this.#config.machine;
374375
switch (emulatorType) {
375376
case "DingusPPC":
376377
args = configToDingusPPCArgs(this.#config, {
@@ -459,6 +460,7 @@ export class Emulator {
459460
ethernet: this.#ethernet.workerConfig(),
460461
clipboard: this.#clipboard.workerConfig(),
461462
dateOffset,
463+
speedGovernorTargetIPS,
462464
};
463465

464466
const serviceWorkerAvailable = await this.#serviceWorkerReady;

src/emulator/emulator-worker-input.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,23 @@ import {type EmulatorFallbackEndpoint} from "./emulator-worker";
1010

1111
export interface EmulatorWorkerInput {
1212
idleWait(timeout: number): boolean;
13+
sleep(timeout: number): void;
1314
acquireInputLock(): number;
1415
releaseInputLock(): void;
1516
getInputValue(addr: number): number;
1617
}
1718

1819
export class SharedMemoryEmulatorWorkerInput implements EmulatorWorkerInput {
1920
#inputBufferView: Int32Array;
21+
#sleepInputBufferView: Int32Array;
2022

2123
constructor(config: EmulatorWorkerSharedMemoryInputConfig) {
2224
this.#inputBufferView = new Int32Array(
2325
config.inputBuffer,
2426
0,
2527
config.inputBufferSize
2628
);
29+
this.#sleepInputBufferView = new Int32Array(new SharedArrayBuffer(4));
2730
}
2831

2932
idleWait(timeout: number): boolean {
@@ -37,6 +40,12 @@ export class SharedMemoryEmulatorWorkerInput implements EmulatorWorkerInput {
3740
return hadInput;
3841
}
3942

43+
sleep(timeout: number): void {
44+
// sleepInputBufferView will never change, just use it to wait
45+
// efficiently.
46+
Atomics.wait(this.#sleepInputBufferView, 0, 0, timeout);
47+
}
48+
4049
acquireInputLock(): number {
4150
const hasLock = tryToAcquireCyclicalLock(
4251
this.#inputBufferView,
@@ -117,6 +126,14 @@ export class FallbackEmulatorWorkerInput implements EmulatorWorkerInput {
117126
return false;
118127
}
119128

129+
sleep(timeout: number): void {
130+
const endTime = performance.now() + timeout;
131+
while (performance.now() < endTime) {
132+
// Not worth hitting the fallback endpoint, the timeouts are too
133+
// short.
134+
}
135+
}
136+
120137
acquireInputLock(): number {
121138
this.#inputQueue = this.#inputQueue.concat(
122139
this.#fallbackEndpoint.consumeInputEvents()

0 commit comments

Comments
 (0)