RP6502-OS¶
RP6502 - Operating System
Introduction¶
The RP6502-RIA runs a 32-bit operating system that the 6502 can call into. It lives entirely on the RIA’s own processor — protected from the 6502 and using none of its system RAM — so it never gets in the way of developing a native 6502 OS of your own.
The OS is POSIX-like, with an Application Binary Interface (ABI) modeled
on cc65’s fastcall. It
offers stdio.h and unistd.h services to both the cc65 and llvm-mos
compilers, plus calls to reach RP6502 features and manage FAT
filesystems.
Note
ExFAT is ready to go and will be enabled when the patents expire.
Memory Map¶
There is no ROM, and nothing in zero page is used or reserved — the Picocomputer starts every project as a clean slate. VGA, audio, storage, keyboards, mice, gamepads, the RTC, and networking are all reached through just the 32 registers of the RIA.
Address |
Description |
|---|---|
$0000-$FEFF |
RAM, 63.75 KB |
$FF00-$FFCF |
Unassigned |
$FFD0-$FFDF |
VIA, see the WDC datasheet |
$FFE0-$FFFF |
RIA, see the RP6502-RIA datasheet |
$10000-$1FFFF |
XRAM, 64 KB for RP6502-RIA and RP6502-VGA |
The unassigned space is open for hardware experimenters. Design your own chip-select logic to use it: add more VIAs downward and other hardware upward — for example, VIA0 at $FFD0, VIA1 at $FFC0, SID0 at $FF00, and SID1 at $FF20.
Application Binary Interface¶
See also
RP6502-RIA — the hardware register map referenced throughout this section.
The ABI for calling the operating system is based on fastcall from the cc65 internals. The OS itself uses nothing from cc65 and is just as easy to call from assembly. At its core, the ABI is four simple rules:
Stack arguments are pushed left to right.
Last argument passed by register A, AX, or AXSREG.
Return value in register AX or AXSREG.
May return data on the stack.
A and X are the 6502 registers. The pseudo-register AX combines them into 16 bits, and AXSREG extends that to 32 bits with the 16 SREG bits. Here’s how to make an OS call through the RIA registers. Every OS call is specified as a C declaration, like so:
-
int doit(int arg0, int arg1);¶
The RIA has registers called RIA_A, RIA_X, and RIA_SREG. An
int is 16 bits, so we load arg1 into the RIA_A and RIA_X
registers. Throughout this explanation, “A” means the 6502 register and
“RIA_A” means the RIA register.
arg0 goes on the XSTACK. Reading RIA_XSTACK pops bytes; writing
pushes them. It’s a top-down stack, so push each argument left to right,
keeping little-endian byte order.
To execute the call, store the operation ID in RIA_OP; the operation
begins immediately. You can keep the 6502 busy with other work — a
loading animation, say — by polling RIA_BUSY, or just JSR to
RIA_SPIN to block until it’s done.
JSR RIA_SPIN can unblock within 3 clock cycles and loads A and X for
you. Sequential operations run fastest this way. Under the hood, you’re
jumping into a self-modifying program that runs out of the RIA registers.
FFF1: BRA #$?? ; RIA_BUSY {-2 or 0}
FFF3: LDA #$?? ; RIA_A
FFF5: LDX #$?? ; RIA_X
FFF7: RTS
Polling is just snooping on that same program. The RIA_BUSY register
is the -2 or 0 in the BRA above. Per the RIA datasheet, bit 7 signals
busy, which the 6502 can test quickly with the BIT operator to set flag
N. Once it clears, read RIA_A and RIA_X with absolute instructions.
wait: BIT RIA_BUSY
BMI wait
LDA RIA_A
LDX RIA_X
Any operation that returns RIA_A also returns RIA_X to help with
C integer promotion. Loading X last allows fast testing for negative
return values. RIA_SREG is updated only for 32-bit returns, and
RIA_ERRNO only when there’s an error.
Some operations return strings or structures on the stack. Pull the
entire stack before the next call or use
zxstack() to abandon the stack in O(1) time without a loop.
Tail-call optimizations are still possible, though — you can chain
read_xstack() and write_xstack() to
copy a file without touching any RAM or XRAM.
The time operations chain the same way, without cycling the XSTACK: TIME_GET returns seconds positioned as the input to GMTIME, LOCALTIME, or TIME_SET; their struct tm feeds MKTIME directly, or STRFTIME after pushing only the zero-terminated format on top; and MKTIME returns seconds ready for another conversion.
Short Stacking¶
In the pursuit of saving every cycle, you can trim a few off the stack push when you don’t need the full range. This applies only to the first stack argument pushed. Take LSEEK:
long f_lseek(long offset, int whence, int fildes)
Here you push a 32-bit value, and — not by coincidence — it sits in the right position for short stacking. If the offset always fits in 16 bits, push two bytes instead of four.
Trimmed bytes are zero-filled, so short pushes read as unsigned. The time operations accept seconds as 4 or 8 bytes; negative values need all 8.
Shorter AX¶
Many operations can save a few cycles by ignoring REG_X. Returned integers are always at least 16 bits, to help with C integer promotion, but many operations ignore REG_X on the way in and keep their return value within REG_A. Those are flagged below as “A regs”.
Bulk Data¶
Functions that move bulk data come in two flavors, depending on where the data lives. A RAM pointer means nothing to the RIA, since it can’t touch 6502 RAM — so bulk data moves through the XSTACK or XRAM instead.
Bulk XSTACK Operations¶
These work only for sizes of 512 bytes or less — the size of the XSTACK they pass data on. A pointer in the C prototype marks the type and direction (to or from the OS) of the data. A few examples:
int open(const char *path, int oflag);
Send oflag in RIA_A; per the OPEN docs, RIA_X doesn’t need
to be set. Send the path on the XSTACK by pushing the string from its
last character backward. You can skip the terminating zero, but strings
are capped at 255 bytes. From the C SDK this just works — the
implementation pushes the string for you.
int read_xstack(void *buf, unsigned count, int fildes)
Send count as a short stack and fildes in RIA_A; per the
READ_XSTACK docs, RIA_X doesn’t need to be set. The value returned
in AX tells you how many values to pull from the stack. From the C SDK,
it copies the XSTACK into buf[] for you.
int write_xstack(const void *buf, unsigned count, int fildes)
Send fildes in RIA_A; per the WRITE_XSTACK docs, RIA_X
doesn’t need to be set. Push the buf data onto the XSTACK. Don’t send
count — the OS knows it from its internal stack pointer. From the C
SDK, it copies count bytes of buf[] onto the XSTACK for you.
Note that read() and write() are part of the C SDK, not OS operations. C requires them to handle counts larger than the XSTACK can return, so the implementation makes as many OS calls as it takes.
Bulk XRAM Operations¶
These load and save XRAM directly through READ_XRAM and WRITE_XRAM, so you can pull assets straight in without routing them through 6502 RAM.
int read_xram(unsigned buf, unsigned count, int fildes)
int write_xram(unsigned buf, unsigned count, int fildes)
The OS expects buf and count on the XSTACK as integers, with
fildes in RIA_A. From the 6502, reach
XRAM memory through RIA_RW0 or RIA_RW1.
These operations stand out for their speed and for running in the background while the 6502 does other work. Depending on the request size, expect up to 800 KB/sec — a full 64 KB of XRAM loads or saves multiple times per second with no wait states or 6502 work.
Bulk XRAM operations are why the Picocomputer 6502 has no paged memory. You don’t need it when “disk” access has zero seek time and DMA to XRAM.
Application Programmer Interface¶
See also
FatFs documentation — many of the filesystem functions below are thin wrappers around FatFs.
Much of this API is based on POSIX and FatFs, so filesystem and console
access should feel very familiar. A few operations reorder their
arguments or change their data structures, though. The reason becomes
clear once you’re in assembly, fine-tuning short stacking and integer
demotion — shrinking a return value to fit in fewer registers. In C you
may never notice, because the standard library wraps these calls in
familiar prototypes. The f_lseek() below, for instance, reorders its
arguments to put the long one in position for short stacking — but you
don’t have to call f_lseek() from C. You can call the usual
lseek(), which keeps the traditional argument order.
The OS is built around FAT filesystems, the de facto standard for
unsecured USB storage. POSIX filesystems aren’t fully compatible with
FAT, but there’s a solid core of basic I/O where the two agree
completely. So you’ll find familiar POSIX functions like open()
alongside others like f_stat() — close to their POSIX cousins, but
tailored to FAT. If a true POSIX stat() is ever needed, it can be
built in the C standard library or in an application by translating
f_stat() data.
ZXSTACK¶
-
void zxstack(void);¶
Abandon the XSTACK by resetting the XSTACK pointer. This is the only operation you don’t have to wait on, and you never need it after a failed operation. It’s handy when you want to quickly ignore part of a returned structure.
- Op code:
RIA_OP_ZXSTACK 0x00
- C proto:
rp6502.h
XREG¶
-
int xreg(char device, char channel, unsigned char address, ...);¶
-
int xregn(char device, char channel, unsigned char address, unsigned count, ...);¶
Prefer xreg() from C to avoid a counting mistake. The count isn’t sent over the ABI, so both prototypes are equally valid.
The variadic argument is a list of ints to store in the extended registers, starting at address on the given device and channel. See the RP6502-RIA and RP6502-VGA docs for what each register does. Setting an extended register can fail, which doubles as feature detection: EINVAL means the device sent a negative acknowledgement, and EIO means a timeout waiting for ack/nak.
This is how you add virtual hardware to extended RAM. Both the RP6502-RIA and RP6502-VGA ship with virtual devices you can install, and you can build your own hardware for the PIX bus and configure it with this same call.
- Op code:
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
address – PIX address. 0-255
... – 16 bit integers to set starting at address.
- A regs:
return
- Errno:
EINVAL, EIO
ARGV¶
-
int _argv(char *argv, int size)¶
The virtual _argv is called during C initialization to supply argc and argv to main(). It returns an array of zero-terminated string indexes followed by the strings themselves. e.g. [“ABC”, “DEF”] is 06 00 0A 00 00 00 41 42 43 00 44 45 46 00 The returned data is guaranteed valid.
Because this can use up to 512 bytes of RAM, you opt in by providing storage for the argv data. Use static memory, or dynamically allocated memory you can free afterward. You can also reject an oversized argv by returning NULL.
void *argv_mem(size_t size) { return malloc(size); }
- Op code:
RIA_OP_ARGV 0x08
- C proto:
(none)
- Returns:
Size of argv data
- Errno:
will not fail
EXEC¶
-
int ria_execl(const char *path, ...)¶
-
int ria_execv(const char *path, char *const argv[])¶
-
int _exec(const char *argv, int size)¶
The virtual _exec is called by ria_execl() and ria_execv(). Note one difference from the execl() and execv() you may know: because RAM is precious, the path is supplied once, not again in argv[0]. The launched ROM sees argv[0] as the filename.
The data sent by _exec() is checked for pointer safety and sanity, but the path is assumed to point at a loadable ROM file. On EINVAL, the argv buffer is cleared, so later calls to _argv() return an empty set. If the ROM turns out to be invalid, the user is dropped back to the console with an error message.
The ria_execl() and ria_execv() wrappers accept at most 16 strings (the path plus up to 15 arguments) totaling no more than 512 bytes including the offset table; exceeding either limit returns -1 with EINVAL.
- Op code:
RIA_OP_EXEC 0x09
- C proto:
rp6502.h
- Returns:
Does not return on success — the new ROM begins executing. -1 on error.
- Errno:
EINVAL
ATTR_GET¶
-
long ria_attr_get(unsigned char id)¶
Returns the current value of a RIA attribute. See RIA Attributes for attribute IDs and descriptions.
- Op code:
RIA_OP_ATTR_GET 0x0A
- C proto:
rp6502.h
- Parameters:
id – Attribute ID. One of the
RIA_ATTR_*constants.
- A regs:
id
- Returns:
The attribute value as a 31-bit integer. -1 on error.
- Errno:
EINVAL
ATTR_SET¶
-
int ria_attr_set(long val, unsigned char id)¶
Sets the value of a RIA attribute. See RIA Attributes for attribute IDs and descriptions.
- Op code:
RIA_OP_ATTR_SET 0x0B
- C proto:
rp6502.h
- Parameters:
id – Attribute ID. One of the
RIA_ATTR_*constants.val – New value.
- A regs:
id
- Returns:
0 on success
- Errno:
EINVAL
TIME_GET¶
-
time_t time(time_t *timep)¶
Obtains the current time as seconds since the Unix epoch, 1970-01-01T00:00:00Z. The seconds are pushed to the XSTACK as a 64-bit signed integer.
- Op code:
RIA_OP_TIME_GET 0x3F
- C proto:
time.h
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EIO
TIME_SET¶
-
int time_set(long long time)¶
Sets the clock to seconds since the Unix epoch. Push the seconds to the XSTACK as a signed integer of up to 64 bits; short pushes are unsigned.
- Op code:
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:
EINVAL, ERANGE
GMTIME¶
-
struct tm *gmtime(const time_t *timep)¶
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 */ };
Converts seconds since the Unix epoch to UTC broken-down time. Push the seconds as a signed integer of up to 64 bits; short pushes are unsigned. The struct tm above is pushed back to the XSTACK.
- Op code:
RIA_OP_GMTIME 0x3A
- C proto:
time.h
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
LOCALTIME¶
-
struct tm *localtime(const time_t *timep)¶
Converts seconds since the Unix epoch to local broken-down time using the configured time zone. Run
help set tzon the monitor to learn how to configure your time zone. Push the seconds as a signed integer of up to 64 bits; short pushes are unsigned. A struct tm (see GMTIME) is pushed back to the XSTACK.- Op code:
RIA_OP_LOCALTIME 0x3B
- C proto:
time.h
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
MKTIME¶
-
time_t mktime(struct tm *timep)¶
Converts local broken-down time to seconds since the Unix epoch. Push a struct tm (see GMTIME) to the XSTACK; fields outside their ranges are normalized. The seconds are pushed back as a 64-bit signed integer. The C library mktime() then calls LOCALTIME to write the normalized struct, with tm_wday and tm_yday set, back to the caller.
- Op code:
RIA_OP_MKTIME 0x3C
- C proto:
time.h
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, ERANGE
STRFTIME¶
-
size_t strftime(char *buf, size_t bufsize, const char *format, const struct tm *tm)¶
Formats a broken-down time as a string. Push a struct tm (see GMTIME), then a zero-terminated format string, to the XSTACK. All struct tm fields must be in range, e.g. as returned by GMTIME, LOCALTIME, or MKTIME. The formatted string is pushed back without a terminator and its length returned; the format and result share the XSTACK, which limits the result. The C library strftime() compares the length to its buffer size and abandons an oversized result with ZXSTACK.
%a %A %b %B %c %p %r %x %Xfollow the locale set withSET LOC.%z %Zfollow the time zone set withSET TZ. The format and result are code page text.%Eand%Omodifiers are ignored.- Op code:
RIA_OP_STRFTIME 0x3D
- C proto:
time.h
- Returns:
Length of the formatted string. 0 if empty or it does not fit. -1 on error.
- A regs:
return
- Errno:
EINVAL
OPEN¶
-
int open(const char *path, int oflag)¶
Create a connection between a file and a file descriptor.
- Op code:
RIA_OP_OPEN 0x14
- C proto:
fcntl.h
- Parameters:
path – Pathname to a file.
oflag – Bitfield of options.
- Returns:
File descriptor. -1 on error.
- A regs:
return, oflag
- Errno:
EINVAL, EMFILE, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_DENIED, FR_EXIST, FR_INVALID_OBJECT, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_LOCKED, FR_NOT_ENOUGH_CORE, FR_TOO_MANY_OPEN_FILES
- Options:
- O_RDONLY 0x01Open for reading only.O_WRONLY 0x02Open for writing only.O_RDWR 0x03Open for reading and writing.O_CREAT 0x10Create the file if it does not exist.O_TRUNC 0x20Truncate the file length to 0 after opening.O_APPEND 0x40Read/write pointer is set end of the file.O_EXCL 0x80If O_CREAT and O_EXCL are set, fail if the file exists.
CLOSE¶
-
int close(int fildes)¶
Finish pending writes and release the file descriptor. File descriptor will rejoin the pool available for use by open().
- Op code:
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:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT
READ¶
-
int read(int fildes, void *buf, unsigned count)¶
Read
countbytes from a file into a buffer. This is implemented in the compiler library as a series of calls to READ_XSTACK.- Op code:
None
- C proto:
unistd.h
- Parameters:
buf – Destination for the returned data.
count – Quantity of bytes to read. 0x7FFF max.
fildes – File descriptor from open().
- Returns:
On success, number of bytes read is returned. On error, -1 is returned.
- A regs:
fildes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT
READ_XSTACK¶
-
int read_xstack(void *buf, unsigned count, int fildes)¶
Read
countbytes from a file to the XSTACK.- Op code:
RIA_OP_READ_XSTACK 0x16
- C proto:
rp6502.h
- Parameters:
buf – Destination for the returned data.
count – Quantity of bytes to read. 0x200 max.
fildes – File descriptor from open().
- Returns:
On success, number of bytes read is returned. On error, -1 is returned.
- A regs:
fildes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT
READ_XRAM¶
-
int read_xram(unsigned buf, unsigned count, int fildes)¶
Read
countbytes from a file to XRAM.- Op code:
RIA_OP_READ_XRAM 0x17
- C proto:
rp6502.h
- Parameters:
buf – Destination for the returned data.
count – Quantity of bytes to read. 0x7FFF max.
fildes – File descriptor from open().
- Returns:
On success, number of bytes read is returned. On error, -1 is returned.
- A regs:
fildes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT
WRITE¶
-
int write(int fildes, const void *buf, unsigned count)¶
Write
countbytes from a buffer to a file. This is implemented in the compiler library as a series of calls to WRITE_XSTACK.- Op code:
None
- C proto:
unistd.h
- Parameters:
buf – Location of the data.
count – Quantity of bytes to write. 0x7FFF max.
fildes – File descriptor from open().
- Returns:
On success, number of bytes written is returned. On error, -1 is returned.
- A regs:
fildes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT
WRITE_XSTACK¶
-
int write_xstack(const void *buf, unsigned count, int fildes)¶
Write
countbytes from the XSTACK to a file.- Op code:
RIA_OP_WRITE_XSTACK 0x18
- C proto:
rp6502.h
- Parameters:
buf – Location of the data.
count – Quantity of bytes to write. 0x200 max.
fildes – File descriptor from open().
- Returns:
On success, number of bytes written is returned. On error, -1 is returned.
- A regs:
fildes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT
WRITE_XRAM¶
-
int write_xram(unsigned buf, unsigned count, int fildes)¶
Write
countbytes from XRAM to a file.- Op code:
RIA_OP_WRITE_XRAM 0x19
- C proto:
rp6502.h
- Parameters:
buf – Location of the data.
count – Quantity of bytes to write. 0x7FFF max.
fildes – File descriptor from open().
- Returns:
On success, number of bytes written is returned. On error, -1 is returned.
- A regs:
fildes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT
LSEEK¶
-
long f_lseek(long offset, int whence, int fildes)¶
-
off_t lseek(int fildes, off_t offset, int whence)¶
Move the read/write pointer. The OS uses the ABI format of f_lseek(). An lseek() compatible wrapper is provided with the compiler library.
This can also be used to obtain the current read/write position with
f_lseek(0, SEEK_CUR, fd).- Op code:
See table below.
- C proto:
f_lseek: rp6502.h, lseek: unistd.h
- Parameters:
offset – How far you wish to seek.
whence – From whence you wish to seek. See table below.
fildes – File descriptor from open().
- Returns:
Read/write position. -1 on error. A resulting position past 0x7FFFFFFF cannot be represented in the returned long; the seek then fails with errno ERANGE and the file position is left unchanged.
- A regs:
fildes
- Errno:
EINVAL, ERANGE, FR_DISK_ERR, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT
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
UNLINK¶
-
int unlink(const char *name)¶
Removes a file or directory from the volume.
- Op code:
RIA_OP_UNLINK 0x1B
- C proto:
unistd.h
- Parameters:
name – File or directory name to unlink (remove).
- Returns:
0 on success. -1 on error.
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_DENIED, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_LOCKED, FR_NOT_ENOUGH_CORE
RENAME¶
-
int rename(const char *oldname, const char *newname)¶
Renames and/or moves a file or directory.
- Op code:
RIA_OP_RENAME 0x1C
- C proto:
stdio.h
- Parameters:
oldname – Existing file or directory name to rename.
newname – New object name.
- Returns:
0 on success. -1 on error.
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_EXIST, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_LOCKED, FR_NOT_ENOUGH_CORE
SYNCFS¶
-
int syncfs(int fildes)¶
Finish pending writes for the file descriptor.
- Op code:
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:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT
STAT¶
-
int f_stat(const char *path, f_stat_t *dirent)¶
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;
Returns file or directory info for requested path. See the FatFs documentation for details about the data structure.
- Op code:
RIA_OP_STAT 0x1F
- C proto:
rp6502.h
- Parameters:
path – Pathname to a directory entry.
dirent – Returned f_stat_t data.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
OPENDIR¶
-
int f_opendir(const char *name)¶
Create a connection between a directory and a directory descriptor.
- Op code:
RIA_OP_OPENDIR 0x20
- C proto:
rp6502.h
- Parameters:
name – Pathname to a directory.
- Returns:
Directory descriptor. -1 on error.
- A regs:
return
- Errno:
EINVAL, EMFILE, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_PATH, FR_INVALID_NAME, FR_INVALID_OBJECT, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE, FR_TOO_MANY_OPEN_FILES
READDIR¶
-
int f_readdir(f_stat_t *dirent, int dirdes)¶
Returns directory entry info for the current read position of a directory descriptor, then advances the read position.
- Op code:
RIA_OP_READDIR 0x21
- C proto:
rp6502.h
- Parameters:
dirdes – Directory descriptor from f_opendir().
dirent – Returned f_stat_t data.
- Returns:
0 on success. -1 on error.
- A regs:
return, dirdes
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
CLOSEDIR¶
-
int f_closedir(int dirdes)¶
Release the directory descriptor. Directory descriptor will rejoin the pool available for use by f_opendir().
- Op code:
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:
EINVAL, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT
TELLDIR¶
-
long f_telldir(int dirdes)¶
Returns the read position of the directory descriptor.
- Op code:
RIA_OP_TELLDIR 0x23
- C proto:
rp6502.h
- Parameters:
dirdes – Directory descriptor from f_opendir().
- Returns:
Read position. -1 on error.
- A regs:
dirdes
- Errno:
EINVAL, EBADF
SEEKDIR¶
-
int f_seekdir(long offs, int dirdes)¶
Set the read position for the directory descriptor. Internally, the FatFs directory read position can only move forward by one, so use this for convenience, not performance.
- Op code:
RIA_OP_SEEKDIR 0x24
- C proto:
rp6502.h
- Parameters:
offs – New read position, as returned by f_telldir().
dirdes – Directory descriptor from f_opendir().
- Returns:
Read position. -1 on error.
- A regs:
return, dirdes
- Errno:
EINVAL, EBADF, FR_DISK_ERR, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
REWINDDIR¶
-
int f_rewinddir(int dirdes)¶
Rewind the read position of the directory descriptor.
- Op code:
RIA_OP_REWINDDIR 0x25
- C proto:
rp6502.h
- Parameters:
dirdes – Directory descriptor from f_opendir().
- Returns:
0 on success. -1 on error.
- A regs:
dirdes
- Errno:
EINVAL, EBADF, FR_DISK_ERR, FR_INT_ERR, FR_INVALID_OBJECT, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
CHMOD¶
-
int f_chmod(const char *path, unsigned char attr, unsigned char mask)¶
Change the attributes of a file or directory.
- Op code:
RIA_OP_CHMOD 0x26
- C proto:
rp6502.h
- Parameters:
path – Pathname to a file or directory.
attr – New bitfield of attributes. See table.
mask – Only attributes with bits set here will be changed.
- Returns:
0 on success. -1 on error.
- A regs:
return, mask
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
Attribute
Bit
Read Only
0x01
Hidden
0x02
System
0x04
Directory
0x10
Archive
0x20
UTIME¶
-
int f_utime(const char *path, unsigned fdate, unsigned ftime, unsigned crdate, unsigned crtime)¶
Update the date and time stamps of a file or directory. A date of 0 (invalid) leaves the date and time unchanged.
- Op code:
RIA_OP_UTIME 0x27
- C proto:
rp6502.h
- Parameters:
path – Pathname to a file or directory.
fdate – Modification date.
ftime – Modification time.
crdate – Creation date.
crtime – Creation time.
- Returns:
0 on success. -1 on error.
- A regs:
return, crtime
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
Date¶ bit15:9
Years since 1980 (0..127)
bit8:5
Month (1..12)
bit4:0
Day (1..31)
Time¶ bit15:11
Hour (0..23)
bit10:5
Minute (0..59)
bit4:0
Second / 2 (0..29)
MKDIR¶
-
int f_mkdir(const char *name)¶
Make a new directory entry.
- Op code:
RIA_OP_MKDIR 0x28
- C proto:
rp6502.h
- Parameters:
name – Pathname of the directory to create.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_PATH, FR_INVALID_NAME, FR_DENIED, FR_EXIST, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
CHDIR¶
-
int chdir(const char *name)¶
Change to a directory entry.
- Op code:
RIA_OP_CHDIR 0x29
- C proto:
unistd.h
- Parameters:
name – Pathname of the directory to make current.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_PATH, FR_INVALID_NAME, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
CHDRIVE¶
-
int f_chdrive(const char *name)¶
Change the current drive. Valid names are
MSC0:–MSC9:with shortcuts0:–9:. Each USB device (LUN) mounts as one drive.- Op code:
RIA_OP_CHDRIVE 0x2A
- C proto:
rp6502.h
- Parameters:
name – Drive name to change to.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
FR_INVALID_DRIVE
GETCWD¶
-
int f_getcwd(char *name, int size)¶
Get the current working directory. Size is ignored by the OS but the C wrapper will use it.
- Op code:
RIA_OP_GETCWD 0x2B
- C proto:
rp6502.h
- Parameters:
name – The returned directory.
- Returns:
Size of returned name. -1 on error.
- Errno:
ENOMEM, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE
SETLABEL¶
-
int f_setlabel(const char *name)¶
Change the volume label. Max 11 characters.
- Op code:
RIA_OP_SETLABEL 0x2C
- C proto:
rp6502.h
- Parameters:
name – Label with optional volume name.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_NAME, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT
GETLABEL¶
-
int f_getlabel(const char *path, char *label)¶
Get the volume label. Label must have room for (11+1) bytes.
- Op code:
RIA_OP_GETLABEL 0x2D
- C proto:
rp6502.h
- Parameters:
name – Volume name.
label – Storage for returned label.
- Returns:
Size of returned label. -1 on error.
- A regs:
return
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT
GETFREE¶
-
int f_getfree(const char *name, unsigned long *free, unsigned long *total)¶
struct { unsigned long free; unsigned long total; };
Get the volume free and total space in number of 512 bytes blocks.
- Op code:
RIA_OP_GETFREE 0x2E
- C proto:
rp6502.h
- Parameters:
name – Volume name.
free – Storage for returned value.
total – Storage for returned value.
- Returns:
0 on success. -1 on error.
- A regs:
return
- Errno:
EINVAL, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT
RLN_LASTKEY¶
-
int ria_rln_lastkey(char *key, unsigned char *action)¶
Returns the raw bytes of the most recently completed input sequence typed by the user during a non-blocking cooked read from
CON:. This includes single characters and multi-byte escape sequences such as arrow, function, and editing keys. Theactionout-parameter reports whether the line editor handled the key as an editing action (non-zero) or passed it through (zero). Reading consumes the captured sequence; the next call returns 0 until another key is typed. Sequences longer than 32 bytes, or any call made while no line read is in progress, return 0.- Op code:
RIA_OP_RLN_LASTKEY 0x30
- C proto:
rp6502.h
- Parameters:
key – Storage for the returned byte sequence.
action – Out-parameter set non-zero if the key triggered an editing action.
- Returns:
Length of key sequence. 0 if no key is available.
- A regs:
return
- Errno:
EINVAL
RLN_PEEK¶
-
int ria_rln_peek(char *peek, unsigned char *pos)¶
Returns the current contents of the line editor buffer and the cursor position within it. The buffer bytes are pushed to the XSTACK. Returns 0 with an empty buffer when no line read is in progress.
- Op code:
RIA_OP_RLN_PEEK 0x31
- C proto:
rp6502.h
- Parameters:
peek – Storage for the returned buffer contents. The C wrapper null-terminates the result, so it must hold up to
RIA_ATTR_RLN_LENGTH+ 1 bytes (256 max).pos – Out-parameter set to the cursor position within the buffer.
- Returns:
Length of the buffer contents.
- A regs:
return
- Errno:
EINVAL
RLN_POKE¶
-
int ria_rln_poke(const char *poke)¶
Feeds a string to the line editor as if the user had typed it. The bytes pass through the same input pipeline as live keystrokes: printable characters are written at the cursor (in overwrite mode while the editor is in its line-edit phase), and recognized editing escape sequences are honored. Any C0 control byte (0x00–0x1F) finishes the input, with two exceptions — ESC (
\33) begins a CSI sequence, and CAN (\30) aborts an in-flight one. Control bytes other than CR (\r) echo in caret notation (^@..``^_``) when the input length is at least 2. LF submits the field like CR but adds no linefeed, which is useful for form input on the last terminal row.- Op code:
RIA_OP_RLN_POKE 0x32
- C proto:
rp6502.h
- Parameters:
poke – Null-terminated string to feed into the editor.
- Returns:
- A regs:
return
- Errno:
EINVAL
EXIT¶
-
void exit(int status)¶
Halt the 6502 and hand the console back to the RP6502 monitor. This is the only operation that never returns; the OS pulls RESB low before the next instruction can execute. The status value is kept for the next ROM and is readable via
RIA_ATTR_EXIT_CODE.Dropping the user back to the monitor is generally discouraged, but calling exit() — or falling off the end of main() — beats locking up.
- Op code:
RIA_OP_EXIT 0xFF
- C proto:
stdlib.h
- A regs:
status
- Parameters:
status – 0 is success, 1-255 for error.
Launcher¶
The launcher is a feature of the RP6502 process manager that lets one ROM
act as a persistent host for all the others. A ROM registers as the launcher
by setting RIA_ATTR_LAUNCHER to 1 via ria_attr_set(). From then
on, the process manager automatically re-executes the launcher ROM whenever
any ROM it launched stops. When the launcher ROM itself stops, the chain
ends, the registration clears, and control returns to the monitor.
The launcher ROM decides what to run next by calling EXEC, optionally passing arguments to the new ROM through argv. The launched ROM reads those arguments back with ARGV.
Two keystrokes stop a running ROM. Ctrl-Alt-Del stops it and clears the launcher registration at any time, always returning you to the monitor — handy for system maintenance. Alt-F4 stops the running ROM and returns to the launcher, or to the monitor if the ROM was run from there. Pressing Alt-F4 while the registered launcher ROM is itself running does nothing; it won’t stop it. That makes Alt-F4 the keystroke for ending a ROM while staying inside your preferred launcher framework, and Ctrl-Alt-Del the one for breaking all the way back to the monitor.
RIA Attributes¶
RIA attributes are 31-bit values identified by an 8-bit ID, accessed with
ria_attr_get() and ria_attr_set(). Both succeed for any
valid attribute ID. Getting or setting an unknown ID returns -1 with
errno set to EINVAL, as does trying to set a get-only attribute.
ID / Name |
Description |
|---|---|
0x00
RIA_ATTR_ERRNO_OPT |
Errno mapping option. Selects which set of errno constants the OS uses. Both cc65 and llvm-mos set this automatically at C runtime startup; assembly programs must set it before making OS calls that can fail. See ERRNO_OPT Compiler Constants for option values. |
0x01
RIA_ATTR_PHI2_KHZ |
CPU clock speed in kHz. Range 100–8000. Changes take effect immediately and revert to the system setting when the ROM stops. |
0x02
RIA_ATTR_CODE_PAGE |
Active OEM code page used by the filesystem, console, and default VGA font. Reverts to the system setting when the ROM stops. If the requested page is unavailable, the system setting is selected; follow a set with a get to confirm the result. One of: 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869. |
0x03
RIA_ATTR_RLN_LENGTH |
Maximum input line length for the stdin line editor. 1–255, default 254. |
0x04
RIA_ATTR_LRAND |
31-bit hardware random number seeded with entropy from the RIA.
Returns a value in the range 0x0 to 0x7FFFFFFF. Suitable for
seeding a PRNG or direct use. The 16-bit |
0x05
RIA_ATTR_BEL |
BEL ( |
0x06
RIA_ATTR_LAUNCHER |
Launcher flag. Set to 1 to register the current ROM as the launcher; set to 0 to deregister. See the Launcher section for full details and usage patterns. |
0x07
RIA_ATTR_EXIT_CODE |
The exit code of the last ROM to exit. |
0x08
RIA_ATTR_SIGINT |
Read-only Ctrl-C latch. Returns 1 if a Ctrl-C has been seen on any console input — UART, USB, or telnet (including the telnet Interrupt Process command) — since the previous get; returns 0 otherwise. Reading clears the latch. Same as RIA IRQ SIGINT. |
0x09
RIA_ATTR_RLN_CAPS |
Caps mode applied to keystrokes by the console line editor. 0 (default) passes characters through unchanged; 1 forces all letters to upper case; 2 inverts the case of letters. Reverts to the system setting when the ROM stops. |
0x0A
RIA_ATTR_RLN_WIDTH |
Terminal width in columns used by the stdin line editor. Setting a non-zero value pins the width and bypasses auto-detect; 0 returns the channel to auto-detect (default). Reverts to 0 when the ROM stops. See RP6502-TERM for how the console manifold probes terminal size. |
0x0B
RIA_ATTR_RLN_HEIGHT |
Terminal height in rows used by the stdin line editor. Setting
and revert semantics match |
0x0C
RIA_ATTR_RLN_SUPPRESS_NL |
Prevents read line from sending a CRLF at the end of input. Useful for using the last terminal line for field input. |
0x10
RIA_ATTR_CLK_RUN_MS |
Read-only milliseconds the 6502 has been running, counted from
the release of reset. Wraps approximately every 24.8 days. This
is the C |
0x11
RIA_ATTR_CLK_RUN_CS |
Read-only 6502 run time in 1/100 second ticks. Wraps approximately every 248 days. |
0x12
RIA_ATTR_CLK_RUN_DS |
Read-only 6502 run time in 1/10 second ticks. Wraps approximately every 6.8 years. |
0x13
RIA_ATTR_CLK_RUN_S |
Read-only 6502 run time in whole seconds. Wraps approximately every 68 years. |
ERRNO_OPT Compiler Constants¶
OS calls set RIA_ERRNO when an error occurs. Because cc65 and llvm-mos
each define their own errno constants, the errno option selects which set
of numeric values to use. Both compilers set it automatically in their C
runtime, and errno in C maps directly to RIA_ERRNO. Assembly
programs must set RIA_ATTR_ERRNO_OPT themselves before any OS call that
can fail.
The OS maps FatFs errors onto errno, and RP6502 emulators and simulators are expected to map their native errors too. The table below lists the FatFs mappings. Because FatFs is so central to the OS, each call is documented with its native FatFs errors to help when cross-referencing the FatFs documentation.
cc65 |
llvm-mos |
FatFs |
|
|---|---|---|---|
option |
1 |
2 |
|
ENOENT |
1 |
2 |
FR_NO_FILE, FR_NO_PATH |
ENOMEM |
2 |
12 |
FR_NOT_ENOUGH_CORE |
EACCES |
3 |
13 |
FR_DENIED, FR_WRITE_PROTECTED |
ENODEV |
4 |
19 |
FR_NOT_READY, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM |
EMFILE |
5 |
24 |
FR_TOO_MANY_OPEN_FILES |
EBUSY |
6 |
16 |
FR_LOCKED |
EINVAL |
7 |
22 |
FR_INVALID_NAME, FR_INVALID_PARAMETER |
ENOSPC |
8 |
28 |
|
EEXIST |
9 |
17 |
FR_EXIST |
EAGAIN |
10 |
11 |
FR_TIMEOUT |
EIO |
11 |
5 |
FR_DISK_ERR, FR_INT_ERR, FR_MKFS_ABORTED |
EINTR |
12 |
4 |
|
ENOSYS |
13 |
38 |
|
ESPIPE |
14 |
29 |
|
ERANGE |
15 |
34 |
|
EBADF |
16 |
9 |
FR_INVALID_OBJECT |
ENOEXEC |
17 |
8 |
|
EDOM |
18 |
33 |
|
EILSEQ |
18 |
84 |
|
EUNKNOWN |
18 |
85 |
Note
cc65 does not define EDOM or EILSEQ; under option 1 the OS reports
both as EUNKNOWN (18).