Starting my NESdev journey
Moderator: Moderators
- Individualised
- Posts: 310
- Joined: Mon Sep 05, 2022 6:46 am
Starting my NESdev journey
Now that I have a computer that is suitable for the task, I feel like it's finally time for me to jump into NES homebrew development, after reading up on the NES hardware for so long. I'd like to try and start in the "best" possible way though, so I created this thread to ask what assemblers/other tools to use and what tutorial I should be following, and maybe ask for help further along the line. My plan is to first work my way through a tutorial such as Nerdy Nights so I can get the hang of 6502 assembly, then write some basic test programs independently, then move on to making some simple NROM games and after that maybe attempting some mapper 1 or MMC3 stuff.
One of the main issues I have with beginner's tutorials though, and I believe that Nerdy Nights is like this too having skimmed through it a few times before, is that they assume the user has no prior knowledge of the hardware, or even programming or computer science concepts in general (for example Nerdy Nights begins by explaining number systems and that computers work in binary...). I'm in a bit of a weird spot, where I know all the concepts/theory, I know how the NES hardware functions, I understand how a CPU executes machine code etc, I'm just rusty on putting into practice - actually writing assembly code. This isn't something I've just experienced with NES or retro homebrew stuff either, I remember when I was first learning to program in Java and it was a similar situation for a lot of tutorials I was trying to follow then too, where I quickly lost interest due to them repeatedly explaining basic programming concepts that I already knew. Additionally I know that Nerdy Nights is based around some tools that are considered by some to now be outdated (i.e. NESASM, also keep in mind that I run Ubuntu so while I can run Windows tools in Wine I'd like to keep this as native to my OS as possible). So, I was wondering if there was a "getting started" NES development tutorial that's targeted to users with more advanced knowledge, or should I just stick with Nerdy Nights since it usually seems to be considered the best for beginners? Or should I just look up general 6502 assembly tutorials, since that's really what I mainly need to learn?
Much thanks in advance and looking forward for any response.
One of the main issues I have with beginner's tutorials though, and I believe that Nerdy Nights is like this too having skimmed through it a few times before, is that they assume the user has no prior knowledge of the hardware, or even programming or computer science concepts in general (for example Nerdy Nights begins by explaining number systems and that computers work in binary...). I'm in a bit of a weird spot, where I know all the concepts/theory, I know how the NES hardware functions, I understand how a CPU executes machine code etc, I'm just rusty on putting into practice - actually writing assembly code. This isn't something I've just experienced with NES or retro homebrew stuff either, I remember when I was first learning to program in Java and it was a similar situation for a lot of tutorials I was trying to follow then too, where I quickly lost interest due to them repeatedly explaining basic programming concepts that I already knew. Additionally I know that Nerdy Nights is based around some tools that are considered by some to now be outdated (i.e. NESASM, also keep in mind that I run Ubuntu so while I can run Windows tools in Wine I'd like to keep this as native to my OS as possible). So, I was wondering if there was a "getting started" NES development tutorial that's targeted to users with more advanced knowledge, or should I just stick with Nerdy Nights since it usually seems to be considered the best for beginners? Or should I just look up general 6502 assembly tutorials, since that's really what I mainly need to learn?
Much thanks in advance and looking forward for any response.
Re: Starting my NESdev journey
It sounds like you don't really need a tutorial, per se, but rather just some starting materials and direction. I haven't gone through any of the tutorials myself, but in addition to often locking you into tools that aren't generally well-liked these days, they often don't have the best program design and can teach bad habits.
For assemblers, ca65 seems to be the most well-liked, stable, mature, and featureful. Its main drawback is that it's not as beginner-friendly as other assemblers because it has a linking phase, thus requiring a linker config file. Tepples has a template available here that can get you started with ca65 and linking: https://github.com/pinobatch/nrom-template. Because ca65 links, it can be harder to know exactly where your code is compared to an assembler like ASM6, though this is rarely a significant issue. ca65's anonymous labels are also very lacking compared to other 6502 assemblers; it uses what I've seen referred to as Ophis-style labels, where your jump target is specified as being some number of anonymous labels forward or backward, but this is extremely fragile because adding or removing anonymous labels can break jumps. As such, it's only suitable for 1 or 2 anonymous labels in very close proximity. However, ca65 has excellent support for scoping (.proc directive), so it's easy to use local named labels without polluting the global namespace.
I generally recommend as a starter project that people do something small and simple like Pong. This is enough of a game to teach you the basics of most of the concepts the system requires. To make Pong, you have to handle system init, writing palettes, writing nametables and attributes tables, NMIs, controller input, and an object system with drawing, movement, and collision. You can do simple audio, too, though most people use some kind of audio engine library such that it's mostly a matter of making sound effects and triggering them at the right time. Notably absent from this game is scrolling, but a lot of homebrew doesn't scroll, anyway.
Below is some skeleton code to get you started with good practices for making a game. This is roughly what the NMI handler and frame loop look like in the games I've made.
Most of the concepts here have pages on the wiki. These pages include example code that should work as-is:
Init code
Jump tables
Reading controllers
For a game as simple as Pong, you don't necessary need a HandlePpuWrites function, but it's an extremely common concept across most games. The idea here is that you have some kind of transfer buffer, which at its simplest, is a PPU address, number of bytes, and list of bytes of that length. Then you either have a terminator (such as a negative number; PPU addresses are big endian and always have the highest bit clear) or another address/length/data structure, repeating until a terminator is hit. Often times these formats have flags indicating things like write direction (whether the data is written rightward (add 1) or downward (add 32)) or compression format (run-length encoding is common). You can also have the location of the buffer vary, so a pointer can indicate it's in RAM for a mutable buffer or in ROM for a fixed buffer, the latter useful for things like a title screen.
That's probably enough info to start with. Please ask questions as you have them. I'm sure others will chime in with other suggestions!
For assemblers, ca65 seems to be the most well-liked, stable, mature, and featureful. Its main drawback is that it's not as beginner-friendly as other assemblers because it has a linking phase, thus requiring a linker config file. Tepples has a template available here that can get you started with ca65 and linking: https://github.com/pinobatch/nrom-template. Because ca65 links, it can be harder to know exactly where your code is compared to an assembler like ASM6, though this is rarely a significant issue. ca65's anonymous labels are also very lacking compared to other 6502 assemblers; it uses what I've seen referred to as Ophis-style labels, where your jump target is specified as being some number of anonymous labels forward or backward, but this is extremely fragile because adding or removing anonymous labels can break jumps. As such, it's only suitable for 1 or 2 anonymous labels in very close proximity. However, ca65 has excellent support for scoping (.proc directive), so it's easy to use local named labels without polluting the global namespace.
I generally recommend as a starter project that people do something small and simple like Pong. This is enough of a game to teach you the basics of most of the concepts the system requires. To make Pong, you have to handle system init, writing palettes, writing nametables and attributes tables, NMIs, controller input, and an object system with drawing, movement, and collision. You can do simple audio, too, though most people use some kind of audio engine library such that it's mostly a matter of making sound effects and triggering them at the right time. Notably absent from this game is scrolling, but a lot of homebrew doesn't scroll, anyway.
Below is some skeleton code to get you started with good practices for making a game. This is roughly what the NMI handler and frame loop look like in the games I've made.
Code: Select all
.proc HandleNmi
; Save registers.
PHA
TXA
PHA
TYA
PHA
; If this is a lag frame, do not touch the PPU. We still want to handle audio, though.
LDA frame_ready
BNE PpuDone
; Write the contents of the VRAM transfer buffer to the PPU.
BIT PPU_ADDRESS
JSR HandlePpuWrites
; Set rendering flags. Rendering should generally be toggled in vblank.
LDA ppu_mask_next
STA PPU_MASK
STA ppu_mask_value
; Set the scroll and other flags. Scroll must be set after VRAM writes are complete.
LDA ppu_control_next
STA PPU_CONTROL
STA ppu_control_value
LDA scroll_x
STA PPU_SCROLL
LDY scroll_y
STY PPU_SCROLL
; We do OAM DMA last in vblank because doing it too late is the least disruptive of our possible PPU actions.
LDA #$00
STA OAM_ADDRESS
LDA #>oam_buffer
STA OAM_DMA
; Mark that we've handled the start of this frame already.
LDA #$01
STA frame_ready
PpuDone:
; We do this every NMI, even if it's a lag frame, because lagging audio can be more disruptive than lagging gameplay.
JSR HandleAudio
; Restore registers.
PLA
TAY
PLA
TAX
PLA
RTI
.endproc
.proc HandleFrame
; Wait for the NMI to run.
:
LDA frame_ready
BEQ :-
JSR ReadJoypads
HandleMode:
; Run the handler for the current game mode using a jump table.
LDA current_mode
ASL
TAX
LDA dModeHandlers,X
STA indirect_jump_pointer
LDA dModeHandlers+1,X
STA indirect_jump_pointer+1
JSR DoIndirectJump
; Commit any changes to the game mode and mode state.
LDA next_mode
STA current_mode
LDA next_state
STA current_state
; Indicate that the frame is done.
LDA #$00
STA frame_ready
JMP HandleFrame
.endproc
Init code
Jump tables
Reading controllers
For a game as simple as Pong, you don't necessary need a HandlePpuWrites function, but it's an extremely common concept across most games. The idea here is that you have some kind of transfer buffer, which at its simplest, is a PPU address, number of bytes, and list of bytes of that length. Then you either have a terminator (such as a negative number; PPU addresses are big endian and always have the highest bit clear) or another address/length/data structure, repeating until a terminator is hit. Often times these formats have flags indicating things like write direction (whether the data is written rightward (add 1) or downward (add 32)) or compression format (run-length encoding is common). You can also have the location of the buffer vary, so a pointer can indicate it's in RAM for a mutable buffer or in ROM for a fixed buffer, the latter useful for things like a title screen.
That's probably enough info to start with. Please ask questions as you have them. I'm sure others will chime in with other suggestions!
- Individualised
- Posts: 310
- Joined: Mon Sep 05, 2022 6:46 am
Re: Starting my NESdev journey
Thank you for the thorough reply, this is very helpful info! I've taken your suggestions into consideration. I agree that it may just be best for me to jump straight in using my current knowledge and learn as I go.
I'll be sure to ask more questions!
I'll be sure to ask more questions!
Re: Starting my NESdev journey
Is there any kind of intro/tutorial on ca65's syntax/features/language? I see a lot of references to features of the assembler but I don't really know how to leverage them. For example you mentioned scope in .proc, where can I learn more?
Re: Starting my NESdev journey
Have you looked at the user guide already?
Re: Starting my NESdev journey
There are also examples using it like Rainwarrior's template which I think is a good start for learning how to use ca65.
Answering OP, I agree with Fiskbit that you should start making Pong after having learned the bare basics such as drawing a background displaying a sprite and moving it with the controller. Pong doesn't require a metatile system and only a very simple metasprite system, or you can even get away with hardcoding sprites since there are only 3 entities (2 paddles and 1 ball).
Here is another wiki page I highly recommend you to read after making Pong:
The frame and NMIs
Although Nerdy Nights starts off very basic, it does teach you 6502 in a very introductory way which is excellent and I think is its forte. If you already know 6502 well you can skim it or just check it for some hints on how to make Pong if you are stuck, but otherwise it's still a very good tutorial for learning all the basics of 6502 that you will need to make NES games. I knew hexadecimal and many other things when I started following it, those are things you just may want to skim through if you know them already, all tutorials may have things like this that are too easy for you, so you need some patience.
One thing it does differently is that it puts all code (except the init code) in the NMI (which is what some commercial games like Super Mario Bros also actually does) instead of just having PPU updates there and keeping game logic outside of it like most homebrewers prefer. But by looking at the examples given to you, I think you may figure it out.
The more advanced Nerdy Nights tutorials may also teach you some things. Especially the sound tutorial by Metalslime, it's probably the best Nerdy Nights tutorial of them all. Definitely read it to learn how to use the APU, and it also teaches how you can make a simple system for buffering PPU writes (referring to the "The frame and NMIs" article), though without RLE nor does it work with ROM data directly but it's good enough for most programs. It does teach you how to separate logic and graphic updates using the NMI too.
It's not a too beginner-friendly tutorial though, it teaches you how to build a fully functional and actually pretty advanced sound engine with variable tempo and custom envelopes. It's the only one of its kind that I've seen.
Answering OP, I agree with Fiskbit that you should start making Pong after having learned the bare basics such as drawing a background displaying a sprite and moving it with the controller. Pong doesn't require a metatile system and only a very simple metasprite system, or you can even get away with hardcoding sprites since there are only 3 entities (2 paddles and 1 ball).
Here is another wiki page I highly recommend you to read after making Pong:
The frame and NMIs
Although Nerdy Nights starts off very basic, it does teach you 6502 in a very introductory way which is excellent and I think is its forte. If you already know 6502 well you can skim it or just check it for some hints on how to make Pong if you are stuck, but otherwise it's still a very good tutorial for learning all the basics of 6502 that you will need to make NES games. I knew hexadecimal and many other things when I started following it, those are things you just may want to skim through if you know them already, all tutorials may have things like this that are too easy for you, so you need some patience.
One thing it does differently is that it puts all code (except the init code) in the NMI (which is what some commercial games like Super Mario Bros also actually does) instead of just having PPU updates there and keeping game logic outside of it like most homebrewers prefer. But by looking at the examples given to you, I think you may figure it out.
The more advanced Nerdy Nights tutorials may also teach you some things. Especially the sound tutorial by Metalslime, it's probably the best Nerdy Nights tutorial of them all. Definitely read it to learn how to use the APU, and it also teaches how you can make a simple system for buffering PPU writes (referring to the "The frame and NMIs" article), though without RLE nor does it work with ROM data directly but it's good enough for most programs. It does teach you how to separate logic and graphic updates using the NMI too.
It's not a too beginner-friendly tutorial though, it teaches you how to build a fully functional and actually pretty advanced sound engine with variable tempo and custom envelopes. It's the only one of its kind that I've seen.
- Individualised
- Posts: 310
- Joined: Mon Sep 05, 2022 6:46 am
Re: Starting my NESdev journey
6502 assembly is pretty much the main thing I need to learn. Like I said in the OP, I know the theory, I think I have a decent understanding of the workings of the NES hardware and it's features/quirks, and how I'd theoretically interface with it; but I can't yet put it into practice. I suppose that I could start independently and use Nerdy Nights as a reference. Thanks for the reply!
- Individualised
- Posts: 310
- Joined: Mon Sep 05, 2022 6:46 am
Re: Starting my NESdev journey
Alright, so a few hours ago I downloaded tepples's example game as a test, and set up a ca65 workspace and managed to assemble it, which was a surprisingly intuitive thing to do. What I think I'm going to do is use Nerdy Nights as a reference for understanding 6502 assembly, and try to create a Pong game similar to as it teaches, but I won't follow the tutorial itself, if that makes sense.
First though, I'd like to know how to be able to view the source assembly in Mesen's debugger, rather than disassembled code. I assume that there is some sort of assembler option that I need to use that will create a debug file that I can load into Mesen, correct?
First though, I'd like to know how to be able to view the source assembly in Mesen's debugger, rather than disassembled code. I assume that there is some sort of assembler option that I need to use that will create a debug file that I can load into Mesen, correct?
Re: Starting my NESdev journey
I see, that is kinda what I do too when following a tutorial and I only need to get some pointers from it. You may want to try and get the code from the Nerdy Nights lessons working in ca65 as a challenge and to get some real 6502 practice.
I'm not sure if Mesen can read your source code. There are ways to get it to use your label names in its disassembly though, but I'm not sure how to do that with ca65.
I'm not sure if Mesen can read your source code. There are ways to get it to use your label names in its disassembly though, but I'm not sure how to do that with ca65.
- Individualised
- Posts: 310
- Joined: Mon Sep 05, 2022 6:46 am
Re: Starting my NESdev journey
Alright then. Meanwhile, early this morning (around 12 hours ago in my timezone), I had a little fun with the example program and did a little exercise:
This wasn't just a CHR and palette swap - I'm using the unmodified SMB1 background CHR here, and since the tiles are generated programmatically, I had to rewrite the code that draws the columns and the floor (and of course, the floor is a different pattern too.). This helped me to understand both assembly program flow as well as how certain instructions exactly work better.
Tomorrow, I'll try beginning from a blank canvas and try to recreate the Nerdy Nights pong game in ca65 but using the basic init code posted above.
This wasn't just a CHR and palette swap - I'm using the unmodified SMB1 background CHR here, and since the tiles are generated programmatically, I had to rewrite the code that draws the columns and the floor (and of course, the floor is a different pattern too.). This helped me to understand both assembly program flow as well as how certain instructions exactly work better.
Tomorrow, I'll try beginning from a blank canvas and try to recreate the Nerdy Nights pong game in ca65 but using the basic init code posted above.
Re: Starting my NESdev journey
For symbols, I pass -g to ca65 and --dbgfile [filename].dbg to ld65. I think Mesen will load the .dbg file automatically if it's beside a same-named .nes file.
- Individualised
- Posts: 310
- Joined: Mon Sep 05, 2022 6:46 am
Re: Starting my NESdev journey
That's exactly what I was looking for! Even shows comments, not just label names. Thanks!