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

# Little-endian bit numbering
define token instr16(16)
	op12 =	(0,11)
	op6 =	(6,11)
	op4 =	(8,11)
	op3 =	(9,11)
	d =		(5,5)
	b3 =	(5,7)
	f5 =	(0,4)
	f5h =	(4,4)
	k8 =	(0,7)
	k9 =	(0,8)
;

#
# Unsupported Operations
#

define pcodeop clearWatchDogTimer;

define pcodeop sleep;

#
# MACROS
#

# Pack status bits into STATUS register
macro packStatus() {
#	STATUS = (PA << $(STATUS_PA0_BIT))
#				| (Z << $(STATUS_Z_BIT))
#				| (DC << $(STATUS_DC_BIT))
#				| (C << $(STATUS_C_BIT));
}

# Unpack status bits from STATUS register
macro unpackStatus() {
#	PA = (STATUS & $(STATUS_PA_MASK)) >> $(STATUS_PA0_BIT);
#	Z = ((STATUS & $(STATUS_Z_MASK)) != 0);
#	DC = ((STATUS & $(STATUS_DC_MASK)) != 0);
#	C = ((STATUS & $(STATUS_C_MASK)) != 0);
}

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

macro setAddCCarryFlag(op1,op2) {
	C = (carry(op1,C) || carry(op2,op1 + C));
}

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

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

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

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

macro setSubtractCDigitCarryFlag(op1,op2) {
	# op1 and op2 are assumed to be 8-bit values
	local notDC = ~DC;
	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);
}

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));
}

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

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

#
# SUB-CONSTRUCTORS
#

# File register index (f5!=0): bank selection determined by FSR<5:6> bits 
fREGLoc: f5	is f5												{ 
	 addr:1 = (FSR & $(FSR_BSEL_MASK)) + f5;
	 export *[DATA]:1 addr;
}

# File register index (f5=0): INDF use implies indirect data access using FSR value
fREGLoc: "INDF"	is f5=0												{ 
	addr:1 = FSR;  # only low order 7-bits are used for indirect address
	export *[DATA]:1 addr; 
}

# File register index : low 16-bytes of each bank always mapped to Bank-0
fREGLoc: f5	is f5h=0x0 & f5								{ export *[DATA]:1 f5; }

# Special File Registers which have been mirrored into the register space
# to improve decompiler results
fREGLoc: "STATUS"	is f5=0x03								{ packStatus(); export STATUS; }
fREGLoc: "FSR"		is f5=0x04								{ export FSR; }
fREGLoc: "PCL"		is f5=0x02								{ export PCL; }

# File register index (bank selection determined by RP bits in STATUS reg)
srcREG: fREGLoc	is fREGLoc								{ export fREGLoc; }
#srcREG: "STATUS"	is f5=0x03								{ packStatus(); export STATUS; }
#srcREG: "FSR"		is f5=0x04								{ export FSR; }
srcREG: "PCL"		is f5=0x02								{
	# PCL and PA1:PA0 is latched
	addr:2 = inst_start >> 1; # Compenstate for CODE wordsize
	PCL = addr:1;
	addr = (addr >> 9) & 0x3;
	PA = addr:1;
	export PCL; 
}

# Destination register (either srcREG or W)
destREG: "0"	is d=0									{ export W; }
destREG: "1"	is d=1 & srcREG							{ export srcREG; }
#destREG: "1"	is d=1 & f5=0x03						{ export STATUS; }
#destREG: "1"	is d=1 & f5=0x04						{ export FSR; }
#destREG: "1"	is d=1 & f5=0x02						{ 
#	# Storing to PCL causes a branch, 
#	# PC<8> is always cleared for CALL and modifying instructions.
#	# The MOVWF, ADDWF, BSF and BCF definition below has a specific case to handle this write to PCL
#	export PCL; 
#}
#destREG: "1"	is d=1 & f5=0x00 & fREGLoc				{ 
#	# INDF use (indirect data access)
#	export *[DATA]:1 fREGLoc; 
#}

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

# Absolute addresses generated from k8 or k9 and STATUS.PA
absAddr8: k8	is k8									{
	addr:2 = (zext(PA) << 9) + k8;
	export addr; 
}
absAddr9: k9	is k9									{
	addr:2 = (zext(PA) << 9) + k9;
	export addr; 
}

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

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

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

# PC register write - instruction must set PC with PCLATH/PCL and perform branch operation
pcl: "PC" 			is f5=0x02				{ export PCL; }

