/***************************************************************************

Daikaijuu no Gyakushuu protection sim

Tomasz Slanina 20060529


Communciation from CPU to MCU is encrypted - every byte
is xored with data taken from cyclic , 22 bytes long buffer.

MCU commands:
=============

 0x19 - unknown
----------------
 - no args
 - no results
Few writes at the end of level (level summary screen).


 0x1d - watchdog?
------------------
 - no args
 - no results


  0x31 - VRAM address 1
-----------------------
 - args = row
          column
 - result = 0xd000 + column*2 + row*64
 Address in text layer.


 0x32 - VRAM address 2
-----------------------
 - args = row
          column
 - result = 0xc000 + column*2 + row*64
 Address in BG layer.


 0x36 - Controls 1
------------------
 - args = data from input port
          previous result
 - result - bits 0-2 = direction ,
   encoded as:
    0
   7 1
  6   2
   5 3
    4
 - result - bit 3 = 0 -> no movement (bits 0-2 = bits 0-2 of previous result
                    1 -> movement
 - result - bit 4 = 0/1 -> button A pressed/released
 - result - bit 5 = previous status of button A
 - result - bit 6 = 0/1 -> button B pressed/released
 - result - bit 7 = previous status of button B

 0x40 - Player message(s) status (not working ?)
-------------------------------------------------
 - args = unk (0/9/d/f (stored at $a020))
          unk (0/1/2 (stored at $a023))
          unk (0/1/2 (stored at $a024)) (2nd and 3rd args are ident.)
 - result = 0/1/2/ (same as 2nd/3rd arg)
Result is discarded due to bug (or patch?) in the code - missing "cp 0"/"or a"/"and a"/etc instruction
to set CPU flags before conditional jump at addr. 0x256 (previous flags are tested, Z is _always_ set
(it's result of testing MCU "data ready to send" flag).
Thic mcu command is used to determine if player message(READY/PLAYER1/PLAYER2) should be
displayed (game) or not (attract mode). Due to above bug message is always displayed
(confirmed on real hw).

 0x44 -  Controls 2
--------------------
- args = result of command 0x36
         previous result of command 44
         step (1 or 2)
- result  - bits 0-3 = direction encoded like in result of command 0x36, but
                       16 steps instead 8. This command calculates all steps between
                       new direction (1st arg) and old one with given step.
                       Example:
                       w (2,0,1)  r (1)
                       w (2,1,1)  r (2)
                       w (2,2,1)  r (3)
                       w (2,3,1)  r (4)
                       w (2,4,1)  r (4)
                       ...
                       w (2,4,1)  r (4)
          - bit 4 = 1 -> result bits 0-3 == prev result bits 0-3, otherwise 0
          - bits 5-7 = unknown , always 0 ?

 0x60 - MCU ID
---------------
 - no args
 - result = 0x5a


***************************************************************************/
#include "driver.h"
#include "cpu/z80/z80.h"

#define ID_DAIKAIJU 0x5a

#define MCU_UNKNOWN   0x19
#define MCU_WATCHDOG  0x1d
#define MCU_ADDRESS1  0x31
#define MCU_ADDRESS2  0x32
#define MCU_CONTROLS1 0x36
#define MCU_MESSAGE   0x40
#define MCU_CONTROLS2 0x44
#define MCU_ID 				0x60

#define MCU_ADDRESS1_LENGTH  2
#define MCU_ADDRESS2_LENGTH  2
#define MCU_CONTROLS1_LENGTH 2
#define MCU_MESSAGE_LENGTH   3
#define MCU_CONTROLS2_LENGTH 3
#define MCU_ID_LENGTH				 0

#define SET_COMMAND(n)	daikaiju_command=data; \
			daikaiju_length=n;\
			daikaiju_cnt=0;\
			daikaiju_cntr=0;


extern int lsasquad_sound_pending;
static int daikaiju_xor, daikaiju_command, daikaiju_length, daikaiju_prev, daikaiju_cnt, daikaiju_cntr;

static int daikaiju_buffer[256];

static int xortable[]=
{
 0xF5, 0xD5, 0x6A, 0x26, 0x00, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x16, 0x00, 0xCB, 0x23, 0x19,
 0x11, 0x00, 0xC0, 0x19, 0xD1, 0xF1, 0xC9, -1
};

MACHINE_RESET(daikaiju)
{
	daikaiju_xor=-1;
	daikaiju_command=0;
	daikaiju_length=0;
}

