福野泰介の一日一創

マシン語や、アセンブリ言語、高専で習いましたか?

コンピューターはマシン語で動く機械。家電が電機で動くくらいの基本です。
全小学生がプログラミングする時代、さらっと15分でマシン語の肝、押さえましょう。

前提知識は、鯖江市の小学4年生向け総合2コマでも実施しているプログラミング体験。
大きな文字が大人に優しい、はじめてのプログラミング with IchigoJam スライド資料3種のオープンデータ! KDDI連携、福井県小浜市編
まだの方は IchigoJam または IchigoJam web と上記スライドを使って、まずはさらっとご体験ください!

IchigoJam BASICを使った、1から100までの地道な足し算。

A=0:FOR I=1 TO 100:A=A+I:NEXT:?A 5050

AやIは変数と呼ぶ、コンピューター内の記憶です。Aはひとまず0と覚えてもらいましょう。 リファレンスを見ると、FORとNEXTコマンドでIが1から100まで繰り返すことがわかり、変数Aがどんどん書き換えられて、最終的に?(=PRINT)で、表示するプログラムと読めると思います。(A=A+1 は LET A,A+1 の省略形)

コンピューターは数の計算しかできません。上記のプログラムもすべて数として処理されています。IchigoJam BASICというOSは、かかれたプログラムを1文字ずつ文字コードを照合しながら、知っている言葉を見つけたら、その処理を行うことを繰り返します。知らない言葉に遭遇したら「Syntax error」と返すのもOSの役目です。

一文字ずつ、丁寧に処理しているので、真の力は発揮できていません。
そこでマシン語の登場です!

