From 19a4b7cbc47f925641c7419bccf172376359212a Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:56:57 +0400 Subject: [PATCH] feat: support TVM 12 instructions --- package.json | 2 +- src/generator/instructions.ts | 3 +++ src/runtime/constructors.ts | 16 ++++++++++++++++ src/runtime/instr-gen.ts | 14 ++++++++++++++ src/runtime/test/tests-decompile.spec.ts | 4 ++++ src/runtime/types.ts | 18 ++++++++++++++++++ src/text/convert.ts | 12 ++++++++++++ src/text/printer-gen.ts | 4 ++++ .../test/__snapshots__/parser.spec.ts.snap | 6 ++++++ src/text/test/parser.spec.ts | 13 +++++++++++++ 10 files changed, 91 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b76d5e..a64fbf9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ton-assembly", - "version": "0.4.0", + "version": "0.5.0", "description": "TON assembler and disassembler", "author": "TON Studio & TON Core", "repository": { diff --git a/src/generator/instructions.ts b/src/generator/instructions.ts index 1c16c06..90fee5a 100644 --- a/src/generator/instructions.ts +++ b/src/generator/instructions.ts @@ -1346,6 +1346,9 @@ export const instructions: Record = { INMSG_STATEINIT: version(11, cat("config", mksimple(0xf899, 16, `exec_get_in_msg_param`))), INMSGPARAM: version(11, cat("config", mkfixedrangen(0xf89a, 0xf8a0, 16, 4, seq(uint4), `exec_get_var_in_msg_param`))), + BTOS: version(12, cat("cell_deserialize", mksimple(0xcf50, 16, "exec_builder_to_slice"))), + HASHBU: version(12, cat("crypto_common", mksimple(0xf916, 16, "exec_compute_hash"))), + DEBUGMARK: cat("int_const", mkfixedn(0xF955, 16, 16, seq(uint(16, range(0n, 0n))), `exec_push_pow2dec`)), fPUSHSLICE: cat("int_const", mkfixedn(0, 0, 0, seq(slice(uint4, uint4, 0)), "")), diff --git a/src/runtime/constructors.ts b/src/runtime/constructors.ts index 3d486a8..ba03193 100644 --- a/src/runtime/constructors.ts +++ b/src/runtime/constructors.ts @@ -7801,6 +7801,22 @@ export type INMSGPARAM = { arg0: number loc: $.Loc | undefined } +export const BTOS = (loc?: $.Loc): BTOS => ({ + $: "BTOS", + loc, +}) +export type BTOS = { + $: "BTOS" + loc: $.Loc | undefined +} +export const HASHBU = (loc?: $.Loc): HASHBU => ({ + $: "HASHBU", + loc, +}) +export type HASHBU = { + $: "HASHBU" + loc: $.Loc | undefined +} export const DEBUGMARK = (arg0: number, loc?: $.Loc): DEBUGMARK => ({ $: "DEBUGMARK", arg0, diff --git a/src/runtime/instr-gen.ts b/src/runtime/instr-gen.ts index 647a3d6..56d843a 100644 --- a/src/runtime/instr-gen.ts +++ b/src/runtime/instr-gen.ts @@ -916,6 +916,8 @@ export type Instr = | c.INMSG_VALUEEXTRA | c.INMSG_STATEINIT | c.INMSGPARAM + | c.BTOS + | c.HASHBU | c.DEBUGMARK | cf.fPUSHINT | cf.fPUSHSLICE @@ -5474,6 +5476,16 @@ export const rangeToType = [ max: 16293888, load: types.INMSGPARAM.load, }, + { + min: 13586432, + max: 13586688, + load: types.BTOS.load, + }, + { + min: 16324096, + max: 16324352, + load: types.HASHBU.load, + }, { min: 16340224, max: 16340480, @@ -6389,6 +6401,8 @@ storeMapping.set("INMSG_VALUE", types.INMSG_VALUE.store) storeMapping.set("INMSG_VALUEEXTRA", types.INMSG_VALUEEXTRA.store) storeMapping.set("INMSG_STATEINIT", types.INMSG_STATEINIT.store) storeMapping.set("INMSGPARAM", types.INMSGPARAM.store) +storeMapping.set("BTOS", types.BTOS.store) +storeMapping.set("HASHBU", types.HASHBU.store) storeMapping.set("DEBUGMARK", types.DEBUGMARK.store) storeMapping.set("fPUSHINT", ftypes.fPUSHINT.store) storeMapping.set("fPUSHSLICE", ftypes.fPUSHSLICE.store) diff --git a/src/runtime/test/tests-decompile.spec.ts b/src/runtime/test/tests-decompile.spec.ts index a112130..442b2eb 100644 --- a/src/runtime/test/tests-decompile.spec.ts +++ b/src/runtime/test/tests-decompile.spec.ts @@ -20,6 +20,8 @@ import { ENDC, STREF2CONST, SUB, + BTOS, + HASHBU, } from "../index" import {call, execute} from "../../helpers" import {bin, code, dictMap, hex} from "../util" @@ -349,4 +351,6 @@ ENDC `DEBUGSTR slice should be byte aligned, but 9-bit slice given`, ), ) + it("with TVM 12 BTOS", test([BTOS()], `BTOS\n`)) + it("with TVM 12 HASHBU", test([HASHBU()], `HASHBU\n`)) }) diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 87960a9..931d8c9 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -8436,6 +8436,24 @@ export const INMSGPARAM: $.Type = { $.uint(4).store(b, val.arg0, options) }, } +export const BTOS: $.Type = { + load: s => { + s.skip(16) + return c.BTOS() + }, + store: (b, val, options) => { + b.storeInstructionPrefix(53072, 16, val) + }, +} +export const HASHBU: $.Type = { + load: s => { + s.skip(16) + return c.HASHBU() + }, + store: (b, val, options) => { + b.storeInstructionPrefix(63766, 16, val) + }, +} export const DEBUGMARK: $.Type = { load: s => { s.skip(16) diff --git a/src/text/convert.ts b/src/text/convert.ts index 0ed1e77..eb86b81 100644 --- a/src/text/convert.ts +++ b/src/text/convert.ts @@ -3946,6 +3946,14 @@ export const INMSGPARAM: $.Convert = (ctx, instr, loc) => { const args = $.singleIntegerArg(instr) return c.INMSGPARAM(args, loc) } +export const BTOS: $.Convert = (ctx, instr, loc) => { + u.assertZeroArgs(instr, loc) + return c.BTOS(loc) +} +export const HASHBU: $.Convert = (ctx, instr, loc) => { + u.assertZeroArgs(instr, loc) + return c.HASHBU(loc) +} export const DEBUGMARK: $.Convert = (ctx, instr, loc) => { u.assertSingleArgs(instr, loc) const args = $.singleIntegerArg(instr) @@ -5883,6 +5891,10 @@ export const convertInstruction = (ctx: $.Ctx, instr: $ast.Instruction, loc: c.u return INMSG_STATEINIT(ctx, instr, loc) case "INMSGPARAM": return INMSGPARAM(ctx, instr, loc) + case "BTOS": + return BTOS(ctx, instr, loc) + case "HASHBU": + return HASHBU(ctx, instr, loc) case "DEBUGMARK": return DEBUGMARK(ctx, instr, loc) case "fPUSHINT": diff --git a/src/text/printer-gen.ts b/src/text/printer-gen.ts index ab6c20c..27cda4a 100644 --- a/src/text/printer-gen.ts +++ b/src/text/printer-gen.ts @@ -2389,6 +2389,10 @@ export const printInstruction = (p: $.Printer, instr: c.Instr) => { p.append(" ") p.append(instr.arg0.toString()) return + case "BTOS": + return + case "HASHBU": + return case "DEBUGMARK": p.append(" ") p.append(instr.arg0.toString()) diff --git a/src/text/test/__snapshots__/parser.spec.ts.snap b/src/text/test/__snapshots__/parser.spec.ts.snap index fe08ece..0136419 100644 --- a/src/text/test/__snapshots__/parser.spec.ts.snap +++ b/src/text/test/__snapshots__/parser.spec.ts.snap @@ -6,6 +6,12 @@ exports[`assembly-parser should give an error for too big number 1`] = `"ParseE exports[`assembly-parser should not parse assembly with error 1`] = `"ParseError: Expected "-", "0", "[", "\\"", "boc{", "b{", "c", "embed", "exotic", "ref", "s", "x{", "{", digit, or identifier at test.asm:1"`; +exports[`assembly-parser should parse TVM 12 instructions 1`] = ` +"BTOS +HASHBU +" +`; + exports[`assembly-parser should parse assembly with invalid raw pushref 1`] = ` "PUSHREF { embed x{22221} diff --git a/src/text/test/parser.spec.ts b/src/text/test/parser.spec.ts index efc4972..cec5165 100644 --- a/src/text/test/parser.spec.ts +++ b/src/text/test/parser.spec.ts @@ -273,6 +273,19 @@ describe("assembly-parser", () => { expect(print(res.instructions)).toMatchSnapshot() }) + it("should parse TVM 12 instructions", () => { + const code = ` + BTOS + HASHBU + ` + const res = parse("test.asm", code) + if (res.$ === "ParseFailure") { + throw new Error("unexpected parser error") + } + + expect(print(res.instructions)).toMatchSnapshot() + }) + it("should parse fift instruction", () => { const code = ` fPUSHINT 10