Twitterで蘇る懐かしい記憶。
実家の本棚探ってたら小学生の頃書いたZ80の機械語のノート出てきた。」 by @AoVAさん
そう、当時の中高校生が使いこなしていたZ80マシン語は小学生の憧れ、高速ゲームを夢見て、フリーズと戦った!
※フリーズ=暴走、リセットするしかない状態、マシン語を使うとよく起きる

ということで、Armマシン語入門、その6はちょっと実用的なプログラムをTIPSと共に紹介します。

キャラクターパターンデータを使って、1文字を8x8のテキストキャラクターを使った8倍サイズにするBASICのプログラムはこんな感じ。

12 P="BIG SCRNTEST" 15 CLT:GOSUB 100 30 WAIT 120:?TICK():END 100 FOR I=0 TO 11 110 C=PEEK(S+I) 120 FOR J=0 TO 7 122 N=PEEK(C*8+J) 125 A=#900+I&3*8+(I>>2*8+J)*32 130 FOR K=0 TO 7 140 POKE A+K,N&(1<<(7-K))<>0 150 NEXT 160 NEXT 170 NEXT 180 RETURN

32x24の画面に、8x8の文字は12文字入ります。一文字ずつCで取り出すループ、文字コード*8から8byte分あるキャラクターパターンを取り出すループと、1ビットずつ表示するループの三重ループ構造でできます。描画に242tick(約4秒)もかかってしまうので、ゲームに使うのは厳しそう・・・。

