/////////////////////////////////////////////////////////////////////////////
//
// 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 "qsound_ctr.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;
  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 += device_get_qsound_ctr_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;
}

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 += device_get_qsound_ctr_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
  );

  device_start_qsound_ctr(60000000, QMIXSTATE);

  // Set rates
  qsound_set_rates(QSOUNDSTATE, 8000000, 250, 24038);
  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;

  qsoundc_update(QMIXSTATE, samples_needed, state->sound_buffer);

  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) {
  flush_sound(QSOUNDSTATE);
  return qsoundc_r(QMIXSTATE, a);
}

static void EMU_CALL qsound_data_h_w(void *state, uint16 a, uint8 d) {
  qsoundc_w(QMIXSTATE, 0, d);
}

static void EMU_CALL qsound_data_l_w(void *state, uint16 a, uint8 d) {
  qsoundc_w(QMIXSTATE, 1, d);
}

static void EMU_CALL qsound_cmd_w(void *state, uint16 a, uint8 d) {
  flush_sound(QSOUNDSTATE);
  qsoundc_w(QMIXSTATE, 2, d);
}

/////////////////////////////////////////////////////////////////////////////
//
// 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) {
  qsoundc_alloc_rom(QMIXSTATE, size);
  qsoundc_write_rom(QMIXSTATE, 0, size, (const UINT8 *)rom);
}

/////////////////////////////////////////////////////////////////////////////
