#include "stdafx.h"


extern "C" __declspec(dllexport) extern processor_t LPH;

#define INSTR_SZ_IN_TARGET_BYTES	1

//stack is empty, ascending


#define IDA_INSTR_IDX_ADD		1
#define IDA_INSTR_IDX_ADDC		2
#define IDA_INSTR_IDX_SUB		3
#define IDA_INSTR_IDX_SUBC		4
#define IDA_INSTR_IDX_INC		5
#define IDA_INSTR_IDX_DEC		6
#define IDA_INSTR_IDX_MUL		7
#define IDA_INSTR_IDX_DIV		8
#define IDA_INSTR_IDX_AND		9
#define IDA_INSTR_IDX_OR		10
#define IDA_INSTR_IDX_XOR		11
#define IDA_INSTR_IDX_ROL		12
#define IDA_INSTR_IDX_ROLC		13
#define IDA_INSTR_IDX_ROR		14
#define IDA_INSTR_IDX_RORC		15
#define IDA_INSTR_IDX_LD		16
#define IDA_INSTR_IDX_ST		17
#define IDA_INSTR_IDX_MOV		18
#define IDA_INSTR_IDX_LDC		19
#define IDA_INSTR_IDX_PUSH		20
#define IDA_INSTR_IDX_POP		21
#define IDA_INSTR_IDX_XCH		22
#define IDA_INSTR_IDX_JMP		23
#define IDA_INSTR_IDX_JMPF		24
#define IDA_INSTR_IDX_BR		25
#define IDA_INSTR_IDX_BRF		26
#define IDA_INSTR_IDX_BZ		27
#define IDA_INSTR_IDX_BNZ		28
#define IDA_INSTR_IDX_BP		29
#define IDA_INSTR_IDX_BPC		30
#define IDA_INSTR_IDX_BN		31
#define IDA_INSTR_IDX_DBNZ		32
#define IDA_INSTR_IDX_BE_2_OP	33
#define IDA_INSTR_IDX_BE_3_OP	34
#define IDA_INSTR_IDX_BNE_2_OP	35
#define IDA_INSTR_IDX_BNE_3_OP	36
#define IDA_INSTR_IDX_CALL		37
#define IDA_INSTR_IDX_CALLF		38
#define IDA_INSTR_IDX_CALLR		39
#define IDA_INSTR_IDX_RET		40
#define IDA_INSTR_IDX_RETI		41
#define IDA_INSTR_IDX_CLR1		42
#define IDA_INSTR_IDX_SET1		43
#define IDA_INSTR_IDX_NOT1		44
#define IDA_INSTR_IDX_NOP		45
#define IDA_INSTR_IDX_LDF		46
#define IDA_INSTR_IDX_STF		47
#define IDA_INSTR_IDX_CHANGE	48		//meta-instr (coded as "NOT1 [EXT], #0 ;  JMPF dest_addr")
#define IDA_INSTR_NUM			49		//always last


#define DATA_SEG_FAKE_BASE		0x10000000
#define DATA_SEG_SFR_BASE		0x100
#define DATA_SEG_SIZE			0x200
#define SFR_ADDR_EXT_REG		0x0D
#define SFR_EXT_REG_CHANGE_BIT	0



#define SEG_REG_CODE			0
#define SEG_REG_DATA			(DATA_SEG_FAKE_BASE >> 4)
#define SEG_REG_NUM				2

#define REG_NUM		4		//just R0..R3
static char *procRegNames[REG_NUM];



static void procStructsInit(void);


static void dbg( const char *fmt, ...)
{
	char buf[1024];
	va_list vl;

	va_start(vl, fmt);
	vsprintf_s(buf, sizeof(buf), fmt, vl);
	va_end(vl);
	OutputDebugString(buf);
}




static ea_t prevLoadedSegEa = BADADDR;

static const char *procShortNames[] = { "lc68k", NULL };
static const char *procLongNames[] = { "Sanyo LC68K", NULL };


static uint8 anaNextByte(insn_t *cmdP)
{
	return get_full_byte(cmdP->ea + cmdP->size++);
}

static void read_a12(insn_t *cmdP, uint8 instrSoFar)
{
	uint16 addr;
	
	addr = ((instrSoFar & 0x10) >> 1) | (instrSoFar & 0x07);
	addr <<= 8;
	addr += anaNextByte(cmdP);

	cmdP->Op1.type = o_near;
	cmdP->Op1.addr = ((cmdP->ea + cmdP->size) & 0xf000) + addr;
	cmdP->Op1.flags |= OF_SHOW;
	cmdP->Op1.dtyp = dt_code;
}

static void read_a16_or_r16(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp, bool relative)
{
	uint16 dstAddr = anaNextByte(cmdP);
	uint8 r16_lo = anaNextByte(cmdP);

	dstAddr <<= 8;
	dstAddr += r16_lo;

	if (relative) {	//do not ask, but r16 is MSB first, a16 is LSB first)
	
		dstAddr = (dstAddr >> 8) | (dstAddr << 8);

		dstAddr += cmdP->ea + cmdP->size - 1 /* yes minus 1 is as per spec */;
	}

	dstOp->type = o_near;
	dstOp->addr = dstAddr;
	dstOp->flags |= OF_SHOW;
	dstOp->dtyp = dt_code;
}


static void read_a16(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp)
{
	read_a16_or_r16(cmdP, instrSoFar, dstOp, false);
}

static void read_r16(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp)
{
	read_a16_or_r16(cmdP, instrSoFar, dstOp, true);
}


static void read_r8(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp)
{
	uint8 r8 = anaNextByte(cmdP);
	uint16 dstAddr;

	//r8 is signed
	dstAddr = (int16)(int8)r8;
	dstAddr += cmdP->ea + cmdP->size;

	dstOp->type = o_near;
	dstOp->addr = dstAddr;
	dstOp->flags |= OF_SHOW;
	dstOp->dtyp = dt_code;
}

static void read_d9_and_b3(insn_t *cmdP, uint8 instrSoFar, op_t* d9dstOp)	//this d9 is of the big-block type
{
	uint16 d9;
	uint8 b3;
	
	d9 = (((uint16)(instrSoFar & 0x10)) << 4) | anaNextByte(cmdP);
	b3 = instrSoFar & 0x07;

	d9dstOp->type = o_mem;
	d9dstOp->addr = DATA_SEG_FAKE_BASE + d9;
	d9dstOp->flags |= OF_SHOW;
	d9dstOp->dtyp = dt_byte;

	cmdP->Op2.type = o_imm;
	cmdP->Op2.value = b3;
	cmdP->Op2.flags |= OF_SHOW;
	cmdP->Op2.dtyp = dt_byte;
}

static void read_d9_b3_r8(insn_t *cmdP, uint8 instrSoFar)
{
	read_d9_and_b3(cmdP, instrSoFar, &cmdP->Op1);
	read_r8(cmdP, instrSoFar, &cmdP->Op3);
}

static void read_d9_b3(insn_t *cmdP, uint8 instrSoFar)
{
	read_d9_and_b3(cmdP, instrSoFar, &cmdP->Op1);
}

static void read_d9_using_lsb(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp)
{
	uint16 d9;
	
	d9 = (((uint16)(instrSoFar & 0x1)) << 8) | anaNextByte(cmdP);

	dstOp->type = o_mem;
	dstOp->addr = DATA_SEG_FAKE_BASE + d9;
	dstOp->flags |= OF_SHOW;
	dstOp->dtyp = dt_byte;
}

