;***** FM_StereoRadioII.asm ***********************************************
;
;	ＦＭステレオラジオII
;
;	Copyright (C) 2020-2021   by M.Yamamoto. All rights reserved.
;
;     ( coding:  Ver. 0.00   2020/10/11  by M.Yamamoto )
;	coding:  Ver. 1.00   2020/10/22  初期バージョン、PIC16F684 に変更
;	update:  Ver. 1.01   2020/10/25  スリープ機能の追加
;	update:  Ver. 1.02   2020/10/28  ST_LEDの表示、受信強度のLCD表示
;	update:  Ver. 1.03   2020/10/28  各フラグの ONタイミングを見直し
;	update:  Ver. 1.04   2020/11/04  SEEK機能での選局を追加
;	update:  Ver. 1.05   2020/11/05  計画変更のためのソース変更
;	update:  Ver. 1.06   2020/11/09  FMスペクトラムのためのRSSIデータ収集機能
;	bugfix:  Ver. 1.07   2021/01/06  テーブルデータアドレスの計算を修正
;	bugfix:  Ver. 1.07b  2021/01/12  テーブルデータアドレスの計算を修正(コメント)
;
;******************************************************* PicAsm 0254 ******

;	Device: PIC16F684	Clock: 8MHz (Internal OSC)

#ifdef comment

 <機能概要と使用法>

・本機 FMステレオラジオでは、FMラジオモジュール（ RDA5807M ）と、LCDモジュール（ AQM0802A-RN-GBW ）を
　使用し、共に  PIC から I2C 接続で制御をしている。 なお、本機ではパワーアンプを省いて小型化を図ったた
　め、ヘッドフォン（イヤホン）で聞くことを想定している。

・さて、本機の電源スイッチをオンにすると、LCD に次の開始メッセージが交互に２秒間ずつ連続２回表示される。

			[ FmStereo ]			[ Ver x.xx ]
			[ Radio II ]			[  by M.Y  ]

・その後、２秒間無表示の後、PIC の EEPROM にバックアップが取られている、前回のセーブ情報（ラジオ局番号、
　音量設定値）が読み出され、そのラジオ局番号を基にして、予め番号毎にプリセットがされている情報に変換を
　する。

・そして、LCD の１行目にはラジオ局名、２行目には音量設定値（範囲: 0～15）が表示されるとともに、必要情報
　を FMラジオモジュールに設定をして、選局、および音量を調節して音声を出力する。

・この２行目に表示されている音量設定値は初めの約２秒間で、続いて FMラジオモジュールから得た情報の内の、
　受信強度が約２秒間表示される。 また、ステレオ放送がされているときには、LED （赤）を点灯させる。 
　その後は、受信しているラジオ局の周波数が表示されて、ユーザがスイッチ操作をするか、PIC がスリープ状態
　に移行するまで表示し続ける。 

			[ NHK FM   ]		[ NHK FM   ]		[ NHK FM   ]
			[  Vol:xx  ]		[  Lev:xx  ]		[  82.5MHz ]

・なお、プログラム作成時のデフォルトの EEPROM セーブ情報には、ラジオ局番号 = 名古屋周辺地区では 3、東京
　周辺地区では 4、大阪周辺地区は 3 の、共に NHK FM で、音量 = 2（範囲: 0～15）に設定がされている。

・周波数の表示の後は、ユーザのスイッチ操作待ちとなって、各スイッチの ON／OFF 状態を監視するが、１分が経
　過してもスイッチ操作がないときには、乾電池電源のための省電力化として、タイマーによるカウントを停止さ
　せ、 PIC をスリープ状態にするとともに、LCD の電源も OFF にし、LED も消灯させる。 これらの操作は既に選
　局が済んでいる FMラジオモジュールの動作には影響がない（音声は出力され続ける）。

・本機では、FMラジオモジュールを操作するためのタクトスイッチが４個あり、まず、ラジオ局番号（各ラジオ局
　の局名、周波数、FMラジオモジュールへの設定情報を、番号毎にプリセットしてある）を選択するために、TUNING 
　DOWN スイッチと TUNING UP スイッチがある。なお、ラジオ局番号はプログラム内部で使用をしている番号であ
　って、LCD に表示されることはない。

・この番号毎にプリセットしてある情報には、名古屋周辺地区用、東京周辺地区用、大阪周辺地区用の３種類が用
　意されていて、それらをタクトスイッチとは別に、SELECT 1,0 の２個の DIP スイッチの操作によって、簡単に
　変更をすることが可能である。 ただし、この操作は電源スイッチをオンにする前に行っておく必要がある。

		SELECT 1	SELECT 0	（ 選局方法 ）
		　　0   	    0   	名古屋周辺地区用　┐
		　　0   	    1   	東京周辺地区用　　│プリセット情報で選局
		　　1   	    0   	大阪周辺地区用　　┘
		　　1   	    1   	100kHzステップ毎の SEEK機能で自動選局

・上記のプリセット情報で選局する場合には、TUNING DOWN スイッチは、押す毎にラジオ局番号が－１され、0 の
　次は、プログラム設定されている内の、最大値となる。また、TUNING UP スイッチは、押す毎にラジオ局番号が
　＋１され、最大値の次は、0 となる。

・SEEK機能で選局する場合には、TUNING DOWN スイッチを押すと、現在選択されている放送局の周波数から、100kHz
　ステップ毎に減る方向にシークダウンしながら選局を行う。 

・また、TUNING UP スイッチを押すと、逆に、周波数が 100kHzステップ毎に増す方向にシークアップしながら選
　局を行う。 そして、帯域の上限または下限では停止しないでラップをし、シークを続行する。 なお、本機では、
　帯域を 76 ～ 108 MHz に予め設定がしてある。

・次に、音量（範囲: 0～15）を調節するために、VOLUME DOWN スイッチと VOLUME UP スイッチがあり、VOLUME 
　DOWN スイッチは、押す毎に音量が－１され、0 になると、それ以上押しても更新されることはない。

・また、VOLUME UP スイッチは、押す毎に＋１され、最大値 15 になると同様に、それ以上押しても更新されるこ
　とはない。

・このように、４個のタクトスイッチのどれかを操作すると、ラジオ局番号／シーク選局、または音量設定値が更
　新され、それに応じて LCD の表示、または音声出力の音量が変更される。

			[ FM AICHI ]		[ FM AICHI ]		[ FM AICHI ]
			[  Vol:xx  ]		[  Lev:xxx ]		[  80.7MHz ]

・また、４個のタクトスイッチのどれかを操作したときには、現在カウント中のタイマーが、必ずクリアされて新
　たに０からカウントが始まり、LCD の２行目に表示される内容は上述のように、初めの２秒間は音量設定値が、
　次の２秒間は受信強度が表示され、その後は、周波数が表示されるようになる。

			タイマーカウントの開始　・・・　FMラジオモジュールの設定、
							ラジオ局名、音量設定値の表示
			　　　カウントの２秒後　・・・　FMラジオモジュールから情報の読み出し
							受信強度の表示
					４秒後　・・・　周波数の表示
					６秒後　・・・　セーブ情報をバックアップ
					１分後　・・・　スリープ状態に移行

・そして、カウントが０から始まって６秒が経過をすると、現在 FMラジオモジュールに設定されているプリセット
　の番号（ラジオ局番号）と音量設定値を、電源 OFF 後の次回の本機の使用時に備えたセーブ情報として、 PIC 
　の EEPROM に自動的にバックアップが取られる。

・したがって、この２秒、４秒、６秒の各カウント値が経過する以前に、４個のスイッチのどれかを続けて押した
　場合には、押す度に必ず０からの新たなカウントがされるようになる。

・特に、セーブ情報のバックアップについては、スイッチの操作毎に無条件で取るのではなく、このように一定の
　時間監視（６秒間）を行い、続く操作がなくなった時点でバックアップを取るようにしている。すなわち、６秒
　が経過する以前に次の操作があった場合には、前の操作のバックアップは取られず無視される。これは書き込み
　回数に制限がある EEPROM の寿命を延ばすために、無駄な書き込みをしないための処置である。

・なお、SEEK機能で選局する場合のセーブ情報は、ラジオ局番号ではなく、FMラジオモジュールに設定するための
　チャネル情報等と音量設定値が対象となる。

・また、同じく SEEK機能で選局する場合には、FMラジオモジュールを設定した直後には、まだ選局したラジオ局名
　が定まらないため、LCD の１行目の表示には次のように、シークを実行中である旨を表示をする。

			[ * Seek * ]		[ NHK FM   ]		[ NHK FM   ]
			[  Vol:xx  ]		[  Lev:xx  ]		[  82.5MHz ]

・そして、タイマーカウント開始の２秒後のタイミングで、FMラジオモジュールから読み出した情報（チャネル情
　報）と、プリセットで選局するときの登録情報をもとにラジオ局名を特定して、LCD の１行目の表示を書き換え
　る。また、２行目に表示されるタイミングと表示内容は、プリセット情報で選局するときと同様である。

・なお、このときにプリセットで選局するときの登録情報内に、FMラジオモジュールから読み出した情報（チャネ
　ル情報）が登録されてなかったときには、ラジオ局名が特定できないため、１行目は次のように表示をする。

						[ ???????? ]		[ ???????? ]
						[  Lev:xx  ]		[  xx.xMHz ]

・このときの２行目の周波数の表示には、FMラジオモジュールから読み出した情報（チャネル情報）を、計算によ
　って周波数に変換をして表示をする。

・本機の４個のタクトスイッチの機能については上述した通りであるが、他に PIC がスリープ状態になっていると
　きだけには、RA ポート変化割り込みの機能を持たせて、４個のタクトスイッチのどれかを操作したときに、スリ
　ープからウェイクアップをさせるようにしている。

