/***********************************************************************
 *
 *  AVRA - Assembler for the Atmel AVR microcontroller series
 *
 *  Copyright (C) 1998-2020 The AVRA Authors
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Authors of AVRA can be reached at:
 *     email: jonah@omegav.ntnu.no, tobiw@suprafluid.com
 *     www: https://github.com/Ro5bert/avra
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "misc.h"
#include "avra.h"
#include "device.h"

#define MAX_MNEMONIC_LEN	8	/* Maximum mnemonic length */

enum {
	MNEMONIC_NOP = 0,  /*          0000 0000 0000 0000 */
	MNEMONIC_SEC,      /*          1001 0100 0000 1000 */
	MNEMONIC_CLC,      /*          1001 0100 1000 1000 */
	MNEMONIC_SEN,      /*          1001 0100 0010 1000 */
	MNEMONIC_CLN,      /*          1001 0100 1010 1000 */
	MNEMONIC_SEZ,      /*          1001 0100 0001 1000 */
	MNEMONIC_CLZ,      /*          1001 0100 1001 1000 */
	MNEMONIC_SEI,      /*          1001 0100 0111 1000 */
	MNEMONIC_CLI,      /*          1001 0100 1111 1000 */
	MNEMONIC_SES,      /*          1001 0100 0100 1000 */
	MNEMONIC_CLS,      /*          1001 0100 1100 1000 */
	MNEMONIC_SEV,      /*          1001 0100 0011 1000 */
	MNEMONIC_CLV,      /*          1001 0100 1011 1000 */
	MNEMONIC_SET,      /*          1001 0100 0110 1000 */
	MNEMONIC_CLT,      /*          1001 0100 1110 1000 */
	MNEMONIC_SEH,      /*          1001 0100 0101 1000 */
	MNEMONIC_CLH,      /*          1001 0100 1101 1000 */
	MNEMONIC_SLEEP,    /*          1001 0101 1000 1000 */
	MNEMONIC_WDR,      /*          1001 0101 1010 1000 */
	MNEMONIC_IJMP,     /*          1001 0100 0000 1001 */
	MNEMONIC_EIJMP,    /*          1001 0100 0001 1001 */
	MNEMONIC_ICALL,    /*          1001 0101 0000 1001 */
	MNEMONIC_EICALL,   /*          1001 0101 0001 1001 */
	MNEMONIC_RET,      /*          1001 0101 0000 1000 */
	MNEMONIC_RETI,     /*          1001 0101 0001 1000 */
	MNEMONIC_SPM,      /*          1001 0101 1110 1000 */
	MNEMONIC_ESPM,     /*          1001 0101 1111 1000 */
	MNEMONIC_BREAK,    /*          1001 0101 1001 1000 */
	MNEMONIC_LPM,      /*          1001 0101 1100 1000 */
	MNEMONIC_ELPM,     /*          1001 0101 1101 1000 */
	MNEMONIC_BSET,     /* s        1001 0100 0sss 1000 */
	MNEMONIC_BCLR,     /* s        1001 0100 1sss 1000 */
	MNEMONIC_SER,      /* Rd       1110 1111 dddd 1111 */
	MNEMONIC_COM,      /* Rd       1001 010d dddd 0000 */
	MNEMONIC_NEG,      /* Rd       1001 010d dddd 0001 */
	MNEMONIC_INC,      /* Rd       1001 010d dddd 0011 */
	MNEMONIC_DEC,      /* Rd       1001 010d dddd 1010 */
	MNEMONIC_LSR,      /* Rd       1001 010d dddd 0110 */
	MNEMONIC_ROR,      /* Rd       1001 010d dddd 0111 */
	MNEMONIC_ASR,      /* Rd       1001 010d dddd 0101 */
	MNEMONIC_SWAP,     /* Rd       1001 010d dddd 0010 */
	MNEMONIC_PUSH,     /* Rr       1001 001r rrrr 1111 */
	MNEMONIC_POP,      /* Rd       1001 000d dddd 1111 */
	MNEMONIC_TST,      /* Rd       0010 00dd dddd dddd */
	MNEMONIC_CLR,      /* Rd       0010 01dd dddd dddd */
	MNEMONIC_LSL,      /* Rd       0000 11dd dddd dddd */
	MNEMONIC_ROL,      /* Rd       0001 11dd dddd dddd */
	MNEMONIC_BREQ,     /* k        1111 00kk kkkk k001 */
	MNEMONIC_BRNE,     /* k        1111 01kk kkkk k001 */
	MNEMONIC_BRCS,     /* k        1111 00kk kkkk k000 */
	MNEMONIC_BRCC,     /* k        1111 01kk kkkk k000 */
	MNEMONIC_BRSH,     /* k        1111 01kk kkkk k000 */
	MNEMONIC_BRLO,     /* k        1111 00kk kkkk k000 */
	MNEMONIC_BRMI,     /* k        1111 00kk kkkk k010 */
	MNEMONIC_BRPL,     /* k        1111 01kk kkkk k010 */
	MNEMONIC_BRGE,     /* k        1111 01kk kkkk k100 */
	MNEMONIC_BRLT,     /* k        1111 00kk kkkk k100 */
	MNEMONIC_BRHS,     /* k        1111 00kk kkkk k101 */
	MNEMONIC_BRHC,     /* k        1111 01kk kkkk k101 */
	MNEMONIC_BRTS,     /* k        1111 00kk kkkk k110 */
	MNEMONIC_BRTC,     /* k        1111 01kk kkkk k110 */
	MNEMONIC_BRVS,     /* k        1111 00kk kkkk k011 */
	MNEMONIC_BRVC,     /* k        1111 01kk kkkk k011 */
	MNEMONIC_BRIE,     /* k        1111 00kk kkkk k111 */
	MNEMONIC_BRID,     /* k        1111 01kk kkkk k111 */
	MNEMONIC_RJMP,     /* k        1100 kkkk kkkk kkkk */
	MNEMONIC_RCALL,    /* k        1101 kkkk kkkk kkkk */
	MNEMONIC_JMP,      /* k        1001 010k kkkk 110k + 16k */
	MNEMONIC_CALL,     /* k        1001 010k kkkk 111k + 16k */
	MNEMONIC_BRBS,     /* s, k     1111 00kk kkkk ksss */
	MNEMONIC_BRBC,     /* s, k     1111 01kk kkkk ksss */
	MNEMONIC_ADD,      /* Rd, Rr   0000 11rd dddd rrrr */
	MNEMONIC_ADC,      /* Rd, Rr   0001 11rd dddd rrrr */
	MNEMONIC_SUB,      /* Rd, Rr   0001 10rd dddd rrrr */
	MNEMONIC_SBC,      /* Rd, Rr   0000 10rd dddd rrrr */
	MNEMONIC_AND,      /* Rd, Rr   0010 00rd dddd rrrr */
	MNEMONIC_OR,       /* Rd, Rr   0010 10rd dddd rrrr */
	MNEMONIC_EOR,      /* Rd, Rr   0010 01rd dddd rrrr */
	MNEMONIC_CP,       /* Rd, Rr   0001 01rd dddd rrrr */
	MNEMONIC_CPC,      /* Rd, Rr   0000 01rd dddd rrrr */
	MNEMONIC_CPSE,     /* Rd, Rr   0001 00rd dddd rrrr */
	MNEMONIC_MOV,      /* Rd, Rr   0010 11rd dddd rrrr */
	MNEMONIC_MUL,      /* Rd, Rr   1001 11rd dddd rrrr */
	MNEMONIC_MOVW,     /* Rd, Rr   0000 0001 dddd rrrr */
	MNEMONIC_MULS,     /* Rd, Rr   0000 0010 dddd rrrr */
	MNEMONIC_MULSU,    /* Rd, Rr   0000 0011 0ddd 0rrr */
	MNEMONIC_FMUL,     /* Rd, Rr   0000 0011 0ddd 1rrr */
	MNEMONIC_FMULS,    /* Rd, Rr   0000 0011 1ddd 0rrr */
	MNEMONIC_FMULSU,   /* Rd, Rr   0000 0011 1ddd 1rrr */
	MNEMONIC_ADIW,     /* Rd, K    1001 0110 KKdd KKKK */
	MNEMONIC_SBIW,     /* Rd, K    1001 0111 KKdd KKKK */
	MNEMONIC_SUBI,     /* Rd, K    0101 KKKK dddd KKKK */
	MNEMONIC_SBCI,     /* Rd, K    0100 KKKK dddd KKKK */
	MNEMONIC_ANDI,     /* Rd, K    0111 KKKK dddd KKKK */
	MNEMONIC_ORI,      /* Rd, K    0110 KKKK dddd KKKK */
	MNEMONIC_SBR,      /* Rd, K    0110 KKKK dddd KKKK */
	MNEMONIC_CPI,      /* Rd, K    0011 KKKK dddd KKKK */
	MNEMONIC_LDI,      /* Rd, K    1110 KKKK dddd KKKK */
	MNEMONIC_CBR,      /* Rd, K    0111 KKKK dddd KKKK ~K */
	MNEMONIC_SBRC,     /* Rr, b    1111 110r rrrr 0bbb */
	MNEMONIC_SBRS,     /* Rr, b    1111 111r rrrr 0bbb */
	MNEMONIC_BST,      /* Rr, b    1111 101d dddd 0bbb */
	MNEMONIC_BLD,      /* Rd, b    1111 100d dddd 0bbb */
	MNEMONIC_IN,       /* Rd, P    1011 0PPd dddd PPPP */
	MNEMONIC_OUT,      /* P, Rr    1011 1PPr rrrr PPPP */
	MNEMONIC_SBIC,     /* P, b     1001 1001 PPPP Pbbb */
	MNEMONIC_SBIS,     /* P, b     1001 1011 PPPP Pbbb */
	MNEMONIC_SBI,      /* P, b     1001 1010 PPPP Pbbb */
	MNEMONIC_CBI,      /* P, b     1001 1000 PPPP Pbbb */
	MNEMONIC_LDS,      /* Rd, k    1001 000d dddd 0000 + 16k */
	MNEMONIC_STS,      /* k, Rr    1001 001d dddd 0000 + 16k */
	MNEMONIC_LD,       /* Rd, __   dummy */
	MNEMONIC_ST,       /* __, Rr   dummy */
	MNEMONIC_LDD,      /* Rd, _+q  dummy */
	MNEMONIC_STD,      /* _+q, Rr  dummy */
	MNEMONIC_COUNT,
	MNEMONIC_LPM_Z,    /* Rd, Z    1001 000d dddd 0100 */
	MNEMONIC_LPM_ZP,   /* Rd, Z+   1001 000d dddd 0101 */
	MNEMONIC_ELPM_Z,   /* Rd, Z    1001 000d dddd 0110 */
	MNEMONIC_ELPM_ZP,  /* Rd, Z+   1001 000d dddd 0111 */
	MNEMONIC_LD_X,     /* Rd, X    1001 000d dddd 1100 */
	MNEMONIC_LD_XP,    /* Rd, X+   1001 000d dddd 1101 */
	MNEMONIC_LD_MX,    /* Rd, -X   1001 000d dddd 1110 */
	MNEMONIC_LD_Y,     /* Rd, Y    1000 000d dddd 1000 */
	MNEMONIC_LD_YP,    /* Rd, Y+   1001 000d dddd 1001 */
	MNEMONIC_LD_MY,    /* Rd, -Y   1001 000d dddd 1010 */
	MNEMONIC_LD_Z,     /* Rd, Z    1000 000d dddd 0000 */
	MNEMONIC_LD_ZP,    /* Rd, Z+   1001 000d dddd 0001 */
	MNEMONIC_LD_MZ,    /* Rd, -Z   1001 000d dddd 0010 */
	MNEMONIC_ST_X,     /* X, Rr    1001 001d dddd 1100 */
	MNEMONIC_ST_XP,    /* X+, Rr   1001 001d dddd 1101 */
	MNEMONIC_ST_MX,    /* -X, Rr   1001 001d dddd 1110 */
	MNEMONIC_ST_Y,     /* Y, Rr    1000 001d dddd 1000 */
	MNEMONIC_ST_YP,    /* Y+, Rr   1001 001d dddd 1001 */
	MNEMONIC_ST_MY,    /* -Y, Rr   1001 001d dddd 1010 */
	MNEMONIC_ST_Z,     /* Z, Rr    1000 001d dddd 0000 */
	MNEMONIC_ST_ZP,    /* Z+, Rr   1001 001d dddd 0001 */
	MNEMONIC_ST_MZ,    /* -Z, Rr   1001 001d dddd 0010 */
	MNEMONIC_LDD_Y,    /* Rd, Y+q  10q0 qq0d dddd 1qqq */
	MNEMONIC_LDD_Z,    /* Rd, Z+q  10q0 qq0d dddd 0qqq */
	MNEMONIC_STD_Y,    /* Y+q, Rr  10q0 qq1r rrrr 1qqq */
	MNEMONIC_STD_Z,    /* Z+q, Rr  10q0 qq1r rrrr 0qqq */
	MNEMONIC_LDS_AVR8L,/* Rd, k    1010 0kkk dddd kkkk */
	MNEMONIC_STS_AVR8L,/* Rd, k    1010 1kkk dddd kkkk */
	MNEMONIC_END
};

