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

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: }

unix.superglobalmegacorp.com

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