;***** RamenTimerII.asm ***************************************************
;
;	ラーメン・タイマー II
;
;	Copyright (C) 2019   by M.Yamamoto. All rights reserved.
;
;     ( coding:  Ver. 0.00   2019/09/19  by M.Yamamoto )
;	coding:  Ver. 1.00   2019/10/07  初期バージョン
;	update:  Ver. 1.10   2019/10/12  スイッチ操作なし時の機能改善
;	bugfix:  Ver. x.xx
;
;******************************************************* PicAsm 0248 ******

;	Device: PIC12F635	Clock: 8MHz (Internal OSC)

#ifdef comment

 <機能概要>

・まず、本機の電源をＯＮにすると開始音としての "ピポッ" ブザー音が出力され、その後、タイマー時間ＬＥＤ (1) ～ (7) が約３秒間 左右に
　点滅移動を繰り返す。

・その次に本機が最初に行うことは、EEPROM にバックアップが取られているタイムデータ（分）を読み出して、その内容をＬＥＤ (1) ～ (7) の
　どれかに点灯表示をする。（プログラムの作成直後のタイムデータには、３分が設定されている。）

・その後本機は、２つあるスイッチの操作待ち状態となって、ユーザのスイッチ操作を待ち続ける。

・ユーザは、現在点灯されているＬＥＤ (x) のタイマー時間でよければ、次に START スイッチを押すことによって、タイマー機能が開始される。

・タイマー時間の変更をする場合には、SELECTOR スイッチを押すことによって行われ、押すごとにタイマー時間が＋１されてＬＥＤ (1) ～ (7) 
　に点灯表示がされる。 (1) → (2) → (3) → (4) → (5) → (6) → (7) → (1) → ・・・ の順に変更がされて行く。

・このように本機ではラーメン・タイマー用（短時間）を想定していて、タイマー時間には最低１分から最大７分までを変更範囲としている。

・時間設定の次に START スイッチが押されると、"ピピッ" ブザー音と共にタイマー機能が開始され、設定をしたタイマー時間からカウントダウ
　ンが始まる。

・カウントダウンが始まると、それまで点灯されていたタイマー時間ＬＥＤ (x) は一旦消灯するが、次からは刻々と変化をして行く残りのタイマ
　ー時間を表現するために、毎秒ごとに一瞬（約 50m 秒）だけ点灯表示を繰り返すようになる。

・そして、タイマー時間が１分を経過するごとに、それまで瞬間点灯をしていたＬＥＤ (x) が下位のＬＥＤ (x-1) に切り替わって、同様な表示
　を繰り返す。例えば (3) → (2) → (1) のように切り替わって行く。

・この残りのタイマー時間ＬＥＤ (x) が切り替わったときには、それをユーザに知らせるためにその都度 "ピピッ" ブザー音を出力する。

・また、タイマー時間が残り１分を切って最終の 10 秒前になると、それまでのＬＥＤ (1) の瞬間点灯に加えて、毎秒ごとに "ピッ" ブザー音を
　出力するようになり、もうすぐ終了することを知らせる。

・やがて、設定をしたタイマー時間がゼロとなってタイムアウトになると、まず、そのときまで瞬間点灯をしていたＬＥＤ (1) が消灯したままに
　なる。そして、このときに設定されていた タイマー時間と EEPROM にバックアップされているタイマー時間が比較され、異なる場合にだけ、そ
　のときのタイマー時間で EEPROM のバックアップの更新をする。

・続いて、本機では "チャルメラ" ブザー音を２回出力することで、タイマーが正常に終了したことをユーザに知らせる。

・また、タイマー時間のカウントダウン中に、ユーザの何らかの理由によってカウントダウンを中断したい場合には、START（CANCEL）スイッチを
　押すことによってカウントダウンは中断終了をする。このように、カウントダウン中には START スイッチは CANCEL スイッチとして機能をする。

・このときには、中断終了であることを表すために "ブッブー" ブザー音を出力する。もちろん EEPROM のバックアップの更新はない。

・タイムアウトによる正常終了、CANCEL スイッチによる中断終了のどちらの場合にも、そのときに設定されていたタイマー時間のＬＥＤ (x) を
　再び点灯表示をして、ユーザの次のスイッチ操作を待つためにプログラムのメインに戻る。

・なお、"チャルメラ" ブザー音の出力中において、START（CANCEL）スイッチを押すことによって、そのブザー音の出力を中断することもできる。

・電源をＯＮにした直後や、カウントダウンが終了してユーザの次のスイッチ操作を待つためにプログラムのメインに戻った後、１分以上スイッ
　チ操作がなかった場合には タイマー時間のＬＥＤ (x) を消灯して、毎秒ごとに "ピピッ" ブザー音の出力を繰り返してユーザの注意を喚起し、
　電源の切り忘れを知らせる。

・このときの "ピピッ" ブザー音の注意喚起は 10 秒間だけで、それ以降もユーザのスイッチ操作がなかった場合には、"ブッブー" ブザー音を出
　力した後スリープ状態に入る。

