.he Fido/FidoNet/FidoTerm Script Language Copyright Tom Jennings   #

Tom Jennings
Fido Software
164 Shipley
San Francisco CA 94107


               SCRIPT LANGUAGE MACHINE MODEL

Fido/FidoNet's script language is a machine language, and like allچ
machine languages, there is an underlying "model" machine thatچ
the script instructions manipulate. The buckets and Beetles, thatچ
is.

The script machine model has the following components at yourچ
disposal:

NAME		DESCRIPTION

&A		The numeric "accumulator", values 0 - 65535
&C		The "clock", counts up, in seconds
&T		The "trap register", 0 or 1 - 65535 seconds
&E		The "error register", either 0 or 1

&R		The general purpose Recognizer
&S		The "shift register", 20 characters,
&1 - &8		General purpose registers
stack		10 levels of data stack


&A:	The "accumulator" (such a quaint word) is a simpleچ
register for storing or manipulating numbers. It can be set toچ
any value from 0 to 65535. It can be stored into any otherچ
register. There are instructions to test when the accumulator isچ
zero. 

&T:	The "trap register" is part of a mechanism to breakچ
endless loops; the "TRAP" instruction is used to set a failsafeچ
if a script or part of a script takes too long to execute.

&E:	The "error register" can be either 0 or 1, with 1چ
generally indicating an error condition. There are instructionsچ
to set and test the error register.

&R:	The Recognizer can be used to store text or numbers.

&1 - &8: These are general purpose registers, and can holdچ
numbers or text. They are used to pass parameters to subroutines.چ
Each subroutine level has it's own set of these registers. When aچ
subroutine is called, the initial values are those of the callingچ
routine.

&S:	The "shift register" is a special register that containsچ
the last 20 characters received from the modem, and is whatچ
patterns are matched against. Characters are shifted in from theچ
right, and the left most character "falls off" the end. You canچ
also store text in &S and use the "IF" instruction to do stringچ
comparisons.

&1 through &8 are "local variables"; they are not shared amongstچ
all subroutine levels. When a CALL is executed, the called scriptچ
"inherits" the calling scripts initial values for theseچ
registers, but any changes made are lost upon RETURN. 


The stack is a general purpose "push down" stack; you PUSH itemsچ
onto the stack, and POP them off. Items can only be accessed fromچ
the top of the stack. There is room for up to ten items on theچ
stack. If there are (for instance) five items on the stack, toچ
get at the first one pushed (the "bottom" of the stack) you mustچ
first pop off the four on "top" of it.
.pa

                        INSTRUCTION SYNTAX

All instructions are of the form:


INSTRUCTION	OPERAND	OPERAND

Blank lines or lines beginning with a semicolon are ignored.چ
Lines beginning with a colon (label statements) are ignored whenچ
processing instructions.

Operands are usually text strings, and all strings should beچ
quoted. 


                    INSTRUCTION PROCESSING

The script processor is basically a text processor. The macroچ
language applies to all commands at all times. Anything to theچ
right of the instruction itself is expanded by the macroچ
processor. 

Macros are expanded from left to right, once. There are twoچ
special tokens the macro processor understands:

	&(one letter register name)
	\(special text character)

The language chosen allows just about every disgusting trickچ
possible with machine language; self-modifying code, computerچ
GOTOs, etc. I'll dispense with the formality, and show someچ
generalized examples and leave it at that:

	Message "This is a string"

The MESSAGE command merely displays what it's given on theچ
command status line. It makes for a simple example. In this case,چ
centered in the status line you'd see:

		This is a string

If the recognizer (&R) contained the string "HELLO DUDE" theچ
instruction:

	Message "The recognizer contains &R"

Would display:

	The recognizer contains HELLO DUDE

To display the same thing, but with quotes surrounding theچ
contents of the recognizer, you would use:


	Message "The recognizer contains \"&R\""

Displayed as:

	The recognizer contains "HELLO DUDE"


Note the \ before the quote character merely quotes what follows.چ
There are some convenient exceptions:

	\r		carriage return, 13 decimal
	\n		line feed, 10 decimal
	\e		escape, 27 decimal
	\b		backspace, 8 decimal
	\c		etx (Control-C), 3 decimal
	\1		SOH character, decimal 1
	\127		DEL character, decimal 127
	...		...

