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

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"
                     24: #include "renderer.h"
                     25: #include "scheduler.h"
                     26: #include "tvram.h"
                     27: 
1.1.1.11  root       28: // InsideOut p.135
                     29: const busdata wait = busdata::Wait(9 * 40_nsec);
                     30: 
1.1.1.8   root       31: // コンストラクタ
1.1       root       32: CRTCDevice::CRTCDevice()
1.1.1.10  root       33:        : inherited(OBJ_CRTC)
1.1       root       34: {
1.1.1.8   root       35:        monitor.func = ToMonitorCallback(&CRTCDevice::MonitorUpdate);
1.1.1.9   root       36:        monitor.SetSize(62, 14);
1.1.1.8   root       37:        monitor.Regist(ID_MONITOR_CRTC);
1.1       root       38: }
                     39: 
1.1.1.8   root       40: // デストラクタ
1.1       root       41: CRTCDevice::~CRTCDevice()
                     42: {
1.1.1.10  root       43: }
                     44: 
                     45: // 初期化
                     46: bool
                     47: CRTCDevice::Init()
                     48: {
                     49:        if (inherited::Init() == false) {
                     50:                return false;
                     51:        }
                     52: 
                     53:        mfp = GetMFPDevice();
                     54:        renderer = GetRenderer();
                     55:        tvram = GetTVRAMDevice();
                     56: 
1.1.1.12! root       57:        hsync_event.func = ToEventCallback(&CRTCDevice::HSyncCallback);
        !            58:        hsync_event.SetName("CRTC H-Sync");
        !            59:        vdisp_event.func = ToEventCallback(&CRTCDevice::VDispCallback);
        !            60:        vdisp_event.SetName("CRTC V-Disp");
        !            61:        scheduler->RegistEvent(hsync_event);
        !            62:        scheduler->RegistEvent(vdisp_event);
        !            63: 
        !            64:        // ラスターコピーは実際には CRTC の機能ではなく TVRAM (VRAM チップ) の
        !            65:        // 機能だが、イベントの時間管理が発生するのは CRTC 側なのでここで管理。
        !            66:        // X68030 搭載の VRAM チップは HM514402A だが、ここでは X68000 XVI 搭載の
        !            67:        // HM53461 の値を使用し、これが最小 190nsec * 2 らしいので
        !            68:        // とりあえず 400nsec としておく。
        !            69:        raster_event.func = ToEventCallback(&CRTCDevice::RasterCallback);
        !            70:        raster_event.time = 400_nsec;
        !            71:        raster_event.SetName("CRTC Raster Copy");
        !            72:        scheduler->RegistEvent(raster_event);
        !            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:        // すぐに水平同期と垂直帰線期間を開始する
                     86:        hsync_event.time = 0;
                     87:        hsync_event.code = 1;
1.1.1.10  root       88:        scheduler->RestartEvent(hsync_event);
1.1       root       89:        vdisp_event.time = 0;
                     90:        vdisp_event.code = 1;
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.8   root      240: void
                    241: CRTCDevice::MonitorUpdate(Monitor *, TextScreen& screen)
                    242: {
                    243:        int y;
                    244:        uint16 val;
                    245: 
                    246:        screen.Clear();
                    247:        y = 0;
1.1.1.9   root      248: 
                    249:        screen.Print(0, y++, "R00:$%04x (V.Total)",      crtc.r[0]);
                    250:        screen.Print(0, y++, "R01:$%04x (V.Sync End)",   crtc.r[1]);
                    251:        screen.Print(0, y++, "R02:$%04x (V.Disp Start)", crtc.r[2]);
                    252:        screen.Print(0, y++, "R02:$%04x (V.Disp End)",   crtc.r[3]);
                    253: 
                    254:        screen.Print(0, y++, "R04:$%04x (H.Total)",      crtc.r[4]);
                    255:        screen.Print(0, y++, "R05:$%04x (H.Sync End)",   crtc.r[5]);
                    256:        screen.Print(0, y++, "R06:$%04x (H.Disp Start)", crtc.r[6]);
                    257:        screen.Print(0, y++, "R07:$%04x (H.Disp End)",   crtc.r[7]);
                    258: 
1.1.1.8   root      259:        screen.Print(0, y++, "R10:$%04x (Text ScrollX)", crtc.r[10]);
                    260:        screen.Print(0, y++, "R11:$%04x (Text ScrollY)", crtc.r[11]);
                    261: 
1.1.1.9   root      262:        val = crtc.r[20];
                    263:        screen.Print(0, y,   "R20:$%04x (Mode)", val);
                    264:        screen.Print(29, y, TA::OnOff(val & CRTC::R20_MEM), "MEM");
                    265:        static const char * const r20col_str[] = {
                    266:                "16",
                    267:                "256",
                    268:                "undef",
                    269:                "65536",
                    270:        };
                    271:        screen.Print(33, y, "COL=%s", r20col_str[(val >> 8) & 3]);
                    272:        screen.Print(43, y, TA::OnOff(val & CRTC::R20_HF), "HF");
                    273:        static const char * const r20vd_str[] = {
                    274:                "256",
                    275:                "512",
                    276:                "1024",
                    277:                "undef",
                    278:        };
                    279:        screen.Print(46, y, "VD=%s", r20vd_str[(val >> 2) & 3]);
                    280:        static const char * const r20hd_str[] = {
                    281:                "256",
                    282:                "512",
                    283:                "768",
                    284:                "50MHz",
                    285:        };
                    286:        screen.Print(55, y, "HD=%s", r20hd_str[val & 3]);
                    287:        y++;
                    288: 
1.1.1.8   root      289:        val = crtc.r[21];
                    290:        screen.Print(0, y,   "R21:$%04x (Text Plane)", val);
                    291:        screen.Print(29, y,  "MEN=%c SA=%c AP=%%%c%c%c%c CP=%%%c%c%c%c",
                    292:                (val & 0x0200) ? '1' : '0',
                    293:                (val & 0x0100) ? '1' : '0',
                    294:                (val & 0x0080) ? '1' : '0',
                    295:                (val & 0x0040) ? '1' : '0',
                    296:                (val & 0x0020) ? '1' : '0',
                    297:                (val & 0x0010) ? '1' : '0',
                    298:                (val & 0x0008) ? '1' : '0',
                    299:                (val & 0x0004) ? '1' : '0',
                    300:                (val & 0x0002) ? '1' : '0',
                    301:                (val & 0x0001) ? '1' : '0');
                    302:        y++;
                    303: 
                    304:        val = crtc.r[22];
                    305:        screen.Print(0, y++, "R22:$%04x (Text Raster Copy) src=$%02x dst=$%02x",
                    306:                val, (val >> 8), (val & 0xff));
                    307: 
                    308:        screen.Print(0, y++, "R23:$%04x (Text Access Mask)", crtc.r[23]);
                    309: }
                    310: 
1.1.1.4   root      311: // CRTC レジスタへの書き込み。
                    312: // reg は CRTC::R00 .. CRTC::R23
1.1       root      313: void
1.1.1.4   root      314: CRTCDevice::SetReg(uint32 reg, uint32 data)
1.1       root      315: {
                    316:        crtc.r[reg] = data;
                    317: 
                    318:        switch (reg) {
1.1.1.4   root      319:         case CRTC::R10:
1.1.1.12! root      320:                putlog(2, "R%02u <- $%04x", reg, data);
1.1.1.10  root      321:                tvram->SetScrollX(crtc.r[10]);
1.1.1.4   root      322:                break;
                    323:         case CRTC::R11:
1.1.1.12! root      324:                putlog(2, "R%02u <- $%04x", reg, data);
1.1.1.10  root      325:                tvram->SetScrollY(crtc.r[11]);
1.1.1.4   root      326:                break;
1.1       root      327:         case CRTC::R21:
1.1.1.4   root      328:                // 頻度が高いのでログレベルを上げておく
1.1.1.12! root      329:                putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.10  root      330:                tvram->SetAccessPlane(crtc.r[21]);
1.1.1.8   root      331:                break;
                    332:         case CRTC::R22:
1.1.1.12! root      333:                putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.8   root      334:                break;
                    335:         case CRTC::R23:
                    336:                // 頻度が高いのでログレベルを上げておく
1.1.1.12! root      337:                putlog(3, "R%02u <- $%04x", reg, data);
1.1.1.10  root      338:                tvram->SetAccessMask(crtc.r[23]);
1.1       root      339:                break;
                    340:         default:
1.1.1.12! root      341:                putlog(2, "R%02u <- $%04x (NOT IMPLEMENTED)", reg, data);
1.1.1.4   root      342:                break;
1.1       root      343:        }
                    344: }
                    345: 
1.1.1.8   root      346: // 動作ポートへの書き込み。
                    347: void
                    348: CRTCDevice::WriteOp(uint32 data)
                    349: {
                    350:        uint16 oldop = crtc.op;
                    351: 
                    352:        crtc.op = data & 0x0b;
1.1.1.12! root      353:        putlog(2, "OP <- $%04x", crtc.op);
1.1.1.8   root      354: 
                    355:        uint16 change = oldop ^ crtc.op;
                    356: 
                    357:        if ((change & CRTC::OP_RC)) {
                    358:                // テキスト画面ラスターコピー
                    359:                if ((crtc.op & CRTC::OP_RC)) {
                    360:                        // 0->1 開始
1.1.1.9   root      361:                        raster_copy = true;
1.1.1.12! root      362:                        putlog(3, "Start Raster copy (ラスターコピー指示");
1.1.1.8   root      363:                } else {
1.1.1.9   root      364:                        // 1->0 は停止じゃなくキャンセル?
                    365:                        raster_copy = false;
1.1.1.12! root      366:                        putlog(3, "Cancel Raster copy (ラスターコピーキャンセル)");
1.1.1.8   root      367:                }
                    368:        }
                    369: 
                    370:        if ((change & 0x03)) {
1.1.1.12! root      371:                putlog(0, "OP (動作ポート) $%02x <- $%02x (NOT IMPLEMENTED)",
1.1.1.8   root      372:                        oldop & 0x03, crtc.op & 0x03);
                    373:        }
                    374: }
                    375: 
                    376: // 水平同期イベント
                    377: void
                    378: CRTCDevice::HSyncCallback(Event& ev)
                    379: {
1.1.1.9   root      380:        switch (ev.code) {
                    381:         case 0:        // フロントポーチ
                    382:                // フロントポーチ
                    383:                ev.time = 2.07_usec;
                    384:                ev.code++;
                    385:                break;
1.1.1.8   root      386: 
1.1.1.9   root      387:         case 1:        // 水平同期パルス
1.1.1.8   root      388:                // 水平同期期間
                    389:                ev.time = 3.45_usec;
1.1.1.9   root      390:                ev.code++;
                    391: 
                    392:                // GPIP の状態を更新
1.1.1.10  root      393:                mfp->SetHSync(true);
1.1.1.9   root      394: 
                    395:                // ラスターコピーが指示されていたら実行
                    396:                if (raster_copy) {
                    397:                        raster_copy = false;
                    398: 
1.1.1.12! root      399:                        uint src = (crtc.r[22] >> 8)   * 4;
        !           400:                        uint dst = (crtc.r[22] & 0xff) * 4;
1.1.1.10  root      401:                        tvram->RasterCopy(src, dst);
1.1.1.9   root      402: 
                    403:                        // 所定時間後に完了させる
1.1.1.10  root      404:                        scheduler->RestartEvent(raster_event);
1.1.1.9   root      405:                }
                    406:                break;
                    407: 
                    408:         case 2:        // バックポーチ + 表示期間
                    409:                // バックポーチ + Hdisp
                    410:                ev.time = 4.14_usec + 22.09_usec;
1.1.1.8   root      411:                ev.code = 0;
1.1.1.9   root      412: 
                    413:                // GPIP の状態を更新
1.1.1.10  root      414:                mfp->SetHSync(false);
1.1.1.9   root      415:                break;
1.1.1.8   root      416:        }
1.1.1.9   root      417: 
1.1.1.10  root      418:        scheduler->StartEvent(ev);
1.1.1.8   root      419: }
                    420: 
1.1.1.9   root      421: // ラスターコピー完了イベント
                    422: void
                    423: CRTCDevice::RasterCallback(Event& ev)
                    424: {
                    425:        // 終わったら動作ポートの RC ビットを %0 にする?
                    426:        crtc.op &= ~CRTC::OP_RC;
                    427: }
                    428: 
1.1       root      429: // 垂直表示・帰線イベント
1.1.1.5   root      430: // ev.code が 1 なら表示期間開始(V-disp)、0 なら帰線期間開始(V-blank)。
1.1       root      431: void
1.1.1.5   root      432: CRTCDevice::VDispCallback(Event& ev)
1.1       root      433: {
1.1.1.8   root      434:        int vdisp = ev.code;
1.1.1.5   root      435: 
1.1       root      436:        // GPIP の状態を更新
1.1.1.10  root      437:        mfp->SetVDisp(vdisp);
1.1       root      438: 
                    439:        // 今はここで1画面まるごと作成
1.1.1.5   root      440:        if (vdisp) {
1.1       root      441:                // Render に作画指示
1.1.1.12! root      442:                renderer->NotifyRender();
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.