NCL 110: Combining Conditions

In the last few lessons, we've used branches to make decisions.

A branch asks one question:

BGE $match #value 10

Is #value greater than or equal to 10?

Sometimes one question is enough.

Sometimes a program needs several conditions to be true at the same time.

Or it may need only one of several conditions to be true.

We can build both kinds of decision using the branches we already know.

Between two numbers

Suppose we want to determine whether a number is between 10 and 20, including both ends.

For the number to be in that range, two things must be true:

The number must be at least 10.

The number must be no more than 20.

We could also think about what would make the test fail.

If the number is less than 10, it is outside the range.

If the number is greater than 20, it is outside the range.

That gives us:

#value 15

BLT $outside #value 10
BGT $outside #value 20

D.TXT "Inside"
JUMP $done

$outside
D.TXT "Outside"

$done
D.BLT

The program displays:

Inside

Failing early

Look closely at these two branches:

BLT $outside #value 10
BGT $outside #value 20

Both conditions must fail before the CPU can reach:

D.TXT "Inside"

Let's try a few values.

#value Less than 10? Greater than 20? Result
5 Yes Not reached Outside
10 No No Inside
15 No No Inside
20 No No Inside
25 No Yes Outside

With 5, the first branch immediately sends execution to $outside.

There is no reason to ask whether 5 is greater than 20. We already know the number failed one of the requirements.

With 25, the first test succeeds in continuing downward, but the second branch sends execution to $outside.

Only values that pass both tests reach the inside path.

We can describe the condition as:

value >= 10 AND value <= 20

The number must satisfy the first requirement and the second requirement.

AND with branches

NCL does not need a special AND instruction to express this decision.

Instead, we arrange the branches so that failure of either requirement sends execution away:

BLT $fail #value 10
BGT $fail #value 20

-- both conditions passed

If the first condition fails, branch away.

If the second condition fails, branch away.

If neither branch happens, both requirements have been satisfied.

This is a useful pattern whenever several things all need to be true.

Either of two values

Now let's ask a different kind of question.

Suppose we want to know whether a number is either 3 or 7.

We could test for 3:

BEQ $match #value 3

If it isn't 3, we can test for 7:

BEQ $match #value 3
BEQ $match #value 7

Notice that both branches lead to the same place.

Here's the complete program:

#value 7

BEQ $match #value 3
BEQ $match #value 7

D.TXT "No match"
JUMP $done

$match
D.TXT "Match"

$done
D.BLT

The program displays:

Match

Succeeding early

This time, either branch is enough to reach $match.

#value Equal to 3? Equal to 7? Result
2 No No No match
3 Yes Not reached Match
5 No No No match
7 No Yes Match

With 3, the first branch succeeds.

There is no reason to test whether the value is 7. We already know the whole condition has succeeded.

With 7, the first branch fails, but the second succeeds.

Only if both branches fail does execution reach:

D.TXT "No match"

We can describe this condition as:

value == 3 OR value == 7

The value can satisfy the first condition or the second condition.

OR with branches

Again, we don't need a special instruction for the combined condition.

We arrange several branches so that any one of them can reach the same path:

BEQ $success #value 3
BEQ $success #value 7

-- none of the conditions succeeded

The first condition can succeed.

Or the second condition can succeed.

If neither does, execution falls through.

The arrangement of the branches expresses the relationship between the conditions.

AND and OR

The two patterns look similar, but they work differently.

For AND, every requirement must be satisfied:

BLT $fail #value 10
BGT $fail #value 20

-- success

We branch early when any requirement fails.

For OR, any one condition is enough:

BEQ $success #value 3
BEQ $success #value 7

-- failure

We branch early when any condition succeeds.

That gives us two useful ways to think about them:

Condition Branch early when... Fall through when...
AND A requirement fails Every requirement passed
OR A condition succeeds Every condition failed

Combining the patterns

These patterns can also be used together.

Suppose a program accepts numbers from 10 through 20, but does not accept 13 or 17.

That gives us two kinds of requirement.

The number must be:

at least 10 AND no more than 20

and it must not be:

13 OR 17

We can express all of that with branches:

#value 15

BLT $reject #value 10
BGT $reject #value 20

BEQ $reject #value 13
BEQ $reject #value 17

D.TXT "Accepted"
JUMP $done

$reject
D.TXT "Rejected"

$done
D.BLT

The first pair of branches checks the range:

BLT $reject #value 10
BGT $reject #value 20

Anything below 10 or above 20 is rejected immediately.

If the number survives both tests, we know it is inside the range.

Then:

BEQ $reject #value 13
BEQ $reject #value 17

rejects either excluded value.

Only a number that survives all four branches reaches:

D.TXT "Accepted"

Following the decision

Try several values:

#value In range? Excluded? Result
8 No Rejected
10 Yes No Accepted
13 Yes Yes Rejected
15 Yes No Accepted
17 Yes Yes Rejected
20 Yes No Accepted
22 No Rejected

Notice that the CPU does not necessarily perform every test.

For 8, the very first branch reaches $reject.

For 13, the number passes both range tests before the third branch reaches $reject.

For 15, every branch fails, so execution falls through to the accepted path.

This is another reason the order of instructions matters.

The CPU asks each question only if execution reaches it.

Try it

Start with:

#value 15

BLT $reject #value 10
BGT $reject #value 20

BEQ $reject #value 13
BEQ $reject #value 17

D.TXT "Accepted"
JUMP $done

$reject
D.TXT "Rejected"

$done
D.BLT

First, try changing only #value.

Predict the result before running the program.

Make sure you try values:

  • below the range,
  • at both ends of the range,
  • inside the range,
  • and equal to one of the excluded values.

Then change the rules.

Make the accepted range 20 through 50.

Inside that range, reject 25, 30, and 40.

You will need to change the range comparisons and add another branch to the group of excluded values.

Before running the program, choose a few values and follow the branches yourself.

Which branch, if any, will be taken?

If none are taken, where will execution go?

Building decisions from simple questions

We've now used numbers in several different ways.

We've stored them in registers, changed them with arithmetic, compared them, counted with them, used them to control loops, and used calculated results to choose between several paths.

The individual instructions are simple.

What makes programs useful is how those instructions are arranged.

A branch only asks one question at a time, but several branches together can express much larger decisions:

this AND that
this OR that

or combinations of several requirements.

As programs become more complicated, keep using the same technique we've used throughout these lessons:

Follow one instruction at a time.

Keep track of the values that matter.

And ask which instruction the CPU will execute next.


We've learned how to combine several conditions to make more complicated decisions.

So far, we've mostly introduced one new tool at a time. In the next lesson, we're going to stop adding tools for a moment and use the ones we already have to build something larger.

In NCL 111: Working with Numbers, we'll turn a simple mathematical procedure into a complete program.