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

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
1.1.1.6   root        7: //
                      8: // ビデオコントローラ
                      9: //
1.1       root       10: 
                     11: // e82000..e823ff パレット
                     12: // e82400..e824ff R0(e82400.w) のミラー
                     13: // e82500..e825ff R1(e82500.w) のミラー
                     14: // e82600..e826ff R2(e82600.w) のミラー
                     15: // e82700..e82fff $00 が読めるようだ
                     16: // 以上の 4KB をミラー。
                     17: 
1.1.1.6   root       18: #include "videoctlr.h"
1.1.1.11  root       19: #include "mainapp.h"
1.1.1.10  root       20: #include "planevram.h"
1.1.1.7   root       21: #include "renderer.h"
1.1.1.9   root       22: #include "scheduler.h"
1.1.1.7   root       23: #include "uimessage.h"
1.1.1.9   root       24: #include <cmath>
                     25: 
                     26: // InsideOut p.135
                     27: static const busdata read_wait  = busdata::Wait( 9 * 40_nsec);
                     28: static const busdata write_wait = busdata::Wait(11 * 40_nsec);
1.1       root       29: 
1.1.1.6   root       30: // コンストラクタ
1.1       root       31: VideoCtlrDevice::VideoCtlrDevice()
1.1.1.8   root       32:        : inherited(OBJ_VIDEOCTLR)
1.1       root       33: {
1.1.1.9   root       34:        textbmp.Create(768, 512);
1.1.1.8   root       35:        hostcolor.resize(16);
1.1.1.9   root       36: 
                     37: #if defined(HAVE_AVX2)
                     38:        enable_avx2 = gMainApp.enable_avx2;
                     39: #endif
1.1.1.10  root       40: #if defined(HAVE_NEON)
                     41:        enable_neon = gMainApp.enable_neon;
                     42: #endif
1.1       root       43: }
                     44: 
1.1.1.6   root       45: // デストラクタ
1.1       root       46: VideoCtlrDevice::~VideoCtlrDevice()
                     47: {
1.1.1.8   root       48: }
                     49: 
                     50: // 初期化
                     51: bool
                     52: VideoCtlrDevice::Init()
                     53: {
1.1.1.9   root       54:        planevram = GetPlaneVRAMDevice();
1.1.1.8   root       55:        renderer = GetRenderer();
                     56: 
1.1.1.10  root       57:        // 約30fps。これ以上速くても分からない。
                     58:        contrast_event.func = ToEventCallback(&VideoCtlrDevice::ContrastCallback);
                     59:        contrast_event.time = 30_msec;
                     60:        contrast_event.SetName("VideoCtlr Analog Contrast");
                     61:        scheduler->RegistEvent(contrast_event);
                     62: 
1.1.1.8   root       63:        return true;
1.1       root       64: }
                     65: 
1.1.1.6   root       66: // リセット
1.1.1.3   root       67: void
1.1.1.6   root       68: VideoCtlrDevice::ResetHard(bool poweron)
1.1.1.3   root       69: {
1.1.1.9   root       70:        if (poweron) {
                     71:                // 本当かどうかは知らないが、とりあえず。
                     72:                // 初期状態は放電されていれば 0 のはずで、
                     73:                // ビデオコントローラ側の出力の初期値はおそらく High のはず。
                     74:                running_contrast = 0;
                     75:                SetContrast(15);
                     76:        }
1.1.1.3   root       77: }
                     78: 
1.1.1.10  root       79: /*static*/ inline uint32
                     80: VideoCtlrDevice::Decoder(uint32 addr)
1.1.1.8   root       81: {
                     82:        return addr & 0xfff;
                     83: }
                     84: 
1.1.1.9   root       85: busdata
1.1.1.10  root       86: VideoCtlrDevice::Read(busaddr addr)
1.1       root       87: {
1.1.1.10  root       88:        uint32 paddr = addr.Addr();
                     89:        uint32 offset = Decoder(paddr);
1.1.1.9   root       90:        busdata data;
1.1.1.3   root       91: 
1.1.1.10  root       92:        data = Get16(offset & ~1U);
                     93:        if (__predict_false(loglevel >= 3)) {
                     94:                if (offset < 0x400) {                   // パレット
                     95:                        putlogn("Palette $%06x -> $%04x", (paddr & ~1U), data.Data());
                     96:                } else if (offset < 0x700) {    // R0,R1,R2
                     97:                        uint rn = Offset2Rn(offset);
                     98:                        putlogn("R%u -> $%04x", rn, data.Data());
                     99:                }
1.1       root      100:        }
1.1.1.9   root      101:        data |= read_wait;
1.1.1.10  root      102:        data |= BusData::Size2;
1.1       root      103:        return data;
                    104: }
                    105: 
