diff --git a/doc/_info_/char.sail b/doc/_info_/char.sail new file mode 100755 index 00000000..75607177 --- /dev/null +++ b/doc/_info_/char.sail @@ -0,0 +1,45 @@ +Stanford character set. +These are the exceptions to standard ASCII. +5.10.72 z = 749 + + +^A downarrow +^B alpha +^C beta +^D carat (logical and) +^E not +^F epsilon +^G pi +^H lambda +^I tab +^J line feed +^K vertical tab +^L form feed +^M return +^N infinity (two dots) +^O del +^P subset of +^Q contains +^R intersect +^S union +^T for all +^U there exists +^V exclusive or (plus with a circle around it) +^W equivalence (two way arrow) +^X underbar (note that backarrow is like old ascii and =/ ^X) +^Y forward arrow +^Z end of file +^[ not equal (=/) +^\ less than or equal +^] greater than or equal +^^ equivalence (three bars) +^_ down-carat (logical or) + + +^ is up-arrow, not carat +_ is backarrow, not underbar + + +} is altmode, not right brace +~ is right brace, not 'not' +  \ No newline at end of file diff --git a/doc/_info_/pcode.info b/doc/_info_/pcode.info new file mode 100755 index 00000000..bdda0396 --- /dev/null +++ b/doc/_info_/pcode.info @@ -0,0 +1,88 @@ + + 3. DISPLAY-MODE CHANNELS. ^P-CODES. + +It would be impossible to output a display command using a channel +which simply printed any character sent out on it. If it is desired +to make use of display commands, a channel may be opened in display +mode. That causes the character "^P" to be an escape character when +output on that channel. When a "^P" is output on a display-mode +channel, it has no effect except for setting bits causing the next +character output on that channel to be interpreted as a display code. +The display codes are: + +A Advance to a fresh line. If at beginning of line do nothing, + else act like a CRLF. +B move Backward. Decrements the horizontal position. + If the horizontal position is 0, it is set to the horizontal + screen size minus two, and the vertical position is decreased + by one. Thus, it moves back over one character position even + if there was a line continuation in the way. +C Clear screen. The cursor goes to the upper left corner and + the whole screen is cleared. On a printing tty (%TOMVU is 0) + a CRLF is output, since "clearing the screen" is undefined. + If "^PC" is output to the echo area, only the echo area is + cleared, and only the echo area cursor is moved. +D move Down. The vertical position is incremented. If it becomes + equal to the vertical screen size, it is set to 0. +E clear to Eof. The remainder of the current line, and all lower + lines, are erased. The cursor doesn't move. +F move Forward. The horizontal position is incremented. At the + end of the line it wraps around to the beginning of the + same line. +H set Horizontal position. Takes one argument: the following + character should be the desired horizontal position setting + plus 8. The vertical position is not changed. An attempt to + set the position beyond a margin will position it at the + margin. +K Kill (erase) the character the cursor points at. The cursor + does not move. The position erased is the one that would be + written in next by ordinary output. +L clear Line. The current line, starting with the position that + the cursor points at, is erased. The cursor does not move. +M (More) hang up until a character is read in. See F.3. +N (No home-up) similar, but don't home up after the character is + read. +P output "^P". Useful if it is desired to output "^P" in image + mode with a display-mode channel, or if the terminal handles + the SAIL character set. +Q output "^C". Similar to "^PP". This is needed only for block + mode IOT's. With SIOT, it is not needed since "^C" isn't special. +R Restore cursor position. The cursor is set to the most + recently saved value. +S Save cursor position. Remembers the current cursor position + for use with "^PR". There is no stack for saved cursor + positions; only the most recent saved position is remembered. +T go to Top of screen (home up). The cursor is positioned at the + upper left corner of the screen. +U move Up. The vertical position is decremented. If it was 0, + it becomes equal to the vertical size minus one. +V set Vertical position. Takes the following character as an + argument - it should be 8 plus the desired vertical position. + An attempt to set the cursor beyond the top or bottom margin + will position it as far as allowed in the desired direction. + Similarly, "^PV" will not move the echo area cursor outside + the echo area. Note that vertical positions in the echo area + are to be specified relative to the top of the echo area. +X backspace and erase one character ("^PB" followed by "^PK"). + If done at column zero, it clears the last TWO characters on + the previous line (assuming that one of them was the + character you intended to erase, and the other was an "!" + indicating a continuation). +Z home down. The cursor is positioned at the lower left corner. +[ insert line. The current line and all lines below it are + pushed down one slot. A blank line appears at the current + position. The cursor does not move. Works only on terminals + which have %TOLID set (^P\ also). +\ delete line. The current line disappears, and all teh lines + below it move up by one position. The bottom line becomes + blank. The cursor does not move. +] obsolete - same as "^PL". +^ insert character. All the characters after the cursor move + right one position. The last one moves off the right margin + and disappears. A space appears at the cursor, which does not + move. Works only on terminals which have %TOCID set. +_ delete character. The character at the cursor disappears, and + all the rest of the characters on the line move left one + position. A space appears at the end of the line. The cursor + does not move. + \ No newline at end of file diff --git a/doc/info/lib.28 b/doc/info/lib.28 new file mode 100755 index 00000000..ef7838e6 --- /dev/null +++ b/doc/info/lib.28 @@ -0,0 +1,998 @@ +-*-Text-*- Documentation of MIDAS .INSRT libraries. + +File: LIB Node: Top Up: (DIR) + +.INSRT'able Subroutine Libraries for MIDAS Programs + +ITS now has several libraries of commonly useful subroutines +for MIDAS programs. This file tells how to use them and +what the existing libraries do. + +* Menu: + +* General:: What libraries expect from their callers + +These libraries exist now + +* RFN:: Reading and printing filenames +* DATIME:: Reading, printing and converting dates and times +* NETWRK:: Accessing the ARPANET +* LSRTNS:: Accessing the INQUIR data base +* FORMAT: (SYSENG;FORMAT) Output made easy. Like Lisp's FORMAT function. +* MSGS: (SYSENG;MSGS) Accessing the :MSGS date data base +* BYELIB: (SYSENG;BYELIB) Obtaining epigrams a la BYE + +File: LIB, Node: General, Up: Top, Previous: Top, Next: RFN + +The Calling Conventions of the .INSRT'able Libraries + + MIDAS libraries are designed to be requested with a .INSRT, because +of the fact that MIDAS programs are usually absolute assemblies. +A library named FOO resides (unless otherwise stated) in the file +SYSENG;FOO > on all ITS machines, and can be requested with +.INSRT SYSENG;FOO + + Every library clothes itself in a MIDAS symbol block named after the +library. Almost all of the symbols of the library live within this +block, and thus cannot interfere with the calling program. Some +libraries define their normal entry points in the outer block, while +others define NO symbols except in their own block. Routines in +libraries of the latter sort must be referred to in MIDAS with an +explicit block name, as in RFN"PFN for the PFN routine in the library +RFN. + + Because libraries are included in the assembly with .INSRT, +they inherit (unless you specify otherwise) the symbol definitions +of the program that is calling them. Subroutines use specific +accumulator names for their arguments, rather than specific +accumulator numbers, and they expect to be called with a PUSHJ P, +where P is whatever the .INSRT'ing program defines it to be. + + Most libraries require a block of four or five accumulators, named +A, B, C, and D, and perhaps E. Usually, the code assumes that those +accumulators are consecutive, and the caller should assume that this +is required. Some libraries require another pair of consecutive +accumulators named T and TT. Of course, every library requires a +stack named P. Each library will say exactly which accumulators it +requires and which must be consecutive. + + If your existing program does not provide suitably named +accumulators, you can still use the library if you define the required +names within the library's symbol block. For example, if your program +defines the accumulator names A=1, B=2, C=3, T=4, D=5,, while the +library FOO insists on A through D consecutively, you can do + + FOO"D=4 + +causing the name D within FOO to refer to the program's accumulator T. +Within FOO, A through D are now consecutive, but D outside of FOO +continues to have its old value of 5. The only disadvantage of this +is that arguments to FOO's routines cannot be set up in D=5, but must +be placed in T or FOO"D, which are names for 4. + + Library subroutines are almost always intended to be called +with a "PUSHJ P,". Each subroutine's documentation will specify +whether it can skip return, what arguments are expected in which +accumulators, which values are to be counted on in which accumulators, +and which accumulators are randomly clobbered. Accumulators in the +set used by the library which are not mentioned in the documentation +may be used temporarily if they are saved and restored. Accumulators +not among those "used by the library" are not touched even for an +instant. + + Some libraries assume that the symbols CPOPJ and POPJ1 have been +defined by the caller in the standard manner: + + POPJ1: AOS (P) + CPOPJ: POPJ P, + +They may also expect the user to define specifically named routines to +serve as subroutines for the library to call. This is to allow the +user to customize the behavior of the library. Each library says +which such symbols are required by which routines. Of course, all +libraries assume that the predefined symbols of MIDAS are available. +If you redefine them carefully, you may succeed in faking the +libraries into doing something interesting. If you redefine them +carelessly, you will lose. + + Because many callers will not need all the routines in a library, +the caller can specify which parts of the library should be assembled +by defining some control parameters before .INSRT'ing the library. +Such parameters have names starting with "$$". Each library has its +own set of control parameters, which it documents; those not defined +will usually be taken to be zero, and the associated features NOT +assembled. Thus, the caller must set to one the control parameters +associated with the routines he wishes to use (some libraries may use +the opposite default, assembling all routines except those whose +control parameters are set to zero by the caller). Since it cannot be +assumed that no two libraries will have control paramaters with the +same name, it is good practise to make the control paramaters local to +the library's symbol block, as in: + + RFN"$$RFN==1 ;Assemble the RFN routine. + RFN"$$PFN==1 ;Assemble the PFN routine. + + Libraries know which parts depend on other parts as subroutines, and +automatically assemble the subroutines of any part that is explicitly +requested. + + All code assembled by libraries at the point where they are +.INSRT'ed is pure code. Most libraries create impure static storage +using .SCALAR and .VECTOR. A library that never uses them and has no +static variables will say so; others should be assumed to use them. + + Routines that use or return a text string generally expect to find a +byte pointer to the beginning of it in D. They then advance D as they +read or write the text, returning with D pointing at the last +character read or written. Some routines that write text will store a +null character after the last real character of output, to make the +string ASCIZ; D will not be advanced past the null. + +File: LIB Node: RFN, Up: Top, Previous: General, Next: Datime + +The RFN library contains filename reading and printing routines. + +ACS: This file requires acs A, B, C, D, and E, which NEED NOT be + consecutive, and P. + +VARS: This file defines no static variables. It is entirely pure. + +EXITS: Required exits can include SWITCH, RSIXTP, PSIXTP, MNAME, + and MSNAME, depending on the routines assembled. + +ENTRIES: + The entries defined are RFN, PFN, PFNMCH, and PFNBRF, and + RMNAME. They are defined only in the symbol block RFN. + None of them skips. + +The control parameters all default to zero: + $$RFN enables the filename reading routines. + Requires the RSIXTP be defined. + $$SWITCH enables the portion that handles switches. + Requires that SWITCH be defined. + $$RUC inhibits the definition of the RUC subroutine, used + for reading input, so that the user can define it. + Requires that RUC be defined. + $$PFN enables the PFN filename printing routine. + Requires that PSIXTP be defined. + $$PFNBRF as well enables the PFNBRF printing routine. + Requires that MSNAME be defined. + $$MNAME enables the PFNMCH printing routine. + It also enables the RMNAME routine that sets up MNAME. + Requires that MNAME be defined. + +A Filename Block is a four-word block which holds the four names of a + file in the order DEVICE, FN1, FN2 and SNAME, each as a word + of SIXBIT. These words are in the same order as the arguments + to a symbolic OPEN call. + + +Filename Reading: $$RFN==1 to enable. + + The routine RFN"RFN parses a file specification. It expects in D a +byte pointer to the text, and in B the address of a filename block in +which to store the parsed names. On return, B and C are unchanged, +while D is updated to point at the terminating character, which is +left in A. E contains flags indicating which of the four names were +specified by the filespec. The other words of the filename block are +not changed. The flags are bits 3.1, 3.2, 3.3 and 3.4 set to indicate +that the device, fn1, fn2 and sname, respectively, were specified. +These flags are best written as 1_RFN"DEV, 1_RFN"FN1, 1_RFN"FN2, +and 1_RFN"SNM. + + Filenames as parsed by RFN"RFN recognize the special characters +Space, Colon for the device name, Semicolon for the sname, ^X for +referring to the default fn1, ^Y for referring to the default fn2, and +^Q for quoting a special character to include it in a filename. +All control characters other than ^Q, ^X and ^Y terminate the +filespec. + + Additional filespec terminating characters are specified by the +caller by defining the routine RSIXTP, which should skip if the +character in A is one which should terminate the filespec. + + Default filenames can be supplied by storing them in the filename +block before calling RFN. For more control, you can examine the flags +returned in E to find which names were specified, and default the +others. However, ^X and ^Y will always take their defaults from the +contents of the second and third words of the filename block. + + RFN reads characters from the input string using the subroutine +RFN"RUC. The caller may set $$RUC==1 and define RUC himself, to cause +RFN to get its input from another source. RUC should return the next +input character in A, converted to upper case, and not skip. + + +Filename Reading with Switches: $$SWITCH==1 to enable + + Setting $$SWITCH causes RFN"RFN to call a routine SWITCH, which the +caller must define, whenever a switch is encountered. A slash is +followed by a single switch, while a "(" is followed by several +switches, terminated by a ")". + + The caller's SWITCH routine is called once for each slash, and +several times as appropriate for each "(". It should read and process +one switch, assuming that the first character of the switch is in A. +If it returns without skipping, RFN assumes that the last character +read was part of the switch. If SWITCH skips, RFN assumes that the +last character read, which must be left in A, was a terminator that +was not part of the switch. This terminator is reprocessed. +SWITCH must not clobber C or E. + + Note that slash and "(" are not recognized by RFN unless RSIXTP +skips for them. This gives the caller run-time control over whether +to accept switches. + + +Filename Printing: $$PFN==1 to enable. + + The routine RFN"PFN takes a filename block address in B and prints +the filenames, depositing the text down the byte pointer in D, which +is updated. No accumulator other than D is changed. + + PFN knows how to put ^Q in front of characters that need it. +It requires that the user define a routine PSIXTP that takes a +character in A and skips if that character requires a ^Q. However, +the characters Space, Colon and Semicolon get ^Q's and are not passed +to PSIXTP. Thus, PSIXTP can be the same routine as RSIXTP. + + The routine RFN"PFNBRF is similar to PFN except that it suppresses +mention of the device name if it is DSK, or the sname if it is the +default one. The user must set $$PFNBRF==1 to enable this, and must +define MSNAME to contain the default sname. + + +Filename Printing using AI: instead of DSK: $$MNAME to enable. + + The routine RFN"PFNMCH prints filenames just like PFN except that a +device name of DSK is printed as the running machine's name. +The user must define a location MNAME and store the machine's name +there. A routine RFN"RMNAME is provided which initializes MNAME +correctly. + +File: LIB, Node: DATIME, Up: Top, Previous: RFN, Next: NETWRK + +The DATIME library contains routines for handling dates and times. + +ACS: A, B, C, D and E, which must be consecutive, as well as P. + +VARS: ENGDTP and TMSYST are internal static variables. + +EXITS: None. + +The control parameters all default to zero: + $$OUT enables output as mm/dd/yy hh:mm:ss + $$OUTF enables output as, eg, 3 Mar 1975 1020 + $$OUTZ as well enables routines for outputting time zone + (EST vs EDT). + $$IN enables input of the form mm/dd/yy hh:mm:ss + $$INF enables flexible input allowing named months + $$ABS enables routines to convert dates in y-m-d format + to number of days since 1900, routines to add and subtract + dates, etc. + $$SVNG enables routines to determine whether daylight + savings time was in effect at a given time. + $$UPTM enables conversion of time into how long ITS had + been up at that time. + +Data Formats: + A date and time are usually expressed in the form of a time + word. A time word is a single word containing the time in + units of half a second, and one century worth of date. It is + also known as the "ITS disk-format date/time". + It has these fields: + + ; Mask Field Bits Range Units Actual Range + + TM%SEC== 777776 ; 2.9-1.2 0-131K seconds 0-86399. + TM%DAY== 37,,0 ; 3.5-3.1 0-31 days 1-31 + TM%MON== 740,,0 ; 3.9-3.6 0-15 months 1-12 + TM%YR== 177000,,0 ; 4.7-4.1 0-127 years 0-127 + relative to 1900 (1900-2027) + + Those four symbols, and byte pointer left halves named + TM$YR, TM$MON, TM$DAY and TM$SEC, are defined in DATIME. + So you can do LDB B,[DATIME"TM$YR,,A] to extract the year + from A into B, for example. + + + One routine which is always assembled is TIMGET, which returns the +current time as a time word in A. -1 is returned if ITS doesn't know +what the time is. + + +Output of date/time: + + $$OUT==1 enables the routines DATASC, TIMASC and TWDASC which output +a date and time from a time word in A in the format mm/dd/yy hh:mm:ss, +stuffing the text down the byte pointer in D. A null character is +deposited at the end of the string, but the byte pointer in D is not +advanced over it. B and C are untouched. + + DATASC outputs just the date, TIMASC outputs just the time, +and TWDASC outputs the date and time with a space between. +All three place a null character after the text they output. + + $$OUTF==1 enables the routine TIMENG which, called like TWDASC, +outputs the date and time in the format "7 AUG 1976 0831". +There is also DATENG, called the same way, which outputs only the +date, in the form "7 AUG 1976". + + $$OUTZ==1 enables routines TIMSTD and TIMEXP which know about the +difference between standard time and daylight saving time. +They are both called just like TIMENG. But whereas TIMENG simply +outputs the hour it is told, without worrying about the time zone, +TIMSTD assumes that the time was specified in EST, and converts it to +EDT if EDT was in effect at that time, and then prints the time with +EDT or EST as appropriate. +TIMEXP, on the other hand, assumes that the time was specified in +whichever time, EST or EDT, was in effect at that time. It prints the +time like TIMENG, followed by EST or EDT as appropriate. + + $$OUTT==1 enables the tables DOWTAB and DOWLNG of names of days of +the week. Each is seven words long. DOWTAB's words contain an ASCIZ +string each, such as ASCIZ/SUN/ in the first word. DOWLNG's words +contain pointers to ASCIZ strings of the full names of the days. +DOWTAB and DOWLNG should be used in conjunction with TIMDOW, which is +assembled when $$ABS==1. Another table, MONTAB, contains pointers to +the ASCIZ full names of the months, indexed by month number (Jan = 1). + + +Input of date/time: + + $$IN==1 enables the routine ASCTWD which is the inverse of TWDASC. +It accepts in D a pointer to an ASCIZ string, and returns a time word +in A, advancing D past the text that was used. + + $$INF==1 enables the "flexible format" input routines which accept +such input formats as "march 5, 1971" and "3mar71" for the date, and +"0800", "08:00" "0800:00" and "08:00:00" for the time. They take a +byte pointer in D pointing to the string, and advance it past the text +that they use. The routines are ENGTWD, ENGTIM and ENGDAT. +ENGTWD reads a date and a time, whereas ENGDAT reads just a date and +ENGTIM reads just a time. + + +Addition and Subtraction of Times: $$ABS==1 to enable. + + The time word format, while convenient for I/O and for comparison of +times, is not suitable for computation of the number of days or +seconds between two instants. The routines enabled by $$ABS allow you +do to that sort of thing. + + TIMADY converts the date in the time word in A to the number of days +past Jan 1 1900, which is returned in A. TIMSEC converts the date and +time in the time word in A to the number of seconds since Jan 1, 1900, +returned in A. + + ADYTIM and SECTIM perform the inverse transformations, of an +absolute number of days or seconds since 1900 into a time word. The +argument and value are again in A. + + Slightly higher level routines are TIMADD and TIMSUB. TIMADD adds +the number of seconds in B to the time word in A, returning the new +time as a time word in A. TIMSUB returns in A the number of seconds +from the time in B to the time in A, both arguments being in the form +of time words. + + TIMDOW takes a time word in A and returns in B the day-of-the-week +index (Sunday = 0) of that date. + + TIMNET takes a time word in A and returns in A the same time in +32-bit Arpanet standard format. + + The routines UPTIME and UPINI can convert a time word into the +system up-time in 30'ths of a second. They are assembled if +$$UPTM==1. UPTIME takes a time word in A, and returns the +corresponding system up-time. This works only for times since the +system came up, of course. Before using UPTIME, UPINI must be called +to initialize the variable TMSYST. + + +Daylight Savings Time: $$SVNG==1 to enable. + + The routines ODAYL and IDAYL know at which times daylight savings +time is in effect. + + ODAYL takes a time word in A, assumed to be standard time, and skips +if standard time was really in effect at that time. Thus, it can be +used to decide whether the time ought to be converted to daylight time +before being printed. + + Such conditional conversion can be done this way: + + MOVEI B,3600. + PUSHJ P,DATIME"ODAYL ;If daylight time now in effect, + PUSHJ P,DATIME"TIMADD ;convert to daylight time before printing. + + IDAYL takes a time word in A, which might be either standard or +daylight time, whichever was in effect at that time. Thus, it is the +sort of quantity that you get if you simply ask for the current time +or a file's creation time and do not convert the zone. IDAYL skips if +the argument supplied is presumably standard time. + + This is useful for converting such an unspecified-zone quantity +specifically into standard time, which can be done this wat: + + MOVNI B,3600. + PUSHJ P,DATIME"IDAYL ;If at a time when daylight time is in use, + PUSHJ P,DATIME"TIMADD ;convert daylight to standard. + +File: LIB, Node: NETWRK, Previous: DATIME, Up: Top, Next: LSRTNS + +SYSENG;NETWRK > Contains ARPANET-Handling Routines + +NETWRK is the oldest library, and makes more demands on the caller +than the others do. *Note NCP: (.INFO.;ITS NCP)*, for other +information that you will need for writing programs that use the +ARPANET. + +ACS: A, B, C, D, and E, which need not be consecutive, and + T and TT, which must be consecutive, and P. + All main entry points clobber T and TT. + +VARS: SKTBAS and HSTADR are internal static variables. + +EXITS: CPOPJ and POPJ1 must have the standard definitions. + GETCHR, PUTCHR, SPCHAN and DEBUG may be required. + + PUTCHR is used by several routines to "print" a character. + It should expect the character in T, and not skip. + +Entries: + HSTMAP accesses the host name table file. + HSTLOOK converts a host name string to a host number. + HOSTNM reads a host name from the terminal with completion. + ICP connects to a foreign server. + SERVE in a server listens for and connects to a user. + ANALYZ prints an error message + CONNECT makes a single connection. + CONFIN completes a connection made asynchronously. + HSTSRC converts a host number to a host name. + HSTSIX converts a host number to a concise abbreviation + for its name. + +Other conventions: + The major entry points of NETWRK all skip if succesful. They + are all defined in the containing symbol block, not just in + the block named NETWRK. They all clobber T and TT. + + A rigid correspondance between socket numbers and I/O channels + is assumed. See under the CONNECT routine for how and why. + +Control parameters default to zero: + $$HSTMAP enables mapping in the host names and characteristics + data base, and the conversion of a host number to a host + name. + $$HOSTNM enables the routines to convert host names to host + numbers. The following switches select one or both of + them. + $$SYMLOOK enables the non-interactive host name lookup. + $$SYMGET enables the interactive one. + $$HSTSIX enables the routine to generate a six-character + abbreviation for a specified host name such abbreviations + are used by servers to put in the LOGIN to be printed on + the system console. + $$CONNECT enables the routine to open a connection. + $$ICP enables the routine for a network user program's ICP. + $$SERVE enables the routine for a server's ICP. + $$SYSDBG tells the server ICP routine NOT to refuse + most connections when SYSDBG is on. + $$ERRHAN enables an automatic error message printout when + making a connection fails. + $$ANAYLZE enables the routine for printing an error message + when an error occurs using the net. + +Host Name Lookup: + + Programs that use the Arpanet often accept the name of a host as +input and contact that host. NETWRK provides routines for converting +a host name to the host's number. + + The first requirement for parsing a host name is to access the ITS +host name table, the file SYSBIN;HOSTS1 >. All programs should use +this table rather than have their own, because it is kept up-to-date. +The routine NETWRK"HSTMAP maps this file into core starting at the +page whose number is specified in A, making temporary use of an I/O +channel specified in B. HSTMAP skips if it is successful (it will +fail only if the file is clobbered), returning in rh(A) the number of +the first page not used. NETWRK"HSTADR will retain the address of the +beginning of the file, so NETWRK can access it. + + HSTMAP is assembled if $$HSTMAP==1. This should always be so. + + The basic operation of looking a name up in the table is performed +by the HSTLOOK routine, which takes in A the address of an ASCIZ string +containing the host name (or a host number in octal, or decimal +followed by a point). HSTLOOK skips if the name is valid and +unambiguous, returning the host number in A. If HSTLOOK does not skip, the name +was meaningless if B contains 0, or ambiguous if B is not 0. E, T and +TT are clobbered. To get HSTLOOK assembled, set $$HOSTNM and +$$SYMLOOK. + + If you wish simply to read a host name from the terminal, you can +call HOSTNM, which provides completion and help facilities. HOSTNM +requires no arguments when called, but it expects the user to define +the subroutines GETCHR, PUTCHR and SPCHAN which it calls. GETCHR is +used to read input (from the terminal, presumably); PUTCHR, to echo +the input and print error messages and completions; SPCHAN, to handle +control characters, etc. returned by GETCHR. HOSTNM skips if it +succeeds, returning the host number in A. To get HOSTNM assembled, +set $$HOSTNM and $$SYMGET. + + The user-provided subroutines of HOSTNM must have these calling +conventions: + PUTCHR should expect the character in T, and not skip. + GETCHR should return an input character in T, + skipping unless there is "no more input available". + SPCHAN is used by the interactive host-name lookup routine to + handle "special" input characters, not recognized. + The special character is passed in T, and the number of + characters read so far in TT. If SPCHAN skips, the + character is then ignored. If SPCHAN does not skip, the + read-in process is restarted, ignoring all input so far. + Of course, SPCHAN can collude with GETCHR to feed the + previous input or part of it to HOSTNM again. + + HOSTNM and HSTLOOK accept abbreviations as long as they are +unambiguous. The precise criterion is that, if the specified name +abbreviates more than one known host name, then one of those host +names must abbreviate all the rest. Thus, "Rut" is a valid +abbreviation for "Rutgers" even though "Rugters-10" and "Rutgers-Tip" +both exist and are different machines, because "Rutgers" abbreviates +both of the other two possibilities. If "Rutabaga" also existed, then +none of the four possible completions of "Rut" would abbreviate all +the others, and "Rut" would be invalid. This is an extension of the +normal rule that an exact match is valid even if it is also an +abbreviation for something else. + + In addition, HOSTNM and HSTLOOK accept an octal number, or a decimal +number terminated by a decimal point. + + HOSTNM, the interactive reader, types a bell whenever an invalid +name is entered, and ignores the invalid character. At any time, the +user can type "?" or Altmode to see a list of all the hosts whose +names are abbreviated by what he has typed so far. "?" or Altmode at +the beginning of a host name will type a list of all known hosts. + + +Routines for Servers: + + Arpanet server programs need to listen for and then establish +connections, and often need to be able to translate a host number into +the host's official name. Also, standard practise is for the server +to log in after accepting a connection, and supply as the "terminal +name" argument of the LOGIN symbolic system call a six-character +abbreviation of the host name, which will be printed on the log +console. NETWRK provides subroutines SERVE, HSTSRC and HSTSIX for +these functions. + + SERVE, assembled if $$SERVE==1, is the routine which listens for and +establishes a connection. It takes several arguments: +A contains the number of the first of three consecutive I/O channels. + The first is the ICP listen channel, + the second is the input channel for the TELNET connection, + and the third is the output channel for it. +B contains the local socket number to listen on. +C contains ,, where the modes are those to + be used in the OPENs for the two halves of the TELNET connection. +DEBUG must be defined by the user, and contain a flag which, if + nonzero, disables SERVE's time-out and causes it to .VALUE if + anything strange happens. Deposit -1 in DEBUG when you test the + server undeer DDT. +SERVE skips if successful; it can fail due to a time-out, or because +the setting of SYSDBG specifies that connections from the ICP'ing host +are to be rejected. When SERVE succeeds, it returns the foreign host +number in B, and clobbers A, D, T and TT. The channel whose number +was passed in A is now closed, and the next two channels are open. + + Normally, SERVE itself refuses connections from hosts locked out by +SYSDBG. However, if you set $$SYSDBG==1, SERVE will accept them, and +skip, returning in C a flag which will be nonzero if the connection is +not really wanted. In that case, the caller should break the +connection himself; but he has the opportunity first to send an error +message. + + To convert a host number into a host name, HSTSRC can be used. It +also makes it possible to find out what sort of machine the host is, +and what operating system it runs, if this information is known in the +ITS host name table. + + HSTSRC takes a host number in B, and returns host name as an ASCIZ +string whose address is in A. The sign of A is set if the host is a +TIP. In addition, D points at the entry for the host in the NUMBERS +table of the HOSTS1 file; see KSC;HOSTS1 > for the format of this +table. HSTMAP must be called before HSTSRC can be used. HSTSRC is +assembled is $$HSTMAP==1. + + To get instead a six-character abbreviation for the host name, call +HSTSIX. It takes a host number in A, and returns the abbreviation in +A as a word of SIXBIT, suitable for passing to a LOGIN system call. +If the host is not known, a name is made from its number. HSTSIX +always skips. HSTMAP must be called before using it. HSTSIX is +assembled if $$HSTSIX==1. + + +Routines for User Programs + + "User" ends of network protocols need to at least to establish a +TELNET connection to a server. If any problems arise, they should +decode the status information provided by ITS to print an error +message. The routines ICP and ANALYZ are provided for these purposes. +User ends will often also need the host-name conversion routines +described above. + + The routine ICP creates a two-way TELNET connection to a server. +It takes these arguments: +A contains , the number of the first of a block of four I/O + channels to be used. itself is used only during the ICP. + The input side of the TELNET connection is established on channel + +2, since its socket number must be two greater than the one + used on channel to begin the ICP. The output side takes the + next channel number, +3, since it takes the next socket + number. Channel +1 is not actually by the ICP process, and it + cannot be used for any network connection. It can be in use in any + other way, even while the ICP is happening. +B contains the number of the foreign host. +C contains the foreign socket number to ICP to (1 for TELNET, etc.) +D contains ,,, describing the modes to be + used in the OPENs that establish the two halves of the TELNET + connection. +ICP clobbers all the known ACs, A through E and T and TT, and returns +no values. It skips if the TELNET connection is successfully +established. Otherwise, a caller running under the control of a +terminal should print an error message by calling ANALYZ. Setting the +flag $$ERRHAN==1 will cause ICP to call ANALYZ itself before returning +in the case of failure. + + The ICP routine is synchronous: it waits until the connection +succeeds or fails. An ICP can be done asynchronously also, with the +ICPASN routine. ICPASN takes the same arguments as ICP, and in +addition should be given zero in E when it is called for the first +time. On successful return, the first channel (the ICP socket) will +have been opened. At any later time, calling ICPASN again will wait +for that first connection to complete and then request the two halves +of the TELNET connection. Calling ICPASN a third time will wait for +those two halves to complete. The ACs A through E returned by one +call to ICPASN must be saved and given to ICPASN again the next time. +If ICPASN ever fails to skip, the entire ICP has failed and you should +not attempt to complete it. + + The routine ANALYZ prints an error message after an attempt to open +a network connection fails. It needs no arguments, as it knows how to +track down all relevant information from ITS's debugging variables. +The error message is output by calling the user-supplied subroutine +PUTCHR with a character to be output in T. PUTCHR should output the +character as appropriate and return without skipping. +ANALYZ always skip-returns. + + +Establishing Subsidiary Connections (FTP data connection, etc): + + The CONNECT routine can be used to establish a unidirectional +network connection in either a server or user program. Its arguments +are these: +A contains the I/O channel number on which to open the connection. + This also specifies the socket number, via the channel-socket + correspondance. If a new connection must use a socket number 2 + greater than an existing connection's, then the new connection must + be made on the channel 2 greater than the existing connection's + channel. +B contains the foreign host number. +C contains the foreign socket number to connect to. +D contains in its rh the mode to use in the OPEN. + Its sign is set to request asynchronous mode. +Normally, CONNECT waits until the connection is completed, skipping if +it is successful. Failure can be due to a down host, a refusal, or to +a time out which CONNECT checks. When CONNECT fails to skip, you may +call ANALYZ (see above) to print an error message; or, if you set +$$ERRHAN==1, CONNECT will call ANALYZ itself. In any case, ACs T and +TT are clobbered; the others are preserved. + + Set the sign bit of D when calling CONNECT to create the connection +asynchronously. CONNECT will initiate the connection and return +skipping if there has been no problem yet. At a later time, call +CONFIN to wait for the connection to complete. CONFIN will skip if +the connection has been accepted. + + CONNECT, and ICP and SERVE which call it, uses the ITS feature that +assigns a set of eight consecutive network socket numbers to a job +which requests them. CONNECT assumes that these eight sockets +correspond directly to eight consecutive I/O channels; each socket +can be used only from the corresponding I/O channel. + + The socket-channel correspondance is set up the first time CONNECT +is called. At this time, connect realizes that it has no socket +numbers assigned and asks the system to assign some. The first socket +number of that block is used for the first connection, made on the I/O +channel specified in the first call to CONNECT. The following seven +socket numbers, also assigned to this job, correspond to the following +I/O channels. The first CONNECT stores the offset from the I/O +channel number to the corresponding socket number in NETWRK"SKTBAS, +so that later CONNECTs can convert a channel number to a socket +number. As you can see, the lowest-numbered socket of a set must be +CONNECTed first. + + If you close all network connections at any time, ITS deallocates +the block of socket numbers, so you should store zero in SKTBAS to +cause the next CONNECT to allocate a new set. + +File: LIB, Node: LSRTNS, Previous: NETWRK, Up: Top + +The LSRTNS library contains routines for accessing the INQUIR data + base. *Note LSR1: LSR1 Format, for the details of the format + of that data base. + +ACS: A, B, C, D, and E, which must be consecutive, and P. + +VARS: LSRTNS has these static internal variables: + + LSRADR ;Core address where first part of file is mapped in + DATFPG ;Page number in file of first data page + SCMPRT ;String compare routine to use LSRLNM and LSRLNP + + If overlaying of pages is being done (the default), + these additional variables exist. + + DATADR ;Core address of two pages for mapping in data pages of file + DATPAG ;DATADR/2000 + PAGEIN ;Page number in file of page currently mapped in at DATPAG + ;The page after that maps the next sequential file page. + ;This is negative if no page is currently mapped in. + + + If the HSNAME routines are included, (not by default) these variables + are exist as well. + + HSNADR ; Address in core of HSNAME table base. + HSNPTR ; AOBJN ptr to HSNAME table entries. + HSNLN1 ; Length-1 of an entry in HSNAME table. + LMCHNM ; Local machine name, as returned by SSTATU. + HRETRY ; Switch for re-trying with HX$NRM if HX$TUR (or other) fails. + LSRCHN ; Holds # of temp channel to use. + MNAME ; "Canonical" machname of site specified. + MNAME1 ; "MIT-**" version of machname. + + +Exits: None. + +Entries: (All skip if successful). + LSRMAP opens the data base. + LSRUNM searches for a UNAME's entry. + LSRITM extracts a specifically numbered item from an entry. + LSRLNM searches for the entries for a given last name. + LSRLNP is similar but allows abbreviation. + LSRNAM permutes a name in Lastname, Firstname, Suffix order + (as stored in the data base) into First Last Suffix. + LSRGET converts a file address into a core address, + overlaying if necessary. + LSRHSN looks up the HSNAME for a user, given his XUNAME + LSRTUR skips if an entry is that of a Tourist (T or nonexistant group) + +Control parameters default to One, except for $$HSNM. +Everything is normally assembled except what you say you don't need. + + $$ULNM==0 to omit the routine to search for a last name. + $$ULNP==0 to omit the routine to search for an abbreviated + last name. + $$UNAM==0 to omit the routine to permute a fullname into + "humanized" Firstname Lastname Suffix order. + $$OVLY==0 to omit the overlay routines, and map the whole + data base into core at once. This is good for a + program which wants to scan the whole data base + instead of looking up a few entries. + $$HSNM==1 to include the HSNAME lookup routines. + +Routines in LSRTNS: + + LSRMAP takes in A a channel number, and in B an AOBJN pointer to a +range of free pages, and maps in the data base. If successful, it +returns in B an updated pointer indicating which pages were not +needed, and leaves the file STILL OPEN on the given channel. +Pages which might be needed later for mapping in an entry are +remembered internally and marked off as "used". No skip can +either be due to trouble opening or mapping the file, or due +to a clobbered file, or due to needing more pages than are offered. +If you set LSRTNS"$$OVLY to zero at assembly time, the whole +file will be mapped in instead of just a few pages at a time. +In that case, the channel will not be left open. + + LSRUNM takes in A a channel number, on which the LSR1 file is +assumed to be open, and in B a UNAME as a word of SIXBIT, +and returns in B the address, in core, of the entry for that UNAME. +Such entries are mapped in by LSRUNM and will not +necessarily remain in core if another LSRUNM or LSRLNM is done. +No skip implies there is no entry for that UNAME. + + LSRLNM is like LSRUNM but expects in B the address of +an all-caps last name, as an ASCIZ string, instead +of a UNAME, and returns in B an aobjn pointer to the +LASTNAME table words whose Lastname match the argument. + + LSRLNP is like LSRLNM except that the specified last name +need only be an initial segment of a user's last name for the +user to be matched and included in the range of the lastname +table returned. + + LSRGET takes in A a channel number and in B the file address of an +entry. It maps in the appropriate pages and returns in B the core +address. Fails to skip if the map fails or if the given address does +not point at the header of an entry, Use this after calling LSRLNM, +etc. LSRUNM calls LSRGET itself. If $$OVLY is 0, then this routine +just converts a file address into a core address. + + LSRITM takes in A an item number and in B the address in core +of a LSR entry, and returns in A a b.p. to the specified item. +No skip => this entry lacks that item. A nonexistent item is legal +and should be treated just like a null string. The item number should +usually be specified with one of the I$... item names defined by +LSRTNS in the LSRTNS block: + + I$==,-1 ;Bit typeout mode prefix. + I$UNAM==0 ;UNAME + I$NAME==1 ;FULL NAME + I$NICK==2 ;NICKNAME + I$SSN==3 ;SOC SEC #. + I$MITA==4 ;MIT ADDRESS + I$MITT==5 ;MIT TELEPHONE NUMBER + I$HOMA==6 ;HOME ADDRESS + I$HOMT==7 ;HOME TELEPHONE NUMBER + I$SUPR==10 ;SUPERVISOR(S) + I$PROJ==11 ;PROJECT + I$DIR==12 ;FILE DIR NAMES + I$AUTH==13 ;AUTHORIZATION + I$GRP==14 ;GROUP AFFILIATION + I$REL==15 ;RELATION TO GROUP + I$BRTH==16 ;BIRTHDAY + I$REM==17 ;REMARKS + I$NETA==20 ;NETWORK ADDRESS + I$ALTR==21 ;USER &TIME OF LAST ALTERATION + I$MACH==22 ;I.T.S.'S TO BE KNOWN ON. + + LSRNAM takes in A a b.p. to a Name item, and in B a b.p. to +storage, and IDPBs down B the name, canonically permuted into +Firstname Lastname Suffix order. + + +LSRHSN find HSNAME for entry, given + A/ UNAME of entry + B/ or 0 if none. + C/ - as per SSTATU call, or host #, or 0 for local site. + D/ to use for disk opens (if -1, don't use any.) +It skips if successful, with + D/ HSNAME +It uses the following algorithm: + If no entry address given, + If directory for that UNAME exists, return that as HSNAME + Else assume tourist and use hasher/tables. + Else if "Fildir" entry exists, and a valid dir is specified for + local site, return that as HSNAME. + Else if directory for that UNAME exists, return that as HSNAME + Else determine HSNAME using hash or tables, plus touristness (see + LSRTUR) +LSRHSN is the routine that DDT uses to deterimine what a person's Home +directory is (HSNAME) on logging in. It is also used by the mailer to +determine where a person's mail goes. + +LSRTUR Test an entry for touristness. It takes a LSR entry address and +skips if tourist ("Group" field T or non-existent) + + + +File: LIB, Node: LSR1 Format, Up: LSRTNS + +This is the format of the user data base (INQUIR data base), +which lives in the file INQUIR;LSR1 > on each machine: + +HDRSID==0 ; wd 0 SIXBIT /LSR1!!/ for detection of clobberage. +HDRDAT==1 ; wd 1 Date of compilation as sixbit YYMMDD. +HDRTIM==2 ; wd 2 Time of compilation as sixbit HHMMSS. +HDRUNM==3 ; wd 3 Address in file of UNAME table. +HDRLNM==4 ; wd 4 Address in file LASTNAME table. +HDRDTA==5 ; wd 5 Address in file of start of data area. + ; The data area must start on a page boundary, + ; and it must be the last thing in the file. +HDRHSN==6 ; wd 6 Address in file of HSNAME table. + ;.... other data; the tables just mentioned + ; and strings which they point to. + +;UNAME table: +; wd 0 Number of entries in this table, +;followed by entries, one per page of data area, +;each containing the UNAME of the first data area entry which begins on that page. + +;LASTNAME table: +; wd 0 Number of entries in this table, +; followed by entries, in order by last name +; Each entry looks like this: +; rh addr in file of entry +; lh addr in file of Last-name string + +;The lastname strings are word-aligned upper-case ASCIZ strings, +; and they follow the LASTNAME table. + +;HSNAME table: +; Currently this table is simply snarfed from the file INQUIR;DIRS BIN, +; which is assembled from INQUIR;DMUNCH >. It's format is as follows: + + 0: -<# entries>,,# wds/entry + 1-n: MACH entries + +================================================================= +Sub-tables + + mach wd 0: ; 6bit returned by SSTATU + mach wd 1: ; Arpanet site #. + mach wd 2: ; Addr is rel to start of HSNAME-table. + mach wd 3: + +Each sub-table is simply: + 0: -<# entries>,,# wds per entry ; RH is currently 2. + 1-n: NAME entries + + entry wd 0: UNAME ; For all unames => this but < next entry, + entry wd 1: HSNAME ; this is the HSNAME. + +| + + ; HSNAME table indices +hx$mnm==:0 ; wd 0 of mach-table entry is mach name, as per SSTATU call. +hx$hst==:1 ; wd 1 is arpanet host #. +hx$nrm==:2 ; wd 2 is sub-table ptr for normal unames. +hx$tur==:3 ; wd 3 is sub-table ptr for tourists. +hx$len==:4 ; # wds per mach-table entry + + ; Sub-table indices +hs$unm==:0 ; wd 0 of sub-table entry is UNAME +hs$hsn==:1 ; wd 1 is HSNAME for range. +hs$len==:2 ; # wds per sub-table entry + +;The data area: +; It starts on a page boundary. It is made up of consecutive entries, +; in order by UNAME, each looking like this: +LDACNT==0 ; entry wd 0 lh + ; the number of words in the entry, + ; including this one. + ; entry wd 0 rh -1 + +; followed by unaligned ASCIZ strings, one per item, separated +; only by the single ^@ that ends the ASCIZ. The strings are associated +; with their meanings according to their numerical position in the entry. +; The entire entry is then padded to a word boundary with nulls. +; Note that the low bit is set in the count-words of entries +; and not in any other words of the data area. + +; Symbols for all the item numbers are defined in LSRTNS as follows: + + I$==,-1 ;Bit typeout mode prefix. + I$UNAM==0 ;UNAME + I$NAME==1 ;FULL NAME + I$NICK==2 ;NICKNAME + I$SSN==3 ;SOC SEC #. + I$MITA==4 ;MIT ADDRESS + I$MITT==5 ;MIT TELEPHONE NUMBER + I$HOMA==6 ;HOME ADDRESS + I$HOMT==7 ;HOME TELEPHONE NUMBER + I$SUPR==10 ;SUPERVISOR(S) + I$PROJ==11 ;PROJECT + I$DIR==12 ;FILE DIR NAMES + I$AUTH==13 ;AUTHORIZATION + I$GRP==14 ;GROUP AFFILIATION + I$REL==15 ;RELATION TO GROUP + I$BRTH==16 ;BIRTHDAY + I$REM==17 ;REMARKS + I$NETA==20 ;NETWORK ADDRESS + I$ALTR==21 ;USER &TIME OF LAST ALTERATION + I$MACH==22 ;I.T.S.'S TO BE KNOWN ON. + +; The entries are sorted in ascii order, which is NOT the same +; as the order obtained by arithmetically sorting the SIXBIT unames. + +; The end of the data area is marked by ,,-1, which +; is tantamount to an entry 0 words long. +; That word is also the last word of the file. + + +Local Modes +Auto Fill Mode:1 +end: diff --git a/doc/info/pdp-10.15 b/doc/info/pdp-10.15 new file mode 100755 index 00000000..01975acd --- /dev/null +++ b/doc/info/pdp-10.15 @@ -0,0 +1,2772 @@ +-*-Text-*- + +PDP-10 Node: Top, Up: (DIR), Next: Intro + +PDP-10 Machine Language. + +This file attempts to teach the machine language of the PDP-10 computer. It +describes what instructions are available and what they do. The conventions +of the assembler in which PDP-10 machine programs are usually written are +another subject; see *Note MIDAS: (MIDAS). + +The machine language itself: + +* Menu: + +* Intro:: Introduction +* Memory:: Addressing memory. Formats of memory words. +* IFormat:: General instruction formats. +* Terms:: Terminology. +* Addr-Comp:: Effective address computation. +* Full-Word:: Full word data transfer instructions +* Stack:: Stack instructions. +* Half-Word:: Half word data transfer instructions. +* Arith Tests:: Conditional jumps on signs and comparisons. +* Fixed Point:: Fixed point arithmetic operations. +* KL-Only:: KL-10 double precision fixed point. +* Floating Point:: Floating point arithmetic operations. +* Shifting:: Shift and rotate instructions. +* Byte:: Byte extraction instructions. +* Logical Tests:: Conditional skip on bit test instructions. +* Boolean:: Bitwise logical operations. +* PC Format:: Format of PC saved by subroutine calls. +* Jumps:: Jumps other than arithmetic conditionals. +* Obsolete:: Obsolete subroutine call instructions. + +Program examples: + +* Program:: Complete simple program. +* TTY Output:: Simple terminal output. +* Reverse:: Simple terminal input and output. +* Even-Odd:: Read a line, separate even- and odd-numbered chars. +* Even-Odd-Vowels:: Separates vowels from consonants too. +* Even-Odd-Vowels 2:: Another way of writing the previous example. +* Calculator:: Reads arithmetic expressions, prints results. +* FileIO:: Reading and writing files. +* Parsing:: Converting input to SIXBIT, or to filenames. +* BufInput:: Buffered input from files. +* BufPadding:: Buffered input, discarding padding at end of file. + +I haven't got the time to test these examples. That means they may have bugs. +Sorry. But it's a useful exercise to test one and, if it doesn't work, fix it +and tell BUG-INFO. + +* Debugging:: Using DDT to debug programs or try out instructions. + + +PDP-10 Node: Intro, Up: Top, Previous: Top, Next: Memory + +Introduction to PDP-10 Assembly Language Programming + +The PDP-10 is a general purpose stored program computer. There are four +different processors (computers) in the PDP-10 family (the PDP-6, the KA10, +the KI10 and the KL10). This file discusses primarily the KA-10 version, +which is what most of the ITS systems are. + +There are three principal aspects of assembly language programming: +the machine instructions, the assembler, and the operating system. + +The machine instructions are the primitive operations with which we +write programs. Learning the instruction set means learning what +operations are performed by each instruction. Programming is the art +or science of combining these operations to accomplish some +particular task. + +The machine instructions, like everything else in a computer, are in +binary. The assembler is a program that translates the mnemonic +names by which we refer to instructons into the binary form that the +computer recognizes. The assembler also does a variety of other +chores that are essentially bookkeeping. There are several assemblers +for the PDP-10, which differ in various ways; what they all have in +common is the PDP-10 machine instructions, which are described in this +file. Everything else about how to use a particular assembler is +documented under that assembler. The assembler used most on ITS is +called MIDAS; *Note MIDAS: (MIDAS). + +The operating system is a special program that handles all input and +output and which schedules among user programs. For its own +protection and the protection of other users the operating system +places various restrictions on user programs. User mode programs are +restricted to memory assigned to them by the operating system; they +may not perform any machine input-output instructions, nor can they +perform several other restricted operations (e.g., HALT instruction). +To facilitate user input-output and core allocation the operating +system provides various system calls (UUO or JSYS operations) by +which a user program can communicate its wishes to the system. +Essentially all programs except the operating system itself are run +as user mode programs. Editors, assemblers, compilers, utilities, +and programs that you write yourself are all user mode programs. + +PDP-10 Node: Memory, Previous: Intro, Up: Top, Next: IFormat + +Memory + +In programming the PDP-10 it is convenient to imagine that your +program occupies contiguous virtual memory locations from 0 to some +maximum address. All memory locations are equivalent for most +purposes (but some operating systems reserve some of your space for +their own purposes). + +Accumulators + +Sixteen memory locations (addresses 0 to 17 - note that addresses +will appear in octal) are distinguished by their use as general +purpose registers (also called accumulators or index registers). +Most PDP-10 instructions address one memory operand and one +accumulator (so-called "one and a half address" architecture). This +means that nearly all instructions affect some accumulator. These +registers are actually implemented in high speed solid state memory +rather than in slower core. For any purpose where it is convenient +to do so, a user may reference an accumulator as memory. + + +PDP-10 Node: IFormat, Previous: Memory, Up: Top, Next: Terms + +Instruction Formats + +The PDP-10 is a word oriented machine. Words contain 36 data bits, +numbered (left to right) 0 to 35. Every machine instruction is one +word. The program counter or PC is a register which contains the +address of the next word to be used as an instruction; after a normal +instruction, the PC is incremented by one so that successive +instructions come from successive words. + +There are two formats for machine instructions. Most +instructions have the format: + + 000000000 0111 1 1111 112222222222333333 + 012345678 9012 3 4567 890123456789012345 + ________________________________________ +| | | | | | +| OP | AC |I| X | Y | +|_________|____|_|____|__________________| + + + +Input-output instructions (which are illegal in user mode) have the +format: (These instructions are not discussed any farther by this +file). + + 000 0000000 111 1 1111 112222222222333333 + 012 3456789 012 3 4567 890123456789012345 + _________________________________________ +| | | | | | | +|111| DEV |IOP|I| X | Y | +|___|_______|___|_|____|__________________| + +In the diagrams above the field names are + OP = operation code + AC = accumulator field + I = indirect bit + X = index field + Y = address field + DEV = device code + IOP = input-output operation code + +Some example intructions are: + + MOVE 1,@100 ;MOVE is the OP. AC is 1. + ;@ sets the I bit. + ;X is zero, Y is 100. + + HRRZ 17,1(3) ;HRRZ is the OP. AC is 17, + ;Y = 1, X = 3, I = 0 + + SOS FOO ;SOS is OP, FOO is symbolic + ;for the Y field. AC, X, I are 0. + +The address field, the index field, and the indirect bit are all used +in effective addrss computation. *Note Addr: Addr-comp. + +PDP-10 Node: Terms, Previous: IFormat, Up: Top, Next: Addr-Comp + +Terminology + +Symbols particular to this file: + +# means "not equal". +<= means "less than or equal". +>= means "greater than or equal". +<- means "is assigned the new value". + +AC means the contents of the AC field of the current instruction; + this is a number from 0 to 17. +E means the value of the effective address of the current instruction. + *Note Addr: Addr-Comp, for how this is computed. +PC means the address from which the next instruction will be fetched. +C(...) surrounding an expression refers to the contents of the memory + location whose address is the value of the expression. + Thus, C(AC) is the contents of the accumulator which the + instruction refers to, and C(E) is the contents of the memory + location which the instruction refers to. +CR(...) means the right half, only, of the contents. +CS(...) means the contents, with the two halves swapped. +CL(...) means the left half, only, of the contents. + +Symbols which are taken from assembler language: + +x,,y means a word whose left half contains x, and whose right half + contains y. This is similar to x*1000000+y, except that x,,y + truncates y, using only the low 18 bits of it. + +<...> surrounding an expression serves the same purpose + as parentheses in algebra. Thus, 5*<1,,1> equals 5,,5. + +[...] surrounding an expression means the address of a word + in memory which contains the specified expression. + Thus, [2] means the address of a word containing 2. + This is called a literal. The address could turn out to + be anything at all, but the proper ways to use literals are + such that only the contents matter. + +(...) surrounding an expression exchanges the halves of that + expression. Thus, (3,,4) equals 4,,3. + +PDP-10 Node: Addr-Comp, Previous: Terms, Up: Top, Next: Full-Word + +Effective Address Calculation + +All instructions without exception calculate an "effective address". +The effective address gets its name because it is usually used as the +address of an operand in memory. Depending on the instruction, that +operand might be read, written or both. For some instructions, called +"immediate" instructions, the effective address is not used to address +memory; it is used directly, as a number, in the operation. For +example, the ADD instruction uses the effective address as the +address of a location in memory, and uses the contents of that +location in the addition. The ADDI instruction (Add Immediate) uses +the effective address itself as the number to add. + +The effective address computation uses three fields of the instruction +word: the 18-bit address field (Y), the index field (X), and the +indirect bit (I). The result is an 18-bit effective address. + +If the X field and I bit are zero, the effective address is simply the +contents of the address field (Y). + +If the index field X is nonzero, then it is the number of an +accumulator to use as an index register. Any accumulator except +accumulator 0 can be so used. The right half of the contents of the +index register is added to the address field (Y) from the instruction +to get the effective address. + +The I bit specifies indirect addressing. If it is 1, then the result +of the previous steps (Address field, or address field plus index +quantity) is used as the address of an "indirect word". From the +contents of this indirect word, a new address field Y, index field X +and indirect bit I are obtained. Then the process starts from the +beginning. If the I bit in the indirect word is 1, a second indirect +word is eventually fetched. Indirection can happen any number of +times, and only stops when an indirect word has 0 in its I bit. + +The result of the effective address calculation may be thought of as +an instruction word where bits 0:12 are copied from the original +instruction, bits 13:17 are zero, and 18:35 contain the effective +address. + +The effective address computation is described by the following +program. MA means memory address. PC means program counter. C(MA) +means contents of the word addressed by MA. + +IFETCH: MA <- PC + OP <- Bits 0:8 of C(MA); + AC <- Bits 9:12 of C(MA); +EACOMP: I <- Bit 13 of C(MA); + X <- Bits 14:17 of C(MA); + Y <- Bits 18:35 of C(MA); + E <- Y; + IF NOT(X=0) then E <- E+C(X); + IF I=0 then go to done; + MA <- E; + GO TO EACOMP; +DONE: + + + +PDP-10 Node: Full-Word, Previous: Addr-Comp, Up: Top, Next: Stack + +Full word instructions. MOVE, BLT, EXCH, PUSH, POP + +These are the instructions whose basic purpose is to move one or more +full words of data from one location to another, usually from an +accumulator to a memory location or vice versa. In some cases, minor +arithmetic operations are performed, such as taking the magnitude or +negative of a word. + +The MOVE class of instructions perform full word data transmission +between an accumulator and a memory location. There are sixteen +instructions in the MOVE class. All mnemonics begin with MOV. The +first modifier specifies a data transformation operation; the second +modifier specifies the source of data and the destination of the +result. + + + |E no modification | from memory to AC +MOV |N negate source |I Immediate. Move the address to AC. + |M magnitude |M from AC to memory + |S swap source |S to self. If AC#0, move to AC also + +"Magnitude" means that the absolute value of the input quantity is +what is stored in the output. +"Swap Source" means that the right and left halves of the input +quantity are interchanged before storing into the output. + +In a "to self" instruction, the input value (negated, swapped, or the +magnitude, if appropriate) is stored back into the memory location; if +the AC field is nonzero, the value is stored in the AC as well. + +MOVE C(AC) <- C(E) +MOVEI C(AC) <- 0,,E +MOVEM C(E) <- C(AC) +MOVES C(E) <- C(E); if AC#0 then C(AC) <- C(E) + +MOVN C(AC) <- -C(E) +MOVNI C(AC) <- -E +MOVNM C(E) <- -C(AC) +MOVNS C(E) <- -C(E); if AC#0 then C(AC) <- -C(E) + +MOVM C(AC) <- |C(E)| +MOVMI C(AC) <- 0,,E +MOVMM C(E) <- |C(AC)| +MOVMS C(E) <- |C(E)|; if AC#0 then C(AC) <- |C(E)| + +MOVS C(AC) <- CS(E) +MOVSI C(AC) <- E,,0 +MOVSM C(E) <- CS(AC) +MOVSS C(E) <- CS(E); if AC#0 then C(AC) <- CS(E) + + + + + +EXCH exchanges the contents of the selected ac with the contents of +the effective address. + +EXCH C(AC)>; C(CR(AC))<-C(E) + +The specified accumulator is incremented by adding 1 to each half (in +the KI10 and KL10 carry out of the right half is suppressed). If, as +result of the addition, the left half of the AC becomes positive, a +pushdown overflow condition results (but the instruction procedes to +completion). The word addressed by the effective address is fetched +and stored on the top of the stack which is addressed by the right +half of the (incremented) accumulator. + +POP C(E)<-C(CR(AC)); C(AC)<-C(AC)-<1,,1> + +POP undoes PUSH as follows: the word at the top of the stack +(addressed by the right half of the selected AC) is fetched and +stored at the effective address. Then the AC is decremented by +subtracting 1 from both halves (in the KI10 and KL10 carry out of bit +18 is suppressed). If the AC becomes negative as a result of the +subtraction a pushdown overflow results. + +Often the accumulator used as the pushdown pointer is given the +symbolic name P. To initialize a pushdown pointer (e.g., for N words +starting at PDL), one might do the following: + MOVE P,[-N,,PDL-1] + +Elsewhere in the program should appear: + +PDL: BLOCK N + +which defines the symbolic label PDL and reserves N words +following it. + +PDP-10 Node: Half-Word, Previous: Stack, Up: Top, Next: Arith Tests + +Halfword instructions + +The halfword class of instructions perform data transmission between +one half of an accumulator and one half of a memory location. There +are sixty-four halfword instructions. Each mnemonic begins with H +and has four modifiers. The first modifier specifies which half of +the source word; the second specifies which half of the destination. +The third modifier specifies what to do to the other half of the +destination. The fourth modifier specifies the source of data and +the destination of the result. + +H halfword from |R right of source to + |L left + + |R right of destination + |L left + + | no modification of other half + |Z zero other half + |O set other half to ones + |E sign extend source to other half + + | from memory to AC + |I Immediate + |M from AC to memory + |S to self. If AC#0, then move to AC also. + +C18 means bit 18 of the contents (the high bit of the low half); +C0 means bit 0 of the contents (the high bit of the high half); +E18 means the high bit of the effective address. +777777*X evaluates to 0 if X is 0, or 777777 (all ones) if X is one. +Such expressions represent sign extension. + +HRR CR(AC) <- CR(E) +HRRI CR(AC) <- E +HRRM CR(E) <- CR(AC) +HRRS CR(E) <- CR(E); if AC#0 then CR(AC) <- CR(E) + +HRRZ C(AC) <- 0,,CR(E) +HRRZI C(AC) <- 0,,E +HRRZM C(E) <- 0,,CR(AC) +HRRZS C(E) <- 0,,CR(E); if AC#0 then C(AC) <- 0,,CR(E) + +HRRO C(AC) <- 777777,,CR(E) +HRROI C(AC) <- 777777,,E +HRROM C(E) <- 777777,,CR(AC) +HRROS C(E) <- 777777,,CR(E); if AC#0 then C(AC) <- 777777,,CR(E) + +HRRE C(AC) <- 777777*C18(E),,CR(E); +HRREI C(AC) <- 777777*E18,,E +HRREM C(E) <- 777777*C18(AC),,CR(AC) +HRRES C(E) <- 777777*C18(E),,CR(E); + if AC#0 then C(AC) <- 777777*C18(E),,CR(E) + +HRL CL(AC) <- CR(E) +HRLI CL(AC) <- E +HRLM CL(E) <- CR(AC) +HRLS CL(E) <- CR(E); if AC#0 then CL(AC) <- CR(E) + +HRLZ C(AC) <- CR(E),,0 +HRLZI C(AC) <- E,,0 +HRLZM C(E) <- CR(AC),,0 +HRLZS C(E) <- CR(E),,0; if AC#0 then C(AC) <- CR(E),,0 + +HRLO C(AC) <- CR(E),,777777 +HRLOI C(AC) <- E,,777777 +HRLOM C(E) <- CR(E),,777777 +HRLOS C(E) <- CR(E),,777777; if AC#0 then C(AC) <- CR(E),,777777 + +HRLE C(AC) <- CR(E),,777777*C18(E) +HRLEI C(AC) <- E,,777777*E18 +HRLEM C(E) <- CR(AC),,777777*C18(AC) +HRLES C(E) <- CR(E),,777777*C18(E); + if AC#0 then C(AC) <- CR(E),,777777*C18(E) + +HLR CR(AC) <- CL(E) +HLRI CR(AC) <- 0 +HLRM CR(E) <- CL(AC) +HLRS CR(E) <- CL(E); if AC#0 then CR(AC) <- CL(E) + +HLRZ C(AC) <- 0,,CL(E) +HLRZI C(AC) <- 0 +HLRZM C(E) <- 0,,CL(AC) +HLRZS C(E) <- 0,,CL(E); if AC#0 then C(AC) <- 0,,CL(E) + +HLRO C(AC) <- 777777,,CL(E) +HLROI C(AC) <- 777777,,0 +HLROM C(E) <- 777777,,CL(AC) +HLROS C(E) <- 777777,,CL(E); if AC#0 then C(AC) <- 777777,,CL(E) + +HLRE C(AC) <- 777777*C0(E),,CL(E); +HLREI C(AC) <- 0 +HRREM C(E) <- 777777*C0(AC),,CL(AC) +HRRES C(E) <- 777777*C0(E),,CL(E); + if AC#0 then C(AC) <- 777777*C0(E),,CR(E) + +HLL CL(AC) <- CL(E) +HLLI CL(AC) <- 0 +HLLM CL(E) <- CL(AC) +HLLS CL(E) <- CL(E); if AC#0 then CL(AC) <- CL(E) + +HLLZ C(AC) <- CL(E),,0 +HLLZI C(AC) <- 0 +HLLZM C(E) <- CL(AC),,0 +HLLZS C(E) <- CL(E),,0; if AC#0 then C(AC) <- CL(E),,0 + +HLLO C(AC) <- CL(E),,777777 +HLLOI C(AC) <- 0,,777777 +HLLOM C(E) <- CL(E),,777777 +HLLOS C(E) <- CL(E),,777777; if AC#0 then C(AC) <- CL(E),,777777 + +HLLE C(AC) <- CL(E),,777777*C0(E) +HLLEI C(AC) <- 0 +HLLEM C(E) <- CL(AC),,777777*C0(AC) +HLLES C(E) <- CL(E),,777777*C0(E); + if AC#0 then C(AC) <- CL(E),,777777*C0(E) + +PDP-10 Node: Arith Tests, Previous: Half-word, Up: Top, Next: Fixed Point + +Arithmetic testing. AOBJP, AOBJN, JUMP, SKIP, CAM, CAI, AOS, SOS, SOJ, AOJ + +The AOBJ (Add One to Both halves of AC and Jump) instructions allow +forward indexing through an array while maintaining a control count +in the left half of an accumulator. Use of AOBJN and AOBJP can +reduce loop control to one instruction. + +AOBJN C(AC)<-C(AC)+<1,,1>; If C(AC)<0 then PC<-E; +AOBJP C(AC)<-C(AC)+<1,,1>; If C(AC)>=0 then PC<-E; + +Example. Add 3 to N words starting at location TAB: + MOVSI 1,-N ;Initialize register 1 to -N,,0 + MOVEI 2,3 ;register 2 gets the constant 3. + ADDM 2,TAB(1) ;add 3 to one array element. + AOBJN 1,.-1 ;increment both the index and the control. + ;Loop until the ADDM has been done N times. + +By the way, for the sake of consistency, AOBJN should have been called +AOBJL and AOBJP should have been called AOBJGE. However, they weren't. + + +The JUMP instructions compare the selected accumulator to zero and +jump (to the effective address of the instruction) if the specified +relation is true. + +JUMP Jump never. This instruction is a no-op. +JUMPL If C(AC) < 0 then PC<-E; +JUMPLE If C(AC) <= 0 then PC<-E; +JUMPE If C(AC) = 0 then PC<-E; +JUMPN If C(AC) # 0 then PC<-E; +JUMPGE If C(AC) >= 0 then PC<-E; +JUMPG If C(AC) > 0 then PC<-E; +JUMPA PC<-E. This is an unconditional branch. + +Example: + JUMPLE 5,FOO ;Jump to FOO if AC 5 is negative or zero. + + +The SKIP instructions compare the contents of the effective address +to zero and skip the next instruction if the specified relation is +true. If a non-zero AC field appears, the selected AC is loaded from +memory. + +SKIP If AC#0 then C(AC)<-C(E); +SKIPL If AC#0 then C(AC)<-C(E); If C(E) < 0 then skip +SKIPLE If AC#0 then C(AC)<-C(E); If C(E) <= 0 then skip; +SKIPE If AC#0 then C(AC)<-C(E); If C(E) = 0 then skip; +SKIPN If AC#0 then C(AC)<-C(E); If C(E) # 0 then skip; +SKIPGE If AC#0 then C(AC)<-C(E); If C(E) >= 0 then skip; +SKIPG If AC#0 then C(AC)<-C(E); If C(E) > 0 then skip; +SKIPA If AC#0 then C(AC)<-C(E); skip; + +Example: + SKIPL FOO ;Unless FOO's contents are negative, + MOVE 1,BAR ;load BAR's contents into accumulator 1. + ;By convention, instructions which can be + ;are written indented an extra space. + + SKIPN 2,FOO ;Load FOO's contents into accumulator 2 + ;and if they are nonzero, skip the next + ;instruction. + + +The AOS (Add One to memory and Skip) instructions increment a memory +location, compare the result to zero to determine the skip condition, +If a non-zero AC field appears then the AC selected will be loaded +(with the incremented data). + +AOS Add One to Storage (don't skip). + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + +AOSL Add One and Skip if Less than zero. + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + If C(E) < 0 then skip; + +AOSLE Add One and Skip if Less than or Equal to zero. + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + If C(E) <= 0 then skip; + +AOSE Add One and Skip if Equal to zero. + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + If C(E) = 0 then skip; + +AOSN Add One and Skip if Not zero. + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + If C(E) # 0 then skip; + +AOSGE Add One and Skip if Greater than or Equal to zero. + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + If C(E) >= 0 then skip; + +AOSG Add One and Skip if Greater than zero. + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + If C(E) > 0 then skip; + +AOSA Add One and Skip Always + C(E) <- C(E)+1; If AC#0 then C(AC)<-C(E); + skip; + +Example: +;This is the way, in parallel processing, +;we wait for a lock to be free and then lock it. +;If the lock is unlocked, it contains -1, so incrementing it yields zero. + AOSE FOO ;Increment FOO's contents, skip if zero. + JUMPA .-1 ;If they aren't zero, do it again. + + +The SOS (Subtract One from memory and Skip) instructions decrement a +memory location, then compare the result to zero to decide whether to +skip. If a non-zero AC field appears then the AC selected will +be loaded (with the decremented data). + +The SOS instructions are just like the AOS instrucrtions except that +they subtract one instead of adding one. + +SOS Subtract One from Storage (don't skip). + C(E) <- C(E)-1; If AC#0 then C(AC)<-C(E); + +SOSL Subtract One and Skip if Less than zero. + Perform SOS instruction: + C(E) <- C(E)-1; If AC#0 then C(AC)<-C(E); + Then, if C(E) < 0 then skip; + +The other SOS instructions differ from SOSL only in when they skip. + +SOSLE Subtract One and Skip if Less than or Equal to zero. +SOSE Subtract One and Skip if Equal to zero. +SOSN Subtract One and Skip if Not zero. +SOSGE Subtract One and Skip if Greater than or Equal to zero. +SOSG Subtract One and Skip if Greater than zero. +SOSA Subtract One and Skip Always + + +The AOJ (Add One to AC and Jump) instructions increment the contents +of the selected accumulator. If the result bears the indicated +relation to zero then the instruction will jump to the effective +address. + +AOJ Add One (don't jump). + C(AC) <- C(AC)+1; + +AOJL Add One and Jump if Less than zero. + C(AC) <- C(AC)+1; If C(AC) < 0 then PC <- E; + +The other AOJ instructions differ from AOJL only in how they decide +whether to jump. + +AOJLE Add One and Jump if Less than or Equal to zero. +AOJE Add One and Jump if Equal to zero. +AOJN Add One and Jump if Not zero. +AOJGE Add One and Jump if Greater than or Equal to zero. +AOJG Add One and Jump if Greater than zero. +AOJA Add One and Jump Always + + +The SOJ (Subtract One from AC and Jump) instructions decrement the +contents of the selected accumulator. If the result bears the +indicated relation to zero then the instruction will jump to the +effective address. + +SOJ Subtract One (don't jump). + C(AC) <- C(AC)-1; + +SOJL Subtract One and Jump if Less than zero. + C(AC) <- C(AC)-1; If C(AC) < 0 then PC <- E; + +The other SOJ instructions differ from SOJL only in how they decide +whether to jump. + +SOJLE Subtract One and Jump if Less than or Equall to zero. +SOJE Subtract One and Jump if Equal to zero. +SOJN Subtract One and Jump if Not zero. +SOJGE Subtract One and Jump if Greater than or Equal to zero. +SOJG Subtract One and Jump if Greater than zero. +SOJA Subtract One and Jump Always + + +The CAM (Compare Accumulator to Memory) class compare the contents of +the selected accumulator to the contents of the effective address. +If the indicated condition is true, the instruction will skip. The +CAM instruction is suitable for arithmetic comparision of either +fixed point quantities or normalized floating point quantities. +Needless to say, for the comparison to be meaningful both C(AC) and +C(E) should be in the same format (i.e., either both fixed or both +floating). + +CAM no op (references memory) +CAML If C(AC) < C(E) then skip; +CAMLE If C(AC) <= C(E) then skip; +CAME If C(AC) = C(E) then skip; +CAMN If C(AC) # C(E) then skip; +CAMGE If C(AC) >= C(E) then skip; +CAMG If C(AC) > C(E) then skip; +CAMA skip; + +The CAI (Compare Accumulator Immediate) class compare the contents of +the selected accumulator to the value of the effective address. If +the indicated condition is true, the instruction will skip. +An effective address is an 18 bit quantity that is always considered +to be positive. + +CAI no op +CAIL If C(AC) < E then skip; +CAILE If C(AC) <= E then skip; +CAIE If C(AC) = E then skip; +CAIN If C(AC) # E then skip; +CAIGE If C(AC) >= E then skip; +CAIG If C(AC) > E then skip; +CAIA skip; + +PDP-10 Node: Fixed Point, Previous: Arith Tests, Up: Top, Next: KL-Only + +Fixed point arithmetic ADD, SUB, IMUL, IDIV, MUL, DIV + +In positive numbers bit 0 is zero. Bits 1 is most significant; bit +35 is least significant. Negative numbers are the twos complement of +postive numbers. Results (of ADD, SUB or IMUL) outside the range +-2^|35 to 2^|35-1 will set overflow (PC bit 0). + +Each arithmetic instruction has four forms, with modifier characters +(nothing), M, B and I. + +The ordinary form operates on an accumulator and memory, + putting the result in the accumulator. +The Memory form puts the result in the memory location instead. + The accumulator is not changed. +The Both form stores the result in both the accumulator and the memory + location. +The Immediate form uses an accumulator and the effective address, + putting the result in the accumulator. + +ADD C(AC) <- C(AC) + C(E); +ADDI C(AC) <- C(AC) + E; +ADDM C(E) <- C(AC) + C(E); +ADDB C(AC) <- C(AC) + C(E); C(E) <- C(AC); + +SUB C(AC) <- C(AC) - C(E); +SUBI C(AC) <- C(AC) - E; +SUBM C(E) <- C(AC) - C(E); +SUBB C(AC) <- C(AC) - C(E); C(E) <- C(AC); + +The IMUL instructions are for multiplying numbers where the product +is expected to be representable as one word. + +IMUL C(AC) <- C(AC) * C(E); +IMULI C(AC) <- C(AC) * E; +IMULM C(E) <- C(AC) * C(E); +IMULB C(AC) <- C(AC) * C(E); C(E) <- C(AC); + +The IDIV instructions are for divisions in which the dividend is a one +word quantity. Two consecutive accumulators are used for the results; +these are AC for the quotient, and AC+1 for the remainder (Actually, +AC+1 is calculated mod 20, so if AC=17, the remainder is stored in +accumulator 0.) If the divisor is zero set overflow and no divide; +don't change AC or memory operands. The remainder will have the same +sign as the dividend. + +IDIV C(AC) <- C(AC) / C(E); C(AC+1) <- remainder +IDIVI C(AC) <- C(AC) / E; C(AC+1) <- remainder; +IDIVM C(E) <- C(AC) / C(E); +IDIVB C(AC) <- C(AC) / C(E); C(AC+1) <- remainder; C(E) <- C(AC); + + +The MUL instructions produce a double word product. A double word +integer has 70 bits of significance. Bit 0 of the high order word is +the sign bit. In data, Bit 0 of the low order word is ignored by the +hardware. In results, bit 0 of the low word is the same as bit 0 in +the high word. MUL will set overflow if both operands are -2^|35. + +MUL C(AC AC+1) <- C(AC) * C(E); +MULI C(AC AC+1) <- C(AC) * E; +MULM C(E) <- high word of product of C(AC) * C(E); +MULB C(AC AC+1) <- C(AC) * C(E); C(E) <- C(AC); + +The DIV instructions are for divisions in which the dividend is a two +word quantity (such as produced by MUL). If C(AC) is greater than the +memory operand then set overflow and no divide. + + +DIV C(AC) <- C(AC AC+1) / C(E); C(AC+1) <- remainder; +DIVI C(AC) <- C(AC AC+1) / E; C(AC+1) <- remainder; +DIVM C(E) <- C(AC AC+1) / C(E); +DIVB C(AC) <- C(AC AC+1) / C(E); C(AC+1) <- remainder; + C(E) <- C(AC); + +PDP-10 Node: KL-Only, Previous: Fixed Point, Up: Top, Next: Floating Point + +This node describes some instructions that only KL-10's have. This +means that the only ITS machine which has them is MC. You should +probably not use them even if you expect to run on MC, so that your +program can be moved. + +Double word Move instructions (KI10 and KL10) + +There are four double word move instructions. These are suitable for +manipulating KI10 and KL10 double precision floating point numbers, +and for KL10 double precision integers. + +DMOVE C(AC AC+1) <- C(E E+1) +DMOVEM C(E E+1) <- C(AC AC+1) +DMOVN C(AC AC+1) <- -C(E E+1) +DMOVNM C(E E+1) <- -C(AC AC+1) + +Note that the DMOVN and DMOVNM are NOT to be used for KA10 double +precision floating point numbers! + +If a program is written that may be have to be run on a KA10, the use +of all double word instructions should be avoided. + +Double Precision Integer Arithmetic (KL10 only) + +There are four instructions for double precision integer arithmetic. +None of these instructions have any modifier: they all operate on +double (or quadruple) accumulators and double words in memory with +results to double (or quadruple) accumulators. + +The format for a double word integer is the same as that produced by +MUL, i.e., a 70 bit integer in twos complement, with bit 0 of the +most significant word is the sign; in operands, bit 0 of the low +order word is ignored. A quadruple word has 140 bits; bit 0 of the +most significant word is the sign; in operands, bit 0 in all other +words is ignored. In double (and quadruple) arithmetic results bit 0 +of the low order word(s) is stored with the same value as bit 0 of +the high order word. + +DADD C(AC AC+1) <- C(AC AC+1) + C(E E+1); +DSUB C(AC AC+1) <- C(AC AC+1) - C(E E+1); +DMUL C(AC AC+1 AC+2 AC+3) <- C(AC AC+1) * C(E E+1); +DDIV C(AC AC+1) <- C(AC AC+1 AC+2 AC+3) / C(E E+1); + C(AC+2 AC+3) <- remainder; + +PDP-10 Node: Floating Point, Previous: KL-Only, Up: Top, Next: Shifting + +Floating Point Arithmetic + +Single precision floating point numbers are represented in one 36 bit +word as follows: + + 0 00000000 011111111112222222222333333 + 0 12345678 901234567890123456789012345 + ______________________________________ +| | | | +|S| EXP | Fraction | +|_|_______|____________________________| + + +If S is zero, the sign is positive. If S is one the sign is negative +and the word is in twos complement format. The fraction is +interpreted as having a binary point between bits 8 and 9. The +exponent is an exponent of 2 represented in excess 200 (octal) +notation. In a normalized floating point number bit 9 is different +from bit 0, except in a negative number bits 0 and 9 may both be one +if bits 10:35 are all zero. A floating point zero is represented by +a word with 36 bits of zero. Floating point numbers can represent +numbers with magnitude within the range 0.5*2^|-128 to +(1-2^|-27)*2^|127, and zero. + +A number that in which bit 0 is one and bits 9-35 are zero can +produce an incorrect result in any floating point operation. Any +word with a zero fraction and non-zero exponent can produce extreme +loss of precision if used as an operand in a floating point addition +or subtraction. + +In KI10 (and KL10) double precision floating point, a second word is +included which contains in bits 1:35 an additional 35 fraction bits. +The additional fraction bits do not significantly affect the range of +representable numbers, rather they extend the precision. + +The KA10 lacks double precision floating point hardware, however +there are several instructions by which software may implement double +precision. These instructions are DFN, UFA, FADL, FSBL, FMPL, and +FDVL. Users of the KL10 are strongly advised to avoid using these +intructions. + +In the PDP-6 floating pointing is somewhat different. Consult a wizard. + + + |AD add | result to AC +F floating |SB subtract |R rounded |I Immediate. result to AC + |MP multiply | |M result to memory + |DV divide | |B result to memory and AC + | + | + | no rounding | result to AC + |L Long mode + |M result to memory + |B result to memory and AC + + + |AD add +DF double floating |SB subtract + |MP multiply + |DV divide + + +Note: In immediate mode, the memory operand is . In long mode +(except FDVL) the result appears in AC and AC+1. In FDVL the AC +operand is in AC and AC+1 and the quotient is stored in AC with the +remainder in AC+1. + +Other floating point instructions: + +FSC (Floating SCale) will add E to the exponent of the number in AC +and normalize the result. One use of FSC is to convert an integer in +AC to floating point (but FLTR, available in the KI and KL is better) +To use FSC to float an integer, set E to 233 (excess 200 and shift +the binary point 27 bits). The integer being floated must not have +more than 27 significant bits. FSC will set AROV and FOV if the +resulting exponent exceeds 127. FXU (and AROV and FOV) will be set +if the exponent becomes smaller than -128. + +DFN (Double Floating Negate) is used only to negate KA10 software +format double precision floating point numbers. DFN treats AC and E +as a KA10 double floating point number which it negates and stores +back. AC is the high order word. Usually the low order word is in +AC+1, so the instruction most often appears as DFN AC,AC+1. + +UFA (Unnormalized Floating Add) is used in KA10 to assist in software +format double precision arithmetic. UFA will add C(AC) to C(E) and +place the result in AC+1. The result of UFA will not be postnormalized +unless in original operands the exponents and signs were the same and +a fraction with magnitude greater than or equal to 1 was produced. Only +in this case will a one step normalization (right shift) occur. +UFA will overflow in the same circumstances as FAD. Underflow is not +possible. + +FIX will convert a floating point number to an integer. If the +exponent of the floating point number in C(E) is greater than +(decimal) 35 (which is octal 243) then this instruction will set AROV +and not affect C(AC). Otherwise, convert C(E) to fixed point by the +following procedure: Move C(E) to AC, copying bit 0 of C(E) to bits +1:8 of AC (sign extend). Then ASH AC by X-27 bits (where X is the +exonent from bits 1:9 of C(E) less 200 octal). +FIX will truncate towards zero, i.e., 1.9 is fixed to 1 +and -1.9 is fixed to -1. + +FIXR (Fix and round) will convert a floating point number to an +integer by rounding. If the exponent of the floating point number in +C(E) is greater than (decimal) 35 (which is octal 243) then this +instruction will set AROV and not affect C(AC). Otherwise, convert +C(E) to fixed point by the following procedure: Move C(E) to AC, +copying bit 0 of C(E) to bits 1:8 of AC (sign extend). Then ASH AC +by X-27 bits (where X is the exponent from bits 1:9 of C(E) less 200 +octal). If X-27 is negative (i.e., right shift) then the rounding +process will consider the bits shifted off the right end of AC. If +AC is positive and the discarded bits are >=1/2 then 1 is added to AC. +If AC is negative and the discarded bits are >1/2 then 1 is added to +AC. Rounding is always in the positive direction: 1.4 becomes 1, 1.5 +becomes 2, -1.5 becomes -1, and -1.6 becomes -2. + +FLTR (FLoaT and Round) will convert C(E), an integer, to floating +point and place the result in AC. The data from C(E) is copied to AC +where its is arithmetic shifted right 8 places (keeping the bits that +fall off the end) and the exponent 243 is inserted in bits 1:8. The +resulting number is normalized until bit 9 is significant +(normalization may result in some or all of the bits that were right +shifted being brought back into AC). Finally, if any of the bits +that were right shifted still remain outside the AC the result is +rounded by looking at the bit to the right of the AC. + +PDP-10 Node: Shifting, Previous: Floating Point, Up: Top, Next: Byte + +Shift instructions + +The following instructions shift or rotate the AC or the double word +formed by AC and AC+1. + +Shift instructions are all immediate instructions. The effective +address is not used as the address of a memory operand. Instead, it is +used as the number of places to shift. A positive number means a left +shift; a negative number (bit 18 = 1) means a right shift. + +Aside from the sign bit of the effective address, only the lowest +eight bits are used. The other nine bits are ignored. + + +LSH Logical Shift. C(AC) is shifted as specified by E. Zero + bits are shifted into the AC. + +LSHC Logical Shift Combined. C(AC AC+1) is shifted as a 72 bit + quantity. Zero bits are shifted in. + +ASH Arithmetic Shift. Bit 0 is not changed. In a left + shift zero bits are shifted into the right end of AC. + In a left shift, if any bit of significance is shifted + out of bit 1, AROV (overflow) is set. In a right shift, + bit 0 is shifted into bit 1. + +ASHC Arithmetic Shift Combined. AC bit 0 is not changed. If + E is non zero, AC bit 0 is copied to AC+1 bit 0. + C(AC AC+1) is shifted as a 70 bit quantity. In a left + shift zero bits are shifted into the right end of AC+1. + In a left shift, if any bit of significance is shifted + out of AC bit 1 then AROV is set. In a right shift AC + bit 0 is shifted into AC bit 1. + +ROT Rotate. The 36 bit C(AC) is rotated. In a left rotate bits + shifted out of bit 0 are shifted into bit 35. In a right + rotate, bit 35 is shifted into bit 0. + +ROTC Rotate Combined. AC and AC+1 are rotated as a 72 bit + quantity. In a left rotate AC bit 0 shifts into AC+1 + bit 35 and AC+1 bit 0 shifts into AC bit 35. In a right + rotate, AC+1 bit 35 shifts into AC bit 0, etc. + +JFFO Jump if Find First One. This is not actually a shift + instruction, but it is a related sort of thing. It counts the + number of leading zeros in the contents of AC, and stores this + number in AC+1. (If AC contains zero, the number stored in + AC+1 is zero, not 36). The instruction also jumps to its + effective address if C(AC) # 0 (in other words, if it + succeeded in finding the first one bit). + +Example: + +;Suppose that each bit in accumlator 1 is a flag +;telling us that some sort of processing needs to be done. +;We would like to find out which flags are set +;and, for each one, do the processing. But we don't want to +;waste a lot of time checking flags which are not set. + +LOOP: JFFO 1,[JRST @TABLE(2)] + ... ;Here all flags are zero. + +TABLE: FOO ;FOO handles flag bit 0 + BAR ;BAR handles flag bit 1. + ... ;Other addresses for the remaining flags. + +FOO: ... ;Do the work. + TLZ 1,400000 ;Clear flag bit 0 + JRST LOOP ;Find the next flag which is set. + +PDP-10 Node: Byte, Previous: Shifting, Up: Top, Next: Logical Tests + +Byte instructions + +In the PDP-10 a "byte" is some number of contiguous bits within one +word. A byte pointer is a quantity (which occupies a whole word) +which describes the location of a byte. There are three parts to the +description of a byte: the word (i.e., address) in which the byte +occurs, the position of the byte within the word, and the length of +the byte. + +A byte pointer has the following format: + + 000000 000011 1 1 1111 112222222222333333 + 012345 678901 2 3 4567 890123456789012345 + _________________________________________ +| | | | | | | +| POS | SIZE |U|I| X | Y | +|______|______|_|_|____|__________________| + +POS is the byte position: the number of bits from the right end of +the byte to the right end of the word. + +SIZE is the byte size in bits. + +The U field is ignored by the byte instructions. + +The I, X and Y fields are used, just as in an instruction, to compute +an effective address which specifies the location of the word +containing the byte. + +Here are the byte instructions. + +LDB - Load byte. The contents of the effective address of the LDB +instruction is interpreted as a byte pointer. The byte described +there is loaded, right adjusted, into the AC. The rest of the AC is +cleared. + +DPB - Deposit byte. The contents of the effective address of the DPB +instruction is interpreted as a byte pointer. The byte described +there is deposited from the byte of the same size at the right end of +the AC. AC and the remainder of the word into which the byte is +deposited are left unchanged. + +IBP - Increment byte pointer. The purpose of this instruction is to +advance a byte pointer to point at the next consecutive byte. The AC +field must be zero. The contents of the effective address are +fetched. The POS field is changed by subtracting the size field from +it. If the result of the subtraction is greater than or equal to +zero, store the difference in the POS field. If the difference is +negative, add 1 to the Y field (in the KA10 and PDP-6 if Y contains +777777 then this will carry into the X field; in the KI10 and KL10 the +carry out is suppressed) and set POS field to 44-SIZE (44 is octal). +The effect of this is to modify the byte pointer to address the next +byte (of the same size) that follows the byte addressed by the +original pointer. + +ILDB - Increment and Load Byte. Increment the byte pointer contained +at the effective address. Then perform a LDB function using +the updated byte pointer. + +IDPB - Increment and Deposit Byte. Increment the byte pointer contained +at the effective address. Then perform a DPB function using +the updated byte pointer. + +Text strings are typically stored using seven-bit bytes, five per +word. ILDB and IDPB can then be used to step through a string. The +byte pointer should be initialized to 440700,,
. +Then the first ILDB will increment it to point at the first character +of the string. + +Byte pointers can be decremented by special use of the IBP +instruction. However, since this instruction is not available on all +cpu's, use of the following midas macro is recommended instead. + +DEFINE DBP7 AC + ADD AC,[070000,,0] + TLNE AC,400000 + SUB AC,[430000,,1] +TERMIN + +Note that this only works for 7 bit bytes and only if the byte pointer +is in an accumulator. + +PDP-10 Node: Logical Tests, Previous: Byte, Up: Top, Next: Boolean + +Logical Testing and Modification. + +The Test instructions are for testing and modifying bits in an +accumulator. There are 64 test instructions. Each one's name is +T followed by three modifiers. + + + |R right half immediate +Test accumulator |L left half immediate + |D direct mask + |S swapped mask + + |N no modification + |Z zero selected bits + |O set selected bits to One + |C complement selected bits + + | never skip + |N skip unless all selected bits are zero + |E skip if all selected bits are zero + |A skip always + +The test operation considers two 36 bit quantities. One of these is +the contents of the selected AC. The other quantity, called the +mask, depends on the first modifier letter. For R the mask is +<0,,E>; for L it is . For D the mask is C(E), and for S the +mask is CS(E), the swapped contents of E. + +If the skip condition N is specified, then the test instruction will +skip if the AND of the mask and the AC operand is Not equal to zero. + +If the skip condition E is specified, then the test instruction will +skip if the AND of the mask and the AC operand is Equal to zero. + +If the modification code Z appears then bits that are one in mask are +made zero in the AC. + +If the modification code O appears then bits that are one in mask are +made one in the AC. + +If the modification code C appears then bits that are one in mask are +complemented in the AC. + +Note that the skip condition is determined on the basis of the +contents of the AC before it is modified. + +The principle use for the Test instructions is in testing and +modifying single bit flags that are kept in an accumulator. + + +Example: + + TRON 5,FOOFLG ;Where FOOFLG has the value 200 + +This turns on the 200 bit in the right half of accumulator 5, +and skips if the bit was already on. + +PDP-10 Node: Boolean, Previous: Logical Tests, Up: Top, Next: PC Format + +Boolean Logic + +There are 16 possible boolean functions of 2 variables. The PDP-10 +has 16 instruction classes (each with 4 modifiers) that perform these +operations. Each boolean function operates on the 36 bits of AC and +memory as individual bits. + +C(AC) 0 0 1 1 +C(E) 0 1 0 1 + +SETZ 0 0 0 0 SET to Zero +AND 0 0 0 1 AND +ANDCM 0 0 1 0 AND with Complement of Memory +SETA 0 0 1 1 SET to AC +ANDCA 0 1 0 0 AND with Complement of AC +SETM 0 1 0 1 SET to Memory +XOR 0 1 1 0 eXclusive OR +IOR 0 1 1 1 Inclusive OR +ANDCB 1 0 0 0 AND with Complements of Both +EQV 1 0 0 1 EQuiValence +SETCM 1 0 1 0 SET to Complement of Memory +ORCM 1 0 1 1 OR with Complement of Memory +SETCA 1 1 0 0 SET to Complement of AC +ORCA 1 1 0 1 OR with Complement of AC +ORCB 1 1 1 0 OR with Complements of Both +SETO 1 1 1 1 SET to One + + +Each of the 16 instructions above have four modifiers that specify +where to store the result. No modifier means result to AC. Modifier +I means Immediate: the memory data is <0,,E> and the result goes to +AC. M as a modifier means result should be stored in memory. B +means store the results in both memory and AC. + +PDP-10 Node: PC Format, Previous: Boolean, Up: Top, Next: Jumps + +PC format. + +A subroutine call instruction is one which changes the PC (jumps) but +stores the old value of the PC so that the subroutine can "return" +(jump back) when it is done. There are several subroutine call +instructions on the PDP-10, but they all store the PC in the same +format: + + 0 0 0 0 0 0 0 0 0 0 1 1 1 11111 112222222222333333 + 0 1 2 3 4 5 6 7 8 9 0 1 2 34567 890123456789012345 + __________________________________________________ +|A|C|C|F|F|U|I|P|A|T|T|F|D| | | +|R|R|R|O|P|S|O|U|F|R|R|X|C|00000| PC | +|O|Y|Y|V|D|E|T|B|I|A|A|U|K| | | +|V|0|1| | |R| |L| |P|P| | | | | +| | | | | | | | | |2|1| | | | | +|_|_|_|_|_|_|_|_|_|_|_|_|_|_____|__________________| + + +The right half is the PC proper -- the address of the next instruction +to be executed (the one which follows the subroutine call +instruction). The other individual bits are the status flags. They +are stored in case the subroutine wants to restore them when it +returns. + +AROV, ARithmetic OVerflow, is set by any of the following: + A single instruction has set one of CRY0 or CRY1 without + setting them both. + An ASH or ASHC has left shifted a significant bit out of + AC bit 1. + A MULx instruction has multiplied -2^|35 by itself. + A DMUL instruction has multiplied -2^|70 by itself. + An IMULx instruction has produced a product less than + -2^|35 or greater than 2^|35-1. + A FIX or FIXR has fetched an operand with exponent + greater than 35. + FOV (Floating Overflow) has been set. + DCK (Divide ChecK) has been set. + +CRY0, short for Carry 0, means that there was a carry out of bit 0 in +an addition. Note that a carry out of bit 0, with no accompanying +carry out of bit 1 (into bit 0), causes AROV to be set. +The precise conditions which can set CRY0 and not CRY1 are: + An ADDx has added two negative numbers with sum less + than -2^|35. + A SUBx has subtracted a positive number from a negative + number and produced a result less than -2^|35. + A SOSx or SOJx has decremented -2^|35. + +CRY1, short for Carry 1, means that there was a carry out of bit 1 in +an addition. Note that a carry out of bit 1, with no accompanying +carry out of bit 0, causes AROV to be set. +The precise conditions which can set CRY1 and not CRY0 are: + An ADDx has added two positive number with a sum greater + than 2^|35-1. + A SUBx has subtracted a negative number from a positive + number to form a difference greater than 2^|35-1. + An AOSx or AOJx instruction has incremented 2^|35-1. + A MOVNx or MOVMx has negated -2^|35. + A DMOVNx has negated -2^|70 + +In addition, the following non-overflow conditions set both CRY0 and +CRY1: + In ADDx both summands were negative, or their signs differed + and the postive one was greater than or equal to the + magnitude of the negative summand. + In SUBx the sign of both operands was the same and the AC + operand was greater than or equal to the memory + operand, or the AC operand was negative and the + memory operand was postive. + An AOJx or AOSx has incremented -1. + A SOJx or SOS has decremented a non zero number other + than -2^|35. + A MOVNx has negated zero. + +FOV, Floating point OVerflow, is set by any of: + In a floating point instruction other than FLTR, DMOVNx, + or DFN the exponent of the result exceeds 127. + FXU (Floating eXponent Underflow) has been set. + DCK (Divide ChecK) has been set by FDVx, FDVRx, or DFDV. + +FPD, First Part Done, is set when the processor responds to a +priority interrupt, after having completed the first part of a two +part instruction (e.g., ILDB). This flag is not usually of interest +to the programmer. + +USER is set while the processor is in user mode. In user mode, +various instruction and addressing restrictions are in effect. + +IOT, User IN-Out mode, (also called IOT User), is a special mode in +which some of the user mode instruction (but not addressing) +restrictions are removed. In this mode a user program may perform +the hardware I/O instructions. + +[PUBL, Public mode, signifies that the processor is in user public +mode or in exec supervisor mode. This bit exists only on standard +KL-10 systems; it is not on ANY ITS system.] + +AFI, Address Failure Inhibit, if this flag is set, address break is +inhibited for during the execution of the next instruction [KI10, +KL10 only]. + +TRAP2 - if bit 10 is not also set, pushdown overflow has occurred. +If traps are enabled, setting this flag immediately causes a trap. +At present no hardware condition sets both TRAP1 and TRAP2 +simultaneously. [KI10 KL10 only] + +TRAP1 - if bit 9 is not also set, arithemetic overflow has occurred. +If traps are enabled, setting this flag immediately causes a trap. +At present no hardware condition sets both TRAP1 and TRAP2 +simultaneously. [KI10 KL10 only] + +FXU, Floating eXponent Underflow, is set to signify that in a +floating instruction other than DMOVNx, FLTR, or DFN, the exponent of +the result was less than -128 and AROV and FOV have been set. + +DCK, Divide ChecK, signifies that one of the following conditions has +set AROV: + In a DIVx the high order word of the dividend was greater than + or equal to the divisor. + In an IDIVx the divisor was zero. + In an FDVx, FDVRx, or DFDV, the divisor was zero, or the + magnitude of the dividend fraction was greater than + or equal to twice the magnitude of the divisor fraction. + In either case, FOV is also set. + +Bits 13 through 17 of the PC word are always zero to facilitate the +use of indirect addressing to return from a subroutine. + +Bits 18 through 35 store an address that is one greater than the +address of the instruction that stores the PC. Thus, the PC word +points at the instruction immediately following the subroutine call. + +PDP-10 Node: Jumps, Previous: PC Format, Up: Top, Next: Obsolete + +Recommended jump instructions: PUSHJ, POPJ, JRST, JFCL, XCT. + +PUSHJ C(AC)<-C(AC)+<1,,1>; C(CR(AC))<-; PC<-E; + +PUSHJ (PUSH return address and Jump) is like PUSH except the data +that is pushed onto the top of the stack is the PC and flags word. +The PC that is stored is the PC of the instruction that follows the +PUSHJ. Then the PC is set to the effective address of the +instruction. Pushdown overflow results if the AC becomes positive +when it is incremented. + +------ + +POPJ PC<-CR(CR(AC)); C(AC)<-C(AC)-<1,,1> + +POPJ (POP return address and Jump) undoes PUSHJ. The right half of +the word at the top of the stack is loaded into the PC (the flags are +unchanged). Then the stack pointer is decremented as in POP. The +effective address of POPJ is ignored. Pushdown overflow obtains if +the AC becomes negative as a result of the subtraction. + +Programming hints: + +If a subroutine called by PUSHJ AC, wants to skip over the +instruction following the PUSHJ, the following sequence accomplishes +that result: + AOS (AC) ;AC better be non zero. + POPJ AC, + +If you must restore the flags that PUSHJ saved, the following +sequence should be used instead of POPJ: + POP AC,(AC) ;Adjust the stack + JRST 2,@1(AC) ;Restore flags and PC from old stack top. +However, this sequence has a timing error in that the word is released +from the stack while its contents are still needed. This can cause a +bug if you have any interrupt processing in your program. + +------ + +JRST, Jump and ReSTore, is an unconditional jump instruction. In +JRST, the AC field does not address an accumulator. Instead, the AC +is decoded to signify various things. + +JRST PC<-E; +JRST 2, PC<-E; flags are restored (see text); +JRST 10, PC<-E; Dismiss current priority interrupt; +JRST 12, PC<-E; restore flags and dismiss priority interrupt; + +If the AC field is zero, only a jump occurs. JRST is everyone's +favorite unconditional jump instruction (the only other one is JUMPA +which is more typing, also, on the KA-10 JUMPA is slower than +JRST). + +JRST 2, (i.e., JRST with AC field set to 2) signifies jump and +restore flags. (The assembler also recognizes the mnemonic JRSTF for +JRST 2,). If indirection is used in JRSTF, then the flags are +restored from the last word fetched in the address calculation. If +indexing is used with no indirection, the flags are restored from the +left half of the specified index register. If neither indexing nor +indirection is used in the address calculation the flags are restored +from the left half of the JRSTF itself! In a user mode program JRSTF +cannot clear USER nor can it set IOT User (it can however, clear IOT +User). + +The following are all illegal in user mode and are trapped as UUOs. + +JRST 4, (alternate mnemonic HALT) sets the PC from E and stops the +processor. + +JRST 10, is used to dismiss the current priority interrupt. Usually +JRST 12, is used for this purpose since JRST 10, fails to restore +flags. + +JRST 12, (an alternate mnemonic is JEN, jump and enable priority +interrupts) combines the functions of JRST 10, and JRST 2,. + +------ + +The JFCL (Jump on Flag and CLear) instruction is another case in +which the AC field is decoded to modify the instruction. The AC +field selects the four flags in PC bits 0 through 3. PC bits 0 to 3 +correspond to bits 9 to 12 in the JFCL instruction. JFCL will jump +if any flag selected by the AC field is a 1. All flags selected by +the AC field are set to zero. + +JFCL 0, since it selects no PC bits, is a no-op. + +JFCL 17, will clear all flags, and will jump if any of AROV, CRY0, +CRY1, or FOV are set. + +JFCL 1, (JFOV) jumps if FOV is set and clears FOV. + +JFCL 10, (JOV) jumps if AROV is set and clears AROV. + +------ + +XCT, the execute instruction, fetches the word addressed by the +effective address and executes that word as an instruction. In the +case of XCTing an instruction that stores a PC, the PC that is stored +is the address of the instruction that follows the XCT. If the +executed instruction skips, then that skip is relative to the XCT. +The AC field of the XCT should be zero. [In exec mode a non zero AC +field in an XCT is significant.] + +PDP-10 Node: Obsolete, Previous: Jumps, Up: Top, Next: Program + +Obsolete and not-recommended jump instructions: JSR, JSP, JSA and JRA + +The JSR and JSP instructions are two non-stack subroutine calls which +are typical of most non-stack machines. JSP stores the old PC in an +accumulator, and JSR stores it in a word at the beginning of the +subroutine. JSP is useful once in a while, such as, for a subroutine +whose job is to push or pop several words on the stack. JSR is useful +only for UUO-handlers. *Note JSR: UUO Handlers. PUSHJ and POPJ +should be used for all ordinary subroutines. + +JSA and JRA are a peculiar subroutine call and matching return which +were invented for PDP-6 Fortran. In most programs they are not used +at all. + +JSR C(E)<-; PC<-E+1; + +JSR, Jump to SubRoutine, stores the PC in the word addressed by the +effective address and jumps to the word following the word where the +PC is stored. This is the only PDP-10 instruction that stores the PC +and flags without modifying any ACs; however, it is non-reentrant, so +PUSHJ is favored in most cases. The usual return from a subroutine +called by a JSR is via JRST (or JRST 2,) indirect through the PC +word. (See JRST) + +------ + +JSP C(AC)<-; PC<-E; + +JSP, Jump and Save PC, stores the PC and flags in the selected +accumulator and jumps. + +------ + +JSA C(E)<-C(AC); C(AC)<-; PC<-E+1; + +JSA, Jump and Save AC, stores the AC in word addressed by the +effective address. Then the left half of the AC is set to the +effective address and the right half of AC is set to the return PC. +Then the PC is set to one greater than the effective address. The +JRA instruction unwinds this call. The advantage of this call is +that a routine may have multiple entry points (which is difficult to +do with JSR) and it's easy to find (and later to skip over) arguments +that follow the calling instruction (which is possible to do with +PUSHJ, but not quite so convenient). Among the disadvantages of this +call is that it is non reentrant, and it doesn't save flags. + +------ + +JRA C(AC)<-C(CL(AC)); PC<-E; + +JRA, Jump and Restore AC, is the return from JSA. If, e.g., a +subrountine is called with JSA AC, then the return is made by: + JRA AC,(AC). + +PDP-10 Node: MIT Machines, Previous: Obsolete, Up: Top, Next: Program + +The MIT PDP-10's running ITS (AI, ML, DM, MC) have been modified in several +ways, primarily to implement paging. AI, ML, and DM are KA-10 processors +and are therefore modified by the addition of an additional piece of +hardware called a paging box and some rewiring of the CPU; MC is an early +KL-10 (hardware does not support extended addressing) modified by changing +microcode. + +The paging modifications affect only the operating system (instructions XCTR +(execute relocated), LPM (load/store page map), etc.) although it is through +the mechanism of the paging box that the MAR-break facility is implemented, +which ITS makes available through .mara (in DDT, $I), as well as the JPC +(the last jump address), available in DDT as .JPC. + +Three user-level facilities were added to the AI 10: the Circ instruction, +the PC Index mode, and one-proceed mode. Circ AC,Efad (instruction 247) +rotates AC and AC+1 like Rotc except that (for positive Efad) bits leaving +the top of AC enter the TOP of AC+1 and bits leaving the bottom of AC+1 +enter the BOTTOM of AC. Thus Circ AC,36. reverses the bitstring AC|AC+1. +Caution: Circ is a No-op and not an Illegal-op on machines on which it is +not implemented. PC index mode (bit %PCX17=200 in LH of PC) uses the PC as +index when index 17 is specified. This was intended to be used for +position-independent code; as far as I know, no programs use it. +One-proceed mode (%PC1PR=400) interrupts the program after one instruction +is executed. It is used by DDT's ^N and ^^ commands. + +Of these user facilities, only one-proceed is available on ML and DM. + +The MC KL-10 has had its microcode modified to implement Circ and +one-proceed. The Extend instructions available on standard-microcode +KL's are not available on MC. Jfcl, Jrst, and Blt are compatible with +Dec's KL versions. + +This node added by S. Macrakis (Macrakis@MC). + +PDP-10 Node: Program, Previous: MIT Machines, Up: Top, Next: TTY Output + +A Trivial Complete MIDAS Program. + +This program stores, in each word of TABLE, the index of that word. +Thus, the 0th word gets 0, the next gets 1, etc. + + TITLE COUNT + +A=1 ;Define a name for an accumulator. + +START: MOVSI A,-100 ;initialize loop counter. + ;A contains -100,,0 +LOOP: HRRZM A,TABLE(A) ;Use right half of A to index. + AOBJN A,LOOP ;Add 1 to both halves (-77,,1 -76,,2 etc.) + ;Jump if still negative. + .VALUE ;Halt program. + +TABLE: BLOCK 100 ;Assemble space to fill up. + +END START ;End the assembly. + +NOTES: + +*) TITLE: Every MIDAS program should begin with a TITLE statement +which contains the program's name. + +*) A: It is best to give names to the accumulators you use. +Single character names are good except for an AC which is used +generally for the same thing throughout the program. Define a +symbolic name for an AC with the assembler operator =. + +*) Comments: Everything after a semicolon is a comment, until the end +of the line. Actually, this is only true if the semicolon is not +inside of a text string of some sort. + +*) START: This is a label, which marks a location in the program. The +label START is used to name the instruction at which the program +should start running. The name START itself is nothing special. +MIDAS knows to use the value of START as the starting address because +START is the operand of the END statement. + +*) .VALUE: This is a system call instruction. It halts the program, +and makes DDT print out the program counter. All system calls have +names starting with a period. + +*) BLOCK: The BLOCK n statement reserves n words of storage, which are +not initialized. + +*) END: The end statement tells MIDAS that the assembly input is +finished, and its argument says what address to start the program at. +In this case the argument is the value of the label START. + +PDP-10 Node: TTY Output, Previous: Program, Up: Top, Next: Reverse + +Print the string FOO on the terminal. + + TITLE PRINT +A=1 ;Symbolic AC names are defined + +CHTTYO==1 ;Channel for output + ;== means don't use this symbol + ;for symbolic typeout in DDT. + +START: ;Open TTY channel. + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAO,,CHTTYO] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL + .IOT CHTTYO,["F] ;Print F. + .IOT CHTTYO,["O] ;Print O. + .IOT CHTTYO,["O] ;Print O. + .VALUE ;Halt program. + +END START ;Tell MIDAS this is the end of the text + ;and specify the address to start execution. + +NOTES: + +*) CHTTYO: All I/O is done by means of I/O channels, of which +there are sixteen, numbered 0 through 17. It is best to make symbolic +names for the channels you use and start them all with CH. Define +these names with the MIDAS operator ==, which is like = except that +symbols defined with == are not used for symbolic typeout by the DDT +debugger. We call this "half killing". In this example, CHTTYO and A +both have the value 1, but CHTTYO is half killed, so 1 will always be +output symbolically as A. This is the desired result, because +references to address or AC 1 are more likely to mean A than CHTTYO. + +*) .CALL: This is an ITS symbolic system call. Its format is, in +general, + + .CALL [SETZ ? SIXBIT/callname/ ? arguments ((SETZ))] + +(Note that the value of SETZ is a word with just the sign bit set). +The [ ... ] construct is a literal, and "?" is equivalent to a line +separator. Thus, the .CALL instruction is assembled with an address +field that points at a block of words containing a SETZ, a +SIXBIT/OPEN/, and finally the arguments. ((SETZ)) is a magic +assembler incantation which sets the sign bit of the last argument +word. + +The arguments are simply addresses of words containing data for the +system call. There are other kinds of arguments, but we won't get +into that. See .INFO.;.CALLS > for more information on symbolic +system calls. + +A symbolic system call skips if it is successful. + +*) OPEN: This is the name of the symbolic system call used in the +example. The OPEN call is used to open an I/O channel so it can be +used for I/O. It requires two arguments: the first one containing the +channel number and the I/O mode, and the second one containing the +device name in SIXBIT. In this example, the channel is CHTTYO, the +mode is .UAO (unit ascii output), and the device name is TTY, for the +terminal. + +Most OPENs will also specify two filenames and a directory name as +additional arguments, but for device TTY they are not necessary. + +*) .LOSE %LSFIL: This is a system call which prints an error message +and halts. It is designed to be used as the instruction following an +OPEN or other symbolic system call which deals with an I/O channel. +Sophisticated programs can recover from failing system calls, and +sometimes the failure should simply be ignored, but often it is +easiest just to use .LOSE %LSFIL. This instruction is indented +because it can be skipped over. + +*) .IOT: This is the system call for doing actual I/O. It is an +instruction whose AC field should be the I/O channel and whose address +points to a word containing a character to be output. "F in MIDAS +represents the code for the character F. + +PDP-10 Node: Reverse, Previous: TTY Output, Up: Top, Next: Even-Odd + +The following program accepts a line of input (terminated by Carriage +Return) from the terminal and outputs that line with the characters +reversed. + + TITLE REVERSE +A=1 ;Symbolic AC names are defined +P=17 + +CHTTYO==1 ;Channel for output +CHTTYI==2 ;Channel for input + +PDLLEN==100 ;Length of push down stack +PDL: BLOCK PDLLEN ;Storage for push down stack + +BEG: MOVE P,[-PDLLEN,,PDL-1] ;Initialize stack pointer. + ;Open TTY channels. + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAI,,CHTTYI] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAO,,CHTTYO] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL +LOOP: .IOT CHTTYO,["*] ;Prompt for input. + PUSHJ P,REVERS ;do the work, once. + .IOT CHTTYO,[^M] ;Output CRLF to go to new line. + .IOT CHTTYO,[^J] + JRST LOOP ;Jump back to repeat. + +REVERS: .IOT CHTTYI,A ;Read a character. + CAIN A,^M ;If it is a Carriage Return, the line is ended, + POPJ P, ;so return. + PUSH P,A ;Else save this character on the stack, + PUSHJ P,REVERS ;call REVERS recursively, + POP P,A ;get our character back + .IOT CHTTYO,A ;and print it. + POPJ P, ;Return. + +END BEG + +NOTES: + +*) OPEN: Two tty channels are used, one for input and one for output. +The input channel is opened in mode .UAI (unit ascii input) and the +output channel in mode .UAO (unit ascii output). The mode is how the +system knows whether to make an input channel or an output channel. + +*) .IOT: It works on input channels as well as on output channels. +On an input channel, it stores the input character (or word) in the +addressed memory location. The ITS system does not do any sort of +input editing or "rubout processing". There is a library for that: +SYSENG;RUBOUT >. + +*) To output "go to a new line", output a ^M followed by a ^J. ^M is +the MIDAS syntax for the code for Control-M, which is Carriage Return. +When the user types Carriage Return, which echoes as going to a new +line, it is read by the program as only one character, a ^M. + +*) The subroutine REVERS is recursive. The first call to REVERS reads +the first character, calls REVERS (to read the rest of the line and +print it in reverse), and lastly prints the first character. Over the +recursive call to REVERS, the first character is saved on the stack. + +*) P is the conventional name for the AC used as the stack pointer. +The MOVE instruction is there to set it up with a pointer to the +allocated area. Initializing a stack is one of the first things any +real program should do. + +From then on, calling is done with PUSHJ and returning with POPJ. +PUSH is used to save data on the stack, and POP to get it back. + +*) The POPJ P, which is skipped over by the CAIN is indented one extra +column. If there is a sequence of skipping instructions, each +instruction is indented one more than the previous one. + +It is reasonable to omit the address field of an instruction if it is +unused (as it is in a POPJ). An omitted field assembles as zero, but +you should never omit a field which is supposed to contain a zero +which actually stands for something. For example, instructions which +refer to AC zero should always have a zero (or better, a symbolic +name) where the AC is referred to. + +PDP-10 Node: Even-Odd, Previous: Reverse, Up: Top, Next: Even-Odd-Vowels + +Separate Even-Numbered and Odd-Numbered Characters. Rubout Processing. + +The following program accepts a line of input (terminated by carriage return) +from the terminal and outputs the even (i.e., every second) letters followed +by the odd letters. Also, we exhibit a subroutine for reading a line from the +terminal with rubout processing. + + + TITLE EVEN ODD +A=1 ;Symbolic AC names are defined. +B=2 +P=17 + +CHTTYO==1 ;Channel for output +CHTTYI==2 ;Channel for input + +PDLLEN==100 ;Length of push down stack +PDL: BLOCK PDLLEN ;Storage for push down stack + +BUF: BLOCK 30 ;Storage for 79 characters at 5 per word. +LINBUF: BLOCK 30 +LINBFE:: + +START: MOVE P,[-PDLLEN,,PDL-1] + ;Open TTY channels. + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAI,,CHTTYI] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAO,,CHTTYO] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL +L: PUSHJ P,GETLIN ;Read in an entire line. + MOVE C,[440700,,LINBUF] ;Initialize byte pointer to fetch from the line. + MOVE B,[440700,,BUF] ;Initialize byte pointer to store into BUF. +;Get odd-numbered character. +L1: ILDB A,C ;Get next odd-numbered char of the line. + JUMPE A,L2 ;Zero byte => end of line. + IDPB A,B ;Store char in BUF. Advance pointer first. +;Get even-numbered character. + ILDB A,C ;Get next even-numbered char of the line. + JUMPE A,L2 ;Zero byte => end of line. + .IOT CHTTYO,A ;Output this character right away. + JRST L1 ;Keep going till we get a terminator. + +L2: MOVEI A,0 ;Store a terminating character (code zero) + IDPB A,B ;at the end of the string in BUF. + ;Now it is an ASCIZ string. + MOVEI A,BUF + PUSHJ P,OUTSTR ;Call OUTSTR to output ASCIZ string + ;starting at address in A. + MOVEI A,[ASCIZ / +/] + PUSHJ P,OUTSTR ;Output a CRLF. + JRST L + +;Subroutine to read in a line and store it in LINBUF, +;checking to avoid running past the end of LINBUF. +;Discards the line terminator and leaves a null (zero) character +;in the buffer at the end of the string. +;Rubout cancels one character. +;Clobbers A and B. +GETLIN: .IOT CHTTYO,["*] ;Prompt for input. + MOVE B,[440700,,LINBUF] +GETLI1: .IOT CHTTYI,A ;Read the character + CAIN A,177 ;Rubout means cancel one character. + JRST GETLRB + CAIN A,^M ;Carriage Return ends the line. + JRST GETLCR + CAME B,[100700,,LINBFE-1] ;Don't store if buffer almost full. + ;(Leaves room for one more char: + ; the zero at the end) + IDPB A,B ;Any other character is stored in the buffer. + JRST GETLI1 + +;Come here after reading the carriage return. +GETLCR: SETZ A, ;Store a zero to mark end of string - and make it ASCIZ. + IDPB A,B + POPJ P, + +;Come here after reading a rubout. +GETLRB: CAMN B,[440700,,LINBUF] ;Is there anything to rub out? + JRST GETLNL + LDB A,B ;Yes => get back the char we are cancelling + .IOT CHTTYO,A ;and print it out so user can see it. + ADD B,[070000,,] ;To discard char, decrement the byte pointer. + ;First increase position by one byte (7). + SKIPGE B ;If byte pointer negative, position is 44, + SUB B,[430000,,1] ;so change it to position 01 in previous word. + JRST GETLI1 + +;Come here after reading a rubout with nothing to rub out. +GETLNL: MOVEI A,[ASCIZ / +/] + PUSHJ P,OUTSTR ;Just output a blank line. + JRST GETLIN ;Prompt again. + +;Subroutine to output the ASCIZ string which starts +;in the word whose address is inA. Clobbers A. +OUTSTR: HRLI A,440700 ;Make A into a byte pointer to fetch string. +OUTST1: ILDB B,A ;Fetch next character of string into B. + JUMPE B,CPOPJ ;If it is the terminator, return. + .IOT CHTTYO,B ;Else output it + JRST OUTST1 ;and loop back. + +CPOPJ: POPJ P, ;Return. + + END START + +NOTES: + +*) "LINBFE::" defines the label LINBFE and half kills it. This is because +LINBFE and START are actually the same location; we would prefer to +have DDT print out START, so we tell it not to print out LINBFE. + +*) 440700: The correct left half for a byte pointer for 7-bit bytes +which points to the nonexistent character to the left of a word. The +first increment will make it point to the first actual character in +the word. This is the right way to start off the pointer because the +ILDB and IDPB instructions increment first. + +*) Decrementing a byte pointer: This is done in GETLRB. It is the +inverse of incrementing the byte pointer, as done by IBP, ILDB and IDPB. +The particular three-instruction sequence used above assumes that the +byte size is 7. It is not hard to adapt it to other byte sizes, but sizes +other than 7 are infrequent. + +*) GETLIN: This subroutine does "rubout processing". It gives the +user the ability to type in a line and make corrections within it. +This is a very simple rubout processor; the only editing character +it provides is Rubout, and it does not handle display terminals. +SYSENG;RUBOUT > is a rubout processor that you can use in practice. + +*) OUTSTR: A subroutine to print out an ASCIZ (ASCII, terminated by +a Zero) string. Most programs contain this subroutine under some name +or other. + +*) CPOPJ: Note that it is possible to conditionaly jump to a POPJ +instruction to do a conditional return from a subroutine. Every +subroutine in the program can use the same POPJ instruction for this, +and the traditional name for it is CPOPJ. You can assume when you see +the label CPOPJ used that it is the address of a POPJ P, instruction. + +PDP-10 Node: Even-Odd-Vowels, Previous: Even-Odd, Up: Top, Next: Even-Odd-Vowels 2 + +Separate Even Characters, Odd Vowels, and Odd Consonants. + +The following program accepts a line of input (terminated by carriage +return) from the terminal and outputs the even (i.e., every second) +letters followed by those odd letters that are not vowels, followed by +those odd letters that are vowels. + +In the program of example 4, take the code + +L2: MOVEI A,0 ;Store a terminating character (code zero) + IDPB A,B ;at the end of the string in BUF. + ;Now it is an ASCIZ string. + MOVEI A,BUF + PUSHJ P,OUTSTR ;Call OUTSTR to output ASCIZ string + ;starting at address in A. + MOVEI A,[ASCIZ / +/] + PUSHJ P,OUTSTR ;Output a CRLF. + JRST L + +and replace it with + +L2: MOVEI A,0 ;Deposit code zero to terminate string + IDPB A,B ;of odd letters (i.e., make it ASCIZ). + MOVE B,[440700,,BUF] ;Take pointer for odd letters + MOVE C,B ;Put pointer for vowels +L3: ILDB A,B ;Get one odd letter. + JUMPE A,L4 ;Stop scanning when we reach the zero. + PUSHJ P,ISVOW ;Is this a vowel? + JRST L3A ;No. + IDPB A,C ;Yes. Store for later. + JRST L3 + +L3A: .IOT CHTTYO,A ;Not a vowel, so output now. + JRST L3 + +L4: IDPB A,C ;Store code zero to end string of vowels. + MOVEI A,BUF + PUSHJ P,OUTSTR ;Output that string. + MOVEI A,[ASCIZ / +/] + PUSHJ P,OUTSTR ;Output a CRLF. + JRST L + +;Subroutine to skip if the character in A is a vowel. +ISVOW: CAIE A,"A ;If character is upper case A + CAIN A,"a ;or if it is lower case A, + JRST POPJ1 ;jump to a skip return. + CAIE A,"E ;Same for E, etc. + CAIN A,"e + JRST POPJ1 + CAIE A,"I + CAIN A,"i + JRST POPJ1 + CAIE A,"O + CAIN A,"o + JRST POPJ1 + CAIE A,"U + CAIN A,"u + JRST POPJ1 + CAIE A,"Y + CAIN A,"y + JRST POPJ1 + POPJ P, ;Not a vowel, so return with no skip. + +;Standard address of single skip return. +POPJ1: AOS (P) + POPJ P, + +NOTES: + +*) Skip return: A subroutine is said to skip return when it skips +the instruction which follows the call. The subroutine call instruction +therefore acts as a conditional skip instruction. Subroutines called +with a PUSHJ skip return by incrementing the return address where +it lives on the stack. Subroutines called in other ways implement +skip returning in other ways. + +*) POPJ1: This is the standard name for a place to jump to for a skip +return. Since the procedure for a skip return is independent of the +address of the subroutine, every subroutine can use the same POPJ1. +If you see the label POPJ1 used, you can assume it is a skip return of +the sort shown above. + +*) Consecutive skip instructions appear in ISVOW. When this happens, +each instruction that can be skipped is indented one more than the +previous one. + +If, on the other hand, you had a subroutine that could skip twice, and +followed it by two non-skipping instructions, each of those +instructions would be indented only once. + +*) Re-using the string: as we scan the string of odd characters for +vowels, we print out the consonants and store the vowels in a string +again, reusing the same space. The reason why it works to be reading +out one string and writing another string in the same memory space is +that we write at most one character for each character we read. So we +can never clobber a character that has not been read. Even if every +character is a vowel, the characters are stored into the bytes they +have just been read out of, so nothing is lost. + +*) Lower case letters make a difference in a character constant (such +as "a). + +PDP-10 Node: Even-Odd-Vowels 2, Previous: Even-Odd-Vowels, Up: Top, Next: Calculator + +Alternative Implementation of Previous Example. + +The following program performs the same function as the program written for +example 5. Some different ideas are shown here. + + TITLE EVEN ODD #2 +A=1 ;SYMBOLIC AC NAMES ARE DEFINED +B=2 +C=3 +D=4 +P=17 + +CHTTYO==1 ;Channel for output +CHTTYI==2 ;Channel for input + +PDLLEN==100 ;Length of push down stack +PDL: BLOCK PDLLEN ;Storage for push down stack + +BUF: BLOCK 30 ;Storage for 79 characters at 5 per word. +LINBUF: BLOCK 30 ;Read a line into here. +LINBFE:: + +START: MOVE P,[-PDLLEN,,PDL-1] + ;Open TTY channels. + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAI,,CHTTYI] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAO,,CHTTYO] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL +L: PUSHJ P,GETLIN ;Read in the line. + MOVE C,[440700,,LINBUF] ;Initialize byte pointer to fetch from the line. + MOVE B,[440700,,BUF] ;Initialize byte pointer to store into BUF. + SETZ D,0 ;Low bit of D is even-oddness of character. +L1: ILDB A,C ;Get next character. Jump if end of line. + JUMPE A,L2 + XORI D,1 ;Complement low bit -- count mod 2. + ;D is 1, then 0, then 1, then 0. + XCT XTBL(D) ;Execute an instruction to dispose of char. + JRST L1 + +XTBL: .IOT CHTTYO,A ;Even character non-vowel + IDPB A,B ;Odd character vowel + + +L2: MOVEI A,0 ;Deposit a null byte to make string + IDPB A,B ; of odd letters ASCIZ. + MOVE C,[440700,,BUF] ;Fetch the string. + MOVE B,C ;Store back only the vowels. +L3: ILDB A,C ;Get one odd letter. + JUMPE A,L4 ;Check for end of string. + PUSHJ P,ISVOW ;Test character, skip if vowel. + TDZA D,D ;Not a vowel, set D=0. + MOVEI D,1 ;Vowel. D=-1 + XCT XTBL(D) ;Dispose of char depending. + JRST L3 + +L4: IDPB A,B ;Store null to make string of odd vowels ASCIZ. + MOVEI A,BUF + PUSHJ P,OUTSTR ;Type out that string. + MOVEI A,[ASCIZ / +/] + PUSHJ P,OUTSTR + JRST L + +ISVOW: +IRPC ZZ,,[AEIOUY] + CAIE A,"ZZ + CAIN A,"ZZ+40 + JRST POPJ1 +TERMIN + POPJ P, + +;Copy the GETLIN and OUTSTR subroutines from above into here. + + END START + +NOTES: + +*) IRPC is an assembler macro operation which generates repetitive code +with small variations. In this case, the three instructions CAIE, +CAIN and JRST are repeated once for each vowel. The first time, ZZ is +replaced by A. The second, ZZ is replaced by Z. The sixth time, ZZ +is replaced by Y. As a result, the subroutine ISVOW in this example +is actually identical to the ISVOW in the previous example, though it +takes much fewer lines of source code. + +PDP-10 Node: Calculator, Previous: Even-Odd-Vowels 2, Up: Top, Next: FileIO + +Arithmetic Calculator Program. + +This program will read an arithmetic expression composed of numbers and the +operators +, -, * and /, compute the value and type the result. It shows how +to read and print numbers. It does not know about operator precedence; it +does operations from left to right always. + + TITLE NUM +FL=0 +A=1 +B=2 +C=3 +D=4 +P=17 + +CHTTYI==1 +CHTTYO==2 + +;FLAG NAMES +NEGF==1 +DIGF==2 + +PDLLEN==100 +PDL: BLOCK PDLLEN +OP1: 0 +X1: 0 + +LINBUF: BLOCK 30 +LINBFE:: + +LINPTR: 0 + +START: MOVE P,[-PDLLEN,,PDL-1] + ;Open TTY channels. + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAI,,CHTTYI] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL + .CALL [SETZ ? SIXBIT/OPEN/ + [.UAO,,CHTTYO] ? [SIXBIT/TTY/] ((SETZ))] + .LOSE %LSFIL +START1: PUSHJ P,GETLIN ;Read in a line of input. + MOVE A,[440700,,LINBUF] + MOVEM A,LINPTR ;Set up to fetch chars from the line. + PUSHJ P,EVAL ;Parse and evaluate expression. + PUSHJ P,DECOUT ;Print the answer. + MOVEI A,[ASCIZ/ +/] + PUSHJ P,OUTSTR + JRST START1 + +;Read and evaluate an expression. Value returned in A. +;Clobbers B. +EVAL: PUSHJ P,DECIN ;Read one number. + MOVEM B,OP1 ;Save the number. +EVAL1: MOVEI B,0 + CAIN A,"+ ;Consider the operation character: + MOVE B,[ADD B,OP1] ;B gets an instruction to do that operation. + CAIN A,"- + MOVE B,[SUB B,OP1] + CAIN A,"* + MOVE B,[IMUL B,OP1] + CAIN A,"/ + MOVE B,[IDIV B,OP1] + JUMPE B,EVALX ;If B is still 0, the terminator + ;was not an arith op, so it ends + ;the expression or is illegal. + MOVEM B,X1 ;It is an arith op, so save the instruction. + PUSHJ P,DECIN ;Read the second operand. + EXCH B,OP1 ;B gets first op, OP1 gets second operand. + XCT X1 ;Compute result of operation, in B. + MOVEM B,OP1 ;Save it as first operand of next operation. + JRST EVAL1 ;A has terminator of second operand, + ;which is the next operation. + +;Come here on number terminated by char not an arith op. +EVALX: JUMPN A,ERR ;Should be end of line, or it's an error. + MOVE A,OP1 ;Otherwise, last saved value is value of exp. + POPJ P, + +;Print an error message if we see something we don't recognize. +ERR: MOVEI A,[ASCIZ/Unrecognized character in expression: /] + PUSHJ P,OUTSTR + LDB A,LINPTR ;Print the offending character + .IOT CHTTYO,A ;as part of the error message. + MOVEI A,[ASCIZ / +/] + PUSHJ P,OUTSTR + JRST START1 + +;Read a signed decimal number out of the line, returning number in B +;and terminating character in A. +DECIN: TRZ FL,NEGF!DIGF ;No minus, no digit seen yet. + MOVEI B,0 +DECIN1: ILDB A,LINPTR ;Fetch next character of line. + CAIL A,"0 + CAILE A,"9 + JRST DECIN2 ;Jump if character not a digit. + IMULI B,10. ;Else accumulate this digit into the number. + ADDI B,-"0(A) ;Note that we convert the digit into its value. + ;("0 into the value 0, "1 into 1). + TRO FL,DIGF ;Set flag saying non-null number seen. + JRST DECIN1 + +DECIN2: CAIN A,"- + JRST DECIN3 ;Jump on minus sign. + TRNN FL,DIGF ;Anything else: if we saw a number, + POPJ P, ;negate it if it began with a minus sign. +DECIN4: TRZE FL,NEGF + MOVN B,B + POPJ P, + +;Come here after reading a minus sign. +DECIN3: TRNE FL,DIGF ;Does it follow a number? + JRST DECIN4 ;Yes. This must be a binary minus. + TRC FL,NEGF ;This must be unary minus. + ;Complement flag that number is negative. + JRST DECIN1 ;(So that two minus signs cancel out). + +;Print number in A, positive or negative, in decimal. +;Clobbers A and B. +DECOUT: JUMPGE A,DECOT1 + .IOT CHTTYO,["-] ;If number is negative, print sign. + CAMN A,[400000,,] ;Smallest negative number is a pain: + JRST DECOT2 ;its absolute value cannot fit in one word! + MOVM A,A ;Else get abs val of negative number and print. +DECOT1: IDIVI A,10. + HRLM B,(P) ;Save remainder in LH of stack word + ;whose RH contains our return address. + SKIPE A ;If quotient is nonzero, + PUSHJ P,DECOT1 ;print higher-order digits of number. + HLRZ A,(P) ;Get back this remainder (this digit). + ADDI A,"0 + .IOT CHTTYO,A + POPJ P, + +;Print the abs value of the largest negative number. +DECOT2: MOVEI A,[ASCIZ /34359738368/] + JRST OUTSTR + +;Copy the GETLIN and OUTSTR subroutines here. + + END START + +NOTES + +*) 10.: This is a decimal number. You can tell, because it ends +with a decimal point. + +*) LINPTR: this location holds the byte pointer used for fetching +characters out of the line. It is usually not worth while to keep +this pointer in an accumulator if the parsing is being done over +more than a very small piece of code. + +*) XCT: Note how EVAL chooses an arithmetic instruction based on +the arithmetic operator character, then reads the following argument, +and then executes the instruction chosen earlier, performing the +operation. This is also the first use you have seen of literals +containing instructions. + +*) ERR: this is an example of printing an error message. Error messages +should always show the offending data, not just say "something was wrong". + +*) DECIN: Note how flags in accumulator 0 (FL) are used to keep +track of whether any digits have been seen, and whether a minus +sign came before them. Accumulator 0 is most often used for such flags +because it is the least useful accumulator for anything else +(since it cannot be used as an index register). + +*) DECOUT: This is a very famous program for printing a number. +It works recursively because the first digits extracted as the remainders +in successive divisions by the radix are the last digits to be printed. +So the digits are produced and saved on the way down the recursion, +and printed on the way up. + +*) HRLM: We could save the remainder with PUSH P,B and restore it +with POP P,A, but since the left half of each word saved by a PUSHJ +is not really going to be used, we can save stack space by using those +left halves to store the remainder. It is also faster. + +PDP-10 Node: FileIO, Previous: Calculator, Up: Top, Next: Parsing + +Reading and Writing Files. + +As a preface to this example, several things need to be explained. +In the foregoing examples, only terminal I/O has been discussed. +Here we introduce disk file input. One important thing to emphasize +is the concept of device independence: most of the system calls for +disk i/o are the same as those for say, magtape i/o. Naturally, +different devices have different phyical characteristics; therefore, +some differences between devices will be apparent. Despite these +differences, a programmer really needs to learn only one set of basic +concepts for i/o. + +When doing i/o it is necessary to specify a physical device, a mode +(input vs. output, character vs. word), and possibly a file name to +select among "files" on a particular physical device. + +Each job can has sixteen i/o channels available, each of which can be +opened individually to do i/o. The same OPEN system call used to open +a tty channel is also used to open i/o channels for other devices. +OPEN also specifies the mode and filenames. + +You have already seen the use of modes .UAI (unit ascii input) and +.UAO (unit ascii output). Modes .UII and .UIO are "image" i/o instead +of ascii i/o. They transfer whole words instead of characters, and +are used for binary files. + +There are two filenames, and also a directory name. These are +specified as words of SIXBIT just like the device name. + +Once the channel is open, the .IOT UUO is used to transfer the +characters in or out, just as it is with the terminal. In fact, the +above examples could be adapted to read and write input using disk +files only by changing the two OPEN system calls. Alternatively, +filename translations could be used to translate device TTY: to a disk +file. + +Writing output to a file does require one thing that writing output to +the terminal does not need: you must "close" the channel when you are +finished to make the file appear properly on disk. Closing a terminal +i/o channel is also allowed but nobody bothers since it is not +necessary. Closing is done with the .CLOSE UUO, which takes the +channel number in its AC field just like .IOT. + +Here is an example which copies the file SYS;SYSTEM MAIL (the announcements +printed when DDT or PWORD starts up) to the file FOO BAR on your own directory. + + TITLE FILE COPY +A=1 + +CHDSKI==1 +CHDSKO==2 + +FCOPY: .CALL [ SETZ ? SIXBIT/OPEN/ + [.UAI,,CHDSKI] ? [SIXBIT/DSK/] ;Mode, channel and device name + [SIXBIT /SYSTEM/] ? [SIXBIT/MAIL/] ;Two filenames. + 400000,,[SIXBIT /SYS/]] ;Directory name. + .LOSE %LSFIL + .CALL [ SETZ ? SIXBIT/OPEN/ + [.UAO,,CHDSKO] ? [SIXBIT/DSK/] + [SIXBIT /FOO/] ? 400000,,[SIXBIT/BAR/]] ;Note no directory name! + ;The default (your working directory) is used. + .LOSE %LSFIL +LOOP: .IOT CHDSKI,A ;Read next input character. + JUMPL A,EOF ;Negative => there is none, it's eof. + .IOT CHDSKO,A ;Else write char to output file. + JRST LOOP + +EOF: .CLOSE CHDSKO, ;Make output file appear. + .CLOSE CHDSKI, ;Release input file (to be clean). + .LOGOUT 1, + + END FCOPY + +NOTES: + +*) This example is so simple that no stack is needed, and no tty channels. + +*) When reading from a file instead of the terminal, it is possible to reach the +end of the file. In ascii mode (.UAI), this is signalled by a negative value +returned by the .IOT. The right halfword of that value contains the character +^C, which is the standard end-of-file padding character on ITS. That is, ^C is +used to fill out the last word of a file of characters if that last word is not +entirely used up. In image mode, .IOT causes an error interrupt at end of file. +Usually binary files are read many words at a time using SIOT (which will be +explained soon) or block-mode .IOT; these do provide a way to detect end of file +without getting an error. + +*) .LOGOUT 1, is a convenient way to exit a program and kill the job. It kills +the job if it is running under DDT (the usual condition) and also kills the job +if it is disowned. When a program running under DDT does a .LOGOUT 1, DDT just +prints an asterisk. + +PDP-10 Node: Parsing, Previous: FileIO, Up: Top, Next: BufInput + +Parsing SIXBIT Words, and Filenames. + +Here is a sample routine which accumulates a word of sixbit, returning +it in B. Non-alphanumeric characters are terminators; the terminator +encountered is returned in A. + +We assume that GETCHR is a subroutine we can call +which reads a character of input and returns it in A. + +GETSIX: MOVEI B,0 + MOVE C,[440600,,B] ;Byte pointer for storing into B. +GETSX1: PUSHJ P,GETCHR ;Read next character into A. + CAIN A,40 + JRST [ JUMPE B,GETSX1 ;Flush leading blanks + POPJ P,] ;but trailing blanks are terminators. + CAIL A,"A+40 + CAILE A,"Z+40 + JRST .+2 ;Not lower case + TRZ A,40 ;Make lower case into upper case + CAIL A,"A + CAILE A,"Z + JRST .+2 + JRST GETSX2 ;Letters are ok in sixbit + CAIN A,^Q ;^Q means "quote": use the next char + JRST [ PUSHJ P,GETCHR ;even if it is usually a delimiter. + JRST GETSX2] + CAIL A,"0 + CAILE A,"9 + POPJ P, ;Not a letter, not a digit. This is delim. +GETSX2: SUBI A,40 ;Convert to sixbit + TLNE C,770000 ;Skip if already got 6 characters. + IDPB A,C ;Otherwise we would clobber C here. + JRST GETSX1 + +You don't need to write a filename parsing routine, because you can +use the library SYSENG;RFN >. However, RFN is a good example to read. + +PDP-10 Node: BufInput, Previous: Parsing, Up: Top, Next: BufPadding + +Sample Routine to do Buffered Input. + +So far, the i/o in the examples has always transferred a single character at a +time. When a large file is to be read or written, it is worth while to go to +the extra trouble of transferring many words or characters at a time. There are +two ways of doing this. The old way is to open the i/o channel in "block" mode +instead of "unit" mode: use mode .BAI, .BAO, .BII or .BIO instead of .UAI, .UAO, +.UII or .UIO. Then the .IOT UUO itself transfers a block of consecutive words +instead of a single word or character. The newer way is to open the channel in +unit mode as usual, and use the system call SIOT to transfer any number of +consecutive bytes. SIOT can be interspersed with individual .IOTs. + +We assume that INCHAN has a file open in mode .UAI, unit ascii input. +We use the SIOT system call to read many characters at a time into a +buffer, and then get them out of the buffer one by one. + +SIOT expects three arguments: a channel number, a byte pointer and a number of +characters. The byte pointer argument should not be a literal, because the +SIOT increments it past the characters that are transferred. The count is +also modified; it is decremented for all the characters transferred +(decremented down to zero if the transfer is completed). On input, if end of +file is encountered before the requested number of characters is obtained, +then the byte pointer and count indicate the characters actually transferred. + +INBFR: BLOCK INBFL ;This is the buffer. 40 to 200 is + ;good for INBFL (160. to 640. characters). + +INCNT: 0 ;Number of characters left in buffer + ;not fetched yet. +INPTR: 0 ;Byte pointer used for fetching characters + +;Read another input character into A. +;Skip if successful. No skip if EOF. +GETCHR: SOSL INCNT ;Anything left in the buffer? + JRST GETCH1 + PUSH P,A ;No - must read another load. + PUSH P,B + MOVE A,[440700,,INBFR] + MOVEM A,INPTR + MOVEI B,INBFL*5 + ;; Do the input. A says where to put it and B says how many chars. + .CALL [ SETZ ? SIXBIT /SIOT/ ? %CLIMM,,INCHAN + A ? 400000,,B] + ;Note "immediate" argument: + ;%CLIMM,,INCHAN is the same as [INCHAN]. + .LOSE %LSFIL + MOVNS B ;B is left with number of characters + ADDI B,INBFL*5 ;We wanted but didn't get. + MOVEM B,INCNT ;Compute how many chars we did get. + POP P,B + POP P,A + SOSG INCNT ;If we got none, it's eof. + POPJ P, +GETCH1: ILDB A,INPTR + AOS (P) + POPJ P, + +Note that this GETCHR routine is completely equivalent to + +GETCHR: .IOT INCHAN,A + SKIPGE A + AOS (P) + POPJ P, + +but the buffered one is much faster because it does not have to do a +system call for each character. If your program is supposed to +process characters quickly, use buffered input. If characters will be +read only infrequently and a few at a time, use simple input. + +PDP-10 Node: BufPadding, Previous: BufInput, Up: Top, Next: Debugging + +Buffered Input, Ignoring Padding. + +Sometimes it is desirable to ignore padding (^@ and ^C characters) at +the end of a file, but to consider ^@ and ^C characters within the +file to be significant. The convention usually used is that ^@ and +^C characters are only padding if they occur as the last few +characters in the last word of the file. A ^L character in the last +word followed only by ^C's or ^@'s is also to be ignored. Here is a +routine which claims to have hit end of file when it reaches the +padding. + +This routine works by saving the last word of each buffer load to be +processed at the front of the next buffer load. That is so that, when +we discover end of it is guaranteed that we have not yet processed the +last word of the file. So once we KNOW it is the last word, we can +look through it for padding and discard what we find. + +It is necessary to call the subroutine GETINI before reading the first +character of input. + +INBFR: BLOCK INBFL ;This is the buffer. 40 to 200 is + ;good for INBFL (160. to 640. characters). + +INCNT: 0 ;Number of characters left in buffer + ;not fetched yet. +INPTR: 0 ;Byte pointer used for fetching characters + +INAHED: 0 ;Look-ahead word. + ;The last word of previous bufferful + ;saved for the next bufferful. +INEOF: 0 ;-1 if this bufferful is the last. + +;Read another input character into A. +;Skip if successful. No skip if EOF. +GETCHR: SOSL INCNT ;Anything left in the buffer? + JRST GETCH1 ;Yes => just get it. + SKIPE INEOF ;If we discovered last time that there + POPJ P, ;is no more, it's eof now. + PUSHJ P,GETBUF ;Fill up the buffer again. + JRST GETCHR + +GETCH1: ILDB A,INPTR + AOS (P) + POPJ P, + +;Call this to initialize the buffer, before reading the first character. +;This is to ignore the look-ahead word, +;which is garbage the first time around. +GETINI: SETZM INEOF + SETZM INCNT + PUSHJ P,GETCHR ;So just read and throw away + PUSHJ P,GETCHR ;the supposed look-ahead chars. + PUSHJ P,GETCHR + PUSHJ P,GETCHR + PUSHJ P,GETCHR + POPJ P, + +GETBUF: PUSH P,A + PUSH P,B + MOVE A,INAHED ;Our previous look ahead word is now + MOVEM A,INBFR ;our first word of input. + MOVE A,[440700,,INBFR] ;That's where we should start fetching. + MOVEM A,INPTR + HRLI A,010700 ;The SIOT should start AFTER that word. +;Note that it is essential that we set up A with +;010700,,INBFR rather than 440700,,INBFR+1 +;because of how GETCH4 decrements the byte pointer. +;We are being compatible with SIOT, which also returns +;a byte pointer of the form 010700 rather than 440700. + MOVEI B,INBFL*5-5 ;from the file, but not that word. + ;; Do the input. A says where to put it and B says how many chars. + .CALL [ SETZ ? SIXBIT /SIOT/ ? %CLIMM,,INCHAN + A ? 400000,,B] + .LOSE %LSFIL + JUMPE B,GETCH2 ;Didn't get all we asked for => + SETOM INEOF ; this is the last we will get. + MOVNS B ;B is left with number of characters + ADDI B,INBFL*5 ;we wanted but didn't get. + MOVEM B,INCNT ;Compute how many chars we did get. +;We are now certainly at eof, and the last word of the file +;is now in the buffer. +GETCH4: ADD A,[070000,,] ;Back up to last character. + JUMPL A,GETCH3 ;When we get to left edge of word, + ;we have examined the entire last word, + ;so there is no more padding. + LDB B,A + CAIE B,^C ;Any number of ^@ and ^C chars is padding. + CAIN B,^@ + JRST [ SOS INCNT ;So officially say it is not there. + JRST GETCH4] + CAIN B,^L ;A ^L followed by padding is padding + SOS INCNT ;but nothing before it is padding. + JRST GETCH3 + +;If we did fill the buffer, we must save one word as look-ahead +;for next time. +GETCH2: MOVE A,INBFR+INBFL-1 ;Save the last word we got. + MOVEM A,INAHED + MOVEI A,INBFL*5-5 ;Don't include those 5 chars + MOVEM A,INCNT ;in the count of how many are in the bfr. +GETCH3: POP P,B + POP P,A + POPJ P, + +Both of these examples are suited to reading input from a disk file. +If you are reading input from the terminal, you probably want to do +rubout processing for the user's sake. There is a library you can use +for this; read the file SYSENG;RUBOUT >. + +PDP-10 Node: Debugging, Previous: BufPadding, Up: Top + +Debugging Programs with DDT + +When you try your program for the first time, it won't work. + +To find out why, you use the debugging features of DDT. DDT is +completely documented (*Note DDT: (DDT).) but this is a summary of +what sorts of things you can do with it. + +DDT allows you to set a "breakpoint". Setting a breakpoint at an +instruction means that when the program gets to that instruction it +will stop (return to DDT). At this time you can examine any location +in the program to see if everything is working right so far. +If it is still working right, you can set another breakpoint, later +on, and continue the program. + +When you see that a variable contains a "wrong" value, DDT allows you +to correct the value and continue, from the breakpoint or from +someplace else. You can also change instructions in the program with +DDT so that you can keep running the program and the problem will not +happen again (but don't forget to make the correction in the source +file as well!) DDT allows can print instructions and addresses using +the symbols you define in the program, and it understands those +symbols in your input as well. + +You can also ask DDT to run your program one instruction at a time. +This is often easier than setting breakpoints. + +You don't have to do anything special to be able to use DDT on your +program, because DDT is always available on ITS. It is the program +which processed your command to run INFO. You probably don't want to +set any breakpoints in INFO, but if you type C-Z now you could do just +that. + +Homework # 1 answer + + TITLE IGPAY ATINLAY +A=1 ;SYMBOLIC AC NAMES ARE DEFINED +B=2 +C=3 +D=4 +P=17 +PDLEN==100 ;LENGTH OF PUSH DOWN LIST +PDLIST: BLOCK PDLEN ;STORAGE FOR PUSH DOWN LIST +BUFR: BLOCK 30 ;STORAGE FOR 79 CHARACTERS + + +START: RESET + MOVE P,[IOWD PDLEN,PDLIST] +L: OUTCHR ["*"] ;PROMPT FOR INPUT + MOVE B,[POINT 7,BUFR] +L1: PUSHJ P,DOWORD + CAIE A,12 ;DELIMITER SEEN? + JRST L1 + OUTSTR [BYTE(7)15,12] + JRST L + +DOWORD: MOVE B,[POINT 7,BUFR] + SETZM BUFR + PUSHJ P,GETLTR + JRST EPOPJ ;NON LETTER +DOWRD1: PUSHJ P,ISVOW ;IS IT A VOWEL? + JRST .+2 + JRST DOWRD3 ;YES. + IDPB A,B ;CONSONANT AT FRONT OF WORD + PUSHJ P,GETLTR + TDZA C,C ;"WORD" HAS ALL CONSONANTS + JRST DOWRD1 + IDPB C,B ;STORE ZERO TO MAKE ASCIZ + OUTSTR BUFR +EPOPJ: CAIE A,12 + OUTCHR A ;TYPE NON LETTER + POPJ P, + +DOWRD2: OUTCHR A ;HERE FOR A WORD THAT BEGINS W/VOWEL + PUSHJ P,GETLTR + JRST EPOPJ + JRST DOWRD2 + +DOWRD3: LDB C,[POINT 7,BUFR,6] ;GET FIRST CONSONANT IF ANY. + JUMPE C,DOWRD2 ;JUMP IF NO FIRST CONSONANT. + MOVE D,A ;COPY CASE OF FIRST VOWEL + TRNN C,40 ;SKIP IF FIRST LETTER IS LOWER CASE + TRZ A,40 ;MAKE FIRST VOWEL UPPER CASE. +DOWRD4: OUTCHR A ;OUTPUT FIRST V IN SAME CASE AS FIRST CONS. + PUSHJ P,GETLTR + JRST .+2 ;END OF WORD + JRST DOWRD4 + MOVEI C,"A" + IDPB C,B + MOVEI C,"Y" + IDPB C,B + MOVEI C,0 + IDPB C,B + TRNN D,40 ;WAS FIRST VOWEL IN LOWER CASE? + SKIPA C,[JFCL] + MOVE C,[IORI D,40] ;YES. FORCE REMAINDER TO LOWERCASE. + MOVE B,[POINT 7,BUFR] +DOWRD5: ILDB D,B + JUMPE D,EPOPJ + XCT C + OUTCHR D + JRST DOWRD5 + +GETLTR: .IOT CHTTYI,A ;Read input character into A. + CAIN A,^M + JRST GETLTR + CAIL A,"A + CAILE A,"Z + JRST .+2 + JRST CPOPJ1 + CAIL A,"A+40 + CAILE A,"Z+40 + POPJ P, +CPOPJ1: AOS (P) ;SKIP RETURN, UNLESS LF. + POPJ P, + +ISVOW: +IRPC ZZ,,[AEIOUY] + CAIE A,"ZZ + CAIN A,"ZZ+40 + JRST CPOPJ1 +TERMIN + POPJ P, + + END START + \ No newline at end of file