307 lines
8.7 KiB
Text
307 lines
8.7 KiB
Text
|
|
#IFNDEF __lib_mouse
|
|
#DEFINE __lib_mouse 1
|
|
|
|
GOTO __skip_lib_mouse
|
|
|
|
// ============================================================================
|
|
// COMMODORE 1351 MOUSE DRIVER FOR C64
|
|
// ============================================================================
|
|
// This library provides mouse input reading for the Commodore 1351 mouse.
|
|
//
|
|
// The 1351 uses the SID chip's POT lines to report relative movement.
|
|
// The SID hardware automatically samples at ~2kHz, but software should
|
|
// read once per frame (50-60Hz) to minimize CPU usage.
|
|
//
|
|
// Mouse is read from CONTROL PORT 1 (POT lines + buttons)
|
|
// This allows simultaneous use with joystick on CONTROL PORT 2
|
|
//
|
|
// Based on implementations from C64 OS and cc65.
|
|
//
|
|
// FAST MOVEMENT SMOOTHING:
|
|
// By default, directional momentum smoothing is enabled to fix the 1351's
|
|
// known issue where fast movements can register in the opposite direction.
|
|
// To disable smoothing (saves CPU cycles), define MOUSE_NO_SMOOTHING:
|
|
// #DEFINE MOUSE_NO_SMOOTHING 1
|
|
// #INCLUDE "mouse.c65"
|
|
//
|
|
// Usage:
|
|
// #INCLUDE "mouse.c65"
|
|
// mouse_init()
|
|
// WHILE 1
|
|
// mouse_read() // Call once per frame
|
|
// pointer_update_mouse(mouse_delta_x, mouse_delta_y)
|
|
// WEND
|
|
// ============================================================================
|
|
|
|
// SID POT line addresses
|
|
WORD CONST SID_BASE = $D400
|
|
BYTE pot_x @ SID_BASE+$19 // SID+$19: POT X (analog input)
|
|
BYTE pot_y @ SID_BASE+$1A // SID+$1A: POT Y (analog input)
|
|
|
|
// CIA port for button reading (same as joystick port 1)
|
|
WORD CONST CIA1_BASE = $DC00
|
|
BYTE cia_port1 @ CIA1_BASE+1 // $DC01: Port 1 (mouse buttons)
|
|
|
|
// Mouse state variables
|
|
BYTE mouse_pot_x_old // Previous POT X reading
|
|
BYTE mouse_pot_y_old // Previous POT Y reading
|
|
BYTE mouse_delta_x // Signed X movement delta
|
|
BYTE mouse_delta_y // Signed Y movement delta
|
|
BYTE mouse_buttons // Button state (bit 4=right, bit 0=left)
|
|
|
|
#IFNDEF MOUSE_NO_SMOOTHING
|
|
// Directional momentum tracking (for fast movement disambiguation)
|
|
// 0=no momentum, 1-127=rightward, 128-255=leftward (unsigned byte as signed)
|
|
// Define MOUSE_NO_SMOOTHING before including this library to disable smoothing
|
|
BYTE mouse_momentum_x
|
|
BYTE mouse_momentum_y
|
|
|
|
|
|
// ============================================================================
|
|
// FUNC mouse_apply_momentum
|
|
// Apply directional momentum to disambiguate fast movements
|
|
// When delta magnitude is large (ambiguous), use momentum to determine
|
|
// if we should flip the interpretation (±64 in 6-bit space)
|
|
// ============================================================================
|
|
FUNC mouse_apply_momentum({BYTE delta} {BYTE momentum} out:{BYTE corrected_delta} out:{BYTE new_momentum})
|
|
BYTE abs_delta
|
|
BYTE is_negative
|
|
BYTE is_ambiguous
|
|
BYTE was_corrected
|
|
|
|
// Get absolute value and sign of delta
|
|
is_negative = delta & $80
|
|
IF is_negative
|
|
abs_delta = 0 - delta
|
|
ELSE
|
|
abs_delta = delta
|
|
ENDIF
|
|
|
|
// Check if delta is ambiguous (magnitude >= 12 after division by 2 in convert function)
|
|
// In raw 6-bit space, deltas near ±32 become ±16 after /2, which is ambiguous
|
|
is_ambiguous = 0
|
|
was_corrected = 0
|
|
#PRAGMA _P_USE_LONG_JUMP 1
|
|
IF abs_delta >= 12
|
|
#PRAGMA _P_USE_LONG_JUMP 0
|
|
is_ambiguous = 1
|
|
// Ambiguous - check if flipping (negating) would match momentum better
|
|
BYTE flipped_delta
|
|
BYTE flipped_negative
|
|
|
|
// To flip interpretation, just negate the delta
|
|
flipped_delta = abs_delta
|
|
IF is_negative
|
|
// Delta is negative, flipped would be positive
|
|
flipped_negative = 0
|
|
ELSE
|
|
// Delta is positive, flipped would be negative
|
|
flipped_negative = 1
|
|
ENDIF
|
|
|
|
// Use momentum to decide: if momentum sign matches flipped, use flipped
|
|
IF momentum != 0
|
|
BYTE momentum_is_left
|
|
BYTE signs_match
|
|
|
|
momentum_is_left = 0
|
|
IF momentum >= 128
|
|
momentum_is_left = 1
|
|
ENDIF
|
|
|
|
signs_match = 0
|
|
IF momentum_is_left == flipped_negative
|
|
signs_match = 1
|
|
ENDIF
|
|
|
|
IF signs_match == 1
|
|
// Momentum suggests flipped interpretation
|
|
IF flipped_negative
|
|
corrected_delta = 0 - flipped_delta
|
|
ELSE
|
|
corrected_delta = flipped_delta
|
|
ENDIF
|
|
was_corrected = 1
|
|
ELSE
|
|
// Keep original
|
|
corrected_delta = delta
|
|
was_corrected = 1
|
|
ENDIF
|
|
ELSE
|
|
// No momentum yet - ignore this ambiguous delta, return 0
|
|
corrected_delta = 0
|
|
ENDIF
|
|
ELSE
|
|
// Unambiguous, use as-is
|
|
corrected_delta = delta
|
|
ENDIF
|
|
|
|
// Update momentum based on corrected delta
|
|
// Only update from unambiguous deltas to avoid bootstrapping in wrong direction
|
|
#PRAGMA _P_USE_LONG_JUMP 1
|
|
IF is_ambiguous == 0
|
|
#PRAGMA _P_USE_LONG_JUMP 0
|
|
IF corrected_delta == 0
|
|
// No movement - decay momentum toward zero
|
|
IF momentum > 0
|
|
IF momentum < 128
|
|
IF momentum > 2
|
|
momentum = momentum - 2
|
|
ELSE
|
|
momentum = 0
|
|
ENDIF
|
|
ELSE
|
|
IF momentum < 253
|
|
momentum = momentum + 2
|
|
ELSE
|
|
momentum = 0
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
ELSE
|
|
// Add corrected delta to momentum (clamped)
|
|
BYTE corrected_negative
|
|
corrected_negative = corrected_delta & $80
|
|
|
|
IF corrected_negative
|
|
// Moving left - push momentum toward 128-255
|
|
IF momentum < 128
|
|
momentum = 128
|
|
ELSE
|
|
IF momentum < 240
|
|
momentum = momentum + 8
|
|
ENDIF
|
|
ENDIF
|
|
ELSE
|
|
// Moving right - push momentum toward 1-127
|
|
IF momentum >= 128
|
|
momentum = 127
|
|
ELSE
|
|
IF momentum < 120
|
|
momentum = momentum + 8
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
new_momentum = momentum
|
|
FEND
|
|
#IFEND
|
|
|
|
|
|
// ============================================================================
|
|
// FUNC mouse_convert_pot_delta
|
|
// Convert 6-bit POT delta to 8-bit signed delta
|
|
// The 1351 outputs 6-bit signed values in bits 1-6 (bits 0,7 are noise)
|
|
// This converts them to standard 8-bit signed values (-128 to +127)
|
|
// ============================================================================
|
|
FUNC mouse_convert_pot_delta({BYTE new_val} {BYTE old_val} out:{BYTE delta})
|
|
BYTE diff
|
|
BYTE sign_bit
|
|
|
|
// Calculate raw difference (wraps at 64 due to 6-bit counter)
|
|
diff = new_val - old_val
|
|
|
|
// Mask to 7 bits (ignore bit 7 noise)
|
|
diff = diff & %01111111
|
|
|
|
// Check if value is negative (bit 6 set means >= 64 in 6-bit space)
|
|
sign_bit = diff & %01000000
|
|
|
|
IF sign_bit
|
|
// Negative value: set high bits and divide by 2
|
|
diff = diff | %11000000 // Sign extend
|
|
ASM
|
|
lda |diff|
|
|
cmp #$FF
|
|
beq @zero
|
|
sec
|
|
ror // Arithmetic shift right (preserves sign)
|
|
sta |diff|
|
|
jmp @done
|
|
@zero:
|
|
lda #0
|
|
sta |diff|
|
|
@done:
|
|
ENDASM
|
|
delta = diff
|
|
ELSE
|
|
// Positive value: just divide by 2
|
|
ASM
|
|
lda |diff|
|
|
beq @zero2
|
|
lsr // Logical shift right
|
|
sta |diff|
|
|
jmp @done2
|
|
@zero2:
|
|
lda #0
|
|
sta |diff|
|
|
@done2:
|
|
ENDASM
|
|
delta = diff
|
|
ENDIF
|
|
FEND
|
|
|
|
|
|
// ============================================================================
|
|
// FUNC mouse_init
|
|
// Initialize mouse driver - call once at program start
|
|
// ============================================================================
|
|
FUNC mouse_init
|
|
// Read initial pot values
|
|
mouse_pot_x_old = pot_x
|
|
mouse_pot_y_old = pot_y
|
|
mouse_delta_x = 0
|
|
mouse_delta_y = 0
|
|
mouse_buttons = 0
|
|
#IFNDEF MOUSE_NO_SMOOTHING
|
|
mouse_momentum_x = 0
|
|
mouse_momentum_y = 0
|
|
#IFEND
|
|
FEND
|
|
|
|
|
|
// ============================================================================
|
|
// FUNC mouse_read
|
|
// Read mouse movement and button state
|
|
// Call this every frame to update mouse state
|
|
// Updates mouse_delta_x, mouse_delta_y (signed 8-bit deltas)
|
|
// Applies directional momentum to fix fast movement bugs
|
|
// ============================================================================
|
|
FUNC mouse_read
|
|
BYTE pot_x_new
|
|
BYTE pot_y_new
|
|
|
|
// Read current POT values
|
|
pot_x_new = pot_x
|
|
pot_y_new = pot_y
|
|
|
|
// Calculate X delta
|
|
mouse_convert_pot_delta(pot_x_new, mouse_pot_x_old, mouse_delta_x)
|
|
mouse_pot_x_old = pot_x_new
|
|
|
|
#IFNDEF MOUSE_NO_SMOOTHING
|
|
// Apply momentum correction to X
|
|
mouse_apply_momentum(mouse_delta_x, mouse_momentum_x, mouse_delta_x, mouse_momentum_x)
|
|
#IFEND
|
|
|
|
// Calculate Y delta (invert for correct screen movement)
|
|
mouse_convert_pot_delta(pot_y_new, mouse_pot_y_old, mouse_delta_y)
|
|
mouse_delta_y = 0 - mouse_delta_y // Negate Y
|
|
mouse_pot_y_old = pot_y_new
|
|
|
|
#IFNDEF MOUSE_NO_SMOOTHING
|
|
// Apply momentum correction to Y
|
|
mouse_apply_momentum(mouse_delta_y, mouse_momentum_y, mouse_delta_y, mouse_momentum_y)
|
|
#IFEND
|
|
|
|
// Read buttons from CIA port
|
|
mouse_buttons = cia_port1
|
|
FEND
|
|
|
|
|
|
LABEL __skip_lib_mouse
|
|
|
|
#IFEND
|