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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
/**
* 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;
}
}
|