自作OSに挑戦する日記 19日目
「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。
Chapter 19 「アプリケーション」
今回やった内容
- catコマンド
- 初アプリケーション(!!)
環境
- メインPC
- MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
- macOS Mojave 10.14
- 自作OSを動作させる環境
作業記録
catコマンド
ファイルの中身を出力するようなコマンドを実装します。本では「type」となっていますが、あまり馴染みがないので「cat」として実装することにしました。
現在haribote.img(OS本体)をビルドするときにいくつかのファイルを転送するようにしているので、このファイルを読めるようにしていきます。
ディスクからファイルを読んでメモリ上に配置して…という感じではなく、OSブート時に全部メモリ上に展開しておいてそれを読むような感じで実装します。
まず、ファイル情報ですがこれはディスクの指定された番地に展開されているみたいなのでこれを読むような処理を書きます。
struct FILEINFO{ unsigned char name[8], ext[3], type; char reserve[10]; unsigned short time, date, clustno; unsigned int size; }; // ファイル情報が置かれている所へのポインタ struct FILEINFO *file_info = (struct FILEINFO *) (ADR_DISKIMG + 0x002600); // ファイル情報読み込み->表示 for(int idx = 0; idx < 224; ++ idx){ // 終わり if(file_info[idx].name[0] == 0x00){ break; } // ファイル情報がある if(file_info[idx].name[0] != 0xe5 && (file_info[idx].type & 0x18) == 0){ char s[30]; msprintf(s, "filename.ext : %d", file_info[idx].size); for(int n_idx = 0; n_idx < 8; ++ n_idx){ s[n_idx] = file_info[idx].name[n_idx]; } s[9] = file_info[idx].ext[0]; s[10] = file_info[idx].ext[1]; s[11] = file_info[idx].ext[2]; putstr8_ref(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30); cursor_y = console_newline(sheet, cursor_y); } }
ファイル情報にはファイル内容が置かれている番地も含まれているので、これを元に1バイトずつ出力するような処理を書きます。加えてエスケープ文字にも対応させます。
また、このディスクはFAT形式でフォーマットされているのでFATに対応した読み出しを行います。(ここが一番難しかった…)
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR; int *fat = (int *) memman_alloc_4k(memman, 4 * 2880); char *char_addr = (char *) memman_alloc_4k(memman, file_info[idx].size); int cursor_x = 8; // ファイル読み込み read_fat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200)); load_file(file_info[idx].clustno, file_info[idx].size, char_addr, fat, (char (ADR_DISKIMG + 0x003e00)); // バッファを空に for(int idx = 0; idx < 40; ++ idx){ s[idx] = ' '; } // 内容出力 for(int c_idx = 0; c_idx < file_info[idx].size; ++ c_idx){ s[0] = char_addr[c_idx]; s[1] = ' '; if(s[0] == 0x09){ // \t while(1){ s[0] = ' '; putstr8_ref(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1); cursor_x += 8; if(cursor_x == 8 + 384){ cursor_x = 8; cursor_y = console_newline(sheet, cursor_y); } if((cursor_x - 8) % 32 == 0){ break; } } } else if(s[0] == 0x0a){ // \n cursor_x = 8; cursor_y = console_newline(sheet, cursor_y); } else if(s[0] == 0x0d){ // 何もしない } else{ // 普通の文字 putstr8_ref(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1); cursor_x += 8; if(cursor_x == 8 + 384){ cursor_x = 8; cursor_y = console_newline(sheet, cursor_y); } } } // 後処理 memman_free_4k(memman, (int) fat, 4 * 2880); memman_free_4k(memman, (int) char_addr, file_info[idx].size); cursor_y = console_newline(sheet, cursor_y);
ファイル出力出来るようになったーーー! pic.twitter.com/fPIZgdHzZW
— ゆん (@yn0014) March 21, 2019
ファイル出力、完成です!
初アプリケーション
ついに初アプリケーションです…!
; hlt.asm CLI loop: HLT JMP loop
このアセンブリコードをビルドして、ディスクに転送します。その後、実行ファイルを取得してメモリ上に配置し、その番地へfar-jmpすることでアプリケーションを実行します。
このとき、アプリケーションが動作するセグメントも一緒に登録してあげます。
// 実行ファイル読み込み read_fat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200)); load_file(file_info[idx].clustno, file_info[idx].size, exec_addr, fat, (char *) (ADR_DISKIMG + 0x003e00)); // セグメント設定->実行 set_segmdesc(gdt + 1003, file_info[idx].size - 1, (int) exec_addr, AR_CODE32_ER); far_jmp(0, 1003 * 8);
アプリケーション実行コマンドを「hlt」で登録しました。これで「hlt」コマンドを打つと…
初アプリケーション完成!
— ゆん (@yn0014) March 21, 2019
(割り込みを禁止してHLTする) pic.twitter.com/mSb84BBxNJ
わずか4バイトのアプリケーションですが、正常に動作してくれました!!
ついにアプリケーションが実行できるようになった…
まとめ
「cat」コマンドもですが、アプリケーション実行はかなり大きな進化です。たった4バイトのアプリケーションですが大きな進化です!
+α
ファイル出力を実装する際に「大きなファイルを出力すると動作が止まってしまう」というバグに悩まされました。
解決法は「コンパイルオプション(最適化オプション)を変える」でした。ループの最適化が行われる際に一部のコードが機械語レベルで少し変わるらしく、そこでバグが発生していたみたいです。
この記事を読んでいる方で、同じような現象に遭遇された場合は試してみてください。