#
# PIC-17C7xx Instruction Section
#   includes token definitions, macros, sub-constructors and instruction definitions
#

# 16-bit instruction token uses big-endian bit numbering which agrees with
# instruction bit numbering with PIC documentation.
# 	15-14-13-12-11-10-9-8-7-6-5-4-3-2-1-0 
define token instr16(16)
	op16 =	(0,15)
	op8 =	(8,15)
	op7 =	(9,15)
	op6 =	(10,15)
	op5 =	(11,15)
	op3 =	(13,15)
	t =		(9,9)
	d =		(8,8)
	s =		(8,8)
	i =		(8,8)
	b3 =	(8,10)
	p5_4 =	(12,12)
	p5_3 =	(11,11)
	p5 =	(8,12)
	p5reg =	(8,12)
	u4hi =	(4,7)
	u4lo =	(0,3)
	f8 =	(0,7)
	f8hi =	(5,7)
	f8_4 =	(4,4)
	f8_3 =	(3,3)
	f8reg = (0,4) 
	k8 =	(0,7)
	k8_h =	(4,7)
	k8_l =	(0,3)
	k13 =	(0,12)
;

attach variables [ f8reg p5reg ] [ 
    INDF0	FSR0	PCL		PCLATH	ALUSTA	T0STA	CPUSTA	INTSTA
   	INDF1	FSR1	WREG	TMR0L	TMR0H	TBLPTRL	TBLPTRH	BSR
   	_		_		_		_		_		_		_		_
   	PRODL	PRODH	_		_		_		_		_		_
];

attach variables [ t ] [ TBLATL TBLATH ];

#
# Special PIC-17 Operations
#

# Return a decimal adjusted value for the value provided (see DAW instruction)
define pcodeop decimalAdjust;

# Perform a Master Clear Reset
define pcodeop reset;

define pcodeop clearWatchDogTimer;

define pcodeop sleep;

#
# MACROS
#

macro setResultFlags(result) {
	Z = (result == 0);
}

macro setAddCOverflowFlag(op1,op2) {
	local tmpC = C & 1;
	OV = scarry(op1,tmpC) || scarry(op2,op1 + tmpC);
}

macro setAddCCarryFlag(op1,op2) {
	local tmpC = C & 1;
	C = carry(op1,tmpC) || carry(op2,op1 + tmpC);
}

macro setAddCDigitCarryFlag(op1,op2) {
	# op1 and op2 are assumed to be 8-bit values
	local tmp1 = op1 << 4;
	local tmp2 = op2 << 4;
	local tmpDC = DC & 1;
	DC = carry(tmp1,tmpDC) || carry(tmp2,tmp1 + tmpDC);
}

macro setAddCFlags(op1,op2) {
	setAddCCarryFlag(op1,op2);
	setAddCDigitCarryFlag(op1,op2);
	setAddCOverflowFlag(op1,op2);
}

macro setAddFlags(op1,op2) {
	C = carry(op1,op2);
	DC = carry(op1<<4,op2<<4);
	OV = scarry(op1,op2);
}

macro setSubtractCOverflowFlag(op1,op2) {
	local notC = ~(C & 1);
	OV = sborrow(op1,notC) || sborrow(op2,op1 - notC);
}

macro setSubtractCCarryFlag(op1,op2) {
	local notC = ~(C & 1);
	C = (op1 < notC) || (op2 < (op1 - notC));
}

macro setSubtractCDigitCarryFlag(op1,op2) {
	# op1 and op2 are assumed to be 8-bit values
	local notDC = ~(DC & 1);
	local tmp1 = op1 << 4;
	local tmp2 = op2 << 4;
	local tmp3 = (tmp1 - notDC) << 4;
	DC = (tmp1 < notDC) || (tmp2 < tmp3);
}

macro setSubtractCFlags(op1,op2) {
	setSubtractCCarryFlag(op1,op2);
	setSubtractCDigitCarryFlag(op1,op2);
	setSubtractCOverflowFlag(op1,op2);
}

