|
|
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"
1.1.1.15 root 23: #include "event.h"
1.1.1.8 root 24: #include "mfp.h"
1.1.1.13 root 25: #include "monitor.h"
1.1.1.8 root 26: #include "scheduler.h"
27: #include "tvram.h"
1.1.1.13 root 28: #include "videoctlr.h"
1.1.1.8 root 29:
1.1.1.11 root 30: // InsideOut p.135
1.1.1.15 root 31: /*static*/ const busdata
32: CRTCDevice::wait = busdata::Wait(9 * 40_nsec);
1.1.1.11 root 33:
1.1.1.8 root 34: // コンストラクタ
1.1 root 35: CRTCDevice::CRTCDevice()
1.1.1.10 root 36: : inherited(OBJ_CRTC)
1.1 root 37: {
1.1.1.13 root 38: monitor = gMonitorManager->Regist(ID_MONITOR_CRTC, this);
1.1.1.16! root 39: monitor->SetCallback(&CRTCDevice::MonitorScreen);
1.1.1.15 root 40: monitor->SetSize(79, 27);
1.1 root 41: }
42:
1.1.1.8 root 43: // デストラクタ
1.1 root 44: CRTCDevice::~CRTCDevice()
45: {
1.1.1.10 root 46: }
47:
48: // 初期化
49: bool
50: CRTCDevice::Init()
51: {
52: mfp = GetMFPDevice();
53: tvram = GetTVRAMDevice();
1.1.1.13 root 54: videoctlr = GetVideoCtlrDevice();
1.1.1.10 root 55:
1.1.1.15 root 56: auto evman = GetEventManager();
57: hsync_event = evman->Regist(this,
58: ToEventCallback(&CRTCDevice::HSyncCallback),
59: "CRTC H-Sync");
60: vdisp_event = evman->Regist(this,
61: ToEventCallback(&CRTCDevice::VDispCallback),
62: "CRTC V-Disp");
1.1.1.12 root 63:
64: // ラスターコピーは実際には CRTC の機能ではなく TVRAM (VRAM チップ) の
65: // 機能だが、イベントの時間管理が発生するのは CRTC 側なのでここで管理。
66: // X68030 搭載の VRAM チップは HM514402A だが、ここでは X68000 XVI 搭載の
67: // HM53461 の値を使用し、これが最小 190nsec * 2 らしいので
68: // とりあえず 400nsec としておく。
1.1.1.15 root 69: raster_event = evman->Regist(this,
70: ToEventCallback(&CRTCDevice::RasterCallback),
71: "CRTC Raster Copy");
72: raster_event->time = 400_nsec;
1.1.1.12 root 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: // すぐに水平同期と垂直帰線期間を開始する
1.1.1.15 root 86: hsync_state = 1;
87: hsync_event->time = 0;
1.1.1.10 root 88: scheduler->RestartEvent(hsync_event);
1.1.1.15 root 89: vdisp_state = 1;
90: vdisp_event->time = 0;
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.15 root 240: // 今の所、標準設定しかサポートしていないので、違う時だけログを出したりする。
241: static const uint16 crtc_supported[] = {
242: 0x0089, // R00
243: 0x000e, // R01
244: 0x001c, // R02
245: 0x007c, // R03
246: 0x0237, // R04
247: 0x0005, // R05
248: 0x0028, // R06
249: 0x0228, // R07
250: 0, // R08
251: 0, // R09
252: 0, // R10
253: 0, // R11
254: 0x0000, // R12
255: 0x0000, // R13
256: 0x0000, // R14
257: 0x0000, // R15
258: 0x0000, // R16
259: 0x0000, // R17
260: 0x0000, // R18
261: 0x0000, // R19
262: };
263:
1.1.1.8 root 264: void
1.1.1.16! root 265: CRTCDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1.1.8 root 266: {
267: int y;
268: uint16 val;
269:
270: screen.Clear();
271: y = 0;
1.1.1.9 root 272:
273: screen.Print(0, y++, "R00:$%04x (V.Total)", crtc.r[0]);
274: screen.Print(0, y++, "R01:$%04x (V.Sync End)", crtc.r[1]);
275: screen.Print(0, y++, "R02:$%04x (V.Disp Start)", crtc.r[2]);
276: screen.Print(0, y++, "R02:$%04x (V.Disp End)", crtc.r[3]);
277:
278: screen.Print(0, y++, "R04:$%04x (H.Total)", crtc.r[4]);
279: screen.Print(0, y++, "R05:$%04x (H.Sync End)", crtc.r[5]);
280: screen.Print(0, y++, "R06:$%04x (H.Disp Start)", crtc.r[6]);
281: screen.Print(0, y++, "R07:$%04x (H.Disp End)", crtc.r[7]);
282:
1.1.1.8 root 283: screen.Print(0, y++, "R10:$%04x (Text ScrollX)", crtc.r[10]);
284: screen.Print(0, y++, "R11:$%04x (Text ScrollY)", crtc.r[11]);
285:
1.1.1.15 root 286: screen.Print(0, y++, "R12:$%04x (Grp. Page0 X)", crtc.r[12]);
287: screen.Print(0, y++, "R13:$%04x (Grp. Page0 Y)", crtc.r[13]);
288: screen.Print(0, y++, "R14:$%04x (Grp. Page1 X)", crtc.r[14]);
289: screen.Print(0, y++, "R15:$%04x (Grp. Page1 Y)", crtc.r[15]);
290: screen.Print(0, y++, "R16:$%04x (Grp. Page2 X)", crtc.r[16]);
291: screen.Print(0, y++, "R17:$%04x (Grp. Page2 Y)", crtc.r[17]);
292: screen.Print(0, y++, "R18:$%04x (Grp. Page3 X)", crtc.r[18]);
293: screen.Print(0, y++, "R19:$%04x (Grp. Page3 Y)", crtc.r[19]);
294:
295: for (uint i = 0; i < countof(crtc_supported); i++) {
296: if (8 <= i && i < 12) {
297: continue;
298: }
299: if (crtc.r[i] != crtc_supported[i]) {
300: screen.Print(29, (i < 8 ? i : i - 2), TA::On,
301: "(not supported value)");
302: }
303: }
304:
305: // 3 4 5 6 7
306: // 90123456789012345678901234567890123456789012345678
307: // SIZ=1024x1024 COL=65536 HF=31kHz VD=undef HD=50MHz
1.1.1.9 root 308: val = crtc.r[20];
309: screen.Print(0, y, "R20:$%04x (Mode)", val);
1.1.1.15 root 310: screen.Print(29, y, "SIZ=%s", VideoCtlrDevice::siz_str[(val >> 10) & 1]);
311: screen.Print(43, y, "COL=%s", VideoCtlrDevice::col_str[(val >> 8) & 3]);
312: screen.Print(53, y, "HF=%ukHz", 15 + (val & 0x10));
1.1.1.9 root 313: static const char * const r20vd_str[] = {
314: "256",
315: "512",
316: "1024",
317: "undef",
318: };
1.1.1.15 root 319: screen.Print(62, y, "VD=%s", r20vd_str[(val >> 2) & 3]);
1.1.1.9 root 320: static const char * const r20hd_str[] = {
321: "256",
322: "512",
323: "768",
324: "50MHz",
325: };
1.1.1.15 root 326: screen.Print(71, y, "HD=%s", r20hd_str[val & 3]);
1.1.1.9 root 327: y++;
328:
1.1.1.8 root 329: val = crtc.r[21];
330: screen.Print(0, y, "R21:$%04x (Text Plane)", val);
331: screen.Print(29, y, "MEN=%c SA=%c AP=%%%c%c%c%c CP=%%%c%c%c%c",
332: (val & 0x0200) ? '1' : '0',
333: (val & 0x0100) ? '1' : '0',
334: (val & 0x0080) ? '1' : '0',
335: (val & 0x0040) ? '1' : '0',
336: (val & 0x0020) ? '1' : '0',
337: (val & 0x0010) ? '1' : '0',
338: (val & 0x0008) ? '1' : '0',
339: (val & 0x0004) ? '1' : '0',
340: (val & 0x0002) ? '1' : '0',
341: (val & 0x0001) ? '1' : '0');
342: y++;
343:
344: val = crtc.r[22];
345: screen.Print(0, y++, "R22:$%04x (Text Raster Copy) src=$%02x dst=$%02x",
346: val, (val >> 8), (val & 0xff));
347:
348: screen.Print(0, y++, "R23:$%04x (Text Access Mask)", crtc.r[23]);
1.1.1.15 root 349:
350: // ビデオコントローラもここに並べたほうが読みやすいか。
351: y++;
352: screen.Puts(0, y++, "<Video Controller>");
1.1.1.16! root 353: videoctlr->MonitorScreen(screen, y, crtc.r[20]);
1.1.1.8 root 354: }
355:
1.1.1.4 root 356: // CRTC レジスタへの書き込み。
357: // reg は CRTC::R00 .. CRTC::R23
1.1 root 358: void
1.1.1.4 root 359: CRTCDevice::SetReg(uint32 reg, uint32 data)
1.1 root 360: {
361: crtc.r[reg] = data;
362:
363: switch (reg) {
1.1.1.15 root 364: case CRTC::R00 ... CRTC::R07:
365: case CRTC::R12 ... CRTC::R19:
366: if (crtc.r[reg] == crtc_supported[reg]) {
367: putlog(2, "R%02u <- $%04x", reg, data);
368: return;
369: }
370: break;
1.1.1.4 root 371: case CRTC::R10:
1.1.1.12 root 372: putlog(2, "R%02u <- $%04x", reg, data);
1.1.1.10 root 373: tvram->SetScrollX(crtc.r[10]);
1.1.1.15 root 374: return;
1.1.1.4 root 375: case CRTC::R11:
1.1.1.12 root 376: putlog(2, "R%02u <- $%04x", reg, data);
1.1.1.10 root 377: tvram->SetScrollY(crtc.r[11]);
1.1.1.15 root 378: return;
1.1 root 379: case CRTC::R21:
1.1.1.4 root 380: // 頻度が高いのでログレベルを上げておく
1.1.1.12 root 381: putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.10 root 382: tvram->SetAccessPlane(crtc.r[21]);
1.1.1.15 root 383: return;
1.1.1.8 root 384: case CRTC::R22:
1.1.1.12 root 385: putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.15 root 386: return;
1.1.1.8 root 387: case CRTC::R23:
388: // 頻度が高いのでログレベルを上げておく
1.1.1.12 root 389: putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.10 root 390: tvram->SetAccessMask(crtc.r[23]);
1.1.1.15 root 391: return;
1.1 root 392: default:
1.1.1.4 root 393: break;
1.1 root 394: }
1.1.1.15 root 395: putlog(0, "R%02u <- $%04x (NOT IMPLEMENTED)", reg, data);
1.1 root 396: }
397:
1.1.1.8 root 398: // 動作ポートへの書き込み。
399: void
400: CRTCDevice::WriteOp(uint32 data)
401: {
402: uint16 oldop = crtc.op;
403:
404: crtc.op = data & 0x0b;
1.1.1.12 root 405: putlog(2, "OP <- $%04x", crtc.op);
1.1.1.8 root 406:
407: uint16 change = oldop ^ crtc.op;
408:
409: if ((change & CRTC::OP_RC)) {
410: // テキスト画面ラスターコピー
411: if ((crtc.op & CRTC::OP_RC)) {
412: // 0->1 開始
1.1.1.9 root 413: raster_copy = true;
1.1.1.12 root 414: putlog(3, "Start Raster copy (ラスターコピー指示");
1.1.1.8 root 415: } else {
1.1.1.9 root 416: // 1->0 は停止じゃなくキャンセル?
417: raster_copy = false;
1.1.1.12 root 418: putlog(3, "Cancel Raster copy (ラスターコピーキャンセル)");
1.1.1.8 root 419: }
420: }
421:
422: if ((change & 0x03)) {
1.1.1.12 root 423: putlog(0, "OP (動作ポート) $%02x <- $%02x (NOT IMPLEMENTED)",
1.1.1.8 root 424: oldop & 0x03, crtc.op & 0x03);
425: }
426: }
427:
428: // 水平同期イベント
429: void
1.1.1.15 root 430: CRTCDevice::HSyncCallback(Event *ev)
1.1.1.8 root 431: {
1.1.1.15 root 432: switch (hsync_state) {
1.1.1.9 root 433: case 0: // フロントポーチ
434: // フロントポーチ
1.1.1.15 root 435: ev->time = 2.07_usec;
436: hsync_state++;
1.1.1.9 root 437: break;
1.1.1.8 root 438:
1.1.1.9 root 439: case 1: // 水平同期パルス
1.1.1.8 root 440: // 水平同期期間
1.1.1.15 root 441: ev->time = 3.45_usec;
442: hsync_state++;
1.1.1.9 root 443:
444: // GPIP の状態を更新
1.1.1.10 root 445: mfp->SetHSync(true);
1.1.1.9 root 446:
447: // ラスターコピーが指示されていたら実行
448: if (raster_copy) {
449: raster_copy = false;
450:
1.1.1.12 root 451: uint src = (crtc.r[22] >> 8) * 4;
452: uint dst = (crtc.r[22] & 0xff) * 4;
1.1.1.10 root 453: tvram->RasterCopy(src, dst);
1.1.1.9 root 454:
455: // 所定時間後に完了させる
1.1.1.10 root 456: scheduler->RestartEvent(raster_event);
1.1.1.9 root 457: }
458: break;
459:
460: case 2: // バックポーチ + 表示期間
461: // バックポーチ + Hdisp
1.1.1.15 root 462: ev->time = 4.14_usec + 22.09_usec;
463: hsync_state = 0;
1.1.1.9 root 464:
465: // GPIP の状態を更新
1.1.1.10 root 466: mfp->SetHSync(false);
1.1.1.9 root 467: break;
1.1.1.15 root 468:
469: default:
470: __unreachable();
1.1.1.8 root 471: }
1.1.1.9 root 472:
1.1.1.10 root 473: scheduler->StartEvent(ev);
1.1.1.8 root 474: }
475:
1.1.1.9 root 476: // ラスターコピー完了イベント
477: void
1.1.1.15 root 478: CRTCDevice::RasterCallback(Event *ev)
1.1.1.9 root 479: {
480: // 終わったら動作ポートの RC ビットを %0 にする?
481: crtc.op &= ~CRTC::OP_RC;
482: }
483:
1.1 root 484: // 垂直表示・帰線イベント
1.1.1.15 root 485: // vdisp_state が 1 なら表示期間開始(V-disp)、0 なら帰線期間開始(V-blank)。
1.1 root 486: void
1.1.1.15 root 487: CRTCDevice::VDispCallback(Event *ev)
1.1 root 488: {
489: // GPIP の状態を更新
1.1.1.15 root 490: mfp->SetVDisp(vdisp_state);
1.1 root 491:
492: // 今はここで1画面まるごと作成
1.1.1.15 root 493: if (vdisp_state) {
1.1.1.13 root 494: videoctlr->VDisp();
1.1 root 495: }
496:
497: // 次のタイミングを計算
498: // XXX まだ CRTC から計算していない
1.1.1.15 root 499: if (vdisp_state) {
1.1 root 500: // 垂直表示期間
1.1.1.15 root 501: ev->time = 16250_usec;
502: vdisp_state = 0;
1.1 root 503: } else {
504: // 垂直帰線期間
1.1.1.15 root 505: ev->time = 191_usec + 1111_usec + 476_usec;
506: vdisp_state = 1;
1.1 root 507: }
1.1.1.10 root 508: scheduler->StartEvent(ev);
1.1 root 509: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.