|
|
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"
1.1.1.3 ! root 26: #include "syncer.h"
1.1.1.2 root 27: #include <cmath>
1.1 root 28:
29: //
30: // MPSCC チャンネル
31: //
32:
33: // コンストラクタ
34: MPSCCChan::MPSCCChan()
35: {
1.1.1.2 root 36: // これらのレジスタの値の初期値は実際は不定だがそれは実際難しいのと
37: // 有効範囲内の値にしておく必要があるので、適当に初期値を固定しておく。
38: rxbits = 5;
39: txbits = 5;
40: clkrate = 1;
1.1 root 41: }
42:
43: // デストラクタ
44: MPSCCChan::~MPSCCChan()
45: {
46: }
47:
48: // 初期化
49: void
50: MPSCCChan::Init(int id_)
51: {
52: id = id_;
53: name = 'A' + id;
54: }
55:
56:
57: //
58: // MPSCC デバイス
59: //
60:
61: // コンストラクタ
1.1.1.3 ! root 62: MPSCCDevice::MPSCCDevice()
! 63: : inherited(OBJ_MPSCC)
1.1 root 64: {
65: for (int ch = 0; ch < mpscc.chan.size(); ch++) {
66: auto& chan = mpscc.chan[ch];
67: chan.Init(ch);
68:
69: // time は SetTxPeriod() でセット。
70: // 名前は継承先でセットする。機種ごとに名前を使い分けているため。
71: txevent[ch].dev = this;
72: txevent[ch].func = ToEventCallback(&MPSCCDevice::TxCallback);
73: txevent[ch].code = ch;
74: txevent[ch].Regist();
75:
76: rxevent[ch].dev = this;
77: rxevent[ch].func = ToEventCallback(&MPSCCDevice::RxCallback);
78: rxevent[ch].code = ch;
79: rxevent[ch].Regist();
80: }
81:
82: // モニタは継承先で設定する
83: }
84:
85: // デストラクタ
86: MPSCCDevice::~MPSCCDevice()
87: {
88: if ((bool)hostcom) {
89: hostcom->SetCallbackDevice(NULL);
90: }
91: }
92:
93: bool
94: MPSCCDevice::Create()
95: {
96: // 対応するホストドライバを作成
97: try {
98: hostcom.reset(new HostCOMDevice("HostCOM"));
99: } catch (...) {
100: return false;
101: }
102:
103: hostcom->SetRxCallback(ToDeviceCallback(&MPSCCDevice::HostRxCallback));
104: hostcom->SetCallbackDevice(this);
105:
106: return true;
107: }
108:
109: void
110: MPSCCDevice::SetLogLevel(int loglevel_)
111: {
112: inherited::SetLogLevel(loglevel_);
113:
114: assert((bool)hostcom);
115: hostcom->SetLogLevel(loglevel_);
116: }
117:
118: // 初期化
119: bool
120: MPSCCDevice::Init()
121: {
1.1.1.3 ! root 122: if (inherited::Init() == false) {
! 123: return false;
! 124: }
! 125:
! 126: interrupt = GetInterruptDevice();
! 127:
! 128: scheduler->ConnectMessage(MessageID::HOSTCOM_RX, this,
1.1 root 129: ToMessageCallback(&MPSCCDevice::RxMessageCallback));
130: return true;
131: }
132:
133: // チャンネルリセットの共通部
134: void
135: MPSCCDevice::ResetChannelCommon(MPSCCChan& chan)
136: {
137: // 通信速度を反映する
1.1.1.2 root 138: ChangeBaudrate(chan);
1.1 root 139: SetTxPeriod(chan);
140: SetRxPeriod(chan);
141:
1.1.1.2 root 142: // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。
143: chan.old_cr3 = GetCR3(chan);
144: chan.old_cr4 = GetCR4(chan);
145: chan.old_cr5 = GetCR5(chan);
146:
1.1 root 147: // リセットで送信バッファは空になるが、リセット直後に
148: // Tx 割り込みを許可しても Tx 割り込みは起こさない。
149: chan.txint_ready = false;
150:
151: // 割り込みをネゲートするため
152: ChangeInterrupt();
153: }
154:
155: // CR0/WR0 レジスタの内容を取得
156: // (値の取り出しだけなら共通)
1.1.1.2 root 157: /*static*/ uint8
158: MPSCCDevice::GetCR0(const MPSCCChan& chan)
1.1 root 159: {
160: uint8 data = (chan.crc_cmd << 6) | (chan.cmd << 3) | chan.ptr;
161: return data;
162: }
163:
1.1.1.2 root 164: // CR3/WR3 レジスタを設定
165: /*static*/ void
166: MPSCCDevice::SetCR3(MPSCCChan& chan, uint32 data)
167: {
1.1.1.3 ! root 168: uint cr3_rxbits = data & MPSCC::CR3_RXBITS;
! 169: switch (cr3_rxbits) {
1.1.1.2 root 170: case MPSCC::RXBITS_5:
171: chan.rxbits = 5;
172: break;
173: case MPSCC::RXBITS_6:
174: chan.rxbits = 6;
175: break;
176: case MPSCC::RXBITS_7:
177: chan.rxbits = 7;
178: break;
179: case MPSCC::RXBITS_8:
180: chan.rxbits = 8;
181: break;
182: default:
1.1.1.3 ! root 183: VMPANIC("corrupted cr3_rxbits=$%x", cr3_rxbits);
1.1.1.2 root 184: }
185:
186: chan.auto_enable = (data & MPSCC::CR3_AUTO_EN);
187: chan.cr3_sync = (data & MPSCC::CR3_SYNC_MASK);
188: chan.rx_enable = (data & MPSCC::CR3_RX_EN);
189: }
190:
1.1 root 191: // CR3/WR3 レジスタの内容を取得
1.1.1.2 root 192: /*static*/ uint8
193: MPSCCDevice::GetCR3(const MPSCCChan& chan)
1.1 root 194: {
195: uint8 data;
196:
1.1.1.2 root 197: data = chan.cr3_sync;
198: switch (chan.rxbits) {
199: case 5:
200: data |= MPSCC::RXBITS_5;
201: break;
202: case 6:
203: data |= MPSCC::RXBITS_6;
204: break;
205: case 7:
206: data |= MPSCC::RXBITS_7;
207: break;
208: case 8:
209: data |= MPSCC::RXBITS_8;
210: break;
211: default:
1.1.1.3 ! root 212: VMPANIC("corrupted chan.rxbits=%d", chan.rxbits);
1.1.1.2 root 213: }
214: if (chan.auto_enable) {
215: data |= MPSCC::CR3_AUTO_EN;
216: }
217: if (chan.rx_enable) {
1.1 root 218: data |= MPSCC::CR3_RX_EN;
1.1.1.2 root 219: }
220: return data;
221: }
1.1 root 222:
1.1.1.2 root 223: // CR4/WR4 レジスタを設定
224: /*static*/void
225: MPSCCDevice::SetCR4(MPSCCChan& chan, uint32 data)
226: {
1.1.1.3 ! root 227: uint cr4_clkrate = data & MPSCC::CR4_CLKRATE;
! 228: switch (cr4_clkrate) {
1.1.1.2 root 229: case MPSCC::CLKRATE_1:
230: chan.clkrate = 1;
231: break;
232: case MPSCC::CLKRATE_16:
233: chan.clkrate = 16;
234: break;
235: case MPSCC::CLKRATE_32:
236: chan.clkrate = 32;
237: break;
238: case MPSCC::CLKRATE_64:
239: chan.clkrate = 64;
240: break;
241: default:
1.1.1.3 ! root 242: VMPANIC("corrupted cr4_clkrate=$%x", cr4_clkrate);
1.1.1.2 root 243: }
244: chan.syncmode = (data & MPSCC::CR4_SYNCMODE);
245: chan.stopbits = (data & MPSCC::CR4_STOPBITS) >> 2;
246: chan.parity_even = (data & MPSCC::CR4_PARITY_EVEN);
247: chan.parity_enable = (data & MPSCC::CR4_PARITY_EN) ? 1 : 0;
248: }
249:
250: // CR4/WR4 レジスタの内容を取得
251: /*static*/ uint8
252: MPSCCDevice::GetCR4(const MPSCCChan& chan)
253: {
254: uint8 data = 0;
255:
256: switch (chan.clkrate) {
257: case 1:
258: data |= MPSCC::CLKRATE_1;
259: break;
260: case 16:
261: data |= MPSCC::CLKRATE_16;
262: break;
263: case 32:
264: data |= MPSCC::CLKRATE_32;
265: break;
266: case 64:
267: data |= MPSCC::CLKRATE_64;
268: break;
269: default:
1.1.1.3 ! root 270: VMPANIC("corrupted chan.clkrate=%d", chan.clkrate);
1.1.1.2 root 271: }
272: data |= chan.syncmode;
273: data |= chan.stopbits << 2;
274: if (chan.parity_even) {
275: data |= MPSCC::CR4_PARITY_EVEN;
276: }
277: if (chan.parity_enable) {
278: data |= MPSCC::CR4_PARITY_EN;
279: }
1.1 root 280: return data;
281: }
282:
1.1.1.2 root 283: // CR5/WR5 レジスタを設定
284: /*static*/ void
285: MPSCCDevice::SetCR5(MPSCCChan& chan, uint32 data)
286: {
1.1.1.3 ! root 287: uint cr5_txbits = data & MPSCC::CR5_TXBITS;
! 288: switch (cr5_txbits) {
1.1.1.2 root 289: case MPSCC::TXBITS_5:
290: chan.txbits = 5;
291: break;
292: case MPSCC::TXBITS_6:
293: chan.txbits = 6;
294: break;
295: case MPSCC::TXBITS_7:
296: chan.txbits = 7;
297: break;
298: case MPSCC::TXBITS_8:
299: chan.txbits = 8;
300: break;
301: default:
1.1.1.3 ! root 302: VMPANIC("corrupted cr5_txbits=$%x", cr5_txbits);
1.1.1.2 root 303: }
304: chan.nDTR = (data & MPSCC::CR5_nDTR);
305: chan.sendbreak = (data & MPSCC::CR5_SENDBREAK);
306: chan.tx_enable = (data & MPSCC::CR5_TX_EN);
307: chan.crc16 = (data & MPSCC::CR5_CRC16);
308: chan.nRTS = (data & MPSCC::CR5_nRTS);
309: chan.txcrc_en = (data & MPSCC::CR5_TXCRC_EN);
310: }
311:
1.1 root 312: // CR5/WR5 レジスタの内容を取得
1.1.1.2 root 313: /*static*/ uint8
314: MPSCCDevice::GetCR5(const MPSCCChan& chan)
1.1 root 315: {
1.1.1.2 root 316: uint8 data = 0;
1.1 root 317:
1.1.1.2 root 318: switch (chan.txbits) {
319: case 5:
320: data |= MPSCC::TXBITS_5;
321: break;
322: case 6:
323: data |= MPSCC::TXBITS_6;
324: break;
325: case 7:
326: data |= MPSCC::TXBITS_7;
327: break;
328: case 8:
329: data |= MPSCC::TXBITS_8;
330: break;
331: default:
1.1.1.3 ! root 332: VMPANIC("corrupted chan.txbits=%d", chan.txbits);
1.1.1.2 root 333: }
334: if (chan.nDTR) {
335: data |= MPSCC::CR5_nDTR;
336: }
337: if (chan.sendbreak) {
338: data |= MPSCC::CR5_SENDBREAK;
339: }
340: if (chan.tx_enable) {
1.1 root 341: data |= MPSCC::CR5_TX_EN;
1.1.1.2 root 342: }
343: if (chan.crc16) {
344: data |= MPSCC::CR5_CRC16;
345: }
346: if (chan.nRTS) {
347: data |= MPSCC::CR5_nRTS;
348: }
349: if (chan.txcrc_en) {
350: data |= MPSCC::CR5_TXCRC_EN;
351: }
1.1 root 352: return data;
353: }
354:
355: // SR2B/RR2B レジスタの内容を取得
356: uint8
357: MPSCCDevice::GetSR2B() const
358: {
359: uint8 data = mpscc.vector;
360:
361: // 割り込み要因をベクタに載せない
362: if (mpscc.vis == MPSCC::VIS_FIXED) {
363: return data;
364: }
365:
366: uint stat = GetHighestInt();
367:
368: // 8種類 (3bit) の割り込み要因をベクタに載せる。
369: // 載せる場所 (と向き) はそれぞれ 2種類ずつある。
370: switch (mpscc.vis) {
371: case MPSCC::VIS_432: // uPD7201: INTMode = INT85
372: // 割り込み要求がない場合は %111
373: if (stat == MPSCC::VS_none)
374: stat = 7;
375: data &= ~0x1c;
376: data |= stat << 2;
377: break;
378:
379: case MPSCC::VIS_210: // uPD7201: INTMode = INT86
380: // 割り込み要求がない場合は %111
381: if (stat == MPSCC::VS_none)
382: stat = 7;
383: data &= ~0x07;
384: data |= stat;
385: break;
386:
387: case MPSCC::VIS_321: // Z8530: StatusHigh/StatusLow = %0
388: // 割り込み要求がない場合は %011
389: if (stat == MPSCC::VS_none)
390: stat = 3;
391: data &= ~0x0e;
392: data |= stat << 1;
393: break;
394:
395: case MPSCC::VIS_456: // Z8530: StatusHigh/StatusLow = %1
396: // 割り込み要求がない場合は %011
397: if (stat == MPSCC::VS_none)
398: stat = 3;
399: // VIS_321 とはビット順が逆
400: data &= ~0x70;
401: data |= bitrev(stat << 1);
402: break;
403: default:
1.1.1.3 ! root 404: VMPANIC("corrupted mpscc.vis=%d", (int)mpscc.vis);
1.1 root 405: }
406:
407: return data;
408: }
409:
410: // CR0/WR0 Send Abort コマンド
411: void
412: MPSCCDevice::SendAbort(MPSCCChan& chan)
413: {
1.1.1.2 root 414: putlog(0, "%s: Command 'Send Abort' (NOT IMPLEMENTED)", CRName(chan, 0));
1.1 root 415: }
416:
417: // CR0/WR0 Reset External/Status Interrupt コマンド
418: void
419: MPSCCDevice::ResetESIntr(MPSCCChan& chan)
420: {
421: putlog(3, "%s: Command 'Reset E/S Interrupt'", CRName(chan, 0));
422: chan.es_latched = false;
423: }
424:
425: // CR0/WR0 Enable Interrupt on Next Rx Character コマンド
426: void
427: MPSCCDevice::EnableIntrOnNextRx(MPSCCChan& chan)
428: {
429: putlog(1, "%s: Command 'Enable Intr on Next Rx Character'",
430: CRName(chan, 0));
431: chan.all_or_first = true;
432: }
433:
434: // CR0/WR0 Reset Tx Interrupt/DMA Pending コマンド
435: void
436: MPSCCDevice::ResetTxIntrPending(MPSCCChan& chan)
437: {
438: putlog(2, "%s: Command 'Reset Tx Intr/DMA Pending'", CRName(chan, 0));
439: chan.txint_ready = false;
440: ChangeInterrupt();
441: }
442:
443: // CR0/WR0 Error Reset コマンド
444: void
445: MPSCCDevice::ErrorReset(MPSCCChan& chan)
446: {
447: putlog(1, "%s: Command 'Error Reset'", CRName(chan, 0));
448: chan.sr1 &= ~(MPSCC::SR1_ENDOFFRAME |
449: MPSCC::SR1_FRAMEERROR |
450: MPSCC::SR1_RXOVERRUN |
451: MPSCC::SR1_PARITYERROR);
452: // XXX Special Rx Condition は未実装
453: }
454:
455: // CR0 End of Interrupt コマンド
456: // WR0 Reset Highest IUS コマンド
457: // たぶん名前が違うだけで同じ。後者のほうが分かりやすいのでこっちを使う
458: void
459: MPSCCDevice::ResetHighestIUS(MPSCCChan& chan)
460: {
461: putlog(2, "%s: Command 'Reset Highest IUS'", CRName(chan, 0));
462:
463: // ペンディングのうち最も高い割り込み要因をクリア
464: uint stat = GetHighestInt();
465: switch (stat) {
466: case MPSCC::VS_TxB:
467: mpscc.chan[1].intpend &= ~INTPEND_TX;
468: break;
469: case MPSCC::VS_ESB:
470: mpscc.chan[1].intpend &= ~INTPEND_ES;
471: break;
472: case MPSCC::VS_RxB:
473: mpscc.chan[1].intpend &= ~INTPEND_RX;
474: break;
475: case MPSCC::VS_SPB:
476: mpscc.chan[1].intpend &= ~INTPEND_SP;
477: break;
478:
479: case MPSCC::VS_TxA:
480: mpscc.chan[0].intpend &= ~INTPEND_TX;
481: break;
482: case MPSCC::VS_ESA:
483: mpscc.chan[0].intpend &= ~INTPEND_ES;
484: break;
485: case MPSCC::VS_RxA:
486: mpscc.chan[0].intpend &= ~INTPEND_RX;
487: break;
488: case MPSCC::VS_SPA:
489: mpscc.chan[0].intpend &= ~INTPEND_SP;
490: break;
491:
492: default:
493: break;
494: }
495: ChangeInterrupt();
496: }
497:
498: // CR3/WR3 への書き込み
499: void
500: MPSCCDevice::WriteCR3(MPSCCChan& chan, uint32 data)
501: {
1.1.1.2 root 502: SetCR3(chan, data);
1.1 root 503: putlog(2, "%s <- $%02x", CRName(chan), data);
504:
505: SetRxPeriod(chan);
1.1.1.2 root 506:
507: // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。
508: chan.old_cr3 = GetCR3(chan);
1.1 root 509: }
510:
511: // CR4/WR4 への書き込み
512: void
513: MPSCCDevice::WriteCR4(MPSCCChan& chan, uint32 data)
514: {
1.1.1.2 root 515: SetCR4(chan, data);
1.1 root 516: putlog(2, "%s <- $%02x", CRName(chan), data);
517:
1.1.1.2 root 518: if (chan.stopbits == MPSCC::STOPBITS_SYNC) {
1.1 root 519: // 同期モードはサポートしない
520: putlog(2, "Channel %c Synchronous mode not supported", chan.name);
521: } else {
522: const char *sm[] = { "?", "1", "1.5", "2" };
523: putlog(2, "Channel %c Async Mode (x%d clock, stopbit %s)",
524: chan.name,
1.1.1.2 root 525: chan.clkrate,
526: sm[chan.stopbits]);
527: // なんとなく順序的に何倍と表示した後に速度を表示
528: ChangeBaudrate(chan);
1.1 root 529: }
530:
531: SetTxPeriod(chan);
532: SetRxPeriod(chan);
1.1.1.2 root 533:
534: // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。
535: chan.old_cr4 = GetCR4(chan);
1.1 root 536: }
537:
538: // CR5/WR5 への書き込み
539: void
540: MPSCCDevice::WriteCR5(MPSCCChan& chan, uint32 data)
541: {
1.1.1.2 root 542: SetCR5(chan, data);
1.1 root 543: putlog(2, "%s <- $%02x", CRName(chan), data);
544:
545: SetTxPeriod(chan);
1.1.1.2 root 546: // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。
547: chan.old_cr5 = GetCR5(chan);
548:
1.1 root 549: TrySend(chan);
550: }
551:
552: // CR6/WR6 への書き込み
553: void
554: MPSCCDevice::WriteCR6(MPSCCChan& chan, uint32 data)
555: {
556: chan.cr6 = data;
557:
1.1.1.2 root 558: // どのみちサポートしてないけど初期化のために 0x00 を書き込むようなので
559: // それ以外ならログを出す。
560: if (data != 0) {
561: putlog(0, "Write %s <- $%02x (NOT IMPLEMENTED)", CRName(chan), data);
562: }
1.1 root 563: }
564:
565: // CR7/WR7 への書き込み
566: void
567: MPSCCDevice::WriteCR7(MPSCCChan& chan, uint32 data)
568: {
569: chan.cr7 = data;
570:
1.1.1.2 root 571: // どのみちサポートしてないけど初期化のために 0x00 を書き込むようなので
572: // それ以外ならログを出す。
573: if (data != 0) {
574: putlog(0, "Write %s <- $%02x (NOT IMPLEMENTED)", CRName(chan), data);
575: }
1.1 root 576: }
577:
578: // データポートの読み込み
579: uint8
580: MPSCCDevice::ReadData(MPSCCChan& chan)
581: {
582: uint8 data;
583:
584: // 受信バッファが空でも直近のデータが読めるので常に [0] でよい
585: data = chan.rxbuf[0];
586: if (chan.rxlen > 0) {
587: putlog(3, "Ch%c Data -> $%02x", chan.name, data);
588: chan.rxlen--;
589: if (chan.rxlen == 0) {
590: // 取り出して空になった
591: ChangeInterrupt();
592: } else {
593: chan.rxbuf[0] = chan.rxbuf[1];
594: chan.rxbuf[1] = chan.rxbuf[2];
595: }
596: } else {
597: putlog(3, "Ch%c Data -> $%02x (Empty)", chan.name, data);
598: }
599:
600: return data;
601: }
602:
603: // データポートの書き込み
604: void
605: MPSCCDevice::WriteData(MPSCCChan& chan, uint32 data)
606: {
607: char charbuf[4];
608:
609: if (isprint((int)data)) {
610: snprintf(charbuf, sizeof(charbuf), "'%c'", data);
611: } else {
612: charbuf[0] = '\0';
613: }
614: putlog(3, "Ch%c Data <- $%02x %s", chan.name, data, charbuf);
615:
616: // XXX: EMPTY でないときにデータを書いたらどうなるかは未定義
617:
618: // 1byte の TX Buffer
619: chan.txlen = 1;
620: chan.txbuf = data;
621:
622: // データ書き込み後に Empty になったら割り込みを上げるため
623: // ここで ready をセット
624: chan.txint_ready = true;
625:
626: TrySend(chan);
627: }
628:
629: // データポートの Peek
630: uint8
631: MPSCCDevice::PeekData(const MPSCCChan &chan) const
632: {
633: // 受信バッファにデータがあれば先頭バイトが見える。
634: // 受信バッファが空でも直近に取り出したバイトがそのまま見える。
635: return chan.rxbuf[0];
636: }
637:
638: // 送信できるか調べて送信する
639: void
640: MPSCCDevice::TrySend(MPSCCChan& chan)
641: {
642: // 送信可で送信バッファにデータがあって送信中でなければ
643: if (chan.tx_enable && chan.txlen && !txevent[chan.id].IsRunning()) {
644: // 送信する
645: chan.tx_shiftreg = chan.txbuf;
646: chan.txlen = 0;
1.1.1.3 ! root 647: scheduler->StartEvent(txevent[chan.id]);
1.1 root 648: putlog(2, "Ch%c send $%02x", chan.name, chan.tx_shiftreg);
649:
650: // XXX たぶん送信してから割り込みを上げるまでにいくらか遅れがあるはず
651: }
652:
653: // ネゲートされることがあるので、送信しなくても割り込み状態を更新する
654: ChangeInterrupt();
655: }
656:
657: // 送信のイベントコールバック
658: void
659: MPSCCDevice::TxCallback(Event& event)
660: {
661: int ch = event.code;
662: MPSCCChan& chan = mpscc.chan[ch];
663:
664: // 接続デバイスの呼び出し
665: Tx(ch, chan.tx_shiftreg);
666:
667: // 次の送信データが来てたら送信
668: TrySend(chan);
669: }
670:
671: // ホストスレッドからの受信通知
672: // (HostCOM スレッドで呼ばれる)
673: void
674: MPSCCDevice::HostRxCallback()
675: {
1.1.1.3 ! root 676: // XXX 暫定
! 677: // HostCom からメッセージが大量に送られると、スケジューラの
! 678: // メッセージキューが溢れてしまうことがあったので、とりあえず
! 679: // ここでメッセージを送るかどうかを見ているが、
! 680: // 本当はたぶんこうではなくて、送る側が速度調節するか、
! 681: // またはスケジューラにメッセージではないなにか別の方法で
! 682: // 通知を出すようにする必要がある。
! 683:
! 684: Event& ev = rxevent[COM_CH];
! 685:
! 686: if (ev.IsRunning() == false) {
! 687: // スレッドを超えるためにメッセージを投げる
! 688: scheduler->SendMessage(MessageID::HOSTCOM_RX);
! 689: }
1.1 root 690: }
691:
692: // 1バイト受信する。
693: // 受理されれば true を返す。
694: bool
695: MPSCCDevice::Rx(int ch, uint32 data)
696: {
697: MPSCCChan& chan = mpscc.chan[ch];
698:
699: // Rx Enable でなければ受け取らない?
700: if (chan.rx_enable == false) {
701: return false;
702: }
703:
704: // バッファが一杯なら Rx Overrun Error
705: if (chan.rxlen == sizeof(chan.rxbuf)) {
706: putlog(2, "Ch%c Rx Overrun", chan.name);
707: chan.sr1 |= MPSCC::SR1_RXOVERRUN;
708: // XXX special Rx condition interrupt occurs
709: return false;
710: }
711:
712: // 受信
713: putlog(2, "Ch%c recv $%02x", chan.name, data);
714: chan.rxbuf[chan.rxlen++] = data;
715:
716: // 割り込み状態を変更
717: ChangeInterrupt();
718:
719: return true;
720: }
721:
722: // ホストシリアルからの1バイト受信メッセージ
723: void
724: MPSCCDevice::RxMessageCallback(MessageID msgid, uint32 arg)
725: {
726: Event& ev = rxevent[COM_CH];
727:
728: // 受信ループが止まっていれば開始
729: if (ev.IsRunning() == false) {
1.1.1.3 ! root 730: scheduler->StartEvent(ev);
1.1 root 731: }
732: }
733:
734: // 受信イベントコールバック
735: void
736: MPSCCDevice::RxCallback(Event& ev)
737: {
738: int ch = ev.code;
739: int data;
740:
741: // ホストキューにあるだけ取り出す
742: data = hostcom->Rx();
743: if (data >= 0) {
744: Rx(ch, data);
745:
1.1.1.3 ! root 746: scheduler->StartEvent(ev);
1.1 root 747: }
748: }
749:
750: static double
751: to_freq(uint64 nsec)
752: {
753: return (double)1.0 / nsec * 1e9;
754: }
755:
1.1.1.2 root 756: // ボーレートを再計算する。
757: // CR4(ClkRate) と xc12_ns の変更で呼ぶこと。
758: void
759: MPSCCDevice::ChangeBaudrate(MPSCCChan& chan)
1.1 root 760: {
1.1.1.2 root 761: double old_baudrate = chan.baudrate;
1.1 root 762:
1.1.1.2 root 763: // 誤差を減らすため 12bit 分の時間を基準とする。
764: chan.bit12_ns = chan.xc12_ns * chan.clkrate;
765: chan.baudrate = (double)to_freq(chan.bit12_ns) * 12;
1.1 root 766:
1.1.1.2 root 767: if (loglevel >= 3) {
768: if (chan.baudrate != old_baudrate) {
769: putlogn("Channel %c Baudrate %.1f", chan.name, chan.baudrate);
770: }
771: }
772: }
773:
774: // スタートビット、ストップビット、パリティビットの総数を返す。
775: // enable はサポートしていないモードの時にログを出すため。
776: int
777: MPSCCDevice::AdditionalBits(MPSCCChan& chan, bool enable)
778: {
779: int bit;
1.1 root 780:
781: // start bit
1.1.1.2 root 782: bit = 1;
1.1 root 783:
784: // parity bit
1.1.1.2 root 785: bit += chan.parity_enable;
1.1 root 786:
787: // stop bit
1.1.1.2 root 788: bit += GetStopBits(chan, enable);
789:
790: return bit;
791: }
792:
793: // ストップビット数を返す。
794: // サポートしていないモードなどの場合は enable ならログを出力する。
795: // enable は tx_enable か rx_enable。
796: int
797: MPSCCDevice::GetStopBits(MPSCCChan& chan, bool enable)
798: {
799: switch (chan.stopbits) {
1.1 root 800: case MPSCC::STOPBITS_SYNC:
801: // 非同期モードでの STOPBITS 0 は未定義 (Enable ならログ表示)
802: if (enable) {
1.1.1.2 root 803: putlog(0, "%s Undefined stopbit 0", CRName(chan, 4));
1.1 root 804: }
1.1.1.2 root 805: return 0;
1.1 root 806: case MPSCC::STOPBITS_1:
1.1.1.2 root 807: return 1;
1.1 root 808: case MPSCC::STOPBITS_1_5:
809: // サポートしない (Enable ならログ表示)
810: if (enable) {
1.1.1.2 root 811: putlog(0, "%s Stopbit 1.5 not supported", CRName(chan, 4));
1.1 root 812: }
1.1.1.2 root 813: return 0;
1.1 root 814: case MPSCC::STOPBITS_2:
1.1.1.2 root 815: return 2;
1.1.1.3 ! root 816:
! 817: default:
! 818: VMPANIC("corrupted chan.stopbits=$%x", chan.stopbits);
1.1 root 819: }
820: }
821:
1.1.1.2 root 822: // 受信イベントの時間を設定する。
823: // CR3 または CR4 の変更で呼ぶこと。また呼び出し後は old_cr3、old_cr4 を
824: // 更新すること。
1.1 root 825: void
826: MPSCCDevice::SetRxPeriod(MPSCCChan& chan)
827: {
828: int bit;
829:
830: // 受信データビット数
1.1.1.2 root 831: bit = chan.rxbits;
832: // スタート/ストップ等を足す
833: bit += AdditionalBits(chan, chan.rx_enable);
834: chan.rxframebits = bit;
835:
1.1.1.3 ! root 836: rxevent[chan.id].time = bit * chan.bit12_ns / 12;
! 837:
1.1.1.2 root 838: if (loglevel >= 1) {
839: bool old_enable = chan.old_cr3 & MPSCC::CR3_RX_EN;
840: bool new_enable = chan.rx_enable;
841: if (old_enable == false && new_enable == false) {
842: // 無効→無効ならパラメータが変化しても表示しない
843:
844: } else if (old_enable == true && new_enable == false) {
845: // 有効→無効なら無効にしたことだけ表示
846: putlogn("Channel %c RX Disable", chan.name);
847: sr0_logphase = 0;
848:
849: } else if ((old_enable == false && new_enable == true) ||
850: (chan.old_cr4 ^ GetCR4(chan)) != 0 ||
851: ((chan.old_cr3 ^ GetCR3(chan)) & MPSCC::CR3_RXBITS) != 0)
852: {
853: // 無効→有効なら表示。
854: // そうでなければ(有効→有効なら)、パラメータが変化すれば表示。
855: std::string nsstr;
856: if (loglevel >= 2) {
857: nsstr = string_format(" (%" PRIu64 ")", chan.bit12_ns);
858: }
859: putlogn("Channel %c RX Enable, %.1f bps%s, %d bits/frame",
860: chan.name,
861: chan.baudrate,
862: nsstr.c_str(),
863: bit);
864: sr0_logphase = 0;
865: }
1.1 root 866: }
867:
868: // パフォーマンス測定用のギミック。
869: // シリアルポートを特定の設定 (本体 5bit、ストップビット 2bit) にすると、
870: // VM 電源オン時から現在までの実時間をログに出力する。NetBSD/OpenBSD の
871: // マルチユーザブートがあらかた終わってログインプロンプトが出る手前の
872: // /etc/rc.local に
873: //
874: // stty -f /dev/ttya cs5 cstopb; stty -f /dev/ttya cs8 -cstopb
875: //
876: // と書いておくことで、ここまでにかかる時間を表示できる。
877: // この値はホスト性能に依存するため、同一環境下での相対比較のみ可能。
878: // シリアルポートの設定を (通常ではまず使わなそうなやつに) 変えてすぐ戻す
879: // だけなので、実機でも副作用はないはず。
1.1.1.2 root 880: if (chan.id == 0 &&
881: chan.rxbits == 5 && chan.stopbits == MPSCC::STOPBITS_2)
1.1 root 882: {
1.1.1.3 ! root 883: GetSyncer()->PutlogRealtime();
1.1 root 884: }
885: }
886:
1.1.1.2 root 887: // 送信イベントの時間を設定する。
888: // CR4 または CR5 の変更で呼ぶこと。また呼び出し後は old_cr4、old_cr5 を
889: // 更新すること。
1.1 root 890: void
891: MPSCCDevice::SetTxPeriod(MPSCCChan& chan)
892: {
893: int bit;
894:
895: // 送信データビット数
1.1.1.2 root 896: bit = chan.txbits;
897: // スタート/ストップ等を足す
898: bit += AdditionalBits(chan, chan.tx_enable);
899: chan.txframebits = bit;
900:
1.1.1.3 ! root 901: txevent[chan.id].time = bit * chan.bit12_ns / 12;
! 902:
1.1.1.2 root 903: if (loglevel >= 1) {
904: bool old_enable = chan.old_cr5 & MPSCC::CR5_TX_EN;
905: bool new_enable = chan.tx_enable;
906: if (old_enable == false && new_enable == false) {
907: // 無効→無効ならパラメータが変化しても表示しない
908:
909: } else if (old_enable == true && new_enable == false) {
910: // 有効→無効なら無効にしたことだけ表示
911: putlogn("Channel %c TX Disable", chan.name);
912: sr0_logphase = 0;
913:
914: } else if ((old_enable == false && new_enable == true) ||
915: (chan.old_cr4 ^ GetCR4(chan)) != 0 ||
916: ((chan.old_cr5 ^ GetCR5(chan)) & MPSCC::CR5_TXBITS) != 0)
917: {
918: // 無効→有効なら表示。
919: // そうでなければ(有効→有効なら)、パラメータが変化すれば表示。
920: std::string nsstr;
921: if (loglevel >= 2) {
922: nsstr = string_format(" (%" PRIu64 ")", chan.bit12_ns);
923: }
924: putlogn("Channel %c TX Enable, %.1f bps%s, %d bits/frame",
925: chan.name,
926: chan.baudrate,
927: nsstr.c_str(),
928: bit);
929: sr0_logphase = 0;
930: }
1.1 root 931: }
932: }
933:
934: // 割り込み状態を変更
935: //
936: // uPD7201 の場合:
937: //
938: // RxINT Disable (CR1 b4,3=%00) -->o--+ (注1)
939: // |
940: // o RxCharAvailable ----|& |
941: // |&---|OR +--|&
942: // All/First RxChar ----|& |OR |&--+
943: // |OR---------|& |
944: // o Special Rx Condition -----|OR +---|OR
945: // |OR ---
946: // o External/Status ------------------------------|OR-->o-- INT
947: // |OR
948: // o Tx Buffer Empty ----------|& +---|OR
949: // |&-----|& |
950: // Tx INT/DMA Enable --------|& |& |
951: // (CR1 b1) |&-------+
952: // |&
953: // INT/DMA Mode (CR2A b1,0) --------|& (注2)
954: //
955: // 注1: uPD7201/7201A Figure 3-2 では "Rx Disable (CR1:D1)" だがそこは
956: // Tx INT/DMA Enable ビットなので名称もビット位置も誤り。
957: // uPD7201 ユーザーズマニュアル p.23 の 図3-2 では "Rx Disable (CR1:D4-3=00)"
958: // とあるのでこれのビット位置が正しければ Rx Int Disable が正しいはず。
959: //
960: // 注2: uPD7201 のみ。Z8530 ではこの選択肢はないので常に true のはず。
961: //
962: void
963: MPSCCDevice::ChangeInterrupt()
964: {
965: uint total_intpend = 0;
966: bool int_enable[2];
967:
968: // uPD7201 の CR1 INT/DMA Mode による制御。
969: // Z8530 では intdma_mode = INT_INT 固定。
970: //
971: // ついでにここで txint_enable も演算しておく。見た目は
972: // 無駄っぽいけどコンパイラで最適化されるので気にしない。
973: if (__predict_true(mpscc.intdma_mode == MPSCC::INTDMA_INT_INT)) {
974: int_enable[0] = mpscc.chan[0].txint_enable && true; // ChA INT
975: int_enable[1] = mpscc.chan[1].txint_enable && true; // ChB INT
976: } else if (mpscc.intdma_mode == MPSCC::INTDMA_DMA_INT) {
977: int_enable[0] = mpscc.chan[0].txint_enable && false; // ChA DMA
978: int_enable[1] = mpscc.chan[1].txint_enable && true; // ChB INT
979: } else {
980: int_enable[0] = mpscc.chan[0].txint_enable && false; // ChA DMA
981: int_enable[1] = mpscc.chan[1].txint_enable && false; // ChB DMA
982: }
983:
984: for (auto& chan : mpscc.chan) {
985: // Rx
986: bool rx_avail = (chan.rxlen > 0) && chan.all_or_first;
987: // Special Rx (XXX not yet)
988: bool special = false;
989: bool rx_int = (chan.rxint_mode != MPSCCChan::RXINT_DISABLE) &&
990: (rx_avail || special);
991:
992: // FirstChar モードなら1文字受信割り込み後にフラグを下ろす
993: // (Rx 割り込み用の変数 rx_int が確定したここで行う)
994: if (chan.rxint_mode == MPSCCChan::RXINT_1STCHAR) {
995: chan.all_or_first = false;
996: }
997:
998: // XXX E/S not yet
999: bool es_int = false;
1000:
1001: // Tx
1002: bool tx_int = int_enable[chan.id] &&
1003: chan.txint_ready && (chan.txlen == 0);
1004:
1005: uint intpend = 0;
1006: if (rx_int)
1007: intpend |= INTPEND_RX;
1008: if (tx_int)
1009: intpend |= INTPEND_TX;
1010: if (es_int)
1011: intpend |= INTPEND_ES;
1012: chan.intpend = intpend;
1013:
1014: total_intpend |= chan.intpend;
1015: }
1016:
1017: // いずれかでもあれば割り込みをアサート
1.1.1.3 ! root 1018: interrupt->ChangeINT(this, total_intpend);
1.1 root 1019: }
1020:
1.1.1.3 ! root 1021: // "9600,N,8,1" みたいなのを返す。最大13桁?
1.1 root 1022: std::string
1023: MPSCCDevice::GetParamStr(const MPSCCChan& chan) const
1024: {
1025: std::string rv;
1026: char parity;
1027: char bits;
1028: char stopbits;
1029:
1030: int parity_idx = 0;
1.1.1.2 root 1031: if (chan.parity_enable) {
1.1 root 1032: parity_idx++;
1.1.1.2 root 1033: if (chan.parity_even) {
1.1 root 1034: parity_idx++;
1035: }
1036: }
1037: parity = "NOE"[parity_idx];
1038:
1039: // bits は RX==TX のときのみ有効出力
1040: // 違うときは '?' にしておく。
1.1.1.2 root 1041: if (chan.rxbits == chan.txbits) {
1042: bits = '0' + chan.rxbits;
1.1 root 1043: } else {
1044: bits = '?';
1045: }
1046:
1047: // stop bit は 1, 2 のみ有効出力。
1048: // SYNC とか 1.5 bit とかは '?' にしておく。
1.1.1.2 root 1049: switch (chan.stopbits) {
1.1 root 1050: case MPSCC::STOPBITS_1:
1051: stopbits = '1';
1052: break;
1053: case MPSCC::STOPBITS_2:
1054: stopbits = '2';
1055: break;
1056: default:
1057: stopbits = '?';
1058: break;
1059: }
1060:
1.1.1.2 root 1061: // ここは、このポートに接続する時に相手方で指定する設定値を
1062: // 表示したいので、ボーレートをよく知られている値に丸めたい。
1063: // 75 * 2^n の +-5% に入っていたら採用。
1064: int baud;
1065: int e;
1066:
1067: // 75 * 2 ^ (e-1) を求める
1068: double a = (chan.baudrate / 75) * 1.5;
1069: std::frexp(a, &e);
1070: int b = 75 * (1 << (e - 1));
1071:
1072: if (b * 19 / 20 <= chan.baudrate && chan.baudrate <= b * 21 / 20) {
1073: baud = b;
1074: } else {
1075: // +-5% 範囲外なら仕方ないのでそのままの値
1076: baud = (int)chan.baudrate;
1077: }
1078:
1.1 root 1079: return string_format("%d,%c,%c,%c",
1.1.1.2 root 1080: baud,
1.1 root 1081: parity,
1082: bits,
1083: stopbits);
1084: }
1085:
1086: // モニタに CR3/WR3、CR4/WR4、CR5/WR5 の内容を書き出す。
1087: // 更新後の y を返す。
1088: int
1089: MPSCCDevice::MonitorUpdateCR345(TextScreen& screen, int x, int y,
1090: uint8 cr3, uint8 cr4, uint8 cr5)
1091: {
1.1.1.2 root 1092: const std::array<char, 4> bits_per_char { '5', '7', '6', '8' };
1093:
1.1 root 1094: // CR3/WR3
1095: static const char * const cr3_str[] = {
1096: "", "", "Auto", "", "", "", "", "RxEn"
1097: };
1098: MonitorReg(screen, x + 9, y, cr3, cr3_str);
1.1.1.2 root 1099: screen.Print(x + 9, y, "RxBits=%c", bits_per_char[(cr3 >> 6) & 3]);
1.1 root 1100: // b1-4 は非同期モードでは使わないのでグレーにしておく
1101: screen.Puts(x + 24, y, TA::Disable, (cr3 & 0x10) ? "1" : "0");
1102: screen.Puts(x + 29, y, TA::Disable, (cr3 & 0x08) ? "1" : "0");
1103: screen.Puts(x + 34, y, TA::Disable, (cr3 & 0x04) ? "1" : "0");
1104: screen.Puts(x + 39, y, TA::Disable, (cr3 & 0x02) ? "1" : "0");
1105: y++;
1106:
1107: // CR4/WR4
1108: static const char * const cr4_str[] = {
1109: "", "", "", "", "", "", "PaEv", "PaEn"
1110: };
1111: MonitorReg(screen, x + 9, y, cr4, cr4_str);
1.1.1.2 root 1112: static const char * const clkrate_str[] = {
1113: "1", "16", "32", "64"
1114: };
1115: screen.Print(x + 9, y, "Rate=x%s", clkrate_str[cr4 >> 6]);
1.1 root 1116: static const char * const syncmode_str[] = {
1117: "Mono", "Bi", "HDLC", "Ext"
1118: };
1119: uint stopbit = (cr4 >> 2) & 3;
1120: screen.Print(x + 19, y, (stopbit == 0 ? TA::Normal : TA::Disable),
1121: "Sync=%s", syncmode_str[(cr4 >> 4) & 3]);
1122: static const char * const stopbit_str[] = {
1123: "SyncMode",
1124: "StopBit=1",
1125: "Stop=1.5",
1126: "StopBit=2"
1127: };
1128: screen.Puts(x + 29, y, stopbit_str[stopbit]);
1129: y++;
1130:
1131: // CR5/WR5
1132: static const char * const cr5_str[] = {
1133: "!DTR", "", "", "SBrk", "TxEn", "CRC", "!RTS", "TCEn"
1134: };
1135: MonitorReg(screen, x + 9, y, cr5, cr5_str);
1.1.1.2 root 1136: screen.Print(x + 14, y, "TxBits=%c", bits_per_char[(cr5 >> 5) & 3]);
1.1 root 1137: y++;
1138:
1139: return y;
1140: }
1141:
1142: // モニタに SR1/RR1 の内容を書き出す。
1143: void
1144: MPSCCDevice::MonitorUpdateSR1(TextScreen& screen, int x, int y, uint8 sr1)
1145: {
1146: static const char * const sr1_str[] = {
1147: "EOF", "FrEr", "RxOv", "PaEr", "", "", "", "Sent"
1148: };
1149: MonitorReg(screen, x + 9, y, sr1, sr1_str);
1150: screen.Print(x + 29, y, TA::Disable, "ResidueCode=%d", (sr1 >> 1) & 7);
1151: }
1152:
1153: // レジスタをビットごとに表示する。MonitorUpdate の下請け。
1154: void
1155: MPSCCDevice::MonitorReg(TextScreen& screen,
1156: int x, int y, uint32 reg, const char * const *names)
1157: {
1158: for (int i = 0; i < 8; i++) {
1159: if (names[i][0] == '-') {
1160: screen.Puts(x + i * 5, y, TA::Disable, "----");
1161: } else {
1162: bool b = reg & (1 << (7 - i));
1163: screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]);
1164: }
1165: }
1166: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.