;***************************************************************************
;* 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 :"USB90S2313.asm"
;* Title :USB stack + Infrared remote control to nonUSB MCU
;* Date :5.4.2003
;* Version :1.6
;* Target MCU :AT90S2313-10,AT90S2323-10,AT90S2343-10
;* AUTHOR :Ing. Igor Cesko
;* Slovakia
;* cesko@internet.sk
;* http://www.cesko.host.sk
;*
;* DESCRIPTION:
;* USB protocol implementation into MCU with noUSB interface:
;* Device:
;* Infrared remote control with TSOP17xx/SFH511x sensor and USB connection
;* + Universal USB interface (8-bit I/O port + RS232 serial line + EEPROM)
;*
;* The timing is adapted for 12 MHz crystal (overclocked MCU !!!)
;*
;* Copyright (c) Ing. Igor Cesko 2002-2003
;* All rights reserved
;***************************************************************************
.include "2313def.inc"

.equ inputport =PINB
.equ outputport =PORTB
.equ USBdirection =DDRB
.equ DATAplus =1 ;signal D+ na PB1
.equ DATAminus =0 ;signal D- na PB0 - treba dat na tento pin pull-up 1.5kOhm
.equ USBpinmask =0b11111100 ;mask low 2 bits (D+,D-) on PB
.equ USBpinmaskDplus =~(1<<DATAplus) ;mask D+ bit on PB
.equ USBpinmaskDminus =~(1<<DATAminus);mask D- bit on PB

.equ TSOPPort =PINB
.equ TSOPpullupPort =PORTB
.equ TSOPPin =2 ;signal OUT z IR senzora TSOP1738 na PB2

.equ LEDPortLSB =PORTD ;pripojenie LED diod LSB
.equ LEDPinLSB =PIND ;pripojenie LED diod LSB (vstup)
.equ LEDdirectionLSB =DDRD ;vstup/vystup LED LSB
.equ LEDPortMSB =PORTB ;pripojenie LED diod MSB
.equ LEDPinMSB =PINB ;pripojenie LED diod MSB (vstup)
.equ LEDdirectionMSB =DDRB ;vstup/vystup LED MSB
.equ LEDlsb0 =3 ;LED0 na pin PD3
.equ LEDlsb1 =5 ;LED1 na pin PD5
.equ LEDlsb2 =6 ;LED2 na pin PD6
.equ LEDmsb3 =3 ;LED3 na pin PB3
.equ LEDmsb4 =4 ;LED4 na pin PB4
.equ LEDmsb5 =5 ;LED5 na pin PB5
.equ LEDmsb6 =6 ;LED6 na pin PB6
.equ LEDmsb7 =7 ;LED7 na pin PB7

.equ SOPbyte =0b10000000 ;Start of Packet byte
.equ DATA0PID =0b11000011 ;PID pre DATA0 pole
.equ DATA1PID =0b01001011 ;PID pre DATA1 pole
.equ OUTPID =0b11100001 ;PID pre OUT pole
.equ INPID =0b01101001 ;PID pre IN pole
.equ SOFPID =0b10100101 ;PID pre SOF pole
.equ SETUPPID =0b00101101 ;PID pre SETUP pole
.equ ACKPID =0b11010010 ;PID pre ACK pole
.equ NAKPID =0b01011010 ;PID pre NAK pole
.equ STALLPID =0b00011110 ;PID pre STALL pole
.equ PREPID =0b00111100 ;PID pre PRE pole

.equ nSOPbyte =0b00000001 ;Start of Packet byte - opacne poradie
.equ nDATA0PID =0b11000011 ;PID pre DATA0 pole - opacne poradie
.equ nDATA1PID =0b11010010 ;PID pre DATA1 pole - opacne poradie
.equ nOUTPID =0b10000111 ;PID pre OUT pole - opacne poradie
.equ nINPID =0b10010110 ;PID pre IN pole - opacne poradie
.equ nSOFPID =0b10100101 ;PID pre SOF pole - opacne poradie
.equ nSETUPPID =0b10110100 ;PID pre SETUP pole - opacne poradie
.equ nACKPID =0b01001011 ;PID pre ACK pole - opacne poradie
.equ nNAKPID =0b01011010 ;PID pre NAK pole - opacne poradie
.equ nSTALLPID =0b01111000 ;PID pre STALL pole - opacne poradie
.equ nPREPID =0b00111100 ;PID pre PRE pole - opacne poradie

.equ nNRZITokenPID =~0b10000000 ;PID maska pre Token paket (IN,OUT,SOF,SETUP) - opacne poradie NRZI
.equ nNRZISOPbyte =~0b10101011 ;Start of Packet byte - opacne poradie NRZI
.equ nNRZIDATA0PID =~0b11010111 ;PID pre DATA0 pole - opacne poradie NRZI
.equ nNRZIDATA1PID =~0b11001001 ;PID pre DATA1 pole - opacne poradie NRZI
.equ nNRZIOUTPID =~0b10101111 ;PID pre OUT pole - opacne poradie NRZI
.equ nNRZIINPID =~0b10110001 ;PID pre IN pole - opacne poradie NRZI
.equ nNRZISOFPID =~0b10010011 ;PID pre SOF pole - opacne poradie NRZI
.equ nNRZISETUPPID =~0b10001101 ;PID pre SETUP pole - opacne poradie NRZI
.equ nNRZIACKPID =~0b00100111 ;PID pre ACK pole - opacne poradie NRZI
.equ nNRZINAKPID =~0b00111001 ;PID pre NAK pole - opacne poradie NRZI
.equ nNRZISTALLPID =~0b00000111 ;PID pre STALL pole - opacne poradie NRZI
.equ nNRZIPREPID =~0b01111101 ;PID pre PRE pole - opacne poradie NRZI
.equ nNRZIADDR0 =~0b01010101 ;Adresa = 0 - opacne poradie NRZI

;stavove byty - State
.equ BaseState =0 ;
.equ SetupState =1 ;
.equ InState =2 ;
.equ OutState =3 ;
.equ SOFState =4 ;
.equ DataState =5 ;

;Flagy pozadovanej akcie
.equ DoNone =0
.equ DoReceiveOutData =1
.equ DoReceiveSetupData =2
.equ DoPrepareOutContinuousBuffer =3
.equ DoReadySendAnswer =4


.equ CRC5poly =0b00101 ;CRC5 polynom
.equ CRC5zvysok =0b01100 ;CRC5 zvysok po uspesnpm CRC5
.equ CRC16poly =0b1000000000000101 ;CRC16 polynom
.equ CRC16zvysok =0b1000000000001101 ;CRC16 zvysok po uspesnom CRC16

.equ MAXUSBBYTES =14 ;maximum bytes in USB input message
.equ MAXINFRALENGTH =36 ;maximalna dlzka Infra kodu (pocet jednotiek a nul spolu) (pozor: MAXINFRALENGTH musi byt parne cislo !!!)
.equ NumberOfFirstBits =10 ;kolko prvych bitov moze byt dlhsich
.equ NoFirstBitsTimerOffset =256-12800*12/1024 ;Timeout 12.8ms (12800us) na ukoncenie prijmu po uvodnych bitoch (12Mhz:clock, 1024:timer predivider, 256:timer overflow value)
.equ BaudRate =12000000/16/57600-1 ;nastavit vysielaciu rychlost UART-u na 57600 (pre 12MHz=12000000Hz)

.equ InputBufferBegin =RAMEND-127 ;zaciatok prijimacieho shift buffera
.equ InputShiftBufferBegin =InputBufferBegin+MAXUSBBYTES ;zaciatok prijimacieho buffera
.equ InfraBufferBegin =InputShiftBufferBegin+MAXUSBBYTES ;zaciatok buffera pre Infra prijem

.equ OutputBufferBegin =RAMEND-MAXUSBBYTES-2 ;zaciatok vysielacieho buffera
.equ AckBufferBegin =OutputBufferBegin-3 ;zaciatok vysielacieho buffera Ack
.equ NakBufferBegin =AckBufferBegin-3 ;zaciatok vysielacieho buffera Nak

.equ StackBegin =NakBufferBegin-1 ;spodok zasobnika

.def ConfigByte =R1 ;0=unconfigured state
.def backupbitcount =R2 ;zaloha bitcount registra v INT0 preruseni
.def RAMread =R3 ;ci sa ma citat zo SRAM-ky
.def backupSREGTimer =R4 ;zaloha Flag registra v Timer interrupte
.def backupSREG =R5 ;zaloha Flag registra v INT0 preruseni
.def ACC =R6 ;accumulator
.def lastBitstufNumber =R7 ;pozicia bitstuffingu
.def OutBitStuffNumber =R8 ;kolko bitov sa ma este odvysielat z posledneho bytu - bitstuffing
.def BitStuffInOut =R9 ;ci sa ma vkladat alebo mazat bitstuffing
.def TotalBytesToSend =R10 ;kolko sa ma poslat bytov
.def TransmitPart =R11 ;poradove cislo vysielacej casti
.def InputBufferLength =R12 ;dlzka pripravena vo vstupnom USB bufferi
.def OutputBufferLength =R13 ;dlzka odpovede pripravena v USB bufferi
.def MyUpdatedAddress =R14 ;moja USB adresa na update
.def MyAddress =R15 ;moja USB adresa


.def ActionFlag =R16 ;co sa ma urobit v hlavnej slucke programu
.def temp3 =R17 ;temporary register
.def temp2 =R18 ;temporary register
.def temp1 =R19 ;temporary register
.def temp0 =R20 ;temporary register
.def bitcount =R21 ;counter of bits in byte
.def ByteCount =R22 ;pocitadlo maximalneho poctu prijatych bajtov
.def inputbuf =R23 ;prijimaci register
.def shiftbuf =R24 ;posuvny prijimaci register
.def State =R25 ;byte stavu stavoveho stroja
.def InfraBufptrX =R26 ;XL register - pointer do buffera prijatych IR kodov
.def InfraBufferFull =R27 ;XH register - priznak plneho Infra Buffera
.def USBBufptrY =R28 ;YL register - pointer do USB buffera input/output
.def ROMBufptrZ =R30 ;ZL register - pointer do buffera ROM dat

