|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1.1.8 root 7: //
1.1.1.10 root 8: // LUNA-88K のシステムコントローラのような何か
1.1.1.8 root 9: //
1.1 root 10:
1.1.1.8 root 11: // IODevice
12: // |
13: // +-- InterruptDevice (割り込みコントローラの共通部)
14: // |
15: // +-- M680x0Interrupt (m68k 割り込みコントローラの共通部)
16: // | |
17: // | +-- X68030Interrupt (X68030 の割り込みコントローラ)
18: // | +-- LunaInterrupt (LUNA-I の割り込みコントローラ)
19: // |
20: // +-- PEDECDevice (X68030 の Lv1 担当コントローラ)
21: // |
22: // | +---------------+
1.1.1.10 root 23: // +--| SysCtlrDevice | (LUNA-88K の割り込みコントローラ)
1.1.1.8 root 24: // +---------------+
1.1 root 25:
1.1.1.16 root 26: // この辺を全部このデバイスで受け付けてある。
27: //
28: // 公称アドレス 実態範囲
29: // $6500'000n $64'〜$67' 割り込み制御
30: // $6900'000n $68'〜$6b' ソフトウェア割り込み設定
31: // $6d00'00nn $6c'〜$6d' CPUリセット(?)
32: // ($6e'〜$6f' は不明、$ffffffff が読める)
33:
34: // $6500'000n: 割り込み制御レジスタ(?)
35: // アドレスは
36: // $6500'0000: CPU0
37: // $6500'0004: CPU1
38: // $6500'0008: CPU2
39: // $6500'000c: CPU3
40: // $64..$67 から始まるこの 64MB は 16バイトでミラーされている模様。
41: //
42: // 書き込み時は
43: // 3 2 1
44: // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
45: // +-+-+-+-+-+-+---------------------------------------------------+
46: // |6|5|4|3|2|1| |
47: // +-+-+-+-+-+-+---------------------------------------------------+
48: // bit31: %1 = 割り込み 6 イネーブル
49: // bit30: %1 = 割り込み 5 イネーブル
50: // bit29: %1 = 割り込み 4 イネーブル
51: // bit28: %1 = 割り込み 3 イネーブル
52: // bit27: %1 = 割り込み 2 イネーブル
53: // bit26: %1 = 割り込み 1 イネーブル
54: //
55: // m68k と異なり、個別の割り込みごとに許可/禁止できる。
56: // 該当ビットに 1 を書き込むと許可。空欄部分は無視される模様。
57: //
58: // 読み込み時は
59: // 3 2 1
60: // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
61: // +-----+---------+-+-+-+-+-+-+-----------------------------------+
62: // |Level| |6|5|4|3|2|1| |
63: // +-----+---------+-+-+-+-+-+-+-----------------------------------+
64: // bit31-29: 発生している最上位レベルの割り込み。範囲は7-0。
65: // bit23-18: 書き込んだ割り込みマスク。
66: //
67: // 空欄部分は「不定」の模様。
68: //
69: // bit31-29 のレベルは、割り込みコントローラに入力されている割り込み
70: // 信号線6本をマスク処理した後の、プライオリティバイナリエンコーダの値が
71: // そのまま出力されている模様。ラッチ機構は無いようだ。
72: // 従ってマスクされている割り込み(ビットが 0) の値が出力されることはない。
73: // 同時に入力があった場合は、数字の大きいほうが出力される。
74: // レベル7 はマスクなしでプライオリティエンコーダに入力されているようだ。
75: // 割り込み入力がないときは 0 になる模様。
76: //
77: // 本体 ABRT スイッチを押している間、すべての CPU の Level が %111 (=7)
78: // になる。離すと戻る。
79: // m88100 には NMI が無いので、通常の割り込みと同じように処理している?
80: // (なので IND 状態だとABRT できない。いいのかそれで)
81: // (OpenBSD6.7 の割り込みハンドラはABRTには対応していないように読める)
82: //
83: // 想定される回路
84: //
85: // [割り込みソース]
86: // 7:ABRT スイッチ
87: // | 6:システムクロック
88: // | | 5:(シリアル | XP 高レベル | 拡張 PC-98?)
89: // | | | 4:(LANCE | 拡張スロット?)
90: // | | | | 3:SPC
91: // | | | | | 2:(未使用?)
92: // | | | | | | 1:(XP 低レベル | SOFTINT レジスタ $6900'000n)
93: // | | | | | | |
94: // | V V V V V V
95: // | +---------------------+
96: // | | 割り込みマスク | (R:bit23-18, W:bit31-26)
97: // | +---------------------+
98: // | | | | | | |
99: // V V V V V V V
100: // +-------------------------+
101: // |プライオリティエンコーダ |-> (R:bit31-29)
102: // +-------------------------+
103: // | | |
104: // V V V
105: // (Wired-OR)
106: // |
107: // [M88K INT]
108: //
109: // 割り込みソース調査状況。(LUNA-I とはレベル配置が違う)
110: //
111: // 1:SOFTINT レジスタ $6900'000n
112: // ROM のメモリコマンドで確認。OpenBSD machdep.c の記述の通り。
113: // 1:XP 低レベル
114: // OpenBSD mainbus.c および Issue#131 より。
115: // 3:SPC
116: // PROM 1.20, OpenBSD ともに使用。
117: // 4:LANCE
118: // ROM はまだ不明。OpenBSD は使用。
119: // 4:拡張スロット
120: // LUNA-I からの類推のみ。不明。
121: // 5:拡張PC98
122: // LUNA-I からの類推のみ。
123: // LUNA-88K にはスロットがないようなので LUNA-88K2 が必要。
124: // OpenBSD mainbus.c に記述あり。
125: // 5:シリアル(uPD7201)
126: // OpenBSD mainbus.c に記述。
127: // 5:XP 高レベル
128: // Issue#131 より。
129: // 6:システムクロック
130: // ROM のメモリコマンドではレベル6割り込みが常時上がっているように
131: // 観測できるので、おそらくシステムクロック割り込みが上がっている。
132: // OpenBSD maibus.c にも記述。
133: // 7:ABRTスイッチ
134: // ROM のメモリコマンドで確認。
135: //
136: // $6900'000n: ソフトウェア割り込み設定レジスタ(?)
137: // アドレスは
138: // $6900'0000: CPU0
139: // $6900'0004: CPU1
140: // $6900'0008: CPU2
141: // $6900'000c: CPU3
142: //
143: // この32ビットのどこかにライトアクセスすると、INT1 をアサートする。
144: // この32ビットのどこかをリードアクセスすると、INT1 をネゲートする。
145: // この時のデータバスは無視される。0x00 を書いてもアサートされることに注意。
146: // リードアクセスで読み込める値も不定のようだ。
147: // 要はアドレスバスと R/W 信号線しか見ていない。
148: //
149: // $6d00'000n: CPU 個別リセットレジスタ(?)
150: // このへん触ると CPU がリセットされるというかだんまりになるので調査困難。
151: //
152: // $6d00'0010: 全 CPU リセットレジスタ(?)
1.1 root 153:
1.1.1.8 root 154: #include "sysctlr.h"
1.1.1.10 root 155: #include "lance.h"
1.1.1.8 root 156: #include "mpu88xx0.h"
157: #include "nmi.h"
158: #include "prom.h"
159: #include "sio.h"
160: #include "spc.h"
161: #include "sysclk.h"
1.1 root 162:
163: // コンストラクタ
164: SysCtlrDevice::SysCtlrDevice()
1.1.1.10 root 165: : inherited(OBJ_INTERRUPT)
1.1 root 166: {
1.1.1.11 root 167: SetName("SysCtlr");
1.1.1.5 root 168: AddAlias("SysCtlr");
1.1.1.11 root 169: AddAlias("SystemCtlr");
1.1 root 170:
1.1.1.12 root 171: intmap_status = IntmapSentinel;
172:
1.1.1.14 root 173: monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this);
1.1.1.17! root 174: monitor->SetCallback(&SysCtlrDevice::MonitorScreen);
1.1 root 175: }
176:
177: // デストラクタ
178: SysCtlrDevice::~SysCtlrDevice()
179: {
180: }
181:
1.1.1.10 root 182: // 初期化
183: bool
184: SysCtlrDevice::Init()
185: {
1.1.1.12 root 186: mpu88xx0 = GetMPU88xx0Device(mpu);
187:
188: // 検索順
189: RegistINT(IntmapSysClk, "Clock", GetSysClkDevice());
190: RegistINT(IntmapSIO, "SIO", GetSIODevice());
191: RegistINT(IntmapLance, "Lance", GetLanceDevice());
192: RegistINT(IntmapSPC, "SPC", GetSPCDevice());
193: RegistINT(IntmapXPHigh, "XP(Hi)", DEVICE_XPINT_HIGH);
194: RegistINT(IntmapXPLow, "XP(Lo)", DEVICE_XPINT_LOW);
195: RegistINT(IntmapSoft, "SoftInt", mpu88xx0); // ?
196: RegistINT(IntmapNMI, "NMI", GetNMIDevice());
197:
198: // 表示順
199: disp_list = {
200: IntmapNMI,
201: IntmapSysClk,
202: IntmapSIO,
203: IntmapXPHigh,
204: IntmapLance,
205: IntmapSPC,
206: IntmapXPLow,
207: IntmapSoft,
208: };
209:
210: // 行数が確定したのでサイズ設定
1.1.1.14 root 211: monitor->SetSize(41, 1 + disp_list.size());
1.1.1.10 root 212:
213: return true;
214: }
215:
1.1.1.8 root 216: // リセット
1.1.1.3 root 217: void
1.1.1.8 root 218: SysCtlrDevice::ResetHard(bool poweron)
1.1.1.3 root 219: {
220: // さすがにリセットくらいはされるだろう…
1.1.1.8 root 221: inherited::ResetHard(poweron);
1.1.1.7 root 222:
1.1.1.3 root 223: for (int i = 0; i < 4; i++) {
224: // XXX 初期値は分からないがとりあえず割り込み禁止にしておく
1.1.1.16 root 225: cpu[i].maskval = 0x00000000;
1.1.1.3 root 226: cpu[i].intmask = MakeIntMask(cpu[i].maskval, i);
227: cpu[i].ipl = 0;
1.1.1.12 root 228: cpu[i].softint = 0;
229: cpu[i].softint_count = 0;
1.1.1.3 root 230: }
1.1.1.12 root 231: ChangeInterrupt();
1.1.1.3 root 232: }
233:
1.1.1.12 root 234: busdata
1.1.1.13 root 235: SysCtlrDevice::Read(busaddr addr)
1.1 root 236: {
1.1.1.13 root 237: uint32 paddr = addr.Addr();
238: uint n = (paddr >> 2) & 3;
239: busdata data;
1.1 root 240:
1.1.1.13 root 241: // XXX バイト/ワードアクセスは未対応
242: if (addr.GetSize() < 4) {
243: putlog(0, "Read $%08x size=%u (NOT IMPLEMENTED)",
244: paddr, addr.GetSize());
245: }
246:
1.1.1.16 root 247: if (0x64000000 <= paddr && paddr < 0x68000000) {
248: data = Peek4(paddr);
249: putlog(2, "Read INT_CTRL(CPU#%u) -> $%08x", n, data.Data());
1.1 root 250:
1.1.1.13 root 251: } else if (0x69000000 <= paddr && paddr < 0x69000010) {
1.1.1.3 root 252: // 読み出しで SOFTINT ネゲート (値は関係ない)
1.1.1.12 root 253: if (cpu[n].softint) {
1.1.1.13 root 254: putlog(1, "Softint(CPU#%u) Negate", n);
1.1.1.12 root 255: cpu[n].softint = 0;
256: ChangeInterrupt();
257: }
1.1.1.13 root 258: data = 0xffffffff;
1.1 root 259:
1.1.1.13 root 260: } else if (0x6d000000 <= paddr && paddr < 0x6d000010) {
261: putlog(0, "Read CPU%u reset (NOT IMPLEMENTED)", n);
262: data = 0xffffffff;
263:
264: } else if (paddr == 0x6d000010) {
1.1.1.9 root 265: putlog(0, "Read All CPU reset (NOT IMPLEMENTED)");
1.1.1.13 root 266: data = 0xffffffff;
267:
268: } else {
269: putlog(0, "Read $%08x (NOT IMPLEMENTED)", paddr);
270: data.SetBusErr();
1.1 root 271: }
272:
1.1.1.13 root 273: data |= BusData::Size4;
274: return data;
1.1 root 275: }
276:
1.1.1.12 root 277: busdata
1.1.1.13 root 278: SysCtlrDevice::Write(busaddr addr, uint32 data)
1.1 root 279: {
1.1.1.13 root 280: uint32 paddr = addr.Addr();
281: uint n = (paddr >> 2) & 3U;
1.1 root 282:
1.1.1.13 root 283: // XXX バイト/ワードアクセスは未対応
284: if (addr.GetSize() < 4) {
285: putlog(0, "Write $%08x size=%u (NOT IMPLEMENTED)",
286: paddr, addr.GetSize());
1.1 root 287: }
1.1.1.13 root 288:
1.1.1.16 root 289: if (0x64000000 <= paddr && paddr < 0x68000000) {
1.1.1.13 root 290: // 割り込みマスクを設定。
291: WriteIntMask(n, data);
292:
293: } else if (0x69000000 <= paddr && paddr < 0x69000010) {
1.1.1.3 root 294: // 書き込みで SOFTINT アサート (値は関係ない)
1.1.1.12 root 295: if (cpu[n].softint == false) {
1.1.1.13 root 296: putlog(1, "Softint(CPU#%u) Assert", n);
1.1.1.12 root 297: cpu[n].softint = IntmapSoft;
298: cpu[n].softint_count++;
299: // 親クラスのカウンタも上げる (4つの和になる)
300: int clz = __builtin_clz(IntmapSoft);
301: counter[clz]++;
302: ChangeInterrupt();
303: }
1.1.1.13 root 304:
305: } else if (0x6d000000 <= paddr && paddr < 0x6d000010) {
306: putlog(0, "Write CPU%u reset (NOT IMPLEMENTED)", n);
307:
308: } else if (paddr == 0x6d000010) {
1.1.1.4 root 309: putlog(1, "Reset All CPUs");
1.1.1.8 root 310: // XXX このあたりもう少し整理する必要がある
311: // 今は MPU.ResetHard() が func を ResetCallback に、time をリセット
312: // 間隔に書き換えて Start() した後、この書き込み命令から戻ったところの
313: // ExecCallback が time だけ所要サイクルに書き換えて再度 Start() する
314: // ため、時間はともかく幸い ResetCallback() が呼ばれることで動いている。
1.1.1.10 root 315: GetPROM0Device()->ResetHard(false);
316: mpu->ResetHard(false);
1.1.1.8 root 317: ResetHard(false);
1.1.1.13 root 318:
319: } else {
320: putlog(0, "Write $%08x <- $%08x (NOT IMPLEMENTED)", paddr, data);
1.1 root 321: }
322:
1.1.1.13 root 323: busdata r = BusData::Size4;
324: return r;
1.1 root 325: }
326:
1.1.1.16 root 327: // Peek1() の下請け。ロングワードで処理して後でバイトに分割するほうが楽。
328: uint32
329: SysCtlrDevice::Peek4(uint32 addr) const
1.1 root 330: {
1.1.1.13 root 331: uint n = (addr >> 2) & 3;
1.1 root 332: uint32 data;
333:
1.1.1.16 root 334: if (0x64000000 <= addr && addr < 0x68000000) {
335: // bit31-29 に発生している最上位の割り込みレベル。
336: // bit23-18 に設定したレベル 6-1 の割り込みマスク。
1.1.1.3 root 337: data = (cpu[n].ipl << 29) | (cpu[n].maskval >> 8);
1.1 root 338: } else {
339: data = 0xffffffff;
340: }
1.1.1.16 root 341: return data;
342: }
343:
344: busdata
345: SysCtlrDevice::Peek1(uint32 addr)
346: {
347: uint32 data = Peek4(addr & ~3U);
1.1 root 348: switch (addr & 3) {
349: case 0:
350: return data >> 24;
351: case 1:
352: return (data >> 16) & 0xff;
353: case 2:
354: return (data >> 8) & 0xff;
355: case 3:
356: return data & 0xff;
357: default:
1.1.1.10 root 358: __unreachable();
1.1 root 359: }
360: }
361:
1.1.1.2 root 362: void
1.1.1.17! root 363: SysCtlrDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1 root 364: {
1.1.1.3 root 365: int y;
366:
367: // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい
1.1.1.10 root 368: bool ei = mpu88xx0->IsIntrEnable();
1.1.1.3 root 369:
1.1.1.5 root 370: screen.Clear();
1.1.1.10 root 371:
372: // 0 1 2 3 4
373: // 01234567890123456789012345678901234567890
374: // Lv Device CPU0 Count
375:
1.1.1.3 root 376: y = 0;
1.1.1.10 root 377: // XXX CPU の実装数によってウィンドウ幅を変えること。
1.1.1.12 root 378: screen.Print(0, y, "Lv Device CPU0");
379: screen.Puts(36, y, "Count");
1.1.1.10 root 380: y++;
1.1.1.12 root 381: for (uint32 map : disp_list) {
1.1.1.13 root 382: uint clz = __builtin_clz(map);
383: uint lv = (31 - clz) / 4;
384: screen.Print(0, y, "%u", lv);
1.1.1.12 root 385: screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map));
386: screen.Print(15, y, "%26s", format_number(counter[clz]).c_str());
1.1.1.3 root 387:
388: // CPU ごとのマスク
389: // XXX 今は CPU は1つしかない
390: for (int n = 0; n < 1; n++) {
391: // Softint の他 CPU 分は
392: // 動作としては常にマスクされてて通過しないから問題ないが、
393: // 見せる分にはマスクされているではなく無効と見せたほうがいい。
1.1.1.12 root 394: if ((cpu[n].intmask & map) == 0) {
395: screen.Puts(11 + n * 5, y, "Mask");
1.1.1.3 root 396: } else if (!ei) {
1.1.1.12 root 397: screen.Puts(11 + n * 5, y, "IND");
1.1.1.3 root 398: }
399: }
400:
401: y++;
402: }
403:
1.1.1.12 root 404: // ソフトウェア割り込みの CPU ごとのカウンタいる?
1.1.1.3 root 405: }
406:
1.1.1.13 root 407: // 割り込みマスクを設定する。
408: // data はロングワードレジスタの書き込み値 (bit31-26 のみ有効)。
409: void
410: SysCtlrDevice::WriteIntMask(uint32 n, uint32 data)
411: {
412: data &= 0xfc000000;
413:
414: // 割り込みレベル変えるたびに表示されるのですごく多い。
415: // さらに同レベルが書き込まれることも多い…。
416: if (cpu[n].maskval != data) {
417: putlog(2, "Interrupt Mask(CPU#%u) <- $%08x", n, data);
418: } else {
419: putlog(3, "Interrupt Mask(CPU#%u) <- $%08x", n, data);
420: }
421:
422: // 設定値を保存
423: cpu[n].maskval = data;
424: // 内部マスクを書き換えて割り込み状態を更新
425: cpu[n].intmask = MakeIntMask(data, n);
426: ChangeInterrupt();
427: }
428:
1.1.1.3 root 429: // 割り込みアクノリッジはない
1.1.1.12 root 430: busdata
1.1.1.3 root 431: SysCtlrDevice::InterruptAcknowledge(int lv)
432: {
433: return 0;
434: }
435:
436: // 割り込み状態を更新
437: void
438: SysCtlrDevice::ChangeInterrupt()
439: {
440: // XXX MPU 側がまだマルチプロセッサに対応してないので CPU#0 だけ
441: for (int i = 0; i < 1; i++) {
1.1.1.12 root 442: uint32 localmap = (intmap_status | cpu[i].softint) & cpu[i].intmask;
1.1.1.3 root 443:
1.1.1.12 root 444: // intmap_status の最下位ビットは常に立ててあり localmap は 0 にならない
1.1.1.13 root 445: uint newipl = (uint)(31 - __builtin_clz(localmap)) / 4;
1.1.1.3 root 446:
1.1.1.7 root 447: // 割り込みレベルが変わったら、新しいレベルを通知
1.1.1.3 root 448: if (newipl != cpu[i].ipl) {
449: cpu[i].ipl = newipl;
450: // MPU 側は 1 レベルしかない
1.1.1.10 root 451: mpu->Interrupt(cpu[i].ipl ? 1 : 0);
1.1.1.3 root 452: }
1.1 root 453: }
454: }
1.1.1.3 root 455:
456: // 割り込みマスク設定レジスタの書き込み値から内部用のマスクを作成
457: // data は bit31-26 のみ使用。他のビットは不問。
458: // n は CPU# (Softint 用)。
459: /*static*/ uint32
1.1.1.13 root 460: SysCtlrDevice::MakeIntMask(uint32 data, uint n)
1.1.1.3 root 461: {
462: uint32 val = 0xf0000001; // Lv7 は NMI、最下位は番兵用
463:
464: uint32 mask = 0x0f000000;
465: for (int i = 6; i >= 1; i--) {
466: if ((int32)data < 0) {
467: val |= mask;
468: }
469: mask >>= 4;
470: data <<= 1;
471: }
472:
473: return val;
474: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.