・そして、このウェイクアップをした直後には、再び LCD の電源を ON にして、消してしまっていた表示内容の復
　活、ステレオ放送のときには LED も点灯をさせる。 このときは FMラジオモジュールに対しての設定は既に済ん
　でいるので再設定は行わないが、タイマーのカウントを開始をして、そのタイミングでもって FMラジオモジュー
　ルから情報の再読み出しを行ったり、LCD への各種の情報の表示は上述と同様に行う。

・また、ウェイクアップの直後というのは、４個のタクトスイッチのどれかを操作しても、スリープから目覚めさ
　せることが目的であって、FMラジオモジュールに対して設定の変更は行っていないため、このときには６秒が経
　過してもセーブ情報のバックアップも行わない。

（Ver. 1.06 で以下を追加）
・FMスペクトラム図を作成するために、各チャネルの周波数に対する RSSIデータを収集する機能を追加した。

・本機の電源スイッチをオンにした後の開始メッセージが終了する直前までに、TUNING_UP スイッチと VOLUME_DOWN 
　スイッチを同時に押していると、RSSIデータを収集する機能を開始する。

・数秒後に押していた手を離すと、まず、LCD の１行目に周波数（76.0MHz）、２行目にその受信強度が表示される。
　本機では 76 ～ 108 MHz (world wide) の帯域をカバーしているので、下限の 76.0MHz から始まる。

			[  76.0MHz ]
			[  Lev:xx  ]

・そして、TUNING_UP スイッチを押す毎に＋ 0.1 MHz ずつ周波数が増加して、その周波数に対する受信強度ととも
　に、LCD の１行目と２行目にそれぞれが表示される。 上限の 108 MHz に達すると、次にはまた初めの 76.0MHz 
　に戻る。

・また、TUNING_DOWN スイッチの場合には、逆に、押す毎に－ 0.1 MHz ずつ周波数が減少して、同様に LCD の１
　行目と２行目にそれぞれが表示される。 下限の 76.0MHz に達すると、次には上限の 108 MHz になる。

・なお、これらの操作の後で VOLUME_DOWN スイッチを押すと、現在表示されている周波数の増減はしないで、再度
　RSSIデータの収集だけを行って、同様に LCD に表示をし直す。

・このように、帯域 76 ～ 108 MHz 内を３個のスイッチの操作で、自由に行き来することが可能であるが、本機で
　はそれぞれのデータを LCD に表示することしかできないので、データ収集のためには、ユーザがこれらのデータ
　をメモする必要がある。（ (108 - 76) / 0.1 + 1 = 321 個あるので たいへん !! ）

・また、一旦、この RSSIデータの収集機能の実行に移行した場合には、この機能から抜け出す方法はないので、電
　源スイッチをオフにするしかない。 上述のスイッチ操作をしないで電源を入れ直せば、再び、通常のラジオ受信
　機として機能をするようになる。

#endif

;==========================================================================

;	PIC16F684 のピンと用途

;		(1) Vdd: 電源 +3V DC		(14) Vss: 電源 GND
;		(2) RA5: VOLUME_DOWN SW 入力	(13) RA0: 未使用
;		(3) RA4: 未使用			(12) RA1: TUNING_UP SW 入力
;		(4) RA3: VOLUME_UP SW 入力	(11) RA2: TUNING_DOWN SW 入力
;		(5) RC5: SELECT_1 SW 入力	(10) RC0: I2C SDA 出力
;		(6) RC4: SELECT_0 SW 入力	 (9) RC1: I2C SCL 出力
;		(7) RC3: ST/MO LED 出力		 (8) RC2: LCD 電源制御 出力

;	*注.  RA3 は入力専用で、出力ポートには使えない。

;==========================================================================

		list		p=pic16f684		;デバイス情報
		include		p16f684.inc		;インクルードファイル
		errorlevel	-302			;Eliminate bank warning

;==========================================================================
;		コンフィギュレーションの設定
;==========================================================================

	__config 	_CP_OFF & _CPD_OFF & _BOREN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

;==========================================================================
;		定数の定義と変数のレジスタ割付け
;==========================================================================

#define 	ver_no	"1.07"
#define		chan_h	radio_no

		radix	dec

_swTUP		equ	1		;TUNING UP   スイッチ	;PORTA
_swTDWN		equ	2		;TUNING DOWN スイッチ
_swVUP		equ	3		;VOLUME UP   スイッチ
_swVDWN		equ	5		;VOLUME DOWN スイッチ

SDA		equ	0		;I2C-LCD SDA		;PORTC
SCL		equ	1		;I2C-LCD SCL
LCD_PW		equ	2		;LCD 電源制御
LED_ST		equ	3		;ST/MO LED 出力
_swSEL0		equ	4		;SELECT 0  スイッチ
_swSEL1		equ	5		;SELECT 1  スイッチ

tm0_h_val	equ	256 - 200	;ハードタイマー０のカウント値 (4 / 8MHz * 200 = 100μS)
xx		equ	0		;補正値
tm0_s_val	equ	10000 - xx	;ソフトタイマー０のカウント値

		cblock	h'20'
tm0_cnt_h				;ソフトタイマー０の high カウンタ
tm0_cnt_l				;ソフトタイマー０の low  カウンタ
sec_cnt					;秒カウンタ

sel_sw					;SELECT 1,0 スイッチ値の保存用
radio_no ;=chan_h(sel_sw=b'11'のとき)	;ラジオ局番号	┐
chan_l	  ;chan_l(sel_sw=b'11'のとき)	;dummy		│EEPROM セーブ情報
volume					;音量		┘
seekup					;RDA_Reg 02h.9 = SEEKUP
max_no					;ラジオ局番号の最大値
updt_flg				;更新フラグ
;					; bit7: 初期化直後フラグ
;					; bit6: バックアップ抑止フラグ
;					; bit5: スリープ移行フラグ
;					; bit4: バックアップ要求フラグ
;					; bit3: 受信強度表示の終了フラグ
;					; bit2: 音量表示の終了フラグ
;					; bit1: 音量更新フラグ
;					; bit0: ラジオ局番号更新フラグ
freq_high				;表示する周波数アドレスの保存用 high バイト
freq_low				;表示する周波数アドレスの保存用 low バイト
rda_reg_h				;RDA_Reg への書き込み high バイト
rda_reg_l				;RDA_Reg への書き込み low バイト
reg_0a_h				;RDA_Reg 0ah の読み出し high バイト
reg_0a_l				;RDA_Reg 0ah の読み出し low バイト
reg_0b_h				;RDA_Reg 0bh の読み出し high バイト
reg_0b_l				;RDA_Reg 0bh の読み出し low バイト
freq_flg				;CHAN から周波数へ変換指示フラグ
spec_flg				;FMスペクトラムRSSIデータ収集フラグ

lpcnt					;ワーク
work1					;ワーク
work2					;ワーク
buff		;(00)			;変換文字列の格納エリア
buff1		;(01)			;
buff2		;(02)			;
buff3		;(03)			;
buff4		;(04)			;
dat_a1					;加算引き数Ａ(上位) ／ 結果(上位)
dat_a2					;加算引き数Ａ(下位) ／ 結果(上位)
dat_b1					;加算引き数Ｂ(上位)
dat_b2					;加算引き数Ｂ(下位)

wt1cnt					;ウェイト用ループカウンタ
wt2cnt					;ウェイト用ループカウンタ
wt3cnt					;ウェイト用ループカウンタ

; ( I2C_S_LCD.sub で使用 )
;wt1cnt
addr_high				;読み出すデータの high アドレス
addr_low				;読み出すデータの low アドレス
lcdtmp					;LCD への出力データの一時保存
lcdtmp2					;
i2ctmp					;
bit_cnt					;ループカウンタ
		endc

		cblock	h'70'
w_save					;W レジスタの退避用
s_save					;STATUS レジスタの退避用
		endc

;==========================================================================
;		マクロ命令定義
;==========================================================================

;		I2C LCD メッセージデータ表示マクロ

Disp_I2cLcd	macro	@com,@msg,@x
		movlw	@com
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
	#if 	@x == 0
	#else
		movlw	high @msg	;メッセージデータの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low @msg	;メッセージデータの先頭アドレスの low アドレス
		movwf	addr_low	;
	#endif
		call	I2cLcd_Disp	;メッセージデータ１行表示
		endm

;==========================================================================
;		プログラム開始
;==========================================================================

		org	0		;リセットベクタ
		goto	init
		org	4		;割り込みベクタ
		goto	interrupt

;;;;;		org	h'08'		;↓ 下の(注). のリンクエラーを起こすのを防ぐため、
					;　 とりあえず使用エリアを３ワード増やした。（Ver. 1.06 にて）

;==========================================================================
;		サブルーチンファイルのインクルード
;==========================================================================

#define 	_PORTC

		include		D:\Project\PIC\_sub\I2C_S_LCD.sub

;==========================================================================
;		初期化処理
;==========================================================================

init
		clrf	STATUS		;STATUS クリア
	 	clrf	INTCON		;INTCON クリア

		; PORTA,PORTB をデジタルI/O に設定

		clrf	PORTA		;
		clrf	PORTC		;
		movlw	b'00000111'	;Digital I/O
		movwf	CMCON0		;CMCON0 に設定

		bsf	STATUS,RP0	;バンク 1
		clrf	ANSEL		;Digital I/O
		movlw	b'00111111'
		movwf	TRISA		;ポートA の I/0 設定
		movlw	b'00110000'
		movwf	TRISC		;ポートC の I/0 設定

		movlw	b'00100110'	;RA5,RA2-RA1
		movwf	WPUA		;プルアップ有効
		movlw	b'00101110'	;RA5,RA3-RA1
		movwf	IOCA		;RAポート変化割り込み bit の設定

		; 内部クロックを 8 MHz に設定

		movlw	h'70'		;CLOCK=8MHz
		movwf	OSCCON

		; OPTION_REG の設定

		movlw	h'08'		;プルアップ有効、プリスケーラ: 使用しない
		movwf	OPTION_REG

		; 割り込みの設定

		bcf	STATUS,RP0	;バンク 0
		clrf	TMR0		;TMR0 クリア
