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

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