Skip to main content

Syscalls

When the Spectranet ROM is paged in, it exposes fixed three-byte jump-table entries from $3E00 to $3EF0. Use the symbols from spectranet.inc rather than literal addresses.

Reaching a vector​

On original Spectranet, PAGEIN ($3FF9) must map the ROM before a raw jump-table call. HLCALL ($3FFA) jumps to the address in HL and IXCALL ($3FFD) jumps to the address in IX; both arrange a return through PAGEOUT ($007C). Spectranext traps an instruction fetch in $3FF8–$3FFF, so call IXCALL and call HLCALL work without an earlier explicit PAGEIN.

ld ix, OPEN
call IXCALL

Full jump table reference ($3E00 … $3EF0)​

Every symbol links to its ABI entry below. Unless stated otherwise, carry set means failure and A contains the error code; success clears carry. Registers not named as outputs are volatile.

AddressSymbolAddressSymbol
$3E00SOCKET$3E03CLOSE
$3E06LISTEN$3E09ACCEPT
$3E0CBIND$3E0FCONNECT
$3E12SEND$3E15RECV
$3E18SENDTO$3E1BRECVFROM
$3E1EPOLL$3E21POLLALL
$3E24POLLFD$3E27GETHOSTBYNAME
$3E2APUTCHAR42$3E2DPRINT42
$3E30CLEAR42$3E33SETPAGEA
$3E36SETPAGEB$3E39LONG2IPSTRING
$3E3CIPSTRING2LONG$3E3FITOA8
$3E42RAND16$3E45REMOTEADDRESS
$3E48IFCONFIG_INET$3E4BIFCONFIG_NETMASK
$3E4EIFCONFIG_GW$3E51INITHW
$3E54GETHWADDR$3E57DECONFIG
$3E5AMAC2STRING$3E5DSTRING2MAC
$3E60ITOH8$3E63HTOI8
$3E66GETKEY$3E69KEYUP
$3E6CINPUTSTRING$3E6FGET_IFCONFIG_INET
$3E72GET_IFCONFIG_NETMASK$3E75GET_IFCONFIG_GW
$3E78SETTRAP$3E7BDISABLETRAP
$3E7EENABLETRAP$3E81PUSHPAGEA
$3E84POPPAGEA$3E87PUSHPAGEB
$3E8APOPPAGEB$3E8DPAGETRAPRETURN
$3E90TRAPRETURN$3E93ADDBASICEXT
$3E96STATEMENT_END$3E99EXIT_SUCCESS
$3E9CPARSE_ERROR$3E9FRESERVEPAGE
$3EA2FREEPAGE$3EA5REPORTERR
$3EA8MOUNT$3EABUMOUNT
$3EAEOPENDIR$3EB1OPEN
$3EB4UNLINK$3EB7MKDIR
$3EBARMDIR$3EBDSIZE
$3EC0FREE$3EC3STAT
$3EC6CHMOD$3EC9READ
$3ECCWRITE$3ECFLSEEK
$3ED2VCLOSE$3ED5VPOLL
$3ED8READDIR$3EDBCLOSEDIR
$3EDECHDIR$3EE1GETCWD
$3EE4RENAME$3EE7SETMOUNTPOINT
$3EEAFREEMOUNTPOINT$3EEDRESALLOC
$3EF0SPECTRANEXT

Socket and network calls​

SOCKET ($3E00)​

Allocates a network socket. Input: B = protocol/type; C = socket flags. Output: A = allocated file descriptor.

CLOSE ($3E03)​

Closes a socket. Input: A = socket file descriptor. Output: none.

LISTEN ($3E06)​

Places a bound TCP socket into listening state. Input: A = socket file descriptor. Output: none.

ACCEPT ($3E09)​

Accepts a pending connection. Input: A = listening socket file descriptor. Output: A = newly allocated connected socket file descriptor.

BIND ($3E0C)​

Binds a local port. Input: A = socket file descriptor; DE = host-order port. Output: none.

CONNECT ($3E0F)​

Connects a socket to a remote endpoint. Input: A = socket file descriptor; DE = pointer to four-byte IPv4 address; BC = host-order port. Output: none.

