/*
 * SYSTEM 16 ARCADE EMULATOR SOURCE CODE
 * 
 * Copyright 1996/97 Thierry Lescot
 */

#include "cpudefs.h"
#include <go32.h>
#include <allegro.h>

#include "shinobi.h"

unsigned int resultat=0;

extern char game, game_basename[9], game_name[20];
extern char joy;
int joy_count=1, old_joy=0;

unsigned char *TilesROM1, *TilesROM2, *TilesROM3, *GameROM, *SpritesROM;

extern UBYTE ext_bank, vid_bank, txt_bank, ctr_bank, pal_bank;
extern int sortie, strace, sx, sy;
extern long int total_render;
extern unsigned int *PaletteShinobi;
extern unsigned char number_of_new_colors;
extern long valeur_controle;
extern char sync;

int areg_byteinc[] = {1,1,1,1,1,1,1,2};
int imm8_table[] = {8,1,2,3,4,5,6,7};

int keydef[40] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_0,
   KEY_CONTROL, KEY_ALT, KEY_SPACE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,
   127, 127, 127, 127, 127, 127, 127,
   KEY_Q, KEY_W, KEY_E, KEY_U, KEY_J, KEY_H, KEY_L,
   127, 127, 127,
   127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
};

char joydef[4] = { 1, 2, 3, 4 };

UBYTE spr_tab[16];

UBYTE spr_tab_all[16*LAST_GAME] =
     {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, /* Shinobi         */
	0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00,
	0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, /* Altered Beast   */
	0x08, 0x00, 0x0A, 0x00, 0x0C, 0x00, 0x00, 0x00,
	0x00, 0x02, 0x08, 0x0A, 0x10, 0x12, 0x00, 0x00, /* Golden Axe      */
	0x04, 0x06, 0x0C, 0x0E, 0x14, 0x16, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, /* Time Scanner    */
	0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Quartet II      */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, /* Shadow Dancer   */
	0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E
     };

RGB pal;
UBYTE shinobi_ds1[10] = {0,0,0,0,0,0,0,0,0,0};
UBYTE shinobi_ds2[10] = {0,0,0,0,0,0,0,0,0,0};

#ifdef SON_SIMULE
SAMPLE *Sons[0x100];
#endif

#ifdef MAX_MEM_TEST
UWORD MaximumR[0x100], MaximumW[0x100];
#endif

/* POINTEURS */

ULONG ptr_text_video;
ULONG base_scr_hor_fg, base_scr_hor_bg, base_scr_ver_fg, base_scr_ver_bg;
ULONG base_scr_pag_fg, base_scr_pag_bg;
ULONG base_spr_regist;
ULONG base_brq_page;
UWORD reg_scr_active;

UBYTE fake_page=0x01;

void *bank_ff_base, *sprites_base_data, *bank_44_base;
void *display_base, *text_base, *tiles_base;
void *foreground_base, *background_base;
void *display_bottom, *vga_pointer;
ULONG vscreen_base, vscreen_base_spr;

extern BITMAP *vscreen, *vscreen_sub;
extern UBYTE *TimerA, *TimerB;

#ifndef RELEASE_BETA
extern TraceOn();
#endif

