Hacker News new | past | comments | ask | show | jobs | submit login
No one should use the AT&T syntax (2021) (outerproduct.net)
116 points by Tomte 5 months ago | hide | past | favorite | 107 comments



I think there are two legitimate reasons to use Intel syntax:

1. It matches the manual. AMD and Intel’s manuals agree about operand order, and IMO there is no justification whatsoever for an assembly syntax to fail to match the documentation. This is especially true for 3-address instructions and for two-input instructions like CMP.

All these arguments about mathematical notation or linguistics are nonsense in my book. The correct order for the operands of ADD is what the corresponding manual says.

2. Intel address syntax is so much better than AT&T’s that it’s not even funny.


Don't forget the weirdness of AVX-512 masks in AT&T syntax!


I sincerely hope I never have to touch code like that.

I have messed with code to save and restore AVX-512 registers, and this is about the only thing I like about AT&T syntax: there’s a uniform way to specify the instruction size that works when the instruction has no operands. I’m looking at you, XSAVE, XRSTOR, SYSRET, etc. The manual has no useful syntax to offer here.


AT&T syntax for x86-64 introduces tons of nonstandard instruction names that you will never find in the official manuals. WTF are: movabs, cqto, filds, and ljmp?

I also don't understand why x86 syntax has to be mutilated when special syntax is supported for other architectures. It's somehow OK for ARM64 to have [x1, w9, sxtw #2] or [x3, #32]! and 68K to have (%a2,%d1.l8), but x86-64 can't have [%rcx + %rdx4].

Reading x86-64 disassembly in AT&T syntax is just painful.


I learned ibm 370 assembly first, PDP-11 assembly second, then I think Z80.

I found them to be very regular and understandable.

So when I encountered intel chips and their assembly syntax, I disliked them.

So about that time I switched to higher level languages.

Are there still chips left have a nice orthogonal assembly language? I vaguely recall 68k being orthogonal too.


ARM is pretty orthogonal, though arm64 is less so. RISC-V is very orthogonal, but it sucks to program for it in assembly as the instruction set is missing even basic features.


Sorry, but Z80 being "regular and understandable"?

    ADD  A,B     ;8 bit add, B to A
    ADD  HL,BC   ;16 bit add, BC to HL
    ADD  B,A     ;invalid! destination can only be A or HL

    ADC  A,B     ;add with carry
    SBC  A,B     ;subtract with carry
    SUB  B       ;subtract (A is implicit, same for AND,OR,XOR)

    LD   A,(HL)  ;load A from memory at HL
    LD   B,(HL)  ;load B from HL
    LD   A,(DE)  ;load A from DE
    LD   B,(DE)  ;invalid! must be reg=A or mem=(HL)

    LD   A,(var)  ;load A from variable
    LD   HL,(var) ;load HL from variable
    LD   DE,(var) ;extra opcode byte, 4 cycles slower
    LD   B,(var)  ;invalid! must be A or register pair

    JP   (HL)     ;load PC with contents of HL register, NOT MEMORY!

    EX   DE,HL   ;exchange DE with HL (no other registers allowed)
    EX   HL,(SP) ;exchange HL with top of stack (no other reg,mem allowed)
    EX   AF,AF'  ;wtf were they thinking?
    EXX          ;should be EX BCDEHL,BCDEHL' for consistency :)


JP (HL) seems to be a disambiguator vs. JP HL (jump to label HL) and the register swap instructions, while quirky, seem justifiable as well.

I would just add SUB A, B (etc.) and call it a day.

edit: looks like the Z800 was an improved/orthogonalized (and pipelined!) extension of the Z80 that maintained software compatibility. And it looks like the SUB A, B format is supported by eZ80 assemblers, though A is still the only valid 8-bit accumulator.


I remember the Z80 in relation to the 8080. I might have misremembered but I thought Z80 allowed some combinations that 8080 could not do. It could also be that the assembler/syntax itself was nicer.


> Putting the destination operand first emphasizes the site of mutation.

This doesn't make any more convincing an argument than the "linguistic" and "consistency" arguments he shot down earlier.

Why should putting the mutated argument first emphasize it any more than putting it second? If you order things the same way every time, people will implicitly know which one is the mutated argument because that's how the language is structured.

There really is no strong benefit to either ordering, other than that [src, dst] is closer to human language (which is a minor benefit at best and not worth fighting over).


I'd agree that there is no strong inherent benefit to either ordering, except that it would be good not to needlessly violate expectations about mathematical operations. AT&T also permutes the order of operands of comparisons, which is just bizarre: https://gcc.godbolt.org/z/h6EcP5Yv9

"Compare A and B and do X if result is 'less than'" should never mean "do X if B is less than A".

Anyway, the most important consideration for me is that standard documentation uses Intel operand order. Intel's manuals are hard enough to read, I wouldn't want to additionally permute operands in my head.


Certainly, I'm not defending AT&T in any way, shape, or form. I think it's ugly and confusing as hell.

My only point is that argument ordering is not something to worry about, and every argument I've heard one way or the other has been weak at best.


the comparison operand ordering problem is a legitimate problem that i keep having even after having written thousands of lines of assembly in at&t syntax. i hope it goes away after i get to tens of thousands...


The actual argument there is that most instructions with explicit, specified output registers have exactly one output register (which is mutated) where as most have multiple, non-mutated input registers. Of the instructions with multiple mutated output registers, most encode 1 or 0 (I am just going by memory here) output registers with the remaining being implicit.


Google C++ guide says to put mutable parameters last, but I've seen other style guides say the opposite.


If your mutable thing is mandatory, then I think it should go first. Otherwise, you will be in a strange position when there are default arguments. The mutable thing would then be sandwiched between immutables. Of course if your mutable parameter is optional, it must be after all the required parameters.

The argument above applies neatly to assembly instructions, which generally change one thing.


> [src, dst] is closer to human language

In English, but not Telugu.


Programming languages are pretty much always in English though, including these mnemonics.


I think because it's always in the same place and always first. That definitely emphasises it.

Personally I think assembly should just have a little more syntax. If we wrote

  dst = mv src
or

  dst <- add src
it would just be obvious.


People tried that (I forget the actual language now), and it just turns into a mess because now you have all these special cases (+, -, =, etc) and special syntaxes that don't transfer well to non-mathematical or multi-parameter instructions.

This really is where AT&T fell over: Their syntax evolution feels bolted on for the most part, and not well thought out. The result can be quite confusing in a lot of cases.


Kalray's MPPA does that, and it's really refreshing: https://gcc.godbolt.org/z/8Wch4MfMq


The operand order is because intel were flip flopping between load semantics and move semantics with the 4004, 8008, 8080, 8085, 8086 progression.

Remember that when the 8080 engineers left to found Zilog, they changed the MVI instruction to LD, as it had been intended.

(LD on 4004 and 8008, MOV on 8080, LD/ST and MOV on 8085, MOV on 8086)


8080 and 8085 are basically the same, and have both MOV, MVI and LD/ST. The advantage is that the mnemonics directly correspond to the operations supported by the hardware, once you learn them you know what registers can be used where.

Z80 is also highly unorthogonal (even more so than x86), but obscures this by using the same mnemonic with different operand combinations, not all of which are legal and some require prefix bytes.


While I hadn't considered the emphasis argument before, I think it actually makes a lot of sense. You're kind of front loading the mutation context in your consciousness.


> ...you should still clean your CPU at least annually: use soap, warm water, and a soft rag to get the gunk out of the transistors.

I know the author is just making a joke here, and it is funny. Somehow it reminded me of some actual advice I've seen more than once on /r/thinkpad. I am paraphrasing from memory, but I'm sure I have the gist of it:

> When you get a brand new ThinkPad in a factory sealed box, the first thing you must do is completely disassemble it and inspect all the parts. When you reassemble it, repaste the CPU with a quality paste like Arctic MX-4. Now you will have a ThinkPad built to your standards, not Lenovo's.


Reminds me of a little quip in a 68000 programming language book I read a long time ago.

Paraphrasing: "Upon executing HALT, the processor will stop, and nothing short of a reset will get it going again. Although threatening it with a hammer has been known to work on occasion, especially if it knows you've killed before."


Reading the Wikipedia page on Halt and Catch Fire it seems this issue actually caused a certain set of illegal opcodes to become an "official' HCF instruction (opcode was DD so it was also known as "Drop Dead") as it was also later used by the engineers for product testing or something.


And there will always be a customer that writes to support asking what size hammer should be used to test that parameter.


Why, a rowhammer, of course!


It's more and more true, though, that the physical condition of your CPU and heatsink affects performance. Because under heavy workloads they're often thermally limited, and every 0.01 K/W of thermal resistance makes a noticeable difference.


tangentially reminds me of the advice for waterproofing an outdoors electronics enclosure. use a waterproof enclosure, make sure the seal is properly placed, and intact, seal everything up and drill a hole in the bottom to let the water out.


I didn't really read the article, because/but I spent a minute or two playing with the cat.

Nice little animation.


It used to be known as Oneko or Xneko, and chase your Windows or X11 desktop cursor. I once used it on both.

Forcing it as a high-contrast distraction upon people who just want to read an article seems like it will discourage some of them.

Discouraged people who nevertheless want to read the article, while using their pointing device to scroll, could add this to their uBlock Origin "My filters" tab:

    ||outerproduct.net/oneko.js$important


Alternatively you can enable reduced motion in browser settings and its code won't run.

https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pref...


Yeah, I have a habit of tracing roughly where I'm reading with the mouse pointer. Which works exceedingly poorly when that means a cat will be running straight across my field of vision continuously while reading.


Cool, neko, originally created for X by Masayuki Koba. I have it installed on my Slackware System :)

