I have the following code
tbl dd yes,no,maybe,Error   ; these are addresses of labels
mov ebx,tbl
xor eax,eax
mov eax,4
AND EAX,3
baba:
shl eax,2
jmp

I'm trying to set all the bits in eax to 0 except the first 3. Sort of error checking, all values have to result in a valid jump into the table.
your help is most appriciated.
Posted on 2013-04-22 18:56:31 by mich
Try this

and eax, 3  ;lowest 2 bits set => 2^2 = 4 (yes,no,maybe,Error)
jmp


Biterider
Posted on 2013-04-23 06:16:19 by Biterider
Posted on 2013-04-23 18:59:49 by mich
I doubt if "and" is what you want... but maybe I don't understand what you want.

So the question is whatever number is in eax needs to be reduce to be a value from 1-4. All values > 4 need a jump to error and also all values < 0.


Do you want a value between 1 and 4 or between 0 and 3? Is <0 supposed to be an error, or <=0? Is 5 supposed to be an error, or cycle back to no?

ASSuming that you want 0 - yes, 1 - no, 2 - maybe, 3 or more - error, less than 0 - error... you probably want "ja", not "jg".


mov ebx, 3
cmp eax, ebx
cmova eax, ebx
jmp

I'm not in love with that, but it may do what you want... or what I think you should want... :)

Best,
Frank

Posted on 2013-04-24 00:27:47 by fbkotler
Hi Frank, thanks for your reply.
Yes you are right. In this particular example, there are 4 valid jump addresses in the table. To avoid disasters, each evaluation has to resolve in one of them. So 0 for yes, 1 for no, 2 for all others and anything else HAS to evaluate to 3 ERROR.
mov ebx, 3
cmp eax, ebx
cmova eax, ebx
jmp
In the context of an FSA this is not quite the solution I have been looking for. The program listed above does not use a single logic comparison, so the cmp eax, ebx is a bit of a damper on my ego but hey it works and works very well. Technically the evaluation is not needed as long as the caller assures the correct jump.
By the way, the program above is modeled after my life, no comp's if's and butts, all jumps!
Regards,
Klod
Posted on 2013-04-25 19:05:08 by mich
Hi
In the System.inc file of the OA32 proj. there is a macro called JumpOn what does what you want and automated the task
; 覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
; Macro:      JumpOn
; Purpose:    Create a jump table and executes a jump to a label according the content of a
;            register.
; Arguments:  Arg1: Case register.
;            Arg2: Jump labels.
; Returns:    Nothing.
; Example:    JumpOn eax, @@10, @@20, @@30

JumpOn macro RegName:req, Labels:vararg
    local JumpTable

    .const
    ??Count = 0
    for ??Arg, <Labels>
      ife ??Count
        JumpTable  dd  offset ??Arg
      else
        dd  offset  ??Arg
      endif
      ??Count = ??Count + 1
    endm

    .code
    cmp RegName, ??Count
    jae @F
    jmp
@@:
endm


Only if you are in range it will jump, otherwise it continues with the regular execution.

Regards, Biterider
Posted on 2013-04-26 01:57:43 by Biterider

In the context of an FSA this is not quite the solution I have been looking for. The program listed above does not use a single logic comparison, so the cmp eax, ebx is a bit of a damper on my ego but hey it works and works very well.


What do you mean by that exactly?
A compare is basically just a subtraction where the result is discarded. On modern CPUs it is a single-cycle operation just like and, for example, and no stalls.
It is only the conditional jumps that may come after a compare (or any other instruction that updates the condition flags) that may cause a stall and hamper performance. A cmovcc does not (the problem with conditional jumps is that they may or may not modify the instruction pointer, which may invalidate the pipeline).
Posted on 2013-04-26 02:21:24 by Scali
Hi  Biterider
Thanks for your reply. I haven't even thought that far yet. I had an idea and I had a go at it just to see what it looks like in source code and  disassembled. What I got is enough to add another round of experiments to find out if this a worthwhile subject.
Hi Scali,
Thanks for your insight. The damper on my ego was meant as a diminutive comment about my own abilities to find a solution.  ;)I had interpreted FSA  as a piece of code that does avoid compare's all together and uses calculations and tables to implement the desired logic.
It is only the conditional jumps that may come after a compare (or any other instruction that updates the condition flags) that may cause a stall and hamper performance. A cmovcc does not (the problem with conditional jumps is that they may or may not modify the instruction pointer, which may invalidate the pipeline).

I had replied a little bit to hastily. I had missed to note in the debugger that in fact there was no conditional jump.

Thanks for all your contributions
Klod
Posted on 2013-04-26 16:49:24 by mich
Well if you don't want any 'cmp', here's a variation:

sub eax, 3
sbb edx, edx
and eax, edx
add eax, 3
jmp


How it works is like this:
sub eax, 3 will result in a value < 0 only if eax < 3. In other words, we set carry if eax < 3.
sbb edx, edx will transfer -carry to edx. So if carry then edx = -1 else edx = 0.

Then we and eax with edx, so that if eax was < 3, its value is preserved (and with -1 does not do anything), else eax = 0 (and with 0 will reset all bits).

Now we add 3 back to eax. The result is: if eax was < 3, then its original value is restored (counter-act the sub 3 we did earlier), else eax was 0, so now we get 3, which is our 'Error' value.

The life of real programmers before Pentium Pro :)
Posted on 2013-04-27 03:52:02 by Scali
In fact, the add isn't really necessary, since you can just add an offset to the table as well:

sub eax, 3
sbb edx, edx
and eax, edx
jmp


So you ask yourself, why do you really need cmovcc anyway? This solution goes in 4 instructions, just like the cmov one.
Posted on 2013-04-27 07:59:20 by Scali
Nice! I figured cmovcc would be better than the conditional jump (which may not be true), but only because I couldn't think of a "sbb trick" that would do it. I like this. Of course the jump table is not well predicted anyway, so it may be "racing stripes on a Yugo". :)

Best,
Frank

Posted on 2013-04-27 10:50:14 by fbkotler
Oh well, it's just this thing I have... The Pentium was the last great CPU as far as assembly programming goes.
The Pentium Pro took a lot of the 'magic' away from assembly programming. Partly because it did the instruction scheduling for you, and the old rule of "fecal matter in == fecal matter out" no longer applied as much (you no longer really had to count cycles and try to make use of the U and V pipelines manually, by carefully crafting your routines). Partly because from then on, clockspeeds reached insane heights (and later the number of cores/threads), and the execution speed of code just wasn't an issue anymore. And partly because the Pentium Pro's new execution engine meant that a lot of clever optimizing tricks no longer worked very well, while a lot of 'dumb' code that even a compiler could come up with, would be quite optimal.
The cmovcc instruction symbolizes this to a certain extent. In many cases, you can come up with some nice solutions that work equally well, using just regular 386-instructions, of which I have given one example. But the challenge has always been to find such solutions. That was part of the charm of programming assembly.

So to me it's the end of an era. If you also check out some of the stuff I've written elsewhere on C64/6502 programming... You'll see all these clever tricks that people have come up with over the years. The true art of assembly. But on modern x86 CPUs, most of these tricks don't apply.
Posted on 2013-04-28 07:14:37 by Scali
Hi Scali
Thank you very much for sharing this piece of code. Needless to say it went right away in my collection of  ASMGEMS.
But the challenge has always been to find such solutions. That was part of the charm of programming assembly.

It's this that separates the artist from the professional.
elegance!
Regards,
Klod
Posted on 2013-04-29 15:02:16 by mich