| ¥n |
NYOットやろうぜ
~独田地獄斎が贈るPlayStation研究序説~ NNNNNN
NNNNNNNNNNNN
NNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNNN
NNNNNNNNN NNNNNNNNN
NNNNNNNNN NNNNNNNNN
NNNNNNNNN NNNNNNNNN
NNNNNNNNN NNNNNNNNNN
NNNNNNNNN NNNNNNNNNN
NNNNNNNNN NNNNNNNNNN
NNNNNNNNN NNNNNNNNNN
NNNNNNNNN NNNNNNNNN
NNNNNNNNN NNNNNNNN
OONNNNNNNNN NNNNNNN
YYYOOOONNNNNNNNN
YYYYYYYYYYYNNNNNNNNN OOOOOOOOOOOO
YYYYYYYYYYYYYYYNNNNNNNNNYYOOOOOOOOOOOOOOOOOOO
OOOOOOYYYYYYYYY NNNNNNNNNYYYYYYYOOO OOOOOOOOO
OOOOOOOOOOO YYNNNNNNNNNYYYYY OOOOOOOOOOO
OOOOOOOOO OOOYYYYNNNNNNNNN YYYYYYYYYOOOOOO
OOOOOOOOOOOOOOOOOONNNNNNNNN YYYYYYYYYYYYYYY
OOOOOOOOOOOO NNNNNNNNNOYYYYYYYYYYYY
NNNNNNNNNOOOOOOYYY
NNNNOOOO
はじめに
本ページは未来のPlayStationエミュレータ開発者への情報提供を目的とする。 ただし、ここでコーディングの講義をするつもりはない。 なぜならコーディングの観点からエミュレータ特有と呼べるものは何もないからだ。 動的コンパイルは珍しいかもしれないが、これはエミュレータの本質ではない。 換言すると、この程度のコーディングに躓くようではプログラマとして終っている。 基礎からやり直した方がいいんじゃないか。
解析環境
- PlayStation本体
- SCPH-7000 (SONY)
- 制御マシン
- いわゆる自作DOS/V
- 通信デバイス
- PS X-TERMINATOR (Future Console Design)
- 通信デバイスファームウェア
- caetla 0.34 (K-Communications)
- 通信プログラム
- caetools (K-Communications)
directio (K-Communications) - 解析プログラム作成
- GNU Binary Utilities 2.8.1 mipsel-generic-elf/ecoff (こぺる)
GNU C/C++ Compiler 2.7.2.3 mipsel-generic-elf/ecoff (こぺる)
Borland C++Compiler 5.5 (Borland)
参考文献
- 「ネットやろうぜ」ユーザガイドホームページ
- http://www.scei.co.jp/Net/guide/user/
- PADUA PLAYSTATION RESOURCE
- http://psx.rules.org/psxrul2.shtml
- ただし、これらには多くの誤記があるので鵜呑みにしないこと。 正規開発マニュアルから抜粋したと思われる部分にすら誤記がある (正規マニュアルにも誤記はある)。 開発者ならば最終的には自分でウラをとること。 幸い、PlayStationにはその現実的な手段があるのだから。
- 東芝のTX39のドキュメント
- http://www.semicon.toshiba.co.jp/product/micro/index.html
アーキテクチャ
ブロック図

