;zoomer.asm - LANC controller - many zoom speeds, start/stop
;
;assumes this hardware config:
;16C55, RA0 = LANC in/out
;	RA1 = sending LED
;	RA2 = wakeup (?)
;	RA3 = start/stop 18/33
;	RB0 = focus near 47
;	RB1 = focus far 45 
;	RB2 = zoom tele 0 (slowest)
;	RB3 = zoom tele 1
;	RB4 = zoom tele 2
;	RB5 = zoom tele 3
;	RB6 = zoom tele 4
;	RB7 = zoom tele 5
;	RC0 = zoom tele 6 (fastest)
;	RC1 = zoom wide 0 (slowest)
;	RC2 = zoom wide 1
;	RC3 = zoom wide 2
;	RC4 = zoom wide 3
;	RC5 = zoom wide 4
;	RC6 = zoom wide 5
;	RC7 = zoom wide 6 (fastest)


	LIST    P = PIC16C55, n = 66	
	INCLUDE         <P16C5X.INC>

PIC55   equ     1FFH    ; Define Reset Vector
Same    equ     1
same	equ	1

TRIS_READ_LANC		equ	00dh	;A0, 2, 3 in - 1 out
TRIS_WRITE_LANC		equ	00ch	;A2, 3 in -  0,1 out 
TRIS_READ_SWITCHES	equ	0ffh	; all inputs
INPUT			equ	0	;bit to use for reading LANC - used 2x
OUTPUT			equ	0	;bit to use for writing LANC - used 2x

SEND_COMM_LED		equ	1	;set when sending commands - used 4x

BIT_TIME	equ	.31	;for 4 MHz - used 2x
; on my SONY CCD-TR6, values from 33 to 28 worked - mid range
; is 30 or 31

;send once masks:
; if bit is set to one, then command will be sent only once per
; button press.  If bit is zero, command will be sent continuously
; as long as button is held down

once_a		EQU	B'00001100'
once_b		EQU	B'00000000'
once_c		EQU	B'00000000'

; input masks 
; a '1' indicates that that bit should be tested as an input
; output-only or non-switch conected I/O should be zero

input_a		EQU	B'00001100'
input_b		EQU	B'11111111'
input_c		EQU	B'11111111'


;================================================================
;RAM variables
	org	08h

bit_counter	RES	1	;08
bits_to_send	RES	1	;09	
byte_to_write	RES	1	;0a
command_byte_1	RES	1	;0b
command_byte_2	RES	1	;0c
byte_number	RES	1	;0d
command_pass	RES	1	;0e
temp		RES	1	;0f
index		RES	1	;10
bitmask		RES	1	;11
port		RES	1	;12


;RAM bit masks to track whether a command has been sent for a particular
; button push.  For send once butttons, will be set to one after
; cmd is sent, and will be cleared when button is scanned as released
; for send repetetive buttons, will be cleared after command is sent 
; (and re-cleared after button is released...)

sent_a		RES	1	;13
sent_b		RES	1	;14
sent_c		RES	1	;15

;=====================================================================
	ORG     0
top
	goto	init_real

main    
	call	wait_for_interframe	;do all switch reading in the 
					; 6 ms (4 left after this routine)
					; between frames .... this is
					; lots of time... each machine cycle is 
					; 1 us - so we have ~4000 to use...

	goto	scan_switches_real	;ends with 'goto main'

;----------------------------------------------------------------------
; subroutine call table....since 'call' target must be in first 256 bytes
;  but goto can go anywhere


wait_for_interframe	goto	wait_for_interframe_real
send_command_5		goto	send_command_5_real
scan_switches		goto	scan_switches_real
get_command_byte_1	goto	get_command_byte_1_real
get_command_byte_2	goto	get_command_byte_2_real

;-----------------------------------------------
; command functions - these do not need the _return label since they
;  execute a goto send_command_5_real at their end


;----------------------------------------------------------------------
;wait_for_interframe routine
; when called will eat time waiting for a roughly 2 ms idle time on 
; LANC - reads INPUT bit of PORTA
; uses byte_number byte_to_write

wait_for_interframe_real
sync_up	
	movlw	3
	movwf	byte_to_write	; byte_to_write = 3
	clrf	byte_number		; byte_number = 0

sync_loop
	btfss	PORTA, INPUT	;if lanc = 1, skip jump
	goto	sync_up		; any lanc = 0 resets counters

				; lanc == 1 to get here
	incfsz	byte_number, 1	; inc low byte of byte_number
	goto	sync_loop	; skip jump if byte_number overflows to 0

	decfsz	byte_to_write, 1	; decrement byte_to_write
	goto	sync_loop	; skip jump if --byte_to_write == 0

sync_ed	
	retlw	0		;return to caller