If you don't like that, ("C" programmers will like it though) youچ
can enter control characters like so:

	^A		1 decimal, ASCII SOH
	^M		carriage return, 13 decimal
	^J		linefeed, 10 decimal
	...		...


The ADD instruction adds a value to the accumulator. For example:

	ADD 27

Would add 27 to the accumulator; if it had contained 3, it wouldچ
now contain 30. If the &2 register contained "34", the following:

	ADD &2

Would add the numeric value 34 to &A. If &2 contained "ABRACADAB"چ
then it would add zero to &A.

Here are some further examples. Assume first:

&A contains 20
&R contains "Text"
&1 contains "764-1629"
&2 contains 300

INSTRUCTION 			OPERAND EXPANDED
message "sample text"		sample text
message &R			20
message "pattern is &r, OK?"	pattern is 20, OK?
message "&A&R&A"		20Text20
message "Dialing &1 at &2 baud"	Dialing 764-1629 at 300 baud


You can take this to absurd extremes. The instruction JMP <label>چ
passes execution to the line following the line containingچ
:<label>. You can take advantage of this by using a "computedچ
goto". The command:

	jmp "&R"

Would attempt to jump to a line:

:TEXT
.pa
.sh Pattern Matching
                      PATTERN MATCHING

The main purpose of Fido/FidoNet's script language is patternچ
matching; looking for particular strings of characters receivedچ
from the modem. All of the script language instructions areچ
intended to work together to do this.

The single most important component of the script machine is theچ
Shift Register. The shift register contains the last 20چ
characters received from the modem, and works like this:

Assume that with your modem and Fido/FidoNet, you just called intoچ
and connected with some bulletin board system, and are about toچ
be asked to log in. 

As characters are received they are put into the right hand endچ
of the shift register; the character on the left edge of theچ
shift register is lost. For example, if the bulletin board signonچ
message you'd see on your screen was:

WELCOME TO THE ACME HOG THROWERS BBS

AT THE PROMPT ENTER YOUR NAME AND PASSWORD:


Lots of times a simple "wait until the word XYZ" will do exactlyچ
what you want. This sort of thing is easy to write scripts for,چ
and even in complex scripts is a very useful function. 

The problems start when you need to base different actions on aچ
number of possible patterns, not just one. A simple "MATCH"چ
instruction just won't do it, and brute force techniques ofچ
searching through a screenful of text ("rummaging") has seriousچ
limitations.


In Fido/FidoNet the technique used involves a 20 character arrayچ
called the "shift register", referred to as "&S" in the scriptچ
language.

The shift register changes after each character received from theچ
modem; in the example above, W, E, L, C, O, M, E, etc. Part wayچ
through the above message being displayed, the shift registerچ
would look like:

	....
&S	"                 WEL"
&S	"                WELC"
&S	"               WELCO"
&S	"              WELCOM"
	....
&S	"LCOME TO THE HOG THRO"
	....

Fido/FidoNet compares patterns you specify against the right hand endچ
of the shift register; this means that for each characterچ
received (therefore a new string each time) you can look for anyچ
reasonable number of possible patterns. ("Reasonable" will beچ
discussed later ...)



In this example, to do an automatic login, once we see the wordچ
"PASSWORD:", we can output thename and password. This is a commonچ
and simple case. The instruction "MATCH" will do the job.

"MATCH" compares text you specify against the contents of theچ
shift register. For this example we'd use "WORD:". A string matchچ
is used, so you only need to use enough characters to make itچ
unique; the string "WORD:" does not appear anywhere else in theچ
initial signon message. (Actually, ":" will work fine here, butچ
it's a good idea to use at least a couple of characters; "RD:"چ
would be even better.)

	MATCH "WORD:"

To continue the example above, as characters come in from theچ
modem they shift through the shift register. Fido/FidoNet isچ
executing the "MATCH" instruction above, and the shift registerچ
looks like:

	....
match	                WORD:
&S=	"OUR NAME AND PASSWOR"

match                   WORD:
&S=	"UR NAME AND PASSWORD"

match	                WORD:
&S=	"R NAME AND PASSWORD:"

