福野泰介の一日一創

結論、できる!
ただし、コンピューター自体の仕様であるマシン語へ都度翻訳する、インタプリタ型の IchigoJam BASIC では無理。

真の力を引き出す、マシン語と、プログラミング言語の世界をのぞいてみよう! for 福井高専電子情報工学科1年生。


IchigoJamのハーフキット、はんだづけからスタート!


つないで、LED1、光った!完成! (90分1コマちょっとでほぼ全員完成、はんだごての準備などをしておけば短縮可能、失敗した人用完成版もいくつかあるといい)


授業としてははじめてのプログラミング!


LEDが点滅するロボットの仕組み、かわくだりゲームによるアプリづくりの基本を小学生向けの3倍速で紹介!
IchigoJamは、本当に1秒に5000万回計算できるのか!? 〜IchigoJam マシン語入門〜(keynote/PDF)」
はんだづけから、はじめのいっぽ、ゲームづくりまでの資料はこちら!アレンジ自在のオープンデータ。
一度やったら大丈夫、きっと先生として小学生に教えられますよ!


IchigoJamは、本当に1秒に5000万回計算できるのか!? 〜IchigoJam マシン語入門〜(PDF)」
今回初公開、マシン語入門!かわくだりゲームまでやった人向け、小学生でもきっとOK!


100円のコンピューターがなんと1秒間に5000万回!・・・でも、本当に?


実際測ってみると、VIDEO0で表示を消してもせいぜい1秒に1000回・・・、遅い!
その理由は、IchigoJam BASICがインタプリタ型のプログラミング言語だから。


マシン語ではなそう!


IchigoJamのCPU、NXP LPC1114の説明書と、Armの説明書をまとめてできた、マシン語表


足し算は、Rd += u8 を使う!


2進数、10進数、16進数の変換は IchigoJam BASICのコマンドでも簡単!


