2025年4月30日水曜日

C言語のforとマクロで簡単な時間計測

前回、処理時間計測の必要に駆られた。いちいち時間計測のコードを入れて回るのが面倒なので、forとマクロで簡単に計測した。

enum{
        SW_SH2,
        SW_HBIN,
        SW_HBOUT,
        SW_VBIN,
        SW_VBOUT,
        SW_SCSP,
        SW_SCU,
        SW_68K,
        SW_SMPC,
        SW_CDB,
        SW_SYNC,
        SW_MAX
};
static const char* swname[]={
        "SW_SH2",
        "SW_HBIN",
        "SW_HBOUT",
        "SW_VBIN",
        "SW_VBOUT",
        "SW_SCSP",
        "SW_SCU",
        "SW_68K",
        "SW_SMPC",
        "SW_CDB",
        "SW_SYNC",
        "SW_MAX"
};
static u64 swbuff_[SW_MAX];
static u64 start_time_ = 0;
static int sw_flag = 0;
#define SW(n) for(tick_start();sw_flag;tick_end(n))
static void tick_start()
{
        sw_flag=1;
        start_time_ = YabauseGetTicks();
}
static void tick_end(int n)
{
        u64 diff = YabauseGetTicks() - start_time_;
        swbuff_[n] += diff;
        sw_flag=0;
}
static void tick_clear()
{
        for(int i=0;i<SW_MAX;i++){
                swbuff_[i] = 0;
        }
}
static void tick_show()
{
        for(int i=0;i<SW_MAX;i++){
                printf("%d:%s: %llu\n", i, swname[i], swbuff_[i]);
        }
}
for文は最初と最後で文を実行できるので、これを利用して、tick_start()とtick_end()を呼び、sw_buff_に時間を足していく。tick_startでループ条件ON、tick_endでループ条件OFFとすれば、ループにならない。

こうしておけば以下のようにSW(番号){}で囲むと時間を計って加算してくれる。

SW(SW_SH2){
  計測したい処理(SH2)
}
SW(SW_SCU){
  計測したい処理(SCU)
}

新しいCならforの中で変数宣言できるので、工夫すれば入れ子で計測できたりしそうだが、そこまではやってない。

YabauseGetTicks()は現在時刻を取得するようなyabauseの関数で以下のようになっている。

u64 YabauseGetTicks(void) {
#ifdef WIN32
   u64 ticks;
   QueryPerformanceCounter((LARGE_INTEGER *)&ticks);
   return ticks;
#elif defined(_arch_dreamcast)
   return (u64) timer_ms_gettime64();
#elif defined(GEKKO)
   return gettime();
#elif defined(PSP)
   return sceKernelGetSystemTimeWide();
#elif defined(ANDROID)
        struct timespec clock_time;
        clock_gettime(CLOCK_REALTIME , &clock_time);
        return (u64)clock_time.tv_sec * 1000000 + clock_time.tv_nsec/1000;
#elif defined(HAVE_GETTIMEOFDAY)
   struct timeval tv;
   gettimeofday(&tv, NULL);
   return (u64)tv.tv_sec * 1000000 + tv.tv_usec;
#elif defined(HAVE_LIBSDL)
   return (u64)SDL_GetTicks();
#endif
}

という事でやってみた結果、遅い処理はどうやらSH2とSCUのエミュレーション部分のようだった。

SH2部分はダイナミックリコンパイル(JITコンパイル?)されていて、もうこれ以上は早くならなそう。

SCUも同じような感じでコンパイルしなおせば早くなるのかもしれないが、かなり特殊な命令形態っぽいので、簡単にアセンブラに変換はできないような気がする。これ以上はもう無理か。。あと2倍くらい早いか倍のコア数がある端末ならば、何とかなりそうな気がするが。。

2025年4月19日土曜日

続5:中華linuxゲーム端末Anbernic 353PSでセガサターン

今度こそyabasanshiro-saでGRANDIAのムービーが遅い原因を調査する

とりあえず忘れないうちに覚書をメモ

ここまでの調査でFBCRへの書き込みで画面をマニュアル更新していることは分かっている。要はこの画面更新のタイミングがなぜ遅いのかが問題となる。単にタイマーのエミュレーションにバグがあるとかだったら直せそうなので調べてみる。(そうであってくれ)

 FBCR(25D00002)への書き込みを行っているのがどこなのか探してみる。いろいろやったがよくわからず、SSFのデバッグ機能で、25D00002を検索した。(ような気がする。うろ覚え)

そしてprintf結果と突き合わせて、どうやら060129baでFBCRに書いているようだとわかった(と思う)

