3年ぶりの沖縄高専、前回2年生だったメディア情報工学科5年生に、IchigoJamとマシン語を使ったアドバンス講座。
いつ来ても美しい、沖縄高専。


IchigoJamは、本当に1秒に5000万回計算できるのか!? 〜IchigoJamマシン語入門〜
まずは、IchigoJamのコマンド、TICKとCLTで、時間を測ろう。


インタプリタは、書き込まれたプログラミング言語を都度指定した機能を動かすソフトウェアです。 IchigoJam BASICはインタプリタ。


コンパイラは、事前にマシン語に変換してしまうソフトウェアです。C言語やZen言語は、コンパイラ。 インタプリタと比べて事前に変換する時間がかかりますが、実行は高速です。 実行しながら必要なところをコンパイルするなど、中間的な存在もあります。(参考、JIT


マシン語を打ち込み、USRコマンドで実行!マシン語で直接話ができました。


IchigoJamでは、10進法、2進法、16進法を簡単に変換できますよ!


1回の繰り返しのために4クロック(=1,000万分の1秒)かかっているので、それがないとするとちゃんと5,000万回計算する力がでてることが分かります。(ぜひ実機で確認してみましょう)

応用として、簡単な足し算するだけのコンパイラを IchigoJam BASIC で作ってみましょう。

10 I=0 20 INPUT N:IF N=0 GOTO 50 30 [I]=`00110<<11+N 40 I=I+1:GOTO20

マシン語 Rd+=u8 は、1から255まで足し算に対応しています。まずは、入力された数を足すマシン語を配列にセットする繰り返し。

50 [I]=#4770

何も書かずにエンターでNが0になります。リターンコマンドを最後に書いて、実行してみましょう。

RUN ?250 ?250 ? OK

1つの命令ではできない500足す計算をコンパイルしました。早速実行してみましょう。

?USR(#800,0) 500 OK ?USR(#800,500) 1000 OK

500足すプログラム、ちゃんと動いてますねっ


コンピューターが気になる高専生におすすめ。沖縄高専から車で30分、沖縄市にできた新拠点「Hanaわらび」、クラウドファンディングもスタート!
【Hanaわらび】始動。コザの街からすべての世代に可能性を届けたい。 - CAMPFIRE (キャンプファイヤー)

webアプリに興味ある高専生、勉強会付き会社説明会、近日開催!
2012年にスタートした一日一創をブログへ統合、webアプリ開発入門勉強会&会社説明会、開催!」


鈴木先生、金城先生、4年生学生と、コンピューターを活用した記念写真。


沖縄北部の名物、骨汁、豪快な豚肉、あっさりスープがおいしい!

links
- つなごう高専IT部、沖縄高専で情報セキュリティ実習&ICT委員会訪問、PCN美ら爆誕!

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

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


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

0000010100000101

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

最初の3bitと最後の2bitが足し算をしろという命令コード(c.addi)、一つ飛ばして5bitが使うレジスタの番号、残りの6bit(bit12とbit6-2)が足す数です。

000 0 01010 00001 01

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

POKE#700,`00000101,`00000101

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

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

1000000010000010

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

POKE#702,`10000010,`10000000

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

?USR(#700,1)

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

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

?USR(#700,100)

メモリを書き換えて5足すプログラムに変更してみましょう。(ビット演算で2bitずらし、c.addi命令を表す末尾01を足す。リトルエンディアンなので後8bitがメモリ上では先にくることに注意)

POKE#700,5<<2+1 ?USR(#700,100)

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

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

応用編「IchigoJam Rβでも輝くWS2812B、RISC-Vマシン語で10ナノ秒単位で制御する
RISC-V版マシン語はじめのいっぽの連載、はじめます!

- 連載、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言語からはじめるのは、免許取っていきなりランボルギーニ乗るようなもの

Linuxが動かないラズパイ、Raspberry Pi Pico が登場!

Arm Cortex-M0+を2つ搭載、更にプログラマブルな入出力「PIO」として、ミニミニコンピューターが8つ搭載! 合計10コアを使ったプログラミングが楽しめます。

まずは、MicroPythonを使ったお手軽開発
1. Raspberry Pi Pico をUSBで接続するとUSBメモリ的に認識します(二回目以降はBOOTSELを押しながら差し込み)
2. MicroPythonファームをコピー
3. CoolTermなどで、シリアル接続(115200bps/RTS off/DTR on/local echo off)
4. MicroPythonでプログラム!(LEDをON、OFF)

from machine import Pin led = Pin(25, Pin.OUT) led.value(1) led.value(0)

5. ミニミニコンピューターもPIO向けインラインアセンブリ言語(マシン語の元)を使って開発可能!
長いプログラムは、Ctrl−E を押してからペーストして、Ctrl-D で終了。

フルカラーLED、WS2812Bもこの通り!(sample src on GitHub)

次に、Picoのフルパワーを活用するC/C++での開発。環境構築からエルチカバイナリ生成まで on mac。

brew install cmake brew tap ArmMbed/homebrew-formulae brew install arm-none-eabi-gcc cd ~/ mkdir pico cd pico git clone -b master https://github.com/raspberrypi/pico-sdk.git cd pico-sdk git submodule update --init cd .. git clone -b master https://github.com/raspberrypi/pico-examples.git cd pico-examples mkdir build cd build export PICO_SDK_PATH=../../pico-sdk cmake .. cd blink make

blink.uf2ファイルをBOOTSELボタンを押しながら挿し認識した RPI-PI2 にコピーすればOK!
詳細は、公式ドキュメント「Getting started with Raspberry Pi Pico」をどうぞ!

新しいコンピューター、ワクワクしますね!


IchigoJamの基板の上に載せてみました。
The IchigoJam on Raspbery Pi Pico project started!

PCN仙台の荒木さんと訪問した仙台高専の縁で実現、小中学生向けローカルプロコン「みやぎプロコン」。 第二回目「みやぎプロコン2020」のオンラインプレゼンをオンライン審査!

オンライン参加してくれたこどもたちも含めて記念撮影!高専企画のローカルプログラミング&電子工作コンテスト、オススメです!ぜひ、高専生にも審査員してもらいましょう。

PCN仙台でのキラキラワークショップ」こどもたちの喜ぶ顔に応えたIchigoJamのコマンド、WS.LED。IchigoJam Rでも対応します!

パラメーターを調整し、WS2811にも対応!
IchigoJam BASIC 1.5b02 ファームウェア (RISC-V版 IchigoJam R用)
- IchigoJam 1.5β2
WS.LED対応、IOT.IN()/IOT.OUT対応(未検証)、VER(2)でキーボード状態が反映されないバグ修正

書き込み方はこちら参照、または「IJUtilities」でどうぞ!

WS2811/WS2812B前期/WS2812B後期とちょっとずつパラメータが違いました。だいたい大丈夫なように調整。

2811 (PL9823) 0 high 200nsec-500nsec, low 1200nsec-1500nsec 1 high 1200nsec-1500nsec, low 200nsec-500nsec reset 50,000nsec 2812b before 0 high 150nsec-550nsec, low 700nsec-1000nsec 1 high 650nsec-950nsec, low 300nsec-600nsec reset 50,000nsec 2812b new 0 high 220nsec-380nsec, low 580nsec-1000nsec 1 high 580nsec-1000nsec, low 580nsec-1000nsec reset 280,000nsec IchigoJam R 0 high 220nsec, low 1000nsec 1 high 1000nsec, low 580nsec

正式版をお楽しみに!バグ報告やご要望、お待ちしています!

マシン語の楽しさはコンピューターをフルに使い切れること。1秒間1億回、約100MHzで動作するIchigoJam RとWS2812Bで実験です!(仕様上の最高性能は108MHzですが、現在は96MHzで動作させています)

IchigoJam RのLEDは、ピン配置を見ると汎用入出力(GPIO)のポートAの14番(PA14)を使っていることがわかります。


GD32VF103のドキュメントより」
コンピュータの仕様書にメモリにマップされたアドレスが記載されています。このアドレスに対して書き込むことで出力を変更できます。 まずは単純に付けてみましょう。出力制御のGPIOx_BOPを使います。PA14は、#40010810の14bit目を1にすればONにできます。(Arm版の例)

MODE RV32C R11=#4 'R1=#40010810 R11<<=12 R11+=1 R11<<=8 R11+=8 R11<<=8 R11+=#10 R12=1 R12<<=14 [R11+0]L=R12 ' set BOP RET

みけCATさんが、RISC-V版にも対応してくれたasm15を使ってアセンブルできます。


Volume 1, Unprivileged Spec v. 20191213より」
RISC-Vのレジスタは31コ(X0は0固定)。X10〜X17がパラメータ、X10/X11が返り値かつ、元の値を保存せずに使えます。RV32Cの16bit縮小命令で使える範囲外ですが、X5〜X7/X28〜X31も元の値を保存せずに使えます。 asm15ではX??レジスタはR??レジスタとして記述します。

LEDを消すときは上記のset BOP を下記のように変更します

[R11+1]L=R12 ' clear BC

続いて、WS2812Bを制御しましょう、データシートを見ると、1コに付き24bitを、0の時は短く(220ナノ秒)ON、1の時は長めに(580ナノ秒)ONの後、しばらく(580ナノ秒)OFFという制御をすればいいことがわかります。 96MHzで動くRISCアーキテクチャーのコンピューターは1命令を10.417ナノ秒でこなします。220ナノ秒待つためにループを使います。

R10=100 R10+=-1 IF R10 GOTO -1

これで100回ループします。R10-=1という命令はないので、-1を足し算します。フラグがない代わりにレジスタが0じゃないかどうかで判定してのループです。

Armでは1ループ4clockでしたが、RISC-V(GD32VF103)ではどうなるでしょう?割り込みを禁止して、10秒間停止させる実験をしたところ、分岐は1clock短縮の2clock、つまり1ループ、3clockです。

あとは、Arm版のWS2812BのドライバをRISC-V用に改造です。

' LED WS2812B R11とR5を変更でOUTnに変更可能 ' R10 - 開始アドレス 送信byte数low,送信byte数high,G1,R1,B1,G2,R2,B2 .... ' R11 - base address (初期はRAM offset) ' R12 - temp ' R13 - data value ' R14 - data count ' R15 - bit count ' R5 - OUT value ' R8 - wait count ' R9 - wait count MODE RV32C @WS2812B SP+=-2 PUSH R8,0 PUSH R9,1 CPSID R10=R10+R11 'R11=#40010810 R11=#4 R11<<=12 R11+=1 R11<<=8 R11+=8 R11<<=8 R11+=#10 R5=1 R5<<=14 R14=[R10+0]W R10+=1 @LOOP_DATA R15=#8 R15<<=4 R10+=1 R13=[R10+0] @LOOP_BIT [R11+0]L=R5 R9=20 '3clock*20=60 <- 58-100 R8=20 '3clock*20=60 <- 58-100 R12=R13&R15 IF R12 GOTO @SKIP R9=8 '3clock*8=24 <- 22-38 @SKIP R9+=-1 'wait R9*3 clock IF R9 GOTO -1 [R11+4]L=R5 ' *offset 4byte R8+=-1 'wait R8*3 clock IF R8 GOTO -1 R15>>=1 IF R15 GOTO @LOOP_BIT R14+=-1 '+6clock IF R14 GOTO @LOOP_DATA CPSIE POP R9,1 POP R8,0 SP+=2 RET

CPSID/CPSIEに対応させたasm15を使うとアセンブルできます。

WS2812B光りました!12コのWS2812Bの実験プログラム for IchigoJam Rβ

10 POKE#700,61,113,34,192,38,194,243,119,4,48,51,5,181,0,145,69,178,5,133,5,162,5,161,5,162,5,193,5,133,66,186,2,3,87,5,0,5,5,161,71,146,7,5,5,131,70,5,0,35,160,85,0,209,68,81,68,51,246,246,0,17,226 20 POKE#73E,161,68,253,20,253,252,35,162,85,0,125,20,125,252,133,131,237,243,125,23,113,251,243,103,4,48,146,68,2,68,5,97,130,128 30 POKE#800,12*3,0 40 FOR I=0 TO 11:POKE#802+I*3,I,12-I,0:NEXT:?USR(#700,#800):WAIT30 50 FOR I=0 TO 11:POKE#802+I*3,0,12-I,I:NEXT:?USR(#700,#800):WAIT30 60 FOR I=0 TO 11:POKE#802+I*3,12-I,0,I:NEXT:?USR(#700,#800):WAIT30 70 GOTO 40

WS.LED命令として組み込む準備ができました。

家にリモコン、いくつありますか?
IoTセンサーとリモコン制御プログラミングで、手軽に自作のスマートホーム!

寝室のエアコンの自動運転がいまいちで、寒かったり暑かったりして起きてしまう問題。 IchigoJamと赤外線LED、sakura.ioを使って、家のエアコンをスマホ制御できるようにして一歩前進。 IchigoJamのプログラムはとってもシンプル!

10 N=IOT.IN() 20 IF N=0 GOTO 10 30 ?N:F=FILE():R=10:LRUN N,200

LRUNを使っているので、このプログラムを保存してから、実行。 sakura.ioからデータ受信する IOT.IN() で信号を受け取り、エアコン停止のファイル1、28℃設定ONのファイル2、29℃設定ONのファイル3を呼び出します。(リモコン制御部分のプログラムは後述)

次はスマホ側、sakura.ioの連携サービスとしてWebSocketを設定し、スマホからの操作用のプログラムはJavaScriptで操作用アプリを作ります。 HTML/CSS/JavaScriptの基本スキルと、サンプルプログラムがあればカスタマイズし放題です。

IchigoRemocon IoT src on GitHub

高精度温湿度計SHT-31を使った、家のIoT温湿度計と合わせ、あとは快適に眠っていられる、ちょうどいいしきい値を決めるのみ。

温度が下がりすぎて寒くなって設定を緩めたらどんどん暑くなっていった、夜の記録。

IchigoJamで、赤外線を見よう!
ご家庭のほとんどのリモコンは赤外線リモコン。赤色より少し低い周波数の光を使って信号をリモコンから本体へと届けています。 目には見えませんが、赤外線受光モジュール(センサーの一種)を使って観測しましょう。

赤外線リモコン受信モジュール GP1UXC41QS (秋月電子で50円)を、IchigoJamにつなぎます。 データシートを見ると、丸い部分を手前にして、左から、信号出力のVout、GND、VCCの順。それぞれIchigoJamのIN1、GND、VCCに接続し、早速実験プログラム! (電源電圧VCCは2.7V〜5.5Vとあるので、5Vを使ってもOKです)

1 ?IN(1);:CONT

ずらっと1が並びますが、赤外線受光モジュールに向けて、リモコンボタンを押すと、0001010 などと、反応します。これが信号で、ボタンごと、機種ごとに違うものが送られています。

いろいろなリモコンで試してみましょう。

どんなリモコン信号を受けたら動かすだけなら、これでOKですが、信号の内容によって動きを変えたい場合は、もうひと工夫必要です。

リモコンの原理とマシン語活用
リモコンは赤外線を1秒間に3.8万回、38kHzの点滅を基本として、太陽光などに含まれる赤外線と区別しています。 その点滅があるときがON、無い時がOFFとし、ON時間とOFF時間を交互に計測し、一定時間OFFが続いたら一連の信号が終了、その一連の信号を見て判断すればOKと原理はシンプルですが、IchigoJam BASICでは速度が間に合いません。

そこでマシン語!以前作った、リモコン送受信コードを流用して、プログラム領域にデータを格納し、LRUNを使ったファイル連携で簡単に使えるようにしました。

1 'IR-REMOCON 100 INPUT"0:SEND 1:RECV ",N 110 IF N=0 N=USR(#D00,0):GOTO 100 120 L=USR(#D00,1) 130 ?"LEN:";L 140 FOR I=0 TO L-1 150 IF I%64=0 ?:?"POKE#";HEX$(#E00+I); 170 ?",";PEEK(#E00+I); 180 NEXT:? 190 GOTO 100 200 L=USR(#D00):LRUN F,R POKE#D00,112,181,114,182,14,34,18,2,138,24,0,40,53,208,80,35,27,3,1,51,27,3,1,36,164,4,2,38,54,2,2,62,0,33,0,240,37,248,1,49,33,66,28,209,29,104,0,45,247,209,228,8,0,32,0,33,0,240,26,248,1,49 POKE#D3E,33,66,17,209,29,104,0,45,247,208,17,84,1,48,0,33,0,240,15,248,73,28,33,66,6,209,29,104,0,45,247,209,17,84,1,48,176,66,231,209,0,33,17,84,1,48,98,182,112,189,156,37,173,0,1,61,253,209 POKE#D78,112,71,80,35,27,2,1,51,27,4,4,51,1,36,0,38,17,92,73,0,0,41,234,208,28,96,0,240,22,248,30,96,0,240,19,248,1,57,247,209,1,48,17,92,73,0,0,41,221,208,0,0,0,0,0,240,8,248,0,0,0,0,0,240,4,248 POKE#DBA,1,57,245,209,1,48,226,231,154,37,1,61,253,209,112,71

プログラムを入力し、実行すると 0:SEND 1:RECV と、送信するか受信するか聞いてきます。 まずは1、エンターで受信。リモコン信号を受光モジュールに当てると、#D00に記録した内容が表示されます。

0または単にエンターで、赤外線を発信します。OUT1とGNDに赤外線LEDを付け、本体に向けてやってみましょう!無事制御できましたか!?

1行目に何の信号か分かるように名前をつけて、保存しておきましょう(EEPROMを使えば追加で最大128種類保存可能)。 あとは、好きなようにプログラムを作るだけです。

USBシリアルでPCと接続している場合、受診時に表示されるPOKE文を保存しておき、上記プログラムと合わせて書き込むことでもリモコン信号を復元できます。

ちょっと詳しい解説編
1KBのプログラム領域、こんな風に使っています。
#C00-#CFF 256byte BASIC(溢れてないよう ?FREE() が768以下にならないよう注意!)
#D00-#DFF 256byte マシン語(赤外線送受信)
#E00-#FFF 512byte 赤外線データ

マシン語のasm15ソースコードはこちら。以前作成したものを、#E00から最大512byte使って保存するように変更し、#D00から書き込んで使います。(エアコンの信号で440byte、収まります!)

' IN1 - 赤外線受光モジュール / OUT1 - 赤外線LED PUSH {LR,R4,R5,R6} CPSID '割り込み禁止 R2=#E 'PROGの後半 #E00 R2=R2<<8 R2=R1+R2 R0-0 IF 0 GOTO @SEND R3=#50 ' IN1 の address PIO0_10 R3=R3<<(24-(10+2)) R3+=1 R3=R3<<(10+2) ' PIO0_10 R4=1 R4=R4<<18 '初回信号オーバーフロー判定 R6=2 'R6=512-2 R6=R6<<8 R6-=2 R1=0 @BACK0 GOSUB @WAIT2526 R1+=1 R1&R4 IF !0 GOTO @END R5=[R3]L R5-0 IF !0 GOTO @BACK0 R4=R4>>(18-15) 'オーバーフロー判定変更 R0=0 '配列書き込み位置 @LOOP R1=0 @BACK1 GOSUB @WAIT2526 R1+=1 R1&R4 IF !0 GOTO @END R5=[R3]L R5-0 IF 0 GOTO @BACK1 [R2+R0]=R1 R0+=1 ' 1byteずつ R1=0 @BACK2 GOSUB @WAIT2526 R1=R1+1 R1&R4 IF !0 GOTO @END R5=[R3+0]L R5-0 IF !0 GOTO @BACK2 [R2+R0]=R1 R0+=1 ' 1byteずつ R0-R6 IF !0 GOTO @LOOP @END R1=0 [R2+R0]=R1 R0+=1 CPSIE ' 割り込み許可 POP {PC,R4,R5,R6} @WAIT2526 R5=156 ' 1cycle '(48000/38*2=2526 - overhead 14cycle - 6) / 4 = 626 (=156*4) R5=R5<<2 R5-=1 ' 1 cycle IF !0 GOTO -1 ' 分岐するとき3cycle しないとき1 - 4*loop-2 RET ' 3cycle @SEND R3=#50 ' OUT1 の address PIO1_0 R3=R3<<8 R3+=1 R3=R3<<16 R3+=`00000100 'PIO1_0 R4=1 'PIOオン用 R6=0 'PIOオフ用 'R2 データが格納されたアドレス 'R0 == 0 @ON R1=[R2+R0] R1=R1<<1 ' 2倍する R1-0 IF 0 GOTO @END @ON_LOOP [R3]L=R4 GOSUB @WAIT631 [R3]L=R6 GOSUB @WAIT631 R1-=1 IF !0 GOTO @ON_LOOP R0+=1 @OFF R1=[R2+R0] R1=R1<<1 ' 2倍する R1-0 IF 0 GOTO @END @OFF_LOOP NOP NOP GOSUB @WAIT631 NOP NOP GOSUB @WAIT631 R1-=1 IF !0 GOTO @OFF_LOOP R0+=1 GOTO @ON @WAIT631 'use R5 R5=154 ' 1cycle '(48000/38/2=631 - overhead 5cycle (+10/2) - 5) / 4 = 154 R5-=1 ' 1 cycle IF !0 GOTO -1 ' 分岐するとき3cycle しないとき1 - 4*loop-2 = 618 RET ' 3cycle

受信:ONになっている時間、OFFになっている時間を交互に数えて、メモリに書き出す
送信:メモリを読み、38kHzで点滅するONとただ待つだけのOFFを長さ分だけ繰り返す
(参考、はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

まとめ
プログラミング、言語はいろいろ違っても基本はシンプルです。
大人も子供も学生も、まずは楽しく改造して、ひとつひとつやれること増やしていきましょう!


PCで動く、IchigoJam ap を使って、楽しんでくれたドランクドラゴンのみなさん!


jigインターン、プログラミングできる学生向け、超高速IchigoJam入門&プログラミング大喜利まで1時間!はーどによる「jig.jpオンラインインターン2020 6日目!」に作品あり! アニメーション、音楽、ゲーム、サクッとその場で作れて好評でした!はんだづけもぜひチャレンジしてね!

教え方、または、ゆっくり復習したい方は、こちらの動画をどうぞ!

総合2コマから始める鯖江の小学校プログラミング! 総合的な学習の時間、IT遊具、クラブ活動、地域ICTクラブ、高度IT人材へのベストプラクティススライドPDF

かわくだりゲーム、Hana道場での改造例。

links
- IchigoJamを赤外線リモコンで簡単遠隔操作!スマートホーム実現に向けて、信号の解読にも挑戦しよう!

IchigoJamで楽しく学ぶマシン語入門の16回目、今回はフラグを使ってみましょう。 (キャリーフラグなど対応追加したオープンソースのコアacm0cpu更新、IchigoJam webのマシン語エンジン更新、asm15に新表記追加)

(フラグテクニックも使って、74byteまで縮んだ「オールマシン語かわくだりゲーム」スピードをPOKEで改造するまで動画)

計算結果がゼロかどうかを判定するゼロフラグを使って分岐したりループを作ったりしてきましたが、フラグはそれだけではありません。

Z 結果が0かどうかのゼロフログ
C 桁溢れが分かるキャリーフラグ(引き算は2の補数の足し算になる)
N 結果がマイナスかどうかのネガティブフラグ
V 符号付き計算が破綻したかのオーバーフローフラグ

IchigoJam BASICなどのプログラミング言語では、等号や不等号とIF文を使って判定していますが、実際はこれら4つのフラグが使われています。 試しに、パラーメーターが15以上かどうか判定してみましょう。(CC = キャリーフラグが立っていない時分岐)

R1=0 R0-15 IF CC GOTO 2 R1=1 R0=R1 RET

asm15でアセンブルしたものをIchigoJamでメモリに書き込み、動かしてみましょう。

POKE#700,0,33,15,40,0,219,1,33,8,70,112,71 ?USR(#700,15) 1 ?USR(#700,14) 0

うまく判定できてますね!(-15は、2の補数表現との足し算となるため、15より大きいと桁あふれでキャリーフラグが立ち、CCでキャリフラグが立っていない時はスキップ。)

こちらがフラグ判定に使用できる条件の一覧です。

- フラグ判定
EQ Z=1, ゼロフラグセット(一致) Equals =0
NE Z=0, ゼロフラグクリア(不一致) Not Equals =!0
CS C=1, キャリーフラグセット =HS
CC C=0, キャリーフラグクリア =LO
MI N=1, ネガティブフラグセット
PL N=0, ネガティブフラグクリア
VS V=1, オーバーフローセット
VC V=0, オーバーフローリセット
AL 常に分岐

- 符号なし
HS C=1, 以上, Higher or Same =CS
HI C=1 and Z=0, より大きい, HIgher
LS C=0 or Z=1, 以下, Lower or Same
LO C=0, より小さい, Lower =CC

- 符号付き
GE N=V, 以上
LT N!=V, より小さい
GT Z=0 and N=V, より大きい
LE Z=1 or N!=V, 以下

32bitのレジスタ、符号付きとして使うか、符号なしとして使うかはあなた次第!
いろいろ試してみましょう。

また、キャリーフラグを使った計算命令を使うと大きな桁を使った計算ができます。 IchigoJamの配列を4つ使った8byte、64bitの足し算引き算をしてみましょう。
足し算 [8-11] = [0-3] + [4-7] (下位バイトを先とするリトルエンディアンとしています)

20 CLV 30 LET[0],-1,-1,0,0 40 LET[4],1,0,0,0 60 FOR I=8 TO 11:?[I];" ";:NEXT:?

足し算するマシン語を作ります。

PUSH {R4,PC} R0+=R1 R1=[R0+0]L R2=[R0+1]L R3=[R0+2]L R4=[R0+3]L R1=R1+R3 R2+=R4+C [R0+4]L=R1 [R0+5]L=R2 POP {R4,LR}

R2+=R4+Cで、直前の足し算によるキャリーフラグ分も合わせて足します。 R1+=R3 ではフラグが変化しないので、R0〜R7までのみ使える、R1=R1+R3を使いましょう。 (参考、Cortex-M0 Armマシン語表 asm15

asm15でアセンブルしたマシン語と呼び出しコードを加えて実行してみます。

10 POKE#700,240,181,8,68,1,104,66,104,131,104,196,104,201,24,98,65,1,97,66,97,240,189 50 U=USR(#700,#800) RUN 0 0 1 0

無事、桁上りの計算ができてます!
キャリーフラグ付きの引き算 Rd-=Rm+!C を使って引き算も実装してみましょう。 (引き算は、桁あふれが発生していないときにキャリーフラグがセットされるので、!Cを使います)

@fujitanozomuさんによる、フラグを使ったスゴ技を紹介します。 オールマシン語のかわくだりゲームのキー入力判定、素直につくるとこういう感じで6命令12byte

R0-28 IF !0 GOTO 2 R4-=1 R0-29 IF !0 GOTO 2 R4+=1

左キーが28、右キーが29と連続していることとフラグを使うと、下記のように4命令8byteで実装可能です

R0-=28 R0-=1 IF HI GOTO 2 R4+=R0+C

28減算し、判定したいものを0か1にした後に、更に1減算しつつ符号なしで1より大きければスキップ。 R0が0、つまり左キーの時だったときは、R0が-1になっていて、右キーの時はキャリーフラグが立っているので+1されます。 見事!

IchigoJamのAPIアクセス、#C0をキャッシュし、差分メモリアクセスを使う技を使うと、かわくだりゲームが37命令74byteで実現できました。

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

asm15でアセンブルした実行可能にしたものがこちら。(IchigoJam webではAPI未対応のため動きません)

10 POKE#700,240,181,192,39,190,136,253,137,187,137,152,71,15,36,32,70,5,33,168,71,236,32,176,71,32,32,59,136,152,71,23,33,168,71,42,32,176,71,10,32,176,71,3,32,251,138,152,71,123,137,152,71,28,56 20 POKE#736,1,56,0,216,68,65,32,70,5,33,59,138,152,71,0,40,226,208,240,189 30 U=USR(#700)

c4ij/zen4ij/rust4ij/Ruby on Jam でベンチマークしているスコア付きかわくだりゲームも、なんと82byte!

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

日本のスパコン富岳のコア、A64FXも同じArmのコンピューター(fujitsu/A64FX on GitHub)。 世界最速になったヒミツを探るためにも必要な、マシン語の基本、楽しく習得しておきましょう!

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

Zen言語で作るIchigoJamプログラム zen4ij、なんとZen言語開発者自ら早速改造!?

実機で動かすため、IchigoJamのはんだづけもしてくれました!無事LEDが光り、PCとのUSBシリアル接続もOK。久利寿とオンラインでしつつコンパクト版にトライ、動いた! 最終的なサイズは、112byte。C言語より4byte、Arm thumbのマシン語で2命令少ない!


zen4ij に、プルリク送ってもらって、こちらの環境でも確認、マージ完了!
(プルリク=プルリクエスト=改善案置いておくからよかったら引っ張って使ってねという意味、マージ=改善案を採り込むこと、共にGit用語)

Zen言語の使い方や、ビルドのコツを直接聞くことができた。サイズ縮小のポイントとして不要セクションの削減が肝だった。下記が改良版 かわくだりゲーム in Zen言語(switch構文がいい感じ)

const ij = @import("std15.zen"); export fn main() callconv(.C) i32 { ij.cls(); var x: i32 = 15; var score: i32 = 0; while (true) { ij.locate(x, 5); ij.putc(236); ij.locate(ij.rnd(32), 23); ij.putc('*'); ij.putc(10); ij.wait(3); switch (ij.inkey()) { 28 => x -= 1, 29 => x += 1, else => {}, } if (ij.scr(x, 5) != 0) { break; } score += 1; } return score; }

公平を期すため rust4ij も改良、ただ同じく不要セクションを消しても Rust 版では 124byteと、Zenの112byte、C(gcc -Os)の116byteに及びませんでした。 なぜ、サイズが違うのか?それは、それぞれのコンパイラが出力するマシン語に差があるからです。コンパクトなプログラムでいろいろ見比べると個性があっておもしろい!

果たして、どこまで小さくなるのか?
コンパイラを使わず、asm15マシン語でプログラムしてみたところ、82byteで実装できた! (追記、Thanks 100byte→98byte82byte by @fujitanozomuさん)

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

IcihgoJam APIは、アドレスの8bit範囲内に16bit範囲内の関数ポインタを書き込んであり、ファームウェアのバージョンが変わっても使用できるMSXのBIOS的な仕組みを採っています。(例、CLSの呼び出し)

'cls R7=#CC R7=[R7]W GOSUB R7

削減のポイントは、使える命令が限定されるハイレジスタ(R8以降)で、唯一関数内で値を保存せず使用してよいレジスタ、R12を繰り返し使用するAPIのキャッシュとして使用したところです。 だいぶ最適化が進むコンパイラ技術ですが、まだ発展の余地があるってことですね!

ちょうどネコのキャラクターパターンが96byte目からなので、#700からマシン語を書き込んでも動きます!

POKE#700,240,181,204,39,63,136,184,71,15,36,0,37,196,39,56,136,132,70,127,137,32,70,5,33,184,71,236,32,224,71,32,32,192,38,54,136,176,71,23,33,184,71,42,32,224,71,10,32,224,71,3,32,214,38,54,136 POKE#738,176,71,202,38,54,136,176,71,28,40,0,209,1,60,29,40,0,209,1,52,32,70,5,33,208,38,54,136,176,71,1,53,0,40,219,208,104,30,240,189 ?USR(#700)

コンピューターに初めての触るこどもから、ガチなエンジニアまで、幅広く楽しめるIchigoJam。 そのヒミツを書いた「情報処理 2020年8月号 (日本語) 雑誌」 now on sale!

この度、情報処理学会の会誌『情報処理』に「プログラミング...

上松 恵理子さんの投稿 2020年7月20日月曜日


今日のおやつ、いちごティー

情報処理学会「情報処理」8月号の特集「プログラミング教育の最前線」(目次記事一覧
モダン言語全盛時代に、なぜBASICか?なぜはんだづけなのか?6,586文字、7ページで綴りました。コンピューター好きな人、増やしましょう!

気になるモダンプログラミング言語の1つ、Rust(ラスト)。Denoとrust4ijをきっかけに入門。 IchigoJam と、Arm Cortex-M0 マシン語を使って、C言語の代替手段にできるか検証開始!

Rustの特徴のひとつ、ゼロコスト抽象化は、C言語の構造体と構造体ポインタを使った関数群や、関数ポインタをすっきり記述できていい感じ。 trait(トレイト)は、C言語では関数ポインタで実装するJavaのインターフェイス的なもの、こちらもきれいな記述でゼロコスト。

メモリを扱う際にもよく使う配列、うっかり領域外アクセスがバグやセキュリティホールになりがちです。Rustでどのように防いでいるか逆アセンブルして確認してみました。

let mut ar:[i32; 4] = [1, 2, 3, 4]; ar[param as usize] = 100;

長さ4の配列を作って、パラメータで指定された場所に100を書き込むプログラム。4以上の値が指定されると領域外アクセス、まずいです。

14: 9100 str r1, [sp, #0] 16: 2803 cmp r0, #3 18: d80c bhi.n 0x34 1a: 0080 lsls r0, r0, #2 1c: 4669 mov r1, sp 1e: 2264 movs r2, #100 ; 0x64 20: 500a str r2, [r1, r0]

Rustでコンパイルしたコードを見てみると、配列の初期化後、「cmp r0, #3」で配列の長さ-1と比較し「bhi.n 0x34」で範囲外ならジャンプ(GOTO)させ、不正なプログラムを走らせないように保護するコードが生成されていました。 2命令、たった2クロックのオーバーヘッドなので、サイズ的にも速度的にも問題になることは少ないでしょう。

領域外として判定された際のジャンプ先が panic 処理となります。panic_handlerとして自由に定義できるので、試しにただのループに変更してみました。

use core::panic::PanicInfo; #[panic_handler] fn panic(_info: &PanicInfo) -&lt; ! { loop {} }

すると、なんと、下記のように領域外チェックが消えました。仕様なのかどうなのかは未調査ですが、これならそこそこ攻めた作りもできそうです。

14: 9100 str r1, [sp, #0] 12: 0080 lsls r0, r0, #2 14: 4669 mov r1, sp 16: 2264 movs r2, #100 ; 0x64 18: 500a str r2, [r1, r0]

panic 処理内で、volatile_storeとunsafeを使って、不正なメモリアクセスをあえて実行して、IchigoJam BASICの「Segmentation Fault」を起こすようにすると使いやすくなります。

#![no_std] #![no_main] #![feature(core_intrinsics)] use rust4ij::std15::*; use core::intrinsics::volatile_store; use core::panic::PanicInfo; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { //putstr("panic!\n".as_bytes().as_ptr()); // show message (+23byte) unsafe { volatile_store(1 as *mut i32, 1); } // invoke the Segmentation fault for safe (+32byte) loop {} } #[link_section = ".main"] #[no_mangle] fn main(param:i32, _ram:i32, _rom:i32, _divfunc: fn(u32,u32) -> u64) -> i32 { let mut ar:[i32; 4] = [1, 2, 3, 4]; ar[param as usize] = 100; let mut sum:i32 = 0; for i in 0..ar.len() { sum += ar[i]; } return sum; }

転送して、?USR(#800,4) と領域外アクセスするとエラーとなることが確認できます。不正アクセスするマシン語は下記のように、最小限のマシン語に変換されています。

44: 2001 movs r0, #1 46: 6000 str r0, [r0, #0]

前後に余計なコードが混ざってしまうため、数byte単位でも容量がほしい、現行 IchigoJam の移植はちょっと無理にしろ、かなり広範囲でのマイコン組み込み開発に良さそうです。

Rust、なかなかかわいく思えてきました。 次は、所有権の概念を使った動的メモリ確保を多用するメモアプリとかの実装にチャレンジして、より仲良くなってみようと思います。


創造はじめのいっぽ,Apple I/TK-80/MSX が生んだ感動をすべての子どもたちへ!」と題したIchigoJamにかけるこどもプログラミングの想いでも触れたコンピューター自体を理解し、好きになることと大切さ。

使うだけじゃなくて、IchigoJamみたいな素晴らしいハードウェアのきっかけになったのはすごく嬉しいことだ。あれは素晴らしい。
また英国がコンピューターの歴史を作るときが来た——「英国のMakerムーブメントは世界一」Raspberry Pi財団代表エベン・アプトンが語る | fabcross

Raspberry Pi、安価なパソコンをこども達に!想いに共感しに出会った2013年。小学生にはもう一段簡単なパソコンが必要と思って作った IchigoJam。 イベントを通じて、創始者、Eben Upton とも日本で会え、それがきっかけで産まれた IchigoJam RPi は、ラズパイで使えるサードパーティーOSとしても紹介してくれました!

世界中のこどもたちが「創造はじめのいっぽ」を楽しく踏み出せるよう、日々精進。

プログラミング好きのこどもを増やそう、IchigoJam。
プログラミング好きのおとなの多数のご協力あり、日本に世界にじわじわ成長中です。


BASICでプログラミングの基本を楽しんでから触れてみてほしい、コンピューターの真の力を引き出すマシン語。 スパコン富岳も世界一の計算力を実現するには、マシン語の理解は必須です(Fujitsu's Arm A64FX)。 ファミコンの日の6502系マシン語に続き、MSXでなじみ深いZ80版も試作しました。

Z80の開発者は、Intelの初代マイクロプロセッサ4004にも名前を残す、静岡出身の日本人、嶋正利さん! 初期の日本のパソコン時代、多くのパソコン少年が熱中したマシン語はZ80でした。 欲しかったファミリーベーシックではなく、MSXが届いた私にとってのファーストマシン語もZ80です。


3E は、Aレジスタへの8bitの値を代入するマシン語。C9はたぶん一番有名なZ80マシン語、サブルーチンからのリターン。IchigoJamのUSR関数で呼び出すと、Aレジスタの値が返ります。

今回の試作に使ったのは「libz80 - Z80 emulation library」C言語で書かれたZ80エミュレーターです。ライセンスに明記がなかったため非公開ですが、IchigoJam web で遊べるようにするとか、マイコン上で動かせるよう調整するのもいいかも?

ただ、ネイティブで使ってこそ、マシン語。 ちっちゃくて、100円しかしない IchigoJamのCPU、Arm Cortex-M0 の底力、引き出してみませんか? 素直で書きやすいマシン語なので、ちょっと慣れたこどもはもちろん、かつてチャレンジした大人の方にもオススメです!
はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

日本政府のSNSにて、Hana道場の運営する、NPO法人エルコミュニティー竹部代表が登場!IchigoJamや、イチゴ帽子、どこかで見た後ろ姿が写ってます。 ict4e原さんのアフリカ企画でルワンダのPCNキガリと鯖江の子供をつないだときの様子!

links
- IchigoJamでつながるルワンダこどもと鯖江のこども! Hana道場で踏み出す新たな一歩! ビーズ x はんだづけがかわいい
- ファミコンの日、6502マシン語が使えるIchigoJam試作

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