It is currently Sun Nov 19, 2017 5:31 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Sep 03, 2017 10:33 am 
Offline

Joined: Thu Mar 23, 2017 11:23 am
Posts: 24
Just read about NES architecture and it's... not unusually for me.
So, i want start developing games for NES, but don't know starting point.
I have C, C++ skills, have some experience in x86 asm.
So, from what i should start learning coding for NES?


Top
 Profile  
 
PostPosted: Sun Sep 03, 2017 1:32 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1460
I myself started with the Nerdy Nights tutorial for general knowledge and low level stuff.
http://nintendoage.com/forum/messagevie ... eadid=7155

Be careful: Some information is wrong or poorly explained. You basically never get your program to work if you just follow the instructions. You also always have to download the source code file to notice that it contains a good bunch of stuff that wasn't explained in the actual tutorial at all.

When I was able to do that stuff, I switched to cc65 and converted my program to that assembler.
Then I programmed most of the game logic using C, only using Assembler for hardware-specific stuff, general purpose functions and time-critical things.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Sun Sep 03, 2017 4:01 pm 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 866
Location: Sweden
Nerdy Nights is where I started too. With all it's flaws, it's still the best NES development tutorial out there for beginners.
It should teach you everything you need to know to make your first NES program and some basics. Then you can learn the rest from this forum and the Nesdev wiki.

I prefer programming in assembly. I eventually switched assembler from NESASM to the more popular ASM6.


Top
 Profile  
 
PostPosted: Sun Sep 03, 2017 5:16 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10113
Location: Rio de Janeiro - Brazil
I find it easier to code for the NES in assembly because you have full control of everything that goes on, while using C means relying on some hidden library code you won't understand unless you know assembly anyway, and you often have to tailor your C code in not necessarily intuitive ways in order to produce ASM code that's not slow or buggy, so you end up not only having to learn how ASM works, but you also have to study a whole lot about the C->ASM process and the pre-made libraries if you expect to make anything beyond trivial.

But I guess there are beginner guides for both C and ASM, so maybe you should try both out and see what works best for you.

Pokun wrote:
I eventually switched assembler from NESASM to the more popular ASM6.

I don't think that ASM6 is more popular than NESASM... It certainly is less restrictive than NESASM, so it gets recommended a lot as an alternative when people run into the pitfalls of NESASM, since both are equally easy to use. There's one really important thing that ASM6 lacks though, which's the ability to get bank numbers from labels (NESASM does it with severe limitations, ca65 does it better). I guess it shouldn't be hard to add a "bank" directive that would cause labels to inherit whatever value was last set, and a function to read that value back, but I can't follow ASM6's source for shit.


Top
 Profile  
 
PostPosted: Sun Sep 03, 2017 5:52 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1460
tokumaru wrote:
and you often have to tailor your C code in not necessarily intuitive ways in order to produce ASM code that's not slow or buggy, so you end up not only having to learn how ASM works, but you also have to study a whole lot about the C->ASM process and the pre-made libraries if you expect to make anything beyond trivial.

That's nonsense. Yes, there are a few things to keep in mind, like using global variables instead of locals or parameters or creating an array of a struct as a struct with arrays. But it's not like your C code would look totally outlandish in the end.
I programmed a whole jump'n'run and my code still looks pretty clean and C-like.

Writing C can still be a ton easier than writing Assembly directly.

If you can program Assembly well enough, that's of course always better.

But someone who can write fluently in C, but only very slowly in Assembly:
Don't tell those people that the adjustments that you have to make in the C code to make it efficient enough for NES programming are such a huge disadvantage and such a code uglyfier that they're better off writing Assembly directly. Because that's simply not true.
Don't tell people that they can only either program clean Assembly code or butt-ugly, unintuitive, unreadable C code that's so shitty and energy-taking that they have less trouble using Assembly right away. Because this is clearly nonsense.

For someone who is good in C, but slow in Assembly, C is still a huge help for NES programming. And no, not only for "Pong" or "Space Invaders". It has been proven several times that you can make a real game for the NES by using mostly C:
http://www.youtube.com/watch?v=Eee0yurkIW4

Even if you need to use Assembly for the really time-critical stuff or for understanding what's going on under the hood: There's still a difference whether you force yourself to write an UpdateSprite or ProcessNmi function in Assembly or if you have to code the whole game logic and simply eveything in Assembly.

In the following, I will present you my movement function for the most basic opponent of my game. Please tell me:
Is the function so ugly and un-C-like and tailored in unintuitive ways that you would usually never use in C, so that I would have been better off writing it in Assembly directly? Or is this pretty much regular C for the most part, so that somebody who knows C but can program in Assembly only very slowly really has an advantage in using C?

