|
|
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);
! 38: vdisp_event.Regist("CRTC V-DISP");
! 39:
! 40: monitor.func = ToMonitorCallback(&CRTCDevice::MonitorUpdate);
! 41: monitor.SetSize(60, 5);
! 42: monitor.Regist(ID_MONITOR_CRTC);
1.1 root 43: }
44:
1.1.1.8 ! root 45: // デストラクタ
1.1 root 46: CRTCDevice::~CRTCDevice()
47: {
1.1.1.8 ! root 48: gCRTC = NULL;
1.1 root 49: }
50:
1.1.1.8 ! root 51: // リセット
1.1 root 52: void
1.1.1.8 ! root 53: CRTCDevice::ResetHard(bool poweron)
1.1 root 54: {
55: // XXX 初期値適当
56: crtc.r[20] = 0x0016;
57:
58: // XXX 適当
1.1.1.8 ! root 59: // すぐに水平同期と垂直帰線期間を開始する
! 60: hsync_event.time = 0;
! 61: hsync_event.code = 1;
! 62: gScheduler->RestartEvent(hsync_event);
1.1 root 63: vdisp_event.time = 0;
64: vdisp_event.code = 1;
1.1.1.8 ! root 65: gScheduler->RestartEvent(vdisp_event);
1.1 root 66: }
67:
68: uint64
1.1.1.6 root 69: CRTCDevice::Read(uint32 offset)
1.1 root 70: {
1.1.1.4 root 71: uint64 data;
1.1 root 72:
1.1.1.5 root 73: gMPU->AddCycle(9); // InsideOut p.135
74:
1.1.1.6 root 75: if (offset < 0x400 / 2) { // CRTC レジスタ
1.1.1.4 root 76: // 0x40 バイトでミラー
1.1.1.6 root 77: offset &= (0x40 / 2) - 1;
1.1.1.4 root 78: // R20, R21 のみ読み出し可能
1.1.1.6 root 79: switch (offset) {
1.1.1.4 root 80: case 20:
1.1.1.6 root 81: data = crtc.r[offset];
82: putlog(2, "R%02d -> $%04x", offset, (uint32)data);
1.1.1.4 root 83: break;
84: case 21:
85: // R21 へのアクセスは多いのでログレベルを上げておく
1.1.1.6 root 86: data = crtc.r[offset];
87: putlog(3, "R%02d -> $%04x", offset, (uint32)data);
1.1.1.4 root 88: break;
89: default:
90: // それ以外は読み出し不可 ($00 が読める)
91: data = 0;
1.1.1.6 root 92: if (offset < countof(crtc.r)) {
93: putlog(3, "R%02d -> $%04x", offset, (uint32)data);
1.1.1.4 root 94: } else {
95: putlog(3, "$%06x -> $%04x", gMPU->GetPaddr(), (uint32)data);
96: }
97: break;
1.1 root 98: }
99:
1.1.1.4 root 100: } else { // 動作ポート
1.1.1.6 root 101: if ((offset & (0x80 / 2)) == 0) { // +$00..+$7f
1.1.1.4 root 102: data = (uint64)-1;
1.1.1.6 root 103: } else { // +$80..+$ff
1.1.1.4 root 104: data = crtc.op;
1.1 root 105: }
106: }
107:
108: return data;
109: }
110:
111: uint64
1.1.1.6 root 112: CRTCDevice::Write(uint32 offset, uint32 data)
1.1 root 113: {
1.1.1.5 root 114: gMPU->AddCycle(9); // InsideOut p.135
115:
1.1.1.6 root 116: if (offset < 0x400 / 2) { // CRTC レジスタ
1.1.1.4 root 117: // 0x40 バイトでミラー
1.1.1.6 root 118: offset &= (0x40 / 2) - 1;
119: if (offset < countof(crtc.r)) {
120: SetReg(offset, data);
1.1 root 121: } else {
1.1.1.4 root 122: // ここ何がいるんだ?
123: putlog(2, "$%06x <- $%04x", gMPU->GetPaddr(), data);
1.1 root 124: }
125:
1.1.1.4 root 126: } else { // 動作ポート
1.1.1.6 root 127: if ((offset & (0x80 / 2)) == 0) { // +$00..+$7f
1.1.1.4 root 128: return (uint64)-1;
129: } else {
1.1.1.8 ! root 130: WriteOp(data);
1.1 root 131: }
132: }
1.1.1.4 root 133:
1.1 root 134: return 0;
135: }
136:
137: uint64
1.1.1.6 root 138: CRTCDevice::Peek(uint32 offset)
1.1 root 139: {
1.1.1.4 root 140: uint64 data;
1.1 root 141:
1.1.1.6 root 142: if (offset < 0x400 / 2) { // CRTC レジスタ
1.1.1.4 root 143: // 0x40 バイトでミラー
1.1.1.6 root 144: offset &= (0x40 / 2) - 1;
145: if (offset == 20 || offset == 21) {
1.1.1.4 root 146: // R20, R21 のみ読み出し可能
1.1.1.6 root 147: data = crtc.r[offset];
1.1.1.4 root 148: } else {
149: data = 0;
150: }
1.1 root 151:
1.1.1.4 root 152: } else { // 動作ポート
1.1.1.6 root 153: if ((offset & (0x80 / 2)) == 0) {
1.1.1.4 root 154: data = (uint64)-1;
155: } else {
156: data = crtc.op;
157: }
1.1 root 158: }
1.1.1.4 root 159:
160: return data;
1.1 root 161: }
162:
1.1.1.8 ! root 163: void
! 164: CRTCDevice::MonitorUpdate(Monitor *, TextScreen& screen)
! 165: {
! 166: int y;
! 167: uint16 val;
! 168:
! 169: screen.Clear();
! 170:
! 171: y = 0;
! 172: screen.Print(0, y++, "R10:$%04x (Text ScrollX)", crtc.r[10]);
! 173: screen.Print(0, y++, "R11:$%04x (Text ScrollY)", crtc.r[11]);
! 174:
! 175: val = crtc.r[21];
! 176: screen.Print(0, y, "R21:$%04x (Text Plane)", val);
! 177: screen.Print(29, y, "MEN=%c SA=%c AP=%%%c%c%c%c CP=%%%c%c%c%c",
! 178: (val & 0x0200) ? '1' : '0',
! 179: (val & 0x0100) ? '1' : '0',
! 180: (val & 0x0080) ? '1' : '0',
! 181: (val & 0x0040) ? '1' : '0',
! 182: (val & 0x0020) ? '1' : '0',
! 183: (val & 0x0010) ? '1' : '0',
! 184: (val & 0x0008) ? '1' : '0',
! 185: (val & 0x0004) ? '1' : '0',
! 186: (val & 0x0002) ? '1' : '0',
! 187: (val & 0x0001) ? '1' : '0');
! 188: y++;
! 189:
! 190: val = crtc.r[22];
! 191: screen.Print(0, y++, "R22:$%04x (Text Raster Copy) src=$%02x dst=$%02x",
! 192: val, (val >> 8), (val & 0xff));
! 193:
! 194: screen.Print(0, y++, "R23:$%04x (Text Access Mask)", crtc.r[23]);
! 195: }
! 196:
1.1.1.4 root 197: // CRTC レジスタへの書き込み。
198: // reg は CRTC::R00 .. CRTC::R23
1.1 root 199: void
1.1.1.4 root 200: CRTCDevice::SetReg(uint32 reg, uint32 data)
1.1 root 201: {
202: crtc.r[reg] = data;
203:
204: switch (reg) {
1.1.1.4 root 205: case CRTC::R10:
206: putlog(2, "R%02d <- $%04x", reg, data);
207: gTVRAM->SetScrollX(crtc.r[10]);
208: break;
209: case CRTC::R11:
210: putlog(2, "R%02d <- $%04x", reg, data);
211: gTVRAM->SetScrollY(crtc.r[11]);
212: break;
1.1 root 213: case CRTC::R21:
1.1.1.4 root 214: // 頻度が高いのでログレベルを上げておく
215: putlog(3, "R%02d <- $%04x", reg, data);
1.1.1.8 ! root 216: gTVRAM->SetAccessPlane(crtc.r[21]);
! 217: break;
! 218: case CRTC::R22:
! 219: putlog(3, "R%02d <- $%04x", reg, data);
! 220: break;
! 221: case CRTC::R23:
! 222: // 頻度が高いのでログレベルを上げておく
! 223: putlog(3, "R%02d <- $%04x", reg, data);
! 224: gTVRAM->SetAccessMask(crtc.r[23]);
1.1 root 225: break;
226: default:
1.1.1.4 root 227: putlog(2, "R%02d <- $%04x (未実装)", reg, data);
228: break;
1.1 root 229: }
230: }
231:
1.1.1.8 ! root 232: // 動作ポートへの書き込み。
! 233: void
! 234: CRTCDevice::WriteOp(uint32 data)
! 235: {
! 236: uint16 oldop = crtc.op;
! 237:
! 238: crtc.op = data & 0x0b;
! 239: putlog(2, "$E80480.W <- $%04x", crtc.op);
! 240:
! 241: uint16 change = oldop ^ crtc.op;
! 242:
! 243: if ((change & CRTC::OP_RC)) {
! 244: // テキスト画面ラスターコピー
! 245: if ((crtc.op & CRTC::OP_RC)) {
! 246: // 0->1 開始
! 247: StartRasterCopy();
! 248:
! 249: // XXX 本当は終わったら 0 にするはず
! 250: crtc.op &= ~CRTC::OP_RC;
! 251: } else {
! 252: // 1->0 停止
! 253: // XXX 今の所一瞬で終わるので…
! 254: //StopRasterCopy();
! 255: }
! 256: }
! 257:
! 258: if ((change & 0x03)) {
! 259: putlog(0, "動作ポート $%02x->$%02x 未実装",
! 260: oldop & 0x03, crtc.op & 0x03);
! 261: }
! 262: }
! 263:
! 264: // テキスト画面のラスターコピー開始
! 265: void
! 266: CRTCDevice::StartRasterCopy()
! 267: {
! 268: int src = (crtc.r[22] >> 8) * 4;
! 269: int dst = (crtc.r[22] & 0xff) * 4;
! 270:
! 271: gTVRAM->RasterCopy(src, dst);
! 272: }
! 273:
! 274: // 水平同期イベント
! 275: void
! 276: CRTCDevice::HSyncCallback(Event& ev)
! 277: {
! 278: int hsync = ev.code;
! 279:
! 280: // GPIP の状態を更新
! 281: gMFP->SetHSync(hsync);
! 282:
! 283: // 次のタイミングを計算
! 284: if (hsync) {
! 285: // 水平同期期間
! 286: ev.time = 3.45_usec;
! 287: ev.code = 0;
! 288: } else {
! 289: //
! 290: ev.time = 22.09_usec + 4.14_usec + 2.07_usec;
! 291: ev.code = 1;
! 292: }
! 293: gScheduler->StartEvent(ev);
! 294: }
! 295:
1.1 root 296: // 垂直表示・帰線イベント
1.1.1.5 root 297: // ev.code が 1 なら表示期間開始(V-disp)、0 なら帰線期間開始(V-blank)。
1.1 root 298: void
1.1.1.5 root 299: CRTCDevice::VDispCallback(Event& ev)
1.1 root 300: {
1.1.1.8 ! root 301: int vdisp = ev.code;
1.1.1.5 root 302:
1.1 root 303: // GPIP の状態を更新
1.1.1.5 root 304: gMFP->SetVDisp(vdisp);
1.1 root 305:
306: // 今はここで1画面まるごと作成
1.1.1.5 root 307: if (vdisp) {
1.1 root 308: // Render に作画指示
309: gRenderer->Render();
310: }
311:
312: // 次のタイミングを計算
313: // XXX まだ CRTC から計算していない
1.1.1.5 root 314: if (vdisp) {
1.1 root 315: // 垂直表示期間
1.1.1.5 root 316: ev.time = 16250_usec;
317: ev.code = 0;
1.1 root 318: } else {
319: // 垂直帰線期間
1.1.1.8 ! root 320: ev.time = 191_usec + 1111_usec + 476_usec;
1.1.1.5 root 321: ev.code = 1;
1.1 root 322: }
1.1.1.8 ! root 323: gScheduler->StartEvent(ev);
1.1 root 324: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.