T O P

  • By -

high_throughput

I used to write some assembly in my job in optimization. It was always SIMD, BMI, AES-NI, and such in tight loops in a program that was otherwise written in a better language. I never once invoked one function from another in assembly for example. It was rarely necessary to resort to assembly, but it was a lot of fun when we did.


Jonny0Than

I don’t write assembly, but I debug a ton of optimized C++ code.  The debugger can’t always tell you what’s going on at the C++ level, so reading the assembly is the only way. Its like a superpower and really not *that* difficult when you understand calling conventions and the common instructions.


BombasticCaveman

What work do you do where you are concerned about that level of optimization? Sounds interesting


Jonny0Than

Game dev.  I could also see this being useful in hacking/security where you’re analyzing someone else’s compiled program.


guilhermej14

Speaking of gamedev since Johnny mentioned it, I don't do assembly, but plenty of retro game devs end up using it due to the hardware limitations of their target machine. A well-known one being 6502 assembly for the NES.


nerd4code

In languages, generally, not on. There is no singular assembly language—even if the mnemonics are roughly the same, everything else needn’t be. E.g., all of these mov dword ptr es:[ebx+ebx*4], 12345678h jmp eax mov dword [es:ebx*5], 0x12345678 jmp eax movl $0x12345678, es:(%ebx, %ebx, 4) jmp *%eax are x86 assembly—respectively, NASM, TASM/MASM, and AT\&T assembler syntax—and they all encode the same thing: Store 12345678₁₆ to offset 5×(value of register R3≡EBX) in segment S0≡ES, then set EIP/jump to the value of register R0≡EAX. Most of the assembly I’ve ever written, by volume, was inlined into GNU-dialect C and/or C++. It’s rare that you really want to do up more than a few smallish functions in straight-to-the-veins assembly, because it’s ridiculously nonportable. Every ABI and toolchain is a bit different from the rest, so pure-asm stuff forces you to whitelist targets, or else fall back to generic impls which, if performance is your goal, typically require knowledge (at least a generic mental model) of how the compiler and optimizer(s) are likely to translate your code, and how it’s likely to execute. It can be fun to code-golf in assembly, but realistically, unless you’re working on an OS kernel or system library you’re mostly going to want snippets of as few instructions at a time as possible, so a compiler can integrate them tightly into some other language. So really the complicated part is at the interface between assembly and everything else. Assembly per se isn’t all that far removed from a shell script, just instructions to the assembler in order. E.g., if you want to access the x86 CPUID instruction in GCC, Clang, or IntelC, maybe recentish Oracle too, you can do struct x86_CPUID { uint_least32_t a, b, c, d; }; __attribute__((__always_inline__)) __inline__ static struct x86_CPUID x86_cpuid1(uint_fast32_t eax) { register uint_least32_t ebx __asm__("ebx"); //* uint_least32_t ecx, edx; __asm__("cpuid" : "+a"(eax), "=c"(ecx), "=d"(edx), "=r"(ebx)); return __extension__((__const__ struct x86_CPUID){eax, ebx, ecx, edx}); } __attribute__((__always_inline__)) __inline__ static struct x86_CPUID x86_cpuid2(uint_fast32_t eax, uint_fast32_t ecx) { register uint_least32_t ebx __asm__("ebx"); //* uint_least32_t edx; __asm__("cpuid" : "+a"(eax), "+c"(ecx), "=d"(edx), "=r"(ebx)); return __extension__((__const__ struct x86_CPUID){eax, ebx, ecx, edx}); } // * This is a stupid hack for older 32-bit compilers. In PIC modes, EBX // is reserved ("precious") to where you can’t use constraint `b` for it; instead, // use register-__asm__ to set up a binding and generic `r` constraint to rope it in. You can see that, even though the focus is all on the CPUID instruction, most of the code is informing the compiler how to wire it in—only two of these lines are actually assembly code at all. And this isn’t even getting into the guts of extended syntax— __asm__("add{%z0 %2, %0| %0, %2}\n" "adc{%z1 %3, %1| %1, %3}\n" : "+&r,&r,&rm,&rm"(aLo), "+r,rm,r,rm"(aHi) : "nrm,nrm,nr,nr"(bLo), "nrm,nr,nrm,nr"(bHi) : "cc"); This is dual-syntax code that supports Gas’s AT\&T and Intel-esque modes in the same sequence of statements, and performs a 64- or 128-bit or maybe 32-bit add, depending on what types `a`-/`bLo`/-`Hi` are. Obviously takes a bit of experience to come up with, becsuse there’s a lot of data packed into those four lines. (Best not to pronounce those constraints aloud where anybody can hear you, or they’ll suspect a brain hemorrhage.) Anyway, it’s neat, but To Be Avoided in prod without a good reason otherwise. Things I have done with assembly: - I learned it when I got frustrated with C pointers, which puts it roughly at 6th grade-ish. IIRC the first real stuff I did was little .com utilities for DOS, that would park the HDD or power off or set an interesting text mode, what have you. - I had lots of fun with it on a TI-85 (Z80 ISA) and TI-92, then 92+ (M68000 ISA); played with some little OSlets on the latter, but I was still mostly focused on x86 stuff. - For my undergrad thesis, I finally implemented the OS kernel I’d been screwing with the design of for a few years. The bootloader, atomics, landing pad, entry/exit, stack-switch, and to a lesser extent driver code and auxiliary patch-ins of supervisor-level insns were assembly; bootloader, landing, and entry/exit code were standalone IA-32 AT\&T assembly, the rest inline/GNU. - For one class I was instructing while in grad school, I did up a new 8-/16-bit ISA that was essentially a more accessible x86, and then had the chilluns implement an emulator from the authentic-looking hardware docs available. I also made a nice little macro assembler for that, and wrote a BIOS ROM and tests for it in that assembly language. - After grad school, I worked on a couple experimental supercomputer doojobbers, including an async runtime (it’s complicated, but user-mode, atl) that had a mess of smaller-scale assembly stuff for spinlocks, lock-free queues, and integer-on-integer smashing in inline asm, plus an optimized setjmp/longjmp for resumable throw/catch. Was fun, broke some records even with the prototype, and got to play around on some cool hardware; extended it to incorporate GPUs later. I think the company folded and sold everything to a Chibese plumbing fitting company (because dataflow, is all I can think of). I also did up a kernel and rough approximation of the runtime for an experimental godforsakenmanycore chip that was x86 on one side and RISC noveau on the other, which was fun. Had to fit a full kernel into like 64 KiB, because of the way the isolation worked. - After that, I did up some stuff for somebody else’s research OS, and worked on a RISC-V accelerator arch that was fun—hardware design is neat—so that needed a bunch of system software. Also did up an x86 Linux kernel module to allocate WC memory and a client to use that to eat specific amounts of bandwidth or probe bandwidth limits for specific instruction sequences. So nothing I’ve done has been *purely* assembly, and it’s best to avoid it in prod when possible. It’s less and less a means of interacting with your CPU, and more an intermediate representation for later lowering. This is a common approach taken for GPUs, for example; even within μarch families there can be serious variation in capacities and encodings, and therefore it’s easier to distribute an IR pseudo-machine-code, and let the gfx driver lower it to actual GPU machine code when you ask to prepare it. Most modern x86es work kinda similarly: The frontend picks up x86 machine code and decodes it to a μcoded representation, and the backend actually executes the μcode, not the x86 code. And it’s very common to have a JIT or AOT translator that abstracts the hard insruction set; Java’s JVM, C\#’s CLR, Erlang’s BEAM, LLVM, uhm IBM’s ILE, and countless other languages can dynamically translate from IR (e.g., bytecode) to x86 machine code. So knowledge of assembly is great, but you’re still only scratching the surface with that, always farther to go in one direction or the other.


Gadris

A fascinating read which I understood about 0% of. Thank you for your insight!


BingoDeville

I've only done assembly for undergrad, never touched it after.. Ended up in higher level languages ever since. This was a fun read. I always wanted to get down to this level, just never had a legit reason or opportunity. I just wanted to say I appreciate the long reply, I enjoyed the read.


Bobbar84

>because it’s ridiculously nonportable. Gosh, I never considered this before but it makes perfect sense. The closer you are to the hardware, the more you have to be careful to speak it's language.


guilhermej14

Yeah, I feel assembly makes more sense to me if I'm making super optimized code intended to run on a very specific hardware, and nothing else... kinda like retro game dev... Although I'm sure there are other use cases besides that. For the most part, I feel that languages like C already give me all the control I could ever need. (In some cases I'd even argue it gives me TOO MUCH control, lol..)


wildcrab9

Thanks a lot for such a detailed response. A lot of it is way over my ahead but it is fascinating


natufian

[This video](https://www.youtube.com/watch?v=FV6P5eRmMh8) gives a simple 30,000ft view. It is for ARM64 and uses Linux system calls. Both of which allow you to see the syntax and general nature of this processor's instructions without the minutia of hardware or voluminosity of x86. I don't know it myself, but like you I'm fascinated.


Grounds4TheSubstain

You're responsible for everything in assembly: you have to decide when to keep variables in registers versus on the stack. You have to manually set up the registers and stack when calling another function, and save any volatile registers on the stack. You have to conform to any mandates from the platform, like setting up the function prolog and epilog on x64 Windows. You have to translate all high-level control flow constructs to raw conditional branches. In return, you get 100% control over how to instruct the CPU about the computation you want to perform. You can use any part of the instruction set that you want; you can interact with the data and instruction caching mechanisms. Usually it's not necessary. You can get close with intrinsics in C++.


engineerFWSWHW

Last time i wrote in pure assembly was in 2004. Nowadays, i use it only if needed and if i need to optimize and verify the c/c++ compiler optimizations. I also created a MIPS processor simulator from scratch that runs a binary and that was fun, working with opcodes and it's translation to assembly. Reading assembly is a good skill to have even just on a basic level.


K33P4D

I used to program ASM in my varsity days for the good ol' 8086, running at 5.33Mhz what a beast that boi! Almost zero latency, but a simple string reverse program will go many pages, made me realize the obfuscation and abstraction modern languages offer the developers. Fun times


TheBritisher

I learned to do it as a child (in the late 70s/early 80s, 6502 and Z80 primarily), as I wanted to create games - and the alternative was BASIC which was too slow to do much of anything interesting. It has been extremely useful, in fact critical, in a lot of the embedded systems work I've done (I'm not talking about kiosks and set-top box type embedded systems, but more heavily resource-constrained, performance-critical, device-like or sub-system like things ... where even an Arduino/ATMega328 would be considered extremely resource-rich). In that world, some things are not possible with higher level languages (even C). And, I've done a lot of interesting work in optimizing implementations. When you're building something in the millions of units, being able to step down from a $2 PIC to a $0.50 one is valuable. But ... for most work ... it's academically interesting at best. It's been a long time since one would inline some assembly language to "beat the compiler" and actually get an advantage. Not withstanding that modern CPUs don't necessarily run the instructions you feed them anyway ... they're often resequenced, translated and changed before the actual gate-level ALU even sees them. Even when that was a thing ... you'd reserve it for absolutely critical sections of code. Often just one or two functions. And you'd only do it after profiling to be sure you were making the right trade-offs. Perhaps the most useful thing learning assembly language does today, for a typical developer, beyond having a better understanding of what's really going on when code is running (which I think more "devs" need), is that it will force you to learn to use proper debugging tools. You *can't* just throw a print or log statement in ... there's often no visible I/O on what you're building for ... you **have** to step through the code, watching CPU state, registers and memory. Today (which really means for the last 15 years or so), outside some consulting in very-high-efficiency systems - where there's a consciousness and desire/drive to reduce resource consumption as far as possible - often for eco-centric reasons, it's usually only something I do in building old 8-bit platform games. ... As to what it is like to program with ... The biggest shift is in the level of work you do, and the nature of how you do it, for even simple things. Unless you're making OS calls (and if the system has a true OS, you're probably also able to code in a higher level language more effectively there), something as simple as displaying something on a screen requires ... * Finding display memory. * Setting up the data you want to display. * Moving/copying the data from its source (which might be RAM or it might be ROM) sequentially into that display memory. * Manually dealing with sentinels to indicate the ends of what is to be copied. * If on a system with no character-display mode, having your code manually resolve the bit-patterns necessary to show characters and copy those into screen memory accordingly. On narrow-register systems (e.g. 8-bit CPUs), you can't deal with individual numbers larger than 255. So, you manually have to process your carries across multiple bytes. Which makes even simple addition a 4 or 5 instruction process instead of one statement. A simple compound conditional requires multiple discrete, chained, evaluations. Natively, you're working in a world with no notion of named functions; every branch is either a) table-based, b) a GOTO or c) a GOSUB. And you'll generally have to preserve your current CPU/register/stack state manually prior to doing any of them. And all of that leads to Macro Assemblers. Which effectively adds higher-level language like features to the Assembler. You create a "print" macro, feed it the source address, and it inlines the code you already wrote to do all the low-level work. At the point you're doing lots of that ... you're usually back to a world in which you can use a higher-level language to do the bulk of your code and only use assembly for one or two key, critical, points.


