please dont rip this site

SX Microcontroller based Keyboard IO

California Dreamin SX USB Keyboard Demo

by Michael Hetherington

Used in the EXCELLENT Atapchi: World's Smallest Low-speed USB Analyzer

;California dreamin' (aka SX USB Virtual Peripheral) Version 1.00
;Copyright (C) 2001 Michael Hetherington
;chinook@pacific.net.sg
;
;This program is free software; you can redistribute it and/or
;modify it under the terms of the GNU General Public License
;as published by the Free Software Foundation.
;This program is distributed in the hope that it will be useful,
;but WITHOUT ANY WARRANTY; without even the implied warranty of
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;GNU General Public License for more details.


;SX28AC enumerates as generic USB keyboard and types the lyrics of "California dreamin'"
;on every press of led toggling key (Caps Lock, Scroll Lock and Num Lock). 
;Works perfectly with Windows 2000 PC and any text editor.
;It has not been tested on Macs or Unix platform yet. 
;Power can be driven from USB port, however independent 5V source is recommended. 
;The following design does not meet all USB requirements
;and provides just an example of simple interface to USB using Scenix MCU and widely
;available components: 
;bus driver 74LCX125
;Resonator - 50 MHz (Murata)
;Environment - SX Key
		
		DEVICE	SX28L,oscxtmax,turbo,stackx_optionx
   		RESET   Start


rx_lo		equ	1	; D+ USB line 
rx_hi		equ	2	; D- USB line pulled by 1.5K to 3.3V
comp		equ	3	; comparator output
tx_lo		equ	4	; drive D+
tx_hi		equ	5	; drive D-
oe		equ	6	; line driver output enable (inverted)



usb_port	equ	rb
usb_port_tris	equ	%10001110

rx_lo_pin	equ	rb.1	; respective IC pins
rx_hi_pin	equ	rb.2
comp_pin	equ	rb.3
tx_lo_pin 	equ	rb.4
tx_hi_pin	equ	rb.5 
oe_pin		equ	rb.6

;**********************************************************************************
; Buffer break-down
; Starting address $03
; We have 8 buffers to store data
;**********************************************************************************

address_lo	equ	$30

sync_bit	equ	7	; to keep synchronized (10001000100....)
ep1_in		equ	6	; to keep respective token packets
ep0_in		equ	5	; to give ACK, NACK or STALL
ep0_out		equ	4
setup		equ	3
all_data	equ	2	; all incoming data
ep1_data	equ	1	; buffer of 14 bytes to keep IN data 
ep0_data	equ	0	; buffer of 14 bytes to keep IN data and OUT data

;**********************************************************************************
; Token packets buffers are underutilised
; Use upper address for handshake packets
; Starting address $09
;**********************************************************************************

address_hi	equ	$90

ack		equ	6
nack		equ	5
stall		equ	4

;**********************************************************************************
;  For USB interface execution of interrupt will be done
;  every 20-25 cycles to catch the start of a packet.
;  Once the packet arrived 1,5 MHz acquisition rate should be maintained.
;  With 50MHz resonator it means interrupt executes every 33 cycles 3 times and 
;  every 34 cycles 1 time, this cycle will be called leap cycle (after leap year)
;********************************************************************************** 

int_idle	equ	25
int_active	equ	33
int_leap	equ	34


usb_states	equ	$08		;main body state machine
int_states	equ	$09		;interrupt state machine

buffer_count	equ	$0A		;bit transmit/receive counter in buffers		
temp		equ	$0B		;register to hold function specific values
					;holds current value of rb

usb_mask	equ	$0C		;masking different buffers on reset and address
					;change

packet_rx	equ	$0D		;packet mask while Rx
packet_tx	equ	$0D		;packet mask while Tx
reset_timer	equ	$0D		;monitor the time out for the bus
					;to detect reset conditions for Rx

tx_timer	equ	$0E		;the number of bits to send
					;it is loaded with the value of
					;usb_ep0_counter or usb_ep1_counter

usb_flags	equ	$0F		;general flags

flg_setup	equ	usb_flags.0	;setup packet was received
flg_out		equ	usb_flags.1	;out packet was received
flg_ep0_send	equ	usb_flags.2	;data is ready for transmission from ep0
flg_ep0_in	equ	usb_flags.3	;in packet for ep0 is received
flg_ep1_send	equ	usb_flags.4	;data is ready for transmission from ep1
flg_ep1_in	equ	usb_flags.5	;in packet for ep1 is received
flg_ep0_data	equ	usb_flags.6	;data 0/1 toggle for ep0
flg_ep1_data	equ	usb_flags.7	;data 0/1 toggle for ep1	



		org	$10
USB_BANK	=	$

usb_hold	equ	$10		;the same memory cell
usb_ep0_byte	equ	$11		;keeps temporary variables
usb_sub_states 	equ	$12		;substates of main state machine
usb_ep0_table_s equ	$13		;ep0 tx table start	
usb_ep0_table_e equ	$14		;ep0 tx table end
usb_ep1_table_s equ	$15		;ep1 tx table start
usb_ep1_table_e equ	$16		;ep1 tx table end

usb_buffer	equ	$17
usb_address	equ	$18
usb_temp	equ	$19
usb_bit_stuff	equ	$1A
usb_bit_count	equ	$1B

usb_ep0_counter	equ	$1C		;the number of bits to send on EP0 IN packet 
usb_ep1_counter	equ	$1D		;the number of bits to send on EP1 IN packet

usb_crc5	equ	$1F
usb_crc16_hi	equ	$1F
usb_crc16_lo	equ	$1E

		org	0
Interrupt

		mov	temp,rb	
		mov	w,int_states
		add	PC,w

