福野泰介の一日一創 - create every day

APIに対応し、オールマシン語でかいた「かわくだり」、APIをC言語環境から呼び出せる環境を整えたC言語用ヘッダファイル std15.h を作って、C言語版も作ってみました。 PCとUSBシリアルケーブルを手に入れれば、BASICではできないスゴイモノが簡単に作れます!(マシン語をハンドアセンブルすれば、PCがなくても同等以上のものは作れます)

#include <std15.h> __attribute__ ((section(".main"))) int main(int param, int ram, int rom, int (*divfunc)()) { cls(); int x = 15; for (;;) { locate(x, 5); putc('O'); locate(rnd(32), 23); putc('*'); putc(10); wait(3); int c = inkey(); if (c == LEFT) x--; if (c == RIGHT) x++; if (scr(x, 5)) break; } return x; }

IchigoJam BASIC に慣れた人なら読めますね!(IchigoJam BASIC版、Ruby版
#include というのは、コンパイラ向けの命令で、その後ろに書かれた std15.h というファイル名のファイルの中身をまるっと取り込んでくれます。 このファイルの中に main cls locate などが何なのかが書いてあります。(通常のC言語では stdio.h というファイル名をincludeしますね)

std15.h の中身はこのような感じ。IDを覚えていなくても、分かりやすい名前で呼び出せて、コンパイルする際、間違った使い方は警告してくれるなど、いたれりつくせり。

typedef unsigned int uint32_t; typedef unsigned short uint16_t; typedef uint32_t (*IJFUNC_P1R)(uint32_t n); #define rnd(n) ((IJFUNC_P1R)(void*)(uint32_t)*(uint16_t*)0xC0)((n))

生成されたコードサイズは、108byte、オールマシン語で作ったものが 90byte だったので20%ほどコード量が増えてしまってますが、十分に小さいのでOKですね。 使える容量は、#700-#7FFまでで256byte、変数を使わないことにすれば512byteまで。プログラム領域#C00以降にPOKEコマンドで書き込むように変更すると、1KB近くまで使えます。

ダウンロードはこちら(IchigoJam 1.3.2b12 以降用)
IchigoJam/c4ij: C language for IchigoJam」 - src on GitHub

マシン語を読みたいときは、 make dasm とすると、マシン語コードを表示します。コンパイラがどのようなマシン語にするか辿って、どこに無駄があるか読んでみるもまたおもしろいですよ。

某現役高専生による高専生のためのC言語連載が始まりました!
C言語 - Part.0:C言語とは - 某高専生の某高専生による高専生のための...
ただ、C言語、初めてプログラミングとしていきなり学ぶのはオススメしません。

まずは、IchigoJam BASIC、JavaScript、Python、Ruby、Unity、何でもいいので、サクッと簡単に楽しいゲームが作れることを体験しましょう。 便利なツールを使って、実現したいことを最短で実現し、もっとやってみたい!と興味を持つことが何より大事です。

コンピューターの気持ちが気になったら、コンピューターの構造(アーキテクチャ)、マシン語、C言語、現代言語の順に学ぶのがオススメです。

マシン語を触ってコンピューターの真の力を体感し、ちょっとしたプログラムを作るのに苦労してもらった上で、C言語のコンパイラに触れると、もう一度コンピューターってすごい!となれます。 もっと楽しく、もっと速く作りたい!という思いが、Python、Ruby、JavaScript、Go、Javaなどの言語を産みました。

なぜC言語を学ぶのか、それは、それらのモダン言語と言われている言語自体を作った言語がC言語だからです。

すごいゲームを作りたい!が、マシン語を学ぶ原点でした。
まず体験して、興味を持った状態での座学が効率的!


オールマシン語の「かわくだりゲーム」
アドレスを書き換えて、WAITの値を0にすると、超高速!
当然、即死するので、当たり判定を無効化して無敵モード!
マシン語なので、ESCは効きません。電源を入れ直して元通り!

OSとしてのIchigoJamのテスト版を経て、拡張用にAPIに対応した、IchigoJam 1.3.2b12 公開です。
ichigojam-1.3b12.zip
機能追加:SWITCHの第二パラメータで液晶の濃さを指定可能(0:デフォルト=14、1:最も薄い、63:最も濃い)

マシン語レベルで眺めてみれば、まだまだ見つかる最適化の余地。
また数百バイト単位で空いたので、もうちょっと拡張可能になった IchigoJam!
引き続き、ご要望、お待ちしています。

APIテーブル(β)

オールマシン語かわくだりの作り方。

APIはIDで2byteの値をメモリから読み出し、そのアドレスへGOSUBする。例えば、画面クリア(cls)を呼び出すには、このようにする

PUSH {R7,LR} 'cls R7=#CC R7=[R7]W GOSUB R7 POP {R7,PC}

ハンドアセンブルするか、asm15などでアセンブルして、BASICで実行してみましょう。

POKE#700,128,181,204,39,63,136,184,71,128,189 U=USR(#700,0)

画面が消えました!

locateとputcを使って、ネコを表示するには

PUSH {R4-R7,LR} R4=15 ' X 'locate X,5 R0=R4 R1=5 R7=#CE R7=[R7]W GOSUB R7 'putc neko R0=#EC R6=#C4 R6=[R6]W GOSUB R6 POP {R4-R7,PC}

で、OK。この調子でひとつひとつ作っていく感じは、BASICでもどんな言語でも一緒です。 マシン語はひとつひとつの命令の粒度が小さいので、プログラムが長くなり勝ちです。 コンパイラが作りたくなる気持ちがわかりますね。(コンパイラはじめのいっぽ))

