11 Operand Overloading ********************** INTERCAL-72 C-INTERCAL CLC-INTERCAL J-INTERCAL ------------------------------------------------------------------------ no version 0.26+ version 0.05+ no (Operand overloading in C-INTERCAL is nowhere near as advanced as it is in CLC-INTERCAL. This chapter only explains the partial implementation used by C-INTERCAL; for a full implementation, see CLC-INTERCAL and its documentation.) Operand overloading is a method of using a onespot or twospot variable as a substitute for an expression. When a variable is overloaded to an expression (which could be another variable, or something more complex), any uses of that variable cause the expression to be substituted instead. At the beginning of the program, all variables stand for themselves; so `.1' really does mean `.1', for instance. The meaning of a variable can be overloaded using the slat operator (`/'), which is the same in both Princeton and Atari syntax: it is a binary operator whose left argument must be a onespot or variable and whose right argument can be any expression. The slat operator returns the true value of its left argument, but as a side effect, changes the meaning of its left argument to be its right argument. Here is an example: DO .1 <- .2/'.3~.4' The example causes `.2''s true value to be assigned to `.1' (unless of course `.1' is read-only), but also causes `.2' from then on to actually mean `'.3~.4'', except when it's the left operand of a slat operator. So for instance, `DO .1 <- .2' would actually assign `'.3~.4'' to `.1'. Somewhat confusingly, this also works in the other direction; `DO .2 <- .1' would assign `.1' to `'.3~.4'', which would have the effect of changing the values of `.3' and `.4' so that `'.3~.4'' had the correct value, or throw an error if it couldn't manage this. (The general rule in this case is that any variable or constant in the expression that overloads the variable is at risk of being changed; this is known as a `reverse assignment'. Code like `DO .1 <- .1/#1' is entirely capable of changing the value of `#1', although to protect new INTERCAL users C-INTERCAL will refuse to carry out operations that change the value of constants unless a command-line switch (*note -v::) is used to give it permission. In C-INTERCAL, changing the value of a constant only changes meshes with that value, but in CLC-INTERCAL it can also change non-mesh uses of that constant, so doing so is not portable anyway.) When multiple overloading rules are in effect, they are all applied; overloading `.1' to `'.2~.3'' and `.2' to `'.3$.4'' will cause `.1' to refer to `''.3$.4'~.3''. However, this expansion stops if this would cause a loop; to be precise, overloading is not expanded if the expansion is nested within the same expansion at a higher level (so `.1/.2' and `.2/.1' together cause `.1' to expand to `.2', which expands to `.1', which cannot expand any further). In C-INTERCAL, the expression on the right hand side of a slat is not evaluated and not expanded by operand overloading. `STASHING' a variable causes its overloading information to be stashed too; `RETRIEVING' it causes its overload rule to also be retrieved from the stash (or any overload rule on the variable to be removed if there wasn't one when the variable was stashed). Overloading a onespot variable to a twospot variable or vice versa is possible, but the results are unlikely to be predictable, especially if a onespot variable is used to handle a twospot value. Possible outcomes include truncating the value down to the right bitwidth, throwing an error if a value outside the onespot range is used, and even temporarily handling the entire twospot value as long as it doesn't end up eventually being assigned a value greater than twospot. Note that reverse assignments can cause unpredictable behaviour if an attempt is made to reverse-assign the same variable twice in the same expression. In particular, sequences of commands like `DO .1 <- .2/'.3$.3' DO .2 <- #6' are liable to succeed assigning garbage to `.3' rather than failing as they ought to do, and likewise any situation where a variable is reverse-assigned twice in the same expression may assign garbage to it. This behaviour is seen as unsatisfactory, though, and plans exist to improve it for future versions. 12 PIC-INTERCAL *************** INTERCAL-72 C-INTERCAL CLC-INTERCAL J-INTERCAL ------------------------------------------------------------------------ no version 0.26+ no no PIC-INTERCAL is a simplified version of INTERCAL designed especially for embedded systems, designed to minimise code and data usage by INTERCAL programs so that they can fit on devices whose memory is measured in bytes rather than megabytes. (It is named after the first microcontroller for which code was successfully generated, and which influenced the choices of commands, the PIC16F628 manufactured by Microchip, and is most likely to be portable to other microcontrollers in the same range.) C-INTERCAL only compilers as far as C code when producing PIC-INTERCAL; it is up to the user to find the appropriate cross-compiler to translate this C into the relevant dialect of machine code. (Two header files in the distribution, `src/pick1.h' and `src/pick2.h', don't have any affect on the compiler but are referenced by the generated code, and the intent is for the user to change them to suit the behaviour of the PIC compiler used, because these are not as standardised as C compilers for everyday systems.) There are several restrictions on PIC-INTERCAL programs: * It's impossible to store more than one item in the STASH for any given variable. * Many errors are not checked at runtime; it is the programmer's job to ensure that runtime errors are avoided. (The few errors that are detected will send the program into an infinite loop, as there is no sensible way to exit from a PIC program.) * Double-oh-sevens are not randomised, but instead depend merely on whether the probability given is greater than 50% or not. * The NEXT stack is limited to 16 entries rather than the usual 80. * `ABSTAIN' and `REINSTATE' still work, but cannot be computed `ABSTAIN's, and will not necessarily work when used to affect the system library or calls to it. * `READ OUT' and `WRITE IN' don't work. (See below for a replacement.) * `COME FROM' and `NEXT FROM' must aim at a label, not an expression or gerund. * Multithreading and TriINTERCAL cannot be used on PICs. (Operand overloading can be used, but only if your PIC cross-compiler supports function pointers; some don't.) In order to provide I/O capabilities, a new command `PIN' is available. It controls up to 16 I/O pins on the PIC or other embedded system; an I/O pin is capable of receiving or sending voltages to an electrical or electronic circuit. This explanation assumes that the device being controlled is a PIC16F628A, and therefore has its pins in two blocks of 8 named `PORTA' and `PORTB'; for other microcontrollers, adapting the code in `src/pick1.h' is likely to be necessary to tell the compiler how to control the I/O pins, and the way in which this done will affect which I/O pins it is that the program will end up being able to communicate with. The `PIN' command takes one twospot variable as its argument, like this: DO PIN :2 The twospot variable is conceptually divided into 4 blocks of 8 bits. The highet two blocks control the directions of the pins in `PORTB' (most significant block) and `PORTA' (second most significant block); a 1 on any bit means that the corresponding I/O pin should be set to send data, and a 0 means that it should be set to receive data. The lower two blocks control the values on the pins that are sending (and are ignored for receiving pins); the second least significant block controls `PORTB' and the least significant block controls `PORTA', with a 1 causing the program to set the output voltage to that of the microcontroller's negative voltage supply rail, and a 0 causing the program to set the output voltage to that of the microcontroller's positive voltage supply rail. (These voltages may vary on other systems; consult your system's datasheet and the changes you made to the header files.) After setting the pins, the `PIN' command then reads them as part of the same operation, this time setting the values of the lower blocks that are receiving, rather than setting the pins from the lower blocks that are sending. However, 1 and 0 bits on all bits of the twospot variable have the opposite meaning when doing this, so that 1 means receiving/positive voltage rail and 0 means sending/negative voltage rail. There is no way to input without output, or vice versa, but it's trivial to just send the same output again (which has no effect, because the voltage on sending pins is maintained at the same level until it is changed), or to ignore the input received.