aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals/clock.ts
blob: bf49ac9ce7de489f6f43298716631bafa69d6fac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
 * AVR8 Clock
 * Part of AVR8js
 * Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf
 *
 * Copyright (C) 2020, Uri Shaked
 */

import { CPU } from '../cpu/cpu';
import { u32, u8 } from '../types';

const CLKPCE = 128;

export interface AVRClockConfig {
  CLKPR: u8;
}

export const clockConfig: AVRClockConfig = {
  CLKPR: 0x61,
};

const prescalers = [
  1, 2, 4, 8, 16, 32, 64, 128, 256,

  // The following values are "reserved" according to the datasheet, so we measured
  // with a scope to figure them out (on ATmega328p)
  2, 4, 8, 16, 32, 64, 128,
];

export class AVRClock {
  private clockEnabledCycles = 0;
  private prescalerValue = 1;
  cyclesDelta = 0;

  constructor(
    private cpu: CPU,
    private baseFreqHz: u32,
    private config: AVRClockConfig = clockConfig,
  ) {
    this.cpu.writeHooks[this.config.CLKPR] = (clkpr) => {
      if ((!this.clockEnabledCycles || this.clockEnabledCycles < cpu.cycles) && clkpr === CLKPCE) {
        this.clockEnabledCycles = this.cpu.cycles + 4;
      } else if (this.clockEnabledCycles && this.clockEnabledCycles >= cpu.cycles) {
        this.clockEnabledCycles = 0;
        const index = clkpr & 0xf;
        const oldPrescaler = this.prescalerValue;
        this.prescalerValue = prescalers[index];
        this.cpu.data[this.config.CLKPR] = index;
        if (oldPrescaler !== this.prescalerValue) {
          this.cyclesDelta =
            (cpu.cycles + this.cyclesDelta) * (oldPrescaler / this.prescalerValue) - cpu.cycles;
        }
      }

      return true;
    };
  }

  get frequency() {
    return this.baseFreqHz / this.prescalerValue;
  }

  get prescaler() {
    return this.prescalerValue;
  }

  get timeNanos() {
    return ((this.cpu.cycles + this.cyclesDelta) / this.frequency) * 1e9;
  }

  get timeMicros() {
    return ((this.cpu.cycles + this.cyclesDelta) / this.frequency) * 1e6;
  }

  get timeMillis() {
    return ((this.cpu.cycles + this.cyclesDelta) / this.frequency) * 1e3;
  }
}