;;======================================================================;;
;;			GenHSI	 					;;
;;======================================================================;;
;;									;;
;; Program:		GenHSI -- S88 interface module. HSI protocol	;;
;; Code:		Paco Caņada					;;
;; Platform:		Microchip PIC16F628, 4 Mhz			;;
;; Date:		12.05.2006					;;
;; First release:	20.05.2006					;;
;; LastDate:		17.10.2007					;;
;;									;;
;;======================================================================;;

; S88, EDiTS feedback modules support (up to 496 inputs). Littfinski HSI-88 protocol.
;
; Processor PIC 16F628 running at 4MHz.
;
; Revisions:
;
; 12/05/2006	Start of writing code.
; 20/05/2006	First prototype. Only left line supported
; 22/05/2006	Right line supported
; 17/10/2007	Removed echo. Changed text strings


; ----- Definitions

#define		__VERNUM	0x01
#define		__VERREV	"B"
#define		__VERDAY	0x17
#define		__VERMONTH	0x10
#define		__VERYEAR	0x07


                list    p=16F628,r=hex

		errorlevel -302
		errorlevel -306

	        INCLUDE "P16F628.INC"

                __FUSES _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF  & _INTRC_OSC_NOCLKOUT


#define		RAMINI0		0x020		; 80 bytes
#define		RAMINI1		0x0A0		; 80 bytes
#define		RAMINI2		0x120		; 48 bytes
#define		RAMINT		0x070		; 16 bytes


; ----- Macros

SEL_BANK_0:	macro	
		bcf	STATUS,RP0		; macros for data access
		bcf	STATUS,RP1
		endm

SEL_BANK_1:	macro	
		bsf	STATUS,RP0
		bcf	STATUS,RP1
		endm

SEL_BANK_2:	macro	
		bcf	STATUS,RP0
		bsf	STATUS,RP1
		endm

SEL_BANK_3:	macro	
		bsf	STATUS,RP0
		bsf	STATUS,RP1
		endm

#define		SEL_IBANK_0	bcf	STATUS,IRP

#define		SEL_IBANK_1	bsf	STATUS,IRP

#define		DNOP		goto	$+1


; ----- Constant values

FXTAL		equ	D'4000000'		; Crystal frequency
BaudRate	equ	D'9600'			; Serial baud rate
	
HIGH_BAUD	equ	(((D'10'*FXTAL/(D'16'*BaudRate))+5)/D'10')-1		; BRGH = 1

; Pin definition. GenMotion hardware

						; PortA
;KB_DOUT	equ	0			; Keyb/Light DATA OUT, S88 Right RESET
;RS_CTS		equ	1			; RS232 CTS
;RS_TXD		equ	2			; RS232 TXD
;KB_CLK		equ	3			; Keyb/Light CLOCK
;KB_DIN		equ	4			; Keyb/Light DATA IN
;RS_RXD		equ	5			; RS232 RXD

						; PortB
;TXRX		equ	0			; MAX485 TXRX
;RXD		equ	1			; MAX485 RXD
;TXD		equ	2			; MAX485 TXD
;KB_LOAD	equ	3			; Keyb/Light LOAD
;S88_RESET	equ	4			; S88 RESET
;S88_LOAD	equ	5			; S88 LOAD
;S88_DIN	equ	6			; S88 DATA IN
;S88_CLK	equ	7			; S88 CLOCK

S88PORT		equ	PORTB			; S88 Left line
S88_RESET	equ	4			; S88 RESET
S88_LOAD	equ	5			; S88 LOAD
S88_DIN		equ	6			; S88 DATA IN
S88_CLK		equ	7			; S88 CLOCK

S88RPORT	equ	PORTA			; S88 Right line
S88R_RESET	equ	0			; S88 RESET
S88R_LOAD	equ	3			; S88 LOAD in PORTB
S88R_DIN	equ	4			; S88 DATA IN
S88R_CLK	equ	3			; S88 CLOCK

R4_PORT		equ	PORTA			; Pin definition RS232
CTS		equ	1

