;------------------------------------------------------------------------------
; アラーム時計
;------------------------------------------------------------------------------
;   アラーム登録数       : 84個
;   アラーム音種類       : 長2種、短2種、短の再生時間は長の半分
;   アラーム時刻優先順位 : アラーム番号が若い物を優先
;   PCMデータフォーマット: 符号なし4bitビッグエンディアン
;   PCM再生時間(長種)    : 8.2秒@8kHz、9.1秒@7.2kHz、7.3秒@9kHz、3秒@22kHz
;   オールクリア         : 起動時に全ボタンが押されている場合はオールクリア、クリア後全ボタンがリリースされると通常起動に戻る
;   EEPROMデータ         : ([0]アラームフラグ/音0〜4 [1]時 [2]分)×84=252バイト
;------------------------------------------------------------------------------
;          SW3 (時/＋)       SW4 (分/−)
;  NORM    サウンドテスト    サウンド停止
;  TSET    時＋              分＋秒リセット
;  +CAL    速く              遅く
;  ASEL    アラーム番号＋    アラーム音選択
;  +ALTS   アラーム時＋      アラーム分＋
;------------------------------------------------------------------------------
; バージョン履歴
;   2024/01/06 Ver.0.22 アクションボタンの無視機構の実装が間違っていた、同時押しが使えなくなった
;			 メモリ初期化時にデフォルトキャリブレーションを書き込むようにした
;			 安定版
;   2024/01/06 Ver.0.21 粗キャリブレーションを実装、調整範囲を-FFF〜+FFFに拡大
;			 時刻設定の画面表示にクリック音を付けた
;   2024/01/04 Ver.0.20 クロック調整にクリック音を付けた、全体の整理
;			 アラーム登録数84に変更、粗キャリブレーション実装準備
;   2024/01/04 Ver.0.19 ADCの初期化を忘れていた、EEPROMクリア範囲を全エリアに変更
;   2024/01/02 Ver.0.18 微細キャリブレーションを画面で変更できるようにした
;   2024/01/02 Ver.0.17 クロックの微細キャリブレーションを実装
;			 EEPROMの自動クリア判定が間違っていた
;   2024/01/01 Ver.0.16 サウンド停止を実装、アラーム変更時サウンドを聞けるようにした
;			 クロックの誤差補正を0基準の相対値で設定できるようにCLKCALを設けた
;			 要求仕様の実装は全て終わり
;   2024/01/01 Ver.0.15 モードエントリー部分を切り離して後ろに持って行った、共通部をサブルーチン化
;			 TMR1補正でキャリーアップを計算していなかった
;   2023/01/01 Ver.0.14 1日12秒(139ppm)ほど早くなるのでループ基準数値を変更
;			 安定版
;   2023/01/01 Ver.0.13 PCMの最後のサンプルでNAKを送信していなかったため停止後ハングアップすることがあった。
;	https://community-ja.renesas.com/cafe_rene/forums-groups/mcu-mpu/rl78/f/forum18/8425/i2c-sda-low
;   2023/12/31 Ver.0.12 アラーム時刻設定実装、必要な機能の実装は全て終わり
;   2023/12/30 Ver.0.11 アラーム選択を実装、PCM停止を実装しようとしたがうまく行かなかった
;   2023/12/30 Ver.0.10 時刻設定を実装
;   2023/12/30 Ver.0.09 現在時刻表示、アラーム鳴動を実装
;   2023/12/30 Ver.0.08 メイン画面サウンドテストを実装
;   2023/12/30 Ver.0.07 画面遷移分岐を実装
;   2023/12/30 Ver.0.06 データEEPROMオールクリア実装
;   2023/12/29 Ver.0.05	評価基板製作に伴いプログラムの見直し、LED表示とPCM再生のみの基本版
;   2023/12/26 Ver.0.04	アラーム音を4種類に増やした
;   2023/12/24 Ver.0.03	時計の表示が出た
;   2023/12/24 Ver.0.02	LEDをリフレッシュしながら音が鳴った
;   2023/12/23 Ver.0.01	とりあえず音が鳴った
;------------------------------------------------------------------------------
; IRP,RP1,RP0を切り替える所で◆ RP=, ● IRP=をコメントに置く
; PCLATHを切り替える所で■ PCLATHをコメントに置く
; CLRWDTの場所に▼をコメントに置く
; サブルーチンからの戻りの時、
;  RP1,RP0は0に戻してリターンする。
;  IRP,PCLATHは戻さずにリターンする。
; ルーチン呼び出しの時もRP=0を基本として記述している。
;------------------------------------------------------------------------------
; デバイス	PIC16F877-20/P
; 動作周波数	外部20MHz
;------------------------------------------------------------------------------
	LIST		P=PIC16F877,R=DEC,N=0;,ST=OFF
	INCLUDE		"P16F877.INC"
	ERRORLEVEL	0,-302,-306,-307
	__IDLOCS	0x22
	__CONFIG 	0x3F76		; FOSC	= HS
					; WDTE	= Enable
					; PWRTE	= Enable
					; CP	= Not Protect
					; BOREN	= Enable
					; LVP	= I/O
					; CPD	= Not Protect
					; WRT	= Not Protect
					; DEBUG	= Disable

	#define		PCMRATE	8000	; 22026 9009 8000 7205が使用可能
;------------------------------------------------------------------------------
; 変数宣言
;------------------------------------------------------------------------------
LED_BUFFER	EQU	0x20;-0x23	; LED 表示パターンバッファ
LED_COLUMN_CNT	EQU	0x24		; 点灯させるLED桁のカウンタ FSRの加算に使用
LED_COLUMN_BIT	EQU	0x25		; 点灯させるLED桁のビット ビット出力に使用

PCM_COUNTL	EQU	0x26		; PCM再生サンプルカウンタ
PCM_COUNTH	EQU	0x27
PCM_NUMBER	EQU	0x28
CHIP_ADDR	EQU	0x29

TICK		EQU	0x2A		; 2.5ms×200カウント -200〜0の値をとる
SECOND		EQU	0x2B		; 0.5s×30カウント -15〜0の値をとる
DP_FLAG		EQU	0x2C		; 15s×4カウント DP点灯用のビットフラグ
MINUTE		EQU	0x2D		; 分
HOUR		EQU	0x2E		; 時

EEADDR		EQU	0x2F		; データEEPROMアドレス一時格納場所

MODE		EQU	0x30		; 画面のモード
ACTION		EQU	0x31
ACTION_WAIT	EQU	0x32		; getActionButtonでキー入力を無効化するダウンタイマー

ALARM_NUMBER	EQU	0x33		; 設定中のアラーム番号
ALARM_HOUR	EQU	0x34		; アラーム設定ワークエリア
ALARM_MINUTE	EQU	0x35

DISP_HOUR	EQU	0x36		; 表示する数値
DISP_MINUTE	EQU	0x37

DIV1		EQU	0x38		; DIV1 = DIV1 / DIV2
DIV2		EQU	0x39		; 非破壊
MODULO		EQU	0x3A		; MODULO = DIV1 % DIV2
DIVCNT		EQU	0x3B

MATH0L		EQU	0x3C		; LSB 四則演算用変数
MATH0H		EQU	0x3D		; MSB
MATH1L		EQU	0x3E
MATH1H		EQU	0x3F

CLKCAL		EQU	0x6A
CLKCAL_COARSE	EQU	0x6B
CLKCAL_FINE	EQU	0x6C
CLKCAL_COUNT	EQU	0x6D

PCM_FLAG	EQU	0x6E
FLAG		EQU	0x6F

; 一時使用変数
TMP00		EQU	0x70
TMP01		EQU	0x71
TMP02		EQU	0x72
TMP03		EQU	0x73
TMP10		EQU	0x74
TMP11		EQU	0x75
TMP12		EQU	0x76
TMP13		EQU	0x77
TMP20		EQU	0x78
TMP21		EQU	0x79
TMP22		EQU	0x7A
TMP23		EQU	0x7B

; 割り込みの退避用
W_		EQU	0x7C
STATUS_		EQU	0x7D
PCLATH_		EQU	0x7E
FSR_		EQU	0x7F

