Jump to content
IGNORED

Assembler A instruction: Carry vs. Overflow status bits


Recommended Posts

I'm confused a little about the meaning of the carry and overflow status bits after performing an A (add) instruction in TI-99 assembler.

 

According to the E/A manual, "When there is a carry of bit zero, the carry status bit is set. When there is an overflow, the overflow status bit is set."

 

Aside from the part about the carry bit being ambiguous grammar, I can't figure out under what conditions the overflow bit should be set. I tried the example given in the manual, adding >8 to >3124, and as the manual stated, I got the carry bit and the overflow bit reset. All as expected, no problems here.

 

But then I tried to add >7777 to >7777. As far as I know, this should result in no overflow. >7777 plus >7777 equals >EEEE. No digit carries over to the next, and the addition doesn't overflow beyond the 16-bit space. However, when I add these two values, it results in the overflow bit being set.

 

Furthermore, if I add >9999 to >7777, now, I get the carry bit set, which is as expected, yet I get the overflow bit reset.

 

I cannot understand the logic guiding the setting/resetting of the overflow bit. Am I completely missing something here?

 

Thanks.

Link to comment
Share on other sites

7777 > 0, EEEE < 0, accordingly, the OV is set. You just added two positive numbers and got a negative result, which is not possible from an arithmetic viewpoint.

 

9999 < 0, so actually you are subtracting from 7777 when you add it. One argument is positive, one is negative, result is 1110 > 0, so this is plausible.

Link to comment
Share on other sites

Actually, it does both at the same time (unsigned and signed). The difference is in your application's point of view. For unsigned values there is no overflow; only carry.

 

The only command where signed vs. unsigned matters in the result is multiplication and division, for which reason the TMS9995 has specific MPYS and DIVS commands.

Link to comment
Share on other sites

Ok, that's good for me to know, because I was planning on writing a routine that could multiply 2 signed 32-bit numbers together.

 

Since the E/A manual states that the MUL instruction is for unsigned integers only, and since X * -Y = -(X * Y), can I just take the 2's complement of the negative number to convert it into a positive, perform the multiplication, then take the 2's complement again of the result?

Link to comment
Share on other sites

I gotcha. I'll have to write a 32-bit 2's complement inverter routine then. Thanks for the tips.

 

Also, with the TMS built-in MUL routine, only the result is 32-bit. I'm trying to write a routine that multiplies two 32-bit operands. As I understand it, that involves doing successive multiplications on each 16-bit word. I don't have the algorithm nailed down yet, but I believe I understand it in principle.

Link to comment
Share on other sites

It is not the ADD instruction that is treating the numbers as 2's complement. Binary adders simply add, they don't care or know how *you* are evaluating your data, i.e. either signed or unsigned. It is the overflow flag logic in the CPU that determines if the flag is associated with signed numbers or not. Also, overflow is not necessarily consistent between CPUs (or instructions for that matter) so be sure to check your datasheet!

 

The 9900 overflow flag is described in the E/A manual on pg.42 section 3.1.3.5:

 

"The overflow bit is set when the result of an arithmetic operation is too large or too small to be represented correctly in two's complement representation.

 

In addition operations, the overflow bit is set when the most significant bits of the operands are equal and the most significant bit of the result is not equal to the most significant bit of the destination operand."

 

----

Edit: The quote above is not complete, the E/A manual continues on the following page with descriptions of all the overflow situations, which are more clearly explained in the datahsheet IMO.

----

 

Also, pg.22 of the TMS9900 Data Manual gives the exact conditions for when the overflow bit is set for all 9900 instructions that affect the overflow flag.

Edited by matthew180
Link to comment
Share on other sites

I believe the 99/4A assembly book with children's blocks on the front cover has routines for doing 32-bit multiply and divide. I'm pretty sure you can find a PDF of that book somewhere. My copy is packed in a box somewhere or I would check. Maybe someone else can confirm?

Link to comment
Share on other sites