SEND ($3E12)​

Sends bytes on a stream socket. Input: A = socket file descriptor; DE = source buffer; BC = byte count. Output: BC = bytes sent.

RECV ($3E15)​

Blocks until data is available, then receives bytes. Input: A = socket file descriptor; DE = destination buffer; BC = maximum byte count. Output: BC = bytes received.

SENDTO ($3E18)​

Sends a datagram. Input: A = socket file descriptor; HL = pointer to eight-byte socket-address structure; DE = source buffer; BC = byte count. Output: BC = bytes sent.

RECVFROM ($3E1B)​

Receives a datagram and its peer address. Input: A = socket file descriptor; HL = eight-byte address-result buffer; DE = destination buffer; BC = maximum byte count. Output: BC = bytes received; address structure written through HL.

POLL ($3E1E)​

Checks a list of sockets for an event. Input: DE = file-descriptor array; B = number of entries. Output: A = first ready descriptor and B = event flags, or A = 0 with Z set when none are ready.

POLLALL ($3E21)​

Checks all open sockets. Input: none. Output: A = ready file descriptor and B = event flags, or A = 0 with Z set when none are ready.

POLLFD ($3E24)​

Checks one socket without blocking. Input: A = socket file descriptor. Output: C = event flags when ready; Z set when it is not ready.

GETHOSTBYNAME ($3E27)​

Resolves a hostname with DNS. Input: HL = NUL-terminated hostname; DE = four-byte IPv4 result buffer. Output: IPv4 address written through DE.

UI, conversion, and interface calls​

PUTCHAR42 ($3E2A)​

Writes one character using the 42-column UI. Input: A = character. Output: none.

PRINT42 ($3E2D)​

Writes a NUL-terminated 42-column UI string. Input: HL = string. Output: none.

CLEAR42 ($3E30)​

Clears the Spectranet UI screen. Input: none. Output: none.

SETPAGEA ($3E33)​

Maps a page into page A ($1000–$1FFF). Input: A = page number. Output: none.

SETPAGEB ($3E36)​

Maps a page into page B ($2000–$2FFF). Input: A = page number. Output: none.

LONG2IPSTRING ($3E39)​

Formats a four-byte IPv4 address as dotted decimal. Input: HL = address bytes; DE = destination string buffer. Output: NUL-terminated string through DE.

IPSTRING2LONG ($3E3C)​

Parses dotted-decimal IPv4 text. Input: HL = NUL-terminated string; DE = four-byte result buffer. Output: IPv4 address through DE.

ITOA8 ($3E3F)​

Formats an unsigned byte in decimal. Input: A = value; HL = destination buffer. Output: NUL-terminated decimal text through HL.

RAND16 ($3E42)​

Returns a pseudo-random 16-bit value. Input: none. Output: HL = random value.

REMOTEADDRESS ($3E45)​

Returns a connected socket's peer address. Input: A = socket file descriptor; DE = eight-byte socket-address buffer. Output: address structure through DE.

IFCONFIG_INET ($3E48)​

Sets the interface IPv4 address. Input: DE = four-byte IPv4 address. Output: none.

IFCONFIG_NETMASK ($3E4B)​

Sets the interface netmask. Input: DE = four-byte IPv4 address. Output: none.

IFCONFIG_GW ($3E4E)​

Sets the interface gateway. Input: DE = four-byte IPv4 address. Output: none.

INITHW ($3E51)​

Initialises network hardware. Input: none. Output: none.

GETHWADDR ($3E54)​

Reads the interface MAC address. Input: DE = six-byte destination buffer. Output: MAC address through DE.

DECONFIG ($3E57)​

Clears interface configuration. Input: none. Output: none.

MAC2STRING ($3E5A)​

Formats a six-byte MAC address. Input: HL = MAC bytes; DE = destination string buffer. Output: NUL-terminated text through DE.

STRING2MAC ($3E5D)​

Parses a MAC-address string. Input: HL = NUL-terminated text; DE = six-byte result buffer. Output: MAC bytes through DE.

