From fb340115d79c4821b74d2aaefdfbf241dcbc5994 Mon Sep 17 00:00:00 2001 From: MrPowerGamerBR Date: Thu, 6 Aug 2026 11:58:02 -0300 Subject: [PATCH 1/3] Fix normal dup path --- src/vm.c | 42 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/src/vm.c b/src/vm.c index bfb841611..6c79f7aaf 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1894,39 +1894,15 @@ static void handleDup(VMContext* ctx, uint32_t instr) { } #endif - // Normal dup mode: total bytes to duplicate = (operand + 1) * sizeof(type1) - // WAD17+ uses the low 15 bits of operand. - // WAD16 and below uses the low 15 bits of operand. - // Bit 15 is the swap-mode flag, handled above. - int32_t operandCount; -#if IS_WAD17_OR_HIGHER_ENABLED - operandCount = IS_WAD17_OR_HIGHER(ctx) ? (int32_t)(operand & 0x7FFF) : (int32_t)(operand & 0xFF); -#else - operandCount = (int32_t)(operand & 0xFF); -#endif - int32_t totalBytes = (operandCount + 1) * typeSize; - int32_t count = bytesToSlotCount(ctx, totalBytes, ctx->stack.top); - - // Copy 'count' items from the top of the stack (preserving order) - int32_t startIdx = ctx->stack.top - count; - for (int32_t i = 0; count > i; i++) { - RValue copy = ctx->stack.slots[startIdx + i]; - - // If the value owns a string, duplicate it to avoid double-free. - // For arrays and methods, bump the refcount so each duplicate independently owns a reference. - if (copy.type == RVALUE_STRING && copy.ownsReference && copy.string != nullptr) { - copy.string = safeStrdup(copy.string); - } else if (copy.type == RVALUE_ARRAY && copy.ownsReference && copy.array != nullptr) { - GMLArray_incRef(copy.array); -#if IS_WAD17_OR_HIGHER_ENABLED - } else if (copy.type == RVALUE_METHOD && copy.ownsReference && copy.method != nullptr) { - GMLMethod_incRef(copy.method); -#endif - } else if (copy.type == RVALUE_STRUCT && copy.ownsReference && copy.structInst != nullptr) { - Instance_structIncRef(copy.structInst); - } - - stackPush(ctx, copy); + // In the YoYo Runner, the type1 would've been used to figure out how many bytes to be copied from the stack + // Because all of our types are tagged RValues, we don't need to rely on it (yay) + // extra = how many additional elements will be copied from the stack, that is... + // If the stack is [a, b], and extra is 1, the result will be [a, b, a, b] + int32_t total = operand + 1; + int32_t dupBottom = ctx->stack.top - total; + repeat(total, i) { + RValue target = ctx->stack.slots[dupBottom + i]; + stackPush(ctx, RValue_makeIndependent(target)); } } From d257f039baab60f7fe590a9c96e7ab30a62e1654 Mon Sep 17 00:00:00 2001 From: Fancy2209 <64917206+Fancy2209@users.noreply.github.com> Date: Sat, 8 Aug 2026 00:05:00 +0000 Subject: [PATCH 2/3] Fix PS2 --- src/vm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vm.c b/src/vm.c index 6c79f7aaf..b0a1409f4 100644 --- a/src/vm.c +++ b/src/vm.c @@ -41,6 +41,7 @@ static char* formatStackContents(VMContext* ctx) { } #endif +#if IS_WAD17_OR_HIGHER_ENABLED // Returns the native byte size of a GML data type on the runner's stack. // The Dup instruction (and several BC17+ BREAK sub-opcodes) encode byte counts, not slot counts. static int gmlTypeNativeSize(uint8_t gmlType) { @@ -56,6 +57,7 @@ static int gmlTypeNativeSize(uint8_t gmlType) { default: return 16; } } +#endif static void stackPush(VMContext* ctx, RValue val) { require(VM_STACK_SIZE > ctx->stack.top); @@ -1832,6 +1834,7 @@ static void handleCmp(VMContext* ctx, uint32_t instr) { stackPush(ctx,RValue_makeBool(result)); } +#if IS_WAD17_OR_HIGHER_ENABLED // Converts a native byte count to RValue slot count by walking the stack backwards from a given position. // Reads each slot's gmlStackType (set at push time) to compute its native footprint. static int32_t bytesToSlotCount(VMContext* ctx, int32_t nativeBytes, int32_t stackPos) { @@ -1846,13 +1849,15 @@ static int32_t bytesToSlotCount(VMContext* ctx, int32_t nativeBytes, int32_t sta require(remaining == 0); // Byte count must align exactly to slot boundaries return slots; } +#endif static void handleDup(VMContext* ctx, uint32_t instr) { uint16_t operand = (uint16_t)(instr & 0xFFFF); uint8_t type1 = instrType1(instr); - int32_t typeSize = gmlTypeNativeSize(type1); #if IS_WAD17_OR_HIGHER_ENABLED + int32_t typeSize = gmlTypeNativeSize(type1); + // Swap mode (WAD17+): bit 15 of operand is set. // The Dup instruction doubles as a stack rotation when bit 15 is set. // It takes the top N items and moves them below the next M items. From f2e6ef55e1dd451cd0ebcb2c4a483d1ff7254a57 Mon Sep 17 00:00:00 2001 From: Fancy2209 <64917206+Fancy2209@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:35:42 +0000 Subject: [PATCH 3/3] Fix PS2 2 --- src/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm.c b/src/vm.c index b0a1409f4..bfeab7d5f 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1853,9 +1853,9 @@ static int32_t bytesToSlotCount(VMContext* ctx, int32_t nativeBytes, int32_t sta static void handleDup(VMContext* ctx, uint32_t instr) { uint16_t operand = (uint16_t)(instr & 0xFFFF); - uint8_t type1 = instrType1(instr); #if IS_WAD17_OR_HIGHER_ENABLED + uint8_t type1 = instrType1(instr); int32_t typeSize = gmlTypeNativeSize(type1); // Swap mode (WAD17+): bit 15 of operand is set.