static void read_Rj(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp)
{
	dstOp->type = o_phrase;				//XXX: phrases are hard...how do we handle them???
	dstOp->phrase = instrSoFar & 0x03;
	dstOp->flags |= OF_SHOW;
	dstOp->dtyp = dt_byte;
}

static void read_i8(insn_t *cmdP, uint8 instrSoFar, op_t* dstOp)
{
	dstOp->type = o_imm;
	dstOp->value = anaNextByte(cmdP);
	dstOp->flags |= OF_SHOW;
	dstOp->dtyp = dt_byte;
}

static int instrAna(insn_t *cmdP)
{
	uint8 instr = anaNextByte(cmdP);

	if (instr & 0x08)  {			//large blocks
		switch(instr >> 5) {
			case 0:				//0x08..0x0F, 0x18..0x1F: CALL a12
				cmdP->itype = IDA_INSTR_IDX_CALL;
				read_a12(cmdP, instr);
				break;
			case 1:				//0x28..0x2F, 0x38..0x3F: JMP a12
				cmdP->itype = IDA_INSTR_IDX_JMP;
				read_a12(cmdP, instr);
				break;
			case 2:				//0x48..0x4F, 0x58..0x5F: BPC d9, b3, r8
				cmdP->itype = IDA_INSTR_IDX_BPC;
				read_d9_b3_r8(cmdP, instr);
				break;
			case 3:				//0x68..0x6F, 0x78..0x7F: BP d9, b3, r8
				cmdP->itype = IDA_INSTR_IDX_BP;
				read_d9_b3_r8(cmdP, instr);
				break;
			case 4:				//0x88..0x8F, 0x98..0x9F: BN d9, b3, r8
				cmdP->itype = IDA_INSTR_IDX_BN;
				read_d9_b3_r8(cmdP, instr);
				break;
			case 5:				//0xA8..0xAF, 0xB8..0xBF: NOT1 d9, b3
				cmdP->itype = IDA_INSTR_IDX_NOT1;
				read_d9_b3(cmdP, instr);


				//special case: CHANGE macro
				if (cmdP->Op1.addr == SFR_ADDR_EXT_REG + DATA_SEG_SFR_BASE + DATA_SEG_FAKE_BASE && cmdP->Op2.value == SFR_EXT_REG_CHANGE_BIT) {	//this could be a CHANGE meta-instr

					if (get_full_byte(cmdP->ea + cmdP->size) == 0x21) {		//JMPF

						cmdP->size++;	//account ofr the byte we used

						cmdP->itype = IDA_INSTR_IDX_CHANGE;
						read_a16(cmdP, instr, &cmdP->Op1);
						cmdP->Op1.value = cmdP->Op1.addr;
						cmdP->Op1.type = o_imm;			//it is an address, but not in our address space, so no point trying to trat it as such
						cmdP->Op1.dtyp = dt_word;

						cmdP->Op2.type = o_void;
					}
				}

				break;
			case 6:				//0xC8..0xCF, 0xD8..0xDF: CLR1 d9, b3
				cmdP->itype = IDA_INSTR_IDX_CLR1;
				read_d9_b3(cmdP, instr);
				break;
			case 7:				//0xE8..0xEF, 0xF8..0xFF: SET1 d9, b3
				cmdP->itype = IDA_INSTR_IDX_SET1;
				read_d9_b3(cmdP, instr);
				break;
		}
	}
	else if (instr & 0x06) {		//medium blocks
		static const uint8 mediumBlockInstrs[] = {
			IDA_INSTR_IDX_LD, IDA_INSTR_IDX_ST, IDA_INSTR_IDX_MOV, 0/* BE */,
			0 /* BNE */, IDA_INSTR_IDX_DBNZ, IDA_INSTR_IDX_INC, IDA_INSTR_IDX_DEC, 
			IDA_INSTR_IDX_ADD, IDA_INSTR_IDX_ADDC, IDA_INSTR_IDX_SUB, IDA_INSTR_IDX_SUBC, 
			IDA_INSTR_IDX_XCH, IDA_INSTR_IDX_OR, IDA_INSTR_IDX_AND, IDA_INSTR_IDX_XOR
		};

		cmdP->itype = mediumBlockInstrs[instr >> 4];
		
		//ops in medium-size blocks are a bit more complex
		if (instr == 0x22 || instr == 0x23) {		//MOV #i8,d9

			//order is important here
			read_d9_using_lsb(cmdP, instr, &cmdP->Op2);
			read_i8(cmdP, instr, &cmdP->Op1);
		}
		else if (instr >= 0x24 && instr <= 0x27) {	//MOV MOV #i8,@Ri

			read_Rj(cmdP, instr, &cmdP->Op2);
			read_i8(cmdP, instr, &cmdP->Op1);
		}
		else if (instr >= 0x30 && instr < 0x54) {	//BE d9,r8; BE @Ri,#i8,r8; BNE d9,r8; BNE @Ri,#i8,r8; DBNZ d9,r8;

			if (instr & 0x04) {		//	"@Ri,#i8,r8" mode

				cmdP->itype = (instr < 0x40) ? IDA_INSTR_IDX_BE_3_OP : IDA_INSTR_IDX_BNE_3_OP;
				
				read_Rj(cmdP, instr, &cmdP->Op1);
				read_i8(cmdP, instr, &cmdP->Op2);
				read_r8(cmdP, instr, &cmdP->Op3);
			}
			else {					//	"d9,r8" mode

				if (instr < 0x50)
					cmdP->itype = (instr < 0x40) ? IDA_INSTR_IDX_BE_2_OP : IDA_INSTR_IDX_BNE_2_OP;
				
				read_d9_using_lsb(cmdP, instr, &cmdP->Op1);
				read_r8(cmdP, instr, &cmdP->Op2);
			}
		}
		else if (instr >= 0x54 && instr <= 0x57) {	//DBNZ @Ri,r8

			read_Rj(cmdP, instr, &cmdP->Op1);
			read_r8(cmdP, instr, &cmdP->Op2);
		}
		else if (instr & 0x04){						//all other instrs, "@Ri" mode
			read_Rj(cmdP, instr, &cmdP->Op1);
		}
		else {										//all other instrs, "d9" mode
			read_d9_using_lsb(cmdP, instr, &cmdP->Op1);
		}
	}
	else if ((instr & 0x81) == 0x81){				//regular-ish things in small blocks  (#i8) instrs 0x81, 0x91, 0xA1 ... 0xF1
		static const uint8 smallBlockRegularInstrs1[] = {
			IDA_INSTR_IDX_ADD, IDA_INSTR_IDX_ADDC, IDA_INSTR_IDX_SUB, IDA_INSTR_IDX_SUBC, 
			IDA_INSTR_IDX_LDC, IDA_INSTR_IDX_OR, IDA_INSTR_IDX_AND, IDA_INSTR_IDX_XOR
		};

		cmdP->itype = smallBlockRegularInstrs1[(instr >> 4) - 8];
		
		if (instr != 0xC1)	//LDC has no op
			read_i8(cmdP, instr, &cmdP->Op1);
	}
	else if (instr >= 0xA0) {						//regular-ish things in small blocks (no-operand instrs) 0xA0, 0xB0, 0xC0 .. 0xF0
		static const uint8 smallBlockRegularInstrs2[] = {
			IDA_INSTR_IDX_RET, IDA_INSTR_IDX_RETI,
			IDA_INSTR_IDX_ROR, IDA_INSTR_IDX_RORC, IDA_INSTR_IDX_ROL, IDA_INSTR_IDX_ROLC
		};

		cmdP->itype = smallBlockRegularInstrs2[(instr >> 4) - 10];
	}
	else if (instr >= 0x80) {						//BZ r8 / BNZ r8	0x80, 0x90

		cmdP->itype = (instr == 0x80) ? IDA_INSTR_IDX_BZ : IDA_INSTR_IDX_BNZ;
		read_r8(cmdP, instr, &cmdP->Op1);
	}
	else if (instr >= 0x60) {						//PUSH d9 / POP d9    0x60, 0x61, 0x70, 0x71

		cmdP->itype = (instr >= 0x70) ? IDA_INSTR_IDX_POP : IDA_INSTR_IDX_PUSH;
		read_d9_using_lsb(cmdP, instr, &cmdP->Op1);
	}
	else if (instr == 0x00) {						//NOP 0x00

		cmdP->itype = IDA_INSTR_IDX_NOP;
	}
	else if (instr == 0x01) {						//BR r8 0x01

		cmdP->itype = IDA_INSTR_IDX_BR;
		read_r8(cmdP, instr, &cmdP->Op1);
	}
	else if (instr == 0x30) {						//MUL 0x30

		cmdP->itype = IDA_INSTR_IDX_MUL;
	}
	else if (instr == 0x40) {						//DIV 0x40

		cmdP->itype = IDA_INSTR_IDX_DIV;
	}
	else if (instr == 0x50) {						//LDF 0x50

		cmdP->itype = IDA_INSTR_IDX_LDF;
	}
	else if (instr == 0x51) {						//STF 0x51

		cmdP->itype = IDA_INSTR_IDX_STF;
	}
	else if (instr < 0x20) {						//CALLR r16, BRF r16

		cmdP->itype = (instr == 0x10) ? IDA_INSTR_IDX_CALLR : IDA_INSTR_IDX_BRF;
		read_r16(cmdP, instr, &cmdP->Op1);
	}
	else if (instr < 0x30) {						//CALLF a16, JMPF a16

		cmdP->itype = (instr == 0x20) ? IDA_INSTR_IDX_CALLF : IDA_INSTR_IDX_JMPF;
		read_a16(cmdP, instr, &cmdP->Op1);
	}
	else {											//BE/BNE  0x31, 0x41

		cmdP->itype = (instr == 0x31) ? IDA_INSTR_IDX_BE_2_OP : IDA_INSTR_IDX_BNE_2_OP;
		read_i8(cmdP, instr, &cmdP->Op1);
		read_r8(cmdP, instr, &cmdP->Op2);
	}

	cmdP->cs = SEG_REG_CODE;

	return cmdP->size;
}