RA_TRIS     	equ     0xF4			; RA2, RA4..RA7 input
RB_TRIS     	equ     0x46			; RB6 input, RB1,RB2 serial port
RA_INI      	equ     0x06			; TXD, CTS high
RB_INI      	equ     0x00			; 

INTC_INI	equ	0xC0			; GIE, Peripheal interrupts
PIE1_INI	equ	0x20			; RX interrupt
OPTION_INI	equ	0x88			; Option register 
						; Port B Pull-up:       1   disabled
						; External int edge:    0   falling
						; Timer trigger source: 0   clock
						; Timer trigger edge:   0   rising
						; Prescaler assignment: 1   Watchdog
						; Prescaler value :   000   1:1

CHR_CR		equ	0x0D
CHR_LF		equ	0x0A


; ----- Variables

; --- Top on all banks Section

INT_W		equ	RAMINT+0x00		; Interrupt variables. Last RAM locations
INT_STAT	equ	RAMINT+0x01
INT_PCLATH	equ	RAMINT+0x02
INT_FSR		equ	RAMINT+0x03

EEDATA0		equ	RAMINT+0x04		; EEPROM shadow variables
EEADR0		equ	RAMINT+0x05

; --- Bank 0

MC_BUFRXINI	equ	RAMINI0+0x00		; Serial input buffer. Interrupt use. Do not change
;MC_BUFRXEND	equ	RAMINI0+0x1F

MC_RDRX		equ	RAMINI0+0x20		; Serial Buffer input pointers
MC_WRRX		equ	RAMINI0+0x21
MC_RDTX		equ	RAMINI0+0x22		; Serial Buffer output pointers
MC_WRTX		equ	RAMINI0+0x23

DUMMY		equ	RAMINI0+0x24		; Current serial received byte
TEMP		equ	RAMINI0+0x25

MC_COUNT	equ	RAMINI0+0x26
MC_HEADER	equ	RAMINI0+0x27		; 
MC_DATA1	equ	RAMINI0+0x28
MC_DATA2	equ	RAMINI0+0x29
MC_DATA3	equ	RAMINI0+0x2A
MC_DATA4	equ	RAMINI0+0x2B
MC_DATA5	equ	RAMINI0+0x2C		; 
MC_DATA6	equ	RAMINI0+0x2D

BIT_TBL		equ	RAMINI0+0x30		; Module changes 32 Bit table (0x30..0x33)

S88_LEFT	equ	RAMINI0+0x38		; number of S88 modules
S88_MIDDLE	equ	RAMINI0+0x39
S88_RIGHT	equ	RAMINI0+0x3A

FLAGS		equ	RAMINI0+0x3B		; General flags

COUNT		equ	RAMINI0+0x40
MODULE		equ	RAMINI0+0x41
MOD_ADDR	equ	RAMINI0+0x42
MSB		equ	RAMINI0+0x43
LSB		equ	RAMINI0+0x44
S88DATH		equ	RAMINI0+0x45		; current S88 data
S88DATL		equ	RAMINI0+0x46
S88COUNT	equ	RAMINI0+0x47

; --- Buffers

S88Buffer	equ	RAMINI1+0x00		; S88 buffer. 31 modules * 2 bytes

MC_BUFTXINI	equ	RAMINI2+0x00		; Serial output buffer
MC_BUFTXEND	equ	RAMINI2+0x2F


; --- Flags

#define		RIGHT_LINE	FLAGS,0		; Reading right line
#define		RCV_S_CMD	FLAGS,1		; Received first 's' command
#define		RXEMPTY		FLAGS,4		; Serial receive buffer empty
#define		TERMINAL_MODE	FLAGS,5		; Terminal mode

T_MODE_MSK	equ	0x20


; ------ Program Section

		org	0x000

PowerUp:
		clrf	STATUS			; ensure we are at bank 0
		clrf	PCLATH			; ensure page bits before goto !!
		clrf	INTCON			; disable all interrupts
		goto	Start


; --- Interrupt


