Show HN: NESFab – Programming language for making NES games (pubby.games)
This is a long-running personal project I've had to write an optimizing compiler from scratch. Everything was done by me, including the lexer/parser, SSA-based IR, high-performance data structures, and code generator.
Originally I wasn't targeting the NES. It started as a scripting language, then it morphed into a C++ replacement, and then finally I turned it into what it is today. The large scope of the project and colorful history means it's still a little rough around the edges, but it's now working well enough to post.
97 comments
[ 2.7 ms ] story [ 206 ms ] threadI don’t think any company in 2023 is going to sponsor port of a 6502 compiler, unfortunately.
https://llvm.org/devmtg/2022-05/slides/2022EuroLLVM-LLVM-MOS...
I know this conversation has probably been had many times before. But if I prefer a 2 space tab, and you prefer a 4 space tab, we both get them how we want them with tabs.
With spaces, I have to put up with your stupidly wide indents, and try not to scratch my eyes out from the pain.
https://www.google.com/search?q=spaces+vs+tabs+site%3Anews.y...
What people do locally to pre-emptively comply with the pull-requests rules is up to them, like running the auto-formatter before they commit their code, or running the test suite before making a pull-request.
However: some reasonable people also like to use auto-formatters.
If you want to avoid a lot of noise when looking at diffs eg for your code review, you need to deal with those.
The most obvious problem is two people with differently set-up auto-formatters.
But you also get a problem when you mix people hand-formatting vs the auto-formatter. The first time the auto-formatting developer touches a file, you have a massive diff.
(With lots of discipline, they can stick the formatting into a separate commit or even PR, but people seldom do that.)
All that being said, I'm a very selective stickler for whitespace formatting. I like my source to be neat, and my diffs to be easy to review. I'd like people to avoid mixing tabs and spaces, avoid trailing spaces at the end of lines, and end their last line in a file with a newline.
> Brilliant people perhaps, but they better work on their own.
I have the opposite experience: people who don't cross their Ts nor dot their Is are also those who forget other niceties like freeing their malloced memory.
I approach coding from the point of view as trying to make the reviewer's life as easy as possible. So not just 'how do I get the computer to do X', but 'how do I make this so easy to understand that a reviewer has a chance of spotting any mistakes I make' (also so that reviewers and other readers of the code can take over, so I can take a vacation or move on to other things).
I personally would go for tabs. That seems 'obvious' to me. The author went a different route and I'm interested in their reasoning.
As you say, it's a personal project they're giving away. My question was one of why they went in a particular direction, rather than saying that direction is wrong.
https://www.nesdev.org/wiki/APU#Registers https://github.com/cc65/cc65/blob/master/asminc/nes.inc#L44
Or do you think all NES developers can agree on the same names?
[0] https://atariwiki.org/wiki/attach/Atari%20Macro%20Assembler/...
Now, imagine you've been playing the same game for a couple days in a row. And you're getting kinda sick of it. Not that it's bad, it's got its good moments. You've just had enough of it, for now. Would you continue playing it?
The answer is (hopefully): Of course not! You'd move on to something else you enjoy more. If the game was truly engaging, you'll come back to it anyways, maybe a few days/weeks/months later, when it captures your interest again.
Since a few years this is my attitude towards side projects. The flipside is that you have to accept the fact that 99% of the things you start will never get finished. But that's ok, as long as you enjoy the work itself. Of course that doesn't mean there aren't difficult moments. But there has to be a genuine, intrinsic motivation, which is independent of shipping something.
Keeping a journal/todo-list for every project (I use an infinite bulleted list like Dynalist) really helps, since you can just come back to a project anytime by looking at the end of the list.
I don't always know the best way to do something, but so long as I know some way to do it, I can make progress. Tough bugs are just a matter of perseverance. And hey, if it's still not working, just drop the feature. In a personal project, you can do that and nobody can stop you.
One more thing: don't talk about your projects until they're 95% done. Seeking early validation is a poison pill.
> I don't always know the best way to do something, but so long as I know some way to do it, I can make progress.
This helps a lot. Thanks! One of my issues is being able to be ok with feature gaps or bugs. I can solve them if they were created by me, but there are some that need outside help as they are likely bugs in external software or even drivers. But maybe it makes sense to press on and revisit later.
> One more thing: don't talk about your projects until they're 95% done. Seeking early validation is a poison pill.
Is this because you draw attention to an unfinished project, get distracted by feature requests or praise, or something else?
> Is this because you draw attention to an unfinished project, get distracted by feature requests or praise, or something else?
If you get a bad reaction, you'll be demotivated. If you get a good reaction, you'll feel satisfied and not have the drive to keep working. Either that, or you'll devote more and more time to posting on social media, getting less work done in the process.
This is so important, and not only for sw dev but anything.
Anyone who is struggling with completing personal projects, try starting the next one with the explicit goal to not speak about it - with anyone - until it's either complete, or very near. It may be hard to do at first but ultimately worth it.
Lately, I seem to manage 1-3 days of coding on my hobby project after work, provided I have some features where I can see meaningful success within 90 minutes of coding (+ the same time for debugging or researching new libraries, if needed)
Regarding time, I have thinking periods and coding periods. On thinking periods I'll take a 30-60 minute walks and figure things out in my head. On coding periods, I'll spend 1-2 hours in the text editor per day, sometimes more, at whatever time suits me. It's pretty laid back and I don't beat myself up if I stop.
I would encourage you to ask here on HN and maybe also over at reddit (i.e. https://old.reddit.com/r/cscareerquestions) for feedback on landing a software dev job. Getting the foot in the door can be tough, after that it get easier. I wish you best of success in landing a job! :)
Edit: It might be a good idea to mention your current job search in your most visible comments here in this thread. Also, you could mention it on your website! ;)
The key is to have a project you know is worthwhile and will tickle your curiosity for long periods of time... So even if you burn out, you come back to it.
I go through intense weeks of productive work followed, sometimes, by months of dust covers.
Also it's important to understand that creative energy is hormonal. Some weeks you can't look at a youtube video because you have to do X; some weeks you can't open a code editor if you're not paid for it and just want to netflix and chill. It's normal.
But if the problem at hand is worth your while, if it's interesting to you personally, you'll go back to it and resume intense focus work; maybe with a different approach.
If you totally gave up and you don't want to look at it anymore, it's probably because your inner-senses tell you it's not worthwhile. So you might need some re-convincing or maybe it's just a dead fish, move on. Money is a great motivator, so when the work is unpaid, the value has to come from somewhere else, and that's usually the importance of the project to you personally.
It seems this is a theme in the replies, and it's refreshing to hear because it matches my experience. But it's nice to hear since I was worried this was abnormal.
But seeing that you want to be spitting out optimized 6502 machine code, it's not really useful to do this as a library for most host languages.
Nice work.
Anyway, this looks dope. Truthfully, we could use more hyper-specific languages like this in all sorts of areas.
I plan on writing an article on how my own code generator works. If you check my HN profile in a week or two you'll probably see it submitted.
The "Greedy register allocator" in LLVM is a just finely tuned priority allocator with nice live-range splitting. It works great for zero page cache locations, but it's just not tuned very well for tight register classes like those involving the processor's three architectural registers. I've half a mind to implement Hack's SSA-based register allocator in LLVM to use on A, X, and Y; this would clean up the oodles of spurious copies LLVM-MOS spits out in the worst cases.
For example, let's say I have a short snippet of code that needs to run every H blanking period. I should just declare something like
@every_hblank Blabla_code()
Or scheduling some code for X pixels into a line.
Actual scheduling can be done however the compiler wants to, using interrupts or inserting dummy instructions for delays.
I guess in the end it's a constraint problem to solve where all these snippets fit in.
I imagine this makes it easier to write games/special effects as you remove the tricky cycle counting. Easier, but maybe less fun...
You just do:
Where "foo" and "bar" are the functions you want to be called during hblank and vblank. Here's somebody showing how to pull off some scaling effects using them: https://under-prog.ru/en/sgdk-image-resize/A lot of recent commercial Genesis/MD releases have been made with SGDK; performance seems quite nice. After all, theoretically, if your libraries go hard enough and lean on macros and/or inline assembler C doesn't need to carry a big speed penalty vs. assembler.
I think this is also made possible by the Genesis/Megadrive hardware throwing interrupts during hblank/vblank but don't quote me on that.
Of course, the really insane effects still require cycle counting. My understanding is that the Overdrive 2 demo uses heaps of it. It's strictly 50hz only because it's that tightly coupled to various specific timings (also obviously 50hz gives you room for a few more instructions during blanking intervals vs. 60hz)
https://www.youtube.com/watch?v=OeGdJk5zb6c
Detected: Trojan:Script/Wacatac.H!ml
Status: Removed
Details: This program is dangerous and executes commands from an attacker.
Killer feature, but is there a cost to bank switching? Could abstracting it result in a lot of performance cost?
- What languages did you use as inspiration?
- Is it possible to write a game to a physical cartridge and play it on a real snes?
Thanks for sharing your amazing work!