Skip to content

Commit

Permalink
GameCube joysticks scaling
Browse files Browse the repository at this point in the history
Fixes #7
GameCube joysticks range differs from the N64. N64 has a maximum of ~84 along the axes origin
and ~71 in the diagonals. GameCube main joystick has a maximum (once dead zone & sign applied)
of ~90 and ~65 for the same. C joystick maximums are a bit lower at ~82 and ~57. N64 max value
once plot will give us a square-ish equilateral hexagon while GameCube is an equiangular &
equilateral hexagon.

This may be problematic in some games where the diagonal max speed could be lower. In other games
the joystick may feel too sensitive since the main GC joystick output values are significantly
higher along the origin.

This feature uses the opposite axis as a reference to determine dynamically the scaling value
required. The scaling values are stored in a table using the fixed point format of 1.7. Once
multiplied by the joystick value (in 8.0 format) this gives us in our hardware multiplier a
value in the fixed point 9.7 format.

The D-pad LEFT submenu can disable this function (X option) as it may be beneficial in some case
to use the previous behavior. (Default enabled)
  • Loading branch information
darthcloud committed Feb 6, 2018
1 parent 82d0cf1 commit 1a94707
Show file tree
Hide file tree
Showing 9 changed files with 440 additions and 26 deletions.
107 changes: 84 additions & 23 deletions firmware/cube64.asm
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pll_startup_delay macro
remap_dest_button
ctrl_slot_status
target_slot_status
active_key_map
nv_flags
temp_key_map
crc_work

Expand All @@ -123,8 +123,9 @@ pll_startup_delay macro
n64_crc

n64_id_buffer:3
gamecube_buffer:8
n64_status_buffer:4
gamecube_buffer:6 ; Last 2 bytes for gc_buffer are within the first 2
gamecube_scale:8 ; bytes of gc_scale as it allows using the same macro
n64_status_buffer:4 ; for both buffers.
endc

;; *******************************************************************************
Expand All @@ -150,7 +151,7 @@ startup
clrf menu_flags
clrf calibration_count
clrf FSR1H ; We only need to access first bank, so we set it in FSR high byte right away.
clrf active_key_map
clrf nv_flags

;; Set controller id to occupied slot.
movlw 0x01
Expand Down Expand Up @@ -371,9 +372,40 @@ negative_axis_value
next
endm

;; GameCube joysticks range differs from the N64. N64 has a maximum of ~84 along the axes origin
;; and ~71 in the diagonals. GameCube main joystick has a maximum (once dead zone & sign applied)
;; of ~90 and ~65 for the same. C joystick maximums are a bit lower at ~82 and ~57. N64 max value
;; once plot will give us a square-ish equilateral hexagon while GameCube is an equiangular &
;; equilateral hexagon.
;; This function uses the opposite axis as a reference to determine dynamically the scaling value
;; required. The scaling values are stored in a table using the fixed point format of 1.7. Once
;; multiplied by the joystick value (in 8.0 format) this gives us in our hardware multiplier a
;; value in the fixed point 9.7 format.
apply_js_scale macro table, axis_byte, ref_byte
local set_scale_buffer
movf gamecube_buffer + axis_byte, w
btfsc NV_FLAG_SCALING_OFF ; Bypass scaling if flag set.
bra set_scale_buffer

movlw high table
movwf TBLPTRH ; Load the right scaling table.
movff gamecube_buffer + ref_byte, TBLPTRL
TBLRD*
movf TABLAT, w
mulwf gamecube_buffer + axis_byte

btfsc gamecube_buffer + axis_byte, 7
subwf PRODH, f ; If negative stick value, fixup high byte.

rlcf PRODL, w
rlcf PRODH, w ; We need an 8.0 value, so shift left our 9.7 value and copy the high byte.
set_scale_buffer
movwf gamecube_scale + axis_byte
endm

;; Map a GameCube axis to a virtual button, and eventually to an N64 axis or button.
map_axis_from macro src_byte, virtual
movff gamecube_buffer + src_byte, temp2
movff gamecube_scale + src_byte, temp2
movlw virtual
if virtual & 0x01 ; Check direction (sign) of the virtual button.
btfsc temp2, 7 ; Virtual button is negative, skip if buffer positive.
Expand Down Expand Up @@ -450,7 +482,12 @@ next
;; Copy status from the GameCube buffer to the N64 buffer. This first
;; stage maps all axes, and maps GameCube buttons to virtual buttons.
n64_translate_status
movff active_key_map, temp_key_map
movlw upper gamecube_js_scale ; Preload scale table upper address bytes.
movwf TBLPTRU

movf nv_flags, w
andlw LAYOUT_MASK
movwf temp_key_map
call check_calibration_combo

