墙上时钟¶
Overview¶
RTC - Real-time clock 实时时钟
好吧, 文件系统需要一个时间, 此时我刚刚想到某一天不小心删掉
.git目录而丢失了 所有的 stash 的自己... -- 2024.05.19
CMOS¶
CMOS - Complementary Metal Oxide Semiconductor 互补金属氧化物半导体 它是指制造大规模集成电路芯片用的一种技术或用这种技术制造出来的芯片, 是电脑主板上的一块可读写的 RAM 芯片 因为可读写的特性, 所以在电脑主板上用来保存BIOS设置完电脑硬件参数后的数据,这个芯片仅仅是用来存放数据的 -- BaiduBaike
由于是 RAM , 那么就不能断电, 要不然信息就会丢失, 所有 CMOS 有一块专门的 纽扣电池 来维持信息的存储, 也为 实时时钟 的运转提供支撑.
读取¶
CMOS 有很多个寄存器, 将这么多的寄存器全部暴露未免也太占用端口资源了, 于是它采取了一种好方法,
只提供两个端口: - Port - 0x70 -> 用于定位需要读写的寄存器 - 0x71 -> 用于读写操作
| Register | Contents | Range |
|---|---|---|
| 0x00 | Seconds | 0–59 |
| 0x02 | Minutes | 0–59 |
| 0x04 | Hours | 0–23 in 24-hour mode, 1–12 in 12-hour mode, highest bit set if pm |
| 0x06 | Weekday | 1–7, Sunday = 1 |
| 0x07 | Day of Month | 1–31 |
| 0x08 | Month | 1–12 |
| 0x09 | Year | 0–99 |
| 0x32 | Century (maybe) | 19–20? |
| 0x0A | Status Register A | |
| 0x0B | Status Register B |
在读写之前我们需要先关闭 NMI
NMI - Non-Maskable interrupt 不可屏蔽中断
关闭它很简单, 就是选择寄存器时的附属操作, 端口操作的内容就是 (reg | 0x80)
直接读取出来的东西可能是 BCD 码, 这里是有可能, 具体是怎么的需要 CMOS 自己告诉我们
- Status Register B
- Bit 2 == 0 -> 使用 BCD 码
读取出来的可能与当前时间有点差距, 即使处理没有问题, 少了 12 个小时的话, 就说明是 12 小时模式.
- Status Register B
- Bit 1 == 1 -> 开启 24 小时
这些都是需要进行额外处理的, 剩下的就是写代码了!
写好了代码了吗? 不知道你跑出来的结果是不是有点奇怪, 可能是 qemu 的锅!
更多玩法, 请移步至 qemu-rtc.md
BCD¶
BCD - Binary-coded decimal
对于一个普普通通的 uint8_t, 它可以存储 0 ~ 255 的无符号整型, 如果使用 BCD 来存储, 那么它只能表示 0 ~ 99 的十进制数.
BCD 使用 4bit 来存储一个十进制位:
对于一个 0 ~ 99 的数字:
- (bcd & 0xf) 获取个位
- (bcd >> 4) 获取十位
以此类推...
Unix 时间戳¶
好了前面的内容已经很简单了, 恭喜你到了这里, 发挥你极致的才智, 来将 RTC 提供的乱七八糟的东西变成一个 Unix 时间戳 吧!
起源¶
我当然说不清楚, 但是我建议去看看这期视频 1
从工具开始¶
获取当前时间戳:
做一个转换, 时间戳为 0, date 此时默认时会做一个时区转换的, 于是先在环境变量中设置 UTC
格式¶
时间戳是 从 1970/01/01 00:00:00 (Unix Epoch2) 开始的, 已经过去的秒数.
时间戳是考虑了闰年的...
计算已经过去了多少个闰年是有骚操作的, 我先用朴素算法. 运用一点点容斥原理:
- git
6a3da4a5
2000 是怎么来的? $$ align_up(1972, 100) = align_up(1972, 400) = 2000 $$ 为什么
year? 特殊值最直观,year = 1972, 但是 1972 年还没有度过. 类似的,year = 2000, 2000 年还没有度过
设 \([a, b]\) 有 \(n\) 个正整数可以被 \(p\) 整除, \(a,b,p \in \mathbf{N^*}\) 且 \(p > 1, p \mid a\)
列举:
易得:
若 \(p \mid {b-a}\), 则 \(n = \frac{b-a}{p} + 1 \Leftrightarrow n = \lfloor \frac{b-a}{p} + 1 \rfloor\)
若 \(p \nmid {b-a}\), 则 \(n = \lceil \frac{b-a}{p} \rceil\)
如果原条件变为 \([a, y)\) 则有 \([a, y) \Leftrightarrow [a, y-1]\), 令 \(b = y-1\)
若 \(p \mid {y-a-1}\), 则 \(\textbf{1}:n = \frac{b-a}{p} + 1 = \lfloor \frac{y-a+p-1}{p} \rfloor = \lceil \frac{y-a}{p} \rceil\)
若 \(p \nmid {y-a-1}\), 则 \(\textbf{2}:n = \lceil \frac{y-a-1}{p} \rceil\)
现在证明 \(\textbf{1},\textbf{2}\) 在大条件下是 一致的 即可
当然, 如果不用这个公式可以选择更通用的:
区间 [a, b) 中能被 p 整除的整数个数是:
那么, 接下来的, 就是将上述综合起来