IDLE_loc		=	$	
;***********************************************************************************
; IDLE STATE
; D- stays high
; D+ stays low
;***********************************************************************************
		sb	temp.rx_hi		;catch the start of a packet:	
		jmp	IDLE_x			;when D- falls
		clr	reset_timer		;if D- stays high clear timer
		mov	w,#-int_idle	
		retiw
IDLE_x			
		inc	reset_timer		;monitor the line for reset condition
		snz				
		jmp	BUS_RESET_loc
	        sb	usb_port.comp		;if the output of comp falls
						;which means D+ went up, the start of the  
		jmp	BUS_SYNC_loc		;packet is detected
		mov	w,#-int_idle		;get ready to receive		
		retiw

BUS_RESET_loc				
;***********************************************************************************
; BUS RESET STATE
; Single ended 0 is detected for the period of 8uS 
;***********************************************************************************
Bus_reset	
		bank	USB_BANK
		clr	usb_address		;naturally, address is 0 now
		mov	usb_states,#(USB_RESET_loc-USB_RESET_loc)
		mov	int_states,#(IDLE_loc - IDLE_loc)
		clr	reset_timer		;leave interrupt in idle state
		mov	w,#-int_idle		;update the usb buffer contents
		retiw				;in the main body

BUS_SYNC_loc	=	$
;***********************************************************************************
; BUS SYNCHRONIZATION STATE
; We have to synchronize SX clock with coming data based on sync pattern
; D- lo hi lo hi lo hi lo lo
; D+ hi lo hi lo hi lo hi hi
; This state is over when we catch the start of D- lo
;***********************************************************************************
		mov	packet_rx,#$01		;
		mov	int_states,#(RECEIVE_SYNC_loc-IDLE_loc)
		mov	fsr,#address_lo
		clrb	ind.all_data		;seed with 0 all data buffer
		clrb	ind.ep0_data		;seed with 0 endpoint data buffer
		mov	fsr,#address_hi

:loop1	
		snb	comp_pin		;while output of comp is low, do not care
		jmp	:loop2			;once it is high, start searching for low
		jmp	:loop1
:loop2	
		sb	comp_pin		;catch low of comp_pin
		jmp	BUS_SYNC_x		
		jmp	:loop2		

BUS_SYNC_x
		mov	RTCC,#($FF-$28) 	;IMPORTANT!		
		reti				;to keep synchronized the value
						;in RTCC should be aroud:
						;$FF - #int_active - (3 to 10) cycles
RECEIVE_SYNC_loc	=	$
;***********************************************************************************
; RECEIVE SYCHRONIZATION STATE
; OK the clock is synchronized but we are in the middle of sync pattern
; to get the first data bit lets look for two consecutive low's of D-
;***********************************************************************************
		inc	packet_rx		;once we have two low's
		snb	temp.comp		;it is the last bit of SYNC
		clr	packet_rx
 		mov	w,#(RECEIVE_loc-IDLE_loc)
		snb	flg_setup		;if the prevoius packet was setup
						;receive data
		mov	w,#(RECEIVE_SETUP_loc - IDLE_loc)
		snb	packet_rx.1
		mov	int_states,w
		snb	packet_rx.1
		clr	packet_rx
		mov	buffer_count,#$30	;always start with the firts byte
						;in the buffer
 		mov	w,#-int_active
		retiw	
RECEIVE_loc		=	$
;***********************************************************************************
;  RECEIVE PACKET STATE 
;  The most time hungry state
;  Receive and store all packets, check what packet arrived
;***********************************************************************************
		mov	fsr,buffer_count	;load fsr with the buffer address
		snb	temp.comp
		jmp	:high
:low	
		mov	w,#%00000100		;record received packet
		xor	ind,w			

		snb	ind.all_data		;is one of token packets
		mov	w,#$FF
		xor	w,ind
		or	packet_rx,w

		inc	fsr			;save the state of rb	
		setb	fsr.4			;for the future
		clrb	ind.all_data
		mov	buffer_count,fsr

		mov	w,#-int_active
		snb	ind.sync_bit
		mov	w,#-int_leap
		retiw		

:high
		sb	temp.rx_hi
		jmp	EOP_loc

		clr	w
		snb	ind.all_data
		mov	w,#$FF
		xor	w,ind
		or	packet_rx,w

		inc	fsr			;save the state of rb
		setb	fsr.4
		setb	ind.all_data
		mov	buffer_count,fsr
	
		mov	w,#-int_active
		snb	ind.sync_bit
		mov	w,#-int_leap
		retiw	
RECEIVE_SETUP_loc		=	$
;***********************************************************************************
;  RECEIVE SETUP STATE 
;  Setup packet data, actually there is no need to check what type of packet arrived
;  but we are going to do it anyway just in case one more setup packet arrives
;  EP0 IN buffer is used for OUT as well
;***********************************************************************************
		mov	fsr,buffer_count	;load fsr with the buffer address
		snb	temp.comp
		jmp	:high
:low	
		mov	w,#%00000001		;record received packet
		xor	ind,w			

		snb	ind.ep0_data		;is one of token packets
		mov	w,#$FF
		xor	w,ind
		or	packet_rx,w

		inc	fsr			;save the state of rb	
		setb	fsr.4			;for the future
		clrb	ind.ep0_data
		mov	buffer_count,fsr

		mov	w,#-int_active
		snb	ind.sync_bit
		mov	w,#-int_leap
		retiw		

:high
		sb	temp.rx_hi
		jmp	EOP_loc

		clr	w
		snb	ind.ep0_data
		mov	w,#$FF
		xor	w,ind
		or	packet_rx,w

		inc	fsr			;save the state of rb
		setb	fsr.4
		setb	ind.ep0_data
		mov	buffer_count,fsr
	
		mov	w,#-int_active
		snb	ind.sync_bit
		mov	w,#-int_leap
		retiw	

