  | 
					»  | 
					 | 
				 
			 
			
		 | 
		  | 
		
			
 
						 
							
								  | 
								 | 
							 
							
								  | 
							 
							
								  | 
								
 The stored program computer in its
modern form was developed in the late 1940s. 
About 15 minutes after the first program was written (using patch panels
and/or toggle switches on the front panel) engineers started looking for ways
to make writing programs easier.  Some
of the initial attempts at making programming easier were what are now
considered third generation languages, e.g., Fortran II.  However that still left the problem of
writing low level (close to the hardware) code. Fortran and the other languages
developed at the time were unsuited to the problem of dealing directly with
hardware.  As a result, low level
languages, more or less universally called assembly languages, were
developed.  These languages shared a
number of common characteristics.  These
are:
 
- ONe-for-one mapping from operands in the language to machine instruction
 - Direct access to hardware resources,such as registers, I/O mechanisms, etc.
  
And they made
programming at the lowest level of the computer substantially easier.  They worked. 
Over time, these
assembly languages acquired additional mechanisms for making programming
easier, most notably a macro processing capability through which programmers
could extend the macro language.  Most
frequently “macros” were used to capture frequently-used code sequences, for
example, saving registers at the entrance to routines, restoring them at return,
etc.  Ultimately the purpose of a macro
was to reduce the opportunity for making mistakes.  By putting common instruction sequences into macros, a whole
class of errors was eliminated making the writing of assembly language programs
more reliable and faster. 
At the same time,
substantial research was being done at the higher levels of programming (what
most would consider application programming) resulting in a variety of more or
less general purpose programming languages, Fortran 4, PL/1, Algol, C, Pascal,
and a veritable tower of Babel of others. 
All of these had interesting features and approaches with respect to how
programs were written.  For example,
Fortran included statements that were equivalent to the ubiquitous
compare/branch assembly language instructions (IF (A .EQ. B) GOTO …).  This was probably one of the very first uses
of a “design pattern” in the history of modern computing. 
Most interesting of
all was the gradual elimination of the branch instruction in many of the
languages (notably Pascal which takes the elimination of GOTO to ridiculous
lengths).  The GOTO or branch language
construct was replaced by a variety of flow of control constructs such as: 
- IF THEN ELSE
 - FOR loops
 - DO WHILE/UNTIL loops
  
The “elimination” of
the GOTO and the use of the alternative flow of control constructs became known
as “structured programming.”  Structured
programming is probably the single biggest contributor to the quality and
quantity of code produced since the 1960s. 
There are several reasons for this and they will be discussed later. 
However, assembly
language programmers (and there are a lot of us, though fewer now with the
advent of the RISC machine and the use of higher level languages for OS and
driver development) largely missed the advantages of structured programming. 
This article discusses
the benefits of structured programming and how to do structured programming in
assembly language, specifically Macro-32. 
The techniques discussed here have been used in a number of real time
environments on a variety of platforms. 
								 | 
							 
							
								  | 
							 
						 
						 
							
								  | 
								 | 
							 
							
								  | 
							 
							
								  | 
								
 Well there are a lot