macro setSubtractFlags(op1,op2) {
	# op1 and op2 are assumed to be 8-bit values
	# NOTE:  carry flag is SET if there is NO borrow
	C = (op1 >= op2);
	DC = ((op1<<4) < (op2<<4));
	OV = sborrow(op1,op2);
}

macro push(val) {	# TODO: Uncertain about this !!
#	CheckStackFull();
	*[HWSTACK]:2 STKPTR = val;
	STKPTR = STKPTR + 2;
}

macro pop(rval) {	# TODO: Uncertain about this !!
#	CheckStackUnderflow();
	STKPTR = STKPTR - 2;
	rval = *[HWSTACK]:2 STKPTR;
}

#
# SUB-CONSTRUCTORS
#

# PC register write - instruction must set PCLATH/PCL and perform branch operation
fPC: "PC" 			is f8=0x02				{ export PCL; }
pPC: "PC" 			is p5=0x02				{ export PCL; }

# ALUSTA register
fALUSTA: f8reg 		is f8=0x04 & f8reg		{ export f8reg; }
#pALUSTA: p5reg 		is p5=0x04 & p5reg		{ export p5reg; }

#
# f Register  subconstructors
#

# 0x00-0x0f Unbanked registers
fREGLoc: f8reg		is f8hi=0 & f8_4=0 & f8reg			{ export f8reg; }

# 0x10-0x1f Banked registers
fREGLoc: f8			is f8hi=0 & f8_4=1 & f8_3=0 & f8	{ ptr:2 = (zext(BSR & 0x0f) << 8) + f8; export *[DATA]:1 ptr; }

# 0x18-0x19 Unbanked registers (PRODL,PRODH)
fREGLoc: f8reg		is f8=0x18 & f8reg					{ export f8reg; }
fREGLoc: f8reg		is f8=0x19 & f8reg					{ export f8reg; }

# Unbanked general purpose RAM
fREGLoc: f8			is f8hi=0 & f8_4=1 & f8_3=1 & f8	{ export *[DATA]:1 f8; }

# Banked general purpose RAM
fREGLoc: f8			is f8								{ ptr:2 = (zext(BSR & 0xf0) << 4) + f8; export *[DATA]:1 ptr; }


# Indirect File Register access - INDF0
fREGLoc: f8reg		is f8=0x00 & f8reg		{
	addr:1 = FSR0;
	val:1 = ((FS10 == 0x1) * 1) + ((FS10 == 0x0) * -1);
	FSR0 = addr + val;
	export *[DATA]:1 addr; 
}

# Indirect File Register access - INDF1
fREGLoc: f8reg		is f8=0x08 & f8reg		{
	addr:1 = FSR1;
	val:1 = ((FS32 == 0x1) * 1) + ((FS32 == 0x0) * -1);
	FSR1 = addr + val;
	export *[DATA]:1 addr; 
}


#
# p Register subconstructors
#

# 0x00-0x0f Unbanked registers
pREGLoc: p5reg		is p5_4=0 & p5reg			{ export p5reg; }

# 0x10-0x17 Banked registers
pREGLoc: p5			is p5_4=1 & p5_3=0 & p5		{ ptr:2 = (zext(BSR & 0x0f) << 8) + p5; export *[DATA]:1 ptr; }

# 0x18-0x19 Unbanked registers (PRODL,PRODH)
pREGLoc: p5reg		is p5=0x18 & p5reg			{ export p5reg; }
pREGLoc: p5reg		is p5=0x19 & p5reg			{ export p5reg; }

# Unbanked general purpose RAM
pREGLoc: p5			is p5_4=1 & p5_3=1 & p5	{ export *[DATA]:1 p5; }

# Indirect File Register access - INDF0
pREGLoc: p5reg		is p5=0x00 & p5reg		{
	addr:1 = FSR0;
	val:1 = ((FS10 == 0x1) * 1) + ((FS10 == 0x0) * -1);
	FSR0 = addr + val;
	export *[DATA]:1 addr; 
}

