Page 1 of 3

Limitiation or compromise if write program in c

Posted: Sun Dec 31, 2017 11:36 am
by Banshaku
First, happy new year from Japan!

One of my new year resolution is to try to write something for the nes, anything is fine as long as I finish it. Since I didn't touch asm code for a while, it would take some time before getting back on track. My best guess would be to write code in C but I'm not sure what are the compromise you have to do if you decide to go that way.

I know that a few homebrew were made in C so I would like to know what are the compromise or gotcha that you have to be aware if you write most of the code in c.

Re: Limitiation or compromise if write program in c

Posted: Sun Dec 31, 2017 12:02 pm
by calima
Performance and size. Grab cc65 and neslib, and off to coding!

Re: Limitiation or compromise if write program in c

Posted: Sun Dec 31, 2017 1:45 pm
by dougeff
Passing arguments to functions is incredibly slow for the 6502. Pass 1 argument at max, it can use the a and x registers for the first argument.

I recommend the neslib. Use globals, basically everywhere.

The main advantage of C is ease in programming. I even find debugging easier in C.

Re: Limitiation or compromise if write program in c

Posted: Sun Dec 31, 2017 7:24 pm
by DRW
There are a few useful hints on this site, especially the point "Optimization":
https://shiru.untergrund.net/articles/p ... s_in_c.htm
dougeff wrote:Passing arguments to functions is incredibly slow for the 6502. Pass 1 argument at max, it can use the a and x registers for the first argument.
If the function is implemented in C, has one parameter and is declared __fastcall__, the compiler will still put the variable into the software stack first. So, functions written in C should not use parameters at all.
If the function is implemented in Assembly, then one parameter is alright. If you declare the function as __fastcall__ (which you should do with any function anyway since there's no disadvantage in doing so), it will be put into A (or, if it's two bytes wide, into A and X) and your Assembly function can decide for itself what it wants to do with it.


Besides, even with global variables, you can still simulate normal function calls with the help of macros:

Code: Select all

extern unsigned char DoSomething_Parameter1_;
extern unsigned char DoSomething_Parameter2_;
extern unsigned char DoSomething_Parameter3_;

void __fastcall__ DoSomething_(void);

#define DoSomething(parameter1, parameter2, parameter3)\
{\
    DoSomething_Parameter1_ = parameter1;\
    DoSomething_Parameter2_ = parameter2;\
    DoSomething_Parameter3_ = parameter3;\
    DoSomething_();\
}
If you program efficiently, then C should not prevent you from writing real games.

I wrote a jump'n'run in C:
http://www.youtube.com/watch?v=Eee0yurkIW4

This game has platforms, jump physics etc., so the movement functions are a bit more complex than a simple "if button up, then check for wall at position (X, Y - 1)". And still, it works completely without lags.

Some stuff should still be written in Assembly, like general functions (CopyArray*, randomizer etc.), the stuff that happens in NMI or the sprite rendering function (the one that turns the meta sprites into hardware sprites).
But apart from that, I managed to program the whole game logic in standard C and there was never a situation where I had to optimize a random part of the game to be written in Assembly.


* CopyArray, FillArray etc. should be reimplemented because the official C functions memcpy and memset use a size parameter of two bytes (type size_t). This makes those functions bigger and more complex than if you simply use a byte value as the size parameter.
After all, when do you ever need to copy or fill more than 255 bytes to an array at once?
Also, they use local parameters.

Instead, your own CopyArray could look like this:

Code: Select all

In C:

void __fastcall__ CopyArray_(unsigned char size);

#define CopyArray(destinationArray, sourceArray, size)\
{\
	CommonPointer1 = destinationArray;\
	CommonPointer2 = sourceArray;\
	CopyArray_(size);\
}

In Assembly:

_CopyArray_:

	TAY

@loop:

	CPY #0
	BEQ @end

	DEY

	LDA (CommonPointer2), Y
	STA (CommonPointer1), Y

	JMP @loop

@end:

	RTS
