diff --git a/pvsneslib/include/snes/input.h b/pvsneslib/include/snes/input.h index 0c897b93..0f9024de 100644 --- a/pvsneslib/include/snes/input.h +++ b/pvsneslib/include/snes/input.h @@ -76,36 +76,75 @@ typedef enum SUPERSCOPE_BITS SSC_NOISE = BIT(8), //!< superscope NOISE flag. } SUPERSCOPE_BITS; +/*! + * \brief Mouse sensitivity values + * + * enum values for the mouse sensitivity. + */ +typedef enum MOUSE_SENSITIVITY +{ + MOUSE_SLOW = 0, //!< slow sensitivity + MOUSE_MEDIUM = 1, //!< medium sensitivity + MOUSE_FAST = 2, //!< fast sensitivity +} MOUSE_SENSITIVITY; + extern u16 pad_keys[5]; //!< current pad value extern u16 pad_keysold[5]; //!< previous pad value extern u16 pad_keysdown[5]; //!< newly pressed down pad keys -extern u8 snes_mplay5; /*! \brief 1 if MultiPlay5 is connected */ -extern u8 snes_mouse; /*! \brief 1 if Mouse is going to be used */ -extern u8 snes_sscope; /*! \brief 1 if SuperScope is connected */ - -extern u8 mouseConnect[2]; /*! \brief 1 if Mouse present */ -extern u8 mouseButton[2]; /*! \brief 1 if button is pressed, stays for a bit and then it gets released (Click mode). */ -extern u8 mousePressed[2]; /*! \brief 1 if button is pressed, stays until is unpressed (Turbo mode). */ -extern u8 mouse_x[2], mouse_y[2]; /*! \brief Mouse acceleration. daaaaaaa, d = direction (0: up/left, 1: down/right), a = acceleration. */ -extern u8 mouseSpeedSet[2]; /*! \brief Mouse speed setting. 0: slow, 1: normal, 2: fast */ - -#define mouse_L 0x01 /*! \brief SNES Mouse Left button mask.*/ -#define mouse_R 0x02 /*! \brief SNES Mouse Right button mask.*/ - -extern u16 scope_holddelay; /*! \brief Hold delay. */ -extern u16 scope_repdelay; /*! \brief Repeat rate. */ -extern u16 scope_shothraw; /*! \brief Horizontal shot position, not adjusted. */ -extern u16 scope_shotvraw; /*! \brief Vertical shot position, not adjusted. */ -extern u16 scope_shoth; /*! \brief Horizontal shot position, adjusted for aim. */ -extern u16 scope_shotv; /*! \brief Vertical shot position, adjusted for aim. */ -extern u16 scope_centerh; /*! \brief 0x0000 is the center of the screen, positive values go to bottom right. */ -extern u16 scope_centerv; /*! \brief 0x0000 is the center of the screen, positive values go to bottom right. */ -extern u16 scope_down; /*! \brief flags that are currently true.*/ -extern u16 scope_now; /*! \brief flags that have become true this frame.*/ -extern u16 scope_held; /*! \brief flagsthat have been true for a certain length of time.*/ -extern u16 scope_last; /*! \brief flags that were true on the previous frame.*/ -extern u16 scope_sinceshot; /*! \brief Number of frames elapsed since last shot was fired.*/ +extern u8 snes_mplay5; /*!< \brief 1 if MultiPlay5 is connected */ +extern u8 snes_mouse; /*!< \brief 1 if Mouse is going to be used */ +extern u8 snes_sscope; /*!< \brief 1 if SuperScope is connected */ + +extern u8 mouseConnect[2]; /*!< \brief 1 if Mouse present */ +extern u8 mouseButton[2]; /*!< \brief Mouse buttons that are pressed on this frame (Click mode). */ +extern u8 mousePressed[2]; /*!< \brief Mouse buttons that are currently pressed, stays until is unpressed (Turbo mode). */ +extern u8 mousePreviousPressed[2];/*!< \brief Mouse buttons held or pressed in the previous frame */ +extern u8 mouse_x[2]; /*!< \brief Mouse horizontal displacement. daaaaaaa, d = direction (1: left, 0: right), a = acceleration. */ +extern u8 mouse_y[2]; /*!< \brief Mouse vertical displacement. daaaaaaa, d = direction (1: up, 0: down), a = acceleration. */ + +/*! + * \brief Mouse sensitivity + * + * * When a mouse is connected to the port: sensitivity bits read from the mouse. + * * When no mouse is connected: The sensitivity to set the mouse to when the mouse is connected to the console. + * + * CAUTION: The Hyperkin clone mouse ignores sensitivity changes and always reports a sensitivity of 0. + */ +extern u8 mouseSensitivity[2]; + +/*! + * \brief Request a change mouse sensitivity. + * + * To prevent auto-joypad read corruption the change sensitivity commands are delayed until the + * next non-lag VBlank ISR (after the mouse data has been read). + * + * Values: + * * `0x00`: No changes to mouse sensitivity. + * * `0x01`: Cycle the mouse sensitivity once. + * * `0x02-0x7f`: Cycle the mouse sensitivity twice. + * * `0x80-0xff`: Set the sensitivity to `value & 3`. + * + * CAUTION: The Hyperkin clone mouse ignores sensitivity changes and always reports a sensitivity of 0. + */ +extern u8 mouseRequestChangeSensitivity[2]; + +#define mouse_L 0x01 /*!< \brief SNES Mouse Left button mask.*/ +#define mouse_R 0x02 /*!< \brief SNES Mouse Right button mask.*/ + +extern u16 scope_holddelay; /*!< \brief Hold delay. */ +extern u16 scope_repdelay; /*!< \brief Repeat rate. */ +extern u16 scope_shothraw; /*!< \brief Horizontal shot position, not adjusted. */ +extern u16 scope_shotvraw; /*!< \brief Vertical shot position, not adjusted. */ +extern u16 scope_shoth; /*!< \brief Horizontal shot position, adjusted for aim. */ +extern u16 scope_shotv; /*!< \brief Vertical shot position, adjusted for aim. */ +extern u16 scope_centerh; /*!< \brief 0x0000 is the center of the screen, positive values go to bottom right. */ +extern u16 scope_centerv; /*!< \brief 0x0000 is the center of the screen, positive values go to bottom right. */ +extern u16 scope_down; /*!< \brief flags that are currently true.*/ +extern u16 scope_now; /*!< \brief flags that have become true this frame.*/ +extern u16 scope_held; /*!< \brief flagsthat have been true for a certain length of time.*/ +extern u16 scope_last; /*!< \brief flags that were true on the previous frame.*/ +extern u16 scope_sinceshot; /*!< \brief Number of frames elapsed since last shot was fired.*/ /*! \def REG_JOYxLH @@ -192,15 +231,59 @@ void detectMPlay5(void); */ void detectMouse(void); -/*! \fn mouseSpeedChange(u8 port) - \brief Set mouse hardware speed (populate mouseSpeed[] first). - \param port Specify wich port to use (0-1) -*/ -void mouseSpeedChange(u8 port); - /*! \fn detectSuperScope(void) \brief Detects if SuperScope is connected on Port 1 (second controller port on console) and populate snes_sscope (0 or 1 for connected) */ void detectSuperScope(void); + +/*! + * \brief Enable mouse reading and set the initial mouse sensitivity + * + * Initialises mouse variables and enable mouse reading in the VBlank ISR. + * + * \param sensitivity sensitivity to use when the mouse is connected to the console + * (has no effect on a Hyperkin clone mouse) + */ +void initMouse(u8 sensitivity); + + +/*! + * \brief Queue a cycle mouse sensitivity command for the next VBlank. + * + * \param port the port the mouse is connected to (0 or 1). + * + * CAUTION: + * * The changes to @ref mouseSensitivity are delayed one frame. + * * This function will override any pending @ref mouseRequestChangeSensitivity commands. + * * This function has no effect on the Hyperkin clone mouse. + */ +void mouseCycleSensitivity(u16 port); + +/*! + * \brief Queue a cycle mouse sensitivity twice (decrementing the sensitivity) command for the next VBlank. + * + * \param port the port the mouse is connected to (0 or 1). + * + * CAUTION: + * * The changes to @ref mouseSensitivity are delayed one frame. + * * This function will override any pending @ref mouseRequestChangeSensitivity commands. + * * This function has no effect on the Hyperkin clone mouse. + */ +void mouseCycleSensitivityTwice(u16 port); + +/*! + * \brief Queue a set mouse sensitivity command (to be executed on the next VBlank). + * + * \param port the port the mouse is connected to (0 or 1). + * \param sensitivity the sensitivity to set the mouse to (0 - 2). + * + * CAUTION: + * * The changes to @ref mouseSensitivity are delayed one frame. + * * A sensitivity value of 3 is invalid. + * * This function will override any pending @ref mouseRequestChangeSensitivity commands. + * * This function has no effect on the Hyperkin clone mouse. + */ +void mouseSetSensitivity(u16 port, u8 sensitivity); + #endif // SNES_PADS_INCLUDE diff --git a/pvsneslib/source/input.asm b/pvsneslib/source/input.asm index 2a1737e2..d83ffd69 100644 --- a/pvsneslib/source/input.asm +++ b/pvsneslib/source/input.asm @@ -96,26 +96,29 @@ scope_sinceshot dsb 2 .ENDS ;--------------------------------------------------------------------------------- -; Mouse Driver Routine (Ver 1 .00) +; Mouse variables ;--------------------------------------------------------------------------------- .RAMSECTION ".reg_mouse" BANK 0 SLOT 1 -snes_mouse db ; for lib use. Tells the system to initialize mouse usage -mouseConnect dsb 2 ; Mouse connection ports (D0=4016, D0=4017) +snes_mouse db ; Flag to enable mouse reading in VBlank ISR -mouseSpeedSet dsb 2 ; Mouse speed setting -mouse_sp dsb 2 ; Mouse speed +mouseConnect dsb 2 ; Mouse connection status -mouseButton dsb 2 ; Mouse button trigger -mousePressed dsb 2 ; Mouse button turbo +mouseButton dsb 2 ; Mouse buttons pressed this frame +mousePressed dsb 2 ; Mouse buttons held/pressed +mousePreviousPressed dsb 2 ; Mouse buttons held/pressed in the previous frame -mouse_y dsb 2 ; Mouse Y direction -mouse_x dsb 2 ; Mouse X direction +mouse_y dsb 2 ; Mouse Y displacement +mouse_x dsb 2 ; Mouse X displacement -mouse_sb dsb 2 ; Previous switch status +mouseSensitivity dsb 2 ; Mouse sensitivity & sensitivity to set when mouse is connected -connect_st dsb 2 +; Request a sensitivity change in VBlank ISR +; $01 - cycle sensitivity once +; $02..=$7f - cycle sensitivity twice +; $80..=$ff - set sensitivity to `mouseRequestChangeSensitivity & 3` +mouseRequestChangeSensitivity dsb 2 .ENDS @@ -280,129 +283,163 @@ detectSuperScope: .ENDS -; Must be in bank 0, used by _MouseRead in the VBlank ISR. -.SECTION ".mousespeedchange_text" SEMIFREE BANK 0 + +.SECTION ".detectmouse_text" SUPERFREE ;--------------------------------------------------------------------------------- -; void mouseSpeedChange(u8 port) -mouseSpeedChange: +; detectMouse(void) +detectMouse: php - sep #$30 phb - phx - phy - lda #$00 ; Set Data Bank to 0 + sep #$20 + lda #$0 ; change bank address to 0 pha plb +; DB = 0 - lda 8,s ; Set port - tax + ; Wait until the Joypad Auto-Read has finished. + lda.b #1 + - + bit.w REG_HVBJOY + bne - - jsr @speed_change - ply - plx + ; Set `snes_mouse` if JOY1L or JOY2L is a mouse by checking the signature bits. + lda.w REG_JOY1L + and.b #$0f + cmp.b #1 + beq @MouseDetected + + lda.w REG_JOY2L + and.b #$0f + cmp.b #1 + bne @Return + + @MouseDetected: + lda.b #$01 + sta.w snes_mouse + +@Return: plb plp rtl +.ENDS -; Called by _MouseRead in the Vblank ISR -; X = 0 or 1 -; DB = 0 -.ACCU 8 -.INDEX 8 -@speed_change: - php - sep #$30 - lda mouseConnect,x - beq _s25 +.SECTION ".initMouse_text" SUPERFREE - lda #$10 - sta tcc__r0h +; void initMouse(u8 sensitivity); +initMouse: + php -_s10: - lda #$01 - sta REG_JOYA - lda REG_JOYA,x ; Speed change (1 step) - stz REG_JOYA + ; Clear mouse variables + ; Assumes mouse array variables are 2 bytes in size + rep #$20 - lda #$01 ; Read speed data. - sta REG_JOYA ; Shift register clear. - lda #$00 - sta REG_JOYA + stz mouseConnect + stz mouseButton + stz mousePressed + stz mousePreviousPressed + stz mouse_x + stz mouse_y + stz mouseRequestChangeSensitivity - sta mouse_sp,x ; Speed register clear. - ldy #10 ; Shift register read has no meaning + sep #$20 -_s20: - lda REG_JOYA,x - dey - bne _s20 + ; Set initial mouse sensitivity + lda 5,s ; sensitivity + sta mouseSensitivity + 0 + sta mouseSensitivity + 1 - lda REG_JOYA,x ; Read speed + ; Enable mouse reading in the VBlank ISR + lda #1 + sta snes_mouse - lsr a - rol mouse_sp,x + plp + rts - lda REG_JOYA, x +.ENDS - lsr a - rol mouse_sp,x - lda mouse_sp,x - cmp mouseSpeedSet,x ; Set speed or not? +.SECTION ".mouseCycleSensitivity_text" SUPERFREE - beq _s30 +; void mouseCycleSensitivity(u16 port); +mouseCycleSensitivity: + php + rep #$30 + phx - dec tcc__r0h ; For error check - bne _s10 + lda 7,s ; port argument + cmp.w #2 + bcs + + tax + sep #$20 + .accu 8 -_s25: - lda #$80 ; Speed change error. - sta mouse_sp,x + lda.b #1 + sta.l mouseRequestChangeSensitivity,x + + +// A size unknown -_s30: + plx plp - rts - + rtl .ENDS -.SECTION ".detectmouse_text" SUPERFREE +.SECTION ".mouseCycleSensitivityTwice_text" SUPERFREE -;--------------------------------------------------------------------------------- -; detectMouse(void) -detectMouse: +; void mouseCycleSensitivityTwice(u16 port); +mouseCycleSensitivityTwice: php - phb + rep #$30 + phx - sep #$20 - lda #$0 ; change bank address to 0 - pha - plb + lda 7,s ; port argument + cmp.w #2 + bcs + + tax + sep #$20 + .accu 8 --: lda REG_HVBJOY - and.b #$01 - bne - + lda.b #2 + sta.l mouseRequestChangeSensitivity,x + + +// A size unknown - rep #$20 - lda REG_JOY1L - ora REG_JOY2L - and.w #$000F - cmp.w #$0001 ; Is the mouse connected on any port? - bne + + plx + plp + rtl +.ENDS - sep #$20 - lda #$01 - sta snes_mouse -+: - plb +.SECTION ".mouseSetSensitivity_text" SUPERFREE + +; void mouseSetSensitivity(u16 port, u8 sensitivity); +mouseSetSensitivity: + php + rep #$30 + phx + + lda 7,s ; port argument + cmp.w #2 + bcs + + tax + sep #$20 + .accu 8 + + lda 9,s ; sensitivity argument + ora.b #$80 + sta.l mouseRequestChangeSensitivity,x + + +// A size unknown + + plx plp rtl - .ENDS + + diff --git a/pvsneslib/source/vblank.asm b/pvsneslib/source/vblank.asm index 551ecf06..30057cba 100644 --- a/pvsneslib/source/vblank.asm +++ b/pvsneslib/source/vblank.asm @@ -377,28 +377,7 @@ _GetScope: ;--------------------------------------------------------------------------------- - -;* mouse read - -;--------------------------------------------------------------------------------- - -;* If this routine is called every frame, then the mouse status will be set -;* to the appropriate registers. -;* INPUT -;* None (Mouse key read automatically) -;* OUTPUT -;* Connection status (mouse_con) D0=1 Mouse connected to Joyl -;* D1=1 Mouse connected to Joy2 -;* Switch (mousePressed,1) D0=left switch turbo -;* D1=right switch turbo -;* Switch (mouseButton,1) D0=left switch trigger -;* D1=right switch trigger -;* Mouse movement (ball) value -;* (mouse_x) D7=0 Positive turn, D7=1 Negative turn -;* D6-D0 X movement value -;* (mouse_y) D7=0 Positive turn, D7=1 Negative turn -;* D6-D0 X movement value - +; Mouse read ;--------------------------------------------------------------------------------- @@ -418,35 +397,14 @@ _MouseRead: ; The code assumes Joypad Auto-Read is not active. ; This is enforced by a REG_HVBJOY spinloop in the VBlank ISR. - ldx #$01 - lda REG_JOY2L ; Joy2 + ldx #1 + lda REG_JOY2L jsr _MouseData - lda connect_st+1 - beq @_20 - - jsr mouseSpeedChange@speed_change - stz connect_st+1 - -@_20: - dex - lda REG_JOY1L ; Joy1 + ldx #0 + lda REG_JOY1L jsr _MouseData - lda connect_st - beq @_30 - - jsr mouseSpeedChange@speed_change - stz connect_st - -@_30: - - lda mouseConnect - ora mouseConnect+1 - bne + - stz snes_mouse ; Disable mouse flag if no mouse connected - -+: rep #$10 rts @@ -455,72 +413,209 @@ _MouseRead: ;; ;; IN: A = REG_JOY1L or REG_JOY2L ;; IN: X = 0 or 1 +;; KEEP: X ;; ;; DB = 0 ;; D = tcc__registers_nmi_isr (NOT ZERO) .accu 8 .index 8 _MouseData: + tay + + ; Test if a mouse is connected + and.b #$0f + cmp.b #$01 + bne @NoMouseConnected + + ; Test if the mouse was connected on this frame + lda mouseConnect,x + beq @MouseConnectedThisFrame + + + ; Update mouse button/pressed variables + lda mousePressed,x + sta mousePreviousPressed,x + + tya + lsr a + lsr a + lsr a + lsr a + tay + and #3 + sta mouseSensitivity,x + + tya + lsr a + lsr a + sta mousePressed,x + + eor mousePreviousPressed,x + and mousePressed,x + sta mouseButton,x + - sta tcc__r0 ; (421A / 4218 saved to reg0) - and.b #$0F - cmp.b #$01 ; Is the mouse connected? - beq @_m10 + ; Manually read the displacement bits from the controller port. + ; + ; According to https://snes.nesdev.org/wiki/Mouse the Hyperkin mouse had extra timing requirements: + ; * At least 170 master cycles between bit reads + ; * At least 336 master cycles between reading the 2nd and 3rd byte + ; + ; The second requirement is already met. Auto-Joypad read will read the first 16 bits and there + ; is a significant delay between the end of Auto-Joypad read and the start of this read-loop. + ; + ; SlowROM: This loop is 190 m-cycles. No read-delay was required. + ; FastROM: This loop is 188 m-cycles. 2 nop instructions were required. + ; + ; The delays were manually cycle counted and verified with Mesen's Debugger. + + ; Read 16 bits + ldy #16 + - + lda.w REG_JOYA,x + + lsr a + rol.w mouse_x,x + rol.w mouse_y,x + .ifdef FASTROM + nop ; Read delay for hyperkin mouse support. + nop ; 1 extra nop for safety (to match the SnesDev wiki) + .endif + dey + bne - - stz mouseConnect,x ; No connection. + lda.w mouseRequestChangeSensitivity,x + bne @ChangeSensitivityRequest + + rts + + +; The Nintendo Mouse has a bug where the reported sensitivity and the internal sensitivity do +; not match when the mouse is powered on. +; +; To fix this bug at a cycle-sensitivity command must be sent to the mouse when it is first +; connected to the console, even if the sensitivity bits are what the user wants. +; +@MouseConnectedThisFrame: + ; Using `mouseSensitivity` so the sensitivity can be restored if the mouse is + ; disconnected then reconnected to the console. + lda mouseSensitivity,x + and #3 + jsr @SetMouseSensitvity + + ; Set mouse connected flag + lda #1 + sta mouseConnect,x + + ; Clear stale or uninitialised request change sensitivity command. + stz mouseRequestChangeSensitivity,x + + ; Clear mouse variables, they might not be valid. + bra @ClearMouseState + + +@NoMouseConnected: + stz mouseConnect,x + +@ClearMouseState: + ; Not clearing mouseSensitivity. + ; It is used to restore the sensitivity when the mouse is reconnected stz mouseButton,x stz mousePressed,x + stz mousePreviousPressed,x stz mouse_x,x stz mouse_y,x - rts -@_m10: - lda mouseConnect,x ; When mouse is connected, speed will change. - bne @_m20 ; Previous connection status - ; (mouse.com judged by lower 1 bit) - lda #$01 ; Connection check flag on - sta mouseConnect,x - sta connect_st,x + +; A = mouseRequestChangeSensitivity +; negative flag = MSB of `mouseRequestChangeSensitivity` +@ChangeSensitivityRequest: + stz mouseRequestChangeSensitivity,x + + bmi @RequestSpecificSensitivity + + ; Send one or two cycle-sensitivity commands to the mouse + ldy #$01 + sty REG_JOYA + dec a + beq + + ldy REG_JOYA,x + + + ldy REG_JOYA,x + stz REG_JOYA + +@Return: rts -@_m20: - rep #$10 - ldy #16 ; Read 16 bit data. - sep #$10 -@_m30: - lda REG_JOYA,x +; A = mouseRequestChangeSensitivity +@RequestSpecificSensitivity: + and.b #3 + cmp mouseSensitivity,x + beq @Return - lsr a - rol mouse_x,x - rol mouse_y,x - dey - bne @_m30 - stz mousePressed,x +; Repeatedly cycle through the mouse sensitivity until reported sensitivity matches the requested sensitivity. +; CAUTION: This code will always cycle the sensitivity at least once (required when mouse is connected to the console) +; A = requested sensitivity (0 - 2) +@SetMouseSensitvity: + tay - rol tcc__r0 - rol mousePressed,x - rol tcc__r0 - rol mousePressed,x ; Switch turbo + ; Limit the number of cycle-sensitivity commands to send to the mouse. + ; Done for 2 reasons: + ; 1. Prevents an infinite loop if the mouse has been disconnected. + ; 2. The Hyperkin mouse will always report a mouse sensitivity of 0. + lda #4 + sta.b tcc__r0h - lda mousePressed,x - eor mouse_sb,x ; Get switch trigger - bne @_m40 + @CycleLoop: + ; X = port + ; Y = requested sensitivity + ; tcc__r0h = decrementing loop counter - stz mouseButton,x + ; Send a cycle-sensitivity command to the mouse + lda #$01 + sta REG_JOYA + lda REG_JOYA,x + stz REG_JOYA - rts -@_m40: - lda mousePressed,x - sta mouseButton,x - sta mouse_sb,x + ; Read sensitivity bits from mouse + ; + ; No Hyperkin mouse read delay is required as the Hyperkin mouse does not support + ; cycle-sensitivity commands. - rts + ; Skip the first 10 bits + ; Using A for loop counter so Y is unchanged + lda #10 + - + bit REG_JOYA,x + dec a + bne - + + ; Read the 2 sensitivity bits + stz.b tcc__r0 + lda REG_JOYA,x + lsr + rol.b tcc__r0 + + lda REG_JOYA,x + lsr + rol.b tcc__r0 + + + ; Return if read sensitivity == Y + cpy.b tcc__r0 + beq @EndCycleLoop + + dec.b tcc__r0h + bne @CycleLoop + +@EndCycleLoop: + rts ;--------------------------------------------------------------------------------- diff --git a/snes-examples/input/mouse-data-test/Makefile b/snes-examples/input/mouse-data-test/Makefile new file mode 100644 index 00000000..ad731146 --- /dev/null +++ b/snes-examples/input/mouse-data-test/Makefile @@ -0,0 +1,33 @@ +ifeq ($(strip $(PVSNESLIB_HOME)),) +$(error "Please create an environment variable PVSNESLIB_HOME by following this guide: https://github.com/alekmaul/pvsneslib/wiki/Installation") +endif + + +export ROMNAME := mouse-data-test + +FASTROM := 1 + +# Memory map +# (set to 1 to use HIROM) +# This variable MUST MATCH hdr.asm +HIROM := 0 + + +include ${PVSNESLIB_HOME}/devkitsnes/snes_rules + +.PHONY: bitmaps all + +#--------------------------------------------------------------------------------- + +all: bitmaps $(ROMNAME).sfc + +clean: cleanBuildRes cleanRom cleanGfx + +#--------------------------------------------------------------------------------- + +pvsneslibfont.pic: pvsneslibfont.bmp + @echo convert font with no tile reduction ... $(notdir $@) + $(GFXCONV) -s 8 -o 2 -u 4 -p -t bmp -i $< + +bitmaps : pvsneslibfont.pic + diff --git a/snes-examples/input/mouse-data-test/data.asm b/snes-examples/input/mouse-data-test/data.asm new file mode 100644 index 00000000..277e8408 --- /dev/null +++ b/snes-examples/input/mouse-data-test/data.asm @@ -0,0 +1,9 @@ +;; Resources and Data used by the program + +.include "hdr.asm" + +.section "Resources_Font" SUPERFREE + Font_Tiles: .incbin "pvsneslibfont.pic" + Font_Palette: .incbin "pvsneslibfont.pal" +.ends + diff --git a/snes-examples/input/mouse-data-test/hdr.asm b/snes-examples/input/mouse-data-test/hdr.asm new file mode 100644 index 00000000..ef412c06 --- /dev/null +++ b/snes-examples/input/mouse-data-test/hdr.asm @@ -0,0 +1,100 @@ +;; Memory map and SNES header + + +; `tad-audio.asm` needs to know the memory map. +; `.define` either a HIROM or LOROM constant to to select the memory map. +; +; If you change the mapping, you must also change the `HIROM` variable in the makefile. +.define LOROM + + +.if defined(LOROM) + ; LOROM memory mapping + + ; Memory map MUST MATCH PVSnesLib memory map + .memorymap + slotsize $8000 + defaultslot 0 + + slot 0 $8000 + slot 1 $0000 $2000 ; zeropage and lowram access + slot 2 $2000 $e000 ; bank $7e Work-RAM variables + slot 3 $0000 $10000 + .endme + + .rombanksize $8000 + .rombanks 8 + __HDR_ROM_SIZE__ = 8 + + .base $80 + +.elif defined(HIROM) + ; HIROM memory mapping + + ; Memory map MUST MATCH PVSnesLib memory map + .memorymap + slotsize $10000 + defaultslot 0 + + slot 0 $0000 + slot 1 $0000 $2000 ; zeropage and lowram access + slot 2 $2000 $e000 ; bank $7e Work-RAM variables + slot 3 $0000 $10000 + slot 4 $6000 ; SRAM access + .endme + + .rombanksize $10000 + .rombanks 4 + __HDR_ROM_SIZE__ = 8 + + .base $c0 +.else + .fail "Unknown memory map, please .define HIROM or LOROM in hdr.asm" +.endif + + +.snesheader + id " " + + name "MOUSE DATA TEST " ; Program Title. 21 bytes padded with spaces + ; "123456789012345678901" + + .ifdef FASTROM + fastrom + .else + slowrom + .endif + + .ifdef HIROM + hirom + .else + lorom + .endif + + cartridgetype 0 ; ROM only + romsize __HDR_ROM_SIZE__ ; 2^romsize bytes + sramsize 0 ; No SRAM + country 1 ; US + + licenseecode 0 ; blank + + version 0 ; version 1.00 +.endsnes + + +.snesnativevector + cop EmptyHandler + brk EmptyHandler + abort EmptyHandler + nmi VBlank + irq EmptyHandler +.endnativevector + +.snesemuvector + cop EmptyHandler + abort EmptyHandler + nmi EmptyHandler + reset tcc__start + irqbrk EmptyHandler +.endemuvector + diff --git a/snes-examples/input/mouse-data-test/mouse-data-test.c b/snes-examples/input/mouse-data-test/mouse-data-test.c new file mode 100644 index 00000000..bcb18bfe --- /dev/null +++ b/snes-examples/input/mouse-data-test/mouse-data-test.c @@ -0,0 +1,249 @@ +/* + * Mouse data test. + * + * Used to verify the mouse reading code with a Hyperkin mouse. + */ + +#include + +#define JOYPAD_PORT 0 +#define MOUSE_PORT 1 + +#define MAX_XPOS 255 +#define MAX_YPOS 224 + + +#define COL_1_XPOS 18 +#define COL_2_XPOS 26 + +#define LABEL_YPOS 8 + +#define BUTTON_YPOS 8 +#define SPEED_YPOS 10 +#define DELTA_YPOS 12 +#define POS_YPOS 14 +#define LAG_YPOS 18 + +#define CONTOLS_YPOS 22 + + +#define N_LABELS 6 +const char* const LABELS[N_LABELS] = { + "Button:", + "Sensitivity:", + "Displacement:", + "Position:", + "", + "Lag counter:" +}; + +const char* const BUTTON_STRINGS[4] = { + " ", + "LEFT ", + "RIGHT", + "BOTH ", +}; + +const u8 CURSOR_TILE[32] = { + 0b11000000, 0b00000000, + 0b11100000, 0b01000000, + 0b11110000, 0b01100000, + 0b11111000, 0b01110000, + 0b11111100, 0b01111000, + 0b11111000, 0b01100000, + 0b11100000, 0b00000000, + 0b00000000, 0b00000000, +}; + +const u16 CURSOR_PALETTE[4] = { + 0, 0, 0x17e5, 0x17e5 +}; + +#define VRAM_BG3_MAP_WADDR 0x0000 +#define VRAM_BG3_TILE_WADDR 0x1000 +#define VRAM_OBJ_TILE_WADDR 0x6000 + +void printU8(u16 xPos, u16 yPos, u16 value); +void drawMouseDelta(u16 xPos, u16 value); + +extern char Font_Tiles, Font_Palette; + +u16 cursor_xPos = 128; +u16 cursor_yPos = 112; + +int main(void) +{ + u16 i; + + consoleInit(); + + setMode(BG_MODE1, 0); + bgSetDisable(0); + bgSetDisable(1); + + bgSetGfxPtr(2, VRAM_BG3_TILE_WADDR); + bgSetMapPtr(2, VRAM_BG3_MAP_WADDR, SC_32x32); + + oamInitGfxSet((void*)&CURSOR_TILE, sizeof(CURSOR_TILE), (void*)&CURSOR_PALETTE, sizeof(CURSOR_PALETTE), 0, VRAM_OBJ_TILE_WADDR, OBJ_SIZE8_L16); + + consoleSetTextVramBGAdr(VRAM_BG3_MAP_WADDR); + consoleSetTextVramAdr(VRAM_BG3_TILE_WADDR); + consoleSetTextOffset(0x0000); + consoleInitText(0, 4 * 2, &Font_Tiles, &Font_Palette); + + consoleDrawText(2, 2, "MOUSE DATA TEST:"); + + + consoleDrawText(2, CONTOLS_YPOS, "Controls (Joypad %d):", JOYPAD_PORT + 1); + consoleDrawText(5, CONTOLS_YPOS + 1, "Y/X/A - Set mouse speed"); + consoleDrawText(5, CONTOLS_YPOS + 2, " L/R - Cycle mouse speed"); + consoleDrawText(5, CONTOLS_YPOS + 3, " B - Add lag"); + + + for (i = 0; i < N_LABELS; i++) { + consoleDrawText(2, LABEL_YPOS + i * 2, "%s", LABELS[i]); + } + + // Setup the cursor sprite (hard coded) + oamMemory[0] = cursor_xPos; + oamMemory[1] = cursor_yPos; + oamMemory[2] = 0; + oamMemory[3] = 0x30; + oamMemory[512] = 0; // Clear hi table + + // Enable mouse reading and set the initial mouse sensitivity to medium + initMouse(1); + + consoleDrawText(2, 4, "READING MOUSE ON PORT %d", MOUSE_PORT + 1); + + setScreenOn(); + + + while (true) { + WaitForVBlank(); + + if (mouseConnect[MOUSE_PORT] == false) { + while (mouseConnect[MOUSE_PORT] == false) { + consoleDrawText(2, 4, "NO MOUSE ON PORT %d ", MOUSE_PORT + 1); + + WaitForVBlank(); + } + + consoleDrawText(2, 4, "READING MOUSE ON PORT %d", MOUSE_PORT + 1); + WaitForVBlank(); + } + + // Update cursor position + if (mouse_x[MOUSE_PORT] & 0x80) { + cursor_xPos -= mouse_x[MOUSE_PORT] & 0x7e; + if (cursor_xPos >= 0x8000) { + cursor_xPos = 0; + } + } + else { + cursor_xPos += mouse_x[MOUSE_PORT]; + if (cursor_xPos >= MAX_XPOS + 1) { + cursor_xPos = MAX_XPOS; + } + } + + if (mouse_y[MOUSE_PORT] & 0x80) { + cursor_yPos -= mouse_y[MOUSE_PORT] & 0x7e; + if (cursor_yPos >= 0x8000) { + cursor_yPos = 0; + } + } + else { + cursor_yPos += mouse_y[MOUSE_PORT]; + if (cursor_yPos >= MAX_YPOS + 1) { + cursor_yPos = MAX_YPOS; + } + } + + if (padsDown(JOYPAD_PORT) & (KEY_Y | KEY_X | KEY_A | KEY_L | KEY_R)) { + if (padsDown(JOYPAD_PORT) & KEY_Y) { + mouseSetSensitivity(MOUSE_PORT, MOUSE_SLOW); + } + if (padsDown(JOYPAD_PORT) & KEY_X) { + mouseSetSensitivity(MOUSE_PORT, MOUSE_MEDIUM); + } + if (padsDown(JOYPAD_PORT) & KEY_A) { + mouseSetSensitivity(MOUSE_PORT, MOUSE_FAST); + } + if (padsDown(JOYPAD_PORT) & KEY_R) { + mouseCycleSensitivity(MOUSE_PORT); + } + if (padsDown(JOYPAD_PORT) & KEY_L) { + mouseCycleSensitivityTwice(MOUSE_PORT); + } + } + + if (padsCurrent(JOYPAD_PORT) & KEY_B) { + // Add lag + for(i = 0; i < 1500; i++) {} + } + + // Update mouse cursor sprite + oamMemory[0] = cursor_xPos; + oamMemory[1] = cursor_yPos; + + consoleDrawText(COL_1_XPOS, BUTTON_YPOS, BUTTON_STRINGS[mousePressed[MOUSE_PORT] & 3]); + consoleDrawText(COL_2_XPOS, BUTTON_YPOS, BUTTON_STRINGS[mouseButton[MOUSE_PORT] & 3]); + + printU8(COL_1_XPOS, SPEED_YPOS, mouseSensitivity[MOUSE_PORT]); + + drawMouseDelta(COL_1_XPOS, mouse_x[MOUSE_PORT]); + drawMouseDelta(COL_2_XPOS, mouse_y[MOUSE_PORT]); + + printU8(COL_1_XPOS, POS_YPOS, cursor_xPos); + printU8(COL_2_XPOS, POS_YPOS, cursor_yPos); + + consoleDrawText(COL_1_XPOS, LAG_YPOS, "%d", lag_frame_counter); + } + return 0; +} + +// Hack to quickly print a u8 without lag +const char* const U8_STR_TABLE[256] = { + " 0", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", " 10", + " 11", " 12", " 13", " 14", " 15", " 16", " 17", " 18", " 19", " 20", " 21", + " 22", " 23", " 24", " 25", " 26", " 27", " 28", " 29", " 30", " 31", " 32", + " 33", " 34", " 35", " 36", " 37", " 38", " 39", " 40", " 41", " 42", " 43", + " 44", " 45", " 46", " 47", " 48", " 49", " 50", " 51", " 52", " 53", " 54", + " 55", " 56", " 57", " 58", " 59", " 60", " 61", " 62", " 63", " 64", " 65", + " 66", " 67", " 68", " 69", " 70", " 71", " 72", " 73", " 74", " 75", " 76", + " 77", " 78", " 79", " 80", " 81", " 82", " 83", " 84", " 85", " 86", " 87", + " 88", " 89", " 90", " 91", " 92", " 93", " 94", " 95", " 96", " 97", " 98", + " 99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", "120", + "121", "122", "123", "124", "125", "126", "127", "128", "129", "130", "131", + "132", "133", "134", "135", "136", "137", "138", "139", "140", "141", "142", + "143", "144", "145", "146", "147", "148", "149", "150", "151", "152", "153", + "154", "155", "156", "157", "158", "159", "160", "161", "162", "163", "164", + "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175", + "176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", + "187", "188", "189", "190", "191", "192", "193", "194", "195", "196", "197", + "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", "208", + "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", "230", + "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", + "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", + "253", "254", "255" +}; + + +void drawMouseDelta(u16 xPos, u16 value) { + if (value & 0x80) { + consoleDrawText(xPos - 1, DELTA_YPOS, "-"); + } + else { + consoleDrawText(xPos - 1, DELTA_YPOS, "+"); + } + + consoleDrawText(xPos, DELTA_YPOS, U8_STR_TABLE[value & 0x7f]); +} + +void printU8(u16 xPos, u16 yPos, u16 value) { + consoleDrawText(xPos, yPos, U8_STR_TABLE[value & 0xff]); +} + diff --git a/snes-examples/input/mouse-data-test/pvsneslibfont.bmp b/snes-examples/input/mouse-data-test/pvsneslibfont.bmp new file mode 100644 index 00000000..71bc2193 Binary files /dev/null and b/snes-examples/input/mouse-data-test/pvsneslibfont.bmp differ diff --git a/snes-examples/input/mouse/mouse.c b/snes-examples/input/mouse/mouse.c index 01b0b69b..56db0e57 100644 --- a/snes-examples/input/mouse/mouse.c +++ b/snes-examples/input/mouse/mouse.c @@ -10,15 +10,6 @@ ---------------------------------------------------------------------------------*/ #include -#ifndef MOUSE_SPEED -#define MOUSE_SPEED - -#define slow 0 -#define normal 1 -#define fast 2 - -#endif - extern char snesfont, snespal, cursorsprite, cursorsprite_end, cursorpal, buttonsmap, buttonstiles, buttonstiles_end, buttonspal; char hex_string[4]; @@ -35,7 +26,6 @@ bool printed[2] = {false}; bool mouseDown_L[2] = {false}; bool mouseDown_R[2] = {false}; bool mouseDown_LR[2] = {false}; -bool speedset[2] = {true}; //--------------------------------------------------------------------------------- int main(void) @@ -52,12 +42,10 @@ int main(void) // Draw a wonderful text :P consoleDrawText(11, 1, "MOUSE TEST"); - // we set mouse speed, or it will just output a random speed. We can change it later manually - mouseSpeedSet[0] = slow; - mouseSpeedSet[1] = slow; + // Enable mouse reading and set the initial mouse sensitivity + initMouse(MOUSE_SLOW); - detectMouse(); // Let's check if a mouse is plugged in any port on boot, be sure nmi interrupt was called at least once (in this case, previous oamInitGfxSet() function was enough) - WaitForVBlank(); // Let's make sure we read mouse for the first time after detectMouse() + WaitForVBlank(); // Let's make sure we read mouse for the first time after initMouse() if (mouseConnect[0] == false) consoleDrawText(3, 5, "NO MOUSE PLUGGED ON PORT 0"); @@ -99,9 +87,6 @@ int main(void) while (1) { - if (snes_mouse == false) - detectMouse(); // Let's check if a mouse is plugged in any port - odd++; // Optimize Draw text by printing new text just once if (mouseConnect[0] != mc_mem[0]) @@ -290,130 +275,115 @@ int main(void) } } - WaitForVBlank(); // mouseButton works as a one frame value, so it gets released shortly after pressing the button. Good for clicking stuff that need to be called once, like buttons. - if (mouseConnect[0]) + // mouseButton is 0 if there is no mouse connected + if (mouseButton[0] & mouse_L) { - if (mouseButton[0] & mouse_L) + // Let's choose speed setting + if ((p1_mouse_y > 0x5E) && (p1_mouse_y < 0x6C)) { - // Let's choose speed setting - if ((p1_mouse_y > 0x5E) && (p1_mouse_y < 0x6C)) + if ((p1_mouse_x > 0x44) && (p1_mouse_x < 0x64)) { - if ((p1_mouse_x > 0x44) && (p1_mouse_x < 0x64)) - { - mouseSpeedSet[0] = slow; - speedset[0] = true; - mouseSpeedChange(0); // Let's tell the mouse we want to change speed. mouseSpeedSet[] has to be populated first. - } - if ((p1_mouse_x > 0x6C) && (p1_mouse_x < 0x94)) - { - mouseSpeedSet[0] = normal; - speedset[0] = true; - mouseSpeedChange(0); // Let's tell the mouse we want to change speed. mouseSpeedSet[] has to be populated first. - } - if ((p1_mouse_x > 0x9C) && (p1_mouse_x < 0xBC)) - { - mouseSpeedSet[0] = fast; - speedset[0] = true; - mouseSpeedChange(0); // Let's tell the mouse we want to change speed. mouseSpeedSet[] has to be populated first. - } + mouseSetSensitivity(0, MOUSE_SLOW); // Queue a set sensitivity command + } + if ((p1_mouse_x > 0x6C) && (p1_mouse_x < 0x94)) + { + mouseSetSensitivity(0, MOUSE_MEDIUM); // Queue a set sensitivity command + } + if ((p1_mouse_x > 0x9C) && (p1_mouse_x < 0xBC)) + { + mouseSetSensitivity(0, MOUSE_FAST); // Queue a set sensitivity command } } + } - if (speedset[0]) + if (mouseButton[1] & mouse_L) + { + // Let's choose speed setting + if ((p2_mouse_y > 0xBE) && (p2_mouse_y < 0xCC)) { - dmaCopyVram(&buttonsmap + 0x40, 0x6188, 0x20); // released buttons - dmaCopyVram(&buttonsmap + 0x80, 0x61A8, 0x20); // released buttons - - switch (mouseSpeedSet[0]) + if ((p2_mouse_x > 0x44) && (p2_mouse_x < 0x64)) + { + mouseSetSensitivity(1, MOUSE_SLOW); // Queue a set sensitivity command + } + if ((p2_mouse_x > 0x6C) && (p2_mouse_x < 0x94)) + { + mouseSetSensitivity(1, MOUSE_MEDIUM); // Queue a set sensitivity command + } + if ((p2_mouse_x > 0x9C) && (p2_mouse_x < 0xBC)) { - case slow: - dmaCopyVram(&buttonsmap + 0x60, 0x6188, 0x0A); // SLOW button pressed - dmaCopyVram(&buttonsmap + 0xA0, 0x61A8, 0x0A); // SLOW button pressed - break; - case normal: - dmaCopyVram(&buttonsmap + 0x6A, 0x618D, 0x0C); // NORMAL button pressed - dmaCopyVram(&buttonsmap + 0xAA, 0x61AD, 0x0C); // NORMAL button pressed - break; - case fast: - dmaCopyVram(&buttonsmap + 0x76, 0x6193, 0x0A); // FAST button pressed - dmaCopyVram(&buttonsmap + 0xB6, 0x61B3, 0x0A); // FAST button pressed - break; + mouseSetSensitivity(1, MOUSE_FAST); // Queue a set sensitivity command } - speedset[0] = false; + } + } + + WaitForVBlank(); + + // START VBLANK CODE + + if (mouseConnect[0]) + { + dmaCopyVram(&buttonsmap + 0x40, 0x6188, 0x20); // released buttons + dmaCopyVram(&buttonsmap + 0x80, 0x61A8, 0x20); // released buttons + + // Show reported sensitivity + switch (mouseSensitivity[0]) + { + case MOUSE_SLOW: + dmaCopyVram(&buttonsmap + 0x60, 0x6188, 0x0A); // SLOW button pressed + dmaCopyVram(&buttonsmap + 0xA0, 0x61A8, 0x0A); // SLOW button pressed + break; + case MOUSE_MEDIUM: + dmaCopyVram(&buttonsmap + 0x6A, 0x618D, 0x0C); // NORMAL button pressed + dmaCopyVram(&buttonsmap + 0xAA, 0x61AD, 0x0C); // NORMAL button pressed + break; + case MOUSE_FAST: + dmaCopyVram(&buttonsmap + 0x76, 0x6193, 0x0A); // FAST button pressed + dmaCopyVram(&buttonsmap + 0xB6, 0x61B3, 0x0A); // FAST button pressed + break; } if (mousePressed[0] == false) dmaFillVram(&buttonsmap, 0x6940, 0x40); // wipe text } - else if (speedset[0] == false) + else { dmaFillVram(&buttonsmap + 0x40, 0x6188, 0x20); // remove buttons dmaFillVram(&buttonsmap + 0x80, 0x61A8, 0x20); // remove buttons - speedset[0] = true; } if (mouseConnect[1]) { - if (mouseButton[1] & mouse_L) - { - // Let's choose speed setting - if ((p2_mouse_y > 0xBE) && (p2_mouse_y < 0xCC)) - { - if ((p2_mouse_x > 0x44) && (p2_mouse_x < 0x64)) - { - mouseSpeedSet[1] = slow; - speedset[1] = true; - mouseSpeedChange(1); // Let's tell the mouse we want to change speed. mouseSpeedSet[] has to be populated first. - } - if ((p2_mouse_x > 0x6C) && (p2_mouse_x < 0x94)) - { - mouseSpeedSet[1] = normal; - speedset[1] = true; - mouseSpeedChange(1); // Let's tell the mouse we want to change speed. mouseSpeedSet[] has to be populated first. - } - if ((p2_mouse_x > 0x9C) && (p2_mouse_x < 0xBC)) - { - mouseSpeedSet[1] = fast; - speedset[1] = true; - mouseSpeedChange(1); // Let's tell the mouse we want to change speed. mouseSpeedSet[] has to be populated first. - } - } - } + dmaCopyVram(&buttonsmap + 0x40, 0x6308, 0x20); // released buttons + dmaCopyVram(&buttonsmap + 0x80, 0x6328, 0x20); // released buttons - if (speedset[1]) + switch (mouseSensitivity[1]) { - dmaCopyVram(&buttonsmap + 0x40, 0x6308, 0x20); // released buttons - dmaCopyVram(&buttonsmap + 0x80, 0x6328, 0x20); // released buttons - - switch (mouseSpeedSet[1]) - { - case slow: - dmaCopyVram(&buttonsmap + 0x60, 0x6308, 0x0A); // SLOW button pressed - dmaCopyVram(&buttonsmap + 0xA0, 0x6328, 0x0A); // SLOW button pressed - break; - case normal: - dmaCopyVram(&buttonsmap + 0x6A, 0x630D, 0x0C); // NORMAL button pressed - dmaCopyVram(&buttonsmap + 0xAA, 0x632D, 0x0C); // NORMAL button pressed - break; - case fast: - dmaCopyVram(&buttonsmap + 0x76, 0x6313, 0x0A); // FAST button pressed - dmaCopyVram(&buttonsmap + 0xB6, 0x6333, 0x0A); // FAST button pressed - break; - } - speedset[1] = false; + case MOUSE_SLOW: + dmaCopyVram(&buttonsmap + 0x60, 0x6308, 0x0A); // SLOW button pressed + dmaCopyVram(&buttonsmap + 0xA0, 0x6328, 0x0A); // SLOW button pressed + break; + case MOUSE_MEDIUM: + dmaCopyVram(&buttonsmap + 0x6A, 0x630D, 0x0C); // NORMAL button pressed + dmaCopyVram(&buttonsmap + 0xAA, 0x632D, 0x0C); // NORMAL button pressed + break; + case MOUSE_FAST: + dmaCopyVram(&buttonsmap + 0x76, 0x6313, 0x0A); // FAST button pressed + dmaCopyVram(&buttonsmap + 0xB6, 0x6333, 0x0A); // FAST button pressed + break; } if (mousePressed[1] == false) dmaFillVram(&buttonsmap, 0x6AC0, 0x40); // wipe text } - else if (speedset[1] == false) + else { dmaFillVram(&buttonsmap + 0x40, 0x6308, 0x20); // remove buttons dmaFillVram(&buttonsmap + 0x80, 0x6328, 0x20); // remove buttons - speedset[1] = true; } + + // END VBLANK CODE } return 0; -} \ No newline at end of file +} diff --git a/wiki/Input-and-Output.md b/wiki/Input-and-Output.md index 20129b59..27e990e1 100644 --- a/wiki/Input-and-Output.md +++ b/wiki/Input-and-Output.md @@ -178,31 +178,43 @@ At least, the pad is refresh during VBL (thanks to VBlank function), so it is no ### Mouse -**snes_mouse** has to be turned on (snes_mouse = 1). This is set 0 by default after consoleInit(). -This will tell the system to read from a mouse, if it is found. -Mouse and pads can be used simultaneously, on any ports. Externs in the pad.h file goes like this: +**snes_mouse** has to be turned on (`snes_mouse` = 1). This is set to 0 by default after `consoleInit()`. + +You can set `snes_mouse` to 1 by calling `initMouse()` or `detectMouse()`. + * `initMouse()` will also clear the mouse variables and set the initial mouse sensitivity. + * `detectMouse()` will only set `snes_mouse` to 1 if it detects a mouse on one of the console ports. + + +When `snes_mouse` is non-zero, the VBlank ISR will read mouse data from the controller ports +and update the following mouse state variables (the array index specifies the controller port [0 or +1]): ``` -snes_mouse; /*!1 if Mouse is going to be used */ +extern u8 mouseConnect[2]; /*!< \brief 1 if Mouse present */ +extern u8 mouseButton[2]; /*!< \brief Mouse buttons that are pressed on this frame (Click mode). */ +extern u8 mousePressed[2]; /*!< \brief Mouse buttons that are currently pressed, stays until is unpressed (Turbo mode). */ +extern u8 mousePreviousPressed[2];/*!< \brief Mouse buttons held or pressed in the previous frame */ +extern u8 mouse_x[2]; /*!< \brief Mouse horizontal displacement. daaaaaaa, d = direction (1: left, 0: right), a = acceleration. */ +extern u8 mouse_y[2]; /*!< \brief Mouse vertical displacement. daaaaaaa, d = direction (1: up, 0: down), a = acceleration. */ +extern u8 mouseSensitivity[2]; /*!< \brief Mouse sensitivity (0-2) */ ``` -mouseConnect[2]; /* 1 if Mouse present */ -mouseButton[2]; /* 1 if button is pressed, stays for a bit and then it gets released (Click mode). */ -mousePressed[2]; /* 1 if button is pressed, stays until is unpressed (Turbo mode). */ -mouse_x[2], mouse_y[2]; /* Mouse acceleration. daaaaaaa, d = direction (0: up/left, 1: down/right), a = acceleration. */ -mouseSpeedSet[2]; /* Mouse speed setting. 0: slow, 1: normal, 2: fast */ -First, we might use **detectMouse()** to populate snes_mouse to 1, so It can be called at boot and like this: +To use the mouse, we first call **initMouse()** or **detectMouse()** to populate snes_mouse to 1. +It can be called at boot and like this: ``` -if (snes_mouse == false) -{ - detectMouse(); - // some other code you might need in your program, like displaying warning messages and stopping your game. -} +// Enable mouse reading and set the initial mouse sensitivity to medium +initMouse(1); ``` -the number inside array specifies port (0 or 1). I recommend using this code structure to convert raw acceleration into usable values: +Reading the two mouse buttons is done by reading the `mouseButton` or `mousePressed` variables in +the same manner a detecting joypad buttons. `mouseButton` will contain buttons that are newly +pressed on this frame and `mousePressed` contains the buttons that are currently depressed (held +down). + + +I recommend using this code structure to convert raw displacement data into usable values: ``` u16 p1_mouse_x = 0x80; @@ -210,17 +222,40 @@ u16 p1_mouse_y = 0x70; u16 p2_mouse_x = 0x80; u16 p2_mouse_y = 0x70; - if (mouse_x[0] & 0x80) +if (mouse_x[0] & 0x80) p1_mouse_x -= mouse_x[0] & 0x7F; else - p1_mouse_x += mouse_x[0] & 0x7F; + p1_mouse_x += mouse_x[0]; + if (mouse_y[0] & 0x80) p1_mouse_y -= mouse_y[0] & 0x7F; else - p1_mouse_y += mouse_y[0] & 0x7F; + p1_mouse_y += mouse_y[0]; ``` -And that's most of it. You can look inside the example file (**snes-examples/input** folder) to have an idea of how you can program Mouse games. +To get a better idea on how to read the mouse, please see the following examples: + * `snes-examples/input/mouse`: A mouse demo that can read 2 mice at the same time. + * `snes-examples/input/mouse-data-test`: Prints the mouse data from controller port 2 to the user. + + +#### Sensitivity + +The Nintendo mouse has 3 sensitivity settings (0=low, 1=medium, 2=high) that can be adjusted by +sending cycle-sensitivity commands to the mouse through the controller port. + +The Hyperkin clone mouse will always report 0 sensitity and will ignore cycle sensitivity commands. + +The `u8 mouseRequestChangeSensitivity[2]` variable is used to signal to the VBlank ISR that the +MainLoop wants to change the Nintendo Mouse's sensitivity setting. Cycling the sensitity is done in +the VBlank ISR to prevent a timing conflict with the SNES's Automatic Joypad read. + +To adjust the mouse sensitity, either modify `mouseRequestChangeSensitivity` (see `input.h`) or call +one of these functions: + * `void mouseCycleSensitivity(u16 port);` - cycles the mouse sensitivity once (incrementing the sensitivity) + * `void mouseCycleSensitivityTwice(u16 port);` - cycles the mouse sensitity twice (decrementing the sensitivity) + * `void mouseSetSensitivity(u16 port, u8 sensitivity);` - changes the mouse sensitivity to `sensitivity & 3`. (A sensitivity value of 3 is invalid) + +Please be aware that the reported sensitivity in the `mouseSensitivity` variable will be delayed one frame. ### SuperScope