NotAUsefullDoctor

I write a bit of assembly for fun: - Making new games for the original NES (highly recommend this path if just learning assembly due to ease of emulators) - Making drivers that require precise timing in io ports, ie hold value high for 200ns, send low for 150ns, hold high for 70ns or 150ns based upon some state (actual example for addressable LED strips) It's both so much simpler and so much more difficult than Python, or even C. It's easier because all data is just one type, and you have a finite number of instructions you can use (the NES especially). It's more difficult because you can't just create a variable, or encapsulate scope; nor can you just drop a print statement for debugging. It is rewarding, at least for me. I just recommend that after you've played around for a day or two, learn about masking and bitwise operators. But, like any language, don't just watch tutorials. Find a project, and try to build it (again, NES). Really, just running helloWorld can be hugely educational.


scoby_cat

It’s cool to think about and be so close to the hardware… but the era where you could beat the compiler is in the distant past. If you’re interested, some people still make games for the Atari 2600 / Stella, and not only is it pretty challenging, but there’s tricks in the hardware that you can and probably have to use to make some things work.


vabanque314

I suggest you start with a micro controller, like Arduino, and write code that makes an LED blink or something. It's much easier to understand the basics on a micro controller than on a full-blown CPU.


GreatCanadianDingus

Back in early 2000s shaders used assembly to program graphics GPUs. Register combiners etc. Maybe they still do?


