www.pudn.com > windows2000XP_WDM_DeviceDriverDevelopment_WuAnHe_C > Counter.ASM


; Port Usage 
;  P1.0 - 
;    .1 - 
;    .2 - Button (0=pushed) (input) 
;    .3 - LED (0=on)        (output) 
; 
;******************************************************************************* 
 
;//$PAGE 
; Directives 
FillROM 0 
 
; Microprocessor definitions 
include "63x0x.inc" 
 
;************************************************* 
; Data Segment (RAM) 
;************************************************* 
 
; Program Stack 
gbSysProgramStack       :equ 00h    ; [00h-1Fh] Stack 0x20h 
gbSysDataStack          :equ 50h    ; [50h-6Fh] Stack 0x70h 
gbSysFIFO               :equ 70h    ; [70h-7Fh] EP0 and EP1 FIFO's 
 
; Global Interrupt 
gbSysInterruptMask      :equ 20h    ; Holds the current interrupt mask 
 
; USB management data 
gbUSBSendSequence       :equ 28h    ; Buffer send data 0/1 line 
gbUSBSendBytes          :equ 29h    ; Buffer bytes left to send 
gbUSBSendBuffer         :equ 2Ah    ; Offset into current buffer 
 
gbSuspendCount          :equ 2Bh    ; # of msec bus has been IDLE 
 
; Button management 
gbButtonDebounce        :equ 2Ch    ; Debounce count down value 
gbButtonClicks          :equ 2Dh    ; Button count value 
 
Button_Pin              :equ 04h    ; Pin the switch is on, P12 
LED_ON                  :equ 08h    ; P13 is used to indicate Enumeration 
 
;//$PAGE 
;************************************************* 
; Code Segment (ROM) 
;************************************************* 
 
; Vector Table 
org 00h 
    jmp main                ; Reset of some type 
    jmp SysUnUsed           ; 128us timer (not used) 
    jmp SysTimer1024usEvent ; 1024us timer 
    jmp USBEndPoint0Event   ; EP0 
    jmp USBEndPoint1Event   ; EP1 (not used) 
    jmp SysUnUsed           ; Reserved 
    jmp SysGPIOEvent        ; Button 
    jmp SysUnUsed           ; CExt (not used) 
 
;************************************************* 
; Unused event 
; Do nothing, restore machine to prior state 
;************************************************* 
SysUnUsed:  
    push  a 
    mov   a,[gbSysInterruptMask] 
    ipret SysInterrupt 
 
;//$PAGE 
;******************************************************************************* 
; main() 
; @func Entry point after PowerOn, WatchDog timeout or WakeUp from sleeping. 
; @comm Never returns 
;******************************************************************************* 
main: 
    ; This portion of Main is only executed after a RESET (Power-On or USB) 
    ; Setup data stack in high order RAM, just below EP0 FIFO 
    ; It will grow down from here 
    mov  a,70h      ; USBEndP0FIFO 
    swap a,dsp 
 
    ; Initialize both Ports high  
    mov  a,FFh 
    iowr SysPort0   ; Port 0 Data reg 
    iowr SysPort1   ; Port 1 Data reg  
                    ;   1 on P13 is needed to make sure enumerate LED is off 
                    ;   1 on any port that needs to be an input 
    ; Enable Pullups (0=enable) 
    mov  a,0 
    iowr SysPort0PullUp 
    mov  a,Button_Pin 
    iowr SysPort1PullUp     ; 1 on P12 is needed to make sure GPIO interrupt 
                            ;   occurs on LOW to HIGH transistion. This 
                            ;   disables it's pull up 
                
    ; Enable or disable interrupts on appropriate pins 
    mov  a,0 
    iowr SysPort0IntEnable     ; All pins irq's are disabled on Port 0 
    mov  a,Button_Pin               
    iowr SysPort1IntEnable     ; Enable P12, the button pin. 
                               ; No interrupts will occur until the device 
                               ;   is enumerated. Then GPIO's will be enabled and 
                               ;   we will allow P12 to generate interrupts 
 
    ; Initialize USB variables 
    mov  a,0 
    mov  [gbUSBSendSequence],a  ; Start with a 0 
 
    ; Initialize Counter 
    mov  a,0 
    mov  [gbButtonClicks],a     ; Initial state of 0, no button pushed 
 
    ; Initialize variables 
    mov a,0 
    mov [gbUSBSendBytes],a      ; No bytes to send in FIFO buffers 
    mov [gbSuspendCount],a      ; Reset bus activity to 0 
    mov [gbButtonDebounce],a    ; We are not debouncing 
 
    ; Set interrupt mask 
    mov  a,SysIntTimer1024us  | SysIntUSBEndP0  
    mov  [gbSysInterruptMask],a 
 
    ;********************************************* 
  MainLoop: 
    ; Enable interrupts to current mask 
    mov  a,[gbSysInterruptMask] 
    iowr SysInterrupt 
 
    ; Loop 
    jmp  MainLoop 
 
;******************************************************** 
; SysTimer1024usEvent() 
; @func Timer interrupt event ocurring every 1.024 mSec 
;       using 6Mhz crystal. 
;******************************************************** 
SysTimer1024usEvent: 
 
    ; Save accumulator 
    push a 
 
    ; Clear watchdog timer 
    ; Clearing it here effectively disables the timer 
    iowr SysWatchDog 
 
    ; Keep track of length of any IDLE conditions (No bus activity) 
    iord USBControl                    ; Read the USB Status and Control Reg 
    and a,01h                          ; Check bit 0 
    cmp a,0h 
    jz Inc_Counter                     ; Hmm! No activity. Branch and keep track of it. 
    iord USBControl                    ; Ah! There was activity,  
                                       ;  clear the bus activity bit 
    and a,0feh 
    iowr USBControl 
    mov a,0                            ; Clear the suspend counter 
    mov [gbSuspendCount],a 
    jmp Suspend_End 
 
  Inc_Counter:                         ; Monitor the IDLE count 
    mov a,[gbSuspendCount]             ; Get # of mSec we have been IDLE 
    inc a                              ; Increment the count 
    mov [gbSuspendCount],a               
    cmp a,03h                          ; Has it been 3msec yet? 
    jnz Suspend_End                    ; Not yet, branch 
    mov a,0h                           ; Yes, clear the suspend counter 
    mov [gbSuspendCount],a 
    iord SysStatus 
    or  a,08h                          ; Set the suspend bit to cause a suspend 
    iowr SysStatus                     ; We will enter the suspend state during 
                                       ;   the next instruction.    
 
  Suspend_End: 
    ; Are we counting down a button debounce 
    mov  a,0 
    cmp  a,[gbButtonDebounce] 
    jz   STimerNoDebounce       ; Not debouncing, branch 
 
    ; Yes, we're debouncing. Let's see if we are timed out. 
    dec  [gbButtonDebounce] 
    mov  a,0 
    cmp  a,[gbButtonDebounce] 
 
    ; has debounce timed out? 
    jnz  STimerNoDebounce        ; No, still debouncing, branch. 
 
    ; The debounce timer has timed out 
    ; check if the button pin is at a 1. If not, the button is either still 
    ;    bouncing or still pushed 
    iord SysPort1              ; check the port the button is on 
    and  a,Button_Pin          ; check the pin 
    jz   STimerNoDebounce      ; branch if it is not pushed 
 
    ; Reset debounce since the button is not yet released or is bouncing 
    mov  a,[gbButtonClicks] 
    inc  a; 
    mov  [gbButtonClicks],a 
    mov  [USBEndP1FIFO_0],a 
 
    iord USBEndP1TxConfig 
    and  a,40h 
    or   a,91h 
    iowr USBEndP1TxConfig 
 
    ; Debounce must be over 
  STimerNoDebounce: 
    ; Enable interrupts and return 
    mov  a,[gbSysInterruptMask] 
    ipret SysInterrupt 
 