# Indirect File Register access - INDF1
pREGLoc: p5reg		is p5=0x08 & p5reg		{
	addr:1 = FSR1;
	val:1 = ((FS32 == 0x1) * 1) + ((FS32 == 0x0) * -1);
	FSR1 = addr + val;
	export *[DATA]:1 addr; 
}


# Direct File register data
srcFREG: fREGLoc		is fREGLoc									{ export fREGLoc; }

# PCL read - latch PC into PCL and PCLATH
srcFREG: "PC"		is f8=0x02						{
	PCLAT = inst_start;
	export PCL; 
}

# Destination register (always fREGLoc)
destFREG: fREGLoc		is fREGLoc									{ export fREGLoc; }

# Destination register (either fREGLoc or WREG)
destREG: "0"	is d=0												{ export WREG; }
destREG: "1"	is d=1 & fREGLoc									{ export fREGLoc; }

# Direct File register data
srcPREG: pREGLoc		is pREGLoc									{ export pREGLoc; }

# PCL read - latch PC into PCL and PCLATH
srcPREG: "PC"		is p5=0x02						{
	PCLAT = inst_start;
	export PCL; 
}

# Destination register (always pREGLoc)
destPREG: pREGLoc		is pREGLoc									{ export pREGLoc; }

# Destination operand representation (w: W register is destination; f: specified fREG is destination)
D: "w"		is d=0										{ }
D: "f"		is d=1										{ }

# s-flag used by those instructions which can optionally store result in both srcFREG and WREG
S: "0"		is s=0										{ }
S: "1"		is s=1										{ }

# Table read/write i-flag
I: "0"		is i=0										{ }
I: "1"		is i=1										{ }

# Table read/write t-flag identifies table latch register (high or low byte)
T: t		is t										{ export t; }

# Relative instruction location with an 8K page
shortAddr: nLoc is k13				[ nLoc = (inst_next & 0xe000) + k13; ]	{ 
	tmp:2 = nLoc:2 >> 8;
	PCLATH = tmp:1;
	export *[CODE]:2 nLoc;
}

# Absolute instruction location within 64K space (PCLATH contain upper 8-bits)
longAddr: k8	is k8														{
	addr:2 = (zext(PCLATH) << 8) + k8;
	export addr;
}

# Skip instruction address
skipInst: inst_skip	is op16	[ inst_skip = inst_next + 1; ]		{export *[CODE]:2 inst_skip; }

# Immediate Data (Literal operation)
imm8: "#"k8		is k8											{ export *[const]:1 k8; }
imm8h: "#"k8_h	is k8_h											{ export *[const]:1 k8_h; }
imm8l: "#"k8_l	is k8_l											{ export *[const]:1 k8_l; }

# Bit identifier
bit: "#"b3		is b3											{ export *[const]:1 b3; }


#
# Instructions
#

:ADDLW imm8		is op8=0xb1 & imm8		{
	# 1011 0001 kkkk kkkk
	tmp1:1 = WREG;
	tmp2:1 = imm8;
	setAddFlags(tmp1, tmp2); 
	local tmp = tmp1 + tmp2;
	WREG = tmp;
	setResultFlags(tmp);
}

:ADDWF srcFREG, D	is op7=0x07 & D & srcFREG & destREG {
	# 0000 111d ffff ffff
	tmp1:1 = srcFREG; # read only once!
	tmp2:1 = WREG;
	setAddFlags(tmp1, tmp2); 
	local tmp = tmp1 + tmp2;
	destREG = tmp;
	setResultFlags(tmp);
}