WC1		EQU	0x1ED		; ウェイトカウンタ
WC2		EQU	0x1EE
WC3		EQU	0x1EF
;------------------------------------------------------------------------------
; 定数宣言
;------------------------------------------------------------------------------
; クロックキャリブレーション関係 
;{
DEF_CLKCAL_COARSE EQU	0x3		; クロックの誤差補正値
					; 値を1増やすと80ppm(1日で6.912秒、1時間0.288秒)速くなる
					; https://keisan.casio.jp/exec/user/1602665236

DEF_CLKCAL_FINE	EQU	0x4E		; 値を1増やすと0.3125ppm(80ppm/256)の範囲で微調整できる
					; 1日当たり0.027秒に相当する、1日に1秒ずれるなら37、1時間に1秒ずれるなら887変更する

T1_OFFSET	EQU	65536-12499

CLKCAL_ADDR	EQU	0xFC		; クロックのキャリブレーションが入ってるEEPROMのアドレス
;};

ALARMMAX	EQU	84		; アラーム登録最大数
ANNUNC_WAIT	EQU	50		; モードテキストの表示時間 50×10ms
DEF_ACTION_WAIT	EQU	57		; ACTION_WAITの定数 2.5ms×57=143ms(1秒間に7回キー入力)

FLASH_WRADDR	EQU	0xA0		; I2Cアドレス

; PCMレートタイマー値 
;{
#if PCMRATE == 22026
T2CON_VALUE	EQU	0x00
T2_OFFSET	EQU	30
#endif

#if PCMRATE == 9009
T2CON_VALUE	EQU	0x10
T2_OFFSET	EQU	214
#endif

#if PCMRATE == 8000
T2CON_VALUE	EQU	0x10
T2_OFFSET	EQU	144
#endif

#if PCMRATE == 7205
T2CON_VALUE	EQU	0x10
T2_OFFSET	EQU	75
#endif
;}
; ENUM MODE 
;{
MODE_NORMAL	EQU	0		; モード数値
MODE_TIMESET	EQU	1
MODE_ALARM	EQU	2
MODE_ALARMTIME	EQU	3
;}
SECOND_05	EQU	0		; SECOND-0 0.5秒
; PCM_FLAG 
;{
PLAYING		EQU	0		; PCM_FLAG-0 再生中フラグ
STOPREQ		EQU	1		; PCM_FLAG-1 停止要求フラグ 
;}
; FLAG 
;{
LEDUPD		EQU	0		; FLAG-0 0.5秒経過フラグ、LED表示更新用
ALMCHK		EQU	1		; FLAG-1 1分経過フラグ、アラームチェック用
DIRTY		EQU	2		; FLAG-2 編集中のデータが変更された、保存が必要
BLINK		EQU	3		; FLAG-3 表示点滅フラグ
NOREPEAT	EQU	4		; FLAG-4 アクションボタンのリピートを無効にする
LEDUPD2		EQU	5		; FLAG-5 LED表示更新フラグ(0.5秒の影響を受けない)
;}

; DP_FLAG 
;{
DP_0		EQU	0		; DP_FLAG-0 1桁目のDPを点灯
DP_1		EQU	1		; DP_FLAG-1 2桁目のDPを点灯
DP_2		EQU	2		; DP_FLAG-2 3桁目のDPを点灯
DP_3		EQU	3		; DP_FLAG-3 4桁目のDPを点灯
;}
; PORT 
;{
SW1		EQU	4		; PORTB-4 /SW1 TIMESET
SW2		EQU	5		; PORTB-5 /SW2 ALARMSEL
SW3		EQU	6		; PORTB-6 /SW3 HOUR アクションボタン
SW4		EQU	7		; PORTB-7 /SW4 MIN  アクションボタン
SEG_DP		EQU	7		; PORTD-7 DPセグメント
;}
;------------------------------------------------------------------------------
; 7セグパターン
;------------------------------------------------------------------------------
;		 	 '.CBAFGDE'	; DPは消灯状態にしておく 
;{
CHR_0		EQU	B'01111011'	; 0
CHR_1		EQU	B'01100000'	; 1
CHR_2		EQU	B'00110111'	; 2
CHR_3		EQU	B'01110110'	; 3
CHR_4		EQU	B'01101100'	; 4
CHR_5		EQU	B'01011110'	; 5
CHR_6		EQU	B'01011111'	; 6
CHR_7		EQU	B'01111000'	; 7
CHR_8		EQU	B'01111111'	; 8
CHR_9		EQU	B'01111110'	; 9
CHR_A		EQU	B'01111101'	; 10
CHR_b		EQU	B'01001111'	; 11
CHR_C		EQU	B'00011011'	; 12
CHR_d		EQU	B'01100111'	; 13
CHR_E		EQU	B'00011111'	; 14
CHR_F		EQU	B'00011101'	; 15
CHR_SPACE	EQU	B'00000000'	; 16
CHR_HYPHEN	EQU	B'00000100'	; 17
CHR_L		EQU	B'00001011'	; 
CHR_l		EQU	B'00001001'	; 
CHR_n		EQU	B'01000101'	; 
CHR_o		EQU	B'01000111'	; 
CHR_r		EQU	B'00000101'	; 
CHR_S		EQU	B'01011110'	; 
CHR_t		EQU	B'00001111'	; 
;}
;------------------------------------------------------------------------------
#define	SIMDEBUG	0		; I2CはSSPIFでも単体ビットでもシミュレーション不可能のため、チェックをスキップするルートを用意する
	ORG	0
	CALL	hardware_reset
	CALL	system_reset
	GOTO	MAINLOOP
;------------------------------------------------------------------------------
	ORG	4
	GOTO	INTERRUPT
