diff --git a/README.md b/README.md index 9f5c688..4ae7913 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,13 @@ Oaknut is a header-only library that allows one to dynamically assemble code in- ## Usage -Provide `oaknut::CodeGenerator` with a pointer to a block of memory. Call functions on it to emit code. +Give `oaknut::CodeGenerator` a pointer to a block of memory. Call functions on it to emit code. Simple example: ```cpp #include +#include #include using EmittedFunction = int (*)(); @@ -46,6 +47,44 @@ int main() } ``` +### Emit to `std::vector` + +If you wish to merely emit code into memory without executing it, or if you are developing a cross-compiler that is not running on an ARM64 device, you can use `oaknut::VectorCodeGenerator` instead. + +Provide `oaknut::VectorCodeGenerator` with a reference to a `std::vector` and it will append to that vector. + +Simple example: + +```cpp +#include +#include +#include +#include + +int main() +{ + std::vector vec; + oaknut::VectorCodeGenerator code{vec}; + + code.MOV(W0, 42); + code.RET(); + + std::printf("%08x %08x\n", vec[0], vec[1]); // Output: d2800540 d65f03c0 + + return 0; +} +``` + +## Headers + +| Header | Compiles on non-ARM64 | Contents | +| ------ | --------------------- | -------- | +| `` | Yes | Provides `CodeGenerator` and `VectorCodeGenerator` for code emission, as well as the `oaknut::util` namespace. | +| `` | No | Utility header that provides `CodeBlock`, allocates, alters permissions of, and invalidates executable memory. | +| `` | Yes | Provides `OaknutException` which is thrown on an error. | +| `` | Yes | Utility header that provides `CpuFeatures` which can be used to describe AArch64 features. | +| `` | No | Utility header that provides `detect_features` and `read_id_registers` for determining available AArch64 features. | + ### Instructions Each AArch64 instruction corresponds to one emitter function. For a list of emitter functions see: @@ -108,6 +147,83 @@ List{V0.B(), V1.B(), V2.B()}[1] // This expression has type List`, then call `detect_features` to get a bitset of features in a cross-platform manner. + +CPU feature detection is operating system specific, and some operating systems even have multiple methods. Here are a list of supported operating systems and implemented methods: + +| Operating system | Default Method | +| ---- | ---- | +| Linux / Android | [ELF hwcaps](https://www.kernel.org/doc/html/latest/arch/arm64/elf_hwcaps.html) | +| Apple | [sysctlbyname](https://developer.apple.com/documentation/kernel/1387446-sysctlbyname) | +| Windows | [IsProcessorFeaturePresent](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent) | +| FreeBSD | ELF hwcaps | +| NetBSD | machdep.cpu%d.cpu_id sysctl | +| OpenBSD | CTL_MACHDEP.CPU_ID_* sysctl | + +There are alternative methods available for advanced users to specify specific methods to detect features if they wish. (See `detect_features_via_*`.) + +Simple example: + +```cpp +#include +#include + +int main() { + oaknut::CpuFeatures feats = oaknut::detect_features(); + + std::printf("CPU supports JSCVT: %i\n", feats.has(oaknut::CpuFeature::JSCVT)); +} +``` + +### ID registers + +We also provide a crossplatform way for ID registers to be read: + +| **`OAKNUT_SUPPORTS_READING_ID_REGISTERS`** | Available functionality | +| ---- | ---- | +| 0 | Reading ID registers is not supported on this operating system. | +| 1 | This operating system provides a system-wide set of ID registers, use `read_id_registers()`. | +| 2 | Per-core ID registers, use `get_core_count()` and `read_id_registers(int index)`. | + +All of the above operating systems with the exception of apple also support reading ID registers, and if one prefers one can do feature detection via `detect_features_via_id_registers(*read_id_registers())`. + +Simple example: + +```cpp +#include +#include +#include + +int main() { +#if OAKNUT_SUPPORTS_READING_ID_REGISTERS == 1 + + oaknut::id::IdRegisters id = oaknut::read_id_registers(); + + std::printf("ISAR0 register: %08x\n", id.isar0.value); + +#elif OAKNUT_SUPPORTS_READING_ID_REGISTERS == 2 + + oaknut::id::IdRegisters id = oaknut::read_id_registers(0); + + const std::size_t core_count = oaknut::get_core_count(); + for (std::size_t core_index = 0; core_index < core_count; core_index++) { + std::printf("ISAR0 register (for core %zu): %08x\n", core_index, id.isar0.value); + } + +#else + + std::printf("Reading ID registers not supported\n"); + +#endif +} +``` + ## License This project is [MIT licensed](LICENSE).