・これら "ピピッ" ブザー音の出力中、またはスリープ状態のときに、２つのスイッチの内のどちらかを押すことによって、再びタイマー時間の
　ＬＥＤ (x) を点灯表示をして、ユーザの次のスイッチ操作待ちに戻すことができる。

・なお、本機では PIC がスリープ状態のときにも約 80μA（電源が 4.5 V のとき）程度の電流を消費するため、この機能を電源スイッチ代わり
　には使用しない方が良い。

#endif

;==========================================================================

;	PIC12F635 のピンと用途

;		① Vdd	電源 +5V DC		⑧ Vss	電源 GND
;		② GP5	アラーム音 出力		⑦ GP0	BCD-0 bit 出力
;		③ GP4	START/CANCEL SW 入力　	⑥ GP1	BCD-1 bit 出力
;		④ GP3	SELECTOR SW 入力	⑤ GP2	BCD-2 bit 出力

;	      GP3 は入力専用で、出力ポートには使えない。

;==========================================================================

		list		P=PIC12F635	;デバイス情報
		include        "P12F635.INC"	;インクルードファイル
		errorlevel	-302		;Eliminate bank warning

;		__config	_CPD_OFF & _CP_OFF & _BOD_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT
		__config	_CPD_OFF & _CP_OFF & _BOD_ON & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

;==========================================================================
;		変数、定数の定義とレジスタ割付け
;==========================================================================

;#define 	xx_debug			;TMR0 基準時間の調整中: 補正値(xx)

		radix	dec

tm0_h_val	equ	256 - 200		;ハードタイマー０カウント値 (4 / 8MHz * 200 = 100μS)
xx		equ	-749			;補正値
tm0_s_val	equ	10000 + xx		;ソフトタイマー０のカウント値
selsw		equ	3			;GP3: SELECTOR スイッチ
stsw		equ	4			;GP4: START(CANCEL) スイッチ
buzzer		equ	5			;GP5: アラーム音 出力

Do		equ	28			;ド
Do_		equ	29			;ド＃
Re		equ	30			;レ
Re_		equ	31			;レ＃
Mi		equ	32			;ミ
Fa		equ	33			;ファ
Fa_		equ	34			;ファ＃
So		equ	35			;ソ
So_		equ	36			;ソ＃
Ra		equ	37			;ラ
Ra_		equ	38			;ラ＃
Si		equ	39			;シ

Half		equ	1			;半音
Oct1		equ	12			;1 オクターブ
Nn		equ	h'80'			;休止符
Xx		equ	h'c0'			;テンポ
Rep		equ	h'c1'			;繰り返し

L1		equ	64			;1/1   64 x 1 x tempo = 64 mS x tempo
L2		equ	32			;1/2   32 x 1 x tempo = 32 mS x tempo
L4		equ	16			;1/4   16 x 1 x tempo = 16 mS x tempo
L8		equ	8			;1/8	8 x 1 x tempo =  8 mS x tempo
L16		equ	4			;1/16	4 x 1 x tempo =  4 mS x tempo
L32		equ	2			;1/32	2 x 1 x tempo =  2 mS x tempo
Lx		equ	1			;	1 x 1 x tempo =  1 mS x tempo

f_doh		equ	65536 - 1911		;ド (523.26 Hz)  at 8MHz
f_so		equ	65536 - 2551		;ソ (392.00 Hz)
f_mi		equ	65536 - 3034		;ミ (329.63 Hz)
f_do		equ	65536 - 3822		;ド (261.63 Hz)
po_tim		equ	100			;音長(ms)
pi_tim		equ	100			;音長
bu_tim		equ	100			;音長
buu_tim		equ	250			;音長
dms_tim		equ	200			;音長

		cblock	h'40'
tm0_cnt_h					;ソフトタイマー０の high カウンタ
tm0_cnt_l					;ソフトタイマー０の low  カウンタ
const_high					;音階用分周定数 high の保存
const_low					;音階用分周定数 low の保存
mon_flg						;監視フラグ
sec_flg						;"秒"カウンタ更新フラグ
eep_min						;"分"EEPROMバックアップ値
sel_min						;"分"選択値
min_cnt						;"分"カウンタ
sec_cnt						;"秒"カウンタ
glp_cnt						;汎用繰り返しカウンタ
wt1cnt						;ウェイト用ループカウンタ
wt2cnt						;ウェイト用ループカウンタ

scale						;音階コードの保存
length						;音長の保存
tempo						;テンポ指定
repeat						;繰り返し指定
addr_high					;読み出す曲データの high アドレス
addr_low					;読み出す曲データの low アドレス
rep_addr_high					;繰り返し曲データの high アドレス
rep_addr_low					;繰り返し曲データの low アドレス
lp_cnt						;繰り返しカウンタ
work						;
		endc

		cblock	h'70'
w_save						;W レジスタの退避用
s_save						;STATUS レジスタの退避用
		endc

;==========================================================================
;		マクロ命令定義
;==========================================================================

;		Sound_M マクロ

Sound_M		macro	@scale,@time
		movlw	high @scale	;出力音階
		movwf	const_high
		movlw	low @scale
		movwf	const_low
		movlw	@time		;出力時間(ms)
		call	buzzer_control	;ブザー音出力コントロール
		endm

