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

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.13  root       19: #include "event.h"
                     20: #include "gvram.h"
1.1.1.11  root       21: #include "mainapp.h"
1.1.1.10  root       22: #include "planevram.h"
1.1.1.7   root       23: #include "renderer.h"
1.1.1.9   root       24: #include "scheduler.h"
1.1.1.13  root       25: #include "textscreen.h"
1.1.1.7   root       26: #include "uimessage.h"
1.1.1.9   root       27: #include <cmath>
                     28: 
                     29: // InsideOut p.135
                     30: static const busdata read_wait  = busdata::Wait( 9 * 40_nsec);
                     31: static const busdata write_wait = busdata::Wait(11 * 40_nsec);
1.1       root       32: 
1.1.1.6   root       33: // コンストラクタ
1.1       root       34: VideoCtlrDevice::VideoCtlrDevice()
1.1.1.8   root       35:        : inherited(OBJ_VIDEOCTLR)
1.1       root       36: {
1.1.1.13  root       37:        txbmp.Create(1024, 1024);
                     38:        grbmp.Create(1024, 1024);
                     39:        mixbmp.Create(1024, 1024);
                     40: 
                     41:        palette.resize(512);
                     42:        hostcolor.resize(512);
1.1.1.9   root       43: 
                     44: #if defined(HAVE_AVX2)
                     45:        enable_avx2 = gMainApp.enable_avx2;
                     46: #endif
1.1.1.10  root       47: #if defined(HAVE_NEON)
                     48:        enable_neon = gMainApp.enable_neon;
                     49: #endif
1.1       root       50: }
                     51: 
1.1.1.6   root       52: // デストラクタ
1.1       root       53: VideoCtlrDevice::~VideoCtlrDevice()
                     54: {
1.1.1.8   root       55: }
                     56: 
                     57: // 初期化
                     58: bool
                     59: VideoCtlrDevice::Init()
                     60: {
1.1.1.13  root       61:        gvram = GetGVRAMDevice();
1.1.1.9   root       62:        planevram = GetPlaneVRAMDevice();
1.1.1.14  root       63:        renderer = GetVideoRenderer();
1.1.1.13  root       64:        uimessage = gMainApp.GetUIMessage();
1.1.1.8   root       65: 
1.1.1.13  root       66:        auto evman = GetEventManager();
                     67:        contrast_event = evman->Regist(this,
                     68:                ToEventCallback(&VideoCtlrDevice::ContrastCallback),
                     69:                "VideoCtlr Analog Contrast");
1.1.1.10  root       70:        // 約30fps。これ以上速くても分からない。
1.1.1.13  root       71:        contrast_event->time = 30_msec;
1.1.1.10  root       72: 
1.1.1.8   root       73:        return true;
1.1       root       74: }
                     75: 
1.1.1.6   root       76: // リセット
1.1.1.3   root       77: void
1.1.1.6   root       78: VideoCtlrDevice::ResetHard(bool poweron)
1.1.1.3   root       79: {
1.1.1.9   root       80:        if (poweron) {
                     81:                // 本当かどうかは知らないが、とりあえず。
                     82:                // 初期状態は放電されていれば 0 のはずで、
                     83:                // ビデオコントローラ側の出力の初期値はおそらく High のはず。
                     84:                running_contrast = 0;
                     85:                SetContrast(15);
                     86:        }
1.1.1.13  root       87: 
                     88:        // リセット直後に再描画を起こすため、一致しない値にしておく。
                     89:        prev_reg1 = -1;
                     90:        prev_reg2 = -1;
1.1.1.3   root       91: }
                     92: 
1.1.1.10  root       93: /*static*/ inline uint32
                     94: VideoCtlrDevice::Decoder(uint32 addr)
1.1.1.8   root       95: {
                     96:        return addr & 0xfff;
                     97: }
                     98: 