;poziadavky na deskriptory
.equ GET_STATUS =0
.equ CLEAR_FEATURE =1
.equ SET_FEATURE =3
.equ SET_ADDRESS =5
.equ GET_DESCRIPTOR =6
.equ SET_DESCRIPTOR =7
.equ GET_CONFIGURATION =8
.equ SET_CONFIGURATION =9
.equ GET_INTERFACE =10
.equ SET_INTERFACE =11
.equ SYNCH_FRAME =12

;typy deskriptorov
.equ DEVICE =1
.equ CONFIGURATION =2
.equ STRING =3
.equ INTERFACE =4
.equ ENDPOINT =5


;------------------------------------------------------------------------------------------
;********************************************************************
;* Interrupt table
;********************************************************************
.cseg
;------------------------------------------------------------------------------------------
.org 0 ;po resete
rjmp reset
;------------------------------------------------------------------------------------------
.org INT0addr ;externe prerusenie INT0
rjmp INT0handler
;------------------------------------------------------------------------------------------
.org INT1addr ;externe prerusenie INT1 (pre AT89S2323 je tu Timer0 prerusenie)
sei ;povol interrupty na obsluhu USB
in backupSREGTimer,SREG
rjmp OVF0handler2323 ;na kompatibilitu s AT89S2323 sa skoci na pretecenie casovaca T0
;------------------------------------------------------------------------------------------
;********************************************************************
;* Timer0 interrupt handler
;********************************************************************
.org OVF0addr ;citac/casovac 0
OVF0handler:
sei ;povol interrupty na obsluhu USB
in backupSREGTimer,SREG
OVF0handler2323:
push temp0
ldi temp0,0<<TOIE0 ;zakazat interrupt od casovaca
out TIMSK,temp0
ldi temp0,5 ;nastav clock input na CLK/1024 = tick kazdych 0.0853ms
out TCCR0,temp0
push temp1
push temp2
push temp3

clr temp2 ;vynulovanie offsetu v bufferi (a flagu pretecenia)
ldi temp3,1 ;timeout - plnenie citaca (pre prve bity iny ako pre dalsie bity: aby sa nikdy nevyslala dlzka kodu 255, tak sa musi plnit aspon na 1)
StartTSOPsampling:
ldi InfraBufptrX,InfraBufferBegin+3 ;nastavenie sa na zaciatok buffera Infra kodu + 3byty hlavicky (dlzka kodu + counter + offset)
clr temp1 ;vynulovanie dlzky Infra kodu
WaitForTOSOP1:
sbis TSOPPort,TSOPPin ;ak je TSOPPin v nule
rjmp CheckWaitForTOSOP1 ;tak cakaj na jednotku
rjmp StoreToInfraBuffer
WaitForTOSOP0:
sbic TSOPPort,TSOPPin ;ak je TSOPPin v jednotke
rjmp CheckWaitForTOSOP0 ;tak cakaj na nulu
StoreToInfraBuffer:
in temp0,TCNT0 ;odchyt citac
sub temp0,temp3 ;koriguj odchyteny citac vzhladom na jeho plnenie
cpi temp1,NumberOfFirstBits ;ak este neboli prve bity
brne WasNotFirstBits ;tak nechaj plnenie citaca na nule
ldi temp3,NoFirstBitsTimerOffset ;inak zmen plnenie citaca
WasNotFirstBits:
out TCNT0,temp3 ;nastav citac aby bol ako casovac
out UDR,temp0 ;vysli data na UART
cpi InfraBufferFull,0 ;ak je infra buffer plny
brne NoStoreToInfraBuffer ;tak neukladaj data
sbrs RAMread,0 ;ak RAMread=1 cita sa z RAM - potom neukladaj data
st X+,temp0 ;a uloz ho do buffera
NoStoreToInfraBuffer:
inc temp1 ;zvys dlzku Infra Buffera
cpi temp1,MAXINFRALENGTH ;ak sa nedosiahol maximum Infra buffera
brne NoTSOPoverflow ;tak pokracuj
inc temp2 ;inak si poznac ze pretiekol
cpi temp2,3 ;ak boli uz 3 pretecenia
dec temp1 ;tak uber posledny bit
breq EndInfraSampling ;a skonci so samplovanim
rjmp StartTSOPsampling ;a zacni odznova (pozor: MAXINFRALENGTH musi byt parne cislo !!!)
NoTSOPoverflow:
sbic TSOPPort,TSOPPin ;ak je TSOPPin v jednotke
rjmp WaitForTOSOP0 ;tak cakaj na nulu
;inak cakaj na jednotku
CheckWaitForTOSOP1:
in temp0,TIFR ;odchyt flag pretecenia citaca
sbrc temp0,TOV0 ;ak pretiekol
rjmp EndInfraSampling ;tak skonci so samplovanim
rjmp WaitForTOSOP1 ;inak cakaj na zmenu stavu
CheckWaitForTOSOP0:
in temp0,TIFR ;odchyt flag pretecenia citaca
sbrc temp0,TOV0 ;ak pretiekol
rjmp EndInfraSampling ;tak skonci so samplovanim
rjmp WaitForTOSOP0 ;inak cakaj na zmenu stavu
EndInfraSampling:
sbrs InfraBufferFull,0 ;ak je infra buffer plny - tak neukladaj hlavicku
sbrc RAMread,0 ;ak RAMread=1 cita sa z RAM
rjmp EndTSOPSampling ;potom neukladaj hlavicku
cpi temp2,0 ;ak nebolo nejake pretecenie
breq NoOverflow ;tak nic nerob
mov temp2,temp1 ;inak zaznamenaj offset
ldi temp1,MAXINFRALENGTH ;a maximalnu dlzku buffera
NoOverflow:
sts InfraBufferBegin,temp1 ;dlzku kodu na zaciatok Infra buffera
cpi temp1,3 ;zisti ci je dlzka kodu aspon 3
lds temp1,InfraBufferBegin+1;nacitaj pocitadlo infra kodov
brcs NoIncrementCodeCounter ;ak je dlzka kodu vacsia alebo rovna 3
inc temp1 ;tak zvys pocitadlo infra kodov
ldi InfraBufferFull,1 ;a nastav plnost buffera
NoIncrementCodeCounter:
sts InfraBufferBegin+1,temp1;uloz nove pocitadlo infra kodov
sts InfraBufferBegin+2,temp2;uloz offset zaciatku buffera
EndTSOPSampling:
pop temp3
pop temp2
pop temp1
ldi temp0,0xFF ;napln temp0 hodnotou 0xFF (ukoncovaci znak)
out UDR,temp0 ;a vysli ukoncovaci znak na UART
out SREG,backupSREGTimer ;obnova SREG (a sucasne povolenie interruptu)
rcall InitCounter ;restartni citac na TSOP zmenu a zakaz interrupt
pop temp0
reti
;********************************************************************
;* Init program
;********************************************************************
;------------------------------------------------------------------------------------------
reset: ;inicializacia procesora a premennych na spravne hodnoty
ldi temp0,StackBegin ;inicializacia stacku
out SPL,temp0

clr XH ;Infra pointer
clr YH ;USB pointer
clr ZH ;ROM pointer
sts InfraBufferBegin,YH ;znuluj dlzky Infra kodu v bufferi
sts InfraBufferBegin+1,YH ;znuluj pocitadlo infra kodov v bufferi

clr MyUpdatedAddress ;nova adresa USB - nedekodovana
rcall InitACKBufffer ;inicializacia ACK buffera
rcall InitNAKBufffer ;inicializacia NAK buffera

rcall USBReset ;inicializacia USB adresy

sbi TSOPpullupPort,TSOPpin ;nahodit pull-up na TSOP vstupe

ldi temp0,(1<<LEDlsb0)+(1<<LEDlsb1)+(1<<LEDlsb2)
out LEDPortLSB,temp0 ;nahodit pull-up na vsetkych LED vstupoch LSB
ldi temp0,(1<<LEDmsb3)+(1<<LEDmsb4)+(1<<LEDmsb5)+(1<<LEDmsb6)+(1<<LEDmsb7)
out LEDPortMSB,temp0 ;nahodit pull-up na vsetkych LED vstupoch MSB

sbi PORTD,0 ;nahodit pull-up na RxD vstupe
ldi temp0,BaudRate ;nastavitvysielaciu rychlost UART-u
out UBRR,temp0
sbi UCR,TXEN ;povolit vysielanie UART-u
sbi UCR,RXEN ;povolit prijimanie UART-u

ldi temp0,0x0F ;INT0 - reagovanie na nabeznu hranu
out MCUCR,temp0 ;
ldi temp0,1<<INT0 ;povolit externy interrupt INT0
out GIMSK,temp0
rcall InitCounter ;inicializacia citaca na TSOP zmenu
;------------------------------------------------------------------------------------------
;********************************************************************
;* Main program
;********************************************************************
sei ;povolit interrupty globalne
Main:
sbis inputport,DATAminus ;cakanie az sa zmeni D- na 0
rjmp CheckUSBReset ;a skontroluj, ci to nie je USB reset

cpi ActionFlag,DoReceiveSetupData
breq ProcReceiveSetupData
cpi ActionFlag,DoPrepareOutContinuousBuffer
breq ProcPrepareOutContinuousBuffer
rjmp Main

CheckUSBReset:
ldi temp0,255 ;pocitadlo trvania reset-u (podla normy je to cca 10ms - tu je to cca 100us)
WaitForUSBReset:
sbic inputport,DATAminus ;cakanie az sa zmeni D+ na 0
rjmp Main
dec temp0
brne WaitForUSBReset
rcall USBReset
rjmp Main