Let's see if my function will convince a C coder to switch to Assembly because he considers the code to be such an abomination that he sees no use in programming C on the NES and prefers to become a master in Assembly programming instead because he considers this the easier way.

Code:
/* The most basic opponent who
   just walks left and right. */
void __fastcall__ MoveGoon(void)
{
   /* If the Goon is stunned, */
   if (Npcs.IsStunned[IndexChr])
   {
      /* we decrement the stunned counter.
         The Goon cannot escape from the stun.
         The counter is just there to make
         the vibrating movement in "Level.c". */
      --Chrs.StunnedCounter[IndexChr];

      /* If he's stunned,
         nothing else is done. */
      return;
   }

   /* Often used array variables are put
      into temporary variables because
      accessing them is smaller and faster. */
   MoveGoonX = Chrs.X[IndexChr];
   MoveGoonFramesForNextWalkingAnimation =
      Npcs.GoonFramesForNextWalkingAnimation[IndexChr];

   /* If it is declared that
      the Goon leaves the screen,
      then nothing can stop him. */
   if (Npcs.GoonMovesOutOfScreen[IndexChr])
      MoveGoonX -= GoonMovementPixelsLeft;
   /* Movement to the right and
      to the left are done differently.
      We first check for right. */
   else if (Chrs.Direction[IndexChr] == DirectionRight)
   {
      /* We calculate the new position
         of the right border side.
         We use CharacterFullBorderRight
         because the Goon's drawn size is
         bigger than his bounding boxes. */
      MoveGoonNewPositionRight =
           MoveGoonX
         + (CharacterFullBorderRight + GoonMovementPixelsRight);

      /* If the new position is outside the screen or over a gap, */
      if (MoveGoonNewPositionRight > 255
       || GetOnScreenLevelDataValue(MoveGoonNewPositionRight) == 0)
      {
         /* then we change the direction. */
         Chrs.Direction[IndexChr] = DirectionLeft;

         /* We reset the frames for the next
            walking animation since left and
            right have different values. */
         MoveGoonFramesForNextWalkingAnimation =
            GoonFramesPerWalkingAnimationLeft;

         /* And we immediately let the Goon
            walk the new distance. */
         MoveGoonX -= GoonMovementPixelsLeft;
      }
      /* If the Goon doesn't reach
         a border, we just let him
         continue walking. */
      else
         MoveGoonX += GoonMovementPixelsRight;
   }
   /* Movement to the left. */
   else
   {
      /* We check the new left border position. */
      MoveGoonNewPositionLeft =
           MoveGoonX
         + (CharacterFullBorderLeft - GoonMovementPixelsLeft);

      /* And we check for a position a bit
         to the right of the Goon.
         This way we check whether the room
         where movement is possible for
         him has become very small.
         That room is exactly 2/3
         of CharacterWidth. */
      MoveGoonNewPositionRight =
           MoveGoonX
         + (CharacterFullBorderLeft + 3 * CharacterWidth / 2);

      /* If that certain right position is not a gap
         and the Goon's new left position would be
         either outside the screen or be a gap, */
      if (GetOnScreenLevelDataValue(MoveGoonNewPositionRight) != 0
       && (MoveGoonNewPositionLeft < 0
        || GetOnScreenLevelDataValue(MoveGoonNewPositionLeft) == 0))
      {
         /* then we switch the Goon's direction. */
         Chrs.Direction[IndexChr] = DirectionRight;

         /* Just like in the code above,
            the walking animation frames
            are reset. */
         MoveGoonFramesForNextWalkingAnimation =
            GoonFramesPerWalkingAnimationRight;

         /* And the Goon is moved. */
         MoveGoonX += GoonMovementPixelsRight;
      }
      else
      {
         /* If the Goon's new left position
            would be outside the screen
            and that specific right position
            is a gap, i.e. if the room
            where the Goon can move
            has become very small, */
         if (MoveGoonNewPositionLeft < 0
          && GetOnScreenLevelDataValue(MoveGoonNewPositionRight) == 0)
         {
            /* then leaving the screen is a given. */
            Npcs.GoonMovesOutOfScreen[IndexChr] = true;
         }

         /* The movement is done. */
         MoveGoonX -= GoonMovementPixelsLeft;
      }
   }

   /* The movement animation is done.
      If the frame counter is at zero, */
   if (--MoveGoonFramesForNextWalkingAnimation == 0)
   {
      /* we get the next animation
         phase from the array
         and apply it to the meta sprite. */
      Chrs.MetaSprite[IndexChr] =
         GoonWalkingMetaSprites
         [
            Npcs.GoonWalkingIndex[IndexChr] =
               (Npcs.GoonWalkingIndex[IndexChr] + 1) & BitMask(GoonWalkingMetaSprites)
         ];

      /* And we reset the counter,
         depending on the direction. */
      MoveGoonFramesForNextWalkingAnimation =
         Chrs.Direction[IndexChr] == DirectionRight
            ? GoonFramesPerWalkingAnimationRight
            : GoonFramesPerWalkingAnimationLeft;
   }

   /* The temporary variables
      are put back into their
      original arrays. */
   Chrs.X[IndexChr] = MoveGoonX;
   Npcs.GoonFramesForNextWalkingAnimation[IndexChr] =
      MoveGoonFramesForNextWalkingAnimation;
}

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Sun Sep 03, 2017 6:24 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1823
Location: DIGDUG
I think first thing is to understand what a valid NES ROM looks like. Early games (super mario bros) look like this...