apply_calibration GC_JOYSTICK_X, joystick_x_calibration
Expand All @@ -460,18 +497,25 @@ n64_translate_status
apply_calibration GC_L_ANALOG, left_calibration
apply_calibration GC_R_ANALOG, right_calibration

bcf STATUS, C ; Divide by 2 to fit N64 positive axis values.
rrcf gamecube_buffer + GC_L_ANALOG, f
bcf STATUS, C
rrcf gamecube_buffer + GC_R_ANALOG, f

apply_sign_deadzone GC_JOYSTICK_X, 1
apply_sign_deadzone GC_JOYSTICK_Y, 1
apply_sign_deadzone GC_CSTICK_X, 1
apply_sign_deadzone GC_CSTICK_Y, 1
apply_sign_deadzone GC_L_ANALOG, 0
apply_sign_deadzone GC_R_ANALOG, 0

bcf STATUS, C ; Divide by 2 to fit N64 positive axis values. Close enough to the proper range.
rrcf gamecube_buffer + GC_L_ANALOG, w
movwf gamecube_scale + GC_L_ANALOG
bcf STATUS, C
rrcf gamecube_buffer + GC_R_ANALOG, w
movwf gamecube_scale + GC_R_ANALOG

apply_js_scale gamecube_js_scale, GC_JOYSTICK_X, GC_JOYSTICK_Y
apply_js_scale gamecube_js_scale, GC_JOYSTICK_Y, GC_JOYSTICK_X
apply_js_scale gamecube_cs_scale, GC_CSTICK_X, GC_CSTICK_Y
apply_js_scale gamecube_cs_scale, GC_CSTICK_Y, GC_CSTICK_X

call check_remap_combo ; Must be after calibration, since it uses analog L and R values

;; Restart here if layout modifier button is pressed.
Expand Down Expand Up @@ -649,9 +693,9 @@ check_remap_combo
;; Key combinations require that the L and R buttons be mostly pressed.
;; but that the end stop buttons aren't pressed.
;; Ensure the high bit of each axis is set and that the buttons are cleared.
btfss gamecube_buffer + GC_L_ANALOG, 6
btfss gamecube_buffer + GC_L_ANALOG, 7
return
btfss gamecube_buffer + GC_R_ANALOG, 6
btfss gamecube_buffer + GC_R_ANALOG, 7
return
btfsc gamecube_buffer + GC_L
return
Expand Down Expand Up @@ -782,7 +826,8 @@ save_mapping
movf remap_source_button, w
mullw EEPROM_BTN_BYTE ; Offset base on how many bytes per button.
movff PRODL, EEADR
movf active_key_map, w ; Add offset to EEPROM address to read the right custom buttons layout.
movf nv_flags, w ; Add offset to EEPROM address to read the right custom buttons layout.
andlw LAYOUT_MASK
mullw EEPROM_LAYOUT_SIZE
movf PRODL, w
addwf EEADR, f
Expand Down Expand Up @@ -816,13 +861,24 @@ accept_layout_select
bsf FLAG_WAITING_FOR_RELEASE
bcf FLAG_LAYOUT_SUBMENU
andlw ~LAYOUT_MASK ; Check for any D-pad direction.
bnz check_js_toggle

movlw ~LAYOUT_MASK
andwf nv_flags, f
movf remap_source_button, w
iorwf nv_flags, f
bra write_nv_flags

check_js_toggle
movf remap_source_button, w
xorlw BTN_X
btfss STATUS, Z
return
btg NV_FLAG_SCALING_OFF

movf remap_source_button, w
movwf active_key_map
movwf EEDATA
movlw EEPROM_LAST_KEY_MAP
write_nv_flags
movff nv_flags, EEDATA
movlw EEPROM_NV_FLAGS
movwf EEADR
call eewrite
goto start_rumble_feedback
Expand All @@ -841,9 +897,9 @@ validate_eeprom
btfss STATUS, Z
goto reset_eeprom

movlw EEPROM_LAST_KEY_MAP ; Load last used custom layout.
movlw EEPROM_NV_FLAGS ; Load last used custom layout.
call eeread
movwf active_key_map
movwf nv_flags
return

;; Write an identity mapping and a valid magic word to the EEPROM.
Expand Down Expand Up @@ -876,7 +932,7 @@ next_eeprom_bank
movwf EEDATA
call eewrite

movlw EEPROM_LAST_KEY_MAP ; Init default layout in EEPROM.
movlw EEPROM_NV_FLAGS ; Init default layout in EEPROM.
movwf EEADR
movlw 0x00
movwf EEDATA
Expand All @@ -890,7 +946,8 @@ reset_active_eeprom_layout
movwf TBLPTRH
clrf TBLPTRL