EOP_loc		=	$
;***********************************************************************************
; END OF PACKET STATE
;***********************************************************************************
		
		;check packets first

		bank	USB_BANK

		sb	packet_rx.setup
		jmp	:setup			   ;setup packet arrived
		
		sb	packet_rx.ep0_out
		jmp	:Endpoint0_out		   ;endpoint 0 out packet arrived

		sb	packet_rx.ep1_in  
   		jmp	:Endpoint1_in

		sb	packet_rx.ep0_in
		jmp	:Endpoint0_in
							
		;no packets identified, check previous packet

		snb	flg_setup
		jmp	:ack_setup		  ; ACK setup

		snb	flg_out	
		jmp	:ack_out		  ; ACK out

		snb	flg_ep0_in
		jmp	:release_ep0		

		snb	flg_ep1_in
		jmp	:release_ep1

				
:no_reply
		mov	w,#(IDLE_loc-IDLE_loc) 	    ;If we can not identify the packet
		mov	int_states,w		    ;keep cool, do not reply
		mov	rtcc,#($FF-$0A)		    ;return to interrupt as fast 
		reti				    ;as possible
:setup
		setb	flg_setup
		mov	w,#(IDLE_loc-IDLE_loc) 	    
		mov	int_states,w		    
		mov	rtcc,#($FF-$0F)		    ;return to interrupt as fast 
		reti				    ;as possible
:Endpoint0_out
		setb	flg_out
		mov	w,#(IDLE_loc-IDLE_loc)
		mov	int_states,w
		mov	rtcc,#($FF-$0A)		    ;return to interrupt as fast 
		reti				    ;as possible
:Endpoint1_in
		sb	flg_ep1_send		    ;anything to send?
		jmp	:nack			    ;if not NACK
		setb	flg_ep1_in		    ;set IN packet flag on
		mov	w,#(TRANSMIT_loc-IDLE_loc)
		mov	int_states,w
		mov	packet_tx,#%11111101	    ;set transmit mask
		clrb	usb_port.oe		    ;enable output
		mov	buffer_count,#$30
		mov	tx_timer,usb_ep1_counter
		mov	rtcc,#($FF-$21)		    ;wait about 1 bit before transmit
		reti			    

:Endpoint0_in
		sb	flg_ep0_send		    ;anything to send?
		jmp	:nack			    ;if not, NACK
		setb	flg_ep0_in		    ;set IN packet flag on
		mov	w,#(TRANSMIT_loc-IDLE_loc)
		mov	int_states,w
		mov	packet_tx,#%11111110	    ;set transmit mask
		clrb	usb_port.oe		    ;enable output
		mov	buffer_count,#$30
		mov	tx_timer,usb_ep0_counter
		mov	rtcc,#($FF-$21)		    ;wait about 1 bit before transmit
		reti
:ack_setup
		clrb	flg_setup
		mov	w,#(USB_SETUP_loc - USB_RESET_loc)
		mov	usb_states,w		
		jmp	:ack
:ack_out
		clrb	flg_out			    ;that's where have to check what
		jmp	:ack			    ;out packet data packet is about
						    ;in our case it would be just 
						    ;some configuration settings so 
						    ;so ACK them and skip without a second
						    ;thought
:release_ep0
		clrb	flg_ep0_send	            ;that's where we have to check properly
		clrb	flg_ep0_in		    ;if ACK from the host is received
		jmp	:no_reply		    ;but lets just skip it
:release_ep1
		clrb	flg_ep1_send
		clrb	flg_ep1_in
		jmp	:no_reply
:nack
		mov	w,#(TRANSMIT_loc-IDLE_loc)
		mov	int_states,w
		mov	buffer_count,#$78
		mov	packet_tx,#%11011111
		mov	tx_timer,#$10		    ;NACK is only 16 bit long
		mov	rtcc,#($FF-$21)		    ;wait about 1 bit before transmit
		reti
:ack
		mov	w,#(TRANSMIT_loc-IDLE_loc)
		mov	int_states,w
		mov	buffer_count,#$78
		mov	packet_tx,#%10111111
		mov	tx_timer,#$10		    ;ACK is only 16 bit long
		mov	rtcc,#($FF-$42)		    ;wait about 2 bit before transmit
		reti	

	
TRANSMIT_loc	=	$	
;***********************************************************************************
; TRANSMIT STATE
;***********************************************************************************
		clrb	usb_port.oe		    ;enable output
		mov	fsr,buffer_count
		mov	w,/packet_tx
		and	w,ind
		sz
		jmp	:dont_toggle_bus
		xor	usb_port,#%00110000	     ;tx_lo	equ	4	
						     ;tx_hi	equ	5

:dont_toggle_bus
		mov	w,#(EOP_TRANSMIT_loc-IDLE_loc)
		dec	tx_timer
		snz
		mov	int_states,w

		inc	buffer_count
		setb	buffer_count.4	
		mov	w,#-int_active
		snb	ind.sync_bit
		mov	w,#-int_leap
		retiw		

EOP_TRANSMIT_loc	=	$
;***********************************************************************************
; EOP TRANSMIT STATE
;***********************************************************************************
		clrb	usb_port.tx_hi			;hold SE0 for 2 bits
		clrb	usb_port.tx_lo			;it is one of the traps
							;once eop is > 3 bits
							;host will not accept the 
		mov	w,#(END_TRANSMIT_loc-IDLE_loc)	;packet
		snb	tx_timer.0	
		mov	int_states,w
		inc	tx_timer
		mov	w,#-int_active
		retiw