メモリマップ
| アドレス | 内容 |
|---|---|
| 0x00000000~0x001FFFFF | メインメモリ(命令キャッシュ有効) |
| 0x00200000~0x003FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x00400000~0x005FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x00600000~0x007FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x00800000~ | |
| 0x1F000000~ | PIO |
| 0x1F0?????~ | |
| 0x1F800000~0x1F8003FF | スクラッチパッド |
| 0x1F000400~ | |
| 0x1F801000~ | I/Oポート(いわゆるメモリマップドI/O) |
| 0x1F80????~ | |
| 0x1FC00000~0x1FC7FFFF | OS ROM |
| 0x1F800000~ | |
| 0x80000000~0x801FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x80200000~0x803FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x80400000~0x805FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x80600000~0x807FFFFF | メインメモリ ミラー(命令キャッシュ有効) |
| 0x80800000~ | |
| 0x9FC00000~0x9FC7FFFF | OS ROM ミラー |
| 0x9FC80000~ | |
| 0xA0000000~0xA01FFFFF | メインメモリ ミラー(命令キャッシュ無効) |
| 0xA0200000~0xA03FFFFF | メインメモリ ミラー(命令キャッシュ無効) |
| 0xA0400000~0xA05FFFFF | メインメモリ ミラー(命令キャッシュ無効) |
| 0xA0600000~0xA07FFFFF | メインメモリ ミラー(命令キャッシュ無効) |
| 0xA0800000~ | |
| 0xBFC00000~0xBFC7FFFF | OS ROM ミラー |
| 0xBFC80000~ | |
| 0xFFFE0130~0xFFFE0133 | SwapCache? |
| 0xFFFE0134~ |
レジスタマップ
| アドレス | レジスタ名(仮) | 内容 |
|---|---|---|
| 0x1F801000 | ? | 拡張メモリ領域指定? |
| 0x1F801004 | ? | I/Oポート終端指定? |
| 0x1F801008 | ? | ? |
| 0x1F80100C | ? | ? |
| 0x1F801010 | ? | ? |
| 0x1F801014 | ? | SPU DMAのウェイト? |
| 0x1F801018 | ? | OTC DMAのウェイト? |
| 0x1F80101C | ? | ? |
| 0x1F801020 | ? | CD DMAのウェイト? |
| 0x1F801040 | COMA_DATA | COMAデータ |
| 0x1F801044 | COMA_STAT | COMAステータス |
| 0x1F801048 | COMA_MODE | COMAモード |
| 0x1F80104A | COMA_CTRL | COMAコントロール |
| 0x1F80104E | COMA_BAUD | COMAボー |
| 0x1F801050 | SIO_DATA | SIOデータ |
| 0x1F801054 | SIO_STAT | SIOステータス |
| 0x1F801058 | SIO_MODE | SIOモード |
| 0x1F80105A | SIO_CTRL | SIOコントロール |
| 0x1F80105E | SIO_BAUD | SIOボー |
| 0x1F801060 | RAM_SIZE | 実効メモリサイズ |
| 0x1F801070 | I_STAT | 割り込みステータス |
| 0x1F801074 | I_MASK | 割り込みマスク |
| 0x1F801080 | D0_MADR | DMACチャネル0(MDECin)メモリアドレス |
| 0x1F801084 | D0_BCR | DMACチャネル0(MDECin)ブロックカウント |
| 0x1F801088 | D0_CHCR | DMACチャネル0(MDECin)チャネルコントロール |
| 0x1F801090 | D1_MADR | DMACチャネル1(MDECout)メモリアドレス |
| 0x1F801094 | D1_BCR | DMACチャネル1(MDECout)ブロックカウント |
| 0x1F801098 | D1_CHCR | DMACチャネル1(MDECout)チャネルコントロール |
| 0x1F8010A0 | D2_MADR | DMACチャネル2(GPU)メモリアドレス |
| 0x1F8010A4 | D2_BCR | DMACチャネル2(GPU)ブロックカウント |
| 0x1F8010A8 | D2_CHCR | DMACチャネル2(GPU)チャネルコントロール |
| 0x1F8010B0 | D3_MADR | DMACチャネル3(CD)メモリアドレス |
| 0x1F8010B4 | D3_BCR | DMACチャネル3(CD)ブロックカウント |
| 0x1F8010B8 | D3_CHCR | DMACチャネル3(CD)チャネルコントロール |
| 0x1F8010C0 | D4_MADR | DMACチャネル4(SPU)メモリアドレス |
| 0x1F8010C4 | D4_BCR | DMACチャネル4(SPU)ブロックカウント |
| 0x1F8010C8 | D4_CHCR | DMACチャネル4(SPU)チャネルコントロール |
| 0x1F8010D0 | D5_MADR | DMACチャネル5(PIO)メモリアドレス |
| 0x1F8010D4 | D5_BCR | DMACチャネル5(PIO)ブロックカウント |
| 0x1F8010D8 | D5_CHCR | DMACチャネル5(PIO)チャネルコントロール |
| 0x1F8010E0 | D6_MADR | DMACチャネル6(OTC)メモリアドレス |
| 0x1F8010E4 | D6_BCR | DMACチャネル6(OTC)ブロックカウント |
| 0x1F8010E8 | D6_CHCR | DMACチャネル6(OTC)チャネルコントロール |
| 0x1F8010F0 | D_PCR | DMAC優先度コントロール |
| 0x1F8010F4 | D_ICR | DMAC割り込みコントロール |
| 0x1F801100 | T0_COUNT | タイマ0(システム/ピクセル)カウント |
| 0x1F801104 | T0_MODE | タイマ0(システム/ピクセル)モード |
| 0x1F801108 | T0_COMP | タイマ0(システム/ピクセル)コンペア |
| 0x1F801110 | T1_COUNT | タイマ1(システム/水平同期)カウント |
| 0x1F801114 | T1_MODE | タイマ1(システム/水平同期)モード |
| 0x1F801118 | T1_COMP | タイマ1(システム/水平同期)コンペア |
| 0x1F801120 | T2_COUNT | タイマ2(システム/8分周)カウント |
| 0x1F801124 | T2_MODE | タイマ2(システム/8分周)モード |
| 0x1F801128 | T2_COMP | タイマ2(システム/8分周)コンペア |
| 0x1F801800 | CD_REG0 | CDレジスタ0 |
| 0x1F801801 | CD_REG1 | CDレジスタ1 |
| 0x1F801802 | CD_REG2 | CDレジスタ2 |
| 0x1F801803 | CD_REG3 | CDレジスタ3 |
| 0x1F801810 | GPU_DATA | GPUデータ/コマンド |
| 0x1F801814 | GPU_CTRL | GPUステータス/コントロール |
| 0x1F801820 | MDEC_DATA | MDECデータ/コマンド |
| 0x1F801824 | MDEC_CTRL | MDECステータス/コントロール |
| 0x1F801C00 | SPU_REG00 | SPUレジスタ0 |
| 0x1F801C02 | SPU_REG01 | SPUレジスタ1 |
| 中略 | 中略 | 中略 |
| 0x1F801DFC | SPU_REGFE | SPUレジスタ254 |
| 0x1F801DFE | SPU_REGFF | SPUレジスタ255 |
| 0x1F802041 | ? | ディップスイッチ? |
CPU(中央処理装置)
PlayStationのCPUはMIPSのR3000をベースとしたカスタムCPUである。 動作周波数は33868800Hzと言われ、エンディアンはリトルである。 若干の違いはあるが、それは青本も同じなので、 基本的な理解は東芝のTX39のドキュメントで十分である。
コード表
COP1,COP3,TLB系は存在しない。 サービスコール内のatofでCOP1命令が使われているがatof自体が呼ばれた試しがない。 おそらく試作機ではデバグ処理のために存在するが、結局オミットされたのだろう。
rs+b20をコードと一対一に対応させるのは、ただの慣習である。
| 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
| op | rs | rt | rd | sa | funct | ||||||||||||||||||||||||||
| op | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | SPECIAL | BCOND | J | JAL | BEQ | BNE | BLEZ | BGTZ |
| 0x08 | ADDI | ADDIU | SLTI | SLTIU | ANDI | ORI | XORI | LUI |
| 0x10 | COP0 | COP2 | ||||||
| 0x18 | ||||||||
| 0x20 | LB | LH | LWL | LW | LBU | LHU | LWR | |
| 0x28 | SB | SH | SWL | SW | SWR | |||
| 0x30 | LWC2 | |||||||
| 0x38 | SWC2 |
| funct | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | SLL | SRL | SRA | SLLV | SRLV | SRAV | ||
| 0x08 | JR | JALR | SYSCALL | BREAK | ||||
| 0x10 | MFHI | MTHI | MFLO | MTLO | ||||
| 0x18 | MULT | MULTU | DIV | DIVU | ||||
| 0x20 | ADD | ADDU | SUB | SUBU | AND | OR | XOR | NOR |
| 0x28 | SLT | SLTU | ||||||
| 0x30 | ||||||||
| 0x38 |
| rs | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | BLTZ | BGEZ | ||||||
| 0x08 | ||||||||
| 0x10 | BLTZAL | BGEZAL | ||||||
| 0x18 |
| rs | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | MFC0 | MTC0 | ||||||
| 0x08 | ||||||||
| 0x10 | CP0 | |||||||
| 0x18 | ||||||||
| funct | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | ||||||||
| 0x08 | ||||||||
| 0x10 | RFE | |||||||
| 0x18 | ||||||||
| 0x20 | ||||||||
| 0x28 | ||||||||
| 0x30 | ||||||||
| 0x38 |
| rs | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | MFC2 | CFC2 | MTC2 | CTC2 | ||||
| 0x08 | ||||||||
| 0x10 | CP2 | |||||||
| 0x18 | ||||||||
| funct | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 0x00 | RTPS | NCLIP | ||||||
| 0x08 | OP | |||||||
| 0x10 | DPCS | INTPL | MVMVA | NCDS | CDP | NCDT | ||
| 0x18 | NCCS | CC | NCS | |||||
| 0x20 | NCT | |||||||
| 0x28 | SQR | DCPL | DPCT | AVSZ3 | AVSZ4 | |||
| 0x30 | RTPT | |||||||
| 0x38 | GPF | GPL | NCCT |
I(命令)キャッシュ
ネットやろうぜの記述には重要部であるほど誤りがあるので、注意が必要である。 まずメモリ空間とキャッシュ(バースト転送)の有効/無効の関係を示した表において メモリ空間を取り違えている。また
Iキャッシュが有効な論理メモリ空間上の命令コードは通常の約5倍の速度でCPUに読み込まれます。 また、一旦読み込まれた命令コードはCPU内のIキャッシュメモリに保存されますので、 再実行時にはメインメモリへのアクセスなしに実行されます。という文章もおかしい。 後半の文章があるから、前半はキャッシュミスした状態での実行速度と考えるしかない。 ならば理論的に24/9倍速以上にはならない。
スクラッチパッド Dキャッシュ
アドレス0x1F800000にマッピングされた、ストールの発生しないRAMを PlayStationの「方言」としてDキャッシュと呼んでいるが、 これは一般的な意味のDキャッシュではない。 一般的な意味のDキャッシュはPlayStationには存在しない。
GTE(Geometry Transformation Engine グラフィックデータ生成プロセッサ)
逆数テーブル
RTPS,RTPT命令において、投影変換係数
が求められるが、実際は除算ではなく、逆数テーブルを用いた積算
が行われる。iSzは0x10000~0x1FFFFの範囲であり、小数部16ビットの浮動小数に相当する。 iSzは必ずしも
にはなっていないので、ブルートフォースアタックによる探索が必要になる。
Sz:32768~65535(N=16)について結果を示す。 Sz:0~32767については、Nを減らすだけなので割愛する。
| (int) (h * 65536 / Sz + 0.5) |
| (h * iSz + (1 << (N - 1))) >> N |
| (65536 << N) / Sz |
Sz:32768~65535(N=16)について結果を示す。 Sz:0~32767については、Nを減らすだけなので割愛する。
エミュレート法
動的コンパイル法の一例
コード表から分かるようにR3000は単純な命令だけで構成されており、既にマイクロコードであるとも言える。 従って、一コードに対して一個のコンパイル済みデータを予め用意し、実行時に変数部分を書き換えるだけでも 実用に耐える動的コンパイルが可能である。例えばSRAV,SRLVでは、
の様なデータを予め用意し、実行時に
の様に変数部分を書き換える。後はこれを連結コピーして関数を作成し、出来上がった関数を実行する。
/*
*
*/
uchar __SRAV[] = {
0x8B, 0x0D, '¥¥', '¥¥', '¥¥', '¥¥', /* 00 mov ecx,[GPR[rs]] 02 &GPR[rs] */
0xA1, '¥¥', '¥¥', '¥¥', '¥¥', /* 06 mov eax,[GPR[rt]] 07 &GPR[rt] */
0xD3, 0xF8, /* 0B sar eax,cl */
0xA3, '¥¥', '¥¥', '¥¥', '¥¥' /* 0D mov [GPR[rd]],eax 0E &GPR[rd] */
};
/*
*
*/
uchar __SRLV[] = {
0x8B, 0x0D, '¥¥', '¥¥', '¥¥', '¥¥', /* 00 mov ecx,[GPR[rs]] 02 &GPR[rs] */
0xA1, '¥¥', '¥¥', '¥¥', '¥¥', /* 06 mov eax,[GPR[rt]] 07 &GPR[rt] */
0xD3, 0xE8, /* 0B shr eax,cl */
0xA3, '¥¥', '¥¥', '¥¥', '¥¥' /* 0D mov [GPR[rd]],eax 0E &GPR[rd] */
};
|
/*
*
*/
#define _rs_ ((opcode >> 21) & 31)
#define _rt_ ((opcode >> 16) & 31)
#define _rd_ ((opcode >> 11) & 31)
/*
*
*/
#define CPU_SET_SRAV() ¥
{ ¥
*(ulong *) (&__SRAV[0x02]) = (ulong) & GPR[_rs_]; ¥
*(ulong *) (&__SRAV[0x07]) = (ulong) & GPR[_rt_]; ¥
*(ulong *) (&__SRAV[0x0E]) = (ulong) & GPR[_rd_]; ¥
CPU_OPCODE = __SRAV; ¥
CPU_OPSIZE = sizeof(__SRAV); ¥
}
/*
*
*/
#define CPU_SET_SRLV() ¥
{ ¥
*(ulong *) (&__SRLV[0x02]) = (ulong) & GPR[_rs_]; ¥
*(ulong *) (&__SRLV[0x07]) = (ulong) & GPR[_rt_]; ¥
*(ulong *) (&__SRLV[0x0E]) = (ulong) & GPR[_rd_]; ¥
CPU_OPCODE = __SRLV; ¥
CPU_OPSIZE = sizeof(__SRLV); ¥
}
|
注意事項
巷で誤解があるようなので、注意を喚起すると、 実行毎に新たな関数を作成していたのでは、インタプリタよりも遅くなる。 出来上がった関数を管理・再利用することこそが、動的コンパイルの肝である。 動的コンパイルとは可変長データを対象としたキャッシュ機構を構築することに等しい。
コンパイル済データの作成法の一例
アセンブラで直接記述する以外に、以下の様にCで記述し、 ファイル出力、逆アセンブルするのも一つの手である。
/*
*
*/
#define _rs_ 1 /* 変数部分なので適当 */
#define _rt_ 2 /* 変数部分なので適当 */
#define _rd_ 3 /* 変数部分なので適当 */
/*
*
*/
long GPR[32];
/*
*
*/
void __SRAV(void)
{
GPR[_rd_] = GPR[_rt_] >> GPR[_rs_];
}
/*
*
*/
void ____SRAV(void)
{
}
/*
*
*/
void __SRLV(void)
{
GPR[_rd_] = ((ulong) GPR[_rt_]) >> GPR[_rs_];
}
/*
*
*/
void ____SRLV(void)
{
}
/*
*
*/
void SaveToFile(char *lpFileName, void *lpStart, void *lpEnd)
{
FILE *fp;
if (NULL == (fp = fopen(lpFileName, "wb"))) {
return;
}
fwrite(lpStart, (ulong) lpEnd - (ulong) lpStart, 1, fp);
fclose(fp);
}
/*
*
*/
int main(void)
{
SaveToFile("SRAV.X86", __SRAV, ____SRAV);
SaveToFile("SRLV.X86", __SRLV, ____SRLV);
return(0);
}
|
INTC(割り込みコントローラ)
I_STATレジスタ
ロードした場合
ストアした場合
| 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
| S S P U | S S I O | S C O M A | S T M R 2 | S T M R 1 | S T M R 0 | S D M A C | S C D | S G P U | S V S Y N C | ||||||
| フィールド | 内容 |
|---|---|
| SVSYNC | VSYNCの割り込みステータス 0:VSYNCから割り込み要求がない 1:VSYNCから割り込み要求があった |
| SGPU~SSPU | SVSYNCと同様 |
| 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
| C S P U | C S I O | C C O M A | C T M R 2 | C T M R 1 | C T M R 0 | C D M A C | C C D | C G P U | C V S Y N C | ||||||
| フィールド | 内容 |
|---|---|
| CVSYNC | VSYNCの割り込みクリア 0→SVSYNCを0にする |
| CGPU~CSPU | CVSYNCと同様 |
I_MASKレジスタ
ロードした場合
ストアした値が返る
ストアした場合| 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
| M S P U | M S I O | M C O M A | M T M R 2 | M T M R 1 | M T M R 0 | M D M A C | M C D | M G P U | M V S Y N C | ||||||
| フィールド | 内容 |
|---|---|
| MVSYNC | VSYNCの割り込みマスク MVSYNCとSVSYNCがともに1である限り、INTCはCPUに割り込みを要求し続ける。 |
| MGPU~MSPU | MVSYNCと同様 |
DMAC(DMAコントローラ)
準備中
タイマ
タイマは設定によりシステムクロックではなくピクセル表示や水平同期によりカウントアップさせることができる。
| タイプ | 画面幅 | [A] | [B]システムクロック/水平同期 | システムクロック/ピクセル表示 | システムクロック/垂直同期 | ||
|---|---|---|---|---|---|---|---|
| 非インターレース | インターレース | ||||||
| 偶数フィールド | 奇数フィールド | ||||||
| NTSC | 256 | 566107.5005 | [A]/263 | [A]/89683=[B]/341 | [B]*263 | [B]*263 | [B]*262 |
| 320 | [A]/112038=[B]/426 | ||||||
| 368 | [A]/128081=[B]/487 | ||||||
| 512 | [A]/179366=[B]/682 | ||||||
| 640 | [A]/224339=[B]/853 | ||||||
| PAL | 256 | 674399.5367 | [A]/314 | [A]/106760=[B]/340 | [B]*314 | [B]*313 | [B]*312 |
| 320 | [A]/133764=[B]/426 | ||||||
| 368 | [A]/152604=[B]/486 | ||||||
| 512 | [A]/213834=[B]/681 | ||||||
| 640 | [A]/267214=[B]/851 | ||||||
メインメモリ(Main Memory or RAM 主記憶装置)
準備中
OS ROM(or BIOS 基本操作システム記憶装置)
準備中
MDEC(Motion Decoder データ伸長エンジン)
準備中
PIO(拡張パラレルポート)
準備中
SIO(or COMB 拡張シリアルポート)
準備中
GPU(グラフィック描画処理プロセッサ)
GPU_DATAレジスタ
ロードした場合
基本的には内部のリザルトレジスタの値が返るが、 StoreImageコマンド実行中の場合は、その結果が返る。
リザルトレジスタには、 GPU制御系コマンドの結果や StoreImageコマンドの最初の結果が格納される。 途中の結果は格納されない。
ストアした場合リザルトレジスタには、 GPU制御系コマンドの結果や StoreImageコマンドの最初の結果が格納される。 途中の結果は格納されない。
データは内部の多目的FIFOに格納され、 必要な数のデータが貯まると、 GPU描画系コマンドが実行される。
GPU_CTRLレジスタ
ロードした場合
ストアした場合
| 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
| o d e | t m o d e | i s e m p t y | d a t a r d y | i s i d l e | d m a r d y | r e s e r v e d | i s s t o p | i s i n t e r | i s r g b 2 4 | i s p a l | v r e s | h r e s 1 | h r e s 2 | r e s e r v e d | u n k n o w n | r e s e r v e d | p b c | p b w | d f e | d t d | t p | a b r | t y | t x | |||||||
| フィールド | 内容 |
|---|---|
| ode | ビデオ出力の走査フィールドがフレームバッファ上の奇数ラインかどうか。 よってインターレースでは数百水平同期毎、非インターレースでは1水平同期毎(指定ブランク区間は0のまま)に変化する。 |
| tmode | 転送モード |
| isempty | FIFO(パケットバッファ)が空かどうか |
| datardy | データレディ |
| isidle | アイドル状態である |
| dmardy | DMAレディ |
| isstop | ビデオ出力を停止している |
| isinter | インターレースで出力している(垂直解像度を倍密度にする場合は必須) |
| isrgb24 | rgb24ビットモードである |
| ispal | PALモードである |
| vres | 垂直解像度 |
| hres1 | 水平解像度1 |
| hres2 | 水平解像度2(水平解像度1は無効になる) |
| unknown | 不明(オン/オフ操作は可能) |
| pbc | 優先度ビットコンペアを行う |
| pbw | 優先度ビットを書き込む |
| dfe | インターレースであっても、 非インターレースと同様、 走査フィールドに関係なく描画する |
| dtd | ディザ処理を行う |
| tp | テクスチャのビットモード |
| abr | 半透明率 |
| ty | テクスチャページのY座標 |
| tx | テクスチャページのX座標 |
GPU制御系コマンドが実行される。
SPU(サウンド処理プロセッサ)
ブロック図

