// Copyright (C) 1999-2003 Core Technologies. // // This file is part of tpasm. // // tpasm is free software; you can redistribute it and/or modify // it under the terms of the tpasm LICENSE AGREEMENT. // // tpasm is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // tpasm LICENSE AGREEMENT for more details. // // You should have received a copy of the tpasm LICENSE AGREEMENT // along with tpasm; see the file "LICENSE.TXT". // Handle matching and processing of assembler pseudo-ops // NOTE: assembler pseudo-ops are those that can be considered processor // independant. #include "include.h" static SYMTABLE *pseudoOpcodeSymbols; // prototypes for the handler functions which need to be declared before we can build // the opcode table static bool HandleInclude(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleSeg(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleSegU(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleOrg(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleROrg(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleAlign(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleAlias(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleUnAlias(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleEqu(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleSet(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleUnSet(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleMacro(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleEndMacro(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleIf(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleIfdef(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleIfndef(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleElse(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleEndIf(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleSwitch(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleCase(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleBreak(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleEndSwitch(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleRepeat(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleEndRepeat(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleError(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleWarning(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleMessage(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleList(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleNoList(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleExpand(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleNoExpand(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleProcessor(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static bool HandleEnd(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord); static PSEUDO_OPCODE pseudoOpcodes[]= { {"include", HandleInclude}, {".include", HandleInclude}, {"seg", HandleSeg}, {".seg", HandleSeg}, {"segu", HandleSegU}, {".segu", HandleSegU}, {"seg.u", HandleSegU}, {".seg.u", HandleSegU}, {"org", HandleOrg}, {".org", HandleOrg}, {"rorg", HandleROrg}, {".rorg", HandleROrg}, {"align", HandleAlign}, {".align", HandleAlign}, {"alias", HandleAlias}, {".alias", HandleAlias}, {"unalias", HandleUnAlias}, {".unalias", HandleUnAlias}, {"equ", HandleEqu}, {".equ", HandleEqu}, {"set", HandleSet}, {".set", HandleSet}, {"unset", HandleUnSet}, {".unset", HandleUnSet}, {"macro", HandleMacro}, {".macro", HandleMacro}, {"endm", HandleEndMacro}, {".endm", HandleEndMacro}, {"if", HandleIf}, {".if", HandleIf}, {"ifdef", HandleIfdef}, {".ifdef", HandleIfdef}, {"ifndef", HandleIfndef}, {".ifndef", HandleIfndef}, {"else", HandleElse}, {".else", HandleElse}, {"endif", HandleEndIf}, {".endif", HandleEndIf}, {"switch", HandleSwitch}, {".switch", HandleSwitch}, {"case", HandleCase}, {".case", HandleCase}, {"break", HandleBreak}, {".break", HandleBreak}, {"ends", HandleEndSwitch}, {".ends", HandleEndSwitch}, {"repeat", HandleRepeat}, {".repeat", HandleRepeat}, {"endr", HandleEndRepeat}, {".endr", HandleEndRepeat}, {"error", HandleError}, {".error", HandleError}, {"warning", HandleWarning}, {".warning", HandleWarning}, {"messg", HandleMessage}, {".messg", HandleMessage}, {"message", HandleMessage}, {".message", HandleMessage}, {"list", HandleList}, {".list", HandleList}, {"nolist", HandleNoList}, {".nolist", HandleNoList}, {"expand", HandleExpand}, {".expand", HandleExpand}, {"noexpand", HandleNoExpand}, {".noexpand", HandleNoExpand}, {"processor", HandleProcessor}, {".processor", HandleProcessor}, {"end", HandleEnd}, {".end", HandleEnd}, }; void ReportDisallowedLabel(char *lineLabel) // complain that a label is not allowed on this op { AssemblyComplaint(NULL,false,"Label '%s' not allowed here\n",lineLabel); } PSEUDO_OPCODE *MatchGlobalPseudoOpcode(char *theOpcode) // try to match theOpcode against the list of assembler pseudo-ops { return((PSEUDO_OPCODE *)STFindDataForName(pseudoOpcodeSymbols,theOpcode)); } static bool HandleInclude(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // include the given file into the input stream // If there is a problem, report it and return false { bool fail; char includeName[MAXSTRING]; int stringLength; bool hunt; fail=false; if(contextStack->active) // if not active, includes are just ignored, since they should not change context anyway { if(haveLabel) { ReportDisallowedLabel(lineLabel); } hunt=false; if(ParseQuotedString(theLine,lineIndex,'"','"',includeName,&stringLength)||(hunt=true,ParseQuotedString(theLine,lineIndex,'<','>',includeName,&stringLength))) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { OutputListFileLine(listingRecord,theLine); // output the line first so that the include contents follow it listingRecord->wantList=false; fail=!ProcessSourceFile(includeName,hunt); // go handle this file now } else { AssemblyComplaint(NULL,true,"Missing or incorrectly formatted include file name\n"); } } else { AssemblyComplaint(NULL,true,"Missing or incorrectly formatted include file name\n"); } } return(!fail); } static bool HandleSeg(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // create new segment, or select old one { char theElement[MAXSTRING]; int stringLength; SEGMENT_RECORD *theSegment; bool fail; fail=false; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseQuotedString(theLine,lineIndex,'"','"',theElement,&stringLength)||ParseFirstListElement(theLine,lineIndex,theElement)) // see which processor is being chosen { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if((theSegment=MatchSegment(theElement))) // see if we can find an existing segment with this name { currentSegment=theSegment; if(!currentSegment->generateOutput) { AssemblyComplaint(NULL,false,"Segment is uninitialized (your reference here is for an initialized segment)\n"); } } else { if((theSegment=CreateSegment(theElement,true))) // create the new segment { currentSegment=theSegment; } else { ReportComplaint(true,"Failed to create segment\n"); fail=true; } } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(!fail); } static bool HandleSegU(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // create new uninitialized segment, or select old one { char theElement[MAXSTRING]; int stringLength; SEGMENT_RECORD *theSegment; bool fail; fail=false; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseQuotedString(theLine,lineIndex,'"','"',theElement,&stringLength)||ParseFirstListElement(theLine,lineIndex,theElement)) // see which processor is being chosen { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if((theSegment=MatchSegment(theElement))) // see if we can find an existing segment with this name { currentSegment=theSegment; if(currentSegment->generateOutput) { AssemblyComplaint(NULL,false,"Segment is initialized (your reference here is for an uninitialized segment)\n"); } } else { if((theSegment=CreateSegment(theElement,false))) // create the new segment { currentSegment=theSegment; } else { ReportComplaint(true,"Failed to create segment\n"); fail=true; } } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(!fail); } static bool HandleOrg(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Org statement, set PC within the current segment // If there is a problem, report it and return false { int address; bool unresolved; bool fail; fail=false; if(contextStack->active) { if(ParseExpression(theLine,lineIndex,&address,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(currentSegment) { if(!unresolved) // if unresolved, then just ignore org for the moment (it will get resolved later) { currentSegment->currentPC=address; // ORG to the given address if(haveLabel) { fail=!AssignLabel(lineLabel,isLocal,currentSegment->currentPC+currentSegment->codeGenOffset); // assign this label (after the ORG) } } } else { AssemblyComplaint(NULL,true,"'org' outside of a segment\n"); } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(!fail); } static bool HandleROrg(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Relocatable org statement, set the offset between the PC (place where code is being // written out, and place where code is being generated (offset is normally 0) // If there is a problem, report it and return false { int address; bool unresolved; bool fail; fail=false; if(contextStack->active) { if(ParseExpression(theLine,lineIndex,&address,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(currentSegment) { if(!unresolved) // if unresolved, then just ignore org for the moment (it will get resolved later) { currentSegment->codeGenOffset=address-currentSegment->currentPC; if(haveLabel) { fail=!AssignLabel(lineLabel,isLocal,currentSegment->currentPC+currentSegment->codeGenOffset); // assign this label (after the RORG) } } } else { AssemblyComplaint(NULL,true,"'rorg' outside of a segment\n"); } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(!fail); } static bool HandleAlign(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Align the PC to a boundary which is given // if 0 or 1 are given, do nothing // If there is a problem, report it and return false { int value; bool unresolved; int offset; bool fail; fail=false; if(contextStack->active) { if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(currentSegment) { if(!unresolved) // if unresolved, then just ignore org for the moment (it will get resolved later) { if(value>0) { offset=currentSegment->currentPC%value; if(offset) { currentSegment->currentPC+=value-offset; } } else { AssemblyComplaint(NULL,false,"Bad value for align (%d) ignored\n",value); } } } else { AssemblyComplaint(NULL,true,"'align' outside of segment\n"); } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(!fail); } static bool HandleAlias(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // ALIAS statement, set definition // If there is a hard error, return false { ALIAS_RECORD *aliasMatch; char aliasValue[MAXSTRING]; int stringLength; bool fail; fail=false; if(contextStack->active) { if(haveLabel) { if(!isLocal) { if(ParseFirstLabelElement(theLine,lineIndex,aliasValue)||ParseQuotedString(theLine,lineIndex,'"','"',aliasValue,&stringLength)) // think of operands as a list of one element, or as a quoted string { if(ParseComment(theLine,lineIndex)) // must be at the end of the line now { if(!(aliasMatch=MatchAlias(lineLabel))) // make sure it does not already exist { if(!CreateAlias(lineLabel,aliasValue)) // create the alias, complain if there's trouble { ReportComplaint(true,"Failed to create alias record\n"); fail=true; } } else { AssemblyComplaint(NULL,true,"Alias '%s' already defined\n",lineLabel); AssemblySupplement(&aliasMatch->whereFrom,"Conflicting alias was defined here\n"); } } else { ReportBadOperands(); } } else { AssemblyComplaint(NULL,true,"Bad alias replacement declaration\n"); } } else { AssemblyComplaint(NULL,true,"'alias' requires a non-local label\n"); } } else { AssemblyComplaint(NULL,true,"'alias' requires a label\n"); } } return(!fail); } static bool HandleUnAlias(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // get rid of an alias which currently exists // If there is a hard error, return false { ALIAS_RECORD *aliasMatch; bool fail; fail=false; if(contextStack->active) { if(haveLabel) { if(!isLocal) { if(ParseComment(theLine,lineIndex)) // must be at the end of the line now { if((aliasMatch=MatchAlias(lineLabel))) // does this alias already exist? { DestroyAlias(aliasMatch); // get rid of the alias } else { AssemblyComplaint(NULL,true,"'%s' not defined as an alias\n",lineLabel); } } else { ReportBadOperands(); } } else { AssemblyComplaint(NULL,true,"'unalias' requires a non-local label\n"); } } else { AssemblyComplaint(NULL,true,"'unalias' requires a label\n"); } } return(!fail); } static bool HandleEqu(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // EQU statement, set label // If there is a hard error, return false { int value; bool unresolved; bool fail; fail=false; if(contextStack->active) { if(haveLabel) { if(!isLocal) { if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!intermediatePass) { CreateListStringValue(listingRecord,value,unresolved); } fail=!AssignConstant(lineLabel,value,!unresolved); // assign it (even if it is unresolved, since this way we can remember that there was an attempt to equate it) } else { ReportBadOperands(); } } else { ReportBadOperands(); } } else { AssemblyComplaint(NULL,true,"'equ' requires a non-local label\n"); } } else { AssemblyComplaint(NULL,true,"'equ' requires a label\n"); } } return(!fail); } static bool HandleSet(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // SET statement, set label // If there is a problem, report it and return false { int value; bool unresolved; bool fail; fail=false; if(contextStack->active) { if(haveLabel) { if(!isLocal) { if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!intermediatePass) { CreateListStringValue(listingRecord,value,unresolved); } fail=!AssignSetConstant(lineLabel,value,!unresolved); // assign it (even if it is unresolved, since this way we can remember that there was an attempt to equate it) } else { ReportBadOperands(); } } else { ReportBadOperands(); } } else { AssemblyComplaint(NULL,true,"'set' requires a non-local label\n"); } } else { AssemblyComplaint(NULL,true,"'set' requires a label\n"); } } return(!fail); } static bool HandleUnSet(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // UNSET statement, clear previously existing set label // If there is a problem, report it and return false { bool fail; fail=false; if(contextStack->active) { if(haveLabel) { if(!isLocal) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { UnAssignSetConstant(lineLabel); // unassign it } else { ReportBadOperands(); } } else { ReportBadOperands(); } } else { AssemblyComplaint(NULL,true,"'unset' requires a label\n"); } } return(!fail); } static bool HandleMacro(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // A macro is being defined // If there is a problem, report it and return false // NOTE: even if the macro command was ill formed, we still enter the macro // context (with contextStack->active=false). This generates the least confusing error output. { bool fail; MACRO_RECORD *buildingMacro, *macroMatch; char macroName[MAXSTRING]; int i; bool wasActive; fail=false; wasActive=contextStack->active; if(PushContextRecord(0)) { contextStack->contextType=CT_MACRO; contextStack->active=false; if(wasActive) { if(haveLabel) { if(!isLocal) { i=0; while(lineLabel[i]) // make macro name lower case for later case insensitive matching { macroName[i]=tolower(lineLabel[i]); i++; } macroName[i]='\0'; if(!(macroMatch=MatchMacro(macroName))) // does this macro already exist? { if((buildingMacro=CreateMacro(macroName))) { if(CreateParameterLabels(theLine,lineIndex,&buildingMacro->parameters)) { collectingBlock=&buildingMacro->contents; // start collecting the text here (even if parameters are bad) if(!ParseComment(theLine,lineIndex)) { AssemblyComplaint(NULL,true,"Ill formed macro parameters\n"); } } else { DestroyMacro(buildingMacro); // failed to create parameter list fail=true; } } else { ReportComplaint(true,"Failed to create macro record\n"); fail=true; } } else { AssemblyComplaint(NULL,true,"Macro '%s' already defined\n",macroName); AssemblySupplement(¯oMatch->whereFrom,"Conflicting macro was defined here\n"); } } else { AssemblyComplaint(NULL,true,"'macro' requires a non-local label\n"); } } else { AssemblyComplaint(NULL,true,"'macro' requires a label\n"); } } } else { ReportComplaint(true,"Failed to create context record\n"); fail=true; } return(!fail); } static bool HandleEndMacro(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Process end of macro definition // If there is a problem, report it and return false { bool fail; fail=false; if(contextStack->contextType==CT_MACRO) { PopContextRecord(); if(contextStack->active) // parent was active, therefore we were collecting text into this macro { collectingBlock=NULL; // stop collecting the block, we have the macro now if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(!ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { ReportBadOperands(); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'endm' outside of 'macro'\n"); } } return(!fail); } static bool HandleIf(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Check condition, execute code immediately following if true, or after the optional else // if false. // If there is a problem, report it and return false // NOTE: if the expression is undefined, neither the if, or the else // part will be executed. The "if" statement will remain undefined until the last // pass which defines it, or until it cannot be defined { int value; bool unresolved; bool fail; bool wasActive; IF_CONTEXT *ifContext; fail=false; wasActive=contextStack->active; if(PushContextRecord(sizeof(IF_CONTEXT))) // change context { contextStack->contextType=CT_IF; contextStack->active=wasActive; ifContext=(IF_CONTEXT *)&contextStack->contextData[0]; ifContext->sawElse=false; ifContext->wantElse=false; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!intermediatePass) { CreateListStringValue(listingRecord,value,unresolved); } if(!unresolved) { if(value==0) // use the code below the "if" { contextStack->active=false; // go inactive, go active later on else if currently active ifContext->wantElse=true; } } else { contextStack->active=false; // unresolved, so take neither path } } else { ReportBadOperands(); contextStack->active=false; } } else { ReportBadOperands(); contextStack->active=false; } } } else { ReportComplaint(true,"Failed to create context record\n"); fail=true; } return(!fail); } static bool HandleIfdef(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Check to see if a label is defined, execute code immediately following if true, // or after the optional else if false. // If there is a problem, report it and return false { bool fail; bool wasActive; IF_CONTEXT *ifContext; char theLabel[MAXSTRING]; bool local; LABEL_RECORD *labelRecord; fail=false; wasActive=contextStack->active; if(PushContextRecord(sizeof(IF_CONTEXT))) // change context { contextStack->contextType=CT_IF; contextStack->active=wasActive; ifContext=(IF_CONTEXT *)&contextStack->contextData[0]; ifContext->sawElse=false; ifContext->wantElse=false; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } ParseWhiteSpace(theLine,lineIndex); if(ParseLabel(theLine,lineIndex,&theLabel[0],&local)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if((labelRecord=LocateLabel(theLabel))) // if we found it, it's defined (even if it is not resolved) { // already active, so no more needs to be done here } else { contextStack->active=false; // not defined, so take else path ifContext->wantElse=true; } } else { ReportBadOperands(); contextStack->active=false; } } else { ReportBadOperands(); contextStack->active=false; } } } else { ReportComplaint(true,"Failed to create context record\n"); fail=true; } return(!fail); } static bool HandleIfndef(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Check to see if a label is not defined, execute code immediately following if true, // or after the optional else if false. // If there is a problem, report it and return false { bool fail; bool wasActive; IF_CONTEXT *ifContext; char theLabel[MAXSTRING]; bool local; LABEL_RECORD *labelRecord; fail=false; wasActive=contextStack->active; if(PushContextRecord(sizeof(IF_CONTEXT))) // change context { contextStack->contextType=CT_IF; contextStack->active=wasActive; ifContext=(IF_CONTEXT *)&contextStack->contextData[0]; ifContext->sawElse=false; ifContext->wantElse=false; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } ParseWhiteSpace(theLine,lineIndex); if(ParseLabel(theLine,lineIndex,&theLabel[0],&local)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!(labelRecord=LocateLabel(theLabel))) { // already active, so no more needs to be done here } else { contextStack->active=false; // was defined, so want to take else path if there is one ifContext->wantElse=true; } } else { ReportBadOperands(); contextStack->active=false; } } else { ReportBadOperands(); contextStack->active=false; } } } else { ReportComplaint(true,"Failed to create context record\n"); fail=true; } return(!fail); } static bool HandleElse(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Else pseudo-op handling { IF_CONTEXT *ifContext; if(contextStack->contextType==CT_IF) { ifContext=(IF_CONTEXT *)&contextStack->contextData[0]; if(!ifContext->sawElse) { ifContext->sawElse=true; if(contextStack->active||ifContext->wantElse) { contextStack->active=ifContext->wantElse; if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(!ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { ReportBadOperands(); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'else' already seen\n"); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'else' without 'if'\n"); } } return(true); } static bool HandleEndIf(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Endif pseudo-op handling { if(contextStack->contextType==CT_IF) { PopContextRecord(); if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(!ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { ReportBadOperands(); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'endif' without 'if'\n"); } } return(true); } static bool HandleSwitch(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Evaluate an expression, find a case to match it, assemble code under the case // that it matches // If there is a problem, report it and return false // NOTE: if the expression is undefined, no case will be executed // if a case's expression is undefined, it will be considered a non-match // NOTE: a switch is inactive until the first case that matches it // NOTE: the switch goes inactive when a break is encountered { int value; bool unresolved; bool fail; bool wasActive; SWITCH_CONTEXT *switchContext; fail=false; wasActive=contextStack->active; if(PushContextRecord(sizeof(SWITCH_CONTEXT))) // change context { contextStack->contextType=CT_SWITCH; contextStack->active=false; // switch immediately goes inactive switchContext=(SWITCH_CONTEXT *)&contextStack->contextData[0]; switchContext->wantCase=false; switchContext->unresolved=true; switchContext->value=0; if(wasActive) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!intermediatePass) { CreateListStringValue(listingRecord,value,unresolved); } if(!unresolved) { switchContext->wantCase=true; switchContext->unresolved=false; switchContext->value=value; } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } } else { ReportComplaint(true,"Failed to create context record\n"); fail=true; } return(!fail); } static bool HandleCase(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Case pseudo-op handling { int value; bool unresolved; SWITCH_CONTEXT *switchContext; if(contextStack->contextType==CT_SWITCH) { switchContext=(SWITCH_CONTEXT *)&contextStack->contextData[0]; if(switchContext->wantCase) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!intermediatePass) { CreateListStringValue(listingRecord,value,unresolved); } if(!unresolved) { if(value==switchContext->value) { contextStack->active=true; // case is active, start assembling } } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'case' without 'switch'\n"); } } return(true); } static bool HandleBreak(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Break pseudo-op handling (part of switch) { SWITCH_CONTEXT *switchContext; if(contextStack->contextType==CT_SWITCH) { switchContext=(SWITCH_CONTEXT *)&contextStack->contextData[0]; contextStack->active=false; // honor the break if(switchContext->wantCase) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(!ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { ReportBadOperands(); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'break' without 'switch'\n"); } } return(true); } static bool HandleEndSwitch(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // End of switch statement { if(contextStack->contextType==CT_SWITCH) { PopContextRecord(); if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(!ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { ReportBadOperands(); } } } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'ends' without 'switch'\n"); } } return(true); } static void RepeatFlush(CONTEXT_RECORD *theRecord) // if an unterminated repeat context lingers at the end of assembly, this will get // called to clean it up { REPEAT_CONTEXT *repeatContext; repeatContext=(REPEAT_CONTEXT *)&contextStack->contextData[0]; DestroyTextBlockLines(&repeatContext->theTextBlock); } static bool HandleRepeat(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // A section of code which may be repeated is being defined // If there is a problem, report it and return false // This works by collecting the text up until the end opcode is seen, then // entering it into the stream as many times as requested { bool fail; int value; bool unresolved; bool wasActive; REPEAT_CONTEXT *repeatContext; fail=false; wasActive=contextStack->active; if(PushContextRecord(sizeof(REPEAT_CONTEXT))) { contextStack->contextType=CT_REPEAT; contextStack->active=false; contextStack->theFlush=RepeatFlush; repeatContext=(REPEAT_CONTEXT *)&contextStack->contextData[0]; repeatContext->numTimes=0; repeatContext->theTextBlock.firstLine=repeatContext->theTextBlock.lastLine=NULL; // no lines to be repeated if(wasActive) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseExpression(theLine,lineIndex,&value,&unresolved)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!intermediatePass) { CreateListStringValue(listingRecord,value,unresolved); } if(!unresolved) { repeatContext->numTimes=value; collectingBlock=&repeatContext->theTextBlock; // start collecting the text here } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } } else { ReportComplaint(true,"Failed to create context record\n"); fail=true; } return(!fail); } static bool HandleEndRepeat(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Process end of repeated code (enter the code back into the stream if the repeat count is > 0) // If there is a problem, report it and return false { bool fail; int i; REPEAT_CONTEXT *repeatContext; fail=false; if(contextStack->contextType==CT_REPEAT) { if(contextStack->next->active) // want to repeat within active parent context? { contextStack->active=true; // set our context active now so stuff below executes collectingBlock=NULL; // stop collecting the block, we have the repeated text now repeatContext=(REPEAT_CONTEXT *)&contextStack->contextData[0]; if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(!ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { ReportBadOperands(); } OutputListFileLine(listingRecord,theLine); // output the line first so that the repeat contents follow it listingRecord->wantList=false; for(i=0;!fail&&(inumTimes);i++) { fail=!ProcessTextBlock(&repeatContext->theTextBlock,NULL,NULL,'R'); } } RepeatFlush(contextStack); // flush out saved repeat lines PopContextRecord(); // get rid of this context record } else { if(contextStack->active) { AssemblyComplaint(NULL,true,"'endr' outside of 'repeat'\n"); } } return(!fail); } static bool HandleError(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Report user generated error { char errorString[MAXSTRING]; int stringLength; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseQuotedString(theLine,lineIndex,'"','"',errorString,&stringLength)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { AssemblyComplaint(NULL,true,"%s\n",errorString); } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(true); } static bool HandleWarning(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Report user generated warning { char warningString[MAXSTRING]; int stringLength; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseQuotedString(theLine,lineIndex,'"','"',warningString,&stringLength)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { AssemblyComplaint(NULL,false,"%s\n",warningString); } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(true); } static bool HandleMessage(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // Report user generated message { char messageString[MAXSTRING]; int stringLength; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseQuotedString(theLine,lineIndex,'"','"',messageString,&stringLength)) { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { AssemblySupplement(NULL,"%s\n",messageString); } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(true); } static bool HandleList(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // turn on output listing { if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(!outputListing) { listingRecord->wantList=false; // do not output the list command itself if we weren't listing before } outputListing=true; } else { ReportBadOperands(); } } return(true); } static bool HandleNoList(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // turn off output listing { if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { outputListing=false; } else { ReportBadOperands(); } } return(true); } static bool HandleExpand(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // turn on macro expansion in listing { if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { outputListingExpansions=true; } else { ReportBadOperands(); } } return(true); } static bool HandleNoExpand(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // turn off macro expansion in listing { if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { outputListingExpansions=false; } else { ReportBadOperands(); } } return(true); } static bool HandleProcessor(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // select processor to assemble for { char theElement[MAXSTRING]; int stringLength; bool fail; bool found; fail=false; if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseQuotedString(theLine,lineIndex,'"','"',theElement,&stringLength)||ParseFirstListElement(theLine,lineIndex,theElement)) // see which processor is being chosen { if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { if(SelectProcessor(theElement,&found)) { if(!found) { AssemblyComplaint(NULL,true,"Unrecognized processor '%s'\n",theElement); } } else { fail=true; } } else { ReportBadOperands(); } } else { ReportBadOperands(); } } return(!fail); } static bool HandleEnd(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // We have been told to stop parsing input { if(contextStack->active) { if(haveLabel) { ReportDisallowedLabel(lineLabel); } if(ParseComment(theLine,lineIndex)) // make sure there's nothing else on the line { stopParsing=true; // tell main loop to stop processing the current file } else { ReportBadOperands(); } } return(true); } bool HandleGlobalPseudoOpcode(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // A valid pseudo opcode has been parsed from the line, along with a possible // label. // Call a function to handle it { bool fail; fail=!theOpcode->theFunction(theOpcode,theLine,lineIndex,haveLabel,isLocal,lineLabel,listingRecord); return(!fail); } void UnInitGlobalPseudoOpcodes() // undo what InitGlobalPseudoOpcodes did { STDisposeSymbolTable(pseudoOpcodeSymbols); } bool InitGlobalPseudoOpcodes() // initialize symbol table for assembler pseudo-ops { unsigned int i; bool fail; fail=false; if((pseudoOpcodeSymbols=STNewSymbolTable())) { for(i=0;!fail&&(i