END_TRANSMIT_loc
		setb	usb_port.tx_hi
		mov	w,#(IDLE_loc - IDLE_loc)
		mov	int_states,w
		setb	usb_port.oe		;disable output		
		mov	rtcc,#($FF-$0F)		;return to interrupt as fast as
		reti				;possible


;***********************************************************************************
; TOKEN
; The macro calls supporting function Token to fill in
; token buffers based on the current usb_address
;***********************************************************************************
TOKEN		MACRO	3
		mov	usb_mask,#\1		;set the buffer
		mov	usb_temp,#\2		;set the value of PID
		mov	usb_hold,#\3		;hold the endpoint number
		call	Set_token
		ENDM
;***********************************************************************************
; HANDSHAKE
; To fill respective USB buffers with handshake packets
;***********************************************************************************
HANDSHAKE	MACRO	2
;**********************************************************************************
; PID
;**********************************************************************************
	
		mov	usb_mask,#\1
		mov	usb_hold,#\2		
		call	Set_handshake
		ENDM
	
;**********************************************************************************
; Start
;**********************************************************************************
Start
 		mov	ra,#%0000	
		mov	rc,#%00000000
		mov	!ra,#%11111110 		;all input	
		mov	!rc,#%11111111 		;all input

		mov	!rb,#usb_port_tris 	
		clrb	usb_port.tx_lo
		setb	usb_port.tx_hi
		setb	usb_port.oe

		mode	$08
		mov	!rb,#$00      		;enable comparator
		mode	$0F

		
		clr	fsr			;reset all ram banks
:zero_ram	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram
	
		mov	fsr,#$30		;seed the bit stream
		
:bit_stream	
		setb	fsr.4	
		setb	ind.sync_bit
		add	fsr,#4
		sc
		jmp	:bit_stream

		bank	USB_BANK
		mov	usb_states,#(USB_INTERRUPT_loc - USB_RESET_loc)
		mov	int_states,#(BUS_RESET_loc - IDLE_loc)
		mov	!OPTION,#%10001000	;Enable RTCC rollover interrupt
 						;RTCC inc on clock cycle, no prescaler
		jmp	@Main

		org	$200
;**********************************************************************************
; MAIN
; state machine
; 1. RESET - initialise all buffers
; 2. SETUP packet received - get ready to process it
; 3. INTERRUPT transfer 
;**********************************************************************************
Main
		mov	w,usb_states
		add	PC,w

USB_RESET_loc		=	$
		jmp	_USB_RESET
USB_SETUP_loc		=	$
		jmp	_USB_SETUP
USB_INTERRUPT_loc	=	$
		jmp	_USB_INTERRUPT
;**********************************************************************************
; CONTROL
; state machine
;**********************************************************************************

USB_CONTROL_loc		=	$
		bank	USB_BANK
		mov	w,usb_sub_states
		add	PC,w	
REQUEST_loc		=	$
		jmp	_REQUEST
STANDARD_Req_loc	=	$
		jmp	_STANDARD_Req
ADDRESSED_loc		=	$
		jmp	_ADDRESSED
DESCRIPTOR_loc		=	$
		jmp	_DESCRIPTOR
LOAD_loc		=	$
		jmp	_LOAD
CLASS_Req_loc		=	$
		jmp	_CLASS


;**********************************************************************************
; SUPPORTING FUNCTIONS 
; Jump tabel
;**********************************************************************************
Bit_push
		jmp	_Bit_push
Bit_pull	
		jmp	_Bit_pull
Crc5_calculate	
		jmp	_Crc5_calculate		
Crc16_calculate	
		jmp	_Crc16_calculate
Set_token
		jmp	_Set_token
Set_handshake
		jmp	_Set_handshake
Load_ep0_0	
		jmp	_Load_ep0_0
Load_ep0
		jmp	@_Load_ep0
Load_ep1
		jmp	@_Load_ep1
	
;**********************************************************************************
_USB_RESET
								;Based on the current usb_address
								;fill in:
		TOKEN	%10111111,%01101001,%00000001		;Endpoint 1 IN packet
		TOKEN	%11011111,%01101001,%00000000		;Endpoint 0 IN packet
		TOKEN	%11101111,%11100001,%00000000		;Endpoint 0 OUT packet
		TOKEN	%11110111,%00101101,%00000000		;SETUP packet
		HANDSHAKE %10111111,%11010010			;ACK
		HANDSHAKE %11011111,%01011010			;NACK
		;HANDSHAKE %11101111,%00011110			;STALL
		mov	usb_states,#(USB_INTERRUPT_loc - USB_RESET_loc)
		jmp	Main
_USB_SETUP
		mov	usb_states,#(USB_CONTROL_loc - USB_RESET_loc)
		mov	usb_sub_states,#(REQUEST_loc - REQUEST_loc)
		jmp	Main		
_REQUEST		
		mov	usb_buffer,#$38		;skip PID
		mov	usb_bit_stuff,#$06	;set bit stuffing
		mov	usb_mask,#%11111110	;ep0_in buffer
		mov	usb_bit_count,#$08	;always one byte
		setb    flg_ep0_data		;after setup ep0 responds with DATA 1
		call	Bit_pull
		mov	w,#(STANDARD_Req_loc - REQUEST_loc)
		snb	usb_temp.5		;if bit 5 = 0 - standard request
						;if bit 5 = 1 - class request
		mov	w,#(CLASS_Req_loc - REQUEST_loc)
		mov	usb_sub_states,w
		jmp	Main	
	
_STANDARD_Req
		mov	usb_bit_count,#$08
		call	Bit_pull
		cje	usb_temp,#$05,:set_address
		cje	usb_temp,#$06,:get_descriptor
						;all other requests would be acknowledged 
		call	Load_ep0_0		
		mov	usb_states,#(USB_INTERRUPT_loc - USB_RESET_loc)
		jmp	Main