static void setupDatabase(void)
{
	static bool done = false;

	char dummy[64];
	int i;
	
	if (done)
		return;

	done = true;

	static const struct {
		uint16 addr;		//all assumed at 0x100 + 
		const char *name;
		const char *repeatableComment;
		const char *comment;
	} sfrs[] = {

		//known
		{DATA_SEG_SFR_BASE + 0x00, "ACC",	0, 0},
		{DATA_SEG_SFR_BASE + 0x01, "PSW",	"[CY | AC | - | IRBK1 | IRBK0 | OV | RAMBK | P]", 0},
		{DATA_SEG_SFR_BASE + 0x02, "B",		0, 0},
		{DATA_SEG_SFR_BASE + 0x03, "C",		0, 0},
		{DATA_SEG_SFR_BASE + 0x04, "TRL",	0, 0},
		{DATA_SEG_SFR_BASE + 0x05, "TRH",	0, 0},
		{DATA_SEG_SFR_BASE + 0x06, "SP",	0, 0},
		{DATA_SEG_SFR_BASE + 0x07, "PCON",	0, "[ - | - | - | - | - | - | HOLD (sleep till external interrupt - stops all clocks) | HALT (sleep till any interrupt - stops CPU clock) ]"},
		{DATA_SEG_SFR_BASE + 0x08, "IE",	0, "[ IE7 | - | - | - | - | - | IE1 | IE0 ]\n IE7 low disables all ints\n IE0 high makes INT0 & INT1 low prio\n else IE1 high makes only INT1 low prio"},
		{DATA_SEG_SFR_BASE + 0x09, "IP",	0, "[ PORT3_prio | SIO1_prio | SIO0_prio | T1_prio | T0H_prio | INT3_prio | INT2/T0L_prio] - high = high prio, else low"},
		{DATA_SEG_SFR_BASE + SFR_ADDR_EXT_REG, "EXT",	0, "[ - | - | - | - | FLHI | ? | ? | FLSH ] if (FLSH) we see flash at offset 0, else if (FLHI), ROM, else flash + 64K\nif changed, next instr excutes as is, but after that ROM/flash visibility is swapped\nor perhals jmpf bankswitches"},
		{DATA_SEG_SFR_BASE + 0x0E, "OCR",	0, "[ OCR7 (div) | - | OCR5 (32khz) | OCR4 (6MHz) | - | - | OCR1 (stop 600khz osc) | OCR0 (stop 6MHz osc) ]"},
		{DATA_SEG_SFR_BASE + 0x10, "T0CNT",	0, "[ P0HRUN | P0LRUN | P0LONG | P0LEXT | P0HOVF | T0HIE | T0LOVF | T0LIE ]"},
		{DATA_SEG_SFR_BASE + 0x11, "T0PRR",	0, 0},
		{DATA_SEG_SFR_BASE + 0x12, "T0L",	0, 0},
		{DATA_SEG_SFR_BASE + 0x13, "T0LR",	0, 0},
		{DATA_SEG_SFR_BASE + 0x14, "T0H",	0, 0},
		{DATA_SEG_SFR_BASE + 0x15, "T0HR",	0, 0},
		{DATA_SEG_SFR_BASE + 0x18, "T1CNT",	0, "[ T1HRUN | T1LRUN | T1LONG | ELDT1C | T1HOVF | T1HIE | T1LOVF | T1LIE ]"},
		{DATA_SEG_SFR_BASE + 0x1A, "T1LC",	0, 0},
		{DATA_SEG_SFR_BASE + 0x1B, "T1L_R",	0, "reads as T1L, writes as T1LR"},
		{DATA_SEG_SFR_BASE + 0x1C, "T1HC",	0, 0},
		{DATA_SEG_SFR_BASE + 0x1D, "T1H_R",	0, "reads as T1H, writes as T1HR"},
		{DATA_SEG_SFR_BASE + 0x20, "MCR",	0, "[ SBZ | SBZ | SBZ | MCR4 (clr to halve refresh freq) | MCR3 (lcd on) | - | - | SBO]"},
		{DATA_SEG_SFR_BASE + 0x22, "STAD",  0, "LCD data start addr in XRAM"},
		{DATA_SEG_SFR_BASE + 0x23, "CNR",	0, "not to be used by apps"},
		{DATA_SEG_SFR_BASE + 0x24, "TDR",	0, "not to be used by apps"},
		{DATA_SEG_SFR_BASE + 0x25, "XBNK",	0, "[ - | - | - | - | - | - | XBNK1 | XBNK0 ] - set which bank app is using in XRAM. 0 & 1 is display data, 2 is icons"},
		{DATA_SEG_SFR_BASE + 0x27, "VCCR",	0, "[ VCCR7 (disp_on) | VCCR6 (cpu_ram_access_en) | SBO | SBO | SBO | SBO | SBO | SBO ]"},
		{DATA_SEG_SFR_BASE + 0x30, "SCON0",	0, "[ SCON07 (!CPOL) | SCON06 (overrun_flg) | - | SCON04 (continous_else_8bit) | SCON03 (start_xfer) | SCON02 (msb_first) | SCON01 (!xfer_in_prog) | SCON00 (irq_ena) ]"},
		{DATA_SEG_SFR_BASE + 0x31, "SBUF0",	0, 0},
		{DATA_SEG_SFR_BASE + 0x32, "SBR0",	0, "BRG for SIO0 & 1"},
		{DATA_SEG_SFR_BASE + 0x34, "SCON1",	0, "[ - | SCON16 (overrun_flg) | - | SCON14 (continous_else_8bit) | SCON13 (start_xfer) | SCON12 (msb_first) | SCON11 (!xfer_in_prog) | SCON10 (irq_ena) ]"},
		{DATA_SEG_SFR_BASE + 0x35, "SBUF1",	0, 0},
		{DATA_SEG_SFR_BASE + 0x44, "P1",	0, "[ PWM_out | TEST | SCK1 | SB1 | SO1 | SCK0 | SB0 | SO0 ]"},
		{DATA_SEG_SFR_BASE + 0x45, "P1DDR",	0, "write-only, 1 = output"},
		{DATA_SEG_SFR_BASE + 0x46, "P1FCR", 0, "write-only, 1 = function (see P1), else data"},
		{DATA_SEG_SFR_BASE + 0x4C, "P3",	0, "[ SLEEP | MODE | B | A | RIGHT | LEFT | DOWN | UP ]"},
		{DATA_SEG_SFR_BASE + 0x4D, "P3DDR",	0, "not to be used by apps"},
		{DATA_SEG_SFR_BASE + 0x4E, "P3INT",	0, "[ - | - | - | - | - | P32INT (interrupt ena) | P31INT (interrupt source ena)| P30INT (interrupt req ena) ]"},
		{DATA_SEG_SFR_BASE + 0x5C, "P7",	0, "[ - | - | - | - | ID1 | ID0 | Low_volt | 5V_det ]"},
		{DATA_SEG_SFR_BASE + 0x5D, "I01CR",	0, "[ INT1_det_typ1 | INT1_det_typ0 | INT1_int_src | INT1_int_ena | INT0_det_typ1 | INT0_det_typ0 | INT0_int_src | INT0_int_ena ] - det typs (in order): {fall, low, rise, h }"},
		{DATA_SEG_SFR_BASE + 0x5E, "I23CR", 0, "[ INT3_rise_det | INT3_fall_det | INT3_int_src | INT3_int_ena | INT2_rise_det | INT2_fall_det | INT2_int_src | INT2_int_ena ]"},
		{DATA_SEG_SFR_BASE + 0x5F, "ISL",	0, "[ - | - | ISL5 | ISL4 | ISL3 (SBZ) | ISL2 | ISL1 | ISL0 (T0 clk input pin sel) ]"},
		{DATA_SEG_SFR_BASE + 0x60, "MAPLETXRXCTL", 0, "UNDOC [ - | - | - | LASTPKT (Set if packet is last) | - | HW_ENA (is hw on?) | TX (TX normal packet) | TXS (tx starting packet) ]"},
		{DATA_SEG_SFR_BASE + 0x61, "MAPLESTA", 0, "UNDOC [ - | UNK (if set causes bus to be reinited) | ERR3 | ERR2 | - | IRQREQ (cleared by handler) | ERR1 | TXDONE (Set when tx done) ] (errs set if RXed packet was bad)"},
		{DATA_SEG_SFR_BASE + 0x62, "MAPLERST", 0, "UNDOC [ RST (set and clear to reset bus hw) | - | - | - | - | - | - | - ]"},
		{DATA_SEG_SFR_BASE + 0x63, "VSEL",	0, "[ - | - | - | INCE (autoinc addr) | - | - | SIOSEL (1 if maple bus on P1, else SIO) | ASEL (set if maple xfer is using WRAM) ]"},
		{DATA_SEG_SFR_BASE + 0x64, "VRMAD1","VRMAD_lo", 0},
		{DATA_SEG_SFR_BASE + 0x65, "VRMAD2","VRMAD_hi", 0},
		{DATA_SEG_SFR_BASE + 0x66, "VRTBF",	0, "WRAM access"},
		{DATA_SEG_SFR_BASE + 0x67, "MAPLELEN", 0, "UNDOC number of 32-bit words to send as data on MAPLE bus (header word is not data and doesn't count)"},
		{DATA_SEG_SFR_BASE + 0x7F, "BTCR",	0, 0},


		//undoc (figured out by me - could be wrong)
		{DATA_SEG_SFR_BASE + 0x54, "FLSHCTRL", 0, "UNDOC [ - | - | - | - | - | - | UNOCKSEQ (set to 1 during unlock sequence) | BANKNUM (bank number for LDF instr) ]"},
		
		//unknown
		{DATA_SEG_SFR_BASE + 0x48, "SFR_x48", 0, "unknown, rom writes this once with 0x00 only, related to SFR_x51, SFR_x55"},
		{DATA_SEG_SFR_BASE + 0x55, "SFR_x55", 0, "unknown, rom writes this once with 0xFF only, related to SFR_x48, SFR_x51"},
		{DATA_SEG_SFR_BASE + 0x51, "SFR_x51", 0, "unknown, rom sets bit 5 only, related to SFR_x48, SFR_x55"},


		//Rj regs
		{0x000, "R0_IRBK_0b00", 0, "always accesses RAM. USed when IRBK0..1 == 0b00"},
		{0x001, "R1_IRBK_0b00", 0, "always accesses RAM. USed when IRBK0..1 == 0b00"},
		{0x002, "R2_IRBK_0b00", 0, "always accesses SFRs. USed when IRBK0..1 == 0b00"},
		{0x003, "R3_IRBK_0b00", 0, "always accesses SFRs. USed when IRBK0..1 == 0b00"},

		{0x004, "R0_IRBK_0b01", 0, "always accesses RAM. USed when IRBK0..1 == 0b01"},
		{0x005, "R1_IRBK_0b01", 0, "always accesses RAM. USed when IRBK0..1 == 0b01"},
		{0x006, "R2_IRBK_0b01", 0, "always accesses SFRs. USed when IRBK0..1 == 0b01"},
		{0x007, "R3_IRBK_0b01", 0, "always accesses SFRs. USed when IRBK0..1 == 0b01"},

		{0x008, "R0_IRBK_0b10", 0, "always accesses RAM. USed when IRBK0..1 == 0b10"},
		{0x009, "R1_IRBK_0b10", 0, "always accesses RAM. USed when IRBK0..1 == 0b10"},
		{0x00a, "R2_IRBK_0b10", 0, "always accesses SFRs. USed when IRBK0..1 == 0b10"},
		{0x00b, "R3_IRBK_0b10", 0, "always accesses SFRs. USed when IRBK0..1 == 0b10"},

		{0x00c, "R0_IRBK_0b11", 0, "always accesses RAM. USed when IRBK0..1 == 0b11"},
		{0x00d, "R1_IRBK_0b11", 0, "always accesses RAM. USed when IRBK0..1 == 0b11"},
		{0x00e, "R2_IRBK_0b11", 0, "always accesses SFRs. USed when IRBK0..1 == 0b11"},
		{0x00f, "R3_IRBK_0b11", 0, "always accesses SFRs. USed when IRBK0..1 == 0b11"},


	};

	if (getseg(DATA_SEG_FAKE_BASE) || add_segm(SEG_REG_DATA/*not even remotely right*/, DATA_SEG_FAKE_BASE, DATA_SEG_FAKE_BASE + DATA_SEG_SIZE, "DATA", "DATA")) {
		//allocate SFRs
		for (i = 0; i < sizeof(sfrs) / sizeof(*sfrs); i++) {
			uint32 addr = DATA_SEG_FAKE_BASE + sfrs[i].addr;

			if (!get_name(BADADDR, addr, dummy, sizeof(dummy))) {

				if (set_name(addr, sfrs[i].name, 0)) {

					doByte(addr, 1);

					if (sfrs[i].comment)
						set_cmt(addr, sfrs[i].comment, false);
					if (sfrs[i].repeatableComment)
						set_cmt(addr, sfrs[i].repeatableComment, true);
				}
			}
		}
	}

	static const struct {
		uint16 addr;
		const char *vecName;
	} vecs[] = {
		{0x0000, "_VEC_RESET"},
		{0x0003, "_VEC_INT0"},
		{0x000B, "_VEC_INT1"},
		{0x0013, "_VEC_INT2_or_T0L_OVF"},
		{0x001B, "_VEC_INT3_or_base_timer"},
		{0x0023, "_VEC_T0H_OVF"},
		{0x002B, "_VEC_T1H_OR_T1L_OVF"},
		{0x0033, "_VEC_SIO0"},
		{0x003B, "_VEC_SIO1"},
		{0x0043, "_VEC_RBF"},
		{0x004B, "_VEC_P3"},
	};

	for (i = 0; i < sizeof(vecs) / sizeof(*vecs); i++) {
		
		if (!get_name(BADADDR, vecs[i].addr, dummy, sizeof(dummy))) {

			set_name(vecs[i].addr, vecs[i].vecName, SN_AUTO);
			auto_make_proc(vecs[i].addr);
		}
	}

	static const struct {
		uint16 addr;
		const char *syscallName;
		const char *comment;
	} syscalls [] = {

		{0x100, "fm_wrt_ex",	"flash memory write external program"},
		{0x110, "fm_vrf_ex",	"flash memory verify external program"},
		{0x120, "fm_prd_ex",	"flash memory page read external program"},
		{0x130, "timer_ex",		"timer call external program"},
		{0x140, "vmu_sleep",	"slep till user pushes SLEEP buton"},
		{0x1f0, "game_end",		"return to ROM"},

		//latest entry to not break bios disassembly
		{0x106, "fm_wrt_ex_2",	"flash memory write external program (same thing as fm_wrt_ex)"},
	};

	for (i = 0; i < sizeof(syscalls) / sizeof(*syscalls); i++) {

		if (!get_name(BADADDR, syscalls[i].addr, dummy, sizeof(dummy)) && get_full_byte(syscalls[i].addr) != 0x00 && get_full_byte(syscalls[i].addr) != 0xFF ) {

			set_name(syscalls[i].addr, syscalls[i].syscallName, SN_AUTO);
			auto_make_proc(syscalls[i].addr);
			set_cmt(syscalls[i].addr, syscalls[i].comment, true);
		}
	}
}

