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