Skip to content

Commit 9992c70

Browse files
committed
Add C API VM function call example
1 parent 7a522e7 commit 9992c70

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

docs/emulator/vmcalls.md

+58
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,61 @@ inline std::optional<Script::sgaddr_t> Script::call(gaddr_t address, Args&&... a
271271
}
272272
```
273273
From `script.call(...)` as implemented in the [gamedev example](https://github.com/libriscv/libriscv/blob/master/examples/gamedev/script.hpp). `ScriptDepthMeter` measures the current call depth in order to avoid too many recursive calls back into the script, while also using the faster `vmcall()` on the first call.
274+
275+
## VM Calls with the C API
276+
277+
In the C API each argument register has to be populated manually, and the return value(s) have to be read as well.
278+
279+
A general rule for passing data to a function is that:
280+
1. Each integer goes into the next free integer register
281+
2. Each pointer goes into the next free integer register
282+
3. Each float goes into the next free float register of that type
283+
284+
### Example
285+
286+
Function:
287+
```c
288+
long my_function(const char* arg1, const bool arg2, const struct my_struct* arg3, size_t arg4, float arg5);
289+
```
290+
291+
Input values:
292+
```c
293+
const char* mystring = "";
294+
const bool mybool = true;
295+
const struct mystruct s;
296+
const size_t mysize = 4;
297+
const float myfloat = 5.0f;
298+
```
299+
300+
C++ API:
301+
```cpp
302+
long result =
303+
machine.vmcall(mystring, mybool, mystruct, mysize, myfloat);
304+
```
305+
306+
C API:
307+
```c
308+
/* We start by setting up the call, providing the address of the function */
309+
libriscv_setup_vmcall(machine, guest_function_address);
310+
311+
/**
312+
* We push structs on the stack, and get a guest pointer in return.
313+
* Guest pointers go into integer registers along with other integers
314+
**/
315+
LIBRISCV_ARG_REGISTER(regs, 0) = libriscv_stack_push(machine, regs, ...);
316+
LIBRISCV_ARG_REGISTER(regs, 1) = true;
317+
LIBRISCV_ARG_REGISTER(regs, 2) = libriscv_stack_push(machine, regs, ...);
318+
LIBRISCV_ARG_REGISTER(regs, 3) = 4;
319+
/* Floating point values go into their own registers */
320+
LIBRISCV_FP32_ARG_REG(regs, 0) = 5.0f;
321+
322+
/* Running the VM will now execute the function with the above arguments as input */
323+
if (libriscv_run(machine, ~0)) {
324+
fprintf(stderr, "Ooops! Function call failed!\n");
325+
exit(1);
326+
}
327+
328+
/* Integer return values are in A0. */
329+
long result = LIBRISCV_ARG_REGISTER(regs, 0);
330+
printf("The function returned: %ld\n", result);
331+
```

0 commit comments

Comments
 (0)