mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
1262 lines
44 KiB
C
1262 lines
44 KiB
C
/****************************************************************************
|
|
*
|
|
* This code is Public Domain.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Description: implements hll directives:
|
|
* .IF, .WHILE, .REPEAT, .ELSE, .ELSEIF, .ENDIF,
|
|
* .ENDW, .UNTIL, .UNTILCXZ, .BREAK, .CONTINUE.
|
|
* also handles C operators:
|
|
* ==, !=, >=, <=, >, <, &&, ||, &, !,
|
|
* and flags:
|
|
* ZERO?, CARRY?, SIGN?, PARITY?, OVERFLOW?
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "globals.h"
|
|
#include "memalloc.h"
|
|
#include "parser.h"
|
|
#include "equate.h"
|
|
#include "label.h"
|
|
#include "expreval.h"
|
|
#include "types.h"
|
|
#include "hll.h"
|
|
#include "segment.h"
|
|
#include "listing.h"
|
|
#include "lqueue.h"
|
|
#include "myassert.h"
|
|
|
|
#define LABELSIZE 8
|
|
#define LABELSGLOBAL 0 /* make the generated labels global */
|
|
#define JMPPREFIX /* define spaces before "jmp" or "loop" */
|
|
#define LABELFMT "@C%04X"
|
|
|
|
/* v2.10: static variables moved to ModuleInfo */
|
|
#define HllStack ModuleInfo.g.HllStack
|
|
#define HllFree ModuleInfo.g.HllFree
|
|
|
|
#if LABELSGLOBAL
|
|
#define LABELQUAL "::"
|
|
#else
|
|
#define LABELQUAL ":"
|
|
#endif
|
|
|
|
#ifdef DEBUG_OUT
|
|
#define EOLCHAR '^' /* this allows better displays */
|
|
#define EOLSTR "^"
|
|
#else
|
|
#define EOLCHAR '\n' /* line termination char in generated source */
|
|
#define EOLSTR "\n"
|
|
#endif
|
|
|
|
/* values for struct hll_item.cmd */
|
|
enum hll_cmd {
|
|
HLL_IF,
|
|
HLL_WHILE,
|
|
HLL_REPEAT,
|
|
HLL_BREAK /* .IF behind .BREAK or .CONTINUE */
|
|
};
|
|
|
|
/* index values for struct hll_item.labels[] */
|
|
enum hll_label_index {
|
|
LTEST, /* test (loop) condition */
|
|
LEXIT, /* block exit */
|
|
LSTART, /* loop start */
|
|
};
|
|
|
|
/* values for struct hll_item.flags */
|
|
enum hll_flags {
|
|
HLLF_ELSEOCCURED = 0x01,
|
|
};
|
|
|
|
|
|
/* item for .IF, .WHILE, .REPEAT, ... */
|
|
struct hll_item {
|
|
struct hll_item *next;
|
|
uint_32 labels[3]; /* labels for LTEST, LEXIT, LSTART */
|
|
char *condlines; /* .WHILE-blocks only: lines to add after 'test' label */
|
|
enum hll_cmd cmd; /* start cmd (IF, WHILE, REPEAT) */
|
|
enum hll_flags flags; /* v2.08: added */
|
|
};
|
|
|
|
/* v2.08: struct added */
|
|
struct hll_opnd {
|
|
char *lastjmp;
|
|
uint_32 lasttruelabel; /* v2.08: new member */
|
|
};
|
|
|
|
static ret_code GetExpression( struct hll_item *hll, int *i, struct asm_tok[], int ilabel, bool is_true, char *buffer, struct hll_opnd * );
|
|
|
|
/* c binary ops.
|
|
* Order of items COP_EQ - COP_LE and COP_ZERO - COP_OVERFLOW
|
|
* must not be changed.
|
|
*/
|
|
enum c_bop {
|
|
COP_NONE,
|
|
COP_EQ, /* == */
|
|
COP_NE, /* != */
|
|
COP_GT, /* > */
|
|
COP_LT, /* < */
|
|
COP_GE, /* >= */
|
|
COP_LE, /* <= */
|
|
COP_AND, /* && */
|
|
COP_OR, /* || */
|
|
COP_ANDB, /* & */
|
|
COP_NEG, /* ! */
|
|
COP_ZERO, /* ZERO? not really a valid C operator */
|
|
COP_CARRY,/* CARRY? not really a valid C operator */
|
|
COP_SIGN, /* SIGN? not really a valid C operator */
|
|
COP_PARITY, /* PARITY? not really a valid C operator */
|
|
COP_OVERFLOW /* OVERFLOW? not really a valid C operator */
|
|
};
|
|
|
|
/* items in table below must match order COP_ZERO - COP_OVERFLOW */
|
|
static const char flaginstr[] = { 'z', 'c', 's', 'p', 'o' };
|
|
|
|
/* items in tables below must match order COP_EQ - COP_LE */
|
|
static const char unsigned_cjmptype[] = { 'z', 'z', 'a', 'b', 'b', 'a' };
|
|
static const char signed_cjmptype[] = { 'z', 'z', 'g', 'l', 'l', 'g' };
|
|
static const char neg_cjmptype[] = { 0, 1, 0, 0, 1, 1 };
|
|
|
|
/* in Masm, there's a nesting level limit of 20. In JWasm, there's
|
|
* currently no limit.
|
|
*/
|
|
|
|
#ifdef DEBUG_OUT
|
|
static unsigned evallvl;
|
|
static unsigned cntAlloc; /* # of allocated hll_items */
|
|
static unsigned cntReused; /* # of reused hll_items */
|
|
static unsigned cntCond; /* # of allocated 'condlines'-buffer in .WHILE-blocks */
|
|
static unsigned cntCondBytes; /* total size of allocated 'condlines'-buffers */
|
|
#endif
|
|
|
|
static uint_32 GetHllLabel( void )
|
|
/********************************/
|
|
{
|
|
return ( ++ModuleInfo.hll_label );
|
|
}
|
|
|
|
/* get a C binary operator from the token stream.
|
|
* there is a problem with the '<' because it is a "string delimiter"
|
|
* which Tokenize() usually is to remove.
|
|
* There has been a hack implemented in Tokenize() so that it won't touch the
|
|
* '<' if .IF, .ELSEIF, .WHILE, .UNTIL, .UNTILCXZ or .BREAK/.CONTINUE has been
|
|
* detected.
|
|
*/
|
|
|
|
#define CHARS_EQ '=' + ( '=' << 8 )
|
|
#define CHARS_NE '!' + ( '=' << 8 )
|
|
#define CHARS_GE '>' + ( '=' << 8 )
|
|
#define CHARS_LE '<' + ( '=' << 8 )
|
|
#define CHARS_AND '&' + ( '&' << 8 )
|
|
#define CHARS_OR '|' + ( '|' << 8 )
|
|
|
|
static enum c_bop GetCOp( struct asm_tok *item )
|
|
/**********************************************/
|
|
{
|
|
int size;
|
|
enum c_bop rc;
|
|
char *p = item->string_ptr;
|
|
|
|
size = ( item->token == T_STRING ? item->stringlen : 0 );
|
|
|
|
if ( size == 2 ) {
|
|
switch ( *(uint_16 *)p ) {
|
|
case CHARS_EQ: rc = COP_EQ; break;
|
|
case CHARS_NE: rc = COP_NE; break;
|
|
case CHARS_GE: rc = COP_GE; break;
|
|
case CHARS_LE: rc = COP_LE; break;
|
|
case CHARS_AND: rc = COP_AND; break;
|
|
case CHARS_OR: rc = COP_OR; break;
|
|
default: return( COP_NONE );
|
|
}
|
|
} else if ( size == 1 ) {
|
|
switch ( *p ) {
|
|
case '>': rc = COP_GT; break;
|
|
case '<': rc = COP_LT; break;
|
|
case '&': rc = COP_ANDB; break;
|
|
case '!': rc = COP_NEG; break;
|
|
default: return( COP_NONE );
|
|
}
|
|
} else {
|
|
if ( item->token != T_ID )
|
|
return( COP_NONE );
|
|
/* a valid "flag" string must end with a question mark */
|
|
size = strlen( p );
|
|
if ( *(p+size-1) != '?' )
|
|
return( COP_NONE );
|
|
if ( size == 5 && ( 0 == _memicmp( p, "ZERO", 4 ) ) )
|
|
rc = COP_ZERO;
|
|
else if ( size == 6 && ( 0 == _memicmp( p, "CARRY", 5 ) ) )
|
|
rc = COP_CARRY;
|
|
else if ( size == 5 && ( 0 == _memicmp( p, "SIGN", 4 ) ) )
|
|
rc = COP_SIGN;
|
|
else if ( size == 7 && ( 0 == _memicmp( p, "PARITY", 6 ) ) )
|
|
rc = COP_PARITY;
|
|
else if ( size == 9 && ( 0 == _memicmp( p, "OVERFLOW", 8 ) ) )
|
|
rc = COP_OVERFLOW;
|
|
else
|
|
return( COP_NONE );
|
|
}
|
|
return( rc );
|
|
}
|
|
|
|
/* render an instruction */
|
|
|
|
static char *RenderInstr( char *dst, const char *instr, int start1, int end1, int start2, int end2, struct asm_tok tokenarray[] )
|
|
/*******************************************************************************************************************************/
|
|
{
|
|
int i;
|
|
#ifdef DEBUG_OUT
|
|
char *old = dst;
|
|
#endif
|
|
i = strlen( instr );
|
|
/* copy the instruction */
|
|
memcpy( dst, instr, i );
|
|
dst += i;
|
|
/* copy the first operand's tokens */
|
|
*dst++ = ' ';
|
|
i = tokenarray[end1].tokpos - tokenarray[start1].tokpos;
|
|
memcpy( dst, tokenarray[start1].tokpos, i );
|
|
dst += i;
|
|
if ( start2 != EMPTY ) {
|
|
*dst++ = ',';
|
|
/* copy the second operand's tokens */
|
|
*dst++ = ' ';
|
|
i = tokenarray[end2].tokpos - tokenarray[start2].tokpos;
|
|
memcpy( dst, tokenarray[start2].tokpos, i );
|
|
dst += i;
|
|
} else if ( end2 != EMPTY ) {
|
|
dst += sprintf( dst, ", %d", end2 );
|
|
}
|
|
*dst++ = EOLCHAR;
|
|
*dst = NULLC;
|
|
DebugMsg1(("%u RenderInstr(%s)=>%s<\n", evallvl, instr, old ));
|
|
return( dst );
|
|
}
|
|
|
|
static char *GetLabelStr( int_32 label, char *buff )
|
|
/**************************************************/
|
|
{
|
|
sprintf( buff, LABELFMT, label );
|
|
return( buff );
|
|
}
|
|
|
|
/* render a Jcc instruction */
|
|
|
|
static char *RenderJcc( char *dst, char cc, int neg, uint_32 label )
|
|
/******************************************************************/
|
|
{
|
|
#ifdef DEBUG_OUT
|
|
char *old = dst;
|
|
#endif
|
|
/* create the jump opcode: j[n]cc */
|
|
*dst++ = 'j';
|
|
if ( neg )
|
|
*dst++ = 'n';
|
|
*dst++ = cc;
|
|
if ( neg == FALSE )
|
|
*dst++ = ' '; /* make sure there's room for the inverse jmp */
|
|
|
|
*dst++ = ' ';
|
|
GetLabelStr( label, dst );
|
|
dst += strlen( dst );
|
|
*dst++ = EOLCHAR;
|
|
*dst = NULLC;
|
|
DebugMsg1(("%u RenderJcc()=>%s<\n", evallvl, old ));
|
|
return( dst );
|
|
}
|
|
|
|
/* a "token" in a C expression actually is an assembly expression */
|
|
|
|
static ret_code GetToken( struct hll_item *hll, int *i, struct asm_tok tokenarray[], struct expr *opnd )
|
|
/******************************************************************************************************/
|
|
{
|
|
int end_tok;
|
|
|
|
/* scan for the next C operator in the token array.
|
|
* because the ASM evaluator may report an error if such a thing
|
|
* is found ( CARRY?, ZERO? and alikes will be regarded as - not yet defined - labels )
|
|
*/
|
|
for ( end_tok = *i; end_tok < Token_Count; end_tok++ ) {
|
|
if ( ( GetCOp( &tokenarray[end_tok] ) ) != COP_NONE )
|
|
break;
|
|
}
|
|
if ( end_tok == *i ) {
|
|
opnd->kind = EXPR_EMPTY;
|
|
return( NOT_ERROR );
|
|
}
|
|
if ( ERROR == EvalOperand( i, tokenarray, end_tok, opnd, 0 ) )
|
|
return( ERROR );
|
|
|
|
/* v2.11: emit error 'syntax error in control flow directive'.
|
|
* May happen for expressions like ".if 1 + CARRY?"
|
|
*/
|
|
if ( *i > end_tok ) {
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
static uint_32 GetLabel( struct hll_item *hll, int index )
|
|
/********************************************************/
|
|
{
|
|
/**/myassert( hll->labels[index] );
|
|
return( hll->labels[index] );
|
|
}
|
|
|
|
/* a "simple" expression is
|
|
* 1. two tokens, coupled with a <cmp> operator: == != >= <= > <
|
|
* 2. two tokens, coupled with a "&" operator
|
|
* 3. unary operator "!" + one token
|
|
* 4. one token (short form for "<token> != 0")
|
|
*/
|
|
static ret_code GetSimpleExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true, char *buffer, struct hll_opnd *hllop )
|
|
/**************************************************************************************************************************************************************/
|
|
{
|
|
enum c_bop op;
|
|
char instr;
|
|
int op1_pos;
|
|
int op1_end;
|
|
int op2_pos;
|
|
int op2_end;
|
|
char *p;
|
|
struct expr op1;
|
|
struct expr op2;
|
|
uint_32 label;
|
|
|
|
DebugMsg1(("%u GetSimpleExpression(>%.32s< buf=>%s<) enter\n", evallvl, tokenarray[*i].tokpos, buffer ));
|
|
|
|
while ( tokenarray[*i].string_ptr[0] == '!' && tokenarray[*i].string_ptr[1] == '\0' ) {
|
|
(*i)++; //GetCOp( i );
|
|
is_true = 1 - is_true;
|
|
}
|
|
|
|
/* the problem with '()' is that is might enclose just a standard Masm
|
|
* expression or a "hll" expression. The first case is to be handled
|
|
* entirely by the expression evaluator, while the latter case is to be
|
|
* handled HERE!
|
|
*/
|
|
if ( tokenarray[*i].token == T_OP_BRACKET ) {
|
|
int brcnt;
|
|
int j;
|
|
for ( brcnt = 1, j = *i + 1; tokenarray[j].token != T_FINAL; j++ ) {
|
|
if ( tokenarray[j].token == T_OP_BRACKET )
|
|
brcnt++;
|
|
else if ( tokenarray[j].token == T_CL_BRACKET ) {
|
|
brcnt--;
|
|
if ( brcnt == 0 ) /* a standard Masm expression? */
|
|
break;
|
|
} else if ( ( GetCOp( &tokenarray[j] )) != COP_NONE )
|
|
break;
|
|
}
|
|
if ( brcnt ) {
|
|
(*i)++;
|
|
DebugMsg1(("%u GetSimpleExpression: calling GetExpression, i=%u\n", evallvl, *i));
|
|
if ( ERROR == GetExpression( hll, i, tokenarray, ilabel, is_true, buffer, hllop ) )
|
|
return( ERROR );
|
|
|
|
if ( tokenarray[*i].token != T_CL_BRACKET ) {
|
|
//if (( tokenarray[*i].token == T_FINAL ) || ( tokenarray[*i].token == T_CL_BRACKET ))
|
|
DebugMsg(( "GetSimpleExpression: expected ')', found: %s\n", tokenarray[*i].string_ptr ));
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
(*i)++;
|
|
return( NOT_ERROR );
|
|
}
|
|
}
|
|
|
|
/* get (first) operand */
|
|
op1_pos = *i;
|
|
if ( ERROR == GetToken( hll, i, tokenarray, &op1 ) )
|
|
return ( ERROR );
|
|
op1_end = *i;
|
|
|
|
op = GetCOp( &tokenarray[*i] ); /* get operator */
|
|
|
|
/* lower precedence operator ( && or || ) detected? */
|
|
if ( op == COP_AND || op == COP_OR ) {
|
|
/* v2.11: next 2 lines removed - && and || operators need a valid first operand */
|
|
//if ( op1.kind == EXPR_EMPTY )
|
|
// return( NOT_ERROR );
|
|
op = COP_NONE;
|
|
} else if ( op != COP_NONE )
|
|
(*i)++;
|
|
|
|
label = GetLabel( hll, ilabel );
|
|
|
|
DebugMsg1(("%u GetSimpleExpression: EvalOperand ok, kind=%X, i=%u [%s]\n", evallvl, op1.kind, *i, tokenarray[*i].tokpos ));
|
|
|
|
/* check for special operators with implicite operand:
|
|
* COP_ZERO, COP_CARRY, COP_SIGN, COP_PARITY, COP_OVERFLOW
|
|
*/
|
|
if ( op >= COP_ZERO ) {
|
|
if ( op1.kind != EXPR_EMPTY ) {
|
|
DebugMsg(( "GetSimpleExpression: non-empty expression rejected: %s\n", tokenarray[op1_pos].tokpos ));
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
p = buffer;
|
|
hllop->lastjmp = p;
|
|
RenderJcc( p, flaginstr[ op - COP_ZERO ], !is_true, label );
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
switch ( op1.kind ) {
|
|
case EXPR_EMPTY:
|
|
DebugMsg(( "GetSimpleExpression: empty expression rejected\n" ));
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) ); /* v2.09: changed from NOT_ERROR to ERROR */
|
|
case EXPR_FLOAT:
|
|
DebugMsg(( "GetSimpleExpression: float expression rejected: %s\n", tokenarray[op1_pos].tokpos ));
|
|
return( EmitError( REAL_OR_BCD_NUMBER_NOT_ALLOWED ) ); /* v2.10: added */
|
|
}
|
|
|
|
if ( op == COP_NONE ) {
|
|
switch ( op1.kind ) {
|
|
case EXPR_REG:
|
|
if ( op1.indirect == FALSE ) {
|
|
p = RenderInstr( buffer, "and", op1_pos, op1_end, op1_pos, op1_end, tokenarray );
|
|
hllop->lastjmp = p;
|
|
RenderJcc( p, 'z', is_true, label );
|
|
break;
|
|
}
|
|
/* no break */
|
|
case EXPR_ADDR:
|
|
p = RenderInstr( buffer, "cmp", op1_pos, op1_end, EMPTY, 0, tokenarray );
|
|
hllop->lastjmp = p;
|
|
RenderJcc( p, 'z', is_true, label );
|
|
break;
|
|
case EXPR_CONST:
|
|
#if 0
|
|
/* v2.05: string constant is allowed! */
|
|
if ( op1.string != NULL ) {
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
#endif
|
|
/* v2.11: error if constant doesn't fit in 32-bits */
|
|
if ( op1.hvalue != 0 && op1.hvalue != -1 )
|
|
return( EmitConstError( &op1 ) );
|
|
|
|
hllop->lastjmp = buffer;
|
|
|
|
if ( ( is_true == TRUE && op1.value ) ||
|
|
( is_true == FALSE && op1.value == 0 ) ) {
|
|
sprintf( buffer, "jmp " LABELFMT EOLSTR, label );
|
|
} else {
|
|
//strcpy( buffer, " " EOLSTR ); /* v2.11: obsolete */
|
|
*buffer = NULLC;
|
|
}
|
|
break;
|
|
#ifdef DEBUG_OUT
|
|
default: /**/myassert( 0 ); break;
|
|
#endif
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* get second operand for binary operator */
|
|
op2_pos = *i;
|
|
if ( ERROR == GetToken( hll, i, tokenarray, &op2 ) ) {
|
|
return( ERROR );
|
|
}
|
|
DebugMsg1(("%u GetSimpleExpression: EvalOperand 2 ok, type=%X, i=%u [%s]\n", evallvl, op2.type, *i, tokenarray[*i].tokpos));
|
|
if ( op2.kind != EXPR_CONST && op2.kind != EXPR_ADDR && op2.kind != EXPR_REG ) {
|
|
DebugMsg(("GetSimpleExpression: syntax error, op2.kind=%u\n", op2.kind ));
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
op2_end = *i;
|
|
|
|
/* now generate ASM code for expression */
|
|
|
|
if ( op == COP_ANDB ) {
|
|
p = RenderInstr( buffer, "test", op1_pos, op1_end, op2_pos, op2_end, tokenarray );
|
|
hllop->lastjmp = p;
|
|
RenderJcc( p, 'e', is_true, label );
|
|
} else if ( op <= COP_LE ) { /* ==, !=, >, <, >= or <= operator */
|
|
/*
|
|
* optimisation: generate 'or EAX,EAX' instead of 'cmp EAX,0'.
|
|
* v2.11: use op2.value64 instead of op2.value
|
|
*/
|
|
if ( Options.masm_compat_gencode &&
|
|
( op == COP_EQ || op == COP_NE ) &&
|
|
op1.kind == EXPR_REG && op1.indirect == FALSE &&
|
|
op2.kind == EXPR_CONST && op2.value64 == 0 ) {
|
|
p = RenderInstr( buffer, "or", op1_pos, op1_end, op1_pos, op1_end, tokenarray );
|
|
} else {
|
|
p = RenderInstr( buffer, "cmp", op1_pos, op1_end, op2_pos, op2_end, tokenarray );
|
|
}
|
|
|
|
instr = ( ( IS_SIGNED( op1.mem_type ) || IS_SIGNED( op2.mem_type ) ) ? signed_cjmptype[op - COP_EQ] : unsigned_cjmptype[op - COP_EQ] );
|
|
hllop->lastjmp = p;
|
|
RenderJcc( p, instr, neg_cjmptype[op - COP_EQ] ? is_true : !is_true, label );
|
|
} else {
|
|
DebugMsg(("GetSimpleExpression: unexpected operator %s\n", tokenarray[op1_pos].tokpos ));
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* invert a Jump:
|
|
* - Jx -> JNx (x = e|z|c|s|p|o )
|
|
* - JNx -> Jx (x = e|z|c|s|p|o )
|
|
* - Ja -> Jbe, Jae -> Jb
|
|
* - Jb -> Jae, Jbe -> Ja
|
|
* - Jg -> Jle, Jge -> Jl
|
|
* - Jl -> Jge, Jle -> Jg
|
|
* added in v2.11:
|
|
* - jmp -> 0
|
|
* - 0 -> jmp
|
|
*/
|
|
static void InvertJump( char *p )
|
|
/*******************************/
|
|
{
|
|
if ( *p == NULLC ) { /* v2.11: convert 0 to "jmp" */
|
|
strcpy( p, "jmp " );
|
|
return;
|
|
}
|
|
|
|
p++;
|
|
if ( *p == 'e' || *p == 'z' || *p == 'c' || *p == 's' || *p == 'p' || *p == 'o' ) {
|
|
*(p+1) = *p;
|
|
*p = 'n';
|
|
return;
|
|
} else if ( *p == 'n' ) {
|
|
*p = *(p+1);
|
|
*(p+1) = ' ';
|
|
return;
|
|
} else if ( *p == 'a' ) {
|
|
*p++ = 'b';
|
|
} else if ( *p == 'b' ) {
|
|
*p++ = 'a';
|
|
} else if ( *p == 'g' ) {
|
|
*p++ = 'l';
|
|
} else if ( *p == 'l' ) {
|
|
*p++ = 'g';
|
|
} else {
|
|
/* v2.11: convert "jmp" to 0 */
|
|
if ( *p == 'm' ) {
|
|
p--;
|
|
*p = NULLC;
|
|
}
|
|
return;
|
|
}
|
|
if ( *p == 'e' )
|
|
*p = ' ';
|
|
else
|
|
*p = 'e';
|
|
return;
|
|
}
|
|
|
|
/* Replace a label in the source lines generated so far.
|
|
* todo: if more than 0xFFFF labels are needed,
|
|
* it may happen that length of nlabel > length of olabel!
|
|
* then the simple memcpy() below won't work!
|
|
*/
|
|
|
|
static void ReplaceLabel( char *p, uint_32 olabel, uint_32 nlabel )
|
|
/*****************************************************************/
|
|
{
|
|
char oldlbl[16];
|
|
char newlbl[16];
|
|
int i;
|
|
|
|
GetLabelStr( olabel, oldlbl );
|
|
GetLabelStr( nlabel, newlbl );
|
|
|
|
i = strlen( newlbl );
|
|
|
|
DebugMsg1(("%u ReplaceLabel(%s->%s, >%s<)\n", evallvl, oldlbl, newlbl, p ));
|
|
while ( (p = strstr( p, oldlbl )) != NULL ) {
|
|
memcpy( p, newlbl, i );
|
|
p += i;
|
|
}
|
|
}
|
|
|
|
/* operator &&, which has the second lowest precedence, is handled here */
|
|
|
|
static ret_code GetAndExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true, char *buffer, struct hll_opnd *hllop )
|
|
/***********************************************************************************************************************************************************/
|
|
{
|
|
char *ptr = buffer;
|
|
uint_32 truelabel = 0;
|
|
//char buff[16];
|
|
//char *nlabel;
|
|
//char *olabel;
|
|
|
|
DebugMsg1(("%u GetAndExpression(>%.32s< buf=>%s<) enter\n", evallvl, tokenarray[*i].tokpos, buffer ));
|
|
|
|
if ( ERROR == GetSimpleExpression( hll, i, tokenarray, ilabel, is_true, ptr, hllop ) )
|
|
return( ERROR );
|
|
while ( COP_AND == GetCOp( &tokenarray[*i] ) ) {
|
|
|
|
(*i)++;
|
|
DebugMsg1(("%u GetAndExpression: &&-operator found, is_true=%u, lastjmp=%s\n", evallvl, is_true, hllop->lastjmp ? hllop->lastjmp : "NULL" ));
|
|
|
|
if ( is_true ) {
|
|
/* todo: please describe what's done here and why! */
|
|
if ( hllop->lastjmp ) {
|
|
char *p = hllop->lastjmp;
|
|
InvertJump( p ); /* step 1 */
|
|
if ( truelabel == 0 ) /* step 2 */
|
|
truelabel = GetHllLabel();
|
|
|
|
if ( *p ) { /* v2.11: there might be a 0 at lastjmp */
|
|
p += 4; /* skip 'jcc ' or 'jmp ' */
|
|
GetLabelStr( truelabel, p );
|
|
strcat( p, EOLSTR );
|
|
}
|
|
|
|
DebugMsg1(("%u GetAndExpression: jmp inverted >%s<\n", evallvl, hllop->lastjmp ));
|
|
ReplaceLabel( buffer, GetLabel( hll, ilabel ), truelabel );
|
|
hllop->lastjmp = NULL;
|
|
}
|
|
}
|
|
ptr += strlen( ptr );
|
|
hllop->lasttruelabel = 0; /* v2.08 */
|
|
if ( ERROR == GetSimpleExpression( hll, i, tokenarray, ilabel, is_true, ptr, hllop ) )
|
|
return( ERROR );
|
|
};
|
|
|
|
if ( truelabel > 0 ) {
|
|
ptr += strlen( ptr );
|
|
GetLabelStr( truelabel, ptr );
|
|
strcat( ptr, LABELQUAL EOLSTR );
|
|
DebugMsg1(("%u GetAndExpression: label added >%s<\n", evallvl, ptr ));
|
|
hllop->lastjmp = NULL;
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* operator ||, which has the lowest precedence, is handled here */
|
|
|
|
static ret_code GetExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true, char *buffer, struct hll_opnd *hllop )
|
|
/********************************************************************************************************************************************************/
|
|
{
|
|
char *ptr = buffer;
|
|
uint_32 truelabel = 0;
|
|
|
|
DebugMsg1(("%u GetExpression(>%.32s< buf=>%s<) enter\n", ++evallvl, tokenarray[*i].tokpos, buffer ));
|
|
|
|
/* v2.08: structure changed from for(;;) to while() to increase
|
|
* readability and - optionally - handle the second operand differently
|
|
* than the first.
|
|
*/
|
|
|
|
if ( ERROR == GetAndExpression( hll, i, tokenarray, ilabel, is_true, ptr, hllop ) ) {
|
|
DebugMsg1(("%u GetExpression exit, error\n", evallvl-- ));
|
|
return( ERROR );
|
|
}
|
|
while ( COP_OR == GetCOp( &tokenarray[*i] ) ) {
|
|
|
|
uint_32 nlabel;
|
|
uint_32 olabel;
|
|
char buff[16];
|
|
|
|
/* the generated code of last simple expression has to be modified
|
|
1. the last jump must be inverted
|
|
2. a "is_true" label must be created (it's used to jump "behind" the expr)
|
|
3. create a new label
|
|
4. the current "false" label must be generated
|
|
|
|
if it is a .REPEAT, step 4 is slightly more difficult, since the "false"
|
|
label is already "gone":
|
|
4a. create a new label
|
|
4b. replace the "false" label in the generated code by the new label
|
|
*/
|
|
|
|
(*i)++;
|
|
DebugMsg1(("%u GetExpression: ||-operator found, is_true=%u, lastjmp=%s\n", evallvl, is_true, hllop->lastjmp ? hllop->lastjmp : "NULL" ));
|
|
|
|
if ( is_true == FALSE ) {
|
|
if ( hllop->lastjmp ) {
|
|
char *p = hllop->lastjmp;
|
|
InvertJump( p ); /* step 1 */
|
|
if ( truelabel == 0 ) /* step 2 */
|
|
truelabel = GetHllLabel();
|
|
if ( *p ) { /* v2.11: there might be a 0 at lastjmp */
|
|
p += 4; /* skip 'jcc ' or 'jmp ' */
|
|
GetLabelStr( truelabel, p );
|
|
strcat( p, EOLSTR );
|
|
}
|
|
/* v2.08: if-block added */
|
|
if ( hllop->lasttruelabel )
|
|
ReplaceLabel( ptr, hllop->lasttruelabel, truelabel );
|
|
DebugMsg1(("%u GetExpression: jmp inverted, dest changed >%s<\n", evallvl, ptr ));
|
|
hllop->lastjmp = NULL;
|
|
|
|
nlabel = GetHllLabel(); /* step 3 */
|
|
olabel = GetLabel( hll, ilabel );
|
|
if ( hll->cmd == HLL_REPEAT ) {
|
|
ReplaceLabel( buffer, olabel, nlabel );
|
|
sprintf( ptr + strlen( ptr ), "%s" LABELQUAL EOLSTR, GetLabelStr( nlabel, buff ) );
|
|
} else {
|
|
sprintf( ptr + strlen( ptr ), "%s" LABELQUAL EOLSTR, GetLabelStr( olabel, buff ) );
|
|
ReplaceLabel( buffer, olabel, nlabel );
|
|
}
|
|
DebugMsg1(("%u GetExpression: dest changed, label added >%s<\n", evallvl, ptr ));
|
|
}
|
|
}
|
|
ptr += strlen( ptr );
|
|
hllop->lasttruelabel = 0; /* v2.08 */
|
|
if ( ERROR == GetAndExpression( hll, i, tokenarray, ilabel, is_true, ptr, hllop ) ) {
|
|
DebugMsg1(("%u GetExpression exit, error\n", evallvl-- ));
|
|
return( ERROR );
|
|
}
|
|
}
|
|
if ( truelabel > 0 ) {
|
|
/* v2.08: this is needed, but ober-hackish. to be improved... */
|
|
if ( hllop->lastjmp && hllop->lasttruelabel ) {
|
|
DebugMsg1(("%u GetExpression: suppressed ReplaceLabel %u -> %u, lastjmp=%s\n", evallvl, hllop->lasttruelabel, truelabel, hllop->lastjmp ));
|
|
ReplaceLabel( ptr, hllop->lasttruelabel, truelabel );
|
|
*(strchr( hllop->lastjmp, EOLCHAR ) + 1 ) = NULLC;
|
|
}
|
|
ptr += strlen( ptr );
|
|
GetLabelStr( truelabel, ptr );
|
|
strcat( ptr, LABELQUAL EOLSTR );
|
|
DebugMsg1(("%u GetExpression: label added >%s<\n", evallvl, ptr ));
|
|
hllop->lasttruelabel = truelabel; /* v2.08 */
|
|
}
|
|
DebugMsg1(("%u GetExpression exit\n", evallvl-- ));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/*
|
|
* write assembly test lines to line queue.
|
|
* v2.11: local line buffer removed; src pointer has become a parameter.
|
|
*/
|
|
|
|
static ret_code QueueTestLines( char *src )
|
|
/*****************************************/
|
|
{
|
|
char *start;
|
|
|
|
DebugMsg1(("QueueTestLines(\"%s\") enter\n", src ? src : "NULL" ));
|
|
while ( src ) {
|
|
//if (*src == ' ') src++; /* v2.11: obsolete */
|
|
start = src;
|
|
if ( (src = strchr( src, EOLCHAR )) != NULL )
|
|
*src++ = NULLC;
|
|
if ( *start )
|
|
AddLineQueue( start );
|
|
}
|
|
|
|
DebugMsg1(("QueueTestLines exit\n"));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/*
|
|
* evaluate the C like boolean expression found in HLL structs
|
|
* like .IF, .ELSEIF, .WHILE, .UNTIL and .UNTILCXZ
|
|
* might return multiple lines (strings separated by EOLCHAR)
|
|
* - i = index for tokenarray[] where expression starts. Is restricted
|
|
* to one source line (till T_FINAL)
|
|
* - label: label to jump to if expression is <is_true>!
|
|
* is_true:
|
|
* .IF: FALSE
|
|
* .ELSEIF: FALSE
|
|
* .WHILE: TRUE
|
|
* .UNTIL: FALSE
|
|
* .UNTILCXZ: FALSE
|
|
* .BREAK .IF:TRUE
|
|
* .CONT .IF: TRUE
|
|
*/
|
|
|
|
|
|
static ret_code EvaluateHllExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true, char *buffer )
|
|
/****************************************************************************************************************************************/
|
|
{
|
|
struct hll_opnd hllop = {NULL,0};
|
|
|
|
DebugMsg1(("EvaluateHllExpression enter\n"));
|
|
|
|
*buffer = NULLC;
|
|
if ( ERROR == GetExpression( hll, i, tokenarray, ilabel, is_true, buffer, &hllop ) )
|
|
return( ERROR );
|
|
/* v2.11: changed */
|
|
//if ( *buffer == EOLCHAR ) {
|
|
//DebugMsg(( "EvaluateHllExpression: EOL at pos 0 in line buffer\n" ));
|
|
if ( tokenarray[*i].token != T_FINAL ) {
|
|
DebugMsg(( "EvaluateHllExpression: unexpected tokens >%s<\n", tokenarray[*i].tokpos ));
|
|
return( EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE ) );
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* for .UNTILCXZ: check if expression is simple enough.
|
|
* what's acceptable is ONE condition, and just operators == and !=
|
|
* Constants (0 or != 0) are also accepted.
|
|
*/
|
|
|
|
static ret_code CheckCXZLines( char *p )
|
|
/**************************************/
|
|
{
|
|
int lines = 0;
|
|
int i;
|
|
int addchars;
|
|
char *px;
|
|
bool NL = TRUE;
|
|
|
|
DebugMsg1(("CheckCXZLines enter, p=>%s<\n", p ));
|
|
/* syntax ".untilcxz 1" has a problem: there's no "jmp" generated at all.
|
|
* if this syntax is to be supported, activate the #if below.
|
|
*/
|
|
for (; *p; p++ ) {
|
|
if ( *p == EOLCHAR ) {
|
|
NL = TRUE;
|
|
lines++;
|
|
} else if ( NL ) {
|
|
NL = FALSE;
|
|
if ( *p == 'j' ) {
|
|
p++;
|
|
/* v2.06: rewritten */
|
|
if ( *p == 'm' && lines == 0 ) {
|
|
addchars = 2; /* make room for 2 chars, to replace "jmp" by "loope" */
|
|
px = "loope";
|
|
} else if ( lines == 1 && ( *p == 'z' || (*p == 'n' && *(p+1) == 'z') ) ) {
|
|
addchars = 3; /* make room for 3 chars, to replace "jz"/"jnz" by "loopz"/"loopnz" */
|
|
px = "loop";
|
|
} else
|
|
return( ERROR ); /* anything else is "too complex" */
|
|
//replace_instr:
|
|
for ( p--, i = strlen( p ); i >= 0; i-- ) {
|
|
*(p+addchars+i) = *(p+i);
|
|
}
|
|
memcpy( p, px, strlen( px ) );
|
|
}
|
|
#if 0 /* handle ".untilcxz 1" like masm does */
|
|
else if ( *p == ' ' && *(p+1) == EOLCHAR && lines == 0 ) {
|
|
p++;
|
|
GetLabelStr( hll->labels[LSTART], p );
|
|
strcat( p, EOLSTR );
|
|
addchars = 5;
|
|
px = "loope";
|
|
goto replace_instr;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if ( lines > 2 )
|
|
return( ERROR );
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* .IF, .WHILE or .REPEAT directive */
|
|
|
|
ret_code HllStartDir( int i, struct asm_tok tokenarray[] )
|
|
/********************************************************/
|
|
{
|
|
struct hll_item *hll;
|
|
ret_code rc = NOT_ERROR;
|
|
int cmd = tokenarray[i].tokval;
|
|
char buff[16];
|
|
char buffer[MAX_LINE_LEN*2];
|
|
|
|
DebugMsg1(("HllStartDir(%s) enter\n", tokenarray[i].string_ptr ));
|
|
|
|
i++; /* skip directive */
|
|
|
|
/* v2.06: is there an item on the free stack? */
|
|
if ( HllFree ) {
|
|
hll = HllFree;
|
|
DebugCmd( cntReused++ );
|
|
} else {
|
|
hll = LclAlloc( sizeof( struct hll_item ) );
|
|
DebugCmd( cntAlloc++ );
|
|
}
|
|
|
|
/* structure for .IF .ELSE .ENDIF
|
|
* cond jump to LTEST-label
|
|
* ...
|
|
* jmp LEXIT
|
|
* LTEST:
|
|
* ...
|
|
* LEXIT:
|
|
|
|
* structure for .IF .ELSEIF
|
|
* cond jump to LTEST
|
|
* ...
|
|
* jmp LEXIT
|
|
* LTEST:
|
|
* cond jump to (new) LTEST
|
|
* ...
|
|
* jmp LEXIT
|
|
* LTEST:
|
|
* ...
|
|
|
|
* structure for .WHILE and .REPEAT:
|
|
* jmp LTEST (for .WHILE only)
|
|
* LSTART:
|
|
* ...
|
|
* LTEST: (jumped to by .continue)
|
|
* a) test end condition, cond jump to LSTART label
|
|
* b) unconditional jump to LSTART label
|
|
* LEXIT: (jumped to by .BREAK)
|
|
*/
|
|
|
|
hll->labels[LEXIT] = 0;
|
|
|
|
switch ( cmd ) {
|
|
case T_DOT_IF:
|
|
hll->labels[LSTART] = 0; /* not used by .IF */
|
|
hll->labels[LTEST] = GetHllLabel();
|
|
hll->cmd = HLL_IF;
|
|
hll->flags = 0;
|
|
/* get the C-style expression, convert to ASM code lines */
|
|
rc = EvaluateHllExpression( hll, &i, tokenarray, LTEST, FALSE, buffer );
|
|
if ( rc == NOT_ERROR ) {
|
|
QueueTestLines( buffer );
|
|
/* if no lines have been created, the LTEST label isn't needed */
|
|
//if ( !is_linequeue_populated() ) {
|
|
if ( buffer[0] == NULLC ) {
|
|
hll->labels[LTEST] = 0;
|
|
}
|
|
}
|
|
break;
|
|
case T_DOT_WHILE:
|
|
case T_DOT_REPEAT:
|
|
/* create the label to start of loop */
|
|
hll->labels[LSTART] = GetHllLabel();
|
|
hll->labels[LTEST] = 0; /* v2.11: test label is created only if needed */
|
|
//hll->labels[LEXIT] = GetHllLabel(); /* v2.11: LEXIT is only needed for .BREAK */
|
|
if ( cmd == T_DOT_WHILE ) {
|
|
hll->cmd = HLL_WHILE;
|
|
hll->condlines = NULL;
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
rc = EvaluateHllExpression( hll, &i, tokenarray, LSTART, TRUE, buffer );
|
|
if ( rc == NOT_ERROR ) {
|
|
int size;
|
|
size = strlen( buffer ) + 1;
|
|
hll->condlines = LclAlloc( size );
|
|
memcpy( hll->condlines, buffer, size );
|
|
DebugCmd( cntCond++ ); DebugCmd( cntCondBytes += size );
|
|
}
|
|
} else
|
|
buffer[0] = NULLC; /* just ".while" without expression is accepted */
|
|
|
|
/* create a jump to test label */
|
|
/* optimisation: if line at 'test' label is just a jump, dont create label and don't jump! */
|
|
if ( _memicmp( buffer, "jmp", 3 ) ) {
|
|
hll->labels[LTEST] = GetHllLabel();
|
|
AddLineQueueX( JMPPREFIX "jmp %s", GetLabelStr( hll->labels[LTEST], buff ) );
|
|
}
|
|
} else {
|
|
hll->cmd = HLL_REPEAT;
|
|
}
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LSTART], buff ) );
|
|
break;
|
|
#ifdef DEBUG_OUT
|
|
default: /**/myassert( 0 ); break;
|
|
#endif
|
|
}
|
|
|
|
if ( tokenarray[i].token != T_FINAL && rc == NOT_ERROR ) {
|
|
DebugMsg(("HllStartDir: unexpected token [%s]\n", tokenarray[i].tokpos ));
|
|
EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
|
|
rc = ERROR;
|
|
//return( ERROR ); /* v2.08: continue and parse the line queue */
|
|
}
|
|
/* v2.06: remove the item from the free stack */
|
|
if ( hll == HllFree )
|
|
HllFree = hll->next;
|
|
hll->next = HllStack;
|
|
HllStack = hll;
|
|
|
|
if ( ModuleInfo.list )
|
|
LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
|
|
|
|
if ( is_linequeue_populated() ) /* might be NULL! (".if 1") */
|
|
RunLineQueue();
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/*
|
|
* .ENDIF, .ENDW, .UNTIL and .UNTILCXZ directives.
|
|
* These directives end a .IF, .WHILE or .REPEAT block.
|
|
*/
|
|
ret_code HllEndDir( int i, struct asm_tok tokenarray[] )
|
|
/******************************************************/
|
|
{
|
|
//struct asym *sym;
|
|
struct hll_item *hll;
|
|
ret_code rc = NOT_ERROR;
|
|
int cmd = tokenarray[i].tokval;
|
|
char buff[16];
|
|
char buffer[MAX_LINE_LEN*2];
|
|
|
|
DebugMsg1(("HllEndDir(%s) enter\n", tokenarray[i].string_ptr ));
|
|
|
|
if ( HllStack == NULL ) {
|
|
DebugMsg(("HllEndDir: hll stack is empty\n"));
|
|
return( EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK ) );
|
|
}
|
|
|
|
hll = HllStack;
|
|
HllStack = hll->next;
|
|
/* v2.06: move the item to the free stack */
|
|
hll->next = HllFree;
|
|
HllFree = hll;
|
|
|
|
switch ( cmd ) {
|
|
case T_DOT_ENDIF:
|
|
if ( hll->cmd != HLL_IF ) {
|
|
DebugMsg(("HllEndDir: no .IF on the hll stack\n"));
|
|
return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
|
|
}
|
|
i++;
|
|
/* if a test label isn't created yet, create it */
|
|
if ( hll->labels[LTEST] ) {
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
|
|
}
|
|
break;
|
|
case T_DOT_ENDW:
|
|
if ( hll->cmd != HLL_WHILE ) {
|
|
DebugMsg(("HllEndDir: no .WHILE on the hll stack\n"));
|
|
return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
|
|
}
|
|
i++;
|
|
/* create test label */
|
|
if ( hll->labels[LTEST] ) {
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
|
|
}
|
|
QueueTestLines( hll->condlines );
|
|
LclFree( hll->condlines );
|
|
break;
|
|
case T_DOT_UNTILCXZ:
|
|
if ( hll->cmd != HLL_REPEAT ) {
|
|
DebugMsg(("HllEndDir: no .REPEAT on the hll stack\n"));
|
|
return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
|
|
}
|
|
i++;
|
|
if ( hll->labels[LTEST] ) /* v2.11: LTEST only needed if .CONTINUE has occured */
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
|
|
|
|
/* read in optional (simple) expression */
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
rc = EvaluateHllExpression( hll, &i, tokenarray, LSTART, FALSE, buffer );
|
|
if ( rc == NOT_ERROR ) {
|
|
rc = CheckCXZLines( buffer );
|
|
if ( rc == NOT_ERROR )
|
|
QueueTestLines( buffer ); /* write condition lines */
|
|
else
|
|
EmitError( EXPR_TOO_COMPLEX_FOR_UNTILCXZ );
|
|
}
|
|
} else {
|
|
AddLineQueueX( JMPPREFIX "loop %s", GetLabelStr( hll->labels[LSTART], buff ) );
|
|
}
|
|
break;
|
|
case T_DOT_UNTIL:
|
|
if ( hll->cmd != HLL_REPEAT ) {
|
|
DebugMsg(("HllEndDir: no .REPEAT on the hll stack\n"));
|
|
return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
|
|
}
|
|
i++;
|
|
if ( hll->labels[LTEST] ) /* v2.11: LTEST only needed if .CONTINUE has occured */
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
|
|
|
|
/* read in (optional) expression */
|
|
/* if expression is missing, just generate nothing */
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
rc = EvaluateHllExpression( hll, &i, tokenarray, LSTART, FALSE, buffer );
|
|
if ( rc == NOT_ERROR )
|
|
QueueTestLines( buffer ); /* write condition lines */
|
|
}
|
|
break;
|
|
#ifdef DEBUG_OUT
|
|
default: /**/myassert( 0 ); break;
|
|
#endif
|
|
}
|
|
|
|
/* create the exit label if it has been referenced */
|
|
if ( hll->labels[LEXIT] )
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LEXIT], buff ) );
|
|
|
|
if ( tokenarray[i].token != T_FINAL && rc == NOT_ERROR ) {
|
|
EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
|
|
rc = ERROR;
|
|
}
|
|
if ( ModuleInfo.list )
|
|
LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
|
|
|
|
/* v2.11: always run line-queue if it's not empty. */
|
|
if ( is_linequeue_populated() )
|
|
RunLineQueue();
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/*
|
|
* .ELSE, .ELSEIF, .CONTINUE and .BREAK directives.
|
|
* .ELSE, .ELSEIF:
|
|
* - create a jump to exit label
|
|
* - render test label if it was referenced
|
|
* - for .ELSEIF, create new test label and evaluate expression
|
|
* .CONTINUE, .BREAK:
|
|
* - jump to test / exit label of innermost .WHILE/.REPEAT block
|
|
*
|
|
*/
|
|
ret_code HllExitDir( int i, struct asm_tok tokenarray[] )
|
|
/*******************************************************/
|
|
{
|
|
//int level;
|
|
//struct asym *sym;
|
|
struct hll_item *hll;
|
|
ret_code rc = NOT_ERROR;
|
|
int idx;
|
|
int cmd = tokenarray[i].tokval;
|
|
char buff[16];
|
|
char buffer[MAX_LINE_LEN*2];
|
|
|
|
DebugMsg1(("HllExitDir(%s) enter\n", tokenarray[i].string_ptr ));
|
|
|
|
hll = HllStack;
|
|
|
|
if ( hll == NULL ) {
|
|
DebugMsg(("HllExitDir stack error\n"));
|
|
return( EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK ) );
|
|
}
|
|
|
|
switch ( cmd ) {
|
|
case T_DOT_ELSE:
|
|
case T_DOT_ELSEIF:
|
|
if ( hll->cmd != HLL_IF ) {
|
|
DebugMsg(("HllExitDir(%s): labels[LTEST]=%X\n", tokenarray[i].string_ptr, hll->labels[LTEST]));
|
|
return( EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr ) );
|
|
}
|
|
/* v2.08: check for multiple ELSE clauses */
|
|
if ( hll->flags & HLLF_ELSEOCCURED ) {
|
|
return( EmitError( DOT_ELSE_CLAUSE_ALREADY_OCCURED_IN_THIS_DOT_IF_BLOCK ) );
|
|
}
|
|
|
|
/* the 'exit'-label is only needed if an .ELSE branch exists.
|
|
* That's why it is created delayed.
|
|
*/
|
|
if ( hll->labels[LEXIT] == 0 )
|
|
hll->labels[LEXIT] = GetHllLabel();
|
|
AddLineQueueX( JMPPREFIX "jmp %s", GetLabelStr( hll->labels[LEXIT], buff ) );
|
|
|
|
if ( hll->labels[LTEST] > 0 ) {
|
|
AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
|
|
hll->labels[LTEST] = 0;
|
|
}
|
|
i++;
|
|
if ( cmd == T_DOT_ELSEIF ) {
|
|
/* create new labels[LTEST] label */
|
|
hll->labels[LTEST] = GetHllLabel();
|
|
rc = EvaluateHllExpression( hll, &i, tokenarray, LTEST, FALSE, buffer );
|
|
if ( rc == NOT_ERROR )
|
|
QueueTestLines( buffer );
|
|
} else
|
|
hll->flags |= HLLF_ELSEOCCURED;
|
|
|
|
break;
|
|
case T_DOT_BREAK:
|
|
case T_DOT_CONTINUE:
|
|
for ( ; hll && hll->cmd == HLL_IF; hll = hll->next );
|
|
if ( hll == NULL ) {
|
|
return( EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK ) );
|
|
}
|
|
/* v2.11: create 'exit' and 'test' labels delayed.
|
|
*/
|
|
if ( cmd == T_DOT_BREAK ) {
|
|
if ( hll->labels[LEXIT] == 0 )
|
|
hll->labels[LEXIT] = GetHllLabel();
|
|
idx = LEXIT;
|
|
} else {
|
|
/* 'test' is not created for .WHILE loops here; because
|
|
* if it doesn't exist, there's no condition to test.
|
|
*/
|
|
if ( hll->cmd == HLL_REPEAT && hll->labels[LTEST] == 0 )
|
|
hll->labels[LTEST] = GetHllLabel();
|
|
idx = ( hll->labels[LTEST] ? LTEST : LSTART );
|
|
}
|
|
|
|
/* .BREAK .IF ... or .CONTINUE .IF ? */
|
|
i++;
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
if ( tokenarray[i].token == T_DIRECTIVE && tokenarray[i].tokval == T_DOT_IF ) {
|
|
enum hll_cmd savedcmd = hll->cmd;
|
|
hll->cmd = HLL_BREAK;
|
|
i++;
|
|
/* v2.11: set rc and don't exit if an error occurs; see hll3.aso */
|
|
rc = EvaluateHllExpression( hll, &i, tokenarray, idx, TRUE, buffer );
|
|
if ( rc == NOT_ERROR )
|
|
QueueTestLines( buffer );
|
|
hll->cmd = savedcmd;
|
|
}
|
|
} else {
|
|
AddLineQueueX( JMPPREFIX "jmp %s", GetLabelStr( hll->labels[idx], buff ) );
|
|
}
|
|
break;
|
|
#ifdef DEBUG_OUT
|
|
default: /**/myassert( 0 ); break;
|
|
#endif
|
|
}
|
|
if ( tokenarray[i].token != T_FINAL && rc == NOT_ERROR ) {
|
|
EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
|
|
rc = ERROR;
|
|
}
|
|
|
|
if ( ModuleInfo.list )
|
|
LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
|
|
|
|
/* v2.11: always run line-queue if it's not empty. */
|
|
if ( is_linequeue_populated() )
|
|
RunLineQueue();
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/* check if an hll block has been left open. called after pass 1 */
|
|
|
|
void HllCheckOpen( void )
|
|
/***********************/
|
|
{
|
|
if ( HllStack ) {
|
|
//EmitErr( BLOCK_NESTING_ERROR, ".if-.repeat-.while" );
|
|
EmitErr( UNMATCHED_BLOCK_NESTING, ".if-.repeat-.while" );
|
|
}
|
|
DebugMsg(("HllCheckOpen: allocated items:%u, reused items:%u, .while cond-blocks/bytes:%u/%u\n", cntAlloc, cntReused, cntCond, cntCondBytes ));
|
|
}
|
|
|
|
#if FASTMEM==0
|
|
void HllFini( void )
|
|
/******************/
|
|
{
|
|
struct hll_item *curr;
|
|
struct hll_item *next;
|
|
/* release the items in the HllFree heap.
|
|
* there might also be some left in HllStack...
|
|
*/
|
|
for ( curr = HllFree; curr; curr = next ) {
|
|
next = curr->next;
|
|
LclFree( curr );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* HllInit() is called for each pass */
|
|
|
|
void HllInit( int pass )
|
|
/**********************/
|
|
{
|
|
//if ( pass == PASS_1 )
|
|
// HllFree = NULL;
|
|
|
|
//HllStack = NULL; /* empty stack of open hll directives */
|
|
ModuleInfo.hll_label = 0; /* init hll label counter */
|
|
#ifdef DEBUG_OUT
|
|
evallvl = 0;
|
|
if ( pass == PASS_1 ) {
|
|
cntAlloc = 0;
|
|
cntReused = 0;
|
|
cntCond = 0;
|
|
cntCondBytes = 0;
|
|
}
|
|
#endif
|
|
return;
|
|
}
|