struct instruction {
	char *mnemonic;
	int opcode;
	int flag;	/* Device flags meaning the instruction is not supported */
};

struct instruction instruction_list[] = {
	{"nop",   0x0000,          0},
	{"sec",   0x9408,          0},
	{"clc",   0x9488,          0},
	{"sen",   0x9428,          0},
	{"cln",   0x94a8,          0},
	{"sez",   0x9418,          0},
	{"clz",   0x9498,          0},
	{"sei",   0x9478,          0},
	{"cli",   0x94f8,          0},
	{"ses",   0x9448,          0},
	{"cls",   0x94c8,          0},
	{"sev",   0x9438,          0},
	{"clv",   0x94b8,          0},
	{"set",   0x9468,          0},
	{"clt",   0x94e8,          0},
	{"seh",   0x9458,          0},
	{"clh",   0x94d8,          0},
	{"sleep", 0x9588,          0},
	{"wdr",   0x95a8,          0},
	{"ijmp",  0x9409,  DF_TINY1X},
	{"eijmp", 0x9419, DF_NO_EIJMP},
	{"icall", 0x9509,  DF_TINY1X},
	{"eicall",0x9519, DF_NO_EICALL},
	{"ret",   0x9508,          0},
	{"reti",  0x9518,          0},
	{"spm",   0x95e8, DF_NO_SPM},
	{"espm",  0x95f8, DF_NO_ESPM},
	{"break", 0x9598, DF_NO_BREAK},
	{"lpm",   0x95c8, DF_NO_LPM},
	{"elpm",  0x95d8, DF_NO_ELPM},
	{"bset",  0x9408,          0},
	{"bclr",  0x9488,          0},
	{"ser",   0xef0f,          0},
	{"com",   0x9400,          0},
	{"neg",   0x9401,          0},
	{"inc",   0x9403,          0},
	{"dec",   0x940a,          0},
	{"lsr",   0x9406,          0},
	{"ror",   0x9407,          0},
	{"asr",   0x9405,          0},
	{"swap",  0x9402,          0},
	{"push",  0x920f,  DF_TINY1X},
	{"pop",   0x900f,  DF_TINY1X},
	{"tst",   0x2000,          0},
	{"clr",   0x2400,          0},
	{"lsl",   0x0c00,          0},
	{"rol",   0x1c00,          0},
	{"breq",  0xf001,          0},
	{"brne",  0xf401,          0},
	{"brcs",  0xf000,          0},
	{"brcc",  0xf400,          0},
	{"brsh",  0xf400,          0},
	{"brlo",  0xf000,          0},
	{"brmi",  0xf002,          0},
	{"brpl",  0xf402,          0},
	{"brge",  0xf404,          0},
	{"brlt",  0xf004,          0},
	{"brhs",  0xf005,          0},
	{"brhc",  0xf405,          0},
	{"brts",  0xf006,          0},
	{"brtc",  0xf406,          0},
	{"brvs",  0xf003,          0},
	{"brvc",  0xf403,          0},
	{"brie",  0xf007,          0},
	{"brid",  0xf407,          0},
	{"rjmp",  0xc000,          0},
	{"rcall", 0xd000,          0},
	{"jmp",   0x940c,  DF_NO_JMP},
	{"call",  0x940e,  DF_NO_JMP},
	{"brbs",  0xf000,          0},
	{"brbc",  0xf400,          0},
	{"add",   0x0c00,          0},
	{"adc",   0x1c00,          0},
	{"sub",   0x1800,          0},
	{"sbc",   0x0800,          0},
	{"and",   0x2000,          0},
	{"or",    0x2800,          0},
	{"eor",   0x2400,          0},
	{"cp",    0x1400,          0},
	{"cpc",   0x0400,          0},
	{"cpse",  0x1000,          0},
	{"mov",   0x2c00,          0},
	{"mul",   0x9c00, DF_NO_MUL},
	{"movw",  0x0100, DF_NO_MOVW},
	{"muls",  0x0200, DF_NO_MUL},
	{"mulsu", 0x0300, DF_NO_MUL},
	{"fmul",  0x0308, DF_NO_MUL},
	{"fmuls", 0x0380, DF_NO_MUL},
	{"fmulsu",0x0388, DF_NO_MUL},
	{"adiw",  0x9600,  DF_TINY1X | DF_AVR8L},
	{"sbiw",  0x9700,  DF_TINY1X | DF_AVR8L},
	{"subi",  0x5000,          0},
	{"sbci",  0x4000,          0},
	{"andi",  0x7000,          0},
	{"ori",   0x6000,          0},
	{"sbr",   0x6000,          0},
	{"cpi",   0x3000,          0},
	{"ldi",   0xe000,          0},
	{"cbr",   0x7000,          0},
	{"sbrc",  0xfc00,          0},
	{"sbrs",  0xfe00,          0},
	{"bst",   0xfa00,          0},
	{"bld",   0xf800,          0},
	{"in",    0xb000,          0},
	{"out",   0xb800,          0},
	{"sbic",  0x9900,          0},
	{"sbis",  0x9b00,          0},
	{"sbi",   0x9a00,          0},
	{"cbi",   0x9800,          0},
	{"lds",   0x9000,  DF_TINY1X | DF_AVR8L},
	{"sts",   0x9200,  DF_TINY1X | DF_AVR8L},
	{"ld",    0,          0},
	{"st",    0,          0},
	{"ldd",   0,  DF_TINY1X},
	{"std",   0,  DF_TINY1X},
	{"count", 0,          0},
	{"lpm",   0x9004, DF_NO_LPM|DF_NO_LPM_X},
	{"lpm",   0x9005, DF_NO_LPM|DF_NO_LPM_X},
	{"elpm",  0x9006, DF_NO_ELPM|DF_NO_ELPM_X},
	{"elpm",  0x9007, DF_NO_ELPM|DF_NO_ELPM_X},
	{"ld",    0x900c, DF_NO_XREG},
	{"ld",    0x900d, DF_NO_XREG},
	{"ld",    0x900e, DF_NO_XREG},
	{"ld",    0x8008, DF_NO_YREG},
	{"ld",    0x9009, DF_NO_YREG},
	{"ld",    0x900a, DF_NO_YREG},
	{"ld",    0x8000,          0},
	{"ld",    0x9001, DF_TINY1X},
	{"ld",    0x9002, DF_TINY1X},
	{"st",    0x920c, DF_NO_XREG},
	{"st",    0x920d, DF_NO_XREG},
	{"st",    0x920e, DF_NO_XREG},
	{"st",    0x8208, DF_NO_YREG},
	{"st",    0x9209, DF_NO_YREG},
	{"st",    0x920a, DF_NO_YREG},
	{"st",    0x8200,          0},
	{"st",    0x9201, DF_TINY1X},
	{"st",    0x9202, DF_TINY1X},
	{"ldd",   0x8008, DF_TINY1X},
	{"ldd",   0x8000, DF_TINY1X},
	{"std",   0x8208, DF_TINY1X},
	{"std",   0x8200, DF_TINY1X},
	{"lds",   0xa000, DF_TINY1X},
	{"sts",   0xa800, DF_TINY1X},
	{"end", 0, 0}
};


