All SDK docs

Input

Reading the buttons and joysticks, digital versus analog sticks, and what the console's reset button does in an SDK game.

The API

At the game-library level:

int  vpy_j1_x(void);            /* -127..127 */
int  vpy_j1_y(void);            /* -127..127, + = up */
int  vpy_j1_button(int n);      /* n = 1..4 -> 0/1 */
void vpy_update_buttons(void);  /* force a re-read mid-frame */

At the portable PiTrex-style level:

v_readButtons();            /* -> currentButtonState, bit n-1 = button n */
v_readJoystick1Analog();    /* -> currentJoy1X, currentJoy1Y (-127..127) */

And the raw SDK calls, if you need both controllers:

uint8_t  uvm2_read_buttons(void);   /* RAW: active-low, J1 in bits 0-3, J2 in 4-7 */
uint32_t uvm2_read_axes(void);      /* four packed int8: j1x, j1y, j2x, j2y */
void     uvm2_input_set_analog(int enable);

vpy_run() samples the input before each call to your loop(), so in most games you just read the values.


Reads happen between frames

A read needs the data bus turned around in the middle of a cycle, so it cannot be recorded into the command list. Input is read directly, between frames, while /ZERO holds the beam clamped at the centre. Under dual core this happens on core 1, and the game's calls answer from a cache. Reading costs the game nothing and never fights the beam.

Even between frames, reads aren't free for the picture: removing the input read entirely dropped stray bright vectors from 4–5 per frame to 1. That's why /RAMP is held off through the whole conversion.


Digital or analog

Every axis is read with successive approximation, the same algorithm as the BIOS's Joy_Analog, starting from the sign bit. The stick's rest position is not zero (one console measured X 4..12, Y 31..48), so the first reading is taken as the centre and subtracted from every later one.

uvm2_input_set_analog() picks what the game sees:

modevaluesuse it for
digital (default)-127, 0 or +127, past half the travelordinary if (x > 32) game code, which then behaves as it does on a real cartridge
analogthe centred value, 0 inside a small dead zonesteering, aiming, anything proportional

The console's reset button

Every image built with the SDK watches the Vectrex's own reset button:

helddoes
under 1 snothing; brushing against the button costs nothing
1 to 3 s, then releasedthe game restarts from scratch
3 sback to the UVMC2's menu, at once

The reset line isn't wired to the cartridge edge, but a VIA in reset stops its timers. Between frames, core 1 reads Timer 1 twice, 300 bus cycles apart; if it hasn't moved, the button is down. While it's held, and for two frames after, the controllers aren't read: a VIA in reset reads back zeros, which on active-low buttons means "all pressed".

A restart boots the image still in SRAM again. Initial values in .data are saved at startup and restored before the reboot. If .data is larger than 4 KB the restart is refused and counted in uvm2_restart_refused.

For debugging over SWD, uvm2_reset_seen counts presses noticed and uvm2_reset_held_us is how long the current one has lasted, so "it never reboots" can be told apart from "it never saw the button".

Debug Cart: this reset handling doesn't apply. The Debug Cart's BIOS has its own way back to its menu: hold all four buttons for a second.


Reserved button combinations

A few combinations are taken by the SDK. Avoid giving them meaning in your game:

combinationwheredoes
buttons 2 + 3 held while launchingany SDK gameopens the calibration screen
buttons 1 + 4 held for 2 sany .um2 gametoggles the diagnostics HUD (see Debugging)
buttons 3 + 4Debug Cart BIOSdumps the current command list to the SD card
all four, 1 sDebug Cart BIOSback to the BIOS menu