Beginning Z80 Machine Code, Lesson 1

TIME DESIGNS Editor, Tim Woods, has asked me to write a continuing series on Beginning Z80 Machine Code. I would like to hear from you, what your specific needs are, as well as your suggestions and comments. You can respond direct to me (address at end of article) to avoid delay and extra work for Tim.

We will be developing some machine code (MC) routines to be used as subroutines in a BASIC program. This will aid the learning process, as well as provide a useful goal. I am laboring under the assumption that you already have a good understanding of BASIC. If not, you may wish to master BASIC first, as it is a very powerful programming language all by itself.

You may be asking, “What is machine code, and why would I be interested in learning it?” Your answer may very well be that you are not interested, in which case you need read no further. However, if you are tired of your computer taking a “coffee break” when you ask it to perform some task, you may be interested in how MC can put some speed in your program, and end that coffee break sooner.

What is machine code? Very simply, MC is the “language” understood by your computer’s Central Processing Unit (CPU). Each CPU has its own set of instructions which enable it to perform its task as housekeeper, and communicate with the outside world (other devices). These instructions are permanently stored at birth in the CPU, as numbers. We will be studying the Z80, as it is the CPU that Sinclair has chosen to use in our computers.

I called the CPU, “housekeeper”, for a reason. The CPU is basically “dumb”. It is the “workhorse” in your computer, and can only perform general housekeeping chores. The “brains” of the operation, is the Operating System and Basic Interpreter, which is permanently stored in memory (ROM). Without the operating system, the CPU is even more helpless than a newborn child. The CPU can only perform as instructed. However, it will perform exactly as instructed, which may not always be what is intended. More on this later.

We need to pause a moment to define ROM, RAM, PEEK and POKE.

ROM and RAM are types of memory locations within your computer. ROM stands for Read Only Memory, and RAM for Random Access Memory, neither of which explains their functions very well. All memory locations (addresses) can be thought of as storage boxes. Some of these boxes (ROM) cannot be altered while others (RAM) can be. This difference is all that we will be concerned with here, which brings us to PEEK and POKE.

PEEK allows us to look at the contents of these boxes, as if they had glass tops. POKE is the command that places a number in the boxes (RAM only). Try POKEing various numbers (no teddy bears or marbles), into a safe address (30,000 will do), then PEEKing the location to see if you attain the results you expected. Try other locations if you think there is something magical about 30,000. Safe locations on the T/S 1000 are 16509-32767 (16K), and on the T/S 2068 are 26710-65535. Try an address 0-16383 (ROM). PEEK it first, then POKE, then PEEK again. You cannot change the contents of ROM, but you are welcome to look inside as often as you wish. You can find the proper syntax for PEEK and POKE in your Sinclair users manual.

I mentioned that the instructions being stored in the CPU are numbers. We will also store our MC as numbers, since that is all the CPU understands. Your computer’s operating system and the basic interpreter are written in MC for this reason. For example, the CPU does not understand the basic command “PRINT”. Instead, there is a subroutine of MC instructions (stored as numbers) in the basic interpreter of your computer which tells the CPU what steps are necessary to execute your PRINT command.

To see what I mean, ENTER and RUN Listing No.1 on your computer. You are looking at the numbers (MC) stored in the first 20 bytes of ROM. Obviously, you thoroughly understand what they mean(?)…don’t worry, I don’t understand them either, but the CPU does and that’s the point. Those unintelligible numbers mean something to the CPU, and shortly they will mean something to us.

Listing 1
10 For I = 0 to 19
20 Print I; Tab 6; Peek I
30 Next I

You should notice that all the numbers are between 0 and 255. This is very important. For now, we need only know that 0 to 255 represents the limit of numbers that can be stored in a single byte. The reason for this will be obvious later, when we discuss binary numbers. Try changing the values in line 10 to look at other locations.

A quick scan of the Sinclair manual indicates how the computer stores numbers that are greater than 255. It ties two adjacent bytes together by increasing one of them by a factor of 256 (2^8). Storing numbers this way means that we can now use the numbers 0 to 65535 (2^16). The number that we increase by the factor of 256, is referred to as the high byte, making the other the low byte. The oddity in this, is that the Z80’s designers chose to store the low byte first. For example, let’s assume bytes 1 and 2 hold our number. The number held by these two are:

PEEK (Byte 1) + 256 * PEEK (Byte 2)