ProcPrepareOutContinuousBuffer:
rcall PrepareOutContinuousBuffer ;priprav pokracovanie odpovede do buffera
ldi ActionFlag,DoReadySendAnswer
rjmp Main
ProcReceiveSetupData:
ldi USBBufptrY,InputBufferBegin ;pointer na zaciatok prijimacieho buffera
mov ByteCount,InputBufferLength ;dlzka vstupneho buffera
rcall DecodeNRZI ;prevod kodovania NRZI na bity
rcall MirrorInBufferBytes ;prehodit poradie bitov v bajtoch
rcall BitStuff ;odstranenie bit stuffing
rcall CheckCRCIn ;kontrola CRC
rcall PrepareUSBOutAnswer ;pripravenie odpovede do vysielacieho buffera
ldi ActionFlag,DoReadySendAnswer
rjmp Main
;********************************************************************
;* Main program END
;********************************************************************
;------------------------------------------------------------------------------------------
;********************************************************************
;* Interrupt0 interrupt handler
;********************************************************************
INT0Handler: ;prerusenie INT0
in backupSREG,SREG
push temp0
push temp1

ldi temp0,3 ;pocitadlo trvania log0
ldi temp1,2 ;pocitadlo trvania log1
;cakanie na zaciatok paketu
CheckchangeMinus:
sbis inputport,DATAminus ;cakanie az sa zmeni D- na 1
rjmp CheckchangeMinus
CheckchangePlus:
sbis inputport,DATAplus ;cakanie az sa zmeni D+ na 1
rjmp CheckchangePlus
DetectSOPEnd:
sbis inputport,DATAplus
rjmp Increment0 ;D+ =0
Increment1:
ldi temp0,3 ;pocitadlo trvania log0
dec temp1 ;kolko cyklov trvala log1
nop
breq USBBeginPacket ;ak je to koniec SOP - prijimaj paket
rjmp DetectSOPEnd
Increment0:
ldi temp1,2 ;pocitadlo trvania log1
dec temp0 ;kolko cyklov trvala log0
nop
brne DetectSOPEnd ;ak nenastal SOF - pokracuj
rjmp EndInt0HandlerPOP2
EndInt0Handler:
pop ACC
pop InfraBufptrX
pop temp3
pop temp2
EndInt0HandlerPOP:
pop USBBufptrY
pop ByteCount
mov bitcount,backupbitcount ;obnova bitcount registra
EndInt0HandlerPOP2:
pop temp1
pop temp0
out SREG,backupSREG
ldi shiftbuf,1<<INTF0 ;znulovat flag interruptu INTF0
out GIFR,shiftbuf
reti ;inak skonci (bol iba SOF - kazdu milisekundu)

USBBeginPacket:
mov backupbitcount,bitcount ;zaloha bitcount registra
in shiftbuf,inputport ;ak ano nacitaj ho ako nulty bit priamo do shift registra
USBloopBegin:
push ByteCount ;dalsia zaloha registrov (setrenie casu)
push USBBufptrY
ldi bitcount,6 ;inicializacia pocitadla bitov v bajte
ldi ByteCount,MAXUSBBYTES ;inicializacia max poctu prijatych bajtov v pakete
ldi USBBufptrY,InputShiftBufferBegin ;nastav vstupny buffer
USBloop1_6:
in inputbuf,inputport
cbr inputbuf,USBpinmask ;odmaskovat spodne 2 bity
breq USBloopEnd ;ak su nulove - koniec USB packetu
ror inputbuf ;presun Data+ do shift registra
rol shiftbuf
dec bitcount ;zmensi pocitadlo bitov
brne USBloop1_6 ;ak nie je nulove - opakuj naplnanie shift registra
nop ;inak bude nutne skopirovat shift register bo buffera
USBloop7:
in inputbuf,inputport
cbr inputbuf,USBpinmask ;odmaskovat spodne 2 bity
breq USBloopEnd ;ak su nulove - koniec USB packetu
ror inputbuf ;presun Data+ do shift registra
rol shiftbuf
ldi bitcount,7 ;inicializacia pocitadla bitov v bajte
st Y+,shiftbuf ;skopiruj shift register bo buffera a zvys pointer do buffera
USBloop0: ;a zacni prijimat dalsi bajt
in shiftbuf,inputport ;nulty bit priamo do shift registra
cbr shiftbuf,USBpinmask ;odmaskovat spodne 2 bity
breq USBloopEnd ;ak su nulove - koniec USB packetu
dec bitcount ;zmensi pocitadlo bitov
nop ;
dec ByteCount ;ak sa nedosiahol maximum buffera
brne USBloop1_6 ;tak prijimaj dalej

rjmp EndInt0HandlerPOP ;inak opakuj od zaciatku

USBloopEnd:
cpi USBBufptrY,InputShiftBufferBegin+3 ;ak sa neprijali aspon 3 byte
brcs EndInt0HandlerPOP ;tak skonci
lds temp0,InputShiftBufferBegin+0 ;identifikator paketu do temp0
lds temp1,InputShiftBufferBegin+1 ;adresa do temp1
brne TestDataPacket ;ak je dlzka ina ako 3 - tak to moze byt iba DataPaket
TestIOPacket:
cp temp1,MyAddress ;ak to nie je urcene (adresa) pre mna
brne TestDataPacket ;tak to moze byt este Data Packet
TestSetupPacket:;test na SETUP paket
cpi temp0,nNRZISETUPPID
brne TestOutPacket ;ak nie je Setup PID - dekoduj iny paket
ldi State,SetupState
rjmp EndInt0HandlerPOP ;ak je Setup PID - prijimaj nasledny Data paket
TestOutPacket: ;test na OUT paket
cpi temp0,nNRZIOUTPID
brne TestInPacket ;ak nie je Out PID - dekoduj iny paket
ldi State,OutState
rjmp EndInt0HandlerPOP ;ak je Out PID - prijimaj nasledny Data paket
TestInPacket: ;test na IN paket
cpi temp0,nNRZIINPID
brne TestDataPacket ;ak nie je In PID - dekoduj iny paket
rjmp AnswerToInRequest
TestDataPacket: ;test na DATA0 a DATA1 paket
cpi temp0,nNRZIDATA0PID
breq Data0Packet ;ak nie je Data0 PID - dekoduj iny paket
cpi temp0,nNRZIDATA1PID
brne NoMyPacked ;ak nie je Data1 PID - dekoduj iny paket
Data0Packet:
cpi State,SetupState ;ak bol stav Setup
breq ReceiveSetupData ;prijmi ho
cpi State,OutState ;ak bol stav Out
breq ReceiveOutData ;prijmi ho
NoMyPacked:
ldi State,BaseState ;znuluj stav
rjmp EndInt0HandlerPOP ;a prijimaj nasledny Data paket

AnswerToInRequest:
push temp2 ;zazalohuj dalsie registre a pokracuj
push temp3
push InfraBufptrX
push ACC
cpi ActionFlag,DoReadySendAnswer ;ak nie je pripravena odpoved
brne NoReadySend ;tak posli NAK
rcall SendPreparedUSBAnswer ;poslanie odpovede naspat
and MyUpdatedAddress,MyUpdatedAddress ;ak je MyUpdatedAddress nenulova
brne SetMyNewUSBAddress ;tak treba zmenit USB adresu
ldi State,InState
ldi ActionFlag,DoPrepareOutContinuousBuffer
rjmp EndInt0Handler ;a opakuj - cakaj na dalsiu odozvu z USB
ReceiveSetupData:
push temp2 ;zazalohuj dalsie registre a pokracuj
push temp3
push InfraBufptrX
push ACC
rcall SendACK ;akceptovanie Setup Data paketu
rcall FinishReceiving ;ukonci prijem
ldi ActionFlag,DoReceiveSetupData
rjmp EndInt0Handler
ReceiveOutData:
push temp2 ;zazalohuj dalsie registre a pokracuj
push temp3
push InfraBufptrX
push ACC
cpi ActionFlag,DoReceiveSetupData ;ak sa prave spracovava prikaz Setup
breq NoReadySend ;tak posli NAK
rcall SendACK ;akceptovanie Out paketu
clr ActionFlag
rjmp EndInt0Handler
NoReadySend:
rcall SendNAK ;este nie som pripraveny s odpovedou
rjmp EndInt0Handler ;a opakuj - cakaj na dalsiu odozvu z USB
;------------------------------------------------------------------------------------------
SetMyNewUSBAddress: ;nastavi novu USB adresu v NRZI kodovani
clr MyAddress ;vychodzi stav odpovede - mojej nNRZI USB adresy
ldi temp2,0b00000001 ;maska na xorovanie
ldi temp3,8 ;pocitadlo bitov
SetMyNewUSBAddressLoop:
mov temp0,MyAddress ;zapamatat si koncovu odpoved
ror MyUpdatedAddress ;do carry vysielany bit LSB (v smere naskor LSB a potom MSB)
brcs NoXORBit ;ak je jedna - nemen stav
eor temp0,temp2 ;inak sa bude stav menit podla posledneho bitu odpovede
NoXORBit:
ror temp0 ;posledny bit zmenenej odpovede do carry
rol MyAddress ;a z carry do koncovej odpovede na miesto LSB (a sucasne prehodenie LSB a MSB poradia)
dec temp3 ;zmensi pocitadlo bitov
brne SetMyNewUSBAddressLoop ;ak pocitadlo bitov nie je nulove opakuj vysielanie s dalsim bitom
clr MyUpdatedAddress ;znulovanie adresy ako priznak jej buduceho nemenenia
rjmp EndInt0Handler
;------------------------------------------------------------------------------------------
FinishReceiving: ;korekcne akcie na ukoncenie prijmu
cpi bitcount,7 ;prenes do buffera aj posledny necely byte
breq NoRemainingBits ;ak boli vsetky byty prenesene, tak neprenasaj nic
inc bitcount
ShiftRemainingBits:
rol shiftbuf ;posun ostavajuce necele bity na spravnu poziciu
dec bitcount
brne ShiftRemainingBits
st Y+,shiftbuf ;a skopiruj shift register bo buffera - necely byte
NoRemainingBits:
mov ByteCount,USBBufptrY
subi ByteCount,InputShiftBufferBegin-1 ;v ByteCount je pocet prijatych byte (vratane necelych byte)

mov InputBufferLength,ByteCount ;a uchovat pre pouzitie v hlavnom programe
ldi USBBufptrY,InputShiftBufferBegin ;pointer na zaciatok prijimacieho shift buffera
ldi InfraBufptrX,InputBufferBegin+1 ;data buffer (vynechat SOP)
MoveDataBuffer:
ld temp0,Y+
st X+,temp0
dec ByteCount
brne MoveDataBuffer

