/***************************************************************************
 *   Copyright (C) 2008 by Jürgen Böhm   *
 *   juergen.boehm@aviduratas.de   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "simemu.h"
#include "qjbdisplay.h"
#include "emuprocessor.h"


using namespace std;



EmuProcessor::~EmuProcessor() {

			delete[] pmainmem;
			delete[] pvgamem;

		};

char* EmuProcessor::allocateMem(int size) {

char* p = new char[size];

if (!p)
	throw bad_alloc ();

return p;


};

R32 EmuProcessor::getmemword (char* p, int addr ) {

R32 res = ((R32)((unsigned char)p[addr+3]))
		 + ( 
					( ((R32)((unsigned char)p[addr+2])) + 
						( ((R32)((unsigned char)p[addr+1]))
				 				+ ( ((R32)((unsigned char)p[addr]))*256 ) )*256 )*256);


return res;

}


R32 EmuProcessor::setmemword (char *p, int addr, R32 val) {

R32 val0 = val;

p[addr+3] = (char)(val & 255);
val = val>>8;
p[addr+2] = (char)(val & 255);
val = val>>8;
p[addr+1] = (char)(val & 255);
val = val>>8;
p[addr] = (char)(val & 255);

return val0;

}

R32 EmuProcessor::getmemwordA ( int addr ) {

	return getmemword ( pmainmem, addr );

}

R32 EmuProcessor::setmemwordA (int addr, R32 val ) {

	return setmemword ( pmainmem, addr, val );

}

R32 EmuProcessor::setmemwordAdirect (int addr, R32 val) {

char* p = pmainmem;

p[addr] = (char)(val & 255);
val = val>>8;
p[addr+1] = (char)(val & 255);
val = val>>8;
p[addr+2] = (char)(val & 255);
val = val>>8;
p[addr+3] = (char)(val & 255);

return val;


};


R32 EmuProcessor::pop_cs() {

R32 val = getmemwordA ( csp );
csp += 4;

return val;

}

void EmuProcessor::push_cs(R32 val) {

csp -= 4;
setmemwordA(csp, val);

}

void EmuProcessor::push0_ds(R32 val) {

tos2 = tos;
tos = val;

dsp -= 4;

setmemword (pmainmem, dsp, tos);

}


void EmuProcessor::push1_ds(R32 val) {

	tos = val;

	setmemword (pmainmem, dsp, val);
//	tos2 = getmemword (pmainmem, dsp + 4);
	
}


void EmuProcessor::push2_ds(R32 val) {

	dsp += 4;
	tos = val;

	setmemword (pmainmem, dsp, val);
	tos2 = getmemword (pmainmem, dsp + 4);
	
}

void EmuProcessor::push3_ds(R32 val) {

	dsp += 8;
	tos = val;

	setmemword (pmainmem, dsp, val);
	tos2 = getmemword (pmainmem, dsp + 4);
	
}



void EmuProcessor::execute (int adsp, int acsp, int steps) {

	int halt = 0;
	int irq;
	int wait;

	assert ( adsp == 8192 );
	assert ( acsp == 4096 );

	instrCnt = 0;

	dsp = adsp;
	csp = acsp;

	steps = 0;

	haltFlag = false;

	startupSequence ();


	while ((! halt)&&(! breakFlag)) {


		irq = irqDispatcher.getIrq ();

//		if (irq)
//			cerr << "irq = " << irq << endl;

		if (!haltFlag) {
			halt = executeInstr (irq, wait);
			instrCnt += 4;
		};
		
		if (steps++ == 8192) {
			qApp->processEvents();
			steps = 0;
		};

	}

	breakFlag = false;

}

void EmuProcessor::startupSequence() {

	regC = getmemwordA ( 0 );

	cerr << "NilP = " << regC << endl << endl;

	tp = getmemwordA ( 4 ) & AddrMask;

	cerr << "tp = " << tp << endl << endl;


	litbase = tp + 16;
	
	pc = getmemwordA ( tp + 4 );

	regD = getmemwordA ( 8 );

	push1_ds ( regC );

	tos = getmemwordA(dsp);
	tos2 = getmemwordA (dsp + 4 );


	status = (1<<2);

	cerr << "dsp = " << dsp << endl << endl;

	cerr << "(tp+4) = " << getmemword ( pmainmem, tp+4) << endl;



}

QString destruct (R32 ir) {

	QString res;

	R32 d = ((R32)((short int)(ir & LowMask)));

	R32 il = ((ir & LUMask)>>16) & LowMask;
	R32 ih = ((ir & HUMask)>>24) & LowMask;

	res = QString::number (ih) +  " " + QString::number(il) + " " + QString::number(d);

	return res;

}

int EmuProcessor::irqAccepted (int irq) {

irq_akt = irq;

int res = ((status & 4) && ((irq) > ((status>>3) & 7))); 

return res;

}

int aux_cnt_0;

int in_irq_flag = 0;

void EmuProcessor::statusIrqIn () {

	aux_cnt_0 = 0;	
	in_irq_flag = 1;

	status = (((status & (~(7<<6)))) & (~(7<<3))) | ((status & (7<<3))<<3);
	status = status | (irq_akt<<3);
	status = status & ~(1<<2);

	tosold = tos;

	cerr << "instr = " << destruct (ir) << endl;

	cerr << "entering IRQ: status = " << status << " dsp = " << dsp << endl;
//	cerr << "dsp = " << dsp << endl;


}

void EmuProcessor::statusIrqOut () {

	in_irq_flag = 0;

	status = status & (~(1<<9));
	status = status & (~(7<<3));
	status = status | ((status & (7<<6))>>3);
	status = status | (1<<2);

	assert (tosold == tos);

	cerr << "Leaving IRQ. steps = " << aux_cnt_0 << endl;
	cerr << "status = " << status << " dsp = " << dsp << endl;


} 

void showInstr ( EmuProcessor* ep, R32 ir ) {

	QString ir_string = destruct (ir);

  cerr << "ir = " << ir_string << endl;

  cerr << "tos = " << ep->tos << " " <<ep->getmemwordA(ep->dsp) << " dsp = " << ep->dsp << endl;

	cerr << "tos2 = " << ep->tos2 << " " << ep->getmemwordA(ep->dsp+4) << endl;

	cerr << "tos3 = " << ep->getmemwordA (ep->dsp+8) << endl;

}




int EmuProcessor::executeInstr(int irq, int wait) {

	ir = getmemword ( pmainmem, pc );

//	if (dsp >= 8188) {
//		showInstr(this, ir);
//		assert (dsp <= 8192);
//	};

	assert ( tos == getmemwordA(dsp) );
	assert ( tos2 == getmemwordA (dsp + 4) );

	d = ((R32)((short int)(ir & LowMask)));

	il = ((ir & LUMask)>>16) & LowMask;
	ih = ((ir & HUMask)>>24) & LowMask;

	pc_next = pc + 4;

	aux_cnt_0++;

	

	if (irqAccepted (irq)) {

		IRQCALLCLOS ();

  } else	if (il == 32) {

		(this->*opcode_2_table[ih])();

	} else if (il == 33) {

		(this->*opcode_1_table[ih])();

	} else {

		(this->*function_table[il])();

	}

	pc = pc_next;

	return (il == 30) ? 1 : 0;

}

