
#include "cinstruction.h"
#include <iostream>

using namespace std ;


CInstruction::CInstruction()
{
	m_cpu = (CPicoBlaze*) 0 ;
}

CInstruction::CInstruction( CPicoBlaze *cpu, uint32_t opcode )
{
	m_cpu = cpu ;

	sX = ( opcode & 0x0f00 ) >> 8 ;
	sY = ( opcode & 0x00f0 ) >> 4 ;
	kk = ( opcode & 0x00ff ) >> 0 ;
	pp = ( opcode & 0x00ff ) >> 0 ;
	ss = ( opcode & 0x003f ) >> 0 ;
	address = ( opcode & 0x03ff ) >> 0 ;
	
	hexcode = opcode ;
}

CInstruction::~CInstruction()
{
}

void CInstruction::Print()
{
	cout << "Unknown instruction" ;
}

void ADD_SX_KK::Execute() 
{
	uint16_t val = m_cpu->s[ sX ] + kk ;
	
	m_cpu->flags.carry = ( val > 255 ) ;
	m_cpu->flags.zero = ( val == 0 ) || ( val == 256 ) ;
	
	m_cpu->s[ sX ] = val ; 
	m_cpu->pc->Next() ;
}

void ADD_SX_KK::Print()
{
	cout << "ADD " << "s" << sX << "," << kk ;
}

void ADD_SX_SY::Execute() 
{
	uint16_t val = m_cpu->s[ sX ] + m_cpu->s[ sY ] ;
	
	m_cpu->flags.carry = ( val > 255 ) ;
	m_cpu->flags.zero = ( val == 0 ) || ( val == 256 ) ;
	
	m_cpu->s[ sX ] = val ; 
	m_cpu->pc->Next() ;
	
}

void ADD_SX_SY::Print()
{
	cout << "ADD " << "s" << sX << "," << "s" << sY ;
}

void ADDCY_SX_KK::Execute()
{
	uint16_t val = m_cpu->s[ sX ] + 1 + kk ;
	
	if ( m_cpu->flags.carry ) 
		val = m_cpu->s[ sX ] + 1 + kk ;
	else
		val = m_cpu->s[ sX ] + kk ;
	
	m_cpu->s[ sX ] = val ;
	m_cpu->flags.carry = (val > 255)  ;
	m_cpu->flags.zero = (val == 0) || (val == 256) ;
		
	m_cpu->pc->Next() ;
}

void ADDCY_SX_KK::Print()
{
	cout << "ADDCY " << "s" << sX << "," << sY ;
}

void ADDCY_SX_SY::Execute()
{
	uint16_t val ;

	if ( m_cpu->flags.carry ) 
		val = m_cpu->s[ sX ] + 1 + m_cpu->s[ sY ] ;
	else
		val = m_cpu->s[ sX ] + m_cpu->s[ sY ] ;
	
	m_cpu->s[ sX ] = val ;
	m_cpu->flags.carry = (val > 255)  ;
	m_cpu->flags.zero = (val == 0) || (val == 256) ;
		
	m_cpu->pc->Next() ;
}

void ADDCY_SX_SY::Print()
{
	cout << "ADDCY " << "s" << sX << "," << "s" << sY ;
}

void AND_SX_KK::Execute()
{

	m_cpu->s[ sX ] = m_cpu->s[ sX ] & kk ;
	m_cpu->flags.carry = 0 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;

	m_cpu->pc->Next() ;
}

void AND_SX_KK::Print()
{
	cout << "AND " << "s" << sX << "," << sY ;
}

void AND_SX_SY::Execute()
{

	m_cpu->s[ sX ] = m_cpu->s[ sX ] & m_cpu->s[ sY ] ;
	m_cpu->flags.carry = 0 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;

	m_cpu->pc->Next() ;
}

void AND_SX_SY::Print()
{
	cout << "AND " << "s" << sX << "," << "s" << sY ;
}

void CALL::Execute()
{
	m_cpu->stack->Push( ( m_cpu->pc->Get() + 1)  % 0x400 ) ;
	m_cpu->pc->Set( address ) ;
}

void CALL::Print()
{
	cout << "CALL " << address ;
}

void CALLC::Execute()
{
	if ( m_cpu->flags.carry ) {
		m_cpu->stack->Push( m_cpu->pc->Get() ) ;
		m_cpu->pc->Set( address ) ;
	} else
		m_cpu->pc->Next() ;
}

void CALLC::Print()
{
	cout << "CALL C " << address ;
}