16 byte header (look up iNES)
32768 bytes of program code (0x8000)
8192 bytes of graphics (0x2000)

the program code will load into the CPU at address 0x8000 and run through 0xffff. The last 6 bytes are pointers to system interrupts, and RESET (the start of the program). (look up vectors)

the RAM runs from 0 to 0x7ff. 0x100 to 0x1ff is the hardware stack.

Use a tile editor program (like YY-CHR or Tile Editor Pro) to make the graphics.

Use an assembler (nesasm, asm6, or ca65) to assemble the ASM code and make a .nes file. You would write the code in something like notepad or notepad++.

The most basic lesson would be constructing a valid .nes file, where an emulator loads it correctly, and it actually runs code. Get an emulator with a good set of debugging tools. I use FCEUX (and apologize to all the people making new emulators). The important tools are a hex editor, where you can see all the RAM values...PPU viewer, where you can see the graphics...and debugger, where you can freeze the execution of the program and step through the code.

There are like 1000 tiny but important things you need to learn to get anything to work right...for example, you can't (shouldn't) transfer data to the PPU while rendering is on (register 0x2001, sprite enable (s), background enable (b)).

Then you can learn what the NMI interupt is, what the V-blank is, where the palette is, how to write to the screen, how to put sprites on the screen, how to set up a stable loop, how music works, etc.