できあがった、かわくだりゲームがこちら! (90byte)

PUSH {R4-R7,LR} 'cls R7=#CC R7=[R7]W GOSUB R7 R4=15 ' X @LOOP 'locate X,5 R0=R4 R1=5 R7=#CE R7=[R7]W GOSUB R7 'putc neko R0=#EC R6=#C4 R6=[R6]W GOSUB R6 'rnd(32) R0=32 R5=#C0 R5=[R5]W GOSUB R5 'locate R0,23 R1=23 GOSUB R7 'ptuc *, enter R0=#2A GOSUB R6 R0=10 GOSUB R6 'wait 3 R0=3 R7=#D6 R7=[R7]W GOSUB R7 'inkey() R5=#CA R5=[R5]W GOSUB R5 R0-28 IF !0 GOTO 2 R4-=1 R0-29 IF !0 GOTO 2 R4+=1 'scr(X,5) R0=R4 R1=5 R5=#D0 R5=[R5]W GOSUB R5 R0-0 IF 0 GOTO @LOOP POP {R4-R7,PC}

asm15でアセンブルして、動かしてみましょう。

10 POKE#700,240,181,204,39,63,136,184,71,15,36,32,70,5,33,206,39,63,136,184,71,236,32,196,38,54,136,176,71,32,32,192,37,45,136,168,71,23,33,184,71,42,32,176,71,10,32,176,71,3,32,214,39,63,136,184,71 20 POKE#738,202,37,45,136,168,71,28,40,0,209,1,60,29,40,0,209,1,52,32,70,5,33,208,37,45,136,168,71,0,40,216,208,240,189 30 U=USR(#700,0) RUN

オールマシン語です!

WAITの値を0にして、超高速!

POKE#730,0:U=USR(#700,0)

SCR呼び出しを R0=0 (0,32) に上書きして、当たり判定を無効化して、無敵モード。

POKE#752,0,32:U=USR(#700,0)

こちら、PC/Macで動く、C言語版かわくだりゲームも登場!

IchigoJam用のC言語環境も作ってみるのもいいかも!

links
- はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

高専の情報系の学科にはOSを学ぶ授業があります。自分で使って作って遊んでみるのが近道です。

IchigoJamへの要望に応えるため、一部はIchigoJam OSの外での実装を検討中。 その手段として、マシン語やmrubyなどからの呼び出しに便利なAPI提供を行う予定にしています。公開時はAPIテーブルとして固定のアドレスに置くことになりますが、先行してAPI直接呼び出しする実験方法を紹介します。
* IchigoJam 1.3.2b11専用なのでご注意ください(他のファームウェアだと停止します)


(IchigoJam as an OS: rnd API example)

ゲームでは欠かせないランダム(rnd)はこんな風に呼び出せます。

[0]=#4700:?USR(#800,#11B9) 8423

アドレス #11B8 に、rnd関数が入っていて、+1 した #11B9 でThumbマシン語として呼び出します。返り値はR0に格納されるので、マシン語はシンプルにGOTOするだけ。

GOTO R0

inkeyは#A15、clsは#196D、wait1は#219です。