;==========================================================================
;		プログラム開始
;==========================================================================

		org	h'0000'		;リセットベクタ
		goto	start

		org	h'0004'		;割り込みベクタ
		goto	interrupt

;==========================================================================
;		音階用分周定数を取得(一部だけの省略設定)  at 8MHz
;==========================================================================

get_scale
		movwf	work
		movlw	high $		;;
		movwf	PCLATH		;;

		movlw	Do << 1
		subwf	work,W			;work - (28 * 2)
		addwf	PCL,F			;PC = PC + W

		retlw	high (65536 - 3822)	;28: ド    (261.63Hz)
		retlw	low  (65536 - 3822)
		retlw	high (65536 - 3608)	;29: ド＃  (277.18Hz)
		retlw	low  (65536 - 3608)
		retlw	high (65536 - 3405)	;30: レ    (293.66Hz)
		retlw	low  (65536 - 3405)
		retlw	high (65536 - 3214)	;31: レ＃  (311.13Hz)
		retlw	low  (65536 - 3214)
		retlw	high (65536 - 3034)	;32: ミ    (329.63Hz)
		retlw	low  (65536 - 3034)
		retlw	high (65536 - 2863)	;33: ファ  (349.23Hz)
		retlw	low  (65536 - 2863)
		retlw	high (65536 - 2703)	;34: ファ＃(369.99Hz)
		retlw	low  (65536 - 2703)
		retlw	high (65536 - 2551)	;35: ソ    (392.00Hz)
		retlw	low  (65536 - 2551)
		retlw	high (65536 - 2408)	;36: ソ＃  (415.30Hz)
		retlw	low  (65536 - 2408)
		retlw	high (65536 - 2273)	;37: ラ    (440.00Hz)
		retlw	low  (65536 - 2273)
		retlw	high (65536 - 2145)	;38: ラ＃  (466.16Hz)
		retlw	low  (65536 - 2145)
		retlw	high (65536 - 2025)	;39: シ    (493.88Hz)
		retlw	low  (65536 - 2025)
		retlw	high (65536 - 1911)	;40: ド    (523.26Hz)
		retlw	low  (65536 - 1911)

;==========================================================================
;		初期化処理
;==========================================================================

start
		clrf	STATUS		;STATUS クリア
	 	clrf	INTCON		;INTCON クリア

		; GPIO をデジタルI/O に設定

		clrf	GPIO
		movlw	h'07'		;Comparator mode Off
		movwf	CMCON0		;GP2-0 はデジタルI/O

		bsf	STATUS,RP0	;バンク 1
		movlw	h'18'		;GP0-GP2,GP5: 出力, GP3,GP4: 入力
		movwf	TRISIO

		; 内部クロックを 8 MHz に設定

		movlw	h'70'		;CLOCK=8MHz
		movwf	OSCCON

		; タイマー０,１割り込みの設定

		movlw	h'88'		;プリスケーラ: 使用しない
		movwf	OPTION_REG

		bcf	PIE1,TMR1IE	;TMR1IE: タイマー１割り込みを禁止
		bcf	STATUS,RP0	;バンク 0
		bcf	INTCON,T0IE	;T0IE: タイマー０割り込みを禁止

		movlw	b'00000001'	;bit7-6: 未使用
					;bit5-4=00: プリスケーラのスケール値 1:1
					;bit3=0: 専用の発振器を使う場合に指定
					;bit2=0: 外部入力クロック指定時に有効
					;bit1=0: 内部クロックを使う
					;bit0=1: タイマー１を動作
		movwf	T1CON		;T1CON の設定

		bsf	INTCON,PEIE	;PEIE 周辺機器割り込みを許可
		bsf	INTCON,GIE	;GIE: グローバル割り込み許可
;
#ifndef xx_debug
		call	buzzer_pipo	;"ピポッ" ブザー音出力
		call	led_demo	;LED 点滅デモ
		call	wait_250ms	;250m 秒 ウェイト

		; EEPROM データの読み出し

		bsf	STATUS,RP0	;バンク 1
		movlw	eep_size
		movwf	EEADR		;EEPROM の記録アドレスを設定
		bsf	EECON1,RD	;読み出し制御 = ON
		movf	EEDATA,W	;データの読み出し

		bcf	STATUS,RP0	;バンク 0
		movwf	eep_min		;"分"EEPROMバックアップ値
		movwf	sel_min		;"分"選択値にも読み出しデータを設定

		; 各変数の初期設定

		clrf	mon_flg		;監視フラグの初期設定
		clrf	sec_flg		;"秒"カウンタ更新フラグの初期設定

		movf	sel_min,W
		movwf	GPIO		;"分"LED 点灯
		goto	main
#else
;==========================================================================
;		TMR0 基準時間の調整支援
;==========================================================================

;		待機中			buzzer = "H", LED(1) 点灯
;		START スイッチでカウント開始
;		カウント中		buzzer = "L", LED(7) 点灯
;		タイムアウトで再び待機中に戻る

