概要
ESP32のコプロセッサー用ULPアセンブリ言語の入門向けの情報をまとめました。
ULPとは?
ESP32に搭載されているコプロセッサーで、Ultra Low Powerの略です。非常にかんたんな命令しか利用できませんが、比較的単純なためマシン語の入門に適していると思います。
アセンブリ、アセンブラ、アセンブル?
本来CPUは数値の命令コードでのみ実行することが可能です。数値の命令コードをマシン語や機械語などと呼びます。さすがに数値のみだと理解が難しいので、数値に1対1で対応するプログラミング言語が作られ、アセンブリ言語と呼ばれています。
あとはラテン語とか英語とかの接尾語の変化で、アセンブリ言語をマシン語に変換するプログラムをアセンブラと呼び、変換する行為をアセンブルと呼びます。
後ろ2つは、コンパイラとコンパイルの対応と同じです。
n進法とは?
アセンブリ言語の説明に入る前に、数をどのように表記するのかを整理します。
10進数とは?
普段使っている数字は、10進数だと思います。10進数で56は、1の位が6、10の位が5で56と表記しています。
一番右側の位が1で、その左は10進数なので10、さらに左はさらに10倍して100の位になります。
各位に入っている数値を掛け算したものを、すべて足すことで合計がわかります。
60進数とは?
ちょっと、特殊な例ですが10進数以外に日常で使っているのが60進数になります。時間と分の関係が60進数です。日まで入れると60じゃないので、この2桁だけ考えてください。
一番右側の位が1で、その左が60進数なので60の位です。掛け算して足してあげることで、合計の分が計算できます。
この10進数と60進数の考え方が理解できましたでしょうか? まだよくわからない場合には、もう一度読み直してもらいたいです。理解しないまま次を読むとさらに混乱すると思います。
2進数とは?
説明のため4桁になっていますが、基本的な考えは一緒です。一番右の位は1で、その左は2進数なので2の位、その左はさらに2倍して4の位、その左はさらに2倍して8の位になります。
10進数の場合には、各位には0から9までの数字が入ります。10の場合には上の位に繰り上がりますからね。同じように60進数の場合には0から59までが入ります。
2進数の場合には0から1の数字が入ります。
n進数の場合には各位には0からn-1までの数字が入ります。こう説明すると一気に混乱するので、理解しなくても大丈夫です。ただこの説明から入る入門書が多く、2進数の理解を難しくしていると思います。
コンピューターの内部では、基本的に2進数で動いているのですが、2進数は欠点があり、非常に桁数が多くなります。
ESP32は32ビットコンピューターで、1ビットが2進数の1桁に相当しますので、32桁も必要になります。
10101010101010101011110001110001みたいな表記になっても、上記のルールで右から1,2,4,8,16,32,64,,,と計算していけばわかりますが、ちょっと多すぎてよくわからないですよね?
そこで人が見る数字には16進数を使います。
16進数とは?
いきなりAと書いてありますが、16進数の場合には0から15までの数字が各桁に入ります。できれば1文字で表記したいので、トランプみたいに10はAに置き換えています。
上にあるのが、対応表です。AからFまでの記号を使って表現しています。
2進数と16進数の対応ですが、2進数4桁が16進数1桁に対応しています。2進数で1111は8+4+2+1で15になります。16進数ですとFですね。
16進数2桁だと、2進数8桁の8ビットに相当します。8ビットが基本的な最低単位となっており、1バイトとよんだりします。
上に出ていた10101010101010101011110001110001の2進数を変換する場合には4桁に区切って、1010,1010,1010,1010,1011,1100,0111,0001この4桁を16進数に置換します。するとAAAABC71になります。
ここまでくると覚える必要はありません。基本的はWindowsの電卓などを利用して計算してください。
Windows10だと電卓の種類をプログラマーに変更すると、HEX(16進数)、DEC(10進数)、OCT(8進数)、BIN(2進数)がかんたんにわかります。
エクセルでもHEX2BIN()みたいな関数で変換が可能です。8進数は説明していませんでしたが、ファイルの管理権限などを管理するパーミッションで利用されています。16進数の半分なので、2進数3桁分をまとめたものになります。
ここまで書いておいてなんですが、最終的には16進数の数値から10進数の数値が変換できれば問題ありません。
BC71みたいな16進数があったら、電卓でBC71を入力し、10進数だと48241だとわかれば大丈夫です。
n進数のまとめ
10進数 | 2進数 | 16進数 |
0 | 0 | 0 |
1 | 1 | 1 |
2 | 10 | 2 |
3 | 11 | 3 |
4 | 100 | 4 |
5 | 101 | 5 |
6 | 110 | 6 |
7 | 111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | A |
11 | 1011 | B |
12 | 1100 | C |
13 | 1101 | D |
14 | 1110 | E |
15 | 1111 | F |
16 | 1 0000 | 10 |
255 | 1111 1111 | FF |
256 | 1 0000 0000 | 100 |
511 | 1 1111 1111 | 1FF |
512 | 10 0000 0000 | 200 |
1023 | 11 1111 1111 | 3FF |
1024 | 100 0000 0000 | 400 |
上記が対応です。1から16ぐらいまでは暗記していると便利です。
10進数 | 2進数 | 8進数 | 16進数 | |
Arduino表記 | 100 | 0b1100100 | 0144 | 0x64 |
printf書式 | %d | なし | %o | %x |
プログラム中の表記方法ですが、10進数はそのまま記述、2進数はバイナリを表す0bを先頭に追加。8進数は0を先頭に追加。16進数は0xを先頭に追加します。
昔は8進数をよく使っていたので、10進数と区別するために先頭に0を追加した名残だと思います。その後16進数を使うようになったので0xが追加されたのかな?
数字の0だけれど、8をあらわすオクトパスなどのOctに由来しているかもしれません。
printf書式については、電卓の表記と一緒で、10進数はDECの%d、8進数はOCTの%o、16進数はHEXの%xになります。xは大文字で書くと、abcdefの文字も大文字になります。2進数は標準だと出力できないみたいです。
プログラム中に2進数を使うことはほぼないですが、データシートなどではビットで操作する場合に0b00000001みたいな記述が出てきます。
単位について
ビット(bit)
2進数の1桁に相当します。0か1かのあるなしで、バイナリやbinなどのと表記されていることがあります。
バイト(Byte)
8bit=1Byteです。ビットとバイトは両方bからはじまるのでビットは小文字のb、バイトは大文字のBを使うことが多いですが、bだけだと判断できないことも多いです。
キロ(K)
1000g=1kgなどのときに使う記号です。一般的には1,000をあらわしますが、コンピューターでは1,024をあらわすことが多いです。
これは2進数の0b100 0000 0000が1024(0x40)に相当するためです。
パソコンの空き容量などの表示では1,024のKが使われます。しかしハードディスクなどのメディア販売時の容量は1,000のkが使われます。そのため1,000kByteのメディアを購入しても、パソコン上では976KByteと認識したりします。
多くの場合1,000の場合には小文字のk、1,024の場合には大文字のKが使われますが、Kだけだと判断できないことも多いです。
コンピューターだと通信速度は1,000のkですが、それ以外は1,024のKが多いはずです。
メガ(M)、ギガ(G)、テラ(T)、ペタ(P)
こちらも大きさを表すものです。
1000の場合 | 1024の場合 | |
1K | 1,000 | 1,024 |
1M | 1,000,000 | 1,048,576 |
1G | 1,000,000,000 | 1,073,741,824 |
1T | 1,000,000,000,000 | 1,099,511,627,776 |
1P | 1,000,000,000,000,000 | 1,125,899,906,842,620 |
上記の対応になります。基本的には3桁増えていくのですが、1,024の倍数だとTまで行くと差が1割近くあります。1TByteのハードディスクを買ってきても、パソコンが認識するのは0.9TByteぐらいになってしまいます。
携帯の通信容量のギガがなくなるってのは、上記のGです。通信系は1,000の単位系が多いので、1Gは1,000,000,000Byteになります。
このKが1,000なのか、1,024なのかはわかりにくいので注意してください。昔はMでも差が5%弱と少なかったのであまり気にしなかったのですが、Pだと12.5%も出てきますので、無視できない差です。
ミリ(m)、マイクロ(u)、ナノ(n)
こちらも大きさをあらわす記号です。
1m | 0.001 |
1u | 0.000001 |
1n | 0.000000001 |
こちらはすべて1000分の1をあらわします。コンピューターの場合には時間をあらわすときによく利用します。
1秒は1sec、もしくは1sと表記することが多く、0.001秒を1ms、0.000001秒を1us、0.000000001秒を1usなどと表記します。
プログラムではミリ秒ぐらいを利用することが多いですが、内部的にはマイクロ秒やナノ秒などの単位でコンピューターは動いています。
まとめ
ULPの解説をまったくしていませんが、基礎的なことから解説をはじめて、コンピュター内部でどのように動いているのかという、仕組みも一緒に解説できればと思っています。
コメント