aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUri Shaked2020-12-12 15:01:17 +0200
committerUri Shaked2020-12-12 15:01:17 +0200
commite7ea8c76bd649aa84fdeb084fec02529743ee6eb (patch)
treeffe520a2f308a61805b04ddfbc3432a71205159c /src
parenttest(cpu): fix implicit any error (diff)
downloadavr8js-e7ea8c76bd649aa84fdeb084fec02529743ee6eb.tar.gz
avr8js-e7ea8c76bd649aa84fdeb084fec02529743ee6eb.tar.bz2
avr8js-e7ea8c76bd649aa84fdeb084fec02529743ee6eb.zip
perf(cpu): speed up event system
ditch `array.sort()` and instead manually keep the array sorted when we insert a new item.
Diffstat (limited to 'src')
-rw-r--r--src/cpu/cpu.spec.ts31
-rw-r--r--src/cpu/cpu.ts46
2 files changed, 59 insertions, 18 deletions
diff --git a/src/cpu/cpu.spec.ts b/src/cpu/cpu.spec.ts
index b1bc876..969b51c 100644
--- a/src/cpu/cpu.spec.ts
+++ b/src/cpu/cpu.spec.ts
@@ -26,6 +26,23 @@ describe('cpu', () => {
]);
});
+ it('should correctly sort the events when added in reverse order', () => {
+ const cpu = new CPU(new Uint16Array(1024), 0x1000);
+ const events: ITestEvent[] = [];
+ for (const i of [10, 4, 1]) {
+ cpu.addClockEvent(() => events.push([i, cpu.cycles]), i);
+ }
+ for (let i = 0; i < 10; i++) {
+ cpu.cycles++;
+ cpu.tick();
+ }
+ expect(events).toEqual([
+ [1, 1],
+ [4, 4],
+ [10, 10],
+ ]);
+ });
+
describe('updateClockEvent', () => {
it('should update the number of cycles for the given clock event', () => {
const cpu = new CPU(new Uint16Array(1024), 0x1000);
@@ -65,6 +82,20 @@ describe('cpu', () => {
[10, 10],
]);
});
+
+ it('should return false if the provided clock event is not scheduled', () => {
+ const cpu = new CPU(new Uint16Array(1024), 0x1000);
+ const event4 = cpu.addClockEvent(() => 0, 4);
+ const event10 = cpu.addClockEvent(() => 0, 10);
+ cpu.addClockEvent(() => 0, 1);
+
+ // Both event should be successfully removed
+ expect(cpu.clearClockEvent(event4)).toBe(true);
+ expect(cpu.clearClockEvent(event10)).toBe(true);
+ // And now we should get false, as these events have already been removed
+ expect(cpu.clearClockEvent(event4)).toBe(false);
+ expect(cpu.clearClockEvent(event10)).toBe(false);
+ });
});
});
});
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts
index e7a2544..a609222 100644
--- a/src/cpu/cpu.ts
+++ b/src/cpu/cpu.ts
@@ -173,23 +173,29 @@ 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();
+ // Add the new entry while keeping the array sorted
+ const { clockEvents } = this;
+ if (!clockEvents.length || clockEvents[clockEvents.length - 1].cycles <= entry.cycles) {
+ clockEvents.push(entry);
+ } else if (clockEvents[0].cycles >= entry.cycles) {
+ clockEvents.unshift(entry);
+ } else {
+ for (let i = 1; i < clockEvents.length; i++) {
+ if (clockEvents[i].cycles >= entry.cycles) {
+ clockEvents.splice(i, 0, entry);
+ break;
+ }
+ }
+ }
+ this.nextClockEvent = this.clockEvents[0].cycles;
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();
+ if (this.clearClockEvent(callback)) {
+ this.addClockEvent(callback, cycles);
return true;
}
return false;
@@ -199,18 +205,22 @@ export class CPU implements ICPU {
const index = this.clockEvents.findIndex((item) => item.callback === callback);
if (index >= 0) {
this.clockEvents.splice(index, 1);
- this.updateClockEvents();
+ this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0;
+ return true;
}
+ return false;
}
tick() {
- if (this.nextClockEvent && this.nextClockEvent <= this.cycles) {
- const clockEvent = this.clockEvents.shift();
- clockEvent?.callback();
- this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0;
+ const { nextClockEvent, clockEvents } = this;
+ if (nextClockEvent && nextClockEvent <= this.cycles) {
+ clockEvents.shift()?.callback();
+ this.nextClockEvent = clockEvents[0]?.cycles ?? 0;
}
- if (this.interruptsEnabled && this.nextInterrupt >= 0) {
- const interrupt = this.pendingInterrupts[this.nextInterrupt];
+
+ const { nextInterrupt } = this;
+ if (this.interruptsEnabled && nextInterrupt >= 0) {
+ const interrupt = this.pendingInterrupts[nextInterrupt];
avrInterrupt(this, interrupt.address);
if (!interrupt.constant) {
this.clearInterrupt(interrupt);