1.1.1.9   root       99: busdata
1.1.1.10  root      100: VideoCtlrDevice::Read(busaddr addr)
1.1       root      101: {
1.1.1.10  root      102:        uint32 paddr = addr.Addr();
                    103:        uint32 offset = Decoder(paddr);
1.1.1.9   root      104:        busdata data;
1.1.1.3   root      105: 
1.1.1.10  root      106:        data = Get16(offset & ~1U);
                    107:        if (__predict_false(loglevel >= 3)) {
                    108:                if (offset < 0x400) {                   // パレット
                    109:                        putlogn("Palette $%06x -> $%04x", (paddr & ~1U), data.Data());
                    110:                } else if (offset < 0x700) {    // R0,R1,R2
                    111:                        uint rn = Offset2Rn(offset);
                    112:                        putlogn("R%u -> $%04x", rn, data.Data());
                    113:                }
1.1       root      114:        }
1.1.1.9   root      115:        data |= read_wait;
1.1.1.10  root      116:        data |= BusData::Size2;
1.1       root      117:        return data;
                    118: }
                    119: 
1.1.1.9   root      120: busdata
1.1.1.10  root      121: VideoCtlrDevice::Write(busaddr addr, uint32 data)
1.1       root      122: {
1.1.1.10  root      123:        uint32 offset = Decoder(addr.Addr());
                    124:        uint32 reqsize = addr.GetSize();
                    125:        uint32 datasize = std::min(2 - (offset & 1U), reqsize);
                    126:        data >>= (reqsize - datasize) * 8;
                    127: 
                    128:        if (__predict_false(loglevel >= 2)) {
                    129:                if (offset < 0x400) {                   // パレット
                    130:                        putlogn("Palette $%06x.%c <- $%0*x", addr.Addr(),
                    131:                                (datasize == 1 ? 'B' : 'W'), datasize * 2, data);
                    132:                } else if (offset < 0x700) {    // R0,R1,R2
                    133:                        uint rn = Offset2Rn(offset);
                    134:                        if (datasize == 1) {
1.1.1.13  root      135:                                putlogn("R%u.%c <- $%02x",
1.1.1.10  root      136:                                        rn, ((offset & 1U) ? 'H' : 'L'), data);
                    137:                        } else {
1.1.1.13  root      138:                                putlogn("R%u <- $%04x", rn, data);
1.1.1.10  root      139:                        }
                    140:                }
1.1       root      141:        }
                    142: 
1.1.1.10  root      143:        if (datasize == 1) {
                    144:                Set8(offset, data);
1.1.1.8   root      145:        } else {
1.1.1.10  root      146:                Set16(offset, data);
1.1.1.8   root      147:        }
1.1.1.10  root      148:        busdata r = write_wait;
                    149:        r |= busdata::Size(datasize);
                    150:        return r;
1.1.1.8   root      151: }
1.1       root      152: 
1.1.1.9   root      153: busdata
1.1.1.10  root      154: VideoCtlrDevice::Peek1(uint32 addr)
1.1.1.8   root      155: {
                    156:        uint32 offset = Decoder(addr);
1.1.1.10  root      157:        uint32 data = Get16(offset & ~1U);
                    158:        if ((offset & 1U) == 0) {
                    159:                return data >> 8;
1.1.1.8   root      160:        } else {
1.1.1.10  root      161:                return data & 0xff;
1.1       root      162:        }
1.1.1.8   root      163: }
1.1       root      164: 
1.1.1.9   root      165: bool
1.1.1.10  root      166: VideoCtlrDevice::Poke1(uint32 addr, uint32 data)
1.1.1.8   root      167: {
1.1.1.10  root      168:        // パレットのみ編集可能とする。
1.1.1.8   root      169:        uint32 offset = Decoder(addr);
                    170:        if (offset < 0x400) {
                    171:                if ((int32)data >= 0) {
1.1.1.10  root      172:                        Set8(offset, data);
1.1.1.8   root      173:                }
1.1.1.9   root      174:                return true;
1.1.1.8   root      175:        }
                    176: 
1.1.1.9   root      177:        return false;
1.1       root      178: }
                    179: 
1.1.1.13  root      180: // モニタ更新の下請け。CRTC から呼ばれる。
                    181: int
