Examples

Four programs in examples/ show the library at different sizes: a starter game that you can copy, an audio demo, a speech check and STARFALL, a complete shooter.

The starter

examples/starter/ is a small game that you can copy. Held WASD keys, arrow keys or a joystick move a square at thirty updates each second. A joystick button plays a tone. Q returns to ProDOS. The starter needs RamWorks selectors 1-3 and a slot-4 card for the clock.

/* A libtini starter: a square that held keys or a joystick move at a fixed
 * 30 updates a second, a tone on a joystick button, and Q to quit. */
#include "libtini.h"

static ti_surface screen;
static const uint16_t colors[16]={0x012,0x123,0x235,0x346,0x467,0x68A,0x9BC,0xDEF,
 0xFA6,0xD64,0x832,0x4DC,0xA8F,0xFFB,0x57A,0x9ED};
/* Three voices on one AY chip: the least audio work per update. */
static ti_audio_config audio={TI_AUDIO_MOCKINGBOARD,0,0,0,3};
/* The square's position; tests read it. The caller near arena's NEARBSS
 * holds it, outside main memory, and startup zeroes it like BSS. */
#pragma bss-name (push, "NEARBSS")
int16_t starter_x, starter_y;
#pragma bss-name (pop)

static void square(uint8_t color)
{
    ti_rect r;
    r.x=starter_x;r.y=starter_y;r.width=16;r.height=16;
    ti_gfx_rect(&screen,starter_x,starter_y,16,16,color);
    ti_gfx_present(&screen,&r,0);
}

/* WASD or arrows while held, plus the stick's calibrated directions. */
static uint8_t directions(void)
{
    uint8_t d=ti_stick_read(0),k=ti_input_held();
    if(d==TI_STICK_ABSENT)d=0;
    if(k>='a'&&k<='z')k-=32;
    if(k=='W'||k==11)d|=TI_STICK_UP;
    if(k=='S'||k==10)d|=TI_STICK_DOWN;
    if(k=='A'||k==8)d|=TI_STICK_LEFT;
    if(k=='D'||k==21)d|=TI_STICK_RIGHT;
    return d;
}

int main(void)
{
    uint8_t key=0,steps,d,buttons,last_buttons=0;
    /* The canvas, display backup and audio init's temporary storage loan
     * need 97,536 bytes: three 48,640-byte resource banks, selectors 1-3. */
    if(ti_init(1,3)!=TI_OK)return 1;
    if(ti_gfx_surface_create(&screen,320,200)!=TI_OK)return 1;
    if(ti_gfx_begin(colors)!=TI_OK)return 1;
    ti_gfx_clear(&screen,0);
    ti_gfx_rect(&screen,0,0,320,12,1);
    ti_gfx_text(&screen,8,2,"MY FIRST LIBTINI GAME",13);
    ti_gfx_text(&screen,8,190,"HOLD WASD OR ARROWS, OR USE A STICK. Q QUITS",7);
    /* The audio driver also owns the clock and joystick timer. */
    if(ti_audio_init(&audio)==TI_OK) {
        ti_audio_reserve(0);
        ti_steps_begin(ti_audio_ticks,TI_AUDIO_TICKS_PER_SECOND,30,30);
    } else ti_gfx_text(&screen,8,96,"A SLOT 4 AUDIO CARD IS REQUIRED",9);
    ti_gfx_present(&screen,0,0);
    starter_x=152;starter_y=92;square(11);
    while(key!='Q') {
        ti_audio_update();ti_input_poll();
        while(ti_input_key(&key)==TI_OK&&key!='Q'&&key!='q');
        if(key=='q')key='Q';
        buttons=ti_input_buttons()&3;
        if(buttons&&!last_buttons)ti_audio_effect(0,300,12,10,1);
        last_buttons=buttons;
        steps=ti_steps_due();
        if(!steps||steps==TI_STEPS_GAP)continue;
        d=directions();
        if(!d)continue;
        square(0);
        /* Two pixels a step, inside the play area. */
        while(steps--) {
            if((d&TI_STICK_LEFT)&&starter_x>0)starter_x-=2;
            if((d&TI_STICK_RIGHT)&&starter_x<304)starter_x+=2;
            if((d&TI_STICK_UP)&&starter_y>14)starter_y-=2;
            if((d&TI_STICK_DOWN)&&starter_y<172)starter_y+=2;
        }
        square(11);
    }
    ti_audio_shutdown();ti_gfx_end();ti_gfx_surface_free(&screen);ti_shutdown();return 0;
}