:set_address
		mov	usb_bit_count,#$08	;get the new USB address
		call	Bit_pull
		mov	usb_address,usb_temp	;save the new address		
		call	Load_ep0_0
		mov	usb_sub_states,#(ADDRESSED_loc - REQUEST_loc)
		jmp	Main
:get_descriptor	
		mov	usb_sub_states,#(DESCRIPTOR_loc - REQUEST_loc)
		jmp	Main

_ADDRESSED
		mov	w,#(USB_RESET_loc - USB_RESET_loc)
		sb	flg_ep0_send		;once the status stage is over
						;update all buffers with new address
						;token and handshake packets
		mov	usb_states,w
		jmp	Main	

_DESCRIPTOR
		mov	usb_bit_count,#$08	;as descriptor number is in the 4th byte
		call	Bit_pull		;we have to skip one byte
		mov	usb_bit_count,#$08
		call	Bit_pull
						;standard
		cje	usb_temp,#$01,:device	
		cje	usb_temp,#$02,:configuration
						;class specific

		cje	usb_temp,#$22,:report	
						;other descriptors are not supported
		call	Load_ep0_0		
		mov	usb_states,#(USB_INTERRUPT_loc - USB_RESET_loc)		
		jmp	Main
:device
		mov	usb_ep0_table_s,#(Device_ds - EP0_table_s)
		mov	usb_ep0_table_e,#(Device_de - EP0_table_s)
		mov	usb_sub_states,#(LOAD_loc - REQUEST_loc)
		jmp	Main
:configuration
		mov	usb_bit_count,#$08	;skip one byte	
		call	Bit_pull
		mov	usb_bit_count,#$08	;skip one more byte	
		call	Bit_pull
		mov	usb_bit_count,#$08	;as descriptor size is in the 7th byte
		call	Bit_pull
		cje	usb_temp,#$09,:basic    ;if the given size is only 9 byte,
						;load basic descriptor
						;otherwise load full
:full
		mov	usb_ep0_table_s,#(Config_ds - EP0_table_s)
		mov	usb_ep0_table_e,#(Endpoint_de - EP0_table_s)
		mov	usb_sub_states,#(LOAD_loc - REQUEST_loc)
		jmp	Main
:basic
		mov	usb_ep0_table_s,#(Config_ds - EP0_table_s)
		mov	usb_ep0_table_e,#(Config_de - EP0_table_s)
		mov	usb_sub_states,#(LOAD_loc - REQUEST_loc)
		jmp	Main
:report
		mov	usb_ep0_table_s,#(Report_ds - EP0_table_s)
		mov	usb_ep0_table_e,#(Report_de - EP0_table_s)
		mov	usb_sub_states,#(LOAD_loc - REQUEST_loc)
		jmp	Main
_LOAD
		call	Load_ep0
		cjb	usb_ep0_table_s,usb_ep0_table_e,Main
		mov	usb_states,#(USB_INTERRUPT_loc - USB_RESET_loc)
		jmp	Main
		
_CLASS			=	$
		mov	usb_bit_count,#$08
		call	Bit_pull
		cjne	usb_temp,#$09,:nothing	;if it is set_report request
						;send the song
		mov	usb_ep1_table_s,#(Hello_s - EP1_table_s)
		mov	usb_ep1_table_e,#(Hello_e - EP1_table_s)
:nothing
		call	Load_ep0_0		;class requests are not supported
						;just acknowledged
		mov	usb_states,#(USB_INTERRUPT_loc - USB_RESET_loc)
		jmp	Main

_USB_INTERRUPT		=	$
		call	Load_ep1
		jmp	Main


;**********************************************************************************
; Bit_push
; Supporting function to store data in endpoint 0 and endpoint 1 buffers
; for transmission to host (buffer mask is %11111110 and %11111101)
; usb_temp is the source of data
; usb_bit_count is the number of bits to store (8 for data, 7 for address, etc)
; usb_buffer is the address from which to start
; Bit stuffing is provided
;**********************************************************************************
_Bit_push
		sb	usb_mask.0
		inc	usb_ep0_counter
		sb	usb_mask.1
		inc	usb_ep1_counter

		test	usb_bit_stuff
		sz
		jmp	:bit_push_resume
		sb	usb_mask.0
		inc	usb_ep0_counter			;one more bit
		sb	usb_mask.1
		inc	usb_ep1_counter
		mov	usb_bit_stuff,#$06
		mov	w,usb_mask
		mov	fsr,usb_buffer
		and	ind,w
		bank	USB_BANK
		inc	usb_buffer
		setb	usb_buffer.4
:bit_push_resume
		rr	usb_temp
		sc
		jmp	:zero_bit
:one_bit
		dec	usb_bit_stuff
		mov	fsr,usb_buffer
		mov	w,/usb_mask
		or	ind,w
		jmp	Bit_push_x
:zero_bit
		mov	usb_bit_stuff,#$06
		mov	fsr,usb_buffer
		mov	w,usb_mask
		and	ind,w
Bit_push_x
	
		bank	USB_BANK
		inc	usb_buffer
		setb	usb_buffer.4
		decsz	usb_bit_count



		jmp	_Bit_push
		retp
;**********************************************************************************
; Bit_pull
; Retrieve data from receive buffer (buffer mask %11111110).
; Due to our device configuration only endpoint 0 supports OUT
; packets. This function gets rid of bit stuffing and returns in
; usb_temp the number of bits specified in usb_bit_count
; usb_buffer is the address of the first bit to start with.
;**********************************************************************************

_Bit_pull
		test	usb_bit_stuff
		sz
		jmp	:bit_pull_resume
		mov	usb_bit_stuff,#$06
		inc	usb_buffer
		setb	usb_buffer.4