ITOH8 ($3E60)​

Formats an unsigned byte as two hexadecimal characters. Input: A = value; HL = destination buffer. Output: NUL-terminated text through HL.

HTOI8 ($3E63)​

Parses up to two hexadecimal characters. Input: HL = text. Output: A = byte value.

GETKEY ($3E66)​

Reads a key from the UI input path. Input: none. Output: A = key code.

KEYUP ($3E69)​

Reports key state. Input: none. Output: A = key state.

INPUTSTRING ($3E6C)​

Reads an editable NUL-terminated string. Input: HL = destination buffer; B = maximum length. Output: string through HL.

GET_IFCONFIG_INET ($3E6F)​

Reads the configured IPv4 address. Input: DE = four-byte destination buffer. Output: address through DE.

GET_IFCONFIG_NETMASK ($3E72)​

Reads the configured netmask. Input: DE = four-byte destination buffer. Output: address through DE.

GET_IFCONFIG_GW ($3E75)​

Reads the configured gateway. Input: DE = four-byte destination buffer. Output: address through DE.

Trap, paging, BASIC, and allocation calls​

SETTRAP ($3E78)​

Installs the programmable paging trap. Input: HL = trap routine address; A = trap configuration. Output: none; control transfers to the trap when it fires.

DISABLETRAP ($3E7B)​

Disables the programmable trap. Input: none. Output: none.

ENABLETRAP ($3E7E)​

Enables the programmable trap. Input: none. Output: none.

PUSHPAGEA ($3E81)​

Saves the page-A mapping and maps another page. Input: A = page number. Output: none; matching POPPAGEA consumes the saved page from the stack.

POPPAGEA ($3E84)​

Restores the previous page-A mapping. Input: stack contains the saved page. Output: none.

PUSHPAGEB ($3E87)​

Saves the page-B mapping and maps another page. Input: A = page number. Output: none; matching POPPAGEB consumes the saved page from the stack.

POPPAGEB ($3E8A)​

Restores the previous page-B mapping. Input: stack contains the saved page. Output: none.

PAGETRAPRETURN ($3E8D)​

Completes a page-trap handler and restores saved paging. Input: trap stack/state. Output: no normal register result; returns to trapped code.

TRAPRETURN ($3E90)​

Completes a non-paging trap. Input: trap stack/state. Output: no normal register result; returns to trapped code.

ADDBASICEXT ($3E93)​

Registers a BASIC extension descriptor. Input: HL = extension descriptor. Output: none.

STATEMENT_END ($3E96)​

Validates that a BASIC extension statement has no unwanted trailing text. Input: BASIC parser state. Output: returns only at statement end; otherwise reports a parse error.

EXIT_SUCCESS ($3E99)​

Returns successfully from a BASIC extension. Input: BASIC extension return state. Output: no normal register result; resumes BASIC.

PARSE_ERROR ($3E9C)​

Reports a BASIC parser error and exits the extension. Input: A = BASIC error code. Output: no normal register result.

RESERVEPAGE ($3E9F)​

Reserves a free Spectranet RAM page. Input: none. Output: A = allocated page number.

FREEPAGE ($3EA2)​

Returns a reserved RAM page to the pool. Input: A = page number. Output: none.

REPORTERR ($3EA5)​

Reports a Spectranet error. Input: A = error code. Output: no normal register result.

VFS calls​

The calls below dispatch to the selected VFS mount. Paths are NUL-terminated. Structures and flag values are documented in libspdos.

MOUNT ($3EA8)​

Mounts a filesystem. Input: A = mount point; HL = filesystem-specific mount descriptor. Output: none.

UMOUNT ($3EAB)​

Unmounts a filesystem. Input: A = mount point. Output: none.

OPENDIR ($3EAE)​

Opens a directory. Input: A = mount point; HL = path. Output: A = directory handle.

OPEN ($3EB1)​

Opens a file. Input: A = mount point; HL = path; DE = open flags; BC = mode. Output: A = file handle.

Deletes a file. Input: A = mount point; HL = path. Output: none.

MKDIR ($3EB7)​

