9.4 Flow Control

     

Although it has many advanced features, at heart PASM is an assembly language. All flow control in PASM ”as in most assembly languages ”is done with branches and jumps .

Branch instructions transfer control to a relative offset from the current instruction. The rightmost argument to every branch opcode is a label, which the assembler converts to the integer value of the offset. You can also branch on a literal integer value, but there's rarely any need to do so. The simplest branch instruction is branch :

 branch L1                # branch 4   print "skipped\n" L1:   print "after branch\n"   end 

This example unconditionally branches to the location of the label L1 , skipping over the first print statement.

Jump instructions transfer control to an absolute address. The jump opcode doesn't calculate an address from a label, so it's used together with set_addr :

 set_addr I0, L1   jump I0   print "skipped\n"   end L1:   print "after jump\n"   end 

The set_addr opcode takes a label or an integer offset and returns an absolute address.

You've probably noticed the end opcode as the last statement in many examples above. This terminates the execution of the current run loop. Terminating the main bytecode segment (the first run loop) stops the interpreter. Without the end statement, execution just falls off the end of the bytecode segment, with a good chance of crashing the interpreter.

9.4.1 Conditional Branches

Unconditional jumps and branches aren't really enough for flow control. What you need to implement the control structures of high-level languages is the ability to select different actions based on a set of conditions. PASM has opcodes that conditionally branch based on the truth of a single value or the comparison of two values. The following example has if and unless conditional branches:

 set I0, 0   if I0, TRUE   unless I0, FALSE   print "skipped\n"   end TRUE:   print "shouldn't happen\n"   end FALSE:   print "the value was false\n"   end 

if branches if its first argument is a true value, and unless branches if its first argument is a false value. In this case, the if doesn't branch because I0 is false, but the unless does branch. The comparison branching opcodes compare two values and branch if the stated relation holds true. These are eq (branch when equal), ne (when not equal), lt (when less than), gt (when greater than), le (when less than or equal), and ge (when greater than or equal). The two compared arguments must be the same register type:

 set I0, 4   set I1, 4   eq I0, I1, EQUAL   print "skipped\n"   end EQUAL:   print "the two values are equal\n"   end 

This compares two integers, I0 and I1 , and branches if they're equal. Strings of different character sets or encodings are converted to Unicode before they're compared. PMCs have a cmp vtable method. This gets called on the left argument to perform the comparison of the two objects.

The comparison opcodes don't specify if a numeric or string comparison is intended. The type of the register selects for integers, floats, and strings. With PMCs, the vtable method cmp or is_equal of the first argument is responsible for comparing the PMC meaningfully with the other operand. If you need to force a numeric or string comparison on two PMCs, use the alternate comparison opcodes that end in the _num and _str suffixes.

 eq_str P0, P1, label     # always a string compare gt_num P0, P1, label     # always numerically 

Finally, the eq_addr opcode branches if two PMCs or strings are actually the same object (have the same address), and the is_null opcode branches if a PMC is NULL (has no assigned address):

 eq_addr P0, P1, same_pmcs_found is_null P2, the_pmc_is_null 

9.4.2 Iteration

PASM doesn't define high-level loop constructs. These are built up from a combination of conditional and unconditional branches. A do while -style loop can be constructed with a single conditional branch:

 set I0, 0   set I1, 10 REDO:   inc I0   print I0   print "\n"   lt I0, I1, REDO   end 

This example prints out the numbers 1 to 10. The first time through, it executes all statements up to the lt statement. If the condition evaluates as true ( I0 is less than I1 ) it branches to the REDO label and runs the three statements in the loop body again. The loop ends when the condition evaluates as false.

Conditional and unconditional branches can build up quite complex looping constructs, as follows :

 # loop ($i=1; $i<=10; $i++) {   #    print "$i\n";   # } loop_init:   set I0, 1   branch loop_test loop_body:   print I0   print "\n"   branch loop_continue loop_test:   le I0, 10, loop_body   branch out loop_continue:   inc I0   branch loop_test out:   end 

This example emulates a counter-controlled loop like Perl 6's loop keyword or C's for . The first time through the loop it sets the initial value of the counter in loop_init , tests that the loop condition is met in loop_test , and then executes the body of the loop in loop_body . If the test fails on the first iteration, the loop body will never execute. The end of loop_body branches to loop_continue , which increments the counter and then goes to loop_test again. The loop ends when the condition fails, and it branches to out . The example is more complex than it needs to be just to count to 10, but it nicely shows the major components of a loop.



Perl 6 and Parrot Essentials
Perl 6 and Parrot Essentials, Second Edition
ISBN: 059600737X
EAN: 2147483647
Year: 2003
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net