;		bcf	INTCON,T0IE	;T0IE: タイマー０割り込みを禁止
;		bcf	INTCON,RAIE	;RAIE: RAポート変化割り込みを禁止
		bsf	INTCON,GIE	;GIE: グローバル割り込みを許可

		; スタートメッセージの表示

		bcf	PORTC,LCD_PW	;LCD 電源制御 ON 
		bsf	PORTC,SDA	;SDA="H"
		bsf	PORTC,SCL	;SCL="H"
		call	wait_200ms	;
		call	I2cLcd_Init	;I2C LCD (AQM0802A) の初期設定
		movlw	h'0c'		;表示オン／オフコントロール
		call	I2cLcd_Cmd	;D=1: 表示オン, C=0: カーソル非表示, B=0: 非ブリンク
		bsf	PORTC,LED_ST	;ST/MO LED 表示チェック = ON、

		movlw	2
		movwf	lpcnt		;メッセージの表示回数
init01
;			[FmStereo]	;スタートメッセージ
;			[Radio II]
		Disp_I2cLcd	h'80',start_msg,1	;DDRAM のアドレスをセット(１行目先頭)
		Disp_I2cLcd	h'c0',         ,0	;DDRAM のアドレスをセット(２行目先頭)
		call	wait_1sec
		call	wait_1sec

;			[Ver x.xx]
;			[ by M.Y ]
		Disp_I2cLcd	h'80',         ,0	;DDRAM のアドレスをセット(１行目先頭)
		Disp_I2cLcd	h'c0',         ,0	;DDRAM のアドレスをセット(２行目先頭)
		call	wait_1sec
		call	wait_1sec

		decfsz	lpcnt,F		;メッセージの表示回数 = 終了 か？
		goto	init01		;No

		bcf	PORTC,LED_ST	;ST/MO LED 表示チェック = OFF
		movlw	h'01'		;表示クリア
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		call	wait_1sec
		call	wait_1sec

		; 各変数の初期設定

		clrf	updt_flg	;更新フラグの初期設定
		bsf	updt_flg,7	;初期化直後フラグ = ON
		clrf	freq_flg	;CHAN から周波数へ変換指示フラグの初期設定

		clrf	spec_flg		;;RSSIデータ収集フラグの初期設定
		movlw	b'00101110'		;;（Ver. 1.06 で追加）
		andwf	PORTA,W			;;
		xorlw	b'00001100'		;;
		btfsc	STATUS,Z		;;_swTUP & _swVDWN = ON か？
		goto	spectrum		;;Yes

		swapf	PORTC,W		;SELECT 1,0 スイッチを読み取る
		andlw	h'03'
		xorlw	h'03'		;反転して
		movwf	sel_sw		;SELECT 1,0 スイッチ値を保存

		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'00' か？
		goto	init02		;No
			; sel_sw = b'00'
		movlw	(fm_radio_n_tbl_end - fm_radio_n_tbl) / 20 - 1	;（ 名古屋周辺地区用 ）
		goto	init05

init02		movlw	b'10'
		subwf	sel_sw,W
		btfsc	STATUS,C	;SELECT 1,0 スイッチ < b'10' か？
		goto	init03		;No
			; sel_sw = b'01'
		movlw	(fm_radio_t_tbl_end - fm_radio_t_tbl) / 20 - 1	;（ 東京周辺地区用 ）
		goto	init05

init03		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'10' か？
		goto	init04		;No
			; sel_sw = b'10'
		movlw	(fm_radio_o_tbl_end - fm_radio_o_tbl) / 20 - 1	;（ 大阪周辺地区用 ）
		goto	init05
			; sel_sw = b'11'
init04		movlw	(fm_radio_o_tbl_end - fm_radio_n_tbl) / 20 - 1	;（ SEEK機能で選局時 ）

init05		movwf	max_no		;ラジオ局番号の最大値を設定

		; セーブ情報（ラジオ局番号、音量）の復帰

		call	eeprom_read	;EEPROM からデータ読み出し

		movlw	b'11'
		subwf	sel_sw,W
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	init06		;No
					;chan_l = xx00.00x0
		movlw	h'02'		;         CC  .  S 
		andwf	chan_l,W
		movwf	seekup		;SEEKUP を取り出す
		movlw	h'c0'
		andwf	chan_l,F	;CHAN[1:0] を取り出す

init06		goto	main

;--------------------------------------------------------------------------
;		I2C LCD（ AQM0802A の場合 ）表示用 メッセージ等
;--------------------------------------------------------------------------

		; スタートメッセージ

start_msg	dt	"FmStereo",0
		dt	"Radio II",0
;			 01234567

		dt	"Ver ",ver_no,0
		dt	" by M.Y ",0
;			 01234567

		; 音量メッセージ

volume_msg	dt	" Vol:   ",0
;			 01234567

		; 受信強度メッセージ

level_msg	dt	" Lev:   ",0
;			 01234567

		; シーク操作中メッセージ

seek_msg	dt	"* Seek *",0
;			 01234567

;--------------------------------------------------------------------------
;		EEPROM からデータ読み出し
;--------------------------------------------------------------------------

eeprom_read
		movf	sel_sw,W	;SELECT 1,0 スイッチ値
		addwf	sel_sw,W
		addwf	sel_sw,W	;x 3

		bsf	STATUS,RP0	;バンク 1
		addlw	save_data - eep_start
		movwf	EEADR		;EEPROM の記録アドレスを設定

		bcf	STATUS,RP0	;バンク 0
		movlw	radio_no	;ラジオ局番号のアドレスを
		movwf	FSR		;間接アドレスに設定
		movlw	3
		movwf	lpcnt		;ループカウンタ

eeprd01		bsf	STATUS,RP0	;バンク 1
		bsf	EECON1,RD	;読み出し制御 = ON
		movf	EEDATA,W	;データの読み出し
		incf	EEADR,F		;記録アドレスの更新

		bcf	STATUS,RP0	;バンク 0
		movwf	INDF		;データ読み出し
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	eeprd01		;No

		return

;--------------------------------------------------------------------------
;		EEPROM へデータ書き込み
;--------------------------------------------------------------------------

eeprom_write
		movf	sel_sw,W	;SELECT 1,0 スイッチ値
		addwf	sel_sw,W
		addwf	sel_sw,W	;x 3

		bsf	STATUS,RP0	;バンク1
		addlw	save_data - eep_start
		movwf	EEADR		;EEPROM の記録アドレスを設定

		bcf	STATUS,RP0	;バンク0
		movlw	radio_no	;ラジオ局番号のアドレスを
		movwf	FSR		;間接アドレスに設定
		movlw	3
		movwf	lpcnt		;ループカウンタ

eepwr01		movf	INDF,W		;書き込みデータを読み出し

		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

		incf	EEADR,F		;記録アドレスの更新

		bcf	STATUS,RP0	;バンク 0
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	eepwr01		;No

		return

;==========================================================================
;		メイン・ルーチン
;==========================================================================

;updt_flg	;更新フラグ
;		; bit7: 初期化直後フラグ
;		; bit6: バックアップ抑止フラグ		  タイマー設定の
;		; bit5: スリープ移行フラグ		... 1 分後に ON
;		; bit4: バックアップ要求フラグ		... 6 秒後に ON
;		; bit3: 受信強度表示の終了フラグ	... 4 秒後に ON
;		; bit2: 音量表示の終了フラグ		... 2 秒後に ON
;		; bit1: 音量更新フラグ
;		; bit0: ラジオ局番号更新フラグ

main
		btfsc	updt_flg,7	;初期化直後フラグ = ON か？
		goto	m01		;Yes

		; ウェイクアップ直後かどうかのチェック

		btfss	updt_flg,5	;スリープ移行フラグ = ON か？
		goto	m03		;No

		bcf	PORTC,LCD_PW	;LCD 電源制御 ON
		call	I2cLcd_Init	;I2C LCD (AQM0802A) の初期設定
		movlw	h'0c'		;表示オン／オフコントロール
		call	I2cLcd_Cmd	;D=1: 表示オン, C=0: カーソル非表示, B=0: 非ブリンク

m01		call	table_select	;FM ラジオ放送局のテーブル選択 … 局名、音量の表示

		btfsc	updt_flg,7	;初期化直後フラグ = ON か？
		call	fm_tuner_set	;Yes.（RDA5807M FM Tuner）の設定

		clrf	updt_flg	;更新フラグの初期設定
		bsf	updt_flg,6	;バックアップ抑止フラグ = ON

m02		movf	PORTA,W
		andlw	b'00101110'	;スイッチ入力を
		xorlw	b'00101110'	;反転
		btfss	STATUS,Z	;スイッチ操作 = なし か？
		goto	m02		;No
		goto	m05		;Yes

		; 各スイッチの ON／OFF 状態のチェック