Interrupt:
		movwf	INT_W			; 1 Save registers W, STATUS, PCLATH, FSR
		swapf	STATUS,w		; 2
		clrf	STATUS			; 3 Ensure port / timer access in bank 0
		movwf	INT_STAT		; 4
		movf	FSR,w			; Save FSR
		movwf	INT_FSR

SerialInt:
;		btfss	PIR1,RCIF		; Check if serial reception
;		goto	IntRestore

		btfss	RCSTA,OERR		; yes, handle RX errors
		btfsc	RCSTA,FERR
		goto	IntError

		movf	MC_WRRX,w		; select address to store data received
		movwf	FSR			; 
		movf	RCREG,w			; read serial data
		movwf	INDF			; store in Bank 0,1, status=0
		incf	MC_WRRX,f		; adjust pointer
		bcf	MC_WRRX,6		; buffer ring
		bsf	MC_WRRX,5		; 
		movf	MC_WRRX,w		; check if buffer full
		xorwf	MC_RDRX,w
		btfsc	STATUS,Z
		bsf	R4_PORT,CTS		; yes, busy status on CTS

IntRestore:
		movf	INT_FSR,w		;  Restore the Context Registers and Return
		movwf	FSR
		swapf	INT_STAT,w
		movwf	STATUS
		swapf	INT_W,f
		swapf	INT_W,w
		retfie

IntError:
		movf	RCREG,w			; on error re-init receiver
		bcf	RCSTA,CREN
		bsf	RCSTA,CREN
		goto	IntRestore


; ----- Tables

BitConvert:
		andlw	0x07
		addwf	PCL,f		; Convert bit position to byte mask
		retlw	0x01
		retlw	0x02
		retlw	0x04
		retlw	0x08
		retlw	0x10
		retlw	0x20
		retlw	0x40
		retlw	0x80


; ----- Power Up Section ( Be sure is in first 2K program code)

Start:
		movlw	0x07			; PIC 16F628, all digital
		movwf	CMCON                          
		clrf	RCSTA			; disable UART

		movlw 	RA_INI			; Set ports
		movwf 	PORTA                         
		movlw 	RB_INI
		movwf 	PORTB
		SEL_BANK_1
		movlw	PIE1_INI
		movwf	PIE1			; peripheral interrupts
		movlw	RB_TRIS
		movwf 	TRISB
		movlw 	RA_TRIS         	; Set port A I/O configuration
		movwf 	TRISA
		movlw 	OPTION_INI      	; PORTB pull-ups disabled, Timer configuration
		movwf 	OPTION_REG
		movlw	0x0B			; internal oscillator to 4MHz
		movwf	PCON
		SEL_BANK_0

		clrf	TMR0			; init timers
		clrf	CCP1CON
		movlw	0x31			; Timer 1. Run with 1:8 Prescaler
		movwf	T1CON
		clrf	T2CON			; Timer 2. shut off Timeout timer

		movlw	RAMINI0			; Clear variables bank 0 & bank 1
		movwf	FSR
ClearRAM:
		bcf	FSR,7
		clrf	INDF
		bsf	FSR,7
		clrf	INDF
		incf	FSR,f
		btfss	STATUS,Z
		goto	ClearRAM

		call	UART_INI		; Init peripheals
		movlw	INTC_INI		; Set interrupts
		movwf	INTCON

		call	InitModulesNumber	; 2 modules each line default
		bcf	TERMINAL_MODE		; terminal mode off default
		bcf	RCV_S_CMD		; don't send 'i' until first 's' command received

		bcf	R4_PORT,CTS		; Ready status on CTS
		clrf	MC_COUNT

; --- Main Loop


MainLoop:
		call	Comunic
		call	InitReadS88		; Init both lines
		bcf	RIGHT_LINE		; begin with left line
		clrf	S88COUNT
		goto	MainLoopS88L
MainLoopS88:
		call	Comunic			; ensure buffer is emptied
		call	Comunic
		call	Comunic
		call	Comunic

		call	ReadS88Module
		call	UpdateModule
		incf	S88COUNT,f		; all registered modules readed?
		btfsc	RIGHT_LINE
		goto	MainLoopS88R
