# シリアル通信 2つのゲームボーイ間の通信は、1バイトずつ行われます。 一方のゲームボーイが内部でクロック信号を生成することで、通信のタイミングを制御します。 SPIの用語では、クロックを発生させているゲームボーイをマスター(Master)と呼びます。 もう1台のゲームボーイは外部クロックを使用し(もう1台のゲームボーイからクロックを受け取る)、転送のタイミングをコントロールすることはできません。 もし、転送開始時に次のデータバイトのロードが完了していなければ、最後のデータバイトは再び消えてしまいます。また、次のバイトを送る準備ができていても、最後のバイトがまだ送信されていない場合は、待つしかありません。 ## FF01 - SB - 通信データレジスタ (R/W) 転送開始時には、このレジスタは転送するバイトを保持しておきます。転送中は、送っているバイトと受け取っているバイトが混ざっています。 サイクルごとに、左端のビットがシフトアウトして送信され、相手から入ってくるビットがシフトインされます。 ``` o7 o6 o5 o4 o3 o2 o1 o0 o6 o5 o4 o3 o2 o1 o0 i7 o5 o4 o3 o2 o1 o0 i7 i6 o4 o3 o2 o1 o0 i7 i6 i5 o3 o2 o1 o0 i7 i6 i5 i4 o2 o1 o0 i7 i6 i5 i4 i3 o1 o0 i7 i6 i5 i4 i3 i2 o0 i7 i6 i5 i4 i3 i2 i1 i7 i6 i5 i4 i3 i2 i1 i0 ``` ## FF02 - SC - 通信制御レジスタ (R/W) ``` Bit 0 シフトクロック 0=外部クロック, 1=内部クロック Bit 1 クロック速度(CGBのみ) 0=通常, 1=高速 Bit 7 転送開始フラグ 0=転送中でないor転送はリクエストされていない 1=転送中or転送がリクエストされている ``` マスター側のゲームボーイは送信するデータをSBにロードしてSCに`0x81`(`0b1000_0001`)をセットすることで、内部クロックを使った転送のリクエストを出します。 例えば次のコードは、内部クロックで`0x75`を相手に送る転送をリクエストします。 ```s ld a, $75 ld [$FF01], a # FF01 = SB ld a, $81 ld [$FF02], a # FF02 = SC ``` (1バイトの)転送が完了がしたことは次の2通りの方法で通知されます。 - SC.7がクリアされる(つまりSCが`0x01`になる) - シリアル通信の割り込みハンドラが呼び出される(つまり`0x0058`にジャンプする) スレーブ側のゲームボーイは、送信するデータをSBにロードし、任意でSC.7をセットすることができます。(つまり`SC=0x80`) ただしスレーブ側のSC.7に関係なく、**マスターが転送をしたいと思えば、そのときのSBを使って転送が行われる**ことになります。 外部クロックのゲームボーイは、転送終了時にシリアル通信の割り込みハンドラが呼び出され、SC.7をセットしていた場合はクリアされます。 **内部クロック** 非CGBモードでは、ゲームボーイは内部クロックとして8192Hzのみ利用可能です。これは遅延のためのオーバーヘッドを除けば、1秒間に約1KByteの転送が可能です CGBモードでは、SC.1とCGB倍速モードの有無により、4つの内部クロックレートが利用可能です。 クロック周波数 | 転送速度 | 条件 -----------|----------------|------------ 8192Hz | 1KB/s | bit1=0, 通常速 16384Hz | 2KB/s | bit1=0, 倍速 262144Hz | 32KB/s | bit1=1, 通常速 524288Hz | 64KB/s | bit1=1, 倍速 **外部クロック** 外部クロックを使う場合、通常、クロックは相手側のゲームボーイの内部クロックを使いますが、PCのパラレルポートに接続されている場合など、他のコンピュータから供給される場合もあります。よって外部クロックは理論上どんな値でも取ることが可能です。 古いモノクロのゲームボーイでも、最大500kHzの外部クロックを認識することが報告されています。そして、その逆にも制限はありません。外部クロックの速度が1ヶ月に1bitという非常に遅い速度であっても問題なく動作します。また、クロックが一定の間隔である必要もありません。 ## タイムアウト 外部クロックを使用する場合、最後のビットを受信するまで転送は完了しません。 相手側のゲームボーイからクロック信号が供給されていない場合、相手側のゲームボーイの電源が切れている場合、または相手側のゲームボーイとそもそも接続されていない場合、転送は完了しません。 このため、転送手順では、タイムアウトカウンタを使用し、タイムアウト時間内に応答がない場合は、通信を中止するようにします。 ## 遅延と同期 マスター側のゲームボーイは、スレーブ側のゲームボーイが次の転送の準備をするのに十分な時間を確保するために、各転送の後に常に小さな遅延を設ける必要があります。 つまり、内部クロックを持つゲームボーイが転送を開始する前に、外部クロックを持つゲームボーイが転送開始ビットをセットしている必要があります。 また、2つのゲームボーイ間で、同期を保証するために、転送されるバイトごとに内部クロックと外部クロックを切り替えることもできます。 転送は、マスターであるゲームボーイが転送開始フラグをセットしたときに、相手側のフラグの値に関わらず開始されます。このビットは、転送の終了時に(マスター/スレーブ側の両方とも)自動的に0になります。このビットを読むことで、転送がまだ実行中であるかどうかを判断することができます。 ## 注意 外部のゲームボーイが存在しない状態で、マスター側が内部クロックによるシリアル通信を行った場合、毎回bit`1`が受信されたことになり、最終的には`0xFF`を受け取って終了します。 ゲームボーイは[`Wake-on-LAN`](https://ja.wikipedia.org/wiki/Wake-on-LAN)に対応していません。つまり、外部クロックによるシリアル転送が完了しても、STOPモードは終了しません。