ldi ByteCount,nNRZISOPbyte
sts InputBufferBegin,ByteCount ;ako keby sa prijal SOP - nekopiruje sa zo shift buffera
ret
;------------------------------------------------------------------------------------------
;********************************************************************
;* Other procedures
;********************************************************************
;------------------------------------------------------------------------------------------
USBReset: ;inicializacia USB stavoveho stroja
ldi temp0,nNRZIADDR0 ;inicializacia USB adresy
mov MyAddress,temp0
clr State ;inicializacia stavoveho stroja
clr BitStuffInOut
clr OutBitStuffNumber
clr ActionFlag
clr RAMread ;bude sa vycitavat z ROM-ky
clr ConfigByte ;nenakonfiguravany stav
ret
;------------------------------------------------------------------------------------------
InitCounter: ;inicializacia countera na prijem zmeny z TSOP
ldi temp0,6 ;na zostupnu hranu casovaca - externy pin
out TCCR0,temp0
ldi temp0,0xFF ;napln citac naplno - aby hned na nasledujucu hranu pretiekol
out TCNT0,temp0
ldi temp0,1<<TOV0 ;znuluj pripadny cakajuci flag pretecenia citaca
out TIFR,temp0
ldi temp0,1<<TOIE0 ;povolit interrupt od casovaca
;cli ;zakaz interrupt kvoli zacykleniu
out TIMSK,temp0
ret
;------------------------------------------------------------------------------------------
SendPreparedUSBAnswer: ;poslanie kodovanim NRZI OUT buffer s dlzkou OutputBufferLength do USB
mov ByteCount,OutputBufferLength ;dlzka odpovede
SendUSBAnswer: ;poslanie kodovanim NRZI OUT buffer do USB
ldi USBBufptrY,OutputBufferBegin ;pointer na zaciatok vysielacieho buffera
SendUSBBuffer: ;poslanie kodovanim NRZI dany buffer do USB
ldi temp1,0 ;zvysovanie pointra (pomocna premenna)
mov temp3,ByteCount ;pocitadlo bytov: temp3 = ByteCount
ldi temp2,0b00000011 ;maska na xorovanie
ld inputbuf,Y+ ;nacitanie prveho bytu do inputbuf a zvys pointer do buffera
;USB ako vystup:
cbi outputport,DATAplus ;zhodenie DATAplus : kludovy stav portu USB
sbi outputport,DATAminus ;nahodenie DATAminus : kludovy stav portu USB
sbi USBdirection,DATAplus ;DATAplus ako vystupny
sbi USBdirection,DATAminus ;DATAminus ako vystupny

in temp0,outputport ;kludovy stav portu USB do temp0
SendUSBAnswerLoop:
ldi bitcount,7 ;pocitadlo bitov
SendUSBAnswerByteLoop:
nop ;oneskorenie kvoli casovaniu
ror inputbuf ;do carry vysielany bit (v smere naskor LSB a potom MSB)
brcs NoXORSend ;ak je jedna - nemen stav na USB
eor temp0,temp2 ;inak sa bude stav menit
NoXORSend:
out outputport,temp0 ;vysli von na USB
dec bitcount ;zmensi pocitadlo bitov - podla carry flagu
brne SendUSBAnswerByteLoop ;ak pocitadlo bitov nie je nulove - opakuj vysielanie s dalsim bitom
sbrs inputbuf,0 ;ak je vysielany bit jedna - nemen stav na USB
eor temp0,temp2 ;inak sa bude stav menit
NoXORSendLSB:
dec temp3 ;zniz pocitadlo bytov
ld inputbuf,Y+ ;nacitanie dalsieho bytu a zvys pointer do buffera
out outputport,temp0 ;vysli von na USB
brne SendUSBAnswerLoop ;opakuj pre cely buffer (pokial temp3=0)

mov bitcount,OutBitStuffNumber ;pocitadlo bitov pre bitstuff
cpi bitcount,0 ;ak nie je potrebny bitstuff
breq ZeroBitStuf
SendUSBAnswerBitstuffLoop:
ror inputbuf ;do carry vysielany bit (v smere naskor LSB a potom MSB)
brcs NoXORBitstuffSend ;ak je jedna - nemen stav na USB
eor temp0,temp2 ;inak sa bude stav menit
NoXORBitstuffSend:
out outputport,temp0 ;vysli von na USB
nop ;oneskorenie kvoli casovaniu
dec bitcount ;zmensi pocitadlo bitov - podla carry flagu
brne SendUSBAnswerBitstuffLoop ;ak pocitadlo bitov nie je nulove - opakuj vysielanie s dalsim bitom
ld inputbuf,Y ;oneskorenie 2 cykly
ZeroBitStuf:
nop ;oneskorenie 1 cyklus
cbr temp0,3
out outputport,temp0 ;vysli EOP na USB

ldi bitcount,5 ;pocitadlo oneskorenia: EOP ma trvat 2 bity (16 cyklov pri 12MHz)
SendUSBWaitEOP:
dec bitcount
brne SendUSBWaitEOP

sbi outputport,DATAminus ;nahodenie DATAminus : kludovy stav na port USB
sbi outputport,DATAminus ;oneskorenie 2 cykly: Idle ma trvat 1 bit (8 cyklov pri 12MHz)
cbi USBdirection,DATAplus ;DATAplus ako vstupny
cbi USBdirection,DATAminus ;DATAminus ako vstupny
cbi outputport,DATAminus ;zhodenie DATAminus : treti stav na port USB
ret
;------------------------------------------------------------------------------------------
ToggleDATAPID:
lds temp0,OutputBufferBegin+1 ;nahraj posledne PID
cpi temp0,DATA1PID ;ak bolo posledne DATA1PID byte
ldi temp0,DATA0PID
breq SendData0PID ;tak posli nulovu odpoved s DATA0PID
ldi temp0,DATA1PID ;inak posli nulovu odpoved s DATA1PID
SendData0PID:
sts OutputBufferBegin+1,temp0 ;DATA0PID byte
ret
;------------------------------------------------------------------------------------------
ComposeZeroDATA1PIDAnswer:
ldi temp0,DATA0PID ;DATA0 PID - v skutocnosti sa stoggluje na DATA1PID v nahrati deskriptora
sts OutputBufferBegin+1,temp0 ;nahraj do vyst buffera
ComposeZeroAnswer:
ldi temp0,SOPbyte
sts OutputBufferBegin+0,temp0 ;SOP byte
rcall ToggleDATAPID ;zmen DATAPID
ldi temp0,0x00
sts OutputBufferBegin+2,temp0 ;CRC byte
sts OutputBufferBegin+3,temp0 ;CRC byte
ldi ByteCount,2+2 ;dlzka vystupneho buffera (SOP a PID + CRC16)
ret
;------------------------------------------------------------------------------------------
InitACKBufffer:
ldi temp0,SOPbyte
sts ACKBufferBegin+0,temp0 ;SOP byte
ldi temp0,ACKPID
sts ACKBufferBegin+1,temp0 ;ACKPID byte
ret
;------------------------------------------------------------------------------------------
SendACK:
push USBBufptrY
push bitcount
push OutBitStuffNumber
ldi USBBufptrY,ACKBufferBegin ;pointer na zaciatok ACK buffera
ldi ByteCount,2 ;pocet vyslanych bytov (iba SOP a ACKPID)
clr OutBitStuffNumber
rcall SendUSBBuffer
pop OutBitStuffNumber
pop bitcount
pop USBBufptrY
ret
;------------------------------------------------------------------------------------------
InitNAKBufffer:
ldi temp0,SOPbyte
sts NAKBufferBegin+0,temp0 ;SOP byte
ldi temp0,NAKPID
sts NAKBufferBegin+1,temp0 ;NAKPID byte
ret
;------------------------------------------------------------------------------------------
SendNAK:
push OutBitStuffNumber
ldi USBBufptrY,NAKBufferBegin ;pointer na zaciatok ACK buffera
ldi ByteCount,2 ;pocet vyslanych bytov (iba SOP a NAKPID)
clr OutBitStuffNumber
rcall SendUSBBuffer
pop OutBitStuffNumber
ret
;------------------------------------------------------------------------------------------
ComposeSTALL:
ldi temp0,SOPbyte
sts OutputBufferBegin+0,temp0 ;SOP byte
ldi temp0,STALLPID
sts OutputBufferBegin+1,temp0 ;STALLPID byte
ldi ByteCount,2 ;dlzka vystupneho buffera (SOP a PID)
ret
;------------------------------------------------------------------------------------------
DecodeNRZI: ;enkodovanie buffera z NRZI kodu do binarneho
push USBBufptrY ;zalohuj pointer do buffera
push ByteCount ;zalohuj dlzku buffera
add ByteCount,USBBufptrY ;koniec buffera do ByteCount
ser temp0 ;na zabezpecenie jednotkoveho carry (v nasledujucej rotacii)
NRZIloop:
ror temp0 ;naplnenie carry z predchadzajuceho byte
ld temp0,Y ;nahraj prijaty byte z buffera
mov temp2,temp0 ;posunuty register o jeden bit vpravo a XOR na funkciu NRZI dekodovania
ror temp2 ;carry do najvyssieho bitu a sucasne posuv
eor temp2,temp0 ;samotne dekodovanie NRZI
com temp2 ;negovanie
st Y+,temp2 ;ulozenie spat ako dekodovany byte a zvys pointer do buffera
cp USBBufptrY,ByteCount ;ak este neboli vsetky
brne NRZIloop ;tak opakuj
pop ByteCount ;obnov dlzku buffera
pop USBBufptrY ;obnov pointer do buffera
ret ;inak skonci
;------------------------------------------------------------------------------------------
BitStuff: ;odstranenie bit-stuffingu v buffri
clr temp3 ;pocitadlo vynechanych bitov
clr lastBitstufNumber ;0xFF do lastBitstufNumber
dec lastBitstufNumber
BitStuffRepeat:
push USBBufptrY ;zalohuj pointer do buffera
push ByteCount ;zalohuj dlzku buffera
mov temp1,temp3 ;pocitadlo vsetkych bitov
ldi temp0,8 ;spocitat vsetky bity v bufferi
SumAllBits:
add temp1,temp0
dec ByteCount
brne SumAllBits
ldi temp2,6 ;inicializuj pocitadlo jednotiek
pop ByteCount ;obnov dlzku buffera
push ByteCount ;zalohuj dlzku buffera
add ByteCount,USBBufptrY ;koniec buffera do ByteCount
inc ByteCount ;a pre istotu ho zvys o 2 (kvoli posuvaniu)
inc ByteCount
BitStuffLoop:
ld temp0,Y ;nahraj prijaty byte z buffera
ldi bitcount,8 ;pocitadlo bitov v byte
BitStuffByteLoop:
ror temp0 ;naplnenie carry z LSB
brcs IncrementBitstuff ;ak LSB=0
ldi temp2,7 ;inicializuj pocitadlo jednotiek +1 (ak bola nula)
IncrementBitstuff:
dec temp2 ;zniz pocitadlo jednotiek (predpoklad jednotkoveho bitu)
brne NeposunBuffer ;ak este nebolo 6 jednotiek za sebou - neposun buffer
cp temp1,lastBitstufNumber ;
ldi temp2,6 ;inicializuj pocitadlo jednotiek (ak by sa nerobil bitstuffing tak sa musi zacat odznova)
brcc NeposunBuffer ;ak sa tu uz robil bitstuffing - neposun buffer

