タイマーライブラリ。


ゲーム作りのための道具作りシリーズです。
タイマーライブラリを作ってみました。
2 週間くらい構造に悩んでしまいましたがようやく望むカタチに収まりました。


任意個数のタイマーを動的に生成でき、それぞれが独立した時間計測を行うことが出来る、
そんな感じのライブラリです。


ARM9 側のタイマー割り込みを 1 つ使い、
基準となるカウンタ値をミリ秒間隔でインクリメントさせて、
各々のタイマーインスタンスは、その基準値からの差分で計測時間を割り出すような作りをしてます
(タイマー割り込み資源は ARM7, ARM9 に各 4 つまでの為、
このようなエコ設計で複数タイマーを実現しなけばなりませんでした。)。


カウンタ値の内訳ですが、ミリ秒と秒とで分け
ミリ秒が繰り上がり(1000)を起こしたら秒を 1 増やすという風に桁上がりを自前で行い、
計測時間割り出し時の繰り下がり時の計算もまた自前で行ってます。
これは、ミリ秒を含む値全てを 1 つの変数で扱った場合
long 型で 24 日強を境にオーバーフローが発生してしまうのに対して、
秒以上の位を暦時間と同じく long (time_t) 型で持った場合
タイマー割り込みの初期化から68年強オーバーフローしないことが保障される訳で、
敢えてこのような桁分けを行ってます。


秒単位の部分だけで言えば標準ライブラリにある time ライブラリを使えば良いという声も出てきそうですが
標準ライブラリを使用した場合、以下の懸案事項があるため不採用としました。

・2038 年 1 月上旬、オーバーフロー(値がマイナス値へ突入。)が発生する
・現存する NDS エミュレータでの time ライブラリによるコード動作の再現が不完全


timer library test


サンプルコードを含むプロジェクト一式、ライブラリ・フレームワーク一式は
下記サイトからダウンロードできます。


Nitro Developer Style
http://page.freett.com/ntr/


ソース


main.c

/*---------------------------------------------------------------------------------
	
	timer library test
	
	version 0.01
	May 31, 2010
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <nds.h>
#include "libmy/timer.h"


//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
	
	consoleDemoInit();
	
	timer_Init();
	timer timer1 = timer_Create();
	
	while(1){
		
		swiWaitForVBlank();
		scanKeys();
		
		timer_Now(timer1);
		
		printf("%02d:%02d:%02d.%03d\n", 
			timer_GetHour(timer1), 
			timer_GetMinute(timer1), 
			timer_GetSecond(timer1), 
			timer_GetMilliSecond(timer1)
		);
		
		if(keysDown() & KEY_A){
			timer_Start(timer1);
			
		}else if(keysDown() & KEY_B){
			timer_Stop(timer1);
			
		}else if(keysDown() & KEY_START){
			timer_Clear(timer1);
			
		}
		
	}
	
}

//---------------------------------------------------------------------------------


timer.h

/*---------------------------------------------------------------------------------
	
	timer library header
	
	version 0.01
	May 31, 2010
	
	By. REGEKATSU
	
---------------------------------------------------------------------------------*/

#ifndef _TIMER_H_
#define _TIMER_H_


typedef struct _timer *timer;


#ifdef __cplusplus
extern "C" {
#endif


//タイマーライブラリの実行を開始・停止します。
void timer_Init(void);
void timer_Done(void);

//タイマーインスタンスを生成・破棄します。
timer timer_Create(void);
timer timer_Destroy(timer Itimer);

//タイマー(時間の計測)を開始・再開します。
void timer_Start(timer Itimer);
//タイマーを停止します。
void timer_Stop(timer Itimer);
//タイマーをクリア(計測時間の初期化)します。
void timer_Clear(timer Itimer);

//タイマーの計測時間を更新します。
void timer_Now(timer Itimer);

//タイマーの計測時間を単位毎に取得します(ミリ秒・秒・分・時・年内通算日数・年)。
//計測時間更新関数(timer_Now)を実行することで、値が最新のものに更新されます。
//尚、エラーの場合は -1 が帰ります。
int timer_GetMilliSecond(timer Itimer);
int timer_GetSecond(timer Itimer);
int timer_GetMinute(timer Itimer);
int timer_GetHour(timer Itimer);
int timer_GetYearDay(timer Itimer);
int timer_GetYear(timer Itimer);


#ifdef __cplusplus
}
#endif

#endif	//_TIMER_H_


timer.c

/*---------------------------------------------------------------------------------
	
	timer library routine
	
	version 0.01
	May 31, 2010
	
	By. REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdlib.h>
#include <string.h>
#include <nds/interrupts.h>
#include <nds/timers.h>
#include "timer.h"


typedef long timer_t;

#define MSEC_MAX 1000
#define SEC_MAX    60
#define MIN_MAX    60
#define HOUR_MAX   24
#define YDAY_MAX  365

const static timer_t SECS_MIN  = SEC_MAX;//60
const static timer_t SECS_HOUR = SEC_MAX * MIN_MAX;//3600
const static timer_t SECS_YDAY = SEC_MAX * MIN_MAX * HOUR_MAX;//86400
const static timer_t SECS_YEAR = SEC_MAX * MIN_MAX * HOUR_MAX * YDAY_MAX;//31536000

#define GET_SEC(t)  ((t) % SECS_MIN)
#define GET_MIN(t)  ((t) % SECS_HOUR / SECS_MIN)
#define GET_HOUR(t) ((t) % SECS_YDAY / SECS_HOUR)
#define GET_YDAY(t) ((t) % SECS_YEAR / SECS_YDAY)
#define GET_YEAR(t) ((t) / SECS_YEAR)


typedef enum{
	TIMER_READY = 0, 
	TIMER_START, 
	TIMER_STOP
}TIMER_ACT;

typedef struct{
	int     msec;
	timer_t time;
}TimerStatus;

struct tmr{
	int tmr_msec;  /* ミリ秒 (0-999) */
	int tmr_sec;   /* 秒     (0-59)  */
	int tmr_min;   /* 分     (0-59)  */
	int tmr_hour;  /* 時     (0-23)  */
	int tmr_yday;  /* 日     (0-364) */
	int tmr_year;  /* 年             */
};