static int idaapi idaAnaCbk(void)
{
	setupDatabase();

    return instrAna(&cmd);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		//one time init
		procStructsInit();
		break;

	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
    return TRUE;
}

static int idaapi idaNotifyCbk(processor_t::idp_notify msgid, ...)
{
	va_list vl;
	int ret;

#define RETRIEVE_PARAM(nm, typ)	typ nm = va_arg(vl, typ)

	va_start(vl, msgid);
	ret = invoke_callbacks(HT_IDP, msgid, vl);
	if (!ret) switch (msgid) {
		case processor_t::idp_notify::newprc: {
			ret = va_arg(vl, int) == 0;	//only index 0 exists so only that onr will be supported
			break;
											  }

		case processor_t::idp_notify::loader_elf_machine: {
			break;
														  }

		case processor_t::idp_notify::auto_queue_empty:{

			setupDatabase();

			ret = 1;
			break;
													   }

		case processor_t::idp_notify::get_reg_info:{
			RETRIEVE_PARAM(regname, const char*);
			RETRIEVE_PARAM(main_regname, const char**);
			RETRIEVE_PARAM(mask, uint64*);

			*main_regname = NULL;
			//XX: maybe?
			ret = 0;
			break;
												   }

		case processor_t::idp_notify::is_jump_func: {

			RETRIEVE_PARAM(pfn, func_t*);
			RETRIEVE_PARAM(jump_target, ea_t*);
			RETRIEVE_PARAM(func_pointer, ea_t*);
             
			//TODO: this, for now return "i don't know" for all instrs
			ret = 1;

			break;
													}

		case processor_t::idp_notify::is_ret_insn: {

			RETRIEVE_PARAM(ea, ea_t);
			RETRIEVE_PARAM(strict, bool);
			insn_t myCmd = {0};

			myCmd.ea = ea;
			if (!instrAna(&myCmd))
				ret = 1;	//if we cnanot disasm, it is unknown
			else
				ret = (myCmd.itype == IDA_INSTR_IDX_RET || myCmd.itype == IDA_INSTR_IDX_RETI) ? 2 : 0;
			break;
													}

		case processor_t::idp_notify::is_call_insn: {

			RETRIEVE_PARAM(ea, ea_t);
            insn_t myCmd = {0};

			myCmd.ea = ea;
			if (!instrAna(&myCmd))
				ret = 1;	//if we cnanot disasm, it is unknown
			else
				ret = (myCmd.itype == IDA_INSTR_IDX_CALLR || myCmd.itype == IDA_INSTR_IDX_CALLF || myCmd.itype == IDA_INSTR_IDX_CALL) ? 2 : 0;

			break;
													}

		case processor_t::idp_notify::coagulate_dref: {

			RETRIEVE_PARAM(from, ea_t);
			RETRIEVE_PARAM(to, ea_t);
			RETRIEVE_PARAM(may_define, bool);
			RETRIEVE_PARAM(code_ea, ea_t*);
			
			//TODO
			
			ret = 1;	//why not? :)
			break;
													}

		case processor_t::idp_notify::is_sane_insn: {

			RETRIEVE_PARAM(no_crefs, int);

			ret = no_crefs ? 0 : 1;	//if it was randomly created, say no, else say yes (we could also inspect "cmd" if we wanted too)
			break;
													}

		case processor_t::idp_notify::newseg:	{		//allow all seg

			RETRIEVE_PARAM(seg, segment_t*);

			dbg("NewSeg: %08Xh-%08Xh (len=%08Xh),  perm=%Xh flags=%08Xh type=%Xh sel=%Xh\n", seg->startEA, seg->endEA, seg->endEA - seg->startEA, seg->perm, seg->flags, seg->type, seg->sel);
			
			ret = 1;	//allow
			break;
												}
		case processor_t::idp_notify::rename:	{	//allowed

			RETRIEVE_PARAM(ea, ea_t);
			RETRIEVE_PARAM(new_name, const char *);

			ret = (new_name[0] == '.') ? 0 : 1;	//do not allow names beginning with dot
			break;
												}
												
		case processor_t::idp_notify::str2reg:		{
			RETRIEVE_PARAM(str, const char *);
			int i;

			ret = 1;	//this is the undcumented correct thing to return to specify that a given strnig is not a register name
			for (i = 0; i < REG_NUM; i++) {
				if (!stricmp(procRegNames[i], str)) {
					ret = i + 2;
					break;
				}
			}

			break;
													}
		case processor_t::idp_notify::init:			{
			inf.mf = true;
			ret = 1;
			break;
													}

		case processor_t::idp_notify::may_be_func:	{

			switch(cmd.itype) {
				case IDA_INSTR_IDX_RET:
				case IDA_INSTR_IDX_RETI:
					ret = 1;	//"return" is very unlikely to start a func
					break;
				default:
					ret = 10;	//anything else is moderately likely to start a func
			}
			break;
													}
		case processor_t::idp_notify::setsgr:		{
			RETRIEVE_PARAM(startEA, ea_t);
			RETRIEVE_PARAM(endEA, ea_t);
			RETRIEVE_PARAM(regnum, int);
			RETRIEVE_PARAM(value, sel_t);
			RETRIEVE_PARAM(old_value, sel_t);
			RETRIEVE_PARAM(tag, uchar);
			
			ret = 1;
			break;
													}
			//all of the following are ignored on porpose with a return of 1
		case processor_t::idp_notify::make_data:		//allowed
		case processor_t::idp_notify::make_code:		//allowed
		case processor_t::idp_notify::get_bg_color:		//unimplemented
		case processor_t::idp_notify::undefine:			//allowed
		case processor_t::idp_notify::set_func_start:	//allowed
		case processor_t::idp_notify::set_func_end:		//allowed
		case processor_t::idp_notify::outlabel:				//not sure if this is really needed
			ret = 1;
			break;

		//all of the following cases are ignored on purpose with a return of 0
		case processor_t::idp_notify::newasm:
		case processor_t::idp_notify::kernel_config_loaded:
		case processor_t::idp_notify::renamed:
		case processor_t::idp_notify::loader_finished: 
		case processor_t::idp_notify::savebase: 
		case processor_t::idp_notify::newfile: 
		case processor_t::idp_notify::closebase: 
		case processor_t::idp_notify::term: 
		case processor_t::idp_notify::func_bounds:			//we may want this later
		case processor_t::idp_notify::add_func:				//we may want this later
		case processor_t::idp_notify::coagulate:			//we may want this later
		case processor_t::idp_notify::is_insn_table_jump:	//we may want this later
		case processor_t::idp_notify::custom_mnem:			//we may want this later
		case processor_t::idp_notify::custom_outop:			//we may want this later
		case processor_t::idp_notify::custom_out:			//we may want this later
		case processor_t::idp_notify::custom_emu:			//not needed
		case processor_t::idp_notify::custom_ana:			//not needed
		case processor_t::idp_notify::add_cref:				//allowed
		case processor_t::idp_notify::add_dref:				//allowed
		case processor_t::idp_notify::del_cref:				//allowed
		case processor_t::idp_notify::del_dref:				//allowed
		case processor_t::idp_notify::auto_empty:			//we do not care
		case processor_t::idp_notify::auto_empty_finally:	//we do not care
		case processor_t::idp_notify::is_basic_block_end:	//no
			ret = 0;
			break;

		default:
			ret = 1;
			break;
	}

#undef RETRIEVE_PARAM

	va_end(vl);
	return ret;
}

