Results 1 to 10 of 10

Thread: NES 6502 Programming Tutorial - Part 1: Getting Started

Threaded View

  1. #1

    Thread Starter
    Android OpenGL ES Guru Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,278

    NES 6502 Programming Tutorial - Part 1: Getting Started




    Welcome to my NES 6502 programming tutorial! In this tutorial, I'm gonna teach you step by step on what tools you'll need, some basic 6502 opcodes to use, and the end result, having a basic structured NES program. This tutorial is going to kinda shorter than I intended to write it because I written some basic 6502 commands for you to learn in my Atari 2600 programming tutorial already right HERE.

    To help get your skills improved in the world of 6502 programming, you can play around with them in these sites:
    https://skilldrick.github.io/easy6502/
    http://www.6502asm.com/

    ----------------------------------------------------------------------------------------------------------------------------

    Ok. So by now, you should have a basic understanding of 6502 assembly, and hexadecimals, which is a lot easier than you think. The next thing you will need is a place to code in. Some people could use notepad and save the file as asm, but what I personally use is Notepad++.

    ----------------------------------------------------------------------------------------------------------------------------

    Now after that you are going to need something to convert all that code to an NES file. So what you will need is NESASM3. Using it is so easy you can either drag the asm file into it, or make a batch file to compile it, which is better because it'll display the errors you made. Dragging the asm file into the NESASM3 will create 2 files; an .fns file which shows the addresses of your sub routines, and an .nes file. If there is an error in your code, only the .fns file will be created. However if you create a batch file (ex. compile.bat) using these commands:

    Code:
    NESASM3 mygame.asm
    pause
    it will output this:

    Code:
    F:\Jacobs Stuff\Programming\Projects\NES 6502\Projects\My Game>NESASM3 mygame.asm
    NES Assembler (v3.1)
    
    pass 1
    pass 2
    
    F:\Jacobs Stuff\Programming\Projects\NES 6502\Projects\My Game>pause
    Press any key to continue . . .
    Otherwise it'll show an error in you code at a specific line.

    ----------------------------------------------------------------------------------------------------------------------------

    You will now need some emulators to test your NES programs on. And not just any NES emulator. NES emulator debuggers! FCEUXD SP seems to be the most accurate in emulation, while NO$NES has some really really good debugging tools.
    FCEUXD SP
    NO$NES

    Unless you really REALLY wanna test it on actual hardware, there's also the PowerPak by RetroUSB which costs $135.00 assuming you own a Nintendo:
    PowerPak


    ----------------------------------------------------------------------------------------------------------------------------

    Ok. So now you can start coding, right? Well there is just one thing missing. You will need a graphics file as well. A CHR file to be exact, which contains all the graphic tiles and sprites. You can create your own using a CHR editor such as YYCHR, or you can download mario.chr so you can play around with Super Mario Bros. sprites and tiles. But we won't be placing sprites and tiles just yet. The mario.chr file will look something like this for you to work with:



    Basically it consists of multiple 8x8 pixel tiles, each within it's own hexadecimal address. For example:



    ----------------------------------------------------------------------------------------------------------------------------

    Now it's time to learn the memory map of the Nintendo Entertainment System. And this is important because you will be using these memory locations a lot!



    However it's lacking a bit of information. So I'm going to clarify.

    CPU Memory Map
    • $FFFA - $FFFF is where all of your interrupts are located such as NMI, RESET, and IRQ
    • $8000 - $FFF9 is the PRG-ROM, which basically means it's where all of your programming data will be located in, 32K of it. Larger programs can perform bank switching to execute other code.
    • $6000 - $7FFF even though it says SRAM, I personally call it WRAM because it's Work Ram.
    • $4018 - $5FFF is the expansion ROM, which is for the expansion located on the bottom of the NES. These addresses are never used due to the fact that no expansion device was ever made for it!
    • $4017 Controller 2 plus IRQ interrupts
    • $4016 Controller 1
    • $4015 APU Sound/Vertical Clock Signal Register
    • $4014 Sprite DMA Register (Direct Memory Access)
    • $4013 APU Delta Modulation Data Length Register
    • $4012 APU Delta Modulation Address Register
    • $4011 APU Delta Modulation D/A Register
    • $4010 APU Delta Modulation Control Register
    • $400F APU Noise Frequency Register 2
    • $400E APU Noise Frequency Register 1
    • $400D Not Used
    • $400C APU Noise Control Register 1
    • $400B APU Triangle Frequency Register 2
    • $400A APU Triangle Frequency Register 1
    • $4009 APU Triangle Control Register 2
    • $4008 APU Triangle Control Register 1
    • $4007 APU Pulse 2 Coarse Tune Register
    • $4006 APU Pulse 2 Fine Tune Register
    • $4005 APU Pulse 2 Ramp Control Register
    • $4004 APU Pulse 2 Control Register
    • $4003 APU Pulse 1 Coarse Tune (CT) Register
    • $4002 APU Pulse 1 Fine Tune (FT) Register
    • $4001 APU Pulse 1 Ramp Control Register
    • $4000 APU Pulse 1 Control Register
    • $2008 - $3FFF are mirrors of $2000-$2007 over and over again. Any data changed between $2000-$2007 will be copied in $2008-$3FFF and vice versa, hence the term "mirroring".
    • $2007 VRAM I/O Register
    • $2006 VRAM Address Register 2
    • $2005 VRAM Address Register 1
    • $2004 SPR-RAM I/O Register
    • $2003 SPR-RAM Address Register
    • $2002 PPU Status Register
    • $2001 PPU Control Register 2
    • $2000 PPU Control Register 1
    • $0800 - $1FFF are mirrors of $0000-$07FF over and over again.
    • $0300 - $07FF is RAM
    • $0200 - $02FF is where your sprite data from your CHR file is stored. Note that more sprite data can be swapped using bank switching so you are not limited.
    • $0100 - $01FF is the stack, and generally used for arrays I believe.
    • $0000 - $00FF is known as the Zero Page. Generally, variables can be declared here.


    ----------------------------------------------------------------------------------------------------------------------------

    Now that you know the memory map of the CPU (kinda ), you are going to need to know the memory map of the PPU as well, especially if you plan on changing the background:



    To avoid confusion and having to spend hours explaining what they are, thank god for NES Wiki!
    What is a Pattern Table?
    What is a Name Table?
    What is an Attribute Table?
    How does the NES pallete work?
    --------------------------------------------------------------------------------------------------------------------

    If you made it this far, congratulations! Now I'm going to teach you how to code in 6502 assembly for the NES!

    Warning - The NESASM3 compiler is extremely picky on how you indent things. The code can look beautiful, but it'll fail to compile if indented wrong. For example, the dot commands such as .inesprg must always be indented with a couple spaces from the space bar. I don't remember but if you do not indent the dot commands, it will not compile your file correctly or not at all. The sub routines I generally don't indent, but the code inside I generally indent with 2 spaces, and not with a tab key due to the pickiness of the NESASM3 compiler.

    The first thing we need to do is set up the header using dot commands. And yes, they will need indented with 2 spaces.

    Code:
      .inesprg 1   ; 1x 16KB PRG code
      .ineschr 1   ; 1x  8KB CHR data
      .inesmap 0   ; mapper 0 = NROM, no bank swapping
      .inesmir 1   ; background mirroring
    So lets go into detail here. Since our code will be small, we'll only need one 16k bank of program code for .inesprg, even though you can fit nearly 32k of program code, keyword, nearly. Don't forget that $FFFA-$FFFF is reserved for interrupts. And since our CHR file is only one 8k bank, we put a one for .ineschr.

    Believe it or not, there are 256 mappers for the NES (kinda ), with most not used. But since were bare bones basic here, were gonna stick with mapper 0 for now in .inesmap. Heres a complete list of the mappers if you are curious with detail explanations: https://wiki.nesdev.com/w/index.php/Mapper

    And as for mirroring in .inesmir, this will describe how the game will scroll the nametables.

    • 0 - Horizontal Mirroring - Used in vertical scrolling games such as Commando, Ikari Warriors, 1942, etc.
    • 1 - Vertical Mirroring - Used in horizontal scrolling games such as Super Mario Bros, Contra, Ghost and Goblins, etc.
    • 2 - Single Screen - Used in simple games with no scrolling such as Tetris, Dr Mario, Donkey Kong, Pacman, etc.
    • 3 - Four Screen - Used in games that utilize all 4 nametable screens such as Gauntlet, Festers Quest, etc.


    For more information on mirroring, again, resort to NES Wiki. They are awesome:
    https://wiki.nesdev.com/w/index.php/Mirroring

    The next step is to setup our programming bank at address $C000 using dot commands again. Remember don't forget to 2 space indent:
    Code:
      .bank 0
      .org $C000
    I guess you could do it at $8000 if you wanted to as well, but for now, stick with $C000.

    The next step is the RESET sub routine:
    Code:
    RESET:
    	SEI          ; disable IRQs
    	CLD          ; disable decimal mode
    	LDX #$40	
    	STX $4017    ; disable APU frame IRQ
    	LDX #$FF	
    	TXS          ; Set up stack
    	INX          ; now X = 0
    	STX $2000    ; disable NMI
    	STX $2001    ; disable rendering
    	STX $4010    ; disable DMC IRQs
    Since the 6502 inside the NES is actually a 2A03 with no decimal commands, but rather instead, more sound outputs, decimal mode will need disabled where the line CLD is. Now we need two vblank waiting periods to occur, and clear memory in between:

    Code:
    vblankwait1:       ; First wait for vblank to make sure PPU is ready
    	BIT $2002
    	BPL vblankwait1
    
    clrmem:
    	LDA #$00
    	STA $0000, x
    	STA $0100, x
    	STA $0200, x
    	STA $0400, x
    	STA $0500, x
    	STA $0600, x
    	STA $0700, x
    	LDA #$FE
    	STA $0300, x
    	INX
    	BNE clrmem
       
    vblankwait2:      ; Second wait for vblank, PPU is ready after this
    	BIT $2002
    	BPL vblankwait2
    The next step is to loop forever in between. Note that this is NOT where the game loop is.

    Code:
    Foreverloop:
    	JMP Foreverloop     ;jump back to Forever, infinite loop
    This loop is necessary so it can activate an NMI (non maskable interrupt) every frame, doing so 60 frames per second on NTSC NES's. When it does, then this will be where our game code will enly and do something. The NES will automatically clear the screen so you do not have to worry about trying to clear it with code. The end of all the game code will end at RTI (return from interrupt). But for now well keep it blank.

    Code:
    NMI:
    	RTI
    Now we are going to need the interrupt bank:

    Code:
      .bank 1
      .org $FFFA     ;first of the three vectors starts here
      .dw NMI        ;when an NMI happens (once per frame if enabled) the 
                       ;processor will jump to the label NMI:
      .dw RESET      ;when the processor first turns on or is reset, it will jump
                       ;to the label RESET:
      .dw 0          ;external interrupt IRQ is not used in this tutorial
    And last but not least some code to load external files:

    Code:
      .bank 2
      .org $0000
      .incbin "mario.chr"   ;includes 8KB graphics file from SMB1
    I don't know why the origin is $0000 and not $0200, but don't question it, it just works. Just make sure the CHR file is in the same directory as your asm file. Now after saving the complete code as an asm file, you only need to either drag the asm file into your NESASM3 or open your batch file you created to compile it (compile.bat), with the 2nd option being the preferred choice. If it passes, you should now have two new files both having the extention .nes and .fns. Open up your emulator, and load your new NES game. Vwola, you should see a grey background. This concludes our tutorial. Next time, I will teach you how to setup a color palette, and change the background color!

    Also to make it easier, the complete code will be shown below:

    Code:
      .inesprg 1   ; 1x 16KB PRG code
      .ineschr 1   ; 1x  8KB CHR data
      .inesmap 0   ; mapper 0 = NROM, no bank swapping
      .inesmir 1   ; background mirroring
      
    ;;;;;;;;;;;;;;;
    
      .bank 0
      .org $C000 
    RESET:
      SEI          ; disable IRQs
      CLD          ; disable decimal mode
      LDX #$40	
      STX $4017    ; disable APU frame IRQ
      LDX #$FF	
      TXS          ; Set up stack
      INX          ; now X = 0
      STX $2000    ; disable NMI
      STX $2001    ; disable rendering
      STX $4010    ; disable DMC IRQs
    
    vblankwait1:       ; First wait for vblank to make sure PPU is ready
      BIT $2002
      BPL vblankwait1
    
    clrmem:
      LDA #$00
      STA $0000, x
      STA $0100, x
      STA $0400, x
      STA $0500, x
      STA $0600, x
      STA $0700, x
      LDA #$FE
      STA $0300, x
      INX
      BNE clrmem
       
    vblankwait2:      ; Second wait for vblank, PPU is ready after this
      BIT $2002
      BPL vblankwait2
    
    Foreverloop:
      JMP Foreverloop     ;jump back to Forever, infinite loop
    
    NMI:
      RTI
     
    ;;;;;;;;;;;;;;  
    
      .bank 1
      .org $FFFA     ;first of the three vectors starts here
      .dw NMI        ;when an NMI happens (once per frame if enabled) the 
                       ;processor will jump to the label NMI:
      .dw RESET      ;when the processor first turns on or is reset, it will jump
                       ;to the label RESET:
      .dw 0          ;external interrupt IRQ is not used in this tutorial
      
    ;;;;;;;;;;;;;;  
    
      .bank 2
      .org $0000
      .incbin "mario.chr"   ;includes 8KB graphics file from SMB1
    Attached Files Attached Files

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width