of answers to that question.  The most
common one is “goto-less” programming. 
In fact, “structured
programming” is little more than an enforced discipline that encodes
information directly into the “structure” of a program that makes some of the
characteristics of the program easier to understand by a programmer other than
the original author.  This is also
useful  if the original author returns
to that program after a substantial hiatus. 
In the case of most
higher level languages, the discipline is enforced by the language.  For example, Pascal literally has no GOTO in
the language.  C and C++ do have GOTO
but also have flow-of-control constructs that mitigate their use.   In both languages, it is possible to write
well structured, easy to understand programs. 
It is also possible to write well structured, difficult to understand
programs.  For good examples of such
things, see the winners of the obfuscated C contest held yearly.  The obfuscated C programs all work, many do
useful things, and all are virtually incomprehensible by design. 
So, goto-less
programming is not a panacea.  What use
is it then? 
Without discipline,
none. 
What structured
programming enables is the ability to encode additional information into the body
of the program that makes it easier to understand.  Specifically it let the programmer encode a visual representation
of the flow of control.  In turn, this
lets the programmer use extra pieces of his/her brain to understand the
program.  The additional information is
encoded by indenting the bodies of the various flow-of-control constructs so as
to make them stand out visually.  Thus
the visual cortex is engaged to help understand the program.  Humans evolved using their eyes to detect
predators and a substantial portion of the brain is dedicated to visual
processing.  As a consequence, anything
which adds visual information to a program 
makes understanding the program easier because more of the brain is used
when working with the program. 
Of course the visual
information  must be added consistently,
otherwise the programmer’s eyes get confused and the additional information can
be obscured.  The guidelines for adding
visual information to programs are relatively straightforward.  Basically, every flow-of-con trol structure
must be introduced consistently.  The
code executed with the flow of control must be indented to show the scope of
the flow-of-control construct.  This indentation
must be large enough to separate the code visually while not being so large so
that the eye “skips” over the indented code as though it were divorced from the
flow of control.  In general, indents of
less than three spaces is too little and more than 5 spaces is too much.  However, the human brain is enormously
flexible and as long as the rules for encoding flow-of-control information are
consistently followed within a program, practically anything will work. 
Encoding is therefore
largely a matter of personal style.  In
the open source community, there are at least a dozen different popular
structured programming styles.   In my
experience, Digital Equipment Corporation and the code generated in support of
its many products and product lines was unique in that the same structured
programming style was used for many of them. 
Today much of the programming consistency is supported by
languagesensitive editors such as EMACS and LSEDIT.  Virtually every integrated development environment (IDE) also
enforces one or more programming styles by being sensitive to the indentation
in use at the time in the code being written. 
All modern programming editors can be customized to support virtually
any programming style. 
Again, the assembly
language programmers have largely missed out on the advantages of modern
editors and IDEs bcause assembly language itself doesn’t provide any direct
support for structured programming. 
So what are the
benefits of structured programming in assembly language and how can it be
supported? 
								 | 
							 
							
								  | 
							 
						 
						 
							
								Benefits of Structured Programming | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 One common error in assembly language is a property of the
flexibility of assembly language. 
Assembly language programming is inherently untyped.  The assembly language programmer may treat
any given piece of data as any type, byte, word, longword, ASCII, EBCDIC, null
terminated, counted string, etc.  By
itself, structured programming doesn’t deal with this source of errors.  OpenVMS (and before it, RSX-11M) used naming
conventions to denote data types, byte, word, longword, text, etc.  The strict use of naming conventions
provides an easy visual mechanism for the programmer to make sure that the type
of comparison matches the data type being compared. 
Another common error is also a function of data typing.  In particular, comparisons against all the
common data types can usually be done in either signed or unsigned modes.  Since the difference between a signed branch
versus an unsigned branch is frequently a single character (for example, in
Macro-32, BGTR versus BGTRU for branch on greater than versus branch on greater
than unsigned), it is easy to forget that a data type is unsigned, or to simply
make a typing error and leave off the “U”. 
Again, by itself, structured programming doesn’t help with these errors
but naming conventions help facilitate the discovery and correction of such
errors. 
Another common error is not getting the sense of the
comparison correct.  I program in
assembly language on a variety of machines. 
Some machines test their operands from left to right (A < B) when
usin g comparison instructions.  Others
test their operands from right to left (B < A).  Switching from machine to machine can lead to programming errors
simply from forgetting details of the machine architecture.  Again, structured programming, by itself,
doesn’t help here either. 
The place where structured programming does help is understanding the flow of control through a maze of
assembly language instructions. 
Properly designed, the tools to do structured programming in assembly
language will help with the other problems as well. 
Needless to say, I’m not the first to think of this.  During the development of the Record
Management System (RMS) on the PDP-11, Ed Marison, et al., developed a package
of macros that addressed virtually all of the defects of assembly language for
the PDP-11.  Unfortunately, this package
of macros (known as Super Mac) took forever to assemble, but the increased
programmer productivity and higher quality in terms of number of bugs was felt,
correctly, to more than offset the amount of time it took to assemble any given
portion of RMS. 
I developed and have used a similar macro pachage for 20
years now on a wide variety of embedded systems (PDP-11, Z8000, Motorola 68K)
and any number of driver development projects (mostly on OpenVMS).  This macro package focused mostly on what I
feel are the largest problems associated with assembly language programming,
specifically, exposing the structure (flow of control) of a program written in
assembly language. 
								 | 
							 
						 
						 
							
								  | 
								 | 
							 
							
								  | 
							 
							
								  | 
								
 The simple structured macro package (Simple Mac) has
