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

IchigoJamのキャラクターコード255は、イチゴ。
これで画面を埋め尽くす12の方法、まずはBASICから!


1. 延々イチゴを表示して埋め尽くす(GOTO編)

10 ?CHR$(255);:GOTO10 RUN

2. 延々イチゴを表示して埋め尽くす(CONT編)

1 ?CHR$(255);:CONT

再開するCONTコマンドは、プログラム内に書くとその行を再度実行として機能します

3. FOR/NEXTコマンドで埋め尽くす(768=32*24)

FORI=1TO768:?CHR$(255);:NEXT

4. VIDEO0で一旦画面出力を止めて高速化(画面表示OFFで約3倍速)

VIDEO0:FORI=1TO768:?CHR$(255);:NEXT:VIDEO1

5. LOCATE(短縮計LC)を使って縦に埋める

FORI=0TO31:FORJ=0TO23:LCI,J:?CHR$(255);:NEXT:NEXT

6. VRAMに直接書き込む(HELPコマンドでVRAMのアドレスが確認できます)

FORI=#900TO#BFF:POKEI,255:NEXT

7. COPYコマンドを使って一瞬で埋める

POKE#900,255:COPY#901,#900,768-1

実用的にはこれで十分ですが、マシン語を使って究極を目指してみます

8. マシン語を使って埋める (22byte 5383cycle)

R2=9 R2=R2<<8 R1=R1+R2 R2=32 R3=24 R2*=R3 @LOOP [R1]=R0 R1+=1 R2-=1 IF !0 GOTO @LOOP RET

32*48で768、数値が1byteの上限255を超えるので、掛け算命令で設定。

POKE#700,9,34,18,2,137,24,32,34,24,35,90,67,8,112,1,49,1,58,251,209,112,71 U=USR(#700,255)

ハンドアセンブルか、asm15アセンブラを使ってマシン語を作成しUSRコマンドで実行!
1cycle = 21nsec、割込が無いとすれば、113usec、つまり0.1ミリ秒で処理完了です。

9. マシン語、4byteまとめて埋めて高速化 (26byte 1353cycle)

R2=9 R2=R2<<8 R1=R1+R2 R2=R0<<8 R0+=R2 R2=R0<<16 R2+=R0 R0=192 @LOOP [R1]L=R2 R1+=4 R0-=1 IF !0 GOTO @LOOP RET

メモリへの書き込みは1byte、2byte、4byteと3種類。まとめて4byte書くように変更することで、プログラムは10byte増えてしまいましたが、約4倍速に!

10. マシン語、まとめて転送命令STMを使って高速化 (36byte 378cycle)

PUSH {LR,R4,R5,R6,R7} R2=9 R2=R2<<8 R1=R1+R2 R2=R0<<8 R0+=R2 R2=R0<<16 R2+=R0 R3=R2 R4=R2 R5=R2 R6=R2 R7=R2 R0=32 @LOOP STM R1,{R2,R3,R4,R5,R6,R7} R0-=1 IF !0 GOTO @LOOP POP {PC,R4,R5,R6,R7}

Arm Cortex-M0にあるまとめて転送命令STMは、アドレスR1へ複数指定したレジスタの値をアドレスの値を変更しながら高速に転送してくれます(転送時間=転送数+1クロック)。 R2からR7まで6レジスタ、24byteまとめて転送できるので32回ループすれば画面全部を埋められます。

11. マシン語、ループ展開 (86byte 248cycle、転送部分220cycle)

PUSH {LR,R4,R5,R6,R7} R2=9 R2=R2<<8 R1=R1+R2 R2=R0<<8 R0+=R2 R2=R0<<16 R2+=R0 R3=R2 R4=R2 R5=R2 R6=R2 R7=R2 R0=R2 STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3,R4,R5,R6,R7} STM R1,{R0,R2,R3} POP {PC,R4,R5,R6,R7}

ループのために分岐すると3cycle時間を消費してしまうので、ループの数がそれほど多くなければ展開して高速化できます。 ループ用の変数が不要になるので、7レジスタ使ってSTMの回数も減らせて一石二鳥。 ただ、プログラム量は必然的に多くなりがちなので、容量に余裕がある時に使いましょう。

12. キーボードで ALT+V 押しっぱなし(手動)
プログラムを作ることはあくまで手段。手でやったほうが速いこともあります。

目的を達成するための方法は様々です。

同じマシン語でも8番のシンプルなマシン語に比べ、11番のプログラムでは、24倍も速くできました。
ターゲットのCPUに合わせた最適化を行うことで、ぐっと効率が上げられたりします。

BASICであってもコマンドの使い方次第でぐっと速くできます。
リファレンスを見直して、いろいろな使い方を探ってみるのもいいですね。

速度重視?容量重視?作りやすさ重視?
目的に合わせて選択できる手段を増やしておくと、叶う夢の幅が広がります!

(追記)
13. マシン語、ループ展開 PUSH編 (84byte 247cycle、転送部分214cycle)
IchigoJam-FANにて、PUSHを使った更に速い方法が発見されました!(Thanks Amanagi-san)
転送部分のみで214cycle、方法11と仕様を合わせても84byte/247cycleと、2byte減って1cycle/21nsec高速!

PUSH {LR,R4-R7} R2=#C R2=R2<<8 R1=R1+R2 R2=R0<<8 R0+=R2 R2=R0<<16 R2+=R0 R12=R13 R13=R1 R1=R2 R3=R2 R4=R2 R5=R2 R6=R2 R7=R2 R0=R2 R14=R2 PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {LR,R0-R7} PUSH {R0-R2} R13=R12 POP {PC,R4-R7}

* スタックポインタ(R13)を破壊可能なR12に退避し、VRAMの末尾アドレスに変更、LR(R14)も使って36byte(9レジスタ*4byte)転送するという技!埋めるキャラクターがイチゴ(#FF)なのを利用して、更に縮めることも!?

- 連載、IchigoJamではじめる、Armマシン語入門
1. はじめてのマシン語
2. ハンドアセンブルで超速計算!
3. マシン語メモリアクセスで画面超速表示!
4. マシン語でLEDを光らせよう!
5. 楽しさ広がるマルチバイトメモリアクセスとスタック
6. マシン語使いこなしTIPS
7. カジュアルに使うインラインマシン語
8. アセンブラを使って楽しよう
9. マシン語で高速SPI
10. マシン語を制するもの時間を制す
11. 画面をイチゴで埋め尽くす12の方法
12. レジスタ不足に上位レジスタとスタック操作
13. コンパイラはじめのいっぽ、EVAL実現法とマシン語生成
14. サイズを取るかスピードを取るか、割り算のアルゴリズムとマシン語実装
15. マシン語化で1万倍速!? セットで学ぶアルゴリズムとコンピューター

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