過程が大事

学んだことを適当にアウトプットします

Mikan OSを自作してみる #10

今回やったこと

  • ソースコードの理解
    文字の表示まで出来たが、コードをしっかりと見ていなかったので理解してみる。
解説 は「//」 
/**
 * @file main.cpp
 *
 * カーネル本体のプログラムを書いたファイル.
 */

// includeはファイルの挿入する命令
//例. #include <stdio.h>  これはstdio.hというファイルを挿入し、使えるようになる
//例2. # include <ライブラリ名> これをすることで標準ライブラリを使えるようになる
#include <cstdint>  // 標準ライブラリヘッダーで固定幅の整数型の別名とマクロを提供する 
#include <cstddef>  //  標準ライブラリヘッダーで基本的な型、値、マクロを定義する

#include "frame_buffer_config.hpp"   // frame_buffer_config.hppファイルを挿入

// #@@range_begin(font_a) 

// constは変数を定数(変数の値を変更してはいけない)に定義する修飾子
/ **
* (例)
 int get()  // この関数を呼び出すとint型のxが返される 
{
    const insta x=1;
    // %d は符号あり整数を表示するフォーマット指定子(int, short)
    print("%d\n", x); // 定数を書き換えるわけではないため出力ができる  
    x = 10; // 定数を書き換えるためコンパイルエラーが発生する
    // エラー内容 (error: assigment of read-only variable 'x')
    return x;
}
*/
// unit8_t は8ビットの符号なし整数型 
const uint8_t kFontA[16] = {  
// 文字のAを描く、黒く塗る部分を1、塗らない部分を0にする 
// 0bxxxxxxxxは2進数、0oは8進数、0xは16進数
// このような字形を表すデータをフォントと呼ぶ、本データは横8ピクセル、縦16ピクセルで1文字

  0b00000000, //
  0b00011000, //    **
  0b00011000, //    **
  0b00011000, //    **
  0b00011000, //    **
  0b00100100, //   *  *
  0b00100100, //   *  *
  0b00100100, //   *  *
  0b00100100, //   *  *
  0b01111110, //  ******
  0b01000010, //  *    *
  0b01000010, //  *    *
  0b01000010, //  *    *
  0b11100111, // ***  ***
  0b00000000, //
  0b00000000, //
};

// #@@range_end(font_a)

//structはCではデータ構造を定義し、変数のみ定義できる
// c++では関数が定義できるようになり、デフォルトのアクティビティがprivate
/**
(例)
// struct Xとclass Xは同じ
struct X{
    // デフォルトのアクセシビリティは public
    int get_value();
private:
    int value;
};

class X{
    // デフォルトのアクセシビリティは private
    int value;
public:
    int get_value();
};
*/

// unit8_t は8ビットの符号なし整数型 
struct PixelColor {
  uint8_t r, g, b;
};


class PixelWriter {
 public:
  // クラス定義内に記述され、メソッドの外にある変数をメンバ変数という。
  // クラス名と同じで戻り値のない関数をコントラスタと呼び、クラスのオブジェクトを作成するときの初期化を行う。つまり、メンバ変数を初期化するということ
  //「:」の後ろは : 変数名(初期値)となっており、const変数の初期化に使われる
      // フレームバッファの構成情報(config)を受け取り、クラスのメンバ変数config_にコピーする。
      PixelWriter(const FrameBufferConfig& config)  :  config_{config}{}

 // クラス名と同じで~がついている関数をデストラクタと呼び、インスタンスを破棄するときに使われる
 // (例)
 // p = PixelWriter(); 
 //delete p;  この処理を行った時にデストラクタが呼ばれる
      /* 仮想関数はサブクラスで再定義されるメンバ関数で、これを使うことで親クラスの
      鳥クラスに鳴き声の関数を定義するとするとサブクラスになる鳩やスズメなどの鳴き声は
      それぞれ違うことになる。そこで、仮想関数を使って各サブクラスごとに鳴き声を再定義
      することで柔軟に変えることができる。この多様性をもたせることをポリモーフィズムと
   呼ぶ。*/
      virtual ~PixelWriter() = default;  // 仮想関数としてデストラクタをデフォルト生成
      // 実装を持たず、実数を持たない仮想関数である純粋仮想関数を定義する
      virtual void Write(int x, int y, const PixelColor& c) = 0;


// protectedは、派生クラス(継承先)はアクセスできるように変数と関数を定義する
 protected:
  // unit8_t*型の変数(返り値)を宣言
  uint8_t* PixelAt(int x, int y) {
    return config_.frame_buffer + 4 * (config_.pixels_per_scan_line * y + x);
  }

// privateメンバは外部からアクセスできないようにする
/**(例)
*class Class_A{ 
*private:
*    int age;
*}
*
* Class_A class_a;  // Class_Aクラスの変数class_aを生成
*class_a.age = 10; // エラーが発生する
*/

