|
libose
|
the OSE virtual machine More...
Go to the source code of this file.
Macros | |
| #define | OSEVM_FAULT_SIZE 128 |
| Usable bytes of the fault register. | |
Built-in Addresses | |
| #define | OSEVM_ADDR_INPUT "/_i" |
| #define | OSEVM_ADDR_STACK "/_s" |
| #define | OSEVM_ADDR_ENV "/_e" |
| #define | OSEVM_ADDR_CONTROL "/_c" |
| #define | OSEVM_ADDR_DUMP "/_d" |
| #define | OSEVM_ADDR_STDLIB "/_l" |
| #define | OSEVM_ADDR_HOST "/_x" |
| #define | OSEVM_ADDR_CACHE "/_0" |
| #define | OSEVM_ADDR_FAULT "/_h" |
| #define | OSEVM_ADDR_PARAMS "/_p" |
| The parameter a value is waiting for. | |
Built-in Prefixes | |
| #define | OSEVM_ADDR_assign "/@" |
| #define | OSEVM_ADDR_quote "/'" |
| #define | OSEVM_ADDR_funcall "/!" |
| #define | OSEVM_ADDR_lookup "/$" |
| #define | OSEVM_ADDR_copyRegisterToElem "/>" |
| #define | OSEVM_ADDR_appendElemToRegister "/<<" |
| #define | OSEVM_ADDR_replaceRegisterWithElem "/<" |
| #define | OSEVM_ADDR_moveElemToRegister "/-" |
| #define | OSEVM_ADDR_toType "/," |
| #define | OSEVM_ADDR_toInt32 "/,/i" |
| #define | OSEVM_ADDR_toFloat "/,/f" |
| #define | OSEVM_ADDR_toString "/,/s" |
| #define | OSEVM_ADDR_toBlob "/,/b" |
| #define | OSEVM_ADDR_appendByte "/&" |
Cache Offsets | |
| #define | OSEVM_CACHE_SIZE 30 |
| #define | OSEVM_CACHE_MSG_SIZE |
| #define | OSEVM_CACHE_OFFSET_0 |
| #define | OSEVM_CACHE_OFFSET_1 OSEVM_CACHE_OFFSET_0 + 4 |
| #define | OSEVM_CACHE_OFFSET_2 OSEVM_CACHE_OFFSET_1 + 4 |
| #define | OSEVM_CACHE_OFFSET_3 OSEVM_CACHE_OFFSET_2 + 4 |
| #define | OSEVM_CACHE_OFFSET_4 OSEVM_CACHE_OFFSET_3 + 4 |
| #define | OSEVM_CACHE_OFFSET_5 OSEVM_CACHE_OFFSET_4 + 4 |
| #define | OSEVM_CACHE_OFFSET_6 OSEVM_CACHE_OFFSET_5 + 4 |
| #define | OSEVM_CACHE_OFFSET_7 OSEVM_CACHE_OFFSET_6 + 4 |
| #define | OSEVM_CACHE_OFFSET_8 OSEVM_CACHE_OFFSET_7 + 4 |
| #define | OSEVM_CACHE_OFFSET_9 OSEVM_CACHE_OFFSET_8 + 4 |
| #define | OSEVM_CACHE_OFFSET_10 OSEVM_CACHE_OFFSET_9 + 4 |
| #define | OSEVM_CACHE_OFFSET_11 OSEVM_CACHE_OFFSET_10 + 4 |
| #define | OSEVM_CACHE_OFFSET_12 OSEVM_CACHE_OFFSET_11 + 4 |
| #define | OSEVM_CACHE_OFFSET_13 OSEVM_CACHE_OFFSET_12 + 4 |
| #define | OSEVM_CACHE_OFFSET_14 OSEVM_CACHE_OFFSET_13 + 4 |
| #define | OSEVM_CACHE_OFFSET_15 OSEVM_CACHE_OFFSET_14 + 4 |
| #define | OSEVM_CACHE_OFFSET_16 OSEVM_CACHE_OFFSET_15 + 4 |
| #define | OSEVM_CACHE_OFFSET_17 OSEVM_CACHE_OFFSET_16 + 4 |
| #define | OSEVM_CACHE_OFFSET_18 OSEVM_CACHE_OFFSET_17 + 4 |
| #define | OSEVM_CACHE_OFFSET_19 OSEVM_CACHE_OFFSET_18 + 4 |
| #define | OSEVM_CACHE_OFFSET_20 OSEVM_CACHE_OFFSET_19 + 4 |
| #define | OSEVM_CACHE_OFFSET_21 OSEVM_CACHE_OFFSET_20 + 4 |
| #define | OSEVM_CACHE_OFFSET_22 OSEVM_CACHE_OFFSET_21 + 4 |
| #define | OSEVM_CACHE_OFFSET_23 OSEVM_CACHE_OFFSET_22 + 4 |
| #define | OSEVM_CACHE_OFFSET_24 OSEVM_CACHE_OFFSET_23 + 4 |
| #define | OSEVM_CACHE_OFFSET_25 OSEVM_CACHE_OFFSET_24 + 4 |
| #define | OSEVM_CACHE_OFFSET_26 OSEVM_CACHE_OFFSET_25 + 4 |
| #define | OSEVM_CACHE_OFFSET_27 OSEVM_CACHE_OFFSET_26 + 4 |
| #define | OSEVM_CACHE_OFFSET_28 OSEVM_CACHE_OFFSET_27 + 4 |
| #define | OSEVM_CACHE_OFFSET_29 OSEVM_CACHE_OFFSET_28 + 4 |
| #define | OSEVM_CACHE_OFFSET_30 OSEVM_CACHE_OFFSET_29 + 4 |
| #define | OSEVM_CACHE_POINTER OSEVM_CACHE_OFFSET_0 |
| #define | OSEVM_CACHE_FLAGS OSEVM_CACHE_OFFSET_1 |
| #define | OSEVM_CACHE_OFFSET_INPUT OSEVM_CACHE_OFFSET_2 |
| #define | OSEVM_CACHE_OFFSET_STACK OSEVM_CACHE_OFFSET_3 |
| #define | OSEVM_CACHE_OFFSET_ENV OSEVM_CACHE_OFFSET_4 |
| #define | OSEVM_CACHE_OFFSET_CONTROL OSEVM_CACHE_OFFSET_5 |
| #define | OSEVM_CACHE_OFFSET_DUMP OSEVM_CACHE_OFFSET_6 |
| #define | OSEVM_CACHE_OFFSET_FAULT OSEVM_CACHE_OFFSET_7 |
VM Configuration | |
VM Bundle Offsets | |
| #define | OSEVM_INPUT_CONTEXT_MESSAGE_OFFSET OSE_BUNDLE_HEADER_LEN + OSEVM_CACHE_MSG_SIZE |
| #define | OSEVM_INPUT_BUNDLE_OFFSET OSEVM_INPUT_CONTEXT_MESSAGE_OFFSET + OSE_CONTEXT_BUNDLE_OFFSET |
| #define | OSEVM_STACK_CONTEXT_MESSAGE_OFFSET |
| #define | OSEVM_STACK_BUNDLE_OFFSET OSEVM_STACK_CONTEXT_MESSAGE_OFFSET + OSE_CONTEXT_BUNDLE_OFFSET |
| #define | OSEVM_ENV_CONTEXT_MESSAGE_OFFSET |
| #define | OSEVM_ENV_BUNDLE_OFFSET OSEVM_ENV_CONTEXT_MESSAGE_OFFSET + OSE_CONTEXT_BUNDLE_OFFSET |
| #define | OSEVM_CONTROL_CONTEXT_MESSAGE_OFFSET |
| #define | OSEVM_CONTROL_BUNDLE_OFFSET OSEVM_CONTROL_CONTEXT_MESSAGE_OFFSET + OSE_CONTEXT_BUNDLE_OFFSET |
| #define | OSEVM_DUMP_CONTEXT_MESSAGE_OFFSET |
| #define | OSEVM_DUMP_BUNDLE_OFFSET OSEVM_DUMP_CONTEXT_MESSAGE_OFFSET + OSE_CONTEXT_BUNDLE_OFFSET |
| #define | OSEVM_FAULT_CONTEXT_MESSAGE_OFFSET |
| #define | OSEVM_FAULT_BUNDLE_OFFSET OSEVM_FAULT_CONTEXT_MESSAGE_OFFSET + OSE_CONTEXT_BUNDLE_OFFSET |
Components of the VM | |
| #define | OSEVM_INPUT(osevm) |
| Get the input bundle. | |
| #define | OSEVM_STACK(osevm) |
| Get the stack bundle. | |
| #define | OSEVM_ENV(osevm) |
| Get the environment bundle. | |
| #define | OSEVM_CONTROL(osevm) |
| Get the control bundle. | |
| #define | OSEVM_DUMP(osevm) |
| Get the dump bundle. | |
| #define | OSEVM_FAULT(osevm) |
| Get the fault bundle. | |
Cache Getters and Setters | |
| #define | OSEVM_GET_FLAGS(osevm) ose_readInt32(osevm, OSEVM_CACHE_FLAGS) |
| #define | OSEVM_SET_FLAGS(osevm, flags) ose_writeInt32(osevm, OSEVM_CACHE_FLAGS, flags) |
| #define | OSEVM_GET_CACHE_VALUE(osevm, idx) |
| #define | OSEVM_SET_CACHE_VALUE(osevm, idx, val) |
Functions | |
| ose_bundle | osevm_init (ose_bundle bundle) |
| Initialize the VM. | |
| void | osevm_setFault (ose_bundle osevm, enum ose_errno e) |
| Record a fault in /_h. | |
| enum ose_errno | osevm_getFault (ose_bundle osevm) |
| The fault this machine is in, or OSE_ERR_NONE. | |
| void | osevm_clearFault (ose_bundle osevm) |
| Declare the fault handled. | |
| int32_t | osevm_inputMessages (ose_bundle osevm, int32_t size, const char *const bundle) |
| Copy the contents of an OSC bundle into the input bundle of the VM. | |
| int32_t | osevm_inputData (ose_bundle osevm, int32_t size, const char *const bundle) |
| int32_t | osevm_inputMessage (ose_bundle osevm, int32_t size, const char *const message) |
| Copy a single OSC element into the input bundle of the VM. | |
| void | osevm_run (ose_bundle bundle) |
| Run the VM. | |
| char | osevm_step (ose_bundle osevm) |
| Step the VM. | |
| int32_t | osevm_computeSizeReqs (int n,...) |
| Compute the size requirements of the VM given a list of sizes of each of additional VM bundle to be created at runtime. | |
Default hooks | |
| void | osevm_respondToString (ose_bundle osevm) |
| void | osevm_preInput (ose_bundle osevm) |
| void | osevm_postInput (ose_bundle osevm) |
| void | osevm_popInputToControl (ose_bundle osevm) |
| void | osevm_postControl (ose_bundle osevm) |
| void | osevm_evalType (ose_bundle osevm) |
Declarations | |
Which registers a bundle carries. A bundle declares which of the VM's registers it carries. Running it means installing those; a bundle that declares none is data. What is here and what is not: this reads bytes and nothing else – which of six addresses are present. It does not install, does not judge, and does not know that OSEVM_DECL_I alone is called a thunk. That is the split, and the reason this is in libose rather than a layer above: ose manipulates, and its clients interpret. The vocabulary (thunk, lambda, closure, continuation, machine) belongs to whoever is doing the interpreting, and two clients may reasonably disagree. Bit | |
| #define | OSEVM_DECL_NREGISTERS 6 |
| enum | osevm_decl { OSEVM_DECL_NONE = 0 , OSEVM_DECL_I = 1 << 0 , OSEVM_DECL_S = 1 << 1 , OSEVM_DECL_E = 1 << 2 , OSEVM_DECL_C = 1 << 3 , OSEVM_DECL_D = 1 << 4 , OSEVM_DECL_P = 1 << 5 } |
| const char *const | osevm_decl_registers [6] |
| int32_t | osevm_declares (ose_bundle src) |
Which registers src declares. | |
| int32_t | osevm_declaresAtOffset (ose_bundle B, int32_t offset) |
Which registers the bundle ELEMENT at offset declares. | |
Hooks | |
| #define | OSEVM_ASSIGN osevm_assignStackToEnv |
| #define | OSEVM_LOOKUP osevm_lookupInEnv_impl |
| #define | OSEVM_FUNCALL osevm_funcall |
| #define | OSEVM_QUOTE osevm_quote |
| #define | OSEVM_COPYREGISTERTOELEM osevm_copyRegisterToElem |
| #define | OSEVM_APPENDELEMTOREGISTER osevm_appendElemToRegister |
| #define | OSEVM_REPLACEREGISTERWITHELEM osevm_replaceRegisterWithElem |
| #define | OSEVM_MOVEELEMTOREGISTER osevm_moveElemToRegister |
| #define | OSEVM_TOTYPE osevm_toType |
| #define | OSEVM_TOINT32 osevm_toInt32 |
| #define | OSEVM_TOFLOAT osevm_toFloat |
| #define | OSEVM_TOSTRING osevm_toString |
| #define | OSEVM_TOBLOB osevm_toBlob |
| #define | OSEVM_TOSYMBOL osevm_toSymbol |
| #define | OSEVM_TODOUBLE osevm_toDouble |
| #define | OSEVM_TOINT8 osevm_toInt8 |
| #define | OSEVM_TOUINT8 osevm_toUInt8 |
| #define | OSEVM_TOINT16 osevm_toInt16 |
| #define | OSEVM_TOUINT16 osevm_toUInt16 |
| #define | OSEVM_TOUINT32 osevm_toUInt32 |
| #define | OSEVM_TOINT64 osevm_toInt64 |
| #define | OSEVM_TOUINT64 osevm_toUInt64 |
| #define | OSEVM_TOTIMETAG osevm_toTimetag |
| #define | OSEVM_TOTRUE osevm_toTrue |
| #define | OSEVM_TOFALSE osevm_toFalse |
| #define | OSEVM_TONIL osevm_toNil |
| #define | OSEVM_TOINFINITUM osevm_toInfinitum |
| #define | OSEVM_APPENDBYTE osevm_appendByte |
| #define | OSEVM_RESPONDTOSTRING osevm_respondToString |
| #define | OSEVM_PREINPUT osevm_preInput |
| #define | OSEVM_POSTINPUT osevm_postInput |
| #define | OSEVM_POPINPUTTOCONTROL osevm_popInputToControl |
| #define | OSEVM_POSTCONTROL osevm_postControl |
| #define | OSEVM_TYPE_ADDR_EVALTYPE_SUFFIX "/EVTY" |
| #define | OSEVM_EVALTYPE_ADDR OSE_TYPE_ADDR_PREFIX "%c" OSEVM_TYPE_ADDR_EVALTYPE_SUFFIX |
| #define | OSEVM_EVALTYPE_ADDR_LEN sizeof(OSEVM_EVALTYPE_ADDR) |
| #define | OSEVM_EVALTYPE osevm_evalType_hook |
| void | osevm_evalType_hook (ose_bundle osevm) |
the OSE virtual machine
Sigils, and why a string can be a program.
OSE is a restricted form of OSC that uses only addresses and no data: a program is a sequence of addresses. But a bundle arriving from a wire may carry data, and the only way to encode an OSE program inside an ordinary OSC bundle is to put it in a string. So a string in a message's data section is dispatched exactly as an address is.
The mechanism is in ose_vm.c and is worth knowing before reading either file: popAllControl() pops every item of the control message into its own control element, and the run loop then routes each one through applyControl() on its leading /x/ token. A string item is therefore routed like an address, and a string that begins with a sigil RUNS.
Which means a string carrying an instruction is not inert anywhere in this system. To carry one as DATA, quote it:
"/@/held" assigns -- the sigil runs "/'/@/held" the four characters /@/held, as a string
That is what the quote sigil is for, and it is how a bundle OF instructions is built at all: { "/'/!/one", "/'/!/two" } is two instructions being CARRIED, where the same two unquoted would simply have run.
A string with no sigil falls through to OSEVM_RESPONDTOSTRING, which is an empty hook here, which is why a plain "/x" simply stays where it is.
| #define OSEVM_ADDR_PARAMS "/_p" |
The parameter a value is waiting for.
Defined here and never created here, which is the same arrangement ose.h describes for OSETT_BUNDLE_: libose owns the address so that everyone spells it the same way, and a client gives it meaning. osevm_init() does not make a /_p context message and nothing in libose reads one.
| #define OSEVM_CACHE_MSG_SIZE |
| #define OSEVM_CACHE_OFFSET_0 |
| #define OSEVM_CONTROL | ( | osevm | ) |
Get the control bundle.
| #define OSEVM_CONTROL_CONTEXT_MESSAGE_OFFSET |
| #define OSEVM_DUMP | ( | osevm | ) |
Get the dump bundle.
| #define OSEVM_DUMP_CONTEXT_MESSAGE_OFFSET |
| #define OSEVM_ENV | ( | osevm | ) |
Get the environment bundle.
| #define OSEVM_ENV_CONTEXT_MESSAGE_OFFSET |
| #define OSEVM_FAULT | ( | osevm | ) |
Get the fault bundle.
| #define OSEVM_FAULT_CONTEXT_MESSAGE_OFFSET |
| #define OSEVM_GET_CACHE_VALUE | ( | osevm, | |
| idx | |||
| ) |
| #define OSEVM_INPUT | ( | osevm | ) |
Get the input bundle.
| #define OSEVM_SET_CACHE_VALUE | ( | osevm, | |
| idx, | |||
| val | |||
| ) |
| #define OSEVM_STACK | ( | osevm | ) |
Get the stack bundle.
| #define OSEVM_STACK_CONTEXT_MESSAGE_OFFSET |
| int32_t osevm_computeSizeReqs | ( | int | n, |
| ... | |||
| ) |
Compute the size requirements of the VM given a list of sizes of each of additional VM bundle to be created at runtime.
This function will compute the total size requirements of the VM that can be used to allocate sufficient memory. The list of sizes here is a list of sizes in addition to those that the VM creates (the input, stack, environment, etc).
| n | the number of additional bundle sizes |
| ... | a list of additional bundle sizes |
| int32_t osevm_declares | ( | ose_bundle | src | ) |
Which registers src declares.
| int32_t osevm_declaresAtOffset | ( | ose_bundle | B, |
| int32_t | offset | ||
| ) |
Which registers the bundle ELEMENT at offset declares.
The same question asked of a value in place, without moving it. Doors need this: what arrives is an element inside the input or on the stack, and copying it out to ask would cost more than the answer.
offset must be the offset of a bundle element. A message declares OSEVM_DECL_NONE. | ose_bundle osevm_init | ( | ose_bundle | bundle | ) |
Initialize the VM.
| bundle | an OSE bundle created with ose_newBundleFromCBytes |
bundle must have been created with ose_newBundleFromCBytes.| a | null bundle if bundle had no room for the six registers; the errno on bundle says which way it refused. Half a VM used to come back looking valid – see the note in ose_vm.c. |
| int32_t osevm_inputMessage | ( | ose_bundle | osevm, |
| int32_t | size, | ||
| const char *const | message | ||
| ) |
Copy a single OSC element into the input bundle of the VM.
| osevm | the VM |
| size | the size in bytes of message |
| message | the message to copy |
| int32_t osevm_inputMessages | ( | ose_bundle | osevm, |
| int32_t | size, | ||
| const char *const | bundle | ||
| ) |
Copy the contents of an OSC bundle into the input bundle of the VM.
This function copies the elements contained in bundle into the input bundle of osevm. Since the VM processes input elements backwards, i.e. starting with the last element, this function reverses the order of the elements contained in bundle.
| osevm | the VM |
| size | the size in bytes of bundle |
| bundle | an OSC bundle containing the messages to copy to the input |
| void osevm_run | ( | ose_bundle | bundle | ) |
Run the VM.
This function runs the VM until every element of the input bundle has been processed and it is empty.
| bundle | the VM |
| void osevm_setFault | ( | ose_bundle | osevm, |
| enum ose_errno | e | ||
| ) |
Record a fault in /_h.
The status word that ose_errno_set() writes is a signal from the LAST OPERATION: every caller of an ose_ function may read it, and the run loop clears it before dispatching a handler, so by the time anything downstream looks, it is gone. That is the right lifetime for "the call you just made failed" and the wrong one for "this machine is faulted".
/_h holds the second. It is set when a fault is raised and stays set until something declares the fault handled – which libose never does, because what counts as handled is a property of the language being interpreted, not of the VM. The interpreting client calls osevm_clearFault().
It is a register rather than a header field so that it is addressable: a machine can be asked about its own fault in the language it is written in, which the status word cannot be. A read costs 0.71ns against the status word's 1.45ns when the offset is known – which it is, from the cache – so nothing was traded for it.
It does not travel yet. A machine is packed from the registers named in osevm_decl_registers[], which is six fixed bits, and /_h is not one of them; a machine that crosses a socket still arrives looking healthy. That is the manifest work, not this.
| char osevm_step | ( | ose_bundle | osevm | ) |
Step the VM.
This function executes a single step of the VM, returning a value indicating whether it has more steps to perform or not.
| osevm | the VM |
| OSETT_TRUE | if there are more steps to perform |
| OSETT_FALSE | if the input has been fully processed |