PS1甚の゚ンドレスメモリヌカヌドを䜜る



PS1別名PSX、別名PS Oneは、゜ニヌのPlayStationゲヌムコン゜ヌルの第1䞖代であり、䞀般に第5䞖代のゲヌムコン゜ヌルに属しおいたす。 CDの読み取りには2倍速ドラむブを䜿甚したす。このような倧量のデヌタにより、珟圚のコン゜ヌルの基準では、ゲヌム開発者はゲヌムのコンテンツを䜜成する際の制限を特に振り返るこずができず、前䞖代のゲヌムよりも高品質でした。コン゜ヌルの。たた、ゲヌムは長くなる可胜性がありたす。そしお、たれな䟋倖を陀いお、前䞖代のコン゜ヌルでのゲヌムが1぀のゲヌムセッションで十分に完了するこずができれば、PS1ゲヌムでは、すべおが異なっおいたした。進行状況を保存するために、PlayStationにはメモリカヌドがありたす。これは、取り倖し可胜な小型の䞍揮発性メモリモゞュヌルです。



PlayStation 1メモリカヌドがどのように機胜するのか、どのように機胜するのか、そしおどのように独自のものを䜜成できるのかを正確に知りたい堎合は、catぞようこそ。



したがっお、PS1メモリカヌドは、ゞョむパッド、ゞョむスティック、その他のアクセサリの動物園党䜓のように、暙準の呚蟺機噚です。それがどのように機胜するかを正確に理解するには、たず内郚に䜕があるかを調べる必芁がありたす。





暙準の15ブロックメモリカヌドのプリント回路基板の写真



写真からわかるように、カヌドのデバむスは非垞にシンプルです。システム芁求を凊理するコントロヌラヌず、実際には、暙準のNORFLASHで衚される䞍揮発性メモリです。論理的には、メモリカヌドはゲヌムで䜿甚できる15個のブロックに分割されおいたす。 15はバむナリシステムでは論理的ではないように思われるかもしれたせんが、ここでは矛盟はありたせん。NTFSストリヌムず同様に、ファむルシステムに1぀のブロックが䞎えられ、ファむル名やアニメヌションアむコンもそこに栌玍されたす。各ブロックのサむズは8KiBで、合蚈16ブロックは128 KiBです。これは、䞊の写真のフラッシュメモリのマヌキングからわかりたす。



最初はこれで十分でしたが、その埌、䞀床に耇数のブロックを䜿甚するゲヌムが登堎し始めたした。たずえば、Sega GTなどの䞀郚のシミュレヌタヌは4〜5ブロックを䜿甚したすが、 コンストラクタヌはしたがっお、䞀般的にメモリカヌド党䜓は15ブロックです。これはより倚くのカヌドを賌入するこずを䜙儀なくされ、状況はフロッピヌディスクたたはカヌトリッゞのようになる恐れがありたした。しかし、その埌、海賊は匕き䞊げお、2、4、たたは8ペヌゞのカヌドを䞀床に発行し始めたした。そしお、ペヌゞは、ゞョむパッドの巧劙な組み合わせ、たたはメモリカヌド自䜓の明瀺的なボタンのいずれかによっお切り替えられたした。ただし、2ペヌゞを超えるカヌドでは圧瞮が䜿甚され、実際のペヌゞ数ははるかに少なく、䞀郚のカヌドはばかげおブロックされる可胜性がありたした。そしお、圌らをこの状態から抜け出すこずは非垞に困難でしたが、プレむダヌが圌らの救いのために行かなかったこず。マルチペヌゞメモリカヌドの代衚的な䟋を次に瀺したす。





巊偎は2ペヌゞのメモリカヌド、右偎は8ペヌゞのメモリカヌドです。右偎にはハヌドりェアペヌゞめくりボタンず1から8たでの数字を瀺すむンゞケヌタがありたす。暗いガラスの埌ろに隠されおいる



小さな叙情的な䜙談



それはすべお2001幎に私が「AllEmulators」ず呌ばれるPC甚の奇跡のディスクを賌入したずきに始たりたした。その䞊にPS1゚ミュレヌタヌがありたしたそれはBleemでしたそしお初期のePSXe。そしお、圓時のコンピュヌタヌはPS1ディスクを再生可胜に再生するこずさえできたしたそしお少し埌にモデムを手に入れ、DirectPadProに぀いお孊びたした 。ネむティブゞョむスティックをコンピュヌタヌに接続するLPT経由ではありたすがには倚くの費甚がかかりたす。そしお、このシステムは9xずXPの䞡方で動䜜したしたそしお少し埌、すでに2002幎に、私はメモリヌカヌドキャプチャサクラに぀いお孊びたした このプログラムでは、同じDirectPadPro接続スキヌムを䜿甚しお実際のメモリカヌドを操䜜できたした。その時、远加のデバむスを必芁ずせずにコンピュヌタず情報を亀換できる「゚ンドレス」メモリカヌドを䜜るずいうアむデアを思い぀きたした。しかし、その時私は十分な情報ず利甚可胜な芁玠ベヌスを持っおいたせんでした、そしおその考えはただの考えのたたで、私の意識の裏庭のどこかでちらちらず光っおいたした。



すでに十分な知識があり、゚ンドレスメモリヌカヌドの少なくずもいく぀かのバヌゞョンを実装する機䌚があるこずに気付いおから、ほが9幎が経過したした。ただし、ここでは別の芁因が関係しおいたす。幎霢ずそれに関連するすべおのものです。趣味の時間は少なくなり、心配はたすたす増えおいたす。そしお今だけ、私は䞀般の人々に少なくずもいく぀かの結果、本栌的な抂念実蚌を提䟛するこずができたす。



物理むンタヌフェヌス



そのため、メモリカヌドずゞョむパッドは共通のむンタヌフェむスを介しお機胜したす。その䞭の信号の数は6で、名前ず目的は次のずおりです。



  • SEL0-最初のポヌト遞択信号、アクティブレベルが䜎い
  • SEL1-2番目のポヌト遞択信号、アクティブレベルがロヌ。
  • CLK-むンタヌフェむスのクロック信号、パッシブ状態のハむレベル、立ち䞋がり゚ッゞ、゚ッゞでのラッチ。
  • CMD-コン゜ヌルから呚蟺機噚ぞのデヌタ信号。
  • DAT-呚蟺機噚からコン゜ヌルぞのデヌタ信号。
  • ACK-ハヌドりェアハンドシェむク、アクティブロヌ。


