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

Reply via email to