static void idaapi idaHeaderCbk(void)
{
	//no
}
static void idaapi idaFooterCbk(void)
{
	//no
}

static void idaapi idaSegstartCbk(ea_t ea)
{
	///
}

static void idaapi idaSegendCbk(ea_t ea)
{
	///
}

static void idaapi idaAssumesCbk(ea_t ea)
{
	///
}

static void idaEmuCbkOp(op_t *op, bool forced, bool mayBeWritten, int opNr, uint32 ftr)
{
	if (op->type == o_near)
		ua_add_cref(0, op->addr, (ftr & CF_JUMP) ? fl_JN : fl_CN);
	else if (op->type == o_mem)
		ua_add_dref(0, op->addr, mayBeWritten ? dr_W : dr_R);
}

static int idaapi idaEmuCbk(void)
{
	uint32 ftr = cmd.get_canon_feature();

	if (ftr & (CF_USE1 | CF_CHG1))
		idaEmuCbkOp(&cmd.Op1, is_forced_operand(cmd.ea, 0), !!(ftr & CF_CHG1), 1, ftr);
	if (ftr & (CF_USE2 | CF_CHG2))
		idaEmuCbkOp(&cmd.Op2, is_forced_operand(cmd.ea, 1), !!(ftr & CF_CHG2), 2, ftr);
	if (ftr & (CF_USE3 | CF_CHG3))
		idaEmuCbkOp(&cmd.Op3, is_forced_operand(cmd.ea, 2), !!(ftr & CF_CHG3), 3, ftr);

	if(!(ftr & CF_STOP))  //unless this is an unconditional jump, flow through to get IDA to analize further
		ua_add_cref(0, cmd.ea + cmd.size, fl_F);

	if (cmd.itype == IDA_INSTR_IDX_DIV)
		set_cmt(cmd.ea, "(ACC)(C) / B -> (ACC)(C), remainder -> (B)", false);

	if (cmd.itype == IDA_INSTR_IDX_MUL)
		set_cmt(cmd.ea, "(ACC)(C) * B -> (B)(ACC)(C)", false);

	return 1;
}