1.1.1.9   root      106: busdata
1.1.1.10  root      107: VideoCtlrDevice::Write(busaddr addr, uint32 data)
1.1       root      108: {
1.1.1.10  root      109:        uint32 offset = Decoder(addr.Addr());
                    110:        uint32 reqsize = addr.GetSize();
                    111:        uint32 datasize = std::min(2 - (offset & 1U), reqsize);
                    112:        data >>= (reqsize - datasize) * 8;
                    113: 
                    114:        if (__predict_false(loglevel >= 2)) {
                    115:                if (offset < 0x400) {                   // パレット
                    116:                        putlogn("Palette $%06x.%c <- $%0*x", addr.Addr(),
                    117:                                (datasize == 1 ? 'B' : 'W'), datasize * 2, data);
                    118:                } else if (offset < 0x700) {    // R0,R1,R2
                    119:                        uint rn = Offset2Rn(offset);
                    120:                        if (datasize == 1) {
                    121:                                putlogn("R%u.%c <- $%02x (NOT IMPLEMENTED)",
                    122:                                        rn, ((offset & 1U) ? 'H' : 'L'), data);
                    123:                        } else {
                    124:                                putlogn("R%u <- $%04x (NOT IMPLEMENTED)", rn, data);
                    125:                        }
                    126:                }
1.1       root      127:        }
                    128: 
1.1.1.10  root      129:        if (datasize == 1) {
                    130:                Set8(offset, data);
1.1.1.8   root      131:        } else {
1.1.1.10  root      132:                Set16(offset, data);
1.1.1.8   root      133:        }
1.1.1.10  root      134:        busdata r = write_wait;
                    135:        r |= busdata::Size(datasize);
                    136:        return r;
1.1.1.8   root      137: }
1.1       root      138: 
1.1.1.9   root      139: busdata
1.1.1.10  root      140: VideoCtlrDevice::Peek1(uint32 addr)
1.1.1.8   root      141: {
                    142:        uint32 offset = Decoder(addr);
1.1.1.10  root      143:        uint32 data = Get16(offset & ~1U);
                    144:        if ((offset & 1U) == 0) {
                    145:                return data >> 8;
1.1.1.8   root      146:        } else {
1.1.1.10  root      147:                return data & 0xff;
1.1       root      148:        }
1.1.1.8   root      149: }
1.1       root      150: 
1.1.1.9   root      151: bool
1.1.1.10  root      152: VideoCtlrDevice::Poke1(uint32 addr, uint32 data)
1.1.1.8   root      153: {
1.1.1.10  root      154:        // パレットのみ編集可能とする。
1.1.1.8   root      155:        uint32 offset = Decoder(addr);
                    156:        if (offset < 0x400) {
                    157:                if ((int32)data >= 0) {
1.1.1.10  root      158:                        Set8(offset, data);
1.1.1.8   root      159:                }
1.1.1.9   root      160:                return true;
1.1.1.8   root      161:        }
                    162: 
1.1.1.9   root      163:        return false;
1.1       root      164: }
                    165: 
1.1.1.10  root      166: // R0, R1, R2 のアドレスオフセットからレジスタ番号 0, 1, 2 を返す。
1.1.1.8   root      167: // offset は R0, R1, R2 の領域を指していること。
1.1.1.10  root      168: /*static*/ inline uint32
                    169: VideoCtlrDevice::Offset2Rn(uint32 offset)
1.1       root      170: {
1.1.1.8   root      171:        assert(0x400 <= offset && offset < 0x700);
1.1.1.10  root      172:        return (offset >> 8) - 4;
1.1       root      173: }
                    174: 
1.1.1.10  root      175: // offset の位置の内容をワードで返す。
                    176: // offset は偶数であること。
1.1.1.8   root      177: uint32
1.1.1.10  root      178: VideoCtlrDevice::Get16(uint32 offset) const
1.1       root      179: {
1.1.1.10  root      180:        assert((offset & 1U) == 0);
                    181: 
                    182:        if (offset < 0x400) {                           // パレット
                    183:                return palette[offset >> 1];
                    184:        } else if (offset < 0x700) {            // R0,R1,R2
                    185:                uint rn = Offset2Rn(offset);
                    186:                return reg[rn];
                    187:        } else {
                    188:                return 0x00;
                    189:        }
1.1       root      190: }
                    191: 
