diff options
Diffstat (limited to 'c/echo.c')
| -rw-r--r-- | c/echo.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/c/echo.c b/c/echo.c new file mode 100644 index 0000000..246c6b6 --- /dev/null +++ b/c/echo.c @@ -0,0 +1,183 @@ +// Required headers +#include <stdint.h> +#include "echoblob.h" +#include "echo.h" + +// Z80 addresses +static volatile uint8_t* const z80_ram = (uint8_t *) 0xA00000; +static volatile uint16_t* const z80_busreq = (uint16_t *) 0xA11100; +static volatile uint16_t* const z80_reset = (uint16_t *) 0xA11200; + +// Macros to control the Z80 +#define Z80_REQUEST() \ + { *z80_busreq = 0x100; while (*z80_busreq & 0x100); } +#define Z80_RELEASE() \ + { *z80_busreq = 0; } +#define Z80_RESET() \ + { *z80_reset = 0; \ + int16_t i; for (i = 8; i >= 0; i--); \ + *z80_reset = 0x100; } + +//*************************************************************************** +// echo_init +// Initializes Echo and gets it running. +//--------------------------------------------------------------------------- +// param list: pointer to instrument list +//*************************************************************************** + +void echo_init(const void *list) { + // Take over the Z80 + Z80_RESET(); + Z80_REQUEST(); + + // Tell Echo to load the pointer list as soon as it starts + uint32_t param = (uint32_t) list; + z80_ram[0x1FFF] = ECHO_CMD_LOADLIST; + z80_ram[0x1FFD] = param; + param >>= 8; + z80_ram[0x1FFE] = param | 0x80; + param >>= 8; + param = (param & 0x7F) | (param >> 1 & 0x80); + z80_ram[0x1FFC] = param; + + // Copy the Echo blob into Z80 RAM + // No, memcpy() won't do here since we must ensure accesses are byte-sized + // (memcpy() may not know this and try word or long accesses) + const uint8_t *src = echo_blob; + volatile uint8_t *dest = z80_ram; + int16_t count = sizeof(echo_blob)-1; + while (count >= 0) + *dest++ = *src++; + + // Let Echo start running! + Z80_RESET(); + Z80_RELEASE(); +} + +//*************************************************************************** +// echo_send_command +// Sends a raw command to Echo. No parameters are taken. +//--------------------------------------------------------------------------- +// param cmd: command to send +//*************************************************************************** + +void echo_send_command(unsigned char cmd) { + // We need access to Z80 bus + Z80_REQUEST(); + + // Is Echo busy yet? + while (z80_ram[0x1FFF] != 0x00) { + Z80_RELEASE(); + int16_t i; + for (i = 0x3FF; i >= 0; i--); + Z80_REQUEST(); + } + + // Write the command + z80_ram[0x1FFF] = cmd; + + // Done with the Z80 + Z80_RELEASE(); +} + +//*************************************************************************** +// echo_send_command_ex +// Sends a raw command to Echo. An address parameter is taken. +//--------------------------------------------------------------------------- +// param cmd: command to send +// param addr: address parameter +//*************************************************************************** + +void echo_send_command_ex(unsigned char cmd, const void *addr) { + // Since we need to split the address into multiple bytes we put it in an + // integer. This is a bad practice in general, period, but since we don't + // care about portability here we can afford to do it this time. + uint32_t param = (uint32_t) addr; + + // We need access to Z80 bus + Z80_REQUEST(); + + // Is Echo busy yet? + while (z80_ram[0x1FFF] != 0x00) { + Z80_RELEASE(); + int16_t i; + for (i = 0x3FF; i >= 0; i--); + Z80_REQUEST(); + } + + // Write the command + z80_ram[0x1FFF] = cmd; + z80_ram[0x1FFD] = param; + param >>= 8; + z80_ram[0x1FFE] = param | 0x80; + param >>= 8; + param = (param & 0x7F) | (param >> 1 & 0x80); + z80_ram[0x1FFC] = param; + + // Done with the Z80 + Z80_RELEASE(); +} + +//*************************************************************************** +// echo_play_bgm +// Starts playing background music. +//--------------------------------------------------------------------------- +// param ptr: pointer to BGM stream +//*************************************************************************** + +void echo_play_bgm(const void *ptr) { + echo_send_command_ex(ECHO_CMD_PLAYBGM, ptr); +} + +//*************************************************************************** +// echo_stop_bgm +// Stops background music playback. +//*************************************************************************** + +void echo_stop_bgm(void) { + echo_send_command(ECHO_CMD_STOPBGM); +} + +//*************************************************************************** +// echo_play_sfx +// Starts playing a sound effect. +//--------------------------------------------------------------------------- +// param ptr: pointer to SFX stream +//*************************************************************************** + +void echo_play_sfx(const void *ptr) { + echo_send_command_ex(ECHO_CMD_PLAYSFX, ptr); +} + +//*************************************************************************** +// echo_stop_sfx +// Stops sound effect playback. +//*************************************************************************** + +void echo_stop_sfx(void) { + echo_send_command(ECHO_CMD_STOPSFX); +} + +//*************************************************************************** +// echo_get_status +// Retrieves Echo's current status. +//--------------------------------------------------------------------------- +// return: status flags (see ECHO_STAT_*) +//*************************************************************************** + +unsigned short echo_get_status(void) { + // We need access to the Z80 + Z80_REQUEST(); + + // Retrieve status from Z80 RAM + uint16_t status = 0; + status = z80_ram[0x1FF0]; + if (z80_ram[0x1FFF] != 0) + status |= ECHO_STAT_BUSY; + + // Done with the Z80 + Z80_RELEASE(); + + // Return status + return status; +} |
