aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUri Shaked2021-07-07 15:32:09 +0300
committerGitHub2021-07-07 15:32:09 +0300
commitaf5ac6d4687b8b3903fa300527655ff43be12526 (patch)
tree52d885a00242ad05804e9356ceb83809fe76dc91 /src
parent0.15.3 (diff)
downloadavr8js-af5ac6d4687b8b3903fa300527655ff43be12526.tar.gz
avr8js-af5ac6d4687b8b3903fa300527655ff43be12526.tar.bz2
avr8js-af5ac6d4687b8b3903fa300527655ff43be12526.zip
feat(gpio): external interrupt/PCINT support (#82)
close #70, #84
Diffstat (limited to '')
-rw-r--r--src/cpu/cpu.ts2
-rw-r--r--src/index.ts7
-rw-r--r--src/peripherals/gpio.spec.ts188
-rw-r--r--src/peripherals/gpio.ts280
4 files changed, 459 insertions, 18 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts
index 55b8a68..3cb4846 100644
--- a/src/cpu/cpu.ts
+++ b/src/cpu/cpu.ts
@@ -5,6 +5,7 @@
* Copyright (C) 2019, Uri Shaked
*/
+import { AVRIOPort } from '../peripherals/gpio';
import { u32, u16, u8, i16 } from '../types';
import { avrInterrupt } from './interrupt';
@@ -78,6 +79,7 @@ export class CPU implements ICPU {
// This lets the Timer Compare output override GPIO pins:
readonly gpioTimerHooks: CPUMemoryHooks = [];
+ readonly gpioPorts = new Set<AVRIOPort>();
pc: u32 = 0;
cycles: u32 = 0;
diff --git a/src/index.ts b/src/index.ts
index f4ac3aa..01af188 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -18,6 +18,13 @@ export {
AVRIOPort,
GPIOListener,
AVRPortConfig,
+ AVRPinChangeInterrupt,
+ AVRExternalInterrupt,
+ PCINT0,
+ PCINT1,
+ PCINT2,
+ INT0,
+ INT1,
portAConfig,
portBConfig,
portCConfig,
diff --git a/src/peripherals/gpio.spec.ts b/src/peripherals/gpio.spec.ts
index d4b3c7d..692deaa 100644
--- a/src/peripherals/gpio.spec.ts
+++ b/src/peripherals/gpio.spec.ts
@@ -1,9 +1,37 @@
import { CPU } from '../cpu/cpu';
-import { AVRIOPort, portBConfig, PinState } from './gpio';
+import { AVRIOPort, portBConfig, PinState, portDConfig } from './gpio';
+// CPU registers
+const SREG = 95;
+
+// GPIO registers
const PINB = 0x23;
const DDRB = 0x24;
const PORTB = 0x25;
+const EIFR = 0x3c;
+const EIMSK = 0x3d;
+const PCICR = 0x68;
+const EICRA = 0x69;
+const PCIFR = 0x3b;
+const PCMSK0 = 0x6b;
+
+// Register bit names
+const INT0 = 0;
+const ISC00 = 0;
+const ISC01 = 1;
+const PCIE0 = 0;
+const PCINT3 = 3;
+
+// Pin names
+const PB0 = 0;
+const PB1 = 1;
+const PB3 = 3;
+const PB4 = 4;
+const PD2 = 2;
+
+// Interrupt vector addresses
+const PC_INT_INT0 = 2;
+const PC_INT_PCINT0 = 6;
describe('GPIO', () => {
it('should invoke the listeners when the port is written to', () => {
@@ -67,7 +95,7 @@ describe('GPIO', () => {
const port = new AVRIOPort(cpu, portBConfig);
cpu.writeData(DDRB, 0x1);
cpu.writeData(PORTB, 0x1);
- expect(port.pinState(0)).toEqual(PinState.High);
+ expect(port.pinState(PB0)).toEqual(PinState.High);
});
it('should return PinState.Low when the pin set to output and LOW', () => {
@@ -75,13 +103,13 @@ describe('GPIO', () => {
const port = new AVRIOPort(cpu, portBConfig);
cpu.writeData(DDRB, 0x8);
cpu.writeData(PORTB, 0xf7);
- expect(port.pinState(3)).toEqual(PinState.Low);
+ expect(port.pinState(PB3)).toEqual(PinState.Low);
});
it('should return PinState.Input by default (reset state)', () => {
const cpu = new CPU(new Uint16Array(1024));
const port = new AVRIOPort(cpu, portBConfig);
- expect(port.pinState(1)).toEqual(PinState.Input);
+ expect(port.pinState(PB1)).toEqual(PinState.Input);
});
it('should return PinState.InputPullUp when the pin is set to input with pullup', () => {
@@ -89,7 +117,7 @@ describe('GPIO', () => {
const port = new AVRIOPort(cpu, portBConfig);
cpu.writeData(DDRB, 0);
cpu.writeData(PORTB, 0x2);
- expect(port.pinState(1)).toEqual(PinState.InputPullUp);
+ expect(port.pinState(PB1)).toEqual(PinState.InputPullUp);
});
it('should reflect the current port state when called inside a listener', () => {
@@ -97,9 +125,9 @@ describe('GPIO', () => {
const cpu = new CPU(new Uint16Array(1024));
const port = new AVRIOPort(cpu, portBConfig);
const listener = jest.fn(() => {
- expect(port.pinState(0)).toBe(PinState.High);
+ expect(port.pinState(PB0)).toBe(PinState.High);
});
- expect(port.pinState(0)).toBe(PinState.Input);
+ expect(port.pinState(PB0)).toBe(PinState.Input);
cpu.writeData(DDRB, 0x01);
port.addListener(listener);
cpu.writeData(PORTB, 0x01);
@@ -111,9 +139,9 @@ describe('GPIO', () => {
const cpu = new CPU(new Uint16Array(1024));
const port = new AVRIOPort(cpu, portBConfig);
const listener = jest.fn(() => {
- expect(port.pinState(0)).toBe(PinState.Low);
+ expect(port.pinState(PB0)).toBe(PinState.Low);
});
- expect(port.pinState(0)).toBe(PinState.Input);
+ expect(port.pinState(PB0)).toBe(PinState.Input);
port.addListener(listener);
cpu.writeData(DDRB, 0x01);
expect(listener).toHaveBeenCalled();
@@ -125,9 +153,9 @@ describe('GPIO', () => {
const cpu = new CPU(new Uint16Array(1024));
const port = new AVRIOPort(cpu, portBConfig);
cpu.writeData(DDRB, 0);
- port.setPin(4, true);
+ port.setPin(PB4, true);
expect(cpu.data[0x23]).toEqual(0x10);
- port.setPin(4, false);
+ port.setPin(PB4, false);
expect(cpu.data[0x23]).toEqual(0x0);
});
@@ -136,10 +164,146 @@ describe('GPIO', () => {
const port = new AVRIOPort(cpu, portBConfig);
cpu.writeData(DDRB, 0x10);
cpu.writeData(PORTB, 0x0);
- port.setPin(4, true);
+ port.setPin(PB4, true);
expect(cpu.data[PINB]).toEqual(0x0);
cpu.writeData(DDRB, 0x0);
expect(cpu.data[PINB]).toEqual(0x10);
});
});
+
+ describe('External interrupt', () => {
+ it('should generate INT0 interrupt on rising edge', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portDConfig);
+ cpu.writeData(EIMSK, 1 << INT0);
+ cpu.writeData(EICRA, (1 << ISC01) | (1 << ISC00));
+
+ expect(cpu.data[EIFR]).toEqual(0);
+ port.setPin(PD2, true);
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+
+ cpu.data[SREG] = 0x80; // SREG: I------- (enable interrupts)
+ cpu.tick();
+ expect(cpu.pc).toEqual(PC_INT_INT0);
+ expect(cpu.cycles).toEqual(2);
+ expect(cpu.data[EIFR]).toEqual(0);
+
+ port.setPin(PD2, false);
+ expect(cpu.data[EIFR]).toEqual(0);
+ });
+
+ it('should generate INT0 interrupt on falling edge', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portDConfig);
+ cpu.writeData(EIMSK, 1 << INT0);
+ cpu.writeData(EICRA, 1 << ISC01);
+
+ expect(cpu.data[EIFR]).toEqual(0);
+ port.setPin(PD2, true);
+ expect(cpu.data[EIFR]).toEqual(0);
+ port.setPin(PD2, false);
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+
+ cpu.data[SREG] = 0x80; // SREG: I------- (enable interrupts)
+ cpu.tick();
+ expect(cpu.pc).toEqual(PC_INT_INT0);
+ expect(cpu.cycles).toEqual(2);
+ expect(cpu.data[EIFR]).toEqual(0);
+ });
+
+ it('should generate INT0 interrupt on level change', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portDConfig);
+ cpu.writeData(EIMSK, 1 << INT0);
+ cpu.writeData(EICRA, 1 << ISC00);
+
+ expect(cpu.data[EIFR]).toEqual(0);
+ port.setPin(PD2, true);
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+ cpu.writeData(EIFR, 1 << INT0);
+ expect(cpu.data[EIFR]).toEqual(0);
+ port.setPin(PD2, false);
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+ });
+
+ it('should a sticky INT0 interrupt while the pin level is low', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portDConfig);
+ cpu.writeData(EIMSK, 1 << INT0);
+ cpu.writeData(EICRA, 0);
+ expect(cpu.data[EIFR]).toEqual(0);
+
+ port.setPin(PD2, true);
+ expect(cpu.data[EIFR]).toEqual(0);
+
+ port.setPin(PD2, false);
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+
+ // This is a sticky interrupt, verify we can't clear the flag:
+ cpu.writeData(EIFR, 1 << INT0);
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+
+ cpu.data[SREG] = 0x80; // SREG: I------- (enable interrupts)
+ cpu.tick();
+ expect(cpu.pc).toEqual(PC_INT_INT0);
+ expect(cpu.cycles).toEqual(2);
+
+ // Flag shouldn't be cleared, as the interrupt is sticky
+ expect(cpu.data[EIFR]).toEqual(1 << INT0);
+
+ // But it will be cleared as soon as the pin goes high.
+ port.setPin(PD2, true);
+ expect(cpu.data[EIFR]).toEqual(0);
+ });
+ });
+
+ describe('Pin change interrupts (PCINT)', () => {
+ it('should generate a pin change interrupt when PB3 (PCINT3) goes high', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portBConfig);
+ cpu.writeData(PCICR, 1 << PCIE0);
+ cpu.writeData(PCMSK0, 1 << PCINT3);
+
+ port.setPin(PB3, true);
+ expect(cpu.data[PCIFR]).toEqual(1 << PCIE0);
+
+ cpu.data[SREG] = 0x80; // SREG: I-------
+ cpu.tick();
+ expect(cpu.pc).toEqual(PC_INT_PCINT0);
+ expect(cpu.cycles).toEqual(2);
+ expect(cpu.data[PCIFR]).toEqual(0);
+ });
+
+ it('should generate a pin change interrupt when PB3 (PCINT3) goes low', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portBConfig);
+
+ port.setPin(PB3, true);
+ cpu.writeData(PCICR, 1 << PCIE0);
+ cpu.writeData(PCMSK0, 1 << PCINT3);
+ expect(cpu.data[PCIFR]).toEqual(0);
+
+ port.setPin(PB3, false);
+ expect(cpu.data[PCIFR]).toEqual(1 << PCIE0);
+
+ cpu.data[SREG] = 0x80; // SREG: I-------
+ cpu.tick();
+ expect(cpu.pc).toEqual(PC_INT_PCINT0);
+ expect(cpu.cycles).toEqual(2);
+ expect(cpu.data[PCIFR]).toEqual(0);
+ });
+
+ it('should clear the interrupt flag when writing to PCIFR', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const port = new AVRIOPort(cpu, portBConfig);
+ cpu.writeData(PCICR, 1 << PCIE0);
+ cpu.writeData(PCMSK0, 1 << PCINT3);
+
+ port.setPin(PB3, true);
+ expect(cpu.data[PCIFR]).toEqual(1 << PCIE0);
+
+ cpu.writeData(PCIFR, 1 << PCIE0);
+ expect(cpu.data[PCIFR]).toEqual(0);
+ });
+ });
});
diff --git a/src/peripherals/gpio.ts b/src/peripherals/gpio.ts
index 4130c82..01d7da3 100644
--- a/src/peripherals/gpio.ts
+++ b/src/peripherals/gpio.ts
@@ -3,84 +3,175 @@
* Part of AVR8js
* Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf
*
- * Copyright (C) 2019, 2020, Uri Shaked
+ * Copyright (C) 2019, 2020, 2021 Uri Shaked
*/
-import { CPU } from '../cpu/cpu';
+import { AVRInterruptConfig, CPU } from '../cpu/cpu';
import { u8 } from '../types';
+export interface AVRExternalInterrupt {
+ EICRA: u8;
+ EICRB: u8;
+ EIMSK: u8;
+ EIFR: u8;
+ index: u8; // 0..7
+ interrupt: u8;
+}
+
+export interface AVRPinChangeInterrupt {
+ PCIE: u8; // bit index in PCICR/PCIFR
+ PCICR: u8;
+ PCIFR: u8;
+ PCMSK: u8;
+ pinChangeInterrupt: u8;
+ mask: u8;
+ offset: u8;
+}
+
export interface AVRPortConfig {
// Register addresses
PIN: u8;
DDR: u8;
PORT: u8;
+
+ // Interrupt settings
+ pinChange?: AVRPinChangeInterrupt;
+ externalInterrupts: (AVRExternalInterrupt | null)[];
}
+export const INT0: AVRExternalInterrupt = {
+ EICRA: 0x69,
+ EICRB: 0,
+ EIMSK: 0x3d,
+ EIFR: 0x3c,
+ index: 0,
+ interrupt: 2,
+};
+
+export const INT1: AVRExternalInterrupt = {
+ EICRA: 0x69,
+ EICRB: 0,
+ EIMSK: 0x3d,
+ EIFR: 0x3c,
+ index: 1,
+ interrupt: 4,
+};
+
+export const PCINT0 = {
+ PCIE: 0,
+ PCICR: 0x68,
+ PCIFR: 0x3b,
+ PCMSK: 0x6b,
+ pinChangeInterrupt: 6,
+ mask: 0xff,
+ offset: 0,
+};
+
+export const PCINT1 = {
+ PCIE: 1,
+ PCICR: 0x68,
+ PCIFR: 0x3b,
+ PCMSK: 0x6c,
+ pinChangeInterrupt: 8,
+ mask: 0xff,
+ offset: 0,
+};
+
+export const PCINT2 = {
+ PCIE: 2,
+ PCICR: 0x68,
+ PCIFR: 0x3b,
+ PCMSK: 0x6d,
+ pinChangeInterrupt: 10,
+ mask: 0xff,
+ offset: 0,
+};
+
export type GPIOListener = (value: u8, oldValue: u8) => void;
export const portAConfig: AVRPortConfig = {
PIN: 0x20,
DDR: 0x21,
PORT: 0x22,
+ externalInterrupts: [],
};
export const portBConfig: AVRPortConfig = {
PIN: 0x23,
DDR: 0x24,
PORT: 0x25,
+
+ // Interrupt settings
+ pinChange: PCINT0,
+ externalInterrupts: [],
};
export const portCConfig: AVRPortConfig = {
PIN: 0x26,
DDR: 0x27,
PORT: 0x28,
+
+ // Interrupt settings
+ pinChange: PCINT1,
+ externalInterrupts: [],
};
export const portDConfig: AVRPortConfig = {
PIN: 0x29,
DDR: 0x2a,
PORT: 0x2b,
+
+ // Interrupt settings
+ pinChange: PCINT2,
+ externalInterrupts: [null, null, INT0, INT1],
};
export const portEConfig: AVRPortConfig = {
PIN: 0x2c,
DDR: 0x2d,
PORT: 0x2e,
+ externalInterrupts: [],
};
export const portFConfig: AVRPortConfig = {
PIN: 0x2f,
DDR: 0x30,
PORT: 0x31,
+ externalInterrupts: [],
};
export const portGConfig: AVRPortConfig = {
PIN: 0x32,
DDR: 0x33,
PORT: 0x34,
+ externalInterrupts: [],
};
export const portHConfig: AVRPortConfig = {
PIN: 0x100,
DDR: 0x101,
PORT: 0x102,
+ externalInterrupts: [],
};
export const portJConfig: AVRPortConfig = {
PIN: 0x103,
DDR: 0x104,
PORT: 0x105,
+ externalInterrupts: [],
};
export const portKConfig: AVRPortConfig = {
PIN: 0x106,
DDR: 0x107,
PORT: 0x108,
+ externalInterrupts: [],
};
export const portLConfig: AVRPortConfig = {
PIN: 0x109,
DDR: 0x10a,
PORT: 0x10b,
+ externalInterrupts: [],
};
export enum PinState {
@@ -99,15 +190,26 @@ export enum PinOverrideMode {
Toggle,
}
+enum InterruptMode {
+ LowLevel,
+ Change,
+ FallingEdge,
+ RisingEdge,
+}
+
export class AVRIOPort {
+ private readonly externalInts: (AVRInterruptConfig | null)[];
+ private readonly PCINT: AVRInterruptConfig | null;
private listeners: GPIOListener[] = [];
private pinValue: u8 = 0;
private overrideMask: u8 = 0xff;
- private overrideValue: u8;
+ private overrideValue: u8 = 0;
private lastValue: u8 = 0;
private lastDdr: u8 = 0;
+ private lastPin: u8 = 0;
constructor(private cpu: CPU, private portConfig: AVRPortConfig) {
+ cpu.gpioPorts.add(this);
cpu.writeHooks[portConfig.DDR] = (value: u8) => {
const portValue = cpu.data[portConfig.PORT];
cpu.data[portConfig.DDR] = value;
@@ -128,14 +230,14 @@ export class AVRIOPort {
const ddrMask = cpu.data[portConfig.DDR];
const portValue = oldPortValue ^ value;
cpu.data[portConfig.PORT] = portValue;
- cpu.data[portConfig.PIN] = (cpu.data[portConfig.PIN] & ~ddrMask) | (portValue & ddrMask);
+ this.updatePinRegister(portValue, ddrMask);
this.writeGpio(portValue, ddrMask);
return true;
};
// The following hook is used by the timer compare output to override GPIO pins:
cpu.gpioTimerHooks[portConfig.PORT] = (pin: u8, mode: PinOverrideMode) => {
const pinMask = 1 << pin;
- if (mode == PinOverrideMode.None) {
+ if (mode === PinOverrideMode.None) {
this.overrideMask |= pinMask;
} else {
this.overrideMask &= ~pinMask;
@@ -157,6 +259,62 @@ export class AVRIOPort {
}
this.writeGpio(cpu.data[portConfig.PORT], cpu.data[portConfig.DDR]);
};
+
+ // External interrupts
+ const { externalInterrupts } = portConfig;
+ this.externalInts = externalInterrupts.map((externalConfig) =>
+ externalConfig
+ ? {
+ address: externalConfig.interrupt,
+ flagRegister: externalConfig.EIFR,
+ flagMask: 1 << externalConfig.index,
+ enableRegister: externalConfig.EIMSK,
+ enableMask: 1 << externalConfig.index,
+ }
+ : null
+ );
+ const EICRA = externalInterrupts.find((item) => item && item.EICRA)?.EICRA ?? 0;
+ this.attachInterruptHook(EICRA);
+ const EICRB = externalInterrupts.find((item) => item && item.EICRB)?.EICRB ?? 0;
+ this.attachInterruptHook(EICRB);
+ const EIMSK = externalInterrupts.find((item) => item && item.EIMSK)?.EIMSK ?? 0;
+ this.attachInterruptHook(EIMSK, 'mask');
+ const EIFR = externalInterrupts.find((item) => item && item.EIFR)?.EIFR ?? 0;
+ this.attachInterruptHook(EIFR, 'flag');
+
+ // Pin change interrupts
+ const { pinChange } = portConfig;
+ this.PCINT = pinChange
+ ? {
+ address: pinChange.pinChangeInterrupt,
+ flagRegister: pinChange.PCIFR,
+ flagMask: 1 << pinChange.PCIE,
+ enableRegister: pinChange.PCICR,
+ enableMask: 1 << pinChange.PCIE,
+ }
+ : null;
+ if (pinChange) {
+ const { PCIFR, PCMSK } = pinChange;
+ cpu.writeHooks[PCIFR] = (value) => {
+ for (const gpio of this.cpu.gpioPorts) {
+ const { PCINT } = gpio;
+ if (PCINT) {
+ cpu.clearInterruptByFlag(PCINT, value);
+ }
+ }
+ return true;
+ };
+ cpu.writeHooks[PCMSK] = (value) => {
+ cpu.data[PCMSK] = value;
+ for (const gpio of this.cpu.gpioPorts) {
+ const { PCINT } = gpio;
+ if (PCINT) {
+ cpu.updateInterruptEnable(PCINT, value);
+ }
+ }
+ return true;
+ };
+ }
}
addListener(listener: GPIOListener) {
@@ -200,7 +358,117 @@ export class AVRIOPort {
}
private updatePinRegister(port: u8, ddr: u8) {
- this.cpu.data[this.portConfig.PIN] = (this.pinValue & ~ddr) | (port & ddr);
+ const newPin = (this.pinValue & ~ddr) | (port & ddr);
+ this.cpu.data[this.portConfig.PIN] = newPin;
+ if (this.lastPin !== newPin) {
+ for (let index = 0; index < 8; index++) {
+ if ((newPin & (1 << index)) !== (this.lastPin & (1 << index))) {
+ this.toggleInterrupt(index, !!(newPin & (1 << index)));
+ }
+ }
+ this.lastPin = newPin;
+ }
+ }
+
+ private toggleInterrupt(pin: u8, risingEdge: boolean) {
+ const { cpu, portConfig, externalInts, PCINT } = this;
+ const { externalInterrupts, pinChange } = portConfig;
+ const externalConfig = externalInterrupts[pin];
+ const external = externalInts[pin];
+ if (external && externalConfig) {
+ const { index, EICRA, EICRB, EIMSK } = externalConfig;
+ if (cpu.data[EIMSK] & (1 << index)) {
+ const configRegister = index >= 4 ? EICRB : EICRA;
+ const configShift = (index % 4) * 2;
+ const configuration = (cpu.data[configRegister] >> configShift) & 0x3;
+ let generateInterrupt = false;
+ external.constant = false;
+ switch (configuration) {
+ case InterruptMode.LowLevel:
+ generateInterrupt = !risingEdge;
+ external.constant = true;
+ break;
+ case InterruptMode.Change:
+ generateInterrupt = true;
+ break;
+ case InterruptMode.FallingEdge:
+ generateInterrupt = !risingEdge;
+ break;
+ case InterruptMode.RisingEdge:
+ generateInterrupt = risingEdge;
+ break;
+ }
+ if (generateInterrupt) {
+ cpu.setInterruptFlag(external);
+ } else if (external.constant) {
+ cpu.clearInterrupt(external, true);
+ }
+ }
+ }
+
+ if (pinChange && PCINT && pinChange.mask & (1 << pin)) {
+ const { PCMSK } = pinChange;
+ if (cpu.data[PCMSK] & (1 << (pin + pinChange.offset))) {
+ cpu.setInterruptFlag(PCINT);
+ }
+ }
+ }
+
+ private attachInterruptHook(register: number, registerType: 'flag' | 'mask' | 'other' = 'other') {
+ if (!register) {
+ return;
+ }
+
+ const { cpu } = this;
+
+ cpu.writeHooks[register] = (value: u8) => {
+ if (registerType !== 'flag') {
+ cpu.data[register] = value;
+ }
+ for (const gpio of cpu.gpioPorts) {
+ for (const external of gpio.externalInts) {
+ if (external && registerType === 'mask') {
+ cpu.updateInterruptEnable(external, value);
+ }
+ if (external && !external.constant && registerType === 'flag') {
+ cpu.clearInterruptByFlag(external, value);
+ }
+ }
+
+ gpio.checkExternalInterrupts();
+ }
+
+ return true;
+ };
+ }
+
+ private checkExternalInterrupts() {
+ const { cpu } = this;
+ const { externalInterrupts } = this.portConfig;
+ for (let pin = 0; pin < 8; pin++) {
+ const external = externalInterrupts[pin];
+ if (!external) {
+ continue;
+ }
+ const pinValue = !!(this.lastPin & (1 << pin));
+ const { index, EICRA, EICRB, EIMSK, EIFR, interrupt } = external;
+ if (!(cpu.data[EIMSK] & (1 << index)) || pinValue) {
+ continue;
+ }
+ const configRegister = index >= 4 ? EICRB : EICRA;
+ const configShift = (index % 4) * 2;
+ const configuration = (cpu.data[configRegister] >> configShift) & 0x3;
+ if (configuration === InterruptMode.LowLevel) {
+ cpu.queueInterrupt({
+ address: interrupt,
+ flagRegister: EIFR,
+ flagMask: 1 << index,
+ enableRegister: EIMSK,
+ enableMask: 1 << index,
+ constant: true,
+ });
+ }
+ }
}
private writeGpio(value: u8, ddr: u8) {
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.