; Tab width = 4

;----------------------------------------------------------------------------
; ** シリアル通信サンプル3 (割り込み駆動受信バッファ付きシリアルドライバ) **
;
; ・16byte受信バッファ付きシリアル送受信通信ドライバと利用サンプル
; ・受信データを数値表示する
; ・一定期間ごとに単純なデータを送信する
;----------------------------------------------------------------------------
; 1.01 990208 SEGA Enterprises,LTD.
;----------------------------------------------------------------------------

chip	LC868700			; チップの種類をアセンブラに指定
world	external			; 外部メモリ用プログラム

public	main				; ghead.asm から参照されるシンボル
public	int_BaseTimer		; ghead.asm から参照されるシンボル
public	int_SioRx			; ghead.asm から参照されるシンボル

extern	_game_end			; アプリケーション終了


; **** システム定数の定義 ***************************************************

								; OCR(発振制御レジスタ)設定値
osc_rc			equ 04dh		;	システムクロックに内蔵RC発振を指定
osc_xt			equ 0efh		;	システムクロックに水晶発振を指定

LowBattChk		equ 06eh		; ローバッテリー検出フラグ(RAMバンク0)

SioRxCueSize	equ 16			; シリアル受信バッファサイズ


; *** データセグメント ******************************************************

		dseg				; データセグメント開始

r0:		ds		1			; 間接アドレッシングレジスタ r0
r1:		ds		1			; 間接アドレッシングレジスタ r1
r2:		ds		1			; 間接アドレッシングレジスタ r2
r3:		ds		1			; 間接アドレッシングレジスタ r3
		ds		12			; その他のレジスタ

							; ** シリアルドライバ用 **
SioRxCueBehind:	ds	1		; 受信データ溜り量
SioRxCueRPnt:	ds	1		; 受信バッファ読み出しポイント
SioRxCueWPnt:	ds	1		; 受信バッファ書き込みポイント
SioRxCue:		ds	SioRxCueSize ; 受信バッファ
SioOverRun:		ds	1		; 受信オーバーランフラグ

							; ** 利用サンプル用ワーク **
bcount:	ds		1			; ベースクロックカウンタ
work1:	ds		1			; ワーク1
work2:	ds		1			; ワーク2

work0:	ds		1			; ワーク(put2digit)


; *** コードセグメント ******************************************************

		cseg				; コードセグメント開始
		
; *-------------------------------------------------------------------------*
; * シリアル通信ドライバ使用サンプル										*
; * 一定期間後とに単純なデータを送信する。									*
; * 受信したデータは順次LCDに数値表示する。									*
; *-------------------------------------------------------------------------*
main:
		mov		#0,bcount
		mov		#0,work1		; 送信データの初期値
		clr1	P3INT,0			; P3割り込みをマスク
		call	cls				; LCDを消去する
		call	BattChkOff		; ローバッテリー自動検出機能 OFF
		call	SioInit			; シリアル通信の初期化
		bnz		finish			; VMS が接続されていなければ終了

stlp1:
								; *** 受信データがあれば表示 ***
		call	SioGet1			; 1byte受信
		be		#0ffh,stnx1		; 受信データがなければスキップ		
		bz		stnx3			; 正常受信データありのときstnx3へ
error:	br		finish			; 各種エラーを検出したとき強制終了
stnx3:
		ld		b				; acc <- b 受信データ
		mov		#0,c			; 表示座標(水平)
		mov		#0,b			; 表示座標(垂直)
		call	put2digit		; LCDに数値表示
		br		stlp1			; 受信データがある限り優先して繰り返す
stnx1:							;

		set1	pcon,0			; 次の割り込みまで待つ

								; *** 一定期間毎に単純なデータ送信する ***
		ld		bcount			; ベースタイマのカウンタ値
		be		work2,stnx4		; 前回と同じならば送信しない
		st		work2			; work2を更新
							
		ld		work1			; 送信用データを取り出す
		call	SioPut1			; 送信する

		inc		work1			; 送信用データの更新
		ld		work1			; (0〜99 の値を次々に送信する)
		bne		#100,stnx2		;
		mov		#0,work1		;
