//
// (C) 2004 Mike Brent aka Tursi aka HarmlessLion.com
// This software is provided AS-IS. No warranty
// express or implied is provided.
//
// This notice defines the entire license for this code.
// All rights not explicity granted here are reserved by the
// author.
//
// You may redistribute this software provided the original
// archive is UNCHANGED and a link back to my web page,
// http://harmlesslion.com, is provided as the author's site.
// It is acceptable to link directly to a subpage at harmlesslion.com
// provided that page offers a URL for that purpose
//
// Source code, if available, is provided for educational purposes
// only. You are welcome to read it, learn from it, mock
// it, and hack it up - for your own use only.
//
// Please contact me before distributing derived works or
// ports so that we may work out terms. I don't mind people
// using my code but it's been outright stolen before. In all
// cases the code must maintain credit to the original author(s).
//
// -COMMERCIAL USE- Contact me first. I didn't make
// any money off it - why should you? ;) If you just learned
// something from this, then go ahead. If you just pinched
// a routine or two, let me know, I'll probably just ask
// for credit. If you want to derive a commercial tool
// or use large portions, we need to talk. ;)
//
// If this, itself, is a derived work from someone else's code,
// then their original copyrights and licenses are left intact
// and in full force.
//
// http://harmlesslion.com - visit the web page for contact info
//
/////////////////////////////////////////////////////////////////////
// Classic99 - TMS9900 CPU Routines
// M.Brent
// The TMS9900 is a 16-bit CPU by Texas Instruments, with a 16
// bit data and 16-bit address path, capable of addressing
// 64k of memory. All reads and writes are word (16-bit) oriented.
// Byte access is simulated within the CPU by reading or writing
// the entire word, and manipulating only the requested byte.
// This is not currently emulated here. The CPU uses external
// RAM for all user registers. There are 16 user registers, R0-R15,
// and the memory base for these registers may be anywhere in
// memory, set by the Workspace Pointer. The CPU also has a Program
// Counter and STatus register internal to it.
// This emulation generates a lookup table of addresses for each
// opcode. It's not currently intended for use outside of Classic99
// and there may be problems with dependancy on other parts of the
// code if it is moved to another project.
// Word is defined to be an unsigned 16-bit integer (__int16)
// Byte is defined to be an unsigned 8-bit integer (__int8)
/////////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0400

#include <stdio.h>
#include <windows.h>

#include "tiemul.h"

// CPU Global variables
Word PC;											// Program Counter
Word WP;											// Workspace Pointer
Word X_flag;										// Set during an 'X' instruction
Word ST;											// Status register
Word in,D,S,Td,Ts,B;								// Opcode interpretation
void (*opcode[65536])(void);						// CPU Opcode address table

int nCycleCount=0;									// Used in CPU throttle

/////////////////////////////////////////////////////////////////////
// Status register defines
/////////////////////////////////////////////////////////////////////
#define BIT_LGT 0x8000
#define BIT_AGT 0x4000
#define BIT_EQ  0x2000
#define BIT_C   0x1000
#define BIT_OV  0x0800
#define BIT_OP  0x0400
#define BIT_X   0x0200

#define ST_LGT (ST & BIT_LGT)						// Logical Greater Than
#define ST_AGT (ST & BIT_AGT)						// Arithmetic Greater Than
#define ST_EQ  (ST & BIT_EQ)						// Equal
#define ST_C   (ST & BIT_C)							// Carry
#define ST_OV  (ST & BIT_OV)						// Overflow
#define ST_OP  (ST & BIT_OP)						// Odd Parity
#define ST_X   (ST & BIT_X)							// Set during an XOP instruction
#define ST_INTMASK (ST&0x000f)						// Interrupt mask (the TI uses only values 0 and 2)

#define set_LGT (ST|=0x8000)						// Logical Greater than: >0x0000
#define set_AGT (ST|=0x4000)						// Arithmetic Greater than: >0x0000 and <0x8000
#define set_EQ  (ST|=0x2000)						// Equal: ==0x0000
#define set_C   (ST|=0x1000)						// Carry: carry occurred during operation
#define set_OV  (ST|=0x0800)						// Overflow: overflow occurred during operation
#define set_OP  (ST|=0x0400)						// Odd parity: word has odd number of '1' bits
#define set_X   (ST|=0x0200)						// Executing 'X' statement

#define reset_LGT (ST&=0x7fff)						// Clear the flags
#define reset_AGT (ST&=0xbfff)
#define reset_EQ  (ST&=0xdfff)
#define reset_C   (ST&=0xefff)
#define reset_OV  (ST&=0xf7ff)
#define reset_OP  (ST&=0xfbff)
#define reset_X   (ST&=0xfdff)

// Group clears
#define reset_EQ_LGT (ST&=0x5fff)
#define reset_LGT_AGT_EQ (ST&=0x1fff)
#define reset_EQ_LGT_AGT_OV (ST&=0x17ff)
#define reset_EQ_LGT_AGT_C (ST&=0x0fff)
#define reset_EQ_LGT_AGT_C_OV (ST&=0x7ff)

// Assignment masks
#define mask_EQ_LGT (BIT_EQ|BIT_LGT)
#define mask_LGT_AGT_EQ (BIT_LGT|BIT_AGT|BIT_EQ)
#define mask_LGT_AGT_EQ_OP (BIT_LGT|BIT_AGT|BIT_EQ|BIT_OP)
#define mask_LGT_AGT_EQ_OV (BIT_LGT|BIT_AGT|BIT_EQ|BIT_OV)
#define mask_LGT_AGT_EQ_OV_C (BIT_LGT|BIT_AGT|BIT_EQ|BIT_OV|BIT_C)