void CALLNC::Execute()
{
	if ( !m_cpu->flags.carry ) {
		m_cpu->stack->Push( m_cpu->pc->Get() ) ;
		m_cpu->pc->Set( address ) ;
	} else
		m_cpu->pc->Next() ;
}

void CALLNC::Print()
{
	cout << "CALL NC " << address ;
}

void CALLNZ::Execute()
{
	if ( !m_cpu->flags.zero ) {
		m_cpu->stack->Push( m_cpu->pc->Get() ) ;
		m_cpu->pc->Set( address ) ;
	} else
		m_cpu->pc->Next() ;
}

void CALLNZ::Print()
{
	cout << "CALL NZ " << address ;
}

void CALLZ::Execute()
{
	if ( m_cpu->flags.zero ) {
		m_cpu->stack->Push( m_cpu->pc->Get() ) ;
		m_cpu->pc->Set( address ) ;
	} else
		m_cpu->pc->Next() ;
}

void CALLZ::Print()
{
	cout << "CALL Z " << address ;
}


void COMPARE_SX_KK::Execute()
{
	m_cpu->flags.carry = kk > m_cpu->s[ sX ] ;
	m_cpu->flags.zero = kk == m_cpu->s[ sX ] ;

	m_cpu->pc->Next() ;	
}

void COMPARE_SX_KK::Print()
{
	cout << "COMPARE s" << sX << ", "  << kk ;
}

void COMPARE_SX_SY::Execute()
{
	m_cpu->flags.carry = m_cpu->s[ sY ] > m_cpu->s[ sX ] ;
	m_cpu->flags.zero = m_cpu->s[ sY ] == m_cpu->s[ sX ] ;

	m_cpu->pc->Next() ;	
}

void COMPARE_SX_SY::Print()
{
	cout << "COMPARE s" << sX << ", s" << kk ;
}

void DISABLE_INTERRUPT::Execute()
{
	m_cpu->flags.interrupt_enable = false ;
	m_cpu->pc->Next() ;
}

void DISABLE_INTERRUPT::Print()
{
	cout << "DISABLE INTERRUPT" ;
}

void ENABLE_INTERRUPT::Execute()
{
	m_cpu->flags.interrupt_enable = true ;
	m_cpu->pc->Next() ;
}

void ENABLE_INTERRUPT::Print()
{
	cout << "ENABLE INTERRUPT" ;
}

void FETCH_SX_SS::Execute()
{
	m_cpu->s[ sX ] = m_cpu->scratch->Get( ss ) ;
	m_cpu->pc->Next() ;
}

void FETCH_SX_SS::Print()
{
	cout << "FETCH " << "s" << sX << ", " << ss ;
}

void FETCH_SX_SY::Execute()
{
	m_cpu->s[ sX ] = m_cpu->scratch->Get( m_cpu->s[ sY ] & 0x3f ) ;
	m_cpu->pc->Next() ;
	
	
}

void FETCH_SX_SY::Print() {
	cout << "FETCH " << "s" << sX << ", " << "s" << sY ;
}

void INPUT_SX_SY::Execute()
{
	m_cpu->port->PortID( m_cpu->s[ sY ] ) ;
	m_cpu->s[ sX ] = m_cpu->port->PortIn() ;
	m_cpu->pc->Next() ;
}

void INPUT_SX_SY::Print()
{
	cout << "INPUT " << "s" << sX << ", " << "s" << sY ;
}

void INPUT_SX_PP::Execute()
{
	m_cpu->port->PortID( pp ) ;
	m_cpu->s[ sX ] = m_cpu->port->PortIn() ;
	m_cpu->pc->Next() ;
}

void INPUT_SX_PP::Print()
{
	cout << "INPUT " << "s" << sX << ", " << pp ;
}

void JUMP::Execute()
{
	m_cpu->pc->Set( address ) ;
}

void JUMP::Print()
{
	cout << "JUMP " << address ;
}

void JUMPC::Execute()
{
	if ( m_cpu->flags.carry )
		m_cpu->pc->Set( address ) ;
	else
		m_cpu->pc->Next() ;
}

void JUMPC::Print()
{
	cout << "JUMP C " << address ;
}

void JUMPNC::Execute()
{
	if ( !m_cpu->flags.carry )
		m_cpu->pc->Set( address ) ;
	else
		m_cpu->pc->Next() ;
}

void JUMPNC::Print()
{
	cout << "JUMP NC " << address ;
}