/* We try to parse the command name. Is it a assembler mnemonic or anything else ?
 * If so, it may be a macro. */

int
parse_mnemonic(struct prog_info *pi)
{
	int mnemonic;
	int i;
	int opcode = 0;
	int opcode2 = 0;
	int instruction_long = False;
	char *operand1;
	char *operand2;
	struct macro *macro;
	char temp[MAX_MNEMONIC_LEN + 1];

	operand1 = get_next_token(pi->fi->scratch, TERM_SPACE);  /* we get the first word on line */
	mnemonic = get_mnemonic_type(pi);
	if (mnemonic == -1) {				/* if -1 this must be a macro name */
		macro = get_macro(pi, pi->fi->scratch); /* and so, we try to get the corresponding macro struct. */
		if (macro) {
			return (expand_macro(pi, macro, operand1)); /* we expand the macro */
		} else { 				/* if we cant find a name, this is a unknown word. */
			print_msg(pi, MSGTYPE_ERROR, "Unknown mnemonic/macro: %s", pi->fi->scratch);
			return (True);
		}
	}
	if (pi->pass == PASS_2) {
		if (mnemonic <= MNEMONIC_BREAK) {
			if (operand1) {
				print_msg(pi, MSGTYPE_WARNING, "Garbage after instruction %s: %s", instruction_list[mnemonic].mnemonic, operand1);
			}
			opcode = 0;			/* No operand */
		} else if (mnemonic <= MNEMONIC_ELPM) {
			if (operand1) {
				operand2 = get_next_token(operand1, TERM_COMMA);
				if (!operand2) {
					print_msg(pi, MSGTYPE_ERROR, "%s needs a second operand", instruction_list[mnemonic].mnemonic);
					return (True);
				}
				get_next_token(operand2, TERM_END);
				i = get_register(pi, operand1);
				opcode = i << 4;
				i = get_indirect(pi, operand2);
				if (i == 6) { /* Means Z */
					if (mnemonic == MNEMONIC_LPM)
						mnemonic = MNEMONIC_LPM_Z;
					else if (mnemonic == MNEMONIC_ELPM)
						mnemonic = MNEMONIC_ELPM_Z;
				} else if (i == 7) { /* Means Z+ */
					if (mnemonic == MNEMONIC_LPM)
						mnemonic = MNEMONIC_LPM_ZP;
					else if (mnemonic == MNEMONIC_ELPM)
						mnemonic = MNEMONIC_ELPM_ZP;
				} else {
					print_msg(pi, MSGTYPE_ERROR, "Unsupported operand: %s", operand2);
					return (True);
				}
			} else
				opcode = 0;
		} else {
			if (!operand1) {
				print_msg(pi, MSGTYPE_ERROR, "%s needs an operand", instruction_list[mnemonic].mnemonic);
				return (True);
			}
			operand2 = get_next_token(operand1, TERM_COMMA);
			if (mnemonic >= MNEMONIC_BRBS) {
				if (!operand2) {
					print_msg(pi, MSGTYPE_ERROR, "%s needs a second operand", instruction_list[mnemonic].mnemonic);
					return (True);
				}
				get_next_token(operand2, TERM_END);
			}
			if (mnemonic <= MNEMONIC_BCLR) {
				if (!get_bitnum(pi, operand1, &i))
					return (False);
				opcode = i << 4;
			} else if (mnemonic <= MNEMONIC_ROL) {
				i = get_register(pi, operand1);
				if ((mnemonic == MNEMONIC_SER) && (i < 16)) {
					print_msg(pi, MSGTYPE_ERROR, "%s can only use a high register (r16 - r31)", instruction_list[mnemonic].mnemonic);
					i &= 0x0f;
				}
				opcode = i << 4;
				if (mnemonic >= MNEMONIC_TST)
					opcode |= ((i & 0x10) << 5) | (i & 0x0f);
			} else if (mnemonic <= MNEMONIC_RCALL) {
				if (!get_expr(pi, operand1, &i))
					return (False);
				i -= pi->cseg->addr + 1;
				if (mnemonic <= MNEMONIC_BRID) {
					if ((i < -64) || (i > 63))
						print_msg(pi, MSGTYPE_ERROR, "Branch out of range (-64 <= k <= 63)");
					opcode = (i & 0x7f) << 3;
				} else {
					if (((i < -2048) || (i > 2047)) && (pi->device->flash_size != 4096))
						print_msg(pi, MSGTYPE_ERROR, "Relative address out of range (-2048 <= k <= 2047)");
					opcode = i & 0x0fff;
				}
			} else if (mnemonic <= MNEMONIC_CALL) {
				if (!get_expr(pi, operand1, &i))
					return (False);
				if ((i < 0) || (i > 4194303))
					print_msg(pi, MSGTYPE_ERROR, "Address out of range (0 <= k <= 4194303)");
				opcode = ((i & 0x3e0000) >> 13) | ((i & 0x010000) >> 16);
				opcode2 = i & 0xffff;
				instruction_long = True;
			} else if (mnemonic <= MNEMONIC_BRBC) {
				if (!get_bitnum(pi, operand1, &i))
					return (False);
				opcode = i;
				if (!get_expr(pi, operand2, &i))
					return (False);
				i -= pi->cseg->addr + 1;
				if ((i < -64) || (i > 63))
					print_msg(pi, MSGTYPE_ERROR, "Branch out of range (-64 <= k <= 63)");
				opcode |= (i & 0x7f) << 3;
			} else if (mnemonic <= MNEMONIC_MUL) {
				i = get_register(pi, operand1);
				opcode = i << 4;
				i = get_register(pi, operand2);
				opcode |= ((i & 0x10) << 5) | (i & 0x0f);
			} else if (mnemonic <= MNEMONIC_MOVW) {
				i = get_register(pi, operand1);
				if ((i % 2) == 1)
					print_msg(pi, MSGTYPE_ERROR, "%s must use a even numbered register for Rd", instruction_list[mnemonic].mnemonic);
				opcode = (i / 2) << 4;
				i = get_register(pi, operand2);
				if ((i % 2) == 1)
					print_msg(pi, MSGTYPE_ERROR, "%s must use a even numbered register for Rr", instruction_list[mnemonic].mnemonic);
				opcode |= i / 2;
			} else if (mnemonic <= MNEMONIC_MULS) {
				i = get_register(pi, operand1);
				if (i < 16)
					print_msg(pi, MSGTYPE_ERROR, "%s can only use a high register (r16 - r31)", instruction_list[mnemonic].mnemonic);
				opcode = (i & 0x0f) << 4;
				i = get_register(pi, operand2);
				if (i < 16)
					print_msg(pi, MSGTYPE_ERROR, "%s can only use a high register (r16 - r31)", instruction_list[mnemonic].mnemonic);
				opcode |= (i & 0x0f);
			} else if (mnemonic <= MNEMONIC_FMULSU) {
				i = get_register(pi, operand1);
				if ((i < 16) || (i >= 24))
					print_msg(pi, MSGTYPE_ERROR, "%s can only use registers (r16 - r23)", instruction_list[mnemonic].mnemonic);
				opcode = (i & 0x07) << 4;
				i = get_register(pi, operand2);
				if ((i < 16) || (i >= 24))
					print_msg(pi, MSGTYPE_ERROR, "%s can only use registers (r16 - r23)", instruction_list[mnemonic].mnemonic);
				opcode |= (i & 0x07);
			} else if (mnemonic <= MNEMONIC_SBIW) {
				i = get_register(pi, operand1);
				if (!((i == 24) || (i == 26) || (i == 28) || (i == 30)))
					print_msg(pi, MSGTYPE_ERROR, "%s can only use registers R24, R26, R28 or R30", instruction_list[mnemonic].mnemonic);
				opcode = ((i - 24) / 2) << 4;
				if (!get_expr(pi, operand2, &i))
					return (False);
				if ((i < 0) || (i > 63))
					print_msg(pi, MSGTYPE_ERROR, "Constant out of range (0 <= k <= 63)");
				opcode |= ((i & 0x30) << 2) | (i & 0x0f);
			} else if (mnemonic <= MNEMONIC_CBR) {
				i = get_register(pi, operand1);
				if (i < 16)
					print_msg(pi, MSGTYPE_ERROR, "%s can only use a high register (r16 - r31)", instruction_list[mnemonic].mnemonic);
				opcode = (i & 0x0f) << 4;
				if (!get_expr(pi, operand2, &i))
					return (False);
				if ((i < -128) || (i > 255))
					print_msg(pi, MSGTYPE_WARNING, "Constant out of range (-128 <= k <= 255). Will be masked");
				if (mnemonic == MNEMONIC_CBR)
					i = ~i;
				opcode |= ((i & 0xf0) << 4) | (i & 0x0f);
			} else if (mnemonic <= MNEMONIC_BLD) {
				i = get_register(pi, operand1);
				opcode = i << 4;
				if (!get_bitnum(pi, operand2, &i))
					return (False);
				opcode |= i;
			} else if (mnemonic == MNEMONIC_IN) {
				i = get_register(pi, operand1);
				opcode = i << 4;
				if (!get_expr(pi, operand2, &i))
					return (False);
				if ((i < 0) || (i > 63))
					print_msg(pi, MSGTYPE_ERROR, "I/O out of range (0 <= P <= 63)");
				opcode |= ((i & 0x30) << 5) | (i & 0x0f);
			} else if (mnemonic == MNEMONIC_OUT) {
				if (!get_expr(pi, operand1, &i))
					return (False);
				if ((i < 0) || (i > 63))
					print_msg(pi, MSGTYPE_ERROR, "I/O out of range (0 <= P <= 63)");
				opcode = ((i & 0x30) << 5) | (i & 0x0f);
				i = get_register(pi, operand2);
				opcode |= i << 4;
			} else if (mnemonic <= MNEMONIC_CBI) {
				if (!get_expr(pi, operand1, &i))
					return (False);
				if ((i < 0) || (i > 31))
					print_msg(pi, MSGTYPE_ERROR, "I/O out of range (0 <= P <= 31)");
				opcode = i << 3;
				if (!get_bitnum(pi, operand2, &i))
					return (False);
				opcode |= i;
			} else if (mnemonic == MNEMONIC_LDS) {
				i = get_register(pi, operand1);
				opcode = i << 4;
				/* AVR8L has one word LDS. High nibble of k in funny order */
				if (pi->device->flag & DF_AVR8L) {
					mnemonic = MNEMONIC_LDS_AVR8L;
					opcode &= 0x00f0;
				}
				if (!get_expr(pi, operand2, &i))
					return (False);
				if (pi->device->flag & DF_AVR8L) {
					if ((i < 0x40) || (i > 0xbf))
						print_msg(pi, MSGTYPE_ERROR, "SRAM out of range (0x40 <= k <= 0xbf)");
					opcode |= ((i & 0x40) << 2) | ((i & 0x30) << 5) | (i & 0x0f);
				} else {
					if ((i < 0) || (i > 65535))
						print_msg(pi, MSGTYPE_ERROR, "SRAM out of range (0 <= k <= 65535)");
					opcode2 = i;
					instruction_long = True;
				}
			} else if (mnemonic == MNEMONIC_STS) {
				if (!get_expr(pi, operand1, &i))
					return (False);
				/* AVR8L has one word STS. High nibble of k in funny order */
				if (pi->device->flag & DF_AVR8L) {
					mnemonic = MNEMONIC_STS_AVR8L;
					if ((i < 0x40) || (i > 0xbf))
						print_msg(pi, MSGTYPE_ERROR, "SRAM out of range (0x40 <= k <= 0xbf)");
					opcode |= ((i & 0x40) << 2) | ((i & 0x30) << 5) | (i & 0x0f);
				} else {
					if ((i < 0) || (i > 65535))
						print_msg(pi, MSGTYPE_ERROR, "SRAM out of range (0 <= k <= 65535)");
					opcode2 = i;
					instruction_long = True;
				}
				i = get_register(pi, operand2);
				if (pi->device->flag & DF_AVR8L)
					opcode |= ((i << 4) & 0x00f0);
				else
					opcode = i << 4;
			} else if (mnemonic == MNEMONIC_LD) {
				i = get_register(pi, operand1);
				opcode = i << 4;
				mnemonic = MNEMONIC_LD_X + get_indirect(pi, operand2);
			} else if (mnemonic == MNEMONIC_ST) {
				mnemonic = MNEMONIC_ST_X + get_indirect(pi, operand1);
				i = get_register(pi, operand2);
				opcode = i << 4;
			} else if (mnemonic == MNEMONIC_LDD) {
				i = get_register(pi, operand1);
				opcode = i << 4;
				if (tolower(operand2[0]) == 'z')
					mnemonic = MNEMONIC_LDD_Z;
				else if (tolower(operand2[0]) == 'y')
					mnemonic = MNEMONIC_LDD_Y;
				else
					print_msg(pi, MSGTYPE_ERROR, "Garbage in second operand (%s)", operand2);
				i = 1;
				while ((operand2[i] != '\0') && (operand2[i] != '+')) i++;
				if (operand2[i] == '\0')	{
					print_msg(pi, MSGTYPE_ERROR, "Garbage in second operand (%s)", operand2);
					return (False);
				}
				if (!get_expr(pi, &operand2[i + 1], &i))
					return (False);
				if ((i < 0) || (i > 63))
					print_msg(pi, MSGTYPE_ERROR, "Displacement out of range (0 <= q <= 63)");
				opcode |= ((i & 0x20) << 8) | ((i & 0x18) << 7) | (i & 0x07);
			} else if (mnemonic == MNEMONIC_STD) {
				if (tolower(operand1[0]) == 'z')
					mnemonic = MNEMONIC_STD_Z;
				else if (tolower(operand1[0]) == 'y')
					mnemonic = MNEMONIC_STD_Y;
				else
					print_msg(pi, MSGTYPE_ERROR, "Garbage in first operand (%s)", operand1);
				i = 1;
				while ((operand1[i] != '\0') && (operand1[i] != '+')) i++;
				if (operand1[i] == '\0')	{
					print_msg(pi, MSGTYPE_ERROR, "Garbage in first operand (%s)", operand1);
					return (False);
				}
				if (!get_expr(pi, &operand1[i + 1], &i))
					return (False);
				if ((i < 0) || (i > 63))
					print_msg(pi, MSGTYPE_ERROR, "Displacement out of range (0 <= q <= 63)");
				opcode = ((i & 0x20) << 8) | ((i & 0x18) << 7) | (i & 0x07);
				i = get_register(pi, operand2);
				opcode |= i << 4;
			} else
				print_msg(pi, MSGTYPE_ERROR, "Shit! Missing opcode check [%d]...", mnemonic);
		}
		if (pi->device->flag & instruction_list[mnemonic].flag)	{
			strncpy(temp, instruction_list[mnemonic].mnemonic, MAX_MNEMONIC_LEN);
			print_msg(pi, MSGTYPE_ERROR, "%s instruction is not supported on %s",
			          my_strupr(temp), pi->device->name);
		}
		opcode |= instruction_list[mnemonic].opcode;
		if (pi->list_on && pi->list_line) {
			if (instruction_long)
				fprintf(pi->list_file, "%c:%06lx %04x %04x %s\n",
				        pi->cseg->ident, pi->cseg->addr, opcode, opcode2, pi->list_line);
			else
				fprintf(pi->list_file, "%c:%06lx %04x      %s\n",
				        pi->cseg->ident, pi->cseg->addr, opcode, pi->list_line);
			pi->list_line = NULL;
		}
		if (pi->cseg->hfi) {
			write_prog_word(pi, pi->cseg->addr, opcode);
			if (instruction_long)
				write_prog_word(pi, pi->cseg->addr + 1, opcode2);
		}
		if (instruction_long)
			pi->cseg->addr += 2; /* XXX advance */
		else
			pi->cseg->addr ++;
	} else { /* Pass 1 */
		if (pi->device->flag & DF_AVR8L)
			mnemonic = MNEMONIC_LDS_AVR8L;
		if ((mnemonic == MNEMONIC_JMP) || (mnemonic == MNEMONIC_CALL)
		        || (mnemonic == MNEMONIC_LDS) || (mnemonic == MNEMONIC_STS)) {
			pi->cseg->addr += 2;
			pi->cseg->count += 2;
		} else {
			pi->cseg->addr++;
			pi->cseg->count++;
		}
	}
	return (True);
}