;------------------------------------------------------------------------------
led_table				; LED点灯パターンテーブル 
;{
	ADDWF	PCL,F
	 RETLW	CHR_0
	 RETLW	CHR_1
	 RETLW	CHR_2
	 RETLW	CHR_3
	 RETLW	CHR_4
	 RETLW	CHR_5
	 RETLW	CHR_6
	 RETLW	CHR_7
	 RETLW	CHR_8
	 RETLW	CHR_9
	 RETLW	CHR_A
	 RETLW	CHR_b
	 RETLW	CHR_C
	 RETLW	CHR_d
	 RETLW	CHR_E
	 RETLW	CHR_F
	 RETLW	CHR_SPACE
;}
;------------------------------------------------------------------------------
chip_table				; メロディチップセレクトテーブル 
;{
	ADDWF	PCL,F
	 RETLW	FLASH_WRADDR
	 RETLW	FLASH_WRADDR + 2
	 RETLW	FLASH_WRADDR + 4
	 RETLW	FLASH_WRADDR + 4
;}
;------------------------------------------------------------------------------
; メインループ
;------------------------------------------------------------------------------
MAINLOOP 
;{
	CLRWDT				; ▼
	CALL	getActionButton
	MOVWF	ACTION

	RLF	MODE,W			; 遷移チェック
	MOVWF	TMP00			; 現在モードとSW1,SW2で要求される次のモードで分岐させる
	RLF	TMP00,W
	ANDLW	0xFC
	MOVWF	TMP00

	CALL	getNextMode
	MOVWF	TMP01			; 次のモードは分岐先でも使うので記録しておく
	ADDWF	TMP00,W
	ADDWF	PCL,F
	 GOTO	proc_NORMAL		; 0→0 通常画面継続
	 GOTO	enter_TIMESET		; 0→1 時刻設定へ
	 GOTO	enter_ALARM		; 0→2 アラーム設定へ
	 GOTO	enter_CLKADJ		; 0→3 クロック調整 

	 GOTO	leave_TIMESET		; 1→0 クリック音を消して、通常画面へ
	 GOTO	proc_TIMESET		; 1→1 時刻設定継続
	 GOTO	leave_TIMESET		; 1→2 クリック音を消して、アラーム設定へ
	 GOTO	leave_TIMESET		; 1→3 クリック音を消して、クロック調整

	 GOTO	enter_NORMAL		; 2→0 通常画面へ
	 GOTO	enter_TIMESET		; 2→1 時刻設定へ
	 GOTO	proc_ALARM		; 2→2 アラーム設定継続
	 GOTO	enter_ALARMTIME		; 2→3 アラーム時刻設定へ

	 GOTO	leave_ALARMTIME		; 3→0 アラーム保存後、通常画面へ
	 GOTO	leave_ALARMTIME		; 3→1 アラーム保存後、時刻設定へ
	 GOTO	leave_ALARMTIME		; 3→2 アラーム保存後、アラーム設定へ
	 GOTO	proc_ALARMTIME		; 3→3 アラーム時刻設定継続
;}
;------------------------------------------------------------------------------
; アクションボタンの分岐
;------------------------------------------------------------------------------
proc_NORMAL 
;{
	MOVF	ACTION,W
	ADDWF	PCL,F
	 GOTO	NORMAL_DISPLAY		; 無押:
	 GOTO	NORMAL_TEST		; SW3 : サウンドテスト
	 GOTO	NORMAL_STOP		; SW4 : サウンド停止
;}
;------------------------------------------------------------------------------
proc_TIMESET 
;{
	MOVF	ACTION,W
	ADDWF	PCL,F
	 GOTO	TIMESET_DISPLAY		; 無押:
	 GOTO	TIMESET_HOUR		; SW3 : 時加算
	 GOTO	TIMESET_MINUTE		; SW4 : 分加算、秒リセット
;}
;------------------------------------------------------------------------------
proc_ALARM 
;{
	BCF	FLAG,NOREPEAT
	MOVF	ACTION,W
	ADDWF	PCL,F
	 GOTO	ALARM_DISPLAY		; 無押:
	 GOTO	ALARM_NUM_INC		; SW3 : アラーム番号加算
	 GOTO	ALARM_TYPE_SEL		; SW4 : アラーム音種類加算
;}
;------------------------------------------------------------------------------
proc_ALARMTIME 
;{
	MOVF	ACTION,W
	ADDWF	PCL,F
	 GOTO	ALARMTIME_DISPLAY	; 無押:
	 GOTO	ALARMTIME_HOUR		; SW3 : 時加算
	 GOTO	ALARMTIME_MINUTE	; SW4 : 分加算
;}
;------------------------------------------------------------------------------
leave_TIMESET
;{
	MOVLW	8
	MOVWF	PORTA
	MOVF	TMP01,W			; getNextModeの戻り値
	ADDWF	PCL,F
	 GOTO	enter_NORMAL		; 1→0 通常画面へ
	 GOTO	$			; ここには来ない
	 GOTO	enter_ALARM		; 1→2 アラーム設定へ
	 GOTO	enter_CLKADJ		; 1→3 クロック調整
;}
;------------------------------------------------------------------------------
leave_ALARMTIME				; DIRTYならアラーム時刻を保存してから他のモードに移る
;{
	BTFSS	FLAG,DIRTY
	 GOTO	LEAVE_ALARMTIME		; 保存不要
	CALL	getAlarmAddress
	ADDLW	1
	MOVWF	EEADDR
	MOVF	ALARM_HOUR,W
	CALL	eewrite
	MOVF	ALARM_MINUTE,W
	CALL	eewrite
	CALL	eewait
LEAVE_ALARMTIME
	MOVF	TMP01,W			; getNextModeの戻り値
	ADDWF	PCL,F
	 GOTO	enter_NORMAL		; 3→0 通常画面継続
	 GOTO	enter_TIMESET		; 3→1 時刻設定へ
	 GOTO	enter_ALARM_NOCLR	; 3→2 アラーム設定へ
;}
;------------------------------------------------------------------------------
proc_CLKADJ				; モードを与えずここでループさせる
;{
	CLRWDT				; ▼
	CALL	getNextMode
	MOVWF	TMP00
	SUBLW	3
	BTFSS	STATUS,Z
	 CALL	clkadj_DIRTY		; 他モードへ移行するときは保存する
	MOVF	TMP00,W
	ADDWF	PCL,F
	 GOTO	enter_NORMAL		; X→0 通常画面へ
	 GOTO	enter_TIMESET		; X→1 時刻設定へ
	 GOTO	enter_ALARM		; X→2 アラーム設定へ

	CALL	getActionButton
	ADDWF	PCL,F
	 GOTO	CLKADJ_DISP		; 無押:
	 GOTO	CLKADJ_INC		; SW3 : 速く
	 GOTO	CLKADJ_DEC		; SW4 : 遅く
;}
___PCL___
;------------------------------------------------------------------------------
; 各モードエントリーポイント
;  PCLを使った分岐が無いエントリーポイントは後に持ってくる
;------------------------------------------------------------------------------
enter_NORMAL 
;{
	MOVLW	MODE_NORMAL
	MOVWF	MODE
	CALL	stop_pcm
	BSF	FLAG,LEDUPD		; 表示更新
	BCF	FLAG,BLINK		; 点滅なし
	BCF	FLAG,ALMCHK		; 他のモードで処理しなかったアラームを破棄
	CLRF	ALARM_NUMBER
	GOTO	proc_NORMAL
;}
;------------------------------------------------------------------------------
enter_TIMESET 
;{
	MOVLW	MODE_TIMESET
	MOVWF	MODE
	CALL	stop_pcm
	CALL	str_tSEt
	MOVLW	ANNUNC_WAIT
	CALL	wait_w0ms

	BSF	FLAG,LEDUPD		; 表示更新
	MOVLW	5			; クリック音
	MOVWF	PORTA
	GOTO	proc_TIMESET
;}
;------------------------------------------------------------------------------
enter_ALARM				; 初回エントリー
;{
	CLRF	ALARM_NUMBER
;}
enter_ALARM_NOCLR			; アラーム時刻から戻ってきたときのエントリー
;{
	MOVLW	MODE_ALARM
	MOVWF	MODE
	CALL	stop_pcm
	CALL	str_ASEL
	MOVLW	ANNUNC_WAIT
	CALL	wait_w0ms
	
	BSF	FLAG,LEDUPD2		; 表示更新
	GOTO	proc_ALARM
;}
;------------------------------------------------------------------------------
enter_ALARMTIME 
;{
	MOVLW	MODE_ALARMTIME
	MOVWF	MODE
	CALL	stop_pcm
	CALL	str_ALtS
	MOVLW	ANNUNC_WAIT
	CALL	wait_w0ms

	BSF	FLAG,LEDUPD		; 表示更新
	BCF	FLAG,DIRTY		; クリーン状態
	CALL	getAlarmAddress		; アラームの時刻をロード
	ADDLW	1
	CALL	eeread_w
	MOVWF	ALARM_HOUR
	CALL	eeread
	MOVWF	ALARM_MINUTE
	GOTO	proc_ALARMTIME
;}
;------------------------------------------------------------------------------
enter_CLKADJ 
;{
	CALL	str_CAL
	MOVLW	ANNUNC_WAIT
	CALL	wait_w0ms

	BSF	FLAG,LEDUPD2
	BCF	FLAG,DIRTY
	MOVLW	5			; クリック音
	MOVWF	PORTA
	GOTO	proc_CLKADJ
;}
;------------------------------------------------------------------------------
; 各モードの処理
;------------------------------------------------------------------------------
NORMAL_TEST 
;{
	CALL	stop_pcm
	MOVF	ALARM_NUMBER,W
	CALL	play_pcm
	INCF	ALARM_NUMBER,W
	ANDLW	3
	MOVWF	ALARM_NUMBER
	CALL	releaseActionButton
	GOTO	NORMAL_DISPLAY
;}
NORMAL_STOP 
;{
	CALL	stop_pcm
;}
NORMAL_DISPLAY 
;{
	BTFSC	FLAG,LEDUPD		; 0.5秒ごとに画面を再描画
	 CALL	clock_to_disp
	BTFSC	FLAG,ALMCHK		; 1分毎にアラームチェック
	 CALL	check_alarm
	GOTO	MAINLOOP
;}
;------------------------------------------------------------------------------
TIMESET_HOUR 
;{
	BSF	FLAG,LEDUPD		; 画面更新
	BCF	FLAG,BLINK		; 時は秒をリセットしないため見にくくならないように点滅を止める
	INCF	HOUR,W
	MOVWF	HOUR
	SUBLW	24
	BTFSC	STATUS,Z
	 CLRF	HOUR
	GOTO	TIMESET_DISPLAY
;}
TIMESET_MINUTE 
;{
	BSF	FLAG,LEDUPD		; 画面更新
	CALL	clear_second		; 秒をリセットするので初回表示は勝手に点灯になる
	INCF	MINUTE,W
	MOVWF	MINUTE
	SUBLW	60
	BTFSC	STATUS,Z
	 CLRF	MINUTE
;}
TIMESET_DISPLAY 
;{
	BTFSS	FLAG,LEDUPD
	 GOTO	TIMESET_DISPLAY2
	MOVLW	0x0F
	XORWF	PORTA,F
	CALL	clock_to_disp		; アラームは実行しない
TIMESET_DISPLAY2 
	BSF	FLAG,BLINK		; 点滅あり
	GOTO	MAINLOOP
;}
;------------------------------------------------------------------------------
ALARM_NUM_INC 
;{
	BSF	FLAG,LEDUPD2		; 0.5秒の影響を受けずに画面更新
	INCF	ALARM_NUMBER,F
	MOVLW	ALARMMAX
	SUBWF	ALARM_NUMBER,W
	BTFSC	STATUS,Z
	 CLRF	ALARM_NUMBER
	CALL	stop_pcm
	GOTO	ALARM_DISPLAY
;}
ALARM_TYPE_SEL 
;{
	BSF	FLAG,LEDUPD2
	BSF	FLAG,NOREPEAT		; EEPROMに即時保存するためキーリピートを行わない
	CALL	getAlarmAddress		; 現在の設定値+1
	MOVWF	TMP00
	CALL	eeread_w
	ADDLW	1
	MOVWF	TMP01
	SUBLW	5
	BTFSC	STATUS,Z
	 CLRF	TMP01
	MOVF	TMP00,W			; 保存
	MOVWF	EEADDR
	MOVF	TMP01,W
	CALL	eewrite
	CALL	eewait			; 保存が終わるまで待つ
	CALL	stop_pcm
	MOVF	TMP01,W
	ADDLW	-1
	BTFSC	STATUS,C
	 CALL	play_pcm
;}
ALARM_DISPLAY 
;{
	BTFSS	FLAG,LEDUPD2		; 画面表示
	 GOTO	MAINLOOP
	BCF	FLAG,LEDUPD2
	INCF	ALARM_NUMBER,W		; アラーム番号の表示は01〜85で表示させる
	MOVWF	DIV1
	MOVLW	10
	MOVWF	DIV2
	CALL	div8
	MOVF	DIV1,W
	CALL	led_table
	MOVWF	LED_BUFFER
	MOVF	MODULO,W
	CALL	led_table
	MOVWF	LED_BUFFER + 1

	CALL	getAlarmAddress		; 設定値読み出し
	CALL	eeread_w
	MOVWF	TMP00
	BTFSC	STATUS,Z
	 GOTO	ALARM_OFF		; 0の時はアラームオフ表示
	MOVLW	CHR_SPACE
	MOVWF	LED_BUFFER + 2
	MOVF	TMP00,W
	CALL	led_table
	GOTO	ALARM_DISPLAY2
ALARM_OFF				; oF文字
	MOVLW	CHR_o
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_F
ALARM_DISPLAY2
	MOVWF	LED_BUFFER + 3
	BTFSC	FLAG,NOREPEAT
	 CALL	releaseActionButton
	GOTO	MAINLOOP
;}
;------------------------------------------------------------------------------
ALARMTIME_HOUR 
;{
	CALL	alarmtime_DIRTY
	INCF	ALARM_HOUR,W
	MOVWF	ALARM_HOUR
	SUBLW	24
	BTFSC	STATUS,Z
	 CLRF	ALARM_HOUR
	GOTO	ALARMTIME_DISPLAY
;}
ALARMTIME_MINUTE 
;{
	CALL	alarmtime_DIRTY
	INCF	ALARM_MINUTE,W
	MOVWF	ALARM_MINUTE
	SUBLW	60
	BTFSC	STATUS,Z
	 CLRF	ALARM_MINUTE
;}
ALARMTIME_DISPLAY 
;{
	BTFSC	FLAG,LEDUPD
	 CALL	alarm_to_disp		; アラームは実行しない
	BSF	FLAG,BLINK		; 点滅あり
	GOTO	MAINLOOP
;}
alarmtime_DIRTY 
;{
	BSF	FLAG,LEDUPD
	BSF	FLAG,DIRTY		; 要保存
	BCF	FLAG,BLINK		; 点滅止める
	RETURN
;}
;------------------------------------------------------------------------------
CLKADJ_DEC 
;{
	CALL	clkadj_loadmath
	CALL	neg16			; 正負反転して加算し反転したらデクリメントになる
	CALL	clkadj_inc
	CALL	neg16
	GOTO	CLKADJ_DISPUPD
;}
CLKADJ_INC 
;{
	CALL	clkadj_loadmath
	CALL	clkadj_inc
;}
CLKADJ_DISPUPD				; 更新時共通
;{
	MOVF	MATH0H,W
	MOVWF	CLKCAL_COARSE
	ADDLW	LOW T1_OFFSET
	MOVWF	CLKCAL
	MOVF	MATH0L,W
	MOVWF	CLKCAL_FINE

	BSF	FLAG,LEDUPD2
	BSF	FLAG,DIRTY
;}
CLKADJ_DISP 
;{
	BTFSS	FLAG,LEDUPD
	 GOTO	CLKADJ_DISP2
	MOVLW	0x0F
	XORWF	PORTA,F
	BCF	FLAG,LEDUPD
CLKADJ_DISP2
	BTFSS	FLAG,LEDUPD2
	 GOTO	proc_CLKADJ

	CALL	clkadj_loadmath
	BTFSC	CLKCAL_COARSE,7		; 正負判定
	 GOTO	CLKADJ_DISP_NEG
CLKADJ_DISP_POS
	MOVLW	CHR_SPACE
	MOVWF	LED_BUFFER
	GOTO	CLKADJ_DISP_COM
CLKADJ_DISP_NEG
	MOVLW	CHR_HYPHEN
	MOVWF	LED_BUFFER
	CALL	neg16
CLKADJ_DISP_COM
	MOVF	MATH0H,W
	ANDLW	0x0F
	CALL	led_table
	MOVWF	LED_BUFFER + 1
	BSF	LED_BUFFER + 1,SEG_DP

	SWAPF	MATH0L,W
	ANDLW	0x0F
	CALL	led_table
	MOVWF	LED_BUFFER + 2
	MOVF	MATH0L,W
	ANDLW	0x0F
	CALL	led_table
	MOVWF	LED_BUFFER + 3
	BCF	FLAG,LEDUPD2
	GOTO	proc_CLKADJ
;}
clkadj_DIRTY				; DIRTYの時は保存する、クリック音を消す
;{
	MOVLW	8
	MOVWF	PORTA
	BTFSS	FLAG,DIRTY
	 RETURN
	MOVLW	CLKCAL_ADDR
	MOVWF	EEADDR
	MOVF	CLKCAL_COARSE,W
	CALL	eewrite
	MOVF	CLKCAL_FINE,W
	CALL	eewrite
	CALL	eewait			; 保存が終わるまで待つ
	RETURN
;}
clkadj_loadmath 
;{
	MOVF	CLKCAL_COARSE,W
	MOVWF	MATH0H
	MOVF	CLKCAL_FINE,W
	MOVWF	MATH0L
	RETURN
;}
clkadj_inc
;{
	CLRF	MATH1H
	MOVLW	1
	MOVWF	MATH1L
	CALL	add16
	
	MOVF	MATH0H,W		; 上限チェック
	ADDLW	0x10
	SUBLW	0x1F
	BTFSC	STATUS,C
	 RETURN
	MOVLW	0x0F
	MOVWF	MATH0H
	MOVLW	0xFF
	MOVWF	MATH0L
	RETURN
;}
;------------------------------------------------------------------------------
; 割り込みハンドラ
;------------------------------------------------------------------------------
INTERRUPT 
;{
	MOVWF	W_			; Wセーブ
	SWAPF	STATUS,W		; STATUSセーブ
	MOVWF	STATUS_
;	MOVF	PCLATH,W		; PCLATHセーブ
;	MOVWF	PCLATH_
	MOVF	FSR,W			; FSRセーブ
	MOVWF	FSR_
	CLRF	STATUS
;}
	BTFSS	PIR1,TMR2IF
	 GOTO	INT_TMR1
