diff options
Diffstat (limited to 'src/timer.ts')
| -rw-r--r-- | src/timer.ts | 84 |
1 files changed, 78 insertions, 6 deletions
diff --git a/src/timer.ts b/src/timer.ts index 7203574..1406eda 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -20,6 +20,19 @@ const dividers = { 7: 0 // TODO: External clock source on T0 pin. Clock on rising edge. }; +const WGM_NORMAL = 0; +const WGM_PWM_PHASE_CORRECT = 1; +const WGM_CTC = 2; +const WGM_FASTPWM = 3; + +const TOV = 1; +const OCFA = 2; +const OCFB = 4; + +const TOIE = 1; +const OCIEA = 2; +const OCIEB = 4; + type u8 = number; interface AVRTimerConfig { @@ -95,8 +108,29 @@ export const timer2Config: AVRTimerConfig = { export class AVRTimer { private mask = (1 << this.config.bits) - 1; private lastCycle = 0; + private ocrA: u8 = 0; + private ocrB: u8 = 0; + + constructor(private cpu: CPU, private config: AVRTimerConfig) { + cpu.writeHooks[config.TCNT] = (value: u8) => { + this.TCNT = value; + this.timerUpdated(value); + return true; + }; + cpu.writeHooks[config.OCRA] = (value: u8) => { + // TODO implement buffering when timer running in PWM mode + this.ocrA = value; + }; + cpu.writeHooks[config.OCRB] = (value: u8) => { + this.ocrB = value; + }; + } - constructor(private cpu: CPU, private config: AVRTimerConfig) {} + reset() { + this.lastCycle = 0; + this.ocrA = 0; + this.ocrB = 0; + } get TIFR() { return this.cpu.data[this.config.TIFR]; @@ -114,6 +148,10 @@ export class AVRTimer { this.cpu.data[this.config.TCNT] = value; } + get TCCRA() { + return this.cpu.data[this.config.TCCRA]; + } + get TCCRB() { return this.cpu.data[this.config.TCCRB]; } @@ -126,6 +164,10 @@ export class AVRTimer { return (this.TCCRB & 0x7) as 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; } + get WGM() { + return ((this.TCCRB & 0x8) >> 1) | (this.TCCRA & 0x3); + } + tick() { const divider = dividers[this.CS]; const delta = this.cpu.cycles - this.lastCycle; @@ -135,13 +177,43 @@ export class AVRTimer { const val = this.TCNT; const newVal = (val + counterDelta) & this.mask; this.TCNT = newVal; - if (val > newVal) { - this.TIFR |= 1; // TOV + this.timerUpdated(newVal); + if ( + (this.WGM === WGM_NORMAL || + this.WGM === WGM_PWM_PHASE_CORRECT || + this.WGM === WGM_FASTPWM) && + val > newVal + ) { + this.TIFR |= TOV; + } + } + if (this.cpu.interruptsEnabled) { + if (this.TIFR & TOV && this.TIMSK & TOIE) { + avrInterrupt(this.cpu, this.config.ovfInterrupt); + this.TIFR &= ~TOV; + } + if (this.TIFR & OCFA && this.TIMSK & OCIEA) { + avrInterrupt(this.cpu, this.config.compAInterrupt); + this.TIFR &= ~OCFA; + } + if (this.TIFR & OCFB && this.TIMSK & OCIEB) { + avrInterrupt(this.cpu, this.config.compBInterrupt); + this.TIFR &= ~OCFB; + } + } + } + + private timerUpdated(value: u8) { + if (this.ocrA && value === this.ocrA) { + this.TIFR |= OCFA; + if (this.WGM === WGM_CTC) { + // Clear Timer on Compare Match (CTC) Mode + this.TCNT = 0; + this.TIFR |= TOV; } } - if (this.TIFR & 0x1 && this.TIMSK & 0x1 && this.cpu.interruptsEnabled) { - avrInterrupt(this.cpu, this.config.ovfInterrupt); - this.TIFR &= ~0x1; + if (this.ocrB && value === this.ocrB) { + this.TIFR |= OCFB; } } } |
