数学をする
すべての計算はレジスタ間で行われます。算術命令は最初に宛先を指定し、その後に読み取る2つのソースが続きます: は dest = src1 + src2 を意味します。
t2に場所 t0 と t1 を追加する。
t0 と t1 で、常にこの順序です。実行される演算が加算、減算、乗算、除算、剰余のいずれであっても、同じ形で入ってきます。2つの値が入り、1つの演算が行われ、1つの結果が右側の t2 に出てきます。 が最初に t2 を指定するのは、そこに結果が格納されるからであり、これはこのレッスンのすべての算術命令が従う唯一の順序規則と一致します。足し算と引き算
は2つのレジスタに保持された数値を取り、その和を3つ目のレジスタに格納します。 は減算について同じことを行います: は t0 - t1 を計算します。どちらの命令もレジスタからのみ読み取ります。
add t2, t0, t1 # t2 = t0 + t1sub t2, t0, t1 # t2 = t0 - t1
知っておくべきこと:レジスタは32ビットしか保持できないため、上限があります。結果が大きすぎて収まらない場合でも、エラーは発生しません。値は静かにラップアラウンドして非常に小さな数値になり、実行は続行されます。日常的な値がこの限界に達することはありませんが、上限について知っておく価値はあります。
2つ目のレジスタの代わりに固定数値を使うには、即値を意味する文字 i を追加します: は定数1を加算します。subi は存在しないため、定数を減算するには負の値を加算します:。
掛け算と割り算
は2つのレジスタを乗算します。 はそれらを整数として除算し、端数を切り捨てるため、7 / 2 は 3 になります。 は剰余を返すため、7 % 2 は 1 になります。除算がクラッシュを引き起こすことはありません。ゼロで除算すると -1 が返されます。
mul t2, t0, t1 # t2 = t0 * t1div t2, t0, t1 # t2 = t0 / t1 (整数)rem t2, t0, t1 # t2 = t0 % t1 (余り)
まとめ
li t0, 7li t1, 5add t1, t0, t1 # t1 = 7 + 5 = 12mv a0, t1 # 出力サービスはa0を読むli a7, 1ecallli a7, 10ecall
は7をt0にロードします。 は5をt1にロードします。 はそれらを加算し、12をt1に書き込みます。 は結果をa0にコピーします。これは、printサービスが常にa0から値を読み取るためです。次に がprint-integerサービスを選択し、 がそれを実行します。最後に、 と がプログラムを終了します。
おまけ:シフトを用いた乗算
因数が2の累乗である場合、 よりも高速な方法が存在します。(論理左シフト)はレジスタのビットを左に移動し、シフトする各位置が値を2倍にします:1シフトで2倍、2で4倍、3で8倍。シフトは乗算よりもコストが低いため、コンパイラは頻繁にこれを使用します。
li t0, 5 # 5から始める# 左に3ビットシフト (5 * 2^3 = 5 * 8)sll a0, t0, 3 # a0 = 40li a7, 1 # 整数を出力ecallli a7, 10ecall
は5をt0に入れます。 はそのビットを3桁左にシフトします。各桁が値を2倍にするため、5は3回2倍され(5、10、20、40)、a0に40が得られます。残りは以前のprintパターンに従います。3を1や2に変えて、実行する前に結果を予測してみてください。
5を0ビット左にシフトしても5のままです。