;--- TMR2補正 
;{
	BCF	PIR1,TMR2IF		; PCM転送割り込み
	MOVLW	T2_OFFSET		; 割り込みタイミング調整
	ADDWF	TMR2,F
;}
;--- PCM再生制御 
;{
	BTFSC	PCM_COUNTL,0		; MSB/LSBで処理を切り替え
	 GOTO	PCM_SEND_LSB
PCM_SEND_MSB				; MSB4bit 読み出し待ち・DAC転送・ACK送信
#if SIMDEBUG != 1			; 読み出し完了待ち
	BTFSS	PIR1,SSPIF		;  基本的にここでループがかかる事はありえない
	 GOTO	$-1
#endif
#if SIMDEBUG == 1
	BSF	PIR1,SSPIF
	NOP
#endif
	SWAPF	SSPBUF,W		; DAC転送
	ANDLW	0x0F
	MOVWF	PORTA

	INCF	PCM_COUNTL,F		; 次回はLSBなのでメモリ読み出しを省く
	BTFSC	STATUS,Z
	 INCF	PCM_COUNTH,F

	BTFSS	PCM_FLAG,STOPREQ
	 GOTO	PCM_ACK
	MOVLW	0xFF
	MOVWF	PCM_COUNTL
	MOVWF	PCM_COUNTH
PCM_ACK
	MOVF	PCM_COUNTL,W		; ACK送信
	ANDWF	PCM_COUNTH,W
	ADDLW	1
	BCF	PIR1,SSPIF
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	SSPCON2,ACKDT
	BTFSC	STATUS,Z		; 最後のデータはNAKを送信する
	 BSF	SSPCON2,ACKDT
	BSF	SSPCON2,ACKEN
	BCF	STATUS,RP0		; ◆ RP=0
	GOTO	INT_TMR1		; 時間計測も同時処理しておく

PCM_SEND_LSB				; LSB4bit DAC転送・次の読み出し開始
	NOP				; タイミング調整
	MOVF	SSPBUF,W		; DAC転送
	ANDLW	0x0F
	MOVWF	PORTA

	INCFSZ	PCM_COUNTL,F		; 次回はMSBアドレスなのでメモリを読み出す
	 GOTO	PCM_NEXTADDR		; 65,536サンプル全て読み出したか?
	INCFSZ	PCM_COUNTH,F
	 GOTO	PCM_NEXTADDR

	BCF	T2CON,TMR2ON		; 読み出すデータが残っていない場合PCMタイマー停止
	BCF	PIR1,SSPIF		; i2cstop
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	PIE1,SSPIE
	BSF	SSPCON2,PEN
	BCF	STATUS,RP0		; ◆ RP=0
	GOTO	INT_TMR1		; 時間計測も同時処理しておく
PCM_NEXTADDR				; 読み出すデータが残っている場合
	BCF	PIR1,SSPIF
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	SSPCON2,RCEN		; 受信開始
	BCF	STATUS,RP0		; ◆ RP=0 時間計測も同時処理しておく
;}
INT_TMR1				; LED桁間、時間計測割り込み
	BTFSS	PIR1,TMR1IF
	 GOTO	INT_SSPIF
