MIPSのアセンブリコードは基本的に以下のような形をしています。本演習ではこのコードを必要に応じてコピーして使用してかまいません。
mainはシステムからサブルーチンとして呼び出され、呼び出し元のアドレスが$raに格納されている。このため、mainの終了が「jr $ra」となっている。プログラム内でこの$raを値を破壊してしまうと、呼び出し元に戻れず正しく終了できないので、レジスタ$raの扱いには特に注意すること。
1行に1命令。
上記のmainやloopのように、":" が続く識別子は「ラベル」という。ジャンプ命令のジャンプ先アドレスや、データ領域のアドレスを参照するために使用します。名前がMIPSの命令と重複しないよう注意。
8文字ごとの字下げをして記述することが慣用となっています。
入力された整数値が0かどうかを判定し、その結果に応じて"zero"もしくは"non zero"の文字列を出力するプログラムの例を載せます。ここで .asciizは末尾がnull文字で終わる文字列をメモリに格納することを意味しています。
ここではシステムコールread_intで読み込んだ値が返されるレジスタ$v0を$zeroレジスタと比較し、等しければラベルzeroにジャンプ (beqによる条件付き分岐) しています。ここがちょうどC言語のifの処理に当たります。
beqでジャンプせず、そのままを処理を継続した場合は、無条件ジャンプのb命令でラベルzeroからの処理を飛び越え、ラベルendに処理が移ります。こうした流れによりC言語のif ~ else ~の構造が実現されています。
改行コードの文字列データを用意し、これをシステムコールprint_stringで出力することで改行を実現します。
CPU が持っているレジスタの数には限りがあり、各レジスタの使用方法も定められています。したがって、少しでも大きなプログラムを書くときにはメモリ上にデータ領域を確保し、メモリとレジスタの間でのデータ転送が不可欠になります。ここで、予めデータ領域を確保しておく方法 (大域変数に当たる) の他に、必要に応じて領域を確保する方法があります。後者のために使用されるのが「スタック」です。
一般にスタックはアドレスが大きいメモリからアドレスが小さいメモリに向かって順に使用していきます。どこまで使用しているかを指し示すのが「スタックポインタ」で、MIPSではレジスタ$sp がこれに当たり、$spが指すメモリまでが使用されていることになります。
たとえば$spから16を引くと 16バイト (= 4ワード) 分の領域を確保したことになり、4ワードの値をスタックに納めることが可能となります。この場合、0($sp), 4($sp), 8($sp), 12($sp) の4ワード分のメモリを使用できる。用途はレジスタ値の待避だけでなく、局所変数として使用しても良い。
なお、スタックに確保したデータ領域は、使用後には確保した分をきちんと解放しなくてはなりません。この例で言うと、使用後には $sp に 16 を加える必要があります。
よくあるプログラミングスタイルでは、計算を始める前にメモリを確保し、全ての計算が終わったらメモリを解放します。これにレジスタの値の待避と復元の処理を合わせると以下の例のようになります。ここでは戻り先アドレスを示す$raと、作業レジスタ$s0, $s1の3つのレジスタの内容について、スタックへの待避と復元をしています。
サブルーチンを使ったプログラムの基本的なテンプレートを載せる。
ここでは、サブルーチンsub1がサブルーチンsub2を呼び出し、sub2は他のサブルーチンを一切使用していないと仮定している。また $s0~$s2 等の他のサブルーチンを呼び出しても値が変更されない作業用レジスタを使用してる。
なお、sub2については他のサブルーチンを呼び出さないため、値の保存が保証されない$t0~$t9を値が変更される心配なしに使用することが可能である。