virtually eliminated my most common errors in assembly programming and has
substantially improved my ability to revisit and understand programs that I’ve
written years ago.  Since I’m a
pragmatic programmer (use what you need when you need it), Simple Mac also
makes it easy to spot where I don’t
use proper structured programming by making it possible to use labels only
where unexpected branches occur rather than everywhere a branch destination is
required. 
Listing 1 shows a basic structured program using Simple Mac. 
.LIBRARY /SMPMAC.MLB/
SM32INIT 
 
ONE:	.BLKL 
TWO:	.BLKL
START.MODULE 
SMPMAR_EXAMPLES:
IF #1 SET.IN R0 
THEN 
    MOVAB ONE,TWO 
ELSE 
    MOVAB TWO,ONE 
END IF
10$:
BEGIN BLOCK_TEST 
    	IF RESULT IS VC LEAVE BLOCK_TEST 
   	MOVAB ONE,TWO 
    END BLOCK_TEST
IFW R0 EQLU #0 GOTO 10$
IFL  IS PLUS THEN 
REPEAT 
   	MOVL ONE,TWO 
   IF TWO GEQL ONE AND TWO NEQU #-1 NEXT 
    MOVL TWO,ONE 
END
DECRU ONE FROM #43 TO #-44 BY #13 
    MCOML TWO,TWO 
    NEXT 
    MCOML TWO,ONE 
END
DECRU ONE FROM #43 TO #-44 BY R0 
	MCOML TWO,TWO 
   	NEXT  
   	MCOML TWO,ONE 
END
DECR ONE FROM #43 TO #-44 BY #13 
   	MCOML TWO,TWO 
  	NEXT 
 	MCOML TWO,ONE 
END
DECR ONE FROM #43 TO #-44 BY R0 
 	MCOML TWO,TWO 
   	NEXT 
 	MCOML TWO,ONE 
 END
DECRU ONE FROM ONE TO #-44 BY #1     
 	MCOML TWO,TWO 
 	GOTOW 10$ 
 	MCOML TWO,ONE 
END
DECR ONE FROM ONE TO #-44 BY #1 
 	MCOML TWO,TWO 
 	LEAVE 
 	MCOML TWO,ONE 
END 
REPEAT 
 	ON.ERROR THEN  
DECRS ONE TO #13 BY #25 
 
REPEAT 
 	ON.NOERROR LEAVE MCOML TWO,TWO 
DECRU ONE TO #13 BY #25 
 
REPEAT 
 	ON.ERROR THEN  
INCRS ONE TO #13 BY #25 
 
REPEAT 
 	ON.ERROR THEN  
INCRS ONE TO #13 BY R0 
 
REPEAT 
 	ON.NOERROR LEAVE 
 	MCOML TWO,TWO 
INCRU ONE TO #13 BY #25 
 
REPEAT 
    ON.NOERROR LEAVE 
    MCOML TWO,TWO 
INCRU ONE TO #13 BY R0 
REPEAT 
    ON.ERROR THEN      
DECRS ONE TO #0 BY #1 
 
REPEAT 
    ON.ERROR THEN      
DECRS ONE TO #0 BY R0 
 
REPEAT 
    ON.NOERROR LEAVE 
    MCOML TWO,TWO 
DECRU ONE TO #0 BY #1 
 
REPEAT 
    ON.NOERROR LEAVE 
    MCOML TWO,TWO 
DECRU ONE TO #0 BY R0 
 
REPEAT 
    ON.ERROR THEN  
INCRS ONE TO #13 BY #1 
 
REPEAT 
ON.NOERROR LEAVE 
    MCOML TWO,TWO 
INCRU ONE TO #13 BY #1 
SCASE R0 FROM 10 TO 30 
SET 
  $CASE OUTRANGE 
 	MCOML ONE,ONE 
  END 
 
  $CASE 10 TO 15 
 	MCOML TWO,TWO 
  END 
 
  $CASE 16 TO 20,10$
$CASE INRANGE 
 		MOVL R0,ONE 
 	  END 
 	END 
 
 	SCASE R0 FROM 10 TO 30 
 	SET 
$CASE 10 TO 15 
 		MCOML TWO,TWO 
 	  END 
 
 	  $CASE 16 TO 20,10$ 
 
 	  $CASE INRANGE 
 		MOVL R0,ONE 
 	  END 
 	END 
 