Creates a directory. Input: A = mount point; HL = path; BC = mode. Output: none.

RMDIR ($3EBA)​

Removes an empty directory. Input: A = mount point; HL = path. Output: none.

SIZE ($3EBD)​

Returns filesystem capacity. Input: A = mount point. Output: DEHL = 32-bit total size.

FREE ($3EC0)​

Returns free filesystem capacity. Input: A = mount point. Output: DEHL = 32-bit free size.

STAT ($3EC3)​

Retrieves file metadata. Input: A = mount point; HL = path; DE = stat-result buffer. Output: metadata through DE.

CHMOD ($3EC6)​

Changes file mode/attributes. Input: A = mount point; HL = path; DE = mode. Output: none.

For normal VFS mounts, the mode is passed to the mounted filesystem. On Spectranext's local XFS overlay, bit 15 ($8000, decimal 32768) is the commit-to-flash attribute: it copies a RAM-backed file or directory into persistent flash storage and removes the RAM source. This is an XFS-specific extension, not POSIX permission handling, and is valid only for a file that currently lives in the writable RAM layer. See Committing files for the overlay behaviour, examples, and persistence rules.

READ ($3EC9)​

Reads from an open file. Input: A = file handle; DE = destination buffer; BC = maximum byte count. Output: BC = actual bytes read; zero is end of file.

WRITE ($3ECC)​

Writes to an open file. Input: A = file handle; HL = source buffer; BC = byte count. Output: BC = actual bytes written.

LSEEK ($3ECF)​

Changes an open file's position. Input: A = file handle; DEHL = signed 32-bit offset; C = origin (0 set, 1 current, 2 end). Output: DEHL = resulting position.

On HTTP(S) filesystems, seeking is supported when the GET response has a known size strictly below 256 KiB (262,144 bytes). These files are downloaded in full before OPEN completes and retained until VCLOSE. All three seek origins are supported; seeks past EOF clamp to the file size. Empty files also support seeking.

Files of 256 KiB or larger, and responses with an unknown size, use a circular buffer for sequential reads. Consumed bytes are discarded, so LSEEK fails with carry set and A = EINVAL, including attempts to rewind to zero.

VCLOSE ($3ED2)​

Closes an open VFS file. Input: A = file handle. Output: none.

VPOLL ($3ED5)​

Polls a VFS handle. Input: A = file handle. Output: A = availability/status from the mounted filesystem.

READDIR ($3ED8)​

READDIR has three forms, selected by DE and C. All three use A as the directory handle returned by OPENDIR. The directory position is a four-byte, little-endian filesystem cookie; it is deliberately opaque to callers. Do not increment it or construct one yourself—only save a cookie returned by TELLDIR and later provide that exact value to SEEKDIR.

Directory entry read​

Returns the next entry in the open directory stream.

  • Input: A = directory handle; DE = non-zero pointer to the directory-entry buffer.
  • Output: entry data written at DE; carry clear.
  • End of directory: carry set and A = EODIR ($21).

The mounted filesystem defines the entry structure. Its leading field is a NUL-terminated filename; consumers that need metadata should use the VFS entry layout rather than assuming a fixed filename length.

TELLDIR form​

Returns a resumable cookie for the directory's current position. This is not a separate jump-table vector; it is the READDIR position-control form.

  • Input: A = directory handle; DE = 0; HL = non-zero pointer to a writable four-byte cursor; C = 0.
  • Output: the four-byte little-endian cursor is written at HL; carry clear.

The cursor identifies the position after the entries already read. Save all four bytes unchanged. A cookie may be a small ordinal on one filesystem and an arbitrary server-generated value on another.

SEEKDIR form​

Restores a previously saved directory position. This is also a READDIR position-control form.

  • Input: A = directory handle; DE = 0; HL = non-zero pointer to a four-byte cursor previously produced by TELLDIR; C = 1.
  • Output: carry clear; the next directory-entry read continues from that saved position.

C values other than 0 and 1, or an HL value of 0 in either position-control form, fail with EINVAL.

CLOSEDIR ($3EDB)​

