📄 usbmain.asm
字号:
; ReInitialize:
; 1. clears watchdog timer
; 2. enables the 1ms, GPIO and USB reset interrupts
; 3. enables device address 0
; 4. enables EP0 to respond to SETUP packets so that the keyboard
; can enumerate
; 5. enable EP0 interrupts
; 6. disables EP1
; 7. calls the Init routine to initialze RAM and port registers
;
ReInitialize:
di ;disable interrupts
iowr Watchdog ; clear watchdog timer
iord Status_Control ; clear the Bus Reset bit in the
and A, ~(USBReset + WatchDogReset + PowerOnReset) ; Status & Control register
iowr Status_Control
mov A, GPIO_TIMER_RESET_MASK; enable 1ms and bus reset timer ints
iowr Global_Interrupt
mov A, 0 ; disable endpoint zero interrupt
iowr Endpoint_Interrupt ; disable endpoint one interrupt
iord EP_A0_Mode ; unlock Mode register
mov A, STALL ; enable endpoint zero setup
iowr EP_A0_Mode
mov A, 0 ; disable endpoint zero response
iowr USB_Device_Address
mov A, DISABLED ; disable endpoint one
iowr EP_A1_Mode
mov A, DISABLED ; disable endpoint two
iowr EP_A2_Mode
iowr Watchdog ; clear Watchdog timer
mov A, NORMAL ; initialize the ports
iowr GPIO_Config ; write
call ksc_init_keyscan
mov A, 0
iowr Port0_Interrupt ; disable port 0 interrupts
iowr Port1_Interrupt ; disable port 1 interrupts
iowr Port2_Interrupt ; disable port 2 interrupts
iowr Port3_Interrupt ; disable port 3 interrupts
; initialize variables
CLEARRAM usbmain_ram_base,USBMAIN_RAM_SIZE
CLEARRAM e8h,16
mov A, UNCONFIGURED
mov [configuration_status], A ; start out unconfigured
mov A, REPORT_PROTOCOL
mov [protocol_status], A ; default to report protocol
call usb_init_keyscan
iowr Watchdog ; clear Watchdog timer again
call mouse_init_module
ret
;========================================================================
; RESET
; We execute the Reset routine whenever a Power-On Reset (POR) or a
; WatchDog reset (WDR) occurs.
;========================================================================
; reset processing
Reset:
iowr Watchdog ; clear Watchdog timer
mov A, datastack_start ; initialize the data stack pointer
swap A, dsp ; to a position so it does not write over USB fifos
call ReInitialize ; initialize system variables
ei
jmp main
USB_Bus_Reset_ISR:
iowr Watchdog
mov A, datastack_start ; initialize the data stack pointer
swap A, dsp ; to a position so it does not write over USB fifos
mov a,0 ;set psp stack to 0
mov psp,a ;
call ReInitialize ; initialize system variables
mov A, ADDRESS_ENABLE_BIT ; enable endpoint zero response
iowr USB_Device_Address
mov A, ENUMERATE ; enable endpoint zero interrupt
iowr Endpoint_Interrupt ; disable endpoint one interrupt
mov A,(CAPS_LOCK_LED + NUM_LOCK_LED + SCROLL_LOCK_LED)
call ksc_writeLED
ei
;========================================================================
; MAIN LOOP
;========================================================================
main:
iowr Watchdog ; clear Watchdog timer
mov A,[EP0_FLAG]
cmp A,0
jz .config ;
mov A,0
mov [EP0_FLAG],A
call EP0
.config:
call MouseConnectTask
mov A, [configuration_status]
cmp A, UNCONFIGURED
jz main ; if we're configured.....
.test_suspend:
mov A,[background_flags]
and A,SUSPEND_FLAG ; if we need to suspend
jz .scan
call Suspend ; do so
mov A,[background_flags] ; then clear the suspend flag
and A,~SUSPEND_FLAG
mov [background_flags],A
.scan:
mov A,[background_flags] ;if we need to scan the kbd
and A,SCAN_FLAG
jz other_tasks
call usbkey_scan_keys ; do so
call Send_Keyboard_Report ; send a keyboard report
call PowerTask ; check for power report
call MouseTask ; check for mouse data
call ConsumerTask ; and consumer report
.next:
mov A,[background_flags] ;clear the scan flag
and A,~SCAN_FLAG
mov [background_flags],A
; other tasks can be inserted here
other_tasks:
call SendPowerReport ;send reports on EP2
call SendConsumerReport
jmp main ; no tasks defined
;========================================================================
; PowerTask maintains the idle count for the power packets. If the idle
; count expires, usb_power_flag is set to indicate the need for sending
; a packet
;========================================================================
PowerTask:
.idle_period_check:
mov A, [power_idle_period_ctr] ; if idle_period is zero, then we only send
cmp A, 0 ; reports on change
jz .exit ; exit task
dec A
mov [power_idle_period_ctr],A ; else decrement idle period
jnz .exit ; exit if idle period not expired
.xmit:
mov A,[power_idle_period] ; reset idle period
mov [power_idle_period_ctr],A
mov A,1
mov [usb_power_flag],A ;and set flag to send a report
.exit:
ret
;========================================================================
; ConsumerTask maintains the idle count for the consumer packets. If the idle
; count expires, usb_consumer_flag is set to indicate the need for sending
; a packet
;========================================================================
ConsumerTask:
.idle1_period_check:
mov A, [consumer_idle_period_ctr] ; if idle_period is zero, then we only send
cmp A, 0 ; reports on change
jz .exit ; exit task
dec A
mov [consumer_idle_period_ctr],A ; else decrement idle period
jnz .exit ; exit if idle period not expired
.xmit:
mov A,[consumer_idle_period] ; reset idle period
mov [consumer_idle_period_ctr],A
mov A,1
mov [usb_consumer_flag],A ;and set flag to send a report
.exit:
ret
;
;========================================================================
; SendPowerReport sends a power key report out on endpoint 2
;========================================================================
SendPowerReport:
mov A,[protocol_status]
cmp A,BOOT_PROTOCOL
jz .exit ;don't report power keys in boot mode
mov A,[usb_current_state] ;don't report power keys if error situation
cmp A,USB_REPORT_ERROR
jz .exit
mov A,[usb_power_flag] ; if power, sleep, or wake keys
cmp A,00 ; have changed,
jz .exit ; transmit them
iord EP_A2_Mode ; are we currently transmitting a report?
and A, USB_MODE_MASK
cmp A, ACKIN
jz .exit ; yes, then exit (so we don't mess up
;current transaction)
mov A,[usb_power_id] ; set report ID
mov [endpoint_2],A
mov A,[usb_power_keys] ; get bit field
mov [endpoint_2+1],A
iord EP_A2_Counter
and A, DATATOGGLE ; keep data toggle setting
or A, POWER_REPORT_LEN ; packet size
iowr EP_A2_Counter
mov A, ACKIN ; enable packet transmission
iowr EP_A2_Mode
mov A,0
mov [usb_power_flag],A
.exit:
ret
;========================================================================
; SendConsumerReport sends a consumer key report out on endpoint 2
;========================================================================
SendConsumerReport:
mov A,[protocol_status]
cmp A,BOOT_PROTOCOL
jz .exit ;don't report power keys in boot mode
mov A,[usb_current_state] ;don't report power keys if error situation
cmp A,USB_REPORT_ERROR
jz .exit
mov A,[usb_consumer_flag] ; if power, sleep, or wake keys
cmp A,00 ; have changed,
jz .exit ; transmit them
iord EP_A2_Mode ; are we currently transmitting a report?
and A, USB_MODE_MASK
cmp A, ACKIN
jz .exit ; yes, then exit (so we don't mess up
;current transaction)
mov A,[usb_consumer_id] ; set report ID
mov [endpoint_2],A
mov A,[usb_consumer_keys] ; get bit field
mov [endpoint_2+1],A
iord EP_A2_Counter
and A, DATATOGGLE ; keep data toggle setting
or A, CONSUMER_REPORT_LEN ; packet size
iowr EP_A2_Counter
mov A, ACKIN ; enable packet transmission
iowr EP_A2_Mode
mov A,0
mov [usb_consumer_flag],A
.exit:
ret
;========================================================================
; Send keyboard report to host
;========================================================================
Send_Keyboard_Report:
mov A,[usb_tx_required]
cmp A,0 ; if nothing to report
jz .idle ; check idle status
cmp A,USB_REPORT_ERROR ; else if we've got to send an error report
jnz .send
cmp A,[last_key_report] ; and the last one sent was not an error
jnz .send ; we can send this one too
.idle: ; else check idle status
mov A, [kbd_idle_period_ctr] ; if idle_period is zero, then we only send
cmp A, 0 ; reports on change
jz .exit ; exit task
dec A
mov [kbd_idle_period_ctr],A ; decrement idle period
jnz .exit ; and exit if idle period not expired
mov A,[last_key_report] ; otherwize get type of last report
.send: ;ok, we're REALLY going to send a new report
push A
iord EP_A1_Mode ; are we currently transmitting a report?
and A, USB_MODE_MASK
cmp A, ACKIN
pop A ; pop current request into A
jz .out ; yes, then exit (so we don't mess up
; the current transmission)
;
mov [last_key_report],A
mov A,0
mov [usb_tx_required],A
call Copy_report_buffer_to_EP1 ; copy report buffer to EP1 FIFO
call Enable_EP1_Transmission ; enable EP1 transmission
.out:
mov A,[kbd_idle_period]
mov [kbd_idle_period_ctr],A ;reset idle period
.exit:
ret
;========================================================================
; Copy report_buffer contents to EP1 FIFO
;========================================================================
Copy_report_buffer_to_EP1:
push A ; save registers on stack
push X
mov X, 7 ;count 8 of the darn things
copy_report_buffer_loop:
mov A,[last_key_report] ;if we're sending an error report
cmp A,USB_REPORT_ERROR
mov A,1 ;stuff a '1' into the buffer
jz .next
mov A, [X + usb_report_buffer] ;else fetch a byte from the key buffer
.next:
mov [X + endpoint_1], A ;put it into ep0 fifo
dec X
jnc copy_report_buffer_loop ;do next one
pop X ; restore registers from stack
pop A
ret
;========================================================================
; Enable transmission from EP1 FIFO
;========================================================================
Enable_EP1_Transmission:
push A ; save accumulator on stack
iord EP_A1_Counter
and A, DATATOGGLE ; keep data toggle setting
or A, 8h ; packet size
iowr EP_A1_Counter
mov A, ACKIN ; enable packet transmission
iowr EP_A1_Mode
pop A ; restore accumulator
ret ; return
;========================================================================
;USB_EP0_ISR
;
;this routine is entered due to an interrupt on EP0. In the event that a
;setup packet causes the interrupt, the SIE automatically switches to NAKing ins and outs.
;
;the other possible sources of this interrupt are:
;
;control read data phase: the SIE switches from ACKing INS and status OUTs, to
;NAKing INs, ACKing STATUS OUTs. It is possible that in this case, the last
;data phase in a control read transaction could be followed very quickly by the
;STATUS OUT phase, or the start of another SETUP if the STATUS OUT phase is
;omitted. If either or both of these occurs while
;the firmware is getting around to processing the last data phase,
;the firmware will recover cleanly.
;The out will be acked, and/or the ensuing setup will generate another interrupt
;which will cause the SIE to NAK ins and outs.
;
;
;control read status out phase: The SIE does not switch modes. The STATUS OUT
;will be ACKed by the SIE. This interrupt will flag this occurrence to the main loop.
;If the next SETUP occurs before the main loop sees the STATUS OUT, it will simply
;switch to NAKing INs and OUTs and allow the main loop to catch up.
;
;
;control write data phase: the SIE switches from ACKing OUTs and STATUS INs,
;to NAKing OUTS, ACKing STATUS INs. As the only data out transaction to worry
; about in the keyboard code is the LED byte (1st byte in the fifo), this byte
;is copied within this ISR to a shadow location, to prevent its being trampled
;by another SETUP before the byte is processed by the main loop code.
;note that although it is possible that this status out is very quickly followed
;by the STATUS IN, this will still be ACKed by the SIE for us. In addition,
;if the STATUS IN is omitted and a rapidly succeeding SETUP occurs, the SIE will
;still ACK it, switch to NAKing INs and OUTs, and allow the main loop to catch up.
;
;control write status in phase: The SIE does not switch modes. The STATUS IN
;will be ACKed by the SIE. This interrupt will flag this occurrence to the main loop.
;If the next SETUP occurs before the main loop sees the STATUS IN, it will simply
;switch to NAKing INs and OUTs and allow the main loop to catch up.
;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -