Jump to content
matthew180

Assembly on the 99/4A

Recommended Posts

Posted (edited)
18 minutes ago, Lee Stewart said:

Considering the necessity of jumps, I see this as shortest:

       MOV  R0,R0
       JEQ  DONE
       SETO R0
       JLT  DONE
       INCT R0
DONE   ...

 

...lee

I like this solution.

Edited by Asmusr

Share this post


Link to post
Share on other sites
Posted (edited)
12 minutes ago, intvnut said:

It's annoying MOV is 14 + 4*mem.  Does ABS set Carry in a useful way for positive vs. negative numbers?  It's faster than MOV for positive numbers, and uses fewer memory cycles in all cases.

Answering my own question by looking at the code in MAME.

void tms99xx_device::alu_abs()
{
	// LAECO (from original word!)
	// O if >8000
	// C is alwas reset
	set_status_bit(ST_OV, m_current_value == 0x8000);
	set_status_bit(ST_C, false);
	compare_and_set_lae(m_current_value, 0);

	if ((m_current_value & 0x8000)!=0)
	{
		m_current_value = (((~m_current_value) & 0x0000ffff) + 1) & 0xffff;
		pulse_clock(2);     // If ABS is performed it takes one machine cycle more
	}
	else
	{
		MPC++; // skips over the next micro operation (MEMORY_WRITE)
	}
	pulse_clock(2);
}

It appears Carry isn't set usefully.  But, it appears the other flags are set based on the original value, not the value after negation.   So, you could shave a couple cycles with:

       ABS  R0     ; 12 + 2*mem (if positive)
       JEQ  DONE   ;  8 + 1*mem (n/t)
       SETO R0     ; 10 + 3*mem
       JLT  DONE   ;  8 + 1*mem (n/t)
       INCT R0     ; 10 + 3*mem
DONE   ...         ;------------
                   ; 48 + 10*mem

I guess I had missed the fine print "If MSB(SA) = 0 and (SA) ≠ 0" here:

 

1243193655_ScreenShot2020-05-16at12_01_13PM.thumb.png.5cea092e981e5d094aa1dcbc7a2d82a3.png

 

Edited by intvnut
Correct # of mems for ABS.
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
51 minutes ago, intvnut said:

It appears Carry isn't set usefully.  But, it appears the other flags are set based on the original value, not the value after negation.   So, you could shave a couple cycles with: ...

* R0 negative...
* Lee Stewart......
       MOV  R0,R0   ; 14 + 4*mem
       JEQ  DONE    ;  8 + 1*mem (n/t)
       SETO R0      ; 10 + 3*mem
       JLT  DONE    ;  8 + 1*mem
       INCT R0      ; 10 + 3*mem (n/t)
DONE   ...          ;-------------+
                    ; 40 + 9*mem |
                    ;-------------+

* intvnut.........
       ABS  R0     ; 14 + 3*mem (IF NEGATIVE)
       JEQ  DONE   ;  8 + 1*mem (n/t)
       SETO R0     ; 10 + 3*mem
       JLT  DONE   ;  8 + 1*mem 
       INCT R0     ; 10 + 3*mem (n/t)
DONE   ...         ;-------------+
                   ; 40 + 8*mem |
                   ;-------------+

We are closest when R0 < 0, with yours winning by only 1*mem. But, when R0 ≥ 0, yours is better by 2 + 2*mem,  so you win. :waving:

 

...lee

Edited by Lee Stewart
general corrections of timing
  • Like 1

Share this post


Link to post
Share on other sites

Ah yes, SETO does not affect the status bits. Good idea.

 

As for ABS, the Ed/Ass manual says the instruction does not affect the Carry bit, yet the TMS9900/9995 specs say it does. But it will always be reset. Here is a test I ran on my Geneve.

 

*        
*           LAECOPX
*   FFFE    10000***
*   FFFF    10000***
*   0000    00100***
*   0001    11000***
*   0002    11000***
*   7FFE    11000***
*   7FFF    11000***
*   8000    10001***
*   8001    10000***

  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
26 minutes ago, Lee Stewart said:
* Lee Stewart......
       MOV  R0,R0   ; 14 + 4*mem
       JEQ  DONE    ;  8 + 1*mem (n/t)
       SETO R0      ; 10 + 3*mem
       JLT  DONE    ;  8 + 1*mem (n/t)
       INCT R0      ; 10 + 3*mem
DONE   ...          ;-------------+
                    ; 50 + 12*mem |
                    ;-------------+

* intvnut.........
       ABS  R0     ; 14 + 3*mem (IF NEGATIVE)
       JEQ  DONE   ;  8 + 1*mem (n/t)
       SETO R0     ; 10 + 3*mem
       JLT  DONE   ;  8 + 1*mem (n/t)
       INCT R0     ; 10 + 3*mem
DONE   ...         ;-------------+
                   ; 50 + 11*mem |
                   ;-------------+

Your worst case scenario with R0 < 0 is 1 memory access less than mine, so you win. :waving:

 

...lee

I only got there with your help, naturally.  :D  You shaved more cycles off mine than I did off yours.

 

For the R0 < 0 case, the JLT gets taken, so we never actually end up with 50 + 11*mem.  That case ends up being 42 + 8*mem, since we skip the INCT, but the JLT becomes 10 cycles.

 