MainLoopS88L:
		movf	S88_LEFT,w		; in left line?
		xorwf	S88COUNT,w
		btfss	STATUS,Z
		goto	MainLoopS88		; no, read next module

		bsf	RIGHT_LINE		; yes, next read right line
		movf	S88_MIDDLE,w		; skip middle line
		addwf	S88COUNT,f
;		goto	MainLoopS88

MainLoopS88R:
		movf	S88_LEFT,w
		addwf	S88_MIDDLE,w
		addwf	S88_RIGHT,w
		xorwf	S88COUNT,w
		btfss	STATUS,Z
		goto	MainLoopS88		; no, read next module

		call	ReportChanges		; report changes
		call	ClearS88Changes
		goto	MainLoop



; --- RS-232 Communication

UART_INI:
		bsf	R4_PORT,CTS		; Busy status on CTS
		movlw	LOW (MC_BUFTXINI)
		movwf	MC_WRTX			; empty buffers
		movwf	MC_RDTX
		movlw	LOW (MC_BUFRXINI)
		movwf	MC_WRRX
		movwf	MC_RDRX

		SEL_BANK_1
		movlw	HIGH_BAUD
		movwf	SPBRG
		movlw	0x24			; Enable TX, BRGH=1
		movwf	TXSTA
		SEL_BANK_0
		movlw	0x90			; Enable RX
		movwf	RCSTA
		clrw				; Settling time
SETTLE:
		addlw	0xFF
		btfss	STATUS,Z
    		goto 	SETTLE
		movf 	RCREG,w         	; flush receive buffer
		movf 	RCREG,w
		movf 	RCREG,w
		return


MC_BUFOUT:					; send a byte from output buffer
		btfss	PIR1,TXIF		; last byte sended?
		return				; no, quit
		movf	MC_WRTX,w		; buffer empty?
		xorwf	MC_RDTX,w
		btfsc	STATUS,Z
		return				; yes, quit
MC_OUTBYTE:
		movf	MC_RDTX,w		; get byte from buffer and send it
		movwf	FSR
		SEL_IBANK_1
		movf	INDF,w
		SEL_IBANK_0
		movwf	TXREG			; send byte
		incf	MC_RDTX,f		; check buffer ring
		movf	MC_RDTX,w
		xorlw	LOW (MC_BUFTXEND+1)
		movlw	LOW (MC_BUFTXINI)
		btfsc	STATUS,Z
		movwf	MC_RDTX
		return


; --- Serial Buffer

MC_GET:
		bsf	RXEMPTY			; get byte from input buffer
		movf	MC_WRRX,w		; full or empty buffer?
		xorwf	MC_RDRX,w
		btfss	STATUS,Z
		goto	MC_GETBYTE		; no, get byte
		btfss	R4_PORT,CTS		; yes, is full?
		return				; no
		bcf	R4_PORT,CTS		; yes, read data and clear CTS
MC_GETBYTE:
		movf	MC_RDRX,w		; get byte
		movwf	FSR
		movf	INDF,w
		incf	MC_RDRX,f		; check buffer ring
		bcf	MC_RDRX,6		; buffer ring
		bsf	MC_RDRX,5		; 
		bcf	RXEMPTY
		return

MC_SEND_DATA:
		btfss	TERMINAL_MODE
		goto	MC_SEND
		movwf	DUMMY
		call	NIBBLEH
		call	MC_SEND
		movf	DUMMY,w
		call	NIBBLEL
MC_SEND:
		movwf	TEMP			; Put a byte in tx buffer
		movf	MC_WRTX,w		; get byte
		movwf	FSR
		movf	TEMP,w
		SEL_IBANK_1
		movwf	INDF
		SEL_IBANK_0
		incf	MC_WRTX,f		; check buffer ring
		movf	MC_WRTX,w
		xorlw	LOW (MC_BUFTXEND+1)
		movlw	LOW (MC_BUFTXINI)
		btfsc	STATUS,Z
		movwf	MC_WRTX
		movf	MC_WRTX,w		; check if buffer full
		xorwf	MC_RDTX,w
		btfss	STATUS,Z
		return				; no