To me, as a non-WEB developer, I think that would be hard to do on a WEB Page.

As for AT&T vs x86 assembly, it is a flame war I have not heard from in decades and a couple of arguments were hit upon in the article were part of that war. Nice hearing about an old classic war :)

I have not even looked at assembly in many decades, and back then I did the bare minimum with help from someone, so most of the article was over my head.


I love whimsical stuff like this. Software can be very clinical.


another implementation: https://dimden.dev/


Is this a multiplayer implementation?



Looks like a port of xneko which has been around since at least the early 1990's, I had it on my old Digital workstation


Fun little thing: If you really want to confuse an LLM, try to make it reason about x86 assembler, and watch it trip all over operand order (along with many other things).

I've had models assume different operand order in two different places in the same file.

x86 assembler really is the gift that keeps on giving. Sadly it doesn't given you anything nice.


Honestly they don't do much better for Z80 or 6502 assembly from what I've seen either.


Any unstructured language seems to be a problem, but x86 just maximises the pitfalls...


Years ago I emailed the creator of Exapunks (a puzzle game where you write assembly in a made up instruction set) and asked why he used AT&T syntax instead of Intel syntax. He said it seemed more intuitive for English speakers.

> This is usually only brought up by people who have prior assembly programming experience, which makes me think we made the right choice.


