Annotation of nono/vm/adpcm.cpp, revision 1.1.1.7

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2022 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // ADPCM (MSM6258V)
                      9: //
                     10: 
1.1.1.7 ! root       11: // ミキサー周波数はこれらがすべて割り切れるよう 62500 [Hz] (= 16 usec)
        !            12: // にする。
        !            13: //
        !            14: //     clk  div        freq            mixerfreq に対する倍率
        !            15: //     ---- ----       --------        ---
        !            16: //     8MHz 512        15625           4
        !            17: //     8MHz 768        10416.67        6
        !            18: //     8MHz 1024        7812.5         8
        !            19: //     4MHz 512         〃
        !            20: //     4MHz 768         5208.33        12
        !            21: //     4MHz 1024        3906.25        16
        !            22: 
        !            23: // ある 16 usec * (freq による倍率) の時間内での書き込みは、このサンプル
        !            24: // の代表値。1期間内に1回しか来ないはず。
        !            25: // かつ、ブロックは 40 msec で割り切れる時間ごとに区切られているため、
        !            26: // 前回の書き込み位置の次、ではなく、現在時刻に応じた位置に書き出す。
        !            27: 
1.1       root       28: #include "adpcm.h"
                     29: #include "dmac.h"
1.1.1.6   root       30: #include "event.h"
1.1.1.7 ! root       31: #include "monitor.h"
1.1       root       32: #include "mpu.h"
                     33: #include "scheduler.h"
1.1.1.7 ! root       34: #include "sound.h"
1.1       root       35: 
                     36: // コンストラクタ
                     37: ADPCMDevice::ADPCMDevice()
1.1.1.2   root       38:        : inherited(OBJ_ADPCM)
1.1       root       39: {
1.1.1.7 ! root       40:        monitor = gMonitorManager->Regist(ID_MONITOR_ADPCM, this);
        !            41:        monitor->func = ToMonitorCallback(&ADPCMDevice::MonitorUpdate);
        !            42:        monitor->SetSize(40, 7);
1.1       root       43: }
                     44: 
                     45: // デストラクタ
                     46: ADPCMDevice::~ADPCMDevice()
                     47: {
1.1.1.2   root       48: }
                     49: 
                     50: // 初期化
                     51: bool
                     52: ADPCMDevice::Init()
                     53: {
                     54:        dmac = GetDMACDevice();
1.1.1.7 ! root       55: 
        !            56:        sound = GetSoundRenderer();
        !            57:        track = sound->RegistTrack(this);
        !            58:        if (track == NULL) {
        !            59:                // エラーメッセージは表示済み。
        !            60:                return false;
        !            61:        }
        !            62: 
        !            63:        mixer_frame_nsec = 1_sec / sound->GetMixerFreq();
1.1.1.2   root       64: 
1.1.1.6   root       65:        auto evman = GetEventManager();
                     66:        event = evman->Regist(this,
                     67:                ToEventCallback(&ADPCMDevice::EventCallback),
                     68:                "ADPCM");
1.1.1.4   root       69: 
1.1.1.2   root       70:        return true;
1.1       root       71: }
                     72: 
                     73: // リセット
                     74: void
                     75: ADPCMDevice::ResetHard(bool poweron)
                     76: {
1.1.1.7 ! root       77:        StopPlay();
        !            78: 
        !            79:        stepidx = 0;
        !            80:        xprev = 0;
1.1       root       81: 
                     82:        // ?
1.1.1.7 ! root       83:        clkMHz = 8;
        !            84:        // div は PPI の初期化でセットされるはず。
1.1       root       85:        div = 512;
1.1.1.7 ! root       86: 
        !            87:        // リセットからコマンド受け付けまで 16 msec (MSM6258.pdf)
1.1       root       88: }
                     89: 
1.1.1.3   root       90: busdata
1.1.1.4   root       91: ADPCMDevice::ReadPort(uint32 offset)
1.1       root       92: {
1.1.1.3   root       93:        busdata data;
1.1       root       94: 
                     95:        switch (offset) {
                     96:         case 0:        // STAT
1.1.1.7 ! root       97:                data = GetSTAT();
1.1.1.3   root       98:                putlog(1, "STAT -> $%02x", data.Data());
1.1       root       99:                break;
                    100:         case 1:        // DATA
1.1.1.2   root      101:                putlog(0, "Read $%06x (NOT IMPLEMENTED)", mpu->GetPaddr());
1.1.1.7 ! root      102:                data = databuf;
1.1       root      103:                break;
                    104:         default:
1.1.1.4   root      105:                VMPANIC("corrupted offset=%u", offset);
1.1       root      106:        }
                    107: 
1.1.1.3   root      108:        // InsideOut p.135
                    109:        const busdata wait = busdata::Wait(18 * 40_nsec);
                    110:        data |= wait;
                    111: 
1.1.1.4   root      112:        data |= BusData::Size1;
1.1       root      113:        return data;
                    114: }
                    115: 