;//$PAGE 
;******************************************************** 
; SysGPIOEvent() 
; @func General purpose port event 
; @comm Which pin? 
;******************************************************** 
SysGPIOEvent: 
 
    ; Save accumulator 
    push a 
 
    ; Reset debounce any time we are here 
    mov  a,50 
    mov  [gbButtonDebounce],a 
 
  SysGPIOButtonDebouncing: 
    ; Enable interrupts and return 
    mov  a,[gbSysInterruptMask] 
    ipret SysInterrupt 
 
USBEndPoint1Event: 
 
    ; Save accumulator 
    push a 
 
    iord USBEndP1TxConfig 
    xor a,40h 
    iowr USBEndP1TxConfig 
 
    ; Enable interrupts and return 
    mov  a,[gbSysInterruptMask] 
    ipret SysInterrupt 
 
;******************************************************************************* 
; 
;    This section of code responds to activity on End Point 0 and determines 
;    what needs to be done. 
; 
;******************************************************************************* 
 
;//$PAGE 
;******************************************************** 
; USBEndPoint0Event() 
; @func End Point zero USB event. 
; @comm Default end point. 
;******************************************************** 
USBEndPoint0Event: 
 
    ; This code checks to see what type of packet was received  
    ;   (Setup, Out, or In) and jumps to the correct routine to decode the  
    ;   specifics. After the code to which the jump points is through, it jumps 
    ;   back to USBEventEP0End. 
 
    ; Save accumulator 
    push a 
 
    ; Is this a SETUP packet? 
    iord USBEndP0RxStatus 
    and  a,USBEndP0RxSetup      ; Check the setup bit 
    jnz  USBEventEP0_SETUP      ; Yes it's a setup, branch 
 
  USBEventEP0End: 
    ; OK. We're done with the packet. 
    ;   Let's enable interrupts and return 
    mov  a,[gbSysInterruptMask] 
    ipret SysInterrupt    ; done with EP0 irq service routine 
 
  USBEventEP0Stall: 
    ; Stall any subsequent IN's or OUT's until the 
    ;       stall bit (bit 5) is cleard by an I/O write to 
    ;       the  USB End Point 0 TX Configuration Register (0x10) 
    ;       or any SETUP is received. 
    iord USBEndP0TxConfig 
    or   a,USBEndP0TxStall 
    iowr USBEndP0TxConfig 
 
    ; OK. We've set the stall condition for Endpoint 0. 
    ;   Now let's complete the routine. 
    jmp  USBEventEP0End 
 
;******************************************************************************* 
;  
;    We know we have received a Setup token. Now we need to parse it to 
;    determine what command it is. 
; 
;******************************************************************************* 
 