stnx2:							;
stnx4:
				
								; ** [M](モード)ボタンチェック **
		ld		P3
		bn		acc,6,finish	; [M]ボタンが押されていたらアプリ終了

		jmp		stlp1			; 繰り返す

finish:							; ** アプリケーション終了処理 **
		call	SioEnd			; シリアル通信終了処理
		call	BattChkOn		; ローバッテリー自動検出機能 ON
		jmp		_game_end		; アプリケーション終了

; *=========================================================================*
; ***** 簡易シリアル通信ドライバ										*****
; *=========================================================================*

; *-------------------------------------------------------------------------*
; * シリアル通信の初期化													*
; *																			*
; * システムクロックが水晶モードであることを前提としている。				*
; *-------------------------------------------------------------------------*
SioInit:
									; **** VMS同士の接続を確認 ****
		ld		P7					; 接続状態の確認
		and		#%00001101			; P70, P72, P73 をチェック
		be		#%00001000,next3	; P70=0, P72=0, P73=1 か判定し
									; 接続されているときnext3へ
		mov		#0ffh,acc			; 接続されていないとき acc=0ffh で異常終了
		ret							; SioInit終わり
next3:

									; **** シリアル通信の初期化 ****
		mov		#0,SCON0			; LSBを先頭に出力するよう指定
		mov		#0,SCON1			; LSBを先頭に入力するよう指定
		mov		#088h,SBR			; 転送レートを設定する
		clr1	P1,0				; P10ラッチをクリア(P10/SO0)
		clr1	P1,2				; P12ラッチをクリア(P12/SCK0)
		clr1	P1,3				; P13ラッチをクリア(P13/SO1)

		mov		#%00000101,P1FCR	; 端子機能設定
		mov		#%00000101,P1DDR	; 端子機能設定

		mov		#0,SBUF0			; 転送バッファクリア
		mov		#0,SBUF1			; 転送バッファクリア

		mov		#0,acc
		st		SioRxCueBehind		; 受信データ溜り量リセット
		st		SioRxCueRPnt		; 受信バッファ読み出しポイント
		st		SioRxCueWPnt		; 受信バッファ書き込みポイント
		st		SioOverRun			; 受信オーバーランフラグのリセット

		set1	SCON1,0				; 受信側転送終了割り込み有効
		set1	SCON1,3				; 受信待機
		
		ret							; SioInit終わり


; *-------------------------------------------------------------------------*
; * シリアル通信の終了														*
; *-------------------------------------------------------------------------*
SioEnd:								; **** シリアル通信終了処理 ****

		mov		#0,SCON0			; SCON0 = 0
		mov		#0,	SCON1			; SCON1 = 0
		mov		#0bfh,P1FCR			; P1FCR = 0bfh
		mov		#0a4h,P1DDR			; P1DDR = 0a4h

		ret							; SioEnd終わり


; *-------------------------------------------------------------------------*
; * 1byte送信する															*
; *																			*
; * 入力 acc: 送信データ													*
; *-------------------------------------------------------------------------*
SioPut1:
		push	acc					; 送信データを退避
splp1:	ld		SCON0				; 転送中なら転送終了まで待つ
		bp		acc,3,splp1 		;
		pop		acc					; 送信データ復帰
		
		st		SBUF0				; 転送するデータをセットする
		set1	SCON0,3				; 転送開始

		ret							; SioPut1終わり


; *-------------------------------------------------------------------------*
; * 受信バッファから1byte読み込む(非同期受信)								*
; *																			*
; * 出力 acc: 0=正常終了													*
; *           0ffh=受信データなし											*
; *           0feh=バッファ・オーバーフロー									*
; *           0fdh=オーバーラン・エラー										*
; *        b: 受信したデータ(正常終了時のみ有効)							*
; *-------------------------------------------------------------------------*
SioGet1:
									; ** 溜まっているデータ量をチェック **
		ld		SioRxCueBehind		; 溜まり量
		bnz		sgnx1				; 溜まり量 != 0 のとき
		mov		#0ffh,acc			; 溜まり量 == 0 のとき..
		ret							;   acc = 0ffh で帰る (受信データなし)