:bit_pull_resume
		mov	fsr,usb_buffer
		mov	w,/usb_mask
		and	w,ind	
		bank	USB_BANK
		snz
		jmp	:zero_bit
:one_bit
		dec	usb_bit_stuff
		stc
		rr	usb_temp
		jmp	Bit_pull_x
:zero_bit
		mov	usb_bit_stuff,#$06
		clc
		rr	usb_temp

Bit_pull_x
		inc	usb_buffer
		setb	usb_buffer.4
		decsz	usb_bit_count
		jmp	_Bit_pull
		retp

;**********************************************************************************
; Crc5_calculate, the MSb is in usb_crc5.0
;**********************************************************************************
_Crc5_calculate 
		clc
		rr	usb_crc5
		mov	w,#%00010100
		snb	usb_temp.0
		jmp	:one_bit
:zero_bit
		snc		
		xor	usb_crc5,w
		jmp	Crc5_calculate_x
:one_bit
		sc	
		xor	usb_crc5,w

Crc5_calculate_x
		rr	usb_temp
		decsz	usb_bit_count		
		jmp	_Crc5_calculate	
		retp

;**********************************************************************************
; Crc16_calculate, the MSb is in usb_crc16_lo.0
;**********************************************************************************
_Crc16_calculate 
		clc
		rr	usb_crc16_hi
		rr	usb_crc16_lo
		mov	w,#%10100000
		snb	usb_temp.0
		jmp	:one_bit
:zero_bit
		snc		
		xor	usb_crc16_hi,w
		mov	w,#%00000001
		snc
		xor	usb_crc16_lo,w

		jmp	Crc16_calculate_x
:one_bit
		sc		
		xor	usb_crc16_hi,w
		mov	w,#%00000001
		sc
		xor	usb_crc16_lo,w

Crc16_calculate_x
		rr	usb_temp
		decsz	usb_bit_count		
		jmp	_Crc16_calculate	
		retp


;**********************************************************************************
; Set_token
;**********************************************************************************
 _Set_token
		mov	usb_buffer,#address_lo	;all token buffers start at address_lo
		mov	usb_bit_stuff,#$06	;initialise bit stuffing
		mov	usb_bit_count,#$08	;PID is always 8-bit long
		mov	usb_crc5,#%00011111	;seed crc5 with all 1's
		call	Bit_push
;**********************************************************************************
; Address
; as it belongs to the data field, calculate crc
;**********************************************************************************
		mov	usb_temp,usb_address
		mov	usb_bit_count,#$07	;address is only 7-bit long
		call	Crc5_calculate		;start calculating Crc5
		mov	usb_temp,usb_address	
		mov	usb_bit_count,#$07
		call	Bit_push		;fill in the buffer
;**********************************************************************************
; Endpoint
;**********************************************************************************
		mov	usb_temp,usb_hold	;set the endpoint number
		mov	usb_bit_count,#$04	;the number is 4-bit long
		call	Crc5_calculate
		mov	usb_temp,usb_hold
		mov	usb_bit_count,#$04
		call	Bit_push
;**********************************************************************************
; Crc5
;**********************************************************************************
		xor	usb_crc5,#$FF		;invert CRC5
		mov	usb_temp,usb_crc5
		mov	usb_bit_count,#$05
		call	Bit_push
		mov	fsr,usb_buffer
		mov	w,/usb_mask		;why don't we add 1's
		or	ind,w			;in case EOP is detected late
		inc	fsr
		setb	fsr.4
		or	ind,w
		bank	USB_BANK
		retp

;**********************************************************************************
; Set_handshake
;**********************************************************************************
_Set_handshake
;**********************************************************************************
; Synchronization
;**********************************************************************************
		mov	usb_buffer,#$78		;the actual handshake starts from
						;address_hi, however we save sync pattern
						;starting $78, because we need to send 
						;handshakes too
		mov	usb_bit_stuff,#$06	;nobody needs it for handshake, too small

		mov	usb_temp,#%10000000	;sync
		mov	usb_bit_count,#$08
		call	Bit_push
		mov	usb_bit_count,#$08	;always 8
		mov	usb_temp,usb_hold
;**********************************************************************************
; Pid
;**********************************************************************************
		call	Bit_push
		mov	fsr,usb_buffer
		mov	w,/usb_mask		;why don't we add 1's
		or	ind,w			;in case EOP is detected late
		inc	fsr
		setb	fsr.4
		or	ind,w
		bank	USB_BANK


		retp

;**********************************************************************************
; Load_ep0_0
; load IN buffer for endpoint 0 with 0 bytes of data
;**********************************************************************************
_Load_ep0_0
		mov	usb_buffer,#address_lo	;the actual handshake starts from
		mov	usb_bit_stuff,#$06	
		mov	usb_mask,#%11111110
		mov	usb_temp,#%10000000	;sync
		mov	usb_bit_count,#$08
		clr	usb_ep0_counter
		call	Bit_push
		mov	usb_bit_count,#$08
		mov	usb_temp,#%01001011	;always DATA1 pid
		call	Bit_push
		mov	usb_bit_count,#$08
		mov	usb_temp,#%00000000	;crc16 inverted
		call	Bit_push
		mov	usb_bit_count,#$08
		mov	usb_temp,#%00000000	;crc16 inverted
		call	Bit_push
		setb	flg_ep0_send		;there is something to send
		retp	
		
		org	$500
