Skip to content

Commit

Permalink
stack ops
Browse files Browse the repository at this point in the history
Signed-off-by: andreypfau <[email protected]>
  • Loading branch information
andreypfau committed Feb 19, 2025
1 parent 846a9ed commit 3b10a12
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 0 deletions.
59 changes: 59 additions & 0 deletions vm/src/OpCodes.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ton.kotlin.tvm

public object OpCodes {
// A.2.1. Basic stack manipulation primitives.
public const val NOP: Int = 0x00
public const val SWAP: Int = 0x01
public const val XCHG_0_2: Int = 0x02
Expand Down Expand Up @@ -66,6 +67,7 @@ public object OpCodes {
public const val POP_14: Int = 0x3E
public const val POP_15: Int = 0x3F

// A.2.2. Compound stack manipulation primitives.
public const val XCHG3_0: Int = 0x40
public const val XCHG3_1: Int = 0x41
public const val XCHG3_2: Int = 0x42
Expand All @@ -89,6 +91,7 @@ public object OpCodes {

public const val XCHG_PUSH_SUBSET: Int = 0x54

// A.2.3. Exotic stack manipulation primitives.
public const val BLKSWAP: Int = 0x55
public const val PUSH_L: Int = 0x56
public const val POP_L: Int = 0x57
Expand All @@ -113,4 +116,60 @@ public object OpCodes {
public const val ONLYTOPX: Int = 0x6A
public const val ONLYX: Int = 0x6B
public const val BLKDROP2: Int = 0x6C

// A.3.1. Null primitives.
public const val NULL: Int = 0x6D
public const val ISNULL: Int = 0x6E

// A.3.2. Tuple primitives.
public const val TUPLE: Int = 0x6F
public const val NIL: Int = 0x00
public const val SINGLE: Int = 0x01
public const val PAIR: Int = 0x02
public const val TRIPLE: Int = 0x03
public const val TUPLE_4: Int = 0x04
public const val TUPLE_5: Int = 0x05
public const val TUPLE_6: Int = 0x06
public const val TUPLE_7: Int = 0x07
public const val TUPLE_8: Int = 0x08
public const val TUPLE_9: Int = 0x09
public const val TUPLE_10: Int = 0x0A
public const val TUPLE_11: Int = 0x0B
public const val TUPLE_12: Int = 0x0C
public const val TUPLE_13: Int = 0x0D
public const val TUPLE_14: Int = 0x0E
public const val TUPLE_15: Int = 0x0F
public const val FIRST: Int = 0x10
public const val SECOND: Int = 0x11
public const val THIRD: Int = 0x12
public const val INDEX_3: Int = 0x13
public const val INDEX_4: Int = 0x14
public const val INDEX_5: Int = 0x15
public const val INDEX_6: Int = 0x16
public const val INDEX_7: Int = 0x17
public const val INDEX_8: Int = 0x18
public const val INDEX_9: Int = 0x19
public const val INDEX_10: Int = 0x1A
public const val INDEX_11: Int = 0x1B
public const val INDEX_12: Int = 0x1C
public const val INDEX_13: Int = 0x1D
public const val INDEX_14: Int = 0x1E
public const val INDEX_15: Int = 0x1F
public const val UNTUPLE_0: Int = 0x20
public const val UNSINGLE: Int = 0x21
public const val UNPAIR: Int = 0x22
public const val UNTRIPLE: Int = 0x23
public const val UNTUPLE_4: Int = 0x24
public const val UNTUPLE_5: Int = 0x25
public const val UNTUPLE_6: Int = 0x26
public const val UNTUPLE_7: Int = 0x27
public const val UNTUPLE_8: Int = 0x28
public const val UNTUPLE_9: Int = 0x29
public const val UNTUPLE_10: Int = 0x2A
public const val UNTUPLE_11: Int = 0x2B
public const val UNTUPLE_12: Int = 0x2C
public const val UNTUPLE_13: Int = 0x2D
public const val UNTUPLE_14: Int = 0x2E
public const val UNTUPLE_15: Int = 0x2F

}
31 changes: 31 additions & 0 deletions vm/src/Stack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ public class Stack {
top -= count
}

public fun onlyTop(count: Int) {
val elements = elements
val top = top
val toIndex = top + 1
elements.copyInto(elements, 0, top - count + 1, toIndex)
elements.fill(null, count, toIndex)
this.top = count - 1
}

public fun rot() {
val elements = elements
val a = elements[top - 2]
Expand Down Expand Up @@ -140,6 +149,13 @@ public class Stack {
lastElements.copyInto(elements, top - i - j + 1)
}

public fun blockDrop(i: Int, j: Int) {
val elements = elements
elements.copyInto(elements, top - j - i + 1, top - j + 1, top + 1)
elements.fill(null, top - i + 1, top + 1)
top = top - i
}

public fun copy(srcSlot: Int, dstSlot: Int) {
val elements = elements
val value = elements[srcSlot]
Expand All @@ -159,6 +175,21 @@ public class Stack {
}
append("]")
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as Stack
if (top != other.top) return false
if (!elements.contentEquals(other.elements)) return false
return true
}

override fun hashCode(): Int {
var result = top
result = 31 * result + elements.contentHashCode()
return result
}
}

/*
Expand Down
34 changes: 34 additions & 0 deletions vm/src/Tvm.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.ton.kotlin.tvm

import org.ton.bigint.toBigInt
import org.ton.cell.Cell
import org.ton.cell.CellSlice
import org.ton.kotlin.tvm.OpCodes.BLKDROP2
import org.ton.kotlin.tvm.OpCodes.BLKSWAP
import org.ton.kotlin.tvm.OpCodes.BLKSWX
import org.ton.kotlin.tvm.OpCodes.BLK_SUBSET
Expand All @@ -14,6 +16,8 @@ import org.ton.kotlin.tvm.OpCodes.DUP
import org.ton.kotlin.tvm.OpCodes.DUP2
import org.ton.kotlin.tvm.OpCodes.NIP
import org.ton.kotlin.tvm.OpCodes.NOP
import org.ton.kotlin.tvm.OpCodes.ONLYTOPX
import org.ton.kotlin.tvm.OpCodes.ONLYX
import org.ton.kotlin.tvm.OpCodes.OVER
import org.ton.kotlin.tvm.OpCodes.OVER2
import org.ton.kotlin.tvm.OpCodes.PICK
Expand Down Expand Up @@ -109,11 +113,16 @@ import org.ton.kotlin.tvm.OpCodes.XCHG_1_9
import org.ton.kotlin.tvm.OpCodes.XCHG_PUSH_SUBSET
import org.ton.kotlin.tvm.OpCodes.XCPU
import org.ton.kotlin.tvm.exception.StackUnderflowException
import org.ton.kotlin.tvm.exception.UnknownOpcodeException
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

public class Tvm {

public fun execute(stack: Stack, code: Cell) {
executeCp0(stack, code.beginParse())
}

public fun execute(stack: Stack, code: CellSlice) {
executeCp0(stack, code)
}
Expand Down Expand Up @@ -515,6 +524,31 @@ public class Tvm {
}
}

// ( ... a(i)...a(1) i - a(i)...a(1))
ONLYTOPX -> {
val i = stack.popInt().toInt()
logOpcode { "execute ONLYTOPX $i" }
stack.onlyTop(i)
}

// (a(depth)...a(depth-i+1) ... i - a(depth)...a(depth-i+1))
ONLYX -> {
val i = stack.popInt().toInt()
logOpcode { "execute ONLYX $i" }
stack.dropTop(stack.depth - i)
}

BLKDROP2 -> {
val arg = code.loadUInt(8).toInt()
val i = arg ushr 4
if (i == 0) {
throw UnknownOpcodeException((opcode shl 8) or arg)
}
val j = arg and 0xF
logOpcode { "execute BLKDROP2 $i,$j" }
stack.blockDrop(i, j)
}

//
// OpCodes.PUSHINT_LONG -> {
// val l = code.loadUInt(5).toInt()
Expand Down
4 changes: 4 additions & 0 deletions vm/src/exception/UnknownOpcodeException.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.ton.kotlin.tvm.exception

public class UnknownOpcodeException(public val opcode: Int) :
IllegalStateException("Unknown opcode: ${opcode.toHexString()}")
30 changes: 30 additions & 0 deletions vm/test/BlockSwapTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.ton.kotlin.tvm
import org.ton.bitstring.BitString
import org.ton.cell.buildCell
import kotlin.test.Test
import kotlin.test.assertEquals

class BlockSwapTest {
@Test
Expand Down Expand Up @@ -56,4 +57,33 @@ class BlockSwapTest {
}.beginParse())
println(stack)
}

@Test
fun testOnlytopx() {
val stack = stackOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
stack.pushInt(4)
Tvm().execute(stack, buildCell {
storeBitString(BitString("6A"))
})
assertEquals(stackOf(6, 7, 8, 9), stack)
}

@Test
fun testOnlyX() {
val stack = stackOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
stack.pushInt(4)
Tvm().execute(stack, buildCell {
storeBitString(BitString("6B"))
})
assertEquals(stackOf(0, 1, 2, 3), stack)
}

@Test
fun testBlockDrop() {
val stack = stackOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
Tvm().execute(stack, buildCell {
storeBitString(BitString("6C52"))
})
assertEquals(stackOf(0, 1, 2, 8, 9), stack)
}
}

0 comments on commit 3b10a12

Please sign in to comment.