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