1.1.1.15! root      182: VideoCtlrDevice::MonitorScreen(TextScreen& screen, int y, uint32 crtc_r20)
1.1.1.13  root      183: {
                    184:        std::array<uint16, 3> tmp = reg;
                    185: 
                    186:        for (uint i = 0; i < tmp.size(); i++) {
                    187:                screen.Print(0, y + i, "$%06x R%u: $%04x",
                    188:                        baseaddr + 0x400 + i * 0x100, i, tmp[i]);
                    189:        }
                    190: 
                    191:        // R0 (p.234)
                    192:        // CRTC R20 の bit10-8 と同じでなければいけないと書いてあるが、
                    193:        // 違ったら CRTC のほうが使われてるっぽいので、こっちを赤とかにしてみる。
                    194:        // (NetBSD/x68k の X)
                    195:        uint tmp0 = tmp[0] & 7;
                    196:        uint diff = tmp0 ^ (crtc_r20 >> 8);
                    197:        screen.Print(18, y, ((diff & R0_SIZ) ? TA::ReverseRed : TA::Normal),
                    198:                "SIZ=%s", siz_str[tmp0 >> 2]);
                    199:        screen.Print(32, y, ((diff & R0_COL) ? TA::ReverseRed : TA::Normal),
                    200:                "COL=%s", col_str[tmp0 & R0_COL]);
                    201:        // ちょっと間借りして内部情報を表示。
                    202:        {
                    203:                char sobuf[32];
                    204:                int len = strlcpy(sobuf, "(SCREEN_ORDER=", sizeof(sobuf));
                    205:                for (uint32 s = screen_order; s != 0; s >>= 4) {
                    206:                        static const char so_str[] = ".STGBK";
                    207:                        sobuf[len++] = so_str[s & 0xf];
                    208:                }
                    209:                sobuf[len] = '\0';
                    210:                len = strlcat(sobuf, ")", sizeof(sobuf));
                    211:                screen.Puts(screen.GetCol() - len, y, sobuf);
                    212:        }
                    213:        y++;
                    214: 
                    215:        // R1 (p.188)
                    216:        {
                    217:                std::array<const char *, 4> plist {};
                    218:                uint sp = (tmp[1] >> 12) & 3;
                    219:                uint tp = (tmp[1] >> 10) & 3;
                    220:                uint gp = (tmp[1] >>  8) & 3;
                    221:                plist[sp] = "SP";
                    222:                plist[tp] = "TX";
                    223:                plist[gp] = "GR";
                    224:                std::string pstr;
                    225:                if (plist[3]) {
                    226:                        pstr = "Invalid";
                    227:                } else if (plist[0] == NULL || plist[1] == NULL || plist[2] == NULL) {
                    228:                        pstr = "Conflict";
                    229:                } else {
                    230:                        pstr = string_format("%s > %s > %s", plist[0], plist[1], plist[2]);
                    231:                }
                    232: 
                    233:                uint gn =  tmp[1] & 0xff;
                    234:                std::string gstr;
                    235:                switch (tmp0 & 3) {
                    236:                 case 0:        // 16色モード、グラフィックページは4枚。
                    237:                 {
                    238:                        std::array<const char *, 4> glist {};
                    239:                        uint p3 = (tmp[1] >> 6) & 3;
                    240:                        uint p2 = (tmp[1] >> 4) & 3;
                    241:                        uint p1 = (tmp[1] >> 2) & 3;
                    242:                        uint p0 =  tmp[1]       & 3;
                    243:                        glist[p0] = "P0";
                    244:                        glist[p1] = "P1";
                    245:                        glist[p2] = "P2";
                    246:                        glist[p3] = "P3";
                    247:                        if (glist[0] && glist[1] && glist[2] && glist[3]) {
                    248:                                gstr = string_format("%s > %s > %s > %s",
                    249:                                        glist[0], glist[1], glist[2], glist[3]);
                    250:                        } else {
                    251:                                gstr = "Conflict";
                    252:                        }
                    253:                        break;
                    254:                 }
                    255: 
                    256:                 case 1:        // 256色モード、グラフィックページは2枚。
                    257:                        // この場合 $e4 か $4e だけが有効。
                    258:                        if (gn == 0xe4) {
                    259:                                gstr = "P0 > P1";
                    260:                        } else if (gn == 0x4e) {
                    261:                                gstr = "P1 > P0";
                    262:                        } else {
                    263:                                gstr = "Invalid";
                    264:                        }
                    265:                        break;
                    266: 
                    267:                 case 2:        // 未定義
                    268:                        gstr = "?";
                    269:                        break;
                    270: 
                    271:                 case 3:        // 65536色モード、グラフィックページは1枚。
                    272:                        gstr = "P0";
                    273:                        break;
                    274: 
                    275:                 default:
                    276:                        __unreachable();
                    277:                }
                    278: 
                    279:                //   2         3         4         5         6
                    280:                // 89012345678901234567890123456789012345678901
                    281:                // Priority=SP > TX > GR,  GP=P0 > P1 > P2 > P3
                    282:                screen.Print(18, y, "Priority=%s,", pstr.c_str());
                    283:                screen.Print(42, y, "GP=%s", gstr.c_str());
                    284:        }
                    285:        y++;
                    286: 
                    287:        // R2 (p.210)
                    288:        uint x = 18;
                    289:        uint tmp2 = tmp[2];
                    290:        screen.Print(x +  0, y, TA::OnOff(tmp2 & R2_YS), "YS");
                    291:        screen.Print(x +  3, y, TA::OnOff(tmp2 & R2_AH), "AH");
                    292:        screen.Print(x +  6, y, TA::OnOff(tmp2 & R2_VH), "VH");
                    293:        screen.Print(x +  9, y, TA::OnOff(tmp2 & R2_EX), "EX");
                    294:        screen.Print(x + 12, y, TA::OnOff(tmp2 & R2_HP), "HP");
                    295:        screen.Print(x + 15, y, TA::OnOff(tmp2 & R2_BP), "BP");
                    296:        screen.Print(x + 18, y, TA::OnOff(tmp2 & R2_GG), "GG");
                    297:        screen.Print(x + 21, y, TA::OnOff(tmp2 & R2_GT), "GT");
                    298:        screen.Print(x + 24, y, TA::OnOff(tmp2 & 0x0080), "0");
                    299:        screen.Print(x + 26, y, TA::OnOff(tmp2 & R2_SP_ON), "SP");
                    300:        screen.Print(x + 29, y, TA::OnOff(tmp2 & R2_TX_ON), "TX");
                    301:        screen.Print(x + 32, y, TA::OnOff(tmp2 & R2_GR_ON), "GR");
                    302:        screen.Print(x + 35, y, TA::OnOff(tmp2 & R2_G3_ON), "G3");
                    303:        screen.Print(x + 38, y, TA::OnOff(tmp2 & R2_G2_ON), "G2");
                    304:        screen.Print(x + 41, y, TA::OnOff(tmp2 & R2_G1_ON), "G1");
                    305:        screen.Print(x + 44, y, TA::OnOff(tmp2 & R2_G0_ON), "G0");
                    306:        y++;
                    307: 
                    308:        return y;
                    309: }
                    310: 
                    311: // CRTC からも使う。
                    312: /*static*/ const char * const
                    313: VideoCtlrDevice::siz_str[2] = {
                    314:        "512x512",
                    315:        "1024x1024",
                    316: };
                    317: 
                    318: /*static*/ const char * const
                    319: VideoCtlrDevice::col_str[4] = {
                    320:        "16",
                    321:        "256",
                    322:        "undef",
                    323:        "65536",
                    324: };
                    325: 
