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