I haven't played Exapunks, so I can't say, but about AT&T vs Intel: maybe the order of "mov source, destination" and "sub subtrahend, minuend" is easier, since it lines up with English phrases like "move this into that" and "subtract this number from that number," but everything else about Intel syntax was easier for me when I first learned assembly.


I love AT&T syntax.

I originally started with Intel syntax when learning assembly since it was just in my textbook. I sometimes ran into weird syntax issues I didn't yet understand.

For example, why "mov eax, [ebx + 2 * ecx + 4]" is allowed, but not "mov eax, [ebx + ecx + edx]" (1)? Or maybe "mov eax, [ebx + 3 * ecx]" (2)? Or maybe "mov eax, [2 * ebx + 4 * ecx]" (3)?

As long as it is a math expression it should work right? Why did the compiler keeps telling me the expression is invalid?

Later when I learned about AT&T syntax, everything started to make sense. The syntax ensures you cannot construct (1) and (3), and when trying to use (2) it explicitly tells you it expects 1, 2, 4, or 8 but got 3.

I started to use AT&T syntax since then.

In my opinion, there is one most confusing part about AT&T syntax, which is the condition based instructions. I guess the original author of the article did not do much programming with AT&T syntax so they did not notice.

  cmpl %eax, %ebx
  jae label
Now tell me if eax is bigger, should you jump to label?

