😀

機械語

2024/05/17に公開

コンピュータシステムの理論と実践の学習内容のダンプ

ハードウェアとソフトウェアの抽象化を担うのが機械語。ハードウェアが理解できるのは特定の機械語で書かれた命令コードのみ。
今回はHack機械の仕様に合わせて機械語を構築する。
機械語では3つ物体が抽象化される。メモリ・プロセッサ・レジスタ。

メモリ

データや命令を保存するデバイスのこと。固定幅のセル(ワード / アロケーション)が並んでいてそれぞれがユニークなアドレスを持つ

プロセッサ

中央演算装置 / CPUと呼ばれる。決められた命令セットを実行できる装置。演算の結果はレジスタかメモリに格納することが可能。

レジスタ

高速にアクセスできる記憶領域。

機械語

機械語は通常バイナリコードで表現されるが、これを抽象化したと二ーモックと呼ばれる記号・英単語で記述することができる。レジスタのアドレスをR3, R4のような形式で表し、演算命令をADDとかで表すことができる。これでかかれたものがアセンブリとなる。このアセンブリから機械語を生成するプログラムをアセンブラと呼ぶ。

コマンド

算術演算と論理演算

一般的に、論理演算と加算・演算程度は機械語でサポートすることが求められる

例:レジスタの値同士の演算

ADD R2, R1, R3 // R2 <- R1 + R3
AND R1, R1, R2 // R1 <- R1 & R2

メモリアクセス

レジスタだけでなく、メモリの中の値も参照して演算する必要がある。また、計算結果をメモリに記憶させたい場合もある。
つまりloadstoreが求められる。
メモリアクセスのために、アドレッシングモードといういくつかの方法を用いてメモリのワードのアドレスを指定する。

direct addressing

アドレスを直接指定するか、シンボルを用いて参照する方法

LOAD R1 67 // R1 <- Memory[67]
LOAD R1 bar // R1 <- Memory[67], barがメモリ67を指定すると知っている場合

immediate addressing

命令コード中に現れる定数をそのまま読み込むために使われる

LOAD R1 100 // R1 <- 100

indirect addressing

メモリのアドレスは命令中にハードコードされない。その代わり必要なアドレスを保持しているメモリ位置が指定される。これをポインタと呼ぶ
x に foo + j 番地にあるデータをロードしてくるときの書き方。

ADD R1, foo, j // R1 <- foo + j
LOAD R2, R1 // R2 <- R1
STR R2, x // x <- R2

分岐命令

通常プログラムの実行は順に行われるが、反復・条件分岐・サブルーチン呼び出しなどを実行する場合に機械語はプログラムの指定された位置に移動する手段が必要。これを分岐命令という

Hack機械語の仕様

  • 16bitマシーン
  • CPU
  • メモリ (命令用・データ用)
  • メモリマップド I/Oデバイス(スクリーン用・キーボード用)

メモリアドレス空間

命令メモリとデータメモリで分かれている。16bit幅で、15bitのアドレス空間をそれぞれ持つ。
命令メモリはリードオンリー。

レジスタ

16bitレジスタ

  • D: データ値だけを保持する

  • A: データとアドレスを保持する。
    • Mというシンボルは、Aレジスタに保存されているアドレスが指すレジスタの値を意味する
    • ジャンプ命令の場合、その値はすべてAレジスタに保存されているアドレスを指すものとする
    • @valueと書いてある時、これはvalueそのものまたはこれが参照する値をAレジスタに保存することを意味する

A命令

A命令はAレジスタに15bitの値を設定する。先頭のbitは常に0を表す
@5こうすると、Aレジスタに0000000000000101を格納することになる。
使い方は3つ

  • Aレジスタを用いて定数を代入する
  • メモリ操作を行う: あらかじめAレジスタにメモリのアドレスを設定することで、その後に続くC命令でAレジスタが参照するメモリのデータを操作できる
  • 移動命令を扱う: あらかじめAレジスタに移動先のメモリアドレスを読み込む。その後、移動用のC命令で次に実行する命令の位置に移動することができる

C命令

計算命令。C命令は3つのことを行う。

  • 何を計算するか
  • 計算した結果をどこに格納するか
  • 次に何をするか

dest = comp:jumpという形式で表される。
上位3bitは1である
残りの13bitを3つの命令様式で分け合う

comp 領域

4~10bit目を使う。これらのbitを使用して、D、A、Mレジスタの値に対し、ALUに様々な関数を適用させる。
先頭bitをa, 残りをcとした時、組み合わせ次第で128通りの関数が定義可能だが、今回は28種類のみ

dest領域

11~13bit目を使う
1,2bitで計算結果を保存するレジスタがAかDかを決める。3bit目は計算結果をM(Memory[A])に保存するかを決める。
ゆえに、011なら、DレジスタとMレジスタに値を保存することができる

jump領域

14~16bit目を使用する。この3つのbitとALUの出力値に従って処理を決める。
次に何を行うかを指定する。ふたつの選択肢がある

  • 次の命令をフェッチして実行する
  • どこか別の場所に位置する命令をフェッチし実行する

シンボル

アセンブラによるコマンドは、定数もしくはシンボルをもちいてメモリアドレスを参照する。

定義済みシンボル

使いやすいようにあらかじめ特定のRAMアドレスがシンボルになっている

仮想レジスタ

R0~R15までRAMが定義されているこれはRAMの0~15アドレスのレジスタを意味する

定義済みポインタ

SP、LCL, ARG,THIS はRAMの0~4をそれぞれ参照するように定義されている

入出力ポインタ

SCREENとKBDはRAMの0x4000と0x6000を参照するようになっている。これはスクリーンとキーボードのメモリマップのベースアドレス。

ラベルシンボル

ユーザー定義のシンボル。(XXX)で定義され、このシンボルは定義された場所の次のコマンドのアドレスを表す(命令メモリ中の話)。どこからでも参照できる

変数シンボル

予約されておらずラベルシンボル形式でないと変数として扱われる。これは0x0010から始まるアドレスに一意に割り当てられる

入出力操作

スクリーンは 512 * 256のスクリーン。左上からスタートし、各行16bitのワードが32個連続して並ぶ
上から{r}行の{c}列のピクセルは{RAM}[16384 + r * 32 + c / 16] に位置するワードの{c}%16番目に相当する。0 =白、1 = 黒である。-1を入れれば、すべてのピクセルを塗りつぶすことができる(補数)

Discussion