EDTASM and the Shift Key

I think it was 1981 when my brother and I bought the "Series-I Editor Assembler — Cassette" for our TRS-80 Model III computer. It was an exciting prospect as it made it so much easier to write programs in assembly language to unlock the full potential of the machine.

We soon discovered a quirk in EDTASM (as everyone called it). When typing only the left shift key worked. The right shift key had no effect. It was relatively easy to avoid the problem by only using the left shift key. This went counter to our touch-typing training that insisted one use the shift key opposite the key to shift. The technique only really mattered on manual typewriters where it was easier for a pinkie to activate shift when it was the only finger being used on a hand. After all, it did have to physically lift the entire carriage fully and quickly to work properly.

Using only left shift on EDTASM soon became a habit when typing anywhere else. To this day I still only use the left shift key. The lettering on my keyboard's left shift key is barely legible while the right is as pristine as they day I bought it. The scars of childhood do stay with us.

It didn't take long to discover the problem was due to a slight difference between the hardware on the Model I and Model III. On both machines reading memory location $3880 (14464 decimal) gives the status of the shift keys. If no shift key is held down a 0 will be read back. On the Model I it will return 1 if either shift key is held. On the Model III it will return 1 if only the left shift key is held, 2 if the right shift key is held and 3 if both are held. However EDTASM looked for the shift keys it was clear it only checked the lower bit of $3880 and in testing on the Model III they either didn't notice or didn't care about the shift key problem.

Not having the wherewithal to solve the problem we simply lived with it. Some few years later in 2023 I wondered how exactly EDTASM went wrong and if it were possible to patch the problem. It wasn't hard to find the offending part of the code:

     rrc  b
     jr   c,shift
At this point B register is loaded with the contents of $3880. The RRC rotates the bits of B to the right and puts the lowest bit of B into the carry flag. The JR C, is a conditional jump which is taken if the carry flag is set. In short, if the shift key was pressed the program will go to shift where it does the expected processing. On the Model I this is fine; on the Model III the right shift key is ignored.

Adding two lines of code will fix the problem:

     rrc  b
     jr   c,shift
     rrc  b
     jr   c,shift
By repeating the test we end up testing the second bit which is the right shift key on the Model III. Thus if either key is pressed they are recognized and handled properly. As a bonus the code will work fine on a Model 1 as it will always have a zero in the second bit.

The problem is that this approach is not easily applied. We don't have the source code where adding a couple lines is trivial. To patch the binary we'd have to find some place to put the extra code and patch in a jump to it. Replacing the existing code with correct code of the same or smaller size makes the whole exercise is far simpler.

There is a way to do it. I encourage all the Z-80 programmers out there to give it a try and see if they can come up with a solution. Note that you can't use A register unless you preserve its value. Here are some hints you can peek at if needed:

Memory location $3880 only has the two shift keys. You can safely assume the other 6 bits are always zero.

You may need to change the JR condition.

There aren't many instructions that set flags based on the B register. RRC B is a two byte instruction so you may be able to use two of them.

Did you figure it out? Here's the solution I came up with: [ there are others; see below ]

     dec  b
     inc  b
     jr   nz,shift

Those two instructions effectively test if B register is zero or not. Since they are single byte instructions they fit in the same space as RRC B. It's a solution that I find doesn't come readily to mind as I'm so used to doing zero/non-zero tests with OR A on the accumulator. I'll get tunnel vision and think the only way is to load B into the accumulator first.

The process of writing this up raised two observations. First (as you may have discovered) there's an arguably better solution that fits in a mere 3 bytes. Since it is shorter the patch will need to include a NOP to line up:

     nop
     inc  b
     djnz shift

Second, why did the original code use RRC? Why not use SRL whch is much more idiomatic? My best guess is that the coders were more familiar with the 8080 whose only bit shifting instructions were rotates.

Third, (yeah, I said two but writing up the two lead to a third) I could have gotten the patch down to a single byte change! I went back and changed my second hint as it led almost directly to this solution:

     rrc  b
     jr   nz,shift

RRC B not only permutes the bits in B register but sets the zero flag based on the result. Thus we only need to change the condition on the JR to test both shift keys. Also note that RLC B or SLA B would work equally well in place of RRC B.

Back in 1981 this incident taught me a few early lessons in computing.

As a bonus I had some fun some 40-odd years later playing around with Z-80 code.


George Phillips, September 17, 2023. george -at- 48k.ca