PVS-Studioおよび3DOエミュレータ

写真2

私はもう3DOコンソールエミュレーションに手を触れないことを約束しました。 しかし、その後、静的コードアナライザー、つまりPVS-Studioなどのエキゾチックなものを扱う機会を得ました。 もちろん、アナライザーを試すことにした最初のことは、3DOコンソールエミュレーター(Phoenix Project)でした。 90年代前半には、CDドライブを備えた最初の32ビットコンソールという接頭辞がありました。兄とモスクワから父親を連れてきたことを思い出します。 さて、ケースが判明したので、3DOエミュレーションのすべての主要プロジェクトについて確認します。 さあ行こう...



FreeDOCore-GoogleCodeのリポジトリの元のコア。



Picture 3



プロジェクトのウェブサイトhttp : //www.freedo.org/



リビジョン :8。



ヘルプ :最初に、このコンソールの唯一のエミュレーターと言えますが、残りはすべてこのコードに基づいています。



書き込みエラー。



V523:「then」ステートメントは「else」ステートメントと同等です。



673行目-clio.cpp



Picture 12



通常のコンパイラは、警告に対してもこのような魅力を考慮しません。

if((cregs[0x404])&0x200) { ptr=0; while(len>=0) { b3=_xbus_GetDataFIFO(); ... } cregs[0x400]|=0x80; } else { ptr=0; while(len>=0) { b3=_xbus_GetDataFIFO(); ... } cregs[0x400]|=0x80; }
      
      





この場合、冗長コードだけでなくエミュレーションエラーもあるため、XBUSプロトコルは双方向であり、この場合は常に読み取り用に機能します。これはもちろんCDドライブのエミュレートにとって重要ではありませんが、エミュレートされたゲームにとっては重大で潜在的に危険なエラーです-ブランクを焼くことがすべて頭に浮かんだら? しかし、真剣に、エミュレートされたXBUSインターフェイスに書き込む代わりに、DMAレジスタで指定されたメモリが破損します。もちろん、このようなアプローチでは、FZ-EM256 3DOメモリユニットのような希少性をエミュレートすることはできません。



読み取りエラー。



V614:潜在的に初期化されていない変数 'val'が使用されました。



803行目-clio.cpp



Picture 13