Instead, with Intel syntax it is pretty straight forward. The "jae ..." following "cmp ebx, eax" translates to "if (ebx >= eax) goto ..."

(BTW, the cat or whatever following your mouse pointer is super annoying)


>For example, why "mov eax, [ebx + 2 * ecx + 4]" is allowed, but not "mov eax, [ebx + ecx + edx]"

Because it's translated into a single machine instruction!!! "mov eax,[[ebx]]" doesn't work either - not on x86 at least, historically there were architectures that had indirect memory references :)

It's still MUCH easier to read and write. How do you even remember the order in AT&T syntax? Shouldn't the scale factor maybe be given as a shift count since that's how the hardware works? By using familiar arithmetic expressions, it becomes perfectly clear what is meant, and anyone who actually writes assembly will quickly learn what forms are allowed. And if you are only reading the code, it doesn't matter.

Of course, AT&T syntax comes from UNIX, where the zeroth commandment is "Thou shalt have no other programming languages but C and shell scripts". People who have invested the effort to memorize something like 42 different levels of operator precedence, and fluently read and write symbol vomit like "(((void *)(int [])fn(foo,bar=baz==quux++))" are obviously desperate to make assembly look EVEN MORE complicated...


> Because it's translated into a single machine instruction!!!

Actually we never learned the x86 binary representation of instructions in that assembly language class. The textbook also did not cover that.

All I wanted to say is: Intel syntax hides the fact that there are only 4 things in address calculation: displacement, base, index, scale. The compilation error is also hard to understand (at least for the compiler I used). It says something like "the expression is invalid" but you never know what went wrong.

AT&T syntax exposed the underlying requirement, and the compilation error is easy to understand.

Now I am okay with both AT&T and Intel, but when I was learning, I appreciated AT&T syntax more. Assembly is mandatory for CS major in that college, and AT&T syntax made my semester easier.


It's easy enough to learn "base + index*scale +- displacement". And it can be written in exactly that way, in whatever order makes the meaning of the code clearest, as you would do it in a higher level language.

AT&T syntax forces you to learn this before writing or even reading a single line of code that references memory, instead of giving the illusion that maybe something like [eax+ebx+ecx] or [[eax]] was also allowed. I don't think that's very helpful. It also forces you to learn a very specific way of writing it that is completely unintuitive.

I'm somewhat sympathetic to the argument that assembly syntax should correspond to the underlying hardware, just not to such an extreme. For example, I prefer 8080 over Z80 for that reason (one mnemonic for each addressing mode).


> It's easy enough to learn "base + index*scale +- displacement".

Well, you already know this requirement, so it is not a problem for you. However for learners, who don't have even the remotest idea of machine instructions, all they get is some invalid expression error messages. Same message for all (1) (2) and (3) cases I listed above.

Maybe Intel syntax works better for experienced programmers. But at least for me, when I was a newbie to assembly, AT&T was better than Intel syntax. It made learning process easier.

> AT&T syntax forces you to learn this before writing or even reading a single line of code that references memory

I do believe that rules forced by language is a good thing. At least it helped me understand why (1) (2) and (3) didn't work

Many people believe Rust can help people write safer code. Why? Because of its rules. Incorrect ownership will be discovered by the borrow checker so a compilation error will force the programmer to correct it.

Maybe an experienced C++ programmer will be able to handle memory management correctly in C++, and loves the extra freedom that C++ brings. But Rust can be helpful for learners. It provides clear error message helping them understand why their code is wrong. While in C++? Segmentation fault.

(Needless to say, Rust also helps experienced programmers. Even experienced programmers write buggy C++ code.)


In almost every respect I have a slight preference for the intel way (nasm dialect), but could live with the at&t one. But it's just ridiculous how

    mov eax, [edi + 8*ebx + 3]
is written as

    mov 3(%edi,%ebx,8), %eax


> Suffixesq arew annoyingw

No, DWORD DWORD DWORD is obnoxious.


As someone who has both written and read a lot of assembly in a professional context, my colleagues who are assembly writers very frequently prefer Intel syntax. The ones who are mostly (exclusively) readers seem to generally prefer AT&T. It is pretty undisputed that AT&T is easier for computers to parse, but it's also generally easier for humans to parse when reading it.

