;
; X68000のキーボードからキーコードを読み取り
; LCDに表示してみる
;
;                                      2001/11/16 作成
;                                      2001/11/21 更新
;
; 秋月アセンブラ用



;              ----__----
;       RA2 1-|*         |-18 RA1
;       RA3 2-|          |-17 RA0
; RA4 TOCKI 3-|          |-16 OSC1 CLKIN
;     /MCLR 4-|          |-15 OSC2 CLKOUT
;       Vss 5-| PIC16C84 |-14 Vdd
;   RB0 INT 6-|          |-13 RB7
;       RB1 7-|          |-12 RB6
;       RB2 8-|          |-11 RB5
;       RB3 9-|          |-10 RB4
;              ----------

;	I/Oポートの割り当て
; PORT A
LCD_E		equ		0	; RA0 - LCD E
LCD_RS		equ		1	; RA1 - LCD RS
X68_READY	equ		2	; RA2 - X68 KY READY
;						  RA3 - 未使用 (入力に設定し、GNDへ接続)
;						  RA4 - 未使用 (入力に設定し、GNDへ接続)

; PORT B
PC_CLK		equ		0	; RB0 - PS/2 CLK  (外部にプルアップ抵抗をつける 2kくらい？)
PC_DATA		equ		1	; RB1 - PS/2 DATA (外部にプルアップ抵抗をつける 2kくらい？)
X68_RxD		equ		2	; RB2 - X68 KY RxD
X68_TxD		equ		3	; RB3 - X68 KY TxD
LCD_DB4		equ		4	; RB4 - LCD DB4
LCD_DB5		equ		5	; RB5 - LCD DB5
LCD_DB6		equ		6	; RB6 - LCD DB6
LCD_DB7		equ		7	; RB7 - LCD DB7

;	LCDの余ったピンの処理
;	LCD DB0  - GND
;	LCD DB1  - GND
;	LCD DB2  - GND
;	LCD DB3  - GND
;	LCD R/~W - GND 常時ライト


	.16c84				; デバイスの設定（省略できません）
	
	.osc	HS			; 発振タイプの設定
						; LP	ローパワー発振子（～約２００ｋＨｚ）
						; HS	高速クリスタル（約４ＭＨｚ～２０ＭＨｚ）
						; RC	ＲＣ発振
						; XT	クリスタル・セラロック（～約４ＭＨｚ）

	.wdt	off			; ウォッチドックタイマの有効・無効の設定
						; 省略時は“on”
						
	.pwrt	on			; パワーアップタイマの有効・無効の設定
	
	.protect	off		; プロテクトの設定
						; “on”にすると、その IC からプログラムを読み出す事が
						; 出来なくなる
						; 省略時は“off”

; -----------------------------------------------------------------------------

;	システムレジスタにラベル付け

TMR0		equ		01h		; タイマ(BANK0)
PCL			equ		02h		; プログラムカウンタの下位8ビット(BANK0)

STATUS		equ		03h		; ステータスレジスタ(BANK0)
C			equ		0		; STATUS,C   キャリーフラグ
DC			equ		1		; STATUS,DC  DCフラグ
Z			equ		2		; STATUS,Z   ゼロフラグ
PD			equ		3		; STATUS,PD  パワーダウンフラグ
TO			equ		4		; STATUS,TO  タイムアウトフラグ
RP0			equ		5		; STATUS,RP0 RAMのバンク切り替え

PORTA		equ		05h		; I/OポートA(BANK0)
PORTB		equ		06h		; I/OポートB(BANK0)

INTCON		equ		0bh		; 割り込み制御レジスタ(BANK0)
RBIF		equ		0		; RB<4:7>ポートチェンジ割り込みフラグ
INTF		equ		1		; INT(RA4)割り込みフラグ
T0IF		equ		2		; TMR0オーバーフロー割り込みフラグ
RBIE		equ		3		; RBIF割り込み許可
INTE		equ		4		; INTF割り込み許可
T0IE		equ		5		; TMR0割り込み許可
EEIE		equ		6		; データEEPROM書き込み終了割り込み許可
GIE			equ		7		; 全体割り込み許可

OPTION		equ		01h		; オプションレジスタ(BANK1)
RBPU		equ		7		; OPTION,RBPU ポートBのプルアップ 0:行う 1:行わない

TRISA		equ		05h		; ポートA出力/入力設定 0:出力 1:入力(BANK1)
TRISB		equ		06h		; ポートB出力/入力設定 0:出力 1:入力(BANK1)