m03		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		call	tuning_down	;Yes

		btfss	PORTA,_swTUP	;TUNING UP スイッチ = ON か？
		call	tuning_up	;Yes

		btfsc	updt_flg,0	;ラジオ局番号更新フラグ = ON か？
		goto	m04		;Yes

		btfss	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		call	volume_down	;Yes

		btfss	PORTA,_swVUP	;VOLUME UP スイッチ = ON か？
		call	volume_up	;Yes

		btfss	updt_flg,1	;音量更新フラグ = ON か？
		goto	m06		;No

		; ラジオ局番号、音量の更新があったとき

m04		call	table_select	;FM ラジオ放送局のテーブル選択 … 局名、音量の表示
		call	fm_tuner_set	;（RDA5807M FM Tuner）の設定

		clrf	updt_flg	;更新フラグの初期設定
		call	wait_30ms	;30m 秒 ウェイト

		; タイマー０の設定

m05		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		movlw	60		;1 分
		movwf	sec_cnt		;秒カウンタ
		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	;タイマー０割り込みを許可
		goto	main

		; 音量表示の終了フラグのチェック

m06		btfss	updt_flg,2	;音量表示の終了フラグ = ON か？
		goto	m07		;No

		call	rssi_disp	;(２行目表示) … 受信強度の表示
		bcf	updt_flg,2	;音量表示の終了フラグ = OFF
		goto	main

		; 受信強度表示の終了フラグのチェック

m07		btfss	updt_flg,3	;受信強度表示の終了フラグ = ON か？
		goto	m10		;No

		btfsc	freq_flg,0	;CHAN から周波数へ変換指示フラグ = ON か？
		goto	m08		;Yes

		movf	freq_high,W	;表示する周波数のアドレスを復帰
		movwf	addr_high	;
		movf	freq_low,W	;
		movwf	addr_low	;

		Disp_I2cLcd    h'c0',,0 ;(２行目表示) … 周波数の表示
		goto	m09

m08		call	chan_freq_conv	;CHAN から周波数へ変換と周波数の表示

m09		bcf	updt_flg,3	;受信強度表示の終了フラグ = OFF
		goto	main

		; バックアップ要求フラグのチェック

m10		btfss	updt_flg,4	;バックアップ要求フラグ = ON か？
		goto	m11		;No

		call	eeprom_write	;EEPROM へデータ書き込み
		bcf	updt_flg,4	;バックアップ要求フラグ = OFF
		goto	main

		; スリープ移行フラグのチェック

m11		btfss	updt_flg,5	;スリープ移行フラグ = ON か？
		goto	main		;No

		bsf	PORTC,LCD_PW	;LCD 電源制御 OFF
		bcf	PORTC,LED_ST	;ST/MO LED 消灯

m12		bcf	INTCON,RAIF	;RAポート変化割り込みフラグをクリア
		bsf	INTCON,RAIE	;RAポート変化割り込みを許可
		sleep			;おやすみなさい
		;
		;			;wake up
		movf	PORTA,W
		andlw	b'00101110'	;スイッチ入力を
		xorlw	b'00101110'	;反転
		btfsc	STATUS,Z	;スイッチ操作 = なし か？
		goto	m12		;Yes. 再度 sleep
		goto	main		;No

;==========================================================================
;		（ デバッグ用 ）
;==========================================================================

#ifdef _debug

		; レジスタ内容の表示
test_disp
		movlw	h'80'		;
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力

		movlw	reg_0a_h
		movwf	FSR		;間接アドレスに設定
		movlw	4
		movwf	lpcnt		;

tdsp01		movf	INDF,W
		call	lcd_hex_out
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;メッセージの表示回数 = 終了 か？
		goto	tdsp01

		return

;--------------------------------------------------------------------------

#define		cnvtmp	buff
#define		hextmp	buff + 1

		; １バイト変換 (BIN → HEX 文字) ２文字 LCD 出力
lcd_hex_out
		movwf	cnvtmp		;変換データの一時保存
		swapf	cnvtmp,W
		andlw	h'0f'
		call	hex_conv	;上位４ビットの変換
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力

		movlw	h'0f'
		andwf	cnvtmp,W
		call	hex_conv	;下位４ビットの変換
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		return

		; ４ビット変換 (BIN → HEX 文字)
hex_conv
		movwf	hextmp
		sublw	h'0a'
		btfsc	STATUS,Z	;
		goto	cnv01		;W = h'0a' 

		btfss	STATUS,C	;
		goto	cnv01		;W > h'0a' 

		movlw	h'30'		;0 ～ 9
		goto	cnv02

cnv01		movlw	h'40' - 9	;A ～ F

cnv02		addwf	hextmp,W
		return

#endif

;--------------------------------------------------------------------------
;		TUNING DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

tuning_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		goto	tdwn02		;No

		bsf	updt_flg,0	;ラジオ局番号更新フラグ = ON

		movlw	b'11'
		subwf	sel_sw,W
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	tdwn01		;No

		bcf	seekup,1	;SEEKUP = 0: Seek down
		goto	tdwn02

tdwn01		decf	radio_no,F	;ラジオ局番号 - 1
		incf	radio_no,W	;
		btfss	STATUS,Z	;W reg = 0 か？
		goto	tdwn02		;No

		movf	max_no,W	;
		movwf	radio_no	;ラジオ局番号 = 最大値

tdwn02		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		TUNING UP スイッチ = ON の処理
;--------------------------------------------------------------------------

tuning_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTUP	;TUNING DOWN スイッチ = ON か？
		goto	tup02		;No

		bsf	updt_flg,0	;ラジオ局番号更新フラグ = ON

		movlw	b'11'
		subwf	sel_sw,W
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	tup01		;No

		bsf	seekup,1	;SEEKUP = 1: Seek up
		goto	tup02

tup01		incf	radio_no,F	;ラジオ局番号 + 1
		movf	radio_no,W
		subwf	max_no,W	;max_no - W(radio_no)
		btfsc	STATUS,C	;radio_no =< max_no か？
		goto	tup02		;Yes

		clrf	radio_no	;ラジオ局番号 = 最小値 = 0

tup02		btfss	PORTA,_swTUP	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		VOLUME DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

volume_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		goto	vdwn01		;No

		bsf	updt_flg,1	;音量更新フラグ = ON
		decf	volume,F	;音量 - 1
		incf	volume,W	;
		btfss	STATUS,Z	;W reg = 0 か？
		goto	vdwn01		;No

		clrf	volume		;音量 = 最小値 = 0

vdwn01		btfss	PORTA,_swVDWN	;VOLUME DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		VOLUME UP スイッチ = ON の処理
;--------------------------------------------------------------------------

volume_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swVUP	;VOLUME UP スイッチ = ON か？
		goto	vup01		;No

		bsf	updt_flg,1	;音量更新フラグ = ON
		incf	volume,F	;音量 + 1
		movf	volume,W
		sublw	15		;15 - W(volume)
		btfsc	STATUS,C	;volume > 15 か？
		goto	vup01		;No

		movlw	15		;h'0f'
		movwf	volume		;音量 = 最大値

vup01		btfss	PORTA,_swVUP	;VOLUME UP スイッチ = OFF か？
		goto	$ - 1		;No

		return

;==========================================================================
;		FM ラジオ放送局のテーブル選択と表示
;==========================================================================

;	入力:	radio_no: ラジオ局番号

table_select
		; ラジオ局番号から該当のテーブルデータアドレスの計算

		movf	sel_sw,W	;SELECT 1,0 スイッチ値
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'00' か？
		goto	tblsel01	;No
					;（ 名古屋周辺地区用 ）
		movlw	high fm_radio_n_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_n_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;
		goto	tblsel03

tblsel01	movlw	b'10'
		subwf	sel_sw,W
		btfsc	STATUS,C	;SELECT 1,0 スイッチ < b'10' か？
		goto	tblsel02	;No
					;（ 東京周辺地区用 ）
		movlw	high fm_radio_t_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_t_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;
		goto	tblsel03
		
tblsel02	btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'10' か？
		goto	tblsel04	;No
					;（ 大阪周辺地区用 ）
		movlw	high fm_radio_o_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_o_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;

tblsel03	movf	radio_no,W	;ラジオ局番号
		movwf	work1		;x20 倍する、(1 テーブルデータ = 20 byte)
		bcf	STATUS,C
		rlf	work1,F		;x 2
		rlf	work1,F		;x 4
		movf	work1,W
		movwf	work2
		rlf	work1,F		;x 8
	;;	btfsc	STATUS,C	;オーバーフローした か？
	;;	incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新
	;;	bcf	STATUS,C	;(Ver. 1.07b で追加)

		rlf	work1,W		;x16
		btfsc	STATUS,C	;オーバーフローした か？			***（注）↓
		incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新	*** 下の例を参照

		addwf	work2,W		;x16 + x4 = x20
		btfsc	STATUS,C	;オーバーフローした か？			***（注）↓      (Ver. 1.07 で 2行追加)
		incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新	*** 下の例を参照

		addwf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新

		; LCD へ局名の表示

;			[NHK FM  ]
;			 01234567
		Disp_I2cLcd    h'80',,0 ;(１行目表示) … 局名の表示

		movf	addr_high,W	;表示する周波数のアドレスを保存
		movwf	freq_high	;
		movf	addr_low,W	;
		movwf	freq_low	;

		; テーブルデータ（RDA_Reg 03h への書き込み用データ）の読み出し

		movlw	9		;ポインタを進める
		addwf	addr_low,F
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し
		movwf	rda_reg_h	;RDA_Reg 03h への書き込み high バイト

		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し
		movwf	rda_reg_l	;RDA_Reg 03h への書き込み low バイト
		goto	tblsel05

		; sel_sw = b'11' （SEEK選局）のとき

;			[* Seek *]
;			 01234567
tblsel04	Disp_I2cLcd    h'80',seek_msg,1 ;(１行目表示) … シーク操作中メッセージの表示

		; LCD へ音量の表示	;音量の範囲 = 0 ～ 15