static void idaapi idaOutCbk(void)
{
	char buf[MAXSTR];

	init_output_buffer(buf, sizeof(buf));

	OutMnem();

	if (cmd.Op1.type != o_void) {
		out_one_operand(0);
	}
	if (cmd.Op2.type != o_void) {
		out_symbol(',');
		OutChar(' ');
		out_one_operand(1);
	}
	if (cmd.Op3.type != o_void) {
		out_symbol(',');
		OutChar(' ');
		out_one_operand(2);
	}

	//terminate line
	term_output_buffer();

	// show user's comments
	gl_comm = 1;

	MakeLine(buf);
}

static bool idaapi idaOutOpCbk(op_t &op)
{
	char* target_name;
	char nm[MAX_NUMBUF];
	ea_t ea;

	switch (op.type) {
		case o_void:
			return false;

		case o_mem:
			
			OutChar('[');
			if (0 < get_name_expr(cmd.ea, &op - cmd.Operands, op.addr, BADADDR, nm, sizeof(nm), 0)) {
				OutLine(nm);
			}
			else {
				out_addr_tag(cmd.ea);
				OutValue(op, OOF_ADDR | OOFS_NOSIGN | OOFW_32);
			}
			OutChar(']');
			return true;

		case o_phrase:	//we use this for "@Rj" mode
			OutChar('@');
			out_register(procRegNames[op.phrase]);
			return true;

		case o_near:
			ea = toEA(cmd.cs, op.addr);
			target_name = get_colored_name(cmd.ea, ea, nm, sizeof(nm));

			if (target_name)
				OutLine(target_name);
			else if (!out_name_expr(op, ea)) {
				out_tagon(COLOR_ERROR);
				OutLong(ea, 16);
				out_tagoff(COLOR_ERROR);
			}
			return true;

		case o_imm:
			OutChar('#');
			OutValue(op, OOF_NUMBER);
			return true;

		default:
			OutLine("UNKNOWN_OP_");
			OutLong(op.type, 10);
			return true;
	}
}

