「30日でできる！OS自作入門」六〜八日目(harib03a〜harib05d)をUbuntu18.04/NASMで作る

#はじめに
[「30日でできる！OS自作入門」五日目](https://qiita.com/furble/items/021236479c2c4d98f111)の続きです。今回は変更箇所が少ないので、三日分をまとめました。

#環境
```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
```

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

#harib03b
Makefileだけ変えました。

```:Makefile
MAKE     = make -r
NASM     = nasm
CFLAGS	= -m32 -march=i486 -nostdlib -fno-pie
...
%.o : %.c Makefile
	gcc $(CFLAGS) -c $*.c -o $*.o
...
```
#harib03c、harib03d
新しく変える必要のある箇所無し。

#harib03e
nasmfunc.asmに追加する関数の頭にある"_"はすべて消します。

```diff
$ diff -u harib03d/nasmfunc.asm harib03e/nasmfunc.asm 
--- harib03d/nasmfunc.asm	2020-04-09 17:36:46.935452049 +0900
+++ harib03e/nasmfunc.asm	2020-04-09 17:28:08.816957515 +0900
@@ -4,6 +4,8 @@
 		GLOBAL	io_out8, io_out16, io_out32
 		GLOBAL	io_load_eflags, io_store_eflags
     GLOBAL	load_gdtr, load_idtr
+		GLOBAL	asm_inthandler21, asm_inthandler27, asm_inthandler2c
+		EXTERN	inthandler21, inthandler27, inthandler2c
 
 
 io_hlt:	; void io_hlt(void);
@@ -80,3 +82,51 @@
 		MOV		[ESP+6],AX
 		LIDT	[ESP+6]
 		RET
+
+asm_inthandler21:
+		PUSH	ES
+		PUSH	DS
+		PUSHAD
+		MOV		EAX,ESP
+		PUSH	EAX
+		MOV		AX,SS
+		MOV		DS,AX
+		MOV		ES,AX
+		CALL	inthandler21
+		POP		EAX
+		POPAD
+		POP		DS
+		POP		ES
+		IRETD
+
+asm_inthandler27:
+		PUSH	ES
+		PUSH	DS
+		PUSHAD
+		MOV		EAX,ESP
+		PUSH	EAX
+		MOV		AX,SS
+		MOV		DS,AX
+		MOV		ES,AX
+		CALL	inthandler27
+		POP		EAX
+		POPAD
+		POP		DS
+		POP		ES
+		IRETD
+
+asm_inthandler2c:
+		PUSH	ES
+		PUSH	DS
+		PUSHAD
+		MOV		EAX,ESP
+		PUSH	EAX
+		MOV		AX,SS
+		MOV		DS,AX
+		MOV		ES,AX
+		CALL	inthandler2c
+		POP		EAX
+		POPAD
+		POP		DS
+		POP		ES
+		IRETD
```

#harib04a
Makefileの依存ファイルにbootpack.hを追加します。

```bash:Makefile
%.o : %.c bootpack.h Makefile
	gcc $(CFLAGS) -c $*.c -o $*.o
```
#harib04b
必要な箇所を変更してmake runすると、どのキーを押しても2Xとしか表示されません。これは、五日目に追加したmprintf関数がmsprintf(s, "%02X", i);という表記に対応していないためです。
これをmsprintf(s, "%x", i);にすると表示されます。
しかしせっかくなので、0詰めの表記に対応できるようにmsprintfを変えてみます。

```C:msprintf.c
#include <stdarg.h>

//10進数からASCIIコードに変換
int dec2asc (char *str, int dec, int dig) {
  int len = 0, len_buf, fill; //桁数
    int buf[10];
    while (1) { //10で割れた回数（つまり桁数）をlenに、各桁をbufに格納
        buf[len++] = dec % 10;
        if (dec < 10) break;
        dec /= 10;
    }
    len_buf = len;
    fill = dig - len; //0で満たす必要のある桁数
    if(fill > 0)len_buf += fill;

    while(fill > 0){
      *(str++) = '0';
      fill--;
    }
    while (len) {
        *(str++) = buf[--len] + 0x30;
    }
    return len_buf;
}
 
//16進数からASCIIコードに変換
int hex2asc (char *str, int dec, int dig) { //16で割れた回数（つまり桁数）をlenに、各桁をbufに格納
  int len = 0, len_buf, fill; //桁数
    int buf[10];
    while (1) {
        buf[len++] = dec % 16;
        if (dec < 16) break;
        dec /= 16;
    }
    len_buf = len;
    fill = dig - len; //0で満たす必要のある桁数
  if(fill > 0)len_buf += fill;

  while(fill > 0){
    *(str++) = 0x30;
    fill--;
  }
  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, dig = 0;
    va_start (list, fmt);

    while (*fmt) {
        if(*fmt=='%') {
            fmt++;
            if('1' < *fmt && *fmt <= '9'){
              dig = *fmt - '0';
              fmt++;
            }
            switch(*fmt){
                case 'd':
                  len = dec2asc(str, va_arg (list, int), dig);
                    break;
                case 'x':
                  len = hex2asc(str, va_arg (list, int), dig);
                    break;
            }
            str += len; fmt++;
        } else {
            *(str++) = *(fmt++);
        }
    }
    *str = 0x00; //最後にNULLを追加
    va_end (list);
}
```
これでbootpack.cにmsprintf(s, "%2x", i);と書けば、iが2桁の0詰めで表示できるようになりました。これは当然9桁までしか指定できませんが、今はそれで十分なのでこのまま進めます。

#harib04c〜harib04g、harib05a〜harib05c
新しく変える必要のある箇所無し。

#harib05d
make runで実行すると、bootpack.cの
msprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
の箇所の表示がおかしくなります。これはmprintf関数がマイナス表記に対応していないためです。
そこでまた変更するついでに右寄せ表記にも対応させます。

```C:mprintf.c
#include <stdarg.h>

//10進数からASCIIコードに変換
//digは表示すべき桁数、pad0は空いた箇所を0で満たすかどうか
int dec2asc (char *str, int dec, int dig, int pad0) {
  int len = 0, len_buf, fill, fminus = 0;
    int buf[10];
    if(dec < 0){
      fminus = 1;
      dec *= -1;
    }
    while (1) { //10で割れた回数（つまり桁数）をlenに、各桁をbufに格納
        buf[len++] = dec % 10;
        if (dec < 10) break;
        dec /= 10;
    }
    len_buf = len + fminus;
    fill = dig - len - fminus; //0で満たす必要のある桁数
    if(fill > 0)len_buf += fill;

    while(fill > 0){
      if(pad0){
      *(str++) = '0';
      }else{
        *(str++) = ' ';
      }
      fill--;
    }
    if(fminus){
      *(str++) = '-';
    }
    while (len) {
        *(str++) = buf[--len] + '0';
    }
    return len_buf;
}

//16進数からASCIIコードに変換
int hex2asc (char *str, int dec, int dig, int pad0) { 
  int len = 0, len_buf, fill, fminus = 0; 
    int buf[10];
    while (1) {//16で割れた回数（つまり桁数）をlenに、各桁をbufに格納
        buf[len++] = dec % 16;
        if (dec < 16) break;
        dec /= 16;
    }
    len_buf = len + fminus;
    fill = dig - len - fminus; //0で満たす必要のある桁数
  if(fill > 0)len_buf += fill;

  while(fill > 0){
    if(pad0){
      *(str++) = '0';
    }else{
      *(str++) = ' ';
    }
    fill--;
  }
  if(fminus){
    *(str++) = '-';
  }
  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, dig = 0, pad0 = 0;
    va_start (list, fmt);

    while (*fmt) {
        if(*fmt=='%') {
            fmt++;
            if(*fmt == '0'){
              pad0 = 1;//0詰め表記
              fmt++;
            }else{
              pad0 = 0;//スペース詰め（右寄せ）表記
            }
            if('1' < *fmt && *fmt <= '9'){
              dig = *fmt - '0';
              fmt++;
            }
            switch(*fmt){
                case 'd':
                  len = dec2asc(str, va_arg (list, int), dig, pad0);
                    break;
                case 'x':
                  len = hex2asc(str, va_arg (list, int), dig, pad0);
                    break;
            }
            str += len; fmt++;
        } else {
            *(str++) = *(fmt++);
        }
    }
    *str = 0x00; //最後にNULLを追加
    va_end (list);
}
```
だいぶコードが汚くなりましたが、これでマイナス表記ができます。
msprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
で数値を4桁の右寄せで表示、%04dで0詰めの表示ができるようにもなりました。