# STATUS register
status: "STATUS" 	is f5=0x03				{ export STATUS; }

#
# BYTE-ORIENTED FILE REGISTER OPERATIONS
#

:ADDWF srcREG, D	is op6=0x07 & srcREG & D & destREG					{
	#  ---- 0001 11df ffff
	#  0000 0001 1100 0000	->	ADDWF INDF, 0 
	#  0000 0001 1110 0000	->	ADDWF INDF, 1
	#  0000 0001 1101 0010	->	ADDWF 0x12, 0
	#  0000 0001 1111 0010	->	ADDWF 0x12, 1
	tmp:1 = srcREG;
	setAddFlags(W, tmp); 
	tmp = W + tmp;
	destREG = tmp;
	setResultFlags(tmp);
}

:ADDWF pcl, D		is op6=0x07 & D & pcl	{
	#  ---- 0001 11df ffff
	#  0000 0001 1110 0010  ->  ADDWF PCL, w, ACCESS
	addr:2 = (inst_start >> 1) & 0x3f; # shift compenstates for CODE wordsize
	tmpLo:1 = addr:1;
	PA = addr(1);
	setAddFlags(tmpLo, W);
	tmpLo = tmpLo + W;
	setResultFlags(tmpLo);
	addr = (zext(PA) << 9) + zext(tmpLo);
	PCL = tmpLo;
	goto [addr];
}

:ANDLW imm8				is op4=0xe & imm8								{
	#  ---- 1110 kkkk kkkk
	#  0000 1110 0001 0010	->	ANDLW #0x12
	W = W & imm8;
	setResultFlags(W);
}