END.MODULE 
.END
 
 
Listing 1 Simple M ac example program.
The Simple Mac example program doesn’t do anything except
demonstrate that using Simple Mac allows the programmer to focus on the
implementation instead of worrying about how to implement the flow of control
thorough the program.  In the above
example, the necessary code to actually implement the flow of control would
substantially outnumber the actual executable code in the program (not a normal
situation, but for complex programs this can appear to be the case).  Additional documentation in the form of
comments, discussion about the purpose of the program, why each block exists
and what each statement is doing would also appear in a real program, adding to
the ease of maintanence. 
I have used Simple Mac in many environments.  Of course, its principal use is in the development
of Macro-11 and Macro-32 programs.  I’ve
written many device drivers for OpenVMS compatible with both the VAX and AXP
versions of the system using Simple Mac. 
I’ve written system services and a variety of other applications in
assembly language using Simple Mac. 
I’ve also developed embedded systems using Simple Mac on processor
architectures other than the 16 and 32 bit Digital/Compaq/HP machines.  In these cases, the macro processing
capabilities of the assembler in the development environment was not sufficient
to implement Simple Mac directly.  Under
these circumstances I found it necessary to write a preprocessor that converted
the Simple Mac statements into assembly language which were then processed by the
embedded system’s development environment. 
By leaving the Simple Mac statements embedded in the generated assembly
language source files, debugging was straightforward. 
Since developing Simple Mac 20+ years ago, I’ve written
several hundred thousand lines of assembly language on several different
processor architectures.  Use of Simple
Mac has virtually eliminated the most common of my programming errors in
assembly language and substantially improved my ability to maintain the
assembly language code that I’ve written. 
								 | 
							 
							
								  | 
							 
						 
						 
						
							
								Simple Mac syntax elements | 
							 
							
								  | 
							 
							
								  | 
							 
							
								
 
  | 
   Module 
   | 
  
   A group of assembly language source lines which begin with
  a START.MODULE, end with an END.MODULE, and [may] include one or more Simple
  Mac statements. 
   | 
  
 
  | 
   Module declaration 
   | 
  
   A callable unit (CALL/CALLS/CALLG). 
   | 
  
 
  | 
   Macro statement 
   | 
  
   Any valid assembly source statement, except one of the Simple Mac statements. 
   | 
  
 
  | 
   Block statement 
   | 
  
   Any of the block-structured statements: BEGIN, REPEAT,
  CASE, IF-THEN-ELSE, REPEAT, etc. 
   | 
  
 
  | 
   Block type 
   | 
  
   LOOP 
  BLOCK 
  CASE 
  Segment name 
  INNER 
  OUTER 
  REPEAT 
  INCR 
  DECR 
   | 
  
 
  | 
   Segment name 
   | 
  
   1-15 character symbolic name given to a program segment by
  a BEGIN statement. 
   | 
  
 
  | 
   label 
   | 
  
   Any valid MACRO address label. 
   | 
  
 
  | 
   condition 
   | 
  
   operand relation operand 
  operand SET.IN/CLR.IN operand 
  operand ON.IN/OFF.IN operand 
  operand MASK.ON/MASK.OFF  
  RESULT IS relation 
  <macro-statement> IS relation 
   | 
  
 
  | 
   Conditional expression 
   | 
  
   condition 
  condition AND condition 
  condition OR condition 
   | 
  
 
  | 
   Asm constant expr 
   | 
  
   Any assembly time constant expression.  It must be possible to evalute the
  expression  at assembly time, not link
  time. 
   | 
  
 
  | 
   Case range expression 
   | 
  
   asm-cons-expr TO asm-cons-expr[1] 
  asm-cons-expr 
   | 
  
 
  | 
   $Case range expression 
   | 
  
   case-range-expression 
  INRANGE 
  OUTRANGE 
  <case-range-expression, …> 
   | 
  
 
  | 
   Operand 
   | 
  
   Any valid assmembly language operand. 
   | 
  
 
  | 
   Status 
   | 
  
   ERROR 
  NOERROR 
   | 
  
 
  | 
   Relation 
   | 
  
   EQ/EQL             EQU/EQLU            NE/NEQ            NEU/NEQU            GT/GTR            HI/GTU/GTRU            GE/GEQ             
  HIS/GEU/GEQU             
  LT/LSS               LO/LTU/LSSU            LE/LEQLOS/LEU/LEQU             
  MINUS              ZERO                        PLUS                        CC                        CS                        VC                        VS                         
  SET.IN /ON.IN  CLR.IN/OFF.IN  MASK.ON           
  MASK.OFF          
   | 
  
   Signed Equal to 
    
  Unsigned Equal to 
  
 
  
  Not Equal To 
   
  Not Equal To  
   
  Greater than 
   
  Greater than Unsigned
 
  
  Greater than or Equal to 
   
  Greater than or Equal to Unsigned 
  Less than
 
  
  Less than Unsigned 
   
  Less than or equal  
   
  Less than or Equal to Unsigned 
  Sign bit set 
   
  Zero bit set
 
  
  Sign bit clear 
  Carry Clear
 
  
  Carry Set 
   
  Overflow Clear
 
  
  Overflow Set 
   
  Bit set in 
   
  Bit off in 
   
  Bit(s) on in the masked operand 
  Bit(s) off in the masked operand 
   | 
  
 
  | 
   Type 
   | 
  
   B (byte) 
  W (word) 
  L (longword) 
  F (float, currently not implemented) 
  Q (quadword, currently not implemented) 
  O (octaword, currently not implemented) 
   | 
  
 
  | 
   Sign 
   | 
  
   S (signed) 
  U (unsigned) 
   | 
  
 