* The suffixes give you a lot of easy specificity about what the operands are, while they are unnecessary if you already know what the operands are.

* DWORD PTR [x + A + B * C] is wordy, but at least the math is clear, and you can write the arithmetic in any order you want (as well as using 3, 5, and 9 as multipliers). X(A, B, C) is more concise and has no operand order to think about, and not terribly hard to read once you learn.

* AT&T syntax from a compiler always uses the easy cases and never makes mistakes about placement of the "glyphs" ($, %, etc.). There is none of the example's "movq ($28), %rax" because having a symbol called "$28" is fundamentally dumb.

* Operand order in AT&T is really dumb, especially for 3-operand instructions, but if you're just reading, you really don't need to know operand order to understand what's going on.

For these reasons, I assume the OP was written by someone who I assume writes a lot of assembly by comparison.


> As someone who has both written and read a lot of assembly in a professional context, my colleagues who are assembly writers very frequently prefer Intel syntax. The ones who are mostly (exclusively) readers seem to generally prefer AT&T. It is pretty undisputed that AT&T is easier for computers to parse, but it's also generally easier for humans to parse when reading it.

Wow, that surprises me. Without knowing anything, I would think it were the other way around, because in Intel syntax, the address calculation is more explicit than the comma-and-parentheses form; the registers don't require a % sign every time, making it distracting to read the code; and the opcodes are easier to read out loud because they lack the suffixes.


I suspect it's just a different culture. People coming from UNIX and C learn from tutorials that use AT&T syntax, and probably would never think of writing code in assembly to begin with.


> Operand order in AT&T is really dumb

I always found AT&T order much more intuitive to read. The "what am I doing" and then "where will it go" felt natural to me.

But I didn't do much assembler, so my opinion doesn't matter :)


If you write for any CPU other than an x86, the Intel assembler syntax matches the syntax order you would get. It also sort of lines up with C and C++ syntax of putting the result on the left.

I might be biased as an Intel-syntax-loving assembly-writing heathen.


> It also sort of lines up with C and C++ syntax of putting the result on the left.

The article also discussed this argument, and I find it weird, because higher level languages also put the operation on the right side.

Maybe I just prefer having the operation and the target close together? Having `abs(x) = y` put the absolute value of y into x, would be extremly weird to me in C++.

> I might be biased as an Intel-syntax-loving assembly-writing heathen.

I guess the world is on your side, and I just avoid assembly where possible :D (although, not really because of the syntax part, I can live with either syntax decision in the end after getting used to it)


Careful, in C++ `abs(x) = y` is equally likely to put y into abs(x).

Jokes about C++ aside, many DSPs with their own assembly languages have chosen to learn from C and higher level languages. The following style of syntax is not uncommon for DSPs (I am not using a specific one, but an abstract style), where writing assembly is expected:

R1 = R2 + R3;

R5 = R1 * R3;

R4 = POPCOUNT(R1);

R3 = [R0] <- loads data at pointer in R0 into R3

Curiously, RISC-V didn't do this despite having the option to write their own new assembler. I guess the old opcodes die hard, but I also assume it has to do with LLVM strongly suggesting [OPCODE] [ARGS] syntax for assembly.


> Careful, in C++ `abs(x) = y` is equally likely to put y into abs(x).

I know, if it returned something it could write into, it would. .__. https://godbolt.org/z/7h6YMqbEW

> Jokes about C++ aside, many DSPs with their own assembly languages have chosen to learn from C and higher level languages. The following style of syntax is not uncommon for DSPs (I am not using a specific one, but an abstract style), where writing assembly is expected:

> R1 = R2 + R3 R5 = R1 * R3 R4 = POPCOUNT(R1) R3 = [R0] <- loads data at pointer in R0 into R3

That's really cool to know, my exposure to assembly is mostly limited to a little bit disassembly for all kinds of reasons (so mostly x86 reading) or 1 instance of inline assembly. Most of the other instances I worked with SSE intrinsics instead, so my knowledge is very limited.

This thread was extremly interesting to read.


> If you write for any CPU other than an x86

The at&t syntax didn't appear in a vacuum. There are several architectures, e.g. popularly m68k, which use this order.