;**********************************************************************************
; Load_ep0
; load IN buffer for endpoint 0 with data from the table
; starting address usb_ep0_table_s 
;**********************************************************************************
_Load_ep0
;**********************************************************************************
; Synchronization
; is first
;**********************************************************************************
		cjae	usb_ep0_table_s,usb_ep0_table_e,_Load_ep0_x
		snb	flg_ep0_send
		jmp	_Load_ep0_x		;do not load anything if send flag 
						;is set or there is nothing to send
		mov	usb_ep0_byte,#$09	;set the number of bytes counter
		mov	usb_buffer,#address_lo
		mov	usb_bit_stuff,#$06
		mov	usb_bit_count,#$08	;always 8
		mov	usb_crc16_hi,#$FF	;seed crc16 registers
		mov	usb_crc16_lo,#$FF
		mov	usb_temp,#%10000000	;start with sync	
		mov	usb_mask,#%11111110	;set to IN endpoint 0 buffer
		clr	usb_ep0_counter		

		call	@Bit_push		
;**********************************************************************************
; Pid
; Data 0/1
;**********************************************************************************		
		mov	w,#%11000011		
		snb	flg_EP0_data
		mov	w,#%01001011
		mov	usb_temp,w
		xor	usb_flags,#%01000000	;toggle DATA 1/0
		mov	usb_bit_count,#$08
		call	@Bit_push		;DATA 1/0
;**********************************************************************************
; 8 or less bytes
; of real data
;**********************************************************************************
:data
		cjae	usb_ep0_table_s,usb_ep0_table_e,:crc16
		dec	usb_ep0_byte
		snz
		jmp	:crc16
		mov	w,usb_ep0_table_s
		call	EP0_table
		mov	usb_temp,w
		mov	usb_bit_count,#$08
		call	@Crc16_calculate
		mov	w,usb_ep0_table_s
		call	EP0_table
		mov	usb_temp,w
		mov	usb_bit_count,#$08
		call	@Bit_push
		inc	usb_ep0_table_s
		jmp	:data
;**********************************************************************************
; 2 bytes
; of CRC
;**********************************************************************************
:crc16
		mov	usb_temp,usb_crc16_lo
		xor	usb_temp,#$FF
		mov	usb_bit_count,#$08
		call	@Bit_push
		mov	usb_temp,usb_crc16_hi
		xor	usb_temp,#$FF
		mov	usb_bit_count,#$08
		call	@Bit_push
		setb	flg_ep0_send		;set flag for sending
		retp
_Load_ep0_x
		retp	

		org	$700
;**********************************************************************************
; Load_ep1
; load IN buffer for endpoint 0 with data from the table
; starting address usb_ep1_table_s 
;**********************************************************************************
_Load_ep1
;**********************************************************************************
; Synchronization
; is first
;**********************************************************************************

		snb	flg_ep1_send
		jmp	_Load_ep1_x			;do not load anything if send flag 
						;is set or there is nothing to send
		mov	usb_buffer,#address_lo
		mov	usb_bit_stuff,#$06
		mov	usb_bit_count,#$08	;always 8
		mov	usb_crc16_hi,#$FF	;seed crc16 registers
		mov	usb_crc16_lo,#$FF
		mov	usb_temp,#%10000000	;start with sync	
		mov	usb_mask,#%11111101	;set usb_mask to IN endpoint 1 buffer
		clr	usb_ep1_counter		
		call	@Bit_push		
;**********************************************************************************
; Pid
; Data 0/1
;**********************************************************************************
		clr	usb_hold
		mov	usb_temp,#%11000011
		sb	flg_EP1_data
		jmp	:data0
:data1						;for data 1 packet send make code
	 	cjae	usb_ep1_table_s,usb_ep1_table_e,_Load_ep1_x
		mov	w,usb_ep1_table_s
		call	EP1_table
		mov	usb_hold,w
		inc	usb_ep1_table_s
		mov	usb_temp,#%01001011
:data0						;for data 0 packet send break code
		mov	usb_bit_count,#$08
		xor	usb_flags,#%10000000	;toggle DATA 1/0
		call	@Bit_push		;DATA 1/0
;**********************************************************************************
; Always 8 bytes 
; of real data
;**********************************************************************************
DATABYTE	MACRO
		clr	usb_temp
		mov	usb_bit_count,#$08
		call	@Crc16_calculate
		clr	usb_temp
		mov	usb_bit_count,#$08
		call	@Bit_push
		ENDM
		
		DATABYTE			;byte 0 - $00
		DATABYTE			;byte 1 - $00
		mov	usb_temp,usb_hold	;byte 2 - $00 for DATA 0
		mov	usb_bit_count,#$08	;	  value from EP1_table
		call	@Crc16_calculate	;	  for DATA 1	
		mov	usb_temp,usb_hold
		mov	usb_bit_count,#$08
		call	@Bit_push
		DATABYTE			;byte 3 - $00
		DATABYTE			;byte 4 - $00
		DATABYTE			;byte 5	- $00
		DATABYTE			;byte 6	- $00
		DATABYTE			;byte 7 - $00
		
;**********************************************************************************
; 2 bytes
; of CRC
;**********************************************************************************
:crc16
		mov	usb_temp,usb_crc16_lo
		xor	usb_temp,#$FF
		mov	usb_bit_count,#$08
		call	@Bit_push
		mov	usb_temp,usb_crc16_hi
		xor	usb_temp,#$FF
		mov	usb_bit_count,#$08
		call	@Bit_push
		setb	flg_ep1_send		;set flag for sending
		retp
_Load_ep1_x
		retp

		org	$400
EP0_table
		jmp	PC+w

EP0_table_s	=	$
		retw	$80
		retw	$06
		retw	$00
		retw	$01
		retw	$00
		retw	$00
		retw	$40
		retw	$00

