0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[32bitOS自作連載] CMOS(RTC)から時刻を読み出す仕組みと C 言語実装

0
Posted at

「手探りで OS作成に挑む」連載

この記事は「手探りでOS作成に挑む」連載の一部です。
全体の目次は「手探りでOS作成に挑む」連載目次を御覧下さい。

以下は開発中のソースです。
投稿時点 : https://github.com/ooe1220/KansoOS/tree/553801f2a9bdbd7df615f781f2791e90427c1791
最新 : https://github.com/ooe1220/KansoOS

使い方

パソコンに搭載されているCMOSと呼ばれるチップより日付及び時間の情報を取得します。
CMOS(RTC)から現在の日付と時刻を取得し、画面に表示します。
カーネル処理からcmos_read_rtc() を呼び出すことで動作します。

※画面は記事執筆時点のものです。現在の実装とは異なる場合があります。
668689d7b2de08.jpg

時間取得の原理

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 ビット 00113
下位 4 ビット 01106

実装

cmos.h内にてCMOSから取得したデータを格納する構造体を定義しています。

cmos.h
// 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
// 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    
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?