;--------------------------------------------------------------------
; send_command_5_real  sends command in command_byte_1 and command_byte_2
; the required 5 times.

send_command_5_real
	bsf	PORTA, SEND_COMM_LED	;turn on the 'sending command' LED

	movlw	5
	movwf	command_pass

sc5_1
	call	wait_for_interframe


;------------------------------------------------------------------
; send_command
; waits until bit INPUT of PORTA goes low, then delays
; 1 bit time minus a few cycles, then sends the byte in 
; command_byte_1, then waits for INPUT to go low again, then
; sends byte in command_byte_2
; uses:
;	bit_counter, bits_to_send, byte_to_write, byte_number
send_command
	movlw	2
	movwf	byte_number	;initialize outer loop counter

	movf	command_byte_1, 0
	movwf	byte_to_write	;put command byte into byte_to_write

sc_wait_for_LANC_start
	movlw	TRIS_READ_LANC	; A0 = input, A1 = output
	tris	PORTA

sc_wfLs_1
	btfsc	PORTA, INPUT	;waiting for input port to drop - 
	goto	sc_wfLs_1	;is start bit from camcorder

	movlw	BIT_TIME - 1	; @ 4 MHz
	movwf	bit_counter

sc_wfLs_2				;each loop = 1.5 us @ 8MHz
	decfsz	bit_counter, 1
	goto	sc_wfLs_2


sc_write_byte
	movlw	8
	movwf	bits_to_send

	movlw	TRIS_WRITE_LANC	; A0 = input, A1 = output
	tris	PORTA

sc_write_bit
	movlw	BIT_TIME	;34 loops @ 3 clocks @ 1us each @ 4mhz
	movwf	bit_counter

	btfsc	byte_to_write, 0	;test bit 0
	goto	sc_wb_set_1		;skipped if bit 0 = 0

	bsf	PORTA, OUTPUT	;clear output bit to 0
	goto	sc_wb_1
sc_wb_set_1
	bcf	PORTA, OUTPUT	;set output bit to 1 - 

sc_wb_1
	decfsz	bit_counter, 1
	goto	sc_wb_1

	rrf	byte_to_write, same
	decfsz	bits_to_send, same
	goto	sc_write_bit

	movlw	TRIS_READ_LANC	; A0 = input, A1 = output
	tris	PORTA

	decfsz	byte_number, same
	goto	sc_byte_2
	goto	sc_done

sc_byte_2
	movf	command_byte_2, 0
	movwf	byte_to_write
	goto	sc_wait_for_LANC_start

sc_done
	decfsz	command_pass, same
	goto	sc5_1
	
	bcf	PORTA, SEND_COMM_LED	;turn off the sending comand LED

	retlw	0

;----------------------------
; scan for pressed switches - open switch is pulled up to be a 
; 1 - so any zeros will be pressed switches
; index holds the current bit index to be tested - values are:
; PORTA 0 through 7 (even though PORTA is only 4 bits on the 1655)
; PORTB	8 through 15
; PORTC	16 through 23
;
; will test the bit index of the input_X masks to determine if we need
; to test the particular bit as an input, 

scan_switches_real
	movlw	7		;0000 0111
	andwf	index, 0	; get low 3 bits of index in w
	movwf	temp		; temp == index & 0x7
	incf	temp, 1		; temp == (index & 0x7)+ 1
	movlw	1
	movwf	bitmask		;bitmask = 0000 0001
shift_top_1
	decfsz	temp, 1
	goto	do_shift
	goto	end_shift
do_shift
	rlf	bitmask, 1
	goto	shift_top_1
end_shift			;bitmask will now have the correct bit
				;set to 1 to use to test in masks
	movf	index, 0
	movwf	port
	rrf	port, 1
	rrf	port, 1
	rrf	port, 1		;>> 3
	movlw	01fh		; 0001 1111
	andwf	port, 1		; port == (index & 0xf8)>>3
	movlw	PORTA		; base address for ports
	addwf	port, 1		; port == 5, 6, or 7 for port A, B, or C
	
;test to see if port:bit is a switch input
	movf	bitmask, 0	; used in all tests W = bitmask
	btfss	port, 1		;bit 1 == 0 only for portA
	goto	test_input_a
	btfss	port, 0		;bit 0 == 0 only for port b
	goto	test_input_b

test_input_c
	andlw	input_c		;Z = 1 if no match
	goto	test_input_comm
test_input_b
	andlw	input_b
	goto	test_input_comm
test_input_a
	andlw	input_a
test_input_comm
	btfsc	STATUS, Z
	goto	next_index