;//$PAGE 
;******************************************************************************* 
; USBEventEP0_SETUP() 
; @func End point event SETUP packet handler. 
; @devnote Runs in interrupt enabled context. 
;******************************************************** 
USBEventEP0_SETUP: 
    ; Well, we have a SETUP packet. Let's find out what to do. 
    mov A,[gbSysInterruptMask] 
    iowr SysInterrupt 
 
    ; If we are here and are and we are processing a previous Setup,  
    ;  we need to abort the processing of the previous Setup 
    mov  a,0    ; Clear any indication that we have bytes left to transfer 
    mov [gbUSBSendBytes],a 
  
    ; Clear EP0 RxReg (including the Setup flag) 
    ; The Data toggle bit remains unchanged, however. 
    mov  a,0 
    iowr USBEndP0RxStatus 
 
    ;********************************************* 
    ; Setup Event 
    ;********************************************* 
 
    ; Check the request type and branch to the correct location to handle it. 
    mov  a,[USBEndP0FIFO_0] 
 
  USBEventEP0SetupTargetDeviceOUT: 
    ; Target Device? 
    cmp  a,USBRqstTargetDevice 
    jz   USBEventEP0SetupIsSetAddress   ; Yes 
 
  USBEventEP0SetupTargetInterfaceOUT: 
    cmp  a,USBRqstTargetInterface 
    jz   USBEventEP0Stall               ; Yes. Oops! We don't have an interface. 
 
  USBEventEP0SetupTargetEndpointOUT: 
    cmp  a,USBRqstTargetEndPoint 
    jz   USBEventEP0Stall               ; Yes 
 
  USBEventEP0SetupTargetDeviceIN: 
    cmp  a,USBRqstTargetDevice | USBRqstTypeDirection 
    jz   USBEventEP0SetupGetDescriptor  ; Yes 
 
  USBEventEP0SetupTargetInterfaceIN: 
    cmp  a,USBRqstTargetInterface | USBRqstTypeDirection 
    jz   USBEventEP0Stall               ; Yes Oops! We don't have an interface. 
 
  USBEventEP0SetupTargetEndpointIN: 
    cmp  a,USBRqstTargetEndPoint | USBRqstTypeDirection 
    jz   USBEventEP0Stall               ; Yes 
 
    ; Vendor specific commands 
  USBEventEP0SetupTargetVendorIN_OUT: 
    ; Check request (IN packet OK, OUT packet ERR) 
    mov  a,[USBEndP0FIFO_0] 
    and  a,USBRqstTypeVendor | USBRqstTargetEndPoint | USBRqstTypeDirection 
    cmp  a,USBRqstTypeVendor | USBRqstTargetEndPoint | USBRqstTypeDirection 
    jz   USBEventEP0VendorRqst 
 
    ; Unsupported request !!! 
    jmp  USBEventEP0Stall               ; Oops! We don't support whatever 
                                        ;   request was made. 
 
;//$PAGE 
;******************************************************** 
; USBEventEP0SetupIsSet() 
; @func End point event SETUP to set address. 
; @devnote Runs in interrupt enabled context. 
;******************************************************** 
USBEventEP0SetupIsSetAddress: 
 
    ; Set device address? 
    mov  a,[USBRqstMessage] 
    cmp  a,USBRqstSetAddress 
    jz   USBEventEP0SetupSetAddress         ; Yes 
 
    ; Set device configuration? 
    mov  a,[USBRqstMessage] 
    cmp  a,USBRqstSetConfiguration 
    jz   USBEventEP0SetupSetConfig          ; Yes 
 
    ; Unsupported set request !!! 
    jmp  USBEventEP0Stall                   ; No. Stall 
 
;******************************************************** 
; USBEventEP0SetupSetAddress() 
; @func End point zero event SETUP to set address. 
; @devnote Runs in interrupt enabled context. 
; @comm 
; The status token of the SetAddress is an IN. So, we send status manually. 
;******************************************************** 
USBEventEP0SetupSetAddress: 
 
    ; Send ACK 
    call USBSendACK 
    ; Now that we have been acknowleged, we actually set the address. 
    ; This is different from all other commands which execute first 
    ;   and then acknowlege (_________________) 
 
    ; Set Address 
    mov  a,[USBRqstWordValueLo] 
    iowr USBDeviceAddress 
 
    ; Done 
    jmp  USBEventEP0End 
 
;******************************************************** 
; USBEventEP0SetupSetConfig() 
; @func End point zero event SETUP to Set Configuration. 
; @devnote Runs in interrupt enabled context. 
; 1 
;  set enumerated (gbSysEnumerated) state,  
;  enable GPIO (and EP1, if appropriate) 
;  Enable P0 and P1 
; 0 
;  Reset enumerated (gbSysEnumerated) state,  
;  Turn off LED 
;  Reset variables 
;  Disable GPIO and EP1 
;  Disable dallas chip and P0 and P1 
;******************************************************** 
USBEventEP0SetupSetConfig: 
 
    ; Enumerated ! 
    ; Write a 0 to the LED on P13 to turn it on 
    mov a,~(LED_ON) 
    iowr SysPort1 
 
    mov  a,10h 
    iowr USBEndP1TxConfig;NAK 
 
    ; enable all appropriate irq's 
    mov  a,SysIntTimer1024us | SysIntGPIO | SysIntUSBEndP0 | SysIntUSBEndP1 
    mov  [gbSysInterruptMask],a 
 
    ; Send ACK 
    call USBSendACK 
    jmp  USBEventEP0End 
 
