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