MC_SENDW:
		btfss	PIR1,TXIF
		goto	MC_SENDW
		call	MC_OUTBYTE		; yes, wait to send one byte
		return

NIBBLEH:
		swapf	DUMMY,w
NIBBLEL:
		andlw	0x0F			; w to char (0..F)
		addlw	0xF6
		btfsc	STATUS,C
		addlw	0x27			; a..f
		addlw	0x3A
		return


ASCII2HEX:
		addlw	0xD0			; 0x30		; 0x30...0x39 -> 0..9
		btfss	STATUS,C				; 0x41...0x46 -> A..F
		retlw	0x00					; 0x61...0x66 -> a..f
		addlw	0xF6
		btfss	STATUS,C
		goto	ASC_NUM

		addlw	0xF9			; 0x3A -> 0
		btfss	STATUS,C
		retlw	0x00
		addlw	0xFA
		btfss	STATUS,C
		goto	ASC_CHR

		addlw	0xE6			; 0x47 -> 0
		btfss	STATUS,C
		retlw	0x00
		addlw	0xFA
		btfsc	STATUS,C
		retlw	0x00
ASC_CHR:
		addlw	0x10
		return
ASC_NUM:
		addlw	0x0A
		return



; ------------- HSI protocol -------------------------------------------

Comunic:
		call	MC_BUFOUT		; send bytes from buffer
		call	MC_GET			; Get new byte
		btfsc	RXEMPTY
		return
		movwf	DUMMY			; save byte

;		call	MC_SEND			; echo
;		movf	DUMMY,w

		xorlw	CHR_CR			; end of command?
		btfsc	STATUS,Z
		goto	Decode			; yes, decode
		movf	MC_COUNT,w
		btfsc	STATUS,Z		; receiving first byte?
		goto	ComunicHeader		; yes
		xorlw	d'7'			; longest command
		btfsc	STATUS,Z
		goto	CommandError
ComunicPacket:
		movlw	MC_HEADER		; save byte
		addwf	MC_COUNT,w
		movwf	FSR
		movf	DUMMY,w
		movwf	INDF
		incf	MC_COUNT,f
		return

ComunicHeader:
		btfsc	DUMMY,6			; 0x40..0x6F  'A..z'?
		goto	ComunicPacket		; yes, save header
CommandError:
		clrf	MC_COUNT
		return


; --- HSI commands

Decode:
		movf	MC_HEADER,w
		iorlw	0x20			; 'A'..'Z' or 'a'..'z'
Decode_V_command:
		xorlw	'v'			; version
		btfss	STATUS,Z
		goto	Decode_T_command
		movf	MC_COUNT,w
		xorlw	d'1'
		btfss	STATUS,Z
		goto	EndDecode
Send_V_command:
		movlw	HIGH (MSG_VER)
		movwf	MSB			; PCLATH
		movlw	LOW (MSG_VER)
		movwf	LSB			; PCL
LCDSTRCHR:
		call	GetChar
		clrf	PCLATH
		movwf	TEMP
		btfsc	TEMP,7
		goto	Send_command_End
		call	MC_SEND
		incf	LSB,f			; next character
		btfsc	STATUS,Z
		incf	MSB,f
		goto	LCDSTRCHR

GetChar:
		movf	MSB,w			; goto MSB,LSB
		movwf	PCLATH
		movf	LSB,w
		movwf	PCL


Decode_T_command:
		xorlw	('v' ^ 't')		; toggle mode
		btfss	STATUS,Z
		goto	Decode_M_command
		movf	MC_COUNT,w
		xorlw	d'1'
		btfsc	STATUS,Z
		goto	Send_T_command		; t
		xorlw	( d'1' ^ d'2')
		btfss	STATUS,Z
		goto	EndDecode
		movf	MC_DATA1,w
		xorlw	'0'			; t0
		btfsc	STATUS,Z
		bcf	TERMINAL_MODE
		xorlw	('0' ^ '1')		; t1
		btfsc	STATUS,Z
		bsf	TERMINAL_MODE
		goto	Set_T_command
