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