aboutsummaryrefslogtreecommitdiff
path: root/src/timer.ts
diff options
context:
space:
mode:
authorUri Shaked2019-11-21 17:09:40 +0200
committerUri Shaked2019-11-21 17:11:02 +0200
commitca96ec1ab43979e75c8a551e2aaddda8ad4b9e06 (patch)
tree6b591d80de7e00e1889b77f8fbad1eb1e493877c /src/timer.ts
parentfix: typescript errors on TS < 3.7 (diff)
downloadavr8js-ca96ec1ab43979e75c8a551e2aaddda8ad4b9e06.tar.gz
avr8js-ca96ec1ab43979e75c8a551e2aaddda8ad4b9e06.tar.bz2
avr8js-ca96ec1ab43979e75c8a551e2aaddda8ad4b9e06.zip
feat: initial timer implementation
8-bit timers basic functionality + tests: 1. basic counting + prescaler 2. timer overflow 3. timer overflow interrupt
Diffstat (limited to '')
-rw-r--r--src/timer.ts147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/timer.ts b/src/timer.ts
new file mode 100644
index 0000000..7203574
--- /dev/null
+++ b/src/timer.ts
@@ -0,0 +1,147 @@
+/**
+ * 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;
+ }
+ }
+}