ボイス
全24チャネルの内蔵音源のこと。各チャネルは独立に 「波形データ(ADPCMまたは共通ノイズ)×エンベロープ×ボイス音量(左右独立)」 の三者の掛け合わせを出力する。
エンベロープ
波形データにかけるバイアスのこと。
[*]ディケイレート、リリースレートは1/4で指定されるので4倍して考える
[*]アタックレート0~47では5サンプリング時間で、 それ以上は徐々に短くなる。 これはADPCMデコードのフィルタ処理に起因する、 残響が消えるための時間である。 レートが大きくなると、アタックの方で0になる時間が増えるから、 その分短くて良いのである。
| レート[*] | 実効レート | ||
|---|---|---|---|
| 分母 | 分子(増加) | 分子(減少) | |
| 0~47 | 1 | (7 - (RATE & 3)) << (11 - (RATE >> 2)) | (-8 + (RATE & 3)) << (11 - (RATE >> 2)) |
| 48~ | 1 << ((RATE >> 2) - 11) | 7 - (RATE & 3) | -8 + (RATE & 3) |
| モード | 内容 |
|---|---|
| 線形増加 | 分母サンプリング時間毎にエンベロープレベルに分子(増加)を加算 |
| 線形減少 | 分母サンプリング時間毎にエンベロープレベルに分子(減少)を加算 |
| 指数増加 | 基本的には線形増加と同じだがエンベロープレベルが0x6000以上になるとレートが8増加 |
| 指数減少 | 分母サンプリング時間毎にエンベロープレベルに ((分子(減少) * レベル) >> 15)を加算 |
| ステータス | 開始条件 | 終了条件 | 飽和下限 | 飽和上限 |
|---|---|---|---|---|
| 初期化 | キーオンする | アタックレートに依存した初期化時間[*]が経過する | 0 | 0 |
| アタック | 初期化が終了する | エンベロープレベルが32767以上になる | 32767 | |
| ディケイ | アタックが終了する | エンベロープレベルがサステインレベル未満になる | 0 | |
| サステイン | ディケイが終了する | キーオフする | 0 | 32767 |
| リリース | キーオフする | エンベロープレベルが0以下になる | -1 | |
| 停止 | ・リリースが終了する ・ADPCMデコーダが強制停止する | キーオンする | 0 | 0 |

