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