tmr0_adj
		movlw	(1 << buzzer) + 1
		movwf	GPIO		;buzzer = "H", LED(1) 点灯

		btfsc	GPIO,stsw	;START スイッチ = ON か？
		goto	$ - 1		;No

		movlw	7
		movwf	min_cnt		;"分"カウンタの設定
		movlw	60
		movwf	sec_cnt		;"秒"カウンタの設定
		call	tm0_proc	;タイマー０割り込みの手続き		;(Ver.1.10 にて変更)

		clrf	GPIO		;buzzer = "L", LED(1) 消灯
		bsf	sec_flg,0	;"秒"カウンタ更新フラグ = ON

tm0a1		btfss	sec_flg,0	;"秒"カウンタ更新フラグ = ON か？
		goto	tm0a2		;No

		movlw	7
		movwf	GPIO		;buzzer = "L", LED(7) 点灯
		call	wait_50ms	;50m 秒 ウェイトルーチン
		clrf	GPIO		;buzzer = "L", LED(7) 消灯
		clrf	sec_flg		;"秒"カウンタ更新フラグ = OFF

tm0a2		movf	min_cnt,W
		btfss	STATUS,Z	;"分"カウンタ = 0 か？
		goto	tm0a1		;No

		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		goto	tmr0_adj
#endif

;==========================================================================
;		LED 点滅デモ・ルーチン
;==========================================================================

		; LED 点滅デモ
led_demo
		call	led_demo1
		call	led_demo2
		call	led_demo1
		call	led_demo2
		call	led_demo1
		return

		; (1) → (2) → (3) → (4) → (5) → (6) → (7)
led_demo1
		movlw	7
		movwf	glp_cnt		;LED デモ表示用カウンタ

ldm1_01		movf	glp_cnt,W
		sublw	8
		movwf	GPIO		;LED (1) ～ (7) を順に点灯
		call	wait_50ms
		movlw	0
		movwf	GPIO		;LED すべてを消灯
		call	wait_50ms
		decfsz	glp_cnt,F	;glp_cnt - 1 = 0 か？
		goto	ldm1_01		;No

		return

#ifdef comment
		; (7) → (6) → (5) → (4) → (3) → (2) → (1)
led_demo2
		movlw	7
		movwf	glp_cnt		;LED デモ表示用カウンタ

ldm2_01		movf	glp_cnt,W
		movwf	GPIO		;LED (7) ～ (1) を順に点灯
		call	wait_50ms
		movlw	0
		movwf	GPIO		;LED すべてを消灯
		call	wait_50ms
		decfsz	glp_cnt,F	;glp_cnt - 1 = 0 か？
		goto	ldm2_01		;No

		return
#endif

		; (6) → (5) → (4) → (3) → (2)
led_demo2
		movlw	6
		movwf	glp_cnt		;LED デモ表示用カウンタ

ldm2_01		decf	glp_cnt,W
		btfsc	STATUS,Z	;glp_cnt - 1 = 0 か？
		goto	ldm2_02		;Yes

		movf	glp_cnt,W
		movwf	GPIO		;LED (6) ～ (2) を順に点灯
		call	wait_50ms
		movlw	0
		movwf	GPIO		;LED すべてを消灯
		call	wait_50ms
		decfsz	glp_cnt,F	;glp_cnt - 1 = 0 か？
		goto	ldm2_01		;No

ldm2_02		return

;==========================================================================
;		メイン・ルーチン
;==========================================================================

;		START スイッチは CANCEL スイッチも兼ねている
;		カウント動作前に ON すると START スイッチとして機能
;		カウント動作中に ON すると CANCEL スイッチとして機能

;		SELECTOR スイッチは タイマー時間（分）を選択するもの

main
;-------------- ; カウントダウン開始前の処理

		; SELECTOR スイッチのチェック
		btfsc	GPIO,selsw	;SELECTOR スイッチ = ON か？
		goto	m01		;No

		; SELECTOR スイッチの処理
		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		bcf	mon_flg,0	;監視フラグ = 0
		incf	sel_min,F	;"分"選択値 +1 更新
		movlw	1
		btfsc	sel_min,3	;"分"選択値 = 8 か？
		movwf	sel_min		;Yes. ""分"選択値 = 1
		movf	sel_min,W
		movwf	GPIO		;"分"LED 点灯更新
		call	wait_250ms	;250m 秒 ウェイト（チャタリング吸収）
		goto	main

		; START スイッチのチェック
m01		btfss	GPIO,stsw	;START スイッチ = ON か？
		goto	m03		;Yes

		btfsc	mon_flg,0	;監視フラグ = 1 か？
		goto	m02		;Yes

		; スイッチ操作なしの監視（１分間）を開始
		bsf	mon_flg,0	;監視フラグ = 1
		movlw	1
		movwf	min_cnt		;"分"カウンタの設定
		movlw	60
		movwf	sec_cnt		;"秒"カウンタの設定
		call	tm0_proc	;タイマー０割り込みの手続き		;(Ver.1.10 にて変更)
		goto	main

		; スイッチ操作なしの監視中