A conditional expression is true if: 
- it is a single condition and that condition is true.
 - it is an OR expression and either of the conditions is true.
 - it is an AND expression and both conditions are true.
  
It is false otherwise. 
The IF, UNTIL, and WHILE statements operate on the specified
data types when evaluating a conditional expression.  If a type is unspecified, the default type is word. 
The SCASE statement operates on the specified data type when
evaluating a range, otherwise word entities are used.  Float values are not valid for case ranges. 
The IS operation in a condition tests the settings of the
current condition codes.  If the first
operand is the reserved word RESULT, then the current setting of those codes is
tested, otherwise the first operand is assumed to be a macro statement.  This macro statement is executed and the
resulting condition codes are tested. 
SET.IN/CLR.IN and ON.IN/OFF.IN in a conditional expression
refer to a bit in  the second operand,
as selected by the first operand. 
MASK.ON/MASK.OFF in a condition expression refer to a collection
of bits in the second operand, as masked by the first operand. 
A Simple Mac source file contains one or more modules.   SIMPLE-MAC statements may only appear
within a Simple Mac module (START.MODULE/END.MODULE). 
A program block consists of one or more assembly language
statements delimited by a starting statement and an END statement. 
- Conditional blocks begin with IF or SCASE statements.
 - Loops begin with REPEAT, INCR, or DECR statements.
 - Program segments are started by BEGIN or $CASE statements.
  
Multiline conditional blocks and program segments must be
terminated by an END statement.  Loops
can be terminated by an END, UNTIL, or WHILE statement.  Note that THEN and ELSE statements do not
terminate a block. 
Single line IF-THEN, IF-LEAVE, IF-NEXT, and IF-GOTO
statements do not constitute a conditional block and do not require an END
statement. 
Single line $CASE-range-expression, label statements do not
constitute a program segment. 
								 | 
							 
						 
						
						 
							
								BEGIN segment-name | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Assigns the specific symbolic name to this program
segment.  The symbolic name can then be
used in LEAVE statements to exit the code contained within the block.  BEGIN blocks may be nested. 
BEGIN CONTROL    CALL
INIT    CALL
CSIOV1     ON.ERROR
LEAVE CONTROL    CALL
PROCES    CALL
CLEAN 
END 
								 | 
							 
						 
						 
							
								SCASE[type] operand FROM case-range-expression | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 To avoid confusing the Macro-32 compiler, the CASE
instruction has been renamed SCASE. 
This stands for Simple Mac CASE.  This is the only distinction in syntax between SMPMAC.MAC
(Macro-11) and SMPMAC.MAR (Macro-32). 
Many processors do not directly implement a case instruction.  The SCASE macro provides the hooks by which
one may be impleme nted. 
Provides an EXTREMELY fast dispatch mechanism to a variety of
possible alternative processing paths. 
This function is expensive in memory since the speed is achieved using a
dispatch table. If the value of the case operand is outside the specified range
and no OUTRANGE action is specified, the case falls through to the end.  If no action is specified for some set of values
of the operand, the case falls through to the end (remove the $CASE INRANGE in
the example and values 0 to 3 would fall through the CASE). 
SCASE I FROM 0 TO 7 
SET   $CASE OUTRANGE,CASE.ERROR Go here if out of range. 
   $CASE 4 TO 7                     If 4 <=