So, the final cycle counts end up being:

  • R0 > 0: 48 + 10*mem
  • R0 = 0: 22 + 3*mem  (ABS is 12+2*mem, JEQ is 10+1*mem)
  • R0 < 0: 42 + 8*mem (ABS is 14+3*mem, JLT becomes 10+1*mem)

To put it in perspective, a single SRA R0, 15 is 42 + 3*mem.  In zero wait-state memory, this is only slightly slower than that shift in the worst case.  Nice!

Edited by intvnut
Clarify we never actually have 11*mem, because JLT taken when R0<0.
  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, intvnut said:

Answering my own question by looking at the code in MAME.

Yes, that code manifests my findings as shown above in the test value list. The list was output from a program that ran on my real Geneve.

Share this post


Link to post
Share on other sites
36 minutes ago, mizapf said:

Ah yes, SETO does not affect the status bits. Good idea.

 

As for ABS, the Ed/Ass manual says the instruction does not affect the Carry bit, yet the TMS9900/9995 specs say it does. But it will always be reset. Here is a test I ran on my Geneve.

 

 

*        
*           LAECOPX
*   FFFE    10000***
*   FFFF    10000***
*   0000    00100***
*   0001    11000***
*   0002    11000***
*   7FFE    11000***
*   7FFF    11000***
*   8000    10001***
*   8001    10000***

I'm mildly confused here.  Neither the "JLT" or "JEQ" depend on the carry flag to operate, so it does not effect the assembly code presented here.  Only the "JOC" and "JNC" test the carry flag to make a decision.  The examples you've presented here seem to indicate that the TMS900/9995 docs are correct.  Assuming that the carry flag was set before executing the ABS, in each case, that seems to be the case.  Is that what this means?

 

Share this post


Link to post
Share on other sites

Yes, my test was done in two passes; first setting the status register to 0000, then again setting the status register to FFFF. The 9995 has a helpful command: LST. The asterisks show the bits that are not affected by the operation.

 

It is easy to see that C=0 for every value presented to ABS:

 

- If the value is nonnegative, it remains unchanged.

- If the value is negative, it is replaced by its two's complement.

 

NEG on 0 implies C=1, because the ALU obviously calculates the one's complement first, then increments it by 1. This increment may cause a carry. As you see, it only occurs for NEG 0. But for 0, ABS does nothing.

  • Thanks 1

Share this post


Link to post
Share on other sites
1 hour ago, mizapf said:

As for ABS, the Ed/Ass manual says the instruction does not affect the Carry bit, yet the TMS9900/9995 specs say it does.

In the datasheet, I see the ABS instruction listed in the ST3 (carry) row of the status-flags table, where the condition is "if CARRY OUT = 1".  However, absolute value operation is performed on negative numbers *only* (there is a pre-test of the value, which is why values that are already positive are not written), and is calculated by inverting the the bits and adding 1 (this is the same operation as NEG).  As soon as you invert the negative number, the MSbit becomes zero, so it is not possible to have a carry out.  ABS is probably listed as affecting the carry bit because the instruction will cause it to change, i.e. be set to 0 if it was previously a 1.

Share this post


Link to post
Share on other sites
25 minutes ago, mizapf said:

Yes, my test was done in two passes; first setting the status register to 0000, then again setting the status register to FFFF. The 9995 has a helpful command: LST. The asterisks show the bits that are not affected by the operation.

 

It is easy to see that C=0 for every value presented to ABS:

 

- If the value is nonnegative, it remains unchanged.

- If the value is negative, it is replaced by its two's complement.

 

NEG on 0 implies C=1, because the ALU obviously calculates the one's complement first, then increments it by 1. This increment may cause a carry. As you see, it only occurs for NEG 0. But for 0, ABS does nothing.

 

The unsigned vs. signed greater than flags (L> and A>) on TMS9900 family always throw me off when I return for a visit. 

 

On most other CPUs I've encountered, there isn't a separate unsigned greater-than flag.  Rather, the carry bit serves that purpose.  If you treat a 2's complement subtract as merely "invert and inject a carry," then the carry will be clear after A - B when A < B (unsigned), and set otherwise.  That gives you "unsigned less than" and "unsigned greater than/equal" just looking at the carry bit.  Add in the EQ bit and you'd have "unsigned less than/equal" and "unsigned greater than."  You can get by with four flag bits rather than five.

 

In general, the TMS9900 family is weird about their carry bit compared to other architectures I've worked with.  You can set it or clear it, but it's difficult to consume it without a branch.  You can't use it directly for extended precision adds, subtracts, or shifts.  You gotta branch, or do a funky dance with STST.

 

It makes sense that ABS always zeros it out given how it's implemented.  Without the L> bit, I could see using the carry flag to indicate whether ABS had modified the number.

 

31 minutes ago, HOME AUTOMATION said:

There can only be one "ZERO".:rolling::rolling::rolling::rolling::rolling::rolling::rolling::rolling:

Not in floating point or 1's complement!  ;););)   I'm pretty sure that includes the 99/4A's weird Radix-100 floating point.  :D 

  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)

My Linear Algebra professor in my first semester once said, "the zeros cannot hide".

 

(From the math point of view, it meant that a zero vector makes every family of vectors linearly dependent. Of course, there was another subtle meaning concerning the audience...)

Edited by mizapf
Oh man, could you possibly agree on null or zero in English?
  • Like 1
  • Haha 1

Share this post


Link to post
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.

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...