mikanOS 4章を整理 [前編]

# はじめに
今回は RGB を意識して
Frame Buffer を制御してみる。
前回のコードからの変更点も含めて必要なフローをまとめた

[**⇦前|mikanOS 3章を整理[後編]**](https://qiita.com/AKpirion/items/54d00f2afd9999337022)

# PIXEL control flow
![flow_chart.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/2b1e303d-e964-8d52-706c-120018578ca2.png)

# 諸情報を構造体で Kernel に共有
4 章のソースからヘッダファイル"**frame_buffer_config.hpp**"が追加されます。

```frame_buffer_config.hpp
#pragma once
#include <stdint.h>
enum PixelFormat {
  kPixelRGBResv8BitPerColor,
  kPixelBGRResv8BitPerColor,
};
struct FrameBufferConfig {
  uint8_t* frame_buffer;
  uint32_t pixels_per_scan_line;
  uint32_t horizontal_resolution;
  uint32_t vertical_resolution;
  enum PixelFormat pixel_format;
};
```

enum は初対面。[**ココ**](https://www.sejuku.net/blog/25891)が分かりやすい。
なぜ、このような事をするのか、答えは **Main.c** にありました。

```Main.c
  struct FrameBufferConfig config = {      //struct FrameBufferConfig{
    (UINT8*)gop->Mode->FrameBufferBase,    //  uint8_t* frame_buffer;
    gop->Mode->Info->PixelsPerScanLine,    //  uint32_t pixels_per_scan_line;
    gop->Mode->Info->HorizontalResolution, //  uint32_t horizontal_resolution;
    gop->Mode->Info->VerticalResolution,   //  vertical_resolution;
    0                                      //  enum PixelFormat pixel_format;};
  };
```

FrameBufferBase はピクセルそのものなので、
それ以外が何者か調べてみました。

![pixel_exp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/283df414-8f51-ebbb-a0fc-219892b295d9.png)
前述の struct FrameZBufferConfig config の後に
以下の記述が Main.c で続きます。

```Main.c
  switch (gop->Mode->Info->PixelFormat) {
    case PixelRedGreenBlueReserved8BitPerColor:
      config.pixel_format = kPixelRGBResv8BitPerColor;
      break;
    case PixelBlueGreenRedReserved8BitPerColor:
      config.pixel_format = kPixelBGRResv8BitPerColor;
      break;
    default:
      Print(L"Unimplemented pixel format: %d\n", gop->Mode->Info->PixelFormat);
      Halt();
  }
```
gop->Mode->Info->PixelFormat を確認して,
RGB であれば、**kPixelRGBResv8BitPerColor**
BGR であれば、**kPixelBGRResv8BitPerColor**としています。
※kPixelRGBResv8BitPerColor/kPixelBGRResv8BitPerColor は
　UEFI の規格書にないパラメータなので有難くコピーさせていただきます。

画像に関する緒情報を構造体 config に集約している事が分かります。
これらを kernel に渡すために Main.c の記述は以下のように変更します。

```Main.c
//[before]
    typedef void EntryPointType(UINT64, UINT64);
    EntryPointType* entry_point = (EntryPointType*)entry_addr;
    entry_point(gop->Mode->FrameBufferBase, gop->Mode->FrameBufferSize);

//[after]
    typedef void EntryPointType(const struct FrameBufferConfig*);
    EntryPointType* entry_point = (EntryPointType*)entry_addr;
    entry_point(&config);
```

# 画面全体を白塗り
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/4f4317e6-df34-761c-26c7-30048ed877f4.png)
白線 1 本目 (x,y): (0,0)->(0,1)->(0,2),,,,,->(0,vertical_resolution-1) 
白線 2 本目 (x,y): (1,0)->(1,1)->(1,2),,,,,->(1,vertical_resolution-1)
っというように sample code では**縦の白線**を繋ぎ合わせて画面を白くしています。

```main.cpp
extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
  for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
    for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
/*ココ=>*/ WritePixel(frame_buffer_config, x, y, {255, 255, 255});
    }
  }
  while (1) __asm__("hlt");
}
```

次に 1 ピクセルに色付けするための事前知識を整理します。
・1 ピクセル 32bit(4Byte)
&emsp;=>RGB の場合
&emsp;&emsp;8bit(1Byte) : Red
&emsp;&emsp;8bit(1Byte) : Green
&emsp;&emsp;8bit(1Byte) : Blue
&emsp;&emsp;8bit(1Byte) : Reserved
&emsp;=>BGR の場合
&emsp;&emsp;8bit(1Byte) : Blue
&emsp;&emsp;8bit(1Byte) : Green
&emsp;&emsp;8bit(1Byte) : Red
&emsp;&emsp;8bit(1Byte) : Reserved
・各ピクセルを総合し、配列として一塊で考えて制御する
・x 軸には表示できない領域もピクセルを配置している

シンプルに簡潔に伝えたかったのですが
むしろ長いコメントになった(伝わってるかなー、ボキャ貧でスマソ)。
```main.cpp
struct PixelColor {
  uint8_t r, g, b;
};

int WritePixel(const FrameBufferConfig& config,
               int x, int y, const PixelColor& c) {
  /*↓ 何個目の pixel を編集するか整理*/
  const int pixel_position = config.pixels_per_scan_line * y + x;

  /*↓ pixel_position が分かれば、1pixel=32bit/uint8=8bitなので 
      pixel_position *4 とすると &config.frame_buffer[4 * pixel_position] は
      [R / G / B / Reserved] or [B / G / R / Reserved]の
      最初の R or B のアドレスを呼び出せる*/
  uint8_t* p = &config.frame_buffer[4 * pixel_position];

  /*ポインタを使って各 pixel のアドレスを p と定義しているため p[0],p[1],p[2] とシンプルに書ける*/
  p[0] = c.r;
  p[1] = c.g;
  p[2] = c.b;
  return 0;
}
```

あとは pixel format に併せて RGB/BGR の並びを変える記述を追記

```main.cpp
struct PixelColor {
  uint8_t r, g, b;
};

int WritePixel(const FrameBufferConfig& config,
               int x, int y, const PixelColor& c) {
  const int pixel_position = config.pixels_per_scan_line * y + x;
  if (config.pixel_format == kPixelRGBResv8BitPerColor) {
    uint8_t* p = &config.frame_buffer[4 * pixel_position];
    p[0] = c.r;
    p[1] = c.g;
    p[2] = c.b;
  } else if (config.pixel_format == kPixelBGRResv8BitPerColor) {
    uint8_t* p = &config.frame_buffer[4 * pixel_position];
    p[0] = c.b;
    p[1] = c.g;
    p[2] = c.r;
  } else {
    return -1;
  }
  return 0;
}
```

# 画面一部を緑塗り

以下の白塗りの領域に一部上書きで緑塗りの領域を指定します。

```main.cpp
//[before]
extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
  for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
    for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
      WritePixel(frame_buffer_config, x, y, {255, 255, 255});
    }
  }
  while (1) __asm__("hlt");
}
/***********/

//[after]
extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
  for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
    for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
      WritePixel(frame_buffer_config, x, y, {255, 255, 255});
    }
  }
  for (int x = 0; x < 200; ++x) {
    for (int y = 0; y < 100; ++y) {
      WritePixel(frame_buffer_config, 100 + x, 100 + y, {0, 255, 0});
    }
  }
  while (1) __asm__("hlt");
}
```

後編はブートローダーの修正と
C++ クラスを使った main.cpp の書き換えとかボンヤリ考えてます。
(C++ 初心者だから勉強ですね、ワクワク)

make を整理するかは悩み中です。