■#6GEN日記NEO_PSP0.5(2006-01-23)
昨日はDGENさわらなかった。(気がする)
(注:このDGENは1.00です。最新のDGENはもっとずっと進んでいます。
スキルがあり、開発をしたい方は開発者に直接コンタクトをとって下さい。
私は開発者とは全く関係のない第三者です。
あくまでこのページの話は実験用途(教育用)です。)
そうそう、DGENは、ras.cpp弄ってたんだっけ。
早くなったり遅くなったり。
早くなるって確信を持って言える所は一つもないし、
// if( (((unsigned long)where) % 4) != 0 )
if( (((unsigned long)where) & 3) != 0 )
(こういうのは直すのが常識)
そもそも、ras.cppは、結論として結構軽い。
うーん、少しシンプルにしたけど、最適化で消えちゃうかも知れない。例えば、
// Blit the tile (this is UGLY! Do you know of a better way? :P)
if(which & 0x800)
{
tt=((tile & 0x0f000000)>>22); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0xf0000000)>>26); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x000f0000)>>14); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x00f00000)>>18); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x00000f00)>> 6); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x0000f000)>>10); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x0000000f)<< 2); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x000000f0)>> 2); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where ) = pal[tt ];
} else {
tt=((tile & 0x000000f0)>> 2); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x0000000f)<< 2); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x0000f000)>>10); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x00000f00)>> 6); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x00f00000)>>18); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x000f0000)>>14); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0xf0000000)>>26); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where++) = pal[tt ];
tt=((tile & 0x0f000000)>>22); *(where++) = pal[tt+1]; *(where++) = pal[tt+2]; *(where ) = pal[tt ];
}
ttをレジスタに割り付けてくれないと困るけど、多分大丈夫だよなあとか考えながら。
あと、「draw_lay_fast」をinlineからdefineにしたら、えらくコードが変わるぞ。(サイズも小さくなる)
inlineのオーバーヘッドが減ったんだと思うぞ。(早くなるかは別問題)
引数の型が全くいっしょだから、define版はこんな感じ。(ttは外)
#define draw_lay_fast(which,tile,palnum) \
{ \
if( tile ) \
{ \
if( which & 0x0800 ) /* x flipped */ \
{ \
which = ((which >> 9) & 0x0030) ; \
tt=(tile & (0x0f000000));if(tt){ *( palnum ) = ((tt) >> 24) + which ; } \
tt=(tile & (0xf0000000));if(tt){ *( palnum+1 ) = ((tt) >> 28) + which ; } \
tt=(tile & (0x000f0000));if(tt){ *( palnum+2 ) = ((tt) >> 16) + which ; } \
tt=(tile & (0x00f00000));if(tt){ *( palnum+3 ) = ((tt) >> 20) + which ; } \
tt=(tile & (0x00000f00));if(tt){ *( palnum+4 ) = ((tt) >> 8) + which ; } \
tt=(tile & (0x0000f000));if(tt){ *( palnum+5 ) = ((tt) >> 12) + which ; } \
tt=(tile & (0x0000000f));if(tt){ *( palnum+6 ) = ((tt) >> 0) + which ; } \
tt=(tile & (0x000000f0));if(tt){ *( palnum+7 ) = ((tt) >> 4) + which ; } \
} \
else \
{ \
which = ((which >> 9) & 0x0030) ; \
tt=(tile & (0x000000f0));if(tt){ *( palnum ) = ((tt) >> 4) + which ; } \
tt=(tile & (0x0000000f));if(tt){ *( palnum+1 ) = ((tt) >> 0) + which ; } \
tt=(tile & (0x0000f000));if(tt){ *( palnum+2 ) = ((tt) >> 12) + which ; } \
tt=(tile & (0x00000f00));if(tt){ *( palnum+3 ) = ((tt) >> 8) + which ; } \
tt=(tile & (0x00f00000));if(tt){ *( palnum+4 ) = ((tt) >> 20) + which ; } \
tt=(tile & (0x000f0000));if(tt){ *( palnum+5 ) = ((tt) >> 16) + which ; } \
tt=(tile & (0xf0000000));if(tt){ *( palnum+6 ) = ((tt) >> 28) + which ; } \
tt=(tile & (0x0f000000));if(tt){ *( palnum+7 ) = ((tt) >> 24) + which ; } \
} \
} \
} \
割り算回収作業もしたっけ。
えーと、割り算ってのは、
( a % 10 ) とか ( a / 10 ) とかいう奴。%は内部では割り算そのものです。
何で、こんな事をするかというと、最近のCPUはDOS/Vを含めて、中身はRISCでしょ?
RISCはキャッシュは基本だし。キャッシュに乗っていれば「投機実行」する訳。
投機実行ってのは、CPUは早いけど、バスがあんまりトロイから、
(バスが速ければそもそもキャッシュが要らない。でもそこまで速くなることは、ありえない。
光速度cは意外とトロイ)
計算できる所はとにかく全部計算しとけって奴。
if(「絶対成立する条件」){
「頭のいい処理;」
}else{
「タコな処理;」
}
っていうのが、あったとして、(「絶対成立する条件」が変数だったりして、コンパイラの最適化で
判断がつかず、「タコな処理;」が消えない場合。)
「頭のいい処理;」が実行されるのは、当たり前だけど、
「タコな処理;」もきちんと実行され、結果が破棄される。
「どうせ、CPU速いんだし、そんなん関係ないでしょ?」って思うかも知れないけど、
そうはSXNYがPSPを問屋が卸させない。
仮にタコな処理が超越関数バリバリだったら、
R4000に内蔵FPUいくつあるのか知らないけど、CPU内のFPU、BUSYになっちゃうでしょ。
「電話をかけても、しばらくお持ちください」って奴。
「頭のいい処理;」がほんの少しFPU使ってたとして、どうせ結果の破棄される。「タコな処理;」が
終わるまで、次に進めない訳。
「実行順序最適化スケジューラー」君も「パイプライン」もみーーんな無力。なんせ「タコな処理;」
だから長くて重い。ループ展開してあるから、パイプライン破棄されることも、他を先読みする事も
できない。ね、手詰まりでしょ?
FPUじゃなくて「整数演算ユニット」の場合も事情は同じ。仮にデーターシートに
「割り算は平均1クロックでーす。」なんて書いてあったとしても、素直に信用してはいけない。
今のCPUがどれくらいか判らないけど、それは通常時の話だし、トータルで考えて、
割り算ペナルティーがそんなに軽いとは考えがたい。
たまに、割り算が出てくるのなら、パイプラインが効いて1クロック相当になっても、連続で出てきては、
10クロック相当まで落ちるかもしれない。その場所に割り算が書いてなくても、キャッシュ&先読み
アーキテキチャーなんだから、最低平均性能が脅かされる事になる。自分の書いた部分が非の打ち所
がなかったとしても、一緒にキャッシュに乗った部分がタコならば、絶対に本来の性能がでない。
「そんなあ、俺はCのプログラマーであってGCCが勝手にインクルードするライブラリの中まで感知せんよ」
って、声が聞こえてくるような気がする。実際そのとおりで、その辺は(全部アセンブラで書くのならともかく)
仕方がないっちゃあ、仕方がない。そうなった場合は運が悪かったとあきらめて、気にしない事にする。
ええええ、この長い前振り、「気にしない」で終わりかい。
いえいえ、まだ続きます。
で、割り算の話。DGENのメニューとか実行速度が問われない部分に(a % 10)とかちりばめてあるけど、
これは直接命令に変換する筈なので、気になったから、mod10(a)とか自前の関数に閉じ込めた。
(どうせ実行速度は問われないんだから、関数のオーバーヘッドなんてどおでもいいでしょ?)
これは、先読み実行で割り算が読まれる確率を減らそうって試み。
最近のキャッシュはでかいから、ゲーム中でもメニューとかが、キャッシュに乗ってしまう可能性がある。
(OBJ分けてあってもクラス内部でも無力)沢山散らばってるより、一ヶ所に固まっている方が、確率
低いでしょ?こういうのは地味な改造で、fps等には出てこないが、さり気に重要だったりする。
(性能の低いマシンでエミュを安定実行させたい場合)
普段60fpsでてるのに、平均fpsでは80fpsでてるのに、それでもタイマー式実装のVSYNC、ONで
(本物のVsyncではない)たまにコマ落ちするから、遊べないんだよなあ。って状態の人は気にする所。
って、長い話だったな。疲れた。
管理人最近少し記憶が混乱気味。
そうそう、管理人は技術力がありません。
もしかしたら、「技術力があるのかも?」等と誤解される方が
いらっしゃるかも知れないので、念のために、書いておきました。
ちなみに技術力が身につく為の時間もありません。
ブログですから、自分の判る事だけを書いてるんですから、
そういう基本的な処で誤解が生じやすい訳です。
以下は単なる日記でPSP開発と関係ないです。
(ほんとに)
えーと、何の話だったっけ?
そうそう、SYN-Z様(PSP版DGEN作者)のHPに行って、
130のバイナリを落としてきました。
この前行った時は、2006-01-02になってる。
それはともかく、 更新履歴。
■Ver 1.30 ( 2006.01/20. )
・ターボモードを実装(キーコンフィグに追加)
・ステートの削除機能の追加(SELECT+□)
・ステートセーブ/ロード時の確認メッセージオプションを追加
・ステートセーブの日付表示
■Ver 1.20 ( 2005.12/01. )
・既存の68Kコアをバージョンアップ(MUSASHI 2.0A を 3.3 に変更)
・高速な68Kコアを実装
・既存の68Kコアと高速な68Kコアを設定で切り替え可能にしました
・Z80コア入れ替え
・キーコンフィグにコア切り替えとリセットを追加しました
・高速な68KコアとZ80コアは NeoCDPSP(Yoyofr) のものを流用しました
68KやZ80コア入れ替えたって書いてある。
で、yoyofrに行ってソース落としてきた。
TYLは5秒で逃げた。から、あまり、やる気がしなかったんだけど。
で、とりあえずMake。
うちには、「psp-config」(.exe?)なんてもんないから、
削除:うーんわからん。 -D_PSPyo_が悪そう、下記じゃあbuild.makが駄目。
ついでに$(PSPSDK)/lib/build.makもないから、
//PSPSDK=$(shell psp-config --pspsdk-path)
PSPSDK=./../../../usr/local/pspdev/psp
include $(PSPSDK)/lib/build.mak
ってな感じにして、(home/ユーザー名/の下で作業をしている)
「build.mak」はRINの「Makefile.psp」に少し追加して貼り付け。
# 2006-01-22
#C-Programs
AR = psp-ar
CC = psp-gcc-4.0.0
LD = psp-gcc-4.0.0
STRIP = psp-strip
RM= rm -f
RMDIR= rm -rf
MKDIR= @mkdir -p
#Compiler Flags
ARCHFLAGS=-mgp32 -mlong32 -msingle-float -mabi=eabi
CFLAGS= -Wall -O3 -fomit-frame-pointer $(ARCHFLAGS)
LDFLAGS=-nostartfiles -Wl,-Ttext=0x08900000
#add tools
RANLIB = psp-ranlib
MD = -mkdir.exe
うちはGCC400だからこんな感じ。まだ書くのかも知れないが、
そんなの知ったことか。(わたしは初心者)
O3って書いちゃうのは、さすがに気がひけたけどね。
で、(どおせ環境が違うし)手直ししたりしてたんだけど。
c68kexecのC68k_Exec()で、
#ifdef __x86__
(必要)
#endif
#ifdef __sh__
(必要)
#endif
#ifdef _PSPyo_
(必要)
#endif
となっている。(必要)の部分は、同じもので、分ける必要があるかもしれないと思ったけど、
結果的には分ける必要なかった。という感じ。新しい環境に適応できるように残っている。
(必要)の部分は、どれでも同じだが絶対にいる。なければこのファイルのコンパイルが
通らない。(インクルードファイルを加味しても)
X86はDOS/Vだし、SHはドリキャス用だろう、だから残りの_PSPyo_かな?
と思ったが、types.hを見ると、
その後X時間迷走。
管理人やっと思い出したよ。2001年って(MUSASI3.3)5年も前の話やん。
そうそう、その頃MAMEとRAINE弄ってて、F3とかS2とかあの辺。
68がMUSASI2.0Aから3.3に変わって、MAMEもすっかり変わって、
管理人、古いソースをせこせこ新しくトランスレートしてたっけ。
(元がMAMEでも、ほぼ移植不可能な、すっかり全然違うソースになっていたので)
えーと、まだやってないけど、インターフェースが
「m68k_cpu_context」コンテキストで弄る方式から、
レジスタいじる方式に、変わったんだよね。
MUSASI2.0Aは
typedef struct /* CPU Context */
{
unsigned int mode; /* CPU Operation Mode (68000, 68010, or 68020) */
unsigned int sr; /* Status Register */
unsigned int ppc; /* Previous program counter */
unsigned int pc; /* Program Counter */
unsigned int d[8]; /* Data Registers */
unsigned int a[8]; /* Address Registers */
unsigned int usp; /* User Stack Pointer */
unsigned int isp; /* Interrupt Stack Pointer */
unsigned int msp; /* Master Stack Pointer */
unsigned int vbr; /* Vector Base Register. Used in 68010+ */
unsigned int sfc; /* Source Function Code. Used in 68010+ */
unsigned int dfc; /* Destination Function Code. Used in 68010+ */
unsigned int stopped; /* Stopped state: only interrupt can restart */
unsigned int halted; /* Halted state: only reset can restart */
unsigned int int_state; /* Current interrupt line states -- ASG: changed from ints_pending */
unsigned int int_cycles; /* Extra cycles taken due to interrupts -- ASG: added */
int (*int_ack_callback)(int int_level); /* Interrupt Acknowledge */
void (*bkpt_ack_callback)(int data); /* Breakpoint Acknowledge */
void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */
void (*pc_changed_callback)(int new_pc); /* Called when the PC changes by a large amount */
void (*set_fc_callback)(int new_fc); /* Called when the CPU function code changes */
void (*instr_hook_callback)(void); /* Called every instruction cycle prior to execution */
} m68k_cpu_context;
って構造体があって、直接いじれば(本当はダメなんだろうけど)直接読み書きできた。(確か)
PCが欲しけりゃmy_pc = m68k_cpu_context.pc;みたいな感じ。
my_pc = &m68k_cpu_context->pc;だったかも知れないけど、本質的には同じことだな。
対するMUSASI3.3は、直接読み書きした記憶がない。
m68k_get_context()だかm68k_set_context()だかで、コンテキストもってきて、
m68k_set_reg()とかm68k_get_reg()とかで間接的に読み書きしたような。確かCPUの中直接いじるの
禁止になって、素直に従ってた気がする。例えばPC欲しけりゃあらかじめm68k_set_context()で、
my68kのコンテキスト設定しといて、my_pc=m68k_get_reg(my68k_context,M68K_REG_PC);みたいな
感じで使ったよな。
ってこれだけ書いておいて、間違ってたらどおしよう。まだなーーーーんもやってないんすけど。
明日あたり、やってみようかな。
昔のソースはその辺のPCにあるが持ってくるのがめんどい。
どれが最新かわからんし、早めにCDに焼いておかないと。HDDが沢山ありすぎてどれが何だったのか。
区別がつかん。5年前のHDDでも10から40Gはあるんよ。しかもそれが20個ぐらいは、...
HDD100%活用しないと気が済まない性格なんで、全てのHDDが起動領域四つずつある。
つまり環境が4x20で80個ぐらいあって、どれが重要な環境だったのか、わけ判らん。
Winは不安定過ぎるから、ソフトは一旦必ず実験環境でしばらく使ってたしな。
バックアップとか言って環境ごとコピーできたしな。HDDにHDDのバックアップ取ると結局どっちが
どっちだったか判らなくなって両方立ち上げて使うから結局ねずみ算式に増える。バックアップは
HDDに取ったら、あかんねん。で、増えすぎて強制的に日付が98-05-05 22:22の奴
(98SEのファイル)検索で全部消すとかやるから、立ち上がらなくなって、余計ごみが増える仕組み。
殆どぜーんぶ消してもいいんだけど、消したらまずいもの(オリジナルのソース)もあるし。
どれが何か区別がつけばCDに焼いて終わりなんだけど。
しかも昔過ぎて、何処にあるのかよく判らん。五年は古すぎる。すーーーっかり、忘れてる。
あーん、部屋整理やだなぁ。
HDD20個持ったことありますか?旦那。地獄ですぜ。みかん箱にぜえんぶ詰めたら、底が抜けますぜ。
大体持ち上がりませんぜ。これ落としたらどおしようっていう。重量上げ+恐怖が同時に味わえますぜ。
バックアップさえ取れば、単なるゴミなんだけど、そのバックアップがめんどくさいことこの上ない。
ゴミでも何でもいいやって、バックアップ取ると今度はCDが重くて死にますぜ。罠なんですわ。CDの方が
もっと重い。DVDに取れよですか?でもうちにないし、っていうか買ってくるのめんどくさい。メディアは
簡単に手に入るけど、あーでも買ってくるか。買うのはいいんだけど、うちにある機械殆どCDしか読めない。
だから2~3台買わないと現実的ではないな。で、DVDに焼いた所で本質的に問題が解決しないんですわ。
要するに一番重要な事はデーターの整理であって、ごみを消す事。DVDに焼くと余計訳が判らなくなりそうで
いまいち踏み切れないっていうか必要ない。まえ一度HDDに訳わからんもんぜーんぶコピーってやってたら、
80GのHDDで半分超えたあたりで飽きてきて(あきれて)止めました。このほぼ99%ごみのデーター。
DVD1枚4.3Gだから10枚か、DLなら5枚か。やっぱ買ってこようかな。本質的な解決につながらなくても
災害対策には、なるよな。でもこのデーター災害でぜーんぶ消えても、実はわたしは本質的に何一つ
困らない。単に昔の軌跡が消えるだけ。