From 825f14dc90a1f9adb47c647bbdee4b312aa5309a Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Tue, 1 Oct 2019 22:47:01 +0200 Subject: [PATCH] Fixed a disassembler bug: va list arg_push* instructions would not be output when in automatic stack management mode. --- .../scripting/disassembly.test.ts | 37 ++++++++++++++++++- src/quest_editor/scripting/disassembly.ts | 17 ++++++++- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/quest_editor/scripting/disassembly.test.ts b/src/quest_editor/scripting/disassembly.test.ts index b3ffc473..1ca42de4 100644 --- a/src/quest_editor/scripting/disassembly.test.ts +++ b/src/quest_editor/scripting/disassembly.test.ts @@ -6,7 +6,7 @@ import { BufferCursor } from "../../core/data_formats/cursor/BufferCursor"; import { parse_bin, write_bin } from "../../core/data_formats/parsing/quest/bin"; import { assemble } from "./assembly"; import { disassemble } from "./disassembly"; -import { Instruction, object_code_equal, SegmentType } from "./instructions"; +import { Instruction, object_code_equal, Segment, SegmentType } from "./instructions"; import { Opcode } from "./opcodes"; test("vararg instructions should be disassembled correctly", () => { @@ -36,6 +36,40 @@ test("vararg instructions should be disassembled correctly", () => { ); }); +// arg_push* instructions should always be output when in a va list whether manual stack management +// is on or off. +test("va list instructions should be disassembled correctly", () => { + const ir: Segment[] = [ + { + type: SegmentType.Instructions, + labels: [0], + instructions: [ + new Instruction(Opcode.VA_START, []), + new Instruction(Opcode.ARG_PUSHW, [{ value: 1337, size: 2 }]), + new Instruction(Opcode.VA_CALL, [{ value: 100, size: 2 }]), + new Instruction(Opcode.VA_END, []), + new Instruction(Opcode.RET, []), + ], + }, + ]; + + for (const manual_stack of [true, false]) { + const asm = disassemble(ir, manual_stack); + + expect(asm).toEqual( + `.code + +0: + va_start + arg_pushw 1337 + va_call 100 + va_end + ret +`.split("\n"), + ); + } +}); + // Round-trip test. test("assembling disassembled object code with manual stack management should result in the same IR", () => { const orig_buffer = readFileSync("test/resources/quest27_e.bin"); @@ -62,7 +96,6 @@ test("assembling disassembled object code with automatic stack management should expect(warnings).toEqual([]); expect(object_code_equal(object_code, bin.object_code)).toBe(true); - // expect(object_code).toBe(bin.object_code); }); // Round-trip test. diff --git a/src/quest_editor/scripting/disassembly.ts b/src/quest_editor/scripting/disassembly.ts index a5375b8f..2a023e2d 100644 --- a/src/quest_editor/scripting/disassembly.ts +++ b/src/quest_editor/scripting/disassembly.ts @@ -1,6 +1,6 @@ import { reinterpret_i32_as_f32 } from "../../core/primitive_conversion"; import { Arg, Segment, SegmentType } from "./instructions"; -import { AnyType, Kind, Param, StackInteraction } from "./opcodes"; +import { AnyType, Kind, Opcode, Param, StackInteraction } from "./opcodes"; import Logger from "js-logger"; const logger = Logger.get("quest_editor/scripting/disassembly"); @@ -76,8 +76,21 @@ export function disassemble(object_code: Segment[], manual_stack = false): strin } else if (segment.type === SegmentType.String) { lines.push(" " + JSON.stringify(segment.value)); } else { + // SegmentType.Instructions + let in_va_list = false; + for (const instruction of segment.instructions) { - if (!manual_stack && instruction.opcode.stack === StackInteraction.Push) { + if (instruction.opcode.code === Opcode.VA_START.code) { + in_va_list = true; + } else if (instruction.opcode.code === Opcode.VA_END.code) { + in_va_list = false; + } + + if ( + !manual_stack && + !in_va_list && + instruction.opcode.stack === StackInteraction.Push + ) { stack.push(...add_type_to_args(instruction.opcode.params, instruction.args)); } else { let args: string[] = [];