マシン語をメモリに書き込んで、USRを使って呼び出し!(呼び出し元にかえる #4770 がないと、どうなっちゃう!?)


マシン語で足し算のループを作ろう!


1000回でも1万回でもCLTとTICK()では1/60秒未満で測定不能なので、ループ回数を100倍に!


ビデオ表示を消して、1ループ1000万回になった!プログラムとマシン語表のサイクルを見てみると、確かに1秒間に5000万回計算できてることが判明!


実は、CPUだけだと、最新のCPUでも、1つだけならたかだか40倍くらいしか変わらない。
3D計算やAIなど、特定の計算に特化したハードウェア(GPU)の活用が、圧倒的計算力を活かす鍵!


圧倒的パワーが魅力なコンピューター。いろいろたのしいものつくっちゃおう!


好評だったNT鯖江2019、来年のNT鯖江2020の開催は、2020/10/3-4が有力!
見に来るだけでももちろん歓迎、でも、せっかくなら、なにかつくって出展しよう!

links
- はじめてのマシン語 - IchigoJamではじめるArmマシン語その1
- 福井で初開催!なにかつくろう、NT鯖江2019! ダイジェスト動画と交互連打で目指せ世界記録ゲーム!

IoT人流センサーでも活躍、超音波で距離を測るセンサー「HC-SR04」は、デジタル入力を使ってアナログ量が測れます。
0か1かしかないデジタルでどうやって、量がわかるのでしょう?
1になるまでの時間を見るのです。

改めて、このセンサーの使うプログラムを見てみましょう。
(測距センサーHC-SR04:IchigoJam、Trig:OUT1、Echo:IN1、GND:GND、Vcc:5V)

10 OUT1,1:OUT1,0:A=0 20 A=A+1:IF IN(1) CONT ELSE ?A 30 WAIT 5:GOTO 10

OUT1としてしまうと、他のOUTポートもまとめてコントロールしてしまうので、OUT1,1:OUT1,0と変更しました。
これがセンサーのTrigとなり、超音波センサーの2つの丸めの1つから超音波のパルス(短い時間の1)が発射されます。
この超音波が返ってくるまでの時間を変数Aを使って、IN1が0になるのを待つ。Aが距離。
以上が使い方の基本です。

OUT1が埋まっている時は、TrigをOUT2から6まで空いているところに挿して、例えば10行をOUT3,1:OUT3,0とかすればOK!
IN1が埋まっている時は、EchoをIN2から4までの空いているところに挿して、例えば20行をIN(2)とかすればOK!

(測距センサーHC-SR04:IchigoJam、Trig:OUT3、Echo:IN2、GND:GND、Vcc:5V)

10 OUT3,1:OUT3,0:A=0 20 A=A+1:IF IN(2) CONT ELSE ?A 30 WAIT 5:GOTO 10

IchigoJamが速いほどに、近いものが認識でき、細かく測れます。高速化した1.4β、お試しください!

いろんなセンサーやモーターを組み合わせて、誰も作ったこと無いものつくっちゃいましょう!


HC-SR04 x IchigoJam S

更に、精度が必要な場合や、ファームウェアの書き換えが面倒な場合、マシン語を使って時間計測を高速化します。

R0=0 R1=[@IN3]L @LOOP R2=[R1]L R0=R0+1 R2-0 IF !0 GOTO @LOOP R0=R0>>9 RET @IN3 DATA L #50000080

IN1のアドレスは #50001000 でした。
IN2はアナログ入力設定になっているので切り替えが必要なため、少しややこしい。
IN3のアドレスは #50000080
IN4のアドレスは #50000100
となります。

これをasm15を使ってアセンブルするか、マシン語表を見ながらハンドアセンブルしてできるマシン語がこちら。

10 POKE#700,0,32,3,73,10,104,64,28,0,42,251,209,64,10,112,71,128,0,0,80 20 OUT1,1:OUT1,0:?USR(#700,0) 30 WAIT 5:GOTO 20

このアドレスの求め方、IchigoJamのCPU、LPC1114の取説によると、PIO0のベースアドレスは #50000000
IN3は、PIO0_5の場所なので ?"#5000";HEX$(1<<(5+2),4) → #50000080
IN4は、PIO0_6の場所なので ?"#5000";HEX$(1<<(6+2),4) → #50000100
(参考、マシン語でLEDを光らせよう! - IchigoJamではじめるArmマシン語その4


Hana道場、越前がにロボコンに向けて開発が進んでます!超音波センサーを違うポートで使いたい?


2つのモーター制御に、たまごを惑星においてくるためのサーボ制御、ライントレース用の2つセンサー、もりもりです!
これにレアメタル判定用に、超音波センサーを追加!


福井の小学生向けご当地ロボコン「越前がにロボコン」いよいよ今週末11/3開催
11/3日 9:00-17:00 ショッピングシティ ベル、あじさいホールにて!(Googleマップ

マシン語が分かると楽しいC言語!
IchigoJamで楽しむC言語「c4ij」を最新API対応のバージョンアップ!

1セクター4KB、8セクターで32KBあるIchigoJamの内、2セクターを使って、最大7KBものマシン語プログラムを書き込んで、BASICから呼び出すことが可能です。

デモとして、WS2812B/WS2811用のドライバ呼び出しを使って色相環をぐるぐるまわすプログラムを作りました。

IchigoJam BASICで書いたHSV2RGB変換をC言語に移植!
割り算処理は、IchigoJam OSに含まれるものを使って、省メモリ化してます。

#include <std15.h> /* 100 @HSV2RGB:R=V:G=V:B=V:IF S=0 RTN 110 D=H/60%6:C=V-(59-H%60)*V/60*S/100:E=V-H%60*V/60*S/100:F=V*(100-S)/100 120 IF!DG=C:B=F 130 IFD=1R=E:B=F 140 IFD=2R=F:B=C 150 IFD=3R=F:G=E 160 IFD=4R=C:G=F 170 IFD=5G=F:B=E 180 RTN */ static void hsv2rgb(int h, int s, int v, uint8_t* grb, uint64_t (*divfunc)()) { int r = v, g = v, b = v; if (s == 0) return; int d = divfunc((uint32_t)divfunc(h, 60), 6) >> 32; int hmod60 = divfunc(h, 60) >> 32; int c = v - (uint32_t)divfunc((59 - hmod60) * v * s, 6000); int e = v - (uint32_t)divfunc(hmod60 * v * s, 6000); int f = (uint32_t)divfunc(v * (100 - s), 100); if (d == 0) { g = c; b = f; } else if (d == 1) { r = e; b = f; } else if (d == 2) { r = f; b = c; } else if (d == 3) { r = f; g = e; } else if (d == 4) { r = c; g = f; } else { g = f; b = e; } grb[0] = g; grb[1] = r; grb[2] = b; } __attribute__ ((section(".main"))) int main(int param, int ram, int rom, uint64_t (*divfunc)()) { const int v = 50; const int n = 20; uint8_t* grb = (uint8_t*)(ram + 0x800); int h = 0; for (;;) { for (int i = 0; i < n; i++) { hsv2rgb(h + i * 5, 100, v, grb + i * 3, divfunc); } ws_led(3 * n, grb, GPIO_OUT1); if (inkey() == 27) break; h++; } return 0; }

新API、ws_ledを使って多数のWS2812Bを高速制御!最大輝度50(=v)で、20コのLEDを5度ずつまわす色相環。ESC(=27)が押されたらマシン語からBASICへ処理を戻します。64bitで返すことにして使うdivfuncは、上位32bit(=R1)があまり、下位32bit(=R0)が除数となります。

このコードで256byteをちょっと超える感じ(追記、hsv2rgbをstatic関数にすることで下回りました。Thanks > @fujitanozomu さん)。#700-#8FFまでを使った512byteまではプログラムに含められます(make poke)。それ以上はFILE1〜3の領域を使って、3KBまでのプログラムで書き込みます(make write)。 SAVEするのを諦め、まるっと残り7KB使ったプログラムづくりも可能です。(bas2bin機能をc4ijに統合しました)

IchigoJamで容量不足や、速度不足を感じて来たときの選択肢のひとつ!
(C言語はちょっと難しそうという方、Ruby版もあります)

「明和電機 x ギャル電」のイケてるアクセワークショップ!電子の力でキラキラさせよう!


夏休み特別企画「明和電機 超! 技能訓練所」開催! - 明和電機 - Maywa Denki明和電機 – Maywa Denki」 (オモチャは買って遊ぶより、自分で作ったほうがはるかに楽しい)

links
- 高専でなぜC言語を学ぶのか? IchigoJamマシン語生成プログラム c4ij で作る、C言語版かわくだり
- C言語で拡張するIchigoJam BASIC - 3KBマシン語をIchigoJamで動かすウラワザ
- c4ij - イチゴジャム レシピ
- IchigoJamで計算する色相環、色相・彩度・明度を光の三原色に / HSV2RGB in BASIC
- 実機転送実行まで1秒! Ruby on Jam でサクサク楽しい組み込みアジャイル開発

8/5は、日本最古級のパソコン、PC-8001の誕生日!


40周年を記念した、ちっちゃい版がお披露目!
BASICやマシン語で作られたゲームが16本、もちろん、自分で開発も可能!
細部まで再現し、BASICが動作す���「PasocomMini PC-8001」 - PC Watch

今回はオリジナルのマイクロソフトのN-BASIC搭載!
33年前の私も、同マイクロソフト製のMSX-BASICで学びました!

35年後輩にあたる、IchigoJam BASICで自在に扱える国産パソコンIchigoJamは、現在バージョン1.4調整中。
こんなBASICのプログラムを入力すると・・・

10 A=RND(14)+1:T=RND(3)*20+20 15 FOR J=0 TO 3 20 FOR I=0 TO A-1 25 LET[I*3],RND(16),RND(16),RND(16) 26 NEXT 30 WS.LED A,256/A+1 40 WAIT T 42 NEXT 50 GOTO 10

オシャレな模様をランダム模様が表示されます!

こちら先週末の永平寺イベントで活躍した、WS2812Bを16x16、256コ並べた、大型ディスプレイ。

WS.LEDコマンドは配列にRGBやGRBの順に入った値を使って最大34コのWS2812BなどのLEDを制御できる1.4新設コマンド。 台二パラメータでリピート設定すれば、何千個ものフルカラーLEDも制御できます!

16x16、256コを個別に制御するには?
ちょっと背伸びしてマシン語を使いましょう。10進数の数を打ち込むだけで使えます。

10 POKE#700,16,181,6,72,9,34,18,2,17,68,3,34,18,2,1,35,238,36,36,136,160,71,16,189,112,71,0,0,4,0,1,80 50 CLS 60 S="プログラミングフェス2019 in エイヘイジ " 100 FOR K=0 TO LEN(S)-1 101 GSB200 102 C=ASC(S+K):P=1:GSB210 103 C=ASC(S+K+1):P=0:GSB210 104 U=USR(#700) 105 NEXT 110 GOTO 100 200 FORI=0TO5:[I]=RND(5)+I/3*15:NEXT:RTN 210 FOR I=0 TO 7 220 FOR J=0 TO 7 230 N=PEEK(C*8+7-I)>>J&1*3 235 A=#900+I*6+J*3*16+P*8*3*16 240 POKE A,[N],[N+1],[N+2],[N],[N+1],[N+2] 250 NEXT 260 NEXT 270 RTN

こちらが1.4で対応するAPIを呼び出す、マシン語部分のプログラム、asm15でアセンブルすると、10行の数の並び、マシン語が生成されます。

PUSH {R4,LR} R0=[@OUT1]L R2=9 ' = data address #900 R2=R2<<8 R1+=R2 R2=3 R2=R2<<8 ' = send 256*3 byte R3=1 ' = step 1 R4=#EE ' API to use WS2812B R4=[R4]W GOSUB R4 POP {R4,PC} RET @OUT1 DATA L #50010004

ただ、32byteもあって、APIを使っている割にはちょっと長い。

使うレジスタを破壊してOKなR3までにできればGOTOで呼び出しでき、PUSH/POPが不要になりそう。短縮するために数とリピートの指定を16bitシフトして1つのレジスタで渡すようにして、OUT1を表すアドレスの設定に6〜8byteも使うので、0指定ならOUT1ってことにしておくと、次のように14byteまで縮まる!

R0+=R1 R1=3 R1=R1<<(8+16) ' 256*3 R2=0 R3=#EE R3=[R3]W GOTO R3

APIづくりは、利用者想いが大事!
まずは自分で使うことだと、身にしみました。

近々バージョンアップします。
BASICで配列を使ったLED遊びはできるので、ベータ版お試しください
IchigoJam 1.4β7


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 (BASICではVAL) に対応していない 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万倍速!? セットで学ぶアルゴリズムとコンピューター

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