;	定数のセット
;	PICの動作クロックに応じて書き換えてください

CONST_WAIT_MS	equ		200	; ms 単位のウェイト用定数
							; CONST_WAIT_MS = (動作クロック[MHz]/4)*100

CONST_COMSPEED	equ		155	; シリアル通信用割り込み間隔
							; CONST_COMSPEED = 255 - (osc[Hz] / bps / 32)+4?

; -----------------------------------------------------------------------------

;	変数を宣言
		org		0ch
temp1				ds		1	; 0ch から 1 バイト分のメモリを確保し、
								; ラベル“temp1”を付ける（変数temp1）汎用一時変数
temp2				ds		1	; 汎用一時変数
temp3				ds		1	; 汎用一時変数
temp4				ds		1	; 汎用一時変数
temp5				ds		1	; 汎用一時変数
lcd_temp1			ds		1	; LCD表示で使う変数
lcd_temp2			ds		1	; LCD表示で使う変数
lcd_temp3			ds		1	; LCD表示で使う変数
lcd_temp4			ds		1	; LCD表示で使う変数
wait_count1			ds		1	; ウェイトルーチンで使うカウンタ
wait_count2			ds		1	; ウェイトルーチンで使うカウンタ
int_w				ds		1	; 割り込み処理でのレジスタ退避用
int_status			ds		1	; 割り込み処理でのレジスタ退避用
x68txd				ds		1	; 送信バッファ
x68rxd				ds		1	; 受信バッファ
int_x68rmode		ds		1	; 受信処理内部状態
int_x68rcount		ds		1	; 受信割り込みカウンタ
int_x68tmode		ds		1	; 送信処理内部状態
int_x68tcount		ds		1	; 送信割り込みカウンタ
com_status			ds		1	; 通信ステータスフラグ
x68receiving		equ		0	; com_status,x68receiving		受信中
x68transmitrq		equ		1	; com_status,x68transmitrq		送信要求
x68rxdvalid			equ		2	; com_status,x68rxdvalid		受信データ有効

; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

;	リセット処理

		org		000h
		goto	start

; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

;	割り込み処理

		org		004h

interrupt

		movwf	int_w					; wレジスタを退避
		movf	STATUS,0
		movwf	int_status				; ステータスレジスタを退避
		movlw	CONST_COMSPEED
		movwf	TMR0					; TMR0プリセット



; -----------------------------------------------------------------------------

; x68キーボードとの通信処理
; 2400bps,8ビット,ストップビット1,パリティなし

; 受信すると x68rxd にデータが入り
; キーレディ信号 PORTA,X68_READY を 0
; データ有効フラグ com_status,x68rxdvalid を 1 にセットする

; 受信処理中は 受信処理中フラグ com_status,x68receiving が 1 になる

; プログラムで受信データを引き受けたら、
; キーレディ信号 PORTA,X68_READY を 1
; にすることで、次のデータの受信を行う
; (PORTA,X68_READY を 1 にしなければ、キーボードが次のデータを送信してこない)



;	受信処理

		movf	int_x68rmode,0			; 内部状態に応じて分岐
		addwf	PCL,1					; PCL=PCL+int_x68rmode
		goto	int_x68rmode0			; スタートビット検出処理
		goto	int_x68rmode1			; 本当にスタートビット？
		goto	int_x68rmode2_9			; データの受信1
		goto	int_x68rmode2_9			; データの受信2
		goto	int_x68rmode2_9			; データの受信3
		goto	int_x68rmode2_9			; データの受信4
		goto	int_x68rmode2_9			; データの受信5
		goto	int_x68rmode2_9			; データの受信6
		goto	int_x68rmode2_9			; データの受信7
		goto	int_x68rmode2_9			; データの受信8
		goto	int_x68rmode10			; 受信処理終わり


int_x68rmode0
		btfsc	PORTB,X68_RxD
		goto	int_x68rbreak			; スタートビットを確認せず 中断

;	X68_RxDが0 スタートビットがきた
		bsf		com_status,x68receiving	; 受信中フラグセット
		movlw	2
		movwf	int_x68rcount			; int_x68rcount=2
		incf	int_x68rmode,1			; int_x68rmode=int_x68rmode+1=1
		goto	int_x68rend


int_x68rmode1
		btfsc	PORTB,X68_RxD
		goto	int_x68rbreak			; スタートビットを確認せず 中断