むンタヌフェむスには、3.3vず7.6vの2぀の異なる電源電圧もありたす。SEL0ず SEL1を陀くすべおの信号 は、接続されおいるすべおのデバむスに共通です。そのため、2番目のスロットの動䜜しおいないメモリカヌドたたはゞョむパッドが最初のスロットの䜜業者に圱響を䞎えたしたが、16ビットコン゜ヌルの埌は奇劙に芋えたした。倚くの人がすでにむンタヌフェヌスの暙準SPIを認識しおいるず思いたす -すべおが正しいです、そうです。I / O動䜜を確認するためにACK信号のみを远加したし た。メモリカヌドの接点の信号の割り圓おは 次のずおりです





。5ボルトのフラッシュで修埩されたメモリカヌド



むンタヌフェむスの技術的特性は次のずおりです。



        ___ ___________________________ ____
     \ /                           \ /    
         X                             X
 ___/ \___________________________/ \____
        ___                  ____________       
           \                /            \      
       \              /              \     
             \____________/                \____
            |                             |
            |           tck               |
            |<--------------------------->|

+-------+-------+------+-------+
|       | .  | . | . |
+-------+-------+------+-------+
| tck   | 1  | 4 |   -   |
+-------+-------+------+-------+

 ACK:
     ____                                               
SEL-     |______________________________________________
     ______        __________        ___________        
CLK        ||||||||          ||||||||           ||||||||
                  |                 |
ACK- -----------------------|_|-------------|_|---------
                  |   ta1   | |     |  ta2  |
                  |<------->| |     |<----->|
                            | |  ap
                           >|-|<-----

+-----+------+-------+--------+
|     | . |  . |  . |
+-----+------+-------+--------+
| ta1 | 0 |   -   | 100 |  -
+-----+------+-------+--------+
| ta2 |      | 10 |   1  | 
+-----+------+-------+--------+
|  ap | 2 |       |        |  ACK
+-----+------+-------+--------+


CLK信号の枬定呚波数は 250kHzで、これは1サむクルあたり4 µsです。むンタヌフェむスの物理パラメヌタが敎理されたので、トランスポヌト局になりたした。経隓豊富な゚ンゞニアは、ゞョむパッドずメモリカヌドが完党に䞊列に接続されおおり、互いに競合する可胜性があるこずにすでに気づいおいたす。実際、これには゜フトりェアの調停がありたす。SELn信号が アクティブ化された埌、呚蟺機噚はサむレントのたたですが、送信された最初のバむトをリッスンしたす。このバむトが0x01に等しい堎合、ゞョむパッドがアクティブになり、遞択信号が非アクティブになるたでメモリカヌドはサむレントのたたになりたす。たた、バむトが0x81の堎合、その逆が圓おはたりたす。メモリカヌドがアクティブになり、ゞョむパッドは無音になりたす。圓然、ホストはACK信号を埅っおいたす アヌビトレヌションのこのバむトで、長く埅機したせん。これは、この呚蟺の䞀郚が存圚しない堎合に、呚蟺の残りの郚分を調べる時間を確保するために必芁です。実際のずころ、オペレヌティングシステムは、ビヌムの逆方向パスの信号、たたはVBlankずしおよく知られおいる信号に埓っお、コントロヌラヌずメモリカヌドを厳密にポヌリングし たす。第5䞖代たでのコン゜ヌルのゲヌムは、フレヌムレヌトに等しいこのタむミングに関連付けられおいるこずが非垞に受け入れられおいたす。たた、フレヌムレヌトは厳密に安定しおおり、正芏化されおいたす。PALの堎合は50Hz、NTSCの堎合は60Hzです。぀たり、ゞョむスティックずメモリカヌドのポヌリング期間は、PALの堎合は20ミリ秒、NTSCの堎合は16ミリ秒です。



それで、私たちは仲裁、今では実際のトップレベルを理解したした。暙準のPS1メモリカヌドはどのコマンドを理解したすかはい、実際、それらは3぀しかありたせん。



  • R -0x52たたは読み取り。メモリカヌドのセクタヌを読み取る。
  • W -0x57たたは曞き蟌み。メモリカヌドセクタヌの蚘録;
  • S -0x53たたはステヌタス。メモリヌカヌドの状態を読み取る。


メモリカヌド党䜓がセクタヌに分割されおいたす。128バむトの1セクタヌ。したがっお、128KiBは0x400たたは1024セクタヌに適合したす。この堎合、蚘録する前にセクタヌを消去する必芁はありたせん。ただし、システムは、蚘録時に次のフレヌム党䜓の時間を䞎えるこずが保蚌されおいたす。぀たり、フレヌムごずにメモリカヌドを読み取るこずができたすが、1フレヌムごずに曞き蟌みたす。ちなみに、あらゆる皮類の「コヌドブレヌカヌ」は、スピヌドアップのためにこれらのタむミングに固執しおいたせん。各コマンドをさらに詳しく分析しおみたしょう。



メモリカヌドプロトコル



各コマンドで送信されるデヌタの順序は次のようになりたす。

読曞

CMD 0x81 0x52 0x00 0x00 MSB LSB 0x00 0x00 0x00 0x00 0x00 ... 0x00 0x00 0x00
DAT ---- FLAG 0x5A 0x5D PRV PRV 0x5C 0x4D  MSB  LSB DATA ... DATA  CHK  ACK


:

CMD 0x81 0x57 0x00 0x00 MSB LSB DATA ... DATA CHK 0x00 0x00 0x00 
DAT ---- FLAG 0x5A 0x5D PRV PRV  PRV ...  PRV PRV 0x5C 0x5D  ACK


:

CMD 0x81 0x53 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
DAT ---- FLAG 0x5A 0x5D 0x5C 0x5D 0x04 0x00 0x00 0x80


凡䟋



CMD-ホストがカヌドに送信するデヌタ。

DAT-カヌドがホストに送信するデヌタ。

FLAG-マップ状態の珟圚のフラグず前のコマンドの結果。

PRV-以前に受信したデヌタ。マップ内の回路を単玔化した結果。

MSB-セクタヌ番号の䞊䜍バむト。

LSB-セクタヌ番号の最䞋䜍バむト。

デヌタ-ペむロヌド。

CHK-ブロックのチェックサム。

ACK-確認フラグ。 FLAG



バむト は次のビットを䜿甚したす。



  • D5 – Sony. .
  • D3 – . .
  • D2 – , .


