Jump to content
Willsy

Dumb ass assembly question

Recommended Posts

Ok, here we go...

 

+1 and +2

 

Which is greater? +2, obviously.

 

Ok then:

 

-1 and -2

 

Which is greater? There is no agreement here in the office! Some say -1 and some say -2. I'm leaning towards -1 being greater, but only when you imagine a conventional number line. If you don't care about 0, then you could say -2 is greater!

 

To put this into a programming context...

 

If I am counting backwards, say from -1 to -10 in a loop, which JMP instruction do I use?

 

(I don't have access to my beloved Classic99 today, otherwise I would knock up some assembly to test it. But, essentially:)

 

     CLR R0      ; INITIAL VALUE
     LI R1,-1    ; INCREMENT VALUE
     LI R2,-50   ; END POINT
AGAIN < DO SOME STUFF HERE >
     A R1,R0     ; GO BACKWARDS
     C R0,R2     ; COMPARE TO -50
     JGT AGAIN   ; LOOP IF NOT GREATER

 

So, I want the code to exit when R0 (in this case) =-51

 

You are probably thinking "Why not just test for equality then you don't care about positive or negative numbers..." - true, but only true when the incrementor is 1 or -1. If it's 3 or -9 etc then you won't ever get equality, so you are reduced to looking for boundary crosses.

 

This is connected with a nasty, evil bitch of a bug in +LOOP in TurboForth which I now have to correct, after major long-term procrastination. I have to fix it now, because I am just about to document the functionality!

 

So, to summarise, where 'n' is positive, we can test for boundary crossing with JGT to exit a loop:

    LI R0,0  ; INITIAL VALUE
    LI R1,10 ; BOUNDARY
    LI R2,3  ; 'N'
LOOP A R2,R0  ; ADD N TO VALUE
    C R0,R1  ; CROSSED THE BOUNDARY?
    JGT EXIT ; EXIT IF YES
    JMP LOOP ; OTHERWISE REPEAT
EXIT ....

 

So where N=3 as above, we would get 0 3 6 9 12 and out of the loop.

 

But what about when we are negative? Do we use JGT or JLT? :?

Share this post


Link to post
Share on other sites

    LI R0,0  ; INITIAL VALUE
    LI R1,10 ; BOUNDARY
    LI R2,3  ; 'N'
LOOP A R2,R0  ; ADD N TO VALUE
    C R0,R1  ; CROSSED THE BOUNDARY?
    JGT EXIT ; EXIT IF YES
    JMP LOOP ; OTHERWISE REPEAT
EXIT ....

But what about when we are negative? Do we use JGT or JLT? :?

Looks like everyday conversation with teenager. Advice ... Try and stay positive ... :party:

Share this post


Link to post
Share on other sites

If you use signed numbers then -1 is > -2, for unsigned numbers -1 is < -2.

 

So it depends on the definition of the branch instruction, what is bigger, greater, above etc.

Share this post


Link to post
Share on other sites

Thomas nailed it. On the TI there are jump instructions that are very specifically used to branch based on the UNSIGNED or SIGNED results of a comparison. That's why the CPU has two flags in the status register called "Logical Greater Than" (ST0) and "Arithmetic Greater Than" (ST1).

 

The jump instructions that use ST0 are looking at the data as UNSIGNED values, i.e. they will never be considered negative:

 

JH - Jump high

JHE - Jump high or equal

JL - Jump low

JLE - Jump low or equal

 

The ONLY two that consider the numbers as SIGNED are:

 

JGT - Jump greater than

JLT - Jump less than

 

The naming of the instructions emphasizes the Logical vs. Arithmetic. The logical instructions use the terms "high" and "low". The arithmetic instructions use "greater" and "less". So, which one you use depends on if you are treating your numbers as SIGNED or UNSIGNED. Because CPUs store numbers in two's complement form, the bits that indicate any signed negative value, when looked at as unsigned value, will always be greater than the values that represent the signed positive numbers.

 

Anyway, two's complement is one of those things you just have to read about and study until you "get it". At that point the answer will be clear.

 

Matthew

Share this post


Link to post
Share on other sites

Yea, only the arithmetic jumps should be used if negative values are a factor. Otherwise, unsigned values invert the number line; -1 is highest positive value, so it messes comparisons up.

 

If I may ask, what exactly are you doing that requires negative values? In my own experience, there isn't a lot that mandates their use that can't be reworked into a base address + positive offset, which is generally easier to work with.

 

Adamantyr

Share this post


Link to post
Share on other sites

Yea, only the arithmetic jumps should be used if negative values are a factor. Otherwise, unsigned values invert the number line; -1 is highest positive value, so it messes comparisons up.

 

If I may ask, what exactly are you doing that requires negative values? In my own experience, there isn't a lot that mandates their use that can't be reworked into a base address + positive offset, which is generally easier to work with.

 

Adamantyr

 

Thanks for the replies everyone.

 

To answer your question, Adamantyr, Forth has a word called +LOOP which is used in conjunction with DO. First the traditional DO with LOOP:

 

: TEST 10 0 DO I . LOOP ;

ok

TEST

0 1 2 3 4 5 6 7 8 9 ok

 

Each time LOOP is encountered, I (which starts at 0 in this example, and ends on the 10th loop) is incremented by 1

 

However, +LOOP has a variable incrementor, supplied via the stack:

 

: TEST 10 0 DO I . 2 +LOOP ;

ok

TEST

0 2 4 6 8 ok

 

So, because you can go in bigger steps than 1, you can no longer test for the index being equal to the maximum loop count, as you can with LOOP - you have to detect crossing a boundary. Furthermore, you could supply a negative incrementer:

 

: TEST -10 -20 DO I . -2 +LOOP ;

TEST

-10 -12 -14 -16 etc

 

So, if you are going in a positive direction, you want JGT however, if you are going in a negative direction, you want the opposite, as far as I can see. It just means that the code has to look and see if we are adding a positive or negative value to the index and run one of two code paths accordingly. No big deal, but I didn't have access a machine at the time :-)

 

Markk

Share this post


Link to post
Share on other sites

Neat! I wrote the following code today at work, but I couldn't test it. I'm using a different laptop at the moment (an 11.9" NetBook - nice) and I only just got my TI dev environment working due to a missing Microsoft library, so I couldn't do a TurboForth build.

 

Anyway, just tried the code that I wrote today:

;[ +LOOP & (+LOOP)
; note: +LOOP is immediate and compiles a reference to (+LOOP)
plooh1	data looph,>8005
text '+LOOP '
ploop1	data docol,lit,docnt,refdn,compile,ploop,exit

plooph	data plooh1,7
text '(+LOOP)'
ploop	data $+2
a *stack,@-6(rstack)    ; add value on stack to index
dect stack		; remove value from stack
mov @-4(rstack),r0	; get limit
jlt nll			; if <0 then do 'negative loop limit' leg
; else do 'positive loop limit' leg:
c @-6(rstack),@-4(rstack); compare index to limit
jgt ploopx		; if index > limit then exit
jmp dodo		; else loop again
nll	c @-6(rstack),@-4(rstack); compare index to limit
jlt ploopx		; if index < limit then exit
jmp dodo		; else loop again
ploopx	ai rstack,-8		; remove DO/LOOP frame from return stack
b *next			; NEXT

 

Gives the following results, which looks correct to me :)

: TEST 10 0 DO I . 1 +LOOP ; 
TEST 0 1 2 3 4 5 6 7 8 9 10 (11 iterations) 

: TEST 10 0 DO I . 3 +LOOP ; 
TEST 0 3 6 9 (4 iterations) 

: TEST -30 -7 DO I . -3 +LOOP ; 
TEST -7 -10 -13 -16 -19 -22 -25 -28 (8 iterations)

Share this post


Link to post
Share on other sites

Nice. Then you've gotta try something like this too ...

 

: TEST -7 -30 DO I . 3 +LOOP ;

: TEST 30 7 DO I . -3 +LOOP ;

: TEST 30 -7 DO I . -3 +LOOP ;

: TEST -7 30 DO I . -3 +LOOP ;

: TEST 30 -7 DO I . 0 +LOOP ;

;)

Share this post


Link to post
Share on other sites

Nice. Then you've gotta try something like this too ...

 

: TEST -7 -30 DO I . 3 +LOOP ;   <--- Produces -30 (1 iteration) This is wrong.
                                Should produce -30 -27 -24 -21 -18 -15 -12 -9. Shit.

: TEST 30 7 DO I . -3 +LOOP ;    <--- Big humungous loop (obviously!)

: TEST 30 -7 DO I . -3 +LOOP ;   <--- Big humungous loop

: TEST -7 30 DO I . -3 +LOOP ;   <--- 30 27 24 31 18 15 12 9 6 3 0 -3 -6

: TEST 30 -7 DO I . 0 +LOOP ;    <--- nooooooooooo!

Edited by Willsy

Share this post


Link to post
Share on other sites

Fixed it, and come up with what I think is some nice code ;)

