/*	Copyright  (c)	Günter Woigk 2013 - 2015
					mailto:kio@little-bat.de

	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.

	Permission to use, copy, modify, distribute, and sell this software and
	its documentation for any purpose is hereby granted without fee, provided
	that the above copyright notice appear in all copies and that both that
	copyright notice and this permission notice appear in supporting
	documentation, and that the name of the copyright holder not be used
	in advertising or publicity pertaining to distribution of the software
	without specific, written prior permission.  The copyright holder makes no
	representations about the suitability of this software for any purpose.
	It is provided "as is" without express or implied warranty.

	THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
	INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
	EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
	CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
	DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
	TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
	PERFORMANCE OF THIS SOFTWARE.
*/


#include "kio/kio.h"
#include "Cpu.h"
#include "unix/os_utilities.h"
#include "unix/FD.h"
#include "Sio.h"
#include "math.h"
#include "Templates/StrArray.h"
#include "unix/pthreads.h"
#include "util.h"
#include "kio/util/msbit.h"







/*	Data Bus:

	cmd_clk	 =0,	ival_oe=0,
	mem		 =1,
	io		 =2,
	axd_oe	 =3,	dnop_clk=3,
	d0		 =4,
	d1		 =5,
	d2		 =6,
	d3		 =7,	dtmp=7,
	alu		 =8,
	dreg_9	 =9,	// unused
	swop	 =10,
	dreg_11	 =9,	// unused
	sr		 =12,
	sl		 =13,
	msbit	 =14,
	cmd_lo_oe=15,	d2ar_clk=15

	Address Bus:

	ip		=0,
	dxa_oe	=1,		anop_clk=1,
	sp		=2,
	a0		=3,
	a1		=4,
	hp		=5,
	atmp	=6,
	d2ar_oe =7,		ioaddr_clk=7
*/


#define mem_oe		(1<<0)
#define dxa_oe		(1<<8)
#define ip_oe		(0<<8)
#define anop_clk	(1<<11)

#define	dreg_oe_mask	(15<<0)
#define	dreg_clk_mask	(15<<4)
#define	areg_oe_mask	(7<<8)
#define	areg_clk_mask	(7<<11)

#define	BIT(N,B)		(((N)>>(B))&1)										// get bit value at bit position B


uint verbose=0;
uint debug=0;
int warnings=0;

const uint32 XTAL = 16000000;




void* run_run ( void* arg ) { Cpu* cpu = (Cpu*)arg; cpu->run();  return NULL; }

void Cpu::run()
{
	logline("executing microcode");

	for(;;)
	{
		double when = now();
		double cc = this->cc;

		while(state==running)
		{
			cc += this->xtal/128;		// muss besser als xtal/100 sein wg. system timer irpt mit 100 Hz
			while(cc >= XTAL) { cc -= XTAL; this->cc -= XTAL; } 	// limit cc to nominal XTAL ((16e6))

			run(cc,yes/*stop_on_breakpoints*/);
			waitUntil(when+=1.0/128);
		}

		waitUntil(when+=0.1);
	}
}


// ================================================================
// ctor, dtor, initialize
// ================================================================


Cpu::Cpu()
{
	k1bus_select_bits		= 0;		// K1 bus device select mask
	k1bus_irpt_enable_bits  = 0;		// K1 bus interrupt enable mask
	k1bus_irpt_pending_bits = 0;

//	breakpoint_read  = -1;
//	breakpoint_write = -1;
	breakpoint_exec  = -1;
	breakpoint_return= -1;

	k1bus_selected_device = NULL;
	memset(k1bus_devices,0,sizeof(k1bus_devices));
}

Cpu::~Cpu()
{
}