;--- TMR1補正 
;{
	BCF	PIR1,TMR1IF
	MOVF	CLKCAL_FINE,W		; TMR1の割り込み周期をPWMのように細かく制御する
	SUBWF	CLKCAL_COUNT,W
	MOVF	CLKCAL,W		; 割り込みタイミング調整
	BTFSS	STATUS,C
	 ADDLW	1
	ADDWF	TMR1L,F
	MOVLW	HIGH T1_OFFSET
	BTFSC	STATUS,C
	 ADDLW	1
	ADDWF	TMR1H,F
	INCF	CLKCAL_COUNT,F
	MOVLW	B'00100000'		; クロック調整用200Hz出力
	XORWF	PORTC,F
;}
;--- LEDダイナミック点灯制御 
;{
	MOVLW	LED_BUFFER
	ADDWF	LED_COLUMN_CNT,W
	MOVWF	FSR
	CLRF	PORTB			; 一旦消す
	MOVF	INDF,W			; セグメントパターンロード
	MOVWF	PORTD
	MOVF	LED_COLUMN_BIT,W	; 桁ビット
	MOVWF	PORTB			; 点灯

	INCF	LED_COLUMN_CNT,W	; 次回用に次の桁へ進める
	ANDLW	3
	MOVWF	LED_COLUMN_CNT
	BCF	STATUS,C
	BTFSC	STATUS,Z
	 BSF	STATUS,C
	RLF	LED_COLUMN_BIT,W
	ANDLW	0x0F
	MOVWF	LED_COLUMN_BIT
;}
;--- アクションボタン無視タイマー 
;{
	MOVF	ACTION_WAIT,W		; getActionButtonでキー入力を無効化するタイマー
	BTFSS	STATUS,Z
	 DECF	ACTION_WAIT,F
;}
;--- 時間計測 
;{
	INCFSZ	TICK,F			; 2.5ms×200回=500msカウント
	 GOTO	INT_RETURN
	MOVLW	-200
	MOVWF	TICK
	BSF	FLAG,LEDUPD
	INCFSZ	SECOND,F		; 500ms×30回=15秒カウント
	 GOTO	INT_RETURN
	MOVLW	-30
	MOVWF	SECOND
	BCF	STATUS,C		; 15秒×4回=1分カウント
	RLF	DP_FLAG,F
	BTFSS	DP_FLAG,4
	 GOTO	INT_RETURN
	MOVLW	1
	MOVWF	DP_FLAG
	BSF	FLAG,ALMCHK
	INCF	MINUTE,F		; 1分×60回=1時間カウント
	MOVLW	60
	SUBWF	MINUTE,W
	BTFSS	STATUS,Z
	 GOTO	INT_RETURN
	CLRF	MINUTE
	INCF	HOUR,F			; 1時間×24回=1日カウント
	MOVLW	24
	SUBWF	HOUR,W
	BTFSC	STATUS,Z
	 CLRF	HOUR
	GOTO	INT_RETURN
;}
INT_SSPIF
	BTFSS	PIR1,SSPIF
	 GOTO	INT_RETURN