;			[ Vol:   ]
;			 01234567
tblsel05	Disp_I2cLcd    h'c0',volume_msg,1 ;(２行目表示) … 音量の表示

		movlw	10
		subwf	volume,W
		btfsc	STATUS,C	;volume >= 10 か？
		goto	tblsel06	;Yes

		movlw	h'c6'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movf	volume,W
		goto	tblsel07

tblsel06	movlw	h'c5'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movlw	a'1'		;
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		movlw	10
		subwf	volume,W

tblsel07	iorlw	h'30'		;ASCII コードに変換
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力

		return

;--------------------------------------------------------------------------		***（注）↑

;	12		13		14		15		16		31		32

;	0000.1100	0000.1101	0000.1110	0000.1111	0001.0000	0001.1111	0010.0000

;x 4	0011.0000	0011.0100	0011.1000	0011.1100	0100.0000	0111.1100	1000.0000
;x16	1100.0000	1101.0000	1110.0000       1111.0000     1.0000.0000     1.1111.0000    10.0000.0000
;	---------     -----------     -----------     -----------    ------------    ------------    ------------
;x20	1111.0000     1.0000.0100     1.0001.1000     1.0010.1100     1.0100.0000    10.0110.1100    10.1000.0000
;									       ( radio_no = 0～31まで可 )

;--------------------------------------------------------------------------
;		FM ラジオ放送局 データテーブル 
;--------------------------------------------------------------------------

;	     ( RDA5807M のデータシート Page 10、REG 03H: Channel Select を参照 )

;	     周波数: Frequency (MHz) = SPACE (kHz) x CHAN + 76.0 MHz  … SPACE = 100

;				CHAN = (Frequency (MHz) - 76.0 (MHz)) / SPACE (kHz)
;				     = (Frequency - 76.0) x 10

; 例.  FM AICHI:80.7MHz   →	CHAN = 807 - 760 = 47 = b'0010 1111'         … Binary に変換

;				         CHAN << 6 =   b'00 1011 1100 0000'  … 左に 6 bit シフト

;				                   +             b'01 1000'  … OR をする
;				                   ------------------------
;				                   = b'0000 1011 1101 1000'  … 16 bit に書き換える

;;;;;	各地区で受信可能な FM ラジオ局が、以下の各テーブル内に設定がない場合には、（ radio_no = 0～31まで）追加が可能です。

fm_radio_n_tbl
;		                                                         00: SPACE = 100 kHz
;			 局名           周波数       ┌─  CHAN ──┐  ┌┐	;ラジオ局番号

		dt	"MID-FM  ",0, " 76.1MHz",0, b'00000000', b'01011000'	; 0 … （ 名古屋周辺地区用 ）
		dt	"ZIP-FM  ",0, " 77.8MHz",0, b'00000100', b'10011000'	; 1
		dt	"FM AICHI",0, " 80.7MHz",0, b'00001011', b'11011000'	; 2
		dt	"NHK FM  ",0, " 82.5MHz",0, b'00010000', b'01011000'	; 3
		dt	"TOKAI   ",0, " 92.9MHz",0, b'00101010', b'01011000'	; 4
		dt	"CBC     ",0, " 93.7MHz",0, b'00101100', b'01011000'	; 5

;			                                              └┘
;			                                               10: BAND = 76-108 MHz
fm_radio_n_tbl_end

fm_radio_t_tbl
		dt	"NACK5   ",0, " 79.5MHz",0, b'00001000', b'11011000'	; 0 … （ 東京周辺地区用 ）
		dt	"TOKYO FM",0, " 80.0MHz",0, b'00001010', b'00011000'	; 1
		dt	"J-WAVE  ",0, " 81.3MHz",0, b'00001101', b'01011000'	; 2
		dt	"NHK YKH ",0, " 81.9MHz",0, b'00001110', b'11011000'	; 3
		dt	"NHK TKO ",0, " 82.5MHz",0, b'00010000', b'01011000'	; 4
		dt	"YOKOHAMA",0, " 84.7MHz",0, b'00010101', b'11011000'	; 5
		dt	"InterFM ",0, " 89.7MHz",0, b'00100010', b'01011000'	; 6
		dt	"TBS FM  ",0, " 90.5MHz",0, b'00100100', b'01011000'	; 7
		dt	"BUNKA FM",0, " 91.6MHz",0, b'00100111', b'00011000'	; 8
		dt	"NIPPON  ",0, " 93.0MHz",0, b'00101010', b'10011000'	; 9
fm_radio_t_tbl_end

fm_radio_o_tbl
		dt	"CO.CO.LO",0, " 76.5MHz",0, b'00000001', b'01011000'	; 0 … （ 大阪周辺地区用 ）
		dt	"BeHappy!",0, " 78.9MHz",0, b'00000111', b'01011000'	; 1
		dt	"FM OSAKA",0, " 85.1MHz",0, b'00010110', b'11011000'	; 2
		dt	"NHK FM  ",0, " 88.1MHz",0, b'00011110', b'01011000'	; 3
		dt	"MBS     ",0, " 90.6MHz",0, b'00100100', b'10011000'	; 4
		dt	"OBC     ",0, " 91.9MHz",0, b'00100111', b'11011000'	; 5
		dt	"ABC     ",0, " 93.3MHz",0, b'00101011', b'01011000'	; 6
fm_radio_o_tbl_end

;==========================================================================
;		（RDA5807M FM Tuner）の設定
;==========================================================================

fm_tuner_set
		btfss	updt_flg,7	;初期化直後フラグ = ON か？
		goto	ftset03		;No

		; updt_flg.7 = 1 （初期化直後）のとき

		call	i2c_start	;スタートコンディション
		movlw	h'20'		;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		movlw	b'11010000'	;RDA_Reg 02h、15=DHIZ,14=DMUTE,13=MONO,12=BASS,11=RCLK_N,10=RCLK_D,9=SEEKUP,8=SEEK
		call	byte_send	;1 バイト送信
		movlw	b'00000001'	;RDA_Reg 02h、7=SKMODE,6:4=CLK_MODE[2:0],3=RDS_EN,2=NEW_METHOD,1=SOFT_RESET,0=ENABLE
		call	byte_send	;1 バイト送信

		movlw	b'11'
		subwf	sel_sw,W
		btfsc	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	ftset01		;Yes

		movf	rda_reg_h,W	;RDA_Reg 03h、15:8=CHAN[9:2]
		call	byte_send	;1 バイト送信      ---------
		movf	rda_reg_l,W	;RDA_Reg 03h、7:6=CHAN[1:0],5=DIRECT MODE,4=TUNE,3:2=BAND[1:0],1:0=SPACE[1:0]
		call	byte_send	;1 バイト送信     ---------
		goto	ftset02

ftset01		movf	radio_no,W	;RDA_Reg 03h、15:8=CHAN[9:2], (radio_no = chan_h)
		call	byte_send	;1 バイト送信      ---------
		movf	chan_l,W	;RDA_Reg 03h、7:6=CHAN[1:0],5=DIRECT MODE,4=TUNE,3:2=BAND[1:0],1:0=SPACE[1:0]
		iorlw	h'18'		;4=TUNE = '1', 3:2=BAND[1:0] = '10' = 76-108 MHz (world wide), 1:0=SPACE[1:0] = '00' = 100 kHz
					;チューニング操作が完了すると、TUNE = 0 に設定され、STC = 1 に設定される
		call	byte_send	;1 バイト送信

ftset02		movlw	b'00000000'	;RDA_Reg 04h、15:12=RSVD,11=DE,10=RSVD,9=SOFTMUTE_EN,8=AFCD
		call	byte_send	;1 バイト送信
		movlw	b'00000000'	;RDA_Reg 04h
		call	byte_send	;1 バイト送信

		movlw	b'10001000'	;RDA_Reg 05h、15=INT_MODE,14:12=RSVD,11:8=SEEKTH[3:0]
		call	byte_send	;1 バイト送信
		movf	volume,W
		iorlw	b'10000000'	;RDA_Reg 05h、7:6=?? ,5:4=RSVD,3:0=VOLUME[3:0]
		call	byte_send	;1 バイト送信                      -----------

		call	i2c_stop	;ストップコンディション
		goto	ftset06

ftset03		btfss	updt_flg,0	;ラジオ局番号更新フラグ = ON か？
		goto	ftset05		;No

		; updt_flg.0 = 1 （ラジオ局番号更新）のとき

		movlw	b'11'
		subwf	sel_sw,W
		btfsc	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	ftset04		;Yes

		movlw	h'03'		;
		call	rda_reg_write	;RDA_Reg = h'03' へ書き込み
		goto	ftset06

		; updt_flg.0 = 1 && sel_sw = b'11' （SEEK選局）のとき

		;	9 = SEEKUP:  0 = シークダウン, 1 = シークアップ
		;	8 = SEEK:    0 = ストップシークを無効, 1 = 有効
		;	    シーク動作が完了すると、SEEK = 0 に設定され、STC = 1 に設定される
		;	7 = SKMODE:  0 = 帯域の上限または下限でラップしてシークを続行, 1 = シークを停止

ftset04		movlw	b'11010001'	;RDA_Reg 02h、15=DHIZ,14=DMUTE,13=MONO,12=BASS,11=RCLK_N,10=RCLK_D,9=SEEKUP,8=SEEK
		iorwf	seekup,W	;                                                                  -------- ------
		movwf	rda_reg_h
		movlw	b'00000001'	;RDA_Reg 02h、7=SKMODE,6:4=CLK_MODE[2:0],3=RDS_EN,2=NEW_METHOD,1=SOFT_RESET,0=ENABLE
		movwf	rda_reg_l	;             --------                                                      --------
		movlw	h'02'		;
		call	rda_reg_write	;RDA_Reg = h'02' へ書き込み
		goto	ftset06

		; updt_flg.1 = 1 （音量更新）のとき