check_bit
	movf	port, 0		;get port into W
	movwf	FSR		;FSR -> port
	movf	INDF, 0		;read port *FSR into W
	andwf	bitmask, 0	; W = W & bitmask
				; if Z, then button pressed, else next button
	btfsc	STATUS, Z
	goto	process_button	;got a zero on port, process it

	;get here if current port:bit is not pressed
	;can clear the bit in the 'sent_X' vars since key
	;has been released.  (even if not pressed, can clear
	;an already cleared bit - no harm)
	
	comf	bitmask, 0	; used in all bitclears W = !(bitmask)
	btfss	port, 1		;bit 1 == 0 only for portA
	goto	clear_sent_a
	btfss	port, 0		;bit 0 == 0 only for port b
	goto	clear_sent_b

clear_sent_c
	andwf	sent_c, 1	;sent_c = (sent_c & !bitmask)
	goto	clear_sent_comm
clear_sent_b
	andwf	sent_b, 1
	goto	clear_sent_comm
clear_sent_a
	andwf	sent_a, 1
clear_sent_comm


next_index
	incf	index, 1
	movf	index, 0	; W = index
	movwf	temp
	movlw	.24		; 24 total bits to test
	subwf	temp, 0		; W = temp - W
	btfsc	STATUS, C	; 1 => no borrow, 0 => borrow
				; temp >= 24	temp < 24
	clrf	index
	goto	scan_switches_real
	
;-----------------------------------------------------------------------
process_button
;test to see if this is a "send once command"
	movf	bitmask, 0	; used in all tests W = bitmask
	btfss	port, 1		;bit 1 == 0 only for portA
	goto	test_once_a
	btfss	port, 0		;bit 0 == 0 only for port b
	goto	test_once_b

test_once_c
	andlw	once_c		;Z = 1 if no match
	goto	test_once_comm
test_once_b
	andlw	once_b
	goto	test_once_comm
test_once_a
	andlw	once_a
test_once_comm
	btfsc	STATUS, Z
	goto	send_it		;is not a 'once' command - go do it

is_once				;_is_ a send once - have we already 
				;sent it?
	movf	bitmask, 0	; used in all tests W = bitmask
	btfss	port, 1		;bit 1 == 0 only for portA
	goto	test_sent_a
	btfss	port, 0		;bit 0 == 0 only for port b
	goto	test_sent_b

test_sent_c
	andwf	sent_c, 0	;Z = 1 if no match
	goto	test_sent_comm
test_sent_b
	andwf	sent_b, 0
	goto	test_sent_comm
test_sent_a
	andwf	sent_a, 0
test_sent_comm
	btfss	STATUS, Z
	goto	next_index	;is a once that we've already sent

mark_as_sent		;is a "send once"
			; and we haven't already sent it
	movf	bitmask, 0	;used in all tests W = bitmask
	btfss	port, 1		;bit 1 == 0 only for portA
	goto	mark_sent_a
	btfss	port, 0		;bit 0 == 0 only for port b
	goto	mark_sent_b
mark_sent_c
	iorwf	sent_c, 1	;Z = 1 if no match
	goto	test_sent_comm
mark_sent_b
	iorwf	sent_b, 1
	goto	test_sent_comm
mark_sent_a
	iorwf	sent_a, 1
mark_sent_comm


send_it
	movf	index, 0	;get index into W
	call	get_command_byte_1
	movwf	command_byte_1
	
	movf	index, 0
	call	get_command_byte_2
	movwf	command_byte_2

	call	send_command_5

	goto	main

;--------------------------------------------------------------------
de	"BYTE 1  "
get_command_byte_1_real
	addwf	PCL, 1
	retlw	0x00	;index = 0, PORTA, bit 0 LANC I/O
	retlw	0x00	;1	PORTA, bit 1	output only
	retlw	0x28	;2	PORTA, bit 2	toggle backlight 28/51
	retlw	0x18	;3	PORTA, bit 3	start/stop 18/33
	retlw	0x00	;4	PORTA, bit 4	non-existent
	retlw	0x00	;5	PORTA, bit 5	non-existent
	retlw	0x00	;6	PORTA, bit 6	non-existent
	retlw	0x00	;7	PORTA, bit 7	non-existent
	retlw	0x28	;8	PORTB, bit 0	focus near 28/47
	retlw	0x28	;9	PORTB, bit 1	focus far 28/45
	retlw	0x28	;10	PORTB, bit 2	zoom tele 0 28/00
	retlw	0x28	;11	PORTB, bit 3	zoom tele 1 28/02
	retlw	0x28	;12	PORTB, bit 4	zoom tele 2 28/04
	retlw	0x28	;13	PORTB, bit 5	zoom tele 3 28/06
	retlw	0x28	;14	PORTB, bit 6	zoom tele 4 28/08
	retlw	0x28	;15	PORTB, bit 7	zoom tele 5 28/0a
	retlw	0x28	;16	PORTC, bit 0	zoom tele 6 28/0c
	retlw	0x28	;17	PORTC, bit 1	zoom wide 0 28/10
	retlw	0x28	;18	PORTC, bit 2	zoom wide 1 28/12
	retlw	0x28	;19	PORTC, bit 3	zoom wide 2 28/14
	retlw	0x28	;20	PORTC, bit 4	zoom wide 3 28/16
	retlw	0x28	;21	PORTC, bit 5	zoom wide 4 28/18
	retlw	0x28	;22	PORTC, bit 6	zoom wide 5 28/1a
	retlw	0x28	;23	PORTC, bit 7	zoom wide 6 28/1c