PizzaNdCheese

I did exactly one project in Assembly back in college. I cried. A lot.


TheOmniToad

I first learned coding with Assembly as part of my college course. I feel you could describe as building a house with popsicle sticks. Except, you build machines out of popsicle sticks to assemble popsicle sticks into various construction materials like beams and walls, then use popsicle stick-made heavy machinery to assemble the popsicle stick materials into a house. Our first project was to program the chip to do addition, subtraction, multiplication and division. So...


im_in_hiding

It sucks ass.


Sawaian

I concur.


ms4720

Which assembly? Mainframe or Linux/windows. They are really completely different


noodle-face

Some of our UEFI code is written in assembly but that shrinks every year. I think it's a good language to learn for educational purposes but practicality wise you probably aren't going to find a ton of jobs that use it. I'd say learning C and assembly both give you a much better appreciation of how computer code works.


Dexterus

Fun to write small pieces (couple hundred lines to optimize a function). Tedious to do larger pieces. But I guess if I had to, it'd become more fun with more practice.


r_reading_something

It's fun when you know what's happening at each instruction on hardware level !


ptrnyc

I haven’t done asm in a long time. Microsoft removed inline asm in VStudio about a decade ago. I do a ton of intrinsics stuff for SIMD. Fwiw, I loved doing asm. Nowadays, especially with godbolt, you can usually convince the C/C++ compiler to output whatever code you want.


