2023-03-31 11:52:04 +02:00
|
|
|
package vm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
2023-03-31 19:04:11 +02:00
|
|
|
"fmt"
|
2023-03-31 11:52:04 +02:00
|
|
|
)
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// NewLine creates a new instruction line for the VM.
|
|
|
|
func NewLine(instructionList []byte, instruction uint16, strargs []string, byteargs []byte, numargs []uint8) []byte {
|
|
|
|
if instructionList == nil {
|
|
|
|
instructionList = []byte{}
|
|
|
|
}
|
|
|
|
b := []byte{0x00, 0x00}
|
|
|
|
binary.BigEndian.PutUint16(b, instruction)
|
|
|
|
for _, arg := range strargs {
|
|
|
|
b = append(b, uint8(len(arg)))
|
|
|
|
b = append(b, []byte(arg)...)
|
|
|
|
}
|
|
|
|
if byteargs != nil {
|
|
|
|
b = append(b, uint8(len(byteargs)))
|
|
|
|
b = append(b, byteargs...)
|
|
|
|
}
|
|
|
|
if numargs != nil {
|
|
|
|
b = append(b, numargs...)
|
|
|
|
}
|
|
|
|
return append(instructionList, b...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseOp verifies and extracts the expected opcode portion of an instruction
|
2023-04-02 16:00:56 +02:00
|
|
|
func ParseOp(b []byte) (Opcode, []byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
op, b, err := opSplit(b)
|
|
|
|
if err != nil {
|
|
|
|
return NOOP, b, err
|
|
|
|
}
|
|
|
|
return op, b, nil
|
|
|
|
}
|
2023-04-02 13:12:06 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseLoad parses and extracts the expected argument portion of a LOAD instruction
|
2023-04-02 13:12:06 +02:00
|
|
|
func ParseLoad(b []byte) (string, uint32, []byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
return parseSymLen(b)
|
2023-03-31 11:52:04 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseReload parses and extracts the expected argument portion of a RELOAD instruction
|
2023-04-02 13:12:06 +02:00
|
|
|
func ParseReload(b []byte) (string, []byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
return parseSym(b)
|
2023-03-31 11:59:55 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseMap parses and extracts the expected argument portion of a MAP instruction
|
2023-04-02 13:12:06 +02:00
|
|
|
func ParseMap(b []byte) (string, []byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
return parseSym(b)
|
2023-04-02 13:12:06 +02:00
|
|
|
}
|
2023-04-01 23:19:12 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseMove parses and extracts the expected argument portion of a MOVE instruction
|
2023-04-02 13:12:06 +02:00
|
|
|
func ParseMove(b []byte) (string, []byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
return parseSym(b)
|
2023-03-31 11:59:55 +02:00
|
|
|
}
|
2023-03-31 11:52:04 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseHalt parses and extracts the expected argument portion of a HALT instruction
|
2023-04-02 13:12:06 +02:00
|
|
|
func ParseHalt(b []byte) ([]byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
return parseNoArg(b)
|
2023-04-02 13:12:06 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseCatch parses and extracts the expected argument portion of a CATCH instruction
|
2023-04-02 14:59:40 +02:00
|
|
|
func ParseCatch(b []byte) (string, uint32, bool, []byte, error) {
|
|
|
|
return parseSymSig(b)
|
2023-04-02 13:12:06 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseCroak parses and extracts the expected argument portion of a CROAK instruction
|
2023-04-02 14:59:40 +02:00
|
|
|
func ParseCroak(b []byte) (uint32, bool, []byte, error) {
|
|
|
|
return parseSig(b)
|
2023-03-31 11:52:04 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseInCmp parses and extracts the expected argument portion of a INCMP instruction
|
2023-04-02 13:12:06 +02:00
|
|
|
func ParseInCmp(b []byte) (string, string, []byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
return parseTwoSym(b)
|
2023-04-02 13:12:06 +02:00
|
|
|
}
|
|
|
|
|
2023-04-08 11:01:28 +02:00
|
|
|
// ParseMPrev parses and extracts the expected argument portion of a MPREV instruction
|
|
|
|
func ParseMPrev(b []byte) (string, string, []byte, error) {
|
|
|
|
return parseTwoSym(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseMNext parses and extracts the expected argument portion of a MNEXT instruction
|
|
|
|
func ParseMNext(b []byte) (string, string, []byte, error) {
|
|
|
|
return parseTwoSym(b)
|
|
|
|
}
|
2023-04-03 10:11:44 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseMSize parses and extracts the expected argument portion of a MSIZE instruction
|
2023-04-03 10:52:58 +02:00
|
|
|
func ParseMSize(b []byte) (uint32, uint32, []byte, error) {
|
|
|
|
if len(b) < 2 {
|
|
|
|
return 0, 0, b, fmt.Errorf("argument too short")
|
2023-04-03 00:53:21 +02:00
|
|
|
}
|
|
|
|
r := uint32(b[0])
|
2023-04-03 10:52:58 +02:00
|
|
|
rr := uint32(b[1])
|
|
|
|
b = b[2:]
|
|
|
|
return r, rr, b, nil
|
2023-04-03 00:53:21 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// ParseMOut parses and extracts the expected argument portion of a MOUT instruction
|
2023-04-03 00:53:21 +02:00
|
|
|
func ParseMOut(b []byte) (string, string, []byte, error) {
|
|
|
|
return parseTwoSym(b)
|
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// noop
|
2023-04-02 14:59:40 +02:00
|
|
|
func parseNoArg(b []byte) ([]byte, error) {
|
|
|
|
return b, nil
|
2023-04-02 13:12:06 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// parse and extract two length-prefixed string values
|
2023-04-02 14:59:40 +02:00
|
|
|
func parseSym(b []byte) (string, []byte, error) {
|
2023-04-04 08:42:15 +02:00
|
|
|
sym, b, err := instructionSplit(b)
|
2023-03-31 14:24:14 +02:00
|
|
|
if err != nil {
|
2023-04-02 13:12:06 +02:00
|
|
|
return "", b, err
|
2023-03-31 14:24:14 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
return sym, b, nil
|
2023-03-31 16:03:54 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// parse and extract two length-prefixed string values
|
2023-04-02 14:59:40 +02:00
|
|
|
func parseTwoSym(b []byte) (string, string, []byte, error) {
|
2023-04-04 08:42:15 +02:00
|
|
|
symOne, b, err := instructionSplit(b)
|
2023-03-31 16:03:54 +02:00
|
|
|
if err != nil {
|
2023-04-02 13:12:06 +02:00
|
|
|
return "", "", b, err
|
2023-03-31 16:03:54 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
symTwo, b, err := instructionSplit(b)
|
2023-03-31 20:24:30 +02:00
|
|
|
if err != nil {
|
2023-04-04 08:42:15 +02:00
|
|
|
return "", "", b, err
|
2023-03-31 20:24:30 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
return symOne, symTwo, b, nil
|
2023-03-31 16:03:54 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// parse and extract one length-prefixed string value, and one length-prefixed integer value
|
2023-04-02 14:59:40 +02:00
|
|
|
func parseSymLen(b []byte) (string, uint32, []byte, error) {
|
2023-04-04 08:42:15 +02:00
|
|
|
sym, b, err := instructionSplit(b)
|
2023-04-02 00:56:02 +02:00
|
|
|
if err != nil {
|
2023-04-02 13:12:06 +02:00
|
|
|
return "", 0, b, err
|
2023-04-02 00:56:02 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
sz, b, err := intSplit(b)
|
2023-04-01 23:19:12 +02:00
|
|
|
if err != nil {
|
2023-04-02 13:12:06 +02:00
|
|
|
return "", 0, b, err
|
2023-04-01 23:19:12 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
return sym, sz, b, nil
|
2023-04-02 13:12:06 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// parse and extract one length-prefixed string value, and one single byte of integer
|
2023-04-02 14:59:40 +02:00
|
|
|
func parseSymSig(b []byte) (string, uint32, bool, []byte, error) {
|
2023-04-04 08:42:15 +02:00
|
|
|
sym, b, err := instructionSplit(b)
|
2023-04-02 13:12:06 +02:00
|
|
|
if err != nil {
|
2023-04-02 14:59:40 +02:00
|
|
|
return "", 0, false, b, err
|
2023-04-01 23:19:12 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
sig, b, err := intSplit(b)
|
2023-04-01 23:19:12 +02:00
|
|
|
if err != nil {
|
2023-04-02 14:59:40 +02:00
|
|
|
return "", 0, false, b, err
|
2023-04-01 23:19:12 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
if len(b) == 0 {
|
2023-04-02 14:59:40 +02:00
|
|
|
return "", 0, false, b, fmt.Errorf("instruction too short")
|
2023-04-01 23:19:12 +02:00
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
matchmode := b[0] > 0
|
|
|
|
b = b[1:]
|
2023-04-02 14:59:40 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
return sym, sig, matchmode, b, nil
|
2023-04-02 14:59:40 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// parse and extract one single byte of integer
|
2023-04-02 14:59:40 +02:00
|
|
|
func parseSig(b []byte) (uint32, bool, []byte, error) {
|
|
|
|
sig, b, err := intSplit(b)
|
|
|
|
if err != nil {
|
|
|
|
return 0, false, b, err
|
|
|
|
}
|
|
|
|
if len(b) == 0 {
|
|
|
|
return 0, false, b, fmt.Errorf("instruction too short")
|
|
|
|
}
|
|
|
|
matchmode := b[0] > 0
|
|
|
|
b = b[1:]
|
|
|
|
|
|
|
|
return sig, matchmode, b, nil
|
|
|
|
}
|
|
|
|
|
2023-04-01 23:19:12 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// split bytecode into head and b using length-prefixed bitfield
|
2023-04-02 14:59:40 +02:00
|
|
|
func byteSplit(b []byte) ([]byte, []byte, error) {
|
|
|
|
bitFieldSize := b[0]
|
|
|
|
bitField := b[1:1+bitFieldSize]
|
|
|
|
b = b[1+bitFieldSize:]
|
|
|
|
return bitField, b, nil
|
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// split bytecode into head and b using length-prefixed integer
|
2023-04-02 13:12:06 +02:00
|
|
|
func intSplit(b []byte) (uint32, []byte, error) {
|
|
|
|
l := uint8(b[0])
|
|
|
|
sz := uint32(l)
|
|
|
|
b = b[1:]
|
|
|
|
if l > 0 {
|
|
|
|
r := []byte{0, 0, 0, 0}
|
|
|
|
c := 0
|
|
|
|
ll := 4 - l
|
|
|
|
var i uint8
|
|
|
|
for i = 0; i < 4; i++ {
|
|
|
|
if i >= ll {
|
|
|
|
r[i] = b[c]
|
|
|
|
c += 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sz = binary.BigEndian.Uint32(r)
|
|
|
|
b = b[l:]
|
2023-04-01 15:47:03 +02:00
|
|
|
}
|
2023-04-02 13:12:06 +02:00
|
|
|
return sz, b, nil
|
2023-03-31 11:52:04 +02:00
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// split bytecode into head and b using length-prefixed string
|
2023-03-31 16:03:54 +02:00
|
|
|
func instructionSplit(b []byte) (string, []byte, error) {
|
|
|
|
if len(b) == 0 {
|
|
|
|
return "", nil, fmt.Errorf("argument is empty")
|
|
|
|
}
|
|
|
|
sz := uint8(b[0])
|
|
|
|
if sz == 0 {
|
|
|
|
return "", nil, fmt.Errorf("zero-length argument")
|
|
|
|
}
|
2023-04-04 08:42:15 +02:00
|
|
|
bSz := uint8(len(b))
|
|
|
|
if bSz < sz {
|
|
|
|
return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", bSz, sz)
|
2023-03-31 16:03:54 +02:00
|
|
|
}
|
|
|
|
r := string(b[1:1+sz])
|
|
|
|
return r, b[1+sz:], nil
|
2023-03-31 11:52:04 +02:00
|
|
|
}
|
2023-04-02 13:12:06 +02:00
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// check if the start of the given bytecode contains a valid opcode, extract and return it
|
2023-04-02 13:12:06 +02:00
|
|
|
func opCheck(b []byte, opIn Opcode) ([]byte, error) {
|
2023-04-02 14:59:40 +02:00
|
|
|
var bb []byte
|
|
|
|
op, bb, err := opSplit(b)
|
2023-04-02 13:12:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return b, err
|
|
|
|
}
|
2023-04-02 14:59:40 +02:00
|
|
|
b = bb
|
2023-04-02 13:12:06 +02:00
|
|
|
if op != opIn {
|
|
|
|
return b, fmt.Errorf("not a %v instruction", op)
|
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
2023-04-04 08:42:15 +02:00
|
|
|
// split bytecode into head and b using opcode
|
2023-04-02 13:12:06 +02:00
|
|
|
func opSplit(b []byte) (Opcode, []byte, error) {
|
|
|
|
l := len(b)
|
|
|
|
if l < 2 {
|
|
|
|
return 0, b, fmt.Errorf("input size %v too short for opcode", l)
|
|
|
|
}
|
|
|
|
op := binary.BigEndian.Uint16(b)
|
|
|
|
if op > _MAX {
|
|
|
|
return 0, b, fmt.Errorf("invalid opcode %v", op)
|
|
|
|
}
|
|
|
|
return Opcode(op), b[2:], nil
|
|
|
|
}
|