Input

#include "libtini/input.h"

This header gives key events and the held key from the //e keyboard, the three Apple buttons, raw paddle values and calibrated joystick directions.

FunctionPurpose
ti_input_pollMove a waiting keypress into the key queue
ti_input_keyGet the oldest character in the queue
ti_input_heldGet the key that is held down, for movement while a key is held
ti_input_buttonsGet the three button inputs
ti_input_resetEmpty the key queue
ti_joystick_readGet the raw values of paddle 0, paddle 1 and the buttons
ti_stick_read, ti_stick_recalibrateGet up, down, left and right from a calibrated stick
ti_mouse_readReserved; always unavailable

The keyboard

The //e keyboard gives one character at a time through its latch at $C000. It does not give a matrix of keys. Thus the library cannot tell which keys are held when there are several. Characters are 7-bit ASCII. The keyboard and the buttons need no ti_init and no audio.

KeyCodeKeyCode
Left arrow8Return13
Right arrow21Escape27
Up arrow11Tab9
Down arrow10Delete127

ti_input_poll

ti_error ti_input_poll(void);

When a key waits in the latch, this function clears the keyboard strobe and adds the character to an eight-character queue. Call it one time in each loop.

Returns

TI_OK, or TI_E_NOMEM when the queue is full. The key is then lost.

ti_input_key

ti_error ti_input_key(uint8_t *character);

This function removes the oldest character from the queue and puts it in *character.

Returns

TI_OK; TI_E_UNAVAILABLE when the queue is empty; TI_E_ARGUMENT for a pointer that is not writable.

uint8_t key;
ti_input_poll();
while (ti_input_key(&key) == TI_OK) {
    if (key >= 'a' && key <= 'z') key -= 32;
    if (key == 'Q') quit = 1;
    if (key == 'P' || key == 27) toggle_pause();
}

ti_input_held

uint8_t ti_input_held(void);

This function returns the most recent key while a key is held down. When no key is held, it returns 0. The //e reports only that one key. For example, hold A and push Space: the function returns Space until all keys are released. Thus a diagonal movement needs a joystick.

The latch keeps the code of the last key. Bit 7 of $C010 tells if a key is held down. A read of $C010 also clears the strobe. Thus the function reads $C010 only when no key event waits. A key that waits counts as held, and it stays in the latch for ti_input_poll. A key that arrives between the two reads loses its strobe. The function then adds it to the queue from the latch.

ti_input_buttons

uint8_t ti_input_buttons(void);

Bit 0 is button 0 (Open Apple, $C061). Bit 1 is button 1 (Solid Apple, $C062). Bit 2 is button 2 ($C063). A bit is 1 while its button is pushed. To find a new push, compare with the last value.

ti_input_reset

void ti_input_reset(void);

This function removes all characters from the queue.

The joystick

An Apple joystick has two paddles, and the 558 timer of the //e measures them. An access to $C070 starts the two timers. The input of each paddle at $C064 or $C065 stays high for a time that is proportional to its position. libtini measures that time with the VIA timers of the audio driver, not with a CPU loop. Thus the values are the same at 33 MHz and in TURBO. The joystick needs initialized audio.

ti_joystick

typedef struct ti_joystick { uint8_t x, y, buttons; } ti_joystick;

Paddle 0 and paddle 1 as 0-255, with approximately 11 microseconds of device time for each unit. buttons is the value of ti_input_buttons() at the time of the read.

ti_joystick_read

ti_error ti_joystick_read(ti_joystick *state);

This function measures the two paddles. When VIA-B timer 2 counts, the function uses it. When timer 2 does not count, the function measures windows of timer 1, which runs continuously. It reads only the high counter byte, thus it does not clear the clock interrupt flag. When a clock interrupt disturbs a measurement, the function measures again, a maximum of three times.

Each access to $C070-$C07F starts the paddle timers. This includes the RamWorks selector writes at $C073 that banked transfers make. A 558 timer that runs ignores a new start. Thus, before each read, the function waits a maximum of 4 ms until the two paddles stop. Bank 0 transfers do not write the selector. All screen publications are bank 0 transfers.

Returns

TI_OKThe function measured the two axes.
TI_E_TIMINGA paddle still ran after 4 ms. Its value is 255, and the other axis is still valid. A disconnected stick gives 255 on the two axes.
TI_E_BUSYInterrupts disturbed all attempts. *state does not change. A subsequent read can succeed.
TI_E_UNAVAILABLEAudio is not initialized.
TI_E_ARGUMENTstate is not writable.

When an axis does not stop, the read uses approximately 8 ms. These values are raw values. The rest position, the travel and the direction are different from stick to stick.

ti_stick_read

#define TI_STICK_UP     1
#define TI_STICK_DOWN   2
#define TI_STICK_LEFT   4
#define TI_STICK_RIGHT  8
#define TI_STICK_ABSENT 128
uint8_t ti_stick_read(ti_joystick *state);

This function reads the stick and returns its directions as TI_STICK_* bits. It measures the directions from the rest position of that stick. When state is not NULL, it receives the raw values.

  • Rest. The first value in the range 48-207 on the two axes after startup or after ti_stick_recalibrate becomes the rest position. Call recalibrate when the stick is at rest, for example when a run starts.
  • Threshold. Each side starts with a travel of 60 units and records the largest value that it sees. A direction starts after one third of that travel, with a minimum of 16 units. It stops at three quarters of the threshold. Thus a stick near the threshold does not change between on and off.
  • Axes that do not stop. After a stick answered one time, an axis that still runs at the 4 ms limit counts as a full movement.
  • Disturbed reads. When interrupts disturb a read, the function returns the last directions. When no stick answered before, it returns TI_STICK_ABSENT.
  • No stick. When no stick answered before and the two axes do not stop, the function returns TI_STICK_ABSENT. After four such reads, it returns TI_STICK_ABSENT immediately for three seconds of audio ticks and does not touch the paddles. Thus a missing stick costs a game little time.

When state is not writable, the function also returns TI_STICK_ABSENT. The calibration is static state. The function is not reentrant, and there is one calibrated stick.

uint8_t d = ti_stick_read(0);
if (d == TI_STICK_ABSENT) d = 0;
if (d & TI_STICK_LEFT)  x -= 2;
if (d & TI_STICK_RIGHT) x += 2;
if (d & TI_STICK_UP)    y -= 2;
if (d & TI_STICK_DOWN)  y += 2;

ti_stick_recalibrate

void ti_stick_recalibrate(void);

This function makes the next correct value the new rest position. It also ends the wait for a missing stick.

ti_mouse_read

ti_error ti_mouse_read(void *state);

This function is reserved for an optional AppleMouse driver. It always returns TI_E_UNAVAILABLE. It never changes slot personalities.