1.1.1.10  root      192: // offset の位置のバイトを更新する。
1.1       root      193: void
1.1.1.10  root      194: VideoCtlrDevice::Set8(uint32 offset, uint32 data)
1.1       root      195: {
1.1.1.10  root      196:        uint32 offset2 = offset & ~1U;
1.1.1.8   root      197: 
1.1.1.10  root      198:        uint32 tmp = Get16(offset2);
                    199:        if ((offset & 1U) == 0) {
                    200:                tmp = (data << 8) | (tmp & 0xff);
                    201:        } else {
                    202:                tmp = (tmp & 0xff00) | data;
                    203:        }
                    204:        Set16(offset2, tmp);
                    205: }
                    206: 
                    207: // offset の位置のワードを更新する。
                    208: // offset は偶数であること。
                    209: void
                    210: VideoCtlrDevice::Set16(uint32 offset, uint32 data)
                    211: {
                    212:        assert((offset & 1U) == 0);
                    213: 
                    214:        if (offset < 0x400) {                           // パレット
                    215:                palette[offset >> 1] = data;
                    216:                MakePalette();
                    217:        } else if (offset < 0x700) {            // R0,R1,R2
                    218:                uint rn = Offset2Rn(offset);
                    219:                switch (rn) {
                    220:                 case 0:        reg[rn] = data & 0x0003U;       break;
                    221:                 case 1:        reg[rn] = data & 0x3fffU;       break;
                    222:                 case 2:        reg[rn] = data & 0xff7fU;       break;
                    223:                 default:
                    224:                        break;
                    225:                }
                    226:        } else {
                    227:                // nop
1.1.1.8   root      228:        }
1.1       root      229: }
1.1.1.2   root      230: 
1.1.1.9   root      231: // コントラストを設定する。
                    232: // 指定値は 0-15 だが、内部では 0-xff で保持。
                    233: void
1.1.1.10  root      234: VideoCtlrDevice::SetContrast(uint contrast_)
1.1.1.9   root      235: {
                    236:        target_contrast = contrast_ * 0x11;
                    237: 
                    238:        if (running_contrast != target_contrast) {
                    239:                initial_contrast = running_contrast;
                    240:                contrast_time0 = scheduler->GetVirtTime();
                    241:                scheduler->RestartEvent(contrast_event);
                    242:        }
                    243: }
                    244: 
                    245: // コントラスト変化イベント。
1.1.1.2   root      246: //
1.1.1.9   root      247: // Human68k 電源オフ時などの画面フェードアウトは ROM や Human68k がソフト
                    248: // ウェアで時間をかけてコントラストを変えているのではなく、ハードウェアで
                    249: // 実装してある。(わざわざ?) RC 回路が入っているのでおそらく意図していると
                    250: // 思われ。電源オフ時は ROM ルーチンがシステムポートにコントラスト 0 を1回
                    251: // だけ書き込んでいる。
                    252: //
                    253: // 細かいことは無視して RC 回路の過渡現象の式だけ持ってきて使う。
                    254: //
                    255: // 下がる時は E * exp(-t / RC) で、
                    256: // E は開始時の値なので initial_contrast、原点は target_contrast とする。
                    257: //
                    258: //  E |*
                    259: //    |*
                    260: //    | *
                    261: //    |  ***
                    262: //    |     ****
                    263: //  0 +---------
                    264: //
                    265: // 上がる時は E * (1 - exp(-t / RC)) で、
                    266: // この場合 E は定常値なので target_contrast、原点が initial_contrast となる。
                    267: //
                    268: //  E |     ****
                    269: //    |  ***
                    270: //    | *
                    271: //    |*
                    272: //    |*
                    273: //  0 +---------
                    274: //
                    275: // 途中まで充電されてる状態から始まる過渡現象がこれと同じかどうかは知らない
                    276: // けど、とりあえず毎回 initial と target の差だけで動作する。
                    277: void
                    278: VideoCtlrDevice::ContrastCallback(Event& ev)
                    279: {
                    280:        const float RC = 0.18;  // 時定数 1.8[KΩ]*100[uF]
                    281: 
                    282:        if (running_contrast != target_contrast) {
                    283:                // 下がる時は (initial - target) * exp(-t / RC)
                    284:                // 上がる時は (target - initial) * (1 - exp(-t / RC))
                    285:                uint64 now = scheduler->GetVirtTime();
                    286:                float t = (float)(now - contrast_time0) / 1e9;
                    287:                float e = std::exp(-t / RC);
                    288:                float n;
                    289:                if (initial_contrast > target_contrast) {
                    290:                        float v = (float)(initial_contrast - target_contrast);
                    291:                        n = target_contrast + v * e;
                    292:                } else {
                    293:                        float v = (float)(target_contrast - initial_contrast);
                    294:                        n = initial_contrast + v * (1 - e);
                    295:                }
                    296:                uint32 new_contrast = (uint32)(n + 0.5);
                    297:                putlog(2, "Contrast %x->%x: t=%g e=%g new=%x",
                    298:                        initial_contrast, target_contrast, t, e, new_contrast);
                    299: 
                    300:                if (new_contrast != running_contrast) {
                    301:                        running_contrast = new_contrast;
                    302:                }
                    303: 
                    304:                scheduler->RestartEvent(ev);
                    305:        }
                    306: }
                    307: 