m02		movf	min_cnt,W
		btfss	STATUS,Z	;"分"カウンタ = 0 か？
		goto	main		;No

		; スイッチ操作なしの監視終了
		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		bcf	mon_flg,0	;監視フラグ = 0
		clrf	GPIO		;"分"LED 消灯

		movlw	1							;
		movwf	min_cnt		;"分"カウンタの設定			;
		movlw	10							;
		movwf	sec_cnt		;"秒"カウンタの設定			;
		call	tm0_proc	;タイマー０割り込みの手続き		;
		clrf	sec_flg		;"秒"カウンタ更新フラグ = OFF		;
										;
		; 警告音の出力（10秒間）と解除スイッチ操作			;
m0x		movf	min_cnt,W						;
		btfsc	STATUS,Z	;"分"カウンタ = 0 か？			;
		goto	m0z		;Yes					;
										;
		btfsc	sec_flg,0	;"秒"カウンタ更新フラグ = ON か？	;
		call	buzzer_pipi	;Yes. "ピピッ" ブザー音出力		;
		clrf	sec_flg		;"秒"カウンタ更新フラグ = OFF		;
										;
		movf	GPIO,W							;
		andlw	h'18'		;スイッチ入力を				;
		xorlw	h'18'		;反転					;
		btfsc	STATUS,Z	;スイッチ操作 = あり か？		;
		goto	m0x		;No					;
										;
		bcf	INTCON,T0IE	;タイマー０割り込みを禁止		;
		call	wait_50ms						;
										;
m0y		movf	GPIO,W							;
		andlw	h'18'		;スイッチ入力を				;
		xorlw	h'18'		;反転					;
		btfss	STATUS,Z	;スイッチ操作 = なし か？		;
		goto	m0y		;No					;(Ver.1.10 にて変更)
										;
		call	wait_50ms						;
		movf	sel_min,W						;
		movwf	GPIO		;"分"LED 点灯				;
		goto	main							;
										;
		; スリープ状態に突入						;
m0z		bcf	INTCON,T0IE	;タイマー０割り込みを禁止		;
		clrf	sec_flg		;"秒"カウンタ更新フラグ = OFF		;
		call	wait_1s							;
		call	buzzer_bubuu	;"ブッブー" ブザー音出力		;
		clrf	GPIO							;
										;
		bsf	STATUS,RP0	;バンク 1				;
		movlw	b'00011000'	;ポート変化割り込みbit			;
		movwf	IOCA		;					;
		bcf	STATUS,RP0	;バンク 0				;
										;
m0w		bsf	INTCON,RAIE	;RAポート変化割り込みを許可		;
		sleep			;おやすみなさい				;
		;								;
		movf	GPIO,W							;
		andlw	h'18'		;スイッチ入力を				;
		xorlw	h'18'		;反転					;
		btfsc	STATUS,Z	;スイッチ操作 = なし か？		;
		goto	m0w		;Yes. 再度 sleep			;
		;								;
		call	wait_50ms						;
		goto	m0y		;wake up				;

;-------------- ; カウントダウンの開始

m03		movf	sel_min,W
		movwf	min_cnt		;"分"カウンタの設定
		movlw	60
		movwf	sec_cnt		;"秒"カウンタの設定
		call	tm0_proc	;タイマー０割り込みの手続き		;(Ver.1.10 にて変更)

		clrf	GPIO		;"分"LED 消灯
		bsf	sec_flg,0	;"秒"カウンタ更新フラグ = ON

;-------------- ; カウントダウン中の処理

m04	;	movf	min_cnt,W						;
	;	btfsc	STATUS,Z	;"分"カウンタ = 0 か？			;(Ver.1.10 にて削除)
	;	goto	m06		;Yes					;

		btfss	sec_flg,0	;"秒"カウンタ更新フラグ = ON か？
		goto	m05		;No

		movf	min_cnt,W
		movwf	GPIO		;"分"LED 点灯
		call	wait_50ms	;50m 秒 ウェイトルーチン
		clrf	GPIO		;"分"LED 消灯
		clrf	sec_flg		;"秒"カウンタ更新フラグ = OFF

		movf	sec_cnt,W
		sublw	60
		btfsc	STATUS,Z	;"秒"カウンタ = 60 か？
		call	buzzer_pipi	;Yes. "ピピッ" ブザー音出力

		decfsz	min_cnt,W	;"分"カウンタ = 1 か？
		goto	m05		;No

		movf	sec_cnt,W
		sublw	10
		btfsc	STATUS,C	;"秒"カウンタ =< 10 か？
		call	buzzer_pi	;Yes. "ピッ" ブザー音出力

		decfsz	sec_cnt,W	;"秒"カウンタ = 1 か？			;
		goto	m05		;No					;
										;(Ver.1.10 にて追加)
		call	wait_500ms						;
		goto	m06		;Yes					;

m05		btfsc	GPIO,stsw	;START スイッチ = ON （キャンセル操作）か？
		goto	m04		;No

		clrf	mon_flg		;監視フラグをクリア

;-------------- ; 終了処理