ADPCM波形
ADPCMデコーダが圧縮データをデコードして得たサンプリング波形のこと。 重要なのはADPCMデコーダが(リバーブプロセスも)常時稼動していることだろう。 停止したように見えて、それはエンベロープを強制停止させた結果にすぎない。 つまり、常に割り込みが発生する可能性がある。 しかしながら、常時稼動のエミュレーションは、高負荷のワリに見返りが小さい。 モラトリアム期間を設けるのが現実解であろう。
組み合わせとしては以下の5通りとなる(ループアドレスが強制的に設定された場合は、この限りではない)。
| フィールド | 内容 |
|---|---|
| 0x04 | カレントブロックの先頭をループアドレスに設定する。 |
| 0x02 | エンベロープを強制停止しない。 |
| 0x01 | カレントブロックを演奏後、ループアドレスにジャンプする。 0x02が設定されていないと、エンベロープを強制停止する。 |
| ループフラグ | 内容 |
|---|---|
| 0x06,0x04 | カレントブロックの先頭をループアドレスに設定する。 |
| 0x03 | カレントブロック演奏後、ループアドレスにジャンプする。 |
| 0x07 | 上2つの組み合わせにより、カレントブロックを延々と演奏する。 |
| 0x01 | ループアドレスにジャンプするが、エンベロープを停止するので無音になる。 |
| 0x05 | カレントブロックを延々と演奏するが、エンベロープを停止するので無音になる。 |
共通ノイズ波形
ADPCM波形の替わりに用いるランダム波形のこと。 チャネル毎にどちらかを選択できるが、 チャネル毎に異なるノイズ波形を得るといったことは出来ない。
アルゴリズム

