#define __KS_NES_C
#include "common.h"
#include "audio/emuaudio.h"

#include "ks_rfc.inc"
rfcstate_object *rfcstatep[1];
#include <PR/sched.h>
#include "ks_nes.h"


u8 *ks_nes_nesromstartp; /* asmrfc.s $B$+$i;2>H$5$l$k(B */
static u32 ks_emu_bss_start, ks_emu_bss_end;
static u8 *ks_emu_get_memblock(int size)
{
  u32 tmp;

  tmp = ALIGN64B(ks_emu_bss_start);
  if ((tmp + size) >= ks_emu_bss_end) {
    /* out of memory */
    osSyncPrintf("ks_emu_get_memblock: failed to get 0x%x bytes from 0x%x\n", size, tmp);
    while (TRUE)
      ;
  }
  ks_emu_bss_start = tmp + size;
  osSyncPrintf("ks_emu_get_memblock: from 0x%x, 0x%x bytes\n", tmp, size);
  return (u8 *)tmp;
}

static int sizeof_rfcstate_object;

static void ks_nes_audio_cleanup(GAME_NES *game_p)
{
  EmuSound_Exit();
}

static void ks_nes_cleanup(GAME_NES *game_p)
{
  rfcstate_object *p;
  p = rfcstatep[0];
  if (p -> work_rspsignal == 0) {
    /* SP $B=hM}$,=*N;$9$k$^$GBT$D!#(B*/
    while((IO_READ(SP_STATUS_REG) & SP_STATUS_RSPSIGNAL) == 0) {
      /* wait */
    }
    IO_WRITE(SP_STATUS_REG, SP_CLR_RSPSIGNAL);
    p -> work_rspsignal = SP_STATUS_RSPSIGNAL;
  }
}

static void ks_nes_audio_main(GAME_NES *game_p)
{
  EmuSound_VFrameCallback();
}
  

static void ks_nes_main(GAME_NES *game_p)
{
  extern int ks_emu_rfc_asm(rfcstate_object *p);
  extern u64 kspRFCTextStart[], kspRFCTextEnd[];
  rfcstate_object *p;
  OSTask *taskp;

  int i, j;

  p = rfcstatep[0];

  p -> serial4016d0 = game_p -> n64_buttons[0] << 16; // Controller Port 1P
  p -> serial4016d1 = game_p -> n64_buttons[2] << 16; // Controller Port 3P
  p -> serial4017d0 = game_p -> n64_buttons[1] << 16; // Controller Port 2P
  p -> serial4017d1 = game_p -> n64_buttons[3] << 16; // Controller Port 4P

  p -> work_4015h_output = (p -> work_4015h_output & 0xc0) | Sound_Read(0x4015);
  p -> soundcycle = 0;
  p -> sound_cycle_divider = 1; /* speed $B$O(B 1 $B$N$_(B */

  p -> gfx_selbuf_next = p -> gfx_selbuf_next ^ 1;
  i = ks_emu_rfc_asm(p);

  p -> work_rspsignal = 0; /* $B$3$N4X?t$,=*$o$C$?$i(B $BI,$:(B RSP task $B$r<B9T$9$k!#(B*/

  taskp = &game_p -> os_sc_task.list;
  taskp -> t.type = M_AUDTASK;
  taskp -> t.flags = 0;
  taskp -> t.ucode_boot = kspRFCTextStart;
  /* sptask.c $B7PM3$G(B ../monegi/rsp/sprawdma.c $B$K%A%'%C%/$5$l$k$N$G!"(B 16bytes alignment $B$,I,MW(B */
  taskp -> t.ucode_boot_size = (((u32)kspRFCTextEnd - (u32)kspRFCTextStart) + 0xf) & ~0xf;
  taskp -> t.dram_stack = (u64 *)&p -> gfxbuf[sizeof(rfcstate_gfxbuf2_object)];
  taskp -> t.dram_stack_size = (u32)p -> gfxptr;
  taskp -> t.output_buff = (u64 *)game_p -> cfbp[game_p -> cfb_no];
  taskp -> t.output_buff_size = (u64 *)p -> ucode_workp;
  taskp -> t.data_ptr = (u64 *)*(u32 *)((u32)(&p -> headerp) + 8);

  game_p -> cfb_no ^= 1;
  osWritebackDCacheAll();
}