m06		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		clrf	GPIO		;"分"LED 消灯

		btfsc	mon_flg,0	;キャンセル操作 か？
		goto	m07		;No

		; 中断終了
		call	buzzer_bubuu	;"ブッブー" ブザー音出力
		call	wait_1s		;1 秒 ウェイト
		goto	m10

		; 正常タイムアウト
m07		clrf	mon_flg		;監視フラグをクリア

		movf	sel_min,W	;"分"選択値
		subwf	eep_min,W	;"分"EEPROMバックアップ値
		btfsc	STATUS,Z	;両者は等しい か？
		goto	m08		;Yes

		; EEPROM への書き込み（バックアップ）
		bsf	STATUS,RP0	;バンク1
		movlw	eep_size
		movwf	EEADR		;EEPROM の記録アドレスを設定

		bcf	STATUS,RP0	;バンク0
		movf	sel_min,W	;"分"選択値
		movwf	eep_min		;"分"EEPROMバックアップ値

		bsf	STATUS,RP0	;バンク1
		movwf	EEDATA		;書き込みデータを設定
		bsf	EECON1,WREN	;書き込みイネーブル = ON
		movlw	h'55'
		movwf	EECON2		;h'55' を EECON2 に書き込む
		movlw	h'aa'
		movwf	EECON2		;h'aa' を EECON2 に書き込む
		bsf	EECON1,WR	;書き込み開始
		btfsc	EECON1,WR	;書き込み 終了か?
		goto	$-1		;No

		bcf	STATUS,RP0	;バンク0

		; チャルメラ x 2 ブザー音出力
m08		movlw	2
		movwf	glp_cnt		;繰り返しカウンタ

m09		movlw	high charamela
		movwf	addr_high	;読み出す曲データの high アドレスを設定
		movlw	low  charamela
		movwf	addr_low	;読み出す曲データの low  アドレスを設定

		call	play_back	;曲データの再生
		btfss	GPIO,stsw	;START スイッチ = ON （キャンセル操作）か？
		goto	m10		;Yes

		call	wait_1s		;1 秒 ウェイト
		decfsz	glp_cnt,F	;glp_cnt - 1 = 0 か？
		goto	m09		;No

m10		movf	sel_min,W
		movwf	GPIO		;"分"LED 点灯
		call	wait_500ms
		goto	main

;==========================================================================

;		タイマー０割り込みの手続き					;(Ver.1.10 にて追加)

tm0_proc
		movlw	tm0_h_val	;ハードタイマー０カウント値
		movwf	TMR0		;タイマー０ TMR0 の設定
		movlw	high tm0_s_val	;ソフトタイマー０の high カウント値
		movwf	tm0_cnt_h	;ソフトタイマー０の high カウンタの設定
		movlw	low tm0_s_val	;ソフトタイマー０の low  カウント値
		movwf	tm0_cnt_l	;ソフトタイマー０の low  カウンタの設定
		bsf	INTCON,T0IE	;タイマー０割り込みを許可
		return

;==========================================================================
;		曲データの再生
;		入力: addr_high: 読み出す曲データの high アドレス
;		      addr_low:  読み出す曲データの low  アドレス
;==========================================================================

play_back
		movlw	1
		movwf	repeat		;繰り返し指定の初期設定

		; 曲データの読み出し

ply01		call	kdt_read	;曲データ1 を読み出す
		movwf	scale	       ;;音階コードに保存

		movf	scale,W
		btfsc	STATUS,Z	;読み出しデータ 終了か?
		goto	ply07		;Yes

		call	kdt_read	;曲データ2 を読み出す
		movwf	length	       ;;音長に保存

		; 音階コードの分析

		movlw	Nn		;音階コードが (Nn) の時は休止符
		subwf	scale,W
		btfsc	STATUS,Z	;音階コード = (Nn) か？
		goto	ply04		;Yes

		movlw	Xx		;音階コードが (Xx) の時は テンポ指定
		subwf	scale,W
		btfss	STATUS,Z	;音階コード = (Xx) か？
		goto	ply02		;No

		movf	length,W	;テンポを取り出す
		movwf	tempo		;テンポ指定を設定
		goto	ply06

ply02		movlw	Rep		;音階コードが (Rep) の時は 繰り返し指定
		subwf	scale,W
		btfss	STATUS,Z	;音階コード = (Rep) か？
		goto	ply03		;No

		movf	addr_high,W
		movwf	rep_addr_high	;繰り返し曲データの high アドレスを保存
		movf	addr_low,W
		movwf	rep_addr_low	;繰り返し曲データの low アドレスを保存

		movf	length,W	;繰り返しを取り出す
		movwf	repeat		;繰り返し指定を設定
		goto	ply06

		; 音符, 休符の再生

ply03	;;	decf	scale,F		;音階コード -1
		bcf	STATUS,C
		rlf	scale,W		;音階コードを 2 倍する
		call	get_scale	;音階用分周定数を取得
		movwf	const_high     ;;high に保存する
		bsf	STATUS,C
		rlf	scale,W		;音階コードを 2 倍して +1 する
		call	get_scale	;音階用分周定数を取得
		movwf	const_low      ;;low に保存する

		bsf	STATUS,RP0	;バンク1 を選択
		bsf	PIE1,TMR1IE	;タイマー１割り込みを許可
		bcf	STATUS,RP0	;バンク0 を選択