sgnx1:
									; ** バッファオーバーフローの検出 **
									; SioRxCueBehind - SioRxCueSize
		be		#SioRxCueSize,sgnx3	; SioRxCueBehind == SioRxCueSize
		bp		PSW,7,sgnx3			; SioRxCueBehind < SioRxCueSize
									; SioRxCueBehind >  SioRxCueSize
		mov		#0feh,acc			; バッファ量を越えてしまったとき
		ret							;   acc = 0feh で帰る (Buffer overflow)
sgnx3:
									; ** オーバーランエラーの検出 **
		ld		SioOverRun			; オーバーランフラグ
		bz		sgnx4				; 検出されていないとき
		mov		#0fdh,acc			; 検出されているとき
		ret							;   acc = 0fdh で帰る (Overrun error)
sgnx4:
		
		dec		SioRxCueBehind		; dec 溜まり量
		
									; ** 受信データ取り出しポイントの計算
		ld		SioRxCueRPnt		; r0 = SioRxCue + SioRxCueRPnt
		add		#SioRxCue			;
		st		r0					;
		
		inc		SioRxCueRPnt		; inc データ取り出しポイント
		
									; ** 取り出しポイント==バッファサイズ なら
									; ** 取り出しポイント=0にリセットする
		ld		SioRxCueRPnt
		bne		#SioRxCueSize,sgnx2	; SioRxCueRPnt != SioRxCueSize のとき
		mov		#0,SioRxCueRPnt		; SioRxCueRPnt == SioRxCueSize のとき
sgnx2:

		ld		@r0					; 入力データをaccに取り込み..
		st		b					; bへ
		mov		#0,acc				; acc = 0 (正常終了 - データあり)
		
		ret							; SioGet1終わり


; *-------------------------------------------------------------------------*
; * 受信バッファから1byte読み込む											*
; * (受信データが無い場合はデータがくるまで待つ)							*
; *																			*
; * 出力 acc: 0=正常終了													*
; *           0feh=バッファ・オーバーフロー									*
; *           0fdh=オーバーラン・エラー										*
; *        b: 受信したデータ(正常終了時のみ有効)							*
; *-------------------------------------------------------------------------*
SioGet1W:
		call	SioGet1				; 非同期受信
		be		#0ffh,SioGet1W		; データがくるまで待つ
		
		ret							; SioGet1W終わり


; *-------------------------------------------------------------------------*
; * 受信バッファに溜まっているデータ量を取得する							*
; *																			*
; * 出力 acc: データ量(bytes)												*
; *-------------------------------------------------------------------------*
SioGetRxLen:
		ld		SioRxCueBehind		; 溜まり量
		
		ret							; SioGetRxLen終わり


; *-------------------------------------------------------------------------*
; * SIO受信割り込みハンドラ													*
; *-------------------------------------------------------------------------*
int_SioRx:	
		push	acc					; 使用レジスタ退避
		push	PSW					;
		set1	PSW,1				; データRAMバンク1を選択
		push	r0					; レジスタ退避

									; ** 書き込みポイントの計算 **
		ld		SioRxCueWPnt		; r0 = SioRxCue + SioRxCueWPnt
		add		#SioRxCue			;
		st		r0					;
		
		ld		SBUF1				; 受信データを読み込む
		st		@r0					; バッファに書き込む

		inc		SioRxCueWPnt		; 書き込みポイント++

									; ** 書き込みポイントがバッファサイズに
									; ** 達したらポイントをリセット
		ld		SioRxCueWPnt		;
		bne		#SioRxCueSize,isnx1	;
		mov		#0,SioRxCueWPnt		;
isnx1:

		inc		SioRxCueBehind		; データ溜まり量++

		clr1	SCON1,1				; 転送終了フラグをリセット
		
									; ** オーバーランエラーのチェック **
		bn		SCON1,6,isnx2		; オーバーランしていないならisnx2
		mov		#1,SioOverRun		; オーバーランしている→フラグセット
		clr1	SCON1,6				; オーバーランフラグをリセット
isnx2:
		
		set1	SCON1,3				; 次の転送を開始

		pop		r0					; 使用レジスタ復帰
		pop		PSW					;
		pop		acc					;
		
		reti						; int_SioRx終わり


; *-------------------------------------------------------------------------*
; * 2桁の数値を表示する														*
; * 入力 acc : 数値															*
; *        c : 文字水平位置												  	*
; *        b : 文字垂直位置												  	*
; *-------------------------------------------------------------------------*
put2digit:
		push	b			; 座標データを退避
		push	c			;
		st		c			; 10の位と1の位の値をそれぞれ計算
		xor		a			; ( acc = acc/10, work0 = acc mod 10 )
		mov		#10,b		;
		div					;
		ld		b			;
		st		work0		; 1の位の計算結果をwork0に保存
		ld		c			;
		pop		c			; 座標値を(c.b)に復帰
		pop		b			; 
		push	b			; また退避
		push	c			;
		call	putch		; 10の位を表示
		ld		work0		; 1の位の値を読み込み
		pop		c			; 座標値を(c,b)に復帰
		pop		b			; 
		inc		c			; 表示座標を右へ
		call	putch		; 1の位を表示

		ret					; put2digit終わり
		

; *-------------------------------------------------------------------------*
; * LCD表示イメージを消去する												*
; *-------------------------------------------------------------------------*
cls:
		push	OCR			; OCR値を退避
		mov		#osc_rc,OCR ; システムクロックを指定

		mov		#0,XBNK		; 表示用RAMのバンクアドレスを指定(BANK0)
		call	cls_s		; そのバンク内のデータをクリア

		mov		#1,XBNK		; 表示用RAMのバンクアドレスを指定(BANK1)
		call	cls_s		; そのバンク内のデータをクリア
		pop		OCR			; OCR値を復帰
		
		ret					; cls終わり
		
cls_s:						; *** 表示用RAM 1BANK分の消去 ***
		mov		#80h,r2		; 間接アドレッシングレジスタを表示用RAMの先頭に
		mov		#80h,b 		; ループカウンタbにループ数をセット
loop3:
		mov		#0,@r2		; アドレスをインクリメントしながら0を書き込む
		inc		r2			;
		dbnz	b,loop3		; bが0になるまで繰り返す
		
		ret					; cls_s終わり
		

; *-------------------------------------------------------------------------*
; * 指定位置に1キャラクタ表示する											*
; * 入力 acc : キャラクタコード												*
; *        c : 文字水平位置												  	*
; *        b : 文字垂直位置												  	*
; *-------------------------------------------------------------------------*
putch:
		push	XBNK
		push	acc
		call	locate		; 座標から表示RAMのアドレスを計算
		pop		acc
		call	put_chara	; 1キャラクタ表示する
		pop		XBNK
		
		ret					; putch終わり
		

locate:	; **** 表示位置指定から表示用RAMのアドレスを計算 ****
		; ** 入力 c: 水平位置(0〜5) b: 垂直位置(0〜3)
		; ** 出力 r2: RAMアドレス XBNK: 表示用RAMバンク

							; *** 表示用RAMバンクアドレスの判断 ***
		ld		b			; b>=2 のとき next1 へ
		sub		#2			;
		bn		PSW,7,next1	;
		
		mov		#00h,XBNK	; 表示用RAMのバンクアドレスを指定(BANK0)
		br		next2
next1:
		st		b
		mov		#01h,XBNK	; 表示用RAMのバンクアドレスを指定(BANK1)
next2:

							; *** 表示指定位置のRAMアドレス計算 ***
		ld		b			; b * 40h + c + 80h
		rol					;
		rol					;
		rol					;
		rol					;
		rol					;
		rol					;
		add		c			;
		add		#80h		;
		st		r2			; RAMアドレスをr2に格納
				
		ret					; locate終わり