ftset05		movlw	b'10001000'	;
		movwf	rda_reg_h
		movf	volume,W
		iorlw	b'10000000'	;
		movwf	rda_reg_l
		movlw	h'05'		;
		call	rda_reg_write	;RDA_Reg = h'05' へ書き込み

ftset06		return

;==========================================================================
;	（RDA5807M FM Tuner）からレジスタ内容の読み出しと表示
;==========================================================================

rssi_disp
		movlw	reg_0a_h	;読み出しデータの格納アドレス
		movwf	FSR		;間接アドレスに設定
		movlw	h'0a'		;
		call	rda_reg_read	;RDA_Reg = h'0a' から読み出し

		btfss	reg_0a_h,6	;STC ビット = 1（シーク/チューン完了）か？
		goto	rssi_disp	;No

		incf	FSR,F
		movlw	h'0b'		;
		call	rda_reg_read	;RDA_Reg = h'0b' から読み出し

		bcf	PORTC,LED_ST	;ST/MO LED 消灯
		btfsc	reg_0a_h,2	;ST（Stereo Indicator）ビット = 1 か？
		bsf	PORTC,LED_ST	;Yes. ST/MO LED 点灯

		rrf	reg_0b_h,W
		andlw	h'7f'		;RSSI[6:0]
		movwf	work1
		call	bindec8cz_cnv	;10進数文字列に変換（ゼロサプレス)

;			[ Lev:   ]
;			 01234567
		Disp_I2cLcd    h'c0',level_msg,1 ;(２行目表示) … 受信強度の表示

		movlw	h'c5'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力

;		movlw	buff		;変換文字列の格納エリア
		movlw	buff + 1	;変換文字列の格納エリア
		movwf	FSR		;間接アドレスの設定
;		movlw	3		;
		movlw	2		;
		movwf	lpcnt		;ループカウンタに設定

rssi01		movf	INDF,W
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	rssi01		;No

		movlw	b'11'
		subwf	sel_sw,W
		btfsc	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		call	channel_compare	;Yes. 局名の表示

		return

;--------------------------------------------------------------------------
;	SEEK で選局時の局名の表示と 周波数のテーブルアドレスを保存
;--------------------------------------------------------------------------

;		Reg_03h 15:6     CHAN[9:0]	xxxx.xxxx xx00.0000
;		Reg_0Ah  9:0 READCHAN[9:0]	0000.00xx xxxx.xxxx

channel_compare
		movf	reg_0a_h,W	;READCHAN[9:8]
		movwf	work2
		movf	reg_0a_l,W	;READCHAN[7:0]
		movwf	work1		; work2:????.??xx, work1:xxxx.xxxx

		; 10 ビットデータを 右詰め → 左詰め に変換
		rrf	work2,F		;????.???x x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;x???.???? x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;xx??.???? ? 
		movlw	h'c0'
		andwf	work2,F		; work1:xxxx.xxxx, work2:xx00.0000

		movlw	high fm_radio_n_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_n_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;

		movf	max_no,W	;
		movwf	lpcnt		;ループカウンタ

chan01		movf	addr_high,W	;テーブルの先頭アドレス（局名）を退避
		movwf	freq_high	;
		movf	addr_low,W	;
		movwf	freq_low	;

		movlw	9 * 2		;ポインタを進める
		addwf	addr_low,F
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し

		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新

		subwf	work1,W		;high バイトの比較
		btfss	STATUS,Z	;READCHAN[9:2] = CHAN[9:2] か？
		goto	chan02		;No

		call	tbl_read	;テーブルデータの読み出し
		andlw	h'c0'
		subwf	work2,W		;low バイトの比較
		btfsc	STATUS,Z	;READCHAN[1:0] = CHAN[1:0] か？
		goto	chan04		;Yes

chan02		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	chan01		;No

		; テーブル上に登録がなかったとき
		movlw	h'80'		;１行目表示
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movlw	8
		movwf	lpcnt		;ループカウンタ

chan03		movlw	'?'
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	chan03		;No

		bsf	freq_flg,0	;CHAN から周波数へ変換指示フラグ = 1
		goto	chan05

		; テーブル上に登録があったとき
chan04		movf	freq_high,W	;表示する局名のアドレスを復帰
		movwf	addr_high	;
		movf	freq_low,W	;
		movwf	addr_low	;

		Disp_I2cLcd    h'80',,0 ;(１行目表示) … 局名の表示

		movf	addr_high,W	;表示する周波数のアドレスを保存
		movwf	freq_high	;
		movf	addr_low,W	;
		movwf	freq_low	;
		bcf	freq_flg,0	;CHAN から周波数へ変換指示フラグ = 0

		; EEPROM へのセーブ対象情報
chan05		movf	work1,W
		movwf	radio_no	;= chan_h
		movf	work2,W
		iorwf	seekup,W	;SEEKUP を足し込む
		movwf	chan_l		;

		return

;--------------------------------------------------------------------------
;	CHAN から周波数へ変換と 周波数の表示（テーブル上に登録がないとき）
;--------------------------------------------------------------------------

;	周波数: Frequency (MHz) = SPACE (kHz) x CHAN + 76.0 MHz
;		Frequency x 10  =               CHAN + 760 

;		Reg_0Ah  9:0 READCHAN[9:0]	0000.00xx xxxx.xxxx

chan_freq_conv
		btfsc	spec_flg,0	;RSSIデータ収集フラグ = ON か？
		goto	chfrcv01	;Yes

		movf	reg_0a_h,W	;READCHAN[9:8]
		andlw	h'03'
		movwf	dat_a1
		movf	reg_0a_l,W	;READCHAN[7:0]
		movwf	dat_a2
		goto	chfrcv02

chfrcv01	movf	chan_h,W	;CHAN[9:8]
		movwf	dat_a1
		movf	chan_l,W	;CHAN[7:0]
		movwf	dat_a2

chfrcv02	movlw	high 760
		movwf	dat_b1
		movlw	low 760
		movwf	dat_b2
		call	add_16		;READCHAN[9:8] + 760

		call	bindec16cz_cnv	;10進数 5桁の文字列に変換（ゼロサプレス)

		movlw	h'c0'		;２行目表示
		btfsc	spec_flg,0	;RSSIデータ収集フラグ = ON か？
		movlw	h'80'		;Yes. １行目表示
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力

		movlw	buff + 1	;変換文字列の格納エリア
		movwf	FSR		;間接アドレスの設定
		movlw	4
		movwf	lpcnt		;ループカウンタ

chfrcv03	decf	lpcnt,W
		movlw	'.'
		btfsc	STATUS,Z	;lpcnt - 1 = 0 か？
		call	I2cLcd_Data	;Yes. I2C LCD へ '.' を１文字出力

		movf	INDF,W
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		incf	FSR,F		;間接アドレスの更新

		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	chfrcv03	;No

		movlw	high (fm_radio_n_tbl + 14) ;先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low (fm_radio_n_tbl + 14)  ;先頭アドレスの low アドレス
		movwf	addr_low	;
		call	I2cLcd_Disp	;メッセージデータ "MHz" 表示

		return

;--------------------------------------------------------------------------
;		（RDA5807M FM Tuner）の 指定 RDA_Reg へ書き込み
;--------------------------------------------------------------------------

;		入力 W_reg:	RDA_Reg 番号

rda_reg_write
		movwf	work1		;RDA_Reg 番号を退避
		movlw	rda_reg_h	;書き込みデータの格納アドレス
		movwf	FSR		;間接アドレスに設定

		call	i2c_start	;スタートコンディション
		movlw	h'22'		;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		movf	work1,W		;RDA_Reg 番号
		call	byte_send	;1 バイト送信

		movf	INDF,W		;書き込みデータの high バイト
		call	byte_send	;1 バイト送信
		incf	FSR,F		;間接アドレスの更新
		movf	INDF,W		;書き込みデータの low バイト
		call	byte_send	;1 バイト送信

		call	i2c_stop	;ストップコンディション
		return

;--------------------------------------------------------------------------
;		（RDA5807M FM Tuner）の 指定 RDA_Reg から読み出し
;--------------------------------------------------------------------------

;		入力 W_reg:	RDA_Reg 番号

rda_reg_read
		movwf	work1		;RDA_Reg 番号を退避
;		movlw	reg_0a_h	;読み出データの格納アドレス
;		movwf	FSR		;間接アドレスに設定

		call	i2c_start	;スタートコンディション
		movlw	h'22'		;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		movf	work1,W		;RDA_Reg 番号
		call	byte_send	;1 バイト送信

		call	i2c_start	;再スタートコンディション
		movlw	h'22' + 1	;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		call	byte_reciv	;データ 1 バイト受信
		call	ack		;ACK
		movwf	INDF
		incf	FSR,F		;間接アドレスの更新
		call	byte_reciv	;データ 1 バイト受信
		call	nack		;NACK
		movwf	INDF

		call	i2c_stop	;ストップコンディション
		return

;==========================================================================
;	FMスペクトラム作成のための RSSIデータ収集（Ver. 1.06 で追加）
;==========================================================================

;		Frequency x 10  =  CHAN + 760 
;		CHAN		=  Frequency x 10 - 760

		org	h'0600'		;;;;; リンクエラーの回避策