Closes a directory handle. Input: A = directory handle. Output: none.

CHDIR ($3EDE)​

Changes a mount's current directory. Input: A = mount point; HL = path. Output: none.

GETCWD ($3EE1)​

Returns a mount's current directory. Input: A = mount point; HL = destination buffer. Output: NUL-terminated path through HL.

RENAME ($3EE4)​

Renames a path within a mount. Input: A = mount point; HL = old path; DE = new path. Output: none.

SETMOUNTPOINT ($3EE7)​

Selects the current VFS mount. Input: A = mount point. Output: none.

FREEMOUNTPOINT ($3EEA)​

Finds a free VFS mount point. Input: none. Output: A = free mount point.

RESALLOC ($3EED)​

Allocates or frees internal VFS resources. Input: C = resource operation; A = handle for a free operation. Output: A = allocated handle or HL = resource pointer, depending on operation.

Spectranext controller call​

SPECTRANEXT ($3EF0)​

Spectranext's controller-operation multiplexer. Input: A = operation ID; other registers depend on the operation. Output: carry clear on controller success; operation-specific results below.

AOperationAdditional input registersOutput registers / memory
0Get controller statusDE = four-byte IPv4 bufferB = controller status; C = Wi-Fi state; IPv4 through DE
1Scan Wi-Fi APs—A = number of networks
2Read scanned AP nameC = AP index; DE = name bufferNUL-terminated name through DE
3Connect Wi-FiHL = SSID; DE = passwordnone
4Disconnect Wi-Fi—none
5DNS hostname lookupHL = hostname; DE = four-byte IPv4 bufferIPv4 through DE
6Engine callHL = input; DE = output; BC = operation stringcontroller-defined result in A
7Get controller messageDE = destination bufferNUL-terminated message through DE
8XFS readHL = XFS_READ request descriptorDEHL = bytes read
9Read protected user settingsHL = buffer outside page B; BC = capacity16-byte payload through HL; BC = 16; carry/A report errors
10Write protected user settingsHL = 16-byte payload outside page B; BC = 16carry/A report errors
11Read system diagnosticsHL = writable 10-byte bufferDiagnostics through HL: bootloader version, active policy, lockout reason, safety capabilities
12Request staged upgrade—carry/A report errors; successful preparation takes effect after page-out
13Internal .spx upgrade handoffHL = $8080 handoff blockCarry set if handled; A = result
14Read USB gamepad statusHL = writable 8-byte bufferState, enabled flag, Kempston bits, device address, VID, PID through HL
15Get firmware build versionHL = writable 32-byte buffer outside page BNUL-terminated version string through HL

Operation 13 in this table is used by ROM startup for the .spx upgrade command. It is not a public SPECTRANEXT operation for applications.

XFS_READ (operation 8)​

Reads a bounded range of a file through the controller's default XFS overlay into controller RAM pages. Input: HL points to this packed, little-endian 139-byte request:

OffsetSizeField
0128source_filename: NUL-terminated absolute path (up to 127 characters)
1284source_offset: byte offset in the source file
1321target_first_page: first target RAM page ($C0–$DF)
1332target_first_page_offset: offset within that page ($0000–$0FFF)
1354maximum_data: maximum number of bytes to read

Output: carry clear and DEHL contains the actual number of bytes read. Carry set indicates an invalid request or an XFS open, seek, read, or close failure. A short read at end of file succeeds. The complete target range must fit within pages $C0–$DF; requests that would run past $DF fail without reading.

Constants and C wrappers are declared in spectranext.h. For the underlying $3400/$3401 mailbox protocol, see I/O Ports.

Controller channels​

Spectranext provides two controller channels on both hardware and FuseX. Each channel has its own request workspace, command register and completion status. Both accept the same commands and payload layouts.

The SDK's C functions and ROM calls use channel 0 and wait for completion. Channel 1 is available through direct controller memory access. Submit a request there, continue your program, and check its status when convenient. You can use the normal SDK on channel 0 while channel 1 is working.

For example, channel 1 can download a file with cp while channel 0 performs spectranext_xfs_read() on a previously completed file. Engine calls share resources and execute one at a time, so submitting an engine call on each channel does not make the two engines run simultaneously.

