
Floppy Bird: A Flappy Bird clone in 16-bit x86 assembly - iamnader
https://github.com/icebreaker/floppybird
======
e12e
This is just beautiful:

[https://github.com/icebreaker/floppybird/blob/master/src/mai...](https://github.com/icebreaker/floppybird/blob/master/src/main.asm)

Nicely structured code! I'm almost tempted to port it to amd64, just for fun.
After all we really don't need any more x86/16bit example code, we need more
amd64 code! :-)

~~~
girvo
I've been saying this to my co-workers over the past week as I've gotten into
modern day assembly (ARM, but still): It's basically a high level language at
this point! It's amazing how much structure you can actually give yourself
with modern tools.

~~~
e12e
I'm not sure you really need much in the form of modern tools to achieve this
-- the code base above is basically include macros and jump labels?

As mentioned by agumonkey, it feels a little bit like Forth.

In fact, I seem to recall a rather excellent tutorial on 68000 assembly
featured very similar structure (even if I never finished it -- at the time I
was defeated by my own bugs and the fact that those bugs, without an MMU or
memory protection, could easily crash the entire machine. It was rather
demotivating...).

For another example, see the original source of Apple II DOS, posted here a
while back:

[http://www.computerhistory.org/atchm/apple-ii-dos-source-
cod...](http://www.computerhistory.org/atchm/apple-ii-dos-source-code/)

Fundamentally I don't think the structure of the code is all that different?
(Please correct me if I'm oversimplifying, or missing something).

------
salgernon
Thanks for wasting 4 minutes of my life! For some reason, I picked up a Wyse
Winterminal S50 from Weird Stuff last week... a thin linux client box from
2006 that has some sort of underpowered x86 and very little memory.

So I was playing with it and it has this weird linux on it that lets you set
it up to very slowly browse the web and such, but it wasn't very interesting,
all told.

When I saw this project come up, I grabbed the .img, wrote it to a spare usb
stick and damn if the thing didn't just boot up to floppy bird. Nice work! Now
I wonder what other dumb PCs I have sitting around that can boot it...

~~~
danelectro
Turns out after you have written the floppy IMG to a blank uFD or HDD, and it
boots and plays the game, then you can partition the drive (I used Linux
Mint), format it as FAT32 or NTFS, and use the full drive for mass storage
without a conflict.

Same drive still boots to Floppy Bird only, unless you install some other OS
or something which overwrites the Floppy Bird bootloader on sector 0, and give
up the bird.

Partitioning and formatting alone do not overwrite the MBR, even if it is just
a custom floppy boot sector at the time.

------
nsxwolf
"Floppy Bird is a clone of the infamous Flappy Bird written in 16 bit (x86)
assembly.

In other words it works on RAW METAL and doesn't require an Operating System."

The second statement doesn't quite follow the first. You can certainly write a
16 bit x86 assembly DOS application.

~~~
bri3d
It's also not really running on "RAW METAL" because it's using BIOS interrupt
routines for drawing and input.

It's still awesome, though, and the code is really cleanly written and easy to
follow.

Because the code is so nice, this would be a fun project to try to port to
Protected Mode with its own HAL and maybe drivers for one set of hardware
(perhaps QEMU's). Could be a fun experiment in why OSes are hard and x86 is
painful.

~~~
drivers99
vga.asm copies bytes directly to video memory in a raw manner, once it's in
VGA mode. It only uses BIOS to switch into VGA mode ("mov ax, 0x13" and then
"int 10h"), and back to text mode before rebooting when you quit. It does use
BIOS to read the keyboard. It also reads bytes from sectors of the disk using
BIOS rather than the OS when it starts up. The sound is a beep created with
raw "OUT" commands on the data ports (for the PC speaker presumably).

In other words, it's using BIOS for input, but not drawing.

~~~
asveikau
ticks() and sleep() also use BIOS. The author could have implemented this by
handling IRQ 0. :-) Come to think of it handling the keyboard interrupts isn't
that hard either...

Also looks like boot.asm uses the BIOS to read the rest of the program...

~~~
danelectro
Seems like if the "boot.asm" (actually sector 0 of the floppy) could be made
relocatable away from sector0, then it could still be functional when stored
in a filesystem (including on a HDD) not unlike BOOTSECT.DOS. The remainder of
the floppy (sectors 1 through 16 proper) when copied to the same sectors
(which are normally blank and unused) on a MBR/BIOS style HDD work just the
same as from floppy without having to be in a filesystem themself.

Then you could easily chainload to Floppy Bird on a HDD from other bootloaders
(like WinNT5 & NT6), with no changes to the main ASM program.

EDIT: the comments in boot.asm refer to sectors 1 & 2 since they are the first
2 sectors of the floppy, these two are officially numbered 0 & 1 in a disk
editor or DD command.

~~~
asveikau
This stuff is kind of annoying to write. When I wrote stuff on raw metal
(hobby OS kernel) I mainly just built an ELF file that GRUB can load.
(Surprisingly easy to do.) Or, in days before that when I used more Microsoft
stuff, a small .COM file where ms-dos or freedos is a glorified bootloader.
(DOS is totally cool with a program hijacking everything.)

------
spiritplumber
Super sweet!

Does it have a switch that lets the computer cheat?

[http://www.catb.org/jargon/html/story-of-
mel.html](http://www.catb.org/jargon/html/story-of-mel.html)

~~~
ErikRogneby
I had not read that before.. a great read. thank you.

------
habosa
First of all, this is really cool and I think the closest thing I have seen to
'readable' assembly.

Tiny nitpick: doesn't running asm still require an operating system? I haven't
read all of the code but I assume that there are instructions for I/O or other
traps that require the OS. Isn't the OS just the layer between the hardware
resources and programs that want to access those resources?

~~~
icebraining
It might or might not require an OS; you can implement the hardware access
code directly into the program.

~~~
cyorir
True, you could leave out the bootloader and some of the sys code and this
would then nearly be a DOS program, at least the port to DOS would be easy.
Leaving it as is allows it to boot without an OS.

------
PhasmaFelis
Is "I made a Flappy Bird/2048 clone with a TWIST" the 2014 equivalent of "I
installed Linux on my hat"?

Although I admit this is more impressive than the usual "Floppy Bird only
slightly different."

~~~
golergka
And all of that, in 24 lines of javascript.

------
michaelgrafl
I'm wondering: how hard would it be to add sound to this thing? I think it
uses Piezo sound, which is probably easily addressed. But what about midi or
PCM output? Would you have to target every manufacturer specifically?

I really liked how fast my computer "booted" into the game. I would totally
start collecting games like these if there were a scene.

~~~
zokier
Correct me if I'm wrong, but I think you could cover fairly wide range of
hardware with two generic drivers, AC'97 and HDA.

edit: for this project specifically there is the small problem that you
probably need to enter protected mode for "modern" audio.

------
michaelq
This is really cool. Is there a practical reason why you would write a game in
assembly (speed? portability?)

~~~
dllthomas
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.

~~~
bnegreve
> _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 :)

~~~
dllthomas
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.

~~~
bnegreve
> _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...](https://github.com/icebreaker/floppybird/blob/master/src/game/bird.asm)),
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.

~~~
dllthomas
_" 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.

~~~
danelectro
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.

The TAB key changes the bird's color scheme.

~~~
dllthomas
_" 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?

------
ginko
Does anyone know why GIMP is listed as a build dependency?

I don't see it being called anywhere in the Makefile.

~~~
sovok
It's probably just used to create the .tga images loaded in
[https://github.com/icebreaker/floppybird/blob/master/src/gam...](https://github.com/icebreaker/floppybird/blob/master/src/game/data.asm)

------
andrewstuart2
Floppy bird? Written in assembly, I half expected it to be called "Snappy
Bird."

~~~
Narishma
It's called like that because it's a booter[1], so it's loaded from a floppy
disk at boot time.

1\.
[http://en.wikipedia.org/wiki/PC_booter](http://en.wikipedia.org/wiki/PC_booter)

------
sfeng
I would love to see a chart of when each of these ports came out. It would be
a pretty interesting representation of the difficulty of building the exact
same thing in various languages.

------
carlsborg
For systems running Debian you might need to install a mkisofs replacement.

$apt-get install genisoimage $ln -s /usr/bin/genisoimage /usr/bin/mkisofs

builds fine after this.

------
NickWarner775
This simple game has gotten so much attention since it was released (and ten
taken down).

------
edpichler
First time I'm understanding Assembly easily, very good this source code.

------
nemasu
Yes! Enjoying the assembly love recently. Awesome work!

------
gangster_dave
I like his girlfriend implementation better:
[https://github.com/icebreaker/girlfriend](https://github.com/icebreaker/girlfriend)

------
3327
great work always looking forward to seeing amazing examples like this on HN.
Thanks you

------
carlsborg
Nice work, thanks for posting.

------
wwhhyy
just added this as a option to a fog server

------
fogleman
Mechanics seem... a little off.

