; This program demonstrates use of all mouse functions known to author. Written for Scenix SX28 ; with LCD configured for display purposes. ; Author: Steve Parkis. Pls direct comments, additional information, or corrections to ; parkiss@earthlink.net DEVICE SX28L, OSC4MHZ, TURBO, STACKX_OPTIONX ; "A" silicon, 4 MHz internal oscillator RESET startExecution ; begin execution location ;************************************************************************** ; Pin Definitions and Equates ;************************************************************************** ; SX28 port a equates (ra.0 through ra.3 connected to LCD d4 through d7) ; SX28 port b equates mouse_clock_pin equ 0 ; connected to mouse clock line mouse_data_pin equ 1 ; connected to mouse data line ; SX28 port c equates lcd_rs equ 4 ; LCD register select output lcd_en equ 5 ; LCD enable output lcd_rw equ 6 ; LCD read/write output ;************************************************************************** ; Equates ;************************************************************************** ; mouse mask equates clock_mask equ $01 << mouse_clock_pin data_mask equ $01 << mouse_data_pin ; mouse data packet equates left_button equ 0 right_button equ 1 x_disp_neg equ 4 y_disp_neg equ 5 x_overflow equ 6 y_overflow equ 7 ; mouse command equates reset_scale equ $e6 ; set X, Y scaling 1:1 set_scale equ $e7 ; set X, Y scaling 2:1 set_resolution equ $e8 ; counts/mm send_status equ $e9 ; get mouse status (3 bytes returned) set_stream equ $ea ; send data upon mouse operation send_data equ $eb ; force mouse data xmit (for remote mode) reset_loopback equ $ec ; leave wrap (loopback) mode set_loopback equ $ee ; set wrap (loopback) mode set_remote equ $f0 ; send data by request only id_mouse equ $f2 ; send id info set_sample_rate equ $f3 ; samples/sec enable_stream equ $f4 ; enable transfers if in stream mode disable_stream equ $f5 ; disable transfers if in stream mode set_standard equ $f6 ; set mouse to default parameters repeat_xmit equ $fe ; repeat last mouse transmission mouse_reset equ $ff ; reset, do self-test ; mouse reply equate mouse_ack equ $fa ; command acknowledged mouse_huh equ $fe ; last command invalid mouse_bat equ $aa ; self-test completed successfully mouse_id equ $00 ; response to id_mouse command ; boolean equates True equ 1 False equ 0 ; port control register equates CMPR equ $08 WKPND equ $09 WKED equ $0a WKEN equ $0b ST equ $0c LVL equ $0d PLP equ $0e DIR equ $0f ; register bank equates Zero equ $00 One equ $30 Two equ $50 Three equ $70 Four equ $90 Five equ $B0 Six equ $D0 Seven equ $F0 ; LCD equates lcd_busy equ 3 ; bit position (HO bit of nibble) external_reset equ TRUE ; controls whether external reset logic executed ;************************************************************************** ; Global Variables ;************************************************************************** org $8 lcd_out ds 1 ; command or data destined for LCD lcd_in ds 1 ; data from LCD tst ds 1 ; byte for binary display on LCD bit_counter ds 1 ; counts bits for debug binary display delay0 ds 1 ; delay variables delay1 ds 1 delay2 ds 1 byte_counter ds 1 ; counts bytes for character demo ;************************************************************************** ; Bank 0 (non-global) registers ;************************************************************************** mouse0 ds 1 mouse1 ds 1 mouse2 ds 1 mouse3 ds 1 mouse_data ds 1 parity ds 1 temp ds 1 loop_counter ds 1 response_count ds 1 ;************************************************************************** ; Bank 1 (non-global) registers ;************************************************************************** org $30 ;************************************************************************** ; Format of bytes transmitted by mouse ;************************************************************************** ; status bytes...returned in response to 'send status' command ; byte1 bit7 reserved ; bit6 mode (0 = stream, 1 = remote) ; bit5 stream setting (0 = disabled, 1 = enabled) ; bit4 displacement count scaling (0 = 1:1, 1 = 2:1) ; bit3 reserved (usually '0') ; bit2 left button (1 = currently pressed) ; bit1 reserved (usually '0') ; bit0 right button (1 = currently pressed) ;byte2 resolution (counts reported per millimeter displacement in sample period) ;byte3 sample rate (samples/sec...max sample transmission rate in stream mode) ; data bytes...sent in stream mode upon operator action or when prompted in remote mode ; byte1 bit7 y displacement overflowed = 1 ; bit6 x displacement overflowed = 1 ; bit5 1 = y displacement count is two's complement negative number (mouse moved down) ; bit4 1 = x displacement count is two's complement negative number (mouse moved left) ; bit3 reserved (usually '1') ; bit2 reserved (usually '0') ; bit1 right button (1 = currently pressed) ; bit0 left button (1 = currently pressed) ; byte2 x displacement count (left-right) ; byte3 y displacement count (up-down) ;************************************************************************** ; Macros ;************************************************************************** jl MACRO 2 ; jump if low sb \1 jmp \2 ENDM jh MACRO 2 ; jump if high snb \1 jmp \2 ENDM cmp MACRO 2 ; compare register value to literal value mov w,#\2 mov w,\1 - w ENDM ;#################################################################################################### ;#################################################################################################### org $000 ; page 0, interrupt service routine & start ;#################################################################################################### ;#################################################################################################### ; (ISR not implemented) startExecution jmp @start ; main routine on page 1 ;#################################################################################################### ;#################################################################################################### org $200 ; page 1, main routine ;#################################################################################################### ;#################################################################################################### start call @init ; initialize variables and LCD jmp mouseReset ; start running through mouse commands awaitIdle ; await idle condition (solid clock and data high) mov loop_counter,#100 awaitIdleLoop jl rb.mouse_clock_pin,awaitIdle jl rb.mouse_data_pin,awaitIdle djnz loop_counter,awaitIdleLoop ret mouseDialog1 ; send mouse command and accept 1 byte in response mov response_count,#1 call commandToMouse call getMouseByte jnc oops mov mouse0,mouse_data ret mouseDialog2 ; send mouse command and accept 2 bytes in response mov response_count,#2 call commandToMouse jnc oops mov mouse0,mouse_data call getMouseByte jnc oops mov mouse1,mouse_data ret mouseDialog3 ; send mouse command and accept 3 bytes in response mov response_count,#3 call commandToMouse jnc oops mov mouse0,mouse_data call getMouseByte jnc oops mov mouse1,mouse_data call getMouseByte jnc oops mov mouse2,mouse_data ret mouseDialog4 ; send mouse command and accept 4 bytes in response mov response_count,#4 call commandToMouse call getMouseByte jnc oops mov mouse0,mouse_data call getMouseByte jnc oops mov mouse1,mouse_data call getMouseByte jnc oops mov mouse2,mouse_data call getMouseByte jnc oops mov mouse3,mouse_data inc response_count ret showResponses ; show bytes returned in response to command mov tst,mouse0 call @debug dec response_count test response_count snz ret mov tst,mouse1 call @debug dec response_count test response_count snz ret mov tst,mouse2 call @debug dec response_count test response_count snz ret mov tst,mouse3 call @debug ret commandToMouse ; send command to mouse ; drive clock low ~150 usec to 'seize' interface...drive data line low and release clock (serves ; as start bit)...mouse will drive clock low to prompt 8 data bits, (odd) parity bit and (high) ; stop bit...mouse will drive clock and data low to acknowledge command reception call awaitIdle mov loop_counter,#150 ; seize interface by driving clock low mov !rb,#%11111110 inhibitMouse djnz loop_counter,inhibitMouse mov !rb,#%11111101 ; open clock + low data serves as 'start bit' mov loop_counter,#8 ; prepare to send 8 bits clr temp ; track parity in temp.0 txBitLoop call awaitLowClock ; wait for mouse to drive clock low snb mouse_data.0 ; release data line if sending '1' mov !rb,#%11111111 sb mouse_data.0 ; drive data line low if sending '0' mov !rb,#%11111101 xor temp,mouse_data ; include this bit in parity calculation rr mouse_data call awaitHighClock ; wait for clock to go back high djnz loop_counter,txBitLoop ; repeat until 8 bits transmitted call awaitLowClock ; send parity bit based on temp.0 sb temp.0 mov !rb,#%11111111 snb temp.0 mov !rb,#%11111101 call awaitHighClock ; let clock go back high call awaitLowClock ; send (high) stop bit mov !rb,#%11111111 call awaitHighClock ; let clock go back high call awaitLowClock ; await ack bit from mouse jh rb.mouse_data_pin,oops ; should be low call awaitHighClock ; let clock go back high ret getMouseByte ; receive byte from mouse ; upon low clock from mouse ensure data line is low (start bit)...get 8 data bits on ; succeeding low clocks...get (odd) parity bit and (high) stop bit on next two clocks mov delay0,#10 ; wait for start bit's low clock, but not forever clr delay1 clr delay2 getMouseByteAwait jl rb.mouse_clock_pin,mouseResponding ; if clock line low, mouse is talking djnz delay2,getMouseByteAwait djnz delay1,getMouseByteAwait djnz delay0,getMouseByteAwait clc ; if no clock, return with no carry = no byte received ret mouseResponding jh rb.mouse_data_pin,oops ; data line should be low (start bit) clr temp ; form parity bit in temp call awaitHighClock ; let clock go back high mov loop_counter,#8 ; get 8 data bits rxBitLoop call awaitLowClock ; wait for mouse to drive clock low stc ; take a guess sb rb.mouse_data_pin clc ; guessed wrong if here rr mouse_data xor temp,rb ; include this bit in parity calculation call awaitHighClock ; wait for clock to return high djnz loop_counter,rxBitLoop ; repeat until 8 bits received call awaitLowClock ; await parity bit xor temp,rb ; see if parity matches and temp,#$01 << mouse_data_pin jz oops ; if not, problems call awaitHighClock ; wait for clock to return high call awaitLowClock ; await stop bit jl rb.mouse_data_pin,oops ; problems if stop bit not high call awaitHighClock stc ; return with carry set = byte received ret oops ; critical error in reception occurred mov lcd_out,#'?' call @lcdData jmp $ awaitLowClock ; wait for solid low on clock pin jh rb.mouse_clock_pin,awaitLowClock jh rb.mouse_clock_pin,awaitLowClock ret awaitHighClock ; wait for solid high on clock pin jl rb.mouse_clock_pin,awaitHighClock jl rb.mouse_clock_pin,awaitHighClock ret mouseReset ; Initiate mouse reset and self-test. ; sequence: To mouse From mouse Comments ; ffh reset, self-test command ; fah command acknowledgement ; (mouse conducts self-test) ; aah passed self-test ; 00h ????? mov mouse_data,#mouse_reset call mouseDialog3 call showResponses idMouse ; Prompt mouse to return ID code (00h) ; sequence: To mouse From mouse Comments ; ; f2h return mouse ID command ; fah command acknowledgement ; 00h mouse ID mov mouse_data,#id_mouse call mouseDialog2 call showResponses setSampleRate ; Set samples/sec rate. Send # samples per second as parameter following command. ; Valid parameter values: 10, 20, 40, 60, 80, 100, 200. ; sequence: To mouse From mouse Comments ; f3h set sample rate command ; fah command acknowledgement ; parameter sample rate ; fah command acknowledgement mov mouse_data,#set_sample_rate call mouseDialog1 call showResponses mov mouse_data,#40 ; 40 samples/sec call mouseDialog1 call showResponses setResolution ; Set counts/mm reported by mouse. Send # counts per mm following command ; as 2 ^ parameter (e.g. 2 ^ 0 = 1 count/mm...2 ^ 3 = 8 counts/mm). Valid ; parameter values: 0, 1, 2, 3. ; sequence: To mouse From mouse Comments ; e8h set resolution command ; fah command acknowledgement ; parameter resolution ; fah command acknowledgement mov mouse_data,#set_resolution call mouseDialog1 call showResponses mov mouse_data,#$01 ; 2 count/mm call mouseDialog1 call showResponses setScaling ; Double X, Y count values reported by mouse (for stream mode only). ; sequence: To mouse From mouse Comments ; e7h set scaling command ; fah command acknowledgement mov mouse_data,#set_scale call mouseDialog1 call showResponses setRemoteMode ; Set mouse to 'report data only upon host request' mode. Mouse accumulates ; movement data and reports summations in this mode. ; sequence: To mouse From mouse Comments ; f0h set remote mode command ; fah command acknowledgement mov mouse_data,#set_remote call mouseDialog1 call showResponses sendStatus ; Prompt mouse to report internal status. Accept ack followed by 3 status bytes. ; sequence: To mouse From mouse Comments ; e9h send status command ; fah command acknowledgement ; data1 ; data2 ; data3 mov mouse_data,#send_status call mouseDialog4 call showResponses getRemoteData ; Request data while in remote mode. Accept ack followed by 3 data bytes in response. ; Instantaneous state of buttons is reported (button pressed and released since last ; getRemoteData goes unreported). ; sequence: To mouse From mouse Comments ; ebh get remote mode data command ; fah command acknowledgement ; data1 ; data2 ; data3 mov mouse_data,#send_data call mouseDialog4 call showResponses repeatXmit ; Repeat last data packet. Accept 3 data bytes in response. Note no ack in response. ; sequence: To mouse From mouse Comments ; feh repeat last data packet command ; data1 ; data2 ; data3 mov mouse_data,#repeat_xmit call mouseDialog3 call showResponses setStream ; Set mouse to 'report data upon operation' mode. ; sequence: To mouse From mouse Comments ; eah set mouse to stream mode command ; fah command acknowledgement mov mouse_data,#set_stream call mouseDialog1 call showResponses enableStream ; Enable automatic transmission of operational data when in stream mode. ; sequence: To mouse From mouse Comments ; f4h enable stream command ; fah command acknowledgement mov mouse_data,#enable_stream call mouseDialog1 call showResponses resetScaling ; 1:1 X, Y count values reported by mouse (for stream mode only). ; sequence: To mouse From mouse Comments ; e6h reset scaling command ; fah command acknowledgement mov mouse_data,#reset_scale call mouseDialog1 call showResponses disableStream ; Disable mouse transmission of operational data when in stream mode. ; sequence: To mouse From mouse Comments ; f5h disable stream mode transmissions command ; fah command acknowledgement mov mouse_data,#disable_stream call mouseDialog1 call showResponses setLoopBack ; Set mouse in loopback mode. ; sequence: To mouse From mouse Comments ; eeh set mouse in loopback mode command ; fah command acknowledgement ; xx ; xx mov mouse_data,#set_loopback call mouseDialog1 call showResponses mov mouse_data,#$99 call mouseDialog1 call showResponses resetLoopBack ; Reset loopback mode. ; sequence: To mouse From mouse Comments ; ech reset loopback mode command ; fah command acknowledgement mov mouse_data,#reset_loopback call mouseDialog1 call showResponses setStandard ; Initialize mouse to default operating parameters ; sequence: To mouse From mouse Comments ; f6h set mouse to standard parameters command ; fah command acknowledgement mov mouse_data,#set_standard call mouseDialog1 call showResponses mov mouse_data,#enable_stream call mouseDialog1 mov mouse_data,#set_resolution call mouseDialog1 mov mouse_data,#$00 ; 1 count/mm call mouseDialog1 mov mouse_data,#set_sample_rate call mouseDialog1 mov mouse_data,#10 ; 40 samples/sec call mouseDialog1 mov mouse_data,#send_status call mouseDialog4 call showResponses analyzeStream ; Examine stream input and display operator actions on LCD...repeat indefinitely jh rb.mouse_clock_pin,analyzeStream ;wait for mouse to drive clock low call getMouseByte ; get first data byte mov mouse0,mouse_data call getMouseByte ; get second data byte mov mouse1,mouse_data call getMouseByte ; get third data byte mov mouse2,mouse_data mov lcd_out,#1 ; clear LCD screen call @lcdCommand mov lcd_out,#'L' ; display 'L' if left button pressed snb mouse0.left_button call @lcdData mov lcd_out,#'R' ; display 'R' if right button pressed snb mouse0.right_button call @lcdData test mouse1 ; skip x displacement display if zero jz xDispDone jl mouse0.x_disp_neg,xDispPositive ; branch if x displacement positive mov lcd_out,#'l' ; display 'l' for left (-x) motion call @lcdData not mouse1 ; convert displacement to positive value inc mouse1 mov tst,mouse1 ; display displacement call @debug jmp xDispDone xDispPositive mov lcd_out,#'r' ; display 'r' for right (+x) motion call @lcdData mov tst,mouse1 ; display displacement call @debug xDispDone test mouse2 ; skip y displacement display if zero jz yDispDone jl mouse0.y_disp_neg,yDispPositive ; branch if y displacement positive mov lcd_out,#'d' ; display 'd' for downward (-y) motion call @lcdData not mouse2 ; convert displacement to positive value inc mouse2 mov tst,mouse2 ; display displacement call @debug jmp yDispDone yDispPositive mov lcd_out,#'u' ; display 'u' for upward (+y) displacement call @lcdData mov tst,mouse2 ; display displacement call @debug yDispDone mov lcd_out,#'!' ; display '!' if either displacement overflowed and mouse0,#($01 << x_overflow) + ($01 << y_overflow) jz analyzeComplete call @lcdData analyzeComplete call awaitIdle ; await idle condition jmp analyzeStream ; go get the next one ;#################################################################################################### ;#################################################################################################### org $400 ; page 2, LCD routines ;#################################################################################################### ;#################################################################################################### initLCD mov delay2,#20 ; let LCD do internal initialization call delay clrb rc.lcd_rs ; set LCD register select to command clrb rc.lcd_rw ; set LCD R/W to write IF external_reset mov lcd_out,#%00110000 ; send external reset sequence call nibbleToLCD mov lcd_out,#%00110000 call nibbleToLCD call pause120 mov lcd_out,#%00110000 call nibbleToLCD ENDIF mov lcd_out,#%00100000 ; 4-bit i'face (special 1-time 4-bit command) call nibbleToLCD mov lcd_out,#%00101000 ; 4-bit i'face, 2 lines, 5 x 7 call @lcdCommand mov lcd_out,#%00001111 ; display on, blinking cursor call @lcdCommand mov lcd_out,#%00000110 ; cursor increments, no shift call @lcdCommand mov lcd_out,#%00000001 ; clear display, cursor home call @lcdCommand retp lcdCommand call awaitNotBusy ; ensure execution of last command complete clrb rc.lcd_rs ; send register select low (sending command) clrb rc.lcd_rw ; send R/W low jmp byteToLCD lcdData call awaitNotBusy ; ensure execution of last command complete setb rc.lcd_rs ; send register select high (sending data) clrb rc.lcd_rw ; send R/W low byteToLCD call nibbleToLCD ; send HO nibble to LCD call nibbleToLCD ; send LO nibble to LCD retp nibbleToLCD swap lcd_out ; swap lcd_out nibbles mov ra,lcd_out ; preload LCD data lines to ra's LO nibble setb rc.lcd_en ; send LCD enable high mode DIR ; assert LCD data lines mov !ra,#%0000 call pause480 ; pause 480 nsec (require >= 360 nsec 'data delay time') clrb rc.lcd_en ; send LCD enable back low mov !ra,#%1111 ; return LCD data pins to input call pause540 ; pause 540 nsec (require >= 1000 nsec enable cycle time) ret awaitNotBusy clrb rc.lcd_rs setb rc.lcd_rw ; set up busy flag read jmp $+1 ; 60 nsec @ 50 MHz (require >= 60 nsec 'address set-up time') setb rc.lcd_en ; send LCD enable high call pause480 ; pause 480 nsec (require >= 360 nsec 'data delay time') mov lcd_in,ra ; read LCD data clrb rc.lcd_en ; send LCD enable back low call pause540 ; pause 540 nsec (require >= 1000 nsec enable cycle time) setb rc.lcd_en ; send LCD enable back high for second read call pause480 ; pause 480 nsec (require >= 360 nsec 'data delay time') nop ; ignore low order nibble--only want busy flag status clrb rc.lcd_en ; send LCD enable back low again awaitNotBusyPause djnz delay0,awaitNotBusyPause ; let LCD do some work after each busy flag check sb lcd_in.lcd_busy ; if busy flag set, repeat ret ; else return jmp awaitNotBusy pause540 ; call here gives 540 nsec pause @ 50 MHz jmp $+1 pause480 ; call here gives 480 nsec pause @ 50 MHz jmp $+1 jmp $+1 jmp $+1 jmp $+1 jmp $+1 pause180 ; call here gives 180 nsec pause @ 50 MHz jmp $+1 pause120 ; call here gives 120 nsec pause @ 50 MHz ret delay djnz delay0,delay djnz delay1,delay djnz delay2,delay ret ;#################################################################################################### ;#################################################################################################### org $600 ; page 3: debug and utility routines ;#################################################################################################### ;#################################################################################################### init ; clear registers, initialize I/O pins, set up LCD mov fsr,#$08 ; clear all application registers clrMem1 ; clear $08-$1f clr ind inc fsr jl fsr.5,clrMem1 clrMem2 ; clear $30-$3f, $50-$5f,...$f0-$ff setb fsr.4 clr ind incsz fsr jmp clrMem2 bank Zero ; direct attention to bank 0 clr rb clr rc mode LVL mov !rb,#%00000000 mode PLP mov !rb,#%00000000 ; use internal pullups to accomplish open drain configuration mode DIR ; set input/output states mov !ra,#%1111 ; ra inputs to start mov !rb,#%11111111 ; rb inputs to start mov !rc,#%10001111 ; rc.4 through rc.6 outputs, others input call @initLCD retp debug ; send binary representation of byte in tst to LCD mov bit_counter,#8 ; show each bit in the byte debugLoop mov w,#$30 ; represent as '0' or '1' snb tst.7 mov w,#$31 mov lcd_out,w call @lcdData rl tst ; rotate next bit into tst.0 djnz bit_counter,debugLoop ; repeat until 8 bits processed mov lcd_out,#$20 ; append 2 spaces for readability call @lcdData mov lcd_out,#$20 call @lcdData retp