#ifdef SEARCH
void m68k_dumpstate()
{
   int i;
   CPTR nextpc;
   for(i = 0; i < 8; i++){
      printf("D%d: %08lx ", i, regs.d[i]);
      if ((i & 3) == 3) printf("\n");
   }
   for(i = 0; i < 8; i++){
      printf("A%d: %08lx ", i, regs.a[i]);
      if ((i & 3) == 3) printf("\n");
   }
    if (regs.s == 0) regs.usp = regs.a[7];
   if (regs.s && regs.m) regs.msp = regs.a[7];
   if (regs.s && regs.m == 0) regs.isp = regs.a[7];
   printf("USP=%08lx ISP=%08lx MSP=%08lx VBR=%08lx SR=%08x\n",
	  regs.usp,regs.isp,regs.msp,regs.vbr,regs.sr);
   printf ("T=%d%d S=%d M=%d N=%d Z=%d V=%d C=%d IMASK=%d\n",
	   regs.t1, regs.t0, regs.s, regs.m,
	   NFLG, ZFLG, VFLG, CFLG, regs.intmask);
   for(i = 0; i < 8; i++){
	printf("FP%d: %g ", i, regs.fp[i]);
      if ((i & 3) == 3) printf("\n");
   }
   printf("N=%d Z=%d I=%d NAN=%d\n",
	  (regs.fpsr & 0x8000000) != 0,
	  (regs.fpsr & 0x4000000) != 0,
	  (regs.fpsr & 0x2000000) != 0,
	  (regs.fpsr & 0x1000000) != 0);
   MC68000_disasm(m68k_getpc(), &nextpc, 1);
   printf("Next PC = 0x%0x\n", nextpc);
}
#endif

void WAccessCtr(UWORD r, UBYTE v) {
}

UWORD CheckInput(UWORD r) {
   UBYTE rt=0, update=1;
   if (game==3) RAM[0xFF][0xEC96]=0xFF;
   switch (r) {
    case 0x0001:
    case 0x1003: // 1P CONTROL 
      if (joy==1) {
	 poll_joystick();
	 if (joy_b[joydef[0]]) rt|=0x02;
	 if (joy_b[joydef[1]]) rt|=0x04;
	 if (joy_b[joydef[2]]) rt|=0x01;
	 if (joy_left) rt|=0x80; else if (joy_right) rt|=0x40;
	 if (joy_down) rt|=0x10; else if (joy_up) rt|=0x20;
      }
      if (key[keydef[6]]) rt|=0x02; // ATTACK, PUNCH, LEFT FLIPPER
      if (key[keydef[7]]) rt|=0x04; // JUMP, KICK, RIGHT FLIPPER
      if (key[keydef[8]]) rt|=0x01; // MAGIC, JUMP, NOT USED
      if (key[keydef[9]]) rt|=0x20; // UP, UP, PUSH SWITCH
      else if (key[keydef[10]]) rt|=0x10;// DOWN, DOWN, NOT USED
      if (key[keydef[11]]) rt|=0x80;// LEFT, LEFT, LEFT SWITCH
      else if (key[keydef[12]]) rt|=0x40;// RIGHT, RIGHT, RIGHT SWITCH
      break;
    case 0x1007: // 2P CONTROL
      if (joy==2) {
	 poll_joystick();
	 if (joy_b[joydef[0]]) rt|=0x02;
	 if (joy_b[joydef[1]]) rt|=0x04;
	 if (joy_b[joydef[2]]) rt|=0x01;
	 if (joy_left) rt|=0x80; else if (joy_right) rt|=0x40;
	 if (joy_down) rt|=0x10; else if (joy_up) rt|=0x20;
      }
      if (key[keydef[20]]) rt|=0x02; // ATTACK
      if (key[keydef[21]]) rt|=0x04; // JUMP
      if (key[keydef[22]]) rt|=0x01; // MAGIC 
      if (key[keydef[23]]) rt|=0x20; // UP
      if (key[keydef[24]]) rt|=0x10; // DOWN
      if (key[keydef[25]]) rt|=0x80; // LEFT
      if (key[keydef[26]]) rt|=0x40; // RIGHT
      break;
    case 0x0009:
    case 0x1001:
      if (key[keydef[0]]) rt|=0x10; // 1P START
      if (key[keydef[1]]) rt|=0x20; // 2P START
      if (key[keydef[5]]) rt|=0x04; // TEST SWITCH
      if (key[keydef[2]]) rt|=0x02; // COIN SWITCH 2
      if (key[keydef[3]]) rt|=0x01; // COIN SWITCH 1
      if (key[keydef[4]]) rt|=0x08; // SERVICE SWITCH
//      RAM[0xFF][0xEC96]=~rt;
      break;
    case 0x000B:
    case 0x2001: // DIP SWITCH 2
      rt=shinobi_ds2[game];
      break;
    case 0x1005: // DIP SWICTH 1
      rt=shinobi_ds1[game];
      break;
    default:
      update=0;
      break;
   }
   if (update) RAM[ctr_bank][r]=~rt;
   return(r);
}