ply04		movf	tempo,W
		movwf	lp_cnt		;ループカウンタを設定

ply05		movlw	1
		call	wait_w1ms	;音長を (tempo * length) 倍で再生
		decfsz	lp_cnt,F	;ループ終了 か?
		goto	ply05		;No

		decfsz	length,F	;音長 = 0 か？
		goto	ply04		;No

		bsf	STATUS,RP0	;バンク1 を選択
		bcf	PIE1,TMR1IE	;タイマー１割り込みを禁止
		bcf	STATUS,RP0	;バンク0 を選択

		; 再生中断の監視と繰り返し処理

ply06		btfsc	GPIO,stsw	;START（CANCEL）スイッチ = ON か？
		goto	ply01		;No
		goto	ply09		;Yes

ply07		decfsz	repeat,F	;繰り返し終了 か?
		goto	ply08		;No
		goto	ply09		;Yes

ply08		movf	rep_addr_high,W
		movwf	addr_high	;繰り返し曲データの high アドレスを復帰
		movf	rep_addr_low,W
		movwf	addr_low	;繰り返し曲データの low アドレスを復帰
		goto	ply01

		; 再生の終了

ply09		return

		; 曲データの読み出し
kdt_read
		call	kdt_read_0	;テーブルデータの読み出し
		incf	addr_low,F	;曲データの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;曲データの high アドレスの更新
		return

kdt_read_0	movf	addr_high,W	;読み出す曲データの high アドレス
		movwf	PCLATH
		movf	addr_low,W	;読み出す曲データの low アドレス
		movwf	PCL

;==========================================================================

		; チャルメラ音出力データ
charamela
		retlw	Xx		;テンポ
		retlw	35

;		retlw	Nn
;		retlw	L2
;		retlw	Nn
;		retlw	L4
		retlw	Nn
		retlw	L8
		retlw	So
		retlw	L16
		retlw	Ra
		retlw	L16

		retlw	Si
		retlw	L4 + L8
		retlw	Ra
		retlw	L16
		retlw	So
		retlw	L16
		retlw	Nn
		retlw	L8 + L16
		retlw	So
		retlw	L16
		retlw	Ra
		retlw	L16
		retlw	Si
		retlw	L16
		retlw	Ra
		retlw	L16
		retlw	So
		retlw	L16

		retlw	Ra
		retlw	L2
;		retlw	Nn
;		retlw	L2

		retlw	0

;==========================================================================
;		アラーム（ブザー）音出力ルーチン
;==========================================================================

		; "ピッ" ブザー音出力
buzzer_pi
		Sound_M		f_so, pi_tim
		return

		; "ポッ" ブザー音出力
buzzer_po
		Sound_M		f_doh, po_tim
		return

		; "ピピッ" ブザー音出力 (230 mS)
buzzer_pipi
		call	buzzer_pi	;"ピッ" ブザー音出力
	;	call	wait_30ms
		movlw	15		;
		call	wait_w1ms	;
		call	buzzer_pi	;"ピッ" ブザー音出力
		return

		; "ピポッ" ブザー音出力 (230 mS)
buzzer_pipo
		call	buzzer_pi	;"ピッ" ブザー音出力
		call	wait_30ms
		call	buzzer_po	;"ポッ" ブザー音出力
		return

		; "ブッ" ブザー音出力
buzzer_bu
		Sound_M		f_do, bu_tim
		return

		; "ブー" ブザー音出力
buzzer_buu
		Sound_M		f_do, buu_tim
		return

		; "ブッブー" ブザー音出力 (450 mS)
buzzer_bubuu
		call	buzzer_bu	;"ブッ" ブザー音出力
		movlw	100
		call	wait_w1ms
		call	buzzer_buu	;"ブー" ブザー音出力
		return

		; ブザー音出力コントロール
buzzer_control
		bsf	STATUS,RP0	;バンク 1
		bsf	PIE1,TMR1IE	;タイマー１割り込みを許可
		bcf	STATUS,RP0	;バンク 0

		call	wait_w1ms	;入力 W: 音長（ループカウンタ）

		bsf	STATUS,RP0	;バンク 1
		bcf	PIE1,TMR1IE	;タイマー１割り込みを禁止
		bcf	STATUS,RP0	;バンク 0
		return

;==========================================================================
;		ウェイト・ルーチン (at 8 MHz)
;==========================================================================

		; 1m 秒 x W ウェイトルーチン
wait_w1ms
		movwf	wt1cnt		;1

w1m01		movlw	221		;1
		movwf	wt2cnt		;1

w1m02		goto	$ + 1		;2
		goto	$ + 1		;2
		goto	$ + 1		;2
		decfsz	wt2cnt,F	;1,2
		goto	w1m02		;2,0	(2+2+2+1+2)*220+(2+2+2+2+0)=1988

		goto	$ + 1		;2
		goto	$ + 1		;2
		decfsz	wt1cnt,F	;1,2
		goto	w1m01		;2,0	(1+1+1988+2+2+1+2)*(W-1)+(1+1+1988+2+2+2+0)=1997*W-1

		return			;2	1+(1997*W-1)+2=1997*W+2
					;Total	(1997*W+2)/8MHz*4 = (998.5*W + 1) μS

		; 30m 秒 ウェイトルーチン