1.1.1.10  root      326: // R0, R1, R2 のアドレスオフセットからレジスタ番号 0, 1, 2 を返す。
1.1.1.8   root      327: // offset は R0, R1, R2 の領域を指していること。
1.1.1.10  root      328: /*static*/ inline uint32
                    329: VideoCtlrDevice::Offset2Rn(uint32 offset)
1.1       root      330: {
1.1.1.8   root      331:        assert(0x400 <= offset && offset < 0x700);
1.1.1.10  root      332:        return (offset >> 8) - 4;
1.1       root      333: }
                    334: 
1.1.1.10  root      335: // offset の位置の内容をワードで返す。
                    336: // offset は偶数であること。
1.1.1.8   root      337: uint32
1.1.1.10  root      338: VideoCtlrDevice::Get16(uint32 offset) const
1.1       root      339: {
1.1.1.10  root      340:        assert((offset & 1U) == 0);
                    341: 
                    342:        if (offset < 0x400) {                           // パレット
                    343:                return palette[offset >> 1];
                    344:        } else if (offset < 0x700) {            // R0,R1,R2
                    345:                uint rn = Offset2Rn(offset);
                    346:                return reg[rn];
                    347:        } else {
                    348:                return 0x00;
                    349:        }
1.1       root      350: }
                    351: 
