
Assembly Language Programming: Still Relevant Today (2015) - mr_tyzic
http://wilsonminesco.com/AssyDefense/
======
peter_d_sherman
Excerpt:

"Jeff Laughton (Dr Jefyll on the 6502.org forum) says, "I recall hanging out
with a programmer pal o' mine and a younger fella who was in college. The
young fella was complaining, 'We _have_ to take assembly language,' and Len
corrected him immediately, saying, 'You _get_ to take assembly language!'"

<g>

~~~
dimator
What a great way to put it.

What's great is that learning assembly is like taking the first step to
understanding the bridge between software and hardware.

I remember a taking class that started out as confusing as hell. The first few
exercises seemed really mystical and just so brittle. You'd have to be a
wizard to ever get this, one thinks. But by the end of the class, we made an
asteroids-like game, complete with RNG based on input timing (the time between
game execution and when the user clicked start). That was really an
enlightening moment.

------
joe_the_user
"...self-modifying code, [is] sometimes appropriate to solve certain problems
that have no other solution, or for improving efficiency, as in double-
indirect addressing."

I have read that self-modifying code on the x86 architecture is pretty
dangerous at the assembly level.

More broadly, this kind of comes back to all the issues in the "C is not a low
level language" thread. Some level of assembler certainly gives the programmer
as full access to the machine as possible. But naive assembler from the 8086 -
80486 eras is going to be rearranged in a lot of ways in a modern Pentium
processor and counting on in-order execution may be a mistake.

Edit: at the same time, the modern processor doesn't really allow a lower
level than assembler normally and the default approach is assuming flat memory
but being aware of the pitfalls of multiple caches being involved.

~~~
tlb
It's quite widely used for dynamic library jump tables. When calling a
function in a dynamically linked library, it calls a stub, which is initially
a call to the lazy linker, but gets replaced with a call to the resolved
function.

You may be remembering early x86 chips that didn't properly invalidate the
instruction cache after a write. Modern chips are fully cache-coherent.

~~~
matheusmoreira
This is interesting. I've read documentation about dynamic linking and it
describes this replacement process but I never truly understood the fact it
was self-modifying code. Doesn't this imply the program's code is writable? I
know that JIT compilers also emit code into writable and executable pages.
Aren't there security implications?

~~~
saagarjha
There are. That’s why dynamic linking typically doesn’t use self-modifying
code and JIT compilers take a number of precautions to prevent attackers from
being able to execute arbitrary code.

------
shaggie76
While I can only recall writing real assembly once in 20 years I've _read_
disassembly thousands of times while debugging.

------
commandlinefan
> It is common for the beginner to want all the fancy tools too soon

Am I the only one who’s never felt that way? I get grief from people around me
(especially “hurry up and get it done” management types) for spending too much
time in the low levels, trying to really understand what I’m doing and what’s
going on.

~~~
FartyMcFarter
You should change what people are around you, if possible.

------
mjpuser
Not sure why HN is saying it is obvious that assembly is relevant because
compilers... The article's intent is around a programmer writing assembly. I'm
sure there are niches but I can see web developers getting away without
writing assembly in their professional career.

~~~
jkoudys
Might see that flip in the coming years, if you consider web assembly (wasm)
to be assembly. I do.

~~~
pizlonator
WebAssembly is not assembly in the ways that the article talks about. Like,
writing it directly doesn’t give you any special control or guarantees over
timing.

~~~
jkoudys
It's assembly against a virtual machine, not a physical one. You're right it's
not appropriate for an embedded system or some other RTS, but assembly doesn't
stop being assembly when you target a virtual machine.

~~~
pizlonator
It kinda does in this case. Don’t kid yourself. In real assembly, the really
interesting part is how to use a finite register file. WebAssembly has an
infinite slab of variables available, in the sense that you get to say how big
it is. That fundamentally changes the game.

~~~
pjmlp
There are real CPUs that are just like that.

In fact, most mainframes have always made use of microcoded CPUs, with
Assembly being referred as bytecode on the programming manuals.

You just need to dive into IBM and Xerox PARC manuals, for starters.

~~~
pizlonator
Sounds like you’re saying those machines executed bytecode.

Otherwise there isn’t a great limiting principle to your logic. Just because
someone once built hardware that executes such a high level assembly that the
manual referred to it as bytecode doesn’t mean that all bytecode formats are
assembly.

~~~
pjmlp
Indeed I am, the interpreter is the microcoded CPU.

Even modern 80x86 Assembly is a low level form of bytecode, given that the
micro-ops that are processed by the microcoded CPU are completly unrelated to
80x86 Assembly opcodes.

------
chrisseaton
> Assembly language yields maximum control and execution speed.

It yields the _only_ control and execution. Almost programs are generated
through an assembler and assembly language. How could it possibly _not_ be
relevant?

~~~
saagarjha
There are a huge number of software engineers today, dare I say most, who
cannot read or write assembly and are isolated from it by multiple layers of
abstractions.

~~~
chrisseaton
I know it's abstracted away, but it's still relevant to how the programs run
because almost all programs go through an assembly language phase.