1.1.1.3   root      116: busdata
1.1.1.4   root      117: ADPCMDevice::WritePort(uint32 offset, uint32 data)
1.1       root      118: {
                    119:        switch (offset) {
                    120:         case 0:        // CMD
                    121:                putlog(1, "CMD  <- $%02x", data);
1.1.1.7 ! root      122:                // XXX 同時に立ってるとどうなる?
1.1       root      123:                if ((data & REC_START)) {
                    124:                        putlog(0, "Start recording (NOT IMPLEMENTED)");
                    125:                }
                    126:                if ((data & PLAY_START)) {
                    127:                        StartPlay();
                    128:                }
                    129:                if ((data & STOP)) {
1.1.1.7 ! root      130:                        StopPlay();
1.1       root      131:                }
                    132:                break;
                    133:         case 1:        // DATA
1.1.1.7 ! root      134:                WriteData(data);
1.1       root      135:                break;
                    136:         default:
1.1.1.4   root      137:                VMPANIC("corrupted offset=%u", offset);
1.1       root      138:        }
                    139: 
1.1.1.3   root      140:        // InsideOut p.135
                    141:        const busdata wait = busdata::Wait(22 * 40_nsec);
1.1.1.4   root      142: 
                    143:        busdata r = wait;
                    144:        r |= BusData::Size1;
                    145:        return r;
1.1       root      146: }
                    147: 
1.1.1.3   root      148: busdata
1.1.1.4   root      149: ADPCMDevice::PeekPort(uint32 offset)
1.1       root      150: {
1.1.1.7 ! root      151:        switch (offset) {
        !           152:         case 0:        // STAT
        !           153:                return GetSTAT();
        !           154: 
        !           155:         case 1:        // DATA
        !           156:                return databuf;
        !           157: 
        !           158:         default:
        !           159:                VMPANIC("corrupted offset=%d", offset);
        !           160:        }
1.1       root      161: }
                    162: 
1.1.1.4   root      163: bool
                    164: ADPCMDevice::PokePort(uint32 offset, uint32 data)
                    165: {
                    166:        return false;
                    167: }
                    168: 
1.1       root      169: void
1.1.1.7 ! root      170: ADPCMDevice::MonitorUpdate(Monitor *, TextScreen& screen)
1.1       root      171: {
1.1.1.7 ! root      172:        int y;
        !           173: 
        !           174:        // Address: $e92000
        !           175:        //
        !           176:        // Mode: Recording
        !           177:        // Freq: 15625 Hz  (clk=8MHz div=1024)
        !           178:        // Pan : L R
        !           179: 
        !           180:        screen.Clear();
        !           181:        y = 0;
        !           182: 
        !           183:        screen.Puts(0, y++, "Address: $e92000");
        !           184:        y++;
        !           185:        screen.Puts(0, y, "Mode:");
        !           186:        if (playing) {
        !           187:                screen.Puts(6, y, "Playing");
        !           188:        } else {
        !           189:                screen.Puts(6, y, "Stopped");
        !           190:        }
        !           191:        y++;
        !           192: 
        !           193:        screen.Print(0, y, "Freq: %u Hz", freq);
        !           194:        screen.Print(15, y, "(clk=%uMHz div=%u)", clkMHz, div);
        !           195:        y++;
        !           196: 
        !           197:        screen.Print(0, y, "Pan :");
        !           198:        screen.Putc(6, y, 'L' | TA::OnOff(panout_L));
        !           199:        screen.Putc(8, y, 'R' | TA::OnOff(panout_R));
        !           200:        y++;
        !           201:        y++;
        !           202: 
        !           203:        screen.Print(0, y, "Internal: Xprev=%-5d StepIdx=%d", xprev, stepidx);
        !           204: }
        !           205: 
        !           206: // STAT レジスタの内容を返す。
        !           207: uint32
        !           208: ADPCMDevice::GetSTAT() const
        !           209: {
        !           210:        uint32 data;
        !           211: 
        !           212:        data = 0x40;
        !           213:        if (playing) {
        !           214:                data |= STAT_PLAY;
1.1       root      215:        }
1.1.1.7 ! root      216:        return data;
        !           217: }
        !           218: 
        !           219: // 再生開始。
        !           220: void
        !           221: ADPCMDevice::StartPlay()
        !           222: {
        !           223:        putlog(1, "Start playing %uHz", freq);
1.1       root      224: 
                    225:        playing = true;
1.1.1.7 ! root      226:        xprev = 0;
        !           227:        sound->StartPlay(track);
        !           228: 
1.1.1.2   root      229:        dmac->AssertREQ(3);
1.1       root      230: 
1.1.1.7 ! root      231:        event->time = guest_frame_nsec * 2;
1.1.1.2   root      232:        scheduler->StartEvent(event);
1.1       root      233: }
                    234: 
