Annotation of nono/vm/crtc.cpp, revision 1.1.1.13

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.