# Prelude This guide is just a set of rules I have been cooking up while hacking on the NES/Famicom. These are rules which I believe that have made things easier for me on NES/Famicom development, but it's not a general recommendation nor something to be enforced. That is, it's just a set of ideas I try to follow in order to be consistent mainly with myself. Hence, it's not complete and I can still be persuaded away from things I say here. All in all, take all of this with a grain of salt and assume that if I write something that looks fishy, maybe it's just that I don't know any better and I might be able to be convinced via a [Github issue](https://github.com/mssola/style.nes/issues). That is to say, discussions are welcome, even if I can always just say no. Last but not least: get to know the NES/Famicom first! This is not a reference guide nor a list of pitfalls that should be avoided. For all of this, just refer to the [NESDev wiki](https://www.nesdev.org/wiki/). # Programming language There are multiple ways to code on the NES/Famicom. I have seen helpful and insightful projects which have used C as a programming language. That being said, the NES/Famicom is really scarce when it comes to resources. Hence, if you can, you should try to make the most out of it and **write everything in MOS 6502 assembly**. This is not a comment against C, but as clever and helpful as tools like [cc65](https://github.com/cc65/cc65) can be, they can never quite reach the level of optimization over what the machine is executing as assembly. Couple that with the fact that MOS 6502 assembly is not that hard to learn, and that most learning resources are also given in assembly. That being said, one good argument could be made that you could write the most performance critical bits in assembly and leave the business code in C so it's easier to understand. That certainly is a possibility but in the end the mixing of both languages can go wrong in different and subtle ways, makes building, linking and debugging more complex, and, in my humble opinion, for not too much to gain. Or at least that's how I feel, and programming in C might just be fine for some people, but I prefer doing everything in assembly on NES/Famicom development. That's why for the rest of this guide we will be talking about assembly and never about C or any other language. # Source code layout ## Encoding Use **UTF-8** as the source file encoding. For the code itself you should stick to good ol' ASCII, but for comments and sharing your code around, just use UTF-8 which is supported virtually everywhere. ## Indentation, tabs vs spaces Use only spaces for indentation, no hard tabs. Each indentation level is 4 spaces long. Hence: ``` assembly bad: lda #1 good: lda #1 ``` Introduce a new indentation level after any label: ``` assembly function1: lda #1 ; bad! function2: lda #1 ; good! ``` That being said, if the label exists in relation to another one, then it ought to be kept at the same indentation level as the other label. For example, labels for control flow inside of a function: ``` assembly foo: lda #1 @label: ; bad! jmp @label foo: lda #1 @label: ; good! jmp @label ``` That is, `@label` exists in relation to `foo`, and so it should be at the same indentation level. The same would apply to a function which has some pre-computed data for some of its logic: ``` assembly function1: ldx #$00 lda data, x ;; Do something rts data: .byte $01, $02 ``` The rationale for this is that labels ought to be clearly visible, and by being at a different indentation level as instructions they certainly stick out. Moreover, note that every instruction for a function is kept at the same indentation level regardless of its flow. The recommended `@` prefix for labels (see [Naming conventions](#naming-conventions)) further disambiguates with the name of the function as well. One could argue that labels for control flow could be put at the same indentation level, and hence you would be able to clearly denote where loops or branches are inside of a given function. Hence, having something such as: ``` assembly function1: lda #1 @loop: ldx #00 jmp @loop rts ``` I have the following disagreements with this approach: 1. It artificially makes it look like a higher-level programming language (e.g. there is no lexical scoping). 2. I feel like labels (e.g. `@loop` in the example above) are easier to miss. 3. Having multiple indentation levels is already a code smell: avoid too much complexity on your functions as you ought to be as performant as possible. In a similar spirit, introduce a new indentation level inside of `.proc`, `.macro`, `.repeat`, `.if` and similar control statements which expect a block of code inside. Thus: ``` assembly .proc bad lda #1 .endproc .proc good lda #1 .endproc ``` Last but not least, and again for clarity's sake, labels are to be put on their own line: ``` assembly bad: lda #1 good: lda #1 ;; Yes, data too. bad: .byte $02 good: .byte $02 ``` ## Line endings and the likes Let's get simple statements out of the way: - Limit lines to 80 characters. - No trailing whitespace. - Use Unix-style line endings. - End each file with a newline. I will not bother to further explain on the above, as others have wasted more time on this than me on these arguments. It can be easily configured through your editor (and this repository also holds an [.editorconfig](./.editorconfig) to help you on this). If your editor doesn't support some of these options, just replace it. # Numeric literals In 6502 assembly you can express numeric literals in decimal, hexadecimal and binary formats. One useful rule I have been developing over the course of programming in 6502 assembly is: 1. Prefer the hexadecimal format: debuggers, emulators, ROM dumps, and related tooling will default to this format. 2. Use the decimal format for simple numbers which can be trivially translated into hexadecimal format, but which are more simple to write this way (e.g. `lda #0`). 3. Use binary format for bitmap masks, or other arrangements where each bit has been set/unset following a very strict order (e.g. preparing the value for a PPU register). As for masks, it might be quite trivial to mentally parse which bits are set/unset with something like `lda #$81`, but something like `lda #$AC` might take more time to mentally parse than the more explicit `lda #%10101100`. # Allocation conventions I am not going to reinvent the wheel here: just stick to the comments on the [NESDev wiki](https://www.nesdev.org/wiki/CPU_memory_map), or the [sample RAM map](https://www.nesdev.org/wiki/Sample_RAM_map) on how to allocate memory on the NES/Famicom. In general, you should be very mindful when placing your data, and note that because of the MOS 6502 architecture, there is a noteworthy difference between placing data on the zero page or not. Hence, just to reiterate: ensure that the data you use more often is placed on the zero page. Note that the [Calling conventions](#calling-conventions) further reiterate on this fact. Mainly because of this, and in stark contrast to many other code bases, avoid using the `.res` control statement for "variables". Hence: ``` assembly bad_var: .res 1 good_var = $01 ``` Using the `.res` control statement has two main benefits: 1. The compiler can enforce that you don't go over the capacity for a given segment. 2. You can add/remove variables without too much hassle. But it also has its drawbacks: 1. You don't know where data is placed. This is important when debugging, where you have to watch for a specific address. Hence, you'd need to manually compute anyways the address for a variable (multiple times if you have added/removed variables since the last time), while for `good_var` you already know where it is located. 2. The `.res` statement guarantees that the data will be zero'ed out (or with the given optional fill value). This is not possible for variables, as "memory" will not be a part of the ROM file (for obvious reasons). Hence, you still need to take care of initializing these variables. By using the `.res` statement you are being misleading on how things work. This is because the `.res` statement is meant to be used for stuff that will actually appear on the ROM file, not for "variables" in memory. All of that being said, the first benefit that we pointed out is not to be overlooked, but it can arguably be achieved via tooling as well. On that note I'm working on an "address sanitizer" in [mssola/tools.nes](https://github.com/mssola/tools.nes) which could take care of listing which slots are available or which slots have already been taken. # Naming conventions Use `snake_case` everywhere. ``` assembly badThing: .byte $01 good_thing: .byte $01 ``` Only use upper case for macros or regular constants (as [detailed below](#UPPER_CASE-for-macros-and-regular-constants)). For the rest of your code stick to lower case and allow syntax highlighting on modern editors do the rest. Hence: ``` assembly LDA #01 ; bad! lda #01 ; good! ``` One good idea for `.macro` is to use them to define pseudo-instructions, as it's done in other assembly languages like RISC-V. For example, on [these scrolling examples](https://github.com/mssola/code.nes/tree/main/scroll) there is a macro for the pseudo-instruction `JAL`, which makes explicit when a `jmp` has been used instead of `jsr` for reducing the stack usage on tail calls. In this scenario, you can find code like this: ```assembly .proc foo ;; Previous code that might jump/branch to @end. lda Some::Variable bne @end JAL prepare_next_column @end: rts .endproc ``` Here the casing makes explicit that `JAL` is actually a pseudo-instruction, and so that it follows somewhat different rules than the ones by its side. ## Use the `@` prefix for named labels which affect the control flow Named labels which are part of the control flow are to use the `@` symbol as a prefix for their names. This is in accordance to the style used virtually everwhere, and it clearly denotes which labels are actually part of the control flow. Thus, labels which reference a piece of data should not be prefixed with `@`, but labels which are part of the flow of branching/jumping should be prefixed accordingly. ``` assembly ;; bad loop: ldx #0 lda @data, x @data: .byte $00 ;; good @loop: ldx #0 lda data, x data: .byte $00 ``` ## Use memory-explicit prefixes for variables Just to reiterate over what was said on [Allocation conventions](#allocation-conventions): be very mindful on where data is placed. On this, the name of "variables" can also help out, and more so on code that is accessing it but it's far from where it was initialized or declared. Hence, the context might have been lost and you might not be fully aware on what kind of data you are operating on. So, use the following prefixes: - `zp_` for variables on the zeropage. - `wr_` for variables on "Working RAM". - `m_` for the rest. Consider the following code: ``` assembly lda Creative Commons Attribution 4.0 International.