アルゴリズム
/*
* レベル変化時の計算式
*/
char Addition[64] = {
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1
};
NoiseLevel = (short) (NoiseLevel + NoiseLevel + Addition[(NoiseLevel >> 10) & 63]);
/*
* レベル変化の周期
*/
Freq = 0x8000 >> (NoiseClock >> 2);
/*
* 周期が半減する頻度
*/
Half = ((NoiseClock & 3) * 2) / (4 + (NoiseClock & 3));
|

リバーブ
アルゴリズム
/*
* PlayStation Reverberation Algorithm (C)Dr.Hell, 2005
* 厳密には、左右の処理のタイミングは1サンプリング時間ずれており、
* 各々は2サンプリング時間毎に実行される
*/
mAPF1 = *((ushort *) 0x1F801DC0) * 4;
mAPF2 = *((ushort *) 0x1F801DC2) * 4;
gIIR = *((short *) 0x1F801DC4) / 32768.0;
gCOMB1 = *((short *) 0x1F801DC6) / 32768.0;
gCOMB2 = *((short *) 0x1F801DC8) / 32768.0;
gCOMB3 = *((short *) 0x1F801DCA) / 32768.0;
gCOMB4 = *((short *) 0x1F801DCC) / 32768.0;
gWALL = *((short *) 0x1F801DCE) / 32768.0;
gAPF1 = *((short *) 0x1F801DD0) / 32768.0;
gAPF2 = *((short *) 0x1F801DD2) / 32768.0;
z0_Lsame = *((ushort *) 0x1F801DD4) * 4;
z0_Rsame = *((ushort *) 0x1F801DD6) * 4;
m1_Lcomb = *((ushort *) 0x1F801DD8) * 4;
m1_Rcomb = *((ushort *) 0x1F801DDA) * 4;
m2_Lcomb = *((ushort *) 0x1F801DDC) * 4;
m2_Rcomb = *((ushort *) 0x1F801DDE) * 4;
zm_Lsame = *((ushort *) 0x1F801DE0) * 4;
zm_Rsame = *((ushort *) 0x1F801DE2) * 4;
z0_Ldiff = *((ushort *) 0x1F801DE4) * 4;
z0_Rdiff = *((ushort *) 0x1F801DE6) * 4;
m3_Lcomb = *((ushort *) 0x1F801DE8) * 4;
m3_Rcomb = *((ushort *) 0x1F801DEA) * 4;
m4_Lcomb = *((ushort *) 0x1F801DEC) * 4;
m4_Rcomb = *((ushort *) 0x1F801DEE) * 4;
zm_Ldiff = *((ushort *) 0x1F801DF0) * 4;
zm_Rdiff = *((ushort *) 0x1F801DF2) * 4;
z0_Lapf1 = *((ushort *) 0x1F801DF4) * 4;
z0_Rapf1 = *((ushort *) 0x1F801DF6) * 4;
z0_Lapf2 = *((ushort *) 0x1F801DF8) * 4;
z0_Rapf2 = *((ushort *) 0x1F801DFA) * 4;
gLIN = *((short *) 0x1F801DFC) / 32768.0;
gRIN = *((short *) 0x1F801DFE) / 32768.0;
z1_Lsame = z0_Lsame - 1;
z1_Rsame = z0_Rsame - 1;
z1_Ldiff = z0_Ldiff - 1;
z1_Rdiff = z0_Rdiff - 1;
zm_Lapf1 = z0_Lapf1 - mAPF1;
zm_Rapf1 = z0_Rapf1 - mAPF1;
zm_Lapf2 = z0_Lapf2 - mAPF2;
zm_Rapf2 = z0_Rapf2 - mAPF2;
for (;;) {
/*
* LoadFromLowPassFilterは35もしくは39タップのFIRフィルタ
* 最外周の係数が0でも結果は同じなので、35か39かは特定できず
*/
L_in = gLIN * LoadFromLowPassFilterL();
R_in = gRIN * LoadFromLowPassFilterR();
/*
* Left -> Wall -> Left Reflection
*/
L_temp = ReadReverbWork(zm_Lsame);
R_temp = ReadReverbWork(zm_Rsame);
L_same = L_in + gWALL * L_temp;
R_same = R_in + gWALL * R_temp;
L_temp = ReadReverbWork(z1_Lsame);
R_temp = ReadReverbWork(z1_Rsame);
L_same = L_temp + gIIR * (L_same - L_temp);
R_same = R_temp + gIIR * (R_same - R_temp);
/*
* Left -> Wall -> Right Reflection
*/
L_temp = ReadReverbWork(zm_Rdiff);
R_temp = ReadReverbWork(zm_Ldiff);
L_diff = L_in + gWALL * L_temp;
R_diff = R_in + gWALL * R_temp;
L_temp = ReadReverbWork(z1_Ldiff);
R_temp = ReadReverbWork(z1_Rdiff);
L_diff = L_temp + gIIR * (L_diff - L_temp);
R_diff = R_temp + gIIR * (R_diff - R_temp);
/*
* Early Echo(Comb Filter)
*/
L_in = gCOMB1 * ReadReverbWork(m1_Lcomb) + gCOMB2 *ReadReverbWork(m2_Lcomb) + gCOMB3 *ReadReverbWork(m3_Lcomb) + gCOMB4 *ReadReverbWork(m4_Lcomb);
R_in = gCOMB1 * ReadReverbWork(m1_Rcomb) + gCOMB2 *ReadReverbWork(m2_Rcomb) + gCOMB3 *ReadReverbWork(m3_Rcomb) + gCOMB4 *ReadReverbWork(m4_Rcomb);
/*
* Late Reverb(Two All Pass Filters)
*/
L_temp = ReadReverbWork(zm_Lapf1);
R_temp = ReadReverbWork(zm_Rapf1);
L_apf1 = L_in - gAPF1 * L_temp;
R_apf1 = R_in - gAPF1 * R_temp;
L_in = L_temp + gAPF1 * L_apf1;
R_in = R_temp + gAPF1 * R_apf1;
L_temp = ReadReverbWork(zm_Lapf2);
R_temp = ReadReverbWork(zm_Rapf2);
L_apf2 = L_in - gAPF2 * L_temp;
R_apf2 = R_in - gAPF2 * R_temp;
L_in = L_temp + gAPF2 * L_apf2;
R_in = R_temp + gAPF2 * R_apf2;
/*
* Output
*/
SetOutputL(L_in);
SetOutputR(R_in);
/*
* Write Buffer
*/
WriteReverbWork(z0_Lsame, L_same);
WriteReverbWork(z0_Rsame, R_same);
WriteReverbWork(z0_Ldiff, L_diff);
WriteReverbWork(z0_Rdiff, R_diff);
WriteReverbWork(z0_Lapf1, L_apf1);
WriteReverbWork(z0_Rapf1, R_apf1);
WriteReverbWork(z0_Lapf2, L_apf2);
WriteReverbWork(z0_Rapf2, R_apf2);
/*
* Update Circular Buffer
*/
UpdateReverbWork();
}
|
CD(コンパクトディスク読込み装置)
CDコマンド
| 番号 | コマンド名 | 数 | 1 | 2 | 3 |
|---|---|---|---|---|---|
| 0x00 | Sync | 0 | |||
| 0x01 | Nop | 0 | |||
| 0x02 | Setloc | 3 | AMin | ASec | AFrac |
| 0x03 | Play | 0/1 | Track | ||
| 0x04 | Forward | 0 | |||
| 0x05 | Backward | 0 | |||
| 0x06 | ReadN | 0 | |||
| 0x07 | Standby | 0 | |||
| 0x08 | Stop | 0 | |||
| 0x09 | Pause | 0 | |||
| 0x0A | Reset | 0 | |||
| 0x0B | Mute | 0 | |||
| 0x0C | Demute | 0 | |||
| 0x0D | Setfilter | 2 | File | Chan | |
| 0x0E | Setmode | 1 | Mode | ||
| 0x0F | Getparam | 0 | |||
| 0x10 | GetlocL | 0 | |||
| 0x11 | GetlocP | 0 | |||
| 0x12 | ReadT | 1 | 0x01 | ||
| 0x13 | GetTN | 0 | |||
| 0x14 | GetTD | 1 | Track | ||
| 0x15 | SeekL | 0 | |||
| 0x16 | SeekP | 0 | |||
| 0x17 | Setclock | ||||
| 0x18 | Getclock | ||||
| 0x19 | Test | 1 | Num | ||
| 0x1A | Id | 0 | |||
| 0x1B | ReadS | 0 | |||
| 0x1C | Init | 0 | |||
| 0x1D | |||||
| 0x1E | ReadTOC | 0 | |||
| 0x1F |
| 番号 | コマンド名 | 所要 クロック | σ% | 割り 込み | 数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x00 | Sync | 0x03 | 1 | Stat | |||||||||
| 0x01 | Nop | 30547 | 23 | 0x03 | 1 | Stat | |||||||
| 0x02 | Setloc | 31557 | 7 | 0x03 | 1 | Stat | |||||||
| 0x03 | Play | 51444 | 0x03 | 1 | Stat | ||||||||
| 0x04 | Forward | 193250 | 12 | 0x03 | 1 | Stat | |||||||
| 0x05 | Backward | 125119 | 0 | 0x03 | 1 | Stat | |||||||
| 0x06 | ReadN | 75701 | 7 | 0x03 | 1 | Stat | |||||||
| 0x07 | Standby | 74753 | 5 | 0x03 | 1 | Stat | |||||||
| 0x08 | Stop | 43503 | 13 | 0x03 | 1 | Stat | |||||||
| 0x09 | Pause | 27648 | 24 | 0x03 | 1 | Stat | |||||||
| 0x0A | Reset | 80295 | 12 | 0x03 | 1 | Stat | |||||||
| 0x0B | Mute | 36114 | 41 | 0x03 | 1 | Stat | |||||||
| 0x0C | Demute | 32768 | 10 | 0x03 | 1 | Stat | |||||||
| 0x0D | Setfilter | 39105 | 48 | 0x03 | 1 | Stat | |||||||
| 0x0E | Setmode | 41082 | 52 | 0x03 | 1 | Stat | |||||||
| 0x0F | Getparam | 40163 | 25 | 0x03 | 5 | Stat | Mode | 0x00 | File | Chan | |||
| 0x10 | GetlocL | 43335 | 20 | 0x03 | 8 | セクタの13番目~20番目のデータ(つまりRead,SeekLが必要) | |||||||
| 0x11 | GetlocP | 37937 | 17 | 0x03 | 8 | Track | Index | Min | Sec | Frac | AMin | ASec | AFrac |
| 0x12 | ReadT | 98762 | 13 | 0x03 | 1 | Stat | |||||||
| 0x13 | GetTN | 40833 | 0x03 | 3 | Stat | Begin | End | ||||||
| 0x14 | GetTD | 39091 | 0x03 | 3 | Stat | AMin | ASec | ||||||
| 0x15 | SeekL | 41771 | 25 | 0x03 | 1 | Stat | |||||||
| 0x16 | SeekP | 62041 | 40 | 0x03 | 1 | Stat | |||||||
| 0x17 | Setclock | ||||||||||||
| 0x18 | Getclock | ||||||||||||
| 0x19 | Test(0x20) | 40864 | 14 | 0x03 | 4 | Year | Mon | Day | Ver | ||||
| 0x1A | Id | 30980 | 22 | 0x03 | 1 | Stat | |||||||
| 0x1B | ReadS | 81925 | 2 | 0x03 | 1 | Stat | |||||||
| 0x1C | Init | 25847 | 29 | 0x03 | 1 | Stat | |||||||
| 0x1D | |||||||||||||
| 0x1E | ReadTOC | 73292 | 10 | 0x03 | 1 | Stat | |||||||
| 0x1F | |||||||||||||
| 番号 | コマンド名 | 所要 クロック | σ% | 割り 込み | 数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x00 | Sync | ||||||||||||
| 0x01 | Nop | ||||||||||||
| 0x02 | Setloc | ||||||||||||
| 0x03 | Play | 0x01 | 8 | Stat | Track | Index | A/Min | A/Sec | A/Frac | LvHi | LvLo | ||
| 0x04 | Forward | ||||||||||||
| 0x05 | Backward | ||||||||||||
| 0x06 | ReadN | 0x01 | 1 | Stat | |||||||||
| 0x07 | Standby | 51429486 | 55 | 0x02 | 1 | Stat | |||||||
| 0x08 | Stop | 12767194 | 2 | 0x02 | 1 | Stat | |||||||
| 0x09 | Pause | 3665794 | 0 | 0x02 | 1 | Stat | |||||||
| 0x0A | Reset | 3656479 | 6 | 0x02 | 1 | Stat | |||||||
| 0x0B | Mute | ||||||||||||
| 0x0C | Demute | ||||||||||||
| 0x0D | Setfilter | ||||||||||||
| 0x0E | Setmode | ||||||||||||
| 0x0F | Getparam | ||||||||||||
| 0x10 | GetlocL | ||||||||||||
| 0x11 | GetlocP | ||||||||||||
| 0x12 | ReadT | 57701665 | 0 | 0x02 | 1 | Stat | |||||||
| 0x13 | GetTN | ||||||||||||
| 0x14 | GetTD | ||||||||||||
| 0x15 | SeekL | 0x02 | 1 | Stat | |||||||||
| 0x16 | SeekP | 0x02 | 1 | Stat | |||||||||
| 0x17 | Setclock | ||||||||||||
| 0x18 | Getclock | ||||||||||||
| 0x19 | Test(0x20) | ||||||||||||
| 0x1A | Id | 19658 | 1 | 0x02 | 8 | 0x02 | 0x00 | 0x20 | 0x00 | 'S' | 'C' | 'E' | 'I' |
| 0x1B | ReadS | 0x01 | 1 | Stat | |||||||||
| 0x1C | Init | ||||||||||||
| 0x1D | |||||||||||||
| 0x1E | ReadTOC | 31330364 | 14 | 0x02 | 1 | Stat | |||||||
| 0x1F |
| コマンド名 | 補足事項 |
|---|---|
| Play | Setmodeで0x04を立てると、AFrac(BCD)の下位4ビットが0の時にレポートを返す。上位が奇数の時は位置情報がトラック相対になるが、その際フラグとしてSec(BCD)の0x80が立つ。 |
| Reset | 位置もホームポジションに戻る。 |
| ReadT | 引数は0x01以外上手くいっていない。 |
| GetTD | 引数を0x00にすると全トラックの合計になる。 |
| Init | 承認後も暫く時間をあける必要がある。 |
通信デバイス(Comms Device or COMA)
準備中
独自解析プログラム群
準備中
エミゅってしまうま


No comments:
Post a Comment