Files
ku1255cfw/main.s
T
2022-01-11 17:38:55 +01:00

1154 lines
23 KiB
ArmAsm

CHIP SN8F2288
//{{SONIX_CODE_OPTION
; Options for Lenovo Compact Keyboard
.Code_Option Fcpu "Fosc/4"
.Code_Option Fslow "Flosc/2"
.Code_Option High_CLK "12M_X'tal"
.Code_Option LVD "LVD_M"
.Code_Option Reset_Pin "P07"
.Code_Option Rst_Length "No"
.Code_Option Security "Enable"
.Code_Option Watch_Dog "Enable"
//}}SONIX_CODE_OPTION
.DATA
_canary_check EQU 0x2880
_flasher EQU 0x2890
UTX EQU P0.6 ; S15
URX EQU P0.5 ; S10
S0 EQU P2.2
S0M EQU P2M.2
S1 EQU P2.0
S1M EQU P2M.0
S2 EQU P4.0
S2M EQU P4M.0
S3 EQU P4.5
S3M EQU P4M.5
S4 EQU P4.1
S4M EQU P4M.1
S5 EQU P2.1
S5M EQU P2M.1
S6 EQU P4.4
S6M EQU P4M.4
S7 EQU P4.2
S7M EQU P4M.2
S8 EQU P4.3
S8M EQU P4M.3
S9 EQU P2.3
S9M EQU P2M.3
S10 EQU P0.5
S10M EQU P0M.5
S11 EQU P0.4
S11M EQU P0M.4
S12 EQU P4.6
S12M EQU P4M.6
S13 EQU P4.7
S13M EQU P4M.7
S14 EQU P0.3
S14M EQU P0M.3
S15 EQU P0.6
S15M EQU P0M.6
R0 EQU P1.0
R0M EQU P1M.0
R1 EQU P1.7
R1M EQU P1M.7
R2 EQU P1.2
R2M EQU P1M.2
R3 EQU P1.1
R3M EQU P1M.1
R4 EQU P1.4
R4M EQU P1M.4
R5 EQU P1.3
R5M EQU P1M.3
R6 EQU P1.5
R6M EQU P1M.5
R7 EQU P1.6
R7M EQU P1M.6
EP0_BYTES EQU 8
EP1_BYTES EQU 8
EP2_BYTES EQU 8
bmRequestType DS 1
bRequest DS 1
wValueLo DS 1
wValueHi DS 1
wIndexLo DS 1
wIndexHi DS 1
wLengthLo DS 1
wLengthHi DS 1
txPtrLo DS 1
txPtrHi DS 1
txSizeLo DS 1
txSizeHi DS 1
txPktLen DS 1
txTmpCnt DS 1
usbState DS 1
usbStateSetupInvalid EQU usbState.0
usbStateSetAddress EQU usbState.1
usbStateIn0Done EQU usbState.3
usbStateZeroPad EQU usbState.4
usbStateStringCnt EQU usbState.5
usbStateCopyRAM EQU usbState.6
usbStateHTDData EQU usbState.7
usbState2 DS 1
usbStateAddressValid EQU usbState2.0
usbStateEnterFlasher EQU usbState2.1
xlatVal1 DS 1
xlatVal2 DS 1
kbdRow DS 1
kbdRowsLow DS 1
kbdSense1 DS 1
kbdSense2 DS 1
.CODE
ORG 0x0 ; Reset vector
; Jump to bootloader, checks canary and continues execution at 0x10 if found
JMP _canary_check
ORG 0x8 ; Interrupt vector
JMP _flasher
ORG 0x10 ; Bootloader jumps here on successful canary check, start of payload execution
_start:
; Set stack pointer and disable interrupts
MOV A, #7
B0MOV STKP, A
MOV A, #0
B0MOV RBANK, A
; Jump into bootloader if watchdog triggered or undefined reset source
B0BTS1 FNT0
JMP _flasher ; NT0 == 0 => watchdog reset or undefined reason
; Light up the power LED (P5.3/PWM0)
B0BCLR P5.3 ; Set to low level to light up
B0BSET P5M.3 ; Set to output
; DS indicates watchdog may start running before CPU
; Tickle it once so we have a well-defined time left
MOV A, #0x5a
B0MOV WDTR, A
; Set up P0.6/UTX (UART TX)
; FIXME: PnUR is write-only, B0BSET/B0BCLR are likely broken
B0BSET P0UR.6 ; Enable pull-up (UART idle is high)
B0BCLR P0M.6 ; Set to input (TXEN will override to output)
MOV A, #0x60 ; Baud 115200
B0MOV URBRC, A
MOV A, #0x90 ; 24MHz clock, TX enabled, 8n1, 1-byte mode
B0MOV URTX, A
; Send a message that we are alive
MOV A, #'H'
CALL _uart_tx
MOV A, #'i'
CALL _uart_tx
MOV A, #'!'
CALL _uart_tx
MOV A, #13
CALL _uart_tx
MOV A, #10
CALL _uart_tx
; Jump into bootloader if "Return" key (S10/R6) is held
B0BSET P0M.5 ; Set S10 to output
B0BCLR P1M.5 ; Set R6 to input
B0BSET P1UR.5 ; Enable pull-up on R6
B0BSET S10 ; Set S10 high
CALL _delayshort
B0BTS1 R6 ; Jump if R6 is low
JMP @F
B0BCLR S10 ; Set S10 low
CALL _delayshort
B0BTS1 R6 ; Jump if R6 is low
JMP _return_held
@@:
; Jump into bootloader if P0.5/P0.6 are shorted (URX/UTX)
B0BCLR 0xa9.4 ; Switch UTX back to GPIO
B0BSET P0M.6 ; Set UTX to output
B0BCLR P0M.5 ; Set URX to input
B0BSET P0UR.5 ; Enable pull-up on URX
B0BSET UTX ; Set P0.6/UTX high
CALL _delayshort
B0BTS1 URX ; Jump if P0.5/URX is low
JMP @F
B0BCLR UTX ; Set P0.6/UTX low
CALL _delayshort
B0BTS1 URX ; Jump if P0.5/URX is low
JMP _uart_shorted
@@:
B0BSET 0xa9.4 ; Switch UTX back to UART
; Reset either from undervoltage (power-on) or external reset
; Cold reset state per datasheet:
; - Clock is 12MHz PLL synced to external oscillator
; - IOs set to input
; Setup GPIO
CALL _gpio_init
; Setup USB registers
CALL _usb_init
_mainloop:
; Tickle watchdog
MOV A, #0x5a
B0MOV WDTR, A
@@:
B0BTS0 FEP0SETUP ; Jump if SETUP packet rx'd
JMP _usb_setup
B0BTS0 FEP0OUT
JMP _usb_ep0_out
B0BTS0 FEP0IN
JMP _usb_ep0_in
B0BTS0 FBUS_RST
JMP _usb_reset
B0BTS0 FSOF
JMP _usb_sof
JMP @B
_usb_sof:
B0BCLR FSOF
; Check for cross-talk from too many depressed keys
CALL _kbd_count_rows_low
; Read the columns
CALL _kbd_sense
; Release the scanned row
CALL _kbd_set_row_input
; Increment row
INCMS kbdRow
MOV A, #0x07
AND kbdRow, A
; Lower the next row
CALL _kbd_set_row_output
B0MOV A, kbdSense1
OR A, kbdSense2
B0BTS0 FZ ; Jump if kbdSense is zero
JMP _mainloop
MOV A, #'K'
CALL _uart_tx
B0MOV A, kbdRow
CALL _uart_hex
B0MOV A, kbdSense1
CALL _uart_hex
B0MOV A, kbdSense2
CALL _uart_hex
JMP _mainloop
_usb_reset:
MOV A, #13
CALL _uart_tx
MOV A, #10
CALL _uart_tx
MOV A, #'R'
CALL _uart_tx
CALL _usb_init
; Wait until reset is de-asserted
_usb_reset_wait:
B0BTS0 FBUS_RST
JMP _usb_reset_wait
JMP _mainloop
_gpio_init:
; Enable all pull-ups
MOV A, #0xff
B0MOV P0UR, A
B0MOV P1UR, A
B0MOV P2UR, A
B0MOV P4UR, A
B0MOV P5UR, A
; Switch all to input
MOV A, #0x00
B0MOV P0M, A
B0MOV P1M, A
B0MOV P2M, A
B0MOV P4M, A
B0MOV P5M, A
; Set port output data to 0
B0MOV P0, A
B0MOV P1, A
B0MOV P2, A
B0MOV P4, A
B0MOV P5, A
; Reset kbd scan state
B0MOV kbdRow, A
; Re-light LED
B0BCLR P5.3 ; Set to low level to light up
B0BSET P5M.3 ; Set to output
;; Set UTX/URX to open-drain
;MOV A, #0x0c
;B0MOV P1OC, A
RET
_usb_init:
MOV A, #0
B0MOV usbState, A
B0MOV usbState2, A
B0MOV USTATUS, A
MOV A, #0x80
B0MOV UDA, A ; Set address to 0 and enable
B0MOV UE1R, A ; Enable EP1, set to NAK
B0MOV UE2R, A ; Enable EP2, set to NAK
B0BSET FDP_PU_EN ; Enable D+ pull-up
B0BSET FSOF_INT_EN ; Enable SOF interrupt request
RET
_kbd_sense:
MOV A, #0
B0MOV kbdSense1, A
B0MOV kbdSense2, A
B0BTS1 S0
B0BSET kbdSense1.0
B0BTS1 S1
B0BSET kbdSense1.1
B0BTS1 S2
B0BSET kbdSense1.2
B0BTS1 S3
B0BSET kbdSense1.3
B0BTS1 S4
B0BSET kbdSense1.4
B0BTS1 S5
B0BSET kbdSense1.5
B0BTS1 S6
B0BSET kbdSense1.6
B0BTS1 S7
B0BSET kbdSense1.7
B0BTS1 S8
B0BSET kbdSense2.0
B0BTS1 S9
B0BSET kbdSense2.1
B0BTS1 S10
B0BSET kbdSense2.2
B0BTS1 S11
B0BSET kbdSense2.3
B0BTS1 S12
B0BSET kbdSense2.4
B0BTS1 S13
B0BSET kbdSense2.5
B0BTS1 S14
B0BSET kbdSense2.6
B0BTS1 S15
B0BSET kbdSense2.7
RET
_kbd_set_row_output:
B0MOV A, kbdRow
AND A, #0x7
MOV R, A
ADD A, R
B0ADD PCL, A
B0BSET R0M
RET
B0BSET R1M
RET
B0BSET R2M
RET
B0BSET R3M
RET
B0BSET R4M
RET
B0BSET R5M
RET
B0BSET R6M
RET
B0BSET R7M
RET
_kbd_set_row_input:
B0MOV A, kbdRow
AND A, #0x7
MOV R, A
ADD A, R
B0ADD PCL, A
B0BCLR R0M
RET
B0BCLR R1M
RET
B0BCLR R2M
RET
B0BCLR R3M
RET
B0BCLR R4M
RET
B0BCLR R5M
RET
B0BCLR R6M
RET
B0BCLR R7M
RET
_kbd_count_rows_low:
MOV A, #0
B0MOV kbdRowsLow, A
B0BTS1 R0
INCMS kbdRowsLow
B0BTS1 R1
INCMS kbdRowsLow
B0BTS1 R2
INCMS kbdRowsLow
B0BTS1 R3
INCMS kbdRowsLow
B0BTS1 R4
INCMS kbdRowsLow
B0BTS1 R5
INCMS kbdRowsLow
B0BTS1 R6
INCMS kbdRowsLow
B0BTS1 R7
INCMS kbdRowsLow
NOP
RET
_usb_ep0_in:
B0BCLR FEP0IN ; Early ack of IN irq, we can't get a new one until EP0 NAK state is cleared
MOV A, #'i'
CALL _uart_tx
CALL _usb_write_ep0
B0BTS0 usbStateSetAddress
JMP _usb_ep0_set_addr
JMP _mainloop
_usb_ep0_set_addr:
B0BSET wValueLo.7
B0MOV A, wValueLo
B0MOV UDA, A ; Update address register
MOV A, #'A'
CALL _uart_tx
B0MOV A, wValueLo
CALL _uart_hex
JMP _mainloop
_usb_ep0_out:
B0BCLR FEP0OUT ; Ack OUT irq
MOV A, #'o'
CALL _uart_tx
B0MOV A, EP0OUT_CNT
B0BTS1 FZ ; Call if non-zero
CALL _uart_hex
B0BTS0 usbStateHTDData
JMP _usb_setup_htd_got_data
JMP _mainloop
_setup_dispatch_table:
DW 0x0005 ; SET_ADDRESS
JMP _usb_htd_set_address
DW 0x0009 ; SET_CONFIGURATION
JMP _usb_htd_set_configuration
DW 0x2109 ; HID SET_REPORT
JMP _usb_htd_hid_set_report
DW 0x210a ; HID SET_IDLE
JMP _usb_htd_hid_set_idle
DW 0x210b ; HID SET_PROTOCOL
JMP _usb_setup_default
DW 0x8000 ; GET_STATUS
JMP _usb_dth_get_status
DW 0x8006 ; GET_DESCRIPTOR (device)
JMP _usb_dth_get_device_descriptor
DW 0x8106 ; GET_DESCRIPTOR (interface)
JMP _usb_dth_get_interface_descriptor
DW 0xa101 ; HID GET_REPORT
JMP _usb_dth_hid_get_report
DW 0xa102 ; HID GET_IDLE
JMP _usb_setup_default
DW 0xa103 ; HID GET_PROTOCOL
JMP _usb_setup_default
DW 0xFFFF
JMP _usb_setup_default
_usb_setup_default:
B0BSET usbStateSetupInvalid
MOV A, #'?'
CALL _uart_tx
B0MOV A, bmRequestType
CALL _uart_hex
B0MOV A, bRequest
CALL _uart_hex
RET
_usb_dth_get_device_descriptor:
B0MOV A, wValueHi
ADD A, #0xfc
B0BTS0 FC
JMP _usb_get_desc_badidx
B0MOV A, wValueHi
B0ADD PCL, A
JMP _usb_get_desc_badidx
JMP _usb_get_desc_device
JMP _usb_get_desc_config
JMP _usb_get_desc_string
_usb_dth_get_interface_descriptor:
B0MOV A, wValueHi
SUB A, #0x20
ADD A, #0xfc
B0BTS0 FC
JMP _usb_get_desc_badidx
B0MOV A, wValueHi
SUB A, #0x20
B0ADD PCL, A
JMP _usb_get_desc_badidx
JMP _usb_get_desc_hid
JMP _usb_get_desc_report
JMP _usb_get_desc_physical
_usb_get_desc_badidx:
MOV A, #'d'
CALL _uart_tx
B0MOV A, wValueHi
CALL _uart_hex
B0MOV A, wValueLo
CALL _uart_hex
B0BSET usbStateSetupInvalid
RET
_device_descriptor:
DB 0x12 ; bLength
DB 1 ; bDescriptorType (DEVICE)
DB 0x00, 0x02 ; bcdUSB (2.00)
DB 0, 0, 0 ; Class/Subclass/Protocol
DB EP0_BYTES ; EP0 max packet size
DB 0xef, 0x17 ; Vendor 0x17ef (Lenovo)
DB 0x47, 0x60 ; Device 0x6047 (Lenovo ThinkPad Compact Keyboard with TrackPoint)
DB 0x00, 0x01 ; bcdDevice (1.0)
DB 1 ; iManufacturer (String 1)
DB 2 ; iProduct (String 2)
DB 0 ; iSerial (n/a)
DB 1 ; bNumConfigurations (1)
_device_descriptor_end:
_configuration_descriptor:
DB 9 ; bLength
DB 2 ; bDescriptorType (CONFIGURATION)
DB 0x32, 0x00 ; wTotalLength
DB 2 ; bNumInterfaces
DB 1 ; bConfigurationValue
DB 0 ; iConfiguration (n/a)
DB 0xa0 ; bmAttributes (BUS_POWERED, REMOTE_WAKEUP)
DB 50 ; bMaxPower (100mA)
; Keyboard interface
DB 9 ; bLength
DB 4 ; bDescriptorType (INTERFACE)
DB 0 ; bInterfaceNumber
DB 0 ; bAlternateSetting
DB 1 ; bNumEndpoints
DB 3 ; bInterfaceClass (HID)
DB 1 ; bInterfaceSubClass (boot interface)
DB 1 ; bInterfaceProtocol (keyboard)
DB 0 ; iInterface (n/a)
DB 9 ; bLength
DB 0x21 ; bDescriptorType (HID)
DB 0x00, 0x01 ; bcdHID (1.00)
DB 0 ; bCountryCode (Not Supported)
DB 1 ; bNumDescriptors
DB 0x22 ; bDescriptorType (Report)
DB 63, 0 ; wDescriptorLength (63)
DB 7 ; bLength
DB 5 ; bDescriptorType (ENDPOINT)
DB 0x81 ; bEndpointAddress (EP1 IN)
DB 0x03 ; bmAttributes (Interrupt, Data)
DB 0x3f, 0x00 ; wMaxPacketSize (63 bytes)
DB 10 ; bInterval (10ms)
; Mouse interface
DB 9 ; bLength
DB 4 ; bDescriptorType (INTERFACE)
DB 1 ; bInterfaceNumber
DB 0 ; bAlternateSetting
DB 1 ; bNumEndpoints
DB 3 ; bInterfaceClass (HID)
DB 1 ; bInterfaceSubClass (boot interface)
DB 2 ; bInterfaceProtocol (mouse)
DB 0 ; iInterface (n/a)
; FIXME: Add Mouse HID descriptor once Keyboard is working.
DB 7 ; bLength
DB 5 ; bDescriptorType (ENDPOINT)
DB 0x82 ; bEndpointAddress (EP2 IN)
DB 0x03 ; bmAttributes (Interrupt, Data)
DB 0x3f, 0x00 ; wMaxPacketSize (63 bytes)
DB 10 ; bInterval (10ms)
_configuration_descriptor_end:
_string_langids:
DB 4, 3, 9, 4
_string_mfg:
DB 14, 3
DW "Lenovo"
_string_product:
DB 92, 3
DW "ThinkPad Compact USB Keyboard with TrackPoint"
_usb_status_ok:
DW 0
_kbd_hid_descriptor:
DB 9 ; bLength
DB 0x21 ; bDescriptorType (HID)
DB 0x00, 0x01 ; bcdHID (1.00)
DB 0 ; bCountryCode (Not Supported)
DB 1 ; bNumDescriptors
DB 0x22 ; bDescriptorType (Report)
DB 63, 0 ; wDescriptorLength (63)
_kbd_hid_descriptor_end:
_kbd_report_descriptor:
; Boot-compatible descriptor
DB 0x05, 1 ; Usage Page (Generic Desktop)
DB 0x09, 6 ; Usage (Keyboard)
DB 0xa1, 1 ; Collection (Application)
; Modifier keys
DB 0x05, 7 ; Usage Page (Key Codes)
DB 0x19, 224 ; Usage Minimum (224)
DB 0x29, 231 ; Usage Maximum (231)
DB 0x15, 0 ; Logical Minimum (0)
DB 0x25, 1 ; Logical Minimum (1)
DB 0x75, 1 ; Report Size (1)
DB 0x95, 8 ; Report Count (8)
DB 0x81, 2 ; Input (Data, Variable, Absolute)
; Reserved byte
DB 0x95, 1 ; Report Count (1)
DB 0x75, 8 ; Report Size (8)
DB 0x81, 1 ; Input (Constant, Absolute)
; LED byte (5 bits + 3 bits padding)
DB 0x95, 5 ; Report Count (5)
DB 0x75, 1 ; Report Size (1)
DB 0x05, 8 ; Usage Page (LEDs)
DB 0x19, 1 ; Usage Minimum (1)
DB 0x29, 5 ; Usage Maximum (5)
DB 0x91, 2 ; Output (Data, Variable, Absolute)
DB 0x95, 1 ; Report Count (1)
DB 0x75, 3 ; Report Size (3)
DB 0x91, 1 ; Output (Data, Variable, Absolute)
; 6 key code bytes
DB 0x95, 6 ; Report Count (6)
DB 0x75, 8 ; Report Size (8)
DB 0x15, 0 ; Logical Minimum (0)
DB 0x25, 175 ; Logical Maximum (175)
DB 0x05, 7 ; Usage Page (Key Codes)
DB 0x19, 0 ; Usage Minimum (0)
DB 0x29, 175 ; Usage Maximum (175)
DB 0x81, 0 ; Input (Data, Array)
DB 0xc0 ; End Collection (Application)
_kbd_report_descriptor_end:
_clamp_size:
B0MOV A, wLengthLo
SUB A, txSizeLo
B0MOV A, wLengthHi
SBC A, txSizeHi
B0BTS0 FC ; Return if carry set
RET
B0MOV A, wLengthLo
B0MOV txSizeLo, A
B0MOV A, wLengthHi
B0MOV txSizeHi, A
RET
_usb_get_desc_device:
MOV A, #'D'
CALL _uart_tx
MOV A, #_device_descriptor$M
B0MOV txPtrHi, A
MOV A, #_device_descriptor$L
B0MOV txPtrLo, A
MOV A, #0x12
B0MOV txSizeLo, A
_usb_get_desc:
CALL _clamp_size
CALL _usb_write_ep0
RET
_usb_get_desc_config:
MOV A, #'C'
CALL _uart_tx
MOV A, #_configuration_descriptor$M
B0MOV txPtrHi, A
MOV A, #_configuration_descriptor$L
B0MOV txPtrLo, A
MOV A, #0x32
B0MOV txSizeLo, A
JMP _usb_get_desc
_usb_get_desc_string:
B0MOV A, wValueLo
ADD A, #0xfd
B0BTS0 FC
JMP _usb_get_desc_badidx
B0MOV A, wValueLo
B0ADD PCL, A
JMP _usb_get_string_langids
JMP _usb_get_string_mfg
JMP _usb_get_string_product
_usb_get_string_langids:
MOV A, #_string_langids$M
B0MOV txPtrHi, A
MOV A, #_string_langids$L
B0MOV txPtrLo, A
MOV A, #4
B0MOV txSizeLo, A
JMP _usb_get_desc
_usb_get_string_mfg:
MOV A, #_string_mfg$M
B0MOV txPtrHi, A
MOV A, #_string_mfg$L
B0MOV txPtrLo, A
MOV A, #14
B0MOV txSizeLo, A
JMP _usb_get_desc
_usb_get_string_product:
MOV A, #_string_product$M
B0MOV txPtrHi, A
MOV A, #_string_product$L
B0MOV txPtrLo, A
MOV A, #92
B0MOV txSizeLo, A
JMP _usb_get_desc
_usb_get_desc_hid:
_usb_get_desc_report:
_usb_get_desc_physical:
MOV A, #_kbd_report_descriptor$M
B0MOV txPtrHi, A
MOV A, #_kbd_report_descriptor$L
B0MOV txPtrLo, A
MOV A, #63
B0MOV txSizeLo, A
JMP _usb_get_desc
_usb_write_ep0:
MOV A, #'W'
CALL _uart_tx
; Check if this is a 0byte-write and bail out early
B0MOV A, txSizeLo
OR A, txSizeHi
B0BTS0 FZ ; Jump if zero
JMP _write_empty_to_ep0
; Clamp packet byte count to EP0 size
B0MOV A, txSizeHi
B0BTS1 FZ ; Jump if not zero
JMP _write_to_ep0_big
MOV A, txSizeLo
B0MOV R, A
MOV A, #EP0_BYTES
SUB A, txSizeLo
B0BTS1 FC ; Skip if carry
_write_to_ep0_big:
B0MOV R, #EP0_BYTES
_write_to_ep0_copy:
; Save packet size fo updating UE0R later
MOV A, R
B0MOV txPktLen, A
B0MOV txTmpCnt, A
; Copy data into EP0 xmit buffer
MOV A, #0
B0MOV UDP0, A
_write_to_ep0_copy_loop:
B0MOV A, txPtrHi
B0MOV Y, A
B0MOV A, txPtrLo
B0MOV Z, A
MOVC
B0MOV UDR0_W, A
INCMS UDP0
B0MOV A, R
B0MOV UDR0_W, A
INCMS UDP0
INCMS txPtrLo
JMP @F
INCMS txPtrHi
@@:
DECMS txTmpCnt
JMP @F
JMP _write_to_ep0_update_len
@@:
DECMS txTmpCnt
JMP _write_to_ep0_copy_loop
_write_to_ep0_update_len:
B0MOV A, txPktLen
XCH A, txSizeLo
SUB txSizeLo, A ; M = A - M
MOV A, #0
XCH A, txSizeHi
SBC txSizeHi, A ; M = A - M - /C
B0MOV A, txPktLen
OR A, #0x20 ; Set to ACK for
B0MOV UE0R, A
B0MOV A, txPktLen
CALL _uart_hex
B0MOV A, txPktLen
B0BTS0 FZ ; Skip if not zero
_write_done:
B0BSET usbStateIn0Done
_write_not_yet_done:
RET
_write_empty_to_ep0:
B0MOV txPktLen, A
B0BTS1 usbStateIn0Done ; Jump if unset
JMP _write_to_ep0_update_len
MOV A, #'s'
CALL _uart_tx
JMP _write_to_ep0_update_len
;B0BSET FUE0M1 ; Stall EP0 (FUE0M0 doesn't matter)
;RET
_usb_dth_get_status:
MOV A, #_usb_status_ok$M
B0MOV txPtrHi, A
MOV A, #_usb_status_ok$L
B0MOV txPtrLo, A
MOV A, #2
B0MOV txSizeLo, A
JMP _usb_write_ep0
_usb_htd_set_configuration:
; Only one configuration, nothing to do.
MOV A, #0x20 ; ACK with no TX
B0MOV UE0R, A
RET
_usb_htd_set_address:
MOV A, #'a'
CALL _uart_tx
B0BSET usbStateSetAddress
MOV A, #0x20 ; ACK with no TX
B0MOV UE0R, A
RET
_usb_htd_hid_set_idle:
MOV A, #'I'
CALL _uart_tx
MOV A, #0x20 ; ACK with no TX
B0MOV UE0R, A
RET
_usb_htd_hid_set_report:
MOV A, #'r'
CALL _uart_tx
B0MOV A, EP0OUT_CNT
CALL _uart_hex
; First, set the "enter flasher bit"
B0BSET usbStateEnterFlasher
; Check each bit against the expected pattern and clear the flag if mismatch
B0MOV A, EP0OUT_CNT
CMPRS A, #0x08
B0BCLR usbStateEnterFlasher
MOV A, #0
B0MOV UDP0, A
B0MOV A, UDR0_R
CMPRS A, #0xaa
B0BCLR usbStateEnterFlasher
CALL _uart_hex
INCMS UDP0
B0MOV A, UDR0_R
CMPRS A, #0x55
B0BCLR usbStateEnterFlasher
CALL _uart_hex
INCMS UDP0
B0MOV A, UDR0_R
CMPRS A, #0xa5
B0BCLR usbStateEnterFlasher
CALL _uart_hex
INCMS UDP0
B0MOV A, UDR0_R
CMPRS A, #0x5a
B0BCLR usbStateEnterFlasher
CALL _uart_hex
MOV A, #0x20 ; ACK with no TX
B0MOV UE0R, A
; Enter flasher if flag is still set
B0BTS0 usbStateEnterFlasher
JMP _flasher
RET
_usb_dth_hid_get_report:
MOV A, #'g'
CALL _uart_tx
; Send last 8 bytes in buffer
; FIXME: Handle this properly
MOV A, #0x28
B0MOV UE0R, A
RET
_usb_setup:
MOV A, #0 ; Unstall & NAK EP0 IN/OUT
B0MOV UE0R, A
B0MOV txSizeLo, A
B0MOV txSizeHi, A
B0MOV usbState, A
MOV A, #' '
CALL _uart_tx
MOV A, #'S'
CALL _uart_tx
; Copy setup packet
MOV A, #8
B0MOV R, A
B0MOV Y, #bmRequestType$M
B0MOV Z, #bmRequestType$L
MOV A, #0
B0MOV UDP0, A
@@:
B0MOV A, UDR0_R
B0MOV @YZ, A
INCMS UDP0
INCMS Z
DECMS R
JMP @B
NOP
; Log bRequest
MOV A, #1
B0MOV UDP0, A
B0MOV A, UDR0_R
CALL _uart_hex
B0BTS0 bmRequestType.7 ; Jump if bit7 set
JMP _usb_setup_dth
; host is writing to device, check if there will be OUT with data.
MOV A, wLengthLo
OR A, wLengthHi
B0BTS0 FZ ; Jump if length is 0
JMP _usb_setup_htd_no_data
B0BSET FUE0M0 ; Change EP0 to ACK, wait for data
B0BSET usbStateHTDData
JMP _usb_setup_exit
_usb_setup_htd_got_data:
_usb_setup_htd_no_data:
_usb_setup_dth:
B0MOV Y, #_setup_dispatch_table$M
B0MOV Z, #_setup_dispatch_table$L
_usb_setup_do_dispatch:
B0MOV A, bmRequestType
B0MOV R, A
B0MOV A, bRequest
CALL _dispatch
B0BTS1 usbStateSetupInvalid
JMP _setup_done_valid
B0BSET FUE0M1 ; Stall EP0
_setup_done_valid:
B0BTS1 bmRequestType.7 ; Skip if bit7 set
B0BSET FUE0M0 ; Unnak EP0 IN for final ACK
_usb_setup_exit:
B0BCLR FEP0SETUP ; Late ack of SETUP irq
JMP _mainloop
_dispatch:
B0MOV xlatVal1, A
MOV A, R
B0MOV xlatVal2, A
JMP _xlat_next
_xlat_loop:
AND A, R
CMPRS A, #0xff ; Jump if last entry
JMP @F
JMP _xlat_do_indirect_jump
@@:
CALL _inc_yz ; skip jump target
CALL _inc_yz
_xlat_next:
MOVC ; Read ROM word into R (hi) and A (lo)
CMPRS A, xlatVal1 ; Jump if A != xlatVal1
JMP _xlat_loop
XCH A, R
CMPRS A, xlatVal2 ; Jump if R != xlatVal2
JMP _xlat_loop
_xlat_do_indirect_jump:
CALL _inc_yz
CALL _jmp_yz
RET ; never reached, kept for disassembler
_jmp_yz: ; FIXME: Interrupts must be disabled
; DS is underspecified, but experimentally it
; looks like if CALL goes from level 0 to level 1,
; then the return PC is stored in STK0H/STK0L
;
; Level STKPB2 STKPB1 STKPB0 HighByte LowByte
; 0 1 1 1 n/a n/a
; 1 1 1 0 STK0H STK0L
; 2 1 0 1 STK1H STK1L
; [...]
; 6 0 0 1 STK5H STK5L
; 7 0 0 0 STK6H STK6L
; 8 1 1 1 STK7H STK7L
B0MOV A, STKP
AND A, #7
B0ADD PCL, A
JMP _set_stack_6 ; STKP 0 / Level 7
JMP _set_stack_5 ; STKP 1 / Level 6
JMP _set_stack_4 ; STKP 2 / Level 5
JMP _set_stack_3 ; STKP 3 / Level 4
JMP _set_stack_2 ; STKP 4 / Level 3
JMP _set_stack_1 ; STKP 5 / Level 2
JMP _set_stack_0 ; STKP 6 / Level 1
JMP _set_stack_7 ; STKP 7 / Level 8 [or 0]
_set_stack_0:
B0MOV A, Y
B0MOV STK0H, A
B0MOV A, Z
B0MOV STK0L, A
RET
_set_stack_1:
B0MOV A, Y
B0MOV STK1H, A
B0MOV A, Z
B0MOV STK1L, A
RET
_set_stack_2:
B0MOV A, Y
B0MOV STK2H, A
B0MOV A, Z
B0MOV STK2L, A
RET
_set_stack_3:
B0MOV A, Y
B0MOV STK3H, A
B0MOV A, Z
B0MOV STK3L, A
RET
_set_stack_4:
B0MOV A, Y
B0MOV STK4H, A
B0MOV A, Z
B0MOV STK4L, A
RET
_set_stack_5:
B0MOV A, Y
B0MOV STK5H, A
B0MOV A, Z
B0MOV STK5L, A
RET
_set_stack_6:
B0MOV A, Y
B0MOV STK6H, A
B0MOV A, Z
B0MOV STK6L, A
RET
_set_stack_7:
B0MOV A, Y
B0MOV STK7H, A
B0MOV A, Z
B0MOV STK7L, A
RET
_return_held:
MOV A, #'E'
CALL _uart_tx
MOV A, #'r'
CALL _uart_tx
JMP _flasher
_uart_shorted:
B0BSET 0xa9.4 ; Switch UTX back to UART
MOV A, #'E'
CALL _uart_tx
MOV A, #'s'
CALL _uart_tx
JMP _flasher
_inc_yz:
INCMS Z
JMP @F
INCMS Y
RET
@@:
RET
_uart_hex:
MOV R, A
SWAP R
CALL _uart_nibble
MOV A, R
; fall-through
_uart_nibble:
AND A, #0xf
ADD A, #0xf6 ; -0xa
B0BTS0 FC ; Skip next insn if carry unset
ADD A, #0x27 ; 'a' - '0' - 0xa
ADD A, #0x3a ; '0' + 0xa
; fall-through
_uart_tx:
B0MOV URTXD1, A
@@:
B0BTS1 FUTTXIRQ ; Check if TX is done
JMP @B
B0BCLR FUTTXIRQ
RET
_delayshort:
MOV A, #0
B0MOV R, A
@@:
DECMS R
JMP @B
RET
ORG 0x27ff
DW 0xaaaa ; canary
ORG _canary_check
JMP _start
ORG _flasher
JMP $