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;
}
- Memory.
ti_init(1, 3)grants three banks. The 32,000-byte canvas, the 32,768-byte screen backup and the temporary 32,768-byte loan of the audio init need 97,536 bytes. This is a little more than two banks. - Near arena. The position of the square is in
NEARBSS, outside the main area of the program. The checks read it there. - Audio as the clock.
voices3 limits the driver to one AY chip, which gives the least work for each update.ti_audio_reserve(0)gives all voices to effects. Thus the tone of the button can use voice 0. If audio fails, the game shows a message and the square does not move, because there is no clock. - Fixed steps.
ti_steps_duegives the number of updates that are due. The loop does not draw when no update is due, or after a gap. - Draw. Each move erases the old square and draws the new square. It publishes only these two 16x16 rectangles.
- Exit. The cleanup calls at the end do the same work as a
returnalone.
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:
- 1 says READY.
- 2 speaks 1984 with
ti_speech_number. - S stops speech.
- Q or Esc exits.
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.
| Line | Result |
|---|---|
VIA-B T2 | Whether timer 2 of the $C480 VIA counts. When it counts, the joystick reader uses it. |
RAW START | The 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 PHONEMES | The four phonemes of READY, one at a time: the number that finished, the flags of each VIA and the total time. |
MOCKINGBOARD, PHASOR | The 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+JOYSTICK | The 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:
| Part | Use in STARFALL |
|---|---|
| Code layout | game.c and game.h are the portable simulation. The host checks also compile them. main.c controls input, audio and the screen. |
| Memory | STARFALL 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. |
| Errors | All 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. |
| Playfield | A 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. |
| Interface | A 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. |
| Effects | Palette 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. |
| Timing | ti_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. |
| Sound | M 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. |
| Speech | Eight prepared phrases, compiled with --no-numbers. Each phrase has a caption on the screen. |
| Input | Held 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.