UBYTE RAccessCtr(UWORD r) {
   UBYTE rt=0;
   switch (r) {
    case 0x0001:
    case 0x1003: // 1P CONTROL 
      if (key[keydef[8]]) rt|=0x01; // MAGIC 
      if (key[keydef[6]]) rt|=0x02; // ATTACK
      if (key[keydef[7]]) rt|=0x04; // JUMP
      if (key[keydef[10]]) rt|=0x10;// DOWN
      if (key[keydef[9]]) rt|=0x20; // UP
      if (key[keydef[12]]) rt|=0x40;// RIGHT
      if (key[keydef[11]]) rt|=0x80;// LEFT
      break;
    case 0x1007: // 2P CONTROL 
      if (key[keydef[24]]) rt|=0x01; // MAGIC 
      if (key[keydef[25]]) rt|=0x02; // ATTACK
      if (key[keydef[26]]) rt|=0x04; // JUMP
      if (key[keydef[21]]) rt|=0x10;
      if (key[keydef[20]]) rt|=0x20;
      if (key[keydef[23]]) rt|=0x40;
      if (key[keydef[22]]) rt|=0x80;
      break;
    case 0x0009:
    case 0x1001:
      if (key[keydef[0]]) rt|=0x10; // 1P
      if (key[keydef[1]]) rt|=0x20; // 2P
      if (key[keydef[5]]) rt|=0x04; // TEST
      if (key[keydef[2]]) rt|=0x02; // COIN SWITCH 2
      if (key[keydef[3]]) rt|=0x01; // COIN SWITCH 1
      if (key[keydef[4]]) rt|=0x08; // SERVICE
      break;
    case 0x000B:
    case 0x2001: // DIP SWITCH 2
      rt=shinobi_ds2[game];
      break;
    case 0x2003: // DIP SWICTH 1
      rt=shinobi_ds1[game];
      break;
    default:
      rt=(0x00); break;
   }
   return(~rt);
}

#ifdef MAX_MEM_TEST
void TestMemAccessW(LONG p) {
   int s=(p&0xFF0000)>>16;
   int o=(p&0x00FFFF);
   if (o>MaximumW[s]) MaximumW[s]=o;
}
void TestMemAccessR(LONG p) {
   int s=(p&0xFF0000)>>16;
   int o=(p&0x00FFFF);
   if (o>MaximumR[s]) MaximumR[s]=o;
}
#endif

// Proc‚dures d'acc‚s … la m‚moire //

UBYTE ReadMEM(LONG p) {
   UBYTE bank = ((p&0xFF0000)>>16);
   #ifdef MAX_MEM_TEST
   TestMemAccessR(p);
   #endif
   switch (bank) {
    case 0x00:
    case 0x01:
    case 0x02:
    case 0x03:
    case 0x04:
    case 0x05:
    case 0x06:
    case 0x07:
    case 0x08:
    case 0x09:
    case 0x0A:
    case 0x0B:
    case 0x0C:
    case 0x0D:
    case 0x0E:
    case 0x0F:
    case 0x40:
    case 0x41:
    case 0x43:
    case 0x84:
    case 0x10:
    case 0x11:
    case 0x14:
    case 0x3F:
    case 0x7F:
    case 0x1F:
    case 0xC0:
    case 0x44:
    case 0x20:
    case 0xFE:
    case 0x1E:
    case 0xFF: 
      return(RAM[bank][p&0x0000FFFF]); 
      break;
    case 0xE4:
    case 0xC4:
      return(RAccessCtr(p&0x0000FFFF)); 
      break;
#ifndef RELEASE_BETA
    default: {
      
       TraceOn();
       printf("Tentative d'accŠs en lecture … une zone m‚moire non ‚mul‚e !\n");
       printf("PC=0x%0x, zone lue=0x%0x (0x%0x)\n", m68k_getpc(), p, bank);
       m68k_dumpstate();
    }
#endif
   }
}

