1 /****************************************************************************
2 *
3 *  This code is Public Domain.
4 *
5 *  ========================================================================
6 *
7 * Description:  implements hll directives:
8 *               .IF, .WHILE, .REPEAT, .ELSE, .ELSEIF, .ENDIF,
9 *               .ENDW, .UNTIL, .UNTILCXZ, .BREAK, .CONTINUE.
10 *               also handles C operators:
11 *               ==, !=, >=, <=, >, <, &&, ||, &, !,
12 *               and flags:
13 *               ZERO?, CARRY?, SIGN?, PARITY?, OVERFLOW?
14 *
15 ****************************************************************************/
16 
17 #include <ctype.h>
18 
19 #include "globals.h"
20 #include "memalloc.h"
21 #include "parser.h"
22 #include "equate.h"
23 #include "label.h"
24 #include "expreval.h"
25 #include "types.h"
26 #include "hll.h"
27 #include "segment.h"
28 #include "listing.h"
29 #include "lqueue.h"
30 #include "myassert.h"
31 
32 #define LABELSIZE 8
33 #define LABELSGLOBAL 0 /* make the generated labels global */
34 #define JMPPREFIX      /* define spaces before "jmp" or "loop" */
35 #define LABELFMT "@C%04X"
36 
37 /* v2.10: static variables moved to ModuleInfo */
38 #define HllStack ModuleInfo.g.HllStack
39 #define HllFree  ModuleInfo.g.HllFree
40 
41 #if LABELSGLOBAL
42 #define LABELQUAL "::"
43 #else
44 #define LABELQUAL ":"
45 #endif
46 
47 #ifdef DEBUG_OUT
48 #define EOLCHAR '^'  /* this allows better displays */
49 #define EOLSTR  "^"
50 #else
51 #define EOLCHAR '\n' /* line termination char in generated source */
52 #define EOLSTR  "\n"
53 #endif
54 
55 /* values for struct hll_item.cmd */
56 enum hll_cmd {
57     HLL_IF,
58     HLL_WHILE,
59     HLL_REPEAT,
60     HLL_BREAK  /* .IF behind .BREAK or .CONTINUE */
61 };
62 
63 /* index values for struct hll_item.labels[] */
64 enum hll_label_index {
65     LTEST,      /* test (loop) condition */
66     LEXIT,      /* block exit            */
67     LSTART,     /* loop start            */
68 };
69 
70 /* values for struct hll_item.flags */
71 enum hll_flags {
72     HLLF_ELSEOCCURED = 0x01,
73 };
74 
75 
76 /* item for .IF, .WHILE, .REPEAT, ... */
77 struct hll_item {
78     struct hll_item     *next;
79     uint_32             labels[3];      /* labels for LTEST, LEXIT, LSTART */
80     char                *condlines;     /* .WHILE-blocks only: lines to add after 'test' label */
81     enum hll_cmd        cmd;            /* start cmd (IF, WHILE, REPEAT) */
82     enum hll_flags      flags;          /* v2.08: added */
83     uint_32             truelabel;      /* v2.08: new member */
84     uint_32             deflabel;       /* v2.13: default, replaces ilabel argument, is one of labels[3] */
85     uint_32             falselabel;     /* v2.08: new member */
86 };
87 
88 enum hll_op {
89     HLLO_NONE,
90     HLLO_OR,
91     HLLO_AND
92 };
93 
94 /* v2.08: struct added */
95 struct hll_opnd {
96     enum  hll_op op; /* v2.13: added */
97     char *lastjmp;
98 };
99 
100 static ret_code GetExpression( struct hll_item *hll, int *i, struct asm_tok[], bool is_true, char *buffer, struct hll_opnd * );
101 
102 /* c binary ops.
103  * Order of items COP_EQ - COP_LE  and COP_ZERO - COP_OVERFLOW
104  * must not be changed.
105  */
106 enum c_bop {
107     COP_NONE,
108     COP_EQ,   /* == */
109     COP_NE,   /* != */
110     COP_GT,   /* >  */
111     COP_LT,   /* <  */
112     COP_GE,   /* >= */
113     COP_LE,   /* <= */
114     COP_AND,  /* && */
115     COP_OR,   /* || */
116     COP_ANDB, /* &  */
117     COP_NEG,  /* !  */
118     COP_ZERO, /* ZERO?   not really a valid C operator */
119     COP_CARRY,/* CARRY?  not really a valid C operator */
120     COP_SIGN, /* SIGN?   not really a valid C operator */
121     COP_PARITY,  /* PARITY?   not really a valid C operator */
122     COP_OVERFLOW /* OVERFLOW? not really a valid C operator */
123 };
124 
125 /* items in table below must match order COP_ZERO - COP_OVERFLOW */
126 static const char flaginstr[] = { 'z',  'c',  's',  'p',  'o' };
127 
128 /* items in tables below must match order COP_EQ - COP_LE */
129 static const char unsigned_cjmptype[] = {  'z',  'z',  'a',  'b',  'b', 'a' };
130 static const char signed_cjmptype[]   = {  'z',  'z',  'g',  'l',  'l', 'g' };
131 static const char neg_cjmptype[]      = {   0,    1,    0,    0,    1,   1  };
132 
133 /* in Masm, there's a nesting level limit of 20. In JWasm, there's
134  * currently no limit.
135  */
136 
137 #ifdef DEBUG_OUT
138 static unsigned evallvl;
139 static unsigned cntAlloc;  /* # of allocated hll_items */
140 static unsigned cntReused; /* # of reused hll_items */
141 static unsigned cntCond;   /* # of allocated 'condlines'-buffer in .WHILE-blocks */
142 static unsigned cntCondBytes; /* total size of allocated 'condlines'-buffers */
143 #endif
144 
GetHllLabel(void)145 static uint_32 GetHllLabel( void )
146 /********************************/
147 {
148     return ( ++ModuleInfo.hll_label );
149 }
150 
151 /* get a C binary operator from the token stream.
152  * there is a problem with the '<' because it is a "string delimiter"
153  * which Tokenize() usually is to remove.
154  * There has been a hack implemented in Tokenize() so that it won't touch the
155  * '<' if .IF, .ELSEIF, .WHILE, .UNTIL, .UNTILCXZ or .BREAK/.CONTINUE has been
156  * detected.
157  */
158 
159 #define CHARS_EQ  '=' + ( '=' << 8 )
160 #define CHARS_NE  '!' + ( '=' << 8 )
161 #define CHARS_GE  '>' + ( '=' << 8 )
162 #define CHARS_LE  '<' + ( '=' << 8 )
163 #define CHARS_AND '&' + ( '&' << 8 )
164 #define CHARS_OR  '|' + ( '|' << 8 )
165 
GetCOp(struct asm_tok * item)166 static enum c_bop GetCOp( struct asm_tok *item )
167 /**********************************************/
168 {
169     int size;
170     enum c_bop rc;
171     char *p = item->string_ptr;
172 
173     size = ( item->token == T_STRING ? item->stringlen : 0 );
174 
175     if ( size == 2 ) {
176         switch ( *(uint_16 *)p ) {
177         case CHARS_EQ:  rc = COP_EQ;  break;
178         case CHARS_NE:  rc = COP_NE;  break;
179         case CHARS_GE:  rc = COP_GE;  break;
180         case CHARS_LE:  rc = COP_LE;  break;
181         case CHARS_AND: rc = COP_AND; break;
182         case CHARS_OR:  rc = COP_OR;  break;
183         default: return( COP_NONE );
184         }
185     } else if ( size == 1 ) {
186         switch ( *p ) {
187         case '>': rc = COP_GT;   break;
188         case '<': rc = COP_LT;   break;
189         case '&': rc = COP_ANDB; break;
190         case '!': rc = COP_NEG;  break;
191         default: return( COP_NONE );
192         }
193     } else {
194         if ( item->token != T_ID )
195             return( COP_NONE );
196         /* a valid "flag" string must end with a question mark */
197         size = strlen( p );
198         if ( *(p+size-1) != '?' )
199             return( COP_NONE );
200         if ( size == 5 && ( 0 == _memicmp( p, "ZERO", 4 ) ) )
201             rc = COP_ZERO;
202         else if ( size == 6 && ( 0 == _memicmp( p, "CARRY", 5 ) ) )
203             rc = COP_CARRY;
204         else if ( size == 5 && ( 0 == _memicmp( p, "SIGN", 4 ) ) )
205             rc = COP_SIGN;
206         else if ( size == 7 && ( 0 == _memicmp( p, "PARITY", 6 ) ) )
207             rc = COP_PARITY;
208         else if ( size == 9 && ( 0 == _memicmp( p, "OVERFLOW", 8 ) ) )
209             rc = COP_OVERFLOW;
210         else
211             return( COP_NONE );
212     }
213     return( rc );
214 }
215 
216 /* render an instruction */
217 
RenderInstr(char * dst,const char * instr,int start1,int end1,int start2,int end2,struct asm_tok tokenarray[])218 static char *RenderInstr( char *dst, const char *instr, int start1, int end1, int start2, int end2, struct asm_tok tokenarray[] )
219 /*******************************************************************************************************************************/
220 {
221     int i;
222 #ifdef DEBUG_OUT
223     char *old = dst;
224 #endif
225     i = strlen( instr );
226     /* copy the instruction */
227     memcpy( dst, instr, i );
228     dst += i;
229     /* copy the first operand's tokens */
230     *dst++ = ' ';
231     i = tokenarray[end1].tokpos - tokenarray[start1].tokpos;
232     memcpy( dst, tokenarray[start1].tokpos, i );
233     dst += i;
234     if ( start2 != EMPTY ) {
235         *dst++ = ',';
236         /* copy the second operand's tokens */
237         *dst++ = ' ';
238         i = tokenarray[end2].tokpos - tokenarray[start2].tokpos;
239         memcpy( dst, tokenarray[start2].tokpos, i );
240         dst += i;
241     } else if ( end2 != EMPTY ) {
242         dst += sprintf( dst, ", %d", end2 );
243     }
244     *dst++ = EOLCHAR;
245     *dst = NULLC;
246     DebugMsg1(("%u RenderInstr(%s)=>%s<\n", evallvl, instr, old ));
247     return( dst );
248 }
249 
GetLabelStr(int_32 label,char * buff)250 static char *GetLabelStr( int_32 label, char *buff )
251 /**************************************************/
252 {
253     sprintf( buff, LABELFMT, label );
254     return( buff );
255 }
256 
257 /* render a Jcc instruction */
258 
RenderJcc(char * dst,char cc,int neg,uint_32 label)259 static char *RenderJcc( char *dst, char cc, int neg, uint_32 label )
260 /******************************************************************/
261 {
262 #ifdef DEBUG_OUT
263     char *old = dst;
264 #endif
265     /* create the jump opcode: j[n]cc */
266     *dst++ = 'j';
267     if ( neg )
268         *dst++ = 'n';
269     *dst++ = cc;
270     if ( neg == FALSE )
271         *dst++ = ' '; /* make sure there's room for the inverse jmp */
272 
273     *dst++ = ' ';
274     GetLabelStr( label, dst );
275     dst += strlen( dst );
276     *dst++ = EOLCHAR;
277     *dst = NULLC;
278     DebugMsg1(("%u RenderJcc()=>%s<\n", evallvl, old ));
279     return( dst );
280 }
281 
282 /* a "token" in a C expression actually is an assembly expression */
283 
GetToken(struct hll_item * hll,int * i,struct asm_tok tokenarray[],struct expr * opnd)284 static ret_code GetToken( struct hll_item *hll, int *i, struct asm_tok tokenarray[], struct expr *opnd )
285 /******************************************************************************************************/
286 {
287     int end_tok;
288 
289     /* scan for the next C operator in the token array.
290      * because the ASM evaluator may report an error if such a thing
291      * is found ( CARRY?, ZERO? and alikes will be regarded as - not yet defined - labels )
292      */
293     for ( end_tok = *i; end_tok < Token_Count; end_tok++ ) {
294         if ( ( GetCOp( &tokenarray[end_tok] ) ) != COP_NONE )
295             break;
296     }
297     if ( end_tok == *i ) {
298         opnd->kind = EXPR_EMPTY;
299         return( NOT_ERROR );
300     }
301     if ( ERROR == EvalOperand( i, tokenarray, end_tok, opnd, 0 ) )
302         return( ERROR );
303 
304     /* v2.11: emit error 'syntax error in control flow directive'.
305      * May happen for expressions like ".if 1 + CARRY?"
306      */
307     if ( *i > end_tok ) {
308         return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
309     }
310 
311     return( NOT_ERROR );
312 }
313 
314 /* a "simple" expression is
315  * 1. two tokens, coupled with a <cmp> operator: == != >= <= > <
316  * 2. two tokens, coupled with a "&" operator
317  * 3. unary operator "!" + one token
318  * 4. one token (short form for "<token> != 0")
319  */
GetSimpleExpression(struct hll_item * hll,int * i,struct asm_tok tokenarray[],bool is_true,char * buffer,struct hll_opnd * hllop)320 static ret_code GetSimpleExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], bool is_true, char *buffer, struct hll_opnd *hllop )
321 /**************************************************************************************************************************************************/
322 {
323     enum c_bop op;
324     char instr;
325     int op1_pos;
326     int op1_end;
327     int op2_pos;
328     int op2_end;
329     char *p;
330     struct expr op1;
331     struct expr op2;
332     uint_32 label;
333 
334     DebugMsg1(("%u GetSimpleExpression(>%.32s< buf=>%s<) enter\n", evallvl, tokenarray[*i].tokpos, buffer ));
335 
336     while ( tokenarray[*i].string_ptr[0] == '!' && tokenarray[*i].string_ptr[1] == '\0' ) {
337         (*i)++; //GetCOp( i );
338         is_true = 1 - is_true;
339     }
340 
341     /* the problem with '()' is that is might enclose just a standard Masm
342      * expression or a "hll" expression. The first case is to be handled
343      * entirely by the expression evaluator, while the latter case is to be
344      * handled HERE!
345      */
346     if ( tokenarray[*i].token == T_OP_BRACKET ) {
347         int brcnt;
348         int j;
349         for ( brcnt = 1, j = *i + 1; tokenarray[j].token != T_FINAL; j++ ) {
350             if ( tokenarray[j].token == T_OP_BRACKET )
351                 brcnt++;
352             else if ( tokenarray[j].token == T_CL_BRACKET ) {
353                 brcnt--;
354                 if ( brcnt == 0 ) /* a standard Masm expression? */
355                     break;
356             } else if ( ( GetCOp( &tokenarray[j] )) != COP_NONE )
357                 break;
358         }
359         if ( brcnt ) {
360             (*i)++;
361             DebugMsg1(("%u GetSimpleExpression: calling GetExpression, i=%u\n", evallvl, *i));
362             if ( ERROR == GetExpression( hll, i, tokenarray, is_true, buffer, hllop ) )
363                 return( ERROR );
364 
365             if ( tokenarray[*i].token != T_CL_BRACKET ) {
366                 //if (( tokenarray[*i].token == T_FINAL ) || ( tokenarray[*i].token == T_CL_BRACKET ))
367                 DebugMsg(( "GetSimpleExpression: expected ')', found: %s\n", tokenarray[*i].string_ptr ));
368                 return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
369             }
370             (*i)++;
371             return( NOT_ERROR );
372         }
373     }
374 
375     /* get (first) operand */
376     op1_pos = *i;
377     if ( ERROR == GetToken( hll, i, tokenarray, &op1 ) )
378         return ( ERROR );
379     op1_end = *i;
380 
381     op = GetCOp( &tokenarray[*i] ); /* get operator */
382 
383     /* lower precedence operator ( && or || ) detected? */
384     if ( op == COP_AND || op == COP_OR ) {
385         /* v2.11: next 2 lines removed - && and || operators need a valid first operand */
386         //if ( op1.kind == EXPR_EMPTY )
387         //    return( NOT_ERROR );
388         op = COP_NONE;
389     } else if ( op != COP_NONE )
390         (*i)++;
391 
392     label = hll->deflabel;
393 
394     DebugMsg1(("%u GetSimpleExpression: EvalOperand ok, kind=%X, i=%u [%s]\n", evallvl, op1.kind, *i, tokenarray[*i].tokpos ));
395 
396     /* check for special operators with implicite operand:
397      * COP_ZERO, COP_CARRY, COP_SIGN, COP_PARITY, COP_OVERFLOW
398      */
399     if ( op >= COP_ZERO ) {
400         if ( op1.kind != EXPR_EMPTY ) {
401             DebugMsg(( "GetSimpleExpression: non-empty expression rejected: %s\n", tokenarray[op1_pos].tokpos ));
402             return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
403         }
404         p = buffer;
405         hllop->lastjmp = p;
406         RenderJcc( p, flaginstr[ op - COP_ZERO ], !is_true, label );
407         return( NOT_ERROR );
408     }
409 
410     switch ( op1.kind ) {
411     case EXPR_EMPTY:
412         DebugMsg(( "GetSimpleExpression: empty expression rejected\n" ));
413         return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) ); /* v2.09: changed from NOT_ERROR to ERROR */
414     case EXPR_FLOAT:
415         DebugMsg(( "GetSimpleExpression: float expression rejected: %s\n", tokenarray[op1_pos].tokpos ));
416         return( EmitError( REAL_OR_BCD_NUMBER_NOT_ALLOWED ) ); /* v2.10: added */
417     }
418 
419     if ( op == COP_NONE ) {
420         switch ( op1.kind ) {
421         case EXPR_REG:
422             if ( op1.indirect == FALSE ) {
423                 p = RenderInstr( buffer, "and", op1_pos, op1_end, op1_pos, op1_end, tokenarray );
424                 hllop->lastjmp = p;
425                 RenderJcc( p, 'z', is_true, label );
426                 break;
427             }
428             /* no break */
429         case EXPR_ADDR:
430             p = RenderInstr( buffer, "cmp", op1_pos, op1_end, EMPTY, 0, tokenarray );
431             hllop->lastjmp = p;
432             RenderJcc( p, 'z', is_true, label );
433             break;
434         case EXPR_CONST:
435 #if 0
436             /* v2.05: string constant is allowed! */
437             if ( op1.string != NULL ) {
438                 return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
439             }
440 #endif
441             /* v2.11: error if constant doesn't fit in 32-bits */
442             if ( op1.hvalue != 0 && op1.hvalue != -1 )
443                 return( EmitConstError( &op1 ) );
444 
445             hllop->lastjmp = buffer;
446 
447             if ( ( is_true == TRUE && op1.value ) ||
448                 ( is_true == FALSE && op1.value == 0 ) ) {
449                 sprintf( buffer, "jmp " LABELFMT EOLSTR, label );
450                 DebugMsg1(("%u GetSimpleExpression: jmp created >%s<\n", evallvl, buffer ));
451             } else {
452                 //strcpy( buffer, " " EOLSTR ); /* v2.11: obsolete */
453                 *buffer = NULLC;
454                 DebugMsg1(("%u GetSimpleExpression: nullc created\n", evallvl ));
455             }
456             break;
457 #ifdef DEBUG_OUT
458         default: /**/myassert( 0 ); break;
459 #endif
460         }
461         return( NOT_ERROR );
462     }
463 
464     /* get second operand for binary operator */
465     op2_pos = *i;
466     if ( ERROR == GetToken( hll, i, tokenarray, &op2 ) ) {
467         return( ERROR );
468     }
469     DebugMsg1(("%u GetSimpleExpression: EvalOperand 2 ok, type=%X, i=%u [%s]\n", evallvl, op2.type, *i, tokenarray[*i].tokpos));
470     if ( op2.kind != EXPR_CONST && op2.kind != EXPR_ADDR && op2.kind != EXPR_REG ) {
471         DebugMsg(("GetSimpleExpression: syntax error, op2.kind=%u\n", op2.kind ));
472         return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
473     }
474     op2_end = *i;
475 
476     /* now generate ASM code for expression */
477 
478     if ( op == COP_ANDB ) {
479         p = RenderInstr( buffer, "test", op1_pos, op1_end, op2_pos, op2_end, tokenarray );
480         hllop->lastjmp = p;
481         RenderJcc( p, 'e', is_true, label );
482     } else if ( op <= COP_LE ) { /* ==, !=, >, <, >= or <= operator */
483         /*
484          * optimisation: generate 'or EAX,EAX' instead of 'cmp EAX,0'.
485          * v2.11: use op2.value64 instead of op2.value
486          */
487         if ( Options.masm_compat_gencode &&
488             ( op == COP_EQ || op == COP_NE ) &&
489             op1.kind == EXPR_REG && op1.indirect == FALSE &&
490             op2.kind == EXPR_CONST && op2.value64 == 0 ) {
491             p = RenderInstr( buffer, "or", op1_pos, op1_end, op1_pos, op1_end, tokenarray );
492         } else {
493             p = RenderInstr( buffer, "cmp", op1_pos, op1_end, op2_pos, op2_end, tokenarray );
494         }
495 
496         instr = ( ( IS_SIGNED( op1.mem_type ) || IS_SIGNED( op2.mem_type ) ) ? signed_cjmptype[op - COP_EQ] : unsigned_cjmptype[op - COP_EQ] );
497         hllop->lastjmp = p;
498         RenderJcc( p, instr, neg_cjmptype[op - COP_EQ] ? is_true : !is_true, label );
499     } else {
500         DebugMsg(("GetSimpleExpression: unexpected operator %s\n", tokenarray[op1_pos].tokpos ));
501         return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
502     }
503     return( NOT_ERROR );
504 }
505 
506 /* invert a Jump:
507  * - Jx  -> JNx (x = e|z|c|s|p|o )
508  * - JNx -> Jx  (x = e|z|c|s|p|o )
509  * - Ja  -> Jbe, Jae -> Jb
510  * - Jb  -> Jae, Jbe -> Ja
511  * - Jg  -> Jle, Jge -> Jl
512  * - Jl  -> Jge, Jle -> Jg
513  * added in v2.11:
514  * - jmp -> 0
515  * - 0   -> jmp
516  */
InvertJump(char * p)517 static void InvertJump( char *p )
518 /*******************************/
519 {
520     if ( *p == NULLC ) { /* v2.11: convert 0 to "jmp" */
521         strcpy( p, "jmp " );
522         return;
523     }
524 
525     p++;
526     if ( *p == 'e' || *p == 'z' || *p == 'c' || *p == 's' || *p == 'p' || *p == 'o' ) {
527         *(p+1) = *p;
528         *p = 'n';
529         return;
530     } else if ( *p == 'n' ) {
531         *p = *(p+1);
532         *(p+1) = ' ';
533         return;
534     } else if ( *p == 'a' ) {
535         *p++ = 'b';
536     } else if ( *p == 'b' ) {
537         *p++ = 'a';
538     } else if ( *p == 'g' ) {
539         *p++ = 'l';
540     } else if ( *p == 'l' ) {
541         *p++ = 'g';
542     } else {
543         /* v2.11: convert "jmp" to 0 */
544         if ( *p == 'm' ) {
545             p--;
546             *p = NULLC;
547         }
548         return;
549     }
550     if ( *p == 'e' )
551         *p = ' ';
552     else
553         *p = 'e';
554     return;
555 }
556 
557 /* Replace a label in the source lines generated so far.
558  * todo: if more than 0xFFFF labels are needed,
559  * it may happen that length of nlabel > length of olabel!
560  * then the simple memcpy() below won't work!
561  */
562 
ReplaceLabel(char * p,uint_32 olabel,uint_32 nlabel)563 static bool ReplaceLabel( char *p, uint_32 olabel, uint_32 nlabel )
564 /*****************************************************************/
565 {
566     char oldlbl[16];
567     char newlbl[16];
568     int i;
569     bool ret = FALSE;
570 
571     GetLabelStr( olabel, oldlbl );
572     GetLabelStr( nlabel, newlbl );
573 
574     i = strlen( newlbl );
575 
576     DebugMsg1(("%u ReplaceLabel(%s->%s, >%s<)\n", evallvl, oldlbl, newlbl, p ));
577     while ( p = strstr( p, oldlbl ) ) {
578         memcpy( p, newlbl, i );
579         p += i;
580         ret = TRUE;
581     }
582     return( ret );
583 }
584 
585 /* operator &&, which has the second lowest precedence, is handled here */
586 
GetAndExpression(struct hll_item * hll,int * i,struct asm_tok tokenarray[],bool is_true,char * buffer,struct hll_opnd * hllop)587 static ret_code GetAndExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], bool is_true, char *buffer, struct hll_opnd *hllop )
588 /***********************************************************************************************************************************************/
589 {
590     char *ptr = buffer;
591     char opfound = FALSE;
592 
593     DebugMsg1(("%u GetAndExpression(>%.32s< buf=>%s<) enter, truelabel=%u\n", evallvl, tokenarray[*i].tokpos, buffer, hll->truelabel ));
594 
595     if ( ERROR == GetSimpleExpression( hll, i, tokenarray, is_true, ptr, hllop ) )
596         return( ERROR );
597     while ( COP_AND == GetCOp( &tokenarray[*i] ) ) {
598 
599         struct hll_opnd hllop2 = {HLLO_NONE,NULL};
600         char buff[16];
601 
602         (*i)++;
603         DebugMsg1(("%u GetAndExpression: &&-operator found, is_true=%u, lastjmp=%s, truelabel=%u\n",
604             evallvl, is_true, hllop->lastjmp ? hllop->lastjmp : "NULL", hll->truelabel ));
605 
606         if ( hllop->op == HLLO_OR ) {
607             ptr = buffer+strlen( buffer );
608             if (!hll->truelabel) hll->truelabel = GetHllLabel();
609             GetLabelStr( hll->truelabel, ptr );
610             strcat( ptr, LABELQUAL EOLSTR );
611             DebugMsg1(("%u GetAndExpression: label appended >%s<, truelabel=%u\n", evallvl, ptr, hll->truelabel ));
612             if (is_true) /* todo: explain why this is needed for &&, but not for || */
613                 ReplaceLabel( buffer, hll->deflabel, hll->truelabel );
614             hllop->op = HLLO_NONE;
615             hll->truelabel = 0;
616         }
617         if ( is_true ) {
618             if ( hllop->lastjmp ) {
619                 /* the last jmp has to be inverted and the label modified */
620                 char *p = hllop->lastjmp;
621                 InvertJump( p );           /* step 1 */
622                 if ( hll->falselabel == 0 ) {/* step 2 */
623                     hll->falselabel = GetHllLabel();
624                     opfound = TRUE;
625                 }
626                 if ( *p ) {               /* v2.11: there might be a 0 at lastjmp */
627                     p += 4;               /* skip 'jcc ' or 'jmp ' */
628                     /* v2.13: don't skip a label that might be behind lastjmp */
629                     //GetLabelStr( hllop->truelabel, p );
630                     //strcat( p, EOLSTR );
631                     GetLabelStr( hll->falselabel, buff );
632                     DebugMsg1(("%u GetAndExpression: lastjmp to be changed: >%s< ->%s\n", evallvl, hllop->lastjmp, buff ));
633                     strcat( buff, EOLSTR );
634                     if (*p)
635                         memcpy( p, buff, strlen(buff) );
636                     else
637                         strcpy( p, buff );
638                 }
639                 /* v2.13 removed */
640                 //ReplaceLabel( buffer, hll->deflabel, hll->truelabel );
641                 DebugMsg1(("%u GetAndExpression: falselabel=%u, buffer >%s<\n", evallvl, hll->falselabel, buffer ));
642                 hllop->lastjmp = NULL;
643                 //opfound = TRUE;
644             }
645         }
646 
647         ptr += strlen( ptr );
648         if ( ERROR == GetSimpleExpression( hll, i, tokenarray, is_true, ptr, &hllop2 ) )
649             return( ERROR );
650         memcpy( hllop, &hllop2, sizeof(hllop2) );
651     };
652     if (opfound)
653         hllop->op = HLLO_AND;
654     DebugMsg1(("%u GetAndExpression: exit, truelabel=%u, opfound=%u\n", evallvl, hll->truelabel, opfound ));
655     return( NOT_ERROR );
656 }
657 
658 /* operator ||, which has the lowest precedence, is handled here */
659 
GetExpression(struct hll_item * hll,int * i,struct asm_tok tokenarray[],bool is_true,char * buffer,struct hll_opnd * hllop)660 static ret_code GetExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], bool is_true, char *buffer, struct hll_opnd *hllop )
661 /********************************************************************************************************************************************/
662 {
663     char *ptr = buffer;
664     uint_32 truelabel = 0;
665     char opfound = FALSE;
666 
667     DebugMsg1(("%u GetExpression(>%.32s< is_true=%u buf=>%s<) enter\n", ++evallvl, tokenarray[*i].tokpos, is_true, buffer ));
668 
669     /* v2.08: structure changed from for(;;) to while() to increase
670      * readability and - optionally - handle the second operand differently
671      * than the first.
672      */
673 
674     if ( ERROR == GetAndExpression( hll, i, tokenarray, is_true, ptr, hllop ) ) {
675         DebugMsg1(("%u GetExpression exit, error\n", evallvl-- ));
676         return( ERROR );
677     }
678     while ( COP_OR == GetCOp( &tokenarray[*i] ) ) {
679 
680         uint_32 nlabel;
681         uint_32 olabel;
682         struct hll_opnd hllop2 = {HLLO_NONE,NULL};
683         char buff[16];
684 
685         /* what's done inside the loop:
686          1. check if the preceding expression needs a "terminating" label ( hllop->op != HLLO_NONE )
687             if so, create the label (truelabel)
688          2. create a (new) truelabel
689          3. if is_true == FALSE, the last Jcc target must be inverted and the label replaced
690          4. get the next expression
691          */
692 
693         (*i)++;
694         DebugMsg1(("%u GetExpression: ||-operator found, is_true=%u, lastjmp=%s, truelabel=%u\n", evallvl, is_true, hllop->lastjmp ? hllop->lastjmp : "NULL", hll->truelabel ));
695 
696         if ( hllop->op == HLLO_AND ) {
697             ptr = buffer+strlen( buffer );
698             GetLabelStr( hll->falselabel, ptr );
699             strcat( ptr, LABELQUAL EOLSTR );
700             DebugMsg1(("%u GetExpression: label appended >%s<, falselabel=%u\n", evallvl, ptr, hll->falselabel ));
701             //ReplaceLabel( buffer, hll->deflabel, hll->truelabel );
702             hllop->op = HLLO_NONE;
703             hll->falselabel = 0;
704         }
705         opfound = TRUE;
706         if ( truelabel == 0 ) {
707             truelabel = GetHllLabel();
708             DebugMsg1(("%u GetExpression: new truelabel=%u\n", evallvl, truelabel ));
709         }
710         if ( is_true == FALSE ) {
711             if ( hllop->lastjmp ) {
712                 char *p = hllop->lastjmp;
713                 InvertJump( p );
714                 if ( *p ) { /* v2.11: there might be a 0 at lastjmp */
715                     p += 4;                /* skip 'jcc ' or 'jmp ' */
716                     GetLabelStr( truelabel, p );
717                     strcat( p, EOLSTR );
718                 }
719                 DebugMsg1(("%u GetExpression: jmp inverted(%s), truelabel=%u\n", evallvl, hllop->lastjmp, truelabel ));
720 
721                 nlabel = GetHllLabel();
722                 olabel = hll->deflabel;
723                 if ( ReplaceLabel( buffer, olabel, nlabel )) {
724                     sprintf( ptr + strlen( ptr ), "%s" LABELQUAL EOLSTR, GetLabelStr( nlabel, buff ) );
725                     DebugMsg1(("%u GetExpression: dest changed, label added >%s<\n", evallvl, ptr ));
726                 } else
727                     ModuleInfo.hll_label--;
728 
729                 hllop->lastjmp = NULL;
730             }
731         }
732         ptr += strlen( ptr );
733         if ( ERROR == GetAndExpression( hll, i, tokenarray, is_true, ptr, &hllop2 ) ) {
734             DebugMsg1(("%u GetExpression exit, error\n", evallvl-- ));
735             return( ERROR );
736         }
737         memcpy( hllop, &hllop2, sizeof(hllop2) );
738     }
739     if (opfound) {
740         hllop->op = HLLO_OR;
741         DebugMsg1(("%u GetExpression, HLLO_OR set, truelabels=%u/%u\n", evallvl, truelabel, hll->truelabel ));
742         if ( truelabel )
743             if ( hll->truelabel )
744                 ReplaceLabel( buffer, truelabel, hll->truelabel );
745             else
746                 hll->truelabel = truelabel;
747     }
748     DebugMsg1(("%u GetExpression exit, truelabel=%u, opfound=%u >%.32s<\n", evallvl--, hll->truelabel, opfound, tokenarray[*i].tokpos ));
749     return( NOT_ERROR );
750 }
751 
752 /*
753  * write assembly test lines to line queue.
754  * v2.11: local line buffer removed; src pointer has become a parameter.
755  */
756 
QueueTestLines(char * src)757 static ret_code QueueTestLines( char *src )
758 /*****************************************/
759 {
760     char *start;
761 
762     DebugMsg1(("QueueTestLines(\"%s\") enter\n", src ? src : "NULL" ));
763     while ( src ) {
764         //if (*src == ' ') src++; /* v2.11: obsolete */
765         start = src;
766         if ( src = strchr( src, EOLCHAR ) )
767             *src++ = NULLC;
768         if ( *start )
769             AddLineQueue( start );
770     }
771 
772     DebugMsg1(("QueueTestLines exit\n"));
773     return( NOT_ERROR );
774 }
775 
776 /*
777  * evaluate the C like boolean expression found in HLL structs
778  * like .IF, .ELSEIF, .WHILE, .UNTIL and .UNTILCXZ
779  * might return multiple lines (strings separated by EOLCHAR)
780  * - i = index for tokenarray[] where expression starts. Is restricted
781  *       to one source line (till T_FINAL)
782  * - ilabel: label to jump to if expression is <is_true>!
783  *
784  *              is_true    label
785  * ------------------------------------
786  *   .IF:       FALSE      LTEST
787  *   .ELSEIF:   FALSE      LTEST
788  *   .WHILE:    TRUE       LSTART
789  *   .UNTIL:    FALSE      LSTART
790  *   .UNTILCXZ: FALSE      LSTART
791  *   .BREAK .IF:TRUE       LEXIT
792  *   .CONT .IF: TRUE       LSTART/LTEST (.while/.repeat)
793  */
794 
EvaluateHllExpression(struct hll_item * hll,int * i,struct asm_tok tokenarray[],int ilabel,bool is_true,char * buffer)795 static ret_code EvaluateHllExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true, char *buffer )
796 /****************************************************************************************************************************************/
797 {
798     struct hll_opnd hllop = {HLLO_NONE,NULL};
799     char *ptr;
800 
801     hll->deflabel = hll->labels[ilabel];
802     DebugMsg1(("EvaluateHllExpression enter, deflabel=%u, is_true=%u\n", hll->deflabel, is_true ));
803     hll->truelabel = 0;
804     hll->falselabel = 0;
805 
806     *buffer = NULLC;
807     if ( ERROR == GetExpression( hll, i, tokenarray, is_true, buffer, &hllop ) )
808         return( ERROR );
809     /* v2.11: changed */
810     //if ( *buffer == EOLCHAR ) {
811         //DebugMsg(( "EvaluateHllExpression: EOL at pos 0 in line buffer\n" ));
812     if ( tokenarray[*i].token != T_FINAL ) {
813         DebugMsg(( "EvaluateHllExpression: unexpected tokens >%s<\n", tokenarray[*i].tokpos ));
814         return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
815     }
816     if ( hll->falselabel || hll->truelabel ) {
817         ptr = buffer+strlen( buffer );
818         GetLabelStr( hll->falselabel ? hll->falselabel : hll->truelabel, ptr );
819         strcat( ptr, LABELQUAL EOLSTR );
820         DebugMsg1(("EvaluateHllExpression: label appended >%s<, labels=%u/%u\n", ptr, hll->falselabel, hll->truelabel ));
821     }
822     return( NOT_ERROR );
823 }
824 
825 /* for .UNTILCXZ: check if expression is simple enough.
826  * what's acceptable is ONE condition, and just operators == and !=
827  * Constants (0 or != 0) are also accepted.
828  */
829 
CheckCXZLines(char * p)830 static ret_code CheckCXZLines( char *p )
831 /**************************************/
832 {
833     int lines = 0;
834     int i;
835     int addchars;
836     char *px;
837     bool NL = TRUE;
838 
839     DebugMsg1(("CheckCXZLines enter, p=>%s<\n", p ));
840     /* syntax ".untilcxz 1" has a problem: there's no "jmp" generated at all.
841      * if this syntax is to be supported, activate the #if below.
842      */
843     for (; *p; p++ ) {
844         if ( *p == EOLCHAR ) {
845             NL = TRUE;
846             lines++;
847         } else if ( NL ) {
848             NL = FALSE;
849             if ( *p == 'j' ) {
850                 p++;
851                 /* v2.06: rewritten */
852                 if ( *p == 'm' && lines == 0 ) {
853                     addchars = 2; /* make room for 2 chars, to replace "jmp" by "loope" */
854                     px = "loope";
855                 } else if ( lines == 1 && ( *p == 'z' || (*p == 'n' && *(p+1) == 'z') ) ) {
856                     addchars = 3; /* make room for 3 chars, to replace "jz"/"jnz" by "loopz"/"loopnz" */
857                     px = "loop";
858                 } else
859                     return( ERROR ); /* anything else is "too complex" */
860             //replace_instr:
861                 for ( p--, i = strlen( p ); i >= 0; i-- ) {
862                     *(p+addchars+i) = *(p+i);
863                 }
864                 memcpy( p, px, strlen( px ) );
865             }
866 #if 0 /* handle ".untilcxz 1" like masm does */
867             else if ( *p == ' ' && *(p+1) == EOLCHAR && lines == 0 ) {
868                 p++;
869                 GetLabelStr( hll->labels[LSTART], p );
870                 strcat( p, EOLSTR );
871                 addchars = 5;
872                 px = "loope";
873                 goto replace_instr;
874             }
875 #endif
876         }
877     }
878     if ( lines > 2 )
879         return( ERROR );
880     return( NOT_ERROR );
881 }
882 
883 /* .IF, .WHILE or .REPEAT directive */
884 
HllStartDir(int i,struct asm_tok tokenarray[])885 ret_code HllStartDir( int i, struct asm_tok tokenarray[] )
886 /********************************************************/
887 {
888     struct hll_item      *hll;
889     ret_code             rc = NOT_ERROR;
890     int                  cmd = tokenarray[i].tokval;
891     char buff[16];
892     char buffer[MAX_LINE_LEN*2];
893 
894     DebugMsg1(("HllStartDir(%s) enter\n", tokenarray[i].string_ptr ));
895 
896     i++; /* skip directive */
897 
898     /* v2.06: is there an item on the free stack? */
899     if ( HllFree ) {
900         hll = HllFree;
901         DebugCmd( cntReused++ );
902     } else {
903         hll = LclAlloc( sizeof( struct hll_item ) );
904         DebugCmd( cntAlloc++ );
905     }
906 
907     /* structure for .IF .ELSE .ENDIF
908      *    cond jump to LTEST-label
909      *    ...
910      *    jmp LEXIT
911      *  LTEST:
912      *    ...
913      *  LEXIT:
914 
915      * structure for .IF .ELSEIF
916      *    cond jump to LTEST
917      *    ...
918      *    jmp LEXIT
919      *  LTEST:
920      *    cond jump to (new) LTEST
921      *    ...
922      *    jmp LEXIT
923      *  LTEST:
924      *    ...
925 
926      * structure for .WHILE and .REPEAT:
927      *   jmp LTEST (for .WHILE only)
928      * LSTART:
929      *   ...
930      * LTEST: (jumped to by .continue)
931      *   a) test end condition, cond jump to LSTART label
932      *   b) unconditional jump to LSTART label
933      * LEXIT: (jumped to by .BREAK)
934      */
935 
936     hll->labels[LEXIT] = 0;
937 
938     switch ( cmd ) {
939     case T_DOT_IF:
940         hll->labels[LSTART] = 0; /* not used by .IF */
941         hll->labels[LTEST] = GetHllLabel();
942         hll->cmd = HLL_IF;
943         hll->flags = 0;
944         /* get the C-style expression, convert to ASM code lines */
945         rc = EvaluateHllExpression( hll, &i, tokenarray, LTEST, FALSE, buffer );
946         if ( rc == NOT_ERROR ) {
947             QueueTestLines( buffer );
948             /* if no lines have been created, the LTEST label isn't needed */
949             //if ( !is_linequeue_populated() ) {
950             if ( buffer[0] == NULLC ) {
951                 hll->labels[LTEST] = 0;
952             }
953         }
954         break;
955     case T_DOT_WHILE:
956     case T_DOT_REPEAT:
957         /* create the label to start of loop */
958         hll->labels[LSTART] = GetHllLabel();
959         hll->labels[LTEST] = 0; /* v2.11: test label is created only if needed */
960         //hll->labels[LEXIT] = GetHllLabel(); /* v2.11: LEXIT is only needed for .BREAK */
961         if ( cmd == T_DOT_WHILE ) {
962             hll->cmd = HLL_WHILE;
963             hll->condlines = NULL;
964             if ( tokenarray[i].token != T_FINAL ) {
965                 rc = EvaluateHllExpression( hll, &i, tokenarray, LSTART, TRUE, buffer );
966                 if ( rc == NOT_ERROR ) {
967                     int size;
968                     size = strlen( buffer ) + 1;
969                     hll->condlines = LclAlloc( size );
970                     memcpy( hll->condlines, buffer, size );
971                     DebugCmd( cntCond++ ); DebugCmd( cntCondBytes += size );
972                 }
973             } else
974                 buffer[0] = NULLC;  /* just ".while" without expression is accepted */
975 
976             /* create a jump to test label */
977             /* optimisation: if line at 'test' label is just a jump, dont create label and don't jump! */
978             if ( _memicmp( buffer, "jmp", 3 ) ) {
979                 hll->labels[LTEST] = GetHllLabel();
980                 AddLineQueueX( JMPPREFIX "jmp %s", GetLabelStr( hll->labels[LTEST], buff ) );
981             }
982         } else {
983             hll->cmd = HLL_REPEAT;
984         }
985         AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LSTART], buff ) );
986         break;
987 #ifdef DEBUG_OUT
988     default: /**/myassert( 0 ); break;
989 #endif
990     }
991 
992     if ( tokenarray[i].token != T_FINAL && rc == NOT_ERROR ) {
993         DebugMsg(("HllStartDir: unexpected token [%s]\n", tokenarray[i].tokpos ));
994         EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
995         rc = ERROR;
996         //return( ERROR ); /* v2.08: continue and parse the line queue */
997     }
998     /* v2.06: remove the item from the free stack */
999     if ( hll == HllFree )
1000         HllFree = hll->next;
1001     hll->next = HllStack;
1002     HllStack = hll;
1003 
1004     if ( ModuleInfo.list )
1005         LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
1006 
1007     if ( is_linequeue_populated() ) /* might be NULL! (".if 1") */
1008         RunLineQueue();
1009 
1010     return( rc );
1011 }
1012 
1013 /*
1014  * .ENDIF, .ENDW, .UNTIL and .UNTILCXZ directives.
1015  * These directives end a .IF, .WHILE or .REPEAT block.
1016  */
HllEndDir(int i,struct asm_tok tokenarray[])1017 ret_code HllEndDir( int i, struct asm_tok tokenarray[] )
1018 /******************************************************/
1019 {
1020     //struct asym       *sym;
1021     struct hll_item     *hll;
1022     ret_code            rc = NOT_ERROR;
1023     int                 cmd = tokenarray[i].tokval;
1024     char buff[16];
1025     char buffer[MAX_LINE_LEN*2];
1026 
1027     DebugMsg1(("HllEndDir(%s) enter\n", tokenarray[i].string_ptr ));
1028 
1029     if ( HllStack == NULL ) {
1030         DebugMsg(("HllEndDir: hll stack is empty\n"));
1031         return( EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK ) );
1032     }
1033 
1034     hll = HllStack;
1035     HllStack = hll->next;
1036     /* v2.06: move the item to the free stack */
1037     hll->next = HllFree;
1038     HllFree = hll;
1039 
1040     switch ( cmd ) {
1041     case T_DOT_ENDIF:
1042         if ( hll->cmd != HLL_IF ) {
1043             DebugMsg(("HllEndDir: no .IF on the hll stack\n"));
1044             return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
1045         }
1046         i++;
1047         /* if a test label isn't created yet, create it */
1048         if ( hll->labels[LTEST] ) {
1049             AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
1050         }
1051         break;
1052     case T_DOT_ENDW:
1053         if ( hll->cmd != HLL_WHILE ) {
1054             DebugMsg(("HllEndDir: no .WHILE on the hll stack\n"));
1055             return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
1056         }
1057         i++;
1058         /* create test label */
1059         if ( hll->labels[LTEST] ) {
1060             AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
1061         }
1062         QueueTestLines( hll->condlines );
1063         LclFree( hll->condlines );
1064         break;
1065     case T_DOT_UNTILCXZ:
1066         if ( hll->cmd != HLL_REPEAT ) {
1067             DebugMsg(("HllEndDir: no .REPEAT on the hll stack\n"));
1068             return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
1069         }
1070         i++;
1071         if ( hll->labels[LTEST] ) /* v2.11: LTEST only needed if .CONTINUE has occured */
1072             AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
1073 
1074         /* read in optional (simple) expression */
1075         if ( tokenarray[i].token != T_FINAL ) {
1076             rc = EvaluateHllExpression( hll, &i, tokenarray, LSTART, FALSE, buffer );
1077             if ( rc == NOT_ERROR ) {
1078                 rc = CheckCXZLines( buffer );
1079                 if ( rc == NOT_ERROR )
1080                     QueueTestLines( buffer ); /* write condition lines */
1081                 else
1082                     EmitError( EXPR_TOO_COMPLEX_FOR_UNTILCXZ );
1083             }
1084         } else {
1085             AddLineQueueX( JMPPREFIX "loop %s", GetLabelStr( hll->labels[LSTART], buff ) );
1086         }
1087         break;
1088     case T_DOT_UNTIL:
1089         if ( hll->cmd != HLL_REPEAT ) {
1090             DebugMsg(("HllEndDir: no .REPEAT on the hll stack\n"));
1091             return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
1092         }
1093         i++;
1094         if ( hll->labels[LTEST] ) /* v2.11: LTEST only needed if .CONTINUE has occured */
1095             AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
1096 
1097         /* read in (optional) expression */
1098         /* if expression is missing, just generate nothing */
1099         if ( tokenarray[i].token != T_FINAL ) {
1100             rc = EvaluateHllExpression( hll, &i, tokenarray, LSTART, FALSE, buffer );
1101             if ( rc == NOT_ERROR )
1102                 QueueTestLines( buffer ); /* write condition lines */
1103         }
1104         break;
1105 #ifdef DEBUG_OUT
1106     default: /**/myassert( 0 ); break;
1107 #endif
1108     }
1109 
1110     /* create the exit label if it has been referenced */
1111     if ( hll->labels[LEXIT] )
1112         AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LEXIT], buff ) );
1113 
1114     if ( tokenarray[i].token != T_FINAL && rc == NOT_ERROR ) {
1115         EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
1116         rc = ERROR;
1117     }
1118     if ( ModuleInfo.list )
1119         LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
1120 
1121     /* v2.11: always run line-queue if it's not empty. */
1122     if ( is_linequeue_populated() )
1123         RunLineQueue();
1124 
1125     return( rc );
1126 }
1127 
1128 /*
1129  * .ELSE, .ELSEIF, .CONTINUE and .BREAK directives.
1130  * .ELSE, .ELSEIF:
1131  *    - create a jump to exit label
1132  *    - render test label if it was referenced
1133  *    - for .ELSEIF, create new test label and evaluate expression
1134  * .CONTINUE, .BREAK:
1135  *    - jump to test / exit label of innermost .WHILE/.REPEAT block
1136  *
1137  */
HllExitDir(int i,struct asm_tok tokenarray[])1138 ret_code HllExitDir( int i, struct asm_tok tokenarray[] )
1139 /*******************************************************/
1140 {
1141     //int               level;
1142     //struct asym       *sym;
1143     struct hll_item     *hll;
1144     ret_code            rc = NOT_ERROR;
1145     int                 idx;
1146     int                 cmd = tokenarray[i].tokval;
1147     char buff[16];
1148     char buffer[MAX_LINE_LEN*2];
1149 
1150     DebugMsg1(("HllExitDir(%s) enter\n", tokenarray[i].string_ptr ));
1151 
1152     hll = HllStack;
1153 
1154     if ( hll == NULL ) {
1155         DebugMsg(("HllExitDir stack error\n"));
1156         return( EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK ) );
1157     }
1158 
1159     switch ( cmd ) {
1160     case T_DOT_ELSE:
1161     case T_DOT_ELSEIF:
1162         if ( hll->cmd != HLL_IF ) {
1163             DebugMsg(("HllExitDir(%s): labels[LTEST]=%X\n", tokenarray[i].string_ptr, hll->labels[LTEST]));
1164             return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
1165         }
1166         /* v2.08: check for multiple ELSE clauses */
1167         if ( hll->flags & HLLF_ELSEOCCURED ) {
1168             return( EmitError( DOT_ELSE_CLAUSE_ALREADY_OCCURED_IN_THIS_DOT_IF_BLOCK ) );
1169         }
1170 
1171         /* the 'exit'-label is only needed if an .ELSE branch exists.
1172          * That's why it is created delayed.
1173          */
1174         if ( hll->labels[LEXIT] == 0 )
1175             hll->labels[LEXIT] = GetHllLabel();
1176         AddLineQueueX( JMPPREFIX "jmp %s", GetLabelStr( hll->labels[LEXIT], buff ) );
1177 
1178         if ( hll->labels[LTEST] > 0 ) {
1179             AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
1180             hll->labels[LTEST] = 0;
1181         }
1182         i++;
1183         if ( cmd == T_DOT_ELSEIF ) {
1184             /* create new labels[LTEST] label */
1185             hll->labels[LTEST] = GetHllLabel();
1186             rc = EvaluateHllExpression( hll, &i, tokenarray, LTEST, FALSE, buffer );
1187             if ( rc == NOT_ERROR )
1188                 QueueTestLines( buffer );
1189         } else
1190             hll->flags |= HLLF_ELSEOCCURED;
1191 
1192         break;
1193     case T_DOT_BREAK:
1194     case T_DOT_CONTINUE:
1195         for ( ; hll && hll->cmd == HLL_IF; hll = hll->next );
1196         if ( hll == NULL ) {
1197             return( EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK ) );
1198         }
1199         /* v2.11: create 'exit' and 'test' labels delayed.
1200          */
1201         if ( cmd == T_DOT_BREAK ) {
1202             if ( hll->labels[LEXIT] == 0 )
1203                 hll->labels[LEXIT] = GetHllLabel();
1204             idx = LEXIT;
1205         } else {
1206             /* 'test' is not created for .WHILE loops here; because
1207              * if it doesn't exist, there's no condition to test.
1208              */
1209             if ( hll->cmd == HLL_REPEAT && hll->labels[LTEST] == 0 )
1210                 hll->labels[LTEST] = GetHllLabel();
1211             idx = ( hll->labels[LTEST] ? LTEST : LSTART );
1212         }
1213 
1214         /* .BREAK .IF ... or .CONTINUE .IF ? */
1215         i++;
1216         if ( tokenarray[i].token != T_FINAL ) {
1217             if ( tokenarray[i].token == T_DIRECTIVE && tokenarray[i].tokval == T_DOT_IF ) {
1218                 enum hll_cmd savedcmd = hll->cmd;
1219                 hll->cmd = HLL_BREAK;
1220                 i++;
1221                 /* v2.11: set rc and don't exit if an error occurs; see hll3.aso */
1222                 rc = EvaluateHllExpression( hll, &i, tokenarray, idx, TRUE, buffer );
1223                 if ( rc == NOT_ERROR )
1224                     QueueTestLines( buffer );
1225                 hll->cmd = savedcmd;
1226             }
1227         } else {
1228             AddLineQueueX( JMPPREFIX "jmp %s", GetLabelStr( hll->labels[idx], buff ) );
1229         }
1230         break;
1231 #ifdef DEBUG_OUT
1232     default: /**/myassert( 0 ); break;
1233 #endif
1234     }
1235     if ( tokenarray[i].token != T_FINAL && rc == NOT_ERROR ) {
1236         EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
1237         rc = ERROR;
1238     }
1239 
1240     if ( ModuleInfo.list )
1241         LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
1242 
1243     /* v2.11: always run line-queue if it's not empty. */
1244     if ( is_linequeue_populated() )
1245         RunLineQueue();
1246 
1247     return( rc );
1248 }
1249 
1250 /* check if an hll block has been left open. called after pass 1 */
1251 
HllCheckOpen(void)1252 void HllCheckOpen( void )
1253 /***********************/
1254 {
1255     if ( HllStack ) {
1256         //EmitErr( BLOCK_NESTING_ERROR, ".if-.repeat-.while" );
1257         EmitErr( UNMATCHED_BLOCK_NESTING, ".if-.repeat-.while" );
1258     }
1259     DebugMsg(("HllCheckOpen: allocated items:%u, reused items:%u, .while cond-blocks/bytes:%u/%u\n", cntAlloc, cntReused, cntCond, cntCondBytes ));
1260 }
1261 
1262 #if FASTMEM==0
HllFini(void)1263 void HllFini( void )
1264 /******************/
1265 {
1266     struct hll_item *curr;
1267     struct hll_item *next;
1268     /* release the items in the HllFree heap.
1269      * there might also be some left in HllStack...
1270      */
1271     for ( curr = HllFree; curr; curr = next ) {
1272         next = curr->next;
1273         LclFree( curr );
1274     }
1275 }
1276 #endif
1277 
1278 /* HllInit() is called for each pass */
1279 
HllInit(int pass)1280 void HllInit( int pass )
1281 /**********************/
1282 {
1283     //if ( pass == PASS_1 )
1284     //    HllFree = NULL;
1285 
1286     //HllStack = NULL; /* empty stack of open hll directives */
1287     ModuleInfo.hll_label = 0; /* init hll label counter */
1288 #ifdef DEBUG_OUT
1289     evallvl = 0;
1290     if ( pass == PASS_1 ) {
1291         cntAlloc = 0;
1292         cntReused = 0;
1293         cntCond = 0;
1294         cntCondBytes = 0;
1295     }
1296 #endif
1297     return;
1298 }
1299