;	スタートビットを確認した
		decf	int_x68rcount,1			; int_x68rcount=int_x68rcount-1
		btfss	STATUS,Z
		goto	int_x68rend				; int_x68rcountがゼロでなければ終了

;	3回続けてスタートビットを確認したので受信処理に入る
		movlw	4
		movwf	int_x68rcount			; int_x68rcount=4
		incf	int_x68rmode,1			; int_x68rmode=int_x68rmode+1=2
		bcf		com_status,x68rxdvalid	; 受信データ有効フラグクリア
		clrf	x68rxd					; 受信バッファクリア
		goto	int_x68rend


int_x68rmode2_9
		decf	int_x68rcount,1			; int_x68rcount=int_x68rcount-1
		btfss	STATUS,Z
		goto	int_x68rend				; int_x68rcountがゼロでなければ終了
;	実際の受信 (割り込みが4回発生するごとに1回実行される)
		rrf		x68rxd,1				; 受信バッファを右シフト
		btfsc	PORTB,X68_RxD
		bsf		x68rxd,7				; RxDが1なら受信バッファのbit7をセット
		movlw	4
		movwf	int_x68rcount			; int_x68rcount=4
		incf	int_x68rmode,1			; int_x68rmode=int_x68rmode+1
		goto	int_x68rend


int_x68rmode10
		decf	int_x68rcount,1			; int_x68rcount=int_x68rcount-1
		btfss	STATUS,Z
		goto	int_x68rend				; int_x68rcountがゼロでなければ終了

		btfss	PORTB,X68_RxD
		goto	int_x68rbreak			; ストップビットを確認せず 中断

;	受信正常終了
		bcf		PORTA,X68_READY			; 
		bsf		com_status,x68rxdvalid	; 受信データ有効フラグセット

;	受信異常終了
int_x68rbreak
		bcf		com_status,x68receiving	; 受信中処理フラグクリア
		clrf	int_x68rmode
		clrf	int_x68rcount
int_x68rend



; -----------------------------------------------------------------------------

;	送信処理
; x68txd にデータをセットして
; 送信要求フラグ com_status,x68transmitrq を 1 にすると
; 送信が開始される

; 送信が終了すると com_status,x68transmitrq が 0 になる

; 送信を開始する前に com_status,x68transmitrq が 0 であることを確認する必要あり



		movf	int_x68tmode,0			; 内部状態に応じて分岐
		addwf	PCL,1					; PCL=PCL+int_x68tmode
		goto	int_x68tmode0			; スタートビットを送信
		goto	int_x68tmode1_8			; 1ビット目を送信
		goto	int_x68tmode1_8			; 2ビット目を送信
		goto	int_x68tmode1_8			; 3ビット目を送信
		goto	int_x68tmode1_8			; 4ビット目を送信
		goto	int_x68tmode1_8			; 5ビット目を送信
		goto	int_x68tmode1_8			; 6ビット目を送信
		goto	int_x68tmode1_8			; 7ビット目を送信
		goto	int_x68tmode1_8			; 8ビット目を送信
		goto	int_x68tmode9			; ストップビットを送信
		goto	int_x68tmode10			; 送信おわり


int_x68tmode0
		btfss	com_status,x68transmitrq
		goto	int_x68tend				; 送信要求フラグが0なら終了

		movlw	4
		movwf	int_x68tcount			; int_x68tcount=4
		incf	int_x68tmode,1			; int_x68tmode=int_x68tmode+1=1
		bcf		PORTB,X68_TxD			; スタートビット(0)を送信
		goto	int_x68tend


int_x68tmode1_8
		decf	int_x68tcount,1			; int_x68tcount=int_x68tcount-1
		btfss	STATUS,Z
		goto	int_x68tend				; int_x68rcountがゼロでなければ終了

;	受信バッファの下位ビットから順に送信する
		btfsc	x68txd,0
		goto	intx68tmode1_8_1
intx68tmode1_8_0
		bcf		PORTB,X68_TxD			;送信バッファのbit0が0なら0を送信
		goto	intx68tmode1_8end
intx68tmode1_8_1
		bsf		PORTB,X68_TxD			;送信バッファのbit0が1なら1を送信

intx68tmode1_8end
		rrf		x68txd,1				;送信バッファを右シフト
		movlw	4
		movwf	int_x68tcount			; int_x68tcount=4
		incf	int_x68tmode,1			; int_x68tmode=int_x68tmode+1
		goto	int_x68tend


