Fix LI_* instructions

The 'lint' type in system 4 is NOT a 64-bit integer type. It is actually
a signed 32-bit integer type that is clamped between 0 and INT32_MAX.

Negative numbers can be assigned to a lint using SH_LOCALASSIGN (or at
least, the unofficial compiler emits such code) but LI_* instructions
always produce a positive result.

I'm guessing System40.exe converts lints to 64-bit integers for doing
arithmetic, and then clamps the result to the 32-bit integer range
before writing back to a variable.

This fixes a bug in Sengoku Rance where units would become invincible in
commander battles after their HP fell below zero.
This commit is contained in:
Nunuhara Cabbage
2020-01-08 19:14:08 -08:00
parent 6a4d11f3de
commit e00501346b
6 changed files with 68 additions and 65 deletions
-7
View File
@@ -29,7 +29,6 @@ struct string;
// Non-heap values. Stored in pages and on the stack.
union vm_value {
int32_t i;
int64_t li;
float f;
void *ref; // for casting HLL return value
};
@@ -59,11 +58,6 @@ static inline union vm_value vm_int(int32_t v)
return (union vm_value) { .i = v };
}
static inline union vm_value vm_long(int64_t v)
{
return (union vm_value) { .li = v };
}
static inline union vm_value vm_bool(bool b)
{
return (union vm_value) { .i = !!b };
@@ -77,7 +71,6 @@ static inline union vm_value vm_float(float v)
#define vm_value_cast(v) _Generic((v), \
union vm_value: _vm_id, \
int32_t: vm_int, \
int64_t: vm_long, \
bool: vm_bool, \
float: vm_float)(v)
+2 -15
View File
@@ -114,12 +114,6 @@ sexp sexp_vm_value_get_i (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
return sexp_make_integer(ctx, ((union vm_value*)sexp_cpointer_value(x))->i);
}
sexp sexp_vm_value_get_li (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
if (! (sexp_pointerp(x) && (sexp_pointer_tag(x) == sexp_unbox_fixnum(sexp_opcode_arg1_type(self)))))
return sexp_type_exception(ctx, self, sexp_unbox_fixnum(sexp_opcode_arg1_type(self)), x);
return sexp_make_integer(ctx, ((union vm_value*)sexp_cpointer_value(x))->li);
}
sexp sexp_vm_value_get_f (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
if (! (sexp_pointerp(x) && (sexp_pointer_tag(x) == sexp_unbox_fixnum(sexp_opcode_arg1_type(self)))))
return sexp_type_exception(ctx, self, sexp_unbox_fixnum(sexp_opcode_arg1_type(self)), x);
@@ -267,10 +261,9 @@ sexp sexp_init_library (sexp ctx, sexp self, sexp_sint_t n, sexp env, const char
sexp_env_define(ctx, env, tmp, sexp_vm_value_type_obj);
sexp_type_slots(sexp_vm_value_type_obj) = SEXP_NULL;
sexp_push(ctx, sexp_type_slots(sexp_vm_value_type_obj), sexp_intern(ctx, "f", -1));
sexp_push(ctx, sexp_type_slots(sexp_vm_value_type_obj), sexp_intern(ctx, "li", -1));
sexp_push(ctx, sexp_type_slots(sexp_vm_value_type_obj), sexp_intern(ctx, "i", -1));
sexp_type_getters(sexp_vm_value_type_obj) = sexp_make_vector(ctx, SEXP_THREE, SEXP_FALSE);
sexp_type_setters(sexp_vm_value_type_obj) = sexp_make_vector(ctx, SEXP_THREE, SEXP_FALSE);
sexp_type_getters(sexp_vm_value_type_obj) = sexp_make_vector(ctx, SEXP_TWO, SEXP_FALSE);
sexp_type_setters(sexp_vm_value_type_obj) = sexp_make_vector(ctx, SEXP_TWO, SEXP_FALSE);
tmp = sexp_make_type_predicate(ctx, name, sexp_vm_value_type_obj);
name = sexp_intern(ctx, "vm-value?", 9);
sexp_env_define(ctx, env, name, tmp);
@@ -331,12 +324,6 @@ sexp sexp_init_library (sexp ctx, sexp self, sexp_sint_t n, sexp env, const char
sexp_opcode_return_type(op) = sexp_make_fixnum(SEXP_FLONUM);
sexp_opcode_arg1_type(op) = sexp_make_fixnum(sexp_type_tag(sexp_vm_value_type_obj));
}
if (sexp_vectorp(sexp_type_getters(sexp_vm_value_type_obj))) sexp_vector_set(sexp_type_getters(sexp_vm_value_type_obj), SEXP_TWO, op);
op = sexp_define_foreign(ctx, env, "vm-value-long", 1, sexp_vm_value_get_li);
if (sexp_opcodep(op)) {
sexp_opcode_return_type(op) = sexp_make_fixnum(SEXP_FIXNUM);
sexp_opcode_arg1_type(op) = sexp_make_fixnum(sexp_type_tag(sexp_vm_value_type_obj));
}
if (sexp_vectorp(sexp_type_getters(sexp_vm_value_type_obj))) sexp_vector_set(sexp_type_getters(sexp_vm_value_type_obj), SEXP_ONE, op);
op = sexp_define_foreign(ctx, env, "vm-value-int", 1, sexp_vm_value_get_i);
if (sexp_opcodep(op)) {
-1
View File
@@ -69,7 +69,6 @@
(define-c-union vm_value
predicate: vm-value?
(int i vm-value-int)
(long li vm-value-long)
(float f vm-value-float))
(define-c (free string) (vm-value-string vm_value_string) (vm_value))
+3 -4
View File
@@ -50,8 +50,7 @@ static enum page_type string_to_page_type(const char *str)
static cJSON *value_to_json(union vm_value v)
{
// XXX: type punning through int64_t
return cJSON_CreateNumber(v.li);
return cJSON_CreateNumber(v.i);
}
static cJSON *resume_page_to_json(struct page *page)
@@ -204,7 +203,7 @@ static void load_page(int slot, cJSON *json)
int i = 0;
cJSON *item;
cJSON_ArrayForEach(item, values) {
page->values[i].li = item->valuedouble; // XXX: using valueint would truncate longs
page->values[i].i = item->valueint;
i++;
}
@@ -309,7 +308,7 @@ static void load_stack(cJSON *json)
cJSON *item;
cJSON_ArrayForEach(item, json) {
type_check(cJSON_Number, item);
stack_push_value(vm_long(item->valuedouble));
stack_push_value(vm_int(item->valueint));
}
}
+2 -8
View File
@@ -56,11 +56,10 @@ cJSON *vm_value_to_json(enum ain_data_type type, union vm_value val)
case AIN_BOOL:
case AIN_FUNC_TYPE:
case AIN_DELEGATE:
case AIN_LONG_INT:
return cJSON_CreateNumber(val.i);
case AIN_FLOAT:
return cJSON_CreateNumber(val.f);
case AIN_LONG_INT:
return cJSON_CreateNumber(val.li);
case AIN_STRING:
return cJSON_CreateString(heap[val.i].s->text);
case AIN_STRUCT:
@@ -206,6 +205,7 @@ union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type stru
switch (type) {
case AIN_INT:
case AIN_BOOL:
case AIN_LONG_INT:
if (!cJSON_IsNumber(json)) {
invalid_save_data("Not a number", json);
return vm_int(0);
@@ -217,12 +217,6 @@ union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type stru
return vm_float(0);
}
return vm_float(json->valuedouble);
case AIN_LONG_INT:
if (!cJSON_IsNumber(json)) {
invalid_save_data("Not a number", json);
return vm_long(0);
}
return vm_long(json->valueint);
case AIN_STRING:
slot = heap_alloc_slot(VM_STRING);
if (!cJSON_IsString(json)) {
+61 -30
View File
@@ -35,6 +35,15 @@
#include "vm/heap.h"
#include "vm/page.h"
static inline int32_t lint_clamp(int64_t n)
{
if (n < 0)
return 0;
if (n > INT32_MAX)
return INT32_MAX;
return (int32_t)n;
}
#define INITIAL_STACK_SIZE 4096
// When the IP is set to VM_RETURN, the VM halts
@@ -935,95 +944,117 @@ static void execute_instruction(enum opcode opcode)
// --- 64-bit integers ---
//
case ITOLI: {
stack_set(0, (int64_t)stack_peek(0).i);
stack_set(0, lint_clamp(stack_peek(0).i));
break;
}
case LI_ADD: {
stack[stack_ptr-2].li += stack[stack_ptr-1].li;
int64_t a = stack[stack_ptr-2].i;
int64_t b = stack[stack_ptr-1].i;
stack[stack_ptr-2].i = lint_clamp(a + b);
stack_ptr--;
break;
}
case LI_SUB: {
stack[stack_ptr-2].li -= stack[stack_ptr-1].li;
int64_t a = stack[stack_ptr-2].i;
int64_t b = stack[stack_ptr-1].i;
stack[stack_ptr-2].i = lint_clamp(a - b);
stack_ptr--;
break;
}
case LI_MUL: {
stack[stack_ptr-2].li *= stack[stack_ptr-1].li;
int64_t a = stack[stack_ptr-2].i;
int64_t b = stack[stack_ptr-1].i;
stack[stack_ptr-2].i = lint_clamp(a * b);
stack_ptr--;
break;
}
case LI_DIV: {
stack[stack_ptr-2].li /= stack[stack_ptr-1].li;
int64_t a = stack[stack_ptr-2].i;
int64_t b = stack[stack_ptr-1].i;
stack[stack_ptr-2].i = lint_clamp(a / b);
stack_ptr--;
break;
}
case LI_MOD: {
stack[stack_ptr-2].li %= stack[stack_ptr-1].li;
int64_t a = stack[stack_ptr-2].i;
int64_t b = stack[stack_ptr-1].i;
stack[stack_ptr-2].i = lint_clamp(a % b);
stack_ptr--;
break;
}
case LI_ASSIGN: {
int64_t v = stack_pop().li;
stack_push(stack_pop_var()->li = v);
int64_t v = stack_pop().i;
stack_push(stack_pop_var()->i = lint_clamp(v));
break;
}
case LI_PLUSA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li += n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i + n));
break;
}
case LI_MINUSA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li -= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i - n));
break;
}
case LI_MULA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li *= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i * n));
break;
}
case LI_DIVA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li /= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i / n));
break;
}
case LI_MODA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li %= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i % n));
break;
}
case LI_ANDA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li &= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i & n));
break;
}
case LI_ORA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li |= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i | n));
break;
}
case LI_XORA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li ^= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i ^ n));
break;
}
case LI_LSHIFTA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li <<= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i << n));
break;
}
case LI_RSHIFTA: {
int64_t n = stack_pop().li;
stack_push(stack_pop_var()->li >>= n);
int64_t n = stack_pop().i;
union vm_value *v = stack_pop_var();
stack_push(v->i = lint_clamp((int64_t)v->i >> n));
break;
}
case LI_INC: {
stack_pop_var()->li++;
union vm_value *v = stack_pop_var();
v->i = lint_clamp((int64_t)v->i + (int64_t)1);
break;
}
case LI_DEC: {
stack_pop_var()->li--;
union vm_value *v = stack_pop_var();
v->i = lint_clamp((int64_t)v->i - (int64_t)1);
break;
}
//