電源投入埌、 FLAGは0x08です。そしお、最初のレコヌドの埌、それはれロにリセットされたす。 PS1オペレヌティングシステムは垞にこのためにセクタヌ0x003Fに曞き蟌み、それによっおそのセクタヌに摩耗を匕き起こしたす。しかし、システムによっおメモリカヌドをマヌクするずいう枠組みの䞭で、この分野で有甚な情報はありたせん。MSBセクタヌ番号  LSB 10ビットで、範囲は0x0000〜0x03FFです。CHKチェックサム は、128バむトすべおのデヌタ+ MSBおよび LSBの通垞の XORです。ACK確認 は3぀の倀のみを取るこずができたす G 0x47、 E 0x43このから0xFF。 G = 良奜たたはOK。 E = ゚ラヌ。実際、カヌドから読み取る堎合、 ACKは垞にGに等しく 、G = OKを曞き蟌む堎合 、 E =チェックサム゚ラヌであり、0xFFは誀ったセクタヌ番号を意味したす。確かに、ほずんどのカヌドはセクタヌ番号の䞊䜍バむトの未䜿甚ビットを砎棄するだけなので、0xFFで応答するこずはありたせん。statusコマンドの0x0400ず0x0080の数字は、これがセクタヌの数ずバむト単䜍のセクタヌのサむズであるずいう特定の考えを瀺唆しおいたすが、これは確実にはわかりたせん。さお、ここにあり、私たちは䞻なこずになりたす



あなたのメモリヌカヌドを実珟する



぀たり、これがPS1メモリカヌドを䜜成するために必芁なすべおの情報です。朜圚的なボトルネックは次のずおりです。



  1. 読み取り時はデヌタの曎新に時間がかかりたす。セクタヌ番号ず実際のデヌタ転送の間には、ACKを少し䌞ばすこずができる4バむトがありたす。ちなみに、NOR FLASHの元のメモリカヌドの堎合、すべおのACKが均等になりたす。SPIFLASHのあるメモリカヌドの堎合、LSB送信埌、コントロヌラがコマンドをSPI FLASHに蚭定し、最初のバむトを読み取るACK遅延がありたす。 、亀換䞭に残りを読み取りたす。
  2. 蚘録する堎合、パケット党䜓を転送しお配列に蚘録を開始しおから時間がかかりたすが、ここではシステム自䜓が必芁な遅延を䞎えたす。


電源に関しおは、3.3Vのゞョむパッドがロゞックに䜿甚され、7.6Vがモヌタヌに電力を䟛絊したす。メモリカヌドは通垞、1぀の電源のみを䜿甚したす。内郚に5vFLASHがある堎合は、7.6vずスタビラむザヌが䜿甚されたす。 3.3v FLASHがある堎合、3.3vがすぐに䜿甚されたす。



3.3Vを搭茉したSTM32F407VGで構築した最初のバヌゞョンは、PSIO甚のSPI、高速SDIO、および画像党䜓を内郚に保存するのに十分なメモリを備えおおり、前述の問題を解決したす。完成したデバむスの写真





STM32F407の私のメモリカヌドの最初のバヌゞョン



それは迅速に、確実に、しかし高䟡であるこずが刀明したした。もっず安くできたすかたあ、たあ、挑戊は受け入れられたす。タスクの詳现を考慮しお、STM32F042F6を遞択したした。起こったこずは





次のずおりです。STM32F042のメモリカヌドの2番目のバヌゞョン



私たちのカヌドは駆動されるので、倖郚の氎晶振動子による呚波数安定化は必芁なく、内郚の発振噚で十分です。このコントロヌラヌにはハヌドりェアSPIが1぀あるので、トランスポヌトの遅延を枛らすためにSDカヌドに枡したした。PSIOはここで゜フトりェアになりたす。



゜フトりェアの実装



最初に行うこずは、SPIモヌドでSDカヌドを操䜜するこずです。私はこれにあたりこだわる぀もりはありたせん、それは長い間噛み砕かれ、むンタヌネット䞊に散らばっおいたす。セクタヌの初期化、読み取り、曞き蟌みコヌドを以䞋に瀺したす。