dec temp1
mov lastBitstufNumber,temp1 ;zapamataj si poslednu poziciu bitstuffingu
cpi bitcount,1 ;aby sa ukazovalo na 7 bit (ktory sa ma vymazat alebo kde sa ma vlozit nula)
brne NoBitcountCorrect
ldi bitcount,9
inc USBBufptrY ;zvys pointer do buffera
NoBitcountCorrect:
dec bitcount
bst BitStuffInOut,0
brts CorrectOutBuffer ;ak je Out buffer - zvys dlzku buffera
rcall PosunDeleteBuffer ;posun In buffer
dec temp3 ;zniz pocitadlo vynechani
rjmp CorrectBufferEnd
CorrectOutBuffer:
rcall PosunInsertBuffer ;posun Out buffer
inc temp3 ;zvys pocitadlo vynechani
CorrectBufferEnd:
pop ByteCount ;obnov dlzku buffera
pop USBBufptrY ;obnov pointer do buffera
rjmp BitStuffRepeat ;a restartni od zaciatku
NeposunBuffer:
dec temp1 ;ak uz boli vsetky bity
breq EndBitStuff ;ukonci cyklus
dec bitcount ;zniz pocitadlo bitov v byte
brne BitStuffByteLoop ;ak este neboli vsetky bity v byte - chod na dalsi bit
;inak nahraj dalsi byte
inc USBBufptrY ;zvys pointer do buffera
rjmp BitStuffLoop ;a opakuj
EndBitStuff:
pop ByteCount ;obnov dlzku buffera
pop USBBufptrY ;obnov pointer do buffera
bst BitStuffInOut,0
brts IncrementLength ;ak je Out buffer - zvys dlzku Out buffera
DecrementLength: ;ak je In buffer - zniz dlzku In buffera
cpi temp3,0 ;bolo aspon jedno znizenie
breq NoChangeByteCount ;ak nie - nemen dlzku buffera
dec ByteCount ;ak je In buffer - zniz dlzku buffera
subi temp3,256-8 ;ak nebolo viac ako 8 bitov naviac
brcc NoChangeByteCount ;tak skonci
dec ByteCount ;inak este zniz dlzku buffera
ret ;a skonci
IncrementLength:
mov OutBitStuffNumber,temp3 ;zapamataj si pocet bitov naviac
subi temp3,8 ;ak nebolo viac ako 8 bitov naviac
brcs NoChangeByteCount ;tak skonci
inc ByteCount ;inak zvys dlzku buffera
mov OutBitStuffNumber,temp3 ;a zapamataj si pocet bitov naviac (znizene o 8)
NoChangeByteCount:
ret ;skonci
;------------------------------------------------------------------------------------------
PosunInsertBuffer: ;posuv buffera o jeden bit vpravo od konca az po poziciu: byte-USBBufptrY a bit-bitcount
mov temp0,bitcount ;vypocet: bitcount= 9-bitcount
ldi bitcount,9
sub bitcount,temp0 ;do bitcount poloha bitu, ktory treba nulovat

ld temp1,Y ;nahraj byte ktory este treba posunut od pozicie bitcount
rol temp1 ;a posun vlavo cez Carry (prenos z vyssieho byte a LSB do Carry)
ser temp2 ;FF do masky - temp2
HalfInsertPosuvMask:
lsl temp2 ;nula do dalsieho spodneho bitu masky
dec bitcount ;az pokial sa nedosiahne hranica posuvania v byte
brne HalfInsertPosuvMask

and temp1,temp2 ;odmaskuj aby zostali iba vrchne posunute bity v temp1
com temp2 ;invertuj masku
lsr temp2 ;posun masku vpravo - na vlozenie nuloveho bitu
ld temp0,Y ;nahraj byte ktory este treba posunut od pozicie bitcount do temp0
and temp0,temp2 ;odmaskuj aby zostali iba spodne neposunute bity v temp0
or temp1,temp0 ;a zluc posunutu a neposunutu cast

ld temp0,Y ;nahraj byte ktory este treba posunut od pozicie bitcount
rol temp0 ;a posun ho vlavo cez Carry (aby sa nastavilo spravne Carry pre dalsie prenosy)
st Y+,temp1 ;a nahraj spat upraveny byte
PosunInsertBufferLoop:
cpse USBBufptrY,ByteCount ;ak nie su vsetky cele byty
rjmp NoEndPosunInsertBuffer ;tak pokracuj
ret ;inak skonci
NoEndPosunInsertBuffer:
ld temp1,Y ;nahraj byte
rol temp1 ;a posun vlavo cez Carry (prenos z nizsieho byte a LSB do Carry)
st Y+,temp1 ;a nahraj spat
rjmp PosunInsertBufferLoop ;a pokracuj
;------------------------------------------------------------------------------------------
PosunDeleteBuffer: ;posuv buffera o jeden bit vlavo od konca az po poziciu: byte-USBBufptrY a bit-bitcount
mov temp0,bitcount ;vypocet: bitcount= 9-bitcount
ldi bitcount,9
sub bitcount,temp0 ;do bitcount poloha bitu, ktory este treba posunut
mov temp0,USBBufptrY ;uschovanie pointera do buffera
inc temp0 ;pozicia celych bytov do temp0
mov USBBufptrY,ByteCount ;maximalna pozicia do pointra
PosunDeleteBufferLoop:
ld temp1,-Y ;zniz buffer a nahraj byte
ror temp1 ;a posun vpravo cez Carry (prenos z vyssieho byte a LSB do Carry)
st Y,temp1 ;a nahraj spat
cpse USBBufptrY,temp0 ;ak nie su vsetky cele byty
rjmp PosunDeleteBufferLoop ;tak pokracuj