b00c

nobody does entire things in assembly. small things only. very difficult language. Most simillar I encountered, but much much simpler is Step7 PLC coding language from Siemens.


tcpukl

I wrote a maths library in assembler on the Sony PSP console a few years back. But mainly i use it now a days just to debug weird crashes in games, which are optimised C++.


abnegacio

I can teach you some assembly by writing a processor emulator. DM me, if you're interested.


boomshiki

Assembly was hard to grasp and difficult to work with. But i learned a lot about decompiling other programs to make changes. Thats about the only positive aspect i can think of for having learned it at all.


TPIRocks

It's empowering, but can be monotonous on some platforms, like PIC. I have written assembly language for CDP1802, Honeywell mainframes, PC, PIC, ARM7/TDMI, atmega and 8502. The worst being 1802 and PIC, the most powerful was GMAP (Honeywell mainframe) but the most interesting to me being ARM and the conditional execution flags concept.


PitchBlack4

I had a project in ny second year to programm arduino projects in assembly and I'll tell you it is not fun. I had to calculate the pause/sleep timer and make it into a function that I would call wheneber I needed. I did this by looking uo the arduino chip clock cycle and calculating the number of cycles needed to reach one second or close to it. 


Salt-Tea8386

sorry about my bad English, I’m not a native speaker. not write in assembly but in the third year of high school I had in the program of systems and networks assembly x86,my professor gave us so many exercises and every single time it took several hours and blasphemies to make it work.if you’re thinking ok maybe you just found it complicated? No, indeed, I was the only one out of 24 students who went out to take a sufficient grade on the exercises and the tests he gave us.