static void idaapi idaOutDataCbk(ea_t ea)
{
	intel_data(ea);
}

static bool idaapi idaCompareOperandCbk(const op_t &op1, const op_t &op2)
{
	///
	return false;
}

static bool idaapi idaCanOperandHaveType(op_t &op)
{
	return (op.type == o_mem || op.type == o_phrase || op.type == o_displ);	//XXX: this
}
static int idaapi idaIsFarJumpCbk(int icode)
{
	//this is likely ok for now
	return 0;
}

static ea_t idaapi idaTranslateCbk(ea_t base, adiff_t offset)
{
	///
	return 0;
}

static int idaapi idaRealCvtCbk(void *m, uint16 *e, uint16 swt)
{
	///
	return 0;
}

static bool idaapi idaIsSwitchCbk(switch_info_ex_t *si)
{
	// TODO: maybe this?
	return false;
}

static ea_t idaapi idaExtractAddressCbk(ea_t ea, const char *string, int x)
{
	return BADADDR - 1;	//for now use standard algo
}

static int idaapi idaIsSpBasedCbk(const op_t &x)
{
	//no sp -> no sp-based things

	return 0;
}

static bool idaapi idaCreateFuncFrameCbk(func_t *pfn)
{
	insn_t myCmd = {0,};

	myCmd.ea = pfn->startEA;
	if (!instrAna(&myCmd))
		return false;	//if we cnanot disasm, it is unknown

	return true;
}

static int idaGetFrameRetSizeCkb(func_t *pfn)
{
	return 2;	//always it seems
}

static bool idaapi idaOutSpecCbk(ea_t ea,uchar segtype)
{
	///
	return false;
}

static const char* idaapi idaSetIdpOptionsCbk(const char *keyword, int value_type, const void *value)
{
	///
	return NULL;
}

static int idaapi idaIsInsnForAlignmentOnlyCbk(ea_t ea)
{
	//never for alignment only - it messes with the disassembly

	return 0;
}

static const char *myHeaderLines[] = { "This is a header for this fake LC86K assembler that IDA requires", "Enjoy...", NULL};
static const uint16 myAsmInvalidInsns[] = {0};

static asm_t myAssembler = {
	/* .flag =            */ AS_OFFST | AS_COLON | AS_UDATA | AS_1TEXT | AS_NHIAS | ASH_HEXF3 | ASD_DECF0 | ASO_OCTF1 | ASB_BINF3 | AS_ONEDUP,
	/* .uflag =           */ 0,
	/* .name =            */ "LC86K fake assembler",
	/* .help =            */ 0, //no help exists
	/* .header =          */ myHeaderLines,
	/* .badworks =        */ myAsmInvalidInsns,
	/* .origin =          */ ".org",
	/* .end =             */ ".end",
	/* .cmnt =            */ ";",
	/* .ascsep =          */ '"',
	/* .accsep =          */ '\'',
	/* .esccodes =        */ "'\"\\",
	/* .a_ascii =         */ ".ascii",
	/* .a_byte =          */ ".u8",
	/* .a_word =          */ ".u16",
	/* .a_dword =         */ ".u32",
	/* .a_qword =         */ ".u64",
	/* .a_oword =         */ NULL,
	/* .a_float =         */ NULL,
	/* .a_double =        */ NULL,
	/* .a_tbyte =         */ NULL,
	/* .a_packreal =      */ NULL,
	/* .a_dups =          */ ".arr  h = #h  d = #d  v = #v  s = #s",	//todo: follow up on this
	/* .a_bss =           */ ".bss x %s",
	/* .a_equ =           */ "EQU not used",	//in theory nobody should read this ever since AS_UNEQU is not set in flags
	/*. a_seg =           */ "seg", //who'll ever use this?
	/* .checkarg_dispatch =              */ NULL,
	/* ._UNUSED1_was_atomprefix =        */ NULL,
	/* ._UNUSED2_was_checkarg_operations */ NULL,
	/* .XlatAsciiOutput = */ NULL,
	/* .a_curip =         */ "$", //is this right?
	/* .func_header =     */ NULL, //TODO: revisit this maybe
	/* .func_footer =     */ NULL, //TODO: revisit this maybe
	/* .a_public =        */ "public",
	/* .a_weak =          */ "weak",
	/* .a_extrn =         */ "extern",
	/* .a_comdef =        */ "comunal",
	/* .get_type_name =   */ NULL,
	/* .a_align =         */ "align",
	/* .lbrace =          */ '(',
	/* .rbrace =          */ ')',
	/* .a_mod =           */ "%",
	/* .a_band =          */ "&",
	/* .a_bor =           */ "|",
	/* .a_xor =           */ "^",
	/* .a_bnot =          */ "~",
	/* .a_shl =           */ "<<",
	/* .a_shr =           */ ">>",
	/* .a_sizeof_fmt =    */ "sizeof(%s)",
	/* .flag2 =           */ 0, //TODO: AS2_STRINV maybe?
	/* .cmnt2 =           */ 0,
	/* .low8 =            */ "lo8(%s)",
	/* .high8 =           */ "hi8(%s)",
	/* .low16 =           */ "lo16(%s)",
	/* .high16 =          */ "hi16(%s)",
	/* .a_include_fmt =   */ "#include \"%s\"",
	/* .a_vstruc_fmt =    */ "TODO: vstruc-",	//todo: maybe empty str
	/* .a_3byte =         */ "TODO:3byte-", //todo: maybe empty str
	/* .a_rva =           */ "RVA",
};

static const bytes_t procFuncStartSequences[] = {{0, NULL}};
static const bytes_t procRetCodesSequences[] = {{0, NULL}};


static asm_t *procAsms[] = {&myAssembler, NULL}; // this may be changed at runtime when processor is changed. we do not do that
static instruc_t procInstructions[IDA_INSTR_NUM];