spectrum
		movlw	b'00101110'
		xorwf	PORTA,W
		andlw	b'00101110'
		btfss	STATUS,Z	;すべてのスイッチ = OFF か？
		goto	spectrum	;No

		; 各変数の初期設定

		bsf	spec_flg,0	;RSSIデータ収集フラグ = ON

		clrf	chan_h		;CHAN[9:8] 初期値の設定
		clrf	chan_l		;CHAN[7:0]
		clrf	sel_sw		;sel_sw を無効にする

		clrf	rda_reg_h	;CHAN-H: Reg03H[15:8]=CHAN[9:2]
		movlw	h'18'		;TUNE=1, BAND[1：0]=10, SPACE[1：0]=00
		movwf	rda_reg_l	;CHAN-L: Reg03H[ 7:6]=CHAN[1:0]
		movlw	h'02'		;
		movwf	volume		;
		call	fm_tuner_set	;（RDA5807M FM Tuner）の設定
		call	wait_1sec
;;;;;		call	wait_1sec	;;;;; ↓ 下の(注). を参照 ↓

		; LCD への表示

spect01		call	wait_1sec	;;;;;wait_200ms
		call	chan_freq_conv	;(１行目表示) … 周波数の表示
		call	rssi_disp	;(２行目表示) … 受信強度の表示

		; 各スイッチの ON／OFF 状態のチェック

spect02		btfss	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		goto	spect01		;Yes. 再表示

		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		call	sp_tune_down	;Yes

		btfss	PORTA,_swTUP	;TUNING UP スイッチ = ON か？
		call	sp_tune_up	;Yes

		btfss	updt_flg,0	;更新フラグ = ON か？
		goto	spect02		;No

		; チャネルの変更 （ RDA_Reg = h'03' へ書き込み ）

		movf	chan_h,W	;CHAN[9:8]
		movwf	work2
		movf	chan_l,W	;CHAN[7:0]
		movwf	work1		; work2:0000.00xx, work1:xxxx.xxxx

		; 10 ビットデータを 右詰め → 左詰め に変換
		rrf	work2,F		;?000.000x x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;x?00.0000 x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;xx?0.0000 0 
		movlw	h'c0'
		andwf	work2,F		; work1:xxxx.xxxx, work2:xx00.0000

		movf	work1,W		;work1:xxxx.xxxx
		movwf	rda_reg_h	;書き込み high バイト
		movf	work2,W		;work2:xx00.0000
		iorlw	h'18'		;TUNE=1, BAND[1：0]=10, SPACE[1：0]=00
		movwf	rda_reg_l	;書き込み low バイト
		movlw	h'03'		;RDA_Reg 番号
		call	rda_reg_write	;RDA_Reg = h'03' へ書き込み

		bcf	updt_flg,0	;更新フラグ = OFF
		goto	spect01

;--------------------------------------------------------------------------

#ifdef comment

(注).	call wait_1sec を２行以上続けると、何故か下記のリンクエラーを起こす。１行ではＯＫ。
	ただし、アセンブルは正常に行われている。

	MPLAB IDE v8.92 を使用。

	'.org_3' can not fit the absolute section.  '.org_3'は絶対セクションに適合できない。

	？？？？？  '.org_3' の意味がよく分からないので、ここに記録をしておく。

	――――――――――

	その後、分かったこと　(１)

	命令の種類の問題ではなく命令数の問題で、現状より１以上を増加させると同エラーとなる。
	これでは今後のプログラムの更新ができない。どうすれば良いのか？？？

	次はアセンブルリストで表示される、エラー発生時（call wait_1sec が２行のとき）の
	メモリの使用状況等

	All other memory blocks unused.

	Program Memory Words Used:  1531	← 1530 を超えるとエラーになる！　？？？
	Program Memory Words Free:   517

	Errors   :     0
	Warnings :     0 reported,     0 suppressed
	Messages :     0 reported,    23 suppressed

	――――――――――

	その後、分かったこと　(２)

	MEMORY USAGE MAP ('X' = Used,  '-' = Unused)

	     :
	     :
	0580 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
	05C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX  ← 5FF を超えるとエラーになる
	2000 : -------X-------- ---------------- ---------------- ----------------
	2100 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXX--- ----------------

	この事象が起こるのは、リンカーのバグではないのか？？？？？

	本機で使用の PIC16F684 においては、
	今後、プログラムの更新をする場合には、（Ver. 1.06 で追加）をした spectrum 関連のルーチンを
	取り除いて、プログラムエリアを空けるしか方法がないのでは？？？
	現在、500 ワード以上も空きエリアがあるにもかかわらず！！

	――――――――――

	その後、分かったこと　(３)

	MEMORY USAGE MAP ('X' = Used,  '-' = Unused)

	     :
	     :
	0480 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
	04C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXX----  ← 5FF を超えて連続で
	0600 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX     600 以降に続かないようにする
	0640 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
	     :
	     :

	上のメモリマップのように、5FF を超えて連続で 600 以降に続かないようにすれば
	リンクエラーを回避することが可能なことが分かった。

	具体的に回避するプログラムを示すと、次のようにする。

		org	h'0600'		;;;;; リンクエラーの回避策

spectrum
		movlw	b'00101110'
		xorwf	PORTA,W
	     :
	     :

	以上から、結論は、やはりリンカーのバグだと思う。

	そして、この事象の回避策としては、5FF を超えるような大きなプログラムの場合には、
	5FF 番地のメモリを使用しないようにすることである。

	――――――――――
	――――――――――

	以下は、当初エラー発生時に MPLAB IDE v8.92 で表示されたメッセージの内容

----------------------------------------------------------------------
Debug build of project `D:\Project\PIC\FM_StereoRadioII\FM_StereoRadioII.mcp' started.
Language tool versions: MPASMWIN.exe v5.51, mplink.exe v4.49, mplib.exe v4.49
Preprocessor symbol `__DEBUG' is defined.
Sun Nov 08 02:46:05 2020
----------------------------------------------------------------------
Make: The target "D:\Project\PIC\FM_StereoRadioII\FM_StereoRadioII.o" is out of date.
Executing: "C:\Program Files (x86)\Microchip\MPASM Suite\MPASMWIN.exe" /q /p16F684 "FM_StereoRadioII.asm" /l"FM_StereoRadioII.lst" /e"FM_StereoRadioII.err" /d__DEBUG=1
Make: The target "D:\Project\PIC\FM_StereoRadioII\FM_StereoRadioII.cof" is out of date.
Executing: "C:\Program Files (x86)\Microchip\MPASM Suite\mplink.exe" /p16F684 "FM_StereoRadioII.o" /u_DEBUG /z__MPLAB_BUILD=1 /z__MPLAB_DEBUG=1 /o"FM_StereoRadioII.cof" /M"FM_StereoRadioII.map" /W /x
MPLINK 4.49, Linker
Device Database Version 1.14
Copyright (c) 1998-2011 Microchip Technology Inc.
Error - section '.org_3' can not fit the absolute section. Section '.org_3' start=0x00000008, length=0x00000bf2
Errors    : 1

Link step failed.
----------------------------------------------------------------------
Debug build of project `D:\Project\PIC\FM_StereoRadioII\FM_StereoRadioII.mcp' failed.
Language tool versions: MPASMWIN.exe v5.51, mplink.exe v4.49, mplib.exe v4.49
Preprocessor symbol `__DEBUG' is defined.
Sun Nov 08 02:46:06 2020
----------------------------------------------------------------------
BUILD FAILED

#endif

;--------------------------------------------------------------------------
;		TUNING DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

;			chan_h    chan_l
;			0000.00xx xxxx.xxxx

sp_tune_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		goto	tdwn01		;No

		bsf	updt_flg,0	;更新フラグ = ON

		decf	chan_l,F	;chan_l - 1
		incf	chan_l,W	;
		btfss	STATUS,Z	;W reg = 0 (chan_l < 0) か？
		goto	stdw01		;No

		decf	chan_h,F	;chan_h - 1
		incf	chan_h,W	;
		btfss	STATUS,Z	;W reg = 0 (chan_h < 0) か？
		goto	stdw01		;No

		movlw	high (1080 - 760) ;最大値
		movwf	chan_h
		movlw	low (1080 - 760)
		movwf	chan_l

stdw01		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		TUNING UP スイッチ = ON の処理
;--------------------------------------------------------------------------

;			chan_h    chan_l
;			0000.00xx xxxx.xxxx

sp_tune_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTUP	;TUNING DOWN スイッチ = ON か？
		goto	stup02		;No

		bsf	updt_flg,0	;更新フラグ = ON

		movlw	high (1080 - 760)
		subwf	chan_h,W
		btfsc	STATUS,C	;chan_h < high (1080 - 760) か？
		goto	stup01		;No

		incf	chan_l,F	;chan_l + 1
		btfss	STATUS,Z	;W reg = 0 (chan_l = 0) か？
		goto	stup02		;No

		incf	chan_h,F	;chan_h + 1
		goto	stup02		;No

stup01		incf	chan_l,F	;chan_l + 1
		movlw	low (1080 - 760 + 1)
		subwf	chan_l,W
		btfss	STATUS,C	;chan_l < low (1080 - 760 + 1)
		goto	stup02		;Yes

		clrf	chan_h		;最小値 = 0
		clrf	chan_l		;

stup02		btfss	PORTA,_swTUP	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;==========================================================================
;		ウェイト・ルーチン (8 MHz)
;==========================================================================

		; 100μ秒 ウェイトルーチン
wait_100us
		movlw	39		;1
		movwf	wt1cnt		;1
wu01		goto	$+1		;2
		decfsz	wt1cnt,F	;1,2
		goto	wu01		;2,0	1+1+(2+1+2)*38+(2+2+0)=196
;		goto	$+1		;2
		return			;2	196+2=198
					;Total	198/8MHz*4 = 99 μS = 100 μS = 0.1 mS

		; 30m 秒 ウェイトルーチン