Send_T_command:
		movlw	T_MODE_MSK		; toggle mode
		xorwf	FLAGS,f
Set_T_command:
		movlw	't'
		call	MC_SEND
		movlw	'0'
		btfsc	TERMINAL_MODE
		movlw	'1'
		call	MC_SEND
Send_command_End:
		clrf	MC_COUNT		; prepare decoding next packet
		movlw	CHR_CR
		goto	MC_SEND


Decode_M_command:
		xorlw	('t' ^ 'm')		; query input conditions
		btfss	STATUS,Z
		goto	Decode_S_command
		movf	MC_COUNT,w
		xorlw	d'1'
		btfss	STATUS,Z
		goto	EndDecode
Send_M_command:
		movlw	'm'			; m 'number of registered modules'
		call	MC_SEND
		movf	S88_LEFT,w
		addwf	S88_MIDDLE,w
		addwf	S88_RIGHT,w
		movwf	COUNT
		call	MC_SEND_DATA

		clrf	MODULE
Send_M_data:
		movf	MODULE,w
		xorwf	COUNT,w
		btfsc	STATUS,Z
		goto	Send_command_End

		movf	MODULE,w
		addlw	0x01			; module number, high byte, low byte
		call	MC_SEND_DATA
		movf	MODULE,w		; module x 2
		addwf	MODULE,w
		addlw	S88Buffer
		movwf	MOD_ADDR
		movwf	FSR
		movf	INDF,w			; high byte
		call	MC_SEND_DATA
		incf	MOD_ADDR,w
		movwf	FSR
		movf	INDF,w			; low byte
		call	MC_SEND_DATA

		incf	MODULE,f
		goto	Send_M_data


Decode_S_command:
		xorlw	('m' ^ 's')		; register feedback modules
		btfss	STATUS,Z
		goto	EndDecode
		btfss	TERMINAL_MODE
		goto	Decode_S_TermOff
		movf	MC_COUNT,w		; terminal on
		xorlw	d'7'
		btfss	STATUS,Z
		goto	EndDecode

		movf	MC_DATA1,w
		call	ASCII2HEX
		movwf	MC_DATA1
		swapf	MC_DATA1,f
		movf	MC_DATA2,w
		call	ASCII2HEX
		iorwf	MC_DATA1,w
		movwf	S88_LEFT

		movf	MC_DATA3,w
		call	ASCII2HEX
		movwf	MC_DATA3
		swapf	MC_DATA3,f
		movf	MC_DATA4,w
		call	ASCII2HEX
		iorwf	MC_DATA3,w
		movwf	S88_MIDDLE


		movf	MC_DATA5,w
		call	ASCII2HEX
		movwf	MC_DATA5
		swapf	MC_DATA5,f
		movf	MC_DATA6,w
		call	ASCII2HEX
		iorwf	MC_DATA5,w
		movwf	S88_RIGHT

		goto	Decode_S_Check

Decode_S_TermOff:
		movf	MC_COUNT,w		; terminal off
		xorlw	d'4'
		btfss	STATUS,Z
		goto	EndDecode
		movf	MC_DATA1,w
		movwf	S88_LEFT
		movf	MC_DATA2,w
		movwf	S88_MIDDLE
		movf	MC_DATA3,w
		movwf	S88_RIGHT
Decode_S_Check:
		movf	S88_LEFT,w		; if modules more than 31 then reset values
		addwf	S88_MIDDLE,w
		addwf	S88_RIGHT,w
		andlw	0xE0
		btfss	STATUS,Z
		call	InitModulesNumber

Send_S_command:
		movlw	's'			; registered modules answer
		call	MC_SEND

		movf	S88_LEFT,w		; total number of registered modules
		addwf	S88_MIDDLE,w
		addwf	S88_RIGHT,w
		movwf	COUNT
		call	MC_SEND_DATA

		call	InitReadS88		; mark all as changed
		clrf	S88COUNT
		bsf	RCV_S_CMD
		movlw	0xFF
		movwf	BIT_TBL
		movwf	BIT_TBL+1
		movwf	BIT_TBL+2
		movwf	BIT_TBL+3		; next will be reported as all changed

		goto	Send_command_End