int
get_mnemonic_type(struct prog_info *pi)
{
	int i;
	char *mnemonic;

	mnemonic = my_strlwr(pi->fi->scratch);

	for (i = 0; i < MNEMONIC_COUNT; i++) {
		if (!strcmp(mnemonic, instruction_list[i].mnemonic)) {
			return (i);
		}
	}
	return (-1);
}


int
get_register(struct prog_info *pi, char *data)
{
	char *second_reg;
	int reg = 0;
	struct def *def;

	/* Check for any occurence of r1:r0 pairs, and if so skip to second register */
	second_reg = strchr(data, ':');
	if (second_reg != NULL)
		data = second_reg + 1;

	for (def = pi->first_def; def; def = def->next)
		if (!nocase_strcmp(def->name, data)) {
			reg = def->reg;
			return (reg);
		}
	if ((tolower(data[0]) == 'r') && isdigit(data[1])) {
		reg = atoi(&data[1]);
		if (reg > 31)
			print_msg(pi, MSGTYPE_ERROR, "R%d is not a valid register", reg);
		return (reg);
	}
	if (data[1] != '\0') {
		print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", data);
	}
	switch (data[0]) {
	case 'x':
		reg = 26;
		break;
	case 'y':
		reg = 28;
		break;
	case 'z':
		reg = 30;
		break;
	default:
		print_msg(pi, MSGTYPE_ERROR, "No register associated with %s", data);
	}
	if ((reg < 16) && (pi->device->flag & DF_AVR8L))
		print_msg(pi, MSGTYPE_ERROR, "%s can only use a high registers (r16 - r31)", pi->device->name);

	return (reg);
}

