Results 1 to 10 of 10

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

  1. #1

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,339

    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

  2. #2
    New Member
    Join Date
    Dec 2019
    Posts
    1

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

    Hello man. First at all et me thank you for this great and useful tutorial. I'm a total noob in NES programming. I've finished the excersice, but i can't get the NESASM3 works. If i try to open it, only get a flicker of the screen an thats it. I don't understand how to make the 2 files. Surelly im doing something wrong. So I hope you can help me. Thanks again.

  3. #3
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

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

    You don't open it.
    You open a cmd window, and you run it from there. It is a command line interface.

    You could drag a file and drop it on the executable, and then you should see what you're seeing, i.e. a command windows flashes up briefly while the program is being assembled, and then goes away. If everything worked, you would have the output files in the directory. If it didn't, you don't know what happen.

    That is why Jacob suggest using a batch file (.bat), with the contents as shown in the first code window of his post. Double clicking on the bat file, will automatically open a Cmd window to run the batch file, which will assemble your file, and then the "pause" will keep the window open so you can see the results.

    Personally, when I use batch files or compile things from the command line, I just open a command window and work from it, rather than use Windows Explorer, but either will work.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  4. #4

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,339

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

    Yup. BAT file man

  5. #5
    New Member
    Join Date
    May 2020
    Posts
    3

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

    If i have some nes rom game, then how Can i see its source code ?

  6. #6

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,339

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

    Quote Originally Posted by tonia6970 View Post
    If i have some nes rom game, then how Can i see its source code ?
    You have to understand what the contents of an NES rom is and how to code in 6502 assembly (all 56 opcodes and 13 address modes, not including illegal opcodes ) and know the hexadecimal numbers of the instructions are to know the source code. The rom contains more than just code, but also the chr graphics file. When you read a rom, the first thing you will see is 'N' 'E' 'S' and the hexadecimal '1A'. The next value is how many PRGROM pages there are (the source code). The next value is how many CHRROM pages are there (the graphics). The value after that is known as the ROM_Control_Byte_1. The value after that is known as ROM_Control_Byte_2. Using this formula:

    Code:
    nes_rom.Mapper = (ROM_Control_Byte_1 >> 4) + (ROM_Control_Byte_2 & 0xf0);
    it determines what mapper number the rom is. And there is lots of em. 256 available but actually less than that because some mapper numbers are unused or are death mappers. You can see it here: https://wiki.nesdev.com/w/index.php/Mapper

    ROM_Control_Byte_1 also determines what kind of screen mirroring. Is it horizontal vertical, single screen, or four screen? And determines if there is a battery save or trainer is present. If there is one, its gonna determine where in the rom the source code is found. I'll show you a small clip of code from my C++ version of my NES emulator to show you what I mean:

    C++ Code:
    1. if (nes_rom.Trainer_Present)
    2. {
    3.     NES_File.seekg(16);
    4.     NES_File.read((char *)Train, 0x200);
    5.     NES_File.seekg(528);
    6.     NES_File.read(reinterpret_cast<char *>(nes_rom.PRG_ROM.data()), nes_rom.PRG_ROM.size());
    7.     NES_File.seekg((nes_rom.PRG_ROM_Pages * 0x4000) + 528);
    8.     NES_File.read(reinterpret_cast<char *>(nes_rom.CHR_ROM.data()), nes_rom.CHR_ROM.size());
    9. }
    10. else
    11. {
    12.     NES_File.seekg(16);
    13.     NES_File.read(reinterpret_cast<char *>(nes_rom.PRG_ROM.data()), nes_rom.PRG_ROM.size());
    14.     NES_File.seekg((nes_rom.PRG_ROM_Pages * 0x4000) + 16);
    15.     NES_File.read(reinterpret_cast<char *>(nes_rom.CHR_ROM.data()), nes_rom.CHR_ROM.size());
    16. }

    So nes_rom.PRG_ROM_Pages * 0x4000 plus the offset shows you where the source code to your rom is located as well as how many pages. So if there is one page, theres 0x4000 lines of code (16,384 in standard decimal format) times the number of pages. at either 16 or 528 depending if there is a trainer present.

    As for the code, have fun comparing each hexadecimal you come across with these

    C++ Code:
    1. //////////////////////
    2.     // Constants:
    3.     //////////////////////
    4.     // ADC: 0x69; 0x65; 0x75; 0x6d; 0x7d; 0x79; 0x61; 0x71
    5.     static const byte ADC_IMMEDIATE = 0x69;
    6.     static const byte ADC_ZERO_PAGE = 0x65;
    7.     static const byte ADC_ZERO_PAGE_X = 0x75;
    8.     static const byte ADC_ABSOLUTE = 0x6d;
    9.     static const byte ADC_ABSOLUTE_X = 0x7d;
    10.     static const byte ADC_ABSOLUTE_Y = 0x79;
    11.     static const byte ADC_INDIRECT_X = 0x61;
    12.     static const byte ADC_INDIRECT_Y = 0x71;
    13.  
    14.     // AND: 0x29; 0x25; 0x35; 0x2d; 0x3d; 0x39; 0x21; 0x31
    15.     static const byte AND_IMMEDIATE = 0x29;
    16.     static const byte AND_ZERO_PAGE = 0x25;
    17.     static const byte AND_ZERO_PAGE_X = 0x35;
    18.     static const byte AND_ABSOLUTE = 0x2d;
    19.     static const byte AND_ABSOLUTE_X = 0x3d;
    20.     static const byte AND_ABSOLUTE_Y = 0x39;
    21.     static const byte AND_INDIRECT_X = 0x21;
    22.     static const byte AND_INDIRECT_Y = 0x31;
    23.  
    24.     // ASL: 0x0a; 0x06; 0x16; 0x0e; 0x1e
    25.     static const byte ASL_ACCUMULATOR = 0x0a;
    26.     static const byte ASL_ZERO_PAGE = 0x06;
    27.     static const byte ASL_ZERO_PAGE_X = 0x16;
    28.     static const byte ASL_ABSOLUTE = 0x0e;
    29.     static const byte ASL_ABSOLUTE_X = 0x1e;
    30.  
    31.     // BCC: 0x90
    32.     static const byte BCC_RELATIVE = 0x90;
    33.  
    34.     // BCS: 0xb0
    35.     static const byte BCS_RELATIVE = 0xb0;
    36.  
    37.     // BEQ: 0xf0
    38.     static const byte BEQ_RELATIVE = 0xf0;
    39.  
    40.     // BIT: 0x24; 0x2c
    41.     static const byte BIT_ZERO_PAGE = 0x24;
    42.     static const byte BIT_ABSOLUTE = 0x2c;
    43.  
    44.     // BMI: 0x30
    45.     static const byte BMI_RELATIVE = 0x30;
    46.  
    47.     // BNE: 0xd0
    48.     static const byte BNE_RELATIVE = 0xd0;
    49.  
    50.     // BPL: 0x10
    51.     static const byte BPL_RELATIVE = 0x10;
    52.  
    53.     // BRK: 0x00
    54.     static const byte BRK_IMPLIED = 0x00;
    55.  
    56.     // BVC: 0x50
    57.     static const byte BVC_RELATIVE = 0x50;
    58.  
    59.     // BVS: 0x70
    60.     static const byte BVS_RELATIVE = 0x70;
    61.  
    62.     // CLC: 0x18
    63.     static const byte CLC_IMPLIED = 0x18;
    64.  
    65.     // CLD: 0xd8
    66.     static const byte CLD_IMPLIED = 0xd8;
    67.  
    68.     // CLI: 0x58
    69.     static const byte CLI_IMPLIED = 0x58;
    70.  
    71.     // CLV: 0xb8
    72.     static const byte CLV_IMPLIED = 0xb8;
    73.  
    74.     // CMP: 0xc9; 0xc5; 0xd5; 0xcd; 0xdd; 0xd9; 0xc1; 0xd1
    75.     static const byte CMP_IMMEDIATE = 0xc9;
    76.     static const byte CMP_ZERO_PAGE = 0xc5;
    77.     static const byte CMP_ZERO_PAGE_X = 0xd5;
    78.     static const byte CMP_ABSOLUTE = 0xcd;
    79.     static const byte CMP_ABSOLUTE_X = 0xdd;
    80.     static const byte CMP_ABSOLUTE_Y = 0xd9;
    81.     static const byte CMP_INDIRECT_X = 0xc1;
    82.     static const byte CMP_INDIRECT_Y = 0xd1;
    83.  
    84.     // CPX: 0xe0; 0xe4; 0xec
    85.     static const byte CPX_IMMEDIATE = 0xe0;
    86.     static const byte CPX_ZERO_PAGE = 0xe4;
    87.     static const byte CPX_ABSOLUTE = 0xec;
    88.  
    89.     // CPY: 0xc0; 0xc4; 0xcc
    90.     static const byte CPY_IMMEDIATE = 0xc0;
    91.     static const byte CPY_ZERO_PAGE = 0xc4;
    92.     static const byte CPY_ABSOLUTE = 0xcc;
    93.  
    94.     // DEC: 0xc6; 0xd6; 0xce; 0xde
    95.     static const byte DEC_ZERO_PAGE = 0xc6;
    96.     static const byte DEC_ZERO_PAGE_X = 0xd6;
    97.     static const byte DEC_ABSOLUTE = 0xce;
    98.     static const byte DEC_ABSOLUTE_X = 0xde;
    99.  
    100.     // DEX: 0xca;
    101.     static const byte DEX_IMPLIED = 0xca;
    102.  
    103.     // DEY: 0x88;
    104.     static const byte DEY_IMPLIED = 0x88;
    105.  
    106.     // EOR: 0x49; 0x45; 0x55; 0x4d; 0x5d; 0x59; 0x41; 0x51
    107.     static const byte EOR_IMMEDIATE = 0x49;
    108.     static const byte EOR_ZERO_PAGE = 0x45;
    109.     static const byte EOR_ZERO_PAGE_X = 0x55;
    110.     static const byte EOR_ABSOLUTE = 0x4d;
    111.     static const byte EOR_ABSOLUTE_X = 0x5d;
    112.     static const byte EOR_ABSOLUTE_Y = 0x59;
    113.     static const byte EOR_INDIRECT_X = 0x41;
    114.     static const byte EOR_INDIRECT_Y = 0x51;
    115.  
    116.     // INC: 0xe6; 0xf6; 0xee; 0xfe
    117.     static const byte INC_ZERO_PAGE = 0xe6;
    118.     static const byte INC_ZERO_PAGE_X = 0xf6;
    119.     static const byte INC_ABSOLUTE = 0xee;
    120.     static const byte INC_ABSOLUTE_X = 0xfe;
    121.  
    122.     // INX: 0xe8
    123.     static const byte INX_IMPLIED = 0xe8;
    124.  
    125.     // INY: 0xc8
    126.     static const byte INY_IMPLIED = 0xc8;
    127.  
    128.     // JMP: 0x4c; 0x6c
    129.     static const byte JMP_ABSOLUTE = 0x4c;
    130.     static const byte JMP_INDIRECT = 0x6c;
    131.  
    132.     // JSR: 0x20
    133.     static const byte JSR_ABSOLUTE = 0x20;
    134.  
    135.     // LDA: 0xa9; 0xa5; 0xb5; 0xad; 0xbd; 0xb9; 0xa1; 0xb1
    136.     static const byte LDA_IMMEDIATE = 0xa9;
    137.     static const byte LDA_ZERO_PAGE = 0xa5;
    138.     static const byte LDA_ZERO_PAGE_X = 0xb5;
    139.     static const byte LDA_ABSOLUTE = 0xad;
    140.     static const byte LDA_ABSOLUTE_X = 0xbd;
    141.     static const byte LDA_ABSOLUTE_Y = 0xb9;
    142.     static const byte LDA_INDIRECT_X = 0xa1;
    143.     static const byte LDA_INDIRECT_Y = 0xb1;
    144.  
    145.     // LDX: 0xa2; 0xa6; 0xb6; 0xae; 0xbe
    146.     static const byte LDX_IMMEDIATE = 0xa2;
    147.     static const byte LDX_ZERO_PAGE = 0xa6;
    148.     static const byte LDX_ZERO_PAGE_Y = 0xb6;
    149.     static const byte LDX_ABSOLUTE = 0xae;
    150.     static const byte LDX_ABSOLUTE_Y = 0xbe;
    151.  
    152.     // LDY: 0xa0; 0xa4; 0xb4; 0xac; 0xbc
    153.     static const byte LDY_IMMEDIATE = 0xa0;
    154.     static const byte LDY_ZERO_PAGE = 0xa4;
    155.     static const byte LDY_ZERO_PAGE_X = 0xb4;
    156.     static const byte LDY_ABSOLUTE = 0xac;
    157.     static const byte LDY_ABSOLUTE_X = 0xbc;
    158.  
    159.     // LSR: 0x4a; 0x46; 0x56; 0x4e; 0x5e
    160.     static const byte LSR_ACCUMULATOR = 0x4a;
    161.     static const byte LSR_ZERO_PAGE = 0x46;
    162.     static const byte LSR_ZERO_PAGE_X = 0x56;
    163.     static const byte LSR_ABSOLUTE = 0x4e;
    164.     static const byte LSR_ABSOLUTE_X = 0x5e;
    165.  
    166.     // NOP: 0xea
    167.     static const byte NOP_IMPLIED = 0xea;
    168.  
    169.     // ORA: 0x09; 0x05; 0x15; 0x0d; 0x1d; 0x19; 0x01; 0x11
    170.     static const byte ORA_IMMEDIATE = 0x09;
    171.     static const byte ORA_ZERO_PAGE = 0x05;
    172.     static const byte ORA_ZERO_PAGE_X = 0x15;
    173.     static const byte ORA_ABSOLUTE = 0x0d;
    174.     static const byte ORA_ABSOLUTE_X = 0x1d;
    175.     static const byte ORA_ABSOLUTE_Y = 0x19;
    176.     static const byte ORA_INDIRECT_X = 0x01;
    177.     static const byte ORA_INDIRECT_Y = 0x11;
    178.  
    179.     // PHA: 0x48
    180.     static const byte PHA_IMPLIED = 0x48;
    181.  
    182.     // PHP: 0x08
    183.     static const byte PHP_IMPLIED = 0x08;
    184.  
    185.     // PLA: 0x68
    186.     static const byte PLA_IMPLIED = 0x68;
    187.  
    188.     // PLP: 0x28
    189.     static const byte PLP_IMPLIED = 0x28;
    190.  
    191.     // ROL: 0x2a; 0x26; 0x36; 0x2e; 0x3e;
    192.     static const byte ROL_ACCUMULATOR = 0x2a;
    193.     static const byte ROL_ZERO_PAGE = 0x26;
    194.     static const byte ROL_ZERO_PAGE_X = 0x36;
    195.     static const byte ROL_ABSOLUTE = 0x2e;
    196.     static const byte ROL_ABSOLUTE_X = 0x3e;
    197.  
    198.     // ROR: 0x6a; 0x66; 0x76; 0x6e; 0x7e;
    199.     static const byte ROR_ACCUMULATOR = 0x6a;
    200.     static const byte ROR_ZERO_PAGE = 0x66;
    201.     static const byte ROR_ZERO_PAGE_X = 0x76;
    202.     static const byte ROR_ABSOLUTE = 0x6e;
    203.     static const byte ROR_ABSOLUTE_X = 0x7e;
    204.  
    205.     // RTI: 0x40
    206.     static const byte RTI_IMPLIED = 0x40;
    207.  
    208.     // RTS: 0x60
    209.     static const byte RTS_IMPLIED = 0x60;
    210.  
    211.     // SBC: 0xe9; 0xe5; 0xf5; 0xed; 0xfd; 0xf9; 0xe1; 0xf1
    212.     static const byte SBC_IMMEDIATE = 0xe9;
    213.     static const byte SBC_ZERO_PAGE = 0xe5;
    214.     static const byte SBC_ZERO_PAGE_X = 0xf5;
    215.     static const byte SBC_ABSOLUTE = 0xed;
    216.     static const byte SBC_ABSOLUTE_X = 0xfd;
    217.     static const byte SBC_ABSOLUTE_Y = 0xf9;
    218.     static const byte SBC_INDIRECT_X = 0xe1;
    219.     static const byte SBC_INDIRECT_Y = 0xf1;
    220.  
    221.     // SEC: 0x38
    222.     static const byte SEC_IMPLIED = 0x38;
    223.  
    224.     // SED: 0xf8
    225.     static const byte SED_IMPLIED = 0xf8;
    226.  
    227.     // SEI: 0x78
    228.     static const byte SEI_IMPLIED = 0x78;
    229.  
    230.     // STA: 0x85; 0x95; 0x8d; 0x9d; 0x99; 0x81; 0x91
    231.     static const byte STA_ZERO_PAGE = 0x85;
    232.     static const byte STA_ZERO_PAGE_X = 0x95;
    233.     static const byte STA_ABSOLUTE = 0x8d;
    234.     static const byte STA_ABSOLUTE_X = 0x9d;
    235.     static const byte STA_ABSOLUTE_Y = 0x99;
    236.     static const byte STA_INDIRECT_X = 0x81;
    237.     static const byte STA_INDIRECT_Y = 0x91;
    238.  
    239.     // STX: 0x86; 0x96; 0x8e
    240.     static const byte STX_ZERO_PAGE = 0x86;
    241.     static const byte STX_ZERO_PAGE_Y = 0x96;
    242.     static const byte STX_ABSOLUTE = 0x8e;
    243.  
    244.     // STY: 0x84; 0x94; 0x8c
    245.     static const byte STY_ZERO_PAGE = 0x84;
    246.     static const byte STY_ZERO_PAGE_X = 0x94;
    247.     static const byte STY_ABSOLUTE = 0x8c;
    248.  
    249.     // TAX: 0xaa
    250.     static const byte TAX_IMPLIED = 0xaa;
    251.  
    252.     // TAY: 0xa8
    253.     static const byte TAY_IMPLIED = 0xa8;
    254.  
    255.     // TSX: 0xba
    256.     static const byte TSX_IMPLIED = 0xba;
    257.  
    258.     // TXA: 0x8a
    259.     static const byte TXA_IMPLIED = 0x8a;
    260.  
    261.     // TXS: 0x9a
    262.     static const byte TXS_IMPLIED = 0x9a;
    263.  
    264.     // TYA: 0x98
    265.     static const byte TYA_IMPLIED = 0x98;

  7. #7
    New Member
    Join Date
    May 2020
    Posts
    3

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

    Quote Originally Posted by Jacob Roman View Post
    You have to understand what the contents of an NES rom is and how to code in 6502 assembly (all 56 opcodes and 13 address modes, not including illegal opcodes ) and know the hexadecimal numbers of the instructions are to know the source code. The rom contains more than just code, but also the chr graphics file. When you read a rom, the first thing you will see is 'N' 'E' 'S' and the hexadecimal '1A'. The next value is how many PRGROM pages there are (the source code). The next value is how many CHRROM pages are there (the graphics). The value after that is known as the ROM_Control_Byte_1. The value after that is known as ROM_Control_Byte_2. Using this formula:

    Code:
    nes_rom.Mapper = (ROM_Control_Byte_1 >> 4) + (ROM_Control_Byte_2 & 0xf0);
    it determines what mapper number the rom is. And there is lots of em. 256 available but actually less than that because some mapper numbers are unused or are death mappers. You can see it here: https://wiki.nesdev.com/w/index.php/Mapper

    ROM_Control_Byte_1 also determines what kind of screen mirroring. Is it horizontal vertical, single screen, or four screen? And determines if there is a battery save or trainer is present. If there is one, its gonna determine where in the rom the source code is found. I'll show you a small clip of code from my C++ version of my NES emulator to show you what I mean:

    C++ Code:
    1. if (nes_rom.Trainer_Present)
    2. {
    3.     NES_File.seekg(16);
    4.     NES_File.read((char *)Train, 0x200);
    5.     NES_File.seekg(528);
    6.     NES_File.read(reinterpret_cast<char *>(nes_rom.PRG_ROM.data()), nes_rom.PRG_ROM.size());
    7.     NES_File.seekg((nes_rom.PRG_ROM_Pages * 0x4000) + 528);
    8.     NES_File.read(reinterpret_cast<char *>(nes_rom.CHR_ROM.data()), nes_rom.CHR_ROM.size());
    9. }
    10. else
    11. {
    12.     NES_File.seekg(16);
    13.     NES_File.read(reinterpret_cast<char *>(nes_rom.PRG_ROM.data()), nes_rom.PRG_ROM.size());
    14.     NES_File.seekg((nes_rom.PRG_ROM_Pages * 0x4000) + 16);
    15.     NES_File.read(reinterpret_cast<char *>(nes_rom.CHR_ROM.data()), nes_rom.CHR_ROM.size());
    16. }

    So nes_rom.PRG_ROM_Pages * 0x4000 plus the offset shows you where the source code to your rom is located as well as how many pages. So if there is one page, theres 0x4000 lines of code (16,384 in standard decimal format) times the number of pages. at either 16 or 528 depending if there is a trainer present.

    As for the code, have fun comparing each hexadecimal you come across with these

    C++ Code:
    1. //////////////////////
    2.     // Constants:
    3.     //////////////////////
    4.     // ADC: 0x69; 0x65; 0x75; 0x6d; 0x7d; 0x79; 0x61; 0x71
    5.     static const byte ADC_IMMEDIATE = 0x69;
    6.     static const byte ADC_ZERO_PAGE = 0x65;
    7.     static const byte ADC_ZERO_PAGE_X = 0x75;
    8.     static const byte ADC_ABSOLUTE = 0x6d;
    9.     static const byte ADC_ABSOLUTE_X = 0x7d;
    10.     static const byte ADC_ABSOLUTE_Y = 0x79;
    11.     static const byte ADC_INDIRECT_X = 0x61;
    12.     static const byte ADC_INDIRECT_Y = 0x71;
    13.  
    14.     // AND: 0x29; 0x25; 0x35; 0x2d; 0x3d; 0x39; 0x21; 0x31
    15.     static const byte AND_IMMEDIATE = 0x29;
    16.     static const byte AND_ZERO_PAGE = 0x25;
    17.     static const byte AND_ZERO_PAGE_X = 0x35;
    18.     static const byte AND_ABSOLUTE = 0x2d;
    19.     static const byte AND_ABSOLUTE_X = 0x3d;
    20.     static const byte AND_ABSOLUTE_Y = 0x39;
    21.     static const byte AND_INDIRECT_X = 0x21;
    22.     static const byte AND_INDIRECT_Y = 0x31;
    23.  
    24.     // ASL: 0x0a; 0x06; 0x16; 0x0e; 0x1e
    25.     static const byte ASL_ACCUMULATOR = 0x0a;
    26.     static const byte ASL_ZERO_PAGE = 0x06;
    27.     static const byte ASL_ZERO_PAGE_X = 0x16;
    28.     static const byte ASL_ABSOLUTE = 0x0e;
    29.     static const byte ASL_ABSOLUTE_X = 0x1e;
    30.  
    31.     // BCC: 0x90
    32.     static const byte BCC_RELATIVE = 0x90;
    33.  
    34.     // BCS: 0xb0
    35.     static const byte BCS_RELATIVE = 0xb0;
    36.  
    37.     // BEQ: 0xf0
    38.     static const byte BEQ_RELATIVE = 0xf0;
    39.  
    40.     // BIT: 0x24; 0x2c
    41.     static const byte BIT_ZERO_PAGE = 0x24;
    42.     static const byte BIT_ABSOLUTE = 0x2c;
    43.  
    44.     // BMI: 0x30
    45.     static const byte BMI_RELATIVE = 0x30;
    46.  
    47.     // BNE: 0xd0
    48.     static const byte BNE_RELATIVE = 0xd0;
    49.  
    50.     // BPL: 0x10
    51.     static const byte BPL_RELATIVE = 0x10;
    52.  
    53.     // BRK: 0x00
    54.     static const byte BRK_IMPLIED = 0x00;
    55.  
    56.     // BVC: 0x50
    57.     static const byte BVC_RELATIVE = 0x50;
    58.  
    59.     // BVS: 0x70
    60.     static const byte BVS_RELATIVE = 0x70;
    61.  
    62.     // CLC: 0x18
    63.     static const byte CLC_IMPLIED = 0x18;
    64.  
    65.     // CLD: 0xd8
    66.     static const byte CLD_IMPLIED = 0xd8;
    67.  
    68.     // CLI: 0x58
    69.     static const byte CLI_IMPLIED = 0x58;
    70.  
    71.     // CLV: 0xb8
    72.     static const byte CLV_IMPLIED = 0xb8;
    73.  
    74.     // CMP: 0xc9; 0xc5; 0xd5; 0xcd; 0xdd; 0xd9; 0xc1; 0xd1
    75.     static const byte CMP_IMMEDIATE = 0xc9;
    76.     static const byte CMP_ZERO_PAGE = 0xc5;
    77.     static const byte CMP_ZERO_PAGE_X = 0xd5;
    78.     static const byte CMP_ABSOLUTE = 0xcd;
    79.     static const byte CMP_ABSOLUTE_X = 0xdd;
    80.     static const byte CMP_ABSOLUTE_Y = 0xd9;
    81.     static const byte CMP_INDIRECT_X = 0xc1;
    82.     static const byte CMP_INDIRECT_Y = 0xd1;
    83.  
    84.     // CPX: 0xe0; 0xe4; 0xec
    85.     static const byte CPX_IMMEDIATE = 0xe0;
    86.     static const byte CPX_ZERO_PAGE = 0xe4;
    87.     static const byte CPX_ABSOLUTE = 0xec;
    88.  
    89.     // CPY: 0xc0; 0xc4; 0xcc
    90.     static const byte CPY_IMMEDIATE = 0xc0;
    91.     static const byte CPY_ZERO_PAGE = 0xc4;
    92.     static const byte CPY_ABSOLUTE = 0xcc;
    93.  
    94.     // DEC: 0xc6; 0xd6; 0xce; 0xde
    95.     static const byte DEC_ZERO_PAGE = 0xc6;
    96.     static const byte DEC_ZERO_PAGE_X = 0xd6;
    97.     static const byte DEC_ABSOLUTE = 0xce;
    98.     static const byte DEC_ABSOLUTE_X = 0xde;
    99.  
    100.     // DEX: 0xca;
    101.     static const byte DEX_IMPLIED = 0xca;
    102.  
    103.     // DEY: 0x88;
    104.     static const byte DEY_IMPLIED = 0x88;
    105.  
    106.     // EOR: 0x49; 0x45; 0x55; 0x4d; 0x5d; 0x59; 0x41; 0x51
    107.     static const byte EOR_IMMEDIATE = 0x49;
    108.     static const byte EOR_ZERO_PAGE = 0x45;
    109.     static const byte EOR_ZERO_PAGE_X = 0x55;
    110.     static const byte EOR_ABSOLUTE = 0x4d;
    111.     static const byte EOR_ABSOLUTE_X = 0x5d;
    112.     static const byte EOR_ABSOLUTE_Y = 0x59;
    113.     static const byte EOR_INDIRECT_X = 0x41;
    114.     static const byte EOR_INDIRECT_Y = 0x51;
    115.  
    116.     // INC: 0xe6; 0xf6; 0xee; 0xfe
    117.     static const byte INC_ZERO_PAGE = 0xe6;
    118.     static const byte INC_ZERO_PAGE_X = 0xf6;
    119.     static const byte INC_ABSOLUTE = 0xee;
    120.     static const byte INC_ABSOLUTE_X = 0xfe;
    121.  
    122.     // INX: 0xe8
    123.     static const byte INX_IMPLIED = 0xe8;
    124.  
    125.     // INY: 0xc8
    126.     static const byte INY_IMPLIED = 0xc8;
    127.  
    128.     // JMP: 0x4c; 0x6c
    129.     static const byte JMP_ABSOLUTE = 0x4c;
    130.     static const byte JMP_INDIRECT = 0x6c;
    131.  
    132.     // JSR: 0x20
    133.     static const byte JSR_ABSOLUTE = 0x20;
    134.  
    135.     // LDA: 0xa9; 0xa5; 0xb5; 0xad; 0xbd; 0xb9; 0xa1; 0xb1
    136.     static const byte LDA_IMMEDIATE = 0xa9;
    137.     static const byte LDA_ZERO_PAGE = 0xa5;
    138.     static const byte LDA_ZERO_PAGE_X = 0xb5;
    139.     static const byte LDA_ABSOLUTE = 0xad;
    140.     static const byte LDA_ABSOLUTE_X = 0xbd;
    141.     static const byte LDA_ABSOLUTE_Y = 0xb9;
    142.     static const byte LDA_INDIRECT_X = 0xa1;
    143.     static const byte LDA_INDIRECT_Y = 0xb1;
    144.  
    145.     // LDX: 0xa2; 0xa6; 0xb6; 0xae; 0xbe
    146.     static const byte LDX_IMMEDIATE = 0xa2;
    147.     static const byte LDX_ZERO_PAGE = 0xa6;
    148.     static const byte LDX_ZERO_PAGE_Y = 0xb6;
    149.     static const byte LDX_ABSOLUTE = 0xae;
    150.     static const byte LDX_ABSOLUTE_Y = 0xbe;
    151.  
    152.     // LDY: 0xa0; 0xa4; 0xb4; 0xac; 0xbc
    153.     static const byte LDY_IMMEDIATE = 0xa0;
    154.     static const byte LDY_ZERO_PAGE = 0xa4;
    155.     static const byte LDY_ZERO_PAGE_X = 0xb4;
    156.     static const byte LDY_ABSOLUTE = 0xac;
    157.     static const byte LDY_ABSOLUTE_X = 0xbc;
    158.  
    159.     // LSR: 0x4a; 0x46; 0x56; 0x4e; 0x5e
    160.     static const byte LSR_ACCUMULATOR = 0x4a;
    161.     static const byte LSR_ZERO_PAGE = 0x46;
    162.     static const byte LSR_ZERO_PAGE_X = 0x56;
    163.     static const byte LSR_ABSOLUTE = 0x4e;
    164.     static const byte LSR_ABSOLUTE_X = 0x5e;
    165.  
    166.     // NOP: 0xea
    167.     static const byte NOP_IMPLIED = 0xea;
    168.  
    169.     // ORA: 0x09; 0x05; 0x15; 0x0d; 0x1d; 0x19; 0x01; 0x11
    170.     static const byte ORA_IMMEDIATE = 0x09;
    171.     static const byte ORA_ZERO_PAGE = 0x05;
    172.     static const byte ORA_ZERO_PAGE_X = 0x15;
    173.     static const byte ORA_ABSOLUTE = 0x0d;
    174.     static const byte ORA_ABSOLUTE_X = 0x1d;
    175.     static const byte ORA_ABSOLUTE_Y = 0x19;
    176.     static const byte ORA_INDIRECT_X = 0x01;
    177.     static const byte ORA_INDIRECT_Y = 0x11;
    178.  
    179.     // PHA: 0x48
    180.     static const byte PHA_IMPLIED = 0x48;
    181.  
    182.     // PHP: 0x08
    183.     static const byte PHP_IMPLIED = 0x08;
    184.  
    185.     // PLA: 0x68
    186.     static const byte PLA_IMPLIED = 0x68;
    187.  
    188.     // PLP: 0x28
    189.     static const byte PLP_IMPLIED = 0x28;
    190.  
    191.     // ROL: 0x2a; 0x26; 0x36; 0x2e; 0x3e;
    192.     static const byte ROL_ACCUMULATOR = 0x2a;
    193.     static const byte ROL_ZERO_PAGE = 0x26;
    194.     static const byte ROL_ZERO_PAGE_X = 0x36;
    195.     static const byte ROL_ABSOLUTE = 0x2e;
    196.     static const byte ROL_ABSOLUTE_X = 0x3e;
    197.  
    198.     // ROR: 0x6a; 0x66; 0x76; 0x6e; 0x7e;
    199.     static const byte ROR_ACCUMULATOR = 0x6a;
    200.     static const byte ROR_ZERO_PAGE = 0x66;
    201.     static const byte ROR_ZERO_PAGE_X = 0x76;
    202.     static const byte ROR_ABSOLUTE = 0x6e;
    203.     static const byte ROR_ABSOLUTE_X = 0x7e;
    204.  
    205.     // RTI: 0x40
    206.     static const byte RTI_IMPLIED = 0x40;
    207.  
    208.     // RTS: 0x60
    209.     static const byte RTS_IMPLIED = 0x60;
    210.  
    211.     // SBC: 0xe9; 0xe5; 0xf5; 0xed; 0xfd; 0xf9; 0xe1; 0xf1
    212.     static const byte SBC_IMMEDIATE = 0xe9;
    213.     static const byte SBC_ZERO_PAGE = 0xe5;
    214.     static const byte SBC_ZERO_PAGE_X = 0xf5;
    215.     static const byte SBC_ABSOLUTE = 0xed;
    216.     static const byte SBC_ABSOLUTE_X = 0xfd;
    217.     static const byte SBC_ABSOLUTE_Y = 0xf9;
    218.     static const byte SBC_INDIRECT_X = 0xe1;
    219.     static const byte SBC_INDIRECT_Y = 0xf1;
    220.  
    221.     // SEC: 0x38
    222.     static const byte SEC_IMPLIED = 0x38;
    223.  
    224.     // SED: 0xf8
    225.     static const byte SED_IMPLIED = 0xf8;
    226.  
    227.     // SEI: 0x78
    228.     static const byte SEI_IMPLIED = 0x78;
    229.  
    230.     // STA: 0x85; 0x95; 0x8d; 0x9d; 0x99; 0x81; 0x91
    231.     static const byte STA_ZERO_PAGE = 0x85;
    232.     static const byte STA_ZERO_PAGE_X = 0x95;
    233.     static const byte STA_ABSOLUTE = 0x8d;
    234.     static const byte STA_ABSOLUTE_X = 0x9d;
    235.     static const byte STA_ABSOLUTE_Y = 0x99;
    236.     static const byte STA_INDIRECT_X = 0x81;
    237.     static const byte STA_INDIRECT_Y = 0x91;
    238.  
    239.     // STX: 0x86; 0x96; 0x8e
    240.     static const byte STX_ZERO_PAGE = 0x86;
    241.     static const byte STX_ZERO_PAGE_Y = 0x96;
    242.     static const byte STX_ABSOLUTE = 0x8e;
    243.  
    244.     // STY: 0x84; 0x94; 0x8c
    245.     static const byte STY_ZERO_PAGE = 0x84;
    246.     static const byte STY_ZERO_PAGE_X = 0x94;
    247.     static const byte STY_ABSOLUTE = 0x8c;
    248.  
    249.     // TAX: 0xaa
    250.     static const byte TAX_IMPLIED = 0xaa;
    251.  
    252.     // TAY: 0xa8
    253.     static const byte TAY_IMPLIED = 0xa8;
    254.  
    255.     // TSX: 0xba
    256.     static const byte TSX_IMPLIED = 0xba;
    257.  
    258.     // TXA: 0x8a
    259.     static const byte TXA_IMPLIED = 0x8a;
    260.  
    261.     // TXS: 0x9a
    262.     static const byte TXS_IMPLIED = 0x9a;
    263.  
    264.     // TYA: 0x98
    265.     static const byte TYA_IMPLIED = 0x98;
    Thanks for the respond. I have some nes rom game which I try to convert it in Unity platform (C# programming language). I have created the half game but I should understand the logic of game for example every when the enemy fly or shoot etc... If I have the source code in some high level programming language like Python, C, C++, Java then if I read it then I could understand the logic of game so that to convert it to C# for Unity. Now it is in assembly which is not understable. If I sent you the source code, then can you undrstand the logic of game or does it exist some way to convert assembly to C or C++ (or to some other high level programming language) - I don't care if the converted file is perfect converted so that to be runnable because I care to see atleast in around how to be the source code & I will understand the logic of game.

  8. #8

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,339

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

    I think it would be easier if you visualize what the game is doing and code it by hand. Getting the direct source code wont convert well to modern hardware because the code is working around the NES's hardware, which includes bank switching, something modern hardware doesn't have to do. Not to mention it would be redundant to convert every line of 6502 assembly one line at a time. Just saying.

  9. #9
    New Member
    Join Date
    May 2020
    Posts
    3

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

    Quote Originally Posted by Jacob Roman View Post
    I think it would be easier if you visualize what the game is doing and code it by hand. Getting the direct source code wont convert well to modern hardware because the code is working around the NES's hardware, which includes bank switching, something modern hardware doesn't have to do. Not to mention it would be redundant to convert every line of 6502 assembly one line at a time. Just saying.
    This is what I have done until now. I try to see and understand the behavior of each enemy and after to translate to C#. But at to point which I am now, the behavior in some points is some random and can't get accurate conclusion and for this reason I found the source code to view it but it was in assembly. I fantasize that all these tutorials which exists, and say for assembly to C or to C++, are fake ?
    The nes game is the Galaxian.

  10. #10

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,339

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

    You will most definitely run into problems during the conversion, which is why it is pointless. Collision is definitely one of them. The PPU chip in the NES already handles the collision using hardware one bit out of the 8 bits of the PPU Address register, if I remember correctly, or at least one of the PPU Addresses from $2000 to $2007, not including mirroring.

    If it is collision you are after, then I can definitely help. Currently I been designing collision algorithms for primitives no matter how fast the object goes, even if it is a billion pixels a frame, it will register a collision. I don't wanna give too much away since it is my algorithms, but lets say it involves some vector math and the use of what is called signed distance. If, for example, a circle is on one side of the wall, the signed distance is positive, but once it is on the other side, it is negative. It involves getting the normal of the segment wall (the direction the wall is facing) with some dot products:

    Code:
    VECTOR2D normal = normal.Set_Normal(segment_vector);
    float old_signed_distance = normal.Dot_Product(old_position) - (normal.Dot_Product(segment_origin));
    float signed_distance = normal.Dot_Product(position) - (normal.Dot_Product(segment_origin));
    I also use not just position but the old position of the circle. So pretend one frame of animation went by. If the old position of circle is on one side of the wall, and the new position of the circle is on the other side of the wall, and its invisible capsule (2 circles old position and new position plus the rectangle formed from the one frame movement) used instead of a ray pierced the segment in any way shape or form, a collision definitely took place, and I use some nifty trig to offset the new position with the circles radius to be against the wall. Its pretty neat actually. And I can do it with ellipses as well using vector spaces.

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