:ANDWF srcREG, D	is op6=0x05 & srcREG & D & destREG					{
	#  ---- 0001 01df ffff
	#  0000 0001 0100 0000	->	ANDWF INDF, 0 
	#  0000 0001 0110 0000	->	ANDWF INDF, 1
	#  0000 0001 0101 0010	->	ANDWF 0x12, 0
	#  0000 0001 0111 0010	->	ANDWF 0x12, 1
	tmp:1 = W & srcREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:BCF srcREG, bit			is op4=0x4 & bit & srcREG							{
	#  ---- 0100 bbbf ffff
	#  0000 0100 1000 0000	->	BCF INDF, #0x4
	#  0000 0100 1001 0010	->	BCF 0x12, #0x4
	local bitmask = ~(1 << bit);
	srcREG = srcREG & bitmask;
}

:BCF status, bit		is op4=0x4 & b3=0 & bit & status							{
	#  ---- 0100 bbbf ffff
	#  0000 0100 0000 0000	->	BCF STATUS, #C
	C = 0;
	local bitmask = ~(1 << bit);
	STATUS = STATUS & bitmask;
}

:BCF status, bit		is op4=0x4 & b3=1 & bit & status							{
	#  ---- 0100 bbbf ffff
	#  0000 0100 0010 0000	->	BCF STATUS, #DC
	DC = 0;
	local bitmask = ~(1 << bit);
	STATUS = STATUS & bitmask;
}

:BCF status, bit		is op4=0x4 & b3=2 & bit & status							{
	#  ---- 0100 bbbf ffff
	#  0000 0100 0100 0000	->	BCF STATUS, #Z
	Z = 0;
	local bitmask = ~(1 << bit);
	STATUS = STATUS & bitmask;
}

:BCF status, bit		is op4=0x4 & b3=5 & bit & status							{
	#  ---- 0100 bbbf ffff
	#  0000 0100 1010 0000	->	BCF STATUS, #PA0
	PA = PA & 0x1;
	local bitmask = ~(1 << bit);
	STATUS = STATUS & bitmask;
}

:BCF status, bit		is op4=0x4 & b3=6 & bit & status							{
	#  ---- 0100 bbbf ffff
	#  0000 0100 1100 0000	->	BCF STATUS, #PA1
	PA = PA & 0x2;
	local bitmask = ~(1 << bit);
	STATUS = STATUS & bitmask;
}

:BSF srcREG, bit			is op4=0x5 & bit & srcREG							{
	#  ---- 0101 bbbf ffff
	#  0000 0101 1000 0000	->	BSF INDF, #0x4
	#  0000 0101 1001 0010	->	BSF 0x12, #0x4
	local bitmask = 1 << bit;
	srcREG = srcREG | bitmask;
}

:BSF status, bit			is op4=0x5 & b3=0 & bit & status							{
	#  ---- 0101 bbbf ffff
	#  0000 0101 0000 0000	->	BSF STATUS, #C
	C = 1;
	local bitmask = 1 << bit;
	STATUS = STATUS | bitmask;
}

:BSF status, bit			is op4=0x5 & b3=1 & bit & status							{
	#  ---- 0101 bbbf ffff
	#  0000 0101 0010 0000	->	BSF STATUS, #DC
	DC = 1;
	local bitmask = 1 << bit;
	STATUS = STATUS | bitmask;
}

:BSF status, bit			is op4=0x5 & b3=2 & bit & status							{
	#  ---- 0101 bbbf ffff
	#  0000 0101 0100 0000	->	BSF STATUS, #Z
	Z = 1;
	local bitmask = 1 << bit;
	STATUS = STATUS | bitmask;
}

:BSF status, bit			is op4=0x5 & b3=5 & bit & status							{
	#  ---- 0101 bbbf ffff
	#  0000 0101 1010 0000	->	BSF STATUS, #PA0
	PA = PA | 0x1;
	local bitmask = 1 << bit;
	STATUS = STATUS | bitmask;
}

:BSF status, bit			is op4=0x5 & b3=6 & bit & status							{
	#  ---- 0101 bbbf ffff
	#  0000 0101 1100 0000	->	BSF STATUS, #PA1
	PA = PA | 0x2;
	local bitmask = 1 << bit;
	STATUS = STATUS | bitmask;
}

:BTFSC srcREG, bit		is op4=0x6 & bit & srcREG	& skipInst				{
	#  ---- 0110 bbbf ffff
	#  0000 0110 1000 0000	->	BTFSC INDF, #0x4
	#  0000 0110 1001 0010	->	BTFSC 0x12, #0x4
	local bitmask = 1 << bit;
	local tmp = srcREG & bitmask;
	if (tmp == 0) goto skipInst;
}

:BTFSC status, bit		is op4=0x6 & b3=0 & bit & status & skipInst				{
	#  ---- 0110 bbbf ffff
	#  0000 0110 0000 0000	->	BTFSC STATUS, #C
	if (C == 0) goto skipInst;
}

:BTFSC status, bit		is op4=0x6 & b3=1 & bit & status & skipInst				{
	#  ---- 0110 bbbf ffff
	#  0000 0110 0010 0000	->	BTFSC STATUS, #DC
	if (DC == 0) goto skipInst;
}

:BTFSC status, bit		is op4=0x6 & b3=2 & bit & status & skipInst				{
	#  ---- 0110 bbbf ffff
	#  0000 0110 0100 0000	->	BTFSC STATUS, #Z
	if (Z == 0) goto skipInst;
}

:BTFSS srcREG, bit		is op4=0x7 & bit & srcREG	& skipInst				{
	#  ---- 0111 bbbf ffff
	#  0000 0111 1000 0000	->	BTFSS INDF, #0x4
	#  0000 0111 1001 0010	->	BTFSS 0x12, #0x4
	local bitmask = 1 << bit;
	local tmp = srcREG & bitmask;
	if (tmp != 0) goto skipInst;
}

:BTFSS status, bit		is op4=0x7 & b3=0 & bit & status & skipInst				{
	#  ---- 0111 bbbf ffff
	#  0000 0111 0000 0000	->	BTFSS STATUS, #C
	if (C != 0) goto skipInst;
}

:BTFSS status, bit		is op4=0x7 & b3=1 & bit & status & skipInst				{
	#  ---- 0111 bbbf ffff
	#  0000 0111 0010 0000	->	BTFSS STATUS, #DC
	if (DC != 0) goto skipInst;
}

:BTFSS status, bit		is op4=0x7 & b3=2 & bit & status & skipInst				{
	#  ---- 0111 bbbf ffff
	#  0000 0111 0100 0000	->	BTFSS STATUS, #Z
	if (Z != 0) goto skipInst;
}

:CALL absAddr8			is op4=0x9 & absAddr8							{
	#  ---- 1001 kkkk kkkk
	#  0000 1001 0010 0011	->	CALL 0x23
	#  0000 1001 0001 0000	->	CALL 0x10
	push(&:2 inst_next);
	call [absAddr8];
}

:CLRF srcREG				is op6=0x01 & d=1 & srcREG						{
	#  ---- 0000 011f ffff
	#  0000 0000 0110 0000	->	CLRF INDF
	#  0000 0000 0111 0010	->	CLRF 0x12
	srcREG = 0;
	Z = 1;
}

:CLRW					is op6=0x01 & d=0 & f5=0						{
	#  ---- 0000 0100 0000
	#  0000 0001 0000 0000	->	CLRW
	W = 0;
	Z = 1;
}

:CLRWDT					is op12=0x0004				{
	#  ---- 0000 0000 0100
	# Clear Watchdog Timer - Not Implemented
	clearWatchDogTimer();
}

:COMF srcREG, D		is op6=0x09 & srcREG & D & destREG					{
	#  ---- 0010 01df ffff
	#  0000 0010 0100 0000	->	COMF INDF, 0 
	#  0000 0010 0110 0000	->	COMF INDF, 1
	#  0000 0010 0101 0010	->	COMF 0x12, 0
	#  0000 0010 0111 0010	->	COMF 0x12, 1
	tmp:1 = ~srcREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:DECF srcREG, D		is op6=0x03 & srcREG & D & destREG					{
	#  ---- 0000 11df ffff
	#  0000 0000 1100 0000	->	DECF INDF, 0 
	#  0000 0000 1110 0000	->	DECF INDF, 1
	#  0000 0000 1101 0010	->	DECF 0x12, 0
	#  0000 0000 1111 0010	->	DECF 0x12, 1
	tmp:1 = srcREG - 1;
	destREG = tmp;
	setResultFlags(tmp);
}

:DECFSZ srcREG, D		is op6=0x0b & srcREG & D & destREG & skipInst		{
	#  ---- 0010 11df ffff
	#  0000 0010 1100 0000	->	DECFSZ INDF, 0 
	#  0000 0010 1110 0000	->	DECFSZ INDF, 1
	#  0000 0010 1101 0010	->	DECFSZ 0x12, 0
	#  0000 0010 1111 0010	->	DECFSZ 0x12, 1
	tmp:1 = srcREG - 1;
	destREG = tmp;
	if (tmp == 0) goto skipInst;
}

:GOTO absAddr9			is op3=0x5 & absAddr9							{
	#  ---- 101k kkkk kkkk
	#  0000 1011 0010 0011	->	GOTO 0x123
	#  0000 1010 0001 0000	->	GOTO 0x10
	goto [absAddr9];
}

:INCF srcREG, D		is op6=0x0a & srcREG & D & destREG					{
	#  ---- 0010 10df ffff
	#  0000 0010 1000 0000	->	INCF INDF, 0 
	#  0000 0010 1010 0000	->	INCF INDF, 1
	#  0000 0010 1001 0010	->	INCF 0x12, 0
	#  0000 0010 1011 0010	->	INCF 0x12, 1
	tmp:1 = srcREG + 1;
	destREG = tmp;
	setResultFlags(tmp);
}

:INCFSZ srcREG, D		is op6=0x0f & srcREG & D & destREG & skipInst		{
	#  ---- 0011 11df ffff
	#  0000 0011 1100 0000	->	INCFSZ INDF, 0 
	#  0000 0011 1110 0000	->	INCFSZ INDF, 1
	#  0000 0011 1101 0010	->	INCFSZ 0x12, 0
	#  0000 0011 1111 0010	->	INCFSZ 0x12, 1
	tmp:1 = srcREG + 1;
	destREG = tmp;
	if (tmp == 0) goto skipInst;
}

:IORLW imm8				is op4=0xd & imm8								{
	#  ---- 1101 kkkk kkkk
	#  0000 1101 0001 0010	->	IORLW #0x12
	W = W | imm8;
	setResultFlags(W);
}

:IORWF srcREG, D		is op6=0x04 & srcREG & D & destREG					{
	#  ---- 0001 00df ffff
	#  0000 0001 0000 0000	->	IORWF INDF, 0 
	#  0000 0001 0010 0000	->	IORWF INDF, 1
	#  0000 0001 0001 0010	->	IORWF 0x20, 0
	#  0000 0001 0011 0010	->	IORWF 0x20, 1
	tmp:1 = W | srcREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:MOVLW imm8				is op4=0xc & imm8								{
	#  ---- 1100 kkkk kkkk
	#  0000 1100 0001 0010	->	MOVLW #0x12
	W = imm8;
}

:MOVF srcREG, D		is op6=0x08 & srcREG & D & destREG					{
	#  ---- 0010 00df ffff
	#  0000 0010 0000 0000	->	MOVF INDF, 0 
	#  0000 0010 0010 0000	->	MOVF INDF, 1
	#  0000 0010 0001 0010	->	MOVF 0x12, 0
	#  0000 0010 0011 0010	->	MOVF 0x12, 1
	tmp:1 = srcREG;
	destREG = tmp;
	setResultFlags(tmp);
}

:MOVWF srcREG				is op6=0x00 & d=1 & srcREG						{
	#  ---- 0000 001f ffff
	#  0000 0000 0010 0000	->	MOVWF INDF
	#  0000 0000 0011 0010	->	MOVWF 0x12
	srcREG = W;
}

:MOVWF pcl					is op6=0x00 & pcl			 				{
	#  ---- 0000 001f ffff
	#  0000 0000 0010 0010	->	MOVWF PCL
	PCL = W;
	addr:2 = (zext(PA) << 9) + zext(PCL);
	goto [addr];
}

:NOP					is op12=0x00						{
	#  ---- 0000 0000 0000
}

:OPTION					is op12=0x0002						{
	#  ---- 0000 0000 0010
	OPTION = W;
}

:RETLW imm8				is op4=0x8 & imm8								{
	#  ---- 1000 kkkk kkkk
	#  0000 1000 0001 0010	->	RETLW #0x12
	W = imm8;
	retAddr:2 = 0;
	pop(retAddr);
	return [retAddr];
}

:RLF srcREG, D		is op6=0x0d & srcREG & D & destREG					{
	#  ---- 0011 01df ffff
	#  0000 0011 0100 0000	->	RLF INDF, 0 
	#  0000 0011 0110 0000	->	RLF INDF, 1
	#  0000 0011 0101 0010	->	RLF 0x12, 0
	#  0000 0011 0111 0010	->	RLF 0x12, 1
	local tmpC = C;
	tmp:1 = srcREG;
	C = (tmp s< 0);
	tmp = (tmp << 1) | tmpC;
	destREG = tmp;
	setResultFlags(tmp);
}

:RRF srcREG, D		is op6=0x0c & srcREG & D & destREG					{
	#  ---- 0011 00df ffff
	#  0000 0011 0000 0000	->	RRF INDF, 0 
	#  0000 0011 0010 0000	->	RRF INDF, 1
	#  0000 0011 0001 0010	->	RRF 0x12, 0
	#  0000 0011 0011 0010	->	RRF 0x12, 1
	local tmpC = C << 7;
	tmp:1 = srcREG;
	C = (tmp & 1) != 0;
	tmp = (tmp >> 1) | tmpC;
	destREG = tmp;
	setResultFlags(tmp);
}

:SLEEP					is op12=0x0003				{ 
	#  ---- 0000 0000 0011
	# Sleep - Not Implemented
	sleep();
}

:SUBWF srcREG, D		is op6=0x02 & srcREG & D & destREG					{
	#  ---- 0000 10df ffff
	#  0000 0000 1000 0000	->	SUBWF INDF, 0 
	#  0000 0000 1010 0000	->	SUBWF INDF, 1
	#  0000 0000 1001 0010	->	SUBWF 0x12, 0
	#  0000 0000 1011 0010	->	SUBWF 0x12, 1
	tmp:1 = srcREG;
	setSubtractFlags(tmp, W); 
	tmp = tmp - W;
	destREG = tmp;
	setResultFlags(tmp);
}

:SWAPF srcREG, D		is op6=0x0e & srcREG & D & destREG					{
	#  ---- 0011 10df ffff
	#  0000 0011 1000 0000	->	SUBWF INDF, 0 
	#  0000 0011 1010 0000	->	SUBWF INDF, 1
	#  0000 0011 1001 0010	->	SUBWF 0x12, 0
	#  0000 0011 1011 0010	->	SUBWF 0x12, 1
	tmp:1 = srcREG;
	destREG = (tmp << 4) | (tmp >> 4);
}

:XORLW imm8				is op4=0xf & imm8								{
	#  ---- 1111 kkkk kkkk
	#  0000 1111 0001 0010	->	XORLW #0x12
	W = imm8 ^ W;
	setResultFlags(W);
}

:XORWF srcREG, D		is op6=0x06 & srcREG & D & destREG					{
	#  ---- 0001 10df ffff
	#  0000 0001 1000 0000	->	XORWF INDF, 0 
	#  0000 0001 1010 0000	->	XORWF INDF, 1
	#  0000 0001 1001 0010	->	XORWF 0x12, 0
	#  0000 0001 1011 0010	->	XORWF 0x12, 1
	tmp:1 = W ^ srcREG;
	destREG = tmp;
	setResultFlags(tmp);
}

