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

STM32はじめのいっぽ、64byteでつくるエルチカ1Hz Armマシン語

2017/12/24 23:55:00
#Arm #stm32 

CPUアーキテクチャとしてArmを採用しているメーカーはたくさんあるので、Armマシン語を覚えたらいろいろと応用できます。 今回コントロールするマイコンは、STマイクロエレクトロニクスSTM32F031K6です!


実は古い、STM32との出会い。マイコンに興味を持ち始めた4年前、開発環境が1,000円とあまりに安かったでひとまず買ってWindowsでデモのエルチカを見るも、Macでの開発環境が見つからずお蔵入り。 その後、書籍「組み合わせ自在!超小型ARMマイコン基板」をきっかけにNXP社のDIP版LPC1114に出会ったできた、IchigoJam

IchigoJamでマイコン開発で使っているDIP版LPC810も残念ながら生産終了なので、メーカー問わずマイコン探しをしている中での再会です!

ひとまず一番小さいSTM32F0シリーズのお手軽ボード「STM32 Nucleo Board STM32F031K6」で実験。 Arm Mbedに対応したUSBでPCに接続するとドライブとして認識して、binファイルをコピーするだけで書き込んでくれます。

LPC1114と同様、Arm Cortex-M0なので、基本は一緒。 親切な取説を見ながら、ひとまずミニマムなエルチカ(64byte)が完成! (参考、Cortex-M0 Armマシン語表(抜粋)

DATA L #20001000 ' stack top DATA L #08000009 ' entry point @ENTRY R0=[@RCC_AHBENR]L R1=[R0]L R2=1 R2=R2<<18 R1|=R2 [R0]L=R1 R0=[@GPIOB]L ' GPIOB_MODER R1=`01000000 [R0]L=R1 R0+=#14 ' GPIOB_ODR R1=`1000 R2=0 R3=[@WAIT_CNT]L @LOOP [R0]L=R1 R4=R3 R4-=1 IF !0 GOTO -1 [R0]L=R2 R4=R3 R4-=1 IF !0 GOTO -1 GOTO @LOOP @RCC_AHBENR DATA L #40021014 @GPIOB DATA L #48000400 @WAIT_CNT DATA L 1000000

RCC_AHBENRでGPIOを有効にする(省エネの基本は使う部品だけ有効に!)
GPIOB_MODERでGPIOを設定(Nucleoの取説より、PB3にオンボードLEDがつながっていることを確認)
GPIOB_ODRでGPIOに書き込む(メモリマップドI/Oなので、メモリに書き込むだけ)
空ループでWAIT(1ループ4clock、標準は8MHzなので、1Hzエルチカするには1万回でOK!)
* Flashは#08000000に置かれます。チェックサムがないので手抜きするとヘッダは8byteでOK!

asm15 Assemblyでstart address:0、hex fileにアセンブルして、objcopyでbinファイルを作って、コピーしてみましょう!

cat > obj.hex :1000000000100020090000080A480168012292043B :10001000114301600848402101601430082100228A :10002000064B01601C46013CFDD102601C46013CB0 :10003000FDD1F6E7141002400004004840420F00D2 :00000001FF arm-none-eabi-objcopy -I ihex --output-target=binary obj.hex obj.bin

定格いっぱいフルパワー48MHzでエルチカする方法はこちら

DATA L #20001000 ' stack top DATA L #08000009 ' entry point @ENTRY 'clock 48MHz R0=[@FLASH]L ' FLASH_ACR R1=[R0]L R2=`10001 ' 1<<4:enbale prefetch, 1 wait state R1|=R2 [R0]L=R1 R0=[@RCC]L R3=R0+4 ' RCC_CFGR R1=[R3]L R2=`1111 R2=R2<<18 R2=~R2 R1&=R2 R2=4 ' PLLMUL6 R2=R2<<18 R1|=R2 [R3]L=R1 R1=[R0]L ' RCC_CR R2=1 R2=R2<<24 ' PLLON R1|=R2 [R0]L=R1 R2=R2<<1 ' PLLRDY R1=[R0]L R1&R2 IF !0 GOTO -2 R1=[R3]L ' RCC_CFGR R2=2 ' PLL R1|=R2 [R3]L=R1 R2=12 R1=[R3]L R1&=R2 R1-8 ' 2<<2 == PLL IF !0 GOTO -3 ' init GPIO R0+=#14 'RCC_AHBENR R1=[R0]L R2=1 R2=R2<<18 R1|=R2 [R0]L=R1 R0=[@GPIOB]L ' GPIOB_MODER R1=`01000000 [R0]L=R1 R0+=#14 ' GPIOB_ODR R1=`1000 R2=0 R3=[@WAIT_CNT]L @LOOP [R0]L=R1 R4=R3 R4-=1 IF !0 GOTO -1 [R0]L=R2 R4=R3 R4-=1 IF !0 GOTO -1 GOTO @LOOP @FLASH DATA L #40022000 @RCC DATA L #40021000 @GPIOB DATA L #48000400 @WAIT_CNT DATA L 6000000

24MHzを超える場合はFlashアクセスにウェイトが必要です。ただプリフェッチバッファー4byteが3つあり、それを有効にしておくことで、速度はそこそこ保たれます。 PLLを有効にして、内蔵クロック8MHzを6倍にする設定をして、クロックを切り替えることで48MHz動作になります!エルチカも6倍速くなってしまうので、WAIT_CNTを6倍にして1Hzエルチカのできあがり!

次は、Nucleoの書き込み機能に頼らず、IchigoJamからマイコンへの書き込みに挑戦です!

links
- リファレンスマニュアル STM32F0x1/STM32F0x2/STM32F0x8 advanced ARM®-based 32-bit MCUs
- IchigoJamとLPC810でつくるLED点滅クリスマスアクセサリー
- はじめてのマシン語 - IchigoJamではじめるARMマシン語その1

Arm社訪問! クリスマスはArmマイコン IchigoJam を使ったオリジナルツリーづくりはいかがでしょう? Armマシン語 LPC810 x WS2812B 24MHz版

2017/12/19 23:55:00
#IchigoJam #KidsIT #lpc810 #asm #Arm 

Arm社の日本法人、アーム株式会社を訪問。Armと言えば、に、LPC810に、Z80以来のマシン語、楽しませてもらっています!
* 今年、2017.8.1、Arm社は、ARMからArmへと社名とブランドとロゴを変更(ロゴは全部小文字)。合わせて、本ブログ表記もArmへと変更しました。

内海社長ともお会いできました!クリスマスツリーの電飾は、おなじみWS2812B、Armでコントロールされたました!

おみやげに頂いた厚い本「ディジタル回路設計とコンピュータアーキテクチャ[ARM版]
スイッチサイエンスさんも翻訳出版に協力
原著はなんとKindleでも1万円オーバーの超大作「Digital Design and Computer Architecture: ARM Edition

なぜコンピューターは計算が速いのか?」で解説したスイッチの実体、トランジスタや2進数などの基本もしっかり。

マシン語、アセンブリ言語に関する解説もArmの実アーキテクチャーに添っていて実用的!

HDLや、具体的なCPU設計にまで踏み込むマイクロアーキテクチャの章まで揃っていて読み応え抜群。

サイトからI/Oや、C言語に関する解説などをpdfで読むことができます。
Elsevier · Harris, Harris: Digital Design and Computer Architecture · Welcome

2017.12.25(月)、クリスマス。Yahoo! Japan ロッジにて、Armマイコン、IchigoJamで作るクリスマスツリーづくりワークショップを復興庁さん主催で開催!

Fw:東北Weekly 家族でプログラミングしてみよう! 東北発のほやアートをつくってみよう! | Peatix
小中学生のこどもと保護者の方が対象です。プログラミングがはじめての人でもOK!自分だけのオリジナルキラキラツリーを作りましょうっ

(おまけ)Arm訪問記念マシン語プログラム for LPC810、24MHzで800kHz正確コントロール版。

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 'SWT, 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 ' clock setting 24MHz R0=[@PDRUNCFG]L ' PLL power on R1=[R0]L R2=128 R2=~R2 R1&=R2 [R0]L=R1 R0=[@MAINCLKSEL]L R1=3 ' set PLL [R0]L=R1 R0=[@SYSPLLCTRL]L R1=1 ' 0:12MHz 1:24MHz, 2:36MHz, 3:48Mhz, 4:60MHz, 7:96MHz, 8:108MHz NG [R0]L=R1 R0=[@MAINCLKUEN]L R1=0 [R0]L=R1 R1=1 [R0]L=R1 ' GPIO setting R0=[@PINENABLE0]L R1=[@PINENABLE0_DATA]L [R0]L=R1 R0=[@GPIO_DIR0]L R1=`1100 ' PIO0_2 and PIO0_3 [R0]L=R1 R4=[@GPIO_NOT0]L R2=`100 ' PIO0_2 ' WS2812B driver ' R0 - data count ' R1 - data address (G1,R1,B1,G2,R2,B2 ....) ' R2 - OUT value ' R3 - data value ' R4 - GPIO address ' R5 - bit count ' R7 - wait count @WS2812B R0=15 R1=@DATA_LED @LOOP_DATA R5=#80 R3=[R1] @LOOP_BIT [R4]L=R2 R3&R5 IF !0 GOTO @ONE @ZERO ' 10-20clock R7=2 ' wait 2*3 = 6clock R7-=1 IF !0 GOTO -1 NOP [R4]L=R2 R7=3 ' wait 3*3 = 9clock R7-=1 IF !0 GOTO -1 GOTO @JOIN @ONE ' 20-10clock R7=5 ' wait 5*3 = 15clock R7-=1 IF !0 GOTO -1 NOP [R4]L=R2 NOP @JOIN R5=R5>>1 IF !0 GOTO @SKIP2 ' 7clock == 1 R1+=1 R0-=1 IF !0 GOTO @LOOP_DATA GOTO @END @SKIP2 NOP NOP NOP GOTO @LOOP_BIT @END R7=[@WAIT_N]L R7-=1 'wait R7*3+1 clock IF !0 GOTO -1 GOTO @WS2812B ' 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 ' clock setting @MAINCLKSEL DATA L #40048070 @MAINCLKUEN DATA L #40048074 @SYSPLLCTRL DATA L #40048008 @PDRUNCFG DATA L #40048238 ' WS2812B @WAIT_N DATA L 4000000 @DATA_LED DATA B 10,0,0, 0,10,0, 0,0,10, 10,0,10, 0,10,10

正確に10clockと20clockに合わせているので、きれいな800kHzの波形がでているはず!
Flashのwait stateを0にしながら、24MHzが正しいのかはちょっと謎ですが、動いてはいるようです。
asm15 アセンブリ言語、アセンブルは、asm15 Assembler でどうぞ!

IchigoJam/Ichigolatteで使えるマシン語生成、アセンブリ言語&アセンブラ、asm15 on GitHub

2017/09/15 23:55:00
#IchigoJam #Arm 

ゲームづくりの醍醐味は、どういう仕掛けでプレイヤーを喜ばせようかとあれこれ考えること。 プログラミングすればすぐに実現。まずは自分が楽しめて、人に喜んでもらって二度美味しい!

ゲームづくりに慣れてくるとやってみたくなる複雑な仕掛け。複雑になればなるほど高速なコンピューターといえども、遅くなります。 そこを、いかに効率がいいロジックを考えるかが腕の見せどころ!IchigoJamでのゲームづくりは、限界が低い分、すぐにその成長のチャンスに巡り会えます。

アルゴリズム(=考え方、作り方)でなんとかするのが常套手段ですが、奥の手に「マシン語」があります。

IchigoJam BASICは、インタプリタといって、分かりやすく書かれたプログラミング言語をプログラムでコンピューターが分かる言葉「マシン語」にひとつひとつ翻訳しながら動く仕組みでできています。 もし直接「マシン語」で話せたら、翻訳者(インタプリタ)が必要ない分、速くなるわけです。

IchigoJamをお持ちであれば、すぐに試せるマシン語入門。
はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

0と1の羅列であるマシン語を分かりやすくするためのアセンブリ言語。
初心者にもわかりやすい表記でつくったオリジナルのアセンブリ言語「asm15」としてまとめました。
Cortex-M0 Armマシン語表(抜粋)

Cortex-M0とは、Arm規格のCPUに要求される処理速度別のラインナップの内、最も遅い部類のシリーズ名です。 遅い代わりに、回路が小さく、省エネで、機能が少なくわかりやすいという大きな利点があります!

BoxHeadRoomさんが開発した、この「asm15」表記のアセンブリ言語をマシン語に変換するプログラム(アセンブラといいます)に足りなかった命令などを加えた「asm15 assembler」をGitHub上へのアップしました。
asm15 assembler for IchigoJam / IchigoLatte

まだ使い勝手が悪い部分や、足りない命令があります。
ぜひオープンオースプロジェクト「as15 on GitHub」にご参加ください!

電子工作マガジンの常連、田木屋商店さんのIchigoJam作品、ミサイル防衛ゲームや、落ちものゲームなどには効果的にマシン語が使われています。 まずは遊んで、改造して、少しずつ自分のモノにしていきましょう。

試しに、逆アセンブラを作ってみるのも楽しかも?
座学より体験、体験よりものづくり。ものづくりに勝る学習なし!

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法

アセンブラを使って楽しよう IchigoJamでまなぶ Armマシン語入門その8 / Let's Use Assembler! Learning Arm Machine Language on IchigoJam

2017/05/15 23:55:00
#Arm #asm #IchigoJam 

ちょっと大きなプログラムをマシン語で作っていくとき、変更するたびに、ジャンプ先のアドレスを計算するのが面倒になってきます。そんな時便利なのがアセンブラというツール。いままでやってきた対応する命令を2進数コードにする作業(ハンドアセンブル)を自動化します。
"Assembler" is useful tool to make pretty big program written in machine language. Numbers of address to jump are calculated automatically!

マシン語入門その6でつくった、画面に大きく文字を表示するプログラムをアセンブラを使ってやってみました。
For example, BIG SCREEN program on part 6.

10 POKE#700,240,181,148,70,0,38,15,24,9,35,27,2,201,24,184,93,192,0,18,24,0,36,19,93,128,37,24,70,40,64,0,208,1,32,8,112,1,49,109,8 20 POKE#728,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 RUN OK A=USR(#700,"+IchigoJam+!"):WAIT120


"asm15 - Arm Cortex-M0 Assember for IchigoJam"

ふたつある同じレジスタ上の足し算は、 R1=R1+1 と R1+=1 と区別する形に変更したので注意!
Notice! I modified the code R1=R1+1 and R1+=1

やってみよう! Challenge
- 任意の場所に大きな文字を表示するプログラムをつくろう (BIG CHARs on any position)
- ハーフグラフィクスを使った文字表示プログラムをつくろう (middium chars using half graphics)

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法

How to divide on IchigoJam Assembly

2017/04/28 23:55:00
#IchigoJam #asm #Arm #english 

About the machine language on IchigoJam, you cant't find how to divide (割り算@ja) on the Arm Cortex-M0 command table. It's right. Arm Cortex-M0 has no commands of division. Instead you can use the function to divide.

IchigoJam BASIC command "USR" pass an address of function to divide as the register R3 after IchigoJam 1.2b31. (IchigoJam-FAN to download the latest version 1.2b45)

asm: R1=3 → 00100 001 [num]
asm: GOTO R3 → 010001110 [R3] 000
BASIC: PRINT 100 / 3 → 33

If you want to use the number as remainder (R1), call it!

[0]=`00100 001<<8 + 3 [1]=`1011010 100000000 [2]=`010001111 0011 000 [3]=`0100011000 001 000 [4]=`1011110 100000000 ?USR(#800,100) 1

asm: R1=3 → 00100 001 [num]
asm: PUSH {LR} → 1011010 100000000
asm: CALL R3 → 010001111 [R3] 000
asm: R0=R1 → 0100011000 [R1] [R0]
asm: POP {PC} → 1011110 100000000
BASIC: PRINT 100 % 3 → 1

Enjoy programming with the simple machine language!

links
- Cortex-M0 Armマシン語表(抜粋)
-
はじめてのマシン語 - IchigoJamではじめるArmマシン語その1
- IchigoJam-FAN on Facebook

バーチャルマシンにディスプレイを接続、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-2命令とも互換性ある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サイクルは約21ナノ秒で計算は終了します(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+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. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法

ハンドアセンブルで大きな文字を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&=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の方法

- Armマシン語表(PDF版)

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