|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.8 root 7: //
8: // CRTC
9: //
1.1 root 10:
1.1.1.4 root 11: // e80000..e803ff 内は $40 バイトでミラー。
12: // e80400..e807ff 内は 256 バイトでミラーになっており、このうち
13: // +$00..+$7f はバスエラー、
14: // +$80..+$ff は何かが読める。
15: // 仕様は $e80480.w に1ポートあるので、これがワードで全域にミラーされている?
16: // 全体としては この $800 バイトずつが繰り返して見える。
1.1.1.12 root 17: //
18: // CRTC はデバイスの配置としては BusIO_B 相当で、バイトアクセスも行えるが、
19: // 内部はバイトレジスタではなくワード単位を基本構造としている。そのため
20: // BusIO は使わず自力で処理する。ログの観点からもこのほうが都合がいい。
1.1.1.4 root 21:
1.1.1.8 root 22: #include "crtc.h"
23: #include "mfp.h"
1.1.1.13! root 24: #include "monitor.h"
1.1.1.8 root 25: #include "scheduler.h"
26: #include "tvram.h"
1.1.1.13! root 27: #include "videoctlr.h"
1.1.1.8 root 28:
1.1.1.11 root 29: // InsideOut p.135
30: const busdata wait = busdata::Wait(9 * 40_nsec);
31:
1.1.1.8 root 32: // コンストラクタ
1.1 root 33: CRTCDevice::CRTCDevice()
1.1.1.10 root 34: : inherited(OBJ_CRTC)
1.1 root 35: {
1.1.1.13! root 36: monitor = gMonitorManager->Regist(ID_MONITOR_CRTC, this);
! 37: monitor->func = ToMonitorCallback(&CRTCDevice::MonitorUpdate);
! 38: monitor->SetSize(62, 14);
1.1 root 39: }
40:
1.1.1.8 root 41: // デストラクタ
1.1 root 42: CRTCDevice::~CRTCDevice()
43: {
1.1.1.10 root 44: }
45:
46: // 初期化
47: bool
48: CRTCDevice::Init()
49: {
50: if (inherited::Init() == false) {
51: return false;
52: }
53:
54: mfp = GetMFPDevice();
55: tvram = GetTVRAMDevice();
1.1.1.13! root 56: videoctlr = GetVideoCtlrDevice();
1.1.1.10 root 57:
1.1.1.12 root 58: hsync_event.func = ToEventCallback(&CRTCDevice::HSyncCallback);
59: hsync_event.SetName("CRTC H-Sync");
60: vdisp_event.func = ToEventCallback(&CRTCDevice::VDispCallback);
61: vdisp_event.SetName("CRTC V-Disp");
62: scheduler->RegistEvent(hsync_event);
63: scheduler->RegistEvent(vdisp_event);
64:
65: // ラスターコピーは実際には CRTC の機能ではなく TVRAM (VRAM チップ) の
66: // 機能だが、イベントの時間管理が発生するのは CRTC 側なのでここで管理。
67: // X68030 搭載の VRAM チップは HM514402A だが、ここでは X68000 XVI 搭載の
68: // HM53461 の値を使用し、これが最小 190nsec * 2 らしいので
69: // とりあえず 400nsec としておく。
70: raster_event.func = ToEventCallback(&CRTCDevice::RasterCallback);
71: raster_event.time = 400_nsec;
72: raster_event.SetName("CRTC Raster Copy");
73: scheduler->RegistEvent(raster_event);
74:
1.1.1.10 root 75: return true;
1.1 root 76: }
77:
1.1.1.8 root 78: // リセット
1.1 root 79: void
1.1.1.8 root 80: CRTCDevice::ResetHard(bool poweron)
1.1 root 81: {
82: // XXX 初期値適当
83: crtc.r[20] = 0x0016;
84:
85: // XXX 適当
1.1.1.8 root 86: // すぐに水平同期と垂直帰線期間を開始する
87: hsync_event.time = 0;
88: hsync_event.code = 1;
1.1.1.10 root 89: scheduler->RestartEvent(hsync_event);
1.1 root 90: vdisp_event.time = 0;
91: vdisp_event.code = 1;
1.1.1.10 root 92: scheduler->RestartEvent(vdisp_event);
1.1 root 93: }
94:
1.1.1.12 root 95: /*static*/ inline uint32
96: CRTCDevice::Decoder(uint32 addr)
1.1 root 97: {
1.1.1.12 root 98: return addr & 0x7ff;
1.1.1.11 root 99: }
100:
101: busdata
1.1.1.12 root 102: CRTCDevice::Read(busaddr addr)
1.1.1.11 root 103: {
1.1.1.12 root 104: uint32 offset = Decoder(addr.Addr());
105: busdata data = Peek2(offset);
106:
107: uint32 reqsize = addr.GetSize();
108: if (reqsize == 1) {
109: if ((offset & 1) == 0) {
110: data >>= 8;
111: } else {
112: data &= 0xff;
113: }
114: data |= BusData::Size1;
115: } else {
116: data |= BusData::Size2;
117: }
1.1.1.11 root 118:
119: if (__predict_false(loglevel >= 2)) {
120: if (offset < 0x400) {
121: // CRTC レジスタは 0x40 バイトでミラー
1.1.1.12 root 122: uint32 regno = (offset >> 1) & 0x1f;
123: if (regno < countof(crtc.r)) {
124: // R20, R21 のみ読み出し可能だが、
125: // R21 へのアクセスは多いのでログレベルを上げておく。
126: int lv = (regno == 21) ? 3 : 2;
127: if (reqsize == 1) {
128: putlog(lv, "R%02u.%c -> $%02x", regno,
129: (((offset & 1) == 0) ? 'H' : 'L'), data.Data());
130: } else {
131: putlog(lv, "R%02u -> $%04x", regno, data.Data());
132: }
1.1.1.11 root 133: } else {
1.1.1.12 root 134: putlog(3, "$%06x -> $%0*x", addr.Addr(),
135: reqsize * 2, data.Data());
1.1.1.11 root 136: }
1.1 root 137: }
138: }
139:
1.1.1.11 root 140: data |= wait;
1.1 root 141: return data;
142: }
143:
1.1.1.11 root 144: busdata
1.1.1.12 root 145: CRTCDevice::Write(busaddr addr, uint32 data)
1.1 root 146: {
1.1.1.11 root 147: busdata r;
1.1.1.12 root 148: uint32 offset = Decoder(addr.Addr());
149: uint32 reqsize = addr.GetSize();
150: uint32 datasize = std::min(2 - (offset & 1), reqsize);
151: data >>= (reqsize - datasize) * 8;
1.1.1.5 root 152:
1.1.1.11 root 153: if (offset < 0x400) {
154: // CRTC レジスタは 0x40 バイトでミラー
1.1.1.12 root 155: uint32 regno = (offset >> 1) & 0x1f;
1.1.1.11 root 156: if (regno < countof(crtc.r)) {
1.1.1.12 root 157: if (datasize == 1) {
158: // バイト書き込み
159: if ((offset & 1) == 0) {
160: data = (data << 8) | (crtc.r[regno] & 0xff);
161: } else {
162: data = (crtc.r[regno] & 0xff00) | data;
163: }
1.1.1.11 root 164: } else {
1.1.1.12 root 165: // ワード書き込み
1.1.1.11 root 166: }
1.1.1.12 root 167: SetReg(regno, data);
1.1 root 168: } else {
1.1.1.4 root 169: // ここ何がいるんだ?
1.1.1.12 root 170: putlog(2, "$%06x <- $%0*x", addr.Addr(), datasize * 2, data);
1.1 root 171: }
1.1.1.11 root 172: } else {
173: // 動作ポート
174: if ((offset & 0x80) == 0) { // +$00..+$7f
175: r.SetBusErr();
1.1.1.4 root 176: } else {
1.1.1.12 root 177: if (datasize == 1) {
178: // バイト書き込み
179: if ((offset & 1) == 0) {
180: data = (data << 8) | crtc.op;
181: } else {
182: data = (crtc.op & 0xff00) | data;
183: }
184: } else {
185: // ワード書き込み
186: }
1.1.1.8 root 187: WriteOp(data);
1.1 root 188: }
189: }
1.1.1.4 root 190:
1.1.1.11 root 191: r |= wait;
1.1.1.12 root 192: r |= busdata::Size(datasize);
1.1.1.11 root 193: return r;
1.1 root 194: }
195:
1.1.1.11 root 196: busdata
1.1.1.12 root 197: CRTCDevice::Peek1(uint32 addr)
1.1 root 198: {
1.1.1.12 root 199: busdata data = Peek2(addr);
1.1.1.11 root 200: if (data.IsOK()) {
201: if ((addr & 1) == 0) {
202: return data.Data() >> 8;
203: } else {
204: return data.Data() & 0xff;
205: }
206: } else {
1.1.1.12 root 207: return BusData::BusErr;
1.1.1.11 root 208: }
209: }
1.1 root 210:
1.1.1.11 root 211: // addr のワードとしての読み出し値を返す。
1.1.1.12 root 212: // これは内部用で Read() からも呼ばれるので、
213: // バスエラーなら BusData::BusErr を返す。
1.1.1.11 root 214: busdata
1.1.1.12 root 215: CRTCDevice::Peek2(uint32 addr) const
1.1.1.11 root 216: {
217: busdata data;
218:
1.1.1.12 root 219: uint32 offset = Decoder(addr);
1.1.1.11 root 220: if (offset < 0x400) {
221: // CRTC レジスタは 0x40 バイトでミラー
222: uint32 regno = (offset & 0x3f) / 2;
223: if (regno == 20 || regno == 21) {
1.1.1.4 root 224: // R20, R21 のみ読み出し可能
1.1.1.11 root 225: data = crtc.r[regno];
1.1.1.4 root 226: } else {
227: data = 0;
228: }
1.1.1.11 root 229: } else {
230: // 動作ポート
231: if ((offset & 0x80) == 0) {
232: data.SetBusErr();
1.1.1.4 root 233: } else {
234: data = crtc.op;
235: }
1.1 root 236: }
1.1.1.4 root 237:
238: return data;
1.1 root 239: }
240:
1.1.1.8 root 241: void
242: CRTCDevice::MonitorUpdate(Monitor *, TextScreen& screen)
243: {
244: int y;
245: uint16 val;
246:
247: screen.Clear();
248: y = 0;
1.1.1.9 root 249:
250: screen.Print(0, y++, "R00:$%04x (V.Total)", crtc.r[0]);
251: screen.Print(0, y++, "R01:$%04x (V.Sync End)", crtc.r[1]);
252: screen.Print(0, y++, "R02:$%04x (V.Disp Start)", crtc.r[2]);
253: screen.Print(0, y++, "R02:$%04x (V.Disp End)", crtc.r[3]);
254:
255: screen.Print(0, y++, "R04:$%04x (H.Total)", crtc.r[4]);
256: screen.Print(0, y++, "R05:$%04x (H.Sync End)", crtc.r[5]);
257: screen.Print(0, y++, "R06:$%04x (H.Disp Start)", crtc.r[6]);
258: screen.Print(0, y++, "R07:$%04x (H.Disp End)", crtc.r[7]);
259:
1.1.1.8 root 260: screen.Print(0, y++, "R10:$%04x (Text ScrollX)", crtc.r[10]);
261: screen.Print(0, y++, "R11:$%04x (Text ScrollY)", crtc.r[11]);
262:
1.1.1.9 root 263: val = crtc.r[20];
264: screen.Print(0, y, "R20:$%04x (Mode)", val);
265: screen.Print(29, y, TA::OnOff(val & CRTC::R20_MEM), "MEM");
266: static const char * const r20col_str[] = {
267: "16",
268: "256",
269: "undef",
270: "65536",
271: };
272: screen.Print(33, y, "COL=%s", r20col_str[(val >> 8) & 3]);
273: screen.Print(43, y, TA::OnOff(val & CRTC::R20_HF), "HF");
274: static const char * const r20vd_str[] = {
275: "256",
276: "512",
277: "1024",
278: "undef",
279: };
280: screen.Print(46, y, "VD=%s", r20vd_str[(val >> 2) & 3]);
281: static const char * const r20hd_str[] = {
282: "256",
283: "512",
284: "768",
285: "50MHz",
286: };
287: screen.Print(55, y, "HD=%s", r20hd_str[val & 3]);
288: y++;
289:
1.1.1.8 root 290: val = crtc.r[21];
291: screen.Print(0, y, "R21:$%04x (Text Plane)", val);
292: screen.Print(29, y, "MEN=%c SA=%c AP=%%%c%c%c%c CP=%%%c%c%c%c",
293: (val & 0x0200) ? '1' : '0',
294: (val & 0x0100) ? '1' : '0',
295: (val & 0x0080) ? '1' : '0',
296: (val & 0x0040) ? '1' : '0',
297: (val & 0x0020) ? '1' : '0',
298: (val & 0x0010) ? '1' : '0',
299: (val & 0x0008) ? '1' : '0',
300: (val & 0x0004) ? '1' : '0',
301: (val & 0x0002) ? '1' : '0',
302: (val & 0x0001) ? '1' : '0');
303: y++;
304:
305: val = crtc.r[22];
306: screen.Print(0, y++, "R22:$%04x (Text Raster Copy) src=$%02x dst=$%02x",
307: val, (val >> 8), (val & 0xff));
308:
309: screen.Print(0, y++, "R23:$%04x (Text Access Mask)", crtc.r[23]);
310: }
311:
1.1.1.4 root 312: // CRTC レジスタへの書き込み。
313: // reg は CRTC::R00 .. CRTC::R23
1.1 root 314: void
1.1.1.4 root 315: CRTCDevice::SetReg(uint32 reg, uint32 data)
1.1 root 316: {
317: crtc.r[reg] = data;
318:
319: switch (reg) {
1.1.1.4 root 320: case CRTC::R10:
1.1.1.12 root 321: putlog(2, "R%02u <- $%04x", reg, data);
1.1.1.10 root 322: tvram->SetScrollX(crtc.r[10]);
1.1.1.4 root 323: break;
324: case CRTC::R11:
1.1.1.12 root 325: putlog(2, "R%02u <- $%04x", reg, data);
1.1.1.10 root 326: tvram->SetScrollY(crtc.r[11]);
1.1.1.4 root 327: break;
1.1 root 328: case CRTC::R21:
1.1.1.4 root 329: // 頻度が高いのでログレベルを上げておく
1.1.1.12 root 330: putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.10 root 331: tvram->SetAccessPlane(crtc.r[21]);
1.1.1.8 root 332: break;
333: case CRTC::R22:
1.1.1.12 root 334: putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.8 root 335: break;
336: case CRTC::R23:
337: // 頻度が高いのでログレベルを上げておく
1.1.1.12 root 338: putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.10 root 339: tvram->SetAccessMask(crtc.r[23]);
1.1 root 340: break;
341: default:
1.1.1.12 root 342: putlog(2, "R%02u <- $%04x (NOT IMPLEMENTED)", reg, data);
1.1.1.4 root 343: break;
1.1 root 344: }
345: }
346:
1.1.1.8 root 347: // 動作ポートへの書き込み。
348: void
349: CRTCDevice::WriteOp(uint32 data)
350: {
351: uint16 oldop = crtc.op;
352:
353: crtc.op = data & 0x0b;
1.1.1.12 root 354: putlog(2, "OP <- $%04x", crtc.op);
1.1.1.8 root 355:
356: uint16 change = oldop ^ crtc.op;
357:
358: if ((change & CRTC::OP_RC)) {
359: // テキスト画面ラスターコピー
360: if ((crtc.op & CRTC::OP_RC)) {
361: // 0->1 開始
1.1.1.9 root 362: raster_copy = true;
1.1.1.12 root 363: putlog(3, "Start Raster copy (ラスターコピー指示");
1.1.1.8 root 364: } else {
1.1.1.9 root 365: // 1->0 は停止じゃなくキャンセル?
366: raster_copy = false;
1.1.1.12 root 367: putlog(3, "Cancel Raster copy (ラスターコピーキャンセル)");
1.1.1.8 root 368: }
369: }
370:
371: if ((change & 0x03)) {
1.1.1.12 root 372: putlog(0, "OP (動作ポート) $%02x <- $%02x (NOT IMPLEMENTED)",
1.1.1.8 root 373: oldop & 0x03, crtc.op & 0x03);
374: }
375: }
376:
377: // 水平同期イベント
378: void
379: CRTCDevice::HSyncCallback(Event& ev)
380: {
1.1.1.9 root 381: switch (ev.code) {
382: case 0: // フロントポーチ
383: // フロントポーチ
384: ev.time = 2.07_usec;
385: ev.code++;
386: break;
1.1.1.8 root 387:
1.1.1.9 root 388: case 1: // 水平同期パルス
1.1.1.8 root 389: // 水平同期期間
390: ev.time = 3.45_usec;
1.1.1.9 root 391: ev.code++;
392:
393: // GPIP の状態を更新
1.1.1.10 root 394: mfp->SetHSync(true);
1.1.1.9 root 395:
396: // ラスターコピーが指示されていたら実行
397: if (raster_copy) {
398: raster_copy = false;
399:
1.1.1.12 root 400: uint src = (crtc.r[22] >> 8) * 4;
401: uint dst = (crtc.r[22] & 0xff) * 4;
1.1.1.10 root 402: tvram->RasterCopy(src, dst);
1.1.1.9 root 403:
404: // 所定時間後に完了させる
1.1.1.10 root 405: scheduler->RestartEvent(raster_event);
1.1.1.9 root 406: }
407: break;
408:
409: case 2: // バックポーチ + 表示期間
410: // バックポーチ + Hdisp
411: ev.time = 4.14_usec + 22.09_usec;
1.1.1.8 root 412: ev.code = 0;
1.1.1.9 root 413:
414: // GPIP の状態を更新
1.1.1.10 root 415: mfp->SetHSync(false);
1.1.1.9 root 416: break;
1.1.1.8 root 417: }
1.1.1.9 root 418:
1.1.1.10 root 419: scheduler->StartEvent(ev);
1.1.1.8 root 420: }
421:
1.1.1.9 root 422: // ラスターコピー完了イベント
423: void
424: CRTCDevice::RasterCallback(Event& ev)
425: {
426: // 終わったら動作ポートの RC ビットを %0 にする?
427: crtc.op &= ~CRTC::OP_RC;
428: }
429:
1.1 root 430: // 垂直表示・帰線イベント
1.1.1.5 root 431: // ev.code が 1 なら表示期間開始(V-disp)、0 なら帰線期間開始(V-blank)。
1.1 root 432: void
1.1.1.5 root 433: CRTCDevice::VDispCallback(Event& ev)
1.1 root 434: {
1.1.1.8 root 435: int vdisp = ev.code;
1.1.1.5 root 436:
1.1 root 437: // GPIP の状態を更新
1.1.1.10 root 438: mfp->SetVDisp(vdisp);
1.1 root 439:
440: // 今はここで1画面まるごと作成
1.1.1.5 root 441: if (vdisp) {
1.1.1.13! root 442: videoctlr->VDisp();
1.1 root 443: }
444:
445: // 次のタイミングを計算
446: // XXX まだ CRTC から計算していない
1.1.1.5 root 447: if (vdisp) {
1.1 root 448: // 垂直表示期間
1.1.1.5 root 449: ev.time = 16250_usec;
450: ev.code = 0;
1.1 root 451: } else {
452: // 垂直帰線期間
1.1.1.8 root 453: ev.time = 191_usec + 1111_usec + 476_usec;
1.1.1.5 root 454: ev.code = 1;
1.1 root 455: }
1.1.1.10 root 456: scheduler->StartEvent(ev);
1.1 root 457: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.