CLC-INTERCAL Reference

... The system call interface

Table of contents:

Compiling a program with system call support

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.

Using the system call interface

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 .1
would 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 ,6
executes 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.

List of available calls

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
The resemblance of the first four codes with the ASCII codes for the letters "a", "r", "u" and "w" is purely coincidental and should not be construed as having any meaning; likewise, the resemblance of the other four codes with the first four plus #256.

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 UP
The 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.si
then run it with (for example)
	./http-get.io
	intercal.freeshell.org:80
	/index.html
or 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