You write to the screen (registers 0x2006 and 2007) by sending data to PPU addresses 0x2000 - 0x23ff (nametable #0).

You have to set the scroll (registers 0x2005) (and write to 0x2000, to make sure correct nametable is selected), after writing to 2006/2007. Then turn rendering on.

Good luck. Ask any questions here.

(edits made)

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sun Sep 03, 2017 7:52 pm 
Offline

Joined: Sun Feb 07, 2016 6:16 pm
Posts: 299
dougeff wrote:
I use FCEUX (and apologize to all the people making new emulators)
I am greatly offended by the fact you're using what you're familiar with!
Jokes aside, if there is any reason you prefer FCEUX as your debugger (other than it being what you're familiar with), I'm always looking for feedback :)


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 1:18 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 356
If you can code fluently in C, take that route first. Check neslib, it's user friendly and will help getting your first stuff going quickly. Then you can start getting more hardcore. You'll eventually end up learing assembly, modifying neslib or getting rid of it, and replacing portions in your game with assembly equivalents.

Anyway, my only suggestion is that you have fun.

_________________
http://www.mojontwins.com


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 2:48 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 866
Location: Sweden
You use C only to improve development speed for the cost of performance, not because you don't want to bother learning assembly neither as a stepping stone to learning assembly. You need to learn assembly anyway, so I suggest starting with Nerdy Nights and learn the basics of assembly and the NES hardware. Then you can choose if you want to go the C route like DRW did, or the pure assembly route like I did.


tokumaru wrote:
I don't think that ASM6 is more popular than NESASM... It certainly is less restrictive than NESASM, so it gets recommended a lot as an alternative when people run into the pitfalls of NESASM, since both are equally easy to use.
My impressions is that ASM6 and CA65 are the two most popular assemblers on this forum, but maybe that's only among the regulars.


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 3:06 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1460
Pokun wrote:
You use C only to improve development speed for the cost of performance, not because you don't want to bother learning assembly neither as a stepping stone to learning assembly. You need to learn assembly anyway, so I suggest starting with Nerdy Nights and learn the basics of assembly and the NES hardware.

That's right. You should always start with Assembly first, so that you know what the NES does. And so that you are capable of writing single Assembly functions here and there, even if the majority of your code is still in C.
Once you're able to render a background and move a sprite in Assembly, you can start adding C to your code.

C is merely a help to make it easier for you, not to avoid the low level stuff completely. But it's still a huge help because it frees you from the burden of having to painstakingly write every single function (like game physics etc.) in low level processor code.

For example in my case, I'm totally capable of reading and writing Assembly code.
My problem is just that it takes much, much longer for me to write a function in Assembly and I have to concentrate much more if I try to read such a function and play out in my head what it does.
So, my sprite rendering function (the function that puts the hardware sprites on screen) is still written in Assembly to make it as tight as possible. But a game-specific, character-specific movement function (how a character reacts when he moves agains a wall etc.), that's done in C.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 3:37 am 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1012
Location: Gothenburg, Sweden
A perspective from someone who's never taken a single cs class: Assembly language in itself isn't hard to learn (besides a few 6502 specific quirks). The NES hardware - especially the PPU and the APU -, on the other hand, is. And one'd need to learn that part regardless of writing C or ASM.

One thing i do find difficult with assembly is reading code and immediately understanding what it does. A function in a higher language can often be self-explanatory because it does what it says in a few lines. Assembly language can take quite a bunch of lines to disclose what it really does besides pushing bits and nibbles and bytes here and there. This makes commenting even more important, imo.

_________________
http://www.frankengraphics.com - personal NES blog


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 12:58 pm 
Offline

Joined: Sun May 03, 2015 8:19 pm
Posts: 89
Macros, descriptive function names, and good comments make things insanely more readable


Image


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 3:09 pm 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 356
Well, I cheated 'cause I'm a computer engineer and know how computers work and had to learn assembly for several architectures during my studies. That surely helps. Not 6502 (or Z80) though. Anyways, 6502 assembly is probably the most human friendly, human readable, and human writeable of them all (save for maybe 68K). I just think that starting by writing a game in assembly is too harsh for a newcomer, at least it was for me. I enjoy making games and getting results. If I had taken the hard path I wouldn't have finished any.

My first NES game was Sir Ababol. Learning the NES specifics was hard enough for me, so I didn't want to add more difficulty to the mix, that's why I chose to code it in C + neslib. The game was short, easy and inefficient, but at least I managed to finish it.

Now I've reached a point where I can read and write 6502 assembly. I can modify and expand the core library I still use (neslib) or rewrite parts of my games in assembly. In the meantime, I've produced a number of games which have kept me happy.

Besides, I *really* enjoy the act of trying and produce the most efficient C code, learning how to make the compiler behave, and stuff like that. I guess everybody's motivation is different :)

_________________
http://www.mojontwins.com


Top
 Profile  
 
PostPosted: Mon Sep 04, 2017 5:41 pm 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 866
Location: Sweden
Yeah programming the NES is harsh, you have to be prepared that there is a lot you need to learn. Both 6502 and the NES hardware (although there is no need to focus on the parts not currently needed). But he said he has experience in C/C++ and x86 assembly (which is more than I had when I started with NES), so I think he should be totally fine if he spends some time on it.

You don't have to make a game right away though. I played around a lot with the hardware making various more or less useless apps. But when making a game it isn't that different from doing it in for example C. The game programming logic is the same. Game programming was also something I had to learn though (and is still learning) as I had no experience with programming graphical games before I started with NES programming.


Top
 Profile  
 
PostPosted: Tue Sep 05, 2017 2:33 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19230
Location: NE Indiana, USA (NTSC)
Sour wrote:
dougeff wrote:
I use FCEUX (and apologize to all the people making new emulators)
I am greatly offended by the fact you're using what you're familiar with!
Jokes aside, if there is any reason you prefer FCEUX as your debugger (other than it being what you're familiar with), I'm always looking for feedback :)

FCEUX is familiar.

When I started seriously[1] developing for NES in the fourth quarter of 2008, the big choices were FCEUX and Nintendulator. I had to pick, and Nintendulator had two drawbacks.

First, FCEUX came in two editions, both of which worked for me. The non-debugging was natively ported to GNU/Linux using SDL. And the debugging version worked in Wine, an environment for running Windows programs on x86 operating systems not made by Microsoft. Nintendulator at the time had serious problems with the input configuration dialog on non-Microsoft operating systems (allegedly fixed a year ago). In any case, FCEUX developers felt responsive to my reports of bugs that appear only in Wine, as opposed to developers of some other popular in the NES scene.[2] Often, it revealed reliance on undefined or unspecified behavior of a Win32 API call.

Second, FCEUX runs fine even on old or mobile-spec hardware. For seven and a half years, I used a Linux netbook as my primary PC for NESdev, and things like Nintendulator and bsnes-plus couldn't hold anywhere near 60 fps. I'm no longer using that laptop since its third rechargeable battery stopped holding a charge, and I don't want to try buying a fourth battery for an over seven-year-old PC.

I'll have to try Mesen sometime.


[1]I started back in the late 1990s, when NESticle, loopyNES, and NESten were hot $#!+. Only once I bought a PowerPak did I become truly serious about the NES.

[2] HertzDevil of 0CC-FamiTracker has passed the buck for Wine compatibility to the Wine developers, who have accepted the issue as Bug 40416. But Wine is distributed in a manner that it can take up to three years for improvements to trickle down to users.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group