ld temp1,-Y ;zniz buffer a nahraj byte ktory este treba posunut od pozicie bitcount
ror temp1 ;a posun vpravo cez Carry (prenos z vyssieho byte a LSB do Carry)
ser temp2 ;FF do masky - temp2
HalfDeletePosuvMask:
dec bitcount ;az pokial sa nedosiahne hranica posuvania v byte
breq DoneMask
lsl temp2 ;nula do dalsieho spodneho bitu masky
rjmp HalfDeletePosuvMask
DoneMask:
and temp1,temp2 ;odmaskuj aby zostali iba vrchne posunute bity v temp1
com temp2 ;invertuj masku
ld temp0,Y ;nahraj byte ktory este treba posunut od pozicie bitcount do temp0
and temp0,temp2 ;odmaskuj aby zostali iba spodne neposunute bity v temp0
or temp1,temp0 ;a zluc posunutu a neposunutu cast
st Y,temp1 ;a nahraj spat
ret ;a skonci
;------------------------------------------------------------------------------------------
MirrorInBufferBytes:
push USBBufptrY
push ByteCount
ldi USBBufptrY,InputBufferBegin
rcall MirrorBufferBytes
pop ByteCount
pop USBBufptrY
ret
;------------------------------------------------------------------------------------------
MirrorBufferBytes:
add ByteCount,USBBufptrY ;ByteCount ukazuje na koniec spravy
MirrorBufferloop:
ld temp0,Y ;nahraj prijaty byte z buffera
ldi temp1,8 ;pocitadlo bitov
MirrorBufferByteLoop:
ror temp0 ;do carry dalsi najnizsi bit
rol temp2 ;z carry dalsi bit na obratene poradie
dec temp1 ;bol uz cely byte
brne MirrorBufferByteLoop ;ak nie tak opakuj dalsi najnizsi bit
st Y+,temp2 ;ulozenie spat ako obrateny byte a zvys pointer do buffera
cp USBBufptrY,ByteCount ;ak este neboli vsetky
brne MirrorBufferloop ;tak opakuj
ret ;inak skonci
;------------------------------------------------------------------------------------------
CheckCRCIn:
push USBBufptrY
push ByteCount
ldi USBBufptrY,InputBufferBegin
rcall CheckCRC
pop ByteCount
pop USBBufptrY
ret
;------------------------------------------------------------------------------------------
AddCRCOut:
push USBBufptrY
push ByteCount
ldi USBBufptrY,OutputBufferBegin
rcall CheckCRC
com temp0 ;negacia CRC
com temp1
st Y+,temp1 ;ulozenie CRC na koniec buffera (najskor MSB)
st Y,temp0 ;ulozenie CRC na koniec buffera (potom LSB)
dec USBBufptrY ;pointer na poziciu CRC
ldi ByteCount,2 ;otocit 2 byty CRC
rcall MirrorBufferBytes ;opacne poradie bitov CRC (pri vysielani CRC sa posiela naskor MSB)
pop ByteCount
pop USBBufptrY
ret
;------------------------------------------------------------------------------------------
CheckCRC: ;vstup: USBBufptrY = zaciatok spravy ,ByteCount = dlzka spravy
add ByteCount,USBBufptrY ;ByteCount ukazuje na koniec spravy
inc USBBufptrY ;nastav pointer na zaciatok spravy - vynechat SOP
ld temp0,Y+ ;nahraj PID do temp0
;a nastav pointer na zaciatok spravy - vynechat aj PID
cpi temp0,DATA0PID ;ci je DATA0 pole
breq ComputeDATACRC ;pocitaj CRC16
cpi temp0,DATA1PID ;ci je DATA1 pole
brne CRC16End ;ak nie tak skonci
ComputeDATACRC:
ser temp0 ;inicializacia zvysku LSB na 0xff
ser temp1 ;inicializacia zvysku MSB na 0xff
CRC16Loop:
ld temp2,Y+ ;nahraj spravu do temp2 a zvys pointer do buffera
ldi temp3,8 ;pocitadlo bitov v byte - temp3
CRC16LoopByte:
bst temp1,7 ;do T uloz MSB zvysku (zvysok je iba 16 bitovy - 8 bit vyssieho byte)
bld bitcount,0 ;do bitcount LSB uloz T - MSB zvysku
eor bitcount,temp2 ;XOR bitu spravy a bitu zvysku - v LSB bitcount
rol temp0 ;posun zvysok dolava - nizsi byte (dva byty - cez carry)
rol temp1 ;posun zvysok dolava - vyssi byte (dva byty - cez carry)
cbr temp0,1 ;znuluj LSB zvysku
lsr temp2 ;posun spravu doprava
ror bitcount ;vysledok XOR-u bitov z LSB do carry
brcc CRC16NoXOR ;ak je XOR bitu spravy a MSB zvysku = 0 , tak nerob XOR
ldi bitcount,CRC16poly>>8 ;do bitcount CRC polynom - vrchny byte
eor temp1,bitcount ;a urob XOR zo zvyskom a CRC polynomom - vrchny byte
ldi bitcount,CRC16poly ;do bitcount CRC polynom - spodny byte
eor temp0,bitcount ;a urob XOR zo zvyskom a CRC polynomom - spodny byte
CRC16NoXOR:
dec temp3 ;boli uz vsetky bity v byte
brne CRC16LoopByte ;ak nie, tak chod na dalsi bit
cp USBBufptrY,ByteCount ;bol uz koniec spravy
brne CRC16Loop ;ak nie tak opakuj
CRC16End:
ret ;inak skonci (v temp0 a temp1 je vysledok)
;------------------------------------------------------------------------------------------
LoadDescriptorFromROM:
lpm ;nahraj z pozicie ROM pointra do R0
st Y+,R0 ;R0 uloz do buffera a zvys buffer
adiw ZH:ZL,1 ;zvys ukazovatel do ROM
dec ByteCount ;pokial nie su vsetky byty
brne LoadDescriptorFromROM ;tak nahravaj dalej
rjmp EndFromRAMROM ;inak skonci
;------------------------------------------------------------------------------------------
LoadDescriptorFromROMZeroInsert:
lpm ;nahraj z pozicie ROM pointra do R0
st Y+,R0 ;R0 uloz do buffera a zvys buffer

bst RAMread,3 ;ak je 3 bit jednotkovy - nebude sa vkladat nula
brtc InsertingZero ;inak sa bude vkladat nula
adiw ZH:ZL,1 ;zvys ukazovatel do ROM
lpm ;nahraj z pozicie ROM pointra do R0
st Y+,R0 ;R0 uloz do buffera a zvys buffer
clt ;a znuluj
bld RAMread,3 ;treti bit v RAMread - aby sa v dalsom vkladali nuly
rjmp InsertingZeroEnd ;a pokracuj
InsertingZero:
clr R0 ;na vkladanie nul
st Y+,R0 ;nulu uloz do buffera a zvys buffer
InsertingZeroEnd:
adiw ZH:ZL,1 ;zvys ukazovatel do ROM
subi ByteCount,2 ;pokial nie su vsetky byty
brne LoadDescriptorFromROMZeroInsert ;tak nahravaj dalej
rjmp EndFromRAMROM ;inak skonci
;------------------------------------------------------------------------------------------
LoadDescriptorFromSRAM:
ld R0,Z ;nahraj z pozicie RAM pointra do R0
st Y+,R0 ;R0 uloz do buffera a zvys buffer
inc ZL ;zvys ukazovatel do RAM
dec ByteCount ;pokial nie su vsetky byty
brne LoadDescriptorFromSRAM ;tak nahravaj dalej
rjmp EndFromRAMROM ;inak skonci
;------------------------------------------------------------------------------------------
LoadDescriptorFromEEPROM:
out EEAR,ZL ;nastav adresu EEPROM
sbi EECR,EERE ;vycitaj EEPROM do registra EEDR
in R0,EEDR ;nahraj z EEDR do R0
st Y+,R0 ;R0 uloz do buffera a zvys buffer
inc ZL ;zvys ukazovatel do RAM
dec ByteCount ;pokial nie su vsetky byty
brne LoadDescriptorFromEEPROM;tak nahravaj dalej
rjmp EndFromRAMROM ;inak skonci
;------------------------------------------------------------------------------------------
LoadXXXDescriptor:
ldi temp0,SOPbyte ;SOP byte
sts OutputBufferBegin,temp0 ;na zaciatok vysielacieho buffera dat SOP
ldi ByteCount,8 ;8 bytov nahrat
ldi USBBufptrY,OutputBufferBegin+2 ;do vysielacieho buffera

and RAMread,RAMread ;ci sa bude citat z RAM alebo ROM-ky alebo EEPROM-ky
brne FromRAMorEEPROM ;0=ROM,1=RAM,2=EEPROM,4=ROM s vkladanim nuly
FromROM:
rjmp LoadDescriptorFromROM ;nahrat descriptor z ROM-ky
FromRAMorEEPROM:
sbrc RAMread,2 ;ak RAMread=4
rjmp LoadDescriptorFromROMZeroInsert ;citaj z ROM s vkladanim nuly
sbrc RAMread,0 ;ak RAMread=1
rjmp LoadDescriptorFromSRAM ;nahraj data zo SRAM-ky
rjmp LoadDescriptorFromEEPROM ;inak citaj z EEPROM
EndFromRAMROM:
sbrc RAMread,7 ;ak je najvyssi bit v premennej RAMread=1
clr RAMread ;znuluj RAMread
rcall ToggleDATAPID ;zmenit DATAPID
ldi USBBufptrY,OutputBufferBegin+1 ;do vysielacieho buffera - pozicia DATA PID
ret
;------------------------------------------------------------------------------------------
PrepareUSBOutAnswer: ;pripravenie odpovede do buffera
rcall PrepareUSBAnswer ;pripravenie odpovede do buffera
MakeOutBitStuff:
inc BitStuffInOut ;vysielaci buffer - vkladanie bitstuff bitov
ldi USBBufptrY,OutputBufferBegin ;do vysielacieho buffera
rcall BitStuff
mov OutputBufferLength,ByteCount ;dlzku odpovede zapamatat pre vysielanie
clr BitStuffInOut ;prijimaci buffer - mazanie bitstuff bitov
ret
;------------------------------------------------------------------------------------------
PrepareUSBAnswer: ;pripravenie odpovede do buffera
clr RAMread ;nulu do RAMread premennej - cita sa z ROM-ky
lds temp0,InputBufferBegin+2 ;bmRequestType do temp0
lds temp1,InputBufferBegin+3 ;bRequest do temp1
cbr temp0,0b10011111 ;ak je 5 a 6 bit nulovy
brne VendorRequest ;tak to nie je Vendor Request
rjmp StandardRequest ;ale je to standardny request
;--------------------------
DoSetInfraBufferEmpty:
clr InfraBufferFull ;nastav prazdnost buffera
SendZeroAnswerInfraEmpty:
rjmp OneZeroAnswer ;potvrd prijem jednou nulou
;--------------------------
VendorRequest:
clr ZH ;pre citanie z RAM alebo EEPROM

cpi temp1,1 ;
breq DoSetInfraBufferEmpty ;restartne infra prijimanie (ak bolo zastavene citanim z RAM-ky)

cpi temp1,2 ;
breq DoGetInfraCode ;vysle prijaty infra kod (ak je v bufferi)

cpi temp1,3 ;
breq DoSetDataPortDirection ;nastavi smer toku datovych bitov
cpi temp1,4 ;
breq DoGetDataPortDirection ;zisti smer toku datovych bitov

cpi temp1,5 ;
breq DoSetOutDataPort ;nastavi datove bity (ak su vstupne, tak ich pull-up)
cpi temp1,6 ;
breq DoGetOutDataPort ;zisti nastavenie datovych out bitov (ak su vstupne, tak ich pull-up)

cpi temp1,7 ;
breq DoGetInDataPort ;vrati hodnotu datoveho vstupneho portu

cpi temp1,8 ;
breq DoEEPROMRead ;vrati obsah EEPROM od urcitej adresy
cpi temp1,9 ;
breq DoEEPROMWrite ;zapise EEPROM na urcitu adresu urcite data

cpi temp1,10 ;
breq DoRS232Send ;vysle byte na seriovy linku
cpi temp1,11 ;
breq DoRS232Read ;vrati prijaty byte zo seriovej linky (ak sa nejaky prijal)

cpi temp1,12 ;
breq DoSetRS232Baud ;nastavi prenosovu rychlost seriovej linky
cpi temp1,13 ;
breq DoGetRS232Baud ;vrati prenosovu rychlost seriovej linky

