ブートケーブルUSB(ブートUSB)テクニカル情報


ブートUSBの仕組み

ブートUSBには16KbyteのフラッシュROMを内蔵した高速(24MIPS)CPUを使用しています。8Kbyteをブートケーブルのファームウエアが使用して、残りの8Kbyteをユーザ領域として使用できるようにしています。

ユーザ領域にはGBAで起動できるプログラムを格納します。ここに格納したプログラムはGBAブート直後のボタンの状態によって起動されるプログラムが決定されます。
現状はジョイスティックプログラムとブートチッププログラムが格納されておりそれぞれRボタンとLボタンに割り当てられています。この2つのプログラムは両方合わせても1Kbyte程度です。これではちょっともったいないので現在はグラフィックのロゴを入れてお茶を濁しています。

将来新たな使い方が開発されれば、別のボタンを割り当て新たなプログラムをフラッシュに書きこみます。(もちろんユーザのPCを使って書き込めますので、ケーブルを送り返していただく必要などはありません。)

ブート時に何もボタンが押されないと、ブートUSBはPCからのプログラムを待ちます。
PCはブートUSBに転送したいプログラムの長さを伝え、その後プログラム本体を送ります。
全てのプログラムの転送が終わるとGBAはそのプログラムの実行を開始します。
実行を開始したプログラムは引き続きGBAの通信ポートを使用してPCと通信することが出来ます。

devmanやbtconsなどのコントロールソフトはGBAにfwlibと呼ばれるGBAのカセットをコントロールするプログラムをダウンロードして実行させます。そしてユーザがROMの吸出しボタンを押すと、通信ポートを介してfwlibにROMの読み込みコマンド(CMD_READ)が発行されます。このコマンドを受け取ったfwlibは指定されたアドレスのROMデータを通信ポートに送り出します。これがブートUSBの基本的な動作パターンです。

USBコマンド

ブートUSBとPCの間は3本のパイプによって接続されています。USBの専門用語になるのですがパイプとはデータの流れるパイプ(管)のようなものです。この3本のパイプを使用してPCはブートUSBを制御しています。

パイプ0(cmd_h)はPCからブートUSBにコマンドを送るために使用します。
パイプ1(i_h)はブートUSBからPCにデータを送るために使用します。
パイプ2(o_h)はPCからブートUSBにデータを送るために使用します。

  if(usb_open()) {
    printf("Error can't found BootCable USB !!.\n");
    exit(-1);
  }
  cmd_h = usb_pipe_open(0);
  usb_pipe_reset(cmd_h);
  o_h = usb_pipe_open(2);
  usb_pipe_reset(o_h);
  i_h = usb_pipe_open(1);
  usb_pipe_reset(i_h);

ケーブルと通信を行う手順は上記のようになります。
usb_open()関数でケーブルとのコネクションを確立して、
データをやり取りするための3本のパイプをオープンします。

パイプとのデータのやり取りはWindowsのAPIのReadFile()/WriteFile()を使用します。

ケーブルとPCは下記のコマンドを使用します
(詳しい使用方法はbtcons_usbのソースコードを参照してください)
コマンド パラメータ 解説
USB_STATUS 0-16:マジックコードリード
129-144:マジックコードライト

マジックコード
0: ケーブルステータス(リードのみ)
  bit0:GBA power
  bit1:Ready to download
  bit2:GBA sio ready
1: GBAがOFFで0クリア
2-16: ケーブルがOFFで0クリア
GBAの状態取得や、マジックナンバーの読み出し書き込み

マジックナンバーは変数のようなもので特定の値をケーブルに格納してアプリケーションで使用する
USB_CMD パラメータ数
パラメータデータ0
パラメータデータ1
..
GBAにダウンロードしたプログラム(fwlibなど)に対してコマンドを発行する

