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

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

unix.superglobalmegacorp.com

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