1.1.1.10  root      352: // offset の位置のバイトを更新する。
1.1       root      353: void
1.1.1.10  root      354: VideoCtlrDevice::Set8(uint32 offset, uint32 data)
1.1       root      355: {
1.1.1.10  root      356:        uint32 offset2 = offset & ~1U;
1.1.1.8   root      357: 
1.1.1.10  root      358:        uint32 tmp = Get16(offset2);
                    359:        if ((offset & 1U) == 0) {
                    360:                tmp = (data << 8) | (tmp & 0xff);
                    361:        } else {
                    362:                tmp = (tmp & 0xff00) | data;
                    363:        }
                    364:        Set16(offset2, tmp);
                    365: }
                    366: 
                    367: // offset の位置のワードを更新する。
                    368: // offset は偶数であること。
                    369: void
                    370: VideoCtlrDevice::Set16(uint32 offset, uint32 data)
                    371: {
                    372:        assert((offset & 1U) == 0);
                    373: 
                    374:        if (offset < 0x400) {                           // パレット
                    375:                palette[offset >> 1] = data;
                    376:                MakePalette();
                    377:        } else if (offset < 0x700) {            // R0,R1,R2
                    378:                uint rn = Offset2Rn(offset);
                    379:                switch (rn) {
                    380:                 case 0:        reg[rn] = data & 0x0003U;       break;
                    381:                 case 1:        reg[rn] = data & 0x3fffU;       break;
                    382:                 case 2:        reg[rn] = data & 0xff7fU;       break;
                    383:                 default:
                    384:                        break;
                    385:                }
                    386:        } else {
                    387:                // nop
1.1.1.8   root      388:        }
1.1       root      389: }
1.1.1.2   root      390: 
1.1.1.9   root      391: // コントラストを設定する。
                    392: // 指定値は 0-15 だが、内部では 0-xff で保持。
                    393: void
1.1.1.10  root      394: VideoCtlrDevice::SetContrast(uint contrast_)
1.1.1.9   root      395: {
                    396:        target_contrast = contrast_ * 0x11;
                    397: 
                    398:        if (running_contrast != target_contrast) {
                    399:                initial_contrast = running_contrast;
                    400:                contrast_time0 = scheduler->GetVirtTime();
                    401:                scheduler->RestartEvent(contrast_event);
                    402:        }
                    403: }
                    404: 
                    405: // コントラスト変化イベント。
1.1.1.2   root      406: //
1.1.1.9   root      407: // Human68k 電源オフ時などの画面フェードアウトは ROM や Human68k がソフト
                    408: // ウェアで時間をかけてコントラストを変えているのではなく、ハードウェアで
                    409: // 実装してある。(わざわざ?) RC 回路が入っているのでおそらく意図していると
                    410: // 思われ。電源オフ時は ROM ルーチンがシステムポートにコントラスト 0 を1回
                    411: // だけ書き込んでいる。
                    412: //
                    413: // 細かいことは無視して RC 回路の過渡現象の式だけ持ってきて使う。
                    414: //
                    415: // 下がる時は E * exp(-t / RC) で、
                    416: // E は開始時の値なので initial_contrast、原点は target_contrast とする。
                    417: //
                    418: //  E |*
                    419: //    |*
                    420: //    | *
                    421: //    |  ***
                    422: //    |     ****
1.1.1.13  root      423: //  0 +---------> t
1.1.1.9   root      424: //
                    425: // 上がる時は E * (1 - exp(-t / RC)) で、
                    426: // この場合 E は定常値なので target_contrast、原点が initial_contrast となる。
                    427: //
                    428: //  E |     ****
                    429: //    |  ***
                    430: //    | *
                    431: //    |*
                    432: //    |*
