2

I'm trying to implement logical NOT and logical AND in assembly, I did logical NOT already using x < 1 but I can't think of how to implement AND, I can use binary and but that's broken for negative numbers (it assumes -1 is true) and it doesn't make any sense when NOT obviously works because -1 < 1 would return 1

So I'm confused, how could I do it, any known implementation which I could use? I can't find it, been looking for a while

Ari157
  • 95
  • 4
  • 16

3 Answers3

3

The standard solution is to implement a && b as

if (a)
    return (b);
else
    return (0);

i.e. use a conditional jump. On sufficiently new x86, you can also use a cmov instruction like this:

; assumes input in eax and ebx
mov ecx, eax    ; ecx = A
test ebx, ebx   ; B?
cmovnz ecx, ebx ; ecx = A ? B : A
fuz
  • 88,405
  • 25
  • 200
  • 352
  • This is even better of a solution, thank you! – Ari157 Jul 13 '22 at 04:59
  • 2
    ... and if you want to branch on the `a && b` result, `test ecx,ecx` / `jz` or `jnz`. Or booleanize it to a 0/1 integer with `test ecx,ecx` / `setnz cl`. Another answer suggests a more complicated way to get a 0/1, but this is more efficient. x86-64 guarantees availability of cmovcc and setcc. – Peter Cordes Jul 13 '22 at 05:27
3

This is an answer without jumps: Assume you want to compute logical AND of RCX and RDX and store it in RAX and you may use RBX (note that called functions usually must preserve RBX!).

xorl %eax, %eax   # RAX = 0
xorl %ebx, %ebx   # RBX = 0
cmpq %rcx, %rbx   # CF = (RCX != 0)
adcb %bl, %al     # RAX = (RCX != 0)
cmpq %rdx, %rbx   # CF = (RDX != 0)
adcb %bl, %al     # RAX = (RCX != 0) + (RDX != 0)
shrb $1, %al      # RAX = ((RCX != 0) + (RDX != 0)) >> 1

Now RAX == 1 if and only if RCX != 0 and RDX != 0; otherwise, RAX == 0

Kolodez
  • 553
  • 2
  • 9
  • x86-64 guarantees the availability of `cmovc` and `setc %al`. And `movzbl`. So `test %rcx, %rcx` / `setnz %al` could replace the first cmp/adc. Also, C `a && b` returns either `a` or `b`; you're implementing `(bool)(a && b)` to get a booleanized integer value instead of just a branch condition. That might be useful, but in many cases overkill. But if that is what you want, this isn't terrible. Probably a test/`setnz %bl` / `and %ebx, %eax` would be better, though. But wouldn't save any instructions if you want to avoid partial-register problems on all CPUs including old P6-family. – Peter Cordes Jul 13 '22 at 05:10
  • "The `&&` operator shall yield 1 if both of its operands compare unequal to 0; otherwise, it yields 0." (C99 standard, 6.5.13, 3) – Kolodez Jul 13 '22 at 05:19
  • 1
    Oops, my bad. Of course normally you don't need that, if you're actually just going to branch on it you can `test %reg,%reg` / `jnz`. I think the best way to get the same result would be to build off of fuz's answer: mov/test/cmov, then booleanize that result if you want. (e.g. with a possibly-non-zero value in RCX, `test %rcx,%rcx` / `setnz %al`). For 5 total instructions. Or 4 if you just want to get the `a && b` condition into FLAGS, with mov/test/cmov / `test`. – Peter Cordes Jul 13 '22 at 05:20
  • 1
    re: calling convention: right, RBX is not normally a good choice for a temporary; if you did need two, your first two function args would be in RDI and RSI (note the [linux] tag), and you can use RAX/RCX/RDX as temporaries. And any of R8..R11, which are also call-clobbered in both x86-64 SysV and Windows x64. – Peter Cordes Jul 13 '22 at 05:25
  • Also, BTW, [fasm] syntax is an Intel syntax fairly close to NASM. For register instructions, GAS `.intel_syntax noprefix` would also be compatible with it, but not the AT&T syntax you're using in this answer. Not a big deal for readers who know both and can easily translate to/from AT&T syntax. – Peter Cordes Jul 13 '22 at 05:29
  • I was thinking about this some more. If you want to play tricks with CF instead of using CMOV, another fun way is `xor eax,eax` / `sub rax, rcx` / `sbb rax, rax` (0 if RCX=0, else -1) / `and rax, rdx` (definitely 0 if RCX=0, otherwise = RDX, which might or might not be non-zero.) Hmm, so I've just re-invented cmp/CMOV less efficiently. The final instruction can be `test rax,rdx` if you want. (AMD CPUs treat `sbb same,same` as only dependent on CF, write-only on the integer reg. But other CPUs don't, which is one reason I picked the register I just xor-zeroed) – Peter Cordes Jul 13 '22 at 15:56
2

One way to do it is doing logical not twice on each input, and then doing a bitwise and (AND instruction).

pts
  • 80,836
  • 20
  • 110
  • 183
  • 1
    True, that works, but a lot code, I'll wait a bit to see if I can get a better answer, if I can't find anything better or get any better answer I will use this and mark this as the solution :) – Ari157 Jul 13 '22 at 04:23
  • I went for this, thank you – Ari157 Jul 13 '22 at 04:49