i <= 7 do this block        CALL
ON.HIBIT   END 
   $CASE INRANGE        CALL
OFF.HIBIT             Otherwise do this block.   END 
END 
								 | 
							 
						 
						 
							
								$CASE $case-range-expression[,label] | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Identifies which value or range of values will be processed
by the following block.  If a label is
specified, the $CASE causes a jump to that label to occur.  A $CASE without a label argument defines the
beginning of a block. The block may be exitted via a LEAVE CASE.  See the SCASE example above for usage. 
								 | 
							 
						 
						 
							
								DECR[sign] loop-index FROM start TO end BY decrement | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Initialize the loop index with the start value, and repeat
the loop until the value of the loop index is less than the stated end
value.  The test for termination is
either SIGNED or UNSIGNED depending on the value of the sign argument (S or
U).  The default is SIGNED
comparison.  The loop index must be of
type long.  This loop is a 0 trip loop,
that is, if the initial value of the loop index is less than the end value, the
loop is not executed.  The termination
test is performed at the top of the loop. 
If the lexical value of the loop-index and the start value are the same,
no loop initialization is generated. 
If the loop index is identical to the initial value, the loop
assumes that the last thing done before entering the loop was to set the loop
index.  No special code is generated to
test things.  Since the 0 trip versions
of these loops assume the loading of the loop index, you MUST either load the
loop index just before the DECR/INCR instruction or issue a processor
appropriate comparison instruction (TSTL or CMPL for Macro-32) to get the
condition codes set up properly. 
								 | 
							 
						 
						 
							
								ELIF | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 ELIF[type]
conditional-expression 
[THEN]    conditional-block  
[ELSE    
 conditional-block]  
END    
ELIF is syntactic sugar equivalent to: 
ELSE 
 IF[type] conditional-expression 
  … 
  END 
END 
except that a single END statement to a series of
IF-THEN-ELIF-THEN-ELIF statements will terminate all statements including the
introducing IF. The purpose of the ELIF statement is to avoid generating
excessive indentation with nested ifs. 
Excessive indentation can make a program very difficult to read and
obscure the flow of control. 
All forms of IF are also valid with ELIF, e.g., ELIF-LEAVE,
ELIF-GOTO, etc.  When used in this
fashion, the ELIF terminates the current conditional block, and NO END is
necessary since these forms of IF statement have no ELSE clauses. 
IFW A EQ #14 
THEN        … 
ELIF A EQ #19 
THEN        … 
ELIF A EQ #23 
THEN        … 
ELSE        … 
END                                Conditional
block is EXPLICITLY                                     terminated. 
IFW A EQ #14 
THEN   
    … 
ELIF A EQ #23 GOTO DONE            Conditional
block is IMPLICITLY                                    terminated. 
								 | 
							 
						 
						 
							
								IF | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 IF[type]
conditional-expression  
[THEN]                                           conditional-block        
[ELSE                                     conditional-block]        
END                                    
Executes the subsequent conditional block if the specified
conditional expression is true, otherwise, it executes the optional ELSE
conditional block.   Type can be any of
the primitive types supported by the processor architecture.  For Macro-32 these are Byte, Word, Longword,
Quadword, and Octaword, e.g. IFB, IFL, IFW, … 
IFB (R1)+ GT #0 
THEN        MOVL
R0,-(R3)        INCL
COUNT        CALL
NEWLIN 
ELSE        CALL
ERROR 
END 
								 | 
							 
						 
						 
							
								IF[type] conditional-expression THEN <macro-statement>  | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Executes the specified macro statement if the conditional
expression is  true.  There are no restrictions on the macro
statement.  It can be anything from a
single instruction, to a subroutine, to a macro-invocation in its own
right.  The macro-statement can, in
fact, be another Simple Mac statement. 
IFW R0 LT #MAX THEN <CALL SWQR5> 
IFB R5 LSSU #TABEND THEN <ADD #3,R5> 
IF[type] conditional-expression LEAVE block-type
Transfers control to the end statement of the specified block
type. LEAVE searches for the innermost block that satisfies block-type.  For example, you can exit a repeat loop from
within a case block or a BEGIN block from within an IF block.  If a segment name is used in a LEAVE, the
named segment is exited.  If the block
type OUTER is used, the  outermost block
is exited, independent of block type. 
If the block type  INNER is used,
the innermose block is exited, independent of block type. The default value for
block-type is INNER. 
REPEAT        IF
A EQ #END        THEN               …               IF (R0) EQL #0 OR RO GTRU LEAVE LOOP               …        ELSE 
      …     END    … 