Memory layout​

Map controller page $48 into Spectranet page B ($2000–$2FFF) with SETPAGEB. The addresses below assume that mapping. Keep your program and source strings outside page B, and ensure the controller is mapped whenever you access its workspace or registers.

AreaChannel 0Channel 1
Workspace$2800–$2BFB$2C00–$2FFB
Command$2FFE$2FFC
Status$2FFF$2FFD

Each workspace holds 1,020 bytes. $2BFC–$2BFF are reserved. The existing channel 0 register addresses and SDK interface are preserved.

Submit, continue, poll​

  1. Use a channel only after its previous operation has finished and you have consumed its result.
  2. Write the command's input fields into that channel's workspace.
  3. Write $FF to its status register, then write the command number to its command register. The status stays $FF while the request is pending or running.
  4. Continue computing, rendering, or making channel 0 SDK calls.
  5. Poll the submitted channel's status once when you need an update. If it is still $FF, return to your other work.
  6. When status changes, read the result from that channel's workspace before reusing it.
StatusMeaning
$FFPending or running. Leave this channel's workspace and registers alone.
0Completed successfully; response fields are ready.
1Completed with an error; inspect the command's result fields.

The controller resets the command register to $FF when it accepts a request. Poll status for completion, rather than the command register. There is no completion interrupt; your program chooses when to check.

Example: copy on channel 1​

An engine call uses command 6, with these workspace fields:

FieldOffsetChannel 0Channel 1Format
Input path$000$2800$2C00NUL-terminated string, at most 127 characters
Output path$080$2880$2C80NUL-terminated string, at most 127 characters
Operation$100$2900$2D00NUL-terminated string, at most 255 characters
Result$200$2A00$2E00Signed byte: 0 success, negative error

The following routine assumes controller page $48 is already mapped in page B and this program owns channel 1. It returns immediately after submission; carry is set if channel 1 is still busy.

start_copy:
ld a,($2ffd)
cp $ff
jr z,copy_busy

ld hl,input_path
ld de,$2c00
call copy_string
ld hl,output_path
ld de,$2c80
call copy_string
ld hl,operation
ld de,$2d00
call copy_string

ld a,$ff
ld ($2ffd),a ; pending
ld a,6 ; ENGINECALL
ld ($2ffc),a ; submit
or a ; carry clear
ret

copy_busy:
scf
ret

copy_string:
ld a,(hl)
ld (de),a
inc hl
inc de
or a
jr nz,copy_string
ret

input_path: defb "3:assets.bin",0
output_path: defb "assets-cache.bin",0
operation: defb "cp",0

On later iterations, read $2FFD. While it is $FF, keep doing other work. Once it becomes 0 or 1, read the signed engine result at $2E00. Only a successful completion makes assets-cache.bin ready to consume. The same submission sequence works for other engines by changing operation.

Other operations and shared memory​

Use the same command numbers and workspace offsets on either channel. For example, command 8 (XFS_READ) has this packed layout. All multi-byte values are little-endian.

FieldWorkspace offsetFormat
Source filename$000NUL-terminated path, up to 127 characters
Source file offset$08032-bit byte offset
First destination RAM page$0848-bit page number
Offset in that page$08516-bit byte offset
Maximum bytes to read$08732-bit byte count
Bytes read, after completion$08B32-bit byte count

For channel 1, add each offset to $2C00. Its source path follows the same local-overlay lookup rules as the SDK call. After a successful read, inspect bytes_read; EOF can produce a short read or zero bytes.

The channels have separate workspaces but share files and destination RAM. Give simultaneous reads separate destination regions, and wait for a copy to finish before reading its output. If an XFS_READ targets controller page $48, its destination must fit inside its own workspace after the first 256 bytes: $2900–$2BFB for channel 0, or $2D00–$2FFB for channel 1.

See also​

  • I/O Ports — controller mailbox and operation IDs
  • Memory map — paging and PAGEIN / PAGEOUT
  • libspdos — VFS structures, flags, and C wrappers