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