END 
								 | 
							 
						 
						 
							
								IF[type] conditional-expression GOTO label | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 If the conditional expression is true, control is transferred
to the specified label.  This operation
isn't frequently needed, but is here for those times when a good old GOTO is
just what's needed. 
IF #ERROR SET.IN CONTROL THEN GOTO ABORT 
								 | 
							 
						 
						 
							
								END [COMMENT] | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Terminates the current loop, conditional, or program
segment.  The optional comment can be
used to match end statement with block start statement, improving readability. 
BEGIN        … 
END BEGIN 
END.MODULE 
Terminates the current module.  A single file can contain more than one module. 
								 | 
							 
						 
						 
							
								GOTO[type] label | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Transfers execution to the specified label.  A GOTOB uses a branch  instruction, a GOTOW uses a jump.  Unlike the other instructions, the default
type for GOTO is B rather than W.  The
implementation of this statement is rather heavily slanted towards the VAX
processor architecture and may need to be modified on other architectures.  In that event, I suggest that the GOTOB
statement be used to indicate transfer of control to somewhere “close” visually
and GOTOW to someplace “far away.” 
								 | 
							 
						 
						 
							
								INCR[sign] loop-index FROM start TO end BY increment END | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Initialize the loop index with the start value, and repeat
the loop until the value of the loop index is less than the stated end
value.  The  test for termination is either SIGNED or UNSIGNED depending upon
the value of the sign argument.  The
default is SIGNED comparison.  The loop
index must be of type word.  This loop
is a 0-trip loop.  The termination test
is performed at the top of the loop.  If
the lexical value of the loop-index and the start value are the same, no loop
initialization is generated.  This
statement is biased towards the VAX implementation for loops. 
See also DECR. 
								 | 
							 
						 
						 
							
								LEAVE block-type | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Transfers control to the end of the specified block
type.   
See IF conditional-expression LEAVE block-type for examples. 
								 | 
							 
						 
						 
							
								NEXT | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Transfer control to the next iteration of the loop.  Control is transferred to the loop
termination tests. 
ON.ERROR THEN <macro-statement>  
ON.ERROR GOTO label  
ON.ERROR LEAVE block-type
 Perform the specific action if the low bit of R0 is
clear.  This is an implementation of the
OpenVMS-specific error-condition code covention in which error codes are
returned in R0 and the low order bit is cleared.  Other processors and systems have other conventions.For example,
PDP-11 operating systems generally returned success and failure by clearing and
setting the carry-condition flag.  The
Macro-11 implementation of Simple Mac reflects this difference. 
								 | 
							 
						 
						 
							
								ON.NOERROR THEN <macro-statement>  ON.NOERROR GOTO label  ON.NOERROR LEAVE block-type | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 The opposite of the ON.ERROR statement. 
REPEAT                                         
END                                                         
 
REPEAT UNTIL[type] condition               
 
END      
 
REPEAT 
UNTIL[type] condition 
 
REPEAT WHILE[type] condition               
 
END      
 
REPEAT                          
WHILE[type] condition            
 
REPEAT                                     
 
DECR[sign] loop-index TO value BY decrement  
 
REPEAT                                     
 
INCR[sign] loop-index TO value BY increment  
Perform the loop UNTIL the condition is true, WHILE the
condition is true, or until the loop-index is less than or equal to the
specified value (for decrement repeat loops) or is greater than or equal to the
specified value (for increment repeat loops). 
The termination conditions are tested at the place where they appear in
the loop.  If at the top, they are
tested before the loop begins and the loop is a 0-trip loop; if at the bottom,
they are tested when the loop terminates and the loop is a 1-trip loop.  The loop index must be initialized before
entering the loop and must be of type word. 
This will vary from processor architecture to architecture. 
								 | 
							 
						 
						 
							
								START.MODULE | 
							 
							
								  | 
							 
							
								  | 
							 
							
								| 
 Defines the beginning of a module.  Initializes the state of Simple Mac. 
								 | 
							 
						 
						 
 
[1] the first
asm constant expression must be less than the second 
 
  
		 |