wait_30ms
		movlw	30
		goto	wait_xxms

		; 100m 秒 ウェイトルーチン
wait_100ms
		movlw	100
		goto	wait_xxms

		; 200m 秒 ウェイトルーチン
wait_200ms
		movlw	200
		goto	wait_xxms

		; 1m 秒 ウェイトルーチン
wait_1ms
		movlw	1

		; xx m 秒（ 1m 秒 x W ）ウェイトルーチン
wait_xxms
		movwf	wt2cnt		;1

w1m01		movlw	221		;1
		movwf	wt1cnt		;1

w1m02		goto	$ + 1		;2
		goto	$ + 1		;2
		goto	$ + 1		;2
		decfsz	wt1cnt,F	;1,2
		goto	w1m02		;2,0	(2+2+2+1+2)*220+(2+2+2+2+0)=1988

		nop			;1
		goto	$ + 1		;2
		goto	$ + 1		;2
		decfsz	wt2cnt,F	;1,2
		goto	w1m01		;2,0	(1+1+1988+1+2+2+1+2)*(W-1)+(1+1+1988+1+2+2+2+0)=1998*W-1

		return			;2	1+(1998*W-1)+2=1998*W+2
					;Total	(1998*W+2)/8MHz*4 = (999*W + 1) μS

		; 1 秒 ウェイトルーチン
wait_1sec
		movlw	10
w1s01		movwf	wt3cnt

w1s02		call	wait_100ms
		decfsz	wt3cnt,F
		goto	w1s02
		return

;==========================================================================
;		割り込み処理
;==========================================================================

interrupt
		; レジスタの保存

		movwf	w_save		;W レジスタの保存
		movf	STATUS,W
		movwf	s_save		;STATUS レジスタの保存

		bcf	STATUS,RP0	;バンク 0
		btfsc	INTCON,T0IF	;タイマー０割り込み か？
		goto	int_t0		;Yes

		btfsc	INTCON,RAIF	;RAポート変化割り込み か？
		goto	int_ra		;Yes
		goto	int_end		;(Illegal interrupt)

		; タイマー０割り込み処理（ 100μ秒毎に割り込み ）

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	;ソフトタイマー０の high カウント値
		movwf	tm0_cnt_h	;ソフトタイマー０の high カウンタの設定
		movlw	low tm0_s_val	;ソフトタイマー０の low  カウント値
		movwf	tm0_cnt_l	;ソフトタイマー０の low  カウンタの設定

		movlw	60 - 2 + 1	;２秒経過の監視
		subwf	sec_cnt,W	;
		btfsc	STATUS,Z	;秒カウンタ = 60 - 1 か？
		bsf	updt_flg,2	;Yes. 音量表示の終了フラグ = ON

		movlw	60 - 4 + 1	;４秒経過の監視
		subwf	sec_cnt,W	;
		btfsc	STATUS,Z	;秒カウンタ = 60 - 3 か？
		bsf	updt_flg,3	;Yes. 受信強度表示の終了フラグ = ON

		movlw	60 - 6 + 1	;６秒経過の監視
		subwf	sec_cnt,W	;
		btfss	STATUS,Z	;秒カウンタ = 60 - 5 か？
		goto	int02		;No

		btfss	updt_flg,6	;バックアップ抑止フラグ = ON か？
		bsf	updt_flg,4	;No. バックアップ要求フラグ = ON
		btfsc	updt_flg,6	;バックアップ抑止フラグ = ON か？
		bcf	updt_flg,6	;Yes. バックアップ抑止フラグ = OFF

int02		decfsz	sec_cnt,F	;秒カウンタ - 1 = 0 か？
		goto	int_end		;No

		bsf	updt_flg,5	;スリープ移行フラグ = ON
		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		goto	int_end

		; RA ポート変化割り込み処理

int_ra		bcf	INTCON,RAIF	;RAポート変化割り込みフラグをクリア
		bcf	INTCON,RAIE	;RAポート変化割り込みを禁止
;		goto	int_end

		; レジスタの復帰

int_end		movf	s_save,W
		movwf	STATUS		;STATUS レジスタの復帰
		swapf	w_save,F
		swapf	w_save,W	;W レジスタの復帰

		retfie

;==========================================================================
;    （ 以下の add_16, bindec16cz_cnv, bindec8cz_cnv については
;	サイト "ELECTRONICS SHELVES" の "サブルーチン集" から
;	add16_mp.txt, hexdec16_mp.txt, hexdec8_mp.txt を
;       拝借して、編集し直しと改変をしたもの 
;	original: http://orange.zero.jp/electronics/pic/pic.html ）
;==========================================================================

		; １６＋１６＝１６ビットの加算

;	(dat_a1,dat_a2) + (dat_b1,dat_b2) = (dat_a1,dat_a2) 

; 使用変数:	dat_a1			;引き数Ａ(上位) ／ 結果(上位)
;		dat_a2			;引き数Ａ(下位) ／ 結果(下位)
;		dat_b1			;引き数Ｂ(上位)
;		dat_b2			;引き数Ｂ(下位)

add_16
		movf	dat_b2,W
		addwf	dat_a2,F
		btfsc	STATUS,C
		incf	dat_a1,F
		movf	dat_b1,W
		addwf	dat_a1,F

		return

;--------------------------------------------------------------------------

		; 16 ビット数値を 10進数 5桁の文字列に変換（ゼロサプレス)

		;	(dat_a1,dat_a2): 変換する 16 ビット数値
		;	work2: ワーク

bindec16cz_cnv
		movlw	buff + 4 + 1	;変換文字列の格納エリア
		movwf	FSR		;間接アドレスの設定
		call	bd16c		;(04) 最下位変換
		call	bd16c		;(03)
		call	bd16c		;(02)
		call	bd16c		;(01)
		call	bd16c		;(00) 最上位変換

		movlw	4		;
		movwf	lpcnt		;ループカウンタに設定

bd16cv01	movf	INDF,W
		sublw	h'30'
		btfss	STATUS,Z	;(INDF) = h'30' か？
		goto	bd16cv02	;No

		movlw	' '
		movwf	INDF		;
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd16cv01	;No

bd16cv02	return

		; ÷10（10で除算）
bd16c
		movlw	16		;16ビット繰り返し
		movwf	lpcnt		;ループカウンタに設定
		clrf	work2
bd16c01
		bcf	STATUS,C	;キャリフラグのクリア
		rlf	dat_a2,F
		rlf	dat_a1,F
		rlf	work2,F
		movlw	-10		;-10 = h'f6'
		addwf	work2,W
		btfsc	STATUS,C	;C = 0 か？
		movwf	work2		;No
		btfsc	STATUS,C	;C = 0 か？
		incf	dat_a2,F	;No
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd16c01		;No

		decf	FSR,F		;間接アドレスの更新
		movf	work2,W		;余り
		addlw	h'30'
		movwf	INDF		;文字コードに変換
		return

;--------------------------------------------------------------------------

		; 8 ビット数値を 10進数 3桁の文字列に変換（ゼロサプレス)

		;	work1: 変換する 8 ビット数値
		;	work2: ワーク

bindec8cz_cnv
		movlw	buff + 2 + 1	;変換文字列の格納エリア
		movwf	FSR		;間接アドレスの設定
		call	bd8c		;(02) 最下位変換
		call	bd8c		;(01)
		call	bd8c		;(00) 最上位変換

		movlw	2		;
		movwf	lpcnt		;ループカウンタに設定

bd8cv01		movf	INDF,W
		sublw	h'30'
		btfss	STATUS,Z	;(INDF) = h'30' か？
		goto	bd8cv02		;No

		movlw	' '
		movwf	INDF		;
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd8cv01		;No

bd8cv02		return

		; ÷10（10で除算）
bd8c
		movlw	8		;8ビット繰り返し
		movwf	lpcnt		;シフトカウンタに設定
		clrf	work2		;一時余り格納場所のクリア

bd8c01		bcf	STATUS,C	;キャリーフラグのクリア
		rlf	work1,F
		rlf	work2,F

		movlw	-10		;-10 = h'f6'
		addwf	work2,W
		btfsc	STATUS,C	;C = 0 か？
		movwf	work2		;No
		btfsc	STATUS,C	;C = 0 か？
		incf	work1,F		;No
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd8c01		;No

		decf	FSR,F		;間接アドレスの更新
		movf	work2,W		;余り
		addlw	h'30'
		movwf	INDF		;文字コードに変換
		return

;==========================================================================
;		EEPROM エリア
;==========================================================================

		org	h'2100'

eep_start	de	"Copyright (C) 2020-2021 by M.Yamamoto"

		de	h'ff'

		; セーブ情報（ラジオ局番号、音量）の格納位置

save_data				;以下の設定値は初回の起動時のみ

		de	3		;ラジオ局番号（default=3:NHK FM） … （ 名古屋周辺地区 ）
		de	h'00'		;dummy
		de	h'02'		;音量

		de	4		;ラジオ局番号（default=4:NHK FM） … （ 東京周辺地区 ）
		de	h'00'		;dummy
		de	h'02'		;音量

		de	3		;ラジオ局番号（default=3:NHK FM） … （ 大阪周辺地区 ）
		de	h'00'		;dummy
		de	h'02'		;音量

		de	h'00'		;CHAN-H: Reg03H[15:8]=CHAN[9:2]   … （ SEEK機能で選局時 ）
		de	h'02'		;CHAN-L: Reg03H[ 7:6]=CHAN[1:0] + Reg02H[9]=SEEKUP
		de	h'02'		;音量

;==========================================================================

		end			;プログラムの終了
