Annotation of nono/vm/tvram.cpp, revision 1.1.1.4

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
1.1       root        7: //
                      8: // TVRAM
                      9: //
                     10: 
                     11: #include "tvram.h"
1.1.1.3   root       12: #include "crtc.h"
                     13: #include "renderer.h"
                     14: #include "videoctlr.h"
1.1.1.4 ! root       15: #if defined(__x86_64__)
        !            16: #include <x86intrin.h>
        !            17: #endif
        !            18: 
        !            19: #if defined(PERF)
        !            20: // テキスト画面合成のパフォーマンス測定。PERF をどっかで有効にしてビルド。
        !            21: // X68030 IPLROM で起動してくるあたりの画面更新で測定する。
        !            22: #include <chrono>
        !            23: static int total_elapsed;
        !            24: static int total_count;
        !            25: static std::list<int> fpsarray;
        !            26: #endif
1.1       root       27: 
                     28: //
                     29: // TVRAM はワード単位でホストバイトオーダ配置なので、
                     30: // バイトアクセス時は HB() マクロを使用のこと。
                     31: //
                     32: 
1.1.1.2   root       33: std::unique_ptr<TVRAMDevice> gTVRAM;
1.1       root       34: 
                     35: TVRAMDevice::TVRAMDevice()
                     36: {
                     37:        logname = "tvram";
                     38:        devname = "TVRAM";
                     39:        devaddr = baseaddr;
                     40:        devlen  = 512 * 1024;
                     41: 
1.1.1.2   root       42:        mem.reset(new uint8[devlen]);
1.1.1.4 ! root       43: 
        !            44:        // レンダリング時に使うテーブルを事前に計算。
        !            45:        // %abcdefgh の8ビットを
        !            46:        // %000h000g'000f000e'000d000c'000b000a
        !            47:        // の32ビットに伸張する。(各 nibble の最下位ビットに対応させる)
        !            48:        for (int i = 0; i < 256; i++) {
        !            49:                uint32_t res = 0;
        !            50:                for (int bit = 0; bit < 8; bit++) {
        !            51:                        res |= ((i >> bit) & 1) << (28 - bit * 4);
        !            52:                }
        !            53:                deptable[i] = res;
        !            54:        }
1.1       root       55: }
                     56: 
                     57: TVRAMDevice::~TVRAMDevice()
                     58: {
                     59: }
                     60: 
                     61: void
                     62: TVRAMDevice::ResetHard()
                     63: {
                     64:        // XXX ここ?
                     65:        Invalidate();
1.1.1.4 ! root       66: 
        !            67: #if defined(PERF)
        !            68:        // 分かりやすさのために表示
        !            69:        printf("measuring TVRAM performance...\n");
        !            70: #endif
1.1       root       71: }
                     72: 
1.1.1.2   root       73: inline uint64
                     74: TVRAMDevice::Decoder(uint32 addr) const
                     75: {
                     76:        return addr - baseaddr;
                     77: }
                     78: 
1.1       root       79: uint64
                     80: TVRAMDevice::Read8(uint32 addr)
                     81: {
1.1.1.2   root       82:        uint32 offset = Decoder(addr);
1.1       root       83:        return mem[HB(offset)];
                     84: }
                     85: 
                     86: uint64
                     87: TVRAMDevice::Read16(uint32 addr)
                     88: {
1.1.1.2   root       89:        uint32 offset = Decoder(addr);
1.1       root       90:        return *(uint16 *)&mem[offset];
                     91: }
                     92: 
                     93: uint64
                     94: TVRAMDevice::Write8(uint32 addr, uint32 data)
                     95: {
1.1.1.2   root       96:        uint32 offset = Decoder(addr);
1.1       root       97: 
                     98:        // XXX とりあえずね
                     99:        putlog(3, "Write $%06x <- $%02x", addr, data);
                    100: 
                    101:        if (tvram.sa) {
                    102:                // 同時アクセス
                    103:                uint32 poffset = offset & 0x1ffff;
                    104:                for (int plane = 0; plane < 4; plane++) {
                    105:                        if (tvram.ap[plane]) {
                    106:                                mem[HB(0x20000 * plane + poffset)] = data;
                    107:                        }
                    108:                }
                    109:        } else {
                    110:                // 通常アクセス
                    111:                mem[HB(offset)] = data;
                    112:        }
                    113:        atomic_dirty[(offset % 0x20000) / 128] = 1;
                    114:        return 0;
                    115: }
                    116: 
                    117: uint64
                    118: TVRAMDevice::Write16(uint32 addr, uint32 data)
                    119: {
1.1.1.2   root      120:        uint32 offset = Decoder(addr);
1.1       root      121: 
                    122:        // XXX とりあえずね
                    123:        putlog(3, "Write $%06x <- $%04x", addr, data);
                    124: 
                    125:        if (tvram.sa) {
                    126:                // 同時アクセス
                    127:                uint32 poffset = offset & 0x1ffff;
                    128:                for (int plane = 0; plane < 4; plane++) {
                    129:                        if (tvram.ap[plane]) {
                    130:                                *(uint16 *)&mem[0x20000 * plane + poffset] = data;
                    131:                        }
                    132:                }
                    133:        } else {
                    134:                // 通常アクセス
                    135:                *(uint16 *)&mem[offset] = data;
                    136:        }
                    137:        atomic_dirty[(offset % 0x20000) / 128] = 1;
                    138:        return 0;
                    139: }
                    140: 
                    141: uint64
                    142: TVRAMDevice::Peek8(uint32 addr)
                    143: {
1.1.1.2   root      144:        uint32 offset = Decoder(addr);
                    145:        return mem[HB(offset)];
1.1       root      146: }
                    147: 
                    148: // 全画面の更新フラグを立てる
                    149: void
                    150: TVRAMDevice::Invalidate()
                    151: {
                    152:        for (int i = 0; i < 1024; i++) {
                    153:                atomic_dirty[i] = 1;
                    154:        }
                    155: }
                    156: 
