API¶
Application Programming Interface
Introduction¶
The API is how a 6502 program uses the host. The host is the modern side
of the machine behind the registers of the RIA, such as the Pico
2 of an RP6502-PICO or the computer that runs the RP6502-EMU. A program
calls a function such as open() or time(), and the C library
passes the call through the RIA registers to the host. The host does the
work and passes the result back, much like a remote procedure call. The
host runs outside the 6502 and uses none of its RAM, so all of RAM
remains available to the program.
The API comes with the compilers. It is part of the C libraries of cc65 and llvm-mos, so a program needs nothing else to use it. This is the “Hello, world!” program of the SDK template:
#include <rp6502.h>
#include <stdio.h>
#include "xram.h"
int main(void)
{
puts("Hello, world!");
}
puts() is the standard C function. In both compilers it ends in
WRITE_XSTACK, an API call. The C library pushes the bytes to the
extended stack (XSTACK), a 512-byte stack in the RIA, and the host
writes them to the console. Most of the API is standard C in this way:
fcntl.h, unistd.h, and stdio.h for the console and files,
and time.h for the clock. rp6502.h adds what standard C has no
call for: XRAM, the RIA attributes, the FAT
attributes and dates of files, directories, drives, the line editor, and
the start of another ROM.
The API has no calls for the video, the sound, or the state of the
keyboard, mouse, and gamepads. A program maps each of those devices to
an XRAM address with the device’s macro, as XRAM Memory Map in the SDK shows. Then the program reads
and writes the XRAM of the device with the XRAM functions of
rp6502.h. The RIA and VGA pages describe each
device.
Under the C functions is the Application Binary Interface (ABI), the register-level form of each call. The C library implements the ABI, so a C program needs it only to make an API call itself. Application Binary Interface describes it for assembly programs, and for a C program that makes API calls itself.
Application Programming Interface¶
Most calls behave as their POSIX equivalents do. The calls for files and
directories follow FAT, the standard filesystem of USB drives and memory
cards, so f_stat() replaces stat() and returns the attributes and
dates of FAT.
Each section below has one or more C declarations, a description, and a list of fields. Most declarations are both a C function and the ABI form of an API call. A few API calls take their arguments in a different order in the ABI than in C, so their sections have one declaration of each form. A flag before the return type marks a declaration that has only one of the two forms:
ABI marks a declaration of the ABI form only. lib marks a
function of the C library only.
The fields are:
Operationis the operation ID that a program writes to the RIA registerRIA_OPto make the API call. A section with noOperationfield describes functions that make no API call of their own.C protois the header that declares the C function.a regslists the arguments that a program may pass inRIA_Aalone, withRIA_Xunset, andreturnwhen the return value fits inRIA_A.errnolists every errno that the section can set, from the API call or from the C library. Errno Translations gives the number of each.
The Operation and a regs fields are for the ABI. A C program uses
them only when it makes API calls itself.
Extended Memory¶
Extended RAM (XRAM) is 64 KB of memory outside the address space of the
6502. A program reads and writes it through the two XRAM portals of the RIA. Each portal has its own address and step
registers, so the two portals can point at different places in XRAM. The
functions whose names start with xram0_ use portal 0, and the
functions whose names start with xram1_ use portal 1. Each function
changes the registers of its portal.
XRAM_READ¶
-
lib void xram0_read(void *dest, unsigned src, unsigned count)¶
-
lib void xram1_read(void *dest, unsigned src, unsigned count)¶
xram0_read()andxram1_read()copycountbytes from XRAM to 6502 RAM, asmemcpy()does.- C proto:
rp6502.h
- Parameters:
dest – Destination in 6502 RAM.
src – Source address in XRAM.
count – Number of bytes to copy.
XRAM_WRITE¶
-
lib void xram0_write(unsigned dest, const void *src, unsigned count)¶
-
lib void xram1_write(unsigned dest, const void *src, unsigned count)¶
xram0_write()andxram1_write()copycountbytes from 6502 RAM to XRAM, asmemcpy()does.- C proto:
rp6502.h
- Parameters:
dest – Destination address in XRAM.
src – Source in 6502 RAM.
count – Number of bytes to copy.
XRAM_SET¶
-
lib void xram0_set(unsigned dest, unsigned char val, unsigned count)¶
-
lib void xram1_set(unsigned dest, unsigned char val, unsigned count)¶
xram0_set()andxram1_set()fillcountbytes of XRAM withval, asmemset()does.- C proto:
rp6502.h
- Parameters:
dest – Address in XRAM to fill.
val – Byte to write to every address.
count – Number of bytes to fill.
XRAM_MOVE¶
-
lib void xram_move(unsigned dest, unsigned src, unsigned count)¶
xram_move()copiescountbytes from one XRAM address to another, asmemmove()does, so the result is correct when the two ranges overlap. It uses both portals, so the copy is fast and the data does not pass through 6502 RAM.- C proto:
rp6502.h
- Parameters:
dest – Destination address in XRAM.
src – Source address in XRAM.
count – Number of bytes to copy.
XRAM_PEEK¶
-
lib unsigned char xram0_peek8(unsigned addr)¶
-
lib unsigned char xram1_peek8(unsigned addr)¶
-
lib unsigned xram0_peek16(unsigned addr)¶
-
lib unsigned xram1_peek16(unsigned addr)¶
xram0_peek8()andxram1_peek8()read one byte from XRAM.xram0_peek16()andxram1_peek16()read a 16-bit value.- C proto:
rp6502.h
- Parameters:
addr – Address in XRAM to read.
- Returns:
The value read.
XRAM_POKE¶
-
lib void xram0_poke8(unsigned addr, unsigned char val)¶
-
lib void xram1_poke8(unsigned addr, unsigned char val)¶
-
lib void xram0_poke16(unsigned addr, unsigned val)¶
-
lib void xram1_poke16(unsigned addr, unsigned val)¶
xram0_poke8()andxram1_poke8()write one byte to XRAM.xram0_poke16()andxram1_poke16()write a 16-bit value.- C proto:
rp6502.h
- Parameters:
addr – Address in XRAM to write.
val – Value to write.
XREG¶
-
int xreg(char device, char channel, unsigned char index, ...)¶
xreg()configures the peripherals of the Peripheral Information Exchange (PIX). A program uses it to map devices, such as the keyboard and the video modes, to XRAM. Then the program uses each device through its XRAM, with the XRAM functions. The section of each device on the RIA and VGA pages gives its registers and its macro.You are unlikely to call
xreg()yourself. The code block of each device has a macro that makes the call for you, such asxreg_ria_keyboard(), and the macro is easier to read. Only someone who experiments with a custom device on the PIX bus callsxreg()directly.The manual writes the address of an extended register (XREG) as $device:channel:index. These are the first three arguments of
xreg():device,channel, andindex. Each value after them goes to the next register in sequence, fromindexup. This call sets $1:0:01 to 3, $1:0:02 to 2, and $1:0:03 to $FF00:xreg(0x1, 0x0, 0x01, 3, 2, 0xFF00);
When a program maps a device, the device can refuse a value, and the call fails. A refusal usually means that the value is not valid, but it can also mean that the machine does not have the feature.
An assembly program pushes the device, the channel, and the index, one byte each, and then each value, high byte first.
RIA_AandRIA_Xcarry no argument.- Operation:
RIA_OP_XREG 0x01
- C proto:
rp6502.h
- Parameters:
device – PIX device ID. 0:RIA, 1:VGA, 2-6:unassigned
channel – PIX channel. 0-15
index – Index of the register in the channel. 0-255
... – 16-bit values to set, from
indexup.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO
Process¶
ARGV¶
-
ABI int _argv(char *argv, int size)¶
The C runtime runs this operation before
main()to getargcandargv. The argv data can use up to 512 bytes of RAM, soargcandargvare opt-in. To get them inmain(), define__argv_mem()in any source file of the program, such asmain.c:void *__argv_mem(size_t size) { return malloc(size); }
The C runtime calls
__argv_mem()with the size of the argv data.__argv_mem()returns storage of that size, in static memory or frommalloc(). A program can free the memory frommalloc()after it reads the arguments.__argv_mem()can return NULL to reject an argv that is too large. With no__argv_mem(), or when it returns NULL,main()gets anargcof 0.The operation returns the argv data on the XSTACK: a table of 16-bit offsets that ends with 0, then the zero-terminated strings. Each offset is the position of a string from the start of the data. The data is always valid. For example, [“ABC”, “DEF”] is:
06 00 0A 00 00 00 41 42 43 00 44 45 46 00
- Operation:
RIA_OP_ARGV 0x08
- Returns:
Size of the argv data. -1 on error.
- Errno:
EINVAL
EXEC¶
-
ABI int _exec(const char *argv, int size)¶
-
lib int ria_execl(const char *path, ...)¶
-
lib int ria_execv(const char *path, char *const argv[])¶
ria_execl()andria_execv()start another ROM.pathis the path of the ROM. The arguments of the new ROM are the strings afterpathinria_execl(), or the arrayargvinria_execv(), and each list ends with NULL. Unlike the POSIXexecl()andexecv(), the arguments do not repeat the path, which saves RAM. In the new ROM,argv[0]is the absolute path of the ROM, with its drive, or:NAMEfor a ROM on the null drive, as Installed ROMs describes. The arguments follow it, so the first argument of the call,argv[0]ofria_execv(), becomesargv[1]in the new ROM.After a successful call, the RIA stops the 6502, loads the new ROM, and starts it. When the ROM does not load, the 6502 stays stopped, and the display shows the console, with its text and the error. An RP6502-PICO returns to its monitor. The RP6502-EMU exits, unless it runs with
--debug. The other machines stay stopped. The ROM does not load when the file is missing or is not a valid ROM, when its absolute path is longer than 255 bytes, or when the argv data with that path does not fit in 512 bytes.To run again when the new ROM exits, a ROM first registers as the Launcher.
ria_execl()andria_execv()take at most 16 strings, the path and up to 15 arguments, in at most 512 bytes with the offset table. A call over either limit returns -1 with EINVAL.The EXEC data has the same layout as the ARGV data, with at least one string. With the terminator of its last string, it has at most 512 bytes. Each string starts after the zero of the string before it, in the order of the offset table. The path in
argv[0]has at most 255 bytes. The RIA checks the layout and the length of the path, but does not check that the path is a ROM that loads. When a check fails, the call fails with EINVAL.- Operation:
RIA_OP_EXEC 0x09
- C proto:
rp6502.h
- Returns:
Does not return on success. -1 on error.
- Errno:
EINVAL
EXIT¶
-
void exit(int status)¶
exit()ends the program and does not return. A return frommain()also ends the program, with the return value as the exit code. The RIA stops the 6502 before it runs another instruction, and keeps the exit code for the next ROM, which reads it fromRIA_ATTR_EXIT_CODE.When another ROM is the launcher, that ROM runs again, as Launcher describes. Otherwise, an RP6502-PICO returns to its monitor, and the RP6502-EMU exits with the same exit code, unless it runs with
--debug. The other machines stay stopped.- Operation:
RIA_OP_EXIT 0xFF
- C proto:
stdlib.h
- Parameters:
status – The exit code. 0 is success, and any other value is an error.
Files¶
OPEN¶
-
int open(const char *path, int oflag)¶
open()opens a file or a device stream, and returns a file descriptor.oflagis the options below, ORed together. It must include O_RDONLY, O_WRONLY, or O_RDWR, or the call fails with EINVAL. O_RDONLY is 0x01, not 0 as on most POSIX systems, soopen(path, 0)fails.0x01
O_RDONLY
Open for reading only.
0x02
O_WRONLY
Open for writing only.
0x03
O_RDWR
Open for reading and writing.
0x10
O_CREAT
Create the file when it does not exist.
0x20
O_TRUNC
With O_WRONLY or O_RDWR, set the length of the file to 0.
0x40
O_APPEND
Set the read/write position to the end of the file once, at the open, not before each write.
0x80
O_EXCL
With O_CREAT, fail when the file exists.
- Operation:
RIA_OP_OPEN 0x14
- C proto:
fcntl.h
- Parameters:
path – Path of the file or the device stream.
oflag – Options, ORed together.
- Returns:
File descriptor. -1 on error.
- A regs:
return, oflag
- Errno:
EACCES, EBUSY, EEXIST, EINVAL, EIO, EMFILE, ENODEV, ENOENT, ENOMEM, ENOSPC
CLOSE¶
-
int close(int fildes)¶
close()closes a file descriptor.- Operation:
RIA_OP_CLOSE 0x15
- C proto:
fcntl.h
- Parameters:
fildes – File descriptor from open().
- Returns:
0 on success. -1 on error.
- A regs:
return, fildes
- Errno:
EBADF, EIO, ENOSPC
READ¶
-
lib int read(int fildes, void *buf, unsigned count)¶
read()reads up tocountbytes from a file intobuf. It is a C library function that calls READ_XSTACK as many times as it needs to.- C proto:
unistd.h
- Parameters:
fildes – File descriptor from open().
buf – Destination of the data.
count – Number of bytes to read. 0x7FFF max.
- Returns:
The number of bytes read. -1 on error.
- A regs:
fildes
- Errno:
EAGAIN, EBADF, EBUSY, EINTR, EINVAL, EIO
READ_XSTACK¶
-
int read_xstack(void *buf, unsigned count, int fildes)¶
read_xstack()reads up tocountbytes from a file intobuf, through the XSTACK.- Operation:
RIA_OP_READ_XSTACK 0x16
- C proto:
rp6502.h
- Parameters:
buf – Destination of the data.
count – Number of bytes to read. 0x200 max.
fildes – File descriptor from open().
- Returns:
The number of bytes read. -1 on error.
- A regs:
fildes
- Errno:
EAGAIN, EBADF, EBUSY, EINTR, EINVAL, EIO
READ_XRAM¶
-
int read_xram(unsigned xaddr, unsigned count, int fildes)¶
read_xram()reads up tocountbytes from a file into XRAM atxaddr.- Operation:
RIA_OP_READ_XRAM 0x17
- C proto:
rp6502.h
- Parameters:
xaddr – Destination address in XRAM.
count – Number of bytes to read. 0x7FFF max.
fildes – File descriptor from open().
- Returns:
The number of bytes read. -1 on error.
- A regs:
fildes
- Errno:
EAGAIN, EBADF, EBUSY, EINTR, EINVAL, EIO
WRITE¶
-
lib int write(int fildes, const void *buf, unsigned count)¶
write()writescountbytes frombufto a file. It is a C library function that calls WRITE_XSTACK as many times as it needs to.- C proto:
unistd.h
- Parameters:
fildes – File descriptor from open().
buf – Source of the data.
count – Number of bytes to write. 0x7FFF max.
- Returns:
The number of bytes written. -1 on error.
- A regs:
fildes
- Errno:
EAGAIN, EBADF, EBUSY, EINTR, EINVAL, EIO, ENOSPC
WRITE_XSTACK¶
-
int write_xstack(const void *buf, unsigned count, int fildes)¶
write_xstack()writescountbytes frombufto a file, through the XSTACK.- Operation:
RIA_OP_WRITE_XSTACK 0x18
- C proto:
rp6502.h
- Parameters:
buf – Source of the data.
count – Number of bytes to write. 0x200 max.
fildes – File descriptor from open().
- Returns:
The number of bytes written. -1 on error.
- A regs:
fildes
- Errno:
EAGAIN, EBADF, EBUSY, EINTR, EINVAL, EIO, ENOSPC
WRITE_XRAM¶
-
int write_xram(unsigned xaddr, unsigned count, int fildes)¶
write_xram()writescountbytes from XRAM atxaddrto a file.- Operation:
RIA_OP_WRITE_XRAM 0x19
- C proto:
rp6502.h
- Parameters:
xaddr – Source address in XRAM.
count – Number of bytes to write. 0x7FFF max.
fildes – File descriptor from open().
- Returns:
The number of bytes written. -1 on error.
- A regs:
fildes
- Errno:
EAGAIN, EBADF, EBUSY, EINTR, EINVAL, EIO, ENOSPC
LSEEK¶
-
long f_lseek(long offset, char whence, int fildes)¶
-
lib off_t lseek(int fildes, off_t offset, int whence)¶
lseek()andf_lseek()move the read/write position of a file.f_lseek(0, SEEK_CUR, fd)returns the current position.A seek past the end of a file that is open for writing extends the file with zeros. On a full drive, that seek fails with ENOSPC and does not change the file or the position. A seek past the end of a file that is open only for reading stops at the end.
cc65 and llvm-mos give SEEK_SET, SEEK_CUR, and SEEK_END different values, so each compiler has its own operation ID:
RIA_OP_LSEEK_LLVM
RIA_OP_LSEEK_CC65
RIA_OP_LSEEK
0x1D
0x1A
SEEK_SET
0
2
SEEK_CUR
1
0
SEEK_END
2
1
- Operation:
See the table above.
- C proto:
f_lseek: rp6502.h, lseek: unistd.h
- Parameters:
offset – Distance in bytes from the position that
whenceselects.whence – SEEK_SET, SEEK_CUR, or SEEK_END.
fildes – File descriptor from open().
- Returns:
The new read/write position. -1 on error. A position past 0x7FFFFFFF does not fit in the returned long, so that seek fails with ERANGE and does not change the position.
- A regs:
fildes
- Errno:
EBADF, EINVAL, EIO, ENOSPC, ERANGE, ESPIPE
SYNCFS¶
-
int syncfs(int fildes)¶
syncfs()flushes the pending writes of a file descriptor to storage.- Operation:
RIA_OP_SYNCFS 0x1E
- C proto:
unistd.h
- Parameters:
fildes – File descriptor from open().
- Returns:
0 on success. -1 on error.
- A regs:
return, fildes
- Errno:
EACCES, EBADF, EINVAL, EIO, ENOSPC
ISATTY¶
-
lib int isatty(int fildes)¶
isatty()returns 1 when a file descriptor is the console: stdin, stdout, stderr,CON:, orTTY:.- C proto:
unistd.h
- Parameters:
fildes – File descriptor.
- Returns:
1 for the console. 0 for any other file descriptor.
Paths¶
STAT¶
-
int f_stat(const char *path, f_stat_t *dirent)¶
f_stat()returns the directory entry of a file or a directory. For the root of a drive, such as/orMSC0:/, it returns a directory named/, with size 0 and zero dates.- Operation:
RIA_OP_STAT 0x1F
- C proto:
rp6502.h
- Parameters:
path – Path of a file or a directory.
dirent – The returned f_stat_t data.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
REMOVE¶
-
int remove(const char *name)¶
remove()removes a file or an empty directory. It is the lightest of the three calls that remove an entry, because it does not test the type of the entry first. The removal fails with EACCES on a read-only file.- Operation:
RIA_OP_REMOVE 0x1B
- C proto:
stdio.h
- Parameters:
name – Path of the file or the directory to remove.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EBUSY, EEXIST, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
UNLINK¶
-
lib int unlink(const char *name)¶
unlink()removes a file. It makes two API calls: STAT tests that the entry is not a directory, and REMOVE removes it.unlink()fails with EACCES on a directory, and on a read-only file.- C proto:
unistd.h
- Parameters:
name – Path of the file to remove.
- Returns:
0 on success. -1 on error.
- Errno:
EACCES, EBUSY, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
RMDIR¶
-
lib int rmdir(const char *name)¶
rmdir()removes an empty directory. It makes two API calls: STAT tests that the entry is a directory, and REMOVE removes it.rmdir()fails with EACCES on a file.- C proto:
unistd.h
- Parameters:
name – Path of the directory to remove.
- Returns:
0 on success. -1 on error.
- Errno:
EACCES, EBUSY, EEXIST, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
RENAME¶
-
int rename(const char *oldname, const char *newname)¶
rename()renames or moves a file or a directory on its drive. A replacement of a read-only file, or of a file by a directory or a directory by a file, fails with EACCES. A move to another drive fails with ENODEV.- Operation:
RIA_OP_RENAME 0x1C
- C proto:
stdio.h
- Parameters:
oldname – Path of the file or directory to rename.
newname – New path.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EBUSY, EEXIST, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSPC, ENOSYS
CHMOD¶
-
int f_chmod(const char *path, unsigned char attr, unsigned char mask)¶
f_chmod()changes the attributes of a file or a directory, the bits offattribin its directory entry. It changes only the attributes whose bits are set inmask, and it does not change the Directory attribute.- Operation:
RIA_OP_CHMOD 0x26
- C proto:
rp6502.h
- Parameters:
path – Path of a file or a directory.
attr – New attributes.
mask – Attributes to change.
- Returns:
0 on success. -1 on error.
- A regs:
return, mask
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
UTIME¶
-
int f_utime(const char *path, unsigned fdate, unsigned ftime, unsigned crdate, unsigned crtime)¶
f_utime()sets the dates and times of a file or a directory, in the format of its directory entry. Anfdateof 0 keeps the modification date and time, and acrdateof 0 keeps the creation date and time.- Operation:
RIA_OP_UTIME 0x27
- C proto:
rp6502.h
- Parameters:
path – Path of a file or a directory.
fdate – Modification date.
ftime – Modification time.
crdate – Creation date.
crtime – Creation time.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
MKDIR¶
-
int f_mkdir(const char *name)¶
f_mkdir()creates a directory.- Operation:
RIA_OP_MKDIR 0x28
- C proto:
rp6502.h
- Parameters:
name – Path of the directory to make.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EEXIST, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSPC, ENOSYS
Directories¶
OPENDIR¶
-
int f_opendir(const char *name)¶
f_opendir()opens a directory and returns a directory descriptor for it.- Operation:
RIA_OP_OPENDIR 0x20
- C proto:
rp6502.h
- Parameters:
name – Path of a directory.
- Returns:
Directory descriptor. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO, EMFILE, ENODEV, ENOENT, ENOMEM, ENOSYS
READDIR¶
-
int f_readdir(f_stat_t *dirent, int dirdes)¶
f_readdir()returns the next directory entry of a directory. At the end of the directory, it succeeds with an emptyfname.- Operation:
RIA_OP_READDIR 0x21
- C proto:
rp6502.h
- Parameters:
dirent – The returned f_stat_t data.
dirdes – Directory descriptor from f_opendir().
- Returns:
0 on success. -1 on error.
- A regs:
return, dirdes
- Errno:
EACCES, EBADF, EINVAL, EIO, ENOENT, ENOMEM, ENOSYS
CLOSEDIR¶
-
int f_closedir(int dirdes)¶
f_closedir()closes a directory descriptor.- Operation:
RIA_OP_CLOSEDIR 0x22
- C proto:
rp6502.h
- Parameters:
dirdes – Directory descriptor from f_opendir().
- Returns:
0 on success. -1 on error.
- A regs:
return, dirdes
- Errno:
EBADF, EINVAL, ENOSYS
TELLDIR¶
-
long f_telldir(int dirdes)¶
f_telldir()returns the read position of a directory descriptor, as a location forf_seekdir().- Operation:
RIA_OP_TELLDIR 0x23
- C proto:
rp6502.h
- Parameters:
dirdes – Directory descriptor from f_opendir().
- Returns:
Location. -1 on error.
- A regs:
dirdes
- Errno:
EBADF, EINVAL, ENOSYS
SEEKDIR¶
-
int f_seekdir(long loc, int dirdes)¶
f_seekdir()restores the read position of a directory descriptor to a location fromf_telldir().- Operation:
RIA_OP_SEEKDIR 0x24
- C proto:
rp6502.h
- Parameters:
loc – Location from f_telldir().
dirdes – Directory descriptor from f_opendir().
- Returns:
0 on success. -1 on error.
- A regs:
return, dirdes
- Errno:
EACCES, EBADF, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
REWINDDIR¶
-
int f_rewinddir(int dirdes)¶
f_rewinddir()sets the read position of a directory descriptor to the start of the directory.- Operation:
RIA_OP_REWINDDIR 0x25
- C proto:
rp6502.h
- Parameters:
dirdes – Directory descriptor from f_opendir().
- Returns:
0 on success. -1 on error.
- A regs:
return, dirdes
- Errno:
EACCES, EBADF, EINVAL, EIO, ENODEV, ENOENT, ENOSYS
Drives¶
CHDIR¶
-
int chdir(const char *name)¶
chdir()changes the current directory. On a host with named drives, it also changes the drive.- Operation:
RIA_OP_CHDIR 0x29
- C proto:
unistd.h
- Parameters:
name – Path of the directory to make current.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
GETCWD¶
-
ABI int _getcwd(char *buf, int size)¶
-
lib char *getcwd(char *buf, size_t size)¶
getcwd()returns the current directory, as an absolute path that starts with its drive, such asMSC0:/games. When the path is longer than 255 bytes, as it can be in the directory that the RP6502-EMU was started from,getcwd()fails with ERANGE.- Operation:
RIA_OP_GETCWD 0x2B
- C proto:
unistd.h
- Parameters:
buf – The returned path.
size – Size of
buf. The API call does not take it.
- Returns:
buf. NULL on error.- A regs:
return
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ERANGE
GETLABEL¶
-
int f_getlabel(const char *drive, char *label)¶
f_getlabel()returns the volume label of a drive.labelmust hold 12 bytes, 11 and the terminator. Only an RP6502-PICO has volume labels.- Operation:
RIA_OP_GETLABEL 0x2D
- C proto:
rp6502.h
- Parameters:
drive – Device name of the drive, such as
MSC0:.label – The returned label.
- Returns:
Length of the returned label. -1 on error.
- A regs:
return
- Errno:
EINVAL, EIO, ENODEV, ENOSYS
SETLABEL¶
-
int f_setlabel(const char *name)¶
f_setlabel()changes the volume label of a drive. A label has at most 11 characters. Only an RP6502-PICO has volume labels.- Operation:
RIA_OP_SETLABEL 0x2C
- C proto:
rp6502.h
- Parameters:
name – Device name of the drive, then the label, such as
MSC0:GAMES.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOSPC, ENOSYS
GETFREE¶
-
int f_getfree(const char *drive, unsigned long *free, unsigned long *total)¶
f_getfree()returns the free space and the total space of a drive, in 512-byte blocks. The API call returns both counts on the XSTACK, in this layout:struct { unsigned long free; unsigned long total; };
- Operation:
RIA_OP_GETFREE 0x2E
- C proto:
rp6502.h
- Parameters:
drive – Device name of the drive, such as
MSC0:.free – The returned free space.
total – The returned total space.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL, EIO, ENODEV, ENOENT, ENOMEM, ENOSYS
Line Editor¶
RLN_LASTKEY¶
-
int ria_rln_lastkey(char *key, unsigned char *action)¶
ria_rln_lastkey()returns the last key that the line editor received during a read fromCON:, and clears it. A key is one character or an escape sequence. A string from RLN_POKE also sets the last key. A key longer than 32 bytes is not returned.actionis 1 when the line editor handled the key, and 0 when it did not. For example, the line editor does not handle Left at the start of the line.The action byte is on top of the key bytes on the XSTACK.
- Operation:
RIA_OP_RLN_LASTKEY 0x30
- C proto:
rp6502.h
- Parameters:
key – The returned bytes, with no terminator.
keymust hold 32 bytes.action – The returned flag.
- Returns:
Length of the key sequence. 0 when no key is available or the line editor is not reading a line. -1 on error.
- A regs:
return
- Errno:
EINVAL
RLN_PEEK¶
-
int ria_rln_peek(char *peek, unsigned char *pos)¶
ria_rln_peek()returns the text in the line editor and the position of the cursor in it. It fails with EINVAL when the line editor is not reading a line.The position byte is on top of the text on the XSTACK.
- Operation:
RIA_OP_RLN_PEEK 0x31
- C proto:
rp6502.h
- Parameters:
peek – The returned text.
peekmust holdRIA_ATTR_RLN_LENGTH+ 1 bytes, 256 at most.pos – The returned position of the cursor in the text.
- Returns:
Length of the text. -1 on error.
- A regs:
return
- Errno:
EINVAL
RLN_POKE¶
-
int ria_rln_poke(const char *poke)¶
ria_rln_poke()sends a string to the line editor as typed input, in overwrite mode. While the line editor is not reading a line, the call does nothing.A control byte, 0x00 to 0x1F, ends the input and discards the rest of the string. An empty string sends 0x00. ESC (
\33) and CAN (\30) are the exceptions: ESC starts an escape sequence, and CAN cancels one. WhenRIA_ATTR_RLN_LENGTHis 2 or more, the line editor shows the byte that ends the input at the cursor in caret notation,^@to^_, but does not put it in the text. CR (\r) shows nothing.- Operation:
RIA_OP_RLN_POKE 0x32
- C proto:
rp6502.h
- Parameters:
poke – String to send to the line editor, with a length of at most 512.
- Returns:
-1 on error.
- A regs:
return
- Errno:
EINVAL
Time¶
TIME_GET¶
-
ABI int _time(long long *time)¶
-
lib time_t time(time_t *timep)¶
time()returns the current time in seconds since the Unix epoch, 1970-01-01T00:00:00Z. llvm-mos has 64-bit integers, and itstime_tis along long. cc65 has integers of at most 32 bits, so itstime_tis anunsigned long, and itstime()fails with ERANGE when the seconds do not fit. Chaining API Calls, in the ABI section, shows how a cc65 program keeps all 64 bits of the time.- Operation:
RIA_OP_TIME_GET 0x3F
- C proto:
time.h
- Returns:
The current time. -1 on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
TIME_SET¶
-
int time_set(long long time)¶
time_set()sets the clock, in seconds since the Unix epoch. Only an RP6502-PICO can set its clock. On other machines the call fails with EACCES. llvm-mos passes all 64 bits. cc65 has integers of at most 32 bits, so it declarestimeas anunsigned long, and a cc65 program can set only the years 1970 to 2106.- Operation:
RIA_OP_TIME_SET 0x3E
- C proto:
rp6502.h
- Parameters:
time – Seconds since 1970-01-01T00:00:00Z.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EACCES, EINVAL
GMTIME¶
-
ABI int _gmtime(struct tm *tm, const long long *time)¶
-
lib struct tm *gmtime(const time_t *timep)¶
gmtime()converts seconds since the Unix epoch to broken-down time in UTC. Thestruct tmof the API calls has this layout:struct tm { int16_t tm_sec; /* 0-61 */ int16_t tm_min; /* 0-59 */ int16_t tm_hour; /* 0-23 */ int16_t tm_mday; /* 1-31 */ int16_t tm_mon; /* 0-11 */ int16_t tm_year; /* years since 1900 */ int16_t tm_wday; /* 0-6, Sunday = 0 */ int16_t tm_yday; /* 0-365 */ int16_t tm_isdst; /* >0 DST, 0 no DST, <0 unknown */ };
- Operation:
RIA_OP_GMTIME 0x3A
- C proto:
time.h
- Returns:
Pointer to a static struct tm. NULL on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
LOCALTIME¶
-
ABI int _localtime(struct tm *tm, const long long *time)¶
-
lib struct tm *localtime(const time_t *timep)¶
localtime()converts seconds since the Unix epoch to local broken-down time, in the time zone of the machine. On an RP6502-PICO, the time zone is a setting of the monitor.- Operation:
RIA_OP_LOCALTIME 0x3B
- C proto:
time.h
- Returns:
Pointer to a static struct tm. NULL on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
MKTIME¶
-
ABI int _mktime(long long *time, const struct tm *tm)¶
-
lib time_t mktime(struct tm *timep)¶
mktime()converts local broken-down time to seconds since the Unix epoch.- Operation:
RIA_OP_MKTIME 0x3C
- C proto:
time.h
- Returns:
Seconds since the Unix epoch. -1 on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
STRFTIME¶
-
ABI int _strftime(char *buf, int bufsize, const struct tm *tm, const char *format)¶
-
lib size_t strftime(char *buf, size_t bufsize, const char *format, const struct tm *tm)¶
strftime()formats a broken-down time as a string. Every member of thestruct tmmust be in range, as GMTIME and LOCALTIME return it. The API call returns 0 when the result is empty or does not fit.%z %Zfollow the time zone of the machine, and%a %A %b %B %c %p %r %x %Xfollow its locale. On an RP6502-PICO, the locale and the time zone are settings of the monitor. The RP6502-EMU uses the locale of the computer that runs it. The RP6502-WEB and the RP6502-FPGA use the C locale, in English. The format and the result are code page text. The%Eand%Omodifiers have no effect.- Operation:
RIA_OP_STRFTIME 0x3D
- C proto:
time.h
- Parameters:
bufsize – Size of
buf. The API call does not take it.
- Returns:
Length of the string in
buf. 0 on error, or when the result is empty or does not fit.- Errno:
EINVAL
ASCTIME¶
Attributes¶
ATTR_GET¶
-
long ria_attr_get(unsigned char id)¶
ria_attr_get()returns the value of a RIA attribute. It fails with EINVAL for an unknown ID. RIA Attributes lists the attributes.- Operation:
RIA_OP_ATTR_GET 0x0A
- C proto:
rp6502.h
- Parameters:
id – Attribute ID. One of the
RIA_ATTR_*constants.
- Returns:
The value. -1 on error.
- A regs:
id
- Errno:
EINVAL
ATTR_SET¶
-
int ria_attr_set(long val, unsigned char id)¶
ria_attr_set()sets the value of a RIA attribute. It fails with EINVAL for an unknown ID, for a read-only attribute, and for a value outside the range of the attribute.- Operation:
RIA_OP_ATTR_SET 0x0B
- C proto:
rp6502.h
- Parameters:
val – New value.
id – Attribute ID. One of the
RIA_ATTR_*constants.
- Returns:
0 on success. -1 on error.
- A regs:
return, id
- Errno:
EINVAL
Registers¶
SPIN¶
-
lib int ria_spin(void)¶
ria_spin()isJSR RIA_SPIN, which Application Binary Interface describes. It waits until the running API call finishes. It returns with A and X loaded fromRIA_AandRIA_X.- C proto:
rp6502.h
DROP¶
-
void ria_drop(void)¶
ria_drop()isSTZ RIA_OP, which writesRIA_OP_DROP_XSTACKtoRIA_OP. It empties the XSTACK. It is the only API call that does not need a SPIN. A program that makes API calls itself usesria_drop()to quickly discard any unused data on the XSTACK, with one write instead of a loop of pulls.- Operation:
RIA_OP_DROP_XSTACK 0x00
- C proto:
rp6502.h
VSYNC¶
IRQ¶
-
lib unsigned char ria_irq_read(void)¶
-
lib void ria_irq_write(unsigned char mask)¶
The RIA can interrupt the 6502 on two events:
Bit 7, VSYNC, at each increment of the VSYNC register.
Bit 6, SIGINT, when a terminal of the console manifold sends Ctrl-C.
ria_irq_write()enables the interrupt of each event whose bit is 1 inmask, and disables the others. For example,ria_irq_write(0x80)enables VSYNC only. The first interrupt comes from an event after the write. A ROM starts with both interrupts disabled.An interrupt handler calls
ria_irq_read()to learn which events happened.ria_irq_read()returns a 1 in the bit of each event that happened, enabled or not, and clears the bits. The read ends the interrupt request.- C proto:
rp6502.h
- Parameters:
mask – The interrupts to enable.
Directory Entries¶
STAT and READDIR return a directory entry in an f_stat_t, which
rp6502.h defines:
typedef struct {
unsigned long fsize;
unsigned fdate;
unsigned ftime;
unsigned crdate;
unsigned crtime;
unsigned char fattrib;
char altname[12 + 1];
char fname[255 + 1];
} f_stat_t;
Member |
Description |
|---|---|
|
The size of the file in bytes. 0 for a directory. |
|
The date and the time of the last modification. On a FAT drive, a directory keeps the date and the time of its creation here. |
|
The date and the time of the creation. 0 when the drive does not store them. |
|
The attributes, in the bits below. |
|
The short 8.3 name, such as |
|
The name. |
A date and a time are each 16 bits, in the format of FAT:
Bits |
Date |
|---|---|
15-9 |
Year, from 1980 (0 to 127) |
8-5 |
Month (1 to 12) |
4-0 |
Day (1 to 31) |
Bits |
Time |
|---|---|
15-11 |
Hour (0 to 23) |
10-5 |
Minute (0 to 59) |
4-0 |
Second, divided by 2 (0 to 29) |
The attributes are these bits of fattrib:
Bit |
Attribute |
Meaning |
|---|---|---|
0x01 |
Read Only |
The file cannot be written or removed. |
0x02 |
Hidden |
Marks an entry to leave out of a normal listing. |
0x04 |
System |
Marks an entry of an operating system. |
0x10 |
Directory |
The entry is a directory. |
0x20 |
Archive |
Marks a file that changed since a backup program cleared this bit. A machine sets it when it creates or writes a file. |
RIA Attributes¶
Each RIA attribute has a value from 0 to 0x7FFFFFFF.
ria_attr_get() returns the value of an attribute, and
ria_attr_set() sets it.
ID / Name |
Description |
|---|---|
0x00
RIA_ATTR_ERRNO_OPT |
The C runtime sets this attribute, so a C program does not use it.
An assembly program that reads |
0x01
RIA_ATTR_PHI2_KHZ |
The clock speed of the 6502, in kHz. Set it to a value from 100 to 8000 to change the speed. On an RP6502-PICO, a set rounds the speed up, so get the attribute after a set to learn the speed. |
0x02
RIA_ATTR_CODE_PAGE |
The OEM code page of the filesystem, the console, and the built-in font of the VGA. Set it to 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, or 869. |
0x03
RIA_ATTR_RLN_LENGTH |
The maximum number of characters that the user can type into a line of the line editor, 0 to 255. It is 254 after a reset of the 6502. With 0, the user can enter only a blank line. |
0x04
RIA_ATTR_LRAND |
Read-only. A random number from 0 to 0x7FFFFFFF. A program can use
it directly, or seed |
0x05
RIA_ATTR_BEL |
Set it to 0 to silence the bell of the machine, which sounds for
each BEL ( |
0x06
RIA_ATTR_LAUNCHER |
Set it to 1 to register the running ROM as the launcher, and to 0 to clear the registration. A get returns 1 while any ROM is the launcher. Launcher describes how a launcher works. |
0x07
RIA_ATTR_EXIT_CODE |
Read-only. The exit code of the last ROM that exited, 0 to 65535,
for a launcher to test. |
0x08
RIA_ATTR_SIGINT |
Read-only. 1 after the user presses Ctrl-C on the console, even while the input before the Ctrl-C is still unread. A get clears it. It is the SIGINT bit of IRQ. |
0x09
RIA_ATTR_RLN_CAPS |
Compatibility for programs from 8-bit systems that had no lower case. Set it to 1 to change the letters that the user types into the line editor to upper case, or to 2 to invert their case. |
0x0A
RIA_ATTR_RLN_WIDTH |
The width of the console, in columns, for a program that fits its output to the console. The line editor detects the width and the height of the console each time it reads a line. Get them after the line editor reads a line. Set it to a width to use that width, or to 0 to use the detected width. |
0x0B
RIA_ATTR_RLN_HEIGHT |
The height of the console, in rows. Set it to a height to use that height, or to 0 to use the detected height. |
0x0C
RIA_ATTR_RLN_SUPPRESS_NL |
Set it to 1 for a program that draws a form. Enter then keeps the
cursor on the row of the field, so a field on the last row does not
scroll the console. The line that the program reads still ends with
|
0x10
RIA_ATTR_CLK_RUN_MS |
Read-only. The time that the 6502 has run since the release of
reset, in milliseconds. It wraps about every 24.9 days. This is the
C |
0x11
RIA_ATTR_CLK_RUN_CS |
Read-only. The run time of the 6502 in 1/100 seconds. It wraps about every 249 days. |
0x12
RIA_ATTR_CLK_RUN_DS |
Read-only. The run time of the 6502 in 1/10 seconds. It wraps about every 6.8 years. |
0x13
RIA_ATTR_CLK_RUN_S |
Read-only. The run time of the 6502 in seconds. It wraps about every 68 years. |
Launcher¶
A ROM is the whole state of the machine as it comes out of reset, like a cartridge, so a ROM that starts replaces whatever ran before. A ROM also takes arguments through ARGV and returns an exit code through EXIT, like a program. A launcher puts the two together: it is a ROM that starts other ROMs with EXEC, and runs again when each one exits. A menu for an anthology of games works this way. So does a shell that runs a compiler, a linker, and a packager, and stops when a step fails.
To write a launcher:
Set
RIA_ATTR_LAUNCHERto 1 withria_attr_set().Start a ROM with EXEC, with its arguments.
When that ROM exits, the launcher runs again from the start, with only its own path in
argv.RIA_ATTR_EXIT_CODEholds the exit code of the ROM that exited.
The launcher starts fresh each time, so it keeps anything that it needs between runs in a file, such as the next step of a shell. The registration clears when the launcher itself exits. When a ROM that the launcher starts does not load, the console shows the error, and the launcher does not run again.
A menu works best when every game in it has a way to exit, such as an Exit item in its menu.
Application Binary Interface¶
Every API call goes through the RIA registers. The calling convention is based on cc65 fastcall: the last argument goes in registers, and the arguments before it go on a stack.
Making an API Call¶
In the declarations on this page, a char is 8 bits, an int is 16 bits, and a long is 32 bits. An API call takes five steps:
Push each argument except the last to the extended stack (XSTACK) of the RIA, from left to right. A write of
RIA_XSTACKpushes a byte. Push each value high byte first, so that it is in memory low byte first.Put the last argument in
RIA_Afor 8 bits, inRIA_AandRIA_Xfor 16 bits, or inRIA_A,RIA_X, andRIA_SREGfor 32 bits. A last argument of 64 bits goes on the XSTACK instead.Store the operation ID in
RIA_OP. The call starts at once.JSR RIA_SPIN. It returns when the call finishes, with the return value in A and X. The high 16 bits of a 32-bit return value are inRIA_SREG.Before the next call, pull any data that the call returned on the XSTACK with reads of
RIA_XSTACK, or discard it withSTZ RIA_OP, as DROP describes.
This call of ria_attr_set() runs the 6502 at 4 MHz:
LDA #$0F ; 4000 is $0FA0
STA RIA_XSTACK
LDA #$A0
STA RIA_XSTACK
LDA #RIA_ATTR_PHI2_KHZ
STA RIA_A
LDA #RIA_OP_ATTR_SET
STA RIA_OP
JSR RIA_SPIN
BMI error
val is a long, but 4000 fits in 16 bits, so the example pushes only
two bytes, as Short Stacking describes.
When a call fails, RIA_A, RIA_X, and RIA_SREG hold -1,
RIA_ERRNO holds the errno, and the XSTACK is empty, so there is
nothing to pull. A call changes RIA_SREG only when it returns 32 bits
or fails.
RIA_SPIN is a short program in the RIA registers, and the RIA changes
the program as the call runs:
FFF1: BRA #$?? ; RIA_BUSY {-2 or 0}
FFF3: LDA #$?? ; RIA_A
FFF5: LDX #$?? ; RIA_X
FFF7: RTS
RIA_BUSY is the offset of the BRA. While the call runs, it is -2, and
the BRA branches to itself. When the call finishes, it is 0, and the
program goes on to load A and X. X is loaded last, so when RIA_SPIN
returns, flag N is the top bit of X. For a 16-bit return value, that bit
is the sign, so the BMI after JSR RIA_SPIN branches on an error.
To do other work while a call runs, poll RIA_BUSY in place of
JSR RIA_SPIN, as XRAM Transfers shows.
Short Stacking¶
Only the first argument that a program pushes can be shorter than its
type, and only when the value fits. For this reason, offset is the
first argument of LSEEK:
long f_lseek(long offset, char whence, int fildes)
Push an offset that fits in 16 bits as two bytes in place of four. The RIA extends a short signed argument with its sign, so two bytes carry -32768 to 32767, and it fills a short unsigned argument with zeros.
Warning
Size a short push by the signed range. An offset of 200 pushed as the single byte 0xC8 arrives as -56, because the top bit of 0xC8 is the sign. Push 0x00 and then 0xC8 to send 200.
Shorter AX¶
Many calls need only RIA_A. When the a regs field of a call lists
its last argument, put that argument in RIA_A and skip RIA_X.
When a regs lists return, read the return value from A alone, or
from A and X as a 16-bit int.
Strings and Structures¶
The RIA cannot read or write 6502 RAM, so strings, structures, and
buffers go on the XSTACK, up to 512 bytes for each call. In a C
declaration, a pointer gives the type of that data and its direction.
Push the data of a const pointer before the call, and pull the data
of any other pointer after it.
A program may leave out the terminator of the string at the bottom of
the XSTACK, the one pushed first and pulled last, because a pull from the
empty XSTACK returns 0. A returned string comes without it, except inside
a structure, such as f_stat_t or the argv data. A string pushed on
top of other data, such as the new name of RENAME, needs its
terminator. A call that returns data of variable size returns its length
in bytes. The length does not include data of fixed size, such as a
structure or the position byte of RLN_PEEK.
Three examples:
int open(const char *path, int oflag)
Push the path from its last character to its first. A path has a length
of at most 255. Put oflag in RIA_A.
int read_xstack(void *buf, unsigned count, int fildes)
Push count, and put fildes in RIA_A. The call returns the
number of bytes to pull from the XSTACK.
int write_xstack(const void *buf, unsigned count, int fildes)
Push the data from its last byte to its first, and put fildes in
RIA_A. Do not push count, because the RIA takes it from the size
of the data on the XSTACK.
XRAM Transfers¶
READ_XRAM and WRITE_XRAM move data between a file and XRAM without passing it through 6502 RAM. They work like DMA: start a transfer, and the 6502 is free to do other work while it runs.
int read_xram(unsigned xaddr, unsigned count, int fildes)
int write_xram(unsigned xaddr, unsigned count, int fildes)
Push xaddr and then count, and put fildes in RIA_A.
An RP6502-PICO moves up to 800 KB per second, and most other machines are faster. Even well below that speed, all 64 KB of XRAM loads several times per second, with no wait states for the 6502. READ_XRAM and WRITE_XRAM are why the Picocomputer 6502 has no paged memory: data moves between storage and XRAM fast enough that paged memory is not worth its cost and complication.
A program does not have to wait for a transfer. It can start one, go on
with its other work, and check RIA_BUSY each time through its main
loop. Bit 7 of RIA_BUSY is 1 while the transfer runs:
loop: BIT RIA_BUSY
BMI work ; still transferring
... ; done: start the next transfer
work: ... ; other work
JMP loop
The C functions read_xram() and write_xram() wait for the
transfer to finish. To transfer in the background from C, set up the call
with RIA.xstack, RIA.a, and RIA.op, and test
RIA.busy & 0x80.
Chaining API Calls¶
Some calls return data on the XSTACK that another call takes as its argument. Make the second call without pulling the data first, and the data goes straight from one call to the next. A program that copies a file this way needs no buffer for the data:
#include <rp6502.h>
#pragma optimize (push, off) // for cc65
int copy(int src, int dst)
{
int n;
for (;;)
{
RIA.xstack = 0x02; // count: 512, high byte first
RIA.xstack = 0x00;
RIA.a = src;
RIA.op = RIA_OP_READ_XSTACK; // read a block onto the XSTACK
n = ria_spin();
if (n <= 0)
return n; // 0 at the end of the file
RIA.a = dst;
RIA.op = RIA_OP_WRITE_XSTACK; // write the block from the XSTACK
if (ria_spin() != n)
return -1;
}
}
#pragma optimize (pop)
Time calls chain too. A cc65 program needs the chain to keep all 64 bits
of the time, because its 32-bit time_t cannot hold them. In
assembly, a chain also keeps the code small. To format the local time:
Call TIME_GET. Leave the time on the XSTACK.
Call LOCALTIME. Leave the
struct tmon the XSTACK.Push the format string on top, with its terminator.
Call STRFTIME.
Pull as many bytes as STRFTIME returns.
Any call that returns seconds, TIME_GET or MKTIME, chains into
GMTIME, LOCALTIME, or TIME_SET. Any call that returns a
struct tm, GMTIME or LOCALTIME, chains into MKTIME or
STRFTIME.
Errno Translations¶
errno in C is RIA_ERRNO, the register that a failed API call
sets. cc65 and llvm-mos give the errno names different numbers, so
RIA_ATTR_ERRNO_OPT selects the numbers that RIA_ERRNO uses: 1
for cc65, or 2 for llvm-mos. In a C program, the C runtime has already
selected the numbers of its compiler before main() starts. An
assembly program must select its own. Until it does, the option is 0,
and every failed API call sets RIA_ERRNO to $FFFF.
cc65 has no EDOM or EILSEQ, so a cc65 program gets EUNKNOWN, 18, in their place.
cc65 |
llvm-mos |
|
|---|---|---|
option |
1 |
2 |
ENOENT |
1 |
2 |
ENOMEM |
2 |
12 |
EACCES |
3 |
13 |
ENODEV |
4 |
19 |
EMFILE |
5 |
24 |
EBUSY |
6 |
16 |
EINVAL |
7 |
22 |
ENOSPC |
8 |
28 |
EEXIST |
9 |
17 |
EAGAIN |
10 |
11 |
EIO |
11 |
5 |
EINTR |
12 |
4 |
ENOSYS |
13 |
38 |
ESPIPE |
14 |
29 |
ERANGE |
15 |
34 |
EBADF |
16 |
9 |
ENOEXEC |
17 |
8 |
EDOM |
18 |
33 |
EILSEQ |
18 |
84 |
EUNKNOWN |
18 |
85 |
Host Error Codes¶
Every library and every operating system has its own error codes. The
RIA maps them to the errno names that cc65 and llvm-mos both have. The
table shows the littlefs codes without their LFS_ERR_ prefix, and
the Windows codes without their ERROR_ prefix. A code that is not in
the table becomes EIO.
FatFs |
littlefs |
POSIX |
Windows |
|
|---|---|---|---|---|
ENOENT |
FR_NO_FILE, FR_NO_PATH |
NOENT, NOTDIR |
ENOENT, ENOTDIR |
FILE_NOT_FOUND, PATH_NOT_FOUND, NO_MORE_FILES, DIRECTORY |
EACCES |
FR_DENIED, FR_WRITE_PROTECTED |
ISDIR |
EACCES, EPERM, EROFS, EISDIR |
ACCESS_DENIED, WRITE_PROTECT |
EINVAL |
FR_INVALID_NAME, FR_INVALID_PARAMETER |
INVAL, NAMETOOLONG |
EINVAL, ENAMETOOLONG |
FILENAME_EXCED_RANGE, INVALID_NAME, INVALID_PARAMETER, NEGATIVE_SEEK |
ENODEV |
FR_NOT_READY, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM |
ENODEV, ENXIO, EXDEV |
NOT_READY, BAD_UNIT, INVALID_DRIVE, NOT_SAME_DEVICE |
|
ENOSPC |
FR_NO_SPACE |
FBIG, NOSPC |
ENOSPC, EFBIG, EDQUOT |
DISK_FULL, HANDLE_DISK_FULL, DISK_QUOTA_EXCEEDED, CANNOT_MAKE, FILE_TOO_LARGE |
EEXIST |
FR_EXIST, FR_NOT_EMPTY |
EXIST, NOTEMPTY |
EEXIST, ENOTEMPTY |
ALREADY_EXISTS, FILE_EXISTS, DIR_NOT_EMPTY |
EMFILE |
FR_TOO_MANY_OPEN_FILES |
EMFILE, ENFILE |
TOO_MANY_OPEN_FILES |
|
ENOMEM |
FR_NOT_ENOUGH_CORE |
NOMEM |
ENOMEM |
NOT_ENOUGH_MEMORY, OUTOFMEMORY |
EBUSY |
FR_LOCKED |
EBUSY, ETXTBSY |
BUSY, PIPE_BUSY, SHARING_VIOLATION, LOCK_VIOLATION |
|
EBADF |
FR_INVALID_OBJECT |
BADF |
EBADF |
INVALID_HANDLE |
EAGAIN |
FR_TIMEOUT |
EAGAIN |
||
EINTR |
EINTR |
OPERATION_ABORTED |
||
ESPIPE |
ESPIPE |
|||
ERANGE |
ERANGE |
|||
EIO |
FR_DISK_ERR, FR_INT_ERR, FR_MKFS_ABORTED |
IO, CORRUPT, NOATTR |
EIO |