All of them but x86 are obsolete. I don't know anyone who has written 68000 assembly for professional use in the last 20 years.


I first learned assembler on Z80, and so the Intel syntax should be more natural to me, but I migrated from that to 68000 which uses destination-last syntax, and never really had any problems with it even though I'd been using Z80 for a few years by then. About a year after learning 68000, I started coding 8086 assembler and felt that Intel syntax was backwards, even though it's the same order as the Z80 I started on. Now it's been almost 2 decades since I wrote 68000 code, and I've drifted back to Intel syntax as the default, although I've always had a particular hatred for the square brackets for memory accesses. I remember being particularly disgusted by the DWORD PTR nonsense that the original MASM insisted on, and back in the day preferred a86 (no, not the Linux one, the old shareware DOS assembler from the late 80s) which had a much more sane syntax.

AT&T syntax also pre-dated Intel syntax by several years, and was based on the PDP-11 assembler which had the destination last. Many UNIX vendors later moved to 68000 which also had the destination last (possibly influenced by AT&T syntax, as all their 8-bit processors embedded the destination in the opcode). Other chips like SPARC, which was developed by a UNIX vendor for a UNIX-like machines followed suit, probably because all their engineers were already using AT&T syntax anyway.

As for the other arguments in the article, I think it's kind of fair to complain about the syntax, but also important to remember that as part of the GCC toolchain, the assembler was never really intended for humans to use, other than for short bits of glue code, but was instead really intended to just have enough functionality to assemble the output from cc1. When GCC was ported to Intel, it almost certainly made sense to keep AT&T syntax in gas because gcc developers were familiar with it, and the cc1 backend would probably have required extensive changes to output in a different order even though it wouldn't really benefit anyone because nobody was actually intended to read the intermediate .S file other than while debugging gcc itself. Obviously nowadays, cc1 directly outputs to .o and the -S flag is more for debug, and sometimes generates code that doesn't always exactly correspond to the generated code in the .o.

But anyway, despite all that defence for why AT&T syntax, I agree, it's ugly and I always hate writing code with it. I almost always use an assembler that uses the chip vendor's preferred syntax on whatever platform I'm using, mostly because it's then easier to refer to documentation.


If Intel allowed you to do "MOV RAX, [address]" (inferring the QWORD PTR part from the "RAX"), most peoples' primary aesthetic objection to the syntax would be gone.


Which assembler requires that? Let me guess, GAS in Intel mode, just to make it more painful because they don't want you to use it?

NASM syntax:

    mov   rax,[Foo]             ;load var into reg, QWORD is implicit
    add   dword [Bar],1234h     ;add constant to DWORD var
    movzx eax,byte [rsi]        ;zero extend BYTE to DWORD reg


NASM is good, but yes, GAS in Intel mode wants you to be very wordy. I believe MASM is the most annoying one for Intel syntax, though.


MASM was also excessively wordy in many other areas. The amount you had to write to introduce a new segment or access something from another segment was ridiculous. I guess they probably improved that in later releases after TASM showed you didn't need any of that nonsense (and just had .TEXT and .DATA instead).


How many of them have experience with other assembly languages?

For me, it was exposure to M68k that made Intel syntax seem absolutely awful. AT&T syntax is also awful, but at least the operand order is the way that I'm (still) more used to.


Many of the assembly writers have exposure across platforms, but often with modern assemblers whose syntax tracks Intel syntax more closely.

I don't know why 68000 is so popular here (maybe people on HN learned it in school?), but you're far more likely to be writing ARM assembly or these days RISC-V assembly than writing for an antiquated core like the 68000.

Believe it or not, outside the x86 world, Intel-style syntax has basically won.


Because assembler used to be common, and isn't anymore, and because M68k assembler was nice to write, and because the Amiga and Atari were big once.

I abandoned assembler when I switched to Linux and got my first PC because x86 assembler was so awful I couldn't bear working with it.

AT&T syntax made it slightly less atrocious through familiarity, but only slightly.


username checks out


Related:

AT&T syntax is broken (2021) - https://news.ycombinator.com/item?id=33652023 - Nov 2022 (44 comments)

Why no one should use the AT&T syntax ever - https://news.ycombinator.com/item?id=27089329 - May 2021 (8 comments)