li r0,jgti		  ; point to "JGT PLOOPX" instruction
mov *stack,r1       	  ; check incrementor
jgt ploops		  ; if positive, skip
inct r0			  ; else point to "JLT PLOOPX"
ploops	a *stack,@-6(rstack)	  ; add value on stack to index
dect stack		  ; remove incrementor from stack
c @-6(rstack),@-4(rstack) ; compare index to limit
x *r0			  ; jump if boundary has been crossed
mov @-2(rstack),pc	  ; else reload PC (computed by DO)
b *next			  ; and do another loop
ploopx	ai rstack,-8		  ; remove DO/LOOP frame from return stack
b *next			  ; NEXT
jgti	jgt $+10		  ; equivalent to "jgt ploopx"
jlti	jlt $+10		  ; equivalent to "jlt ploopx"

 

Note the crafty use of the X instruction ;) - the first time I've ever used it I think (though it wasn't really necessary). I use X to either execute JGT or JLT depending on the sign of the incrementor (passed via the stack).

 

I checked the examples that you and I posted with GForth (ANS compatible) and TurboForth now behaves the same way! :cool:

Share this post


Link to post
Share on other sites

Fixed it, and come up with what I think is some nice code ;)

li r0,jgti		  ; point to "JGT PLOOPX" instruction
mov *stack,r1       	  ; check incrementor
jgt ploops		  ; if positive, skip
inct r0			  ; else point to "JLT PLOOPX"
ploops	a *stack,@-6(rstack)	  ; add value on stack to index
dect stack		  ; remove incrementor from stack
c @-6(rstack),@-4(rstack) ; compare index to limit
x *r0			  ; jump if boundary has been crossed
mov @-2(rstack),pc	  ; else reload PC (computed by DO)
b *next			  ; and do another loop
ploopx	ai rstack,-8		  ; remove DO/LOOP frame from return stack
b *next			  ; NEXT
jgti	jgt $+10		  ; equivalent to "jgt ploopx"
jlti	jlt $+10		  ; equivalent to "jlt ploopx"

 