#ifdef SON_SIMULE
void Load_Samples() {
   int i;
   char fn[30], bn[6];
   switch (game) {
    case 1: strcpy(bn, "SHINO"); break;
    case 2: strcpy(bn, "ALTER"); break;
   }
   printf("Loading samples...\n");
   for (i=0;i!=255;i++) {
      sprintf(fn, "samples\\%s_%02x.WAV", bn, i);
      if (file_exists(fn, 0, NULL)) {
	 printf("%s\n", fn);
	 Sons[i]=load_sample(fn);
      } else Sons[i]=NULL;
   }
   printf("Son charg‚s!\n");
}
#endif

void SoundRequest(int son) {
   #ifdef SON_SIMULE
   RGB pal;
   char status[40];
   RAM[0xFE][0x0007]=0x00;
   if ((son>=0x40)&&(son<0x90)) last_voice=((last_voice<<8)|son);
   else 
     if ((son>=0x90)&&(son<0xA0)) last_bgm=((last_bgm<<8)|son);
   else
     if ((son>=0xA0)&&(son<0xC0)) last_sfx=((last_sfx<<8)|son);
   /* 90-9A = BGM , A0-BD = SFX , 41-4F = VOICE */
   pal.r=pal.g=pal.b=63;
   set_color(255, (RGB *)&pal);
   sprintf(status, "B:%08x S:%08x V:%08x", last_bgm, last_sfx, last_voice);
   textout(screen, font, status, 8, 232, 255);
   if (Sons[son]!=NULL) play_sample(Sons[son], 200, 100, 400, 0);
   #endif
}

void WriteMEM(LONG p, UBYTE v) {
   UBYTE bank = ((p&0xFF0000)>>16);
   #ifdef MAX_MEM_TEST
   TestMemAccessW(p);
   #endif
   /*
   if ((p==0xFFFFD780)&&(v==0x00)) {
      TraceOn();
      printf("$D780 = $00!\n");
   }*/
   p&=0x0000FFFF;
   switch (bank) {
    case 0x00:
    case 0x01:
    case 0x02:
    case 0x03:  
    case 0x04:
    case 0x05:
    case 0x06:
    case 0x07:
    case 0x08:
    case 0x09:
    case 0x0A:
    case 0x0B:
    case 0x0C:
    case 0x0D:
    case 0x0E:
    case 0x0F:
      RAM[bank][p]=v;
      break;
    case 0xFE:
      if ((p&0xFFFF)==7) SoundRequest(v);
      break;
    case 0xFF:
    case 0x44:
    case 0x20:
    case 0x40:
    case 0x43:
    case 0x41:
    case 0x84:
    case 0x10:
    case 0x11:
    case 0x14:
    case 0x3F:
    case 0x7F:
    case 0x1F:
    case 0x1E:
    case 0xC0:
      RAM[bank][p]=v;
      break;
    case 0xE4:
    case 0xC4:
      WAccessCtr(p&0x0000FFFF, v); 
      break;
#ifndef RELEASE_BETA
    default: {
       TraceOn();
       printf("Tentative d'accŠs en ‚criture … une zone m‚moire non ‚mul‚e !\n");
       printf("PC=0x%0x, zone acc‚d‚e=0x%0x (0x%0x)\n", m68k_getpc(), p, bank);
       m68k_dumpstate();
       sortie=0;
    }
#endif
   }
}
#ifdef ASM_MEMORY
UBYTE get_byte(LONG a);
void put_byte(LONG a, UBYTE b);
void put_word(LONG a, UWORD b);
UWORD get_word(LONG a);
void put_long(LONG a, ULONG b);
ULONG get_long(LONG a);
#else
UBYTE get_byte(LONG a) {
//   if (((a&0xC40000)>>16)==0xC4) CheckInput(a&0xFFFF);
   return(ReadMEM(a));
}