Also:

AT&T Syntax versus Intel Syntax (2001) - https://news.ycombinator.com/item?id=33585154 - Nov 2022 (105 comments)

What was the original reason for the design of AT&T assembly syntax? (2017) - https://news.ycombinator.com/item?id=26127368 - Feb 2021 (58 comments)

Others?


Here is where Intel syntax breaks down:

> mov eax, ebx ; (1) load one dword from the EBX register and store it in the EAX register

Note how the comment says something entirely different than the assembler mnemonics ("load" instead of "move", and the source and target register in the comment are in different order than in the mnemonics).

My simple rule of thumb when I worked both on Z80 and 68k in the early 90s:

- LOAD dst WITH src

- MOVE src INTO dst

Intel syntax would make a lot more sense if it would use the Z80-style LD instead of MOV.

Not that I'm an AT&T syntax fan, but this one detail always bothered me. But I guess I should get used to it because ARM also got it "wrong" ;)


AT&T syntax: We don't care. We don't have to. We're the phone company.

https://www.youtube.com/watch?v=CHgUN_95UAw


>For example, consider the following number >030514 >...that number is understand to mean ‘add the contents of registers A1 and A4, and store the result in register A5’. The association between this numeric form and its meaning under the ISA is completely arbitrary

They are not as arbritary as first glance as they might appear, related functions often cluster together creating families of opcodes. How many instructions in the family, what circuits and hardware components they use, and other optimization factors influence the arrangement of instructions as well which can determine the binary number the opcode uses.


I much prefer AT&T to intel syntax. i concede that cmp has the operand order swapped but otherwise i have to think less about it. Of course it's a matter of taste, but to me at least AT&T just feels nicer.


