diff options
| author | Uri Shaked | 2020-12-09 15:46:53 +0200 |
|---|---|---|
| committer | Uri Shaked | 2020-12-09 15:49:41 +0200 |
| commit | 9c1288f18889ae3bd10869a9f6ebc53defa3024b (patch) | |
| tree | 1857fe48d3e2d32a39cfe810a0dfdd7d96526b3a /src/cpu | |
| parent | refactor: central interrupt handling #38 (diff) | |
| download | avr8js-9c1288f18889ae3bd10869a9f6ebc53defa3024b.tar.gz avr8js-9c1288f18889ae3bd10869a9f6ebc53defa3024b.tar.bz2 avr8js-9c1288f18889ae3bd10869a9f6ebc53defa3024b.zip | |
perf!: centeral timekeeping
This should improve performance, especially when running simulations with
multiple peripherals. For instance, the demo project now runs at ~322%,
up from ~185% in AVR8js 0.13.1.
BREAKING CHANGE: `tick()` methods were removed from individual peripherals.
You now need to call `cpu.tick()` instead.
Diffstat (limited to '')
| -rw-r--r-- | src/cpu/cpu.ts | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts index 4b70efb..fb98786 100644 --- a/src/cpu/cpu.ts +++ b/src/cpu/cpu.ts @@ -55,6 +55,13 @@ export interface AVRInterruptConfig { constant?: boolean; } +export type AVRClockEventCallback = () => void; + +interface AVRClockEventEntry { + cycles: number; + callback: AVRClockEventCallback; +} + export class CPU implements ICPU { readonly data: Uint8Array = new Uint8Array(this.sramBytes + registerSpace); readonly data16 = new Uint16Array(this.data.buffer); @@ -63,6 +70,7 @@ export class CPU implements ICPU { readonly readHooks: CPUMemoryReadHooks = []; readonly writeHooks: CPUMemoryHooks = []; private readonly pendingInterrupts: AVRInterruptConfig[] = []; + private readonly clockEvents: AVRClockEventEntry[] = []; readonly pc22Bits = this.progBytes.length > 0x20000; // This lets the Timer Compare output override GPIO pins: @@ -71,6 +79,7 @@ export class CPU implements ICPU { pc: u32 = 0; cycles: u32 = 0; nextInterrupt: i16 = -1; + private nextClockEvent: u32 = 0; constructor(public progMem: Uint16Array, private sramBytes = 8192) { this.reset(); @@ -164,10 +173,44 @@ export class CPU implements ICPU { } } + private updateClockEvents() { + this.clockEvents.sort((a, b) => a.cycles - b.cycles); + this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0; + } + + addClockEvent(callback: AVRClockEventCallback, cycles: number) { + const entry = { cycles: this.cycles + Math.max(1, cycles), callback }; + this.clockEvents.push(entry); + this.updateClockEvents(); + return callback; + } + + updateClockEvent(callback: AVRClockEventCallback, cycles: number) { + const entry = this.clockEvents.find((item) => (item.callback = callback)); + if (entry) { + entry.cycles = this.cycles + Math.max(1, cycles); + this.updateClockEvents(); + return true; + } + return false; + } + + clearClockEvent(callback: AVRClockEventCallback) { + const index = this.clockEvents.findIndex((item) => (item.callback = callback)); + if (index >= 0) { + this.clockEvents.splice(index, 1); + this.updateClockEvents(); + } + } + tick() { + if (this.nextClockEvent && this.nextClockEvent <= this.cycles) { + const clockEvent = this.clockEvents.shift(); + clockEvent?.callback(); + this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0; + } if (this.interruptsEnabled && this.nextInterrupt >= 0) { const interrupt = this.pendingInterrupts[this.nextInterrupt]; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion avrInterrupt(this, interrupt.address); if (!interrupt.constant) { this.clearInterrupt(interrupt); |
