Table of contents:
Syscall is a standard compiler extension, so programs must include the appropriate compile option to use it.
The command-line compiler tool, sick, automatically enables the Syscall extension if the program suffix includes the letter "s". Alternatively, if you are not relying on sick's guesses and are specifying a list of preloads yourself, just add -psyscall to your command line.
If the INET extension is loaded, either by including a "r" in the suffix or by asking for the internet preload, it extends the syscall preload with its own calls; this is a generic mechanism which can be used by any extension, but currently is only used by the INET extension.
Using the INTERCAL Calculator, INTERCALC, you can easily add Syscall support by selecting "syscall" from the Options menu or by adding -osyscall to the command line.
The syscall compiler object, which implements this extension,
uses direct access to the runtime's internal state and other undocumented
methods to extend the runtime as necessary to support its functions.
If you really want to know, look at the assembler source in
INTERCAL/Include/syscall.iasm
in the CLC-INTERCAL-Base distribution.
The system call interface adds a hidden "DO NEXT FROM (666)" somewhere in your operating system. This means that when your program uses label (666) you do a system call as soon as the corresponding statement is executed.
The system call interface will access your program's registers to execute. All registers accessed this way will have the same number, for example .1 and ;1 - what number is actually used depends on the last assignment you've made just before the system call. For example, the fragment:
DO :6 <- #1 (666) DO READ OUT .1would use registers with number 6 - because the last register being assigned to was :6. Since one must always provide a system call number in a spot register, it is good practice to assign this number in a statement with label (666) - this will have the side effect of making sure the last assignment is to the correct register number. For example:
(666) DO .6 <- #2 DO READ OUT ,6executes system call #2 (because .6 contains the system call code), which just happens to store its result in ,6 (because it returns a tail array, and it would naturally use the tail array with the same register number). The second statement would therefore produce "CLC-INTERCAL" as output, since system call #2 returns the name of the compiler.
In the following list, we use a cross ("X") instead of a register number, to remind the reader that the actual number used depends on the most recent assignment before the system call was executed. Also remember that the system call number is in the spot register (.X). After the list of calls, a more extensive description, or even an example, may follow for some of the calls.
Number | Description | Registers modified |
---|---|---|
#0 | No operation | - |
#1 | Store version number in ,X | ,X |
#2 | Store compiler name in ,X | ,X |
#3 | Open file using path in ,X and mode in :X; see below for the meaning of the file mode | @X |
#4 | Reset (seek to position 0) file @X | @X |
#5 | Seek file @X to position :X | @X |
#7 | Toggle newline handling | - |
#8 | Sleep :X microseconds; this ignores the blocking / non-blocking state of the program and always blocks all threads | - |
#9 | Sleep :X microseconds; this ignores the blocking / non-blocking state of the program and only blocks the calling thread | - |
System call #3 interprets ,X as the name of a local file and :X as the file access mode:
Mode | Meaning |
---|---|
#97 | Appending (like #114, but always at end of file); file is created if it does not exist |
#100 | (Since CLC-INTERCAL 1.-94.-1 and only supported if the INET extension is installed) Writing in (that is, input only); if the file does not exist, it will look for it somewhere; the call could block while the compiler goes looking for the file; the blocking / non-blocking state determines whether this will block all threads or the calling thread only; if the file exists but is a dangling symlink, the system will go and look for the link's target instead |
#114 | Reading out (that is, output only); file is created or truncated |
#117 | Updating (that is, reading in and writing out); file is created if it does not exist |
#119 | Writing in (that is, input only); file must already exist |
Mode | Meaning |
---|---|
#353 | Reading out to the file appends at the end; writing in can happen at any location. The file is created if it doesn't exist. |
#356 | (Since CLC-INTERCAL 1.-94.-1 and only supported if the INET extension is installed) Like #100, will find the file somewhere; once it's been found, it can be read out and written in at any position |
#370 | Like #353, but the file is truncated if it already exists |
#373 | Updating; same as #117 |
#375 | Updating, same as #117 |
System call #7 affects all alphanumeric READ OUT in sick mode; it is not localised to a specific filehandle. Normally, after an alphanumeric READ OUT the runtime adds a newline character; executing system call #7 disables this; executing the call again re-enables the newline.
Here is a simple example of using system calls #1, #2 and #7 to print the name and version number of the compiler:
(666) DO .1 <- #2 DO ,2 <- #3 DO ,2 SUB #1 <- #91 DO ,2 SUB #2 <- #95 DO ,2 SUB #3 <- #68 (666) DO .3 <- #1 DO ,4 <- #3 DO ,4 SUB #1 <- #91 DO ,4 SUB #2 <- #95 DO ,4 SUB #3 <- #66 (666) DO .1 <- #7 PLEASE READ OUT ,1 + ,2 + ,3 + ,4 PLEASE GIVE UPThe first line uses system call #2 to get the compiler name into ,1; the next four lines prepare ,2 to contain a single space; after that, system call #1 provides the compiler version number; then ,4 is prepared to contain a newline; system call #7 disables the automatic newline. finally, the four arrays are READ OUT, producing something like "compiler-name version", or, at the time of writing, "CLC-INTERCAL 1.-94.-1" - the source for this example can be found in
doc/examples/syscall1.si
in the CLC-INTERCAL-Docs
distribution.
A more interesting example (from examples/http-get.rsi
) shows
how to use system call #6 to obtain a network socket, send a request to it
and print the results. Compile it with:
sick -lObject http-get.sithen run it with (for example)
./http-get.io google.com:80 /or provide your own input: a SERVER:PORT in the first line, and a path within the server in the second line. Note that https is not supported and many sites will just reply with a redirect, which won't be followed. Implementing TLS is left as an exercise to the reader.
Here follows the source code for this example:
PLEASE NOTE: READING SERVER:PORT DO ,1 <- #1024 DO WRITE IN ,1 PLEASE NOTE: READING PATH DO ,2 <- #1024 DO WRITE IN ,2 PLEASE NOTE: OPENING HTTP CONNECTION (OR SPLAT) DO :1 <- #117 (666) DO .1 <- #6 PLEASE NOTE: SENDING HTTP REQUEST PLEASE DO ;1 <- #4 DO ;1 SUB #1 <- #18 DO ;1 SUB #2 <- #247 DO ;1 SUB #3 <- #365 DO ;1 SUB #4 <- #277 PLEASE DO ,3 <- #14 DO ,3 SUB #1 <- #91 DO ,3 SUB #2 <- #95 DO ,3 SUB #3 <- #68 DO ,3 SUB #4 <- #84 DO ,3 SUB #5 <- #80 DO ,3 SUB #6 <- #80 DO ,3 SUB #7 <- #86 PLEASE DO ,3 SUB #8 <- #91 DO ,3 SUB #9 <- #93 DO ,3 SUB #10 <- #87 DO ,3 SUB #11 <- #92 DO ,3 SUB #12 <- #86 DO ,3 SUB #13 <- #72 DO ,3 SUB #14 <- #66 (666) DO .1 <- #7 DO READ OUT @1 + ;1 + ,2 + ,3 PLEASE DO ,3 <- #4 DO ,3 SUB #1 <- #91 DO ,3 SUB #2 <- #95 DO ,3 SUB #3 <- #72 DO ,3 SUB #4 <- #66 PLEASE DO ;1 <- #6 DO ;1 SUB #1 <- #29 DO ;1 SUB #2 <- #1098 DO ;1 SUB #3 <- #2574 DO ;1 SUB #4 <- #692 DO ;1 SUB #5 <- #105 DO ;1 SUB #6 <- #213 DO READ OUT @1 + ;1 + ,1 + ,3 DO READ OUT @1 + ,3 PLEASE NOTE: GETTING RESULT AND PRINTING IT PLEASE DO ;1 <- #1024 PLEASE DO .1 <- #0 DO COME FROM .1 DO WRITE IN @1 + ;1 DO READ OUT @2 + ;1 (1) DO .1 <- ';1 SUB #1 ~ ;1 SUB #1' ~ #1 PLEASE GIVE UP
The program is mostly self explanatory, but we'll explain it anyway. The first few lines get the two lines you type; then system call #6 is used to open a TCP socket; note that the last assignment is to .1, so that the access mode is taken from :1 (#117 - reading out and writing in) and the network address from ,1 (whatever you typed).
The next block creates the string "GET " in ;1 and " HTTP/1.0\r\n" in ,3 (\r\n represents a carriage return, line feed sequence). A call to system call #7 disables the automatic newline, which we must do because network servers expect a carriage return, line feed instead. We are now ready to send out ;1 + ,2 + ,3 or in other words "GET path HTTP/1.0" to the server (note that @1 has been connected to the network socket).
Following, the carriage return, line feed sequence is stored in ,3 and the string "Host: " is stored in ;1 - this means that the next READ OUT sends "Host: server:port" to the HTTP server, just as it expects to be able to resolve the virtual host name, if necessary; the READ OUT ,3 just produces a blank line, to indicate the end of the request.
The last block of the program writes the reply from the server in and reads it out to the user; this is done by dimensioning ;1 to hold 1024 characters (you can increase the buffer size, of course), writing that in using the TCP socket filehandle, and reading it out again using the standard read filehandle (@2); the runtime signals that a block is less than 1024 characters by filling all unused elements of ;1 with #0 - at end of file, no elements will be used, so ;1 SUB #1 will be #0 (the runtime guarantees that this element will be nonzero if there has been any data received from the server). Therefore the assignment to .1 results in the computed COME FROM a few lines earlier to execute if there is more data, or to do nothing at end of file.
More system calls may be planned for future versions of CLC-INTERCAL, but we aren't telling at the moment
The following system calls are available if the INET extension is loaded, otherwise they will produce a splat.
Number | Description | Registers modified |
---|---|---|
#6 | Open TCP socket using name in ,X; before CLC-INTERCAL 1.-94.-1 this call always blocked the whole program until the connection succeeded or failed; starting with 1.-94.-1 this call checks the blocking / non-blocking state of the program and uses that to decide whether to block all threads or just the calling thread. Non-blocking behaviour may not be supported on older versions of perl and the name lookups can still block (use a non-blocking CASE to do the lookup separately). Register :X contains a file access mode, like system call #3. | @X |
#900 | No operation; this could be used to verify that the INET extension is loaded as it will splat if not, but if it is loaded will have no effect | - |
#901 | Set hop limit for multicast to :X | - |
System call #6 interprets ,X as a SERVER:PORT string, where SERVER can be a DNS name or IP address, and PORT can be a port number or service name. Register :X is passed to the underlying filehandle code which checks it for correctness, however the number is curretly ignored by the underlying network code. For best results, set :X to #117 in case a future version does something with this number.
System call #901 takes an IPv6 multicast group encoded in a 32 bit
number in the same way as the CASE
statement, however the bits which would normally contain the
interface index contain the hop limit instead. For example,
remembering that ff02::1 is always encoded as #28672 ¢ #61456
plus the interface index, the following changes its hop limit to 4:
This is because #28674 ¢ #61456 is the same as #28672 ¢
#61456 plus #4.
DO :1 <- #28674 ¢ #61456
(666) DO .1 <- #901