On Sun, Apr 5, 2026 at 6:28 AM B 9 [email protected]
<https://mailto:[email protected]> wrote:
[...W]hy do the interrupt handlers PUSH and POP the *A* register seemingly
> needlessly?
>
To answer my own question. At the highest level, the 8085 CPU has to use
the *A* register for every comparison, so its value has to be stashed
elsewhere if one needs to check two different things. The interrupt
handlers compare a byte in *A* to the list of characters, [Æ\Ø]Å{æ|ø}å, and
also check if they’ve hit the end of the list, marked by character 0xff.
Since there are very few registers on the Intel 8080, it is easiest to just
use PUSH PSW (or PUSH AF in Z80 dialect) to save the value of *A* on the
stack before doing the second comparison.
However, in the code I’m learning from (mostly the Tandy 200 system ROM),
I’ve never seen that idiom used, which is why it struck me as weird. I
think the main reason it isn’t seen is that doing PUSH and POP inside a
loop is very slow compared to using MOV to copy with a secondary register,
such as *B*. If necessary, one could PUSH and POP *B* outside of the loop.
So, instead of doing this:
PUSH PSW ; Why not just PUSH/POP BC?
LXI H,Mapping ; HL=>mapping = [Æ\Ø]Å{æ|ø}å
RxCheckForBrackets:
POP PSW
CMP M ; [\]{|}
JZ RxReplaceWithAccents
INX H
INX H
PUSH PSW
MOV A,M
CPI 0xff
JNZ RxCheckForBrackets
POP PSW
JMP RxDone
RxReplaceWithAccents:
INX H ; ÆØÅæøå
MOV A,M
RxDone:
I think it would be better to do this:
PUSH B
LXI H,Mapping ; HL=>mapping = [Æ\Ø]Å{æ|ø}å
RxCheckForBrackets:
CMP M ; [\]{|}
JZ RxReplaceWithAccents
INX H
INX H
MOV B,A
MOV A,M
CPI 0xff
MOV A,B
JNZ RxCheckForBrackets
JMP RxDone
RxReplaceWithAccents:
INX H ; ÆØÅæøå
MOV A,M
RxDone:
POP B
And, actually, if one reads the Rx handler code carefully, it jumps back to
a point in the ROM that overwrites *B* without using its value. That means
the PUSH and POP instructions I wrapped the code in above are not even
needed.
The Open handler is more difficult to analyze for whether *B* is being used
afterwards because I don’t yet understand how the BASIC parser works
(particularly RST 1). Fortunately, it turns out the character we want to
compare is passed into the handler in the *C* register, so we don’t need to
save *A* at all! Simply, use MOV A,C to restore *A* immediately after the
comparison.
MOV A,M
CPI 0xff
MOV A,C
JNZ OpenCheckForAccents
Which leads me to another thing that has surprised me as I continue
learning Model T assembly language: It’s common to do a clean up step
*between* the comparison (CPI, here) and a conditional jump (JNZ). That
smelt peculiar when I first saw it, rankling my sense of atomicity: Why
would you split the test from the action with something that might mess up
the status flags? I’m now coming to realize that this is probably the best
possible method for the 8085; there’s no cleaner way to restore *A* if the
jump is taken. Programming on the Model T must have been quite a puzzle and
I have a lot of respect for the folks who were able to craft such brilliant
solutions.
—b9