~~~
saagarjha
What languages don’t? All of them execute on the processor in some way.

~~~
cryptoz
I could be mistaken, but, don't JVM languages avoid assembly steps? They use
bytecode and are translated directly to machine code; I'm not sure there is an
translate-to-assembly step. I'm also not sure how much of this confusion is
semantics vs actually different components of the compilation and running
process.

~~~
chrisseaton
They still have assemblers in them - they're just done with method calls
rather than text.

[https://github.com/openjdk/jdk/blob/e73ce9b406c34bd460f0797f...](https://github.com/openjdk/jdk/blob/e73ce9b406c34bd460f0797f0c86f423e141782a/src/hotspot/cpu/x86/assembler_x86.hpp#L817-L820)

Also if you look at the compiler's intermediate representation at the very
last phases you'll see it basically looks like an assembly program.

~~~
saagarjha
Pure interpreters like CPython?

~~~
chrisseaton
The interpreters themselves go through assembly language. Assembly is relevant
because the whole stack is still actively built on it.

------
georgeplusplus
To me, assembly is one of the things while self studying CS that I felt lacked
good support in resources such as this, MOOCs, or just plain explaining it.
Usually when I post a topic trying to Demystify the topic I am greeted with an
extremely hard to digest read about said topic that is more meant for people
Already knowledgeable in the subject.

And I feel assembly should be more a core building skill in a programmers
toolbox. So this article is very welcoming for o me.

~~~
kick
Modern assembly is kind of...bad.

I would recommend checking out an old book for an old mainframe's assembly
language. They're usually much less mystic by virtue of being much less
complex. IBM had some really nice manuals and books; no one ever got fired for
buying IBM because an IBM machine could be programmed by a dog.

Octal is where it's really at, though, if you get really into this. A fun
weekend project is to write an octal "decompiler" (ideally you won't have
compiled anything, just having written some octal by hand) that allows you to
reason with what it's doing by translating it to an actual language rather
than just thin syntactic sugar over 1s and 0s. Octal itself isn't so
difficult, it makes binary much easier to reason with, but this definitely
helps you get a more intuitive sense of what is what.

Of course, it's not something that has a substantial amount of value with
modern machines. Maybe eventually we'll get back there; I think I'll enjoy it
when we do. Until then, though, it's fun to play with.

~~~
bear8642
> Octal is where it's really at

Why octal, not hex?

~~~
kick
Octal is easier to keep in your head while not sacrificing any efficiency.

------
FpUser
As recently as a year ago I used built in assembler in Delphi to perform some
low level timing. It was not for just super-duper performance though.
Surprisingly it was the most straightforward and simple way. Meanwhile my
firmware for 3-Phase AC motor torque and speed control for really low power
micro controller was doing fine with plain C without any assembly.

~~~
7thaccount
What do you do for a living?

~~~
FpUser
I design and develop product for living. Universal guy. Mostly software but
sometimes part of it is also firmware / elecronics / hardware. I happened to
be a person who did everything from billing systems and other giant products
for TELCOs and down to firmware for micro-controller.

Some products I own and some are made to order. Some I did on my own and for
some I was a leader of big team

------
zzo38computer
There is many kind of assembly language, some for the actual computer
hardware, some for VMs, and some used as both.

I have used and sometimes still do use assembly language, including 6502
(specifically, NMOS 6502 without decimal arithmetic, including unofficial
opcodes), and a little bit of x86 stuff (although the modern x86 is very
messy, I think), but also Z-machine and Glulx. I have also used MIX and MMIX
assembly (and may use MMIX more if I would actually make a computer with it).
And then some other programs (such as ZZ Zero, which is similar to ZZT) has
its own kind of assembly language.

One feature not mentioned is the relative numbered labels such as 1H and 2H
available in MIXAL and MMIXAL; you can then use 2F to find the next 2H label
forward, or 2B to find the next 2H label backward. My own assemblers for Glulx
and ZZ Zero support the similar feature too.

~~~
commandlinefan
He does touch on local labels briefly - he suggests (and I agree) that you can
get the same effect, more maintainably, with macros.

~~~
zzo38computer
Macros are good too, although I think both are useful.

------
Lerc
There's food for thought there. As part of my current project I wrote an
assembler for 8-bit Avr. (on github
[https://github.com/Lerc/AvrAsm/](https://github.com/Lerc/AvrAsm/) )

Part of my motivation for this was to have an assembler that ran in the
browser (for my fantasy console that also runs in the browser), but another
big part of it was to write an assembler designed to be more friendly to
people writing assembly directly.

When I wrote 6502 asm I mostly did it from Supermon which is a no frills
experience. It's nice to see the features that assemblers have now, I think
I'll be implementing quite a few of those macros from this link in my own
assembler.

------
dang
A thread from 2015:
[https://news.ycombinator.com/item?id=10522158](https://news.ycombinator.com/item?id=10522158)

------
z3t4
Optimizations have come a long way. Not only do you need to be an asm
programmer to beat the optimizer, you need to be a _good_ asm programmer.

------
pjmlp
Sure it is, someone has to write compiler backends and OS bootloaders, even if
all instructions would be exposed as intrinsics.

------
davidhbolton
Even back in the 1980s, author Lance Leventhal who wrote some great
programming books on assembly always warned that productivity wise you write
the same number of lines of code whether its 6502 or C.

As that 6502 example in the article shows, you don't great great productivity
with assembly. And even macros don't improve on it that much.

~~~
cmrdporcupine
One writes 6502 assembly by hand because there is really no alternative on
that ISA. It does not make a good target for a C compiler, there are very few
of them to begin with, and hand written 6502 (or 65816) is going to be better
than anything produced by any compiler for it at this point in time.

------
p0nce
On modern x86 it's not very useful at all anymore. An ICC or LLVM backend with
compiler intrinsics will get you quicker performance, with reduced maintenance
and cost. Performance will also move over time with backend optimizations
getting better.

You can still do it if you care about debug build performance.

------
buserror
I'm rarely keen on posting negatives on articles that clearly took a lot of
time to make, but I think this requires a bit of correction.

I think this article is very, very simplistic. All of it relates to a 8 bits
CPU that is 40+ years old.

I switched to HLL as soon as I could get my hand on a compiler, namely, UCSD
Pascal at the time! Then the Pascal, then to C and then myriads of other
languages. I covered 6502, Z80, 68k (all of them, to 68040), PowerPC (all of
them from 601 prototypes to G5s), ARMs (more than I can count) and x86s
(same).

True to be told, the assembly language I started with /helped a LOT/ with be
becoming an efficient developer; a developer who understand what 'code' is
being generated when he writes an expression, a statement, a loop, and one who
understands what the runtime implication are for most of the 'sugar coating'
HLL gives.

However, starting (a bit) with the 68k, then even more so with the PowerPC, it
became pretty much impossible to write /from scratch/ an assembly equivalent
that was QUICKER than the compiler generated code. That was 20+ years ago.
DRAM latency happened, pipelining happened and SIMD happened.

Today, hand writing assembly is pretty much stupid on modern CPUs. Given the
register files, timings, shadow registers, bus latencies etc etc the compiler
will ALWAYS be better because there is _so much_ criteria to think about when
generating code...

I'm not saying that having the knowledge is not useful; the _best_ use of
assembly is to write some code il HLL, one that is supposed to be super-mega-
critical-quick, then disassemble it and see how it looks. More often then not,
you can't make it better than it is _in situ_ \-- most of the time you will
gain is to prepare your data better, align it better etc etc -- basically,
'hinting' the compiler to do a better job. You can do serious code butchery
like that, without a hint of assembler [0].

But really, I haven't written any assembly for /performance reasons/ in 15
years, and that was Altivec on PowerPC.

For 8 bits, it's all smooth as butter, but the article also doesn't take into
account the massive progress in compilers; I'm the author of SimAVR [1] and
I've seen my load of generated code for that CPU, and the GCC toolchain is
/very hard to beat/ by hand these days.

[0]: critical audio loop on one of my old PCI card driver, converting
float<->int, applying gain etc while using the register file to the max, and
making most use of the pipelining of the G4 (at the time)
[https://gist.github.com/buserror/0a3a69cca927b8da6c9c7ee1605...](https://gist.github.com/buserror/0a3a69cca927b8da6c9c7ee1605007fc)
\-- note, the inner loop was generated by a script that was doing the cycle
calculations (!)

[1]: [https://github.com/buserror/simavr](https://github.com/buserror/simavr)

~~~
druzyek
This article is specific to the 6502 where the commonly used CC65 C compiler
the author references produces much, much worse code speed wise than what you
can with pure assembly. In that regard, the article is not simplistic in the
least. Coincidentally, I messaged the author just yesterday about the for loop
example to point out that it was generated without optimization. Even with
optimization enabled, the code is still about 3 times slower than hand written
assembly. I know this may not be typical for other architectures like AVR but
it certainly is for 6502.

~~~
cardiffspaceman
I used to do my production code exclusively in 6502 assembler, with some tools
in P-system Pascal. As I would read in magazines about the C language I would
try to imagine what the C compiler would generate for certain constructs, and
I couldn't imagine it being efficient compared to other 8-bit processors. Then
we decided to experiment (at the company) with C and got a compiler. I was
right, the code was awful. It used exactly the idioms I thought I would use if
I had to to it. I can picture a really top-notch compiler doing better
(because I'm more familiar with optimization in compilers now), but sooner or
later some of the quirks (like 8-bit index registers and only page 0 can be
used for pointers) will catch you.

------
aportnoy
This is like being an architect and noticing that bricks and cement are still
relevant... Of course they are!

~~~
neonate
It's more like being an architect and working with bricks and cement yourself.
The author's argument is that sometimes an "architect" should do that. Agree
or disagree, but it's certainly not a truism.

~~~
kazinator
An architect might in fact design something down to the brick-and-mortar
level, such that someone else assembles it to the actual building. That
architect is working with bricks and cement, effectively at the design level.

That's the same as using assembly language, rather than poking binary/hex
values into memory.