int
get_bitnum(struct prog_info *pi, char *data, int *ret)
{
	if (!get_expr(pi, data, ret))
		return (False);
	if ((*ret < 0) || (*ret > 7)) {
		print_msg(pi, MSGTYPE_ERROR, "Operand out of range (0 <= s <= 7)");
		return (False);
	}
	return (True);
}


int
get_indirect(struct prog_info *pi, char *operand)
{
	int i = 1;

	switch (tolower(operand[0])) {
	case '-':
		while (IS_HOR_SPACE(operand[i])) i++;
		if (operand[i + 1] != '\0')
			print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", operand);
		switch (tolower(operand[i])) {
		case 'x':
			if (pi->device->flag & DF_NO_XREG)
				print_msg(pi, MSGTYPE_ERROR, "X register is not supported on %s", pi->device->name);
			return (2);
		case 'y':
			if (pi->device->flag & DF_NO_YREG)
				print_msg(pi, MSGTYPE_ERROR, "Y register is not supported on %s", pi->device->name);
			return (5);
		case 'z':
			return (8);
		default:
			print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", operand);
			return (0);
		}
	case 'x':
		if (pi->device->flag & DF_NO_XREG)
			print_msg(pi, MSGTYPE_ERROR, "X register is not supported on %s", pi->device->name);
		while (IS_HOR_SPACE(operand[i])) i++;
		if (operand[i] == '+') {
			if (operand[i + 1] != '\0')
				print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", operand);
			return (1);
		} else if (operand[i] == '\0')
			return (0);
		else
			print_msg(pi, MSGTYPE_ERROR, "Garbage after operand (%s)", operand);
		return (0);
	case 'y':
		if (pi->device->flag & DF_NO_YREG)
			print_msg(pi, MSGTYPE_ERROR, "Y register is not supported on %s", pi->device->name);
		while (IS_HOR_SPACE(operand[i])) i++;
		if (operand[i] == '+') {
			if (operand[i + 1] != '\0')
				print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", operand);
			return (4);
		} else if (operand[i] == '\0')
			return (3);
		else
			print_msg(pi, MSGTYPE_ERROR, "Garbage after operand (%s)", operand);
		return (0);
	case 'z':
		while (IS_HOR_SPACE(operand[i])) i++;
		if (operand[i] == '+') {
			if (operand[i + 1] != '\0')
				print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", operand);
			return (7);
		} else if (operand[i] == '\0')
			return (6);
		else
			print_msg(pi, MSGTYPE_ERROR, "Garbage after operand (%s)", operand);
		return (0);
	default:
		print_msg(pi, MSGTYPE_ERROR, "Garbage in operand (%s)", operand);
	}
	return (0);
}

/* Return 1 if instruction name is supported by the current device,
 * 0 if unsupported, -1 if it is invalid */
int
is_supported(struct prog_info *pi, char *name)
{
	int mnemonic;

	strncpy(pi->fi->scratch,name,MAX_MNEMONIC_LEN);
	mnemonic = get_mnemonic_type(pi);
	if (mnemonic == -1) return -1;
	if (pi->device->flag & instruction_list[mnemonic].flag) return 0;
	return 1;
}

int
count_supported_instructions(int flags)
{
	int i = 0, count = 0;
	while (i < MNEMONIC_END) {
		if ((i < MNEMONIC_LD) || (i > MNEMONIC_COUNT))
			if (!(flags & instruction_list[i].flag))
				count++;
		i++;
	}
	return (count);
}

/* end of mnemonic.c */

