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