void put_byte(LONG a, UBYTE b) {
   WriteMEM(a, b);
}

void put_word(LONG a, UWORD b) {
   WriteMEM(a, (UBYTE)((b&0xFF00)>>8));
   WriteMEM(a+1, (UBYTE)(b&0x00FF));
}

UWORD get_word(LONG a) {
   return(((ULONG)ReadMEM(a)<<8)+(ULONG)ReadMEM(a+1));
}

void put_long(LONG a, ULONG b) {
   WriteMEM(a, (UBYTE)((b&0xFF000000)>>24));
   WriteMEM(a+1, (UBYTE)((b&0x00FF0000)>>16));
   WriteMEM(a+2, (UBYTE)((b&0x0000FF00)>>8));
   WriteMEM(a+3, (UBYTE)(b&0x000000FF));
}

ULONG get_long(LONG a) {
   return(
	  (((ULONG)ReadMEM(a))<<24)+
	  (((ULONG)ReadMEM(a+1))<<16)+
	  (((ULONG)ReadMEM(a+2))<<8)+
	  ((ULONG)ReadMEM(a+3))
	  );
}
#endif

void Setup_Asm() {
   ULONG ptr, ptr2;
   int i;
   vscreen_base_spr=vscreen_base=(ULONG)vscreen->line[0];
   bank_ff_base=RAM[0xFF]; bank_44_base=RAM[0x44];
   sprites_base_data=SpritesROM;
   display_base=&(vscreen->line[88][88]);
   display_bottom=vscreen->line[312];
   text_base=&RAM[txt_bank][0x002E];
   tiles_base=TilesROM1;
   foreground_base=RAM[vid_bank];
   background_base=RAM[vid_bank];
   for (i=0;i!=16;i++) spr_tab[i]=spr_tab_all[(16*(game-1))+i];
   ptr=(ULONG)RAM[0xFF];
   ptr2=(ULONG)RAM[0x41];
   switch (game) {
    case 2: /* Altered Beast */
      base_spr_regist=(ULONG)RAM[0x44];
      base_scr_hor_fg=ptr2+0x0E98;
      base_scr_hor_bg=ptr2+0x0E9A;
      base_scr_ver_fg=ptr2+0x0E91;
      base_scr_ver_bg=ptr2+0x0E93;
      base_scr_pag_fg=ptr2+0x0E81;
      base_scr_pag_bg=ptr2+0x0E83;
      base_brq_page=ptr+0xF095;
      reg_scr_active=0xF018;
      TimerA=&RAM[0xFF][0xF01C];
      TimerB=&RAM[0xFF][0xF01E];
      break;
    case 3: /* Golden Axe */
      ptr2=(ULONG)RAM[0x11];
      base_spr_regist=(ULONG)RAM[0x20];
      base_scr_hor_fg=ptr2+0x0E98;
      base_scr_hor_bg=ptr2+0x0E9A;
      base_scr_ver_fg=ptr2+0x0E91;
      base_scr_ver_bg=ptr2+0x0E93;
      base_scr_pag_fg=ptr2+0x0E81;
      base_scr_pag_bg=ptr2+0x0E83;
      reg_scr_active=0xF018;
      base_brq_page=(ULONG)&RAM[0x1F][0x2003];
      break;
    case 5:
      base_spr_regist=(ULONG)RAM[0x44];
      base_scr_hor_fg=ptr+0xCD14;
      base_scr_hor_bg=ptr+0xCD18;
      base_scr_ver_fg=ptr+0xFFF0;
      base_scr_ver_bg=ptr+0xFFF0;
      base_scr_pag_fg=ptr+0xCD1D;
      base_scr_pag_bg=ptr+0xCD1F;
      TimerA=&RAM[0xFF][0xC800];
      break;
    case 6:
      base_spr_regist=(ULONG)RAM[0x44];
      ptr_text_video=(ULONG)RAM[0x41];
      /* foreground */
      base_scr_hor_fg=ptr2+0x0E98;
      base_scr_ver_fg=ptr2+0x0E91;
      base_scr_pag_fg=ptr2+0x0E81;//E81
      /* background */
      base_scr_hor_bg=ptr2+0x0E9E;//E9A//E9E
      base_scr_ver_bg=ptr2+0x0E97;//E93//E97
      base_scr_pag_bg=ptr2+0x0E87;//E83//E87
//      base_brq_page=(ULONG)&fake_page;
      base_brq_page=(ULONG)&RAM[0xC0][0x0001];
      break;
    case 4:
      reg_scr_active=0xC010;
      base_spr_regist=(ULONG)RAM[0x44];
      base_scr_hor_fg=ptr2+0x0E98;
      base_scr_hor_bg=ptr2+0x0E9A;
      base_scr_ver_fg=ptr2+0x0E91;
      base_scr_ver_bg=ptr2+0x0E93;
      base_scr_pag_fg=ptr2+0x0E81;
      base_scr_pag_bg=ptr2+0x0E83;
      base_brq_page=(ULONG)&fake_page;
      break;
    default: /* Shinobi */
      base_spr_regist=ptr+0xF800;
      base_scr_hor_fg=ptr2+0x0E98;
      base_scr_hor_bg=ptr2+0x0E9A;
      base_scr_ver_fg=ptr2+0x0E91;
      base_scr_ver_bg=ptr2+0x0E93;
      base_scr_pag_fg=ptr2+0x0E81;
      base_scr_pag_bg=ptr2+0x0E83;
      base_brq_page=(ULONG)&fake_page;
      TimerA=&RAM[0xFF][0xF01C];
      TimerB=&RAM[0xFF][0xF01E];
      reg_scr_active=0xF018;
      break;
   }
}