To build the starter, use python tools/build.py --cc65-home D:/Repos/cc65 --game starter. To make a separate project from it, use examples/starter/create.py. See First steps.

The audio demo

examples/audio_demo.c plays the event score from assets/music.json and speaks phrases from assets/speech.json. It uses the 40-column text screen, with no graphics and no ti_init. Its configuration enables speech and ducking: {TI_AUDIO_MOCKINGBOARD, 1, 1, 0}. Boot build/audio_demo.po. Use these keys:

The speech check

examples/speech_check/ is a text-mode check of the SSI-263 on the Appletini. Boot build/speech_check.po. The check runs for a maximum of approximately 25 seconds. It shows one line for each test and then waits for a key.

LineResult
VIA-B T2Whether timer 2 of the $C480 VIA counts. When it counts, the joystick reader uses it.
RAW STARTThe check starts the SSI directly, without the speech code of the driver. The line shows which VIA set the CA1 flag for a finished phoneme, and the time until the flag. NONE after 1.00 s means that no flag came: the SSI-263 has no clock.
RAW PHONEMESThe four phonemes of READY, one at a time: the number that finished, the flags of each VIA and the total time.
MOCKINGBOARD, PHASORThe driver says READY and EXCELLENT. The line shows OK and the time, or the first error and its time. SEEN counts the finished-phoneme signals that the check sampled before the driver took them. In Phasor mode the interrupt handler takes them first, so SEEN stays 0.
MB+JOYSTICK, PH+JOYSTICKThe same test with a joystick read each 30 ms. J gives the number of reads and of failed reads.

A line that shows TIMING AT 2.00S means that a phoneme did not finish in the two-second limit of the driver. In standalone ONE//e mode, speech needs Appletini firmware F1.3.2 or later. In the emulator, all lines show success and timer 2 does not count.

STARFALL

examples/starfall/ is a vertical scrolling survival shooter. It has five enemy types, four weapon levels, wingmen, overdrive, chain scores, bombs, an eight-track song and spoken callouts. examples/starfall/README.md describes the game and its controls. The folder also contains a built disk, STARFALL.po. The game also plays in a browser at appletini.org/demo/starfall. STARFALL is written fully in C. It shows these parts of the library:

PartUse in STARFALL
Code layoutgame.c and game.h are the portable simulation. The host checks also compile them. main.c controls input, audio and the screen.
MemorySTARFALL has its own layout, starfall.cfg. It is a copy of cfg/game.cfg without the file buffer, because the game makes no file calls. The simulation state, star columns, animated palette and text buffers use 639 of the 640 bytes of the near arena. The sprite cells, the speech, the song and the effect table are constants in low memory.
ErrorsAll draw calls go through ti_error_keep. The loop stops when ti_error_first is not zero. The text screen then shows ERROR ee STEP s: the library code and the startup step.
PlayfieldA 240x184 sprite scene with a maximum of 112 sprites and a 120-byte strip. Stars, the shield bubble and the core of the ship are rectangle images. Atlas sub-sprites are images in one cell. When the scene is busy, the game lists fewer stars and backdrop aircraft, so that all aircraft, shots and pickups have space in the list.
InterfaceA banked canvas holds the HUD, captions and panels. HUD values are opaque text of fixed length, published cell by cell. After the game publishes the full canvas, it calls ti_scene_forget.
EffectsPalette entries 14 and 15 pulse for aimed bullets and shields. Entry 0 flashes for bombs and hits. The game calls ti_gfx_palette one time in each frame.
Timingti_steps_due gives thirty updates each second. A gap or an invalid audio clock pauses the game. When the game continues, it starts the clock again and resets the steps.
SoundM or P selects the card. The song plays on voices 0-7 of a Phasor or voices 0-1 of a Mockingboard. One effect table works on the two cards through ti_audio_effect_voice. The service function of the scene calls ti_audio_update.
SpeechEight prepared phrases, compiled with --no-numbers. Each phrase has a caption on the screen.
InputHeld keys and taps move the ship. A calibrated stick has priority over the keys when it moves from rest. J disables the stick. A pause panel shows the raw stick readings.

The benchmark program

examples/benchmark.c is not a demo. tools/benchmark.py builds it into its own disk and uses it to measure library functions on the emulator. See Tools.