{"id":15027947,"url":"https://github.com/overv/mineassemble","last_synced_at":"2025-05-16T15:02:51.544Z","repository":{"id":8746109,"uuid":"10424213","full_name":"Overv/MineAssemble","owner":"Overv","description":"A tiny bootable Minecraft clone written partly in x86 assembly","archived":false,"fork":false,"pushed_at":"2023-09-11T09:31:31.000Z","size":111,"stargazers_count":1057,"open_issues_count":7,"forks_count":70,"subscribers_count":61,"default_branch":"master","last_synced_at":"2025-04-03T11:09:33.208Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Overv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2013-06-01T15:31:07.000Z","updated_at":"2025-04-01T05:27:36.000Z","dependencies_parsed_at":"2022-07-25T22:32:57.090Z","dependency_job_id":"cfcf0c07-a059-404e-8408-ba9e570ee322","html_url":"https://github.com/Overv/MineAssemble","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FMineAssemble","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FMineAssemble/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FMineAssemble/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FMineAssemble/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Overv","download_url":"https://codeload.github.com/Overv/MineAssemble/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248564848,"owners_count":21125412,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T20:07:20.425Z","updated_at":"2025-04-12T11:49:22.128Z","avatar_url":"https://github.com/Overv.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"MineAssemble\n============\n\n\u003cimg src=\"http://i.imgur.com/j3cD4ur.png\" /\u003e\u0026nbsp;\n\u003cimg src=\"http://i.imgur.com/OmRT52a.png\" /\u003e\n\n**[Prebuilt ISO](https://while.io/mineassemble.iso) - [Video](http://www.youtube.com/watch?v=HDcr5dnellM)**\n\nMineAssemble is a tiny bootable Minecraft clone written partly in x86 assembly.\nI made it first and foremost because a university assignment required me to\nimplement a game in assembly for a computer systems course. Because I had never\nimplemented anything more complex than a \"Hello World\" bootloader before, I\ndecided I wanted to learn about writing my own kernel code at the same time.\n\nNote that the goal of this project was **not** to write highly efficient\nhand-optimized assembly code, but rather to have fun and write code that\nbalances readability and speed. This is primarily accomplished by proper\ncommenting and consistent code structuring.\n\nStarting in assembly right away would be a bit too insane, so I first wrote a\nreference implementation in C using the SDL library, which can be found in the\n*reference* directory. I started writing it with the idea that if it was longer\nthan 150 statements excluding boilerplate, it wouldn't be worth doing it in\nassembly. Like all estimates in the world of programming, this limit turned out\nto be a gross underestimate, reaching about 134 lines before adding the texture\nor even the input code.\n\nAfter completing the reference code, I wrote the kernel boilerplate code\n(setting up VGA, interrupts, etc.) and changed the reference C code to work with\nthis. Then I began slowly porting everything to handwritten assembly.\n\nUnfortunately this turned out to be a lot more work than I expected, so\ncurrently a large fraction of the codebase is still in C. Slowly porting\neverything to assembly is an ongoing process. The code also isn't fully\ncompatible with all systems yet. It seems to cause floating point exceptions on\nsome setups.\n\nHow to play\n-----------\n\n### QEMU\n\nTo run the game with QEMU, simply run `make test` This is a quick and easy way\nto play around with it.\n\n### Virtual machine\n\nIf you want to use virtualization software like VirtualBox, you can produce an\n.iso image with `make iso` and mount it. The virtual machine doesn't need a hard\ndrive and requires no more than 4 MB of RAM.\n\nYou can also burn this image to a CD or DVD, but that is rather wasteful. Use\nthe USB stick method to try it on real hardware unless it really isn't an option\nfor some reason.\n\n### USB stick\n\nBooting from an USB stick is an excellent way to try it on real hardware, but\ndoes involve a little bit more work. Note that this process will remove all data\ncurrently on the USB stick. Also, make sure to get the drive name right or you\nmight accidentally wipe your hard drive!\n\n1. Format your USB stick to FAT32 with 1 MB free space preceding.\n2. Mount it using `mount /dev/sdx1 /mnt` where `sdx` is the drive name.\n3. Turn it into a GRUB rescue disk with `grub-install --no-floppy --root-directory=/mnt /dev/sdx`.\n4. Run `make iso` and copy the contents of the *iso* directory to the USB stick.\n5. Unmount with `umount -l /dev/sdx1`.\n\nNow reboot your PC and boot from USB.\n\n### Debugging with GDB and QEMU\n\n```\nqemu-system-i368 -gdb tcp::1234 -S -kernel mineassemble.elf\ni686-pc-elf-gdb mineassemble.elf\n(gdb) target remote :1234\n(gdb) continue\n```\n\nBuilding a cross compiler toolchain\n-----------------------------------\n\nIn order to build MineAssemble, you need to have a cross compiler toolchain\nconsisting of the [GNU Binutils](http://www.gnu.org/software/binutils/) and\n[GNU C Compiler](http://gcc.gnu.org/) (GCC).\nThe toolchain must be built for **i686-pc-elf** target.\nFor legacy reasons, GNU GCC and Binutils are configured at compile time and\nyou will need to compile them from source to create a cross compiler\ntoolchain.\nYou will also need the NASM Assembler.\n\nIn addition, you might need [QEMU](http://www.qemu.org/) for testing\nMineAssemble in an emulator and the\n[GNU Debugger](http://www.gnu.org/software/gdb/) (GDB) for debugging.\n\n### Installing prerequisites\n\nYou will need to install a handful of utility libraries to build Binutils,\nGCC, QEMU and GDB.\n\nUse your operating system's equivalent for apt-get or compile from source.\n\n#### Install [libmpc](http://www.multiprecision.org/), [libgmp](http://gmplib.org/), [libmpfr](http://www.mpfr.org/) (required for binutils, gcc and gdb)\n\n```\nsudo apt-get install libmpc-dev libgmp-dev libmpfr-dev\n```\n\n#### Install [flex](http://flex.sourceforge.net/) and [bison](http://www.gnu.org/software/bison/) (required for GCC)\n\n```\nsudo apt-get install flex bison\n```\n\n#### Install [libsdl](http://www.libsdl.org) (optional front end for qemu)\n\n```\nsudo apt-get install libsdl-dev\n```\n\n#### Install [NASM](http://www.nasm.us/)\n\nNASM does not need to be configured at compile time, you can install it using your package manager.\n\n```\nsudo apt-get install nasm\n```\n\n#### Create directories for source, build files and binaries\n\n```\nmkdir ~/src                 # Source code\nmkdir ~/i686-pc-elf-build   # Temporary build files\nmkdir ~/i686-pc-elf         # Toolchain install destination\n```\n\n#### Get source code for Binutils, GCC, QEMU and GDB\n\n```\ngit clone git://sourceware.org/git/binutils.git ~/src/binutils\ngit clone git://gcc.gnu.org/git/gcc.git ~/src/gcc\ngit clone git://git.qemu-project.org/qemu.git ~/src/qemu\ngit clone git://sourceware.org/git/gdb.git ~/src/gdb\n```\n\n#### Check out latest release versions\n\n```\ncd ~/src/binutils ; git checkout binutils-2_23_1\ncd ~/src/gcc ; git checkout gcc-4_8-branch\ncd ~/src/qemu ; git checkout v1.5.0\ncd ~/src/gdb ; git checkout gdb_7_6-branch\n```\n\n#### Build binutils for target i686-pc-elf\n\n```\nmkdir ~/i686-pc-elf-build/binutils ; cd ~/i686-pc-elf-build/binutils\n~/src/binutils/configure --prefix=$HOME/i686-pc-elf --target=i686-pc-elf --disable-shared --disable-nls\nmake -j 4  # parallel make for 4 cpus\nmake install\n```\n\n#### Build GCC (C compiler only) for target i686-pc-elf\n\n```\nmkdir ~/i686-pc-elf-build/gcc ; cd ~/i686-pc-elf-build/gcc\n~/src/gcc/configure --prefix=$HOME/i686-pc-elf --target=i686-pc-elf --enable-languages=c --disable-shared --disable-nls\nmake -j 4 all-gcc\nmake install-gcc\n```\n\n#### Build qemu with i386-softmmu target (with SDL front end)\n\n```\nmkdir ~/i686-pc-elf-build/qemu ; cd ~/i686-pc-elf-build/qemu\n~/src/qemu/configure --prefix=$HOME/i686-pc-elf --target-list=i386-softmmu --enable-sdl\nmake -j 4\nmake install\n```\n\n#### Build GDB for i686-pc-elf target\n\n```\nmkdir ~/i686-pc-elf-build/gdb ; cd ~/i686-pc-elf-build/gdb\n~/src/gdb/configure --prefix=$HOME/i686-pc-elf --target=i686-pc-elf\nmake -j 4\nmake install\n```\n\n#### Add toolchain to $PATH\n\n```\nexport PATH=$HOME/i686-pc-elf/bin:$PATH\n```\n\n\nStyle conventions\n-----------------\n\nWith something as low-level as assembly, you quickly risk writing unreadable\ncode if you don't have proper style conventions. This project uses the following\nidentifier name conventions:\n\n    WORLD_SX - C-style define, usually allowing you to configure things\n    worldSX - Non-local variable (local vars are referred to by stack offsets)\n    init_world - Subroutine (uses underscores instead of camelCase)\n    .main_loop - Local label, only used from within subroutine\n\nVariable names carry no type prefix, because it is almost always very clear what\ntype a variable uses from its name or usage. Here are some examples:\n\n    float - vectors, angles, distance\n    int - time, block coordinates, block normal\n    byte - palette color, block type\n\nDirectives that apply to segments of a file or the whole file, such as\n\n    [bits 32]\n    section .text\n\nhave no indentation. Subroutines and local labels have one level of indentation\nand code or data within have two levels of indentation. One level of indentation\nis equal to 4 spaces.\n\nCode is commonly separated in blocks with a comment above describing what is\ndone in the block. If a line requires extra explanation, a comment is placed\nafter the instruction.\n\nFloating point math expressions are systematically converted from the reference\ninfix expression to RPN (Reverse Polish Notation) to FPU instructions. Any\noptimizations are applied afterwards if deemed necessary. This systematic\napproach makes converting single-line C expressions to dozens of assembly\ninstructions bearable and relatively error-free.\n\nExplanation\n-----------\n\nThe inner workings of this demo are really quite straight-forward. The code can\nbe divided into four different components.\n\n### World\n\nThe world is stored as an *unsigned byte* array where every block has a value of\neither `BLOCK_AIR` or `BLOCK_DIRT`. The array is stored in the BSS section and\nis initialized by the `init_world` function. It loops over every x, y and z and\ncreates a world where the lower half is dirt and the upper half is air.\n\nWhile playing, other code calls `set_block` or `get_block` to interact with\nthe world. These simply calculate the correct index and write to or read from\nthe array.\n\n### Input and collision\n\nKeyboard input is collected by an IRQ1 interrupt handler. It writes the up/down\nstate to a 128-byte array indexed by the [scan code](http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html).\nIt also sets the upper bit to `1` to mark that key as updated. It ignores a key\ndown event if the key is already set to down to ignore automatic key repeats.\n\nBecause the input handling needs to be independent of performance, an IRQ0\ninterrupt handler increases a `time` variable by 1 every millisecond to keep\ntrack of time. This is used to compute a delta time to scale movement by.\n\nBefore every frame is rendered, the `handle_input` function is called and\ncollects the values from the `keys` array written to by the interrupt handler.\nIf the upper bit of a cell is set to `1`, then it knows that the key state has\nchanged and processes it accordingly. All keys except for the movement keys\n(AWSD) are handled here.\n\nAfter that, the update function is called to move the player according to the\ncurrent velocity. This velocity is controlled partly by the `handle_input`\nfunction and partly by checking the down state of the AWSD keys in this\nfunction. The Y velocity is decreased to simulate gravity. Then the next player\nposition is determined by adding the velocity multiplied by delta time.\n\nBefore the new position is assigned, the code first runs the `handle_collision`\nfunction for the head, center of the body and feet. It calls the raytrace\nfunction from these positions with the velocity as direction to determine if\na collision will occur if the player moves to the new position. If that is the\ncase, the velocity is corrected to *mostly* prevent collision. (The algorithm\nis not perfect, but it works pretty well.)\n\n### Rendering\n\nNormally games use rasterization to render and this is very fast. Unfortunately\na graphics library like OpenGL is not available at this level. Instead, code\nneeds to be written that writes directly to the graphics memory. At this point,\nI had two choices: write my own rasterizer or implement a raytracer. I decided\nto go with raytracing, because:\n\n- It's much more straight-forward by simply computing the color per pixel\n- It's cool, because it allows for easy effects like raytraced shadows\n- It's *fast enough*, because we have a uniform 3D grid\n\nThe raytrace algorithm computes the distance to reach the sides of the block\nthe ray starts in for every dimension. The shortest distance wins and the ray\nposition is moved by that distance times the ray direction. This is repeated\nuntil the position is inside a `BLOCK_DIRT` or if it's out of the world. The\nfinal position is used to compute the side that was hit and the texture\ncoordinates. The `ray_color` function is then called to let the block decide\nwhat color it's going to output. This function calls the `raytrace` function\nagain to decide whether the pixel is shadowed or not by using the `sunDir`\ndirection for the ray. It prevents infinite recursion by requesting an *info*\nraytrace instead of a color raytrace. This alternative returns a struct with\nhit info instead of a color.\n\n### Resources\n\nOne of the details you deal with when using a low-level VGA mode (mode `0x13`)\nis that you can't just specify 24-bit or 32-bit RGB color for every pixel.\nInstead, you have to decide on a 256 color palette and specify an index for\nevery pixel. The easy solution here is to use 3-2-3 bit channels and use an RGB\ncolor as index into the palette. Unfortunately this doesn't work at all, because\nwith only 4 options for the green color channel, there's no way to represent all\nthe subtle different shades of a grass block.\n\nSo I decided to generate a palette that could represent every color that the\ntextures used exactly, well almost exactly. The palette does allow you to\nspecify RGB colors, but with only 6 bits per channel instead of 8. That means\nthat colors will be slightly off, but this is pretty much unnoticeable.\n\nI wrote a program in C# that took the grass, dirt and side textures along with\nthe reserved colors black, white and sky and automatically generated a palette\nand a palette colored representation of the three textures. This ended up\nworking perfectly!\n\nThe splash screen works slightly differently. The reason that it's a bitmap\ninstead of just using text mode is to make things a bit more streamlined. I\nfirst tried encoding it the same way as the textures, but this resulted in a\n6400 line C file. Then I changed it to simply write a string of 1's and 0's for\nevery line, which works much better. It even allows you to view the splash\nscreen using a text editor! :-)\n\nThe bitmap of the splash screen is copied directly to VGA memory where `'0'` is\nsubtracted from every byte. The `keys` array is then checked for an ENTER key\npress before the game is loaded. A problem here is that the user has to press\nENTER in the GRUB bootloader menu as well, which means it would skip the splash\nscreen immediately. That problem is currently solved by waiting for an ENTER key\npress twice. Somehow this even works when booting with Ctrl-X or another\ncombination.\n\nLicense\n-------\n\nThis project is licensed under the MIT license.\n\nSome derived work with compatible licensing is also included:\n\n- **init.asm**, **interrupts.asm** - Derived from [code by Maarten de Vries and Maurice Bos](https://github.com/m-ou-se/bootlib) (licensed under the MIT license)\n- **vga.asm** - Derived from [code by Christoffer Bubach](http://bos.asmhackers.net/docs/vga_without_bios/snippet_5/vga.php) (public domain)\n\nDerived work here means that the code was adapted to fit the requirements of this project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverv%2Fmineassemble","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foverv%2Fmineassemble","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverv%2Fmineassemble/lists"}