// Status register lookup table (hey, what's another 64k these days??)
Word WStatusLookup[64*1024];
Word BStatusLookup[256];

/////////////////////////////////////////////////////////////////////
// Inlines for getting source and destination addresses
/////////////////////////////////////////////////////////////////////
#define FormatI { Td=(in&0x0c00)>>10; Ts=(in&0x0030)>>4; D=(in&0x03c0)>>6; S=(in&0x000f); B=(in&0x1000)>>12; fixDS(); }
#define FormatII { D=(in&0x00ff); }
#define FormatIII { Td=0; Ts=(in&0x0030)>>4; D=(in&0x03c0)>>6; S=(in&0x000f); B=0; fixDS(); }
#define FormatIV { Td=4; D=(in&0x03c0)>>6; Ts=(in&0x0030)>>4; S=(in&0x000f); B=(D>8); fixDS(); }	// Td=4 - non-existant type to fixDS (CRU ops)
#define FormatV { D=(in&0x00f0)>>4; S=(in&0x000f); S=WP+(S<<1); }
#define FormatVI { Td=4; Ts=(in&0x0030)>>4; S=in&0x000f; B=0; fixDS(); }							// Td=4 - non-existant type (single argument instructions)
#define FormatVII {}																				// no argument
#define FormatVIII_0 { D=(in&0x000f); D=WP+(D<<1); }
#define FormatVIII_1 { D=(in&0x000f); D=WP+(D<<1); S=romword(PC); ADDPC(2); }
#define FormatIX  { D=(in&0x03c0)>>6; Td=4; Ts=(in&0x0030)>>4; S=(in&0x000f); B=0; fixDS(); }		// Td=4 - non-existant type (dest calc'd after call) (DIV, MUL, XOP)

//////////////////////////////////////////////////////////////////////////
// Get addresses for the destination and source arguments
// Note: the format code letters are the official notation from Texas
// instruments. See their TMS9900 documentation for details.
// (Td, Ts, D, S, B, etc)
// Note that some format codes set the destination type (Td) to
// '4' in order to skip unneeded processing of the Destination address
//////////////////////////////////////////////////////////////////////////
void fixDS()
{
	int temp,t2;													// temp vars

	switch (Ts)														// source type
	{ 
	case 0: S=WP+(S<<1); 
			break;													// register						(R1)			Address is the address of the register

	case 1: S=romword(WP+(S<<1)); 
			nCycleCount+=4; 
			break;													// register indirect			(*R1)			Address is the contents of the register

	case 2: if (S) { 
				S=romword(PC)+romword(WP+(S<<1)); 					// indexed						(@>1000(R1))	Address is the contents of the argument plus the
			} else {												//												contents of the register
				S=romword(PC); 										// symbolic						(@>1000)		Address is the contents of the argument
			}
			ADDPC(2); 
			nCycleCount+=8;
			break;

	case 3: t2=WP+(S<<1); 
			temp=romword(t2); 
			wrword(t2,temp+(2-B));									// (add 1 if byte, 2 if word)	(*R1+)			Address is the contents of the register, which
			S=temp; 
			nCycleCount+=(B==1?6:8); 
			break;													// register indirect autoincrement				is incremented by 1 for byte or 2 for word ops
	}

	switch (Td)														// destination type - some Formats pass '4' to skip this part.
	{																// same as the source types
	case 0: D=WP+(D<<1); 
			break;													// register

	case 1: D=romword(WP+(D<<1)); 
			nCycleCount+=4;
			break;													// register indirect

	case 2: if (D) { 
				D=romword(PC)+romword(WP+(D<<1));					// indexed 
			} else {
				D=romword(PC);										// symbolic
			}
			ADDPC(2);
			nCycleCount+=8;
			break;

	case 3: t2=WP+(D<<1); 
			temp=romword(t2); 
			wrword(t2,temp+(2-B));									// (add 1 if byte, 2 if word)
			D=temp; 
			nCycleCount+=(B==1?6:8); 
			break;													// register indirect autoincrement
	}
}

/////////////////////////////////////////////////////////////////////////
// Check parity in the passed byte and set the OP status bit
/////////////////////////////////////////////////////////////////////////
void parity(Byte x)
{
	int z;															// temp vars

	for (z=0; x; x&=(x-1)) z++;										// black magic?
	
	if (z&1)														// set bit if an odd number
		set_OP; 
	else 
		reset_OP;
}

////////////////////////////////////////////////////////////////////////
// Fill the CPU Opcode Address table
////////////////////////////////////////////////////////////////////////
void buildcpu()
{
	Word in,x,z;
	unsigned int i;

	for (i=0; i<65536; i++)
	{ 
		in=(Word)i;

		x=(in&0xf000)>>12;
		switch(x)
		{ 
		case 0: opcode0(in);		break;
		case 1: opcode1(in);		break;
		case 2: opcode2(in);		break;
		case 3: opcode3(in);		break;
		case 4: opcode[in]=op_szc;	break;
		case 5: opcode[in]=op_szcb; break;
		case 6: opcode[in]=op_s;	break;
		case 7: opcode[in]=op_sb;	break;
		case 8: opcode[in]=op_c;	break;
		case 9: opcode[in]=op_cb;	break;
		case 10:opcode[in]=op_a;	break;
		case 11:opcode[in]=op_ab;	break;
		case 12:opcode[in]=op_mov;	break;
		case 13:opcode[in]=op_movb; break;
		case 14:opcode[in]=op_soc;	break;
		case 15:opcode[in]=op_socb; break;
		default: opcode[in]=op_bad;
		}
	} 

	// build the Word status lookup table
	for (i=0; i<65536; i++) {
		WStatusLookup[i]=0;
		// LGT
		if (i>0) WStatusLookup[i]|=BIT_LGT;
		// AGT
		if ((i>0)&&(i<0x8000)) WStatusLookup[i]|=BIT_AGT;
		// EQ
		if (i==0) WStatusLookup[i]|=BIT_EQ;
		// C
		if (i==0) WStatusLookup[i]|=BIT_C;
		// OV
		if (i==0x8000) WStatusLookup[i]|=BIT_OV;
	}
	// And byte
	for (i=0; i<256; i++) {
		Byte x=(Byte)(i&0xff);
		BStatusLookup[i]=0;
		// LGT
		if (i>0) BStatusLookup[i]|=BIT_LGT;
		// AGT
		if ((i>0)&&(i<0x80)) BStatusLookup[i]|=BIT_AGT;
		// EQ
		if (i==0) BStatusLookup[i]|=BIT_EQ;
		// C
		if (i==0) BStatusLookup[i]|=BIT_C;
		// OV
		if (i==0x80) BStatusLookup[i]|=BIT_OV;
		// OP
		for (z=0; x; x&=(x-1)) z++;						// black magic?
		if (z&1) BStatusLookup[i]|=BIT_OP;				// set bit if an odd number
	}
}