I like it, too. If you understand how it came to be (https://stackoverflow.com/a/42250270/417501), it's quite sensible. Also saves me from writing DWORD PTR all over the place.


TASM Ideal mode was great.


Indeed, I once ported a nasm source into AT&T syntax, never again.


1.Once we learn Intel syntax, same knowledge can re-used in other ISA (RISC-V, ARM) This is not true for AT&T syntax, hence one more no for the use of AT&T.


The best argument for Intel syntax is subtraction and division.

Intel syntax's

    sub rax, rbx
is equivalent to AT&T syntax's

    subq %rbx, %rax
...and it computes (rax - rbx), not (rbx - rax)!

I suppose you could justify the AT&T syntax as meaning "subtract rbx from rax". But it seems incredibly counterintuitive to me.

Edit: As another comment pointed out, this issue also applies to comparisons and is even worse there. Intel's

    cmp rax, rbx
    jg foo
is equivalent to AT&T syntax's

    cmp %rbx, %rax
    jg foo
and it means "compare rax to rbx; if it's 'g'reater, then 'j'ump to foo". There's no good way to write that in English where rbx comes first. At best you could say: "compare rbx to rax; if rax is greater"…


OK, so renaming a file should be:

  $ mv new-name.txt old-name.txt
I defer to the blogger's superior UX expertise!


Well consider consistency. In assembly's case. Using to from order makes it consistent with C. Consistent with manuals. And in the case of arithmetic instructions, consistent with the order used in mathematical notation.

On the other hand for shell scripts, using from to makes it more consistent. With what? Pipelines read a from-file process it and then write. from to.


Ironically, to from order makes it consistent with memcpy/memmove. But that ISO C function came from AT&T Unix. BSD has bcopy which uses "AT&T syntax".


Hey it works for github pull requests: base <- compare

Merge the right into the left. Sure, totally intuitive.


There are other operating systems that did use something like "ren new-name.txt=old-name.txt".

Also, memcpy(dest,source,len) in C.


IMO there shouldn't be a way to run programs without named arguments.


Have you ever programmed in objectivec or swift? I imagine what you're describing working something like that, but on the command line.

A few years back, mpv changed to make named arguments require an equals rather than a whitespace. I've gotten used to it, but it seems like such a weird outlier.


Eh, there's examples of every permutation:

  ;; Lisp
  (setf a (xor b c))
  ;; Kanren
  ;; Unification instead of mutation.
  ;; Therefore these are equivalent:
  (== a (xor b c))
  (== (xor b c) a)
  # Contrived untested Raku example
  $b ^^ $c ==> my $a;
  % Amberjack
  b xor c -> a
  // Java
  var a = b ^ c;


tests the raku...

  my $b = 42;
  my $c = False; 

  $b ^^ $c ==> my $a; 
  say $a;    #[42]
^^ yep


new-name.txt := old-name.txt


Might suggest to change the title to specify this is about the Assembly language.


I'm curious if there's another "AT&T syntax" that would be confused with this? I always assumed was a very well-known assembly style.


AT&T: Local Analog Loopback Test

The modem will perform the local analog loopback test if &T1 is selected. The test can be run only when in an asynchronous operation in non-error-correction mode (normal). To terminate the test in progress, the escape sequence must be entered first (see Section 3.1.1). If S18 is non-zero, the test will terminate automatically after the time specified by S18 and the OK result code will be reported.

https://web.archive.org/web/20151028101531/http://www.zoomte...


Is that a syntax? Would anyone be reasonably be confused by it?


I guessed it was about the (Hayes) AT Command Set for modems.

AT vs AT&T phone company and childhood memories.

(Clearly this was wrong after clicking into the article)


How anyone could keep x86 assembly and AT&T’s assembly straight is beyond me. Learning the industry standard assembly was hard enough. Thank goodness there are people who like this stuff, phew!


I grew up on Intel-style because that's what Borland and Microsoft assemblers used. This article seems like stylistic bikeshedding/religious war of left vs. right hand destination apart from a few ergonomic gotchas.


> mov eax, ebx ; (1) load one dword from the EBX register and store it in the EAX register

This is a mov instruction, not a ld instruction.

There is no loading going on. The value in EBX is already loaded. Otherwise it wouldn't be in a register.

It's hard for me to take someone seriously who doesn't understand the difference between a load and a move but feels as if they have the right to an opinion about assembly.


Note that LEA (load effective address) doesn't load from memory either. Intel also uses "load" in the descriptions of register-to-register moves: https://www.felixcloutier.com/x86/mov-2 . Although confusingly, here "load register X" seems to mean "store to register X" (and again, not a memory store). Anyway, the word "load" doesn't imply "from memory" the way you suggest.


No, Intel gets the distinction exactly right: "The MOV instruction performs basic load data and store data operations between memory and the processor’s registers and data movement operations between registers."

https://cdrdv2-public.intel.com/819723/325462-sdm-vol-1-2abc... Volume 1, page 7-3 (page 181 of the PDF).

In the context of assembly, load/store refers to memory ⇆ register.


https://www.felixcloutier.com/x86/lea: "LEA — Load Effective Address"

https://www.felixcloutier.com/x86/mov-2: "MOV — Move to/from Debug Registers ... At the opcode level, the reg field within the ModR/M byte specifies which of the debug registers is loaded or read. The two bits in the mod field are ignored. The r/m field specifies the general-purpose register loaded or read."


Completely irrelevant to the conversation.

The question is whether "Intel also uses 'load' in the descriptions of register-to-register moves."

What Felix Cloutier says is not probative on this question.

What Intel says in Intel's manual posted on Intel's website is the definitive authority on what Intel says on the matter.


https://cdrdv2-public.intel.com/819723/325462-sdm-vol-1-2abc..., Vol. 2A page 3-598 (physical page 1191): "LEA—Load Effective Address"

https://cdrdv2-public.intel.com/819723/325462-sdm-vol-1-2abc..., Vol. 2B page 4-42, (physical page 1263): "MOV—Move to/from Debug Registers ... At the opcode level, the reg field within the ModR/M byte specifies which of the debug registers is loaded or read. The two bits in the mod field are ignored. The r/m field specifies the general-purpose register loaded or read."


Frankly this article's complains read childish to me.

Also, the masm syntax is full of weird-o heuristics to account for the fact that you can omit the size of the operands, and I have been hit by that several times. Yes, I prefer writing in masm, but if I had seen at&t syntax first, I am sure I would be a fan of at&t syntax.


Howant people are writing nee assembly languages such that this matters?

Why would anyone even write in an assembly language instead of a human friendly language that has a simple compiler to to goofy assembly?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: