Manual For The MCPL Programming Language
Manual For The MCPL Programming Language
Manual
and User Guide
by
Martin Richards
[email protected]
http://www.cl.cam.ac.uk/users/mr/
Computer Laboratory
University of Cambridge
May 23, 2007
Abstract
MCPL is a programming language that has been derived from BCPL by the
inclusion of features found in ML, C and Prolog. Like BCPL, it is typeless, uses
a contiguous runtime stack and has no builtin garbage collector, but it does make
extensive use of ML-like pattern matching. The low level aspects of the language
resemble those of BCPL and C. MCPL uses its own function calling sequence,
however it is designed to allow MCPL and C functions to call each other.
Notable features of MCPL are its pattern matching facilities and the simple
way in which data structures are handled.
This document gives the definition of the language, its library and how to
obtain and install the system.
Keywords
Systems programming language, Typeless language, MCPL, BCPL, Mintcode,
Coroutines.
CONTENTS
Contents
1 Introduction
2 Language Overview
2.1 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
3
3 Expressions
3.1 Names and Constants . . . . . .
3.2 Vectors and Tables . . . . . . .
3.3 Function Calls . . . . . . . . . .
3.4 Method Calls . . . . . . . . . .
3.5 Postfixed Expression Operators
3.6 Prefixed Expression Operators .
3.7 Infixed Expression Operators . .
3.8 VALOF Expressions . . . . . . .
3.9 MATCH and EVERY Expressions .
3.10 Expression Precedence . . . . .
3.11 Manifest Constant Expressions .
3.12 Static Constant Expressions . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Commands
4.1 Assignments . . . . . . . . . . . . . . .
4.2 Conditional Commands . . . . . . . . .
4.3 Repetitive Commands . . . . . . . . .
4.4 Flow of Control . . . . . . . . . . . . .
4.5 Exception Handling . . . . . . . . . . .
4.6 Sequences and Compound Commands .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
4
5
6
6
7
7
8
8
9
10
10
.
.
.
.
.
.
10
11
12
12
13
14
15
5 Patterns
16
6 Declarations
6.1 External Declarations
6.2 Manifest Declarations
6.3 Global Declarations .
6.4 Static Declarations .
6.5 Function Definitions
17
17
18
18
19
19
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
20
21
ii
CONTENTS
9 The Library
9.1 MSYSLIB . . . . . . . . . . . . . . . .
9.1.1 sys . . . . . . . . . . . . . . .
9.1.2 Interpreter Management . . .
9.1.3 I/O sys Operations . . . . . .
9.1.4 Other sys Operations . . . .
9.2 MLIB . . . . . . . . . . . . . . . . . .
9.2.1 Stream Input/Output . . . .
9.2.2 Input Functions . . . . . . . .
9.2.3 Output Functions . . . . . . .
9.2.4 The Filing System . . . . . .
9.2.5 Command Arguments . . . .
9.2.6 Program Loading and Control
9.2.7 Character Handling . . . . . .
9.2.8 Coroutines . . . . . . . . . . .
9.2.9 Hammings Problem . . . . .
9.2.10 Scaled Arithmetic . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
22
22
22
24
25
25
26
28
29
30
31
33
35
36
39
41
42
42
44
11 The Debugger
45
12 Installation
12.1 Linux Installation . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.2 Installation on Other Machines . . . . . . . . . . . . . . . . . . .
12.3 The Native Code Version . . . . . . . . . . . . . . . . . . . . . . .
49
50
53
53
13 Example Programs
13.1 Coins . . . . . . . . . .
13.2 Primes . . . . . . . . .
13.3 Queens . . . . . . . . .
13.4 Fridays . . . . . . . . .
13.5 Prover . . . . . . . . .
13.6 Eval . . . . . . . . . .
13.7 Fast Fourier Transform
13.8 Turing . . . . . . . . .
53
53
54
54
55
55
61
64
66
Bibliography
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
70
Introduction
The directive GET "mcpl.h" is like the C #include directive and causes a file
of standard declarations to be included in the program. Function definitions are
introduced by the word FUN followed by the function name and a list of pattern
match items that test and decompose the function arguments. The function
start has an empty pattern indicating that it is parameterless, and, as can be
seen, it will output a message, read three numbers and then output two messages
about the triangle whose side lengths have been entered.
2 LANGUAGE OVERVIEW
The function sort of triangle returns a string that depends on its three
arguments. If the second argument is less than the first, the first match item
succeeds and causes sort of triangle to be re-entered with these two arguments
swapped. The second match performs a similar test on the second and third
arguments, and so, by the time the third match item is reached the arguments
a, b and c are in sorted order. A compiler could notice that both calls are tail
recursive, and optimise them accordingly. Indeed, the first call would be further
optimised to jump to the second match item since after swapping a and b the
first match cannot succeed.
The absence of types avoids the need to clutter programs and forces programmers to represent data simply. My experience is that, for many applications, data
can be represented satisfactorily by means of integers, strings, bit patterns and
pointers to vectors composed of these kinds. Although some say that lack of compile time type checking makes programs difficult to debug, I have not found this
to be the case in practice. Indeed, there are situations where the absence of types
is an advantage since it often allows a more concise and readable representation
of an algorithm.
Language Overview
2.1 Comments
2.1
Comments
There are two form of comments. One starts with the symbol // and extends
up to but not including the end-of-line character, and the other starts with the
symbol /* and ends at a matching occurrence of */. Comment brackets (/*
and */ may be nested, and within such a comments the lexical analyser is only
looking for /* and */. Some care is needed when commenting out fragments of
program containing string constants. Comments may not occur in the middle of
multi-character symbols such as identifiers or constants.
Expressions
Expressions are composed of names, constants and expression operators and may
be grouped using parentheses. The precedence and associativity of the different
expression constructs is given in Section 3.10.
3.1
The constants TRUE and FALSE have values -1 and 0, respectively, which are
the conventional MCPL representations of the two truth values. Whenever a
boolean test is made, this is performed by a comparison with FALSE (=0).
Numbers may also be represented by character constants, which consist of a
single quote () followed by zero or more characters, followed by a second single
3 EXPRESSIONS
quote. The characters are packed into 8 bit bytes to form the value, padded
on the left with zeroes, if necessary. The rightmost character of the constant is
the least significant byte of the result. The normal ASCII character set is used
augmented by escape sequences as follows:
Escape
Replacement
\n
\c, \r
\p, \f
\s
\b
\t
\e
\^c
\"
\
\\
\ddd
\
The single character with number ddd (one or more decimal digits denoting an integer in the interval [0,255]).
This sequence is ignored, where f..f stands for a sequence of one or more space, tab, newline and newpage
characters.
\f..f \
3.2
An expression of the form: [E0 ,..., En ] returns a pointer to n + 1 consecutive locations of dynamic memory initialised with the values of the expressions
E0 , . . . , En . The space is allocated when control passes into the current dynamic
scope (see Section 7 on Scope and Extent). It is released when execution leaves
the current dynamic scope. The pointer behaves like a vector with bounds 0 and
n, and its elements can be accessed using the subscripting operator !, described
Section 3.7. The initialising expressions E0 , . . . , En may, of course, contain dynamic vectors, tables and strings, and so an expression such as:
[ ->, [=, [Id, "x"], [Numb, 0]],
[Numb, 0],
[Id, "abc"]
]
is legal. However, remember that the space for this dynamic structure only
remains allocated as long as control remains within the current dynamic scope.
Uninitialised dynamic vectors of words or characters can be created by expressions of the form: VEC K or CVEC K, respectively, where K is a manifest
constant expression (see Section 3.11) giving the upper bound. The lower bound
is always zero. The space for a dynamic vector is only allocated while control is
within the current dynamic scope, see Section 7.
A static vector can be created using an expression of the following form:
TABLE [K0 , . . . , Kn ] where K0 , . . . , Kn are static constant expressions, see Section 3.12. The space for a static vector is allocated for the lifetime of the program.
3.3
Function Calls
3 EXPRESSIONS
from other modules, and, if it was declared to use the C calling sequence, it will
be callable from C directly. This scheme allows for convenient mixing of C and
MCPL programs.
If a call occurs in the context of an expression then it is expected to return a
result. The result, if any, of a call in any other context is thrown away.
3.4
Method Calls
Method calls are designed to make an object oriented style of programming more
convenient. they are syntactically similar to a function calls but uses a hash
symbol (#) to separate the function specifier from its arguments. The expression:
E#(E1 ,..,En )
is defined to be equivalent to:
(E1 !0!E)(E1 ,..,En )
Here, E1 points to the fields of an object, with the convention that its zeroth field
(E1 !0) is a pointer to the methods vector. Element E of this vector is applied to
the given set of arguments. Normally, E is a manifest expression. An example
program illustrating method calls can be found in MCPL/mcplprogs/objdemo.m
in the MCPL distribution system (see Section 12).
3.5
Expressions of the form: E++, E+++, E-- or E--- cause the location specified by
E to be incremented or decremented. The result of the expression is its original
value, before modification. The operator ++ increments by one, +++ increments
by the number of bytes in a word, -- decrements by one and --- decrements
by the number of bytes in a word. In addition to working with integers, the
operators ++ and -- adjust byte pointers to point to adjacent bytes, and +++
and --- adjust word pointers to point to adjacent words. It is thus assumed, in
MCPL, that pointers to adjacent bytes are integers that differ by one. Pointers to
adjacent words differ by an amount Bpw (declared in mcpl.h) is implementation
dependent, but is usually 4 and sometimes 8.
3.6
Expressions of the form: ++E, +++E, --E or ---E cause the location specified
by E to be incremented or decremented. The result of the expression is its value
after modification. As with the post fixed operators, ++ and -- increment or
decrement by one, while +++ and --- increment and decrement by Bpw. They
work as expected with pointers.
An expression of the form !E returns the contents of the memory word pointed
to by the value of E, and an expression of the form %E returns an unsigned integer
equal to the byte pointed to by the value of E.
An expression of the form @E returns a pointer to the 8 bit or word sized
memory location specified by E. E can only be a variable name or an expression
with leading operator ! or %.
Expressions of the form: +E, -E, ~E, ABS E or NOT E return the result
of applying the given prefixed operator to the value of the expression E. The
operator + returns the value unchanged, - returns the integer negation, ~ returns
the bitwise complement of the value, ABS returns the absolute value, and NOT
returns the boolean complement of the value.
3.7
3 EXPRESSIONS
3.8
VALOF Expressions
3.9
3.10
Expression Precedence
A lexical token that can start an expression or a pattern cannot denote an infixed
or postfixed operator when occuring as the first token of a line. This rule applies
to the tokens: !, %, +, ++, +++, -, --, ---, =, ~=, <=, list of syntactically the same
as the argument list of a function call and consists of either a single argument (a
name, a number, a string, TRUE, FALSE, ? or an initialised dynamic vector), or it
is a list of arguments enclosed in parentheses.
>=, < and >, and its purpose is to allow semicolons at the end of lines to be
omitted.
Table 1 specifies the precedence of the various expression constructs. The
precedence values are in the range 0 to 15, with the higher values signifying greater
binding power. The letters L and R denote the associativity of the operators.
For instance, the dyadic operator ! is left associative and so v!i!j is equivalent
to (v!i)!j, while b1->x,b2->y,z is equivalent to b1->x,(b2->y,z).
15L Names, Literals, ?, TRUE, FALSE,
(E), [E,..,E],
Function and method calls
14
TABLE VEC CVEC
13
++ +++ -- --Postfixed
12
++ +++ -- --Prefixed
11L ! %
Dyadic
10
! % @ ~ + - ABS
Prefixed
9L << >>
Dyadic operators
8L * / MOD &
7L XOR
6L + - |
5
= ~= <= >= < >
Extended Relations
4
NOT
Truth value operators
3L AND
2L OR
1R -> ,
Conditional expression
0
VALOF MATCH EVERY
Miscellaneous
Table 1: Operator precedence
10
4 COMMANDS
3.11
means
means
means
means
means
means
means
means
means
means
means
! (f x)
! (p +++)
! (@ x)
! ((v!i)!j)
@ ((v!i)!j)
@ ("abc" % i)
(x<<1) + (y>>1)
~ (x!y)
(~x) = y
NOT (x=y)
b1 -> x, (b2 -> y, z)
3.12
A static constant expression may be used to specify the initial value of a static
location. It may be a string, a static vector, a function, the address of a static
variable or a manifest constant expression. Within a static constant expression,
the following constructs are allowed and declare static vectors: VEC K and CVEC
K, where K denote a manifest constant expression, and [K0 ,..,Kn ], where Ki
denote static constant expressions.
Commands
11
4.1 Assignments
4.1
Assignments
12
4 COMMANDS
current scope, but the assignment taken place when the statement is executed.
Each initialisation component (=Ei ) is optional, so, for example:
LET a=1, b, c=10
declares a,b and c, but only assigns values to a and c.
4.2
Conditional Commands
4.3
Repetitive Commands
E
E
E2 DO C
E2 BY K DO C
The symbol DO may be omitted whenever it is followed by a command keyword. The WHILE command repeatedly executes the command C as long as E
is non-zero. The UNTIL command executes C until E is zero. The REPEAT command executes C indefinitely. The REPEATWHILE and REPEATUNTIL commands
first execute C then behave like WHILE E DO C or UNTIL E DO C, respectively.
The FOR command first initialises its control variable (vid) to the value of
E1 , and evaluates the end limit E2 . Until vid moves beyond the end limit, the
command C is executed and vid increment by the step length given by K which
must be a manifest constant expression (see Section 3.11). If BY K is omitted
13
4.4
Flow of Control
14
4.5
4 COMMANDS
Exception Handling
15
4.6
16
5 PATTERNS
The result of any such expression is thrown away unless it is the last position of
a sequence.
Empty commands are allowed.
Patterns
Pattern matching is one of the most important facilities provided within MCPL
since it allows both a mechanism for multiway selection and a means of associating
variable names with locations in memory. Patterns are used in function definitions, the MATCH and EVERY constructs, and in the exception handling mechanism.
Within each of these constructs, the user may supply a list of match items, which
must be terminated by a dot (.). Each match item has the following syntactic
form:
: Plist => Clist
where Plist is a list of zero or more patterns separated by commas and Clist is
a sequence of commands, separated by semicolons or newlines. At the moment
when control is passes to a match item, there is a list of argument values laid out
in consecutive locations of memory. The patterns in the pattern list are matched
against these arguments in left to right order. If all matches are successful then
control passes to the corresponding command sequence.
A pattern can be an explicit numerical constant (e.g. 1234 or A) or a
range (e.g. 0..9 or A..Z) or an alternation of constants or ranges (e.g.
2 | 3 | 5 | 10..20 ). An argument matches such a pattern if
(a) it equals the number,
(b) it lies within the given range
or (c) it is matched by one of the alternations.
Manifest names may be used within patterns wherever numbers are allowed.
The patterns TRUE and FALSE match the corresponding values in the current
argument, and a question mark (?) or empty pattern will match any argument
value.
A pattern consisting of a variable name always matches successfully and is
treated as a declaration of the name, causing it to be attached to the corresponding argument location. The scope of the name is the entire pattern list and
associated command sequence.
An argument which is a pointer can be matched by a pattern of the following
form:
[ Plist ]
17
where Plist is a list of patterns separated by commas. If this pattern occurs,
the corresponding argument is assumed to be a valid pointer into memory. The
consecutive locations pointed to are matched in turn by the patterns in Plist.
This construct can be nested to any depth.
A pattern may consist of a relational operator ( =, ~=, <=, >=, < or >) followed
by an expression. The corresponding argument is compared with the value of the
expression to determine the success of the match.
A pattern consisting of one of the assignment operator ( :=, <<:=, >>:=, &:=,
*:=, /:=, MOD:=, XOR:=, |:=, +:= or -:= ) followed by an expression is always
successful. It has the side effect that, if the entire pattern matches succesfully,
the assignment is performed with the implied left hand operand, just prior to
executing the corresponding command sequence.
If two patterns are justaposed, then both are matched against the same argument location. For instance, in
: sum, coins[val(<=sum)] => ...
coins matches the second argument, which must be a pointer to a memory
location which is given the name val and which must be less than or equal to
the first argument (sum).
Declarations
Declarations are used to declare external names, manifest constants, global variables, static variables and to define functions, and they may only occur at the
outermost level of a program. It is thus not possible to declare a function, for
instance, within another function. The five kinds of declaration are described
below.
6.1
External Declarations
18
6 DECLARATIONS
v
p
s
i
u
l
The first letter of a qualifier specifies the result type of the function and the
remaining letters specify the types of the first few arguments. If an MCPL call
of a C function supplies more arguments than the specification declares then the
extra arguments are assumed to be of type long int.
If an MCPL function is declared in the scope of an external declaration for
the same name then the function is declared as an external entry point and can
be called from other modules. If the external declaration specifies a C function
type, then the MCPL function is compiled to be compatible with the C calling
sequence.
6.2
Manifest Declarations
6.3
Global Declarations
A global declaration consists of the word GLOBAL followed by a list of names, each
possibly followed by a position specifier :K where K is a manifest expression (see
Section 3.11). For example, the following declaration:
19
6.4
Static Declarations
6.5
Function Definitions
20
Since functions have no dynamic free variables, the calling overhead is small.
It is permissible to pass functions as arguments to other functions, assign them
to variables or return them as function results. They are also allowed in static
constant expressions.
If a function declaration occurs in the scope of a global declaration of the
same name, then it is the initial value of the corresponding global variable. This
provides a means of calling functions defined in seperately compiled modules
without using external declarations.
All identifiers used in a program must have a declaration and must be used only
within the scope of that declaration. The scope of an identifier is the textual
region of program for its declaration is valid, and this depends on the kind of
declaration.
MANIFEST identifiers are syntactically distinct from all other identifiers and
can be considered separately. MANIFEST declarations are treated as if they were
all brought to the head of the entire program, and so the scope of a MANIFEST
identifier is the entire program. It is permissible to use a manifest identifier in
the same MANIFEST declaration provided its use occurs later in the declaration
list.
Identifiers declared by EXTERNAL declarations are regarded as being declared
at the start of the program, and have a scope that extends throughout the entire
program. All identifiers declared by function declarations are regarded as declared immediately after the externals and so have a similar scope. All identifiers
declared by GLOBAL declarations are regarded as declared immediately after the
function identifier. STATIC identifiers are regarded as being declared next.
The function definitions occur within the scope of all these declarations.
All other identifiers are declared within functions. These are called dynamic
variables since they come into existence when a function is called and cease to
exist when the function returns. Multiple activations of such variables can occur
if a function is called recursively.
The scope of a dynamic variable is the smallest dynamic scope containing its
declaration. A dynamic scope is defined to be any region of program that
1. starts at the colon (:) of a match item and extends to the end of its
command sequence,
2. starts just after the symbol => of a match item and extends to the end of
the command sequence,
21
3. starts just after the symbol { and extends to the matching },
4. is the body of a VALOF expression, or
5. extends from the word FOR to then end of the body of the FOR command.
Thus, the scope of an identifier declared within a pattern extends forwards
and backwards to include the entire pattern and command sequence of the current
match item, and the scope of identifiers declared by a LET declaration extends
forwards and backwards to include the whole of the smallest enclosing dynamic
scope, which usually starts at the nearest preceding =>, VALOF, FOR or { symbol.
Dynamic scopes also determine the dynamic lifetime (or extent) of function
arguments, variables declared by LET and local vectors declared by VEC, CVEC or
[E,..,E]. Space for such variables and vectors is allocated when control passes
into the current dynamic scope, and is freed when control leaves this scope.
22
9 THE LIBRARY
The Library
This and the following sections describe the standard library functions for the
interpretive MCPL system. The interpreter is called minterp and is implemented
in C. Sometimes the system contains a second faster interpreter called mintasm
that is implemented in assembly language and provided fewer debugging aids.
The library functions are defined in MSYSLIB and MLIB and use the first 200
elements of the global vector. The size of the global vector is held in globsize
(global 0) and, by convention, the global result2 is used by some functions to
return a second result.
9.1
MSYSLIB
This module contains the definitions of the functions sys, chgco and muldiv.
These functions are hand written in Mintcode because they need to execute the
Mintcode instructions (SYS, CHGCO and MDIV) that cannot be generated by the
MCPL compiler. The instruction SYS provides an interface with the host operating system, CHGCO is used in the implementation of coroutines (see Section 9.2.8),
and MDIV performs the operation required by muldiv(see Section 9.2.10).
9.1.1
sys
The Mintcode instruction SYS causes the interpreter to call the C function dosys.
The current stack frame and global vector pointers are passed as arguments
of dosys so that it can access any local or global variables belonging to the
interpreted code. The SYS instruction is only invoked by the MCPL function sys
that is defined in MSYSLIB. A typical call of sys is as follows:
res := sys(op,...)
The action performed by this call depends on op. Some actions concern the
management of the interpreter, some are concerned with input and output, while
others provide access to functions implementated in C.
9.1.2
Interpreter Management
sys(0, code)
This will cause a return from the the interpreter yielding code as the return
code. A code of zero denotes successful completion and, if invoked at the outermost level, causes the MCPL Mintcode System to terminate. If code equal -1,
interpretation continues using the fast interpreter (mintasm). If code equal -2,
interpretation continues using the slow interpreter (minterp).
23
9.1 MSYSLIB
A register
B register
C register
P register
G register
ST register
PC register
Count register
H register
work register
work register
work register
the stack frame pointer
the base of the global vector
the status register (unused)
the program counter
see below
used for exception handling
24
9 THE LIBRARY
execution statistics. It uses the tally vector to hold frequency counts of Mintcode
instructions executed. When tallying is enabled, the ith element of the tally vector is incremented by one every time the instruction at location i of the Mintcode
memory is executed. The upper bound of the tally vector is implementation dependent but is stored in the zeroth element of the vector. The location of the
tally vector can be found by evaluating the expression rootnode!Rtn tallyv.
9.1.3
ch := sys(10)
Return the next character from the keyboard.
sys(11, ch)
Send character ch to the standard output (normally the screen).
n := sys(12, fp, buf, len)
Read upto len bytes from the file specified by the file pointer fp into the byte
buffer buf. The file pointer must have been created by a call of sys(14,...).
The number of bytes actually read is returned as the result.
n := sys(13, fp, buf, len)
Write len bytes to the file specified by the file pointer fp from the byte buffer
buf. The file pointer must have been created by a call of sys(15,...). The
result is the number of bytes transferred, or zero if there was an error.
fp := sys(14, name)
This opens for reading the file whose name is given by the string name. It
returns 0 if the file cannot be opened, otherwise it returns the file pointer for the
opened file.
fp := sys(15, name)
This opens for writing the file whose name is given by the string name. It
returns 0 if the file cannot be opened, otherwise it returns the file pointer for the
opened file.
sys(16, fp)
This closes the file whose file pointer is fp.
25
9.2 MLIB
9.1.4
FUN
FUN
FUN
FUN
FUN
FUN
FUN
FUN
FUN
FUN
deletefile
renamefile
getvec
freevec
loadseg
globin
unloadseg
muldiv
setraster
intflags
:
:
:
:
:
:
:
:
:
:
name
old, new
upb
ptr
name
seg
seg
a, b, c
n, val
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
sys(17, name).
sys(18, old, new).
sys(21, upb).
sys(22, ptr).
sys(23, name).
sys(24, seg).
sys(25, seg).
sys(26,a,b,c).
sys(27,n,val).
sys(28).
9.2
MLIB
This section describes the MLIB library function and their associated data structures.
When the Mintcode System is started, a region of store is allocated for the
Mintcode memory. It is where Mintode stacks, global vectors, program code and
system data is placed. Within it there is a vector called the rootnode that allows
running programs to locate components of the system data structure. The global
variable rootnode holds a pointer to the rootnode and there are manifest constants to ease access to its various elements. For instance, the pointer to the start
of the memory block chain can be obtained by evaluating rootnode!Rtn blklist.
Ten elements are defined in the rootnode as shown below.
Expression
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
rootnode!Rtn
Value
membase
memsize
blklist
tallyv
syslib
blib
boot
cli
keyboard
screen
v := getvec(upb)
freevec(v)
Allocation and release of space is performed by getvec and freevec, which
26
9 THE LIBRARY
use a first fit algorithm based on a list of blocks chained together in memory
order. Word zero of each block in the chain contains a flag in its least significant
bit indicating whether the block is allocated or free. The rest of the word is an
even number giving the size of the block in words. A pointer to the first block in
the chain is held in the rootnode which is used to hold system wide information.
getvec allocates a vector with upper bound upb from the first large enough
free block on the block list. If no such block exists it returns zero. A vector previously allocated by getvec can be freed by the above call of freevec. Coalescing
of adjacent free blocks is performed by getvec.
ch := sardch()
sawrch(ch)
sawritef(format, a, b, ...)
These functions provide standalone input and output designed primarily as
an aid for debugging the system before the full stream based I/O mechanism is
available.
The function sardch returns the next character from the keyboard as soon as
it is available, echoing the character to the screen. It is implemented by means
of sys(10), described above.
The call sawrch(ch) outputs the character ch to the screen. It is implemented
by means of sys(11,ch), described above. The function sawritef is similar to
writef but performs its output using sawrch.
9.2.1
Stream Input/Output
27
9.2 MLIB
pos
end
file
id
work
rdfn
wrfn endfn
scb
28
9 THE LIBRARY
Input Functions
scb := findinput(name)
selectinput(scb)
ch := rdch()
flag := unrdch()
scb := input()
endread()
n := readn()
The call findinput(name) opens an input stream. If name is the string "*"
then input comes from the keyboard, otherwise name is taken to be a filename.
If the stream cannot be opened the result is zero.
The call selectinput(scb) selects scb as the currently selected input stream.
It aborts if scb is not an input stream.
The call rdch() reads the next character from the currently selected input
stream. If the stream is exhausted, it returns the special value Endstreamch.
Input from the keyboard is buffered until the RETURN key is pressed to allow
simple line editing in which the backspace key may be used to delete the most
recent character typed.
The call unrdch() attempts to step the current input stream back by one
character position. It returns TRUE if successful, and FALSE otherwise. A call of
unrdch will always succeeds the first time after a call of rdch. Its main purpose
is for use in functions such as readn (described below) where single character
lookahead is necessary.
The call input() returns the currently selected input stream.
The call endread() closes the currently selected input stream.
The call readn() reads an optionally signed decimal integer from the currently
selected input stream. Leading spaces, tabs and newlines are ignored. If the
9.2 MLIB
29
number is syntactically correct, readn returns its value with result2 set to zero,
otherwise it returns zero with result2 set to -1. In either case, it uses unrdch
to replace the terminating character.
9.2.3
Output Functions
scb := findoutput(name)
This function opens an output stream. If name is the string "*" then output
is to the screen, otherwise name is taken to be a file name. If the stream cannot
be opened, the result is zero.
selectoutput(scb)
This routine selects scb as the currently selected output stream. It aborts if
scb is not an output stream.
wrch(ch)
This routine writes the character ch to the currently selected output stream.
If output is to the screen, ch is transmitted immediately.
scb := output()
This function returns the SCB of the currently selected output stream.
endwrite()
This routine closes the currently selected output stream.
newline()
newpage()
These two routines simply output the newline character (\n) or the newpage (form-feed) character (\p), respectively, to the currently selected output
stream.
writed(n, d)
writeu(n, d)
writen(n)
These routines output the integer n in decimal to the currently selected output
stream. For writed and writeu, the output is padded with leading spaces to fill
a field width of d characters. If writen is used or if d is too small, the number is
written without padding. If writeu is used, n is regarded as an unsigned integer.
writehex(n, d)
writeoct(n, d)
These routines output, repectively, the least significant d octal or hexadecimal
digits of the integer n to the currently selected output stream.
30
9 THE LIBRARY
writes(str)
writet(str, d)
These routines output the string str to the currently selected output stream.
If writet is used, trailing spaces are added to fill a field width of d characters.
writef(format,a,b,c,d,e,f,g,h,i,j,k)
The first argument (format) is a string that is copied character by character
to the currently selected output stream until a substitution item such as %s or
%5d is encountered when a value (usually the next argument) is output in the
specified format. The substitution items are given in table 5.
Item Substitution
%s
%nt
%c
%no
%nx
%nd
%d
%nu
%$
%%
When a field width (denoted by n) is required, it is specified by a possibly empty sequence of decimal digits. The implementation of writef is a
good example of how a variadic function can be defined. It can be found in
MCPL/mintcode/sys/MLIB.m.
9.2.4
BCPL uses the filing system of the host machine and so such details as the
maximum length of filenames or what characters they may contain are machine
9.2 MLIB
31
dependents. However, within a file name the character slash (/) or backslash (\)
is regarded as a file separator and is converted into the appropriate separator
character for the operating system being used. For Unix systems this is a slash,
for MS-DOS, WINDOWS and OS/2 it is a backslash, and on the Macintosh it
is a colon. Thus, under MS-DOS, findoutput can be given a file name such as
"tmp/RASTER" and it will be treated as if the name "tmp\RASTER" had been given.
This somewhat ad hoc feature greatly improves portability between systems.
As described above, files may be opened and closed for reading and writing.
They may also be deleted or renamed using the the following routines.
flag := deletefile(name)
flag := renamefile(oldname, newname)
The call deletefile(name) deletes the file with the given name. It returns
TRUE if the deletion was successful, and FALSE otherwise.
The call renamefile(oldname, newname) renames the file oldname as file
newname, deleting newname if necessary. Both oldname and newname are strings.
The function returns TRUE if the renaming was successful, and FALSE otherwise.
9.2.5
Command Arguments
32
9 THE LIBRARY
Example items
Kind of item
"from"
"\ntwo words\n" Quoted string
abc
123-45*6
Unquoted string
carriage return
;
end-of-stream
Terminator
An error
Result
2
1
0
-1
In this example, there are three possible arguments and their values will be
placed in the first three elements of argv. The first argument has keyword FROM
and must receive a value because of the qualifier /A. The second has keyword TO
and its qualifier /K insists that, if the argument is given, it must be introduced
by its keyword. The third argument has the qualifier /S indicating that it is a
switch that can be turned on by the presence of its keyword. If an argument is
supplied, the corresponding element of argv will be set to -1, if it is a switch
argument, otherwise it will be set to a string containing the characters of the
argument value. The elements of argv corresponding to unset arguments are
cleared. Table 7 shows the values in placed in argv and the result when the call:
rdargs("FROM/A,TO=AS/K,N/S", argv, 50)
is given various argument strings.
This example illustrates that keyword synonyms can be defined using = within the
key string. Positional arguments are those not introduced by keywords. When
one is encountered, it becomes the value of the lowest numbered unset non-switch
argument.
n := findarg(keys, item)
The function findarg was primarily designed for use by rdargs but since it
is sometimes useful on its own, it is publicly available. Its first argument, keys,
33
9.2 MLIB
Arguments
abc TO xyz
"abc"
to xyz from abc "abc"
as xyz abc n
"abc"
abc xyz
"from" to "to"
"from"
"xyz"
"xyz"
"xyz"
"to"
0
0
-1
0
>0
>0
>0
=0
>0
As can be seen, it allocates a vector for the fields of the object, initialises its
zeroth element to point to the methods vector and calls the initialisation methos
that is expected to be in element Initobj of fns. It returns the a pointer to the
initialised fields vector. If it fails, it returns zero.
9.2.6
In this implementation, the MCPL compiler generates a file of hexadecimal numbers for the compiled code. For instance the compiled form of the logout com-
34
9 THE LIBRARY
mand:
MODULE logout
GET "mcpl.h"
FUN start : => abort 0
.
is
000007D0
0000000D
72617473
00000001
0000000D
0000FDDF 6F676F6C 00207475 0000DFDF
00202074 BA1F2310 7BFF1003 00000000
0000001C 0000001F
The first two words indicate the presence of a hunk of code of size 13
words which then follow. The two words following the pattern 0000FDDF contain
the MODULE name "logout" and the two words following 0000DFDF contain the
procedure name "start". The body of start (BA1F2310 7BFF1003) then follows.
The remaining 4 words are global initialisation data indicating that global 1 is
to be set to the entry point at position 28 (0000001C) relative to the start of
the hunk, and that the highest referenced global number is 31 (0000001F). The
following functions are related to the loading and control of programs.
code := start(a1, a2, a3, a4)
This function is, by convention, the main procedure of a program. If it is called
from the command language interpreter (see section 10), its first argument is zero
and its result should be the command completion code; however, if it is the main
procedure of a module run by callseg, defined below, then it can take up to 4
arguments and its result is up to the user. By convention, a command completion
code of zero indicates successful completion and larger numbers indicate errors
of ever greater severity
clihook()
This procedure is defined in MLIB and simply calls start. Its purpose is
to assist debugging by providing a place to set a breakpoint in the command
language interpreter (MCLI) just before a command in entered. Its is also permissible for the user to override the standard definition of clihook with a private
version.
stop(code)
This function is provided to stop the execution of the current command running under control of the CLI. Its argument code is the command completion
code.
9.2 MLIB
35
abort(code)
This procedure causes an exit from the current activation of the interpreter,
returning code as the fault code. If code is zero execution leaves the Mintcode
system altogether, if code is -1 execution resumes using the faster version of the
interpreter (mintasm). If code is positive, under normal conditions, the interactive debugger is entered.
flag := intflag()
This function provides a machine dependent test to determine whether the
user is asking to interrupt the normal execution of a program. On the Apple
Macintosh, flag will be set to TRUE only if the COMMAND, OPTION and
SHIFT keys are simultaneously pressed.
segl := loadseg(name)
This function loads the compiled program into memory from the specified file
name. It return the list of loaded program modules if loading was successful and
zero otherwise. It does not initialise the global variables defined in the program.
res := globin(segl)
This function initialises the global variables defined in the list of program
modules given by its argument segl. It returns zero if the global vector was too
small, otherwise it returns segl.
unloadseg(segl)
This routine unloads the list of loaded program modules given by segl.
res := callseg(name,a1,a2,a3,a4)
This function loads the compiled program from the file name, initialises its
global variables and calls start with the four arguments a1,...,a4. It returns
the result of this call, after unloading the program.
9.2.7
Character Handling
ch := capitalch(ch)
This function converts lowercase letters to uppercase, leaving other characters
unchanged.
res := compch(ch1, ch2)
This function compares two characters ignoring case. It yields -1 (+1) if ch1
is earlier (later) in the collating sequence than ch2, and 0 if they match.
36
9 THE LIBRARY
Coroutines
MCPL uses a stack to hold function arguments, local variables and anonymous
results, and it uses the global vector and static variables to hold non-local quanitities. Occasionally, applications arise where it is convenient to have separate
runtime stacks so that different parts of the program can run in pseudo parallelism. The coroutine mechanism provides this facility.
In this implementation, they have distinct stacks but share the same global
vector, and so it is natural to represent a coroutine by a pointer to its stack. At
the base of each stack there are seven words of system information as shown in
figure 8.
resumption point
fn sz c
P1 L1
coroutine chain
cptr
parent link
suspended
stack frame
37
9.2 MLIB
changeco
stack frame
currco
P1 L1
resumption point
a
cptr
resumption point
CHGCO
PC
P1 L1
PC
currco
38
9 THE LIBRARY
c := fn(cowait(c)) REPEAT
When control is next transfered to the new coroutine, the value passed becomes the result of cowait and hence the argument of fn. If fn(..) returns
normally, its result is assigned to c which is returned to the parent coroutine by
the repeated call of cowait. Thus, if fn is simple, a call of the coroutine convert
the value passed, val say, into fn(val). However, in general, fn may contain
calls of callco, cowait or resumeco, and so the situation is not always quite so
simple.
In detail, the implementation of createco uses getvec to allocate a vector
with upper bound size+7 and initialises its first seven locations ready for the
call of changeco(0,c) that follows. The state just after this call is shown in
figure 10. Notice that cowait(c) is about to be executed in the environment of
the new coroutine, and that this call will cause a return from createco in the
original coroutine, passing back a pointer to the new coroutine as a result.
createco
stack frame
P2 L2
changeco
stack frame
fn sz c h P1 L1
PC
coroutine chain
LP5
K10G 24
LP3
K6
SP6
J -7
{
cowait(c)
fn( ... )
c := ...
} REPEAT
fn sz c h
colist
currco
P
deleteco(cptr)
This call takes a coroutine pointer as argument and, after checking that the
corresponding coroutine has no parent, deletes it by returning its stack to free
store.
cptr := initco(fn, size, a,...)
39
9.2 MLIB
A coroutine with main function fn and given size is created and, if successful,
it is initialised by callco(cptr, @a). Thus, fn should expect a vector containing
up to 11 values. Once the coroutine has initialised itself, it should return control
to initco by means a call of cowait. Examples of the use of initco can be
found in the example that follows.
9.2.9
Hammings Problem
This problem is attributed to R.W.Hamming. The solution given here shows how
data can flow round a network of coroutines. It is illustrated in figure 11 in which
each box represents a coroutine and the edges represent callco/cowait connections. The end of a connection corresponding to callco is marked by c, and end
corresponding to cowait is marked by w. The arrows on the connections show
the direction in which data moves. Notice that, in tee1, callco is sometimes
used for input and sometimes for output.
w
BUF1
TEE1
BUF2
TEE2
BUF3
w
c
w
c
X2
X3
X5
w
c
c
w
c
w
MER1
MER2
w
MAIN
40
9 THE LIBRARY
yield a value extracted from buf1, after sending a copy of it to buf2. tee2
similarly takes values from buf2 passing them to buf3 and x3. Values passing
through x2, x3 and x5 are multiplied by 2, 3 and 5, repectively. mer1 merges
two monotonically increasing streams of numbers produced by x2 and x3. The
resulting stream is then merged by mer2 with the stream produced by x5. The
stream produced by mer2 is the required Hamming sequence, each value of which
is printed by main and then inserted into buf1.
The MCPL code for this solution is as follows:
GET "mcpl.h"
FUN buf : =>
LET val = cowait()
LET p=0, q=0
LET v = VEC 200
41
9.2 MLIB
FUN start : =>
LET buf1 = initco(buf,
LET buf2 = initco(buf,
LET buf3 = initco(buf,
LET tee1 = initco(tee,
LET tee2 = initco(tee,
LET x2
= initco(mul,
LET x3
= initco(mul,
LET x5
= initco(mul,
LET mer1 = initco(merge,
LET mer2 = initco(merge,
500)
500)
500)
100,
100,
100,
100,
100,
100,
100,
buf1,
buf2,
2,
3,
5,
x2,
mer1,
buf2)
buf3)
tee1)
tee2)
buf3)
x3)
x5)
LET val = 1
FOR i = 1 TO 100 DO { writef(" %6d", val)
UNLESS i MOD 10 DO newline()
callco(buf1, val) // Push val into buf1
val := callco mer2
}
deleteco
deleteco
deleteco
deleteco
buf1;
tee1;
x2;
mer1;
deleteco
deleteco
deleteco
deleteco
9.2.10
Scaled Arithmetic
The library function muldiv makes full precision scaled arithmetic convenient.
res := muldiv(a, b, c)
The result is the value obtained by dividing c into the double length product
of a and b, the remainder of this division is left in the global variable result2.
The result is undefined if it is too large to fit into a single length word or if c is
zero. In this implementation, the result is also undefined if any of a, b or c is the
largest negative integer. As an example, the function defined below calculates
the cosine of the angle between two unit vectors in three dimensions using scaled
integers to represent numbers with 6 digits after the decimal point.
MANIFEST Unit=1000000 // Scaling factor for numbers of the
// form ddd.dddddd
FUN inprod : v, w => muldiv(v!0, w!0, Unit) +
muldiv(v!1, w!1, Unit) +
muldiv(v!2, w!2, Unit)
.
On some processors, such as the Pentium, muldiv can be encoded very efficiently in assembly language.
42
10
The Command Language Interpreter (MCLI) is a simple interactive interface between the user and the system. Its source code is in /mintcode/sys/MCLI.m.
It loads and executes previously compiled programs that are normally held in
the mintcode directory. The source code of most commands can be found in
mintcode/com. Many of the system provided commands are described in section 10.2. However, any compiled program can be regarded as a command and
so the command language can be extended easily by the user.
10.1
Bootstrapping
When the Mintcode System is started, control is passed to the interpreter which,
after a few initial checks, allocates vectors for the memory of the mintcode abstract machine machine and the tally vector available for statistics gathering.
The mintcode memory is initialised suitably for sub-allocation by getvec, which
is then used to allocate space for the root node, the initial stack and the initial
global vector. The initial state shown in figure 12 is completed by loading the
object modules MSYSLIB, MLIB and MBOOT, and initialising the root node, the
stack and global vector. Interpretation of mintcode instructions now begins with
the mintcode register PC, P and G set as shown in the figure, and Count set to -1.
The other registers are cleared. The first mintcode instruction to be executed is
the first instruction of the body of the routine start in MBOOT, whose source is
in mintcode/sys/MBOOT.m. Since no return link has been stored into the stack,
this call of start must not attempt to return in the normal way; however, its
execution can still be terminated using sys(0,0).
The stack and global vector shown in figure 12 are used by start and form the
running environment of the debugger. The MCLI, on the other hand, is provided
with a new stack and a separate global vector, thus allowing the debugger to use
globals freely without danger of interference from the command language interpreter or from running commands. The global vector of 1000 words is allocated
for MCLI and this is shared by the MCLI program and its running commands. The
stack, on the other hand, is used exclusively by the command language interpreter
since it creates a coroutine for each command it runs.
Control is passed to the MCLI by means of the call sys(1,regs) which recursively enters the intepreter from an initial mintcode state specified by the vector
regs in which that P and G are set to point to the bases of a new stack and a new
global vector for MCLI, respectively, PC is the location of the first instruction of
43
10.1 Bootstrapping
Tally vector
blklist
stack
rootnode
PC
globals
G
Entry to start
MSYSLIB
MLIB
MBOOT
At the moment sys(1,regs) is first called, only globsize, sys and rootnode
have been set in the CLI global vector and so the body of startcli must be coded
with care to avoid calling global functions before their entry points have be placed
in the global vector. Thus, for instance, instead of calling globin to initialise the
globals defined in MSYSLIB and MLIB, the following code is used:
sys(24, rootnode!rtn_syslib)
sys(24, rootnode!rtn_blib)
If a fault occurs during the execution of MCLI or a command that it is running, the call of sys(1,regs) will return with the fault code and regs will hold
the dumped mintcode registers. A result of zero, signifying successful completion, causes execution of the mintcode system to terminate; however, if a non
zero result is returned, the debugger in entered by means of the call debug(res).
Note that the mintcode registers are available to the debugger since regs is a
global variable. When debug returns, the REPEAT-loop ensures that the command language interpreter is re-entered. The debugger is briefly described in the
section 11.
On entry to startcli, the coroutine environment is initialised by setting
currco and colist to point to the base of the current stack which is then setup
as the root coroutine. The remaining globals are the initialised and the standard
44
input and output streams opened before loading the MCLI program by means of
the following statement:
rootnode!rtn_cli := globin(loadseg("MCLI"))
10.2
Commands
This section describes the commands whose source code can be found in
mintcode/com. Each command is introduced by its name and rdargs argument
format string.
abort
NUMBER
The command: abort n causes the MLIB function abort to be called with
argument n. If n = 0, it will cause a successful return from the mintcode system.
If n is non zero, the interactive debugger is entered with fault code n. The default
value for n is 99. A brief description of the debugger is given in section 11.
echo
TEXT,N/S
This command will output its first argument TEXT. Unless the N switch if
given, the text will be followed by a newline.
interpreter FAST/S,SLOW/S
This command allows the user to select the fast (mintasm) or the slow
(minterp) version of the interpreter. If no arguments are given the fast one
is selected.
map
BLOCKS/S,NAMES/S,CODE/S,MAPSTORE/S,TO/K,PIC/S
This command outputs the state of the mintcode memory in a form that
depends on the arguments given. The output goes to the screen unless a filename
is given using the TO keyword.
preload ,,,,,,,,,
This command will preload into the mintcode memory up to 10 commands.
Without arguments it outputs the list of preloaded commands. Preloading
slightly improves the efficiency of command execution and is also useful in conjunction with the stats command, see below.
prompt
PROMPT
This command allows the user to change the prompt string. The default
prompt format is: "%d> ", which will output the time in milliseconds used by
the previous command.
45
stack
SIZE
The command stack n causes the size of the coroutine stack allocated for
subsequent commands to be n words long. If called without an argument stack
outputs the current setting.
stats
TO/K,PROFILE/S,ANALYSIS/S
This command controls the tallying facility which counts the execution of
individual mintcode instructions. If no arguments are given, stats turns on
tallying by clearing the tally vector and causing tallying to be enabled for the
next command to be executed. Subsequent commands are not tallied, making it
possible to process the tally vector while it is in a static state. Typical usage of
the stats command is illustrated below:
preload queens
stats
queens
interpreter
type
FROM/A,TO,N/S
This command will output the file given by the FROM argument, sending it to
the screen unless the TO argument is given. The swirch argument N causes line
numbers to be added.
unpreload ,,,,,,,,,,ALL/S
This command will remove preloaded commands from the mintcode memory.
The ALL switch will cause all preloaded commands to be removed.
11
The Debugger
When the mintcode system starts up, control first passes to MBOOT which initialises the system and creates a running environment for the command language
interpreter (MCLI). This is run by a recursive invocation of the interpreter and
so when faults occur control returns to MBOOT which then enters an interactive
46
11 THE DEBUGGER
debugger. This allows the user to inspect the state of the registers and memory,
and perform other debugging operations on the faulted program. The debugger
can also be entered using the abort command, as follows:
560> abort
!! ABORT 99: User requested
*
The asterisk (*) is the debuggers prompt character. A brief description of the
available debug commands can be display using the query (?) command.
* ?
?
Print list of debug commands
Gn Pn Rn Vn
Variables
G P R V
Pointers
n #b101 #o377 #x7FF c Constants
*e /e %e +e -e |e &e
Dyadic operators
< > !
Postfixed operators
SGn SPn SRn SVn
Store in variable
=
Print current value
Tn
Print n consecutive locations
$c
Set print style C, D, F, B, O, S, U or X
LL LH
Set Low and High store limits
I
Print current instruction
N
Print next instruction
Q
Quit
B 0Bn eBn List, Unset or Set breakpoints
C
Continue execution
X
Equivalent to G4B9C
Z
Equivalent to P1B9C
\
Execute one instruction
,
Move down one stack frame
.
Move to current coroutine
;
Move to parent coroutine
[
Move to first coroutine
]
Move to next coroutine
*
The debugger has a current value that can be loaded, modified and displayed.
For example:
*
*
*
*
*
*
*
*
12
-2
*3
=
30
<
=
60
12 -2 *3 < =
60
Four areas of memory, namely: the global vector, the current stack frame, the
mintcode register, and 10 scratch variables are easily accessed using the letters
G, P, R, V, respectively.
47
Put 10 and 11 in variables 1 and 2
Display the first 5 variables
* 10sv1 11sv2
* vt5
V
*
*
*
*
*
*
G
G
*
0:
v1*50+v2=
g0=
1000
g=
32924
! =
1000
gt10
0:
5:
1000
GLOB 5
10
11
511
start
changec
stop
42844
sys
42844
clihook
52
Notice that values that appear to be entry points display the first 7 characters
of the functions name. Other display styles can be specified by the commands
$C, $D, $F, $B, $O, $S, $U or $X. These respectively display values as characters,
decimal number, in function style (the default), binary, octal, string, unsigned
decimal and hexadecimal.
It is possible to display mintcode instructions using the commands I and N.
For example:
*
*
*
*
*
*
*
*
g4=
i
n
n
n
n
n
clihook
21352:
LP3
21353:
K4G
21355:
J
21357:
L0
21358: RAISE
21359:
RTN
1
21359
A breakpoint can be set at the first instruction of clihook and debugged program
re-entered by the following:
* g4=
* b9
* c
10>
clihook
The X command could have been used since it is a shorhand for G4B9C. The
function clihook is defined in MLIB and is called whenever a command is invoked.
For example:
Invoke the echo command
clihook
0 B=
48
11 THE DEBUGGER
Notice that the values of the mintcode registers A and B are displayed, followed
by the program counter PC and the mintcode instruction at that point. Single
step execution is possible, for example:
*
*
*
*
*
*
*
*
*
*
*
\A=
\A=
\A=
\A=
\A=
\A=
\A=
\A=
\A=
\A=
0
0
42904
42904
42904
80
80
42720
rdargs
42720
B=
B=
B=
B=
B=
B=
B=
B=
B=
B=
0
0
0
0
0
42904
42904
80
42720
42720
21353:
42768:
42770:
42771:
42773:
42775:
42777:
42779:
42781:
23660:
K4G
LLP
SP3
SP
L
SP
LLL
LG
K
LP4
1
4
89
80
90
42720
78
85
At this point the first instruction of rdargs is about to be executed. Its return
address is in P1, so a breakpoint can be set to catch the return, as follows:
* p1b8
* c
!! BPT 8:
A=
*
40655
42783 B=
42783:
CTA
Set breakpoint 1
Display the currently set of breakpoints
The next three calls of sys will be to write the characters ABC. The following
example steps through these and displays the state of the runtime stack just
before the third call, before leaving the debugger.
49
* c
!! BPT 1:
sys
A=
11 B=
65
21188:
SYS
* c
A
!! BPT 1:
sys
A=
11 B=
66
21188:
SYS
* c
B
!! BPT 1:
sys
A=
11 B=
67
21188:
SYS
* .
42844: Active coroutine
clihook
Size 20000 Hwm
43284:
sys
11
67
312
* ,
43268:
cnslwrf
37772
* ,
43248:
wrch
67
32
* ,
43228:
writes
42915
67
* ,
42888:
start
42904
42912
0
* ,
42872:
clihook
0
* , Base of stack
* 0b1c
Clear breakpoint 1 and resume
C
210>
127
43228
4407873
12
Installation
A free distribution of MCPL is available via my World Wide Web Home Page
(http://www.cl.cam.ac.uk/users/mr/) to individuals for private use and to
academic institutions. If you install the system, please send me a message (to
[email protected]) so I can keep a record of who is interested in it.
This distribution is still under construction and is in an unpolished state,
however, it works well enough to run several demonstration programs. It is
ultimately meant to provide a machine independent interpretive version of MCPL.
The interpreter is implemented in C, but its efficiency will be improved when an
50
12 INSTALLATION
assembly code version of part of the system is completed, as has been done for
the BCPL Cintcode system.
This distribution is being developed under Linux, but should be (easily?) transferable to DEC Alpha machines, Mips R2000/3000 machines, Sun4s,
Sun/SPARCs and 386/486 machines under MSDOS/Windows, MAC or OS/2.
The simplest installation is for Linux machines.
12.1
Linux Installation
This section describes how to install the MCPL Mintcode on an IBM PC running
Linux.
First install the BCPL Cintcode system (bcpl.targz) including the latest version of BCPL/bcplprogs (bcplprogs.tar.gz), available from my
WWW Homepage.
The following directory structure is required:
|
*--BCPL
| |
| *--cintcode
| |
| *--bcplprogs
| |
| *--natbcpl
|
*--MCPL
|
*--mcplcomp
|
*--mintcode
|
*--mcplprogs
|
*--natmcpl
51
This will compile various files putting the results (minterp, MBOOT, MLIB
and MCLI) in mintcode.
Add this directory to the shell variables PATH and MCPLPATH. This could
possibly be done by adding the following to your .profile file:
export PATH=$PATH:$(HOME)/MCPL/mintcode
export MCPLPATH=$(HOME)/MCPL/mintcode
-----
This will compile queens.m leaving the compiled code queens in the current
working directory.
52
12 INSTALLATION
To execute the queens program, first create a new shell (xterm) window
then type:
cd ..../MCPL/mcplprogs/demos
minterp
queens
----------
mc
mc
mc
mc
mc
mc
echo
abort
logout
stack
map
prompt
12.2
53
This has not yet been tested but will be similar to installing BCPL on other
machines.
12.3
The directory MCPL/natmcpl contains files that make up the native code versions
of MCPL for Linux. Look at the file README in MCPL/natmcpl/doc to see how to
install and use the system. README in MCPL/natmcpl/doc to see how to install
and use the system.
MCPL/natmcpl contains a version of a MCPL system compiled into native
assembly language, currently for Intel 386/486/Pentium based machines running
under Linux. It uses a very simple codegenerator (cvmial386) that translates
the MCPL internal assembly language (MIAL) into Gnu assembler for the 386.
MCPL is translated to MIAL by a BCPL program called mcpl2mial, whose source
code is in BCPL/bclpprogs/mcpl2mial.b. MIAL is sufficiently close to machine
code to make it possible to modify cvmial386 for other architectures in about
three days.
13
13.1
Example Programs
Coins
The following program prints out how many different ways a sum of money can
be composed from coins of various denominations.
GET "mcpl.h"
FUN ways
: 0,
?
: ?,
[0]
: s, coins[>s]
: s, coins[v ]
.
=>
=>
=>
=>
1
0
ways(s, @ coins!1)
ways(s, @ coins!1) + ways(s-v, coins)
FUN t
: sum => writef("Sum = %3d,
sum,
)
.
Ways = %4d\n",
ways(sum, [50, 20, 10, 5, 2, 1, 0])
54
13.2
13 EXAMPLE PROGRAMS
Primes
The following program prints out a table of all primes less than 1000, using the
sieve method.
GET "mcpl.h"
MANIFEST Upb = 1000
FUN start : =>
writef "\nTable of prime numbers\n\n"
LET count=0, isprime=VEC Upb
FOR i = 2 TO Upb DO isprime!i := TRUE
FOR p = 2 TO Upb IF isprime!p DO
{ LET i = p*p
UNTIL i>Upb DO { isprime!i := FALSE
i +:= p
}
writef(" %3d", p)
IF ++count MOD 10 = 0 DO writef "\n"
}
writef "\nEnd of output\n"
RETURN 0
.
13.3
Queens
The following program calculates the number of ways n-queens can be placed on
a nn chess board without any two occupying the same row, column or diagonal.
GET "mcpl.h"
STATIC count, all
FUN try
: ?, =all,
? => count++
: ld, row, rd => LET poss = ~(ld | row | rd) & all
WHILE poss DO
{ LET bit = poss & -poss
poss -:= bit
try( (ld|bit)<<1, row|bit, (rd|bit)>>1 )
}
.
55
13.4 Fridays
FUN start : =>
all := 1
FOR i = 1 TO 12 DO
{ count := 0
try(0, 0, 0)
writef("There are %5d solutions to %2d-queens problem\n",
count,
i )
all := 2*all + 1
}
RETURN 0
.
13.4
Fridays
The following program prints a table of how often the 13th day of the month lies
on each day of the week over a 400 year period.
GET "mcpl.h"
MANIFEST Mon = 0,
Jan = 0,
STATIC
count
days
daysinmonth
dayname
=
=
=
=
Sun = 6,
Feb = 1,
Dec = 11
[0,0,0,0,0,0,0],
0,
[31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
["Monday", "Tuesday", "Wednesday","Thursday",
"Friday", "Saturday", "Sunday"]
.
FUN start : =>
FOR year = 1973 TO 1973 + 399 DO
{ daysinmonth!Feb := febdays year
FOR month = Jan TO Dec DO { LET day13 = (days+12) MOD 7
(count!day13) ++
days +:= daysinmonth!month
}
}
FOR day = Mon TO Sun DO writef(" %3d %ss\n", count!day, dayname!day)
RETURN 0
.
FUN febdays : year => year MOD 400 = 0 -> 29,
year MOD 100 = 0 -> 28,
year MOD 4
= 0 -> 29,
28
.
13.5
Prover
This program is a very free translation of the LISP version of the Wang Algorithm
given in the LISP 1.5 book. It checks whether formulae in propositional logic are
56
13 EXAMPLE PROGRAMS
tautologies. The program includes both a parser for the expression and a prover
function pr to test whether it is true for all possible settings of the propositional
variables.
GET "mcpl.h"
/* This is a very free translation into MCPL by M. Richards
of the LISP version of the Wang Algorithm given in the
LISP 1.5 book.
*/
MANIFEST
Id, Not, And, Or, Imp, Eqv,
Lparen, Rparen,Eof,
E_syntax=100, E_space
// Lexical tokens
// Exceptions
FUN member : ?,
0 => FALSE
: x, [=x, ?] => TRUE
: x, [ ?, ys] => member(x, ys)
.
FUN add : x, xs => member(x, xs) -> xs, mk2(x, xs)
.
FUN pr
: ?, 0, ?,
0 => FALSE
: al, 0, ar,
[[Not,x], cr] => pr( al, [x,0], ar, cr )
: al, 0, ar, [[And,x,y], cr] => pr( al, 0, ar, [x,cr] )
AND
pr( al, 0, ar, [y,cr] )
: al, 0, ar, [[Or,x,y], cr] => pr( al, 0, ar, [x,[y,cr]], x)
: al, 0, ar, [[Imp,x,y], cr] => pr( al,[x,0], ar, [y,cr], x)
: al, 0, ar, [[Eqv,x,y], cr] => pr( al,[x,0], ar, [y,cr], x)
AND
pr( al,[y,0], ar, [x,cr], x)
: al, 0, ar,
[[Id,x], cr] => member(x, al)
OR
pr( al, 0, add(x,ar), cr )
: al,
[[Not,x],cl], ar, cr
: al, [[And,x,y],cl], ar, cr
: al, [[Or,x,y],cl], ar, cr
: al, [[Imp,x,y],cl], ar, cr
: al, [[Eqv,x,y],cl], ar, cr
: al,
.
[[Id,x],cl], ar, cr
57
13.5 Prover
//********************* Lexical Analyser ******************
STATIC
strp, ch, nch, token, lexval
FUN lex_init : str => strp := str; rch(); rch().
FUN rch : => ch, nch := nch, %strp
UNLESS nch=0 DO strp++
.
FUN lex : =>
: | \n
: A..Z
: (
: )
: ~
: &
: |
: =
: -, >
: 0
:
.
.
rch()
rch()
rch()
rch()
rch()
rch()
rch()
rch()
A .. Z
~x
x & y
x | y
x -> y
x = y
-->
-->
-->
-->
-->
-->
[Id, A] .. [Id, Z]
[Not, x]
[And,x,y]
[Or,x,y]
[Imp,x,y]
[Eqv,x,y]
58
13 EXAMPLE PROGRAMS
a,
a,
a,
a,
nexp
nexp
nexp
nexp
3)
2)
1)
1)
59
13.5 Prover
writef "\nLaws involving implication\n"
try "(P|Q -> R) = (P->R) & (Q->R)"
try "(P & Q -> R) = (P-> (Q->R))"
try "(P -> Q & R) = (P->Q) & (P->R)"
writef "\nClassical theorems\n"
try "P | Q -> P | ~P & Q"
try "(P->Q)&( ~P->R) -> (P&Q | R)"
try "P & Q | ~P & R = (P->Q) & (~P->R)"
try "(P->Q) | (P->R) = (P -> Q | R)"
try "(P = Q) = (Q = P)"
/* Sample problems from F.J. Pelletier,
Seventy-Five Problems for Testing Automatic Theorem Provers,
J. Automated Reasoning 2 (1986), 191-216.
*/
writef "\nProblem 5\n"
try "((P|Q)->(P|R)) -> (P|(Q->R))"
writef "\nProblem 9\n"
try "((P|Q) & ( ~P | Q) & (P | ~Q)) ->
~( ~P | ~Q)"
R))"
~(~P | Q)"
(Y=D)"
60
13 EXAMPLE PROGRAMS
/**************************************************************
*
*
Print Tree Functions and Data
*
*
prtree(tree, depth, maxdepth)
*
prlinev
*
**************************************************************/
STATIC
prlinev = VEC 50
FUN prtree : x, depth, maxdepth =>
LET opstr=0, upb=0
IF x=0 DO { writef "Nil"; RETURN }
{ MATCH x
: [Id, ch]
: [And, x,
: [Or, x,
: [Imp, x,
: [Eqv, x,
: [Not, x]
:
.
}
y]
y]
y]
y]
=>
=>
=>
=>
=>
=>
=>
writef("%c", ch);
RETURN
opstr, upb := "And",
2
opstr, upb := "Or",
2
opstr, upb := "Imp",
2
opstr, upb := "Eqv",
2
opstr, upb := "Not",
1
opstr, upb := "Unknown", 0
61
13.6 Eval
13.6
Eval
The following program is a simple parser and evaluator for lambda expressions.
GET "mcpl.h"
MANIFEST
Eof=0, Id, Num, Times, Div, Pos, Neg, Plus, Minus,
Eq, Cond, Lam, Ap, Y,
Syntax=1, Lookup, Eval
STATIC spacep
FUN lookup
: ?,
: n, [=n, val,
: n, [ ?,
?,
.
FUN eval
: [Id, x],
: [Num, k],
: [Pos, x],
: [Neg, x],
: [Times,x,y],
: [Div,x,y],
: [Plus,x,y],
: [Minus,x,y],
: [Eq,x,y],
: [Cond,b,x,y],
: [Lam,x,body],
: [Ap,x,y],
e
?
e
e
e
e
e
e
e
e
e
e
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
lookup(x, e)
k
eval(x, e)
- eval(x, e)
eval(x, e) * eval(y, e)
eval(x, e) / eval(y, e)
eval(x, e) + eval(y, e)
eval(x, e) - eval(y, e)
eval(x, e) = eval(y, e)
eval(b, e) -> eval(x, e), eval(y, e)
mk3(x, body, e)
MATCH eval(x, e)
: [bv, body, env] =>
eval(body, mk3(bv, eval(y, e), env))
.
: [Y, exp],
e =>
LET bigf = eval(exp, e)
MATCH bigf
// bigf should be a closure whose body is an
// abstraction eg Lf Ln n=0 -> 1, n*f(n-1)
: [f, [Lam, n, body], env] =>
LET res = mk3(n, body, 0) // environment to be filled in later
LET env1 = mk3(f, res, env) // make environment including f
res!2 := env1
// fill in environment component of closure
RETURN res
// return this closure
.
: ?,
? => RAISE Eval
.
62
13 EXAMPLE PROGRAMS
// Construct
//
//
//
//
//
//
//
//
//
//
//
a ,.., z
dddd
x y
Y x
x * y
x / y
x + y
x - y
x = y
b -> x, y
Li y
Corresponding Tree
-->
-->
-->
-->
-->
-->
-->
-->
-->
-->
-->
>
: | \n, ?
: ( | ) | * | / | + | - | L | Y | = | ,,
? => token := ch; rch()
: a..z, ?
: 0..9, ?
0,
:
.
?,
13.6 Eval
FUN prim
: => MATCH token
: Id
=> LET a = mk2(Id, lexval)
lex()
a
: Num => LET a = mk2(Num, lexval)
lex()
a
: Y => mk2(Y, nexp 6)
: L => lex()
UNLESS token=Id RAISE Syntax
LET bv = lexval
mk3(Lam, bv, nexp 0)
: ( => LET a = nexp 0
UNLESS token=) RAISE Syntax
lex()
a
: + => mk2(Pos, nexp 3)
: - => mk2(Neg, nexp 3)
: ?
=> RAISE Syntax
.
.
FUN nexp : n => lex(); exp n
FUN exp
: n => LET a = prim()
MATCH (token, n)
:
( | Num | Id,
<6 => a := mk3(
Ap, a, exp 6)
:
*, <5 => a := mk3(Times, a, nexp 5)
:
/, <5 => a := mk3( Div, a, nexp 5)
:
+, <4 => a := mk3( Plus, a, nexp 4)
:
-, <4 => a := mk3(Minus, a, nexp 4)
:
=, <3 => a := mk3(
Eq, a, nexp 3)
: ->, <1 => LET b = nexp 0
UNLESS token=, RAISE Syntax
a := mk4(Cond, a, b, nexp 0)
:
?,
? => RETURN a
. REPEAT
.
FUN mk1 : a
=> !---spacep := a; spacep.
FUN mk2 : a, b
=> mk1(b); mk1(a).
FUN mk3 : a, b, c
=> mk1(c); mk1(b); mk1(a).
FUN mk4 : a, b, c, d => mk1(d); mk1(c); mk1(b); mk1(a).
FUN wrs : s => writef("%s\n", s).
FUN wrn : n => writef("%d\n", n).
FUN try : e => { newline()
wrs e
spacep := @ (VEC 1000)!1000
wrn ( eval(parse e, 0) )
} HANDLE : Syntax => wrs "Bad syntax"
: Lookup => wrs "Bad lookup"
: Eval
=> wrs "Bad eval"
.
.
63
64
13 EXAMPLE PROGRAMS
13.7
The following program is a simple demonstration of the algorithm for the fast
fourier transform. Instead of using complex numbers, it uses integer arithmetic
modulo 65537 with an appropriate Nth root of unity.
GET "mcpl.h"
MANIFEST
Modulus = #x10001,
// 2**16 + 1
= N - 1,
= N>>1,
= 1
Omega**N = 1
// N is a power of 2
STATIC v, w
FUN start : =>
LET a = 1
// First check Omega has the right properties
FOR i = 1 TO Upb DO
{ a := mul(a, Omega)
IF a=1 DO writef("Omega****%d = 1\n", i)
}
UNLESS mul(a, Omega)=1 DO writef("Omega****%d ~= 1\n", N)
v := VEC Upb
w := VEC Upb
FOR i = 0 TO Upb DO v!i := i
65
4
12
5
13
6
14
7
15
dofft v
pr(v, 15)
// prints
-- Transformed data
// 65017 26645 38448 37467 30114 19936 15550 42679
// 39624 42461 43051 65322 18552 37123 60445 26804
invfft v
pr(v, 15)
// prints -- Inverse transform of transformed data
//
0
1
2
3
4
5
6
7
//
8
9
10
11
12
13
14
15
RETURN 0
.
FUN dofft : v =>
w!0 := 1
// Nth roots of unity
FOR i = 1 TO Upb DO w!i := mul(w!(i-1), Omega)
fft(N, v, 0, MSB)
reorder(v, v, MSB, LSB)
.
FUN invfft : v =>
w!0 := 1
// inverse Nth roots of unity
FOR i = 1 TO Upb DO w!i := ovr(w!(i-1), Omega)
fft(N, v, 0, MSB)
reorder(v, v, MSB, LSB)
FOR i = 0 TO Upb DO v!i := ovr(v!i, N)
.
FUN reorder
:
p, <p, 0, ? => RETURN
: [x], [y], 0, ? => x, y := y, x
:
p,
q, a, b => LET a1=a>>1, b1=b<<1
reorder(@p!a, @q!b, a1, b1)
reorder(p,
q,
a1, b1)
.
FUN fft
: nn, v, pp, msb => LET n=nn>>1, p=pp>>1
FOR i = 0 TO n-1 DO butterfly(@v!i, @v!(i+n), w!p)
IF n=1 RETURN
fft(n, @v!0,
p, msb)
fft(n, @v!n, msb+p, msb)
.
FUN butterfly
: [x], [y], wk => LET t = mul(y, wk)
x, y := add(x, t), sub(x, t)
.
66
13 EXAMPLE PROGRAMS
13.8
Turing
indicating the the head is positioned on the right hand A, with the answer repre-
67
13.8 Turing
sented by the six 1s.
GET "mcpl.h"
FUN start : =>
writef "Turing entered\n\n"
{ mk_init 200000
turing(s0, "A11", B, "111A")
} HANDLE : E_mk, mess => writef("Error: %s\n",
mess).
mk_close()
.
// Space Allocation ****************************************
STATIC spacev=0, spacep=0
MANIFEST E_mk
FUN mk_init : upb =>
spacev := getvec upb
IF spacev=0 RAISE (E_mk, "Unable to allocate workspace")
spacep := @ spacev!upb
.
FUN mk_close : => freevec spacev
.
FUN mk2 : x, y => LET p = @ spacep!-2
IF p<spacev RAISE (E_mk, "Out of space")
p!0, p!1, spacep := x, y, p
RETURN p
.
// Turing Machine Implementation ***************************
STATIC ltape, ch, rtape
FUN turing : init_state, lstr, char, rstr =>
ltape, ch, rtape := 0, char, 0
LET i
WHILE
WHILE
WHILE
= 0
rstr%i DO i++
i
DO rtape := mk2(rtape, rstr%--i)
lstr%i DO ltape := mk2(ltape, lstr%i++)
pr()
init_state ch
.
68
13 EXAMPLE PROGRAMS
.
.
FUN left : c =>
IF ltape=0 DO ltape := mk2(0, )
MATCH ltape
: [link, k] =>
.
.
FUN halt : c => ch := c; pr()
.
// Output Routines *****************************************
FUN pr : =>
prb ltape
// Print the tape on the left
writef("[%c]", ch)
// Print the current character
prf rtape
// Print the tape on the right
newline()
.
FUN prb
// Print list of chars backwards
:
0 => RETURN
: [chs, ch] => prb chs; wrch ; wrch ch
.
FUN prf
// Print list of chars forwards
:
0 => RETURN
: [chs, ch] => wrch ch; wrch ; prf chs
.
13.8 Turing
// Turing Machine Definition *******************************
FUN s0 :
:
:
.
FUN s1 :
:
:
.
FUN s2 :
:
.
FUN s3 :
:
:
.
FUN s4 :
:
.
FUN s5 :
:
:
.
1 => s1 (right 0)
A => s2 (right )
c => s0 (left
c )
A => s3 (left A)
X => s1 (right 1)
c => s1 (right c )
A => s5 (right A)
? => s2 (right )
B => s0 (left B)
1 => s4 (right X)
c => s3 (left
c )
=> s3 (left X)
c => s4 (right c )
=>
halt A
X => s5 (right 1)
c => s5 (right c )
/*
The above Turing Machine will multiply two numbers
given in unary. It is exercised by the call:
turing(s0, "A11", B, "111A")
which will trace the execution of the machine as follows:
A
A
A
A
A
A
A
A
A
A
A
A
1 1[B]1 1 1 A
1[1]B 1 1 1 A
1 0[B]1 1 1 A
1 0 B[1]1 1 A
1 0 B 1[1]1 A
1 0 B 1 1[1]A
1 0 B 1 1 1[A]
1 0 B 1 1[1]A
1 0 B 1 1 X[A]
1 0 B 1 1 X A[ ]
1 0 B 1 1 X[A]X
1 0 B 1 1[X]A X
69
70
REFERENCES
References
[CM81]
[HS87]