wait_30ms
		movlw	30		;1
		goto	wait_w1ms	;2	1+2+1+(1997*W-1)+2=1997*W+5
					;Total	(1997*30+5)/8MHz*4 = 29.9575 mS

		; 50m 秒 ウェイトルーチン
wait_50ms
		movlw	50		;1
		goto	wait_w1ms	;2	1+2+1+(1997*W-1)+2=1997*W+5
					;Total	(1997*50+5)/8MHz*4 = 49.9275 mS

		; 1 秒 ウェイトルーチン
wait_1s
		call	wait_500ms

		; 500m 秒 ウェイトルーチン
wait_500ms
		call	wait_250ms

		; 250m 秒 ウェイトルーチン
wait_250ms
		movlw	250		;1
		goto	wait_w1ms	;2	1+2+1+(1997*W-1)+2=1997*W+5
					;Total	(1997*250+5)/8MHz*4 = 249.6275 mS

;==========================================================================
;		割り込み処理
;==========================================================================

interrupt
		; レジスタの保存

		movwf	w_save		;W レジスタの保存
		movf	STATUS,W
		movwf	s_save		;STATUS レジスタの保存

		bcf	STATUS,RP0	;バンク 0
		btfsc	INTCON,T0IF	;タイマー０割り込み か？
		goto	int_t0		;Yes

		btfsc	PIR1,TMR1IF	;タイマー１割り込み か？
		goto	int_t1		;Yes
										;
		btfsc	INTCON,RAIF	;RAポート変化割り込み か？		;(Ver.1.10 にて追加)
		goto	int_ra		;Yes					;
		goto	int_end		;(Illegal interrupt)

		; タイマー０割り込み処理

int_t0		bcf	INTCON,T0IF	;タイマー０割込みフラグをクリア

		movlw	tm0_h_val	;ハードタイマー０カウント値
		movwf	TMR0		;タイマー０ TMR0 の設定

		movlw	1
		subwf	tm0_cnt_l,F	;ソフトタイマー０の low  カウンタ - 1
		btfsc	STATUS,Z	;ソフトタイマー０の low  カウンタ = 0 か？
		goto	int01		;Yes

		btfsc	STATUS,C	;ソフトタイマー０の low  カウンタ > 0 か？
		goto	int_end		;Yes

		subwf	tm0_cnt_h,F	;ソフトタイマー０の high カウンタ - 1
		goto	int_end	

int01		movf	tm0_cnt_h,F
		btfss	STATUS,Z	;ソフトタイマー０の high カウンタ = 0 か？
		goto	int_end		;No

		movlw	high tm0_s_val	;ソフトタイマー０のカウント値
		movwf	tm0_cnt_h	;ソフトタイマー０の high カウンタの設定
		movlw	low tm0_s_val	;ソフトタイマー０のカウント値
		movwf	tm0_cnt_l	;ソフトタイマー０の low  カウンタの設定

		bsf	sec_flg,0	;"秒"カウンタ更新フラグ = ON
		decfsz	sec_cnt,F	;"秒"カウンタ - 1 = 0 か？
		goto	int_end		;No

		movlw	60
		movwf	sec_cnt		;"秒"カウンタの設定
		decf	min_cnt,F	;"分"カウンタ - 1
		goto	int_end

		; タイマー１割り込み処理

int_t1		bcf	PIR1,TMR1IF	;タイマー１割込みフラグをクリア

		movf	const_high,W	;音階用分周定数 high を
		movwf	TMR1H		;TMR1H に設定
		movf	const_low,W	;音階用分周定数 low を
		movwf	TMR1L		;TMR1L に設定

		movlw	1 << buzzer	;対象ビットをセット
		xorwf	GPIO,F		;出力をビット反転
		goto	int_end

		; RA ポート変化割り込み処理					;
										;
int_ra		bcf	INTCON,RAIF	;RAポート変化割込みフラグをクリア	;
		bcf	INTCON,RAIE	;RAポート変化割り込みを禁止		;(Ver.1.10 にて追加)
		bsf	STATUS,RP0	;バンク 1				;
		clrf	IOCA		;ポート変化割り込みbit をクリア		;
		bcf	STATUS,RP0	;バンク 0				;
;		goto	int_end

		; レジスタの復帰

int_end		movf	s_save,W
		movwf	STATUS		;STATUS レジスタの復帰
		swapf	w_save,F
		swapf	w_save,W	;W レジスタの復帰

		retfie

;==========================================================================
;		EEPROM エリア
;==========================================================================

		org	h'2100'

eep_st		de	"Copyright (C) 2019 by M.Yamamoto"

		de	h'ff'

eep_size	equ	$ - eep_st	;EEPROM データサイズ

;		de	h'ff'		;タイマー設定値の記録位置

		de	h'03'		;"分" 既定値

;==========================================================================

		end			;プログラムの終了
