diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts index 2675d1d..fe3bf63 100644 --- a/src/cpu/cpu.ts +++ b/src/cpu/cpu.ts @@ -141,9 +141,10 @@ export class CPU { } updateInterruptEnable(interrupt: AVRInterruptConfig, registerValue: u8) { - const { enableMask, flagRegister, flagMask } = interrupt; + const { enableMask, flagRegister, flagMask, inverseFlag } = interrupt; if (registerValue & enableMask) { - if (this.data[flagRegister] & flagMask) { + const bitSet = this.data[flagRegister] & flagMask; + if (inverseFlag ? !bitSet : bitSet) { this.queueInterrupt(interrupt); } } else { diff --git a/src/peripherals/eeprom.spec.ts b/src/peripherals/eeprom.spec.ts index 8f08e47..a90116f 100644 --- a/src/peripherals/eeprom.spec.ts +++ b/src/peripherals/eeprom.spec.ts @@ -99,7 +99,30 @@ describe('EEPROM', () => { cpu.writeData(EEDR, 0x55); cpu.writeData(EEARL, 15); cpu.writeData(EEARH, 0); - cpu.writeData(EECR, EEMPE | EERIE); + cpu.writeData(EECR, EEMPE); + cpu.data[SREG] = 0x80; // SREG: I------- + cpu.writeData(EECR, EEPE | EERIE); + cpu.cycles += 1000; + cpu.tick(); + // At this point, write shouldn't be complete yet + expect(cpu.data[EECR] & EEPE).toEqual(EEPE); + expect(cpu.pc).toEqual(0); + cpu.cycles += 10000000; + // And now, 10 million cycles later, it should. + cpu.tick(); + expect(eepromBackend.memory[15]).toEqual(0x55); + expect(cpu.data[EECR] & EEPE).toEqual(0); + expect(cpu.pc).toEqual(0x2c); // EEPROM Ready interrupt + }); + + it('should clear the fire an interrupt when there is a pending interrupt and the interrupt flag is enabled (issue #110)', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eepromBackend = new EEPROMMemoryBackend(1024); + new AVREEPROM(cpu, eepromBackend); + cpu.writeData(EEDR, 0x55); + cpu.writeData(EEARL, 15); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EEMPE); cpu.data[SREG] = 0x80; // SREG: I------- cpu.writeData(EECR, EEPE); cpu.cycles += 1000; @@ -112,6 +135,8 @@ describe('EEPROM', () => { cpu.tick(); expect(eepromBackend.memory[15]).toEqual(0x55); expect(cpu.data[EECR] & EEPE).toEqual(0); + cpu.writeData(EECR, EERIE); + cpu.tick(); expect(cpu.pc).toEqual(0x2c); // EEPROM Ready interrupt }); diff --git a/src/peripherals/eeprom.ts b/src/peripherals/eeprom.ts index 8056847..045ffe8 100644 --- a/src/peripherals/eeprom.ts +++ b/src/peripherals/eeprom.ts @@ -58,6 +58,7 @@ const EEMPE = 1 << 2; const EERIE = 1 << 3; const EEPM0 = 1 << 4; const EEPM1 = 1 << 5; +const EECR_WRITE_MASK = EEPE | EEMPE | EERIE | EEPM0 | EEPM1; export class AVREEPROM { /** @@ -91,6 +92,9 @@ export class AVREEPROM { const addr = (this.cpu.data[EEARH] << 8) | this.cpu.data[EEARL]; + this.cpu.data[EECR] = (this.cpu.data[EECR] & ~EECR_WRITE_MASK) | (eecr & EECR_WRITE_MASK); + this.cpu.updateInterruptEnable(this.EER, eecr); + if (eecr & EERE) { this.cpu.clearInterrupt(this.EER); } @@ -116,6 +120,7 @@ export class AVREEPROM { if (eecr & EEPE) { // If EEMPE is zero, setting EEPE will have no effect. if (this.cpu.cycles >= this.writeEnabledCycles) { + this.cpu.data[EECR] &= ~EEPE; return true; } // Check for write-in-progress @@ -147,10 +152,9 @@ export class AVREEPROM { // When EEPE has been set, the CPU is halted for two cycles before the // next instruction is executed. this.cpu.cycles += 2; - return true; } - return false; + return true; }; } }