put_chara:
		push	PSW			; PSW値を退避
		set1	PSW,1		; データRAMバンク1を選択

							; *** キャラクタデータアドレスの計算 ***
		rol					; (TRH,TRL) = acc*8 + fontdata
		rol					;
		rol					;	
		add		#low(fontdata)	;
		st		TRL				;
		mov		#0,acc			;
		addc	#high(fontdata)	;
		st		TRH			;

		push	OCR			; OCR値を退避
		mov		#osc_rc,OCR ; システムクロックを指定

		mov		#0,b		; キャラクタデータ読み出し用オフセット値
		mov		#4,c		; ループカウンタ
loop1:
		ld		b			; 1ライン目の表示データを読み出す
		ldc					;
		inc		b			; 読み出しデータのオフセットを +1
		st		@r2			; 表示データを表示用RAMに転送
		ld		r2			; 表示用RAMアドレス +6
		add		#6			;
		st		r2			;

		ld		b			; 2ライン目の表示データを読み出す
		ldc					;
		inc		b			; 読み出しデータのオフセットを +1
		st		@r2			; 表示データを表示用RAMに転送
		ld		r2			; 表示用RAMアドレス +10
		add		#10			;
		st		r2			;
		
		dec		c			; ループカウンタのデクリメント
		ld		c			; 
		bnz		loop1		; 8ライン分(4回)繰り返し
		
		pop		OCR			; OCR値を復帰
		pop		PSW			; PSW値を復帰

		ret					; put_chara終わり


; *-------------------------------------------------------------------------*
; * キャラクタのビットイメージデータ										*
; *-------------------------------------------------------------------------*
fontdata:
		db 07ch, 0e6h, 0c6h, 0c6h, 0c6h, 0ceh, 07ch, 000h	;0
		db 018h, 038h, 018h, 018h, 018h, 018h, 03ch, 000h	;1
		db 07ch, 0c6h, 0c6h, 00ch, 038h, 060h, 0feh, 000h	;2
		db 07ch, 0e6h, 006h, 01ch, 006h, 0e6h, 07ch, 000h	;3
		db 00ch, 01ch, 03ch, 06ch, 0cch, 0feh, 00ch, 000h	;4
		db 0feh, 0c0h, 0fch, 006h, 006h, 0c6h, 07ch, 000h	;5
		db 01ch, 030h, 060h, 0fch, 0c6h, 0c6h, 07ch, 000h	;6
		db 0feh, 0c6h, 004h, 00ch, 018h, 018h, 038h, 000h	;7
		db 07ch, 0c6h, 0c6h, 07ch, 0c6h, 0c6h, 07ch, 000h	;8
		db 07ch, 0c6h, 0c6h, 07eh, 006h, 00ch, 078h, 000h	;9
          

; *-------------------------------------------------------------------------*
; *	ローバッテリー自動検出機能 ON											*
; *-------------------------------------------------------------------------*
BattChkOn:
		push	PSW					; PSW値を退避
		clr1	PSW,1				; データRAMバンク0を選択
		
		mov		#0,LowBattChk		; ローバッテリー検出をする(0)

		pop		PSW					; PSW値を復帰
		ret							; BattChkOn終わり


; *-------------------------------------------------------------------------*
; *	ローバッテリー自動検出機能 OFF											*
; *-------------------------------------------------------------------------*
BattChkOff:
		push	PSW					; PSW値を退避
		clr1	PSW,1				; データRAMバンク0を選択
		
		mov		#0ffh,LowBattChk	; ローバッテリー検出をしない(0ffh)

		pop		PSW					; PSW値を復帰
		ret							; BattChkOff終わり


; *-------------------------------------------------------------------------*
; *	ベースタイマ割り込みハンドラ											*
; *-------------------------------------------------------------------------*
int_BaseTimer:
		clr1	btcr,1				; ベースタイマ割り込み要因クリア
		inc		bcount				; カウンタ++
		ret							; ユーザー側割り込み処理終了


