summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2020-12-30 19:07:31 +0000
committerJuan J. Martinez <jjm@usebox.net>2020-12-30 19:23:41 +0000
commit2682bc5d1d864341aaeb42a449db73c3ecd16d70 (patch)
tree9116764364b4ee0ce7f6037305077807b57776de /include
downloadubox-msx-lib-1.0.tar.gz
ubox-msx-lib-1.0.zip
Initial import1.0
Diffstat (limited to 'include')
-rw-r--r--include/mplayer.h181
-rw-r--r--include/spman.h149
-rw-r--r--include/ubox.h668
3 files changed, 998 insertions, 0 deletions
diff --git a/include/mplayer.h b/include/mplayer.h
new file mode 100644
index 0000000..a8c1dc4
--- /dev/null
+++ b/include/mplayer.h
@@ -0,0 +1,181 @@
+/*
+ * mplayer lib
+ * Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Arkos 2 AKM
+ * Copyright (c) 2016-2020 Julien Névo (contact@julien-nevo.com)
+ * (check mplayer/akm/LICENSE.txt; MIT License)
+ */
+
+#ifndef _MPLAYER_H
+#define _MPLAYER_H
+
+#include <stdint.h>
+
+// @Initialization
+
+/**
+ * Sets the player to play `sub_song` from `song`.
+ *
+ * `sub_song` starts from 0.
+ *
+ * This function can be called as many times as needed, for example, to change
+ * the playback to a different sub-song.
+ *
+ * It is required to call it at least once before using
+ * [mplayer_play](#mplayer_play).
+ *
+ * Example:
+ *
+ * ```c
+ * // tell the compiler we will use this
+ * // global symbol
+ * extern uint8_t SONG[];
+ *
+ * mplayer_init(SONG, 0);
+ * ```
+ *
+ * See [exporting songs and effects](#exporting-songs-and-effects) for details
+ * on how to add songs to your project.
+ */
+void mplayer_init(uint8_t *song, uint8_t sub_song);
+
+/**
+ * Inits the effects table.
+ *
+ * It is required to call it least once before using
+ * [mplayer_play](#mplayer_play) or any of the play effect functions.
+ *
+ * Example:
+ *
+ * ```c
+ * // tell the compiler we will use this
+ * // global symbol
+ * extern uint8_t EFFECTS[];
+ *
+ * mplayer_init_effects(EFFECTS);
+ * ```
+ *
+ * See [exporting songs and effects](#exporting-songs-and-effects) for details
+ * on how to add effects to your project.
+ */
+void mplayer_init_effects(uint8_t *effects) __z88dk_fastcall;
+
+// @Playback
+
+/**
+ * Plays a frame using the PSG.
+ *
+ * This must be called from the interrupt handler. See
+ * [set_user_isr](ubox-lib-ref.html#ubox_set_user_isr).
+ *
+ * [mplayer_init](#mplayer_init) must be called before using it.
+ *
+ * If the player is stopped, [mplayer_stop](#mplayer_stop) must be called to
+ * silence the PSG.
+ */
+void mplayer_play();
+
+/**
+ * Silences the PSG, stopping any sound.
+ *
+ * This doesn't stop the player if [mplayer_play](#mplayer_play) is called.
+ */
+void mplayer_stop();
+
+// @Sound effects
+
+/**
+ * Plays `effect_no` on `chan` channel at `inv_vol` volume.
+ *
+ * `effect_no` starts on 1 and refers to the index in the effects table set by
+ * [mplayer_init_effects](#mplayer_init_effects).
+ *
+ * `chan` can be 0, 1 or 2. It is recommended to use one channel for effects
+ * and the other two for the music.
+ *
+ * `inv_vol` is the inverted volume, with 0 for max volume and 16 for no sound.
+ *
+ * If an effects is already playing on the channel, it will be interrupted and
+ * replaced by this effect.
+ *
+ * The effects table must be set with
+ * [mplayer_init_effects](#mplayer_init_effects) before using this function.
+ *
+ * See [mplayer_play_effect_p](#mplayer_play_effect_p) for playing effects with
+ * priority.
+ *
+ * Example:
+ *
+ * ```c
+ * // plays effect 1 on channel 2 at highest volume
+ * mplayer_play_effect(1, 2, 0);
+ * ```
+ */
+void mplayer_play_effect(uint8_t effect_no, uint8_t chan, uint8_t inv_vol);
+
+/**
+ * Plays `effect_no` on `chan` channel at `inv_vol` volume using priority.
+ *
+ * `effect_no` starts on 1 and refers to the index in the effects table set by
+ * [mplayer_init_effects](#mplayer_init_effects).
+ *
+ * `effect_no` is used as priority, being effect number 1 the one with
+ * lowest priority.
+ *
+ * `chan` can be 0, 1 or 2. It is recommended to use one channel for effects
+ * and the other two for the music.
+ *
+ * `inv_vol` is the inverted volume, with 0 for max volume and 16 for no sound.
+ *
+ * The effect is played if no effect is already being played or if the effect
+ * being played has less priority. In that case, the effect currently being
+ * played will be interrupted and replaced by this effect.
+ *
+ * The effects table must be set with
+ * [mplayer_init_effects](#mplayer_init_effects) before using this function.
+ *
+ * See [mplayer_play_effect](#mplayer_play_effect) for playing effects without
+ * priority.
+ *
+ * Example:
+ *
+ * ```c
+ * // plays effect 2 on channel 2 at highest volume
+ * mplayer_play_effect_p(2, 2, 0);
+ * // won't interrupt effect 2 because it is higher priority than 1
+ * mplayer_play_effect_p(1, 2, 0);
+ * ```
+ */
+void mplayer_play_effect_p(uint8_t effect_no, uint8_t chan, uint8_t inv_vol);
+
+/**
+ * Stops the effect being played on `chan` channel.
+ */
+void mplayer_stop_effect_channel(uint8_t chan) __z88dk_fastcall;
+
+/**
+ * Returns 0 if there's no sound effect being played on `chan` channel, or
+ * a value different than 0 otherwise.
+ */
+uint8_t mplayer_is_sound_effect_on(uint8_t chan) __z88dk_fastcall;
+
+#endif // _MPLAYER_H
diff --git a/include/spman.h b/include/spman.h
new file mode 100644
index 0000000..67f5e6d
--- /dev/null
+++ b/include/spman.h
@@ -0,0 +1,149 @@
+/*
+ * spman lib
+ * Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _SPMAN_H
+#define _SPMAN_H
+
+#include <stdint.h>
+
+#include "ubox.h"
+
+// @Initialization and pattern allocation
+
+/**
+ * Initialize **spman**.
+ *
+ * Sets all the patterns as unused and no allocated sprites.
+ *
+ * it needs to be called before using any of the functions of the sprite manager.
+ */
+void spman_init();
+
+/**
+ * Allocates a pattern for sprite `type` using `data`.
+ *
+ * `len` specifies the number of sprite patterns to allocate (16x16 each,
+ * exactly 32 bytes).
+ *
+ * If `flip` is set to a non zero value, the pattern data will be flipped
+ * horizontally.
+ *
+ * The function returns the allocated index in the pattern table, than can be
+ * used in [struct sprite_attr](ubox-lib-ref.html#struct-sprite_attr). It
+ * can be called multiple times for the same `type` and the allocation happens
+ * only once per `type`, returning the previously allocated index.
+ *
+ * `type` is an abstraction to group patterns. For example, if our game's
+ * player character has 3 animation frames per horizontal direction, we could
+ * use two different types to group those.
+ *
+ * Example:
+ * ```c
+ * enum sprite_types
+ * {
+ * SPRITE_PLAYER = 0,
+ * SPRITE_PLAYER_FLIP,
+ * SPRITE_ENEMY,
+ * }:
+ *
+ * spman_alloc_pat(SPRITE_PLAYER, player_sprite, 3, 0);
+ * // allocate the same frames flipped horizontally, on the next type
+ * spman_alloc_pat(SPRITE_PLAYER_FLIP, player_sprite, 3, 1);
+ *
+ * // only one frame, keep the allocated pattern
+ * uint8_t enemy_pat = spman_alloc_pat(SPRITE_ENEMY, enemy_sprite, 1, 0);
+ * ```
+ *
+ * A `type` can't be reused with a different pattern, [spman_init](#spman_init)
+ * has to be called again to free all the patterns.
+ */
+uint8_t spman_alloc_pat(uint8_t type, uint8_t *data, uint8_t len, uint8_t flip);
+
+// @Sprite allocation
+
+/**
+ * Allocates a sprite described by `sp` to be shown on the screen on the next
+ * call to [spman_update](#spman_update). This sprite will be excluded from
+ * flickering.
+ *
+ * It is recommended that the number of fixes sprites per line is below 4, or
+ * some sprites may never be shown.
+ *
+ * The limit of sprites to be allocated is 31, including non-fixed sprites.
+ *
+ * The pattern specified in the struct should be allocated with
+ * [spman_alloc_pat](#spman_alloc_pat).
+ *
+ * Allocated sprites are shown on screen by calling to
+ * [spman_update](#spman_update).
+ */
+void spman_alloc_fixed_sprite(struct sprite_attr *sp);
+
+/**
+ * Allocates a sprite described by `sp` to be shown on the screen on the next
+ * call to [spman_update](#spman_update).
+ *
+ * If more than 4 sprites are drawn on the same line, the sprite manager will
+ * alternate which ones are drawn, resulting on a "flickering" effect.
+ *
+ * The limit of sprites to be allocated is 31, including fixed sprites.
+ *
+ * The pattern specified in the struct should be allocated with
+ * [spman_alloc_pat](#spman_alloc_pat).
+ *
+ * Allocated sprites are shown on screen by calling to
+ * [spman_update](#spman_update).
+ */
+void spman_alloc_sprite(struct sprite_attr *sp);
+
+/**
+ * Any allocated sprite will be freed.
+ *
+ * This doesn't affect sprites currently being shown on the screen.
+ */
+void spman_sprite_flush();
+
+// @Sprites on screen
+
+/**
+ * Allocated sprites are processed, making the changes visible on screen.
+ *
+ * Once the changes have been applied, all the allocations are freed. This
+ * doesn't affect the sprites being shown on screen.
+ *
+ * Sprites must be allocated with
+ * [spman_alloc_fixed_sprite](#spman_alloc_fixed_sprite) and
+ * [spman_alloc_sprite](#spman_alloc_sprite).
+ *
+ */
+void spman_update();
+
+/**
+ * Hides all the sprites currently shown on screen.
+ *
+ * This doesn't affect any allocated sprites. To free allocated sprites use
+ * [spman_sprite_flush](#spman_sprite_flush).
+ */
+void spman_hide_all_sprites();
+
+#endif // _SPMAN_H
diff --git a/include/ubox.h b/include/ubox.h
new file mode 100644
index 0000000..2a747c0
--- /dev/null
+++ b/include/ubox.h
@@ -0,0 +1,668 @@
+/*
+ * ubox MSX lib
+ * Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _UBOX_MSX_H
+#define _UBOX_MSX_H
+
+#include <stdint.h>
+
+// @Screen and VDP functions
+//
+// These functions are not necessarily MSX 1 specific, but only MSX 1
+// functionality is documented.
+//
+// Both the tiles and sprite functions target Screen 2 (256x192 pixels graphics
+// mode).
+//
+// The VRAM layout for Screen 2 is as follows:
+//
+// | Address range | Desctiption |
+// | --- | --- |
+// | <tt>0x0000-0x17ff</tt> | Background tiles |
+// | <tt>0x1800-0x1aff</tt> | Background tile map |
+// | <tt>0x1b00-0x1b7f</tt> | Sprite attributes |
+// | <tt>0x2000-0x37ff</tt> | Background colors |
+// | <tt>0x3800-0x3fff</tt> | Sprite patterns |
+//
+
+/**
+ * Sets the screen mode to `mode`.
+ *
+ * This is a list of the different MSX 1 modes:
+ *
+ * | Mode | Name | Description |
+ * | --- | --- | --- |
+ * | 0 | Screen 0 | 40x24 text mode |
+ * | 1 | Screen 1 | 32x24 colored text mode |
+ * | 2 | Screen 2 | 256x192 graphics mode |
+ * | 3 | Screen 3 | 64*48 block graphics multicolor mode |
+ *
+ * Example:
+ *
+ * ```c
+ * // sets Screen 2
+ * ubox_set_mode(2);
+ * ```
+ */
+void ubox_set_mode(uint8_t mode) __z88dk_fastcall;
+
+/**
+ * Enables the screen.
+ */
+void ubox_enable_screen();
+
+/**
+ * Disables the screen.
+ *
+ * Any content drawn to the screen won't be visible until the screen is enabled
+ * with [ubox_enable_screen](#ubox_enable_screen).
+ *
+ * Note that [ubox_set_mode](#ubox_set_mode) also enables the screen.
+ */
+void ubox_disable_screen();
+
+/**
+ * Changes the screen colors.
+ *
+ * First it sets `FORCLR` (foreground) to `fg`, `BAKCLR` (background) to `bg`
+ * and `BDRCLR` to `border`, and then makes them active.
+ *
+ * Example:
+ *
+ * ```c
+ * // sets all three colours to black
+ * ubox_set_colors(1, 1, 1);
+ * ```
+ */
+void ubox_set_colors(uint8_t fg, uint8_t bg, uint8_t border);
+
+/**
+ * Writes a block of `len` bytes from `src` in memory to `dst` in VRAM (video
+ * memory).
+ *
+ * Example:
+ * ```c
+ * // copy 4 sprites attributes from a buffer to the
+ * // sprite attribute table in Screen 2 mode
+ * ubox_write_vm((uint8_t*)0x1b00, 4 * 4, buffer);
+ * ```
+ */
+void ubox_write_vm(uint8_t *dst, uint16_t len, uint8_t *src);
+
+/**
+ * Reads a block of `len` bytes from `src` in VMEM (video memory) to `dst`
+ * in memory.
+ */
+void ubox_read_vm(uint8_t *dst, uint16_t len, uint8_t *src);
+
+/**
+ * Writes `data` to the `reg` VDP register.
+ *
+ * This function is used to setup the VDP, for example to enable 16x16 sprites.
+ *
+ * The VDP has 8 registers, from 0 to 7. For example, it is common to use
+ * register 1 to enable 16x16 sprites.
+ *
+ * The control bits in register 1 are:
+ *
+ * | Bit | Description |
+ * | --- | --- |
+ * | 0 | Enable sprite zoom (x2) |
+ * | 1 | Enable 16x16 sprites (default is 8x8) |
+ * | 2 | Not used |
+ * | 3 | Enable Mode M2, Screen 3 (block) |
+ * | 4 | Enable Mode M1, Screen 0 (text) |
+ * | 5 | Enable v-blank interrupt |
+ * | 6 | Enable screen output control |
+ * | 7 | VRAM size control (0: 4K, 1: 16K) |
+ *
+ * Please refer to [Portar
+ * Doc](http://problemkaputt.de/portar.htm#vdpregisters00h07hbasicmsx1msx2videoregisters)
+ * for further info on VDP registers.
+ *
+ * Example:
+ * ```c
+ * // reg 1: activate sprites, v-blank int on, 16x16 sprites
+ * ubox_wvdp(1, 0xe2);
+ * ```
+ */
+void ubox_wvdp(uint8_t reg, uint8_t data);
+
+/**
+ * Returns the default interrupt frequency according to the BIOS.
+ *
+ * Possible values are:
+ *
+ * | Value | Frequency |
+ * | --- | --- |
+ * | 0 | 60Hz |
+ * | 1 | 50Hz |
+ */
+uint8_t ubox_get_vsync_freq();
+
+// *INDENT-OFF*
+/**
+ * This macro waits for the *vsync* interrupt.
+ *
+ * Interrupts must be enabled or this code will block indefinitely.
+ *
+ * Example:
+ * ```c
+ * ubox_wait_vsync();
+ * // code to run after the int
+ * ```
+ */
+#define ubox_wait_vsync() do { \
+ __asm; \
+ halt \
+ __endasm; \
+} while(0)
+// *INDENT-ON*
+
+// @Tile functions
+//
+// All the functions require **screen 2** (256x192 graphics mode, see
+// [ubox_set_mode](#ubox_set_mode)).
+
+/**
+ * Sets the current tile set to the tile set pointed by `tiles`.
+ *
+ * The tile set is expected to have 256 tiles (8x8 pixels, 8 bytes per tile)
+ * and it will be loaded into the VDP in all three screen sections.
+ *
+ * All the tiles functions use an index in this tile set (usually via a `tile`
+ * parameter).
+ */
+void ubox_set_tiles(uint8_t *tiles) __z88dk_fastcall;
+
+/**
+ * Sets the current color table for current tile set to the color table pointed
+ * by `colors`.
+ *
+ * The color table is expected to have the 8 color rows for each of the 256 tiles
+ * of the tiles table (8 rows, 8 bytes per tile). The colors will be loaded into
+ * the VDP in all three screen sections.
+ */
+void ubox_set_tiles_colors(uint8_t *colors) __z88dk_fastcall;
+
+/**
+ * Puts a tile from current tile set on the screen. The tile is identified by
+ * index `tile` and placed on position (`x`,`y`).
+ *
+ * `x` and `y` units are tiles, and both are 0 based.
+ *
+ * Example:
+ * ```c
+ * // put the tile with index 1 on the top left corner of the screen
+ * ubox_put_tile(0, 0, 1);
+ * ```
+ *
+ * This function is expensive and it performs two calls to the VDP that must
+ * keep the state between them. In case the interrupt handler performs
+ * operations with the VDP, this function shouldn't be used.
+ *
+ * To put multiple tiles in a row, use [ubox_write_vm](#ubox_write_vm) instead. The
+ * tile map is in the memory address `0x1800`.
+ *
+ * Example putting multiple tiles:
+ * ```c
+ * ubox_wait_vsync();
+ * // write a complete row of tiles (32) from a buffer
+ * ubox_write_vm((uint8_t *)0x1800, 32, buffer);
+ * ```
+ */
+void ubox_put_tile(uint8_t x, uint8_t y, uint8_t tile);
+
+/**
+ * Returns the tile index at position (`x`, `y`).
+ *
+ * `x` and `y` units are tiles, and both are 0 based.
+ */
+uint8_t ubox_get_tile(uint8_t x, uint8_t y);
+
+/**
+ * Fills the screen with the tile from current tile set identified by `tile` index.
+ *
+ * See [ubox_set_tiles](#ubox_set_tiles) for for information on how to set a
+ * tile set.
+ */
+void ubox_fill_screen(uint8_t tile) __z88dk_fastcall;
+
+// @Interrupt and clock functions
+
+/**
+ * Installs and initializes the interrupt handler.
+ *
+ * `wait_ticks` is the maximum number of interrupts that [ubox_wait](#ubox_wait)
+ * will wait. This value divides the frequency at which the machine generates the *vsync*
+ * interrupt (50Hz for PAL and 60Hz for NTSC).
+ *
+ * Some common values are:
+ *
+ * | wait ticks | PAL | NTSC |
+ * | --- | --- | --- |
+ * | 1 | 50 FPS | 60 FPS |
+ * | 2 | 25 FPS | 30 FPS |
+ * | 3 | 16.6 FPS | 20 FPS |
+ *
+ * Where FPS is the number of *frames per second* desired to update the game logic.
+ *
+ * Example:
+ * ```c
+ * // ubox_wait will wait for 2 ints (25 FPS)
+ * ubox_init_isr(2);
+ * ```
+ *
+ * This function must be called once before any of the interrupt and clock
+ * functions are used.
+ *
+ * The installed interrupt handler uses `HTIMI_HOOK` instead of replacing the
+ * BIOS handler.
+ *
+ * The interrupt handler will save all registers. The shadow registers are not
+ * preserved or used.
+ *
+ * For performance reasons the BIOS keyboard buffer and key repeat functionality
+ * are disabled, and because of that the some BIOS functions, such as `CHGET`,
+ * won't work.
+ */
+void ubox_init_isr(uint8_t wait_ticks) __z88dk_fastcall;
+
+/**
+ * Installs an user interrupt handler.
+ *
+ * The function pointed by `fn` doesn't need to preserve any registers because
+ * that is done by the main interrupt handler.
+ *
+ * It is recommended that the user interrupt handler is short and uses the
+ * minimal amount of CPU cycles.
+ *
+ * Example:
+ *
+ * ```c
+ * uint8_t tick = 0;
+ *
+ * void my_isr()
+ * {
+ * // count interrupts
+ * ++tick;
+ * }
+ *
+ * ubox_set_user_isr(my_isr);
+ * ```
+ */
+void ubox_set_user_isr(void (*fn)()) __z88dk_fastcall;
+
+/**
+ * Waits for a maximum `wait_ticks` interrupts (see [ubox_init_isr](#ubox_init_isr)).
+ *
+ * This function counts the interrupts (ticks) between between current call to
+ * `ubox_wait` and the previous one, and if less than `wait_ticks` ticks have
+ * passed, it will wait.
+ *
+ * This is used to ensure that the game loop used time is constant and hence
+ * the game plays smooth in case some updates are faster than others.
+ *
+ * If the time passed between current `ubox_wait` call and the previous is
+ * larger than `wait_ticks`, no waiting happens. This may mean that the game
+ * loop takes longer than expected and, if that happens frequently, it would be
+ * recommended to increase `wait_ticks` value when calling
+ * [ubox_init_isr](#ubox_init_isr).
+ *
+ * Example:
+ * ```c
+ * // 25 FPS on PAL, 30 FPS on NTSC
+ * ubox_init_isr(2);
+ *
+ * // game loop example
+ * while (1)
+ * {
+ * update_controls();
+ *
+ * update_entities();
+ * draw_entities();
+ *
+ * // ensure that one loop takes at least 2 interrupts
+ * ubox_wait();
+ * }
+ * ```
+ */
+void ubox_wait();
+
+/**
+ * Waits for `frames` frames.
+ *
+ * This is used to wait a fixed amount of time measured in frames.
+ *
+ * Because `frames` is 8-bit value, this funcion can only wait for 255 frames
+ * in one single call.
+ *
+ * Example:
+ * ```c
+ * // assuming PAL and wait_ticks 2
+ * // wait for 10 seconds
+ * ubox_wait_for(250);
+ * ```
+ */
+void ubox_wait_for(uint8_t frames) __z88dk_fastcall;
+
+extern uint8_t ubox_tick;
+
+/**
+ * Sets `ubox_tick` to zero.
+ *
+ * `ubox_tick` is a 8-bit global variable that is incremented on every interrupt.
+ */
+void ubox_reset_tick();
+
+// @Sprite functions
+//
+// All the functions require **screen 2** (256x192 graphics mode, see
+// [ubox_set_mode](#ubox_set_mode)).
+
+/**
+ * Structure to define sprite attributes.
+ *
+ * | Field | Description |
+ * | --- | --- |
+ * | `x` | The horizontal position of the sprite on the screen. |
+ * | `y` | The vertical position of the sprite on the screen. This coordinate starts in -1 (0xff) instead of 0. |
+ * | `pattern` | The index in the sprite pattern table. When 16x16 sprites are enabled, the lower two bits are ignored (4 regular 8x8 sprites form one 16x16 sprite). |
+ * | `attr` | The sprite attributes. The bits 0-3 are used for color and bit 7 is EC (Early Clock). |
+ *
+ * When EC is enabled, the sprite is displaced 32 pixels to the left.
+ */
+struct sprite_attr {
+ uint8_t y;
+ uint8_t x;
+ uint8_t pattern;
+ uint8_t attr;
+};
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 8x8 pixels pattern.
+ *
+ * See [ubox_set_sprite_pat16](#ubox_set_sprite_pat16) to set a 16x16 pixels pattern.
+ */
+void ubox_set_sprite_pat8(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`, flipping the pattern data horizontally.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 8x8 pixels pattern.
+ *
+ * See [ubox_set_sprite_pat16_flip](#ubox_set_sprite_pat16_flip) to set a 16x16
+ * pixels pattern.
+ */
+void ubox_set_sprite_pat8_flip(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite attributes of sprite number `sprite` using the attributes
+ * pointed by `attr`.
+ *
+ * `sprite` is the index in the sprite table of Screen 2.
+ *
+ * See [struct sprite_attr](#struct-sprite_attr) description for details on
+ * the sprite attributes.
+ *
+ * Example:
+ * ```c
+ * ubox_set_sprite_attr(&player_sprite_attr, 0);
+ * ```
+ *
+ * To set the attributes of multiple sprites that are contiguous in the sprite
+ * table, it is recommended to use [ubox_write_vm](#ubox_write_vm). The sprite
+ * attribute table is in the memory address `0x1b00`.
+ */
+void ubox_set_sprite_attr(struct sprite_attr *attr, uint8_t sprite);
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 16x16 pixels pattern. The pattern is equivalent
+ * to setting four 8x8 patterns (upper left, lower left, upper right and lower right).
+ *
+ * See [ubox_set_sprite_pat8](#ubox_set_sprite_pat8) to set a 8x8 pixels pattern.
+ */
+void ubox_set_sprite_pat16(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`, flipping the pattern data horizontally.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 16x16 pixels pattern. The pattern is equivalent
+ * to setting four 8x8 patterns (upper left, lower left, upper right and lower right).
+ *
+ * See [ubox_set_sprite_pat8_flip](#ubox_set_sprite_pat8_flip) to set a 8x8
+ * pixels pattern.
+ */
+void ubox_set_sprite_pat16_flip(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite attributes of sprite number `sprite` using the attributes
+ * pointed by `attr`.
+ *
+ * `sprite` is the index in the sprite table of Screen 2.
+ *
+ * See [struct sprite_attr](#struct-sprite_attr) description for details on
+ * the sprite attributes.
+ *
+ * Example:
+ * ```c
+ * ubox_set_sprite_attr(&player_sprite_attr, 0);
+ * ```
+ *
+ * To set the attributes of multiple sprites that are contiguous in the sprite
+ * table, it is recommended to use [ubox_write_vm](#ubox_write_vm). The sprite
+ * attribute table is in the memory address `0x1b00`.
+ */
+void ubox_set_sprite_attr(struct sprite_attr *attr, uint8_t sprite);
+
+// @Control functions
+//
+// The supported controls are: cursor and joystick (port 1 and port 2).
+//
+// In the case of cursor, space key is used as fire 1 and the "m" key as fire 2.
+//
+// For one button joysticks, [ubox_read_keys](#ubox_read_keys) can be used to
+// provide an alternative fire 2 button using the keyboard.
+
+/**
+ * Waits for trigger on cursor (space or "m") or any of the joysticks, and
+ * returns the selected control.
+ *
+ * See [ubox_read_ctl](#ubox_read_ctl) for possible control values.
+ */
+uint8_t ubox_select_ctl();
+
+/**
+ * Read the control identified by `control` and return the status of it.
+ *
+ * Possible control values are:
+ *
+ * | Control | Description |
+ * | --- | --- |
+ * | UBOX_MSX_CTL_CURSOR | Cursor for move, space for fire 1 and "m" for fire 2 |
+ * | UBOX_MSX_CTL_PORT1 | Joystick on port 1 |
+ * | UBOX_MSX_CTL_PORT2 | Joystick on port 2 |
+ * | UBOX_MSX_CTL_NONE | No control is selected. This is the default output of [ubox_select_ctl](#ubox_select_ctl) |
+ *
+ * The control status is a bit map with the following values:
+ *
+ * | Mask | Bit |
+ * | --- | --- |
+ * | UBOX_MSX_CTL_UP | 0 |
+ * | UBOX_MSX_CTL_DOWN | 1 |
+ * | UBOX_MSX_CTL_LEFT | 2 |
+ * | UBOX_MSX_CTL_RIGHT | 3 |
+ * | UBOX_MSX_CTL_FIRE1 | 4 |
+ * | UBOX_MSX_CTL_FIRE2 | 5 |
+ *
+ * Example:
+ * ```c
+ * if (ubox_read_ctl(UBOX_MSX_CTL_CURSOR) & UBOX_MSX_CTL_UP)
+ * {
+ * // in the cursor, UP is on
+ * }
+ * ```
+ */
+uint8_t ubox_read_ctl(uint8_t control) __z88dk_fastcall;
+
+#define UBOX_MSX_CTL_CURSOR 0
+#define UBOX_MSX_CTL_PORT1 1
+#define UBOX_MSX_CTL_PORT2 2
+#define UBOX_MSX_CTL_NONE 0xff
+
+#define UBOX_MSX_CTL_UP 1
+#define UBOX_MSX_CTL_DOWN 2
+#define UBOX_MSX_CTL_LEFT 4
+#define UBOX_MSX_CTL_RIGHT 8
+#define UBOX_MSX_CTL_FIRE1 16
+#define UBOX_MSX_CTL_FIRE2 32
+
+/**
+ * Reads a row of the keyboard matrix and returns a bit map with the state of
+ * the keys in that row.
+ *
+ * It is important to remember that there are different keyboard layouts and
+ * that there is no reliable way of detecting 100% of them.
+ *
+ * Constants for the QWERTY layout are provided as `UBOX_MSX_KEY_*`, and that covers
+ * the most common models.
+ *
+ * Example:
+ * ```c
+ * // read row 7 that includes RET, SEL, BS, STOP, TAB, ESC, F5 and F4
+ * uint8_t keys = ubox_read_keys(7);
+ *
+ * if (keys & UBOX_MSX_KEY_ESC)
+ * {
+ * // ESC key is pressed
+ * }
+ * ```
+ */
+uint8_t ubox_read_keys(uint8_t row) __z88dk_fastcall;
+
+// row 0
+#define UBOX_MSX_KEY_7 0x80
+#define UBOX_MSX_KEY_6 0x40
+#define UBOX_MSX_KEY_5 0x20
+#define UBOX_MSX_KEY_4 0x10
+#define UBOX_MSX_KEY_3 0x08
+#define UBOX_MSX_KEY_2 0x04
+#define UBOX_MSX_KEY_1 0x02
+#define UBOX_MSX_KEY_0 0x01
+
+// row 1
+#define UBOX_MSX_KEY_SEMI 0x80 // ";"
+#define UBOX_MSX_KEY_CSBRACKET 0x40 // "]"
+#define UBOX_MSX_KEY_OSBRACKET 0x20 // "["
+#define UBOX_MSX_KEY_BSLASH 0x10 // "\"
+#define UBOX_MSX_KEY_EQUAL 0x08 // "="
+#define UBOX_MSX_KEY_MINUS 0x04 // "-"
+#define UBOX_MSX_KEY_9 0x02
+#define UBOX_MSX_KEY_8 0x01
+
+// row 2
+#define UBOX_MSX_KEY_B 0x80 // "B"
+#define UBOX_MSX_KEY_A 0x40 // "A"
+#define UBOX_MSX_KEY_FSLASH 0x10 // "/"
+#define UBOX_MSX_KEY_DOT 0x08 // "."
+#define UBOX_MSX_KEY_COMMA 0x04 // ","
+#define UBOX_MSX_KEY_QUOTE 0x02 // "'"
+#define UBOX_MSX_KEY_TICK 0x01 // "`"
+
+// row 3
+#define UBOX_MSX_KEY_J 0x80 // "J"
+#define UBOX_MSX_KEY_I 0x40 // "I"
+#define UBOX_MSX_KEY_H 0x20 // "H"
+#define UBOX_MSX_KEY_G 0x10 // "G"
+#define UBOX_MSX_KEY_F 0x08 // "F"
+#define UBOX_MSX_KEY_E 0x04 // "E"
+#define UBOX_MSX_KEY_D 0x02 // "D"
+#define UBOX_MSX_KEY_C 0x01 // "C"
+
+// row 4
+#define UBOX_MSX_KEY_R 0x80 // "R"
+#define UBOX_MSX_KEY_Q 0x40 // "Q"
+#define UBOX_MSX_KEY_P 0x20 // "P"
+#define UBOX_MSX_KEY_O 0x10 // "O"
+#define UBOX_MSX_KEY_N 0x08 // "N"
+#define UBOX_MSX_KEY_M 0x04 // "M"
+#define UBOX_MSX_KEY_L 0x02 // "L"
+#define UBOX_MSX_KEY_K 0x01 // "K"
+
+// row 5
+#define UBOX_MSX_KEY_Z 0x80 // "Z"
+#define UBOX_MSX_KEY_Y 0x40 // "Y"
+#define UBOX_MSX_KEY_X 0x20 // "X"
+#define UBOX_MSX_KEY_W 0x10 // "W"
+#define UBOX_MSX_KEY_V 0x08 // "V"
+#define UBOX_MSX_KEY_U 0x04 // "U"
+#define UBOX_MSX_KEY_T 0x02 // "T"
+#define UBOX_MSX_KEY_S 0x01 // "S"
+
+// row 6
+#define UBOX_MSX_KEY_F3 0x80 // F3
+#define UBOX_MSX_KEY_F2 0x40 // F2
+#define UBOX_MSX_KEY_F1 0x20 // F1
+#define UBOX_MSX_KEY_CODE 0x10 // CODE
+#define UBOX_MSX_KEY_CAP 0x08 // CAP
+#define UBOX_MSX_KEY_GRAPH 0x04 // GRAPH
+#define UBOX_MSX_KEY_CTRL 0x02 // CTRL
+#define UBOX_MSX_KEY_SHIFT 0x01 // SHIFT
+
+// row 7
+#define UBOX_MSX_KEY_RET 0x80 // RET
+#define UBOX_MSX_KEY_SEL 0x40 // SEL
+#define UBOX_MSX_KEY_BS 0x20 // BS
+#define UBOX_MSX_KEY_STOP 0x10 // STOP
+#define UBOX_MSX_KEY_TAB 0x08 // TAB
+#define UBOX_MSX_KEY_ESC 0x04 // ESC
+#define UBOX_MSX_KEY_F5 0x02 // F5
+#define UBOX_MSX_KEY_F4 0x01 // F4
+
+// row 8
+#define UBOX_MSX_KEY_RIGHT 0x80 // RIGHT
+#define UBOX_MSX_KEY_DOWN 0x40 // DOWN
+#define UBOX_MSX_KEY_UP 0x20 // UP
+#define UBOX_MSX_KEY_LEFT 0x10 // LEFT
+#define UBOX_MSX_KEY_DEL 0x08 // DEL
+#define UBOX_MSX_KEY_INS 0x04 // INS
+#define UBOX_MSX_KEY_HOME 0x02 // HOME
+#define UBOX_MSX_KEY_SPACE 0x01 // SPACE
+
+#endif // _UBOX_MSX_H