1.1.1.13  root      433: //  0 +---------> t
1.1.1.9   root      434: //
                    435: // 途中まで充電されてる状態から始まる過渡現象がこれと同じかどうかは知らない
                    436: // けど、とりあえず毎回 initial と target の差だけで動作する。
                    437: void
1.1.1.13  root      438: VideoCtlrDevice::ContrastCallback(Event *ev)
1.1.1.9   root      439: {
                    440:        const float RC = 0.18;  // 時定数 1.8[KΩ]*100[uF]
                    441: 
                    442:        if (running_contrast != target_contrast) {
                    443:                // 下がる時は (initial - target) * exp(-t / RC)
                    444:                // 上がる時は (target - initial) * (1 - exp(-t / RC))
                    445:                uint64 now = scheduler->GetVirtTime();
1.1.1.15! root      446:                float t = (float)(now - contrast_time0) / 1_sec;
1.1.1.9   root      447:                float e = std::exp(-t / RC);
                    448:                float n;
                    449:                if (initial_contrast > target_contrast) {
                    450:                        float v = (float)(initial_contrast - target_contrast);
                    451:                        n = target_contrast + v * e;
                    452:                } else {
                    453:                        float v = (float)(target_contrast - initial_contrast);
                    454:                        n = initial_contrast + v * (1 - e);
                    455:                }
                    456:                uint32 new_contrast = (uint32)(n + 0.5);
                    457:                putlog(2, "Contrast %x->%x: t=%g e=%g new=%x",
                    458:                        initial_contrast, target_contrast, t, e, new_contrast);
                    459: 
                    460:                if (new_contrast != running_contrast) {
                    461:                        running_contrast = new_contrast;
                    462:                }
                    463: 
                    464:                scheduler->RestartEvent(ev);
                    465:        }
                    466: }
                    467: 
1.1.1.2   root      468: void
                    469: VideoCtlrDevice::MakePalette()
                    470: {
1.1.1.6   root      471:        // パレット情報から Color 形式の色データを作成
1.1.1.2   root      472:        // X680x0 のパレットは %GGGGG'RRRRR'BBBBB'I で並んでいる。
1.1.1.13  root      473:        for (int i = 0; i < palette.size(); i++) {
                    474:                uint I = (palette[i] & 1) << 2; // 輝度ビット
                    475:                uint R = (((palette[i] >> 6) & 0x1f) << 3) + I;
                    476:                uint G = (((palette[i] >> 11)) << 3) + I;
                    477:                uint B = (((palette[i] >> 1) & 0x1f) << 3) + I;
1.1.1.8   root      478:                hostcolor[i].r = R;
                    479:                hostcolor[i].g = G;
                    480:                hostcolor[i].b = B;
1.1.1.2   root      481:        }
1.1.1.6   root      482: 
1.1.1.11  root      483:        // パレット変更が起きたことをテキストレンダラに通知。
                    484:        // この後ここでグラフィックレンダラにも通知。
                    485:        planevram->Invalidate2();
1.1.1.7   root      486: 
                    487:        // UI にも通知
1.1.1.13  root      488:        uimessage->Post(UIMessage::PALETTE);
1.1.1.8   root      489: }
1.1.1.9   root      490: 
1.1.1.11  root      491: // 画面の作成。CRTC から VDisp のタイミングで呼ばれる。
1.1.1.14  root      492: // VideoRenderer に作画指示を出すかどうかを決める。
1.1.1.11  root      493: void
                    494: VideoCtlrDevice::VDisp()
                    495: {
                    496:        bool update_needed = false;
                    497: 
1.1.1.13  root      498:        // 各画面に更新があるか。モニタ用にもなるためオフでも行う。
1.1.1.11  root      499:        if (planevram->Snap()) {
                    500:                update_needed = true;
                    501:        }
1.1.1.13  root      502:        if (gvram->Snap()) {
                    503:                update_needed = true;
                    504:        }
                    505: 
                    506:        // プライオリティかオンオフが変わっていても再描画。
                    507:        if (reg[1] != prev_reg1 || (reg[2] & 0xff) != prev_reg2) {
                    508:                std::array<uint32, 4> list {};
                    509:                // オンオフと描画順。
                    510:                // XXX グラフィック画面のプレーン(ページ)は未対応
                    511: #if 0
                    512:                if ((reg[2] & R2_SP_ON)) {
                    513:                        uint sp = (reg[1] >> 12) & 3;
                    514:                        list[sp] = SORDER_SP;
                    515:                }
                    516: #endif
                    517:                if ((reg[2] & R2_TX_ON)) {
                    518:                        uint tp = (reg[1] >> 10) & 3;
                    519:                        list[tp] = SORDER_TX;
                    520:                }
                    521:                if ((reg[2] & R2_GR_ON)) {
                    522:                        uint gp = (reg[1] >>  8) & 3;
                    523:                        list[gp] = SORDER_GR;
                    524:                }
                    525: 
                    526:                uint32 new_order = 0;
                    527:                for (uint i = 0; i < 3; i++) {
                    528:                        if (list[i] != SORDER_NONE) {
                    529:                                new_order <<= 4;
                    530:                                new_order |= list[i];
                    531:                        }
                    532:                }
                    533:                // 全レイヤーオフなら仮想的な黒画面を出力。
                    534:                if (new_order == 0) {
                    535:                        new_order = SORDER_BLACK;
                    536:                }
                    537:                // 変化があれば再描画。
                    538:                if (new_order != screen_order) {
                    539:                        screen_order = new_order;
                    540:                        update_needed = true;
                    541:                }
                    542: 
                    543:                prev_reg1 = reg[1];
                    544:                prev_reg2 = reg[2] & 0xff;
                    545:        }
1.1.1.11  root      546: 
                    547:        // コントラストが変わっていても再描画。
                    548:        if (__predict_false(rendering_contrast != running_contrast)) {
                    549:                pending_contrast = running_contrast;
                    550:                update_needed = true;
                    551:        }
                    552: 
                    553:        if (update_needed) {
                    554:                renderer->NotifyRender();
                    555:        }
                    556: }
                    557: 