 private:
  const FrameBufferConfig&  config_; // 
};

// PixcelWriterクラスを継承する
class RGBResv8BitPerColorPixelWriter : public PixelWriter {

 public:
  // usingを使うことで親クラスのコントラスタがサブクラスのコントラスタになる
  // コントラスタはメンバ定義
  using PixelWriter::PixelWriter;

  // メイン(親)クラスの関数をサブ(子)クラスで上書きすることをオーバライド
  // オーバーライドは元の関数と同じ名前、引数、戻り値を持つように定義する
  virtual void Write(int x, int y, const PixelColor& c) override {
    auto p = PixelAt(x, y);
    p[0] = c.r;
    p[1] = c.g;
    p[2] = c.b;
  }
};

// PixelWriterクラスを継承(継承先のクラスをサブ(子)クラスと呼ぶ)
class BGRResv8BitPerColorPixelWriter : public PixelWriter {
 public:
  //PixclelWriterのコントラスタを使う
  using PixelWriter::PixelWriter;

  virtual void Write(int x, int y, const PixelColor& c) override {
    auto p = PixelAt(x, y);
    p[0] = c.b;
    p[1] = c.g;
    p[2] = c.r;
  }
};

// #@@range_begin(write_ascii)
// 
void WriteAscii(PixelWriter& writer, int x, int y, char c, const PixelColor& color) {
  if (c != 'A') { //文字cがAでなければ処理しない
    return;
  }
  for (int dy = 0; dy < 16; ++dy) {
    for (int dx = 0; dx < 8; ++dx) {
      if ((kFontA[dy] << dx) & 0x80u) {
        writer.Write(x + dx, y + dy, color); // 文字Aを描画
      }
    }
  }
}
// #@@range_end(write_ascii)

// operator 演算子  で演算子を定義できる
// 配置newの実装
void* operator new(size_t size, void* buf) {
  return buf;
}

void operator delete(void* obj) noexcept {
}

// グローバル変数を定義
char pixel_writer_buf[sizeof(RGBResv8BitPerColorPixelWriter)];
PixelWriter* pixel_writer;

extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
  switch (frame_buffer_config.pixel_format) {
    case kPixelRGBResv8BitPerColor:
     // 演算子newは「new クラス名」として使うが、今回は引数があり、配置newである。
     /* 演算子newはメモリ領域を確保した後にコントラスタを呼び出すが、
         配置newはメモリ領域の確保を行わず、引数に指定したメモリ領域の上に
         インスタンスを生成する。メモリ領域に対してコントラスタを呼び出すため
         OSにメモリ管理機能がなくても配列を使えばメモリ領域確保できる。*/
       pixel_writer = new(pixel_writer_buf)
           RGBResv8BitPerColorPixelWriter{frame_buffer_config};
       break;
    case kPixelBGRResv8BitPerColor:
       pixel_writer = new(pixel_writer_buf)
            BGRResv8BitPerColorPixelWriter{frame_buffer_config};
      break;
  }

  for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
    for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
      pixel_writer->Write(x, y, {255, 255, 255});
    }
  }
  for (int x = 0; x < 200; ++x) {
    for (int y = 0; y < 100; ++y) {
      pixel_writer->Write(x, y, {0, 255, 0});
    }
  }

  // #@@range_begin(write_aa)
// 
  WriteAscii(*pixel_writer, 50, 50, 'A', {0, 0, 0});
  WriteAscii(*pixel_writer, 58, 50, 'A', {0, 0, 0});
  // #@@range_end(write_aa)

//while(1)は無限ループ
  while (1) __asm__("hlt");
}