|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // uPD7201(MPSC) と Z8530(SCC) の共通部分 (シリアルポート1個つき)
9: //
10:
11: // IODevice
12: // |
13: // | +-------------+
14: // +--| MPSCCDevice | (uPD7201 と Z8530 の共通部分)
15: // +-------------+
16: // |
17: // +-- SCCDevice (X68030、Z8530)
18: // +-- SIODevice (LUNA、uPD7201)
19:
20: #define MPSCC_SIO_LOG_HACK
21: #include "mpscc.h"
22: #include "bitops.h"
23: #include "hostcom.h"
24: #include "interrupt.h"
25: #include "scheduler.h"
26: #include "sync.h"
27:
28: //
29: // MPSCC チャンネル
30: //
31:
32: // コンストラクタ
33: MPSCCChan::MPSCCChan()
34: {
35: }
36:
37: // デストラクタ
38: MPSCCChan::~MPSCCChan()
39: {
40: }
41:
42: // 初期化
43: void
44: MPSCCChan::Init(int id_)
45: {
46: id = id_;
47: name = 'A' + id;
48: }
49:
50:
51: //
52: // MPSCC デバイス
53: //
54:
55: // コンストラクタ
56: MPSCCDevice::MPSCCDevice(const std::string& objname_)
57: : inherited(objname_)
58: {
59: for (int ch = 0; ch < mpscc.chan.size(); ch++) {
60: auto& chan = mpscc.chan[ch];
61: chan.Init(ch);
62:
63: // time は SetTxPeriod() でセット。
64: // 名前は継承先でセットする。機種ごとに名前を使い分けているため。
65: txevent[ch].dev = this;
66: txevent[ch].func = ToEventCallback(&MPSCCDevice::TxCallback);
67: txevent[ch].code = ch;
68: txevent[ch].Regist();
69:
70: rxevent[ch].dev = this;
71: rxevent[ch].func = ToEventCallback(&MPSCCDevice::RxCallback);
72: rxevent[ch].code = ch;
73: rxevent[ch].Regist();
74: }
75:
76: // モニタは継承先で設定する
77: }
78:
79: // デストラクタ
80: MPSCCDevice::~MPSCCDevice()
81: {
82: if ((bool)hostcom) {
83: hostcom->SetCallbackDevice(NULL);
84: }
85: }
86:
87: bool
88: MPSCCDevice::Create()
89: {
90: // 対応するホストドライバを作成
91: try {
92: hostcom.reset(new HostCOMDevice("HostCOM"));
93: } catch (...) {
94: return false;
95: }
96:
97: hostcom->SetRxCallback(ToDeviceCallback(&MPSCCDevice::HostRxCallback));
98: hostcom->SetCallbackDevice(this);
99:
100: return true;
101: }
102:
103: void
104: MPSCCDevice::SetLogLevel(int loglevel_)
105: {
106: inherited::SetLogLevel(loglevel_);
107:
108: assert((bool)hostcom);
109: hostcom->SetLogLevel(loglevel_);
110: }
111:
112: // 初期化
113: bool
114: MPSCCDevice::Init()
115: {
116: gScheduler->ConnectMessage(MessageID::HOSTCOM_RX, this,
117: ToMessageCallback(&MPSCCDevice::RxMessageCallback));
118: return true;
119: }
120:
121: // チャンネルリセットの共通部
122: void
123: MPSCCDevice::ResetChannelCommon(MPSCCChan& chan)
124: {
125: // 通信速度を反映する
126: SetTxPeriod(chan);
127: SetRxPeriod(chan);
128:
129: // リセットで送信バッファは空になるが、リセット直後に
130: // Tx 割り込みを許可しても Tx 割り込みは起こさない。
131: chan.txint_ready = false;
132:
133: // 割り込みをネゲートするため
134: ChangeInterrupt();
135: }
136:
137: // CR0/WR0 レジスタの内容を取得
138: // (値の取り出しだけなら共通)
139: uint8
140: MPSCCDevice::GetCR0(const MPSCCChan& chan) const
141: {
142: uint8 data = (chan.crc_cmd << 6) | (chan.cmd << 3) | chan.ptr;
143: return data;
144: }
145:
146: // CR3/WR3 レジスタの内容を取得
147: uint8
148: MPSCCDevice::GetCR3(const MPSCCChan& chan) const
149: {
150: uint8 data;
151:
152: data = chan.cr3 & ~MPSCC::CR3_RX_EN;
153: if (chan.rx_enable)
154: data |= MPSCC::CR3_RX_EN;
155:
156: return data;
157: }
158:
159: // CR5/WR5 レジスタの内容を取得
160: uint8
161: MPSCCDevice::GetCR5(const MPSCCChan& chan) const
162: {
163: uint8 data;
164:
165: data = chan.cr5 & ~MPSCC::CR5_TX_EN;
166: if (chan.tx_enable)
167: data |= MPSCC::CR5_TX_EN;
168:
169: return data;
170: }
171:
172: // SR2B/RR2B レジスタの内容を取得
173: uint8
174: MPSCCDevice::GetSR2B() const
175: {
176: uint8 data = mpscc.vector;
177:
178: // 割り込み要因をベクタに載せない
179: if (mpscc.vis == MPSCC::VIS_FIXED) {
180: return data;
181: }
182:
183: uint stat = GetHighestInt();
184:
185: // 8種類 (3bit) の割り込み要因をベクタに載せる。
186: // 載せる場所 (と向き) はそれぞれ 2種類ずつある。
187: switch (mpscc.vis) {
188: case MPSCC::VIS_432: // uPD7201: INTMode = INT85
189: // 割り込み要求がない場合は %111
190: if (stat == MPSCC::VS_none)
191: stat = 7;
192: data &= ~0x1c;
193: data |= stat << 2;
194: break;
195:
196: case MPSCC::VIS_210: // uPD7201: INTMode = INT86
197: // 割り込み要求がない場合は %111
198: if (stat == MPSCC::VS_none)
199: stat = 7;
200: data &= ~0x07;
201: data |= stat;
202: break;
203:
204: case MPSCC::VIS_321: // Z8530: StatusHigh/StatusLow = %0
205: // 割り込み要求がない場合は %011
206: if (stat == MPSCC::VS_none)
207: stat = 3;
208: data &= ~0x0e;
209: data |= stat << 1;
210: break;
211:
212: case MPSCC::VIS_456: // Z8530: StatusHigh/StatusLow = %1
213: // 割り込み要求がない場合は %011
214: if (stat == MPSCC::VS_none)
215: stat = 3;
216: // VIS_321 とはビット順が逆
217: data &= ~0x70;
218: data |= bitrev(stat << 1);
219: break;
220: default:
221: __unreachable();
222: }
223:
224: return data;
225: }
226:
227: // CR0/WR0 Send Abort コマンド
228: void
229: MPSCCDevice::SendAbort(MPSCCChan& chan)
230: {
231: putlog(0, "%s: Command 'Send Abort' 未実装", CRName(chan, 0));
232: }
233:
234: // CR0/WR0 Reset External/Status Interrupt コマンド
235: void
236: MPSCCDevice::ResetESIntr(MPSCCChan& chan)
237: {
238: putlog(3, "%s: Command 'Reset E/S Interrupt'", CRName(chan, 0));
239: chan.es_latched = false;
240: }
241:
242: // CR0/WR0 Enable Interrupt on Next Rx Character コマンド
243: void
244: MPSCCDevice::EnableIntrOnNextRx(MPSCCChan& chan)
245: {
246: putlog(1, "%s: Command 'Enable Intr on Next Rx Character'",
247: CRName(chan, 0));
248: chan.all_or_first = true;
249: }
250:
251: // CR0/WR0 Reset Tx Interrupt/DMA Pending コマンド
252: void
253: MPSCCDevice::ResetTxIntrPending(MPSCCChan& chan)
254: {
255: putlog(2, "%s: Command 'Reset Tx Intr/DMA Pending'", CRName(chan, 0));
256: chan.txint_ready = false;
257: ChangeInterrupt();
258: }
259:
260: // CR0/WR0 Error Reset コマンド
261: void
262: MPSCCDevice::ErrorReset(MPSCCChan& chan)
263: {
264: putlog(1, "%s: Command 'Error Reset'", CRName(chan, 0));
265: chan.sr1 &= ~(MPSCC::SR1_ENDOFFRAME |
266: MPSCC::SR1_FRAMEERROR |
267: MPSCC::SR1_RXOVERRUN |
268: MPSCC::SR1_PARITYERROR);
269: // XXX Special Rx Condition は未実装
270: }
271:
272: // CR0 End of Interrupt コマンド
273: // WR0 Reset Highest IUS コマンド
274: // たぶん名前が違うだけで同じ。後者のほうが分かりやすいのでこっちを使う
275: void
276: MPSCCDevice::ResetHighestIUS(MPSCCChan& chan)
277: {
278: putlog(2, "%s: Command 'Reset Highest IUS'", CRName(chan, 0));
279:
280: // ペンディングのうち最も高い割り込み要因をクリア
281: uint stat = GetHighestInt();
282: switch (stat) {
283: case MPSCC::VS_TxB:
284: mpscc.chan[1].intpend &= ~INTPEND_TX;
285: break;
286: case MPSCC::VS_ESB:
287: mpscc.chan[1].intpend &= ~INTPEND_ES;
288: break;
289: case MPSCC::VS_RxB:
290: mpscc.chan[1].intpend &= ~INTPEND_RX;
291: break;
292: case MPSCC::VS_SPB:
293: mpscc.chan[1].intpend &= ~INTPEND_SP;
294: break;
295:
296: case MPSCC::VS_TxA:
297: mpscc.chan[0].intpend &= ~INTPEND_TX;
298: break;
299: case MPSCC::VS_ESA:
300: mpscc.chan[0].intpend &= ~INTPEND_ES;
301: break;
302: case MPSCC::VS_RxA:
303: mpscc.chan[0].intpend &= ~INTPEND_RX;
304: break;
305: case MPSCC::VS_SPA:
306: mpscc.chan[0].intpend &= ~INTPEND_SP;
307: break;
308:
309: default:
310: break;
311: }
312: ChangeInterrupt();
313: }
314:
315: // CR3/WR3 への書き込み
316: void
317: MPSCCDevice::WriteCR3(MPSCCChan& chan, uint32 data)
318: {
319: bool old_enable;
320:
321: old_enable = chan.rx_enable;
322: chan.cr3 = data;
323: chan.rx_enable = (data & MPSCC::CR3_RX_EN);
324:
325: putlog(2, "%s <- $%02x", CRName(chan), data);
326:
327: // Disable は、Enable から変化した時だけ表示したい
328: // (Enable は SetRxPeriod() 内で表示)
329: if (old_enable == true && chan.rx_enable == false) {
330: putlog(1, "Channel %c RX Disable", chan.name);
331: }
332:
333: SetRxPeriod(chan);
334: }
335:
336: // CR4/WR4 への書き込み
337: void
338: MPSCCDevice::WriteCR4(MPSCCChan& chan, uint32 data)
339: {
340: chan.cr4 = data;
341: putlog(2, "%s <- $%02x", CRName(chan), data);
342: uint8 stopbits = (chan.cr4 & MPSCC::CR4_STOPBITS);
343:
344: if (stopbits == MPSCC::STOPBITS_SYNC) {
345: // 同期モードはサポートしない
346: putlog(2, "Channel %c Synchronous mode not supported", chan.name);
347: } else {
348: const char *sm[] = { "?", "1", "1.5", "2" };
349: putlog(2, "Channel %c Async Mode (x%d clock, stopbit %s)",
350: chan.name,
351: clkrates[(chan.cr4 & MPSCC::CR4_CLKRATE) >> 6],
352: sm[stopbits >> 2]);
353: }
354:
355: SetTxPeriod(chan);
356: SetRxPeriod(chan);
357: }
358:
359: // CR5/WR5 への書き込み
360: void
361: MPSCCDevice::WriteCR5(MPSCCChan& chan, uint32 data)
362: {
363: bool old_enable;
364:
365: old_enable = chan.tx_enable;
366: chan.cr5 = data;
367: chan.tx_enable = (data & MPSCC::CR5_TX_EN);
368:
369: putlog(2, "%s <- $%02x", CRName(chan), data);
370:
371: // Disable は、Enable から変化した時だけ表示したい
372: // (Enable は SetTxPeriod() 内で表示)
373: if (old_enable == true && chan.tx_enable == false) {
374: putlog(1, "Channel %c TX Disable", chan.name);
375: }
376:
377: SetTxPeriod(chan);
378: TrySend(chan);
379: }
380:
381: // CR6/WR6 への書き込み
382: void
383: MPSCCDevice::WriteCR6(MPSCCChan& chan, uint32 data)
384: {
385: chan.cr6 = data;
386:
387: putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data);
388: }
389:
390: // CR7/WR7 への書き込み
391: void
392: MPSCCDevice::WriteCR7(MPSCCChan& chan, uint32 data)
393: {
394: chan.cr7 = data;
395:
396: putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data);
397: }
398:
399: // データポートの読み込み
400: uint8
401: MPSCCDevice::ReadData(MPSCCChan& chan)
402: {
403: uint8 data;
404:
405: // 受信バッファが空でも直近のデータが読めるので常に [0] でよい
406: data = chan.rxbuf[0];
407: if (chan.rxlen > 0) {
408: putlog(3, "Ch%c Data -> $%02x", chan.name, data);
409: chan.rxlen--;
410: if (chan.rxlen == 0) {
411: // 取り出して空になった
412: ChangeInterrupt();
413: } else {
414: chan.rxbuf[0] = chan.rxbuf[1];
415: chan.rxbuf[1] = chan.rxbuf[2];
416: }
417: } else {
418: putlog(3, "Ch%c Data -> $%02x (Empty)", chan.name, data);
419: }
420:
421: return data;
422: }
423:
424: // データポートの書き込み
425: void
426: MPSCCDevice::WriteData(MPSCCChan& chan, uint32 data)
427: {
428: char charbuf[4];
429:
430: if (isprint((int)data)) {
431: snprintf(charbuf, sizeof(charbuf), "'%c'", data);
432: } else {
433: charbuf[0] = '\0';
434: }
435: putlog(3, "Ch%c Data <- $%02x %s", chan.name, data, charbuf);
436:
437: // XXX: EMPTY でないときにデータを書いたらどうなるかは未定義
438:
439: // 1byte の TX Buffer
440: chan.txlen = 1;
441: chan.txbuf = data;
442:
443: // データ書き込み後に Empty になったら割り込みを上げるため
444: // ここで ready をセット
445: chan.txint_ready = true;
446:
447: TrySend(chan);
448: }
449:
450: // データポートの Peek
451: uint8
452: MPSCCDevice::PeekData(const MPSCCChan &chan) const
453: {
454: // 受信バッファにデータがあれば先頭バイトが見える。
455: // 受信バッファが空でも直近に取り出したバイトがそのまま見える。
456: return chan.rxbuf[0];
457: }
458:
459: // 送信できるか調べて送信する
460: void
461: MPSCCDevice::TrySend(MPSCCChan& chan)
462: {
463: // 送信可で送信バッファにデータがあって送信中でなければ
464: if (chan.tx_enable && chan.txlen && !txevent[chan.id].IsRunning()) {
465: // 送信する
466: chan.tx_shiftreg = chan.txbuf;
467: chan.txlen = 0;
468: gScheduler->StartEvent(txevent[chan.id]);
469: putlog(2, "Ch%c send $%02x", chan.name, chan.tx_shiftreg);
470:
471: // XXX たぶん送信してから割り込みを上げるまでにいくらか遅れがあるはず
472: }
473:
474: // ネゲートされることがあるので、送信しなくても割り込み状態を更新する
475: ChangeInterrupt();
476: }
477:
478: // 送信のイベントコールバック
479: void
480: MPSCCDevice::TxCallback(Event& event)
481: {
482: int ch = event.code;
483: MPSCCChan& chan = mpscc.chan[ch];
484:
485: // 接続デバイスの呼び出し
486: Tx(ch, chan.tx_shiftreg);
487:
488: // 次の送信データが来てたら送信
489: TrySend(chan);
490: }
491:
492: // TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換する。
493: /*static*/ const int
494: MPSCCDevice::bits_per_char[] = { 5, 7, 6, 8 };
495:
496: // CR4 CLKRATE のレジスタ値を実際の倍率に変換する。
497: /*static*/ const int
498: MPSCCDevice::clkrates[] = { 1, 16, 32, 64 };
499:
500: // ホストスレッドからの受信通知
501: // (HostCOM スレッドで呼ばれる)
502: void
503: MPSCCDevice::HostRxCallback()
504: {
505: // スレッドを超えるためにメッセージを投げる
506: gScheduler->SendMessage(MessageID::HOSTCOM_RX);
507: }
508:
509: // 1バイト受信する。
510: // 受理されれば true を返す。
511: bool
512: MPSCCDevice::Rx(int ch, uint32 data)
513: {
514: MPSCCChan& chan = mpscc.chan[ch];
515:
516: // Rx Enable でなければ受け取らない?
517: if (chan.rx_enable == false) {
518: return false;
519: }
520:
521: // バッファが一杯なら Rx Overrun Error
522: if (chan.rxlen == sizeof(chan.rxbuf)) {
523: putlog(2, "Ch%c Rx Overrun", chan.name);
524: chan.sr1 |= MPSCC::SR1_RXOVERRUN;
525: // XXX special Rx condition interrupt occurs
526: return false;
527: }
528:
529: // 受信
530: putlog(2, "Ch%c recv $%02x", chan.name, data);
531: chan.rxbuf[chan.rxlen++] = data;
532:
533: // 割り込み状態を変更
534: ChangeInterrupt();
535:
536: return true;
537: }
538:
539: // ホストシリアルからの1バイト受信メッセージ
540: void
541: MPSCCDevice::RxMessageCallback(MessageID msgid, uint32 arg)
542: {
543: Event& ev = rxevent[COM_CH];
544:
545: // 受信ループが止まっていれば開始
546: if (ev.IsRunning() == false) {
547: gScheduler->StartEvent(ev);
548: }
549: }
550:
551: // 受信イベントコールバック
552: void
553: MPSCCDevice::RxCallback(Event& ev)
554: {
555: int ch = ev.code;
556: int data;
557:
558: // ホストキューにあるだけ取り出す
559: data = hostcom->Rx();
560: if (data >= 0) {
561: Rx(ch, data);
562:
563: gScheduler->StartEvent(ev);
564: }
565: }
566:
567: static double
568: to_freq(uint64 nsec)
569: {
570: return (double)1.0 / nsec * 1e9;
571: }
572:
573: // 送受信タイミングのうち CR4 から分かる共通部分を計算。
574: // enable は tx_enable or rx_enable、
575: // bitp は IN/OUT パラメータで、
576: // 呼び出し時は CR3 or CR5 から求まる送受信のデータビット数を渡し、
577: // パリティやストップビット等を付与したフレーム内のビット数を書き戻される。
578: // *evtime にはイベント間隔 [nsec] を書き戻す。
579: std::string
580: MPSCCDevice::SetPeriod(MPSCCChan& chan, bool enable, int *bitp, uint64 *evtime)
581: {
582: uint64 bit12_ns;
583: int bit;
584:
585: // 誤差を減らすため 12bit 単位が基準
586: // 12bit 時間
587: bit12_ns = xc12_ns * clkrates[(chan.cr4 & MPSCC::CR4_CLKRATE) >> 6];
588:
589: bit = *bitp;
590:
591: // start bit
592: bit += 1;
593:
594: // parity bit
595: bit += (chan.cr4 & MPSCC::CR4_PARITY_EN) ? 1 : 0;
596:
597: // stop bit
598: switch (chan.cr4 & MPSCC::CR4_STOPBITS) {
599: case MPSCC::STOPBITS_SYNC:
600: // 非同期モードでの STOPBITS 0 は未定義 (Enable ならログ表示)
601: if (enable) {
602: putlog(0, "%s 未定義ストップビット長 0", CRName(chan, 4));
603: }
604: break;
605: case MPSCC::STOPBITS_1:
606: bit += 1;
607: break;
608: case MPSCC::STOPBITS_1_5:
609: // サポートしない (Enable ならログ表示)
610: if (enable) {
611: putlog(0, "%s 未実装ストップビット長 1.5", CRName(chan, 4));
612: }
613: break;
614: case MPSCC::STOPBITS_2:
615: bit += 2;
616: break;
617: }
618: *bitp = bit;
619:
620: *evtime = bit * bit12_ns / 12;
621:
622: // 表示用
623: chan.baudrate = (double)to_freq(bit12_ns) * 12;
624:
625: // Enable なら表示用のログメッセージを作成して返す
626: if (enable) {
627: std::string nsstr;
628: if (loglevel >= 2) {
629: nsstr = string_format(" (%" PRIu64 ")", bit12_ns);
630: }
631: return string_format("%.1f baud%s, %d bits/frame",
632: chan.baudrate,
633: nsstr.c_str(),
634: bit);
635: }
636:
637: return "";
638: }
639:
640: // 受信イベントの時間を設定する (CR3, CR4 の変更で呼ぶ)
641: void
642: MPSCCDevice::SetRxPeriod(MPSCCChan& chan)
643: {
644: uint64 evtime;
645: int bit;
646:
647: // 受信データビット数
648: bit = bits_per_char[(chan.cr3 & MPSCC::CR3_RXBITS) >> 6];
649:
650: // CR4 などからイベント時間を求めてセット
651: // XXX 速度などのパラメータは送受信端双方で一致していることにする。
652: std::string msg = SetPeriod(chan, chan.rx_enable, &bit, &evtime);
653: chan.rxbits = bit;
654: rxevent[chan.id].time = evtime;
655:
656: // RX Enable (CR3) ならパラメータを含めて表示
657: if (chan.rx_enable) {
658: putlog(1, "Channel %c RX Enable, %s", chan.name, msg.c_str());
659: }
660:
661: // パフォーマンス測定用のギミック。
662: // シリアルポートを特定の設定 (本体 5bit、ストップビット 2bit) にすると、
663: // VM 電源オン時から現在までの実時間をログに出力する。NetBSD/OpenBSD の
664: // マルチユーザブートがあらかた終わってログインプロンプトが出る手前の
665: // /etc/rc.local に
666: //
667: // stty -f /dev/ttya cs5 cstopb; stty -f /dev/ttya cs8 -cstopb
668: //
669: // と書いておくことで、ここまでにかかる時間を表示できる。
670: // この値はホスト性能に依存するため、同一環境下での相対比較のみ可能。
671: // シリアルポートの設定を (通常ではまず使わなそうなやつに) 変えてすぐ戻す
672: // だけなので、実機でも副作用はないはず。
673: if ((chan.cr3 & MPSCC::CR3_RXBITS) == MPSCC::RXBITS_5 &&
674: (chan.cr4 & MPSCC::CR4_STOPBITS) == MPSCC::STOPBITS_2)
675: {
676: gSync->PutlogRealtime();
677: }
678: }
679:
680: // 送信イベントの時間を設定する (CR4, CR5 の変更で呼ぶ)
681: void
682: MPSCCDevice::SetTxPeriod(MPSCCChan& chan)
683: {
684: uint64 evtime;
685: int bit;
686:
687: // 送信データビット数
688: bit = bits_per_char[(chan.cr5 & MPSCC::CR5_TXBITS) >> 5];
689:
690: // CR4 などからイベント時間を求めてセット
691: std::string msg = SetPeriod(chan, chan.tx_enable, &bit, &evtime);
692: chan.txbits = bit;
693: txevent[chan.id].time = evtime;
694:
695: // TX Enable (CR5) ならパラメータを含めて表示
696: if (chan.tx_enable) {
697: putlog(1, "Channel %c TX Enable, %s", chan.name, msg.c_str());
698: }
699: }
700:
701: // 割り込み状態を変更
702: //
703: // uPD7201 の場合:
704: //
705: // RxINT Disable (CR1 b4,3=%00) -->o--+ (注1)
706: // |
707: // o RxCharAvailable ----|& |
708: // |&---|OR +--|&
709: // All/First RxChar ----|& |OR |&--+
710: // |OR---------|& |
711: // o Special Rx Condition -----|OR +---|OR
712: // |OR ---
713: // o External/Status ------------------------------|OR-->o-- INT
714: // |OR
715: // o Tx Buffer Empty ----------|& +---|OR
716: // |&-----|& |
717: // Tx INT/DMA Enable --------|& |& |
718: // (CR1 b1) |&-------+
719: // |&
720: // INT/DMA Mode (CR2A b1,0) --------|& (注2)
721: //
722: // 注1: uPD7201/7201A Figure 3-2 では "Rx Disable (CR1:D1)" だがそこは
723: // Tx INT/DMA Enable ビットなので名称もビット位置も誤り。
724: // uPD7201 ユーザーズマニュアル p.23 の 図3-2 では "Rx Disable (CR1:D4-3=00)"
725: // とあるのでこれのビット位置が正しければ Rx Int Disable が正しいはず。
726: //
727: // 注2: uPD7201 のみ。Z8530 ではこの選択肢はないので常に true のはず。
728: //
729: void
730: MPSCCDevice::ChangeInterrupt()
731: {
732: uint total_intpend = 0;
733: bool int_enable[2];
734:
735: // uPD7201 の CR1 INT/DMA Mode による制御。
736: // Z8530 では intdma_mode = INT_INT 固定。
737: //
738: // ついでにここで txint_enable も演算しておく。見た目は
739: // 無駄っぽいけどコンパイラで最適化されるので気にしない。
740: if (__predict_true(mpscc.intdma_mode == MPSCC::INTDMA_INT_INT)) {
741: int_enable[0] = mpscc.chan[0].txint_enable && true; // ChA INT
742: int_enable[1] = mpscc.chan[1].txint_enable && true; // ChB INT
743: } else if (mpscc.intdma_mode == MPSCC::INTDMA_DMA_INT) {
744: int_enable[0] = mpscc.chan[0].txint_enable && false; // ChA DMA
745: int_enable[1] = mpscc.chan[1].txint_enable && true; // ChB INT
746: } else {
747: int_enable[0] = mpscc.chan[0].txint_enable && false; // ChA DMA
748: int_enable[1] = mpscc.chan[1].txint_enable && false; // ChB DMA
749: }
750:
751: for (auto& chan : mpscc.chan) {
752: // Rx
753: bool rx_avail = (chan.rxlen > 0) && chan.all_or_first;
754: // Special Rx (XXX not yet)
755: bool special = false;
756: bool rx_int = (chan.rxint_mode != MPSCCChan::RXINT_DISABLE) &&
757: (rx_avail || special);
758:
759: // FirstChar モードなら1文字受信割り込み後にフラグを下ろす
760: // (Rx 割り込み用の変数 rx_int が確定したここで行う)
761: if (chan.rxint_mode == MPSCCChan::RXINT_1STCHAR) {
762: chan.all_or_first = false;
763: }
764:
765: // XXX E/S not yet
766: bool es_int = false;
767:
768: // Tx
769: bool tx_int = int_enable[chan.id] &&
770: chan.txint_ready && (chan.txlen == 0);
771:
772: uint intpend = 0;
773: if (rx_int)
774: intpend |= INTPEND_RX;
775: if (tx_int)
776: intpend |= INTPEND_TX;
777: if (es_int)
778: intpend |= INTPEND_ES;
779: chan.intpend = intpend;
780:
781: total_intpend |= chan.intpend;
782: }
783:
784: // いずれかでもあれば割り込みをアサート
785: gInterrupt->ChangeINT(this, total_intpend);
786: }
787:
788: // "9600,N,8,1" みたいなのを返す
789: std::string
790: MPSCCDevice::GetParamStr(const MPSCCChan& chan) const
791: {
792: std::string rv;
793: char parity;
794: char bits;
795: char stopbits;
796:
797: int parity_idx = 0;
798: if ((chan.cr4 & MPSCC::CR4_PARITY_EN)) {
799: parity_idx++;
800: if ((chan.cr4 & MPSCC::CR4_PARITY_EVEN)) {
801: parity_idx++;
802: }
803: }
804: parity = "NOE"[parity_idx];
805:
806: // bits は RX==TX のときのみ有効出力
807: // 違うときは '?' にしておく。
808: int rxbits = (chan.cr3 >> 6) & 3;
809: int txbits = (chan.cr5 >> 5) & 3;
810: if (rxbits == txbits) {
811: bits = '0' + bits_per_char[rxbits];
812: } else {
813: bits = '?';
814: }
815:
816: // stop bit は 1, 2 のみ有効出力。
817: // SYNC とか 1.5 bit とかは '?' にしておく。
818: switch ((chan.cr4 & MPSCC::CR4_STOPBITS)) {
819: case MPSCC::STOPBITS_1:
820: stopbits = '1';
821: break;
822: case MPSCC::STOPBITS_2:
823: stopbits = '2';
824: break;
825: default:
826: stopbits = '?';
827: break;
828: }
829:
830: return string_format("%d,%c,%c,%c",
831: (int)chan.baudrate,
832: parity,
833: bits,
834: stopbits);
835: }
836:
837: // モニタに CR3/WR3、CR4/WR4、CR5/WR5 の内容を書き出す。
838: // 更新後の y を返す。
839: int
840: MPSCCDevice::MonitorUpdateCR345(TextScreen& screen, int x, int y,
841: uint8 cr3, uint8 cr4, uint8 cr5)
842: {
843: // CR3/WR3
844: static const char * const cr3_str[] = {
845: "", "", "Auto", "", "", "", "", "RxEn"
846: };
847: MonitorReg(screen, x + 9, y, cr3, cr3_str);
848: screen.Print(x + 9, y, "RxBits=%d", bits_per_char[(cr3 >> 6) & 3]);
849: // b1-4 は非同期モードでは使わないのでグレーにしておく
850: screen.Puts(x + 24, y, TA::Disable, (cr3 & 0x10) ? "1" : "0");
851: screen.Puts(x + 29, y, TA::Disable, (cr3 & 0x08) ? "1" : "0");
852: screen.Puts(x + 34, y, TA::Disable, (cr3 & 0x04) ? "1" : "0");
853: screen.Puts(x + 39, y, TA::Disable, (cr3 & 0x02) ? "1" : "0");
854: y++;
855:
856: // CR4/WR4
857: static const char * const cr4_str[] = {
858: "", "", "", "", "", "", "PaEv", "PaEn"
859: };
860: MonitorReg(screen, x + 9, y, cr4, cr4_str);
861: screen.Print(x + 9, y, "Rate=x%d", clkrates[(cr4 >> 6) & 3]);
862: static const char * const syncmode_str[] = {
863: "Mono", "Bi", "HDLC", "Ext"
864: };
865: uint stopbit = (cr4 >> 2) & 3;
866: screen.Print(x + 19, y, (stopbit == 0 ? TA::Normal : TA::Disable),
867: "Sync=%s", syncmode_str[(cr4 >> 4) & 3]);
868: static const char * const stopbit_str[] = {
869: "SyncMode",
870: "StopBit=1",
871: "Stop=1.5",
872: "StopBit=2"
873: };
874: screen.Puts(x + 29, y, stopbit_str[stopbit]);
875: y++;
876:
877: // CR5/WR5
878: static const char * const cr5_str[] = {
879: "!DTR", "", "", "SBrk", "TxEn", "CRC", "!RTS", "TCEn"
880: };
881: MonitorReg(screen, x + 9, y, cr5, cr5_str);
882: screen.Print(x + 14, y, "TxBits=%d", bits_per_char[(cr5 >> 5) & 3]);
883: y++;
884:
885: return y;
886: }
887:
888: // モニタに SR1/RR1 の内容を書き出す。
889: void
890: MPSCCDevice::MonitorUpdateSR1(TextScreen& screen, int x, int y, uint8 sr1)
891: {
892: static const char * const sr1_str[] = {
893: "EOF", "FrEr", "RxOv", "PaEr", "", "", "", "Sent"
894: };
895: MonitorReg(screen, x + 9, y, sr1, sr1_str);
896: screen.Print(x + 29, y, TA::Disable, "ResidueCode=%d", (sr1 >> 1) & 7);
897: }
898:
899: // レジスタをビットごとに表示する。MonitorUpdate の下請け。
900: void
901: MPSCCDevice::MonitorReg(TextScreen& screen,
902: int x, int y, uint32 reg, const char * const *names)
903: {
904: for (int i = 0; i < 8; i++) {
905: if (names[i][0] == '-') {
906: screen.Puts(x + i * 5, y, TA::Disable, "----");
907: } else {
908: bool b = reg & (1 << (7 - i));
909: screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]);
910: }
911: }
912: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.