You have probably seen this formula before, and may have wondered what was happening. Many of the system’s variables are stored in this manner, as well as all addresses. Even addresses less than 255 require two bytes storage. More on this later, also.

One other point. All the numbers we have discussed thus far are in decimal (base 10) which is how most of us count. Scientists and philosophers have debated why we use decimal numbers since approximately 1134 BC (before computers), and the general concensus, despite lack of empirical proof, is that it has something to do with our having ten fingers. (This has nothing to do with MC. However, I thought you trivia buffs would want to know.)

In this series, I will represent all MC with hexadecimal (base 16) numbers. This is not an effort to confuse you. I prefer using hexadecimal (hex) numbers for several reasons:

1. All numbers 0 to 255 can be represented with two digits.

2. Larger numbers can then be represented with a multiple of two digits.

3. Cleaner/easier screen displays.

4. Less keystrokes to enter MC.

5. My MC loader program handles hex numbers only.

6. Many MC listings are presented in hex numbers.

7. Personal preference, after all I’m writing this.

The fact that your computer only recognizes decimal numbers is not a problem. The computer is very good at conversions of this nature, and I prefer to let it do them.

Now that we know what the hieroglyphics are called…what is hex? It is base 16 numbers. Just as we represent decimal numbers with the digits 0-9, we also represent the first ten digits of hex numbers with the digits 0-9. The next six digits are the letters A-F. Therefore, we count in hex:

0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F

Don’t be alarmed if this confused you. I have provided a full conversion chart. Practice using this chart. You will eventually be able to add and subtract, even think in hex. You will see that the chart represents all decimal numbers 0 to 255, as 00 to FF hex. It is important to realize that hex numbers are always two digits (or some multiple of two) long, even though I show them as single digits above. That is only to avoid confusion over what the first sixteen digits are. Many hex numbers are obviously not decimal. However, when there could be confusion, you should show a “h” after the hex number (10h). There is no “d” required for decimal, as decimal is the default. Some operating systems use another character to represent hex (such as # or $). However, we will use a “h”.

       0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
  0    0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
  1   16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31
  2   32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47
  3   48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63
  4   64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79
  5   80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95
  6   96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111
  7  112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
  8  128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  9  144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
  A  160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
  B  176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
  C  192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
  D  208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
  E  224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
  F  240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

To use the chart, follow down the left edge to get the first hex digit. Then go to the top row for the second digit. The chart can be used to convert hex to decimal, and vice-versa.

I will leave you with the hexadecimal loaders for the T/S 1000 and T/S 2068. Enter the appropriate one for your computer, and save it for use in subsequent lessons. This loader will serve you well, while we are hand-assembling our MC, due to our routines being short. You do not need a full assembler until you are involved writing much larger MC routines, or entire MC programs.

The listings are self explanatory. You are prompted for an address in decimal, then begin entering your code in hex. Enter a “s” to stop. Your code is reflected on the sceen to aid error spotting. The T/S 2068 will automatically save your code and clear the loader from memory. Unfortunately, that cannot be easily accomplished on the T/S 1000. We will discuss this in greater detail next time.

Syd Wyncoop
[address omitted]
[phone number omitted]

Hex Loader TS1000
10 Print "Enter start address in decimal"
20 Input a
30 Cls
40 Print "Code begins at "; a
50 Print
60 Let a$ = ""
70 If a$ = "" then Input a$
80 If a$ = "s" then Stop
90 Poke a, 16 * Code a$ + Code a$(2) - 476
100 Print a$( to 2);" ";
110 Let a = a + 1
120 Let a$ = a$(3 to )
130 Goto 70
Hex Loader TS2068
10 Cls: Input "Enter start address in decimal"; a: Clear a - 1: Input "Please put on the caps lock"; a$
20 Let a = Peek 23730 + Peek 23731 * 256 +1: Print "Code begins at "; a: Let x = a
30 Let a$ = ""
40 If a$ = "" then Input a$
50 If a$ = "S" then Goto 70
60 Poke a, 16 * (Code a$(1) - 48 - (7 and a$(1) > "9")) + Code a$(2) - 48 - (7 and a$(2) > "9"): Print a$( to 2);" ";: Let a = a + 1: Let a$ = a$(3 to ): Goto 40
70 Let y = a + 1 - x: Save "mc" Code x, y: Save "mc" Code x, y: New