パイプ0(cmd_h)を使用してコマンドとパラメータをGBAに渡す
USB_READ GBAから読み込むワード(4byte)数 GBAからデータを読み込む
読み込む経路はパイプ1(i_h)を使用する
USB_WRITE GBAに書き込むワード(4byte)数 GBAにデータを書き込む
書き込む経路はパイプ2(o_h)を使用する

<例>
void bt_cmd(u32 c,u32 p0,u32 p1,u32 p2,u32 *ret,u32 n){
 u8 cmd[18];
 u32 size;

 cmd[0]=USB_CMD;
 cmd[1]=4;
 *((u32 *)(cmd+2))=c;
 *((u32 *)(cmd+6))=p0;
 *((u32 *)(cmd+10))=p1;
 *((u32 *)(cmd+14))=p2;
 WriteFile(cmd_h, cmd, 18, (LPDWORD)&size, NULL);
 if(n){
   cmd[0]=USB_READ;
   cmd[1]=n;
   cmd[2]=(n>>8);
   WriteFile(cmd_h, cmd, 3, (LPDWORD)&size, NULL);
   ReadFile(i_h,ret,4*n,(LPDWORD)&size,NULL);
 }
}

fwlibを使用する場合の流れは
 ・USB_CMDでコマンドを発行
 ・USB_READ(USB_WRITE)でデータを読み込む(書き込む)
というようになります。

プログラムのダウンロード

GBAにプログラムをダウンロードする方法は2通りあります。

一つはGBAのブート直後に目的のプログラムを転送する方法。
もう一つはGBAのブート直後にはfwlibのようなファームウエアを転送して、ファームウエアのコマンドでプログラムをダウンロードさせる方法です。

ここではGBAのブート直後にプログラムを転送する方法を説明します。

1) GBAの電源投入のダウンロード可能状態を待ちます。
   USB_STATUSコマンドを発行してGBAの状態を取得します。

2) GBAに転送するプログラムの長さを送ります。

3) プログラム本体を送ります。


<ソースコード>
{
 //GBAのブート完了を待つ
 for(;;){
  ret=bt_getmagic(0);
  Sleep(100);
  if((ret & 3)==3) break; //GBA ON
 }

 //PGMの長さを渡す
 cmd[0]=USB_WRITE;
 cmd[1]=1;
 cmd[2]=0;
 WriteFile(cmd_h, cmd, 3, (LPDWORD)&size, NULL);
 Sleep(50);
 WriteFile(o_h, (LPCVOID)&len, 4, (LPDWORD)&size, NULL);

 //PGM本体を渡す
 cmd[0]=USB_WRITE;
 cmd[1]=(len+3)/4;
 cmd[2]=((len+3)/4)>>8;
 WriteFile(cmd_h, cmd, 3, (LPDWORD)&size, NULL);
 WriteFile(o_h, buf, (len+3)/4*4, (LPDWORD)&size, NULL);
 Sleep(100); //wait GBA is up

 //fwlibに対するコマンド発行
 bt_cmd(CMD_ROM_PROBE,0,0,0,buf,3);
 rom_type=buf[0];
 rom_size=buf[1];
 rom_blksize=buf[2];
}

u32 bt_getmagic(u8 n){
 u32 ret,size;

 cmd[0]=USB_STATUS;
 cmd[1]=n;
 WriteFile(cmd_h, cmd, 2, (LPDWORD)&size, NULL);
 ReadFile(i_h,&ret,4,(LPDWORD)&size,NULL);
 return(ret);
}


FWLIBのコマンド

fwlibに実装されているコマンドです。devman、btconsは下記のコマンドを使用してカセットをコントロールしています。詳しくはbtcons_usbのソースコードを参照してください。