Note the crafty use of the X instruction ;) - the first time I've ever used it I think (though it wasn't really necessary). I use X to either execute JGT or JLT depending on the sign of the incrementor (passed via the stack).

 

I checked the examples that you and I posted with GForth (ANS compatible) and TurboForth now behaves the same way! :cool:

 

Off topic i know but are you planning to run turboforth against the forth test library to fully validate it?

Share this post


Link to post
Share on other sites

Off topic i know but are you planning to run turboforth against the forth test library to fully validate it?

Hi there,

 

No. It's largely-kind-of-F83-compatible - whatever that means!

 

However, strict compliance with Forth standards (ANS is a joke) was not at the top of the list - Fast code was!

 

Is there anything in particular you had in mind?

 

Mark

Share this post


Link to post
Share on other sites

: TEST 30 -7 DO I . 0 +LOOP ;    <--- nooooooooooo!

So I guess the code is accepted (no error), but the loop loops forever ?

 

Also I guess you can manipulate the "I" variable within the loop (if you want to) ?

 

Is it possible to "break" out of a running user program (like a long/forever loop) ?

 

:)

Share this post


Link to post
Share on other sites

"So I guess the code is accepted (no error), but the loop loops forever ?"

Yep - it's perfectly legal code. Though rather stupid!

 

"Also I guess you can manipulate the "I" variable within the loop (if you want to) ?"

No. I always represents the current Index value of the current loop that you are in. I is not actually a variable. It's a word (like a function in C) - It returns (via the stack) the current index of the loop you are in.

 

Loops of course can be nested:

: TEST
 10 0 DO 
    I . 45 EMIT SPACE
    5 0 DO
      I . 
    LOOP
    CR
 LOOP ; ok:0
TEST
0 - 0 1 2 3 4
1 - 0 1 2 3 4
2 - 0 1 2 3 4
3 - 0 1 2 3 4
4 - 0 1 2 3 4
5 - 0 1 2 3 4
6 - 0 1 2 3 4
7 - 0 1 2 3 4
8 - 0 1 2 3 4
9 - 0 1 2 3 4

 

"Is it possible to "break" out of a running user program (like a long/forever loop) ?"

Yes it is, but you have to test for it (by design). This is because scanning the keyboard all the time just in case someone pressed the break key is slow. And maybe you, as the author of the program don't want people to break the program...! So, I have included a word called BREAK? that you insert only where you want to (like in your main game loop ;), like this:

 

: TEST
 10 0 DO 
    I . 45 EMIT SPACE
    5 0 DO
      I . 
      BREAK?
    LOOP
    CR
 LOOP ; ok:0
TEST
0 - 0 1 2 3 4
1 - 0 1 2 3 4
2 - 0 1 2 3 4
3 - 0 1 2 3 4
4 - 0 1 2 [press function & 4]
Break

break-test.png

Break handles scanning for the F4 key, and takes no action if not detected. If detected, he handles un-winding the return stack for you - doesn't matter how deeply your program is nested when break is pressed - he will unwind the return stack and take you to the command line.

:D

Edited by Willsy

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