LoginSignup
1
0

More than 1 year has passed since last update.

IchigoJam のマシン語でBMIを求める

Posted at

先日の記事
IchigoJam でBMIを求める試み - Qiita
では貪欲法を用いて IchigoJam BASIC でBMIを求めたが、誤差が出てしまった。
そこで、今回はマシン語を用い、より正確なBMIを求める。

仕様

前回同様、身長はcm単位の値、体重はkg単位の値の10倍を入力してもらう。
なるべく多くの環境をサポートする。

プログラム

IchigoJam BASIC

10 ' BMI ケイサン (マシン ゴ)
20 POKE#700,1,0,183,47,18,224,151,6,0,0,147,134,230,15,131,213,6,0,3,214,38,0,179,133,181,2,19,5,16,39,18,5,51,6,166,2,51,85,182,2,130,128,0,0,53,163,25,136,90,136,73,67,100,35,91,67,90,67,48,180
30 POKE#73C,1,35,12,70,1,32,192,7,100,0,91,0,4,66,251,208,0,32,68,28,156,66,10,210,28,26,100,8,36,24,37,70,77,67,149,66,1,217,35,70,243,231,32,70,241,231,48,188,112,71
40 INPUT "シンチョウ(cm):",H
50 INPUT "タイジュウ(kg ノ 10 バイ):",W
60 [0]=H:[1]=W:[2]=H:[3]=W:B=USR(#702,0)
70 ?"BMI:";B/10;".";B%10

マシン語

改造版 asm15 用のコードである。

	' 1.0.0 で2バイト前から実行されるバグ対策
	R1 = R0 << 0
	IF M0 GOTO @M0CODE
MODE RV32C
	' @HDATA の位置を求める
	R13 = PC + 0
	R13 = R13 + 254
	' R11 = H * H
	' R12 = W * 10000
	R11 = [R13 + 0]W
	R12 = [R13 + 2]W
	R11 = R11 * R11
	R10 = R0 + #271
	R10 <<= 4
	R12 = R12 * R10
	' return (W * 10000) / (H * H)
	R10 = DIVU(R12, R11)
	RET

ALIGN 4, 0, 0
MODE M0
@M0CODE
	' R1 = H * H
	' R2 = W * 10000
	R3 = @HDATA
	R1 = [R3]W
	R2 = [R3 + 1]W
	R1 *= R1
	R3 = 100
	R3 *= R3
	R2 *= R3
	' x * R1 <= R2 となる最大の x を求めたい
	PUSH {R4, R5}
	R3 = 1
	R4 = R1
	R0 = 1
	R0 = R0 << 31
@GETNO
	R4 = R4 << 1
	R3 = R3 << 1
	R4 & R0
	IF 0 GOTO @GETNO
	R0 = 0
	' R0:yes, R3:no で二分探索
@BINSEARCH
	' while (yes + 1 < no)
	R4 = R0 + 1
	R4 - R3
	IF CS GOTO @BINSEARCH_END
	' m = yes + (no - yes) / 2
	R4 = R3 - R0
	R4 = R4 >> 1
	R4 = R4 + R0
	' if (m * R1 <= R2) { yes = m } else { no = m }
	R5 = R4
	R5 *= R1
	R5 - R2
	IF LS GOTO @BINSEARCH_YES
	R3 = R4
	GOTO @BINSEARCH
@BINSEARCH_YES
	R0 = R4
	GOTO @BINSEARCH
@BINSEARCH_END
	POP {R4, R5}
	RET

	' IchigoJam web で4バイト前から読まれるバグ対策
ORGR #104
@HDATA
	SPACE 2
@WDATA

解説

前回の記事より、入力された身長の値を H、体重の値を W として、(W*10000) / (H*H) を求めたい。

RV32C

RV32C (IchigoJam R) では、掛け算も割り算も命令があるので、それを使えばよい。
(正確にはRV32CではなくRV32Mの範囲だが)

M0

M0 (従来型 IchigoJam) では、掛け算の命令はあるが、割り算の命令は無い。
そこで、割り算の商をめぐる式二分探索を用いて求めることにした。
二分探索アルゴリズムを一般化 〜 めぐる式二分探索法のススメ 〜 - Qiita
めぐる式二分探索|株式会社コアテック]]

今回は、x * (H*H) <= W*10000 となる最大の整数 x を求める。
(H, W0 < H < 32768, 0 <= W < 32768 を満たす整数とする)
まず、x = 0 のときは、必ず条件を満たす。
また、W * 10000 <= 32767 * 10000 = 0x1387d8f0 なので、
H*H に掛けた時の商が 0x80000000 以上になるような x は、必ず条件を満たさない。
よって、H*H を左に n ビットシフトすると 0x80000000 以上になるような最小の整数 nについて、
0 以上 1 << n 以下の範囲で二分探索を行えばよい。

既知のバグ対策

1.0.0

IchigoJam BASIC 1.0.0 のUSRバグ - Qiita
で解析した結果、1.0.0 では USR で指定したアドレスの2バイト前から実行されることがわかっている。
そこで、最初の2バイトを実行されてもされなくても結果に影響しない命令にし、USR で2バイト目 (0-origin) を指定して実行するようにした。

IchigoJam web

IchigoJam web のマシン語の実行のバグの解析 - Qiita
で解析した結果、IchigoJam web では Rd = PC + u8 命令で本来より4バイト手前のアドレスが取得されることがわかっている。
そこで、指定した位置の4バイト前にも同じデータ (身長と体重) を配置し、どっちを参照してもいいようにした。
さらに、今回の実装中、新たに Rd = PC + u8 命令があるアドレスが4の倍数でない場合、本来より2バイトしか手前にならない(可能性がある)ことがわかった。
そこで、Rd = PC + u8 命令を4バイト境界にアラインメントした。

実行結果

  • IchigoJam U (1.0.0)
  • IchigoJam R (1.5b)
  • IchigoJam web (1.4.3web)

において、前回より正確な大石泉さんのBMIの値が求まった。
なお、小数第2位以降は四捨五入ではなく切り捨てである。

IchigoJam U での実行結果
IchigoJam R での実行結果
IchigoJam web での実行結果

おわりに

※IchigoJamはjig.jpの登録商標です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0