Skip to content

A Letter Is a Number

The machine holds nothing but numbers, so a letter is a number too. Which number is an agreement, and the agreement has a pattern in it.

  • Foundations

A byte holds eight bits. A program can interpret those bits as a number, a pixel pattern or a character code.

When a program shows you the word SCORE, it needs an agreement about which codes stand for those letters. The code and the shape drawn on screen are different things.

The agreement

An encoding is an agreement about how characters are represented. Here we use ASCII for the letters and digits, with each example’s code fitting in one byte. Other encodings can assign different meanings to the same bits.

Write CODE in front of one character to ask for its code in our chosen encoding:

Code
SHOW CODE "A"
Output
65

Capital A is 65. Go the other way with CHARACTER:

Code
SHOW CHARACTER 66
Output
B

Nothing about 65 is naturally an A. It means A under this agreement. The Spectrum uses these ASCII values for its ordinary letters and digits, but its full character set also includes its own graphics and tokens. Do not assume every machine or every part of its display uses the same table.

That is worth holding on to. A character code is a convention, not a fact about the universe.

The codes run in order

The agreement was not made at random. A is 65, B is 66, C is 67, on through the alphabet in order.

That ordering is what lets a program sort names, or check whether a letter falls between two others under this encoding. Code order is not a promise of the alphabetical order a reader expects: case and the chosen encoding matter.

The trap: a digit is not its value

Digits have codes too, and here is where people come unstuck:

Code
SHOW CODE "7"
Output
55

The character "7" is 55, not 7.

They are different things. The number 7 is a quantity you can add. The character "7" is a shape on the screen that happens to look like a digit. Ask someone to type a number, treat their answer as characters, and your sums come out as nonsense.

The ASCII digits run in order too, from "0" at 48 to "9" at 57. For one valid digit, subtract the code of "0":

Code
SHOW CODE "7" - CODE "0"
Output
7

Check that the input is exactly one character and its code is between the codes for "0" and "9" before using that rule. Subtracting 48 from the code of "A" gives 17, but that does not make A a digit.

A string such as "73" needs whole-number conversion, not just its first character’s code. In Spectrum BASIC, CODE "73" gives 55, the code of its first character, "7". Check what your language’s conversion function does.

Does CODE “7” give 7? Can subtracting CODE “0” turn any character into a valid digit?

CODE "7" gives 55 in our ASCII examples. Subtracting CODE "0" gives the numeric value 7, but only after checking for one character in "0"–"9". A non-digit or a longer string needs a different response.

ASCII upper and lower are one bit apart

Now look at what the agreement did with capitals. A is 65 and a is 97. The gap is 32.

It is the same gap for the 26 ASCII letter pairs, and 32 is not a round number by accident. Write both in binary:

Code
SHOW BIN 1000001
SHOW BIN 1100001
Output
65
97

1000001 and 1100001. Look along them. Every bit is the same except one, and 32 is the place value of the bit that differs.

For an ASCII capital in "A"–"Z", setting this bit produces its lower-case partner:

Code
SHOW CHARACTER (CODE "A" BITOR 32)
Output
a

BITOR 32 sets the bit. It does not toggle it: applying the same mask again leaves the lower-case letter as it is. To convert ASCII "a"–"z" to capitals, clear that bit with the eight-bit mask 11011111 (223).

Check the appropriate letter range first. This rule describes ASCII letters, not every encoding or every character that can appear in text.

What does BITOR 32 do to ASCII “7” and “@”? Does either result prove the input was a letter?

"7" has code 55 with that bit already set, so it stays "7". "@" has code 64, so setting the bit gives code 96, a backtick. Neither operation turns the input into a letter or checks whether it is one.

Meet BASIC’s stored greeting explains strings and quoted text in a complete program. It uses PRINT rather than our SHOW; its opening section explains how to enter it.

For a machine-specific comparison, the Vault’s PETSCII chart distinguishes the C64’s text codes from the codes written directly into screen memory. Choosing an encoding also means checking where a code will be used.

When it’s wrong, see why

  • Your sums came out huge or strange. You are adding character codes rather than numbers. "7" is 55.
  • A comparison sorted oddly, with every capital before every lower-case letter. It did exactly what you asked. Z is 90 and a is 97, so by code, Z does come first.
  • The same file looked wrong on another machine. The agreement is not universal. Check both the encoding and the table used for text or direct screen memory.
  • BITOR 32 mangled something. Check the ASCII letter range first. Nonletters may change or stay the same; the mask does not validate them.

What you’ve learnt

  • A program interprets stored bits using a character encoding.
  • Which number is an agreement, commonly ASCII, and machines did not all agree.
  • ASCII letters and digits occupy ordered ranges; code order depends on the encoding.
  • The character "7" is not the number 7.
  • Validate one digit before subtracting CODE "0" to obtain its value.
  • The 26 ASCII letter pairs differ by one bit; check their range before setting or clearing it.

What’s next

Every number so far has been a whole one. In Unit 4 we handle the in-between: half a pixel, and two-thirds speed.

Sources

Sinclair Research, ZX Spectrum BASIC Programming, chapter 14, p. 91 and Appendix A, describes CODE, CHR$ and the Spectrum character table. Commodore Business Machines, Commodore 64 Programmer’s Reference Guide, Appendices B–C, pp. 376–381, separates screen display codes from text/CHR$ codes.