void LstWrite( enum lsttype type, uint_32 oldofs, void *value ) /*************************************************************/ { uint_32 newofs; struct asym *sym = value; int len; int len2; int idx; int srcfile; char *p1; char *p2; char *pSrcline; struct lstleft *pll; struct lstleft ll; //char buffer2[MAX_LINE_LEN]; /* stores text macro value */ if ( ModuleInfo.list == FALSE || CurrFile[LST] == NULL || ( ModuleInfo.line_flags & LOF_LISTED ) ) return; if ( ModuleInfo.GeneratedCode && ( ModuleInfo.list_generated_code == FALSE ) ) return; if ( MacroLevel ) { switch ( ModuleInfo.list_macro ) { case LM_NOLISTMACRO: return; case LM_LISTMACRO: /* todo: filter certain macro lines */ break; } } ModuleInfo.line_flags |= LOF_LISTED; DebugMsg1(("LstWrite( %u, %" I32_SPEC "u ): enter [ pos=%" I32_SPEC "u, GeneratedCode=%u, MacroLevel=%u ]\n", type, oldofs, list_pos, ModuleInfo.GeneratedCode, MacroLevel )); pSrcline = CurrSource; #if FASTPASS if ( ( Parse_Pass > PASS_1 ) && UseSavedState ) { if ( ModuleInfo.GeneratedCode == 0 ) { if ( !( ModuleInfo.line_flags & LOF_SKIPPOS ) ) list_pos = LineStoreCurr->list_pos; #if USELSLINE /* either use CurrSource + CurrComment or LineStoreCurr->line (see assemble.c, OnePass() */ pSrcline = LineStoreCurr->line; if ( ModuleInfo.CurrComment ) { /* if comment was removed, readd it! */ *( LineStoreCurr->line + strlen( LineStoreCurr->line ) ) = ';'; ModuleInfo.CurrComment = NULL; } #endif DebugMsg1(("LstWrite: Pass=%u, stored pos=%" I32_SPEC "u\n", Parse_Pass+1, list_pos )); } fseek( CurrFile[LST], list_pos, SEEK_SET ); } #endif ll.next = NULL; memset( ll.buffer, ' ', sizeof( ll.buffer ) ); srcfile = get_curr_srcfile(); switch ( type ) { case LSTTYPE_DATA: if ( Parse_Pass == PASS_1 && Options.first_pass_listing == FALSE ) { break; } /* no break */ case LSTTYPE_CODE: newofs = GetCurrOffset(); sprintf( ll.buffer, "%08" I32_SPEC "X", oldofs ); ll.buffer[OFSSIZE] = ' '; if ( CurrSeg == NULL ) break; //if ( write_to_file == FALSE ) if ( Options.first_pass_listing ) { if ( Parse_Pass > PASS_1 ) break; #ifdef DEBUG_OUT } else if ( Options.max_passes == 1 ) { ; /* write a listing in pass 1 */ #endif } else if ( Parse_Pass == PASS_1 ) /* changed v1.96 */ break; len = CODEBYTES; p2 = ll.buffer + OFSSIZE + 2; if ( CurrSeg->e.seginfo->CodeBuffer == NULL || CurrSeg->e.seginfo->written == FALSE ) { while ( oldofs < newofs && len ) { *p2++ = '0'; *p2++ = '0'; oldofs++; len--; } break; } /* OMF hold just a small buffer for one LEDATA record */ /* if it has been flushed, use LastCodeBufSize */ idx = (CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc) - (newofs - oldofs); if ( Options.output_format == OFORMAT_OMF ) { /* v2.11: additional check to make the hack more robust [ test case: db 800000h dup (0) ] */ if ( ( idx+LastCodeBufSize ) < 0 ) break; /* just exit. The code bytes area will remain empty */ while ( idx < 0 && len ) { sprintf( p2, "%02X", CurrSeg->e.seginfo->CodeBuffer[idx+LastCodeBufSize] ); p2 += 2; idx++; oldofs++; len--; } } else if (idx < 0) idx = 0; while ( oldofs < newofs && len ) { sprintf( p2, "%02X", CurrSeg->e.seginfo->CodeBuffer[idx] ); p2 += 2; idx++; oldofs++; len--; } *p2 = ' '; break; case LSTTYPE_EQUATE: /* v2.10: display current offset if equate is an alias for a label in this segment */ idx = 1; if ( sym->segment && sym->segment == &CurrSeg->sym ) { sprintf( ll.buffer, "%08" I32_SPEC "X", GetCurrOffset() ); idx = 10; } ll.buffer[idx] = '='; #if AMD64_SUPPORT if ( sym->value3264 != 0 && ( sym->value3264 != -1 || sym->value >= 0 ) ) sprintf( &ll.buffer[idx+2], "%-" PREFFMTSTR I64_SPEC "X", (uint_64)sym->value + ( (uint_64)sym->value3264 << 32 ) ); else #endif sprintf( &ll.buffer[idx+2], "%-" PREFFMTSTR I32_SPEC "X", sym->value ); ll.buffer[28] = ' '; break; case LSTTYPE_TMACRO: ll.buffer[1] = '='; //GetLiteralValue( buffer2, sym->string_ptr ); //strcpy( buffer2, sym->string_ptr ); for ( p1 = sym->string_ptr, p2 = &ll.buffer[3], pll = ≪ *p1; ) { if ( p2 >= &pll->buffer[28] ) { struct lstleft *next = myalloca( sizeof( struct lstleft ) ); pll->next = next; pll = next; pll->next = NULL; memset( pll->buffer, ' ', sizeof( pll->buffer) ); p2 = &pll->buffer[3]; } *p2++ = *p1++; } break; case LSTTYPE_MACROLINE: ll.buffer[1] = '>'; pSrcline = value; break; case LSTTYPE_LABEL: oldofs = GetCurrOffset(); /* no break */ case LSTTYPE_STRUCT: sprintf( ll.buffer, "%08" I32_SPEC "X", oldofs ); ll.buffer[8] = ' '; break; case LSTTYPE_DIRECTIVE: if ( CurrSeg || value ) { sprintf( ll.buffer, "%08" I32_SPEC "X", oldofs ); ll.buffer[8] = ' '; } break; default: /* LSTTYPE_MACRO */ if ( *pSrcline == NULLC && ModuleInfo.CurrComment == NULL && srcfile == ModuleInfo.srcfile ) { DebugMsg1(("LstWrite: type=%u, writing CRLF\n", type )); fwrite( NLSTR, 1, NLSIZ, CurrFile[LST] ); list_pos += NLSIZ; return; } break; } #if FASTPASS if ( Parse_Pass == PASS_1 || UseSavedState == FALSE ) { #endif idx = sizeof( ll.buffer ); if ( ModuleInfo.GeneratedCode ) ll.buffer[28] = '*'; if ( MacroLevel ) { len = sprintf( &ll.buffer[29], "%u", MacroLevel ); ll.buffer[29+len] = ' '; } if ( srcfile != ModuleInfo.srcfile ) { ll.buffer[30] = 'C'; } #ifdef DEBUG_OUT ll.last = NULLC; #endif #if FASTPASS } else { idx = OFSSIZE + 2 + 2 * CODEBYTES; #ifdef DEBUG_OUT ll.buffer[idx] = NULLC; #endif } #endif fwrite( ll.buffer, 1, idx, CurrFile[LST] ); len = strlen( pSrcline ); len2 = ( ModuleInfo.CurrComment ? strlen( ModuleInfo.CurrComment ) : 0 ); list_pos += sizeof( ll.buffer ) + len + len2 + NLSIZ; DebugMsg1(("LstWrite: writing (%u b) >%s< [%u/%u], new pos=%" I32_SPEC "u\n", idx, ll.buffer, len, len2, list_pos )); /* write source and comment part */ #if FASTPASS if ( Parse_Pass == PASS_1 || UseSavedState == FALSE ) { #endif if ( len ) fwrite( pSrcline, 1, len, CurrFile[LST] ); if ( len2 ) { fwrite( ModuleInfo.CurrComment, 1, len2, CurrFile[LST] ); DebugMsg1(("LstWrite: writing (%u b) >%s%s<\n", len + len2 + NLSIZ, pSrcline, ModuleInfo.CurrComment )); } #ifdef DEBUG_OUT else DebugMsg1(("LstWrite: writing (%u b) >%s<\n", len + NLSIZ, pSrcline )); #endif fwrite( NLSTR, 1, NLSIZ, CurrFile[LST] ); #if FASTPASS } #endif /* write optional additional lines. * currently works in pass one only. */ for ( pll = ll.next; pll; pll = pll->next ) { fwrite( pll->buffer, 1, 32, CurrFile[LST] ); fwrite( NLSTR, 1, NLSIZ, CurrFile[LST] ); list_pos += 32 + NLSIZ; DebugMsg1(("LstWrite: additional line >%s<, new pos=%" I32_SPEC "u\n", pll->buffer, list_pos )); } return; }
ret_code LoopDirective( int i, struct asm_tok tokenarray[] ) /**********************************************************/ { int directive = tokenarray[i].tokval; int arg_loc; int len; //int skipcomma; char *parmstring; char *ptr; struct dsym *macro; bool is_exitm; struct expr opnd; struct macro_info macinfo; struct dsym tmpmacro; #ifdef DEBUG_OUT uint_32 count = 0; #endif /* v2.08: use myalloca() to get space to store the argument */ //char line[MAX_LINE_LEN]; char buffer[4]; DebugMsg1(("LoopDirective(%s) enter\n", GetResWName( directive, NULL ) )); i++; /* skip directive */ if ( ModuleInfo.list == TRUE ) LstWriteSrcLine(); switch ( directive ) { case T_WHILE: arg_loc = i; /* no break */ case T_REPT: case T_REPEAT: /* the expression is "critical", that is, no forward * referenced symbols may be used here! */ if ( EvalOperand( &i, tokenarray, Token_Count, &opnd, EXPF_NOUNDEF ) == ERROR ) { opnd.value = 0; i = Token_Count; } else if ( opnd.kind != EXPR_CONST ) { /* syntax <REPEAT|WHILE 'A'> is valid! */ DebugMsg(( "LoopDirective(%s): invalid argument type %u\n", GetResWName( directive, NULL ), opnd.kind )); EmitError( CONSTANT_EXPECTED ); opnd.value = 0; } else if( tokenarray[i].token != T_FINAL ) { EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ); /* v2.09: don't exit, the macro lines must be read first. */ //return( ERROR ); opnd.value = 0; } break; default: /* FOR, FORC, IRP, IRPC */ /* get the formal parameter and the argument list */ /* the format parameter will become a macro parameter, so it can * be a simple T_ID, but also an instruction or something else. * v2.02: And it can begin with a '.'! */ if( tokenarray[i].token == T_FINAL ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i-1].tokpos ) ); } /* v2.02: allow parameter name to begin with a '.' */ //c = *tokenarray[i].string_ptr; //if( ( is_valid_id_char(c) == FALSE ) || ( isdigit(c) == TRUE ) ) { if( is_valid_id_first_char( *tokenarray[i].string_ptr ) == FALSE ) { DebugMsg(( "LoopDirective(FOR/FORC): token %s is not a valid parameter name\n", tokenarray[i].string_ptr )); return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) ); } arg_loc = i; i++; if( directive == T_FORC || directive == T_IRPC ) { if( tokenarray[i].token != T_COMMA ) { return( EmitErr( EXPECTING_COMMA, tokenarray[i].tokpos ) ); } i++; /* FORC/IRPC accepts anything as "argument list", even nothing! */ if( tokenarray[i].token == T_STRING && tokenarray[i].string_delim == '<' ) { len = tokenarray[i+1].tokpos - (tokenarray[i].tokpos+1); parmstring = myalloca( len ); //GetLiteralValue( parmstring, tokenarray[i].string_ptr ); memcpy( parmstring, tokenarray[i].tokpos+1, len ); while( *(parmstring+len-1) != '>' ) len--; *(parmstring+len-1) = NULLC; /* v2.02: if there's additional stuff behind the <> literal, * it's an error! */ if ( tokenarray[i+1].token != T_FINAL ) EmitErr( SYNTAX_ERROR_EX, tokenarray[i+1].tokpos ); } else { char *ptr2; ptr = tokenarray[i].tokpos; ptr2 = ptr; /* this is what Masm does: use the string until a space * is detected. Anything beyond the space is ignored. */ while ( *ptr2 && ( isspace( *ptr2 ) == FALSE ) ) ptr2++; len = ptr2 - ptr; parmstring = myalloca( len + 1 ); memcpy( parmstring, ptr, len ); *(parmstring+len) = NULLC; } } else { /* for FOR/IRP, skip everything between the name and the comma! * these items will be stored as (first) macro parameter. * for example, valid syntax is: * FOR xxx,<a, ...> * FOR xxx:REQ,<a, ...> */ while ( tokenarray[i].token != T_FINAL && tokenarray[i].token != T_COMMA ) i++; if( tokenarray[i].token != T_COMMA ) { return( EmitErr( EXPECTING_COMMA, tokenarray[i].tokpos ) ); } i++; /* FOR/IRP accepts a literal enclosed in <> only */ if( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) ); } /* v2.03: also ensure that the literal is the last item */ if( tokenarray[i+1].token != T_FINAL ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i+1].tokpos ) ); } /* v2.08: use myalloca() instead of a fixed-length buffer. * the loop directives are often nested, they call RunMacro() * and hence should be careful with stack usage because of JWASMR! */ //parmstring = myalloca( tokenarray[i].stringlen + 1 ); /* v2.0: use GetLiteralValue() instead of memcpy!!! */ //memcpy( line, tokenarray[i].string_ptr, tokenarray[i].stringlen + 1 ); //GetLiteralValue( parmstring, tokenarray[i].string_ptr ); parmstring = tokenarray[i].string_ptr; DebugMsg1(("LoopDirective(FOR): param string >%s<\n", parmstring)); } /* to run StoreMacro(), tokenarray must be setup correctly. */ /* clear contents beginning with the comma! */ i--; tokenarray[i].token = T_FINAL; Token_Count = i; i = arg_loc; } /* now make a temporary macro */ macro = &tmpmacro; memset( &tmpmacro, 0, sizeof(tmpmacro) ); tmpmacro.sym.name = ""; tmpmacro.e.macroinfo = &macinfo; memset( &macinfo, 0, sizeof(macinfo) ); macinfo.srcfile = get_curr_srcfile(); #if 0 //DEBUG_OUT if ( directive == T_WHILE ) tmpmacro.sym.name = "<WHILE>"; else if ( directive == T_REPEAT || directive == T_REPT ) tmpmacro.sym.name = "<REPT>"; else if ( directive == T_FORC || directive == T_IRPC ) tmpmacro.sym.name = "<FORC>"; else tmpmacro.sym.name = "<FOR>"; #endif DebugMsg1(("LoopDirective(%s): calling StoreMacro\n", GetResWName( directive, NULL ))); if( StoreMacro( macro, i, tokenarray, TRUE ) == ERROR ) { ReleaseMacroData( macro ); return( ERROR ); } /* EXITM <> is allowed inside a macro loop. * This doesn't make the loop a macro function, reset the bit! */ macro->sym.isfunc = FALSE; /* now run the just created macro in a loop */ /* don't run the macro if there are no lines (macroinfo->data == NULL)! * this isn't exactly what Masm does; an empty 'WHILE 1' * will loop "forever" in Masm, */ if ( macinfo.data ) /* added in v2.01 */ switch ( directive ) { case T_REPEAT: case T_REPT: /* negative repeat counts are accepted and are treated like 0 */ for ( ; macro->sym.value < opnd.value; macro->sym.value++ ) { /* v2.10: Token_Count becomes volatile if MF_NOSAVE is set */ tokenarray[0].token = T_FINAL; Token_Count = 0; //RunMacro( macro, Token_Count, tokenarray, NULL, MF_NOSAVE, &is_exitm ); RunMacro( macro, 0, tokenarray, NULL, MF_NOSAVE, &is_exitm ); if ( is_exitm ) break; DebugMsg1(("LoopDirective REPT: iteration=%" I32_SPEC "u\n", ++count )); } break; case T_WHILE: while ( opnd.kind == EXPR_CONST && opnd.value != 0 ) { DebugMsg1(("LoopDirective WHILE: cnt=%u\n", count++ )); RunMacro( macro, Token_Count, tokenarray, NULL, 0, &is_exitm ); if ( is_exitm ) break; i = arg_loc; if ( EvalOperand( &i, tokenarray, Token_Count, &opnd, 0 ) == ERROR ) break; macro->sym.value++; } break; case T_FORC: case T_IRPC: for( ptr = parmstring; *ptr; ptr++, macro->sym.value++ ) { tokenarray[0].token = T_STRING; tokenarray[0].string_delim = NULLC; tokenarray[0].string_ptr = buffer; tokenarray[0].tokpos = buffer; tokenarray[1].token = T_FINAL; buffer[2] = NULLC; Token_Count = 1; if ( *ptr == '!' ) { buffer[0] = *ptr++; buffer[1] = *ptr; if ( *ptr == NULLC ) /* ensure the macro won't go beyond the 00 */ ptr--; tokenarray[0].stringlen = 2; tokenarray[1].tokpos = buffer+2; } else if ( isspace( *ptr ) ) { buffer[0] = '!'; buffer[1] = *ptr; tokenarray[0].stringlen = 2; tokenarray[1].tokpos = buffer+2; } else { buffer[0] = *ptr; tokenarray[0].stringlen = 1; tokenarray[1].tokpos = buffer+1; buffer[1] = NULLC; } RunMacro( macro, 0, tokenarray, NULL, MF_NOSAVE, &is_exitm ); if ( is_exitm ) break; DebugMsg1(("LoopDirective FORC: call RunMacro(), cnt=%" I32_SPEC "u, param=>%s<\n", count++, buffer )); } break; default: /* T_FOR, T_IRP */ i = Token_Count + 1; Token_Count = Tokenize( parmstring, i, tokenarray, TOK_RESCAN | TOK_NOCURLBRACES ); DebugMsg1(("LoopDirective FOR: full param=>%s<\n", tokenarray[i].tokpos )); /* v2.09: if a trailing comma is followed by white space(s), add a blank token */ if ( i != Token_Count && tokenarray[Token_Count-1].token == T_COMMA && *(tokenarray[Token_Count-1].tokpos+1) ) { tokenarray[Token_Count].token = T_STRING; tokenarray[Token_Count].string_delim = NULLC; tokenarray[Token_Count].stringlen = strlen( tokenarray[Token_Count].tokpos ); tokenarray[Token_Count+1].tokpos = tokenarray[Token_Count].tokpos + tokenarray[Token_Count].stringlen; Token_Count++; tokenarray[Token_Count].token = T_FINAL; } /* a FOR/IRP parameter can be a macro function call */ /* that's why the macro calls must be run synchronously */ /* v2.05: reset an optional VARARG attribute for the macro * parameter. * take care of a trailing comma, this is to make another * RunMacro() call with a "blank" argument. */ macro->sym.mac_vararg = FALSE; /* v2.09: flag MF_IGNARGS introduced. This allows RunMacro() to * parse the full argument and trigger macro expansion if necessary. * No need anymore to count commas here. */ for( ; i < Token_Count; i++, macro->sym.value++ ) { DebugMsg1(("LoopDirective FOR: cnt=%" I32_SPEC "u, calling RunMacro( param=>%s< )\n", count++, tokenarray[i].tokpos )); i = RunMacro( macro, i, tokenarray, NULL, MF_IGNARGS, &is_exitm ); if ( i < 0 || is_exitm ) break; } } ReleaseMacroData( macro ); DebugMsg1(("LoopDirective(%s) exit\n", GetResWName( directive, NULL ) )); return( NOT_ERROR ); }