このようにマシン語でいきなり作り始める前に、BASICで考え方(アルゴリズム)が間違ってないか、実際に動かして確認するといいです。(2進数のビット演算には慣れておきましょう

では、早速マシン語で高速化します!
Armマシン語対応表(PDF版)」を片手に、BASICの変数の代わりにレジスタを使ってつくっていきます。この時、いきなり全部つくろうとせず、ループの内部の細かい部分から順に動作を確かめながらつくるのがコツです。問題は切り分ける!

1. 画面の左上にパラメータ(R0)として渡した数を表示しよう
2. 0ならCHR$(0)そうでなければCHR$(1)を表示しよう
3. 渡したパラメータの下位8bitでパターンを表示しよう(上位bitが左上になるように)
4. キャラクターコードを渡してキャラクターパターンを表示させよう
5. キャラクターコードが12コ連続してかかれたメモリ位置(=アドレス、C言語で言うポインタ)を渡して12コ表示しよう

つくりかたですが、いきなり2進数にしてしまうと読みづらいので、読みやすいプログラム(=アセンブリ言語)で先に書いて、変換(=ハンドアセンブル)するのがおすすめです。

'BIG SCREEN PROGRAM 'R0:param, address of 12 characters 'R1:virtual memory offset(RAM) 'R2:virtual memory offset(ROM) PUSH {LR,R4,R5,R6,R7} R12=R2 'save to R12 R6=0 'offset 0-11 R7=R1+R0 'real memory address of 12 characters R3=9 R3=R3<<8 R1=R1+R3 @LOOP0 R0=[R7+R6] R0=R0<<3 R2=R2+R0 R4=0 @LOOP1 R3=[R2+R4] R5=#80 @LOOP2 R0=R3 R0&=R5 IF 0 GOTO @ELSE1 R0=1 @ELSE1 [R1]=R0 R1+=1 R5=R5>>1 IF !0 GOTO @LOOP2 R1+=24 R4+=1 R4-8 IF !0 GOTO @LOOP1 R1-=24 R0=3 R0&=R6 R0-3 IF 0 GOTO @ELSE2 R1-=224 @ELSE2 R2=R12 R6+=1 R6-12 IF !0 GOTO @LOOP0 @END POP {PC,R4,R5,R6,R7}

レジスタはR0からR3とR12以外を使う場合は、呼び出し元(この場合、IchigoJamの動き)を壊してしまう恐れがあるので、PUSH/POPを使って退避しておきます。この時LR(リンクレジスタ、戻り先のアドレスが入っている)をまとめてPUSHしておき、POPするタイミングでPC(プログラムカウンタ、現在実行中のアドレス)に戻すと、RET(=#4770)の代わりになって便利です。

今回使用したレジスタは破壊してOKなR12を含めて、R7までに収まったのでPUSH/POPは一組で済みましたが、足りなくなった場合は、適宜PUSH/POPをループ前に使いましょう。その都度、レジスタの使いみちを簡単にメモしておくと便利です。


ハンドアセンブルは、IchigoJamの2進数表記とビット演算などを使って少し楽できます。

10 [0]=`1011010 1 11110000 11 [1]=#4600+`1 0010 100 12 [2]=`00100 110<<8+0 14 [3]=`0001100 000 001 111 16 [4]=`00100 011<<8+9 18 [5]=`00000 01000 011 011 20 [6]=`0001100 001 011 001 22 [7]=`0101110 110 111 000 30 [8]=`00000 00011 000 000 40 [9]=`0001100 000 010 010 60 [10]=`00100 100<<8+0 70 [11]=`0101110 100 010 011 80 [12]=`00100 101<<8+#80 90 [13]=`0100011000 011 000 100 [14]=`0100000000 101 000 102 [15]=`11010000<<8+(2-2)&#FF 104 [16]=`00100 000<<8+1 106 [17]=`01110 00000 001 000 110 [18]=`00110 001<<8+1 120 [19]=`00001 00001 101 101 130 [20]=`11010001<<8+(-7-2)&#FF 140 [21]=`00110 001<<8+24 150 [22]=`00110 100<<8+1 160 [23]=`00101 100<<8+8 170 [24]=`11010001<<8+(-13-2)&#FF 180 [25]=`00111 001<<8+24 190 [26]=`00100 000<<8+3 200 [27]=`0100000000 110 000 205 [28]=`00101 000<<8+3 210 [29]=`11010000<<8+(2-2)&#ff 220 [30]=`00111 001<<8+(224) 225 [31]=#4600+`0 1100 010 230 [32]=`00110 110<<8+1 240 [33]=`00101 110<<8+12 250 [34]=`11010001<<8+(-27-2)&#ff 260 [35]=`1011110 1 11110000 RUN A=USR(#800,"IchigoJamASM"):WAIT120

GOTOなどのジャンプ先は、命令の数を相対的に数えます。例えば2つ先なら2、2つ戻るなら-2。その数を-2した数を5桁または11桁の2進数に変換します(例、13コ前に飛ばす場合、(-13-2)&#FF)。命令を追加、変更、削除したときにアドレスを計算し直す必要があるので注意!

PUSHとPOPの数が合わない、ループから抜け出さないなど、どこか間違った場合、保護する機能はないので、高確率でフリーズします。実行前には保存しておきましょう。 フリーズしても、リセットか電源入れ直しですぐに復帰するのがIchigoJamのいいところ!(MSX時代は、暴走時の謎のディスクアクセスに怯えました)

動作が確認できたら、BASICのプログラムに簡単に使えるように、短いコードにしておくのも便利です。(LRUNでつなげてももちろんOKです)

?"1 POKE#700,";:FOR I=0 TO 31:?PEEK(#800+i);",";:NEXT:?CHR$(8) ?"2 POKE#720,";:FOR I=0 TO 36*2-1-32:?PEEK(#820+i);",";:NEXT:?CHR$(8)

アドレスをすべて相対的に使っているため配置変更可能(=リロケータブル)です。このように#800(配列)の領域から#700のPCG領域へ移しておくと、BASICで配列が使えて便利です。

BASICと速度を比べてみましょう。

1 POKE#700,240,181,148,70,0,38,15,24,9,35,27,2,89,24,184,93,192,0,18,24,0,36,19,93,128,37,24,70,40,64,0,208 2 POKE#720,1,32,8,112,1,49,109,8,247,209,24,49,1,52,8,44,241,209,24,57,3,32,48,64,3,40,0,208,224,57,98,70,1,54,12,46,227,209,240,189 10 FOR I=0 TO 12*4-1:POKE#800+I,RND(256):NEXT 12 P="BIG SCRNTEST" 15 FOR S=#800 TO #800+12*3:IF !BTN() GOSUB 100 ELSE A=USR(#700,S) 17 NEXT 20 S=P:IF !BTN() GOSUB 100 ELSE A=USR(#700,S) 22 WAIT 10 30 GOTO 15 100 FOR I=0 TO 11 110 C=PEEK(S+I) 120 FOR J=0 TO 7 122 N=PEEK(C*8+J) 125 A=#900+I&3*8+(I>>2*8+J)*32 130 FOR K=0 TO 7 140 POKE A+K,N&(1<<(7-K))<>0 150 NEXT 160 NEXT 170 NEXT 180 RETURN

ボタンでBASICとマシン語の速度差が体感できます!マシン語だと1tick(1/60秒)かかりません!
1行と2行だけ流用すると、他のプログラムでも大きな文字表示、使えます。

作るのに手間がかかるけど、動作は高速なマシン語。
作るのは簡単・高速だけど、動作は遅いBASIC。
いろいろなプログラミング言語がある理由はここにあります。

面倒なアドレス計算や表からのピックアップを自動化したものをアセンブラといいます。

また、数式など分かりやすいプログラミング言語から、マシン語の生成してくれるソフトのことをコンパイラといって、C言語はその代表。 IchigoJam BASICは、BASIC言語のコマンドに合わせて対応するマシン語を順に呼び出すインタープリタ。 Ruby、JavaScript、Swift、Javaなど最近の言語はバーチャルマシンといって仮想的なマシンのマシン語を使ったり、適宜コンパイルしたりと、両方のいいとこ取りをしています。

マシン語が分かったら、IchigoJam BASICで実際にアセンブラやコンパイラを作って、プログラミング言語の自作にも挑戦してみてください! Armマシン語は、Android/iPhoneでも使えます。他のマシン語も似たようなものなので、一つ使えるようになっておけば怖いものなし!(実際、小学生の頃のZ80マシン語の知識が活きました)

日本産の世界に広がるプログラミング言語、Rubyに続こう!

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

- Armマシン語表(PDF版)

IchigoJam BASICで扱える数は、メモリ節約のため16bitの整数(-32768〜32767)としていますが、CPUは32bitのLPC1114。マシン語を使えば、プラスマイナス約21億まで使った高い精度で高速に計算ができます。

その3では1byte単位のメモリアクセスをやりましたが、Armマシン語には、IchigoJamの変数をマシン語で扱うのに便利な2byte単位や、32bitの大きな数をそのまま読み書きできる4byte単位のメモリアクセスも用意されています。

Armマシン語表(メモリアクセス)

※[]の後のWだと2byte単位、Lだと4byte単位で読み書き。u5/2などの表記は2で割った数を指定という意味
※u5/u8:Wの場合2byte単位、Lの場合4byte単位となる

これを使って、IchigoJamの変数AとBを足した値をCにいれるマシン語プログラムをつくってみます。 (R1:IchigoJam BASIC上の仮想アドレスを実アドレスに変換するオフセット)

R0=8 R0=R0<<8 R0=R0+204 R0=R1+R0 '仮想メモリ #800+204 の変数Aの実アドレス位置を計算 R2=[R0+0]W '変数Aの16bitの数をR2へ入れる R3=[R0+2]W '変数Bの16bitの数をR3へ入れる R2=R2+R3 [R0+4]W=R2 'R2を変数Cへ入れる RET

これを「マシン語表」を見ながらハンドアセンブルして、実行します

[0]=`00100 000 00001000 'R0=8 [1]=`00000 01000 000 000 'R0=R0<<8 [2]=`00110 000<<8 + 204 'R0=R0+204 [3]=`0001100 000 001 000 'R0=R1+R0 '仮想メモリ #800+204 の変数Aの位置 [4]=`10001 00000 000 010 'R2=[R0+0]2 '変数Aの16bitの数をR2へ入れる [5]=`10001 00001 000 011 'R3=[R0+2]2 '変数Bの16bitの数をR3へ入れる [6]=`0001100 011 010 010 'R2=R2+R3 [7]=`10000 00010 000 010 '[R0+4]2=R2 'R2を変数Cへ入れる [8]=#4770 'RET =HEX$(`0100011101110000) A=100 B=555 R=USR(#800,0) ?C 655

うまくいきました!

C言語で作られたシステムから呼び出される場合、ArmのABI(Application Binary Interface = 関数呼び出し規約)に従い、元に戻さず破壊して使って良いレジスタはR0-R3までです。(日本語解説、32ビットへの誘い

ただ、R0からR3までの4つのレジスタだけではちょっと窮屈です。そんなとき便利なのがスタックです。メモリに書類を積むように一端保存しておき、必要になったら上から順番にレジスタに戻します。積むことをPUSH(プッシュ)、戻すことをPOP(ポップ)と呼びます。


Armマシン語表(スタック操作)

※ひとまず LR、PC には 0 を指定して使ってください

PUSH {R4,R5,R6,R7} と、スタックに保存しておけば、R4からR7までを思う存分に使ってもOK。RETする前に、POP {R4,R5,R6,R7} としましょう。 (R4〜R7を保存しないといけない理由

[0]=`1011010 0 00010000 'PUSH {R4} [1]=`00100 100 11111111 'R4=255 [2]=`0001100 100 000 000 'R0=R0+R4 [3]=`1011110 0 00010000 'POP {R4} [4]=#4770 'RET ?USR(#800,100) 355

全部PUSHしておいてもいいですが、1レジスタに付き4byteのメモリと約21ナノ秒(48MHzの1周期)の時間が無駄になるので、必要なだけPUSH/POPするようにしましょう。また、重ねてPUSHすることで、どんどん途中の数を保存しておくこともできます。ただし、PUSHした数とPOPした数が合わないと暴走やフリーズの原因になるので注意しましょう。

やってみよう
1. PUSHしてPOPせずにRETするとどうなるかやってみよう
2. A+B*Cを計算してDにいれるマシン語プログラムをつくってみよう
3. A+B*Cを計算して[0]にいれるマシン語プログラムが2度目には動かない原因を探ってみよう
4. A*Bの数をCとDの2つの変数に結果をいれるプログラムをつくってみよう
5. 変数X,Yの場所に4x4のキャラを表示するプログラムをつくり、BASICと速度を比べてみよう

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

やりたいことが遅くでできない時、速いCPUに変えるのは簡単ですが、その分コストアップするので製品化する際の値段に直接響きます。 IchigoJamではじめるArmマシン語、待望(?)のその4、公開です!
8ビットマイコン時代に逆戻り? アセンブリ言語が人気上昇 - ITmedia ニュース

1列に並んだLEDをすばやく振ると文字や絵が浮かび上がるものをバーサライターと呼びます(海外ではPOV=残像)。

4つのLEDをIchigoJamのOUT1〜OUT4につないで・・・

こんなプログラムを打ち込んで、振ってみましょう!(動画 on Vine

10 LET[0],`0110,`1001,`1001,`0110:N=4 20 FOR I=0 TO N:OUT[i]:OUT0:WAIT1:NEXT 30 WAIT 5 40 GOTO20

10行のデータとデータの数(N)を変更するとパターンを変えられます。

この1列のLEDをモーターでぐるぐる回転させて、その上に文字や絵を浮かび上がらせたい場合、BASICではちょっと速度が足りないかもしれません。 そんな時、CPUパワーをフルに使えるマシン語を使います!

IchigoJamのCPU、LPC1114の説明書(ユーザーマニュアル)を開きます。(LPCマイコン情報:LPC1100 (X)L Cortex-M0搭載-低消費電力内)
LPC1114FN28とFDH28のピン配置で、OUT1〜OUT4は、PIO1_0〜PIO1_3であることがわかります。

続いて、メモリーマッピングを見るとそのピンの操作はGPIO PIO1として、#50010000にマッピングされていることがわかります。

GPIOの項目を見ると、#50010000のアドレスから書き換えたいピンをbitに変更して2bitずらしたところを足したメモリに書き込めばいいことがわかります。

つまり、OUT1〜OUT4の状態を変更するプログラムを擬似的に表すと

[#50010000+`111100]=R0

となります。

ただ、32bitのアドレスを値で指定してメモリを直接書き込む命令はないため、前回のマシン語表を見て、分解します。

R3=#50 R3=R3<<8 R3=R3+1 R3=R3<<16 R3+=`00111100 [R3]L=R0 RET

これをマシン語に変換します。

R3=#50 `00100 011 01010000 R3=R3<<8 `00000 01000 011 011 R3=R3+1 `00110 011 00000001 R3=R3<<16 `00000 10000 011 011 R3+=`00111100 `00110 011 00111100 [R3]L=R0 `01100 00000 011 000 RET `0100011101110000

できたマシン語を打ち込みます。
16bitの2進法をIchigoJamのメモリ#700から順番に書く、簡易モニターを使って打ち込んでみます。

10 A=#700 20 ?"AD:";HEX$(A) 30 INPUT N 40 ?"VAL:";HEX$(N,4) 50 POKE A,N:A=A+1 60 POKE A,N>>8:A=A+1 70 GOTO 20

できたら、呼び出してみましょう

?USR(#700,1) OK ?USR(#700,15) OK ?USR(#700,0) OK

OKと出ずにフリーズしてしまったら、それは残念ながらどこか打ち間違えて、暴走してしまったことになります。 あきらめて、本体の電源を切ってやり直しましょう。

- やってみよう
マシン語でPOVをつくってみよう
LEDやOUT5,6を使ってもっと大きなPOVをつくってみよう
IN1の入力をマシン語で読み取って、モーターで回りながら上を認識させてみよう(ヒント、GPIO0を読みだすと入力になる)

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

Armマシン語第三弾、超高速計算を画面表示に活かしてみましょう!
※今回以降はIchigoJamのバージョン1.1を使用します(開発版はグループのファイルからダウンロード可能

コンピューターの記憶には2種類あります。
データを保存しておく本棚と、作業用の机をイメージしてください。
本棚から必要なデータを机に出して、机の上であれこれ計算してつくったデータをまた本棚に戻したりします。

前回紹介したレジスタが机に相当します。
レジスタは一度に使える量は限られます。(ひとまず、R0〜R3の4つのみを使ってください)

もうひとつの記憶、本棚に相当するのがメモリ(RAM)です。
こどもパソコンIchigoJamは、4KB(4キロバイト=4096byte 約4千文字分)のメモリがあります。
※私のパソコンのメモリ、4GB(4ギガバイト=4,294,967,296byte 42億文字入る!)と比べて、100万倍の1!

今回、マシン語表に新たにメモリアクセスを追記しました。
これがデータが詰まった本棚からとってきたり、書き換えたりするためのマシン語です。

値 = [メモリの場所] ... メモリから値を読み出します
[メモリの場所] = 値 ... メモリを書き換えます

IchigoJamは、メモリの一部を画面に表示用に使っているので、そこを書き換えると画面を書き換えることができます。

HELP MEM MAP #000 CHAR #700 PCG #800 VAR #900 VRAM #C00 LIST #1002 KEY

HELPコマンドでメモリの位置を確認することができます。
画面用のメモリは、VRAM(ブイラム、ビデオ用のラムということ)が#900からなので、こんなBASICのプログラムを書くと、キャラクター一覧が画面上部に順番に表示されます。

FOR I=#900 TO #9FF:POKE I,I:NEXT

つらつらと書いていく様子が目に見えますね。
では、これをマシン語にしてみましょう。

IchigoJam ver 1.1 から、R0に渡されたパラメータ、R1にBASICで使うメモリの位置の実メモリ位置、R2にキャラクターマップの実メモリ位置がセットされるようになりました。 今回はR1のみを使います。

R3=9
R3=R3<<8
[R1+R3]=R3
R3=R3+1
R2=R3>>8
R2-10
IF !0 GOTO -4
RET

これをハンドアセンブル(マシン語表からひとつずつ2進数列に変換すること)します。

00100 011 00001001 ← R3=9
00000 01000 011 011 ← R3=R3<<8
0101010 011 001 011 ← [R1+R3]=R3
00110 011 00000001 ← R3+=1
00001 01000 011 010 ← R2=R3>>8
00101 010 00001010 ← R2-10
11010001 11111010 ← IF !0 GOTO -4
0100011101110000 ← RET

マシン語表を見ながら順番に書いていくだけなので簡単ですね!
* R3+=1 とは R3=R3+1 という意味です。後述の表記では3bit(0〜7)までですが、+=を使うと8bit(0〜255)まで表記できます。

GOTO命令だけがちょっと特殊なので、注意が必要です。
4つ分命令を戻りたいときは、更に-2した-6を、2進数8bit補数表現にします。

?BIN$(-4-2,8) 11111010

このように、IchigoJamで計算すると便利です。

では、マシン語をメモリに書き込んで実行してみましょう。前回はPOKE文で上のメモリマップでいう#700、キャラクターパターンの領域に書き込んでいましたが、今回は配列の領域#800を使って楽します。 上位8bitと下位8bitに分けずに、16bitそのまま書き込めるので楽です。

10 [0]=`00100 011 00001001 20 [1]=`00000 01000 011 011 30 [2]=`0101010 011 001 011 40 [3]=`00110 011 00000001 50 [4]=`00001 01000 011 010 60 [5]=`00101 010 00001010 70 [6]=`11010001 11111010 80 [7]=`0100011101110000 90 U=USR(#800,0)

実行すると一瞬にして、画面に全キャラクターコードが表示されます!

配列領域を使っているので、配列を使ったプログラムと混ぜる時には要注意です!

?BIN$(PEEK(#800),8),BIN$(PEEK(#801),8)

このように、ちゃんと入れ替わって#800からのメモリに入っていることを確認してみましょう。
(下位から順にいれる方式をリトルエンディアン、上位から入れる方式をビッグエンディアンといいます、現在リトルエンディアンが主流!)

ミッション
- 1. 画面全部をキャラクターコードで埋めてみよう
- 2. USRコマンドで渡したキャラクターコード(R0)で画面全体を埋めてみよう
- 3. 画面を市松模様で埋めるマシン語プログラムを背景に、BASICのゲームをつくってみよう

IchigoJamで使えるマシン語表(基本+1byteメモリアクセス編)

※GOTOに指定する値は、飛ばしたい命令数の差分から更に-2した数を指定する

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

マシン語の体験が終わったので、高速に足し算するプログラムをつくってみましょう!

プログラムをひとつひとつマシン語対応表を見て、2進数列にして打ち込んでいくことをハンドアセンブルといいます。手(ハンド)で組み立てる(アセンブル)ので、ハンドアセンブル、てづくりですねっ!

IchigoJamのマシン語では、レジスタというBASICで使う変数のようなものが使えます(ひとまず安全に使えるのは、R0-R3の4つ)。レジスタに値をいれたり、レジスタ同士で計算して、その結果によって分岐(BASICでいうGOTO)することで、プログラムを組み立てます。

まずは、指定した数字までを足し算するマシン語プログラムをつくってみます。
BASICから渡される数は、レジスタR0に入っていて、RETした時に同じくレジスタR0に入っている数がUSRコマンドの値となります。

まずは実験 R0=R0+1 をハンドアセンブルしてみましょう。
下記の表から、演算の一番上 00110 が該当します。Rd = R0 なので、0を2進数3桁で表す 000、足す値は 1 を2進数8桁で表す 00000001
これを全部くっつけると
0011000000000001
という16bitのマシン語ができあがります。
マシン語プログラムからBASICへ戻る、分岐内にある RET 0100011101110000 を付け加えて、前回やったように上位8bitと下位8bitを入れ替えて書き込みます。

POKE #700,`00000001,`00110000,`01110000,`01000111 ?USR(#700,10) 11

では、繰り返し計算させてみます。

R1=0 R1=R1+R0 R0=R0-1 IF !0 GOTO -2 (計算結果が0でなければ2つ前の命令にジャンプ!) R0=R1 RET

これをハンドアセンブルすると...

00100 001 00000000 (R1=0) 0001100 001 000 001 (R1=R1+R0) 00111 000 00000001 (R0=R0-1, ※0001111 001 000 000 でもok) 11010001 11111100 (指定の-2から更に-2した-4の2進数表現256-4=252を使う) 0100011000 001 000 (R0=R1) 0100011101110000 (RET)

IchigoJamに書き込み、実行してみます。

POKE #700,`00000000,`00100001 POKE #702,`01000001,`00011000 POKE #704,`00000001,`00111000 POKE #706,`11111100,`11010001 POKE #708,`00001000,`01000110 POKE #70A,`01110000,`01000111 ?USR(#700,10) 55 ?USR(#700,100) 5050

動きました!

ミッション1. BASICで同じようなプログラムをつくって速度を比べてみましょう
ミッション2. 階乗(指定した値まで掛け算する 3の階乗=1*2*3)するマシン語プログラムをつくってみましょう

IchigoJamで使えるマシン語表(基本編)

※GOTOに指定する値は、飛ばしたい差分から更に-2した数を指定する
負の数は補数表現(256を足した数、-1 = 255、-2 = 254・・・)を使用する
命令2コ前に飛ばしたいとき252、3コ前の時251・・・

IchigoJamのCPU、NXP社のLPC1114FN28は、Arm社のCortex-M0をベースに作られています。Cortex-M0で動くマシン語が、16bitが基本のシンプルで美しいArm Thumb-2命令セットです。

多くのスマホやタブレットでもArmアーキテクチャーが使われていて、この命令セットと互換性があります。つまり、Armマシン語をマスターすれば、超高速動作するiPhoneアプリを創ることができちゃうかも!?

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

コンピューターが本当にわかる言葉は0か1のカタマリ(2進法)で表現されたマシン語だけです。

どのような2進法表現でどう動くかはコンピューターによって違います。

IchigoJamで使っているコンピューターは、16コの0か1のカタマリ(16bit/16ビット)で動きをコントロールします。

0011000000000001

これはR0レジスタというコンピューターの一時記憶の1番目に1を足すというマシン語です。

最初の5bitが足し算をしろという命令コード、次の3bitが使うレジスタの番号、後に続く8bitが足す数です。

00110 000 00000001

これをPOKE文を使ってメモリに書き込みます。(IchigoJam web でも試せます)

POKE#700,`00000001,`00110000

#700はメモリの位置を表します。`は2進数で数値を表します。
下位の8bitを先に、上位の8bitを後に書くのがこのコンピューターのルールです。

動かすにはもう1つ命令が必要です。IchigoJamからの呼び出しから戻って来いという命令です。 (BASICのGOSUBに対して、RETURN的なもの)

0100011101110000

この呼び出し元に戻るマシン語も同じようにメモリに書き込みます。

POKE#702,`01110000,`01000111

準備ができたので実行してみます。

?USR(#700,1)

結果に正しく2と返って来ましたか?
マシン語は間違えると、IchigoJamのシステム自体が停止してしまいます。
安全策が無い分、コンピューターのフルパワーが出せるのがマシン語の最大のメリットです!
長いプログラムをつくった時は、保存してから動かしましょう。

USRコマンドは呼び出しメモリ位置と、渡す数を指定します。100を渡して結果を確認してみてください。

?USR(#700,100)

メモリを書き換えて5足すプログラムに変更してみましょう。

POKE#700,5 ?USR(#700,100)

足す数を変更できました。

このような計算命令や、計算した結果によって処理を変更する命令などのマシン語でコンピューターは動いているわけです。


- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

- 参考リンク
命令一覧「Cortex-M0 テクニカルリファレンス マニュアル - Arm
マシン語の仕様「Armv6-M アーキテクチャ リファレンス マニュアル - Arm」 - 登録するとダウンロードできます
マシン語の仕様(日本語)「Armv5 Architecture Reference Manual Japanese - Arm」 - バージョン違いですが、日本語です
Arm Cortex-M0 1,500円 BASICパソコン 「IchigoJam
福野ツイート「プログラミングをC言語からはじめるのは、免許取っていきなりランボルギーニ乗るようなもの

Tweet
クリエイティブ・コモンズ・ライセンス
この作品は「Creative Commons — CC BY 4.0」の下に提供されています。
CC BY / @taisukef / アイコン画像 / プロフィール画像 / RSS