そしてその近辺でpcをprintfした。普通こういうタイミングが重要な処理は割り込みのタイミングでやるはず。急にアドレスが飛んで戻っているので、おそらく割り込みだろうと決めつけて調べる。

pc 06039f94
pc 06037b10
pc 06013a1c <= なんとなくここで割り込みっぽい気がする
pc 06012952
pc 06012988
pc 0601298e
pc 06012996
pc 060129a8
pc 060129ba
mw 25D00002 @060129ba
pc 06013a34
pc 06015144
pc 06015160
pc 060151c8
pc 060151f4
pc 06015210
pc 06015222
pc 06039ae4

再びSSFで06013a1cを検索、06000a04が見つかる。なんか割り込みベクタっぽいアドレスだ。ただ、SSFの画面を見る限りはVBRは06000000と060000400で、微妙に遠い。ただ、限りなく割り込みっぽいので、Interruptで検索してprintfを入れまくる。そしてここで06013a1cを登録しているっぽいことが分かる。

static void FASTCALL BiosSetScuInterrupt(SH2_struct * sh)
{
   SH2GetRegisters(sh, &sh->regs);

   LOG("BiosSetScuInterrupt. vector = %02X, func = %08X\n", sh->regs.R[4], sh->regs.R[5]);
これで、どうやらベクタ番号41番と分かり、先日見つけたサターンの仕様を見ると

SCU Interrupt
bit 1    V-blank-OUT    VDP2    vector 41    Level E

となっている。1画面書き終わったら発生するV-blank-OUTの割り込みのようだ。という事は単に1画面60フレームごとに割り込みでマニュアル更新しているということになる。

単に画面の描画が60fpsに間に合ってなくて遅くなっているという事だろう。エミュレーションバグとかではなかった。簡単に治せそうにない。当てが外れたようだ。

フレームスキップが有効になっているが、無効にしても同じ速度で動いている。自動フレームスキップが何の役にも立ってなさそうだ。 何か違うところが遅いのかもしれない。

実機でしか動かせないからどこが遅いのかしらべるの大変そう。。

2025年4月13日日曜日

MOONLIGHT & SUNSHINE

 ROCKNIXでMoonlightとやらの設定があった。どうやらNvidiaのGameStreamとやらでPC上のゲームをlinuxゲーム端末で遊べるというものらしい。

ただ、いくら探してもnvidiaのアプリ上にgamestreamらしきものはなかった。どうやら非対応になった模様。

それに代わるものとしてMoonlightの開発チームがsunshineというものを作っているようだ。PC上にsunshineをインストールし、PCのIPをlinuxゲーム端末に設定すると、遠隔でPC上のゲームを遊べる。

PCは電源を入れてsunshineを動かしておかなければならない。wakeonlanとか設定して自動ログインとかにしておけばいつでも遊べる感じになりそうだが、めんどくさいのでそこまではしない。

steamのbig pictureが多分最初から設定されていて(インストールされてあったら自動的に登録されてたりする?) 、まあまあそれなりに遊べそう。

マウス入力はできなそうなので、ゲームパッドで遊べるやつ専用かな。とりあえず428は遊べそうだが、画面サイズが640x480でなんか縦長に圧縮されたような画面になってちょっと気になる。

終わらせ方はSELECT+RUN+L1+R2同時押しで終わる。

2025年4月10日木曜日

自民党がまた金配るみたいだな

現役世代から金をとって老人に配りたいんだろう。それで票を買いたいわけだ。そもそも配るくらいなら最初から取るなと言いたいところだが、一度作った仕組みは変えたくない、一度取り始めた税金は意地でも減税したくないという事だろう。

プログラム的に言うと継ぎ足し継ぎ足しで無駄コードがわんさか入っているスパゲッティコードだけど、動かなくなるから削るなみたいな感じだろうか?

放送法とか。当初は電波塔とかの整備で予算が必要だったのかもしれないが、もう要らんはずだし。 ガソリン税も二重課税になって、一般財源にされて何に使ってるかよくわからなくなってる。流用流用で何に使ってるか分からないまま税金だけどんどん増えている。

プログラムだったら、コードを見直して、何がどうなっているか把握できる状態を保っておかないと管理できなくなるんだが。都度見直しして根本原因を直す。コードは常にきれいに無駄なく保つってことができない奴は優秀とはいえない。

口先だけの文系に任せてるからこうなってるんじゃないのか?もっと理論立てて仕組みを考えられるやつにやらせたほうがマシになるんじゃないのか?

と思ったりしていたが、アメリカではイーロンマスクが容赦なくリファクタリングしまくっているようだ。将来的には無駄を排除したすっきりしたシステムになるのかもしれないが、どうなるんだろう。結構な痛みは伴っているようだが。

しかしアメリカはすごいな。日本だと一人二人変わったところで、何も変わらずスパゲッティを肥大化させていくだけだろう。

2025年4月5日土曜日

続4:中華linuxゲーム端末Anbernic 353PSでセガサターン (更新)

yabasanshiro-saでGRANDIAのムービーの音がおかしい原因の調査の覚書のメモ(更新)

GRANDIAを動かすとムービーが遅い気がする。そして雑音だらけで何を言っているか分からない。遅いせいで雑音だらけなのか?音だけでも何とかしたい。と思って調べた。

ソースコードvdp2.cpp:882くらい

swap_frame_bufferがmanual changeされていて、VDP1 offset 0x02にval=3が書かれたとき更新される。

 FBCR
 Frame buffer switching mode

どうやら遅いわけではなく、もともとそういうスピードにしてそう??

音が雑音だらけで遅くなっているのかと勘違いしていた。どうやら雑音だけの問題の模様。

セガサターンの音を再生する機能(SCSP)にはSLOTというものがあり、オープニングムービーでは、SLOT04とSLOT08を使っている模様(SSFのデバッグモードを使うとわかる)

08だけ生かすとほぼ無音(あっているのか?)
04だけ生かすとノイズまみれ。ただ、ノイズにまみれた中で、ところどころ正常な音が鳴る。部分的に問題なさそう。

とりあえずSLOT04が悪いようだ。

データを見てみると、サウンドバッファにあるデータをM68kに投げて、データを戻してから再生するようだ。おそらく、M68kとやらを使って圧縮された音を展開しているんじゃないだろうか。

原因を調べようとprintfを入れると音がマシになる。

またタイミング問題かよ。。。

タイミングの問題を調べようとする

なんかyabause.cのあたりで、CPUシミュレーションとSCSPシミュレーションの同期が取れてないような気がしてきた。

とりあえずyabause.cの中を見てみる

deciline = 1/10ライン
SCSP 44100 * 512 clock?
M68k 44100 * 256 clock?

decilineの9と10でHBlankINとHBlankOUTになり、ScspExecされる

PAL 50フレーム x 313ライン
NTSC? 60フレーム x 263ライン

1/10ライン当たり 143.0874... clockということ
この端数が良くないとか?

scsp_cycles_per_deciline =

VLBlankで
    setM68kCounter((u64)(44100*256/60)<<SCSP_FRACTIONAL_BITS)
    SmpcINTBACKEnd()
    Vdp2VBlankIN()
    SyncCPUtoSCSP() 

と見ていった中で、SyncCPUtoSCSPが実質何もやっていないように見える。

CPUから書いたデータをSCSPで受けて、M68kに渡して展開、戻ってきたデータを(ここで壊れている感じ?)SCSPで鳴らす。 CPUとSCSPでタイミングを合わせないとうまくデータがやり取りできないのでは?

とりあえずの解決策

よく見ると、SCSPのコードには2通り実行コードがあった。

void ScspExec(){
  if (thread_running == 0){
    thread_running = 1;
    if (g_scsp_main_mode == 0) {
      YabThreadStart(YAB_THREAD_SCSP, (void * (*)(void *))ScspAsynMainCpuTime, NULL);
    }
    else {
      YabThreadStart(YAB_THREAD_SCSP, (void * (*)(void *))ScspAsynMainRealtime, NULL);
    }
    YabThreadUSleep(100000);
  }
}

何も変えない場合、ScspAsynMainRealtimeで動く。試しにg_scsp_main_modeを0にして、ScspAsynMainCpuTimeに変えてみると、雑音がなくなった。ただ、やっぱりスロー再生みたいになっているようだ。なんか遅い気がした感覚は気のせいではなかったようだ。

見たところRealtimeの方は時計の時間に同期して動くように見える。それに対してCpuTimeはおそらくシミュレーション時間に同期して動くものだと思われる。ここで雑音になった原因を考えてみると、端末のCPUが貧弱なためにCPUエミュレートが遅くなり、ムービーがスロー再生気味になるのに対し、SCSPだけ現実時間で動いて同期が取れなくなり、音声データがおかしくなり雑音だらけになったという事だろう。当初推測した原因(遅いせいで雑音になる)は一応当たっていたという事になる。

 なんにせよグランディアの音が正常に鳴るようになった。やったー。と言いたいところだが、今度はスロー再生になるのが気になってしまう。CPUの処理能力が足りないんだろうが、うーん。。

FBCRで毎回書いてるみたいだから、なぜそれが遅くなっているかだなあ。