;//$PAGE 
;******************************************************** 
; USBEventEP0SetupGetDescriptor() 
; @func End point zero event SETUP to Get Descriptor. 
; @devnote Runs in interrupt enabled context. 
;******************************************************** 
USBEventEP0SetupGetDescriptor: 
 
    ; Get descriptor type 
    mov  a,[USBRqstWordValueHi] 
 
  USBEventEP0SetupGetDescriptorDevice: 
    ; Device Descriptor? 
    cmp  a,USBDescriptorTypeDevice 
    jnz  USBEventEP0SetupGetDescriptorConfig    ; No 
 
    ;********************************************* 
    ; Get Device Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBDeviceDescription -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,12h                  ;[USBDeviceDescription] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorConfig: 
    ; Configuration Descriptor? 
    cmp  a,USBDescriptorTypeConfig 
    jnz  USBEventEP0SetupGetDescriptorString    ; No 
 
    ;********************************************* 
    ; Get Configuration Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBConfigurationDescription -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,09h                  ;[USBConfigurationDescription] 
    add  a,09h                  ;[USBInterfaceDescription] 
    add  a,07h                  ;[USBEndPointDescriptionInt] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorString: 
    ; Get String Descriptor? 
    cmp  a,USBDescriptorTypeString 
    jnz  USBEventEP0SetupGetDescriptorEnd       ; No 
 
    ;********************************************* 
    ; Get String Descriptor Event 
    ;********************************************* 
 
    ; Get string descriptor index 
    mov  a,[USBRqstWordValueLo] 
 
  USBEventEP0SetupGetDescriptorString0: 
    cmp  a,0h 
    jnz  USBEventEP0SetupGetDescriptorString1   ; No 
 
    ;********************************************* 
    ; Get String Language(s) Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBStringLanguageDescription -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,4h                   ;[USBStringLanguageDescription] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorString1: 
    cmp  a,1 
    jnz  USBEventEP0SetupGetDescriptorString2   ; No 
 
    ;********************************************* 
    ; Get String 1 Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBStringDescription1 -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,10h                  ;[USBStringDescription1] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorString2: 
    cmp  a,2 
    jnz  USBEventEP0SetupGetDescriptorString3   ; No 
 
    ;********************************************* 
    ; Get String 2 Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBStringDescription2 -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,10h                  ;[USBStringDescription2] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorString3: 
    cmp  a,3 
    jnz  USBEventEP0SetupGetDescriptorString4   ; No 
 
    ;********************************************* 
    ; Get String 3 Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBStringDescription3 -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,0Ah                  ;[USBStringDescription3] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorString4: 
    cmp  a,4 
    jnz  USBEventEP0SetupGetDescriptorString5   ; No 
 
    ;********************************************* 
    ; Get String 4 Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBStringDescription4 -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,28h                  ;[USBStringDescription4] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorString5: 
    cmp  a,5 
    jnz  USBEventEP0SetupGetDescriptorEnd       ; No 
 
    ;********************************************* 
    ; Get String 5 Descriptor Event 
    ;********************************************* 
    ; Descriptor pointer 
    mov  a,(USBStringDescription5 -USBSendROMBufferBase) 
    mov  [gbUSBSendBuffer],a 
 
    ; Descriptor size 
    mov  a,3Eh                  ;[USBStringDescription5] 
    mov  [gbUSBSendBytes],a 
 
    ; Check request size field 
    call USBSendDescriptorCheckLength 
 
    ; Send buffer 
    call USBSendROMBuffer 
    jmp  USBEventEP0End 
 
  USBEventEP0SetupGetDescriptorEnd: 
    ; Unsupported Get request !!! 
    jmp  USBEventEP0Stall 
 
