mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-10 08:18:14 +03:00
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:
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user