1.1.1.9   root      558: // 画面合成。
                    559: // レンダリングスレッドから呼ばれる。
                    560: bool
1.1.1.11  root      561: VideoCtlrDevice::Render(BitmapRGBX& dst)
1.1.1.9   root      562: {
1.1.1.13  root      563:        uint32 local_screen_order = screen_order;
                    564: 
                    565:        // 各画面を描画。各画面(レイヤー)はモニタ表示用としても使うため、
                    566:        // 表示オンオフに関わらず、常に作成は行う。
                    567:        bool tx_updated = planevram->Render(txbmp);
                    568:        bool gr_updated = gvram->Render(grbmp);
                    569: 
                    570:        // mixbmp に合成。mixbmp もモニタ表示に使う。
                    571:        // XXX 重ね合わせはまだ
                    572:        bool mix_updated = false;
                    573:        for (uint32 s = local_screen_order; s != 0; s >>= 4) {
                    574:                switch (s & 0xf) {
                    575:                 case SORDER_TX:
                    576:                        mixbmp.CopyFrom(&txbmp);
                    577:                        mix_updated = tx_updated;
                    578:                        break;
                    579:                 case SORDER_GR:
                    580:                        mixbmp.CopyFrom(&grbmp);
                    581:                        mix_updated = gr_updated;
                    582:                        break;
                    583:                 case SORDER_BLACK:
                    584:                        mixbmp.Fill(0);
                    585:                        mix_updated = true;
                    586:                        break;
                    587:                 case SORDER_SP:
                    588:                 case SORDER_BG:
                    589:                 default:
                    590:                        break;
                    591:                }
                    592:        }
1.1.1.9   root      593: 
                    594:        // 最後にコントラスト適用。
1.1.1.13  root      595:        bool updated = false;
1.1.1.11  root      596:        int contrast = pending_contrast.exchange(rendering_contrast);
                    597:        if (contrast != rendering_contrast) {
                    598:                rendering_contrast = contrast;
1.1.1.9   root      599:                updated = true;
                    600:        }
1.1.1.13  root      601:        if (__predict_false(mix_updated || updated)) {
                    602:                RenderContrast(dst, mixbmp);
1.1.1.9   root      603:                updated = true;
                    604:        }
1.1.1.13  root      605: 
1.1.1.9   root      606:        return updated;
                    607: }
                    608: 
                    609: #if 0
                    610: #define PERFCONT
                    611: #include "stopwatch.h"
                    612: static uint64 perf_total;
