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