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.
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 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 | @X |
#4 | Reset (seek to position 0) file @X | @X |
#5 | Seek file @X to position :X | @X |
#6 | Open TCP socket using name in ,X and mode in :X | @X |
#7 | Toggle newline handling | - |
System calls #3 and #6 take their name from ,X - call #3 interprets the name as a local file path, while call #6 interprets it 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. The file access mode in :X is one of:
Mode | Meaning |
---|---|
#97 | Appending (like #114, but always at end of file); file is created if it does not exist |
#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 |
#353 | Appending (like #97) |
#370 | Reading out, like #114, but the file is not 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.-3" - the source for this example can be found in
examples/syscall1.si
in the distribution.
A more interesting example (from examples/http-get.si
) 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 intercal.freeshell.org:80 /index.htmlor provide your own input: a SERVER:PORT in the first line, and a path within the server in the second line.
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