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マシン語でプログラムしてみたところ、96byteで実装できた! (追記、Thanks 100→98byte @fujitanozomu さん!98byte → 96byte @fujitanozomuさん)

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

1983年7月15日、ファミコン発売日、プログラマー人生のきっかけだった。 CPUは、MOS 6502(1975年)から、BCDとメモリーマップ機能を省き、音源とDMAを追加した Richo 2A03。 6502は、Apple I/IIや、アメリカでのMSX的存在であるコモドール社製のパソコンに搭載されたので、きっとアメリカではZ80よりメジャーなはず。

Arm設計の参考になったとも言われる、RISCアーキテクチャーに通じるシンプルな設計が今見ても美しい。 記念に、オープンソースな6502のC言語実装「Fake6502 CPU emulator core v1.1」を使って、IchigoJam ap で、6502マシン語が使えるようにしてみました。


6502のエミュレーションはコンパイルすると数十キロ程度、通常のIchigoJamには組み込めないので、ひとまず IchigoJam ap で仮実装して、遊んでみた。
渡されたパラメータに1を足す、簡単なマシン語プログラムが動いた!

#18 = CLC、キャリフラグのクリア
#69 = ADC、Aレジスタにキャリーと指定数値(1)の足す
#60 = RTS、サブルーチンからの復帰(RETURN)
(参考、NES研究室 - 6502

なんと、秋月電子で、現役6502系CPUが購入可能!
8ビットマイコン W65C02S6TPG-14: マイコン関連 秋月電子通商-電子部品・ネット通販
開発者のいろんな想いが詰まったコンピューターのアーキテクチャー、6502は知っておいて損なし!


IchigoJam web の6502版を作って、ぜひ遊んでもらおうと思いましたが、関数ポインタがうまくWebAssemblyでコンパイルできず・・・。またがんばります。


MSXとの出会い、頼んだのはこちらファミリーベーシックだった。


50音順に並ぶカタカナ。この配置がメジャーだったら、ローマ字派はここまで多くはならなかった!?


裏面。ファミリーコンピュータ用 キーボード 任天堂株式会社。IchigoJamで使えるキーボードとして改造したい。

links
- 日本政府広報に登場、国産PC、IchigoJam! Z80マシン語が使えるIchigoJam試作

ITの楽しさ伝える、福井県立科学技術高校、2年生全員向け講演。

体育館に掲げられている「技」と「創」が良い!


情報技術に関してとリクエストいただいたので「情報技術の活かし方・遊び方・学び方」と題したプレゼンテーション、IchigoJamゲームのライブコーディング、質疑応答の50分。


IchigoJamのCPU、凄い!という感想。作った私も凄いと思ったほどです。100円なのにね。スライドの16:9化に伴い、京と富岳を並べる形にアップグレード。 同じ予算なのに性能100倍!世界一のスパコン「富岳」の活躍、これからも楽しみです。


こちら、引退したスパコン「京」で、実際動いてたコンピューター。かっこいい!
7億6000万個のトランジスタでできているこのコンピューターを、88,128個つないで動かしていたのがスパコン京。 CPUの動作周波数は2GHzとみなさんのスマホと同等です。IchigoJamの48MHzともたかだか40倍しか変わりません。

計算が速い=時間の創出=社会が豊かになる

IchigoJamで三平方の定理を使った斜めの距離計算をやってみましょう。

10 ?"CALC LEN" 20 INPUT "X?",X 30 INPUT "Y?",Y 40 N=X*X+Y*Y 50 ?"X*X+Y*Y=";N

横(X)と縦(Y)の長さを入れてもらって、それぞれ自乗したものを足した数(N)。これのルートをとれば斜めの距離がでます。 ルートを計算するコマンドはないので、ひとまずシンプルに愚直に繰り返して求めてみましょう。

60 L=0 70 L=L+1 80 IF L*L<=N GOTO 70 90 L=L-1 100 ?"SQRT(";N;")=";L

長さ(L)を1足しながら自乗した値が先程計算したNを超えたら、1戻して表示するプログラム。 X=3、Y=4で結果Lが5とでました!

30, 40 だと、50。100,100だと144。数が大きくなると遅くなります。IchigoJam BASIC は、1コマンド1文字ずつ間違いがないか確認しながら丁寧に実行するので、間違っても安心な楽ちん言語ですが、その分速度が犠牲になっています。

そんな時は「マシン語」、コンピューターの能力をフルに引き出してみましょう。 BASICに似たマシン語生成用プログラミング言語 asm15 を使ったルートの計算はこちら。

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

変数の代わりにコンピューター内部にある最も高速なメモリ、レジスタ(R0〜)を使います。IF GE GOTO は、もし、直前の計算結果が0以上だったらを意味する(GE)を使って、ループしています。 やってることはほぼ同じですね。

C言語と同様、これをマシン語に変換します。asm15 assemblerを使ってできたマシン語をメモリに書き込み、USR関数を使って呼び出しましょう。

POKE#700,0,33,1,49,10,70,82,67,144,66,250,218,72,30,112,71 ?USR(#700,25) 5 ?USR(#700,10000) 144

計算できました!大きな値でも一瞬ですね!
IchigoJamは1秒間5000万回、富岳は100京回、マシン語を使うとコンピューターの性能をフルに引き出せます。

汎用的に使えるC言語(IchigoJam BASICもC言語で作っています)、スマホやPC、サーバーでも気軽に動かせるJavaScript、並列処理に便利なC言語に似たGLSLなど、用途に合わせたいろんな言語も最終的にはそれぞれのコンピューターに合わせたマシン語となって動いています。

コンピューターと人間はステキな補完関係、うまく付き合っていきましょう!


HTMLはじめのいっぽ
30分でOK、HTMLからはじめる、自分だけのアプリづくり入門。CSSでデザインに凝るもよし、JavaScriptで動きに凝るも良し。


科学技術高校生による福井県てぬぐいがステキです!オリジナルスマホアプリづくりにもぜひチャレンジしたくなったら、どうぞお声がけください!


ハードウェア好きなら、栃木県立栃木工業高校による「SkyBerryJAM」のように、IchigoJamをベースにした、オリジナルハードづくり、楽しいかも?


講演資料「情報技術の活かし方・遊び方・学び方
CC BY のオープンデータです。ご活用ください!

福井県立科学技術高校のみなさん、楽しい時間、ありがとうございました!

京都府舞鶴市、東舞鶴の商店街の大門通り沿い、元舞鶴高専の先生で、PCN舞鶴代表の町田さんによる、プログラミングもできる電子工作&模型屋さん「舞鶴電脳工作室」オープン!(アクセス


こどもから大人まで、ワクワクするお店!店頭の模型や、電子工作見ていると、ちょっと覗いてみたくなりますね。


福井県大野市、元福井高専の先生、前川さんによるPCN大野研究所で採れたイチゴを直送!


電波の先生、前川さん。電波を見るツール「Nano VNA」が良い感じとのこと。 オープンソースな高周波解析機。日本語ユーザーガイドもオープンソースで作成されていました。 コロナ禍、飛行機に電波を当てて反射させての通信実験、旅客機は減っても、貨物は比較的飛んでいるとのこと。人には見えないものが見える力、広めましょう!


真っ赤になった、甘いイチゴ!いつの日かしたい、イチゴ収穫ロボコン。


舞鶴電脳工作室で仕入れた電子部品と大野さんイチゴのコラボ!IchigoJamでMAX7219マトリクスLEDチェーンの制御できました!


風通しの良い店内、ミニ四駆コースがこどもに人気とのこと!IchigoJamを使った計測システムも計画中とのこと!


抵抗、コンデンサ、LED、基本パーツが揃っていてうれしい、電子パーツコーナー。


15 マイコン、と書かれた電子工作用部品コーナーにはIchigoJamも。右下に見慣れない部品を発見!


8x8の赤色マトリクスLEDx4、全部に制御用のIC、MAX7219搭載でデイジーチェーン対応。これで400円は安い!

MAX7219制御のマトリクスLEDx4のIchigoJam制御チャレンジ!

MAX7219/MAX7221データシートをチェック、日本語ドキュメントがありますね。7セグLEDを8コまとめて制御するチップの様子。 4bitで数を指定すると7セグLED用にデコードするモードと、そのままストレートに8bit分出すモードがあります。8桁以上に対応するためにWS2812Bの用にデータを後ろにパスする仕組みがあります。 転送方式は16bitのSPI、FM音源チップYMF825などでやりました。

VCC 5V
GND GND
DIN シリアルデータ入力 - IchigoJamのOUT3
CS チップセレクト - IchigoJamのOUT1
CLK クロック - IchigoJamのOUT2

とIchigoJamと接続して、ひとまずBASICで実験!

500 @SPIW:OUT1,0:FORI=15TO0STEP-1:OUT2,0:OUT3,N>>I&1:OUT2,1:NEXT:OUT1,1:RTN N=12<<8|1:GSB@SPIW

付きました!(SHUTDOWNアドレス12のbit0に1で表示)

SPIはBASICのままだとゆっくりなので、マシン語化して高速化。ライブラリとして整えて、サンプルプログラムと合わせてできあがり!

10 GSB@INIT 20 N=5:GSB@INTENSITY 30 S="0528":GSB@SHOW:WAIT30 40 S=" jig.jg ":FORK=0TO10:GSB@SHOW:WAIT10:S=S+1:NEXT 60 S="17th":GSB@SHOW 70 FORJ=0TO7:N=J&1*9+5:GSB@INTENSITY:WAIT10:NEXT:WAIT30 80 GOTO 20 200 @INIT 210 POKE#700,80,35,27,2,91,28,27,4,1,33,201,3,0,34,8,66,0,208,4,50,154,97,2,50,154,97,73,8,246,209,112,71 220 N=7:GSB@SCANLIMIT:N=1:GSB@DISPLAY:RTN 300 @SHOW:FORJ=0TO7:OUT1,0:A=(J+1)<<8:FORI=0TO3:C=ASC(S+I):N=PEEK(C*8+J):U=USR(#700,A|N):NEXT:OUT1,1:NEXT:RTN 400 @INTENSITY:N=10<<8|N&#F:GSB@CMD:RTN 410 @DISPLAY:N=12<<8|N&1:GSB@CMD:RTN 420 @SCANLIMIT:N=11<<8|N:GSB@CMD:RTN 500 @CMD:OUT1,0:FORI=0TO3:U=USR(#700,N):NEXT:OUT1,1:RTN

300 @SHOW S:display chars 4桁表示 (I,J,A,C,N,Uが破壊される)
400 @INTENSITY N=0-15 輝度設定 (I,Uが破壊される)
410 @DISPLAY N=0/1 表示のON/OFF制御、最初にONにする (I,Uが破壊される)
420 @SCANLIMIT N=0-7 使用する最後の行、今回は7まで8行全部使う (I,Uが破壊される)
500 @CMD cmd send x4 - 16bitのコマンドを4つ分送信 (I,Uが破壊される)


後ろにどんどんつなげることができるので、もっと長くしたり、縦16ドットにしてみたり、高速化するためにループをマシン語化してみるなど、必要に応じて改造しましょう!

今回のプログラムの見どころ、4文字表示する処理300行@SHOWを丁寧に書くと、下記のような2重ループになっています。

300 @SHOW 310 FORJ=0TO7 320 OUT1,0:A=(J+1)<<8 330 FORI=0TO3 340 C=ASC(S+I):N=PEEK(C*8+J) 350 U=USR(#700,A|N) 360 NEXT 370 OUT1,1 380 NEXT 390 RTN

OUT1でCSを0に、4つ分のデータをSPI転送して、CSを1にすることでバケツリレー!

@SPIWのマシン語バージョンはこちら。asm15でアセンブルしてみましょう。

' @SPIW:FORI=15TO0STEP-1:OUT2,0:OUT3,N>>I&1:OUT2,1:NEXT:RTN @SPIW R3=#50 ' R3=#50010000 R3=R3<<8 R3=R3+1 R3=R3<<16 R1=1 R1=R1<<15 @LOOP R2=0 R0&R1 'F=N>>(15-I)&1 IF 0 GOTO @SKIP R2+=4 ' for OUT3 @SKIP [R3+`110]L=R2 ' OUT2,0:OUT3,F R2+=2 ' for OUT2 [R3+`110]L=R2 ' OUT2,1 R1=R1>>1 IF !0 GOTO @LOOP @END RET

OUTポートを変更する場合は、適宜プログラム内の数を変更しましょう(マシン語入門


小学生の頃、敷居の高さにあきらめたプラモデルの塗装。


塗装コーナー、人気とのこと!


美しい、塗装された模型!VRで塗装体験できるもの作ってみるのも入門にいいかも!?

新しい世界への入口「舞鶴電脳工作室&PCN舞鶴」でした!

links
- 舞鶴電脳工作室に子供パソコンIchigoJamの開発者の福野さんがご来店 | Cyber Workshop in Maizuru

以前紹介した、5コ950円で買える、安価でIchigoJam BASICだけでも使える超音波センサー「HC-SR04」。 他デバイスとの関係で、どうしてもBTNを使いたいとご要望あり。


できました!IchigoSodaにつないでIoT化していますが、IchigoJam、IchigoDakeでもOKです!

BTNはIchigoJam付属のボタンとSLEEPからの復帰のため外部でプルアップ抵抗をつなげていて、ANA(0)としてアナログ入力としても使えます。 つまり、CPU内部の設定としてはアナログになっているので前回紹介したマシン語で読み出そうとしてもうまくいきません。


BTNを割り当てているPIO1_4、LPC1114のデータシートの IOCON_PIO1_4 (アドレス#40044094) を見ると設定方法が書いてあります。 一度、マシン語を使ってどういう設定になっているか読み出してみましょう。

R0=[@IOCON_PIO1_4]L R0=[R0]L RET @IOCON_PIO1_4 DATA L #40044094

asm15でアセンブルして、読みだした値を8桁の2進法で表示

POKE#700,1,72,0,104,112,71,0,0,148,64,4,64 OK ?BIN$(USR(#700,0),8) 01000001

2:0 つまり最下位の0bit目から2bit目の3bitを使ったFUNCは1、次の2bitのMODEは0(プルアップなどなし)、次の1bitのHYSは0(無効)、6bit目はReservedとして予約されていて1を設定することになっていて、7bit目ADMODEが0でアナログ入力モードとなっています。

これをADMODE:デジタル入力+FUNC:PIO1_4と、`11000000 に一時的に変更する作戦でいきます。前回のカウントプログラムの前後にこの設定を足します。

PIO1のベースアドレスは #50010000、BTNは、PIO1_4の場所なので
?"#5001";HEX$(1<<(4+2),4) → #50010040 を@INに設定します。

'BTN(PIO1_4) <- digital R1=`11000000 R3=[@IOCON_PIO1_4]L [R3]L=R1 'count check R0=0 R1=[@IN]L @LOOP R2=[R1]L R0=R0+1 R2-0 IF !0 GOTO @LOOP R0=R0>>9 'BTN(PIO1_4) <- analog R1=`01000001 [R3]L=R1 RET @IOCON_PIO1_4 DATA L #40044094 @IN DATA L #50010040 'BTN=PIO1_4

アセンブルして、BASICのプログラムと組み合わせて、できあがり!

10 POKE#700,192,33,6,75,25,96,0,32,5,73,10,104,64,28,0,42,251,209,64,10,65,33,25,96,112,71,0,0,148,64,4,64,64,0,1,80 20 OUT1,1:OUT1,0:?USR(#700,0) 30 WAIT 5:GOTO 20

マシン語でカウントした値、R0をBASICに返す前に9ビット右シフト、つまり512分の1にしています。マシン語のカウントだと速すぎて大きな値になってしまうからですが、ここをいじると精度アップが見込めます。 ただ、IchigoJamでは1秒間に1.5万回くらい画面表示用の割り込みが発生するので、高い精度で計測するには割り込みを一時的に止める必要があります。

CPSID R0=0 R1=[@IN]L @LOOP R2=[R1]L R0=R0+1 R2-0 IF !0 GOTO @LOOP R0=R0>>5 CPSIE RET @IN DATA L #50001000 'IN1=PIO0_10

割り込みの一時停止は CPSID、最下位は CPSIE です。戻し忘れると何も操作できなくなってしまいます。下記、CPSIDした後リターンする危険なプログラム、SAVEした後、お試しください。

LET[0],#B672,#4770:?USR(#800,0)

完成した、超音波センサー、TrigをIN1に接続し、精度16倍バージョンがこちら!

10 POKE#700,114,182,0,32,3,73,10,104,64,28,0,42,251,209,64,9,98,182,112,71,0,16,0,80 20 OUT1,1:OUT1,0:A=USR(#700,0):?A 25 LED A<300 30 WAIT 5:GOTO 20

一定距離から近づくとLEDがついてお知らせ、リレー経由でスゴイ照明と組み合わせると防犯ライトにもなりますね!

5 B=0 26 IF B=0 AND A<300 B=1:N=N+1:IOT.OUT N:?"IOT!";N:WAIT30 27 IF B=1 AND A>=300 B=0

IchigoSodaを使って通ったらIOT.OUTでカウントを送るように改造も簡単です!
福井県で始まった「混雑状況オープンデータ」との連携も便利そう。


Let's IoT with IchigoJam x various sensors!

links
- 200円 超音波距離センサー HC-SR04 を IchigoJam で使う方法 - Armマシン語で時間を測ろう
- 今週末は越前がにロボコン! IchigoJamで超音波センサーをIN1とは違うポートで使う方法 BASIC編 マシン語編

初心者にはオススメできないC言語ですが、多くの高専の授業で登場するC言語

#include <stdio.h> int main() { printf("Hello World!\n"); return 0; }

このC言語のプログラムを実行すると

Hello World!

と出ます。これで感激できるのはC言語を作った人と、環境構築に苦労した人くらい。大抵の人は「で?」と思うだけでしょう。 なぜ # で始まる? <stdio.h> はHTMLのタグとは違う? studio のつづり違い?など1行目から分からないことだらけです。

こちら、その謎に本質から迫るため開発した、x64版の超シンプルマシン語標準入出力ライブラリ stdio.asm を Windows 10で動かした様子。Mac/Linux/FreeBSDでも同様に動きます。

今日の記事はC言語を少しやったけど、なんだかスッキリしない人、現代PCの主力CPU、Intel/AMDのx86系x64マシン語を使ったOSの深い話が気になる人向けです。 まずは楽しくプログラミングでゲームを作ってみたい人や、マシン語の基本は、シンプル&コンパクトなパソコン、IchigoJamからスタートするのがオススメです。(IchigoJam入門動画はじめてのマシン語

C言語プログラミング、本当のはじめのいっぽはこちらです。

int main() { return 1 + 1; }

これを test1.c と保存し、コンパイルし、実行し、実行結果を表示すると(for macOS/Linux)

gcc test1.c ./a.out echo $? 2

1 + 1 という人間らしい言葉で、コンピューターに計算させることに成功!

細かく解説すると・・・。 こちら、整数(int)を返すmainと名付けた関数(数学の関数と似たもの)を作り、中身に「1 + 1」を返して(return)と記述したC言語のプログラムです。 mainという名前は特別で、プログラム起動時に最初に呼び出されます。 「1 + 1 」という人にわかりやすい言葉を、コンピューターが分かる言葉、マシン語へ変換してくれるのがC言語のコンパイラというツールです。 gccというコンパイラは、特に何も指定しないと a.out という実行ファイルができます。それを、実行。 プログラムが実行した結果をみるコマンド(echo $?)を使って、コンピューターが計算した結果「2」を得ることができた。というわけです。

ゲームのキャラクターを表示したい、コントローラー入力を使いたい、音を慣らしたい、いろいろ欲が出てきます。 そこで登場するのがライブラリ。コンピューターの仕様を隅から隅まで調べなくても、誰かが作ってくれた便利な部品を使うことで楽できます!

そんなライブラリの代表格が stdio.h(スタンダードI/Oの略) で定義されている標準ライブラリというものです。 基本的な文字の表示(output)と入力(input)のための関数が揃っています。 冒頭に登場した、printfという関数もそのひとつ。

int main() { printf("hello!\n"); return 0; }

上記のようにプログラムを変更してコンパイルすると、printfって何?とエラーになります。

printfを事前に定義する必要があります。stdio.h というファイルに定義があります。#include は指定したファイルを埋め込んでくれます。 ファイル stdio.h はどこにあるのでしょう?コンパイラが含めるために使うディレクトリが決まっていてその中のを使ってというのが、「<」と「>」で囲う意味でした。 その場にある自分で作ったファイルを含めるときは、ダブルクォートで囲みます。 HTML,CSS,BASIC,C,JavaScript,Pythonなど、コンピューター言語毎に、記号の意味は変わります。

文字の表示やサウンド出力などの手順は、Windows/macOS/LinuxなどのOS毎に違います。 C言語のコンパイラはCPUの違いを吸収してマシン語を生成してくれますが、手順の違いを吸収するのはライブラリの役目。

例えば、Macで文字を出力するには、レジスタrsiに出力したい文字列の先頭アドレスを、レジスタrdxに長さを、rdiに1を、raxに0x2000004をそれぞれ設定して、システムコール(syscall)を呼ぶことで、OSが文字を表示してくれます。(stdio_mac64.asm src on GitHub、cmd: nasm -f macho64 hello.asm -DMAC64; ld -lSystem hello.o -o hello )

mov rdi, 1 ; fd = stdout mov rax, 0x2000004 ; syscall write syscall

CentOS, Ubuntu, Debian, OpenSuSE, Arch Lniux, Fedoraなど、多くのLinuxではレジスタraxに設定するシステムコール番号を変えるだけでOKです。(stdio_linux64.asm src on GitHub、cmd: nasm -f elf64 hello.asm -DLINUX64; ld -e _main hello.o -o hello )

mov rdi, 1 ; fd = stdout mov rax, 1 ; syscall write syscall

FreeBSDでは他のLinuxと異なります。macOSはBSD系の子孫、この値に 0x2000000 数を足したものです。(stdio_bsd64.asm src on GitHub、cmd: nasm -f elf64 hello.asm -DBSD64; ld -m elf_x86_64_fbsd -e _main hello.o -o hello )

mov rdi, 1 ; fd = stdout mov rax, 4 ; syscall write syscall

Windowsでは直接システムコールを呼んではいけないことになっていて、dll(ダイナミックリンクライブラリ)を経由しての呼び出しとなります。stdio_stdout の初期化が必要で、レジスタの使い方や、スタックの使い方も違います。(stdio_win64.asm src on GitHub、cmd: nasm.exe -fwin64 hello.asm -DWIN64& link.exe /entry:_main /subsystem:console hello.obj kernel32.lib )

mov r8, rdx ; len mov rdx, rsi ; buffer mov rcx, [rel stdio_stdout] mov r9, stdio_bytesWritten push qword 0 call WriteFile

このようにOSによってそれぞれ使い方は異なりますが、名前と使い方に揃えれば、以後気にしなくてよくなります。stdio.asm で、環境によって使うライブラリを切り替えるようにしておきます。

%ifdef WIN64 %include "stdio_win64.asm" %elifdef MAC64 %include "stdio_mac64.asm" %elifdef LINUX64 %include "stdio_linux64.asm" %elifdef BSD64 %include "stdio_bsd64.asm" %endif

こうして準備をしておき、stdio.asm を include すれば、どのOSでも動くマシン語でプログラム「Hello World!」が、このようにシンプルに書けます。NASMでのファイルへの埋め込みのincludeは#ではなく%。 いろんな環境での動作確認はConoHaのVPSを使うと1環境1時間1円〜と、とても手軽です。

%include "stdio.asm" global _main section .rodata MESSAGE db "Hello World!", 0x0d, 0x0a LEN_MESSAGE equ $-MESSAGE section .text _main: call stdio_init mov rsi, MESSAGE mov rdx, LEN_MESSAGE call stdio_write mov rdi, 0 call stdio_exit ret

開発効率、速度、汎用性の向上や、特殊な用途への特化するためなど、さまざまなプログラミング言語が誕生し、使われたり、廃れたりしていますが、結局動いているのはこれらマシン語です。 基本を抑えておけば、余計な手間をかけることなく、楽しく楽に開発できてますますプログラミングが楽しくなります。

理解を深めるには、実際にやってみるのが一番です!Windows/Mac/Linux/FreeBSD、それぞれ環境に合わせた c-*.sh/bat、c-*-test.sh/bat を使って、実際に動かしてみましょう。(src on GitHub

開発に必要なツールを設定しましょう。Windowsでは「Visual Studio C++ 2019 コミュニティ」、Macでは「Xcode」がそれぞれ無料で提供されています。 アセンブリ言語をマシン語化してくれるツール、アセンブラ「NASM」もオープンソース、無料です。

Visual Studio C++ 2019 コミュニティ、右側赤枠で囲んだ2つ、ビルドツールとSDKがあれば、ひとまず今回の開発は可能です。セットアップ後、コマンドプロンプトで hello-x64asm のディレクトへ移動し init-win.bat を動かし、開発ツールとNASMにPATHを通して、レッツトライ!

stdio.asm は、stdio.h が持つ機能の極一部。自分なりのライブラリや、プログラミング言語づくりにチャレンジするのもおもしろいですよ!

links
- taisukef/hello-x64asm: the first step of x64 64bit assembly programming on Windows/macOS/Linux
- C言語開発者「C言語は初心者にはお勧めできない」 エントリーにオススメBASICは、世界初のクラウド対応言語だった! IoTで起業家甲子園目指す、長岡高専チームメンタリング
- プログラミング言語は何から学ぶべきか? ロボットプログラミングゲームをIchigoJamでプログラミング! C言語の教科書「Springs of C」より
- 高専でなぜC言語を学ぶのか? IchigoJamマシン語生成プログラム c4ij で作る、C言語版かわくだり
- IchigoJamからのステップアップ - IchigoJam BASIC / Python3 / JavaScript / Java / C言語 対照表
- ハンドアセンブルで高速計算! RISC-V、RV32ICエミュレーターのC言語実装
- マシン語対応 IchigoJam web、気軽にハンドアセンブルして遊べます!
- OpenCL/C言語 GPUプログラミングはじめのいっぽ on Mac
- わずか16KB! WebAssemblyで動くミニC言語オフラインコンパイラ&インタプリタ webci0
- 深いプログラミング言語学習に最適! 512行のC言語コンパイラ ci0 を使ってみました
- 地味なC言語がなぜ楽しいのか?

長らく使ってきた macOS Mojave から Catalina へ移行。32bit開発のためにと残して来たがRISC-Vの開発環境が非対応となり決心。 記念に動かなくなってしまう 32bit版bootBASIC を、64bit化。


「bootBASIC_64bit for MacOS」(src on GitHub)
nasmでアセンブル可能。 コンパクトな実装でプログラミング言語づくりの入門にぴったり。 WindowsやLinuxへの移植もチャレンジしたいが、やってみたい方、fork/プルリク、大歓迎! 512byteのブートセクターに収まる「bootBASIC」を開発した、nanochessさん、ありがとう!(Thank you! nanochess-san.)

10 A=1 10 A=1 20 A=A*10 30 PRINT A 40 GOTO 20 >RUN 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000 10000000000 100000000000 1000000000000 10000000000000 100000000000000 1000000000000000 10000000000000000 100000000000000000 1000000000000000000 -8446744073709551616

16bitのAXレジスタは、32bitでEAX、64bitでRAXへと段階的に進化を遂げたx86マシン語。今回変数も64bit化。100京まで表示!

; Compare statement (rsi, rdi, length:rcx) ; case sensitive check (original) ; rep cmpsb ; jne statement_not_match ; Equal? No, jump ; case insensitive check statement_check: mov al, [rdi] and al, 0xdf mov ah, [rsi] and ah, 0xdf cmp al, ah jne statement_not_match inc rdi inc rsi dec rcx jnz statement_check statement_check_end:

やっぱり、BASICなので、大文字表記にも対応したい。rep cmpsb という、ループ付きマシン語命令を大文字小文字を区別しないループに変更。

慣れない言語であってもネット時代、探せばすぐに解決する良い時代。x86マシン語に関して「Jun's Homepage」が参考になりました。 32bitのEAXで代入するとRAXの上位32bitは0クリアされるとか、MULやDIVが暗黙的にRDXが使われるとか、ハマりやすいポイントが分かります。

このサイト作者によるコンパクトなプログラミング言語「Return of Very Tiny Language」を発見!

IchigoJam BASICの元になったTinyBASICより、更にコンパクトなプログラミング言語VTLの64bit版!ファイルや画像も扱えるように大幅パワーアップ!(VTL系言語の歴史

Linux版をmacOSで動くように少しだけ書き換えて、コンソールでシンプルのVTLを体験することができました。
taisukef/rvtl-amd64: RVTL : Tiny BASIC in x86_64 assembly language for macOS (開発途中)

移植で困ったのは、アセンブル時にでるエラー「error: 32-bit absolute addressing is not supported in 64-bit mode」。アドレスに @GOTPCREL(%rip) をつけて回避とありましたが、Intel表記では使えず、ひとまず使ってなさそうなr15レジスタを介する形に修正。

macOS Catalina 化もトラブルなく終了。Windows NT 3.51 (1996年)から続いた32bit開発環境に分かれを告げました(クロス開発は除く)。 当時の開発環境は、Intel Pentium 90MHz、RAM16MB、HDD1GB、VC++。何より技術資料へのアクセス手段が紙頼み。良い時代になりました。

速いプログラムを作る基本は、ループ数を減らすこと、ループ内を軽くすること。

100円のCPUでも1秒間5,000万回計算してくれますが、4K動画に必要な2500万コのLED制御、1コ1計算だとしても1秒間に2コマの紙芝居にしかなりません。 滑らかで美しい映像を計算するためには高速化が欠かせません。

計算で作る美しい映像例「ライフゲーム」、IchigoJamでも「Kubotaさんのライフゲーム - IchigoJam-FAN」にて、高速化チャレンジ中。 100円CPU、Arm Cortex-M0では、足し算、引き算、掛け算と比較して、割り算が150倍遅いことを「IchigoJamマシン語入門」で確認しました。では、普通のPCではどうでしょうか?

MacやWindowsは、Intel/AMDのx64アーキテクチャーが大半です。Intelの資料によると、レイテンシー(ざっくり実行してから結果がでるまでの時間)で、掛け算(MUL)は足し算(ADD)の3倍、割り算(DIV)は足し算の80倍〜90倍ほどかかることがわかります。
インテル ® 64 アーキテクチャーおよび IA-32 アーキテクチャー最適化リファレンス・マニュアル
プログラミング言語、コンパイラーを作る人には必見のこの資料、日本語でここまで用意されているのはスゴイ! ちなみに、ハードウェア除算命令がある Arm Cortex-M3 での掛け算は足し算と変わらず1クロック、除算はその2〜12倍です。(18.2. プロセッサ命令のタイミング - ARM Information Center

どんな環境でもループ内の割り算は少なくするのが得策です。ループ外にもって行けないか、ビットシフトや掛け算で代用できないか検討しましょう。32で割る整数計算は、2進数のビット演算で5bit右にシフトするのと一緒で、速度はどのアーキテクチャーでも1クロックになるのでとても有効です。

掛け算の使用はアーキテクチャーで判断が分かれます。IchigoJamやスマホなど、Armアーキテクチャーでは掛け算が足し算と同様に高速なので、カジュアルに使えますが、PC向けの場合多少命令が増えても、事前計算や、足し算ビット演算などでの代用が有効そう。

IchigoJamでのライフゲーム、1更新が16秒から14.5秒に短縮されたとのこと!Cortex-M0は、構造がシンプルなので速度計算が簡単で、遅い分、効果が体感で分かるのが楽しいです。

ループ内を軽くする手段の一つ、並列化。Intel/AMDのx64には512bitのSIMD命令、AVX-512という512bit(8bit x 32コ)まとめて計算する命令があります。 zmm0〜zmm31という512bitレジスタを使った計算ができるとのことでしたが、残念ながら今使っているMacBookProではちょっと古くて非対応。 AVX2の256bit(ymm0〜ymm15)で足し算(vpaddb = 1クロック)させてみました。(src for Mac on GitHub)

せっかくなら持てる力、フルパワー使いたいですね!もっと長いビット長が計算できるRISC-Vのベクトル拡張も楽しみです。

GPUやFPGAなど、高速化のための様々な技術、必要に応じて楽しく使っていきましょう!

2020-06-29 追記、すみません!執筆時、10倍、66倍〜80倍と記述していたのは異なるCPUIDでの比較をしていました(、ご指摘ありがとうございます! @57tggx さん)。
現行のアーキテクチャーを調べて、比較表を作ってみたところ、下記のようになりました。2008年のNehalemでは、掛け算で10倍、割り算で70-80倍でした。2015年のSkylakeで少し遅くなっています。Ice LakeはSkylakeがベースとの記述があるので、この部分は変わっていないのではないかと思います。

アーキテクチャー,ADD,SAL 左シフト,MUL 下位64bit,MUL 上位64bit,DIV Core 65nm (2006),1,1,6,7,-90 拡張 Core,1,1,6,7,- Nehalem (2008),1,1,3,10,70-80 Westmere (2008),1,1,3,10,70-80 SandyBridge (2011),1,1,3,4,80-90 Ivy Bridge (2012),1,1,3,4,80-95 Haswell (2013),1,1,3,4,80-95 Broadwel (2014),1,1,3,4,80-95 Skylake (2015),1,1,4,5,80-95 Intel® 64 and IA-32 Architectures Optimization Reference Manualより)

links
- サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装 / IchigoJamではじめるArmマシン語その14
- ハンドアセンブルで高速計算! RISC-V、RV32ICエミュレーターのC言語実装
- MacのGPUでも700倍速! パスワード20文字時代の盾と矛 / さくらクラウドで分散探査する方法
- Macで開発、FPGAで作る4bit学習用CPU「GMC-4」 - Parallels x Ubuntu x Quartus x Verilog

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