@joezilla/8sim 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +542 -0
- package/dist/8sim.browser.js +4728 -0
- package/dist/bundles/CardBundle.d.ts +83 -0
- package/dist/bundles/CardBundle.d.ts.map +1 -0
- package/dist/bundles/CardBundle.js +41 -0
- package/dist/bundles/CardBundle.js.map +1 -0
- package/dist/bundles/kernels.d.ts +48 -0
- package/dist/bundles/kernels.d.ts.map +1 -0
- package/dist/bundles/kernels.js +132 -0
- package/dist/bundles/kernels.js.map +1 -0
- package/dist/bundles/seed/index.d.ts +24 -0
- package/dist/bundles/seed/index.d.ts.map +1 -0
- package/dist/bundles/seed/index.js +266 -0
- package/dist/bundles/seed/index.js.map +1 -0
- package/dist/bus/Bus.d.ts +21 -0
- package/dist/bus/Bus.d.ts.map +1 -0
- package/dist/bus/Bus.js +62 -0
- package/dist/bus/Bus.js.map +1 -0
- package/dist/bus/BusRegion.d.ts +8 -0
- package/dist/bus/BusRegion.d.ts.map +1 -0
- package/dist/bus/BusRegion.js +8 -0
- package/dist/bus/BusRegion.js.map +1 -0
- package/dist/bus/SnoopBus.d.ts +15 -0
- package/dist/bus/SnoopBus.d.ts.map +1 -0
- package/dist/bus/SnoopBus.js +41 -0
- package/dist/bus/SnoopBus.js.map +1 -0
- package/dist/cards/BankRamCard.d.ts +35 -0
- package/dist/cards/BankRamCard.d.ts.map +1 -0
- package/dist/cards/BankRamCard.js +56 -0
- package/dist/cards/BankRamCard.js.map +1 -0
- package/dist/cards/DazzlerCard.d.ts +42 -0
- package/dist/cards/DazzlerCard.d.ts.map +1 -0
- package/dist/cards/DazzlerCard.js +83 -0
- package/dist/cards/DazzlerCard.js.map +1 -0
- package/dist/cards/DisplaySurface.d.ts +32 -0
- package/dist/cards/DisplaySurface.d.ts.map +1 -0
- package/dist/cards/DisplaySurface.js +11 -0
- package/dist/cards/DisplaySurface.js.map +1 -0
- package/dist/cards/FdcPlusClient.d.ts +35 -0
- package/dist/cards/FdcPlusClient.d.ts.map +1 -0
- package/dist/cards/FdcPlusClient.js +130 -0
- package/dist/cards/FdcPlusClient.js.map +1 -0
- package/dist/cards/ImsaiMioCard.d.ts +36 -0
- package/dist/cards/ImsaiMioCard.d.ts.map +1 -0
- package/dist/cards/ImsaiMioCard.js +48 -0
- package/dist/cards/ImsaiMioCard.js.map +1 -0
- package/dist/cards/ImsaiSioCard.d.ts +19 -0
- package/dist/cards/ImsaiSioCard.d.ts.map +1 -0
- package/dist/cards/ImsaiSioCard.js +54 -0
- package/dist/cards/ImsaiSioCard.js.map +1 -0
- package/dist/cards/KeyboardCard.d.ts +37 -0
- package/dist/cards/KeyboardCard.d.ts.map +1 -0
- package/dist/cards/KeyboardCard.js +79 -0
- package/dist/cards/KeyboardCard.js.map +1 -0
- package/dist/cards/Mc6850Acia.d.ts +68 -0
- package/dist/cards/Mc6850Acia.d.ts.map +1 -0
- package/dist/cards/Mc6850Acia.js +132 -0
- package/dist/cards/Mc6850Acia.js.map +1 -0
- package/dist/cards/Mits2SioCard.d.ts +27 -0
- package/dist/cards/Mits2SioCard.d.ts.map +1 -0
- package/dist/cards/Mits2SioCard.js +36 -0
- package/dist/cards/Mits2SioCard.js.map +1 -0
- package/dist/cards/MitsDcddCard.d.ts +52 -0
- package/dist/cards/MitsDcddCard.d.ts.map +1 -0
- package/dist/cards/MitsDcddCard.js +294 -0
- package/dist/cards/MitsDcddCard.js.map +1 -0
- package/dist/cards/ParallelCard.d.ts +35 -0
- package/dist/cards/ParallelCard.d.ts.map +1 -0
- package/dist/cards/ParallelCard.js +32 -0
- package/dist/cards/ParallelCard.js.map +1 -0
- package/dist/cards/Port8212.d.ts +31 -0
- package/dist/cards/Port8212.d.ts.map +1 -0
- package/dist/cards/Port8212.js +47 -0
- package/dist/cards/Port8212.js.map +1 -0
- package/dist/cards/RtcCard.d.ts +30 -0
- package/dist/cards/RtcCard.d.ts.map +1 -0
- package/dist/cards/RtcCard.js +61 -0
- package/dist/cards/RtcCard.js.map +1 -0
- package/dist/cards/SerialCard.d.ts +31 -0
- package/dist/cards/SerialCard.d.ts.map +1 -0
- package/dist/cards/SerialCard.js +28 -0
- package/dist/cards/SerialCard.js.map +1 -0
- package/dist/cards/Tr1602Uart.d.ts +55 -0
- package/dist/cards/Tr1602Uart.d.ts.map +1 -0
- package/dist/cards/Tr1602Uart.js +102 -0
- package/dist/cards/Tr1602Uart.js.map +1 -0
- package/dist/cards/Usart8251.d.ts +28 -0
- package/dist/cards/Usart8251.d.ts.map +1 -0
- package/dist/cards/Usart8251.js +88 -0
- package/dist/cards/Usart8251.js.map +1 -0
- package/dist/cards/VdmCard.d.ts +27 -0
- package/dist/cards/VdmCard.d.ts.map +1 -0
- package/dist/cards/VdmCard.js +40 -0
- package/dist/cards/VdmCard.js.map +1 -0
- package/dist/clock/ImmediateClock.d.ts +8 -0
- package/dist/clock/ImmediateClock.d.ts.map +1 -0
- package/dist/clock/ImmediateClock.js +13 -0
- package/dist/clock/ImmediateClock.js.map +1 -0
- package/dist/clock/SystemClock.d.ts +45 -0
- package/dist/clock/SystemClock.d.ts.map +1 -0
- package/dist/clock/SystemClock.js +71 -0
- package/dist/clock/SystemClock.js.map +1 -0
- package/dist/cpu/Cpu8080.d.ts +34 -0
- package/dist/cpu/Cpu8080.d.ts.map +1 -0
- package/dist/cpu/Cpu8080.js +126 -0
- package/dist/cpu/Cpu8080.js.map +1 -0
- package/dist/cpu/Decoder.d.ts +12 -0
- package/dist/cpu/Decoder.d.ts.map +1 -0
- package/dist/cpu/Decoder.js +23 -0
- package/dist/cpu/Decoder.js.map +1 -0
- package/dist/cpu/Flags.d.ts +18 -0
- package/dist/cpu/Flags.d.ts.map +1 -0
- package/dist/cpu/Flags.js +33 -0
- package/dist/cpu/Flags.js.map +1 -0
- package/dist/cpu/Registers.d.ts +22 -0
- package/dist/cpu/Registers.d.ts.map +1 -0
- package/dist/cpu/Registers.js +26 -0
- package/dist/cpu/Registers.js.map +1 -0
- package/dist/cpu/instructions/alu.d.ts +3 -0
- package/dist/cpu/instructions/alu.d.ts.map +1 -0
- package/dist/cpu/instructions/alu.js +221 -0
- package/dist/cpu/instructions/alu.js.map +1 -0
- package/dist/cpu/instructions/branch.d.ts +3 -0
- package/dist/cpu/instructions/branch.d.ts.map +1 -0
- package/dist/cpu/instructions/branch.js +117 -0
- package/dist/cpu/instructions/branch.js.map +1 -0
- package/dist/cpu/instructions/control.d.ts +3 -0
- package/dist/cpu/instructions/control.d.ts.map +1 -0
- package/dist/cpu/instructions/control.js +12 -0
- package/dist/cpu/instructions/control.js.map +1 -0
- package/dist/cpu/instructions/data.d.ts +3 -0
- package/dist/cpu/instructions/data.d.ts.map +1 -0
- package/dist/cpu/instructions/data.js +137 -0
- package/dist/cpu/instructions/data.js.map +1 -0
- package/dist/cpu/instructions/io.d.ts +3 -0
- package/dist/cpu/instructions/io.d.ts.map +1 -0
- package/dist/cpu/instructions/io.js +18 -0
- package/dist/cpu/instructions/io.js.map +1 -0
- package/dist/cpu/instructions/logical.d.ts +3 -0
- package/dist/cpu/instructions/logical.d.ts.map +1 -0
- package/dist/cpu/instructions/logical.js +129 -0
- package/dist/cpu/instructions/logical.js.map +1 -0
- package/dist/cpu/instructions/rotate.d.ts +3 -0
- package/dist/cpu/instructions/rotate.d.ts.map +1 -0
- package/dist/cpu/instructions/rotate.js +34 -0
- package/dist/cpu/instructions/rotate.js.map +1 -0
- package/dist/cpu/instructions/stack.d.ts +3 -0
- package/dist/cpu/instructions/stack.d.ts.map +1 -0
- package/dist/cpu/instructions/stack.js +84 -0
- package/dist/cpu/instructions/stack.js.map +1 -0
- package/dist/cpu/status8080.d.ts +33 -0
- package/dist/cpu/status8080.d.ts.map +1 -0
- package/dist/cpu/status8080.js +73 -0
- package/dist/cpu/status8080.js.map +1 -0
- package/dist/cpu/z80/CpuZ80.d.ts +53 -0
- package/dist/cpu/z80/CpuZ80.d.ts.map +1 -0
- package/dist/cpu/z80/CpuZ80.js +168 -0
- package/dist/cpu/z80/CpuZ80.js.map +1 -0
- package/dist/cpu/z80/DecoderZ80.d.ts +26 -0
- package/dist/cpu/z80/DecoderZ80.d.ts.map +1 -0
- package/dist/cpu/z80/DecoderZ80.js +107 -0
- package/dist/cpu/z80/DecoderZ80.js.map +1 -0
- package/dist/cpu/z80/FlagsZ80.d.ts +27 -0
- package/dist/cpu/z80/FlagsZ80.d.ts.map +1 -0
- package/dist/cpu/z80/FlagsZ80.js +47 -0
- package/dist/cpu/z80/FlagsZ80.js.map +1 -0
- package/dist/cpu/z80/RegistersZ80.d.ts +60 -0
- package/dist/cpu/z80/RegistersZ80.d.ts.map +1 -0
- package/dist/cpu/z80/RegistersZ80.js +90 -0
- package/dist/cpu/z80/RegistersZ80.js.map +1 -0
- package/dist/cpu/z80/flagHelpers.d.ts +25 -0
- package/dist/cpu/z80/flagHelpers.d.ts.map +1 -0
- package/dist/cpu/z80/flagHelpers.js +136 -0
- package/dist/cpu/z80/flagHelpers.js.map +1 -0
- package/dist/cpu/z80/instructions/alu16.d.ts +4 -0
- package/dist/cpu/z80/instructions/alu16.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/alu16.js +27 -0
- package/dist/cpu/z80/instructions/alu16.js.map +1 -0
- package/dist/cpu/z80/instructions/alu8.d.ts +4 -0
- package/dist/cpu/z80/instructions/alu8.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/alu8.js +100 -0
- package/dist/cpu/z80/instructions/alu8.js.map +1 -0
- package/dist/cpu/z80/instructions/bits.d.ts +10 -0
- package/dist/cpu/z80/instructions/bits.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/bits.js +164 -0
- package/dist/cpu/z80/instructions/bits.js.map +1 -0
- package/dist/cpu/z80/instructions/block.d.ts +10 -0
- package/dist/cpu/z80/instructions/block.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/block.js +141 -0
- package/dist/cpu/z80/instructions/block.js.map +1 -0
- package/dist/cpu/z80/instructions/control.d.ts +4 -0
- package/dist/cpu/z80/instructions/control.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/control.js +62 -0
- package/dist/cpu/z80/instructions/control.js.map +1 -0
- package/dist/cpu/z80/instructions/ed.d.ts +4 -0
- package/dist/cpu/z80/instructions/ed.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/ed.js +149 -0
- package/dist/cpu/z80/instructions/ed.js.map +1 -0
- package/dist/cpu/z80/instructions/exchange.d.ts +4 -0
- package/dist/cpu/z80/instructions/exchange.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/exchange.js +37 -0
- package/dist/cpu/z80/instructions/exchange.js.map +1 -0
- package/dist/cpu/z80/instructions/io.d.ts +8 -0
- package/dist/cpu/z80/instructions/io.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/io.js +22 -0
- package/dist/cpu/z80/instructions/io.js.map +1 -0
- package/dist/cpu/z80/instructions/jump.d.ts +4 -0
- package/dist/cpu/z80/instructions/jump.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/jump.js +113 -0
- package/dist/cpu/z80/instructions/jump.js.map +1 -0
- package/dist/cpu/z80/instructions/load.d.ts +7 -0
- package/dist/cpu/z80/instructions/load.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/load.js +103 -0
- package/dist/cpu/z80/instructions/load.js.map +1 -0
- package/dist/cpu/z80/instructions/rotate.d.ts +9 -0
- package/dist/cpu/z80/instructions/rotate.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/rotate.js +48 -0
- package/dist/cpu/z80/instructions/rotate.js.map +1 -0
- package/dist/cpu/z80/instructions/stack.d.ts +4 -0
- package/dist/cpu/z80/instructions/stack.d.ts.map +1 -0
- package/dist/cpu/z80/instructions/stack.js +19 -0
- package/dist/cpu/z80/instructions/stack.js.map +1 -0
- package/dist/cpu/z80/regcodes.d.ts +22 -0
- package/dist/cpu/z80/regcodes.d.ts.map +1 -0
- package/dist/cpu/z80/regcodes.js +93 -0
- package/dist/cpu/z80/regcodes.js.map +1 -0
- package/dist/cpu/z80/types.d.ts +59 -0
- package/dist/cpu/z80/types.d.ts.map +1 -0
- package/dist/cpu/z80/types.js +2 -0
- package/dist/cpu/z80/types.js.map +1 -0
- package/dist/cpu/z80/views.d.ts +8 -0
- package/dist/cpu/z80/views.d.ts.map +1 -0
- package/dist/cpu/z80/views.js +40 -0
- package/dist/cpu/z80/views.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/IBus.d.ts +8 -0
- package/dist/interfaces/IBus.d.ts.map +1 -0
- package/dist/interfaces/IBus.js +2 -0
- package/dist/interfaces/IBus.js.map +1 -0
- package/dist/interfaces/IBusObserver.d.ts +7 -0
- package/dist/interfaces/IBusObserver.d.ts.map +1 -0
- package/dist/interfaces/IBusObserver.js +2 -0
- package/dist/interfaces/IBusObserver.js.map +1 -0
- package/dist/interfaces/IClock.d.ts +6 -0
- package/dist/interfaces/IClock.d.ts.map +1 -0
- package/dist/interfaces/IClock.js +2 -0
- package/dist/interfaces/IClock.js.map +1 -0
- package/dist/interfaces/ICpu.d.ts +46 -0
- package/dist/interfaces/ICpu.d.ts.map +1 -0
- package/dist/interfaces/ICpu.js +2 -0
- package/dist/interfaces/ICpu.js.map +1 -0
- package/dist/interfaces/IIODevice.d.ts +7 -0
- package/dist/interfaces/IIODevice.d.ts.map +1 -0
- package/dist/interfaces/IIODevice.js +2 -0
- package/dist/interfaces/IIODevice.js.map +1 -0
- package/dist/interfaces/IInterruptController.d.ts +8 -0
- package/dist/interfaces/IInterruptController.d.ts.map +1 -0
- package/dist/interfaces/IInterruptController.js +2 -0
- package/dist/interfaces/IInterruptController.js.map +1 -0
- package/dist/interfaces/IMemory.d.ts +9 -0
- package/dist/interfaces/IMemory.d.ts.map +1 -0
- package/dist/interfaces/IMemory.js +2 -0
- package/dist/interfaces/IMemory.js.map +1 -0
- package/dist/interfaces/IModule.d.ts +5 -0
- package/dist/interfaces/IModule.d.ts.map +1 -0
- package/dist/interfaces/IModule.js +2 -0
- package/dist/interfaces/IModule.js.map +1 -0
- package/dist/interfaces/IS100Card.d.ts +6 -0
- package/dist/interfaces/IS100Card.d.ts.map +1 -0
- package/dist/interfaces/IS100Card.js +2 -0
- package/dist/interfaces/IS100Card.js.map +1 -0
- package/dist/interfaces/index.d.ts +10 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +2 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interrupt/InterruptController.d.ts +13 -0
- package/dist/interrupt/InterruptController.d.ts.map +1 -0
- package/dist/interrupt/InterruptController.js +36 -0
- package/dist/interrupt/InterruptController.js.map +1 -0
- package/dist/io/IoSpace.d.ts +9 -0
- package/dist/io/IoSpace.d.ts.map +1 -0
- package/dist/io/IoSpace.js +30 -0
- package/dist/io/IoSpace.js.map +1 -0
- package/dist/machine/MachineRunner.d.ts +54 -0
- package/dist/machine/MachineRunner.d.ts.map +1 -0
- package/dist/machine/MachineRunner.js +102 -0
- package/dist/machine/MachineRunner.js.map +1 -0
- package/dist/machine/MachineSpec.d.ts +80 -0
- package/dist/machine/MachineSpec.d.ts.map +1 -0
- package/dist/machine/MachineSpec.js +9 -0
- package/dist/machine/MachineSpec.js.map +1 -0
- package/dist/machine/buildMachine.d.ts +19 -0
- package/dist/machine/buildMachine.d.ts.map +1 -0
- package/dist/machine/buildMachine.js +122 -0
- package/dist/machine/buildMachine.js.map +1 -0
- package/dist/memory/MemoryMappedIOAdapter.d.ts +14 -0
- package/dist/memory/MemoryMappedIOAdapter.d.ts.map +1 -0
- package/dist/memory/MemoryMappedIOAdapter.js +23 -0
- package/dist/memory/MemoryMappedIOAdapter.js.map +1 -0
- package/dist/memory/Ram.d.ts +17 -0
- package/dist/memory/Ram.d.ts.map +1 -0
- package/dist/memory/Ram.js +36 -0
- package/dist/memory/Ram.js.map +1 -0
- package/dist/memory/Rom.d.ts +13 -0
- package/dist/memory/Rom.d.ts.map +1 -0
- package/dist/memory/Rom.js +25 -0
- package/dist/memory/Rom.js.map +1 -0
- package/dist/util/bits.d.ts +11 -0
- package/dist/util/bits.d.ts.map +1 -0
- package/dist/util/bits.js +35 -0
- package/dist/util/bits.js.map +1 -0
- package/dist/util/hostConsole.d.ts +2 -0
- package/dist/util/hostConsole.d.ts.map +1 -0
- package/dist/util/hostConsole.js +4 -0
- package/dist/util/hostConsole.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { SystemClock } from '../clock/SystemClock.js';
|
|
2
|
+
/** Steps executed per tick in 'max' mode. */
|
|
3
|
+
const MAX_SPEED_BATCH = 40_000;
|
|
4
|
+
/** Longest uninterrupted wall-time slice while catching up (keeps I/O live). */
|
|
5
|
+
const MAX_SLICE_MS = 20;
|
|
6
|
+
/** Behind by more than this → forfeit the lost time instead of sprinting. */
|
|
7
|
+
const RESYNC_BEHIND_MS = 250;
|
|
8
|
+
/**
|
|
9
|
+
* Drives a CPU's step() loop at a configurable real-time speed.
|
|
10
|
+
*
|
|
11
|
+
* Throttled mode runs the CPU in ~1 ms slices of simulated time and compares
|
|
12
|
+
* simulated progress against wall time (absolute accounting via SystemClock,
|
|
13
|
+
* so timer jitter self-corrects): ahead → sleep the difference; behind → keep
|
|
14
|
+
* stepping up to MAX_SLICE_MS per tick, then yield so host I/O stays
|
|
15
|
+
* responsive. Host stalls beyond RESYNC_BEHIND_MS are forfeited rather than
|
|
16
|
+
* replayed at full speed.
|
|
17
|
+
*/
|
|
18
|
+
export class MachineRunner {
|
|
19
|
+
cpu;
|
|
20
|
+
hz;
|
|
21
|
+
clock;
|
|
22
|
+
schedule;
|
|
23
|
+
onError;
|
|
24
|
+
now;
|
|
25
|
+
running = false;
|
|
26
|
+
startedAt = 0;
|
|
27
|
+
cyclesAtStart = 0n;
|
|
28
|
+
constructor(cpu, options = {}) {
|
|
29
|
+
this.cpu = cpu;
|
|
30
|
+
this.hz = options.hz ?? 2_000_000;
|
|
31
|
+
this.now = options.now ?? (() => performance.now());
|
|
32
|
+
this.clock = new SystemClock(typeof this.hz === 'number' ? this.hz : 2_000_000, this.now);
|
|
33
|
+
this.schedule = options.schedule ?? ((fn, ms) => setTimeout(fn, ms));
|
|
34
|
+
this.onError = options.onError ?? ((err) => { console.error('[MachineRunner]', err); });
|
|
35
|
+
}
|
|
36
|
+
get targetHz() {
|
|
37
|
+
return this.hz;
|
|
38
|
+
}
|
|
39
|
+
/** Measured average speed in Hz since start() — for status displays. */
|
|
40
|
+
get effectiveHz() {
|
|
41
|
+
const wallSec = (this.now() - this.startedAt) / 1000;
|
|
42
|
+
if (!this.running || wallSec <= 0)
|
|
43
|
+
return 0;
|
|
44
|
+
return Number(this.clock.getElapsedCycles() - this.cyclesAtStart) / wallSec;
|
|
45
|
+
}
|
|
46
|
+
get isRunning() {
|
|
47
|
+
return this.running;
|
|
48
|
+
}
|
|
49
|
+
/** Total simulated T-states executed across the runner's lifetime. */
|
|
50
|
+
get elapsedCycles() {
|
|
51
|
+
return this.clock.getElapsedCycles();
|
|
52
|
+
}
|
|
53
|
+
start() {
|
|
54
|
+
if (this.running)
|
|
55
|
+
return;
|
|
56
|
+
this.running = true;
|
|
57
|
+
this.startedAt = this.now();
|
|
58
|
+
this.cyclesAtStart = this.clock.getElapsedCycles();
|
|
59
|
+
this.clock.resync();
|
|
60
|
+
this.schedule(this.tick, 0);
|
|
61
|
+
}
|
|
62
|
+
stop() {
|
|
63
|
+
this.running = false;
|
|
64
|
+
}
|
|
65
|
+
/** Change speed while running; pacing restarts from this moment. */
|
|
66
|
+
setHz(hz) {
|
|
67
|
+
this.hz = hz;
|
|
68
|
+
// Re-baseline either way: entering throttled mode after a 'max' stint
|
|
69
|
+
// would otherwise see the simulation absurdly far ahead and sleep for it.
|
|
70
|
+
this.clock.setHz(typeof hz === 'number' ? hz : this.clock.targetHz);
|
|
71
|
+
}
|
|
72
|
+
tick = () => {
|
|
73
|
+
if (!this.running)
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
if (this.hz === 'max') {
|
|
77
|
+
for (let i = 0; i < MAX_SPEED_BATCH; i++)
|
|
78
|
+
this.clock.addCycles(this.cpu.step());
|
|
79
|
+
this.schedule(this.tick, 0);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Host stalled (GC, suspended tab): forfeit the lost time.
|
|
83
|
+
if (this.clock.getAheadMs() < -RESYNC_BEHIND_MS)
|
|
84
|
+
this.clock.resync();
|
|
85
|
+
const chunk = Math.max(1, Math.floor(this.hz / 1000)); // ~1 ms of simulated time
|
|
86
|
+
const sliceStart = this.now();
|
|
87
|
+
do {
|
|
88
|
+
let cycles = 0;
|
|
89
|
+
while (cycles < chunk)
|
|
90
|
+
cycles += this.cpu.step();
|
|
91
|
+
this.clock.addCycles(cycles);
|
|
92
|
+
} while (this.clock.getAheadMs() < 0 && this.now() - sliceStart < MAX_SLICE_MS);
|
|
93
|
+
const aheadMs = this.clock.getAheadMs();
|
|
94
|
+
this.schedule(this.tick, aheadMs > 1 ? Math.floor(aheadMs) : 0);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
this.running = false;
|
|
98
|
+
this.onError(err);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=MachineRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MachineRunner.js","sourceRoot":"","sources":["../../src/machine/MachineRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { IInterruptController } from '../interfaces/IInterruptController.js';
|
|
2
|
+
import type { IS100Card } from '../interfaces/IS100Card.js';
|
|
3
|
+
import type { ICpu } from '../interfaces/ICpu.js';
|
|
4
|
+
import type { Bus } from '../bus/Bus.js';
|
|
5
|
+
import type { MachineRunner } from './MachineRunner.js';
|
|
6
|
+
/**
|
|
7
|
+
* Normative declarative description of a machine (AD-1). `buildMachine(spec)`
|
|
8
|
+
* turns this into a running Bus+CPU+cards deterministically. Kept 8sim-native
|
|
9
|
+
* and minimal: the rich, portable "Machine Profile" (catalog, identity,
|
|
10
|
+
* versioning) lives in fdcplus-web; the MachineSpec is only what the engine
|
|
11
|
+
* needs to construct a machine.
|
|
12
|
+
*
|
|
13
|
+
* Per AD-2 the engine is code-loading-agnostic: a card entry carries an
|
|
14
|
+
* ALREADY-LOADED `factory` (a live function), never a bundle reference — 8sim
|
|
15
|
+
* never imports anything.
|
|
16
|
+
*/
|
|
17
|
+
/** Closed CPU enum — no silent default. */
|
|
18
|
+
export type CpuKind = 'i8080' | 'z80';
|
|
19
|
+
/** Target clock: a frequency in Hz, or unthrottled. */
|
|
20
|
+
export type Clock = {
|
|
21
|
+
hz: number;
|
|
22
|
+
} | 'max';
|
|
23
|
+
/** A typed memory region. `mmio` is reserved for forward-compat; cards provide
|
|
24
|
+
* memory-mapped I/O via MemoryMappedIOAdapter at attach time, so buildMachine
|
|
25
|
+
* supports `ram` and `rom` and rejects `mmio` in this build. */
|
|
26
|
+
export interface MemoryRegionSpec {
|
|
27
|
+
id: string;
|
|
28
|
+
base: number;
|
|
29
|
+
size: number;
|
|
30
|
+
kind: 'ram' | 'rom' | 'mmio';
|
|
31
|
+
image?: Uint8Array;
|
|
32
|
+
}
|
|
33
|
+
/** Instance-scoped dependencies injected into a card factory (AD-5). 8sim
|
|
34
|
+
* supplies `pic` and `log`; the caller (fdcplus-web) supplies the FDC channel
|
|
35
|
+
* and anything else through `services`. Dumb cards ignore it. */
|
|
36
|
+
export interface CardContext {
|
|
37
|
+
pic: IInterruptController;
|
|
38
|
+
log: (message: string) => void;
|
|
39
|
+
services: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
/** An already-loaded card factory (AD-2). */
|
|
42
|
+
export type CardFactory = (id: string, config: Record<string, unknown>, ctx: CardContext) => IS100Card;
|
|
43
|
+
export interface CardSpec {
|
|
44
|
+
id: string;
|
|
45
|
+
factory: CardFactory;
|
|
46
|
+
config?: Record<string, unknown>;
|
|
47
|
+
/** Declared bus resources, used for define-time collision rejection (AD-1).
|
|
48
|
+
* The engine's IoSpace is last-writer-wins and the PIC has no ownership, so
|
|
49
|
+
* buildMachine asserts these are collision-free rather than trusting silence. */
|
|
50
|
+
claims?: {
|
|
51
|
+
ports?: number[];
|
|
52
|
+
irq?: number | null;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface MachineSpec {
|
|
56
|
+
cpuKind: CpuKind;
|
|
57
|
+
clock: Clock;
|
|
58
|
+
/** Program counter at power-on (e.g. 0xFF00 for CDBL). Construction is data,
|
|
59
|
+
* not an imperative `cpu.pc = …` step. */
|
|
60
|
+
resetVector: number;
|
|
61
|
+
/** Memory regions; attached in array order (overlaps are rejected). */
|
|
62
|
+
memory: MemoryRegionSpec[];
|
|
63
|
+
/** Cards in slot order; attached after memory, before the CPU runs. */
|
|
64
|
+
cards: CardSpec[];
|
|
65
|
+
}
|
|
66
|
+
/** The runnable handle buildMachine returns. */
|
|
67
|
+
export interface Machine {
|
|
68
|
+
cpu: ICpu;
|
|
69
|
+
bus: Bus;
|
|
70
|
+
pic: IInterruptController;
|
|
71
|
+
cards: IS100Card[];
|
|
72
|
+
runner: MachineRunner;
|
|
73
|
+
spec: MachineSpec;
|
|
74
|
+
}
|
|
75
|
+
/** Thrown when a MachineSpec is invalid or would build an inconsistent machine.
|
|
76
|
+
* Plain Error subclass — 8sim stays zero-dep and browser-portable. */
|
|
77
|
+
export declare class MachineSpecError extends Error {
|
|
78
|
+
constructor(message: string);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=MachineSpec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MachineSpec.d.ts","sourceRoot":"","sources":["../../src/machine/MachineSpec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;;;;GAUG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AAEtC,uDAAuD;AACvD,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC;AAE3C;;gEAEgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED;;iEAEiE;AACjE,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,oBAAoB,CAAC;IAC1B,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,CACxB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,WAAW,KACb,SAAS,CAAC;AAEf,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;qFAEiF;IACjF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb;8CAC0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,uEAAuE;IACvE,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,gDAAgD;AAChD,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,oBAAoB,CAAC;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;sEACsE;AACtE,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Thrown when a MachineSpec is invalid or would build an inconsistent machine.
|
|
2
|
+
* Plain Error subclass — 8sim stays zero-dep and browser-portable. */
|
|
3
|
+
export class MachineSpecError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'MachineSpecError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=MachineSpec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MachineSpec.js","sourceRoot":"","sources":["../../src/machine/MachineSpec.ts"],"names":[],"mappings":"AAsFA;sEACsE;AACtE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type MachineSpec, type Machine } from './MachineSpec.js';
|
|
2
|
+
export interface BuildOptions {
|
|
3
|
+
/** Per-machine logger injected into each card's CardContext. */
|
|
4
|
+
log?: (message: string) => void;
|
|
5
|
+
/** Caller-supplied instance-scoped services (e.g. the FDC channel a disk
|
|
6
|
+
* controller card pulls from `ctx.services`). */
|
|
7
|
+
services?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Validate a MachineSpec and build a runnable machine (AD-1, AD-2).
|
|
11
|
+
*
|
|
12
|
+
* Construction order is pinned: memory regions (array order) → cards (slot
|
|
13
|
+
* order) → CPU. All collisions are rejected here — the Bus is first-region-wins
|
|
14
|
+
* and IoSpace is last-writer-wins, so a silently-overlapping spec would produce
|
|
15
|
+
* a wrong machine. `buildMachine` never loads code: each card entry carries an
|
|
16
|
+
* already-loaded factory.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildMachine(spec: MachineSpec, opts?: BuildOptions): Machine;
|
|
19
|
+
//# sourceMappingURL=buildMachine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildMachine.d.ts","sourceRoot":"","sources":["../../src/machine/buildMachine.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EAGb,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC;qDACiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAKD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CA8ChF"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Bus } from '../bus/Bus.js';
|
|
2
|
+
import { Ram } from '../memory/Ram.js';
|
|
3
|
+
import { Rom } from '../memory/Rom.js';
|
|
4
|
+
import { InterruptController } from '../interrupt/InterruptController.js';
|
|
5
|
+
import { Cpu8080 } from '../cpu/Cpu8080.js';
|
|
6
|
+
import { CpuZ80 } from '../cpu/z80/CpuZ80.js';
|
|
7
|
+
import { MachineRunner } from './MachineRunner.js';
|
|
8
|
+
import { MachineSpecError, } from './MachineSpec.js';
|
|
9
|
+
const inU16 = (n) => Number.isInteger(n) && n >= 0 && n <= 0xffff;
|
|
10
|
+
const regionEnd = (r) => r.base + r.size - 1;
|
|
11
|
+
/**
|
|
12
|
+
* Validate a MachineSpec and build a runnable machine (AD-1, AD-2).
|
|
13
|
+
*
|
|
14
|
+
* Construction order is pinned: memory regions (array order) → cards (slot
|
|
15
|
+
* order) → CPU. All collisions are rejected here — the Bus is first-region-wins
|
|
16
|
+
* and IoSpace is last-writer-wins, so a silently-overlapping spec would produce
|
|
17
|
+
* a wrong machine. `buildMachine` never loads code: each card entry carries an
|
|
18
|
+
* already-loaded factory.
|
|
19
|
+
*/
|
|
20
|
+
export function buildMachine(spec, opts = {}) {
|
|
21
|
+
validate(spec);
|
|
22
|
+
const pic = new InterruptController();
|
|
23
|
+
const bus = new Bus(pic);
|
|
24
|
+
// 1) Memory regions, in array order.
|
|
25
|
+
for (const region of spec.memory) {
|
|
26
|
+
if (region.kind === 'rom') {
|
|
27
|
+
bus.attachMemory(new Rom(region.id, region.base, region.image));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// ram
|
|
31
|
+
const ram = new Ram(region.id, region.base, region.size);
|
|
32
|
+
if (region.image) {
|
|
33
|
+
for (let i = 0; i < region.image.length; i++)
|
|
34
|
+
ram.write(i, region.image[i]);
|
|
35
|
+
}
|
|
36
|
+
bus.attachMemory(ram);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// 2) Cards, in slot order. Each factory is already loaded (AD-2).
|
|
40
|
+
// NOTE (forward-compat gap): a card may attach its own memory (a
|
|
41
|
+
// memory-mapped I/O card via MemoryMappedIOAdapter) through `attach(bus)`,
|
|
42
|
+
// which bypasses the declarative memory-region overlap check above. No seed
|
|
43
|
+
// card does this today; when memory-mapped cards land, their regions must be
|
|
44
|
+
// declared and validated too (tracked with the deferred `mmio` region kind).
|
|
45
|
+
const ctx = {
|
|
46
|
+
pic,
|
|
47
|
+
log: opts.log ?? (() => { }),
|
|
48
|
+
services: opts.services ?? {},
|
|
49
|
+
};
|
|
50
|
+
const cards = [];
|
|
51
|
+
for (const card of spec.cards) {
|
|
52
|
+
const built = card.factory(card.id, card.config ?? {}, ctx);
|
|
53
|
+
built.attach(bus);
|
|
54
|
+
cards.push(built);
|
|
55
|
+
}
|
|
56
|
+
// 3) CPU last; power-on PC comes from the spec, not an imperative step.
|
|
57
|
+
const cpu = spec.cpuKind === 'z80' ? new CpuZ80(bus, pic) : new Cpu8080(bus, pic);
|
|
58
|
+
cpu.reset();
|
|
59
|
+
cpu.pc = spec.resetVector;
|
|
60
|
+
const runner = new MachineRunner(cpu, { hz: spec.clock === 'max' ? 'max' : spec.clock.hz });
|
|
61
|
+
return { cpu, bus, pic, cards, runner, spec };
|
|
62
|
+
}
|
|
63
|
+
function validate(spec) {
|
|
64
|
+
if (spec.cpuKind !== 'i8080' && spec.cpuKind !== 'z80') {
|
|
65
|
+
throw new MachineSpecError(`cpuKind must be "i8080" or "z80", got ${JSON.stringify(spec.cpuKind)}`);
|
|
66
|
+
}
|
|
67
|
+
if (spec.clock !== 'max' && !(typeof spec.clock === 'object' && spec.clock.hz > 0)) {
|
|
68
|
+
throw new MachineSpecError(`clock must be "max" or { hz: >0 }, got ${JSON.stringify(spec.clock)}`);
|
|
69
|
+
}
|
|
70
|
+
if (!inU16(spec.resetVector)) {
|
|
71
|
+
throw new MachineSpecError(`resetVector must be an integer in 0x0000..0xFFFF, got ${spec.resetVector}`);
|
|
72
|
+
}
|
|
73
|
+
// Memory regions: shape + bounds.
|
|
74
|
+
for (const r of spec.memory) {
|
|
75
|
+
if (r.kind === 'mmio') {
|
|
76
|
+
throw new MachineSpecError(`memory region "${r.id}": kind "mmio" is not supported by buildMachine (memory-mapped I/O is provided by cards)`);
|
|
77
|
+
}
|
|
78
|
+
if (!inU16(r.base) || !Number.isInteger(r.size) || r.size < 1 || r.base + r.size > 0x10000) {
|
|
79
|
+
throw new MachineSpecError(`memory region "${r.id}": invalid base/size (base=${r.base}, size=${r.size}); must fit within 0x0000..0xFFFF`);
|
|
80
|
+
}
|
|
81
|
+
if (r.kind === 'rom' && (!r.image || r.image.length !== r.size)) {
|
|
82
|
+
throw new MachineSpecError(`memory region "${r.id}": rom requires an image whose length (${r.image?.length ?? 0}) equals size (${r.size})`);
|
|
83
|
+
}
|
|
84
|
+
if (r.kind === 'ram' && r.image && r.image.length > r.size) {
|
|
85
|
+
throw new MachineSpecError(`memory region "${r.id}": ram image length (${r.image.length}) exceeds size (${r.size})`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Memory overlaps: sort a copy by base, check neighbours.
|
|
89
|
+
const sorted = [...spec.memory].sort((a, b) => a.base - b.base);
|
|
90
|
+
for (let i = 0; i + 1 < sorted.length; i++) {
|
|
91
|
+
const a = sorted[i];
|
|
92
|
+
const b = sorted[i + 1];
|
|
93
|
+
if (regionEnd(a) >= b.base) {
|
|
94
|
+
throw new MachineSpecError(`memory regions overlap: "${a.id}" (0x${a.base.toString(16)}-0x${regionEnd(a).toString(16)}) and ` +
|
|
95
|
+
`"${b.id}" (0x${b.base.toString(16)}-0x${regionEnd(b).toString(16)})`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// I/O port collisions across card claims.
|
|
99
|
+
const portOwner = new Map();
|
|
100
|
+
for (const card of spec.cards) {
|
|
101
|
+
for (const port of card.claims?.ports ?? []) {
|
|
102
|
+
const prev = portOwner.get(port & 0xff);
|
|
103
|
+
if (prev !== undefined) {
|
|
104
|
+
throw new MachineSpecError(`I/O port 0x${(port & 0xff).toString(16)} claimed by both "${prev}" and "${card.id}"`);
|
|
105
|
+
}
|
|
106
|
+
portOwner.set(port & 0xff, card.id);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// IRQ line collisions across card claims.
|
|
110
|
+
const irqOwner = new Map();
|
|
111
|
+
for (const card of spec.cards) {
|
|
112
|
+
const irq = card.claims?.irq;
|
|
113
|
+
if (irq === undefined || irq === null)
|
|
114
|
+
continue;
|
|
115
|
+
const prev = irqOwner.get(irq);
|
|
116
|
+
if (prev !== undefined) {
|
|
117
|
+
throw new MachineSpecError(`IRQ line ${irq} claimed by both "${prev}" and "${card.id}"`);
|
|
118
|
+
}
|
|
119
|
+
irqOwner.set(irq, card.id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=buildMachine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildMachine.js","sourceRoot":"","sources":["../../src/machine/buildMachine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IMemory } from '../interfaces/IMemory.js';
|
|
2
|
+
import type { IIODevice } from '../interfaces/IIODevice.js';
|
|
3
|
+
export declare class MemoryMappedIOAdapter implements IMemory {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly baseAddress: number;
|
|
6
|
+
readonly size: number;
|
|
7
|
+
readonly readOnly = false;
|
|
8
|
+
private device;
|
|
9
|
+
constructor(baseAddress: number, size: number, device: IIODevice);
|
|
10
|
+
read(offset: number): number;
|
|
11
|
+
write(offset: number, value: number): void;
|
|
12
|
+
reset(): void;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=MemoryMappedIOAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryMappedIOAdapter.d.ts","sourceRoot":"","sources":["../../src/memory/MemoryMappedIOAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,qBAAa,qBAAsB,YAAW,OAAO;IACnD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,SAAS;IAC1B,OAAO,CAAC,MAAM,CAAY;gBAEd,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS;IAOhE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAI5B,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class MemoryMappedIOAdapter {
|
|
2
|
+
id;
|
|
3
|
+
baseAddress;
|
|
4
|
+
size;
|
|
5
|
+
readOnly = false;
|
|
6
|
+
device;
|
|
7
|
+
constructor(baseAddress, size, device) {
|
|
8
|
+
this.id = `mmio:${device.id}`;
|
|
9
|
+
this.baseAddress = baseAddress & 0xffff;
|
|
10
|
+
this.size = size;
|
|
11
|
+
this.device = device;
|
|
12
|
+
}
|
|
13
|
+
read(offset) {
|
|
14
|
+
return this.device.ioRead(offset);
|
|
15
|
+
}
|
|
16
|
+
write(offset, value) {
|
|
17
|
+
this.device.ioWrite(offset, value);
|
|
18
|
+
}
|
|
19
|
+
reset() {
|
|
20
|
+
this.device.reset();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=MemoryMappedIOAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryMappedIOAdapter.js","sourceRoot":"","sources":["../../src/memory/MemoryMappedIOAdapter.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,qBAAqB;IACvB,EAAE,CAAS;IACX,WAAW,CAAS;IACpB,IAAI,CAAS;IACb,QAAQ,GAAG,KAAK,CAAC;IAClB,MAAM,CAAY;IAE1B,YAAY,WAAmB,EAAE,IAAY,EAAE,MAAiB;QAC9D,IAAI,CAAC,EAAE,GAAG,QAAQ,MAAM,CAAC,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,MAAc;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,KAAa;QACjC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IMemory } from '../interfaces/IMemory.js';
|
|
2
|
+
export declare class Ram implements IMemory {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly baseAddress: number;
|
|
5
|
+
readonly size: number;
|
|
6
|
+
readonly readOnly = false;
|
|
7
|
+
private data;
|
|
8
|
+
constructor(id: string, baseAddress: number, size: number);
|
|
9
|
+
read(offset: number): number;
|
|
10
|
+
write(offset: number, value: number): void;
|
|
11
|
+
reset(): void;
|
|
12
|
+
/** Load bytes into RAM starting at offset */
|
|
13
|
+
load(data: Uint8Array, offset?: number): void;
|
|
14
|
+
/** Direct access for testing */
|
|
15
|
+
getBytes(): Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=Ram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Ram.d.ts","sourceRoot":"","sources":["../../src/memory/Ram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAGxD,qBAAa,GAAI,YAAW,OAAO;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,SAAS;IAC1B,OAAO,CAAC,IAAI,CAAa;gBAEb,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAOzD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAK5B,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1C,KAAK,IAAI,IAAI;IAIb,6CAA6C;IAC7C,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,SAAI,GAAG,IAAI;IAIxC,gCAAgC;IAChC,QAAQ,IAAI,UAAU;CAGvB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { u8 } from '../util/bits.js';
|
|
2
|
+
export class Ram {
|
|
3
|
+
id;
|
|
4
|
+
baseAddress;
|
|
5
|
+
size;
|
|
6
|
+
readOnly = false;
|
|
7
|
+
data;
|
|
8
|
+
constructor(id, baseAddress, size) {
|
|
9
|
+
this.id = id;
|
|
10
|
+
this.baseAddress = baseAddress & 0xffff;
|
|
11
|
+
this.size = size;
|
|
12
|
+
this.data = new Uint8Array(size);
|
|
13
|
+
}
|
|
14
|
+
read(offset) {
|
|
15
|
+
if (offset < 0 || offset >= this.size)
|
|
16
|
+
return 0xff;
|
|
17
|
+
return this.data[offset] ?? 0xff;
|
|
18
|
+
}
|
|
19
|
+
write(offset, value) {
|
|
20
|
+
if (offset < 0 || offset >= this.size)
|
|
21
|
+
return;
|
|
22
|
+
this.data[offset] = u8(value);
|
|
23
|
+
}
|
|
24
|
+
reset() {
|
|
25
|
+
this.data.fill(0);
|
|
26
|
+
}
|
|
27
|
+
/** Load bytes into RAM starting at offset */
|
|
28
|
+
load(data, offset = 0) {
|
|
29
|
+
this.data.set(data, offset);
|
|
30
|
+
}
|
|
31
|
+
/** Direct access for testing */
|
|
32
|
+
getBytes() {
|
|
33
|
+
return this.data;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=Ram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Ram.js","sourceRoot":"","sources":["../../src/memory/Ram.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAErC,MAAM,OAAO,GAAG;IACL,EAAE,CAAS;IACX,WAAW,CAAS;IACpB,IAAI,CAAS;IACb,QAAQ,GAAG,KAAK,CAAC;IAClB,IAAI,CAAa;IAEzB,YAAY,EAAU,EAAE,WAAmB,EAAE,IAAY;QACvD,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,MAAc;QACjB,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACnD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,KAAa;QACjC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QAC9C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,6CAA6C;IAC7C,IAAI,CAAC,IAAgB,EAAE,MAAM,GAAG,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,gCAAgC;IAChC,QAAQ;QACN,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IMemory } from '../interfaces/IMemory.js';
|
|
2
|
+
export declare class Rom implements IMemory {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly baseAddress: number;
|
|
5
|
+
readonly size: number;
|
|
6
|
+
readonly readOnly = true;
|
|
7
|
+
private data;
|
|
8
|
+
constructor(id: string, baseAddress: number, data: Uint8Array);
|
|
9
|
+
read(offset: number): number;
|
|
10
|
+
write(_offset: number, _value: number): void;
|
|
11
|
+
reset(): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Rom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rom.d.ts","sourceRoot":"","sources":["../../src/memory/Rom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAExD,qBAAa,GAAI,YAAW,OAAO;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,QAAQ;IACzB,OAAO,CAAC,IAAI,CAAa;gBAEb,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IAO7D,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAK5B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAI5C,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class Rom {
|
|
2
|
+
id;
|
|
3
|
+
baseAddress;
|
|
4
|
+
size;
|
|
5
|
+
readOnly = true;
|
|
6
|
+
data;
|
|
7
|
+
constructor(id, baseAddress, data) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.baseAddress = baseAddress & 0xffff;
|
|
10
|
+
this.size = data.length;
|
|
11
|
+
this.data = new Uint8Array(data);
|
|
12
|
+
}
|
|
13
|
+
read(offset) {
|
|
14
|
+
if (offset < 0 || offset >= this.size)
|
|
15
|
+
return 0xff;
|
|
16
|
+
return this.data[offset] ?? 0xff;
|
|
17
|
+
}
|
|
18
|
+
write(_offset, _value) {
|
|
19
|
+
// ROM is read-only; writes silently ignored
|
|
20
|
+
}
|
|
21
|
+
reset() {
|
|
22
|
+
// ROM content is immutable
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=Rom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rom.js","sourceRoot":"","sources":["../../src/memory/Rom.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG;IACL,EAAE,CAAS;IACX,WAAW,CAAS;IACpB,IAAI,CAAS;IACb,QAAQ,GAAG,IAAI,CAAC;IACjB,IAAI,CAAa;IAEzB,YAAY,EAAU,EAAE,WAAmB,EAAE,IAAgB;QAC3D,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,MAAc;QACjB,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACnD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,MAAc;QACnC,4CAA4C;IAC9C,CAAC;IAED,KAAK;QACH,2BAA2B;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function u8(v: number): number;
|
|
2
|
+
export declare function u16(v: number): number;
|
|
3
|
+
export declare function signBit(v: number): boolean;
|
|
4
|
+
export declare function zeroFlag(v: number): boolean;
|
|
5
|
+
export declare function parityFlag(v: number): boolean;
|
|
6
|
+
export declare function auxCarryAdd(a: number, b: number, carry?: number): boolean;
|
|
7
|
+
export declare function auxCarrySub(a: number, b: number, borrow?: number): boolean;
|
|
8
|
+
export declare function toWord(hi: number, lo: number): number;
|
|
9
|
+
export declare function hi(w: number): number;
|
|
10
|
+
export declare function lo(w: number): number;
|
|
11
|
+
//# sourceMappingURL=bits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bits.d.ts","sourceRoot":"","sources":["../../src/util/bits.ts"],"names":[],"mappings":"AAAA,wBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpC;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErC;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE1C;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE3C;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAM7C;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAEpE;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,OAAO,CAErE;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpC;AAED,wBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function u8(v) {
|
|
2
|
+
return v & 0xff;
|
|
3
|
+
}
|
|
4
|
+
export function u16(v) {
|
|
5
|
+
return v & 0xffff;
|
|
6
|
+
}
|
|
7
|
+
export function signBit(v) {
|
|
8
|
+
return (v & 0x80) !== 0;
|
|
9
|
+
}
|
|
10
|
+
export function zeroFlag(v) {
|
|
11
|
+
return (v & 0xff) === 0;
|
|
12
|
+
}
|
|
13
|
+
export function parityFlag(v) {
|
|
14
|
+
let x = v & 0xff;
|
|
15
|
+
x ^= x >> 4;
|
|
16
|
+
x ^= x >> 2;
|
|
17
|
+
x ^= x >> 1;
|
|
18
|
+
return (x & 1) === 0;
|
|
19
|
+
}
|
|
20
|
+
export function auxCarryAdd(a, b, carry = 0) {
|
|
21
|
+
return ((a & 0xf) + (b & 0xf) + carry) > 0xf;
|
|
22
|
+
}
|
|
23
|
+
export function auxCarrySub(a, b, borrow = 0) {
|
|
24
|
+
return ((a & 0xf) - (b & 0xf) - borrow) < 0;
|
|
25
|
+
}
|
|
26
|
+
export function toWord(hi, lo) {
|
|
27
|
+
return ((hi & 0xff) << 8) | (lo & 0xff);
|
|
28
|
+
}
|
|
29
|
+
export function hi(w) {
|
|
30
|
+
return (w >> 8) & 0xff;
|
|
31
|
+
}
|
|
32
|
+
export function lo(w) {
|
|
33
|
+
return w & 0xff;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=bits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bits.js","sourceRoot":"","sources":["../../src/util/bits.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,EAAE,CAAC,CAAS;IAC1B,OAAO,CAAC,GAAG,IAAI,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,CAAS;IAC3B,OAAO,CAAC,GAAG,MAAM,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACjB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACZ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACZ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACZ,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS,EAAE,KAAK,GAAG,CAAC;IACzD,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS,EAAE,MAAM,GAAG,CAAC;IAC1D,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,EAAU,EAAE,EAAU;IAC3C,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,EAAE,CAAC,CAAS;IAC1B,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,EAAE,CAAC,CAAS;IAC1B,OAAO,CAAC,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostConsole.d.ts","sourceRoot":"","sources":["../../src/util/hostConsole.ts"],"names":[],"mappings":"AAOA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAElD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostConsole.js","sourceRoot":"","sources":["../../src/util/hostConsole.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,eAAe,CAAC,IAAY;IACzC,UAAwC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;AACzE,CAAC"}
|