/* * pasmop.c * * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /*=========================================================================== * Copyright (c) Texas Instruments Inc 2010-12 * * Use of this software is controlled by the terms and conditions found in the * license agreement under which this software has been supplied or provided. * ============================================================================ */ /*=========================================================================== // PASM - PRU Assembler //--------------------------------------------------------------------------- // // File : pasmop.c // // Description: // Handles the processing of PRU opcodes. // - Provides a function to test for reserved words // - Processes assembly lines and generates opcodes // - Contains private functions to process operand fields // //--------------------------------------------------------------------------- // Revision: // 21-Jun-13: 0.84 - Open source version ============================================================================*/ #include #include #include #if !defined(__APPLE__) && !defined(__FreeBSD__) #include #else #include #endif #include #include "pasm.h" char *OpText[] = { "$ERROR$","ADD","ADC","SUB","SUC","LSL","LSR","RSB","RSC","AND","OR", "XOR","NOT","MIN","MAX","CLR","SET","LDI","LBBO","LBCO","SBBO", "SBCO","LFC","STC","JAL","JMP","QBGT","QBLT","QBEQ","QBGE","QBLE", "QBNE","QBA","QBBS","QBBC","LMBD","CALL","WBC","WBS","MOV","MVIB", "MVIW","MVID","SCAN","HALT","SLP", "RET", "ZERO", "FILL", "XIN", "XOUT", "XCHG","SXIN","SXOUT","SXCHG","LOOP","ILOOP","NOP0","NOP1","NOP2","NOP3", "NOP4","NOP5","NOP6","NOP7","NOP8","NOP9","NOPA","NOPB","NOPC","NOPD", "NOPE","NOPF"}; /* Local Support Funtions */ static int GetImValue( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa, uint low, uint high ); static int GetConstant( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ); static int GetR0offset( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ); static int GetJmpOffset( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ); static int GetLoopOffset( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ); static int Offset2Reg( SOURCEFILE *ps, int num, PRU_ARG *pa, uint addr, uint size ); /* // CheckOpcode // // Called to see if the supplied token is an opcode. // // Returns index of opcode, 0 if not an opcode */ int CheckOpcode( char *word ) { int i; for(i=1; i<=OP_MAXIDX; i++) { if( !stricmp( word, OpText[i] ) ) return(i); } return(0); } /* // GetTokenType // // Called to see if the supplied token is reserved word. // // Returns 1 if reserved, 0 if not */ uint CheckTokenType( char *word ) { int i,parse_state; uint flags; char c; /* Opcodes are reserved */ if( CheckOpcode(word) ) return(TOKENTYPE_FLG_OPCODE); /* Check for command */ if( CheckDotCommand( word ) ) return(TOKENTYPE_FLG_DIRECTIVE); if( !strcmp(word,"SIZE") || !strcmp(word,"OFFSET") ) return(TOKENTYPE_FLG_DIRECTIVE); /* // [&,*][--](B,C,R,T,W)#[#][.(B,C,R,T,W)#[#]][++ ] is reserved // // For example: // R - not reserved // R23 - reserved // R100 - not reserved // C34.XYZ - not reserved // C34.T34 - reserved */ i=0; parse_state=0; if( word[i]=='*' ) { /* Mark as a pointer */ flags = TOKENTYPE_FLG_REG_PTR; i++; /* Check if its an immediate address */ if( word[i]=='&' ) { flags |= TOKENTYPE_FLG_REG_ADDR; i++; } /* Else its a register base */ else { flags |= TOKENTYPE_FLG_REG_BASE; /* Registers can also be pre-dec */ if( word[i]=='-' ) { if( word[i+1]!='-' ) return(TOKENTYPE_UNRESERVED); i+=2; flags |= TOKENTYPE_FLG_REG_PREDEC; } } } else if( word[i]=='&' ) { flags = TOKENTYPE_FLG_REG_ADDR; i++; } else flags = TOKENTYPE_FLG_REG_BASE; for(;;) { c = word[i++]; c = toupper(c); switch( parse_state ) { case 0: if( c=='B' || c=='C' || c=='R' || c=='T' || c=='W' ) parse_state=1; else return(TOKENTYPE_UNRESERVED); break; case 1: if( isdigit(c) ) parse_state=2; else return(TOKENTYPE_UNRESERVED); break; case 2: case 3: if( isdigit(c) ) { parse_state++; if( parse_state>3 ) return(TOKENTYPE_UNRESERVED); } else if( c=='.' ) parse_state=0; else if( !c || c==' ' || c==0x9 ) return(flags); else if( c=='+' ) { /* Can not be an address or predec */ if( flags & (TOKENTYPE_FLG_REG_ADDR|TOKENTYPE_FLG_REG_PREDEC) ) return(TOKENTYPE_UNRESERVED); /* Rest of type must match */ if( word[i]!=c || word[i+1]!=0 ) return(TOKENTYPE_UNRESERVED); flags |= TOKENTYPE_FLG_REG_POSTINC; return(flags); } else return(TOKENTYPE_UNRESERVED); break; } } } /* // ProcessOp // // Opcode processor // // This is the function that assembles opcode statements // // ps - Pointer to source file record // TermCnt - Number of terms (including the command) // pTerms - Pointer to the terms // // Returns: // 1 : Success // 0 : Error */ int ProcessOp( SOURCEFILE *ps, int TermCnt, char **pTerms ) { PRU_INST inst; unsigned int opcode; unsigned int utmp; /* Get opcode */ inst.Op = CheckOpcode(pTerms[0]); if( !inst.Op ) { Report(ps,REP_ERROR,"Invalid opcode"); return(0); } switch( inst.Op ) { case OP_NOT: /* // Instruction in the form of: // NOT Rdst, Rsrc, OP(255) // NOT Rdst, Rsrc */ if( TermCnt!=3 && TermCnt!=4 ) { Report(ps,REP_ERROR,"Expected 2 or 3 operands on NOT"); return(0); } goto PARSE_ARITHMETIC; case OP_ADD: case OP_ADC: case OP_SUB: case OP_SUC: case OP_LSL: case OP_LSR: case OP_RSB: case OP_RSC: case OP_AND: case OP_OR: case OP_XOR: case OP_MIN: case OP_MAX: case OP_LMBD: case OP_NOP0: case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOPA: case OP_NOPB: case OP_NOPC: case OP_NOPD: case OP_NOPE: case OP_NOPF: /* // Instruction in the form of: // OPCODE Rdst, Rsrc, OP(255) */ if( inst.Op==OP_LMBD && Core=OP_NOP0 && inst.Op<=OP_NOPF && Core=OP_NOP0 && inst.Op<=OP_NOPF ) opcode = (0x50+inst.Op-OP_NOP0) << 25; else if( inst.Op == OP_LMBD ) opcode = 0x13 << 25; else if( inst.Op == OP_SCAN ) opcode = 0x14 << 25; else opcode = (inst.Op-OP_ADD) << 25; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; opcode |= inst.Arg[1].Value << 8; opcode |= inst.Arg[1].Field << 13; if( inst.Arg[2].Type == ARGTYPE_REGISTER ) { opcode |= inst.Arg[2].Value << 16; opcode |= inst.Arg[2].Field << 21; } else { opcode |= inst.Arg[2].Value << 16; opcode |= 1<<24; } GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_CLR: case OP_SET: /* // Instruction in the form of: // OPCODE Rdst, Rsrc, OP(255) -or- // OPCODE Rdst, Rsrc.Tnn -or- // OPCODE Rdst, OP(255) -or- // OPCODE Rdst.Tnn */ if( TermCnt<2 || TermCnt>4 ) { Report(ps,REP_ERROR,"Expected 1 to 3 operands"); return(0); } /* The 3 argument (4 term) variant can be parsed as arithmetic */ if( TermCnt==4 ) goto PARSE_ARITHMETIC; else if( TermCnt==2 ) { /* // Hande "OPCODE Rdst.Tnn" */ if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 1, 0 ) ) return(0); if( inst.Arg[0].Type != ARGTYPE_REGISTERBIT ) { Report(ps,REP_ERROR,"Single operand mode must specify .T field"); return(0); } /* 2nd arg = 1st arg, move bit# to 3rd arg */ inst.Arg[0].Type = ARGTYPE_REGISTER; inst.Arg[1] = inst.Arg[0]; inst.Arg[2].Type = ARGTYPE_IMMEDIATE; inst.Arg[2].Value = inst.Arg[0].Bit; } else { /* // Handle "OPCODE Rdst, Rsrc.Tnn", and // "OPCODE Rdst, OP(255)" */ if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) return(0); if( CheckTokenType(pTerms[2]) & TOKENTYPE_FLG_REG_BASE ) { if( !GetRegister( ps, 2, pTerms[2], &(inst.Arg[1]), 1, 0 ) ) return(0); } else { if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 31 ) ) return(0); } /* Patch up the args to the standard "arithmetic" form */ if( inst.Arg[1].Type == ARGTYPE_REGISTERBIT ) { /* Here the 2nd arg is Rsrc.Tnn, move bit# to 3rd arg */ inst.Arg[1].Type = ARGTYPE_REGISTER; inst.Arg[2].Type = ARGTYPE_IMMEDIATE; inst.Arg[2].Value = inst.Arg[1].Bit; } else { /* Here the 2nd arg is OP(255), 3rd arg = 2nd arg, 2nd=1st */ inst.Arg[2] = inst.Arg[1]; inst.Arg[1] = inst.Arg[0]; } } goto CODE_ARITHMETIC; case OP_LDI: /* // Instruction in the form of: // LDI Rdst, &Rsrc // LDI Rdst, #Im65535 */ if( TermCnt != 3 ) { Report(ps,REP_ERROR,"Expected 2 operands"); return(0); } if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) { return(0); } if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 65535 ) ) { return(0); } opcode = 0x24 << 24; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; opcode |= inst.Arg[1].Value << 8; GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_MOV: /* // Instruction in the form of: // MOV Rdst, Rsrc -or- // MOV Rdst, &Rsrc -or- // MOV Rdst, #Imm */ if( TermCnt != 3 ) { Report(ps,REP_ERROR,"Expected 2 operands"); return(0); } /* If the second term is not reg, treat similar to LDI */ if( !(CheckTokenType(pTerms[2]) & TOKENTYPE_FLG_REG_BASE) ) { // Unlike LDI, we will auto-select the best opcodes to implement the move if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) { return(0); } switch( inst.Arg[0].Field ) { case FIELDTYPE_7_0: case FIELDTYPE_15_8: case FIELDTYPE_23_16: case FIELDTYPE_31_24: if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 255 ) ) return(0); break; case FIELDTYPE_15_0: case FIELDTYPE_23_8: case FIELDTYPE_31_16: if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 65535 ) ) return(0); break; case FIELDTYPE_31_0: if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 0xFFFFFFFF ) ) return(0); // If the value is greater than 0xFFFF, code the upper half here if( inst.Arg[1].Value > 0xFFFF ) { opcode = 0x24 << 24; opcode |= inst.Arg[0].Value; opcode |= FIELDTYPE_31_16 << 5; opcode |= (inst.Arg[1].Value>>16) << 8; GenOp( ps, TermCnt, pTerms, opcode ); inst.Arg[0].Field = FIELDTYPE_15_0; inst.Arg[1].Value &= 0xFFFF; } break; default: return(0); } opcode = 0x24 << 24; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; opcode |= inst.Arg[1].Value << 8; GenOp( ps, TermCnt, pTerms, opcode ); return(1); } if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) return(0); if( !GetRegister( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 0 ) ) return(0); /* Code as an AND */ inst.Op = OP_AND; inst.Arg[2] = inst.Arg[1]; goto CODE_ARITHMETIC; case OP_SCAN: /* // Instruction in the form of: // SCAN Rdst, OP(255) */ if( Core!=CORE_V1 ) { Report(ps,REP_ERROR,"Instruction illegal with specified core version"); return(0); } if( TermCnt!=3 ) { Report(ps,REP_ERROR,"Expected 2 operands"); return(0); } if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) return(0); if( inst.Arg[0].Field != FIELDTYPE_31_0 ) { Report(ps,REP_ERROR,"Register fields not allowed on operand 1"); return(0); } inst.Arg[1] = inst.Arg[0]; if( CheckTokenType(pTerms[2]) & TOKENTYPE_FLG_REG_BASE ) { if( !GetRegister( ps, 2, pTerms[2], &(inst.Arg[2]), 0, 0 ) ) return(0); } else { if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[2]), 0, 255 ) ) return(0); } goto CODE_ARITHMETIC; case OP_MVIB: /* // Instruction in the form of: // MVIB [*][&][--]Rdst[++], [*][&][--]Rsrc[++] [, bn] */ utmp = 1; goto CODE_MVI; case OP_MVIW: /* // Instruction in the form of: // MVIW [*][&][--]Rdst[++], [*][&][--]Rsrc[++] [, bn] */ utmp = 2; goto CODE_MVI; case OP_MVID: /* // Instruction in the form of: // MVID [*][&][--]Rdst[++], [*][&][--]Rsrc[++] [, bn] */ utmp = 4; CODE_MVI: if( Core=4 && (inst.Arg[0].Value!=1 || inst.Arg[0].Field>FIELDTYPE_31_24)) || (itype%4 && (inst.Arg[1].Value!=1 || inst.Arg[1].Field>FIELDTYPE_31_24)) ) { Report(ps,REP_ERROR,"RegFile pointers must be R1.b0 through R1.b3"); return(0); } if( itype==0 ) { Report(ps,REP_ERROR,"No indirection specified (use MOV)"); return(0); } opcode = 0x16 << 25; opcode |= itype << 21; if( TermCnt==4 ) { opcode |= 1 << 20; opcode |= inst.Arg[2].Value << 18; } opcode |= (utmp/2) << 16; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; opcode |= inst.Arg[1].Value << 8; opcode |= inst.Arg[1].Field << 13; } GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_HALT: if( Core>2) & 0x1F; opcode |= (inst.Arg[0].Value&0x3) << 5; } else { opcode |= inst.Arg[0].Value; if( !(Options & OPTION_BIGENDIAN) ) { /* // ** Little Endian Version ** */ switch( inst.Arg[0].Field ) { case FIELDTYPE_15_8: case FIELDTYPE_23_8: opcode |= 1<<5; break; case FIELDTYPE_23_16: case FIELDTYPE_31_16: opcode |= 2<<5; break; case FIELDTYPE_31_24: opcode |= 3<<5; break; } } else { /* // ** Big Endian Version ** */ switch( inst.Arg[0].Field ) { case FIELDTYPE_23_16: case FIELDTYPE_23_8: opcode |= 1<<5; break; case FIELDTYPE_15_0: case FIELDTYPE_15_8: opcode |= 2<<5; break; case FIELDTYPE_7_0: opcode |= 3<<5; break; } } } opcode |= inst.Arg[1].Value << 8; if( inst.Arg[2].Type == ARGTYPE_REGISTER ) { opcode |= inst.Arg[2].Value << 16; opcode |= inst.Arg[2].Field << 21; } else { opcode |= inst.Arg[2].Value << 16; opcode |= 1<<24; } if( inst.Arg[3].Type == ARGTYPE_R0BYTE ) utmp = inst.Arg[3].Value + 124; else utmp = inst.Arg[3].Value - 1; opcode |= (utmp&0x70)<<(25-4); opcode |= (utmp&0x0E)<<(13-1); opcode |= (utmp&0x01)<<7; GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_SXIN: opcode = (0x5D << 23) | (1 << 14); goto CODE_XFR_V3; case OP_SXOUT: opcode = (0x5E << 23) | (1 << 14); goto CODE_XFR_V3; case OP_SXCHG: opcode = (0x5F << 23) | (1 << 14); goto CODE_XFR_V3; case OP_XIN: opcode = 0x5D << 23; goto CODE_XFR; case OP_XOUT: opcode = 0x5E << 23; goto CODE_XFR; case OP_XCHG: opcode = 0x5F << 23; goto CODE_XFR; CODE_XFR_V3: if( Core>2) & 0x1F; opcode |= (inst.Arg[1].Value&0x3) << 5; } else { opcode |= inst.Arg[1].Value; if( !(Options & OPTION_BIGENDIAN) ) { /* // ** Little Endian Version ** */ switch( inst.Arg[1].Field ) { case FIELDTYPE_15_8: case FIELDTYPE_23_8: opcode |= 1<<5; break; case FIELDTYPE_23_16: case FIELDTYPE_31_16: opcode |= 2<<5; break; case FIELDTYPE_31_24: opcode |= 3<<5; break; } } else { /* // ** Big Endian Version ** */ switch( inst.Arg[1].Field ) { case FIELDTYPE_23_16: case FIELDTYPE_23_8: opcode |= 1<<5; break; case FIELDTYPE_15_0: case FIELDTYPE_15_8: opcode |= 2<<5; break; case FIELDTYPE_7_0: opcode |= 3<<5; break; } } } opcode |= inst.Arg[0].Value << 15; if( inst.Arg[2].Type == ARGTYPE_R0BYTE ) utmp = inst.Arg[2].Value + 124; else utmp = inst.Arg[2].Value - 1; opcode |= utmp<<7; GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_LFC: /* // Instruction in the form of: // LFC Rdst, #Im255 */ if( Core!=CORE_V0 ) { Report(ps,REP_ERROR,"Instruction illegal with specified core version"); return(0); } if( TermCnt != 3 ) { Report(ps,REP_ERROR,"Expected 2 operands"); return(0); } if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) return(0); if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 255 ) ) return(0); opcode = 0xb << 28; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; opcode |= inst.Arg[1].Value << 8; opcode |= 1 << 24; GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_STC: /* // Instruction in the form of: // STC Rsrc, #Im255 // STC Rsrc, #Im255, OP(255) */ if( Core!=CORE_V0 ) { Report(ps,REP_ERROR,"Instruction illegal with specified core version"); return(0); } if( TermCnt!=3 && TermCnt!=4 ) { Report(ps,REP_ERROR,"Expected 2 or 3 operands"); return(0); } if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) return(0); if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 255 ) ) return(0); if( TermCnt==4 ) { if( CheckTokenType(pTerms[3]) & TOKENTYPE_FLG_REG_BASE ) { if( !GetRegister( ps, 3, pTerms[3], &(inst.Arg[2]), 0, 0 ) ) return(0); } else { if( !GetImValue( ps, 3, pTerms[3], &(inst.Arg[2]), 0, 255 ) ) return(0); } } opcode = 0xa << 28; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; opcode |= inst.Arg[1].Value << 8; if( TermCnt==4 ) { if( inst.Arg[2].Type == ARGTYPE_REGISTER ) { opcode |= inst.Arg[2].Value << 16; opcode |= inst.Arg[2].Field << 21; } else { opcode |= inst.Arg[2].Value << 16; opcode |= 1<<24; } } else { /* When a 32 bit reg is used, we need the 8 MS bits from that reg */ if( inst.Arg[0].Field==FIELDTYPE_31_0 ) { opcode |= inst.Arg[0].Value << 16; opcode |= FIELDTYPE_31_24 << 21; } /* Less than 32 bits were used, so clear the 8 MS bits via #0 */ else opcode |= 1<<24; } GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_JAL: /* // Instruction in the form of: // JAL Rdst, Rjmp // JAL Rdst, #Im65535 */ if( TermCnt != 3 ) { Report(ps,REP_ERROR,"Expected 2 operands"); return(0); } if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[0]), 0, 0 ) ) return(0); if( CheckTokenType(pTerms[2]) & TOKENTYPE_FLG_REG_BASE ) { if( !GetRegister( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 0 ) ) return(0); } else { if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[1]), 0, 65535 ) ) return(0); } CODE_JAL: opcode = 0x11 << 25; opcode |= inst.Arg[0].Value; opcode |= inst.Arg[0].Field << 5; if( inst.Arg[1].Type == ARGTYPE_REGISTER ) { opcode |= inst.Arg[1].Value << 16; opcode |= inst.Arg[1].Field << 21; } else { opcode |= inst.Arg[1].Value << 8; opcode |= 1<<24; } GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_RET: if( TermCnt != 1 ) { Report(ps,REP_ERROR,"Expected no operands"); return(0); } inst.Arg[1].Type = ARGTYPE_REGISTER; inst.Arg[1].Value = RetRegValue; inst.Arg[1].Field = RetRegField; goto CODE_JMP; case OP_JMP: case OP_CALL: /* // Instruction in the form of: // OPCODE Rjmp // OPCODE #Im65535 */ if( TermCnt != 2 ) { Report(ps,REP_ERROR,"Expected 1 operand"); return(0); } if( CheckTokenType(pTerms[1]) & TOKENTYPE_FLG_REG_BASE ) { if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[1]), 0, 0 ) ) return(0); } else { if( !GetImValue( ps, 1, pTerms[1], &(inst.Arg[1]), 0, 65535 ) ) return(0); } if( inst.Op==OP_CALL ) { inst.Arg[0].Type = ARGTYPE_REGISTER; inst.Arg[0].Value = RetRegValue; inst.Arg[0].Field = RetRegField; goto CODE_JAL; } CODE_JMP: opcode = 0x10 << 25; if( inst.Arg[1].Type == ARGTYPE_REGISTER ) { opcode |= inst.Arg[1].Value << 16; opcode |= inst.Arg[1].Field << 21; } else { opcode |= inst.Arg[1].Value << 8; opcode |= 1<<24; } GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_ILOOP: opcode = (3<<28) | (1<<15); goto CODE_LOOP; case OP_LOOP: opcode = (3<<28); CODE_LOOP: /* // Instruction in the form of: // LOOP LoopDest, OP(255) */ if( Core3 ) { Report(ps,REP_ERROR,"Expected 1 to 2 operands"); return(0); } inst.Arg[0].Type = ARGTYPE_OFFSET; inst.Arg[0].Value = 0; inst.Arg[0].Field = 0; if( !GetRegister( ps, 1, pTerms[1], &(inst.Arg[1]), 1, 0 ) ) return(0); if( TermCnt==2 ) { /* // Handle "OPCODE JmpDest, Rtest.Tnn" */ if( inst.Arg[1].Type != ARGTYPE_REGISTERBIT ) { Report(ps,REP_ERROR,"Two operand mode must specify .T field"); return(0); } /* Move bit# to 3rd arg */ inst.Arg[1].Type = ARGTYPE_REGISTER; inst.Arg[2].Type = ARGTYPE_IMMEDIATE; inst.Arg[2].Value = inst.Arg[1].Bit; } else { /* // Handle "OPCODE JmpDest, Rtest, OP(255)" */ if( inst.Arg[1].Type != ARGTYPE_REGISTER ) { Report(ps,REP_ERROR,"Three operand mode may not use .T field"); return(0); } if( CheckTokenType(pTerms[2]) & TOKENTYPE_FLG_REG_BASE ) { if( !GetRegister( ps, 2, pTerms[2], &(inst.Arg[2]), 0, 0 ) ) return(0); } else { if( !GetImValue( ps, 2, pTerms[2], &(inst.Arg[2]), 0, 31 ) ) return(0); } } goto CODE_QB; case OP_FILL: /* // Instruction in the form of: // FILL &Rdst, #Im124 // FILL #Im123, #Im124 */ if( Core124 ) { Report(ps,REP_ERROR,"Length exceeds register file length"); return(0); } if( !inst.Arg[1].Value ) { Report(ps,REP_ERROR,"Zero length fill"); return(0); } /* Implement with XIN */ opcode = 0x5D << 23; opcode |= (inst.Arg[0].Value>>2) & 0x1F; opcode |= (inst.Arg[0].Value&0x3) << 5; opcode |= 254 << 15; inst.Arg[1].Value--; opcode |= inst.Arg[1].Value<<7; GenOp( ps, TermCnt, pTerms, opcode ); return(1); case OP_ZERO: /* // Instruction in the form of: // ZERO &Rdst, #Im124 // ZERO #Im123, #Im124 */ if( Core124 ) { Report(ps,REP_ERROR,"Clear length exceeds register file length"); return(0); } if( !inst.Arg[1].Value ) { Report(ps,REP_ERROR,"Zero length clear"); return(0); } if( Core>=CORE_V2 ) { /* Implement with XIN */ opcode = 0x5D << 23; opcode |= (inst.Arg[0].Value>>2) & 0x1F; opcode |= (inst.Arg[0].Value&0x3) << 5; opcode |= 255 << 15; inst.Arg[1].Value--; opcode |= inst.Arg[1].Value<<7; GenOp( ps, TermCnt, pTerms, opcode ); return(1); } while( inst.Arg[1].Value ) { uint reg,field=0,size=0; reg = inst.Arg[0].Value/4; if( !(Options & OPTION_BIGENDIAN) ) { /* // Little Endian Version */ switch( inst.Arg[0].Value & 0x3 ) { case 0: if( inst.Arg[1].Value >= 4 ) { size = 4; field = FIELDTYPE_31_0; } else if( inst.Arg[1].Value >= 2 ) { size = 2; field = FIELDTYPE_15_0; } else { size = 1; field = FIELDTYPE_7_0; } break; case 1: if( inst.Arg[1].Value >= 2 ) { size = 2; field = FIELDTYPE_23_8; } else { size = 1; field = FIELDTYPE_15_8; } break; case 2: if( inst.Arg[1].Value >= 2 ) { size = 2; field = FIELDTYPE_31_16; } else { size = 1; field = FIELDTYPE_23_16; } break; case 3: size = 1; field = FIELDTYPE_31_24; break; } } else { /* // Big Endian Version */ switch( inst.Arg[0].Value & 0x3 ) { case 0: if( inst.Arg[1].Value >= 4 ) { size = 4; field = FIELDTYPE_31_0; } else if( inst.Arg[1].Value >= 2 ) { size = 2; field = FIELDTYPE_31_16; } else { size = 1; field = FIELDTYPE_31_24; } break; case 1: if( inst.Arg[1].Value >= 2 ) { size = 2; field = FIELDTYPE_23_8; } else { size = 1; field = FIELDTYPE_23_16; } break; case 2: if( inst.Arg[1].Value >= 2 ) { size = 2; field = FIELDTYPE_15_0; } else { size = 1; field = FIELDTYPE_15_8; } break; case 3: size = 1; field = FIELDTYPE_7_0; break; } } inst.Arg[0].Value += size; inst.Arg[1].Value -= size; /* LDI */ opcode = 0x24 << 24; opcode |= reg; opcode |= field << 5; GenOp( ps, TermCnt, pTerms, opcode ); } return(1); } Report(ps,REP_ERROR,"Invalid opcode"); return(0); } /*=================================================================== // // Private Functions // ====================================================================*/ /* // GetRegister // // Get Register Argument // // Parses the source string for a register and field // // ps - Pointer to source file record // num - operand number (1 based) // src - source string // pa - Pointer to register structure // fBitOk - Can accept Rxx.Txx // termC - Set to non-zero if special terminating character ('+') // // Returns: // 1 : Success // 0 : Error */ int GetRegister( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa, int fBitOk, char termC ) { uint idx; char c,field; int val,reg,width,offset,bit; /* // The following register syntaxes are valid: // Raa aa=(0-31) // Raa.Wb aa=(0-31) b=(0-2) // Raa.Bc aa=(0-31) c=(0-3) // Raa.Tdd aa=(0-31) dd=(0-31) // Raa.Wb.Be aa=(0-31) b=(0-2) e=(0-1) // Raa.Wb.Tff aa=(0-31) b=(0-2) ff=(0-15) // Raa.Bc.Tgg aa=(0-31) c=(0-3) gg=(0-7) // Raa.Wb.Be.Tgg aa=(0-31) b=(0-2) e=(0-1) gg=(0-7) */ idx=0; c = src[idx++]; /* Get initial 'R##' */ if( toupper(c) != 'R' ) goto INVALID_REG; c = src[idx++]; if( !isdigit(c) ) goto INVALID_REG; val = 0; while( isdigit(c) ) { val *= 10; val += c-'0'; c = src[idx++]; } if( val>31 ) goto INVALID_REG; reg = val; width = 32; offset = 0; bit = -1; for(;;) { /* This char must be '.', or terminator */ /* If terminated, we're done */ if( c==termC ) break; if( c != '.' ) goto INVALID_REG; c = src[idx++]; /* This char must be 'W', 'B', or 'T' */ c = toupper(c); if( c!='T' && c!='W' && c!='B' ) goto INVALID_REG; field=c; c = src[idx++]; if( !isdigit(c) ) goto INVALID_REG; val = 0; while( isdigit(c) ) { val *= 10; val += c-'0'; c = src[idx++]; } if( field=='W' ) { if( ((val*8)+16)>width ) goto INVALID_REG; width = 16; offset += val; } if( field=='B' ) { if( ((val*8)+8)>width ) goto INVALID_REG; width = 8; offset += val; } if( field=='T' ) { if( !fBitOk ) { Report(ps,REP_ERROR,"Operand %d use of .T field not allowed here",num); return(0); } if( val>=width ) goto INVALID_REG; if(c!=termC) goto INVALID_REG; bit = val; } if( c!=termC && (Core==CORE_V0) ) goto INVALID_REG; } /* Build the record */ pa->Type = ARGTYPE_REGISTER; pa->Value = reg; if( width==32 ) pa->Field = FIELDTYPE_31_0; if( width==16 ) { if( !offset ) pa->Field = FIELDTYPE_15_0; else if( offset==1 ) pa->Field = FIELDTYPE_23_8; else pa->Field = FIELDTYPE_31_16; } if( width==8 ) { if( !offset ) pa->Field = FIELDTYPE_7_0; else if( offset==1 ) pa->Field = FIELDTYPE_15_8; else if( offset==2 ) pa->Field = FIELDTYPE_23_16; else pa->Field = FIELDTYPE_31_24; } if( bit>=0 ) { pa->Type = ARGTYPE_REGISTERBIT; pa->Bit = bit; } return(1); INVALID_REG: Report(ps,REP_ERROR,"Operand %d '%s' invalid register or register mode",num,src); return(0); } /* // GetImValue // // Get Register Argument // // Parses the source string for an immediate value // // ps - Pointer to source file record // num - operand number (1 based) // src - source string // pa - Pointer to register structure // min - Lowest legal value // max - Highest legal value // // Returns: // 1 : Success // 0 : Error */ static int GetImValue( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa, uint min, uint max ) { char tstr[TOKEN_MAX_LEN]; uint val; int idx; /* Handler register file offsets as immediates */ val = CheckTokenType(src); if( val!=TOKENTYPE_UNRESERVED && !(val&TOKENTYPE_FLG_REG_ADDR) ) { Report(ps,REP_ERROR,"Operand %d reserved word '%s' not legal here",num,src); return(0); } // Get our value from a an expression if( *src=='#' ) src++; strcpy( tstr, src ); if( Expression(ps, tstr, &val, &idx)<0 ) { Report(ps,REP_ERROR,"Operand %d error in expression",num); return(0); } if( Pass==2 && (Options & OPTION_DEBUG) ) printf("%s(%5d) : EXP : '%s' = %d\n", ps->SourceName,ps->CurrentLine,src,val); /* Setup the record */ pa->Type = ARGTYPE_IMMEDIATE; pa->Value = val; pa->Field = 0; if( valmax ) { Report(ps,REP_ERROR,"Operand %d immediate value out of range (%d-%d)",num,min,max); return(0); } return(1); } /* // GetConstant // // Get Constant Table Argument // // Parses the source string for a constant register // // ps - Pointer to source file record // num - source line number // src - source string // pa - Pointer to register structure // // Returns: // 1 : Success // 0 : Error */ static int GetConstant( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ) { uint idx; char c; int val; idx=0; c = src[idx++]; /* Get C## */ if( toupper(c) != 'C' ) goto INVALID_CONST; c = src[idx++]; if( !isdigit(c) ) goto INVALID_CONST; val = 0; while( isdigit(c) ) { val *= 10; val += c-'0'; c = src[idx++]; } if( c || val>31 ) goto INVALID_CONST; /* Setup the record */ pa->Type = ARGTYPE_CONSTANT; pa->Value = val; pa->Field = 0; return(1); INVALID_CONST: Report(ps,REP_ERROR,"Operand %d invalid constant table entry '%s'",num,src); return(0); } /* // GetR0offset // // Get "R0-offset" Argument for Burst Opcodes // // Parses the source string for a register and field // // ps - Pointer to source file record // num - source line number // src - source string // pa - Pointer to register structure // // Returns: // 1 : Success // 0 : Error */ static int GetR0offset( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ) { uint idx; char c; int val; idx=0; c = src[idx++]; /* Get B## */ if( toupper(c) != 'B' ) goto INVALID_R0BYTE; c = src[idx++]; if( !isdigit(c) ) goto INVALID_R0BYTE; val = 0; while( isdigit(c) ) { val *= 10; val += c-'0'; c = src[idx++]; } if( c || val>3 ) goto INVALID_R0BYTE; /* Setup the record */ pa->Type = ARGTYPE_R0BYTE; pa->Value = val; pa->Field = 0; return(1); INVALID_R0BYTE: Report(ps,REP_ERROR,"Operand %d invalid byte count '%s'",num,src); return(0); } /* // GetJmpOffset // // Get 10 bit Jump Offset for Quick Branch // // Parses the source string for a register and field // // ps - Pointer to source file record // num - source line number // src - source string // pa - Pointer to register structure // // Returns: // 1 : Success // 0 : Error */ static int GetJmpOffset( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ) { char tstr[TOKEN_MAX_LEN]; uint val; int idx; int jmpoff; strcpy( tstr, src ); if( Expression(ps, tstr, &val, &idx)<0 ) { Report(ps,REP_ERROR,"Operand %d error in expression",num); return(0); } if( Pass==2 && (Options & OPTION_DEBUG) ) printf("%s(%5d) : EXP : '%s' = %d\n", ps->SourceName,ps->CurrentLine,src,val); jmpoff = ((int)val) - CodeOffset; if( Pass==2 && (jmpoff<-512 || jmpoff>511) ) { Report(ps,REP_ERROR,"Operand %d relative jump out of range",num); return(0); } /* Setup the record */ pa->Type = ARGTYPE_OFFSET; pa->Value = (uint)jmpoff; pa->Field = 0; return(1); } /* // GetLoopOffset // // Get 8 bit Loop Offset // // Parses the source string for a register and field // // ps - Pointer to source file record // num - source line number // src - source string // pa - Pointer to register structure // // Returns: // 1 : Success // 0 : Error */ static int GetLoopOffset( SOURCEFILE *ps, int num, char *src, PRU_ARG *pa ) { char tstr[TOKEN_MAX_LEN]; uint val; int idx; int jmpoff; strcpy( tstr, src ); if( Expression(ps, tstr, &val, &idx)<0 ) { Report(ps,REP_ERROR,"Operand %d error in expression",num); return(0); } if( Pass==2 && (Options & OPTION_DEBUG) ) printf("%s(%5d) : EXP : '%s' = %d\n", ps->SourceName,ps->CurrentLine,src,val); jmpoff = ((int)val) - CodeOffset; if( Pass==2 && (jmpoff<2 || jmpoff>255) ) { Report(ps,REP_ERROR,"Operand %d invalid loop termination point",num); return(0); } /* Setup the record */ pa->Type = ARGTYPE_OFFSET; pa->Value = (uint)jmpoff; pa->Field = 0; return(1); } /* // Offset2Reg // // Convert Register Offset to Register // // ps - Pointer to source file record // num - operand number (1 based) // pa - Pointer to register structure // addr - Field address (0 - 127) // size - Field size (1, 2, or 4) // // Returns: // 1 : Success // 0 : Error */ static int Offset2Reg( SOURCEFILE *ps, int num, PRU_ARG *pa, uint addr, uint size ) { uint offset; offset = addr&3; if( addr+size > 128 ) { Report(ps,REP_ERROR,"Operand %d, field extends past register file", num); return(0); } if( (size==2 && offset==3) || (size==4 && offset!=0) ) { Report(ps,REP_ERROR,"Operand %d, this field alignment not supported", num); return(0); } /* Build the record */ pa->Type = ARGTYPE_REGISTER; pa->Value = addr/4; /* Get field type */ if( !(Options & OPTION_BIGENDIAN) ) { /* // Little Endian Version */ if( size==4 ) pa->Field = FIELDTYPE_31_0; else if( size==2 ) { if( !offset ) pa->Field = FIELDTYPE_15_0; else if( offset==1 ) pa->Field = FIELDTYPE_23_8; else pa->Field = FIELDTYPE_31_16; } else { if( !offset ) pa->Field = FIELDTYPE_7_0; else if( offset==1 ) pa->Field = FIELDTYPE_15_8; else if( offset==2 ) pa->Field = FIELDTYPE_23_16; else pa->Field = FIELDTYPE_31_24; } } else { /* // Big Endian Version */ if( size==4 ) pa->Field = FIELDTYPE_31_0; else if( size==2 ) { if( !offset ) pa->Field = FIELDTYPE_31_16; else if( offset==1 ) pa->Field = FIELDTYPE_23_8; else pa->Field = FIELDTYPE_15_0; } else { if( !offset ) pa->Field = FIELDTYPE_31_24; else if( offset==1 ) pa->Field = FIELDTYPE_23_16; else if( offset==2 ) pa->Field = FIELDTYPE_15_8; else pa->Field = FIELDTYPE_7_0; } } return(1); }