/** * AVR-8 Timers * 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, Uri Shaked */ import { CPU } from './cpu'; import { avrInterrupt } from './interrupt'; const dividers = { 0: 0, 1: 1, 2: 8, 3: 64, 4: 256, 5: 1024, 6: 0, // TODO: External clock source on T0 pin. Clock on falling edge. 7: 0 // TODO: External clock source on T0 pin. Clock on rising edge. }; type u8 = number; interface AVRTimerConfig { bits: 8 | 16; captureInterrupt: u8; compAInterrupt: u8; compBInterrupt: u8; ovfInterrupt: u8; // Register addresses TIFR: u8; OCRA: u8; OCRB: u8; ICR: u8; TCNT: u8; TCCRA: u8; TCCRB: u8; TCCRC: u8; TIMSK: u8; } export const timer0Config: AVRTimerConfig = { bits: 8, captureInterrupt: 0, // not available compAInterrupt: 0x1c, compBInterrupt: 0x1e, ovfInterrupt: 0x20, TIFR: 0x35, OCRA: 0x47, OCRB: 0x48, ICR: 0, // not available TCNT: 0x46, TCCRA: 0x44, TCCRB: 0x45, TCCRC: 0, // not available TIMSK: 0x6e }; export const timer1Config: AVRTimerConfig = { bits: 16, captureInterrupt: 0x14, compAInterrupt: 0x16, compBInterrupt: 0x18, ovfInterrupt: 0x1a, TIFR: 0x36, OCRA: 0x88, OCRB: 0x8a, ICR: 0x86, TCNT: 0x84, TCCRA: 0x80, TCCRB: 0x81, TCCRC: 0x82, TIMSK: 0x6f }; export const timer2Config: AVRTimerConfig = { bits: 8, captureInterrupt: 0, // not available compAInterrupt: 0x0e, compBInterrupt: 0x10, ovfInterrupt: 0x12, TIFR: 0x37, OCRA: 0xb3, OCRB: 0xb4, ICR: 0, // not available TCNT: 0xb2, TCCRA: 0xb0, TCCRB: 0xb1, TCCRC: 0, // not available TIMSK: 0x70 }; export class AVRTimer { private mask = (1 << this.config.bits) - 1; private lastCycle = 0; constructor(private cpu: CPU, private config: AVRTimerConfig) {} get TIFR() { return this.cpu.data[this.config.TIFR]; } set TIFR(value: u8) { this.cpu.data[this.config.TIFR] = value; } get TCNT() { return this.cpu.data[this.config.TCNT]; } set TCNT(value: u8) { this.cpu.data[this.config.TCNT] = value; } get TCCRB() { return this.cpu.data[this.config.TCCRB]; } get TIMSK() { return this.cpu.data[this.config.TIMSK]; } get CS() { return (this.TCCRB & 0x7) as 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; } tick() { const divider = dividers[this.CS]; const delta = this.cpu.cycles - this.lastCycle; if (divider && delta >= divider) { const counterDelta = Math.floor(delta / divider); this.lastCycle += counterDelta * divider; const val = this.TCNT; const newVal = (val + counterDelta) & this.mask; this.TCNT = newVal; if (val > newVal) { this.TIFR |= 1; // TOV } } if (this.TIFR & 0x1 && this.TIMSK & 0x1 && this.cpu.interruptsEnabled) { avrInterrupt(this.cpu, this.config.ovfInterrupt); this.TIFR &= ~0x1; } } }