BFOSIX is a single-(user|process|thread) Brainfuck-based operating system specification. Akin to POSIX, it defines a basic set of primitives (syscalls) for things that a programming language such as Brainfuck needs to rely on the Operating System in.
$
is the only command that BFOSIX adds to Brainfuck command set. This is to ensure a clean separation between the programming language and Operating System. Brainfuck should stay minimal, even if used as a full-fledged OS-aware language. $
has two uses:
- As a syscall-invoking command.
- As
exec
-like command to run the code from some file.
Boring things first.
If you lay out a file name in memory and invoke $
at the first cell of the name, then $
will
- Leave the memory and program pointer the same.
- Immediately execute the Brainfuck code (script thereof) in the chosen file.
- The memory that the script have altered is left as the script left it after terminating (either with
exit
or simply reaching the end of the script). - Once script finishes running, return to the code that invoked
$
and continue running it.
Example of the code: file called “hello”:
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
main program:
+[>>++++++++++[>++++++++++<-]>[>+>+>+>+>+<<<<<-]>++++>+>++++++++>++++++++>+++++++++++<<<<$[-]++++++++++.[-]<<<<]
In this example, the main program invokes the script from the file named “hello” (essentially a “Hello World!”-printing program from Wikipedia), outputs a newline and loops. While not immediately useful (unless you want to clutter your terminal), it shows the point – you can now modularize your programs and invoke the code from other files, sharing the memory between them.
Because of this memory sharing, the exec
-like usage of $
is significantly different from the POSIX exec
: it has no arguments. Thus, scripts invoked by $
should rely of what is in the memory at the moment they are ran. More so: scripts can freely access the memory of the processes that called them. While not exactly a good thing security-wise, this is a feature that allows scripts to look up the “environment” they are running in.
In case the cell that $
is invoked on is a cell with the value of less than 32, the $
call is considered to be a syscall. The number of syscalls to have is implementation-dependent. but all the BFOSIX-compliant systems should have at least the syscalls listed below.
For all the syscalls, a certain notation is used to show memory contents that they require. The rules of this notation are:
|0|
is a cell. In this case, a cell with a value of zero.|...|
is a sequence of cells that I was too lazy to write in full. Imply ranges or whatever makes sense here.|value|
is one value in the cell.|0|1|2|
is a sequence of cells. In this case, these cells have 0, 1, and 2 in them.|...values|
is a sequence of values in a null-terminated sequence of cells.|?value|
is an optional value cell. “Optional” means that syscall will work even without it, so one can omit setting such a cell from the syscall setup. If there are several optional cells one after the other, it means that they all can be omitted. Some of them can be omitted too, starting from the rightmost one.|...?values|
is an optional null-terminated sequence of cells with certain values in them.|?value(x)|
is an optional value that has a default in case it wasn’t provided.|...?filename("a.out")|
means thatfilename
is an optional string defaulting to “a.out”.
Examples:
|a|0|b|?c|...?d|
means “a one-cell value a
followed by the zero cell and cell with value b
. After those, there can be a cell with value c
, and, after it, there can be a null-terminated sequence d
.”
Now to syscalls.
Stops executing current Brainfuck script/process. Returns to the parent script/process. If there’s no parent process, shuts BFOSIX system down.
|0|...?args|
No arguments. Implementations are welcome to add errno
and other UNIX-y exit
niceties as implementation-dependent args
.
|0|
Nothing, leaves zero (syscall code) intact.
|1|...filename|0|...?flags|
where filename
is either a string of text naming the file, an IPv4 address like
|ipv4one(0)|ipv4two(0)|ipv4three(0)|ipv4four(0)|
or IPv6 address like
|ipv6one(0)|ipv6two(0)|ipv6three(0)|ipv6four(0)|ipv6five(0)|ipv6six(0)|ipv6seven(0)|...|ipv6sixteen(0)|
or something else that the implementation could allow, including non-TCP/IP addresses.
Sets the input/output streams of Brainfuck program (the one used by ,
and .
) to the file/socket named filename
.
Behavior differs between the cases of file and socket:
- If filename if a null byte, set input/output streams to the default (
stdin
/stdout
) value. - If
filename
is a name of the file, set the stream to the stream of its contents. When the file end,,
will store the null byte into the memory. - If
filename
is an IP address, connect and listen on it and fetch characters with,
one by one. If the message is over,,
reads a null byte. After this,,
can be invoked again to start listening and reading the next message.- Using
.
on an IP address or other network address should stark a new packet terminated by the null byte.
- Using
The flags
are implementation-dependent, and may be set to (non-exhaustive list):
- Set input and output separately.
- Allow overwriting/appending to the file.
- Setting the offset into the file.
|0|
Leaves memory empty.
|2|?year(0 i.e. 1900)|?month(1 i.e. Jan)|?day(1)|?hour(0 i.e. midnight)|?minute(0)|?second(0)|
If at least some of the year
, month
, day
, hour
, minute
, second
are present, set the system time to the respective values and defaults. The year starts counting from 1900, so 173 in the year
cell would be 2073, while 255 (the maximum possible cell value for 8-bit implementations) caps the existence of BFOSIX to 2155. Hopefully we’ll get a new specification by then :)
If none of the time cells are set, time
fills all of those with the current date and time. In case of the time of writing (Thu, 02 December of 2021, 01:55:49 AM), it would write 121, 12, 2, 1, 55, and 49 to the six cells after the syscall code.
In both cases (no time cells and any of those), syscall cell is erased. In case of some cells being set at the time of the call, they are erased too.
If called with non-empty arguments, erases all the arguments
|0|
otherwise, returns current date in the format
|2|year|month|day|hour|minute|second|
|3|...?seed|
No arguments. Implementations are welcome to have an additional seed
argument to generate the random value from it.
Put the random value into the cell with the syscall code. It is implementation-dependent whether the random value generation is potato random, pseudo-random, almost random, or absolutely random.
|rand|
Random number.
Implementations are welcome to introduce new syscalls in case those are considered useful. However, the maximum syscall number should never exceed 32, so that it never interlaces with the area of printable characters and files. Other than that, there are no restrictions on what those syscalls should look like and how they should behave. One can even make their Brainfuck-based OS to be a multi-(user|process|threaded) one, all with the help of additional syscalls.
Implementations can add special files (akin to UNIX /dev/null
and /dev/random
) that will behave in special ways when read/written to. Those, however, should not replace syscalls like time
and rand
, they can only add to those.