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