1.1.1.2   root      308: void
                    309: VideoCtlrDevice::MakePalette()
                    310: {
                    311:        const uint16 *p;
                    312: 
                    313:        // パレット前半 256 ワードはグラフィックパレット、
                    314:        // テキストパレットは後半。
1.1.1.10  root      315:        p = &palette[256];
1.1.1.2   root      316: 
1.1.1.6   root      317:        // パレット情報から Color 形式の色データを作成
1.1.1.2   root      318:        // X680x0 のパレットは %GGGGG'RRRRR'BBBBB'I で並んでいる。
                    319:        for (int i = 0; i < 16; i++) {
1.1.1.10  root      320:                uint I = (p[i] & 1) << 2;       // 輝度ビット
                    321:                uint R = (((p[i] >> 6) & 0x1f) << 3) + I;
                    322:                uint G = (((p[i] >> 11)) << 3) + I;
                    323:                uint B = (((p[i] >> 1) & 0x1f) << 3) + I;
1.1.1.8   root      324:                hostcolor[i].r = R;
                    325:                hostcolor[i].g = G;
                    326:                hostcolor[i].b = B;
1.1.1.2   root      327:        }
1.1.1.6   root      328: 
1.1.1.11  root      329:        // パレット変更が起きたことをテキストレンダラに通知。
                    330:        // この後ここでグラフィックレンダラにも通知。
                    331:        planevram->Invalidate2();
1.1.1.7   root      332: 
                    333:        // UI にも通知
                    334:        UIMessage::Post(UIMessage::PALETTE);
1.1.1.2   root      335: }
1.1.1.8   root      336: 
                    337: // ゲストパレットを uint32 形式にしたものの一覧を返す。(GUI 用)
                    338: // XXX 今の所テキスト16色しか考えてない
                    339: const std::vector<uint32>
                    340: VideoCtlrDevice::GetIntPalette() const
                    341: {
                    342:        std::vector<uint32> ipal;
1.1.1.10  root      343:        const uint16 *p = &palette[256];
1.1.1.8   root      344: 
                    345:        for (int i = 0; i < 16; i++) {
1.1.1.10  root      346:                ipal.push_back(p[i]);
1.1.1.8   root      347:        }
                    348:        return ipal;
                    349: }
1.1.1.9   root      350: 
1.1.1.11  root      351: // 画面の作成。CRTC から VDisp のタイミングで呼ばれる。
                    352: // Renderer に作画指示を出すかどうかを決める。
                    353: void
                    354: VideoCtlrDevice::VDisp()
                    355: {
                    356:        bool update_needed = false;
                    357: 
                    358:        // 今はテキスト画面しかない。
                    359:        if (planevram->Snap()) {
                    360:                update_needed = true;
                    361:        }
                    362: 
                    363:        // コントラストが変わっていても再描画。
                    364:        if (__predict_false(rendering_contrast != running_contrast)) {
                    365:                pending_contrast = running_contrast;
                    366:                update_needed = true;
                    367:        }
                    368: 
                    369:        if (update_needed) {
                    370:                renderer->NotifyRender();
                    371:        }
                    372: }
                    373: 
