「手探りで OS作成に挑む」連載
この記事は「手探りでOS作成に挑む」連載の一部です。
全体の目次は「手探りでOS作成に挑む」連載目次を御覧下さい。
以下は開発中のソースです。
投稿時点 : https://github.com/ooe1220/KansoOS/tree/553801f2a9bdbd7df615f781f2791e90427c1791
最新 : https://github.com/ooe1220/KansoOS
使い方
パソコンに搭載されているCMOSと呼ばれるチップより日付及び時間の情報を取得します。
CMOS(RTC)から現在の日付と時刻を取得し、画面に表示します。
カーネル処理からcmos_read_rtc() を呼び出すことで動作します。
※画面は記事執筆時点のものです。現在の実装とは異なる場合があります。

時間取得の原理
CMOSの時刻取得については、以下の記事で過去に解説しています。
本記事では実装例を中心に説明するため、仕組みを知りたい方は以下も参照してください。
CMOS時間取得をBIOSのソースから読み解く ~自力でINT 0x1Aを実装してみた~
CPU が以下の 2 行を実行することで、CMOS から値を取得できます。
秒・分・時間・日・月など、取得したい項目ごとに決められたレジスタ番号が存在します。
まず CMOS の 0x70 ポートにレジスタ番号を設定し、その後 0x71 ポートから対応する値を読み取ります。CMOSの具体的なレジスタ番号はC言語のソースをご参照下さい。
OUT 070H, AL ; 読みたい CMOS レジスタ番号を指定
IN AL, 071H ; そのレジスタの値を読み込む
ここで注意が必要なのが BCD(Binary-Coded Decimal)形式です。
IN AL, 071H 命令で取得できる値は BCD 形式で格納されており、
8 ビットのうち上位 4 ビットが 10 の位、下位 4 ビットが 1 の位を表します。
例として、次の値を考えます。
00110110 = 10進数36
上位 4 ビット 0011 → 3
下位 4 ビット 0110 → 6
実装
cmos.h内にてCMOSから取得したデータを格納する構造体を定義しています。
// cmos.h
#ifndef CMOS_H
#define CMOS_H
#include "stdint.h"
struct RTC {
uint8_t second;
uint8_t minute;
uint8_t hour;
uint8_t day;
uint8_t month;
uint8_t year;
uint16_t century;
};
void cmos_read_rtc(struct RTC* rtc);
#endif
// cmos.c
#include "cmos.h"
#include "io.h" // inb/outb
static uint8_t cmos_read(uint8_t reg) {
outb(0x70, reg);
return inb(0x71);
}
// BCD (Binary-Coded Decimal) を通常の2進数に変換
// 上位4bit: 10の位 下位4bit: 1の位
static uint8_t bcd_to_bin(uint8_t val) {
return (val & 0x0F) + ((val >> 4) * 10);
}
// 現在時刻をstruct RTCに読み込む
void cmos_read_rtc(struct RTC* rtc) {
rtc->second = bcd_to_bin(cmos_read(0x00)); // 秒
rtc->minute = bcd_to_bin(cmos_read(0x02)); // 分
rtc->hour = bcd_to_bin(cmos_read(0x04)); // 時
rtc->day = bcd_to_bin(cmos_read(0x07)); // 日
rtc->month = bcd_to_bin(cmos_read(0x08)); // 月
rtc->year = bcd_to_bin(cmos_read(0x09)); // 西暦(下2桁)
rtc->century = bcd_to_bin(cmos_read(0x32)); // 西暦(上2桁)
}
cmos_read()内部からIN及びOUT命令を呼び出しています。
bcd_to_bin()関数は少しややこしいので説明します。
この関数ではBCD形式をバイナリ形式へ変換します。
例えば00110110 = 10進数36の場合
val=00110110
val & 0x0Fにて0x0F(=00001111b)とのAND(論理積)を取ることで下記4ビットのみを残します。
00110110
AND 00001111
-------------
00000110 (=6)
次に val >> 4 により、上位 4 ビット(10 の位)を下位側へ移動します。
00110110 >> 4
↓
00000011
よって3だけが残ります。上位4ビットは10の位のため、10(10進数)をかけます。
すると00000011b(=3)×10 = 30となり、足し合わせると36となります。
機械語の確認
どのような機械語が生成されたかを見てみます。
objdump -d -M intel build/cmos.o
test@test-fujitsu:~/kaihatsu/KansoOS$ objdump -d -M intel build/cmos.o
build/cmos.o: 文件格式 elf32-i386
Disassembly of section .text:
00000000 <inb>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 14 sub esp,0x14
6: e8 fc ff ff ff call 7 <inb+0x7>
b: 05 01 00 00 00 add eax,0x1
10: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
13: 66 89 45 ec mov WORD PTR [ebp-0x14],ax
17: 0f b7 45 ec movzx eax,WORD PTR [ebp-0x14]
1b: 89 c2 mov edx,eax
1d: ec in al,dx
1e: 88 45 ff mov BYTE PTR [ebp-0x1],al
21: 0f b6 45 ff movzx eax,BYTE PTR [ebp-0x1]
25: c9 leave
26: c3 ret
00000027 <outb>:
27: 55 push ebp
28: 89 e5 mov ebp,esp
2a: 83 ec 08 sub esp,0x8
2d: e8 fc ff ff ff call 2e <outb+0x7>
32: 05 01 00 00 00 add eax,0x1
37: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
3a: 8b 55 0c mov edx,DWORD PTR [ebp+0xc]
3d: 66 89 45 fc mov WORD PTR [ebp-0x4],ax
41: 89 d0 mov eax,edx
43: 88 45 f8 mov BYTE PTR [ebp-0x8],al
46: 0f b6 45 f8 movzx eax,BYTE PTR [ebp-0x8]
4a: 0f b7 55 fc movzx edx,WORD PTR [ebp-0x4]
4e: ee out dx,al
4f: 90 nop
50: c9 leave
51: c3 ret
00000052 <cmos_read>:
52: 55 push ebp
53: 89 e5 mov ebp,esp
55: 83 ec 04 sub esp,0x4
58: e8 fc ff ff ff call 59 <cmos_read+0x7>
5d: 05 01 00 00 00 add eax,0x1
62: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
65: 88 45 fc mov BYTE PTR [ebp-0x4],al
68: 0f b6 45 fc movzx eax,BYTE PTR [ebp-0x4]
6c: 50 push eax
6d: 6a 70 push 0x70
6f: e8 b3 ff ff ff call 27 <outb>
74: 83 c4 08 add esp,0x8
77: 6a 71 push 0x71
79: e8 82 ff ff ff call 0 <inb>
7e: 83 c4 04 add esp,0x4
81: c9 leave
82: c3 ret
00000083 <bcd_to_bin>:
83: 55 push ebp
84: 89 e5 mov ebp,esp
86: 83 ec 04 sub esp,0x4
89: e8 fc ff ff ff call 8a <bcd_to_bin+0x7>
8e: 05 01 00 00 00 add eax,0x1
93: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
96: 88 45 fc mov BYTE PTR [ebp-0x4],al
99: 0f b6 45 fc movzx eax,BYTE PTR [ebp-0x4]
9d: 83 e0 0f and eax,0xf
a0: 89 c1 mov ecx,eax
a2: 0f b6 45 fc movzx eax,BYTE PTR [ebp-0x4]
a6: c0 e8 04 shr al,0x4
a9: 89 c2 mov edx,eax
ab: 89 d0 mov eax,edx
ad: c1 e0 02 shl eax,0x2
b0: 01 d0 add eax,edx
b2: 01 c0 add eax,eax
b4: 01 c8 add eax,ecx
b6: c9 leave
b7: c3 ret
000000b8 <cmos_read_rtc>:
b8: 55 push ebp
b9: 89 e5 mov ebp,esp
bb: e8 fc ff ff ff call bc <cmos_read_rtc+0x4>
c0: 05 01 00 00 00 add eax,0x1
c5: 6a 00 push 0x0
c7: e8 86 ff ff ff call 52 <cmos_read>
cc: 83 c4 04 add esp,0x4
cf: 0f b6 c0 movzx eax,al
d2: 50 push eax
d3: e8 ab ff ff ff call 83 <bcd_to_bin>
d8: 83 c4 04 add esp,0x4
db: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
de: 88 02 mov BYTE PTR [edx],al
e0: 6a 02 push 0x2
e2: e8 6b ff ff ff call 52 <cmos_read>
e7: 83 c4 04 add esp,0x4
ea: 0f b6 c0 movzx eax,al
ed: 50 push eax
ee: e8 90 ff ff ff call 83 <bcd_to_bin>
f3: 83 c4 04 add esp,0x4
f6: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
f9: 88 42 01 mov BYTE PTR [edx+0x1],al
fc: 6a 04 push 0x4
fe: e8 4f ff ff ff call 52 <cmos_read>
103: 83 c4 04 add esp,0x4
106: 0f b6 c0 movzx eax,al
109: 50 push eax
10a: e8 74 ff ff ff call 83 <bcd_to_bin>
10f: 83 c4 04 add esp,0x4
112: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
115: 88 42 02 mov BYTE PTR [edx+0x2],al
118: 6a 07 push 0x7
11a: e8 33 ff ff ff call 52 <cmos_read>
11f: 83 c4 04 add esp,0x4
122: 0f b6 c0 movzx eax,al
125: 50 push eax
126: e8 58 ff ff ff call 83 <bcd_to_bin>
12b: 83 c4 04 add esp,0x4
12e: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
131: 88 42 03 mov BYTE PTR [edx+0x3],al
134: 6a 08 push 0x8
136: e8 17 ff ff ff call 52 <cmos_read>
13b: 83 c4 04 add esp,0x4
13e: 0f b6 c0 movzx eax,al
141: 50 push eax
142: e8 3c ff ff ff call 83 <bcd_to_bin>
147: 83 c4 04 add esp,0x4
14a: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
14d: 88 42 04 mov BYTE PTR [edx+0x4],al
150: 6a 09 push 0x9
152: e8 fb fe ff ff call 52 <cmos_read>
157: 83 c4 04 add esp,0x4
15a: 0f b6 c0 movzx eax,al
15d: 50 push eax
15e: e8 20 ff ff ff call 83 <bcd_to_bin>
163: 83 c4 04 add esp,0x4
166: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
169: 88 42 05 mov BYTE PTR [edx+0x5],al
16c: 6a 32 push 0x32
16e: e8 df fe ff ff call 52 <cmos_read>
173: 83 c4 04 add esp,0x4
176: 0f b6 c0 movzx eax,al
179: 50 push eax
17a: e8 04 ff ff ff call 83 <bcd_to_bin>
17f: 83 c4 04 add esp,0x4
182: 0f b6 d0 movzx edx,al
185: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
188: 66 89 50 06 mov WORD PTR [eax+0x6],dx
18c: 90 nop
18d: c9 leave
18e: c3 ret
Disassembly of section .text.__x86.get_pc_thunk.ax:
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov eax,DWORD PTR [esp]
3: c3 ret