int_x68tmode9
		decf	int_x68tcount,1			; int_x68tcount=int_x68tcount-1
		btfss	STATUS,Z
		goto	int_x68tend				; int_x68rcountがゼロでなければ終了

		bsf		PORTB,X68_TxD			; ストップビット(1)を送信
		movlw	4
		movwf	int_x68tcount			; int_x68tcount=4
		incf	int_x68tmode,1			; int_x68tmode=int_x68tmode+1
		goto	int_x68tend


int_x68tmode10
		decf	int_x68tcount,1			; int_x68tcount=int_x68tcount-1
		btfss	STATUS,Z
		goto	int_x68tend				; int_x68rcountがゼロでなければ終了

		clrf	int_x68tcount
		clrf	int_x68tmode
		bcf		com_status,x68transmitrq	; 送信が終わりましたの印

int_x68tend



; -----------------------------------------------------------------------------

;	割り込み処理の終了

int_return
		bcf		INTCON,T0IF				; TMR0オーバーフロー割り込みフラグをクリア
		movf	int_status,0
		movwf	STATUS					; ステータスレジスタを元に戻す
		movf	int_w,0					; wレジスタを元に戻す

;		goto	interrupt

		retfie							; 割り込みを許可してリターン





; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

;	初期設定いろいろ

start
		bsf		STATUS,RP0	; bank1

		movlw	11000000b			; プリスケーラを使用、分周比1:2
		;		|||||||+----- PS0		-+
		;		||||||+------ PS1		 +- プリスケーラ分周比
		;		|||||+------- PS2		-+
		;		||||+-------- PSA		プリスケーラ設定		0:TMR0	1:WDT
		;		|||+--------- RTE		TMR0信号のエッジ		0:↑	1:↓
		;		||+---------- RTS		TMR0信号のソース		0:内部	1:外部
		;		|+----------- INTEDG	INT 割り込みのエッジ	0:↓	1:↑
		;		+------------ /RBPU		ポートBのプルアップ		0:行う	1:行わない
		movwf	OPTION

;	I/Oポートの設定
;	ポートA
		movlw	11111000b	; w=11111000b
		;		   ||||+----- LCD_E			out
		;		   |||+------ LCD_RS		out
		;		   ||+------- X68_READY		out
		;		   |+-------- RA3 - 未使用	in
		;		   +--------- RA4 - 未使用	in
		movwf	TRISA		; 設定レジスタに書き込み

;	ポートB
		movlw	00000100b	;
		;		|||||||+----- PC_DATA		out
		;		||||||+------ PC_CLK		out
		;		|||||+------- X68_RxD		in
		;		||||+-------- X68_TxD		out
		;		|||+--------- LCD_DB4		out
		;		||+---------- LCD_DB5		out
		;		|+----------- LCD_DB6		out
		;		+------------ LCD_DB7		out
		movwf	TRISB		; 設定レジスタに書き込み

		bcf		STATUS,RP0	; bank0

;	X68キーボードデータ送出禁止
		bcf		PORTA,X68_READY

;	X68キーボードへの送信ポートを1にしておく
		bsf		PORTB,X68_TxD

;	変数の初期化

		clrf	int_x68rmode
		clrf	int_x68rcount
		clrf	int_x68tcount
		clrf	int_x68tmode
		clrf	com_status



;		goto	interrupt


;	LCDの初期化

		movlw	15
		call	wait			; 15ms 待つ

		bcf		PORTA,LCD_RS	;  RS DB7 DB6 DB5 DB4
		movlw	00110000b		;  0   0   0   1   1
		call	lcd_write4

		movlw	5
		call	wait			; 5ms 待つ

		movlw	00110000b
		call	lcd_write4

		movlw	1
		call	wait			; 1ms 待つ

		movlw	00110000b
		call	lcd_write4

		movlw	00100000b		;  DB7 DB6 DB5 DB4
		call	lcd_write4		;   0   0   1   0
								; 4ビットモードに設定

		movlw	00101000b		; 4bit, 1/16duty, 5x7
		call	lcd_write

		movlw	00000001b		; 表示クリア
		call	lcd_write

		movlw	2				; クリアには少し時間がかかるので
		call	wait			; 2ms 待つ

		movlw	00000110b		; エントリーモードセット
		call	lcd_write		; （カーソルの進み方の設定）

		movlw	00001110b		; 表示オン／オフコントロール
		call	lcd_write		; 表示オン, カーソル表示あり, ブリンクなし





; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

