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

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

unix.superglobalmegacorp.com

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