Like many who grew up in the 1980s, my first programming language experience was on BASIC (on the VIC-20, in my case) and although I haven’t used it for 30 years I felt right at home.
MTS BASIC falls firmly in the first camp, with a limited number of keywords, restrictions on numbers and names of variables, but with support for matrix operations that was not present in later versions of the language.
The below is a quick look at the main features in MTS BASIC; see the Further Information section for more complete guides.
Program format
Each line of the program must start with a line number - note this is different from line numbers in MTS native files and only integers are allowed.
Full line comments can be introduced with REM or *. To add a comment that lasts until the end of the line you can use /* (very confusing to C programmers!).
10 rem This is a comment
20 * This also
30 let x = 3 /* Line comment
40 let y = 4
Types and variables
BASIC has two data types (only one less than Javascript!) - floating point numbers and strings. Numeric variable names must start with a letter followed by one or two numbers; string variables names must have two identical letters followed by an optional number (ie AA - ZZ, AA1, AA2 etc). Maximum length of strings is 127 characters. Variables can be initialised with LET:
10 let A = 1.23
20 let A1 = 3
30 let AA = "Hello world"
You can have arrays and matrices of numbers which can be indexed using (), eg a(1) or b(2,3). The size can be set using the DIM statement but if not given they are size 10. Indices start at 0. The below will print 100.
Strings are implemented as vectors of cells containing strings, so AA is the same as AA(0) and you can also access AA(1), AA(2) etc.
Statements
INPUT will prompt for the user to enter a value which will be assigned to a variable; PRINT will display a variable or expression, using ; or , to control formatting.
10 input a, b
20 print a; b, (a+b)/2
: run
? 10
? 20
10 20 15
INP and OUT are like INPUT and PRINT but also return the value assigned to the variable.
GOTO x will jump unconditionally to line x. Note you can’t GOTO a non-executable line like a comment. There is also a rather hairy computed GOTO where they program will jump to a line based on the result of an expression.
10 goto 40
20 print "This will be printed second"
30 stop
40 print "This will be printed first"
45 let a=2
50 goto (60,60,20,60) a+1
60 print "This will not be printed"
The IF .. THEN statement will calculate an expression and then either jump to another line or execute a statement. Note that there is no ELSE.
10 let a = 5
20 if a > 1 then 40
30 print "This will not be printed"
40 if a <> 5 then: print "This will not be printed"
50 if a = 5 then: print "This will be printed"
Simple loops can be done with FOR .. NEXT with an optional STEP.
10 for i = 1 to 10
20 for j = 0 to 10 step 2
30 x = x + (i*j)
40 next j
50 next i
60 print x
GOSUB x will call a subroutine at line x; execution will resume from the current line when a RETURN statement is found.
It’s possible to temporarily transfer execution to another program with CALL or permanently transfer with CHAIN.
Simple functions can be defined with DEF, eg DEF FNS(x)=x*x for a square function. Only one parameter is allowed and the function name can only be a single character A-Z.
STOP will end a program and PAUSE will prompt the user for input before continuing.
Library functions
BASIC comes with a number of library functions. Note that you have to assign results of these functions to variables rather than printing them directly - so in the example below I can’t do PRINT CLS().
String operations include conversion to numbers (STN) and back (NTS) along with access to the surrounding environment such as UID for user ID and CLS for time of day.
10 let ss = cls()
20 print ss
30 let ss = uid()
40 print ss
: run
0:59:02
ST01
Numeric functions include mathematical support functions like ABS() and SIN().
Matrix and vector support is especially powerful, with the ability to enter, print and do matrix operations. The below will prompt for a 2x2 matrix, add it to its transpose and then print it.
10 dim x(2,2), y(2,2), z(2,2)
20 mat input x
30 mat let y = trn(x)
40 mat let z = x + y
50 mat print z
: run
? 1,2
? 3,4
2 5
5 8
There is also limited support to read and write files - but these are special data files attached to the BASIC environment rather than general MTS files.
And that’s pretty much it for BASIC. In the next post we’ll look at a larger example of a MTS BASIC program.
Further information
MTS Volume 10 offers a complete description of the BASIC language and environment.
As a gentle start to this series we’ll look at something suitably basic - the BASIC language itself.
The BASIC language
Originally developed at Dartmouth in 1964, BASIC is a simple unstructured language influenced by FORTRAN and ALGOL. It was designed to be an easy to learn language for people who were not experienced computer scientists. It achieved wide popularity in the 1980s when it was included in many home computers and in the 1990s-2000s was in common use on Windows as Visual Basic and Visual Basic.NET, which included object orientated features.
BASIC on MTS
The version of BASIC in MTS was developed at the University of Michigan and appears to date from the early 1970s, with the first version appearing in D2.0. The implementation is close to Dartmouth Basic, with some minor changes such as using ** instead of ↑ for exponentiation.
Prerequisites
No special installation instructions to get BASIC running - just do the standard D6.0 setup as described in this guide and then sign on as a regular user such as ST01.
Running BASIC
Type $run *BASIC to start. Unlike other compilers on MTS, this will put you in a self-contained environment where you can create, run, debug and organise BASIC programs.
When you start BASIC you will see the : prompt. Here you can enter commands, starting with /, to control the BASIC environment. Use /open NAME to create a new program called NAME. This will start a new file in memory that will last for the rest of the session; to save the current version to disk do /save. Note the files are saved in a special, sequential format that can only easily be read by BASIC.
To enter the actual program you can type lines starting with a line number. To correct lines you can retype it; there are also / commands such as /alter to do simple line editing. Type a line number on its own to delete it. As it is a separate environment you can’t use tools like $edit to visually edit files; you could create a text file outside of BASIC and use /include FILE to import it, however.
To view the code, type /list; to execute the program, type /run. BASIC will then compile the source file and if there are no syntax errors start to execute it. If there are any run time errors you will enter the debugger which has a > prompt. Here you can /display variables, change them with /modify and either /continue or /stop execution.
To leave BASIC, type /bye, remembering to /save first if you want to keep your program. When you start BASIC again, use /permcatalog to view the programs on disk and /open to load one.
Hello world
Here’s a terminal log of how to start BASIC and create a simple program.
# $run *basic
: /open hello
& "HELLO" has been created.
: 10 for i = 1 to 5
: 20 print "Hello World!"
: 30 next i
: /run
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
+ Program Ends
: /save
& Done
: /bye
& Off at 21:53:35 on 11-17-14
In the next post we’ll look at BASIC language features in more detail.
Further information
See the Wikipedia article for more details on the history of BASIC. In 2014, Dartmouth created the website BASIC at 50 which has some interesting background on the creation of the language. TIME did an article looking at BASIC’s legacy.
For the MTS version, MTS Volume 10 offers a complete description.
There’s also a great beginner’s introduction called “Fundamental Use of the Michigan Terminal System (Including Simple MTS BASIC)” by Thomas J. Schriber from the UM Business School, which is available in the Hathi Trust archives or on Google Books.
MTS supported over 40 languages, representing a cross-section of programming tools available in 1988, from well known ones like LISP and FORTRAN to the more obscure such as GOM and PLUS.
In this series of posts I will take a look at each of these - plan of attack is 1-3 posts per language covering:
A brief introduction, how to get it working on MTS and “Hello World”
A tour of language features
A more complex example, either implementing a task from Rosetta Stone or running a classic piece of code for that language.
Some languages may warrant more or less attention, and I will also look at the programming environment such as the debugger and link editor.
Trying to do all this may be quixotic - and I may lose interest among all the FORTRAN dialects - but it will be interesting to see what was available then and how they compare to the languages of today.
I am a working programmer by trade rather than an academic so don’t expect too much deep insight or theory - and if you spot any mistakes please let me know.
For new users of MTS, follow the setup guide, log on as a regular user such as ST01 and check the ‘Prerequisites’ section in the first article for each language for how to get it running - in most cases no extra work is needed once you have MTS running.
Source code
Source code for each of the examples can be found on Github.
Coming up later
ALGOL 68
BCPL
COBOL
GM APL
ICON
LISPs
PROLOG/KR
XPLs
Specialised languages
*CSMP Continuous System Modeling Program
*UMIST An interactive text-processing language patterned after TRAC
*CLPARSEGEN A parser generator that outputs PLUS code.
Cross assemblers
I will probably not look at these, unless they are especially interesting.
*1130ASM IBM 1130 and 1800 Assembler
*11ASR PDP-11 Assembler, version AN231
*1ASR PDP-1 Assembler (obsolete in D6.0)
*8ASR PDP-8 Assembler (obsolete in D6.0)
*9ASR PDP-9 Assembler (obsolete in d6.0)
*ASMT IBM TSS Assembler (5.0T2)
*I8080ASR A relocatable cross-assembler for the INTEL 8080
*M6800ASR An absolute cross-assembler for the Motorola M6800
*M6809ASR An absolute cross-assembler for the Motorola 6809
*MCS650XASR An absolute cross-assembler for the MOS Technology MCS6500 family
*PDP11ASR A relocatable cross-assembler for the DEC PDP11
*Z80ASR A Z80 cross-assembler
UNSP:11PAL A DEC compatable PDP-11 assembler and simulator
UNSP:F8ASR A cross-assembler for the Mostek F8
UNSP:PLM A cross compiler for the INTEL 8080 PL/M language
Further information
This list is based on the information in the MTS Archive describing what languages were available at the time of D6.0 and which work today, including those where there is no license available.
The information that MTS stores about files is quite different from today’s operating systems and it also has an interesting permissions model. In this post we’ll look at this information in more detail using the $FILESTATUS and $PERMIT commands.
$FILESTATUS
$FILESTATUS, which can be abbreviated to $F, displays information about files. It takes three parameters:
name
format
information
Running it without any parameters lists the files owned by the current user:
# f
ALPHA BETA
Use ? as a wildcard to specify what files to display. Note that this will not include temporary files; to see these you will need to do f -?. To see all files under another ID use f CCID:?; this will only list files you have access to.
The information parameter allows you to see further details about each file. To see all information MTS has, use the TOTAL parameter:
Size and MaxSize give the current and maximum size in pages (4096 bytes)
MTS allocates more space to a file than its contents to allow it to grow. ExpFac is the expansion factor each time it needs to grow, here the default 10%. MinSaves shows how much space would be saved if the file was resized to its minimum possible size; TruncSaves shows spaces saved if the $truncate command was run to remove unused space at the end of the file. AvailSpace gives the amount of space in bytes before the file needs to be expanded.
Type is the file type, line or sequential
RPM is the number of references to the file per month since its creation date and IdleDays the number of days since its last reference. RPM always seems to be zero and Idledays larger than 36,000 whatever the usage of the file, so I suspect something is not working correctly here. Update 14-Apr-2015: this is due to a Y2K bug, see Jeff’s comment below.
UseCnt gives the number of times the file has been used since its creation; this appears to be set correctly.
Lines, Avlen and MaxLen give the number of lines and their average and maximum lengths
Holes and MaxHole refer to holes in line file due to line replacement operations that have left spaces
Create gives the creation date, LastRef the time the file was last used, LastCatalog the time the catalog was updated for this file and LastData the last time data was changed in the file
Volume and Loc gives the disk volume and type of storage the file is on
Owner gives the file owner’s ID
PKey and the remaining information gives details on who can access the file, which we will look at further below.
Instead of showing all this you can specify individual items to display:
Finally, the format parameter determines how the information selected will be displayed. You can choose name=value pairs, columns or packed, with options for headers, spacing and indentation. For example:
# fi ? col spacing=3 create lastref
File name Create DateLast
Date Referenc
ALPHA Nov15/14 Nov15/14
BETA Nov15/14 Nov15/14
$PERMIT
The $PERMIT command, or just $P, takes three parameters
files
access level
users
You can also selectively clone permissions from one file to another with the syntax $PERMIT files1 LIKE files2 EXCEPT access_level.
There are six basic categories of access level:
R - Read
WE - Write-expand - can add lines to a file but can’t change or delete existing ones
WC - Write-change - can change or delete lines from a file but can’t add new ones
T - Truncate and renumber
D - Destroy and rename
P - Permit - allow other users to change permissions
These can be combined using various aliases, for example RW allows the first three categories above, N specifies no access and U unlimited access
The users parameter allows you specify a single user ID or even a wildcard user ID (like ST0?) as well as other combinations:
ALL - all users
OTHERS - all users except those which already have access info specified
ME - access for signed in user ID
OWNER - file owner
PROJECT=xxxx - users under project ID xxxx
PKey=xxxx - see the next section
*PERMIT will show the current level of access after each command. Some examples:
# permit alpha r st01, rw st02
Access to file "ALPHA" is now Read ST01, RW ST02, Unlim Owner, None Others
# permit alpha d st0?, read others
Access to file "ALPHA" is now Read ST01, RW ST02, Unlim Owner, Destroy ST0?,
Read Others
Program keys
You may want a file to be only accessible when run by a certain program. In Unix the solution is setuid, where when a program is run your user ID temporarily becomes the user ID of the program so it can access files owned by that user ID. This can lead to security issues though, eg if a buffer overrun in the program can be exploited to allow access to other files.
MTS has a better solution: each program can have a key, and access to a file can be granted to that key. You can then run a privileged program under your normal ID.
One example is the *CKID program that runs at sign on for certain shared IDs and checks which secondary IDs can use that ID. The config file, CKID:ACCESS should only be accessible to that program, so it is permitted to the pkey for *CKID:
# fi ckid:access access
CKID:ACCESS FullAccess=None Others, Read Pkey=*CKID
You can set this up for your own programs by setting a pkey for the executable and then permitting access. Say you have a database program MYDB which is the only way a user should access the database file DBFILE. First set a program key:
$CONTROL MYDB PKEY=MYDB
and then permit access:
$PERMIT DBFILE RW PKEY=MYDB
Further information
See the online help for $FILESTATUS and $PERMIT, and a detailed write up in MTS Volume 1.
This post is a more detailed look at what happens at system start-up in MTS - it’s useful to know what job is printing what messages in case something goes wrong, and also as a step towards further customisation of your MTS system.
Most of this was put together by reading the job programs and config files referenced by the Operator’s manual: if I’ve omitted or misunderstood anything please let me know in the comments and I’ll update this post.
IPL
When you start the IPL process by typing ipl 260 on the Hercules console, the system will load IPLBOOT from a special area on the disk drive on device 260, which will in turn load UMLOAD and then IPLREAD.
IPLREAD will give the familiar prompt
Do you wish to run the current system? (YES or NO).
Typing yes at this point will cause the system to load the file *IPL.0 and then execute it.
Typing no will drop you into the command line for IPLREAD: here you can examine or change memory, or choose a different system to load. RAMROD keeps the last 3 versions of the system in *IPL.1, *IPL.2 etc, so if there was something wrong with your last change to the system you could boot the previous version with the commands
LOAD NAME=*IPL.1
START
INIT
The system will then configure itself and start the INIT job, which will print:
Time and date have been set to ...
and will start the OPER and DMGR jobs which manage the operator’s console and disk respectively.
MTS INIT
The system will then run a MTS task as user INIT, running commands from the file INIT:INITCMD. This will print the system name and prompt for the reason for rebooting, which will be written to file INIT:REASON; history of all reboots can be found in INIT:RELOADLOG.
It will then start the PDP (Paging Drum Processor) and a number of operator jobs:
*CHK to display a list of offline devices for the operator to check (the distribution comes with a number of disk devices defined, but you only need one on Hercules, so this can be ignored by pressing PA2).
*S2L to load shared components into memory, eg the editor; this will invoke the Named Address Loader *NAL. I think this also prints the PEEK initialization messages.
It will also clean out any temporary files left after an unexpected reboot and start the system status display at the top of the operator’s console.
Basic start-up is now complete.
HASP
Next, the operator would start up the spooler by running HASP, and the HASP initialisation jobs MTS *HSP. The latter reads commands from STRT:HSP which defines which devices should be started:
O $START MORE RDR1
O $START PCH1
O $START TN PTR2
LAS
The penultimate step is to ’let out the lines’ by running MTS *LAS. This reads commands from STRT:LAS. It will start MTS on each of the configured terminal devices with MTS DS02 … MTS DS05. It will then run the following operator jobs:
*CLK to start the job scheduling program
*TPR which I believe is part of the tape cataloguing system
*MGR which manages email
*TCM and *FTP SERV which refer to network services we don’t have on Hercules.
Finally, start batch job processing again with the HASP command $release ex.
Further information
The Operator’s manual has further information on the start up process and many of the operator jobs.
On the distribution tapes, Component 816 has documentation on Clockwatcher.