?USR(#800,100) Segmentation Fault

指定したメモリをマシン語として解釈して、動かすコマンドUSR(ユーザー)を使うと、エラーになります。(IchigoJam 1.4未満だと固まるので、電源入れ直しましょう)

IchigoJamで使っているNXP社のCPU LPC1114は、Arm Cortex-M0アーキテクチャーなので 0-65535(16bit)の数(=マシン語)によって動作を指定します。
BASICと同じように、繰り返し足し算するプログラムをマシン語でいれてみます。

LET[0],#2100,#1841,#3801,#D1FC,#4608,#4770 ?USR(#800,100) 5050

LETコマンドは配列に連続した値をセットしていきます。HELPコマンドで確認できますが、IchigoJamの配列はコンピューター内のメモリ#800(16進法で800という位置)に記憶された数の並びです。

マシン語を解読してみましょう! Armマシン語の仕様は2進法で区切ると読みやすいので、BIN$コマンドを使います。

?BIN$([0],16) 0010000100000000

マシン語表をみて、0と1の並びが一致するものを探しましょう。 早速、一番最初の Rd=u8 が、一致しますね。Rdの部分、6〜8番目が0なので、2進法で001、つまりR1、その後8bitが全部0、つまり R1=0 と解読できます。(R0〜R15は、CPUがもっている記憶領域、レジスタと呼びます)

同じように、6つのマシン語を解読していきましょう。

FORI=0TO5:?HEX$([I],4),BIN$([I],16):NEXT 2100 0010000100000000 1841 0001100001000001 3801 0011100000000001 D1FC 1101000111111100 4608 0100011000001000 4770 0100011101110000

解読すると、下記のような動作をしていることがわかります。

R1=0 R1=R1+R0 R0=R0-1 IF !0 GOTO -2 R0=R1 RET

IF !0 GOTO n8 は、ちょっとややこしいです。11010001の並びでまずマシン語がわかります。n8とは、2進法で8bit(8桁)使った飛び先を指定しますが、一番上の桁が1になっているものはマイナスの数を表します。全部8桁、全部が1だと-1、最後が0だと-2・・・と続く、2の補数表現を使います。 -4だとわかりますが、実はこれに2を足した場所 -2 が飛び先となります。R0を1減らして、0でなければ2つ前のマシン語、R1=R1+R0 のマシン語へ飛びます。

RETでBASICに戻ってくるときの R0 の値が、USRコマンドの値となって、表示されます。

CPU、Arm Cortex-M0 アーキテクチャーのLPC1114の最大動作周波数は50MHz、IchigoJam標準の設定では48MHzで動かしています。 1Hzは、1秒間に1回という単位、M(メガ)は、100万の意味。ほとんどのマシン語を1つ1クロックで処理するので、秒間最大5000万回で計算できるというわけです!

ループさせずにRETで戻らせてみたり、引き算、掛け算など、いろいろいじって思い通りになったり、ならなかったり、遊んでみましょう!

命令をいろいろ組み合わせれば何でもできそうな感が伝わりましたか? このマシン語をいい感じに生成するプログラムのことをコンパイラやランタイムと呼び、C言語をはじめ、いろいろな言語が動いている裏っ側となります。
(参考、C言語開発者「C言語は初心者にはお勧めできない」 エントリーにオススメBASICは、世界初のクラウド対応言語だった!

スマホで使われているのは 64bit化された Arm Cortex-Aシリーズ、PCでは Intel/AMD、これから活躍楽しみな RISC-Vなど、CPUによってマシン語の違いはありますが、基本は同じです。

もうちょっとマシン語遊んでみたい人は、こちら「はじめてのマシン語」をどうぞ!
マシン語も分かったし、いろいろ作ってみたい人にオススメは、JavaScript。サーバーからスマホ、PC、ゲーム機まで動くプラットフォーム数、最高です!

プログラミング好きな高専生、鯖江の自然豊かな古民家でのインターン、来てみませんか?

高専生なら気になる、徳島県神山町。
日本で4つめ、2023年に開校予定の私立「神山まるごと高専」ができる町!

CoderDojo神山と一緒にイベント「はじめてのプログラミング with IchigoJam」
山と川と光ファイバーのまち、神山町。自然もコンピューターも仲間にしちゃおう!

たっぷり2時間、楽しんでくれました!未来の高専生!?


今回の使用機材は学校などでの多人数一斉体験にオススメ「IchigoDyhook
お習字のように、最初はゆっくりタイピング、だんだん慣れてきたら、自分なりに改造してオリジナルゲームにして遊んだり、遊んでもらったり! 作ったり、使ってもらったりするこの楽しさ、病みつきになって早34年。飽きる兆候がありません。


はじめてのプログラミング with IchigoJam (IchigoDyhook)
標準、USキーボード、大きな文字版もあります(slide pptx/keynote/pdf on GitHub

加えて、情報系の高専の授業で登場する、一歩先行くマシン語体験
「コンピューターの真の速さを見てみよう!」

1から100まで地道に足していく計算、みなさんは何秒でできますか?

A=0:FOR I=1 TO 100:A=A+I:NEXT:?A

さすが IchigoJam、なかなかの速さで計算しますね。

でも、まってください。IchigoJamのCPU、1秒間に5000万回だったはず?
そうです、BASICという言語は自転車の補助輪のようにやさしく丁寧にまちがいがあったら教えるために、実はこれでもゆっくり動いています。

ちょっと背伸びして、コンピューターが分かる言葉「マシン語」を使ってみましょう。大丈夫、こちらを間違えないよう、ただ打つだけでOKです。

LET[0],#2100,#1841,#3801,#D1FC,#4608,#4770:?USR(#800,100)

でてくる答は同じですが、速さが全然違いますね!これが100円くんの真の力です。
興味が出てきた人は、こちら「はじめてのマシン語」をどうぞ!


会場はステキコワーキングスペース「神山バレー・サテライトオフィス・コンプレックス


Fabスペースもあり!高校生がいろいろ作っているとのこと! お互いのレーザーカッターや3Dプリンター用のデータを交換したり、Hana道場とのコラボできたらおもしろそう。


こちらが噂の「神山まるごと高専」の校舎候補、大きい!部室でも何でも遊び放題!?


鮎喰川(あくいがわ)を挟んだ向かい岸に寮が並ぶ予定とのこと


山の中に秘密基地!?なんと手作りのサウナ!杉の木を切り開き、木材や薪にするリアルマイクラな世界!灰は撒いて新たな木への栄養にする循環型な遊び場。


SHIZQ | 神山しずくプロジェクト
神山町内、移住組に寄るステキなお店がたくさん!ご案内いただいたグリーンバレー大南さん、キネトスコープ廣瀬と!


アメリカからの移住者によるブッガカフェ!おじいちゃんスペシャルという、ベトナムコーヒー風をいただく。


近くでは集合住宅新築中!人が集まる熱い町!

自然と人とコンピューターにがっつり触れ合うのが21世紀流の学び=遊び。
高専生のみなさん、鯖江の自然豊かな古民家でのインターン来てみませんか?
(現在、3/15-19、主に高専4年生向け春インターン募集中)

links
- IchigoJamとBASICで遊ぶプログラミングワークショップ|CoderDojo神山|note
- 神山まるごと高専設立準備委員会 on Strikingly

かわいく思えるようになったx86マシン語、深みにハマって行き着いた「アセンブラ短歌
57577の5行、31byteで動くマシン語で、2byte以上の命令が行をまたいではいけないというストイックなルールが良い!

一句詠んでみました。

68 57 61 6B 61 31 C0 68 6D 62 6C 79 68 41 73 73 65 89 E1 6A 0C 51 6A 01 50 B0 04 CD 80 EB E1

タイトル「WakaAssembly...」 (x86/macOS Mojabe 32bit)

コンピューターの凄さを一番すぐに感じられるのは、繰り返し。 人間では不可能な速さで正確無比に繰り返される様を見せつけ、それが自分の指示によるものだと体感したものを虜にしてしまう。 (参考、IchigoJam BASIC のプログラム、RUN on IchigoJam web by WebAssembly

10 PRINT "WakaAssembly"; 20 GOTO 10

アセンブラ和歌アセンブラ和歌と繰り返しから誕生した、WakaAssembly、WebAssemblyっぽくて輸出しやすいかも。

CatalinaにOSアップデートで動作しなくなってしまう、macOS最後の32bitアプリの記念に。

x86マシン語(アセンブリ言語)で書かれたソースはこちら

00000000 <_main>: 0: 68 57 61 6b 61 push 0x796c626d 5: 31 c0 xor eax, eax ; eax = 0 7: 68 6d 62 6c 79 push 0x65737341 c: 68 41 73 73 65 push 0x616b6157 11: 89 e1 mov ecx, esp 13: 6a 0c push 12 ; length 15: 51 push ecx ; buffer 16: 6a 01 push 1 ; stdout 18: 50 push eax ; dummy 19: b0 04 mov al, 4 ; sys_write 1b: cd 80 int 0x80 ; syscall 1d: eb e1 jmp _main

pushで一度に4文字分、直接詰めるのがさすがCISC!jmp _mainで先頭へジャンプし、繰り返すたびにスタックを積み続けるので Segmentation faul を出してちゃんと止まり、現代OSのありがたみを感じる。(bits 32 として、32bitモードでのアセンブルが必要)

行儀よく、スタック位置を合わせ ret で 0 を返して終わるバージョン main-1shot.s、31byteのバイナリを和歌表示するために作ったミニプログラム wakaout.c もあります。
詳しくは「x86asm/wakaasm src on GitHub」で!

links
- アセンブラ短歌 - 坂井弘亮 / slideshare
- 「オープンソース」を使ってみよう (第33回 アセンブラ短歌)
- 31バイトでつくるアセンブラプログラミング ~アセンブラ短歌の世界~ Kindle版
- 「57577」の機械語を詠む「アセンブラ短歌本」まとめ - Togetter
- 根っこは同じ! Z80経験者によるx86マシン語はじめ、512byte bootBASIC をmacOS用に移植

マシン語秘伝の書を片手にx86マシン語入門が楽しい。Z80と8086のレジスタ対応表、根っこが同じと分かれば怖くない。 どちらの設計にも深く関わる、嶋正利さん

512byteのブートセクターに収まりブートする、x86マシン語で書かれた「bootBASIC」を発見! 世界最小のチェスプログラムの作者でもある、nanochessさんによる著作「Programming Boot Sector Games (PDF ver.)」を片手に、x86の入門として、16bitのリアルモード向けのbootBASICをmacOSの32bitアプリへと移植。


パスカルの三角形が動いた!


bootBASICは、使える命令も最小限、マルチステートメントもなしのシンプルなBASIC。固定メモリを使っていた元のコードを .data セクションに移して、si/diを32bit化したesi/ediに変更、BIOSの代わりに syscall を使う形にしてできあがり。 アセンブリ言語を使った開発は、アセンブラ nasm によって瞬時にバイナリ化され、即動作。スピード感が気持ちいい。


「Pascal's Triangle」 RUN on IchigoJam web
bootBASICの構文は、IchigoJam BASICにも使えるので、サンプルのプログラムそのままで動いた、パスカルの三角形!


bootBASIC_32bit src on GitHub
現在、Mac用のみ。


Programming Boot Sector Games (PDF ver.)」
1KBの半分、512byteにコンパクトに収めるためのマシン語の基本や、テクニックなど丁寧に解説された本がステキでした!

32bit化でEが付き、64bit化してRが付きと拡張されはしてますが、8bitの名機Z80(1976年)や、16bitアーキテクチャーの王様8086(1978年)の面影、しっかり残ってます。

Z80マシン語でブイブイいわせた人、最近の重量級開発環境が苦手な人、言語の流行り廃りに疲れた人、アセンブラによる超軽量開発で、マシン語でしか到達できない世界を切り開くとかどうでしょう?

links
- 41年経った今でも使える、いにしえのx86マシン語AAAをMacで動かす
- x86 - Wikipedia
- x64 アセンブリーの概要 | iSUS

Intel 8086から現代のCore iシリーズまで、脈々と続くx86アーキテクチャ。実は、誕生1978年と私と同い年。RISC-Vのシンプルさと合わせて引き合いに出される古の命令「AAA」を動かしてみました。


参考図書「8086マシン語秘伝の書 単行本 – 1990/10 by 日高徹氏、青山学氏
MSXでマシン語を使ってみたくて買った「Z80マシン語秘伝の書」の8086版! 16bit CPUである8086向けに、変更、セグメントなどx86ならではの項目が追加されている。Z80同様、古典と思いきや、普通に現代のMacでも普通に使えてしまうことに驚きます。

AAA命令もちゃんと載ってました!
2進法/16進法が基本のコンピューターと、10進法が基本の人をつなぐ命令の一つ。0から15までの数を2byte2桁表現にしてくれる便利命令でした。(src on GitHub)

_main: mov eax, 0 main_loop: push eax clc ; reset carry flag AAA call putnumhex pop eax inc eax cmp eax, 16 jne main_loop mov al, 0 ret

こちら実行結果

0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 0100 0101 0102 0103 0104 0105

こちら0から15までをAXレジスタにセットして、AAA命令を通した結果です。確かに2byteになってくれると表示しやすくて便利です。

このような基本的な部分は一度作ってモジュール化してしまえば、再度作る必要はないので、専用の命令はリソースの無駄になります。 実際、x86アーキテクチャでも64bit環境ではAAA命令は削除されているので、上記プログラムは32bitモード(-f macho32)でコンパイルしています。(syscallの呼び出し方も違うので注意!)


こちら、8086マシン語秘伝の書から、144倍するという定数による掛け算を分解してする高速化するテクニック(main.s src on GitHub

SHL AX, 1 SHL AX, 1 SHL AX, 1 SHL AX, 1 MOV BX, AX SHL AX, 1 SHL AX, 1 SHL AX, 1 ADD AX, BX

144倍=128倍+16倍であることを利用して、シフトと足し算命令を使って実現しています。8086には掛け算命令MULがあるにも関わらずなぜこのテクニックが必要だったか?

秘伝の書記載の仕様によると、16bitの掛け算(MUL)にかかるクロック数はなんと118〜133。1bitシフト(SHL)も9クロックかかるとはいえ圧倒的に速いわけです。 現代CPUは掛け算も足し算も1クロックで終了するので、このテクニック自体は不要です。

そういえば、なぜわざわざ1bitずつシフトしているのでしょう?

SHL AX, 4 MOV BX, AX SHL AX, 3 ADD AX, BX

と書けばもっと短く、高速です。実は、この元になったZ80マシン語秘伝の書でも同じ内容があって、Z80には1bitシフトしかないので、ベタ移植したままになってしまったと想像。(追記、・・・違いました。8086にはイミディエイトのシフトは1限定。上記は80186から加えられた命令でした。 thanks! > @fujitanozomu

MOV CL, 4 SHL AX, CL MOV BX, AX MOV CL, 3 SHL AX, CL ADD AX, BX

8086命令を使って、このように書くこともできますが、2クロックで終わる1bitシフトと比較し、CLを使ったシフトは 8+4N クロックと遅く、速度を取るなら秘伝書記載の通りが正解でした。

コンピューターの特性を知り、いかに活かすか、今でも大事な基本ですね!
普段使っているCPUに近づくマシン語の世界、こちらのサンプルや入門をどうぞ!(x86asm src on GitHub

links
- 日本人が創ったCPUの歴史、MacのCPU Intel 64 マシン語はじめのいっぽ
- ハンドアセンブルで高速計算! RISC-V、RV32ICエミュレーターのC言語実装
- はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

人気のArmマシン語入門の「つぎのいっぽ」ハンドアセンブルをRISC-Vでやってみました。


参考図書「RISC-V原典 オープンアーキテクチャのススメ | デイビッド・パターソン, アンドリュー・ウォーターマン, 成田 光彰
著者、デイビッド・パターソン氏は、RISC生みの親!

せっかくなのでハンドアセンブルしやすく実行効率の良い、16bitの圧縮命令拡張RV32Cを使いたいので、以前使用したエミュレーターにC拡張を勉強ついでに追加実装。(rv32emuの元、TinyEMUはC対応してます)

R11=0 R11+=R10 R10-=1 IF R10 GOTO -2 R10=R11 RET

RISC-VのC言語呼び出し規約では、R10が第一引数で返り値となり、R11も一時レジスタとして使用可能なので、R10/R11を使用。C拡張の命令のみを使ってasm15r表記で作成。

比較のためにこちらがArm Cortex-M0用、asm15表記ですが、ほぼ同じですね。

R1=0 R0=R0+R1 R0=R0-1 IF !0 GOTO -2 R0=R1 RET

RISC-Vにはフラグがない代わりに分岐命令で比較が可能です(比較した結果を0か1かで代入する命令でキャリーフラグを代用できます)。 足りない命令は、32bit命令を使えばOKなので、割り切ったコンパクトな設計が美しい。

RV32C RISC-Vマシン語表」を見ながらハンドアセンブルします。

0b0010010110000001, // R11=0 0b1001010110101010, // R11+=R10 0b0001010101111101, // R10-=1 0b1111110101110101, // IF R10 GOTO -2 0b1000010100101110, // R10=R11 0b1000000010000010, // RET

こちらを emu-rv32i-test-c.c で、仮想RAMに書き込んで、実行!
繰り返し足し算するプログラムが動きました!(src on GitHub


ArmのThmubと違って、C拡張はハードウェアで実装する別名的扱い。 よく使われる命令に絞って実装されており、32bit命令と併用できるのが特徴。たった400ゲートで実装できると言うだけあって、C言語での実装も約200行とコンパクト。(関数 convert_insn_from_c / src on GitHub

16bit命令を使うことで30%ほど容量が削減できて、ArmのThumbとほぼ近いサイズくらいになります。レジスタを複数PUSHするなどの命令を削減したため、より小さくはなりませんが、ハードウェアコストや実行性能にメリットあり。後発優位でもありますが、この辺りのバランス感覚が気持ちいい。

64bit時代にプログラムの容量削減に意味はあるのか?大有りなのです。メモリとのアクセスは時間がかかるので、何段ものキャッシュを使って実行効率を上げている現代CPU、命令がコンパクトになるとその分キャッシュヒット率が向上し、速く動作するわけです。実行時間=電力消費量=コストなので、省エネ化にもつながります。

RISC-V原典、一番おもしろかったのはV(ベクトル)拡張。CPU+GPUの歴史が変わりそう!V実装のフィックスと実CPUの広まり、大いに楽しみです。
(参考、RISC-Vベクトル拡張について解説する - Fixstars Tech Blog /proc/cpuinfoArm64のSIMD

理研のスパコンに使われたり、格安ハードが登場した2019年のRISC-V。今年もかなり伸びそうな予感のCPU、RISC-V(リスク・ファイブ)。RISC-V版Androidや、RISC-V版ノートパソコンの発売もあるかも?

RISC-Vは、由緒正しいハーバード大生まれのRISCアーキテチャーの第五世代。ライセンスフリーで自由に利用できるオープンかつ時代に合わせたチューニングによるハイパフォーマンスが特徴です。 以前32bit版で紹介しましたが、短くシンプルな16bitの縮小命令RV32Cを使って、Arm Cortex-M0学習用のasm15をRISC-Vに対応させたasm15rを設計。

そもそも、コンピューターとは何でしょう?
コンピューターとは、膨大な数を記憶し、正確かつ超高速に計算するものです。

どう記憶し、どう計算するかを指示する数、それがマシン語。 CPUが違えばマシン語が違います。各メーカーによって系統があり、具体的なCPUによって使える命令が異なりますが、基本的はどれも一緒です。


RV32C RISC-Vマシン語表 (asm15r)
こちらまとめたRISC-Vマシン語です(抜粋)
RISC-Vのラインナップは、32bit/64bit/128bitの大きく三種類。今回は入門しやすい32bitで縮小命令、掛け算割り算命令対応版をセレクト。

32bitコンピューターでの記憶は大きく2種類。数個から数十個ある32bit(4byte)のレジスタと呼ばれるものと、数KBから数GBまでのメモリ(RAMやROM)と呼ばれるもので記憶します。 RISCアーキテクチャでは計算はレジスタを使って行い、メモリとレジスタとのやり取りは別の命令で行います。


こちらがメモリとレジスタをやりとりする命令。

ここでRISC-Vを使った超シンプルなRISC-V 32bitコンピューターを仮定します。
1. メモリはアドレス0から始まる32byteのROM(16bitずつ16セットのスイッチで0/1切り替え可能、上下8bit入れ替え)
2. アドレス32から始まる4byte(32bit)には、対応する32コのLEDがついている(エンディアン入れ替え)
3. アドレス0から始めるリセットボタン

この架空のRISC-Vコンピューターに1+1を計算させるプログラムを上記asm15rを使った組みます。

R10=1 R10+=1 R11=32 [R11]=R10

メモリアクセスにレジスタR8以降しか使えないので、計算もレジスタR10とR11を使うことにします。


他にも、コンピューターにおいて大事な分岐や、複雑な計算の一時記憶に便利な機構スタックを実現する命令などが定義されています。

計算が終わった後もどんどん次のメモリを命令として実行していってしまうので、GOTO命令を使ってはじめに戻してあげましょう。

R10=1 R10+=1 R11=32 [R11+0]=R10 GOTO -4

この5つ、20byteの命令を命令表を見ながらビット列に置き換える、ハンドアセンブルし、スイッチを設定。(この作業を自動化するソフトがアセンブラやコンパイラ)

001 0 01010 00001 01 :'R10=1 000 0 01010 00001 01 :'R10+=1 001 1 01011 00000 01 :'R11=32 110 000 011 0 0 010 00 :'[R11+0]=R10 101 1 1 11 1 1 1 100 1 0 1 :'GOTO -4

これでLEDの右から2つ目だけが光って、1+1の計算結果が2であることを出してくれるプログラム、完成です!
スイッチをいじって、2+1の計算などにしてみましょう。

この架空のコンピューター、作ってみたくなりますね!
RISC-Vは誰もがオープンに使えるアーキテクチャ、また本ブログもCC BYのオープンデータ、自由に作って遊んだり売ったりして、OKです!

Armは、スマホや、IchigoJam、開発中の日本のスパコン富岳など、世界で最も普及しているCPUアーキテクチャ。Armの一番シンプルなCortex-M0のマシン語と比較してみましょう。

Cortex-M0 Armマシン語表 (asm15)
こちらも16bitが基本、似てますね。RISC-Vの縮小命令は32bitの標準命令と下位2bitで区別できるので混在できるのが特徴です。(RISC-Vの縮小命令には掛け算割り算がないので、32bit命令を使用)

Cortex-M0はレジスタ16本(プログラムカウンター含む)、RISC-Vは32本(内R0は0固定、プログラムカウンターは別)など、細かな違いの理由に思いを馳せるのもまた一興。

IchigoJamで、いますぐ遊べるマシン語の世界もぜひどうぞ!
はじめてのマシン語 - IchigoJamではじめるArmマシン語その1

LPC1114やラズパイで動く IchigoJam が対応しているマシン語は、Arm Cortex-M0 の16bitが基本のThumb命令。 マシン語表を見れば一目瞭然、とってもシンプルで使いやすい!

C言語でArm Cortex-M0を真似る(エミュレーション)プログラムを作り、IchigoJam web に組み込んでみました。

年賀アプリ2020のマシン語版を動かしてみた様子。


こちらArmマシン語入門から「画面をイチゴで埋め尽くす12の方法

これで実機でなくてもマシン語の世界に触れられます!
ただ、圧倒的なスピードを感じられないので、引き続き実機で触れるのが一番です。

まだ作りがいい加減なので、実機と挙動が違うところが多々あるはずです。
オープンライセンスなオープンソースなので、ご興味ある方、気軽にコミッターとしてご参加ください!
IchigoJam/acm0emu: CPU emulator for Arm Cortex-M0
組み込み簡単、とってもシンプルなC言語ヘッダー1ファイル、わずか700行あまり。
おかしな挙動のご報告(issues)や、テストコード寄稿も大歓迎です。

一日一創、デザインをシンプルに見やすくプチリニューアル。
鍵は、HTML viewportの幅指定。

<meta name="viewport" content="width=650">

iPhoneからの見た目、いい感じになりました。

HAPPY 2020 あけましておめでとうございます!
今年のプログラミング初めはマシン語で、7セグエミュレーション。

IchigoJam ver 1.4.1 実機での動作比較。BASICでもまぁまぁの速さですが、左から徐々に書き換わるところが目でも追えてしまう程度。対して、マシン語版は瞬時に切り替え、キビキビ動きます。

今年18年目を迎える株式会社jig.jp、社名の由来は「軽快」を辞書で探して見つけた単語「軽快なテンポで踊るjigダンス」に、日本発の意気込み加えた「jig.jp」。 原点は34年前から始めたプログラム、もっと快適に、もっと速く、もっと便利に。より豊かな社会の実現への探求は続きます。

一日一創 2020
今年もよろしくおねがいします

7セグエミュレーション、マシン語化メイキング

BASICのカウントダウンプログラムから、マシン語化する部分を決める

95 FORI=0TO7:[I]=0:NEXT 100 IFN&1[0]=[0]|#38 101 IFN&2[1]=[1]|4:[2]=[2]|4:[3]=[3]|4 102 IFN&4[5]=[5]|4:[6]=[6]|4 103 IFN&8[7]=[7]|#38 104 IFN&16[5]=[5]|#40:[6]=[6]|#40 105 IFN&32[1]=[1]|#40:[2]=[2]|#40:[3]=[3]|#40 106 IFN&64[4]=[4]|#38 107 IFN&128[2]=[2]|1:[5]=[5]|1 110 FORI=0TO7:POKE#700+I+M*8,[I]:NEXT

パラメータのセグメントを表すNと、書き換えるキャラクターを表すMをマシン語ルーティーンに渡す方法を決めます。 マシン語はPCG領域の始めの方を空けて、#740からスタート。192byteまでのマシン語が入ります。

U=USR(#740,M<<8|N)

RAMのアドレスR1を計算して、PCGをメモリアクセスを使って初期化(参考、マシン語入門その5

R2=R0 R3=8 R2>>=R3 R3=3 R2<<=R3 R1=R1+R2 R2=7 R3=8 R2<<=R3 R1=R1+R2 R2=0 [R1+0]L=R2 [R1+1]L=R2

100行のBASICのプログラムのマシン語化、Nの1ビット目を判定して、メモリを書き換えます

R2=1 R0&R2 IF 0 GOTO @SKIP0 R3=#38 R4=[R1+0] R4|=R3 [R1+0]=R4 @SKIP0

この調子で7+1セグメント分作ります(asm15ソース全文

レジスタR4は元に戻すルールなので、PUSH {R4,LR} ではじめて POP {R4,PC} で終わる、Armマシン語の定石。

できたアセンブリ言語のプログラムをハンドアセンブルか、asm15を使ってマシン語を生成して、プログラムへ組み込みます。
サクサクバージョンのできあがり!

10 POKE#740,16,181,2,70,8,35,218,64,3,35,154,64,137,24,7,34,8,35,154,64,137,24,0,34,10,96,74,96,1,34,16,66,3,208,56,35,12,120,28,67,12,112,82,0,16,66,9,208,4,35,76,120,28,67,76,112,140,120,28,67 20 POKE#77C,140,112,204,120,28,67,204,112,82,0,16,66,6,208,4,35,76,121,28,67,76,113,140,121,28,67,140,113,82,0,16,66,3,208,56,35,204,121,28,67,204,113,82,0,16,66,6,208,64,35,76,121,28,67,76,113 30 POKE#7B4,140,121,28,67,140,113,82,0,16,66,9,208,64,35,76,120,28,67,76,112,140,120,28,67,140,112,204,120,28,67,204,112,82,0,16,66,3,208,56,35,12,121,28,67,12,113,82,0,16,66,6,208,1,35,140,120 40 POKE#7EC,28,67,140,112,76,121,28,67,76,113,16,189 RUN NEW 10 S=1*60*60 20 VIDEO5:CLS:LC2,2:FORI=0TO4:?CHR$(224+I);:NEXT 25 LET[10],`111111,`110,`1011011,`1001111,`1100110,`1101101,`1111101,`100111,`1111111,`1101111 31 N=S/60/60:F=~S&1:M=0:GSB90 32 N=S/60%60/10:F=0:M=1:GSB90 33 N=S/60%60:F=~S&1:M=2:GSB90 34 N=S%60/10:F=0:M=3:GSB90 35 N=S%60:M=4:GSB90 40 S=S-1 42 WAIT60 50 IF S>=0 GOTO31 55 LET[20],`1110110,`1110111,`1110011,`1110011,`1110010 60 FORM=0TO4:N=[20+M]:GSB95:NEXT:WAIT60 61 LET[30],2,0,2,0:FORM=0TO3:N=[30+M]:GSB90:NEXT:N=0:M=4:GSB95:WAIT60 62 GOTO60 90 N=[N%10+10]+F*128 95 U=USR(#740,M<<8|N):RTN

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

CPUの世界にもオープンデータ。オープンソースでオープンライセンス(BSDライセンス)なCPUアーキテクチャ「RISC-V(リスク・ファイブ)」の実機が続々登場! 1981年にバークレー大学から発表されたRISC-Iからスタートして5世代目のRISC-V、Google / NVIDIA / NXP / Qualcomm など、数多くの企業をメンバー企業に支えられて躍進前夜。

Amazonが発表した自社開発CPU、Graviton2はArmベースでしたが、次世代がRISC-Vになっても不思議はありません。(現時点でAmazonはまだメンバーではないが、オープンに誰でも使えるため、使用するのにメンバーである必要もない)

そんな RISC-V というオープンなコンピューターの気持ちになってみましょう!
IchigoJamで試した、はじめてマシン語Arm編のように、はじめのいっぽはハンドアセンブル!
Specifications - RISC-V Foundation(RISC-V仕様書 P130より)

Instructionsとは、RISC-Vというコンピューターができること一覧、基本は32bit(4byte)、メモリに書かれた命令にしたがって、どんどん計算していきます。 レジスタは基本32コ、そのうち0番レジスタX0は0固定。実行位置を表すプログラムカウンタ(PC)は別となっています。

ひとまず、1+1をさせてみましょう。
レジスタ同士の足し算は ADD (上の図の一番下)

ADD rd,rs1,rs2

rd = rs1 + rs2 という足し算をします。
RISC-Vは、32コあるレジスタ(X0-X31)の内、x10,x11...を引数や、返り値に使うことになっています(x10,x11は別名a0,a1)

ADD x10,x10,x11

こちらをマシン語表を見て、二進法32桁の数値に変換します。(RISC-Vのマシン語の基本は32bit、16bitの圧縮版もあります)

0000000 01011 01010 000 01010 0110011         rs2 rs1 rd

これを16進数に表すと

-> #00b50533

この4byteをメモリに書き込めばOKです!(RISC-Vは、little endian なので、#33,#05,#b5,#00 の順)

ただ、これだけではプログラムの終わりがわからずどんどんよくわからない計算が進んでしまうので、戻す(RETURN)命令を加えます。 RISC-Vでは、戻り先がレジスタ1番に入っているので、そちらへのジャンプ(GOTO)すればOK。 命令表の中から Brunch の JALR x1 を上記同様に16進数に変換します。(x1は別名ra)

JALR x1 (JALR rd,rs1) 0b00000000000 000001 000 00000 1100111         rs1 rd -> #00008067

できました!

実機で動かしてみたいところですが、今回は誰でも使えるエミュレーターを使います。

WebAssemblyにも対応し、ブラウザ上でRISC-V版Linux(JSLinux)まで動かしてしまうTinyEMUをベースにしたコンパクトなエミュレーターrv32emuを使います。

いろいろ組み込みに便利なようにコア部分を分離するように改造した rv32emu を作りました。
emu-rv32i.h をインクルードし、下記のように使います。

#include "emu-rv32i.h" #include <stdio.h> int main(int argc, char** argv) { uint32_t start = 0; ram_start = 0; uint32_t end = 0xfffffffe; *(uint*)(ram + start + 0) = 0x00b50533; // ADD x10,x10,x11 *(uint*)(ram + start + 4) = 0x00008067; // JALR x1 pc = start; reg[2] = ram_start + RAM_SIZE; // sp - stack pointer reg[1] = end; // ra - return adderss reg[10] = 1; // a0 reg[11] = 1; // a1 while (machine_running) { next_pc = pc + 4; insn = get_insn32(pc); printf("[%08x]=%08x\n", pc, insn); execute_instruction(); pc = next_pc; if (pc == end) break; } printf("x10 a0: %08x\n", reg[10]); return 0; }

結果はこちら

x10 a0: 00000002

見事、x10に 1+1 の結果、2が入っていますね!
reg[10]やreg[11]の初期値を変えたり、他の命令をハンドアセンブルしてみたりと、いろいろ試してみましょう。

MacでRISC-Vの開発環境を入れるなら、Homebrewを使うと楽ですよ!

$ brew tap riscv/riscv $ brew install riscv-tools

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

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