;*************************************************************************** ;* 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 ;******************************************************************** 1
Hosted by www.Geocities.ws