For each character received, the "MATCH" instruction is comparingچ
it's pattern ("WORD:") against the contents of the shiftچ
register. When the patterns match, the next line of the scriptچ
file is executed, which in this case could be an instruction toچ
output your name and password.
.pa
                COMPLEX PATTERN MATCHING

You don't need to understand the how and whys of the shiftچ
register to use the "MATCH" command, and for most casual scriptچ
writers that will be all you need.

A good example of the limitations of such a simple system is aچ
script that dials phone numbers with a Hayes modem using "AT"چ
commands, and responding to the many possible "result codes".چ
(This example assumes a basic understanding of Hayes modemچ
commands.)

The dialing part is easy; you just issue the ATDT command and theچ
phone number. The problem arises when you try to interpret theچ
results of that command, such as busy, no answer, connecting atچ
various baud rates, errors, etc. A simple "MATCH" instructionچ
just won't work.

For example the US Robotics Courier HST has about a dozenچ
possible responses to a simple dial command; any script you writeچ
must be able to handle them all. That's what we'll do here.

Dialing a US Robotics Courier HST

The command we'll use, for simplicity, is:

	"AT DT 764 1629\r"

("\r" means "carriage return, CR, decimal 13)

The modem can respond with any of the following result words:

	CONNECT			(300 baud)
	RING			(incoming call)
	NO CARRIER		(failed to connect)
	ERROR			(bad command given)
	CONNECT 1200
	NO DIAL TONE
	BUSY
	NO ANSWER
	CONNECT 2400
	RINGING
	VOICE
	CONNECT 9600

This is where you take advantage of the shift register. Theچ
script fragment below handles the problem at hand and isچ
explained below. You will be using this SAMPLE ... IF ... GOTOچ
loop to do most complex string compares.


	&T= 60
	&C= 0
	TRAP dial-failed
	output "ATDT 764-1629\r"
:get-result
	sample
	if "NO" dial-failed
	if "ERR" dial-failed
	if "12" conn-1200
	if "24" conn-2400
	if "96" conn-9600
	if "ECT\r" conn-300
	if "BU" redial
	if "VO" dial-failed
	if "RING\r" dial-failed
	goto getresult

:dial-failed
	&A= 0
	return 1

:conn-300
	&A= 300
	goto connect

:conn-1200
	&A= 1200
	goto connect

:conn-2400
	&A= 2400
	goto connect

:conn-9600
	&A= 9600
:connect
	$$ "b &A\r"
	message "connected at &a baud"
	return 0


"SAMPLE" is a special instruction for use in SAMPLE/IF/JMP loops,چ
and causes one character to shift into the shift register. (Theچ
"MATCH" instruction and most others do this automatically, butچ
"IF" doesn't.) You should have one, and only one, "SAMPLE"چ
instruction before each set of "IF" instructions; if you haveچ
none, the shift register will not ever change, and if you haveچ
more than one you will miss patterns.

"IF" compares the given pattern against the current contents ofچ
the shift register, and branches to the specified label if aچ
match is made.

The patterns searched for by the "IF" instructions need to beچ
mentioned. You could search for the entire modem responseچ
("CONNECT 1200" instead of just "12") but the script will runچ
faster is short strings are used. Use as few as possible is theچ
general rule. The "NO" pattern will match any of the following,چ
which all mean no connection: NO CARRIER, NO DIAL TONE, NOچ
ANSWER. The 300 baud connection result code is "CONNECT", hard toچ
distinguish from "CONNECT 1200" because the "IF" will matchچ
"CONNECT" without waiting for the "1200" or whatever follows. Theچ
"ECT\r" means that it will only match that word at the end of aچ
line, that is, followed by a CR character.

A loop of this length will run pretty slowly. It really shouldچ
have a "timeout" check, using the &C clock, to limit the dialingچ
attempt to a minute or so, and this will slow it further.

To accomodate this, Fido/FidoNet has rather huge buffers, and usingچ
the explicit "SAMPLE" instruction means that characters won'tچ
speed through the shift register faster than you can test them. 

With a loop of approximately this size, it would take a fewچ
thousand characters at continuous 9600 baud to cause characterچ
loss. In the example above, speed is not a problem; the modemچ
issues very little text.

SAMPLE/IF/JMP loops should be as tight as possible. This isچ
interpreted, so keep that in mind; keep comments out of loops,چ
keep labels and operands short. Use as few "IF"s as possible.

If you need to do such a series of compares through a largeچ
amount of text at high speed, it is best to cut down the search,چ
for example by issuing commands to reach some intermediate pointچ
from which you can do your multiple compares.
.pa
               FURTHER NOTES ON PATTERN MATCHING

The strings you provide to the "MATCH" and "IF" instructions haveچ
some characteristics that make for easier comparisons.

All searches are case IN-sensitive. "ANC" matches "anc", etc. Youچ
can specify it in your script in either way.

"?" is a wildcard character; it mean match any character.چ
"CONNECT ??00" will match "CONNECT 1200", "CONNECT 2400", etc.

Control characters are treated just as any other character. "Endچ
of line" characters will vary with the bulletin board program orچ
whatever it is you call; Fido for instance is like most BBSs, andچ
uses a CR/LF sequence (decimal 13, decimal 10). You can searchچ
for "\r" or "\13" or for "\r\n" or "\13\10", but keep in mindچ
that other systems could use LF/CR, or other combination.

HINT: You can store strings directly in to the Shift Register andچ
then test it with the "IF" command. For instance, user input fromچ
the "INPUT" command copied into &S could be tested for aچ
particular value.

.pa
.sh Register and Arithmetic Instructions

These are the most basic instructions to change the contents ofچ
the various registers. Please note that the space after the "="چ
is signifigant.

&S= text
&1= text or number
&2= text or number
&3= text or number
...
&8= text or number
	Set the specified register. Numbers are really textچ
strings, but you can treat them either way. The string must beچ
under 20 characters.

&A= value
	Set the accumulator to the specified value. Theچ
accumulator is numeric only.

&B= value
	Set the baud rate register to the specified value; if theچ
value is not a valid baud rate then the &B register will notچ
change. 

&C= value
	Set the free-running clock to an initial value. The clockچ
counts up continuously every second. 

&T= value
	Set the trap register to the specified value. Setting itچ
to zero disables the trap.

ADD value
SUB value
	Add or subtract the value to the accumulator.

PUSH text
	Puts the text onto the top of data stack. There are onlyچ
10 levels so watch out. Anything can be pushed onto the stack.

POP register
	Take the top item off the stack and place it in theچ
specified register. If you try to put non-numeric text into aچ
numeric register it becomes "0". You cannot POP items not PUSHed.

.pa
.sh Flow Control Instructions

TRAP label
	This instruction is used to set a time limit on scriptچ
execution, usually loops searching for a pattern. The trap usesچ
the &C clock. 

The &T trap register is set to the specified number of seconds,چ
and the &C clock is reset. Before each script instruction isچ
executed, the clock is compared against the trap time; if theچ
time limit is exceeded, the script branches to the specifiedچ
label in the script file that initially set the trap; thereforeچ
the trap can even abort scripts running many subroutine callsچ
deep. Setting the &T trap register to zero disables the trap. 

	&T= 0
	&C= 0
	TRAP label
	....

	If at any time during script execution the &C clockچ
reaches or exceeds the &T trap register, script executionچ
proceeds from the specified label in the current script file.


JMP label
	Branch to the specified label unconditionally.

JZ label
JNZ label
	Branch to the specified label in the current script fileچ
if the accumulator is zero (JZ) or not zero (JNZ). This is usedچ
to test previous arithmetic instructions.

DJNZ label
	A basic looping instruction: "Decrement and Jump if Notچ
Zero". Subtracts 1 from &A, and if the result is not zero,چ
branches to the specified label.

JERROR label
	Branches if the &E error register is not zero. &E isچ
generally set by the MATCH instruction failing, the RETURN (fromچ
subroutine) instruction.

CALL scriptfile parameters ...
	Execute the script file as a subroutine. The filename isچ
"scriptfile.SCR". The called script file can in turn call otherچ
scripts, and each executes until the end of the script file or aچ
RETURN instruction is executed.

Each subroutine has it's own set of registers, &1 to &8.چ
Parameters can be passed to the called routine, which set theچ
initial value of the local registers. The contents of the localچ
registers are lost when the subroutine returns.


RETURN value
	Return to the calling subroutine (if any) and optionallyچ
set the error register to the specified value.
.pa
.sh String Comparison Instructions

MATCH pattern limit
	Wait until the pattern is found in the incoming characterچ
stream before executing the next instruction in the script. Theچ
pattern can contain "?" wildcard characters. There is an implicitچ
limit of 60 seconds; if a match is not found with the time limitچ
the error register is set and the next instruction executed.چ
(Presumably a JERROR ... instruction.) Optionally a time limit,چ
in seconds can be specified.

SAMPLE
	Shifts one character into the shift register; to be usedچ
in SAMPLE/IF/GOTO loops.

IF pattern label
	Compares the pattern against the contents of the shift register, and branches to the specified label if it matches. The pattern can contain "?" wildcard characters.
.pa
.sh Character Output Instructions

OUTPUT string
	Queue the string for output to the modem, simulatingچ
manual keyboard entry. The speed with which characters are output 
is determined by the THROTTLE setting.

COPY string
	Output the string directly to the modem at maximum speed.

THROTTLE n (0 - 500)
	This controls the maximum speed that Fido/FidoNet will outputچ
characters queued up by the OUTPUT command. The speed isچ
specified in a minimum wait between characters, in milliseconds.چ
The default is 20 milliseconds.
.pa
.sh File I/O Instructions

Fido/FidoNet's file system is very simple; there can only be one fileچ
open at a time. It is not a cooincidence that the file format isچ
compatible with most BASICs; each record in a Fido/FidoNet fileچ
contains 8 fields, each field is a quoted string seperated byچ
commas. Each record is delimited with a CR/LF. There is noچ
Control-Z or other end of file terminator.

FOPEN filename
	Opens a file for reading or writing. The script isچ
aborted if the file cannot be found, or if "UNATTENDED" is set,چ
then the error register &E is set. The file must be FCLOSEd whenچ
not needed.

FNEW filename
	Creates a new file of the specified name. The script isچ
aborted if the file cannot be created. The file will be emptyچ
after creation.

FREAD
	Reads one record from the file, and deposits the contentsچ
of the record into the eight registers &1 to &8. Each record canچ
contain any number of items, but only the first 8 will be used.چ
Each should be an argument as described elsewhere, preferablyچ
quoted. 

	Lines are read from the file one per FREAD instruction,چ
starting with the first line in the file, until the last line isچ
read; all FREADs after the last line will leave all registersچ
empty and set the error register &E.

FWRITE
	A new record is added to the end of the open file, thatچ
contains the contents of the eight registers &1 to &8. Each isچ
"quoted". The script is aborted if the disk becomes full, or ifچ
"UNATTENDED" is set, then the error register &E is set. 
th

FCLOSE
	This closes the current file and allows the FOPENing ofچ
another file. If any FWRITEs were done, they are written out toچ
disk at this time.

.pa
.sh User Input Instructions
KEYBD char
	Temporarily suspend script file operation until the userچ
types the specified key; CR if none specified. All characters theچ
user types, including the terminating one, are outout to theچ
modem. This allows the user to manually enter something such asچ
password.

INPUT prompt
	The prompt is displayed and the user enters a line ofچ
text, which is parsed into variables &1 through &8. Any extraچ
text is ignored.

ASK question 
	The question is displayed and Y or N is input as aچ
response, with &E set true if the answer is Y.

PAUSE prompt
	The prompt is displayed and the user must hit any key toچ
continue.

MESSAGE message
	Output the message to the command status line.

.pa
.sh Special Instructions

These instructions generally take one operand and perform someچ
hopefully useful instruction. They are generally high levelچ
functions to perform common functions.


IOPORT n (1 - 2)
	Select the serial port to do business with; the defaultچ
is the first port, on a pclone COM1. 

CD-BIT (1,2,4,8,16,32,64,128)
	Set the Carrier Detect (CD) mask bit for proper operationچ
of the on-screen "ONLINE/OFFLINE" display.

TIME hh:mm (00:00 - 23:59)
	Wait until the specified time of day before executing theچ
next line in the script file.

FILES filespec
	Sets &A to the number of files matching the fileچ
specification.

DELAY milliseconds (1 - 65000)
	Delay execution of the script until time elapsed. 

QUIET milliseconds (1 - 65000)
	Wait for no input from the modem for the specified time.

DTR
	Lowers the modem control line Data Terminal Ready (DTR)چ
for 1/2 second.

BREAK
	Causes a 1/2 second line break.