;--- PCM停止処理 
;{
	BCF	PIR1,SSPIF
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	PIE1,SSPIE
	BCF	STATUS,RP0		; ◆ RP=0
	BCF	PCM_FLAG,PLAYING	; ストップビットを送り終わってから再生中フラグを落とす
	BCF	PCM_FLAG,STOPREQ
;}
INT_RETURN 
;{
	MOVF	FSR_,W			; FSRリストア
	MOVWF	FSR
;	MOVF	PCLATH_,W		; PCLATHリストア
;	MOVWF	PCLATH
	SWAPF	STATUS_,W		; STATUSリストア
	MOVWF	STATUS
	SWAPF	W_,F			; Wリストア
	SWAPF	W_,W
	RETFIE
;}
;------------------------------------------------------------------------------
; ハードウエア初期化
;  GIE以外のハードウエアの初期化を記述
;------------------------------------------------------------------------------
hardware_reset 
;{
	MOVLW	8			; DAC中央値
	MOVWF	PORTA
	CLRF	PORTB
	MOVLW	B'00011000'		; I2C定常値
	MOVWF	PORTC
	CLRF	PORTD
	CLRF	PORTE

	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	TRISA			; [54]未使用 [3-0]DAC出力
	MOVLW	B'11110000'		; [7]SW4 [6]SW3 [5]SW2 [4]SW1
	MOVWF	TRISB			; [3]LED4 [2]LED3 [1]LED2 [0]LED1
	MOVLW	B'00011000'		; [76]未使用 [5]200Hz出力 [43]I2C [2-0]未使用
	MOVWF	TRISC
	CLRF	TRISD			; [7-0]LEDセグメント
	CLRF	TRISE			; [2-0]未使用
	
	MOVLW	6			; RAデジタルI/O
	MOVWF	ADCON1
	BCF	OPTION_REG,NOT_RBPU	; RBプルアップオン

	MOVLW	12			; I2C初期化 20,000,000/4/(12+1)=384kHz
	MOVWF	SSPADD

	BSF	PIE1,TMR1IE		; タイムカウント、LED桁間タイマー割り込みON
	BSF	PIE1,TMR2IE		; PCM転送タイマー割り込みオン

	BCF	STATUS,RP0		; ◆ RP=0
	MOVLW	0x28			; I2C初期化
	MOVWF	SSPCON			; SSPEN、マスターモード

	CLRF	TMR1L			; タイムカウント、LED桁間タイマークリア
	CLRF	TMR1H			; 初回は13msで割り込みをかける
	BSF	T1CON,TMR1ON
	
	MOVLW	T2CON_VALUE		; TMR2=PCMビットレート生成タイマー
	MOVWF	T2CON			; プリスケーラのみ設定、転送時にONにすると割り込みで再生後に自動でOFFに戻る

	BSF	INTCON,PEIE
	RETURN
;}
;------------------------------------------------------------------------------
; システムリセット
;  LED表示のセットアップ、EEPROMのクリア、メインループ前の変数の初期化を行う
;------------------------------------------------------------------------------
system_reset
;{
	CLRF	LED_COLUMN_CNT		; LEDセットアップ
	MOVLW	1
	MOVWF	LED_COLUMN_BIT
	CALL	str_HYPHEN		; 起動表示
	BSF	INTCON,GIE		; LED点灯、時間計測開始

	MOVLW	50			; 電源ON直後のスイッチ誤検出ガード
	CALL	wait_w0ms
	
	MOVF	PORTB,W			; ボタンが全押しされていたらオールクリア
	ANDLW	0xF0
	BTFSS	STATUS,Z
	 GOTO	AUTO_CLEAR
	CALL	clear_all
WAIT_ALL_RELEASE			; 全てのボタンが離されるまで待機
	CLRWDT				; ▼
	COMF	PORTB,W
	ANDLW	0xF0
	BTFSS	STATUS,Z
	 GOTO	WAIT_ALL_RELEASE

AUTO_CLEAR
	CLRW				; データEEPROMの0番地に5以上の不正値があれば強制クリアする
	CALL	eeread_w
	SUBLW	4
	BTFSS	STATUS,C
	 CALL	clear_all
	
	CALL	str_CLEAR		; 画面クリア
	MOVLW	10
	CALL	wait_w0ms

	MOVLW	CLKCAL_ADDR
	CALL	eeread_w
	MOVWF	CLKCAL_COARSE
	ADDLW	LOW T1_OFFSET
	MOVWF	CLKCAL
	CALL	eeread
	MOVWF	CLKCAL_FINE
	CLRF	CLKCAL_COUNT

	CALL	clear_second		; 時間計測セットアップ
	CLRF	MINUTE
	CLRF	HOUR

	CLRF	FLAG
	CLRF	PCM_FLAG
	CLRF	ACTION_WAIT

	MOVLW	MODE_NORMAL		; enter_NORMALの冒頭と同じ
	MOVWF	MODE
	CLRF	ALARM_NUMBER
	BSF	FLAG,LEDUPD		; 強制表示更新
	RETURN
;}
;------------------------------------------------------------------------------
; PCM再生
;  チップ内のデータを再生する
;  既に再生中の時は何もせず戻る
;
; 引数		W	メロディー番号0〜3 0,1=long 2,3=short
;------------------------------------------------------------------------------
play_pcm
;{
	BTFSC	PCM_FLAG,PLAYING
	 RETURN
	BSF	PCM_FLAG,PLAYING
	BCF	PCM_FLAG,STOPREQ
	ANDLW	3
	MOVWF	PCM_NUMBER
	CALL	chip_table
	MOVWF	CHIP_ADDR
	CALL	i2c_transmit_with_start
	MOVF	PCM_NUMBER,W
	SUBLW	3
	MOVLW	0x40
	BTFSS	STATUS,Z
	 CLRW
	CALL	i2c_transmit
	CLRW
	CALL	i2c_transmit
	CALL	i2c_restart
	INCF	CHIP_ADDR,W
	CALL	i2c_transmit
	CALL	i2c_receive_async
	CLRF	PCM_COUNTL		; 65,536サンプル再生する
	CLRF	PCM_COUNTH
	BTFSC	PCM_NUMBER,1		; 2,3の時は半分の再生時間
	 BSF	PCM_COUNTH,7
	CLRF	TMR2
	BSF	T2CON,TMR2ON		; タイマー割り込みで自動転送
	RETURN
;}
;------------------------------------------------------------------------------
stop_pcm				; PCM停止リクエストと停止完了待ち 
;{
	BSF	PCM_FLAG,STOPREQ
STOP_WAIT
	BTFSC	PCM_FLAG,PLAYING
	 GOTO	STOP_WAIT
	RETURN
;}
;------------------------------------------------------------------------------
; 内蔵EEPROM関連
;  アドレスは自動でインクリメントされる
;
; 引数		EEADDR, W
; 使用変数	なし
;------------------------------------------------------------------------------
eeread					; EEADDRのアドレスを読んでWで返す 
;{
	MOVF	EEADDR,W
;}
eeread_w				; Wのアドレスを読んでWで返す 
;{
	MOVWF	EEADDR			; ゼロフラグも反映する
	INCF	EEADDR,F		; アドレスオートインクリメント
	BSF	STATUS,RP1		; ◆ RP=2
	MOVWF	EEADR
	BSF	STATUS,RP0		; ◆ RP=3
	BCF	EECON1,EEPGD
	BSF	EECON1,RD
	BCF	STATUS,RP0		; ◆ RP=2
	MOVF	EEDATA,W
	BCF	STATUS,RP1		; ◆ RP=0
	RETURN
;}
;------------------------------------------------------------------------------
eewrite					; WのデータをEEADDRに書き込む 
;{
	CALL	eewait
	BSF	STATUS,RP1		; ◆ RP=2
	MOVWF	EEDATA
	BCF	STATUS,RP1		; ◆ RP=0
	MOVF	EEADDR,W
	INCF	EEADDR,F		; アドレスオートインクリメント
	BSF	STATUS,RP1		; ◆ RP=2
	MOVWF	EEADR
	BSF	STATUS,RP0		; ◆ RP=3
	BCF	EECON1,EEPGD
	BSF	EECON1,WREN
	BCF	INTCON,GIE
	MOVLW	0x55
	MOVWF	EECON2
	MOVLW	0xAA
	MOVWF	EECON2
	BSF	EECON1,WR
	BSF	INTCON,GIE
	BCF	EECON1,WREN
	BCF	STATUS,RP1		; ◆ RP=1
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN
;}
;------------------------------------------------------------------------------
eewait					; 書き込みが完了するまで待つ 
;{
	BSF	STATUS,RP1		; ◆ RP=2
	BSF	STATUS,RP0		; ◆ RP=3
	BTFSC	EECON1,WR
	 GOTO	$-1
	BCF	STATUS,RP1		; ◆ RP=1
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN
;}
;------------------------------------------------------------------------------
clear_all				; EEPROM 256バイトゼロクリア 
;{
	CALL	str_Clr
	CLRF	TMP00
	CLRF	EEADDR
	BSF	LED_BUFFER+3,SEG_DP
CLEAR_LOOP
	MOVLW	64
	SUBWF	TMP00,W
	BTFSC	STATUS,Z
	 BSF	LED_BUFFER,SEG_DP

	MOVLW	128
	SUBWF	TMP00,W
	BTFSC	STATUS,Z
	 BSF	LED_BUFFER+1,SEG_DP

	MOVLW	192
	SUBWF	TMP00,W
	BTFSC	STATUS,Z
	 BSF	LED_BUFFER+2,SEG_DP

	CLRW
	CALL	eewrite
	DECFSZ	TMP00,F
	 GOTO	CLEAR_LOOP

	MOVLW	CLKCAL_ADDR		; キャリブレーションをデフォルト値に書き戻す
	MOVWF	EEADDR
	MOVLW	DEF_CLKCAL_COARSE
	CALL	eewrite
	MOVLW	DEF_CLKCAL_FINE
	CALL	eewrite
	CALL	eewait			; 書き終わるまで待つ
	CALL	str_End
	RETURN
;}
;------------------------------------------------------------------------------
; ウェイトルーチン
;------------------------------------------------------------------------------
wait_10ms				; 10msのウェイト 
;{
	MOVLW	1
;}
wait_w0ms				; W*10msのウェイト 
;{
	BSF	STATUS,RP0		; ◆ RP=3
	BSF	STATUS,RP1
	MOVWF	WC1
WAIT0
	MOVLW	65
	MOVWF	WC2
WAIT1
	CLRF	WC3
WAIT2
	DECFSZ	WC3,F
	 GOTO	WAIT2
	DECFSZ	WC2,F
	 GOTO	WAIT1
    	CLRWDT				; ▼
	DECFSZ	WC1,F
	 GOTO	WAIT0
	BCF	STATUS,RP0		; ◆ RP=0
	BCF	STATUS,RP1
	RETURN
;}
;------------------------------------------------------------------------------
; I2C関連ルーチン
;   動作中の監視はSSPIFで一括して行う
;   AN976参照
;------------------------------------------------------------------------------
i2c_transmit_with_start			; スタートビット送ってからWを送信 
;{
	BCF	PIR1,SSPIF
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	SSPCON2,SEN
	BCF	STATUS,RP0		; ◆ RP=0
;}
i2c_transmit				; Wを送信、ZフラグにACKの値を入れて返す 
;{
	CALL	i2c_wait		;  ACK:Z=0  NAK:Z=1
	BCF	PIR1,SSPIF
	MOVWF	SSPBUF
	BCF	STATUS,Z
	CALL	i2c_wait
	BSF	STATUS,RP0		; ◆ RP=1
	BTFSC	SSPCON2,ACKSTAT
	 BSF	STATUS,Z
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN	
;}
;------------------------------------------------------------------------------
i2c_restart				; リスタート
;{
	CALL	i2c_wait
	BCF	PIR1,SSPIF
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	SSPCON2,RSEN
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN
;}
;------------------------------------------------------------------------------
i2c_receive_async			; 非同期受信動作を開始 
;{
	CALL	i2c_wait
	BCF	PIR1,SSPIF
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	SSPCON2,RCEN		; 受信開始
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN
;}
;------------------------------------------------------------------------------
i2c_wait				; I2C実行完了待ち 
;{
#if SIMDEBUG != 1
	BTFSS	PIR1,SSPIF
	 GOTO	$-1
#endif
#if SIMDEBUG == 1
	BSF	PIR1,SSPIF
#endif
	RETURN	
;}
;------------------------------------------------------------------------------
; 16bit 加算
;
; 引数
;	MATH0L	LSB 足される数
;	MATH0H	MSB
;	MATH1L	LSB 足す数(非破壊)
;	MATH1H	MSB
;
; 戻り値
;	MATH0L	LSB 和
;	MATH0H	MSB
;	C	キャリーフラグ
;	Z	ゼロフラグ
;------------------------------------------------------------------------------
add16 
;{
	MOVF	MATH1L,W		; 下位加算
	ADDWF	MATH0L,F

	MOVF	MATH1H,W		; 下位キャリー処理
	BTFSC	STATUS,C
	 INCF	MATH1H,W
	ADDWF	MATH0H,F		; 上位加算
	
	MOVF	MATH0L,W		; 全ビットゼロ検出
	IORWF	MATH0H,W
	RETURN
;}
;------------------------------------------------------------------------------
; 16bit 減算
;
; 引数
;	MATH0L	LSB 引かれる数
;	MATH0H	MSB
;	MATH1L	LSB 引く数(非破壊)
;	MATH1H	MSB
;
; 戻り値
;	MATH0L	LSB 差
;	MATH0H	MSB
;	C	キャリーフラグ
;	Z	ゼロフラグ
;------------------------------------------------------------------------------
sub16 
;{
	MOVF	MATH1L,W		; 下位減算
	SUBWF	MATH0L,F

	MOVF	MATH1H,W		; 下位ボロー処理
	BTFSS	STATUS,C
	 INCF	MATH1H,W
	SUBWF	MATH0H,F		; 上位減算
	
	MOVF	MATH0L,W		; 全ビットゼロ検出
	IORWF	MATH0H,W
	RETURN
;}
;------------------------------------------------------------------------------
; 16bit 2の補数符号変換
;
; 引数
;	MATH0L	LSB
;	MATH0H	MSB
;
; 戻り値
;	MATH0L	LSB
;	MATH0H	MSB
;------------------------------------------------------------------------------
neg16 
;{
	COMF	MATH0L,F
	COMF	MATH0H,F
	INCF	MATH0L,F
	BTFSC	STATUS,Z
	 INCF	MATH0H,F
	RETURN
;}
;------------------------------------------------------------------------------
; 割り算 8bit / 8bit = 商8bit + 剰余8bit
;	http://elm-chan.org/docs/avrlib/div08.txt
; 引数		
;	DIV1		割られる数(破壊)
; 	DIV2		割る数(非破壊)
;
; 戻り値
;	DIV1		商  -> 10の桁として使う
;	MODULO 		剰余-> 1の桁として使う
;
; 使用変数
;	DIVCNT		ループカウンタ
;------------------------------------------------------------------------------
div8 
;{
	CLRF	MODULO			; clr	mod
	MOVLW	8			; ldi	divcnt,8
	MOVWF	DIVCNT
DIV8LOOP
	BCF	STATUS,C		; lsl	div1
	RLF	DIV1,F
	RLF	MODULO,F		; rol	modulo
	MOVF	DIV2,W			; cp	modulo,div2
	SUBWF	MODULO,W
	BTFSS	STATUS,C		; brcs	DIV8NEXTLOOP
	GOTO	DIV8NEXTLOOP
	INCF	DIV1,F			; inc	div1
	MOVF	DIV2,W			; sub	modulo,div2
	SUBWF	MODULO,F
DIV8NEXTLOOP
	DECFSZ	DIVCNT,F		; dec	divcnt
	GOTO	DIV8LOOP		; brne	PC-7
	RETURN				; ret
;}
;------------------------------------------------------------------------------
; 時計・アラーム時刻レジスタから表示データを作る
;
; 引数		ALARM_HOUR, ALARM_MINUTE
; 引数		HOUR, MINUTE
; 使用変数	TMP00
;------------------------------------------------------------------------------
alarm_to_disp 
;{
	MOVF	ALARM_HOUR,W
	MOVWF	DISP_HOUR
	MOVF	ALARM_MINUTE,W
	GOTO	TO_DISP
;}

clock_to_disp 
;{
	MOVF	HOUR,W
	MOVWF	DISP_HOUR
	MOVF	MINUTE,W
TO_DISP
	MOVWF	DISP_MINUTE

	BCF	FLAG,LEDUPD		; 表示済み
	BTFSC	FLAG,BLINK		; 点滅指示の時は0.5秒ごとにスペース表示に置き換える
	 BTFSS	SECOND,SECOND_05
	 GOTO	DISP_SHOW
	CALL	str_CLEAR
	GOTO	DISP_DP
DISP_SHOW
	CALL	time_to_disp
DISP_DP
	BTFSC	SECOND,SECOND_05	; 秒→DP点滅処理
	 RETURN

	RRF	DP_FLAG,W
	MOVWF	TMP00
	BTFSC	STATUS,C
	 BSF	LED_BUFFER + 3,SEG_DP

	RRF	TMP00,F
	BTFSC	STATUS,C
	 BSF	LED_BUFFER + 2,SEG_DP

	RRF	TMP00,F
	BTFSC	STATUS,C
	 BSF	LED_BUFFER + 1,SEG_DP

	RRF	TMP00,F
	BTFSC	STATUS,C
	 BSF	LED_BUFFER,SEG_DP
	RETURN
;}
;------------------------------------------------------------------------------
; 表示用時分レジスタから4ケタの表示データを作る
;
; 引数		DISP_HOUR, DISP_MINUTE
; 使用変数	div8用変数
;------------------------------------------------------------------------------
time_to_disp 
;{
	MOVF	DISP_HOUR,W
	MOVWF	DIV1
	MOVLW	10
	MOVWF	DIV2
	CALL	div8
	MOVF	DIV1,W
	BTFSC	STATUS,Z		; 時の10位が0の場合は16(スペース)に置き換える
	 MOVLW	16
	CALL	led_table
	MOVWF	LED_BUFFER

	MOVF	MODULO,W
	CALL	led_table
	MOVWF	LED_BUFFER + 1
	
	MOVF	DISP_MINUTE,W
	MOVWF	DIV1
	MOVLW	10
	MOVWF	DIV2
	CALL	div8
	MOVF	DIV1,W
	CALL	led_table
	MOVWF	LED_BUFFER + 2

	MOVF	MODULO,W
	CALL	led_table
	MOVWF	LED_BUFFER + 3
	RETURN
;}
;------------------------------------------------------------------------------
; アラームチェック、鳴動
;  若い番号からアラームを検索し、アラームを鳴らしたらその番号で終了する
;
; 引数		なし
; 使用変数	ALARM_NUMBER, TMP00
;------------------------------------------------------------------------------
check_alarm 
;{
	BCF	FLAG,ALMCHK		; アラームチェック済み
	CLRF	ALARM_NUMBER
CHECKALARM_LOOP
	BCF	STATUS,C		; ループカウンタ×3でアラームフラグアドレスを算出
	RLF	ALARM_NUMBER,W
	ADDWF	ALARM_NUMBER,W
	CALL	eeread_w
	BTFSC	STATUS,Z
	 GOTO	CHECKALARM_NEXT		; アラームoffだった
	ADDLW	-1
	MOVWF	TMP00			; PCM番号として一時保存

	CALL	eeread			; 時刻チェック
	SUBWF	HOUR,W
	BTFSS	STATUS,Z
	 GOTO	CHECKALARM_NEXT
	CALL	eeread
	SUBWF	MINUTE,W
	BTFSS	STATUS,Z
	 GOTO	CHECKALARM_NEXT

	MOVF	TMP00,W			; 時刻一致
	CALL	play_pcm
	RETURN
CHECKALARM_NEXT
	INCF	ALARM_NUMBER,W
	MOVWF	ALARM_NUMBER
	SUBLW	ALARMMAX
	BTFSS	STATUS,Z
	 GOTO	CHECKALARM_LOOP
	RETURN
;}
;------------------------------------------------------------------------------
; その他 集約ルーチン
;------------------------------------------------------------------------------
getNextMode 
;{
	SWAPF	PORTB,W
	XORLW	0xFF
	ANDLW	3
	RETURN
;}
;------------------------------------------------------------------------------
getActionButton				; Zフラグチェック使用不可能
;{
	MOVF	ACTION_WAIT,F
	BTFSS	STATUS,Z
	 RETLW	0
	COMF	PORTB,W			; 押されていない場合は0
	ANDLW	0xC0
	BTFSC	STATUS,Z
	 RETLW	0
	MOVWF	TMP00
	COMF	TMP00,W			; 同時押しも対象外
	ANDLW	0xC0
	BTFSC	STATUS,Z
	 RETLW	0
	
	MOVLW	DEF_ACTION_WAIT		; アクションキー入力後しばらくはキー入力を無効化する
	MOVWF	ACTION_WAIT
	CLRW
	BTFSC	TMP00,SW3
	 ADDLW	1
	BTFSC	TMP00,SW4
	 ADDLW	2
	RETURN
;}
;------------------------------------------------------------------------------
releaseActionButton 
;{
	CLRWDT				; ▼
	COMF	PORTB,W
	ANDLW	0xC0
	BTFSS	STATUS,Z
	 GOTO	releaseActionButton
	RETURN
;}
;------------------------------------------------------------------------------
getAlarmAddress 
;{
	BCF	STATUS,C
	RLF	ALARM_NUMBER,W
	ADDWF	ALARM_NUMBER,W
	RETURN
;}
;------------------------------------------------------------------------------
clear_second 
;{
	MOVLW	-200
	MOVWF	TICK
	MOVLW	-30
	MOVWF	SECOND
	MOVLW	1
	MOVWF	DP_FLAG
	RETURN
;}
;------------------------------------------------------------------------------
; 文字列テーブル
;------------------------------------------------------------------------------
str_CLEAR				;      
;{
	CLRW
	GOTO	SAME_CHAR
;}