void JUMPNZ::Execute()
{
	if ( !m_cpu->flags.zero )
		m_cpu->pc->Set( address ) ;
	else
		m_cpu->pc->Next() ;
}

void JUMPNZ::Print()
{
	cout << "JUMP NZ " << address ;
}

void JUMPZ::Execute()
{
	if ( m_cpu->flags.zero )
		m_cpu->pc->Set( address ) ;
	else
		m_cpu->pc->Next() ;
}

void JUMPZ::Print()
{
	cout << "JUMP Z " << address ;
}

void LOAD_SX_KK::Execute()
{
	m_cpu->s[ sX ] = kk ;
	m_cpu->pc->Next() ;
}

void LOAD_SX_KK::Print()
{
	cout << "LOAD " << "s" << sX << ", " << kk ;
}

void LOAD_SX_SY::Execute()
{
	m_cpu->s[ sX ] = m_cpu->s[ sY ] ;
	m_cpu->pc->Next() ;
}

void LOAD_SX_SY::Print()
{
	cout << "LOAD " << "s" << sX << ", " << "s" << sY ;
}

void OR_SX_KK::Execute()
{
	m_cpu->s[ sX ] = m_cpu->s[ sX ] | kk ;
	m_cpu->flags.carry = 0 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void OR_SX_KK::Print()
{
	cout << "OR " << "s" << sX << ", " << kk ;
}

void OR_SX_SY::Execute()
{
	m_cpu->s[ sX ] = m_cpu->s[ sX ] | m_cpu->s[ sY ] ;
	m_cpu->flags.carry = 0 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void OR_SX_SY::Print()
{
	cout << "OR " << "s" << sX << ", " << "s" << sY ;
}

void OUTPUT_SX_SY::Execute()
{
	m_cpu->port->PortID( m_cpu->s[ sY ] ) ;
	m_cpu->port->PortOut( m_cpu->s[ sX ] ) ;
	m_cpu->pc->Next() ;
}

void OUTPUT_SX_SY::Print()
{
	cout << "OUTPUT " << "s" << sX << ", " << "s" << sY ;
}

void OUTPUT_SX_PP::Execute()
{
	m_cpu->port->PortID( pp ) ;
	m_cpu->port->PortOut( m_cpu->s[ sX ] ) ;
	m_cpu->pc->Next() ;
}

void OUTPUT_SX_PP::Print()
{
	cout << "OUTPUT " << "s" << sX << ", " << pp ;
}

void RETURN::Execute()
{
	m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
}

void RETURN::Print()
{
	cout << "RETURN" ;
}

void RETURNC::Execute()
{
	if ( m_cpu->flags.carry )
		m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
	else
		m_cpu->pc->Next() ;
}

void RETURNC::Print()
{
	cout << "RETURN C" ;
}

void RETURNNC::Execute()
{
	if ( !m_cpu->flags.carry )
		m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
	else
		m_cpu->pc->Next() ;
}

void RETURNNC::Print()
{
	cout << "RETURN NC" ;
}

void RETURNNZ::Execute()
{
	if ( !m_cpu->flags.zero )
		m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
	else
		m_cpu->pc->Next() ;
}

void RETURNNZ::Print()
{
	cout << "RETURN NZ" ;
}

void RETURNZ::Execute()
{
	if ( m_cpu->flags.zero )
		m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
	else
		m_cpu->pc->Next() ;
}

void RETURNZ::Print()
{
	cout << "RETURN Z" ;
}

void RETURNI_DISABLE::Execute()
{
	m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
	m_cpu->flags.carry = m_cpu->flags.preserved_carry ;
	m_cpu->flags.zero = m_cpu->flags.preserved_zero ;
	m_cpu->flags.interrupt_enable = false ;
}

void RETURNI_DISABLE::Print()
{
	cout << "RETURNI DISABLE" ;
}

void RETURNI_ENABLE::Execute()
{
	m_cpu->pc->Set( m_cpu->stack->Pop() ) ;
	m_cpu->flags.carry = m_cpu->flags.preserved_carry ;
	m_cpu->flags.zero = m_cpu->flags.preserved_zero ;
	m_cpu->flags.interrupt_enable = true ;
}

void RETURNI_ENABLE::Print()
{
	cout << "RETURNI ENABLE" ;
}

void RL_SX::Execute()
{
	m_cpu->flags.carry = ( m_cpu->s[ sX ] & 0x80 ) != 0 ;

	m_cpu->s[ sX ] <<= 1 ;
	if ( m_cpu->flags.carry )
		m_cpu->s[ sX ] |= 1 ;

	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void RL_SX::Print()
{
	cout << "RL s" << sX ;
}

void RR_SX::Execute()
{
	m_cpu->flags.carry = ( m_cpu->s[ sX ] & 0x01 ) != 0 ;

	m_cpu->s[ sX ] >>= 1 ;
	if ( m_cpu->flags.carry )
		m_cpu->s[ sX ] |= 0x80 ;

	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void RR_SX::Print()
{
	cout << "RR s" << sX ;
}


void SL0_SX::Execute()
{
	m_cpu->flags.carry = ( m_cpu->s[ sX ] & 0x80 ) != 0 ;

	m_cpu->s[ sX ] <<= 1 ;

	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SL0_SX::Print()
{
	cout << "SL0 s" << sX ;
}

void SL1_SX::Execute()
{
	m_cpu->flags.carry = ( m_cpu->s[ sX ] & 0x80 ) != 0 ;

	m_cpu->s[ sX ] <<= 1 ;
	m_cpu->s[ sX ] |= 1 ;

	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SL1_SX::Print()
{
	cout << "SL1 s" << sX ;
}


void SLA_SX::Execute()
{
	bool c ;

	c = m_cpu->flags.carry ;

	m_cpu->flags.carry = ( m_cpu->s[ sX ] & 0x80 ) != 0 ;

	m_cpu->s[ sX ] <<= 1 ;
	if ( c )
		m_cpu->s[ sX ] |= 1 ;
	
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SLA_SX::Print()
{
	cout << "SLA s" << sX ;
}


void SLX_SX::Execute()
{
	m_cpu->flags.carry = ( m_cpu->s[ sX ] & 0x80 ) != 0 ;

	m_cpu->s[ sX ] <<= 1 ;
	if ( m_cpu->s[ sX ] & 0x02 )
		m_cpu->s[ sX ] |= 1 ;
	
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SLX_SX::Print()
{
	cout << "SLX s" << sX ;
}


void SR0_SX::Execute()
{
	m_cpu->flags.carry = m_cpu->s[ sX ] & 0x01 ;
	m_cpu->s[ sX ] >>= 1 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SR0_SX::Print()
{
	cout << "SR0 s" << sX ;
}


void SR1_SX::Execute()
{
	m_cpu->flags.carry = m_cpu->s[ sX ] & 0x01 ;
	m_cpu->s[ sX ] >>= 1 ;
	m_cpu->s[ sX ] |= 0x80 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SR1_SX::Print()
{
	cout << "SR1 s" << sX ;
}


void SRA_SX::Execute()
{
	bool c = m_cpu->flags.carry ;
	m_cpu->flags.carry = m_cpu->s[ sX ] & 0x01 ;
	m_cpu->s[ sX ] >>= 1 ;
	if ( c )
		m_cpu->s[ sX ] |= 0x80 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SRA_SX::Print()
{
	cout << "SRA s" << sX ;
}


void SRX_SX::Execute()
{
	m_cpu->flags.carry = m_cpu->s[ sX ] & 0x01 ;
	m_cpu->s[ sX ] >>= 1 ;
	if ( m_cpu->s[ sX ] & 0x40  )
		m_cpu->s[ sX ] |= 0x80 ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void SRX_SX::Print()
{
	cout << "SRX s" << sX ;
}


void STORE_SX_SS::Execute()
{
	m_cpu->scratch->Set( ss, m_cpu->s[ sX ] ) ;
	m_cpu->pc->Next() ;

}

void STORE_SX_SS::Print()
{
	cout << "STORE s" << sX << ", " << ss ;
}


void STORE_SX_SY::Execute()
{
	m_cpu->scratch->Set( m_cpu->s[ sY ], m_cpu->s[ sX ] ) ;
	m_cpu->pc->Next() ;
}

void STORE_SX_SY::Print()
{
	cout << "STORE s" << sX << ", s" << sY ;
}

void SUB_SX_KK::Execute()
{
	int val ;
	
	val = m_cpu->s[ sX ] ;
	val -= kk ;

	m_cpu->flags.carry = val < 0 ;
	m_cpu->flags.zero = val == 0 ;
	
	m_cpu->s[ sX ] -= kk ;

	m_cpu->pc->Next() ;
}

void SUB_SX_KK::Print()
{
	cout << "SUB s" << sX << ", " << kk ;
}

void SUB_SX_SY::Execute()
{
	int val ;
	
	val = m_cpu->s[ sX ] ;
	val -= m_cpu->s[ sY ] ;

	m_cpu->flags.carry = val < 0 ;
	m_cpu->flags.zero = val == 0 ;
	
	m_cpu->s[ sX ] -= m_cpu->s[ sY ] ;

	m_cpu->pc->Next() ;
}

void SUB_SX_SY::Print()
{
	cout << "SUB s" << sX << ", s" << sY ;
}


void SUBCY_SX_KK::Execute()
{
	int val ;
	bool c = m_cpu->flags.carry ;	
	val = m_cpu->s[ sX ] ;
	val -= kk ;
	if ( c )
		val -= 1 ;

	m_cpu->flags.carry = val < 0 ;
	m_cpu->flags.zero = val == 0 ;

	m_cpu->s[ sX ] = val ;

	m_cpu->pc->Next() ;
}

void SUBCY_SX_KK::Print()
{
	cout << "SUBCY s" << sX << ", " << kk ;
}

void SUBCY_SX_SY::Execute()
{
	int val ;
	bool c = m_cpu->flags.carry ;	
	val = m_cpu->s[ sX ] ;
	val -= m_cpu->s[ sY ] ;
	if ( c )
		val -= 1 ;

	m_cpu->flags.carry = val < 0 ;
	m_cpu->flags.zero = val == 0 ;
	m_cpu->s[ sX ] = val ;

	m_cpu->pc->Next() ;
}

void SUBCY_SX_SY::Print()
{
	cout << "SUBCY s" << sX << ", s" << sY ;
}

void TEST_SX_KK::Execute()
{
	uint8_t and_test = ( m_cpu->s[ sX ] & kk ) ;
	m_cpu->flags.zero = and_test == 0 ;

	int i ;

	uint8_t xor_test = 0, b ;

	for ( i = 0 ; i < 7 ; i++ ) {
		b = ( and_test & ( 1 << i ) ) != 0 ; 
		xor_test = b ^ xor_test ;	
	}

	m_cpu->flags.carry = xor_test != 0 ;
	m_cpu->pc->Next() ;
}

void TEST_SX_KK::Print()
{
	cout << "TEST s" << sX << ", " << kk ;
}

void TEST_SX_SY::Execute()
{
	uint8_t and_test = ( m_cpu->s[ sX ] & m_cpu->s[ sY ] ) ;
	m_cpu->flags.zero = and_test == 0 ;

	int i ;

	uint8_t xor_test = 0, b ;

	for ( i = 0 ; i < 7 ; i++ ) {
		b = ( and_test & ( 1 << i ) ) != 0 ; 
		xor_test = b ^ xor_test ;	
	}

	m_cpu->flags.carry = xor_test != 0 ;
	m_cpu->pc->Next() ;
}

void TEST_SX_SY::Print()
{
	cout << "TEST s" << sX << ", s" << sY ;
}

void XOR_SX_KK::Execute()
{
	m_cpu->s[ sX ] ^= kk ;
	
	m_cpu->flags.carry = false ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void XOR_SX_KK::Print()
{
	cout << "XOR s" << sX << ", " << kk ;
}


void XOR_SX_SY::Execute()
{
	m_cpu->s[ sX ] ^= m_cpu->s[ sY ] ;
	
	m_cpu->flags.carry = false ;
	m_cpu->flags.zero = m_cpu->s[ sX ] == 0 ;
	m_cpu->pc->Next() ;
}

void XOR_SX_SY::Print()
{
	cout << "XOR s" << sX << ", s" << sY ;
}

void RESET_EVENT::Execute() 
{
	m_cpu->pc->Set( 0 ) ;
	m_cpu->flags.interrupt_enable = false ;
	m_cpu->flags.zero = false ;
	m_cpu->flags.carry = false ;
	m_cpu->stack->Reset() ;
} 

void RESET_EVENT::Print()
{
	cout << "(RESET EVENT)" ;
}

void INTERRUPT_EVENT::Execute()
{
	if ( m_cpu->flags.interrupt_enable ) {
		m_cpu->flags.interrupt_enable = false ;
		m_cpu->stack->Push( m_cpu->pc->Get() ) ;
		m_cpu->flags.preserved_carry = m_cpu->flags.carry ;
		m_cpu->flags.preserved_zero = m_cpu->flags.zero ;
		m_cpu->pc->Set( 0x3FF ) ;	
	}
}

void INTERRUPT_EVENT::Print()
{
	cout << "(INTERRUPT EVENT)" ;
}

