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

バーチャルマシンにディスプレイを接続、avmで動かすIchigoJamマシン語

2016/10/01 23:55:00
#IchigoJam #js #ARM #avm 

決められた手順(プログラム)通りに、ひたすら高速に計算するだけの機械、CPU。
これに手足顔をつなぐことで、ゲーム機、家電、パソコン、ロボットになります。

前回つくった、ソフトウェアによるARM Cortex-M0風CPUシミュレーション。
これに人間の顔にあたる、ディスプレイをつなげてみました。


avm with display
[RUN]ボタンをおすと、レジスタを忙しく変化させながら、健気に計算しまくる様子をのぞけます。

プログラムやデータ、計算した結果を記憶する機能を担う「メモリ」。特定の場所(アドレスと言う)を表示すると決めれば、それが顔になります。今回接続したディスプレイは、IchigoJamと同じ仕様、アドレス0から256コ分のキャラクターパターン、16進数で900から32x24の768byteが表示するキャラクターコードにしています。

メモリが潤沢にあるパソコン上の仮想コンピューターなので、6.2Mbyteをディスプレイ用のメモリにして、1920x1080のフルHD、1画素にRGB256階調(3byte)対応に改造することも可能です。(参考、IchigoJamのメモリマップ

サンプルとして設定済みのプログラムは、IchigoJamではじめるマシン語その5「大きな文字を256倍速表示」で生成したマシン語です。 コンピューターへの理解を深める、マシン語学習のお供にぜひどうぞ!(deleyが1以上の時、JavaScriptのコンソールには、実行した命令を表示します。)

VMとしては不完全なので、C言語などで開発したARMマシン語を貼り付けても「not supported op」と出てスキップされます。必要に応じて改造ください。

楽しく制するマシン語、ARMマシン語学習用VM on JavaScript

2016/09/27 23:55:00
#js #ARM #avm 

イマドキのBASICとしてIchigoJamが紹介サれている今月の「日経ソフトウェア」で見かけたマシン語の話題。 ドラゴンクエストのゲームデザイナー、堀井雄二氏はNEC PC-6001のBASICでプログラムを始め、速度を求めてZ80マシン語へ。ハンドアセンブルでマシン語プログラムを作ったそう。 「矢沢久雄のソフトウェアの基礎Q&A」では、基本情報技術者試験で使われる仮想マシン語CASLIIのススメ。 Z80ハンドアセンブルした小学生時代、CASLIIがJavaVMと似てると思った高専生時代が懐かしい。

私の一押しは、32bit CPU、ARM Cortex-M0のマシン語。IchigoJamで使っているLPC1114で使え、コンパクトで強力かつ、iPhoneなどスマホでも動きます。 (でも、本気で動かすなら64bit対応のARM64マシン語!)


連載、ARMマシン語入門と合わせて使えるシミュレーター「avm」をつくってみました。フラグなどがいい加減で、まだ表示がないので、連載その2までの対応です。

サンプルのマシン語プログラムは、10から1までをループしながら足し算するプログラム。たかだか100円ちょっとのCPUですが、54サイクル、1.12μ秒(約100万分の1秒)で計算するパワーをフルに使い切る感こそ、マシン語の醍醐味!

JavaScriptでつくったシンプルなバーチャルマシン(VM)「avm」、どうぞいろいろ改造してお使いください!
IchigoJamで学ぶARMマシン語入門」と合わせてどうぞ!

iPhoneインラインアセンブラ入門、64bit ARMマシン語はじめのいっぽ

2016/08/17 23:55:00
#ARM #swift 

IchigoJamもiPhoneも同じARMのCPU、IchigoJamでマシン語を覚えたらiPhoneでも使ってみましょう。今回の対象は、64bit化したARM64(ARMv8)対応のiPhone5s以降です。ネットや書籍での資料には32bit時代のものも混在しているので注意が必要です。(iPhone Wikipedia)。

Swiftにはインラインアセンブラ機能はまだないようなので、Objective-CでGameをテンプレートに選び、次の行を viewDidLoad の [super viedDidLoad]; の後に追加します。

__asm__ volatile ("mov x0, x0");

※__asm__ はインラインアセンブラ、volatile で最適化を停止
エミュレーター向けだとコンパイルエラーになります。(IntelがARMライセンス取得とのことなので、MacBookへのARMv8組み込みにも期待!まずはApple-Aの生産から?詳しい解説記事


iPhoneでARM64マシン語、動きました!
(3Dの飛行機はサンプルのままで、今回のアセンブラでの動作とは無関係です)

mov x0, x0 は、レジスタX0の内容をレジスタX0に入れる(つまり、ほぼ何もしない)というマシン語を生成します。やっかいなことにこのコードはMacのiPhoneエミュレーターでは動きません。iPhoneエミュレーターはARMのCPUまでは動かしてくれないようです。実機向けにコンパイルすると動きます。(ARM64では64bitになったレジスタをRではなくXで呼ぶようになった様子)

Objective-Cでの変数との足し算をやってみます。

long num = 1; __asm__ volatile ( "mov x1, #1 \n\t" "add %[n], %[n], x1 \n\t" : [n] "+r" (num) : : "x1" ); NSLog(@"res: %ld\n", n);

コンソールに結果として 2 と表示されます。mov x1,#1 でレジスタX1に1を入れ、add命令で%[n]というコンパイラ任せなレジスタX?にX1の値を加えます。IchigoJamマシン語入門では X1=1 と表記していました。

最初のコロンの後 [n] "+r" (num) で、Objective-Cの変数numを%[n]というレジスタに割り当てて、読み書き"+"するよと指定しています。

三番目のコロンの後の "x1" で、インラインアセンブラ内で X1 レジスタを使用すると宣言して、前後のプログラムと干渉しないようにコンパイラに指定します。

これで基本はOKですね!
より詳しい解説は、こちら「ARM GCC Inline Assembler Cookbook」など参照ください

せっかくなので、アセンブラならでは機能を使った便利な関数を作ってみます。

- (unsigned long)rbit:(unsigned long)n { __asm__ volatile ( "rbit %[n], %[n] \n\t" : [n] "+r" (n) ); return n; }

rbitというのは、64のbit列を反転する命令です。普通に計算すると結構手間ですが、マシン語なら1クロック、0.1nsecで計算します!(参照、Arm64(ARMv8) Assembly Programming (08) 分岐命令

iPhone(ARMv8)とIchigoJam(ARMv6-M)との比較です。

IchigoJamのCortex-M0のThumb命令とも互換性あるiPhone6sですが、浮動小数やSIMD命令など性能をフルに活かすならARM64マシン語プログラミングへのステップアップがオススメです!レジスタもたくさんあるので作るのも楽!

参考
- Arm64(ARMv8) Assembly Programming (00)(とっても詳しいRAM64解説)
- 連載、IchigoJamではじめる、ARMマシン語入門
- 連載、iPhoneゲームづくりからはじめる、Swift入門

掛け算もインクリメントも1クロック、BASICでカジュアルに使うインラインマシン語とツール紹介 - IchigoJamではじめるARMマシン語その7

2016/08/16 23:55:00
#IchigoJam #ARM 

じっくり取り組むマシン語も楽しいですが、手軽で便利なマシン語ならではの技を紹介します。

CPUには足し算、引き算、メモリアクセスなど基本的な機能の他に、CPUによってそれぞれ固有な機能があります。現代のパソコンやスマホに使われる高性能なCPUには、3Dグラフィックスや、音声、映像などの処理の高速化のために、4つの計算を同時に行うなどのSIMD命令が含まれます。ほとんどのスマホで使われているARMアーキテクチャー、Cortex-AにはNEONというSIMD機能が搭載されています。

電源が限られるモバイルにおいて、いかに無駄な電力消費を抑えつつ、サクサク軽快なアプリを提供するかが重要です。C、JavaScript、Swiftでは、CPU固有の機能を表現する方法がないので、マシン語を使って、コンピューターの性能をフルに引き出しましょう。誰かがつくった機能(ライブラリ)を使わせてもらうのもいいですが、自分でも作ってみたくもなるでしょう。(参考「iPhoneでのピクセル処理をNEON(ベクタ演算)を使って4倍高速化する」)

IchigoJamが使っている低電力CPU、LPC1114は、ARM Cortex-M0という、Cortex-Aと命令に互換性があるサブセット(一部)です。SIMD命令はありませんが、BASICやCでは扱えない特別な演算があるので、今回はそれらをカジュアルに使います。

Cortex-M0 マシン語表(抜粋)」に、特殊演算を追記しました。

※bic:ビットクリア、asr:符号付き右シフト、ror:右ローテート
※rev:byteオーダー反転、rev16:byteオーダー反転(2byteずつ)、revSH:符号付き16bitを反転32bit化
※abc:キャリー付き足し算、sbc:キャリー付き引き算

IchigoJam BASICには当てはまる演算子がないために、関数のような書き方にしていますが、これらの計算はすべて、通常の計算と同様、1サイクルで処理できます。IchigoJamの場合、48MHzで動作させているので1サイクルは約20ナノ秒で計算は終了します(iPhone6sは最大1.84GHzなので約40倍高速な0.1nsec)。

bic:指定したビット位置のみをクリアする。普通に書くと Rd = Rd & ~Rm と、2回の演算が必要。
ror:指定した数だけ右にビットシフトして、はみ出た分は上位ビットにセットする。
rev16:16bitの上位8bitと下位8bitを入れ替える(上位32bitも同様に)

I2Cで外部センサーから取得したビッグエンディアン(=最初に上位8bitがある)なデータの読み取りに便利そうな rev16 を実際使ってみます。

[0]=`1011101001 000 000 'R0=rev16(R0) [1]=#4770 ?USR(#800,1) 256 ?HEX$(USR(#800,#3412)) 1234

#4770は呼び出し元に戻るRETURNのマシン語です。(Z80でいう#C9)
同じコードをIchigoJam BASICで書くと、下記のように少し長くなるので、繰り返し使う場合に有利です。

N=#3412 ?N<<8&#FF00+N>>8

マシン語をいれておくメモリとして、PCG領域を借りて、下記のように書くこともできます。このように、別のプログラムでいろいろな便利な関数を用意しておき、メインのプログラムを呼び出すとメモリを有効に使えます。

10 POKE#700,#40,#BA,#70,#47:P=#700 20 ?HEX$(USR(P,#CDAB));HEX$(USR(P,#3412))

Cortex-M0 マシン語表(抜粋)」にサイクル数を追加しました。ほとんどの命令が1サイクルで終了することが分かります(LPC1114は掛け算も1クロック)。実行時間が正確に計算できるのもマシン語のいいところです!

ハンドアセンブルでコツをつかんだら、アドレス計算も自動でやってくれるARMアセンブラ搭載、Mac/Windows/Linux対応IchigoJam用ツール「IJUtilities」が便利です。C言語からIchigoJam BASIC用のコードを生成する「rohiniku/IchigoJam_bin2poke」の逆アセンブル機能を使った解析も勉強に便利!

ARMマシン語は、スマホでもそのまま活かせる究極の高速化テクニック。自分の書いたコードが実際どういうマシン語になるかだいたい想像できる程度には押さえておきましょう。

C言語やJavaScriptでお馴染みの「i++」という iに1足す演算子が、Swift3ではなくなってしまいます。MSXでZ80マシン語で1足す命令(INC)や1引く命令(DEC)を知っていたため、ごく自然に受け入れて今でもよく使っているだけにさみしいものがあります。Z80では1足す専用の命令、INCを使えば足し算命令ADDよりサイズが節約できました(機械語の手帳 ステート数表)。ただ、近年使われるよくARMは、1足すのも2足すのも255足すのも、速度もサイズも一緒。演算子削減の一因かもしれません。

ゲーム、スマホ、ロボット、クラウド、IoT、AIなど、奥深いプログラミングの世界も第一歩から!
福井新聞主催、PCN共催「福井県小中学生プログラミング・フェス2016」参加者募集中!

- 連載、IchigoJamではじめる、ARMマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語

ハンドアセンブルで大きな文字を256倍速表示!マシン語使いこなしTIPS - IchigoJamではじめるARMマシン語その6

2016/08/15 23:55:00
#IchigoJam #ARM 

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=R0&R5 IF 0 GOTO else1(2) R0=1 else1: [R1]=R0 R1=R1+1 R5=R5>>1 IF !0 GOTO loop2(-7) R1=R1+24 R4=R4+1 R4-8 IF !0 GOTO loop1(-13) R1=R1-24 R0=3 R0=R0&R6 R0-3 IF 0 GOTO eles2(2) R1=R1-224 else2: R2=R12 R6=R6+1 R6-12 IF !0 GOTO loop0(-27) 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 WAIT 1: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行だけ流用すると、他のプログラムでも大きな文字表示、使えます。


動画 on Vine

作るのに手間がかかるけど、動作は高速なマシン語。
作るのは簡単・高速だけど、動作は遅い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. カジュアルに使うインラインマシン語

- ARMマシン語表(PDF版)

楽しさ広がるマルチバイトメモリアクセスとスタック - IchigoJamではじめるARMマシン語その5

2016/08/11 23:55:00
#IchigoJam #ARM 

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

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

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

※[]の後の数が2だと2byte単位、4だと4byte単位で読み書き。u5/2は2で割った数を指定。

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

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

うまくいきました!

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) 200

全部PUSHしておいてもいいですが、1レジスタに付き4byteのメモリと約20ナノ秒(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. カジュアルに使うインラインマシン語

マシン語でLEDを光らせよう! - IchigoJamではじめるARMマシン語その4

2016/08/08 23:55:00
#KidsIT #IchigoJam #ARM 

やりたいことが遅くでできない時、速い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<<8 R3=R3<<8 R3=R3+`00111100 [R3+0]=R0 RET

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

R3=#50 `00100 011 01010000 R3=R3<<8 `00000 01000 011 011 R3=R3+1 `00110 011 00000001 R3=R3<<8 `00000 01000 011 011 R3=R3<<8 `00000 01000 011 011 R3=R3+`00111100 `00110 011 00111100 [R3+0]=R0 `01110 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と出ずにフリーズしてしまったら、それは残念ながらどこか打ち間違えて、暴走してしまったことになります。 あきらめて、本体の電源を切ってやり直しましょう。

(追記)当初、計算にレジスタR4を使っていましたが、ARMのABI(Application Binary Interface = 関数呼び出し規約)にて、破壊して使って良いレジスタはR0-R3までと分かり、R3に変更しました。(日本語解説、32ビットへの誘い

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

- 連載、IchigoJamではじめる、ARMマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語

マシン語メモリアクセスで画面超速表示! - IchigoJamではじめるARMマシン語その3

2015/10/25 23:55:00
#IchigoJam #KidsIT #event #ARM 

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 001 011 011 ← [R1+R3]=R3
00110 011 00000001 ← R3=R3+1
00001 01000 011 010 ← R2=R3>>8
00101 010 00001010 ← R2-10
11010001 11111010 ← IF !0 GOTO -4
0100011101110000 ← RET

マシン語表を見ながら順番に書いていくだけなので簡単ですね!
GOTO命令だけがちょっと特殊なので、注意が必要です。
4つ分命令を戻りたいときは、更に-2した-6を、2進数8bit補数表現にします。

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

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

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

10 [0]=`0010001100001001 20 [1]=`0000001000011011 30 [2]=`0101010001011011 40 [3]=`0011001100000001 50 [4]=`0000101000011010 60 [5]=`0010101000001010 70 [6]=`1101000111111010 80 [7]=`0100011101110000 90 CLS:A=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. カジュアルに使うインラインマシン語

ハンドアセンブルで超速計算! - IchigoJamではじめるARMマシン語その2

2015/10/23 23:55:00
#IchigoJam #KidsIT #lpc1114 #ARM 

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

プログラムをひとつひとつマシン語対応表を見て、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,`01000001,`00011000 POKE #704,`00000001,`00111000,`11111100,`11010001 POKE #708,`00001000,`01000110,`01110000,`01000111 ?USR(#700,100) 55

動きました!

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

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

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

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

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

- 連載、IchigoJamではじめる、ARMマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語

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

2015/10/21 23:55:00
#IchigoJam #KidsIT #ARM 

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

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

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

0011000000000001

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

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

00110 000 00000001

これをPOKE文を使ってメモリに書き込みます

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. カジュアルに使うインラインマシン語

- 参考リンク
命令一覧「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 福野泰介 - Taisuke Fukuno / @taisukef / high-res profile image