WRITE8_HANDLER( daikaiju_mcu_w )
{
  if(daikaiju_xor <0)
	{
		//reset
		daikaiju_xor=0;
		data^=0xc3;
	}
	else
	{
		data^=xortable[daikaiju_xor];
		//check for end of table
		if(xortable[++daikaiju_xor]<0)
		{
			daikaiju_xor=0;
		}
	}
	daikaiju_prev=data;

	if(daikaiju_length == 0) //new command
	{
		switch(data)
		{
			case MCU_UNKNOWN:
			case MCU_WATCHDOG: break;
			case MCU_ADDRESS1:  SET_COMMAND(MCU_ADDRESS1_LENGTH); break;
			case MCU_ADDRESS2:  SET_COMMAND(MCU_ADDRESS2_LENGTH); break;
			case MCU_CONTROLS1: SET_COMMAND(MCU_CONTROLS1_LENGTH); break;
			case MCU_MESSAGE: 	SET_COMMAND(MCU_MESSAGE_LENGTH); break;
			case MCU_CONTROLS2: SET_COMMAND(MCU_CONTROLS2_LENGTH); break;
			case MCU_ID: 				SET_COMMAND(MCU_ID_LENGTH); break;
			default:
				daikaiju_command=data;
				logerror("Unknown MCU command W %x %x \n",data,activecpu_get_pc());
		}
	}
	else
	{
		--daikaiju_length;
		daikaiju_buffer[daikaiju_cnt++]=data;
	}
}

READ8_HANDLER( daikaiju_mcu_r )
{
	switch(daikaiju_command)
	{
		case MCU_ID:
			return ID_DAIKAIJU;

		case MCU_CONTROLS1:
 		{
			int n;
			switch( (daikaiju_buffer[0]&0xf)^0xf)
			{
				case 0x00:	n = (daikaiju_buffer[1]&(~8))&0xf; break;
				case 0x08:	n = 0 | 8; break;
				case 0x0a:	n = 1 | 8; break;
				case 0x02:	n = 2 | 8; break;
				case 0x06:	n = 3 | 8; break;
				case 0x04:	n = 4 | 8; break;
				case 0x05:	n = 5 | 8; break;
				case 0x01:	n = 6 | 8; break;
				case 0x09:	n = 7 | 8; break;

				default:		n = 0; break;
			}

			if( !(daikaiju_buffer[0]&0x10) ) //button 1 pressed
			{
				if(daikaiju_buffer[1]&0x10) //previous status
				{
					n|=0x40;	// button 0->1
				}
				else
				{
					n|=0; //button 1->1
				}
			}
			else
			{
				if(daikaiju_buffer[1]&0x10) //previous status
				{
					n|=0x40+0x10;	// button 0->0
				}
				else
				{
					n|=0x10; //button 1->0
				}
			}

			if( !(daikaiju_buffer[0]&0x20) ) //button 2 pressed
			{
				if(daikaiju_buffer[1]&0x20) //previous status
				{
					n|=0x80;	// button 0->1
				}
				else
				{
					n|=0; //button 1->1
				}
			}
			else
			{
				if(daikaiju_buffer[1]&0x20) //previous status
				{
					n|=0x80+0x20;	// button 0->0
				}
				else
				{
					n|=0x20; //button 1->0
				}
			}
			return n;
		}
		return 0;

		case MCU_ADDRESS1:
		{
			int address=daikaiju_buffer[1]*2+64*daikaiju_buffer[0];

			address&=0x0fff; // ?
			address+=0xd000;

			if(daikaiju_cntr==0)
			{
				daikaiju_cntr++;
				return address&0xff;
			}
			else
			{
				return address>>8;
			}
		}
		return 0;

		case MCU_ADDRESS2:
		{
			int address=daikaiju_buffer[1]*2+64*daikaiju_buffer[0];

			address&=0x1fff; // ?
			address+=0xc000;

			if(daikaiju_cntr==0)
			{
				daikaiju_cntr++;
				return address&0xff;
			}
			else
			{
				return address>>8;
			}
		}
		return 0;

		case MCU_MESSAGE:
			return daikaiju_buffer[1];

		case MCU_CONTROLS2:
		{
			int n;
			int dest=(daikaiju_buffer[0]&0x7)<<1;
			int prev=(daikaiju_buffer[1]&0xf);

			if(daikaiju_buffer[2]==2)
			{
				prev&=0xfe;
			}
			if(prev!=dest)
			{
				int diff=(dest-prev);

				if((diff>8 )|| (diff<0 && diff >-8))
				{
					n=(prev-daikaiju_buffer[2])&0xf;
				}
				else
				{
					n=(prev+daikaiju_buffer[2])&0xf;
				}
			}
			else
			{
				n=prev;
			}
			prev&=0xf;
			if(prev!=n)
			{
				n|=0x10;
			}
			return n;
		}
	}
	logerror("Unknown MCU R %x %x %x %x\n",activecpu_get_pc(), daikaiju_command, daikaiju_length, daikaiju_prev);
	return 0xff;
}

READ8_HANDLER( daikaiju_mcu_status_r )
{
	int res = input_port_3_r(0);

	res^=mame_rand()&3;
	res |=((lsasquad_sound_pending & 0x02)^2)<<3; //inverted flag
	lsasquad_sound_pending &= ~0x02;
	return res;
}