パラメーターを渡すAPI、画面表示のputcは、こんな風に使います。(UARTのみのputcは#A51)

LET[0],#4900,#4708,#18BD,0:U=USR(#800,ASC("A"))

マシン語は、このように呼び出し先アドレスをR1にセットしています

R1=#18 R1=R1<<8 R1+=#BD GOTO R1

パラメータを一つ受け取るAPIとして、数を出力するputnum(#18E1)もおもしろいです。

LET[0],#4900,#4708,#18E1,0:U=USR(#800,12345) 12345

RNDを使った応用例として画面をランダムに埋め続けるプログラムを、ステップバイステップで解説します。

'R3=#900 R3=9 R3=R3<<8 R3=R3+R1 [R3]=R0 RET

asm15でアセンブルするか、マシン語表を使ってハンドアセンブル

POKE#700,9,35,27,2,91,24,24,112,112,71 ?USR(#700,65)

Aが画面左上(#900)に表示されます。USRの第二パラメータに表示したいキャラクターコード、レジスタR0として使えます。

rndを使って、ランダムなキャラを出します。

PUSH {LR,R4} 'R4=#900 R4=9 R4=R4<<8 R4=R4+R1 'R2=#11B9 R2=#11 R2=R2<<8 R2+=#B9 GOSUB R2 'RND [R4]=R0 POP {PC,R4}

関数内で更に関数を呼び出す場合、戻り先LRをPUSHで保存しておき、POPで戻しましょう。

POKE#700,16,181,9,36,36,2,100,24,17,34,18,2,185,50,144,71,32,112,16,189 ?USR(#700,0)

ランダムな場所に出します。

PUSH {LR,R4-R6} 'R6=#11B9 (RND) R6=#11 R6=R6<<8 R6+=#B9 'R4=#900 R4=9 R4=R4<<8 R4=R4+R1 GOSUB R6 'RND R1=#FF R0&=R1 R5=R4+R0 GOSUB R6 'RND [R5]=R0 POP {PC,R4-R6}

R3の割り算を使って、%(32*24)を実現して画面全体ランダム表示。
捨て身の無限ループなので、電源を切るまで停止することはできません。戻ってこないのでPUSHもなし。

'R6=#11B9 (RND) R6=#11 R6=R6<<8 R6+=#B9 'R7=32*24 R0=32 R7=24 R7*=R0 'R12=R3 (UDIV) R12=R3 'R4=#900 R4=9 R4=R4<<8 R4=R4+R1 @LOOP GOSUB R6 'RND R1=R7 GOSUB R12 'UDIV R0=R1 R5=R4+R0 GOSUB R6 'RND [R5]=R0 GOTO @LOOP


POKE#700,17,38,54,2,185,54,32,32,24,39,71,67,156,70,9,36,36,2,100,24,176,71,57,70,224,71,8,70,37,24,176,71,40,112,247,231 ?USR(#700,0)

正式実装をお楽しみに!

see also
連載、IchigoJamではじめる、Armマシン語入門

アルゴリズムの工夫や、マシン語化で速くなることを体験しました。 今回、それぞれ実測してみましょう。

まずはシンプルに引き算を続けるBASIC版から、CLTとTICK()を使って計測します。コンピューターくんへの課題は「10000/7」とします。

10 N=10000 20 M=7 25 CLT 30 A=0 40 N=N-M:IF N>=0 A=A+1:CONT 50 N=N+M 55 ?TICK() 60 ?A;"...";N RUN 370 1428...4

BASICでは369、TICK()は1秒間に60進むので、6.1秒です。(IchigoJam上で10倍して60で割ると小数1位まで計算できます。また、IchigoJamのバージョンによって数は変わります)

?3690/60 61

BASIC筆算版はどのくらい速いでしょう?

10 N=10000 20 M=7 25 CLT 30 A=0 40 FOR I=10 TO 0 STEP -1 50 L=M<<I 60 IF N>=L N=N-L:A=A+1<<I 70 NEXT 75 ?TICK() 80 ?A;"...";N RUN 5 1428...4

なんと、0.08秒(5/60秒)。73倍も速くなってます。アルゴリズムって大事ですね。

続いて、マシン語の単純ループ版。

POKE#700,7,33,2,70,0,32,1,48,82,26,252,218,2,56,81,24,112,71 OK CLT:U=USR(#700,10000):?TICK() 0

0、つまり1/60秒未満。この時点で単純ループ版でもBASIC筆算版を上回る速度がでていることがわかります。 ただ、0ではどのくらい速いかわからないので、ループを使います。

CLT:FORI=1TO1000:U=USR(#700,10000):NEXT:?TICK() 283

1000回で283、4.7秒でした。・・・が、待ってください。BASICの処理で時間がかかっているかもしれません。そこで何もせず帰ってくるだけのマシン語(=RET #4770)でも測ります。

[0]=#4770:CLT:FORI=1TO1000:U=USR(#800,10000):NEXT:?TICK() 264 ?283-264 19

やはり、ほとんどがBASICの処理時間でした。差分は19、0.31秒/1000回。1回あたり310マイクロ秒(=0.31ミリ秒)、BASIC版の2万倍も速いことがわかります。

次に、マシン語筆算版を計測してみます。

POKE#700,7,33,16,181,2,70,0,32,1,35,27,4,12,70,92,67,162,66,1,219,18,27,24,68,91,8,247,209,17,70,16,189 OK CLT:FORI=1TO1000:U=USR(#700,10000):NEXT:?TICK() 264 ?264-264 0

速すぎて、差分が0になってしまいました。ループ回数を10倍の10000回にして比較します。

[0]=#4770:CLT:FORI=1TO10000:U=USR(#800,10000):NEXT:?TICK() 2643 CLT:FORI=1TO10000:U=USR(#700,10000):NEXT:?TICK() 2648 ?2648-2643 5 ?5*1000/60 83

10000回で0.083秒。つまり1回あたり8.3マイクロ秒。ループ版から37倍、速くなりました。
BASIC筆算版の約1万倍速と、高速化具合は似たようなものですね。

まとめ
1. アルゴリズムによる速度差は環境を変えても変わらない(工夫の価値)
2. 遅いアルゴリズムでも言語や環境を変えるとなんとかなってしまう(力技)

いくらコンピューターが高速化しようとも、同じ環境上でいかにアルゴリズムの工夫で高速化できるかが、電気代やサーバー台数など、かかる費用に直結します。 また、同じ機能のアプリなら、サクサク快適に動く方がうれしいですよね? これが流行る流行らないの決定的な差になったりします。アルゴリズム、大事。

アルゴリズムなど理論ばかり見ていてもどう使えるのかイメージが沸かずつまらないし、プログラミング言語の文法だけ学んでもアルゴリズムが弱いとせっかくのパワーが活かせません。

IchigoJam BASICのスピードは、アルゴリズム変更が人間にとってリアルにコンピューターを体感できる速度域。入門時の学習がむしろ、手軽で速いはず。

アルゴリズムやコンピューターを学ぶコツをさえ身に着けたら、数億倍速い環境が待ってます。
世界中にある様々な課題、新しい問題解決手法(アルゴリズム)を創りましょう!
Enjoy to create own Algorithm with Computers!

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

IchigoJamのCPU、LPC1114 Arm Cortex-M0のマシン語には、割り算命令がありません。
参考:マシン語表Cortex-M3以降にはあります SDIV/UDIV)

無いものは作ればOK!(IchigoJamでのR3はここでは封印

N / M = A ... R(A:商、R:あまり、ただしNもMも正の整数とする)という割り算をする場合、割り算記号を使わずに作成できればOKです。何か、得意な言語で作ってみましょう。

例えば、IchigoJam BASICなら

10 INPUT N 20 INPUT M 30 A=0 40 N=N-M:IF N>=0 A=A+1:CONT 50 N=N+M 60 ?A;"...";N RUN ?300 ?7 42...6

割り算記号を使わない割り算、できました!

これをasm15マシン語にします(USRでは引数が1つなので、割る数R1に7と設定)

R1=7 R2=R0 R0=0 @LOOP R0+=1 R2=R2-R1 IF GE GOTO @LOOP R0-=1 R1=R2+R1 RET

アセンブルしたバイナリ(18byte、R1設定を除くと16byte)を使って、動かしてみましょう

POKE#700,7,33,2,70,0,32,1,48,82,26,252,218,1,56,81,24,112,71 ?USR(#700,300) 42

これで完成! ・・・としてもいいのですが、割られる数が大きくて、割る数が小さい時、何万回もループすることになって遅そうです。

例えば、30000/1を計算する場合、1ループ5cycle3万回で15万cycle。CPUの周波数48MHzの逆数、21nsecが1cycleにかかる時間なので、63万nsec = 630usec = 0.63ミリ秒かかります。 足し算引き算かけ算の15万倍も遅いとなると、高速処理が命のゲームで使うには厳しいこともありそうです。

そこで登場、アルゴリズム(問題解決手法)!

ひとまず簡単に思いつくところで、2進数を使った筆算アルゴリズムで高速化してみます。 割り算を手で計算するときに筆算を使うように、大きな桁からかけ算して引いてを繰り返して答を求めます。 幸い、2進数の筆算は0か1かしかないので、とってもシンプル。何か書きやすいプログラミング言語でアルゴリズムを確かめます。

10 INPUT N 20 INPUT M 30 A=0 40 FOR I=10 TO 0 STEP -1 50 L=M<<I 60 IF N>=L N=N-L:A=A+1<<I 70 NEXT 80 ?A;"...";N RUN ?300 ?7 42...6

(変数が16bit用に、IchigoJam BASIC桁溢れ防止のため10bitシフトからスタート)
前の手法に比べて速くなったことが体感できましたね?では、こちらをマシン語にします。

R1=7 PUSH {LR,R4} R2=R0 R0=0 R3=16 @LOOP R4=R1 R4<<=R3 R2-R4 IF LT GOTO @SKIP R2=R2-R4 R4=1 R4<<=R3 R0+=R4 @SKIP R3-=1 IF !MI GOTO @LOOP R1=R2 POP {PC,R4}

ループ回数が16回にまで劇的に減り、先の最悪の場合と比較し、約1000倍高速です!

ただし、その代償として、マシン語の量が32byteと16byte増えてました。これが多いか少ないかは状況次第です。 また、割る数と割られる数が近い場合は毎回150cycleかかる今回の手法と違って、単純ループの実装の方が速かったりします。 状況によって使い分けましょう。

OSとしてのIchigoJamとしては、汎用的に使われることを想定して、あまりに遅い除算は残念なので、第二案でいきたいです。 ただ容量は極力小さくしたいので、かけ算が1cycleで動くことを利用して、もうひと工夫縮めてみます。

R1=7 PUSH {LR,R4} R2=R0 R0=0 R3=1 R3=R3<<16 @LOOP R4=R1 R4*=R3 R2-R4 IF LT GOTO @SKIP R2=R2-R4 R0+=R3 @SKIP R3=R3>>1 IF !0 GOTO @LOOP R1=R2 POP {PC,R4}

2byte縮み、引き算する場合のcycleが2減りました!
たった2byteと笑うかもしれませんが、その2byteを削るのに結構苦労したりするんです。

POKE#700,7,33,16,181,2,70,0,32,1,35,27,4,12,70,92,67,162,66,1,219,18,27,24,68,91,8,247,209,17,70,16,189 ?USR(#700,300) 42

実は、まだ落とし穴があります。マイナスの値で割り算の計算をさせてみてください。
おかしくなりますね。また、割る数に0を指定した時にどんな動きをしてほしいですか?
すべては、あなたの思い次第!

より高速で効率良いアルゴリズムを探してみるのも楽しそうです。Wikipediaの「除算」を見てみると、懐かしの割り算バグありPentiumの話がでてました。 試行錯誤の積み重ねで、今のコンピューター社会ができていることがわかります。

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

プログラミングしていると自分言語が作りたくなる時期が来ます。
IchigoJam BASICでコンパイラを作りたくなった時、書かれた数式を評価できたら便利です。

1 S="R0=1<<4+5"

容量の都合と優先度の関係で、数式を評価する関数 EVAL に対応していない IchigoJam BASIC ですが、実は数式を解釈するINPUTコマンドとキーバッファ操作を組み合わせれば実現できます。

まずはINPUTコマンドの実験

INPUT N:?N ?1<<4+5 261

計算してくれました!変数も関数も実は使えます。

次に予め数式をかいてみましょう

CLS:?" 3*5":LC0,0:INPUT N:?N 15

あとはエンターキーを押せば、Nに数式を評価した結果が入ります。
特殊キー、Home(18 = #12)を使って、行頭へカーソルを移動させておくこともできます。

?" 3*5";CHR$(18);:INPUT N:?N 15

あとは、メモリ内、キーバッファを直接操作して、入力したことにすれば自動化できます。

?"1<<5";CHR$(18);:POKE#1003,1,10:INPUT"",N:?N 32

メモリマップを見ると、#1003がキーバッファの数、その後がキーバッファとなっています。 POKEコマンドで、キーバッファの数を1、キー入力の値としてエンターを表す10をいれておくと、INPUTを呼び出した瞬間、帰ってきます。 (INPUTあとに空文字列指定で、?非表示に)

これを活用して、R0レジスタに数をいれるだけのコンパイラを作ってみます。

10 S="R0=1<<4+5" 20 A=#700 30 C="R0=":GSB@SCMP 40 IF F ?STR$(S+3);:GSB@EVAL:POKE A,N,32:A=A+2 50 POKE A,#70,#47:?USR(#700,0):END 100 @SCMP:F=1:FORI=0TOLEN(C)-1:F=F*(ASC(C+I)=ASC(S+I)):NEXT:RTN 110 @EVAL:?CHR$(18);:POKE#1003,1,10:INPUT"",N:RTN

40行、R0=n は、マシン語表を見ると、32(=1<<5)とレジスタ番号を足したものに、値と並べることがわかります。 Arm Cortex-M0は、下位が先に来る、リトルエンディアンなので、N,32の順番にメモリに書き込んでコンパイル完了!Armマシン語を生成されました。

ちょっと実験

10 S="R0=1<<10+12" RUN 12 OK ?N 1036

おや、Nの値はちゃんと計算されているのに、コンパイルしたプログラムはおかしいですね。
マシン語表を見ると、u8、つまり8bitまでしか対応していないわけです。

16bitまで対応させるに

R0=N>>8 R0=R0<<8 R0+=N&#FF

と、コンピューターくんに分かるように噛み砕いてあげましょう。

マシン語表を見ながらメモリに書くマシン語にします。(&#FFはなくても一緒なので省略)

N>>8,32 0,2 N,48

これをコンパイラに組み込んで、できあがり!

40 IF F ?STR$(S+3);:GSB@EVAL:POKE A,N>>8,32,0,2,N,48:A=A+6 RUN 1<<10+12 1036 OK

こんな調子で、自分だけのアセンブラ、自分だけのコンパイラを作っていきます。
Enjoy create an own computer language!

Maker Faire Bay Area では、IchigoJamで動くFORTHも人気だったとか!
モノ作りの祭典 MakerFaire は出会いの場!共に創ろう令和時代、中経連「中部圏イノベーション促進プログラム」にぜひお越しください!
キーバッファとIoTを使って、遠隔から任意コマンドを動かす技!
BASICコマンドをLTE送信!PCやスマホからIchiogoSodaを遠隔制御 | ボクにもわかる電子工作のブログ

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

IchigoJam 1.3で容量を使い切ってしまって不具合修正もできず困っていた中、C言語で書く例外処理を学んだ結果、容量の大幅圧縮に成功。なんと、864byte空きました。


複雑な計算式で計算用のメモリが溢れた際「Complex expression」の代わりに「Segmentation Fault」が発生します。たまに、当たりどころが悪く(?)固まってしまうことがあるようなので、要調査。割込中の割込じゃないかと予想。

?((((15)))) Segmentation Fault

マシン語でROM領域であるアドレス0へ無理やり書き込んでみます。 [R0]=R0; RET (asm15)

10 POKE #700,0,112,112,71 20 ?USR(#700,0) RUN Segmentation Fault

落ちません!マシン語の敷居がちょっと低くなりました。
はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

LPC1114の4KBのRAMの使い方が変わって、スタック、仮想RAM、システム用データ、IAP用領域の順となっています。詳細は、以前のバージョンのメモリマップも合わせてご参照ください。
IchigoJam BASIC RAM 4kbyte のつかいかた(メモリマップ)


先日、IchigoJamでプログラミングを紹介に伺った、ネパールの学校「SHREE MADAN-AASHIRIT ADARSHA BASIC SCHOOL」より、ステキな証明書が届きました! ありがとうございます!
Let's get smiles of kids! Programming for students of Tribhuvan University in Nepal
世界中のこども達向けに、もう一弾、更なる改善が図れそうです!

IchigoJam 1.3 beta 7 のダウンロードはこちらからどうぞ。
ichigojam-1.3b07.zip
問題報告やご要望、ぜひお送りください! → IchigoJam-FAN on Facebook / #IchigoJam on Twitter

24KBに、ギリギリまで詰め込んだOS、IchigoJam BASIC。でも、もうちょっとだけ改善したい!

ということで、気になっていた4KBのRAM、特にスタックの使い方にメスをいれてみました。

普段使うMacやWindowsやLinuxに組み込まれている、怪しいアプリに大事な情報をアクセスさせないための基本機能。 RAM(LPC1114FDH28では4KB)の範囲を超えると一般保護違反として、例外「HaldFault」が発生します。

プログラマーなら押さえておきたい、基本データ構造のひとつ、後入れ先出し(LIFO)のスタック。 IchigoJamのCPU、LPC1114のアーキテクチャ、Cortex-M0のスタックは、使うほどにアドレスが小さい方向に動いていきます。 現状のIchigoJamの実装では、スタックがどのくらい使われるかはBASICで記述する数式に依存するので、超えないように時々監視しながら、Comprex expression エラーを出しています。ただ、やり口、速度面、プログラム量の面で、ちょっと不格好な点、実は気にしていました。

そこで今回、スタックの領域を一般的なRAMの後ろではなく、先頭側におき、RAMの領域を超えた際に発生する例外をうまいこと使おうという作戦。


ということで、まずはミニマムに実験! 再帰呼び出しでスタックを使い切ったら、soft_entryからリスタート!成功です。 スタート直後、わざとROM領域に書き込もうとして、例外を発生させ、スタックポインタを初期化してスタートする雛形ができました。

#include "LPC1100.h" static inline void led(int n) { GPIO1MASKED[1 << 5] = n << 5; } static inline void wait(int n) { for (volatile int i = 0; i < 50000 * n; i++); } void job(int n) { for (;;) { led(1); wait(1); led(0); wait(1); job(n++); // 再帰でstack溢れ -> HardFault -> soft_entry } } void soft_entry() { // ここからリスタートする wait(10); job(0); } void main() { // Reset_Handlerから呼び出される IOCON_PIO1_5 = 0x000000d0; GPIO1DIR = 1 << 5; *(char*)0 = 0; // アクセス違反 -> HaldFault -> soft_entry } void HardFault_Hander() { // 一般保護違反 RAMの範囲を超えたアクセスなど uint32_t* sp; // = 0x10000000 + 1024 - 32 __asm( "mov r1, #1\n" "mov %0, r1\n" "lsl %0, %0, #18\n" "add %0, %0, r1\n" "lsl %0, %0, #10\n" "sub %0, %0, #32\n" "mov sp, %0\n" : "=r" (sp) ); *(sp + 6) = (uint32_t)(void*)soft_entry; // set PC *(sp + 7) = (1 << 24); // need to set PSR, Thumb mode }

スタックを使い切ってしまっているので、通常の割り込み時にセットされているはずのデータはありません。なので、スタックを初期化して、復帰用のデータを捏造しています。 副次的に、Armマシン語でミスした際にも、復帰できる確率が上がって、よりプログラミングを楽しめるかも!? IchigoJamへの組み込み、がんばります。
はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

子供も大人も楽しい学びの場、地域ICTクラブ!
入り口は広くやさしく、でも、とんでもなく深く広いIT/プログラミングの世界、じっくりみんなで楽しみましょう!


今年の福井こどもプログラミング協議会、ますます楽しくなりそうです!
Thanks: 蕎麦と旨酒と 亀蔵 - 福井県鯖江市

mrubyのmgemのように、IchigoJamをC言語で拡張する方法をつくりました。
最大バイナリサイズ、なんと3KB!

IchigoJamのプログラムは、USBシリアル経由でターミナルなどで接続すれば、PS/2キーボードやテレビを用意することなく、らくらく開発! 完成したプログラムをサクっと転送するのに便利なツール「bas2bin for IchigoJam

これを使えば、BASICのファイルをIchigoJamに書き込むバイナリファイルにできるので、4ファイルまとめて書き込めるので、ワークショップ用などでまとめて準備する時、実製品として出荷するときなどに便利です。(初公開は2016/2/28

今回、C言語でつくったマシン語プログラムをIchigoJamのファイル1〜3へ書き込み、呼び出すマシン語プログラムを追加開発!

bas2bin/src/main.c の、main関数を使って開発し、ファイル1-3の3KBへ書き込んだプログラムを、ファイル0に書き込んだ起動用マシン語が入ったBASICプログラムから呼び出します。

__attribute__ ((section(".MAIN"))) int main(int param, int ram, int rom, int (*divfunc)()) { //return param + 1; return divfunc(100, param); // == 100 / param }

* 割り算はIchiogoJam内のバイナリを使えるので、400byte節約できます。

呼び出し用、ファイル0におかれる IchigoJam BASICプログラム (entry.bas)

1 '3KB asm 10 POKE#700,3,180,5,73,8,120,1,40,0,208,4,73,5,49,139,70,3,188,88,71,112,71,0,0,0,100,0,0,0,116,0,0 20 ?"100/3=";USR(#700,3)

実行すると、100/3の「33」と表示され、無事マシン語が呼び出されたことを確認できます。

IchigoJamはOSサイズ24KB、LPC1114FDH28のROMサイズ32KBの後ろ8KBをユーザーファイル用としています。 残念なことに4KB単位でしか消去できないので、1KB x 4のユーザープログラムを交互に書き込む仕組みになっています。

この後ろ3ファイル、3KBにマシン語をおき、25KB目か29KB目のどちらかマシン語がある方を呼び出しています。 FILESで見ても、1-3は何もでなかったりおかしな表示になる部分が、マシン語です。 先頭4byteを使って、マシン語が書き込まれているかどうかを判定しているので、厳密には3068byte(3KB-4B)まで。 利便性を削って、ファイル0の領域にも食い込ませれば、約4KB使ったオールマシン語プログラムも!

IchigoJam、どんな拡張しましょう!?
初回起動時に動く、すごいデモプログラムを作るのも楽しいかも!

bas2bin for IchigoJam - src on GitHub

呼び出し用のマシン語プログラム、asm15ソースがこちら (entry.bas)

PUSH {R0,R1} R1=[@FILE1A]L R0=[R1] R0-1 IF 0 GOTO 2 R1=[@FILE1B]L R1+=5 R12=R1 POP {R0,R1} GOTO R12 @FILE1A DATA L (24+1) * 1024 @FILE1B DATA L (24+4+1) * 1024

ファイル1の場所にマシン語があるかどうかチェックして、破壊可能なレジスタR12を使って呼び出し!

links
- USB-シリアルモジュール - ショップのふうせん
- はじめてのマシン語 - IchigoJamではじめるArmマシン語その1
- Ruby on Jam! IchigoJamで動くROM8KBのミニ Ruby VM と、mruby ver2.0対応はじめのいっぽ - mmruby for LPC1114

jig.jp、創業から一緒、インキュベイトファンドの初パーティー!

VR、宇宙、食、教育など様々な分野でチャレンジするスタートアップ仲間との楽しく、刺激的な時間!


さりげなく着用していた「光るネクタイ」の改造方法を紹介。

宇宙ビジネスで熱い、水という資源が眠る月!
月色に光るネクタイを作ってみましょう。コアとなるマシン語プログラムはこちら。

R0=18 R1=[@DATA_GRB]L CALL @WS2812B @DATA_GRB DATA L #141000

RGB、3つのLEDの強さは、G、R、Bの順番に16進数で指定します。

月色に光りました!

asm15でアセンブルした後、BASICのデータ部分の末尾3byteを書き換えれば自在に色が変えられます。

40 POKE#7AD,20,0,0


緑に!


今回は昇圧回路とスイッチを使わず、単4電池ケース縦2本でつくる重いけどより安価版。
基本的な作り方は、「光るネクタイのつくりかた」と一緒です。


電池温存する場合は紙などを挟んでおくと便利。(やっぱりスイッチはほしかった)


4KBに拡張された、IchigoCake BASICを使うと、LPC810 Writerとプログラムが1ファイル化もできます。


LPC810への書き込みを実行、SAVE0しておけば、画面がなくてもボタン押しながら実行で書き込み簡単!

Armマシン語(asm15形式)、WS2812B単色表示プログラム全文

DATA L #10000400 ' stack DATA L #21 ' entry point DATA L 0, 0, 0, 0, 0 DATA L #EFFFFBDF ' VALID_CODE = 0x100000000-0x10000400-0x21 @ENTRY R0=[@SYSAHBCLKCTRL]L R1=#DF 'SWM, GPIO, I2C:off, FLASH, FLASHREG, RAM, ROM, SYS [R0]L=R1 R0=[@FLASHCFG]L R1=[R0]L R2=3 ' flash no wait: set 0b00 R2=~R2 R1&=R2 [R0]L=R1 R0=[@PINENABLE0]L R1=[@PINENABLE0_DATA]L [R0]L=R1 R0=[@GPIO_DIR0]L R1=`1100 ' PIO0_2 and PIO0_3 [R0]L=R1 @MAIN R0=18 R1=[@DATA_GRB]L CALL @WS2812B CALL @WAIT GOTO @MAIN 'WS2812B driver 'param ' R0 - data count (ex WS2812B 1 led = 1) ' R1 - G R B 'use ' R2 - OUT value ' R4 - GPIO address ' R5 - bit count @WS2812B R4=[@GPIO_NOT0]L R2=`100 ' PIO0_2 @LOOP_DATA R5=1 R5=R5<<23 @LOOP_BIT [R4]L=R2 R1&R5 IF 0 GOTO @ZERO NOP NOP NOP NOP NOP NOP NOP [R4]L=R2 GOTO @JOIN @ZERO NOP [R4]L=R2 NOP NOP NOP NOP NOP NOP @JOIN R5=R5>>1 IF !0 GOTO @LOOP_BIT R0-=1 IF !0 GOTO @LOOP_DATA RET 'WAIT 'use ' R7 - wait count @WAIT R7=[@WAIT_N]L R7-=1 'wait R7*3+1 clock IF !0 GOTO -1 RET ' init @SYSAHBCLKCTRL DATA L #40048080 @FLASHCFG DATA L #40040010 @PINENABLE0 DATA L #4000C1C0 @PINENABLE0_DATA DATA L #FFFFFFBF @GPIO_DIR0 DATA L #A0002000 @GPIO_NOT0 DATA L #A0002300 ' WS2812B @WAIT_N DATA L 400000 @DATA_GRB DATA L #141000

いろいろ改造してお楽しみください!
光るネクタイ、明和電機秋葉原店「ラジオスーパー」で入手可!

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