「30日でできる！OS自作入門」五日目(harib02a〜harib02i)をUbuntu18.04/NASMで作る

#はじめに
[「30日でできる！OS自作入門」四日目](https://qiita.com/furble/items/dee29d1e889f79c08afa)の続きです。

#環境
```bash
$ uname -a
Linux furble 5.3.0-45-generic #37~18.04.1-Ubuntu SMP Fri Mar 27 15:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ nasm -v
NASM version 2.13.02

$ qemu-system-i386 --version
QEMU emulator version 2.11.1(Debian 1:2.11+dfsg-1ubuntu7.23)

$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
```

#harib02a〜harib02d
新しく変える必要のある箇所無し。

#harib02e
元ファイルでは、hankaku.txtの内容を一行が1バイトになるよう、makefont.exeを使って変換していますが、ubuntuでは使えないので代わりを作ります。
hankaku.txtから以下のhankaku.cを生成します。

```C:hankaku.c
char hankaku[4096] =  {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x38, 0x44, 0x82, 0xaa, 0xaa, 0x82, 0x82, 0xaa, 0x92, 0x44, 0x38, 0x00, 0x00, 0x00, 
... //16バイトx256行
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

```
[OS自作入門 5日目-1 【Linux】 | サラリーマンがハッカーを真剣に目指す](http://bttb.s1.valueserver.jp/wordpress/blog/2017/12/13/makeos-5-1/)
からコードをお借りしました。

```C:conv-hankakuTxt.c
#include <stdio.h>
#include <string.h>
 
int main() {
    FILE *infp;
    FILE *outfp;
    char s1[] = "char hankaku[4096] =  {\n\t";
    char s2[] = "\n};\n\n";
    char buf[256] = {0};
    char wbuf[256] = {0};
    int d;
 
    if ((infp = fopen("hankaku.txt","r")) == NULL) {
        printf("input file open error\n");
        return 0;
    }
 
    if ((outfp = fopen("hankaku.c","w")) == NULL) {
        printf("output file open error\n");
        return 0;
    }
 
    fwrite(s1, sizeof(char), strlen(s1), outfp);
 
    fgets(buf, sizeof(buf), infp);
    for(int k = 0; k < 256; k++) {
      // 2行読み飛ばす
        for(int i = 0; i < 2; i++) {
          fgets(buf, sizeof(buf), infp);
        }
        for(int i = 0; i < 16; i++) {
            d = 0;
            fgets(buf, sizeof(buf), infp);
            for(int j = 0; j < 8; j++) {
                if(buf[j] == '*') d |= (1 << (7-j));
            }
            sprintf(wbuf, "0x%02x, ", d); //0詰めの2桁16進数で表示
            if(k == 255 && i == 15) {
              //256文字分すべてを読み終わったとき
              fwrite(wbuf, 1, 4, outfp); //一番最後は", "は出力しない
            } else {
              fwrite(wbuf, 1, 6, outfp); // "0x00, "の6byte分
            }
        }
        fwrite("\n\t", 1, 2, outfp); //16B分書き込んだら改行を入れる
    }

    fwrite(s2, sizeof(char), strlen(s2), outfp);
    fclose(infp);
    fclose(outfp);
}
```
このconv-hankakuTxt.cをコンパイル、実行すればhankaku.cに先程の配列が入ることになります。
あとはMakefileに書いてある通りです。

```:Makefile
...
conv-hankakuTxt : conv-hankakuTxt.c Makefile
	gcc conv-hankakuTxt.c -o conv-hankakuTxt

hankaku.c : hankaku.txt conv-hankakuTxt Makefile
	./conv-hankakuTxt

hankaku.o : hankaku.c Makefile
	gcc -m32 -c hankaku.c -o hankaku.o

bootpack.hrb : bootpack.c har.ld nasmfunc.o hankaku.o Makefile
	gcc -fno-pie -no-pie -march=i486 -m32 -nostdlib -T har.ld bootpack.c hankaku.o nasmfunc.o -o bootpack.hrb
...
```
hankaku.cをコンパイルする際には、-m32オプションを入れてください。入れないと

```bash
gcc -c hankaku.c -o hankaku.o
gcc -fno-pie -no-pie -march=i486 -m32 -nostdlib -T har.ld bootpack.c hankaku.o nasmfunc.o -o bootpack.hrb
/usr/bin/ld: i386:x86-64 アーキテクチャ (入力ファイル`hankaku.o') は i386 出力と互換性がありません
collect2: error: ld returned 1 exit status
```
とエラーが出ます。

#harib02f
新しく変える必要のある箇所無し。

#harib02g
元ファイルの通りにbootpack.cを変更すると、下のように#include \<stdio.h\>の箇所でエラーが出ます。

```bash
In file included from bootpack.c:1:0:
/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: そのようなファイルやディレクトリはありません
 #include <bits/libc-header-start.h>
          ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
```
そこで再びこちらのサイトからsprintf関数を作ります。
[sprintfを実装する | OS自作入門 5日目-2 【Linux】 | サラリーマンがハッカーを真剣に目指す](http://bttb.s1.valueserver.jp/wordpress/blog/2017/12/17/makeos-5-2/)

```C:mprintf.c
#include <stdarg.h>
 
//10進数からASCIIコードに変換
int dec2asc (char *str, int dec) {
    int len = 0, len_buf; //桁数
    int buf[10];
    while (1) { //10で割れた回数（つまり桁数）をlenに、各桁をbufに格納
        buf[len++] = dec % 10;
        if (dec < 10) break;
        dec /= 10;
    }
    len_buf = len;
    while (len) {
        *(str++) = buf[--len] + 0x30;
    }
    return len_buf;
}
 
//16進数からASCIIコードに変換
int hex2asc (char *str, int dec) { 
    int len = 0, len_buf; //桁数
    int buf[10];
    while (1) {//16で割れた回数（つまり桁数）をlenに、各桁をbufに格納
        buf[len++] = dec % 16;
        if (dec < 16) break;
        dec /= 16;
    }
    len_buf = len;
    while (len) {
        len --;
        *(str++) = (buf[len]<10)?(buf[len] + 0x30):(buf[len] - 9 + 0x60);
    }
    return len_buf;
}
 
void msprintf (char *str, char *fmt, ...) {
    va_list list;
    int i, len;
    va_start (list, fmt);
 
    while (*fmt) {
        if(*fmt=='%') {
            fmt++;
            switch(*fmt){
                case 'd':
                    len = dec2asc(str, va_arg (list, int));
                    break;
                case 'x':
                    len = hex2asc(str, va_arg (list, int));
                    break;
            }
            str += len; fmt++;
        } else {
            *(str++) = *(fmt++);
        }   
    }
    *str = 0x00; //最後にNULLを追加
    va_end (list);
}
```
一箇所だけ、下の警告が出ていたので、
va_start (list, fmt);
に直したら警告が消えました。可変個引数の前の最後の引数名を指定すると良いようです。

```C:msprintf.c
m-sprintf.c: In function ‘msprintf’:
m-sprintf.c:39:5: warning: second parameter of ‘va_start’ not last named argument [-Wvarargs]
     va_start (list, 2);
     ^~~~~~~~
```
自分は何となく不安なので関数名をmsprintfにしましたが、stdio.hをインクルードしない限りsprintfのままで問題無いと思います。
あとは、bootpack.cの頭のstdio.hのインクルードを消し、代わりに
void msprintf (char *str, char *fmt, ...);
を追加、Makefileは下のように変更しました。

```:Makefile
MAKE     = make -r
NASM     = nasm
CFLAGS	= -m32 -march=i486 -nostdlib
...
msprintf.o : msprintf.c Makefile
	gcc -fno-pie $(CFLAGS) -c msprintf.c -o msprintf.o

bootpack.hrb : bootpack.c har.ld nasmfunc.o hankaku.o msprintf.o Makefile
	gcc -fno-pie -no-pie $(CFLAGS) -T har.ld bootpack.c hankaku.o nasmfunc.o msprintf.o -o bootpack.hrb
...
```

#harib02h、harib02i
新しく変える必要のある箇所無し。
ただし、asmファイルに追加する関数名の頭に_(アンダーバー)は付けないよう注意してください。