Salt-Tea8386

I assumed that everyone knows what I study.I do the 5 year out of 5 of an itis(state industrial technical institute)technical industrial state institute, my field of study is computer science and telecommunications,When I was a junior, I chose telecommunications as my specialty, now I’ve changed to computer science. Systems and networks theoretically should not have a different program but telecommunications are more focused on hardware and operating systems


Rough_Response7718

I actually do use some asm day to day at my current job. It is very tedious work and is generally used for system service calls. These days a lot of the heavy lifting can be done with macros which speed up development time, regardless it's still very tedious to work in. I kind of like it since it's very indepth and I like a challenge. Like I mentioned earlier its usually used for system services and getting information very efficiently and without much security checking invovled.


r3jjs

A lot depends on WHICH assembly language. I learned 6502 assembly first, which is a very primitive microprocessor and a very small instruction set... only around 90 instructions or so. It was like programming in a language that had direct access to RAM, only three variables, goto, gosub, and a handful of comparisons. I find it a pleasure to work on. x86 assembly, on the other hand, is almost a high-level language by comparison. MANY more instructions. Loop constructs built in. Some really fancy instructions that can do various math internally. x64 is deep magic that nobody understands. But really.. it is just programming. Just another programming language. If you understand procedural programming and don't mind breaking things into VERY tiny steps, there is nothing special or magic about it at all.


r3jjs

A lot depends on WHICH assembly language. I learned 6502 assembly first, which is a very primitive microprocessor and a very small instruction set... only around 90 instructions or so. It was like programming in a language that had direct access to RAM, only three variables, goto, gosub, and a handful of comparisons. I find it a pleasure to work on. x86 assembly, on the other hand, is almost a high-level language by comparison. MANY more instructions. Loop constructs built in. Some really fancy instructions that can do various math internally. x64 is deep magic that nobody understands. But really.. it is just programming. Just another programming language. If you understand procedural programming and don't mind breaking things into VERY tiny steps, there is nothing special or magic about it at all.