Do you already know what kind of game you want to program?
dougeff wrote:I even find debugging easier in C.
How do you debug the C code of an NES game?

Re: Limitiation or compromise if write program in c

Posted: Sun Dec 31, 2017 8:58 pm
by Banshaku
Performances and the uses of parameters in c was mostly what I though would be an issue so it seems my guess were right or I did remember some of the comments posted on nesdev in the past.

thank you for refreshing my memory on the subject!

I'm not expecting to write everything in C since my experience from the dos days reminds me that some part has to be written in assembler because of performance issues (any copy related method mentioned above are better in asm).
DRW wrote: Do you already know what kind of game you want to program?
Right now I have not decided yet. I always had ideas that were too technical so they went nowhere so I need to focus on something small first so I can build some base code.

There is one game I started 25 years ago under dos that I would like to make on the nes but this game too is incomplete so only the basic characters and concept would be ported. It was a double dragon style game and liked that alot in the past but now my taste changed a little bit but still would like to see something out of it.

I should start with a one screen game, it just that I never really liked those in the first place. Still, it would be a good start.

Is using other modules like either famitracker or famitone(?) more complicated with c code?

Re: Limitiation or compromise if write program in c

Posted: Sun Dec 31, 2017 9:37 pm
by DRW
Banshaku wrote:I'm not expecting to write everything in C since my experience from the dos days reminds me that some part has to be written in assembler because of performance issues (any copy related method mentioned above are better in asm).
I was surprised in the end that I only had to use generic functions in Assembly. There was never an issue where I had to write a real game-specific function (like MoveHero etc.) in Assembly.
Banshaku wrote:It was a double dragon style game and liked that alot in the past but now my taste changed a little bit but still would like to see something out of it.
My hint: Beat'em ups are always pretty boring due to their slow speed: You have to hit your opponents many times and the game prevents you from going on until all of them are defeated.
How about a fast-paced beat'em up?
Banshaku wrote:I should start with a one screen game, it just that I never really liked those in the first place. Still, it would be a good start.
I was pretty glad that my first game was not just a one screen game, but that I actually included full scrolling and realtime level loading. This way it was a mundane issue to do this for the next game.
Banshaku wrote:Is using other modules like either famitracker or famitone(?) more complicated with c code?
No. Just like you can mix and match C code with Assembly, you can also include the Assembly code of FamiTone into your own code.
You might want to create a little Assembly file that wraps the other file.


Besides, the FamiTracker driver itself should probably not be used. The code is very big (about 5 KB), the music files will become big, it doesn't have support for sound effects and I assume it's not very efficient and you need the CPU time for other things.

If FamiTone is not too limited for your music compositions, it's pretty good for games.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 1:20 am
by Banshaku
I have an idea for a multiple player game, more or less a beat-em-up, but I need to define what the characters can do and do quick prototype to see how interesting it would be. Maybe this would be the chance to build it in C and test how it goes.

As for Famitone, I'm not a very talented artist music wise so I do not use much effects so I guess I would have no issue with the famitone requirements. I was using up to now FT 2.7 driver (which is smaller) but the issue was sound effects.

When I have something working I will be more than happy to share it. For now, it's just a new year resolution. The other reason for C is to be able to port it to other platform but maybe with the nes limitation (for c code) it may just be wishful thinking only.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 4:29 am
by calima
I have ported NES games written in C to Genesis and GBA without much issue.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 6:45 am
by DRW
Banshaku wrote:I have an idea for a multiple player game, more or less a beat-em-up, but I need to define what the characters can do and do quick prototype to see how interesting it would be.
How about a first attempt of a real "Street Fighter II"-like fighting game instead of a multiplayer versus version of "Double Dragon"? Maybe with small 16 x 40 characters in the first step. (Huge characters with many animation frames would require MMC3 for instant change of the CHR values.)
Banshaku wrote:The other reason for C is to be able to port it to other platform but maybe with the nes limitation (for c code) it may just be wishful thinking only.
The limitations for C code (like using global variables instead of parameters) are nothing that go against standard C, so you can use the code for everything else as well. It might be considered bad programming style in another context, but it's still all valid C.

