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