void Cpu::init(cstr microcodefile, cstr labelsfile, cstr sio_eepromfile)
{
	assert(microcodefile);
	assert(labelsfile);
	assert(sio_eepromfile);

// read labels:

	logline("reading labels");

	FD fd(labelsfile,'r');
	uint32 filesize = fd.file_size();
	str s = newstr(filesize);
	fd.read_bytes(s,filesize);
	fd.close_file(0);
	StrArray ss = split(s);
	delete[] s;
	ss.drop();

	for(uint i=0;i<ss.count();i++)
	{
		s = ss[i];

		// Beispiel:
		// "DivuPoke        = $7D13         [2--0] ( int int& -- )"

		cptr js = strchr(s,'='); if(!js) continue;
		if(*(js+1)!=' ' || *(js+2)!='$') continue;	// z.B. unresolved label

		int j = js-s;
		uint v = evalhex(substr(s+j+3,s+j+7));
		if(((v&0x4000)==0) && (v&0xFF)) continue;	// z.B. INCR = ADDQ+1
		v = (v&0x3FFF) + ((v>>1)&0x4000);

		j = min(j,int(strchr(s,' ')-s));
		label[v] = leftstr(s,j);
	}

// read microcode:

	logline("reading microcode");

	fd.open_file_r(microcodefile);
	filesize = fd.file_size();
	s = tempstr(filesize);
	fd.read_bytes(s,filesize);
	fd.close_file(0);
	ss = split(s);
	memset(mc,0xFF,sizeof(mc));

	for(uint i=0; i < ss.count(); i++)
	{
		cstr l = ss[i];

		// Beispiel:
		// ":3045F000FFFFFFFFFFFF1E0001FFFFFFFFFFFFFFFFFFFFFFFF20CF00FE45F8FFFFFFFFFFFFFFFFFF210F8820C003E0C800FFFFFF2C"
		// total 107 bytes = 1+2+4+2 + 6*16 + 2

		int a = evalhex(substr(l+3,l+7));
		int e = (strlen(l)-2);

		for(int j=9;j<e;j+=6)
		{
			mc[a++] = evalhex(substr(l+j,l+j+6));
		}
	}

// fill ram and registers:

	memset(ram,0,sizeof(ram));
	for(uint i=0;i<0x10000;i++) ram[i].data = random();

	dreg_d0		= random();
	dreg_d1		= random();
	dreg_d2		= random();
	dreg_d3		= random();
	dreg_alu	= random();
	dreg_swap	= random();
	dreg_sr		= random();
	dreg_sl		= random();
	dreg_msbit	= random();
	dreg_cmd_lo	= random();

	areg_ip		= random();
	areg_sp		= random();
	areg_a0		= random();
	areg_a1		= random();
	areg_hp		= random();
	areg_atmp	= random();
	areg_ioaddr	= random();
	reg_d2ar	= random();

// create peripherals:

	k1bus_devices[SIO_ID] = sio = new Sio(this,SIO_ID,sio_eepromfile);

// reset:

	xtal = XTAL;

	mca1 = 0x0000;
	mca2 = 0x7FFF;
	div_ff = 0;

	pthread_create(&tid,NULL,run_run,this);
}



// =========================================================
//	Logging:
// =========================================================

void Cpu::log_z(uint32 mc2, uint16 dbus)
{
	log(" z:$%04X:$%04X=$%04X ", dreg_alu, dbus, get_alu_result(mc2, dbus));
}

void Cpu::log_cy(uint32 mc2, uint16 dbus)
{
	log(" cy:$%04X+%s$%04X%s=$%05X ",
		dreg_alu, mc2&0x20000?"~":"",
		dbus,     mc2&0x10000?"+1":"",     get_alu_cy(mc2, dbus));
}

void Cpu::log_labelstack()
{
	for(uint i=0;i < labelstack.count(); i++)
	{
		log("%s%s",label[labelstack[i]]," >> ");
		if(i%8==7) log("\n    ");
	}
}

void Cpu::log_registers()
{
	cstr s="",t="",u="";

	s = catstr(s, "d0 = $", hexstr(dreg_d0,4), "  ");
	s = catstr(s, "d1 = $", hexstr(dreg_d1,4), "  ");
	s = catstr(s, "d2 = $", hexstr(dreg_d2,4), "  ");
	s = catstr(s, "d3 = $", hexstr(dreg_d3,4), "  ");
	s = catstr(s, "alu = $", hexstr(dreg_alu,4), "  ");
	s = catstr(s, "swap = $", hexstr(dreg_swap,4), "  ");

	t = catstr(t, "sr = $", hexstr(dreg_sr,4), "  ");
	t = catstr(t, "sl = $", hexstr(dreg_sl,4), "  ");
	t = catstr(t, "msbit = $", hexstr(dreg_msbit,4), "  ");
	t = catstr(t, "cmd_lo = $", hexstr(dreg_cmd_lo,4), "  ");
	t = catstr(t, "d2ar = $", hexstr(reg_d2ar,4), "  ");

	u = catstr(u, "ip = $", hexstr(areg_ip,4), "  ");
	u = catstr(u, "sp = $", hexstr(areg_sp,4), "  ");
	u = catstr(u, "a0 = $", hexstr(areg_a0,4), "  ");
	u = catstr(u, "a1 = $", hexstr(areg_a1,4), "  ");
	u = catstr(u, "hp = $", hexstr(areg_hp,4), "  ");
	u = catstr(u, "atmp = $", hexstr(areg_atmp,4), "  ");

	logNl();
	log_labelstack();
	log("%s\n%s\n%s\n",s,t,u);
}

