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

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