最初はそれはナンセンスだと思っていましたが、突然FIFOのゴーストを思い出しました...

 unsigned short __fastcall _clio_EIFIFO(unsigned short channel) { unsigned int val,base,mask; ... if(FIFOI[channel].StartAdr!=0)//channel enabled { ... } return val; }
      
      





ここで 、条件が満たされた場合にのみ変数valが初期化されるため、予測できない値を読み取ることができる場合があります。 理論的には、空にした後のFIFO DSPプロセッサは、そこから読み取った最後の値、一種のゴーストを返すはずです。 実際には空のFIFOからの読み取りは発生しませんが、修正後すぐに別のゲームが開始されるのを知っていますか?



合計で、2つの注目すべき間違いがありますが、正直なところ、もっとあると思いました。



FourDO-SourceForgeリポジトリから変更されたコア



Picture 17



プロジェクトのウェブサイトhttp : //www.fourdo.com/



リビジョン :387。



参照 :このプロジェクトは2つの人生を生きました。最初は-著者が自分でエミュレーターを最初から書き始めたときでしたが、悲しいかな、患者はcom睡状態に陥り、FreeDOソースコードを開いた後、すでに新しい器官で第二の人生が始まりました。 それでは、インプラントがどのように根付くのか見てみましょう...



修正されたが、それでもバグ。



Picture 4



すぐに元のカーネルの最後のエラー(V614:潜在的に初期化されていない変数 'val'が使用されています。803行目-clio.cpp)に注意します。ゴーストは不必要に処理されました(または不必要に同じですか?)会話:

 unsigned short __fastcall _clio_EIFIFO(unsigned short channel) { unsigned int val,base,mask; base=0x400+(channel*16); mask=1<<channel; if(FIFOI[channel].StartAdr!=0) { ... } else { val=0; // JMK SEZ: What is this? // It was commented out along with this whole "else" block, // but I had to bring this else block back from the dead // in order to initialize val appropriately. } return val; }
      
      





しかし、彼らは無駄に対処しました! 真の問題は未解決のままでしたが、すべてが外部から美しく、誰もその問題を思い出せませんでした。 最もエレガントな解決策は、変数val静的として宣言し、それをゼロで初期化することです。関数の外に置き、変数のリストに追加してすばやく保存し、混乱を避けるためにelseブロックを削除する方が正しいでしょう。



未修正エラー。



V523:「then」ステートメントは「else」ステートメントと同等です。



673行目-clio.cpp



Picture 19



クリエイターの足はここに足を踏み入れませんでした-すべてはオリジナルのものと同じです。 トピックに載っていない人にとって、「Creator」はFourDOの著者の1人です。Victor(彼の名前がかどうかわからない、彼もStirlitzです)、彼は3DOPlayの著者、FreeDOの別のフォーク、エラーは同じですオリジナルのように。 3DOPlayには面白い話がありました。Victorはトロールすることに決め、彼が3DOエミュレータの作成者であり、FreeDO開発者が彼からコードを盗んだと言われています。 FreeDOの共著者である私は、残念ながら彼の3DOPlayプロジェクトに対する敵対行為に積極的に参加することはできませんでした。名前は素晴らしいですが、だれかがぼんやりしていました-3つの窪みが始まり、その結果、VictorはFourDOチームに参加しました。そして、私たちは敬意を払わなければなりません。彼は、ソースの作者に加えて、3DOエミュレーションの開発に関して何かをした唯一の人です。



まだ間違いではありません。



V550:奇妙な正確な比較:Rez2T!= 0. fabs(A-B)> Epsilonのように、定義された精度の比較を使用する方がおそらく良いでしょう。



778行目-madam.cpp



Picture 20



以下のコードは完全に機能しますが、深刻な懸念事項です。

 static double Rez0T,Rez1T,Rez2T,Rez3T; ... Rez2T=(signed int)((M20*V0+M21*V1+M22*V2)/65536.0); if(Rez2T!=0) M=Nfrac16/(double)Rez2T; else M=Nfrac16;
      
      





元のプロジェクトでは、 Rez2Tintでしたが、おそらく作者は型変換の警告をなくすことを決定し、 破棄しましたが、誰かが突然符号付きint型への強制変換を削除することを決定した場合、 Nfrac16Rez2Tで除算するときにコプロセッサー例外をキャッチするリスクがあります。



そして、もう1つのコードにより、今回はFourDOチームの開発者の健康について深刻な懸念が生じます。

 void __fastcall _qrz_PushARMCycles(unsigned int clks) { uint32 arm,cnt; int timers=21000000; //default int sp=0; if(sdf>0) sdf--; if(sf>0) sf--; if(unknownflag11>0)unknownflag11--; if(ARM_CLOCK<0x5F5E10)ARM_CLOCK=0x5F5E10; if(ARM_CLOCK>0x2FAF080)ARM_CLOCK=0x2FAF080; if(speedfixes>0&&speedfixes<0x186A1) {/*sp=0x2DC6C0;*/ speedfixes--;} else if(speedfixes>0x186A1&&speedfixes<0x30D41) {/*if(sdf==0)sp=0x4C4B40; */speedfixes--;} else if(speedfixes<0) {sp=0x3D0900; speedfixes++;} else if(speedfixes>0x30D41) {/*sp=0x249F00;*/ speedfixes--;} else if(speedfixes==0x30D41||speedfixes==0x186A1) speedfixes=0; if((fixmode&FIX_BIT_TIMING_2)&&sf<=2500000) {sp=0; timers=21000000; if(sf==0)sp=-(0x1C9C380-ARM_CLOCK);} if((fixmode&FIX_BIT_TIMING_1)/*&&jw>0*/&&sf<=1500000) {/*jw--;*/timers=1000000;sp=-1000000;} if((fixmode&FIX_BIT_TIMING_4)/*&&jw>0*/) {/*jw--;*/timers=1000000;sp=-1000000;} if((fixmode&FIX_BIT_TIMING_3)&&(sf>0&&sf<=100000)/*&&jw>0*/) {/*jw--;*/timers=900000;} if((fixmode&FIX_BIT_TIMING_5)&&sf==0/*&&jw>0*/) {/*jw--;*/timers=1000000;} if((fixmode&FIX_BIT_TIMING_6)/*&&jw>0*/) {/*jw--;*/timers=1000000; if(sf<=80000)sp=-23000000;} if(fixmode&FIX_BIT_TIMING_7){sp=-3000000; timers=21000000;} if((sf>0x186A0&&!(fixmode&FIX_BIT_TIMING_2))|| ((fixmode&FIX_BIT_TIMING_2)&&sf>2500000)) sp=-(12200000-ARM_CLOCK); if((ARM_CLOCK-sp)<0x2DC6C0)sp=-(0x2DC6C0-ARM_CLOCK); if((ARM_CLOCK-sp)!=THE_ARM_CLOCK) { THE_ARM_CLOCK=(ARM_CLOCK-sp); io_interface(EXT_ARM_SYNC,(void*)THE_ARM_CLOCK); //fix for working with 4do } arm=(clks<<24)/(ARM_CLOCK-sp); qrz_AccARM+=arm*(ARM_CLOCK-sp); if( (qrz_AccARM>>24) != clks ) { arm++; qrz_AccARM+=ARM_CLOCK; qrz_AccARM&=0xffffff; } qrz_AccDSP+=arm*SND_CLOCK; qrz_AccVDL+=arm*(VDL_CLOCK); if(_clio_GetTimerDelay())qrz_TCount+=arm*((timers)/ (_clio_GetTimerDelay())); }
      
      





アナライザーの観点から与えられたコードは正しいですが、常識の観点から、エミュレートされたプロセッサーのクロックサイクルを考慮するときにこれを行う(「これ」しか推測できない)ことは、形のない不名誉です、以下は元のコードです:

 void __fastcall _qrz_PushARMCycles(unsigned int clks) { uint32 arm; arm=(clks<<24)/ARM_CLOCK; qrz_AccARM+=arm*ARM_CLOCK; if( (qrz_AccARM>>24) != clks ) { arm++; qrz_AccARM+=ARM_CLOCK; qrz_AccARM&=0xffffff; } qrz_AccDSP+=arm*SND_CLOCK; qrz_AccVDL+=arm*(VDL_CLOCK); if(_clio_GetTimerDelay()) qrz_TCount+=arm*((__temporalfixes?12500000:25000000)/ (_clio_GetTimerDelay())); }
      
      





一般的に、患者は生存も死亡もしていない、エミュレーターのコアにほとんど変化はなく、すべてが良いというわけではなく、リポジトリによって判断して、1年以上もそれらの変化はなかったと言えます。



Phoenix Emu-Project-新しいカーネル-新しいバグ。



Picture 5



プロジェクトサイトhttp : //www.arts-union.ru



バージョン :1.7



ヘルプ :3DOエミュレーションを完璧に仕上げることをman望の目標として書き直された3DOエミュレーターですが、適切なコードインフラストラクチャを備えたマルチシステムエミュレーターと考えていましたが、これまでのところ3DOのみです。



エラー-テクスチャの剥離!



V501: '!='演算子の左右に同一の部分式があります:val.flags!= Val.flags。



207行目-gfx_celengine.h



Picture 7

 struct gfxCelTexturePDECAttrib { uint32 pre0; uint32 flags; int plutcount; uint16 plut[32]; bool operator==(const gfxCelTexturePDECAttrib &val) const { if(val.pre0!=pre0)return false; if(val.flags!=val.flags)return false; if(val.plutcount!=plutcount)return false; for(int i=0;i<val.plutcount;i++) { if(val.plut[i]!=plut[i])return false; } return true; } };
      
      





キャッシュ内のテクスチャのCELフラグが異なっていても気づかない場合、そうでない場合は同じであるため、ゲーム中に不注意でミスが発生し、テクスチャの欠陥につながります。間違ったシェーダがテクスチャの表示に使用されます。 以下は正しいオプションです。

 if(val.flags!=flags)return false;
      
      





エラー-画面上のゴミ!



V579:memset関数は、ポインターとそのサイズを引数として受け取ります。 間違いかもしれません。 3番目の引数を調べます。



36行目-vdlp_3do.cpp

Picture 22

ここではすべてが簡単です-PAL形式のゲームのサポートを追加して変更すると、VDLPが不注意で再び犠牲になりました。 以前は、エミュレーターはNTSCゲームのみをサポートしていました。その大部分とフレームバッファーは320 x 240ピクセルの固定サイズであったため、クラスはメモリ割り当てなしで配列として宣言されました。

 screen=new uint8[384*288*3*4]; memset(screen,0,sizeof(screen));
      
      





そして、エラーが見えなくなるように(文字通り、ゲーム開始時の最初のかろうじて知覚可能なフレームがゴミでいっぱいになっているため)、たとえば、次のように書くことができます:

 memset(screen,0,sizeof(uint8)*384*288*3*4);
      
      





エラー-しかし、ディスクはありません!



V595:nullptrに対して検証される前に、「適応」ポインターが使用されました。 行を確認してください:375、376。



375行目-dumplibrary.cpp

Picture 21

また、不注意...オブジェクトにアクセスする前に、その正確さを確認する必要があるため、最後の2行を交換する必要があります。 そうしないと、必要な画像がない場合、保存されたゲームの読み込み中に例外が発生します。

 dumpAdapter *adapt=createDumpAdapter(j, inf->Parent()->Attribute("attach").toString()); adapt->SetSign(signs[names[i]]); if(!adapt)break;
      
      





何が言えますか? エミュレータをプログラミングするのではなく、夕方には注意するかリラックスする必要があります。



おわりに



したがって、私の最初の経験から、静的コード分析は間違いなく有用であり、開発者の多くの時間と神経を節約できることが示されました。 しかし、emuシーンでは、ARMのHex-Rayデコンパイラーの場合のように、質問の価格は確かに高く、3DOエミュレーションを大幅に前進させることができます。




All Articles