Device_ds	=	$
		retw	(Device_de - Device_ds)		;length
		retw	$01				;type
		retw	$00,$01 			;USB specifications 1.0	
		retw	$00				;class code
		retw	$00				;subclass code
		retw	$00				;protocol	
		retw	$08				;max.packet size
		retw	$88,88				;vendor ID	
		retw	$02,00				;product ID
		retw    $01,00				;device release number
		retw	$00				;manufacturer string descriptor index
		retw	$00				;product string descriptor index
		retw	$00				;serial number string descriptor
		retw	$01				;number of possible configurations	
Device_de	=	$
Config_ds	=	$
		retw	(Config_de - Config_ds)		;length
		retw	$02				;type
		retw	(Endpoint_de - Config_ds),$00	;total data length
		retw	$01				;interface supported
		retw	$01				;configuration value
		retw	$00				;string descriptor index
		retw	$A0				;configuration
		retw	$32				;maximum power consumption
Config_de	=	$
Inter_ds	=	$
		retw	(Inter_de - Inter_ds)		;length
		retw	$04				;type
		retw	$00				;number of interfaces
		retw	$00				;alternate settings
		retw	$01				;number of endpoints
		retw	$03				;class code
		retw	$01				;subclass code
		retw	$01				;protocol code
		retw	$00				;string index
Inter_de	=	$
Class_ds	=	$
		retw	(Class_de - Class_ds)		;length
		retw	$21				;type
		retw	$00,$01				;HID class release number	
		retw	$00				;country code
		retw	$01				;number of HID descriptors to follow
		retw	$22				;report descriptor type
		retw	(Report_de - Report_ds),$00	;total length of report descriptor
Class_de	=	$
Endpoint_ds	=	$
		retw	(Endpoint_de - Endpoint_ds)	;length
		retw	$05				;type
		retw	$81				;encoded address (IN to endpoint 1)
		retw	$03				;endpoint attribute
		retw	$08,$00				;max. packet size
		retw	$0A				;polling interval
Endpoint_de	=	$
;**********************************************************************************
; Generic USB keyboard report, as give at page 70 of
; Device Class Definition for Human Interface Devices (HID)
;**********************************************************************************
Report_ds	=	$
		retw	$05,$01				;generic desktop
		retw	$09,$06				;keyboard
		retw	$A1,$01				;collection (application)
		retw	$05,$07				;key codes
		retw	$19,$E0				;usage minimum
		retw	$29,$E7				;usage maximum
		retw	$15,$00				;logical minimum
		retw	$25,$00				;logical maximum
		retw	$75,$01				;report size
		retw	$95,$08				;report count (8 bytes)
		retw	$81,$02				;input (data, variable, absolute)
		retw	$95,$01				;report count
		retw	$75,$08				;report size
		retw	$81,$01				;input (constant)
		retw	$95,$05				;report count
		retw	$75,$01				;report size
		retw	$05,$08				;leds
		retw	$19,$01				;usage minimum
		retw	$29,$05				;usage maximum
		retw	$91,$02				;output (data, variable, absolute)
		retw	$95,$03				;report count
		retw	$75,$01				;report size
		retw	$91,$01				;output (constant)
		retw	$95,$06				;report count
		retw	$75,$08				;report size
		retw	$15,$00				;logical minimum
		retw	$25,$65				;logical maximum
		retw	$05,$07				;key codes
		retw	$19,$00				;usage minimum
		retw	$29,$65				;usage maximum
		retw	$81,00				;input
		retw	$C0				;end collection
Report_de	=	$

		org	$600
EP1_table
		jmp	PC+w
EP1_table_s	=	$
Hello_s		=	$
		retw	$28
			;To my parents
		retw	$17,$12,$2C,$10,$1C,$2C,$13,$04,$15,$08,$11,$17,$16,$28,$28

			;All the leaves are brown and the sky is gray
		retw	$04,$0F,$0F,$2C,$17,$0B,$08,$2C,$0F,$08,$04,$19,$08,$16,$2C
		retw	$04,$15,$08,$2C,$05,$15,$12,$1A,$11,$2C,$04,$11,$07,$2C
		retw	$17,$0B,$08,$2C,$16,$0E,$1C,$2C,$0C,$16,$2C,$0A,$15,$04,$1C,$28

			;I've been for a walk on a winter's day
		retw	$0C,$34,$19,$08,$2C,$05,$08,$08,$11,$2C,$09,$12,$15,$2C,$04,$2C,$1A,$04,$0F,$0E,$2C
		retw	$12,$11,$2C,$04,$2C,$1A,$0C,$11,$17,$08,$15,$34,$16,$2C,$07,$04,$1C,$28

			;I'd be safe and warm if I was in L.A.
		retw	$0C,$34,$07,$2C,$05,$08,$2C,$16,$04,$09,$08,$2C,$04,$11,$07,$2C
		retw	$1A,$04,$15,$10,$2C,$0C,$09,$2C,$0C,$2C,$1A,$04,$16,$2C
		retw	$0C,$11,$2C,$0F,$37,$04,$37,$28

			;California dreamin' on such a winter's day
		retw	$06,$04,$0F,$0C,$09,$12,$15,$11,$0C,$04,$2C,$07,$15,$08,$04,$10,$0C,$11,$34,$2C
		retw	$12,$11,$2C,$16,$18,$06,$0B,$2C,$04,$2C,$1A,$0C,$11,$17,$08,$15,$34,$16,$2C
		retw	$07,$04,$1C,$28

				
Hello_e		=	$

Questions:


file: /Techref/scenix/lib/io/dev/keys/usbdemo-mh.htm, 38KB, , updated: 2005/3/25 17:07, local time: 2024/11/20 01:34, owner: MDH-pacific-468,
TOP NEW HELP FIND: 
3.14.134.31:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://linistepper.com/Techref/scenix/lib/io/dev/keys/usbdemo-mh.htm"> SX Keyboard IO - California Dreamin SX USB Keyboard Demo</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?