cog/Frameworks/HighlyQuixotic/HighlyQuixotic/Core/qsound.c

632 lines
19 KiB
C

/////////////////////////////////////////////////////////////////////////////
//
// qsound - QSound emulation
//
/////////////////////////////////////////////////////////////////////////////
#ifndef EMU_COMPILE
#error "Hi I forgot to set EMU_COMPILE"
#endif
#include "qsound.h"
#include "z80.h"
#include "kabuki.h"
#include "qmix.h"
/////////////////////////////////////////////////////////////////////////////
//
// Static information
//
static uint8 safe_rom_area[4] = {0,0,0,0};
/////////////////////////////////////////////////////////////////////////////
//
// Static init for the whole library
//
static uint8 library_was_initialized = 0;
//
// Deliberately create a NULL dereference
// Useful for calling attention to show-stopper problems like forgetting to
// call qsound_init() or compiling with the wrong byte order
//
static void qsound_hang(const char *message) {
for(;;) { *((volatile char*)0) = *message; }
}
//
// Endian check
//
static void qsound_endian_check(void) {
uint32 num = 0x61626364;
// Big
if(!memcmp(&num, "abcd", 4)) {
#ifdef EMU_BIG_ENDIAN
return;
#endif
qsound_hang("endian check");
}
// Little
if(!memcmp(&num, "dcba", 4)) {
#ifndef EMU_BIG_ENDIAN
return;
#endif
qsound_hang("endian check");
}
// Don't know what!
qsound_hang("endian check");
}
//
// Data type size check
//
static void qsound_size_check(void) {
if(sizeof(uint8 ) != 1) qsound_hang("size check");
if(sizeof(uint16) != 2) qsound_hang("size check");
if(sizeof(uint32) != 4) qsound_hang("size check");
if(sizeof(uint64) != 8) qsound_hang("size check");
if(sizeof(sint8 ) != 1) qsound_hang("size check");
if(sizeof(sint16) != 2) qsound_hang("size check");
if(sizeof(sint32) != 4) qsound_hang("size check");
if(sizeof(sint64) != 8) qsound_hang("size check");
}
sint32 EMU_CALL qsound_init(void) {
sint32 r;
qsound_endian_check();
qsound_size_check();
r = z80_init(); if(r) return r;
r = qmix_init(); if(r) return r;
library_was_initialized = 1;
return 0;
}
/////////////////////////////////////////////////////////////////////////////
//
// Version information
//
const char* EMU_CALL qsound_getversion(void) {
static const char s[] = "QCore0001a (built " __DATE__ ")";
return s;
}
/////////////////////////////////////////////////////////////////////////////
//
// State information
//
struct QSOUND_STATE {
struct Z80_MEMORY_MAP *map_op;
struct Z80_MEMORY_MAP *map_read;
struct Z80_MEMORY_MAP *map_write;
void *z80_state;
void *qmix_state;
sint16 *sound_buffer; // TEMPORARY pointer. safe.
uint32 sound_buffer_samples_free;
uint32 sound_cycles_pending;
uint16 qsound_data_latch;
uint8 fatalflag;
uint8 dummy0;
uint32 bank_ofs;
uint32 cycles_until_irq;
uint32 cycles_per_irq;
uint32 cycles_per_sample;
uint64 odometer;
uint8 ramC[0x1000];
uint8 ramF[0x1000];
uint8 *z80_rom;
uint32 z80_rom_size;
uint32 kabuki_swap_key1;
uint32 kabuki_swap_key2;
uint16 kabuki_addr_key;
uint8 kabuki_xor_key;
uint8 kabuki_op [0x8000];
uint8 kabuki_data[0x8000];
};
#define QSOUNDSTATE ((struct QSOUND_STATE*)(state))
#define Z80STATE (QSOUNDSTATE->z80_state)
#define QMIXSTATE (QSOUNDSTATE->qmix_state)
extern const uint32 qsound_map_op_entries;
extern const uint32 qsound_map_read_entries;
extern const uint32 qsound_map_write_entries;
/*
void EMU_CALL qsound_write_ram_C(void *state, uint16 addr, uint8 data) {
QSOUNDSTATE->ramC[addr & 0xFFF] = data;
}
void EMU_CALL qsound_write_ram_F(void *state, uint16 addr, uint8 data) {
QSOUNDSTATE->ramF[addr & 0xFFF] = data;
}
*/
uint32 EMU_CALL qsound_get_state_size(void) {
uint32 size = 0;
size += sizeof(struct QSOUND_STATE);
size += sizeof(struct Z80_MEMORY_MAP) * qsound_map_op_entries;
size += sizeof(struct Z80_MEMORY_MAP) * qsound_map_read_entries;
size += sizeof(struct Z80_MEMORY_MAP) * qsound_map_write_entries;
size += z80_get_state_size();
size += qmix_get_state_size();
return size;
}
static void EMU_CALL recompute_memory_maps(struct QSOUND_STATE *state);
static void EMU_CALL qsound_advance(void *state, uint32 elapse);
static struct Z80_MEMORY_MAP qsound_map_in [1];
static struct Z80_MEMORY_MAP qsound_map_out[1];
void EMU_CALL qsound_set_rates(
void *state,
uint32 z80,
uint32 irq,
uint32 sample
) {
QSOUNDSTATE->cycles_per_irq = (z80 + (irq /2)) / irq ;
QSOUNDSTATE->cycles_per_sample = (z80 + (sample/2)) / sample;
qmix_set_sample_rate(QMIXSTATE, sample);
}
void EMU_CALL qsound_clear_state(void *state) {
uint32 offset;
// Clear local struct
memset(state, 0, sizeof(struct QSOUND_STATE));
// Set up sub-pointers
offset = sizeof(struct QSOUND_STATE);
QSOUNDSTATE->map_op = (void*)(((char*)state)+offset); offset += sizeof(struct Z80_MEMORY_MAP) * qsound_map_op_entries;
QSOUNDSTATE->map_read = (void*)(((char*)state)+offset); offset += sizeof(struct Z80_MEMORY_MAP) * qsound_map_read_entries;
QSOUNDSTATE->map_write = (void*)(((char*)state)+offset); offset += sizeof(struct Z80_MEMORY_MAP) * qsound_map_write_entries;
QSOUNDSTATE->z80_state = (void*)(((char*)state)+offset); offset += z80_get_state_size();
QSOUNDSTATE->qmix_state = (void*)(((char*)state)+offset); offset += qmix_get_state_size();
//
// Set other variables
//
QSOUNDSTATE->bank_ofs = 0x8000;
//
// Take care of substructures: memory maps, Z80 state, mixer state
//
recompute_memory_maps(QSOUNDSTATE);
z80_clear_state(Z80STATE);
z80_set_advance_callback(Z80STATE, qsound_advance, QSOUNDSTATE);
z80_set_memory_maps(
Z80STATE,
QSOUNDSTATE->map_op,
QSOUNDSTATE->map_read,
QSOUNDSTATE->map_write,
qsound_map_in,
qsound_map_out
);
qmix_clear_state(QMIXSTATE);
// Set rates
qsound_set_rates(QSOUNDSTATE, 8000000, 250, 44100);
QSOUNDSTATE->cycles_until_irq = QSOUNDSTATE->cycles_per_irq;
// Done
}
/////////////////////////////////////////////////////////////////////////////
//
// Obtain substates
//
void* EMU_CALL qsound_get_r3000_state(void *state) { return Z80STATE; }
void* EMU_CALL qsound_get_qmix_state (void *state) { return QMIXSTATE; }
/////////////////////////////////////////////////////////////////////////////
uint16 EMU_CALL qsound_getpc(void *state) { return z80_getpc(Z80STATE); }
/////////////////////////////////////////////////////////////////////////////
//
// Signal an interrupt
//
static void EMU_CALL irq_signal(struct QSOUND_STATE *state) {
z80_setirq(Z80STATE, 1, 0x00);
}
/////////////////////////////////////////////////////////////////////////////
//
// Bank switching
//
static void set_memory_map_rom_area(
struct Z80_MEMORY_MAP *map,
uint8 *rom,
sint32 rom_size
) {
if(rom_size < 1) { rom = safe_rom_area; rom_size = 4; }
if(rom_size > ((map->type.mask)+1)) rom_size = ((map->type.mask)+1);
map->y = map->x + (rom_size-1);
map->type.p = rom;
}
/*
static void recompute_fixed_rom_areas(struct QSOUND_STATE *state) {
set_memory_map_rom_area(
state->map_op,
state->op_rom,
state->op_rom_size
);
set_memory_map_rom_area(
state->map_read,
state->data_rom,
state->data_rom_size
);
}
*/
static void recompute_banked_rom_areas(struct QSOUND_STATE *state) {
set_memory_map_rom_area(
state->map_op + 1,
state->z80_rom + state->bank_ofs,
state->z80_rom_size - state->bank_ofs
);
set_memory_map_rom_area(
state->map_read + 1,
state->z80_rom + state->bank_ofs,
state->z80_rom_size - state->bank_ofs
);
}
static void EMU_CALL qsound_banksw_w(void *state, uint16 a, uint8 d) {
QSOUNDSTATE->bank_ofs = 0x8000 + 0x4000 * ((uint32)(d&0xF));
recompute_banked_rom_areas(QSOUNDSTATE);
// just in case we're executing from the banked area
// (for CPS1/2 this is exquisitely unlikely)
z80_break(Z80STATE);
}
/////////////////////////////////////////////////////////////////////////////
//
// QMIX: Forward read/write/advance calls to the QSound mixer
//
static void EMU_CALL flush_sound(struct QSOUND_STATE *state) {
uint32 samples_needed = state->sound_cycles_pending / (state->cycles_per_sample);
if(samples_needed > state->sound_buffer_samples_free) {
samples_needed = state->sound_buffer_samples_free;
}
if(!samples_needed) return;
qmix_render(QMIXSTATE, state->sound_buffer, samples_needed);
if(state->sound_buffer) state->sound_buffer += 2 * samples_needed;
state->sound_buffer_samples_free -= samples_needed;
state->sound_cycles_pending -= (state->cycles_per_sample) * samples_needed;
}
//
// Register reads/writes
//
static uint8 EMU_CALL qsound_status_r(void *state, uint16 a) {
return 0x80;
}
static void EMU_CALL qsound_data_h_w(void *state, uint16 a, uint8 d) {
QSOUNDSTATE->qsound_data_latch =
(QSOUNDSTATE->qsound_data_latch & 0x00FF) |
(((uint32)d) << 8);
}
static void EMU_CALL qsound_data_l_w(void *state, uint16 a, uint8 d) {
QSOUNDSTATE->qsound_data_latch =
(QSOUNDSTATE->qsound_data_latch & 0xFF00) |
((uint32)d);
}
static void EMU_CALL qsound_cmd_w(void *state, uint16 a, uint8 d) {
flush_sound(QSOUNDSTATE);
qmix_command(QMIXSTATE, d, QSOUNDSTATE->qsound_data_latch);
}
/////////////////////////////////////////////////////////////////////////////
//
// Invalid-address catchers
//
static uint8 EMU_CALL catcher_read(void *state, uint16 a) { return 0; }
static void EMU_CALL catcher_write(void *state, uint16 a, uint8 d) { }
/////////////////////////////////////////////////////////////////////////////
//
// Advance hardware activity by the given cycle count
//
static void EMU_CALL qsound_advance(void *state, uint32 elapse) {
if(!elapse) return;
//
// Check timers
//
if(QSOUNDSTATE->cycles_until_irq <= elapse) {
QSOUNDSTATE->cycles_until_irq += QSOUNDSTATE->cycles_per_irq;
irq_signal(QSOUNDSTATE);
}
QSOUNDSTATE->cycles_until_irq -= elapse;
//
// Update pending sound cycles
//
QSOUNDSTATE->sound_cycles_pending += elapse;
//
// Update odometer
//
QSOUNDSTATE->odometer += ((uint64)elapse);
}
/////////////////////////////////////////////////////////////////////////////
//
// Determine how many cycles until the next interrupt
//
// This is then used as an upper bound for how many cycles can be executed
// before checking for futher interrupts
//
static uint32 EMU_CALL cycles_until_next_interrupt(
struct QSOUND_STATE *state
) {
uint32 cyc, min = 0xFFFFFFFF;
//
// Timers
//
cyc = state->cycles_until_irq;
if(cyc < min) min = cyc;
if(min < 1) min = 1;
return min;
}
/////////////////////////////////////////////////////////////////////////////
//
// Memory map structures
//
#define STATEOFS(thetype,thefield) ((void*)(&(((struct thetype*)0)->thefield)))
static struct Z80_MEMORY_MAP qsound_map_op[] = {
{ 0x0000, 0x7FFF, { 0x7FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,kabuki_op) } },
{ 0x8000, 0xBFFF, { 0x3FFF, Z80_MAP_TYPE_POINTER , NULL } },
{ 0xC000, 0xCFFF, { 0x0FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,ramC) } },
{ 0xF000, 0xFFFF, { 0x0FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,ramF) } },
{ 0x0000, 0xFFFF, { 0xFFFF, Z80_MAP_TYPE_CALLBACK, catcher_read } }
};
static struct Z80_MEMORY_MAP qsound_map_read[] = {
{ 0x0000, 0x7FFF, { 0x7FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,kabuki_data) } },
{ 0x8000, 0xBFFF, { 0x3FFF, Z80_MAP_TYPE_POINTER , NULL } },
{ 0xC000, 0xCFFF, { 0x0FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,ramC) } },
{ 0xF000, 0xFFFF, { 0x0FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,ramF) } },
{ 0xD007, 0xD007, { 0x0000, Z80_MAP_TYPE_CALLBACK, qsound_status_r } },
{ 0x0000, 0xFFFF, { 0xFFFF, Z80_MAP_TYPE_CALLBACK, catcher_read } }
};
static struct Z80_MEMORY_MAP qsound_map_write[] = {
{ 0xC000, 0xCFFF, { 0x0FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,ramC) } },
{ 0xF000, 0xFFFF, { 0x0FFF, Z80_MAP_TYPE_POINTER , STATEOFS(QSOUND_STATE,ramF) } },
{ 0xD000, 0xD000, { 0x0000, Z80_MAP_TYPE_CALLBACK, qsound_data_h_w } },
{ 0xD001, 0xD001, { 0x0000, Z80_MAP_TYPE_CALLBACK, qsound_data_l_w } },
{ 0xD002, 0xD002, { 0x0000, Z80_MAP_TYPE_CALLBACK, qsound_cmd_w } },
{ 0xD003, 0xD003, { 0x0000, Z80_MAP_TYPE_CALLBACK, qsound_banksw_w } },
{ 0x0000, 0xFFFF, { 0xFFFF, Z80_MAP_TYPE_CALLBACK, catcher_write } }
};
static struct Z80_MEMORY_MAP qsound_map_in [1] = {
{ 0x0000, 0xFFFF, { 0xFFFF, Z80_MAP_TYPE_CALLBACK, catcher_read } }
};
static struct Z80_MEMORY_MAP qsound_map_out[1] = {
{ 0x0000, 0xFFFF, { 0xFFFF, Z80_MAP_TYPE_CALLBACK, catcher_write } }
};
#define QSOUND_ARRAY_ENTRIES(x) (sizeof(x)/sizeof((x)[0]))
const uint32 qsound_map_op_entries = QSOUND_ARRAY_ENTRIES(qsound_map_op );
const uint32 qsound_map_read_entries = QSOUND_ARRAY_ENTRIES(qsound_map_read );
const uint32 qsound_map_write_entries = QSOUND_ARRAY_ENTRIES(qsound_map_write);
/////////////////////////////////////////////////////////////////////////////
//
// Memory map recomputation
//
static void perform_state_offset(
struct Z80_MEMORY_MAP *map,
struct QSOUND_STATE *state
) {
//
// It's safe to typecast this "pointer" to a uint32 since, due to
// the STATEOFS hack, it'll never be bigger than 4GB
//
uint32 o = (uint32)(map->type.p);
map->type.p = ((uint8*)state) + o;
}
static void perform_nonnull_state_offsets(
struct QSOUND_STATE *state,
struct Z80_MEMORY_MAP *map,
uint32 entries
) {
uint32 i;
for(i = 0; i < entries; i++) {
if(
(map[i].type.n == Z80_MAP_TYPE_POINTER) &&
(map[i].type.p)
) {
perform_state_offset(map + i, state);
}
}
}
//
// Recompute the memory maps.
// PERFORMS NO REGISTRATION with the actual R3000 state.
//
static void recompute_memory_maps(struct QSOUND_STATE *state) {
//
// First, just copy from the static tables
//
memcpy(state->map_op , qsound_map_op , sizeof(qsound_map_op ));
memcpy(state->map_read , qsound_map_read , sizeof(qsound_map_read ));
memcpy(state->map_write, qsound_map_write, sizeof(qsound_map_write));
//
// Now perform state offsets on all non-NULL pointers
//
perform_nonnull_state_offsets(state, state->map_op , qsound_map_op_entries );
perform_nonnull_state_offsets(state, state->map_read , qsound_map_read_entries );
perform_nonnull_state_offsets(state, state->map_write, qsound_map_write_entries);
//
// Now take care of ROM areas
//
// recompute_fixed_rom_areas (state);
recompute_banked_rom_areas(state);
}
/////////////////////////////////////////////////////////////////////////////
//
// Executes the given number of cycles or the given number of samples
// (whichever is less)
//
// Sets *sound_samples to the number of samples actually generated,
// which may be ZERO or LESS than the number requested, but never more.
//
// Return value:
// >= 0 The number of cycles actually executed, which may be ZERO, MORE,
// or LESS than the number requested
// < 0 Unrecoverable error
//
sint32 EMU_CALL qsound_execute(
void *state,
sint32 cycles,
sint16 *sound_buf,
uint32 *sound_samples
) {
sint32 r = 0;
sint64 cyc_upperbound;
uint64 old_odometer;
uint64 target_odometer;
old_odometer = QSOUNDSTATE->odometer;
QSOUNDSTATE->sound_buffer = sound_buf;
QSOUNDSTATE->sound_buffer_samples_free = *sound_samples;
//
// If the fatal flag was set, return -2
//
if(QSOUNDSTATE->fatalflag) { return -1; }
//
// If we have a bogus cycle count, return error
//
if(cycles < 0) { return -1; }
/*
** Begin by flushing any pending sound data into the newly available
** buffer, if necessary
*/
flush_sound(QSOUNDSTATE);
/*
** Compute an upper bound for the number of cycles that can be generated
** while still fitting in the buffer
*/
cyc_upperbound =
((sint64)(QSOUNDSTATE->cycles_per_sample)) *
((sint64)(QSOUNDSTATE->sound_buffer_samples_free));
if(cyc_upperbound > (QSOUNDSTATE->sound_cycles_pending)) {
cyc_upperbound -= QSOUNDSTATE->sound_cycles_pending;
} else {
cyc_upperbound = 0;
}
/*
** Bound cyc_upperbound by the number of cycles requested
*/
if(cycles > 0x70000000) cycles = 0x70000000;
if(cyc_upperbound > cycles) cyc_upperbound = cycles;
/*
** Now cyc_upperbound is the number of cycles to execute.
** Compute the target odometer
*/
target_odometer = (QSOUNDSTATE->odometer)+((uint64)(cyc_upperbound));
/*
** Execution loop
*/
for(;;) {
uint32 diff, ci;
if(QSOUNDSTATE->odometer >= target_odometer) break;
diff = (uint32)((target_odometer) - (QSOUNDSTATE->odometer));
ci = cycles_until_next_interrupt(QSOUNDSTATE);
if(diff > ci) diff = ci;
r = z80_execute(Z80STATE, diff);
// Create an error if necessary
if(r < 0 || QSOUNDSTATE->fatalflag) { r = -1; break; }
}
//
// End with a final sound flush
//
flush_sound(QSOUNDSTATE);
/*
** Determine how many sound samples we generated
*/
(*sound_samples) -= (QSOUNDSTATE->sound_buffer_samples_free);
//
// If there was an error, return it
//
if(r < 0) {
//char s[100];
//sprintf(s,"blah blah %d", r);
//MessageBox(NULL,s,NULL,MB_OK);
return r;
}
//
// Otherwise return the number of cycles we executed
//
r = (sint32)(QSOUNDSTATE->odometer - old_odometer);
return r;
}
/////////////////////////////////////////////////////////////////////////////
uint64 EMU_CALL qsound_get_odometer(void *state) {
return QSOUNDSTATE->odometer;
}
/////////////////////////////////////////////////////////////////////////////
static void recompute_kabuki_rom(struct QSOUND_STATE *state) {
uint32 len = state->z80_rom_size;
if(len > 0x8000) len = 0x8000;
kabuki_decode(
state->z80_rom,
state->kabuki_op,
state->kabuki_data,
len,
state->kabuki_swap_key1,
state->kabuki_swap_key2,
state->kabuki_addr_key,
state->kabuki_xor_key
);
}
/////////////////////////////////////////////////////////////////////////////
void EMU_CALL qsound_set_z80_rom(void *state, void *rom, uint32 size) {
QSOUNDSTATE->z80_rom = rom;
QSOUNDSTATE->z80_rom_size = size;
recompute_kabuki_rom(QSOUNDSTATE);
recompute_memory_maps(QSOUNDSTATE);
}
void EMU_CALL qsound_set_kabuki_key(void *state,
uint32 swap_key1,
uint32 swap_key2,
uint16 addr_key,
uint8 xor_key
) {
QSOUNDSTATE->kabuki_swap_key1 = swap_key1;
QSOUNDSTATE->kabuki_swap_key2 = swap_key2;
QSOUNDSTATE->kabuki_addr_key = addr_key;
QSOUNDSTATE->kabuki_xor_key = xor_key;
recompute_kabuki_rom(QSOUNDSTATE);
}
void EMU_CALL qsound_set_sample_rom(void *state, void *rom, uint32 size) {
qmix_set_sample_rom(QMIXSTATE, rom, size);
}
/////////////////////////////////////////////////////////////////////////////