0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ARMv7-M thumbで自作OS作ってみた

0
Posted at

はじめに

やはり、プログラミングをやるのにあたって基礎は必要不可欠だと思い、それを解消するために自作OSを作ってみることにしました。

ARMとX86のCPUアーキテクチャの違いとは?

そもそもARMとX86は命令セットの構造の設計概念が全く違います。
実際には他にもLoongArchなどがありますが、今回は省略させていただきます。(実際のところライセンス的にARMとかx86はお金がかかるからはLoongArchを使った方が便利だからいずれそっちに移行するかも)

比較項目 RISC (ARM) CISC (x86)
代表例 ARM x86
得意分野 単純な命令 複雑な命令
命令の傾向 単純な命令が多く、構造がシンプル 複雑な命令を実行可能
特徴・背景 設計がシンプルで効率的 歴史的遺産(互換性)を引き継ぐため高機能だが「重い」

そもそも、なぜ今回ARMを採用したのかというと、x86は互換性を高めるためにBootのプロセスが複雑で難しいため、今回はARM(RISC)を採用しました。

x86の場合

[ 16-bit: リアルモード ] ──( CR0レジスタ変更 )──> [ 32-bit: プロテクトモード ] ──( EFERレジスタ変更 )──> [ 64-bit: ロングモード ]
     └─ 1MBの壁 / BIOS動作                             └─ 4GBの壁 / メモリ保護ON                             └─ 現代のOS / フルパワー

ARMの中にも種類がある

x86が16bit版、32bit版、64it版とあるようにARMにもいくつかの種類が存在します。
そしてx86とARMのようにアセンブリ言語ごとにそもそも互換性がありません。

項目 ARM32 (AArch32) ARM64 (AArch64) ARM32 Thumb (Thumb / Thumb-2)
命令コード長 固定 32-bit (4 Byte) 固定 32-bit (4 Byte) 16-bit 主体(Thumb-2は16bit/32bit混在)
汎用レジスタ数 16個 (R0〜R15) 31個 (X0〜X30) 16個(16bit命令でアクセスしやすいのは主に R0〜R7)
アドレス空間 最大 4 GB (32-bit) 理論上 16 EB (実質48〜52-bit幅) 最大 4 GB (32-bit)

アセンブリ言語とは?

アセンブリ言語とは、機械語に近いプログラミング言語であり、低水準言語と呼ばれている。
そしてアセンブラはコンパイラのことを指す。

そして機械語に強く依存するためx86,ARM,ARM32,ARM64とあるものに互換性が全くないです。
しかも、CPUのレジスタ数によってもpushの区切りを変えたりしなくてはなりません。

CPUに依存すること

基本中の基本の命令一覧

命令 役割 概要
push スタック退避 レジスタの値をスタック最上段へ保存する(4/8byte推奨)
pop スタック復元 スタック最上段の値を順次汎用レジスタへ書き戻す
movs データ転送 即値やレジスタの値を転送先レジスタに格納する
adds 加算 レジスタ値を加算して結果を格納(フラグ更新あり)
ldr メモリ読込 指定アドレスのメモリ(1ワード)からレジスタへデータを入れる
blx 関数呼び出し 戻り先を記録しながら指定アドレス/関数へジャンプする

push

pushはレジスタ(CPU内部にあるデータを一時的に保管するところ)中に、格納されているデータをメモリ上に一時的に退避させるものです。
特にbyte単位での大きさ制限はないがもし、データをはみ出してしまうと、2つのレジスタから取り出し、連結する必要が出てくるため、4byteか8byte単位で区切ることが推奨されています。

ビット数 名称 データ記述 メモリ確保記述
8 bit byte .byte .ds.b
16 bit halfword .hword .ds.w
32 bit word .word .ds.l
push {r14}

この命令はレジスタ(r14、通称Link Register)をpush(メモリ上に一時的に退避)するコードです。

movs

即値という変化しない値(命令に書き込まれている値のこと)を転送先のレジスタに書き込みます。

movs r14, pc

この命令はレジスタ(r14、通称Link Register)に4byte先(次の命令)の命令アドレスが入ります。

adds

レジスタ値(レジスタに保存されている値)を加算して書き込み先に結果を書く命令

adds r14, 0x06

この命令はr14に入っている値(0x06)に0x06(10進数の6)を加算するコードです。

ldr

メモリ上のデータをロードしてレジスタに書き込む命令です。

ldr r2, [r2]

この命令はメモリの[r2]からレジスタのr2に書き込む命令です。

blx

関数を呼び出すための、ジャンプ命令

blx r2

r2に入っている関数にジャンプする命令です。

pop

スタックしていた、汎用レジスタを1つ1つ汎用レジスタに戻し、pushしたアドレスにジャンプする

push {r14}

pop

スタックしていた汎用レジスタを元に戻しr14に入っているアドレスにジャンプする命令文です。

ARMの関数の規則

  • 引数1〜4はr0〜r3
  • 5個目からスタック経由となる

しかし、アセンブリには関数が存在しないです。

def (a=10,b=20,c=30):

C言語でインラインアセンブリ

# include <stdio.h>

int main() {
    asm volatile(
        "push {lr}"
        "adds r2, 0x06"
    );
}

asm volatileを関数のように呼ぶことで、アセンブラのニーモニック(0 1で書いたものを英語(mov)で置き換えたもの)を書くことができます。

まとめ

実際にARMv7-M thumbで自作OSを作ってみたので、次回はx86やARMの自作OSを作ってみたりもしようと思いました。

複雑な実際のコード

参考記事

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?