1.1.1.3   root      157: void
                    158: TVRAMDevice::SetScrollX(int x)
                    159: {
                    160:        tvram.xscroll = x;
                    161: }
                    162: 
                    163: void
                    164: TVRAMDevice::SetScrollY(int y)
                    165: {
                    166:        tvram.yscroll = y;
                    167:        // 全ラインを無効にする XXX とりあえずね
                    168:        Invalidate();
                    169: }
                    170: 
1.1       root      171: // CRTC R21 での同時アクセス設定を反映する
                    172: // CRTC から呼ばれる
                    173: void
                    174: TVRAMDevice::set_crtc_21(uint16 data)
                    175: {
                    176:        tvram.sa = (data & 0x100);
                    177:        tvram.ap[0] = (data & 0x10);
                    178:        tvram.ap[1] = (data & 0x20);
                    179:        tvram.ap[2] = (data & 0x40);
                    180:        tvram.ap[3] = (data & 0x80);
                    181: 
                    182:        if (0) {
                    183:                if (tvram.sa) {
                    184:                        putlog(2, "テキスト画面同時アクセス有効 %c%c%c%c",
                    185:                                tvram.ap[0] ? '0' : '-',
                    186:                                tvram.ap[1] ? '1' : '-',
                    187:                                tvram.ap[2] ? '2' : '-',
                    188:                                tvram.ap[3] ? '3' : '-');
                    189:                } else {
                    190:                        putlog(2, "テキスト画面同時アクセス無効");
                    191:                }
                    192:        }
                    193: }
                    194: 
                    195: // テキスト画面合成
                    196: bool
                    197: TVRAMDevice::Render(uint8 *imagebuf)
                    198: {
1.1.1.4 ! root      199:        const uint32 *pal;
1.1       root      200:        const uint16 *plane0;
                    201:        const uint16 *plane1;
                    202:        const uint16 *plane2;
                    203:        const uint16 *plane3;
                    204:        uint8 *dst0;
                    205:        bool updated;
                    206: 
1.1.1.4 ! root      207: #if defined(PERF)
        !           208:        auto start = std::chrono::system_clock::now();
        !           209: #endif
        !           210: 
1.1       root      211:        dst0 = imagebuf;
                    212: 
1.1.1.4 ! root      213:        // 加工済みパレットを取得
        !           214:        pal = gVideoCtlr->GetCookedPalette();
1.1       root      215: 
                    216:        updated = false;
                    217: 
                    218:        // dirty は VRAM への書き込みがあれば常に 1 にする。
                    219:        // レンダリング時に dirty が 1 なら 2 にしてレンダリング。
                    220:        // レンダリング終了時に dirty が 2 なら 0 に戻す。
                    221:        // これによりレンダリング中に VRAM への書き込みがあれば次回描画される。
                    222: 
                    223:        // sy はメモリ上の Y 座標
                    224:        // dy はレンダラ画面の Y 座標 (CRTC のXXX を考慮)
                    225:        // XXX スクロールは未実装
                    226: 
                    227:        // テキスト画面
1.1.1.2   root      228:        plane0 = (uint16 *)&mem[0x00000];
                    229:        plane1 = (uint16 *)&mem[0x20000];
                    230:        plane2 = (uint16 *)&mem[0x40000];
                    231:        plane3 = (uint16 *)&mem[0x60000];
1.1.1.3   root      232:        int sy = tvram.yscroll;
1.1       root      233:        for (int dy = 0; dy < 512; dst0 += 768 * Renderer::BPP, dy++, sy++) {
                    234:                sy &= 0x3ff;
                    235: 
1.1.1.4 ! root      236: #if defined(PERF)
        !           237:                // 計測中は常に全域を展開。
        !           238:                // 起動直後のタイトルが消えるくらいまでではほぼ更新がなく
        !           239:                // ほぼ atomic_cas の時間を測定してるだけになってしまうので。
        !           240:                atomic_dirty[sy] = 2;
        !           241:                if (1) {
        !           242: #else
1.1       root      243:                if (atomic_cas_8(&atomic_dirty[sy], 1, 2) == 1) {
1.1.1.4 ! root      244: #endif
        !           245:                        uint32 *dst = (uint32 *)dst0;
        !           246:                        uint32 *prb = rb;
        !           247: 
1.1       root      248:                        // sx はメモリ上の X オフセット (ワード単位)
                    249:                        // dx はレンダラ画面の X オフセット (ワード単位)
                    250: 
                    251:                        int sx = sy * 1024 / 16;
                    252:                        for (int dx = 0; dx < 768 / 16; dx++, sx++) {
1.1.1.4 ! root      253: #if defined(__SSE2__) && !defined(NO_SSE)
        !           254:                                __m128i a;
        !           255:                                // 各プレーン 16 ビットを DWORD * 4 に読み込む。
        !           256:                                // plane0 = A, plane1 = B, plane2 = C, plane3 = D とすると
        !           257:                                // a = $0000DDDD'0000CCCC'0000BBBB'0000AAAA;
        !           258:                                a = _mm_set_epi32(
        !           259:                                        plane3[sx],
        !           260:                                        plane2[sx],
        !           261:                                        plane1[sx],
        !           262:                                        plane0[sx]);
        !           263:                                // 各DWORD中の上位ワードに移動
        !           264:                                // a = $DDDD0000'CCCC0000'BBBB0000'AAAA0000;
        !           265:                                a = _mm_slli_si128(a, 2);
        !           266:                                for (int j = 0; j < 16; j++) {
        !           267:                                        // 各DWORDの最上位ビットを計4ビット取り出す
        !           268:                                        int c = _mm_movemask_ps(_mm_castsi128_ps(a));
        !           269:                                        // 各DWORDを左シフト
        !           270:                                        a = _mm_add_epi32(a, a);
        !           271:                                        *prb++ = pal[c];
        !           272:                                }
        !           273: #else
        !           274:                                // t0..t3 は 16 ドット× 4 プレーン分の TVRAM 生データ。
        !           275:                                // このうち先頭の2ドットのカラーコードが %abcd, %efgh で
        !           276:                                // 表される場合、t0..t3 は以下のようになっている。
        !           277:                                // t0 = %dhxxxxxx'xxxxxxxx
        !           278:                                // t1 = %cgxxxxxx'xxxxxxxx
        !           279:                                // t2 = %bfxxxxxx'xxxxxxxx
        !           280:                                // t3 = %aexxxxxx'xxxxxxxx
        !           281:                                //
        !           282:                                // deptable[] によってこの t0..t3 の 16ビットを
        !           283:                                // d0..d3 の 64ビット(16 nibble) に展開する。
        !           284:                                // d0 = %000x'000x' .. '000h'000d
        !           285:                                // d1 = %000x'000x' .. '000g'000c
        !           286:                                // d2 = %000x'000x' .. '000f'000b
        !           287:                                // d3 = %000x'000x' .. '000e'000a
        !           288:                                //
        !           289:                                // これを 1ビットずつずらして合成する。
        !           290:                                // cc = %xxxx'xxxx' .. 'efgh'abcd
        !           291:                                //
        !           292:                                // 16進数で表すと
        !           293:                                // cc = $PONMLKJI'HGFEDCBA で
        !           294:                                // $A が +0 ドット目のカラーコード、
        !           295:                                // $B が +1 ドット目のカラーコード、
        !           296:                                //  :
        !           297:                                // $P が +15ドット目のカラーコード、
        !           298:                                // となる。
        !           299: 
1.1       root      300:                                uint16 t0 = plane0[sx];
                    301:                                uint16 t1 = plane1[sx];
                    302:                                uint16 t2 = plane2[sx];
                    303:                                uint16 t3 = plane3[sx];
                    304: 
1.1.1.4 ! root      305:                                union64 d0, d1, d2, d3;
        !           306:                                d0.l = deptable[t0 >> 8];
        !           307:                                d1.l = deptable[t1 >> 8];
        !           308:                                d2.l = deptable[t2 >> 8];
        !           309:                                d3.l = deptable[t3 >> 8];
        !           310:                                d0.h = deptable[t0 & 0xff];
        !           311:                                d1.h = deptable[t1 & 0xff];
        !           312:                                d2.h = deptable[t2 & 0xff];
        !           313:                                d3.h = deptable[t3 & 0xff];
        !           314: 
        !           315:                                uint64 cc = d0.q | (d1.q << 1) | (d2.q << 2) | (d3.q << 3);
        !           316: 
1.1       root      317:                                for (int j = 0; j < 16; j++) {
1.1.1.4 ! root      318:                                        uint32 c = cc & 0xf;
        !           319:                                        *prb++ = pal[c];
        !           320:                                        cc >>= 4;
1.1       root      321:                                }
1.1.1.4 ! root      322: #endif
1.1       root      323:                        }
                    324: 
1.1.1.4 ! root      325: #if defined(__SSE4_1__) && !defined(NO_SSE)
        !           326:                        // XBGR32 16ピクセル (16 ロングワード = 64バイト) から
        !           327:                        // RGB24  16ピクセル (12 ロングワード = 48バイト) に変換する
        !           328:                        for (int j = 0; j < 768; j += 16) {
        !           329:                                // rb[0,1,2,3] = $00B0G0R0,$00B1G1R1,$00B2G2R2,$00B3G3R3
        !           330:                                // なので LE で 128ビット値としてレジスタに読み込むと
        !           331:                                // a[0] = $00B3G3R3'00B2G2R2'00B1G1R1'00B0G0R0 となる。
        !           332:                                //
        !           333:                                // ここは1文字が1バイトで x111 = $X1B1G1R1 だと思いねえ。
        !           334:                                // 入力の rb[0..15] と a[0..3] (どちらも64バイト) はこう。
        !           335:                                // rb[0,1,2,3] = a[0] = x333 x222 x111 x000
        !           336:                                // rb[4,5,6,7] = a[1] = x777 x666 x555 x444
        !           337:                                // rb[8,9,a,b] = a[2] = xbbb xaaa x999 x888
        !           338:                                // rb[c,d,e,f] = a[3] = xfff xeee xddd xccc
        !           339:                                //
        !           340:                                // suff(shuffle index) を使って a[0..3] を b[0..3] にする。
        !           341:                                // この次のステージで OR しやすい順を意識して並べ替える。
        !           342:                                // **** は使わないところ。
        !           343:                                // b[0] = **** 3332 2211 1000
        !           344:                                // b[1] = 5444 **** 7776 6655
        !           345:                                // b[2] = aa99 9888 **** bbba
        !           346:                                // b[3] = fffe eedd dccc ****
        !           347:                                //
        !           348:                                // {P,Q,R,S} 表記を4ロングワードそれぞれに対するマスクとして、
        !           349:                                // c[0] = (b[0] & {0,1,1,1}) | (b[1] & {1,0,0,0})
        !           350:                                // c[1] = (b[1] & {0,0,1,1}) | (b[2] & {1,1,0,0})
        !           351:                                // c[2] = (b[2] & {0,0,0,1}) | (b[3] & {1,1,1,0})
        !           352:                                // で
        !           353:                                // c[0] = 5444 3332 2211 1000
        !           354:                                // c[1] = aa99 9888 7776 6655
        !           355:                                // c[2] = fffe eedd dccc bbba
        !           356:                                // ができる。
        !           357: 
        !           358:                                __m128i suff0 = _mm_set_epi8(
        !           359:                                        -1,-1,-1,-1,  14,13,12,10,  9, 8, 6, 5,  4, 2, 1, 0);
        !           360:                                __m128i suff1 = _mm_set_epi8(
        !           361:                                         4, 2, 1, 0,  -1,-1,-1,-1, 14,13,12,10,  9, 8, 6, 5);
        !           362:                                __m128i suff2 = _mm_set_epi8(
        !           363:                                         9, 8, 6, 5,  4, 2, 1, 0,  -1,-1,-1,-1, 14,13,12,10);
        !           364:                                __m128i suff3 = _mm_set_epi8(
        !           365:                                        14,13,12,10,  9, 8, 6, 5,   4, 2, 1, 0, -1,-1,-1,-1);
        !           366: 
        !           367:                                __m128i a0 = _mm_load_si128((__m128i *)(&rb[j +  0]));
        !           368:                                __m128i a1 = _mm_load_si128((__m128i *)(&rb[j +  4]));
        !           369:                                __m128i a2 = _mm_load_si128((__m128i *)(&rb[j +  8]));
        !           370:                                __m128i a3 = _mm_load_si128((__m128i *)(&rb[j + 12]));
        !           371: 
        !           372:                                __m128i b0 = _mm_shuffle_epi8(a0, suff0);
        !           373:                                __m128i b1 = _mm_shuffle_epi8(a1, suff1);
        !           374:                                __m128i b2 = _mm_shuffle_epi8(a2, suff2);
        !           375:                                __m128i b3 = _mm_shuffle_epi8(a3, suff3);
        !           376: 
        !           377: #define PS(x) _mm_castsi128_ps(x)
        !           378: #define SI(x) _mm_castps_si128(x)
        !           379:                                __m128i c0 = SI(_mm_blend_ps(PS(b0), PS(b1), 0b1000));
        !           380:                                __m128i c1 = SI(_mm_blend_ps(PS(b1), PS(b2), 0b1100));
        !           381:                                __m128i c2 = SI(_mm_blend_ps(PS(b2), PS(b3), 0b1110));
        !           382: 
        !           383:                                _mm_storeu_si128((__m128i *)(&dst[0]), c0);
        !           384:                                _mm_storeu_si128((__m128i *)(&dst[4]), c1);
        !           385:                                _mm_storeu_si128((__m128i *)(&dst[8]), c2);
        !           386:                                dst += 12;
        !           387:                        }
        !           388: #else
        !           389:                        // xBGR32 4つ(16バイト)を RGB24 3つ(12バイト)に変換して
        !           390:                        // ロングワードで書き出す。
        !           391:                        for (int j = 0; j < 768; j += 4) {
        !           392:                                // rb[0]  = $x0B0G0R0
        !           393:                                // rb[1]  = $x1B1G1R1
        !           394:                                // rb[2]  = $x2B2G2R2
        !           395:                                // rb[3]  = $x3B3G3R3
        !           396:                                //
        !           397:                                // dst[0] = $00B0G0R0 | $R1000000 = $R1B0G0R0
        !           398:                                // dst[1] = $0000B1G1 | $G2R20000 = $G2R2B1G1
        !           399:                                // dst[2] = $000000B2 | $B3G3R300 = $B3G3R3B2
        !           400: 
        !           401:                                *dst++ = le32toh((rb[j + 0]      ) | (rb[j + 1] << 24));
        !           402:                                *dst++ = le32toh((rb[j + 1] >>  8) | (rb[j + 2] << 16));
        !           403:                                *dst++ = le32toh((rb[j + 2] >> 16) | (rb[j + 3] <<  8));
        !           404:                        }
        !           405: #endif
        !           406: 
1.1       root      407:                        // 2の時だけ0に戻す
                    408:                        atomic_cas_8(&atomic_dirty[sy], 2, 0);
                    409:                        updated = true;
                    410:                }
                    411:        }
                    412: 
1.1.1.4 ! root      413: #if defined(PERF)
        !           414:        auto end = std::chrono::system_clock::now();
        !           415:        total_elapsed += std::chrono::duration_cast<std::chrono::microseconds>
        !           416:                (end - start).count();
        !           417:        if (++total_count >= 100) {
        !           418:                int fps = 1000 * 1000 / (total_elapsed / total_count);
        !           419:                total_elapsed = 0;
        !           420:                total_count = 0;
        !           421:                printf("%d fps", fps);
        !           422:                fpsarray.push_back(fps);
        !           423:                if (fpsarray.size() > 4) {
        !           424:                        fpsarray.pop_front();
        !           425:                }
        !           426:                if (fpsarray.size() > 2) {
        !           427:                        int minv = 1e9;
        !           428:                        int sum = 0;
        !           429:                        for (auto v : fpsarray) {
        !           430:                                if (v < minv)
        !           431:                                        minv = v;
        !           432:                                sum += v;
        !           433:                        }
        !           434:                        // 最悪値1つを除いた移動平均
        !           435:                        sum -= minv;
        !           436:                        int avg = sum / (fpsarray.size() - 1);
        !           437:                        printf(" (avg %d)", avg);
        !           438:                }
        !           439:                printf("\n");
        !           440:        }
        !           441: #endif
        !           442: 
1.1       root      443:        return updated;
                    444: }
1.1.1.4 ! root      445: 
        !           446: // スタティック変数
        !           447: /*static*/ uint32 TVRAMDevice::deptable[256];

unix.superglobalmegacorp.com

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