1.1.1.7 ! root      235: // 再生停止。
        !           236: void
        !           237: ADPCMDevice::StopPlay()
        !           238: {
        !           239:        if (playing) {
        !           240:                putlog(1, "Stop playing");
        !           241:        }
        !           242:        playing = false;
        !           243: 
        !           244:        // REQ はどうなる?
        !           245: 
        !           246:        scheduler->StopEvent(event);
        !           247: 
        !           248:        sound->StopPlay(track);
        !           249: }
        !           250: 
        !           251: // データ書き込み。
        !           252: void
        !           253: ADPCMDevice::WriteData(uint32 data)
        !           254: {
        !           255:        if (__predict_false(playing == false)) {
        !           256:                // 非再生中の書き込み?
        !           257:                putlog(1, "DATA <- $%02x (Not Playing)", data);
        !           258:        } else {
        !           259:                putlog(2, "DATA <- $%02x", data);
        !           260:        }
        !           261:        databuf = data;
        !           262: }
        !           263: 
1.1       root      264: // イベント
                    265: void
1.1.1.6   root      266: ADPCMDevice::EventCallback(Event *ev)
1.1       root      267: {
                    268:        if (playing) {
1.1.1.7 ! root      269:                DecodeADPCM(databuf);
        !           270: 
        !           271:                // 空いたので REQ を上げる。
1.1.1.2   root      272:                dmac->AssertREQ(3);
1.1       root      273:        }
1.1.1.7 ! root      274: 
        !           275:        event->time = guest_frame_nsec * 2;
        !           276:        scheduler->StartEvent(event);
        !           277: }
        !           278: 
        !           279: // DACK 信号をアサートする (DMAC から呼ばれる)。
        !           280: // 実際には PEDEC がやってるはず?。
        !           281: // ここではデータの受け渡しが完了したはずなのでその REQ を下げるだけ。
        !           282: void
        !           283: ADPCMDevice::AssertDACK()
        !           284: {
        !           285:        dmac->NegateREQ(3);
1.1       root      286: }
                    287: 
1.1.1.7 ! root      288: // パンアウト L を設定する。PPI から呼ばれる。
1.1       root      289: void
1.1.1.7 ! root      290: ADPCMDevice::SetPanOutL(bool on)
1.1       root      291: {
1.1.1.7 ! root      292:        panout_L = on;
1.1       root      293: }
                    294: 
