Protocol API
Header: zbc_protocol.h
Low-level RIFF chunk parsing, constants, and helper functions. Most users don’t need these directly – use the client or host APIs instead.
Constants
ARM Semihosting Opcodes
#define SH_SYS_OPEN 0x01
#define SH_SYS_CLOSE 0x02
#define SH_SYS_WRITEC 0x03
#define SH_SYS_WRITE0 0x04
#define SH_SYS_WRITE 0x05
#define SH_SYS_READ 0x06
#define SH_SYS_READC 0x07
#define SH_SYS_ISERROR 0x08
#define SH_SYS_ISTTY 0x09
#define SH_SYS_SEEK 0x0A
#define SH_SYS_FLEN 0x0C
#define SH_SYS_TMPNAM 0x0D
#define SH_SYS_REMOVE 0x0E
#define SH_SYS_RENAME 0x0F
#define SH_SYS_CLOCK 0x10
#define SH_SYS_TIME 0x11
#define SH_SYS_SYSTEM 0x12
#define SH_SYS_ERRNO 0x13
#define SH_SYS_GET_CMDLINE 0x15
#define SH_SYS_HEAPINFO 0x16
#define SH_SYS_EXIT 0x18
#define SH_SYS_EXIT_EXTENDED 0x20
#define SH_SYS_ELAPSED 0x30
#define SH_SYS_TICKFREQ 0x31
#define SH_SYS_TIMER_CONFIG 0x32
Open Mode Flags
#define SH_OPEN_R 0 /* "r" */
#define SH_OPEN_RB 1 /* "rb" */
#define SH_OPEN_R_PLUS 2 /* "r+" */
#define SH_OPEN_R_PLUS_B 3 /* "r+b" */
#define SH_OPEN_W 4 /* "w" */
#define SH_OPEN_WB 5 /* "wb" */
#define SH_OPEN_W_PLUS 6 /* "w+" */
#define SH_OPEN_W_PLUS_B 7 /* "w+b" */
#define SH_OPEN_A 8 /* "a" */
#define SH_OPEN_AB 9 /* "ab" */
#define SH_OPEN_A_PLUS 10 /* "a+" */
#define SH_OPEN_A_PLUS_B 11 /* "a+b" */
RIFF FourCC Codes
#define ZBC_ID_RIFF ZBC_MAKEFOURCC('R','I','F','F')
#define ZBC_ID_SEMI ZBC_MAKEFOURCC('S','E','M','I')
#define ZBC_ID_CNFG ZBC_MAKEFOURCC('C','N','F','G')
#define ZBC_ID_CALL ZBC_MAKEFOURCC('C','A','L','L')
#define ZBC_ID_PARM ZBC_MAKEFOURCC('P','A','R','M')
#define ZBC_ID_DATA ZBC_MAKEFOURCC('D','A','T','A')
#define ZBC_ID_RETN ZBC_MAKEFOURCC('R','E','T','N')
#define ZBC_ID_ERRO ZBC_MAKEFOURCC('E','R','R','O')
Device Register Offsets
#define ZBC_REG_SIGNATURE 0x00 /* 8 bytes, R - ASCII "SEMIHOST" */
#define ZBC_REG_RIFF_PTR 0x08 /* 16 bytes, RW - pointer to RIFF buffer */
#define ZBC_REG_DOORBELL 0x18 /* 1 byte, W - write to trigger request */
#define ZBC_REG_STATUS 0x19 /* 1 byte, RW - interrupt pending (write 0 to clear) */
#define ZBC_REG_SIZE 0x20 /* Total register space: 32 bytes */
Structures
-
type zbc_chunk_t
Generic RIFF chunk: id(4) + size(4) + data[size].
All chunk access goes through this struct - no magic offsets.
-
type zbc_riff_t
RIFF container: “RIFF”(4) + size(4) + form_type(4) + chunks…
-
type zbc_parsed_t
Parsed RIFF SEMI structure.
Parse once with zbc_riff_parse(), then access fields directly. Pointers reference the original buffer (no copies).
Chunk Functions
-
int zbc_chunk_validate(const zbc_chunk_t *chunk, const uint8_t *container_end)
Validate that a chunk fits within container bounds.
- Parameters:
chunk – Pointer to chunk to validate
container_end – First byte PAST the valid container region
- Returns:
ZBC_OK, ZBC_ERR_NULL_ARG, ZBC_ERR_HEADER_OVERFLOW, or ZBC_ERR_DATA_OVERFLOW
-
int zbc_chunk_next(zbc_chunk_t **out, const zbc_chunk_t *chunk)
Get pointer to next sibling chunk.
Caller MUST validate the returned chunk before accessing it.
- Parameters:
out – Receives pointer to next chunk
chunk – Current chunk
- Returns:
ZBC_OK or ZBC_ERR_NULL_ARG
-
int zbc_chunk_first_sub(zbc_chunk_t **out, const zbc_chunk_t *container, size_t header_size)
Get pointer to first sub-chunk within a container chunk.
- Parameters:
out – Receives pointer to first sub-chunk
container – Parent chunk (e.g., CALL) that contains sub-chunks
header_size – Bytes to skip before sub-chunks (e.g., sizeof(zbc_call_header_t))
- Returns:
ZBC_OK or ZBC_ERR_NULL_ARG
-
int zbc_chunk_end(const uint8_t **out, const zbc_chunk_t *chunk)
Get container end pointer (for validating sub-chunks).
- Parameters:
out – Receives pointer to first byte past chunk data
chunk – The container chunk
- Returns:
ZBC_OK or ZBC_ERR_NULL_ARG
-
int zbc_chunk_find(zbc_chunk_t **out, const uint8_t *start, const uint8_t *end, uint32_t id)
Find chunk by ID within container bounds.
Validates each chunk while searching.
- Parameters:
out – Receives pointer to found chunk
start – Start of search region (first chunk)
end – First byte PAST the search region
id – FourCC to find
- Returns:
ZBC_OK, ZBC_ERR_NULL_ARG, ZBC_ERR_NOT_FOUND, ZBC_ERR_HEADER_OVERFLOW, or ZBC_ERR_DATA_OVERFLOW
RIFF Functions
-
int zbc_riff_validate(const zbc_riff_t *riff, size_t buf_size, uint32_t expected_form)
Validate RIFF container.
- Parameters:
riff – Pointer to RIFF container
buf_size – Total buffer size
expected_form – Expected form type (e.g., ZBC_ID_SEMI)
- Returns:
ZBC_OK, ZBC_ERR_NULL_ARG, ZBC_ERR_BAD_RIFF_MAGIC, ZBC_ERR_BAD_FORM_TYPE, or ZBC_ERR_RIFF_OVERFLOW
-
int zbc_riff_end(const uint8_t **out, const zbc_riff_t *riff)
Get end pointer for RIFF container.
- Parameters:
out – Receives pointer to first byte past RIFF data
riff – The RIFF container
- Returns:
ZBC_OK or ZBC_ERR_NULL_ARG
-
int zbc_riff_parse_request(zbc_parsed_t *out, const uint8_t *buf, size_t buf_size, int int_size, int endian)
Parse a RIFF SEMI request buffer into a zbc_parsed_t structure.
This is the host-side entry point for parsing client requests. It walks all chunks (CNFG/CALL/PARM/DATA), extracts relevant fields, and populates the parsed structure. After this call, the caller can simply check fields like:
if (parsed.has_call) { dispatch parsed.opcode; }
Note: Clients should not call this function. Client response parsing is done internally by zbc_parse_response() which only extracts RETN/ERRO.
- Parameters:
out – Receives parsed structure
buf – RIFF buffer to parse
buf_size – Size of buffer
int_size – Guest int size (for decoding PARM/RETN values)
endian – Guest endianness (ZBC_ENDIAN_*)
- Returns:
ZBC_OK on success, error code on parse failure
Example:
zbc_parsed_t parsed;
int rc = zbc_riff_parse_request(&parsed, buf, size, 4, ZBC_ENDIAN_LITTLE);
if (rc == ZBC_OK) {
if (parsed.has_retn) {
/* Use parsed.result, parsed.host_errno */
}
if (parsed.has_erro) {
/* Handle protocol error */
}
}
Helper Functions
-
size_t zbc_strlen(const char *s)
Calculate string length without libc dependency.
- Parameters:
s – Null-terminated string
- Returns:
Length of string (not including null terminator)
-
void zbc_write_native_uint(uint8_t *buf, uintptr_t value, int size, int endianness)
Write an unsigned integer in specified endianness.
- Parameters:
buf – Destination buffer
value – Value to write
size – Size in bytes (1-4)
endianness – ZBC_ENDIAN_LITTLE or ZBC_ENDIAN_BIG
-
intptr_t zbc_read_native_int(const uint8_t *buf, int size, int endianness)
Read a signed integer in specified endianness.
- Parameters:
buf – Source buffer
size – Size in bytes (1-8)
endianness – ZBC_ENDIAN_LITTLE or ZBC_ENDIAN_BIG
- Returns:
Signed integer value
-
uintptr_t zbc_read_native_uint(const uint8_t *buf, int size, int endianness)
Read an unsigned integer in specified endianness.
- Parameters:
buf – Source buffer
size – Size in bytes (1-8)
endianness – ZBC_ENDIAN_LITTLE or ZBC_ENDIAN_BIG
- Returns:
Unsigned integer value
RIFF Writing Functions
-
uint8_t *zbc_riff_begin_chunk(uint8_t *buf, size_t capacity, size_t *offset, uint32_t fourcc)
Begin writing a new RIFF chunk.
Writes the FourCC ID and reserves space for the size field. Returns pointer to the size field so caller can patch it later.
- Parameters:
buf – Buffer to write to
capacity – Total buffer capacity
offset – Current write offset (updated on return)
fourcc – FourCC chunk ID
- Returns:
Pointer to size field for later patching, or NULL if no space
-
void zbc_riff_patch_size(uint8_t *size_ptr, size_t data_size)
Patch the size field of a chunk after writing its data.
- Parameters:
size_ptr – Pointer returned by zbc_riff_begin_chunk()
data_size – Actual size of chunk data
-
int zbc_riff_write_bytes(uint8_t *buf, size_t capacity, size_t *offset, const void *data, size_t size)
Write raw bytes to a RIFF buffer.
- Parameters:
buf – Buffer to write to
capacity – Total buffer capacity
offset – Current write offset (updated on return)
data – Data to write
size – Number of bytes to write
- Returns:
ZBC_OK on success, ZBC_ERR_BUFFER_FULL if no space
-
void zbc_riff_pad(uint8_t *buf, size_t capacity, size_t *offset)
Add padding byte if needed for RIFF word alignment.
RIFF requires chunks to be word-aligned (2-byte boundary).
- Parameters:
buf – Buffer to write to
capacity – Total buffer capacity
offset – Current write offset (updated on return)
-
uint8_t *zbc_riff_begin_container(uint8_t *buf, size_t capacity, size_t *offset, uint32_t form_type)
Begin writing a RIFF container.
Writes “RIFF”, reserves space for size, and writes form type.
- Parameters:
buf – Buffer to write to
capacity – Total buffer capacity
offset – Current write offset (updated on return)
form_type – Form type FourCC (e.g., ZBC_ID_SEMI)
- Returns:
Pointer to size field for later patching, or NULL if no space
-
int zbc_riff_validate_container(const uint8_t *buf, size_t capacity, uint32_t expected_form_type)
Validate a RIFF container header.
- Parameters:
buf – Buffer containing RIFF data
capacity – Total buffer capacity
expected_form_type – Expected form type (e.g., ZBC_ID_SEMI)
- Returns:
ZBC_OK on success, ZBC_ERR_HEADER_OVERFLOW if buffer too small, ZBC_ERR_BAD_RIFF_MAGIC if not “RIFF”, ZBC_ERR_BAD_FORM_TYPE if wrong form
RIFF Reading Functions
-
int zbc_riff_read_header(const uint8_t *buf, size_t capacity, size_t offset, uint32_t *fourcc, uint32_t *size)
Read a RIFF chunk header.
- Parameters:
buf – Buffer to read from
capacity – Total buffer capacity
offset – Offset to chunk header
fourcc – Receives FourCC chunk ID
size – Receives chunk data size
- Returns:
ZBC_OK on success, ZBC_ERR_HEADER_OVERFLOW if not enough data
-
size_t zbc_riff_skip_chunk(const uint8_t *buf, size_t capacity, size_t offset)
Skip past a chunk to the next sibling.
- Parameters:
buf – Buffer containing chunk
capacity – Total buffer capacity
offset – Offset to current chunk header
- Returns:
Offset to next chunk, or capacity if at end
Opcode Table
-
const zbc_opcode_entry_t *zbc_opcode_lookup(int opcode)
Look up an opcode table entry by opcode number.
- Parameters:
opcode – The SH_SYS_* opcode to look up
- Returns:
Pointer to opcode entry, or NULL if not found
-
int zbc_opcode_count(void)
Get the number of entries in the opcode table.
- Returns:
Number of defined opcodes
Byte Manipulation Macros
/* Read/write little-endian 32-bit */
ZBC_WRITE_U32_LE(buf, val)
ZBC_READ_U32_LE(buf)
/* Read/write little-endian 16-bit */
ZBC_WRITE_U16_LE(buf, val)
ZBC_READ_U16_LE(buf)
/* Write FourCC */
ZBC_WRITE_FOURCC(buf, c0, c1, c2, c3)
/* Memory operations (no libc) */
ZBC_MEMCPY(dst, src, n)
ZBC_MEMSET(dst, val, n)
/* Padding to even boundary */
ZBC_PAD_SIZE(size)