:ADDWF fPC, D		is op7=0x07 & D & d=1 & fPC	{
	# 0000 111d ffff ffff
	# 0000 1110 ffff ffff  ->  ADDWF PCL, w
	addr:2 = inst_start >> 1; # Compenstate for CODE wordsize
	addrHi:1 = addr(1);
	PCLATH = addrHi;
	addrLo:1 = addr:1;
	tmpW:1 = WREG;
	setAddFlags(addrLo, tmpW);
	addrLo = addrLo + tmpW;
	addr = (zext(addrHi) << 8) + zext(addrLo);
	setResultFlags(addrLo);
	goto [addr];
}

:ADDWFC srcFREG, D	is op7=0x08 & D & srcFREG & destREG {
	# 0001 000d ffff ffff
	local tmpC = C & 1;
	tmp1:1 = srcFREG; # read only once!
	tmp2:1 = WREG;
	setAddCFlags(tmp1, tmp2); 
	local tmp = tmp1 + tmp2 + tmpC;
	destREG = tmp;
	setResultFlags(tmp);
}

:ANDLW imm8	is op8=0xb5 & imm8	{
	# 1011 0101 kkkk kkkk
	tmp:1 = WREG & imm8;
	WREG = tmp;
	setResultFlags(tmp);
}

:ANDWF srcFREG, D	is op7=0x05 & D & srcFREG & destREG {
	# 0000 101d ffff ffff
	tmp:1 = srcFREG & WREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:BCF srcFREG, bit			is op5=0x11 & bit & srcFREG							{
	#  1000 1bbb ffff ffff
	local bitmask = ~(1 << bit);
	srcFREG = srcFREG & bitmask;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=0 & fALUSTA & bit							{
	#  1000 1000 0000 0100	->	BCF ALUSTA, #C
	C = 0;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=1 & fALUSTA & bit							{
	#  1000 1001 0000 0100	->	BCF ALUSTA, #DC
	DC = 0;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=2 & fALUSTA & bit							{
	#  1000 1010 0000 0100	->	BCF ALUSTA, #Z
	Z = 0;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=3 & fALUSTA & bit							{
	#  1000 1011 0000 0100	->	BCF ALUSTA, #OV
	OV = 0;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=4 & fALUSTA & bit							{
	#  1000 1100 0000 0100	->	BCF ALUSTA, #FS0
	FS10 = FS10 & 0x2;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=5 & fALUSTA & bit							{
	#  1000 1101 0000 0100	->	BCF ALUSTA, #FS1
	FS10 = FS10 & 0x1;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=6 & fALUSTA & bit							{
	#  1000 1110 0000 0100	->	BCF ALUSTA, #FS2
	FS32 = FS32 & 0x2;
}

:BCF fALUSTA, bit			is op5=0x11 & b3=7 & fALUSTA & bit							{
	#  1000 1111 0000 0100	->	BCF ALUSTA, #FS3
	FS32 = FS32 & 0x1;
}

:BSF srcFREG, bit			is op5=0x10 & bit & srcFREG				{
	# 1000 0bbb ffff ffff
	local bitmask = 1 << bit;
	srcFREG = srcFREG | bitmask;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=0 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #C
	C = 1;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=1 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #DC
	DC = 1;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=2 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #Z
	Z = 1;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=3 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #OV
	OV = 1;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=4 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #FS0
	FS10 = FS10 | 0x1;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=5 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #FS1
	FS10 = FS10 | 0x2;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=6 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #FS2
	FS32 = FS32 | 0x1;
}

:BSF fALUSTA, bit			is op5=0x10 & b3=7 & bit & fALUSTA		{
	# 1000 0000 0000 0100	->	BSF ALUSTA, #FS3
	FS32 = FS32 | 0x2;
}

:BTFSC srcFREG, bit		is op5=0x13 & bit & srcFREG	& skipInst				{
	#  1001 1bbb ffff ffff
	local bitmask = 1 << bit;
	local tmp = srcFREG & bitmask;
	if (tmp == 0) goto skipInst;
}

:BTFSC fALUSTA, bit		is op5=0x13 & b3=0 & bit & fALUSTA & skipInst				{
	#  1001 1000 0000 0100	->	BTFSC STATUS, #C
	if (C == 0) goto skipInst;
}

:BTFSC fALUSTA, bit		is op5=0x13 & b3=1 & bit & fALUSTA & skipInst				{
	#  1001 1001 0000 0100	->	BTFSC STATUS, #DC
	if (DC == 0) goto skipInst;
}

:BTFSC fALUSTA, bit		is op5=0x13 & b3=2 & bit & fALUSTA & skipInst				{
	#  1001 1010 0000 0100	->	BTFSC STATUS, #Z
	if (Z == 0) goto skipInst;
}

:BTFSC fALUSTA, bit		is op5=0x13 & b3=3 & bit & fALUSTA & skipInst				{
	#  1001 1011 0000 0100	->	BTFSC STATUS, #OV
	if (OV == 0) goto skipInst;
}

:BTFSS srcFREG, bit		is op5=0x12 & bit & srcFREG	& skipInst				{
	#  1001 0bbb ffff ffff
	local bitmask = 1 << bit;
	local tmp = srcFREG & bitmask;
	if (tmp != 0) goto skipInst;
}

:BTFSS fALUSTA, bit		is op5=0x12 & b3=0 & bit & fALUSTA & skipInst				{
	#  1001 1000 0000 0100	->	BTFSS STATUS, #C
	if (C != 0) goto skipInst;
}

:BTFSS fALUSTA, bit		is op5=0x12 & b3=1 & bit & fALUSTA & skipInst				{
	#  1001 1001 0000 0100	->	BTFSS STATUS, #DC
	if (DC != 0) goto skipInst;
}

:BTFSS fALUSTA, bit		is op5=0x12 & b3=2 & bit & fALUSTA & skipInst				{
	#  1001 1010 0000 0100	->	BTFSS STATUS, #Z
	if (Z != 0) goto skipInst;
}

:BTFSS fALUSTA, bit		is op5=0x12 & b3=3 & bit & fALUSTA & skipInst				{
	#  1001 1011 0000 0100	->	BTFSS STATUS, #OV
	if (OV != 0) goto skipInst;
}

:BTG srcFREG, bit		is op5=0x7 & bit & srcFREG & skipInst 		{
	# 0011 1bbb ffff ffff
	local bitmask = 1 << bit;
	tmp:1 = srcFREG;
	srcFREG = ~(tmp & bitmask) | (tmp & ~bitmask);
}

:CALL shortAddr			is op3=0x7 & shortAddr						{
	# 111k kkkk kkkk kkkk
	push(&:2 inst_next);
	call shortAddr;
}

# Special case for Call which appears to correspond to uninitialized
:BADCALL shortAddr			is op16=0xffff & shortAddr		{ addr:2 = shortAddr; return [addr]; }

:CLRF destFREG, S		is op7=0x14 & s=0 & S & destFREG					{
	# 0010 1000 ffff ffff
	destFREG = 0;
	WREG = 0;
}

:CLRF destFREG, S		is op7=0x14 & s=1 & S & destFREG					{
	# 0010 1001 ffff ffff
	destFREG = 0;
}

:CLRF fALUSTA, S		is op7=0x14 & s=0 & S & fALUSTA					{
	# 0010 1000 0000 0100
	C = 0;
	DC = 0;
	Z = 0;
	OV = 0;
	FS10 = 0;
	FS32 = 0;
	WREG = 0;
}

:CLRF fALUSTA, S		is op7=0x14 & s=1 & S & fALUSTA					{
	# 0010 1001 0000 0100
	C = 0;
	DC = 0;
	Z = 0;
	OV = 0;
	FS10 = 0;
	FS32 = 0;
}

:CLRWDT				is op16=0x0004 			{
	# 0000 0000 0000 0100
	clearWatchDogTimer();
}

:COMF srcFREG, D	is op7=0x09 & D & srcFREG & destREG			{
	# 0001 001d ffff ffff
	tmp:1 = ~srcFREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:CPFSEQ srcFREG		is op8=0x31 & srcFREG & skipInst		{
	# 0011 0001 ffff ffff
	if (srcFREG == WREG) goto skipInst;
}

:CPFSGT srcFREG		is op8=0x32 & srcFREG & skipInst		{
	# 0011 0010 ffff ffff
	if (srcFREG > WREG) goto skipInst;
}

:CPFSLT srcFREG		is op8=0x30 & srcFREG & skipInst		{
	# 0011 0000 ffff ffff
	if (srcFREG < WREG) goto skipInst;
}

:DAW destFREG, S	is op7=0x17 & s=0 & S & destFREG			{ 
	#  0010 1110 ffff ffff
	tmp:1 = decimalAdjust(WREG);
	destFREG = tmp;
	WREG = tmp;
}

:DAW destFREG, S	is op7=0x17 & s=1 & S & destFREG			{ 
	# 0010 1111 ffff ffff
	tmp:1 = decimalAdjust(WREG);
	destFREG = tmp;
	setResultFlags(tmp);
}

:DECF srcFREG, D	is op7=0x03 & D & srcFREG & destREG		{
	# 0000 011d ffff ffff
	tmp:1 = srcFREG;
	setSubtractFlags(tmp, 1); 
	tmp = tmp - 1;
	destREG = tmp;
	setResultFlags(tmp);
}

:DECFSZ srcFREG, D	is op7=0x0b & D & srcFREG & destREG	& skipInst	{
	# 0001 011d ffff ffff
	val:1 = srcFREG - 1;
	destREG = val;
	if (val == 0) goto skipInst;
}

:DCFSNZ srcFREG, D	is op7=0x13 & D & srcFREG & destREG & skipInst	{
	# 0010 011d ffff ffff
	val:1 = srcFREG - 1;
	destREG = val;
	if (val != 0) goto skipInst;
}

:GOTO shortAddr		is op3=0x6 & shortAddr {
	# 110k kkkk kkkk kkkk
	goto shortAddr;
}

:INCF srcFREG, D	is op7=0x0a & D & srcFREG & destREG			{
	# 0001 010d ffff ffff
	tmp:1 = srcFREG; # read once only!
	setAddFlags(tmp, 1); 
	tmp = tmp + 1;
	destREG = tmp;
	setResultFlags(tmp);
}

:INCFSZ srcFREG, D	is op7=0x0f & D & srcFREG & destREG	& skipInst		{
	# 0001 111d ffff ffff
	val:1 = srcFREG + 1;
	destREG = val;
	if (val == 0) goto skipInst;	
}

:INFSNZ srcFREG, D	is op7=0x12 & D & srcFREG & destREG	& skipInst		{
	# 0010 010d ffff ffff
	val:1 = srcFREG + 1;
	destREG = val;
	if (val != 0) goto skipInst;	
}

:IORLW imm8			is op8=0xb3 & imm8		{
	# 1011 0011 kkkk kkkk
	tmp:1 = WREG | imm8;
	WREG = tmp;
	setResultFlags(tmp);
}

:IORWF srcFREG, D	is op7=0x04 & D & srcFREG & destREG		{
	# 0000 100d ffff ffff
	tmp:1 = WREG | srcFREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:LCALL longAddr		is op8=0xb7 & longAddr					{
	# 1011 0111 kkkk kkkk
	push(&:2 inst_next);
	call [longAddr];
}

:MOVFP srcFREG, destPREG	is op3=0x3 & srcFREG & destPREG		{
	# 011p pppp ffff ffff
	destPREG = srcFREG;
}

:MOVFP srcFREG, pPC			is op3=0x3 & srcFREG & pPC		{
	# 0110 0010 ffff ffff
	addr:2 = (zext(PCLATH) << 8) + zext(srcFREG);
	goto [addr];
}

:MOVLB imm8l			is op8=0xb8 & u4hi=0 & imm8l 		{
	# 1011 1000 0000 kkkk
	BSR = (BSR & 0xf0) | imm8l;
}

:MOVLR imm8h			is op7=0x5d & u4lo=0 & imm8h			{
	# 1011 101x kkkk 0000
	BSR = (BSR & 0x0f) | (imm8h << 4);
}

:MOVLW imm8			is op8=0xb0 & imm8				{
	# 1011 0000 kkkk kkkk
	WREG = imm8;
}

:MOVPF srcPREG, destFREG	is op3=0x2 & srcPREG & destFREG		{
	# 010p pppp ffff ffff
	tmp:1 = srcPREG;
	destFREG = tmp;
	setResultFlags(tmp);
}

:MOVPF srcPREG, fPC	is op3=0x2 & srcPREG & fPC		{
	tmp:1 = srcPREG;
	addr:2 = (zext(PCLATH) << 8) + zext(tmp);
	setResultFlags(tmp);
	goto [addr];
}

:MOVWF destFREG				is op8=0x01 & destFREG		{
	# 0000 0001 ffff ffff
	destFREG = WREG;
}

:MOVWF fPC					is op8=0x01 & fPC		{
	addr:2 = (zext(PCLATH) << 8) + zext(WREG);
	goto [addr];
}

:MULLW imm8					is op8=0xbc & imm8		{
	# 1011 1100 kkkk kkkk
	PROD = zext(WREG) * zext(imm8);
}

:MULLWF srcFREG				is op8=0x34	& srcFREG			{
	# 0011 0100 ffff ffff
	PROD = zext(WREG) * zext(srcFREG);
}

:NEGW destFREG, S			is op7=0x16 & s=0 & S & destFREG	{
	# 0010 110s ffff ffff
	tmp:1 = -WREG;
	destFREG = tmp;
	WREG = tmp;
	C = (tmp s< 0);
	OV = sborrow(0,tmp);
	setResultFlags(tmp);
}

:NEGW destFREG, S			is op7=0x16 & s=1 & S & destFREG	{
	# 0010 110s ffff ffff
	tmp:1 = -WREG;
	destFREG = tmp;
	C = (tmp s< 0);
	OV = sborrow(0,tmp);
	setResultFlags(tmp);
}

:NOP						is op16=0x0		{ }

:RETFIE						is op16=0x0005		{
	# 0000 0000 0000 0101
	retAddr:2 = 0;
	pop(retAddr);
	return [retAddr];
}

:RETLW imm8					is op8=0xb6 & imm8		{
	# 1011 0110 kkkk kkkk
	WREG = imm8;
	retAddr:2 = 0;
	pop(retAddr);
	return [retAddr];
}

:RETURN 					is op16=0x0002			{
	# 0000 0000 0000 0010
	retAddr:2 = 0;
	pop(retAddr);
	return [retAddr];
}

:RLCF srcFREG, D			is op7=0x0d & D & srcFREG & destREG		{
	# 0001 101d ffff ffff
	local tmpC = C;
	val:1 = srcFREG;
	C = (val s< 0);
	val = (val << 1) | tmpC;
	destREG = val;
}

:RLNCF srcFREG, D			is op7=0x11 & D & srcFREG & destREG		{
	# 0010 001d ffff ffff
	tmp:1 = srcFREG << 1;
	destREG = tmp;
}

:RRCF srcFREG, D			is op7=0x0c & D & srcFREG & destREG		{
	# 0001 100d ffff ffff
	local tmpC = C << 7;
	tmp:1 = srcFREG;
	C = (tmp & 1) != 0;
	tmp = (tmp >> 1) | tmpC;
	destREG = tmp;
}

:RRNCF srcFREG, D			is op7=0x10 & D & srcFREG & destREG		{
	# 0010 000d ffff ffff
	tmp:1 = srcFREG >> 1;
	destREG = tmp;
}

:SETF destFREG, S			is op7=0x15 & s=0 & S & destFREG		{
	# 0010 1010 ffff ffff
	destFREG = 0xff;
	WREG = 0xff;
}

:SETF destFREG, S			is op7=0x15 & s=1 & S & destFREG		{
	# 0010 1011 ffff ffff
	destFREG = 0xff;
}

:SETF fALUSTA, S		is op7=0x15 & s=0 & S & fALUSTA					{
	# 0010 1010 0000 0100
	C = 1;
	DC = 1;
	Z = 1;
	OV = 1;
	FS10 = 0x3;
	FS32 = 0x3;
	WREG = 0xff;
}

:SETF fALUSTA, S		is op7=0x15 & s=1 & S & fALUSTA					{
	# 0010 1011 0000 0100
	C = 1;
	DC = 1;
	Z = 1;
	OV = 1;
	FS10 = 0x3;
	FS32 = 0x3;
}

:SLEEP						is op16=0x0003		{
	# 0000 0000 0000 0011
	sleep();
}

:SUBLW imm8					is op8=0xb2 & imm8		{
	# 1011 0010 kkkk kkkk
	tmp:1 = imm8;
	tmpW:1 = WREG;
	setSubtractFlags(tmp, tmpW); 
	tmp = tmp - tmpW;
	WREG = tmp;
	setResultFlags(tmp);
}

:SUBWF srcFREG, D		is op7=0x02 & D & srcFREG & destREG		{
	# 0000 010d ffff ffff
	tmp:1 = srcFREG;
	tmpW:1 = WREG;
	setSubtractFlags(tmp, tmpW); 
	tmp = tmp - tmpW;
	destREG = tmp;
	setResultFlags(tmp);
}

:SUBWFB srcFREG, D		is op7=0x01 & D & srcFREG & destREG		{
	# 0000 001d ffff ffff
	local notC = ~(C & 1);
	tmp:1 = srcFREG;
	tmpW:1 = WREG;
	setSubtractCFlags(tmp, tmpW); 
	tmp = tmp - tmpW - notC;
	destREG = tmp;
	setResultFlags(tmp);
}

:SWAPF srcFREG, D		is op7=0x0e & D & srcFREG & destREG		{
	# 0001 110d ffff ffff
	tmp:1 = srcFREG;
	destREG = (tmp << 4) | (tmp >> 4);
}

:TABLRD T, I, destFREG		is op6=0x2a & T & I & i & destFREG			{
	# 1010 10ti ffff ffff
	destFREG = T;
	ptr:2 = TBLPTR;
	TBLAT = *[CODE]:2 ptr;
	TBLPTR = ptr + i;
}

:TABLWT T, I, srcFREG		is op6=0x2b & T & I & i & srcFREG			{
	# 1010 11ti ffff ffff
	T = srcFREG;
	ptr:2 = TBLPTR;
	*[CODE]:2 ptr = TBLAT;
	TBLPTR = ptr + i;
}

:TLRD T, destFREG			is op6=0x28 & T & destFREG			{
	# 1010 00tx ffff ffff
	destFREG = T;
}

:TLWT T, srcFREG			is op6=0x29 & T & srcFREG			{
	# 1010 01tx ffff ffff
	T = srcFREG;
}

:TSTFSZ srcFREG				is op8=0x33 & srcFREG & skipInst	{
	# 0011 0011 ffff ffff
	if (srcFREG == 0) goto skipInst;
} 

:XORLW imm8					is op8=0xb4 & imm8			{
	# 1011 0100 kkkk kkkk
	tmp:1 = WREG ^ imm8;
	WREG = tmp;
	setResultFlags(tmp);
}

:XORWF srcFREG, D			is op7=0x06 & D & srcFREG & destREG		{
	# 0000 110d ffff ffff
	tmp:1 = WREG ^ srcFREG;
	destREG = tmp;
	setResultFlags(tmp);
}