extern int ks_nes_reset(GAME_NES *game_p)
{
  extern int ks_reset_rfcstate_asm(rfcstate_object *p);
  rfcstate_object *p;
  int i;
  u8 *p2;
  u32 save[5];

  p = rfcstatep[0];

  
  bcopy(&p -> headerp, save, sizeof(save));
  p2 = p -> ucode_workp;
  bzero(p, sizeof_rfcstate_object);
  bcopy(save, &p -> headerp, sizeof(save));
  p -> ucode_workp = p2;
  p -> work_rspsignal = SP_STATUS_RSPSIGNAL;
  osWritebackDCacheAll();
  i = ks_reset_rfcstate_asm(p);
  Sound_Write(0x5015, 0x07, 50);

  return 0;
}

extern void ks_nes_audio_init(GAME_NES *game_p)
{
  /* $B4X?t%]%$%s%?=i4|2=(B */
  game_p -> g.exec = ks_nes_audio_main;
  game_p -> g.cleanup = ks_nes_audio_cleanup;
  EmuSound_Start(NULL);
}


extern void ks_nes_init(GAME_NES *game_p)
{
  extern void Sound_Write(u16 reg, u8 value, u16 time); /*** Write Register (4000-4015) ***/
  rfcstate_object *p;
  int i;
  
  /* $B4X?t%]%$%s%?=i4|2=(B */
  game_p -> g.exec = ks_nes_main;
  game_p -> g.cleanup = ks_nes_cleanup;

  game_p -> os_sc_task.next = NULL;
  game_p -> os_sc_task.state = 0;
  game_p -> os_sc_task.flags = OS_SC_NEEDS_RSP;
  game_p -> os_sc_task.framebuffer = 0;
  osCreateMesgQueue(&game_p -> os_sc_task_msgQ, game_p -> os_sc_task_msgbuf, 1);
  game_p -> os_sc_task.msgQ = &game_p -> os_sc_task_msgQ;

  ks_nes_nesromstartp = game_p -> nesrom;
  
  /* $B%(%_%e%l!<%?MQ(B BSS $B$N8GDj3d$jEv$F(B ($B;CDj(B) */
  ks_emu_bss_start = 0x80400000;
  ks_emu_bss_end = 0x80800000;

  /* $BG0$N$?$a(B 16bytes align $B$7$F$*$/!#(B*/
  sizeof_rfcstate_object = (sizeof(rfcstate_object) + 0xf) & ~0xf;

  p = (rfcstate_object *)ks_emu_get_memblock(sizeof_rfcstate_object);
  rfcstatep[0] = p;
  bzero(p, sizeof_rfcstate_object);

  p -> headerp = ks_emu_get_memblock(0x10 + 0x200);

  bcopy(game_p -> nesrom, p -> headerp, 0x10);

  p -> ucode_workp = ks_emu_get_memblock(sizeof(rfcstate_ucodewk_object));
  p -> work_rspsignal = SP_STATUS_RSPSIGNAL;

  osViSetMode(&osViModeNtscLpn1);

  osViSetSpecialFeatures(
			 OS_VI_GAMMA_OFF |
			 OS_VI_GAMMA_DITHER_OFF |
			 OS_VI_DIVOT_OFF
			 );
  /* OS bug workaround */
  osViSetSpecialFeatures(OS_VI_DITHER_FILTER_OFF);

  Sound_Write(0x4010, 0, 0);
  Sound_Write(0x4013, 0, 0);
  Sound_Write(0x4015, 0, 0);
  ks_nes_reset(game_p);
}