1.1.1.7 ! root      295: // パンアウト R を設定する。PPI から呼ばれる。
1.1       root      296: void
1.1.1.7 ! root      297: ADPCMDevice::SetPanOutR(bool on)
1.1       root      298: {
1.1.1.7 ! root      299:        panout_R = on;
        !           300: }
        !           301: 
        !           302: // サンプリングレートを設定する。PPI から呼ばれる。
        !           303: void
        !           304: ADPCMDevice::SetRate(uint32 rate)
        !           305: {
        !           306:        assert(rate < 4);
        !           307: 
        !           308:        // %11 は %01 と同じになる (Inside p295)
        !           309:        if (rate == 3) {
        !           310:                rate = 1;
        !           311:        }
        !           312: 
        !           313:        div = 1024 - rate * 256;
        !           314:        CalcRate();
1.1       root      315: }
                    316: 
                    317: // クロック切り替え。OPM から呼ばれる。
                    318: // CT1 = %0 で 8MHz、%1 で 4MHz。
                    319: void
                    320: ADPCMDevice::SetClock(bool ct1)
                    321: {
                    322:        if (ct1) {
1.1.1.7 ! root      323:                clkMHz = 4;
1.1       root      324:        } else {
1.1.1.7 ! root      325:                clkMHz = 8;
        !           326:        }
        !           327:        CalcRate();
        !           328: }
        !           329: 
        !           330: // div と clk から必要なパラメータを計算する。
        !           331: void
        !           332: ADPCMDevice::CalcRate()
        !           333: {
        !           334:        // 1フレームにかかる時間。15625 Hz なら 64 usec。
        !           335:        guest_frame_nsec = (uint64)(div / clkMHz * 1000);
        !           336: 
        !           337:        // 周波数。主に表示用。
        !           338:        freq = clkMHz * 1000'000 / div;
        !           339: 
        !           340:        // アップスケール用の倍率。15625Hz なら 4倍。
        !           341:        scale_factor = guest_frame_nsec / mixer_frame_nsec;
        !           342: 
        !           343:        // イベント名に載せておく。
        !           344:        event->SetName(string_format("ADPCM %u Hz", freq));
        !           345: }
        !           346: 
        !           347: // ADPCM ストリームの1バイトをデコードする。(再生)
        !           348: void
        !           349: ADPCMDevice::DecodeADPCM(uint32 data)
        !           350: {
        !           351:        int16 s1 = adpcm2pcm_step(data & 0x0f);
        !           352:        int16 s2 = adpcm2pcm_step(data >> 4);
        !           353: 
        !           354:        uint64 vtime = scheduler->GetVirtTime();
        !           355: 
        !           356:        // 1サンプル目。
        !           357:        WritePCM(vtime, s1);
        !           358:        // 2サンプル目。
        !           359:        WritePCM(vtime + guest_frame_nsec, s2);
        !           360: }
        !           361: 
        !           362: // vtime に相当する位置に PCM データを書き込む。
        !           363: // その際、値は pcm0 から target までを線形補間し、
        !           364: // target を次の pcm0 とする。
        !           365: void
        !           366: ADPCMDevice::WritePCM(uint64 vtime, int16 target)
        !           367: {
        !           368:        uint64 vseq = vtime / Sound::BLK_NSEC;
        !           369:        uint64 voff = vtime % Sound::BLK_NSEC;
        !           370: 
        !           371:        // 40msec バッファでは 1バイト (2サンプル) がバッファをまたぐ事があるため、
        !           372:        // サンプルごとに行う必要がある。
        !           373:        int16 *blkbuf = track->AllocSeq(vseq);
        !           374:        assert(blkbuf);
        !           375: 
        !           376:        // このフレームがミキサーバッファのどの位置か。
        !           377:        uint32 mixer_frame_idx =
        !           378:                voff / guest_frame_nsec * Sound::NCHAN * scale_factor;
        !           379:        int16 *d = blkbuf + mixer_frame_idx;
        !           380: 
        !           381:        // ここから scale_factor 個で pcm0 から target になるよう線形補間する。
        !           382:        int16 delta = (target - pcm0) / scale_factor;
        !           383:        int16 val;
        !           384: 
        !           385:        val = pcm0;
        !           386:        for (uint i = 0; i < scale_factor; i++) {
        !           387:                val += delta;
        !           388:                *d++ = __predict_true(panout_L) ? val : 0;
        !           389:                *d++ = __predict_true(panout_R) ? val : 0;
        !           390:        }
        !           391:        pcm0 = target;
        !           392: }
        !           393: 
        !           394: // エンコード (PCM -> ADPCM) 側は録音方向なので使ってない。
        !           395: //
        !           396: // let Bit3 = Bit2 = Bit1 = Bit0 = 0
        !           397: // if (d(n) < 0)
        !           398: //   Bit3 = 1
        !           399: // d(n) = ABS(d(n))
        !           400: // if (d(n) >= ss(n))
        !           401: //   Bit2 = 1; d(n) = d(n) - ss(n)
        !           402: // if (d(n) >= ss(n) / 2)
        !           403: //   Bit1 = 1; d(n) = d(n) - ss(n) / 2
        !           404: // if (d(n) >= ss(n) / 4)
        !           405: //   Bit0 = 1
        !           406: // L(n) = (0b1000 * Bit3) + (0b100 * Bit2) + (0b10 + Bit1) + Bit0
        !           407: inline uint32
        !           408: ADPCMDevice::adpcm_encode(int32 dn)
        !           409: {
        !           410:        uint32 Ln = 0;
        !           411: 
        !           412:        int ss = adpcm_stepsize[stepidx];
        !           413: 
        !           414:        if (dn < 0) {
        !           415:                Ln = 0x8;
        !           416:                dn = -dn;
        !           417:        }
        !           418:        if (dn >= ss) {
        !           419:                Ln |= 0x4;
        !           420:                dn -= ss;
        !           421:        }
        !           422:        ss /= 2;
        !           423:        if (dn >= ss) {
        !           424:                Ln |= 0x2;
        !           425:                dn -= ss;
        !           426:        }
        !           427:        ss /= 2;
        !           428:        if (dn >= ss) {
        !           429:                Ln |= 0x1;
1.1       root      430:        }
1.1.1.7 ! root      431:        return Ln;
1.1       root      432: }
1.1.1.7 ! root      433: 
        !           434: // d(n) = (ss(n) * Bit2) + (ss(n) / 2 * Bit1) + (ss(n) / 4 * Bit0) + (ss(n) / 8)
        !           435: // if (Bit3 = 1)
        !           436: //   d(n) = d(n) * (-1)
        !           437: // X(n) = X(n-1) + d(n)
        !           438: inline int32
        !           439: ADPCMDevice::adpcm_decode(uint32 data, int32 xn)
        !           440: {
        !           441:        int b3 = (data & 8) ? 1 : 0;
        !           442:        int b2 = (data & 4) ? 1 : 0;
        !           443:        int b1 = (data & 2) ? 1 : 0;
        !           444:        int b0 = (data & 1);
        !           445: 
        !           446:        int32 ss = adpcm_stepsize[stepidx];
        !           447:        int32 dn = (ss * b2) + (ss / 2 * b1) + (ss / 4 * b0) + ss / 8;
        !           448:        if (b3) {
        !           449:                dn = -dn;
        !           450:        }
        !           451: 
        !           452:        xn += dn;
        !           453: 
        !           454:        // Saturate in 12 bits.
        !           455:        if (__predict_false(xn > 2047)) {
        !           456:                xn = 2047;
        !           457:        } else if (__predict_false(xn < -2047)) {
        !           458:                xn = -2047;
        !           459:        }
        !           460: 
        !           461:        return xn;
        !           462: }
        !           463: 
        !           464: inline void
        !           465: ADPCMDevice::adpcm_calcstep(uint32 data)
        !           466: {
        !           467:        stepidx += adpcm_stepadj[data];
        !           468:        if (__predict_false(stepidx < 0)) {
        !           469:                stepidx = 0;
        !           470:        } else if (__predict_false(stepidx > 48)) {
        !           471:                stepidx = 48;
        !           472:        }
        !           473: }
        !           474: 
        !           475: uint32
        !           476: ADPCMDevice::pcm2adpcm_step(int16 pcm)
        !           477: {
        !           478:        // Input of this algorithm is 12 bit.
        !           479:        int32 xn = pcm / 16;
        !           480:        int32 dn = xn - xprev;
        !           481:        uint32 Ln = adpcm_encode(dn);
        !           482: 
        !           483:        // next はこの Ln をデコードして PCM にした値
        !           484:        xprev = adpcm_decode(Ln, xprev);
        !           485:        adpcm_calcstep(Ln);
        !           486: 
        !           487:        return Ln;
        !           488: }
        !           489: 
        !           490: int16
        !           491: ADPCMDevice::adpcm2pcm_step(uint32 data)
        !           492: {
        !           493:        int32 xn = adpcm_decode(data, xprev);
        !           494: 
        !           495:        // next
        !           496:        xprev = xn;
        !           497:        adpcm_calcstep(data);
        !           498: 
        !           499:        // このアルゴリズムの出力は(下詰めで) 12 bit。
        !           500:        // ただし MSM6258 の出力 DAC は 10 bit 分しかない。
        !           501:        xn /= 4;
        !           502:        // その 10 bit を 16 bit PCM にする。
        !           503:        xn *= 64;
        !           504:        return xn;
        !           505: }
        !           506: 
        !           507: /*static*/ const int
        !           508: ADPCMDevice::adpcm_stepadj[16] = {
        !           509:        -1, -1, -1, -1, 2, 4, 6, 8,
        !           510:        -1, -1, -1, -1, 2, 4, 6, 8,
        !           511: };
        !           512: 
        !           513: /*static*/ const int32
        !           514: ADPCMDevice::adpcm_stepsize[49] = {
        !           515:         16,  17,  19,  21,  23,  25,  28,  31,  34,  37,
        !           516:         41,  45,  50,  55,  60,  66,  73,  80,  88,  97,
        !           517:        107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
        !           518:        279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
        !           519:        724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
        !           520: };

unix.superglobalmegacorp.com

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