typedef struct _timer{
	
	int act;
	
	TimerStatus start;
	TimerStatus stop;
	TimerStatus measure;
	
	struct tmr tmr;
	
}_timer;


static TimerStatus st_timer = {0, 0};


void timer_Timer0Handler(void);
void timer_UpdateValue(timer Itimer);
void timer_Measurement(const TimerStatus *start, const TimerStatus *stop, TimerStatus *measure);


//---------------------------------------------------------------------------------
void timer_Init(void){
//---------------------------------------------------------------------------------
	
	memset(&st_timer, 0, sizeof(st_timer));
	
	timerStart(0, ClockDivider_256, TIMER_FREQ_256(1000), timer_Timer0Handler);
	
}

//---------------------------------------------------------------------------------
void timer_Done(void){
//---------------------------------------------------------------------------------
	
	timerStop(0);
	
}

//---------------------------------------------------------------------------------
void timer_Timer0Handler(void){
//---------------------------------------------------------------------------------
	
	if(++st_timer.msec >= MSEC_MAX){
		st_timer.msec = 0;
		st_timer.time++;
	}
	
}

//---------------------------------------------------------------------------------
timer timer_Create(void){
//---------------------------------------------------------------------------------
	
	timer Itimer;
	
	Itimer = (timer)malloc(sizeof(_timer));
	timer_Clear(Itimer);
	
	return Itimer;
	
}

//---------------------------------------------------------------------------------
timer timer_Destroy(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return NULL;
	
	free(Itimer);
	
	return NULL;
	
}

//---------------------------------------------------------------------------------
void timer_Start(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return;
	
	switch(Itimer->act){
		
	case TIMER_READY:
		Itimer->start.msec = st_timer.msec;
		Itimer->start.time = st_timer.time;
		Itimer->stop.msec  = st_timer.msec;
		Itimer->stop.time  = st_timer.time;
		Itimer->act = TIMER_START;
		break;
		
	case TIMER_STOP:
		timer_Measurement(&Itimer->measure, &st_timer, &Itimer->start);
		Itimer->stop.msec = st_timer.msec;
		Itimer->stop.time = st_timer.time;
		Itimer->act = TIMER_START;
		break;
		
	default://TIMER_START
		break;
		
	}
	
	
}

//---------------------------------------------------------------------------------
void timer_Stop(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return;
	
	if(Itimer->act != TIMER_START) return;
	
	Itimer->stop.msec = st_timer.msec;
	Itimer->stop.time = st_timer.time;
	
	timer_Measurement(&Itimer->start, &Itimer->stop, &Itimer->measure);
	timer_UpdateValue(Itimer);
	
	Itimer->act = TIMER_STOP;
	
}

//---------------------------------------------------------------------------------
void timer_Clear(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return;
	
	memset(Itimer, 0, sizeof(_timer));
	
}

//---------------------------------------------------------------------------------
void timer_Now(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return;
	
	if(Itimer->act == TIMER_START){
		Itimer->stop.msec = st_timer.msec;
		Itimer->stop.time = st_timer.time;
	}
	
	timer_Measurement(&Itimer->start, &Itimer->stop, &Itimer->measure);
	timer_UpdateValue(Itimer);
	
}

//---------------------------------------------------------------------------------
void timer_UpdateValue(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return;
	
	Itimer->tmr.tmr_msec = Itimer->measure.msec;
	Itimer->tmr.tmr_sec  = GET_SEC(Itimer->measure.time);
	Itimer->tmr.tmr_min  = GET_MIN(Itimer->measure.time);
	Itimer->tmr.tmr_hour = GET_HOUR(Itimer->measure.time);
	Itimer->tmr.tmr_yday = GET_YDAY(Itimer->measure.time);
	Itimer->tmr.tmr_year = GET_YEAR(Itimer->measure.time);
	
}

//---------------------------------------------------------------------------------
void timer_Measurement(const TimerStatus *start, const TimerStatus *stop, TimerStatus *measure){
//---------------------------------------------------------------------------------
	
	int msec;
	timer_t time;
	
	if(stop->msec < start->msec){
		time = stop->time - 1;
		msec = stop->msec + MSEC_MAX;
	}else{
		time = stop->time;
		msec = stop->msec;
	}
	
	measure->msec = msec - start->msec;
	measure->time = time - start->time;
	
}

//---------------------------------------------------------------------------------
int timer_GetMilliSecond(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return -1;
	
	return Itimer->tmr.tmr_msec;
	
}

//---------------------------------------------------------------------------------
int timer_GetSecond(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return -1;
	
	return Itimer->tmr.tmr_sec;
	
}

//---------------------------------------------------------------------------------
int timer_GetMinute(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return -1;
	
	return Itimer->tmr.tmr_min;
	
}

//---------------------------------------------------------------------------------
int timer_GetHour(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return -1;
	
	return Itimer->tmr.tmr_hour;
	
}

//---------------------------------------------------------------------------------
int timer_GetYearDay(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return -1;
	
	return Itimer->tmr.tmr_yday;
	
}

//---------------------------------------------------------------------------------
int timer_GetYear(timer Itimer){
//---------------------------------------------------------------------------------
	
	if(Itimer == NULL) return -1;
	
	return Itimer->tmr.tmr_year;
	
}

//---------------------------------------------------------------------------------