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