movf active_key_map, w
movf nv_flags, w
andlw LAYOUT_MASK
mullw EEPROM_LAYOUT_SIZE
movff PRODL, EEADR
TBLRD*
Expand Down Expand Up @@ -1397,10 +1454,14 @@ gamecube_rx

;; This contain the default configuration used if the EEPROM is empty, corrupt or
;; user perform reset.
org 0x3E00
org 0x3C00
eeprom_default
#include eeprom_default.inc

;; This contains the scaling tables used to scale our GC joysticks values.
org 0x3D00
#include js_scale.inc

;; 256-byte table extracted from the test vectors, that can be used to
;; compute any CRC. This table is the inverted CRC generated for every
;; possible message with exactly one bit set.
Expand Down
9 changes: 6 additions & 3 deletions firmware/cube64.inc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
;; Set when we want to remap an analog trigger.
#define FLAG_TRIGGER menu_flags, 6

;; Set when we want to disable GC joysticks scaling.
#define NV_FLAG_SCALING_OFF nv_flags, 2

;; Button IDs. These are a superset of the GameCube and N64, without any correspondence
;; with the wire protocol used by either. They're used as intermediate values when
;; translating from GameCube to N64. These IDs are used as indices into the remapping
Expand Down Expand Up @@ -130,7 +133,7 @@
#define EEPROM_LAYOUT_1 EEPROM_LAYOUT_0 + EEPROM_LAYOUT_SIZE
#define EEPROM_LAYOUT_2 EEPROM_LAYOUT_1 + EEPROM_LAYOUT_SIZE
#define EEPROM_LAYOUT_3 EEPROM_LAYOUT_2 + EEPROM_LAYOUT_SIZE
#define EEPROM_LAST_KEY_MAP EEPROM_LAYOUT_3 + EEPROM_LAYOUT_SIZE
#define EEPROM_NV_FLAGS EEPROM_LAYOUT_3 + EEPROM_LAYOUT_SIZE

;; Magic word and the address it should be in the EEPROM,
;; as a big-endian 16-bit value.
Expand All @@ -141,8 +144,8 @@
;; Change this value if the EEPROM data format changes.
;;

#define EEPROM_MAGIC_WORD 0xAC0F
#define EEPROM_MAGIC_ADDR EEPROM_LAST_KEY_MAP + 1
#define EEPROM_MAGIC_WORD 0xEA5C
#define EEPROM_MAGIC_ADDR EEPROM_NV_FLAGS + 1

;; Convert virtual button in w to eeprom byte address in w.
eeprom_btn_addr macro layout, byte
Expand Down
9 changes: 9 additions & 0 deletions firmware/js_scale.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
;; 256 bytes table for scaling GC joysticks.
;; Generated by Jacques Gagnon <darthcloud@gmail.com>.
;; See notes/tbd.py

gamecube_js_scale
db 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8A, 0x89, 0x88, 0x87, 0x87, 0x86, 0x85, 0x84, 0x83, 0x83, 0x82, 0x81, 0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x7A, 0x79, 0x78, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8A, 0x8A, 0x8A, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A, 0x79, 0x79, 0x79, 0x78, 0x78, 0x78, 0x77
gamecube_cs_scale
db 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8B, 0x8B, 0x8C, 0x8C, 0x8D, 0x8D, 0x8E, 0x8E, 0x8F, 0x8F, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x97, 0x97, 0x98, 0x98, 0x99, 0x99, 0x9A, 0x9A, 0x9B, 0x9B, 0x9C, 0x9C, 0x9D, 0x9D, 0x9E, 0x9D, 0x9B, 0x9A, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x90, 0x8F, 0x8E, 0x8D, 0x8C, 0x8B, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x84, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x84, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9D, 0x9E, 0x9D, 0x9D, 0x9C, 0x9C, 0x9B, 0x9B, 0x9A, 0x9A, 0x99, 0x99, 0x98, 0x98, 0x97, 0x97, 0x96, 0x96, 0x95, 0x95, 0x95, 0x94, 0x94, 0x93, 0x93, 0x92, 0x92, 0x91, 0x91, 0x90, 0x90, 0x8F, 0x8F, 0x8E, 0x8E, 0x8D, 0x8D, 0x8C, 0x8C, 0x8B, 0x8B, 0x8A, 0x8A, 0x89, 0x89, 0x89, 0x88, 0x88, 0x87, 0x87, 0x86, 0x86, 0x85, 0x85, 0x84, 0x84, 0x83, 0x83

Loading

0 comments on commit 1a94707

Please sign in to comment.