;//$PAGE 
;******************************************************** 
; USBSendDescriptorCheckLength() 
; @func Check and update send length for Get Descriptor 
;       requests on end point 0. 
; @parm BYTE | gbUSBSendBytes | Number of bytes to send. 
;******************************************************** 
USBSendDescriptorCheckLength: 
 
    ; High byte set? (Assume <255 bytes) 
    mov  a,[USBEndP0FIFO_7] 
    cmp  a,0 
    jnz  USBSendDescriptorCheckLengthEnd    ; Yes 
 
    ; Check size 
    mov  a,[USBEndP0FIFO_6] 
    cmp  a,[gbUSBSendBytes] 
    jz   USBSendDescriptorCheckLengthEnd    ; equal 
    jnc  USBSendDescriptorCheckLengthEnd    ; greater than 
 
    ; New size 
    mov  [gbUSBSendBytes],a 
 
  USBSendDescriptorCheckLengthEnd: 
    ret 
 
;//$PAGE 
;******************************************************** 
; USBSendROMBuffer() 
; @func Send a number of ROM bytes on end point 0. 
; @parm BYTE | gbUSBSendBytes | Number of bytes to send. 
; @parm BYTE | gbUSBSendBuffer | Offset from ROM base 
;       of data to send. 
; @comm assumes IN packets are ignored in the interrupt routine 
; @devnote Enables interrupts 
;******************************************************** 
USBSendROMBuffer: 
 
    ; Clear flag 
    mov  a,0h 
    iowr USBEndP0RxStatus 
 
    ; Enable interrupts 
    mov  a,[gbSysInterruptMask] 
    and  a,~SysIntUSBEndP0 
    iowr SysInterrupt 
 
    ; Auto ACK OUT packet (This would be a Status Out) 
    mov  a,USBControlAckStatusData 
    iowr USBControl 
 
    ; Initialize sequence 
    mov  a,0h 
    mov  [gbUSBSendSequence],a 
 
    ; Send count 
    mov  a,[gbUSBSendBytes] 
 
USendROMBufferLoop: 
    ; One 8-byte chunk or less left? 
    cmp  a,08h 
    jz   USendROMBufferLoopDone     ; exactly 8 bytes left, branch 
    jc   USendROMBufferLoopDone     ; less than 8 bytes left, branch 
 
    ; more than 8 bytes left, fall through and loop  
    ;    until there are 8 bytes or less. 
    ; Save count 
    push a 
 
    ; Send 8 byte chunk 
    mov  a,08h 
    mov  [gbUSBSendBytes],a 
    call _USBSendROMBuffer 
 
    ; Check for OUT packet cancelling send 
    iord USBEndP0RxStatus 
    and  a,USBEndP0RxOut 
 
    ; Restore count 
    pop  a 
 
    ; Handle exception: OUT packet cancel send 
    jnz  USendROMBufferLoopExit     ; Cancelled 
 
    ; Save bytes left 
    sub  a,08h 
    mov  [gbUSBSendBytes],a 
    jmp  USendROMBufferLoop 
 
  USendROMBufferLoopDone: 
    ; Send last 8 or less bytes 
    call _USBSendROMBuffer 
 
USendROMBufferLoopExit: 
    ret 
 
;//$PAGE 
;******************************************************** 
; _USBSendROMBuffer() 
; @func Buffer and inialize USB send of up 
;               to 8 bytes of ROM data on end point 0. 
; @comm affects gbUSBSendBytes & gbUSBSendBuffer 
;******************************************************** 
_USBSendROMBuffer: 
 
    ; Save x 
    push x 
 
    ; Initialize 
    mov  x,0h 
 
  _USendROMBufferLoop: 
    ; Any more? 
    mov  a,0h 
    cmp  a,[gbUSBSendBytes] 
    jz  _USendROMBufferLoopDone         ; No more 
    dec  [gbUSBSendBytes] 
 
    ; Move bytes to FIFO 
    mov  a,[gbUSBSendBuffer] 
    index USBSendROMBufferBase 
    mov  [x +USBEndP0FIFO],a 
    inc  x 
 
    ; Next byte 
    inc  [gbUSBSendBuffer] 
    jmp _USendROMBufferLoop 
 
  _USendROMBufferLoopDone: 
    ; Re-enable reception 
    mov  a,0h 
    iowr USBEndP0RxStatus 
 
    ; Toggle sequence 
    mov  a,USBEndP0TxSequence 
    xor  [gbUSBSendSequence],a 
 
    ; Send bytes 
    push x 
    pop  a 
    or   a,[gbUSBSendSequence] 
    or   a,USBEndP0TxRespond 
    iowr USBEndP0TxConfig 
 
    ; The FIFO is loaded, go and wait untill it's read 
    call USBSendWaitForComplete 
 
  _USendROMBufferEnd: 
    ; Restore and exit 
    pop  x 
    ret 
 
;//$PAGE 
;******************************************************** 
; USBSendACK() 
; func Respond to a "USB Status In" with a zero byte buffer with  
;   Sequence field set) on end point 0. 
; Called by SetAddress and SetConfig commands 
;******************************************************** 
USBSendACK: 
 
    ; Status response to Status In is to send a zero byte packet 
    mov  a,USBEndP0TxRespond | USBEndP0TxSequence 
    iowr USBEndP0TxConfig 
 
    ; Enable interrupts 
    mov  a,[gbSysInterruptMask] 
    iowr SysInterrupt 
 
    ; Wait for send complete 
    jmp  USBSendWaitForComplete 
 
;******************************************************** 
; USBSendWaitForComplete() 
; @func Wait for send to complete on end point 0. 
;******************************************************** 
 
; At some point, either the 0 data will be ACK'd or a SETUP 
;    will come in.  
; Either event will cause the "Enable Respond 
;    to In Packets" to be reset, and we will fall out of the loop. 
; In either case, an EP0 IRQ will be generated (5.9.2.2 in Cyp 
;    device spec) if EP0 irq is enabled. 
 
USBSendWaitForComplete: 
 
    ; Poll the send complete bit 
    ; This will be reset when the data has been sent to the host 
    ;   and the host has ACK's, or the host has sent another SETUP 
    ;   which should terminate this activity in any case. 
    iord USBEndP0TxConfig 
    and  a,USBEndP0TxRespond 
    jz   USBSendWaitComplete 
 
    ; Check for OUT packet cancelling send. A STATUS OUT should 
    ;   terminate any pending IN's. A Setup could also set the Out bit. 
    iord USBEndP0RxStatus 
    and  a,USBEndP0RxOut 
    jnz  USBSendWaitComplete    ; Cancelled 
 
    ; Keep waiting 
    jmp  USBSendWaitForComplete 
 
  USBSendWaitComplete: 
    ret 
 
;******************************************************** 
; USBEventEP0VendorRqst() 
; @func Vendor request on end point zero. 
; @devnote Runs in interrupt disabled context. 
;******************************************************** 
USBEventEP0VendorRqst: 
 
    ; Save it 
    push x 
 
    ; Check Protocol 
    mov  a,[USBEndP0FIFO_1] 
    cmp  a,0h 
    jnz  USBEventEP0Stall                   ; No 
 
    ;********************************************* 
    ; Reset Counter Event 
    ;********************************************* 
    ; Initialize Counter 
    mov  a,0 
    mov  [gbButtonClicks],a     ; Initial state of 0, no button pushed 
    mov  [USBEndP1FIFO_0],a 
 
    iord USBEndP1TxConfig 
    and  a,40h 
    or   a,91h 
    iowr USBEndP1TxConfig 
 
    ;USBEventEP0VendorRqstFinish: 
 
    ; Protocol ACK 
    mov  a,42h 
    mov  [USBEndP0FIFO_0],a 
 
    ; Auto ACK OUT packet 
    mov  a,USBControlAckStatusData 
    iowr USBControl 
 
    ; Send bytes as Data1 
    mov  a,8 
    or   a,USBEndP0TxSequence 
    or   a,USBEndP0TxRespond 
    iowr USBEndP0TxConfig 
 
    ;call USBSendWaitForComplete 
 
    ; Restore it 
    pop  x 
 
    ; Return 
    jmp  USBEventEP0End 
 
