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?

x86ベアメタルで浮動小数を使う

0
Posted at

目的

将来x86上でOSを介さずに動く簡単な3Dエンジンを開発したいと思っています。
浮動小数を使うことになりますが、現在の環境で生成される浮動小数の命令が、x87 FPUを使用しているかSSEを使用しているかを確かめます。

参考にした筆者の過去記事

ブートローダや、32ビットへ移行する方法及びC言語でのベアメタル開発の手順は以下に纏めています。
DVDから直接起動してC言語を動かす手順
x86ベアメタル直線描画

boot.asm,swtc.asm,linker.ldは前記事中のものを流用しています。

x86ベアメタル三角形描画

C言語及び生成された機械語(逆アセンブル結果)

gcc -m32 -ffreestanding -c float.c -o float.o
objdump -d -M intel float.o
float.c
float add(float f1,float f2){
  return f1+f2;
}

float sub(float f1,float f2){
  return f1-f2;
}

float mul(float f1, float f2) {
    return f1 * f2;
}

float div(float f1, float f2) {
    return f1 / f2;
}


int main(){
  float num1=1.234;
  float num2=5.678;
  float num3=add(num1,num2);
  float num4=sub(num1,num2);
  float num5=mul(num1,num2);
  float num6=div(num1,num2);
}
逆アセンブル結果
test@test-fujitsu:~/kaihatsu$ gcc -m32 -ffreestanding -c float.c -o float.o
test@test-fujitsu:~/kaihatsu$ objdump -d -M intel float.o

float.o:     文件格式 elf32-i386


Disassembly of section .text:

00000000 <add>:
   0:	55                   	push   ebp
   1:	89 e5                	mov    ebp,esp
   3:	e8 fc ff ff ff       	call   4 <add+0x4>
   8:	05 01 00 00 00       	add    eax,0x1
   d:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  10:	d8 45 0c             	fadd   DWORD PTR [ebp+0xc]
  13:	5d                   	pop    ebp
  14:	c3                   	ret    

00000015 <sub>:
  15:	55                   	push   ebp
  16:	89 e5                	mov    ebp,esp
  18:	e8 fc ff ff ff       	call   19 <sub+0x4>
  1d:	05 01 00 00 00       	add    eax,0x1
  22:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  25:	d8 65 0c             	fsub   DWORD PTR [ebp+0xc]
  28:	5d                   	pop    ebp
  29:	c3                   	ret    

0000002a <mul>:
  2a:	55                   	push   ebp
  2b:	89 e5                	mov    ebp,esp
  2d:	e8 fc ff ff ff       	call   2e <mul+0x4>
  32:	05 01 00 00 00       	add    eax,0x1
  37:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  3a:	d8 4d 0c             	fmul   DWORD PTR [ebp+0xc]
  3d:	5d                   	pop    ebp
  3e:	c3                   	ret    

0000003f <div>:
  3f:	55                   	push   ebp
  40:	89 e5                	mov    ebp,esp
  42:	e8 fc ff ff ff       	call   43 <div+0x4>
  47:	05 01 00 00 00       	add    eax,0x1
  4c:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  4f:	d8 75 0c             	fdiv   DWORD PTR [ebp+0xc]
  52:	5d                   	pop    ebp
  53:	c3                   	ret    