rjmp ZeroDATA1Answer ;ak to bolo nieco nezname, tak priprav nulovu odpoved
;----------------------------- USER FUNCTIONS --------------------------------------
DoGetInfraCode:
cpi InfraBufferFull,0 ;ak je Infra Buffer prazdny
breq SendZeroAnswerInfraEmpty ;tak nic neposli
ldi ZL,InfraBufferBegin
lds temp1,InputBufferBegin+4 ;prvy parameter - offset v infra bufferi
add ZL,temp1 ;plus offset v buffri
lds temp0,InfraBufferBegin ;pocet mojich bytovych odpovedi do temp0
ldi temp1,3 ;plus 3 byty hlavicky
add temp0,temp1
inc RAMread ;RAMread=1 - cita sa z RAM-ky
rjmp ComposeEndXXXDescriptor ;a priprav data
DoEEPROMRead:
lds ZL,InputBufferBegin+4 ;prvy parameter - offset v EEPROM-ke
ldi temp0,2
mov RAMread,temp0 ;RAMread=2 - cita sa z EEPROM-ky
ldi temp0,E2END+1 ;pocet mojich bytovych odpovedi do temp0 - cela dlzka EEPROM
rjmp ComposeEndXXXDescriptor ;inak priprav data
DoEEPROMWrite:
lds ZL,InputBufferBegin+4 ;prvy parameter - offset v EEPROM-ke (adresa)
lds R0,InputBufferBegin+6 ;druhy parameter - data, ktore sa maju zapisat do EEPROM-ky (data)
rjmp EEPROMWrite ;zapis do EEPROM a aj ukonci prikaz
DoSetDataPortDirection:
lds ACC,InputBufferBegin+4 ;prvy parameter - smer datovych bitov
rcall SetDataPortDirection
rjmp OneZeroAnswer ;potvrd prijem jednou nulou
DoGetDataPortDirection:
rcall GetDataPortDirection
rjmp DoGetIn

DoSetOutDataPort:
lds ACC,InputBufferBegin+4 ;prvy parameter - hodnota datovych bitov
rcall SetOutDataPort
rjmp OneZeroAnswer ;potvrd prijem jednou nulou
DoGetOutDataPort:
rcall GetOutDataPort
rjmp DoGetIn

DoGetInDataPort:
rcall GetInDataPort
DoGetIn:
ldi ZL,0 ;posiela sa hodnota v R0
ldi temp0,0x81 ;RAMread=1 - cita sa z RAM-ky
mov RAMread,temp0 ;(najvyssi bit na 1 - aby sa hned premenna RAMread znulovala)
ldi temp0,1 ;posli iba jeden byte
rjmp ComposeEndXXXDescriptor ;a priprav data

DoSetRS232Baud:
lds temp0,InputBufferBegin+4 ;prvy parameter - hodnota baudrate na RS232
out UBRR,temp0 ;nastav rychlost UART-u
rjmp OneZeroAnswer ;potvrd prijem jednou nulou
DoGetRS232Baud:
in R0,UBRR ;vrat rychlost UART-u v R0
rjmp DoGetIn ;a ukonci
DoRS232Send:
lds temp0,InputBufferBegin+4 ;prvy parameter - hodnota vysielana na RS232
out UDR,temp0 ;vysli data na UART
WaitForRS232Send:
sbis UCR,TXEN ;ak nie je povoleny UART vysielac
rjmp OneZeroAnswer ;tak skonci - ochrana kvoli zacykleniu v AT90S2323/2343
sbis USR,TXC ;pockat na dovysielanie bytu
rjmp WaitForRS232Send
rjmp OneZeroAnswer ;potvrd prijem jednou nulou
DoRS232Read:
sbis USR,RXC ;ak je prijimaci register prazdny
rjmp TwoZeroAnswer ;tak iba potvrd prijem dvoma nulami
in R0,UDR ;inak nacitaj do R0 prijate data z UART-u
ldi ZL,0 ;posiela sa hodnota v R0
ldi temp0,0x81 ;RAMread=1 - cita sa z RAM-ky
mov RAMread,temp0 ;(najvyssi bit na 1 - aby sa hned premenna RAMread znulovala)
ldi temp0,1 ;posli iba jeden byte
sbic USR,OR ;ak bolo pretecenie prijimacieho buffera
ldi temp0,3 ;posli 3 byty :prvy je prijata hodnota (na ostatnych nezalezi)
rjmp ComposeEndXXXDescriptor ;tak priprav data
;----------------------------- END USER FUNCTIONS ----------------------------------

OneZeroAnswer: ;posle jednu nulu
ldi temp0,1 ;pocet mojich bytovych odpovedi do temp0
rjmp ComposeGET_STATUS2

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 ;ak sa ma nastavit adresa
breq ComposeSET_ADDRESS ;nastav adresu

cpi temp1,GET_DESCRIPTOR ;ak sa ziada descriptor
breq ComposeGET_DESCRIPTOR ;vygeneruj ho

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 ;
;ak sa nenasla znama poziadavka
rjmp ZeroDATA1Answer ;ak to bolo nieco nezname, tak priprav nulovu odpoved

ComposeSET_ADDRESS:
lds MyUpdatedAddress,InputBufferBegin+4 ;nova adresa do MyUpdatedAddress
rjmp ZeroDATA1Answer ;posli nulovu odpoved

