NCL 211: Working with Text

Over the last several lessons, we've collected quite a few tools for working with text.

We can store strings, change them, inspect them, take them apart, compare them, build new ones, and even include characters that change how the Display presents them.

This time, we're not going to introduce another string operation.

Instead, we're going to put several of the ones we already know to work together.

A little provided code

We'll start with this program:

#device s0
#status s1

MOVE r31 0; JUMP $next

$case0
SMOVE #device "printer"; SMOVE #status "ready"; INC r31; JUMP $format
$case1
SMOVE #device "network"; SMOVE #status "warning"; INC r31; JUMP $format
$case2
SMOVE #device "drive"; SMOVE #status "error"; INC r31; JUMP $format
$case3
SMOVE #device "display"; SMOVE #status "offline"; INC r31; JUMP $format

$next
BEQ $case0 r31 0; BEQ $case1 r31 1; BEQ $case2 r31 2; BEQ $case3 r31 3; D.COL 15 0 0; JUMP 0

$format
-- Your code here.

JUMP $next

There's quite a bit packed into the top of this program, but there isn't anything new hiding in it.

It uses registers, aliases, MOVE, SMOVE, INC, BEQ, JUMP, labels, and coalesced instructions. We've worked with all of those before.

For this lesson, however, we don't need to worry about the details.

The provided code will reach $format four times.

Each time it does, it puts a new pair of strings into these registers:

#device
#status

Then our formatting code runs.

When it reaches:

JUMP $next

the provided code prepares another pair and sends execution through $format again.

The four pairs are:

Pass #device #status
1 "printer" "ready"
2 "network" "warning"
3 "drive" "error"
4 "display" "offline"

We'll leave the provided code alone and work below $format.

Start by displaying the values

Let's begin with the simplest formatter we can make.

Replace:

-- Your code here.

with:

D.TXT #device
D.TXT " "
D.TXT #status
D.TXT "\r\n"

Our formatter is now:

$format
D.TXT #device
D.TXT " "
D.TXT #status
D.TXT "\r\n"

JUMP $next

Run the program.

The Display shows:

printer ready
network warning
drive error
display offline

We only wrote the formatting code once.

The provided code changed the contents of #device and #status, then sent execution through the same formatter each time.

Our formatter doesn't need to know which pass is running.

It simply works with whatever values are currently in those registers.

Normalize the text

The output works, but we'd like the device and status names to be uppercase:

PRINTER READY
NETWORK WARNING
DRIVE ERROR
DISPLAY OFFLINE

We already know an operation that can do that.

Add these instructions at the beginning of $format:

SUPR #device #device
SUPR #status #status

Our formatter becomes:

$format
SUPR #device #device
SUPR #status #status

D.TXT #device
D.TXT " "
D.TXT #status
D.TXT "\r\n"

JUMP $next

Now the same formatting code transforms each pair before displaying it.

Notice that we're changing #device and #status directly.

That's fine here.

Once we're finished with the current pair, the provided code replaces both registers with the values for the next pass.

Line up the statuses

Our uppercase output is easier to read, but the statuses begin in different places:

PRINTER READY
NETWORK WARNING
DRIVE ERROR
DISPLAY OFFLINE

Let's give the device name a fixed-width field.

We used SPADR in NCL 206 to extend a string on the right until it reaches a requested length.

Add:

SPADR #device #device 12 " "

after converting the device name to uppercase:

$format
SUPR #device #device
SUPR #status #status
SPADR #device #device 12 " "

D.TXT #device
D.TXT #status
D.TXT "\r\n"

JUMP $next

We no longer need the separate space between the two fields. The spaces are now part of #device.

The result is:

PRINTER     READY
NETWORK     WARNING
DRIVE       ERROR
DISPLAY     OFFLINE

The same three operations prepare every pair:

Value Before After
#device "printer" "PRINTER "
#status "ready" "READY"

The other passes go through exactly the same transformations.

Give READY a colour

Now let's make the status itself communicate a little more information.

We want:

  • READY displayed normally in green;
  • WARNING displayed normally in yellow;
  • ERROR displayed inverted in red;
  • anything else displayed normally in white.

We'll start with READY.

Our status is already uppercase by the time we need to make this decision.

That means we can compare it directly with:

"READY"

Add this after preparing the strings:

BSEQ $ready #status "READY"

Then add a $ready path:

$ready
D.TXT #device
D.TXT "\s04"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

Palette entry 4 is green, so:

\s04

selects normal green text.

The final:

\s0F

restores the normal Display attributes.

We still need somewhere for all the statuses that aren't READY to go. For now, they can use our original output:

$format
SUPR #device #device
SUPR #status #status
SPADR #device #device 12 " "

BSEQ $ready #status "READY"

D.TXT #device
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

