10 ms·
I wrote a Game Boy Advance game in Zig
- actionfromafar 2y agoFor the upper right quadrant, something like a Silicon Graphics might fit.
- erik 2y agoUpper right quadrant would be an 8-bit (or maybe 16-bit) CPU paired with a 3D graphics capable GPU. SNES + the SuperFX chip is the closest example I can think of. Similarly, there is the Genesis/Mega Drive + SVP chip. I can't name a fully 8-bit machine with a 3d focused graphics chip. Maybe there are arcade boards?
- coolsunglasses 2y agoMega Drive is probably earliest that was capable of it really, but Namco System 21 and especially Sega Model 1 (1990) were designed with 3d/polygons in mind but have relatively old chips in them. Programming for those things could not have been easy.
- wk_end 2y agoIt's stretching the definition of "3D-focused graphics chip", but an early example might be I, Robot. An 8-bit 6809 CPU drives a custom polygon-pushing graphics processor. It's primitive but must've been mind-blowing in 1984. https://en.wikipedia.org/wiki/I,_Robot_(video_game) https://en.wikipedia.org/wiki/I,_Robot_(video_game)
- lynguist 2y agoN64 is actually Silicon Graphics and it’s upper left.
- phire 2y agoYou mean the early systems like the IRIS 2000? It arguably fits, because it pairs a 68010 with hardware accelerated 3d graphics. As far as I can tell, it's not quite modern 3d graphics. If you paid for the geometry engine, you did get hardware accelerated transform and clipping. And the framebuffer supported hardware acceleration of 2d polygon. But if you wanted texture mapping, that was all done on the CPU. I don't think we can consider SGI as having "modern" 3d accelerated graphics until they implemented support for hardware accelerated texturing with the Reality Engine in 1992, long after they switched to MIPS.
- aquova 2y agoThis is really interesting. As an aside, someone has created a GBA toolchain for the Nim language as well. It's really cool seeing these projects for a variety of languages. https://github.com/exelotl/natu https://github.com/exelotl/natu
- archargelod 2y agoNatu has an amazing commercial game called Goodboy Galaxy [0]. In addition to the Game Boy Advance version, they've also ported it to Windows and Linux with Xatu [1] (which is also written in Nim). 0 - https://goodboygalaxy.com/ https://goodboygalaxy.com/ 1 - https://git.sr.ht/~exelotl/xatu https://git.sr.ht/~exelotl/xatu
- girvo 2y agoAnd interestingly, one of the problems raised in the article __attribute__((target("arm"))) void interrupt_handler() { ... } that fancy __attribute output can be achieved quite nicely using a custom pragma/codegendecl -- we did exactly this for the embedded firmware written in Nim at my previous workplace, to get access to some specific features of the ESP32-S3 (things like RTC_ATTR_NOINIT for example)
- keyle 2y agoInteresting post. I wish it went deeper but I guess it's just a post-mortem. Related, I found this old post that goes into the basics of GBA coding [1]. It doesn't look nearly as bad as I expected. That said I'm sure performance might be the issue once you start having a few things going. If you have a good resource please let me know (I prefer C). Edit: also this library [2]. [1] http://www.loirak.com/gameboy/gbatutor.php http://www.loirak.com/gameboy/gbatutor.php [2] https://www.coranac.com/tonc/text/toc.htm https://www.coranac.com/tonc/text/toc.htm
- discard38484 2y agoCheck out https://gbadev.net https://gbadev.net
- dijit 2y ago> I started using Linux five years ago, mainly because I couldn’t figure out how to get Python working on Windows Windows being by far the most dominant operating system from 97-2015 definitely exacerbated a dearth in knowledge among the young. In the early days internet access was not ubiquitous and shipping an operating system without even a primitive programming environment definitely lead to missed years of programming for me. I learned a lot about windows, lots of UIs and how the system was constructed somewhat, but no visual studio, no functioning compiler in base and the only interpreter being fucking batchfiles with no docs… come on…
- ashleyn 2y agowindows 95 did come with Qbasic for MS-DOS. it wasn't the latest and greatest, but it did contribute to a moderately large online scene of kids making Qbasic games or Windows-like Qbasic GUIs for DOS.
- bigfishrunning 2y agoThat's where I got started, copying basic code from the back of Boy's Life magazine, before I had a modem. I'm sure I'm not the only one here!
- binary132 2y agoI learned BASIC from those Usborne books on it, muddling my way through with qbasic. The PDFs of them are actually available on their site now!
- vrighter 2y agoholy shit, thanks for the tip! I also learned programming from those usborne books, using qbasic (The dragon32 ran a version of microsoft basic, so the code for it was generally quite compatible with qbasic). I'll go download them all for archiving.
- 2y ago
- oxidant 2y agoThis was an interesting read, thanks. One nit on mobile (Firefox and Chrome, Android), your code blocks overflow the background instead of scrolling.
- yapyap 2y agoin safari as well
- vanderZwan 2y agoRegarding the nit: this seems to be so common these days that I'm wondering if it's a bug in a default CSS theme in a popular static site generator or something
- jonot 2y agoshould be fixed now
- dropbear3 2y ago> How would you do [packed structs] in C? Bitfields! This is valid C: struct foo { unsigned bg_priority: 2; unsigned character_base: 2; // ... };
- purple-leafy 2y agoBut C/ C-compilers don't guarantee your struct wont have holes (by default), so you may have to do something like __attribute__((packed)) to ensure they are packed structs: struct bitmap_file_header { UWord signature; UDWord file_size; UWord reserved_1; UWord reserved_2; UDWord file_offset_to_pixel_array; } __attribute__((packed));
- travisgriggs 2y agoThere’s also a directive (don’t have code in front of me) that you can do at file level that will cause all subsequent struct defs to be packed… #push(pragma(pack(0)) ?? I’ve done a lot of direct register access in C this way. I do like Zigs ability to just define the different sizes though.
- mananaysiempre 2y agoIt’s MS(V)C syntax, now supported by GCC and Clang as well: #pragma pack(push) #pragma pack(1) /* ... */ #pragma pack(pop) The first two lines can also be condensed into #pragma pack(push, 1)
- dropbear3 2y agoThis is not true of adjacent bitfields, at least for C99: An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf 6.7.2.1 § 10
- mananaysiempre 2y ago
- MobiusHorizons 2y ago> Sometimes, the compiler would get too smart for its own good, and recognize that a function that I wrote just copies memory. It would helpfully replace the function body with memcpy to save space. I would generally expect this optimization to only be done for programs executing in userspace, not os / kernel style programs like this is. I would have expected the equivalent of -nostdlib to handle this automatically. I wonder if the author's build could be tweaked to tell llvm (which zig is based on) not to perform this or other "default to stdlib implementation" style optimizations
- pcwalton 2y agoThis is controllable by customizing TargetLibraryInfo [1] for your target in LLVM. Note, however, that detecting memcpy is always a valid optimization in C, not just in userspace. The C language standard, not POSIX, requires that a function spelled "memcpy" be available. As a side note, the optimization pass that detects and replaces memcpy is called LoopIdiomRecognize [2]. It actually detects quite a bit, not just memcpy. [1]: https://llvm.org/doxygen/TargetLibraryInfo_8cpp_source.html https://llvm.org/doxygen/TargetLibraryInfo_8cpp_source.html [2]: https://llvm.org/doxygen/LoopIdiomRecognize_8cpp.html https://llvm.org/doxygen/LoopIdiomRecognize_8cpp.html
- MobiusHorizons 2y agoVery Interesting! I guess I would still expect the optimization to use the library implementation of memcpy to be invalid when interacting with memory mapped IO. In this case the issue was the size of writes being issued (byte vs word), but you can easily imagine other issues that would not matter something like memcpy. I would have thought volatile might have signaled this, even if the issue here is not related to values being cached in registers. Does zig have a version of volatile?
- deleted 2y ago[deleted]
- deleted 2y ago[deleted]
- klooney 2y ago> Did you know that you can’t write to video memory on the Game Boy Advance in units of 8 bits, and if you do, it’ll still work but all your graphics will be messed up with no obvious way to figure it out unless you read one specific paragraph of the documentation? Hey did you know that when you compile a binary optimized for size, memory copies will be by units of 8 bits, but not if you use debug mode? Brutal. I don't miss but banging much.
- 1propionyl 2y ago[flagged]
- vanderZwan 2y agoI imagine it must be a real (occasionally Freudian) pain in the ass
- kookamamie 2y agoYou either missed a 't' or an 'i'.
- klooney 2y agoOh good grief
- sitzkrieg 2y agoill take bit banging over retooling to a worse pile of software written by clueless idiots every couple of years (muh clout) its crazy how nice knowing 100% of whats going on is
- zzo38computer 2y ago> Did you know that you can’t write to video memory on the Game Boy Advance in units of 8 bits, and if you do, it’ll still work but all your graphics will be messed up with no obvious way to figure it out unless you read one specific paragraph of the documentation? I did. At one time I did write a program that wrote to video memory in units of 8 bits, but the emulator I was using did not prevent that, so my program worked on the emulator but not on the real hardware, and I could not figure it out until I found out that was the problem and I managed to fix my program so that it would work.
- yard2010 2y agoHow did you find the cause of it?
- urronglol 2y ago[dead]
- bdhcuidbebe 2y agocould it be… reading the docs?
- bmacho 2y agoAsk IRC/Stackoverflow/Reddit/ChatGPT?
- AndyKelley 2y ago> it would be nice if there was some way to specify how memory in certain address ranges need to be addressed. If making the respective loads and stores volatile doesn't solve this problem please feel free to file a bug against the compiler. https://ziglang.org/documentation/0.13.0/#volatile https://ziglang.org/documentation/0.13.0/#volatile
- ant6n 2y agoOh look, small world!
- AndyKelley 2y agoHowdy!
- underdeserver 2y ago> Did you know that you can’t write to video memory on the Game Boy Advance in units of 8 bits, and if you do, it’ll still work but all your graphics will be messed up with no obvious way to figure it out unless you read one specific paragraph of the documentation? Hey did you know that when you compile a binary optimized for size, memory copies will be by units of 8 bits, but not if you use debug mode? Andrew (or any Zig mavens) - What would be the idiomatic way of solving this issue? Can you define your own memcpy with @std.mem.doNotOptimizeAway or something?
- MatthiasPortzel 2y agoAndrew’s point is that marking the memory as volatile should prevent this optimization. (i.e. the compiler isn’t allowed to split a 16-bit volatile write into two 8-but writes.) This is idiomatic because volatile is intended for MMIO. There’s a proposal for a way to disable LLVM generating builtin calls like memcpy. But I’d argue volatile would still be more appropriate in this case. https://github.com/ziglang/zig/issues/22110 https://github.com/ziglang/zig/issues/22110 Edit: this comment from later in the thread is also relevant: https://news.ycombinator.com/item?id=42556624 https://news.ycombinator.com/item?id=42556624
- __loam 2y agoExtremely excited to read through this. I'm fascinated by Zig and the GBA. It's really cool to see people still developing software on such an old machine.
- mysecretaccount 2y agoWhy is the DS in the "Non-modern graphics" row? It does not use a tile based renderer.
- msephton 2y agoDS has both 2D tile-based renderer and 3D renderer. You can find many examples of games with tiled graphics. I made a tile map viewer for my favourite DS game https://blog.gingerbeardman.com/2021/04/21/level-viewer-for- https://blog.gingerbeardman.com/2021/04/21/level-viewer-for-...
- msephton 2y agoComplete link https://blog.gingerbeardman.com/2021/04/21/level-viewer-for-pang-nintendo-ds/ https://blog.gingerbeardman.com/2021/04/21/level-viewer-for-...
- mysecretaccount 2y agoInteresting, thank you!
- Dwedit 2y agoWhile the GBA does have decompression routines in its BIOS, APLIB does a far better job with compression. I made a fast APLIB depacker for GBA.
- bru3s 2y ago[flagged]
- Validark 2y ago> Unfortunately, it only has support for one output. Some of the BIOS functions on the GBA that you might use inline assembly for output multiple values in different registers, which is problematic for Zig. This is currently being worked on so it might be improved in the future. Can you post the code that's impossible to write properly?
- Validark 2y ago> As it would turn out, the Game Boy Advance has quite a bit of “weird memory” that you have to work around in weird ways. I know that this is a long shot, but it would be nice if there was some way to specify how memory in certain address ranges need to be addressed. Could you elaborate on this? Does LLVM support this feature? If so, it might not be too hard to get it into Zig.
- NobodyNada 2y agoI'm not a compiler dev and I don't know Zig, but I can think of a couple of things that make this tricky: - The compiler doesn't typically know anything about memory address ranges; that's the linker's business. - Even if you taught the compiler about memory address ranges, the compiler often won't know what address ranges a pointer could point to at runtime. For instance, suppose you declare a function that takes a pointer as its parameter; and then maybe elsewhere in your code you call it with pointers to various address ranges. What's the compiler supposed to do, check the pointer value at runtime and branch based on its address range? That's certainly not good for performance or code size. So we clearly need some way to "color" the pointer type itself as belonging to a specific address range -- a function needs to be able to declare what color of memory it wants to operate on, and it should be an error to pass a wrong-colored pointer to a function. Except you can already do this in programming languages today! Create a wrapper type representing a pointer to a specific type of memory (something like 'struct VramPtr(*mut u32)' in Rust), and define read and write functions that issue volatile writes of the correct type (or even inline assembly if you need some specific instruction that volatile doesn't guarantee).
- latexr 2y ago> You can try the game out in an emulator <a href="">here</a>. You left out the most important part of the post.
- kristianp 2y agoThey must have fixed the link, it points to a page with a .gba file now. I would have liked it to point to a page that loaded a js/wasm emulator with the playable game in it.
- anta40 2y agoHow to build this? I'm on M2 Mac with 2 Zigs installed: 0.13.0 and 0.14.0-dev.2589+0df1f3df2 Both gave the same error: run gbaimg (tiles.zig) transitive failure │ │ └─ zig build-exe gbaimg Debug native 1 errors