コマンド パラメータ 解説
CMD_ROM_PROBE なし カセットROMの種別を検出する
CMD_BU_PROBE なし バックアップデバイスの種別を検出する
CMD_WRAM_LOADEXEC len GBAの内臓RAMにプログラムを転送して実行する
CMD_BOOT_ROM なし カセットROMからブートする
CMD_ROM_BERASE add ROM(Flash)をセクター単位で消去する
CMD_ROM_WRITE add
len
ROM(Flash)に書き込む
CMD_ROM_BWRITE add
len
ROM(Flash)に消去しながら書き込む
CMD_READ add
len
GBAのメモリー空間の読み出し
CMD_WRITE add
len
GBAのメモリー空間の書き込み
CMD_FIND add
len
strlen
カセットROMから指定した文字列(データ)の検索
CMD_SRAM_READ ofs
len
SRAM,FLASHなどのバックアップデバイスから読み込む(自動バンク切り替え)
CMD_SRAM_WRITE ofs
len
SRAMへの書き込み(自動バンク切り替え)
CMD_EEP_READ ofs
len
EEPの読み出し
CMD_EEP_WRITE ofs
len
EEPの書き込み
CMD_FLASH_WRITE ofs
len
FLASHの書き込み(自動バンク切り替え)
CMD_BLANK なし カセットROM(Flash)の空き容量確認(追加書き込み用)
CMD_IS_PRO なし FA-PRO以降のフラッシュカセットか調査する
PROカセットは32Kbyte単位にマルチを配置出来る

ホストインターフェイス機能

<特徴>

この機能はGBAのプログラムからPC(ホスト)に対してメッセージ(printf)を出力したり、PCのファイルをオープンしてGBAから書き込んだり・読み出したりする機能です。
この機能は主にデバッグや、開発ツール(例えばグラフィックビューア)などに使用することが出来ます。

<使用方法>

この機能を使用するにはbios_usb.zipに含まれるhio.cとhio.hが必要です。hio.cをデバッグ中のプログラムにリンクしてください。hio.hにはホストインターフェイスに必要な関数や、定数などの定義があります。
パラレルポート用のブートケーブルでも同様なホストインターフェイス(bios.zipに含まれます)は提供されていますが、パラレル版は通信割り込みを使用する設計になっているため、USB版のように単純にリンクするだけでは動きません。
関数名 機能 使用例
s32 PcPrintf(s8 *fmt, ...) PCにメッセージを表示する
書式として
%d 10進表示
%x 16進表示
%s 文字列表示
%c 文字表示
PcPrintf("X=%d\n",x);
s32 PcOpen(s8 *filename, s32 mode) PCのファイルをオープンする
modeに指定できるのは
PC_RDONLY 読み込み専用
PC_WRONLY 書き込み専用
PC_RDWR   読み書き
PC_APPEND 追加
PC_CREAT  ファイル作成
PC_TRUNC  長さを0に
s32 fh;
fh=PcOpen("bios.bin",
PC_CREAT|
PC_TRUNC|
PC_WRONLY);
s32 PcClose(s32 fh) PCのファイルをクローズする PcClose(fh);
s32 PcRead(s32 fh,void *buf,s32 len) PCのファイルをリードする
fh : オープンしたファイル
buf : 読み込むバッファ
len : 読み込む長さ

戻り値 : 実際に読み込んだ長さ
u8 buf[1024];
len=
PcRead(fh,buf,1024);
s32 PcWrite(s32 fh,void *buf,s32 len) PCのファイルをライトする
fh : オープンしたファイル
buf : 読み込むバッファ
len : 読み込む長さ

戻り値 : 実際に読み込んだ長さ
u8 buf[1024];
len=PcWrite(fh,
buf,1024);
s32 PcLseek(s32 fh,s32 ofs,s32 where) PCのファイルをシークする
fh : オープンしたファイル
ofs : シークするオフセット
where : シークの起点を下記から
PC_SEEK_SET ファイルの先頭から
PC_SEEK_CUR 現在のポイントから
PC_SEEK_END ファイルの最後から

戻り値 : シーク後のファイル位置
s32 flen;
flen=PcLseek(fh,
0, PC_SEEK_END)
詳しくはhio.hを参照してください