;******************************************************** 
; Data Segment (ROM) 
;******************************************************** 
USBSendROMBufferBase: 
USBDeviceDescription: 
    db 12h          ; Length 
    db 01h          ; Type (1=device) 
    db 10h,01h      ; Complies to USB Spec. v1.10 
    db 00h          ; Class code (0=??) 
    db 00h          ; SubClass code (0=??) 
    db 00h          ; Protocol (0=none)(9.6.1) 
    db 08h          ; Max. packet size for port0 
    db 34h,12h      ; Vendor ID: (0x1234=WahBook) 
    db 78h,56h      ; Product ID (0x01=USB Counter) 
    db 00h,01h      ; Device release v1.00 
    db 01h          ; Manufacturer string descriptor index (0=none) 
    db 02h          ; Product string descriptor index (0=none) 
    db 03h          ; Serial number string descriptor index (0=none) 
    db 01h          ; Number of possible configurations 
USBDeviceDescriptionEnd: 
 
;************************************************* 
; 
USBConfigurationDescription: 
    db 09h          ; Length 
    db 02h          ; Type (2=config) 
    db 19h,00h      ; Total data length (1 config,1 interface,1 endpoints) 
    db 01h          ; Interface supported (1=???) 
    db 01h          ; Configuration value (1=???) 
    db 04h          ; Confituration string descriptor index (0=none) 
    db 80h          ; Configuration (bus powered) 
    db 32h          ; Maximum power consumption in 2mA units 
USBConfigurationDescriptionEnd: 
 
;************************************************* 
; 
USBInterfaceDescription: 
    db 09h          ; Length 
    db 04h          ; Type (4=interface) 
    db 00h          ; Number of interfaces (0 based) 
    db 00h          ; Alternate settings 
    db 01h          ; Number of endpoints (1 based) (9.6.3) 
    db 00h          ; Class code (0=non-specified,1=kb,2=mouse,3=joystick ???) 
    db 00h          ; Subclass code (0=???) 
    db 00h          ; Protocol code (0=non-specified) 
    db 05h          ; Interface string index (0=non-specified, 1,2,3,...) 
USBInterfaceDescriptionEnd: 
 
;************************************************* 
; Never used for EP0 
USBEndPointDescriptionInt: 
    db 07h          ; Length 
    db 05h          ; Type (5=endpoint) 
    db 81h          ; Address (EP#=1 | [0x80=IN, 0=OUT]) 
    db 03h          ; Attribute (0=control,1=isochronous,2=bulk,3=interrupt) 
    db 08h,00h      ; Max packet size 
    db B8h          ; Interval (200 ms) 
USBEndPointDescriptionIntEnd: 
 
;************************************************* 
; 
USBStringLanguageDescription: 
    db 04h          ; Length 
    db 03h          ; Type (3=string) 
    db 09h          ; Language:  English 
    db 01h          ; Sub-language: US 
 
USBStringDescription1: 
    db 10h          ; Length 
    db 03h          ; Type (3=string) 
    dsu "WAHBOOK" 
 
USBStringDescription2: 
    db 10h          ; Length 
    db 03h          ; Type (3=string) 
    dsu "Counter" 
 
USBStringDescription3: 
    db 0Ah          ; Length 
    db 03h          ; Type (3=string) 
    dsu "0001" 
 
USBStringDescription4: 
    db 28h          ; Length 
    db 03h          ; Type (3=string) 
    dsu "Count Button Clicks" 
 
USBStringDescription5: 
    db 3Eh          ; Length 
    db 03h          ; Type (3=string) 
    dsu "EndPoint1 200ms Interrupt Pipe"