Assembly is about as nonportable as you can get. Hardware access (not exposed by other languages), speed, and possibly executable size are the practical reasons one would write in assembly. The second and third are almost entirely outdated - storage of executables is free relative to storage of data; hardware speeds have made "slow" programs plenty fast for most things; and compilers can do better than you can quickly do, very quickly. The first is still a valid reason to use assembly, but typically in the form of a small snippet of inline assembly in C/C++ code.
> Hardware access (not exposed by other languages), speed, and possibly executable size are the practical reasons one would write in assembly.
This is a bit misleading, accesses to physical memory, disks and to other devices are usually protected by the operating system, not by the language. So you don't have direct hardware access because you program in assembly, but because you run a program without the control of an operating system. This can be done in assembly, but also in C or in any other language that can generate native code.
The only thing that you can do with assembly and that you can't really do with higher level language like C is accessing processor specific registers/instruction sets (e.g. SSE). And this is typically done using inline assembly snippets, as there is no reason to write a whole program in assembly when you only need to optimize a single function.
So I would say that having better hardware access is not a good reason to write an entire program in assembly.
On the other hand, building small, standalone executable file that use very little (stack) memory is still relevant for programs that run on very small (usually embedded) hardware. Especially when they need to run fast or to have low energy consumption. So speed and memory usage are still pretty good reasons to use assembly :)
Hmm, I guess there is some room for misinterpretation.
First, you seem to think I was answering a slightly different question - "In what circumstances should I make the decision to write assembly?" The question I was answering was closer to "When people need to write assembly, why?"
The actual question, of course, was not quite either of those.
Also, addressing a point of ambiguity:
I did not mean "hardware access ([this is something that is] not exposed by other languages)" - that is, in general, wrong.
I meant "hardware access ([when the particular thing is] not exposed by other languages)". This includes SSE (though that is starting to be included as builtins in some compilers). Outside of SSE, I grant that there isn't much on x86 platforms that is relevant to a game, since games on x86 platforms typically run under an OS. It can be relevant if you're writing a driver, though. And this can vary substantially by platform.
And no, it's not a good reason to write an entire program in assembly unless a sufficient percentage of your program is calls to these things that the C inlines are just adding cruft.
"On the other hand, building small, standalone executable file that use very little (stack) memory is still relevant for programs that run on very small (usually embedded) hardware. Especially when they need to run fast or to have low energy consumption. So speed and memory usage are still pretty good reasons to use assembly :)"
No, that is substantially more misleading. Like I said, you cannot quickly do better than a modern compiler, and a compiler does it very, very quick.
> I meant "hardware access ([when the particular thing is] not exposed by other languages ...
Ok, I agree.
>Like I said, you cannot quickly do better than a modern compiler, and a compiler does it very, very quick.
I'm not sure what you mean by "quickly" but this submission proves that it can be done in reasonable time. And if you look at the code (for example here:
https://github.com/icebreaker/floppybird/blob/master/src/gam...), it's obvious that you cannot produce anything nearly as compact and efficient with a compiler.
So, claiming that assembly is still a language of choice nowadays was maybe exaggerated, nevertheless it's nice to remember that no matter how good they are, compilers come with a cost. If you need some evidence simply compare the submitted code with any code produced with a compiler.
"it's obvious that you cannot produce anything nearly as compact and efficient with a compiler."
It's not obvious. We can argue over what's "nearly", but without even trying, manually converting the assembly to C I get 71 (1.5x) instructions out of gcc and that includes an inlining of animatebird. The resulting code is otherwise not so far from the original. It certainly reads more poorly, but that's to human eyes not x86. I'm not sure which would be more performant, and playing more carefully with the code and optimizations it can probably get even better.
Well, on Android the original Flappy Bird was 894kb in size,
and this Floppy Bird is only 8.7kb.
Plus the 8.7kb includes enough utility code to obviate the need to load an OS for the game to run on top of, so the size ratio actually is better than the apparent 100:1.
"Well, on Android the original Flappy Bird was 894kb in size, and this Floppy Bird is only 8.7kb."
You're actually making my point. Virtually the entirety of both those numbers is going to be image (and, on the Android version, audio data). Using lower-resolution assets is not something reserved to assembly language, and the size of the code is just not an issue next to that, most of the time. The biggest exceptions on desktops/servers being when executable makes the difference between spilling instruction cache and not. Things can get tighter on embedded, but there too tables and stuff are more often the cause of contention.
"Plus the 8.7kb includes enough utility code to obviate the need to load an OS for the game to run on top of, so the size ratio actually is better than the apparent 100:1."
Enough code to obviate the need for an OS (for this narrow use case, in Real Mode with BIOS calls available) is probably fewer bytes than enough metadata to convince Android to run you. But as mentioned, you're not actually comparing code size at all really.
"The TAB key changes the bird's color scheme."
I'm not sure what your point is here. Cycling palettes is a neat trick, but nothing new, nothing complicated, and certainly nothing that couldn't have been done as easily in another language.
Edited to add: Perusing your other comments, you seem to have plenty more savvy than your comment would show... were you trolling or trying to make some oblique point that I missed or what?
Well, this is a time I didn't really have an opinion
except maybe I like for things to take up less space for
the same functionality. I was just putting up the
numbers that I observed.
I didn't realize the Android version might have been
more sizable due to graphics & audio, so I guess
the functional code difference might not be as dramatic
as the 100:1 download size ratio after all.
The Tab key function was just another observation that
I had not seen in the documentation, don't even know
if it is intentional.
Check my other comment on this topic, it would be good
to get some feedback from the author.
>The only thing that you can do with assembly and that you can't really do with higher level language like C is accessing processor specific registers/instruction sets (e.g. SSE). And this is typically done using inline assembly snippets, as there is no reason to write a whole program in assembly when you only need to optimize a single function.
The other three most important things you can do in assembly that you can't do in C are:
* Indirect jumps (aka the "labels as values" or "computed goto" extensions admitted available in GNU C)
* Directly accessing status flags like the carry flag (necessary for efficiently implementing bignum arithmetic and CPU emulators)
* and optimizing register allocation across complicated control flow (like language interpreters or emulators)
It can absolutely be worthwhile to write an appreciable portion of a program in assembly if these "features" make the code significantly faster or even easier to express.
'It can absolutely be worthwhile to write an appreciable portion of a program in assembly if these "features" make the code significantly faster or even easier to express.'
Strictly speaking, yes. I would urge some hesitancy in concluding that is the case, though.
WRT the first, you take some portability penalty for using GNU extensions, but that is probably significantly less than you take by writing in assembly. Unless you're targeting an architecture GCC does not target and your alternative compilers don't have a similar extension, you should still not be writing assembly for this reason alone.
"Directly accessing status flags like the carry flag (necessary for efficiently implementing bignum arithmetic and CPU emulators)"
This falls under both "hardware access" and speed. It certainly is a reason to occasionally break out the inline assembly.
"and optimizing register allocation across complicated control flow (like language interpreters or emulators)"
This is a very bad reason until it actually proves strictly necessary (at which point it falls back under speed and/or size). Modern compilers are quite good at register allocation and getting better, and chip architectures are complicated enough that you're likely to be wrong about what's fastest and/or best. The ability to rapidly try new things will probably get you faster results faster - compare the time taken tweaking optimization flags and rebuilding, versus working out an alternative register allocation and threading it through all the relevant code. Moreover, a small change in requirements can entirely invalidate all of that work. An understanding of assembly and your target architecture sufficient to allow you to allocate registers by hand is still highly valuable when you get into that space, though, so you understand what you're looking for when comparing generated code and benchmark results.
I don't really agree with that, unless you want to call any use of a CPU "hardware access," but ok. I also don't consider it to be like accessing architecture specific instructions like SSE, because I simply cannot think of a general purpose CPU from the dawn of the micro that lacks the basic carry, overflow, zero, and sign flags, and the ability to branch on them.
>WRT the first, you take some portability penalty for using GNU extensions, but that is probably significantly less than you take by writing in assembly.
In practice, for a lot of developers, there are only one or two relevant CPU architectures, and they're almost always from the set of 32 and 64-bit x86 and ARM. Depending on what kind of program you're writing, it could be much more likely that you'd switch C or C++ compilers than switch target architectures. And if you are programming for obscure microcontrollers or whatever, you've pretty much thrown portability out of the roof to begin with and writing the whole program in assembly is not out of the question.
>Modern compilers are quite good at register allocation and getting better, and chip architectures are complicated enough that you're likely to be wrong about what's fastest and/or best.
Compilers are pretty bad at optimizing virtual machine interpreters, and this is unlikely to change. Here we go, straight from the horse's (or, uh, Mike Pall's) mouth:
"Compilers are pretty bad at optimizing virtual machine interpreters, and this is unlikely to change. Here we go, straight from the horse's (or, uh, Mike Pall's) mouth"
This could possibly fall into my "strictly necessary" bucket. I'm not sure what the timing differences actually amount to, either - I've got a threaded interpreter in a fast path in some of my code, and it hasn't proved a bottleneck (less than 1% of time during a low-latency event is spent running the programs) so I've not looked closely at register allocation through it.
I reiterate that this comes at severe cost to ease of extension and refactoring, however, and shouldn't be undertaken lightly.
Does anyone still use assembly for embedded? Of course. Is it a good idea? For some of them, and for the reasons I listed (I suppose I should add "you are working on a legacy assembly codebase").
"Still used pretty much everywhere" is just not the case anymore, even in embedded. At least, that's what the chart seems to say and it matches my experience working alongside embedded developers, knowing some embedded developers I've never had the pleasure of working alongside, and doing a small amount of embedded work myself.
(Edited to remove: Right in that link, both C and C++ are used more than assembly in the embedded space, and Arduino (more C/C++) follows closely behind assembly. The chart is unclear, but the rankings seem to be based on numbers that don't change when different filters are applied, which would mean that it does not show this. It is still certainly consistent with "assembly no longer dominates embedded", though - some of the C and C++ will be embedded, and all of the Arduino will be embedded.)
The person who wrote this wrote it to be booted directly from BIOS. That means it must handle everything an OS would otherwise do for it: bootloader, resource management, hardware access, random number generation, etc. So a project like this has great value as an introduction to low level systems and OS development.
As someone who has written no-os code, you do not need to do everything the OS does. If you do not mind giving up most of your RAM, you can stick to realmode and take advantage of the BIOS functions (you can work around the RAM limitation by jumping between modes). If your computer has UEFI, you have access to a much more advanced set of functions, and (for many purposes) can target that. I haven't done anything beyond hello world type stuff on UEFI, but it feels like you can get alot done without noticing the lack of an OS.
UEFI is awesome in terms of what it enables you to do without an OS, but once you install a few tools like an EFI shell, it's at least as much of an OS as DOS ever was.