aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals
diff options
context:
space:
mode:
Diffstat (limited to 'src/peripherals')
-rw-r--r--src/peripherals/eeprom.spec.ts34
-rw-r--r--src/peripherals/eeprom.ts22
-rw-r--r--src/peripherals/spi.spec.ts11
-rw-r--r--src/peripherals/spi.ts22
-rw-r--r--src/peripherals/timer.spec.ts212
-rw-r--r--src/peripherals/timer.ts30
-rw-r--r--src/peripherals/twi.spec.ts7
-rw-r--r--src/peripherals/twi.ts13
-rw-r--r--src/peripherals/usart.spec.ts16
-rw-r--r--src/peripherals/usart.ts17
10 files changed, 173 insertions, 211 deletions
diff --git a/src/peripherals/eeprom.spec.ts b/src/peripherals/eeprom.spec.ts
index d293bd9..8f08e47 100644
--- a/src/peripherals/eeprom.spec.ts
+++ b/src/peripherals/eeprom.spec.ts
@@ -23,11 +23,10 @@ describe('EEPROM', () => {
describe('Reading the EEPROM', () => {
it('should return 0xff when reading from an empty location', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const eeprom = new AVREEPROM(cpu, new EEPROMMemoryBackend(1024));
+ new AVREEPROM(cpu, new EEPROMMemoryBackend(1024));
cpu.writeData(EEARL, 0);
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EERE);
- eeprom.tick();
cpu.tick();
expect(cpu.cycles).toEqual(4);
expect(cpu.data[EEDR]).toEqual(0xff);
@@ -36,12 +35,11 @@ describe('EEPROM', () => {
it('should return the value stored at the given EEPROM address', () => {
const cpu = new CPU(new Uint16Array(0x1000));
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
eepromBackend.memory[0x250] = 0x42;
cpu.writeData(EEARL, 0x50);
cpu.writeData(EEARH, 0x2);
cpu.writeData(EECR, EERE);
- eeprom.tick();
cpu.tick();
expect(cpu.data[EEDR]).toEqual(0x42);
});
@@ -51,13 +49,12 @@ describe('EEPROM', () => {
it('should write a byte to the given EEPROM address', () => {
const cpu = new CPU(new Uint16Array(0x1000));
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
cpu.writeData(EEDR, 0x55);
cpu.writeData(EEARL, 15);
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EEMPE);
cpu.writeData(EECR, EEPE);
- eeprom.tick();
cpu.tick();
expect(cpu.cycles).toEqual(2);
expect(eepromBackend.memory[15]).toEqual(0x55);
@@ -84,10 +81,10 @@ describe('EEPROM', () => {
const cpu = new CPU(program);
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
eepromBackend.memory[9] = 0x0f; // high four bits are cleared
- const runner = new TestProgramRunner(cpu, eeprom);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount);
// EEPROM was 0x0f, and our program wrote 0x55.
@@ -98,7 +95,7 @@ describe('EEPROM', () => {
it('should clear the EEPE bit and fire an interrupt when write has been completed', () => {
const cpu = new CPU(new Uint16Array(0x1000));
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
cpu.writeData(EEDR, 0x55);
cpu.writeData(EEARL, 15);
cpu.writeData(EEARH, 0);
@@ -106,14 +103,12 @@ describe('EEPROM', () => {
cpu.data[SREG] = 0x80; // SREG: I-------
cpu.writeData(EECR, EEPE);
cpu.cycles += 1000;
- eeprom.tick();
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.
- eeprom.tick();
cpu.tick();
expect(eepromBackend.memory[15]).toEqual(0x55);
expect(cpu.data[EECR] & EEPE).toEqual(0);
@@ -123,16 +118,14 @@ describe('EEPROM', () => {
it('should skip the write if EEMPE is clear', () => {
const cpu = new CPU(new Uint16Array(0x1000));
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
cpu.writeData(EEDR, 0x55);
cpu.writeData(EEARL, 15);
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EEMPE);
cpu.cycles = 8; // waiting for more than 4 cycles should clear EEMPE
- eeprom.tick();
cpu.tick();
cpu.writeData(EECR, EEPE);
- eeprom.tick();
cpu.tick();
// Ensure that nothing was written, and EEPE bit is clear
expect(cpu.cycles).toEqual(8);
@@ -143,7 +136,7 @@ describe('EEPROM', () => {
it('should skip the write if another write is already in progress', () => {
const cpu = new CPU(new Uint16Array(0x1000));
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
// Write 0x55 to address 15
cpu.writeData(EEDR, 0x55);
@@ -151,7 +144,6 @@ describe('EEPROM', () => {
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EEMPE);
cpu.writeData(EECR, EEPE);
- eeprom.tick();
cpu.tick();
expect(cpu.cycles).toEqual(2);
@@ -161,7 +153,6 @@ describe('EEPROM', () => {
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EEMPE);
cpu.writeData(EECR, EEPE);
- eeprom.tick();
cpu.tick();
// Ensure that second write didn't happen
@@ -173,7 +164,7 @@ describe('EEPROM', () => {
it('should write two bytes sucessfully', () => {
const cpu = new CPU(new Uint16Array(0x1000));
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
// Write 0x55 to address 15
cpu.writeData(EEDR, 0x55);
@@ -181,13 +172,11 @@ describe('EEPROM', () => {
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EEMPE);
cpu.writeData(EECR, EEPE);
- eeprom.tick();
cpu.tick();
expect(cpu.cycles).toEqual(2);
// wait long enough time for the first write to finish
cpu.cycles += 10000000;
- eeprom.tick();
cpu.tick();
// Write 0x66 to address 16
@@ -196,7 +185,6 @@ describe('EEPROM', () => {
cpu.writeData(EEARH, 0);
cpu.writeData(EECR, EEMPE);
cpu.writeData(EECR, EEPE);
- eeprom.tick();
cpu.tick();
// Ensure both writes took place
@@ -226,10 +214,10 @@ describe('EEPROM', () => {
const cpu = new CPU(program);
const eepromBackend = new EEPROMMemoryBackend(1024);
- const eeprom = new AVREEPROM(cpu, eepromBackend);
+ new AVREEPROM(cpu, eepromBackend);
eepromBackend.memory[9] = 0x22;
- const runner = new TestProgramRunner(cpu, eeprom);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount);
expect(eepromBackend.memory[9]).toEqual(0xff);
diff --git a/src/peripherals/eeprom.ts b/src/peripherals/eeprom.ts
index bc386ca..d492aa7 100644
--- a/src/peripherals/eeprom.ts
+++ b/src/peripherals/eeprom.ts
@@ -95,7 +95,11 @@ export class AVREEPROM {
}
if (eecr & EEMPE) {
- this.writeEnabledCycles = this.cpu.cycles + 4;
+ const eempeCycles = 4;
+ this.writeEnabledCycles = this.cpu.cycles + eempeCycles;
+ this.cpu.addClockEvent(() => {
+ this.cpu.data[EECR] &= ~EEMPE;
+ }, eempeCycles);
}
// Read
@@ -134,6 +138,11 @@ export class AVREEPROM {
}
this.cpu.data[EECR] |= EEPE;
+
+ this.cpu.addClockEvent(() => {
+ this.cpu.setInterruptFlag(this.EER);
+ }, this.writeCompleteCycles - this.cpu.cycles);
+
// When EEPE has been set, the CPU is halted for two cycles before the
// next instruction is executed.
this.cpu.cycles += 2;
@@ -143,15 +152,4 @@ export class AVREEPROM {
return false;
};
}
-
- tick() {
- const { EECR } = this.config;
-
- if (this.writeEnabledCycles && this.cpu.cycles > this.writeEnabledCycles) {
- this.cpu.data[EECR] &= ~EEMPE;
- }
- if (this.writeCompleteCycles && this.cpu.cycles > this.writeCompleteCycles) {
- this.cpu.setInterruptFlag(this.EER);
- }
- }
}
diff --git a/src/peripherals/spi.spec.ts b/src/peripherals/spi.spec.ts
index 8e11b94..635e657 100644
--- a/src/peripherals/spi.spec.ts
+++ b/src/peripherals/spi.spec.ts
@@ -160,7 +160,7 @@ describe('SPI', () => {
return 0x5b; // we copy this byte to
};
- const runner = new TestProgramRunner(cpu, spi);
+ const runner = new TestProgramRunner(cpu);
runner.runToBreak();
// 16 cycles per clock * 8 bits = 128
@@ -172,11 +172,10 @@ describe('SPI', () => {
it('should set the WCOL bit in SPSR if writing to SPDR while SPI is already transmitting', () => {
const cpu = new CPU(new Uint16Array(1024));
- const spi = new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
+ new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
cpu.writeData(SPCR, SPE | MSTR);
cpu.writeData(SPDR, 0x50);
- spi.tick();
cpu.tick();
expect(cpu.readData(SPSR) & WCOL).toEqual(0);
@@ -186,7 +185,7 @@ describe('SPI', () => {
it('should clear the SPIF bit and fire an interrupt when SPI transfer completes', () => {
const cpu = new CPU(new Uint16Array(1024));
- const spi = new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
+ new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
cpu.writeData(SPCR, SPE | SPIE | MSTR);
cpu.writeData(SPDR, 0x50);
@@ -194,13 +193,11 @@ describe('SPI', () => {
// At this point, write shouldn't be complete yet
cpu.cycles += 10;
- spi.tick();
cpu.tick();
expect(cpu.pc).toEqual(0);
// 100 cycles later, it should (8 bits * 8 cycles per bit = 64).
cpu.cycles += 100;
- spi.tick();
cpu.tick();
expect(cpu.data[SPSR] & SPIF).toEqual(0);
expect(cpu.pc).toEqual(0x22); // SPI Ready interrupt
@@ -215,12 +212,10 @@ describe('SPI', () => {
cpu.writeData(SPDR, 0x8f);
cpu.cycles = 10;
- spi.tick();
cpu.tick();
expect(cpu.readData(SPDR)).toEqual(0);
cpu.cycles = 32; // 4 cycles per bit * 8 bits = 32
- spi.tick();
cpu.tick();
expect(cpu.readData(SPDR)).toEqual(0x88);
});
diff --git a/src/peripherals/spi.ts b/src/peripherals/spi.ts
index f67143f..7dd5d43 100644
--- a/src/peripherals/spi.ts
+++ b/src/peripherals/spi.ts
@@ -38,7 +38,7 @@ const bitsPerByte = 8;
export class AVRSPI {
public onTransfer: SPITransferCallback | null = null;
- private transmissionCompleteCycles = 0;
+ private transmissionActive = false;
private receivedByte: u8 = 0;
// Interrupts
@@ -59,7 +59,7 @@ export class AVRSPI {
}
// Write collision
- if (this.transmissionCompleteCycles > this.cpu.cycles) {
+ if (this.transmissionActive) {
cpu.data[SPSR] |= SPSR_WCOL;
return true;
}
@@ -69,7 +69,13 @@ export class AVRSPI {
this.cpu.clearInterrupt(this.SPI);
this.receivedByte = this.onTransfer?.(value) ?? 0;
- this.transmissionCompleteCycles = this.cpu.cycles + this.clockDivider * bitsPerByte;
+ const cyclesToComplete = this.clockDivider * bitsPerByte;
+ this.transmissionActive = true;
+ this.cpu.addClockEvent(() => {
+ this.cpu.data[SPDR] = this.receivedByte;
+ this.cpu.setInterruptFlag(this.SPI);
+ this.transmissionActive = false;
+ }, cyclesToComplete);
return true;
};
cpu.writeHooks[SPSR] = (value: u8) => {
@@ -78,13 +84,9 @@ export class AVRSPI {
};
}
- tick() {
- if (this.transmissionCompleteCycles && this.cpu.cycles >= this.transmissionCompleteCycles) {
- const { SPDR } = this.config;
- this.cpu.data[SPDR] = this.receivedByte;
- this.cpu.setInterruptFlag(this.SPI);
- this.transmissionCompleteCycles = 0;
- }
+ reset() {
+ this.transmissionActive = false;
+ this.receivedByte = 0;
}
get isMaster() {
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts
index 6b6a9eb..40db1f6 100644
--- a/src/peripherals/timer.spec.ts
+++ b/src/peripherals/timer.spec.ts
@@ -25,6 +25,7 @@ const TCNT0 = 0x46;
const OCR0A = 0x47;
const OCR0B = 0x48;
const TIMSK0 = 0x6e;
+const TIMSK1 = 0x6f;
// Timer 1 Registers
const TIFR1 = 0x36;
@@ -66,12 +67,11 @@ const nopOpCode = '0000';
describe('timer', () => {
it('should update timer every tick when prescaler is 1', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(1);
@@ -79,12 +79,11 @@ describe('timer', () => {
it('should update timer every 64 ticks when prescaler is 3', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCCR0B, CS01 | CS00); // Set prescaler to 64
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.cycles = 64;
- timer.tick();
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(1);
@@ -92,10 +91,11 @@ describe('timer', () => {
it('should not update timer if it has been disabled', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCCR0B, 0); // No prescaler (timer disabled)
+ cpu.cycles = 1;
+ cpu.tick();
cpu.cycles = 100000;
- timer.tick();
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0); // TCNT should stay 0
@@ -103,13 +103,12 @@ describe('timer', () => {
it('should set TOV if timer overflows', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0);
@@ -118,13 +117,12 @@ describe('timer', () => {
it('should clear the TOV flag when writing 1 to the TOV bit, and not trigger the interrupt', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
expect(cpu.data[TIFR0]).toEqual(TOV0);
cpu.writeData(TIFR0, TOV0);
@@ -133,15 +131,14 @@ describe('timer', () => {
it('should set TOV if timer overflows in FAST PWM mode', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.writeData(OCR0A, 0x7f);
cpu.writeData(TCCR0A, WGM01 | WGM00); // WGM: Fast PWM
- cpu.cycles = 1;
- timer.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0);
@@ -150,26 +147,25 @@ describe('timer', () => {
it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.data[TIMSK0] = TOIE0;
cpu.data[SREG] = 0x80; // SREG: I-------
- cpu.cycles = 1;
- timer.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(2); // TCNT should be 2 (one tick above + 2 cycles for interrupt)
expect(cpu.data[TIFR0] & TOV0).toEqual(0);
expect(cpu.pc).toEqual(0x20);
- expect(cpu.cycles).toEqual(3);
+ expect(cpu.cycles).toEqual(4);
});
it('should support overriding TIFR/TOV and TIMSK/TOIE bits (issue #64)', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, {
+ new AVRTimer(cpu, {
...timer0Config,
// The following values correspond ATtiny85 config:
@@ -182,163 +178,154 @@ describe('timer', () => {
});
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.data[TIMSK0] = 2;
cpu.data[SREG] = 0x80; // SREG: I-------
- cpu.cycles = 1;
- timer.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(2); // TCNT should be 2 (one tick above + 2 cycles for interrupt)
expect(cpu.data[TIFR0] & 2).toEqual(0);
expect(cpu.pc).toEqual(0x20);
- expect(cpu.cycles).toEqual(3);
+ expect(cpu.cycles).toEqual(4);
});
it('should not generate an overflow interrupt when global interrupts disabled', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.data[TIMSK0] = TOIE0;
cpu.data[SREG] = 0x0; // SREG: --------
- cpu.cycles = 1;
- timer.tick();
+ cpu.cycles = 2;
cpu.tick();
expect(cpu.data[TIFR0]).toEqual(TOV0);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should not generate an overflow interrupt when TOIE0 is clear', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.data[TIMSK0] = 0;
cpu.data[SREG] = 0x80; // SREG: I-------
- cpu.cycles = 1;
- timer.tick();
+ cpu.cycles = 2;
cpu.tick();
expect(cpu.data[TIFR0]).toEqual(TOV0);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should set OCF0A flag when timer equals OCRA', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x10);
cpu.writeData(OCR0A, 0x11);
cpu.writeData(TCCR0A, 0x0); // WGM: Normal
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
expect(cpu.data[TIFR0]).toEqual(OCF0A);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should clear the timer in CTC mode if it equals to OCRA', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x10);
cpu.writeData(OCR0A, 0x11);
cpu.writeData(TCCR0A, WGM01); // WGM: CTC
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should set OCF0B flag when timer equals OCRB', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x10);
cpu.writeData(OCR0B, 0x11);
cpu.writeData(TCCR0A, 0x0); // WGM: (Normal)
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
expect(cpu.data[TIFR0]).toEqual(OCF0B);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should generate Timer Compare A interrupt when TCNT0 == TCNTA', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x20);
cpu.writeData(OCR0A, 0x21);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
cpu.writeData(TIMSK0, OCIE0A);
cpu.writeData(95, 0x80); // SREG: I-------
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0x23); // TCNT should be 0x23 (one tick above + 2 cycles for interrupt)
expect(cpu.data[TIFR0] & OCF0A).toEqual(0);
expect(cpu.pc).toEqual(0x1c);
- expect(cpu.cycles).toEqual(3);
+ expect(cpu.cycles).toEqual(4);
});
it('should not generate Timer Compare A interrupt when OCIEA is disabled', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x20);
cpu.writeData(OCR0A, 0x21);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
cpu.writeData(TIMSK0, 0);
cpu.writeData(95, 0x80); // SREG: I-------
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0x21);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should generate Timer Compare B interrupt when TCNT0 == TCNTB', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x20);
cpu.writeData(OCR0B, 0x21);
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
cpu.writeData(TIMSK0, OCIE0B);
cpu.writeData(95, 0x80); // SREG: I-------
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
const tcnt = cpu.readData(TCNT0);
expect(tcnt).toEqual(0x23); // TCNT should be 0x23 (0x23 + 2 cycles for interrupt)
expect(cpu.data[TIFR0] & OCF0B).toEqual(0);
expect(cpu.pc).toEqual(0x1e);
- expect(cpu.cycles).toEqual(3);
+ expect(cpu.cycles).toEqual(4);
});
it('should not increment TCNT on the same cycle of TCNT write (issue #36)', () => {
@@ -353,26 +340,24 @@ describe('timer', () => {
IN r17, 0x26 ; r17 <- TCNT
`);
const cpu = new CPU(program);
- const timer = new AVRTimer(cpu, timer0Config);
- const runner = new TestProgramRunner(cpu, timer);
+ new AVRTimer(cpu, timer0Config);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount);
expect(cpu.data[R17]).toEqual(0x31);
});
it('timer2 should count every 256 ticks when prescaler is 6 (issue #5)', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer2Config);
+ new AVRTimer(cpu, timer2Config);
cpu.writeData(TCCR2B, CS22 | CS21); // Set prescaler to 256
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
cpu.cycles = 511;
- timer.tick();
cpu.tick();
expect(cpu.readData(TCNT2)).toEqual(1);
cpu.cycles = 512;
- timer.tick();
cpu.tick();
expect(cpu.readData(TCNT2)).toEqual(2);
});
@@ -387,8 +372,8 @@ describe('timer', () => {
LDS r1, 0x46 ; r1 <- TCNT0 (2 cycles)
`);
const cpu = new CPU(program);
- const timer = new AVRTimer(cpu, timer0Config);
- const runner = new TestProgramRunner(cpu, timer);
+ new AVRTimer(cpu, timer0Config);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount);
expect(cpu.data[R1]).toEqual(2);
});
@@ -405,8 +390,8 @@ describe('timer', () => {
LDS r17, 0xb2 ; TCNT should equal 2 at this point
`);
const cpu = new CPU(program);
- const timer = new AVRTimer(cpu, timer2Config);
- const runner = new TestProgramRunner(cpu, timer);
+ new AVRTimer(cpu, timer2Config);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount);
expect(cpu.readData(R17)).toEqual(2);
});
@@ -432,8 +417,8 @@ describe('timer', () => {
IN r22, 0x26 ; TCNT0 will be 1 (end of test)
`);
const cpu = new CPU(program);
- const timer = new AVRTimer(cpu, timer0Config);
- const runner = new TestProgramRunner(cpu, timer);
+ new AVRTimer(cpu, timer0Config);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount);
expect(cpu.readData(R17)).toEqual(2);
@@ -463,14 +448,14 @@ describe('timer', () => {
`);
const cpu = new CPU(program);
- const timer = new AVRTimer(cpu, timer0Config);
+ new AVRTimer(cpu, timer0Config);
// Listen to Port D's internal callback
const gpioCallback = jest.fn();
cpu.gpioTimerHooks[PORTD] = gpioCallback;
const nopCount = lines.filter((line) => line.bytes == nopOpCode).length;
- const runner = new TestProgramRunner(cpu, timer);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount - nopCount);
expect(cpu.readData(TCNT0)).toEqual(0xfd);
@@ -495,7 +480,7 @@ describe('timer', () => {
describe('16 bit timers', () => {
it('should increment 16-bit TCNT by 1', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer1Config);
+ new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x22); // TCNT1 <- 0x2233
cpu.writeData(TCNT1, 0x33); // ...
const timerLow = cpu.readData(TCNT1);
@@ -503,10 +488,9 @@ describe('timer', () => {
expect((timerHigh << 8) | timerLow).toEqual(0x2233);
cpu.writeData(TCCR1A, 0x0); // WGM: Normal
cpu.writeData(TCCR1B, CS10); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
cpu.readData(TCNT1);
expect(cpu.dataView.getUint16(TCNT1, true)).toEqual(0x2234); // TCNT1 should increment
@@ -514,69 +498,68 @@ describe('timer', () => {
it('should set OCF0A flag when timer equals OCRA (16 bit mode)', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer1Config);
+ new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x10); // TCNT1 <- 0x10ee
cpu.writeData(TCNT1, 0xee); // ...
cpu.writeData(OCR1AH, 0x10); // OCR1 <- 0x10ef
cpu.writeData(OCR1A, 0xef); // ...
cpu.writeData(TCCR1A, 0x0); // WGM: Normal
cpu.writeData(TCCR1B, CS10); // Set prescaler to 1
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
expect(cpu.data[TIFR1]).toEqual(OCF1A); // TIFR1 should have OCF1A bit on
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(1);
+ expect(cpu.cycles).toEqual(2);
});
it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer1Config);
- cpu.writeData(TCNT1H, 0x3); // TCNT1 <- 0x3ff
- cpu.writeData(TCNT1, 0xff); // ...
+ new AVRTimer(cpu, timer1Config);
cpu.writeData(TCCR1A, 0x3); // TCCR1A <- WGM10 | WGM11 (Fast PWM, 10-bit)
cpu.writeData(TCCR1B, 0x9); // TCCR1B <- WGM12 | CS10
- cpu.data[0x6f] = 0x1; // TIMSK1: TOIE1
+ cpu.writeData(TIMSK1, 0x1); // TIMSK1: TOIE1
cpu.data[SREG] = 0x80; // SREG: I-------
- timer.tick();
- cpu.tick();
+ cpu.writeData(TCNT1H, 0x3); // TCNT1 <- 0x3ff
cpu.cycles = 1;
- timer.tick();
cpu.tick();
+ cpu.writeData(TCNT1, 0xff); // ...
+ cpu.cycles++; // This cycle shouldn't be counted
+ cpu.tick();
+ cpu.cycles++;
+ cpu.tick(); // This is where we cause the overflow
cpu.readData(TCNT1); // Refresh TCNT1
expect(cpu.dataView.getUint16(TCNT1, true)).toEqual(2);
expect(cpu.data[TIFR1] & TOV1).toEqual(0);
expect(cpu.pc).toEqual(0x1a);
- expect(cpu.cycles).toEqual(3);
+ expect(cpu.cycles).toEqual(5);
});
it('should reset the timer once it reaches ICR value in mode 12', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer1Config);
+ new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x50); // TCNT1 <- 0x500f
cpu.writeData(TCNT1, 0x0f); // ...
cpu.writeData(ICR1H, 0x50); // ICR1 <- 0x5010
cpu.writeData(ICR1, 0x10); // ...
cpu.writeData(TCCR1B, WGM13 | WGM12 | CS10); // Set prescaler to 1, WGM: CTC
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
- cpu.cycles = 2; // 2 cycles should increment timer twice, beyond ICR1
- timer.tick();
+ cpu.cycles = 3; // 2 cycles should increment timer twice, beyond ICR1
cpu.tick();
cpu.readData(TCNT1); // Refresh TCNT1
expect(cpu.dataView.getUint16(TCNT1, true)).toEqual(0); // TCNT should be 0
expect(cpu.data[TIFR1] & TOV1).toEqual(0);
- expect(cpu.cycles).toEqual(2);
+ expect(cpu.cycles).toEqual(3);
});
it('should not update the high byte of TCNT if written after the low byte (issue #37)', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer1Config);
+ new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1, 0x22);
cpu.writeData(TCNT1H, 0x55);
- timer.tick();
+ cpu.cycles = 1;
cpu.tick();
const timerLow = cpu.readData(TCNT1);
const timerHigh = cpu.readData(TCNT1H);
@@ -585,14 +568,13 @@ describe('timer', () => {
it('reading from TCNT1H before TCNT1L should return old value (issue #37)', () => {
const cpu = new CPU(new Uint16Array(0x1000));
- const timer = new AVRTimer(cpu, timer1Config);
+ new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0xff);
cpu.writeData(TCNT1, 0xff);
cpu.writeData(TCCR1B, WGM12 | CS10); // Set prescaler to 1, WGM: CTC
- timer.tick();
- cpu.tick();
cpu.cycles = 1;
- timer.tick();
+ cpu.tick();
+ cpu.cycles = 2;
cpu.tick();
// We read the high byte before the low byte, so the high byte should still have
// the previous value:
@@ -622,14 +604,14 @@ describe('timer', () => {
`);
const cpu = new CPU(program);
- const timer = new AVRTimer(cpu, timer1Config);
+ new AVRTimer(cpu, timer1Config);
// Listen to Port B's internal callback
const gpioCallback = jest.fn();
cpu.gpioTimerHooks[PORTB] = gpioCallback;
const nopCount = lines.filter((line) => line.bytes == nopOpCode).length;
- const runner = new TestProgramRunner(cpu, timer);
+ const runner = new TestProgramRunner(cpu);
runner.runInstructions(instructionCount - nopCount);
expect(cpu.readData(TCNT1)).toEqual(0x49);
diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts
index 81c1e47..b8a6668 100644
--- a/src/peripherals/timer.ts
+++ b/src/peripherals/timer.ts
@@ -245,6 +245,7 @@ export class AVRTimer {
private timerMode: TimerMode;
private topValue: TimerTopValue;
private tcnt: u16 = 0;
+ private tcntNext: u16 = 0;
private compA: CompBitsValue;
private compB: CompBitsValue;
private tcntUpdated = false;
@@ -280,7 +281,7 @@ export class AVRTimer {
constructor(private cpu: CPU, private config: AVRTimerConfig) {
this.updateWGMConfig();
this.cpu.readHooks[config.TCNT] = (addr: u8) => {
- this.tick();
+ this.count(false);
if (this.config.bits === 16) {
this.cpu.data[addr + 1] = this.tcnt >> 8;
}
@@ -288,9 +289,10 @@ export class AVRTimer {
};
this.cpu.writeHooks[config.TCNT] = (value: u8) => {
- this.tcnt = (this.highByteTemp << 8) | value;
+ this.tcntNext = (this.highByteTemp << 8) | value;
this.countingUp = true;
this.tcntUpdated = true;
+ this.cpu.updateClockEvent(this.count, 0);
this.timerUpdated();
};
this.cpu.writeHooks[config.OCRA] = (value: u8) => {
@@ -324,8 +326,10 @@ export class AVRTimer {
};
cpu.writeHooks[config.TCCRB] = (value) => {
this.cpu.data[config.TCCRB] = value;
- this.tcntUpdated = true;
this.divider = this.config.dividers[this.CS];
+ this.reschedule();
+ this.tcntUpdated = true;
+ this.cpu.updateClockEvent(this.count, 0);
this.updateWGMConfig();
return true;
};
@@ -348,6 +352,11 @@ export class AVRTimer {
this.lastCycle = 0;
this.ocrA = 0;
this.ocrB = 0;
+ this.icr = 0;
+ this.tcnt = 0;
+ this.tcntNext = 0;
+ this.tcntUpdated = false;
+ this.countingUp = false;
}
get TCCRA() {
@@ -389,7 +398,7 @@ export class AVRTimer {
this.topValue = topValue;
}
- tick() {
+ count = (reschedule = true) => {
const { divider, lastCycle } = this;
const delta = this.cpu.cycles - lastCycle;
if (divider && delta >= divider) {
@@ -411,7 +420,18 @@ export class AVRTimer {
this.cpu.setInterruptFlag(this.OVF);
}
}
- this.tcntUpdated = false;
+ if (this.tcntUpdated) {
+ this.tcnt = this.tcntNext;
+ this.tcntUpdated = false;
+ }
+ if (reschedule) {
+ this.cpu.addClockEvent(this.count, this.lastCycle + divider - this.cpu.cycles);
+ }
+ };
+
+ private reschedule() {
+ this.cpu.clearClockEvent(this.count);
+ this.cpu.addClockEvent(this.count, this.lastCycle + this.divider - this.cpu.cycles);
}
private phasePwmCount(value: u16, delta: u8) {
diff --git a/src/peripherals/twi.spec.ts b/src/peripherals/twi.spec.ts
index e43ae38..2628b57 100644
--- a/src/peripherals/twi.spec.ts
+++ b/src/peripherals/twi.spec.ts
@@ -51,7 +51,6 @@ describe('TWI', () => {
cpu.writeData(TWCR, TWIE);
cpu.data[SREG] = 0x80; // SREG: I-------
twi.completeStart(); // This will set the TWINT flag
- twi.tick();
cpu.tick();
expect(cpu.pc).toEqual(0x30); // 2-wire Serial Interface Vector
expect(cpu.cycles).toEqual(2);
@@ -64,7 +63,7 @@ describe('TWI', () => {
const twi = new AVRTWI(cpu, twiConfig, FREQ_16MHZ);
jest.spyOn(twi.eventHandler, 'start');
cpu.writeData(TWCR, TWINT | TWSTA | TWEN);
- twi.tick();
+ cpu.cycles++;
cpu.tick();
expect(twi.eventHandler.start).toHaveBeenCalledWith(false);
});
@@ -173,7 +172,7 @@ describe('TWI', () => {
`);
const cpu = new CPU(program);
const twi = new AVRTWI(cpu, twiConfig, FREQ_16MHZ);
- const runner = new TestProgramRunner(cpu, twi, onTestBreak);
+ const runner = new TestProgramRunner(cpu, onTestBreak);
twi.eventHandler = {
start: jest.fn(),
stop: jest.fn(),
@@ -342,7 +341,7 @@ describe('TWI', () => {
`);
const cpu = new CPU(program);
const twi = new AVRTWI(cpu, twiConfig, FREQ_16MHZ);
- const runner = new TestProgramRunner(cpu, twi, onTestBreak);
+ const runner = new TestProgramRunner(cpu, onTestBreak);
twi.eventHandler = {
start: jest.fn(),
stop: jest.fn(),
diff --git a/src/peripherals/twi.ts b/src/peripherals/twi.ts
index 914b67f..000bd2a 100644
--- a/src/peripherals/twi.ts
+++ b/src/peripherals/twi.ts
@@ -95,8 +95,6 @@ export class NoopTWIEventHandler implements TWIEventHandler {
export class AVRTWI {
public eventHandler: TWIEventHandler = new NoopTWIEventHandler(this);
- private nextTick: (() => void) | null = null;
-
// Interrupts
private TWI: AVRInterruptConfig = {
address: this.config.twiInterrupt,
@@ -116,7 +114,7 @@ export class AVRTWI {
const { status } = this;
if (clearInt && value & TWCR_TWEN) {
const twdrValue = this.cpu.data[this.config.TWDR];
- this.nextTick = () => {
+ this.cpu.addClockEvent(() => {
if (value & TWCR_TWSTA) {
this.eventHandler.start(status !== STATUS_TWI_IDLE);
} else if (value & TWCR_TWSTO) {
@@ -129,19 +127,12 @@ export class AVRTWI {
const ack = !!(value & TWCR_TWEA);
this.eventHandler.readByte(ack);
}
- };
+ }, 0);
return true;
}
};
}
- tick() {
- if (this.nextTick) {
- this.nextTick();
- this.nextTick = null;
- }
- }
-
get prescaler() {
switch (this.cpu.data[this.config.TWSR] & TWSR_TWPS_MASK) {
case 0:
diff --git a/src/peripherals/usart.spec.ts b/src/peripherals/usart.spec.ts
index c1e1570..fb56967 100644
--- a/src/peripherals/usart.spec.ts
+++ b/src/peripherals/usart.spec.ts
@@ -146,10 +146,9 @@ describe('USART', () => {
describe('tick()', () => {
it('should trigger data register empty interrupt if UDRE is set', () => {
const cpu = new CPU(new Uint16Array(1024));
- const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
cpu.writeData(UCSR0B, UDRIE | TXEN);
cpu.data[SREG] = 0x80; // SREG: I-------
- usart.tick();
cpu.tick();
expect(cpu.pc).toEqual(PC_INT_UDRE);
expect(cpu.cycles).toEqual(2);
@@ -158,12 +157,11 @@ describe('USART', () => {
it('should trigger data TX Complete interrupt if TXCIE is set', () => {
const cpu = new CPU(new Uint16Array(1024));
- const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
cpu.writeData(UCSR0B, TXCIE | TXEN);
cpu.writeData(UDR0, 0x61);
cpu.data[SREG] = 0x80; // SREG: I-------
cpu.cycles = 1e6;
- usart.tick();
cpu.tick();
expect(cpu.pc).toEqual(PC_INT_TXC);
expect(cpu.cycles).toEqual(1e6 + 2);
@@ -172,10 +170,9 @@ describe('USART', () => {
it('should not trigger data TX Complete interrupt if UDR was not written to', () => {
const cpu = new CPU(new Uint16Array(1024));
- const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
cpu.writeData(UCSR0B, TXCIE | TXEN);
cpu.data[SREG] = 0x80; // SREG: I-------
- usart.tick();
cpu.tick();
expect(cpu.pc).toEqual(0);
expect(cpu.cycles).toEqual(0);
@@ -183,12 +180,11 @@ describe('USART', () => {
it('should not trigger any interrupt if interrupts are disabled', () => {
const cpu = new CPU(new Uint16Array(1024));
- const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
cpu.writeData(UCSR0B, UDRIE | TXEN);
cpu.writeData(UDR0, 0x61);
cpu.data[SREG] = 0; // SREG: 0 (disable interrupts)
cpu.cycles = 1e6;
- usart.tick();
cpu.tick();
expect(cpu.pc).toEqual(0);
expect(cpu.cycles).toEqual(1e6);
@@ -242,16 +238,14 @@ describe('USART', () => {
describe('integration', () => {
it('should set the TXC bit after ~1.04mS when baud rate set to 9600', () => {
const cpu = new CPU(new Uint16Array(1024));
- const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
cpu.writeData(UCSR0B, TXEN);
cpu.writeData(UBRR0L, 103); // baud: 9600
cpu.writeData(UDR0, 0x48); // 'H'
cpu.cycles += 16000; // 1ms
- usart.tick();
cpu.tick();
expect(cpu.data[UCSR0A] & TXC).toEqual(0);
cpu.cycles += 800; // 0.05ms
- usart.tick();
cpu.tick();
expect(cpu.data[UCSR0A] & TXC).toEqual(TXC);
});
diff --git a/src/peripherals/usart.ts b/src/peripherals/usart.ts
index c1441ad..c7dcbd4 100644
--- a/src/peripherals/usart.ts
+++ b/src/peripherals/usart.ts
@@ -87,8 +87,6 @@ export class AVRUSART {
enableMask: UCSRB_TXCIE,
};
- private txCompleteCycles = 0;
-
constructor(private cpu: CPU, private config: USARTConfig, private freqMHz: number) {
this.reset();
this.cpu.writeHooks[config.UCSRA] = (value) => {
@@ -119,7 +117,11 @@ export class AVRUSART {
}
}
const symbolsPerChar = 1 + this.bitsPerChar + this.stopBits + (this.parityEnabled ? 1 : 0);
- this.txCompleteCycles = this.cpu.cycles + (this.UBRR * this.multiplier + 1) * symbolsPerChar;
+ const cyclesToComplete = (this.UBRR * this.multiplier + 1) * symbolsPerChar;
+ this.cpu.addClockEvent(() => {
+ cpu.setInterruptFlag(this.UDRE);
+ cpu.setInterruptFlag(this.TXC);
+ }, cyclesToComplete);
this.cpu.clearInterrupt(this.TXC);
this.cpu.clearInterrupt(this.UDRE);
};
@@ -131,15 +133,6 @@ export class AVRUSART {
this.cpu.data[this.config.UCSRC] = UCSRC_UCSZ1 | UCSRC_UCSZ0; // default: 8 bits per byte
}
- tick() {
- const { txCompleteCycles, cpu } = this;
- if (txCompleteCycles && cpu.cycles >= txCompleteCycles) {
- cpu.setInterruptFlag(this.UDRE);
- cpu.setInterruptFlag(this.TXC);
- this.txCompleteCycles = 0;
- }
- }
-
private get UBRR() {
return (this.cpu.data[this.config.UBRRH] << 8) | this.cpu.data[this.config.UBRRL];
}
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.