X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?p=rmac;a=blobdiff_plain;f=dsp56k_amode.c;fp=dsp56k_amode.c;h=c5644cce93b2de09d79cb3a74e07e9c015ff7437;hp=0000000000000000000000000000000000000000;hb=30a208654896284b50e7b362e97d3e63ec717b96;hpb=790cd95291aa2dcfd3128652ef9c34bab1398a8d diff --git a/dsp56k_amode.c b/dsp56k_amode.c new file mode 100644 index 0000000..c5644cc --- /dev/null +++ b/dsp56k_amode.c @@ -0,0 +1,2806 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// AMODE.C - DSP 56001 Addressing Modes +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source utilised with the kind permission of Landon Dyer +// + +#include "dsp56k_amode.h" +#include "error.h" +#include "token.h" +#include "expr.h" +#include "rmac.h" +#include "procln.h" +#include "sect.h" +#include "math.h" + +#define DEF_KW +#include "kwtab.h" +#define DEF_MN +#include "mntab.h" + +// Address-mode information +int nmodes; // Number of addr'ing modes found +int dsp_am0; // Addressing mode +int dsp_a0reg; // Register +TOKEN dsp_a0expr[EXPRSIZE]; // Expression +uint64_t dsp_a0exval; // Expression's value +WORD dsp_a0exattr; // Expression's attribute +LONG dsp_a0memspace; // Addressing mode's memory space (P, X, Y) +SYM * dsp_a0esym; // External symbol involved in expr + +int dsp_am1; // Addressing mode +int dsp_a1reg; // Register +TOKEN dsp_a1expr[EXPRSIZE]; // Expression +uint64_t dsp_a1exval; // Expression's value +WORD dsp_a1exattr; // Expression's attribute +LONG dsp_a1memspace; // Addressing mode's memory space (P, X, Y) +SYM * dsp_a1esym; // External symbol involved in expr + +int dsp_am2; // Addressing mode +int dsp_a2reg; // Register +TOKEN dsp_a2expr[EXPRSIZE]; // Expression +uint64_t dsp_a2exval; // Expression's value +WORD dsp_a2exattr; // Expression's attribute +SYM * dsp_a2esym; // External symbol involved in expr + +int dsp_am3; // Addressing mode +int dsp_a3reg; // Register +TOKEN dsp_a3expr[EXPRSIZE]; // Expression +uint64_t dsp_a3exval; // Expression's value +WORD dsp_a3exattr; // Expression's attribute +SYM * dsp_a3esym; // External symbol involved in expr + +TOKEN dspImmedEXPR[EXPRSIZE]; // Expression +uint64_t dspImmedEXVAL; // Expression's value +WORD dspImmedEXATTR; // Expression's attribute +SYM * dspImmedESYM; // External symbol involved in expr +int deposit_extra_ea; // Optional effective address extension +TOKEN dspaaEXPR[EXPRSIZE]; // Expression +uint64_t dspaaEXVAL; // Expression's value +WORD dspaaEXATTR; // Expression's attribute +SYM * dspaaESYM; // External symbol involved in expr + +int dsp_k; // Multiplications sign + +static inline LONG checkea(const uint32_t termchar, const int strings); + +// ea checking error strings put into a table because I'm not sure there's an easy way to do otherwise +// (the messages start getting differerent in many places so it will get awkward to code those in) +// (I'd rather burn some RAM in order to have more helpful error messages than the other way round) + +#define X_ERRORS 0 +#define Y_ERRORS 1 +#define L_ERRORS 2 +#define P_ERRORS 3 + +const char *ea_errors[][12] = { + // X: + { + "unrecognised X: parallel move syntax: expected '(' after 'X:-'", // 0 + "unrecognised X: parallel move syntax: expected ')' after 'X:-(Rn'", // 1 + "unrecognised X: parallel move syntax: expected R0-R7 after 'X:-('", // 2 + "unrecognised X: parallel move syntax: expected N0-N7 after 'X:(Rn+'", // 3 + "unrecognised X: parallel move syntax: expected same register number in Rn and Nn for 'X:(Rn+Nn)'", // 4 + "unrecognised X: parallel move syntax: expected ')' after 'X:(Rn+Nn'", // 5 + "unrecognised X: parallel move syntax: expected same register number in Rn and Nn for 'X:(Rn)+Nn'", // 6 + "unrecognised X: parallel move syntax: expected N0-N7 after 'X:(Rn)+'", // 7 + "unrecognised X: parallel move syntax: expected same register number in Rn and Nn for 'X:(Rn)-Nn'", // 8 + "unrecognised X: parallel move syntax: expected N0-N7 after 'X:(Rn)-'", // 9 + "unrecognised X: parallel move syntax: expected '+', '-' or ',' after 'X:(Rn)'", // 10 + "unrecognised X: parallel move syntax: expected '+' or ')' after 'X:(Rn'", // 11 + }, + // Y: + { + "unrecognised Y: parallel move syntax: expected '(' after 'Y:-'", // 0 + "unrecognised Y: parallel move syntax: expected ')' after 'Y:-(Rn'", // 1 + "unrecognised Y: parallel move syntax: expected R0-R7 after 'Y:-('", // 2 + "unrecognised Y: parallel move syntax: expected N0-N7 after 'Y:(Rn+'", // 3 + "unrecognised Y: parallel move syntax: expected same register number in Rn and Nn for 'Y:(Rn+Nn)'", // 4 + "unrecognised Y: parallel move syntax: expected ')' after 'Y:(Rn+Nn'", // 5 + "unrecognised Y: parallel move syntax: expected same register number in Rn and Nn for 'Y:(Rn)+Nn'", // 6 + "unrecognised Y: parallel move syntax: expected N0-N7 after 'Y:(Rn)+'", // 7 + "unrecognised Y: parallel move syntax: expected same register number in Rn and Nn for 'Y:(Rn)-Nn'", // 8 + "unrecognised Y: parallel move syntax: expected N0-N7 after 'Y:(Rn)-'", // 9 + "unrecognised Y: parallel move syntax: expected '+', '-' or ',' after 'Y:(Rn)'", // 10 + "unrecognised Y: parallel move syntax: expected '+' or ')' after 'Y:(Rn'", // 11 + }, + // L: + { + "unrecognised L: parallel move syntax: expected '(' after 'L:-'", // 0 + "unrecognised L: parallel move syntax: expected ')' after 'L:-(Rn'", // 1 + "unrecognised L: parallel move syntax: expected R0-R7 after 'L:-('", // 2 + "unrecognised L: parallel move syntax: expected N0-N7 after 'L:(Rn+'", // 3 + "unrecognised L: parallel move syntax: expected same register number in Rn and Nn for 'L:(Rn+Nn)'", // 4 + "unrecognised L: parallel move syntax: expected ')' after 'L:(Rn+Nn'", // 5 + "unrecognised L: parallel move syntax: expected same register number in Rn and Nn for 'L:(Rn)+Nn'", // 6 + "unrecognised L: parallel move syntax: expected N0-N7 after 'L:(Rn)+'", // 7 + "unrecognised L: parallel move syntax: expected same register number in Rn and Nn for 'L:(Rn)-Nn'", // 8 + "unrecognised L: parallel move syntax: expected N0-N7 after 'L:(Rn)-'", // 9 + "unrecognised L: parallel move syntax: expected '+', '-' or ',' after 'L:(Rn)'", // 10 + "unrecognised L: parallel move syntax: expected '+' or ')' after 'L:(Rn'", // 11 + }, + // P: + { + "unrecognised P: effective address syntax: expected '(' after 'P:-'", // 0 + "unrecognised P: effective address syntax: expected ')' after 'P:-(Rn'", // 1 + "unrecognised P: effective address syntax: expected R0-R7 after 'P:-('", // 2 + "unrecognised P: effective address syntax: expected N0-N7 after 'P:(Rn+'", // 3 + "unrecognised P: effective address syntax: expected same register number in Rn and Nn for 'P:(Rn+Nn)'", // 4 + "unrecognised P: effective address syntax: expected ')' after 'P:(Rn+Nn'", // 5 + "unrecognised P: effective address syntax: expected same register number in Rn and Nn for 'P:(Rn)+Nn'", // 6 + "unrecognised P: effective address syntax: expected N0-N7 after 'P:(Rn)+'", // 7 + "unrecognised P: effective address syntax: expected same register number in Rn and Nn for 'P:(Rn)-Nn'", // 8 + "unrecognised P: effective address syntax: expected N0-N7 after 'P:(Rn)-'", // 9 + "unrecognised P: effective address syntax: expected '+', '-' or ',' after 'P:(Rn)'", // 10 + "unrecognised P: effective address syntax: expected '+' or ')' after 'P:(Rn'", // 11 + } +}; + +enum +{ + NUM_NORMAL = 0, + NUM_FORCE_LONG = 1, + NUM_FORCE_SHORT = 2 +}; + +// +// Parse a single addressing mode +// +static inline int dsp_parmode(int *am, int *areg, TOKEN * AnEXPR, uint64_t * AnEXVAL, WORD * AnEXATTR, SYM ** AnESYM, LONG *memspace, LONG *perspace, const int operand) +{ + if (*tok == KW_A || *tok == KW_B) + { + *am = M_ACC56; + *areg = *tok++; + return OK; + } + else if (*tok == '#') + { + tok++; + + if (*tok == '<') + { + // Immediate Short Addressing Mode Force Operator + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXVAL > 0xfff && *AnEXVAL<-4096) + return error("immediate short addressing mode forced but address is bigger than $fff"); + if ((int32_t)*AnEXVAL <= 0xff && (int32_t)*AnEXVAL>-0x100) + { + *am = M_DSPIM8; + return OK; + } + *am = M_DSPIM12; + return OK; + } + else if (*tok == '>') + { + // Immediate Long Addressing Mode Force Operator + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if ((int32_t)*AnEXVAL > 0xffffff || (int32_t)*AnEXVAL < -0xffffff) + return error("long immediate is bigger than $ffffff"); + *am = M_DSPIM; + return OK; + } + + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + if (*AnEXATTR & DEFINED) + { + if ((int32_t)*AnEXVAL < 0x100 && (int32_t)*AnEXVAL >= -0x100) + { + *AnEXVAL &= 0xff; + *am = M_DSPIM8; + } + else if (*AnEXVAL < 0x1000) + *am = M_DSPIM12; + else + *am = M_DSPIM; + } + else + { + // We have no clue what size our immediate will be + // so we have to assume the worst + *am = M_DSPIM; + } + return OK; + } + else if (*tok >= KW_X0 && *tok <= KW_Y1) + { + *am = M_ALU24; + *areg = *tok++; + return OK; + } + else if (*tok == KW_X && *(tok + 1) == ':') + { + tok = tok + 2; + if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + *memspace = 0 << 6; // Mark we're on X memory space + + // Check if value is between $ffc0 and $ffff, AKA X:pp + { + uint32_t temp = (LONG)(((int32_t)(((uint32_t)*AnEXVAL) << (32 - 6))) >> (32 - 6)); // Sign extend 6 to 32 bits + if ((temp >= 0xffffffc0 /* Check for 32bit sign extended number */ + && ((int32_t)*AnEXVAL<0)) /* Check if 32bit signed number is negative*/ + ||(*AnEXVAL<0xffff && *AnEXVAL>=0x8000)) /* Check if 16bit number is negative*/ + { + *AnEXVAL = temp; + *am = M_DSPPP; + *memspace = 0 << 6; // Mark we're on X memory space + *perspace = 0 << 16; // Mark we're on X peripheral space + *areg = *AnEXVAL & 0x3f; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + + } + + // If the symbol/expression is defined then check for valid range. + // Otherwise the value had better fit or Fixups will bark! + if (*AnEXVAL > 0x3f) + { + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + else + { + *am = M_DSPAA; + } + } + else + { + // Assume the worst + *memspace = 0 << 6; // Mark we're on X memory space + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + return OK; + } + else if (*tok == '<') + { + // X:aa + // Short Addressing Mode Force Operator in the case of '<' + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + // If the symbol/expression is defined then check for valid range. + // Otherwise the value had better fit or Fixups will bark! + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0x3f) + return error("short addressing mode forced but address is bigger than $3f"); + } + else + { + // Mark it as a fixup + deposit_extra_ea = DEPOSIT_EXTRA_FIXUP; + } + *am = M_DSPAA; + *memspace = 0 << 6; // Mark we're on X memory space + *areg = (int)*AnEXVAL; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + else if (*tok == '>') + { + // Long Addressing Mode Force Operator + tok++; + + if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXATTR&DEFINED) + { + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + *memspace = 0 << 6; // Mark we're on X memory space + + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + else + { + // Assume the worst + *memspace = 0 << 6; // Mark we're on X memory space + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + return OK; + } + } + else if (*tok == SHL) // '<<' + { + // I/O Short Addressing Mode Force Operator + // X:pp + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + // If the symbol/expression is defined then check for valid range. + // Otherwise the value had better fit or Fixups will bark! + if (*AnEXATTR & DEFINED) + { + *AnEXVAL = (LONG)(((int32_t)(((uint32_t)*AnEXVAL) << (32 - 6))) >> (32 - 6)); // Sign extend 6 to 32 bits + if (*AnEXVAL < 0xffffffc0) + return error("I/O Short Addressing Mode addresses must be between $ffc0 and $ffff"); + } + *am = M_DSPPP; + *memspace = 0 << 6; // Mark we're on X memory space + *perspace = 0 << 16; // Mark we're on X peripheral space + *areg = *AnEXVAL & 0x3f; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + + if ((*areg = checkea(0, X_ERRORS)) != ERROR) + { + // TODO: what if we need M_DSPAA here???? + *memspace = 0 << 6; // Mark we're on X memory space + *am = M_DSPEA; + return OK; + } + else + return ERROR; + + } + else if (*tok == KW_Y && *(tok + 1) == ':') + { + tok = tok + 2; + + if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + *memspace = 1 << 6; // Mark we're on Y memory space + + // Check if value is between $ffc0 and $ffff, AKA Y:pp + { + uint32_t temp = (LONG)(((int32_t)(((uint32_t)*AnEXVAL) << (32 - 6))) >> (32 - 6)); // Sign extend 6 to 32 bits + if ((temp >= 0xffffffc0 /* Check for 32bit sign extended number */ + && ((int32_t)*AnEXVAL<0)) /* Check if 32bit signed number is negative*/ + || (*AnEXVAL<0xffff && *AnEXVAL >= 0x8000)) /* Check if 16bit number is negative*/ + { + *AnEXVAL = temp; + *am = M_DSPPP; + *perspace = 1 << 16; // Mark we're on X peripheral space + *areg = *AnEXVAL & 0x3f; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + + } + + // If the symbol/expression is defined then check for valid range. + // Otherwise the value had better fit or Fixups will bark! + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0x3f) + { + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + else + { + *am = M_DSPAA; + } + } + else + { + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + + return OK; + } + else if (*tok == '<') + { + // Y:aa + // Short Addressing Mode Force Operator in the case of '<' + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + // If the symbol/expression is defined then check for valid range. + // Otherwise the value had better fit or Fixups will bark! + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0x3f) + { + warn("short addressing mode forced but address is bigger than $3f - switching to long"); + *am = M_DSPEA; + *memspace = 1 << 6; // Mark we're on Y memory space + *areg = DSP_EA_ABS; + return OK; + } + } + else + { + // Mark it as a fixup + deposit_extra_ea = DEPOSIT_EXTRA_FIXUP; + } + + *am = M_DSPAA; + *memspace = 1 << 6; // Mark we're on Y memory space + *areg = (int)*AnEXVAL; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + else if (*tok == '>') + { + // Long Addressing Mode Force Operator + tok++; + + if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXATTR&DEFINED) + { + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + *memspace = 1 << 6; // Mark we're on Y memory space + + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + else + { + // Assume the worst + *memspace = 1 << 6; // Mark we're on Y memory space + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + return OK; + } + } + else if (*tok == SHL) // '<<' + { + // I/O Short Addressing Mode Force Operator + // Y:pp + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + // If the symbol/expression is defined then check for valid range. + // Otherwise the value had better fit or Fixups will bark! + if (*AnEXATTR & DEFINED) + { + *AnEXVAL = (LONG)(((int32_t)(((uint32_t)*AnEXVAL) << (32 - 6))) >> (32 - 6)); // Sign extend 6 to 32 bits + if (*AnEXVAL < 0xffffffc0) + return error("I/O Short Addressing Mode addresses must be between $ffe0 and $1f"); + } + *am = M_DSPPP; + *memspace = 1 << 6; // Mark we're on Y memory space + *perspace = 1 << 16; // Mark we're on Y peripheral space + *areg = *AnEXVAL & 0x3f; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + + if ((*areg = checkea(0, X_ERRORS)) != ERROR) + { + *memspace = 1 << 6; // Mark we're on Y memory space + *am = M_DSPEA; + return OK; + } + else + return ERROR; + // TODO: add absolute address checks + + } + else if (*tok >= KW_X&&*tok <= KW_Y) + { + *am = M_INP48; + *areg = *tok++; + return OK; + } + else if (*tok >= KW_M0 && *tok <= KW_M7) + { + *am = M_DSPM; + *areg = (*tok++) & 7; + return OK; + } + else if (*tok >= KW_R0 && *tok <= KW_R7) + { + *am = M_DSPR; + *areg = (*tok++) - KW_R0; + return OK; + } + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + *am = M_DSPN; + *areg = (*tok++) & 7; + return OK; + } + else if (*tok == KW_A0 || *tok == KW_A1 || *tok == KW_B0 || *tok == KW_B1) + { + *am = M_ACC24; + *areg = *tok++; + return OK; + } + else if (*tok == KW_A2 || *tok == KW_B2) + { + *am = M_ACC8; + *areg = *tok++; + return OK; + } + else if (*tok == '-' && (*(tok + 1) == KW_X0 || *(tok + 1) == KW_X1 || *(tok + 1) == KW_Y0 || *(tok + 1) == KW_Y1)) + { + // '-X0', '-Y0', '-X1' or '-Y1', used in multiplications + tok++; + + // Check to see if this is the first operand + if (operand != 0) + return error("-x0/-x1/-y0/-y1 only allowed in the first operand"); + + *am = M_ALU24; + *areg = *tok++; + dsp_k = 1 << 2; + return OK; + } + else if (*tok == '+' && (*(tok + 1) == KW_X0 || *(tok + 1) == KW_X1 || *(tok + 1) == KW_Y0 || *(tok + 1) == KW_Y1)) + { + // '+X0', '+Y0', '+X1' or '+Y1', used in multiplications + tok++; + + // Check to see if this is the first operand + if (operand != 0) + return error("+x0/+x1/+y0/+y1 only allowed in the first operand"); + + *am = M_ALU24; + *areg = *tok++; + dsp_k = 0 << 2; + return OK; + } + else if (*tok == '(' || *tok == '-') + { + // Could be either an expression or ea mode + if (*tok + 1 == SYMBOL) + { + tok++; + + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + *am = M_DSPIM; + return OK; + } + if ((*areg = checkea(0, P_ERRORS)) != ERROR) + { + *am = M_DSPEA; + return OK; + } + else + return ERROR; + // TODO: add absolute address checks + return error("internal assembler error: parmode checking for '(' and '-' does not have absolute address checks yet!"); + } + else if (*tok == KW_P && *(tok + 1) == ':') + { + tok = tok + 2; + if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + // Address + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + if (*AnEXVAL > 0x3f) + { + *am = M_DSPEA; + *areg = DSP_EA_ABS; + } + else + { + *areg = (int)*AnEXVAL; // Lame, but what the hell + *am = M_DSPAA; + } + return OK; + } + else if (*tok == '<') + { + // X:aa + // Short Addressing Mode Force Operator in the case of '<' + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXVAL > 0x3f) + return error("short addressing mode forced but address is bigger than $3f"); + *am = M_DSPAA; + *areg = (int)*AnEXVAL; // Since this is only going to get used in dsp_ea_imm5... + return OK; + } + else if (*tok == '>') + { + // Long Addressing Mode Force Operator + tok++; + // Immediate Short Addressing Mode Force Operator + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + } + *am = M_DSPEA; + *areg = DSP_EA_ABS; + return OK; + } + + if ((*areg = checkea(0, P_ERRORS)) != ERROR) + { + *am = M_DSPEA; + return OK; + } + else + return ERROR; + + } + else if (*tok == SHL) + { + // I/O Short Addressing Mode Force Operator + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXVAL > 0xfff) + return error("I/O short addressing mode forced but address is bigger than $fff"); + *am = M_DSPABS06; + return OK; + } + else if (*tok == '<') + { + // Short Addressing Mode Force Operator + tok++; + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0xfff) + return error("short addressing mode forced but address is bigger than $fff"); + } + *am = M_DSPABS12; + return OK; + } + else if (*tok == '>') + { + // Long Addressing Mode Force Operator + tok++; + // Immediate Short Addressing Mode Force Operator + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + if (*AnEXATTR & DEFINED) + { + if (*AnEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + } + *am = M_DSPEA; + *areg = DSP_EA_ABS; + return OK; + } + + else if (*tok == KW_PC || *tok == KW_CCR || *tok == KW_SR || *tok == KW_SP || (*tok >= KW_MR&&*tok <= KW_SS)) + { + *areg = *tok++; + *am = M_DSPPCU; + return OK; + } + // expr + else + { + if (expr(AnEXPR, AnEXVAL, AnEXATTR, AnESYM) != OK) + return ERROR; + + // We'll store M_DSPEA_ABS in areg and if we have + // any extra info, it'll go in am + if (*AnEXATTR & DEFINED) + { + *areg = DSP_EA_ABS; + if (*AnEXVAL < 0x1000) + *am = M_DSPABS12; + else if (*AnEXVAL < 0x10000) + *am = M_DSPABS16; + else if (*AnEXVAL < 0x1000000) + *am = M_DSPABS24; + else + return error("address must be smaller than $1000000"); + return OK; + } + else + { + // Well, we have no opinion on the expression's size, so let's assume the worst + *areg = DSP_EA_ABS; + *am = M_DSPABS24; + return OK; + } + } + return error("internal assembler error: Please report this error message: 'reached the end of dsp_parmode' with the line of code that caused it. Thanks, and sorry for the inconvenience"); // Something bad happened +} + +// +// Parse all addressing modes except parallel moves +// +int dsp_amode(int maxea) +{ + LONG dummy; + // Initialize global return values + nmodes = dsp_a0reg = dsp_a1reg = 0; + dsp_am0 = dsp_am1 = M_AM_NONE; + dsp_a0expr[0] = dsp_a1expr[0] = ENDEXPR; + dsp_a0exval = 0; + dsp_a1exval = 0; + dsp_a0exattr = dsp_a1exattr = 0; + dsp_a0esym = dsp_a1esym = (SYM *)NULL; + dsp_a0memspace = dsp_a1memspace = -1; + dsp_a0perspace = dsp_a1perspace = -1; + dsp_k = 0; + + // If at EOL, then no addr modes at all + if (*tok == EOL) + return 0; + + if (dsp_parmode(&dsp_am0, &dsp_a0reg, dsp_a0expr, &dsp_a0exval, &dsp_a0exattr, &dsp_a0esym, &dsp_a0memspace, &dsp_a0perspace, 0) == ERROR) + return ERROR; + + + // If caller wants only one mode, return just one (ignore comma); + // If there is no second addressing mode (no comma), then return just one anyway. + nmodes = 1; + + if (*tok != ',') + { + if (dsp_k != 0) + return error("-x0/-x1/-y0/-y1 only allowed in multiply operations"); + + return 1; + } + + // Eat the comma + tok++; + + // Parse second addressing mode + if (dsp_parmode(&dsp_am1, &dsp_a1reg, dsp_a1expr, &dsp_a1exval, &dsp_a1exattr, &dsp_a1esym, &dsp_a1memspace, &dsp_a1perspace, 1) == ERROR) + return ERROR; + + if (maxea == 2 || *tok == EOL) + { + if (dsp_k != 0) + return error("-x0/-x1/-y0/-y1 only allowed in multiply operations"); + + nmodes = 2; + return 2; + } + + if (*tok == ',') + { + // Only MAC-like or jsset/clr/tst/chg instructions here + tok++; + if (dsp_parmode(&dsp_am2, &dsp_a2reg, dsp_a2expr, &dsp_a2exval, &dsp_a2exattr, &dsp_a2esym, &dummy, &dummy, 2) == ERROR) + return ERROR; + if (maxea == 3) + return 3; + if (*tok != EOL) + return error(extra_stuff); + nmodes = 3; + return 3; + + } + + // Only Tcc instructions here, and then only those that accept 4 operands + + if (dsp_parmode(&dsp_am2, &dsp_a2reg, dsp_a2expr, &dsp_a2exval, &dsp_a2exattr, &dsp_a2esym, &dummy, &dummy, 2) == ERROR) + return ERROR; + + if (*tok++ != ',') + return error("expected 4 parameters"); + + if (dsp_parmode(&dsp_am3, &dsp_a3reg, dsp_a3expr, &dsp_a3exval, &dsp_a3exattr, &dsp_a3esym, &dummy, &dummy, 3) == ERROR) + return ERROR; + + if (*tok == EOL) + { + if (dsp_k != 0) + return error("-x0/-x1/-y0/-y1 only allowed in multiply operations"); + + nmodes = 4; + return 4; + } + else + { + // Tcc instructions do not support parallel moves, so any remaining tokens are garbage + return error(extra_stuff); + } + + return error("internal assembler error: Please report this error message: 'reached the end of dsp_amode' with the line of code that caused it. Thanks, and sorry for the inconvenience"); //Something bad happened + +} + + +// +// Helper function which gives us the encoding of a DSP register +// +static inline int SDreg(int reg) +{ + if (reg >= KW_X0 && reg <= KW_N7) + return reg & 0xff; + else if (reg >= KW_A0&® <= KW_A2) + return (8 >> (reg & 7)) | 8; + else //if (reg>=KW_R0&®<=KW_R7) + return reg - KW_R0 + 16; + // Handy map for the above: + // (values are of course taken from keytab) + // Register | Value | Return value + // x0 | 260 | 4 + // x1 | 261 | 5 + // y0 | 262 | 6 + // y1 | 263 | 7 + // b0 | 265 | 8 + // b2 | 267 | 9 + // b1 | 269 | 10 + // a | 270 | 14 + // b | 271 | 15 + // n0 | 280 | 24 + // n1 | 281 | 25 + // n2 | 282 | 26 + // n3 | 283 | 27 + // n4 | 284 | 28 + // n5 | 285 | 29 + // n6 | 286 | 30 + // n7 | 287 | 31 + // a0 | 136 | 0 + // a1 | 137 | 1 + // a2 | 138 | 2 + // r0 | 151 | 16 + // r1 | 152 | 17 + // r2 | 153 | 18 + // r3 | 154 | 19 + // r4 | 155 | 20 + // r5 | 156 | 21 + // r6 | 157 | 22 + // r7 | 158 | 23 +} + + +// +// Check for X:Y: parallel mode syntax +// +static inline LONG check_x_y(LONG ea1, LONG S1) +{ + LONG inst; + LONG eax_temp, eay_temp; + LONG D1, D2, S2, ea2; + LONG K_D1, K_D2; + LONG w = 1 << 7; // S1=0, D1=1<<14 + if ((ea1 & 0x38) == DSP_EA_POSTINC || (ea1 & 0x38) == DSP_EA_POSTINC1 || + (ea1 & 0x38) == DSP_EA_POSTDEC1 || (ea1 & 0x38) == DSP_EA_NOUPD) + { + + switch (ea1 & 0x38) + { + case DSP_EA_POSTINC: ea1 = (ea1&(~0x38)) | 0x8;break; + case DSP_EA_POSTINC1: ea1 = (ea1&(~0x38)) | 0x18;break; + case DSP_EA_POSTDEC1: ea1 = (ea1&(~0x38)) | 0x10;break; + case DSP_EA_NOUPD: ea1 = (ea1&(~0x38)) | 0x00;break; + } + + if (S1 == 0) + { + // 'X:eax,D1 Y:eay,D2', 'X:eax,D1 S2,Y:eay' + // Check for D1 + switch (K_D1 = *tok++) + { + case KW_X0: D1 = 0 << 10; break; + case KW_X1: D1 = 1 << 10; break; + case KW_A: D1 = 2 << 10; break; + case KW_B: D1 = 3 << 10; break; + default: return error("unrecognised X:Y: parallel move syntax: expected x0, x1, a or b after 'X:eax,'"); + } + } + else + { + // 'S1,X:eax Y:eay,D2' 'S1,X:eax S2,Y:eay' + w = 0; + switch (S1) + { + case 4: D1 = 0 << 10; break; + case 5: D1 = 1 << 10; break; + case 14: D1 = 2 << 10; break; + case 15: D1 = 3 << 10; break; + default: return error("unrecognised X:Y: parallel move syntax: S1 can only be x0, x1, a or b in 'S1,X:eax'"); + } + } + + if (*tok == KW_Y) + { + tok++; + // 'X:eax,D1 Y:eay,D2' 'S1,X:eax Y:eay,D2' + if (*tok++ != ':') + return error("unrecognised X:Y: parallel move syntax: expected ':' after 'X:ea,D1/S1,X:ea Y'"); + if (*tok++ == '(') + { + if (*tok >= KW_R0 && *tok <= KW_R7) + { + ea2 = (*tok++ - KW_R0); + if (((ea1 & 7) < 4 && ea2 < 4) || ((ea1 & 7) >= 4 && ea2 > 4)) + return error("unrecognised X:Y: parallel move syntax: eax and eay register banks must be different in 'X:ea,D1/S1,X:ea Y:eay,D2'"); + } + else + return error("unrecognised X:Y: parallel move syntax: expected 'Rn' after 'X:ea,D1/S1,X:ea Y:('"); + // If eax register is r0-r3 then eay register is r4-r7. + // Encode that to 2 bits (i.e. eay value is 0-3) + eax_temp = (ea2 & 3) << 5; // Store register temporarily + if (*tok++ != ')') + return error("unrecognised X:Y: parallel move syntax: expected ')' after 'X:ea,D1/S1,X:ea Y:(Rn'"); + if (*tok == '+') + { + tok++; + if (*tok == ',') + { + // (Rn)+ + ea2 = 3 << 12; + tok++; + } + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)+Nn + if ((*tok++ & 7) != ea2) + return error("unrecognised X:Y: parallel move syntax(Same register number expected for Rn, Nn in 'X:ea,D1/S1,X:ea Y:(Rn)+Nn,D')"); + ea2 = 1 << 12; + if (*tok++ != ',') + return error("unrecognised X:Y: parallel move syntax: expected ',' after 'X:ea,D1/S1,X:ea Y:(Rn)+Nn'"); + } + else + return error("unrecognised X:Y: parallel move syntax: expected ',' or 'Nn' after 'X:ea,D1/S1,X:ea Y:(Rn)+'"); + + } + else if (*tok == '-') + { + // (Rn)- + ea2 = 2 << 12; + tok++; + if (*tok++ != ',') + return error("unrecognised X:Y: parallel move syntax: expected ',' after 'X:ea,D1/S1,X:ea Y:(Rn)-'"); + } + else if (*tok++ == ',') + { + // (Rn) + ea2 = 0 << 12; + } + else + return error("unrecognised X:Y: parallel move syntax: expected ',' after 'X:ea,D1/S1,X:ea Y:eay'"); + + ea2 |= eax_temp; // OR eay back from temp + + switch (K_D2 = *tok++) + { + case KW_Y0: D2 = 0 << 8; break; + case KW_Y1: D2 = 1 << 8; break; + case KW_A: D2 = 2 << 8; break; + case KW_B: D2 = 3 << 8; break; + default: return error("unrecognised X:Y: parallel move syntax: expected y0, y1, a or b after 'X:ea,D1/S1,X:ea Y:eay,'"); + } + + if (*tok != EOL) + return error("unrecognised X:Y: parallel move syntax: expected end-of-line after 'X:ea,D1/S1,X:ea Y:eay,D'"); + + if (S1 == 0) + if (K_D1 == K_D2) + return error("unrecognised X:Y: parallel move syntax: D1 and D2 cannot be the same in 'X:ea,D1 Y:eay,D2'"); + + inst = B16(11000000, 00000000) | w; + inst |= ea1 | D1 | ea2 | D2; + return inst; + } + else + return error("unrecognised X:Y: parallel move syntax: expected '(Rn)', '(Rn)+', '(Rn)-', '(Rn)+Nn' after 'X:ea,D1/S1,X:ea Y:'"); + } + else if (*tok == KW_Y0 || *tok == KW_Y1 || *tok == KW_A || *tok == KW_B) + { + // 'X:eax,D1 S2,Y:eay' 'S1,X:eax1 S2,Y:eay' + switch (*tok++) + { + case KW_Y0: S2 = 0 << 8; break; + case KW_Y1: S2 = 1 << 8; break; + case KW_A: S2 = 2 << 8; break; + case KW_B: S2 = 3 << 8; break; + default: return error("unrecognised X:Y: parallel move syntax: expected y0, y1, a or b after 'X:ea,D1/S1,X:ea Y:eay,'"); + } + + if (*tok++ != ',') + return error("unrecognised X:Y: parallel move syntax: expected ',' after 'X:ea,D1/S1,X:ea S2'"); + + if (*tok++ == KW_Y) + { + // 'X:eax,D1 Y:eay,D2' 'S1,X:eax Y:eay,D2' + if (*tok++ != ':') + return error("unrecognised X:Y: parallel move syntax: expected ':' after 'X:ea,D1/S1,X:ea Y'"); + if (*tok++ == '(') + { + if (*tok >= KW_R0 && *tok <= KW_R7) + { + ea2 = (*tok++ - KW_R0); + if (((ea1 & 7) < 4 && ea2 < 4) || ((ea1 & 7) >= 4 && ea2 > 4)) + return error("unrecognised X:Y: parallel move syntax: eax and eay register banks must be different in 'X:ea,D1/S1,X:ea S2,Y:eay'"); + } + else + return error("unrecognised X:Y: parallel move syntax: expected 'Rn' after 'X:ea,D1/S1,X:ea S2,Y:('"); + // If eax register is r0-r3 then eay register is r4-r7. + // Encode that to 2 bits (i.e. eay value is 0-3) + eay_temp = (ea2 & 3) << 5; //Store register temporarily + if (*tok++ != ')') + return error("unrecognised X:Y: parallel move syntax: expected ')' after 'X:ea,D1/S1,X:ea S2,Y:(Rn'"); + if (*tok == '+') + { + tok++; + if (*tok == EOL) + // (Rn)+ + ea2 = 3 << 12; + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)+Nn + if ((*tok++ & 7) != ea2) + return error("unrecognised X:Y: parallel move syntax(Same register number expected for Rn, Nn in 'X:ea,D1/S1,X:ea S2,Y:(Rn)+Nn')"); + ea2 = 1 << 12; + if (*tok != EOL) + return error("unrecognised X:Y: parallel move syntax: expected end-of-line after 'X:ea,D1/S1,X:ea S2,Y:(Rn)+Nn'"); + } + else + return error("unrecognised X:Y: parallel move syntax: expected ',' or 'Nn' after 'X:ea,D1/S1,X:ea S2,Y:(Rn)+'"); + + } + else if (*tok == '-') + { + // (Rn)- + ea2 = 2 << 12; + tok++; + if (*tok != EOL) + return error("unrecognised X:Y: parallel move syntax: expected end-of-line after 'X:ea,D1/S1,X:ea S2,Y:(Rn)-'"); + } + else if (*tok == EOL) + { + // (Rn) + ea2 = 0 << 12; + } + else + return error("unrecognised X:Y: parallel move syntax: expected end-of-line after 'X:ea,D1/S1,X:ea S2,Y:eay'"); + + ea2 |= eay_temp; //OR eay back from temp + + inst = B16(10000000, 00000000) | w; + inst |= (ea1 & 0x1f) | D1 | S2 | ea2; + return inst; + } + else + return error("unrecognised X:Y: parallel move syntax: expected '(Rn)', '(Rn)+', '(Rn)-', '(Rn)+Nn' after 'X:ea,D1/S1,X:ea Y:'"); + + } + else + return error("unrecognised X:Y: parallel move syntax: expected '(Rn)', '(Rn)+', '(Rn)-', '(Rn)+Nn' in 'X:ea,D1/S1,X:ea'"); + } + else + return error("unrecognised X:Y: or X:R parallel move syntax: expected Y:, A or B after 'X:ea,D1/S1,X:ea S2,'"); + + + } + return error("unrecognised X:Y: parallel move syntax: expected '(Rn)', '(Rn)+', '(Rn)-', '(Rn)+Nn' in 'X:ea,D1/S1,X:ea'"); +} + +// +// Parse X: addressing space parallel moves +// +static inline LONG parse_x(const int W, LONG inst, const LONG S1, const int check_for_x_y) +{ + int immreg; // Immediate register destination + LONG S2, D1, D2; // Source and Destinations + LONG ea1; // ea bitfields + uint32_t termchar = ','; // Termination character for ea checks + int force_imm = NUM_NORMAL; // Holds forced immediate value (i.e. '<' or '>') + ea1 = -1; // initialise e1 (useful for some code paths) + if (W == 0) + termchar = EOL; + if (*tok == '-') + { + if (tok[1] == CONST || tok[1] == FCONST) + { + tok++; + dspImmedEXVAL = *tok++; + goto x_check_immed; + } + // This could be either -(Rn), -aa or -ea. Check for immediate first + if (tok[1] == SYMBOL) + { + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) == OK) + { + if (S1 != 0) + { + x_checkea_right: + + // 'S1,X:ea S2,D2', 'A,X:ea X0,A', 'B,X:ea X0,B', 'S1,X:eax Y:eay,D2', 'S1,X:eax S2,Y:eay' + if (*tok == KW_X0 && tok[1] == ',' && tok[2] == KW_A) + { + // 'A,X:ea X0,A' + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + if (S1 != 14) + return error("unrecognised X:R parallel move syntax: S1 can only be a in 'a,X:ea x0,a'"); + if (ea1 == -1) + return error("unrecognised X:R parallel move syntax: absolute address not allowed in 'a,X:ea x0,a'"); + if (ea1 == B8(00110100)) + return error("unrecognised X:R parallel move syntax: immediate data not allowed in 'a,X:ea x0,a'"); + inst = B16(00001000, 00000000) | ea1 | (0 << 8); + return inst; + } + else if (*tok == KW_X0 && tok[1] == ',' && tok[2] == KW_B) + { + // 'B,X:ea X0,B' + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + if (S1 != 15) + return error("unrecognised X:R parallel move syntax: S1 can only be b in 'b,X:ea x0,b'"); + if (ea1 == -1) + return error("unrecognised X:R parallel move syntax: absolute address not allowed in 'b,X:ea x0,b'"); + if (ea1 == B8(00110100)) + return error("unrecognised X:R parallel move syntax: immediate data not allowed in 'b,X:ea x0,b'"); + inst = B16(00001001, 00000000) | ea1 | (1 << 8); + return inst; + } + else if (*tok == KW_A || *tok == KW_B) + { + // 'S1,X:ea S2,D2', 'S1,X:eax S2,Y:eay' + + switch (S1) + { + case 4: D1 = 0 << 10; break; + case 5: D1 = 1 << 10; break; + case 14: D1 = 2 << 10; break; + case 15: D1 = 3 << 10; break; + default: return error("unrecognised X:R parallel move syntax: S1 can only be x0, x1, a or b in 'S1,X:ea S2,D2'"); + } + + if (tok[1] == ',' && tok[2] == KW_Y) + { + // 'S1,X:eax S2,Y:eay' + return check_x_y(ea1, S1); + } + + // 'S1,X:ea S2,D2' + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + switch (*tok++) + { + case KW_A: S2 = 0 << 9; break; + case KW_B: S2 = 1 << 9; break; + default: return error("unrecognised X:R parallel move syntax: expected a or b after 'S1,X:eax'"); + } + if (*tok++ != ',') + return error("unrecognised X:R parallel move syntax: expected ',' after 'S1,X:eax S2'"); + + if (*tok == KW_Y0 || *tok == KW_Y1) + { + if (*tok++ == KW_Y0) + D2 = 0 << 8; + else + D2 = 1 << 8; + + if (*tok != EOL) + return error("unrecognised X:R parallel move syntax: unexpected text after 'X:eax,D1 S2,S2'"); + + inst = B16(00010000, 00000000) | (0 << 7); + inst |= ea1 | D1 | S2 | D2; + return inst; + } + else + return error("unrecognised X:R parallel move syntax: expected y0 or y1 after 'X:eax,D1 S2,'"); + + } + else if (*tok == KW_Y) + { + // 'S1,X:eax Y:eay,D2' + return check_x_y(ea1, S1); + } + else if (*tok == KW_Y0 || *tok == KW_Y1) + { + // 'S1,X:eax S2,Y:eay' + return check_x_y(ea1, S1); + } + else + return error("unrecognised X:Y or X:R parallel move syntax: expected y0 or y1 after 'X:eax,D1/X:ea,D1"); + } + else + { + // Only check for aa if we have a defined number in our hands or we've + // been asked to use a short number format. The former case we'll just test + // it to see if it's small enough. The later - it's the programmer's call + // so he'd better have a small address or the fixups will bite him/her in the arse! + if (dspImmedEXATTR&DEFINED || force_imm == NUM_FORCE_SHORT) + { + + // It's an immediate, so ea or eax is probably an absolute address + // (unless it's aa if the immediate is small enough) + // 'X:ea,D' or 'X:aa,D' or 'X:ea,D1 S2,D2' or 'X:eax,D1 Y:eay,D2' or 'X:eax,D1 S2,Y:eay' + x_check_immed: + // Check for aa (which is 6 bits zero extended) + if (dspImmedEXVAL < 0x40 && force_imm != NUM_FORCE_LONG) + { + if (W == 1) + { + // It might be X:aa but we're not 100% sure yet. + // If it is, the only possible syntax here is 'X:aa,D'. + // So check ahead to see if EOL follows D, then we're good to go. + if (*tok == ',' && ((*(tok + 1) >= KW_X0 && *(tok + 1) <= KW_N7) || (*(tok + 1) >= KW_R0 && *(tok + 1) <= KW_R7) || (*(tok + 1) >= KW_A0 && *(tok + 1) <= KW_A2)) && *(tok + 2) == EOL) + { + // Yup, we're good to go - 'X:aa,D' it is + tok++; + immreg = SDreg(*tok++); + inst = inst | (uint32_t)dspImmedEXVAL; + inst |= ((immreg & 0x18) << (12 - 3)) + ((immreg & 7) << 8); + inst |= 1 << 7; // W + return inst; + } + } + else + { + if (*tok == EOL) + { + // 'S,X:aa' + inst = inst | (uint32_t)dspImmedEXVAL; + inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); + return inst; + } + else + { + // 'S1,X:ea S2,D2', 'A,X:ea X0,A', 'B,X:ea X0,B', + // 'S1,X:eax Y:eay,D2', 'S1,X:eax S2,Y:eay' + ea1 = DSP_EA_ABS; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + goto x_checkea_right; + } + } + } + } + + // Well, that settles it - we do have an ea in our hands + if (W == 1) + { + // 'X:ea,D' [... S2,d2] + if (*tok++ != ',') + return error("unrecognised X: parallel move syntax: expected ',' after 'X:ea'"); + if ((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2)) + { + D1 = SDreg(*tok++); + if (*tok == EOL) + { + // 'X:ea,D' + inst = inst | B8(01000000) | (1 << 7); + inst |= ((D1 & 0x18) << (12 - 3)) + ((D1 & 7) << 8); + inst |= ea1; + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + return inst; + } + else + { + // 'X:ea,D1 S2,D2' + if (*tok == KW_A || *tok == KW_B) + { + S2 = SDreg(*tok++); + if (*tok++ != ',') + return error("unrecognised X:R parallel move syntax: expected comma after X:ea,D1 S2"); + if (*tok == KW_Y0 || *tok == KW_Y1) + { + D2 = SDreg(*tok++); + if (*tok != EOL) + return error("unrecognised X:R parallel move syntax: expected EOL after X:ea,D1 S2,D2"); + inst = B16(00010000, 00000000) | (1 << 7); + inst |= ((D1 & 0x8) << (12 - 4)) + ((D1 & 1) << 10); + inst |= (S2 & 1) << 9; + inst |= (D2 & 1) << 8; + inst |= ea1; + return inst; + } + else + return error("unrecognised X:R parallel move syntax: expected y0,y1 after X:ea,D1 S2,"); + } + else + return error("unrecognised X:R parallel move syntax: expected a,b after X:ea,D1"); + } + } + else + return error("unrecognised X: parallel move syntax: expected x0,x1,y0,y1,a0,b0,a2,b2,a1,b1,a,b,r0-r7,n0-n7 after 'X:ea,'"); + } + else + { + if (*tok == EOL) + { + // 'S,X:ea' + inst = inst | B8(01000000) | (0 << 7); + inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); + inst |= ea1; + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + return inst; + } + else + { + // 'S1,X:ea S2,D2', 'A,X:ea X0,A', 'B,X:ea X0,B', + // 'S1,X:eax Y:eay,D2', 'S1,X:eax S2,Y:eay' + goto x_checkea_right; + } + } + + } + } + } + else + { + // It's not an immediate, check for '-(Rn)' + ea1 = checkea(termchar, X_ERRORS); + + if (ea1 == ERROR) + return ERROR; + + goto x_gotea1; + + } + } + else if (*tok == '#') + { + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + // Okay so we have immediate data - mark it down + ea1 = DSP_EA_IMM; + // Now, proceed to the main code for this branch + goto x_gotea1; + } + else if (*tok == '(') + { + // Maybe we got an expression here, check for it + if (tok[1] == CONST || tok[1] == FCONST || tok[1] == SUNARY || tok[1] == SYMBOL || tok[1] == STRING) + { + // Evaluate the expression and go to immediate code path + expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM); + goto x_check_immed; + } + + // Nope, let's check for ea then + ea1 = checkea(termchar, X_ERRORS); + if (ea1 == ERROR) + return ERROR; + + x_gotea1: + if (W == 1) + { + if (*tok++ != ',') + return error("Comma expected after 'X:(Rn)')"); + + // It might be 'X:(Rn..)..,D' but we're not 100% sure yet. + // If it is, the only possible syntax here is 'X:ea,D'. + // So check ahead to see if EOL follows D, then we're good to go. + if (((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2)) && *(tok + 1) == EOL) + { + //'X:ea,D' + D1 = SDreg(*tok++); + + inst = inst | B8(01000000) | (1 << 7); + inst |= ea1; + inst |= ((D1 & 0x18) << (12 - 3)) + ((D1 & 7) << 8); + return inst; + } + } + else + { + if (*tok == EOL) + { + //'S,X:ea' + inst = inst | B8(01000000) | (0 << 7); + inst |= ea1; + inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); + return inst; + } + else + { + goto x_checkea_right; + } + } + // 'X:eax,D1 Y:eay,D2', 'X:eax,D1 S2,Y:eay' or 'X:ea,D1 S2,D2' + // Check ahead for S2,D2 - if that's true then we have 'X:ea,D1 S2,D2' + if ((*tok == KW_X0 || *tok == KW_X1 || *tok == KW_A || *tok == KW_B) && (*(tok + 1) == KW_A || *(tok + 1) == KW_B) && (*(tok + 2) == ',') && (*(tok + 3) == KW_Y0 || (*(tok + 3) == KW_Y1))) + { + // 'X:ea,D1 S2,D2' + // Check if D1 is x0, x1, a or b + switch (*tok++) + { + case KW_X0: D1 = 0 << 10; break; + case KW_X1: D1 = 1 << 10; break; + case KW_A: D1 = 2 << 10; break; + case KW_B: D1 = 3 << 10; break; + default: return error("unrecognised X:R parallel move syntax: expected x0, x1, a or b after 'X:eax,'"); + } + + switch (*tok++) + { + case KW_A: S2 = 0 << 9; break; + case KW_B: S2 = 1 << 9; break; + default: return error("unrecognised X:R parallel move syntax: expected a or b after 'X:eax,D1 '"); + } + + if (*tok++ != ',') + return error("unrecognised X:R parallel move syntax: expected ',' after 'X:eax,D1 S2'"); + + if (*tok == KW_Y0 || *tok == KW_Y1) + { + if (*tok++ == KW_Y0) + D2 = 0 << 8; + else + D2 = 1 << 8; + + if (*tok != EOL) + return error("unrecognised X:R parallel move syntax: unexpected text after 'X:eax,D1 S2,S2'"); + + inst = B16(00010000, 00000000) | (W << 7); + inst |= ea1 | D1 | S2 | D2; + return inst; + } + else + return error("unrecognised X:R parallel move syntax: expected y0 or y1 after 'X:eax,D1 S2,'"); + + } + + // Check to see if we got eax (which is a subset of ea) + if (check_for_x_y) + { + if ((inst = check_x_y(ea1, 0)) != 0) + return inst; + else + { + // Rewind pointer as it might be an expression and check for it + tok--; + if (expr(dspaaEXPR, &dspaaEXVAL, &dspaaEXATTR, &dspaaESYM) != OK) + return ERROR; + // Yes, we have an expression, so we now check for + // 'X:ea,D' or 'X:aa,D' or 'X:ea,D1 S2,D2' or 'X:eax,D1 Y:eay,D2' or 'X:eax,D1 S2,Y:eay' + goto x_check_immed; + } + } + } + else if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + // Check for immediate address + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + + // We set ea1 here - if it's aa instead of ea + // then it won't be used anyway + ea1 = DSP_EA_ABS; + if (!(dspImmedEXATTR&DEFINED)) + { + force_imm = NUM_FORCE_LONG; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + } + + goto x_check_immed; + } + else if (*tok == '>') + { + // Check for immediate address forced long + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + if (dspImmedEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + force_imm = NUM_FORCE_LONG; + ea1 = DSP_EA_ABS; + goto x_check_immed; + } + else if (*tok == '<') + { + // Check for immediate address forced short + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + force_imm = NUM_FORCE_SHORT; + if (dspImmedEXATTR & DEFINED) + { + if (dspImmedEXVAL > 0x3f) + { + warn("short addressing mode forced but address is bigger than $3f - switching to long"); + force_imm = NUM_FORCE_LONG; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + ea1 = DSP_EA_ABS; + } + } + else + { + // This might end up as something like 'move Y:') + if (*tok == '-') + { + if (tok[1] == CONST || tok[1] == FCONST) + { + tok++; + dspImmedEXVAL = *tok++; + goto y_check_immed; + } + // This could be either -(Rn), -aa or -ea. Check for immediate first + if (*tok == SYMBOL || tok[1] == SYMBOL) + { + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) == OK) + { + // Only check for aa if we have a defined number in our hands or we've + // been asked to use a short number format. The former case we'll just test + // it to see if it's small enough. The later - it's the programmer's call + // so he'd better have a small address or the fixups will bite him/her in the arse! + if (dspImmedEXATTR&DEFINED || force_imm == NUM_FORCE_SHORT) + { + // It's an immediate, so ea is probably an absolute address + // (unless it's aa if the immediate is small enough) + // 'Y:ea,D', 'Y:aa,D', 'S,Y:ea' or 'S,Y:aa' + y_check_immed: + // Check for aa (which is 6 bits zero extended) + if (dspImmedEXVAL < 0x40 && force_imm != NUM_FORCE_LONG) + { + // It might be Y:aa but we're not 100% sure yet. + // If it is, the only possible syntax here is 'Y:aa,D'/'S,Y:aa'. + // So check ahead to see if EOL follows D, then we're good to go. + if (*tok == EOL && S1 != 0) + { + // 'S,Y:aa' + inst = B16(01001000, 00000000); + inst |= dspImmedEXVAL; + inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); + return inst; + } + if (*tok == ',' && ((*(tok + 1) >= KW_X0 && *(tok + 1) <= KW_N7) || (*(tok + 1) >= KW_R0 && *(tok + 1) <= KW_R7) || (*(tok + 1) >= KW_A0 && *(tok + 1) <= KW_A2)) && *(tok + 2) == EOL) + { + // Yup, we're good to go - 'Y:aa,D' it is + tok++; + immreg = SDreg(*tok++); + inst |= dspImmedEXVAL; + inst |= ((immreg & 0x18) << (12 - 3)) + ((immreg & 7) << 8); + return inst; + } + } + } + // Well, that settles it - we do have a ea in our hands + if (*tok == EOL && S1 != 0) + { + // 'S,Y:ea' + inst = B16(01001000, 01110000); + inst |= ea1; + inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + return inst; + } + if (*tok++ != ',') + return error("unrecognised Y: parallel move syntax: expected ',' after 'Y:ea'"); + if (D1 == 0 && S1 == 0) + { + // 'Y:ea,D' + if ((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2)) + { + D1 = SDreg(*tok++); + if (*tok != EOL) + return error("unrecognised Y: parallel move syntax: expected EOL after 'Y:ea,D'"); + inst |= B16(00000000, 01110000); + inst |= ea1; + inst |= ((D1 & 0x18) << (12 - 3)) + ((D1 & 7) << 8); + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + return inst; + } + else + return error("unrecognised Y: parallel move syntax: expected x0,x1,y0,y1,a0,b0,a2,b2,a1,b1,a,b,r0-r7,n0-n7 after 'Y:ea,'"); + } + else + { + // 'S1,D1 Y:ea,D2' + if (*tok == KW_A || *tok == KW_B || *tok == KW_Y0 || *tok == KW_Y1) + { + D2 = SDreg(*tok++); + inst |= ea1; + inst |= 1 << 7; + inst |= (S1 & 1) << 11; + inst |= (D1 & 1) << 10; + inst |= (D2 & 8) << (9 - 3); + inst |= (D2 & 1) << 8; + return inst; + } + else + return error("unrecognised R:Y: parallel move syntax: expected a,b after 'S1,D1 Y:ea,'"); + } + } + else + return ERROR; + } + else + { + // It's not an immediate, check for '-(Rn)' + ea1 = checkea(',', Y_ERRORS); + + if (ea1 == ERROR) + return ERROR; + + goto y_gotea1; + + } + } + else if (*tok == '#') + { + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + // Okay so we have immediate data - mark it down + ea1 = DSP_EA_IMM; + // Now, proceed to the main code for this branch + goto y_gotea1; + } + else if (*tok == '(') + { + // Maybe we got an expression here, check for it + if (tok[1] == CONST || tok[1] == FCONST || tok[1] == SUNARY || tok[1] == SYMBOL || tok[1] == STRING) + { + // Evaluate the expression and go to immediate code path + expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM); + goto y_check_immed; + } + + // Nope, let's check for ea then + if (S1 == 0 || (S1 != 0 && D1 != 0)) + ea1 = checkea(',', Y_ERRORS); + else + ea1 = checkea(EOL, Y_ERRORS); + + if (ea1 == ERROR) + return ERROR; + + y_gotea1: + if (S1 != 0 && *tok == EOL) + { + // 'S,Y:ea' + inst = B16(01001000, 01000000); + inst |= ea1; + inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); + return inst; + } + else if (S1 != 0 && D1 != 0 && S2 == 0) + { + // 'S1,D1 Y:ea,D2' + switch (S1) + { + case 14: S1 = 0 << 11; break; // A + case 15: S1 = 1 << 11; break; // B + default: return error("unrecognised R:Y parallel move syntax: S1 can only be A or B in 'S1,D1 Y:ea,D2'"); break; + } + switch (D1) + { + case 4: D1 = 0 << 10; break; // X0 + case 5: D1 = 1 << 10; break; // X1 + default: return error("unrecognised R:Y parallel move syntax: D1 can only be x0 or x1 in 'S1,D1 Y:ea,D2'");break; + } + if (*tok++ != ',') + return error("unrecognised R:Y parallel move syntax: expected ',' after 'S1,D1 Y:ea'"); + switch (*tok++) + { + case KW_Y0: D2 = 0 << 8; break; + case KW_Y1: D2 = 1 << 8; break; + case KW_A: D2 = 2 << 8; break; + case KW_B: D2 = 3 << 8; break; + default: return error("unrecognised R:Y parallel move syntax: D2 can only be y0, y1, a or b after 'S1,D1 Y:ea'"); + } + inst = B16(00010000, 11000000); + inst |= S1 | D1 | D2; + inst |= ea1; + return inst; + } + if (*tok++ != ',') + return error("Comma expected after 'Y:(Rn)')"); + // It might be 'Y:(Rn..)..,D' but we're not 100% sure yet. + // If it is, the only possible syntax here is 'Y:ea,D'. + // So check ahead to see if EOL follows D, then we're good to go. + if (((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2)) && *(tok + 1) == EOL) + { + //'Y:ea,D' + D1 = SDreg(*tok++); + inst |= B16(00000000, 01000000); + inst |= ea1; + inst |= ((D1 & 0x18) << (12 - 3)) + ((D1 & 7) << 8); + return inst; + } + } + else if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + // Check for immediate address + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + + // Yes, we have an expression, so we now check for + // 'Y:ea,D' or 'Y:aa,D' + ea1 = DSP_EA_ABS; // Reluctant - but it's probably correct + if (!(dspImmedEXATTR&DEFINED)) + { + force_imm = NUM_FORCE_LONG; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + } + + goto y_check_immed; + } + else if (*tok == '>') + { + // Check for immediate address forced long + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + if (dspImmedEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + force_imm = NUM_FORCE_LONG; + ea1 = DSP_EA_ABS; + goto y_check_immed; + } + else if (*tok == '<') + { + tok++; + if (S1 != 0 && D1 != 0) + { + // We're in 'S1,D1 Y:ea,D2' or 'S1,D1 S1,Y:ea' + // there's no Y:aa mode here, so we'll force long + warn("forced short addressing in R:Y mode is not allowed - switching to long"); + + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + + ea1 = DSP_EA_ABS; + + force_imm = NUM_FORCE_LONG; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + goto y_check_immed; + } + else + { + // Check for immediate address forced short + ea1 = DSP_EA_ABS; // Reluctant - but it's probably correct + + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + force_imm = NUM_FORCE_SHORT; + if (dspImmedEXATTR & DEFINED) + { + if (dspImmedEXVAL > 0xfff) + { + warn("short addressing mode forced but address is bigger than $fff - switching to long"); + ea1 = DSP_EA_ABS; + force_imm = NUM_FORCE_LONG; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + } + } + else + { + // This might end up as something like 'move Y:') + if (*tok == '-') + { + if (*tok == CONST || tok[1] == FCONST) + { + tok++; + dspImmedEXVAL = *tok++; + goto l_check_immed; + } + // This could be either -(Rn), -aa or -ea. Check for immediate first + // Maybe we got an expression here, check for it + if (*tok == SYMBOL || tok[1] == SYMBOL) + { + // Evaluate the expression and go to immediate code path + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) == OK) + { + // Only check for aa if we have a defined number in our hands or we've + // been asked to use a short number format. The former case we'll just test + // it to see if it's small enough. The later - it's the programmer's call + // so he'd better have a small address or the fixups will bite him/her in the arse! + if (dspImmedEXATTR&DEFINED || force_imm == NUM_FORCE_SHORT) + { + // It's an immediate, so ea is probably an absolute address + // (unless it's aa if the immediate is small enough) + // 'L:ea,D' or 'L:aa,D' + l_check_immed: + // Check for aa (which is 6 bits zero extended) + if (*tok == EOL) + { + // 'S,L:aa' + if (dspImmedEXVAL < 0x40 && force_imm != NUM_FORCE_LONG) + { + // 'S,L:aa' + if (S1 == KW_A) + S1 = 4; + else if (S1 == KW_B) + S1 = 5; + else + S1 &= 7; + + inst = B16(01000000, 00000000); + inst |= dspImmedEXVAL; + inst |= ((S1 & 0x4) << (11 - 2)) + ((S1 & 3) << 8); + return inst; + } + else + { + // 'S,L:ea' + if (S1 == KW_A) + S1 = 4; + else if (S1 == KW_B) + S1 = 5; + else + S1 &= 7; + + if (ea1 == DSP_EA_ABS) + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + inst |= B16(01000000, 01110000); + inst |= ((S1 & 0x4) << (11 - 2)) + ((S1 & 3) << 8); + inst |= ea1; + return inst; + } + + } + if (*tok++ != ',') + return error("unrecognised L: parallel move syntax: expected ',' after 'L:ea/L:aa'"); + // Check for allowed registers for D (a0, b0, x, y, a, b, ab or ba) + if (!((*tok >= KW_A10 && *(tok + 1) <= KW_BA) || (*tok >= KW_A && *tok <= KW_B))) + return error("unrecognised L: parallel move syntax: expected a0, b0, x, y, a, b, ab or ba after 'L:ea/L:aa'"); + + if (dspImmedEXVAL < (1 << 6) && (dspImmedEXATTR&DEFINED)) + { + // 'L:aa,D' + l_aa: + immreg = *tok++; + if (immreg == KW_A) + immreg = 4; + else if (immreg == KW_B) + immreg = 5; + else + immreg &= 7; + + if (*tok != EOL) + return error("unrecognised L: parallel move syntax: expected End-Of-Line after L:aa,D"); + + inst &= B16(11111111, 10111111); + inst |= dspImmedEXVAL; + inst |= ((immreg & 0x4) << (11 - 2)) + ((immreg & 3) << 8); + return inst; + } + } + + if (deposit_extra_ea == DEPOSIT_EXTRA_FIXUP) + { + // Hang on, we've got a L:= KW_A10 && *tok <= KW_BA) || (*tok >= KW_A && *tok <= KW_B)) && *(tok + 1) == EOL) + { + //'L:ea,D' + D1 = *tok++; + if (D1 == KW_A) + D1 = 4; + else if (D1 == KW_B) + D1 = 5; + else + D1 &= 7; + + inst |= ea1; + inst |= ((D1 & 0x4) << (11 - 2)) + ((D1 & 3) << 8); + return inst; + } + } + else if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + // Check for immediate address + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + + // We set ea1 here - if it's aa instead of ea + // then it won't be used anyway + ea1 = DSP_EA_ABS; + if (!(dspImmedEXATTR & DEFINED)) + { + force_imm = NUM_FORCE_LONG; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + } + else if (dspImmedEXVAL > 0x3f) + { + // Definitely no aa material, so it's going to be a long + // Mark that we need to deposit an extra word + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + } + + // Yes, we have an expression, so we now check for + // 'L:ea,D' or 'L:aa,D' + goto l_check_immed; + } + else if (*tok == '>') + { + // Check for immediate address forced long + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + if (dspImmedEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + force_imm = NUM_FORCE_LONG; + goto l_check_immed; + } + else if (*tok == '<') + { + // Check for immediate address forced short + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + { + if (dspImmedEXVAL > 0xfff) + return error("short addressing mode forced but address is bigger than $fff"); + } + else + { + // This might end up as something like 'move Y:= KW_R0 && *tok <= KW_R7) + { + // We got '-(Rn' so mark it down + ea = DSP_EA_PREDEC1 | (*tok++ - KW_R0); + if (*tok++ != ')') + return error(ea_errors[strings][1]); + // Now, proceed to the main code for this branch + return ea; + } + else + return error(ea_errors[strings][2]); + } + else if (*tok == '(') + { + // Checking for ea of type (Rn) + tok++; + if (*tok >= KW_R0 && *tok <= KW_R7) + { + // We're in 'X:(Rn..)..,D', 'X:(Rn..)..,D1 Y:eay,D2', 'X:(Rn..)..,D1 S2,Y:eay' + ea = *tok++ - KW_R0; + if (*tok == '+') + { + // '(Rn+Nn)' + tok++; + if (*tok < KW_N0 || *tok > KW_N7) + return error(ea_errors[strings][3]); + if ((*tok++ & 7) != ea) + return error(ea_errors[strings][4]); + ea |= DSP_EA_INDEX; + if (*tok++ != ')') + return error(ea_errors[strings][5]); + return ea; + } + else if (*tok == ')') + { + // Check to see if we have '(Rn)+', '(Rn)-', '(Rn)-Nn', '(Rn)+Nn' or '(Rn)' + tok++; + if (*tok == '+') + { + tok++; + if (termchar == ',') + { + if (*tok == ',') + { + // (Rn)+ + ea |= DSP_EA_POSTINC1; + return ea; + } + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)+Nn + if ((*tok++ & 7) != ea) + return error(ea_errors[strings][6]); + ea |= DSP_EA_POSTINC; + return ea; + } + else + return error(ea_errors[strings][7]); + } + else + { + if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)+Nn + if ((*tok++ & 7) != ea) + return error(ea_errors[strings][6]); + ea |= DSP_EA_POSTINC; + return ea; + } + else + { + // (Rn)+ + ea |= DSP_EA_POSTINC1; + return ea; + } + } + } + else if (*tok == '-') + { + tok++; + if (termchar == ',') + { + if (*tok == ',') + { + // (Rn)- + ea |= DSP_EA_POSTDEC1; + return ea; + } + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)-Nn + if ((*tok++ & 7) != ea) + return error(ea_errors[strings][8]); + ea |= DSP_EA_POSTDEC; + return ea; + } + else + return error(ea_errors[strings][9]); + } + else + { + if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)-Nn + if ((*tok++ & 7) != ea) + return error(ea_errors[strings][8]); + ea |= DSP_EA_POSTDEC; + return ea; + } + else + { + // (Rn)- + ea |= DSP_EA_POSTDEC1; + return ea; + } + } + } + else if (termchar == ',') + { + if (*tok == ',') + { + // (Rn) + ea |= DSP_EA_NOUPD; + return ea; + } + else + return error(ea_errors[strings][10]); + } + else + { + // (Rn) + ea |= DSP_EA_NOUPD; + return ea; + } + } + else + return error(ea_errors[strings][11]); + } + } + return error("internal assembler error: Please report this error message: 'reached the end of checkea' with the line of code that caused it. Thanks, and sorry for the inconvenience"); +} + + +// +// Checks for all ea cases, i.e. all addressing modes that checkea handles +// plus immediate addresses included forced short/long ones. +// In other words this is a superset of checkea (and in fact calls checkea). +// +LONG checkea_full(const uint32_t termchar, const int strings) +{ + LONG ea1; + + if (*tok == CONST || *tok == FCONST || *tok == SYMBOL) + { + // Check for immediate address + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + // Yes, we have an expression + return DSP_EA_ABS; + } + else if (*tok == '>') + { + // Check for immediate address forced long + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + if (dspImmedEXVAL > 0xffffff) + return error("long address is bigger than $ffffff"); + + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + + // Yes, we have an expression + return DSP_EA_ABS; + } + else if (*tok == '<') + { + // Check for immediate address forced short + tok++; + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + if (dspImmedEXVAL > 0xfff) + return error("short addressing mode forced but address is bigger than $fff"); + + // Yes, we have an expression + return DSP_EA_ABS; + } + else + { + ea1 = checkea(termchar, strings); + if (ea1 == ERROR) + return ERROR; + else + return ea1; + } + +} + +// +// Main routine to check parallel move modes. +// It's quite complex so it's split into a few procedures (in fact most of the above ones). +// A big effort was made so this can be managable and not too hacky, however one look at +// the 56001 manual regarding parallel moves and you'll know that this is not an easy +// problem to deal with! +// dest=destination register from the main opcode. This must not be the same as D1 or D2 +// and that even goes for stuff like dest=A, D1=A0/1/2!!! +// +LONG parmoves(WORD dest) +{ + int force_imm; // Addressing mode force operator + int immreg; // Immediate register destination + LONG inst; // 16 bit bitfield that has the parallel move opcode + LONG S1, S2, D1, D2; // Source and Destinations + LONG ea1; // ea bitfields + + if (*tok == EOL) + { + // No parallel move + return B16(00100000, 00000000); + } + if (*tok == '#') + { + // '#xxxxxx,D', '#xx,D' + tok++; + force_imm = NUM_NORMAL; + if (*tok == '>') + { + force_imm = NUM_FORCE_LONG; + tok++; + } + else if (*tok == '<') + { + force_imm = NUM_FORCE_SHORT; + tok++; + } + + if (expr(dspImmedEXPR, &dspImmedEXVAL, &dspImmedEXATTR, &dspImmedESYM) != OK) + return ERROR; + + if (*tok++ != ',') + return error("expected comma"); + + if (!((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2))) + return error("expected x0,x1,y0,y1,a0,b0,a2,b2,a1,b1,a,b,r0-r7,n0-n7 after immediate"); + immreg = SDreg(*tok++); + + if (*tok == EOL) + { + if (!(dspImmedEXATTR & FLOAT)) + { + if (dspImmedEXATTR & DEFINED) + { + // From I parallel move: + // "If the destination register D is X0, X1, Y0, Y1, A, or B, the 8-bit immediate short operand + // is interpreted as a signed fraction and is stored in the specified destination register. + // That is, the 8 - bit data is stored in the eight MS bits of the destination operand, and the + // remaining bits of the destination operand D are zeroed." + // The funny bit is that Motorola assembler can parse something like 'move #$FF0000,b' into an + // I (immediate short move) - so let's do that as well then... + if (((immreg >= 4 && immreg <= 7) || immreg == 14 || immreg == 15) && force_imm != NUM_FORCE_LONG) + { + if ((dspImmedEXVAL & 0xffff) == 0) + { + dspImmedEXVAL >>= 16; + } + } + if (force_imm == NUM_FORCE_SHORT) + { + if (dspImmedEXVAL<0xff && (int32_t)dspImmedEXVAL>-0x100) + { + // '#xx,D' + // value fits in 8 bits - immediate move + inst = B16(00100000, 00000000) + (immreg << 8) + (uint32_t)dspImmedEXVAL; + return inst; + } + else + { + warn("forced short immediate value doesn't fit in 8 bits - switching to long"); + force_imm = NUM_FORCE_LONG; + } + } + if (force_imm == NUM_FORCE_LONG) + { + // '#xxxxxx,D' + // it can either be + // X or Y Data move. I don't think it matters much + // which of the two it will be, so let's use X. + deposit_immediate_long_with_register: + inst = B16(01000000, 11110100); + inst |= ((immreg & 0x18) << (12 - 3)) + ((immreg & 7) << 8); + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + return inst; + } + if (((int32_t)dspImmedEXVAL < 0x100) && ((int32_t)dspImmedEXVAL >= -0x100)) + { + // value fits in 8 bits - immediate move + deposit_immediate_short_with_register: + inst = B16(00100000, 00000000) + (immreg << 8) + (uint32_t)dspImmedEXVAL; + return inst; + } + else + { + // value doesn't fit in 8 bits, so it can either be + // X or Y Data move. I don't think it matters much + // which of the two it will be, so let's use X:. + // TODO: if we're just goto'ing perhaps the logic can be simplified + goto deposit_immediate_long_with_register; + } + } + else + { + if (force_imm != NUM_FORCE_SHORT) + { + // '#xxxxxx,D' + // TODO: if we're just goto'ing perhaps the logic can be simplified + goto deposit_immediate_long_with_register; + } + else + { + // '#xx,D' - I mode + // No visibility of the number so let's add a fixup for this + AddFixup(FU_DSPIMM8, sloc, dspImmedEXPR); + inst = B16(00100000, 00000000); + inst |= ((immreg & 0x18) << (11 - 3)) + ((immreg & 7) << 8); + return inst; + } + + } + } + else + { + // Float constant + if (dspImmedEXATTR & DEFINED) + { + + double f = *(double *)&dspImmedEXVAL; + // Check direct.c for ossom comments regarding conversion! + dspImmedEXVAL = ((uint32_t)(int32_t)round(f*(1 << 23))) & 0xffffff; + double g; + g = f*(1 << 23); + g = round(g); + + if ((dspImmedEXVAL & 0xffff) == 0) + { + // Value's 16 lower bits are not set so the value can fit in a single byte + // (check parallel I move quoted above) + warn("Immediate value fits inside 8 bits, so using instruction short format"); + dspImmedEXVAL >>= 16; + goto deposit_immediate_short_with_register; + } + + if (force_imm == NUM_FORCE_SHORT) + { + if ((dspImmedEXVAL & 0xffff) != 0) + { + warn("Immediate value short format forced but value does not fit inside 8 bits - switching to long format"); + goto deposit_immediate_long_with_register; + } + return error("internal assembler error: we haven't implemented floating point constants in parallel mode parser yet!"); + } + // If we reach here we either have NUM_FORCE_LONG or nothing, so we might as well store a long. + goto deposit_immediate_long_with_register; + } + else + { + if (force_imm == NUM_FORCE_SHORT) + { + goto deposit_immediate_short_with_register; + } + else + { + // Just deposit a float fixup + AddFixup(FU_DSPIMMFL8, sloc, dspImmedEXPR); + inst = B16(00100000, 00000000); + inst |= ((immreg & 0x18) << (12 - 3)) + ((immreg & 7) << 8); + return inst; + } + } + } + } + else + { + // At this point we can only have '#xxxxxx,D1 S2,D2' (X:R Class I) + + switch (immreg) + { + case 4: D1 = 0 << 10;break; // X0 + case 5: D1 = 1 << 10;break; // X1 + case 14: D1 = 2 << 10;break; // A + case 15: D1 = 3 << 10;break; // B + default: return error("unrecognised X:R parallel move syntax: D1 can only be x0,x1,a,b in '#xxxxxx,D1 S2,D2'"); break; + } + + switch (*tok++) + { + case KW_A: S2 = 0 << 9; break; + case KW_B: S2 = 1 << 9; break; + default: return error("unrecognised X:R parallel move syntax: S2 can only be A or B in '#xxxxxx,D1 S2,D2'"); break; + } + + if (*tok++ != ',') + return error("unrecognised X:R parallel move syntax: expected comma after '#xxxxxx,D1 S2'"); + + switch (*tok++) + { + case KW_Y0: D2 = 0 << 8; break; + case KW_Y1: D2 = 1 << 8; break; + default: return error("unrecognised X:R parallel move syntax: D2 can only be Y0 or Y1 in '#xxxxxx,D1 S2,D2'"); break; + } + + if (*tok != EOL) + return error("unrecognised X:R parallel move syntax: expected end-of-line after '#xxxxxx,D1 S2,D2'"); + + inst = B16(00010000, 10110100) | D1 | S2 | D2; + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + return inst; + } + } + else if (*tok == KW_X) + { + if (tok[1] == ',') + // Hey look, it's just the register X and not the addressing mode - fall through to general case + goto parse_everything_else; + + tok++; + + if (*tok++ != ':') + return error("expected ':' after 'X' in parallel move (i.e. X:)"); + + // 'X:ea,D' or 'X:aa,D' or 'X:ea,D1 S2,D2' or 'X:eax,D1 Y:eay,D2' or 'X:eax,D1 S2,Y:eay' + return parse_x(1, B16(01000000, 00000000), 0, 1); + } + else if (*tok == KW_Y) + { + if (tok[1] == ',') + // Hey look, it's just the register y and not the addressing mode - fall through to general case + goto parse_everything_else; + + tok++; + if (*tok++ != ':') + return error("expected ':' after 'Y' in parallel move (i.e. Y:)"); + + // 'Y:ea,D' or 'Y:aa,D' + return parse_y(B16(01001000, 10000000), 0, 0, 0); + } + else if (*tok == KW_L) + { + // 'L:ea,D' or 'L:aa,D' + tok++; + if (*tok++ != ':') + return error("expected ':' after 'L' in parallel move (i.e. L:)"); + + return parse_l(1, B16(01000000, 11000000), 0); + } + else if ((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2) || (*tok >= KW_A10 && *tok <= KW_BA)) + { + // Everything else - brace for impact! + // R: 'S,D' + // X: 'S,X:ea' 'S,X:aa' + // X:R 'S,X:ea S2,D2' 'A,X:ea X0,A' 'B,X:ea X0,B' + // Y: 'S,Y:ea' 'S,Y:aa' + // R:Y: 'S1,D1 Y:ea,D2' 'S1,D1 S2,Y:ea' 'Y0,A A,Y:ea' 'Y0,B B,Y:ea' 'S1,D1 #xxxxxx,D2' 'Y0,A A,Y:ea' 'Y0,B B,Y:ea' + // L: 'S,L:ea' 'S,L:aa' + LONG L_S1; +parse_everything_else: + L_S1 = *tok++; + S1 = SDreg(L_S1); + + if (*tok++ != ',') + return error("Comma expected after 'S')"); + + if (*tok == KW_X) + { + // 'S,X:ea' 'S,X:aa' 'S,X:ea S2,D2' 'S1,X:eax Y:eay,D2' 'S1,X:eax S2,Y:eay' + // 'A,X:ea X0,A' 'B,X:ea X0,B' + tok++; + if (*tok++ != ':') + return error("unrecognised X: parallel move syntax: expected ':' after 'S,X'"); + return parse_x(0, B16(01000000, 00000000), S1, 1); + } + else if (*tok == KW_Y) + { + // 'S,Y:ea' 'S,Y:aa' + tok++; + if (*tok++ != ':') + return error("unrecognised Y: parallel move syntax: expected ':' after 'S,Y'"); + return parse_y(B16(0000000, 00000000), S1, 0, 0); + } + else if (*tok == KW_L) + { + // 'S,L:ea' 'S,L:aa' + tok++; + if (*tok++ != ':') + return error("unrecognised L: parallel move syntax: expected ':' after 'S,L'"); + return parse_l(1, B16(00000000, 00000000), L_S1); + } + else if ((*tok >= KW_X0 && *tok <= KW_N7) || (*tok >= KW_R0 && *tok <= KW_R7) || (*tok >= KW_A0 && *tok <= KW_A2)) + { + // 'S,D' + // 'S1,D1 Y:ea,D2' 'S1,D1 S2,Y:ea' 'S1,D1 #xxxxxx,D2' + // 'Y0,A A,Y:ea' 'Y0,B B,Y:ea' + D1 = SDreg(*tok++); + if (*tok == EOL) + { + // R 'S,D' + inst = B16(00100000, 00000000); + inst |= (S1 << 5) | (D1); + return inst; + } + else if (*tok == KW_Y) + { + // 'S1,D1 Y:ea,D2' + tok++; + if (*tok++ != ':') + return error("expected ':' after 'Y' in parallel move (i.e. Y:)"); + return parse_y(B16(00010000, 01000000), S1, D1, 0); + + } + else if (*tok == KW_A || *tok == KW_B || *tok == KW_Y0 || *tok == KW_Y1) + { + // 'Y0,A A,Y:ea' 'Y0,B B,Y:ea' 'S1,D1 S2,Y:ea' + S2 = SDreg(*tok++); + if (S1 == 6 && D1 == 14 && S2 == 14) + { + // 'Y0,A A,Y:ea' + if (*tok++ != ',') + return error("unrecognised Y: parallel move syntax: expected ',' after Y0,A A"); + if (*tok++ != KW_Y) + return error("unrecognised Y: parallel move syntax: expected 'Y' after Y0,A A,"); + if (*tok++ != ':') + return error("unrecognised Y: parallel move syntax: expected ':' after Y0,A A,Y"); + ea1 = checkea_full(EOL, Y_ERRORS); + if (ea1 == ERROR) + return ERROR; + inst = B16(00001000, 10000000); + inst |= 0 << 8; + inst |= ea1; + return inst; + } + else if (S1 == 6 && D1 == 15 && S2 == 15) + { + // 'Y0,B B,Y:ea' + if (*tok++ != ',') + return error("unrecognised Y: parallel move syntax: expected ',' after Y0,B B"); + if (*tok++ != KW_Y) + return error("unrecognised Y: parallel move syntax: expected 'Y' after Y0,B B,"); + if (*tok++ != ':') + return error("unrecognised Y: parallel move syntax: expected ':' after Y0,B B,Y"); + ea1 = checkea_full(EOL, Y_ERRORS); + if (ea1 == ERROR) + return ERROR; + inst = B16(00001000, 10000000); + inst |= 1 << 8; + inst |= ea1; + return inst; + } + else if ((S1 == 14 || S1 == 15) && (D1 == 4 || D1 == 5) && (S2 == 6 || S2 == 7 || S2 == 14 || S2 == 15)) + { + //'S1,D1 S2,Y:ea' + if (*tok++ != ',') + return error("unrecognised Y: parallel move syntax: expected ',' after S1,D1 S2"); + if (*tok++ != KW_Y) + return error("unrecognised Y: parallel move syntax: expected 'Y' after S1,D1 S2,"); + if (*tok++ != ':') + return error("unrecognised Y: parallel move syntax: expected ':' after S1,D1 S2,Y"); + ea1 = checkea_full(EOL, Y_ERRORS); + if (ea1 == ERROR) + return ERROR; + inst = B16(00010000, 01000000); + inst |= (S1 & 1) << 11; + inst |= (D1 & 1) << 10; + inst |= ((S2 & 8) << (10 - 4)) | ((S2 & 1) << 8); + inst |= ea1; + return inst; + } + else + return error("unrecognised Y: parallel move syntax: only 'Y0,A A,Y:ea' 'Y0,B B,Y:ea' allowed'"); + // Check for Y: + } + else if (*tok == '#') + { + // R:Y: 'S1,D1 #xxxxxx,D2' + tok++; + if (*tok == '>') + { + // Well, forcing an immediate to be 24 bits is legal here + // but then it's the only available option so my guess is that this + // is simply superfluous. So let's just eat the character + tok++; + } + if (expr(dspaaEXPR, &dspaaEXVAL, &dspaaEXATTR, &dspaaESYM) != OK) + return ERROR; + if (dspImmedEXATTR & DEFINED) + if (dspImmedEXVAL > 0xffffff) + return error("immediate is bigger than $ffffff"); + deposit_extra_ea = DEPOSIT_EXTRA_WORD; + if (*tok++ != ',') + return error("Comma expected after 'S1,D1 #xxxxxx')"); + // S1 is a or b, D1 is x0 or x1 and d2 is y0, y1, a or b + switch (*tok++) + { + case KW_Y0: D2 = 0 << 8; break; + case KW_Y1: D2 = 1 << 8; break; + case KW_A: D2 = 2 << 8; break; + case KW_B: D2 = 3 << 8; break; + default: return error("unrecognised R:Y: parallel move syntax: D2 must be y0, y1, a or b in 'S1,D1 #xxxxxx,D2'"); + } + + if (S1 == 14 || S1 == 15) + { + if (D1 == 4 || D1 == 5) + { + inst = B16(00010000, 11110100); + inst |= (S1 & 1) << 11; + inst |= (D1 & 1) << 10; + inst |= D2; + dspImmedEXVAL = dspaaEXVAL; + return inst; + } + else + return error("unrecognised R:Y: parallel move syntax: D1 must be x0 or x1 in 'S1,D1 #xxxxxx,D2'"); + } + else + return error("unrecognised R:Y: parallel move syntax: S1 must be a or b in 'S1,D1 #xxxxxx,D2'"); + } + else + return error("unrecognised R:Y: parallel move syntax: Unexpected text after S,D in 'S1,D1 #xxxxxx,D2'"); + } + else + return error("unrecognised R:Y: parallel move syntax: Unexpected text after 'S,'"); + } + else if (*tok == '(') + { + // U: 'ea' + // U 'ea' can only be '(Rn)-Nn', '(Rn)+Nn', '(Rn)-' or '(Rn)+' + tok++; + if (*tok >= KW_R0 && *tok <= KW_R7) + { + ea1 = (*tok++ - KW_R0); + } + else + return error("unrecognised U parallel move syntax: expected 'Rn' after '('"); + if (*tok++ != ')') + return error("unrecognised U parallel move syntax: expected ')' after '(Rn'"); + if (*tok == '+') + { + tok++; + if (*tok == EOL) + // (Rn)+ + ea1 |= 3 << 3; + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)+Nn + if ((*tok++ & 7) != ea1) + return error("unrecognised U parallel move syntax: Same register number expected for Rn, Nn in '(Rn)+Nn')"); + ea1 |= 1 << 3; + if (*tok != EOL) + return error("unrecognised U parallel move syntax: expected End-Of-Line after '(Rn)+Nn'"); + } + else + return error("unrecognised U parallel move syntax: expected End-Of-Line or 'Nn' after '(Rn)+'"); + + } + else if (*tok == '-') + { + tok++; + if (*tok == EOL) + { + // (Rn)- + ea1 |= 2 << 3; + tok++; + } + else if (*tok >= KW_N0 && *tok <= KW_N7) + { + // (Rn)-Nn + if ((*tok++ & 7) != ea1) + return error("unrecognised U parallel move syntax: Same register number expected for Rn, Nn in '(Rn)-Nn')"); + ea1 |= 0 << 3; + if (*tok != EOL) + return error("unrecognised U parallel move syntax: expected End-Of-Line after '(Rn)-Nn'"); + } + } + + inst = B16(00100000, 01000000); + inst |= ea1; + return inst; + } + else + return error("extra (unexpected) text found"); + + return OK; +} +