This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

nel - Another 6502 compiler for the NES.

nel - Another 6502 compiler for the NES.
by on (#75184)
Hey there, my name's Overkill. I've been lurking NESdev for a bit, hanging out on the IRC, and making a homebrew project.

I think a few people on #nesdev may have heard about my assembler sideproject already, but I've recently made enough progress on it that I think it's probably okay to release.

One day, I was developing my NES project, and I got kind of annoyed with NESASM and some other 6502 assemblers. (NESHLA looked interesting, I suppose, but I wasn't really convinced about it -- it really wasn't that "high-level" in my opinion, anyway). I eventually wrote my own 6502 compiler for writing things on the NES. It was originally a written using C++, flex and bison, but I got fed up with that, so I rewrote that as a recursive descent compiler in D.


I call this project "nel".

It's a low-level assembler, but one that uses a cleaner syntax (in my biased opinion) than the standard 6502 mnemonics. It's an MIT-licensed project, and its source is hosted on GitHub.

The reason I'm releasing this is because I figure if I don't finish the game, I've at least released the tool that I developed in order to develop the game more comfortably. And I figure someone out there might find it handy if they want to do homebrew and want an alternative assembler.

At some point, I want to consider adding if and while statements (edit: done), assembly macros, and possibly allow automatically converting relative branches into absolute jumps when they exceed -128..127 bytes, but these are kind of low on the list. For now, I've been fine without any of these things.

Download nel 0.2 for Windows here.

The source is written in D, and should be compilable on other platforms. Precompiled builds for other systems could easily done with Digital Mars D2, but for Linux systems, it's probably easier to just give source. And I don't own a Mac, so I can't really precompile for that. Nonetheless, there is a Windows exe.

The manual, with detailed language descriptions, program usage, and other stuff.
The source on GitHub, for compiling yourself or just for viewing out of interest's sake.

Your feedback is appreciated.

by on (#75187)
Brings back horrific memories of basic.

by on (#75188)
Overkill was an appropriate handle to choose, then, since you're announcing yet another assembler as your first project :D I'm only teasing, as I understand the impulse to create one's own tools due to being frustrated with certain specific, individually desired features being lacking in available ones. The debate about abstraction versus low level efficiency and control will probably continue until we're all dead. :D

by on (#75190)
Hello Overkil,

Welcome to the forums. I like to write my own tools too, but I've not written a new assembler macro language. I find ca65 just fine for my needs. To each his own.

Guys (and our one gal),

I've seen Overkil's games (PC version, both of them). They are really cool. Uses an entirely new graphics aesthetic for NES games. I really hope that Overkil finishes it. I would definitely buy one for myself and one for my mother in law (she loves NES games too).

by on (#75195)
I've read part of the way through the manual, and here are my comments:

ines: directive

Do you plan on supporting NES 2.0 extensions?

rom bank b, loc:

I see you assume 8192 byte banks, just like NESASM. Does your assembler share NESASM's limitation of prohibiting code or data that crosses 8 KiB bank boundaries, for mappers such as 1, 2, 7, etc. that switch in other than 8 KiB units?

include

If you're going for an include model rather than a compile/link model, why not handle mutually recursive includes in the same way that e.g. PHP handles directives like require_once()?

let

Why use 'let'? Why not just the following?
Code:
ppu_ctrl = 0x2000

var

What made you decide to call 2 bytes a "word"?

"Hello World!\0"

Some games store strings in a custom character encoding to simplify translation of code units into tile numbers. Do you plan to allow the use of "tables" that describe custom character encodings?

by on (#75201)
I am too lazy to read the manual at the moment (the non-assembly codes hurt my brain already but people unfamiliar with assembly may find this easier to handle) and I tried to assemble the test rom but failed, with some garbage error message:
Image

I'm not sure whether it's the problem with my system though.

by on (#75203)
Hey guys, thanks for the replies. I'll try and answer them in time. Prepare for a large post.

ManicGenius: I was inspired by Pascal and Python (although unlike Python, no indentation rules, or any high-levelness) to an extent, and some of it was just "creative decision". I can see how it might give you nightmares though.

Gradualore:

Hey Gradualore, nice to meet you.

I think I agree that the debate on abstraction/efficiency will never die. For this, I decided that for the sake of efficiency, you can't really abstract too much on the NES hardware and expect a compiler to do a good job of optimizing. At least, probably not as good a job as human-written assembly.

Or it can be done, but probably not to a great extent with a language like C on the 6502. This is because C tends to make a lot of assumptions that make me think that minimum, a 16-bit CPU and more stack and general RAM space is needed.

Clueless: Thanks for prodding me to post on the forums, and for the introduction. I have other games than just Molasses Meow on my website though!

Tepples: Thank you for the comments! I'll try and address them below.

tepples wrote:
ines: directive

Do you plan on supporting NES 2.0 extensions?


Currently, it doesn't support NES 2.0, but that would be a nice thing to have.

tepples wrote:
rom bank b, loc:

I see you assume 8192 byte banks, just like NESASM. Does your assembler share NESASM's limitation of prohibiting code or data that crosses 8 KiB bank boundaries, for mappers such as 1, 2, 7, etc. that switch in other than 8 KiB units?


It was just a starting point. I definitely could see this changing though.

I originally wrote this for working with MMC3 (mapper 4) ROMs, but I can see why other boundaries would be useful. This was definitely ripped off of NESASM (probably a bad thing). It currently prohibits overflow, but I could make it so that bank sizes could be customizable. For that matter, I could probably separate prg and chr rom, so that banks are easier to deal with.

I agree that bank-switching should at least be accomodated. Possibly I could even allow bank overflow to wrap to the next bank as an option, rather than requiring fixed boundaries -- this seems a bit dangerous though, at least for PRG ROM.

tepples wrote:
include

If you're going for an include model rather than a compile/link model, why not handle mutually recursive includes in the same way that e.g. PHP handles directives like require_once()?


I suppose I hadn't really considered that, but it could work. It just prevents you from including the same file twice, which might be useful for data-like source files with labels in them. So for now, it just has a depth limit on includes to prevent cycles.

Perhaps this isn't required though, and I should just limit includes to once.

tepples wrote:
let

Why use 'let'? Why not just the following?
Code:
ppu_ctrl = 0x2000


Hmm. I agree that I could go without it, and for that matter, labels don't necessarily need a "def" prefix, so call it a weird preference. This allows me to expand the language later with other "=" use later. I dunno.

tepple wrote:
var

What made you decide to call 2 bytes a "word"?


I guess it was lack of good names for 2-byte values. Short and int didn't quite seem right. I was considering "pair", "dual" or "wide". "Word" is definitely a misnomer (since the 6502 word size is 1 byte), but I've seen lots of ".word" and ".dw" in assemblers, for 16-bit values, so I settled on that, even though it annoys me.

Quote:
"Hello World!\0"

Some games store strings in a custom character encoding to simplify translation of code units into tile numbers. Do you plan to allow the use of "tables" that describe custom character encodings?


Hm. This is something I hadn't considered! But I can definitely see the usefulness, even for strings describing other kinds data besides tiles. Then you can encode a set of literal bytes into a much smaller string of characters in code.

I could probably make it possible to define character mapping types or something to help to that effect. You could define a conversion table, maybe with something like
Code:
let mapping = { 'A': 1, 'B': 3, 'C': 6, 'D': 5, 'Z': 25 }
def foo:
    byte: mapping("ABCDABABABCCZZCZZ")



Could possibly make it a general byte mapping, not just character mapping (treat characters as a special case of numbers), and then you can basically define compile-time conversion maps for things.


Anyway, these are all good questions/suggestions! You've given me a few good ideas of what to improve. I think of the most immediately useful, resizable banks and character remapping could be handy.

by on (#75204)
Gilbert wrote:
I am too lazy to read the manual at the moment (the non-assembly codes hurt my brain already but people unfamiliar with assembly may find this easier to handle) and I tried to assemble the test rom but failed, with some garbage error message:
Image

I'm not sure whether it's the problem with my system though.


That's... unusual. I'll try and look into it. I notice the exception is a std.file.FileException on "test.nes", so that could be an issue with calling D's filesystem helpers. I'll see I can figure out what's causing that.

EDIT: Okay, I see the problem now. This was a silly mistake on my part (using isdir() without checking if the file exists first). It will be fixed.

by on (#75205)
Overkill wrote:
"Word" is definitely a misnomer (since the 6502 word size is 1 byte)


Wait a tick... Did I miss something? Because if this is the case I need to scrap my entire project and start over.

EDIT: Nevermind, I misunderstood *wipes sweat from brow*

by on (#75206)
Thanks. FYI, I am using Windows XP (SP3) in traditional Chinese (well, in case the problem was caused by some conflict with system using a non-western encoding).

by on (#75207)
Maybe a .bank 8K or .bank 16K or even more for bank-size changes? That way you can keep "banks" grouped in nice chunks of different sizes.

by on (#75208)
Okay, thanks for reporting that Gilbert, just made a critical fix to the nel 0.1 release, which fixes a dumb std.file.FileException being thrown. Download nel 0.1.1 and the program should actually run.

3gengames wrote:
Maybe a .bank 8K or .bank 16K or even more for bank-size changes? That way you can keep "banks" grouped in nice chunks of different sizes.


Hmm. That's a pretty cool idea actually, since then you don't just have two fixed size banks for CHR and PRG, and can lump things into logical groups as you see fit. Banks could then just be a sort of self-imposed limit to ensure your code "fits" properly for bank-switches. So you could, for an MMC3 game, do something like 1K banks for some animated CHR, 8K banks for switchable PRG, and a single 16K bank for fixed PRG.

I'm not entirely sure how it'd work though. But it's giving me some ideas. I know some assembler call banks "sections" instead, I'm not completely averse to that, as long sections can still be numbered rather than just named.

I'll need to fuss over it a bit I think.

Also, I've noticed I could probably put in a shorthand for expressing multiples of 1024 (a K suffix, like NESHLA), and possibly add operators for grabbing high and low bytes of addresses, so you don't NEED to write out masking/shifting for the bytes of an address expression.

I was thinking of maybe expanding constant definitions to allow arguments, making it possible to define functions of constants. This, along with tables/mappings for byte sequences (described earlier in reponse to Tepples' character table mention), could make for some pretty neat macro functionality!

by on (#75252)
I know that you've already solved your tokenizer / EBNF problem, and that you tried lex/yacc (or equiv) already. But I wanted to let you (and anyone else into writing their own language tools) that there is another parser that is quite nice.

The "lemon parser" is used to generate the parsing code for the SQL syntax used by sqlite. It was created for this purpose, but is flexible enough to create parsers for anything that can be described with EBNF. It is free to use. The Lemon Parser merges the required features of lex and yacc into a single, cohesive, piece of software.

http://www.hwaci.com/sw/lemon/

Disclaimer: I have not used lemon myself in any of my projects, but it has a great track record for others. If I write a parser in the future, I will probably use lemon instead of flex/bison or lex/yacc.

by on (#82896)
Alrighty, there have been a few updates to nel, as of late that will make it a little bit easier to use (hopefully). Recently started working on it again because a person that I know from online was recently trying to compile nel's compiler source in the most recent version of D2 (There were a few deprecations to stuff, like no default on a switch statement, in a more recent D2 build). Anyways, it works now.

And there have been a few nice changes made since last release:
  • if statements added, for conditional compilation and a more convenient syntax for runtime branching.
  • while statements added, shorthand for looping 0-or-more times.
  • repeat statements added, shorthand for looping 1-or-more times, or infinitely.
  • No longer uses the rom and ram keywords, and awkward prg/chr sizing in the ines header. Instead you can define banks (of RAM, PRG ROM, or CHR ROM), by using bank definitions and then use in bank, location: to layout your program.
  • A new horizontal scrolling tilemap demo.


That bank change is pretty useful, because it means PRG and CHR ROM sections can be arbitrarily sized, and referred to be name. The resulting .nes file is automagically rounded to the nearest 16K of PRG, and 8K of CHR.

Download nel 0.2 for Windows here.

Or for more information:
Check the source.
Check the updated language reference.

I'm thinking this new bank system will help improve things, and allow people to more easily use whatever mapper they want.

Time will tell!

by on (#83057)
This is starting to look like BatariBASIC for the Atari 2600. In bB I like how you can declare how much ROM you're going to use and then use the banks just like any other label. Of course, certain banks can only be used for graphics sometimes.

Assembly scares the Octoroks out of me but I was actually able to read the hello.nel. Very cool!

by on (#83069)
slobu wrote:
Assembly scares the Octoroks out of me but I was actually able to read the hello.nel. Very cool!


I'm so hungry (for this compiler), I can eat an Octorok!