$ready
D.TXT #device
D.TXT "\s04"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

Run it again.

All four lines still appear, but READY is now green.

We haven't changed the provided code at all. We've changed what our formatter does when it receives one particular value.

Add WARNING

Now we want to recognize another status.

We've built this kind of multiple-choice path before.

Before falling through to the ordinary white output, add another comparison:

BSEQ $ready #status "READY"
BSEQ $warning #status "WARNING"

Then give WARNING its own path:

$warning
D.TXT #device
D.TXT "\s0D"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

Palette entry D is yellow, so WARNING is displayed normally in yellow.

Our formatter can now distinguish between three possibilities:

  1. READY takes the $ready path.
  2. WARNING takes the $warning path.
  3. Anything else falls through to the ordinary white output.

This is the same chained-decision pattern we used back in NCL 109.

We're simply using it with strings now.

Add ERROR

There's one more special status in our input:

ERROR

This time, we'll make it inverted red.

We already know the pattern.

Add another BSEQ to the chain:

BSEQ $ready #status "READY"
BSEQ $warning #status "WARNING"
BSEQ $error #status "ERROR"

Then add an $error path.

Try writing this one yourself before continuing.

You'll need to:

  • display #device;
  • change to inverted red;
  • display #status;
  • restore the Display attributes;
  • move to the next line;
  • jump back to $next.

In NCL 210, we used:

\s1X

for inverted text using palette entry X.

Red is palette entry 1.

So one solution is:

$error
D.TXT #device
D.TXT "\s11"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

Run the program again.

We now have four differently handled inputs:

Status Presentation
READY Green
WARNING Yellow
ERROR Inverted red
Anything else Normal white

OFFLINE doesn't have its own path.

That's intentional.

None of the three BSEQ instructions match it, so execution falls through to our ordinary output.

The complete formatter

Our part of the program now looks like this:

$format
SUPR #device #device
SUPR #status #status
SPADR #device #device 12 " "

BSEQ $ready #status "READY"
BSEQ $warning #status "WARNING"
BSEQ $error #status "ERROR"

D.TXT #device
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

$ready
D.TXT #device
D.TXT "\s04"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

$warning
D.TXT #device
D.TXT "\s0D"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

$error
D.TXT #device
D.TXT "\s11"
D.TXT #status
D.TXT "\s0F\r\n"
JUMP $next

There isn't a new instruction anywhere in it.

We used:

  • SUPR to normalize text;
  • SPADR to construct a fixed-width field;
  • BSEQ to choose between string values;
  • labels and JUMP to arrange different paths;
  • D.TXT to build our displayed output;
  • \sXX characters to change its presentation;
  • \r\n to begin another line.

Those pieces came from several different lessons.

Here, they're working together to solve one problem.

What the provided code was doing

We didn't need to understand the provided code in detail to write the formatter.

But by now, it may look less mysterious than it did at the beginning.

Consider one of its lines:

SMOVE #device "network"; SMOVE #status "warning"; INC r31; JUMP $format

Read it from left to right.

It:

  1. puts "network" in #device;
  2. puts "warning" in #status;
  3. increments r31;
  4. jumps to $format.

Our code works with those values and eventually executes:

JUMP $next

At $next, the provided code checks r31:

BEQ $case0 r31 0; BEQ $case1 r31 1; BEQ $case2 r31 2; BEQ $case3 r31 3; D.COL 15 0 0; JUMP 0

The counter determines which pair should be loaded next.

When there are no more pairs, none of the branches are taken. The Display attributes are restored, and execution moves beyond the program.

The supplied code is dense, but its individual pieces aren't unfamiliar.

It's the same registers, comparisons, branches, labels, and jumps we've been using throughout the course.

Try it

Now that the formatter works, change it.

Perhaps OFFLINE deserves its own appearance.

Add another comparison:

BSEQ $offline #status "OFFLINE"

Choose a colour or inversion for it yourself, then add the corresponding path.

Or change the width of the device field:

SPADR #device #device 16 " "

and see how the output changes.

You can also change one of the test cases in the provided code. For example:

SMOVE #device "keyboard"; SMOVE #status "connected"; INC r31; JUMP $format

Your formatter has never seen either of those values before.

It doesn't need to.

It still converts them to uppercase, pads the device field, and displays the unknown status normally:

KEYBOARD    CONNECTED

Try adding your own status and deciding whether it needs special presentation.


In this lesson, we didn't learn another operation.

We started with two values and gradually decided what our program should do with them. Each improvement used tools we'd already learned.

As programs become larger, this becomes increasingly important. A new problem doesn't always need a new instruction. Often, the pieces you already have can be combined in a new way.

So far, however, all of our programs have already had the values they needed.

In NCL 301: Reading the Keyboard, we'll start getting those values from the person using the computer.