void Cpu::log_mem()
{
	logNl();logNl();

	for(uint a=0;a<0x10000;a+=16)
	{
		uint i;
		for(i=0;i<16 && ram[a+i].data==0;i++){}
		if(i==16) continue;

		log("$%04X: ",a);

		for(i=0;i<16;i++) log("%04X %s",ram[a+i],i==8?" ":"");
		log(" ");

		for(i=0;i<16;i++) log( "%c", ram[a+i].data>=32 && ram[a+i].data<127 ? (char)ram[a+i].data : (char)' ' );
		logNl();
	}

	logNl();
}


// ===========================================================
//			Read / Write on Data Bus / Address Bus
// ===========================================================


/*	calc. result of alu:
 */
uint16 Cpu::get_alu_result(uint32 mc2, uint16 dbus)
{
	switch( (mc2>>14) & 15 )
	{
		case 0:	return dreg_alu + dbus;		// add		cpl=0, cy=0
		case 1:	return dreg_alu ^ dbus;		// xor		cpl=0, cy=0
		case 2:	return dreg_alu & dbus;		// and/or	cpl=0, cy=0 => AND
		case 3:	return dbus;				// load		cpl=0, cy=0

		case 4:	return dreg_alu + dbus +1;	// add		cpl=0, cy=1 => ADD_CY
		case 5:	return dreg_alu ^ dbus;		// xor		cpl=0, cy=1
		case 6:	return dreg_alu | dbus;		// and/or	cpl=0, cy=1 => OR
		case 7:	return dbus;				// load		cpl=0, cy=1

		case 8:	return dreg_alu + ~dbus;	// add		cpl=1, cy=0 => SUB_NC
		case 9:	return dreg_alu ^ ~dbus;	// xor		cpl=1, cy=0	=> XOR_N
		case 10:return dreg_alu & ~dbus;	// and/or	cpl=1, cy=0 => AND_N
		case 11:return ~dbus;				// load		cpl=1, cy=0 => LOAD_N

		case 12:return dreg_alu + ~dbus +1;	// add		cpl=1, cy=1 => SUB
		case 13:return dreg_alu ^ ~dbus;	// xor		cpl=1, cy=1
		case 14:return dreg_alu | ~dbus;	// and/or	cpl=1, cy=1 => OR_N
		case 15:return ~dbus;				// load		cpl=1, cy=1
	}
	IERR();
}


/*	calc. cy output bit of alu:
 */
bool Cpu::get_alu_cy(uint32 mc2, uint16 dbus)
{
	switch( (mc2>>16) & 3 )
	{
		case 0: return (dreg_alu +  dbus   ) >> 16;	// add
		case 1: return (dreg_alu +  dbus +1) >> 16;	// add_c
		case 2: return (dreg_alu + (uint16)~dbus   ) >> 16;	// sub_nc
		case 3: return (dreg_alu + (uint16)~dbus +1) >> 16;	// sub
	}
	IERR();
}


/*	calc. ovfl output bit of alu:
 */
inline uint16 Cpu::get_alu_ovfl(uint32 mc2, uint16 dbus)
{
	if( (dreg_alu ^ dbus ^ (mc2>>2)) & 0x8000 )
	{
		return 0;		// vza!=vzb => !ovfl
	}
	else				// else ovfl = vza!=vz(a+b)
	{
		uint16 adder_result;
		switch( (mc2>>16) & 3 )
		{
			case 0: adder_result = dreg_alu +  dbus;
			case 1: adder_result = dreg_alu +  dbus +1;
			case 2: adder_result = dreg_alu + ~dbus;
			case 3: adder_result = dreg_alu + ~dbus +1;
		}
		return (dbus ^ adder_result) & 0x8000;
	}
}


