August 8th, 2019

The SuperH-3, part 4: Basic arithmetic

Okay, we’re ready to do some arithmetic. Due to the limited instruction encoding space, there isn’t room for any three-operand instructions.¹ All of the arithmetic instructions are two-operand, where the second source operand also acts as the destination.

    ADD     Rm, Rn      ; Rn += Rm    , no effect on T
    ADD     #imm, Rn    ; Rn += imm   , no effect on T
    ADDC    Rm, Rn      ; Rn += Rm + T, T receives carry
    ADDV    Rm, Rn      ; Rn += Rm    , T receives signed overflow

The ADD instructions add two values and put the result in the second register. You can add two registers together, or you can add a signed 8-bit immediate to the destination register.

The ADDC instruction treats the T flag as a carry flag: It is added to the sum, and it receives the carry of the result.

The ADDV instruction treats the T flag as an overflow flag: It reports whether a signed overflow occurred.

Okay, subtraction is going to look really similar now.

    SUB     Rm, Rn      ; Rn -= Rm    , no effect on T
    SUB     #imm, Rn    ; Rn -= imm   , no effect on T
    SUBC    Rm, Rn      ; Rn -= Rm + T, T receives borrow
    SUBV    Rm, Rn      ; Rn -= Rm    , T receives signed underflow

Basically the same as addition, except you’re now subtracting. The SH-3 treats T as a borrow flag in the case of SUBC, whereas for SUBV it reports whether a signed underflow occurred.

Arithmetic negation is up next.

    NEG     Rm, Rn      ; Rn = -Rm    , no effect on T
    NEGC    Rm, Rn      ; Rn = -Rm - T, T receives borrow

There is no NEGV, but overflow occurs only if the value is 0x80000000, so I guess you could test for that value specifically.

There is a special instruction for for decrementing a register:

    DT      Rn          ; Rn = Rn - 1, T  = (Rn == 0)

The decrement and test instruction decrements a register and compares the result against zero. This is presumably for counted loops.

Next come the comparison instructions.

    CMP/EQ #imm, r0     ; T = (r0 == signed 8-bit immediate)
    CMP/EQ Rm, Rn       ; T = (Rn == Rm)
    CMP/HS Rm, Rn       ; T = (Rn ≥ Rm), unsigned comparison
    CMP/GE Rm, Rn       ; T = (Rn ≥ Rm),   signed comparison
    CMP/HI Rm, Rn       ; T = (Rn > Rm), unsigned comparison
    CMP/GT Rm, Rn       ; T = (Rn > Rm),   signed comparison
    CMP/PZ Rn           ; T = (Rn ≥ 0),    signed comparison
    CMP/PL Rn           ; T = (Rn > 0),    signed comparison
    CMP/STR Rm, Rn      ; T = 1 iff any corresponding bytes are equal

These instructions set the T flag according to a particular comparison. Note that the comparison is backward! For example, CMP/GE r1, r2 does not check whether r1 ≥ r2; rather, it checks whether r2 ≥ r1. This takes a lot of getting used to.

You have the special ability to compare r0 for equality with a signed 8-bit immediate. Otherwise, you can compare two registers against each other, or a register against zero.

The special CMP/STR compares two registers to determine whether any of the four component bytes are equal. It’s clear from the mnemonic that the intended purpose is to search for a null terminator in a string. You set Rn to zero and then do a CMP/STR against every longword in the string until it says, “Hey, I found a zero byte!” and then you can study that longword to see where the zero byte is.

The processor documentation doesn’t explain why they chose the names for the mnemonics, but I can guess.

Condition Meaning
EQ equal
HS high or same
GE greater or equal
HI high
GT greater than
PZ plus or zero
PL plus
STR string

It took me a while to come up with a plausible explanation for HS.

Exercise 1: Synthesize the SETT and CLRT instructions.

Exercise 2: Perform the opposite of the MOVT instruction: Set the T register to 0 if a register is zero, or 1 if the register is nonzero.

The last arithmetic instructions are the extension instructions.

    EXTS.B Rm, Rn       ; sign extend byte in Rm to Rn
    EXTS.W Rm, Rn       ; sign extend word in Rm to Rn
    EXTU.B Rm, Rn       ; zero extend byte in Rm to Rn
    EXTU.W Rm, Rn       ; zero extend word in Rm to Rn

That’s it for the basic arithmetic instructions. We’ll start looking at the more complicated arithmetic instructions next time, starting with multiplication.

¹ Well, okay, you can have three-operand instructions if some of them are hard-coded! But that’s not what I mean. I mean three-operand instructions where the programmer can choose all three of the operands.

Topics
History

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

6 comments

Discussion is closed. Login to edit/delete existing comments.

  • Alex Nesemann

    The comparison mnemonics come from the Motorola 68000's branch instructions. Hitachi was a major second source of 68Ks, so it's not much of a suprise that they borrowed from it.
    CMP/STR is handy when searching for any byte, not just zeroes. Just replicate the byte value in the register. If you're looking for ASCII 'a', you would compare with 0x61616161.
    Answer to exercise 1:
    SETT -> CMP/EQ R0, R0
    CLRTT -> CMP/NE R0, R0
    Answer...

    Read more
  • Kasper Brandt

    > T receives signed underflowAn underflow is when a floating point number is too small to be representable. A negative overflow is still an overflow.

  • Neil Rashbrook

    > It took me a while to come up with a plausible explanation for HS.

    I think ARM uses HS as a synonym for CS (carry set). (But then again it use a single comparison operator corresponding to (most of) these with multiple conditions on instructions.)

  • Harry Johnston

    I think this will work for exercise 2, but I’m not certain that I’ve understood the meaning of “borrow” correctly:

    CLRT
    NEGC R0, R0
    NEG R0, R0
    • Daniel Sturm

      I had to look up the exact semantics too. The ISA description gives it as follows (this will look awful I’m sure but I hope it’ll be readable)

      NEGC(long m,long n) /* NEGC Rm,Rn */
      { 
          unsigned long temp;
          temp=0-R[m];
          R[n]=temp-T;
          if (0<temp) T=1; else T=0;
          if (temp<R[n]) T=1;
          PC+=2;
      }
  • Zak Larue-Buckley

    Exercise 1:
    CMP/EQ r0, r0 => T = 1
    CMP/HI r0, r0 => T = 0

    Exercise 2:
    CMP/PL Rx => T = Rx > 0…but won’t work for negative Rx. Can’t think of way to solve without destroying Rx.