;***************************************************************************
;* U S B S T A C K F O R T H E A V R F A M I L Y
;*
;* File Name :"MJoy.asm"
;* Title :MJoy - USB Joystick based on USB stack
;* Date :1.17.2004
;* Version :1.2
;* Target MCU :ATmega8
;* Assembler :Atmel AVR Studio
;* AUTHOR : Mindaugas Milasauskas
;* Lithuania
;* [email protected]
;* http://www.mindaugas.com
;* Based on code of:
;* Ing. Igor Cesko
;* Slovakia
;* [email protected]
;* http://www.cesko.host.sk
;*
;* DESCRIPTION:
;* USB protocol implementation into MCU with noUSB interface:
;* Device:
;* MJoy USB Joystick
;*
;* The timing is adapted for 12 MHz crystal
;*
;*
;* Release Notes:
;*
;* v1.2: Second public release
;* 6 axis (4 x 10 bit + 2 x 8 bit), 24 buttons, 1 hat (uses 4 buttons)
;* Added autocalibration
;* Changed layout of HID descriptor
;* Changed USB Vendor ID to make MJoy appear on top of the game devices list
;* Optimized HatSwitch Code
;* Rudder and Throttle axis ar now 10bit
;* Changed axis Dial, Slider to Rx, Z
;* Optimized partially non-blocking ADC reading
;*
;*
;* v1.1: First public release
;* 6 axis - 2x10bit axis + 4x8bit axis
;* 24 buttons
;* 1 hat (uses 4 buttons)
;*
;* v1.0: Very first lab release with 2x10bit axis and 4 buttons
;*
;*
;***************************************************************************
.include "m8def.inc"
.equ UCR =UCSRB
.equ UBRR =UBRRL
.equ EEAR =EEARL
.equ USR =UCSRA
.equ E2END =127
.equ RAMSIZE =159
.equ RAMEND_MJ =255
.equ inputport =PINB
.equ outputport =PORTB
.equ USBdirection =DDRB
.equ DATAplus =1 ;signal D+ on PB1
.equ DATAminus =0 ;signal D- on PB0 - give on this pin pull-up 1.5kOhm
.equ USBpinmask =0b11111100 ;mask low 2 bit (D+,D-) on PB
.equ USBpinmaskDplus =~(1<>8 ;to bitcount CRC polynomial - high byte
eor temp1,bitcount ;and make XOR from remains and CRC polynomial - high byte
ldi bitcount,CRC16poly ;to bitcount CRC polynomial - low byte
eor temp0,bitcount ;and make XOR of remainder and CRC polynomial - low byte
CRC16NoXOR:
dec temp3 ;were already all bits in byte
brne CRC16LoopByte ;unless, then go to next bit
cp USBBufptrY,ByteCount ;was already end-of-message
brne CRC16Loop ;unless then repeat
CRC16End:
ret ;otherwise finish (in temp0 and temp1 is result)
;------------------------------------------------------------------------------------------
LoadDescriptorFromROM:
lpm ;load from ROM position pointer to R0
st Y+,R0 ;R0 save to buffer and increment buffer
adiw ZH:ZL,1 ;increment index to ROM
dec ByteCount ;till are not all bytes
brne LoadDescriptorFromROM ;then load next
rjmp EndFromRAMROM ;otherwise finish
;------------------------------------------------------------------------------------------
LoadDescriptorFromROMZeroInsert:
lpm ;load from ROM position pointer to R0
st Y+,R0 ;R0 save to buffer and increment buffer
bst RAMread,3 ;if bit 3 is one - don't insert zero
brtc InsertingZero ;otherwise zero will be inserted
adiw ZH:ZL,1 ;increment index to ROM
lpm ;load from ROM position pointer to R0
st Y+,R0 ;R0 save to buffer and increment buffer
clt ;and clear
bld RAMread,3 ;the third bit in RAMread - for to the next zero insertion will be made
rjmp InsertingZeroEnd ;and continue
InsertingZero:
clr R0 ;for insertion of zero
st Y+,R0 ;zero save to buffer and increment buffer
InsertingZeroEnd:
adiw ZH:ZL,1 ;increment index to ROM
subi ByteCount,2 ;till are not all bytes
brne LoadDescriptorFromROMZeroInsert ;then load next
rjmp EndFromRAMROM ;otherwise finish
;------------------------------------------------------------------------------------------
LoadDescriptorFromSRAM:
ld R0,Z ;load from position RAM pointer to R0
st Y+,R0 ;R0 save to buffer and increment buffer
adiw ZH:ZL,1 ;increment index to RAM
dec ByteCount ;till are not all bytes
brne LoadDescriptorFromSRAM ;then load next
rjmp EndFromRAMROM ;otherwise finish
;------------------------------------------------------------------------------------------
LoadDescriptorFromEEPROM:
out EEARL,ZL ;set the address EEPROM Lo
out EEARH,ZH ;set the address EEPROM Hi
sbi EECR,EERE ;read EEPROM to register EEDR
in R0,EEDR ;load from EEDR to R0
st Y+,R0 ;R0 save to buffer and increment buffer
adiw ZH:ZL,1 ;increment index to EEPROM
dec ByteCount ;till are not all bytes
brne LoadDescriptorFromEEPROM;then load next
rjmp EndFromRAMROM ;otherwise finish
;------------------------------------------------------------------------------------------
LoadXXXDescriptor:
ldi temp0,SOPbyte ;SOP byte
sts OutputBufferBegin,temp0 ;to begin of tramsmiting buffer store SOP
ldi ByteCount,8 ;8 byte store
ldi USBBufptrY,OutputBufferBegin+2 ;to transmitting buffer
and RAMread,RAMread ;if will be reading from RAM or ROM or EEPROM
brne FromRAMorEEPROM ;0=ROM,1=RAM,2=EEPROM,4=ROM with zero insertion (string)
FromROM:
rjmp LoadDescriptorFromROM ;load descriptor from ROM
FromRAMorEEPROM:
sbrc RAMread,2 ;if RAMREAD=4
rjmp LoadDescriptorFromROMZeroInsert ;read from ROM with zero insertion
sbrc RAMread,0 ;if RAMREAD=1
rjmp LoadDescriptorFromSRAM ;load data from SRAM
rjmp LoadDescriptorFromEEPROM ;otherwise read from EEPROM
EndFromRAMROM:
sbrc RAMread,7 ;if is most significant bit in variable RAMread=1
clr RAMread ;clear RAMread
rcall ToggleDATAPID ;change DATAPID
ldi USBBufptrY,OutputBufferBegin+1 ;to transmitting buffer - position of DATA PID
ret
;------------------------------------------------------------------------------------------
PrepareUSBOutAnswer: ;prepare answer to buffer
rcall PrepareUSBAnswer ;prepare answer to buffer
MakeOutBitStuff:
inc BitStuffInOut ;transmitting buffer - insertion of bitstuff bits
ldi USBBufptrY,OutputBufferBegin ;to transmitting buffer
rcall BitStuff
mov OutputBufferLength,ByteCount ;length of answer store for transmiting
clr BitStuffInOut ;receiving buffer - deletion of bitstuff bits
ret
;------------------------------------------------------------------------------------------
PrepareUSBAnswer: ;prepare answer to buffer
clr RAMread ;zero to RAMread variable - reading from ROM
lds temp0,InputBufferBegin+2 ;bmRequestType to temp0
lds temp1,InputBufferBegin+3 ;bRequest to temp1
cbr temp0,0b10011111 ;if is 5 and 6 bit zero
brne CheckVendor ;then this isn't Vendor Request
rjmp StandardRequest ;but this is standard Request
CheckVendor:
cpi temp0, 0b01000000
breq VendorRequest
cpi temp0, 0b00100000
breq ClassRequest
rjmp VendorRequest
;--------------------------
;----------------------------------
ClassRequest:
cpi temp1,GET_REPORT ;
breq ComposeGET_REPORT ;
cpi temp1,GET_IDLE ;
breq ComposeGET_IDLE ;
cpi temp1,GET_PROTOCOL ;
breq ComposeGET_PROTOCOL ;
cpi temp1,SET_REPORT ;
breq ComposeSET_REPORT ;
cpi temp1,SET_IDLE ;
breq ComposeSET_IDLE ;
cpi temp1,SET_PROTOCOL ;
breq ComposeSET_PROTOCOL ;
rjmp ZeroDATA1Answer ;if that was something unknown, then prepare zero answer
;--------- Class Requests ------------------
ComposeGET_REPORT: ;
rjmp ZeroDATA1Answer
ComposeGET_IDLE:
rjmp ZeroDATA1Answer
ComposeGET_PROTOCOL:
rjmp ZeroDATA1Answer
ComposeSET_REPORT:
rjmp ZeroDATA1Answer
ComposeSET_IDLE:
rjmp ZeroDATA1Answer
ComposeSET_PROTOCOL:
rjmp ZeroDATA1Answer
;------------------------------------
VendorRequest:
rjmp ZeroDATA1Answer ;if that it was something unknown, then prepare zero answer
;----------------------------- USER FUNCTIONS --------------------------------------
;----------------------------- END USER FUNCTIONS ------------------------------------- END USER
FUNCTIONS ------------------------------
OneZeroAnswer: ;send single zero
ldi temp0,1 ;number of my bytes answers to temp0
rjmp ComposeGET_STATUS2
;----------------------------- STANDARD USB REQUESTS ---------------------------------- STANDARD
USB REQUESTS ------------------------------
StandardRequest:
cpi temp1,GET_STATUS ;
breq ComposeGET_STATUS ;
cpi temp1,CLEAR_FEATURE ;
breq ComposeCLEAR_FEATURE ;
cpi temp1,SET_FEATURE ;
breq ComposeSET_FEATURE ;
cpi temp1,SET_ADDRESS ;if to set address
breq ComposeSET_ADDRESS ;set the address
cpi temp1,GET_DESCRIPTOR ;if requested descriptor
breq ComposeGET_DESCRIPTOR ;generate it
cpi temp1,SET_DESCRIPTOR ;
breq ComposeSET_DESCRIPTOR ;
cpi temp1,GET_CONFIGURATION ;
breq ComposeGET_CONFIGURATION ;
cpi temp1,SET_CONFIGURATION ;
breq ComposeSET_CONFIGURATION ;
cpi temp1,GET_INTERFACE ;
breq ComposeGET_INTERFACE ;
cpi temp1,SET_INTERFACE ;
breq ComposeSET_INTERFACE ;
cpi temp1,SYNCH_FRAME ;
breq ComposeSYNCH_FRAME ;
;if not found known request
rjmp ZeroDATA1Answer ;if that was something unknown, then prepare zero answer
ComposeSET_ADDRESS:
lds MyUpdatedAddress,InputBufferBegin+4 ;new address to MyUpdatedAddress
rjmp ZeroDATA1Answer ;send zero answer
ComposeSET_CONFIGURATION:
lds temp0,InputBufferBegin+4 ;number of configuration to variable ConfigByte
sts ConfigByte,temp0 ;
ComposeCLEAR_FEATURE:
ComposeSET_FEATURE:
ComposeSET_INTERFACE:
ZeroStringAnswer:
rjmp ZeroDATA1Answer ;send zero answer
ComposeGET_STATUS:
TwoZeroAnswer:
ldi temp0,2 ;number of my bytes answers to temp0
ComposeGET_STATUS2:
ldi ZH, high(StatusAnswer<<1) ;ROMpointer to answer
ldi ZL, low(StatusAnswer<<1)
rjmp ComposeEndXXXDescriptor ;and complete
ComposeGET_CONFIGURATION:
lds temp0,ConfigByte
;and temp0,temp0 ;if I am unconfigured
;breq OneZeroAnswer ;then send single zero - otherwise send my configuration
ldi temp0,1 ;number of my bytes answers to temp0
ldi ZH, high(ConfigAnswerMinus1<<1) ;ROMpointer to answer
ldi ZL, low(ConfigAnswerMinus1<<1)+1
rjmp ComposeEndXXXDescriptor ;and complete
ComposeGET_INTERFACE:
ldi ZH, high(InterfaceAnswer<<1) ;ROMpointer to answer
ldi ZL, low(InterfaceAnswer<<1)
ldi temp0,1 ;number of my bytes answers to temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeSYNCH_FRAME:
ComposeSET_DESCRIPTOR:
rcall ComposeSTALL
ret
ComposeGET_DESCRIPTOR:
; check if we received HID Class Descriptor request
lds temp0,InputBufferBegin+2 ;bmRequestType to temp0
cpi temp0, 0b10000001
breq ComposeClassDescriptor
; if not, process standard descriptor requests
lds temp1,InputBufferBegin+5 ;DescriptorType to temp1
cpi temp1,DEVICE ;DeviceDescriptor
breq ComposeDeviceDescriptor ;
cpi temp1,CONFIGURATION ;ConfigurationDescriptor
breq ComposeConfigDescriptor ;
cpi temp1,STRING ;StringDeviceDescriptor
breq ComposeStringDescriptor ;
ret
ComposeClassDescriptor:
lds temp1,InputBufferBegin+5 ;DescriptorType to temp1
cpi temp1,CLASS_HID ;HID class descripto
breq ComposeHIDClassDescriptor ;
cpi temp1,CLASS_Report ;ConfigurationDescriptor
breq ComposeReportDescriptor ;
cpi temp1,CLASS_Physical ;StringDeviceDescriptor
breq ComposePhysicalDescriptor
ret
;
ComposeDeviceDescriptor:
ldi ZH, high(DeviceDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(DeviceDescriptor<<1)
ldi temp0,0x12 ;number of my bytes answers to temp0
rjmp ComposeEndXXXDescriptor ;and completen
ComposeConfigDescriptor:
ldi ZH, high(ConfigDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(ConfigDescriptor<<1)
ldi temp0,9+9+9+7 ;number of my bytes answers to temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeHIDClassDescriptor:
ldi ZH, high(HIDDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(HIDDescriptor<<1)
ldi temp0,9 ;number of my bytes answers to temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeReportDescriptor : ;
ldi ZH, high(ReportDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(ReportDescriptor<<1)
ldi temp0,ReportDescriptorSize ;number of my bytes answers to temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposePhysicalDescriptor:
rjmp ComposeEndXXXDescriptor ;and complete
ComposeStringDescriptor:
ldi temp1,4+8 ;if RAMread=4(insert zeros from ROM reading) + 8(behind first byte no load zero)
mov RAMread,temp1
lds temp1,InputBufferBegin+4 ;DescriptorIndex to temp1
cpi temp1,0 ;LANGID String
breq ComposeLangIDString ;
cpi temp1,2 ;DevNameString
breq ComposeDevNameString
cpi temp1,3 ;NameString
breq ComposeNameString
cpi temp1,1 ;ComposeVendorString
breq ComposeVendorString
rjmp ZeroStringAnswer
rjmp ZeroDATA1Answer ;if is DescriptorIndex higher than 2 - send zero answer
ComposeVendorString:
ldi ZH, high(VendorStringDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(VendorStringDescriptor<<1)
ldi temp0,(VendorStringDescriptorEnd-VendorStringDescriptor)*4-2 ;number of my bytes answers to
temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeDevNameString:
ldi ZH, high(DevNameStringDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(DevNameStringDescriptor<<1)
ldi temp0,(DevNameStringDescriptorEnd-DevNameStringDescriptor)*4-2 ;number of my bytes answers to
temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeNameString:
ldi ZH, high(NameStringDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(NameStringDescriptor<<1)
ldi temp0,(NameStringDescriptorEnd-NameStringDescriptor)*4-2 ;number of my bytes answers to temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeLangIDString:
clr RAMread
ldi ZH, high(LangIDStringDescriptor<<1) ;ROMpointer to descriptor
ldi ZL, low(LangIDStringDescriptor<<1)
ldi temp0,(LangIDStringDescriptorEnd-LangIDStringDescriptor)*2;number of my bytes answers to
temp0
rjmp ComposeEndXXXDescriptor ;and complete
ComposeEndXXXDescriptor:
lds TotalBytesToSend,InputBufferBegin+8 ;number of requested bytes to TotalBytesToSend
cp TotalBytesToSend,temp0 ;if not requested more than I can send
brcs HostConfigLength ;transmit the requested number
mov TotalBytesToSend,temp0 ;otherwise send number of my answers
HostConfigLength:
mov temp0,TotalBytesToSend ;
clr TransmitPart ;zero the number of 8 bytes answers
andi temp0,0b00000111 ;if is length divisible by 8
breq Length8Multiply ;then not count one answer (under 8 byte)
inc TransmitPart ;otherwise count it
Length8Multiply:
mov temp0,TotalBytesToSend ;
lsr temp0 ;length of 8 bytes answers will reach
lsr temp0 ;integer division by 8
lsr temp0
add TransmitPart,temp0 ;and by addition to last non entire 8-bytes to variable TransmitPart
ldi temp0,DATA0PID ;DATA0 PID - in the next will be toggled to DATA1PID in load descriptor
sts OutputBufferBegin+1,temp0 ;store to output buffer
rjmp ComposeNextAnswerPart
;------------------------------------------------------------------------------------------
ZeroDATA1Answer:
rcall ComposeZeroDATA1PIDAnswer
ret
;----------------------------- END USB REQUESTS ------
PrepareOutContinuousBuffer:
rcall PrepareContinuousBuffer
rcall MakeOutBitStuff
ret
;------------------------------------------------------------------------------------------
PrepareContinuousBuffer:
mov temp0,TransmitPart
cpi temp0,1
brne NextAnswerInBuffer ;if buffer empty
rcall ComposeZeroAnswer ;prepare zero answer
ret
NextAnswerInBuffer:
dec TransmitPart ;decrement general length of answer
ComposeNextAnswerPart:
mov temp1,TotalBytesToSend ;decrement number of bytes to transmit
subi temp1,8 ;is is necessary to send more as 8 byte
ldi temp3,8 ;if yes - send only 8 byte
brcc Nad8Bytov
mov temp3,TotalBytesToSend ;otherwise send only given number of bytes
clr TransmitPart
inc TransmitPart ;and this will be last answer
Nad8Bytov:
mov TotalBytesToSend,temp1 ;decremented number of bytes to TotalBytesToSend
rcall LoadXXXDescriptor
ldi ByteCount,2 ;length of output buffer (only SOP and PID)
add ByteCount,temp3 ;+ number of bytes
rcall AddCRCOut ;addition of CRC to buffer
inc ByteCount ;length of output buffer + CRC16
inc ByteCount
ret ;finish
;***************************************************************************
;*
;* "div16u" - 16/16 Bit Unsigned Division
;*
;* This subroutine divides the two 16-bit numbers
;* "dd8uH:dd8uL" (dividend) and "dv16uH:dv16uL" (divisor).
;* The result is placed in "dres16uH:dres16uL" and the remainder in
;* "drem16uH:drem16uL".
;*
;* Number of words :196 + return
;* Number of cycles :148/173/196 (Min/Avg/Max)
;* Low registers used :2 (drem16uL,drem16uH)
;* High registers used :4 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH)
;*
;***************************************************************************
;***** Subroutine Register Variables
.def drem16uL=r14
.def drem16uH=r15
.def dres16uL=r16
.def dres16uH=r17
.def dd16uL =r16
.def dd16uH =r17
.def dv16uL =r18
.def dv16uH =r19
;***** Code
div16u: clr drem16uL ;clear remainder Low byte
sub drem16uH,drem16uH;clear remainder High byte and carry
rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_1 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_2 ;else
d16u_1: sec ; set carry to be shifted into result
d16u_2: rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_3 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_4 ;else
d16u_3: sec ; set carry to be shifted into result
d16u_4: rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_5 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_6 ;else
d16u_5: sec ; set carry to be shifted into result
d16u_6: rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_7 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_8 ;else
d16u_7: sec ; set carry to be shifted into result
d16u_8: rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_9 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_10 ;else
d16u_9: sec ; set carry to be shifted into result
d16u_10:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_11 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_12 ;else
d16u_11:sec ; set carry to be shifted into result
d16u_12:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_13 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_14 ;else
d16u_13:sec ; set carry to be shifted into result
d16u_14:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_15 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_16 ;else
d16u_15:sec ; set carry to be shifted into result
d16u_16:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_17 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_18 ;else
d16u_17: sec ; set carry to be shifted into result
d16u_18:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_19 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_20 ;else
d16u_19:sec ; set carry to be shifted into result
d16u_20:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_21 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_22 ;else
d16u_21:sec ; set carry to be shifted into result
d16u_22:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_23 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_24 ;else
d16u_23:sec ; set carry to be shifted into result
d16u_24:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_25 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_26 ;else
d16u_25:sec ; set carry to be shifted into result
d16u_26:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_27 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_28 ;else
d16u_27:sec ; set carry to be shifted into result
d16u_28:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_29 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_30 ;else
d16u_29:sec ; set carry to be shifted into result
d16u_30:rol dd16uL ;shift left dividend
rol dd16uH
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_31 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_32 ;else
d16u_31:sec ; set carry to be shifted into result
d16u_32:rol dd16uL ;shift left dividend
rol dd16uH
ret
;------------------------------------------------------------------------------------------
.equ USBversion =0x0100 ;for what version USB is that (1.00)
.equ VendorUSBID =0x0000 ;vendor identifier
;********************************************************************************
;*
;* IMPORTANT NOTE about Vendor ID
;*
;* For some reason known only to God the possibility to change
;* joystick's position in control panel is not implemented
;* in Microsoft Windows XP.
;* All joysticks in control panel and DirectX enumeration appear
;* in order of increasing Vendor ID.
;* So if you want MJoy to appear on top of the list set VendorUSBID=0x0000.
;* If you want to have second MJoy behaving as secondary
;* controls (throttle quadrant, trimmers, propeller pitch, mixture, etc.)
;* you can set this controller's VendorUSBID=0x0001 and so on.
;* Of course if you want to have MJoy the last on the list I suppose you
;* should set this to VendorUSBID=0xFFFF
;*
;* Disclaimer: Vendor ID values that are specified in standards and vendors list
;* are different than that just described. Change this to your own risk although
;* I don't see any unless you set it equal to some existing USB device on
;* your system. This measure is a workaround to overcome a flaw in operating
;* system.
;*
;********************************************************************************
.equ DeviceUSBID =0x0001 ;product identifier (USB Joystick)
.equ DeviceVersion =0x0102 ;version number of product (version=1.02 - MJoy with autocalibration)
.equ MaxUSBCurrent =0xA0 ;current consumption from USB (100mA - arbitrary)
;------------------------------------------------------------------------------------------
DeviceDescriptor:
.db 0x12,0x01 ;0 byte - size of descriptor in byte
;1 byte - descriptor type: Device descriptor
.dw USBversion ;2,3 byte - version USB LSB (1.00)
.db 0x00,0x00 ;4 byte - device class
;5 byte - subclass
.db 0x00,0x08 ;6 byte - protocol code
;7 byte - FIFO size in bytes
.dw VendorUSBID ;8,9 byte - vendor identifier
.dw DeviceUSBID ;10,11 byte - product identifier
.dw DeviceVersion ;12,13 byte - product version number
.db 0x01,0x02 ;14 byte - index of string "vendor"
;15 byte - index of string "product"
.db 0x00,0x01 ;16 byte - index of string "serial number" (0=none)
;17 byte - number of possible configurations
DeviceDescriptorEnd:
;------------------------------------------------------------------------------------------
ConfigDescriptor:
.db 0x9,0x02 ;length, descriptor type
ConfigDescriptorLength:
.dw 9+9+9+7 ;entire length of all descriptors + HID
ConfigAnswerMinus1: ;for sending the number - congiguration number (attention - addition of 1
required)
.db 1,1 ;numInterfaces, congiguration number
.db 2,0x80 ;string index (0=none), attributes; bus powered
;InterfaceDescriptor-1:
.db MaxUSBCurrent/2,0x09 ;current consumption, interface descriptor length
.db 0x04,0 ;interface descriptor; number of interface
InterfaceAnswer: ;for sending number of alternatively interface
.db 0,1 ;alternatively interface; number of endpoints except EP0
.db 0x03,0 ;interface class - HID; interface subclass
.db 0,3 ;protocol code; string index - Device name
HIDDescriptor:
.db 0x09,0x21 ; HID descriptor length , HID descriptor type (defined by USB)
.dw 0x101 ; HID Class Specification release number
.db 0,0x01 ;Hardware target country.; ;Number of HID class descriptors to follow.
.db 0x22,ReportDescriptorSize ;Report descriptor type.; length LSB
.db 0, 0x07 ;Total length of Report descriptor MSB, EndPointDescriptor length
;EndPointDescriptor:
;.db 0x07,0x5 ;length, descriptor type - endpoint
.db 0x5, 0x81 ;, descriptor type - endpoint
;.db 0x81,0 ;eendpoint address; transfer type
.db 0x3, 0x08 ;endpoint address In 1; transfer type -interrupt;max packet size LSB
;.dw 0x08 ;max packet size
.db 0, 10 ;max packet size MSB,polling interval [ms];
;.db 10,0 ;polling interval [ms]; dummy byte (for filling)
ConfigDescriptorEnd:
;-------------------------
StatusAnswer:
.db 0,0 ;2 zero answers
.equ ReportDescriptorSize =(0x51 + 32)
.equ JoystickReportCount =2
.equ JoystickReport1Size =8
.equ JoystickReport2Size =5
;-----------------------------------
ReportDescriptor:
.db 0x05,0x01 ;Usage_Page (Generic Desktop)
.db 0x15,0x00 ;Logical_Minimum (0)
.db 0x09,0x04 ;Usage (Joystick)
.db 0xA1,0x01 ;Collection (Application)
.db 0x85,0x01 ;Report_ID (1)
.db 0x09,0x01 ;Usage (Pointer)
.db 0xA1,0x00 ;Collection (Physical)
.db 0x09,0x30 ;Usage (X) - OK
.db 0x09,0x31 ;Usage (Y) - OK
.db 0x16,0x00 ;Logical_Minimum (-512)
.db 0xFE,0x26
.db 0xFF,0x01 ;Logical Maximum (511)
.db 0x75,0x0A ;Report_Size (10)
.db 0x95,0x02 ;Report_Count (2)
.db 0x81,0x02 ;Input (Data, Var, Abs)
.db 0x05,0x02 ;Usage_Page (Simulation Controls)
.db 0x09,0xBA ;Usage (Rudder) - OK
.db 0x09,0xBB ;Usage (Throttle) - OK
.db 0x81,0x02 ;Input (Data, Var, Abs)
.db 0xC0,0x16 ;End_Collection
.db 0x01,0xFE ;dummy logical minimum
.db 0x05,0x01 ;Usage_Page (Generic Desktop)
.db 0x09,0x32 ;Usage (Slider) - OK
.db 0x09,0x33 ;Usage (Dial) - OK
.db 0x15,0x81 ;Logical_Minimum (-127)
.db 0x25,0x7F ;Logical Maximum (127)
.db 0x75,0x08 ;Report_Size (8)
.db 0x95,0x02 ;Report_Count (2)
.db 0x81,0x02 ;Input (Data, Var, Abs)
.db 0x85,0x02 ;Report_ID (2)
.db 0x05,0x09 ;Usage_Page (Button)
.db 0x19,0x01 ;Usage_Minimum (Button 1)
.db 0x29,0x18 ;Usage_Maximum (Button 24)
.db 0x15,0x00 ;Logical_Minimum (0)
.db 0x25,0x01 ;Logical_Maximum (1)
.db 0x75,0x01 ;Report_Size (1)
.db 0x95,0x18 ;Report_Count (24)
.db 0x55,0x00 ;Unit_Exponent (0)
.db 0x65,0x00 ;Unit (None)
.db 0x81,0x02 ;Input (Data, Var, Abs)
.db 0x05,0x01 ;Usage_Page (Generic Desktop)
.db 0x09,0x39 ;Usage (Hat switch)
.db 0x35,0x00 ;Physical_Minimum (0)
.db 0x46,0x3B ;Physical_Maximum (315)
.db 0x01,0x16 ;... dummy logical minimum
.db 0x01,0xFE ;
.db 0x15,0x00 ;Logical_Minimum (0)
.db 0x25,0x07 ;Logical_Maximum (7)
.db 0x65,0x14 ;Unit (Eng Rot:Angular Pos)
.db 0x75,0x04 ;Report_Size (4)
.db 0x95,0x01 ;Report_Count (1)
.db 0x81,0x02 ;Input (Data, Var, Abs)
.db 0x09,0x01 ;USAGE (Vendor Usage 1)
.db 0x75,0x04 ;Report_Size (4)
.db 0x95,0x01 ;Report_Count (1)
.db 0x81,0x03 ;Input (Data, Const, Abs)
.db 0xC0,0 ;End_Collection , dummy padding
ReportDescriptorEnd:
;bits:
; 0 - UP
; 1 - RIGHT
; 2 - DOWN
; 3 - LEFT
HatValues: ; hat values mapping table
.db 0x0F, 0x00 ; 0 = no hat , 1= Up
.db 0x02, 0x01 ; 2 = Right , 3 = Up Right
.db 0x04, 0x0F ; 4 = Down , 5 = invalid (D U)
.db 0x03, 0x0F ; 6 = Down Right, 7 = invalid (D U R)
.db 0x06, 0x07 ; 8 = Left , 9 = Up Left
.db 0x0F, 0x0F ; 10 = invalid (L R), 11 = invalid (L R U)
.db 0x05, 0x0F ; 12 = Down Left , invalid
.db 0x0F, 0x0F ; invalid values
;------------------------------------------------------------------------------------------
LangIDStringDescriptor:
.db (LangIDStringDescriptorEnd-LangIDStringDescriptor)*2,3 ;length, type: string descriptor
.dw 0x0009 ;English
LangIDStringDescriptorEnd:
;------------------------------------------------------------------------------------------
VendorStringDescriptor:
.db (VendorStringDescriptorEnd-VendorStringDescriptor)*4-2,3 ;length, type: string descriptor
CopyRight:
.db "Mindaugas Milasauskas (c) 2004, Ing. Igor Cesko, Copyright(c) 2003"
CopyRightEnd:
VendorStringDescriptorEnd:
;------------------------------------------------------------------------------------------
DevNameStringDescriptor:
.db (DevNameStringDescriptorEnd-DevNameStringDescriptor)*4-2,3;length, type: string descriptor
.db "MJoy"
;********************************************************************************
;*
;* NOTE about DevNameStringDescriptor
;*
;* This name will appear in game devices list control panel in Windows.
;* If you want to have several MJoy devices on the system you can
;* change this name together with Vendor ID to the one you like.
;* Please note that for some reason only first 4 characters will be
;* displayed so there is no point making it longer than 4 characters.
;*
;********************************************************************************
DevNameStringDescriptorEnd:
NameStringDescriptor:
.db (NameStringDescriptorEnd-NameStringDescriptor)*4-2,3;length, type: string descriptor
.db "MJoy, ATMega8 based USB Joystick v1.2"
NameStringDescriptorEnd:
;------------------------------------------------------------------------------------------
;********************************************************************
;* End of program
;********************************************************************
;------------------------------------------------------------------------------------------
;********************************************************************
;* EEPROM contents
;********************************************************************
;------------------------------------------------------------------------------------------
.eseg ;data in EEPROM (at final version comment)
;.org 0x400 ;for filling EEPROM give on right addresses - behind the program code (at final version
uncomment)
EEData:
;**********************************************************
;* Axis ranges initial information.
;* It is advisory but not necessary to program EEPROM
;* as the software writes correct values when it
;* detects empty EEPROM.
;**********************************************************
XAxisEE:
.db 3,255,0,0
YAxisEE:
.db 3,255,0,0
RudderAxisEE:
.db 3,255,0,0
ThrottleAxisEE:
.db 3,255,0,0
ZAxisEE:
.db 3,255,0,0
RxAxisEE:
.db 3,255,0,0
;------------------------------------------------------------------------------------------
;********************************************************************
;* End of file
;********************************************************************