int Cpu::wait(int cc)
{
	if(div_ff&(1<<div_halt))
	{
//		log("W");
		while(k1bus_no_irpt() && cc>0)
		{
			int ccc = minmax(1,cc,(int)xtal/256);
			waitDelay(ccc/xtal);
			cc -= ccc;
		}
	}
	return cc;
}


// =========================================================
//				RUN CPU:
// =========================================================


void Cpu::run( uint32 cc_end, bool stop_on_breakpoints )
{
	uint16 mca1 = this->mca1;				// microcode in stage 1 (->Ival, div.FF)
	uint32 mc1 = mc[mca1];

	uint16 mca2 = this->mca2;				// microcode in stage 2 (Execution)
	uint32 mc2 = mc[mca2];
	if(mca2==0x7FFF)						// reset?
		mc2 = (mc2 | 0x100000) & ~(7<<21);	// last code copied at init, forced cmd_dis and cond0

	uint16 abus;							// address on address bus
	uint16 dbus;							// data on data bus

	bool ei = BIT(div_ff,div_ei);			// interrupts enabled?

	int32 cc = cc_end - this->cc;			// cpu cycles to run
	int32 ccpi = 1 << (~div_ff&3);			// cpu cycles per instruction

	bool dxa_oe_flag = false;


	if(div_ff&(1<<div_halt)) cc = wait(cc);


	while(cc>0)
	{
		if(mca2==breakpoint_exec && stop_on_breakpoints) goto x;

		if(label[mca2]) { lastlabel2=lastlabel; lastlabel=mca2; }

		cc -= ccpi;


// ------------------------------------
// Opcode Disabled?

		if(~mc2 & 0x100000)				// cmd_dis=false => enabled
		{


// ------------------------------------
// areg.oe:
// Put Address On Address Bus:

			switch( (mc2>>8) & 7 )
			{
			case 0:	abus = areg_ip;	if((ram[abus].bits&cpu_break_x) && (mc2&dreg_oe_mask)==mem_oe && stop_on_breakpoints) goto x; break;
			case 2:	abus = areg_sp;	if(abus==breakpoint_return && (mc2&dreg_oe_mask)==mem_oe && stop_on_breakpoints) goto x; break;
			case 1:	dxa_oe_flag = 1; break;	// erst dbus bestimmen
			case 3:	abus = areg_a0;	 break;
			case 4:	abus = areg_a1;	 break;
			case 5:	abus = areg_hp;	 break;
			case 6:	abus = areg_atmp;break;
			case 7:	abus = reg_d2ar; break;
			}

// ------------------------------------
// dreg.oe:
// Put Data On Data Bus:

			switch( (mc2>>0) & 15 )
			{
			case 2:	// io.oe => input
			  {
				switch( (mc2>>14) & 3 )
				{
				case 0:		// io_rd_dummy
					dbus = floating_k1_bus;
					break;
				case 1:		// io_rd_i2c
					dbus = k1bus_selected_device
						? k1bus_selected_device->rd_i2c(mc2)
						: (floating_k1_bus & ~1) | ((mc2>>17)&1);
					break;
				case 2:		// io_rd_irpt
					dbus = ~k1bus_irpt_pending_bits;
					break;
				case 3:		// io_rd_data
					dbus = k1bus_selected_device
						? k1bus_selected_device->rd_data(areg_ioaddr)
						: floating_k1_bus;
					break;
				}
				if(div_ff&(1<<div_halt)) cc = wait(cc);	// falls durch den io der irpt weg ging
				break;
			  }
			case 0:	 dbus = mc1; break;			// ival
			case 1:
				{
					Byte& byte = ram[abus];
					if((byte.bits&cpu_break_r) && stop_on_breakpoints) goto x;
					dbus = byte.data; break;	// mem
				}
			case 3:	 dbus = abus; break;		// axd
			case 4:	 dbus = dreg_d0; break;		// d0
			case 5:	 dbus = dreg_d1; break;		// d1
			case 6:	 dbus = dreg_d2; break;		// d2
			case 7:	 dbus = dreg_d3; break;		// d3
			case 8:	 dbus = dreg_alu; break;	// alu
			case 10: dbus = dreg_swap; break;	// swap
			case 12: dbus = dreg_sr; break;		// sr
			case 13: dbus = dreg_sl; break;		// sl
			case 14: dbus = dreg_msbit; break;	// msbit
			case 15: dbus = dreg_cmd_lo; break;	// cmd_lo
			}


		// areg.oe was postponed for data-to-address bus driver dxa:

			if(dxa_oe_flag) { dxa_oe_flag = false; abus = dbus; }

		} // cmd_dis


// ------------------------------------
// Calculate Next Microcode Address:

		uint16 mca = mca1 + 1;				// input to microcode address counters if not loaded by cmd.clk

		// remove plane bit
		mca &= 0x3FFF;

		// add plane bit from cond.SEL:
		switch( (mc1>>21)&7 )
		{
		case 2:	if(get_alu_cy(mc2, dbus))			mca |= 0x4000; break;	// cy
		case 3:	if(get_alu_result(mc2, dbus)==0)	mca |= 0x4000; break;	// z
		case 4:	if(get_alu_ovfl(mc2, dbus))			mca |= 0x4000; break;	// ovfl
		case 5: mca |= random()  & 0x4000; break;	// rnd
		case 6: mca |= (dbus<<14)& 0x4000; break;	// dbus.bit0
		case 7: mca |= (dbus>>1) & 0x4000; break;	// dbus.bit15
		case 1:	mca |= 0x4000; break;				// plane := 1
		case 0:	break;			 					// plane := 0
		}


		if(~mc2 & 0x100000)					// cmd_dis=false => enabled
		{

// ------------------------------------
// dreg.clk:
// Read Data From Data Bus:

			switch( (mc2>>4) & 15 )
			{
			case 0: // cmd.clk
			  {
				mca &= 0x4000;				// plane bit

//				if((mc2&dreg_oe_mask)==mem_oe && (mc2&areg_oe_mask)==ip_oe)
//					logline("OPCODE $%04X",(uint)dbus);

				mca |= (mc2&dreg_oe_mask)==mem_oe && (mc2&areg_oe_mask)==ip_oe && ei && k1bus_is_irpt()
					? dbus&0x3000					// interrupt vector
					: dbus&0x4000 ? dbus&0x3FFF		// standard OPCODE address
								  : dbus&0x3F00;	// OPCODE with 8-bit argument in cmd_lo
				dreg_cmd_lo = dbus&0xFF;

				if((mca&0x0FFF)==0)
					log("");

				break;
			  }
			case 2: // io.clk => output
			  {
				switch( (mc2>>14) & 15 )	// dbus option control lines opt.0 to opt.3
				{
				case 0:	// io_wr_data
					if(k1bus_selected_device) k1bus_selected_device->wr_data(areg_ioaddr,dbus);
					break;

				case 1:	// io_wr_select
					k1bus_select_bits = ~dbus;
					for(uint i=0;i<16;i++) if(k1bus_devices[i]) k1bus_devices[i]->wr_select(~dbus);
					if((uint16)~dbus) k1bus_selected_device = k1bus_devices[msbit((uint16)~dbus)];
					break;

				case 2:	// io_wr_irpt
					k1bus_irpt_enable_bits = ~dbus;
					for(uint i=0;i<16;i++) if(k1bus_devices[i]) k1bus_devices[i]->wr_irpt(~dbus);
					break;

				case 3:	// io_wr_dummy
					break;

			// debugger hooks:
				case 4:	log("%c",(char)dbus); break;			// logchar
				case 5:	log("%u",(uint)dbus); break;			// lognum
				case 6:	log("%4X",(uint)dbus); break;			// loghex
				case 7:											// logfloat
				  {
					uint e=ram[dbus].data; double m = (ram[1+dbus].data<<16) + ram[2+dbus].data;
					if(e&0x8000) { e^=0x8000; m=-m; } log("%d",ldexp(m,e-0x401F)); break;
				  }
				case 8:  log_registers(); break;				// log_registers
				case 9:											// log_cc
					break;

				case 10: labelstack.append(lastlabel); log("{%s",label[lastlabel]); break;	// login
				case 11: lastlabel = labelstack.pop(); log("}"); break;						// logout

				case 12: log_mem(); break;						// log_mem
				//case 13: break;
				//case 14: break;
				//case 15: break;
				}
				if(div_ff&(1<<div_halt)) cc = wait(cc);	// falls durch den io der irpt weg ging
				break;
			  }
			case 1:
				{
					Byte& byte = ram[abus];
					if((byte.bits&cpu_break_w) && stop_on_breakpoints) goto x;
					ram[abus].data = dbus; break;
				}
			case 4:	 dreg_d0	= dbus; break;
			case 5:	 dreg_d1	= dbus; break;
			case 6:	 dreg_d2	= dbus; break;
			case 7:	 dreg_d3	= dbus; break;
			case 8:	 dreg_alu	= get_alu_result(mc2, dbus); break;
			case 10: dreg_swap	= ((dbus>>8)&0xFF) + ((dbus<<8)&0xFF00); break;
			case 12: dreg_sr	= ((dbus>>1)&0x7FFF) + ((mc2>>1)&0x8000); break;
			case 13: dreg_sl	= ((dbus<<1)&0xFFFE) + ((mc2>>16)&1); break;
			case 14: dreg_msbit = msbit((uint16)~dbus); break;
			case 15: reg_d2ar	= dbus; break;
			}



// ------------------------------------
// areg.clk:
// Read Address From Address Bus:

			uint32 areg_clk = mc2 & areg_clk_mask;

			if(areg_clk != anop_clk)
			{
				uint16 addr_adder_result;

				switch( (mc2>>18) & 3 )	// abus option control lines opt.4 and opt.5
				{
					case 0:	addr_adder_result = (mc2&areg_oe_mask)==dxa_oe ? abus   : abus+1; break; // dis=0  cpl=0
					case 1:	addr_adder_result = (mc2&areg_oe_mask)==dxa_oe ? abus-2 : abus-1; break; // dis=0  cpl=1
					case 2:	addr_adder_result = abus+dreg_cmd_lo;		break;						 // dis=1  cpl=0
					case 3:	addr_adder_result = abus+dreg_cmd_lo-256;	break;						 // dis=1  cpl=1
				}

				switch(areg_clk>>11)
				{
				case 0:	areg_ip   = addr_adder_result; break;
			  //case 1: break;
				case 2:	areg_sp   = addr_adder_result; break;
				case 3:	areg_a0   = addr_adder_result; break;
				case 4:	areg_a1   = addr_adder_result; break;
				case 5:	areg_hp   = addr_adder_result; break;
				case 6:	areg_atmp = addr_adder_result; break;
				case 7:	areg_ioaddr = abus&0x3F; break;
				}
			}

		} // cmd_dis


// ------------------------------------
// Set Bit in Div.FF:

		if(mc1 & 0x100000)
		{
			bool bit = BIT(mc1,16);
			uint reg = (mc1>>17)&7;

			if(BIT(div_ff,reg)!=bit)	// bit toggled?
			{
				div_ff ^= 1<<reg;		// toggle bit if div.FF

				switch( reg )
				{
				case div_clk2:
				case div_clk4:	ccpi = 1 << (~div_ff&3); break;
				case div_ei:	ei = bit; break;
			//	case led_grn:	break;
			//	case led_yel:	break;
			//	case led_red:	break;

				case div_halt:

					if(bit) cc = wait(cc);
					break;

				case div_reset:
					//
					// $ffffff wird als Stopper in ABORT etc. benutzt
					// nach einem Reset lädt die CPU die echten Eproms
					//
					logNl();
					logline("executed 0x%06X at 0x%04X",(uint)mc1,(uint)mca1);
					logline("last labels: %s, %s",label[lastlabel2],label[lastlabel]);
					log_registers();
					log_mem();
					goto x;
				}
			}
		}


// ------------------------------------
// Advance Microcode on the Control Unit:

		mca2 = mca1;		// code from stage 1 moves into stage 2
		mc2  = mc1;

		mca1 = mca;			// counter input advances to counter output;
		mc1  = mc[mca];		// code from eprom shows up in stage 1

	} // loop


	if(0) { x: state=stopped; }

	this->cc   = cc_end-cc;
	this->mca1 = mca1;
	this->mca2 = mca2;
}








// ===========================================================
// 	K1-Bus:
// ===========================================================


/*	callback from Board:
 *	note: die CPU wartet bei HALT nur, wenn sie das Weggehen des letzten Interrupts bemerkt:
 *	Es wird angenommen, dass ein Irpt nur als Reaktion auf einen i/o weggenommen wird
 *	und deshalb wird k1bus_get_irpt() nach i/o getestet.
 */
void Cpu::k1bus_set_irpt(uint16 pmask, bool flag)
{
	if(flag) k1bus_irpt_pending_bits |=  pmask;
	else	 k1bus_irpt_pending_bits &= ~pmask;
}














