// // RMAC - Renamed Macro Assembler for the Atari Jaguar Console System // AMODE.C - DSP 56001 Addressing Modes // Copyright (C) 199x Landon Dyer, 2011-2021 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_MN #include "mntab.h" #define DEF_REG56 #include "56kregs.h" // Address-mode information 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 LONG dsp_a0perspace; // Peripheral space (X, Y - used in movep) LONG dsp_a1perspace; // Peripheral space (X, Y - used in movep) 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 == REG56_A || *tok == REG56_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 >= REG56_X0 && *tok <= REG56_Y1) { *am = M_ALU24; *areg = *tok++; return OK; } else if (*tok == REG56_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 == REG56_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) { if (CHECK_OPTS(OPT_56K_AUTO_LONG)) { if (optim_warn_flag) warn("o11: 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 { return error("short addressing mode forced but address is bigger than $3F - turn opt switch o11 on to bypass"); } } } 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 >= REG56_X) && (*tok <= REG56_Y)) { *am = M_INP48; *areg = *tok++; return OK; } else if ((*tok >= REG56_M0) && (*tok <= REG56_M7)) { *am = M_DSPM; *areg = (*tok++) & 7; return OK; } else if ((*tok >= REG56_R0) && (*tok <= REG56_R7)) { *am = M_DSPR; *areg = (*tok++) - REG56_R0; return OK; } else if ((*tok >= REG56_N0) && (*tok <= REG56_N7)) { *am = M_DSPN; *areg = (*tok++) & 7; return OK; } else if ((*tok == REG56_A0) || (*tok == REG56_A1) || (*tok == REG56_B0) || (*tok == REG56_B1)) { *am = M_ACC24; *areg = *tok++; return OK; } else if ((*tok == REG56_A2) || (*tok == REG56_B2)) { *am = M_ACC8; *areg = *tok++; return OK; } else if ((*tok == '-') && (*(tok + 1) == REG56_X0 || *(tok + 1) == REG56_X1 || *(tok + 1) == REG56_Y0 || *(tok + 1) == REG56_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) == REG56_X0 || *(tok + 1) == REG56_X1 || *(tok + 1) == REG56_Y0 || *(tok + 1) == REG56_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 == REG56_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 == REG56_PC || *tok == REG56_CCR || *tok == REG56_SR || *tok == REG56_SP || (*tok >= REG56_MR&&*tok <= REG56_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 >= REG56_X0 && reg <= REG56_N7) return reg & 0xFF; else if (reg >= REG56_A0&® <= REG56_A2) return (8 >> (reg & 7)) | 8; else //if (reg>=REG56_R0&®<=REG56_R7) return reg - REG56_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 REG56_X0: D1 = 0 << 10; break; case REG56_X1: D1 = 1 << 10; break; case REG56_A: D1 = 2 << 10; break; case REG56_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 == REG56_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 >= REG56_R0 && *tok <= REG56_R7) { ea2 = (*tok++ - REG56_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 >= REG56_N0 && *tok <= REG56_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 REG56_Y0: D2 = 0 << 8; break; case REG56_Y1: D2 = 1 << 8; break; case REG56_A: D2 = 2 << 8; break; case REG56_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 = 0b1100000000000000 | 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 == REG56_Y0 || *tok == REG56_Y1 || *tok == REG56_A || *tok == REG56_B) { // 'X:eax,D1 S2,Y:eay' 'S1,X:eax1 S2,Y:eay' switch (*tok++) { case REG56_Y0: S2 = 0 << 8; break; case REG56_Y1: S2 = 1 << 8; break; case REG56_A: S2 = 2 << 8; break; case REG56_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++ == REG56_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 >= REG56_R0 && *tok <= REG56_R7) { ea2 = (*tok++ - REG56_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 >= REG56_N0 && *tok <= REG56_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 = 0b1000000000000000 | 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 == REG56_X0 && tok[1] == ',' && tok[2] == REG56_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 == 0b00110100) return error("unrecognised X:R parallel move syntax: immediate data not allowed in 'a,X:ea x0,a'"); inst = 0b0000100000000000 | ea1 | (0 << 8); return inst; } else if (*tok == REG56_X0 && tok[1] == ',' && tok[2] == REG56_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 == 0b00110100) return error("unrecognised X:R parallel move syntax: immediate data not allowed in 'b,X:ea x0,b'"); inst = 0b0000100100000000 | ea1 | (1 << 8); return inst; } else if (*tok == REG56_A || *tok == REG56_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] == REG56_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 REG56_A: S2 = 0 << 9; break; case REG56_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 == REG56_Y0 || *tok == REG56_Y1) { if (*tok++ == REG56_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 = 0b0001000000000000 | (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 == REG56_Y) { // 'S1,X:eax Y:eay,D2' return check_x_y(ea1, S1); } else if (*tok == REG56_Y0 || *tok == REG56_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) >= REG56_X0 && *(tok + 1) <= REG56_N7) || (*(tok + 1) >= REG56_R0 && *(tok + 1) <= REG56_R7) || (*(tok + 1) >= REG56_A0 && *(tok + 1) <= REG56_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 >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_A2)) { D1 = SDreg(*tok++); if (*tok == EOL) { // 'X:ea,D' inst = inst | 0b01000000 | (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 == REG56_A || *tok == REG56_B) { S2 = SDreg(*tok++); if (*tok++ != ',') return error("unrecognised X:R parallel move syntax: expected comma after X:ea,D1 S2"); if (*tok == REG56_Y0 || *tok == REG56_Y1) { D2 = SDreg(*tok++); if (*tok != EOL) return error("unrecognised X:R parallel move syntax: expected EOL after X:ea,D1 S2,D2"); inst = 0b0001000000000000 | (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 | 0b01000000 | (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 >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_A2)) && *(tok + 1) == EOL) { //'X:ea,D' D1 = SDreg(*tok++); inst = inst | 0b01000000 | (1 << 7); inst |= ea1; inst |= ((D1 & 0x18) << (12 - 3)) + ((D1 & 7) << 8); return inst; } } else { if (*tok == EOL) { //'S,X:ea' inst = inst | 0b01000000 | (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 == REG56_X0 || *tok == REG56_X1 || *tok == REG56_A || *tok == REG56_B) && (*(tok + 1) == REG56_A || *(tok + 1) == REG56_B) && (*(tok + 2) == ',') && (*(tok + 3) == REG56_Y0 || (*(tok + 3) == REG56_Y1))) { // 'X:ea,D1 S2,D2' // Check if D1 is x0, x1, a or b switch (*tok++) { case REG56_X0: D1 = 0 << 10; break; case REG56_X1: D1 = 1 << 10; break; case REG56_A: D1 = 2 << 10; break; case REG56_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 REG56_A: S2 = 0 << 9; break; case REG56_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 == REG56_Y0 || *tok == REG56_Y1) { if (*tok++ == REG56_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 = 0b0001000000000000 | (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) && (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) { if (CHECK_OPTS(OPT_56K_AUTO_LONG)) { if (optim_warn_flag) warn("o11: 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 { return error("short addressing mode forced but address is bigger than $3F - turn opt switch o11 on to bypass"); } } } 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 = 0b0100100000000000; inst |= dspImmedEXVAL; inst |= ((S1 & 0x18) << (12 - 3)) + ((S1 & 7) << 8); return inst; } if (*tok == ',' && ((*(tok + 1) >= REG56_X0 && *(tok + 1) <= REG56_N7) || (*(tok + 1) >= REG56_R0 && *(tok + 1) <= REG56_R7) || (*(tok + 1) >= REG56_A0 && *(tok + 1) <= REG56_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 = 0b0100100001110000; 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 >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_A2)) { D1 = SDreg(*tok++); if (*tok != EOL) return error("unrecognised Y: parallel move syntax: expected EOL after 'Y:ea,D'"); inst |= 0b0000000001110000; 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 == REG56_A || *tok == REG56_B || *tok == REG56_Y0 || *tok == REG56_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 = 0b0100100001000000; 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 REG56_Y0: D2 = 0 << 8; break; case REG56_Y1: D2 = 1 << 8; break; case REG56_A: D2 = 2 << 8; break; case REG56_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 = 0b0001000011000000; 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 >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_A2)) && *(tok + 1) == EOL) { //'Y:ea,D' D1 = SDreg(*tok++); inst |= 0b0000000001000000; 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) && (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 if (CHECK_OPTS(OPT_56K_AUTO_LONG)) { if (optim_warn_flag) 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 { return error("forced short addressing in R:Y mode is not allowed - turn opt switch o11 on to bypass"); } } 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) { if (CHECK_OPTS(OPT_56K_AUTO_LONG)) { if (optim_warn_flag) 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 { return error("short addressing mode forced but address is bigger than $FFF - turn opt switch o11 on to bypass"); } } } 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 == REG56_A) S1 = 4; else if (S1 == REG56_B) S1 = 5; else S1 &= 7; inst = 0b0100000000000000; inst |= dspImmedEXVAL; inst |= ((S1 & 0x4) << (11 - 2)) + ((S1 & 3) << 8); return inst; } else { // 'S,L:ea' if (S1 == REG56_A) S1 = 4; else if (S1 == REG56_B) S1 = 5; else S1 &= 7; if (ea1 == DSP_EA_ABS) deposit_extra_ea = DEPOSIT_EXTRA_WORD; inst |= 0b0100000001110000; 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 >= REG56_A10 && *(tok + 1) <= REG56_BA) || (*tok >= REG56_A && *tok <= REG56_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 == REG56_A) immreg = 4; else if (immreg == REG56_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 &= 0b1111111110111111; 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:= REG56_A10 && *tok <= REG56_BA) || (*tok >= REG56_A && *tok <= REG56_B)) && *(tok + 1) == EOL) { //'L:ea,D' D1 = *tok++; if (D1 == REG56_A) D1 = 4; else if (D1 == REG56_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) && (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:= REG56_R0 && *tok <= REG56_R7) { // We got '-(Rn' so mark it down ea = DSP_EA_PREDEC1 | (*tok++ - REG56_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 >= REG56_R0 && *tok <= REG56_R7) { // We're in 'X:(Rn..)..,D', 'X:(Rn..)..,D1 Y:eay,D2', 'X:(Rn..)..,D1 S2,Y:eay' ea = *tok++ - REG56_R0; if (*tok == '+') { // '(Rn+Nn)' tok++; if (*tok < REG56_N0 || *tok > REG56_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 >= REG56_N0 && *tok <= REG56_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 >= REG56_N0 && *tok <= REG56_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 >= REG56_N0 && *tok <= REG56_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 >= REG56_N0 && *tok <= REG56_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) && (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) && (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 0b0010000000000000; } 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 >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_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 = 0b0010000000000000 + (immreg << 8) + (uint32_t)dspImmedEXVAL; return inst; } else { if (CHECK_OPTS(OPT_56K_AUTO_LONG)) { if (optim_warn_flag) warn("forced short immediate value doesn't fit in 8 bits - switching to long"); force_imm = NUM_FORCE_LONG; } else { return error("forced short immediate value doesn't fit in 8 bits - turn opt switch o11 on to bypass"); } } } 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 = 0b0100000011110100; 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 = 0b0010000000000000 + (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 = 0b0010000000000000; 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! //N.B.: This is bogus, we need to fix this so it does this the right way... !!! FIX !!! dspImmedEXVAL = ((uint32_t)(int32_t)round(f * (1 << 23))) & 0xFFFFFF; double g; g = f * (1 << 23); g = round(g); if ((dspImmedEXVAL & 0xFFFF) == 0) { if (CHECK_OPTS(OPT_56K_SHORT)) { // Value's 16 lower bits are not set so the value can // fit in a single byte (check parallel I move quoted // above) if (optim_warn_flag) warn("o10: Immediate value fits inside 8 bits, so using instruction short format"); dspImmedEXVAL >>= 16; goto deposit_immediate_short_with_register; } else { return error("Immediate value fits inside 8 bits, so using instruction short format - turn opt switch o10 on to bypass"); } } if (force_imm == NUM_FORCE_SHORT) { if ((dspImmedEXVAL & 0xFFFF) != 0) { if (CHECK_OPTS(OPT_56K_AUTO_LONG)) { if (optim_warn_flag) warn("o11: Immediate value short format forced but value does not fit inside 8 bits - switching to long format"); goto deposit_immediate_long_with_register; } else { return error("Immediate value short format forced but value does not fit inside 8 bits - turn opt switch o11 on to bypass"); } } 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 = 0b0010000000000000; 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 REG56_A: S2 = 0 << 9; break; case REG56_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 REG56_Y0: D2 = 0 << 8; break; case REG56_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 = 0b0001000010110100 | D1 | S2 | D2; deposit_extra_ea = DEPOSIT_EXTRA_WORD; return inst; } } else if (*tok == REG56_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, 0b0100000000000000, 0, 1); } else if (*tok == REG56_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(0b0100100010000000, 0, 0, 0); } else if (*tok == REG56_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, 0b0100000011000000, 0); } else if ((*tok >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_A2) || (*tok >= REG56_A10 && *tok <= REG56_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 == REG56_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, 0b0100000000000000, S1, 1); } else if (*tok == REG56_Y) { // 'S,Y:ea' 'S,Y:aa' tok++; if (*tok++ != ':') return error("unrecognised Y: parallel move syntax: expected ':' after 'S,Y'"); return parse_y(0b000000000000000, S1, 0, 0); } else if (*tok == REG56_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, 0b0000000000000000, L_S1); } else if ((*tok >= REG56_X0 && *tok <= REG56_N7) || (*tok >= REG56_R0 && *tok <= REG56_R7) || (*tok >= REG56_A0 && *tok <= REG56_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 = 0b0010000000000000; inst |= (S1 << 5) | (D1); return inst; } else if (*tok == REG56_Y) { // 'S1,D1 Y:ea,D2' tok++; if (*tok++ != ':') return error("expected ':' after 'Y' in parallel move (i.e. Y:)"); return parse_y(0b0001000001000000, S1, D1, 0); } else if (*tok == REG56_A || *tok == REG56_B || *tok == REG56_Y0 || *tok == REG56_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++ != REG56_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 = 0b0000100010000000; 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++ != REG56_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 = 0b0000100010000000; 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++ != REG56_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 = 0b0001000001000000; 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) && (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 REG56_Y0: D2 = 0 << 8; break; case REG56_Y1: D2 = 1 << 8; break; case REG56_A: D2 = 2 << 8; break; case REG56_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 = 0b0001000011110100; 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 >= REG56_R0 && *tok <= REG56_R7) { ea1 = (*tok++ - REG56_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 >= REG56_N0 && *tok <= REG56_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 >= REG56_N0 && *tok <= REG56_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 = 0b0010000001000000; inst |= ea1; return inst; } else return error("extra (unexpected) text found"); return OK; }