macOSでSDカードにFAT16パーティションを切ってフォーマットする

# はじめに
最近、FAT16パーティションしか読めない[特殊な環境](https://github.com/Konamiman/Nextor)を使う必要があり、16GBのSDカードの先頭にパーティションを切ってFAT16フォーマットしました。しかし、これをmacOSでやろうとすると意外と手こずったので、記事としてまとめておきます。

# 事前知識
普通に考えると `diskutil` コマンドでサラッと実行できそうですが、できませんでした。

macOS では既存のパーティションを `newfs_msdos -F 16` で FAT16 フォーマットすることができます。ネットで検索すると、単純にこのコマンドを実行するだけで良い、という記事がいくつか見つかります。

しかし`newfs_msdos`コマンドはパーティションタイプを FAT16 に変更してくれません。なので、例えば FAT32 でパーティションが切られているディスクを `newfs_msdos -F 16` でフォーマットすると、パーティションタイプは FAT32 なのに中身は FAT16 という不整合な状態となります。これでも macOS からは見えるのですが、環境によっては読めないディスクになってしまいます。

では、新規に `diskutil partitionDisk` で FAT16 パーティションを作ればよいのですが、これはエラーが出てしまいます。
以下は /dev/disk2 の先頭 2GB だけ FAT16 パーティションに、残りを空き領域にしようとした例ですが、パーティションサイズのエラーで、失敗します。

```shell
% diskutil partitionDisk /dev/disk2 2 MBR "MS-DOS FAT16" NONAME 2G "Free Space" UNTITLED 0B 
Started partitioning on disk2
Unmounting disk
Creating the partition map
Waiting for partitions to activate
Formatting disk2s1 as MS-DOS (FAT16) with name NONAME
Error: -69850: The chosen size is not valid for the chosen file system
```

`diskutil eraseVolume` や `diskutil eraseDisk` でも同様のエラーで失敗します。 `diskutil` コマンドは FAT16 パーティションのサイズ指定が動作しないようです。

試行錯誤の末、一度 FAT32 でパーティションを作った後、 `fdisk` でコマンドでパーティション種別を FAT16 に変更することで対処できましたので、以下に方法をまとめておきます。

# FAT16パーティションを作ってフォーマットする手順
この手順は以下の前提で書きます。

- SDカードのマウント先が /dev/disk2
- ディスクの先頭 2GB だけを FAT16 パーティション、残りは空き容量にする
- パーティション形式は MBR

動作確認した環境は、 ｍacOS Big Sur (11.3) です。

## ステップ1. FAT32 でパーティションを作る
まず、diskutil を使って FAT32 パーティションを切ります。

```shell
% diskutil partitionDisk /dev/disk2 2 MBR "MS-DOS FAT32" NONAME 2G "Free Space" UNTITLED 0B
```

パーティションの作成後、ディスクが自動でマウントされてしまうので、/dev/disk2 のマウントを解除します。

```shell
% diskutil unmountDisk /dev/disk2
```

## ステップ2. fdisk でパーティションの種類を FAT16 に変更する

fdisk /dev/disk2 を実行して、 パーティションテーブルを表示します。テーブルの最初の行に表示された start と size の値をメモっておきます。

```shell
% sudo fdisk /dev/disk2
Disk: /dev/disk2	geometry: 1890/255/63 [30375936 sectors]
Signature: 0xAA55
         Starting       Ending
 #: id  cyl  hd sec -  cyl  hd sec [     start -       size]
------------------------------------------------------------------------
 1: 0B 1023 254  63 - 1023 254  63 [      2048 -    3906256] Win95 FAT-32
 2: 00    0   0   0 -    0   0   0 [         0 -          0] unused      
 3: 00    0   0   0 -    0   0   0 [         0 -          0] unused      
 4: 00    0   0   0 -    0   0   0 [         0 -          0] unused 
```

fdisk を起動して、/dev/disk2 のエディットモードに入ります。

```shell
% sudo fdisk -e /dev/disk2
fdisk: 1>
```

対話型プロンプトに、`edit 1` と入力すると、編集モードに入るので、

- Partition id に 6
  - 前の手順でパーティションのサイズを32MB以下にしていた場合は 4 にします。
- CHS mode は n
- Partition Offset は 前の手順でメモった start の値
- Partition Size は 前の手順でメモした size の値

をそれぞれ入力します。以下が入力例です。

```shell
fdisk: 1> edit 1
         Starting       Ending
 #: id  cyl  hd sec -  cyl  hd sec [     start -       size]
------------------------------------------------------------------------
 1: 0B 1023 254  63 - 1023 254  63 [      2048 -    7812504] Win95 FAT-32
Partition id ('0' to disable)  [0 - FF]: [B] (? for help) 6
Do you wish to edit in CHS mode? [n] n
Partition offset [0 - 30375936]: [63] 2048
Partition size [1 - 30373888]: [30373888] 7812504
fdisk: *1>
```

入力が終わると上記のようにプロンプトに戻ってきます。`write` で変更結果を書き込み、`quit` します。

```shell
fdisk:*1> write
Writing MBR at offset 0.
fdisk: 1> quit
% 
```

ディスクが自動でマウントされてしまうので、再度 /dev/disk2 のマウントを解除します。

```shell
% diskutil unmountDisk /dev/disk2
```

## ステップ3. パーティションを FAT16 フォーマットする

`newfs_msdos -F 16` コマンドを実行します。対象デバイスはディスク（/dev/disk2）ではなくボリューム（この記事の例では /dev/disk2s1）ですので注意してください。誤ってディスクを指定するとパーティションが破壊されます。

```shell
% sudo newfs_msdos -F 16 /dev/disk2s1
newfs_msdos: warning: /dev/disk2s1 is not a character device
512 bytes per physical sector
/dev/disk2s1: 3905728 sectors in 61027 FAT16 clusters (32768 bytes/cluster)
bps=512 spc=64 res=1 nft=2 rde=512 mid=0xf8 spf=239 spt=32 hds=255 hid=2048 drv=0x80 bsec=3906256
```

以上で、完了です。 `diskutil mountDisk /dev/disk2` するとパーティションがマウントされ、操作可能になります。

# 補足
`newfs_msdos` コマンドはデフォルトでは2GB以上のパーティションをフォーマットしても、フォーマット後の容量は2GBになってしまいます。

2GBを超えるパーティションをフォーマットする場合は、 -c オプションでクラスターに含まれるセクタ数を指定する必要があります。例えば パーティションサイズ4GBでセクタ長がデフォルトの 512bytes の場合 `newfs_msdos -F 16 -c 128` でフォーマットします。

# おわりに
もしかするともっと簡単なやり方があるかもしれせん。もしご存知の方がおられましたら教えて下さい。
手元ではSDカードでのみ確認しましたが、他のストレージデバイスでもこの方法でできそうに思います。