00000054 <main>:
  54:	55                   	push   ebp
  55:	89 e5                	mov    ebp,esp
  57:	83 ec 20             	sub    esp,0x20
  5a:	e8 fc ff ff ff       	call   5b <main+0x7>
  5f:	05 01 00 00 00       	add    eax,0x1
  64:	d9 80 00 00 00 00    	fld    DWORD PTR [eax+0x0]
  6a:	d9 5d fc             	fstp   DWORD PTR [ebp-0x4]
  6d:	d9 80 04 00 00 00    	fld    DWORD PTR [eax+0x4]
  73:	d9 5d f8             	fstp   DWORD PTR [ebp-0x8]
  76:	ff 75 f8             	push   DWORD PTR [ebp-0x8]
  79:	ff 75 fc             	push   DWORD PTR [ebp-0x4]
  7c:	e8 fc ff ff ff       	call   7d <main+0x29>
  81:	83 c4 08             	add    esp,0x8
  84:	d9 5d f4             	fstp   DWORD PTR [ebp-0xc]
  87:	ff 75 f8             	push   DWORD PTR [ebp-0x8]
  8a:	ff 75 fc             	push   DWORD PTR [ebp-0x4]
  8d:	e8 fc ff ff ff       	call   8e <main+0x3a>
  92:	83 c4 08             	add    esp,0x8
  95:	d9 5d f0             	fstp   DWORD PTR [ebp-0x10]
  98:	ff 75 f8             	push   DWORD PTR [ebp-0x8]
  9b:	ff 75 fc             	push   DWORD PTR [ebp-0x4]
  9e:	e8 fc ff ff ff       	call   9f <main+0x4b>
  a3:	83 c4 08             	add    esp,0x8
  a6:	d9 5d ec             	fstp   DWORD PTR [ebp-0x14]
  a9:	ff 75 f8             	push   DWORD PTR [ebp-0x8]
  ac:	ff 75 fc             	push   DWORD PTR [ebp-0x4]
  af:	e8 fc ff ff ff       	call   b0 <main+0x5c>
  b4:	83 c4 08             	add    esp,0x8
  b7:	d9 5d e8             	fstp   DWORD PTR [ebp-0x18]
  ba:	90                   	nop
  bb:	c9                   	leave  
  bc:	c3                   	ret    

Disassembly of section .text.__x86.get_pc_thunk.ax:

00000000 <__x86.get_pc_thunk.ax>:
   0:	8b 04 24             	mov    eax,DWORD PTR [esp]
   3:	c3                   	ret    

加減乗除それぞれの命令

x87 FPUを使用する命令に変換されていることが分かった。

加算

   d:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  10:	d8 45 0c             	fadd   DWORD PTR [ebp+0xc]

減算

  22:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  25:	d8 65 0c             	fsub   DWORD PTR [ebp+0xc]

乗算

  37:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  3a:	d8 4d 0c             	fmul   DWORD PTR [ebp+0xc]

除算

  4c:	d9 45 08             	fld    DWORD PTR [ebp+0x8]
  4f:	d8 75 0c             	fdiv   DWORD PTR [ebp+0xc]

FPU の初期化

BIOS内でFPUを初期化しているかも知れないが、念の為に明示的に初期化しておく。

    mov eax, cr0

    ; EM(bit2)=0, TS(bit3)=0, MP(bit1)=1
    and eax, 11111111111111111111111111110011b
    or  eax, 00000000000000000000000000000010b

    mov cr0, eax

    fninit              ; FPUの初期化

動作検証

QEMU

qemu-system-i386 -cdrom os.iso -m 512 -boot d
截图 2026-07-13 20-09-03

dynabook SS M10 11L/2

sudo dvd+rw-format -blank=fast /dev/sr0
sudo growisofs -dvd-compat -Z /dev/sr0=os.iso
f5615c403df0e8
sudo dvd+rw-format -blank=fast /dev/sr0
sudo growisofs -dvd-compat -Z /dev/sr0=os.iso

コード

ビルドコマンド
rmdir tmp
mkdir tmp

nasm -f bin boot.asm -o tmp/boot.bin

gcc -m32 -ffreestanding -c kernel.c -o tmp/kernel.o
nasm -f elf32 swtc.asm -o tmp/swtc.o
ld -m elf_i386 -T linker.ld -o tmp/kernel.elf tmp/swtc.o tmp/kernel.o
objcopy -O binary tmp/kernel.elf tmp/kernel.bin
cat tmp/boot.bin tmp/kernel.bin > tmp/disk.img

mkdir iso
cp tmp/disk.img iso/

xorriso -as mkisofs \
  -o os.iso \
  -b disk.img \
  -c boot.cat \
  -no-emul-boot \
  -boot-load-size 4 \
  iso
