<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Try MTS</title>
    <link>https://try-mts.com/</link>
    <description>  Try MTS</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 25 Aug 2023 16:40:19 +0000</lastBuildDate>
    <atom:link href="https://try-mts.com/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Site Update, August 2023</title>
      <link>https://try-mts.com/site-update-2023-08/</link>
      <pubDate>Fri, 25 Aug 2023 16:40:19 +0000</pubDate>
      <guid>https://try-mts.com/site-update-2023-08/</guid>
      <description>&lt;p&gt;archive.michigan-terminal-system.org is currently down, so I&amp;rsquo;ve replaced links on this site with links to archive.org. &lt;a href=&#34;https://web.archive.org/web/20230206195434/http://archive.michigan-terminal-system.org/home&#34;&gt;Here&lt;/a&gt; is a recent snapshot of the site and &lt;a href=&#34;https://archive.org/details/michigan-terminal-system-distribution-documentation-and-files&#34;&gt;here&lt;/a&gt; is a copy of the MTS distribution that can be easily downloaded.&lt;/p&gt;
&lt;p&gt;Please leave a comment or &lt;a href=&#34;mailto:rupert@try-mts.com&#34;&gt;email me&lt;/a&gt; if anything is still broken.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PL360 - Introduction and a hypotenuse program</title>
      <link>https://try-mts.com/pl360-introduction-and-a-hypotenuse-program/</link>
      <pubDate>Sun, 01 Mar 2020 10:18:30 +0000</pubDate>
      <guid>https://try-mts.com/pl360-introduction-and-a-hypotenuse-program/</guid>
      <description>&lt;p&gt;In this single part post, we&amp;rsquo;ll take a quick look at PL360, a low level programming language targeted at the System/360.&lt;/p&gt;
&lt;h2 id=&#34;pl360&#34;&gt;PL360&lt;/h2&gt;
&lt;p&gt;PL360 was initially designed by Niklaus Wirth at Stanford in the late 1960s. He was working on a successor to ALGOL and was unsatisfied with the choices of implementation languages: assembler or FORTRAN. Quoting from his original paper, he wanted a tool which would:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(1) allow full use of the facilities provided by the 360 hardware, (2) provide convenience in writing and correcting programs, and (3) encourage the user to write in a clear and comprehensible style.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a result, he and his team came up with PL360. As the name suggests, the language is tied to the architecture of the System/360, providing direct access to opcodes like &lt;code&gt;STC&lt;/code&gt; and the need to specify which registers to use. It also contains high level features like &lt;code&gt;IF ... THEN&lt;/code&gt; conditionals and &lt;code&gt;WHILE ... DO&lt;/code&gt; loops, plus syntax to make common tasks easier, eg &lt;code&gt;R1 := R1 + R2 + R3&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;pl360-on-mts&#34;&gt;PL360 on MTS&lt;/h2&gt;
&lt;p&gt;PL360 on MTS seems to date back to at least distribution 1.0. The version was have on the latest distribution 6.0 has the following note in the drive file listing under component 108:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This version of PL360 is based on one received from Stanford in late 1977 and installed at UM in January, 1978. The latest versions are distributed by UBC as part of the SPIRES distribution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PL360 is installed on MTS as &lt;code&gt;*PL360&lt;/code&gt;. The compiler takes input from &lt;code&gt;SCARDS&lt;/code&gt; and writes an object file to &lt;code&gt;SPUNCH&lt;/code&gt;. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$RUN *PL360 SCARDS=input SPUNCH=object
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At run time, to use the standard I/O library you need to add &lt;code&gt;*PL360LIB&lt;/code&gt; to the &lt;code&gt;$RUN&lt;/code&gt; command, ie:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$RUN object+*PL360LIB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that documentation linked below mentions that some procedures in the standard library are not available on MTS, such as &lt;code&gt;VALTOBCD&lt;/code&gt; which converts a number to an EBCDIC representation.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get this language running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;hypotenuse-program&#34;&gt;Hypotenuse program&lt;/h2&gt;
&lt;p&gt;Our sample program will calculate the hypotenuse of a right angled triangle: this is taken from the PL360 textbook linked below. The original version used &lt;code&gt;VALTOBCD&lt;/code&gt; etc which is not available, so we&amp;rsquo;ll supply the input values in the main program and use the MTS debugger to view the results.&lt;/p&gt;
&lt;p&gt;The program uses Newton&amp;rsquo;s methods to take square roots &lt;code&gt;R&#39;&#39;  =  ((X/R&#39;) + R&#39;)/2&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;where R&amp;rsquo; is an approximation for the root of X, and R&amp;rsquo;&amp;rsquo; is the next approximation. When the difference between R&amp;rsquo; and R&amp;rsquo;&amp;rsquo; becomes very small, R&amp;rsquo;&amp;rsquo; is the assumed root of X. The first approximation of R&amp;rsquo; is taken to be X with its exponent cut in half. Thus, the first approximation for the root of 128 would be 8. Likewise, 1/8 would be the approximation for 1/128. (128 = #80)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The procedure to calculate square roots looks like below. The format is mostly unchanged; I&amp;rsquo;ve added line numbers in the first two columns which would not be present in the actual program text.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; 1   PROCEDURE SQRT (R14);  IF F01 &amp;gt; 0L THEN
 2   |-- THIS PROCEDURE TAKES THE SQUARE ROOT OF THE VALUE IN F01 --|
 3   BEGIN  LONG REAL TEMPCELL;  |-- TEMPORARY STORAGE --|
 4          LONG REAL REGISTER DIFF SYN F67,  |-- TEMPORARY --|
 5          APROX1 SYN F23, APROX2 SYN F45;   |-- REGISTERS --|
 6      TEMPCELL := F01;  R1 := R1-R1;
 7   |-- COMPUTE HALF THE ORIGINAL EXPONENT, EXCESS 64 NOTATION --|
 8      IC(R1,TEMPCELL);  R1 := R1 - #40S SHRA 1 + #40S;
 9      STC(R1,TEMPCELL);  APROX2 := TEMPCELL;  DIFF := 2L;
10      WHILE DIFF &amp;gt; 10&amp;#39;_6L DO  |-- FIND SQRT OF F01 --|
11      BEGIN  APROX1 := APROX2;  |-- NEW APPROXIMATION FROM OLD --|
12         APROX2 := F01/APROX1 + APROX1 / 2L;
13         DIFF := APROX2 - APROX1;  DIFF := ABS DIFF;
14      END;  F01 := APROX2;  |-- REPLACE F01 BY SQRT OF F01 --|
15   END;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The procedure implicitly takes its input and returns output to floating point register 0. R14 is used to denote the register containing the address to jump to on return.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;IF&lt;/code&gt; statement will guard against negative input values, skipping the whole procedure unless the input is positive.&lt;/p&gt;
&lt;p&gt;The body of the &lt;code&gt;iF&lt;/code&gt; starts on line 3 with &lt;code&gt;BEGIN&lt;/code&gt;. It creates temporary storage &lt;code&gt;TEMPCELL&lt;/code&gt; and provides synonyms for floating point registers to make the code clearer.&lt;/p&gt;
&lt;p&gt;Cutting the exponent is done by taking the byte containing the exponent (the &amp;lsquo;characteristic&amp;rsquo; in IBM&amp;rsquo;s language) out of the floating point value (with &lt;code&gt;IC&lt;/code&gt;), shifting it right one bit (which divides it by two) and then storing it back into the floating point value. Why is &lt;code&gt;#40S&lt;/code&gt; subtracted before the shift and then added back again? I had to look at the &amp;lsquo;Principles of Operation&amp;rsquo; for this: turns out characteristic is biased by +64, ie hex 40.&lt;/p&gt;
&lt;p&gt;We then use a &lt;code&gt;WHILE ... DO&lt;/code&gt; statement to loop the approximation calculation until the difference is less then 10^-6. Note that precedence is strictly left to right, so we can write the approximation calculation on line 12 in that way.&lt;/p&gt;
&lt;p&gt;To calculate the hypotenuse, we need to sum the squares of the two sides and call &lt;code&gt;SQRT&lt;/code&gt;. This can be easily done:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BEGIN
   F01 := 3.0 * 3.0;
   F23 := 4.0 * 4.0;
   F01 := F01 + F23;
   SQRT;
END.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the &lt;code&gt;END&lt;/code&gt; is terminated with a period to denote this is the main program.&lt;/p&gt;
&lt;h2 id=&#34;compiling-and-running-the-program&#34;&gt;Compiling and running the program&lt;/h2&gt;
&lt;p&gt;We compile the program as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$run *PL360 SCARDS=hyp.pl3 SPUNCH=hyp.l
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output shows a listing and a cross-reference table, and ends with &lt;code&gt;NO ERRORS DETECTED&lt;/code&gt; so we are good.&lt;/p&gt;
&lt;p&gt;To test, we run this under the debugger and print out the floating point registers at the end with &lt;code&gt;DUMP FRS&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$debug hyp.l
Ready
run
User program return.
Ready
dump frs

 Floating Point Registers

                 Hex              Decimal

   FR0 :  41500000 00000000    5.
   FR2 :  41500000 02C4F885    5.00000001031688268
   FR4 :  41500000 00000000    5.
   FR6 :  3A2C4F88 50000000    .103168826814936665E-07
stop
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;FR0 contains 5, so it looks like it worked!&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;PL360 occupies a unique slot in the set of languages on the System/360: tied closely to the architecture but operating at a higher level then assembler. Apart from Wirth&amp;rsquo;s work, it did enjoy some use for other applications, eg the &lt;a href=&#34;https://en.wikipedia.org/wiki/Stanford_Physics_Information_Retrieval_System&#34;&gt;SPIRES&lt;/a&gt; database system, but was not widely adopted on MTS or other systems.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The original paper Wirth wrote on PL360, &lt;a href=&#34;https://dl.acm.org/citation.cfm?id=321442&amp;amp;bnc=1&#34;&gt;PL360, a Programming Language for the 360 Computers&lt;/a&gt; gives background on why it was developed and an overview of the language.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://babel.hathitrust.org/cgi/pt?id=mdp.39015026567852;view=1up;seq=99&#34;&gt;MTS CCMEMO 75&lt;/a&gt; has an overview of the language and its implementation on MTS.&lt;/p&gt;
&lt;p&gt;Copies of the &amp;ldquo;PL360 Reference Manual&amp;rdquo; formerly distributed by Stanford can be found on the Internet, eg &lt;a href=&#34;http://web.eah-jena.de/~kleine/history/languages/pl360man.txt&#34;&gt;here&lt;/a&gt;. A textbook based on this manual can be found &lt;a href=&#34;https://web.stanford.edu/~guertin/manuals/PL360TXT.HTML&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Full source code for this program can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PLUS - Grep</title>
      <link>https://try-mts.com/plus-grep/</link>
      <pubDate>Sun, 26 Jan 2020 09:45:32 +0000</pubDate>
      <guid>https://try-mts.com/plus-grep/</guid>
      <description>&lt;p&gt;For the final post in this &lt;a href=&#34;https://try-mts.com/tags/plus/&#34;&gt;series&lt;/a&gt;, let&amp;rsquo;s write a real program in PLUS. Rather than working on a Rosetta Code problem, I&amp;rsquo;ll use this opportunity to implement a useful program, a version of Unix grep for MTS.&lt;/p&gt;
&lt;h2 id=&#34;grep&#34;&gt;Grep&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Grep&#34;&gt;Grep&lt;/a&gt; is a program from Unix to find text in files. The first version was written in around 1974 in PDP-11 assembly language, and is available on pretty much every Unix derived system today. I don&amp;rsquo;t believe there is an equivalent for MTS - the &lt;a href=&#34;https://try-mts.com/editing-files/&#34;&gt;MTS File Editor&lt;/a&gt; does have a rich pattern matching language but is a bit unwieldy to use from the command line.&lt;/p&gt;
&lt;p&gt;The requirements for the program are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input a pattern to search for, which should support some kind of regular expression to match arbitrary text&lt;/li&gt;
&lt;li&gt;input a file or list of files to search in&lt;/li&gt;
&lt;li&gt;compare the pattern to the contents of the files and print any lines that match.&lt;/li&gt;
&lt;li&gt;follow MTS conventions for calling the program.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pattern-matching&#34;&gt;Pattern matching&lt;/h2&gt;
&lt;p&gt;The first problem is how can we match patterns? There is no regular expression library like regex in Unix. Looking through &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol03-SystemSubroutineDescriptions-Sep1989.pdf&#34;&gt;Volume 3&lt;/a&gt;, on page 366.3 there is a set of routines that look promising:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Three system subroutines, PATBUILD, PATMATCH, and PATFREE, are
available for implementing $FILESTATUS-like pattern-matching capabili-
ties from user programs.&lt;/p&gt;
&lt;p&gt;PATBUILD will build a pattern from an input string. The input string
may be of any length and may specify a file name or a generic string.
For example:&lt;/p&gt;
&lt;p&gt;File names: &amp;ldquo;2CYB:data?&amp;rdquo;, &amp;ldquo;?.doc&amp;rdquo;, &amp;ldquo;2ABC:?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Generic strings: &amp;ldquo;Bill R?&amp;rdquo;, &amp;ldquo;in the state of ?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;PATMATCH will compare an input string against the pattern built by
PATBUILD. The input string may be of any length.&lt;/p&gt;
&lt;p&gt;PATFREE will free the storage used to build the pattern.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Although this is more suited for file wildcard expansion than general text search, we&amp;rsquo;ll use this as a quick way to get the program running.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfBritishColumbia/PlusLib-Jan1983.pdf&#34;&gt;PLUS Source Library
Definition&lt;/a&gt;
does not mention these routines, but looking through the actual contents of &lt;code&gt;*PLUS.SOURCELIB&lt;/code&gt; there are definitions available. For example for &lt;code&gt;patbuild&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;%Include(Numeric_Types);
type Patbuild_Procedure_Type is
     procedure
     reference parameters Patstring is value unknown,
        Strlen is value Integer,
        Patworkaddr is pointer to unknown,
     optional reference parameters Flags is value bit(32),
        Ccid is value character(4),
        Chars is value character(2),
     end;

/* Values used in Flags word */
constant Patbuild_Filename is &amp;#39;00000000&amp;#39;,
  Patbuild_Nonfilename is &amp;#39;00000001&amp;#39;,
  Patbuild_Ignore_Case is &amp;#39;00000000&amp;#39;,
  Patbuild_Dont_Ignore_Case is &amp;#39;00000004&amp;#39;;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;driving-the-pattern-matcher&#34;&gt;Driving the pattern matcher&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s define a data structure to be used by our grep program.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;global Grep
   /* The pat subroutines need a working area. */
   type Pattern_Work_Area_Storage_Type is bit(32);
   type Pattern_Work_Area_Type is pointer to Pattern_Work_Area_Storage_Type;
   constant Pattern_Work_Area_Size is Byte_Size(Pattern_Work_Area_Storage_Type);

   constant Wildcard_Character is &amp;#34;?&amp;#34;;

   type Pattern_Struct is
      record
         Work_Area is Pattern_Work_Area_Type,
         Error_Message is Varying_String,
         Match_Count is Integer
      end record;
end global Grep;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And also some procedures, using pass-by-reference for structs and strings to avoid copying.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;procedure Grep_Initialize is
   procedure
   reference parameters
      Pattern is Pattern_Struct,
      Pattern_Expression is Varying_String
   result
      Grep_Initialize_Result is Boolean
   end;

procedure Grep_Match is
   procedure
   reference parameters
      Pattern is Pattern_Struct,
      Query_Text is Varying_String
   result
      Match_Result is Boolean
   end;

procedure Grep_Terminate is
   procedure
   reference parameter
      Pattern is Pattern_Struct
   end;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Grep_Initialize&lt;/code&gt; will take our data structure and a pattern expression input by the user. It will fill out the data structure and return a Boolean to show if it worked or not. If it failed, the &lt;code&gt;Error_Message&lt;/code&gt; field in the struct will be set to an appropriate message.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Grep_Match&lt;/code&gt; will apply the pattern to a line of query text and return a Boolean stating whether there was a match or not. It will update the &lt;code&gt;Match_Count&lt;/code&gt; member in the structure so we can keep track of how many results we get.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;Grep_Terminate&lt;/code&gt; will free up the memory used by the system.&lt;/p&gt;
&lt;h2 id=&#34;grep_initialize-in-detail&#34;&gt;&lt;code&gt;Grep_Initialize&lt;/code&gt; in detail&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at the implementation of &lt;code&gt;Grep_Initialize&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;definition Grep_Initialize;
   Pattern.Match_Count := 0;
   Pattern.Error_Message := &amp;#34;&amp;#34;;
   Pattern.Work_Area := Getspace(0, Pattern_Work_Area_Size);

   if Length(Pattern_Expression) = 0
   then
      Pattern.Error_Message := &amp;#34;Missing pattern in PAR field&amp;#34;;
      return with False;
   end if;

   /* Add leading and trailing wildcards to match anywhere
      on a line. */
   variable Match_In_Line is Varying_String;
   Match_In_Line := Wildcard_Character || Pattern_Expression ||
                    Wildcard_Character;

   variable Len is Integer;
   variable Text_Ptr is pointer to Fixed_String;
   Get_String_Text_And_Length(Match_In_Line, Text_Ptr, Len);

   variable Return_Code is Return_Code_Type;
   Patbuild(Text_Ptr@, Len, Pattern.Work_Area, Pattern_Flags,
            return code Return_Code);

   /* Any error code greater than this is unrecoveable. */
   constant Patbuild_Error_Code_Threshold is 8;
   if Return_Code &amp;gt; Patbuild_Error_Code_Threshold
   then
      Pattern.Error_Message := &amp;#34;Error constructing pattern&amp;#34;;
      return with False;
   end if;

   return with True;
end Grep_Initialize;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It first starts by initializing the &lt;code&gt;Pattern&lt;/code&gt; structure we passed in. The work area is a piece of memory needed by the system pattern routines so this is allocated. It then checks the pattern expression provided to see if it is not empty. If OK, it adds the wildcard &lt;code&gt;?&lt;/code&gt; to the start and end of the expression, so we can match text anywhere on a line.&lt;/p&gt;
&lt;p&gt;We are nearly ready to call &lt;code&gt;Patbuild&lt;/code&gt; to initialize the pattern. However, our pattern expression is a &lt;code&gt;Varying_String&lt;/code&gt; and &lt;code&gt;Patbuild&lt;/code&gt; requires a location in memory for the underlying string and a length. To get this, we define a macro &lt;code&gt;Get_String_Text_And_Length&lt;/code&gt; that does some type casting to get access to the members of &lt;code&gt;Varying_String&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;macro Get_String_Text_And_Length

   /* Given a Varying_String, get the address of the underlying
      Fixed_String and copy the length to an Integer variable.
      This is needed for the calls to system subrountines
      like Patbuild below. */

   parameters are V_String, Text_Ptr, Len;
   equate V_String_Struct to V_String as Varying_String_Structure_Type;
   equate Text to V_String_Struct.Varying_String_Text as Fixed_String;
   Text_Ptr := Address(Text);
   Len := Length(V_String);
end macro;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The macro encapsulates the details of accessing these members and allows us to use this in other parts of the program, but avoids the cost of a procedure call as the code will be inlined to the current function.&lt;/p&gt;
&lt;p&gt;We can now call &lt;code&gt;Patbuild&lt;/code&gt; using the &lt;code&gt;return code Return_Code&lt;/code&gt; parameter to get the system routine result code. Volume 3 states this can fail under some cases, so we check this code and return an appropriate Boolean.&lt;/p&gt;
&lt;h2 id=&#34;grep_match-and-grep_terminate&#34;&gt;&lt;code&gt;Grep_Match&lt;/code&gt; and &lt;code&gt;Grep_Terminate&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The procedure to do the actual matching is then simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;definition Grep_Match;
   variable Len is Integer;
   variable Text_Ptr is pointer to Fixed_String;
   Get_String_Text_And_Length(Query_Text, Text_Ptr, Len);

   variable Return_Code is Return_Code_Type;
   Patmatch(Text_Ptr@, Len, Pattern.Work_Area, return code Return_Code);

   if Return_Code = 0
   then
      Pattern.Match_Count +:= 1;
      return with True;
   end if;
   return with False;
end Grep_Match;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Grep_Terminate&lt;/code&gt; just calls &lt;code&gt;Patfree&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;calling-the-grep-routines&#34;&gt;Calling the &lt;code&gt;Grep&lt;/code&gt; routines&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Main&lt;/code&gt; procedure can now be constructed by&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reading the PAR parameter from the command line to get the pattern expression&lt;/li&gt;
&lt;li&gt;calling &lt;code&gt;Grep_Initialize&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;loop, reading lines from &lt;code&gt;SCARDS&lt;/code&gt;, matching and printing results&lt;/li&gt;
&lt;li&gt;print a final count of matches&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;definition Main;
   variable Msg is pointer to Stream_Type;
   variable Pattern is Pattern_Struct;
   variable Pattern_Expression is Varying_String;
   variable Line is Varying_String;
   variable Return_Code is Return_Code_Type;

   Msg := Message_Initialize();

   Pattern_Expression := Trim(Par);
   if not Grep_Initialize(Pattern, Pattern_Expression)
   then
      Message(Msg, &amp;#34;&amp;lt;v&amp;gt;&amp;lt;/&amp;gt;&amp;#34;, Pattern.Error_Message);
   else
      /* Read lines from SCARDS and print any that match. */
      cycle
         Scards_Varying(Line, Return_Code);
         if Return_Code &amp;gt; 0
         then
            exit;
         end if;
         if Grep_Match(Pattern, Line)
         then
            Sprint_Varying(Line);
         end if ;
      end cycle;
      Message(Msg, &amp;#34;&amp;lt;i&amp;gt; match(es) found&amp;lt;/&amp;gt;&amp;#34;, Pattern.Match_Count);
   end if;

   Grep_Terminate(Pattern);
   Message_Terminate(Msg);
end Main;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;compiling-and-running-the-program&#34;&gt;Compiling and running the program&lt;/h2&gt;
&lt;p&gt;The program can be compiled with &lt;code&gt;$run *PLUS SCARDS=grep.plus SPUNCH=grep&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here are some examples of running the program.&lt;/p&gt;
&lt;p&gt;Matching a wildcard expression:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; # $run grep scards=hello.plus par=hello?world
 # Execution begins   16:37:36
   %Title := &amp;#34;Hello World Program&amp;#34;;
      Sprint_String(&amp;#34;Hello, world!&amp;#34;);
   2 match(es) found
 # Execution terminated   16:37:37  T=0.001
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Matching against a large file (around 372kB)&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run grep scards=*PLUS.SOURCELIB par=patmatch
# Execution begins   16:45:30
  PATMATCH    30300
  PATMATCH_PROCEDURE_TYPE         30400
  /begin Patmatch
  %Include(Patmatch_Procedure_Type);
  procedure Patmatch is system Patmatch_Procedure_Type
  /begin Patmatch_Procedure_Type
  type Patmatch_Procedure_Type is
  7 match(es) found
# Execution terminated   16:45:31  T=0.232
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Matching across several files:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run grep scards=hello.plus+grep.plus par=title
# Execution begins   16:47:19
  %Title := &amp;#34;Hello World Program&amp;#34;;
  %Title := &amp;#34;Find text in files&amp;#34;;
  2 match(es) found
# Execution terminated   16:47:19  T=0.006
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;areas-for-improvement&#34;&gt;Areas for improvement&lt;/h2&gt;
&lt;p&gt;By using the pattern routines provided by the MTS system, we have produced a basic grep program in 166 lines of code. There are, however, several ways we could improve it.&lt;/p&gt;
&lt;p&gt;The program only supports case-insensitive matching. This is a by-product of the way the PAR command line is passed to the program in upper case. It would be nice to search multiple files, such as all files in a catalog, without having to use concatenation. It would also be good to show which file and line was matched.&lt;/p&gt;
&lt;p&gt;One way to support this would be to use the PAR command line to pass a file specification, eg &lt;code&gt;PLUS:?&lt;/code&gt;, and use &lt;code&gt;SCARDS&lt;/code&gt; to pass in the text to be searched. The file specification could be expanded to a list of files, and by reading from &lt;code&gt;SCARDS&lt;/code&gt; we could supply a mixed case pattern expression.&lt;/p&gt;
&lt;p&gt;Another area to improve would be the pattern matching. As you can see from the run of &lt;code&gt;grep&lt;/code&gt; on a large file, it&amp;rsquo;s not that fast. We also don&amp;rsquo;t provide as rich a pattern matching language as POSIX regexes. Looking at the source code for the pattern matching routines, they do a simple scan of the text - which is reasonable for the purpose they were designed for, to match filenames - but for a more complete implementation we&amp;rsquo;d want to look at writing our own regex library using something like the &lt;a href=&#34;https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm&#34;&gt;Boyer-Moore&lt;/a&gt; algorithm to improve the search time.&lt;/p&gt;
&lt;p&gt;One last crazy idea. MTS has a PLUS compiler that produced PDP-11 object code, &lt;code&gt;*PLUS11&lt;/code&gt;. What if we compiled &lt;code&gt;grep.plus&lt;/code&gt; to run on the PDP_11, bringing grep back home? Unfortunately the pattern system routines like &lt;code&gt;patbuild&lt;/code&gt; aren&amp;rsquo;t available on the PDP-11, but it&amp;rsquo;s only a matter of implementation&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update 28 June 2020&lt;/em&gt;: according to the comment from Jeff Ogden, it is possible to get access to the contents of the parameter field without case conversion by using the PARSTR subroutine documented on page 366.1 of &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol03-SystemSubroutineDescriptions-Sep1989.pdf&#34;&gt;Volume 3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Any other suggestions for improvements? Please post in the comments.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts-on-plus&#34;&gt;Final thoughts on PLUS&lt;/h2&gt;
&lt;p&gt;I really enjoyed using PLUS. The language feels like C with a more Pascal-ly syntax, balancing power with expressiveness. The integration with MTS is extensive, and if you want to extend MTS further it would be an ideal choice.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for these programs can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s an interesting post on a FreeBSD mailing list by the original author of GNU grep on &lt;a href=&#34;https://lists.freebsd.org/pipermail/freebsd-current/2010-August/019310.html&#34;&gt;why it is so fast&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The MTS File Editor pattern matching language is described from page 159 onwards in &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol18-TheMTSFileEditor-Feb1988.pdf&#34;&gt;Volume 18&lt;/a&gt; of the MTS documentation set.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PLUS - Language Features</title>
      <link>https://try-mts.com/plus-language-features/</link>
      <pubDate>Sun, 12 Jan 2020 09:28:53 +0000</pubDate>
      <guid>https://try-mts.com/plus-language-features/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s look in more detail at some of the features of the PLUS language.&lt;/p&gt;
&lt;h2 id=&#34;format&#34;&gt;Format&lt;/h2&gt;
&lt;p&gt;PLUS is free format and case-insensitive, so the variable &lt;code&gt;count&lt;/code&gt; is the same as &lt;code&gt;COUNT&lt;/code&gt;. Comments are the same as PL/I and C, ie &lt;code&gt;/* ... /*&lt;/code&gt;. Statements are separated by semicolons. Input files to the compiler contain procedures and global definitions so they can be compiled separately.&lt;/p&gt;
&lt;h2 id=&#34;variables-and-types&#34;&gt;Variables and types&lt;/h2&gt;
&lt;p&gt;Variables must be defined before use and given a type. They can then be assigned to with &lt;code&gt;:=&lt;/code&gt;. Numeric types must be given a range, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variable Count is (1 to 5);
variable Temperature is (-100 to 100);
Count := 1;
Temperature := -100;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Type definitions can be names with &lt;code&gt;type&lt;/code&gt; and multiple definitions can be done on the same line:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type Temperature_Type is (-100 to 100);
variable Current_Temp is Temperature_Type;
variables High_Temp, Low_Temp are Temperature_Type;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The third line above is an example of multiple definitions on the same line; the  &lt;code&gt;variables&lt;/code&gt; and &lt;code&gt;are&lt;/code&gt; keywords are just syntactical sugar for the singular forms.&lt;/p&gt;
&lt;p&gt;Fixed string types include the length of the string, variable strings contain a min/max length. So in the below, &lt;code&gt;My_Name&lt;/code&gt; is varying. Strings can be concatenated with &lt;code&gt;||&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variable My_Name is character(50);
variables First_Name, Last_Name are character(1 to 25);
First_Name := &amp;#34;Jane&amp;#34;;
Last_Name := &amp;#34;Doe&amp;#34;;
My_Name := First_Name || Last_Name;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Bit types allow precise control over the amount of memory used:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variable Full_Word is bit(32);
variable Byte is bit(8);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are type attributes such as &lt;code&gt;aligned&lt;/code&gt; or &lt;code&gt;packed&lt;/code&gt; for further fine-grained control.&lt;/p&gt;
&lt;p&gt;Identifiers are like C &lt;code&gt;enum&lt;/code&gt;s:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variable Device is (Reader, Punch, Printer);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are also real and set types defined in the language but not fully implemented - so you can create a &lt;code&gt;real&lt;/code&gt; and assign to it but not do arithmetic.&lt;/p&gt;
&lt;p&gt;Arrays have an index (which can be non-numeric) and a value&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variable Lookup is array character(1) of character(8);
variable Matrix is array (1 to 10) of array (1 to 10) of Temperature_Type;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The size of the array must be determined at compile time. Elements can be accessed via round brackets.&lt;/p&gt;
&lt;p&gt;Pointers work similarly to C pointers but use the suffix &lt;code&gt;@&lt;/code&gt; for de-referencing and the built-in procured &lt;code&gt;Address&lt;/code&gt; for assignment.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variable Ptr is pointer to Count_Type;
type Count_Type is (1 to 10);
variable x is Count_Type;

x := 5;
Ptr := Address(x);
Ptr@ +:= 1;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that the type of what the pointer is accessing (here &lt;code&gt;Count_Type&lt;/code&gt;) can be defined after it is introduced. Note also the &lt;code&gt;+:=&lt;/code&gt; form to increment a value; here the underlying variable &lt;code&gt;x&lt;/code&gt;  pointed to by &lt;code&gt;Ptr&lt;/code&gt; is updated to be 6.&lt;/p&gt;
&lt;p&gt;Record types define structures.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type My_Record is
   record
      a is (1 to 1),
      b is character(50)
   end;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are also variant records which allow something similar to C&amp;rsquo;s &lt;code&gt;union&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;equate&lt;/code&gt; allows aliasing of variables or variable expressions.&lt;/p&gt;
&lt;h2 id=&#34;procedures&#34;&gt;Procedures&lt;/h2&gt;
&lt;p&gt;Procedures need to be declared before they are defined. They can also be treated as types.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/* Pull in definition of Integer */
%Include(Numeric_Types);

type Arithmetic_Procedure is
   procedure
   parameters
      x is Integer,
      y is Integer
   result
      answer is Integer
   end;

/* Declaration */
procedure Plus is Arithmetic_Procedure;

/* Definition */
definition Plus;
  answer := x + y;
end Plus;

/* ... */
variable x is Integer;
x := Plus(2, 3);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Parameters are passed by value by default; you can define them as &lt;code&gt;reference parameter&lt;/code&gt; to pass a reference in instead, and the compiler will behind the scenes pass a pointer and de-reference it in the procedure.&lt;/p&gt;
&lt;h2 id=&#34;expressions-and-control-structures&#34;&gt;Expressions and control structures&lt;/h2&gt;
&lt;p&gt;The usual range of arithmetic and logical expressions are supported. There is no separate Boolean type but anything non zero is counted as true.&lt;/p&gt;
&lt;p&gt;For conditionals, there is &lt;code&gt;if ... then ... elseif ... else... end if&lt;/code&gt; and &lt;code&gt;select ... case ... end select&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;cycle&lt;/code&gt; statement starts a infinite loop that can be broken out of
using &lt;code&gt;exit&lt;/code&gt; or restarted with &lt;code&gt;repeat&lt;/code&gt;, which can be used to write
loops. An example from the tutorial:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;variables Count, Return_Code are Integer,
String is Varying_String;
Count := 0;
/* Loop reading and writing records, counting number copied. */
cycle
   Scards_Varying(String, Return_Code);
   if Return_Code ¬= 0
   then
      /* Terminate loop when no more input */
      exit
   end if;
   Spunch_Varying(String);
   Count := Count + 1
end cycle;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A more restrictive &lt;code&gt;do&lt;/code&gt; statement allows single variable incrementing or decrementing, like we saw in the Hello World program in &lt;a href=&#34;https://try-mts.com/plus-introduction/&#34;&gt;part 1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;begin ... end&lt;/code&gt; can be used to start a new block with its own scoped variables, and supports &lt;code&gt;exit&lt;/code&gt; and &lt;code&gt;break&lt;/code&gt; like &lt;code&gt;cycle&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;constants-macros-and-compiler-directives&#34;&gt;Constants, macros and compiler directives&lt;/h2&gt;
&lt;p&gt;Compile time constants can be defined as follows&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;constant Zero_Temp is 0;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and can be used in type definitions as well as variable initialisation.&lt;/p&gt;
&lt;p&gt;Macros provide a procedure like interface and copy the text of the macro into the current scope.&lt;/p&gt;
&lt;p&gt;Compiler directives start with &lt;code&gt;%&lt;/code&gt;. We&amp;rsquo;ve seen &lt;code&gt;%Title&lt;/code&gt; in part 1 and &lt;code&gt;$Include&lt;/code&gt; above; other directives control things like compiler listing output and extra code checks.&lt;/p&gt;
&lt;h2 id=&#34;inputoutput-and-the-standard-library&#34;&gt;Input/output and the standard library&lt;/h2&gt;
&lt;p&gt;There are only a few built in procedures in PLUS; most functionality is provided by the standard source library &lt;code&gt;*PLUS.SOURCELIB&lt;/code&gt;. This include I/O routines like &lt;code&gt;Sprint_String&lt;/code&gt; or &lt;code&gt;Scards_Varying&lt;/code&gt; which respectively send a fixed string to the output device and read a varying string from the input device.&lt;/p&gt;
&lt;p&gt;A more complex, &lt;code&gt;printf&lt;/code&gt; style procedure called &lt;code&gt;Message&lt;/code&gt; can be used for string printing.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Message(Msg, &amp;#34;&amp;lt;hi&amp;gt; items found at &amp;lt;v&amp;gt;&amp;lt;/&amp;gt;&amp;#34;, count, location);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will print the halfword integer and varying strings included in the parameter list. The &lt;code&gt;&amp;lt;/&amp;gt;&lt;/code&gt; prints a new line. (I think the resemblance to HTML-like tags is coincidental, as PLUS predates even SGML of which HTML was a subset).&lt;/p&gt;
&lt;p&gt;The standard source library also includes many routines to call MTS system procedures so you can control devices, file attributes directly from PLUS.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;As mentioned in &lt;a href=&#34;https://try-mts.com/plus-introduction/&#34;&gt;part 1&lt;/a&gt;, the main language tutorial and reference is at &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfBritishColumbia/Plus-Oct1987.pdf&#34;&gt;Bitsavers&lt;/a&gt; along with the &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfBritishColumbia/PlusLib-Jan1983.pdf&#34;&gt;source library reference&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PLUS - Introduction</title>
      <link>https://try-mts.com/plus-introduction/</link>
      <pubDate>Sun, 05 Jan 2020 09:45:05 +0000</pubDate>
      <guid>https://try-mts.com/plus-introduction/</guid>
      <description>&lt;p&gt;The PLUS language holds a special place in the history of MTS - let&amp;rsquo;s look at it in more detail.&lt;/p&gt;
&lt;h2 id=&#34;plus&#34;&gt;PLUS&lt;/h2&gt;
&lt;p&gt;PLUS was created by Alan Ballard and Paul Whaley of the University of British Columbia in the 1970s, based on language work done for a previous experimental OS called SUE. According to the HELP pages for PLUS:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PLUS is superficially quite different from Sue or Pascal; however the underlying language semantics are really very similar.  Users familiar with the programming language C will also recognize much of its structure and semantics in PLUS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;plus-on-mts&#34;&gt;PLUS on MTS&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Plus_(programming_language)&#34;&gt;PLUS&lt;/a&gt; was used heavily at MTS sites as a system development language, as it provides a structured programming environment easier to use than lower level languages, while still allowing access to machine level features in a way a more generic language such as Pascal did not. The implementation also came with a large source library that made writing complex programs easier. It could generate object code both for the System/360 and both PDP/11 and Motorola 68000 systems.&lt;/p&gt;
&lt;p&gt;The compiler was written in PLUS and the source can be found on the distribution tapes under component 1042.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get this language running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;compiling-and-running-a-program&#34;&gt;Compiling and running a program&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;*PLUS&lt;/code&gt; compiler takes its input from &lt;code&gt;scards&lt;/code&gt; and writes output to &lt;code&gt;spunch&lt;/code&gt;, like many other language translators.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$run *PLUS scards=hello.plus spunch=-load
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s show this in action. Assume we have a program in the file &lt;code&gt;hello.plus&lt;/code&gt;, let&amp;rsquo;s look how we&amp;rsquo;d list, compile and run it.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.plus

      1     %Title := &amp;#34;Hello World Program&amp;#34;;
      2
      3     %Include(Sprint_String, Main);
      4
      5     definition Main;
      6       variable Count is Integer;
      7       do Count := 1 to 5
      8         Sprint_String(&amp;#34;Hello, world!&amp;#34;);
      9       end do;
     10     end Main;


# $run *plus scards=hello.plus spunch=-load
# Execution begins   07:26:15


 Hello World Program                                                                User REL.   Fri May  3/19 07:26:16     Page    1


      1.       1 |%Include(Sprint_String, Main);                                                               hello.plus

 No messages for global String_Types.
 Global size 0 bytes.

 No messages for global Numeric_Types.
 Global size 0 bytes.

                   ****************************************************************************************
                   *                                                                                      *
                   *  procedure Main                                                                      *
                   *     parameter is Par (ref).                                                          *
                   *     result is Rc.                                                                    *
                   *     external name is &amp;#34;MAIN    &amp;#34;.                                                     *
                   *                                                                                      *
                   ****************************************************************************************

  24810.       1 |definition Main;
      6.       2 |   variable Count is Integer;
      7.       3 |   do Count := 1 to 5
      8.       1 |      Sprint_String(&amp;#34;Hello, world!&amp;#34;);
      9.      25 |   end do;
     10.      26 |end Main;

 No messages for procedure Main.
 215 bytes code generated; 424 bytes stack required.

 String_Types   Numeric_Types   Main   Mts_Io_Types   Main                                                                 Page    1


 Plus/370 Compilation -- University of Michigan -- Version 28/16                    User REL.   Fri May  3/19 07:26:16     Page    2

                                                   C r o s s    R e f e r e n c e

       +------------------------------------------------+             Sprint_String         macro       1:8       1:4
       | References are of form   page:coordinate,...   |             Sprint_Varying        macro       1:7       1:7
       |                                                |             Standard_String_Length
       | Codes used are:                                |                                   constant    1:2       1:3,4,5
       |    D - procedure definition                    |             Str                   macro parm  1:8       1:23,24
       |    S - store reference                         |             Str                   macro parm  1:9       1:6
       |    @ - pointer dereferenced                    |             String_Length_Type    type        1:3       1:8
       |    A - address taken                           |             String_Types          global      1:1
       |    E - equated or opened                       |             Transmitted_Length    field       1:8
       |                                                |             Varying_String        type        1:5       1:5
       | *  indicates procedures and globals with errors|             Varying_String_Length field       1:8
       +------------------------------------------------+             Varying_String_Structure_Type
                                                                                            type        1:6
 +-------------------------------------------------------------+      Varying_String_Text   field       1:9
 |   Symbol                Usage      Decl      References     |
 +-------------------------------------------------------------+

   Actual_Length         field       1:8
   Buff                  parameter   1:15
   Count                 variable    1:2       1:3S
   Fdub                  field       1:17
   Fixed_String          type        1:4       1:9
   Integer               type        1:6       1:15,2,19
   Len                   parameter   1:16
   Len                   variable    1:22      1:23S,24A
   Line                  variable    1:5       1:6S,23,24A
   Lio_Number            field       1:19
   Liounit               field       1:15
   Lnum                  parameter   1:18
   Main                  procedure   1:17      1:1D
   Main_Procedure_Type   type        1:12      1:17
   Maximum_Integer       constant    1:4       1:5,6
   Maximum_Length        field       1:8
   Maximum_Short_Integer constant    1:2       1:3,7
   Minimum_Integer       constant    1:5       1:6
   Minimum_Short_Integer constant    1:3       1:7
   Mods                  parameter   1:17
   Mts_Fdub_Kind         constant    1:10      1:16
   Mts_Fdub_Type         type        1:2       1:17
   Mts_Io_Extended_Modifiers_Type
                         type        1:5
   Mts_Io_Length_Type    type        1:6
   Mts_Io_Modifiers_Type type        1:4
   Mts_Io_Types          global      1:1
   Mts_Io_Unit_Type      type        1:11
   Mts_Line_Number_Type  type        1:3       1:18
   Mts_Lio_Number_Kind   constant    1:10      1:18
   Mts_Liounit_Kind      constant    1:10      1:14
   Mts_Unit_Kind_Type    type        1:10      1:13
   Notification          result      1:19
   Numeric_Types         global      1:1
   Par                   parameter   1:14
   Rc                    result      1:15
   Short_Integer         type        1:7       1:8,16,22
   Sprint                procedure   1:21      1:24
   Sprint_Procedure_Type type        1:13      1:21


                                                                                                                           Page    2


 Plus/370 Compilation -- University of Michigan -- Version 28/16                    User REL.   Fri May  3/19 07:26:16     Page    3


 The following libraries were specified for this compilation:

   *PLUS.SOURCELIB       9 members included.


 Total code generated:             215 (bytes)
 Total warning messages:             0
 Total error messages:               0
# Execution terminated   07:26:15  T=0.075

# $run -load
# Execution begins   07:26:23
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
# Execution terminated   07:26:23  T=0.001
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the next article, we&amp;rsquo;ll look at the features of PLUS in more detail.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;A paper describing Project SUE can be found at the &lt;a href=&#34;https://www.computer.org/csdl/proceedings/afips/1972/5080/00/50800331.pdf&#34;&gt;ACM&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;The main language tutorial and reference is at &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfBritishColumbia/Plus-Oct1987.pdf&#34;&gt;Bitsavers&lt;/a&gt; along with the &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfBritishColumbia/PlusLib-Jan1983.pdf&#34;&gt;source library reference&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Site Update, December 2019</title>
      <link>https://try-mts.com/site-update-2019-12/</link>
      <pubDate>Mon, 30 Dec 2019 18:42:21 +0000</pubDate>
      <guid>https://try-mts.com/site-update-2019-12/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve switched the blog engine for this site from &lt;a href=&#34;https://ghost.org/&#34;&gt;Ghost&lt;/a&gt; to &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt;. All existing links should work, but please leave a comment or &lt;a href=&#34;mailto:rupert@try-mts.com&#34;&gt;email me&lt;/a&gt; if anything is broken.&lt;/p&gt;
&lt;p&gt;Expect some new posts soon, starting with a look at the PLUS language.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>GPSS - Introduction and Barber Shop Simulation</title>
      <link>https://try-mts.com/gpss-introduction-and-barber-shop-simulation/</link>
      <pubDate>Sun, 07 Jan 2018 01:28:53 +0000</pubDate>
      <guid>https://try-mts.com/gpss-introduction-and-barber-shop-simulation/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2017/11/gpss.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;super&gt;&lt;sub&gt;Barber shop simulation GPSS block diagram from &lt;a href=&#34;https://deepblue.lib.umich.edu/handle/2027.42/7463&#34;&gt;General Purpose Simulation System/360: Introductory Concepts and Case Studies&lt;/a&gt; by Thomas J. Schriber, October, 1968.&lt;/super&gt;&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll now look at GPSS, a simulation language included in MTS. Due to the specialized nature of this language I&amp;rsquo;ll only devote a single post to it - but it&amp;rsquo;s a very interesting language, so let&amp;rsquo;s dive in!&lt;/p&gt;
&lt;h2 id=&#34;general-purpose-simulation-system&#34;&gt;General Purpose Simulation System&lt;/h2&gt;
&lt;p&gt;GPSS, General Purpose Simulation System, is a language to simulate discrete systems to see how they perform over time. Sample uses include modeling a checkout at a supermarket or how a manufacturing assembly line will work. The programmer defines how transactions are created (eg a shopper), what resources are available (eg how many checkouts and how long they take to process) and how transactions move through the system. When run, it simulates a clock and processes these transactions, giving output that shows the performance of the system, eg how long on average a shopper had to queue for and how busy the checkouts where during a simulated day.&lt;/p&gt;
&lt;h2 id=&#34;gpss-on-mts&#34;&gt;GPSS on MTS&lt;/h2&gt;
&lt;p&gt;GPSS was created by Geoffrey Gordon in around 1961 and was originally implemented on IBM systems. The version we have on MTS, GPSS/360, is available as &lt;code&gt;*GPSS&lt;/code&gt;. Later versions of GPSS such as GPSS/H did run on MTS and may be available in future distributions (see &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/discussions/permission-to-include-licensed-programs/forgpsshandgpssh2fromjimhenriksen&#34;&gt;this message&lt;/a&gt; at the MTS Archive).&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get this language running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;a-barber-shop-simulator&#34;&gt;A barber shop simulator&lt;/h2&gt;
&lt;p&gt;As a sample program, let&amp;rsquo;s look at the Barber Shop: First Model from &lt;a href=&#34;https://deepblue.lib.umich.edu/handle/2027.42/7463&#34;&gt;General Purpose Simulation System/360: Introductory Concepts and Case Studies&lt;/a&gt; by Thomas J. Schriber.&lt;/p&gt;
&lt;p&gt;We are modeling a barber shop with the following qualities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The shop contains one barber and one barber&amp;rsquo;s chair, open for eight hours in a day.&lt;/li&gt;
&lt;li&gt;Customers arrive on average every 18 minutes, with the arrival time varying between 12 and 24 minutes.&lt;/li&gt;
&lt;li&gt;If the barber is busy, the customer will wait in a queue.&lt;/li&gt;
&lt;li&gt;Once the barber is free, the next customer will have a haircut.&lt;/li&gt;
&lt;li&gt;Each haircut takes between 12 and 18 minutes, with the average being 15 minutes.&lt;/li&gt;
&lt;li&gt;Once the haircut is done, the customer will leave the shop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We want to answer these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How utilised is the barber through the day?&lt;/li&gt;
&lt;li&gt;How long does the queue get?&lt;/li&gt;
&lt;li&gt;On average, how long does a customer have to wait.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The manual uses a block diagram notation to show how this is simulated - you can see this at the top of this page. The equivalent GPSS program looks like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;       SIMULATE
       GENERATE   18,6       CREATE CUSTOMERS
       QUEUE      2          CUSTOMERS QUEUE UP IF NECESSARY
       SEIZE      3          ENGAGE THE BARBER WHEN HE BECOMES AVAILABLE
       DEPART     2          CUSTOMER LEAVES THE QUEUE
       ADVANCE    15,3       CUSTOMER GETS HIS HAIR CUT
       RELEASE    3          RELEASE THE BARBER
       TERMINATE  0          LEAVE THE BARBER SHOP
       GENERATE   480        GENERATE A TIMER AFTER 8 HOURS OF SIMULATED TIME
       *
       TERMINATE  1          SHUT OFF THE RUN
       START      1          CARRY OUT THE SIMULATION
       END                   RETURN CONTROL TO THE OPERATING SYSTEM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The text at the end of each line is a comment; starting a line with &lt;code&gt;*&lt;/code&gt; means the rest of the line is a comment; we use this just to separate the two parts of the program as explained below.&lt;/p&gt;
&lt;p&gt;GPSS works in units rather than absolute time, so we shall say that 1 time unit is equal to 1 minute.  Programs can be read from top to bottom: the simulator runs by advancing the clock one time unit and working out where transactions are in the system.&lt;/p&gt;
&lt;p&gt;The first line, &lt;code&gt;SIMULATE&lt;/code&gt;, denotes the start of the code.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GENERATE 18,6&lt;/code&gt; means generate a transaction - a barber shop customer - every 18 minutes ± 6 minutes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QUEUE 2&lt;/code&gt; defines a queue with ID 2, denoting the queue where customers will wait.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SEIZE 3&lt;/code&gt; defines a facility with ID 3. The facility is the barber and this line means if the barber is free, the next customer occupies the barber until released.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DEPART 2&lt;/code&gt; says that the customer leaves the queue when occupying the barber.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ADVANCE 15,3&lt;/code&gt; means that transactions in this state only move on after 15 minutes ± 3 minutes - modeling the time taken for a haircut. After that &lt;code&gt;RELEASE 3&lt;/code&gt; shows that the customer no longer occupies the barber and &lt;code&gt;TERMINATE 0&lt;/code&gt; ends the transaction, showing that the customer has left the shop.&lt;/p&gt;
&lt;p&gt;That is all that is needed for the basic simulation, but if run like this it would never stop, as we have not modeled the 8 hour period the shop is open.&lt;/p&gt;
&lt;p&gt;To do this we generate a new transaction with &lt;code&gt;GENERATE 480&lt;/code&gt;, which means generate a transaction after 480 minutes, ie 8 hours. The next line, &lt;code&gt;TERMINATE 1&lt;/code&gt;, stops the simulation after this transaction is generated.&lt;/p&gt;
&lt;p&gt;With that done, the last two lines start the simulator and returns control to MTS when it is done.&lt;/p&gt;
&lt;h2 id=&#34;running-the-program-using-gpss&#34;&gt;Running the program using &lt;code&gt;*GPSS&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*GPSS&lt;/code&gt; takes the input file on &lt;code&gt;scards&lt;/code&gt; and outputs to &lt;code&gt;*sink*&lt;/code&gt; by default. Assuming the simulation defined above is stored in &lt;code&gt;barb1.gpss&lt;/code&gt;, this is how it would look:&lt;/p&gt;
&lt;p&gt;We start off with the program listing, where GPSS denotes which lines are components of the simulation with a block ID. So &lt;code&gt;GENERATE 18,6&lt;/code&gt; is on line 3 but is block 1.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$run *gpss scards=barb1.gpss
Execution begins   16:07:50


     GPSS/360    VERSION 1  MODIFICATION LEVEL 4    MTS MODEL (AN104)    16:07:50  11-03-17

     LINE# CARD#  BLOCK#  *LOC   OPERATION  A,B,C,D,E,F,G    COMMENTS

     1.000     1                 SIMULATE
     2.000     2          * BARBER SHOP SIMULATOR
     3.000     3  1              GENERATE   18,6       CREATE CUSTOMERS
     4.000     4  2              QUEUE      2          CUSTOMERS QUEUE UP IF NECESSARY
     5.000     5  3              SEIZE      3          ENGAGE THE BARBER WHEN HE BECOMES AVAILABLE
     6.000     6  4              DEPART     2          CUSTOMER LEAVES THE QUEUE
     7.000     7  5              ADVANCE    15,3       CUSTOMER GETS HIS HAIR CUT
     8.000     8  6              RELEASE    3          RELEASE THE BARBER
     9.000     9  7              TERMINATE  0          LEAVE THE BARBER SHOP
    10.000    10          *
    11.000    11  8              GENERATE   480        GENERATE A TIMER AFTER 8 HOURS OF SIMULATED
    12.000    12  9              TERMINATE  1          SHUT OFF THE RUN
    13.000    13                 START      1          CARRY OUT THE SIMULATION
    14.000    14                 END                   RETURN CONTROL TO THE OPERATING SYSTEM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, GPSS shows how many transactions were in each block at the end of the simulation and in total.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;RELATIVE CLOCK          480  ABSOLUTE CLOCK          480
 BLOCK COUNTS
 BLOCK CURRENT    TOTAL    BLOCK CURRENT    TOTAL    BLOCK CURRENT    TOTAL    BLOCK CURRENT    TOTAL    BLOCK CURRENT    TOTAL
     1      0        27
     2      0        27
     3      0        27
     4      0        27
     5      1        27
     6      0        26
     7      0        26
     8      0         1
     9      0         1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see that 27 transactions were generated in block 1, which means 27 customers entered the shop during the day. Blocks 8 and 9 had 1 transaction, which is expected as this was the timing transaction. Why does block 5 have one current transaction while all others are zero? Block 5 is the &lt;code&gt;ADVANCE&lt;/code&gt; statement, representing the haircut, and this means one customer was in the chair at the end of the simulated day.&lt;/p&gt;
&lt;p&gt;GPSS then prints out statistics on the facility, or the barber in our simulation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; FACILITY        AVERAGE         NUMBER        AVERAGE          SEIZING    PREEMPTING
               UTILIZATION      ENTRIES       TIME/TRAN        TRANS. NO.  TRANS. NO.
        3          .816              27          14.518            1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So our barber was used 81.6% of the day, and each haircut took on average 14.518 minutes.&lt;/p&gt;
&lt;p&gt;We then see details of the queue:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; QUEUE      MAXIMUM     AVERAGE     TOTAL       ZERO     PERCENT      AVERAGE      $AVERAGE      TABLE     CURRENT
           CONTENTS    CONTENTS    ENTRIES    ENTRIES     ZEROS     TIME/TRANS    TIME/TRANS    NUMBER    CONTENTS
     2            1        .085        27         16       59.2         1.518         3.727
  $AVERAGE TIME/TRANS = AVERAGE TIME/TRANS EXCLUDING ZERO ENTRIES
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;MAXIMUM CONTENTS&lt;/code&gt; means there was never more than one customer in the queue. Although 27 customers entered the queue, 16 were zero entries - this means they entered and immediately left the queue, ie the barber was unoccupied when they entered the shop. For those that did wait in the queue, the average wait was 3.727 minutes; including the zero entries, the average time for all customers to wait was 1.518 minutes.&lt;/p&gt;
&lt;p&gt;Finally, GPSS prints usage statistics and exits:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CPU TIME USED (SECONDS)

  ASSEMBLY:      .001

TOTAL BLOCK EXECUTIONS:     189

MSEC/BLOCK AVG CPU TIME:   .003

Execution terminated   16:07:50  T=0.004  RC=0  $0.00
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&amp;rsquo;s interesting to compare this to the printout in the manual from 1968. Our run took 0.004 seconds of CPU time, compared to 4.5 seconds then. And of course our run had no charge, whereas the run from 1968 would have cost 41¢.&lt;/p&gt;
&lt;p&gt;The manual goes on to refine the model further - dealing with the customer left in the chair, and simulating more complex scenarios - but we will leave it at that for now.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for the program described above can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The original IBM manuals for GPSS/360 don&amp;rsquo;t appear to be available anywhere online. The best reference I&amp;rsquo;ve found is, as mentioned above, &lt;a href=&#34;https://deepblue.lib.umich.edu/handle/2027.42/7463&#34;&gt;General Purpose Simulation System/360: Introductory Concepts and Case Studies&lt;/a&gt; at Deepblue.&lt;/p&gt;
&lt;p&gt;Modern implementations of GPSS on Windows are sold by &lt;a href=&#34;http://www.wolverinesoftware.com/&#34;&gt;Wolverine Software&lt;/a&gt; and &lt;a href=&#34;http://www.minutemansoftware.com/simulation.htm&#34;&gt;Minuteman Software&lt;/a&gt;; both offer a free student version with limited model sizes.&lt;/p&gt;
&lt;p&gt;Several other simulation languages have been created, eg &lt;a href=&#34;https://en.wikipedia.org/wiki/Simula&#34;&gt;Simula&lt;/a&gt;; a list of languages for discrete event simulations can be found &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_discrete_event_simulation_software&#34;&gt;at Wikipedia&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>System/360 Assembly Language - Caesar Cipher</title>
      <link>https://try-mts.com/system-360-assembly-language-caesar-cipher/</link>
      <pubDate>Sun, 17 Dec 2017 01:59:09 +0000</pubDate>
      <guid>https://try-mts.com/system-360-assembly-language-caesar-cipher/</guid>
      <description>&lt;p&gt;In the last part of this &lt;a href=&#34;https://try-mts.com/system-360-assembly-language-introduction/&#34;&gt;series&lt;/a&gt; we&amp;rsquo;ll write a real program in assembly language.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll implement the Caesar cipher from &lt;a href=&#34;http://rosettacode.org/wiki/Caesar_cipher&#34;&gt;Rosetta Code&lt;/a&gt;. This is a very simple cipher where the key indicates how much to rotate the alphabet by. So with a key of 2, &lt;code&gt;ABC XYZ&lt;/code&gt; would be encoded as &lt;code&gt;CDE ZAB&lt;/code&gt;. The solution should handle both encoding and decoding; decoding simply needs a rotation by the key in the opposite direction.&lt;/p&gt;
&lt;h2 id=&#34;finding-a-algorithm&#34;&gt;Finding a algorithm&lt;/h2&gt;
&lt;p&gt;This appears quite simple at first. Take each letter&amp;rsquo;s code, add the key and return the new letter, accounting for wrap around at Z.&lt;/p&gt;
&lt;p&gt;However, this assumes that letters are coded in a contiguous manner, as they are in ASCII. The System/360 uses EBCDIC, and if you look at the code chart on &lt;a href=&#34;https://en.wikipedia.org/wiki/EBCDIC&#34;&gt;Wikipedia&lt;/a&gt; you&amp;rsquo;ll see that A, B, C, &amp;hellip; I are contiguous, but there&amp;rsquo;s then a gap of 7 characters before J, K, &amp;hellip; R, then another gap and so on.&lt;/p&gt;
&lt;p&gt;Now you could account for this but it would be very fiddly in assembler. Instead, let&amp;rsquo;s try another way.&lt;/p&gt;
&lt;p&gt;What if we transformed A, B, C into offsets 0, 1, 2. Next, add the key and use that as an index into a table of letters repeated to allow wrap around. This should work, but how to implement it?&lt;/p&gt;
&lt;h2 id=&#34;turning-letters-into-offsets&#34;&gt;Turning letters into offsets&lt;/h2&gt;
&lt;p&gt;The first step is turning A, B, C into 0, 1, 2. There&amp;rsquo;s a useful opcode called &lt;code&gt;TR&lt;/code&gt; that can help us here.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TR WORK,OFFSETS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will take each byte in WORK, look up its position in OFFSETS and update WORK to be the value found there. So if WORK[0] is &amp;lsquo;A&amp;rsquo; (193 in EBCDIC), find the value at OFFSETS[193], which we could arrange to be 0, and then update WORK[0] to be that value.&lt;/p&gt;
&lt;p&gt;For this to work, we need to set up OFFSETS correctly. This can be done with some trickery with &lt;code&gt;DC&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OFFSETS  DC    256AL1(*-OFFSETS)
         ORG   OFFSETS+C&amp;#39;A&amp;#39;
         DC    X&amp;#39;000102030405060708&amp;#39;
         ORG   OFFSETS+C&amp;#39;J&amp;#39;
         DC    X&amp;#39;090A0B0C0D0E0F1011&amp;#39;
         ORG   OFFSETS+C&amp;#39;S&amp;#39;
         DC    X&amp;#39;1213141516171819&amp;#39;
         ORG
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line defines&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an address &lt;code&gt;A&lt;/code&gt;, 32 bits&lt;/li&gt;
&lt;li&gt;converted to length of 1 byte with &lt;code&gt;L1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;repeated 256 times&lt;/li&gt;
&lt;li&gt;with value at each time &lt;code&gt;*-OFFSETS&lt;/code&gt;, where &lt;code&gt;*&lt;/code&gt; is the current location&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The effect of this is to create a block of memory 256 bytes long with values 0, 1, 2, &amp;hellip;, 255.&lt;/p&gt;
&lt;p&gt;The next line uses &lt;code&gt;ORG&lt;/code&gt; to set the location counter to &lt;code&gt;OFFSETS+C&#39;A&#39;&lt;/code&gt;, ie the position where &lt;code&gt;A&lt;/code&gt; would be in the EBCDIC table. We then overwrite a part of the 256 byte block with the bytes 00, 01, 02, &amp;hellip;, 09, creating the offset to replace letters A - I with. We do this again for the letter run starting with J and S.&lt;/p&gt;
&lt;h2 id=&#34;turning-offsets-into-letters&#34;&gt;Turning offsets into letters&lt;/h2&gt;
&lt;p&gt;To go the other way, ie change an offset like 0, 1 into letters A, B, we define a block of letters:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;FLAT0    DC    C&amp;#39;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#39;
FLAT     DC    C&amp;#39;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#39;
FLAT1    DC    C&amp;#39;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We do this contiguously three times to allow for forward and reverse rotations with the key, so FLAT+1 gives us B and FLAT-1 gives us Z. Doing it this way allows easy access; we could just use one block of letters and deal with rotated offsets past the ends with extra logic, but I don&amp;rsquo;t mind giving up memory for ease of access here.&lt;/p&gt;
&lt;h2 id=&#34;byte-access-to-memory&#34;&gt;Byte access to memory&lt;/h2&gt;
&lt;p&gt;The final piece we need is a way to access bytes in memory. For this we can read a byte (insert a character) with &lt;code&gt;IC&lt;/code&gt;, eg:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         IC    4,WORK(1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will set register 4 to the value in memory at the byte addressed by &lt;code&gt;WORK&lt;/code&gt; + 1.&lt;/p&gt;
&lt;p&gt;To store a character back into memory, &lt;code&gt;STC&lt;/code&gt; can be used in a similar way.&lt;/p&gt;
&lt;h2 id=&#34;the-rot-subroutine&#34;&gt;The ROT subroutine&lt;/h2&gt;
&lt;p&gt;With this we have enough to define the subroutine to rotate a message by a given key:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* ROT subroutines implements Caesar cipher message letter rotation
*
* Parameters:
* R0 - Key for cipher. 1 to 25 to encode, -1 to -25 to decode
* WORK - Buffer containing message to encode/decode of length MSGLEN
*        Will be overwritten with encoded/decoded message
* Return values:
* None
*
         ENTRY ROT                Subroutine entry for ROT
ROT      ENTER 12,SA=RSAVE        Use R12 for base address
         TR    WORK,OFFSETS       Translate letters into offsets
         L     1,=F&amp;#39;0&amp;#39;            Loop start value
         L     2,=F&amp;#39;1&amp;#39;            Loop increment
         LA    3,L&amp;#39;MESSAGE-1      Loop final value
LOOP     IC    4,WORK(1)          Get message byte at loop index
         C     4,MAXOFF           Is it an offset?
         BH    STORE              If no, don&amp;#39;t change it
         LA    10,FLAT            Load base of flat letter array
         AR    10,0               Add the key displacement
         IC    4,0(4,10)          Fetch into R4 FLAT+KEY+OFFSET
STORE    STC   4,WORK(1)          Store char back into work
         BXLE  1,2,LOOP           Loop if index &amp;lt;= final
         EXIT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We use a predefined area of memory &lt;code&gt;WORK&lt;/code&gt; to do the transformation rather than passing in an address, again just for convenience.&lt;/p&gt;
&lt;p&gt;Note the explicit use of addressing in &lt;code&gt;IC 4,0(4,10)&lt;/code&gt;. The second parameter means use register 10 as the base address and add the contents of register 4 to the displacement 0.&lt;/p&gt;
&lt;p&gt;One other observation is that the &lt;code&gt;BXLE&lt;/code&gt; loop is only needed as we want to skip over non-alphabetic characters in the message. If we knew the message can only contain A-Z, we could do something like &lt;code&gt;TR WORK,FLAT+KEY&lt;/code&gt; to do the whole message in one go.&lt;/p&gt;
&lt;h2 id=&#34;putting-it-together&#34;&gt;Putting it together&lt;/h2&gt;
&lt;p&gt;We need a couple more things: first the definition of the message and key, and working areas:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*
* Inputs to program
*
MESSAGE  DC    C&amp;#39;THE FIVE BOXING WIZARDS JUMP QUICKLY!&amp;#39;
KEY      DC    F&amp;#39;7&amp;#39;
*
* Working buffers
*
ENC      DS    CL(L&amp;#39;MESSAGE)
DEC      DS    CL(L&amp;#39;MESSAGE)
WORK     DS    CL(L&amp;#39;MESSAGE)
RSAVE    DS    18F                Save area
*
* Constants
*
MAXOFF   DC    F&amp;#39;25&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And finally, a test call to encode and decode the message, using &lt;code&gt;MVC&lt;/code&gt; to copy a block of memory to another location:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CAESAR   START 0                  Program at relative address 0
         USING CAESAR,12          R12 will contain program addr
         LR    12,15              Load R12 with absolute addr
*
* Encode the plain-text message to ENC
         MVC   WORK,MESSAGE       Copy message to WORK
         L     0,KEY              Set key
         CALL  ROT                Encode the message
         MVC   ENC,WORK           Copy encoded message to ENC
*
* Decode the encoded message to DEC
         L     0,KEY              Set the key
         LNR   0,0                Negate the key for decoding
         CALL  ROT                Decode the message
         MVC   DEC,WORK           Copy decoded message to DEC
*
* Print results and exit
         SPRINT MESSAGE           Print the original message
         SPRINT ENC               Print the decoded message
         SPRINT DEC               Print the encoded message
         SYSTEM                   Exit program
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;assembling-and-running-the-program&#34;&gt;Assembling and running the program&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ run *asmg scards=caesar.asm spunch=-load sercom=*sink* par=test

...

$run -load
Execution begins   18:29:02
THE FIVE BOXING WIZARDS JUMP QUICKLY!
AOL MPCL IVEPUN DPGHYKZ QBTW XBPJRSF!
THE FIVE BOXING WIZARDS JUMP QUICKLY!
Execution terminated   18:29:02  T=0  RC=0  $0.00
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;thoughts-on-the-program-and-assembly&#34;&gt;Thoughts on the program and assembly&lt;/h2&gt;
&lt;p&gt;I think that was the hardest program to write of all the languages so far. The documentation is very good, but terse - I can imagine reading the &lt;em&gt;Principle of Operation&lt;/em&gt; several times would pay off greatly. Also having to flip between &lt;em&gt;Principles&lt;/em&gt;, the Assembler F reference and MTS documentation rather than having everything in one place was tough. But it was very rewarding to get the program running.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not entirely happy with the final design I came up with - I&amp;rsquo;m sure there&amp;rsquo;s an easier or more efficient way. I&amp;rsquo;d love to hear from experience System/360 assembly language programmers out there how you&amp;rsquo;d implement this.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>System/360 Assembly Language - Features</title>
      <link>https://try-mts.com/system-360-assembly-language-features/</link>
      <pubDate>Sun, 10 Dec 2017 01:17:33 +0000</pubDate>
      <guid>https://try-mts.com/system-360-assembly-language-features/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2017/10/cpu.png&#34; alt=&#34;&#34;&gt;
&lt;super&gt;&lt;sub&gt;Functions of the central processing unit, from &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/asm/SC20-1646-6_int360asm_Aug70.pdf&#34;&gt;A Programmer&amp;rsquo;s Introduction to IBM System/360 Assembler Language&lt;/a&gt;&lt;/super&gt;&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://try-mts.com/system-360-assembly-language-introduction/&#34;&gt;previous post&lt;/a&gt; we got a simple assembly language program running.  Let&amp;rsquo;s now look in more detail at how to program in System/360 assembly language. Although a lot of this material is common across other System/360 operating systems and assemblers, it does contain specifics for MTS and Assembler G.&lt;/p&gt;
&lt;p&gt;This only covers the surface of the subject: see the end of this post for some of what was missed and how to find more information.&lt;/p&gt;
&lt;h2 id=&#34;architecture-overview&#34;&gt;Architecture overview&lt;/h2&gt;
&lt;p&gt;The IBM System/360 and its derivatives has the following basic architecture.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;32 bit words, with access to 16 bit halfwords and 8 bit bytes.&lt;/li&gt;
&lt;li&gt;Big-endian, using 2&amp;rsquo;s compliment for signed values.&lt;/li&gt;
&lt;li&gt;16 general purpose 32 bit registers, 4 64 bit floating point registers, plus support for BCD arithmetic.&lt;/li&gt;
&lt;li&gt;24 bit memory addressing.&lt;/li&gt;
&lt;li&gt;A 64 bit program status word (PSW), which contains the address of the current instruction being executed, condition codes and interrupt masks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most System/360 models did not support virtual memory, but the one used for MTS did.&lt;/p&gt;
&lt;h2 id=&#34;assembly-language&#34;&gt;Assembly language&lt;/h2&gt;
&lt;p&gt;The assembly language defined by IBM is line based, with the following components separated by whitespace:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An optional label, which must start in column 1 if present.&lt;/li&gt;
&lt;li&gt;An opcode, assembler directive or macro indicating the operation to perform.&lt;/li&gt;
&lt;li&gt;An optional set of operands separated by commas.&lt;/li&gt;
&lt;li&gt;An optional comment.&lt;/li&gt;
&lt;li&gt;An optional identification sequence, in columns 73-80, commonly used to number each statement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from the first and last element, which columns are used is not important, but by convention they are lined up.&lt;/p&gt;
&lt;h2 id=&#34;an-example&#34;&gt;An example&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at lines from the &amp;lsquo;Hello, world&amp;rsquo; example from the &lt;a href=&#34;https://try-mts.com/system-360-assembly-language-introduction/&#34;&gt;last post&lt;/a&gt; to illustrate these features.&lt;/p&gt;
&lt;p&gt;This line loads registers 12 with the contents of register 15.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         LR      12,15             LOAD R12 WITH ABSOLUTE ADDR
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The next line loads register 3 with the value found at the address with label &lt;code&gt;RUNS&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         L       3,RUNS            R3 COUNTS DOWN NUMBER OF RUNS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looking at the line labeled &lt;code&gt;RUNS&lt;/code&gt;, we can see the value pointed to defined as a constant using the assembler directive &lt;code&gt;DC&lt;/code&gt;. Constants of different types are represented with quotes, so &lt;code&gt;F&#39;5&#39;&lt;/code&gt; means a fullword (32 bit quantity) with a value of 5. &lt;code&gt;X&#39;BEEF0&#39;&lt;/code&gt; would be a 20 bit value composed of the 5 hex digits &lt;code&gt;BEEF0&lt;/code&gt; each taking up 4 bits.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;RUNS     DC      F&amp;#39;5&amp;#39;              NUMBER OF RUNS TO MAKE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;DC&lt;/code&gt; stands for data constant, and arranges a value to be placed at a location in memory after the previous line. &lt;code&gt;DS&lt;/code&gt; can be used to arrange data storage, allowing the program to write values back to memory.&lt;/p&gt;
&lt;p&gt;Finally, here is a macro call, which will take the string as parameter and arrange a subroutine call to the &lt;code&gt;SPRINT&lt;/code&gt; service provided by MTS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LOOP     SPRINT &amp;#39;Hello, world!&amp;#39;    PRINT THE MESSAGE
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;operation-formats-base-registers-and-addressing&#34;&gt;Operation formats, base registers and addressing&lt;/h2&gt;
&lt;p&gt;Next let&amp;rsquo;s see how the assembler constructs machine instructions from parts of this program. The listing sent to &lt;code&gt;sprint&lt;/code&gt; when running the assembler contains a dump of memory locations and assembled code for each line. For example, here&amp;rsquo;s the line where the &lt;code&gt;RUNS&lt;/code&gt; constant is stored (I&amp;rsquo;ve omitted some columns from the listing for clarity). Here we can see the fullword quantity 5 being stored at address 3c.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LOC    OBJECT CODE  SOURCE STATEMENT
00003C 00000005     RUNS     DC      F&amp;#39;5&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The load statement &lt;code&gt;L 3,RUNS&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LOC    OBJECT CODE   SOURCE STATEMENT
000002 5830 C03C     L       3,RUNS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Breaking down the object code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Opcodes on System/360 are a single byte: here &lt;code&gt;58&lt;/code&gt; is the opcode for &lt;code&gt;L&lt;/code&gt;, load.&lt;/li&gt;
&lt;li&gt;The next four bits, &lt;code&gt;3&lt;/code&gt;, represents the register to load into.&lt;/li&gt;
&lt;li&gt;The next four bits, &lt;code&gt;0&lt;/code&gt; is the index register&lt;/li&gt;
&lt;li&gt;The next four bits, &lt;code&gt;C&lt;/code&gt;, is the base register.&lt;/li&gt;
&lt;li&gt;The final 12 bits, &lt;code&gt;03C&lt;/code&gt;, is the displacement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The address to load from is base register + index register + displacement. In this example, the index register is 0 which indicates it is not being used, so at run time the system will find the value at offset &lt;code&gt;3c&lt;/code&gt; from whatever is in the base register.&lt;/p&gt;
&lt;p&gt;The base register is present in nearly all addressing operations and ensures that System/360 is &lt;em&gt;relocatable&lt;/em&gt;: at run time, the start of this piece of code is loaded into memory somewhere and a register is set to that address so all further address arithmetic can use it.&lt;/p&gt;
&lt;p&gt;In our example, this was set up with assembler directives at the start of the program:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;HELLO    START   0
         USING   HELLO,12
         LR      12,15
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, &lt;code&gt;START 0&lt;/code&gt; means the program will be placed at offset 0 in the block of memory being composed. &lt;code&gt;USING HELLO,12&lt;/code&gt; states that register 12 (&lt;code&gt;C&lt;/code&gt;) will be the base register. Finally, &lt;code&gt;LR 12,15&lt;/code&gt; loads register 12 with the contents of register 15: this is part of the calling convention for MTS, so when the program starts it knows it can find its own start address in register 15. Using &lt;code&gt;USING&lt;/code&gt; allows the assembler to take care of the base register for you, but you can also specify it and the index register directly.&lt;/p&gt;
&lt;p&gt;This sequence of opcode and operands is called RX format. Other opcodes use other formats; for example, the &lt;code&gt;LR 12,15&lt;/code&gt; is in RR format, where the one byte opcode is followed by two 4 bit register operands. Other formats can take an immediate value and an address, or two addresses.&lt;/p&gt;
&lt;h2 id=&#34;loads-and-stores-and-binary-arithmetic&#34;&gt;Loads and stores and binary arithmetic&lt;/h2&gt;
&lt;p&gt;Armed with this knowledge, it&amp;rsquo;s fairly easy to understand the basic instruction set described in the &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/princOps/A22-6821-7_360PrincOpsDec67.pdf&#34;&gt;IBM System/360 Principles of Operation&lt;/a&gt; manual. Let&amp;rsquo;s look at the load instructions for example:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Mnemonic&lt;/th&gt;
          &lt;th&gt;Type&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Load&lt;/td&gt;
          &lt;td&gt;LR&lt;/td&gt;
          &lt;td&gt;RR&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load&lt;/td&gt;
          &lt;td&gt;L&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load Halfword&lt;/td&gt;
          &lt;td&gt;LH&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load and Test&lt;/td&gt;
          &lt;td&gt;LTR&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load Complement&lt;/td&gt;
          &lt;td&gt;LCR&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load Positive&lt;/td&gt;
          &lt;td&gt;LPR&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load Negative&lt;/td&gt;
          &lt;td&gt;LNR&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Load Multiple&lt;/td&gt;
          &lt;td&gt;LM&lt;/td&gt;
          &lt;td&gt;RS&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We&amp;rsquo;ve seen &lt;code&gt;L&lt;/code&gt; and &lt;code&gt;LR&lt;/code&gt; already. &lt;code&gt;LH&lt;/code&gt; loads a halfword (16 bit) quantity from the given address, sign extending it to a fullword. &lt;code&gt;LTR&lt;/code&gt; will load a value and set condition codes, which will look at in the next section. &lt;code&gt;LCR&lt;/code&gt; will load a value and toggle the sign; &lt;code&gt;LPR&lt;/code&gt; and &lt;code&gt;LNR&lt;/code&gt; will load and force positive or negative.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;LM&lt;/code&gt;, load multiple, uses a format we&amp;rsquo;ve not seen so far, RS. This format is composed like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One byte opcode&lt;/li&gt;
&lt;li&gt;Four bits register R&lt;sub&gt;1&lt;/sub&gt;&lt;/li&gt;
&lt;li&gt;Four bits register R&lt;sub&gt;3&lt;/sub&gt;&lt;/li&gt;
&lt;li&gt;Four bits register B&lt;sub&gt;2&lt;/sub&gt;&lt;/li&gt;
&lt;li&gt;12 bits displacement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For LM, data is loaded from the address computed from the base register B&lt;sub&gt;2&lt;/sub&gt; + displacement into a sequence of registers starting at R&lt;sub&gt;1&lt;/sub&gt; and ending at R&lt;sub&gt;3&lt;/sub&gt;. So the instruction&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LM 3,5,12,0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will load up the registers R3, R4 and R5 with data from the memory address in base register 12 + 0 offset, reading 3 fullword values in sequence.&lt;/p&gt;
&lt;p&gt;For stores, we have &lt;code&gt;ST&lt;/code&gt; for store fullword quantity in a register to memory, &lt;code&gt;STH&lt;/code&gt; for storing a halfword and &lt;code&gt;SM&lt;/code&gt; for storing multiple registers to a sequence in memory.&lt;/p&gt;
&lt;p&gt;Addition opcodes are &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;AR&lt;/code&gt; for add memory to register and add register to register. &lt;code&gt;AH&lt;/code&gt; to add halfwords and &lt;code&gt;AL&lt;/code&gt;/&lt;code&gt;ALR&lt;/code&gt; to add ignoring the sign bit. Subtraction is orthogonal.&lt;/p&gt;
&lt;p&gt;Multiplication and division result in a 64 bit value so as not to lose precision. The input register must have an even number and the results are stored in that plus the next sequential register, so&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;M 2,LABEL
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will multiply the value of register 2 and the value found at &lt;code&gt;LABEL&lt;/code&gt;, then store the results in registers 2 and 3.&lt;/p&gt;
&lt;h2 id=&#34;the-psw-and-branches&#34;&gt;The PSW and branches&lt;/h2&gt;
&lt;p&gt;The PSW, or program status word, is a doubleword that defines the current state of the processor. It includes fields that indicate interrupt masks, state (eg problem state), condition codes and the current instruction address.&lt;/p&gt;
&lt;p&gt;The condition code occupies bits 34 and 35. They have the following meaning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;00&lt;/code&gt; - zero&lt;/li&gt;
&lt;li&gt;&lt;code&gt;01&lt;/code&gt; - negative&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10&lt;/code&gt; - positive&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11&lt;/code&gt; - carry/overflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The condition code is set after many operations, for example add, subtract or load and test.&lt;/p&gt;
&lt;p&gt;The branch on condition opcode can be used to branch based on its value. This comes in two formats, a RR instruction &lt;code&gt;BCR&lt;/code&gt; where if the branch is made, the address is taken from one register, or &lt;code&gt;BC&lt;/code&gt; which uses the base+index+displacement system.&lt;/p&gt;
&lt;p&gt;Whether to take the branch or not is determine by a four bit mask. A mask value of 8 means branch if condition code &lt;code&gt;00&lt;/code&gt; is set, mask value 4 for &lt;code&gt;01&lt;/code&gt; etc. These can be added together, so a mask value of 12 means branch on either condition code &lt;code&gt;00&lt;/code&gt; or &lt;code&gt;01&lt;/code&gt; (zero or negative). If all four mask bits are set, the branch is unconditional, if unset, then the instruction is a no-op.&lt;/p&gt;
&lt;p&gt;As specifying the mask each time can be tedious, the assembler provides directives for common cases such as branch if zero or branch if positive. We can see this in the &amp;lsquo;hello, world&amp;rsquo; program:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         S       3,DECR            DECREMENT R3
         BP      LOOP              IF R3 POSITIVE, LOOP AGAIN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;BP&lt;/code&gt; stands for branch if positive, and will test the condition code from the previous &lt;code&gt;S&lt;/code&gt; subtract: if the value is positive, then jump to address labeled as &lt;code&gt;LOOP&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Internally, the assembler turns &lt;code&gt;BP LOOP&lt;/code&gt; into &lt;code&gt;BC 2,LOOP(0,12)&lt;/code&gt;, using mask 2 = condition code &lt;code&gt;10&lt;/code&gt; and if positive branching to the address given by the base register 12 + &lt;code&gt;LOOP&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are also higher level branch instructions suitable for loops, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         BXLE 1,2,LABEL
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;BXLE stands for branch on index low or equal. In this example, register 1 is incremented by the value of register 2. If register 1 is less than equal to the value in register 3, then the branch is taken.&lt;/p&gt;
&lt;h2 id=&#34;subroutines&#34;&gt;Subroutines&lt;/h2&gt;
&lt;p&gt;Supporting subroutines or functions requires a facility to pass in parameters, jump to the called code, do an operation, and return to the calling code with any results. On most modern systems this is done via a stack, but you may have noticed this was not part of the architecture overview above, so how is this done?&lt;/p&gt;
&lt;p&gt;The answer is that a combination of a save area in memory is used to store data plus a calling convention so the caller and callee can exchange information via registers. The actual change in location address is done by a branch when this is set up. The calling convention used is up to the programmer, but in practice IBM defines a standard calling convention so that code in different languages can operate together.&lt;/p&gt;
&lt;p&gt;(&lt;em&gt;24 March 2018: See the comment below from Jeff Ogden for more details on the calling convention.&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol03-SystemSubroutineDescriptions-Sep1989.pdf&#34;&gt;MTS Volume 3: System Subroutine Descriptions&lt;/a&gt; defines this calling convention. Simplifying greatly, for the common S-type routines used by most MTS library functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Register 1 points to an address where a list of parameters for the called program is stored..&lt;/li&gt;
&lt;li&gt;Register 13 points to a save area, described below.&lt;/li&gt;
&lt;li&gt;Register 14 points to the next instruction in the calling program that the called program should return to after execution is done.&lt;/li&gt;
&lt;li&gt;Register 15 points to the entry point of the called program.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The save area is a portion of memory with a specific format, owned by the calling program where the called program can save registers and other data. For example, word 6 is used to store the contents of register 0. It is the responsibility of the called program to save data in the save area and restore it before returning execution to the caller.&lt;/p&gt;
&lt;p&gt;A byproduct of this is that recursion is not freely available like on stack based architectures, as there is a single save area per program call, multiple calls would overwrite this area. Special code is needed to dynamically allocate save areas if recursion is required.&lt;/p&gt;
&lt;h2 id=&#34;macros-and-calling-the-mts-system&#34;&gt;Macros and calling the MTS system&lt;/h2&gt;
&lt;p&gt;Setting up these subroutine calls by hand would be tedious. Luckily, the assembler includes a powerful macro facility that helps abstract these away, and macros have been defined as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CALL&lt;/code&gt; to pass parameters to a subroutine&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ENTER&lt;/code&gt; to start a subroutine and set up registers and save area&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXIT&lt;/code&gt; to restore saved values and pass back a result code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Macros are also defined for MTS system facilities. We saw this in the &amp;lsquo;hello world&amp;rsquo; program in two places:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LOOP     SPRINT &amp;#39;Hello, world!&amp;#39;    PRINT THE MESSAGE
...
         SYSTEM                    EXIT PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see how these were assembled by looking at the listing file. The &lt;code&gt;SYSTEM&lt;/code&gt; call is easiest as it takes and returns no parameters:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L     15,=V(SYSTEM)
BR    15
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Register 15 is loaded with the address of the &lt;code&gt;SYSTEM&lt;/code&gt; subroutine (which is determined at link time) and an unconditional branch is made.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;SPRINT&lt;/code&gt; call is more complex:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L     15,=V(SPRINT)                SUBROUTINE TO DO I/O
BAL   1,*+18+((L&amp;#39;###1+1)/2*2)      AROUND CONSTANTS
DC    A(*+14)                      DATA ADDRESS
DC    A(*+8)                       LENGTH
DC    A(0)                         NO MODIFIERS SPECIFIED
DC    Y(L&amp;#39;###1)                    LENGTH
DC    C&amp;#39;Hello, world!&amp;#39;
BALR  14,15                        BRANCH TO SUBROUTINE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;L&lt;/code&gt; sets up the subroutine address in register 15. A parameter list is defined with the &lt;code&gt;DC&lt;/code&gt;s; the &lt;code&gt;BAL&lt;/code&gt; puts the address of this in register 1 and jumps over the constants.. Finally, &lt;code&gt;BALR&lt;/code&gt; is used to jump to the address stored in register 15, storing the current address (and other bits from the PSW) in register 14.&lt;/p&gt;
&lt;h2 id=&#34;other-features&#34;&gt;Other features&lt;/h2&gt;
&lt;p&gt;The information presented above should be enough to get started with assembler programming on MTS. We have not covered a number of other features supported by the system, including&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Decimal and floating point arithmetic&lt;/li&gt;
&lt;li&gt;Logical operations like &lt;code&gt;AND&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Byte access and character operations&lt;/li&gt;
&lt;li&gt;I/O operations and channels&lt;/li&gt;
&lt;li&gt;Other parts of the PSW and their use in system programs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the end of the &lt;a href=&#34;https://try-mts.com/system-360-assembly-language-introduction/&#34;&gt;last post&lt;/a&gt; for links to documentation to find out more about assembly language.&lt;/p&gt;
&lt;p&gt;In the next &lt;a href=&#34;https://try-mts.com/system-360-assembly-language-caesar-cipher/&#34;&gt;post&lt;/a&gt; we&amp;rsquo;ll write a complete program in assembly.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>System/360 Assembly Language - Introduction</title>
      <link>https://try-mts.com/system-360-assembly-language-introduction/</link>
      <pubDate>Sun, 26 Nov 2017 02:02:54 +0000</pubDate>
      <guid>https://try-mts.com/system-360-assembly-language-introduction/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2017/10/asm-example.png&#34; alt=&#34;&#34;&gt;&lt;super&gt;&lt;sub&gt;Sample assembly language program form, from &amp;lsquo;A Programmer&amp;rsquo;s Introduction to IBM System/360 Assembler Language&lt;/super&gt;&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;After the heights of &lt;a href=&#34;https://try-mts.com/apl-introduction&#34;&gt;APL&lt;/a&gt;, let&amp;rsquo;s turn to the lowest level language possible: System/360 assembly language.&lt;/p&gt;
&lt;h2 id=&#34;system360-assembly-language&#34;&gt;System/360 Assembly Language&lt;/h2&gt;
&lt;p&gt;MTS runs on the IBM System/360, designed from scratch by IBM in the early 1960s as a unified successor to a number of different architectures. System programmers, and application programmers looking for maximum performance used assembler to write code as close to the bare metal as possible.&lt;/p&gt;
&lt;p&gt;As the System/360 was a new design, the architecture is clean and fairly simple. It&amp;rsquo;s a 32 bit architecture with 24 bit addresses, 16 full word registers and 4 64 bit floating point registers. Binary Coded Decimal (BCD) arithmetic and I/O operations are also supported.&lt;/p&gt;
&lt;h2 id=&#34;assemblers-on-mts&#34;&gt;Assemblers on MTS&lt;/h2&gt;
&lt;p&gt;MTS ran originally on a System/360-67, and later on 370 and Amdahl CPUs which provided extensions to the basic instruction set that we will not consider here.&lt;/p&gt;
&lt;p&gt;The main assembler available to us today is Assembler G (&lt;code&gt;*ASMG&lt;/code&gt;). This is a basic assembler, derived from Assembler F provided by IBM for OS/360 with extensions by the University of Waterloo for improved performance.&lt;/p&gt;
&lt;p&gt;At the time of MTS D6, the most common assembler in use was Assembler H. This has a considerable number of improvements to the assembly language supported in Assembler G; however, this is not available in the MTS distribution due to copyright reasons.&lt;/p&gt;
&lt;p&gt;ASSIST, Assembler System for Student Instruction &amp;amp; Systems Teaching, was an assembler and emulator used by students to learn assembly. As it emulates the underlying machine it can provide additional debug information and run time control, at the expense of performance. It is available on the MTS distribution as &lt;code&gt;*ASSIST&lt;/code&gt;. Finally, &lt;code&gt;*ASMT&lt;/code&gt; is a specialised assembler compatible with features on IBM&amp;rsquo;s time sharing system TSS. We will not consider either of these further in these blog posts.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get this language running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;running-a-program-using-asmg&#34;&gt;Running a program using &lt;code&gt;*ASMG&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*ASMG&lt;/code&gt; will take a file of assembly language instructions from &lt;code&gt;scards&lt;/code&gt; and write output to &lt;code&gt;spunch&lt;/code&gt;. A program listing can be sent to &lt;code&gt;sprint&lt;/code&gt; and errors to &lt;code&gt;sercom&lt;/code&gt; if these are set on the command line. Extra parameters can be set with &lt;code&gt;par&lt;/code&gt;, for example &lt;code&gt;par=test&lt;/code&gt; will add debugging information to the object file.&lt;/p&gt;
&lt;p&gt;Linking is done at run time by MTS, so if you are just using the system libraries the object file can be run directly with &lt;code&gt;$run&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how to run a simple program to print &amp;lsquo;Hello, world!&amp;rsquo; five times using assembly language.&lt;/p&gt;
&lt;p&gt;The source file (with line numbers) looks like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# list hello.asm
       1     HELLO    START   0                 PROGRAM AT RELATIVE ADDRESS 0
       2              USING   HELLO,12          R12 WILL CONTAIN PROGRAM ADDR
       3              LR      12,15             LOAD R12 WITH ABSOLUTE ADDR
       4              L       3,RUNS            R3 COUNTS DOWN NUMBER OF RUNS
       5     LOOP     SPRINT &amp;#39;Hello, world!&amp;#39;    PRINT THE MESSAGE
       6              S       3,DECR            DECREMENT R3
       7              BP      LOOP              IF R3 POSITIVE, LOOP AGAIN
       8              SYSTEM                    EXIT PROGRAM
       9     RUNS     DC      F&amp;#39;5&amp;#39;              NUMBER OF RUNS TO MAKE
      10     DECR     DC      F&amp;#39;1&amp;#39;              DECREMENT FOR LOOP
      11              END     HELLO             END OF CODE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s how to run the assembler. We send errors to &lt;code&gt;*sink*&lt;/code&gt; so they are displayed immediately and a full program listing to &lt;code&gt;-hello.l&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *asmg scards=hello.asm spunch=-load sercom=*sink* sprint=-hello.l par=test
. *** *ASMG has been changed to use *ASMGSYSMAC for the default macro
. *** library instead of *SYSMAC.  It will no longer work with *SYSMAC.
# Execution begins   18:19:08 
 
  ASSEMBLER (G) DONE           18:19:08   30 SEP 17 
 
  NO STATEMENTS FLAGGED IN THIS ASSEMBLY 
# Execution terminated   18:19:08  T=0.091 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can ignore the &lt;code&gt;*ASMG has been changed...&lt;/code&gt; message; &lt;code&gt;NO STATEMENTS FLAGGED&lt;/code&gt; means that it worked OK.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s run the assembled program.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run -load
# Execution begins   18:19:17 
  Hello, world!
  Hello, world!
  Hello, world!
  Hello, world!
  Hello, world!
# Execution terminated   18:19:17  T=0 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;A good place to start is &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/asm/SC20-1646-6_int360asm_Aug70.pdf&#34;&gt;A Programmer&amp;rsquo;s Introduction to IBM System/360 Assembler Language&lt;/a&gt;, which gives an overview of the System/360 architecture and then teaches different aspects of assembly language programming using examples.&lt;/p&gt;
&lt;p&gt;A full description of how the System/360 works and the opcodes available is in &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/princOps/A22-6821-7_360PrincOpsDec67.pdf&#34;&gt;IBM System/360 Principles of Operation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/asm/GC28-6514-9_OS_Assembler_Rel21_Jan74.pdf&#34;&gt;OS Assembler Language&lt;/a&gt; is a reference guide to assembly language. &lt;code&gt;*ASMG&lt;/code&gt; is documented in &lt;a href=&#34;https://deepblue.lib.umich.edu/handle/2027.42/79581&#34;&gt;MTS Volume 2X&lt;/a&gt; from page 27 onwards.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol14-360370AssemblersInMTS-Sep1986.pdf&#34;&gt;MTS Volume 14: 360/370 Assemblers in MTS&lt;/a&gt; details the differences between Assembler G and Assembler H, macro libraries, structured programming macros and the ASSIST assembler.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol03-SystemSubroutineDescriptions-Sep1989.pdf&#34;&gt;MTS Volume 3: System Subroutine Descriptions&lt;/a&gt; describes the &amp;lsquo;standard library&amp;rsquo; of routines available to assembler programmers on MTS.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>APL - Knuth Shuffle</title>
      <link>https://try-mts.com/apl-knuth-shuffle/</link>
      <pubDate>Sun, 12 Nov 2017 02:50:12 +0000</pubDate>
      <guid>https://try-mts.com/apl-knuth-shuffle/</guid>
      <description>&lt;p&gt;In the final part of this &lt;a href=&#34;https://try-mts.com/apl-introduction/&#34;&gt;series&lt;/a&gt;, let&amp;rsquo;s create a real program in APL. As before, we&amp;rsquo;ll show both the original and transliterated version so we can run the program on MTS&amp;rsquo;s APL.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll implement Knuth Shuffle (also known as Fisher/Yates shuffle) from &lt;a href=&#34;https://rosettacode.org/wiki/Knuth_shuffle&#34;&gt;Rosetta Code&lt;/a&gt;. This produces a random permutation of a vector.&lt;/p&gt;
&lt;h2 id=&#34;using-deal&#34;&gt;Using deal&lt;/h2&gt;
&lt;p&gt;Dyadic &lt;code&gt;?&lt;/code&gt;, or deal, looks an ideal candidate here. Recall from the previous part that &lt;code&gt;x ? y&lt;/code&gt; means take &lt;code&gt;x&lt;/code&gt; unique items from the population 1 &amp;hellip; &lt;code&gt;y&lt;/code&gt;. This means that &lt;code&gt;x ? x&lt;/code&gt; will give a random permutation of all the values 1 &amp;hellip; &lt;code&gt;x&lt;/code&gt;, which we can use as vector indices.&lt;/p&gt;
&lt;p&gt;What else will we need?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We&amp;rsquo;ll need to define a &lt;em&gt;monadic function&lt;/em&gt; that takes the vector to be shuffled as input and returns a new, shuffled vector.&lt;/li&gt;
&lt;li&gt;We need to know the &lt;em&gt;length&lt;/em&gt; of the vector, for which we can use rho (&lt;code&gt;⍴&lt;/code&gt; or &lt;code&gt;$RH&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;We need to access a series of vector contents by their index, which we can do with &lt;code&gt;[&lt;/code&gt; &amp;hellip; &lt;code&gt;]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Putting this together:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;∇S ← SHUFFLE1 V                     &amp;#34;S = SHUFFLE1 V    
S ← V[(⍴ V) ? ⍴ V]                  S = V[($RH V) ? $RH
∇                                   &amp;#34;                  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s try to run this and see if it works:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        SHUFFLE1 11 22 33
11  33  22  
        SHUFFLE1 11 22 33
22  11  33  
        SHUFFLE1 11 22
22  11  
        SHUFFLE1 11 22
22  11  
        SHUFFLE1 11 22
11  22  
        SHUFFLE1 ,11
11  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the last example we need to use ravel (&lt;code&gt;,&lt;/code&gt;) to change the input scalar 11 into a vector of length 1 containing 11.&lt;/p&gt;
&lt;p&gt;This function will also work on strings, which are treated as vectors of characters:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        SHUFFLE1 &amp;#39;FACE&amp;#39;
FCEA  
        SHUFFLE1 &amp;#39;FACE&amp;#39;
ACFE  
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;implementing-the-algorithm&#34;&gt;Implementing the algorithm&lt;/h2&gt;
&lt;p&gt;What if we wanted to implement the algorithm itself, rather then just use deal? Let&amp;rsquo;s start by looking at the pseudocode given on Rosetta Code. For a vector with indices 0 - last:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i from last downto 1 do:
    let j = random integer in range 0 ≤ j ≤ i
    swap items[i] with items[j]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What language features will we need from APL?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;temporary&lt;/em&gt; variables &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;j&lt;/code&gt; mean we will need to declare local variables in our function.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Looping&lt;/em&gt; can be done by a sequence of instructions with the branch operator (&lt;code&gt;→&lt;/code&gt; or &lt;code&gt;$GO&lt;/code&gt;) testing whether we have reached the start of the vector or not.&lt;/li&gt;
&lt;li&gt;By default, APL vectors are &lt;em&gt;indexed&lt;/em&gt; from 1 &amp;hellip; length so we&amp;rsquo;ll need to account for that in the loop.&lt;/li&gt;
&lt;li&gt;The algorithm needs a &lt;em&gt;random number&lt;/em&gt; to determine what to shuffle, for which we can use the monadic form of &lt;code&gt;?&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We need to &lt;em&gt;swap&lt;/em&gt; two elements of the vector. I thought this might need a separate function at first that uses a temporary to swap, but after some review of how indexing works in APL I realised we can use &lt;code&gt;v[x,y] = v[y,x]&lt;/code&gt; to swap elements at &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s the complete program:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;∇S ← SHUFFLE2 V;I;J                 &amp;#34;S = SHUFFLE2 V;I;J
I ← ⍴ V                             I = $RH V
→ (3,7)[1 + I ≤ 1]                  $GO (3,7)[1 + I $LE 1]
J ← ? I                             J = ? I
V[I,J] ← V[J,I]                     V[I,J] = V[J,I]
I ← I - 1                           I = I - 1
→ 2                                 $GO 2
S ← V                               S = V
∇                                   &amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Line 2 is tricky: it branches to lines in the vector in round brackets based on the expression in the square brackets. If &lt;code&gt;I&lt;/code&gt; is less than or equal to 1 then the expression evaluates to 2, so control jumps to line 7 (&lt;code&gt;S ← V&lt;/code&gt;) where the output value is assigned and the function ends. Otherwise it will continue to line 3 and swap an element.&lt;/p&gt;
&lt;p&gt;Running this on some test cases shows this works as expected.&lt;/p&gt;
&lt;p&gt;Ideally we&amp;rsquo;d like to use idiomatic APL and avoid a loop altogether. Modern APLs have control structures like &lt;code&gt;repeat&lt;/code&gt;, but I can&amp;rsquo;t see an easy way to do this in APL\360 - if you can, please add a comment!&lt;/p&gt;
&lt;h2 id=&#34;performance&#34;&gt;Performance&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s do a test by running each of these implementations 60,000 times with the same input (1 2 3). We can use code like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;∇RUN1;I                             &amp;#34;RUN1;I
I ← 60000                           I = 60000
SHUFFLE1 1 2 3                      SHUFFLE1 1 2 3
I ← I - 1                           I = I - 1
→ 2 × I &amp;gt; 0                         $GO 2 * I $GT 0
∇                                   &amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On my system, SHUFFLE1 takes 7.3s to run and SHUFFLE2 24.6s, which we&amp;rsquo;d expect as SHUFFLE1 uses the system provided deal function.&lt;/p&gt;
&lt;p&gt;I also ran a quick check for randomness by printing the results to a file and seeing how many times each permutation occurred. Over 60,000 runs we&amp;rsquo;d expect each permutation to appear around 10,000 times; SHUFFLE1 was within 0.5% of that and SHUFFLE2 0.6%.&lt;/p&gt;
&lt;p&gt;Internally, &lt;code&gt;?&lt;/code&gt; uses a pseudo-random number generator so true randomness cannot be achieved. Also with a 32 bit word, the number of possible states is 2^32, which limits the number of combinations that can occur. For example, if we wanted to shuffle a deck of 52 cards there are 52! combinations, much more than 2^32.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts-on-apl&#34;&gt;Final thoughts on APL&lt;/h2&gt;
&lt;p&gt;APL&amp;rsquo;s use of a large number of symbols is what first strikes you when learning the language. I found by going through the tutorials that you start picking them up quite quickly, and it&amp;rsquo;s not hard to write simple programs. However, reading other people&amp;rsquo;s programs can be difficult, given its compact form and right-to-left structure; I imagine that understanding a large APL code base would take some time.&lt;/p&gt;
&lt;p&gt;The language itself is unique and even beautiful; some times I feel like I&amp;rsquo;m writing mathematics rather than coding. If you have the opportunity then I&amp;rsquo;d recommend trying it out.&lt;/p&gt;
&lt;p&gt;The transliterations needed on emulated MTS add another level of difficulty and unless you want to experience the historical aspects of running APL like this I&amp;rsquo;d recommend learning APL with a modern implementation that can use APL symbols directly. There are several commercial implementations that run on modern hardware, for example &lt;a href=&#34;https://www.dyalog.com/&#34;&gt;Dynalog&lt;/a&gt;. The GNU project also has &lt;a href=&#34;https://www.gnu.org/software/apl/&#34;&gt;GNU APL&lt;/a&gt;. Several languages have been derived from APL, including &lt;a href=&#34;https://en.wikipedia.org/wiki/K_(programming_language)&#34;&gt;K&lt;/a&gt;, used for financial analysis.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Jeff Atwood&amp;rsquo;s Coding Horror blog has a good post on &lt;a href=&#34;https://blog.codinghorror.com/shuffling/&#34;&gt;shuffling&lt;/a&gt; with a follow-up on the &lt;a href=&#34;https://blog.codinghorror.com/the-danger-of-naivete/&#34;&gt;dangers&lt;/a&gt; of mis-implementing the algorithm.&lt;/p&gt;
&lt;p&gt;Eugene McDonnell wrote an interesting article on &lt;a href=&#34;http://www.jsoftware.com/papers/roll.htm&#34;&gt;How the Roll Function Works&lt;/a&gt; in APL\360 and other APLs, giving insight on its implementation.&lt;/p&gt;
&lt;p&gt;To see some amazing APL one-liners in action, check out &lt;a href=&#34;http://archive.vector.org.uk/art10501480&#34;&gt;Sixteen APL Amuse-Bouches&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>APL - Language features</title>
      <link>https://try-mts.com/apl-language-features/</link>
      <pubDate>Sun, 05 Nov 2017 00:42:39 +0000</pubDate>
      <guid>https://try-mts.com/apl-language-features/</guid>
      <description>&lt;p&gt;After the &lt;a href=&#34;https://try-mts.com/apl-introduction&#34;&gt;introduction&lt;/a&gt; to APL in the last post, let&amp;rsquo;s now look in more detail on how APL works.&lt;/p&gt;
&lt;p&gt;As the emulated system does not support entry and display of APL symbols, we will have to use transliterations, eg * for the multiply symbol ×. The code samples below show the original version on the left and the transliterated version on the right. An appendix at the end of this post lists all symbols used and their transliterations.&lt;/p&gt;
&lt;h2 id=&#34;the-interpreter&#34;&gt;The interpreter&lt;/h2&gt;
&lt;p&gt;Similar to the last language we looked at, &lt;a href=&#34;https://try-mts.com/pil-introduction&#34;&gt;PIL&lt;/a&gt;, when APL starts up it allows you to enter expressions and get results immediately. The prompt is eight leading spaces and results are printed in the first column.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        21 × 2                        21 * 2
42                            42
a &amp;#34;string&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;monadic-and-dyadic-functions&#34;&gt;Monadic and dyadic functions&lt;/h2&gt;
&lt;p&gt;We can enter simple arithmetic functions and get results as expected.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        21 + 2                        21 + 2
23                            23            
        21 − 2                        21 - 2
19                            19            
        21 ÷ 2                        21 / 2
10.5                          10.5          
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These are all &lt;em&gt;dyadic&lt;/em&gt; functions, ie they take a left and right parameter. Many APL functions also have &lt;em&gt;monadic&lt;/em&gt; versions which take only one parameter on the right. Let&amp;rsquo;s look at how the four basic arithmetic functions work:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        +21                           +21   
21                            21            
        −21                           -21   
−21                           _21           
        ×21                           *21   
1                             1             
        ÷21                           /21   
0.04761904762                 0.04761904762 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Plus and minus work as expected; note that negative numbers are displayed with a leading underscore to represent the &lt;code&gt;−&lt;/code&gt; symbol. Multiply and divide give more interesting results. Multiply gives the signum of its parameter: -1 if negative, 0 if zero or 1 if positive. Divide gives the reciprocal of its parameter.&lt;/p&gt;
&lt;p&gt;The modulus operator (&lt;code&gt;∣&lt;/code&gt; or &lt;code&gt;$|&lt;/code&gt;) works the opposite way around from what you may be used to from other languages, so &lt;code&gt;10 ∣ 12&lt;/code&gt; is &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Order of execution is strictly right to left; using brackets allows you to specify what operators works on what parameters&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        2×3+2                         2*3+2   
10                            10              
        ÷2×3+2                        /2*3+2  
0.1                           0.1             
        (2×3)+2                       (2*3)+2 
8                             8               
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;variables-and-types&#34;&gt;Variables and types&lt;/h2&gt;
&lt;p&gt;Values can be assigned to a variable with &lt;code&gt;←&lt;/code&gt; (&lt;code&gt;=&lt;/code&gt;). Variable names need to start with a letter. Underlined letters can also be used; these are transliterated to lower case letters.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        X ← 3                         X = 3    
        Y ← 22                        Y = 22   
        Z ← X × Y                     Z = X * Y
        Z                             Z        
66                            66               
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As well as single numbers, you can have vectors of numbers. These are entered by separating the elements with spaces. Operators work on vectors as well.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        X ← 1 2 3                     X = 1 2 3       
        Y ← 10 100 1000               Y = 10 100 1000 
        X × Y                         X * Y           
10  200  3000                 10  200  3000           
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the dimension of the parameters differ, APL will extend the shorter vector as appropriate - so &lt;code&gt;1 2 3 × 2&lt;/code&gt; will give &lt;code&gt;2 4 6&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Strings are introduced using quotes; internally APL treats them as vectors of characters.&lt;/p&gt;
&lt;p&gt;Booleans are represented as numbers with a value of 0 or 1. These are returned by comparison functions like equal, greater or equal (&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;≥&lt;/code&gt; or &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;$GE&lt;/code&gt;). Logical operators like and (&lt;code&gt;∧&lt;/code&gt; or &lt;code&gt;&amp;amp;&lt;/code&gt;) can operate on these.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        3 ≥ 2                         3 $GE 2              
1                             1                            
        (3 ≥ 2) ∧ (2 = 2)             (3 $GE 2) &amp;amp; (2 $EQ 2)
1                             1                            
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;vector-functions&#34;&gt;Vector functions&lt;/h2&gt;
&lt;p&gt;The rho function (&lt;code&gt;⍴&lt;/code&gt; or &lt;code&gt;$,&lt;/code&gt;) gives the dimension of a vector when used moadically. Used dyadically, it can create a matrix from a vector on its right side by giving the shape on the left side.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ⍴ 10 20 30                    $, 10 20 30               
3                             3                                 
        X ← 1 2 3 4 5 6 7 8           X = 1 2 3 4 5 6 7 8 
            9 10                          9 10
        2 5 ⍴ X                       2 5 $, X                  
1   2   3   4   5             1   2   3   4   5                 
6   7   8   9  10             6   7   8   9  10                 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Comma (&lt;code&gt;,&lt;/code&gt;) used dyadically is called catenate, and adds to a vector.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        x ← 1 2 3 4 5                 x = 1 2 3 4 5 
        x , 6                         x , 6         
1  2  3  4  5  6              1  2  3  4  5  6      
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Monadic comma (known as ravel) turns a scalar into a vector&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         ,1                            ,1  
 1                             1           
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A vector of length 1 looks like a scalar when displayed. We can tell them apart with double rho, which gives the rank.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         ⍴⍴ 1                          $,$, 1 
 0                             0              
         ⍴⍴ ,1                         $,$, ,1
 1                             1              
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Iota (&lt;code&gt;⍳&lt;/code&gt;, which can be transliterated as any of &lt;code&gt;$IO&lt;/code&gt;, &lt;code&gt;$IN&lt;/code&gt; or &lt;code&gt;$.&lt;/code&gt;) creates a sequence when used monadically and gives index positions when used dyadically. Square brackets can be used to extract elements based on index positions.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ⍳ 5                            $IO 5           
1  2  3  4  5                  1  2  3  4  5           
        X ← &amp;#39;ABCDEFGHIJ&amp;#39;               X = &amp;#39;ABCDEFGHIJ&amp;#39;
        X ⍳ &amp;#39;CAFE&amp;#39;                     X $IN &amp;#39;CAFE&amp;#39;    
3  1  6  5                     3  1  6  5              
        X ⍳ &amp;#39;CAZE&amp;#39;                     X $IN &amp;#39;CAZE&amp;#39;    
3  1  11  5                    3  1  11  5             
        X[X ⍳ &amp;#39;CAFE&amp;#39;]                  X[X $IN &amp;#39;CAFE&amp;#39;] 
CAFE                           CAFE                    
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;more-apl-functions&#34;&gt;More APL functions&lt;/h2&gt;
&lt;p&gt;There are over 50 APL functions so it would be difficult to go through them all here, but let&amp;rsquo;s take a brief tour to show some of them in action.&lt;/p&gt;
&lt;p&gt;Ceiling (&lt;code&gt;⌈&lt;/code&gt; or &lt;code&gt;$CE&lt;/code&gt;, &lt;code&gt;$MA&lt;/code&gt;) and floor (&lt;code&gt;⌊&lt;/code&gt; or &lt;code&gt;$FL&lt;/code&gt;, &lt;code&gt;$MI&lt;/code&gt;) rounds up/down when used monadically and finds the max/min when used dyadically&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         ⌈ 2.3                         $CE 2.3             
 3                             3                           
         ⌊ 2.3                         $FL 2.3             
 2                             2                           
         3 ⌈ 2                         3 $CE 2             
 3                             3                           
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Factorial (&lt;code&gt;!&lt;/code&gt;) means take m of n when used dyadically:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        !5                             !5                   
120                            120                          
        2 ! 4                          2 ! 4                
 6                             6                            
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Rotation (&lt;code&gt;⌽&lt;/code&gt;, &lt;code&gt;$RO&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ⌽ 1 2 3                        $RO 1 2 3                   
3  2  1                        3  2  1                             
        2 ⌽ 1 2 3                      2 $RO 1 2 3                 
3  1  2                        3  1  2                             
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Take (&lt;code&gt;↑&lt;/code&gt;, &lt;code&gt;$TA&lt;/code&gt;) and drop (&lt;code&gt;↓&lt;/code&gt;, &lt;code&gt;$DR&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        3 ↑ ⍳ 10                       3 $TA $IO 10                
1  2  3                        1  2  3                             
        3 ↓ ⍳ 10                       3 $DR $IO 10                
4  5  6  7  8  9  10           4  5  6  7  8  9  10                
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Grade up (&lt;code&gt;⍋&lt;/code&gt; or &lt;code&gt;$GU&lt;/code&gt;) gives sorted indices, with grade down doing sort in reversed order.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        X ← 2 14 1 42                  X = 2 14 1 42               
        ⍋ X                            $GU X                       
3  1  2  4                     3  1  2  4                          
        X[⍋ X]                         X[$GU X]                    
1  2  14  42                   1  2  14  42                        
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;?&lt;/code&gt; used monadically is known as roll, producing a random number between 1 and the right hand argument. Used dyadically, it is known as deal: &lt;code&gt;x ? y&lt;/code&gt; means take &lt;code&gt;x&lt;/code&gt; unique items at random from the population 1 .. &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ?6                             ?6                     
3                              3                              
        ?6                             ?6                     
5                              5                              
        3 ? 6                          3 ? 6                  
6  2  3                        6  2  3                        
        3 ? 6                          3 ? 6                  
3  4  5                        3  4  5                        
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Decode (&lt;code&gt;⊥&lt;/code&gt; or &lt;code&gt;$DE&lt;/code&gt;, &lt;code&gt;$BA&lt;/code&gt;) converts bases. Encode (&lt;code&gt;⊤&lt;/code&gt; or &lt;code&gt;$EN&lt;/code&gt;, &lt;code&gt;$RP&lt;/code&gt;) goes the other way. Below we show 2 hours 30 minutes decoded into number of minutes and then re-encoded.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        60 60 ⊥ 2 30                   60 60 $DE 2 30         
150                            150                            
        60 60 ⊤ 150                    60 60 $EN 150          
2  30                          2  30                          
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;operators&#34;&gt;Operators&lt;/h2&gt;
&lt;p&gt;An operator in APL differs from a function in that it takes a function on its left hand side. One example is reduction (&lt;code&gt;/&lt;/code&gt; or &lt;code&gt;%&lt;/code&gt;): in the example below we give it the plus function which will sum up the elements on the right&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        +/ 1 2 3 4                     +% 1 2 3 4                 
10                             10                                 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s try it with &lt;code&gt;-&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        -% 1 2 3 4                     -% 1 2 3 4  
-2                             _2                  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why does it return -2? This is due to the right to left associativity of APL: we could expand this as 1 - (2 - (3 - 4)).&lt;/p&gt;
&lt;h2 id=&#34;defining-your-own-functions&#34;&gt;Defining your own functions&lt;/h2&gt;
&lt;p&gt;You can define your own functions with del (&lt;code&gt;∇&lt;/code&gt; or &lt;code&gt;&amp;quot;&lt;/code&gt;), Starting a line with del creates a function: the rest of the line specifies the variable that will contain the return value, the function name and its parameters. On subsequent lines, APL will prompt you for the next statement with the line number. A del on its own will close the function. A simple example for a monadic function called &lt;code&gt;INCR&lt;/code&gt; that increments its right hand side:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ∇ C ← INCR A                   &amp;#34; C = INCR A                 
&amp;lt;1&amp;gt;     C ← A + 1              &amp;lt;1&amp;gt;     C = A + 1                    
&amp;lt;2&amp;gt;     ∇                      &amp;lt;2&amp;gt;     &amp;#34;                            
        INCR 3                         INCR 3                       
4                              4                                    
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dyadic functions are created by giving variables before and after the function names. For example, the hypotenuse function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ∇ C ← A HYP B                  &amp;#34; C = A HYP B
&amp;lt;1&amp;gt;     C ← ((A⋆2) + (B⋆2))    &amp;lt;1&amp;gt;     C = ((A@2) + (B@2))
          ⋆ 0.5                          @ 0.5
&amp;lt;2&amp;gt;     ∇                      &amp;lt;2&amp;gt;     &amp;#34;
        3 HYP 4                        3 HYP 4
5                              5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Apart from the parameters, any variables used in defined functions will be global by default. To set up local variables, add a semicolon and the variable names on the function definition line.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ∇ Z ← X FOO Y; A               &amp;#34; Z = X FOO Y;
&amp;lt;1&amp;gt;     A ← X + Y              &amp;lt;1&amp;gt;     A = X + Y     
&amp;lt;2&amp;gt;     Z ← A + 2              &amp;lt;2&amp;gt;     Z = A + 2     
&amp;lt;3&amp;gt;     ∇                      &amp;lt;3&amp;gt;     &amp;#34;             
        3 FOO 5                        3 FOO 5       
10                             10                    
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Flow control in a function can be introduced with the branch function (&lt;code&gt;→&lt;/code&gt; or &lt;code&gt;$GO&lt;/code&gt;), which takes the line number to branch to on the right hand side. Branching to line number 0 is equivalent to returning from the function: we saw the in part 1 of this series with the line&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;→ 2 × N &amp;lt; 5                    $GO 2 * N $LE 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which would branch to line 2 if &lt;code&gt;N&lt;/code&gt; was less than 5, else return from
the function.&lt;/p&gt;
&lt;p&gt;After defining a function, you can list its contents by entering del, the function name, quad (&lt;code&gt;⎕&lt;/code&gt; or &lt;code&gt;#&lt;/code&gt;) in square brackets then del, all on the same line.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;∇FOO[⎕]∇                       &amp;#34;FOO[#]&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can edit or append lines in a function by replacing quad in the above example with a line number. Line numbers can include decimal points, eg to insert a line between current lines 1 and 2 you&amp;rsquo;d do &lt;code&gt;∇FOO[1.5]∇&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To delete an entire function you need to use the erase system command with the function name, eg &lt;code&gt;)ERASE FOO&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;system-commands-and-workspaces&#34;&gt;System commands and workspaces&lt;/h2&gt;
&lt;p&gt;System commands, starting with &lt;code&gt;)&lt;/code&gt;, manipulate the APL environment. &lt;code&gt;)OFF&lt;/code&gt; will quit APL, &lt;code&gt;)FNS&lt;/code&gt; and &lt;code&gt;)VARS&lt;/code&gt; will list currently defined functions and variables respectively.&lt;/p&gt;
&lt;p&gt;To manage sets of functions and variables, APL has the concept of workspaces. The current set can be saved to a named workspace, eg &lt;code&gt;FOO&lt;/code&gt; by the user with the command &lt;code&gt;)SAVE FOO&lt;/code&gt; and then loaded later with &lt;code&gt;)LOAD FOO&lt;/code&gt;. APL will also automatically save the current set to the workspace &lt;code&gt;CONTINUE&lt;/code&gt; on exit and load it again at startup. To wipe out the current running set, use &lt;code&gt;)CLEAR&lt;/code&gt;; to delete from disk user &lt;code&gt;)DROP ws&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are also system workspaces, organised into libraries identified by numbers. Use &lt;code&gt;)LIB n&lt;/code&gt; to see the workspaces in library &lt;code&gt;n&lt;/code&gt; and then &lt;code&gt;)LOAD n ws&lt;/code&gt; to load a names workspace. For example, let&amp;rsquo;s look at the workspace &lt;code&gt;APLCOURSE&lt;/code&gt; in library 1. This defines a function &lt;code&gt;DESCRIBE&lt;/code&gt; which explains its contents.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        )LIB 1
ADVANCEDE  
APLCOURSE  
CLASS  
NEWS  
PLOTFORMA  
TYPEDRILL  
WSFNS  
EIGENVALU  
BRFNS  
        )LOAD 1 APLCOURSE
SAVED  16.13.05 08%08%68  
        )FNS
B1X     CHECK   DESCRIBE        DIM     DRILL   DYAD1   DYAD2   EASY  
EASYDRILL       FORM    FUNDRILL        GET     INPUT   QUES    RANDOM 
REDSCAPATCH     REPP    TEACH  
        DESCRIBE
  
THE MAIN FUNCTIONS IN THIS LIBRARY WORKSPACE ARE:  
  
                         TEACH  
                         EASYDRILL  
  
ALL OTHER FUNCTIONS ARE SUBFUNCTIONS AND ARE NOT  
SELF-CONTAINED.  
SYNTAX                         DESCRIPTION  
______                         ___________  
TEACH           AN EXERCISE IN APL FUNCTIONS USING SCALARS  
                AND VECTORS.  THE FUNCTION PRINTS OUT THE  
                CHOICES AND OPTIONS AVAILABLE.  EXAMPLES  
                ARE SELECTED AT RANDOM WITH A RANDOM  
                STARTING POINT.  
  
EASYDRILL       THIS IS THE SAME AS TEACH EXCEPT THAT THE  
                PROBLEMS SELECTED ARE GENERALLY SIMPLER IN  
                STRUCTURE.  PROBLEMS INVOLVING VECTORS OF  
                LENGTH ZERO OR ONE ARE EXCLUDED.  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Workspace 6 contains a resource management game called KINGDOM.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://try-mts.com/apl-knuth-shuffle/&#34;&gt;next post&lt;/a&gt; we&amp;rsquo;ll look at implementing a real program in APL.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;This post has only scratched the surface of APL. See the Further Information section in the &lt;a href=&#34;https://try-mts.com/apl-introduction&#34;&gt;APL introduction&lt;/a&gt; post for more resources to learn about APL.&lt;/p&gt;
&lt;h2 id=&#34;appendix-transliterations&#34;&gt;Appendix: Transliterations&lt;/h2&gt;
&lt;p&gt;This is a copy of the table UM Computing Center Memo 382, excluding characters that are marked as not in use.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Meaning&lt;/th&gt;
          &lt;th&gt;APL Symbol&lt;/th&gt;
          &lt;th&gt;Transliteration&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;And&lt;/td&gt;
          &lt;td&gt;∧&lt;/td&gt;
          &lt;td&gt;&amp;amp;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Branch&lt;/td&gt;
          &lt;td&gt;→&lt;/td&gt;
          &lt;td&gt;$GO $&amp;gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Ceiling&lt;/td&gt;
          &lt;td&gt;⌈&lt;/td&gt;
          &lt;td&gt;$CE $MA&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Circular functions&lt;/td&gt;
          &lt;td&gt;○&lt;/td&gt;
          &lt;td&gt;$$ $CI $PI&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Comma&lt;/td&gt;
          &lt;td&gt;,&lt;/td&gt;
          &lt;td&gt;,&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Comment / lamp&lt;/td&gt;
          &lt;td&gt;⍝&lt;/td&gt;
          &lt;td&gt;$* $CO&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compression&lt;/td&gt;
          &lt;td&gt;/&lt;/td&gt;
          &lt;td&gt;%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compression axis 1&lt;/td&gt;
          &lt;td&gt;⌿&lt;/td&gt;
          &lt;td&gt;$C1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Deal / random&lt;/td&gt;
          &lt;td&gt;?&lt;/td&gt;
          &lt;td&gt;?&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Decode&lt;/td&gt;
          &lt;td&gt;⊥&lt;/td&gt;
          &lt;td&gt;$DE $BA&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Del&lt;/td&gt;
          &lt;td&gt;∇&lt;/td&gt;
          &lt;td&gt;&amp;quot;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Delta&lt;/td&gt;
          &lt;td&gt;Δ&lt;/td&gt;
          &lt;td&gt;$&amp;quot;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Delta underlined&lt;/td&gt;
          &lt;td&gt;⍙&lt;/td&gt;
          &lt;td&gt;$U&amp;quot;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Divide&lt;/td&gt;
          &lt;td&gt;÷&lt;/td&gt;
          &lt;td&gt;/&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Drop&lt;/td&gt;
          &lt;td&gt;↓&lt;/td&gt;
          &lt;td&gt;$DR $DO&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Encode&lt;/td&gt;
          &lt;td&gt;⊤&lt;/td&gt;
          &lt;td&gt;$EN $RP&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Equal&lt;/td&gt;
          &lt;td&gt;=&lt;/td&gt;
          &lt;td&gt;$EQ&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Expansion&lt;/td&gt;
          &lt;td&gt;\&lt;/td&gt;
          &lt;td&gt;$%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Expansion axis 1&lt;/td&gt;
          &lt;td&gt;⍀&lt;/td&gt;
          &lt;td&gt;$X1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Exponentiation&lt;/td&gt;
          &lt;td&gt;⋆&lt;/td&gt;
          &lt;td&gt;@&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Factorial&lt;/td&gt;
          &lt;td&gt;!&lt;/td&gt;
          &lt;td&gt;! $FA $BC&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Floor&lt;/td&gt;
          &lt;td&gt;⌊&lt;/td&gt;
          &lt;td&gt;$FL $MI&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Grade down&lt;/td&gt;
          &lt;td&gt;⍒&lt;/td&gt;
          &lt;td&gt;$GD&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Grade up&lt;/td&gt;
          &lt;td&gt;⍋&lt;/td&gt;
          &lt;td&gt;$GU&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Greater or equal&lt;/td&gt;
          &lt;td&gt;≥&lt;/td&gt;
          &lt;td&gt;$GE&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Greater than&lt;/td&gt;
          &lt;td&gt;&amp;gt;&lt;/td&gt;
          &lt;td&gt;$GT&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Ibeam&lt;/td&gt;
          &lt;td&gt;⌶&lt;/td&gt;
          &lt;td&gt;$IB $SY&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Iota&lt;/td&gt;
          &lt;td&gt;⍳&lt;/td&gt;
          &lt;td&gt;$IO $IN $.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Less or equal&lt;/td&gt;
          &lt;td&gt;≤&lt;/td&gt;
          &lt;td&gt;$LE&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Less than&lt;/td&gt;
          &lt;td&gt;&amp;lt;&lt;/td&gt;
          &lt;td&gt;$LT&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Locked function&lt;/td&gt;
          &lt;td&gt;⍫&lt;/td&gt;
          &lt;td&gt;$L&amp;quot;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Logarithm&lt;/td&gt;
          &lt;td&gt;⍟&lt;/td&gt;
          &lt;td&gt;$@ $LO $LN&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Membership&lt;/td&gt;
          &lt;td&gt;∈&lt;/td&gt;
          &lt;td&gt;$EP $ME&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Minus&lt;/td&gt;
          &lt;td&gt;−&lt;/td&gt;
          &lt;td&gt;-&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Modulus&lt;/td&gt;
          &lt;td&gt;∣&lt;/td&gt;
          &lt;td&gt;$MO&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Multiply&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;*&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Nand&lt;/td&gt;
          &lt;td&gt;⍲&lt;/td&gt;
          &lt;td&gt;$N&amp;amp;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Negation / overbar&lt;/td&gt;
          &lt;td&gt;−&lt;/td&gt;
          &lt;td&gt;_ $-&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Nor&lt;/td&gt;
          &lt;td&gt;⍱&lt;/td&gt;
          &lt;td&gt;$N $WR&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Not&lt;/td&gt;
          &lt;td&gt;~&lt;/td&gt;
          &lt;td&gt;$NO&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Not equal&lt;/td&gt;
          &lt;td&gt;≠&lt;/td&gt;
          &lt;td&gt;$NE&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Null&lt;/td&gt;
          &lt;td&gt;∘&lt;/td&gt;
          &lt;td&gt;$:&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Or&lt;/td&gt;
          &lt;td&gt;∨&lt;/td&gt;
          &lt;td&gt;| $OR&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Period&lt;/td&gt;
          &lt;td&gt;.&lt;/td&gt;
          &lt;td&gt;.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Plus&lt;/td&gt;
          &lt;td&gt;+&lt;/td&gt;
          &lt;td&gt;+&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Quad&lt;/td&gt;
          &lt;td&gt;⎕&lt;/td&gt;
          &lt;td&gt;#&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Quad-quote&lt;/td&gt;
          &lt;td&gt;⍞&lt;/td&gt;
          &lt;td&gt;$#&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Quote&lt;/td&gt;
          &lt;td&gt;&#39;&lt;/td&gt;
          &lt;td&gt;&#39;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Random&lt;/td&gt;
          &lt;td&gt;?&lt;/td&gt;
          &lt;td&gt;?&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Rho / dimension&lt;/td&gt;
          &lt;td&gt;⍴&lt;/td&gt;
          &lt;td&gt;$, $RH $DI&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Rotation&lt;/td&gt;
          &lt;td&gt;⌽&lt;/td&gt;
          &lt;td&gt;$RO $RV&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Rotation axis 1&lt;/td&gt;
          &lt;td&gt;⊖&lt;/td&gt;
          &lt;td&gt;$R1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Semicolon&lt;/td&gt;
          &lt;td&gt;;&lt;/td&gt;
          &lt;td&gt;;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Specification&lt;/td&gt;
          &lt;td&gt;←&lt;/td&gt;
          &lt;td&gt;=&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Take&lt;/td&gt;
          &lt;td&gt;↑&lt;/td&gt;
          &lt;td&gt;$TA $UP&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Transposition&lt;/td&gt;
          &lt;td&gt;⍉&lt;/td&gt;
          &lt;td&gt;$TR&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Underlined letters&lt;/td&gt;
          &lt;td&gt;&lt;u&gt;A&lt;/u&gt; - &lt;u&gt;Z&amp;lt;/u&lt;/td&gt;
          &lt;td&gt;a - z  _A - _Z&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
    </item>
    
    <item>
      <title>APL - Introduction</title>
      <link>https://try-mts.com/apl-introduction/</link>
      <pubDate>Sun, 22 Oct 2017 12:20:47 +0000</pubDate>
      <guid>https://try-mts.com/apl-introduction/</guid>
      <description>&lt;p&gt;We now turn our attention to APL, a unique symbolic programming language that can be run on MTS.&lt;/p&gt;
&lt;h2 id=&#34;apl-a-programming-language&#34;&gt;APL, A Programming Language&lt;/h2&gt;
&lt;p&gt;The concepts behind APL came from work done by Kenneth E. Iverson at Harvard in the late 1950s. He wrote the book &lt;em&gt;A Programming Language&lt;/em&gt; from which APL got its name. He moved to IBM in the early 1960s and helped produce the first working version of the language. IBM distributed versions of APL in the 1960s and 1970s, during which time the language was refined into APL2. Implementations were made for other architectures, including microcomputers in the 1980s.&lt;/p&gt;
&lt;p&gt;APL is unique for its use of special symbols for functions and the ability to operate on multi-dimensional arrays. Put together, this allows a small amount of code to do a large amount of work. An example (from &lt;a href=&#34;https://en.wikipedia.org/wiki/APL_%28programming_language%29#Prime_numbers&#34;&gt;Wikipedia&lt;/a&gt; to compute the prime numbers from 1 to R:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(~R∊R∘.×R)/R←1↓ιR
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;apl-on-mts&#34;&gt;APL on MTS&lt;/h2&gt;
&lt;p&gt;The version of APL on MTS is based on APL\360, developed at IBM in the late 1960s. This was adapted to use the local MTS file system and devices, and portions for multi-user support were removed as they were not needed on MTS.  Later versions of IBM APL did run on MTS but are not available on the D6 distribution due to copyright reasons.&lt;/p&gt;
&lt;p&gt;APL symbols were supported using teletypewriters with a custom keyboard layout and typeballs that could display these symbols on paper.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2017/06/410px-APL-keybd2.svg.png&#34; alt=&#34;&#34;&gt;
&lt;super&gt;&lt;sub&gt;APL Keyboard layout from &lt;a href=&#34;https://en.wikipedia.org/wiki/File:APL-keybd2.svg&#34;&gt;Wikipedia&lt;/a&gt;. CC-SA 3.0&lt;/sub&gt;&lt;/super&gt;&lt;/p&gt;
&lt;p&gt;Not all users would have this special teletypwriter, so APL supports the standard keyboard and printer character set using transliterations for symbols. For example, the divide operator &lt;code&gt;÷&lt;/code&gt; is replaced with &lt;code&gt;/&lt;/code&gt; and the ceiling operator &lt;code&gt;⌈&lt;/code&gt;, which finds the maximum of its arguments, is replaced with either &lt;code&gt;$MA&lt;/code&gt; or &lt;code&gt;$CE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The hardware used by MTS for APL is not supported on Hercules so we will need to use these transliterations when running MTS under emulation.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Unlike other languages seen so far, we do need to set up APL before using it by installing it from the D5 tapes. The below method was adapted from work done by user halfmeg on the &lt;a href=&#34;https://groups.yahoo.com/neo/groups/H390-MTS/conversations/topics/731&#34;&gt;H390-MTS&lt;/a&gt; list.&lt;/p&gt;
&lt;p&gt;Start with a regular D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt;. Ensure that MTS is not running before following these steps.&lt;/p&gt;
&lt;p&gt;Get a copy of the &lt;a href=&#34;http://bitsavers.informatik.uni-stuttgart.de/bits/univOfMichigan/mts/d5.0.tar.gz&#34;&gt;D5 tapes&lt;/a&gt; from Bitsavers and extract into a temporary directory.&lt;/p&gt;
&lt;p&gt;Locate the files &lt;code&gt;d5.0t1.aws&lt;/code&gt; and &lt;code&gt;d5.0t2.aws&lt;/code&gt; under the extraction directory and copy these to the &lt;code&gt;Tapes&lt;/code&gt; directory under your MTS install&lt;/p&gt;
&lt;p&gt;Edit your &lt;code&gt;hercules.cnf&lt;/code&gt; and add these lines. These tape devices are unused in the stock D6.0 install; if you have already assigned these for your own use then change the device names here and in the instructions below.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Add D5 tapes needed to restore APL
018B   3420   Tapes/d5.0t1.aws   ro  # T90B, D5.0T1
018C   3420   Tapes/d5.0t2.aws   ro  # T90C, D5.0T2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The batch instructions to restore APL from these disks is available as a card deck from my &lt;a href=&#34;https://github.com/rupertl/mts-languages/blob/master/apl/install.txt&#34;&gt;github repo&lt;/a&gt; on MTS languages. Download that file and copy it to &lt;code&gt;Units/RDR1.txt&lt;/code&gt; under your Hercules install, replacing the existing file. Note that the whitespace in the first line is important, so clone the git repo or download the file as raw text.&lt;/p&gt;
&lt;p&gt;Start up MTS as normal, including HASP. When it is running, type &lt;code&gt;devinit c&lt;/code&gt; from the Hercules console to load the card deck. You should see the below printed on the MTS console if this worked.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00051 MTS **** Remove tape from T90B (6250 BPI)
00051 MTS **** Remove tape from T90C (6250 BPI)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output from the batch job can be found on the printer in Hercules file &lt;code&gt;Units/PTR2.txt&lt;/code&gt;. Examine it for any errors; you can ignore lines like &lt;code&gt;You restored files saved before FEB. 22, 1988&lt;/code&gt;. You should see that job extracted files from the tape and set permissions appropriately.&lt;/p&gt;
&lt;p&gt;Finally, test that it works by logging into a normal user account (eg ST01) and running&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$run *APL,par=sp,noball
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The APL start up message should appear. Type &lt;code&gt;)LIB 1&lt;/code&gt; and you should see this listing of library files:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; ADVANCEDE
 APLCOURSE
 CLASS
 NEWS
 PLOTFORMA
 TYPEDRILL
 WSFNS
 EIGENVALU
 BRFNS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Type &lt;code&gt;)OFF&lt;/code&gt; to exit APL.&lt;/p&gt;
&lt;p&gt;When you next shutdown MTS, you can comment out the two D5.0 tapes in &lt;code&gt;hercules.cnf&lt;/code&gt; to free up these devices for future use.&lt;/p&gt;
&lt;h2 id=&#34;running-a-program-using-apl&#34;&gt;Running a program using &lt;code&gt;*APL&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*APL&lt;/code&gt; is an interactive environment where you can enter expressions and program lines. To start APL, run &lt;code&gt;*APL&lt;/code&gt; with the parameters &lt;code&gt;sp&lt;/code&gt; (to print spaces after each operator) and &lt;code&gt;noball&lt;/code&gt; (to indicate we are not using the special APL typeball.&lt;/p&gt;
&lt;p&gt;APL prompts with six leading spaces. You can enter expressions and get results back immediately, aligned in column 1.&lt;/p&gt;
&lt;p&gt;System commands start with &lt;code&gt;)&lt;/code&gt;. &lt;code&gt;)SOURCE&lt;/code&gt; will read lines from a given text file and execute them. &lt;code&gt;)CONTINUE&lt;/code&gt; will save a copy of the current workspace to a binary file which will automatically be loaded next time you start APL. &lt;code&gt;)OFF&lt;/code&gt; will exit APL.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;As an example, here&amp;rsquo;s a simple program to print &amp;lsquo;Hello, world!&amp;rsquo; five times. This uses a simple loop - there&amp;rsquo;s probably a more concise way to do this.&lt;/p&gt;
&lt;p&gt;First, create a file called &lt;code&gt;hello.apl&lt;/code&gt; containing the following lines:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;HELLO
N=1
&amp;#39;Hello, world!&amp;#39;
N=N+1
$GO 2 * N $LE 5
&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then start APL and load the text file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *apl par=sp,noball
# Execution begins   16:30:56 
SAVED  16.30.28 05%27%17
        )SOURCE HELLO.APL
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After you enter the &lt;code&gt;)SOURCE&lt;/code&gt; command APL will read the file but will not prompt you it has completed. Press the &lt;code&gt;ATTN&lt;/code&gt; key to interrupt APL and return control to you. You can then enter &lt;code&gt;HELLO&lt;/code&gt; to run the loaded program:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        HELLO
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
        )OFF
  16.31.16 05%27%17 CONTINUE
     16.31.16 05%27%17
CONNECTED    0.00.19
CPU TIME     0.00.00
# Execution terminated   16:31:15  T=0.034 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the &lt;a href=&#34;https://try-mts.com/apl-language-features&#34;&gt;next post&lt;/a&gt; we&amp;rsquo;ll look at the APL language in more detail.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;IBM&amp;rsquo;s &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/apl/C20-1702-0_APL_360_Primer_1969.pdf&#34;&gt;APL\360 Primer&lt;/a&gt; is a great first read as it introduces the APL\360 system and APL language in a tutorial form. The &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/apl/360D-03.3.007_APL_360_Aug69.pdf&#34;&gt;APL\360 User&amp;rsquo;s Manual&lt;/a&gt; can then be consulted for more in-depth information.&lt;/p&gt;
&lt;p&gt;A classic introduction to APL is &amp;ldquo;APL 360: An Interactive Approach&amp;rdquo; by Gillman and Rose. A copy can be found at the &lt;a href=&#34;http://www.softwarepreservation.org/projects/apl/Books/GillmanAndRose/view&#34;&gt;Software Preservation Group&lt;/a&gt; of the Computer History Museum.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://hdl.handle.net/2027/mdp.39015026567589?urlappend=%3Bseq=319&#34;&gt;UM Computing Center Memo 382&lt;/a&gt; is a guide to the implementation of APL\360 on MTS. I recommend reading the printed copy of this memo from the above source as it includes the hand written APL symbols missing on the source copy.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PIL - Roman numerals</title>
      <link>https://try-mts.com/pil-roman-numerals/</link>
      <pubDate>Sun, 15 Oct 2017 03:06:39 +0000</pubDate>
      <guid>https://try-mts.com/pil-roman-numerals/</guid>
      <description>&lt;p&gt;In the final part of this &lt;a href=&#34;https://try-mts.com/pil-introduction/&#34;&gt;series&lt;/a&gt;, let&amp;rsquo;s create a real program in PIL.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;We will implement &lt;a href=&#34;http://rosettacode.org/wiki/Roman_numerals/Encode#BASIC&#34;&gt;arabic to roman&lt;/a&gt; number conversion from Rosetta Code.&lt;/p&gt;
&lt;p&gt;The algorithm we&amp;rsquo;re going to use is similar to the one used there for BASIC:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have a table of all distinct roman numbers ordered by size, including the -1 variants like IV. So roman(0) = &amp;ldquo;M&amp;rdquo;, roman(1) = &amp;ldquo;CM&amp;rdquo;, roman(2) = &amp;ldquo;D&amp;rdquo; etc.&lt;/li&gt;
&lt;li&gt;Have another table with the same indices for their arabic equivalents. arabic(0) = 1000, arabic(1) = 900, arabic(2) = 500 etc.&lt;/li&gt;
&lt;li&gt;Loop through each index. For each, if the input value is greater than the value of the arabic table at that value, accumulate the roman equivalent at the end of the output string and decrease the input value by the arabic amount. Keep doing this until the remaining input value is smaller than the arabic number.&lt;/li&gt;
&lt;li&gt;So for input 2900 the steps would be
&lt;ul&gt;
&lt;li&gt;index 0, output -&amp;gt; &amp;ldquo;M&amp;rdquo;, input -&amp;gt; 1900&lt;/li&gt;
&lt;li&gt;index 0, output -&amp;gt; &amp;ldquo;MM&amp;rdquo; , input -&amp;gt; 900&lt;/li&gt;
&lt;li&gt;index 1, output -&amp;gt; &amp;ldquo;MMCM&amp;rdquo;, input -&amp;gt; 0 and end&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;As PIL is an interpreted language I&amp;rsquo;ll show a lightly reformatted transcript of my session as I build up the program in separate parts (and make mistakes along the way). Let&amp;rsquo;s get started!&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *pil
# Execution begins   20:09:19
  PIL/2: Ready
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;the-tables&#34;&gt;The tables&lt;/h3&gt;
&lt;p&gt;First we need to set up the tables for arabic numbers in part 1. I will use the &lt;code&gt;number&lt;/code&gt; command so that PIL prompts me with line numbers followed by an underscore automatically.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 1, 0.01
&amp;amp;*1.0  _arabic(0) = 1000
&amp;amp;*1.01 _arabic(1) = 900
&amp;amp;*1.02 _arabic(2) = 500
&amp;amp;*1.03 _arabic(3) = 400
&amp;amp;*1.04 _arabic(4) = 100
&amp;amp;*1.05 _arabic(5) = 90
&amp;amp;*1.06 _arabic(6) = 50
&amp;amp;*1.07 _arabic(7) = 40
&amp;amp;*1.08 _arabic(8) = 10
&amp;amp;*1.09 _arabic(9) = 9
&amp;amp;*1.10 _arabic(10) = 5
&amp;amp;*1.11 _arabic(11) = 4
&amp;amp;*1.12 _arabic(12) = 1
&amp;amp;*1.13 _$unnumber
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;unnumber&lt;/code&gt; command exits numbered line prompting mode. It needs to be prefixed with &lt;code&gt;$&lt;/code&gt; to be executed immediately rather than be entered as part of the program.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s run that immediately so we can check it looks correct&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*do part 1
*type arabic
  arabic(0) =  1000.0
  arabic(1) =  900.0
  arabic(2) =  500.0
  arabic(3) =  400.0
  arabic(4) =  100.0
  arabic(5) =  90.0
  arabic(6) =  50.0
  arabic(7) =  40.0
  arabic(8) =  10.0
  arabic(9) =  9.0
  arabic(10) =  5.0
  arabic(11) =  4.0
  arabic(12) =  1.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can then do the same for the roman numbers.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 2, 0.01
&amp;amp;*2.0 _roman(0) = &amp;#34;M&amp;#34;
&amp;amp;*2.01 _roman(1) = &amp;#34;CM&amp;#34;
&amp;amp;*2.02 _roman(2) = &amp;#34;D&amp;#34;
&amp;amp;*2.03 _roman(3) = &amp;#34;CD&amp;#34;
&amp;amp;*2.04 _roman(4) = &amp;#34;C&amp;#34;
&amp;amp;*2.05 _roman(5) = &amp;#34;XC&amp;#34;
&amp;amp;*2.06 _roman(6) = &amp;#34;L&amp;#34;
&amp;amp;*2.07 _roman(7) = &amp;#34;XL&amp;#34;
&amp;amp;*2.08 _roman(8) = &amp;#34;X&amp;#34;
&amp;amp;*2.09 _roman(9) = &amp;#34;IX&amp;#34;
&amp;amp;*2.1 _roman(10) = &amp;#34;V&amp;#34;
&amp;amp;*2.11 _roman(11) = &amp;#34;IV&amp;#34;
&amp;amp;*2.12 _roman(12) = &amp;#34;I&amp;#34;
&amp;amp;*2.13 _$unnumber
*do part 2
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;the-main-loop&#34;&gt;The main loop&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s now make the main loop to convert the number. We&amp;rsquo;ll do it in three parts, first the loop over the indices. I put in some comments fir the function.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 5, 0.01
&amp;amp;*5.0 _* Main entry point to arabic -&amp;gt; roman converter
&amp;amp;*5.01 _* Input: a (arabic number to convert)
&amp;amp;*5.02 _* Output: r (roman number equivalent of a)
&amp;amp;*5.03 _for i = 0 to 12: do part 6
&amp;amp;*5.04 _done
&amp;amp;*5.05 _$unnumber
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, the loop for each arabic number. We can use a &lt;code&gt;for&lt;/code&gt; with a dummy variable and the &lt;code&gt;while&lt;/code&gt; controlling how often it is run.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 6, 0.01
&amp;amp;*6.0 _for j = 0 while a &amp;gt;= arabic(i): do part 7
&amp;amp;*6.01 _done
&amp;amp;*6.02 _$unnumber
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, in part 7 build up the roman number string and decrease the arabic number.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 7, 0.01
&amp;amp;*7.0 _r = r + roman(i)
&amp;amp;*7.01 _a = a - arabic(i)
&amp;amp;*7.02 _done
&amp;amp;*7.03 _$unnumber
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s see what these look like now.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*type part 5, part 6, part 7

  5.0    * Main entry point to arabic -&amp;gt; roman converter
  5.01   * Input: a (arabic number to convert)
  5.02   * Output: r (roman number equivalent of a)
  5.03   for i = 0 to 12: do part 6
  5.04   done


  6.0    for j = 0 while a &amp;gt;= arabic(i): do part 7
  6.01   done


  7.0    r = r + roman(i)
  7.01   a = a - arabic(i)
  7.02   done
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;trying-it-out&#34;&gt;Trying it out&lt;/h3&gt;
&lt;p&gt;We can set up the input number in &lt;code&gt;a&lt;/code&gt; then call part 5 to convert. The output should go into &lt;code&gt;r&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*a = 13
*do part 5
  Error at step 7.0: r = ?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ah, &lt;code&gt;r&lt;/code&gt; is not initialised so cannot be appended to. We can patch part 5 and try again.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*5.025 r = &amp;#34;&amp;#34;
*do part 5
*type r
  r = &amp;#34;XIII&amp;#34;
*type a
  a =  0.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Great! There is a side effect though, the input value in &lt;code&gt;a&lt;/code&gt; is wiped out as PIL does not have local variables.&lt;/p&gt;
&lt;p&gt;Thinking about it, we are relying on the tables being initialised when we run part 5. We should really make it stand-alone by calling part 1 and 2 first.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*5.026 do part 1
*5.027 do part 2
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;making-it-interactive&#34;&gt;Making it interactive&lt;/h3&gt;
&lt;p&gt;We should have a way to prompt for a number and then display the conversion.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 10, 0.01
&amp;amp;*10.0 _demand a
&amp;amp;*10.01 _do part 5
&amp;amp;*10.02 _type r
&amp;amp;*10.03 _$unnumber

*do part 10
&amp;amp; a = ? _1992
  r = &amp;#34;MCMXCII&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;unit-tests&#34;&gt;Unit tests!&lt;/h3&gt;
&lt;p&gt;It may be anachronistic, but we should have some unit tests to see if the conversion works. First let&amp;rsquo;s define a unit test handler in part 20 that takes the arabic number in &lt;code&gt;a&lt;/code&gt;, the expected result in &lt;code&gt;rExpected&lt;/code&gt; and then checks this matches.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 20, 0.01
&amp;amp;*20.0 _do part 5
&amp;amp;*20.01 _if r = rExpected, then type &amp;#34;OK&amp;#34;, r; else type &amp;#34;ERROR&amp;#39;, r, rExpected
  Error at step 20.01: SYMBOLIC NAME TOO LONG
&amp;amp;*20.02 _if r = re, then type &amp;#34;OK&amp;#34;, r; else type &amp;#34;ERROR&amp;#34;, r, re
&amp;amp;*20.03 _done
&amp;amp;*20.04 _$unnumber
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;rExpected&lt;/code&gt; is too long for a variable number so we use a shorter name instead, &lt;code&gt;re&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s test the tester out.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*re = &amp;#34;XLII&amp;#34;
*a = 42
*do part 20
  Error at step 20.01: SYMBOLIC NAME TOO LONG
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ah, the bad line is still there, so delete that and try again.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*delete step 20.01
*do part 20
  ERROR
  r = &amp;#34;&amp;#34;
  re = &amp;#34;XLII&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wait, that&amp;rsquo;s not right, why is the output in &lt;code&gt;r&lt;/code&gt; blank?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; *type r
  r = &amp;#34;&amp;#34;
 *type a
  a =  0.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Oh OK, &lt;code&gt;a&lt;/code&gt; is clobbered. Let&amp;rsquo;s set it up again.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*a = 42
*do part 5
*type r
  r = &amp;#34;XLII&amp;#34;
*do step 20.02
  OK
  r = &amp;#34;XLII&amp;#34;
*do step 20.02
  OK
  r = &amp;#34;XLII&amp;#34;
*type part 20

  20.0    do part 5
  20.02   if r = re, then type &amp;#34;OK&amp;#34;, r; else type &amp;#34;ERROR&amp;#34;, r, re
  20.03   done

*a = 42
*re = &amp;#34;XLII&amp;#34;
*do part 20
  OK
  r = &amp;#34;XLII&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That fixed it. Try the error case.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*a = 42
*re = &amp;#34;XXX&amp;#34;
*do part 20
  ERROR
  r = &amp;#34;XLII&amp;#34;
  re = &amp;#34;XXX&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With that done, set up the tests.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*number 21, 0.01
&amp;amp;*21.0 _a = 2009
&amp;amp;*21.01 _re = &amp;#34;MMIX&amp;#34;
&amp;amp;*21.02 _do part 20
&amp;amp;*21.03 _a = 1666
&amp;amp;*21.04 _re = &amp;#34;MDCLXVI&amp;#34;
&amp;amp;*21.05 _do part 20
&amp;amp;*21.06 _a = 3888
&amp;amp;*21.07 _re = &amp;#34;MMMDCCCLXXXVIII&amp;#34;
&amp;amp;*21.08 _do part 20
&amp;amp;*21.09 _done
&amp;amp;*21.1 _$unnumber
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And run them.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*do part 21
  OK
  r = &amp;#34;MMIX&amp;#34;
  OK
  r = &amp;#34;MDCLXVI&amp;#34;
  OK
  r = &amp;#34;MMMDCCCLXXXVIII&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All green. However we did not test all cases such as zero, negative numbers, non-integral numbers etc.&lt;/p&gt;
&lt;h3 id=&#34;save-and-load&#34;&gt;Save and load&lt;/h3&gt;
&lt;p&gt;To confirm the program is all done and we are not relying on anything in the environment, save it to disk, quit and come back into PIL and try re-running.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*create &amp;#34;roman.pil&amp;#34;
  FILE &amp;#34;ROMAN.PIL&amp;#34; IS CREATED
*save as &amp;#34;roman.pil&amp;#34;, all parts
  SAVE COMPLETED
*stop
# Execution terminated   18:51:16  T=0.279

# $run *pil
# Execution begins   18:51:37
  PIL/2: Ready
*load &amp;#34;roman.pil&amp;#34;
*do part 10
&amp;amp; a = ?  _42
  r = &amp;#34;XLII&amp;#34;
*do part 21
  OK
  r = &amp;#34;MMIX&amp;#34;
  OK
  r = &amp;#34;MDCLXVI&amp;#34;
  OK
  r = &amp;#34;MMMDCCCLXXXVIII&amp;#34;
*stop
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;the-complete-listing&#34;&gt;The complete listing&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*type all parts

  1.0    arabic(0) = 1000
  1.01   arabic(1) = 900
  1.02   arabic(2) = 500
  1.03   arabic(3) = 400
  1.04   arabic(4) = 100
  1.05   arabic(5) = 90
  1.06   arabic(6) = 50
  1.07   arabic(7) = 40
  1.08   arabic(8) = 10
  1.09   arabic(9) = 9
  1.1    arabic(10) = 5
  1.11   arabic(11) = 4
  1.12   arabic(12) = 1

  2.0    roman(0) = &amp;#34;M&amp;#34;
  2.01   roman(1) = &amp;#34;CM&amp;#34;
  2.02   roman(2) = &amp;#34;D&amp;#34;
  2.03   roman(3) = &amp;#34;CD&amp;#34;
  2.04   roman(4) = &amp;#34;C&amp;#34;
  2.05   roman(5) = &amp;#34;XC&amp;#34;
  2.06   roman(6) = &amp;#34;L&amp;#34;
  2.07   roman(7) = &amp;#34;XL&amp;#34;
  2.08   roman(8) = &amp;#34;X&amp;#34;
  2.09   roman(9) = &amp;#34;IX&amp;#34;
  2.1    roman(10) = &amp;#34;V&amp;#34;
  2.11   roman(11) = &amp;#34;IV&amp;#34;
  2.12   roman(12) = &amp;#34;I&amp;#34;

  5.0    * Main entry point to arabic -&amp;gt; roman converter
  5.01   * Input: a (arabic number to convert)
  5.02   * Output: r (roman number equivalent of a)
  5.025  r = &amp;#34;&amp;#34;
  5.026  do part 1
  5.027  do part 2
  5.03   for i = 0 to 12: do part 6
  5.04   done

  6.0    for j = 0 while a &amp;gt;= arabic(i): do part 7
  6.01   done

  7.0    r = r + roman(i)
  7.01   a = a - arabic(i)
  7.02   done

  10.0    demand a
  10.01   do part 5
  10.02   type r

  20.0    do part 5
  20.02   if r = re, then type &amp;#34;OK&amp;#34;, r; else type &amp;#34;ERROR&amp;#34;, r, re
  20.03   done

  21.0    a = 2009
  21.01   re = &amp;#34;MMIX&amp;#34;
  21.02   do part 20
  21.03   a = 1666
  21.04   re = &amp;#34;MDCLXVI&amp;#34;
  21.05   do part 20
  21.06   a = 3888
  21.07   re = &amp;#34;MMMDCCCLXXXVIII&amp;#34;
  21.08   do part 20
  21.09   done
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;JOSS is a simple but well designed language - it&amp;rsquo;s easy to pick up, has a carefully chosen set of features and does the job it&amp;rsquo;s supposed to do well. Compared to BASIC it seems much more intuitive as a simple language for non-specialists who want to get numeric calculations done quickly. The lack of functions and local variables, plus the heavily interactive nature of the language makes it harder to write larger programs, but given the first version was running in 1963 it&amp;rsquo;s quite an impressive feat of engineering.&lt;/p&gt;
&lt;p&gt;PIL, the version of JOSS implemented on MTS, improves the usability of the original language, eg by not requiring a period at the end of each statement. There is enough integration with the operating system to make it usable. It would be interesting to know what type of use it got at UM.&lt;/p&gt;
&lt;p&gt;Several languages were inspired by JOSS, including FOCAL on PDP-8s. It&amp;rsquo;s also one of the influences on MUMPS, which is still in use today.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PIL - Language features</title>
      <link>https://try-mts.com/pil-language-features/</link>
      <pubDate>Sun, 08 Oct 2017 08:54:19 +0000</pubDate>
      <guid>https://try-mts.com/pil-language-features/</guid>
      <description>&lt;p&gt;In the last &lt;a href=&#34;https://try-mts.com/pil-introduction&#34;&gt;post&lt;/a&gt; we saw some of the history of PIL and how to run it on MTS. We&amp;rsquo;ll now take a closer look at the features of PIL. Examples shown can be entered directly into PIL after starting it with &lt;code&gt;$run *pil&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;direct-mode&#34;&gt;Direct mode&lt;/h2&gt;
&lt;p&gt;PIL starts up in direct mode, where statements entered are immediately executed when you press &lt;code&gt;RETURN&lt;/code&gt;. &lt;code&gt;*&lt;/code&gt; is used by PIL as the prompt to enter input. You can use the &lt;code&gt;TYPE&lt;/code&gt; statement and simple arithmetic expressions to make PIL act as a calculator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type 22 * 2
  22 * 2 =  44.0
* type 2 ** 16
  2 ** 16 =  65536.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On MTS, PIL is case-insensitive for keywords and lines can optionally end with a period. Errors are immediately reported, usually starting with &lt;code&gt;Eh?&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* TYPE 2+2.
  2+2 =  4.0
* TYPE
  Eh? IMPROPERLY FORMED STATEMENT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the last character entered on the line is &lt;code&gt;-&lt;/code&gt; then input will continue on the next line before it is executed. The prompt will change to &lt;code&gt;&amp;amp;&lt;/code&gt; to show this continuation. &lt;code&gt;*&lt;/code&gt; at the start of line can be used to make comments.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; * type 1 + 2 + 3 +-
 &amp;amp; 5 + 6
   1 + 2 + 3 +5 + 6 =  17.0
 * * comment here
 *
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;variables-and-types&#34;&gt;Variables and types&lt;/h2&gt;
&lt;p&gt;Variables can be introduced by the optional keyword &lt;code&gt;SET&lt;/code&gt; followed by a variable name and an assignment.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* set a = 2
* b = 3
* type a, b, a+b
  a =  2.0
  b =  3.0
  a+b =  5.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;PIL understands three types: numerical, which are stored as floating point values, character strings and Boolean values.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* a = 1 / 3
* b = &amp;#34;PIL&amp;#34;
* c = The True
* type a, b, c
  a =  0.3333333
  b = &amp;#34;PIL&amp;#34;
  c = The True
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Booleans constants are &amp;lsquo;The True&amp;rsquo; or &amp;lsquo;The False&amp;rsquo; - something I&amp;rsquo;ve not seen in any other language. Strings can be up to 255 characters long and can be entered with single or double quotes as delimiters. Floats have 7 digits of precision and can be entered with exponential notation. Volume 12 mentions type 9.999999e64 as the maximum value but on the version I&amp;rsquo;m running it seems 62 is the maximum exponent.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type 9.999999e62
  9.999999e62 =  9.999999E+62
* type 9.999999e63
  Eh? EXPONENT OUT OF RANGE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Variable names are up to 8 characters long, are case sensitive and are distinguished from keywords, as this silly example shows.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* set set = 1
* set SET = 2
* type set, SET
  set =  1.0
  SET =  2.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Arrays are allowed with any number of dimensions, though once set the number of dimensions cannot be changed.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* x(1, -2) = 3
* x(3, 44.0) = 4
* type x
  x(1,-2) =  3.0
  x(3,44) =  4.0
* x(3,4,5) = 42
  Eh? ??-
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;expressions&#34;&gt;Expressions&lt;/h2&gt;
&lt;p&gt;Arithmetic expressions work mostly as expected. The absolute value can be taken by surrounding an expression with &lt;code&gt;|&lt;/code&gt;; exponentiation is done with &lt;code&gt;**&lt;/code&gt;. Functions for arithmetic operations such as square root, cosine, log etc generally have a short and long form and do not need parentheses unless needed to resolve ambiguities.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type 1 + |-41|
  1 + |-41| =  42.0
* type the square root of 9
  the square root of 9 =  3.0
* type sqrt of 16
  sqrt of 16 =  4.0
* type sqrt of 25+1
  sqrt of 25+1 =  6.0
* type sqrt of (25+1)
  sqrt of (25+1) =  5.09902
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are functions for min/max, random numbers and extracting parts of a number, as well as special functions to get time (in 300ths of seconds since midnight), date, cpu/elapsed time and storage used.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type the min of (1,2,3)
  the min of (1,2,3) =  1.0
* type the time, the date, the elapsed time, the cpu time
  the time =  1.963908E+07
  the date =  17133.0
  the elapsed time =  7.819461E+07
  the cpu time =  464.0
* type the total size, the size
  the total size =  188.0
  the size =  165.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Boolean functions are similar to other languages, with &lt;code&gt;#&lt;/code&gt; standing in for logical or.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type 2 &amp;gt;= 3
  2 &amp;gt;= 3 = The False
* type 2 $lt 3
  2 $lt 3 = The True
* type the true # the false
  the true # the false = The True
* type the true &amp;amp; the false
  the true &amp;amp; the false = The False
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Character expressions include length, case conversion, comparison and extraction.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type the l of &amp;#34;abc&amp;#34;
  the l of &amp;#34;abc&amp;#34; =  3.0
* type the upper of &amp;#34;abC&amp;#34;
  the upper of &amp;#34;abC&amp;#34; = &amp;#34;ABC&amp;#34;
* type the first 3 characters of &amp;#34;abcde&amp;#34;
  the first 3 characters of &amp;#34;abcde&amp;#34; = &amp;#34;abc&amp;#34;
* type 2 $fc &amp;#34;abcde&amp;#34; + &amp;#34;X&amp;#34; + 1 $lc &amp;#34;abcde&amp;#34;
  2 $fc &amp;#34;abcde&amp;#34; + &amp;#34;X&amp;#34; + 1 $lc &amp;#34;abcde&amp;#34; = &amp;#34;abXe&amp;#34;
* type &amp;#34;aaa&amp;#34; &amp;gt; &amp;#34;AAA&amp;#34;
  &amp;#34;aaa&amp;#34; &amp;gt; &amp;#34;AAA&amp;#34; = The False
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, the type of an expression can be found and run time evaluation performed.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type the mode of 42, the mode of the true, the mode of &amp;#34;abc&amp;#34;
  the mode of 42 =  1.0
  the mode of the true =  2.0
  the mode of &amp;#34;abc&amp;#34; =  3.0
* type the value of &amp;#34;2*21&amp;#34;
  the value of &amp;#34;2*21&amp;#34; =  42.0
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;control-flow&#34;&gt;Control flow&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s an &lt;code&gt;IF&lt;/code&gt; statement with an optional &lt;code&gt;ELSE&lt;/code&gt; clause. The &lt;code&gt;IF&lt;/code&gt; and &lt;code&gt;ELSE&lt;/code&gt; can be omitted but the punctuation is required.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* if 1 &amp;lt; 2, then type &amp;#39;yes&amp;#39;; else type &amp;#39;no&amp;#39;
  yes
* if 1 &amp;gt; 2, type &amp;#39;yes&amp;#39;; type &amp;#39;no&amp;#39;
  no
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For loops have a range or an increment and an optional clause to terminate.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* for i = 1 to 5: type i ** 2
  i ** 2 =  1.0
  i ** 2 =  4.0
  i ** 2 =  9.0
  i ** 2 =  16.0
  i ** 2 =  25.0
* for i = 1 by 2 to 10: type i ** 2
  i ** 2 =  1.0
  i ** 2 =  9.0
  i ** 2 =  25.0
  i ** 2 =  49.0
  i ** 2 =  81.0
* for i = 1 by 2 while i &amp;lt; 20: type i
  i =  1.0
  i =  3.0
  i =  5.0
  i =  7.0
  i =  9.0
  i =  11.0
  i =  13.0
  i =  15.0
  i =  17.0
  i =  19.0
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;indirect-mode&#34;&gt;Indirect mode&lt;/h2&gt;
&lt;p&gt;Lines entered that start with a number are treated as stored instructions, broken down by part and step, that can be run later. Here we define a program in part 1 consisting of four steps.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* 1.0 i = 5
* 1.1 type i
* 1.2 i = i * i
* 1.3 type i
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can then be run with &lt;code&gt;DO&lt;/code&gt;, which will execute all steps in a part.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* do part 1
  i =  5.0
  i =  25.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Variables set in a program remain after execution is completed and it is possible to run a single step at a time.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type i
  i =  25.0
* do step 1.2
* do step 1.3
  i =  625.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Steps can call other parts with &lt;code&gt;DO&lt;/code&gt;: execution will resume after the part is finished. It&amp;rsquo;s also possible to transfer execution with &lt;code&gt;TO&lt;/code&gt; which will not return. &lt;code&gt;DONE&lt;/code&gt; will return from the current part.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* type part 2, part 3
 
  2.0    i = 3
  2.1    do step 3
  2.2    i = 5
  2.3    do step 3
  2.4    i = 7
  2.5    to step 3
  2.6    type &amp;#39;not reached&amp;#39;
 
 
  3.0    type i
  3.1    done
  3.2    type &amp;#39;also not reached&amp;#39;
 
* do part 2
  i =  3.0
  i =  5.0
  i =  7.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above also shows it is possible to list out programs with &lt;code&gt;TYPE PART&lt;/code&gt;. You can see all entered parts with &lt;code&gt;TYPE ALL PARTS&lt;/code&gt;. With &lt;code&gt;TYPE ALL STUFF&lt;/code&gt; you will see all variables and parts defined.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DELETE&lt;/code&gt; can be used to remove a step or a variable definition.&lt;/p&gt;
&lt;h2 id=&#34;io-and-system-access&#34;&gt;I/O and system access&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ve seen &lt;code&gt;TYPE&lt;/code&gt; used to display variables. You can prompt for a value to be entered with &lt;code&gt;DEMAND&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* delete part 1
* 1.0 demand i
* 1.1 i = i * 10
* 1.2 type i
* do part 1
 i = ?_ 3
  i =  30.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also a formatting facility for input and output that is covered in the manual.&lt;/p&gt;
&lt;p&gt;Programs can be saved to disk for future list with &lt;code&gt;SAVE&lt;/code&gt;. This takes a file name (which must already exist) and what to save. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;save as &amp;#39;x.pil&amp;#39;, all stuff
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will save all parts and variables to &lt;code&gt;x.pil&lt;/code&gt;. File format is plain text so it can be edited outside of PIL if needed. &lt;code&gt;LOAD &#39;x.pil&#39;&lt;/code&gt; will then load the file back into PIL.&lt;/p&gt;
&lt;p&gt;The implementation of PIL on MTS includes access to system facilities such as file creation and device access. For example, &lt;code&gt;CREATE &#39;x.pil&#39;&lt;/code&gt; will create a new file.&lt;/p&gt;
&lt;p&gt;In the next &lt;a href=&#34;https://try-mts.com/pil-roman-numerals/&#34;&gt;post&lt;/a&gt; we&amp;rsquo;ll see how to construct a larger PIL program.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol12-PIL2inMTS-Dec1974.pdf&#34;&gt;MTS volume 12&lt;/a&gt; has a complete reference to PIL as implemented on MTS.&lt;/p&gt;
&lt;p&gt;If you can find a copy of the book &amp;lsquo;History of Programming Languages&amp;rsquo;, edited by Richard Wexelblat, this has a great section about JOSS talking about its design principles.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PIL - Introduction</title>
      <link>https://try-mts.com/pil-introduction/</link>
      <pubDate>Sun, 01 Oct 2017 01:32:55 +0000</pubDate>
      <guid>https://try-mts.com/pil-introduction/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2017/01/pil.png&#34; alt=&#34;&#34;&gt;
&lt;super&gt;&lt;sub&gt;Screenshot of a teletypewriter running PIL, from &lt;a href=&#34;https://www.youtube.com/watch?v=zxS8S57etuk&#34;&gt;2 4 Using the Michigan Terminal System 6 01&lt;/a&gt;.&lt;/super&gt;&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;In this series of posts we&amp;rsquo;ll look at the Pittsburgh Interpretative Language, or PIL, a simple interpreted language that can be used to do calculations and build small programs interactively.&lt;/p&gt;
&lt;h2 id=&#34;the-pittsburgh-interpretative-language&#34;&gt;The Pittsburgh Interpretative Language&lt;/h2&gt;
&lt;p&gt;PIL was developed at the University of Pittsburgh for the System/360 in the late 60s. It was based on one of the first interpreted languages &lt;a href=&#34;https://en.wikipedia.org/wiki/JOSS&#34;&gt;JOSS&lt;/a&gt; that originated at the RAND Corporation in 1963. PIL improves on JOSS by providing improved debugging capabilities and error reporting.&lt;/p&gt;
&lt;p&gt;The design goals for PIL were, according to MTS Volume 12:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PIL is oriented toward problem-solving,  with program development and debugging facilities having highest priority.  For the beginning user,  PIL was designed to be clear, unambiguous, and hence, easily learned.  For the experienced programmer,  the language offers increased flexibility with statement structure and expanded capabilities for the solution of non-numeric problems. For the researcher, PIL reduces the amount of time and effort that must be expended in problem solving.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PIL can be used as a simple desktop calculator with variables&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* set a = 3
* set b = 4
* type the square root of (a*a + b*b)
the square root of (a*a + b*b) =  5.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It can also be used to build simple programs interactively that can then be run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* 1.01 demand a
* 1.02 demand b
* 1.03 type the square root of (a*a + b*b)
* do part 1
 a = ?_ 3
 b = ?_ 4
  the square root of (a*a + b*b) =  5.0
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;pil-on-mts&#34;&gt;PIL on MTS&lt;/h2&gt;
&lt;p&gt;PIL on MTS is based on the second version of PIL, PIL/2. It was modified to integrate well with MTS files and system services.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get this language running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;running-a-program-using-pil&#34;&gt;Running a program using &lt;code&gt;*PIL&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Running the command &lt;code&gt;*PIL&lt;/code&gt; on MTS will start the PIL interpreter. It is intended to be used in an interactive way where you enter commands and see the output directly. It can be used in batch mode by feeding commands into &lt;em&gt;source&lt;/em&gt; but this is not the intended mode of operation. Inside the interpreter, programs and data can be loaded and saved with the &lt;code&gt;load&lt;/code&gt; and &lt;code&gt;save as&lt;/code&gt; statements.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how to run a simple program to print &amp;lsquo;Hello, world!&amp;rsquo; five times using PIL.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *pil
# Execution begins   09:56:53 
  PIL/2: Ready
* for i = 1 to 5: type &amp;#34;Hello, world!&amp;#34;
  Hello, world!
  Hello, world!
  Hello, world!
  Hello, world!
  Hello, world!
* stop
# Execution terminated   09:59:06  T=0.004 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After starting PIL, enter the for statement at the &lt;code&gt;*&lt;/code&gt; prompt. Output is shown immediately after the command is entered. Type &lt;code&gt;stop&lt;/code&gt; to return to MTS.&lt;/p&gt;
&lt;p&gt;In the next &lt;a href=&#34;https://try-mts.com/pil-language-features/&#34;&gt;post&lt;/a&gt; we&amp;rsquo;ll look at the language in more detail.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol12-PIL2inMTS-Dec1974.pdf&#34;&gt;MTS volume 12&lt;/a&gt; has a tutorial and reference for the PIL language and describes how it is integrated with MTS.&lt;/p&gt;
&lt;p&gt;The video pictured at the top of this post, &lt;a href=&#34;https://www.youtube.com/watch?v=zxS8S57etuk&#34;&gt;2 4 Using the Michigan Terminal System 6 01&lt;/a&gt;, shows simple operation of PIL on a teletypewriter, starting from 21 minutes into the video.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.computer.org/csdl/proceedings/afips/1968/5072/00/50720001.pdf&#34;&gt;The Pitt time-sharing system for the IBM system 360: Two year&amp;rsquo;s experience&lt;/a&gt;. describes the operation of PIL at the University of Pittsburgh.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.rand.org/content/dam/rand/pubs/research_memoranda/2006/RM5058.pdf&#34;&gt;JOSS: Introduction To A Helpful Assistant&lt;/a&gt; is a very readable paper from the RAND Corporation on the language that inspired PIL.&lt;/p&gt;
&lt;p&gt;The source code and implementation notes for PIL can be found in component 566 on tape 6.0T2 in the D6.0 distribution.&lt;/p&gt;
&lt;p&gt;Source code for the hello world program can be found on &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>RATFOR &amp; FLECS - Emirp primes</title>
      <link>https://try-mts.com/ratfor-flecs-emirp-primes/</link>
      <pubDate>Sun, 15 Jan 2017 00:34:40 +0000</pubDate>
      <guid>https://try-mts.com/ratfor-flecs-emirp-primes/</guid>
      <description>&lt;p&gt;For the final post in this series, let&amp;rsquo;s write a real program in RATFOR and FLECS and see how they compare with the original FORTRAN. We&amp;rsquo;ll be implementing the reverse-primes emirp program &lt;a href=&#34;https://try-mts.com/fortran-calculating-emirps/&#34;&gt;we did before&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;flecs-version&#34;&gt;FLECS version&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C     FLECS PROGRAM TO DISPLAY EMIRPS
C
C     *** TEST IF A NUMBER IS PRIME ***
      LOGICAL FUNCTION PRIME(N)
      INTEGER N
C     DEAL WITH NUMBERS &amp;lt;= 3
      IF (N .LE. 1) GOTO 200
      IF (N .EQ. 2 .OR. N .EQ. 3) GOTO 100
C     CHECK IF DIVISIBLE BY 2 OR 3
      IF (MOD(N,2) .EQ. 0) GOTO 200
      IF (MOD(N,3) .EQ. 0) GOTO 200
C     SEE IF DIVISIBLE BY 5, 7, ..., UP TO APPROX SQRT(N)
      DO (I=5,999999,2)
      IF (I*I .GT. N) GOTO 100
      IF (MOD(N,I) .EQ. 0) GOTO 200
      FIN
 100  PRIME = .TRUE.
      RETURN
 200  PRIME = .FALSE.
      RETURN
      END
C
C     *** REVERSE AN INTEGER&amp;#39;S DIGITS ***
      INTEGER FUNCTION REVRSE(N)
      INTEGER N
      INTEGER M,R
C     M IS COPY OF N FROM WHICH WE TAKE DIGITS
C     R IS REVERSED DIGITS
      M = N
      R = 0
C     LOOP UNTIL NO MORE DIGITS
      UNTIL (M .LT. 1)
C     TAKE LAST DIGIT FROM M AND APPEND TO R
      R = R * 10
      R = R + MOD(M, 10)
      M = M / 10
      FIN
      REVRSE = R
      RETURN
      END
C
C     *** TEST IF AN INTEGER IS AN EMIRP ***
      LOGICAL FUNCTION EMIRP(N)
      INTEGER N
C     EXTERNAL FUNCTIONS
      INTEGER REVRSE
      LOGICAL PRIME
C     R CONTAINS REVERSED DIGITS OF N
      INTEGER R
      R = REVRSE(N)
C     N AND R MUST BOTH BE PRIME AND NOT THE SAME VALUE
      IF (N .NE. R)
      IF (PRIME(N))
      IF (PRIME(R))
      EMIRP = .TRUE.
      RETURN
      FIN
      FIN
      FIN
      EMIRP = .FALSE.
      RETURN
      END
C
C     *** DISPLAY AN INTEGER ***
      SUBROUTINE SHOW(N)
      INTEGER N
      WRITE(6,50) N
 50   FORMAT(I10)
      RETURN
      END
C
C
C     *** MAIN ENTRY POINT ***
C     I IS COUNT OF EMIRPS FOUND
C     N IS NUMBER TO TEST
C     EXTERNAL FUNCTION
      LOGICAL EMIRP
      INTEGER I,N
      TEST-1
      TEST-2
      TEST-3
      STOP
C
C     *** SHOW FIRST 20 EMIRPS ***
      TO TEST-1
      N = 0
      I = 0
      WHILE (I .LT. 20)
      N = N + 1
      IF (EMIRP(N))
      CALL SHOW(N)
      I = I + 1
      FIN
      FIN
      FIN
C
C     *** SHOW EMIRPS BETWEEN 7,700 AND 8,000 ***
      TO TEST-2
      DO (N=7700,8000)
      IF (EMIRP(N)) CALL SHOW(N)
      FIN
      FIN
C
C     *** SHOW 10,000TH EMIRP ***
      TO TEST-3
      N = 0
      DO (I=1,10000)
      REPEAT UNTIL (EMIRP(N)) N = N + 1
      FIN
      CALL SHOW(N)
      FIN
C
      END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Apart from the &lt;code&gt;FORMAT&lt;/code&gt; specification and the &lt;code&gt;PRIME&lt;/code&gt; function we&amp;rsquo;ve eliminated all line numbers. &lt;code&gt;PRIME&lt;/code&gt; could be written without line numbers but with the multiple paths out of the function that would need their own &lt;code&gt;RETURN&lt;/code&gt; I think it&amp;rsquo;s better this way.&lt;/p&gt;
&lt;p&gt;The internal procedures come in handy, eliminating the need for subroutines for &lt;code&gt;TEST1-3&lt;/code&gt;, though this does make &lt;code&gt;N&lt;/code&gt; and &lt;code&gt;I&lt;/code&gt; global which makes me a little uneasy if this was a larger program.&lt;/p&gt;
&lt;p&gt;We use the block structure often, with &lt;code&gt;UNTIL&lt;/code&gt;, &lt;code&gt;WHILE&lt;/code&gt; and &lt;code&gt;REPEAT ... UNTIL&lt;/code&gt;; this simplifies code, though without indentation it&amp;rsquo;s a little hard to follow; the output of the preprocessor is useful here to show what it thinks the indentation should be, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  86           TO TEST-1
  87           .  N = 0
  88           .  I = 0
  89           .  WHILE (I .LT. 20)
  90           .  .  N = N + 1
  91           .  .  IF (EMIRP(N))
  92           .  .  .  CALL SHOW(N)
  93           .  .  .  I = I + 1
  94           .  .  ...FIN
  95           .  ...FIN
  96           ...FIN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The compiler diagnostics also helped a lot with catching errors with missing FINs.&lt;/p&gt;
&lt;h2 id=&#34;ratfor&#34;&gt;RATFOR&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s try writing the RATFOR version.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;######################################################################
# Ratfor program to display emirps
######################################################################

######### Test if a number is prime #########
logical function prime(n)
    integer n  # Number to test

    # Deal with numbers &amp;lt;= 3
    if (n &amp;lt; 1) goto 200
    if (n == 2 | n == 3) goto 100

    # Check if divisible by 2 or 3
    if (mod(n,2) == 0) goto 200
    if (mod(n,3) == 0) goto 200
      
    # See if divisible by 5, 7, ..., up to approx sqrt(n)
    for (i = 5; i &amp;lt; 1000000; i = i + 2) {
        if (I*I &amp;gt; n) goto 100
        if (mod(n,i) == 0) goto 200
    }

 100  prime = .true.
      return
 200  prime = .false.
      return
end

######### Reverse an integer&amp;#39;s digits #########
integer function revrse(n)
    integer n  # Number to reverse
    integer m  # Copy of n from which we take digits
    integer r  # Reversed digits
    m = n
    r = 0
    while (m &amp;gt;= 1) {
        # Take last digit from m and append to r
        r = r * 10
        r = r + mod(m, 10)
        m = m / 10
    }
    revrse = r
    return
end

######### Test if an integer is an emirp #########
logical function emirp(n)
    integer n       # Number to test
    integer revrse  # External function
    logical prime   # External function
    integer r       # Reversed digits of n
    r = revrse(n)
    emirp = .false.
    # n and r must both be prime and not the same value
    if (n .ne. r &amp;amp; prime(n) &amp;amp; prime(r)) {
        emirp = .true.
    }
    return
end

######### Display an integer #########
subroutine show(n)
    integer n
    write(6,50) n
50  format(i10)
    return
end

######### Show first 20 emirps #########
subroutine test1
    logical emirp   # External function
    integer i       # Count of emirps found
    integer n       # Number to test
    n = 0
    for (i = 1; i &amp;lt;= 20; i = i + 1) {
        repeat {
            n = n + 1
        } until (emirp(n))
        call show(n)
    }
    return
end

######### Show emirps between 7,700 and 8,000 #########
subroutine test2
    logical emirp   # External function
    integer n       # Number to test
    for (n = 7700; n &amp;lt;= 8000; n = n + 1) {
        if (emirp(n)) {
            call show(n)
        }
    }
    return
end

######### Show 10,000th emirp #########
subroutine test3
    logical emirp   # External function
    integer i       # Count of emirps found
    integer n       # Number to test
    n = 0
    for (i = 1; i &amp;lt;= 10000; i = i + 1) {
        repeat {
            n = n + 1
        } until (emirp(n))
    }
    call show(n)
    return
end

######### Main entry point #########
call test1
call test2
call test3
stop
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I feel right at home with the braces and the C style &lt;code&gt;for&lt;/code&gt; loops, though I miss the increment operator &lt;code&gt;++&lt;/code&gt;. &lt;code&gt;prime&lt;/code&gt; would be much better if I could just &lt;code&gt;return (.true.)&lt;/code&gt; but that does not work on the version of RATFOR on MTS so we keep the line numbers and &lt;code&gt;goto&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;With the above, plus the free form input (which was supported on MTS FORTRAN anyway) and the operators like &lt;code&gt;&amp;lt;&lt;/code&gt; it was easy to write. However, I got precisely zero diagnostics from the RATFOR preprocessor, with all my typos caught by the FORTRAN compiler, from which I&amp;rsquo;d have to find the problem in the original source. Easy enough in a small program but would be painful in larger ones.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;RATFOR and FLECS both make writing FORTRAN easier and more pleasant at the cost of an extra step in the development process, and I found both succeed at that. RATFOR is clearer and easier to get started with (especially coming from a C background today); the implementation is almost aggressively simple, as the authors admit in their paper, and I wonder how well it would scale for writing larger programs. FLECS has a more robust implementation but a more diffuse design, such as two versions of &lt;code&gt;switch&lt;/code&gt;; features like printing a neatly indented output would certainly help on MTS or its contemporaries but the language lacks the cosmetic features that make RATFOR easier to read.&lt;/p&gt;
&lt;p&gt;Neither are much used today; FORTRAN 77 and beyond took some of these ideas and built them into the core language. The idea of translating a richer language into a widely used but less expressive language is still alive though: think of Coffeescript or Typescript producing Javascript.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for these programs can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>RATFOR &amp; FLECS - Language Features</title>
      <link>https://try-mts.com/ratfor-flecs-language-features/</link>
      <pubDate>Sun, 30 Oct 2016 00:46:24 +0000</pubDate>
      <guid>https://try-mts.com/ratfor-flecs-language-features/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2016/10/fortran-meeting.png&#34; alt=&#34;FORTRAN meeting&#34;&gt;
&lt;super&gt;&lt;sub&gt;From the UM Computing Center Newsletter, Volume 5 Number 14, 24 September 1975, via Google Books. Proposal 2) seems to indicate a different preprocessor was being considered for UM as well as FLECS, I wonder if this was RATFOR or something else?&lt;/sub&gt;&lt;/super&gt;&lt;/p&gt;
&lt;p&gt;Hi and welcome back. Today let&amp;rsquo;s continue our exploration of RATFOR and FLECS by comparing the language features they add to vanilla FORTRAN. The quotes below are from the RATFOR paper and FLECS manual, links to which are provided at the end of this post. Code samples for FORTRAN and FLECS are shown in upper case, RATFOR in lower case.&lt;/p&gt;
&lt;h2 id=&#34;design&#34;&gt;Design&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;RATFOR attempts to retain the merits of FORTRAN (universality, portability, efficiency) while hiding the worst FORTRAN inadequacies. The language &lt;em&gt;is&lt;/em&gt; FORTRAN except for two aspects - [control flow and syntactic sugar] &amp;hellip; Throughout, the design principle which has determined what should be in RATFOR and what should not has been &lt;strong&gt;RATFOR doesn’t know any FORTRAN&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RATFOR focuses on control flow - if statements, blocks, looping - and cosmetics such as free form input, comments and other features that make FORTRAN more pleasant to write. By not knowing any FORTRAN, the design limits what features can be made available but also keeps it simple to implement and reduces the temptation to change FORTRAN into a different language altogether.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FLECS is a language extension of FORTRAN which has additional control mechanisms . These mechanisms make it easier to write FORTRAN by eliminating much of the clerical detail associated with constructing FORTRAN programs. FLECS is also easier to read and comprehend than FORTRAN.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;FLECS also tries ti improve FORTRAN&amp;rsquo;s control statements, taking ideas from several different languages including Pascal and Lisp. It has less cosmetic additions than RATFOR but adds the concept of internal procedures and includes features in the translator that help the programmer see the structure of their program.&lt;/p&gt;
&lt;h2 id=&#34;structure&#34;&gt;Structure&lt;/h2&gt;
&lt;p&gt;RATFOR allows blocks of statements to be introduced within braces where FORTRAN would only allow a single statement. The fixed column format in classic FORTRAN is relaxed so any indentation is allowed. Multiple statements can appear on the same line if they are separated by semicolons.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if (x &amp;gt; 100) {
   call error(x)
   err = 1; return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;FLECS also has blocks which extend from the start of a control statement to the keyword &lt;code&gt;FIN&lt;/code&gt;. It retains the fixed formatting of FORTRAN but prints a nicely indented view of the program when translating. So the example above would be entered as this in FLECS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      IF (X .GT. 100)
      CALL ERROR(X)
      ERR = 1
      FIN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and the translator would print&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;IF (X .GT. 100)
.  CALL ERROR(X)
.  ERR = 1
...FIN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is useful when entering programs via cards where it is difficult to get indentation right.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to have a single statement after a control structure in which case the &lt;code&gt;FIN&lt;/code&gt; is not needed:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;IF (X .GT. 100) CALL ERROR(X)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;RATFOR comments are introduced with &lt;code&gt;#&lt;/code&gt; and apply from that point to the end of the line, less restrictive than &lt;code&gt;C&lt;/code&gt; in FORTRAN and FLECS which must be in the first column.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%&lt;/code&gt; will stop RATFOR processing the rest of the line, passing it through to FORTRAN directly. FLECS will look for a FLECS statement in column 7 and if found will translate the line; if not found it will pass through the whole line to FORTRAN.&lt;/p&gt;
&lt;h2 id=&#34;textual-substitution&#34;&gt;Textual substitution&lt;/h2&gt;
&lt;p&gt;RATFOR allows constants to be set with &lt;code&gt;define SYMBOL VALUE&lt;/code&gt;; any use of &lt;code&gt;SYMBOL&lt;/code&gt; in the RATFOR program will be replaced with &lt;code&gt;VALUE&lt;/code&gt; in the generated FORTRAN program.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;include FILE&lt;/code&gt; will insert a copy of &lt;code&gt;FILE&lt;/code&gt; at that point in the program, just like C&amp;rsquo;s &lt;code&gt;#include&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;operators&#34;&gt;Operators&lt;/h2&gt;
&lt;p&gt;RATFOR allows the now-familiar symbols &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt; etc to be used instead of &lt;code&gt;.LT.&lt;/code&gt;, &lt;code&gt;.LE.&lt;/code&gt;, &lt;code&gt;.NE.&lt;/code&gt;, &lt;code&gt;.OR.&lt;/code&gt; etc. FLECS retains the FORTRAN operators.&lt;/p&gt;
&lt;h2 id=&#34;strings&#34;&gt;Strings&lt;/h2&gt;
&lt;p&gt;Text in RATFOR programs in single or double quotes is converted to FORTRAN nH strings. Backslash escapes the next character. FLECS keeps FORTRAN strings.&lt;/p&gt;
&lt;h2 id=&#34;conditionals&#34;&gt;Conditionals&lt;/h2&gt;
&lt;p&gt;FORTRAN has a simple &lt;code&gt;iF&lt;/code&gt; statement where only one statement can be executed if the condition is true. RATFOR extends this by allowing &lt;code&gt;else&lt;/code&gt; and nested ifs. An else clause is attached to the nearest if.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if (x &amp;gt; 0) {
  if (x &amp;gt; 10)
    write(6, 1) x
  else
    write(6, 2) x
else
  weite(6, 3)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;FLECS has &lt;code&gt;IF&lt;/code&gt; and for negative tests &lt;code&gt;UNLESS&lt;/code&gt;. It also has &lt;code&gt;WHEN&lt;/code&gt; &amp;hellip; &lt;code&gt;ELSE&lt;/code&gt; for a single positive and negative test.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;switch&lt;/code&gt; statement added in RATFOR looks like C but does not have &lt;code&gt;break&lt;/code&gt;; the switch is exited after each &lt;code&gt;case&lt;/code&gt; or &lt;code&gt;default&lt;/code&gt; is executed. FLECS&amp;rsquo;s equivalent is &lt;code&gt;SELECT&lt;/code&gt;, so comparing the two:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch (x) {
  case 1: y=3
  case 2, 3: y=5
  default y=0
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      SELECT (X)
      (1) Y=3
      (2) Y=5
      (3) Y=5
      (OTHERWISE) Y=0
      FIN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;FLECS has &lt;code&gt;CONDITIONAL&lt;/code&gt; which looks a lot like LISP&amp;rsquo;s &lt;code&gt;cond&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      CONDITIONAL
      (X.LT.-5.0)  U = U+W
      (X.LE.1.0)   U = U+W+Z
      (X.LE.10.5)  U = U-Z
      (OTHERWISE)  U = 0
      FIN
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;looping&#34;&gt;Looping&lt;/h2&gt;
&lt;p&gt;The FORTRAN &lt;code&gt;DO&lt;/code&gt; loop has to have a line number marking the point where the loop will restart:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      DO 10 i = 1, n
      x(i) = 0.0
      y(i) = 0.0
      z(i) = 0.0
 10   CONTINUE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;RATFOR replaces this with a block:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;do i = 1, n {
  x(i) = 0.0
  y(i) = 0.0
  z(i) = 0.0
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It also allows &lt;code&gt;break&lt;/code&gt; to exit a loop early and &lt;code&gt;next&lt;/code&gt; to restart the loop like C&amp;rsquo;s &lt;code&gt;continue&lt;/code&gt;. It can be followed by an integer to say how many levels to apply, so &lt;code&gt;break 2&lt;/code&gt; would move out of a two level &lt;code&gt;do&lt;/code&gt; statement immediately.&lt;/p&gt;
&lt;p&gt;RATFOR also adds a &lt;code&gt;while&lt;/code&gt; and &lt;code&gt;for&lt;/code&gt; statement that look like C&amp;rsquo;s - these allow immediate exit from the statement if the condition is true on entry, unlike in FORTRAN &lt;code&gt;DO&lt;/code&gt; where the statement is always executed at least once (in the IBM implementation at least) and the conditional is tested at the end of the statement. A version of C&amp;rsquo;s &lt;code&gt;do&lt;/code&gt; &amp;hellip; &lt;code&gt;while&lt;/code&gt; is provided as &lt;code&gt;repeat&lt;/code&gt; &amp;hellip; &lt;code&gt;until&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The FLECS equivalent for the above &lt;code&gt;do&lt;/code&gt; loop would be:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      DO (I = 1, N)
      X(I) = 0.0
      Y(I) = 0.0
      Z(I) = 0.0
      FIN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;FLEC&amp;rsquo;s &lt;code&gt;WHILE&lt;/code&gt; construct is similar to RATFOR&amp;rsquo;s, with the conditional tested before the loop starts. By using &lt;code&gt;REPEAT WHILE&lt;/code&gt; the body of the loop is executed at least once and the test made at the end of the loop. &lt;code&gt;UNTIL&lt;/code&gt; can be used instead of &lt;code&gt;WHILE&lt;/code&gt; in both cases to indicate that the loop ends when the conditional becomes true&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      X = 0
      UNTIL (X.EQ.5)
      X = X + 1
      FIN
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;return&#34;&gt;Return&lt;/h2&gt;
&lt;p&gt;To return a value from a function in FORTRAN and FLECS you must assign a value to the name of the function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;INTEGER FUNCTION DECREMENT(I)
INTEGER I
DECREMENT = I - 1
RETURN
END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the RATFOR paper it says you can give &lt;code&gt;return&lt;/code&gt; a value:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;integer function decrement(i)
integer i
return (i-1)
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, note this is not supported in the version supplied with MTS - it will just pass through such a &lt;code&gt;return&lt;/code&gt; statement causing an error from the FORTRAN compiler.&lt;/p&gt;
&lt;h2 id=&#34;internal-procedures&#34;&gt;Internal procedures&lt;/h2&gt;
&lt;p&gt;FLECS allows a group of statements to be defined as a procedure with &lt;code&gt;TO&lt;/code&gt; which can then be called by giving its name. No parameters are passed - it uses global variables to communicate. The below example will print 5.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      INTEGER X
      X = 1
      INCREMENT-IT
      DOUBLE-AND-INCREMENT
      WRITE(6,50) X
      STOP
 50   FORMAT(I10) 
      TO INCREMENT-IT X = X + 1
      TO DOUBLE-AND-INCREMENT
      X = X * 2
      INCREMENT
      FIN
      END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Procedure names must include at least one hyphen and recursion is not allowed.&lt;/p&gt;
&lt;h2 id=&#34;operation&#34;&gt;Operation&lt;/h2&gt;
&lt;p&gt;RATFOR runs as a simple translator, taking a RATFOR input file and producing a FORTRAN output file that must then be fed to the FORTRAN compiler. FLECS, as modified at UM, will both translate and call the FORTRAN compiler, producing machine code output that can be run directly.&lt;/p&gt;
&lt;h2 id=&#34;error-handling&#34;&gt;Error handling&lt;/h2&gt;
&lt;p&gt;RATFOR will catch some errors, such as missing closing braces, but will otherwise delegate problems with the program to the FORTRAN compiler to catch, as it does not understand FORTRAN syntax. This could be difficult to trace back to the source of the error as the FORTRAN compiler would show the error in the generated FORTRAN, not the RATFOR original.&lt;/p&gt;
&lt;p&gt;FLECS will find syntax errors and remove them from the program, allowing translation to continue at the cost of possibly causing further errors; it will not move on to compilation in this case.&lt;/p&gt;
&lt;h2 id=&#34;implementation&#34;&gt;Implementation&lt;/h2&gt;
&lt;p&gt;Not surprisingly given its authors&amp;rsquo; roots, RATFOR was originally written in around 1000 lines of C using yacc. The authors say it took less than a week to implement. As C was not widely available in the mid 70&amp;rsquo;s, a version of RATFOR in RATFOR was produced that would generate around 2500 lines of basic FORTRAN so it could be used anywhere.&lt;/p&gt;
&lt;p&gt;The FLECS implementation comes in at around 2200 lines of FLECS and took around six months to develop according to comments in the source code.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&#34;http://striky.ece.jhu.edu/~sasha/SOFTWARE/FORTRAN90/ratfor.pdf&#34;&gt;Kernighan&amp;rsquo;s RATFOR paper&lt;/a&gt; or the FLECS User&amp;rsquo;s Manual (in component 673/22; I&amp;rsquo;ve uploaded a copy &lt;a href=&#34;https://try-mts.com/files/flecs-users-manual.txt&#34;&gt;here&lt;/a&gt;) for more information on the languages.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>RATFOR &amp; FLECS - Introduction</title>
      <link>https://try-mts.com/ratfor-flecs-introduction/</link>
      <pubDate>Sun, 24 Jan 2016 02:57:16 +0000</pubDate>
      <guid>https://try-mts.com/ratfor-flecs-introduction/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Most programmers will agree that FORTRAN is an unpleasant language to
program in, yet there are many occasions when they are forced to use
it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;From the introduction to &amp;lsquo;RATFOR — A Preprocessor for a Rational FORTRAN&amp;rsquo; by Brian W. Kernighan&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;FORTRAN was the lingua franca for mainframe programmers in the 1960s and 1970s, but as Kernighan states it&amp;rsquo;s not always easy to program in - the main reasons are lack of good control structures and the fixed line format. As a result, a number of preprocessors were developed that translated enhanced code down to plain FORTRAN that could then be compiled anywhere a compiler was available.&lt;/p&gt;
&lt;p&gt;In this series of posts, we&amp;rsquo;ll look at two preprocessors available on MTS: RATFOR and FLECS. MTS also had OVERDRIVE, but this is not available on D6.0 due to copyright reasons.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get these preprocessors running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;ratfor&#34;&gt;RATFOR&lt;/h2&gt;
&lt;p&gt;RATFOR was developed by Brian Kernighan at Bell Telephone Labs in 1974; its syntax was (not surprisingly) inspired by the C programming language, with keywords like &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt; and &lt;code&gt;until&lt;/code&gt;. It was used as the language for examples in &lt;em&gt;Software Tools&lt;/em&gt; and became one of the most popular preprocessors in use. Versions are still available today that run on Unix systems.&lt;/p&gt;
&lt;h3 id=&#34;preprocessing-using-ratfor&#34;&gt;Preprocessing using &lt;code&gt;*RATFOR&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The version on MTS is called &lt;code&gt;*RATFOR&lt;/code&gt; and takes a RATFOR program as input on &lt;code&gt;scards&lt;/code&gt; and writes FORTRAN source to &lt;code&gt;spunch&lt;/code&gt;. The generated file can then be compiled with &lt;code&gt;*FTN&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;hello-world&#34;&gt;Hello world&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a terminal log of how to compile and run a simple hello world program in RATFOR. This assumes the source code is in file &lt;code&gt;hello.r&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.r

      1     # *** Simple hello world program ***
      2     #
      3     integer i
      4     for (i = 0; i &amp;lt; 5; i = i + 1)
      5     {
      6        write(6, 200)
      7     }
      8     stop
      9     200 format(&amp;#34;Hello, world!&amp;#34;)
     10     end

# $run *ratfor scards=hello.r spunch=-hello.f
Execution begins   21:56:08
Execution terminated   21:56:08  T=0.004

# #list -hello.f

      1           INTEGERI
      2           CONTINUE
      3           I=0
      4     23000 IF(.NOT.(I.LT.5))GOTO 23002
      5           WRITE(6,200)
      6     23001 I=I+1
      7           GOTO 23000
      8     23002 CONTINUE
      9           STOP
     10     200   FORMAT(13HHello, world!)
     11           END

# $run *ftn scards=-hello.f spunch=-load
Execution begins   21:56:36
 No errors in MAIN
Execution terminated   21:56:36  T=0.008

# #run -load
Execution begins   21:56:39
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
Execution terminated   21:56:39  T=0.001
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;flecs&#34;&gt;FLECS&lt;/h2&gt;
&lt;p&gt;FLECS was written in the early 1970s by Terry Beyer at the University of Oregon. It provides a smaller set of control structures that RATFOR but the syntax is closer to FORTRAN. Keywords include &lt;code&gt;IF...THEN...ELSE&lt;/code&gt; and &lt;code&gt;CONDITIONAL&lt;/code&gt; and multi-line statements are supported. It does not appear to have been used much past the introduction of FORTRAN77, but a a version is still available today for &lt;a href=&#34;http://hpux.connect.org.uk/hppd/hpux/Languages/flecs90-1.0/man.html&#34;&gt;HPUX&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;compiling-using-unspflx&#34;&gt;Compiling using &lt;code&gt;UNSP:FLX&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;At the time D6.0 was released, FLECS was unsupported at UM so is available as the file &lt;code&gt;FLX&lt;/code&gt; in &lt;code&gt;UNSP:&lt;/code&gt;. The preprocessor does not used &lt;code&gt;scards&lt;/code&gt; and &lt;code&gt;spunch&lt;/code&gt;; instead, all parameters need to be passed in to &lt;code&gt;par&lt;/code&gt;. Unlike RATFOR, FLECS can call the FORTRAN compiler directly to generate object code. In the listing below, &lt;code&gt;PAR=SOURCE=hello.fl,P=*SINK*,FTNSOURCE,LOAD=-load&lt;/code&gt; would read source from &lt;code&gt;hello.fl&lt;/code&gt;, print diagnostics to &lt;code&gt;*SINK&lt;/code&gt; including the FORTRAN source generated, and write compiled output to &lt;code&gt;-load.&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;hello-world-1&#34;&gt;Hello world&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a terminal log of how to compile and run a simple hello world program in FLECS. This assumes the source code is in file &lt;code&gt;hello.fl&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.fl

      1     C *** SIMPLE HELLO WORLD PROGRAM ***
      2     C
      3           DO (I = 1,5)
      4           WRITE (6,20)
      5           FIN
      6           STOP
      7        20 FORMAT(13H HELLO, WORLD)
      8           END

# $run UNSP:FLX PAR=SOURCE=hello.fl,P=*SINK*,FTNSOURCE,LOAD=-load
Execution begins   21:47:21
 FFI(CT206)


 (FLECS VERSION 22.38)  MTS Version CT155 21:47:21    JAN 21, 1916    Page   1

 MTS Line#        Indented Source Listing...

      1     C *** SIMPLE HELLO WORLD PROGRAM ***
      2     C
      3           DO (I = 1,5)
      4           .  WRITE (6,20)
      5           ...FIN
      6           STOP
      7        20 FORMAT(13H HELLO, WORLD)
      8           END

   0.001 seconds CPU time used.  Translation rate is 480000 lines per CPU minute.

 There were   NO MAJOR ERRORS and   NO MINOR ERRORS in the above module.
 No preprocessor errors in module  1.


  MICHIGAN TERMINAL SYSTEM FORTRAN G(21.8) MAIN 01-21-16 21:47:21 PAGE P001

    0001              DO 99998 I = 1,5             3.000
    0002              WRITE (6,20)                 4.000
    0003        99998 CONTINUE                     5.000
    0004              STOP                         6.000
    0005           20 FORMAT(13H HELLO, WORLD)     7.000
    0006              END                          8.000
     *OPTIONS IN EFFECT*  ID,EBCDIC,SOURCE,NOLIST,NODECK,LOAD,NOMAP
     *OPTIONS IN EFFECT*  NAME = MAIN    , LINECNT =       57
     *STATISTICS*    SOURCE STATEMENTS =        6,PROGRAM SIZE =      344
     *STATISTICS*  NO DIAGNOSTICS GENERATED
 No errors in MAIN

 NO STATEMENTS FLAGGED IN THE ABOVE COMPILATIONS.
Execution terminated   21:47:21  T=0.018

# $run -load
Execution begins   21:47:31
 HELLO, WORLD
 HELLO, WORLD
 HELLO, WORLD
 HELLO, WORLD
 HELLO, WORLD
Execution terminated   21:47:31  T=0.001
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://en.wikipedia.org/wiki/Ratfor&#34;&gt;Wikipedia&lt;/a&gt; article on RATFOR has a basic introduction to language features and the history of its development. &lt;a href=&#34;http://striky.ece.jhu.edu/~sasha/SOFTWARE/FORTRAN90/ratfor.pdf&#34;&gt;Kernighan&amp;rsquo;s paper&lt;/a&gt; on RATFOR goes into more detail on the language.&lt;/p&gt;
&lt;p&gt;Not much appears to exist on the Internet describing FLECS, but the D6.0 MTS tapes does include the complete User&amp;rsquo;s Manual (in component 673/22) and the interface to MTS.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://hdl.handle.net/2027.42/79601&#34;&gt;MTS Volume 6&lt;/a&gt; describes the FORTRAN compilers on MTS, which are needed to compile the RATFOR preprocessor&amp;rsquo;s output.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SNOBOL - Date formats</title>
      <link>https://try-mts.com/snobol-date-formats/</link>
      <pubDate>Sun, 11 Oct 2015 03:16:45 +0000</pubDate>
      <guid>https://try-mts.com/snobol-date-formats/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s implement a simple program in SNOBOL on MTS to print today&amp;rsquo;s date in different formats.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;The problem is quite simple: take today&amp;rsquo;s date and display it in ISO format (eg 2015-10-11) and a human readable format (eg Sunday, October 11, 2015). Further details and implementations in other languages can be found on &lt;a href=&#34;http://rosettacode.org/wiki/Date_format&#34;&gt;Rosetta Code&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;getting-todays-date&#34;&gt;Getting today&amp;rsquo;s date&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a built in function in SNOBOL to return the date as an eight character string. Interestingly, the SNOBOL 4 Programming Language says this returns &amp;lsquo;MM/DD/YY&amp;rsquo; but on MTS it returns &amp;lsquo;MM-DD-YY&amp;rsquo;. Let&amp;rsquo;s get this and store in a variable.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        NOW = DATE()
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;breaking-the-date-into-components&#34;&gt;Breaking the date into components&lt;/h2&gt;
&lt;p&gt;We take the date and extract the month, day and year using SNOBOL&amp;rsquo;s pattern matching facility and assign it to variables &lt;code&gt;DAY&lt;/code&gt;, &lt;code&gt;MONTH&lt;/code&gt;, and &lt;code&gt;YEAR&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        PART = SPAN(&amp;#34;0123456789&amp;#34;)
        SEP  = &amp;#34;-&amp;#34;
        NOW (PART . MONTH) SEP (PART . DAY) SEP (PART . YEAR)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;y2k-strikes-again&#34;&gt;Y2K strikes again&lt;/h2&gt;
&lt;p&gt;The value returned from &lt;code&gt;DATE()&lt;/code&gt; has a two digit year, so let&amp;rsquo;s assume we are running this in the 21st century.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        CENTURY = 2000
        CYEAR = YEAR + CENTURY
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;displaying-in-iso-format&#34;&gt;Displaying in ISO format&lt;/h2&gt;
&lt;p&gt;So displaying the date in ISO format is now simply a case of concatenating the day, month and four digit year and then outputting it.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        ISO = CYEAR SEP MONTH SEP DAY
        OUTPUT = ISO
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;day-of-the-week&#34;&gt;Day of the week&lt;/h2&gt;
&lt;p&gt;We now turn to the human readable form but there is a slight problem - we need to know which day of the week it is, eg Monday, and there is no facility in SNOBOL to calculate this. There may be a MTS external library we could call to get this, but instead we will use &lt;a href=&#34;https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Formulas_derived_from_Gauss.27_algorithm&#34;&gt;Gauss&amp;rsquo; algorithm&lt;/a&gt; to derive the day as a number from 0 (Sunday) to 6 (Saturday).&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* GYEAR is the 4 digit year, unless Jan or Feb then subtract 2
* GMONTH is MONTH-2 modulus 12, Jan is 11, Feb is 12
        GT(MONTH, 2)               :S(G1)F(G2)
G1      GYEAR = CYEAR
        GMONTH = MONTH - 2         :(GX)
G2      GYEAR = CYEAR - 1
        EQ(MONTH, 1)               :S(G3)F(G4)
G3      GMONTH = 11                :(GX)
G4      GMONTH = 12                :(GX)
GX      WDAY = REMDR(DAY, 7)
* Calculate the month term 
        MT = (2.6 * GMONTH) - 0.2
* Add the month term - the 0.00005 is needed due to lack of FP precision
        WDAY = WDAY + REMDR(CONVERT(MT + 0.00005, &amp;#39;INTEGER&amp;#39;), 7)
        WDAY = WDAY + 5 * REMDR(REMDR(GYEAR, 4), 7)
        WDAY = WDAY + 4 * REMDR(REMDR(GYEAR, 100), 7)
        WDAY = WDAY + 6 * REMDR(REMDR(GYEAR, 400), 7)
        WDAY = REMDR(WDAY, 7)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;month-and-day-names&#34;&gt;Month and day names&lt;/h2&gt;
&lt;p&gt;We will need a way of translating a month and day number into a name, eg January or Monday. SNOBOL&amp;rsquo;s arrays can be used for this. Note that the &lt;code&gt;DAYS&lt;/code&gt; array is indexed from 0 to 6 instead of 1 to 7.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        MONTHS = ARRAY(&amp;#34;12&amp;#34;)
        MONTHS&amp;lt;1&amp;gt; = &amp;#34;January&amp;#34;
        MONTHS&amp;lt;2&amp;gt; = &amp;#34;February&amp;#34;
* ...
        MONTHS&amp;lt;11&amp;gt; = &amp;#34;November&amp;#34;
        MONTHS&amp;lt;12&amp;gt; = &amp;#34;December&amp;#34;


        DAYS = ARRAY(&amp;#34;0:6&amp;#34;)
        DAYS&amp;lt;0&amp;gt; = &amp;#34;Sunday&amp;#34;
        DAYS&amp;lt;1&amp;gt; = &amp;#34;Monday&amp;#34;
* ...
        DAYS&amp;lt;5&amp;gt; = &amp;#34;Friday&amp;#34;
        DAYS&amp;lt;6&amp;gt; = &amp;#34;Saturday&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;displaying-in-readable-format&#34;&gt;Displaying in readable format&lt;/h2&gt;
&lt;p&gt;We now have all the components to display the date in readable format.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        READABLE = DAYS&amp;lt;WDAY&amp;gt; &amp;#34;, &amp;#34; MONTHS&amp;lt;MONTH&amp;gt; &amp;#34; &amp;#34; DAY &amp;#34;, &amp;#34; CYEAR
        OUTPUT = READABLE
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;running-the-program&#34;&gt;Running the program&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what the output of the program looks like.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *snobol4 5=date.sn
# Execution begins   21:09:05

 SNOBOL4 (VERSION 3.10, APRIL 1, 1973)
 (MTS IMPLEMENTATION MAY 1, 1975)

 0 SYNTACTIC ERROR(S) IN SOURCE PROGRAM


 2015-10-11
 Sunday, October 11, 2015


 NORMAL TERMINATION AT LEVEL  0
 LAST STATEMENT EXECUTED WAS   53


 SNOBOL4 STATISTICS SUMMARY
              38 MS. COMPILATION TIME
               1 MS. EXECUTION TIME
              41 STATEMENTS EXECUTED,       0 FAILED
              21 ARITHMETIC OPERATIONS PERFORMED
               1 PATTERN MATCHES PERFORMED
               0 REGENERATIONS OF DYNAMIC STORAGE
               0 READS PERFORMED
               2 WRITES PERFORMED
            0.02 MS. AVERAGE PER STATEMENT EXECUTED

# Execution terminated   21:09:05  T=0.045
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts-on-snobol&#34;&gt;Final thoughts on SNOBOL&lt;/h2&gt;
&lt;p&gt;Using SNOBOL feels close to modern scripting languages such as Perl, Python or Ruby. I really like the pattern matching facilities where you can do things like &lt;code&gt;EXPR = TERM | *EXPR OP TERM&lt;/code&gt; which is much more powerful than regular expressions; I don&amp;rsquo;t think there is any modern language that has this built in. The lack of control flow processing apart from GOTO is annoying; later versions of the language such as SPITBOL addressed this. I imagine that it was also rather slow when running on a mainframe, especially as it had to be compiled each time it was run.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think SNOBOL is much in use today, but the maintainer of SPITBOL is &lt;a href=&#34;http://daveshields.me/2012/09/02/on-being-the-maintainer-sole-developer-and-probably-the-sole-active-user-of-the-programming-language-spitbol/&#34;&gt;still active&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SNOBOL - Language features</title>
      <link>https://try-mts.com/snobol-language-features/</link>
      <pubDate>Sun, 30 Aug 2015 02:54:54 +0000</pubDate>
      <guid>https://try-mts.com/snobol-language-features/</guid>
      <description>&lt;p&gt;Hello again. Let&amp;rsquo;s take a closer look at the SNOBOL language: to run these on MTS see the instructions in &lt;a href=&#34;https://try-mts.com/snobol-introduction&#34;&gt;the previous article&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;statements&#34;&gt;Statements&lt;/h2&gt;
&lt;p&gt;There is only one statement format in SNOBOL, but each part of the statement is optional:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;label subject pattern = replacement :goto
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An example will make this clearer. This will read lines from input and print to output until &lt;code&gt;QUIT&lt;/code&gt; is found anywhere in the input line.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BEGIN  LINE = INPUT
       LINE &amp;#34;QUIT&amp;#34;     :S(END)
       OUTPUT = LINE   :(BEGIN)
END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line &lt;code&gt;BEGIN LINE = INPUT&lt;/code&gt; has a label (&lt;code&gt;BEGIN&lt;/code&gt;) and assigns the subject variable &lt;code&gt;LINE&lt;/code&gt; to the replacement value &lt;code&gt;INPUT&lt;/code&gt;, which is a special keyword meaning take a line from the input device.&lt;/p&gt;
&lt;p&gt;The second line has the subject &lt;code&gt;LINE&lt;/code&gt; and matches that against the pattern &lt;code&gt;&amp;quot;QUIT&amp;quot;&lt;/code&gt;. If the match was a success, the goto statement &lt;code&gt;:S(END)&lt;/code&gt; will jump to the final line with the end label.&lt;/p&gt;
&lt;p&gt;The third line prints the value of the replacement, &lt;code&gt;LINE&lt;/code&gt;, by assigning it to the subject, which is the special keyword &lt;code&gt;OUTPUT&lt;/code&gt;. The goto here is unconditionally back to &lt;code&gt;BEGIN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The fourth line contains just the label &lt;code&gt;END&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;dynamic-variables&#34;&gt;Dynamic variables&lt;/h2&gt;
&lt;p&gt;SNOBOL is the first language we&amp;rsquo;ve seen on MTS that has dynamic typing. Variables do not need to be pre-declared and their values can change types easily. In the below, J starts off containing a string but then is changed so it contains an integer; 42 is printed.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;J = &amp;#34;ABC&amp;#34;
A = 20
B = &amp;#34;22&amp;#34;
J = A + B
OUTPUT = J
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You cam also refer to and create variables indirectly via the &lt;code&gt;$&lt;/code&gt; operator. The below will print &amp;ldquo;123&amp;rdquo;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;A = &amp;#39;B&amp;#34;
B = &amp;#34;123
OUT = $&amp;#34;A&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;arrays-tables-and-data-types&#34;&gt;Arrays, tables and data types&lt;/h2&gt;
&lt;p&gt;Arrays can be defined and initialised with &lt;code&gt;ARRAY&lt;/code&gt;, eg for a tic-tac-toe board you could do:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BOARD = ARRAY(&amp;#34;3,3&amp;#34;, &amp;#34; &amp;#34;)
BOARD&amp;lt;2,2&amp;gt; = &amp;#34;O&amp;#34;
BOARD&amp;lt;1,1&amp;gt; = &amp;#34;X&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Associative arrays can be defined with &lt;code&gt;TABLE&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;DIRECTORY = TABLE()
DIRECTORY&amp;lt;&amp;#34;John&amp;#34;&amp;gt; = 123
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;User defined data types can be created, eg for a 2D point type, the below will print 4:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;DATA(&amp;#34;POINT(X,Y)&amp;#34;)
P = POINT(3, 4)
OUTPUT = Y(P)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;control-flow&#34;&gt;Control flow&lt;/h2&gt;
&lt;p&gt;The only control flow available in SNOBOL is goto. Each statement evaluates to either success or failure, and a jump to another statement can be made based on this result or unconditionally. In the below, a different jump for success and failure is defined on the third line.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;START X = INPUT
      X &amp;#34;YES&amp;#34; :S(START)F(END)
      OUTPUT = &amp;#34;This is not reached&amp;#34;
END
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;pattern-matching&#34;&gt;Pattern matching&lt;/h2&gt;
&lt;p&gt;The above example is a simple pattern matching test: if the variable &lt;code&gt;X&lt;/code&gt; contains &lt;code&gt;YES&lt;/code&gt; then the statement is successful. SNOBOL has many more pattern matching constructs: some are showed below along with a string that would be a successful match.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple concatenation of strings matches the sequence, either &lt;code&gt;|&lt;/code&gt; or ANY can be used for alternation.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;HAMLET&amp;#34;      &amp;#34;ML&amp;#34; &amp;#34;ET&amp;#34;
&amp;#34;THE TEMPEST&amp;#34; &amp;#34;TOMP&amp;#34; | &amp;#34;TEMP&amp;#34;
&amp;#34;MACBETH&amp;#34;     &amp;#34;MA&amp;#34; ANY(&amp;#34;CDY&amp;#34;) &amp;#34;BETH&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Patterns can be grouped together with brackets:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;A MIDSUMMER&amp;#39;S NIGH DREAM&amp;#34;   &amp;#34;MID&amp;#34; (&amp;#34;SUMMER&amp;#34; | &amp;#34;WINTER&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;ARB matches and arbitrary number of chars&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;OTHELLO&amp;#34;     &amp;#34;H&amp;#34; ARB &amp;#34;LO&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;LEN matches a fixed length run of characters&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;HENRY IV PART I&amp;#34;     &amp;#34;HENRY &amp;#34; LEN(2) &amp;#34; PART &amp;#34; LEN(1)
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;SPAN matches a run of anything from a set of characters, BREAK the opposite&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;PERICLES&amp;#34;   &amp;#34;PER&amp;#34; SPAN(&amp;#34;CIXZ&amp;#34;) BREAK(&amp;#34;ABCDE&amp;#34;) &amp;#34;ES&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;BAL matches an string which has balanced parentheses (including no parentheses), so the pattern&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;TO BE&amp;#34; BAL &amp;#34;.&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;would match &amp;ldquo;TO BE (OR NOT TO BE).&amp;rdquo; and &amp;ldquo;TO BE OR NOT TO BE.&amp;rdquo; but not &amp;ldquo;TO BE ((OR NOT TO BE).&amp;rdquo;&lt;/p&gt;
&lt;p&gt;By default, SNOBOL will match at any position on the line; it can be forced to match from a certain column by setting the variable &lt;code&gt;&amp;amp;ANCHOR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Patterns can be defined and referred to later; a pattern can be referred to recursively in the same pattern with &lt;code&gt;*&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A more complex example is below, which will match simple arithmetic expressions, eg &lt;code&gt;Z=21&lt;/code&gt; or &lt;code&gt;X+Y*Z=42&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BEGIN LINE = INPUT
      &amp;amp;ANCHOR = 1
      NUM     = SPAN(&amp;#34;0123456789&amp;#34;)
      TERM    = ANY(&amp;#34;XYZ&amp;#34;)
      OP      = ANY(&amp;#34;+-*/&amp;#34;)
      EXPR    = TERM | *EXPR OP TERM
      LINE    EXPR &amp;#34;=&amp;#34; NUM            :S(END)
      OUTPUT = LINE                   :(BEGIN)
END
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;replacement-and-assignment&#34;&gt;Replacement and assignment&lt;/h2&gt;
&lt;p&gt;If any of the above patterns matches, simple replacement can be done by using &lt;code&gt;pattern = replacement&lt;/code&gt;, so the below will replace the first occurrence of A with Z.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; LINE = INPUT
 LINE &amp;#34;A&amp;#34; = &amp;#34;Z&amp;#34;
 OUTPUT = LINE
END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Assignment of a substring to a variable can be done with the binary operator &lt;code&gt;.&lt;/code&gt; (which will match if the whole pattern matches) or &lt;code&gt;$&lt;/code&gt; (which will match even if the whole pattern fails. So for the line below, AQQQZ will cause FIRST to be A and LAST to be Z, but AQQQ will cause neither to be set.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LINE ANY(&amp;#34;ABC&amp;#34;) . FIRST BREAK(&amp;#34;XYZ&amp;#34;) ANY(&amp;#34;XYZ&amp;#34;) . LAST
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Instead, if you do&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LINE ANY(&amp;#34;ABC&amp;#34;) $ FIRST BREAK(&amp;#34;XYZ&amp;#34;) ANY(&amp;#34;XYZ&amp;#34;) $ LAST
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;then AQQQ will cause FIRST to be set to A. Note that QQQZ will not match to LAST in either case.&lt;/p&gt;
&lt;h2 id=&#34;built-in-functions&#34;&gt;Built in functions&lt;/h2&gt;
&lt;p&gt;SNOBOL has a number of built in functions. Function parameters are passed by value and the function can return a value.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LT, GT, EQ etc for numeric equality&lt;/li&gt;
&lt;li&gt;LGT compares two values lexically and returns true if the first is after the second&lt;/li&gt;
&lt;li&gt;INTEGER to test if a value is an integer&lt;/li&gt;
&lt;li&gt;IDENT and DIFFER to compare two values and return true (or false for DIFFER) if they have the same type and value&lt;/li&gt;
&lt;li&gt;SIZE for string size, DUPL(n, x) to create a string of size n by repeating the value of X&lt;/li&gt;
&lt;li&gt;TRIM to remove trailing blanks&lt;/li&gt;
&lt;li&gt;EVAL(x) will evaluate the expression in the string x at run time; APPLY(f, a, b&amp;hellip;) will take the string f and run its value as a function, passing in variables a, b etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;user-defined-functions&#34;&gt;User defined functions&lt;/h2&gt;
&lt;p&gt;It is possible to define functions, but the syntax is clumsy. First you need to define a function with &lt;code&gt;DEFINE(&amp;quot;name(args)locals&amp;quot;, &amp;quot;entrypoint&amp;quot;)&lt;/code&gt;. &lt;code&gt;args&lt;/code&gt; and &lt;code&gt;locals&lt;/code&gt; are a list of arguments and local variables used by the function. The label &lt;code&gt;entrypoint&lt;/code&gt; sets the start of the function, if omitted it will use &lt;code&gt;name&lt;/code&gt; as the entrypoint.&lt;/p&gt;
&lt;p&gt;Next, define the function at the label given by &lt;code&gt;name&lt;/code&gt;. The return value can be set by assigning to the function name, and the function is exited by goto-ing &lt;code&gt;RETURN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A simple example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;       DEFINE(&amp;#34;CENTRE(S)L,P&amp;#34;)

BEGIN  LINE = TRIM(INPUT)
       CENTRED = CENTRE(LINE)
       OUTPUT = CENTRED        :(END)

CENTRE L = (80 - SIZE(S)) / 2
       P = DUPL(&amp;#34;.&amp;#34;, L)
       CENTRE = P S P          :(RETURN)
END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will input a string and then pad it with leading and trailing dots to make it display on the centre of a line.&lt;/p&gt;
&lt;p&gt;Functions can be recursive in SNOBOL.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The language reference manual &amp;ldquo;The SNOBOL4 Programming Language&amp;rdquo;, linked at &lt;a href=&#34;http://www.snobol4.org/&#34;&gt;snobol4.org&lt;/a&gt;, has complete information on the language and was used to assemble this. Take a look at the example programs in the appendix for a taste of what can be done in SNOBOL.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SNOBOL - Introduction</title>
      <link>https://try-mts.com/snobol-introduction/</link>
      <pubDate>Sun, 26 Jul 2015 03:15:07 +0000</pubDate>
      <guid>https://try-mts.com/snobol-introduction/</guid>
      <description>&lt;p&gt;In this series we&amp;rsquo;ll look at SNOBOL, a unique pattern matching language, and its implementation on MTS.&lt;/p&gt;
&lt;h2 id=&#34;snobol-overview&#34;&gt;SNOBOL overview&lt;/h2&gt;
&lt;p&gt;SNOBOL (StriNg Oriented and symBOlic Language) was developed at Bell Labs in the 1960s to help with a symbolic manipulation project. It had powerful pattern matching and string manipulation features but had a simple syntax: it has no control flow instructions apart from goto and variables are dynamically typed and don&amp;rsquo;t need declarations. It started to spread to other sites and was taught at some universities in the 197-s. The original implementation was for the IBM 7090 but versions were ported to the IBM S/360 and DEC PDP/10. Its use started to die out in the 1980s but its creators went on to work on the ICON language and it influenced later text manipulation languages such as AWK and Perl.&lt;/p&gt;
&lt;h2 id=&#34;snobol-on-mts&#34;&gt;SNOBOL on MTS&lt;/h2&gt;
&lt;p&gt;The main implementation that we will run here is the &lt;code&gt;*SNOBOL4&lt;/code&gt; interpreter. Also available on the D6.0 tapes is &lt;code&gt;*SNOBOL4B&lt;/code&gt; which has an extension to the core language for printing blocks, two- and three-dimensional visualisations of data.&lt;/p&gt;
&lt;p&gt;MTS originally had a number of other implementations of SNOBOL that are not available on the D6.0 tapes due to copyright reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*SPITBOL&lt;/code&gt; - a fast SNOBOL 4 compiler from the Illinois Institute of Technology.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*SNOSTORM&lt;/code&gt; - a SNOBOL preprocessor written at UM to add structured programming features&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(Note that SPITBOL/360 has now been GPL&amp;rsquo;d and should be eligible for inclusion in future distributions of MTS - see &lt;a href=&#34;https://web.archive.org/web/20230206195906/http://archive.michigan-terminal-system.org/discussions/permission-to-include-licensed-programs/forspitbolandspitlib&#34;&gt;here&lt;/a&gt;.)&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get SNOBOL running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;using-snobol&#34;&gt;Using &lt;code&gt;*SNOBOL&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*SNOBOL4&lt;/code&gt; will read the source code for the program and then any input from unit 5 (by default &lt;code&gt;*source*&lt;/code&gt; ie standard input). If you want to take the source code from a file &lt;code&gt;prog.sn&lt;/code&gt; and then enter input from the keyboard you could do something like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *snobol4 5=prog.sn+*source*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Other parameters to &lt;code&gt;*SNOBOL4&lt;/code&gt; are listed in MTS Volume 9.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a transcript of a session where we run a Hello world program. This assumes the source code is contained in the file &lt;code&gt;hello.sn&lt;/code&gt;. Note that the code is not free format: only goto labels and comments (starting with &lt;code&gt;*&lt;/code&gt;) are allowed in the first column.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.sn

      1     * SNOBOL program to print Hello World
      2           I = 1
      3     LOOP  OUTPUT = &amp;#34;Hello, world!&amp;#34;
      4           I = I + 1
      5           LE(I, 5) : S(LOOP)
      6     END

# $run *snobol4 5=hello.sn

 SNOBOL4 (VERSION 3.10, APRIL 1, 1973)
 (MTS IMPLEMENTATION MAY 1, 1975)

         * SNOBOL program to print Hello World
 *1            I = 1
 *2      LOOP  OUTPUT = &amp;#34;Hello, world!&amp;#34;
 *3            I = I + 1
 *4            LE(I, 5) : S(LOOP)
 *5      END

    0 SYNTACTIC ERROR(S) IN SOURCE PROGRAM

 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!

 NORMAL TERMINATION AT LEVEL  0
 LAST STATEMENT EXECUTED WAS    4

SNOBOL4 STATISTICS SUMMARY

               5 MS. COMPILATION TIME
               2 MS. EXECUTION TIME
              16 STATEMENTS EXECUTED,       1 FAILED
               5 ARITHMETIC OPERATIONS PERFORMED
               0 PATTERN MATCHES PERFORMED
               0 REGENERATIONS OF DYNAMIC STORAGE
               0 READS PERFORMED
               5 WRITES PERFORMED
            0.13 MS. AVERAGE PER STATEMENT EXECUTED
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol09-SNOBOLInMTS-May1984.pdf&#34;&gt;MTS Volume 9&lt;/a&gt; describes the SNOBOL compilers available on MTS and includes a basic tutorial on the language.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.snobol4.org/&#34;&gt;snobol4.org&lt;/a&gt; has lots of information about SNOBOL&amp;rsquo;s history, implementations and links to books including the main reference manual for the language, &amp;ldquo;The SNOBOL4 Programming Language&amp;rdquo;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PL/I - Radix sort</title>
      <link>https://try-mts.com/pli-radix-sort/</link>
      <pubDate>Sun, 12 Jul 2015 03:49:43 +0000</pubDate>
      <guid>https://try-mts.com/pli-radix-sort/</guid>
      <description>&lt;p&gt;For the final part of this series on the PL/I programming language as implemented on MTS, let&amp;rsquo;s implement radix sort from &lt;a href=&#34;http://rosettacode.org/wiki/Sorting_algorithms/Radix_sort&#34;&gt;Rosetta Stone&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;Radix sort is intended as a fast way to sort an array of integers by grouping them by digits that share the same value. Digits can be defined depending on the numeric base and the sort can take place from either the most significant digit or the least. Here we&amp;rsquo;ll sort by most significant digit and use base 2. so we are comparing by bits.&lt;/p&gt;
&lt;p&gt;The algorithm we&amp;rsquo;ll use sorts in place. Starting at the largest bit it will group the numbers into one range where the bit is 1 and another where it is 0 by swapping values in the array. It will then recursively sort each range by the next smallest bit.&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s how the implementation looks; this was based on the C version at Rosetta Code.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;RADIX_SORT: PROCEDURE(FROM, TO, BIT, ARR) RECURSIVE;
  DECLARE (FROM, TO) FIXED DECIMAL;
  DECLARE BIT FIXED BINARY(16);
  DECLARE ARR(*) FIXED BINARY(16);

  DECLARE (LEFT, RIGHT) FIXED DECIMAL;
  DECLARE TEMP FIXED BINARY(16);

  /* Exit early if bit down to zero or range is zero */
  IF (BIT = 0) | (TO &amp;lt;= FROM) THEN RETURN;

  LEFT = FROM;
  RIGHT = TO;

  LOOP:
  /* Find the leftmost with the bit set */
  DO WHILE ((LEFT &amp;lt; RIGHT) &amp;amp; ((ARR(LEFT) &amp;amp; BIT) = 0));
    LEFT = LEFT + 1;
  END;

  /* Find the rightmost with the bit not set */
  DO WHILE ((LEFT &amp;lt; RIGHT) &amp;amp; ((ARR(RIGHT) &amp;amp; BIT) ¬= 0));
    RIGHT = RIGHT - 1;
  END;

  /* If found one of each, swap and loop again to look for more */
  IF LEFT &amp;lt; RIGHT THEN BEGIN;
    TEMP = ARR(LEFT);
    ARR(LEFT) = ARR(RIGHT);
    ARR(RIGHT) = TEMP;
    GO TO LOOP;
  END;

  /* If nothing found, make the first sub search do the full range */
  IF ((BIT &amp;amp; ARR(LEFT)) = 0) &amp;amp; (LEFT &amp;lt;= TO) THEN
    LEFT = LEFT + 1;

  /* The two ranges bounded by LEFT are now sorted by BIT.
   * Sort each range by the next least significant bit */
  BIT = BIT / 2;
  CALL RADIX_SORT(FROM, LEFT-1, BIT, ARR);
  CALL RADIX_SORT(LEFT, TO, BIT, ARR);
END RADIX_SORT;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The definition of the function is marked as &lt;code&gt;RECURSIVE&lt;/code&gt;, similar to other languages like ALGOL, as otherwise calling the function from itself will not work.&lt;/p&gt;
&lt;p&gt;We pass in an array of fixed point numbers of up to 16 bits in length with the dimensions of the array bounded by &lt;code&gt;FROM&lt;/code&gt; and &lt;code&gt;TO&lt;/code&gt;. The initial call will set &lt;code&gt;BIT&lt;/code&gt; to be 2^15. To handle an array of any size we can use &lt;code&gt;*&lt;/code&gt; in the declaration &lt;code&gt;DECLARE ARR(*) FIXED BINARY(16);&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Although PL/I has logical operators - and is &lt;code&gt;&amp;amp;&lt;/code&gt;, or is &lt;code&gt;|&lt;/code&gt;, not is &lt;code&gt;¬&lt;/code&gt; - it does not seem to have the Boolean not operator like &lt;code&gt;!&lt;/code&gt; in C, so we have to write &lt;code&gt;(ARR(LEFT) &amp;amp; BIT) = 0&lt;/code&gt; rather than something like &lt;code&gt;! (ARR(LEFT) &amp;amp; BIT)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll also need a procedure to print the array which will be called before and after the sort.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;SHOW_ARR: PROCEDURE(LABEL, ARR);
  DECLARE LABEL CHARACTER(80) VARYING;
  DECLARE ARR(*) FIXED BINARY(16);
  PUT FILE (SCARDS) LIST(LABEL);
  PUT FILE (SCARDS) SKIP;
  PUT FILE (SCARDS) LIST(ARR);
  PUT FILE (SCARDS) SKIP;
END SHOW_ARR;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we set up the main function&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE RADIX_SORT ENTRY (FIXED DECIMAL, FIXED DECIMAL,
                            FIXED BINARY(16),);

  DECLARE NUMS(10) FIXED BINARY(16);
  NUMS(1) = 3;       NUMS(6) = 1630;
  NUMS(2) = 42;      NUMS(7) = 4;
  NUMS(3) = 910;     NUMS(8) = 2721;
  NUMS(4) = 3241;    NUMS(9) = 684;
  NUMS(5) = 99;      NUMS(10) = 11;

  CALL SHOW_ARR(&amp;#39;Before&amp;#39;, NUMS);
  CALL RADIX_SORT(1, 10, 1000000000000000B, NUMS);
  CALL SHOW_ARR(&amp;#39;After&amp;#39;, NUMS);

  /* Definition of RADIX_SORT and SHOW_ARR follow inside TEST here. */
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we need to declare the signature of RADIX_SORT before we use it.&lt;/p&gt;
&lt;p&gt;We can now compile and run the program.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *pl1f scards=radix.pl1 spunch=-load
# Execution begins   20:03:01 

  PROCEDURE TEST: SYNTAX CHECK COMPLETED. COMPILATION CONTINUES.


   NO ERRORS OR WARNINGS DETECTED.
# Execution terminated   20:03:02  T=0.128 

# $run -load+*pl1flib
# Execution begins   20:03:03 
  &amp;#39;Before&amp;#39; 
          3        42       910      3241        99      1630         4      2721
        684        11 
  &amp;#39;After&amp;#39; 
          3         4        11        42        99       684       910      1630
       2721      3241 
   
# Execution terminated   20:03:03  T=0.003 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts-about-pli&#34;&gt;Final thoughts about PL/I&lt;/h2&gt;
&lt;p&gt;To a C programmer, PL/I appears the most familiar of the languages we&amp;rsquo;ve seen so far, from the comments style and semicolon as line terminators to the storage classes and system level access, but with a more ALGOLy syntax. Writing the above I certainly found it expressive and easy to use, but it lacks even the minimal type checking found in C and I wasn&amp;rsquo;t quite sure what conversions were going on behind the scenes.&lt;/p&gt;
&lt;p&gt;In its historical context, PL/I never really took off - it was tough to implement a compiler and too much identified with IBM to be adopted widely. It was important in the development of MULTICS and it nearly had a renaissance when it was made available for OS/2 in the 1990s, but ultimately it did not gain enough adoption from programmers to be truly successful.&lt;/p&gt;
&lt;p&gt;For MTS at UM, according to the &lt;a href=&#34;http://hdl.handle.net/2027/mdp.39015009827232?urlappend=%3Bseq=5&#34;&gt;Introduction to programming and debugging in MTS&lt;/a&gt;, it was the third most popular language (8% by frequency of use) in 1984, but way behind FORTRAN and PASCAL. It would be interesting to know what types of users it had - teaching, business or scientific research? It does not seem to have been used by MTS system programmers much, going by the lack of components in PL/I on the D6.0 tapes.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Go back to the &lt;a href=&#34;https://try-mts.com/pli-introduction/&#34;&gt;first article in the series&lt;/a&gt; on PL/I or see the &lt;a href=&#34;https://try-mts.com/programming-languages-in-mts/&#34;&gt;other languages&lt;/a&gt; being looked at.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PL/I - Language features 2</title>
      <link>https://try-mts.com/pli-language-features-2/</link>
      <pubDate>Sun, 28 Jun 2015 11:12:56 +0000</pubDate>
      <guid>https://try-mts.com/pli-language-features-2/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s continue our look at the PL/I language.&lt;/p&gt;
&lt;h2 id=&#34;storage-classes&#34;&gt;Storage classes&lt;/h2&gt;
&lt;p&gt;There are four storage classes in PL/I. &lt;code&gt;AUTOMATIC&lt;/code&gt; (the default) and &lt;code&gt;STATIC&lt;/code&gt; are much like in C:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CONTROLLED&lt;/code&gt; variables are assigned space dynamically with &lt;code&gt;ALLOCATE&lt;/code&gt; and released with &lt;code&gt;FREE&lt;/code&gt;. Unlike C &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt;, allocations are stacked so you can reallocate space and the program will remember the last allocation after it is freed. For example, the below will print 20 then 10.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE A FIXED BINARY(16) CONTROLLED;

  ALLOCATE A;
  A = 10;
  ALLOCATE A;
  A = 20;
  PUT FILE (SCARDS) LIST(A);
  FREE A;
  PUT FILE (SCARDS) LIST(A);
  FREE A;
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use of controlled variables before they are allocated or after they are freed will lead to a runtime error.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;BASED&lt;/code&gt; variables are similar to &lt;code&gt;CONTROLLED&lt;/code&gt; variables but also allow pointer style access.&lt;/p&gt;
&lt;p&gt;Variables (and procedures) can also be declared &lt;code&gt;EXTERNAL&lt;/code&gt; if they are defined elsewhere in the program.&lt;/p&gt;
&lt;h2 id=&#34;procedures-and-functions&#34;&gt;Procedures and functions&lt;/h2&gt;
&lt;p&gt;Procedures can be declared at file level, within other procedures, or even within DO blocks. They are introduced with &lt;code&gt;PROCEDURE&lt;/code&gt; and executed using &lt;code&gt;CALL&lt;/code&gt;. Parameters are passed by name so the procedure can alter them; you can also pass an expression and the compiler will introduce a temporary variable. The below will print 41.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE A FIXED BINARY(16);
  A = 41;
  CALL INCR(A);
  CALL INCR(A+1);
  PUT FILE (SCARDS) LIST(A);

  INCR: PROCEDURE(X);
    DECLARE X FIXED BINARY(16);
    X = X + 1;
  END INCR;

END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A function is a procedure that returns a value, so the above could also be written as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE A FIXED BINARY(16);
  A = 41;
  A = INCR(A);
  PUT FILE (SCARDS) LIST(A);

  INCR: PROCEDURE(X) RETURNS(FIXED BINARY(16));
    DECLARE X FIXED BINARY(16);
    RETURN(X + 1);
  END INCR;

END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that brackets are needed around the expression being returned.&lt;/p&gt;
&lt;h2 id=&#34;preprocessor&#34;&gt;Preprocessor&lt;/h2&gt;
&lt;p&gt;The source file can be augmented at compile time by using preprocessor directives, which start with &lt;code&gt;%&lt;/code&gt;. On MTS, to use this facility you must set the &lt;code&gt;MACRO&lt;/code&gt; parameter in the compiler command (eg &lt;code&gt;$run *pl1f scards=in.pl1 spunch=-load par=macro&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%INCLUDE otherfile&lt;/code&gt; copies the contents of &lt;code&gt;otherfile&lt;/code&gt; into the file currently being compiled.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%DECLARE&lt;/code&gt; can be used for macro replacement. &lt;code&gt;%DEACTIVATE&lt;/code&gt; will undefine a macro variable allowing it to be used by the program again. The following will print 42.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;%DECLARE A CHARACTER, B FIXED;
%A = &amp;#39;B + 2&amp;#39;;
%B = 40;

TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE X FIXED BINARY(16);
  X = A;
  PUT FILE (SCARDS) LIST(A);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So far so much like the C preprocessor. But you can also do compile time programming using labels, &lt;code&gt;%IF&lt;/code&gt; and &lt;code&gt;%GOTO&lt;/code&gt;. The below will substitute the middle section with &lt;code&gt;X = X + 1;&lt;/code&gt;, &lt;code&gt;X = X + 2;&lt;/code&gt; etc and prints 10.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE X FIXED BINARY(16);
  X = 0;
  
  %DECLARE N FIXED;
  %N = 1;
  %LABEL1:;
  X = X + N;
  %N = N + 1;
  %IF N &amp;lt; 5 %THEN %GO TO LABEL1;

  PUT FILE (SCARDS) LIST(X);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also a &lt;code&gt;%DO&lt;/code&gt; loop and the ability to create compile time &lt;code&gt;%PROCEDURES&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a lot more we haven&amp;rsquo;t covered. PL/I has extensive I/O operations in both stream and record formats. There is support for multi-tasking, although much of this is not available on MTS.&lt;/p&gt;
&lt;p&gt;There is surprisingly little on the internet about PL/I, outside of IBM. There&amp;rsquo;s an (opinionated) &lt;a href=&#34;https://www.uni-muenster.de/ZIV.EberhardSturm/PL1andC.html&#34;&gt;comparison between C and PL/I&lt;/a&gt;. The experience &lt;a href=&#34;http://www.multicians.org/pl1.html&#34;&gt;Multics had using PL/I&lt;/a&gt; is also interesting.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PL/I - Language features 1</title>
      <link>https://try-mts.com/pli-language-features-1/</link>
      <pubDate>Sun, 07 Jun 2015 03:29:03 +0000</pubDate>
      <guid>https://try-mts.com/pli-language-features-1/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s look at the PL/I language in more details. Before we start, PL/I is a big language, and we are going to need two posts to even scratch the surface.&lt;/p&gt;
&lt;h2 id=&#34;format&#34;&gt;Format&lt;/h2&gt;
&lt;p&gt;PL/I is free format, but the IBM implementation requires use of upper case letters for keywords and identifiers. By default, characters after the 72nd column are ignored by the IBM compiler but this can be changed by a command line switch.&lt;/p&gt;
&lt;p&gt;Comments are introduced with &lt;code&gt;/*&lt;/code&gt; and ended with &lt;code&gt;*/&lt;/code&gt; like in C.&lt;/p&gt;
&lt;p&gt;Keywords are not reserved words, so you can do things like the below, where &lt;code&gt;IF&lt;/code&gt; is an identifier; this will print 4. Also note that &lt;code&gt;=&lt;/code&gt; is used for assignment or comparison, depending on context.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE IF FIXED BINARY(16);
  IF = 3;
  IF IF = 3 THEN IF = 4; ELSE IF = 5;
  PUT FILE (SCARDS) LIST(IF);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;variables-and-constants&#34;&gt;Variables and constants&lt;/h2&gt;
&lt;h3 id=&#34;arithmetic-data&#34;&gt;Arithmetic data&lt;/h3&gt;
&lt;p&gt;Arithmetic data can be either fixed point or floating, both using in decimal or binary as its base. An example of each of these combinations:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE XB FIXED BINARY(16);
  DECLARE XD FIXED DECIMAL(8,2);
  DECLARE FB FLOAT BINARY(8);
  DECLARE FD FLOAT DECIMAL(5);
  XB = 42;
  XD = 123.45;
  FB = 10110.01B;
  FD = 123.45678;
  PUT FILE (SCARDS) LIST(XB, XD, FB, FD);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which will print&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;42      123.45  2.22E+01  1.2345E+02
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that for the binary items, the number in brackets specifies the number of bits to be used for storage; for decimals, it is the precision, so &lt;code&gt;FD&lt;/code&gt; has been truncated.&lt;/p&gt;
&lt;p&gt;In the above, you can see that &lt;code&gt;B&lt;/code&gt; can be used as a suffix for binary constants. There is also a &lt;code&gt;L&lt;/code&gt; suffix for pre-decimalisation British currency where the elements designate pounds, shillings and pence. This is converted into pence for storage.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE AMT FIXED BINARY(16);
  AMT = 1.5.2L;
  PUT FILE (SCARDS) LIST(AMT);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I think no other language has this, and it may reflect that PL/I was originally designed at IBM&amp;rsquo;s labs in England.&lt;/p&gt;
&lt;p&gt;You can also declare complex variables and use the &lt;code&gt;I&lt;/code&gt; suffix in constants, and there are &lt;code&gt;PICTURE&lt;/code&gt; definitions for string representations of numeric data similar to COBOL.&lt;/p&gt;
&lt;h3 id=&#34;strings&#34;&gt;Strings&lt;/h3&gt;
&lt;p&gt;Strings can be defined with either a fixed length or maximum length (with the &lt;code&gt;VARYING&lt;/code&gt; keyword). A repeating constant can be introduced with a prefix number, as the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE A CHARACTER(20);
  DECLARE B CHARACTER(20) VARYING;
  A = &amp;#39;HELLO&amp;#39;;
  B = (3)&amp;#39;PL1 &amp;#39;;
  PUT FILE (SCARDS) LIST(A,B);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which prints:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;HELLO               &amp;#39; &amp;#39;PL1 PL1 PL1 &amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are also bit strings, eg &lt;code&gt;DECLARE C BIT(8); C = &#39;1010&#39;B&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;arrays-and-structures&#34;&gt;Arrays and structures&lt;/h3&gt;
&lt;p&gt;Arrays can be defined by adding dimensions after the variable name, eg &lt;code&gt;DECLARE TABLE(10,20) FIXED DECIMAL(16);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Structures are collections of variables using a hierarchical format, for example&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE 1 MEAL,
            2 NAME CHARACTER(20) VARYING,
            2 CUISINE CHARACTER(20) VARYING,
            2 RESTAURANT,
              3 NAME CHARACTER(20) VARYING,
              3 ADDRESS CHARACTER(80) VARYING,
            2 COST FIXED DECIMAL(5,2);
  MEAL.NAME = &amp;#39;Dinner&amp;#39;;
  MEAL.CUISINE = &amp;#39;Italian&amp;#39;;
  MEAL.RESTAURANT.NAME = &amp;#39;Alfredo&amp;#39;;
  MEAL.ADDRESS = &amp;#39;123 Foo Road&amp;#39;;
  MEAL.COST = 123.45;
  PUT FILE (SCARDS) LIST(MEAL);
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that when qualifying a structure member we can omit levels if it is not ambiguous, eg &lt;code&gt;MEAL.ADDRESS&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once a structure has been set up, other variables can be instantiated with the same type using &lt;code&gt;LIKE&lt;/code&gt;, eg &lt;code&gt;DECLARE PARTY LIKE MEAL;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;other-types-and-attributes&#34;&gt;Other types and attributes&lt;/h3&gt;
&lt;p&gt;As well as statement labels, which can be used for &lt;code&gt;GO TO&lt;/code&gt; targets, there are special types for multitasking called events and tasks, but these are not supported by MTS.&lt;/p&gt;
&lt;p&gt;An alias to another variable can be set up with &lt;code&gt;DEFINED&lt;/code&gt;, eg &lt;code&gt;DECLARE A FIXED BINARY(16); DECLARE B DEFINED A;&lt;/code&gt; means B and A point at the same variable.&lt;/p&gt;
&lt;p&gt;Other attributes on types include &lt;code&gt;ALIGNED&lt;/code&gt; and &lt;code&gt;UNALIGNED&lt;/code&gt; and an initial value set at declaration with &lt;code&gt;INITIAL&lt;/code&gt;;&lt;/p&gt;
&lt;p&gt;Not covered here are variable lifetimes and pointers, which I will come back to in the next part.&lt;/p&gt;
&lt;h2 id=&#34;expressions&#34;&gt;Expressions&lt;/h2&gt;
&lt;p&gt;Expressions work mostly as expected, with conversions allowed implicitly between types - which leads to some complex rules given the number of built in types. &lt;code&gt;||&lt;/code&gt; can be used as the string concatenation operator. Whole arrays or structures can be used as parameters to an expression.&lt;/p&gt;
&lt;h2 id=&#34;statements&#34;&gt;Statements&lt;/h2&gt;
&lt;p&gt;Multiple assignments can be made on the left hand side of an expression, eg &lt;code&gt;A,B = 2*21&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Control flow statements include &lt;code&gt;IF ... THEN ... ELSE&lt;/code&gt; and looping via &lt;code&gt;DO&lt;/code&gt;, which allows variable and condition testing as in the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TEST: PROCEDURE OPTIONS(MAIN);
  DECLARE I FIXED BINARY(16);
  DECLARE S FIXED BINARY(16);

  DO I = 1 TO 5, 10 TO 15;
    PUT FILE (SCARDS) LIST(I);
  END;
  PUT FILE (SCARDS) SKIP;

  S = 0;
  DO WHILE (S &amp;lt; 10);
    S = S + 1;
    PUT FILE (SCARDS) LIST(S);
  END;
  PUT FILE (SCARDS) SKIP;

  S = 0;
  DO I = 2 TO 10 BY 2 WHILE (S &amp;lt; 10);
    S = S + I;
    PUT FILE (SCARDS) LIST(S);
  END;
  PUT FILE (SCARDS) SKIP;
END TEST;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;while will print&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;          1         2         3         4         5        10        11  
         13        14        15 

          1         2         3         4         5         6         7  
          9        10 

          2         6        12 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;STOP&lt;/code&gt; and &lt;code&gt;EXIT&lt;/code&gt; statements can be used to terminate program execution immediately. There is also exception support for a set of predefined cases, such as end of file, using &lt;code&gt;ON&lt;/code&gt;, eg &lt;code&gt;ON ENDFILE(SCARDS) GO TO DONE;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In the next post we will look at pointers, block structure and the pre-processor along with other topics.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The original IBM documentation for the PL/I language and PL/I F compiler can be found at &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/pli/GC28-8201-4_PLI_F_Language_Reference_Dec72.pdf&#34;&gt;bitsavers&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PL/I - Introduction</title>
      <link>https://try-mts.com/pli-introduction/</link>
      <pubDate>Sun, 24 May 2015 03:08:35 +0000</pubDate>
      <guid>https://try-mts.com/pli-introduction/</guid>
      <description>&lt;p&gt;In this series we&amp;rsquo;ll look at PL/I, or Programming Language 1, an ambitious multi-purpose language developed by IBM.&lt;/p&gt;
&lt;h2 id=&#34;pli-overview&#34;&gt;PL/I overview&lt;/h2&gt;
&lt;p&gt;PL/I was intended to be a programming language for all users - scientific, business and systems. It was originally developed by IBM in the mid &amp;rsquo;60s and spread to other mainframes, Multics and DEC systems. A subset of the language was used to create CP/M and IBM produced a version for OS/2 on PCs that was competitive with C. A standardised specification was produced by ECMA and ANSI from the early &amp;rsquo;70s. Although it gained some popularity (it was even used as a teaching language at some universities) it never really took off in any of the fields it was intended for.&lt;/p&gt;
&lt;p&gt;Language-wise, it is a procedural imperative language drawing inspiration from FORTRAN, COBOL and ALGOL. Due to its intended wide coverage, it is a very large language with a number of interesting features, some of which were incorporated into later languages such as C.&lt;/p&gt;
&lt;h2 id=&#34;pli-on-mts&#34;&gt;PL/I on MTS&lt;/h2&gt;
&lt;p&gt;There were three PL/I environments originally available on MTS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;*PL1F&lt;/code&gt; was derived from the IBM OS/360 F version 5 compiler with modifications for MTS. This is the only version available on the D6.0 tapes today and the one we will look at here. The MTS documentation refers to this as &lt;code&gt;*PL1&lt;/code&gt; but this name is not available; use &lt;code&gt;*PL1F&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;*PL1OPT&lt;/code&gt; was the IBM optimising compiler for PL/I and is not available due to copyright reasons. It took many of the compiler optimisation techniques developed for the FORTRAN H compiler and also offered a checkout facility for improved debugging.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;*PLC&lt;/code&gt; was a load-and-go processor developer at Cornell. It had the unique feature that any input would compile (but maybe not produce the desired results) due to automatic substitution of syntax errors. A binary version exists but was not distributed with the D6.0 version of MTS due to copyright concerns. It may be available in future versions; see this discussion at the &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/discussions/permission-to-include-licensed-programs/forplc&#34;&gt;MTS Archive&lt;/a&gt; for further information.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are also a number of utility programs for PL/I &lt;code&gt;*PL1SCAN&lt;/code&gt; does a quick syntax check of a program and &lt;code&gt;*PL1TIDY&lt;/code&gt; formats a PL/I source file.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get PL/I running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;using-plif&#34;&gt;Using &lt;code&gt;*PLIF&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*PLIF&lt;/code&gt; will read source from &lt;code&gt;scards&lt;/code&gt; (by default &lt;code&gt;*source*&lt;/code&gt; ie standard input) and will write object files to &lt;code&gt;spunch&lt;/code&gt;. Unlike some other MTS compilers you need to provide a filename for &lt;code&gt;spunch&lt;/code&gt; as there is no default. You should destroy or empty the contents of the object file before recompiling.&lt;/p&gt;
&lt;p&gt;Other compilation parameters are listed in MTS Volume 7.&lt;/p&gt;
&lt;p&gt;To run PL/I programs you need to concatenate the runtime library &lt;code&gt;*PL1LIB&lt;/code&gt; with the object file.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a transcript of a session where we run a Hello world program. This assumes the source code is contained in the file &lt;code&gt;hello.pl1&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.pl1

      1     /* Hello world program in PL/I */
      2     HELLO: PROCEDURE OPTIONS(MAIN);
      3       DECLARE I FIXED BINARY(16);
      4       DO I = 1 TO 5;
      5           PUT FILE (SCARDS) LIST(&amp;#39;Hello, world!&amp;#39;);
      6           PUT FILE (SCARDS) SKIP;
      7     END HELLO;

# $destroy -load
 File &amp;#34;-LOAD&amp;#34; has been destroyed.

# $run *pl1f scards=hello.pl1 spunch=-load
# Execution begins   12:18:31


  PROCEDURE HELLO: SYNTAX CHECK COMPLETED. COMPILATION CONTINUES.


  NO ERRORS OR WARNINGS DETECTED.
# Execution terminated   12:18:31  T=0.087

# $run -load+*pl1lib
# Execution begins   12:18:34
 &amp;#39;Hello, world!&amp;#39;
 &amp;#39;Hello, world!&amp;#39;
 &amp;#39;Hello, world!&amp;#39;
 &amp;#39;Hello, world!&amp;#39;
 &amp;#39;Hello, world!&amp;#39;

# Execution terminated   12:18:34  T=0.002
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol07-PL1InMTS-Sep1985.pdf&#34;&gt;MTS Volume 7&lt;/a&gt; describes the PL/I compilers available on MTS.&lt;/p&gt;
&lt;p&gt;The original IBM documentation for the PL/I language and PL/I F compiler can be found at &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/pli/GC28-8201-4_PLI_F_Language_Reference_Dec72.pdf&#34;&gt;bitsavers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A good introduction to PL/I is the &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/360/pli/C28-6808-0_PLIprimer_1965.pdf&#34;&gt;PL/I Primer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://en.wikipedia.org/wiki/PL/I&#34;&gt;Wikipedia&lt;/a&gt; article gives an extensive history and list of implementations.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Updated 10 June 2020&lt;/em&gt;: corrected status of &lt;code&gt;*PLC&lt;/code&gt; as per &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/discussions/permission-to-include-licensed-programs/forplc&#34;&gt;MTS Archive&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The great FORTRAN-LISP shootout</title>
      <link>https://try-mts.com/the-great-fortran-lisp-shootout/</link>
      <pubDate>Sun, 10 May 2015 03:25:39 +0000</pubDate>
      <guid>https://try-mts.com/the-great-fortran-lisp-shootout/</guid>
      <description>&lt;p&gt;One of the reasons LISP did not become more popular for scientific computing was the perception that it was slower than FORTRAN. In this post I will compare the performance of the LISP and FORTRAN implementations on MTS using a simple problem.&lt;/p&gt;
&lt;h2 id=&#34;the-problem-and-some-caveats&#34;&gt;The problem and some caveats&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll use the &lt;a href=&#34;https://try-mts.com/fortran-calculating-emirps/&#34;&gt;finding emirp primes&lt;/a&gt; problem introduced in the FORTRAN posts on this blog as the subject. To make it simpler we will just look at the third test, finding the 10,000th emirp.&lt;/p&gt;
&lt;p&gt;So some caveats before we start. Benchmarks are notoriously difficult to get right and this is only a single sample. I&amp;rsquo;m not an expert at optimising LISP or FORTRAN so there may well be better ways of implementing this. Finally, the UTILISP implementation is from the 1980s, MTS LISP is from the 1970s and the FORTRAN G and H compilers date from at least the 1960s so this is not a historically accurate recreation.&lt;/p&gt;
&lt;p&gt;Having said that, it&amp;rsquo;ll be interesting to see how they stack up, so let&amp;rsquo;s start.&lt;/p&gt;
&lt;h2 id=&#34;fortran&#34;&gt;FORTRAN&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll use the &lt;a href=&#34;https://try-mts.com/fortran-calculating-emirps/&#34;&gt;source code&lt;/a&gt; for the FORTRAN emirp implementation discussed before, taking out the first and second test.&lt;/p&gt;
&lt;p&gt;We compile without optimisations first (ie using FORTRAN G) and then try again with FORTRAN H. The results look like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# run *ftn scards=emirp3.f
# Execution begins   11:39:08 
  No errors in PRIME 
  No errors in REVRSE 
  No errors in EMIRP 
  No errors in SHOW 
  No errors in TEST3 
  No errors in MAIN 
# Execution terminated   11:39:08  T=0.045 
# run -load
# Execution begins   11:39:10 
     948349
# Execution terminated   11:39:19  T=9.05 
#
# $run *ftn scards=emirp3.f par=opt=h
# Execution begins   11:39:49 

  **** No errors for  PRIME ****      SAT MAY 09/15 11:39:49 

  **** No errors for REVRSE ****      SAT MAY 09/15 11:39:49 

  **** No errors for  EMIRP ****      SAT MAY 09/15 11:39:49 

  **** No errors for   SHOW ****      SAT MAY 09/15 11:39:49 

  **** No errors for  TEST3 ****      SAT MAY 09/15 11:39:49 

  **** No errors for   MAIN ****      SAT MAY 09/15 11:39:49 
# Execution terminated   11:39:49  T=0.074 
# run -load
# Execution begins   11:39:54 
     948349
# Execution terminated   11:39:59  T=4.281 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So 9.05s for FORTRAN G and 4.28s for FORTRAN H.&lt;/p&gt;
&lt;h2 id=&#34;mts-lisp&#34;&gt;MTS LISP&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s try the original MTS LISP first. It&amp;rsquo;s fairly easy to translate using the (GO) form to do looping.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-lisp&#34; data-lang=&#34;lisp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Checks if n is prime using a simple division test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(DEFUN PRIME-P (N)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (COND
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;;; Deal with numbers &amp;lt;= 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((LESS N &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;NIL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((LESS N &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;;; Check if divisible by 2 or 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((EQUAL (REMAIN N &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;NIL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((EQUAL (REMAIN N &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;NIL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;;; See if divisible by 5, 7, ..., up to approx sqrt(n)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt; (PROG (I RESULT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (SETQ I &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;A           (SETQ RESULT (COND ((GREATER (TIMES I I) N) &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;PRIME&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                               ((EQUAL (REMAIN N I) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;NOT-PRIME&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                               (&lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt; (SETQ I (ADD I &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;NIL&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (COND ((NOT RESULT) (GO A)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (EQ RESULT &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;PRIME&lt;/span&gt;)))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Return a number that has the reversed digits in n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(DEFUN REVERSE-DIGITS (N)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (PROG (REVERSED)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (SETQ REVERSED &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;;; Take last digit from n and append to reversed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (PROG ()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;B             (SETQ REVERSED (TIMES REVERSED &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              (SETQ REVERSED (ADD REVERSED (REMAIN N &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              (SETQ N (IDIVIDE N &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              (COND ((GREATER N &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) (GO B))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;;; CAR needed to evaluate REVERSED otherwise PROG will return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;;; the symbol REVERSED&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (CAR REVERSED)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Check if a number is an emirp, ie both it and its reversed digits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; are prime.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(DEFUN EMIRP-P (N)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (PROG (REVERSED)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (SETQ REVERSED (REVERSE-DIGITS N))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (AND (NOT (EQUAL N REVERSED)) (PRIME-P N) (PRIME-P REVERSED))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(DEFUN EMIRP-10K ()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (PROG (N COUNT LAST)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (SETQ N &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (SETQ COUNT &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (SETQ LAST &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;C       (COND ((EMIRP-P N)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               (SETQ COUNT (ADD COUNT &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)) (SETQ LAST N)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (SETQ N (ADD N &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (COND ((LESS COUNT &lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;) (GO C)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (CAR LAST)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(EMIRP-10K)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(STOP)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll first execute it through the interpreter:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *lisp scards=emirp.l
# Execution begins   12:41:27
      WELCOME TO LISP/MTS
 &amp;gt;    PRIME-P
 &amp;gt;    REVERSE-DIGITS
 &amp;gt;    EMIRP-P
 &amp;gt;    EMIRP-10K
* GC: COLLECTED 23370 CELLS
...
* GC: COLLECTED 23370 CELLS
&amp;gt;    948349
# Execution terminated   12:47:15  T=347.517 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next we&amp;rsquo;ll compile the functions. Add &lt;code&gt;(COMPILE PRIME-P)&lt;/code&gt; and so on for all the functions and rerun&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *lisp scards=emirp.l
     WELCOME TO LISP/MTS
&amp;gt;    PRIME-P
&amp;gt;    REVERSE-DIGITS
&amp;gt;    EMIRP-P
&amp;gt;    EMIRP-10K
* COMPILE        04-17-79 RESTORED
&amp;gt;    (PRIME-P)
&amp;gt;    (REVERSE-DIGITS)
&amp;gt;    (EMIRP-P)
&amp;gt;    (EMIRP-10K)
* GC: COLLECTED 23370 CELLS
...
* GC: COLLECTED 23370 CELLS
&amp;gt;    948349
# Execution terminated   12:56:39  T=72.347 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OK, so we are down from 348s to 72s (the compilation itself takes less than 0.5s). Not bad, but still 17 times slower than FORTRAN H.&lt;/p&gt;
&lt;h2 id=&#34;utilisp&#34;&gt;UTILISP&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how UTILISP compares. We do need to change the program in quite a few places to match the different built in functions (eg &lt;code&gt;+&lt;/code&gt; instead of &lt;code&gt;ADD&lt;/code&gt;) but at least we can now use the &lt;code&gt;do&lt;/code&gt; macro to create more expressive loops.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-lisp&#34; data-lang=&#34;lisp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Checks if n is prime using a simple division test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(defun prime-p (n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (cond
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;;; Deal with numbers &amp;lt;= 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;=&lt;/span&gt; n &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;=&lt;/span&gt; n &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;t&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;;; Check if divisible by 2 or 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((&lt;span style=&#34;color:#a6e22e&#34;&gt;=&lt;/span&gt; (remainder n &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ((&lt;span style=&#34;color:#a6e22e&#34;&gt;=&lt;/span&gt; (remainder n &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;;; See if divisible by 5, 7, ..., up to approx sqrt(n)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#66d9ef&#34;&gt;t&lt;/span&gt; (do ((i &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;+&lt;/span&gt; i &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)) (result &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          (result (&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt; result &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;prime&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          (&lt;span style=&#34;color:#66d9ef&#34;&gt;setq&lt;/span&gt; result (cond ((&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;*&lt;/span&gt; i i) n) &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;prime&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             ((&lt;span style=&#34;color:#a6e22e&#34;&gt;=&lt;/span&gt; (remainder n i) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;not-prime&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             (&lt;span style=&#34;color:#66d9ef&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)))))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Return a number that has the reversed digits in n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(defun reverse-digits (n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (do ((reversed &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) (m n (// m &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ((&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;&lt;/span&gt; m &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) reversed)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;;; Take last digit from n and append to reversed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#66d9ef&#34;&gt;setq&lt;/span&gt; reversed (&lt;span style=&#34;color:#a6e22e&#34;&gt;*&lt;/span&gt; reversed &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#66d9ef&#34;&gt;setq&lt;/span&gt; reversed (&lt;span style=&#34;color:#a6e22e&#34;&gt;+&lt;/span&gt; reversed (remainder m &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Check if a number is an emirp, ie both it and its reversed digits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; are prime.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(defun emirp-p (n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; ((reversed (reverse-digits n)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (and (neq n reversed) (prime-p n) (prime-p reversed))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Return the 10,000th emirp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(defun emirp-10k ()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (do ((n &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;+&lt;/span&gt; n &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)) (&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;last&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ((&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;last&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (cond ((emirp-p n) (&lt;span style=&#34;color:#66d9ef&#34;&gt;setq&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)) (&lt;span style=&#34;color:#66d9ef&#34;&gt;setq&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;last&lt;/span&gt; n)))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;;; Run the test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt; (emirp-10k))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(quit)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And let&amp;rsquo;s run it through the interpreter.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *utilisp 0=emirp.ul
# Execution begins   18:18:02 
  948349
# Execution terminated   18:21:30  T=208.35 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So that&amp;rsquo;s 40% quicker than the MTS LISP interpreted version. Let&amp;rsquo;s try compiling the forms, which we can just do by putting&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(compile prime-p reverse-digits emirp-p emirp-10k)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;after the definitions and then adding the &lt;code&gt;fix&lt;/code&gt; parameter to the command line to reserve memory for the compiler (again compilation takes less than 0.5s of the total):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ run *utilisp 0=emirp.ul par=fix=100
# Execution begins   18:32:27 
  ; Loading Utilisp Compiler System Version(82-11-09)
  948349
# Execution terminated   18:32:54  T=27.788 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much better - 3 times faster than the MTS LISP compiled version but 7 times slower still than FORTRAN H.&lt;/p&gt;
&lt;h2 id=&#34;summary-of-results-and-final-thoughts&#34;&gt;Summary of results and final thoughts&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Program&lt;/th&gt;
          &lt;th&gt;Time (s)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;FORTRAN G&lt;/td&gt;
          &lt;td&gt;9.05&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;FORTRAN H&lt;/td&gt;
          &lt;td&gt;4.28&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;MTS LISP interpreted&lt;/td&gt;
          &lt;td&gt;347.52&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;MTS LISP compiled&lt;/td&gt;
          &lt;td&gt;72.35&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;UTILISP interpreted&lt;/td&gt;
          &lt;td&gt;208.35&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;UTILISP compiled&lt;/td&gt;
          &lt;td&gt;27.79&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Compiled UTILISP puts up a good showing and it&amp;rsquo;s definitely usable for light numerical tasks but in the end FORTRAN wins on pure performance. The UTILISP implementation is much faster than MTS LISP for both interpreted and compiled code, which you&amp;rsquo;d expect given the amount of knowledge creating fast LISP implementations that was built up between the release of the two programs.&lt;/p&gt;
&lt;p&gt;Another factor evident from this test was the lack of LISP standardisation (pre-Common Lisp) makes porting programs between implementations a chore, whereas FORTRAN was relatively standardised.&lt;/p&gt;
&lt;p&gt;Any suggestions for optimising the code? Please let me know in the comments.&lt;/p&gt;
&lt;p&gt;In the next post, in a couple of weeks, we&amp;rsquo;ll look at PL/1.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for the programs shown here is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>UTILISP - Language features</title>
      <link>https://try-mts.com/utilisp-language-features/</link>
      <pubDate>Sun, 03 May 2015 02:46:34 +0000</pubDate>
      <guid>https://try-mts.com/utilisp-language-features/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s look at some of the language features in UTILISP compared to the original MTS LISP.&lt;/p&gt;
&lt;h2 id=&#34;strings&#34;&gt;Strings&lt;/h2&gt;
&lt;p&gt;UTILISP brings support for strings, which can also be treated as a sequence of bytes that can be accessed independently.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (setq s &amp;#34;hello world&amp;#34;)
  &amp;#34;hello world&amp;#34;
&amp;gt; (string-length s)
  11
&amp;gt; (sref s 1)
  133
&amp;gt; (sset s 1 134)
  134
&amp;gt; s
  &amp;#34;hfllo world&amp;#34;
&amp;gt; (string-search &amp;#34;world&amp;#34; s)
  6
&amp;gt; (cutout s 0 2)
  34950
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the last expression, we take the first two bytes of s and extract it as an integer. There&amp;rsquo;s also &lt;code&gt;bref&lt;/code&gt; and &lt;code&gt;bset&lt;/code&gt; which can be used to access individual bits in a string.&lt;/p&gt;
&lt;h2 id=&#34;numbers&#34;&gt;Numbers&lt;/h2&gt;
&lt;p&gt;UTILISP has both fixed and floating point numbers; arithmetic operators for floating point numbers have &lt;code&gt;$&lt;/code&gt; at the end of the keyword to distinguish them.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (setq f 0.0)
  +0.0000000?+00
&amp;gt; (setq i 0)
  0
&amp;gt; (0= i)
  T
&amp;gt; (0= f)
 
  @@@ ILLEGAL ARGUMENT TYPE
  +0.0000000?+00 -- C#/0=
&amp;gt; (0=$ f)
  T
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;vectors-and-references&#34;&gt;Vectors and references&lt;/h2&gt;
&lt;p&gt;Vectors are implemented as a primitive data type: they are created with &lt;code&gt;vector&lt;/code&gt; and addressed with &lt;code&gt;vref&lt;/code&gt; and &lt;code&gt;vset&lt;/code&gt;. Vectors can contain any LISP data types. Here we create a four element vector, initially filled with the number 42 in each slot.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (setq v (vector 4 42))
  V#-8057044
&amp;gt; v
  V#-8057044
&amp;gt; (vector-length v)
  4
&amp;gt; (vref v 0)
  42
&amp;gt; (vset v 0 43)
  43
&amp;gt; (vset v 1 &amp;#34;hello&amp;#34;)
  &amp;#34;hello&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can set references, which are pointers to elements of the vector which can then be dereferenced or updated through the reference.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (setq r (reference v 3))
  R#-8057028
&amp;gt; (deref r)
  42
&amp;gt; (setref r 99)
  99
&amp;gt; (vref v 3)
  99
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;mapv&lt;/code&gt; can be used to operate on each element of a vector; it passes a reference to each element.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (mapv v (function (lambda (r) (print (deref r)))))
  43
  &amp;#34;hello&amp;#34;
  42
  99
  V#-8057044
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also a &lt;code&gt;hash&lt;/code&gt; function that could be used to build efficient lookup tables using vectors.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (hash 32)
  32
&amp;gt; (hash &amp;#34;hello&amp;#34;)
  373277
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;macros&#34;&gt;Macros&lt;/h2&gt;
&lt;p&gt;UTILISP has macro support similar to modern LISPs, with selective quoting and evaluation via backtick and comma. Here we define a macro that will change a variable to nil if it is currently t.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (defmacro t-to-nil (v) 
      `(cond ((eq ,v t) (setq ,v nil))))
  T-TO-NIL
&amp;gt; (setq a 1)
  1
&amp;gt; (setq b t)
  T
&amp;gt; (t-to-nil a)
  NIL
&amp;gt; (t-to-nil b)
  NIL
&amp;gt; a
  1
&amp;gt; b
  NIL
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;scope-and-closures&#34;&gt;Scope and closures&lt;/h2&gt;
&lt;p&gt;UTILISP has dynamic scope and no lexical-let. There also seems to be no funarg implementation although this was available in Maclisp. It is possible to mimic closures with macros:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (defun make-adder (init)
   (let ((sym (gensym)))
     (set sym init)
     `(lambda ( val) (cond (val (setq ,sym (+ ,sym val))) (t ,sym)))))
  MAKE-ADDER
&amp;gt; (setq aa (make-adder 40))
  (LAMBDA (VAL) (COND (VAL ?) (T G0002)))
&amp;gt; (funcall aa 1)
  41
&amp;gt; (funcall aa 2)
  43
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;infant&#34;&gt;INFANT&lt;/h2&gt;
&lt;p&gt;One interesting macro package that comes with UTILISP is INFANT (Infix Antidote), which allows infix notation for arithmetic expressions via &lt;code&gt;!&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (! 2 + 3 * 4)
  14
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;MTS &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol22-UTLispInMTS-May1988.pdf&#34;&gt;Volume 22&lt;/a&gt; describes the UTILISP implementation and how to run it on MTS.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>UTILISP - Introduction</title>
      <link>https://try-mts.com/utilisp-introduction/</link>
      <pubDate>Sun, 26 Apr 2015 03:52:21 +0000</pubDate>
      <guid>https://try-mts.com/utilisp-introduction/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s look at the other LISP implementation on MTS, University of Tokyo Lisp or UTILISP.&lt;/p&gt;
&lt;h2 id=&#34;utilisp-overview&#34;&gt;UTILISP overview&lt;/h2&gt;
&lt;p&gt;UTILISP was originally developed at the University of Tokyo in 1980 for S/360 compatible mainframes. It was worked on there by students and staff for the following decade, moving to 68000 and SPARC processors along the way. It was distributed to several sites in Japan and the rest of the world and was used for AI research. One reason for its popularity was the speed of its compiled code.&lt;/p&gt;
&lt;p&gt;Language wise, it is similar to Maclisp; it uses dynamic scope and has macro facilities.&lt;/p&gt;
&lt;h2 id=&#34;utilisp-on-mts&#34;&gt;UTILISP on MTS&lt;/h2&gt;
&lt;p&gt;UTILISP was made available on MTS at UM in 1984 as &lt;code&gt;*UTILISP&lt;/code&gt;: it has some light customisation to make it work with MTS and an initial version of an MTS Volume was produced based on the original UTILISP documentation.&lt;/p&gt;
&lt;p&gt;One key program produced using UTILISP was a Prolog implementation, PROLOG/KR; this is also available on MTS and I will look at this later.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get UTILISP running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;using-utilisp&#34;&gt;Using &lt;code&gt;*UTILISP&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;UTILISP is an interpreted environment. Running &lt;code&gt;*UTILISP&lt;/code&gt; on its own will allow you to start entering expressions which will be immediately evaluated. It&amp;rsquo;s possible to load expressions from a text file by providing it as the &lt;code&gt;0&lt;/code&gt; parameter to &lt;code&gt;*UTILISP&lt;/code&gt;; after the file is read you will remain in the LISP expression reader. To run a complete program and return to MTS afterwards, make the last expression be &lt;code&gt;(quit)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is an editor available via &lt;code&gt;(edit fn)&lt;/code&gt; but it&amp;rsquo;s easier to do this outside of UTILISP.&lt;/p&gt;
&lt;p&gt;One improvement over &lt;code&gt;*LISP&lt;/code&gt; is that there is online help, though not using the MTS help system: as an example, type &lt;code&gt;(help car)&lt;/code&gt; to view a brief description of how the &lt;code&gt;car&lt;/code&gt; function works.&lt;/p&gt;
&lt;p&gt;Like &lt;code&gt;*LISP&lt;/code&gt;, it&amp;rsquo;s possible to compile expressions to machine code with &lt;code&gt;(compile fn)&lt;/code&gt;; note that you have to provide some fixed space memory to UTILISP at startup by setting &lt;code&gt;FIX&lt;/code&gt;, eg with the command &lt;code&gt;$run *utilisp par=fix=100&lt;/code&gt;. There does not seem to be a way to write the current system state or compiled functions back to disk, however.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a transcript of a session where we run a Hello world program. This assumes the source code is contained in the file &lt;code&gt;hello.ul&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.ul
 
       1     ;; Hello world program for UTILISP
       2     (do ((i 0 (1+ i)))
       3         ((= i 5))
       4         (print &amp;#34;Hello, world!&amp;#34;))
       5     (quit)

# $run *utilisp 0=hello.ul
# Execution begins   12:01:48 
  &amp;#34;Hello, world!&amp;#34;
  &amp;#34;Hello, world!&amp;#34;
  &amp;#34;Hello, world!&amp;#34;
  &amp;#34;Hello, world!&amp;#34;
  &amp;#34;Hello, world!&amp;#34;
# Execution terminated   12:01:48  T=0.035 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;MTS &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol22-UTLispInMTS-May1988.pdf&#34;&gt;Volume 22&lt;/a&gt; describes the UTILISP implementation and how to run it on MTS.&lt;/p&gt;
&lt;p&gt;This &lt;a href=&#34;http://member.wide.ad.jp/~wada/utihistory.html&#34;&gt;paper&lt;/a&gt; describes the history of UTILISP.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>LISP - 4 bit adder</title>
      <link>https://try-mts.com/lisp-4-bit-adder/</link>
      <pubDate>Sun, 29 Mar 2015 02:59:32 +0000</pubDate>
      <guid>https://try-mts.com/lisp-4-bit-adder/</guid>
      <description>&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;For this sample program we will simulate a four bit adder chip by constructing it from a series of AND, OR and NOT gates. See &lt;a href=&#34;http://rosettacode.org/wiki/Four_bit_adder&#34;&gt;Rosetta Code&lt;/a&gt; for full details.&lt;/p&gt;
&lt;h2 id=&#34;constructing-the-solution&#34;&gt;Constructing the solution&lt;/h2&gt;
&lt;p&gt;LISP provides AND, OR and NOT functions and we can use &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;NIL&lt;/code&gt; to represent true and false. First task is to make an XOR gate:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(DEFUN XOR (A B)
  (OR (AND A (NOT B))
      (AND B (NOT A))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next we make a half-bit and single bit adder. These return a result and a carry bit which can be fed into the next stage of the full adder. We create a cons cell containing the result and carry bit for the return type.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(DEFUN HALF-ADDER (A B)
  (CONS (XOR A B) (AND A B)))

(DEFUN ADDER (A B C0)
  (PROG (S1 S2)
        (SETQ S1 (HALF-ADDER C0 A))
        (SETQ S2 (HALF-ADDER (CAR S1) B))
        (CONS (CAR S2) (OR (CDR S1) (CDR S2)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We then connect the adders together to form a four bit adder. As this version of LISP&amp;rsquo;s &lt;code&gt;NTH&lt;/code&gt; returns the remainder of the list from the given position, we need a convenience function &lt;code&gt;BIT&lt;/code&gt; to access individual values.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(DEFUN BIT (L N)
  (CAR (NTH L (ADD1 N))))

(DEFUN ADDER-4 (A B)
  (PROG (X1 X2 X3 X4)
        (SETQ X1 (ADDER (BIT A 3) (BIT B 3) NIL))
        (SETQ X2 (ADDER (BIT A 2) (BIT B 2) (CDR X1)))
        (SETQ X3 (ADDER (BIT A 1) (BIT B 1) (CDR X2)))
        (SETQ X4 (ADDER (BIT A 0) (BIT B 0) (CDR X3)))
        (CONS (MAPCAR &amp;#39;CAR (LIST X4 X3 X2 X1)) (CDR X4))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The adder takes as input, and returns, a list of binary values, eg &lt;code&gt;(NIL T NIL T)&lt;/code&gt; for 5. It would be convenient to have a function that can convert an integer into such a list: we can do this recursively.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(DEFUN DEC-BIN (X)
  (PROG (BIT)
        (SETQ BIT (NOT (ZERO (LAND X 1))))  
        (COND ((LESS X 2) (LIST BIT))
              (T (APPEND (DEC-BIN (SHIFT X -1)) BIT)))))
  
;; Pad binary list L to length N by adding NILs in front
(DEFUN PAD (N L)
  (COND ((LESS (LENGTH L) N) 
         (REPEAT &amp;#39;(SETQ L (CONS NIL L)) (SUB N (LENGTH L))))
        (T L)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we write a test function to take two integers (assuming each is between 0 and 15) and add them, printing the sum:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(DEFUN TEST-ADDER-4 (A B)
  (PROG (AB BB RES)
        (SETQ AB (PAD 4 (DEC-BIN A)))
        (SETQ BB (PAD 4 (DEC-BIN B)))
        (SETQ RES (ADDER-4 AB BB))
        (PRIN1 AB) (PRIN1 &amp;#39;+) (PRIN1 BB)
        (PRIN1 &amp;#39;=) (PRIN1 (CAR RES))
        (PRIN1 &amp;#39;carry)  (PRIN1 (CDR RES))
        (TERPRI)))

(TEST-ADDER-4 3 2)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here &lt;code&gt;PRIN1&lt;/code&gt; prints a single item and a space; &lt;code&gt;TERPRI&lt;/code&gt; ends the current line.&lt;/p&gt;
&lt;h2 id=&#34;getting-it-working&#34;&gt;Getting it working&lt;/h2&gt;
&lt;p&gt;The LISP REPL helps here, as you can revise functions and test them until they work; this was fairly simple to get working, though &lt;code&gt;DEC-BIN&lt;/code&gt; was tricky until I separated out PAD into its own function. I struggled with the LISP editor and found it easier to apply changes back to the file using &lt;code&gt;*EDIT&lt;/code&gt; as each was done.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see it in action:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *lisp scards=4bitadd.l
# Execution begins   20:02:38 
     WELCOME TO LISP/MTS
&amp;gt;    XOR
&amp;gt;    HALF-ADDER
&amp;gt;    ADDER
&amp;gt;    BIT
&amp;gt;    ADDER-4
&amp;gt;    DEC-BIN
&amp;gt;    PAD
&amp;gt;    TEST-ADDER-4
&amp;gt;    (NIL NIL T T) + (NIL NIL T NIL) = (NIL T NIL T) carry NIL
&amp;gt;    NIL
# Execution terminated   20:02:38  T=0.007 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts-on-mts-lisp&#34;&gt;Final thoughts on MTS LISP&lt;/h2&gt;
&lt;p&gt;I like Lisp a lot and even though this is a very basic Lisp compared to Common Lisp (or even Emacs Lisp) it still feels a lot higher level than the ALGOL/FORTRAN family of languages we have seen so far. One issue that we don&amp;rsquo;t have, as we are running on fast modern computers, is speed: LISP was much slower than FORTRAN for numeric work and garbage collection was very slow.&lt;/p&gt;
&lt;p&gt;In the next post, which will be in mid-April, we&amp;rsquo;ll look at the other LISP in MTS - UTILISP.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Comparing MTS LISP to modern Lisps</title>
      <link>https://try-mts.com/comparing-mts-lisp-to-modern-lisps/</link>
      <pubDate>Sun, 22 Mar 2015 03:35:24 +0000</pubDate>
      <guid>https://try-mts.com/comparing-mts-lisp-to-modern-lisps/</guid>
      <description>&lt;p&gt;LISP is quite different from the languages we&amp;rsquo;ve looked at so far - but I will not give an introductory description, as there are plenty of resources on the Internet by better writers than me - instead I will look at how MTS LISP differs from more modern Lisps.&lt;/p&gt;
&lt;p&gt;In the examples below, input is prefixed with a &lt;code&gt;*&lt;/code&gt; and output with &lt;code&gt;&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;representation&#34;&gt;Representation&lt;/h2&gt;
&lt;p&gt;MTS LISP assumes all keywords are in uppercase, but is otherwise free format. Do note that if you put the comment character &lt;code&gt;;&lt;/code&gt; in the first column this will be treated as a carriage control instruction to advance to the start of the next page in listings, so best prefix this with a space.&lt;/p&gt;
&lt;h2 id=&#34;types&#34;&gt;Types&lt;/h2&gt;
&lt;p&gt;As in other LISPs, the primitive data structure is the atom, which have values and property lists; atoms can be combined in s-expressions to form binary trees or lists. Here we define three atoms, a, b, c:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ A 42)
&amp;gt;    42
* (SETQ B &amp;#39;A)
&amp;gt;    A
* (SETQ C (CONS A B))
&amp;gt;    (42 . A)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are no strings, but you can double-quote atom names to get printable output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ GREETING &amp;#39;&amp;#34;Hello, world!&amp;#34;)
&amp;gt;    Hello, world!
* (PRINT GREETING)
&amp;gt;    Hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Arrays are simulated using functions: for a 2x2 array:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (DEFINE (A ARRAY (2 2)))
&amp;gt;    A
* (SETA (A 1 2) 42)
&amp;gt;    42
* (PRINT (A 1 2))
&amp;gt;    42
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;standard-functions&#34;&gt;Standard functions&lt;/h2&gt;
&lt;p&gt;Many familiar Lisp functions for list manipulation, arithmetic and control flow are available in this version; a brief sample:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ L1 (LIST 1 2 (SUB 10 7)))
&amp;gt;       (1 2 3)
* (SETQ L2 (LIST 1 2 (SHIFT 2 2)))
&amp;gt;       (1 2 8)
* (CADR L2)
&amp;gt;       2
* (RPLACA L2 42)
&amp;gt;       (42 2 8)
* (DELETE 8 L2)
&amp;gt;       (42 2)
* L2
&amp;gt;       (42 2)
* (MAPC &amp;#39;PRINT L2)
&amp;gt;       42
*       2
&amp;gt;       NIL
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;functions-and-scope&#34;&gt;Functions and scope&lt;/h2&gt;
&lt;p&gt;As expected, you can use &lt;code&gt;LAMBDA&lt;/code&gt; to define an anonymous function and &lt;code&gt;DEFUN&lt;/code&gt; to attach a lambda function to an atom&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* ((LAMBDA (A) (ADD A 2)) 40)
&amp;gt;       42

* (DEFUN FACTORIAL (N) (COND ((LESS N 2) 1)
*                            (T (TIMES N (FACTORIAL (SUB1 N))))))
&amp;gt;       FACTORIAL
* (FACTORIAL 5)
&amp;gt;       120
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;*LISP&lt;/code&gt; has dynamic scoping; variables defined in a function, or in a &lt;code&gt;PROG&lt;/code&gt;, replace the current definitions for the life of the expression, including calls to other functions. See below, where the definition of &lt;code&gt;X&lt;/code&gt; in the &lt;code&gt;PROG&lt;/code&gt; is available in the &lt;code&gt;GETX&lt;/code&gt; function.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ X 42)
&amp;gt;       42
* (DEFUN GETX () X)
&amp;gt;       GETX
* (GETX)
&amp;gt;       42
* (PROG (X) (SETQ X 3) (GETX))
&amp;gt;       3
* (GETX)
&amp;gt;       42
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is no &lt;code&gt;let&lt;/code&gt; or lexical binding in &lt;code&gt;*LISP&lt;/code&gt;, hence no lexical closures. It may be possible to do &lt;a href=&#34;http://c2.com/cgi/wiki?DynamicClosure&#34;&gt;dynamic closures&lt;/a&gt; but there does not seem to be &lt;code&gt;function&lt;/code&gt; or &lt;code&gt;funargs&lt;/code&gt; available which is needed to implement this.&lt;/p&gt;
&lt;p&gt;There are also no macros (like &lt;code&gt;defmacro&lt;/code&gt;) which can be expanded and evaluated at run time; there is a basic macro facility called &lt;code&gt;READMACRO&lt;/code&gt; and &lt;code&gt;PRINTMACRO&lt;/code&gt; where a function can be called whenever a designated atom is encountered while reading or printing.&lt;/p&gt;
&lt;h2 id=&#34;goto-in-lisp&#34;&gt;GOTO in LISP&lt;/h2&gt;
&lt;p&gt;This rather blew my mind: a GOTO in LISP? The below will loop and read input until the user enters a number larger than 99. &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;Z&lt;/code&gt; are labels:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(PROG (X)
A   (SETQ X (READ))
    (COND ((GREATER X 99) (GO Z))
          (T (GO A)))
Z   (PRINT &amp;#39;DONE))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also &lt;code&gt;(RETURN x)&lt;/code&gt;, which will break out of the enclosing &lt;code&gt;PROG&lt;/code&gt; immediately.&lt;/p&gt;
&lt;h2 id=&#34;super-parentheses&#34;&gt;Super parentheses&lt;/h2&gt;
&lt;p&gt;Lisp is famously parenthesis-heavy: today we have text editors that can show matching pairs and indent code automatically but contemporary users of LISP would have had to do this by hand. To help out, &lt;code&gt;*LISP&lt;/code&gt; defines the angle-brackets &lt;code&gt;{}&lt;/code&gt; as super-parentheses: whenever an opening brace is encountered, the depth is remembered and when the matching close is found, the depth returns to that level. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ A (CAR (CDR (CDR (LIST &amp;#39;A &amp;#39;B &amp;#39;C &amp;#39;D)))))
&amp;gt;       C
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ A &amp;lt;CAR (CDR (CDR (LIST &amp;#39;A &amp;#39;B &amp;#39;C &amp;#39;D&amp;gt;)
&amp;gt;    C
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;worlds&#34;&gt;Worlds&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*LISP&lt;/code&gt; has a facility similar to database transactions where you can store the current state, change data and then either commit or rollback. &lt;code&gt;(NEWWORLD)&lt;/code&gt; will return a ticket to the current state; you can then change data with special versions of LISP primitives such as &lt;code&gt;SETQ2&lt;/code&gt; or &lt;code&gt;RPLACA2&lt;/code&gt;. &lt;code&gt;(REALWORLD)&lt;/code&gt; can then be used to commit or &lt;code&gt;(GETWORLD x)&lt;/code&gt; to roll back to the state at the time of ticket x.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* (SETQ2 A &amp;#39;42)
&amp;gt; NEWWORLD       04-15-82 RESTORED
&amp;gt;    42
* (SETQ W (NEWWORLD))
&amp;gt;    (((0 *UNDEF* . 42)) (NIL))
* (SETQ2 A 33)
&amp;gt;    33
* A
&amp;gt;    33
* (GETWORLD W)
&amp;gt;    NIL
* A
&amp;gt;    42
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Chapter 3 of &lt;a href=&#34;http://www.psg.com/~dlamkins/sl/&#34;&gt;Successful Lisp&lt;/a&gt; is a good introduction to Lisp if you&amp;rsquo;ve not used it before; it&amp;rsquo;s written for Common Lisp users but most is applicable here.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>LISP - Introduction</title>
      <link>https://try-mts.com/lisp-introduction/</link>
      <pubDate>Sun, 15 Mar 2015 04:16:52 +0000</pubDate>
      <guid>https://try-mts.com/lisp-introduction/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m still trying to restore ALGOL 68 from D4.0 and although I&amp;rsquo;ve had some great help from the folks on the H390-MTS list I have not yet got a working system running. While I continue to work on this, let&amp;rsquo;s look at a completely different language - LISP.&lt;/p&gt;
&lt;h2 id=&#34;lisp-overview&#34;&gt;LISP overview&lt;/h2&gt;
&lt;p&gt;LISP is one of the oldest high level languages still in use today, with the original version developed by John McCarthy at MIT in 1958. It combines procedural, functional and metaprogramming elements and has a unique syntax based on balanced parentheses that make it unlike any other language of its time.&lt;/p&gt;
&lt;p&gt;LISP gained popularity in the 1960s and 70s during the AI boom, when the language evolved rapidly and there was even special hardware built in order to run it efficiently. This died down towards the end of the 20th century, but it remains influential with concepts from LISP such as lambdas recently being added to languages such as Java and C++. Common Lisp implementation continue to be used today, and LISP has been used as an extension language, most famously in Emacs and AutoCAD.&lt;/p&gt;
&lt;h2 id=&#34;lisp-on-mts&#34;&gt;LISP on MTS&lt;/h2&gt;
&lt;p&gt;The primary LISP environment in MTS, &lt;code&gt;*LISP&lt;/code&gt;, was developed at the Mental Health Research Institute at UM. (Not an organisation you&amp;rsquo;d think would be implementing a programming language; the Wikipedia article on &lt;a href=&#34;http://en.wikipedia.org/wiki/Bruce_Wilcox&#34;&gt;Brian Wilcox&lt;/a&gt;, one of MTS LISP&amp;rsquo;s authors,  mentions that it was developed in order to play the game of Go.). It is based on LISP 1.5, one of the earliest versions of LISP that was in wide use, with some extensions drawn from later LISPs and customisations for MTS.&lt;/p&gt;
&lt;p&gt;There are a number of other LISPs available on MTS, such as the University of Tokyo&amp;rsquo;s UTILISP, that I will look at later.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get LISP running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;using-lisp&#34;&gt;Using &lt;code&gt;*LISP&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;LISP is an interpreted environment. Running &lt;code&gt;*LISP&lt;/code&gt; on its own will allow you to start entering expressions which will be immediately evaluated. It&amp;rsquo;s possible to load expressions from a text file by providing it as the &lt;code&gt;scards&lt;/code&gt; parameter to &lt;code&gt;*LISP&lt;/code&gt;; after the file is read you will remain in the LISP expression reader. To run a complete program and return to MTS afterwards, make the last expression be &lt;code&gt;(STOP)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is a rudimentary line editor available via &lt;code&gt;(EDIT fn)&lt;/code&gt; but it&amp;rsquo;s easier to do this outside of LISP.&lt;/p&gt;
&lt;p&gt;You can also save the current state of the system (to a binary file) with &lt;code&gt;(CHECKPOINT filename)&lt;/code&gt; and reload it in a later session with &lt;code&gt;(RESTORE filename)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to compile expressions to machine code with &lt;code&gt;(COMPILE fn)&lt;/code&gt; but this is unlikely to be worthwhile as MTS on Hercules is fast enough.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a transcript of a session where we run a Hello world program. This assumes the source code is contained in the file &lt;code&gt;hello.l&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.l
 
       1      ; LISP Hello World program
       2     (REPEAT &amp;#39;(PRINT &amp;#39;&amp;#34;Hello, world!&amp;#34;) 5)
       3     (STOP)
# $run *lisp scards=hello.l
# Execution begins   20:54:36 
     WELCOME TO LISP/MTS
&amp;gt;    Hello, world!
&amp;gt;    Hello, world!
&amp;gt;    Hello, world!
&amp;gt;    Hello, world!
&amp;gt;    Hello, world!
&amp;gt;    Hello, world!
# Execution terminated   20:54:36  T=0.002 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the message is actually printed six times, as the value of the &lt;code&gt;REPEAT&lt;/code&gt; expression is printed by the interpreter.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;MTS &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol08-LISPAndSLIPInMTS-Jan1983.pdf&#34;&gt;Volume 8&lt;/a&gt; describes the LISP language and its implementation in MTS &lt;code&gt;*LISP&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The original &lt;a href=&#34;http://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf&#34;&gt;LISP 1.5 reference&lt;/a&gt; from MIT gives a more formal description of the original version of the language.&lt;/p&gt;
&lt;p&gt;John McCarthy&amp;rsquo;s paper on the &lt;a href=&#34;http://www-formal.stanford.edu/jmc/history/lisp/lisp.html&#34;&gt;History of Lisp&lt;/a&gt; gives context on how and why LISP came about.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ALGOL W - Priority queue</title>
      <link>https://try-mts.com/algol-w-priority-queue/</link>
      <pubDate>Sun, 01 Mar 2015 03:13:42 +0000</pubDate>
      <guid>https://try-mts.com/algol-w-priority-queue/</guid>
      <description>&lt;p&gt;For our sample ALGOL W program we&amp;rsquo;ll implement a simple priority queue.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;A priority queue can be used to store items tagged with a  priority value and then retrieve them in order of priority. Several operations need to be implemented&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check if the queue is empty&lt;/li&gt;
&lt;li&gt;Insert an item&lt;/li&gt;
&lt;li&gt;Peek at what the highest priority item is&lt;/li&gt;
&lt;li&gt;Remove the highest priority item from the queue and return it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&#34;http://rosettacode.org/wiki/Priority_queue&#34;&gt;Rosetta Code&lt;/a&gt; for full details.&lt;/p&gt;
&lt;h2 id=&#34;implementation&#34;&gt;Implementation&lt;/h2&gt;
&lt;p&gt;We will use a binary heap to implement the queue: this allows O(log n) inserts and removals, and O(1) for peeking at the highest priority item, as the queue is kept in priority order after each operation.&lt;/p&gt;
&lt;p&gt;Rather than doing this from scratch I&amp;rsquo;m going to base the solution on &lt;a href=&#34;http://algs4.cs.princeton.edu/24pq/&#34;&gt;Robert Sedgewick and Kevin Wayne&amp;rsquo;s Java implementation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;interface&#34;&gt;Interface&lt;/h3&gt;
&lt;p&gt;First let&amp;rsquo;s define the data structures and interfaces available for users of the priority queue.&lt;/p&gt;
&lt;p&gt;Each item can be modeled as a record with an integer priority; the data associated with it is a string here but could be anything else&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;record Item(integer Priority; string Task);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The public interface can then be described using the following procedure prototypes:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;logical procedure PQ_Is_Empty;
procedure PQ_Insert(reference(Item) value New_Item);
reference(Item) procedure PQ_First;
reference(Item) procedure PQ_Delete_First;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;test-driver&#34;&gt;Test driver&lt;/h3&gt;
&lt;p&gt;To test our program, we need to insert some items in random order and then see if they dequeue in priority order:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    % Insert some tasks in random order %
    PQ_Insert(Item(3, &amp;#34;Clean drains&amp;#34;));
    PQ_Insert(Item(4, &amp;#34;Feed cat&amp;#34;));
    PQ_Insert(Item(5, &amp;#34;Make tea&amp;#34;));
    PQ_Insert(Item(1, &amp;#34;Solve RC tasks&amp;#34;));
    PQ_Insert(Item(2, &amp;#34;Tax return&amp;#34;));

    % Remove top priority item from queue and print %
    while not PQ_Is_Empty() do
    begin
        reference(Item) First;
        First := PQ_Delete_First;
        write(Priority(First), Task(First));
    end
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;data-structure-and-constraints&#34;&gt;Data structure and constraints&lt;/h3&gt;
&lt;p&gt;To store the queue, the binary heap implementation needs an array of items and an indication of its current size, which we can define as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;reference(Item) array PQ(1::PQ_Capacity);
integer PQ_Size;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;PQ_Capacity&lt;/code&gt; is the maximum size of the queue, which we&amp;rsquo;ll define as a constant; it does not seem possible to resize an array in ALGOL W. We&amp;rsquo;ll make the array and size global variables for simplicity: a better, if more verbose solution, would be to pass the data structure to each interface function, eg&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;reference(Item) procedure
    PQ_Delete_First(reference(Item) array PQ(1::PQ_Capacity), integer PQ_Size;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It would also have been nice to encapsulate both &lt;code&gt;PQ&lt;/code&gt; and &lt;code&gt;PQ_Size&lt;/code&gt; into a record, but ALGOL W records do not allow arrays as members. One way we could combine this into one variable is to make the array have bounds 0::PQ_Capacity and store the current size at location 0.&lt;/p&gt;
&lt;h3 id=&#34;public-function-implementation&#34;&gt;Public function implementation&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s then fairly simple to define the public functions:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;% Initialise a queue %
procedure PQ_Init();
begin
    PQ_Size := 0;
end;

% Return true if the queue is empty %
logical procedure PQ_Is_Empty();
begin
    PQ_Size = 0
end;

% Add a new item to the queue %
procedure PQ_Insert(reference(Item) value New_Item);
begin
    % Add to end of array and swim down to maintain %
    % priority order %
    PQ_Size := PQ_Size + 1;
    PQ(PQ_Size) := New_Item;
    PQ_Swim(PQ_Size);
end;

% Return the first item in the queue by priority %
reference(Item) procedure PQ_First;
begin
    PQ(1)
end;

% Return the first item and delete from queue %
reference(Item) procedure PQ_Delete_First;
begin
    reference(Item) First;
    
    PQ_Exchange(1, PQ_Size);
    First := PQ(PQ_Size);
    PQ_Size := PQ_Size - 1;
    PQ_Sink(1);
    % Just to be safe, null out reference at end %
    PQ(PQ_Size+1) := null;
    First
end;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;private-functions&#34;&gt;Private functions&lt;/h3&gt;
&lt;p&gt;These call a number of private functions - note that privacy is not enforced in ALGOL W however. First, to keep the heap organised, we need &lt;code&gt;PQ_Swim&lt;/code&gt; and &lt;code&gt;PQ_Sink&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;procedure PQ_Swim(integer value K);
begin
    while K &amp;gt; 1 and PQ_Greater(K div 2, K) do
    begin
        PQ_Exchange(K, K div 2);
        K := K div 2;
    end;
end;

procedure PQ_Sink(integer value k);
begin
    logical Done;
    Done := false;

    while 2*K &amp;lt;= PQ_Size and not Done do
    begin
        integer J;
        J := 2 * K;
        if J &amp;lt; PQ_Size and PQ_Greater(J, J+1)
        then
            J := J + 1;
        if PQ_Greater(K, J)
        then begin
            PQ_Exchange(K, J);
            K := J;
        end
        else begin
            Done := true;
        end;
    end
end;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These need to call primitive functions to exchange and compare:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;logical procedure PQ_Greater(integer value I; integer value J);
begin
    Priority(PQ(I)) &amp;gt; Priority(PQ(J))
end;

procedure PQ_Exchange(integer value I; integer value J);
begin
    reference(Item) Temp;
    Temp := PQ(I);
    PQ(I) := PQ(J);
    PQ(J) := Temp;
end;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;a-diversion---procedures-without-parameters&#34;&gt;A diversion - procedures without parameters&lt;/h2&gt;
&lt;p&gt;While writing this I got confused about the prototype for procedures without parameters; instead of&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;reference(Item) procedure PQ_First;
...
result := PQ_First;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I was writing&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;reference(Item) procedure PQ_First();
...
result := PQ_First();
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and got what looked like garbage as the Item reference returned.&lt;/p&gt;
&lt;p&gt;Trying to reproduce this with a simple example yielded the same problem:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *algolw
= begin
: integer procedure three;
: begin
: 3
: end;
:
: write(three);
: end.
: $endfile
  Options (UN022) :- main, debug 

  (MAIN)    0.016 seconds to compile,  size 448 bytes 

  Execution begins ...
                3

  0.001 seconds in execution

# $run *algolw
= begin
: integer procedure three();
: begin
: 3
: end;
:
: write(three());
: end.
: $endfile
  Options (UN022) :- main, debug 

  (MAIN)    0.016 seconds to compile,  size 504 bytes 

  Execution begins ...
          8632304

  0.001 seconds in execution
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Is the second example legal ALGOL W? I don&amp;rsquo;t think it is, based on the Stanford specification you need something in the parameter list - but there was no error from the compiler.&lt;/p&gt;
&lt;h2 id=&#34;running-the-program&#34;&gt;Running the program&lt;/h2&gt;
&lt;p&gt;Once that was fixed, compiling and running the program was simple.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *algolw scards=pqueue.alw par=deck
Options (UN022) :- main, debug
 Object program:  &amp;#34;-AWLOAD&amp;#34;

 (MAIN)    0.041 seconds to compile,  size 4280 bytes

# $run -awload

              1  Solve RC tasks
              2  Tax return
              3  Clean drains
              4  Feed cat
              5  Make tea

 0.001 seconds in execution
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts-on-algol-w&#34;&gt;Final thoughts on ALGOL W&lt;/h2&gt;
&lt;p&gt;ALGOL W definitely feels more polished and easy to use compared with ALGOL 60: the lack of stropping is a relief. It&amp;rsquo;s not the most powerful language - the restrictions on references stop it being much use for system programming - but I can see it would make a good choice as a teaching language. Apart from the issue I had above, the MTS compiler is much easier to use than the IBM ALGOL 60 one - I liked that it warns you if you use &lt;code&gt;goto&lt;/code&gt; or pass by name.&lt;/p&gt;
&lt;p&gt;Why didn&amp;rsquo;t ALGOL W become more popular? I think there were several factors&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only initially available on S/360 machines&lt;/li&gt;
&lt;li&gt;Not as good performance as FORTRAN&lt;/li&gt;
&lt;li&gt;Did not become the official replacement for ALGOL 60&lt;/li&gt;
&lt;li&gt;Wirth did not work further on ALGOL W, moving on to Pascal, which does feel similar.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next up, I will attempt to look at ALGOL 68 - if I can get it restored from the D4 tapes and running on D6.0.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ALGOL W - Language features</title>
      <link>https://try-mts.com/algol-w-language-features/</link>
      <pubDate>Sun, 22 Feb 2015 03:44:17 +0000</pubDate>
      <guid>https://try-mts.com/algol-w-language-features/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s look at ALGOL W in more detail, focusing how it differs from &lt;a href=&#34;https://try-mts.com/algol-60-language-features/&#34;&gt;ALGOL 60&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;representation&#34;&gt;Representation&lt;/h2&gt;
&lt;p&gt;ALGOL W is free format and case insensitive, though the convention was to have keywords in lower case and identifiers &lt;code&gt;Like_This&lt;/code&gt;. Identifiers can be up to 255 characters long.&lt;/p&gt;
&lt;p&gt;Keywords are reserved words so no stropping is required. A standard EBCDIC character set is used so there is no need for a different reference and hardware representation.&lt;/p&gt;
&lt;p&gt;As well as the comment syntax from ALGOL 60, comments can be started with &lt;code&gt;%&lt;/code&gt; and then terminated with either &lt;code&gt;%&lt;/code&gt; or &lt;code&gt;;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The final statement in a program or separately compiled unit (ie the last &lt;code&gt;end&lt;/code&gt;) should end with a period.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;% This is an ALGOL W program %
begin
    integer Population_Count;
end.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;new-data-types&#34;&gt;New data types&lt;/h2&gt;
&lt;p&gt;As well as the existing types from ALGOL 60 such as real and integer, ALGOL W provides long real, complex, bit and proper string types:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    complex C;
    bits B;
    string(50) S;
    c := 1 + 2I;
    b := #0000010F;
    s := &amp;#34;Hello, world&amp;#34;;
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Bit values are padded to 32 bits. String variables have to be declared with their length, such as 50 above; if the length is not provided the default is 16.&lt;/p&gt;
&lt;h2 id=&#34;new-operators&#34;&gt;New operators&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;div&lt;/code&gt; does integer division and &lt;code&gt;rem&lt;/code&gt; yields the remainder after division. As well as the logical operators like &lt;code&gt;and&lt;/code&gt;, there are also &lt;code&gt;shl&lt;/code&gt; and &lt;code&gt;shr&lt;/code&gt; for bitwise left and right shifts.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    write(#1 shl 2);
    write(42 rem 20);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above will print #00000004 and 2.&lt;/p&gt;
&lt;h2 id=&#34;records-and-references&#34;&gt;Records and references&lt;/h2&gt;
&lt;p&gt;Records group together simple variables like a struct in C. References can point to records but not fundamental types. A reference can be initialised, at which point memory for the record is allocated. After initialisation, fields can be accessed by field(variable).&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    record Person(integer Age; real Height; string(40) Name);
    reference(Person) P, Q;
    P := Person(30,, &amp;#34;N Wirth&amp;#34;);
    Q := Person;
    Age(Q) := 42;
    write(Name(P));
    write(Age(Q));
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note for the initialisation of &lt;code&gt;P&lt;/code&gt; we leave &lt;code&gt;Height&lt;/code&gt; uninitialised, and although memory for Q is allocated it is not initialised at all. This example will print &amp;ldquo;N Wirth&amp;rdquo; and 42.&lt;/p&gt;
&lt;p&gt;References can be used to point at records but not fundamental types. &lt;code&gt;null&lt;/code&gt; can be used to refer to an non-existent record. So a simple linked list could be defined as below, which will print 20.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    record List(integer Data; reference(List) Next);
    reference(List) L;
    L := List(10, null);
    Next(L) := List(20, null);
    write(Data(Next(L)));
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A reference can point to more than one type of record; which one can be determined at run time with the &lt;code&gt;is&lt;/code&gt; operator.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    record A(real Aa);
    record B(integer Bb);
    reference(A, B) R, S;
    R := A(3.0);
    S := B(4);
    S := R;
    if S is A then write(&amp;#34;Got an A&amp;#34;) else write(&amp;#34;Got a B&amp;#34;);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above will print &amp;ldquo;Got an A&amp;rdquo;. I think there is no garbage collector, so the memory allocated for the B is leaked.&lt;/p&gt;
&lt;h2 id=&#34;while-and-case&#34;&gt;&lt;code&gt;while&lt;/code&gt; and &lt;code&gt;case&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;As well as the three types of &lt;code&gt;for&lt;/code&gt; statements from ALGOL 60, ALGOL W has a &lt;code&gt;while&lt;/code&gt; statement. The below will print 256.0&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    real X;
    X := 2.0;
    while X &amp;lt; 100 do
    begin
        X := X * X;
    end;
    write(X);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;case&lt;/code&gt; allows switching by integer value; the below will print &amp;ldquo;Two&amp;rdquo;. Note there is no default case, so if J &amp;lt; 1 or &amp;gt; 4 there will be a runtime error.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    integer J;
    string(10) Word;
    J := 2;
    Word := case J of (&amp;#34;One&amp;#34;, &amp;#34;Two&amp;#34;, &amp;#34;Three&amp;#34;, &amp;#34;Four&amp;#34;);
    write(Word);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;procedures&#34;&gt;Procedures&lt;/h2&gt;
&lt;p&gt;The syntax for procedures differs from ALGOL 60 as types and calling conventions are specified in the function header:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    real procedure Fn(real X; integer value Y);
    begin
        X * Y
    end;
    write(Fn(3.0, 2));
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The default calling convention, as used in the above, is by name, but &lt;code&gt;*ALGOLW&lt;/code&gt; will give you a warning suggesting you use another convention instead.&lt;/p&gt;
&lt;p&gt;Putting &lt;code&gt;value&lt;/code&gt; after the type means pass by value. There is a new calling convention, &lt;code&gt;result&lt;/code&gt;, which is for pass-out variables. &lt;code&gt;value results&lt;/code&gt; allows both pass-in and pass-out without the side effects of pass by name.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    procedure P1(integer value A; integer result B; real value result C);
    begin
        A := 10;
        B := A;
        C := C + A
    end;

    integer A, B;
    real C;
    A := 1; B := 2; C := 3.0;

    P1(A, B, C);
    write(A, B, C);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above will display 1, 10, 13.&lt;/p&gt;
&lt;h2 id=&#34;inputoutput-and-library&#34;&gt;Input/Output and library&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s an extensive input/output library that has been customised for MTS.&lt;/p&gt;
&lt;p&gt;Basic input/output allows reading and writing each from a single channel. The below program will expect input formatted like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1001 &amp;#34;Niklaus&amp;#34; &amp;#34;Wirth&amp;#34;
Inventor of ALGOL W
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;begin
    integer ID;
    string(20) First, Last;
    string(80) Description;

    read(ID);
    readon(First, Last);
    readcard(Description);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;More complex formatting and carriage control can be specified. There is also support for reading/writing to more than one channel, and indexed access.&lt;/p&gt;
&lt;p&gt;The standard library contains mathematical functions and access to operating system information like time and date.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol16-ALGOLWInMTS-Sep1980.pdf&#34;&gt;MTS volume 16&lt;/a&gt; describes the language and the MTS extensions in detail.&lt;/p&gt;
&lt;p&gt;Another introduction to the language can be found at &lt;a href=&#34;http://www.everything2.com/index.pl?node=Algol%20W&#34;&gt;everything2&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ALGOL W - Introduction</title>
      <link>https://try-mts.com/algol-w-introduction/</link>
      <pubDate>Sun, 15 Feb 2015 02:47:29 +0000</pubDate>
      <guid>https://try-mts.com/algol-w-introduction/</guid>
      <description>&lt;p&gt;As discussed in the &lt;a href=&#34;https://try-mts.com/algol-60-josephus-problem/&#34;&gt;previous post&lt;/a&gt;, there were discussions on how to extend ALGOL after the experience of implementing ALGOL 60 on different systems. One such proposal was formulated by Niklaus Wirth and C A R Hoare. It did not get approved by the ALGOL committee, who chose what eventually became ALGOL 68 instead, so Wirth developed his proposal at Stanford and released it as ALGOL W.&lt;/p&gt;
&lt;h2 id=&#34;algol-w-overview&#34;&gt;ALGOL W overview&lt;/h2&gt;
&lt;p&gt;ALGOL W builds upon ALGOL 60 rather than making sweeping changes: its enhancements include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A simpler representation - no stropping of keywords&lt;/li&gt;
&lt;li&gt;String, bits, complex and record types&lt;/li&gt;
&lt;li&gt;References to records are allowed&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;while&lt;/code&gt; keyword&lt;/li&gt;
&lt;li&gt;A larger standard library, including input/output&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;algol-w-on-mts&#34;&gt;ALGOL W on MTS&lt;/h2&gt;
&lt;p&gt;ALGOL W was originally implemented at Stanford in a high level assembly language called PL360, which I will look at later; this was augmented with S/360 assembler support routines at the University of Newcastle, UM and elsewhere to build the MTS compiler &lt;code&gt;*ALGOLW&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It was the most popular of the ALGOL dialects on MTS, used for teaching programming fundamentals to students at several sites, and is well integrated with the operating system. The compiler features both a &amp;lsquo;compile, load and go&amp;rsquo; mode suitable for learning the language and a traditional output to object deck option.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get ALGOL W running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;compiling-using-algolw&#34;&gt;Compiling using &lt;code&gt;*ALGOLW&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;For compile, load and go mode just run &lt;code&gt;*ALGOLW&lt;/code&gt; and type the desired program in directly. Finish it with &lt;code&gt;$endfile&lt;/code&gt; and the system will compile it, print any errors and if OK will execute it once before returning you to the MTS prompt.&lt;/p&gt;
&lt;p&gt;For object mode, run &lt;code&gt;*ALGOLW&lt;/code&gt; with the command line option &lt;code&gt;par=deck&lt;/code&gt;. It will read source from &lt;code&gt;scards&lt;/code&gt; (by default &lt;code&gt;*source*&lt;/code&gt; ie standard input) and will write object files to &lt;code&gt;spunch&lt;/code&gt; (by default the temporary file &lt;code&gt;-awload&lt;/code&gt;). The object file can then be &lt;code&gt;$run&lt;/code&gt; directly without referencing any library.&lt;/p&gt;
&lt;p&gt;Other &lt;code&gt;*ALGOLW&lt;/code&gt; compilation parameters are listed in MTS Volume 16.&lt;/p&gt;
&lt;h2 id=&#34;hello-world---compile-load-and-go&#34;&gt;Hello world - compile, load and go&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a simple program to print &amp;ldquo;Hello, world!&amp;rdquo; five times using the compile, load and go model. After I run &lt;code&gt;*algolw&lt;/code&gt; I can type a compiler control statement at the &lt;code&gt;=&lt;/code&gt; prompt but instead I enter &lt;code&gt;begin&lt;/code&gt;, signaling that source code is being entered. &lt;code&gt;*algolw&lt;/code&gt; prompts for the remainder of the program with &lt;code&gt;:&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *algolw
= begin

 Compilation begins ...
:    integer I;
:    for I := 1 until 5 do
:        write(&amp;#34;Hello, world!&amp;#34;);
:end.
:$endfile

 Options (UN022) :- main, debug

 (MAIN)    0.012 seconds to compile,  size 416 bytes


 Execution begins ...


 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!

 0.001 seconds in execution
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;hello-world---object-file&#34;&gt;Hello world - object file&lt;/h2&gt;
&lt;p&gt;Here I use the two step process of compiling to an object file and then running it. Assume the source code is in file &lt;code&gt;hello.alw&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.alw

     1     begin
     2         % Hello World program for ALGOL W %
     3         integer I;
     4         for I := 1 until 5 do
     5         begin
     6              write(&amp;#34;Hello, world!&amp;#34;);
     7         end
     8     end.

# $run *algolw scards=hello.alw par=deck

 Options (UN022) :- main, debug
 Object program:  &amp;#34;-AWLOAD&amp;#34;

 (MAIN)    0.007 seconds to compile,  size 416 bytes

# $run -awload

 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!

 0.001 seconds in execution
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol16-ALGOLWInMTS-Sep1980.pdf&#34;&gt;MTS volume 16&lt;/a&gt; contains a very readable description of the language, the compiler options and how it integrates with MTS.&lt;/p&gt;
&lt;p&gt;Wirth&amp;rsquo;s Turing award lecture, &lt;a href=&#34;http://dl.acm.org/citation.cfm?id=1283920.1283941&amp;amp;coll=DL&amp;amp;dl=GUIDE&amp;amp;CFID=625057847&amp;amp;CFTOKEN=21370838&#34;&gt;From programming language design to computer construction&lt;/a&gt;, puts ALGOL W in context with Wirth&amp;rsquo;s other work on Pascal and Modula 2.&lt;/p&gt;
&lt;p&gt;The original ALGOL W reference manual from the Stanford implementation can be found &lt;a href=&#34;http://i.stanford.edu/pub/cstr/reports/cs/tr/71/230/CS-TR-71-230.pdf&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ALGOL 60 - Josephus problem</title>
      <link>https://try-mts.com/algol-60-josephus-problem/</link>
      <pubDate>Sun, 08 Feb 2015 04:33:41 +0000</pubDate>
      <guid>https://try-mts.com/algol-60-josephus-problem/</guid>
      <description>&lt;p&gt;For our ALGOL 60 sample program we&amp;rsquo;ll have a quick look at implementing a solution to the Josephus problem.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;Imagine n prisoners standing in a circle, numbered sequentially from 0 to n-1. An executioner walks around the circle, starting at prisoner 0, and kills every kth prisoner, removing them from the circle. As this continues, the circle becomes smaller and smaller; the last prisoner remaining is freed.&lt;/p&gt;
&lt;p&gt;An example for n=11 prisoners and k=3. Prisoners 2, 5 and 8 will be killed on the first trip round the circle. On the second trip, it&amp;rsquo;s goodbye to prisoners 0, 4 and 9. Continuing this leaves prisoner 6 as the survivor.&lt;/p&gt;
&lt;p&gt;The task, given any n and k (both which should be greater than zero), find the prisoner who is freed.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&#34;http://rosettacode.org/wiki/Josephus_problem&#34;&gt;Rosetta Code&lt;/a&gt; for a full description and sample code in other languages.&lt;/p&gt;
&lt;h2 id=&#34;solution&#34;&gt;Solution&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a fairly simple iterative solution to the main function to determine the last survivor:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    &amp;#39;COMMENT&amp;#39; Find surviving prisoner from circle of N;
    &amp;#39;COMMENT&amp;#39; after killing each Kth prisoner;
    &amp;#39;INTEGER&amp;#39; &amp;#39;PROCEDURE&amp;#39; JOSEPHUS(N, K);
    &amp;#39;VALUE&amp;#39; N, K;
    &amp;#39;INTEGER&amp;#39; N, K;
    &amp;#39;BEGIN&amp;#39;
        &amp;#39;INTEGER&amp;#39; INDEX, PRISONER;
         PRISONER := 0;
        &amp;#39;FOR&amp;#39; INDEX := 1 &amp;#39;STEP&amp;#39; 1 &amp;#39;UNTIL&amp;#39; N &amp;#39;DO&amp;#39;
        &amp;#39;BEGIN&amp;#39;
            JOSEPHUS := PRISONER := MODULUS(PRISONER + K, INDEX);
        &amp;#39;END&amp;#39;
    &amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It would seem that we could just use &lt;code&gt;JOSEPHUS&lt;/code&gt; as the variable and not need &lt;code&gt;PRISONER&lt;/code&gt; but ALGOL only allows return value variables to appear on the left hand side of an expression.&lt;/p&gt;
&lt;p&gt;Next, we need to define the &lt;code&gt;MODULUS&lt;/code&gt; function to give the remainder after dividing two integers&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    &amp;#39;COMMENT&amp;#39; Finds the remainder of X and Y for positive values;
    &amp;#39;INTEGER&amp;#39; &amp;#39;PROCEDURE&amp;#39; MODULUS(X, Y);
    &amp;#39;VALUE&amp;#39; X, Y;
    &amp;#39;INTEGER&amp;#39; X, Y;
    &amp;#39;BEGIN&amp;#39;
        MODULUS := X - (Y * (X &amp;#39;/&amp;#39; Y));
    &amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we use the integer division operator ÷ (written as &lt;code&gt;&#39;/&#39;&lt;/code&gt; in the IBM hardware representation) as the regular division operator / on integers will round either up or down depending on the value.&lt;/p&gt;
&lt;p&gt;Finally, for the main program we can call the &lt;code&gt;JOSEPHUS&lt;/code&gt; function and output the integer result: we use 41 prisoners and k=3:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
    &amp;#39;COMMENT&amp;#39; Main program;
    OUTINTEGER(1, JOSEPHUS(41, 3));
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So writing this was fairly easy. Quoting all keywords still feels unnatural so I normally put them in after writing out the program in full.  I had to look up the difference between the two division operators but expressing the main flow was simple. What tripped me up on first compile was the comment syntax: I was using &lt;code&gt;COMMENT abc&lt;/code&gt; which the compiler appeared to accept but would sometimes swallow the next line. Fixing this to quote the COMMENT and terminate the line with a semicolon fixed this (eg &lt;code&gt;&#39;COMMENT&#39; abc;&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&#34;running-the-solution&#34;&gt;Running the solution&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how this looks when we compile and run the program. Don&amp;rsquo;t forger to destroy or empty the output object file first otherwise you will get strange results after each recompile.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $destroy -load
 File &amp;#34;-LOAD&amp;#34; has been destroyed.

# $run *algol scards=josephus.al spunch=-load
Execution begins   21:39:02
 i
  LEVEL 1JUL67                            OS ALGOL F                       DATE  FEB 05 1915
                                       SOURCE PROGRAM                               PAGE 001
   SC     SOURCE STATEMENT
  00000   &amp;#39;BEGIN&amp;#39;
  00000       &amp;#39;COMMENT&amp;#39; Finds the remainder of X and Y for positive values;
  00000       &amp;#39;INTEGER&amp;#39; &amp;#39;PROCEDURE&amp;#39; MODULUS(X, Y);
  00001       &amp;#39;VALUE&amp;#39; X, Y;
  00002       &amp;#39;INTEGER&amp;#39; X, Y;
  00003       &amp;#39;BEGIN&amp;#39;
  00003           MODULUS := X - (Y * (X &amp;#39;/&amp;#39; Y));
  00004       &amp;#39;END&amp;#39;
  00004
  00004       &amp;#39;COMMENT&amp;#39; Find surviving prisoner from circle of N;
  00005       &amp;#39;COMMENT&amp;#39; after killing each Kth prisoner;
  00005       &amp;#39;INTEGER&amp;#39; &amp;#39;PROCEDURE&amp;#39; JOSEPHUS(N, K);
  00006       &amp;#39;VALUE&amp;#39; N, K;
  00007       &amp;#39;INTEGER&amp;#39; N, K;
  00008       &amp;#39;BEGIN&amp;#39;
  00008           &amp;#39;INTEGER&amp;#39; INDEX, PRISONER;
  00009            PRISONER := 0;
  00010           &amp;#39;FOR&amp;#39; INDEX := 1 &amp;#39;STEP&amp;#39; 1 &amp;#39;UNTIL&amp;#39; N &amp;#39;DO&amp;#39;
  00010           &amp;#39;BEGIN&amp;#39;
  00010               JOSEPHUS := PRISONER := MODULUS(PRISONER + K, INDEX);
  00011           &amp;#39;END&amp;#39;
  00011       &amp;#39;END&amp;#39;
  00011
  00011       &amp;#39;COMMENT&amp;#39; Main program;
  00012       OUTINTEGER(1, JOSEPHUS(41, 3));
  00013   &amp;#39;END&amp;#39;
 i
                                       IDENTIFIER TABLE                             PAGE 002
  PBN SC    PBN      NAME   TYPE  DM DSP       NAME   TYPE  DM DSP       NAME   TYPE  DM DSP
            SURR                  PR LN                     PR LN                     PR LN

  001 00000 000      JOSEPH I P   02 074       MODULU I P   02 070

  002 00000 001      MODULU I P   02 070       X      I  V     020       Y      I  V     028

  003 00005 001      INDEX  I        030       JOSEPH I P   02 074       K      I  V     028
                     N      I  V     020       PRISON I        034
 i
                                   STORAGE REQUIREMENTS (DECIMAL)                   PAGE 003

  OBJECT MODULE SIZE 840 BYTES.
  DATA STORAGE AREA SIZES
    PBN   BYTES       PBN   BYTES       PBN   BYTES       PBN   BYTES       PBN   BYTES
    001      36       002      48       003      68
Execution terminated   21:39:02  T=0.016

# $run -load+*algollib
Execution begins   21:39:10
         +30



 END OF ALGOL PROGRAM EXECUTION

Execution terminated   21:39:10  T=0.003
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see in the compiler identifier table output that although we can use long identifiers they are truncated to six characters.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts-on-algol-60&#34;&gt;Final thoughts on ALGOL 60&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a lot to like in ALGOL 60: the language definition is more precise than the informal descriptions used before; the block structure and variable scoping; the powerful control statements. As a programmer with a C background it appears much more familiar then FORTRAN.&lt;/p&gt;
&lt;p&gt;However it does feel like an interim release rather than a fully developed language; I think this is partially due to its academic origins rather than from a computer company looking to sell its hardware. The different language representations and call-by-name are a bit too clever and the emphasis on machine independence went too far - the lack of any I/O means that no useful program could be portable. Another factor not evident when running MTS today is that the FORTRAN development environment and compilers are much more advanced so for any computationally expensive workload you would have been better off with FORTRAN.&lt;/p&gt;
&lt;p&gt;With the experience of implementing compilers in ALGOL 60, the group responsible for the original report looked at what changes should be made for the next version of ALGOL. In the next post I&amp;rsquo;ll look at one of these, ALGOL W.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ALGOL 60 - Language features</title>
      <link>https://try-mts.com/algol-60-language-features/</link>
      <pubDate>Sun, 01 Feb 2015 02:43:31 +0000</pubDate>
      <guid>https://try-mts.com/algol-60-language-features/</guid>
      <description>&lt;h2 id=&#34;algol-60-and-the-revised-report&#34;&gt;ALGOL 60 and the Revised Report&lt;/h2&gt;
&lt;p&gt;As mentioned in the &lt;a href=&#34;https://try-mts.com/algol-60-introduction/&#34;&gt;previous post&lt;/a&gt;, ALGOL was designed by an international committee of computer scientists: their goal was to create a universal language for expressing algorithms without recourse to hardware specific features.&lt;/p&gt;
&lt;p&gt;The result of their discussions was the ALGOL 60 Report, with a slightly corrected version known as the Revised Report released a few years later. This was one of the first attempts to formally specify a language, using Backus-Naur form (JW Backus and Peter Naur were both on the ALGOL committee).&lt;/p&gt;
&lt;h2 id=&#34;representations&#34;&gt;Representations&lt;/h2&gt;
&lt;p&gt;ALGOL was intended as a universal language, but at the time of its creation there was no universal character set such as Unicode or even ASCII; as a result, the report defines three representations of the language:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A reference language used by the committee in the report to describe ALGOL&lt;/li&gt;
&lt;li&gt;Publication language, which modifies the reference language for use in handwritten or published documents&lt;/li&gt;
&lt;li&gt;Hardware representations, which modifies the reference language based on the character set of the hardware it is running on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An example is the exponentiation operator.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the reference language the upwards facing arrow ↑ is used, eg a ↑ b.&lt;/li&gt;
&lt;li&gt;In the publication language, superscript is used for the exponent, eg a&lt;sup&gt;b&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;In the hardware representation used by IBM, &lt;code&gt;&#39;POWER&#39;&lt;/code&gt; or &lt;code&gt;**&lt;/code&gt; is used, eg &lt;code&gt;a ** b&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One of the most important differences in representation were basic symbols like begin or end. In the reference and publication language these were usually underlines, eg &lt;u&gt;begin&lt;/u&gt;. In many hardware representations, including IBM&amp;rsquo;s, they were enclosed in quotes eg &lt;code&gt;&#39;BEGIN&#39;&lt;/code&gt;,&lt;/p&gt;
&lt;p&gt;A summary of the differences between the reference language and the IBM hardware representation  is shown below; in some cases there is more than once choice for the hardware representation.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Reference   Hardware       Hardware alt
×           * 
↑           &amp;#39;POWER&amp;#39;        **
÷           &amp;#39;/&amp;#39;
&amp;gt;           &amp;#39;GREATER&amp;#39;      &amp;gt;
&amp;lt;           &amp;#39;LESS&amp;#39;         &amp;lt;
≤           &amp;#39;NOTGREATER&amp;#39;   &amp;lt;=
≥           &amp;#39;NOTLESS&amp;#39;      &amp;gt;=
≠           &amp;#39;NOT EQUAL&amp;#39;    ¬=
≡           &amp;#39;EQUIV&amp;#39;
⊃           &amp;#39;IMPL&amp;#39;
∧           &amp;#39;AND&amp;#39;          &amp;amp;
∨           &amp;#39;OR&amp;#39;           |
¬           &amp;#39;NOT&amp;#39;
:            :             ..
;            ;             .,
₁₀          &amp;#39;
[           (/
]           /)
‘           &amp;#39;(&amp;#39;
’           &amp;#39;)&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &amp;ldquo;₁₀&amp;rdquo; is a scale factor like &amp;ldquo;E&amp;rdquo; in the C language, eg 1234 could be expressed as &amp;ldquo;1.234₁₀3&amp;rdquo;;&lt;/p&gt;
&lt;p&gt;In the rest of this post, I will use the reference language in the body text and the IBM hardware representation in the monospaced examples.&lt;/p&gt;
&lt;h2 id=&#34;blocks-and-statements&#34;&gt;Blocks and statements&lt;/h2&gt;
&lt;p&gt;ALGOL 60 is free format, with statements separated by semicolons. It is possible to group statements into a block, marked by &lt;u&gt;begin&lt;/u&gt; and &lt;u&gt;end&lt;/u&gt; and optionally labeled. Comments are started with the keyword &lt;u&gt;comment&lt;/u&gt; and ended with a &lt;code&gt;;&lt;/code&gt;; this is not needed at the end of blocks.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;COMMENT&amp;#39; Block example;
&amp;#39;BEGIN&amp;#39;
    X: &amp;#39;BEGIN&amp;#39;
        FN1(10);
        FN1(20);
    &amp;#39;END&amp;#39; this is the end of the X block
&amp;#39;END&amp;#39; and this the unnamed block
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;types-and-variables&#34;&gt;Types and variables&lt;/h2&gt;
&lt;p&gt;ALGOL 60 supports integer, real and Boolean types and variables. There is no restriction in the standard on variable name length, but &lt;code&gt;*ALGOL&lt;/code&gt; limits them to six characters. All variables must be declared with their types and variable lifetime is the block. The standard does define a storage class called &lt;u&gt;own&lt;/u&gt;, similar to C&amp;rsquo;s &lt;code&gt;static&lt;/code&gt;, but this is not supported by the IBM compiler.&lt;/p&gt;
&lt;p&gt;The below will print &amp;ldquo;3 4 3&amp;rdquo;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
    &amp;#39;INTEGER&amp;#39; I, J;
    I := 3;
    OUTINTEGER(1, I);
    &amp;#39;BEGIN&amp;#39;
        &amp;#39;INTEGER&amp;#39; I;
        &amp;#39;BOOLEAN&amp;#39; STATE;
        I := 4;
        OUTINTEGER(1, I);
    &amp;#39;END&amp;#39;
    OUTINTEGER(1, I);
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Multi-dimensional arrays can be declared, with variable bounds:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;INTEGER&amp;#39; &amp;#39;ARRAY&amp;#39; A(/-1:+1, M:N/);
A(/0,15/) := 42;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Strings are supported only as constant parameters to function; it is not possible to declare string variables.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OUTSTRING(1, &amp;#39;(&amp;#39;This is a string&amp;#39;)&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;expressions&#34;&gt;Expressions&lt;/h2&gt;
&lt;p&gt;The usual arithmetic expressions are available; it&amp;rsquo;s possible to include an if/else in such expressions. Unusually, Boolean expressions include operators for implies and equivalent.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
    &amp;#39;INTEGER&amp;#39; X, Y;
    &amp;#39;BOOLEAN&amp;#39; A, B;
    X := 3;
    Y := 4;
    A := &amp;#39;TRUE&amp;#39;;
    B := &amp;#39;FALSE&amp;#39;;
    OUTINTEGER(1, X + Y - (&amp;#39;IF&amp;#39; A &amp;#39;OR&amp;#39; B &amp;#39;THEN&amp;#39; 1 &amp;#39;ELSE&amp;#39; 2));
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above will print 6.&lt;/p&gt;
&lt;h2 id=&#34;control-statements&#34;&gt;Control statements&lt;/h2&gt;
&lt;p&gt;As well as the regular &lt;u&gt;goto&lt;/u&gt; statement, the &lt;u&gt;switch&lt;/u&gt; statement allow selection of a statement label to jump to.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;if&lt;/u&gt; &amp;hellip; &lt;u&gt;then&lt;/u&gt; &amp;hellip; &lt;u&gt;else&lt;/u&gt; can be used for conditional execution. Another &lt;u&gt;if&lt;/u&gt; cannot follow the &lt;u&gt;then&lt;/u&gt; unless it is contained in a block. to prevent ambiguity of which &lt;u&gt;if&lt;/u&gt; then &lt;u&gt;else&lt;/u&gt; is attached to.&lt;/p&gt;
&lt;p&gt;The &lt;u&gt;for&lt;/u&gt; statement is used for repetition: there are three forms:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
    &amp;#39;INTEGER&amp;#39; J;
    &amp;#39;FOR&amp;#39; J := 1, 2, 3 &amp;#39;DO&amp;#39; OUTINTEGER(1, J);
    &amp;#39;FOR&amp;#39; J := 10 &amp;#39;STEP&amp;#39; 5 &amp;#39;UNTIL&amp;#39; 20 &amp;#39;DO&amp;#39; OUTINTEGER(1, J);
    &amp;#39;FOR&amp;#39; J := 100, J*2 &amp;#39;WHILE&amp;#39; J &amp;#39;LESS&amp;#39; 500 &amp;#39;DO&amp;#39; OUTINTEGER(1, J);
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will print&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1, 2, 3&lt;/li&gt;
&lt;li&gt;10, 15, 20&lt;/li&gt;
&lt;li&gt;100, 200, 400&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;procedures&#34;&gt;Procedures&lt;/h2&gt;
&lt;p&gt;Procedures support pass by value or pass by name parameters. Pass by value is familiar: a copy is made of the parameter for those identified with &lt;u&gt;value&lt;/u&gt; in the procedure preamble. The following will print 42:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
    &amp;#39;INTEGER&amp;#39; &amp;#39;PROCEDURE&amp;#39; ADD2(X, Y);
    &amp;#39;VALUE&amp;#39; X, Y;
    &amp;#39;INTEGER&amp;#39; X, Y;
    &amp;#39;BEGIN&amp;#39;
        ADD2 := X + Y;
    &amp;#39;END&amp;#39;

    OUTINTEGER(1, ADD2(20, 22));
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pass by name is similar to pass by reference in C++ or Java in that it allows output to be made to parameters; it also enables &lt;a href=&#34;http://en.wikipedia.org/wiki/Jensen%27s_Device&#34;&gt;Jensen&amp;rsquo;s device&lt;/a&gt;. Take for example the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
    &amp;#39;PROCEDURE&amp;#39; INC(A, B);
    &amp;#39;INTEGER&amp;#39; A, B;
    &amp;#39;BEGIN&amp;#39;
        A := A + 1;
        B := B + 1;
    &amp;#39;END&amp;#39;

    &amp;#39;INTEGER&amp;#39; &amp;#39;ARRAY&amp;#39; X(/10/);
    &amp;#39;INTEGER&amp;#39; J;

    X(/1/) := 10;
    X(/2/) := 20;

    J := 1;
    INC(J, X(/J/));

    OUTINTEGER(1, J);
    OUTINTEGER(1, X(/2/));
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will print 2 (as J has been incremented by the line A := A + 1) and 21 (as X[J] was passed in by name, it was evaluated as X[2] in the line B := B + 1).&lt;/p&gt;
&lt;h2 id=&#34;standard-functions-and-inputoutput&#34;&gt;Standard functions and input/output&lt;/h2&gt;
&lt;p&gt;There are a small number of standard functions defined by the language, mostly mathematical (eg ABS, SIGN, SQRT, SIN, LN). The ENTIER function accepts a real number as parameter and returns an integer.&lt;/p&gt;
&lt;p&gt;No input/output functions are defined at all in the standard, due to the desire for the language to be device independent and as at the time there was no common agreement of what input/output facilities could be provided across all available hardware. The IBM compiler provides PUT and GET for low level I/O, SYSACT for stream manipulation and a number of functions for input or output of variables, eg ININTEGER, OUTREAL.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;http://www.masswerk.at/algol60/report.htm&#34;&gt;Revised Report on the Algorithmic Language Algol 60&lt;/a&gt; is the standard for the language. Donald Knuth wrote an article on &lt;a href=&#34;http://www.ursuletz.com/~asb/teaching/cs415-fall05/docs/algol-3.pdf&#34;&gt;the remaining troublespots in ALGOL 60&lt;/a&gt; on some of the ambiguities in the standard.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://www.bitsavers.org/pdf/ibm/360/algol/C28-6615-1_algolLang_Apr67.pdf&#34;&gt;original IBM documentation for the compiler&lt;/a&gt; contains most of the text of the Revised Report annotated with examples using IBM&amp;rsquo;s representation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Updated 6-Feb-2015 to fix comment syntax&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ALGOL 60 - Introduction</title>
      <link>https://try-mts.com/algol-60-introduction/</link>
      <pubDate>Sun, 25 Jan 2015 09:43:30 +0000</pubDate>
      <guid>https://try-mts.com/algol-60-introduction/</guid>
      <description>&lt;p&gt;In this series of posts we&amp;rsquo;ll look at ALGOL, one of the foundational languages for modern programming, and the ALGOL 60 implementation on MTS.&lt;/p&gt;
&lt;h2 id=&#34;the-algol-family-of-languages-and-algol-60&#34;&gt;The ALGOL family of languages and ALGOL 60&lt;/h2&gt;
&lt;p&gt;Most programming languages spring from a single person or a company; ALGOL is fairly unique in that it was originally designed by a committee of European and American computer scientists in the mid 1950s. The first version to be widely implemented was ALGOL 60, which is what we will look at here. A large number of improvements to ALGOL 60 were proposed or developed: one, by Niklaus Wirth, became ALGOL W. The next standardised version was ALGOL 68 but this was not widely adopted.&lt;/p&gt;
&lt;p&gt;Although it is not in wide use today, ALGOL introduced many influential concepts to programming languages, such as block scoping, so can be considered the ancestor of languages such as Pascal and C.&lt;/p&gt;
&lt;h2 id=&#34;algol-60-on-mts&#34;&gt;ALGOL 60 on MTS&lt;/h2&gt;
&lt;p&gt;The ALGOL compiler provided by IBM for S/360 machines is available on MTS under the name &lt;code&gt;*ALGOL&lt;/code&gt;. This follows the ALGOL 60 specification fairly closely. A number of small changes were made to the compiler to make it work under MTS.&lt;/p&gt;
&lt;p&gt;There are also ALGOL W and ALGOL 68 compilers on MTS, the former being the most popular version in use at the time of D6.0; I will look at these in later posts.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get ALGOL 60 running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;compiling-using-algol&#34;&gt;Compiling using &lt;code&gt;*ALGOL&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*ALGOL&lt;/code&gt; will read source from &lt;code&gt;scards&lt;/code&gt; (by default &lt;code&gt;*source*&lt;/code&gt; ie standard input) and will write object files to &lt;code&gt;spunch&lt;/code&gt;. Unlike some other MTS compilers you need to provide a filename for &lt;code&gt;spunch&lt;/code&gt; as there is no default.&lt;/p&gt;
&lt;p&gt;One gotcha is that &lt;code&gt;*ALGOL&lt;/code&gt; will not clear out the file used for writing object code, so you need to run &lt;code&gt;$empty&lt;/code&gt; on it before recompiling each time.&lt;/p&gt;
&lt;p&gt;Other &lt;code&gt;*ALGOL&lt;/code&gt; compilation parameters are listed in MTS Volume 2X.&lt;/p&gt;
&lt;p&gt;To run ALGOL 60 programs you need to concatenate the runtime library &lt;code&gt;*ALGOLLIB&lt;/code&gt; with the object file. Say your object code is in the file &lt;code&gt;-load&lt;/code&gt; then to run it you would do:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run -load+*algollib
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a simple program to print &amp;ldquo;Hello, world!&amp;rdquo; five times.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#39;BEGIN&amp;#39;
&amp;#39;COMMENT&amp;#39; Hello World program for ALGOL 60;
    &amp;#39;INTEGER&amp;#39; I;
    &amp;#39;FOR&amp;#39; I := 1 &amp;#39;STEP&amp;#39; 1 &amp;#39;UNTIL&amp;#39; 5 &amp;#39;DO&amp;#39;
    &amp;#39;BEGIN&amp;#39;
         OUTSTRING(1, &amp;#39;(&amp;#39;Hello, world!&amp;#39;)&amp;#39;);
         SYSACT(1, 14, 1);
    &amp;#39;END&amp;#39;
&amp;#39;END&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Indentation is not important except to make the program readable. Note the quotation marks around keywords like &lt;code&gt;&#39;INTEGER&#39;&lt;/code&gt; to distinguish them from identifiers: this is known as stropping.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OUTSTRING&lt;/code&gt; and &lt;code&gt;SYSACT&lt;/code&gt; are not part of ALGOL 60 but are library functions provided by IBM; the &lt;code&gt;SYSACT&lt;/code&gt; call here is used to start a new line on the output device.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a terminal log of how to compile and run the program. This assumes the source code is in file &lt;code&gt;hello.al&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *algol scards=hello.al spunch=-load
 i
  LEVEL 1JUL67                            OS ALGOL F                       DATE  JAN 11 1915
                                       SOURCE PROGRAM                               PAGE 001
   SC     SOURCE STATEMENT
  00000   &amp;#39;BEGIN&amp;#39; 
  00000       &amp;#39;COMMENT&amp;#39; Hello World program for ALGOL 60; 
  00000       &amp;#39;INTEGER&amp;#39; I;
  00001       &amp;#39;FOR&amp;#39; I := 1 &amp;#39;STEP&amp;#39; 1 &amp;#39;UNTIL&amp;#39; 5 &amp;#39;DO&amp;#39;
  00001       &amp;#39;BEGIN&amp;#39;
  00001           OUTSTRING(1, &amp;#39;(&amp;#39;Hello, world!  &amp;#39;)&amp;#39;);
  00002           SYSACT(1, 14, 1);
  00003       &amp;#39;END&amp;#39;
  00003   &amp;#39;END&amp;#39;
 i
                                       IDENTIFIER TABLE                             PAGE 002
  PBN SC    PBN      NAME   TYPE  DM DSP       NAME   TYPE  DM DSP       NAME   TYPE  DM DSP
            SURR                  PR LN                     PR LN                     PR LN

  001 00000 000      I      I        018
 i
                                   STORAGE REQUIREMENTS (DECIMAL)                   PAGE 003

  OBJECT MODULE SIZE 540 BYTES.
  DATA STORAGE AREA SIZES
    PBN   BYTES       PBN   BYTES       PBN   BYTES       PBN   BYTES       PBN   BYTES
    001      48

# $run -load+*algollib
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!
 Hello, world!

 END OF ALGOL PROGRAM EXECUTION
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you change the source file and want to recompile, remember to do &lt;code&gt;%empty -load&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;As always, the &lt;a href=&#34;http://en.wikipedia.org/wiki/ALGOL&#34;&gt;Wikipedia&lt;/a&gt; page provides a good overview of the language&amp;rsquo;s history and key features.&lt;/p&gt;
&lt;p&gt;There is no MTS Volume on ALGOL 60 (there is one on ALGOL W, however). Some information on the &lt;code&gt;*ALGOL&lt;/code&gt; compiler and &lt;code&gt;*ALGOLLIB&lt;/code&gt; library can be found in &lt;a href=&#34;http://hdl.handle.net/2027.42/79581&#34;&gt;the MTS Volume on obsolete commands&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The original IBM documentation for the compiler can be found &lt;a href=&#34;http://www.bitsavers.org/pdf/ibm/360/algol/C28-6615-1_algolLang_Apr67.pdf&#34;&gt;at Bitsavers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Updated 6-Feb-2015 to fix comment syntax.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>MAD - Mergesort</title>
      <link>https://try-mts.com/mad-mergesort/</link>
      <pubDate>Sun, 18 Jan 2015 02:49:06 +0000</pubDate>
      <guid>https://try-mts.com/mad-mergesort/</guid>
      <description>&lt;p&gt;For our sample program in MAD, let&amp;rsquo;s look at a classic sorting algorithm, mergesort.&lt;/p&gt;
&lt;h2 id=&#34;the-task&#34;&gt;The task&lt;/h2&gt;
&lt;p&gt;The task description and pseudo-code can be found at &lt;a href=&#34;http://rosettacode.org/wiki/Sorting_algorithms/Merge_sort&#34;&gt;Rosetta Code&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-implementation&#34;&gt;The implementation&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s write this as two parts: a driver program to set up a vector of integers and the actual function to do mergesort. The mergesort function should be able to take any vector size as input.&lt;/p&gt;
&lt;h2 id=&#34;the-driver&#34;&gt;The driver&lt;/h2&gt;
&lt;p&gt;For the driver we&amp;rsquo;ll just define a vector of integers and call the mergesort function. We could extend this to read the integers from a file if needed. We&amp;rsquo;ll also need a way to display the vector before and after the sort, which we&amp;rsquo;ll factor into a separate internal function.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     CONSTANT LENGTH = 15
     VECTOR VALUES NUMBERS = 9, 12, 3, 1, 6, 8, 2, 5, 14, 13, 11, 7, 10, 4, 0

     PRINT COMMENT &amp;#34;Before sort&amp;#34;
     DISPLAY.(NUMBERS, LENGTH)

     MERGESRT.(0, LENGTH, NUMBERS)

     PRINT COMMENT &amp;#34;After sort&amp;#34;
     DISPLAY.(NUMBERS, LENGTH)

*    Display each value in V
     INTERNAL FUNCTION DISPLAY.(V, LEN)
          INTEGER V(1), LEN
          LOOP FOR I=0, 1, I &amp;gt;= LEN
               PRINT RESULTS V(I)
          END LOOP
          FUNCTION RETURN
     END OF FUNCTION

     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-mergesort-function&#34;&gt;The mergesort function&lt;/h2&gt;
&lt;p&gt;The mergesort function is recursive, so we need to use an external function with the &lt;code&gt;RECURSIVE&lt;/code&gt; keyword to signal this fact to the MAD compiler so it can set up a dynamic call stack. If we don&amp;rsquo;t do this, the program will still compile but will die with a protection exception when run.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     RECURSIVE EXTERNAL FUNCTION MERGESRT.(LEFT, RIGHT, V)
          INTEGER LEFT, RIGHT, V(1)
          INTEGER LSTART, LEND, RSTART, REND

*         Return if nothing left to sort
          IF (RIGHT - LEFT &amp;lt;= 1)
               FUNCTION RETURN
          END IF

*         Define left and right halves
          LSTART = LEFT
          LEND = (LEFT + RIGHT) / 2
          RSTART = LEND
          REND = RIGHT

*         Recursively sort left and right
          MERGESRT.(LSTART, LEND, V)
          MERGESRT.(RSTART, REND, V)

*         Merge two halves
          MERGE.(LSTART, LEND, RSTART, REND, V)
          FUNCTION RETURN
*         Merge function definition will go here; see below
     END OF FUNCTION
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we define &lt;code&gt;V&lt;/code&gt; as having length 1: this is to allow us to subscript &lt;code&gt;V&lt;/code&gt;, but it&amp;rsquo;s up to us to manage its actual length.&lt;/p&gt;
&lt;p&gt;External functions need an &lt;code&gt;END OF PROGRAM&lt;/code&gt; as well, as they are effectively compiled separately from the main routine even if they are in the same file.&lt;/p&gt;
&lt;p&gt;Next we need the merge subroutine. As this is private to &lt;code&gt;MERGESRT.&lt;/code&gt;, we can define it as an internal function of &lt;code&gt;MERGESRT.&lt;/code&gt;: the definition can go inside the external function definition as shown above. The code looks like this.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;          INTERNAL FUNCTION MERGE.(LSTART, LEND, RSTART, REND, V)
               INTEGER LSTART, LEND, RSTART, REND, V(1)
               INTEGER LLEN, RLEN, I, L, R
               POINTER LHALF, RHALF, LHP, RHP
               CONSTANT SZ = 4

*              Allocate a temp buffer for each half
               LLEN = LEND - LSTART
               RLEN = REND - RSTART
               ALLOCATE (LLEN * SZ)-&amp;gt;LHALF
               ALLOCATE (RLEN * SZ)-&amp;gt;RHALF

*              Copy values into temporaries
               LHP = LHALF
               LOOP FOR I=LSTART, 1, I &amp;gt;= LEND
                    LHP.IND.INTEGER = V(I)
                    LHP = LHP + SZ
               END LOOP
               RHP = RHALF
               LOOP FOR I=RSTART, 1, I &amp;gt;= REND
                    RHP.IND.INTEGER = V(I)
                    RHP = RHP + SZ
               END LOOP

*              Merge values back into V
               L = 0
               R = 0
               LHP = LHALF
               RHP = RHALF
               LOOP FOR I=LSTART, 1, L &amp;gt;= LLEN .OR. R &amp;gt;= RLEN
*                   If left &amp;lt; right, move left, else move right
                    IF (LHP.IND.INTEGER &amp;lt; RHP.IND.INTEGER)
                         V(I) = LHP.IND.INTEGER
                         L = L + 1
                         LHP = LHP + SZ
                    ELSE
                         V(I) = RHP.IND.INTEGER
                         R = R + 1
                         RHP = RHP + SZ
                    END IF
               END LOOP

*              Handle leftover values
               LOOP WHILE L &amp;lt; LLEN
                    V(I) = LHP.IND.INTEGER
                    I = I + 1
                    L = L + 1
                    LHP = LHP + SZ
               END LOOP
               LOOP WHILE R &amp;lt; RLEN
                    V(I) = RHP.IND.INTEGER
                    I = I + 1
                    R = R + 1
                    RHP = RHP + SZ
               END LOOP

               RELEASE LHALF
               RELEASE RHALF
               FUNCTION RETURN
          END OF FUNCTION
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We need a temporary buffer to hold each half of &lt;code&gt;V&lt;/code&gt; while merging. MAD does not allow run time dimensioning of vectors, so we can either make these fixed size or use dynamic memory. I&amp;rsquo;ve chosen the latter to make the solution more generic: we allocate a chunk of memory which is determined by the size of each half and &lt;code&gt;SZ&lt;/code&gt; which is a constant denoting the size of a MAD integer in bytes:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;               ALLOCATE (LLEN * SZ)-&amp;gt;LHALF
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We then use pointer arithmetic to read or write integers, eg&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                    RHP.IND.INTEGER = V(I)
                    RHP = RHP + SZ
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At the end of the function we must remember to free the allocated memory with &lt;code&gt;RELEASE&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;putting-it-together&#34;&gt;Putting it together&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re now ready to compile and run the program.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *gom scards=mergesrt.mad
  No errors in MAIN 
  No errors in MERGESRT
  No errors in MAIN 
# $run -load
  Before sort

  NUMBERS(0)      =           9 
  NUMBERS(1)      =          12 
  NUMBERS(2)      =           3 
...
  NUMBERS(12)     =          10 
  NUMBERS(13)     =           4 
  NUMBERS(14)     =           0 

  After sort

  NUMBERS(0)      =           0 
  NUMBERS(1)      =           1 
  NUMBERS(2)      =           2 
...
  NUMBERS(12)     =          12 
  NUMBERS(13)     =          13 
  NUMBERS(14)     =          14 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts-on-mad&#34;&gt;Final thoughts on MAD&lt;/h2&gt;
&lt;p&gt;Given that it was first designed at about the same time as FORTRAN, MAD feels much more powerful and flexible to use, while still having an easily understandable syntax, by borrowing ideas from early discussions of ALGOL/IAL. It&amp;rsquo;s a pity it did not see wider adoption as it might have made FORTRAN programmer&amp;rsquo;s lives easier.&lt;/p&gt;
&lt;p&gt;The introduction to the GOM manual has an interesting section on why it was re-implemented for MTS:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This language is currently known as GOM (for Good Old  MAD)
to distinguish it from the ill-fated MAD/1 which was designed as
an  extensible  language  and  which,  although it provided many
ideas which have been borrowed, was incredibly  ugly  and  never
extended itself into usability.&lt;/p&gt;
&lt;p&gt;Why at this point in time resurrect the language?  The main
impetus was that a language was needed for writing Grungy Little
Programs and pieces of the operating system.  In both cases, the
programs  are  often  machine-dependent,  and  require  matching
previously established data structures and interfaces  that  are
not  &amp;ldquo;standard&amp;rdquo;,  in  the sense of being used by most high-level
languages.  Fortran is  not  usually  general  enough,  and  PL1
suffers  from its &amp;ldquo;environment&amp;rdquo; and in many cases is not general
enough either.&lt;/p&gt;
&lt;p&gt;Rather than invent a new language, MAD was chosen as a base
because it provided a happy medium between  the  insufficiencies
of  Fortran  and  the  excesses of PL1, and because the semantic
implications of the various constructs  were  known.   That  did
not,  however,  prevent  judicious  tailoring of the language to
better fit the current programming practices and problems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>MAD - Language features</title>
      <link>https://try-mts.com/mad-language-features/</link>
      <pubDate>Sun, 11 Jan 2015 03:35:14 +0000</pubDate>
      <guid>https://try-mts.com/mad-language-features/</guid>
      <description>&lt;p&gt;In this post we&amp;rsquo;ll look at the MAD language (as implemented by GOM on MTS) in more detail. As MAD is quite a large language, compared to FORTRAN or BASIC, I will only go over some of its interesting or unusual features; for a good overview of the language you may want to read the &lt;a href=&#34;http://en.wikipedia.org/wiki/MAD_%28programming_language%29&#34;&gt;Wikipedia article&lt;/a&gt; first.&lt;/p&gt;
&lt;p&gt;Complete examples below (those ending with &lt;code&gt;END OF PROGRAM&lt;/code&gt;) have been tested using &lt;code&gt;*GOM&lt;/code&gt; on MTS D6.0; do note that initial blanks are needed for lines which do not contain statement labels.&lt;/p&gt;
&lt;h2 id=&#34;synonyms-and-abbreviations&#34;&gt;Synonyms and abbreviations&lt;/h2&gt;
&lt;p&gt;There are a number of synonyms for MAD keywords, (eg &lt;code&gt;TRANSFER TO&lt;/code&gt; for &lt;code&gt;GO TO&lt;/code&gt;, and spaces are not generally needed. For example a goto label X could be expressed as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GOTO X
GO TO X
TRANSFER TO X
TRANSFERTOX
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;GOTOX&lt;/code&gt; would not work as this is ambiguous, as it could be a references to the variable &lt;code&gt;GOTOX&lt;/code&gt;. The &lt;code&gt;TRANSFERTOX&lt;/code&gt; works as this is longer than a variable name could be.&lt;/p&gt;
&lt;p&gt;Keywords can be abbreviated by taking the first and last letter and inserting a &lt;code&gt;&#39;&lt;/code&gt;: for example &lt;code&gt;T&#39;O&lt;/code&gt; for &lt;code&gt;TRANSFER TO&lt;/code&gt;&amp;rsquo;. The compiler can optionally produce a listing of the input with these expanded by setting the &lt;code&gt;sprint&lt;/code&gt; parameter.&lt;/p&gt;
&lt;h2 id=&#34;constant-qualification&#34;&gt;Constant qualification&lt;/h2&gt;
&lt;p&gt;Constants can be qualified to a particular type or format with a suffix: some examples&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1B&lt;/code&gt; - Boolean true&lt;/li&gt;
&lt;li&gt;&lt;code&gt;100@X&lt;/code&gt; - hex integer 100, ie decimal 256.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;12@L1&lt;/code&gt; - one byte integer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;HI&amp;quot;@L4&lt;/code&gt; - character string of length 4, right padded with blanks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;statement-label-vectors&#34;&gt;Statement label vectors&lt;/h2&gt;
&lt;p&gt;As well as simple labels as seen in FORTRAN, you can have vectors of statement labels. For example, the below will print &amp;ldquo;Two&amp;rdquo;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     INTEGER J
     J = 2
     GOTO L(J)
L(1) PRINT COMMENT &amp;#34;One&amp;#34;
     GO TO DONE     
L(2) PRINT COMMENT &amp;#34;Two&amp;#34;
     GO TO DONE     
L(3) PRINT COMMENT &amp;#34;Three&amp;#34;
DONE END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;conversions&#34;&gt;Conversions&lt;/h2&gt;
&lt;p&gt;MAD does implicit conversions by assigning to a variable of different type. You can cast to another type without conversion using &lt;code&gt;.AS&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     INTEGER IM, C1, C2
     FLOATING POINT F
     F = 123.45
     IM = F
     C1 = F.AS.INTEGER
     C2.AS.FLOATING POINT = F
     PRINT RESULTS IM, C1, C2
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will print:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     IM = 123  C1 =  1115386675  C2 =  1115386675
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;character-expressions-and-strings&#34;&gt;Character expressions and strings&lt;/h2&gt;
&lt;p&gt;Strings are expressed as character vectors; sub-elements of a vector can be addressed using block notation, with &lt;code&gt;...&lt;/code&gt; standing for range, with optional first and last subscript, and &lt;code&gt;|&lt;/code&gt; for length. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     CHARACTER HELLO(5),H1(3),H2(2)
     HELLO(...) = &amp;#34;Hello&amp;#34;
     H1(...) = HELLO(2...4)
     H2(...) = HELLO(1|2)
     PRINT COMMENT H1
     PRINT COMMENT H2
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Will print &amp;ldquo;llo&amp;rdquo; and &amp;ldquo;el&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;conditionals&#34;&gt;Conditionals&lt;/h2&gt;
&lt;p&gt;The conditional statement allows else clauses and there is an implicit block structure for each clause:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     INTEGER X
     X = 3
     IF X &amp;gt; 1 .AND. X &amp;lt;= 3
          PRINT COMMENT &amp;#34;Yes&amp;#34;
          PRINT RESULTS X
     ELSE
          PRINT COMMENT &amp;#34;No&amp;#34;
     END IF
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;loops&#34;&gt;Loops&lt;/h2&gt;
&lt;p&gt;As we saw in the hello world example, the basic loop takes an initial variable, increment and condition which when true will mark the end of the loop. So the below will print &amp;ldquo;Hello, world!&amp;rdquo; five times&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     LOOP FOR I=1, 1, I &amp;gt; 5
          PRINT COMMENT &amp;#34;Hello, world!&amp;#34;
     END LOOP
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also a THROUGH variant which is more like a FORTRAN loop: this will print &amp;ldquo;Hi&amp;rdquo; and &amp;ldquo;There&amp;rdquo; five times then &amp;ldquo;Done&amp;rdquo;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     THROUGH L, FOR I=1,1,I&amp;gt;5
     PRINT COMMENT &amp;#34;Hi&amp;#34;
L    PRINT COMMENT &amp;#34;There&amp;#34;
     PRINT COMMENT &amp;#34;Done&amp;#34;
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and a &lt;code&gt;LOOP WHILE b&lt;/code&gt; and &lt;code&gt;LOOP UNTIL b&lt;/code&gt; which implement while loops.&lt;/p&gt;
&lt;h2 id=&#34;dynamic-memory-and-pointers&#34;&gt;Dynamic memory and pointers&lt;/h2&gt;
&lt;p&gt;Addresses can be referenced with &lt;code&gt;.LOC.&lt;/code&gt; and indirection done with &lt;code&gt;.IND.&lt;/code&gt; followed by the type (which does not have to be the original type of what is being pointed to). It&amp;rsquo;s possible to do pointer arithmetic also. The below example will print the result &amp;ldquo;42&amp;rdquo;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     INTEGER Q
     Q = 0
     POINTER P
     P = .LOC.Q
     P.IND.INTEGER = 42
     PRINT RESULTS Q
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A dynamic record defines a simple structure made up of data fields&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     DYNAMIC RECORD (POINT) X,Y
     INTEGER X, Y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To use this you need to point it at an existing memory location or allocate new space with &lt;code&gt;ALLOCATE&lt;/code&gt; and free memory with &lt;code&gt;RELEASE&lt;/code&gt;. You can then use &lt;code&gt;:&lt;/code&gt; to address fields:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     DYNAMIC RECORD (POINT) X,Y
     INTEGER X, Y
     POINTER P
     ALLOCATE (POINT)-&amp;gt;P
     P:X = 10
     P:Y = 20
     PRINT RESULTS P:Y / P:X
     RELEASE P
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;USING&lt;/code&gt; keywords allows you to select a pointer, after which references to a record&amp;rsquo;s fields will be done through that pointer. As an example, the above could be rewritten as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     DYNAMIC RECORD (POINT) X,Y
     INTEGER X, Y
     POINTER P
     ALLOCATE (POINT)-&amp;gt;P
     USING POINTER P, FOR POINT
     X = 10
     Y = 20
     PRINT RESULTS Y / X
     RELEASE P
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;USING&lt;/code&gt; will continue until another &lt;code&gt;USING&lt;/code&gt; replaces it, or you do &lt;code&gt;STOP USING POINTER&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;functions&#34;&gt;Functions&lt;/h2&gt;
&lt;p&gt;MAD supports external (definition can provided at link time) or internal functions. Functions have a number of interesting features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Names must end with a &lt;code&gt;.&lt;/code&gt; so they are recognised by the parser&lt;/li&gt;
&lt;li&gt;Optional or variable number of arguments are supported&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s possible to have multiple entry points into a function&lt;/li&gt;
&lt;li&gt;As well as a return value it&amp;rsquo;s possible to set a return code to signal unusual conditions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A example showing how an internal function can be defined and called, using the return code feature:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     INTERNAL FUNCTION NEGATE.(X)
          NORMAL MODE IS INTEGER
          IF X = 0
               FUNCTION RETURN 0, RC=1
          ELSE
               FUNCTION RETURN - X
          END IF
     END OF FUNCTION

     INTEGER X, X1, R
     READ DATA FROM UNIT 5
     X1 = NEGATE.(X)-&amp;gt;R
     IF R = 0
          PRINT COMMENT &amp;#34;Zero is still zero&amp;#34;
     ELSE
          PRINT RESULTS X1
     END IF
     END OF PROGRAM
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;inputoutput&#34;&gt;Input/output&lt;/h2&gt;
&lt;p&gt;In the previous example, the &lt;code&gt;READ DATA FROM UNIT 5&lt;/code&gt; requires that you set unit 5 to a FDname on the run command. But how does it know where to put the input? It expects the input format to be a set of &lt;code&gt;name=value&lt;/code&gt; pairs separated by commas and terminated by an asterisk, which it will use to set variables. So a sample run of the above program, taking input from the keyboard, would look like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run -load 5=*source*
# Execution begins   10:47:49 
  X=42*

          X1      =         -42 
# Execution terminated   10:47:55  T=0.001 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also use &lt;code&gt;READ AND PRINT DATA&lt;/code&gt; to input as above and then echo the values entered.&lt;/p&gt;
&lt;p&gt;For output, &lt;code&gt;PRINT RESULTS&lt;/code&gt; will print data in a similar way to the format used by &lt;code&gt;READ DATA&lt;/code&gt;&amp;rsquo;; &lt;code&gt;PRINT COMMENT&lt;/code&gt; can be used to output a string.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also possible to do formatted input/output, similar but not identical to FORTRAN.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the &lt;a href=&#34;http://hdl.handle.net/2027.42/79620&#34;&gt;GOM Manual&lt;/a&gt; for a full specification of the language and how it differs from the original version of MAD.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>MAD - Introduction</title>
      <link>https://try-mts.com/mad-introduction/</link>
      <pubDate>Sun, 04 Jan 2015 03:31:41 +0000</pubDate>
      <guid>https://try-mts.com/mad-introduction/</guid>
      <description>&lt;p&gt;In this series of posts we&amp;rsquo;ll look at one of the less widely known languages available on MTS - MAD, or the Michigan Algorithm Decoder.&lt;/p&gt;
&lt;h2 id=&#34;the-mad-language&#34;&gt;The MAD language&lt;/h2&gt;
&lt;p&gt;Originally developed in 1959 at UM for the IBM 704, MAD is an imperative language influenced by ALGOL. It was implemented for a number of mainframes and operating systems in the 1960s, including MIT&amp;rsquo;s CTSS where it was used to develop MAIL and RUNOFF.&lt;/p&gt;
&lt;h2 id=&#34;mad-on-mts&#34;&gt;MAD on MTS&lt;/h2&gt;
&lt;p&gt;GOM, or Good Old MAD, is the version available on the MTS D6.0 distribution. It is a re-implementation of MAD for the IBM S/360 with some changes to the language to bring it up to date. It was used to write the messaging system on MTS.&lt;/p&gt;
&lt;p&gt;There was also a complete redesign of MAD called MAD/I, but this is not available on D6.0 due to copyright reasons.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get GOM running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;compiling-using-gom&#34;&gt;Compiling using &lt;code&gt;*GOM&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*GOM&lt;/code&gt; will read source from &lt;code&gt;scards&lt;/code&gt; (by default &lt;code&gt;*source*&lt;/code&gt; ie standard input) and will write object files to &lt;code&gt;spunch&lt;/code&gt; (by default the temporary file &lt;code&gt;-load&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Other &lt;code&gt;*GOM&lt;/code&gt; parameters are listed in the GOM manual.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a terminal log of how to compile and run a simple hello world program. This assumes the source code is in file &lt;code&gt;hello.mad&lt;/code&gt;. Note that comments are introduced with a &lt;code&gt;*&lt;/code&gt; in column 1; all other statements (apart from line labels) must not start in column 1 but otherwise indentation is not important.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.mad
       1     * HELLO WORLD PROGRAM
       2          LOOP FOR I=1, 1, I &amp;gt; 5
       3               PRINT COMMENT &amp;#34;Hello, world!&amp;#34;
       4          END LOOP
       5          END OF PROGRAM
# $run *gom scards=hello.mad
  No errors in MAIN 
# $run -load
  Hello, world!
  Hello, world!
  Hello, world!
  Hello, world!
  Hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;http://en.wikipedia.org/wiki/MAD_%28programming_language%29&#34;&gt;Wikipedia article&lt;/a&gt; is a good overview of the language and its implementations. An &lt;a href=&#34;https://web.archive.org/web/20230206202139/http://archive.michigan-terminal-system.org/discussions/mad-mad-i-and-gom&#34;&gt;article on the MTS Archive&lt;/a&gt; gives more background on the history of MAD.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://hdl.handle.net/2027.42/79620&#34;&gt;GOM manual&lt;/a&gt; can be downloaded in PDF format from the UM Deepblue archive or the original FORMAT source can be found on the D6.0 distribution tapes under component 659/22 (restore to eg file &lt;code&gt;GOMMAN.WF&lt;/code&gt; and then &lt;code&gt;$run *format scards=gomman.wf sprint=-print&lt;/code&gt;, &lt;code&gt;copy -print *print*&lt;/code&gt; to get an ASCII text copy). This describes the language in detail and how it differs from MAD.&lt;/p&gt;
&lt;p&gt;An interesting book from 1962 by one of the designers of MAD, Bernard Galler, is &amp;ldquo;The Language of Computers&amp;rdquo;  which can be found at &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mad/Galler_TheLangOfComps_1962.pdf&#34;&gt;Bitsavers&lt;/a&gt;. This takes a number of problems and describes from first principles how these can be turned into algorithms and how we might come up with a computer language to express them - and over the course of the book elegantly explains MAD.&lt;/p&gt;
&lt;p&gt;Eric S Raymond has produced a transpiler (MAD to C) that runs on Unixes - source code can be found &lt;a href=&#34;https://gitorious.org/madness/madness&#34;&gt;at gitorious&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FORTRAN - Calculating emirps</title>
      <link>https://try-mts.com/fortran-calculating-emirps/</link>
      <pubDate>Sun, 28 Dec 2014 03:18:26 +0000</pubDate>
      <guid>https://try-mts.com/fortran-calculating-emirps/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s look at an example of a complete program in FORTRAN and compare running it using the different compilers available on MTS.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://rosettacode.org/wiki/Emirp_primes&#34;&gt;Emirps&lt;/a&gt; are prime numbers that when their digits are reversed they make a different prime. An example is 13, which when reversed is the prime 31. The task on Rosetta Code is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;show the first twenty emirps.&lt;/li&gt;
&lt;li&gt;show all emirps between 7,700 and 8,000&lt;/li&gt;
&lt;li&gt;show the 10,000th emirp&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;writing-the-program&#34;&gt;Writing the program&lt;/h2&gt;
&lt;p&gt;I think it&amp;rsquo;s interesting to describe how I wrote the program and got it to work. On modern systems I would tend to dive in and start writing small pieces of code and associated tests, TDD style. On MTS I favour writing the program offline first, import it into MTS and then get it working - this is partially due to being less comfortable with the MTS editor but also to match the usage pattern for MTS when it was in use; then terminal time was expensive so you would try to get more done before reaching for the computer.&lt;/p&gt;
&lt;p&gt;So I started by sketching out what routines I&amp;rsquo;d need&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Three subroutines &lt;code&gt;TEST1&lt;/code&gt;-&lt;code&gt;3&lt;/code&gt;to test each of the tasks given in the problem&lt;/li&gt;
&lt;li&gt;A function &lt;code&gt;EMIRP&lt;/code&gt; to test if a number was an emirp. This would reverse the digits and if different from the original, test if both were prime.&lt;/li&gt;
&lt;li&gt;A function &lt;code&gt;PRIME&lt;/code&gt; to test if a number was a prime&lt;/li&gt;
&lt;li&gt;A function &lt;code&gt;REVRSE&lt;/code&gt; to reverse a number&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I used Emacs on my host OS to write the routines - was pleasantly surprised to find it still supports FORTRAN 66 syntax. This took around 45 minutes.&lt;/p&gt;
&lt;p&gt;The prime number test I used was a brute force approach so not particularly efficient:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C     *** TEST IF A NUMBER IS PRIME ***
      LOGICAL FUNCTION PRIME(N)
      INTEGER N
C     DEAL WITH NUMBERS &amp;lt;= 3
      IF (N .LE. 1) GOTO 200
      IF (N .EQ. 2 .OR. N .EQ. 3) GOTO 100
C     CHECK IF DIVISIBLE BY 2 OR 3
      IF (MOD(N,2) .EQ. 0) GOTO 200
      IF (MOD(N,3) .EQ. 0) GOTO 200
C     SEE IF DIVISIBLE BY 5, 7, ..., UP TO APPROX SQRT(N)
 10   DO 20 I=5,999999,2
         IF (I*I .GT. N) GOTO 100
         IF (MOD(N,I) .EQ. 0) GOTO 200
 20   CONTINUE
 100  PRIME = .TRUE.
      RETURN
 200  PRIME = .FALSE.
      RETURN
      END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Reversing the digits was done by taking the modulus of the number with 10 and then appending each digit to the end:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C     *** REVERSE AN INTEGER&amp;#39;S DIGITS ***
      INTEGER FUNCTION REVRSE(N)
      INTEGER N
      INTEGER M,R
C     M IS COPY OF N FROM WHICH WE TAKE DIGITS
C     R IS REVERSED DIGITS
      M = N
      R = 0
C     LOOP UNTIL NO MORE DIGITS
 10   IF (M .LT. 1) GOTO 100
C     TAKE LAST DIGIT FROM M AND APPEND TO R
      R = R * 10
      R = R + MOD(M, 10)
      M = M / 10
      GOTO 10
 100  REVRSE = R
      RETURN
      END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The emirp function was then a simple case of calling the two functions:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C     *** TEST IF AN INTEGER IS AN EMIRP ***
      LOGICAL FUNCTION EMIRP(N)
      INTEGER N
C     EXTERNAL FUNCTIONS
      INTEGER REVRSE
      LOGICAL PRIME
C     R CONTAINS REVERSED DIGITS OF N
      INTEGER R
      R = REVRSE(N)
C     N AND R MUST BOTH BE PRIME AND NOT THE SAME VALUE
      IF (N .EQ. R) GOTO 200
      IF (.NOT. PRIME(N)) GOTO 200
      IF (.NOT. PRIME(R)) GOTO 200
 100  EMIRP = .TRUE.
      RETURN
 200  EMIRP = .FALSE.
      RETURN
      END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The test functions are simple loops: here I show just the second test as an example.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C     *** SHOW EMIRPS BETWEEN 7,700 AND 8,000 ***
      SUBROUTINE TEST2
C     EXTERNAL FUNCTION
      LOGICAL EMIRP
C     N IS NUMBER TO TEST
      INTEGER N
 10   DO 100 N=7700,8000
         IF (EMIRP(N)) CALL SHOW(N)
 100  CONTINUE
      RETURN
      END
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I factored out the printing to another routine as each test needs to do this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C     *** DISPLAY AN INTEGER ***
      SUBROUTINE SHOW(N)
      INTEGER N
      WRITE(6,50) N
 50   FORMAT(I10)
      RETURN
      END
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;getting-it-running-with-if66&#34;&gt;Getting it running with &lt;code&gt;*IF66&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Next I imported the program into MTS (using the &lt;a href=&#34;https://try-mts.com/submitting-batch-jobs-from-the-reader/&#34;&gt;emulated card reader&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I then ran &lt;code&gt;*IF66&lt;/code&gt; and did &lt;code&gt;/compile emirp.f&lt;/code&gt;. Lots of syntax errors - I forgot the maximum length for identifiers was 7 characters and that I needed to declare functions used externally.&lt;/p&gt;
&lt;p&gt;Next I tried to test the individual routines - here &lt;code&gt;*IF66&lt;/code&gt; was very useful, as I could call eg the reverse routine with some test input and see its output immediately. Some more problems to fix here, eg function parameters seem to be pass by reference in FORTRAN. An error in the DO-loop in the prime testing function lead to an infinite loop that I could not get out of with &lt;code&gt;PA2&lt;/code&gt;, so a trip to the operator&amp;rsquo;s console to &lt;code&gt;STOP&lt;/code&gt; the MTS session was needed.&lt;/p&gt;
&lt;p&gt;Finally I got it running after about another hour. The first two tasks were quick to execute but the third, to find the 10,000th emirp, ran for over 10 minutes without returning so I killed it. Time to run this using the compilers and see how they perform.&lt;/p&gt;
&lt;h3 id=&#34;using-the-compiler&#34;&gt;Using the compiler&lt;/h3&gt;
&lt;p&gt;First I tried it using FORTRANG:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *ftn scards=emirp.f
# Execution begins   20:05:30 
  No errors in PRIME 
  No errors in REVRSE 
  No errors in EMIRP 
  No errors in SHOW 
  No errors in TEST1 
  No errors in TEST2 
  No errors in TEST3 
  No errors in MAIN 
# Execution terminated   20:05:30  T=0.034
# $run -load
# Execution begins   20:05:34 
         13
...
     948349
# Execution terminated   20:05:43  T=8.82 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So just over 0.03 seconds of CPU time to compile and nearly 9s to run all tests.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try it with FORTRANH by using the &lt;code&gt;opt&lt;/code&gt; parameter to &lt;code&gt;*ftn&lt;/code&gt;. Note that the output of the compiler is quite different as FORTRANH is a completely different product:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *ftn scards=emirp.f par=opt=h
# Execution begins   20:06:12 
  **** No errors for  PRIME ****      WED DEC 17/14 20:06:13
  **** No errors for REVRSE ****      WED DEC 17/14 20:06:13
  **** No errors for  EMIRP ****      WED DEC 17/14 20:06:13
  **** No errors for   SHOW ****      WED DEC 17/14 20:06:13
  **** No errors for  TEST1 ****      WED DEC 17/14 20:06:13
  **** No errors for  TEST2 ****      WED DEC 17/14 20:06:13
  **** No errors for  TEST3 ****      WED DEC 17/14 20:06:13
  **** No errors for   MAIN ****      WED DEC 17/14 20:06:13
# Execution terminated   20:06:12  T=0.092 
# $run -load
# Execution begins   20:06:30 
         13
...
     948349
# Execution terminated   20:06:35  T=4.607 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compilation took almost three times as long, but the program ran 48% faster so there&amp;rsquo;s definitely some good optimisation taking place. The compilation time difference may seem insignificant, but in an environment where you are paying for CPU time and working on a large program that requires many recompiles during development the costs would mount up.&lt;/p&gt;
&lt;p&gt;Next step would probably be to improve the algorithm for testing for primeness, but I&amp;rsquo;ll leave that for now.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts-on-fortran&#34;&gt;Final thoughts on FORTRAN&lt;/h2&gt;
&lt;p&gt;FORTRAN 66 has not aged well - its fixed format lines, lack of character/strings, unstructured design and implicit variable types make it hard to use for large scale programming. But it&amp;rsquo;s a simple language to pick up and use, and you can see that if you want to solve a numerical problem it can be very productive. Coupled with the optimising compiler and support tools available you can understand why it was the lingua franca of its time, rather like C today.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Full source code for this program is &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FORTRAN - Language features</title>
      <link>https://try-mts.com/fortran-language-features/</link>
      <pubDate>Sun, 21 Dec 2014 03:51:06 +0000</pubDate>
      <guid>https://try-mts.com/fortran-language-features/</guid>
      <description>&lt;p&gt;In this post I&amp;rsquo;ll look at the FORTRAN 66 language as available on MTS in more detail.&lt;/p&gt;
&lt;h2 id=&#34;source-code-format&#34;&gt;Source code format&lt;/h2&gt;
&lt;p&gt;FORTRAN originally had a fixed format for source code lines and used all upper case letters; as &lt;code&gt;*FTN&lt;/code&gt; does not have this restriction we&amp;rsquo;ll use lower case and free format in the examples below to make them easier to read, but note that this means these examples may not work on other, non-MTS compilers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update 4-Dec-2015:&lt;/em&gt; As noted by Mike Stramba, &lt;code&gt;*FTN&lt;/code&gt; &lt;strong&gt;does&lt;/strong&gt; require source code to be in upper case, although &lt;code&gt;*IF66&lt;/code&gt; is happy with upper or lower case.&lt;/p&gt;
&lt;p&gt;One unique feature is that whitespace is not important, so the variable BXX could also be referred to as B XX. Keywords are context sensitive, so you can write &lt;code&gt;IF IF.GT.3 J=2&lt;/code&gt; where the second IF is actually a variable.&lt;/p&gt;
&lt;h2 id=&#34;types-and-identifiers&#34;&gt;Types and identifiers&lt;/h2&gt;
&lt;p&gt;FORTRAN supports INTEGER, REAL. LOGICAL and COMPLEX number types. Character types are not support directly, but can be accessed through FORMAT statements (see below).&lt;/p&gt;
&lt;p&gt;Variable names can have up to six characters and must start with a letter. Variables can be explicitly declared with their type; if not declared, variables starting with letters I to N are treated as integers, otherwise they are treated as reals.&lt;/p&gt;
&lt;p&gt;Variables can be initialised with the DATA statement.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;integer count, count1
real temp
complex point
data i,count,temp,point/0, 10, 32.0, cmplx(1,2)/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Arrays and matrices can be created with DIMENSION and initialised with DATA then individual values can be accessed using (), first index 1,  in column major order, so in the below X(2, 1) will be intialised with the value 2.0.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dimension x(2,2)
data x/1.0, 2.0, 3.0, 4.0/
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;operators-and-expressions&#34;&gt;Operators and expressions&lt;/h2&gt;
&lt;p&gt;The usual arithmetic operators appear, including &lt;code&gt;**&lt;/code&gt; for exponentiation. Logical operators look like .LT. for less than or .AND. for Boolean-and.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;x = 2.0 ** 4.0
i = 5 + 3
j = x / i
y = x / i
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the result above, J will be set to 2 and y to 2.0.&lt;/p&gt;
&lt;h2 id=&#34;statements&#34;&gt;Statements&lt;/h2&gt;
&lt;p&gt;Branching can be done with GOTO; there are three forms:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;goto 100
goto l
goto (10,20,30) ll
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;The first will jump unconditionally to the line labeled with 100.&lt;/li&gt;
&lt;li&gt;The second will jump to the label based on the value of L&lt;/li&gt;
&lt;li&gt;The third will jump to line 10 if ll = 1, 20 if ll = 2 etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The DO statement allows looping&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;j = 0
do 100 i=10,20,5
j = j + i
100 continue
k = j * 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will initialise I with 10 and then run the contents of the loop for values 10, 15, 20. After that it will jump to the line after the label 100. So for this example, K will end up with the value 90.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to nest loops and jump out of loops, but not jump into them.&lt;/p&gt;
&lt;p&gt;The IF statement has two forms, logical and arithmetic. The example of the logical version below will set k to 3 if i &amp;lt; j.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if (i.lt.j) k=3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The arithmetic version will branch to one of three labels based on whether the expression being tested is negative, zero or positive. For the example below it will branch to label 30 as the expression is positive.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;J = -3
if (j + 4) 10,20,30
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;inputoutput-and-formats&#34;&gt;Input/output and formats&lt;/h2&gt;
&lt;p&gt;Inputs (READ) and outputs (WRITE) are associated with a FORMAT statement, a logical I/O unit and a list of variables. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;read(5,100) i1,x1,y1
100   format(i2,f5.2,f2.0)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;with the input&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1234.5678.90
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will read from I/O unit 5 (the keyboard) i1=12, x1=34.56, y1=78.0.&lt;/p&gt;
&lt;p&gt;Character constants, with a leading length identifier, can be used in READ or WRITE statements as we saw in the hello world example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;write (6,200)
200 format(13h HELLO, WORLD)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;prints &amp;ldquo;HELLO, WORLD&amp;rdquo; on unit 6, the console.&lt;/p&gt;
&lt;h2 id=&#34;functions-and-subroutines&#34;&gt;Functions and subroutines&lt;/h2&gt;
&lt;p&gt;FORTRAN comes with a large number of built in functions for mathematical processing eg MOD, ABS, SIN. On MTS there is also a library of operating system specific functions that can be called but I will not go into these here.&lt;/p&gt;
&lt;p&gt;You can define your own functions to pass parameters by reference and optionally return a value: for example to calculate the average of two values:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;real function avg2(x,y)
real x, y
avg2 = (x + y) / 2.0
return
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This could be called with&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;z = avg2(3.0, x)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also subroutines which  run by using &lt;code&gt;call&lt;/code&gt;. For example, the below would set x to 2.0:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;x = 1.0
call add1(x)

subroutine add1(x)
real x
x = x + 1.0
return
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.math-cs.gordon.edu/courses/cs323/FORTRAN/fortran.html&#34;&gt;FORTRAN IV reference page&lt;/a&gt; is a great summary of FORTRAN 66 syntax.&lt;/p&gt;
&lt;p&gt;A contemporary IBM references for FORTRAN can be found at &lt;a href=&#34;http://bitsavers.trailing-edge.com/pdf/ibm/370/fortran/GC28-6515-11_IBM_System360_and_System370_FORTRAN_IV_Language_Sep83.pdf&#34;&gt;Bitsavers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol06-FortranInMTS-Feb1988.pdf&#34;&gt;MTS Volume 6&lt;/a&gt; describes &lt;code&gt;*FTN&lt;/code&gt; and &lt;code&gt;*IF66&lt;/code&gt; in full detail, along with the other FORTRAN tools available.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FORTRAN - Introduction</title>
      <link>https://try-mts.com/fortran-introduction/</link>
      <pubDate>Sun, 14 Dec 2014 02:44:20 +0000</pubDate>
      <guid>https://try-mts.com/fortran-introduction/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/12/fortran-card-format.png&#34; alt=&#34;fortran card format&#34;&gt;
&lt;sup&gt;Diagram showing fixed format for lines of FORTRAN source code, from &lt;a href=&#34;http://books.google.com/books?id=DNJWAAAAMAAJ&#34;&gt;Digital Computing, FORTRAN IV, WATFIV, and MTS (with *FTN and *WATFIV)&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;For our second language we&amp;rsquo;ll look at FORTRAN, the most popular language run on MTS by far (40% by frequency of use in 1986 according to &lt;a href=&#34;http://hdl.handle.net/2027/mdp.39015009827232?urlappend=%3Bseq=5&#34;&gt;Introduction to programming and debugging in MTS&lt;/a&gt;, followed by Pascal at 35%). Due to its popularity it has the largest range of environments, libraries and tooling of any language available on MTS.&lt;/p&gt;
&lt;h2 id=&#34;the-fortran-language&#34;&gt;The FORTRAN language&lt;/h2&gt;
&lt;p&gt;FORTRAN is probably the oldest language still in wide use today. Originally developed at IBM in the early 1950s, it spread to many platforms in the 1960s and become the language of choice for scientific and mathematical applications. The first standardised version, FORTRAN 66, was a simple unstructured imperative language; later versions such as Fortran 77 and 90 added modular features and even object orientated programming constructs.&lt;/p&gt;
&lt;h2 id=&#34;fortran-on-mts&#34;&gt;FORTRAN on MTS&lt;/h2&gt;
&lt;p&gt;Available with the D6.0 distribution are the two IBM FORTRAN IV (ie FORTRAN 66) compilers, FORTRANG and FORTRANH. The former includes better debugging support whereas the latter produces more efficient object code. Both of these compilers are available via the &lt;code&gt;*FTN&lt;/code&gt; front end.&lt;/p&gt;
&lt;p&gt;There is also &lt;code&gt;*IF66&lt;/code&gt;, an innovative FORTRAN interpreter developed at the University of British Columbia, which is useful for developing and testing programs.&lt;/p&gt;
&lt;p&gt;Due to copyright restrictions, the University of Waterloo compilers WATFOR and WATFIV are not available - these featured a unique compile-and-go model where compiler output was directly executed rather than writing an object file, which allows better diagnostic messages and reduced compilation costs.&lt;/p&gt;
&lt;p&gt;FORTRAN 66 was already obsolete in 1986 and most new development in FORTRAN had moved on to FORTRAN 77 which was supported in MTS by the IBM compiler FORTRANV5 and UBC interpreter &lt;code&gt;*IF77&lt;/code&gt; - however neither of these are available in the D6.0 distribution due to copyright restrictions.&lt;/p&gt;
&lt;p&gt;Also available are a number of FORTRAN preprocessors, such as RATFOR, and tools, such as the &lt;code&gt;*DAVE&lt;/code&gt; data flow analyser, which I will cover in a later series of posts.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get FORTRAN running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;compiling-using-ftn&#34;&gt;Compiling using &lt;code&gt;*FTN&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can invoke FORTRANG and FORTRANH directly, but it&amp;rsquo;s much easier to use the MTS front end &lt;code&gt;*FTN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;*FTN&lt;/code&gt; will read source from &lt;code&gt;scards&lt;/code&gt; (by default &lt;code&gt;*source*&lt;/code&gt; ie standard input) and will write object files to &lt;code&gt;spunch&lt;/code&gt; (by default the temporary file &lt;code&gt;-load&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Other options can be provided using &lt;code&gt;par=&lt;/code&gt; on the command line or interactively.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;format&lt;/code&gt; determines the source code input format: options are IBM, LINE, EDITED or LONG with the default being EDITED. IBM format is the traditional FORTRAN fixed format shown at the diagram at the top of this post; LONG is IBM format with the maximum line length limit lifted; LINE is free format. EDITED is a compromise where the front end will try to guess what format the input is in.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;opt&lt;/code&gt; determines the optimisation level. Choices are G, which will cause the FORTRANG compiler to be used, or 0/1/2 which will select the FORTRANH compiler with increasing levels of optimisation. H is an alias for FORTRANH with level 2 optimisation. The default for &lt;code&gt;opt&lt;/code&gt; is G.&lt;/p&gt;
&lt;p&gt;Other &lt;code&gt;*FTN&lt;/code&gt; parameters are listed in the online help or MTS Volume 6.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a terminal log of how to compile and run a simple hello world program. This assumes the source code is in file &lt;code&gt;hello.f&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list hello.f
       1     C *** HELLO WORLD PROGRAM ***
       2     C
       3           DO 10 I=1,5
       4           WRITE (6,20)
       5        10 CONTINUE
       6           STOP
       7        20 FORMAT(13H HELLO, WORLD)
       8           END
# $run *ftn scards=hello.f
  No errors in MAIN 
# $run -load
  HELLO, WORLD
  HELLO, WORLD
  HELLO, WORLD
  HELLO, WORLD
  HELLO, WORLD
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-if66-fortran-interpreter&#34;&gt;The &lt;code&gt;*IF66&lt;/code&gt; FORTRAN interpreter&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;ve used the UM BASIC interpreter, the &lt;code&gt;*IF66&lt;/code&gt; FORTRAN interpreter will appear familiar, with the ability to switch between editing, running and debugging programs. It integrates well with the MTS environment, allowing you to use the full MTS editor and is fully compatible with the FORTRAN compilers. It supports running of arbitrary FORTRAN statements or routines and debugging features such as atpoints, where code can be run when a breakpoint is reached.&lt;/p&gt;
&lt;p&gt;A quick demo, using the file &lt;code&gt;hello.f&lt;/code&gt; from before:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *if66
* IF(NOV80)
* /compile hello.f
  ROUTINE NAME: MAIN 
* /list main
*     1.    C *** HELLO WORLD PROGRAM ***
*     2.    C
*     3.          DO 10 I=1,5
*     4.          WRITE (6,20) 
*     5.       10 CONTINUE 
*     6.          STOP
*     7.       20 FORMAT(13H HELLO, WORLD) 
*     8.          END
+ /edit main
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You are now in the MTS editor and can type &lt;code&gt;v&lt;/code&gt; to enter visual mode and make changes; press &lt;code&gt;PA2&lt;/code&gt; to exit out of the editor. I&amp;rsquo;ll now show how to run the program and also &lt;code&gt;*IF66&lt;/code&gt;&amp;rsquo;s immediate execution of FORTRAN statements:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* /run main
  HELLO, WORLD
  HELLO, WORLD
  HELLO, WORLD
  HELLO, WORLD
  HELLO, WORLD
  /MAIN:6./ - /STOP /
+ x=40.0
+ y=sqrt(x)
+ print,y
      6.324555 
+ /stop
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Definitely a useful and powerful tool, and I don&amp;rsquo;t think any other platform had such an advanced FORTRAN interpreter at that time.&lt;/p&gt;
&lt;p&gt;In the next post, I&amp;rsquo;ll take a look at the FORTRAN language in more detail.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://hdl.handle.net/2027.42/79601&#34;&gt;MTS Volume 6&lt;/a&gt; describes &lt;code&gt;*FTN&lt;/code&gt; and &lt;code&gt;*IF66&lt;/code&gt; in full detail, along with the other FORTRAN tools available.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://books.google.com/books?id=DNJWAAAAMAAJ&#34;&gt;Digital Computing, FORTRAN IV, WATFIV, and MTS (with *FTN and *WATFIV)&lt;/a&gt; is a good contemporary introduction to programming FORTRAN on MTS from two authors at the Chemical Engineering department at UM. A number of different editions were produced; the linked one is from 1976. It also includes a useful description of the IBM S/360 architecture from the viewpoint of a non-systems programmer.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://8d4ec138056a43619549adaab53ffb70b717bf4c.googledrive.com/host/0B4t_NX-QeWDYZi1rdUtBMVRUUEd2RWM3dFd0dHhMdw/IF-Interactive-Fortran-Compiler-SHARE%2041-13to17August1973-MiamiBeachFlorida.pdf&#34;&gt;IF, an interactive FORTRAN compiler&lt;/a&gt; (link not working as of August 2023) is a paper from 1973 giving background information on why &lt;code&gt;*IF66&lt;/code&gt; was developed and a demo of its use.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>BASIC - Narcissistic numbers</title>
      <link>https://try-mts.com/basic-narcissistic-numbers/</link>
      <pubDate>Sun, 07 Dec 2014 01:25:49 +0000</pubDate>
      <guid>https://try-mts.com/basic-narcissistic-numbers/</guid>
      <description>&lt;p&gt;For our sample BASIC program, let&amp;rsquo;s try to implement the &lt;a href=&#34;http://rosettacode.org/wiki/Narcissistic_decimal_number&#34;&gt;Narcissistic numbers problem&lt;/a&gt; from &lt;a href=&#34;http://rosettacode.org&#34;&gt;Rosetta Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A narcissistic number is a integer where the sum of each digit raised to the power of the number of digits equals the number itself. For example, 153 is narcissistic: it has three digits 1, 5 and 3, and 1³ + 5³ + 3³ = 1 + 125 + 27 = 153.&lt;/p&gt;
&lt;p&gt;The goal of the program is to print the first 25 narcissistic numbers.&lt;/p&gt;
&lt;h2 id=&#34;first-version&#34;&gt;First version&lt;/h2&gt;
&lt;p&gt;The algorithm is fairly simple&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Look at each positive integer in turn&lt;/li&gt;
&lt;li&gt;Find number of digits&lt;/li&gt;
&lt;li&gt;Sum up each digit raised to the power of the number of digits&lt;/li&gt;
&lt;li&gt;Compare to the number and if equals, print it&lt;/li&gt;
&lt;li&gt;Stop after 25 narcissistic numbers found&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To find the number of digits we can convert the number to a string and take its length. To extract each digit we need to divide by 10, take the integer part and multiply by 10 again and subtract the original number as there is no modulus operator.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;100 rem UM BASIC program to print the first 25 narcissistic numbers 
110 let c = 0                /* Count of narcissistic numbers found 
120 *
130 rem Start of loop to find and print narcissistic numbers
140 for n = 0 to 10000000    /* Number to check for narcissism
150 if c &amp;gt;= 25 then: stop    /* End after 25 numbers
160 *
170 let nn = nts(n)          /* Number as a string
180 let d = len(nn)          /* Number of digits
190 let x = n                /* Copy of number
200 let y = 0                /* Result of narcissistic calculation
210 * 
220 rem Start of loop to calculate narcissistic number 
230 for i = 1 to d           /* For each digit
240 let x1 = int(x/10)       /* Get all but last digit of x
250 let x0 = x - (10 * x1)   /* Get last digit of x
260 let y = y + (x0 ** d)    /* Accumulate digit ** num digits
270 let x = x1               /* Shift off last digit from x
280 next i 
290 *
300 if int(y) &amp;lt;&amp;gt; n then 400  /* Not a narcissistic number, jump next
310 print n                  /* Print narcissistic number
320 let c = c + 1            /* Increase count found
330 *
400 next n                   /* Try next number
410 *
500 stop                     /* Fall through
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here&amp;rsquo;s the output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0 1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474 
54748 92727 93084 548834 1741725 4210818 9800817 9926315
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately this takes a long time to run - 6 minutes and 46 seconds of CPU time - so let&amp;rsquo;s see how we can improve this.&lt;/p&gt;
&lt;h2 id=&#34;faster-version&#34;&gt;Faster version&lt;/h2&gt;
&lt;p&gt;There are several optimisations we can try:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Precompute &lt;code&gt;a ** b&lt;/code&gt;, for a and b between 0 and 9, as we do this for each digit in the inner loop and it is likely to be an expensive operation. The precomputed powers are stored in a 2D matrix &lt;code&gt;p&lt;/code&gt; by the routine starting on line 600. This saves 123 seconds.&lt;/li&gt;
&lt;li&gt;Exit early from the calculation inner loop when the accumulated total is larger than the number being tested. This saves 32 seconds.&lt;/li&gt;
&lt;li&gt;Use integer comparison to find the number of digits rather than converting to a string. This saves 54 seconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s the final listing, taking 3m17s. There are probably further optimisations that can be made - any suggestions?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;100 rem UM BASIC program to print the first 25 narcissistic numbers 
110 rem Second, faster version
120 *
130 let c = 0                /* Count of narcissistic numbers found 
140 dim p(9, 9)              /* p(x, y) will contain x ** y
150 gosub 600                /* Precompute p
160 *
170 rem Start of loop to find and print narcissistic numbers
180 for n = 0 to 10000000    /* Number to check for narcissism
190 if c &amp;gt;= 25 then: stop    /* End after 25 numbers
200 *
210 gosub 700                /* Get number of digits in d
220 let x = n                /* Copy of number
230 let y = 0                /* Result of calculation
240 * 
250 rem Start of loop to calculate narcissistic number 
260 for i = 1 to d           /* For each digit
270 let x1 = int(x/10)       /* Get all but last digit of x
280 let x0 = x - (10 * x1)   /* Get last digit of x
290 let y = y + p(x0, d)     /* Accumulate digit ** num digits
300 if y &amp;gt; n then 400        /* Bail out if too big already
310 let x = x1               /* Shift off last digit from x
320 next i                   /* Loop for next digit
330 *
340 if y &amp;lt;&amp;gt; n then 400       /* Not a narcissistic number, jump next
350 print n                  /* Print narcissistic number
360 let c = c + 1            /* Increase count found
370 *
400 next n                   /* Try next number
410 *
500 stop                     /* Fall through
510 *
520 *
599 rem Subroutine to populate p(a, b) with a ** b
600 for a = 0 to 9           /* Digit
610 p(a, 0) = 0              /* a ** 0 = 0
620 p(a, 1) = a              /* a ** 1 = a
630 for b = 2 to 9           /* Power to raise to 
640 p(a, b) = p(a, b-1) * a  /* Calc next power in series
650 next b                   /* Loop power
660 next a                   /* Loop digit
670 return
698 *
699 rem Subroutine to get number of digits in n
700 if n &amp;gt;= 1000000 then 800
710 if n &amp;gt;= 100000  then 810
720 if n &amp;gt;= 10000   then 820
730 if n &amp;gt;= 1000    then 830
740 if n &amp;gt;= 100     then 840
750 if n &amp;gt;= 10      then 850
760 goto 860
800 let d = 7
801 return
810 let d = 6
811 return
820 let d = 5
821 return
830 let d = 4
831 return
840 let d = 3
841 return
850 let d = 2
851 return
860 let d = 1
861 return
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;closing-thoughts-on-basic&#34;&gt;Closing thoughts on BASIC&lt;/h2&gt;
&lt;p&gt;The strength and weaknesses of the BASIC language are that it is, well, basic. With only a handful of features it is hard to get lost, but doing anything more complex than the above would be painful. From a modern programming viewpoint, the need for line numbers, restrictions on variable names and lack of types makes it quite different from what we would do now so despite some nostalgia for 80s home computing I can see why this is a bad choice for even a beginner&amp;rsquo;s programming language today. But it&amp;rsquo;s also clear to see how revolutionary BASIC was when first introduced and how it opened the door to programming for a whole new set of people.&lt;/p&gt;
&lt;p&gt;Likewise, the self contained environment for UM BASIC makes it easy to start writing code without having to know much about MTS, but you lose the access to useful tools like a full screen editor. I think this, and the fact that better languages were available, explains why BASIC was not a popular choice for general users of MTS - according to &lt;a href=&#34;http://hdl.handle.net/2027/mdp.39015009827232?urlappend=%3Bseq=5&#34;&gt;Introduction to programming and debugging in MTS&lt;/a&gt;, only 2% by frequency of use in 1984.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s more on the mathematics of narcissistic numbers &lt;a href=&#34;http://mathworld.wolfram.com/NarcissisticNumber.html&#34;&gt;at Wolfram MathWorld&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Source code for these programs can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on Github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>BASIC - Language features</title>
      <link>https://try-mts.com/basic-language-features/</link>
      <pubDate>Sun, 30 Nov 2014 00:39:38 +0000</pubDate>
      <guid>https://try-mts.com/basic-language-features/</guid>
      <description>&lt;p&gt;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&amp;rsquo;t used it for 30 years I felt right at home.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://en.wikipedia.org/wiki/BASIC&#34;&gt;Wikipedia article&lt;/a&gt; defines three broad generations of BASIC&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Early versions on mainframes, 1964-1980&lt;/li&gt;
&lt;li&gt;Home computer ROM BASIC, 1980-1990&lt;/li&gt;
&lt;li&gt;More structured versions on PCs, 1990-date&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The below is a quick look at the main features in MTS BASIC; see the Further Information section for more complete guides.&lt;/p&gt;
&lt;h2 id=&#34;program-format&#34;&gt;Program format&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Full line comments can be introduced with &lt;code&gt;REM&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt;. To add a comment that lasts until the end of the line you can use &lt;code&gt;/*&lt;/code&gt; (very confusing to C programmers!).&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10 rem This is a comment
20 * This also
30 let x = 3 /* Line comment
40 let y = 4
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;types-and-variables&#34;&gt;Types and variables&lt;/h2&gt;
&lt;p&gt;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 &lt;code&gt;LET&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10 let A = 1.23
20 let A1 = 3
30 let AA = &amp;#34;Hello world&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can have arrays and matrices of numbers which can be indexed using &lt;code&gt;()&lt;/code&gt;, eg &lt;code&gt;a(1)&lt;/code&gt; or &lt;code&gt;b(2,3)&lt;/code&gt;. The size can be set using the &lt;code&gt;DIM&lt;/code&gt; statement but if not given they are size 10. Indices start at 0. The below will print 100.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10 dim a(2,2)
20 a(1,0) = 100
30 a(1,1) = 200
40 print a(1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Strings are implemented as vectors of cells containing strings, so &lt;code&gt;AA&lt;/code&gt; is the same as &lt;code&gt;AA(0)&lt;/code&gt; and you can also access &lt;code&gt;AA(1)&lt;/code&gt;, &lt;code&gt;AA(2)&lt;/code&gt; etc.&lt;/p&gt;
&lt;h2 id=&#34;statements&#34;&gt;Statements&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;INPUT&lt;/code&gt; will prompt for the user to enter a value which will be assigned to a variable; &lt;code&gt;PRINT&lt;/code&gt; will display a variable or expression, using &lt;code&gt;;&lt;/code&gt; or &lt;code&gt;,&lt;/code&gt; to control formatting.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  10 input a, b
  20 print a; b, (a+b)/2

: run
? 10
? 20
  10 20          15
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;INP&lt;/code&gt; and &lt;code&gt;OUT&lt;/code&gt; are like &lt;code&gt;INPUT&lt;/code&gt; and &lt;code&gt;PRINT&lt;/code&gt; but also return the value assigned to the variable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GOTO x&lt;/code&gt; will jump unconditionally to line &lt;code&gt;x&lt;/code&gt;. Note you can&amp;rsquo;t &lt;code&gt;GOTO&lt;/code&gt; a non-executable line like a comment. There is also a rather hairy computed &lt;code&gt;GOTO&lt;/code&gt; where they program will jump to a line based on the result of an expression.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10 goto 40
20 print &amp;#34;This will be printed second&amp;#34;
30 stop
40 print &amp;#34;This will be printed first&amp;#34;
45 let a=2
50 goto (60,60,20,60) a+1
60 print &amp;#34;This will not be printed&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;IF .. THEN&lt;/code&gt; statement will calculate an expression and then either jump to another line or execute a statement. Note that there is no &lt;code&gt;ELSE&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10 let a = 5
20 if a &amp;gt; 1 then 40
30 print &amp;#34;This will not be printed&amp;#34;
40 if a &amp;lt;&amp;gt; 5 then: print &amp;#34;This will not be printed&amp;#34;
50 if a = 5 then: print &amp;#34;This will be printed&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Simple loops can be done with &lt;code&gt;FOR&lt;/code&gt; .. &lt;code&gt;NEXT&lt;/code&gt; with an optional &lt;code&gt;STEP&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;GOSUB x&lt;/code&gt; will call a subroutine at line &lt;code&gt;x&lt;/code&gt;; execution will resume from the current line when a &lt;code&gt;RETURN&lt;/code&gt; statement is found.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to temporarily transfer execution to another program with &lt;code&gt;CALL&lt;/code&gt; or permanently transfer with &lt;code&gt;CHAIN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Simple functions can be defined with &lt;code&gt;DEF&lt;/code&gt;, eg &lt;code&gt;DEF FNS(x)=x*x&lt;/code&gt; for a square function. Only one parameter is allowed and the function name can only be a single character A-Z.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;STOP&lt;/code&gt; will end a program and &lt;code&gt;PAUSE&lt;/code&gt; will prompt the user for input before continuing.&lt;/p&gt;
&lt;h2 id=&#34;library-functions&#34;&gt;Library functions&lt;/h2&gt;
&lt;p&gt;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&amp;rsquo;t do &lt;code&gt;PRINT CLS()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;String operations include conversion to numbers (&lt;code&gt;STN&lt;/code&gt;) and back (&lt;code&gt;NTS&lt;/code&gt;) along with access to the surrounding environment such as &lt;code&gt;UID&lt;/code&gt; for user ID and &lt;code&gt;CLS&lt;/code&gt; for time of day.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10 let ss = cls()
20 print ss
30 let ss = uid()
40 print ss
: run
 0:59:02
 ST01
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Numeric functions include mathematical support functions like &lt;code&gt;ABS()&lt;/code&gt; and &lt;code&gt;SIN()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;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             
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s pretty much it for BASIC. In the next post we&amp;rsquo;ll look at a &lt;a href=&#34;https://try-mts.com/basic-narcissistic-numbers/&#34;&gt;larger example&lt;/a&gt; of a MTS BASIC program.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol10-BASICInMTS-Dec1980.pdf&#34;&gt;MTS Volume 10&lt;/a&gt; offers a complete description of the BASIC language and environment.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>BASIC - Introduction</title>
      <link>https://try-mts.com/basic-introduction/</link>
      <pubDate>Sun, 23 Nov 2014 03:49:59 +0000</pubDate>
      <guid>https://try-mts.com/basic-introduction/</guid>
      <description>&lt;p&gt;As a gentle start to this series we&amp;rsquo;ll look at something suitably basic - the BASIC language itself.&lt;/p&gt;
&lt;h2 id=&#34;the-basic-language&#34;&gt;The BASIC language&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2 id=&#34;basic-on-mts&#34;&gt;BASIC on MTS&lt;/h2&gt;
&lt;p&gt;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 &lt;a href=&#34;http://en.wikipedia.org/wiki/Dartmouth_BASIC&#34;&gt;Dartmouth Basic&lt;/a&gt;, with some minor changes such as using &lt;code&gt;**&lt;/code&gt; instead of &lt;code&gt;↑&lt;/code&gt; for exponentiation.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;No special installation instructions to get BASIC running - just do the standard D6.0 setup as described in &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;this guide&lt;/a&gt; and then sign on as a regular user such as &lt;code&gt;ST01&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;running-basic&#34;&gt;Running BASIC&lt;/h2&gt;
&lt;p&gt;Type &lt;code&gt;$run *BASIC&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;When you start BASIC you will see the &lt;code&gt;:&lt;/code&gt; prompt. Here you can enter commands, starting with &lt;code&gt;/&lt;/code&gt;, to control the BASIC environment. Use &lt;code&gt;/open NAME&lt;/code&gt; 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 &lt;code&gt;/save&lt;/code&gt;. Note the files are saved in a special, sequential format that can only easily be read by BASIC.&lt;/p&gt;
&lt;p&gt;To enter the actual program you can type lines starting with a line number. To correct lines you can retype it; there are also &lt;code&gt;/&lt;/code&gt; commands such as &lt;code&gt;/alter&lt;/code&gt; to do simple line editing. Type a line number on its own to delete it. As it is a separate environment you can&amp;rsquo;t use tools like &lt;code&gt;$edit&lt;/code&gt; to visually edit files; you could create a text file outside of BASIC and use &lt;code&gt;/include FILE&lt;/code&gt; to import it, however.&lt;/p&gt;
&lt;p&gt;To view the code, type &lt;code&gt;/list&lt;/code&gt;; to execute the program, type &lt;code&gt;/run&lt;/code&gt;. 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 &lt;code&gt;&amp;gt;&lt;/code&gt; prompt. Here you can &lt;code&gt;/display&lt;/code&gt; variables, change them with &lt;code&gt;/modify&lt;/code&gt; and either &lt;code&gt;/continue&lt;/code&gt; or &lt;code&gt;/stop&lt;/code&gt; execution.&lt;/p&gt;
&lt;p&gt;To leave BASIC, type &lt;code&gt;/bye&lt;/code&gt;, remembering to &lt;code&gt;/save&lt;/code&gt; first if you want to keep your program. When you start BASIC again, use &lt;code&gt;/permcatalog&lt;/code&gt; to view the programs on disk and &lt;code&gt;/open&lt;/code&gt; to load one.&lt;/p&gt;
&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a terminal log of how to start BASIC and create a simple program.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *basic
: /open hello
&amp;amp;  &amp;#34;HELLO&amp;#34; has been created.
: 10 for i = 1 to 5
: 20 print &amp;#34;Hello World!&amp;#34;
: 30 next i
: /run
  Hello World!
  Hello World!
  Hello World!
  Hello World!
  Hello World!
+  Program Ends
: /save
&amp;amp; Done
: /bye
&amp;amp;  Off at 21:53:35 on 11-17-14
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the next post we&amp;rsquo;ll look at BASIC &lt;a href=&#34;https://try-mts.com/basic-language-features/&#34;&gt;language features&lt;/a&gt; in more detail.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the &lt;a href=&#34;http://en.wikipedia.org/wiki/BASIC&#34;&gt;Wikipedia article&lt;/a&gt; for more details on the history of BASIC. In 2014, Dartmouth created the website &lt;a href=&#34;http://www.dartmouth.edu/basicfifty/&#34;&gt;BASIC at 50&lt;/a&gt; which has some interesting background on the creation of the language. TIME did &lt;a href=&#34;http://time.com/69316/basic/#&#34;&gt;an article&lt;/a&gt; looking at BASIC&amp;rsquo;s legacy.&lt;/p&gt;
&lt;p&gt;For the MTS version, &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol10-BASICInMTS-Dec1980.pdf&#34;&gt;MTS Volume 10&lt;/a&gt; offers a complete description.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a great beginner&amp;rsquo;s introduction called &amp;ldquo;Fundamental Use of the Michigan Terminal System (Including Simple MTS BASIC)&amp;rdquo; by Thomas J. Schriber from the UM Business School, which is available in the Hathi Trust archives or on &lt;a href=&#34;http://books.google.com/books?id=fdZWAAAAMAAJ&amp;amp;dq=fundamental+use+of+the+michigan+terminal+system+including+simple+mts+basic&amp;amp;source=gbs_navlinks_s&amp;amp;redir_esc=y&#34;&gt;Google Books&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Programming languages in MTS</title>
      <link>https://try-mts.com/programming-languages-in-mts/</link>
      <pubDate>Sat, 22 Nov 2014 12:53:53 +0000</pubDate>
      <guid>https://try-mts.com/programming-languages-in-mts/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/11/mts-languages.png&#34; alt=&#34;MTS languagesm created at http://www.wordle.net/&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Language&lt;/th&gt;
          &lt;th&gt;Introduction&lt;/th&gt;
          &lt;th&gt;Features&lt;/th&gt;
          &lt;th&gt;Sample program&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;BASIC&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/basic-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/basic-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/basic-narcissistic-numbers/&#34;&gt;Narcissistic numbers&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;FORTRAN&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/fortran-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/fortran-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/fortran-calculating-emirps/&#34;&gt;Emirp prime&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;MAD&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/mad-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/mad-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/mad-mergesort&#34;&gt;Mergesort&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;ALGOL 60&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/algol-60-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/algol-60-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/algol-60-josephus-problem/&#34;&gt;Josephus problem&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;ALGOL W&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/algol-w-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/algol-w-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/algol-w-priority-queue/&#34;&gt;Priority queue&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;LISP&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/lisp-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/comparing-mts-lisp-to-modern-lisps/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/lisp-4-bit-adder/&#34;&gt;Four bit adder&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;UTILISP&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/utilisp-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/utilisp-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/the-great-fortran-lisp-shootout/&#34;&gt;LISP vs FORTRAN&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;PL/1&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pli-introduction&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pli-language-features-1&#34;&gt;Features 1&lt;/a&gt;&lt;br&gt; &lt;a href=&#34;https://try-mts.com/pli-language-features-2/&#34;&gt;Features 2&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pli-radix-sort/&#34;&gt;Radix sort&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;SNOBOL&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/snobol-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/snobol-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/snobol-date-formats&#34;&gt;Date formats&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;RATFOR&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/ratfor-flecs-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/ratfor-flecs-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/ratfor-flecs-emirp-primes/&#34;&gt;Emirp primes&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;FLECS&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/ratfor-flecs-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/ratfor-flecs-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/ratfor-flecs-emirp-primes/&#34;&gt;Emirp primes&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;PIL&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pil-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pil-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pil-roman-numerals/&#34;&gt;Roman numerals&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;APL&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/apl-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/apl-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/apl-knuth-shuffle/&#34;&gt;Knuth shuffle&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Assembler&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/system-360-assembly-language-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/system-360-assembly-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/system-360-assembly-language-caesar-cipher/&#34;&gt;Caesar cipher&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;GPSS&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/gpss-introduction-and-barber-shop-simulation/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;-&lt;/td&gt;
          &lt;td&gt;-&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;PLUS&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/plus-introduction/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/plus-language-features/&#34;&gt;Features&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/plus-grep/&#34;&gt;Grep&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;PL360&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;https://try-mts.com/pl360-introduction-and-a-hypotenuse-program/&#34;&gt;Intro&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;-&lt;/td&gt;
          &lt;td&gt;-&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;In this series of posts I will take a look at each of these - plan of attack is 1-3 posts per language covering:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A brief introduction, how to get it working on MTS and &amp;ldquo;Hello World&amp;rdquo;&lt;/li&gt;
&lt;li&gt;A tour of language features&lt;/li&gt;
&lt;li&gt;A more complex example, either implementing a task from Rosetta Stone or running a classic piece of code for that language.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Some languages may warrant more or less attention, and I will also look at the programming environment such as the debugger and link editor.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;I am a working programmer by trade rather than an academic so don&amp;rsquo;t expect too much deep insight or theory - and if you spot any mistakes please let me know.&lt;/p&gt;
&lt;p&gt;For new users of MTS, follow the &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;setup guide&lt;/a&gt;, log on as a regular user such as &lt;code&gt;ST01&lt;/code&gt; and check the &amp;lsquo;Prerequisites&amp;rsquo; 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.&lt;/p&gt;
&lt;h2 id=&#34;source-code&#34;&gt;Source code&lt;/h2&gt;
&lt;p&gt;Source code for each of the examples can be found &lt;a href=&#34;https://github.com/rupertl/mts-languages&#34;&gt;on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;coming-up-later&#34;&gt;Coming up later&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ALGOL 68&lt;/li&gt;
&lt;li&gt;BCPL&lt;/li&gt;
&lt;li&gt;COBOL&lt;/li&gt;
&lt;li&gt;GM APL&lt;/li&gt;
&lt;li&gt;ICON&lt;/li&gt;
&lt;li&gt;LISPs&lt;/li&gt;
&lt;li&gt;PROLOG/KR&lt;/li&gt;
&lt;li&gt;XPLs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;specialised-languages&#34;&gt;Specialised languages&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*CSMP&lt;/code&gt; Continuous System Modeling Program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*UMIST&lt;/code&gt; An  interactive  text-processing  language  patterned  after  TRAC&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*CLPARSEGEN&lt;/code&gt; A parser generator that outputs PLUS code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cross-assemblers&#34;&gt;Cross assemblers&lt;/h3&gt;
&lt;p&gt;I will probably not look at these, unless they are especially interesting.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*1130ASM&lt;/code&gt; IBM 1130 and 1800 Assembler&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*11ASR&lt;/code&gt; PDP-11 Assembler, version AN231&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*1ASR&lt;/code&gt; PDP-1 Assembler (obsolete in D6.0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*8ASR&lt;/code&gt; PDP-8  Assembler (obsolete in D6.0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*9ASR&lt;/code&gt; PDP-9  Assembler (obsolete in d6.0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*ASMT&lt;/code&gt; IBM TSS Assembler (5.0T2)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*I8080ASR&lt;/code&gt; A relocatable cross-assembler for the INTEL 8080&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*M6800ASR&lt;/code&gt; An absolute cross-assembler for the Motorola M6800&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*M6809ASR&lt;/code&gt; An absolute cross-assembler for the Motorola 6809&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*MCS650XASR&lt;/code&gt; An absolute cross-assembler for the MOS Technology MCS6500 family&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*PDP11ASR&lt;/code&gt; A relocatable cross-assembler for the DEC PDP11&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*Z80ASR&lt;/code&gt; A Z80 cross-assembler&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNSP:11PAL&lt;/code&gt; A DEC compatable PDP-11 assembler and simulator&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNSP:F8ASR&lt;/code&gt; A cross-assembler for the Mostek F8&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNSP:PLM&lt;/code&gt; A cross compiler for the INTEL 8080 PL/M language&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;This list is based on the information in the &lt;a href=&#34;https://web.archive.org/web/20230206190627/http://archive.michigan-terminal-system.org/discussions/programming-languages-available-under-mts&#34;&gt;MTS Archive&lt;/a&gt; describing what languages were available at the time of D6.0 and which work today, including those where there is no license available.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>$PERMIT me to talk about $FILESTATUS</title>
      <link>https://try-mts.com/permit-me-to-talk-about-filestatus/</link>
      <pubDate>Sun, 16 Nov 2014 03:13:03 +0000</pubDate>
      <guid>https://try-mts.com/permit-me-to-talk-about-filestatus/</guid>
      <description>&lt;p&gt;The information that MTS stores about files is quite different from today&amp;rsquo;s operating systems and it also has an interesting permissions model. In this post we&amp;rsquo;ll look at this information in more detail using the &lt;code&gt;$FILESTATUS&lt;/code&gt; and &lt;code&gt;$PERMIT&lt;/code&gt; commands.&lt;/p&gt;
&lt;h2 id=&#34;filestatus&#34;&gt;&lt;code&gt;$FILESTATUS&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$FILESTATUS&lt;/code&gt;, which can be abbreviated to &lt;code&gt;$F&lt;/code&gt;, displays information about files. It takes three parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name&lt;/li&gt;
&lt;li&gt;format&lt;/li&gt;
&lt;li&gt;information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running it without any parameters lists the files owned by the current user:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; # f
   ALPHA BETA 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use &lt;code&gt;?&lt;/code&gt; 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 &lt;code&gt;f -?&lt;/code&gt;. To see all files under another ID use &lt;code&gt;f CCID:?&lt;/code&gt;; this will only list files you have access to.&lt;/p&gt;
&lt;p&gt;The information parameter allows you to see further details about each file. To see all information MTS has, use the &lt;code&gt;TOTAL&lt;/code&gt; parameter:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# fi ? total
  ALPHA             Size=1P, MinSaves=0P, TruncSaves=0P, MaxSize=32767P, 
   ExpFac=Def, Type=Line, RPM=0, IdleDays=36525, Lines=1, Holes=1, Avlen=19, 
   MaxLen=19, AvailSpace=2969, MaxHole=2969, Create=Nov15/14, 
   LastRef=Nov15/14, LastCatalog=Nov15/14, LastData=10:30:46 Nov15/14, 
   Volume=MTS600, Owner=ST03, Loc=3380, UseCnt=3, PKey=*EXEC, 
   FullAccess=Unlim Owner, None Others 
  BETA              Size=1P, MinSaves=0P, TruncSaves=0P, MaxSize=32767P, 
   ExpFac=Def, Type=Line, RPM=0, IdleDays=36525, Lines=0, Holes=1, Avlen=0, 
   MaxLen=0, AvailSpace=2990, MaxHole=2990, Create=Nov15/14, LastRef=Nov15/14,
   LastCatalog=Nov15/14, LastData=10:30:59 Nov15/14, Volume=MTS600, 
   Owner=ST03, Loc=3380, UseCnt=1, PKey=*EXEC, Empty, FullAccess=Unlim Owner, 
   None Others 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s look at each of these&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Size&lt;/code&gt; and &lt;code&gt;MaxSize&lt;/code&gt; give the current and maximum size in pages (4096 bytes)&lt;/li&gt;
&lt;li&gt;MTS allocates more space to a file than its contents to allow it to grow. &lt;code&gt;ExpFac&lt;/code&gt; is the expansion factor each time it needs to grow, here the default 10%. &lt;code&gt;MinSaves&lt;/code&gt; shows how much space would be saved if the file was resized to its minimum possible size; &lt;code&gt;TruncSaves&lt;/code&gt; shows spaces saved if the &lt;code&gt;$truncate&lt;/code&gt; command was run to remove unused space at the end of the file. &lt;code&gt;AvailSpace&lt;/code&gt; gives the amount of space in bytes before the file needs to be expanded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Type&lt;/code&gt; is the file type, line or sequential&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RPM&lt;/code&gt; is the number of references to the file per month since its creation date and &lt;code&gt;IdleDays&lt;/code&gt; the number of days since its last reference. &lt;code&gt;RPM&lt;/code&gt; always seems to be zero and &lt;code&gt;Idledays&lt;/code&gt; larger than 36,000 whatever the usage of the file, so I suspect something is not working correctly here. &lt;em&gt;Update 14-Apr-2015: this is due to a Y2K bug, see Jeff&amp;rsquo;s comment below.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UseCnt&lt;/code&gt; gives the number of times the file has been used since its creation; this appears to be set correctly.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Lines&lt;/code&gt;, &lt;code&gt;Avlen&lt;/code&gt; and &lt;code&gt;MaxLen&lt;/code&gt; give the number of lines and their average and maximum lengths&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Holes&lt;/code&gt; and &lt;code&gt;MaxHole&lt;/code&gt; refer to holes in line file due to line replacement operations that have left spaces&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Create&lt;/code&gt; gives the creation date, &lt;code&gt;LastRef&lt;/code&gt; the time the file was last used, &lt;code&gt;LastCatalog&lt;/code&gt; the time the catalog was updated for this file and &lt;code&gt;LastData&lt;/code&gt; the last time data was changed in the file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Volume&lt;/code&gt; and &lt;code&gt;Loc&lt;/code&gt; gives the disk volume and type of storage the file is on&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Owner&lt;/code&gt; gives the file owner&amp;rsquo;s ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PKey&lt;/code&gt; and the remaining information gives details on who can access the file, which we will look at further below.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of showing all this you can specify individual items to display:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# fi ? lines avlen
  ALPHA             Lines=1, Avlen=19 
  BETA              Lines=0, Avlen=0 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;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:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# fi ? col spacing=3 create lastref


   File name        Create   DateLast 
                    Date     Referenc 
 


  ALPHA             Nov15/14 Nov15/14 


  BETA              Nov15/14 Nov15/14 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;permit&#34;&gt;&lt;code&gt;$PERMIT&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;$PERMIT&lt;/code&gt; command, or just &lt;code&gt;$P&lt;/code&gt;, takes three parameters&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;files&lt;/li&gt;
&lt;li&gt;access level&lt;/li&gt;
&lt;li&gt;users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also selectively clone permissions from one file to another with the syntax &lt;code&gt;$PERMIT files1 LIKE files2 EXCEPT access_level&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are six basic categories of access level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;R&lt;/code&gt; - Read&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WE&lt;/code&gt; - Write-expand - can add lines to a file but can&amp;rsquo;t change or delete existing ones&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WC&lt;/code&gt; - Write-change - can change or delete lines from a file but can&amp;rsquo;t add new ones&lt;/li&gt;
&lt;li&gt;&lt;code&gt;T&lt;/code&gt; - Truncate and renumber&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D&lt;/code&gt; - Destroy and rename&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt; - Permit - allow other users to change permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These can be combined using various aliases, for example &lt;code&gt;RW&lt;/code&gt; allows the first three categories above, &lt;code&gt;N&lt;/code&gt; specifies no access and &lt;code&gt;U&lt;/code&gt; unlimited access&lt;/p&gt;
&lt;p&gt;The users parameter allows you specify a single user ID or even a wildcard user ID (like &lt;code&gt;ST0?&lt;/code&gt;) as well as other combinations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALL&lt;/code&gt; - all users&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OTHERS&lt;/code&gt; - all users except those which already have access info specified&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ME&lt;/code&gt; - access for signed in user ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OWNER&lt;/code&gt; - file owner&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROJECT=xxxx&lt;/code&gt; - users under project ID xxxx&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PKey=xxxx&lt;/code&gt; - see the next section&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;*PERMIT&lt;/code&gt; will show the current level of access after each command. Some examples:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# permit alpha r st01, rw st02
  Access to file &amp;#34;ALPHA&amp;#34; is now Read ST01, RW ST02, Unlim Owner, None Others
# permit alpha d st0?, read others
  Access to file &amp;#34;ALPHA&amp;#34; is now Read ST01, RW ST02, Unlim Owner, Destroy ST0?,
   Read Others
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;program-keys&#34;&gt;Program keys&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;One example is the &lt;code&gt;*CKID&lt;/code&gt; program that runs at sign on for certain shared IDs and checks which secondary IDs can use that ID. The config file, &lt;code&gt;CKID:ACCESS&lt;/code&gt; should only be accessible to that program, so it is permitted to the pkey for &lt;code&gt;*CKID&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# fi ckid:access access
  CKID:ACCESS       FullAccess=None Others, Read Pkey=*CKID 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;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 &lt;code&gt;MYDB&lt;/code&gt; which is the only way a user should access the database file &lt;code&gt;DBFILE&lt;/code&gt;. First set a program key:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$CONTROL MYDB PKEY=MYDB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and then permit access:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$PERMIT DBFILE RW PKEY=MYDB
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the online help for &lt;code&gt;$FILESTATUS&lt;/code&gt; and &lt;code&gt;$PERMIT&lt;/code&gt;, and a detailed write up in &lt;a href=&#34;http://hdl.handle.net/2027.42/79588&#34;&gt;MTS Volume 1&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>System start-up in more detail</title>
      <link>https://try-mts.com/system-start-up-in-more-detail/</link>
      <pubDate>Sun, 09 Nov 2014 04:08:17 +0000</pubDate>
      <guid>https://try-mts.com/system-start-up-in-more-detail/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/11/2273907366_8c4393141c_o_m.jpg&#34; alt=&#34;IBM S/360 front panel&#34;&gt;
&lt;sup&gt;Front panel of an IBM S/360 mainframe, from &lt;a href=&#34;http://ookaboo.com/o/pictures/picture/26078086/Front_Panel_of_IBM_360_Mainframe&#34;&gt;ookaboo.com&lt;/a&gt;. License: &lt;a href=&#34;http://creativecommons.org/licenses/by/2.0/&#34;&gt;CC BY 2.0&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;This post is a more detailed look at what happens at system start-up in MTS - it&amp;rsquo;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.&lt;/p&gt;
&lt;p&gt;Most of this was put together by reading the job programs and config files referenced by the Operator&amp;rsquo;s manual: if I&amp;rsquo;ve omitted or misunderstood anything please let me know in the comments and I&amp;rsquo;ll update this post.&lt;/p&gt;
&lt;h2 id=&#34;ipl&#34;&gt;IPL&lt;/h2&gt;
&lt;p&gt;When you start the IPL process by typing &lt;code&gt;ipl 260&lt;/code&gt; on the Hercules console, the system will load &lt;code&gt;IPLBOOT&lt;/code&gt; from a special area on the disk drive on device 260, which will in turn load &lt;code&gt;UMLOAD&lt;/code&gt; and then &lt;code&gt;IPLREAD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;IPLREAD&lt;/code&gt; will give the familiar prompt&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Do you wish to run the current system?  (YES or NO).
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Typing &lt;code&gt;yes&lt;/code&gt; at this point will cause the system to load the file &lt;code&gt;*IPL.0&lt;/code&gt; and then execute it.&lt;/p&gt;
&lt;p&gt;Typing &lt;code&gt;no&lt;/code&gt; will drop you into the command line for &lt;code&gt;IPLREAD&lt;/code&gt;: here you can examine or change memory, or choose a different system to load. &lt;code&gt;RAMROD&lt;/code&gt; keeps the last 3 versions of the system in &lt;code&gt;*IPL.1&lt;/code&gt;, &lt;code&gt;*IPL.2&lt;/code&gt; etc, so if there was something wrong with your last change to the system you could boot the previous version with the commands&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LOAD NAME=*IPL.1
START
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;init&#34;&gt;INIT&lt;/h2&gt;
&lt;p&gt;The system will then configure itself and start the &lt;code&gt;INIT&lt;/code&gt; job, which will print:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Time and date have been set to ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and will start the &lt;code&gt;OPER&lt;/code&gt; and &lt;code&gt;DMGR&lt;/code&gt; jobs which manage the operator&amp;rsquo;s console and disk respectively.&lt;/p&gt;
&lt;h2 id=&#34;mts-init&#34;&gt;MTS INIT&lt;/h2&gt;
&lt;p&gt;The system will then run a MTS task as user &lt;code&gt;INIT&lt;/code&gt;, running commands from the file &lt;code&gt;INIT:INITCMD&lt;/code&gt;. This will print the system name and prompt for the reason for rebooting, which will be written to file &lt;code&gt;INIT:REASON&lt;/code&gt;; history of all reboots can be found in &lt;code&gt;INIT:RELOADLOG&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It will then start the PDP (Paging Drum Processor) and a number of operator jobs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*CHK&lt;/code&gt; 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 &lt;code&gt;PA2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*S2L&lt;/code&gt; to load shared components into memory, eg the editor; this will invoke the Named Address Loader &lt;code&gt;*NAL&lt;/code&gt;. I think this also prints the PEEK initialization messages.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*CLN&lt;/code&gt; to clean out old system check files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*CMD&lt;/code&gt; to start writing &lt;a href=&#34;https://try-mts.com/cmdstat-or-why-does-dwb-keep-asking-me-for-tapes/&#34;&gt;command statistics&lt;/a&gt; to buffers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*FSM&lt;/code&gt; which sets things up for file backup jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*STI&lt;/code&gt; to check the statistics file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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&amp;rsquo;s console.&lt;/p&gt;
&lt;p&gt;Basic start-up is now complete.&lt;/p&gt;
&lt;h2 id=&#34;hasp&#34;&gt;HASP&lt;/h2&gt;
&lt;p&gt;Next, the operator would start up the spooler by running &lt;code&gt;HASP&lt;/code&gt;, and the HASP initialisation jobs &lt;code&gt;MTS *HSP&lt;/code&gt;. The latter reads commands from &lt;code&gt;STRT:HSP&lt;/code&gt; which defines which devices should be started:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;O $START MORE RDR1
O $START PCH1
O $START TN PTR2
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;las&#34;&gt;LAS&lt;/h2&gt;
&lt;p&gt;The penultimate step is to &amp;rsquo;let out the lines&amp;rsquo; by running &lt;code&gt;MTS *LAS&lt;/code&gt;. This reads commands from &lt;code&gt;STRT:LAS&lt;/code&gt;. It will start MTS on each of the configured terminal devices with &lt;code&gt;MTS DS02&lt;/code&gt; &amp;hellip; &lt;code&gt;MTS DS05&lt;/code&gt;. It will then run the following operator jobs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*CLK&lt;/code&gt; to start the job scheduling program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*TPR&lt;/code&gt; which I believe is part of the tape cataloguing system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*MGR&lt;/code&gt; which manages email&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*TCM&lt;/code&gt; and &lt;code&gt;*FTP SERV&lt;/code&gt; which refer to network services we don&amp;rsquo;t have on Hercules.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, start batch job processing again with the HASP command &lt;code&gt;$release ex&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYYmVmNTNiMDUtN2ZlNS00ZDI2LTgzZTktNjBmODU4MTE3OGZm&#34;&gt;Operator&amp;rsquo;s manual&lt;/a&gt; has further information on the start up process and many of the operator jobs.&lt;/p&gt;
&lt;p&gt;On the distribution tapes, Component 816 has documentation on Clockwatcher.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some useful Hercules features</title>
      <link>https://try-mts.com/some-useful-hercules-features/</link>
      <pubDate>Sun, 02 Nov 2014 03:24:52 +0000</pubDate>
      <guid>https://try-mts.com/some-useful-hercules-features/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/11/hercules-console.png&#34; alt=&#34;hercules console panel&#34;&gt;
&lt;sup&gt;The Hercules console panel, which you can get by pushing Escape while in the Hercules console.&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In this post we&amp;rsquo;ll look at the Hercules emulator itself in more detail including some useful features when running MTS.&lt;/p&gt;
&lt;h2 id=&#34;which-version&#34;&gt;Which version?&lt;/h2&gt;
&lt;p&gt;As noted at &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/discussions/mts-today/8herculesinoctober2014&#34;&gt;the MTS archive site&lt;/a&gt;, there are two versions of Hercules currently in development, 3.11 and 4.xx. Both run MTS OK, including fixes for printer support not present in older versions. 3.11 has Windows binaries at &lt;a href=&#34;http://www.hercules-390.eu/&#34;&gt;http://www.hercules-390.eu/&lt;/a&gt; and source code that can be built on other platforms; 4.xx is just available in source form on &lt;a href=&#34;https://github.com/hercules-390/hyperion&#34;&gt;Github&lt;/a&gt;. Apart from that it&amp;rsquo;s not clear if there are advantages in one version over the other for MTS users.&lt;/p&gt;
&lt;p&gt;The below items were tested on both versions of Hercules running on Linux. If you encounter any other differences let me know and I&amp;rsquo;ll update this post.&lt;/p&gt;
&lt;p&gt;One word of warning: although you can take an emulated system running on 3.11 and start it using 4.xx, it seems you cannot go the other direction: the 3.11 code will see the disk file written to using 4.xx as corrupt and try to repair it, which makes it unbootable on 3.11.&lt;/p&gt;
&lt;h2 id=&#34;suspend-and-resume&#34;&gt;Suspend and resume&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s possible to suspend a running instance of Hercules to disk and resume it later - useful if you need to reboot your host OS but don&amp;rsquo;t want to shut down and start up MTS again.&lt;/p&gt;
&lt;p&gt;From the Hercules console, issue the &lt;code&gt;suspend&lt;/code&gt; command. A file called &lt;code&gt;hercules.srf.gz&lt;/code&gt; will be created in the same directory as the config file and Hercules will immediately exit.&lt;/p&gt;
&lt;p&gt;When you are ready to start again, run Hercules as normal but issue the &lt;code&gt;resume&lt;/code&gt; command rather than IPLing. You will also need to reconnect any 3270 terminals connected via socket.&lt;/p&gt;
&lt;p&gt;This worked perfectly for me on 4.xx - the MTS clock updated itself and everything kept on running including any logged in sessions. 3.11 could suspend but not resume, at least with my build of the source code.&lt;/p&gt;
&lt;h2 id=&#34;shadow-disks&#34;&gt;Shadow disks&lt;/h2&gt;
&lt;p&gt;Hercules has the concept of &amp;lsquo;shadow disks&amp;rsquo;, where an emulated disk&amp;rsquo;s contents can be started in one file and then continued with updates made to another file. If you take a look at the &lt;code&gt;Disks&lt;/code&gt; directory you will see something like this on the 6.0A distribution:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ls -l Disks/
-r--r--r-- 1 rupert rupert 68738104 Jan 16  2012 mts600.dsk
-r--r--r-- 1 rupert rupert 13798923 Jan 18  2012 mts600_1.dsk
-rw-r----- 1 rupert rupert  6062763 Oct 28 20:16 mts600_2.dsk
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The combination of &lt;code&gt;mts600.dsk&lt;/code&gt; and the two shadow files (denoted with a &lt;code&gt;_&lt;/code&gt;) make up the &lt;code&gt;MTS600&lt;/code&gt; disk file. The D6.0A distribution has been set up so &lt;code&gt;mts600,dsk&lt;/code&gt; contains the base D6, &lt;code&gt;mts600_1.dsk&lt;/code&gt; contains patches to bring it up to D6.0A and your updates are in &lt;code&gt;_2&lt;/code&gt;. So you can delete the &lt;code&gt;_2&lt;/code&gt; file to restore the system to its initial D6.0A state.&lt;/p&gt;
&lt;p&gt;You can use this yourself if you want to make changes to MTS and then have the ability to roll back to previous versions. To do this, shutdown MTS and make &lt;code&gt;mts600_2.dsk&lt;/code&gt; read only. When you start up Hercules and IPL again, a new file &lt;code&gt;mts5600_3.dsk&lt;/code&gt; will be created. To roll back, stop Hercules and remove the &lt;code&gt;_3&lt;/code&gt; file.&lt;/p&gt;
&lt;h2 id=&#34;sockets&#34;&gt;Sockets&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s possible to define the card reader as a socket device instead of a file. This allows you to submit jobs over the network or from your host OS by connecting to a socket instead of writing to the file &lt;code&gt;Units/RDR1.txt&lt;/code&gt;. You can use the &lt;code&gt;netcat&lt;/code&gt; utility which is available for most OSs or you could write a program to do this.&lt;/p&gt;
&lt;p&gt;First, edit the Hercules config file and replace the line under &amp;lsquo;Unit Record Devices&amp;rsquo; for the 3505 device with something like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;000C   3505   127.0.0.1:9090 sockdev ascii eof   # RDR1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you start Hercules, this will listen on port 9090 on the loopback interface for incoming connections. You can change the IP and port as needed in the config file.&lt;/p&gt;
&lt;p&gt;Once MTS is booted and HASP is started, use &lt;code&gt;netcat&lt;/code&gt; like the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;netcat 127.0.0.1 9090 &amp;lt; job.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Where &lt;code&gt;job.txt&lt;/code&gt; is the batch file you want to submit, including a S8 card on the first line (See &lt;a href=&#34;https://try-mts.com/submitting-batch-jobs-from-the-reader/&#34;&gt;the post on using the card reader&lt;/a&gt; for more details on this.)&lt;/p&gt;
&lt;p&gt;If all worked OK, &lt;code&gt;netcat&lt;/code&gt; will not print any output, but you should see the results of the job on the printer device.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also possible to define the printer as an output socket device: when something is printed it will connect to the socket defined in the config file and send the output.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the &lt;a href=&#34;http://hercdoc.glanzmann.org/V311/HerculesUserReference.pdf&#34;&gt;Hercules user manual&lt;/a&gt; for more details on options available.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting the timezone by patching the system with RAMROD and *OBJUTIL</title>
      <link>https://try-mts.com/setting-the-timezone-by-patching-the-system-with-ramrod-and-objutil/</link>
      <pubDate>Sun, 26 Oct 2014 03:01:04 +0000</pubDate>
      <guid>https://try-mts.com/setting-the-timezone-by-patching-the-system-with-ramrod-and-objutil/</guid>
      <description>&lt;p&gt;D6.0A uses the US Eastern Standard timezone by default. In this post we will look at how to set the timezone to something different, which will lead us on to how to patch the system using the RAMROD and OBJUTIL utilities.&lt;/p&gt;
&lt;h2 id=&#34;the-tables-assembly&#34;&gt;The TABLES assembly&lt;/h2&gt;
&lt;p&gt;As MTS was run on a small number of mainframes, the timezone is set inside the resident code of the system rather than by a command. The relevant code is TABLES; you can see the source assembly code in &lt;code&gt;UMPS:UMTABLES#SA&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;...
   359              SET   YEAR=87,TIMEZONE=EST
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which references a macro in &lt;code&gt;copy:tables*sal&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  1500              MACRO
  1501              TIMEZONE &amp;amp;Z
...
  1517     .L4      AIF   (&amp;#39;&amp;amp;Z&amp;#39; NE &amp;#39;EST&amp;#39; AND &amp;#39;&amp;amp;Z&amp;#39; NE &amp;#39;CDT&amp;#39;).L5
  1518     TIMEZONE DC    H&amp;#39;-300&amp;#39;            EASTERN STANDARD OR CENTRAL DAYLIGHT
  1519              AGO   .OUT
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Normally you would change the code, assemble it and reboot. But this requires the &lt;code&gt;ASMH&lt;/code&gt; assembler, which is a licensed IBM product that cannot be distributed with MTS. The alternative, which we will look at here, is to patch the object code with the desired value.&lt;/p&gt;
&lt;h2 id=&#34;roadmap&#34;&gt;Roadmap&lt;/h2&gt;
&lt;p&gt;An overview of the steps we will take&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;RAMROD&lt;/code&gt; to extract the &lt;code&gt;TABLES&lt;/code&gt; deck to a file&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;*OBJUTIL&lt;/code&gt; to patch the file, adding a new &amp;lsquo;card&amp;rsquo; to change the timezone&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;RAMROD&lt;/code&gt; again to incorporate the file into the deck&lt;/li&gt;
&lt;li&gt;Reboot and enjoy the correct time&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ramrod&#34;&gt;&lt;code&gt;RAMROD&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RAMROD&lt;/code&gt; is used to keep track of a set of object decks and combine these into a file suitable for IPL. &amp;lsquo;Decks&amp;rsquo; refers to the older practice of using a deck of punched cards to boot the system; on MTS thankfully this is done using files stored on disk.&lt;/p&gt;
&lt;p&gt;A set of object decks is combined into a system. You can see the systems currently defined, and the current system, with the &lt;code&gt;list systems&lt;/code&gt; and &lt;code&gt;list current&lt;/code&gt; commands in &lt;code&gt;RAMROD&lt;/code&gt;. &lt;code&gt;RAMROD&lt;/code&gt; can be run as the &lt;code&gt;MTS&lt;/code&gt; user as follows; &lt;code&gt;RAMROD&lt;/code&gt; commands are given at the &lt;code&gt;~&lt;/code&gt; prompt:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run mts:ramrod
 Using file &amp;#34;RMRD:ROD&amp;#34;
 Proceed.
~ list systems
 D6.0A-AN172 created from AN152 23:05:42 01-17-12
 AN152 created from AN062 23:29:05 01-15-12
 AN062 created from AN052 01:13:12 01-06-12
 AN052 created from D6.0SYS.600/MP 19:49:40 01-05-84
...
~ list current 
 D6.0A-AN172 created from AN152 23:05:42 01-17-12
 Patch the timezone name and timezone offset in TABLES to be EST
 and GMT minus 5 rather than EDT and GMT minus 4. (jco)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So we can see the current system is &lt;code&gt;D6.0A-AN172&lt;/code&gt;, the comment made when creating the system (which was to set the timezone to EST) and the history of previous systems.&lt;/p&gt;
&lt;p&gt;You can see a list of decks that make up the system with &lt;code&gt;list decknames&lt;/code&gt;; a snippet is below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ LIST DECKNAMES
 CONFIG.CARD.MP (DEC08-86)
 CONFIG.CARD.COMMENTS (AUG16-85)
 LOWCORE.DEFS.MP (SEP11-87)
 SEG0.NCA (SEP17-81)
 UMLOADTV (NOV20/79)
 TABLES.MP (AN172) patched
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;TABLES.MP&lt;/code&gt; is the deck we want. We can see a list of the patches already applied in the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ list sys cur deck tables.mp patches
 TABLES.MP (AN172) patched changed
 REP  004088  0400030001,00000240,00000030,000002A0,00000180 Rearrange OPERDEVS 01:26:36 01-06-12 MTS.
 REP  000180  01F1F0F5F2 CON1 IS A 1052 01:26:51 01-06-12 MTS.
 REP  003F8A  04,FED4 Make the timezone offset -300 (GMT minus 300 minutes or 5 hours)
 REP  003F8C  04,C5E2E340 Make the timezone name &amp;#34;EST&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What we want to do is extract the &lt;code&gt;TABLES.MP&lt;/code&gt; deck to a file, so we can edit it outside the system using &lt;code&gt;*OBJUTIL&lt;/code&gt;. Use the &lt;code&gt;copy&lt;/code&gt; command to do this; let&amp;rsquo;s store this to the temporary file &lt;code&gt;-tables&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ copy current tables.mp -tables
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Exit &lt;code&gt;RAMROD&lt;/code&gt; for now with &lt;code&gt;stop&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;objutil&#34;&gt;&lt;code&gt;*OBJUTIL&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*OBJUTIL&lt;/code&gt; is used to edit a deck. Pass the file to be edited as unit 0:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run *objutil 0=-tables
 OBJUTIL VERSION(PR137) 08:22:26 10-21-14
*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;*OBJUTIL&lt;/code&gt; accepts commands at the &lt;code&gt;*&lt;/code&gt; prompt. First, let&amp;rsquo;s get a listing of the values in the deck using the &lt;code&gt;map&lt;/code&gt; command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* map

  Symbol Type Esid Address Length AF

   Module: TABLES    Size = 0041C8
  TABLES   SD 0001 000000 003E0E     $HSPMNDV LD      0040FC 0004
...
  TIMEZONE LD      003F8A 0004       TIMZONTB LD      003F94 0004
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The timezone is encoded as a half-word (16 bit) offset in minutes from GMT followed by a 8 character string giving the timezone name. We can use the &lt;code&gt;display&lt;/code&gt; command to see the current values, using the &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;length&lt;/code&gt; modifiers:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* display@type=h timezone
  TIMEZONE  &amp;#39;H&amp;#39;  -300
* display@type=c@len=8 timezone+2
  TIMEZONE+2  &amp;#39;C&amp;#39;  &amp;#34;EST     &amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&amp;rsquo;ve confirmed the current setting is GMT minus five hours, name &amp;ldquo;EST&amp;rdquo; padded with blanks. Let&amp;rsquo;s now set it to the value we want; I&amp;rsquo;m going to use plain GMT which is zero offset but you could use any positive or negative value. Use the &lt;code&gt;modify&lt;/code&gt; command with the &lt;code&gt;@repgen&lt;/code&gt; modifier to create a &lt;code&gt;REP&lt;/code&gt; card to set each part of the value&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* modify@repgen timezone H&amp;#39;0&amp;#39;
  TIMEZONE WAS &amp;#39;H&amp;#39;  -300 NOW &amp;#39;H&amp;#39;  +0
  Enter comment for REP card:
? Set timezone to GMT

* modify@repgen timezone+2 C&amp;#39;GMT     &amp;#39;
  TIMEZONE+2 WAS &amp;#39;C&amp;#39;  &amp;#34;EST     &amp;#34; NOW &amp;#39;C&amp;#39;  &amp;#34;GMT     &amp;#34;
  Enter comment for REP card:
? Set timezone name to GMT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A &lt;code&gt;REP&lt;/code&gt; cards means we are adding a line to change the object file rather than changing the existing contents of the file. Note that it prompts for a comment after each change; terminate this with a blank line.&lt;/p&gt;
&lt;p&gt;To be sure, we can now display the new values;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;* display@type=h timezone
  TIMEZONE  &amp;#39;H&amp;#39;  +0
* display@type=c@len=8 timezone+2
  TIMEZONE+2  &amp;#39;C&amp;#39;  &amp;#34;GMT     &amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use &lt;code&gt;stop&lt;/code&gt; to get out of &lt;code&gt;*OBJUTIL&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;back-to-ramrod&#34;&gt;Back to &lt;code&gt;RAMROD&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;RAMROD&lt;/code&gt; again and create a new system from the current system to incorporate your changes:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run mts:ramrod
  Using file &amp;#34;RMRD:ROD&amp;#34;
  Proceed.
~ create D6.0A-CT214
  ** Fromsystem = &amp;#34;D6.0A-AN172&amp;#34;
  &amp;amp; OK
? ok
  Enter comments:
? 2014-10-21 Modify timezone
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The new system name &lt;code&gt;D6.0A-CT214&lt;/code&gt; needs some explanation. The &lt;code&gt;D6.0A&lt;/code&gt; is obvious; the &lt;code&gt;CT214&lt;/code&gt; comes from a convention for naming releases that is described in the distribution driver file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;            1.   The name of the system installed is changed to the &amp;#34;model
                 number&amp;#34; of the date it is installed, e.g. &amp;#34;AY108&amp;#34; for May 10,
                 1978. [2nd and 3rd letters of the month name, two digit day
                  of the month and one digit year.]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So this system I am creating was made on October 21, 2014.&lt;/p&gt;
&lt;p&gt;Next, &lt;code&gt;replace&lt;/code&gt; the &lt;code&gt;TABLES&lt;/code&gt; deck with the one you just edited; supply a comment and leave the version code blank:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ replace tables.mp from -tables
  Enter comments:
? 2014-10-21 Modify timezone to GMT
?
  Enter version code:
?
  ** REPLACE deck &amp;#34;TABLES.MP&amp;#34; from file &amp;#34;-TABLES&amp;#34; :
  &amp;amp; OK
? ok
  Done.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can list the patches again, to be sure:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ list deck tables.mp patches
 TABLES.MP (OCT21-14) patched changed
 REP  004088  0400030001,00000240,00000030,000002A0,00000180 Rearrange OPERDEVS 01:26:36 01-06-12 MTS.
 REP  000180  01F1F0F5F2 CON1 IS A 1052 01:26:51 01-06-12 MTS.
 REP  003F8A  04,FED4 Make the timezone offset -300 (GMT minus 300 minutes or 5 hours)
 REP  003F8C  04,C5E2E340 Make the timezone name &amp;#34;EST&amp;#34;
 REP  003F8A  04,0000 Set timezone to GMT
 REP  003F8C  04,C7D4E34040404040 Set timezone name to GMT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, use the &lt;code&gt;current&lt;/code&gt; command. This will check the system and write to the IPL files:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ current
 There are 4 IPL files with prefix &amp;#34;*IPL.   &amp;#34;
 Loading system D6.0A-CT214 using the UMLOAD deck it contains.
 LOAD: Resident: 2000-5DFC8 UMLOAD Psect: B0000 Pageable: DD000-191A70 End: 1DCFFF
 Contents of IPL file &amp;#34;*IPL.3&amp;#34;:
 AN052 ENTRY=56D18 PSECT=100008 VIRTUAL=B0000...191A70 VIRTUAL_START=100000 UMLOADTV=2000
 WRITTEN BY ID MTSS AT 19:55:37 THU JAN 05/84 BY
 COM 18:50 Jan 05 - Replace RSF to fix the halfword date problem.
 ** Write system &amp;#34;D6.0A-CT214&amp;#34; to IPL file &amp;#34;*IPL.3&amp;#34;:
 &amp;amp; OK
? ok
 System &amp;#34;D6.0A-CT214&amp;#34; has been written to  IPL file &amp;#34;*IPL.3&amp;#34;
 IPL file stacked has been pushed.
 System &amp;#34;D6.0A-CT214&amp;#34; is now the current system.
 &amp;#34;D6.0A-CT214&amp;#34; : Released.
 System &amp;#34;D6.0A-CT214&amp;#34; has been Frozen.
 Done.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-moment-of-truth&#34;&gt;The moment of truth&lt;/h2&gt;
&lt;p&gt;Get out of &lt;code&gt;RAMROD&lt;/code&gt; with &lt;code&gt;stop&lt;/code&gt; and then reboot the system as normal. When it comes up you should get a warning that the timezone has changed, which you can just &lt;code&gt;ok&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; 00000   ** UMMPS/XA ** assembled 04/19/88
 00000 CONFIG CPU 0000 online
 00000 CONFIG CPU 0001 online
 00001 INIT Time and date have been set to  14:45:22 on 10-21-14.
 00002 OPERATOR   Started at 14:45:22 on 10-21-14  Model 3090 Serial 000611
 00002 OPERATOR   Using display DS01 &amp;amp; printer CON1 (1052)
 00004 MTS  Timezone &amp;#34;GMT&amp;#34; is different from timezone during last IPL (&amp;#34;EST&amp;#34;).
 00004 MTS  If you are sure the time is set correctly, enter &amp;#34;OK&amp;#34; to proceed wit
 00004 MTS   IPL; otherwise re-IPL now.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Later on in the boot sequence you can see the new system:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00004 MTS  Reload at 14:45:56 on 10-21-14.  Model: IBM 3090/FF  Serial: 611
00004 MTS  System: D6.0A-CT214 written at 08:34:51 on TUE OCT 21/14.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that&amp;rsquo;s it! The operator&amp;rsquo;s console will now show the correct time for your timezone, as should the &lt;code&gt;display time&lt;/code&gt; command from MTS.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a summary of this process in the &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq#TOC-Q18:-I-can-t-set-the-time-or-timezone-and-the-timezone-and-timezone-offset-used-by-default-are-EDT-Eastern-Daylight-Time-and-GMT-minus-4-hours.-This-isn-t-right-for-my-location.-How-do-you-change-it-&#34;&gt;D6.0A FAQ&lt;/a&gt; and I also found the conversation between Ron Frederick and Jeff Ogden  on the H390-MTS Yahoo list useful to understand the steps required.&lt;/p&gt;
&lt;p&gt;Original instructions to new sites on patching the system deck can be found in the &lt;code&gt;D6.0-NOTES.txt&lt;/code&gt; file in the distribution archive.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RAMROD&lt;/code&gt; has a write-up in file &lt;code&gt;RMRD:RAMROD*PF&lt;/code&gt;, with the wonderful introduction:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;         Cards were made for salesmen and bridge players and in that
    domain they should have undoubtedly remained. Their passing will
    not be mourned by those enlightened programmers  who  have  seen
    fit to rise above the hardcopy world and to place their faith in
    the wonderous MAGNETIC MEDIUM.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;*OBJUTIL&lt;/code&gt; is described in &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol05-SystemServices-Sep1985pdf.pdf&#34;&gt;Volume 5&lt;/a&gt; of the MTS documentation. You can bypass &lt;code&gt;*OBJUTIL&lt;/code&gt; and use the &lt;code&gt;patch&lt;/code&gt; command in &lt;code&gt;RAMROD&lt;/code&gt;, but &lt;code&gt;*OBJUTIL&lt;/code&gt; has much better facilities to enter number and character patches, and to check your work. Using &lt;code&gt;patch&lt;/code&gt; you have to find the address of &lt;code&gt;TIMEZONE&lt;/code&gt;, the ESDID where it located, and convert the offset and the timezone description (in EBCDIC, not ASCII!) to a sequence of hex characters, eg&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~ PATCH DECK TABLES.MP
  Address Esdid Text &amp;lt;comments&amp;gt; :
? 3F8A 4 0000C7D4E34040404040
&lt;/code&gt;&lt;/pre&gt;&lt;!--  LocalWords:  OBJUTIL objutil
 --&gt;
</description>
    </item>
    
    <item>
      <title>CMDSTAT, or why does DWB keep asking me for tapes?</title>
      <link>https://try-mts.com/cmdstat-or-why-does-dwb-keep-asking-me-for-tapes/</link>
      <pubDate>Sun, 19 Oct 2014 03:42:08 +0000</pubDate>
      <guid>https://try-mts.com/cmdstat-or-why-does-dwb-keep-asking-me-for-tapes/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Oct/dwb.png&#34; alt=&#34;DWB prompt&#34;&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve used MTS for more than a few days you will see the above message on the MTS operator&amp;rsquo;s console. In this post we will look at what to do when you get this message and what is going on behind the scenes.&lt;/p&gt;
&lt;h2 id=&#34;the-quick-answer&#34;&gt;The quick answer&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;*DWB&lt;/code&gt; wants to store accumulated logs and command statistics on tape as statistics files fill up. The D6.0A distribution comes with a suitable tape on drive T90A, so at the MTS operator&amp;rsquo;s console answer the tape mount request with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OK M001 T90A
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The complete session will look like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00050 MTS  *DWB: The CMDSTAT file to tape program has been started. ********
00050 MTS  *DWB has been started to dump the CMDSTAT file(s), if any, to tape.
00050 MTS  A tape, labeled as indicated, should be mounted when requested.
00050 MTS  See component 531 for a description of CMDSTAT.
00050 MTS SYS.:  F (1 9TP) M001 on 9TP, ring IN, &amp;#39;CMDSTAT&amp;#39; ********************
OK M001 T90A
00050 MTS Mount check OK.Mount M001 on T90A Ring In
00050 MTS SYS., M001:Accepted on T90A
00050 MTS **** Remove tape M001 from T90A (6250 BPI)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once it has written the statistics, the tape will go offline so the next time &lt;code&gt;*DWB&lt;/code&gt; fires up you will also need to do a&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;devinit 018a
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;on the Hercules console before responding to the tape mount request.&lt;/p&gt;
&lt;h2 id=&#34;the-longer-answer&#34;&gt;The longer answer&lt;/h2&gt;
&lt;p&gt;During system startup, MTS job &lt;code&gt;*CMD&lt;/code&gt; is run automatically. This invokes the &lt;code&gt;CMDPIKUP&lt;/code&gt; program which runs continuously, writing statistics from memory buffers into files.&lt;/p&gt;
&lt;p&gt;Once a file becomes full, &lt;code&gt;CMDPIKUP&lt;/code&gt; will start MTS job &lt;code&gt;*DWB&lt;/code&gt;. This will run the &lt;code&gt;CMDTAPE&lt;/code&gt; program to ask for a tape to be mounted, write the file to tape, empty the file and then exit.&lt;/p&gt;
&lt;p&gt;What files and tapes to use is determined by the file &lt;code&gt;SYS:CMDDIR&lt;/code&gt;. This is only permitted to be read by &lt;code&gt;SYS&lt;/code&gt; so you will have to sign on to &lt;code&gt;SYS&lt;/code&gt; to view it. A typical file looks like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# list cmddir
 
       1     TA MCMD034001 ???4??t?19:46:2405-20-85
       2     FACMDSTAT1
       3     F CMDSTAT2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first character of each line is &lt;code&gt;T&lt;/code&gt; for tape or &lt;code&gt;F&lt;/code&gt; for file. The second character is the status: for files&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SPACE&lt;/code&gt;: not in use&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A&lt;/code&gt; for active, ie the file is being written to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;B&lt;/code&gt; for busy: &lt;code&gt;CMDTAPE&lt;/code&gt; is copying it to tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F&lt;/code&gt; for full: the file needs to be written to tape&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Status for tapes is similar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A&lt;/code&gt; for available but not mounted&lt;/li&gt;
&lt;li&gt;&lt;code&gt;B&lt;/code&gt; for busy, being written to now&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The rest of the file line gives the filename being used to store statistics; the rest of the tape line specifies the tape name, next file and creation date.&lt;/p&gt;
&lt;p&gt;So at this point, file &lt;code&gt;CMDSTAT1&lt;/code&gt; is being written to and no tape is in use.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;*DWB&lt;/code&gt; is running, the &lt;code&gt;CMDDIR&lt;/code&gt; file would look something like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# list cmddir

     1     TB MCMD033001 ???4??t?19:46:2405-20-85
     2     FBCMDSTAT1
     3     FACMDSTAT2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ie the tape is in use and will write out the contents of &lt;code&gt;CMDSTAT1&lt;/code&gt;; meanwhile, &lt;code&gt;CMDSTAT2&lt;/code&gt; is continuing to receive new statistics.&lt;/p&gt;
&lt;p&gt;If the mount request is not attended to and both files fill up, &lt;code&gt;CMDPIKUP&lt;/code&gt; will create a new temporary file &lt;code&gt;CMDSTATx&lt;/code&gt; where x is some character; this will be deleted after it is written to tape.&lt;/p&gt;
&lt;p&gt;On Hercules using D6.0A, this all runs well; the distribution comes with an empty tape file &lt;code&gt;Tapes/cmd001.awd&lt;/code&gt; that is attached to drive T90A ready for use. The frequency that &lt;code&gt;*DWB&lt;/code&gt; needs to run will of course be a lot less than on a real mainframe: a note in the driver listing file says that at busy times the files would fill up in an hour and the tape would fill up in about a week.&lt;/p&gt;
&lt;h2 id=&#34;clearing-the-tape&#34;&gt;Clearing the tape&lt;/h2&gt;
&lt;p&gt;As tapes can have unlimited size on Hercules. the tape file will get bigger and bigger as more statistics are written to it; after running MTS for 4 months my file was over 65 megabytes. I wanted a way to get that space back.&lt;/p&gt;
&lt;p&gt;One solution would appear to be replacing the tape file with an empty tape file (eg by replacing it with the copy of &lt;code&gt;cmd001.aws&lt;/code&gt; that came with the D6.0A distribution). However, &lt;code&gt;CMDTAPE&lt;/code&gt; does not like the tape contents changing from underneath it and gives an error when you try to mount it.&lt;/p&gt;
&lt;p&gt;A better solution is to set a maximum length on the tape in Hercules and use the facility in &lt;code&gt;CMDTAPE&lt;/code&gt; to create new tapes when the existing one is full.&lt;/p&gt;
&lt;p&gt;At the Hercules console, change the tape length with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;devinit 018a Tapes/cmd001.aws maxsizem=50
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(or whatever maximum size in MB you want).&lt;/p&gt;
&lt;p&gt;Then, when you mount the tape responding to &lt;code&gt;*DWB&lt;/code&gt;&amp;rsquo;s request, &lt;code&gt;CMDTAPE&lt;/code&gt; will notice it is full and say&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00049 MTS  *DWB: MOUNT NEW CMDSTAT TAPE RING IN WITH RACK  M002 FOR INITIALIZAT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create a new tape from your host OS command line:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;hetinit -d Tapes/cmd002.aws CMD002
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and at the Hercules console do&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;devinit 018a Tapes/cmd002.aws maxsizem=50
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Back on the MTS operator&amp;rsquo;s console, answer &lt;code&gt;*DWB&lt;/code&gt;&amp;rsquo;s request for a new tape with just the tape drive name&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;T90A
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;CMDTAPE&lt;/code&gt; will mount the tape, label with the next sequence number (so M002 in this case) and initialise it and umount it.&lt;/p&gt;
&lt;p&gt;It will then ask for the tape to be mounted again so it can write statistics to it; &lt;code&gt;devinit&lt;/code&gt; it again at the Hercules console and answer the mount request with the new tape name:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00049 MTS SYS.:  F (1 9TP) M002 on 9TP, ring IN, &amp;#39;CMDSTAT&amp;#39; ********************
OK M002 T90A
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can now safely delete the old tape file &lt;code&gt;cmd001.aws&lt;/code&gt;. Also remember to update the line in &lt;code&gt;hercules.cfg&lt;/code&gt; for the tape to add the &lt;code&gt;maxsizem&lt;/code&gt; parameter:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;018A 3420 Tapes/cmd002.aws maxsizem=50 # T90A, a CMDSTAT tape for use by *DWB
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;turning-off-cmdstat&#34;&gt;Turning off CMDSTAT?&lt;/h2&gt;
&lt;p&gt;Most Hercules users of MTS are not using the command statistics tapes that are being produced so it would nice to turn it off altogether.&lt;/p&gt;
&lt;p&gt;There does not seem to be an easy way to do this (we can hardly blame the MTS designers for this as they were not expecting people to run personal copies of MTS!) One potential solution mentioned on the Yahoo list H390-MTS would seem to be to rewrite &lt;code&gt;CMDTAPE&lt;/code&gt; to only empty the &lt;code&gt;CMDSTAT&lt;/code&gt; files and update &lt;code&gt;CMDDIR&lt;/code&gt;, not actually write data to tape. &lt;code&gt;CMDTAPE&lt;/code&gt; is in 360 assembly language and (I think) requires the IBM copyrighted product &lt;code&gt;ASMH&lt;/code&gt; to assemble, so this would not be a trivial task. In the meantime, feeding tapes occasionally to &lt;code&gt;*DWB&lt;/code&gt; is not a big deal.&lt;/p&gt;
&lt;h2 id=&#34;why-is-the-job-called-dwb&#34;&gt;Why is the job called &lt;code&gt;*DWB&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;DWB is the initials of system programmer Donald W. Boettner. There&amp;rsquo;s a note in the driver file for the &lt;code&gt;CMDSTAT&lt;/code&gt; component 00531/015:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;It would seem that Don could have thought of some name more original than his initials. Don&amp;#39;s response: &amp;#34;Since no one would name an SVC after me, I was forced ... &amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that&amp;rsquo;s why I like MTS so much - you&amp;rsquo;d never see that on a commercial operating system.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYYmVmNTNiMDUtN2ZlNS00ZDI2LTgzZTktNjBmODU4MTE3OGZm&#34;&gt;Operator&amp;rsquo;s Manual&lt;/a&gt;, page 317, has a complete write-up of how the CMDTAPE components fit together and what to do if there are problems.&lt;/p&gt;
&lt;p&gt;You can see the contents of &lt;code&gt;*CMD&lt;/code&gt; and &lt;code&gt;*DWB&lt;/code&gt; by &lt;code&gt;$LIST&lt;/code&gt;ing them from the MTS command line. Source for &lt;code&gt;CMDPIKUP&lt;/code&gt; and &lt;code&gt;CMDTAPE&lt;/code&gt; can be found under component 531 of the MTS D6.0 distribution tapes.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Users and projects</title>
      <link>https://try-mts.com/users-and-projects/</link>
      <pubDate>Sun, 12 Oct 2014 08:57:21 +0000</pubDate>
      <guid>https://try-mts.com/users-and-projects/</guid>
      <description>&lt;p&gt;Rather than be limited by the set of user IDs in the starter system, you can create one or more user IDs for yourself. This post will look in more detail how user IDs are created and used in MTS.&lt;/p&gt;
&lt;h2 id=&#34;user-and-project-ids&#34;&gt;User and project IDs&lt;/h2&gt;
&lt;p&gt;User IDs (or ccIDs) in MTS are four characters long, optionally right padded with &lt;code&gt;.&lt;/code&gt; if shorter (eg &lt;code&gt;ACC.&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Each user ID is a member of one project, which also has a four character ID. A project can have zero or more user IDs in it. Project IDs allowed funds for students on a course or a research team to be grouped together; it also allows sharing of files within a group.&lt;/p&gt;
&lt;p&gt;Note that unlike groups on Unix a user ID can only be a member of one project. One upshot of this on the mainframe is that a person may have several different user IDs, for example one for each course they are on. Thee is no concept of root or Administrator on MTS, but an ID can have extra privileged, eg the ability to access devices directly.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-new-user-id&#34;&gt;Creating a new user ID&lt;/h2&gt;
&lt;p&gt;User and project records are stored in file &lt;code&gt;*ACCOUNTING&lt;/code&gt; and sub files matching &lt;code&gt;*ACCOUNTING?&lt;/code&gt;. Rather than edit these directly you should use the &lt;code&gt;ACCMAINT&lt;/code&gt; program.&lt;/p&gt;
&lt;p&gt;Sign-on as user &lt;code&gt;ACC&lt;/code&gt; using password &lt;code&gt;aardvarks&lt;/code&gt; and then a secondary ID like &lt;code&gt;ST01&lt;/code&gt;. &lt;code&gt;$run ACCMAINT&lt;/code&gt; to start the program, setting unit 0 to be where you want output, eg &lt;code&gt;*print*&lt;/code&gt; or &lt;code&gt;*sink*&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can now use commands like &lt;code&gt;CREATE&lt;/code&gt; and &lt;code&gt;DESTROY&lt;/code&gt;. Here&amp;rsquo;s a sample session where we &lt;code&gt;CRE&lt;/code&gt;ate a privileged ID &lt;code&gt;ABC.&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run accmaint 0=*sink*
# Execution begins   02:55:29 


  MTS ACCOUNTING FILE MAINTENANCE
  CRE ABC.,0,,,,,10000,,,,WABC,LIBRARY=ON,PRIV=ON,PROTECT=ON,PASSWORD=secret1
 


  ABC. password is SECRET1          Name:
  ABC.             SECRET1 
  ABC.             SECRET1 
       (Hexadecimal representations:  ID=X&amp;#39;C1C2C34B&amp;#39; PW=X&amp;#39;E2C5C3D9C5E3F1&amp;#39;)
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looking at the parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt; is the university account number&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10000&lt;/code&gt; is the maximum disk space in pages&lt;/li&gt;
&lt;li&gt;The project name is &lt;code&gt;WABC&lt;/code&gt;: this must start with a W to provide system level privileges.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LIBRARY&lt;/code&gt; - can create a public file, eg one starting with &lt;code&gt;*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PRIV&lt;/code&gt; - bypass checks on funds&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROTECT&lt;/code&gt; - run privileged commands, eg get tape devices directly with &lt;code&gt;GET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Other blank fields are to control resource use and can be blank as shown.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It should also be possible to create non-privileged IDs this way but I have not worked out how to set up funds: if I enter the command&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CRE BA01,0,,,,,10000,,,,BABA,PASSWORD=secret1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and then sign on as &lt;code&gt;ba01&lt;/code&gt; I get &lt;code&gt;The funds allotted this user ID have been depleted.&lt;/code&gt; - if you know how to do this let me know in the comments.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update 14-Apr-2015: see Jeff&amp;rsquo;s comment below on how to do this.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;removing-a-user-id&#34;&gt;Removing a user ID&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$run ACCMAINT&lt;/code&gt; as user &lt;code&gt;ACC&lt;/code&gt; as before but use the &lt;code&gt;DESTROY&lt;/code&gt; command with the user ID and the account number. For example, to remove user &lt;code&gt;ABC.&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run accmaint 0=*sink*
# Execution begins   03:06:37 


  MTS ACCOUNTING FILE MAINTENANCE
  DES ABC,0
  *** ABC. IS TO BE DELETED.
  PLEASE CONFIRM OR ENTER &amp;#34;CANCEL&amp;#34;.
  ok
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;getting-user-and-project-information&#34;&gt;Getting user and project information&lt;/h2&gt;
&lt;p&gt;There are other programs under ID &lt;code&gt;ACC&lt;/code&gt; that may be useful; some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ACCDISPLAY&lt;/code&gt; prompts for a user ID and prints out detailed information for the ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ACCLISTING&lt;/code&gt; lists out info on all user IDs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROJECTDISP&lt;/code&gt; prompts for a project ID and provides details&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROJECTIDS&lt;/code&gt; prompts for a project ID and lists all users in the project&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROJECTLIST&lt;/code&gt; shows details on all projects in the system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Run &lt;code&gt;$filestatus&lt;/code&gt; when signed on as &lt;code&gt;ACC&lt;/code&gt; to see the list.&lt;/p&gt;
&lt;h2 id=&#34;changing-password&#34;&gt;Changing password&lt;/h2&gt;
&lt;p&gt;To change the password of the currently signed-on MTS ID, do &lt;code&gt;SET PW&lt;/code&gt;. It will ask for the old password first then the new password twice for confirmation.&lt;/p&gt;
&lt;p&gt;To change the password for an account you don&amp;rsquo;t know the password for, sign on as the user &lt;code&gt;WPSW&lt;/code&gt;, enter a secondary sign-on ID/password and then &lt;code&gt;$run program1&lt;/code&gt;. If you type a user ID it will reset the password to something new. An example for our new account &lt;code&gt;ST03&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $run program1
# Execution begins   03:17:16 
  ST03
  Password previously set at 04/19/88 17:19:01 EDT has been reset to YLU6GM 
  end of file
# Execution terminated   03:17:26  T=0.011 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;ckid-and-secondary-sign-on&#34;&gt;CKID and secondary sign-on&lt;/h2&gt;
&lt;p&gt;Some privileged IDs, such as MTS, are protected by requiring a secondary sign on. Access is controlled by the file &lt;code&gt;CKID:ACCESS&lt;/code&gt; with lines of the form&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;priv_id secondary_id1 [secondary_id2...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To change this file you will need to sign on as CKID. In the starter system the password for this account is not supplied, so first change its password using the instructions in the previous section.&lt;/p&gt;
&lt;p&gt;Edit the file &lt;code&gt;ACCESS&lt;/code&gt; using &lt;code&gt;$edit&lt;/code&gt; (it&amp;rsquo;s wise to take a backup first). A typical line for ID &lt;code&gt;MTS&lt;/code&gt; looks like this so you can add secondary IDs at the end or remove secondary IDs you don&amp;rsquo;t need.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;MTS. ALWB DLB. DRH. DWB. KLB. MTA. SRB. WSG. W013 W030 W05Q W070 W170 ST00 ST01 ST02 ST03 ST04 ST05 ST06 ST07 ST08 ST09 AB01
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You could also add new privileged IDs and who can access them by adding lines to this file. To ensure the secondary sign-on is checked at sign-on, set the sigfile (the command file run at sign-on) for the account being protected to &lt;code&gt;CKID:CKID.SIG&lt;/code&gt; with &lt;code&gt;set sigfile=CKID:CKID.SIG&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;A write-up on the accounting system can be found in component 104/172 on the MTS tapes. See &lt;a href=&#34;https://try-mts.com/restoring-files-from-tapes/&#34;&gt;restoring components&lt;/a&gt; for details on how to do this and the &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq#TOC-Q31:-How-does-one-create-new-userids-&#34;&gt;FAQ question 31&lt;/a&gt; on how to print the guide.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The operator&#39;s console</title>
      <link>https://try-mts.com/the-operators-console/</link>
      <pubDate>Sun, 05 Oct 2014 04:30:28 +0000</pubDate>
      <guid>https://try-mts.com/the-operators-console/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Sep/operator-screen-annotated.png&#34; alt=&#34;operator console annotated&#34;&gt;&lt;/p&gt;
&lt;p&gt;The operator&amp;rsquo;s console allows you to monitor and control your MTS system. Although you will likely use only a fraction of the features that a real operator would have used, it is worth getting to know how it works and what features are available.&lt;/p&gt;
&lt;p&gt;This post has two sections: a quick tour of the screen, commands and keyboard controls followed in the &amp;ldquo;Further Information&amp;rdquo; section with a full list of commands.&lt;/p&gt;
&lt;h2 id=&#34;the-tour&#34;&gt;The tour&lt;/h2&gt;
&lt;h3 id=&#34;parts-of-the-screen&#34;&gt;Parts of the screen&lt;/h3&gt;
&lt;p&gt;The top two lines show a summary of the system&amp;rsquo;s status. An asterisk by an item means that the system is overloaded.&lt;/p&gt;
&lt;p&gt;The next line, the third, will normally be blank but will show any system alerts. On the screenshot you can see &lt;code&gt;PCH&lt;/code&gt; as the punch device is currently offline and I need to do a Hercules &lt;code&gt;devinit&lt;/code&gt; to bring it back online. This line can also show HASP status alerts, such as when HASP has been started but batch job execution has not been resumed yet.&lt;/p&gt;
&lt;p&gt;Taking up the bulk of the screen is the message area, showing results of commands you type and any job messages. If jobs require any input they will be bolded: on the screenshot you can see a tape mount request that needs a response.&lt;/p&gt;
&lt;p&gt;The last but one line is the request area where you can type commands, and at the very bottom is the system name, date and time.&lt;/p&gt;
&lt;h3 id=&#34;keyboard&#34;&gt;Keyboard&lt;/h3&gt;
&lt;p&gt;Note that the cursor position is important: in the screenshot above I can put the cursor in the line after &lt;code&gt;******&lt;/code&gt; and response to the tape request, or put the cursor in the request area at the bottom to enter commands. When there are multiple input areas active, pressing &lt;code&gt;Tab&lt;/code&gt; will move between them.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t press &lt;code&gt;SysRq&lt;/code&gt;, as this will ask for a dump tape and will exit the operator&amp;rsquo;s console program.&lt;/p&gt;
&lt;h3 id=&#34;types-of-commands&#34;&gt;Types of commands&lt;/h3&gt;
&lt;p&gt;There are four types of commands that can be entered in the request area:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Job commands, which do not have a special prefix&lt;/li&gt;
&lt;li&gt;HASP batch spooler commands, which start with &lt;code&gt;$&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;System status commands, which start with &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Device commands, which start with &lt;code&gt;%&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Job commands&lt;/strong&gt; include &lt;code&gt;HASP&lt;/code&gt; and &lt;code&gt;SHUTDOWN&lt;/code&gt;, used in the start up and shut down process.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;GOOSE&lt;/code&gt;, &lt;code&gt;STOP&lt;/code&gt; and &lt;code&gt;BLAST&lt;/code&gt; commands, in order of severity, can be useful when dealing with user terminal jobs that appear stuck. Be careful with &lt;code&gt;BLAST&lt;/code&gt; as it can lead to file system damage.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;MTS&lt;/code&gt; job has three meanings useful on Hercules&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To run MTS operator command files, eg &lt;code&gt;MTS *LAS&lt;/code&gt;; a list is given below.&lt;/li&gt;
&lt;li&gt;To start an interactive MTS session on the console for any ID, without having to give a password. Type &lt;code&gt;MTS OPER&lt;/code&gt; then &lt;code&gt;SIGNON XXX&lt;/code&gt; where &lt;code&gt;XXX&lt;/code&gt; is the account you want. Can be useful for a quick command but better to use a separate terminal normally.&lt;/li&gt;
&lt;li&gt;To restart MTS on a terminal if it has been STOPed or hung up; use &lt;code&gt;MTS DSxx&lt;/code&gt; for this where xx is the sequence number of the 3270 connection; this is displayed at the top of each terminal connection.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;HASP&lt;/strong&gt; commands include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$release ex&lt;/code&gt; and &lt;code&gt;$hold ex&lt;/code&gt; to start and stop job execution&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$drain system&lt;/code&gt; to bring the scheduler to a halt before shutdown&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$drain DEV&lt;/code&gt; to remove a device, like the punch, from HASP; &lt;code&gt;$start DEV&lt;/code&gt; to add in back again&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$display&lt;/code&gt; to show information about jobs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;System status&lt;/strong&gt; commands are similar to the user MTS command &lt;code&gt;$systemstatus&lt;/code&gt; but replace that word with a &lt;code&gt;/&lt;/code&gt;. So in userland you would do &lt;code&gt;$systemstatus tasks&lt;/code&gt; to view a list of tasks but on the console you would do &lt;code&gt;/tasks&lt;/code&gt; (or just &lt;code&gt;/t&lt;/code&gt;). There are a number of additional subcommands only available on the console.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Device commands&lt;/strong&gt; include &lt;code&gt;%wrap&lt;/code&gt; to control line wrapping and &lt;code&gt;%PF&lt;/code&gt; to set PF hotkey definitions.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;h3 id=&#34;system-status-columns&#34;&gt;System status columns&lt;/h3&gt;
&lt;p&gt;Key to the abbreviations in the system status area (taken directly from the D6.0A FAQ):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;	DT   = Delta Time [time in seconds covered by the sample displayed]
	EXQ  = Execution Queue [number of batch jobs waiting to execute]
	PRT  = Print Queue [number of jobs waiting to print]
	PCH  = Punch queue [number of jobs waiting to punch]
	BP   = Batch Preferred [the number of batch jobs the system would 
	                        &amp;#34;like&amp;#34; to be running]
	AB   = Actual Batch [the number of batch jobs actually running]
	BHP  = Batch per Hour [average number of batch jobs completed per hour]
	AL   = Active lines [MTS terminal sessions]
	VP   = Virtual Pages
	RP   = Real Pages
	DPA  = Drum Pages Available [pages available on the high speed 
	                             paging devices, rather than disk]
	PA   = Paging Activity [paging I/O operations per second]
	DA   = Disk Activity [disk I/O operations per second]
	CA   = Channel Activity [channel I/O operations per second, 
	                         includes DA and a portion of PA as well as 
	                         other I/O operations]
	%PI  = Percent Processor Idle
	Q    = Global CPU Queue [jobs waiting for the CPU]
	TQ   = Tape Queue [MTS jobs waiting for one or more tapes 
	                   drives to become available]
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;job-commands&#34;&gt;Job commands&lt;/h3&gt;
&lt;p&gt;A summary of the job commands available is given below. Full documentation can be found starting from page 81 in the Operator&amp;rsquo;s Manual.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BLAST&lt;/code&gt; - forcibly kill a job if STOP will not kill it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BROADCST&lt;/code&gt; - send a message to terminals attached to the system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DIS&lt;/code&gt; - display contents of memory locations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DMGRSTAT&lt;/code&gt; - manage disks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FLUSH&lt;/code&gt; - clear requests from HASP for I/O for a device (after draining)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GOOSE&lt;/code&gt; - simulates sending an ATTN to a task&lt;/li&gt;
&lt;li&gt;&lt;code&gt;G11&lt;/code&gt; - g11 3211 printer config&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HASP&lt;/code&gt; - start the spooler&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HASPLING&lt;/code&gt; - hasp subjobs, not started by hand&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INIT&lt;/code&gt; - initialize system, not started by hand&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IT&lt;/code&gt; - list networks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JOBDUMP&lt;/code&gt; - dump a job core memory to tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JOBS&lt;/code&gt; - list job status&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MOD&lt;/code&gt; - change a location in memory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MTS&lt;/code&gt; - start an MTS task&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OFFLINE&lt;/code&gt; - mark a device as logically offline&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OK&lt;/code&gt; - determine how jobs that terminate successfully display to the console&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ONLINE&lt;/code&gt; - mark a device as logically online&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OPERATOR&lt;/code&gt; - start the operator console&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PDP&lt;/code&gt; - initiate the Paging Drum Processor&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PDROP&lt;/code&gt; - remove paging devices&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PGET&lt;/code&gt; - set up extra paging devices&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PN&lt;/code&gt; - printer train config&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QN&lt;/code&gt; - printer train config&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SE&lt;/code&gt; - print device table&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SHUTDOWN&lt;/code&gt; - shutdown the (terminal) part of the system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SIGNONM&lt;/code&gt; - print a sign-on message to all users before they sign on&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SOFTCHK&lt;/code&gt; - control the recording of soft (recoverable) machine checks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STARTUP&lt;/code&gt; - reverse the effects of shutdown on a terminal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STAT&lt;/code&gt; - collect system statistics to a tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STATSW&lt;/code&gt; - dynamically change the parameters that a STAT job is using&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STOP&lt;/code&gt; - kill a job&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TABLMOD&lt;/code&gt; - display or modify information in the shared file table&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TASKS&lt;/code&gt; - display status of running tasks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TMTS&lt;/code&gt; - run a task using the test version of MT&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TN&lt;/code&gt; - printer train config&lt;/li&gt;
&lt;li&gt;&lt;code&gt;T11&lt;/code&gt; - printer train config&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNITS&lt;/code&gt; - display config of channels, control units etc&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;mts-operators-files&#34;&gt;MTS operator&amp;rsquo;s files&lt;/h3&gt;
&lt;p&gt;All commands below need to be run by MTS and prefixed with &lt;code&gt;*&lt;/code&gt;, eg &lt;code&gt;MTS *BBS&lt;/code&gt;. Many (most?) of these will not be of use on Hercules, eg those involving networking or plotting hardware. Full documentation starts from page 161 in the Operator&amp;rsquo;s manual.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AAS&lt;/code&gt; - Starts network tasks AA21 - AA29&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ABS&lt;/code&gt; - Starts network tasks AB10 - AB6F&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ADS&lt;/code&gt; - Starts network tasks AD10 - AD73&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AES&lt;/code&gt; - Starts network tasks AE10 - AE73&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AFS&lt;/code&gt; - Starts network tasks AF10 - AF6F&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ANS&lt;/code&gt; - Starts network tasks AN10 - AN6F AND DSN&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BBS&lt;/code&gt; - Babysit program to watch for user ID signon&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BDL&lt;/code&gt; - Dump, reload PCP &amp;ldquo;AB&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCD&lt;/code&gt; - To Defer Plots&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCL&lt;/code&gt; - Initializes Plot Tapes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCP&lt;/code&gt; - Dump Plots To Tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCR&lt;/code&gt; - Reset/Startup PDP-11 &amp;ldquo;C&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCS&lt;/code&gt; - Normal Startup PDP-11 &amp;ldquo;C&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCT&lt;/code&gt; - Length Of Plot Session&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CDL&lt;/code&gt; - Dump, Reload Data Conc. &amp;ldquo;C&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CHK&lt;/code&gt; - Checks For Offline Equipment&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLB&lt;/code&gt; - Retrieves Label From Tape And Inserts It In Tape Catalog&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLK&lt;/code&gt; - Starts Automatic Job Scheduling Program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLN&lt;/code&gt; - Cleans Up machine check and unit check Info.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLO&lt;/code&gt; - Reloads DC &amp;ldquo;C&amp;rdquo;-No Dump&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLR&lt;/code&gt; - Restarts Job Scheduling Program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CMB&lt;/code&gt; - Combine Statistics Tapes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CMD&lt;/code&gt; - Command Statistics&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CPW&lt;/code&gt; - Change Password To Unknown&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CTD&lt;/code&gt; - Check Disks After PM&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DCS&lt;/code&gt; - Starts Data Concentrator C&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DDL&lt;/code&gt; - Dump And Load Primary Communications Processor AD&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DLG&lt;/code&gt; - Logs Disk Errors&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DLO&lt;/code&gt; - Reloads PCP &amp;ldquo;AD&amp;rdquo;-No Dump&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DMP&lt;/code&gt; - Initiates BATCH Job To Print Dumps&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DRN&lt;/code&gt; - Drains HASP Remotes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DSD&lt;/code&gt; - Runs DASDI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DSK&lt;/code&gt; - Add,List,Delete Disks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DSN&lt;/code&gt; - Start User HASP Display - NUBS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DSO&lt;/code&gt; - Amount Free File Space&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DSP&lt;/code&gt; - User HASP Status Display - CNTR&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DWB&lt;/code&gt; - CMDSTAT + Unit Checks To Tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EDL&lt;/code&gt; - Dumps &amp;amp; Reloads PCP AE&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ELO&lt;/code&gt; - Loads Primary Communications Processor AE&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EUP&lt;/code&gt; - Enables terminals on EE01 SCP in the East Engineering building&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FIR&lt;/code&gt; - Names &amp;amp; Ph.  #s Of Fire Fighters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FIX&lt;/code&gt; - Update CCID Accounting Record&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FLB&lt;/code&gt; - Check Tape Labels&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FSC&lt;/code&gt; - Make Copies Of File Save Tapes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FSM&lt;/code&gt; - File Save Merge&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HGS&lt;/code&gt; - Show 10 Jobs With Most CPU, DSK I/O etc&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HLB&lt;/code&gt; - Force Access To UB Host Through Host Locator&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HLG&lt;/code&gt; - Hasp Log Collection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HLM&lt;/code&gt; - Force Access To UM Host Through Host Locator&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HPS&lt;/code&gt; - HASP Status&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HSP&lt;/code&gt; - Normal HASP Start&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HUH&lt;/code&gt; - Merit + Concentrators, State Of&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*IG.SHUFFLE&lt;/code&gt; - Summarizes &lt;code&gt;*IG&lt;/code&gt; Usage&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INI&lt;/code&gt; - .System Initialization&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ITX&lt;/code&gt; - Intertask Communication Test-UMMPS Jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LAR&lt;/code&gt; - Reset Line Adapters-1270&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LAS&lt;/code&gt; - Start 1270 And Misc Jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LBH&lt;/code&gt; - Tape Labeling (non-catalog)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LBL&lt;/code&gt; - Tape Labeling (catalog)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LIM&lt;/code&gt; - Time Remaining-Batch Jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LMT&lt;/code&gt; - Short Version Of MSS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LRL&lt;/code&gt; - Info.  About Last IPL&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MNS&lt;/code&gt; - Normal MERIT Startup&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MSD&lt;/code&gt; - Set Time-Date Merit CCs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MSG&lt;/code&gt; - Cleans Up $Message System&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MSS&lt;/code&gt; - Tape Usage History&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MUP&lt;/code&gt; - Current State Off MERIT Computer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NDL&lt;/code&gt; - Dumps &amp;amp; Reloads Primary Communications Processor AN&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NST&lt;/code&gt; - NAS-Manipulation Of Name Address Spaces&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NUP&lt;/code&gt; - Enables Terminals On NUBS SCPs &amp;amp; Job Status Monitor&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OOT&lt;/code&gt; - Controls NTM On UB &amp;amp; UM&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PAG&lt;/code&gt; - Tests Paging Devices&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PLB&lt;/code&gt; - Prints Identifying Info.  About Plot Tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P16&lt;/code&gt; - Starts 16 PAG Jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RCP&lt;/code&gt; - MTS S-8 Cards&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RES&lt;/code&gt; - File Save: Status &amp;amp; Restoration&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RHB&lt;/code&gt; - Releases &amp;amp; Holds HASP Rate Classes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RIP&lt;/code&gt; - PN Ripple Print&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RST&lt;/code&gt; - Restores Files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RSV&lt;/code&gt; - Reserves Disk Drives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SAV&lt;/code&gt; - Daily File Save&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SDB&lt;/code&gt; - PCP Shutdown Msg.  &amp;amp; Disconnect&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SDM&lt;/code&gt; - D.C.  Shutdown Msg.  &amp;amp; Disconnect&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SFT&lt;/code&gt; - Starts SOFTCHK-Checks For Soft MCHKS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STA&lt;/code&gt; - Saves Statistics On Tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STI&lt;/code&gt; - Checks Condition Of Statistics Work File&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SVS&lt;/code&gt; - Special Large, Weekly Type Filesave&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SVW&lt;/code&gt; - Weekly File Save&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TNR&lt;/code&gt; - TN Ripple Print&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TPD&lt;/code&gt; - Tape &amp;amp; Tape Unit Diagnostics&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TPS&lt;/code&gt; - Displays Amount And Type Of Free Tape Units&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TSH&lt;/code&gt; - Add Tapes To Or Remove From UNSP:TAPESHARE&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TSN&lt;/code&gt; - Weekly Full File Save Slave Stream&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNU&lt;/code&gt; - Enables Terminals On NUBS &amp;amp; UNYN SCPs &amp;amp; Job Stautus Monitor&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;hasp-commands&#34;&gt;HASP commands&lt;/h3&gt;
&lt;p&gt;Once the spooler has been started by running the &lt;code&gt;HASP&lt;/code&gt; job, there are a number of commands available to control spool execution. Full details in the HASP manual.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$ACCEPT [RMID] modifier&lt;/code&gt; - define what output each site can accepr&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ALTER JOB n PRIO +or-M | DEFER | LOW&lt;/code&gt; - alter job priority&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$BACKLOG [log]&lt;/code&gt; - display job backlog&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$BACKSPACE dev n&lt;/code&gt; - fix paper jam by restart print from 2 pages back&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DELETE dev n&lt;/code&gt; - terminate the current job running on device &lt;code&gt;dev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DELETE JOB N&lt;/code&gt; - terminate job number &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DISPLAY JOB n&lt;/code&gt; - display info about job &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DISPLAY PRIO JOB n&lt;/code&gt; - display priority of job &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DISPLAY RMTS|REMOTE NAME|REMOTE NUMBER [LOG]&lt;/code&gt; - display status of remote dvices&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DISPLAY {UNIT Dev N | UNITS [LOG]}&lt;/code&gt; - display I/O unit status&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DRAIN dev n&lt;/code&gt; - Allow current job on device &lt;code&gt;N&lt;/code&gt; to finish then remove device from spooler.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$DRAIN SYSTEM&lt;/code&gt; - Allow all current jobs to finish but do not execute any new jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$FORMS Paper,devn&lt;/code&gt; - Define papet type on device&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$HOLD *...*&lt;/code&gt; - Hold access to the pseudodevices &lt;code&gt;*PRINT*, *PUNCH*, *BATCH*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$HOLD EX&lt;/code&gt; - Hold execution of batch jobs (but allow printing/punching)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$HOLD JOB n or ALL or LOW or DEFER&lt;/code&gt; - Put a hold on batch jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$INTERRUPT dev n&lt;/code&gt; - Interrupt the printing of the job on device &lt;code&gt;N&lt;/code&gt; and put it back on the queue,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$LIST&lt;/code&gt; - Define level of log messages to be printed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$LOCATE JOB n&lt;/code&gt; - Find out execution info for job &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$LOCATE User CCID&lt;/code&gt; - Find out execution info for jobs from user &lt;code&gt;CCID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$LOGIO ON/OFF DEVn&lt;/code&gt; - Log all message for a device&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$LOG MESSAGE&lt;/code&gt; - Send a message to the log file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$NEWHASPLING dev n&lt;/code&gt; - Start a new HASP internal job&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$PASSWORD&lt;/code&gt; - Set password for remote station&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$QUE [MAIN]&lt;/code&gt; - Show queue information&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$REFUSE [RMID] modifier&lt;/code&gt;  - define what output each site can accepr&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$RELEASE *...*&lt;/code&gt; - Release access to the pseudodevices &lt;code&gt;*PRINT*, *PUNCH*, *BATCH*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$RELEASE EX&lt;/code&gt; - Release execuion of batch jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$RELEASE REMOTE JOB n or ALL or LOW or DEFER&lt;/code&gt; - Release jobs put on hold&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$REPEAT dev n&lt;/code&gt; - repeat printed output for job printing to device &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$RERUN JOB n&lt;/code&gt; - Terminate job &lt;code&gt;N&lt;/code&gt; and place back on the queue&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$RESTART dev n&lt;/code&gt; - Terminate job running on device &lt;code&gt;N&lt;/code&gt; and immediately rerun&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$RMSG (nn Or Rmid Or All) Message&lt;/code&gt; - Communicate with remote stations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ROUTE TYPE for-id to-id&lt;/code&gt; - Define routing for output of job&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$SETUC [RMID] On|off&lt;/code&gt; - Set upper-case option for remote stations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$SIGNOFF&lt;/code&gt; - Stop a remote terminal after completion of current read/punch&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$SPACE dev n&lt;/code&gt; - Stop carriage control and single space output for job running on device &lt;code&gt;N&lt;/code&gt; (useful for runaway print jobs)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$START modifier dev n&lt;/code&gt; - Add a device back under HASP control&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$START SYSTEM&lt;/code&gt; - Start HASP operation (done as part of &lt;code&gt;MTS *HSP&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$STATUS [log] N&lt;/code&gt; - Show status of device &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$STOP dev n&lt;/code&gt; - Stop execuion of current job on device &lt;code&gt;N&lt;/code&gt; and remove device from HASP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$TEST&lt;/code&gt; - Check console is working&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;system-status-commands&#34;&gt;System status commands&lt;/h3&gt;
&lt;p&gt;Full documentation can be found starting from page 456 in the Operator&amp;rsquo;s Manual.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/ALTER&lt;/code&gt; - Change system scheduling parameters or virtual memory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/AREAL&lt;/code&gt; - Change real memory contents&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/ATTN&lt;/code&gt; - Send attention interrupt to a task&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/BLAST&lt;/code&gt; - Force kill a job&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/DEVICE&lt;/code&gt; - Display info on a device&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/DISPLAY&lt;/code&gt; LOCK n - Display locks, eg stat job lock&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/DISPLAY&lt;/code&gt; - Display predefined variables&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/DREAL&lt;/code&gt; - Display real memory contents&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/FIO&lt;/code&gt; - Force a fake I/O interrupt&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/JOB&lt;/code&gt; - List details of jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/KILL&lt;/code&gt; - Kill a job&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/LOAD&lt;/code&gt; - Print load info, like the top 2 lines of the operator console&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/OPER&lt;/code&gt; - Print output of operator&amp;rsquo;s console or send command to console&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/QUEUE&lt;/code&gt; - Display queue status&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/SCREEN&lt;/code&gt; - Display job status in TV screen like format&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/TAPEQUEUE &lt;/code&gt; - Control and list the tape queue&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/TASKS&lt;/code&gt; - Print task information&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/USERS&lt;/code&gt; - Prints user information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are also some synonyms, eg &lt;code&gt;/MODIFY&lt;/code&gt; for &lt;code&gt;/ALTER&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;device-commands&#34;&gt;Device commands&lt;/h3&gt;
&lt;p&gt;Like on MTS terminals, device commands start with &lt;code&gt;%&lt;/code&gt;, but the set of available commands is different. Full details are on p331 of the Operator&amp;rsquo;s Manual.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%3270 devn&lt;/code&gt; - switch console device to be &lt;code&gt;devn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%ACCEPT ALL|job&lt;/code&gt; - control if messages are displayed by jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%EMPTY&lt;/code&gt; - Empties the screen.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%FAST&lt;/code&gt; - Switches terminal into fast mode; not of use on Hercules.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%IGNORE jobn&lt;/code&gt; - control if messages are displayed by jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%PF? &lt;/code&gt; - Lists PF key definitions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%PFn=def&lt;/code&gt; - Redefines program  function  key  PFn to be &lt;code&gt;def&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%PTR devn&lt;/code&gt; - Switches the console printer device&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%REFRESH&lt;/code&gt; - Makes sure that the screen is up to date.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%RESTART&lt;/code&gt; - Switch back to main terminal from backup.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%SLOW &lt;/code&gt; -  Switches terminal into slow mode; not of use on Hercules.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%WRAP ON|OFF&lt;/code&gt; - Control line truncation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYYmVmNTNiMDUtN2ZlNS00ZDI2LTgzZTktNjBmODU4MTE3OGZm&#34;&gt;Operator&amp;rsquo;s Manual&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYNWRmNjE3OTQtYjk4Yy00ZTFlLWJjYTEtMjU5MDUxMmU0YjQ0&#34;&gt;HASP Manual&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>MTS System Admin</title>
      <link>https://try-mts.com/operator/</link>
      <pubDate>Sun, 05 Oct 2014 04:00:04 +0000</pubDate>
      <guid>https://try-mts.com/operator/</guid>
      <description>&lt;p&gt;In this series of posts we will look at the operator console, system admin commands and Hercules features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/the-operators-console/&#34;&gt;The operator&amp;rsquo;s console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/users-and-projects/&#34;&gt;Users and projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/cmdstat-or-why-does-dwb-keep-asking-me-for-tapes/&#34;&gt;CMDSTAT, or why does DWB keep asking me for tapes?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/setting-the-timezone-by-patching-the-system-with-ramrod-and-objutil/&#34;&gt;Setting the timezone with &lt;code&gt;RAMROD&lt;/code&gt; and &lt;code&gt;*OBJUTIL&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/some-useful-hercules-features/&#34;&gt;Some useful Hercules features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/system-start-up-in-more-detail/&#34;&gt;System startup in more detail&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>A quicker way to mount tapes</title>
      <link>https://try-mts.com/a-quicker-way-to-mount-tapes/</link>
      <pubDate>Sun, 28 Sep 2014 04:03:18 +0000</pubDate>
      <guid>https://try-mts.com/a-quicker-way-to-mount-tapes/</guid>
      <description>&lt;p&gt;To wrap up this series of posts on tapes I will show a quick way to mount them that does not require switching to the operator&amp;rsquo;s console and responding to the mount request.&lt;/p&gt;
&lt;h2 id=&#34;example&#34;&gt;Example&lt;/h2&gt;
&lt;p&gt;Say you wanted to mount the D6.0T1 distribution tape which is on T904. Normally you would issue the &lt;code&gt;$mount&lt;/code&gt; command from a user terminal:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$mount mts:tape1 9tp *t* vol=6.0T1 lbltype=vlo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and then switch over to the operator&amp;rsquo;s console to say&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OK TAPE1 T904
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Instead you can do this from your MTS session:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$get &amp;gt;t904
$control *afd* volume=6.0T1
$control *afd* lbltype=vlo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can then run any program that uses the tape by pointing it to pseudodevice &lt;code&gt;*afd*&lt;/code&gt;, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$run *fs 0=*afd*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When finished you need to &lt;code&gt;$release *afd*&lt;/code&gt; as usual. Once you release it, the tape will not go offline like for normal mounts so you don&amp;rsquo;t need to &lt;code&gt;devinit&lt;/code&gt; it on the Hercules console before using it again.&lt;/p&gt;
&lt;p&gt;So although this is much quicker to use than regular &lt;code&gt;$mount&lt;/code&gt;, there are a number of drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have to be a privileged user to &lt;code&gt;$get&lt;/code&gt; a tape device - so with the starter set of users you can sign on as &lt;code&gt;MTS&lt;/code&gt; and do this but account &lt;code&gt;ST01&lt;/code&gt; would not work&lt;/li&gt;
&lt;li&gt;You can only have one FDname open at a time whereas you can &lt;code&gt;$mount&lt;/code&gt; multiple tapes and give them different pseudodevice names. If you try to &lt;code&gt;$get&lt;/code&gt; a second tape it will release the first one and reassign &lt;code&gt;*afd*&lt;/code&gt; to the new tape.&lt;/li&gt;
&lt;li&gt;You need to remember to issue the &lt;code&gt;$control&lt;/code&gt; commands to identify the tape, otherwise you will get errors trying to run programs against &lt;code&gt;*afd*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-get-command&#34;&gt;The &lt;code&gt;$get&lt;/code&gt; command&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$get&lt;/code&gt; is a MTS command but is not mentioned in MTS Volume 1 - you will need to get the &lt;a href=&#34;http://hdl.handle.net/2027.42/79585&#34;&gt;System Edition&lt;/a&gt; which lists privileged and obsolete commands.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;*afd*&lt;/code&gt; stands for Active File or Device and is described in the last chapter of the manual. It provides a lower level way of accessing FDnames than regular MTS commands like &lt;code&gt;$edit&lt;/code&gt; or &lt;code&gt;$copy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;*afd*&lt;/code&gt; is activated, lines typed at the terminal which do not start with a &lt;code&gt;$&lt;/code&gt; are interpreted as data lines which are written to &lt;code&gt;*afd*&lt;/code&gt;. Assuming the defaults of line files and auto numbering off, if you type a line at the command prompt starting with a number it will be added to &lt;code&gt;*afd*&lt;/code&gt; using that line number rather than being interpreted as a MTS command.&lt;/p&gt;
&lt;p&gt;An example using a regular file will make this clearer:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$get -gettest
# Ready.
10welcome
20MTS
15to
release
# &amp;#34;*AFD*&amp;#34;: ... AFD released.
$list -gettest
 
      10     welcome
      15     to
      20     MTS
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&#34;http://hdl.handle.net/2027.42/79585&#34;&gt;MTS Volume 1: System Edition&lt;/a&gt; for a write-up of &lt;code&gt;$get&lt;/code&gt; and &lt;code&gt;*afd*&lt;/code&gt;. The &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq&#34;&gt;D6.0A FAQ&lt;/a&gt; also has some examples of using &lt;code&gt;$get&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Making your own tapes</title>
      <link>https://try-mts.com/making-your-own-tapes/</link>
      <pubDate>Sun, 21 Sep 2014 16:43:46 +0000</pubDate>
      <guid>https://try-mts.com/making-your-own-tapes/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Sep/9track.png&#34; alt=&#34;9track&#34;&gt;
&lt;sup&gt;A 9 track magnetic tape, from the &lt;a href=&#34;http://www.obsoletemedia.org/tag/ibm/&#34;&gt;Museum of Obsolete Media&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In this post we&amp;rsquo;ll look at how to create your own emulated tapes to transfer data between MTS and your host operating system. For an introduction to tapes on MTS take a look back at this &lt;a href=&#34;https://try-mts.com/restoring-files-from-tapes/&#34;&gt;earlier article&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-lbltp-program&#34;&gt;The &lt;code&gt;lbltp&lt;/code&gt; program&lt;/h2&gt;
&lt;p&gt;You will need to install the &lt;code&gt;lbltp&lt;/code&gt; program on your host OS. This allows reading from and writing to emulated tape files compatible with Hercules.&lt;/p&gt;
&lt;p&gt;The version &lt;a href=&#34;http://bitsavers.informatik.uni-stuttgart.de/bits/univOfMichigan/mts/lbltp.tar.gz&#34;&gt;at Bitsavers&lt;/a&gt; contains source and executable for Mac and Windows. On Linux, get the latest version from Mike Alexander&amp;rsquo;s SVN repo and build locally:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ svn checkout https://svn.msalexander.com/repo/utilities/lbltp/trunk lbltp
$ cd lbltp
$ mkdir build
$ cd build
$ make -f ../Make/makefile
$ sudo cp lbltp /usr/local/bin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Documentation can be found in the &lt;code&gt;Writeup&lt;/code&gt; directory. Type &lt;code&gt;lbltp&lt;/code&gt; to start the program, give it commands at the &lt;code&gt;Please enter request&lt;/code&gt; prompt and type &lt;code&gt;stop&lt;/code&gt; to exit.&lt;/p&gt;
&lt;h2 id=&#34;set-up-a-spare-tape-drive&#34;&gt;Set up a spare tape drive&lt;/h2&gt;
&lt;p&gt;The starter version of D6.0 comes with a number of tape drives defined and loaded with the distribution tapes. You could use these to mount your own tapes but it is easier to set up a new tape drive.&lt;/p&gt;
&lt;p&gt;Make sure MTS and Hercules are shut down then edit the &lt;code&gt;Hercules.cfg&lt;/code&gt; file. In the tapes section add a line like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0C70 3420 * # User tape drive
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This defines a 3420 model tape drive at address 0C70 which corresponds to MTS device T920. The &lt;code&gt;*&lt;/code&gt; means no tape is loaded when Hercules starts up.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-new-tape-from-mts&#34;&gt;Creating a new tape from MTS&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s create a new tape, mount it for writing and use &lt;code&gt;*FS&lt;/code&gt; to store a couple of files which we can then read from the host OS.&lt;/p&gt;
&lt;p&gt;First you will need to create a blank tape file. You can use the &lt;code&gt;hetinit&lt;/code&gt; program that comes with Hercules to do this. Change directory to &lt;code&gt;Tapes&lt;/code&gt; and issue the following command from your host OS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;hetinit -d new.aws NEW
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will create a file &lt;code&gt;new.aws&lt;/code&gt; with tape volume name &lt;code&gt;NEW&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, issue a mount request for this new tape from MTS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$mount mts:tape1 9tp *t* vol=NEW ring=in
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the &lt;code&gt;ring=in&lt;/code&gt; parameter as we are going to write to the tape. We are using regular labeled tapes so we don&amp;rsquo;t need the &lt;code&gt;lbltype=vlo&lt;/code&gt; parameter used for distribution tapes.&lt;/p&gt;
&lt;p&gt;Switch to the Hercules console and issue a &lt;code&gt;devinit&lt;/code&gt; command to initialise the tape drive with the file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;devinit 0c70 Tapes/new.aws
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go now to the MTS operator&amp;rsquo;s console and respond to the mount request:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OK TAPE1 T920
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If all goes well your MTS session will respond that the tape was mounted. You can now run &lt;code&gt;*FS&lt;/code&gt; to add files to the tape. Note the &lt;code&gt;par=init&lt;/code&gt; parameter as this is a new tape:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$run *fs 0=*t* par=init
# Execution begins   11:24:36 
= save alpha
= File 1 &amp;#34;ALPHA(1)&amp;#34; ... has been saved
= save gamma
= File 2 &amp;#34;GAMMA(1)&amp;#34; ... has been saved
= stop
= Do you want *FS to dismount the tapes it used?
? yes
= &amp;#34;*T*&amp;#34;: (MTS.:TAPE1) T920 released.
# Execution terminated   11:24:46  T=0.058 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;reading-the-tape-from-your-host-os&#34;&gt;Reading the tape from your host OS&lt;/h2&gt;
&lt;p&gt;You can now use &lt;code&gt;lbltp&lt;/code&gt; to open the tape file and extract files to your host OS. Below is a sample session showing how to do this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ITD Tape Utility version 1.3

Please Enter Request
open new.aws
Open input or output tape?
input

Please Enter Request
list
         Listing for IBM labeled *FS tape NEW 
FS file     1  ALPHA                            blks=1 recs=2 bytes=78
FS file     2  GAMMA                            blks=1 recs=13 bytes=366
Logical End of Tape reached.

Please Enter Request
copy file=1-2
Copying FS file 1 ALPHA to alpha.
   1 blocks 2 records 78 bytes             2 records 80 bytes
Copying FS file 2 GAMMA to gamma.
   1 blocks 13 records 366 bytes             13 records 379 bytes

Please Enter Request
stop
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that &lt;code&gt;lbltp&lt;/code&gt; takes care of EBCDIC to ASCII conversion, but line numbers from MTS are not preserved.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-tape-from-your-host-os&#34;&gt;Creating a tape from your host OS&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s try to do it the other way, creating a tape with some files from your host OS and transferring these into MTS.&lt;/p&gt;
&lt;p&gt;One problem is that &lt;code&gt;lbltp&lt;/code&gt; only supports reading &lt;code&gt;*FS&lt;/code&gt; formatted tapes, not writing to them. So we will have to write a regular labeled tape and use lower level tape positioning commands on MTS to extract files.&lt;/p&gt;
&lt;p&gt;(I think it should be technically possible to extend &lt;code&gt;lbltp&lt;/code&gt; to support writing &lt;code&gt;*FS&lt;/code&gt; tapes; could be an interesting project.)&lt;/p&gt;
&lt;p&gt;The below &lt;code&gt;lbltp&lt;/code&gt; session shows how to create a new tape and add two files. Note that you have to give an explicit &lt;code&gt;filename&lt;/code&gt; command before adding each file to the tape.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ITD Tape Utility version 1.3

Please Enter Request
initialize send.aws volume=SEND

Please Enter Request
filename delta

Please Enter Request
copy input=delta output
Copying file delta to send.aws file 1 DELTA.
             3 records 37 bytes   1 blocks 3 records 37 bytes 

Please Enter Request
filename epsilon

Please Enter Request
copy input=epsilon output
Copying file epsilon to send.aws file 2 EPSILON.
             4 records 29 bytes   1 blocks 4 records 29 bytes 

Please Enter Request
stop
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;reading-the-tape-from-mts&#34;&gt;Reading the tape from MTS&lt;/h2&gt;
&lt;p&gt;Now we need to mount the tape on MTS. On the Hercules console (note the &lt;code&gt;ro&lt;/code&gt; parameter):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;devinit 0c70 Tapes/send.aws ro                                                  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From MTS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$mount mts:tape1 9tp *t* vol=SEND
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On the MTS operator&amp;rsquo;s console:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OK TAPE1 T920
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The tape is now mounted on pseudodevice &lt;code&gt;*T*&lt;/code&gt;. To get a listing of files, there&amp;rsquo;s a useful MTS program called &lt;code&gt;*LABELSNIFF&lt;/code&gt; we can run, giving the tape pseudodevice as parameter 0:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# run *labelsniff 0=*t*
# Execution begins   10:50:15 


  Tape = *t* User ID REL. 10:50:15 19 Sept 1914

  Tape name=MTS.:TAPE1
  IBM labeled  6250-bpi 9TP Volume=SEND 
  LP=on BLK=on RING=out DTCHK=on RETRY=10

  File                    Block Record  Tapelen  Record 
     # Data set name      count  count   (feet)  format 

     1 DELTA                  1       3    1.10 VBS(32760,32767) 
     2 EPSILON                1       4    1.08 VBS(32760,32767) 
 
  Total tape length = 2.18 feet.
 
  &amp;lt;*&amp;gt;&amp;lt;*&amp;gt;&amp;lt;*&amp;gt;  End of tape  &amp;lt;*&amp;gt;&amp;lt;*&amp;gt;&amp;lt;*&amp;gt; 
# Execution terminated   10:50:23  T=0.029 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(For those of us who have never used real tapes it&amp;rsquo;s interesting to visualise files having a length in feet!)&lt;/p&gt;
&lt;p&gt;Now to extract the files. For each file we will need to use a &lt;code&gt;$control&lt;/code&gt; command to position the tape and then use the MTS &lt;code&gt;$copy&lt;/code&gt; command to read from the tape and write to a file. We will also need to create the files we want to write to. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$control *t* posn=epsilon
$create epsilon
$copy *t* epsilon
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You don&amp;rsquo;t have to copy to a file; if you just wanted to display the file you could do &lt;code&gt;$list *t*&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When finished, you will need to release the tape:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$release *t*
# &amp;#34;*T*&amp;#34;: (MTS.:TAPE1) T920 released.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol19-MagneticTapes-Oct1993.pdf&#34;&gt;MTS Volume 19&lt;/a&gt; has extensive information on using tapes. I also found the tutorial guide made by the UM Computer Centre &lt;a href=&#34;http://hdl.handle.net/2027/mdp.39015011174631?urlappend=%3Bseq=1&#34;&gt;Introduction to Magnetic Tapes - Getting It on Tape&lt;/a&gt; useful.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a program on MTS called &lt;code&gt;*UNIXTAR&lt;/code&gt; that looks like it can read and write tar format files, so one alternative that I have not tested is to create a tar file on the host OS, add this as the single file on a tape and use &lt;code&gt;*UNIXTAR&lt;/code&gt; to extract all the files in one step.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Finding MTS components from the driver file</title>
      <link>https://try-mts.com/finding-mts-components-from-the-driver-file/</link>
      <pubDate>Sun, 14 Sep 2014 05:04:06 +0000</pubDate>
      <guid>https://try-mts.com/finding-mts-components-from-the-driver-file/</guid>
      <description>&lt;p&gt;The driver listing file contains details of all MTS components that are available on the distribution tapes. It can be found in the file &lt;code&gt;doc/D6.0-LIST.txt&lt;/code&gt; on your host operating system after unzipping the distribution.&lt;/p&gt;
&lt;p&gt;Each component has one or more sub-components which represent individual files. For example, 528 is the component for the Algol-W compiler and it has 66 sub-components; 548 is the component for the &lt;code&gt;*FS&lt;/code&gt; program and it has 9 sub-components. Individual items are identified as component/sub-component, eg 548/9.&lt;/p&gt;
&lt;p&gt;The driver listing has 2 or 3 lines per sub-component giving details like file type, location and size followed by a more detailed description.&lt;/p&gt;
&lt;h2 id=&#34;unpacking-the-file&#34;&gt;Unpacking the file&lt;/h2&gt;
&lt;p&gt;To make browsing the driver file easier I&amp;rsquo;ve converted the file to a tab-separated format, one line per sub-component, which you can &lt;a href=&#34;https://try-mts.com/files/list.zip&#34;&gt;download here&lt;/a&gt;. This can then easily be loaded into a spreadsheet or database for viewing.&lt;/p&gt;
&lt;p&gt;The Perl code I wrote to do this can be found at &lt;a href=&#34;https://gist.github.com/rupertl/f3736dcbf7ef2b094208&#34;&gt;this Github gist&lt;/a&gt; - it could be adapted to produce output in a different format if needed.&lt;/p&gt;
&lt;h2 id=&#34;example-the-fs-program&#34;&gt;Example: the &lt;code&gt;*FS&lt;/code&gt; program&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at component 548, the &lt;code&gt;*FS&lt;/code&gt; program itself, in more detail. The &lt;code&gt;subname&lt;/code&gt; field gives a brief description of each file, with a longer write-up in &lt;code&gt;description&lt;/code&gt;. &lt;code&gt;type&lt;/code&gt; indicates the type of file: the first letter is the class, eg &lt;code&gt;S&lt;/code&gt; for source code, &lt;code&gt;O&lt;/code&gt; for object files; further letters give more details, so type SA is assembler source code, type SF is Fortran source code etc.&lt;/p&gt;
&lt;p&gt;If the sub-component is on the disk already, its filename will be given in the &lt;code&gt;disk_name&lt;/code&gt; column; if it has to be restored from tape this will be blank. So for component 548 we can see the Makefile (548/1), compiled program (548/8) and helpfile (548/9) are on the disk already; the other components, which are source or object files, are not.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;tape_6250&lt;/code&gt; and &lt;code&gt;file_6250&lt;/code&gt; show where to find these files in the distribution tape. So for these items they can be found on 6.0T2 files 478-486, and could be restored by mounting the 6.0T2 tape, running &lt;code&gt;*FS&lt;/code&gt; and executing the command &lt;code&gt;restore (478) (486)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;location&lt;/code&gt; column shows what file names each sub-component had when it was saved to tape; note that &lt;code&gt;*FS&lt;/code&gt; does not use this when restoring the file so you will have to sign on to the correct user and rename files to get them to match their original location.&lt;/p&gt;
&lt;p&gt;For example, sub-component 548/2 has location &lt;code&gt;W930:FS#SA&lt;/code&gt; so you would need to sign on as user &lt;code&gt;W930&lt;/code&gt;, restore the file (&lt;code&gt;*FS&lt;/code&gt; gave it the name &lt;code&gt;FS.BASE_S002&lt;/code&gt; when I tried this) and then run the MTS command &lt;code&gt;$rename FS.BASE_S002 FS#SA&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the file &lt;code&gt;D6.0-NOTES.txt&lt;/code&gt; in the MTS distribution documentation for further information on the driver file list format.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Restoring files from tapes</title>
      <link>https://try-mts.com/restoring-files-from-tapes/</link>
      <pubDate>Sun, 31 Aug 2014 05:08:17 +0000</pubDate>
      <guid>https://try-mts.com/restoring-files-from-tapes/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Aug/tape-drive.png&#34; alt=&#34;tape drive&#34;&gt;
&lt;sup&gt;From &amp;ldquo;Introduction to Magnetic Tapes&amp;rdquo; by the UM Computer Centre, 1986, archived at the &lt;a href=&#34;http://babel.hathitrust.org/cgi/pt?id=mdp.39015011174631;view=1up;seq=1&#34;&gt;Hathi Trust Digital Library&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;On the mainframe, tapes were a cheap, high capacity solution to store files that were not always in use. An example from 1993 at UM - a tape costing $7 could store 165MB of data; storing the same amount on disk would cost $168 per month. Tapes are also portable and could be used to exchange data between different computers. The downside was tapes are slow - they had to be mounted manually and as access was sequential it could take several minutes to find a piece of data towards the end of the tape.&lt;/p&gt;
&lt;p&gt;Running under Hercules, the main use for tapes is to restore files from the MTS distribution and to transfer files to and from your host OS. Hercules emulates tapes using files on your local disk in AWS format.&lt;/p&gt;
&lt;p&gt;In this post we&amp;rsquo;ll look at how to mount a MTS distribution tape and restore a file. First off we need to know some of the terminology used to describe tapes and tape operations.&lt;/p&gt;
&lt;h2 id=&#34;background-and-terminology&#34;&gt;Background and terminology&lt;/h2&gt;
&lt;h3 id=&#34;tape-formats-and-parameters&#34;&gt;Tape formats and parameters&lt;/h3&gt;
&lt;p&gt;Tapes come with a number of different lengths, tracks and densities, which together determine their size. Typically MTS at the time of the D6 release was using 9 track tapes with 6250 BPI density. length varying from 100 to 2400 feet. On Hercules these parameters are not important as the tape capacity can be as much as the free disk space you have available, but you will find references to them if you read the original documentation.&lt;/p&gt;
&lt;p&gt;Tapes had a snap-in ring which if set in would allow writing to the tape; you will see references to RING IN and RING OUT on MTS tape commands and prompts which translates to read/write and read/only access. It is possible to specify an emulated tape drive as read only on Hercules, which means the tape can only be mounted as RING OUT.&lt;/p&gt;
&lt;p&gt;Typically a tape would have a physical label (ie written on the tape case) and a rack ID (used to find the tape from storage). These are not present on Hercules. The volume label, which is written electronically in the header of the tape data, is available on Hercules.&lt;/p&gt;
&lt;p&gt;At a low level, blocks of data can be written to tape in various formats depending on the type of data and what other non-MTS system would use the tape. We will restrict ourselves for now to looking at tapes written in a format understandable by &lt;code&gt;*FS&lt;/code&gt;, the file save and restore program on MTS.&lt;/p&gt;
&lt;h3 id=&#34;tape-drives-and-the-mounting-process&#34;&gt;Tape drives and the mounting process&lt;/h3&gt;
&lt;p&gt;A mainframe has multiple tape drives. When a user requests a tape, an operator would locate the tape (eg from a rack) and load it into a suitable free drive. The operator would then indicate to the user&amp;rsquo;s program which device the tape was mounted on, or if any other condition (no free drives, tape could not be found etc). If mounted the user&amp;rsquo;s program would then read from the assigned tape device.&lt;/p&gt;
&lt;p&gt;After the user&amp;rsquo;s program had finished with the tape it would release it, which would signal to the operator that the tape could be dismounted and stored back in the rack, or physically delivered to the user.&lt;/p&gt;
&lt;h3 id=&#34;emulated-tapes-on-hercules&#34;&gt;Emulated tapes on Hercules&lt;/h3&gt;
&lt;p&gt;Tape drives and the tapes initially loaded on them are defined in the Hercules configuration file &lt;code&gt;hercules.cfg&lt;/code&gt;. For example, here are two drives from the default config:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;	0184   3420   Tapes/d6.0t1.aws   ro  # T904, *FS D6.0 distribution tapes #1
	0185   3420   Tapes/d6.0t2.aws   ro  # T905,  &amp;#34; #2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line shows that a model 3420 emulated tape drive is at address 0184. Its name will be T904 on MTS. The initial tape loaded on the drive is &lt;code&gt;Tapes/d6.0t1.aws&lt;/code&gt; which is mounted read only (ring out).&lt;/p&gt;
&lt;p&gt;It is possible to get Hercules to change the tape loaded in a drive by using the &lt;code&gt;devinit&lt;/code&gt; command on the &lt;em&gt;Hercules console&lt;/em&gt;, which we will look at in a future post.&lt;/p&gt;
&lt;h2 id=&#34;mounting-a-tape---user-request&#34;&gt;Mounting a tape - user request&lt;/h2&gt;
&lt;p&gt;So after that introduction let&amp;rsquo;s mount a tape and extract a file. The tape we want is the D6.0T1 tape. From the MTS command line enter the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$MOUNT MTS:TAPE1 9TP *T* LBLTYPE=VLO VOL=6.0T1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s go through the parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;MTS:TAPE1&lt;/code&gt; is a dummy name; on a real mainframe you would enter the rack ID where the tape would be found. You can use TAPE1 to TAPE9 here if you are mounting multiple tapes at the same time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;9TP&lt;/code&gt; is the type of tape drive, which will always be 9 track.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;*T*&lt;/code&gt; is the pseudodevice that you want the tape to be made available on. You can use any name not already defined here (eg &lt;code&gt;*MYTAPE*&lt;/code&gt; would be OK but not &lt;code&gt;*SOURCE*&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;LBLTYPE=VLO&lt;/code&gt; indicates the format of the tape, which is  Volume Label Only for this particular tape.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;VOL=6.0T1&lt;/code&gt; is the electronic volume label.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that you don&amp;rsquo;t specify what tape drive will be used; this is the job of the operator to provide in the next step.&lt;/p&gt;
&lt;p&gt;After entering this command MTS will print your tape request and change the prompt to &lt;code&gt;%&lt;/code&gt; to indicate it is waiting for the tape to be mounted by the operator.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# MTS:TAPE1 9TP *T* LBLTYPE=VLO VOL=6.0T1
%
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;mounting-a-tape---operator-action&#34;&gt;Mounting a tape - operator action&lt;/h2&gt;
&lt;p&gt;Switch over to the &lt;em&gt;MTS operator&amp;rsquo;s console&lt;/em&gt;. You will see a new prompt similar to the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; 00040 MTS ST01:  T (1 9TP) (TAPE1) on 9TP, (6250), ring OUT, &amp;#39;&amp;#39; ***************
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Respond as follows&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;OK TAPE1 T904
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This tells MTS that the request for TAPE1 can be fulfilled by using device T904, which from the Hercules config file you know is the correct device for the 6.0T1 tape. You can also type other commands to reject or defer the request - see the Operator&amp;rsquo;s Manual for more detail.&lt;/p&gt;
&lt;p&gt;Once you type the &lt;code&gt;OK&lt;/code&gt; response, MTS will check that there is a tape in the drive, the ring is set correctly and the volume label matches the request. If all checks out you will see the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00040 MTS Mount check OK.Mount (TAPE1) TAPE1 on T904 Ring Out
00040 MTS ST01, TAPE1:Accepted on T904
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If instead you got a &amp;rsquo;not ready&amp;rsquo; message then you will need to bring the tape device back online - from the Hercules config file this is device 0184 so on the &lt;em&gt;Hercules console&lt;/em&gt; type &lt;code&gt;devinit 0184&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once the tape is mounted, back on the user MTS session you will see&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*T* (TAPE1): Mounted on T904
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now pseudodevice &lt;code&gt;*T*&lt;/code&gt; can be used to access the tape&amp;rsquo;s contents.&lt;/p&gt;
&lt;h2 id=&#34;using-fs&#34;&gt;Using &lt;code&gt;*FS&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;On the user session, start the &lt;code&gt;*FS&lt;/code&gt; program with &lt;code&gt;*T*&lt;/code&gt; as its parameter:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$RUN *fs 0=*T*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The prompt will change to &lt;code&gt;=&lt;/code&gt;. You can use &lt;code&gt;help&lt;/code&gt; to see the commands available. Let&amp;rsquo;s look for the ASMG user guide: type&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;= list ASMG.USER?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The response you will get is:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;= FS list for tape MTS.:TAPE1 on T904. Density= 6250 Volume=6.0T1 Lbltype=VLO O


= File D             File Name           Ver# Type  Lrecl  Size   Dev    Date a
=
=  230   ASMG.USER&amp;#39;S_GUIDE.P.093            1 Seq      80    41  Page  JULY 03,
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So this is file 230 on the tape. To restore a copy to your current user&amp;rsquo;s file area, type &lt;code&gt;restore (230)&lt;/code&gt; (the brackets are needed) and then &lt;code&gt;stop&lt;/code&gt; to execute the restore. When done, &lt;code&gt;*FS&lt;/code&gt; will ask you if you want to release (unmount) the tape and will then return to the command line prompt.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;= File 230 &amp;#34;ASMG.USER&amp;#39;S_GUIDE.P.093(1)&amp;#34; ... has been restored to ASMG.USER093
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can now print or view the file &lt;code&gt;ASMG.USER093&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;unmounting&#34;&gt;Unmounting&lt;/h2&gt;
&lt;p&gt;If you didn&amp;rsquo;t release the tape in the &lt;code&gt;*FS&lt;/code&gt; session, you can do it from the command line by typing &lt;code&gt;$release *T*&lt;/code&gt;. A message will be printed on the operator&amp;rsquo;s console telling you to remove the tape, which you can ignore.&lt;/p&gt;
&lt;p&gt;If you want to mount the same tape again, remember to &lt;code&gt;devinit&lt;/code&gt; the device from the Hercules console.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Commands to mount and use tapes can be found using online help (eg &lt;code&gt;HELP MOUNT&lt;/code&gt;) or &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol01-TheMichiganTerminalSystem-Nov1991.pdf&#34;&gt;MTS volume 1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A more detailed look at how tapes are formatted and used can be found in &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol19-MagneticTapes-Oct1993.pdf&#34;&gt;MTS volume 19&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Commands that the operator can use to accept or reject tape mount requests can be found in the &lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYYmVmNTNiMDUtN2ZlNS00ZDI2LTgzZTktNjBmODU4MTE3OGZm&#34;&gt;Operator&amp;rsquo;s Manual&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In future posts I will look at &lt;a href=&#34;https://try-mts.com/finding-mts-components-from-the-driver-file/&#34;&gt;how to find and restore files from the distribution tapes&lt;/a&gt; in more detail and how to create your own tapes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Updated 14-Apr-2015: corrected LBLTYPE note&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Submitting batch jobs from the reader</title>
      <link>https://try-mts.com/submitting-batch-jobs-from-the-reader/</link>
      <pubDate>Sun, 24 Aug 2014 03:55:11 +0000</pubDate>
      <guid>https://try-mts.com/submitting-batch-jobs-from-the-reader/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Aug/card-punch.png&#34; alt=&#34;punch&#34;&gt;
&lt;sup&gt;The IBM 29 card punch, from a &lt;a href=&#34;http://deepblue.lib.umich.edu/handle/2027.42/79594&#34;&gt;film&lt;/a&gt; at the Deep Blue collection at the University of Michigan&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://try-mts.com/submitting-batch-jobs-from-the-terminal/&#34;&gt;previous post&lt;/a&gt; we looked at submitting batch jobs from the terminal; this time we will submit jobs from outside MTS using the emulated card reader. Using this method you can create jobs using a text editor on your host OS, submit to MTS and collect output without having to sign on interactively.&lt;/p&gt;
&lt;p&gt;On Hercules, the emulated card reader accepts ASCII format text files, with each line representing one card. On MTS, the HASP batch scheduler reads cards from the reader and sets up batch jobs, with any output going to the printer.&lt;/p&gt;
&lt;p&gt;Compared to submitting jobs from the terminal, you will need two additional items: a S8 card and full sign-on details.&lt;/p&gt;
&lt;h2 id=&#34;s8-cards&#34;&gt;S8 cards&lt;/h2&gt;
&lt;p&gt;For the first card in your job you will need a S8 card. This acts a header for batch jobs submitted to the reader and includes the number that will be used as the batch sequence ID.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq#TOC-Q26:-I-m-trying-to-submit-a-batch-job-to-HASP-from-a-card-reader-but-I-see-that-I-need-an-S-8-card.-What-is-an-S-8-card-and-how-do-I-make-some-&#34;&gt;FAQ&lt;/a&gt;, these were handed out to users at the computer centre in UM for users to attach to their jobs and were then returned as a receipt when the job had finished.&lt;/p&gt;
&lt;p&gt;You can create S8 cards by running the operator job &lt;code&gt;*RCP&lt;/code&gt; and directing output to the card punch.&lt;/p&gt;
&lt;p&gt;Switch to the &lt;em&gt;MTS operator&amp;rsquo;s console&lt;/em&gt;. Enter &lt;code&gt;$DRAIN PCH1&lt;/code&gt; to get HASP to let go of the punch then &lt;code&gt;MTS *RCP&lt;/code&gt;. Enter &lt;code&gt;&amp;gt;PCH1&lt;/code&gt;, &lt;code&gt;NO&lt;/code&gt; and two six digit numbers to select the range of IDs. Then &lt;code&gt;$START PCH1&lt;/code&gt; to give control of the punch back to HASP. The console should look like the below.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$DRAIN PCH1
$*OK
MTS *RCP
00053..MTS *RCP
00053 MTS  MTS RECEIPT CARD PROGRAM 
00053 MTS  ENTER THE NAME OF THE PUNCH
&amp;gt;PCH1
00053 MTS  ENTER YES IF RECEIPT CARDS WANTED, NO IF ONLY DECK CARDS WANTED
NO
00053 MTS  ENTER SIX DIGIT STARTING RECEIPT NUMBER
100000
00053 MTS  ENTER SIX DIGIT ENDING RECEIPT NUMBER
100100
00053 MTS  ALL RECEIPT CARDS HAVE BEEN PUNCHED
$START PCH1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you take a look at the punch file &lt;code&gt;Units/PCH1.txt&lt;/code&gt;, the first three lines should look like the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;\                 100000               DECK                             100000 .
\                 100001               DECK                             100001 .
\                 100002               DECK                             100002 .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can now cut lines from this file and paste into your batch job file at the top. It is possible to reuse cards/IDs once the job using an ID has finished, but it&amp;rsquo;s best to use a new S8 card for each job.&lt;/p&gt;
&lt;h2 id=&#34;sign-on-details&#34;&gt;Sign-on details&lt;/h2&gt;
&lt;p&gt;For interactive submission you could use &lt;code&gt;$signon *&lt;/code&gt; as MTS knew what user ID submitted the request. For card reader submission you need to supply the user name, eg &lt;code&gt;$signon st01&lt;/code&gt;, and in the next card supply the password.&lt;/p&gt;
&lt;h2 id=&#34;putting-it-together&#34;&gt;Putting it together&lt;/h2&gt;
&lt;p&gt;So as an example let&amp;rsquo;s do the simple calculation job again.&lt;/p&gt;
&lt;p&gt;Create a file on your host operating system. For the first line, cut the first S8 card from the punch file. For the second and third line provide the sign-on ID and password. The rest of the job is as before.&lt;/p&gt;
&lt;p&gt;The file should look something like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;\                 100000               DECK                             100000 .
$signon st01
st01
$calc
a = 10  
b = 13  
c = 7  
n = (a * b) / c  
list  
$endfile
$signoff
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;submitting-the-job-and-collecting-output&#34;&gt;Submitting the job and collecting output&lt;/h2&gt;
&lt;p&gt;Copy your batch file to &lt;code&gt;Units/RDR.txt&lt;/code&gt;, overwriting the current contents.&lt;/p&gt;
&lt;p&gt;Switch to the &lt;em&gt;Hercules operator&amp;rsquo;s console&lt;/em&gt; and type &lt;code&gt;devinit c&lt;/code&gt; to bring the reader online.&lt;/p&gt;
&lt;p&gt;HASP will read the job, execute it and print output to the printer file. Look in &lt;code&gt;Units/PTR2.txt&lt;/code&gt; and you will see output like the below, surrounded by the usual header and footer:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$SIGNON ST01
No Charges
Last signon was at 21:55:55, Sun Aug 23/14
User ST01 signed on at 22:38:11, Sun Aug 23/14



$calc
A        = 10
B        = 13
C        = 7
N        = 18.5714285714285694
$signoff
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;copying-files-into-mts&#34;&gt;Copying files into MTS&lt;/h2&gt;
&lt;p&gt;You can use this batch job submission technique to send files into MTS. Say you want to send this file&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Hello world
Welcome to MTS!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;to the file &lt;code&gt;DELTA&lt;/code&gt; owned by ST01. You could create a batch job similar to the below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;\                 100001               DECK                             100001 .
$signon ST01
ST01
$create delta
$copy *source* delta
Hello world
Welcome to MTS!
$endfile
$signoff
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See the &lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYYmVmNTNiMDUtN2ZlNS00ZDI2LTgzZTktNjBmODU4MTE3OGZm&#34;&gt;Operator&amp;rsquo;s manual&lt;/a&gt; for details of the &lt;code&gt;*RCP&lt;/code&gt; job.&lt;/p&gt;
&lt;p&gt;To see how physical cards were punched by users before submitting them to MTS take a look at the film &lt;a href=&#34;http://deepblue.lib.umich.edu/handle/2027.42/79594&#34;&gt;Basic Operation of the IBM 29 Card Punch&lt;/a&gt; from 1967 at UM.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Submitting batch jobs from the terminal</title>
      <link>https://try-mts.com/submitting-batch-jobs-from-the-terminal/</link>
      <pubDate>Sun, 17 Aug 2014 03:02:50 +0000</pubDate>
      <guid>https://try-mts.com/submitting-batch-jobs-from-the-terminal/</guid>
      <description>&lt;p&gt;On a mainframe, batch jobs allowed users to execute long running jobs without tying up their terminal - and reduce the cost of running jobs by scheduling them to quieter parts of the day.&lt;/p&gt;
&lt;p&gt;On Hercules, where you can run a number of terminals and don&amp;rsquo;t get charged for your work, the benefits are less obvious - but it is interesting to learn how batch jobs were processed and it also opens up the possibility of submitting jobs from your host OS to MTS without having to log on.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-job&#34;&gt;Creating a job&lt;/h2&gt;
&lt;p&gt;For a simple example we&amp;rsquo;ll use the &lt;code&gt;$calc&lt;/code&gt; command to do some calculations.&lt;/p&gt;
&lt;p&gt;Create a file - any name will work but we&amp;rsquo;ll use &lt;code&gt;calc.run&lt;/code&gt; and edit it, inserting the below contents:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$signon *
$calc
a = 10
b = 13
c = 7
n = (a * b) / c
list
$endfile
$signoff
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line should be a &lt;code&gt;$signon&lt;/code&gt; command. As you are submitting from the terminal and are already logged in you don&amp;rsquo;t need to provide username or password and the &lt;code&gt;*&lt;/code&gt; means run under the current user. On this line you could also set options to limit the amount of resources used or control printing.&lt;/p&gt;
&lt;p&gt;After that come the commands to execute, which are processed just like they were entered from the keyboard. So here we start the &lt;code&gt;$calc&lt;/code&gt; command then provide it a number of commands up to the &lt;code&gt;$endfile&lt;/code&gt; which exits &lt;code&gt;$calc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The last line of the file should be &lt;code&gt;$signoff&lt;/code&gt; to show the job has ended.&lt;/p&gt;
&lt;h2 id=&#34;submitting-to-hasp&#34;&gt;Submitting to HASP&lt;/h2&gt;
&lt;p&gt;To submit the job, you copy the command file to the &lt;code&gt;*BATCH*&lt;/code&gt; pseudodevice:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$copy calc.run *batch*&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;HASP will receive the contents of the file and schedule the job, giving you a job number eg&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; *BATCH* assigned receipt number 600026
  *BATCH* 600026 released.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, the command file is no longer needed as HASP will make a copy.&lt;/p&gt;
&lt;h2 id=&#34;getting-the-results&#34;&gt;Getting the results&lt;/h2&gt;
&lt;p&gt;On a single user system you should be able to get the results immediately. Check the printer file &lt;code&gt;Units/PTR2.txt&lt;/code&gt; and you will see the output after the usual header page:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$SIGNON ST01
No Charges
Last signon was at 10:50:32, Thu Aug 13/14
User ST01 signed on at 15:19:43, Sat Aug 16/14



$calc
A        = 10
B        = 13
C        = 7
N        = 18.5714285714285694
$signoff
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;The chapter called &amp;ldquo;Batch use of MTS&amp;rdquo; in &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/Michigan_Terminal_System_Second_Edition_Vol1_Dec67.pdf&#34;&gt;MTS Volume 1&lt;/a&gt; gives an overview of running batch jobs, including setting extra parameters and controlling the queue.&lt;/p&gt;
&lt;p&gt;In a future post I will look at submitting jobs using the card reader.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Editing files</title>
      <link>https://try-mts.com/editing-files/</link>
      <pubDate>Sun, 10 Aug 2014 04:00:52 +0000</pubDate>
      <guid>https://try-mts.com/editing-files/</guid>
      <description>&lt;p&gt;So far we&amp;rsquo;ve been using &lt;code&gt;$COPY *SOURCE* file&lt;/code&gt; to create text files - for more complex editing we need to get to grips with the MTS editor. This post will give a quick introduction focusing on the visual editing mode.&lt;/p&gt;
&lt;p&gt;Coming from a Unix or Windows background, the MTS editor will use an unfamiliar set of keys but once you learn them it is easy to use. There are some interesting features such as an integrated, editable copy and paste/command execution area, and the ability to remember locations and jump to them quickly.&lt;/p&gt;
&lt;h2 id=&#34;starting-up-and-exiting&#34;&gt;Starting up and exiting&lt;/h2&gt;
&lt;p&gt;Enter &lt;code&gt;$EDIT gamma&lt;/code&gt; to start editing file gamma. Remember you have to &lt;code&gt;$CREATE&lt;/code&gt; the file in MTS if it does not exist already.&lt;/p&gt;
&lt;p&gt;Initially you will be in command mode with a &lt;code&gt;:&lt;/code&gt; prompt; type &lt;code&gt;VISUAL&lt;/code&gt; or just &lt;code&gt;V&lt;/code&gt; to enter visual mode.&lt;/p&gt;
&lt;p&gt;Hit &lt;code&gt;PA2&lt;/code&gt; to get out of visual mode and &lt;code&gt;PA2&lt;/code&gt; again to quit the editor and get back to the MTS command line.&lt;/p&gt;
&lt;p&gt;The editor makes heavy use of the PF function keys so check with your 3270 terminal emulator documentation on how to access these. Often they are bound to PC function keys, ie &lt;code&gt;PF1&lt;/code&gt; can be issued by pressing &lt;code&gt;F1&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;screen-arrangement&#34;&gt;Screen arrangement&lt;/h2&gt;
&lt;p&gt;On entering visual mode for a new file the screen will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Aug/edit-work-area.png&#34; alt=&#34;edit work area&#34;&gt;&lt;/p&gt;
&lt;p&gt;The bottom line shows a ruler and the last but one line shows the cost of the edit session (not applicable on Hercules of course) and the work field, used to hold copied text and enter commands. The rest of the screen is used to view and edit text.&lt;/p&gt;
&lt;h2 id=&#34;entering-text&#34;&gt;Entering text&lt;/h2&gt;
&lt;p&gt;You can start typing text immediately. The cursor keys allow you to move back and forth in the line; the delete and backspace keys work as expected also.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Insert&lt;/code&gt; will switch to overwrite mode; on my emulator at least I find I need to press &lt;code&gt;Enter&lt;/code&gt; to switch it off.&lt;/p&gt;
&lt;p&gt;Press &lt;code&gt;Newline&lt;/code&gt; to start a new line; the line number will not be displayed immediately. Press &lt;code&gt;Enter&lt;/code&gt; to ask the editor to refresh the screen and set the line number.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Tab&lt;/code&gt; will move to the start of the next line; &lt;code&gt;Backtab&lt;/code&gt; will move to the start of the current line if in the middle of a line, else  the previous line.&lt;/p&gt;
&lt;p&gt;If you move the cursor down a number of lines and start typing then press &lt;code&gt;Enter&lt;/code&gt;, the line number given will reflect the gap and the text will move up so no gap is displayed.&lt;/p&gt;
&lt;h2 id=&#34;scrolling-around&#34;&gt;Scrolling around&lt;/h2&gt;
&lt;p&gt;For documents larger than one screen, press &lt;code&gt;PF1&lt;/code&gt; to scroll up one page and &lt;code&gt;PF4&lt;/code&gt; to scroll down. &lt;code&gt;PF7&lt;/code&gt; will scroll down one line. &lt;code&gt;HOME&lt;/code&gt; will move to the top of the window.&lt;/p&gt;
&lt;p&gt;Moving the cursor up from the top of the screen or down from the bottom will not scroll; instead the cursor will wrap around to the bottom or top.&lt;/p&gt;
&lt;p&gt;The editor can remember your location in the file and return to it after you have moved. To set memory location 1, press &lt;code&gt;PF3&lt;/code&gt; two times in a row. After you have moved, press &lt;code&gt;PF3&lt;/code&gt; once to return to that location. &lt;code&gt;PF6&lt;/code&gt; and &lt;code&gt;PF9&lt;/code&gt; can be used similarly to set and return to memory locations 2 and 3. Initially the memory locations are defined as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Memory 1 - first line of file&lt;/li&gt;
&lt;li&gt;Memory 2 - line 1000 of file&lt;/li&gt;
&lt;li&gt;Memory 3 - last line of file&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;copy-and-paste-and-the-work-field&#34;&gt;Copy and paste and the work field&lt;/h2&gt;
&lt;p&gt;A form of copy and paste is done using the work field at the bottom of the screen. Press &lt;code&gt;PF2&lt;/code&gt; to copy from the cursor to the end of the line to the work field. Press &lt;code&gt;PF5&lt;/code&gt; to paste the contents of the work field at the cursor position.&lt;/p&gt;
&lt;p&gt;You can jump to the work area with &lt;code&gt;SysReq&lt;/code&gt; and edit what&amp;rsquo;s there to control what is pasted..&lt;/p&gt;
&lt;p&gt;You can also enter line-orientated commands in the work area and execute them with &lt;code&gt;PF8&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that instead of using this you can use the native functions provided by your host operating system to copy and paste.&lt;/p&gt;
&lt;h2 id=&#34;insert-and-delete-lines&#34;&gt;Insert and delete lines&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;EraseEOF&lt;/code&gt; will delete the rest of the line or the whole line if the cursor is at the start of a line.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PF10&lt;/code&gt; will insert a new line below the current line. Repeatedly pressing it will insert 2x the number of blank lines each time, so press twice for two lines, three times for four lines, four times for eight lines and so on.&lt;/p&gt;
&lt;p&gt;There is no predefined key to split a line into two. To do this, type &lt;code&gt;VSPLIT&lt;/code&gt; into the work area. Move the cursor back to the place you want to split and press &lt;code&gt;PF8&lt;/code&gt; to execute the split command.&lt;/p&gt;
&lt;h2 id=&#34;line-numbering&#34;&gt;Line numbering&lt;/h2&gt;
&lt;p&gt;The editor will handle line numbers. If you have line 1 and line 2 entered but want to add a line in between (by pressing &lt;code&gt;PF10&lt;/code&gt;) it will use line number 1.5. Remember you can use the &lt;code&gt;$RENUMBER&lt;/code&gt; command back at the MTS command prompt to turn an uneven sequence of lines into 1, 2, 3 etc.&lt;/p&gt;
&lt;h2 id=&#34;line-extensions&#34;&gt;Line extensions&lt;/h2&gt;
&lt;p&gt;When you reach the right end of the screen while typing a line the editor will not let you continue. Press &lt;code&gt;PF11&lt;/code&gt; to extend the line onto the next row of the screen and you can keep typing.&lt;/p&gt;
&lt;h2 id=&#34;saving-and-undo&#34;&gt;Saving and undo&lt;/h2&gt;
&lt;p&gt;There is no explicit save command - the file is saved back to disk when the screen is refreshed (eg on scrolling or with &lt;code&gt;Enter&lt;/code&gt;) or when you exit visual mode. Because of this it is a good idea to make a backup of important files before you start editing them.&lt;/p&gt;
&lt;p&gt;It is possible to undo all changes done in the current editing session. Switch back to command line mode (with &lt;code&gt;PA2&lt;/code&gt;) and type &lt;code&gt;UNDO&lt;/code&gt; at the &lt;code&gt;:&lt;/code&gt; prompt.&lt;/p&gt;
&lt;h2 id=&#34;summary-of-pf-keys&#34;&gt;Summary of PF keys&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PF1&lt;/code&gt; Scroll up ten lines&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF2&lt;/code&gt; Append rest of line to work area&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF3&lt;/code&gt; Memory 1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF4&lt;/code&gt; Scroll down ten lines&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF5&lt;/code&gt; Insert contents of work area in current line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF6&lt;/code&gt; Memory 2&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF7&lt;/code&gt; Scroll up one line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF8&lt;/code&gt; Executes contents of work area as command&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF9&lt;/code&gt; Memory 3&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF10&lt;/code&gt; Insert line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF11&lt;/code&gt; Extend current line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PF12&lt;/code&gt; Move to the previous position&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;command-mode&#34;&gt;Command mode&lt;/h2&gt;
&lt;p&gt;The editor also contains a rich command mode which I will not go into in depth here. Some simple examples:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;: PRINT&lt;/code&gt;&lt;br&gt;
Print the current line&lt;/p&gt;
&lt;p&gt;&lt;code&gt;: +1&lt;/code&gt;&lt;br&gt;
Move forwards one line&lt;/p&gt;
&lt;p&gt;&lt;code&gt;: ALTER &#39;aa&#39;bb&#39;&lt;/code&gt;&lt;br&gt;
Find the first occurrence  on the current line of aa and replace with bb&lt;/p&gt;
&lt;p&gt;&lt;code&gt;: ALTER@ALL &#39;cc&#39;dd&#39;&lt;/code&gt; &lt;br&gt;
Replace all occurrences of cc with dd on the current line&lt;/p&gt;
&lt;p&gt;&lt;code&gt;: ALTER@ALL /FILE &#39;ee&#39;ff&#39;&lt;/code&gt;&lt;br&gt;
Replace all occurrences of ee with ff in the whole file~&lt;/p&gt;
&lt;h2 id=&#34;more-information&#34;&gt;More information&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol18-TheMTSFileEditor-Feb1988.pdf&#34;&gt;MTS Volume 18&lt;/a&gt;, The MTS File Editor for complete documentation; also look at the online help by typing &lt;code&gt;HELP&lt;/code&gt; from the editor command mode prompt.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Extracting files using the card punch</title>
      <link>https://try-mts.com/extracting-files-using-the-card-punch/</link>
      <pubDate>Tue, 05 Aug 2014 12:36:49 +0000</pubDate>
      <guid>https://try-mts.com/extracting-files-using-the-card-punch/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Aug/640px-FortranCardPROJ039-agr.jpg&#34; alt=&#34;punched card&#34;&gt;
&lt;sup&gt;&lt;a href=&#34;http://commons.wikimedia.org/wiki/File:FortranCardPROJ039.agr.jpg#mediaviewer/File:FortranCardPROJ039.agr.jpg&#34;&gt;FortranCardPROJ039.agr&lt;/a&gt; by Arnold Reinhold. Licensed under &lt;a title=&#34;Creative Commons Attribution-Share Alike 2.5&#34; href=&#34;http://creativecommons.org/licenses/by-sa/2.5&#34;&gt;CC BY-SA 2.5&lt;/a&gt; via &lt;a href=&#34;//commons.wikimedia.org/wiki/&#34;&gt;Wikimedia Commons&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Punched cards were a way to get code and data into and out of the IBM mainframe. Under Hercules you can use the emulated card punch as a way to get ASCII format text files out of the system.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;As per &lt;a href=&#34;https://try-mts.com/printing-in-mts/&#34;&gt;printing in MTS&lt;/a&gt; you will need HASP up and running to use the card punch.&lt;/p&gt;
&lt;h2 id=&#34;submitting-the-request&#34;&gt;Submitting the request&lt;/h2&gt;
&lt;p&gt;To send files to the card punch, direct them the &lt;code&gt;*PUNCH*&lt;/code&gt; pseudodevice in the same was as you direct print files to &lt;code&gt;*PRINT*&lt;/code&gt;. Use &lt;code&gt;$LIST&lt;/code&gt; to include line numbers and &lt;code&gt;$COPY&lt;/code&gt; to send file as is.&lt;/p&gt;
&lt;p&gt;Note that there is a maximum of 80 characters per line and carriage control characters will not format the output.&lt;/p&gt;
&lt;p&gt;For example, say you have a file &lt;code&gt;ALPHA&lt;/code&gt; with two lines:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Hello world!
Welcome to MTS.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To send this file to the punch do:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# copy alpha *punch*
&amp;gt; *PUNCH* assigned receipt number 600020
  *PUNCH* 600020 released, 3 cards, croute=CNTR.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As for print jobs, the system will assign a job number, here 60020. The punch prints one card per line with an extra card first as a header.&lt;/p&gt;
&lt;h2 id=&#34;collecting-output&#34;&gt;Collecting output&lt;/h2&gt;
&lt;p&gt;From your host computer, look at the file &lt;code&gt;Units/PCH1.txt&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; COM  -0-     &amp;amp;&amp;amp;  &amp;amp;&amp;amp;   jjjaaa              -0-     &amp;amp;&amp;amp;  &amp;amp;&amp;amp;   jjjaaa      M 600020
Hello world!
Welcome to MTS.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line is the header card, showing the job number. The rest of the file is the same as the file you sent from MTS.&lt;/p&gt;
&lt;h2 id=&#34;clearing-the-card-punch&#34;&gt;Clearing the card punch&lt;/h2&gt;
&lt;p&gt;After you submit a job, the card punch will go offline and will not punch any further jobs until you clear it. On the mainframe this was presumably to allow the operator to separate different card outputs and arrange delivery.&lt;/p&gt;
&lt;p&gt;On the &lt;em&gt;operator&amp;rsquo;s&lt;/em&gt; console you can see that the punch needs attention as the letters &lt;code&gt;PCH1&lt;/code&gt; will be displayed in bold on the third line.&lt;/p&gt;
&lt;p&gt;To clear the punch, go to the &lt;em&gt;Hercules&lt;/em&gt; console and type &lt;code&gt;devinit 000d&lt;/code&gt;. This will bring the punch back online and the next job will be punched - replacing the contents of host file &lt;code&gt;PCH1.txt&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;Although you could use the card reader to move data from the outside world into the MTS system, the default set up is that HASP uses it to receive batch jobs. This will be covered in a future post.&lt;/p&gt;
&lt;p&gt;Another way to get data into and out of the system is using emulated tapes which we will also look at later.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Printing in MTS</title>
      <link>https://try-mts.com/printing-in-mts/</link>
      <pubDate>Sun, 27 Jul 2014 04:38:56 +0000</pubDate>
      <guid>https://try-mts.com/printing-in-mts/</guid>
      <description>&lt;p&gt;MTS has a rich set of options for handling printed output, suitable for a mainframe with many users sharing multiple printers.&lt;/p&gt;
&lt;p&gt;Running under Hercules, the printer is emulated by writing ASCII text output to a file. Once you issue a print job within MTS you can view the file from your host operating system. This is a useful way to extract text files from MTS.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;In order to get printing working you need the print device to the configured correctly (see &lt;a href=&#34;https://try-mts.com/up-and-running-1-installation&#34;&gt;part 1&lt;/a&gt;) and HASP needs to be running (see &lt;a href=&#34;https://try-mts.com/up-and-running-2-booting-the-system/&#34;&gt;part 2&lt;/a&gt;). Once this is done you can direct output to the &lt;code&gt;*PRINT*&lt;/code&gt; pseudodevice and HASP will spool this for printing. With the default D6.0A configuration this will print to the file &lt;code&gt;Units/PTR2.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have problems with this see the &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq#TOC-Q21:-I-get-an-error-when-trying-to-print-to-a-1403-printer-HASPLING-PTRn:-Unrecoverable-error---Printer-status:-0100-0E00-01:80-.-How-do-I-work-around-this-&#34;&gt;D6.0A FAQ question 21&lt;/a&gt; for an alternative way to print.&lt;/p&gt;
&lt;h2 id=&#34;printing-text-files&#34;&gt;Printing text files&lt;/h2&gt;
&lt;p&gt;The LIST command takes a second argument to determine where to send the output. By specifying &lt;code&gt;*PRINT*&lt;/code&gt; you can send this to the print queue.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LIST alpha *PRINT*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MTS will respond with the receipt number for the print job, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; *PRINT* assigned receipt number 600011
&amp;gt; *PRINT* 600011 released, 3 pages, route=CNTR.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On a real shared mainframe it may take time for your job to reach the front of the queue and complete printing (although mainframe line printers were very fast), but on Hercules your output will be available almost immediately.&lt;/p&gt;
&lt;p&gt;Switch back to your host operating system and look at the printer file - you will see output like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jul/print1.png&#34; alt=&#34;print1&#34;&gt;
&lt;img src=&#34;https://try-mts.com/images/2014/Jul/print2.png&#34; alt=&#34;print2&#34;&gt;&lt;/p&gt;
&lt;p&gt;There is a header page in large type showing the username and print receipt number first, then the actual output (2 lines here) and finally a footer page with a summary of the job.&lt;/p&gt;
&lt;h2 id=&#34;carriage-control&#34;&gt;Carriage control&lt;/h2&gt;
&lt;p&gt;Some MTS files have carriage control instructions: these are characters in the first column of each line telling the output device driver how to format the file, eg overprint or centre line.&lt;/p&gt;
&lt;p&gt;For these files you need to use &lt;code&gt;$COPY&lt;/code&gt; to send the file to the printer (or screen).&lt;/p&gt;
&lt;p&gt;For example, let&amp;rsquo;s look at the first ten lines of the &lt;code&gt;*CCMEMOS&lt;/code&gt; file. If we use &lt;code&gt;$list *ccmemos(1,10) *print*&lt;/code&gt; we get the below output; note the carriage control characters like &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt; in the first column after the line number.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Listing of *CCMEMOS(1,10) at 23:08:07 on JUL 26, 1914 for CCid=REL. on MT       
                                           Page    1

     1     1
     2     -
     3                                      *CCMEMOS
     4     0                               April 1988
     5     0         Current Computing Center Memos are available for checkout o
r
     6          in filing cabinets at all Campus Computing Sites.
     7     0         The Computing Center&amp;#39;s Documentation Support Staff (313-763
-
     8          7624) or $MESS send to CC_Doc_Support_Staff, will mail  CC  Memo
s
     9          to  individual  users  who  cannot  pick  up copies at one of th
e
    10          public stations, for example, those who live out-of-town. We wil
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If instead we use the command &lt;code&gt;$copy *ccmemos(1,10) *print*&lt;/code&gt; we get this formatted output.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                                *CCMEMOS

                               April 1988

         Current Computing Center Memos are available for checkout or
    in filing cabinets at all Campus Computing Sites.

         The Computing Center&amp;#39;s Documentation Support Staff (313-763-
    7624) or $MESS send to CC_Doc_Support_Staff, will mail  CC  Memos
    to  individual  users  who  cannot  pick  up copies at one of the
    public stations, for example, those who live out-of-town. We will
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;print-options&#34;&gt;Print options&lt;/h2&gt;
&lt;p&gt;MTS has a large number of options to adjust how print output is processed. Many of these are no longer applicable (eg which physical printer to send it to, where the operators should deliver the hard copy). Look at the online help for &lt;code&gt;SET&lt;/code&gt; to learn more.&lt;/p&gt;
&lt;h2 id=&#34;text-formatters&#34;&gt;Text formatters&lt;/h2&gt;
&lt;p&gt;MTS comes with a number of formatters that will take a marked up file and return output with carriage control.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FORMAT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TEXT360&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TEXTFORM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Donald Knuth&amp;rsquo;s &lt;code&gt;TeX&lt;/code&gt; (appears available but not installed on D6.0A&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I may come back to these in a future post.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;As always, &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol01-TheMichiganTerminalSystem-Nov1991.pdf&#34;&gt;MTS Volume 1&lt;/a&gt; has a good introduction to getting printed output.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol15-FORMATAndTEXT360-Mar1988.pdf&#34;&gt;MTS Volume 15&lt;/a&gt; has more information on &lt;code&gt;TEXT360&lt;/code&gt; and &lt;code&gt;FORMAT&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Pseudodevices and redirection</title>
      <link>https://try-mts.com/pseudodevices-and-redirection/</link>
      <pubDate>Sat, 19 Jul 2014 05:06:00 +0000</pubDate>
      <guid>https://try-mts.com/pseudodevices-and-redirection/</guid>
      <description>&lt;p&gt;As we saw in &lt;a href=&#34;https://try-mts.com/working-with-files/&#34;&gt;working with files&lt;/a&gt; it is usually possible to give a pseudodevice name such as &lt;code&gt;*SOURCE*&lt;/code&gt; instead of a filename. This post will go over what pseudodevices are available and also how redirection of command input/output works.&lt;/p&gt;
&lt;h2 id=&#34;pseudodevices&#34;&gt;Pseudodevices&lt;/h2&gt;
&lt;p&gt;The following pseudodevices come as standard on the D6.0 distribution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*MSOURCE*&lt;/code&gt; is the master source of input, either the keyboard for interactive sessions or the card reader for batch. It cannot be redefined.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*MSINK*&lt;/code&gt; is the master output destination, either the screen for interactive sessions or the printer for batch. It cannot be redefined either.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*SOURCE*&lt;/code&gt; is the current source of input, initially &lt;code&gt;*MSOURCE&lt;/code&gt;, and can be redefined.&lt;/li&gt;
&lt;li&gt;Similarly &lt;code&gt;*SINK*&lt;/code&gt; is the current output destination, initially &lt;code&gt;*MSINK&lt;/code&gt;, and can be redefined.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*DUMMY*&lt;/code&gt; is the empty device, similar to &lt;code&gt;/dev/null/&lt;/code&gt; on Unix.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*PUNCH*&lt;/code&gt; is the card punch device - on Hercules this is emulated by writing to a file. If output is sent to &lt;code&gt;*PUNCH*&lt;/code&gt; during an interactive session, a new batch job will be set up to do this.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*PRINT*&lt;/code&gt; is the print device. Again on Hercules this is emulated by writing to a file.&lt;/li&gt;
&lt;li&gt;Output to &lt;code&gt;*BATCH*&lt;/code&gt; creates a new batch job with the data sent used as the commands to execute.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*IMPORT*&lt;/code&gt; and &lt;code&gt;*EXPORT*&lt;/code&gt; were used for BITNET connections and are not available on Hercules.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*TAPE1*&lt;/code&gt; to &lt;code&gt;*TAPE9*&lt;/code&gt; are used as logical device names when mounting a tape on the emulated system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also create your own pseudodevices using the MTS &lt;code&gt;$MOUNT&lt;/code&gt; and &lt;code&gt;$CREATE&lt;/code&gt; commands.&lt;/p&gt;
&lt;h2 id=&#34;redirection-using-source-and-sink&#34;&gt;Redirection using &lt;code&gt;$SOURCE&lt;/code&gt; and &lt;code&gt;$SINK&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can use the MTS commands &lt;code&gt;$SOURCE&lt;/code&gt; and &lt;code&gt;$SINK&lt;/code&gt; to change the default input and output from &lt;code&gt;*MSOURCE*&lt;/code&gt; and &lt;code&gt;*MSINK*&lt;/code&gt;. In the below example I redirect output to the temporary file &lt;code&gt;-sinkfile&lt;/code&gt; and then run &lt;code&gt;$FILESTATUS&lt;/code&gt;. The output of the command is stored there and not displayed on screen. Finally I run &lt;code&gt;$SINK PREVIOUS&lt;/code&gt; to restore the output to &lt;code&gt;*MSINK*&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; # sink -sinkfile
 # filestatus
 # sink previous
 # list -sinkfile
  
        1      filestatus
        2      ALPHA
        3      BETA
        4      sink previous
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;logging-inputoutput-with-log&#34;&gt;Logging input/output with &lt;code&gt;$LOG&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The MTS command &lt;code&gt;$LOG&lt;/code&gt; will create a copy of input/output.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# log -logfile
# filestatus alp?
  ALPHA
# log off
  Logging of *MSINK* on -LOGFILE terminated.

# copy -logfile


&amp;gt; $Log Output: REL., Job#=40, Host=MT, 22:16:47 Sun Jun 28/14
&amp;gt; #filestatus alpha
&amp;gt;  ALPHA
&amp;gt; #log off
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;$DISPLAY LOGSTATUS&lt;/code&gt; command can be used to show what logging is currently in effect.&lt;/p&gt;
&lt;h2 id=&#34;logical-io-units&#34;&gt;Logical I/O units&lt;/h2&gt;
&lt;p&gt;Logical I/O units provide a way for a program to access files without having to specify them in advance. For example, a program may read from logical I/O unit &lt;code&gt;SCARDS&lt;/code&gt; which you could define to be a file called &lt;code&gt;ALPHA&lt;/code&gt; using &lt;code&gt;SCARDS=ALPHA&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is a common set of I/O units defined for each program, such as &lt;code&gt;SCARDS&lt;/code&gt; and also a free set of I/O units numbered 0-99. The common set of I/O units and their defaults are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SCARDS&lt;/code&gt; is used for input and defaults to &lt;code&gt;*SOURCE*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPRINT&lt;/code&gt; is used for printed output and defaults to &lt;code&gt;*PRINT*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPUNCH&lt;/code&gt; is used for output and defaults to &lt;code&gt;*PUNCH*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SERCOM&lt;/code&gt; and &lt;code&gt;GUSER&lt;/code&gt; default to &lt;code&gt;*MSOURCE*&lt;/code&gt; and &lt;code&gt;*MSINK*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The example below shows the &lt;code&gt;*SORT&lt;/code&gt; command being run with input coming from the file &lt;code&gt;ALPHA&lt;/code&gt; and output going to &lt;code&gt;*SINK*&lt;/code&gt;, ie the screen. The &lt;code&gt;PAR=&lt;/code&gt; part of the command line is the parameters for the sort rather than an I/O device; here &lt;code&gt;CH,A&lt;/code&gt; means sort by characters ascending,&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# list alpha
 
       1     Line B
       2     Line C
       3     Line A
# run *sort scards=alpha spunch=*sink* par=sort=ch,a
# Execution begins   22:41:56 
  Line A
  Line B
  Line C
  Statistics:  3 input/ 0* intermediate files
# Execution terminated   22:41:56  T=0.012 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol01-TheMichiganTerminalSystem-Nov1991.pdf&#34;&gt;MTS Volume 1&lt;/a&gt; or the online help topics such as &lt;code&gt;Pseudodevices&lt;/code&gt; and &lt;code&gt;LOG&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A tour of MTS commands</title>
      <link>https://try-mts.com/a-tour-of-mts-commands/</link>
      <pubDate>Thu, 03 Jul 2014 12:43:20 +0000</pubDate>
      <guid>https://try-mts.com/a-tour-of-mts-commands/</guid>
      <description>&lt;p&gt;Once how you&amp;rsquo;ve learnt how to &lt;a href=&#34;https://try-mts.com/working-with-files/&#34;&gt;work with files&lt;/a&gt; in MTS it&amp;rsquo;s easy to use the built in MTS commands - there are less than 50 commands and they follow the same basic syntax.&lt;/p&gt;
&lt;p&gt;This post will give a quick tour of the commands available in MTS. I&amp;rsquo;ve grouped them by topic and provided a very brief summary for each.&lt;/p&gt;
&lt;p&gt;As always, you can optionally start commands with &lt;code&gt;$&lt;/code&gt;, commands can be abbreviated, eg FILESTATUS to FI, and there are some command synonyms shown with &lt;code&gt;/&lt;/code&gt; below, such as HELP/EXPLAIN.&lt;/p&gt;
&lt;h2 id=&#34;general-commands&#34;&gt;General commands&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;COMMENT&lt;/code&gt; - ignored line, can also use &lt;code&gt;*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DISPLAY&lt;/code&gt; - show parameters and settings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HELP/EXPLAIN&lt;/code&gt; - interactive help system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MTS&lt;/code&gt; - return to MTS command mode from another program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SET&lt;/code&gt; - set MTS global options&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;SET&lt;/code&gt; and &lt;code&gt;DISPLAY&lt;/code&gt; respectively set and show a large number of different settings such as printer settings, passwords, how MTS displays help etc.&lt;/p&gt;
&lt;h2 id=&#34;file-commands&#34;&gt;File commands&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;COPY&lt;/code&gt; - copy lines to files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CREATE&lt;/code&gt; - create a new file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DESTROY&lt;/code&gt; - remove a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DUPLICATE&lt;/code&gt; - create a duplicate of a file, making a new file if needed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EDIT&lt;/code&gt; - edit a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EMPTY&lt;/code&gt; - remove contents of a file or set of files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FILEMENU&lt;/code&gt; - interactive selection of files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FILESTATUS&lt;/code&gt; - view information about files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LIST&lt;/code&gt; - display files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LOCK&lt;/code&gt; - lock a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LOCKSTATUS&lt;/code&gt; - display status of locks on files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PERMIT&lt;/code&gt; - grant access to files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RENAME&lt;/code&gt; - rename a file or set of files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RENUMBER&lt;/code&gt; - renumber a line file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TRUNCATE&lt;/code&gt; - remove unused space at end of file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNLOCK&lt;/code&gt; - unlock a file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A quick illustration of the difference between &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;DUPLICATE&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# create x
  File &amp;#34;X&amp;#34; has been created.
# copy *source* x
&amp;gt; Hello world
&amp;gt; Welcome to MTS
&amp;gt; Goodbye!
&amp;gt; $endfile
# list x
 
       1     Hello world
       2     Welcome to MTS
       3     Goodbye!
# create y
  File &amp;#34;Y&amp;#34; has been created.
# copy x(2,3) y
# list y
 
       1     Welcome to MTS
       2     Goodbye!
# duplicate x z
  File &amp;#34;X&amp;#34; has been duplicated as &amp;#34;Z&amp;#34;; size=1 page; access=Unlim Owner,
   None Others
# list z
 
       1     Hello world
       2     Welcome to MTS
       3     Goodbye!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;EMPTY&lt;/code&gt; effectively deletes a file and then recreates it with no contents. &lt;code&gt;TRUNCATE&lt;/code&gt; will remove space allocated to the file but not used - handy for when disk usage was paid for but less so on an emulated system.&lt;/p&gt;
&lt;h2 id=&#34;sessions&#34;&gt;Sessions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LOG&lt;/code&gt; - log session to a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SIGNOFF&lt;/code&gt; - log out of the system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SIGNON&lt;/code&gt; - log into the system&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;run-programs&#34;&gt;Run programs&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RERUN&lt;/code&gt; - reissue the previous RUN command, optionally changing parameters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUN&lt;/code&gt; - run a program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SINK&lt;/code&gt; - send output to a file/device&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SOURCE&lt;/code&gt; - take input from a file/device&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;devices-and-queues&#34;&gt;Devices and queues&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CONTROL&lt;/code&gt; - operations on files and devices, eg rewind a tape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CANCEL&lt;/code&gt; - stop batch or print jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MOUNT&lt;/code&gt; - mount tapes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RELEASE&lt;/code&gt; - stop using a pseudodevice like &lt;em&gt;tape&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;system-information&#34;&gt;System information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SYSTEMSTATUS&lt;/code&gt; - show system status, eg tasks or queues&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;utilities&#34;&gt;Utilities&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CALC&lt;/code&gt; - desktop calculator&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;programming&#34;&gt;Programming&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALTER/MODIFY&lt;/code&gt; - change contents of a register or shared memory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DEBUG&lt;/code&gt; - load a program and enter debug command mode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DUMP&lt;/code&gt; - print out register and CPU information&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IF&lt;/code&gt; - test a condition, usually for use in a batch file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LOAD&lt;/code&gt; - load a program into memory but don&amp;rsquo;t execute it yet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAKE&lt;/code&gt; - keep interrelated files in an consistent state&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RESTART/START&lt;/code&gt; - restart a program, eg after an error&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SDS&lt;/code&gt; - transfer control to debug mode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNLOAD&lt;/code&gt; - remove a previously loaded program from memory&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;other-commands-of-less-use-for-hercules-users&#34;&gt;Other commands of less use for Hercules users&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ACCOUNTING&lt;/code&gt; - view resources used and charges&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FSMESSAGE&lt;/code&gt; - full screen message/email system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FTP&lt;/code&gt; - file transfer protocol&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MESSAGE&lt;/code&gt; - line driven email system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NET&lt;/code&gt; - create a network connection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Networking on MTS needed special hardware, so the messaging and network system commands will not be of much use.&lt;/p&gt;
&lt;h2 id=&#34;further-information&#34;&gt;Further information&lt;/h2&gt;
&lt;p&gt;There is online help for each command which you can access directly eg &lt;code&gt;HELP CALC&lt;/code&gt;. Full documentation on how to use the commands and what command line parameters are available is available in &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/volumes/MTSVol01-TheMichiganTerminalSystem-Nov1991.pdf&#34;&gt;MTS Volume 1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In future posts I will go into more detail on some of the more complex or interesting commands.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Working with files</title>
      <link>https://try-mts.com/working-with-files/</link>
      <pubDate>Sun, 29 Jun 2014 02:53:22 +0000</pubDate>
      <guid>https://try-mts.com/working-with-files/</guid>
      <description>&lt;p&gt;Working with files in MTS can seem quite different compared to other systems. There are features missing that are considered as standard today - directories, automatic creation of new files - but there are also features in MTS that are more powerful than Unix or Windows.&lt;/p&gt;
&lt;p&gt;For example, the single command&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$copy A(2,10,2)+B(*l)@bkwd C
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;copies the even numbered lines from 2-10 from &lt;code&gt;A&lt;/code&gt; together with all lines from &lt;code&gt;B&lt;/code&gt; in reverse order and stores in &lt;code&gt;C&lt;/code&gt;. These operators apply to the file, not the command, so can be used on any MTS command line where they make sense.&lt;/p&gt;
&lt;h2 id=&#34;types-of-files&#34;&gt;Types of files&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Permanent files need to be created explicitly. Maximum name length is 12 characters and as in Unix a file extension is entirely optional. Permanent files are private to each user unless access is explicitly granted to another user. If access is granted you can use the notation &lt;code&gt;user:file&lt;/code&gt; to refer to the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Temporary files  must start with &lt;code&gt;-&lt;/code&gt; and names must be 8 characters or less. These do not need to be explicitly created but are automatically destroyed by the system when you log out or a batch job that creates them finishes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Public files must begin with a &lt;code&gt;*&lt;/code&gt; and their names must be 16 characters or less.  These are generally system wide programs or libraries.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It&amp;rsquo;s also possible to refer to a pseudodevice instead of a file. These start and end with &lt;code&gt;*&lt;/code&gt; - an example was seen in the up and running guide, &lt;code&gt;copy *source* file&lt;/code&gt;, which copies input from the keyboard to a file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The MTS documentation refers to FDnames  - this means either a filename or a pseudodevice name. (&lt;em&gt;Update&lt;/em&gt;: as Jeff notes in his comment below, FDnames also include devices and other modifiers such as line number ranges.)&lt;/p&gt;
&lt;h2 id=&#34;line-files&#34;&gt;Line files&lt;/h2&gt;
&lt;p&gt;By default files in MTS are line orientated - an individual line can be accessed directly rather than having to read from the start of the file. Each line can be up to 32k long. Line numbers can be negative and can contain up to three digits after the decimal point; the range allowed is -2147483.648 to 2147483.647.&lt;/p&gt;
&lt;p&gt;You can specify part of a file by using &lt;code&gt;(start, end, increment)&lt;/code&gt; after the file name, where each component is optional. You can use &lt;code&gt;*f&lt;/code&gt; to refer to the first line in the file and &lt;code&gt;*l&lt;/code&gt; for the last line. Some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A(5,10)&lt;/code&gt; - all lines between 5 and 10 inclusive from A&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A(5)&lt;/code&gt; - all lines from 5 onwards from A&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A(*f, 5)&lt;/code&gt; all lines from start of file to line 5 inclusive&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A(1,10,2)&lt;/code&gt; - lines 1, 3, 5, 7 and 9 from A&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;file-options&#34;&gt;File options&lt;/h2&gt;
&lt;p&gt;You can add extra options while processing files by using operators starting with &lt;code&gt;@&lt;/code&gt;. Some useful options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@uc&lt;/code&gt; converts the file to upper-case&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@trim&lt;/code&gt; removes trailing spaces&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@bkwd&lt;/code&gt; processes the file in reverse order&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;concatenation&#34;&gt;Concatenation&lt;/h2&gt;
&lt;p&gt;You can explicitly concatenate two or more files using the &lt;code&gt;+&lt;/code&gt; operator, eg &lt;code&gt;copy a+b c&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is also an implicit concatenation similar to the way the C language handles &lt;code&gt;#include&lt;/code&gt; preprocessor directives, but working on all types of file.&lt;/p&gt;
&lt;p&gt;This operation depends on the contents of the file. If a file contains a line such as &lt;code&gt;$continue with b&lt;/code&gt; then the contents of b are effectively inserted at that point and the rest of the current file is ignored. A line such as &lt;code&gt;$continue with b return&lt;/code&gt; will include all of file b at that point and then return to processing the rest of the current file.&lt;/p&gt;
&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;See the &amp;lsquo;Files &amp;amp; Devices&amp;rsquo; chapter in &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/Michigan_Terminal_System_Second_Edition_Vol1_Dec67.pdf&#34;&gt;MTS Volume 1&lt;/a&gt; for a full description.&lt;/p&gt;
&lt;p&gt;I will come back to the topic of pseudodevices in &lt;a href=&#34;https://try-mts.com/pseudodevices-and-redirection/&#34;&gt;a future post&lt;/a&gt;, which will also cover how to do redirection of file input and output.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using the 3270 terminal</title>
      <link>https://try-mts.com/using-the-3270-terminal/</link>
      <pubDate>Wed, 25 Jun 2014 13:06:20 +0000</pubDate>
      <guid>https://try-mts.com/using-the-3270-terminal/</guid>
      <description>&lt;p&gt;The 3270 terminal operates a little differently from command line interfaces you may be used to on Unix or Windows. This article will go over the basics of using the 3270 (via an emulator) and how the device is supported in MTS.&lt;/p&gt;
&lt;h2 id=&#34;line-mode&#34;&gt;Line mode&lt;/h2&gt;
&lt;p&gt;One of the most important differences is that normally the terminal operates in line mode - the host system will not receive any input until you send the line by pressing &lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A very small number of programs on MTS support binary mode, where each key is processed as it is pressed - an example is the editor in visual mode. (&lt;em&gt;Update&lt;/em&gt;: see Jeff Ogden&amp;rsquo;s comment below for a more precise description of how the terminal interacts with the operating system.)&lt;/p&gt;
&lt;h2 id=&#34;special-keys&#34;&gt;Special keys&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/3270-keys.png&#34; alt=&#34;3270 keys&#34;&gt;&lt;/p&gt;
&lt;p&gt;As well as the regular alphanumerical keys the 3270 has a number of special keys used by MTS. On an emulated terminal there is normally a way to bring up a keypad with these keys - above is the keypad on the x3270 emulator. Depending on the emulator there are also keyboard shortcuts, eg &lt;code&gt;Alt-C&lt;/code&gt; invokes &lt;code&gt;Clear&lt;/code&gt; on x3270.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PA1&lt;/code&gt; sends an attention interrupt that will normally make a program stop what it is doing, similar to &lt;code&gt;Ctrl-C&lt;/code&gt; on Unix/DOS.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PA2&lt;/code&gt; sends an end of file or cancel signal, similar to &lt;code&gt;Ctrl-D&lt;/code&gt; on Unix and &lt;code&gt;Ctrl-Z&lt;/code&gt; on DOS.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Clear&lt;/code&gt; will refresh the screen. This is especially useful when you disconnect a terminal session then reconnect.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;PF&lt;/code&gt; keys act as soft function keys&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;device-commands&#34;&gt;Device Commands&lt;/h2&gt;
&lt;p&gt;You can send special device commands to the display driver. These can normally be entered at any time, not just at the &lt;code&gt;#&lt;/code&gt; prompt. Device commands all start with a &lt;code&gt;%&lt;/code&gt; and can be abbreviated like MTS commands. Note that device commands will not appear in the terminal history after you press &lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%?&lt;/code&gt; will display the current terminal settings with output similar to the below.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    Roll mode   Rate=5   Duplex=on    Width=078   Timeout=off
    Dcc=&amp;#34;%&amp;#34;   Keyboard=lc    Hex=off &amp;#34;&amp;#39;&amp;#34;   Buffer=04    Tabs=off &amp;#34;?&amp;#34;    


    PA1 = attention interrupt             PA2 (CNCL) = end of file        
    CLEAR = refresh screen                SYS REQ = pause / continue      

 PF1 =%Wb=*                PF2 =$Copy *msource*@sp * PF3 =%?              
 PF4 =%Wf=*                PF5 =%Mode=page           PF6 =%Enter          
 PF7 =%Wf                  PF8 =%Mode=line           PF9 =%Mode=roll      
 PF10=%Wl                  PF11=%Wl=60               PF12=%Wr=60          
 PF13=%Wb=*                PF14=$Copy *msource*@sp * PF15=%?              
 PF16=%Wf=*                PF17=%Mode=page           PF18=%Enter          
 PF19=%Wf                  PF20=%Mode=line           PF21=%Mode=roll      
 PF22=%Wl                  PF23=%Wl=60               PF24=%Wr=60          
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;function-keys&#34;&gt;Function keys&lt;/h2&gt;
&lt;p&gt;The output of &lt;code&gt;%?&lt;/code&gt; will list the current function keys. You can redefine these with &lt;code&gt;%PFn cmd&lt;/code&gt;, so for example if you wanted &lt;code&gt;PF2&lt;/code&gt; to display file information using the &lt;code&gt;$filestatus&lt;/code&gt; command you could enter&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;%pf2 $filestatus
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that you do not need to press &lt;code&gt;Enter&lt;/code&gt; after using a function key.&lt;/p&gt;
&lt;h2 id=&#34;scrolling&#34;&gt;Scrolling&lt;/h2&gt;
&lt;p&gt;The mode determines how the screen will display output when there is more than one screen of text. Available modes area:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%roll&lt;/code&gt; where the screen will scroll up slowly when the bottom of the page is reached. This is the default mode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%line&lt;/code&gt; is similar to roll but the screen will be cleared, previous output moved up and further output starts from the middle.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%fast&lt;/code&gt; where the whole output is displayed quickly with no scrolling or pausing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%page&lt;/code&gt; where a page of output is displayed at a time. To move to the next page you press the &lt;code&gt;SysReq&lt;/code&gt; or &lt;code&gt;Attn&lt;/code&gt; key - however I have not got those keys to work, at least on x3270; instead I redefined PF3 to act as the pause/continue key using &lt;code&gt;%pf3 %pause&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can adjust the rate of scrolling with &lt;code&gt;%rate N&lt;/code&gt; where N is a number between 1 (slowest) and 9 (fastest), default being 5.&lt;/p&gt;
&lt;p&gt;You can scroll the display backwards and forwards using &lt;code&gt;%wb&lt;/code&gt; and &lt;code&gt;%wf&lt;/code&gt;; these are conveniently bound to &lt;code&gt;PF1&lt;/code&gt; and &lt;code&gt;PF4&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;other-useful-commands&#34;&gt;Other useful commands&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%activity&lt;/code&gt; will display the current task&amp;rsquo;s status and resource utilisation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%grab&lt;/code&gt; will start a second command session using the same terminal. You can then switch between the two sessions by using &lt;code&gt;%flip&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%quit&lt;/code&gt; will force a disconnect - useful if your session has crashed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%reset&lt;/code&gt; will return all device settings you have changed back to their defaults.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%timeout off&lt;/code&gt; will prevent your terminal from being logged out after 30 minutes of inactivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;more-information&#34;&gt;More information&lt;/h2&gt;
&lt;p&gt;See the 3270 chapter in &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/documentation#TOC-MTS-Volumes&#34;&gt;MTS Volume 4: Terminals and Networks in MTS&lt;/a&gt; on the MTS Archive for a full description of how to use the terminal and other device commands.&lt;/p&gt;
&lt;p&gt;Note that the operator&amp;rsquo;s console uses a different set of device commands; see the &lt;a href=&#34;https://docs.google.com/open?id=0B4t_NX-QeWDYYmVmNTNiMDUtN2ZlNS00ZDI2LTgzZTktNjBmODU4MTE3OGZm&#34;&gt;Operator&amp;rsquo;s Manual&lt;/a&gt; for more details.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using MTS</title>
      <link>https://try-mts.com/using-mts/</link>
      <pubDate>Wed, 25 Jun 2014 13:05:55 +0000</pubDate>
      <guid>https://try-mts.com/using-mts/</guid>
      <description>&lt;p&gt;Now that you have your MTS system &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;up and running&lt;/a&gt; it&amp;rsquo;s time to look at how to work with MTS as a regular user at the terminal or via batch.&lt;/p&gt;
&lt;h3 id=&#34;the-terminal&#34;&gt;The terminal&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/using-the-3270-terminal&#34;&gt;Using the 3270 terminal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;files-and-commands&#34;&gt;Files and commands&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/working-with-files/&#34;&gt;Working with files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/a-tour-of-mts-commands/&#34;&gt;A tour of MTS commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/pseudodevices-and-redirection/&#34;&gt;Pseudodevices and redirection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/editing-files/&#34;&gt;Editing file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/permit-me-to-talk-about-filestatus/&#34;&gt;File information and permissions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;inputoutput&#34;&gt;Input/output&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/printing-in-mts/&#34;&gt;Printing in MTS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/extracting-files-using-the-card-punch/&#34;&gt;Extracting files using the card punch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/submitting-batch-jobs-from-the-terminal/&#34;&gt;Submitting batch jobs from the terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/submitting-batch-jobs-from-the-reader/&#34;&gt;Submitting batch jobs from the reader&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;tapes&#34;&gt;Tapes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/restoring-files-from-tapes/&#34;&gt;Introduction and restoring files from tapes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/finding-mts-components-from-the-driver-file/&#34;&gt;Finding MTS components from the driver file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/making-your-own-tapes/&#34;&gt;Making your own tapes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/a-quicker-way-to-mount-tapes/&#34;&gt;A quicker way to mount tapes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Up and running 4 - Shutdown and where to next</title>
      <link>https://try-mts.com/up-and-running-4-shutdown-and-where-to-next/</link>
      <pubDate>Sun, 22 Jun 2014 03:04:10 +0000</pubDate>
      <guid>https://try-mts.com/up-and-running-4-shutdown-and-where-to-next/</guid>
      <description>&lt;p&gt;After you have finished &lt;a href=&#34;https://try-mts.com/up-and-running-3-using-mts&#34;&gt;using MTS&lt;/a&gt; you should shut the system down cleanly. The final part of this guide shows how to do this and what to do next.&lt;/p&gt;
&lt;h2 id=&#34;shutdown&#34;&gt;Shutdown&lt;/h2&gt;
&lt;p&gt;Switch back to the 3270 operator&amp;rsquo;s console (the picture below shows this on the right with the Hercules console on the left).&lt;/p&gt;
&lt;p&gt;First you need to stop the HASP batch system. Enter &lt;code&gt;$HOLD EX&lt;/code&gt; and then &lt;code&gt;$DRAIN SYSTEM&lt;/code&gt; to prevent new jobs running and complete those that are in progress. Output should look like the below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/shutdown1.png&#34; alt=&#34;shutdown1&#34;&gt;&lt;/p&gt;
&lt;p&gt;Next, get MTS to shutdown all terminals and operator jobs with &lt;code&gt;SHUTDOWN ALL&lt;/code&gt;. This will take a few seconds to run and will print a message when complete. Type &lt;code&gt;/t m&lt;/code&gt; to check if any tasks are still running; if there are not then it will report &lt;code&gt;specified jobs not found&lt;/code&gt; like the below. If it does report jobs running see &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq&#34;&gt;question 30 in the distribution FAQ&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/shutdown2.png&#34; alt=&#34;shutdown2&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now switch back to the Hercules console and type &lt;code&gt;stopall&lt;/code&gt; to stop the emulated CPUs and &lt;code&gt;quit&lt;/code&gt; to exit Hercules.&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-next&#34;&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;When you want to start MTS again follow the steps in &lt;a href=&#34;https://try-mts.com/up-and-running-2-booting-the-system/&#34;&gt;part 2&lt;/a&gt; of this guide.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://web.archive.org/web/20230206193130/http://archive.michigan-terminal-system.org/documentation/mts-d6-0-faq&#34;&gt;FAQ&lt;/a&gt; included with D6.0A has answers to many of the questions you will have when first running MTS, such as how to install more software or add a user ID.&lt;/p&gt;
&lt;p&gt;The original documentation included with D6.0 is also interesting&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;D6.0-MTS-DOC.txt&lt;/code&gt; gives a high level overview of MTS.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D6.0-LIST.txt&lt;/code&gt; has information about extra software and files on the distribution tape.
D6.0-LOG.txt.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D6.0-NEWSYS.txt&lt;/code&gt; contains instructions on how to set up MTS from scratch - aimed at original mainframe installations but section 13 is also useful for Hercules operation of MTS.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OperatorsManual-MTSD6.0-1988.pdf&lt;/code&gt; is the original operator&amp;rsquo;s manual used at UM - the sections on how to use the operator&amp;rsquo;s console will be useful and it&amp;rsquo;s also interesting to learn how it was operated as a production system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HASPManual-MTSD6.0-1988.pdf&lt;/code&gt; describes how to operate the HASP batch scheduling system in more detail.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&#34;https://web.archive.org/web/20230206200457/http://archive.michigan-terminal-system.org/documentation#TOC-MTS-Volumes&#34;&gt;MTS Volumes&lt;/a&gt; at the MTS Archive are the official documentation for MTS and are well worth a read. Note that some of these post-date the D6.0 distribution so there will be minor differences in operation. The following volumes are especially useful when starting out with MTS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MTS Volume 1: The Michigan Terminal System&lt;/li&gt;
&lt;li&gt;MTS Volume 2: Public File Descriptions&lt;/li&gt;
&lt;li&gt;MTS Volume 4: Terminals and Networks in MTS&lt;/li&gt;
&lt;li&gt;MTS Volume 18: The MTS File Editor&lt;/li&gt;
&lt;li&gt;MTS Volume 19: Magnetic Tapes&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-end&#34;&gt;The End&lt;/h2&gt;
&lt;p&gt;I hope this series of posts has sparked your interest in MTS and will lead to you exploring more. In future posts I will look at some &lt;a href=&#34;https://try-mts.com/using-mts/&#34;&gt;tips and tricks&lt;/a&gt; in getting MTS to bend to your whim.&lt;/p&gt;
&lt;p&gt;If you have any comments or problems getting MTS to work please leave a comment and I will try to help.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Up and running 3 - Using MTS</title>
      <link>https://try-mts.com/up-and-running-3-using-mts/</link>
      <pubDate>Fri, 20 Jun 2014 13:25:25 +0000</pubDate>
      <guid>https://try-mts.com/up-and-running-3-using-mts/</guid>
      <description>&lt;p&gt;Once you have &lt;a href=&#34;https://try-mts.com/up-and-running-2-booting-the-system&#34;&gt;booted the system&lt;/a&gt; you can now use MTS. This part of the guide will show you how to sign on as a user and the basics of interactive usage of MTS.&lt;/p&gt;
&lt;h2 id=&#34;sign-on-to-mts&#34;&gt;Sign on to MTS&lt;/h2&gt;
&lt;p&gt;Switch to the user 3270 console and enter &lt;code&gt;signon st01&lt;/code&gt;. User &lt;code&gt;st01&lt;/code&gt; is one of the regular user IDs set up as part of the distribution.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/signon1.png&#34; alt=&#34;signon1&#34;&gt;&lt;/p&gt;
&lt;p&gt;Enter the password at the next prompt; the password is the same as the user name &lt;code&gt;st01&lt;/code&gt;. You can ignore the message about the previous login attempt as this appears to be a side-effect of the distribution..&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/signon2.png&#34; alt=&#34;signon2&#34;&gt;&lt;/p&gt;
&lt;p&gt;The distribution comes with a set of regular user IDs, &lt;code&gt;st01&lt;/code&gt; to &lt;code&gt;st09&lt;/code&gt;, password the same as the user name, and shared IDs with greater privileges such as &lt;code&gt;mts&lt;/code&gt; or &lt;code&gt;acc&lt;/code&gt;. For shared IDs you login with the ID eg &lt;code&gt;mts&lt;/code&gt;, password &lt;code&gt;aardvarks&lt;/code&gt;; the system will then prompt for a secondary sign-on with &lt;code&gt;ID?&lt;/code&gt; to which you should enter  one of the regular IDs such as &lt;code&gt;st01&lt;/code&gt; and its password at &lt;code&gt;PW?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/signon3.png&#34; alt=&#34;signon3&#34;&gt;&lt;/p&gt;
&lt;p&gt;Privileged IDs can get direct access to hardware and system files. For this session we do not need this so just sign on as &lt;code&gt;st01&lt;/code&gt; for now.&lt;/p&gt;
&lt;h2 id=&#34;mts-commands&#34;&gt;MTS commands&lt;/h2&gt;
&lt;p&gt;For this first session, let&amp;rsquo;s look at some simple commands to put text in a file and display it on the screen.&lt;/p&gt;
&lt;p&gt;Some important differences between files on MTS and other systems such as Windows or Linux:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need to create a regular file explicitly with the &lt;code&gt;$create&lt;/code&gt; command before using it.&lt;/li&gt;
&lt;li&gt;Files are by default structured as a series of numbered lines rather than containing an arbitrary sequence of characters&lt;/li&gt;
&lt;li&gt;There is no concept of directories in MTS. Each user effectively only has a single place to put files. It is possible to grant access to your files so other users can access them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So first let&amp;rsquo;s create a file called &lt;code&gt;alpha&lt;/code&gt;. At the &lt;code&gt;#&lt;/code&gt; prompt enter &lt;code&gt;$create alpha&lt;/code&gt;; the output should be as below.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $create alpha
  File &amp;#34;ALPHA&amp;#34; has been created.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that filenames and commands are not case sensitive; you could have also typed &lt;code&gt;$CREATE Alpha&lt;/code&gt;. You can also leave off the &lt;code&gt;$&lt;/code&gt; when typing MTS commands&lt;/p&gt;
&lt;p&gt;Next, let&amp;rsquo;s put some text into the file. To do this, rather than using a text editor, we will just take input from the keyboard and copy it to a file. To do this we can use the &lt;code&gt;$copy&lt;/code&gt; command with &lt;code&gt;*source*&lt;/code&gt; as the first parameter; &lt;code&gt;*source*&lt;/code&gt; is a pseudo-device representing the keyboard in interactive sessions.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $copy *source* alpha
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that the prompt now changes to &lt;code&gt;&amp;gt;&lt;/code&gt; to denote that the system is taking keyboard input inside a command. Enter some lines of text and end by typing &lt;code&gt;$endfile&lt;/code&gt; which acts a special marker to tell MTS to stop taking input.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; Hello World!
&amp;gt; Welcome to MTS
&amp;gt; $endfile
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let&amp;rsquo;s display the file you have created using the &lt;code&gt;$list&lt;/code&gt; command. Note that the line numbers are displayed.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $list alpha
 
       1     Hello World!
       2     Welcome to MTS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To show information about your files, similar to &lt;code&gt;dir&lt;/code&gt; on Windows or &lt;code&gt;ls&lt;/code&gt; on Unix, use the &lt;code&gt;filestatus&lt;/code&gt; command. As this is a lot to type, we can use the facility in MTS that allows you to abbreviate command names - in this case we can type &lt;code&gt;fi&lt;/code&gt; instead of &lt;code&gt;filestatus&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# fi alpha
  ALPHA             Type=Line, IdleDays=36525, RPM=0, UseCnt=3, Owner=ST01, 
   Create=Jun17/14, LastRef=Jun17/14, LastCatalog=Jun17/14, 
   LastData=09:38:32 Jun17/14, FullAccess=Unlim Owner, None Others 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Typing &lt;code&gt;fi&lt;/code&gt; or &lt;code&gt;filestatus&lt;/code&gt; without a filename would list all the files under the user ID.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s remove the file using the &lt;code&gt;$destroy&lt;/code&gt; command. MTS will prompt for confirmation.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# $destroy alpha
  File &amp;#34;ALPHA&amp;#34; is to be destroyed.  Please confirm:
? yes
  Done.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Your session so far should look similar to the below screenshot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/using-mts.png&#34; alt=&#34;Using MTS&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;getting-help&#34;&gt;Getting help&lt;/h2&gt;
&lt;p&gt;MTS has a comprehensive built-in help system - type &lt;code&gt;$help&lt;/code&gt; to access.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/help.png&#34; alt=&#34;help&#34;&gt;&lt;/p&gt;
&lt;p&gt;Use the numeric options at the bottom of the screen to navigate between pages - so in this case to look at MTS commands you would enter &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The 3270 terminal special key &lt;code&gt;PA2&lt;/code&gt; can be used to learn more about the help system itself. If an entry has more than one page you can use the &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt; keys (followed by the &lt;code&gt;Enter&lt;/code&gt; key) to navigate. Use the 3270 terminal &lt;code&gt;PA1&lt;/code&gt; key to exit.&lt;/p&gt;
&lt;h2 id=&#34;running-other-commands&#34;&gt;Running other commands&lt;/h2&gt;
&lt;p&gt;Apart from the built-in MTS commands, such as &lt;code&gt;$destroy&lt;/code&gt; or &lt;code&gt;$copy&lt;/code&gt; you can run other programs on the system. Many of these programs are available as public file IDs which start with a &lt;code&gt;*&lt;/code&gt;. To run these you use the MTS &lt;code&gt;$run&lt;/code&gt; command followed by the program. Below we show a simple heads-or-tails guessing program with the public file name &lt;code&gt;*coinflip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/coinflip-1.png&#34; alt=&#34;coinflip&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;signing-off&#34;&gt;Signing off&lt;/h2&gt;
&lt;p&gt;When you&amp;rsquo;ve finished using MTS type &lt;code&gt;$signoff&lt;/code&gt;. MTS will print a summary of the CPU, disk and other resources used during the session before returning to the sign-on screen. Note that when run on a real mainframe there would be a charge for all resources used which would make you careful about what you consumed - but on your own PC everything is free.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/signoff.png&#34; alt=&#34;signoff&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;You can sign on and try more commands; you can also start other 3270 terminals and sign on.&lt;/p&gt;
&lt;p&gt;Move on to &lt;a href=&#34;https://try-mts.com/up-and-running-4-shutdown-and-where-to-next/&#34;&gt;part 4&lt;/a&gt; of this guide to learn how to shut down MTS safely.&lt;/p&gt;
&lt;h2 id=&#34;more-information&#34;&gt;More information&lt;/h2&gt;
&lt;p&gt;The chapter &amp;lsquo;A Brief Overview of MTS&amp;rsquo; in Volume 1 of the MTS documentation on &lt;a href=&#34;http://www.bitsavers.org/pdf/univOfMichigan/mts/Michigan_Terminal_System_Second_Edition_Vol1_Dec67.pdf&#34;&gt;Bitsavers&lt;/a&gt; is a good short tour of MTS and using the system.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Up and running 2 - Booting the system</title>
      <link>https://try-mts.com/up-and-running-2-booting-the-system/</link>
      <pubDate>Wed, 18 Jun 2014 12:56:43 +0000</pubDate>
      <guid>https://try-mts.com/up-and-running-2-booting-the-system/</guid>
      <description>&lt;p&gt;OK, you&amp;rsquo;ve got the software and files installed in &lt;a href=&#34;https://try-mts.com/up-and-running-1-installation&#34;&gt;part 1&lt;/a&gt;. You can now boot the system for the first time.&lt;/p&gt;
&lt;h2 id=&#34;start-hercules-and-a-3270-console&#34;&gt;Start Hercules and a 3270 console&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Go to the &lt;code&gt;d6.0A&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Start Hercules&lt;/li&gt;
&lt;li&gt;The Hercules console should appear and show it has recognised the tapes and disks mentioned in the config file&lt;/li&gt;
&lt;li&gt;Start a 3270 terminal emulator and connect to &lt;code&gt;localhost:3270&lt;/code&gt;. This will act as the MTS operator&amp;rsquo;s console. It will display the Hercules logo to show it is connected to Hercules but no operating system is running on the emulator.&lt;/li&gt;
&lt;li&gt;The screen should now look something like the below; here I have the Hercules console on the left and the operator&amp;rsquo;s console on the right.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/startup.png&#34; alt=&#34;startup&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;boot-from-the-emulated-disk&#34;&gt;Boot from the emulated disk&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;From the Hercules console, issue the command &lt;code&gt;ipl 260&lt;/code&gt;. IPL stands for Initial Program Load and will tell the emulator to boot from the disk known as device 260.&lt;/li&gt;
&lt;li&gt;If everything is working correctly, a message should appear &lt;em&gt;on the 3270 terminal&lt;/em&gt; asking if you want to load the system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/boot.png&#34; alt=&#34;boot&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;start-the-boot-process&#34;&gt;Start the boot process&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Switch over to the 3270 operator&amp;rsquo;s console - all commands from this point on should be entered there rather than the Hercules console unless otherwise specified.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;yes&lt;/code&gt; to the prompt on the 3270 console&lt;/li&gt;
&lt;li&gt;A message may appear warning about the time difference since the last boot. Enter &lt;code&gt;ok&lt;/code&gt; to accept and continue.&lt;/li&gt;
&lt;li&gt;You will be prompted to enter your initials and a reason for booting - you can put anything here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/boot2-1.png&#34; alt=&#34;boot step 2&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More messages will appear as the system boots. Eventually it will stop and ask you to check offline devices with this message:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00007 MTS ** Just cancel after you have checked the offline devices.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/boot3.png&#34; alt=&#34;boot3&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is expected as you do not have (or need) all the disk drives on the system specified in the distribution. To cancel, press the &lt;code&gt;PA2&lt;/code&gt; key on the 3270 operator&amp;rsquo;s console. Where to find this depends on the emulator - on tn3270 text terminals it may be &lt;code&gt;Control-A&lt;/code&gt; followed by &lt;code&gt;2&lt;/code&gt;; on x3270 terminals try &lt;code&gt;Alt-2&lt;/code&gt; or use the keypad available by pressing the toolbar button on the top right of the terminal.&lt;/li&gt;
&lt;li&gt;If that worked, the following message will appear:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*** Cancelled ***
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Wait for the boot process to complete - the final message should be&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;00019 MTS PEEK initialization complete
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;start-the-batch-scheduler-and-user-terminals&#34;&gt;Start the batch scheduler and user terminals&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;On the 3270 operator&amp;rsquo;s console, type &lt;code&gt;HASP&lt;/code&gt; to start the batch scheduler and then &lt;code&gt;MTS *HSP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start a second 3270 terminal and connect to &lt;code&gt;localhost:3270&lt;/code&gt;. This will act as the user terminal.&lt;/li&gt;
&lt;li&gt;On the operator console, type &lt;code&gt;MTS *LAS&lt;/code&gt; to activate the terminal.&lt;/li&gt;
&lt;li&gt;The screen should now look like the below; I have the user terminal to the left of the operator console.
&lt;img src=&#34;https://try-mts.com/images/2014/Jun/terminal-up.png&#34; alt=&#34;terminal up&#34;&gt;&lt;/li&gt;
&lt;li&gt;On the operator console, type &lt;code&gt;$RELEASE EX&lt;/code&gt; to allow the batch scheduler to start jobs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ready-for-use&#34;&gt;Ready for use&lt;/h2&gt;
&lt;p&gt;Congratulations, you now have a running MTS system! Move on to the &lt;a href=&#34;https://try-mts.com/up-and-running-3-using-mts/&#34;&gt;next section&lt;/a&gt; for a brief tour of how to use MTS.&lt;/p&gt;
&lt;h2 id=&#34;more-information&#34;&gt;More information&lt;/h2&gt;
&lt;p&gt;Take a look at the file &lt;code&gt;README-D6.0A.txt&lt;/code&gt; in the D6.0A distribution if you have problems during the boot process or want to learn more about what it is doing.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also written a more detailed look at the start-up process in &lt;a href=&#34;https://try-mts.com/system-start-up-in-more-detail/&#34;&gt;this post&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Up and running 1 - Installation</title>
      <link>https://try-mts.com/up-and-running-1-installation/</link>
      <pubDate>Mon, 16 Jun 2014 13:44:25 +0000</pubDate>
      <guid>https://try-mts.com/up-and-running-1-installation/</guid>
      <description>&lt;p&gt;This part of the guide will show you what programs you need to install and what files to download to get your MTS system running.&lt;/p&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;p&gt;You will need to install the Hercules S/370 emulator and a 3270-compatible terminal emulator.&lt;/p&gt;
&lt;h3 id=&#34;hercules&#34;&gt;Hercules&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://en.wikipedia.org/wiki/File:Hercules-logo.gif#mediaviewer/File:Hercules-logo.gif&#34;&gt;&lt;img alt=&#34;Hercules-logo.gif&#34; src=&#34;http://upload.wikimedia.org/wikipedia/en/a/a1/Hercules-logo.gif&#34; height=&#34;145&#34; width=&#34;145&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://en.wikipedia.org/wiki/Hercules_emulator&#34;&gt;Hercules S/370 emulator&lt;/a&gt; is needed to run MTS. Go to the &lt;a href=&#34;http://www.hercules-390.eu/&#34;&gt;Hercules web site&lt;/a&gt; to download the latest version - source code is available for Unix derivatives and pre-built binaries for Windows.&lt;/p&gt;
&lt;p&gt;I would recommend you get the latest version (currently 3.12) as previous versions have an issue that prevent batch printing working correctly. Hercules 4 will also work: see &lt;a href=&#34;https://try-mts.com/some-useful-hercules-features/&#34;&gt;this discussion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Prepackaged versions are available for Linux, eg Debian and Ubuntu, but these tend to be older versions so best to get the source code and compile it yourself on these platforms.&lt;/p&gt;
&lt;p&gt;Follow the &lt;a href=&#34;http://www.hercules-390.eu/hercinst.html#install&#34;&gt;installation procedure&lt;/a&gt; to get the source or binaries installed. You do not to follow the configuration or DASD creation procedures using this guide.&lt;/p&gt;
&lt;h2 id=&#34;3270-terminal-emulator&#34;&gt;3270 terminal emulator&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://try-mts.com/images/2014/Jun/IBM_3277_Display.jpg&#34; alt=&#34;IBM 3270 - http://commons.wikimedia.org/wiki/File:IBM_3277_Display.jpg&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;&lt;sub&gt;From &#34;&lt;a href=&#34;http://commons.wikimedia.org/wiki/File:IBM_3277_Display.jpg#mediaviewer/File:IBM_3277_Display.jpg&#34;&gt;IBM 3277 Display&lt;/a&gt;&#34; by Gavin Eadie - &lt;a rel=&#34;nofollow&#34; class=&#34;external text&#34; href=&#34;http://picasaweb.google.com/lh/photo/Yuh79IMWKAono3Pe08-1Cg&#34;&gt;picasaweb.google.com&lt;/a&gt;. Licensed under &lt;a title=&#34;Creative Commons Attribution-Share Alike 1.0&#34; href=&#34;http://creativecommons.org/licenses/by-sa/1.0&#34;&gt;CC BY-SA 1.0&lt;/a&gt; via &lt;a href=&#34;//commons.wikimedia.org/wiki/&#34;&gt;Wikimedia Commons&lt;/a&gt;.&lt;/sub&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://en.wikipedia.org/wiki/IBM_3270&#34;&gt;IBM 3270 terminal&lt;/a&gt; acts as the operator&amp;rsquo;s console and for user sign-on sessions. Unless you have an old one lying around, you will need to install an emulator that can connect to Hercules.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/discussions/mts-today/3270terminalemulators&#34;&gt;MTS archive&lt;/a&gt; has a good guide on what emulators are available - pick one for your platform. To use MTS effectively you will need to be able to use special keys like &lt;code&gt;PA1&lt;/code&gt; - see the documentation for your emulator to find out how to do this.&lt;/p&gt;
&lt;h2 id=&#34;files&#34;&gt;Files&lt;/h2&gt;
&lt;p&gt;You will need the D6.0A MTS distribution, the virtual tape files from the D6.0 distribution and the up-to-date Hercules configuration file.&lt;/p&gt;
&lt;h3 id=&#34;mts-d60a-distribution&#34;&gt;MTS D6.0A distribution&lt;/h3&gt;
&lt;p&gt;MTS was distributed as sets of tapes; the latest version was D6.0 from 1988. D6.0A is an updated, packaged, version of D6.0 that includes bug fixes and minor changes to get it running smoothly on Hercules. Work is underway to get a bootable version of MTS from the time it was shut down in the 1990s, but for now D6.0A is the latest available.&lt;/p&gt;
&lt;p&gt;The best source at present for this is a collection at &lt;a href=&#34;https://archive.org/details/michigan-terminal-system-distribution-documentation-and-files&#34;&gt;archive.org&lt;/a&gt;. You want the file &lt;code&gt;d6.0A.zip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unzip the file and change directory to the &lt;code&gt;mts/d6.0A&lt;/code&gt; directory.&lt;/p&gt;
&lt;h3 id=&#34;mts-d60-tape-files&#34;&gt;MTS D6.0 tape files&lt;/h3&gt;
&lt;p&gt;The D6.0A distribution only contains enough to get a bootable version of MTS; to install other software from the distribution you will need the tape files from D6.0. Get this from &lt;a href=&#34;http://bitsavers.org/bits/univOfMichigan/mts/d6.0.tar.gz&#34;&gt;Bitsavers&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Unpack this somewhere else and copy &lt;code&gt;mts/d6.0/*.aws&lt;/code&gt; to your &lt;code&gt;mts/d6.0A/Tapes&lt;/code&gt; directory created in the previous step.&lt;/p&gt;
&lt;p&gt;Note that the archives were created on a Mac, so if you are not on that platform you will see directories like &lt;code&gt;__MACOSX&lt;/code&gt; and files starting with &lt;code&gt;.&lt;/code&gt;. You can safely ignore or delete these.&lt;/p&gt;
&lt;h3 id=&#34;updated-hercules-config-file&#34;&gt;Updated Hercules config file&lt;/h3&gt;
&lt;p&gt;The version of the Hercules configuration file in D6.0A has a minor bug; pick up the latest version of the file fixing this from the &lt;a href=&#34;https://web.archive.org/web/20230209063859/https://sites.google.com/site/michiganterminalsystem/mts-d60A#TOC-Download&#34;&gt;MTS archive&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This file is enough to get MTS started but there are many options that can be tweaked. The only change I&amp;rsquo;d recommend to make now is to enable printer support if you have Hercules 3.10 or later; search for PTR2, uncomment the first line and comment out the next two lines with &lt;code&gt;#&lt;/code&gt; as below:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;000E 1403   Units/PTR2.txt lpp=66 fcb=1:11,4:1,5:5,8:10,14:6,19:4,24:7,34:2,44:6,49:4,54:7,63:8,66:3 optprint # PTR2
#000E 1403  Units/PTR2.txt nofcbcheck  # PTR2
#000E 1403  Units/PTR2.txt             # PTR2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copy this file to &lt;code&gt;mts/d6.0A&lt;/code&gt;, overwriting the previous version of &lt;code&gt;hercules.conf&lt;/code&gt;, eg:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cp /path/to/MTSD6.0A.cnf hercules.cnf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Both D6.0 and D6.0A include a directory &lt;code&gt;mtsDistDocs&lt;/code&gt; containing useful information which you should save elsewhere to read later.&lt;/p&gt;
&lt;h2 id=&#34;ready-to-boot&#34;&gt;Ready to boot&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;ve completed the above steps, the contents of your d6.0A directory should have at least the following files:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;hercules.cnf
Disks/mts600.dsk
Disks/mts600_1.dsk
Tapes/cmd001.aws
Tapes/d6.0dr1.aws
Tapes/d6.0dr2.aws
Tapes/d6.0dr3.aws
Tapes/d6.0t1.aws
Tapes/d6.0t2.aws
Tapes/d6.0t3.aws
Tapes/d6.0t4.aws
Tapes/d6.0t5.aws
Tapes/d6.0t6.aws
Tapes/d6.0util.aws
Units/PCH1.txt
Units/PTR2.txt
Units/RDR1.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If so, you are ready to move on to the next stage - &lt;a href=&#34;https://try-mts.com/up-and-running-2-booting-the-system/&#34;&gt;booting the system for the first time&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Get up and running with MTS</title>
      <link>https://try-mts.com/get-up-and-running-with-mts/</link>
      <pubDate>Mon, 16 Jun 2014 13:42:52 +0000</pubDate>
      <guid>https://try-mts.com/get-up-and-running-with-mts/</guid>
      <description>&lt;p&gt;OK, this will be a four part guide to getting MTS installed and how to start to use the system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/up-and-running-1-installation&#34;&gt;Part 1 - Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/up-and-running-2-booting-the-system/&#34;&gt;Part 2 - Booting the system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/up-and-running-3-using-mts/&#34;&gt;Part 3 - Using MTS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/up-and-running-4-shutdown-and-where-to-next/&#34;&gt;Part 4 - Shutdown and where to go next&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The aim is not to explain in detail what each step does, but I will provide links to further information to learn more.&lt;/p&gt;
&lt;p&gt;An alternative way to get started quickly with MTS is to use Buddy Bell&amp;rsquo;s &lt;a href=&#34;https://www.vintagebigblue.org/MTSr6.0A/MTS3270/mts3270.php&#34;&gt;website&lt;/a&gt; which has an emulated terminal logged in to a student account.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why try MTS?</title>
      <link>https://try-mts.com/why-try-mts/</link>
      <pubDate>Sun, 15 Jun 2014 05:51:53 +0000</pubDate>
      <guid>https://try-mts.com/why-try-mts/</guid>
      <description>&lt;p&gt;MTS, the Michigan Terminal System, is an operating system running on IBM System/360 compatible mainframes dating from the 1960s. It ran at a number of universities in the US, UK and Canada until the late 90s, It provided batch and interactive access with a large number of programs and tools included in the standard distribution.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s now possible to run MTS without having your own mainframe, thanks to two projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Hercules S/390 emulator (&lt;a href=&#34;http://www.hercules-390.eu/&#34;&gt;http://www.hercules-390.eu/&lt;/a&gt;) runs on Linux, Windows and Mac and simulates the hardware and peripherals such as disks and tapes needed to run MTS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The complete source code, object code and a wealth of documentation is freely available under the Creative Commons Attribution 3.0 license (CC BY 3.0).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;so-why-should-you-try-mts-today&#34;&gt;So why should you try MTS today?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Learn how mainframes were operated and the experience of being a user on a large shared system with limited resources.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Experiment with some of the programming languages available in the distribution - eg Algol. APL, BCPL, MAD, Plus, PL/1, SNOBOL.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;History is often neglected in computer science - by learning more about the past we get a better understanding of why things are like they are today. Compared to today, where there is only really Windows and Unix derivatives (Linux, BSD, Mac), back in the 60s and 70s there was an explosion of new operating systems and ideas and it&amp;rsquo;s important to get a flavour of what was going on then.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;learning-more-about-mts&#34;&gt;Learning more about MTS&lt;/h2&gt;
&lt;p&gt;First read the &lt;a href=&#34;http://en.wikipedia.org/wiki/Michigan_Terminal_System&#34;&gt;Wikipedia article&lt;/a&gt; and items linked from there.&lt;/p&gt;
&lt;p&gt;Next go to the &lt;a href=&#34;https://web.archive.org/web/20230206200457/http://archive.michigan-terminal-system.org/documentation&#34;&gt;MTS Archive&lt;/a&gt; - this site has done an amazing job of finding and uploading manuals, tutorials, memos and newsletters about MTS. I had never used MTS before this year and the information on this site was sufficient to understand the system and get it running. The original site archive.michigan-terminal-system.org, is currently down so the above link points to a copy at archive.org.&lt;/p&gt;
&lt;p&gt;Then grab a copy of &lt;a href=&#34;http://www.hercules-390.eu/&#34;&gt;Hercules&lt;/a&gt;, a &lt;a href=&#34;https://web.archive.org/web/20201010050608/https://sites.google.com/site/michiganterminalsystem/discussions/mts-today/3270terminalemulators&#34;&gt;3270 terminal emulator&lt;/a&gt; and the &lt;a href=&#34;https://archive.org/details/michigan-terminal-system-distribution-documentation-and-files&#34;&gt;D6.0A distribution&lt;/a&gt; of MTS and get it working on your PC.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;Up and Running&lt;/a&gt; guide on this blog goes through the process in more detail.&lt;/p&gt;
&lt;h2 id=&#34;this-blog&#34;&gt;This blog&lt;/h2&gt;
&lt;p&gt;In this blog I will provide tips and tricks for using MTS and share what I have discovered running the system. There are four main sections.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/get-up-and-running-with-mts/&#34;&gt;Up and Running&lt;/a&gt; - how to install and run MTS for the first time.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/using-mts/&#34;&gt;Using MTS&lt;/a&gt; - a tour of MTS commands, input/output and using tapes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/operator/&#34;&gt;MTS System Admin&lt;/a&gt; - a closer look at configuring MTS and its services.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try-mts.com/programming-languages-in-mts/&#34;&gt;Programming Languages&lt;/a&gt; - a review at many of the programming languages installed with MTS, including language features and creating simple programs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;d love to have your feedback, so if you have any suggestions, questions or corrections please leave a comment or &lt;a href=&#34;mailto:rupert@try-mts.com&#34;&gt;email me&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
