aboutsummaryrefslogtreecommitdiff
path: root/src/cpu/cpu.ts
diff options
context:
space:
mode:
authorUri Shaked2020-12-09 00:51:13 +0200
committerUri Shaked2020-12-09 00:51:58 +0200
commit36c4134a26063248a2ef47f5ac8defe50d9476b1 (patch)
tree44433290b0d684768216b33550a7af9a6fb4235f /src/cpu/cpu.ts
parenttest(cpu): improve test name (diff)
downloadavr8js-36c4134a26063248a2ef47f5ac8defe50d9476b1.tar.gz
avr8js-36c4134a26063248a2ef47f5ac8defe50d9476b1.tar.bz2
avr8js-36c4134a26063248a2ef47f5ac8defe50d9476b1.zip
refactor: central interrupt handling #38
Diffstat (limited to '')
-rw-r--r--src/cpu/cpu.ts79
1 files changed, 76 insertions, 3 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts
index ed88e71..4b70efb 100644
--- a/src/cpu/cpu.ts
+++ b/src/cpu/cpu.ts
@@ -5,7 +5,8 @@
* Copyright (C) 2019, Uri Shaked
*/
-import { u32, u16, u8 } from '../types';
+import { u32, u16, u8, i16 } from '../types';
+import { avrInterrupt } from './interrupt';
const registerSpace = 0x100;
@@ -45,6 +46,15 @@ export interface CPUMemoryReadHooks {
[key: number]: CPUMemoryReadHook;
}
+export interface AVRInterruptConfig {
+ address: u8;
+ enableRegister: u16;
+ enableMask: u8;
+ flagRegister: u16;
+ flagMask: u8;
+ constant?: boolean;
+}
+
export class CPU implements ICPU {
readonly data: Uint8Array = new Uint8Array(this.sramBytes + registerSpace);
readonly data16 = new Uint16Array(this.data.buffer);
@@ -52,13 +62,15 @@ export class CPU implements ICPU {
readonly progBytes = new Uint8Array(this.progMem.buffer);
readonly readHooks: CPUMemoryReadHooks = [];
readonly writeHooks: CPUMemoryHooks = [];
+ private readonly pendingInterrupts: AVRInterruptConfig[] = [];
readonly pc22Bits = this.progBytes.length > 0x20000;
// This lets the Timer Compare output override GPIO pins:
readonly gpioTimerHooks: CPUMemoryHooks = [];
- pc = 0;
- cycles = 0;
+ pc: u32 = 0;
+ cycles: u32 = 0;
+ nextInterrupt: i16 = -1;
constructor(public progMem: Uint16Array, private sramBytes = 8192) {
this.reset();
@@ -67,6 +79,8 @@ export class CPU implements ICPU {
reset() {
this.data.fill(0);
this.SP = this.data.length - 1;
+ this.pendingInterrupts.splice(0, this.pendingInterrupts.length);
+ this.nextInterrupt = -1;
}
readData(addr: number) {
@@ -101,4 +115,63 @@ export class CPU implements ICPU {
get interruptsEnabled() {
return this.SREG & 0x80 ? true : false;
}
+
+ private updateNextInterrupt() {
+ this.nextInterrupt = this.pendingInterrupts.findIndex((item) => !!item);
+ }
+
+ setInterruptFlag(interrupt: AVRInterruptConfig) {
+ const { flagRegister, flagMask, enableRegister, enableMask } = interrupt;
+ if (interrupt.constant) {
+ this.data[flagRegister] &= ~flagMask;
+ } else {
+ this.data[flagRegister] |= flagMask;
+ }
+ if (this.data[enableRegister] & enableMask) {
+ this.queueInterrupt(interrupt);
+ }
+ }
+
+ updateInterruptEnable(interrupt: AVRInterruptConfig, registerValue: u8) {
+ const { enableMask, flagRegister, flagMask } = interrupt;
+ if (registerValue & enableMask) {
+ if (this.data[flagRegister] & flagMask) {
+ this.queueInterrupt(interrupt);
+ }
+ } else {
+ this.clearInterrupt(interrupt, false);
+ }
+ }
+
+ queueInterrupt(interrupt: AVRInterruptConfig) {
+ this.pendingInterrupts[interrupt.address] = interrupt;
+ this.updateNextInterrupt();
+ }
+
+ clearInterrupt({ address, flagRegister, flagMask }: AVRInterruptConfig, clearFlag = true) {
+ delete this.pendingInterrupts[address];
+ if (clearFlag) {
+ this.data[flagRegister] &= ~flagMask;
+ }
+ this.updateNextInterrupt();
+ }
+
+ clearInterruptByFlag(interrupt: AVRInterruptConfig, registerValue: number) {
+ const { flagRegister, flagMask } = interrupt;
+ if (registerValue & flagMask) {
+ this.data[flagRegister] &= ~flagMask;
+ this.clearInterrupt(interrupt);
+ }
+ }
+
+ tick() {
+ 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);
+ }
+ }
+ }
}