;	割り込み許可
		movlw	10100000b
		movwf	INTCON			; TMR0割り込み許可

;	X68キーボードデータ送出許可
		bsf		PORTA,X68_READY





; -----------------------------------------------------------------------------

		movlw	255
		call	wait

dadada
		btfsc	com_status,x68transmitrq
		goto	dadada

		movlw	10000000b		; LED全部点灯
		movwf	x68txd
		bsf		com_status,x68transmitrq


loop

		btfsc	PORTA,X68_READY
		goto	loop			; データを受信するまで何もしない

		movf	x68rxd,0		; 何か受信したら
		call	disp_h			; キーコード表示

		bsf		PORTA,X68_READY	; X68キーボードデータ送出許可

		goto	loop





; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

; wレジスタの内容を16進表記で表示する


; 例
;	1:	movlw		0ah		; w=0ah
;	2:	call	disp_h
; と実行するとLCDに
;	+----------------+
;	|0A_             |
;	|                |
;	+----------------+ と表示する

disp_h
		clrf	lcd_temp4		; lcd_temp5=0
		movwf	lcd_temp3		; lcd_temp3=w

		bsf		PORTA,LCD_RS	; RS を 1

disp_h_loop
		swapf	lcd_temp3,0

		andlw	00001111b
		movwf	lcd_temp1		; lcd_temp1=w

		addlw	6				; w=w+6
		btfsc	STATUS,DC		; 0fhを超えた（w=9以下）
		goto	disp_h_1

;	0～9の時
		movlw	030h			; w=030h
		addwf	lcd_temp1,0		; w=w+lcd_temp1
		call	lcd_write
		goto disp_h_2

;	a～hの時
disp_h_1
		movlw	037h			; w=037h
		addwf	lcd_temp1,0		; w=w+lcd_temp1
		call	lcd_write

disp_h_2
		btfsc	lcd_temp4,0		; lcd_temp4のbit0が 0以外ならreturn
		return					;

		decf	lcd_temp4,1		; lcd_temp4=lcd_temp4-1 (0-1=0ffh)
		swapf	lcd_temp3,1
		goto	disp_h_loop





; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

;	LCDへの書き込み
;	wレジスタの内容をLCDに書き込む


lcd_write
		movwf	lcd_temp1	; lcd_temp1=w
		call	lcd_write4	; 上位4ビットの書き込み

		swapf	lcd_temp1,0	; lcd_temp1 の上位4ビットと下位4ビットを入れ替えて
							; wレジスタに代入
		call	lcd_write4	; 下位4ビットの書き込み

		return


;
;	書き込みサブルーチンのサブルーチン
;	wレジスタの上位4ビットだけを書き込む

;	ポートBの下位4ビットは変化しない

lcd_write4
		andlw	11110000b	; w=w&11110000b
		movwf	lcd_temp2	; 書き込むデータの上位4ビットのみを退避

		movf	PORTB,0		; w=PORTB
		andlw	00001111b	; w=w&00001111b
							; ポートBの下位4ビットのみを取り出し

		iorwf	lcd_temp2,0	; w=w|lcd_temp2
		movwf	PORTB		; ボートBの下位4ビットとデータの上位4ビットを
							; 合成してポートBに出力

		bsf		PORTA,LCD_E	; 書き込み
		nop
		bcf		PORTA,LCD_E

		movlw	1
		call	wait		; 少し待つ

		return





; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------

;	wレジスタの値に応じて ms 単位のウェイトを入れる

;	使用変数
;		wait_count1,wait_count2

;	例、約2ms待つ
;	1:	movlw	2
;	2:	call	wait

;	メモ
;	使用クロックに応じて、定数 CONST_WAIT_MS を定義してください

;	CONST_WAIT_MS = (動作クロック[MHz]/4)*100

;	例、8Mhz時
;	CONST_WAIT_MS	equ	200

wait
		movwf	wait_count1		; wait_count1=w
wait_loop1
			movlw	CONST_WAIT_MS
			movwf	wait_count2		; wait_count2=CONST_WAIT_MS
wait_loop2
				nop
				nop
				nop
				nop
				nop
				nop
				nop
				decfsz	wait_count2,1	; wait_count2=wait_count2-1
				goto	wait_loop2		; if wait_count2!=0 goto wait_loop2
			decfsz	wait_count1,1	; wait_count1=wait_count1-1
			goto	wait_loop1		; if wait_count1!=0 goto wait_loop1
		return					; else return
