|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "pio.h"
1.1.1.9 ! root 8: #include "bitops.h"
1.1.1.2 root 9: #include "config.h"
1.1 root 10: #include "lcd.h"
1.1.1.3 root 11: #include "mainapp.h"
1.1 root 12: #include "scheduler.h"
13:
14: // IODevice
15: // |
16: // v
17: // i8255Device (8255 としての共通部分)
18: // |
19: // +----------+----------+
20: // v v v
21: // PPIDevice PIO0Device PIO1Device
22: // (X68030) (LUNA) (LUNA)
23:
1.1.1.6 root 24: std::unique_ptr<PPIDevice> gPPI;
1.1 root 25: std::unique_ptr<PIO0Device> gPIO0;
26: std::unique_ptr<PIO1Device> gPIO1;
27:
28: //
29: // i8255
30: //
31:
32: // コンストラクタ
1.1.1.8 root 33: i8255Device::i8255Device(const std::string& objname_)
34: : inherited(objname_)
1.1 root 35: {
36: }
37:
38: // デストラクタ
39: i8255Device::~i8255Device()
40: {
41: }
42:
1.1.1.4 root 43: // リセット
44: void
45: i8255Device::ResetHard()
46: {
47: // clears the control register
48: // and place ports A, B, and C in input mode.
49: // The input latches in ports A, B, and C are not cleared.
50:
51: ctrl = 0b10011011;
52: }
53:
1.1 root 54: // コントロールポートへの書き込み。
55: void
56: i8255Device::WriteCtrl(uint32 data)
57: {
58: if ((data & 0x80)) {
59: // 最上位が立っていればモード設定
60: ctrl = data;
61: putlog(1,
62: "モード設定 groupA=%d groupB=%d portA=%s portB=%s portC=%s/%s",
63: GetGroupAMode(), GetGroupBMode(),
64: PortADir() == DIR_IN ? "IN" : "OUT",
65: PortBDir() == DIR_IN ? "IN" : "OUT",
66: PortCHDir() == DIR_IN ? "IN" : "OUT",
67: PortCLDir() == DIR_IN ? "IN" : "OUT");
68:
69: // モード設定すると PC はゼロクリアされる
70: for (int pc = 0; pc < 8; pc++) {
71: SetPC(pc, 0);
72: }
73: } else {
74: // portC 出力
75: //
76: // 0 x x x P P P V
77: // | | | +-- 書き込むビットデータ
78: // +--+--+----- 書き込むビット位置
79:
80: int pc = (data >> 1) & 7;
81: int val = data & 1;
82: SetPC(pc, val);
83: }
84: }
85:
86: void
87: i8255Device::SetPC(int pc, int data)
88: {
89: // 継承側で用意すること。ここには来ない
90: PANIC("not impl");
91: }
92:
93: //
94: // X68k PPI
95: //
96:
97: // コンストラクタ
98: PPIDevice::PPIDevice()
1.1.1.8 root 99: : inherited("PPI")
1.1 root 100: {
101: devaddr = baseaddr;
102: devlen = 0x2000;
103: }
104:
105: // デストラクタ
106: PPIDevice::~PPIDevice()
107: {
108: }
109:
110: uint64
1.1.1.6 root 111: PPIDevice::Read(uint32 offset)
1.1 root 112: {
1.1.1.4 root 113: gMPU->AddCycle(19); // InsideOut p.135
114:
1.1.1.6 root 115: switch (offset) {
1.1 root 116: case 0:
117: case 1:
118: return 0xff;
119: case 2:
1.1.1.6 root 120: putlog(0, "$e9a005 PortC 未実装読み込み");
1.1.1.5 root 121: return 0xff;
1.1 root 122: case 3:
1.1.1.6 root 123: PANIC("PPI $e9a007 未サポート読み込み");
1.1 root 124: break;
125: default:
126: __unreachable();
127: }
128: PANIC("not impl");
129: }
130:
131: uint64
1.1.1.6 root 132: PPIDevice::Write(uint32 offset, uint32 data)
1.1 root 133: {
1.1.1.4 root 134: gMPU->AddCycle(19); // InsideOut p.135
135:
1.1.1.6 root 136: switch (offset) {
1.1 root 137: case 0:
138: case 1:
139: break;
140: case 2:
1.1.1.6 root 141: putlog(0, "$e9a005 未実装書き込み $%02X (無視)", data);
1.1 root 142: break;
143: case 3:
144: if (data != 0x92) {
1.1.1.6 root 145: PANIC("PPI $e9a007 未実装書き込み $%02X", data);
1.1 root 146: } else {
1.1.1.6 root 147: putlog(0, "$e9a007 未実装書き込み $%02x (無視)", data);
1.1 root 148: }
149: break;
150: default:
151: __unreachable();
152: }
153: return 0;
154: }
155:
156: uint64
1.1.1.6 root 157: PPIDevice::Peek(uint32 offset)
1.1 root 158: {
159: // XXX
160: return 0xff;
161: }
162:
163: //
164: // LUNA PIO0
165: //
166: // 仕様は 0x49000000 から 4バイトだが、
167: // 0x48000000 から 0x4c000000 の手前まで 4バイト単位でミラーが見える。
168: //
169: // LUNA の DIP-SW は #1, #2 にそれぞれ8個、計16個ある。
1.1.1.9 ! root 170: // DIP-SW #1, #2 が設定ファイルの luna-dipsw{1,2} に対応する。
! 171: // luna-dipsw{1,2} はいずれも "0"/"1" を8つ並べた文字列であり、
1.1 root 172: // 先頭が本体表記でいう 1番、末尾側が本体表記でいう 8番に対応する。
1.1.1.9 ! root 173: // 値は "0" が down、"1" が up を表す。
! 174: //
! 175: // PIO0Device 内ではこの設定を読み込んで bool dipsw1[8], dipsw2[8] の配列で
! 176: // 値を保持している。dipsw1[0] が本体表記1番、dipsw1[7] が本体表記 8番に対応
! 177: // する。値は false が down("0")、true が up("1") に対応する。
! 178: //
! 179: // PIO から読み出せる値のビットの並び順が機種によって異なる。
! 180: // LUNA-1 では 1番が LSB、8番が MSB の並び順、
! 181: // LUNA88K では 1番が MSB、8番が LSB の並び順。
! 182: // 値は、並び順に関わらずどちらも
! 183: // %0 が false = down = "0"、%1 が true = up = "1" である。
! 184: //
! 185: // #1 #2 #3 #4 #5 #6 #7 #8
! 186: // +-------------------------+
! 187: // | [] [] [] [] [] [] [] | 本体外観
! 188: // | [] |
! 189: // +-------------------------+
! 190: //
! 191: // luna-dipsw1 = "01111111" ← 設定ファイル
! 192: // |||||||| ↓ このクラスのメンバ変数
! 193: // |||||||+- dipsw1[7] = true;
! 194: // ||||||+-- dipsw1[6] = true;
! 195: // |||||+--- dipsw1[5] = true;
! 196: // ||||+---- dipsw1[4] = true;
! 197: // |||+----- dipsw1[3] = true;
! 198: // ||+------ dipsw1[2] = true;
! 199: // |+------- dipsw1[1] = true;
! 200: // +-------- dipsw1[0] = false;
! 201: //
! 202: // b7 b6 b5 b4 b3 b2 b1 b0
! 203: // +----+----+----+----+----+----+----+----+
! 204: // LUNA-1 | #8 | #7 | #6 | #5 | #4 | #3 | #2 | #1 |
! 205: // PortA | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | %1=UP, %0=DOWN
! 206: // +----+----+----+----+----+----+----+----+
! 207: //
! 208: // b7 b6 b5 b4 b3 b2 b1 b0
! 209: // +----+----+----+----+----+----+----+----+
! 210: // LUNA88K | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 |
! 211: // PortA | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | %1=UP, %0=DOWN
! 212: // +----+----+----+----+----+----+----+----+
1.1 root 213: //
1.1.1.3 root 214: // LUNA-1:
1.1 root 215: // #1-1 up なら UNIX をロードして移行。down なら ROM モニタで起動。
216: // #1-2 up ならディスプレイコンソール。down ならシリアルコンソール。
217: // #1-3 up - color display, down - force to have monochrome display
218: // #1-4 up - no write verification,
219: // down - verification on every harddisk write operation
220: // #1-5 up - OS is UniOS-U (SystemV/COFF),
221: // down - OS is UniOS (4.3BSD/a.out OMAGIC)
222: // #1-6 down - force monochrome display?
223: // #1-7 up - boot from local device, down - boot from network
224: // #1-8 up - normal boot, down - start diagnostics と書いてあるが
225: // down にすると画面真っ暗で詳細不明。
226: //
227: // #2-x すべて up にすると /boot が自動的にカーネルを起動。一つでも down が
228: // あれば /boot がプロンプトで停止。これは ROM ではなく NetBSD のブート
229: // ローダの話。
230: // どこか由来のソースコードらしいがたぶんビット演算を間違えている。
1.1.1.3 root 231: //
232: // LUNA88K:
233: // #1-1 up なら ROM モニタで停止、down ならマルチユーザブート。
234: // OpenBSD カーネルも up なら UserKernelConfig で止まる模様?。
235: // #1-2 up なら外付けシリアルコンソール、down なら内蔵ビットマップ。
1.1.1.9 ! root 236: // #1-3..#1-7 は不明?
! 237: // #1-8 down なら自己診断後電源オフするようだ。
1.1.1.3 root 238: // #2-x はユーザ(?)
1.1 root 239:
240: // コンストラクタ
241: PIO0Device::PIO0Device()
1.1.1.8 root 242: : inherited("PIO0")
1.1 root 243: {
244: devaddr = 0x49000000;
245: devlen = 4;
1.1.1.2 root 246:
1.1.1.3 root 247: // 機種固有パラメータ
248:
249: // LUNA-1 と LUNA88K で使いやすいように初期値を変えておく。
250: switch (gMainApp.GetVMType()) {
251: case VMTYPE_LUNA1:
252: gConfig->SetDefault("luna-dipsw1", "11110111");
1.1.1.9 ! root 253: msb_first = false;
1.1.1.3 root 254: break;
255: case VMTYPE_LUNA88K:
1.1.1.4 root 256: gConfig->SetDefault("luna-dipsw1", "11111111");
1.1.1.9 ! root 257: msb_first = true;
1.1.1.3 root 258: break;
259: default:
260: __unreachable();
261: }
262: // DIP-SW#2 はユーザ定義(?)なので(今の所?)どちらも共通。
263: gConfig->SetDefault("luna-dipsw2", "11111111");
264:
1.1.1.8 root 265: monitor.func = (MonitorCallback_t)&PIO0Device::MonitorUpdate;
266: monitor.SetSize(22, 11);
267: monitor.Regist(ID_MONITOR_PIO0);
1.1 root 268: }
269:
270: // デストラクタ
271: PIO0Device::~PIO0Device()
272: {
273: }
274:
275: bool
276: PIO0Device::Init()
277: {
278: // XXX 本当はラッチのタイミングが違う気もするけど、とりあえずね
279:
1.1.1.2 root 280: const ConfigItem& item1 = gConfig->Find("luna-dipsw1");
1.1 root 281: const std::string& str1 = item1.AsString();
282: if (str1.size() != 8) {
283: item1.Err("must be 8 digits");
284: return false;
285: }
286:
1.1.1.2 root 287: const ConfigItem& item2 = gConfig->Find("luna-dipsw2");
1.1 root 288: const std::string& str2 = item2.AsString();
289: if (str2.size() != 8) {
290: item2.Err("must be 8 digits");
291: return false;
292: }
293: for (int i = 0; i < 8; i++) {
294: dipsw1[i] = (str1[i] != '0');
295: dipsw2[i] = (str2[i] != '0');
296: }
297: return true;
298: }
299:
1.1.1.7 root 300: // XXX PowerOn か ResetHard 未調査
301:
1.1 root 302: uint64
1.1.1.6 root 303: PIO0Device::Read(uint32 offset)
1.1 root 304: {
305: uint32 data;
306:
1.1.1.6 root 307: switch (offset) {
1.1 root 308: case 0:
309: // PortA
310: putlog(1, "DIP-SW 1 読み込み");
311: return GetPA();
312:
313: case 1:
314: // PortB
315: putlog(1, "DIP-SW 2 読み込み");
316: return GetPB();
317:
318: case 2:
319: // PortC
320: data = GetPC();
1.1.1.6 root 321: putlog(1, "PC 読み込み -> $%02x", data);
1.1 root 322: return data;
323:
324: case 3:
325: data = ReadCtrl();
326: putlog(1, "コントロール読み込み -> $%02x", data);
327: return data;
328:
329: default:
330: __unreachable();
331: }
332: }
333:
334: uint64
1.1.1.6 root 335: PIO0Device::Write(uint32 offset, uint32 data)
1.1 root 336: {
1.1.1.6 root 337: switch (offset) {
1.1 root 338: case 0: // PIO0 PortA: DIPSW1 (入力のみ)
339: case 1: // PIO0 PortB: DIPSW2 (入力のみ)
340: return 0;
341:
342: case 2: // PIO0 PortC: ホスト割り込み制御
343: for (int i = 0; i < 8; i++) {
344: SetPC(i, data & 1);
345: data >>= 1;
346: }
347: return 0;
348:
349: case 3: // コントロール
350: WriteCtrl(data);
351: return 0;
352:
353: default:
354: __unreachable();
355: }
356: }
357:
358: uint64
1.1.1.6 root 359: PIO0Device::Peek(uint32 offset)
1.1 root 360: {
1.1.1.6 root 361: switch (offset) {
1.1 root 362: case 0:
363: return GetPA();
364:
365: case 1:
366: return GetPB();
367:
368: case 2:
369: return GetPC();
370:
371: case 3:
372: return ReadCtrl();
373:
374: default:
375: __unreachable();
376: }
377: }
378:
1.1.1.3 root 379: void
1.1.1.8 root 380: PIO0Device::MonitorUpdate(Monitor *, TextScreen& screen)
1.1.1.2 root 381: {
382: int y;
383:
1.1.1.8 root 384: screen.Clear();
1.1.1.2 root 385:
386: y = 0;
1.1.1.8 root 387: screen.Print(0, y++, "PortA(DIPSW1):%c%c%c%c%c%c%c%c",
1.1.1.2 root 388: (dipsw1[0] ? '1' : '0'),
389: (dipsw1[1] ? '1' : '0'),
390: (dipsw1[2] ? '1' : '0'),
391: (dipsw1[3] ? '1' : '0'),
392: (dipsw1[4] ? '1' : '0'),
393: (dipsw1[5] ? '1' : '0'),
394: (dipsw1[6] ? '1' : '0'),
395: (dipsw1[7] ? '1' : '0'));
1.1.1.8 root 396: screen.Print(0, y++, "PortB(DIPSW2):%c%c%c%c%c%c%c%c",
1.1.1.2 root 397: (dipsw2[0] ? '1' : '0'),
398: (dipsw2[1] ? '1' : '0'),
399: (dipsw2[2] ? '1' : '0'),
400: (dipsw2[3] ? '1' : '0'),
401: (dipsw2[4] ? '1' : '0'),
402: (dipsw2[5] ? '1' : '0'),
403: (dipsw2[6] ? '1' : '0'),
404: (dipsw2[7] ? '1' : '0'));
1.1.1.8 root 405: screen.Puts(0, y++, "PortC");
406: screen.Puts(1, y++, TA::OnOff(xp_reset), "b7: XP Reset");
407: screen.Puts(1, y++, TA::OnOff(parity), "b6: Parity");
1.1.1.9 ! root 408: screen.Puts(1, y++, TA::Disable, "b5: ---");
1.1.1.8 root 409: screen.Puts(1, y++, TA::OnOff(int5en), "b4: Int5 Enable");
410: screen.Puts(1, y++, TA::OnOff(int5rq), "b3: Int5 Request");
411: screen.Puts(1, y++, TA::OnOff(int1en), "b2: Int1 Enable");
1.1.1.9 ! root 412: screen.Puts(1, y++, TA::Disable, "b1: ---");
1.1.1.8 root 413: screen.Puts(1, y++, TA::OnOff(int1rq), "b0: Intq Request");
1.1.1.2 root 414: }
415:
1.1 root 416: // PortA (DIP-SW 1) 読み込み
417: uint32
418: PIO0Device::GetPA() const
419: {
420: uint32 data = 0;
421: for (int i = 0; i < 8; i++) {
422: data |= ((int)dipsw1[i]) << i;
423: }
1.1.1.9 ! root 424: if (msb_first) {
! 425: data = bitrev(data);
! 426: }
1.1 root 427: return data & 0xff;
428: }
429:
430: // PortB (DIP-SW 2) 読み込み
431: uint32
432: PIO0Device::GetPB() const
433: {
434: uint32 data = 0;
435: for (int i = 0; i < 8; i++) {
436: data |= ((int)dipsw2[i]) << i;
437: }
1.1.1.9 ! root 438: if (msb_first) {
! 439: data = bitrev(data);
! 440: }
1.1 root 441: return data & 0xff;
442: }
443:
444: // PortC 読み込み
445: uint32
446: PIO0Device::GetPC() const
447: {
448: uint32 data;
449:
450: data = 0x00 // XXX 空きビットは何が読めるか?
451: | (int1rq ? 0x01 : 0)
452: | (int1en ? 0x04 : 0)
453: | (int5rq ? 0x08 : 0)
454: | (int5en ? 0x10 : 0)
455: | (parity ? 0x40 : 0)
456: | (xp_reset?0x80 : 0);
457: return data;
458: }
459:
460: // PortC 書き込み
461: void
462: PIO0Device::SetPC(int pc, int val)
463: {
464: switch (pc) {
465: case 0:
466: if ((val ^ int1rq)) {
467: int1rq = val;
468: putlog(0, "INT1割り込み要求 %d (未実装)", int1rq);
469: }
470: return;
471: case 1:
472: // Not Connected.
473: return;
474: case 2:
475: if ((val ^ int1en)) {
476: int1en = val;
477: putlog(0, "INT1割り込み許可 %d (未実装)", int1en);
478: }
479: return;
480: case 3:
481: if ((val ^ int5rq)) {
482: int5rq = val;
483: putlog(0, "INT5割り込み要求 %d (未実装)", int5rq);
484: }
485: return;
486: case 4:
487: if ((val ^ int5en)) {
488: int5en = val;
489: putlog(0, "INT5割り込み許可 %d (未実装)", int5en);
490: }
491: return;
492: case 5:
493: // Not Connected.
494: return;
495: case 6:
496: // パリティ有効/無効
497: // 使わないので無視するが読み返しのために覚えておく
498: parity = val;
499: return;
500: case 7:
501: // XP リセット
502: if ((val ^ xp_reset)) {
503: xp_reset = val;
504: putlog(0, "XPリセット %d (未実装)", xp_reset);
505: }
506: return;
507: }
508: PANIC("invalid pc=%d", pc);
509: }
510:
511:
512: //
513: // LUNA PIO1
514: //
515: // 仕様は 0x4d000000 から 4バイトだが、
516: // 0x4c000000 から 0x50000000 の手前まで 4バイト単位でミラーが見える。
517:
518: // コンストラクタ
519: PIO1Device::PIO1Device()
1.1.1.8 root 520: : inherited("PIO1")
1.1 root 521: {
522: devaddr = 0x4d000000;
523: devlen = 4;
524:
525: poffevent.func = (DeviceCallback_t)&PIO1Device::PowerOffCallback;
526: poffevent.SetName("PIO1 Power Off");
527: }
528:
529: // デストラクタ
530: PIO1Device::~PIO1Device()
531: {
532: }
533:
1.1.1.4 root 534: // リセット
535: void
536: PIO1Device::ResetHard()
537: {
538: inherited::ResetHard();
539:
540: poffevent.Stop();
541: }
542:
1.1 root 543: uint64
1.1.1.6 root 544: PIO1Device::Read(uint32 offset)
1.1 root 545: {
546: uint32 data;
547:
1.1.1.6 root 548: switch (offset) {
1.1 root 549: case 0:
550: // PIO1 PortA: LCD データ
551: return gLCD->Read();
552:
553: case 1:
554: // PIO1 PortB: 無効
555: return 0xff;
556:
557: case 2:
558: // PortC
559: data = gLCD->Get();
560: data |= xp_intreq ? 0 : 0x02;
561: // 残りのビットは %1 だろうか?
562: data |= 0x0d;
563: putlog(1, "PC 読み込み -> $%02x", data);
564: return data;
565:
566: case 3:
567: data = ReadCtrl();
568: putlog(1, "コントロール読み込み -> $%02x", data);
569: return data;
570:
571: default:
572: __unreachable();
573: }
574: }
575:
576: uint64
1.1.1.6 root 577: PIO1Device::Write(uint32 offset, uint32 data)
1.1 root 578: {
1.1.1.6 root 579: switch (offset) {
1.1 root 580: case 0: // PIO1 PortA: LCD データ
581: gLCD->Write(data);
582: return 0;
583:
584: case 1: // PIO1 PortB: ローカル割り込み
585: break;
586:
587: case 2: // PIO1 PortC: LCD/パワーオフ制御
588: for (int i = 0; i < 8; i++) {
589: SetPC(i, data & 1);
590: data >>= 1;
591: }
592: return 0;
593:
594: case 3: // コントロール
595: WriteCtrl(data);
596: return 0;
597:
598: default:
599: __unreachable();
600: }
1.1.1.6 root 601: PANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data);
1.1 root 602: return 0;
603: }
604:
605: uint64
1.1.1.6 root 606: PIO1Device::Peek(uint32 offset)
1.1 root 607: {
608: uint32 data;
609:
1.1.1.6 root 610: switch (offset) {
1.1 root 611: case 0: // PIO1 PortA LCD データ
612: return gLCD->Peek();
613:
614: case 1: // PIO1 PortB 無効
615: return 0xff;
616:
617: case 2: // PIO1 PortC
618: data = gLCD->Get();
619: data |= xp_intreq ? 0 : 0x02;
620: // 残りのビットは %1 だろうか?
621: data |= 0x0d;
622: return data;
623:
624: case 3:
625: return ReadCtrl();
626:
627: default:
628: __unreachable();
629: }
630: }
631:
632: void
633: PIO1Device::SetPC(int pc, int val)
634: {
635: switch (pc) {
636: case 0:
637: case 1:
638: case 2:
639: case 3:
640: // PortC の下半分は入力ポートとして使われることを想定して
641: // いるので、ここへの書き込みは起きないだろうし、無視する。
642: return;
643:
644: case 4:
645: power = val;
646: if (power) {
647: // 電源オフ取り消し
648: putlog(1, "電源 OFF 取消");
649: poffevent.Stop();
650: } else {
651: // 1msec 後に電源オフ
652: putlog(1, "電源 OFF 指示");
653: poffevent.time = 1_msec;
654: poffevent.Start();
655: }
656: return;
657: case 5:
658: gLCD->SetRW(val);
659: return;
660: case 6:
661: gLCD->SetRS(val);
662: return;
663: case 7:
664: gLCD->SetE(val);
665: return;
666:
667: default:
668: break;
669: }
670: PANIC("PC%d 未実装", pc);
671: }
672:
673: // 電源 OFF イベントのコールバック
674: void
1.1.1.4 root 675: PIO1Device::PowerOffCallback(Event& ev)
1.1 root 676: {
677: putlog(0, "電源 OFF");
678: gScheduler->RequestPowerOff();
679: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.