de	"BYTE 2   "
get_command_byte_2_real
	addwf	PCL, 1
	retlw	0x00	;index = 0, PORTA, bit 0
	retlw	0x00	;1	PORTA, bit 1
	retlw	0x51	;2	PORTA, bit 2	toggle backlight (28/51)
	retlw	0x33	;3	PORTA, bit 3	start/stop 18/33
	retlw	0x00	;4	PORTA, bit 4	non-existent
	retlw	0x00	;5	PORTA, bit 5	non-existent
	retlw	0x00	;6	PORTA, bit 6	non-existent
	retlw	0x00	;7	PORTA, bit 7	non-existent
	retlw	0x47	;8	PORTB, bit 0	focus near 28/47
	retlw	0x45	;9	PORTB, bit 1	focus far 28/45
	retlw	0x00	;10	PORTB, bit 2	zoom tele 0 28/00
	retlw	0x02	;11	PORTB, bit 3	zoom tele 1 28/02
	retlw	0x04	;12	PORTB, bit 4	zoom tele 2 28/04
	retlw	0x06	;13	PORTB, bit 5	zoom tele 3 28/06
	retlw	0x08	;14	PORTB, bit 6	zoom tele 4 28/08
	retlw	0x0a	;15	PORTB, bit 7	zoom tele 5 28/0a
	retlw	0x0c	;16	PORTC, bit 0	zoom tele 6 28/0c
	retlw	0x10	;17	PORTC, bit 1	zoom wide 0 28/10
	retlw	0x12	;18	PORTC, bit 2	zoom wide 1 28/12
	retlw	0x14	;19	PORTC, bit 3	zoom wide 2 28/14
	retlw	0x16	;20	PORTC, bit 4	zoom wide 3 28/16
	retlw	0x18	;21	PORTC, bit 5	zoom wide 4 28/18
	retlw	0x1a	;22	PORTC, bit 6	zoom wide 5 28/1a
	retlw	0x1c	;23	PORTC, bit 7	zoom wide 6 28/1c

;-----------------------------------------------------------------------
init_real
	movlw	TRIS_READ_LANC	; A0 = input, A2 = output, A1,3,4,5 input
	tris	PORTA
	clrf	PORTA		; clear output bits to 0
	movlw	TRIS_READ_SWITCHES
	tris	PORTB
	movlw	TRIS_READ_SWITCHES
	tris	PORTC

	clrwdt
	movlw	7		;tmr0 prescale to 1:256
	option

	movlw	08h
	movwf	FSR
	clrw
wipe_top	
	clrf	INDF
	incf	FSR,1
	movf	FSR, 0		; W = FSR
	andlw	01fh		; 
	btfss	STATUS,2
	goto	wipe_top

;===================================================================
;happy_dance
;  let user know we're alive by winking send command LED
;  about 2HZ for 6 blinks (maybe 5)

happy_dance
	;turn on send command to start
	bsf	PORTA, SEND_COMM_LED	;turn on the 'sending command' LED

	movlw	.6		;6 half-seconds (roughly)
				;each TMR0 loop is 66ms x 7 ~ 460ms
	movwf	bit_counter		;run for 3 seconds

hd_inner
	movlw	7
	movwf	bits_to_send

tw0
	movf	TMR0, 0		;get timer
	btfss	STATUS,2
	goto	tw0		;wait for tmr0 to = 0

tw1
	movf	TMR0, 0		;get timer
	btfsc	STATUS,2
	goto	tw1		;wait for tmr0 to != 0

	decfsz	bits_to_send, same
	goto	tw0

	;toggle LED
	movlw	02h	;flip bit 1
	xorwf	PORTA, same

	decfsz	bit_counter, same
	goto	hd_inner

happy_exit
	bcf	PORTA, SEND_COMM_LED
	clrf	bit_counter
	clrf	bits_to_send

	goto	main

	ORG	1d0h
	
	de	"Ed's    LANC    Zoomer  v 1.00  (c)2000 "

;--------------------------------------------------------------------
	ORG     PIC55
	goto    top


	END