What might be a problem when it comes to porting: There's probably less abstraction in NES games.

For example, my characters on screen contain a pointer variable to a meta sprite that is the currently drawn animation frame. And the data that the pointer points to is an array of tile values from the CHR ROM.
So, I don't have abstraction values for PlayerStanding, PlayerWalking1, PlayerWalking2, PlayerWalking3 etc. Instead, it points directly into the data itself and the UpdateSprites function uses the pointer for buildup.

On another platform, your meta sprites definitions would probably not consist of a bunch of tile values, but each animation would maybe be a bitmap file. (And if you write clean code, your value in the game logic would just be an index for a lookup array, specifically so that you can switch the underlying data without having to change the game logic code. But for the NES, this abstraction layer would cost ROM space and CPU time.)

So, you would have to redefine the pointer to an array of byte values into a string variable that hold the bitmap file name. Or an integer that holds the Windows bitmap resource ID. (Or an integer that holds the index to an array of implementation-defined data.)

Same with the background buildup:

When it's time to update the background, the game logic fills an array with data that's very close to how the PPU structures its data, so that the NMI can process the array as fast as possible.
You probably don't have the same tile-by-tile-with-values-from-CHR buildup in a PC game unless you actually recreate the behavior of the NES PPU. So, you would have to rewrite these parts and they might be mixed with your regular game logic code.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 9:58 am
by tepples
DRW wrote:My hint: Beat'em ups are always pretty boring due to their slow speed: You have to hit your opponents many times and the game prevents you from going on until all of them are defeated.
I'd bet a lot of the scroll stop behavior in these games derives from CHR address space and palette limits, with two players and two enemy types allowed to be loaded at once. The other option is pop-in, where enemies just blink into existence once all enemies of a particular type are defeated.
DRW wrote:How about a fast-paced beat'em up?
Once it gets fast enough, it stops feeling like a beat-em-up in favor of another genre entirely. I never thought of Haunted: Halloween '85 as any more of a beat-em-up than, say, Super Mario 64.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 10:43 am
by Banshaku
DRW wrote:[
How about a first attempt of a real "Street Fighter II"-like fighting game instead of a multiplayer versus version of "Double Dragon"? Maybe with small 16 x 40 characters in the first step. (Huge characters with many animation frames would require MMC3 for instant change of the CHR values.)
What I had in mind was not a double dragon like fighting game but more a fast paced arena like fighting with traps, wall jumping, maybe weapons (or some characters have weapon) either one or one or one to many (depending of limit with four score). The concept is raw and to make it interesting, you have to build interesting battle ground that allow strategy to attack the other player.

The idea is still raw but this is maybe an idea that came out from a screenshot I saw from another game (never saw the game play though) and though it could be interesting on the nes, if the game play is fast enough. This means small characters and interaction with the environment.

As for porting, it was more for other platform like sms, pce etc. The logic may be portable (to some degree) but to abstract the hardware may not be possible so I guess most code must be adapted for the platform anyway.

Once I have some concept I will try to share it. For now I didn't but anything on paper yet. What is the most important is the battleground and make interesting fighters that are balanced.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 10:59 am
by dougeff
How do you debug the C code of an NES game?
Let's say you want to do something like this...

If (some_flag && (x_position>25) && (direction == left)) {
do_something();
}

this is much easier to understand than the equivalent asm, which would be about 10-15 lines long. And, I have it compile and add the C source to the asm file. If, there's a problem, I can set a breakpoint, and step through the code, and look at the asm file (with C source), and still know what's going on.

cc65 command line directive --add-source

even coming back to this code a year later, it would only take an instant to comprehend it.

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 12:47 pm
by tepples
Banshaku wrote:As for porting, it was more for other platform like sms, pce etc. The logic may be portable (to some degree) but to abstract the hardware may not be possible
I'm not entirely sure to what extent the logic would be efficiently portable.

Z80, MC68000, and MIPS have indexing modes with a large (16- to 32-bit) pointer and a small (at least 7-bit) constant offset. This leads to a preference for an "array of structures" (AOS) organization of data, where all fields related to one object are consecutive in memory. For example, the X and Y coordinates of an object might be at $F000 and $F002, while the X coordinates of two different objects might be at $F000 and $F020. In C, it'd look like this:

Code: Select all

#define NUM_ACTORS 16
struct Actor {
  uint16_t x, y;   // displacement
  int16_t dx, dy;  // velocity
  uint8_t health, actor_type, frame_timeleft, frame;
  // et cetera
};
struct Actor actors[NUM_ACTORS];
Then a program can keep a pointer to the current actor structure, which C++ calls this, in a machine register and access fields using each field's offset. In Z80 (not 8080/8085 or LR35902), this is IX; in 68000, this may be A0; and in MIPS, this is the first argument register ($4 aka $a0). Indexing is a bit trickier on 8080/8085 and LR35902: a pattern shown in this post is to pad each struct to a power of two bytes and aligning it to its "natural" boundary so that you can access fields of the object pointed to by HL by doing math on the lower bits of L, leaving H and the upper bits of L alone. I wonder what practical C compilers for 8080/8085/LR35902 do, or what Z80 C compilers do if IX and IY are already occupied.

The fast indexing modes in the 6502, by contrast, add a small (8-bit) variable offset to a large (16-bit) constant pointer. This leads to a "structure of arrays" (SOA) organization, which places values of one field of several objects in consecutive addresses. So you'd get X and Y coordinates of an actor at $F010 and $F030, but X coordinates of multiple actors at $F010, $F011, $F012, ...

Code: Select all

#define NUM_ACTORS 16
uint8_t actor_xsub[NUM_ACTORS];
uint8_t actor_x[NUM_ACTORS];
uint8_t actor_ysub[NUM_ACTORS];
uint8_t actor_y[NUM_ACTORS];
uint8_t actor_dxsub[NUM_ACTORS];
int8_t actor_dx[NUM_ACTORS];
uint8_t actor_dysub[NUM_ACTORS];
int8_t actor_dy[NUM_ACTORS];
uint8_t actor_health[NUM_ACTORS];
uint8_t actor_type[NUM_ACTORS];
uint8_t actor_frame_timeleft[NUM_ACTORS];
uint8_t actor_frame[NUM_ACTORS];
C is traditionally used on machines with large pointer+small offset, which prefer an array of structures. I'm not aware of any C implementation (or even any high-level language in the first place) that automatically translates code to the structure-of-arrays approach. Is there a good way to use the C preprocessor to output both structure-of-arrays and array-of-structures code from one source file?

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 1:34 pm
by DRW
A little stylistic suggestion: I would still put these values into a struct:

Code: Select all

/* In a .h file: */

#define NUM_ACTORS 16

struct Actors
{
    uint8_t xsub[NUM_ACTORS];
    uint8_t x[NUM_ACTORS];
    uint8_t ysub[NUM_ACTORS];
    uint8_t y[NUM_ACTORS];
    uint8_t dxsub[NUM_ACTORS];
    int8_t dx[NUM_ACTORS];
    uint8_t dysub[NUM_ACTORS];
    int8_t dy[NUM_ACTORS];
    uint8_t health[NUM_ACTORS];
    uint8_t type[NUM_ACTORS];
    uint8_t frame_timeleft[NUM_ACTORS];
    uint8_t frame[NUM_ACTORS];
};

extern struct Actors actors;

/* In a .c file: */

struct Actors actors;
This way, you don't need to declare every single variable in a C file and the same set of variables as extern in a header file.

(Also, I like to typedef unsigned char to byte and signed char to sbyte.)

Re: Limitiation or compromise if write program in c

Posted: Mon Jan 01, 2018 5:31 pm
by DRW
Banshaku wrote:What I had in mind was not a double dragon like fighting game but more a fast paced arena like fighting with traps, wall jumping, maybe weapons (or some characters have weapon) either one or one or one to many (depending of limit with four score).
Something like "Nekketsu Kakutou Densetsu"?