spectranext.h
spectranext.h (included in the SDK) exposes the Z80 interface to Spectranext services: controller status, Wi-Fi scan/connect/disconnect, DNS, engine calls (e.g. JSONPath), and direct XFS-to-Spectranext-RAM reads. Everything routes through the single SPECTRANEXT ($3EF0) jump-table entry, selected by register A on entry.
Functions declared here are not linked separately as are provided as part of libspectranet library.
Error convention (assembly): carry set = error; carry clear = success.
Functions
C calling convention: __z88dk_callee (callee cleans stack), except
spectranext_detect and spectranext_get_version, which use
__FASTCALL__.
spectranext_get_version
int8_t spectranext_get_version(char *version_out)
Returns the firmware build identity as a NUL-terminated ASCII string. The
caller supplies SPECTRANEXT_VERSION_SIZE (32) writable bytes outside
controller page B ($2000–$2FFF). Ignore the buffer if the call fails.
| Return | Meaning |
|---|---|
| 0 | Version string written to version_out. |
| -1 | The ROM does not support operation 15, or the call failed. |
- C
- Assembly
#include <spectranext.h>
char version[SPECTRANEXT_VERSION_SIZE];
if (spectranext_get_version(version) == 0) {
// Example: "1.0-12fb395e"
} else {
// Older ROM: the version is unavailable.
}
Operation ID: 15 (SPECTRANEXT_CMD_GET_VERSION).
| Register | Direction | Role |
|---|---|---|
A | in | 15 — get firmware build version |
HL | in | pointer to a writable 32-byte output buffer outside page B |
(HL) | out on success | NUL-terminated version string |
| carry | out | clear on success; set if unsupported or failed |
ld hl, version_buf ; reserve 32 bytes outside $2000-$2FFF
ld a, 15 ; SPECTRANEXT_CMD_GET_VERSION
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD
jr c, .older_rom
; version_buf now contains the NUL-terminated string
The call preserves no output registers or flags other than carry. It does not need a working Wi-Fi connection or issue a controller mailbox command.
The string contains the contents of rp2350/version.txt, a hyphen, and the
build's eight-character Git hash. It has at most 31 visible bytes and no
newline. The shared controller ROM supplies the value on both cartridge
hardware and Fuse; Fuse reports the version bundled with the emulator.
Compare the major.minor prefix when your application needs a release level.
Compare the complete string when it needs one exact build. The hash identifies
a build but does not order builds with the same prefix. A release-level check
depends on version.txt being advanced for that release. This is firmware
identity, not a list of supported API features; the existence of this call
alone does not establish that any later operation is available.
spectranext_detect
Returns whether the cartridge is Spectranext (see Detection):
| Return | Meaning |
|---|---|
| 1 | Spectranext |
| 0 | Classic Spectranet |
| -1 | No compatible interface |
- C
- Assembly
int result = spectranext_detect();
if (result == 1) {
// Spectranext present
}
Detection is not a SPECTRANEXT operation — it probes CTRLREG ($033B) bit 7. See Detection for the full assembly sequence.
ld bc, CTRLREG ; $033B
in a, (c) ; read current value
ld d, a ; save original
and 0x7F ; clear bit 7
out (c), a ; write it
in a, (c) ; read back
bit 7, a ; on Spectranext, written 0 reads as 1
jr z, .not_spectranext
; Spectranext confirmed — restore register
ld a, d
out (c), a
spectranext_get_controller_status
int8_t spectranext_get_controller_status(int8_t *wifi_connection_out, uint32_t *ipv4_out)
Fills Wi-Fi connection state and IPv4 (host byte order).
| Return | Meaning |
|---|---|
| < 0 | ROM/port failure. |
| ≥ 0 | Controller status (compare with WIFI_CONTROLLER_STATUS_*). |
On success, *wifi_connection_out uses WIFI_CONNECT_*; *ipv4_out is the IPv4 address in host order.
- C
- Assembly
uint32_t ipv4;
int8_t wifi_state;
int8_t status = spectranext_get_controller_status(&wifi_state, &ipv4);
if (status >= 0) {
// wifi_state: WIFI_CONNECT_*
// ipv4: address in host order
}
Operation ID: 0 (SPECTRANEXT_CMD_GET_STATUS)
| Register | Direction | Role |
|---|---|---|
A | in | 0 — get controller status |
DE | in | pointer to 4-byte IPv4 output buffer |
B | out | controller status (WIFI_CONTROLLER_STATUS_*) |
C | out | Wi-Fi connection state (WIFI_CONNECT_*) |
(DE..DE+3) | out | IPv4 address, host byte order |
ld de, ipv4_buf ; 4-byte output buffer
ld a, 0 ; SPECTRANEXT_CMD_GET_STATUS (0)
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD — self-paging on Spectranext
jr c, .error
; B = controller status
; C = Wi-Fi connection state
; (ipv4_buf) filled with IPv4 in host order
spectranext_wifi_scan_access_points
int8_t spectranext_wifi_scan_access_points(void)
| Return | Meaning |
|---|---|
| < 0 | Failure. |
| ≥ 0 | Number of networks found. |
- C
- Assembly
int8_t count = spectranext_wifi_scan_access_points();
if (count < 0) {
// error
} else {
// count networks available via spectranext_wifi_get_access_point()
}
Operation ID: 1 (SPECTRANEXT_CMD_WIFI_SCAN_ACCESS_POINTS)
| Register | Direction | Role |
|---|---|---|
A | in | 1 — scan access points |
A | out | number of networks found (carry clear) |
ld a, 1 ; SPECTRANEXT_CMD_WIFI_SCAN_ACCESS_POINTS (1)
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD
jr c, .error
; A = number of networks found
spectranext_wifi_get_access_point
int8_t spectranext_wifi_get_access_point(uint8_t ap, spectranext_wifi_scan_result_t *result)
Copies the extended scan result for index ap (0-based). The result contains a 64-byte NUL-terminated SSID, a 6-byte BSSID, and signed RSSI in dBm. An RSSI of 0 and all-zero BSSID mean that extended metadata was unavailable.
| Return | Meaning |
|---|---|
| 0 | Success. |
| -1 | Failure. |
- C
- Assembly
spectranext_wifi_scan_result_t result;
int8_t r = spectranext_wifi_get_access_point(0, &result);
if (r == 0) {
// result.ssid, result.bssid, and result.rssi are available
}
Operation ID: 2 (SPECTRANEXT_CMD_WIFI_GET_ACCESS_POINT)
| Register | Direction | Role |
|---|---|---|
A | in | 2 — get access point result |
C | in | AP index (0-based) |
DE | in | pointer to output buffer (≥ 71 bytes) |
(DE) | out | SSID [0..63], BSSID [64..69], signed RSSI [70] |
ld c, 0 ; AP index (0-based)
ld de, scan_result ; output buffer, at least 71 bytes
ld a, 2 ; SPECTRANEXT_CMD_WIFI_GET_ACCESS_POINT (2)
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD
jr c, .error
; extended scan result written at (scan_result)
spectranext_wifi_connect_access_point
int8_t spectranext_wifi_connect_access_point(const char *ssid, const char *password,
const uint8_t *bssid)
| Return | Meaning |
|---|---|
| 0 | Success. |
| -1 | Failure. |
- C
- Assembly
int8_t r = spectranext_wifi_connect_access_point(result.ssid, "s3cr3t", result.bssid);
if (r < 0) {
// connection failed
}
Operation ID: 3 (SPECTRANEXT_CMD_WIFI_CONNECT)
| Register | Direction | Role |
|---|---|---|
A | in | 3 — connect |
HL | in | pointer to NUL-terminated SSID |
DE | in | pointer to NUL-terminated password |
BC | in | pointer to 6-byte BSSID, or 0 if unavailable |
ld hl, ssid_str ; NUL-terminated SSID
ld de, pass_str ; NUL-terminated password
ld bc, bssid ; 6-byte BSSID, or 0
ld a, 3 ; SPECTRANEXT_CMD_WIFI_CONNECT (3)
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD
jr c, .error
spectranext_wifi_disconnect
int8_t spectranext_wifi_disconnect(void)
| Return | Meaning |
|---|---|
| 0 | Success. |
| -1 | Failure. |
- C
- Assembly
int8_t r = spectranext_wifi_disconnect();
Operation ID: 4 (SPECTRANEXT_CMD_WIFI_DISCONNECT)
| Register | Direction | Role |
|---|---|---|
A | in | 4 — disconnect |
ld a, 4 ; SPECTRANEXT_CMD_WIFI_DISCONNECT (4)
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD
jr c, .error
spectranext_gethostbyname
int8_t spectranext_gethostbyname(const char *hostname, uint32_t *result_ipv4)
Resolves hostname to an IPv4 address; *result_ipv4 is host byte order.
| Return | Meaning |
|---|---|
| 0 | Success. |
| -1 | Failure (ROM/port or lookup error). |
- C
- Assembly
uint32_t ipv4;
int8_t r = spectranext_gethostbyname("example.com", &ipv4);
if (r == 0) {
// ipv4 contains resolved address in host byte order
}
Operation ID: 5 (SPECTRANEXT_CMD_DNS)
| Register | Direction | Role |
|---|---|---|
A | in | 5 — DNS lookup |
HL | in | pointer to NUL-terminated hostname |
DE | in | pointer to 4-byte IPv4 result buffer |
(DE..DE+3) | out | resolved IPv4, host byte order |
ld hl, hostname_str ; NUL-terminated hostname, e.g. "example.com"
ld de, ipv4_buf ; 4-byte output buffer
ld a, 5 ; SPECTRANEXT_CMD_DNS (5)
ld ix, SPECTRANEXT ; $3EF0
call IXCALL ; $3FFD
jr c, .error
; (ipv4_buf) = resolved IPv4 in host order
spectranext_enginecall
int8_t spectranext_enginecall(const char *input, const char *output, const char *operation)
Runs a named engine call with argv-style arguments in operation (space-separated; see below).
Used to copy mounted files into XFS RAM or run JSONPath and XPath queries.
Input file (input) — a path on the current default XFS mount, or N:path to select mount slot 0…3 explicitly (e.g. 1:data/app.json).
Output file (output) — path on the RAM XFS volume only (no N: prefix); the result is written there.
Operation (operation) — first token is the engine name, such as cp, jsonpath
or xpath (json is also accepted for jsonpath). Further tokens
are engine-specific. See Engine calls for usage and supported
input formats.
spectranext_enginecall("1:example.json", "example.bin", "jsonpath $[*].id $[*].name");
The call waits for completion on controller channel 0. To continue running while an operation executes, submit it through direct memory access on controller channel 1. Channel 0 SDK calls remain available.
| Return | Meaning |
|---|---|
| 0 | Success. |
| < 0 | Failure: negative int8_t error code from the engine (e.g. -1 unknown engine, -2 I/O, -3 JSON parse, -4 JSONPath, -5 non-scalar match, -6 bad argv). |
- C
- Assembly
int8_t r = spectranext_enginecall("0:config.json", "out.bin", "jsonpath $.name");
Operation ID: 6 (SPECTRANEXT_CMD_ENGINECALL)
| Register | Direction | Role |
|---|---|---|
A | in | 6 — engine call |
HL | in | pointer to input path (N:…, NUL-terminated) |
DE | in | pointer to output path on RAM XFS (NUL-terminated) |
BC | in | pointer to operation string (NUL-terminated) |
ld hl, input_spec ; e.g. "1:file.json"
ld de, output_path ; e.g. "out.bin" on RAM
ld bc, op_str ; e.g. "json $[*].id"
ld a, 6 ; SPECTRANEXT_CMD_ENGINECALL
ld ix, SPECTRANEXT
call IXCALL
jr c, .error ; A = error code
spectranext_xfs_read
int32_t spectranext_xfs_read(const spectranext_xfs_read_request_t *request)
Reads a range from an absolute path in the local XFS overlay directly into Spectranext RAM pages. This avoids an intermediate Z80 buffer and is intended for loading a contiguous binary image or data block into page-addressable Spectranext RAM.
The lookup is controller-owned: the path is resolved through the normal local overlay order—RAM, then flash, then ROMFS—not through the caller's currently selected VFS mount. The path must therefore be absolute, for example "/assets/data.bin".
| Return | Meaning |
|---|---|
>= 0 | Actual number of bytes read. A shorter value than maximum_data is end of file. |
-1 | Invalid request, controller failure, or XFS read failure. |
Request structure
typedef struct spectranext_xfs_read_request_s {
char source_filename[128];
uint32_t source_offset;
uint8_t target_first_page;
uint16_t target_first_page_offset;
uint32_t maximum_data;
} spectranext_xfs_read_request_t;
| Field | Meaning |
|---|---|
source_filename | Absolute, NUL-terminated path in the local XFS overlay. Maximum 127 visible characters plus the terminator. |
source_offset | Zero-based byte offset in the source file. |
target_first_page | First destination Spectranext RAM page, in the inclusive range $C0–$DF. |
target_first_page_offset | Offset within that first 4 KiB page, from 0 to $0FFF. |
maximum_data | Maximum number of bytes to copy. The complete destination range must fit from the chosen page/offset through the end of page $DF; requests do not wrap. |
The copy continues across consecutive 4 KiB RAM pages as required. It never modifies the source file. The routine does not change which pages are presently mapped at page A or page B; map the destination page yourself when you want to inspect or execute the loaded data.
- C
- Assembly
#include <spectranext.h>
spectranext_xfs_read_request_t request = {
"/assets/intro.bin", // source_filename
0, // source_offset
0xD8, // target_first_page
0, // target_first_page_offset
4096 // maximum_data
};
int32_t bytes = spectranext_xfs_read(&request);
if (bytes < 0) {
// Controller or XFS failure
} else if (bytes != 4096) {
// Source file ended early
}
// Expose the result at $1000–$1FFF:
setpagea(0xD8);
Operation ID: 8 (CMD_XFS_READ). HL points to the packed 139-byte request structure above. On success, DEHL contains the actual byte count; carry is set on failure.
ld hl, request ; packed spectranext_xfs_read_request_t
ld a, CMD_XFS_READ
ld ix, SPECTRANEXT
call IXCALL
jr c, .error
; DEHL = actual bytes read
Linking (libspectranet vs libspectranet_np)
- libspectranet — Paged build: use when your program already follows the usual Spectranet paging model (see memory).
- libspectranet_np — No-page stubs: often paired with an explicit
pagein()at startup when your code expects Spectranet ROM to be mapped before calling these helpers.
Exact link lines and pragma redirects match your other Spectranet projects (e.g. libterm, ndos). The SDK copies spectranext.h into sdk/include/.
See also
- Syscalls —
SPECTRANEXToperations,IXCALL, register I/O - I/O Ports —
$3400/$3401command/status - Detection —
spectranext_detect - Spectranext module — high-level module development notes
Protected user settings
CMD_SYS_SETTINGS_READ (9) and CMD_SYS_SETTINGS_WRITE (10) use HL as the
payload buffer and BC as capacity or exact length. READ returns BC=16.
The payload contains DivMMC compatibility (0 or 1) at byte 0, with 15 opaque
reserved bytes. Read the complete payload and preserve its other bytes when
editing. Buffers must not overlap page B ($2000–$2fff). Carry/A report errors:
1 unsupported command, 2 insufficient buffer, 3 invalid payload, 4 unsupported
format, 5 old/unrecognized bootloader, 6 busy, 7 storage/staging failure, 8 lockout.
An unsupported READ does not imply native mode.
C callers use spectranext_settings_read(buffer, capacity) and
spectranext_settings_write(buffer, length). READ returns 16, WRITE returns 0,
and failures return negative error codes. spectranext_get_diagnostics(buffer)
returns ten read-only bytes: bootloader version (LE u32), active policy (u8),
lockout reason (LE u32), and supported safety capabilities (u8).
Enabling requires bootloader version 3, safety ABI 1 and its settings/update capabilities. Enabling and disabling both enter limp mode until a complete power cycle. A paged SDK call exits from host RAM through its normal page-out. A no-page caller must explicitly page out from code and stack above 16 KiB after WRITE returns, and must not return into expansion ROM afterwards. Reset cannot clear a lockout; remove Spectrum power and USB, then remove or disable DivMMC before using native mode. These features remain subject to the hardware release checks described in the design document.
.spx upgrade / spectranext_upgrade() requests bootloader mode for the OTA
image staged by the controller's autonomous OTA process. It performs no network
check or download. The bootloader owns file validation and reports missing or
invalid content. Entry requires bootloader revision 7, safety ABI 1 and
explicit-update capability (bit 2), alongside its protected-settings capabilities.
The controller prepares a one-shot PSRAM request and acknowledges it. Completed physical page-out authorizes the request, then it resets the Spectrum and reboots the controller. The application ROM returns through the dot command's normal setup stage; the dot frontend only supplies arguments.
In DivMMC mode the bootloader starts paged out. Run .spx after reset, manually
or from esxDOS startup, to probe $033B, explicitly page in, and call $0004.
That entry runs the full bootloader CRT and launches its checking,
confirmation/progress or error UI. While mapped, it serves the full low 16K.
Native bootloader entry remains automatic. Explicit requests cannot bypass
confirmation through /OTA/SILENT.
SDK calls acknowledge preparation only. Callers use their ordinary high-RAM page-out path and must tolerate a full reset afterward; no fingerprint polling or custom RAM continuation is required. Cancellation/completion releases bus service, resets the Spectrum and restarts the controller with protected settings preserved. Ordinary DivMMC startup without a request stays passive.