@matthew180: This is essentially what I said (or meant to say). The arithmetic operations except for MPY and DIV are not dependent on signed or unsigned arithmetic. Overflow always means that when interpreting the numbers as signed, the sign of the result is not consistent with arithmetic rules.

 

In that context, I found it particular interesting to see that

 

 

CLR R0
S     R0,R0

 

sets the carry bit. Failing to do this was one of the ugly bugs that I had in the 9900 emulation in MESS.

 

This gives some insight into the internal subtraction operation.

Link to comment
Share on other sites

In that context, I found it particular interesting to see that

 

CLR R0
S     R0,R0

sets the carry bit. Failing to do this was one of the ugly bugs that I had in the 9900 emulation in MESS.

 

This gives some insight into the internal subtraction operation.

 

From a software or programmers perspective that would seem strange. From a hardware perspective it is pretty common knowledge. CPUs subtract by adding the two's complement of the second number:

 

A - B == A + (-B) == A + !B + 1 (or in C: A+(~B+1))

 

The adder in the ALU will have a selectable complement (not) circuit for the input of the second operand and the +1 can be accomplished by setting the carry-in to '1' before the add. Thus:

 

Original:
 0000 A
-0000 B

Invert B and set carry in to '1':
 0000 A
+1111 B
    1 carry in

Addition Rules:
0+0 = 0
1+0 = 1
0+1 = 1
1+1 = 0 carry 1

 1 111  carry during add
   0000 A
   1111 B
      1 carry in
 ------
 1 0000
.

So you get 0000 carry 1 as the output of 0 - 0. IMO emulator developers would have a much easier time if they would learn a little about how the hardware is actually implemented. Every emulator I have ever seen code for always implements the *functionality* of the hardware rather than actually emulating the hardware itself.

Edited by matthew180
  • Like 1
Link to comment
Share on other sites

This is way more info than I was expecting to receive, but that's good because I'm learning a lot! Thanks again, for all the input. I'm one who tends to want to know all the nitty-gritty under the hood stuff of how or why a process works the way it does. Understanding the logic that's implemented in the TMS's ALU is definitely something that I'd be interested in. I'm going to take the advice here and give the datasheet a thorough inspection.

 

Thanks again everybody.

Link to comment
Share on other sites

Ok, that's good for me to know, because I was planning on writing a routine that could multiply 2 signed 32-bit numbers together.

 

Since the E/A manual states that the MUL instruction is for unsigned integers only, and since X * -Y = -(X * Y), can I just take the 2's complement of the negative number to convert it into a positive, perform the multiplication, then take the 2's complement again of the result?

 

I look at the signs before I start. So, if I'm multiplying -x by +y then I know my result is going to be negative. Then I convert the -x to +x and do the multiplication, then negate the result at the end. Not sure if that's the most efficient way of doing it, but it worked for me ;-)

Link to comment
Share on other sites

Another note: Multiplication on TMS processors provides 32 bit results.

 

You can, of course, ensure that your operands are positive and then correct the sign. But you have to do this on the 32 bit result.

Yep. That's what I do.

Link to comment
Share on other sites

So you get 0000 carry 1 as the output of 0 - 0. IMO emulator developers would have a much easier time if they would learn a little about how the hardware is actually implemented. Every emulator I have ever seen code for always implements the *functionality* of the hardware rather than actually emulating the hardware itself.

 

Two possible reasons:

  • We don't have the hardware description at that level. We're already happy to have specifications that are detailed enough to come sufficiently close to it.
  • We cannot fully simulate all processes happening in hardware. Hardware has the obvious advantage that many things can happen at the same time, e.g. the address bus propagating the address to all attached devices almost synchronously. Emulations are always limited in that respect.
Link to comment
Share on other sites

I gotcha. I'll have to write a 32-bit 2's complement inverter routine then. Thanks for the tips.

 