EndDecode:
		clrf	MC_COUNT		; prepare next packet
		return


 
ReportChanges:
		movf	BIT_TBL,w		; any changes in S88 inputs?
		iorwf	BIT_TBL+1,w
		iorwf	BIT_TBL+2,w
		iorwf	BIT_TBL+3,w
		btfsc	STATUS,Z
		return

		btfss	RCV_S_CMD		; wait until 's' command received
		return

		movlw	'i'			; yes, report changes
		call	MC_SEND

CountChanges:
		movf	S88_LEFT,w
		addwf	S88_MIDDLE,w
		addwf	S88_RIGHT,w
		movwf	COUNT
		clrf	MODULE
		clrf	DUMMY
CountChangesNxt:
		movf	MODULE,w
		call	GetBit
		btfss	STATUS,Z
		incf	DUMMY,f
		incf	MODULE,f
		decfsz	COUNT,f
		goto	CountChangesNxt
		movf	DUMMY,w
		call	MC_SEND_DATA

		clrf	S88COUNT
ReportChangesModule:
		movf	S88COUNT,w
		call	GetBit
		btfsc	STATUS,Z
		goto	ReportChangesNxt
		incf	S88COUNT,w		; module number
		call	MC_SEND_DATA
		call	GetDataPointer		; high data byte
		movf	INDF,w
		call	MC_SEND_DATA
		call	GetDataPointer		; low data byte
		incf	FSR,f
		movf	INDF,w
		call	MC_SEND_DATA
ReportChangesNxt:
		incf	S88COUNT,f
		movf	S88_LEFT,w
		addwf	S88_MIDDLE,w
		addwf	S88_RIGHT,w
		xorwf	S88COUNT,w
		btfss	STATUS,Z		; all modules?
		goto	ReportChangesModule

		movlw	CHR_CR
		call	MC_SEND
		return


; --- S88 changes

GetPointer:
		movwf	FSR			; Get module position
		movwf	TEMP
		rrf	FSR,f
		rrf	FSR,f
		rrf	FSR,w
		andlw	0x03
		addlw	BIT_TBL
		movwf	FSR
		movf	TEMP,w
		return

GetBit:
		call	GetPointer		; module
		call	BitConvert		; Bit position
		andwf	INDF,w			; Z = ~bit
		return

ResetBit:
		call	GetPointer		; module
		call	BitConvert		; Bit position
		xorlw	0xFF			; complement
		andwf	INDF,f
		return

SetBit:
		call	GetPointer		; module
		call	BitConvert		; Bit position
		iorwf	INDF,f
		return

GetDataPointer:
		movf	S88COUNT,w		; count * 2
		addwf	S88COUNT,w
		addlw	S88Buffer
		movwf	FSR
		return

ClearS88Changes:
		clrf	BIT_TBL
		clrf	BIT_TBL+1
		clrf	BIT_TBL+2
		clrf	BIT_TBL+3
		return

InitModulesNumber:
		movlw	d'2'			; GenHSI reset values
		movwf	S88_LEFT		; 2 modules each line default
		movwf	S88_MIDDLE
		movwf	S88_RIGHT
		return

; --- S88 reading

Delay10us:
		DNOP				; 1,2	Delay w * 10us
		DNOP				; 3,4
		DNOP				; 5,6

		addlw	0xFF			; 7
		btfss	STATUS,Z		; 8
		goto	Delay10us		; 9,10
		return


