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.
| Function | Purpose |
|---|---|
ti_input_poll | Move a waiting keypress into the key queue |
ti_input_key | Get the oldest character in the queue |
ti_input_held | Get the key that is held down, for movement while a key is held |
ti_input_buttons | Get the three button inputs |
ti_input_reset | Empty the key queue |
ti_joystick_read | Get the raw values of paddle 0, paddle 1 and the buttons |
ti_stick_read, ti_stick_recalibrate | Get up, down, left and right from a calibrated stick |
ti_mouse_read | Reserved; 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.
| Key | Code | Key | Code |
|---|---|---|---|
| Left arrow | 8 | Return | 13 |
| Right arrow | 21 | Escape | 27 |
| Up arrow | 11 | Tab | 9 |
| Down arrow | 10 | Delete | 127 |
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_OK | The function measured the two axes. |
TI_E_TIMING | A 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_BUSY | Interrupts disturbed all attempts. *state does not change. A subsequent read can succeed. |
TI_E_UNAVAILABLE | Audio is not initialized. |
TI_E_ARGUMENT | state 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_recalibratebecomes 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 returnsTI_STICK_ABSENTimmediately 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.