void RenderScreen() {
   char status[40];
   RGB pal;
   #ifdef SEARCH
   pal.r=pal.g=pal.b=63;
   set_color(255,(RGB *)&pal);
   sprintf(status, " [%08x]=[%04x]", valeur_controle, get_word(valeur_controle));
/*   sprintf(status ,"BASE=%03x, P=%04x, V=%04x, H=%04x", valeur_controle&0xFFF,
	   get_word(valeur_controle), get_word(valeur_controle+16),
	   get_word(valeur_controle+24));*/
   textout(screen, font, status, 0, 0, 255);
   #endif
   
   if (game<3) {
      if (RAM[0xFF][reg_scr_active]&0x20) asm_render_screen();
   } else asm_render_screen();
   #ifdef SEARCH
   sprintf(status, "R=[%04x]", resultat);
   textout(screen, font, status, 160, 232, 255);
   #endif
   if (number_of_new_colors) Update_Palette();
   if (sync) {
//      while ((inportb(0x3DA)&0x08)!=0);
      while ((inportb(0x3DA)&0x08)==0);
   }
   blit(vscreen, screen, 104, 88, sx, sy, 312, 224);
   total_render++;
}

void Save_SRAM() {
   PACKFILE *f;
   char fn[15];
   sprintf(fn, "%s.ram", game_basename);
   f=pack_fopen(fn, F_WRITE_PACKED);
   if (f!=NULL) {
      pack_fwrite(&RAM[0xFF][0xF000], 0x1000, f);
      pack_fclose(f);
   }
}

void Load_SRAM() {
   PACKFILE *f;
   char fn[15];
   sprintf(fn, "%s.ram", game_basename);
   f=pack_fopen(fn, F_READ_PACKED);
   if (f!=NULL) {
      pack_fread(&RAM[0xFF][0xF000], 0x1000, f);
      pack_fclose(f);
   }
}
/*
UWORD get_word(LONG a) {
   UBYTE bank=(a&0xFF0000)>>16;
   UWORD offs=(a&0xFFFF);
   if (bank==0xC4) CheckInput(offs);
   return((RAM[bank][offs]*256)+RAM[bank][offs+1]);
}
*/
