In this chapter you will learn about the six remaining instructions in the 1802 instruction set. So far we have made considerable use of the fact that Reset forces P=0 and R0=0000. However, it is often convenient to use registers other than R0 for the program counter. Chapter 7 deals with one such occasion. In this chapter, therefore, you will learn how to modify the contents of P. You will also learn about some other ways to operate on X.
You only need one program to experiment with the instructions for this chapter, so I hope you do not mind its being rather large. To check your work in loading it, sequence memory. Then after keying in the last byte of Program 6.1 flip the M/P switch on and verify that the next byte contains 4A. If not, you may have dropped a byte or inserted one too many.
.. PROGRAM 6.1 -- TEST CONTROL INSTRUCTIONS .. 0000 90 GHI 0 .. SET UP R1-R4: 0001 B1 PHI 1 .. ALL IN PAGE 00 0002 B2 PHI 2 0003 B3 PHI 3 0004 B4 PHI 4 0005 F842 LDI ONE 0007 A1 PLO 1 .. R1=0042 0008 F846 LDI FOUR 000A A4 PLO 4 .. R4=0046 000B F83E NEW: LDI TWO .. (LOOP TO HERE) 000D A2 PLO 2 .. R2=003E 000E F81B LDI DOIT 0010 A3 PLO 3 .. R3=001B 0011 E3 SEX 3 .. KEYIN OPCODE: 0012 3F12 BN4 * .. WAIT FOR IT 0014 6C INP 4 .. HERE IT IS 0015 64 OUT 4 .. ECHO IT 0016 3716 B4 * .. WAIT FOR RELEASE 0018 F83A LDI CON .. MOVE R3 001A A3 PLO 3 001B C4 DOIT: NOP .. <-- OPCODE GOES HERE 001C 7B SEQ .. Q ON IF FELL THRU 001D A5 DISP: PLO 5 .. SAVE ACCUMULATOR 001E 64 OUT 4 .. DISPLAY M(R(X)) 001F C4 NOP 0020 3F20 BN4 * .. HOLD 0022 F0 LDX .. BACK UP REGISTER 0023 73 STXD .. POINTED TO BY X 0024 7A REQ 0025 E2 SEX 2 .. FORCE X=2 0026 64 OUT 4 .. DISPLAY M(R(2)) 0027 3727 B4 * 0029 64 OUT 4 .. ALSO NEXT BYTE 002A 3F2A BN4 * 002C 85 GLO 5 .. RECOVER SAVED D 002D 52 STR 2 .. SO TO DISPLAY IT 002E 64 OUT 4 002F 372F B4 * 0031 E3 SEX 3 .. NOW ALSO R3 0032 64 OUT 4 0033 C4 NOP .. IF THIS IS SKIP, 0034 300B BR NEW 0036 7B SEQ .. TURN ON Q 0037 300B BR NEW .. GO REPEAT 0039 00 IDL 003A 24 ,#24 .. R3 POINTS HERE 003B 33 ,#33 .. (DISTINCTIVE #) 003C FF ,#FF 003D 22 ,#22 003E EE TWO: ,#EE .. R2 POINTS HERE 003F DD ,#DD 0040 BB ,#BB 0041 00 IDL 0042 F811 ONE: LDI #11 .. UNIQUE # IN D 0044 301D BR DISP .. GO DISPLAY IT 0046 F844 FOUR: LDI #44 .. DITTO 0048 301D BR DISP 004A
When you first put this program in, leave the M/P switch on and run it. This will help you understand it before trying to use it with new data. Also, if you made a keying error (like I did), you might notice it before clobbering the whole program. Be sure you understand how this program works! Let's review it:
First you will see that there are no instructions you have not already met. Also, aside from the fact that R1 and R4 point to them, there seems to be no way for the computer to execute the instructions between 0042 and 0049. R3 is set up to point to the NOP at 001B, so the first INP will replace that with what you key in (because X=3); we have done that before in slightly different ways. The program is set up to display six data bytes (on three pushes of the "I" key) before going back for a repeat. The six things are:
The opcode you keyed in.
The byte pointed to by the address register pointed to by X. This register is decremented in a tricky way after the output. Convince yourself that it works without destroying memory. Wouldn't it have been nicer to have a "Decrement Register pointed to by X" instruction? Alas, they ran out of opcodes before they ran out of good instructions. In this case, the program assumes that we do not know what is in X, and this is a way to find out (the display will be distinctive, so you can determine which register and which memory location was used).
The memory byte pointed to by R2. This may or may not be the same as the previous display. We assume it will be different most of the time, since X=3 before the unknown instruction.
The next byte in memory (still under R2).
The accumulator containing the results of the unknown instruction. Recall that this is saved in R5, then stored into memory for display here. If the instruction does not change the accumulator, its previous contents will be displayed, which is the "3A" loaded at 0018. Keep that in mind as you run this program.
The byte in memory pointed to by R3. This should help us to discover if R3 was altered, or if the memory under it was changed.
It might have been better to also display the low half of R2 and R3, but the program was already getting too big. You may add the extra instructions to do this as an exercise.
The Q register is set by this program immediately after the unknown opcode, so if for any reason that instruction does not proceed to the next, Q will not come on. It is turned off again the next time you push "I". If you were to change the NOP in 0033 to a LSKP, Q would also come on when the last datum is displayed. We will do that a little later.
At locations 0042 and 0046 there are LDI instructions which then branch to the display routine starting at 001D (notice this misses the SEQ instruction). If you see "11" or "44" when the accumulator is displayed, you will know which instructions were executed.
Now, on with the show. After trying it once or twice with M/P on (notice that it says that D="DD" because it could not store the actual contents into memory), try the program with M/P off and key in a NOP (C4) to see the difference. Try "SEX 2" (E2) to see how the second display is changed. Try something to modify the accumulator; say, GLO 0 (80). You might find it worthwhile to try GLO 1 and GLO 4 and remember the results. What happens if you key in "F8"? Do you know why? If not, review the LDI instruction in Chapter 4. Notice what happens if you key in an instruction to increment or decrement R2 or R3. Try INC 4, followed by GLO 4. How about LDXA (72)? What I want you to do is become familiar with this program. You might even try some instructions I have not suggested, but be careful! Some instructions (such as branches) will cause the program to blow up, forcing you to key the program back in; be sure you understand what to expect from each experiment before doing it.
Now let's try some new opcodes. Just to be safe, quickly scan the program (examine mode) to be sure you have not clobbered anything; if so, fix it. Now try the opcode "D1". Did Q come on? What was in the accumulator? Can you guess why the D1 opcode caused the computer to execute the instructions at 0042 instead of the SEQ at 001C? Reset the computer and try the same opcode, but this time enter a GLO 1 (81) opcode after it. Did you notice that Q came on this time? What is in R1? Why is it not 42? Key in GLO 4 (just to see what is in R4) then key in "D4", followed by "84" again. What happened to R4? Now what happens if you repeat "D1" or "D4" again? Why do you suppose the instructions at 0042 or 0046 are no longer executed? Do you understand that since R1 and R4 both point to 001C, they can no longer get to 0042 or 0046?
If I tell you that the "D1" opcode puts a "1" into P (making R1 the Program Counter), does that help you to understand what happened? R0 is still pointing to the next byte after the "D1" opcode, i.e. R0=001C. But R1 is now used to fetch instructions, and the next one loads "11" into the accumulator. It then branches to the display routine (all the time incrementing and modifying R1, because R1 is the PC). The second time through the instruction at 001B, R1 is already the PC, so nothing new happens, and the next intruction is the SEQ at 001C. R0 still has not changed. You can see that by keying in INC 0. If you do this first, the SEQ instruction will be skipped, because P=0. But after a D1 or D4 you can increment R0 as much as you like, because it is not being used any more (you can see how much it has been incremented with a GLO 0). In the same way you can increment R4 or R1 (whichever is not the PC); just be careful not to set P back to the altered register if you do not know that it points to a valid instruction, or you may wipe out your program.
|Copy r (the low four bits of the instruction) into P, making the designated address register the new Program Counter.|
Because the SEP instruction leaves the old PC register pointing to what was going to be the next instruction, we have a very convenient way to jump to a subroutine. A subroutine is a little program that does some function. A main program normally calls a subroutine by jumping to its beginning; the subroutine does its job, and when it is finished it returns to where it left off in the main program that called it. Most computers have special instructions (called Call instructions) which remember somehow where they came from, so that when the subroutine is ready to return, another special Return instruction returns execution to the instruction immediately following the call. In the 1802 the SEP instruction is used for both Call and Return.
To get an idea how this might work, change the BR instruction in 0044 of Program 6.1 to a NOP and a SEP 0 ("D0"). Run the program and key in a SEP 1 (D1). Do you know why the Q came on? What is in P when the display routine was executed? Test your answer by keying in INC 1 and INC 0. Remember, Q will not come on after incrementing the PC.
Now reset and restart the computer, then key in SEP 1 again. Do it a second time. Why does the D register show "44" and not "11"? To be sure that R4 has not been affected, try SEP 4. You see, what happened is that after the LDI #11 instruction, we set P back to 0 (which continued with the SEQ instruction), leaving R1 pointing to location 0046; the next SEP 1 instruction returned to load "44" into the accumulator. In essence, the main program "Called" the subroutine at 0042 by executing the first SEP 1; this subroutine "Returned" to the main program by the SEP 0. Or you can think of the SEP 0 as a Call to the display routine, which returns to the LDI #44 instruction with a SEP 1. There is really much more subtlety here than I can explain in this chapter. It turns out that this capability makes the 1802 a much more powerful computer than any other eight bit microprocessor.
Let's move on to some other instructions which modify P (and X). Quickly verify that Program 6.1 is still in your computer (i.e. that you have not destroyed part of it by your experiments). Then key in the opcode "70". This instruction does several things; you will want to compare it to a NOP (as an example of an instruction that does nothing), to SEP 4, SEX 2, and IRX (instructions that each do one of the many things that the "70" opcode did). Are you satisfied that you know what happened?
Now try to find a logical basis for all of these things. Since R3 was incremented and X=3, it seems that this instruction operates on the register pointed to by X. Furthermore, the opcode "70" has neither a "2" nor a "4" in it, so we need to look elsewhere to know how X and P got set. But notice: R3 pointed to a byte in memory with both a 2 and a 4. Check out this hypothesis by changing the "24" in 003A to "14"; does the second display now show "F8" (the byte pointed to by R1)? Change 003A to "21"; now does P come out to be 1? Do you think you know what this instruction does?
|Read the byte from the memory location pointed to by the register pointed to by X; then increment that register. Copy the right 4 bits of the byte read into P, and the left four bits into X. Then enable interrupts (set IE to 1).|
This instruction is intended to be used when the computer is finished servicing an interrupt, and in that sense it "returns from the interrupt" to the program which was interrupted. What I mean by interrupts will be explained more completely in Chapter 7. Notice that this instruction enables them (whatever they are). There is another instruction which disables interrupts. Try opcode "71" in Program 6.1 and convince yourself that you cannot tell the difference between it and the RET instruction.
||Return and Disable Interrupts||
|Read the byte from the memory location pointed to by the register pointed to by X; then increment that register. Copy the right 4 bits of the byte read into P, and the left four bits into X. Then disable interrupts (set IE to 0).|
The only difference between RET and DIS is the Interrupt Enable flag inside the 1802. Fortunately, we have a way of looking at that flag in our program. We can test whether interrupts are enabled by that one skip instruction that we have not yet analysed. Change the NOP in location 0033 to an LSZ (CE) and convince yourself that if and only if the instruction you key into the computer results in a zero accumulator, Q comes on with the sixth display and stays on through the entry of the next opcode. You may test this by keying in GLO 0 (80) for non-zero and GHI 0 (90) for zero results. When you are satisfied that you understand when and how the Q comes on, change that LSZ to a "CC" (in 0033). Now compare the results of Q after the RET and DIS instructions. Do you see that this instruction skips when interrupts are enabled?
||Long Skip if Interrupts are Enabled||
|If the internal IE flag enabling interrupts is True=1, skip the next two instruction bytes; if False=0, take the next instruction in sequence.|
Exercise: Devise an experiment to see whether interrupts are enabled or disabled (or unaffected) by Reset.
The RET and DIS instructions are convenient any time you want to load both X and P. This is expecially true when the program itself needs to compute the value for one or both of these four-bit registers. For example, you might write a subroutine which will return to any of three different program counters, depending on what was in P when the subroutine was called. Subroutines are most useful when they can be called from (almost) anywhere in the rest of the program. So the program will save its own value for X and P in memory somewhere, then call the subroutine with a SEP instruction. When the subroutine is ready to return, it sets X to the register which points to that memory byte, and executes a DIS (or RET) instruction, taking the program back to the instruction following the SEP, with the appropriate values in X and P.
But how does the calling program know its own X and P? I'm glad you asked. Obviously, since you wrote the program, you know what is in X and P, so you can let the computer know with an LDI instruction. But suppose you wrote a clever program that may have different values in X and P for different times through the program (something like Program 6.1, where P may be 0, 1, or 4, depending on what instructions you previously keyed in). How then can we know what to save? Well, obviously I would not bring up the subject if there were not an instruction to do just that, so try opcode "79" in Program 6.1.
If you tried the "79" and are not now thoroughly confused, either you already understand this new instruction as well as Program 6.1 (and have no need to be reading this book), or you do not understand the problem at all. Do not reset and try another opcode! If you already did you may have clobbered the whole program; go back and verify it. Fix any bytes that are not correct, then run once with the opcode "79". Why does the program no longer respond to the the "I" key? Did the computer stop? Reset it and try IDL (00). How is that different? Reset and try DEC 0 (20); is that like the IDL or the new opcode? So you can assume the computer did not stop. The first display is supposed to be the byte pointed to by the register pointed to by X. What evidence is there that this instruction was really executed? (Hint; look at Q.) So which register points to a "C4"? You already know there is no C4 anywhere near where R2 and R3 point; the same is true for R1 and R4. You might guess that R7 or R9 might be involved but none of the other instructions in the "7x" series have register numbers attached to them. What about R0? Where does R0 point? Be careful, remember R0 is the PC.
Reset the computer and try SEX 0 (E0). Aha! But why did the computer quit responding? Look at the program again, especially around 0022-0023. If X=0 and you decrement the register pointed to by X, are you not also decrementing the PC? Let's get out of this trap by changing the STXD in 0023 to a NOP. Notice that there is an STXD (73) in location 0024 also, not a REQ (7A)! Why? Think about what the STXD in 0023 did (What is in X? Where does R0 point? What is in D? When was D last loaded?). Put the REQ back into 0024. Also put your constants back into locations 0039-0041. Reset and run the program again, first with a NOP so you see how it is different without the STXD. Now try SEX 0. Finally try the "79" again. You already know this instruction sets X to 0 in our test. Does it change the accumulator? What about the register X previously pointed to (R3), or the memory that register points to? What happened to R2? How about the memory it used to point to?
This instruction is so complicated that I am going to give you another experiment to test it. Fix location 003E (or notice what its new content is) and reset then run Program 6.1 again (with a NOP instead of the STXD). This time key in a SEP4 (D4) first, so that you know that P=4 for the second keyin. This is important. Now key in "79" and look at the results. What was displayed first, that is, what evidence is there as to the contents of X? If X=0. what would you have seen? Remembering that P is still 4, key in SEX 0 for the third entry. Are you convinced that this time the "79" did not set X=0? If not 0, then what did it set X to? Where did the "C4" come from? Which register points to a NOP when the OUT 4 instruction in 001E is executed? Would you believe that this instruction copies P into X? Now, remembering that X is set to 3 before the keyed-in opcode is executed, why do you suppose neither R3 nor the memory under R3 is affected by this instruction, but rather R2 and the memory it points to?
I'll tell you. There are three address registers with special (hardware) significance in the 1802: R0, as you know, is forced to 0000 and becomes the program counter at Reset; it is also used for DMA as you will see in Chapter 7. R1 is used for interrupts, as you will also see in Chapter 7. R2 is also significant in the servicing of interrupts (yes, Chapter 7 again!), but this instruction, and only this instruction, uses R2 and the memory pointed to by R2 regardless of the contents of X and P. As you can see, something was stored into the memory location pointed to by R2, then R2 was decremented. What was stored? Remember the first time you tried this instruction, "30" was stored; the second time it stored "34". The first time P=0 and the second time P=4, so it is a good guess that the low four bits are a copy of P. What about the high four bits? Recall that the RET and DIS instructions expect to see a value for P in the low four bits of the memory byte pointed to by X and a value for X in the high four bits. Thus it is reasonable to assume that the "79" instruction copies X into the high four bits of the byte it stores, and in fact, X was 3 in both cases.
||Save X and P in T||
|Copy X and P into the T register, store T in the memory byte pointed to by R2, then decrement R2; finally copy P into X.|
What's this about a "T register"? The only function of the T register in the 1802 is to receive a copy of X and P. The MARK instruction does this, and an interrupt also does it (these pop up all over this chapter, don't they?). As you will see in the next chapter, we need some way to save and restore what the computer is doing in the case of an interrupt; the T register helps us do this. But of course you have to save the contents of T, so you can put X and P back at the end of the interrupt. The "78" instruction does this. Try it, but first you might want to put the STXD back into location 0023 and fix up the constants in 003A-0040. Notice that now when you run the program and key in 78, the first display (which is the contents of the memory byte pointed to by the register pointed to by X) shows "34" (assuming your last experiment was the MARK instruction after the SEP 4). This is the same as that saved in T by the previous MARK. Notice that T is not affected by Reset. R2 is not involved in this instruction, and D is not changed. If you wanted, you could execute another MARK (don't forget to change the STXD to a NOP!) to put a different value into T, which a subsequent "78" will then reflect.
|Copy the contents of the T register into the memory location pointed to by the address register pointed to by X.|
Now you have learned all the instructions your computer is capable of executing. If you look carefully, you will notice that we never studied the opcode "68". That's because it is not a defined 1802 instruction. It has the form of an INP instruction, but 0 is not a defined input port, so if you execute it (try it!) nothing is input. "Nothing" is the answer to a question; it is data, and something will be put in the accumulator and memory (so now you know what the computer uses to mean "nothing").
However, since the result of the "68" opcode is unpredictable, it should not be used in your programs. In fact, "68" is the first byte of a series of additional instructions for the 1804 and 1805 microprocessors.
In case you had not noticed, the "60" opcode (IRX) is in the form of an OUT instruction, but again, since there is no port 0, the output data goes nowhere. However all other functions of the OUT instruction (i.e. incrementing the register pointed to by X) are still performed. So it turned out to be a useful instruction after all. Clever, these Yankees.
In Chapter 7 we will study some of the special hardware features of the 1802 which enable it to do certain kinds of input and output. Be sure you understand all of the instructions covered in Chapters 3-6 so that you will be able to follow the programs presented in Chapter 7.
[ << Chapter 5 ] [ Index ] [ Chapter 7 >> ]
* (A Short Course In Programming is Copyright 1980 by Tom Pittman, and is reproduced in TinyELF's help book with the author's permission. Visit Tom's website.)