str_HYPHEN				; ---- 
;{
	MOVLW	CHR_HYPHEN
SAME_CHAR
	MOVWF	LED_BUFFER
	MOVWF	LED_BUFFER + 1
	MOVWF	LED_BUFFER + 2
	MOVWF	LED_BUFFER + 3
	RETURN
;}

str_CAL					; CAL  
;{
	MOVLW	CHR_C
	MOVWF	LED_BUFFER
	MOVLW	CHR_A
	MOVWF	LED_BUFFER + 1
	MOVLW	CHR_L
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_SPACE
	MOVWF	LED_BUFFER + 3
	RETURN
;}

str_tSEt				; tSEt 
;{
	MOVLW	CHR_t
	MOVWF	LED_BUFFER
	MOVLW	CHR_S
	MOVWF	LED_BUFFER + 1
	MOVLW	CHR_E
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_t
	MOVWF	LED_BUFFER + 3
	RETURN
;}

str_ASEL				; ASEL 
;{
	MOVLW	CHR_A
	MOVWF	LED_BUFFER
	MOVLW	CHR_S
	MOVWF	LED_BUFFER + 1
	MOVLW	CHR_E
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_L
	MOVWF	LED_BUFFER + 3
	RETURN
;}

str_ALtS				; ALtS 
;{
	MOVLW	CHR_A
	MOVWF	LED_BUFFER
	MOVLW	CHR_L
	MOVWF	LED_BUFFER + 1
	MOVLW	CHR_t
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_S
	MOVWF	LED_BUFFER + 3
	RETURN
;}

str_Clr					; Clr  
;{
	MOVLW	CHR_C
	MOVWF	LED_BUFFER
	MOVLW	CHR_l
	MOVWF	LED_BUFFER + 1
	MOVLW	CHR_r
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_SPACE
	MOVWF	LED_BUFFER + 3
	RETURN
;}

str_End					; End  
;{
	MOVLW	CHR_E
	MOVWF	LED_BUFFER
	MOVLW	CHR_n
	MOVWF	LED_BUFFER + 1
	MOVLW	CHR_d
	MOVWF	LED_BUFFER + 2
	MOVLW	CHR_SPACE
	MOVWF	LED_BUFFER + 3
	RETURN
;}

;------------------------------------------------------------------------------
; EEPROM初期値
;------------------------------------------------------------------------------
	ORG	0x2100 
;{
	DE	1,8,30			; 1
	DE	1,9,0			; 2
	DE	1,10,0			; 3
	DE	1,10,30			; 4
	DE	1,10,35			; 5
	DE	1,12,20			; 6
	DE	1,13,20			; 7
	DE	1,15,0			; 8
	DE	1,15,10			; 9
	DE	1,17,0			; 10
	DE	1,17,30			; 11
	DE	1,18,0			; 12
	DE	0,0,0			; 13
	DE	0,0,0			; 14
	DE	0,0,0			; 15
	DE	0,0,0			; 16
	DE	0,0,0			; 17
	DE	0,0,0			; 18
	DE	0,0,0			; 19
	DE	0,0,0			; 20
	DE	0,0,0			; 21
	DE	0,0,0			; 22
	DE	0,0,0			; 23
	DE	0,0,0			; 24
	DE	0,0,0			; 25
	DE	0,0,0			; 26
	DE	0,0,0			; 27
	DE	0,0,0			; 28
	DE	0,0,0			; 29
	DE	0,0,0			; 30
	DE	0,0,0			; 31
	DE	0,0,0			; 32
	DE	0,0,0			; 33
	DE	0,0,0			; 34
	DE	0,0,0			; 35
	DE	0,0,0			; 36
	DE	0,0,0			; 37
	DE	0,0,0			; 38
	DE	0,0,0			; 39
	DE	0,0,0			; 40
	DE	0,0,0			; 41
	DE	0,0,0			; 42
	DE	0,0,0			; 43
	DE	0,0,0			; 44
	DE	0,0,0			; 45
	DE	0,0,0			; 46
	DE	0,0,0			; 47
	DE	0,0,0			; 48
	DE	0,0,0			; 49
	DE	0,0,0			; 50
	DE	0,0,0			; 51
	DE	0,0,0			; 52
	DE	0,0,0			; 53
	DE	0,0,0			; 54
	DE	0,0,0			; 55
	DE	0,0,0			; 56
	DE	0,0,0			; 57
	DE	0,0,0			; 58
	DE	0,0,0			; 59
	DE	0,0,0			; 60
	DE	3,0,0			; 61
	DE	3,1,0			; 62
	DE	3,2,0			; 63
	DE	3,3,0			; 64
	DE	3,4,0			; 65
	DE	3,5,0			; 66
	DE	3,6,0			; 67
	DE	3,7,0			; 68
	DE	3,8,0			; 69
	DE	3,9,0			; 70
	DE	3,10,0			; 71
	DE	3,11,0			; 72
	DE	3,12,0			; 73
	DE	3,13,0			; 74
	DE	3,14,0			; 75
	DE	3,15,0			; 76
	DE	3,16,0			; 77
	DE	3,17,0			; 78
	DE	3,18,0			; 79
	DE	3,19,0			; 80
	DE	3,20,0			; 81
	DE	3,21,0			; 82
	DE	3,22,0			; 83
	DE	3,23,0			; 84
	DE	DEF_CLKCAL_COARSE	; FC
	DE	DEF_CLKCAL_FINE		; FD
	DE	0			; FE PADDING
	DE	0			; FF PADDING
;}
;------------------------------------------------------------------------------
	END