InitReadS88:
		bcf	S88PORT,S88_CLK		; just to be sure
		bcf	S88RPORT,S88R_CLK
		bsf	S88PORT,S88_LOAD	; Latch data on 4014
		bsf	S88PORT,S88R_LOAD
		movlw	d'2'			; 20us
		call	Delay10us
		bsf	S88PORT,S88_CLK
		bsf	S88RPORT,S88R_CLK
		movlw	d'5'			; 50us
		call	Delay10us
		bcf	S88PORT,S88_CLK
		bcf	S88RPORT,S88R_CLK
		movlw	d'8'			; 80us
		call	Delay10us
		bsf	S88PORT,S88_RESET	; Reset flip-flops
		bsf	S88RPORT,S88R_RESET
		movlw	d'4'			; 40us
		call	Delay10us
		bcf	S88PORT,S88_RESET
		bcf	S88RPORT,S88R_RESET
		movlw	d'6'			; 60us
		call	Delay10us
		bcf	S88PORT,S88_LOAD
		bcf	S88PORT,S88R_LOAD
		movlw	d'5'			; 50us before reading
		call	Delay10us
		return



ReadS88Module:
		movlw	0x10			; read 16 bits (2.8ms)
		movwf	TEMP
ReadS88Bit:
		btfsc	RIGHT_LINE
		goto	ReadS88BitR
ReadS88BitL:
		bsf	STATUS,C		; get input from 4014
		btfss	S88PORT,S88_DIN
		bcf	STATUS,C
		rrf	S88DATH,f
		rrf	S88DATL,f

		bsf	S88PORT,S88_CLK		; Clock pulse left line
		movlw	d'9'			; 90us
		call	Delay10us
		bcf	S88PORT,S88_CLK
		movlw	d'9'			; 90us
		call	Delay10us
		goto	ReadS88BitEnd

ReadS88BitR:
		bsf	STATUS,C		; get input from 4014
		btfss	S88RPORT,S88R_DIN
		bcf	STATUS,C
		rrf	S88DATH,f
		rrf	S88DATL,f

		bsf	S88RPORT,S88R_CLK	; Clock pulse left line
		movlw	d'9'			; 90us
		call	Delay10us
		bcf	S88RPORT,S88R_CLK
		movlw	d'9'			; 90us
		call	Delay10us

ReadS88BitEnd:
		decfsz	TEMP,f			; next bit
		goto	ReadS88Bit
		return


UpdateModule:
		movf	S88COUNT,w		; count * 2
		addwf	S88COUNT,w
		addlw	S88Buffer
		movwf	FSR
		movf	INDF,w			; high byte equal?
		xorwf	S88DATH,w
		btfsc	STATUS,Z
		goto	UpdateModuleLow		; yes
		movf	S88DATH,w		; no, mark as changed
		movwf	INDF
		movf	S88COUNT,w
		call	SetBit
UpdateModuleLow:
		movf	S88COUNT,w		; count * 2
		addwf	S88COUNT,w
		addlw	S88Buffer
		movwf	FSR
		incf	FSR,f
		movf	INDF,w			; low byte equal?
		xorwf	S88DATL,w
		btfsc	STATUS,Z
		goto	UpdateModuleEnd		; yes
		movf	S88DATL,w		; no, mark as changed
		movwf	INDF
		movf	S88COUNT,w
		call	SetBit
UpdateModuleEnd:
		return		


		


; ---------------------- Messages --------------------------------------------

		org	0x780


MSG_VER:
		dt	"ver. 0.89 / "
		dt	(__VERDAY   >> 4)  +0x30
		dt	(__VERDAY   & 0x0F)+0x30,"."
		dt	(__VERMONTH >> 4)  +0x30
		dt	(__VERMONTH & 0x0F)+0x30,"."
		dt	(__VERYEAR  >> 4)  +0x30
		dt	(__VERYEAR  & 0x0F)+0x30
		dt	" / GenHSI / --- --- ",0x80


; ---------------------- Internal EEPROM -------------------------------------

		org	0x2100

		dt	" GenHSI "
		dt	"F.Caņada"
		dt	(__VERDAY   >> 4)  +0x30
		dt	(__VERDAY   & 0x0F)+0x30,"/"
		dt	(__VERMONTH >> 4)  +0x30
		dt	(__VERMONTH & 0x0F)+0x30,"/"
		dt	(__VERYEAR  >> 4)  +0x30
		dt	(__VERYEAR  & 0x0F)+0x30

		end