boot.asm
boot.asm
[org 0x7C00]
bits 16

start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    sti
    
    jmp 0x0000:0x7E00

disk_error:
    hlt
    jmp disk_error

times 512-($-$$) db 0 ;CDの為0xAA55は不要
swtc.asm
swtc.asm
bits 16
global start
extern kernel_main


start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    
    mov ax, 0x0003
    int 0x10 
    ;mov ax, 0x0013
    ;int 0x10

    ; VGAリアルモード表示
    mov ax, 0xB800
    mov es, ax

    ; GDTロード
    lgdt [gdt_descriptor]

    ; プロテクトモード有効化
    mov eax, cr0
    or eax, 1
    mov cr0, eax
    
    ; FPU有効化
    mov eax, cr0
    ; EM(bit2)=0, TS(bit3)=0, MP(bit1)=1
    and eax, 11111111111111111111111111110011b
    or  eax, 00000000000000000000000000000010b
    mov cr0, eax
    fninit              ; FPUの初期化

    ; 32ビットコードへ移行
    jmp 0x08:pm_start

; -----------------------------
gdt_start:
    dq 0x0000000000000000            ; NULL

gdt_code:
    dw 0xFFFF                        ; limit 0-15
    dw 0x0000                        ; base 0-15
    db 0x00                          ; base 16-23
    db 0x9A                          ; code segment
    db 0xCF                          ; flags
    db 0x00                          ; base 24-31

gdt_data:
    dw 0xFFFF
    dw 0x0000
    db 0x00
    db 0x92                          ; data segment
    db 0xCF
    db 0x00
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

; -----------------------------
[bits 32]
pm_start:
    ; データセグメント設定
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov esp, 0x9FC00
    
    call kernel_main

.hlt_loop:
    hlt
    jmp .hlt_loop
linker.ld
linker.ld
ENTRY(start)

SECTIONS {
    . = 0x7E00;

    .text : {
        *(.text*)
    }

    .rodata : {
        *(.rodata*)
    }

    .data : {
        *(.data*)
    }

    .bss : {
        *(.bss*)
        *(COMMON)
    }
}
kernel.c

void putc(char c) {
    static int pos = 0;
    volatile unsigned short* vram = (unsigned short*)0xB8000;
    vram[pos++] = 0x0F00 | c; //属性 背景色 = 0x0 = 黒 文字色 = 0xF = 白
}

void puts(const char* s) {
    while (*s) putc(*s++);
}

/*
 unsigned int の整数を表示する
(再帰)
put_uint(123)
 └ if (123 >= 10) → put_uint(12)
                     └ if (12 >= 10) → put_uint(1)
                                         └ if (1 >= 10)? NO → putc('1')
                     putc('2')
 putc('3')
*/
void put_uint(unsigned int n) {
    if (n >= 10) put_uint(n / 10);
    putc('0' + (n % 10)); // '0'を足して数字を対応するASCIIコードに変換している
}


void put_int(int n) {
    if (n < 0) {
        putc('-');
        n = -n;
    }
    put_uint((unsigned int)n);
}

// float を表示(小数点以下 max_digits 桁)
void putf(float f, int max_digits) {
    if (f < 0.0f) {
        putc('-');
        f = -f;
    }

    // 整数部
    int int_part = (int)f;
    put_int(int_part);

    putc('.');

    // 小数部
    float frac = f - (float)int_part; // 浮動小数から整数部を引いて小数点以下を取り出す
    for (int i = 0; i < max_digits; i++) {
        frac *= 10.0f; // 小数の次の桁を、整数の一の位に持ってくる
        int digit = (int)frac; // 整数部だけを切り取る
        putc('0' + digit);
        frac -= (float)digit; // 既に表示した1の位の数を消す
    }
}

void kernel_main(void)
{
    puts("float test: ");
    float num1=1.234;
    float num2=5.678;
    float num3=num1+num2;
    putf(num3, 4);
    puts("\n");
}
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?