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