1.1.1.9   root      374: // 画面合成。
                    375: // レンダリングスレッドから呼ばれる。
                    376: bool
1.1.1.11  root      377: VideoCtlrDevice::Render(BitmapRGBX& dst)
1.1.1.9   root      378: {
                    379:        // 今はテキスト画面しかない。
1.1.1.11  root      380:        bool updated = planevram->Render(textbmp);
1.1.1.9   root      381: 
                    382:        // 最後にコントラスト適用。
1.1.1.11  root      383:        int contrast = pending_contrast.exchange(rendering_contrast);
                    384:        if (contrast != rendering_contrast) {
                    385:                rendering_contrast = contrast;
1.1.1.9   root      386:                updated = true;
                    387:        }
                    388:        if (__predict_false(updated)) {
                    389:                RenderContrast(dst, textbmp);
                    390:                updated = true;
                    391:        }
                    392:        return updated;
                    393: }
                    394: 
                    395: #if 0
                    396: #define PERFCONT
                    397: #include "stopwatch.h"
                    398: static uint64 perf_total;
1.1.1.10  root      399: static uint perf_count;
1.1.1.9   root      400: #endif
                    401: 
                    402: // コントラストを適用。
                    403: void
                    404: VideoCtlrDevice::RenderContrast(BitmapRGBX& dst, const BitmapRGBX& src)
                    405: {
                    406:        assert(dst.GetWidth() == src.GetWidth());
                    407:        assert(dst.GetHeight() == src.GetHeight());
                    408:        assert(src.GetWidth() % 4 == 0);
                    409: 
                    410:        uint32 contrast = rendering_contrast;
                    411:        if (contrast == 255) {
                    412:                dst.CopyFrom(&src);
                    413:        } else {
                    414: #if defined(PERFCONT)
                    415:                Stopwatch sw;
                    416:                sw.Start();
                    417: #endif
                    418: 
                    419: #if defined(HAVE_AVX2)
                    420:                if (__predict_true(enable_avx2)) {
                    421:                        RenderContrast_avx2(dst, src, contrast);
                    422:                } else
                    423: #endif
1.1.1.10  root      424: #if defined(HAVE_NEON)
                    425:                if (__predict_true(enable_neon)) {
                    426:                        RenderContrast_neon(dst, src, contrast);
                    427:                } else
                    428: #endif
1.1.1.9   root      429:                {
                    430:                        RenderContrast_gen(dst, src, contrast);
                    431:                }
                    432: 
                    433: #if defined(PERFCONT)
                    434:                sw.Stop();
                    435:                perf_total += sw.Elapsed();
                    436:                if (++perf_count % 100 == 0) {
1.1.1.10  root      437:                        printf("%" PRIu64 " usec\n", perf_total / perf_count / 1000);
1.1.1.9   root      438:                }
                    439: #endif
                    440:        }
                    441: }
                    442: 
                    443: // コントラスト (0-254) を適用。C++ 版
                    444: /*static*/ void
                    445: VideoCtlrDevice::RenderContrast_gen(BitmapRGBX& dst, const BitmapRGBX& src,
                    446:        uint32 contrast)
                    447: {
                    448:        std::array<uint8, 256> lut;
                    449: 
1.1.1.10  root      450:        for (uint i = 0; i < lut.size(); i++) {
1.1.1.9   root      451:                lut[i] = (i * contrast) >> 8;
                    452:        }
                    453: 
1.1.1.10  root      454:        // この変換は一次元配列とみなしても行える。
                    455:        // また必ずテキスト画面全域なので端数の考慮は不要。
                    456:        uint pxlen = src.GetWidth() * src.GetHeight();
                    457:        const uint32 *s32 = (const uint32 *)src.GetRowPtr(0);
                    458:        const uint32 *s32end  = s32 + pxlen;
                    459:        uint32 *d32 = (uint32 *)dst.GetRowPtr(0);
                    460: 
                    461:        // 1ピクセルずつ処理する
                    462:        for (; s32 < s32end; ) {
                    463:                Color cs(*s32++);
                    464: 
                    465:                uint32 r = lut[cs.r];
                    466:                uint32 g = lut[cs.g];
                    467:                uint32 b = lut[cs.b];
1.1.1.9   root      468: 
1.1.1.10  root      469:                Color cd(r, g, b);
                    470:                *d32++ = cd.u32;
1.1.1.9   root      471:        }
                    472: }

unix.superglobalmegacorp.com

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