Also, with the TMS built-in MUL routine, only the result is 32-bit. I'm trying to write a routine that multiplies two 32-bit operands. As I understand it, that involves doing successive multiplications on each 16-bit word. I don't have the algorithm nailed down yet, but I believe I understand it in principle.

 

This might help:

 

Unsigned 32-bit multiply:

 

ASM: DU* ( ud:x ud:y -- ud:x*y)
\ multiplies unsigned-double values x and y leaving an unsigned-
\ double result.
    r3  r13  mov,
    r4  r14  mov,
    r5  r15  mov,       \ save
    r0  clr,
    *sp  r6  mov,       \ how(l2)
    2 sp ()  r7  mov,   \ low(l2)
    4 sp ()  r3  mov,
    ne if,              \ check how(l1)=0
        r0  inc,
    endif,      
    r6  r3  mov,      
    ne if,              \ check how(l2)=0
        r0  inc,
    endif,      
    r0  1  ci,
    lte if,
        r0  clr,                \ overflow flag
        6 r14 ()  r1  mov,      \ low(l1)
        r1  r3  mov,            \ copy low(l1)
        r7  r1  mpy,            \ low(l2)*low(l1)
        r6  r3  mpy,            \ how(l2)*low(l1)
        r4  r1  a,
        oc if,
            r0  seto,           \ overflow
        else,
            r3  abs,
            ne if,
                r0  seto,       \ overflow
            else,
                4 r14 ()  r3  mov,      \ how(l1)
                r7  r3  mpy,            \ low(l2)*how(l1)
                r4  r1 a,
                oc if,
                    r0  seto,   \ overflow
                else,
                    r3  abs,
                    ne if,
                        r0  seto,   \ overflow
                    else,
                        r14  4  ai,           \ adjust stack
                        r1  r14 **  mov,      \ how(result)
                        r2  2 r14 ()  mov,    \ low(result)
                    endif,
                endif,
            endif,
        endif,
    else,
        \ both how(l1) and how(l2) are > 0...
        r0  seto,       \ overflow
    endif,
    r0  r0  mov,        \ check overflow flag
    ne if,
        \ set overflow
        r14  2  ai,     \ adjust stack
        r14 **  seto, 
        2 r14 ()  seto, \ push -1
    endif,
   
    r13  r3  mov, 
    r14  r4  mov, 
    r15  r5  mov,       \ restore
;ASM

 

Signed 32 bit multiply:

 

: D* ( d:x d:y  -- d:x*y)
\ multiplies signed-double values x and y leaving an signed-
\ double result.
\ Dependencies: DNEGATE 2SWAP D< DU*
  DUP               \ get sign of y
  3 PICK            \ get sign of x
  XOR               \ flag. if <0 then signs are different
  >R                \ move flag to return stack
  DABS              \ negate y if negative
  2SWAP DABS        \ negate x if negative
  DU*               \ perfom the multiplication
  R>                \ get the flag
  0< IF DNEGATE THEN \ if flag <0 then negate result
;

 

Okay, the unsigned 32-bit multiply is Forth, but, as you probably noticed, it uses the unsigned 32-bit multiply to do the heavy lifting. Before performing the multiply it checks the signs of the two 32 bit numbers, and if they are different then you know the final result should be negative. If it is negate then it calls DNEGATE to negate the 32 bit result:

 

 

ASM: DNEGATE ( d:x -- d:-x)
\ negates the double number on the stack if it is non 0
\ Dependencies: None
    *sp  r0  mov,           \ get high word
    2 sp ()  r1 mov,        \ get low word
    r0 r1  a,               \ check for zero
    ne if,                  \ if non zero then do negate...
        *sp inv,            \ invert high word
        2 sp ()  neg,       \ negate low word
        oc if,
          *sp inc,          \ if carry then increase high word
        endif,
    endif,
;ASM

 

The assembly code posted above is in Forth assembler format, so it has cool features like IF...THEN...ELSE at the assembly language level, but you should be able to follow it. Shout out if anything doesn't make sense. Hopefully it'll be useful to you.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...