You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

478 lines
12 KiB

#include "rc_internal.h"
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#ifndef RC_DISABLE_LUA
#ifdef __cplusplus
extern "C" {
#endif
#include <lua.h>
#include <lauxlib.h>
#ifdef __cplusplus
}
#endif
#endif /* RC_DISABLE_LUA */
static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) {
const char* aux = *memaddr;
#ifndef RC_DISABLE_LUA
const char* id;
#endif
if (*aux++ != '@') {
return RC_INVALID_LUA_OPERAND;
}
if (!isalpha((unsigned char)*aux)) {
return RC_INVALID_LUA_OPERAND;
}
#ifndef RC_DISABLE_LUA
id = aux;
#endif
while (isalnum((unsigned char)*aux) || *aux == '_') {
aux++;
}
#ifndef RC_DISABLE_LUA
if (parse->L != 0) {
if (!lua_istable(parse->L, parse->funcs_ndx)) {
return RC_INVALID_LUA_OPERAND;
}
lua_pushlstring(parse->L, id, aux - id);
lua_gettable(parse->L, parse->funcs_ndx);
if (!lua_isfunction(parse->L, -1)) {
lua_pop(parse->L, 1);
return RC_INVALID_LUA_OPERAND;
}
self->value.luafunc = luaL_ref(parse->L, LUA_REGISTRYINDEX);
}
#endif /* RC_DISABLE_LUA */
self->type = RC_OPERAND_LUA;
*memaddr = aux;
return RC_OK;
}
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
const char* aux = *memaddr;
unsigned address;
char size;
int ret;
switch (*aux) {
case 'd': case 'D':
self->type = RC_OPERAND_DELTA;
++aux;
break;
case 'p': case 'P':
self->type = RC_OPERAND_PRIOR;
++aux;
break;
case 'b': case 'B':
self->type = RC_OPERAND_BCD;
++aux;
break;
case '~':
self->type = RC_OPERAND_INVERTED;
++aux;
break;
default:
self->type = RC_OPERAND_ADDRESS;
break;
}
ret = rc_parse_memref(&aux, &self->size, &address);
if (ret != RC_OK)
return ret;
size = rc_memref_shared_size(self->size);
if (size != self->size && self->type == RC_OPERAND_PRIOR) {
/* if the shared size differs from the requested size and it's a prior operation, we
* have to check to make sure both sizes use the same mask, or the prior value may be
* updated when bits outside the mask are modified, which would make it look like the
* current value once the mask is applied. if the mask differs, create a new
* non-shared record for tracking the prior data. */
if (rc_memref_mask(size) != rc_memref_mask(self->size))
size = self->size;
}
self->value.memref = rc_alloc_memref(parse, address, size, (char)is_indirect);
if (parse->offset < 0)
return parse->offset;
*memaddr = aux;
return RC_OK;
}
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
const char* aux = *memaddr;
char* end;
int ret;
unsigned long value;
int negative;
int allow_decimal = 0;
self->size = RC_MEMSIZE_32_BITS;
switch (*aux) {
case 'h': case 'H': /* hex constant */
if (aux[2] == 'x' || aux[2] == 'X') {
/* H0x1234 is a typo - either H1234 or 0xH1234 was probably meant */
return RC_INVALID_CONST_OPERAND;
}
value = strtoul(++aux, &end, 16);
if (end == aux)
return RC_INVALID_CONST_OPERAND;
if (value > 0xffffffffU)
value = 0xffffffffU;
self->type = RC_OPERAND_CONST;
self->value.num = (unsigned)value;
aux = end;
break;
case 'f': case 'F': /* floating point constant */
if (isalpha((unsigned char)aux[1])) {
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
if (ret < 0)
return ret;
break;
}
allow_decimal = 1;
/* fall through */
case 'v': case 'V': /* signed integer constant */
++aux;
/* fall through */
case '+': case '-': /* signed integer constant */
negative = 0;
if (*aux == '-') {
negative = 1;
++aux;
}
else if (*aux == '+') {
++aux;
}
value = strtoul(aux, &end, 10);
if (*end == '.' && allow_decimal) {
/* custom parser for decimal values to ignore locale */
unsigned long shift = 1;
unsigned long fraction = 0;
aux = end + 1;
if (*aux < '0' || *aux > '9')
return RC_INVALID_FP_OPERAND;
do {
/* only keep as many digits as will fit in a 32-bit value to prevent overflow.
* float only has around 7 digits of precision anyway. */
if (shift < 1000000000) {
fraction *= 10;
fraction += (*aux - '0');
shift *= 10;
}
++aux;
} while (*aux >= '0' && *aux <= '9');
if (fraction != 0) {
/* non-zero fractional part, convert to double and merge in integer portion */
const double dbl_fraction = ((double)fraction) / ((double)shift);
if (negative)
self->value.dbl = ((double)(-((long)value))) - dbl_fraction;
else
self->value.dbl = (double)value + dbl_fraction;
}
else {
/* fractional part is 0, just convert the integer portion */
if (negative)
self->value.dbl = (double)(-((long)value));
else
self->value.dbl = (double)value;
}
self->type = RC_OPERAND_FP;
}
else {
/* not a floating point value, make sure something was read and advance the read pointer */
if (end == aux)
return allow_decimal ? RC_INVALID_FP_OPERAND : RC_INVALID_CONST_OPERAND;
aux = end;
if (value > 0x7fffffffU)
value = 0x7fffffffU;
self->type = RC_OPERAND_CONST;
if (negative)
self->value.num = (unsigned)(-((long)value));
else
self->value.num = (unsigned)value;
}
break;
case '0':
if (aux[1] == 'x' || aux[1] == 'X') { /* hex integer constant */
/* fall through */
default:
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
if (ret < 0)
return ret;
break;
}
/* fall through for case '0' where not '0x' */
case '1': case '2': case '3': case '4': case '5': /* unsigned integer constant */
case '6': case '7': case '8': case '9':
value = strtoul(aux, &end, 10);
if (end == aux)
return RC_INVALID_CONST_OPERAND;
if (value > 0xffffffffU)
value = 0xffffffffU;
self->type = RC_OPERAND_CONST;
self->value.num = (unsigned)value;
aux = end;
break;
case '@':
ret = rc_parse_operand_lua(self, &aux, parse);
if (ret < 0)
return ret;
break;
}
*memaddr = aux;
return RC_OK;
}
#ifndef RC_DISABLE_LUA
typedef struct {
rc_peek_t peek;
void* ud;
}
rc_luapeek_t;
static int rc_luapeek(lua_State* L) {
unsigned address = (unsigned)luaL_checkinteger(L, 1);
unsigned num_bytes = (unsigned)luaL_checkinteger(L, 2);
rc_luapeek_t* luapeek = (rc_luapeek_t*)lua_touserdata(L, 3);
unsigned value = luapeek->peek(address, num_bytes, luapeek->ud);
lua_pushinteger(L, value);
return 1;
}
#endif /* RC_DISABLE_LUA */
int rc_operand_is_float_memref(const rc_operand_t* self) {
switch (self->size) {
case RC_MEMSIZE_FLOAT:
case RC_MEMSIZE_MBF32:
case RC_MEMSIZE_MBF32_LE:
return 1;
default:
return 0;
}
}
int rc_operand_is_memref(const rc_operand_t* self) {
switch (self->type) {
case RC_OPERAND_CONST:
case RC_OPERAND_FP:
case RC_OPERAND_LUA:
return 0;
default:
return 1;
}
}
int rc_operand_is_float(const rc_operand_t* self) {
if (self->type == RC_OPERAND_FP)
return 1;
return rc_operand_is_float_memref(self);
}
unsigned rc_transform_operand_value(unsigned value, const rc_operand_t* self) {
switch (self->type)
{
case RC_OPERAND_BCD:
switch (self->size)
{
case RC_MEMSIZE_8_BITS:
value = ((value >> 4) & 0x0f) * 10
+ ((value ) & 0x0f);
break;
case RC_MEMSIZE_16_BITS:
case RC_MEMSIZE_16_BITS_BE:
value = ((value >> 12) & 0x0f) * 1000
+ ((value >> 8) & 0x0f) * 100
+ ((value >> 4) & 0x0f) * 10
+ ((value ) & 0x0f);
break;
case RC_MEMSIZE_24_BITS:
case RC_MEMSIZE_24_BITS_BE:
value = ((value >> 20) & 0x0f) * 100000
+ ((value >> 16) & 0x0f) * 10000
+ ((value >> 12) & 0x0f) * 1000
+ ((value >> 8) & 0x0f) * 100
+ ((value >> 4) & 0x0f) * 10
+ ((value ) & 0x0f);
break;
case RC_MEMSIZE_32_BITS:
case RC_MEMSIZE_32_BITS_BE:
case RC_MEMSIZE_VARIABLE:
value = ((value >> 28) & 0x0f) * 10000000
+ ((value >> 24) & 0x0f) * 1000000
+ ((value >> 20) & 0x0f) * 100000
+ ((value >> 16) & 0x0f) * 10000
+ ((value >> 12) & 0x0f) * 1000
+ ((value >> 8) & 0x0f) * 100
+ ((value >> 4) & 0x0f) * 10
+ ((value ) & 0x0f);
break;
default:
break;
}
break;
case RC_OPERAND_INVERTED:
switch (self->size)
{
case RC_MEMSIZE_LOW:
case RC_MEMSIZE_HIGH:
value ^= 0x0f;
break;
case RC_MEMSIZE_8_BITS:
value ^= 0xff;
break;
case RC_MEMSIZE_16_BITS:
case RC_MEMSIZE_16_BITS_BE:
value ^= 0xffff;
break;
case RC_MEMSIZE_24_BITS:
case RC_MEMSIZE_24_BITS_BE:
value ^= 0xffffff;
break;
case RC_MEMSIZE_32_BITS:
case RC_MEMSIZE_32_BITS_BE:
case RC_MEMSIZE_VARIABLE:
value ^= 0xffffffff;
break;
default:
value ^= 0x01;
break;
}
break;
default:
break;
}
return value;
}
void rc_evaluate_operand(rc_typed_value_t* result, rc_operand_t* self, rc_eval_state_t* eval_state) {
#ifndef RC_DISABLE_LUA
rc_luapeek_t luapeek;
#endif /* RC_DISABLE_LUA */
/* step 1: read memory */
switch (self->type) {
case RC_OPERAND_CONST:
result->type = RC_VALUE_TYPE_UNSIGNED;
result->value.u32 = self->value.num;
return;
case RC_OPERAND_FP:
result->type = RC_VALUE_TYPE_FLOAT;
result->value.f32 = (float)self->value.dbl;
return;
case RC_OPERAND_LUA:
result->type = RC_VALUE_TYPE_UNSIGNED;
result->value.u32 = 0;
#ifndef RC_DISABLE_LUA
if (eval_state->L != 0) {
lua_rawgeti(eval_state->L, LUA_REGISTRYINDEX, self->value.luafunc);
lua_pushcfunction(eval_state->L, rc_luapeek);
luapeek.peek = eval_state->peek;
luapeek.ud = eval_state->peek_userdata;
lua_pushlightuserdata(eval_state->L, &luapeek);
if (lua_pcall(eval_state->L, 2, 1, 0) == LUA_OK) {
if (lua_isboolean(eval_state->L, -1)) {
result->value.u32 = (unsigned)lua_toboolean(eval_state->L, -1);
}
else {
result->value.u32 = (unsigned)lua_tonumber(eval_state->L, -1);
}
}
lua_pop(eval_state->L, 1);
}
#endif /* RC_DISABLE_LUA */
break;
default:
result->type = RC_VALUE_TYPE_UNSIGNED;
result->value.u32 = rc_get_memref_value(self->value.memref, self->type, eval_state);
break;
}
/* step 2: convert read memory to desired format */
rc_transform_memref_value(result, self->size);
/* step 3: apply logic (BCD/invert) */
if (result->type == RC_VALUE_TYPE_UNSIGNED)
result->value.u32 = rc_transform_operand_value(result->value.u32, self);
}