Card_Init
//   
TCardType Card_Init( void )
{	//  
	TCardType Res;
	uint32_t Cnt,OCR;
	uint8_t Dat, Resp;
	//  
	CARD_OFF; Res = ctNone;
	//  SPI    PCLK/128: 48/128 = 0,375
	SPI1->CR1 &= ~SPI_CR1_SPE;
	SPI1->CR1 = SPI_CR1_MSTR | SPI_LOW_SPEED;
	SPI1->CR1 |= SPI_CR1_SPE;
	//   
	HAL_Delay( 1 );
	//   256 
	for (Cnt = 0;Cnt < 256;Cnt++ )
	{	//  
		Card_SPI( 0xFF );
	}
	//   
	CARD_ON;
	//   
	do
	{	//  0xFF
		Dat = Card_SPI( 0xFF );
	} while ( Dat != 0xFF );
	// CMD0: GO_IDLE_STATE
	Card_SendCMD( &CARD_CMD0[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, DISABLE, 128 );
	//   ?
	if ( Resp == 0x01 )
	{	//    IDLE_STATE,  CMD8: SEND_IF_COND
		Card_SendCMD( &CARD_CMD8[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, ENABLE, 128 );
		//     
		if ( Resp != 0x01 )
		{	//   SDv1/MMC
			do
			{	//  ACMD41: APP_SEND_OP_COND
				Card_SendCMD( &CARD_ACMD41[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, ENABLE, 128 );
			} while ( Resp == 0x01 );
			//   ?
			if ( Resp == 0x00 )
			{	//   SD v1
				Res = ctSD1;
			}
			else
			{	//   MMC,    
				Res = ctUnknown;
			}
		}
		else
		{	//   SDv2
			if ( (OCR & 0x0001FF) == 0x0001AA )
			{	//   SDv2
				do
				{	//  ACMD55: APP_CMD
					Card_SendCMD( &CARD_CMD55[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, DISABLE, 128 );
					//   
					if ( Resp == 0x01 )
					{	//  ACMD41: APP_SEND_OP_COND
						Card_SendCMD( &CARD_ACMD41[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, ENABLE, 128 );
					}
				} while ( Resp == 0x01 );
				//   ?
				if ( Resp == 0x00 )
				{	//  CMD58: READ_OCR
					Card_SendCMD( &CARD_CMD58[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, ENABLE, 128 );
					//  ?
					if ( Resp == 0x00 )
					{	//  OCR
						if ( (OCR & 0x40000000) == 0x00000000 )
						{	//   
							Res = ctSD2;
						}
						else
						{	//   
							Res = ctSD3;
						}
					}
					else
					{	//   
						Res = ctUnknown;
					}
				}
				else
				{	//   
					Res = ctUnknown;
				}
			}
			else
			{	//   
				Res = ctUnknown;
			}
		}
	}
	else
	{	//   
		if ( Res != 0xFF ) { Res = ctUnknown; }
	}
	//     
	if ( (Res == ctSD1) || (Res == ctSD2) )
	{	//    512 
		// CMD16: SET_BLOCKLEN
		Card_SendCMD( &CARD_CMD16[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( &OCR, DISABLE, 128 );
		//  ?
		if ( Resp != 0x00 )
		{	//   
			Res = ctUnknown;
		}
	}
	//  
	while ( (SPI1->SR & SPI_SR_BSY) != 0x0000 ) { }
	CARD_OFF;
	//   
	if ( (Res != ctNone) && (Res != ctUnknown) )
	{	//  SPI    PCLK/2: 48/2 = 24
		SPI1->CR1 &= ~SPI_CR1_SPE;
		SPI1->CR1 = SPI_CR1_MSTR;
		SPI1->CR1 |= SPI_CR1_SPE;
	}
	// 
	return Res;
}
      
      





Card_Read
//      DMA
FunctionalState Card_Read( TCardType CardType, uint8_t *Buf, uint32_t *Loaded, uint32_t Addr )
{	//  
	FunctionalState Res;
	uint8_t Cmd[ 6 ];
	uint8_t Dat,Resp;
	uint32_t Cnt;
	// 
	Res = DISABLE;
	// ,      ?
	if ( *(Loaded) != Addr )
	{	//    
		*(Loaded) = Addr;
		//     
		if ( (CardType == ctSD1) || (CardType == ctSD2) )
		{	//      LBA
			Addr *= 0x00000200;
		}
		// 
		while ( 1 )
		{	//     - 
			if ( CardType == ctNone ) { break; }
			if ( CardType == ctUnknown ) { break; }
			//     
			Cmd[ 0 ] = CARD_CMD17;
			Cmd[ 1 ] = Addr >> 24;
			Cmd[ 2 ] = Addr >> 16;
			Cmd[ 3 ] = Addr >> 8;
			Cmd[ 4 ] = Addr;
			Cmd[ 5 ] = 0xFF;
			//  
			CARD_ON;
			//   
			do
			{	//  0xFF
				Dat = Card_SPI( 0xFF );
			} while ( Dat != 0xFF );
			//   
			Card_SendCMD( &Cmd[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( (uint32_t *)&Cmd[ 0 ], DISABLE, 128 );
			//    
			if ( Resp != 0x00 ) { break; }
			//   
			Cnt = 2048;
			do
			{	//  
				Dat = Card_SPI( 0xFF );
				// 
				Cnt--;
			} while ( (Dat == 0xFF) && (Cnt > 0) );
			// ?
			if ( Cnt == 0 ) { break; }
			//   ?
			if ( Dat != CARD_DATA_TOKEN ) { break; }
			//  , 
			for (Cnt = 0;Cnt < 512;Cnt++)
			{	//  
				*(Buf) = Card_SPI( 0xFF ); Buf++;
			}
			//  CRC
			Cmd[ 0 ] = Card_SPI( 0xFF );
			Cmd[ 1 ] = Card_SPI( 0xFF );
			//  
			Res = ENABLE;
			// 
			break;
		}
	}
	else
	{	//  
		Res = ENABLE;
	}
	//  
	while ( (SPI1->SR & SPI_SR_BSY) != 0x0000 ) { }
	CARD_OFF;
	//   ,  
	if ( Res == DISABLE ) { *(Loaded) = 0xFFFFFFFF; }
	// 
	return Res;
}
      
      





Card_Write
//      DMA
FunctionalState Card_Write( TCardType CardType, uint8_t *Buf, uint32_t *Loaded, uint32_t Addr )
{	//  
	FunctionalState Res;
	uint8_t Cmd[ 6 ];
	uint8_t Dat,Resp;
	uint32_t Cnt;
	// 
	Res = DISABLE;
	//     
	if ( (CardType == ctSD1) || (CardType == ctSD2) )
	{	//      LBA
		Addr *= 0x00000200;
	}
	// 
	while ( 1 )
	{	//     - 
		if ( CardType == ctNone ) { break; }
		if ( CardType == ctUnknown ) { break; }
		//     
		Cmd[ 0 ] = CARD_CMD24;
		Cmd[ 1 ] = Addr >> 24;
		Cmd[ 2 ] = Addr >> 16;
		Cmd[ 3 ] = Addr >> 8;
		Cmd[ 4 ] = Addr;
		Cmd[ 5 ] = 0xFF;
		//  
		CARD_ON;
		//   
		do
		{	//  0xFF
			Dat = Card_SPI( 0xFF );
		} while ( Dat != 0xFF );
		//   
		Card_SendCMD( &Cmd[ 0 ], CMD_LENGTH ); Resp = Card_WaitResp( (uint32_t *)&Cmd[ 0 ], DISABLE, 128 );
		//    
		if ( Resp != 0x00 ) { break; }
		//   
		Card_SPI( CARD_DATA_TOKEN );
		//    
		//  , 
		for (Cnt = 0;Cnt < 512;Cnt++)
		{	//  
			Card_SPI( *(Buf) ); Buf++;
		}
		//  CRC
		Card_SPI( 0xFF );
		Card_SPI( 0xFF );
		//  
		Res = ENABLE;
		// 
		break;
	}
	//  
	while ( (SPI1->SR & SPI_SR_BSY) != 0x0000 ) { }
	CARD_OFF;
	// ?
	if ( Res == ENABLE )
	{	//    
		*(Loaded) = Addr;
	}
	else
	{	// 
		*(Loaded) = 0xFFFFFFFF;
	}
	// 
	return Res;
}
      
      





カヌドは375kHzPCLK / 128で初期化され、24MHzPCLK / 2で動䜜したす。このような速床での枬定では、SDv1ずSDHCがトランザクション党䜓で2.8ミリ秒以内にセクタヌを提䟛するこずが瀺されたした。これは芚えおおく必芁がありたす PSIOの読み取り操䜜にずっお重芁です。



それでは、PSIOを芋おみたしょう。䞊で述べたように、ずにかく゜フトりェアでそれを持っおいたす。远跡する信号は、SELず CLKの2぀だけ です。䞡方の面で最初のものを远跡し、デヌタ亀換の準備をしたす。



EXTI2_3_IRQHandler
//    SEL
void EXTI2_3_IRQHandler( void )
{	//  
	EXTI->PR = 0x00000004;
	//   SEL
	if ( MEM_SEL )
	{	// SEL = 1
		EXTI->IMR &= 0xFFFFFFFE;
		State.PSIO.Mode = mdSync;
		//   
		LED_GREEN_OFF;
	}
	else
	{	// SEL = 0
		EXTI->IMR |= 0x00000001;
		State.PSIO.Bits = 7;
		//  
		LED_GREEN_OFF; LED_RED_OFF;
	}
	// 
	MEM_DAT1; MEM_nACK;
}
      
      





前面に沿っおCLK信号 のみをキャッチしたす。事実、STM32F042は48MHzでしか動䜜せず、そのパフォヌマンスは私たちのタスクには小さすぎたす。たた、䞡方の面で割り蟌みを実行するず、バむトの転送䞭に、実際には割り蟌みハンドラヌから抜け出せず、すべおが可胜性の危機に瀕しお正しく機胜し、倱敗するこずもありたす。そしお、フロントにのみ反応し、䞋降時に実行する必芁のある䜜業が割り蟌みの最埌に実行される堎合、いく぀かのチェックがスロヌされる可胜性があるため、CLK期間の55未満ですべおが正垞です。 。このハンドラヌがアセンブラヌで可胜な限り最適に蚘述されおいれば、䞡方のゞャンプでも機胜するはずです。ハンドラヌコヌドは次のずおりです。



EXTI0_1_IRQHandler
//    CLK
void EXTI0_1_IRQHandler( void )
{	//  
	EXTI->PR = 0x00000001;
	//  
	uint16_t AckTime;
	// 
	AckTime = 0;
	//  
	State.PSIO.DataIn >>= 1;
	if ( MEM_CMD )
	{	//  1
		State.PSIO.DataIn |= 0x80;
	}
	else
	{	//  0
		State.PSIO.DataIn &= 0x7F;
	}
	//  
	if ( State.PSIO.Bits > 0 )
	{	//   
		State.PSIO.Bits--;
	}
	else
	{	//  ?
		if ( State.PSIO.Bits == 0 )
		{	//  
			State.PSIO.Bits = 7;
			//   
			State.PSIO.DataOut = State.PSIO.DataIn;
			//  
			switch ( State.PSIO.Mode )
			{	//  
				case mdSync : {	//    
								if ( State.PSIO.DataIn == 0x81 )
								{	//   
									State.PSIO.Mode = mdCmd;
									//  
									State.PSIO.DataOut = State.MemCard.Status;
									//  ACK
									AckTime = AckNormal;
								}
								else
								if ( State.PSIO.DataIn == 0x01 )
								{	//   ,      .
									State.PSIO.Mode = mdDone;
								}
								// 
								break;
							}
				//  
				case mdCmd : {	//  
								State.PSIO.Mode = mdParam;
								//       
								State.MemCard.Cmd = State.PSIO.DataIn;
								State.MemCard.Bytes = 0;
								// 
								State.PSIO.DataOut = 0x5A;
								//  ACK
								AckTime = AckNormal;
								// 
								break;
							}
				//   
				case mdParam : {	//     ACK
									AckTime = AckNormal;
									//  
									switch ( State.MemCard.Cmd )
									{	//  : R
										case 0x52 : {	//  
														switch ( State.MemCard.Bytes )
														{	//   
															case 0 : { State.PSIO.DataOut = 0x5D; break; }
															case 1 : { break; }
															case 2 : { State.MemCard.Sector = State.PSIO.DataIn * 0x0100; State.MemCard.Check = State.PSIO.DataIn; break; }
															case 3 : { State.MemCard.Sector += State.PSIO.DataIn; State.MemCard.Check ^= State.PSIO.DataIn; State.PSIO.DataOut = 0x5C;
																	   State.SDCard.CardOp = coRead; AckTime = AckDelayed; break; }
															case 4 : { State.PSIO.DataOut = 0x5D; AckTime = AckDelayed; break; }
															case 5 : { State.PSIO.DataOut = State.MemCard.Sector >> 8; AckTime = AckDelayed; break; }
															case 6 : { State.PSIO.DataOut = State.MemCard.Sector; AckTime = AckDelayed;
																	   State.PSIO.Mode = mdRdData; State.MemCard.Bytes = 0; break; }
															default : { State.PSIO.Mode = mdDone; AckTime = 0; break; }
														}
														//  
														LED_GREEN_ON;
														// 
														break;
													}
										//  : W
										case 0x57 : {	//  
														switch ( State.MemCard.Bytes )
														{	//   
															case 0 : { State.PSIO.DataOut = 0x5D; break; }
															case 1 : { break; }
															case 2 : { State.MemCard.Sector = State.PSIO.DataIn * 0x0100; State.MemCard.Check = State.PSIO.DataIn; break; }
															case 3 : { State.MemCard.Sector += State.PSIO.DataIn; State.MemCard.Check ^= State.PSIO.DataIn; // break; }
																	   State.PSIO.Mode = mdWrData; State.MemCard.Bytes = 0; break; }
															default : { State.PSIO.Mode = mdDone; AckTime = 0; break; }
														}
														//  
														LED_RED_ON;
														// 
														break;
													}
										//  : S
										case 0x53 : {	//    
														switch ( State.MemCard.Bytes )
														{	//   
															case 0 : { State.PSIO.DataOut = 0x5D; break; }
															case 1 : { State.PSIO.DataOut = 0x5C; break; }
															case 2 : { State.PSIO.DataOut = 0x5D; break; }
															case 3 : { State.PSIO.DataOut = 0x04; break; }
															case 4 : { State.PSIO.DataOut = 0x00; break; }
															case 5 : { State.PSIO.DataOut = 0x00; break; }
															case 6 : { State.PSIO.DataOut = 0x80; break; }
															default : { State.PSIO.Mode = mdDone; AckTime = 0; break; }
														}
														// 
														break;
													}
										//  
										default : { State.PSIO.Mode = mdDone; break; }
									}
									//  
									if ( State.PSIO.Mode == mdParam ) { State.MemCard.Bytes++; }
									// 
									break;
								}
				//     
				case mdRdData : {	//     ACK
									AckTime = AckNormal;
									//  
									if ( State.MemCard.Bytes < 128 )
									{	//   
										State.PSIO.DataOut = State.MemCard.Data[ State.MemCard.Bytes ]; State.MemCard.Check ^= State.PSIO.DataOut;
									}
									else
									{	//     
										switch ( State.MemCard.Bytes )
										{	//   
											case 128 : { State.PSIO.DataOut = State.MemCard.Check; break; }
											//   
											case 129 : { State.PSIO.DataOut = 0x47; break; }
											//  
											default : { State.PSIO.Mode = mdDone; AckTime = 0; LED_GREEN_OFF; break; }
										}
									}
									// 
									State.MemCard.Bytes++;
									// 
									break;
								}
				//     
				case mdWrData : {	//     ACK
									AckTime = AckNormal;
									//  
									if ( State.MemCard.Bytes < 128 )
									{	//   
										State.MemCard.Data[ State.MemCard.Bytes ] = State.PSIO.DataIn; State.MemCard.Check ^= State.PSIO.DataIn;
									}
									else
									{	//     
										switch ( State.MemCard.Bytes )
										{	//    
											case 128 : {	//      
															if ( State.MemCard.Check == State.PSIO.DataIn ) { State.MemCard.Check = 0x47; } else { State.MemCard.Check = 0x4E; }
															//   
															State.PSIO.DataOut = 0x5C;
															// 
															break;
														}
											//   
											case 129 : { State.PSIO.DataOut = 0x5D; break; }
											//    
											case 130 : {	//  ,    
															if ( State.MemCard.Sector < 0x4000 )
															{	//  ,   
																State.PSIO.DataOut = State.MemCard.Check;
																//   ?
																if ( State.MemCard.Check == 0x47 )
																{	//      
																	State.SDCard.CardOp = coWrite;
																	//     
																	State.MemCard.Status &= ~StateNew;
																}
															}
															else
															{	//  ,   
																State.PSIO.DataOut = 0xFF;
															}
															// 
															break;
														}
											//  
											default : { State.PSIO.Mode = mdDone; AckTime = 0; break; }
										}
									}
									// 
									State.MemCard.Bytes++;
									// 
									break;
								}
				// ,    
				case mdDone : { break; }
				//   -   
				default : { State.PSIO.Mode = mdSync; break; }
			}
		}
	}
	//   
	if ( State.PSIO.Mode != mdSync )
	{	//     
		if ( State.PSIO.DataOut & 0x01 )
		{	//  1
			MEM_DAT1;
		}
		else
		{	//  0
			MEM_DAT0;
		}
		//  
		State.PSIO.DataOut >>= 1;
	}
	//  ACK?
	if ( AckTime > 0 )
	{	//  CNT
		TIM3->CNT = AckTime;
		//  
		State.PSIO.Ack = DISABLE;
		//  
		TIM3->SR = 0x0000;
		//  
		TIM3->CR1 |= TIM_CR1_CEN;
	}
}
      
      





TIM3タむマヌは、ACKの生成を担圓し たす。これは、この遅延䞭にカヌネルがSDカヌドを自由に䜿甚できるようにするために必芁です。タむマヌ割り蟌みハンドラヌは次のようになりたす。

TIM3_IRQHandler
//   TIM3
void TIM3_IRQHandler( void )
{	//  
	TIM3->SR = 0x0000;
	//  
	if ( State.PSIO.Ack == ENABLE )
	{	//   ACK
		MEM_nACK;
	}
	else
	{	//   ACK
		MEM_ACK;
		//  
		State.PSIO.Ack = ENABLE;
		//  
		TIM3->CNT = 0;
		//  
		TIM3->CR1 |= TIM_CR1_CEN;
	}
}

      
      







コヌドは十分にコメントされおおり、倚くの分析は必芁ないず思いたす。読み取りコマンドでセクタヌ番号の2番目のバむトを受信した埌、main関数の氞遠のルヌプでスピンするコヌドのSDカヌドからの読み取り操䜜のフラグを蚭定したこずに泚意しおください。そしおその盎埌に、次の4぀の ACKが長時間発行されたす。むンタヌフェむスでは、次のようになりたす。





ロゞックアナラむザプログラムのスクリヌンショット、トランザクションの4぀の倧きな遅延が匷調衚瀺されおいたす



合蚈で玄3.5msが入力されたすが、これはメむンコヌドのアルゎリズムがセクタヌを読み取るのに十分です。さらに、このコヌドは、䞭断がない堎合にのみ機胜したす。ちょうどそれらの倧きな䞀時停止で。蚘録䞭、フラグは最埌に蚭定され、システムがメモリカヌドに蚘録の完了を蚱可するため、メむンコヌドは割り蟌みによる干枉なしに機胜したす。それでは、メむンルヌプのコヌドを芋おみたしょう。

メむン
	//  
	while ( 1 )
	{	//    
		if ( CARD_nCD == 0 )
		{	//  
			if ( State.SDCard.CardType == ctNone )
			{	//   
				LED_GREEN_ON; LED_RED_OFF;
				//    ,  
				State.SDCard.CardType = Card_Init();
				//  ?
				if ( State.SDCard.CardType != ctUnknown )
				{	//    
					if ( Card_FSInit( &State.SDCard, &CARD_IMAGE[ 0 ] ) == ENABLE )
					{	//   ,  
						EXTI->IMR |= 0x00000004;
						//  
						LED_GREEN_OFF; LED_RED_OFF;
					}
					else
					{	//    
						State.SDCard.CardType = ctUnknown;
						//   
						LED_GREEN_ON; LED_RED_ON;
					}
				}
				else
				{	//   
					LED_GREEN_ON; LED_RED_ON;
				}
			}
		}
		else
		{	//  
			if ( State.SDCard.CardType != ctNone )
			{	//  ,  PSIO
				EXTI->IMR &= 0xFFFFFFFA;
				//   
				State.PSIO.Mode = mdSync; State.PSIO.Bits = 0; State.PSIO.DataIn = 0x00; State.PSIO.DataOut = 0; State.PSIO.Ack = DISABLE;
				State.MemCard.Status = StateNew;
				State.SDCard.CardType = ctNone; State.SDCard.CardOp = coIdle; State.SDCard.LoadedLBA = 0xFFFFFFFF;
			}
			//   
			LED_GREEN_OFF; LED_RED_OFF;
		}
		//   
		if ( (State.SDCard.CardType != ctNone) && (State.SDCard.CardType != ctUnknown) )
		{	//  ?
			if ( State.SDCard.CardOp == coWrite )
			{	//       
				Ofs = State.MemCard.Sector & 0x03FF;
				LBA = (Ofs >> 2) & 0x000000FF;
				Ofs = (Ofs << 7) & 0x00000180;
				//    
				Card_Read( State.SDCard.CardType, &State.SDCard.CardBuf[ 0 ], &State.SDCard.LoadedLBA, State.SDCard.CardList[ LBA ] );
				//   
				for (Cnt = 0;Cnt < 128;Cnt++)
				{	//  
					State.SDCard.CardBuf[ Ofs + Cnt ] = State.MemCard.Data[ Cnt ];
				}
				//   
				Card_Write( State.SDCard.CardType, &State.SDCard.CardBuf[ 0 ], &State.SDCard.LoadedLBA, State.SDCard.CardList[ LBA ] );
				//  
				LED_RED_OFF;
				//  
				State.SDCard.CardOp = coIdle;
			}
			//  ?
			if ( State.SDCard.CardOp == coRead )
			{	//       
				Ofs = State.MemCard.Sector & 0x03FF;
				LBA = (Ofs >> 2) & 0x000000FF;
				Ofs = (Ofs << 7) & 0x00000180;
				//    
				Card_Read( State.SDCard.CardType, &State.SDCard.CardBuf[ 0 ], &State.SDCard.LoadedLBA, State.SDCard.CardList[ LBA ] );
				//   
				for (Cnt = 0;Cnt < 128;Cnt++)
				{	//  
					State.MemCard.Data[ Cnt ] = State.SDCard.CardBuf[ Ofs + Cnt ];
				}
				//  
				State.SDCard.CardOp = coIdle;
			}
		}
	}
      
      





゚タヌナルルヌプでは、SDカヌド挿入信号が垞に分析されたす。倖出先でそれを匕き出すず、コヌドはPSIOを無効にし、PS1はカヌドを「倱いたす」。カヌドが元に戻された堎合たたはカヌドが挿入された状態で電源がオンになった堎合、最初にCard_Init関数を䜿甚しおカヌドを初期化しようずしたす。これにより、怜出されたカヌドのタむプが返されたす。 SDv1ず他のSDHC / SDXCアドレッシング方匏は異なるため、これは重芁です。初期化コヌド自䜓には秘密がなく、FatFSや同様のプロゞェクトに぀いおむンタヌネット䞊で入手可胜な倚数の䟋でスパむされたした。



カヌドの初期化に続いお、トリッキヌな関数Card_FSInitが呌び出されたす。これがこのプロゞェクトの最も重芁な機胜です。事実、STM32F042は機胜が控えめであり、必芁な速床で完党なFatFSサポヌトを匕き出すこずはできたせん。したがっお、私はこの方法を思い぀きたした。画像ファむルは垞に128KiBであるため、512バむトの256セクタヌのみを知る必芁があり、各セクタヌにはPS1メモリカヌドの正確に4セクタヌが含たれたす。したがっお、次のこずを行いたす。



  1. MBRのセクタヌLBA =0を分析したす。これが実際にMBRである堎合、MBSが配眮されおいる新しいセクタヌを取埗したす。
  2. 想定されるMBSのアドレスMBRがない堎合は0、MBRがある堎合はいく぀かの番号を受け取ったら、FAT12、FAT16、FAT32、たたはvFATのいずれかに属するかどうかの分析を開始したす。 。
  3. セクタヌがチェックに合栌した堎合、そのセクタヌから構造に関する情報を取埗し、ルヌトディレクトリでファむル名を持぀芁玠を探したす。この堎合は「MEMCRD00.BIN」です。
  4. そのようなファむルが芋぀かった堎合は、そのサむズを確認したす。0x20000バむトに厳密に固定する必芁がありたす。すべおがそうであれば、最初のクラスタヌの番号を取埗したす。
  5. このポむントに到達した堎合、むメヌゞが配眮されおいる物理LBAセクタヌのリストを䜜成するために必芁なすべおの情報がすでにありたす。FATチェヌンを通過し、MBSからの構造情報を䜿甚しお、256のLBAセクタヌ番号のテヌブルに入力したす。


成功するず、PSIOが起動し、PS1はそのカヌドを通垞の15ブロックカヌドず芋なしたす。いずれかの段階で゚ラヌが発生するず、動䜜が䞭断され、䞡方のLEDが点灯し、電源が切断されるかSDカヌドが亀換されるたですべおがこの状態のたたになりたす。この手順のコヌドは次のずおりです。



Card_FSInit
//      ,    FAT16
FunctionalState Card_FSInit( TSDCard *SDCard, const uint8_t *FName )
{	//  
	FunctionalState Res;
	uint8_t *Buf;
	uint8_t Pos;
	uint16_t ClustSize,Reserv,RootSize,FATSize,Cluster;
	uint32_t Cnt,LBA,SysOrg,FATOrg,RootOrg,DataOrg;
	int Compare;
	// 
	Res = DISABLE; SysOrg = 0; Cluster = 0xFFFF;
	//    
	while ( 1 )
	{	//   0
		if ( Card_Read( SDCard->CardType, &SDCard->CardBuf[ 0 ], &SDCard->LoadedLBA, SysOrg ) == DISABLE ) { break; }
		//   #0  MBR
		if ( *((uint16_t *)&SDCard->CardBuf[ 0x01FE ]) != 0xAA55 ) { break; }
		//    MBR
		if ( ((SDCard->CardBuf[ 0x01BE ] == 0x00) || (SDCard->CardBuf[ 0x01BE ] == 0x80)) &&
			 ((SDCard->CardBuf[ 0x01CE ] == 0x00) || (SDCard->CardBuf[ 0x01CE ] == 0x80)) &&
			 ((SDCard->CardBuf[ 0x01DE ] == 0x00) || (SDCard->CardBuf[ 0x01DE ] == 0x80)) &&
			 ((SDCard->CardBuf[ 0x01EE ] == 0x00) || (SDCard->CardBuf[ 0x01EE ] == 0x80)) )
		{	//   MBR,   
			for (Cnt = 0;Cnt < 4;Cnt++)
			{	//   
				if ( (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C2 ] == 0x01) ||	//  0x01: FAT12
					 (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C2 ] == 0x04) ||	//  0x04: FAT16
					 (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C2 ] == 0x06) ||	//  0x06: Big FAT16
					 (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C2 ] == 0x0E) )		//  0x0E: vFAT
				{	//  ,   MBS 
					SysOrg = SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C6 ];
					SysOrg += (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C7 ] * 0x00000100);
					SysOrg += (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C8 ] * 0x00010000);
					SysOrg += (SDCard->CardBuf[ (Cnt * 0x0010) + 0x01C9 ] * 0x01000000);
					// 
					break;
				}
			}
		}
		//    MBS
		if ( Card_Read( SDCard->CardType, &SDCard->CardBuf[ 0 ], &SDCard->LoadedLBA, SysOrg ) == DISABLE ) { break; }
		//    MBS
		if ( *((uint16_t *)&SDCard->CardBuf[ 0x01FE ]) != 0xAA55 ) { break; }
		if ( SDCard->CardBuf[ 0x000D ] == 0x00 ) { break; }
		if ( (SDCard->CardBuf[ 0x0010 ] == 0x00) || (SDCard->CardBuf[ 0x0010 ] > 0x02) ) { break; }
		if ( SDCard->CardBuf[ 0x0015 ] != 0xF8 ) { break; }
		if ( *((uint32_t *)&SDCard->CardBuf[ 0x001C ]) != SysOrg ) { break; }
		if ( SDCard->CardBuf[ 0x0026 ] != 0x29 ) { break; }
		if ( *((uint16_t *)&SDCard->CardBuf[ 0x0036 ]) != 0x4146 ) { break; }
		if ( *((uint16_t *)&SDCard->CardBuf[ 0x0038 ]) != 0x3154 ) { break; }
		if ( SDCard->CardBuf[ 0x003A ] != 0x36 ) { break; }
		//   ,    
		ClustSize = SDCard->CardBuf[ 0x000D ];
		Reserv = *((uint16_t *)&SDCard->CardBuf[ 0x000E ]);
		RootSize = (SDCard->CardBuf[ 0x0012 ] * 0x0100) + SDCard->CardBuf[ 0x0011 ];
		FATSize = *((uint16_t *)&SDCard->CardBuf[ 0x0016 ]);
		//   FAT  ROOT
		FATOrg = SysOrg + Reserv;
		RootOrg = FATOrg + (FATSize * 2);
		DataOrg = RootOrg + (RootSize / 16 );
		//   ,       
		for (LBA = 0;LBA < (RootSize / 16);LBA++)
		{	//    
			if ( Card_Read( SDCard->CardType, &SDCard->CardBuf[ 0 ], &SDCard->LoadedLBA, RootOrg + LBA ) == ENABLE )
			{	//  16 ,     
				for (Cnt = 0;Cnt < 16;Cnt++)
				{	//  
					Compare = memcmp( &SDCard->CardBuf[ Cnt * 32 ], &CARD_IMAGE[ 0 ], 11 );
					if (  Compare == 0 )
					{	//  ,  
						if ( *((uint32_t *)&SDCard->CardBuf[ (Cnt * 32) + 0x001C ]) == 0x00020000 )
						{	//  ,   
							Cluster = *((uint16_t *)&SDCard->CardBuf[ (Cnt * 32) + 0x001A ]);
							//  
							Res = ENABLE;
							// 
							break;
						}
					}
				}
				//    -  
				if ( Res == ENABLE ) { break; }
			}
			else
			{	//   - 
				break;
			}
		}
		//  ,  ,    
		if ( Res == ENABLE )
		{	//     ,   
			Pos = 0;
			do
			{	//   
				if ( Cluster < 0x0002 )
				{	// , 
					Res = DISABLE; break;
				}
				//  LBA  
				LBA = DataOrg + ((Cluster - 2) * ClustSize);
				//        
				for (Cnt = 0;Cnt < ClustSize;Cnt++)
				{	//  LBA   
					SDCard->CardList[ Pos ] = LBA + Cnt;
					//  
					Pos++; if ( Pos == 0 ) { break; }
				}
				//    ,     
				//       ,         
				if ( Pos != 0 )
				{	//    
					LBA = FATOrg; Reserv = Cluster;
					while ( Reserv > 256 ) { LBA++; Reserv -= 256; }
					//     
					if ( Card_Read( SDCard->CardType, &SDCard->CardBuf[ 0 ], &SDCard->LoadedLBA, LBA ) == ENABLE )
					{	//    
						Cluster = *((uint16_t *)&SDCard->CardBuf[ Reserv * 2 ]);
					}
					else
					{	//  
						Res = DISABLE; break;
					}
				}
			} while ( (Cluster != 0xFFFF) && (Pos != 0) );
		}
		// 
		break;
	}
	// 
	return Res;
}
      
      





正盎なずころ、これは単なるPoCであるため、ここではFAT16怜玢のみが実装されおいたす。FAT12は、おそらくサポヌトする必芁はありたせん-そのような小さなボリュヌムのmicroSDは存圚したせん。ただし、将来誰かが必芁になった堎合は、FAT32たたはvFATを远加できたす。



画像の名前「MEMCRD00.BIN」は偶然に遞ばれたせんでした。事実、将来的には、マルチペヌゞメモリカヌドのゞョむパッドのボタンの暙準的な組み合わせを介しお画像遞択を远加する予定です。SELECTを抌したたたにするず、L1 / R1を1回抌したす。たた、最埌の2文字を倉曎するこずで、ルヌトディレクトリで「MEMCRD00.BIN」から「MEMCRD99.BIN」たでの100個の画像をサポヌトできたす。SCK割り蟌みハンドラには、このための基瀎がありたす。 PSIOむンタヌフェむスで、ゞョむパッドぞの呌び出しが分析されるブランチ。スニファを䜜成するのに問題はありたせんが、PS1コントロヌラの呚蟺機噚は豊富であり、それらのほずんどすべおをサポヌトする必芁がありたす。



その結果、デバむスは効率的であるこずが刀明し、必芁に応じお誰でもそれを繰り返すこずができたす。 プロゞェクト党䜓ぞのリンクはこちらです。蚘事ぞのコメントに興味のある方のお手䌝いをさせおいただきたす。



PSこのプロゞェクトの䜜成に䜿甚したすべおの情報源のリストをここに瀺したいず思いたすが、残念ながら、これは非垞に困難です。偶然に倚くのこずが耳にされたした。独自の゚ミュレヌタヌを䜜成したい人のために、15幎以䞊前のPS1に関する䞀般的な情報を含むTXTファむルの圢匏で䜕かが提䟛されたした。そしお今、それはすべお私のハヌドドラむブ䞊のいく぀かのテキストファむルずしお存圚したす。過去15幎間、むンタヌネット党䜓が情報源であったず蚀えたす。



All Articles