|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2023 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // HD647180 I/O デバイス
9: //
10:
11: #include "mpu64180.h"
12: #include "hd647180.h"
13: #include "scheduler.h"
14:
15: //
16: // タイマー (Programmable Reload Timer)
17: //
18:
19: // タイマーを開始する時 つまり TDE{0,1} の立ち上がりで呼ぶ。
20: // 該当チャンネルの running をセットしてから呼ぶこと。
21: void
22: MPU64180Device::EnableTimer()
23: {
24: // すでに他方が開始しているなら、もう何もしなくていい
25: if (timer[0].running && timer[1].running) {
26: return;
27: } else if (timer[0].running == false && timer[1].running == false) {
28: PANIC("must be called when running");
29: }
30:
31: // TDE を立ててから1回目の TDR の減算が始まるまでの時間は
32: // 0 < t < 20φ と書いてあるので (HD647180.pdf p.112, Figure 13-3)、
33: // タイマーの tick はタイマー開始時点からカウントし始める方式ではなく、
34: // フリーランしている tick が来た時に処理をする方式のようだ。
35: // これの開始点はおそらくリセット時なので、
36: // 停止していたのを開始する時だけ次の tick までの時間を求める。
37:
38: uint64 now = scheduler->GetVirtTime() - timer_epoch;
39: uint64 tick = now / (clock_nsec * 20);
40: uint64 next = (tick + 1) * (clock_nsec * 20);
41:
42: timer_event.time = next - now;
43: scheduler->StartEvent(timer_event);
44: }
45:
46: // タイマー系の割り込み線の状態を変更する。
47: // HD647180.pdf p.112, Figure 13-5。
48: // TIEn, TIFn, IEF1 の変更でコールすること。
49: void
50: MPU64180Device::ChangeTimerInterrupt()
51: {
52: for (int ch = 0; ch < timer.size(); ch++) {
53: auto& t = timer[ch];
54: if (t.tif != 0 && t.intr_enable && GetIEF1()) {
55: AssertIntmap(HD647180::IntmapTimer(ch));
56: } else {
57: NegateIntmap(HD647180::IntmapTimer(ch));
58: }
59: }
60: }
61:
62: // タイマーイベント
63: void
64: MPU64180Device::TimerCallback(Event& ev)
65: {
66: // アクティブがなければ停止
67: if (active_timer.empty()) {
68: return;
69: }
70:
71: // 少なくとも片方は有効
72: for (auto tp : active_timer) {
73: auto& t = *tp;
74: if (__predict_false(t.count == 0)) {
75: // 0 になった次の tick でリロード
76: t.count = t.reload;
77: } else {
78: t.count--;
79: // 0 になったら割り込みを上げる
80: if (t.count == 0) {
81: t.tif = 0x03;
82: ChangeTimerInterrupt();
83: }
84: }
85: }
86:
87: // タイマーへの入力は原クロックの20倍なので、
88: // 厳密には 162.7604… * 20 = 3.255… [nsec] だが、
89: // ここでは clock_nsec * 20 = 3.260 [nsec] とする。
90: // この代入は本当は1回目だけでいいのだが。
91: timer_event.time = clock_nsec * 20;
92: scheduler->RestartEvent(ev);
93: }
94:
95: // アクティブタイマーリストを(再)構成する。
96: // 数が少ないので毎回作り直す。
97: // running を変更するたびに呼ぶこと。
98: void
99: MPU64180Device::MakeActiveTimer()
100: {
101: active_timer.clear();
102:
103: for (int ch = 0; ch < timer.size(); ch++) {
104: auto& t = timer[ch];
105: if (t.running) {
106: active_timer.push_back(&t);
107: }
108: }
109: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.