///////////////////////////////////////////////////////////////////////////
// CPU Opcode 0 helper function
///////////////////////////////////////////////////////////////////////////
void opcode0(Word in)
{
	Word x;

	x=(in&0x0f00)>>8;

	switch(x)
	{ 
	case 2: opcode02(in);		break;
	case 3: opcode03(in);		break;
	case 4: opcode04(in);		break;
	case 5: opcode05(in);		break;
	case 6: opcode06(in);		break;
	case 7: opcode07(in);		break;
	case 8: opcode[in]=op_sra;	break;
	case 9: opcode[in]=op_srl;	break;
	case 10:opcode[in]=op_sla;	break;
	case 11:opcode[in]=op_src;	break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////////////
// CPU Opcode 02 helper function
////////////////////////////////////////////////////////////////////////////
void opcode02(Word in)
{ 
	Word x;

	x=(in&0x00e0)>>4;

	switch(x)
	{ 
	case 0: opcode[in]=op_li;	break;
	case 2: opcode[in]=op_ai;	break;
	case 4: opcode[in]=op_andi; break;
	case 6: opcode[in]=op_ori;	break;
	case 8: opcode[in]=op_ci;	break;
	case 10:opcode[in]=op_stwp; break;
	case 12:opcode[in]=op_stst; break;
	case 14:opcode[in]=op_lwpi; break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////////////
// CPU Opcode 03 helper function
////////////////////////////////////////////////////////////////////////////
void opcode03(Word in)
{ 
	Word x;

	x=(in&0x00e0)>>4;

	switch(x)
	{ 
	case 0: opcode[in]=op_limi; break;
	case 4: opcode[in]=op_idle; break;
	case 6: opcode[in]=op_rset; break;
	case 8: opcode[in]=op_rtwp; break;
	case 10:opcode[in]=op_ckon; break;
	case 12:opcode[in]=op_ckof; break;
	case 14:opcode[in]=op_lrex; break;
	default: opcode[in]=op_bad;
	}
}

///////////////////////////////////////////////////////////////////////////
// CPU Opcode 04 helper function
///////////////////////////////////////////////////////////////////////////
void opcode04(Word in)
{ 
	Word x;

	x=(in&0x00c0)>>4;

	switch(x)
	{ 
	case 0: opcode[in]=op_blwp; break;
	case 4: opcode[in]=op_b;	break;
	case 8: opcode[in]=op_x;	break;
	case 12:opcode[in]=op_clr;	break;
	default: opcode[in]=op_bad;
	}
}

//////////////////////////////////////////////////////////////////////////
// CPU Opcode 05 helper function
//////////////////////////////////////////////////////////////////////////
void opcode05(Word in)
{ 
	Word x;

	x=(in&0x00c0)>>4;

	switch(x)
	{ 
	case 0: opcode[in]=op_neg;	break;
	case 4: opcode[in]=op_inv;	break;
	case 8: opcode[in]=op_inc;	break;
	case 12:opcode[in]=op_inct; break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////////
// CPU Opcode 06 helper function
////////////////////////////////////////////////////////////////////////
void opcode06(Word in)
{ 
	Word x;

	x=(in&0x00c0)>>4;

	switch(x)
	{ 
	case 0: opcode[in]=op_dec;	break;
	case 4: opcode[in]=op_dect; break;
	case 8: opcode[in]=op_bl;	break;
	case 12:opcode[in]=op_swpb; break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////////
// CPU Opcode 07 helper function
////////////////////////////////////////////////////////////////////////
void opcode07(Word in)
{ 
	Word x;

	x=(in&0x00c0)>>4;

	switch(x)
	{ 
	case 0: opcode[in]=op_seto; break;
	case 4: opcode[in]=op_abs;	break;
	default: opcode[in]=op_bad;
	}	
}

////////////////////////////////////////////////////////////////////////
// CPU Opcode 1 helper function
////////////////////////////////////////////////////////////////////////
void opcode1(Word in)
{ 
	Word x;

	x=(in&0x0f00)>>8;

	switch(x)
	{ 
	case 0: opcode[in]=op_jmp;	break;
	case 1: opcode[in]=op_jlt;	break;
	case 2: opcode[in]=op_jle;	break;
	case 3: opcode[in]=op_jeq;	break;
	case 4: opcode[in]=op_jhe;	break;
	case 5: opcode[in]=op_jgt;	break;
	case 6: opcode[in]=op_jne;	break;
	case 7: opcode[in]=op_jnc;	break;
	case 8: opcode[in]=op_joc;	break;
	case 9: opcode[in]=op_jno;	break;
	case 10:opcode[in]=op_jl;	break;
	case 11:opcode[in]=op_jh;	break;
	case 12:opcode[in]=op_jop;	break;
	case 13:opcode[in]=op_sbo;	break;
	case 14:opcode[in]=op_sbz;	break;
	case 15:opcode[in]=op_tb;	break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////////
// CPU Opcode 2 helper function
////////////////////////////////////////////////////////////////////////
void opcode2(Word in)
{ 
	Word x;

	x=(in&0x0c00)>>8;

	switch(x)
	{ 
	case 0: opcode[in]=op_coc; break;
	case 4: opcode[in]=op_czc; break;
	case 8: opcode[in]=op_xor; break;
	case 12:opcode[in]=op_xop; break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////////
// CPU Opcode 3 helper function
////////////////////////////////////////////////////////////////////////
void opcode3(Word in)
{ 
	Word x;

	x=(in&0x0c00)>>8;

	switch(x)
	{ 
	case 0: opcode[in]=op_ldcr; break;
	case 4: opcode[in]=op_stcr; break;
	case 8: opcode[in]=op_mpy;	break;
	case 12:opcode[in]=op_div;	break;
	default: opcode[in]=op_bad;
	}
}

////////////////////////////////////////////////////////////////////
// Classic99 - 9900 CPU opcodes
// Opcode functions follow
// one function for each opcode (the mneumonic prefixed with "op_")
// src - source address (register or memory - valid types vary)
// dst - destination address
// imm - immediate value
// dsp - relative displacement
////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
// DO NOT USE wcpubyte or rcpubyte in here! You'll break the RMW
// emulation and the cycle counting!
/////////////////////////////////////////////////////////////////////
#define wcpubyte #error Do not use in this file
#define rcpubyte #error Do not use in this file

#define RCPUBYTE(S)		romwordbyte(S)
#define WCPUBYTE(D,X)	wrwordbyte(D,X)

void op_a()
{
	// Add words: A src, dst

	Word x1,x2,x3;

	FormatI;
	x1=romword(S); 
	x2=romword(D);

	x3=x2+x1; 
	wrword(D,x3);
																						// most of these are the same for every opcode.
	reset_EQ_LGT_AGT_C_OV;																// We come out with either EQ or LGT, never both
	ST|=WStatusLookup[x3]&mask_LGT_AGT_EQ;

	if (x3<x2) set_C;																	// if it wrapped around, set carry
	if (((x1&0x8000)==(x2&0x8000))&&((x3&0x8000)!=(x2&0x8000))) set_OV;					// if it overflowed or underflowed (signed math), set overflow
	
	nCycleCount+=14;
}

void op_ab()
{ 
	// Add bytes: A src, dst
	Byte x1,x2,x3;

	FormatI;
	x1=RCPUBYTE(S); 
	x2=RCPUBYTE(D);

	x3=x2+x1;
	WCPUBYTE(D,x3);
	
	reset_EQ_LGT_AGT_C_OV;
	ST|=BStatusLookup[x3]&mask_LGT_AGT_EQ_OP;

	if (x3<x2) set_C;
	if (((x1&0x80)==(x2&0x80))&&((x3&0x80)!=(x2&0x80))) set_OV;
	
	nCycleCount+=14;
}

void op_abs()
{ 
	// ABSolute value: ABS src
	
	Word x1,x2;

	FormatVI;
	x1=romword(S);

	if (x1&0x8000) {
		x2=(~x1)+1;																		// if negative, make positive
		wrword(S,x2);
		nCycleCount+=2;
	}

	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ_OV;

	nCycleCount+=12;
}

void op_ai()
{ 
	// Add Immediate: AI src, imm
	
	Word x1,x3;

	FormatVIII_1;
	x1=romword(D);

	x3=x1+S;
	wrword(D,x3);

	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x3]&mask_LGT_AGT_EQ;

	if (x3<x1) set_C;
	if (((x1&0x8000)==(S&0x8000))&&((x3&0x8000)!=(S&0x8000))) set_OV;
	
	nCycleCount+=14;
}

void op_dec()
{ 
	// DECrement: DEC src
	
	Word x1;

	FormatVI;
	x1=romword(S);

	x1--;
	wrword(S,x1);

	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x1!=0xffff) set_C;
	if (x1==0x7fff) set_OV;
	
	nCycleCount+=10;
}

void op_dect()
{ 
	// DECrement by Two: DECT src
	
	Word x1;

	FormatVI;
	x1=romword(S);

	x1-=2;
	wrword(S,x1);
	
	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x1<0xfffe) set_C;
	if ((x1==0x7fff)||(x1==0x7ffe)) set_OV;
	
	nCycleCount+=10;
}

void op_div()
{ 
	// DIVide: DIV src, dst
	// Dest, a 2 word number, is divided by src. The result is stored as two words at the dst:
	// the first is the whole number result, the second is the remainder

	Word x1,x2; 
	unsigned __int32 x3;

	FormatIX;
	x2=romword(S);
	D=WP+(D<<1);
	x3=romword(D);

	if (x2>x3)						// x2 can not be zero because they're unsigned										
	{ 
		x3=(x3<<16)|romword(D+2);
		x1=(Word)(x3/x2);
		wrword(D,x1);
		x1=(Word)(x3%x2);
		wrword(D+2,x1);
		reset_OV;
		nCycleCount+=92;	// (up to 124 "depends on the partial quotient after each clock cycle during execution")
	}
	else
	{
		set_OV;								// division wasn't possible - change nothing
		nCycleCount+=16;
	}
}

void op_inc()
{ 
	// INCrement: INC src
	
	Word x1;

	FormatVI;
	x1=romword(S);
	
	x1++;
	wrword(S,x1);
	
	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ_OV_C;
	
	nCycleCount+=10;
}

void op_inct()
{ 
	// INCrement by Two: INCT src
	
	Word x1;

	FormatVI;
	x1=romword(S);
	
	x1+=2;
	wrword(S,x1);
	
	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x1<2) set_C;
	if ((x1==0x8000)||(x1==0x8001)) set_OV;
	
	nCycleCount+=10;
}

void op_mpy()
{ 
	// MultiPlY: MPY src, dst
	// Multiply src by dest and store 32-bit result

	Word x1; 
	unsigned __int32 x3;

	FormatIX;
	x1=romword(S);
	
	D=WP+(D<<1);
	x3=romword(D);
	x3=x3*x1;
	wrword(D,(Word)(x3>>16)); 
	wrword(D+2,(Word)(x3&0xffff));
	
	nCycleCount+=52;
}

void op_neg()
{ 
	// NEGate: NEG src

	Word x1;

	FormatVI;
	x1=romword(S);

	x1=(~x1)+1;
	wrword(S,x1);

	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ_OV;
	
	nCycleCount+=12;
}

void op_s()
{ 
	// Subtract: S src, dst

	Word x1,x2,x3;

	FormatI;
	x1=romword(S); 
	x2=romword(D);

	x3=x2-x1;
	wrword(D,x3);

	reset_EQ_LGT_AGT_C_OV;
	ST|=WStatusLookup[x3]&mask_LGT_AGT_EQ;

	if (x3<x2) set_C;
	if (((x1&0x8000)!=(x2&0x8000))&&((x3&0x8000)!=(x2&0x8000))) set_OV;
	
	nCycleCount+=14;
}

void op_sb()
{ 
	// Subtract Byte: SB src, dst

	Byte x1,x2,x3;

	FormatI;
	x1=RCPUBYTE(S); 
	x2=RCPUBYTE(D);

	x3=x2-x1;
	WCPUBYTE(D,x3);

	reset_EQ_LGT_AGT_C_OV;
	ST|=BStatusLookup[x3]&mask_LGT_AGT_EQ_OP;

	if (x3<x2) set_C;
	if (((x1&0x80)!=(x2&0x80))&&((x3&0x80)!=(x2&0x80))) set_OV;
	
	nCycleCount+=14;
}

void op_b()
{ 
	// Branch: B src
	// Unconditional absolute branch

	FormatVI;
	PC=S;
	
	nCycleCount+=8;
}

void op_bl()
{	
	// Branch and Link: BL src
	// Essentially a subroutine jump - return address is stored in R11
	// Note there is no stack, and no official return function.
	// A return is simply B *R11. Some assemblers define RT as this.

	FormatVI;
	wrword(WP+22,PC);
	PC=S;
	
	nCycleCount+=12;
}

void op_blwp()
{ 
	// Branch and Load Workspace Pointer: BLWP src
	// A context switch. The src address points to a 2 word table.
	// the first word is the new workspace address, the second is
	// the address to branch to. The current Workspace Pointer,
	// Program Counter (return address), and Status register are
	// stored in the new R13, R14 and R15, respectively
	// Return is performed with RTWP

	Word x1;

	FormatVI;
	x1=WP;
	WP=romword(S);
	wrword(WP+26,x1);
	wrword(WP+28,PC);
	wrword(WP+30,ST);
	PC=romword(S+2);

	skip_interrupt=1;
	
	nCycleCount+=26;
}

void op_jeq()
{ 
	// Jump if equal: JEQ dsp
	// Conditional relative branch. The displacement is a signed byte representing
	// the number of words to branch

	FormatII;
	if (ST_EQ) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jgt()
{ 
	// Jump if Greater Than: JGT dsp

	FormatII;
	if (ST_AGT) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jhe()
{ 
	// Jump if High or Equal: JHE dsp

	FormatII;
	if ((ST_LGT)||(ST_EQ)) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jh()
{ 
	// Jump if High: JH dsp
	
	FormatII;
	if ((ST_LGT)&&(!ST_EQ)) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jl()
{
	// Jump if Low: JL dsp

  	FormatII;
	if ((!ST_LGT)&&(!ST_EQ)) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jle()
{ 
	// Jump if Low or Equal: JLE dsp

	FormatII;
	if ((!ST_LGT)||(ST_EQ)) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jlt()
{ 
	// Jump if Less Than: JLT dsp

	FormatII;
	if ((!ST_AGT)&&(!ST_EQ)) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jmp()
{ 
	// JuMP: JMP dsp
	// (unconditional)
	
	FormatII;
	if (D&0x80) {
		D=128-(D&0x7f);
		ADDPC(-(D+D));
	} else {
		ADDPC(D+D);
	}
	if (X_flag) {
		ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
	}

	nCycleCount+=10;
}

void op_jnc()
{ 
	// Jump if No Carry: JNC dsp
	
	FormatII;
	if (!ST_C) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jne()
{ 
	// Jump if Not Equal: JNE dsp

	FormatII;
	if (!ST_EQ) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jno()
{ 
	// Jump if No Overflow: JNO dsp

	FormatII;
	if (!ST_OV) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_jop()
{ 
	// Jump on Odd Parity: JOP dsp

	FormatII;
	if (ST_OP) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_joc()
{ 
	// Jump On Carry: JOC dsp

	FormatII;
	if (ST_C) 
	{
		if (D&0x80) {
			D=128-(D&0x7f);
			ADDPC(-(D+D));
		} else {
			ADDPC(D+D);
		}
		if (X_flag) {
			ADDPC(-2);		// Update offset - it's relative to the X, not the opcode
		}
		nCycleCount+=10;
	} else {
		nCycleCount+=8;
	}
}

void op_rtwp()
{ 
	// ReTurn with Workspace Pointer: RTWP
	// The matching return for BLWP, see BLWP for description
	
	ST=romword(WP+30);
	PC=romword(WP+28);
	WP=romword(WP+26);

	nCycleCount+=14;
}

void op_x()
{ 
	// eXecute: X src
	// The argument is interpreted as an instruction and executed
	// I'm not sure how well this works or how often it's used.

	if (X_flag!=0) 
	{
		warn("Recursive X instruction!!!!!");
		// While it will probably work (recursive X), I don't like the idea ;)
	}
  
	FormatVI;
	in=romword(S);
	X_flag=1;													// set flag
	skip_interrupt=1;
	nCycleCount+=8-4;	// For X, add this time to the execution time of the instruction found at the source address, minus 4 clock cycles and 1 memory access. 
	
	do1();														// do an instruction
	X_flag=0;													// clear flag
}

void op_xop()
{ 
	// eXtended OPeration: XOP src ???
	// The CPU maintains a jump table starting at 0x0040, containing BLWP style
	// jumps for each operation. In addition, the new R11 gets a copy of the address of
	// the source operand.
	// Apparently not all consoles supported both XOP 1 and 2 (depends on the ROM?)
	// so it is probably rarely, if ever, used on the TI99.
	
	Word x1;

	FormatIX;
	D&=0xf;

	x1=WP;
	WP=romword(0x0040+(D<<2));
	wrword(WP+22,S);
	wrword(WP+26,x1);
	wrword(WP+28,PC);
	wrword(WP+30,ST);
	PC=romword(0x0042+(D<<2));
	set_X;

	skip_interrupt=1;

	nCycleCount+=36;
}

void op_c()
{ 
	// Compare words: C src, dst
	
	Word x3,x4;		// unsigned 16 bit

	FormatI;
	x3=romword(S); 
	x4=romword(D); 

	reset_LGT_AGT_EQ;
	if (x3>x4) set_LGT;
	if (x3==x4) set_EQ;
	if ((x3&0x8000)==(x4&0x8000)) {
		if (x3>x4) set_AGT;
	} else {
		if (x4&0x8000) set_AGT;
	}

	nCycleCount+=14;
}

void op_cb()
{ 
	// Compare Bytes: CB src, dst

	Byte x3,x4;

	FormatI;
	x3=RCPUBYTE(S); 
	x4=RCPUBYTE(D); 
  
	reset_LGT_AGT_EQ;
	if (x3>x4) set_LGT;
	if (x3==x4) set_EQ;
	if ((x3&0x80)==(x4&0x80)) {
		if (x3>x4) set_AGT;
	} else {
		if (x4&0x80) set_AGT;
	}
	ST|=BStatusLookup[x3]&BIT_OP;

	nCycleCount+=14;
}

void op_ci()
{ 
	// Compare Immediate: CI src, imm
	
	Word x3;

	FormatVIII_1;
	x3=romword(D); 
  
	reset_LGT_AGT_EQ;
	if (x3>S) set_LGT;
	if (x3==S) set_EQ;
	if ((x3&0x8000)==(S&0x8000)) {
		if (x3>S) set_AGT;
	} else {
		if (S&0x8000) set_AGT;
	}

	nCycleCount+=14;
}

void op_coc()
{ 
	// Compare Ones Corresponding: COC src, dst
	// Basically comparing against a mask, if all set bits in the src match
	// set bits in the dest (mask), the equal bit is set

	Word x1,x2,x3;

	FormatIII;
	x1=romword(S);
	x2=romword(D);
	
	x3=x1&x2;
  
	if (x3==x1) set_EQ; else reset_EQ;

	nCycleCount+=14;
}

void op_czc()
{ 
	// Compare Zeros Corresponding: CZC src, dst
	// The opposite of COC. Each set bit in the dst (mask) must
	// match up with a zero bit in the src to set the equals flag

	Word x1,x2,x3;

	FormatIII;
	x1=romword(S);
	x2=romword(D);
	
	x3=x1&x2;
  
	if (x3==0) set_EQ; else reset_EQ;

	nCycleCount+=14;
}

void op_ldcr()
{ 
	// LoaD CRu - LDCR src, dst
	// Writes dst bits serially out to the CRU registers
	// The CRU is the 9901 Communication chip, tightly tied into the 9900.
	// It's serially accessed and has 4096 single bit IO registers.
	// It's stupid and thinks 0 is true and 1 is false.
	// All addresses are offsets from the value in R12, which is divided by 2

	Word x1,x3,cruBase; 
	int x2;

	FormatIV;
	if (D==0) D=16;
	x1=(D<9 ? RCPUBYTE(S) : romword(S));
  
	x3=1;
	cruBase=(romword(WP+24)>>1)&0xfff;
	for (x2=0; x2<D; x2++)
	{ 
		wcru(cruBase+x2, (x1&x3) ? 1 : 0);
		x3=x3<<1;
	}

	nCycleCount+=20+2*D;

	if (D>8) return;

	reset_EQ_LGT_AGT_C;
	if (D<9) {
		ST|=BStatusLookup[x1&0xff]&mask_LGT_AGT_EQ_OP;
	} else {
		ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;
	}
}

void op_sbo()
{ 
	// Set Bit On: SBO src
	// Sets a bit in the CRU
	
	Word add;

	FormatII;
	add=(romword(WP+24)>>1)&0xfff;
	if (D&0x80) {
		add-=128-(D&0x7f);
	} else {
		add+=D;
	}
	wcru(add,1);

	nCycleCount+=12;
}

void op_sbz()
{ 
	// Set Bit Zero: SBZ src
	// Zeros a bit in the CRU

	Word add;

	FormatII;
	add=(romword(WP+24)>>1)&0xfff;
	if (D&0x80) {
		add-=128-(D&0x7f);
	} else {
		add+=D;
	}
	wcru(add,0);

	nCycleCount+=12;
}

void op_stcr()
{ 
	// STore CRU: STCR src, dst
	// Stores dst bits from the CRU into src

	Word x1,x3,x4, cruBase; 
	int x2;

	FormatIV;
	if (D==0) D=16;
	x1=0; x3=1;
  
	cruBase=(romword(WP+24)>>1)&0xfff;
	for (x2=0; x2<D; x2++)
	{ 
		x4=rcru(cruBase+x2);
		if (x4) 
		{
			x1=x1|x3;
		}
		x3<<=1;
	}

	if (D<9) 
    {
		WCPUBYTE(S,(Byte)(x1&0xff));  
	}
	else 
    {
		wrword(S,x1);
	}

	if (D<8) {
		nCycleCount+=42;
	} else if (D < 9) {
		nCycleCount+=44;
	} else if (D < 16) {
		nCycleCount+=58;
	} else {
		nCycleCount+=60;
	}

	if (D>8) return;

	reset_LGT_AGT_EQ;
	if (D<9) {
		ST|=BStatusLookup[x1&0xff]&mask_LGT_AGT_EQ_OP;
	} else {
		ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;
	}
}

void op_tb()
{ 
	// Test Bit: TB src
	// Tests a CRU bit

	Word add;

	FormatII;
	add=(romword(WP+24)>>1)&0xfff;
	if (D&0x80) {
		add-=128-(D&0x7f);
	} else {
		add+=D;
	}

	if (rcru(add)) set_EQ; else reset_EQ;

	nCycleCount+=12;
}

// These instructions are valid 9900 instructions but are invalid on the TI-99, as they generate
// improperly decoded CRU instructions.

void op_ckof()
{ 
	warn("ClocK OFf instruction encountered!");					// not supported on 99/4A
	// This will set A0-A2 to 110 and pulse CRUCLK (so not emulated)

	nCycleCount+=12;
}

void op_ckon()
{ 
	warn("ClocK ON instruction encountered!");					// not supported on 99/4A
	// This will set A0-A2 to 101 and pulse CRUCLK (so not emulated)

	nCycleCount+=12;
}

void op_idle()
{
	warn("IDLE instruction encountered!");						// not supported on 99/4A
	// This sets A0-A2 to 010, and pulses CRUCLK until an interrupt is received
	// Although it's not supposed to be used on the TI, at least one game
	// (Slymoids) uses it - perhaps to sync with the VDP? So we'll emulate it someday

	nCycleCount+=12;
}

void op_rset()
{
	warn("ReSET instruction encountered!");						// not supported on 99/4A
	// This will set A0-A2 to 011 and pulse CRUCLK (so not emulated)
	// However, it does have an effect, it zeros the interrupt mask
	ST&=0xfff0;

	nCycleCount+=12;
}

void op_lrex()
{
	warn("Load or REstart eXecution instruction encountered!");	// not supported on 99/4A
	// This will set A0-A2 to 111 and pulse CRUCLK (so not emulated)

	nCycleCount+=12;
}

void op_li()
{
	// Load Immediate: LI src, imm

	FormatVIII_1;
	wrword(D,S);
	
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[S]&mask_LGT_AGT_EQ;

	nCycleCount+=12;
}

void op_limi()
{ 
	// Load Interrupt Mask Immediate: LIMI imm
	// Sets the CPU interrupt mask

	FormatVIII_1;
	ST=(ST&0xfff0)|(S&0xf);

	nCycleCount+=16;
}

void op_lwpi()
{ 
	// Load Workspace Pointer Immediate: LWPI imm
	// changes the Workspace Pointer

	FormatVIII_1;
	WP=S;

	nCycleCount+=10;
}

void op_mov()
{ 
	// MOVe words: MOV src, dst

	Word x1;

	FormatI;
	x1=romword(S);
	
	wrword(D,x1);
  
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	nCycleCount+=14;
}

void op_movb()
{ 
	// MOVe Bytes: MOVB src, dst

	Byte x1;

	FormatI;
	x1=RCPUBYTE(S);
	
	WCPUBYTE(D,x1);
	
	reset_LGT_AGT_EQ;
	ST|=BStatusLookup[x1]&mask_LGT_AGT_EQ_OP;

	nCycleCount+=14;
}

void op_stst()
{ 
	// STore STatus: STST src
	// Copy the status register to memory

	FormatVIII_0;
	wrword(D,ST);

	nCycleCount+=8;
}

void op_stwp()
{ 
	// STore Workspace Pointer: STWP src
	// Copy the workspace pointer to memory

	FormatVIII_0;
	wrword(D,WP);

	nCycleCount+=8;
}

void op_swpb()
{ 
	// SWaP Bytes: SWPB src
	// swap the high and low bytes of a word

	Word x1,x2;

	FormatVI;
	x1=romword(S);

	x2=((x1&0xff)<<8)|(x1>>8);
	wrword(S,x2);

	nCycleCount+=10;
}

void op_andi()
{ 
	// AND Immediate: ANDI src, imm

	Word x1,x2;

	FormatVIII_1;

	x1=romword(D);
	x2=x1&S;
	wrword(D,x2);
	
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x2]&mask_LGT_AGT_EQ;

	nCycleCount+=14;
}

void op_ori()
{ 
	// OR Immediate: ORI src, imm

	Word x1,x2;

	FormatVIII_1;

	x1=romword(D);
  	x2=x1|S;
	wrword(D,x2);
  
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x2]&mask_LGT_AGT_EQ;

	nCycleCount+=14;
}

void op_xor()
{ 
	// eXclusive OR: XOR src, dst

	Word x1,x2,x3;

	FormatIII;
	x1=romword(S);
	x2=romword(D);
  
	x3=x1^x2;
	wrword(D,x3);
  
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x3]&mask_LGT_AGT_EQ;

	nCycleCount+=14;
}

void op_inv()
{ 
	// INVert: INV src

	Word x1;

	FormatVI;

	x1=romword(S);
  	x1=~x1;
	wrword(S,x1);
  
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	nCycleCount+=10;
}

void op_clr()
{ 
	// CLeaR: CLR src
	// sets word to 0

	FormatVI;
	wrword(S,0);

	nCycleCount+=10;
}

void op_seto()
{ 
	// SET to One: SETO src
	// sets word to 0xffff

	FormatVI;
	wrword(S,0xffff);

	nCycleCount+=10;
}

void op_soc()
{ 
	// Set Ones Corresponding: SOC src, dst
	// Essentially performs an OR - setting all the bits in dst that
	// are set in src

	Word x1,x2,x3;

	FormatI;
	x1=romword(S);
	x2=romword(D);
  
	x3=x1|x2;
	wrword(D,x3);
  
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x3]&mask_LGT_AGT_EQ;

	nCycleCount+=14;
}

void op_socb()
{ 
	// Set Ones Corresponding, Byte: SOCB src, dst

	Byte x1,x2,x3;

	FormatI;
	x1=RCPUBYTE(S);
	x2=RCPUBYTE(D);
  
	x3=x1|x2;
    WCPUBYTE(D,x3);

	reset_LGT_AGT_EQ;
	ST|=BStatusLookup[x3]&mask_LGT_AGT_EQ_OP;

	nCycleCount+=14;
}

void op_szc()
{ 
	// Set Zeros Corresponding: SZC src, dst
	// Zero all bits in dest that are zeroed in src

	Word x1,x2,x3;

	FormatI;
	x1=romword(S);
	x2=romword(D);
  
	x3=(~x1)&x2;
	wrword(D,x3);
  
	reset_LGT_AGT_EQ;
	ST|=WStatusLookup[x3]&mask_LGT_AGT_EQ;

	nCycleCount+=14;
}

void op_szcb()
{ 
	// Set Zeros Corresponding, Byte: SZCB src, dst

	Byte x1,x2,x3;

	FormatI;
	x1=RCPUBYTE(S);
	x2=RCPUBYTE(D);
  
	x3=(~x1)&x2;
	WCPUBYTE(D,x3);

	reset_LGT_AGT_EQ;
	ST|=BStatusLookup[x3]&mask_LGT_AGT_EQ_OP;

	nCycleCount+=14;
}

void op_sra()
{ 
	// Shift Right Arithmetic: SRA src, dst
	// For the shift instructions, a count of '0' means use the
	// value in register 0. If THAT is zero, the count is 16.
	// The arithmetic operations preserve the sign bit

	Word x1,x3,x4; 
	int x2;

	FormatV;
	if (D==0)
	{ 
		D=romword(WP) & 0xf;
		if (D==0) D=16;
		nCycleCount+=8;
	}
	x1=romword(S);
	x4=x1&0x8000;
	x3=0;
  
	for (x2=0; x2<D; x2++)
	{ 
		x3=x1&1;   /* save carry */
	    x1=x1>>1;  /* shift once */
	    x1=x1|x4;  /* extend sign bit */
	}
	wrword(S,x1);
  
	reset_EQ_LGT_AGT_C;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x3) set_C;

	nCycleCount+=12+2*D;
}

void op_srl()
{ 
	// Shift Right Logical: SRL src, dst
	// The logical shifts do not preserve the sign

	Word x1,x3; 
	int x2;

	FormatV;
	if (D==0)
	{ 
		D=romword(WP)&0xf;
		if (D==0) D=16;
		nCycleCount+=8;
	}
	x1=romword(S);
	x3=0;
  
	for (x2=0; x2<D; x2++)
	{ 
		x3=x1&1;
	    x1=x1>>1;
	}
	wrword(S,x1);

	reset_EQ_LGT_AGT_C;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x3) set_C;

	nCycleCount+=12+2*D;
}

void op_sla()
{ 
	// Shift Left Arithmetic: SLA src, dst

	Word x1,x3,x4; 
	int x2;

	FormatV;
	if (D==0)
	{ 
		D=romword(WP)&0xf;
		if (D==0) D=16;
		nCycleCount+=8;
	}
	x1=romword(S);
	x4=x1&0x8000;
	reset_EQ_LGT_AGT_C_OV;

	x3=0;
	for (x2=0; x2<D; x2++)
	{ 
		x3=x1&0x8000;
		x1=x1<<1;
		if ((x1&0x8000)!=x4) set_OV;
	}
	wrword(S,x1);
  
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x3) set_C;

	nCycleCount+=12+2*D;
}

void op_src()
{ 
	// Shift Right Circular: SRC src, dst
	// Circular shifts pop bits off one end and onto the other
	// The carry bit is not a part of these shifts, but it set
	// as appropriate

	Word x1,x3,x4; 
	int x2;

	FormatV;
	if (D==0)
	{ 
		D=romword(WP)&0xf;
		if (D==0) D=16;
		nCycleCount+=8;
	}
	x1=romword(S);
	x3=0;
	for (x2=0; x2<D; x2++)
	{ 
		x4=x1&0x1;
		x1=x1>>1;
		if (x4) 
		{
			x1=x1|0x8000;
		}
		x3=x1&0x8000;
	}
	wrword(S,x1);
  
	reset_EQ_LGT_AGT_C;
	ST|=WStatusLookup[x1]&mask_LGT_AGT_EQ;

	if (x3) set_C;

	nCycleCount+=12+2*D;
}

void op_bad()
{ 
	warn("Illegal opcode!");								// Don't know this Opcode
	nCycleCount+=6;
}
