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