/*
 * picasm -- pic16bit.c
 *
 * Copyright 1995-2004 Timo Rossi, <trossi@iki.fi>
 * See the file LICENSE for license terms.
 *
 */

#include <stdio.h>

#include "picasm.h"
#include "token.h"
#include "symtab.h"

/*
 * Assemble 16-bit PIC code
 */
int
assemble_16bit_mnemonic(int op)
{
    long val;
    struct symbol *sym;
    char *cp;
    int symtype;
    long i, t;

    switch(op)
    {
    case KW_SUBLW:
    case KW_ANDLW:
    case KW_IORLW:
    case KW_XORLW:
    case KW_MULLW:
    case KW_MOVLB:
    case KW_MOVLR:
    case KW_CPFSEQ:
    case KW_CPFSGT:
    case KW_CPFSLT:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < -0x80 || val > 0xff)
	    error(0, "8-bit literal out of range");

	if(val < 0)
	    val += 0x100;

	switch(op)
	{
        case KW_SUBLW:  gen_code(0xb200 | val); break;
	case KW_ANDLW:  gen_code(0xb500 | val); break;
	case KW_IORLW:  gen_code(0xb300 | val); break;
	case KW_XORLW:  gen_code(0xb400 | val); break;
	case KW_MULLW:
	    if(pic_instr_set == PIC16BIT)
	    {
		gen_code(0xbc00 | val);
	    }
	    else
	    {
		error(1, "Unimplemented instruction MULLW");
	    }
	    break;
	case KW_MOVLB:  gen_code(0xb800 | (val & 0x0f)); break;
	case KW_MOVLR:
	    if(pic_instr_set == PIC16BIT)
	    {
		gen_code(0xba00 | (val & 0xf0));
	    }
	    else
	    {
		error(1, "Unimplemented instruction MOVLR");
	    }
	    break;
	case KW_CPFSEQ: gen_code(0x3100 | val); break;
	case KW_CPFSGT: gen_code(0x3200 | val); break;
	case KW_CPFSLT: gen_code(0x3000 | val); break;
	}
	break;

    case KW_ADDLW:
	if(gen_byte_c(0xb100) != OK)
	    return FAIL;
	break;

    case KW_MOVLW:
	if(gen_byte_c(0xb000) != OK)
	    return FAIL;
	break;

    case KW_ADDWF:
    case KW_ADDWFC:
    case KW_SUBWF:
    case KW_SUBWFB:
    case KW_ANDWF:
    case KW_IORWF:
    case KW_XORWF:
    case KW_COMF:
    case KW_DECF:
    case KW_INCF:
    case KW_MOVF:
    case KW_DECFSZ:
    case KW_DCFSNZ:
    case KW_INCFSZ:
    case KW_INFSNZ:
    case KW_RLCF:
    case KW_RRCF:
    case KW_RLNCF:
    case KW_RRNCF:
    case KW_SWAPF:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Register file address out of range");

	t = 1;
	if(token_type == TOK_COMMA)
	{
	    get_token();
	    if(get_expression(&t) != OK)
		return FAIL;
	}
	else
	{
	    if(warnlevel > 0)
		warning("Destination speficier omitted");
	}

	val = (val & 0xff) | (t != 0 ? 0x100 : 0);
	switch(op)
	{
        case KW_ADDWF:  gen_code(0x0700 | val); break;
        case KW_ADDWFC: gen_code(0x1000 | val); break;
	case KW_SUBWF:  gen_code(0x0400 | val); break;
	case KW_SUBWFB: gen_code(0x0200 | val); break;
	case KW_ANDWF:  gen_code(0x0a00 | val); break;
	case KW_IORWF:  gen_code(0x0800 | val); break;
	case KW_XORWF:  gen_code(0x0c00 | val); break;
	case KW_COMF:   gen_code(0x1200 | val); break;
	case KW_DECF:   gen_code(0x0600 | val); break;
	case KW_INCF:   gen_code(0x1400 | val); break;
	case KW_MOVFP:
	    /* XXX add check for PIC type - why? */
	    gen_code(0x6000 | val);
	    break;
	case KW_MOVPF:
	    /* XXX add check for PIC type - why? */
	    gen_code(0x4000 | val);
	    break;
	case KW_DECFSZ: gen_code(0x1600 | val); break;
	case KW_DCFSNZ: gen_code(0x2600 | val); break;
	case KW_INCFSZ: gen_code(0x1e00 | val); break;
	case KW_INFSNZ: gen_code(0x2400 | val); break;
	case KW_RLCF:   gen_code(0x1a00 | val); break;
	case KW_RRCF:   gen_code(0x1800 | val); break;
	case KW_RLNCF:  gen_code(0x2200 | val); break;
	case KW_RRNCF:  gen_code(0x2000 | val); break;
	case KW_SWAPF:  gen_code(0x1c00 | val); break;
	}
	break;

/********** XXX */
    case KW_MOVWF:
    case KW_MULWF:
    case KW_TSTFSZ:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Register file address out of range");

	switch(op)
	{
	case KW_MOVWF:  gen_code(0x0100 | val); break;
	case KW_MULWF:
	    if(pic_instr_set == PIC16BIT)
	    {
		gen_code(0x3400 | val);
	    }
	    else
	    {
		error(1, "Unimplemented instruction MULWF");
	    }
	    break;
	case KW_TSTFSZ: gen_code(0x3300 | val); break;
	}
	break;

/************ XXX */

    case KW_BCF:
    case KW_BSF:
    case KW_BTFSC:
    case KW_BTFSS:
    case KW_BTG:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Register file address out of range");

	if(token_type != TOK_COMMA)
	{
	    error(1, "',' expected");
	    return FAIL;
	}
	get_token();
	if(get_expression(&t) != OK)
	    return FAIL;

	if(t < 0 || t > 7)
	{
	    error(0, "Bit number out of range");
	}
	val |= (t << 8);
	switch(op)
	{
        case KW_BCF:   gen_code(0x8800 | val); break;
	case KW_BSF:   gen_code(0x8000 | val); break;
	case KW_BTFSC: gen_code(0x9800 | val); break;
	case KW_BTFSS: gen_code(0x9000 | val); break;
	case KW_BTG:   gen_code(0x3800 | val); break;
	}
	break;

    case KW_MOVFP:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Source register file address out of range");

	if(token_type != TOK_COMMA)
	{
	    error(1, "',' expected");
	    return FAIL;
	}
	get_token();
	if(get_expression(&t) != OK)
	    return FAIL;

	if(t < 0 || t > 0x1f)
	{
	    error(0, "Destination register file address out of range");
	}
	val |= (t << 8);
	gen_code(0x6000 | val);
	break;

    case KW_MOVPF:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0x1f)
	    error(0, "Source register file address out of range");

	if(token_type != TOK_COMMA)
	{
	    error(1, "',' expected");
	    return FAIL;
	}
	get_token();
	if(get_expression(&t) != OK)
	    return FAIL;

	if(t < 0 || t > 0xff)
	{
	    error(0, "Destination register file address out of range");
	}
	t |= (val << 8);
	gen_code(0x4000 | val);
	break;

    case KW_CLRF:	/* XXXX f,s */
    case KW_DAW:
    case KW_NEGW:
    case KW_SETF:
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Register file address out of range");

	t = 1;
	if(token_type == TOK_COMMA)
	{
	    get_token();
	    if(get_expression(&t) != OK)
		return FAIL;
	}
	else
	{
	    if(warnlevel > 0)
		warning("Destination speficier omitted");
	}
	val = (val & 0xff) | (t != 0 ? 0x100 : 0);

	switch(op)
	{
        case KW_CLRF: gen_code(0x2800 | val); break;
	case KW_DAW:  gen_code(0x2e00 | val); break;
	case KW_NEGW: gen_code(0x2c00 | val); break;
	case KW_SETF: gen_code(0x2a00 | val); break;
	}
	break;

    case KW_TLRD:	/* XXXX t,f */
    case KW_TLWT:
	if(get_expression(&t) != OK)
	    return FAIL;

	if(t < 0 || t > 1)
	    error(0, "Upper/lower byte specifier out of range");

	if(token_type != TOK_COMMA)
	{
	    error(1, "',' expected");
	    return FAIL;
	}
	get_token();
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Register file address out of range");

	val = (val & 0xff) | (t != 0 ? 0x200 : 0);

	switch(op)
	{
	case KW_TLRD: gen_code(0xa000 | val); break;
	case KW_TLWT: gen_code(0xa400 | val); break;
	}
	break;

    case KW_TABLRD:	/* XXXX t,i,f */
    case KW_TABLWT:
	if(get_expression(&t) != OK)
	    return FAIL;

	if(t < 0 || t > 1)
	    error(0, "Upper/lower byte specifier out of range");

	if(token_type != TOK_COMMA)
	{
	    error(1, "',' expected");
	    return FAIL;
	}
	get_token();
	if(get_expression(&i) != OK)
	    return FAIL;

	if(i < 0 || i > 1)
	    error(0, "Increment flag out of range");

	if(token_type != TOK_COMMA)
	{
	    error(1, "',' expected");
	    return FAIL;
	}
	get_token();
	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val > 0xff)
	    error(0, "Register file address out of range");

	val = (val & 0xff) | (t != 0 ? 0x200 : 0) | (i != 0 ? 0x200 : 0);
	switch(op)
	{
        case KW_TABLRD: gen_code(0xa800 | val); break;
	case KW_TABLWT: gen_code(0xac00 | val); break;
	}
	break;

    case KW_CALL:
    case KW_GOTO:
    case KW_LCALL:
	t = 0;
	if(token_type == TOK_IDENTIFIER || token_type == TOK_LOCAL_ID)
	{
	    symtype =
		(token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL);

	    if(symtype == SYMTAB_LOCAL && local_level == 0)
	    {
		error(1, "Local symbol outside a LOCAL block");
		return FAIL;
	    }

	    sym = lookup_symbol(token_string, symtype);
	    if(sym == NULL || sym->type == SYM_FORWARD)
	    {
		if(sym == NULL)
		{
		    sym = add_symbol(token_string, symtype);
		    sym->type = SYM_FORWARD;
		}

		val = 0;
		add_patch(symtype, sym, PATCH11);
		t = 1;
		get_token();
		goto gen_goto_call;
	    }
	}

	if(get_expression(&val) != OK)
	    return FAIL;

	if(val < 0 || val >= prog_mem_size)
	    error(0, "GOTO/CALL address out of range");

    gen_goto_call:
	val &= 0x1fff;
	switch(op)
	{
        case KW_CALL: gen_code(0xe000 | val); break;
	case KW_GOTO: gen_code(0xc000 | val); break;
	case KW_LCALL: gen_code(0xb700 | (val & 0xff)); break;
	}
	if(t)
	    list_flags |= LIST_FORWARD;
	break;

    case KW_TRIS:
	error(1, "Unimplemented instruction TRIS");
	return FAIL;
	break;

/*
 * RETLW allows multiple parameters/strings, for generating lookup tables
 */
    case KW_RETLW:
	for(;;)
	{
	    if(token_type == TOK_STRCONST)
	    {
		for(cp = token_string; *cp != '\0'; cp++)
		    gen_code(0xb600 | (int)((unsigned char)(*cp)));
		get_token();
	    }
	    else
	    {
		if(gen_byte_c(0xb600) != OK)
		    return FAIL;
	    }

	    if(token_type != TOK_COMMA)
		break;

	    get_token();
	}
	break;

    case KW_NOP:
	gen_code(0x0000);
	break;

    case KW_CLRW:
	gen_code(0x290a);	/* actually clrf 0x0a,1 */
	break;

    case KW_OPTION:
	error(1, "Unimplemented instruction OPTION");
	return FAIL;
	break;

    case KW_SLEEP:
	gen_code(0x0003);
	break;

    case KW_CLRWDT:
	gen_code(0x0004);
	break;

    case KW_RETFIE:
	gen_code(0x0005);
	break;

    case KW_RETURN:
	gen_code(0x0002);
	break;

    default:
	error(1, "Syntax error");
	return FAIL;
    }
    return OK;
}



syntax highlighted by Code2HTML, v. 0.9.1