31日目
- 「5.2 分割コンパイル」を読んだ。1ファイルにプログラム書いているとコンパイル時間長くなるので分けるとのこと。分割後のファイル見ると、確かに短くなってた。
- 「5.3 フォントを増やそう」に突入。A以外のフォントを使えるようにする。コード中で全部定義するのはしんどいと思っていたが、既に定義されているファイルを用いるらしい。。
↓hankaku.txtには8 * 16で文字が定義されている。

- hankaku.txtをオブジェクトファイルに変換しようとしたら下記のようなエラーが発生。
mikan@mikan-server:~/workspace/mikanos/kernel$ objcopy -I binary -O elf64-x86-64 -B i386:x86-64 hankaku.bin hankaku.o
objcopy: architecture i386:x86-64 unknown
- 「objcopy」コマンドのターゲットがx86_64をサポートしていないのが原因らしい。(Ubuntu環境のためARM系のみサポートするようになっている)
- Geminiによるとクロスコンパイルツールチェーンを使うと良いとのことなので、明日やってみよう
objcopy: supported targets: elf64-littleaarch64 elf64-bigaarch64 elf32-littleaarch64 elf32-bigaarch64 elf32-littlearm elf32-bigarm pei-aarch64-little elf64-little elf64-big elf32-little elf32-big srec symbolsrec verilog tekhex binary ihex plugin
32日目
- クロスコンパイルを行うため、geminiにおすすめされた3つのパッケージをインストール
sudo apt install binutils-x86-64-linux-gnu gcc-x86-64-linux-gnu g++-x86-64-linux-gnu
-
i386:x86-64はサポートされていないように見える
mikan@mikan-server:~/workspace/mikanos/kernel$ x86_64-linux-gnu-objcopy --help
x86_64-linux-gnu-objcopy: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 pei-i386 pe-x86-64 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-bigobj-x86-64 pe-i386 srec symbolsrec verilog tekhex binary ihex plugin
- しかしオブジェクト化は成功した。何で‥?まあx86_64がサポートされているからいいんちゃう?と自分を納得させて先に進むことにする。
mikan@mikan-server:~/workspace/mikanos/kernel$ x86_64-linux-gnu-objcopy -I binary -O elf64-x86-64 -B i386:x86-64 hankaku.bin hankaku.o
- 作成したオブジェクトファイルを参照するfont.cppの写経
-
externを変数前につけると、「別のオブジェクトファイルにある変数を参照する」とコンパイラに伝える効果があるらしい。確かにブートローダのプログラムでもkernel関連で変数使う時にexternをつけていたような気がする。
font.cpp
/**
* @file font.cpp
*
* フォント描画のプログラムを集めたファイル.
*/
#include "font.hpp"
// #@@range_begin(hankaku_bin)
/**
「objcopy -I binary」でオブジェクトファイルを作成すると、リンカがそのデータにアクセス
できるようにシンボルを作成してくれる。_startがついているとデータの開始アドレスを、_endがついているとデータの終了アドレスを表す。
*/
extern const uint8_t _binary_hankaku_bin_start;
extern const uint8_t _binary_hankaku_bin_end;
extern const uint8_t _binary_hankaku_bin_size;
const uin8_t* GetFont(char c) {
auto index = 16 * static_cast<unsigned int>(c);
if (index >= reinterpret_cast<uintptr_t>(&_binary_hankaku_bin_size)) {
return nullptr;
}
return &_binary_hankaku_bin_start + index;
}
// #@@range_end(hankaku_bin)
// #@@range_begin(write_ascii)
void WriteAscii(PixelWriter& writer, int x, int y, char c, const PixelColor& color) {
const uint8_t* font = GetFont(c);
if (font == nullptr) {
return;
}
for (int dy = 0; dy < 16; ++dy){
for (int dx = 0; dx < 8; ++dx) {
if((font[dy] << dx) & 0x80u) {
writer.Write(x + dx, y + dy, color);
}
}
}
}
// #@@range_end(write_ascii)
- 文字が表示できた。font.cppでは
cのみを描画するよう指定しているが、main.cppで!から~までの文字を描画するようにしているため、画像のような感じで表示されている。
main.cpp
// #@@range_begin(write_fonts)
int i = 0;
// この部分
for (char c = '!'; c <= '~'; ++c, ++i) {
WriteAscii(*pixel_writer, 8 * i, 50, c, {0, 0, 0});
}
// #@@range_end(write_fonts)
while (1) __asm__("hlt");
33日目
- 「5.4 文字列描画とsprintf()」に入った。
- 書式付きの文字列が書けるようsprintfを用いるよう修正していた。
- sprintfの使用にあたり、「Newlib」というライブラリを使用すると便利とのこと
- OSに依存しない関数(printf,sinなど)が利用可能
- ※逆にOSに依存するライブラリには「glibc」などがある
- qemuで起動すると下記のように表示される。「1 + 2 = 3」の部分が
sprintf(buf, "1 + 2 = %d", 1 + 2);と書いている部分
34日目
- 「5.5 コンソールクラス」に入った
- とりあえず
console.hppとconsole.cppの写経はしたけど文法的な意味がわからない箇所が色々あるので明日調べる - ちなみに動かすと下記のように表示される。文字列を表示して、画面いっぱいになったらスクロールする機能らしい。まだマウス実装していないのでスクロールという言葉に非常に違和感があるが、とりあえず先に進む
35日目
- 34日目に写経した、25行文字出力するプログラムについて、スクロールの意味がわかっていなかったがどうやら連続で表示する仕組みらしい。25行の時は下記のように出力される。雰囲気は掴んだので次に進むことにした。
- 「5.6 printk()」に突入した。
- Linuxではカーネル内部からメッセージを出すためにprintk()という関数があり、便利なのでこの関数を実装するらしい。
- これも
main.cppを対象に写経した。console.cppで定義したconsoleクラスをグローバル変数として参照できるようにし、それを参照するprintk関数を作成していた。
main.cpp
// #@@range_begin(console_buf)
char console_buf[sizeof(Console)];
Console* console;
// #@@range_end(console_buf)
// #@@range_begin(printk)
int printk(const char* format, ...){
va_list ap;
int result;
char s[1024];
va_start(ap, format);
result = vsprintf(s, format, ap);
va_end(ap);
console->PutString(s);
return result;
}
// #@@range_end(printk)
// (中略)
// #@@range_begin(new_console)
console = new(console_buf) Console{*pixel_writer, {0,0,0}, {255,255,255}}
// #@@range_end(new_console)
// #@@range_begin(use_printk)
for (int i = 0; i < 27; ++i){
printk("printk: %d\n", i);
}
// #@@range_end(use_printk)
- なんか、このやり方で良いのかなと思ってきた。今はgit checkoutしてきたものを写経して、qemu上で動かすのはcheckoutしたものをそのまま動かしているがこれで自作したことにはならないし、苦労もしていないので終わっても何も身につかない気がする。
- 次の章から、真の意味で実装することをやっていこうかな。5.6のcheckoutを最後の地点とし、そこからは自分のgitにあげて管理して開発していくことをやってみたい。
36日目
- 本格的に自作OSをやっていくための環境整備をやった
- 自身の持つGithubアカウントのリポジトリで進捗を管理していけたら。
- とりあえずbuild時のパスなどはどこを変更すれば良いか確認するのは終わった。
- githubアカウントで作成したリポジトリをcloneしてこようとしたらSSH認証が必要だと怒られたので明日対応する
37日目
- sshキーで自分のgithubアカウントにアクセスできるようになった
- リモートリポジトリへプッシュできた。
- 自分のgithubで開発していく体制が整った
- 「6.1 マウスカーソル」に突入。
38日目
- マウスカーソルとデスクトップ画面を描画する
- githubにおけるタグづけされているリリースを比較する方法を理解した。
- osbook_day05fからの変更内容はこちらで主な変更ファイルは下記。
- main.cpp
- マウスカーソルの形を定義。マウスカーソルのデザインがそのまま載っている
- 定義したカーソルの形に沿って、2重forループを使用して白と黒で色を塗る
- デスクトップ画面の絵を描く
- graphics.cppで定義した「FillRectangle」「DrawRectangle」を使ってデスクトップを描画する
- graphics.hpp
- 構造体を定義。詳しいことはわからん。書いているうちにわかるだろう
- graphics.cpp
- 下記の関数を定義
- FillRectangle
- 長方形を描画して塗りつぶす
- DrawRectangle
- 長方形の枠だけ描画する
- FillRectangle
- 下記の関数を定義
- main.cpp
- 明日から上記を写経していく
39日目
- main.cppを写経した。
- マウスカーソルを書くくだりはわかりやすい。
- カーソルの定義で
[kMouseCursorWidth + 1]と書いているのはNULL文字を入れるため。NULL文字がないとコンピュータが文字列の終端を認識できず動作に不具合が生まれてしまう - 2重forループの箇所でWriteに渡す引数で
200 + dxや109 + dyと書いているのはマウスを画面左上に表示するため。
main.cpp
// 中略
// #@@ranget_begin(mouse_cursor_shape)
const int kMouseCursorWidth = 15;
const int kMouseCursorHeight = 24;
const char mouse_cursor_shape[kMouseCursorHeight][kMouseCursorWidth + 1] = {
"@ ",
"@@ ",
"@.@ ",
"@..@ ",
"@...@ ",
"@....@ ",
"@.....@ ",
"@......@ ",
"@.......@ ",
"@........@ ",
"@.........@ ",
"@..........@ ",
"@...........@ ",
"@............@ ",
"@......@@@@@@@@",
"@......@ ",
"@....@@.@ ",
"@...@ @.@ ",
"@..@ @.@ ",
"@.@ @.@ ",
"@@ @.@ ",
"@ @.@ ",
" @.@ ",
" @@@ ",
};
// #@@range_end{mouse_cursor_shape}
// 中略
// #@@range_begin(draw_dektop)
FillRectangle(*pixel_writer,
{0,0},
{kFrameWidth, kFrameHeight - 50},
kDesktopBGColor);
FillRectangle(*pixel_writer,
{0, kFrameHeight - 50},
{kFrameWidth, 50},
{1, 8, 17});
FillRectangle(*pixel_writer,
{0, kFrameHeight - 50},
{kFrameWidth / 5, 50},
{80, 80, 80});
DrawRectangle(*pixel_writer,
{10, kFrameHeight - 40},
{30, 30},
{160, 160, 160});
console = new(console_buf) Console{
*pixel_writer, kDesktopFGColor, kDesktopBGColor
};
printk("Welcome to MikanOS!\n");
// #@@range_end(draw_desktop)
// #@@range_begin(draw_mouse_cursor)
for (int dy = 0; dy < kMouseCursorHeight; ++dy) {
for (int dx = 0; dx < kMouseCursorWidth; ++dx) {
if (mouse_cursor_shape[dy][dx] == '@'){
pixel_writer->Write(200 + dx, 100 + dy, {0, 0, 0});
} else if (mouse_cursor_shape[dy][dx] == '.'){
pixel_writer->Write(200 + dx, 100 + dy, {255, 255, 255});
}
}
}
while (1) __asm__("hlt");
}
PixelWriterクラスの定義
graphics.hpp
#pragma once
// #@@range_begin(pixel_color_def)
#include "frame_buffer_config.hpp"
struct PixelColor {
uint8_t r, g, b;
};
// #@@range_end(pixel_color_def)
class PixelWriter {
public:
PixelWriter(const FrameBufferConfig& config) : config_{config} {
}
virtual ~PixelWriter() = default;
virtual void Write(int x, int y, const PixelColor& c) = 0;
protected:
uint8_t* PixelAt(int x, int y) {
return config_.frame_buffer + 4 * (config_.pixels_per_scan_line * y + x);
}
private:
const FrameBufferConfig& config_;
};
// #@@range_begin(pixel_writer_def)
class RGBResv8BitPerColorPixelWriter : public PixelWriter {
public:
using PixelWriter::PixelWriter;
virtual void Write(int x, int y, const PixelColor& c) override;
};
class BGRResv8BitPerColorPixelWriter : public PixelWriter {
public:
using PixelWriter::PixelWriter;
virtual void Write(int x, int y, const PixelColor& c) override;
};
// #@@range_end(pixel_writer_def)
// #@@range_begin(vector2d)
template <typename T>
struct Vector2D{
T x, y;
};
// #@@range_end(vector2d)
void DrawRectangle(PixelWriter& writer, const Vector2D<int> & pos,
const Vector2D<int>& size, const PixelColor& c);
void FillRectangle(PixelWriter& writer,const Vector2D<int>& pos,
const Vector2D<int>& size, const PixelColor& c);
40日目
- 一通り写経終わってさあコンパイルというところで、
引数が多すぎるというエラーが出て解決できず。 - 明日に持ち越し
graphics.cpp:34:49: error: too many arguments to function call, expected 3, have 4
writer.Write(pos.x, size.x - 1, pos.y + dy, c);
~~~~~~~~~~~~ ^
./graphics.hpp:16:16: note: 'Write' declared here
virtual void Write(int x, int y, const PixelColor& c) = 0;
^
1 error generated.
make: *** [Makefile:22: graphics.o] Error 1
41日目
- 関数の引数の書き方が間違っていただけだった。
- 修正してqemuで動かしたら下記のような画面になった。
- 「6.2 USBホストドライバ」「6.3 PCIデバイスの探索」に突入。色々意味わからんこと書いてある。出てくる用語を調べよう。
- 改修内容はこちら
登場用語について
| 項目 | 説明 |
|---|---|
| ターゲットドライバ | USB機器に搭載するドライバ |
| ホストドライバ | ホスト(コンピュータ)に搭載するドライバ。今回はこちらを作成する |
| ホストコントローラ | ホストに搭載される、USB に関わる処理を担うハードウェア |
| USBバスドライバ | USB規格で定められたAPIを提供する |
| クラスドライバ | USBのターゲットの種類ごとに作成する。例えばマウスやキーボードなどのHDクラス、オーディオ機器が属するオーディオクラス、記憶装置が属するマスストレージクラスなど |
| xHCL | Extensible Host Controller Interfaceの略。USB3.0以降に対応するためのインターフェイス仕様 |
42日目
- ファイルがめちゃくちゃ多い。写経は諦めた。少なくともUSBホストコントローラ周りはコピペしてこよう
- 2日サボってしまったし、今日も身が入らなかったしグダリつつある
43日目
- 本当に続けられなくなりそうだったので、osbook_day06bを丸コピした。
- qemu上では下記のように表示された。PCIデバイス一覧が表示されているとのことで、次でこのPCIデバイスを使ってマウスを動かしていくらしい。
- osbook_day06bとosbook_day06cの違いは下記。
- OS自作入門を教材にした動画がyoutubeにあることを知る。これを補足としてみていけると良さそう。
- 明日は「6.4 ポーリングでマウス入力」を読む&動画みていく。