static void procStructsInit(void)
{

#define PROC_INSTR_INIT_NAMED(nm, flg, unm)						\
	do {														\
		procInstructions[IDA_INSTR_IDX_ ## nm].name = unm;		\
		procInstructions[IDA_INSTR_IDX_ ## nm].feature = flg;	\
	} while(0)

#define PROC_INSTR_INIT(nm, flg)								\
	do {														\
		procInstructions[IDA_INSTR_IDX_ ## nm].name = #nm;		\
		procInstructions[IDA_INSTR_IDX_ ## nm].feature = flg;	\
	} while(0)

	PROC_INSTR_INIT(ADD, CF_USE1);
	PROC_INSTR_INIT(ADDC, CF_USE1);
	PROC_INSTR_INIT(SUB, CF_USE1);
	PROC_INSTR_INIT(SUBC, CF_USE1);
	PROC_INSTR_INIT(INC, CF_USE1 | CF_CHG1);
	PROC_INSTR_INIT(DEC, CF_USE1 | CF_CHG1);
	PROC_INSTR_INIT(MUL, 0);
	PROC_INSTR_INIT(DIV, 0);
	PROC_INSTR_INIT(AND, CF_USE1);
	PROC_INSTR_INIT(OR, CF_USE1);
	PROC_INSTR_INIT(XOR, CF_USE1);
	PROC_INSTR_INIT(ROL, CF_SHFT);
	PROC_INSTR_INIT(ROLC, CF_SHFT);
	PROC_INSTR_INIT(ROR, CF_SHFT);
	PROC_INSTR_INIT(RORC, CF_SHFT);
	PROC_INSTR_INIT(LD, CF_USE1);
	PROC_INSTR_INIT(ST, CF_USE1 | CF_CHG1);
	PROC_INSTR_INIT(MOV, CF_USE1 | CF_USE2 | CF_CHG2);
	PROC_INSTR_INIT(LDC, 0);
	PROC_INSTR_INIT(PUSH, CF_USE1);
	PROC_INSTR_INIT(POP, CF_USE1 | CF_CHG1);
	PROC_INSTR_INIT(XCH, CF_USE1 | CF_CHG1);
	PROC_INSTR_INIT(JMP, CF_USE1 | CF_JUMP | CF_STOP);
	PROC_INSTR_INIT(JMPF, CF_USE1 | CF_JUMP | CF_STOP);
	PROC_INSTR_INIT(BR, CF_USE1 | CF_JUMP | CF_STOP);
	PROC_INSTR_INIT(BRF, CF_USE1 | CF_JUMP | CF_STOP);
	PROC_INSTR_INIT(BZ, CF_USE1 | CF_JUMP);
	PROC_INSTR_INIT(BNZ, CF_USE1 | CF_JUMP);
	PROC_INSTR_INIT(BP, CF_USE1 | CF_USE2 | CF_USE3 | CF_JUMP);
	PROC_INSTR_INIT(BPC, CF_USE1 | CF_USE2 | CF_USE3 | CF_JUMP);
	PROC_INSTR_INIT(BN,  CF_USE1 | CF_USE2 | CF_USE3 | CF_JUMP);
	PROC_INSTR_INIT(DBNZ, CF_USE1 | CF_USE2 | CF_JUMP);
	PROC_INSTR_INIT_NAMED(BE_2_OP, CF_USE1 | CF_USE2 | CF_JUMP, "BE");
	PROC_INSTR_INIT_NAMED(BE_3_OP, CF_USE1 | CF_USE2 | CF_USE3| CF_JUMP, "BE");
	PROC_INSTR_INIT_NAMED(BNE_2_OP, CF_USE1 | CF_USE2 | CF_JUMP, "BNE");
	PROC_INSTR_INIT_NAMED(BNE_3_OP, CF_USE1 | CF_USE2 | CF_USE3| CF_JUMP, "BNE");
	PROC_INSTR_INIT(CALL, CF_USE1 | CF_CALL);
	PROC_INSTR_INIT(CALLF, CF_USE1 | CF_CALL);
	PROC_INSTR_INIT(CALLR, CF_USE1 | CF_CALL);
	PROC_INSTR_INIT(RET, CF_STOP);
	PROC_INSTR_INIT(RETI, CF_STOP);
	PROC_INSTR_INIT(CLR1, CF_USE1 | CF_CHG1 | CF_USE2);
	PROC_INSTR_INIT(SET1, CF_USE1 | CF_CHG1 | CF_USE2);
	PROC_INSTR_INIT(NOT1, CF_USE1 | CF_CHG1 | CF_USE2);
	PROC_INSTR_INIT(NOP, 0);
	PROC_INSTR_INIT(LDF, 0);
	PROC_INSTR_INIT(STF, 0);
	PROC_INSTR_INIT(CHANGE, CF_USE1 | CF_JUMP | CF_STOP);

#undef PROC_INSTR_INIT
#undef PROC_INSTR_INIT_NAMED
	
	procRegNames[0] = "R0";
	procRegNames[1] = "R1";
	procRegNames[2] = "R2";
	procRegNames[3] = "R3";
}

extern "C" __declspec(dllexport) processor_t LPH = {
	/* .version =         */ IDP_INTERFACE_VERSION,
	/* .id =              */ 0x8187, 
	/* .flag =            */ PR_SEGS | PR_NO_SEGMOVE |  PR_USE32 | PR_DEFSEG32 | PRN_HEX | PR_SGROTHER | PR_STACK_UP | PR_NO_SEGMOVE,
	/* .cnbits =          */ 8,
	/* .dnbits =          */ 8,
	/* .psnames =         */ procShortNames,
	/* .plnames =         */ procLongNames,
	/* .assemblers =      */ procAsms, 
	/* .notify =          */ idaNotifyCbk,
	/* .header =          */ idaHeaderCbk,
	/* .footer =          */ idaFooterCbk,
	/* .segstart =        */ idaSegstartCbk,
	/* .segend =          */ idaSegendCbk,
	/* .assumes =         */ idaAssumesCbk,
	/* .u_ana =           */ idaAnaCbk, 
	/* .u_emu =           */ idaEmuCbk,
	/* .u_out =           */ idaOutCbk,
	/* .u_outop =         */ idaOutOpCbk,
	/* .d_out =           */ idaOutDataCbk,
	/* .cmp_opnd =        */ idaCompareOperandCbk,
	/* .can_have_type =   */ idaCanOperandHaveType,
	/* .regsNum =         */ REG_NUM,
	/* .regNames =        */ (const char**)procRegNames,
    /* .getreg =          */ NULL, //as per spec
	/* .rFiles =          */ 0, //as per spec
	/* .rFnames =         */ NULL, //as per spec
	/* .rFdescs =         */ NULL, //as per spec,
	/* .CPUregs =         */ NULL, //as per spec
	/* .regFirstSreg =    */ 0,
	/* .regLastSreg =     */ SEG_REG_NUM,
	/* .segreg_size =     */ 32,
	/* .regCodeSreg =     */ SEG_REG_CODE,
	/* .regDataSreg =     */ SEG_REG_DATA,
	/* .codestart =       */ procFuncStartSequences,
	/* .retcodes =        */ procRetCodesSequences,
	/* .instruc_start =   */ 0, //why not?
	/* .instruc_end =     */ IDA_INSTR_NUM,
	/* .instruc =         */ procInstructions,
	/* .is_far_jump =     */ idaIsFarJumpCbk,
	/* .translate =       */ idaTranslateCbk,
	/* .tbyte_size =      */ 4, //our long double is 64 bits (aks 4 of our "bytes")
	/* .realcvt =         */ idaRealCvtCbk,
	/* .real_width =      */ {0,7,15,15},
	/* .is_switch =       */ idaIsSwitchCbk,
	/* .gen_map_file =    */ NULL, //let IDA generate map files for us
	/* .extract_address = */ idaExtractAddressCbk,
	/* .is_sp_based =     */ idaIsSpBasedCbk,
	/* .create_func_frame = */ idaCreateFuncFrameCbk,
	/* .get_frame_retsize = */ idaGetFrameRetSizeCkb,
	/* .gen_stkvar_def =    */ NULL, //let IDA generate this for us
	/* .u_outspec =         */ idaOutSpecCbk, //not even sure what this is for or if we need it
	/* .icode_return =      */ IDA_INSTR_IDX_RET,
	/* .set_options_t =     */ idaSetIdpOptionsCbk, //not even sure what this is for or if we need it
	/* .is_align_insn =     */ idaIsInsnForAlignmentOnlyCbk, 
	/* .mvm =               */ NULL, //no microcodes
	/* .high_fixup_bits =   */ 0, //we are not SPARC
};