1.1.1.10  root      613: static uint perf_count;
1.1.1.9   root      614: #endif
                    615: 
                    616: // コントラストを適用。
                    617: void
                    618: VideoCtlrDevice::RenderContrast(BitmapRGBX& dst, const BitmapRGBX& src)
                    619: {
                    620:        assert(src.GetWidth() % 4 == 0);
                    621: 
                    622:        uint32 contrast = rendering_contrast;
1.1.1.13  root      623:        if (__predict_false(contrast == 255)) {
                    624:                Rect rect(0, 0, dst.GetWidth(), dst.GetHeight());
                    625:                dst.DrawBitmap(0, 0, src, rect);
                    626:        } else if (__predict_false(contrast == 0)) {
                    627:                uint8 *d = dst.GetBuf();
                    628:                memset(d, 0, dst.GetStride() * dst.GetHeight());
1.1.1.9   root      629:        } else {
                    630: #if defined(PERFCONT)
                    631:                Stopwatch sw;
                    632:                sw.Start();
                    633: #endif
                    634: 
                    635: #if defined(HAVE_AVX2)
                    636:                if (__predict_true(enable_avx2)) {
                    637:                        RenderContrast_avx2(dst, src, contrast);
                    638:                } else
                    639: #endif
1.1.1.10  root      640: #if defined(HAVE_NEON)
                    641:                if (__predict_true(enable_neon)) {
                    642:                        RenderContrast_neon(dst, src, contrast);
                    643:                } else
                    644: #endif
1.1.1.9   root      645:                {
                    646:                        RenderContrast_gen(dst, src, contrast);
                    647:                }
                    648: 
                    649: #if defined(PERFCONT)
                    650:                sw.Stop();
1.1.1.15! root      651:                perf_total += sw.Elapsed_nsec();
1.1.1.9   root      652:                if (++perf_count % 100 == 0) {
1.1.1.15! root      653:                        printf("%" PRIu64 " usec\n", nsec_to_usec(perf_total) / perf_count);
1.1.1.9   root      654:                }
                    655: #endif
                    656:        }
                    657: }
                    658: 
1.1.1.13  root      659: // コントラスト (1-254) を適用。dst サイズでクリップする。C++ 版。
1.1.1.9   root      660: /*static*/ void
                    661: VideoCtlrDevice::RenderContrast_gen(BitmapRGBX& dst, const BitmapRGBX& src,
                    662:        uint32 contrast)
                    663: {
                    664:        std::array<uint8, 256> lut;
                    665: 
1.1.1.10  root      666:        for (uint i = 0; i < lut.size(); i++) {
1.1.1.9   root      667:                lut[i] = (i * contrast) >> 8;
                    668:        }
                    669: 
1.1.1.13  root      670:        for (uint y = 0, yend = dst.GetHeight(); y < yend; y++) {
                    671:                const uint32 *s32 = (const uint32 *)src.GetRowPtr(y);
                    672:                uint32 *d32 = (uint32 *)dst.GetRowPtr(y);
                    673:                for (uint x = 0, xend = dst.GetWidth(); x < xend; x++) {
                    674:                        // 1ピクセルずつ処理する。
                    675:                        Color cs(*s32++);
                    676: 
                    677:                        uint32 r = lut[cs.r];
                    678:                        uint32 g = lut[cs.g];
                    679:                        uint32 b = lut[cs.b];
1.1.1.9   root      680: 
1.1.1.13  root      681:                        Color cd(r, g, b);
                    682:                        *d32++ = cd.u32;
                    683:                }
1.1.1.9   root      684:        }
                    685: }

unix.superglobalmegacorp.com

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