ComposeSET_CONFIGURATION:
lds ConfigByte,InputBufferBegin+4 ;cislo konfiguracie do premennej ConfigByte
ComposeCLEAR_FEATURE:
ComposeSET_FEATURE:
ComposeSET_INTERFACE:
ZeroStringAnswer:
rjmp ZeroDATA1Answer ;posli nulovu odpoved
ComposeGET_STATUS:
TwoZeroAnswer:
ldi temp0,2 ;pocet mojich bytovych odpovedi do temp0
ComposeGET_STATUS2:
ldi ZH, high(StatusAnswer<<1) ;ROMpointer na odpoved
ldi ZL, low(StatusAnswer<<1)
rjmp ComposeEndXXXDescriptor ;a dokonci
ComposeGET_CONFIGURATION:
and ConfigByte,ConfigByte ;ak som nenakonfigurovany
breq OneZeroAnswer ;tak posli jednu nulu - inak posli moju konfiguraciu
ldi temp0,1 ;pocet mojich bytovych odpovedi do temp0
ldi ZH, high(ConfigAnswerMinus1<<1) ;ROMpointer na odpoved
ldi ZL, low(ConfigAnswerMinus1<<1)+1
rjmp ComposeEndXXXDescriptor ;a dokonci
ComposeGET_INTERFACE:
ldi ZH, high(InterfaceAnswer<<1) ;ROMpointer na odpoved
ldi ZL, low(InterfaceAnswer<<1)
ldi temp0,1 ;pocet mojich bytovych odpovedi do temp0
rjmp ComposeEndXXXDescriptor ;a dokonci
ComposeSYNCH_FRAME:
ComposeSET_DESCRIPTOR:
rcall ComposeSTALL
ret
ComposeGET_DESCRIPTOR:
lds temp1,InputBufferBegin+5 ;DescriptorType do temp1
cpi temp1,DEVICE ;DeviceDescriptor
breq ComposeDeviceDescriptor ;
cpi temp1,CONFIGURATION ;ConfigurationDescriptor
breq ComposeConfigDescriptor ;
cpi temp1,STRING ;StringDeviceDescriptor
breq ComposeStringDescriptor ;
ret
ComposeDeviceDescriptor:
ldi ZH, high(DeviceDescriptor<<1) ;ROMpointer na descriptor
ldi ZL, low(DeviceDescriptor<<1)
ldi temp0,0x12 ;pocet mojich bytovych odpovedi do temp0
rjmp ComposeEndXXXDescriptor ;a dokonci
ComposeConfigDescriptor:
ldi ZH, high(ConfigDescriptor<<1) ;ROMpointer na descriptor
ldi ZL, low(ConfigDescriptor<<1)
ldi temp0,9+9+7 ;pocet mojich bytovych odpovedi do temp0
ComposeEndXXXDescriptor:
lds TotalBytesToSend,InputBufferBegin+8 ;pocet pozadovanych bytov do TotalBytesToSend
cp TotalBytesToSend,temp0 ;ak sa neziada viac ako mozem dodat
brcs HostConfigLength ;vysli tolko kolko sa ziada
mov TotalBytesToSend,temp0 ;inak posli pocet mojich odpovedi
HostConfigLength:
mov temp0,TotalBytesToSend ;
clr TransmitPart ;nuluj pocet 8 bytovych odpovedi
andi temp0,0b00000111 ;ak je dlzka delitelna 8-mimi
breq Length8Multiply ;tak nezapocitaj jednu necelu odpoved (pod 8 bytov)
inc TransmitPart ;inak ju zapocitaj
Length8Multiply:
mov temp0,TotalBytesToSend ;
lsr temp0 ;dlzka 8 bytovych odpovedi sa dosiahne
lsr temp0 ;delenie celociselne 8-mimi
lsr temp0
add TransmitPart,temp0 ;a pripocitanim k poslednej necelej 8-mici do premennej TransmitPart
ldi temp0,DATA0PID ;DATA0 PID - v skutocnosti sa stoggluje na DATA1PID v nahrati deskriptora
sts OutputBufferBegin+1,temp0 ;nahraj do vyst buffera
rjmp ComposeNextAnswerPart
ComposeStringDescriptor:
ldi temp1,4+8 ;ak RAMread=4(vkladaj nuly z ROM-koveho citania) + 8(za prvy byte nevkldadaj nulu)
mov RAMread,temp1
lds temp1,InputBufferBegin+4 ;DescriptorIndex do temp1
cpi temp1,0 ;LANGID String
breq ComposeLangIDString ;
cpi temp1,2 ;DevNameString
breq ComposeDevNameString ;
brcc ZeroStringAnswer ;ak je DescriptorIndex vyssi nez 2 - posli nulovu odpoved
;inak to bude VendorString
ComposeVendorString:
ldi ZH, high(VendorStringDescriptor<<1) ;ROMpointer na descriptor
ldi ZL, low(VendorStringDescriptor<<1)
ldi temp0,(VendorStringDescriptorEnd-VendorStringDescriptor)*4-2 ;pocet mojich bytovych odpovedi do temp0
rjmp ComposeEndXXXDescriptor ;a dokonci
ComposeDevNameString:
ldi ZH, high(DevNameStringDescriptor<<1) ;ROMpointer na descriptor
ldi ZL, low(DevNameStringDescriptor<<1)
ldi temp0,(DevNameStringDescriptorEnd-DevNameStringDescriptor)*4-2 ;pocet mojich bytovych odpovedi do temp0
rjmp ComposeEndXXXDescriptor ;a dokonci
ComposeLangIDString:
clr RAMread
ldi ZH, high(LangIDStringDescriptor<<1) ;ROMpointer na descriptor
ldi ZL, low(LangIDStringDescriptor<<1)
ldi temp0,(LangIDStringDescriptorEnd-LangIDStringDescriptor)*2;pocet mojich bytovych odpovedi do temp0
rjmp ComposeEndXXXDescriptor ;a dokonci
;------------------------------------------------------------------------------------------
ZeroDATA1Answer:
rcall ComposeZeroDATA1PIDAnswer
ret
;------------------------------------------------------------------------------------------
PrepareOutContinuousBuffer:
rcall PrepareContinuousBuffer
rcall MakeOutBitStuff
ret
;------------------------------------------------------------------------------------------
PrepareContinuousBuffer:
mov temp0,TransmitPart
cpi temp0,1
brne NextAnswerInBuffer ;ak uz je buffer prazdny
rcall ComposeZeroAnswer ;priprav nulovu odpoved
ret
NextAnswerInBuffer:
dec TransmitPart ;znizit celkovu dlzku odpovede
ComposeNextAnswerPart:
mov temp1,TotalBytesToSend ;zniz pocet bytov na vyslanie
subi temp1,8 ;ci je este treba poslat viac ako 8 bytov
ldi temp3,8 ;ak ano - posli iba 8 bytov
brcc Nad8Bytov
mov temp3,TotalBytesToSend ;inak posli iba dany pocet bytov
clr TransmitPart
inc TransmitPart ;a bude to posledna odpoved
Nad8Bytov:
mov TotalBytesToSend,temp1 ;znizeny pocet bytov do TotalBytesToSend
rcall LoadXXXDescriptor
ldi ByteCount,2 ;dlzka vystupneho buffera (iba SOP a PID)
add ByteCount,temp3 ;+ pocet bytov
rcall AddCRCOut ;pridanie CRC do buffera
inc ByteCount ;dlzka vystupneho buffera + CRC16
inc ByteCount
ret ;skonci
;------------------------------------------------------------------------------------------
.equ USBversion =0x0100 ;pre aku verziu USB je to (1.00)
.equ VendorUSBID =0x03EB ;identifikator dodavatela (Atmel=0x03EB)
.equ DeviceUSBID =0x0002 ;identifikator vyrobku (USB dialkove ovladanie=0x0002)
.equ DeviceVersion =0x0001 ;cislo verzie vyrobku (verzia=0.01)
.equ MaxUSBCurrent =40 ;prudovy odber z USB (40mA)
;------------------------------------------------------------------------------------------
DeviceDescriptor:
.db 0x12,0x01 ;0 byte - velkost deskriptora v bytoch
;1 byte - typ deskriptora: Deskriptor zariadenia
.dw USBversion ;2,3 byte - verzia USB LSB (1.00)
.db 0x00,0x00 ;4 byte - trieda zariadenia
;5 byte - podtrieda zariadenia
.db 0x00,0x08 ;6 byte - kod protokolu
;7 byte - velkost FIFO v bytoch
.dw VendorUSBID ;8,9 byte - identifikator dodavatela (Cypress=0x04B4)
.dw DeviceUSBID ;10,11 byte - identifikator vyrobku (teplomer=0x0002)
.dw DeviceVersion ;12,13 byte - cislo verzie vyrobku (verzia=0.01)
.db 0x01,0x02 ;14 byte - index stringu "vyrobca"
;15 byte - index stringu "vyrobok"
.db 0x00,0x01 ;16 byte - index stringu "seriove cislo"
;17 byte - pocet moznych konfiguracii
DeviceDescriptorEnd:
;------------------------------------------------------------------------------------------
ConfigDescriptor:
.db 0x9,0x02 ;dlzka,typ deskriptoru
ConfigDescriptorLength:
.dw 9+9+7 ;celkova dlzka vsetkych deskriptorov
ConfigAnswerMinus1: ;pre poslanie cisla congiguration number (pozor je treba este pricitat 1)
.db 1,1 ;numInterfaces,congiguration number
.db 0,0x80 ;popisny index stringu, atributy;bus powered
.db MaxUSBCurrent/2,0x09 ;prudovy odber, interface descriptor length
.db 0x04,0 ;interface descriptor; cislo interface
InterfaceAnswer: ;pre poslanie cisla alternativneho interface
.db 0,1 ;alternativne nastavenie interface; pocet koncovych bodov okrem EP0
StatusAnswer: ;2 nulove odpovede (na usetrenie miestom)
.db 0,0 ;trieda rozhrania; podtrieda rozhrania
.db 0,0 ;kod protokolu; index popisneho stringu
.db 0x07,0x5 ;dlzka,typ deskriptoru - endpoint
.db 0x81,0 ;endpoint address; transfer type
.dw 0x08 ;max packet size
.db 10,0 ;polling interval [ms]; dummy byte (pre vyplnenie)
ConfigDescriptorEnd:
;------------------------------------------------------------------------------------------
LangIDStringDescriptor:
.db (LangIDStringDescriptorEnd-LangIDStringDescriptor)*2,3 ;dlzka, typ: string deskriptor
.dw 0x0409 ;English
LangIDStringDescriptorEnd:
;------------------------------------------------------------------------------------------
VendorStringDescriptor:
.db (VendorStringDescriptorEnd-VendorStringDescriptor)*4-2,3 ;dlzka, typ: string deskriptor
CopyRight:
.db "Ing. Igor Cesko, Copyright(c) 2003"
CopyRightEnd:
VendorStringDescriptorEnd:
;------------------------------------------------------------------------------------------
DevNameStringDescriptor:
.db (DevNameStringDescriptorEnd-DevNameStringDescriptor)*4-2,3;dlzka, typ: string deskriptor
.db "IgorPlug-USB (AVR)"
DevNameStringDescriptorEnd:
;------------------------------------------------------------------------------------------
MaskPortData:
bst ACC,0
bld temp0,LEDlsb0
bst ACC,1
bld temp0,LEDlsb1
bst ACC,2
bld temp0,LEDlsb2
bst ACC,3
bld temp1,LEDmsb3
bst ACC,4
bld temp1,LEDmsb4
bst ACC,5
bld temp1,LEDmsb5
bst ACC,6
bld temp1,LEDmsb6
bst ACC,7
bld temp1,LEDmsb7
ret
;------------------------------------------------------------------------------------------
SetDataPortDirection:
in temp0,LEDdirectionLSB ;nacitaj aktualny stav LSB do temp0 (aby sa nezmenili ostatne smery bitov)
in temp1,LEDdirectionMSB ;nacitaj aktualny stav MSB do temp1 (aby sa nezmenili ostatne smery bitov)
rcall MaskPortData
out LEDdirectionLSB,temp0 ;a update smeru LSB datoveho portu
out LEDdirectionMSB,temp1 ;a update smeru MSB datoveho portu
ret
;------------------------------------------------------------------------------------------
SetOutDataPort:
in temp0,LEDPortLSB ;nacitaj aktualny stav LSB do temp0 (aby sa nezmenili ostatne bity)
in temp1,LEDPortMSB ;nacitaj aktualny stav MSB do temp1 (aby sa nezmenili ostatne bity)
rcall MaskPortData
out LEDPortLSB,temp0 ;a update LSB datoveho portu
out LEDPortMSB,temp1 ;a update MSB datoveho portu
ret
;------------------------------------------------------------------------------------------
GetInDataPort:
in temp0,LEDPinMSB ;nacitaj aktualny stav MSB do temp0
in temp1,LEDPinLSB ;nacitaj aktualny stav LSB do temp1
MoveLEDin:
bst temp1,LEDlsb0 ;a daj bity LSB na spravne pozicie (z temp1 do temp0)
bld temp0,0 ;(bity MSB su na spravnom mieste)
bst temp1,LEDlsb1
bld temp0,1
bst temp1,LEDlsb2
bld temp0,2
mov R0,temp0 ;a vysledok uloz do R0
ret
;------------------------------------------------------------------------------------------
GetOutDataPort:
in temp0,LEDPortMSB ;nacitaj aktualny stav MSB do temp0
in temp1,LEDPortLSB ;nacitaj aktualny stav LSB do temp1
rjmp MoveLEDin
;------------------------------------------------------------------------------------------
GetDataPortDirection:
in temp0,LEDdirectionMSB ;nacitaj aktualny stav MSB do temp0
in temp1,LEDdirectionLSB ;nacitaj aktualny stav LSB do temp1
rjmp MoveLEDin
;------------------------------------------------------------------------------------------
EEPROMWrite:
out EEAR,ZL ;nastav adresu EEPROM
out EEDR,R0 ;nastav data do EEPROM
cli ;zakaz prerusenie
sbi EECR,EEMWE ;nastav master write enable
sei ;povol prerusenie (este sa vykona nasledujuca instrukcia)
sbi EECR,EEWE ;samotny zapis
WaitForEEPROMReady:
sbic EECR,EEWE ;pockaj si na koniec zapisu
rjmp WaitForEEPROMReady ;v slucke (max cca 4ms) (kvoli naslednemu citaniu/zapisu)
rjmp OneZeroAnswer ;potvrd prijem jednou nulou
;------------------------------------------------------------------------------------------
;********************************************************************
;* End of Program
;********************************************************************
;------------------------------------------------------------------------------------------
;********************************************************************
;* EEPROM contents
;********************************************************************
;------------------------------------------------------------------------------------------
.eseg ;data v EEPROM-ke (vo finalnej verzii zapoznamkovat)
;.org 0x400 ;pre naplnenie EEPROM dat na spravne adresy - hned za kod programu (vo finalnej verzii odpoznamkovat)
EEData:
.db "This device was developed by Ing. Igor Cesko: cesko@internet.sk "
.db "For more information see: http://www.cesko.host.sk. "
.db "S/N:00000001"
;------------------------------------------------------------------------------------------
;********************************************************************
;* End of file
;********************************************************************
