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

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

unix.superglobalmegacorp.com

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