This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Making a NES game in C++

Making a NES game in C++
by on (#182128)
Hi. So I've seen I couple of videos of Jason Turner where he makes a Commodore 64 game in C++17. And now while watching his Cppcon talk it just clicked. He uses i386 to 6502 translator but NES has this CPU! So I think it is possible to use his approach to game NES games in C++.

I'm certainly interested in this because I hate assembler and love C++ and I want to know how NES works to enhance my NSF player.

Thoughts?
Re: Making a NES game in C++
by on (#182153)
It's a cute demo, but I don't think this approach would work for projects much larger than the Pong that he was doing.
Re: Making a NES game in C++
by on (#182181)
FaTony wrote:
I'm certainly interested in this because I hate assembler and love C++ and I want to know how NES works to enhance my NSF player.

Thoughts?

C++ not so much, unless you find a compiler for it. But since object orientation isn't really necessary for an NES game anyway, you can program in C. Just get cc65.

There are still a few things that you need to do in Assembly, but the majority, i.e. the whole game logic, can be done in C.

And don't let people tell you that you can only do tiny, slow games in C.
My upcoming game "City Trouble" is a quick jump'n'run with up to four characters on screen at once and I still have plenty of rendering time (about 40% or so) that I could fill with something before a lag would happen.
And about the space: If it wasn't for the fat voice sample and a good bunch of dialog texts, I would still have tons of ROM space in a standard 32 KB cartridge, so the fact that compiled C code is larger than hand-written Assembly code is not that much of an issue if you keep some things in mind.

So, yeah, it's definitely possible to write a decent game in C and only bother with Assembly for low level stuff: Initializing the NES, updating the screen during vblank, updating the sprites and maybe some general purpose functions like a memcpy version, randomizer etc.
Re: Making a NES game in C++
by on (#182213)
FaTony wrote:
Hi. So I've seen I couple of videos of Jason Turner where he makes a Commodore 64 game in C++17. And now while watching his Cppcon talk it just clicked. He uses i386 to 6502 translator but NES has this CPU! So I think it is possible to use his approach to game NES games in C++.

I'm certainly interested in this because I hate assembler and love C++ and I want to know how NES works to enhance my NSF player.

Thoughts?

Before "loving" and "hating" things you need to get a deeper understanding on them it seems. (You probably hate assembly, not assembler, as the assembler is the program compiling (*) assembly langage into binary form).

You liking high level language better than assembly is just fine. However you should figure that C++ is just one high level language among a pool of thousands, and that C++ in particular is a weirdo in the sense it is, basically, the C language added with features from every single other language in existance and that this turned into the monster feature-creeped language called C++. This has advantages and drawbacks. The advantage is that if there is any feature you like in any high-level programming language, it is doable in C++ rather easily. Another advantage is that there is multiple ways to do something, so you can just do it the way you like best. The inconvenient is that it's almost impossible to deeply know the C++ language well (unless it's your speciality and that you're doing exclusively that in computer science), and another inconvenient is that it's very easy to be lost and not do exactly what you wanted to do, for instance leaking memory.

Nevetheless I think you should look into other high level programming language first. I made a post listing all realistic possibilities to programm the NES using any high level language. I did not test much of those possibilities, nor would I have any serious idea in what is the best suitable for a game. In addition to what is listed, anyone could make up other tools, but this is hard, time consuming, and error prone.

Finally, you should understand that the NES is a limited platform and that even if you do no want to code a full game in pure assembly, knowing assembly and coding critical parts in assembly will be required.

(*) Some purist would say "assembling" and refuse the term "compiling" in this particular domain
Re: Making a NES game in C++
by on (#182217)
DRW wrote:
C++ not so much, unless you find a compiler for it. But since object orientation isn't really necessary for an NES game anyway, you can program in C. Just get cc65.


No. I can can't handle C with it's pointers and manual memory management. I'm tired of writing low level stuff by hand. That's why I use C++.

Bregalad wrote:
Nevetheless I think you should look into other high level programming language first. I made a post listing all realistic possibilities to programm the NES using any high level language. I did not test much of those possibilities, nor would I have any serious idea in what is the best suitable for a game. In addition to what is listed, anyone could make up other tools, but this is hard, time consuming, and error prone.


C++ is by far the best language I have worked with. Here's what I love about C++:
  • Has an ISO standard
  • Not owned by a single company
  • Has free implementation
  • Runs on hardware without any VM
  • Has lots of high level features
  • Static typing
  • Has a huge standard library

I haven't seen a single language that comes close.
Re: Making a NES game in C++
by on (#182218)
FaTony wrote:
No. I can can't handle C with it's pointers and manual memory management. I'm tired of writing low level stuff by hand. That's why I use C++.

Believe me: You won't need pointers and memory management when you write an NES game. If you can imagine a scene where you would need malloc or new in an NES game, then you have done something wrong. You shouldn't use the heap at all. And even stack variables should be used at a minimum.
The key to success in writing NES games in C (or C++) is: Global variables. This might be bad style for PC programs, but necessary for an NES game to keep ROM space and execution time small.
Therefore, the advantages that C++ has over C couldn't be used in an NES game anyway.

For example, when a new opponent appears, you wouldn't use vector.push_back to put it to the list of opponents. You would have a global fixed-size array with the maximum number of possible opponents and you would simply use the next free slot in this array.

FaTony wrote:
C++ is by far the best language I have worked with. Here's what I love about C++:
  • Has an ISO standard
  • Not owned by a single company
  • Has free implementation
  • Runs on hardware without any VM
  • Has lots of high level features
  • Static typing
  • Has a huge standard library

All of this is also true for C.
I'm surprised that you didn't mention object orientation and templates. Because those are the major things that really make it distinct from C.

But those should not be used when creating an NES game.

Templates are a waste of space anyway.

And object orientation: Each function in the style of object.Function() internally works like Function(&object). And you should avoid function parameters as much as possible anyway. So, for an NES game, you would need to use functional programming instead of object oriented one since methods in classes always work with a pointer/reference to the object that called the function.

And about the standard library: You won't need this when programming an NES game. The only useful thing is maybe memcpy and the like, but you're better off writing your own version in Assembly. A version that only takes a byte value as the length indicator instead of size_t since you will rarely need to copy more than 255 bytes at once and size_t only complicates things.
Re: Making a NES game in C++
by on (#182219)
DRW wrote:
For example, when a new opponent appears, you wouldn't use vector.push_back to put it to the list of opponents. You would have a global fixed-size array with the maximum number of possible opponents and you would simply use the next free slot in this array.


I guess that'll be std::array<std::optional<Enemy>, N> inside an object which will have a push_back member function.

DRW wrote:
I'm surprised that you didn't mention object orientation and templates. Because those are the major things that really make it distinct from C.


That goes under "Has lots of high level features". I love OO and templates. About 70% of all my code is templates. I've made a few libraries that are 100% templates.

DRW wrote:
Templates are a waste of space anyway.


That video shows how templates can be zero overhead.

DRW wrote:
And object orientation: Each function in the style of object.Function() internally works like Function(&object). And you should avoid function parameters as much as possible anyway. So, for an NES game, you would need to use functional programming instead of object oriented one since methods in classes always work with a pointer/reference to the object that called the function.


That can be optimized away.
Re: Making a NES game in C++
by on (#182220)
The problem, as usual, is one of finding someone with the detailed knowledge of both the C++ standard and the 6502 architecture to create a Sufficiently Smart Compiler.
Re: Making a NES game in C++
by on (#182221)
FaTony wrote:
I guess that'll be std::array<std::optional<Enemy>, N> inside an object which will have a push_back member function.

Well, the first problem is: Even an array of a struct is slow. In C for the NES, you'll have to do something like this:
Code:
struct EnemyData
{
    int X[EnemiesNumber];
    int Y[EnemiesNumber];
    byte Energy[EnemiesNumber];
};

struct EnemyData AllEnemyData;

instead of this:
Code:
struct EnemyData
{
    int X;
    int Y;
    byte Energy;
};

struct EnemyData AllEnemyData[AllEnemiesNumber];


The first version, despite unintuitive, is much more effective.
And there goes your object orientation, templates and dynamic arrays. Because these kinds of things work like the below code and are therefore ineffective.

FaTony wrote:
That video shows how templates can be zero overhead.

Not overhead, but additional space because, as far as I know, two instances of the same template class, each with a different template type, are copied twice into the executable. Or did they change this in the meantime?

FaTony wrote:
DRW wrote:
And object orientation: Each function in the style of object.Function() internally works like Function(&object). And you should avoid function parameters as much as possible anyway. So, for an NES game, you would need to use functional programming instead of object oriented one since methods in classes always work with a pointer/reference to the object that called the function.


That can be optimized away.

How? In object orientation, the function is written to be able to be called by any object of the class. MyClass::MyFunction cannot know if it is called by MyClass a or by MyClass b, therefore, whenever this is referenced, it is referenced as a pointer. This means: Indirect access. Which means: More execution time than if you simply access a fixed location in memory.
Re: Making a NES game in C++
by on (#182222)
Interesting fact. The NES uses (essentially) the same processor as the Atari. 1977. 8-bit. Super slow.

C++ compilers today worry about ...should this be 32bit code or 64bit code.

None of them would be of any use toward 8-bit code for a 40 year old processor.

There are no compilers that will do what you want, so the whole 'C++ is better than C' argument is moot. We have a C compiler and we have assembly. That's it.
Re: Making a NES game in C++
by on (#182223)
What I'm also trying to tell him: Even if there was a full ISO Standard C++ compiler for the NES, the code in the end would look basically identically to the corresponding C code because you wouldn't be able to use all the C++-specific functions anyway because it would eat up too much ROM space and too much CPU time. The only real advantage of a C++ compiler would be:

Inline functions instead of macros. (You would still need macros for tons of other things, though.)

Local variables can be declared everywhere in the function instead of only at the start of a block.

void Function() is parameterless, unlike in C where void Function() means you can pass any parameter to this function and you have to write void Function(void) for a parameterless function.

Those are the only real C++ advantages over C that I can think of right now that you can also use in an NES game. But std::array? :lol: Forget it.
Re: Making a NES game in C++
by on (#182224)
Quote:
We have a C compiler and we have assembly. That's it.

No, there's plenty of other mid-level langauges that are in competition with C, see the post I linked to in my 1st post in this thread.

Quote:
No. I can can't handle C with it's pointers and manual memory management. I'm tired of writing low level stuff by hand. That's why I use C++.

C can indeed be awfully complex for some tasks, and some other mid-level programming language can solve some of C's problems. The only reason C and C++ are so popular is just a snowball effect, they aren't really better or worse than any other programming languages out there.
Re: Making a NES game in C++
by on (#182225)
DRW wrote:
The only real advantage of a C++ compiler would be:

Inline functions instead of macros. (You would still need macros for tons of other things, though.)

Local variables can be declared everywhere in the function instead of only at the start of a block.
Not sure about the void parameter functions, but for the other two advantages you wouldn't even need C++, just support for more C99 features. It already has one, flexible array struct members.
Re: Making a NES game in C++
by on (#182227)
DRW wrote:
How? In object orientation, the function is written to be able to be called by any object of the class. MyClass::MyFunction cannot know if it is called by MyClass a or by MyClass b, therefore, whenever this is referenced, it is referenced as a pointer. This means: Indirect access. Which means: More execution time than if you simply access a fixed location in memory.


There will be no classes and functions if compiler is smart enough.

Watch the video in the 1st post and compare source code to the assembly. The reassembler is on GitHub.
Re: Making a NES game in C++
by on (#182228)
FaTony wrote:
No. I can can't handle C with it's pointers and manual memory management. I'm tired of writing low level stuff by hand. That's why I use C++.

Then why are you even messing around with the NES? The only way you're going to get anything reasonable out of this system, is by getting your hands dirty with low level work. Unless you're aiming to create stuff that isn't to the potential of the system (pong, puzzle games, crippled games, etc). And C is particularly high level, in the context of the NES. If you need some additional functionality on top of that (CC65), then write a pre-processor. But honestly, maybe you should move onto something like the Genesis, if you with C++ style environment.

Don't get me wrong; I understand the novelty of seeing something being done for an old system like this - "just because" line of reasoning. But there's a difference between novelty and efficiency, and I think CC65 already kinda straddles that line. Going beyond CC65 and expecting something more than novelty, is unrealistic.


That said, definitely thanks for posting the link. Like I said, I do like see stuff like this; modern concepts on old machines. I even have my own ideas of implementing some type object/class design into 65x arch. Nothing fancy mind you, and nothing that improves efficiency. More of just the fun of seeing it implemented.
Re: Making a NES game in C++
by on (#182229)
tomaitheous wrote:
The only way you're going to get anything reasonable out of this system, is by getting your hands dirty with low level work. Unless you're aiming to create stuff that isn't to the potential of the system (pong, puzzle games, crippled games, etc).

You didn't mean ZapPing in Zap Ruder, the build phase of RHDE, and Wrecking Ball Boy* respectively, did you? :P


* This one is written in Python using the Pygame library, but I designed everything with the intent of remaking it for the NES once it became more than a tech demo. Unfortunately, real life intervened.
Re: Making a NES game in C++
by on (#182230)
tomaitheous wrote:
Then why are you even messing around with the NES?

Because it is the only console I had.

And modern C++ can be more efficient than C because it has constexpr, templates, etc, that do a lot of work at compile time which leads to extremely efficient code.
Re: Making a NES game in C++
by on (#182231)
Plug a PC's HDMI out into a TV, and you have a console. The only difference between a PC with AMD CPU and AMD integrated graphics and a PlayStation 4 is the latter's lockdown.
Re: Making a NES game in C++
by on (#182233)
tepples wrote:
Plug a PC's HDMI out into a TV, and you have a console. The only difference between a PC with AMD CPU and AMD integrated graphics and a PlayStation 4 is the latter's lockdown.


Obviously, I'm making a PC game and I'm currently developing an engine for it.

I was just interested in applying my C++ skills to NES.
Re: Making a NES game in C++
by on (#182234)
I think this idea deserves some more attention. Now that I've had the time to watch Jason's videos, and seen the x86-to-6502 reassembler, I have some thoughts.

https://youtu.be/CdJOSfK1ja0

First, he's clearly using ca65, and the reason he went this route because the cc65 compiler isn't great at optimization. Whereas GCC (etc) does much better optimizations.

And, possibly, converting from one assembly to another could make almost any high-level language usable for 6502 development.

I look forward to hearing about your experiences trying to do NES in C++.
Re: Making a NES game in C++
by on (#182238)
FaTony wrote:
DRW wrote:
How? In object orientation, the function is written to be able to be called by any object of the class. MyClass::MyFunction cannot know if it is called by MyClass a or by MyClass b, therefore, whenever this is referenced, it is referenced as a pointer. This means: Indirect access. Which means: More execution time than if you simply access a fixed location in memory.


There will be no classes and functions if compiler is smart enough.

O.k., now I assume that you don't really know what you're talking about.

Of course there will be no classes. Not only "if the compiler is smart enough", but simply never. Because Assembly doesn't know classes.
But there will definitely be functions, or, as Assembly calls it, subroutines. And if you work with classes, the functions would always access the object's members by indirect access.

You can either access a variable directly: LDA Address
Address is a fixed location in memory. This reads the value stored at Address.

Or with an offset: LDA Address, X
--> This reads the value at Address + X.

Or with indirect access, i.e. with a pointer: LDA (Pointer), Y
--> This reads the value in Pointer. This value is then treated as an address. So, this reads the value in (value in Pointer as Address) + Y.
(And no, "value in value in" is not a typo. Before reading the value, you need to read another value.)

Guess which one takes the most ROM space and the most CPU time. And guess which one you would have exclusively when working with classes.
Re: Making a NES game in C++
by on (#182252)
DRW wrote:
O.k., now I assume that you don't really know what you're talking about.

Of course there will be no classes. Not only "if the compiler is smart enough", but simply never. Because Assembly doesn't know classes.
But there will definitely be functions, or, as Assembly calls it, subroutines. And if you work with classes, the functions would always access the object's members by indirect access.

You can either access a variable directly: LDA Address
Address is a fixed location in memory. This reads the value stored at Address.

Or with an offset: LDA Address, X
--> This reads the value at Address + X.

Or with indirect access, i.e. with a pointer: LDA (Pointer), Y
--> This reads the value in Pointer. This value is then treated as an address. So, this reads the value in (value in Pointer as Address) + Y.
(And no, "value in value in" is not a typo. Before reading the value, you need to read another value.)

Guess which one takes the most ROM space and the most CPU time. And guess which one you would have exclusively when working with classes.


It will have direct access if the logic is straightforward.
Re: Making a NES game in C++
by on (#182256)
Not to argue, but DRW is right.

In C++ a new object is created on the heap and referenced in the asm code with a pointer, and members of the object are dereferenced with indirect addressing, which is comparatively slow...

Global variable, A = 1;
Code:
Lda #1
Sta A


Variable B of class A on heap...A.B = 1;

Code:
Lda #low byte address of A
Sta Pointer
Lda #high byte address of A
Sta Pointer +1
Ldy #offset of variable B
Lda #1
Sta (Pointer), y
Re: Making a NES game in C++
by on (#182257)
Without optimizations and by default C++ objects are created on the stack. With optimizations anything is possible.
Re: Making a NES game in C++
by on (#182262)
FaTony wrote:
It will have direct access if the logic is straightforward.

It cannot have direct access. Because a class can never know if you will create one or more objects of it.

What do you imagine? That the compiler recognizes that you only ever create one object of this class and that it therefore optimizes the code, so that all functions use this one global object? That's not possible.

Because C++ programs are compiled on a source by source basis. The compiler compiles every cpp file separately. And that's the place where the optimizations are done.
That's the reason why you need stuff like function prototypes, so that the compiler knows what it has to do when it sees you calling a function that isn't defined in the same file.

And since the compiler compiles each source file separately, it cannot know if you won't use the same class anywhere else. Therefore, it cannot optimize the functions to work with one specific object. It always has to use indirect access because you can create more than one object of the class and the compiler doesn't know in advance if you actually do this or if you don't.

FaTony wrote:
Without optimizations and by default C++ objects are created on the stack. With optimizations anything is possible.

This is also complete nonsense. Optimization has nothing to do with heap and stack.
If you write MyClass myObject; then the object is created on the stack.
If you write MyClass *myPointer = new MyClass(); then the object is created on the heap, even without optimizations, and you need to call delete myPointer; to remove it from memory.

Alright, that was my last C++-related post directed to you. Because it's obvious that you know much less than you actually think, but still you insist that you're right. Talk to me again when you have written your high quality NES game in C++, using classes, templates and the C++ standard library.
Re: Making a NES game in C++
by on (#182263)
DRW wrote:
What do you imagine? That the compiler recognizes that you only ever create one object of this class and that it therefore optimizes the code, so that all functions use this one global object? That's not possible.
...
And since the compiler compiles each source file separately, it cannot know if you won't use the same class anywhere else. Therefore, it cannot optimize the functions to work with one specific object. It always has to use indirect access because you can create more than one object of the class and the compiler doesn't know in advance if you actually do this or if you don't.

Technically it could, for an instance of a class that is statically allocated, within its translation unit (e.g. inline functions, or any direct member access can know at compile time exactly where it should appear).

That pedantic example aside, though, if you want to tell the compiler there's only one of something in a class, that's what the static keyword is for.

Just like it may be a helpful optimization to use global variables to pass arguments instead of using the stack when using CC65, in this hypothetical NES C++, it may similarly be a helpful optimization to use static members in appropriate places.
Re: Making a NES game in C++
by on (#182264)
rainwarrior wrote:
Technically it could, for an instance of a class that is statically allocated, within its translation unit (e.g. inline functions, or any direct member access can know at compile time exactly where it should appear).

Yeah, static, o.k. But it would be hard to do an object oriented game where everything is static and local, just so that the compiler might optimize this away.

rainwarrior wrote:
That pedantic example aside, though, if you want to tell the compiler there's only one of something in a class, that's what the static keyword is for.

Well, static members are intended for something different: You use it when all objects of the class share the same member.

This is probably not what he had in mind. Static members in classes are fine, but if you have a class with only static members, then the class is nothing more but a glorified namespace of otherwise global variables and functions, therefore defeating the purpose of using object orientation in the first place.

Also, you cannot derive static members from base classes as new instances.
Let's say I have a class Character with only static members and now I derive Player and Opponent from it and I want each of these two classes to have their own instance of the static variables. I.e. I want Character::Energy, Player::Energy and Opponent::Energy to be three separate static variables.
--> Doesn't work. Player and Opponent would share the static members of Character, there would still be only one variable Energy.

rainwarrior wrote:
Just like it may be a helpful optimization to use global variables to pass arguments instead of using the stack when using CC65, in this hypothetical NES C++, it may similarly be a helpful optimization to use static members instead of a singleton instance.

Which, in the end, means, that you're not using object orientation at all. You only use the class name as a namespace for a bunch of global variables. Which confirms my argument: Then you can just program in C because you cannot really use the C++-specific features anyway.
If all you need C++ for is because you want to put a bunch of static variables into a class, then you can still use C and simply prefix the names.
Re: Making a NES game in C++
by on (#182265)
Just because something isn't the most efficient runtime implementation doesn't mean you can't use it at all. There's plenty of valid (hypothetical) practical applications of real C++ features (like classes) to an NES game.

The actual problem here is that the NES C++ compiler doesn't exist. Whether or not classes are efficient is kind of irrelevant. This is an argument about an imaginary compiler for an imaginary game.

You might be able to cobble together something with existing tools. There's an LLVM backend that will let you compile C++ to C, and with some elbow grease you might funnel that into CC65. The OP posted that x86 to 6502 assembly level conversion approach too, but that seems to just be a proof of concept at this point, and not any kind of robust or ready to use tool. There's also at least one attempt to directly target 6502 with LLVM, but I have no idea how far along this effort is.

Practical C++ for NES might come eventually, it doesn't seem at all impossible to me. There's just not a whole lot of people who know the NES and are also interested in writing free C++ compilers for it, so it might be a very long wait.

At least we've already got a moderately useful implementation of C with CC65.
Re: Making a NES game in C++
by on (#182266)
rainwarrior wrote:
Just because something isn't the most efficient runtime implementation doesn't mean you can't use it at all.

Sure, but I've got the feeling that FaTony wanted to use C++ in its intended way. He didn't want to use C++ for the sake of using C++, but because he wants to use the stuff that you really cannot do in C.
So, I consider stuff like pure static classes to be "C++ for the sake of C++" because it's just a bunch of global variables and functions, hence something that you can use just as well in C with, well, global variables and functions.

That's why my argument still stands:

Using all the advantages of C++ that can't be easily recreated in C (object orientation, inheritance, templates, comfortable data types in the standard library like dynamic arrays) cannot be used in NES games anyway since it costs too much ROM space and CPU time.

And using the stuff that doesn't cost additional ROM space and CPU stuff (pure static classes, inline functions) is so mundane that trying to get a C++ compiler isn't worth the time and energy because you can do basically the same stuff by simply using C (static classes: global functions with name prefixes, inline functions: macros).
Re: Making a NES game in C++
by on (#182270)
DRW wrote:
Because C++ programs are compiled on a source by source basis. The compiler compiles every cpp file separately. And that's the place where the optimizations are done.

This used to be true, but nowadays compilers are also capable of doing link-time optimizations.

rainwarrior wrote:
There's an LLVM backend that will let you compile C++ to C, and with some elbow grease you might funnel that into CC65.

Unfortunately this backend got removed in the more recent versions of LLVM due to lack of maintenance. (Of course you could still use the older versions.)
Re: Making a NES game in C++
by on (#182271)
...or the ages old cfront ;)
Re: Making a NES game in C++
by on (#182272)
thefox wrote:
This used to be true, but nowadays compilers are also capable of doing link-time optimizations.

Really? But do they also do what FaTony imagines?
Re: Making a NES game in C++
by on (#182273)
DRW wrote:
Using all the advantages of C++ that can't be easily recreated in C (object orientation, inheritance, templates, comfortable data types in the standard library like dynamic arrays) cannot be used in NES games anyway since it costs too much ROM space and CPU time.


What about metaprogramming and templating? They can make writing games (and any code, really) significantly easier and more readable, and they often times don't have any runtime overhead (if you watch the video, the presenter keeps going to back to how he was able to write cool metaprogramming features without having to sacrifice any runtime).
Re: Making a NES game in C++
by on (#182274)
dougeff wrote:
I think this idea deserves some more attention. Now that I've had the time to watch Jason's videos, and seen the x86-to-6502 reassembler, I have some thoughts.

https://youtu.be/CdJOSfK1ja0

First, he's clearly using ca65, and the reason he went this route because the cc65 compiler isn't great at optimization. Whereas GCC (etc) does much better optimizations.

And, possibly, converting from one assembly to another could make almost any high-level language usable for 6502 development.

I look forward to hearing about your experiences trying to do NES in C++.

It's a nice idea, but I still not wonder wether using some kind of bytecode would not end up being a better idea. Maybe just target a processor that can be easily emulated on the NES. I already use byte code for many non-speed critical parts of my game (i.e. loading levels, loading title screen for instance). However my byte code currently cannot be targetted by any compilers but in theory it could.
Re: Making a NES game in C++
by on (#182280)
DRW wrote:
Using all the advantages of C++ that can't be easily recreated in C (object orientation, inheritance, templates, comfortable data types in the standard library like dynamic arrays) cannot be used in NES games anyway since it costs too much ROM space and CPU time.

And using the stuff that doesn't cost additional ROM space and CPU stuff (pure static classes, inline functions) is so mundane that trying to get a C++ compiler isn't worth the time and energy because you can do basically the same stuff by simply using C (static classes: global functions with name prefixes, inline functions: macros).

This sounds exactly like all the arguments people have against using C on the NES instead of Assembly? Since you seem to like C well enough, I don't really get your beef with hypothetical C++.

"You shouldn't use C++ because I don't wan't to use some of its features."

There's always some things you can do in any language that don't perform well when used with specific machines / compilers / etc. That's just part of programming. Doesn't mean they can't be useful, just means you have to be careful of them. There are ways to write classes that could be fairly efficient on the 6502. (Indexed arrays, for example, are not entirely incompatible with the idea of classes; there are conventions that could support them.)

In fact, the largest performance and code size problems we currently have with C on the NES are just that robust optimization is out of scope for a hobbyist compiler like CC65. It's not even on its roadmap! If you're using CC65 you're already eating tons of inefficiency, which is why I think it's quite bizarre that you're so adamant against C++. This isn't C++ support for CC65, this is using a better compiler as its starting point!!

CC65 doesn't implement C++ because implementing C++ from scratch is a deep dark nightmare. It has nothing to do with any idea about C++ being less efficient than C. The assembly conversion method has potential to outperform it in a very significant way, and the problem of implementing a C++ compiler was already done before it began.
Re: Making a NES game in C++
by on (#182282)
The biggest problem with high level languages on the NES is the lack of proper stack support. So local variables don't have a good place to go.
Nobody has written a Stack Flattener that assigns addresses to local variables, depending on the call graph of the program. Functions that actually need recursion can still have a stack.
Re: Making a NES game in C++
by on (#182283)
DRW wrote:
thefox wrote:
This used to be true, but nowadays compilers are also capable of doing link-time optimizations.

Really? But do they also do what FaTony imagines?

I have not dug deep enough into them to give a definitive answer.
Re: Making a NES game in C++
by on (#182286)
DRW wrote:
Really? But do they also do what FaTony imagines?


They do. Current gcc LTO is very good.
Re: Making a NES game in C++
by on (#182291)
calima wrote:
They do. Current gcc LTO is very good.


Exactly. Since a NES program doesn't really link to anything else, the compiler is able to see the whole program and perform aggressive optimizations.
Re: Making a NES game in C++
by on (#182292)
Does the x86 to 6502 converter take care of redundant loads and stores?
Re: Making a NES game in C++
by on (#182293)
The x86-to-6502 Reassembler, just translates one assembly language to another.

GCC does the optimizations, and has multiple levels of optimization.
Re: Making a NES game in C++
by on (#182303)
But the problem, as I see it, is that there isn't a 1:1 mapping between x86 and 6502 instructions, so unless you perform yet another optimization pass (peepholing) on the result it will be grossly inefficient.
Re: Making a NES game in C++
by on (#182304)
Jarhmander wrote:
But the problem, as I see it, is that there isn't a 1:1 mapping between x86 and 6502 instructions, so unless you perform yet another optimization pass (peepholing) on the result it will be grossly inefficient.

CC65 is already grossly inefficient, and it's still quite useful. The goal doesn't have to be perfect compilation indistinguishable from painstakingly crafted assembly, it merely has to be good enough to be useful.

Local optimizations during translation should also be possible, though. (Even CC65's very limited optimization tries to do this a little.) A lot of JIT compilers are capable of this kind of thing. Again, really just a matter of time and interest on the author's part.

You can follow along at the project's github page to see what state it's in. He does appear to be actively working on it.
Re: Making a NES game in C++
by on (#182323)
rainwarrior wrote:
This sounds exactly like all the arguments people have against using C on the NES instead of Assembly? Since you seem to like C well enough, I don't really get your beef with hypothetical C++.

"You shouldn't use C++ because I don't wan't to use some of its features."

I never said "You shouldn't use C++", I merely explained why most of the additions that C++ has over C are part of the stuff that you shouldn't use in NES games anyway.

I program for the NES in C, yes. But I avoid many things to make the code more effective:
Local variables only in non time-critical functions, otherwise global or local static variables.
No array of structs, but one struct with many arrays instead.
Array access with a variable index as rare as possible, and if the same variable from the array is used more than once in a function, then first copy it to a temp variable and set it back in the end.
etc.

And as far as I see, most of C++'s features that C doesn't have are part of the features that should be avoided for fast and small code.


Using C instead of Assembly is something worth considering because even with only global variables and tons of macros, you still have a programming language that is much more easy to understand, with different data types, loops, simple to write mathematical calculations etc.

But insisting on C++ instead of C will only bring you a marginal advantage: Inline functions and the ability to declare local variables wherever you want in a function. So, what?

But the stuff that makes C++ really strong in comparison to C (classes, inheritance, templates, standard library) is the stuff that you shouldn't use anyway, in the same way why you shouldn't use malloc or arays of structs in C.

And that's what I wanted to communicate: If there is already a C++ compiler for the NES, sure, use it. But in a situation where you do have a C compiler, but no C++ compiler for the NES, it makes no sense to say: "Nah, I love C++, but I don't really like C."

If you say "I hate Assembly, so unless there's a C compiler, I won't program a game at all", then this might be justified. But saying: "I dislike C, so unless there's a C++ compiler, I won't program a game at all" is pretty stupid.

Because first of all, the things that he hates about C (memory management) won't be needed anyway.
And the things he likes about C++ that C doesn't have would eventually get him into trouble because it's not effective enough.

Even with optimizations, I doubt that the binary code of a clean object oriented game design will be as effective as simply using global functions, global variables and a bunch of fixed-length arrays. (Having a class with only one object will probably be the exception, not the rule.)
And if you don't want to use clean object orientation, but pure static classes, then what exactly is the big advantage over simple global functions, so that you have to insist on C++?


In the end, I don't care what he does. I just wanted to communicate that avoiding game programming if you only have Assembly and no C might be a justified decision because Assembly and C are really worlds apart. But avoiding game programming if you only have C and no C++, that's pretty nitpicky.

Because using C++ over C is usually done for one thing: To have clean object oriented data structures with data encapsulation and inheritance etc. But those are exactly the things in an NES game that are either totally unneeded (data encapsulation) or that will break your neck (inheritance, member access by pointers).

rainwarrior wrote:
In fact, the largest performance and code size problems we currently have with C on the NES are just that robust optimization is out of scope for a hobbyist compiler like CC65. It's not even on its roadmap! If you're using CC65 you're already eating tons of inefficiency, which is why I think it's quite bizarre that you're so adamant against C++. This isn't C++ support for CC65, this is using a better compiler as its starting point!!

So? If there was a C++ NES compiler, it would still have the problem of being a hobbyist compiler.
And if you had a professional high quality optimization C++ NES compiler, then guess what: In this very moment you would also automatically have a professional high quality optimization C NES compiler because C is, for the most part, a subset of C++ anyway.

So, whatever disadvantages you can find for C on the NES:
- poor compiler
- poor stack support
- language constructs that make the code slow

You always have the same disadvantages in C++ and some other stuff as well:
- poor compiler
- poor stack support
- language constructs that make the code slow
- object orientation

There is no way that you have a good NES C++ compiler, but only a poor NES C compiler because the NES C++ compiler is also a C compiler.

rainwarrior wrote:
CC65 doesn't implement C++ because implementing C++ from scratch is a deep dark nightmare. It has nothing to do with any idea about C++ being less efficient than C.

I never said something else. All I say is: It makes no sense to wait for a C++ compiler instead of using the C compiler because you'll quickly learn that you better avoid the major C++-specific features anyway.
Remember: FaTony's complaint was not: "I find CC65 ineffective, isn't there another compiler?" His complaint was: "I don't want to program C, I want C++."
Re: Making a NES game in C++
by on (#182333)
I don't really get your narrow viewpoint.
Re: Making a NES game in C++
by on (#182336)
Erm...What? :?

What does this have to do with a viewpoint? We're not discussing philosophical questions here. This is about hard facts, like, you know, source code style relating to ROM space and CPU.

And it shouldn't be hard to understand that this-->MemberVariable = 5; generally requires more space and time than GlobalVariable = 5;.
Re: Making a NES game in C++
by on (#182351)
DRW wrote:
Erm...What? :?

What does this have to do with a viewpoint? We're not discussing philosophical questions here. This is about hard facts, like, you know, source code style relating to ROM space and CPU.

And it shouldn't be hard to understand that this-->MemberVariable = 5; generally requires more space and time than GlobalVariable = 5;.


If object is global, it doesn't make any difference.
Re: Making a NES game in C++
by on (#182355)
FaTony wrote:
If object is global, it doesn't make any difference.

Sigh. Only if you have exactly one object of this class and only if the specific NES compiler is actually capable of optimizing this away.
But how often do you have only one single instance of a class? This should be the exception more than the rule. Maybe for the player character, but for what else?

Let's take the opponents:
I assume if you do a clean object-oriented approach, you would want a dynamic array that holds a pointer of the base type:
Code:
array<Opponent *> AllOpponents;


And then you would probably add the opponents like this:
Code:
AllOpponents.add(new StandardOpponent());
AllOpponents.add(new JumingOpponent());
etc.

Right?

And in this case, how shall the compiler be able to optimize the following?
Code:
AllOpponents[i].IncrementX(2);


It might be able to optimize IncrementX to assign the underlying variable directly instead of calling the function. But you still have three things that waste ROM space and CPU time:

AllOpponents[ i ] is an array access with a variable index, hence the compiler would access it with an offset.
(O.k., this problem also exists in C. But at least in C, you would always use a simple array, i.e. the ROM doesn't get filled with all the functions of the std::array class.)

Then, after calculating the offset address, the compiler has to get the address of your current opponent object itself. This cannot be optimized since the compiler has no way of knowing which opponent address will be used at runtime.

(Even if you had a fixed array where the location of all opponents are known at compile time, the compiler could still not optimize any non-mundane functions.
For example, if you have an Opponent::Move() function and you have an array with five opponents, how shall the compiler optimize for direct access? Copy the Move function into ROM five times, so that each instance works with its fixed values?
Even if you find a compiler that's capable of doing this, you wouldn't want your function to be copied five times if it's more than a simple variable assignment. Because ROM space is sparse.)

From there on, all members would be used by indirect access, i.e. with pointers, which is even worse than array access with offsets.

And since you're doing a clean object-oriented approach, you probably wouldn't feel like putting each member that you use more than once per function into a temporary variable when the function starts and putting it back when the function ends, right? Because this defeats the purpose of data encapsulation and consistency checks in setter functions.

And if IncrementX is a virtual function that gets overridden in the derived classes, then a good bunch of time is wasted to assign this. Again, no optimization here because it's a runtime decision.

The only way to use code suitable for the NES would be to bastardize the whole object-oriented apporach and use classes in a totally hacky way.

This is partly true for C as well.
But in this case it's justified because you use C, so that you can avoid Assembly. Even with only global variables and a ton of macros, C ist still way easier to read and write than Assembly.

You might have to bastardize function calls by avoiding parameters and setting the parameter values to global variables instead:
Code:
void Function(int a, int b, int c);

becomes this:

int Function_A;
int Function_B;
int Function_C;

void Function_(void);

#define Function(a, b, c)\
do\
{\
    Function_A = a;\
    Function_B = b;\
    Function_C = c;\
    Function_();\
}\
while (0)

But this is not so much of an issue because even with the bastardization of, let's say, 20 % of the language, you still have those 80 % of neat stuff that is totally missing in Assembly, like type safety, if-else, loops, scopes or intiuitive calculations. (a = (b + c) * 2 - d; would be a huge chunk of error-prone code in Assembly.)

So, even a bastardized version of C is still better than pure Assembly.

But if you now insist on using C++, then you're left with the choice of either writing clean, but highly inefficient code, like the dynamic array with the opponents above. This is fine for a PC game, but not on the NES where you run into a lag pretty quickly.
Or with the choice to bastardize the whole language features.

In C vs. Assembly, you need to bastardize maybe 20 % of the language features of C, to use a language that's still 10 times more readable (and writable) than Assembly and you would still use 80 % of C in the way it's intended.

In C++ vs. C on the other hand, you would bastardize 100 % of the non-C features of C++, to use a language that's pretty much exactly as readable as C anyway and you would use 0 % of non-C C++ in the way it's intended.

Therefore, instisting to use C++ instead of C, especially when there's no C++ compiler available, but a C compiler, makes absolutely no sense.

I would understand if you used a C++ compiler if you had the choice between C and C++. But you only have the choice of using C or writing no game at all. And in this case, it makes no sense to go for not writing a game at all, just because you prefer C++ over C. Simply because you wouldn't really be able to use the advantages of C++ anyway.
Re: Making a NES game in C++
by on (#182362)
DRW wrote:
But this is not so much of an issue because even with the bastardization of, let's say, 20 % of the language, you still have those 80 % of neat stuff that is totally missing in Assembly, like type safety, if-else, loops, scopes or intiuitive calculations. (a = (b + c) * 2 - d; would be a huge chunk of error-prone code in Assembly.)

So, even a bastardized version of C is still better than pure Assembly.

Lolwut? Spoken like someone truly ignorant of assembly.
Re: Making a NES game in C++
by on (#182369)
O.k., maybe "huge" was a bit exaggerated. But my point still stands:

Does this look particularly intuitive as a mathematical equation?
Code:
LDA VarB
CLC
ADC VarC
ASL
SEC
SBC VarD
STA VarA


And about the bastardization: It is not that bad. Global variables, some macros, a bit caution when using arrays. But other than that you can still write C pretty cleanly. It's not like you have to break the language in a way that only BASIC-like spaghetti code is left.
So, yeah, mostly clean C with a few unclean things is still better than Assembly, unless you're someone who has written Assembly his whole life.
Re: Making a NES game in C++
by on (#182404)
DRW wrote:
O.k., maybe "huge" was a bit exaggerated. But my point still stands:

Does this look particularly intuitive as a mathematical equation?
Code:
LDA VarB
CLC
ADC VarC
ASL
SEC
SBC VarD
STA VarA



Now it does..
Code:
 ; The follow code is: a = (b + c) * 2 - d;
LDA VarB
CLC
ADC VarC
ASL
SEC
SBC VarD
STA VarA


That's what comments are for; understanding. But I read that code just fine. What you're arguing is trivial stuff. It's all write once and done. You don't have to spend your whole life with Assembly in order to write efficient code. Matter of fact, six months of actively writing assembly should get your there. Anything else requires learning how to optimize high level data structures and algorithms in a low level perspective. This is what's needed on a processor like the 6502. Stuff like split arrays (high and low byte, or even ones split across multiple bytes), when to direct access data with indexing and when indirection is needed, small LUTs for speeding up small calculations, etc.

I know of someone that uses C to prototype his code for 65x, and once it's working - he converts the functions into pure ASM by hand. He's not particular comfortable in design concepts directly in assembly, but has no problem converting what he did write in C into efficient assembly. And his experience with assembly is pretty minimal. This is how I see C being a useful tool for 65x; fast prototyping of ideas, but not the final product.

Quote:
And about the bastardization: It is not that bad. Global variables, some macros, a bit caution when using arrays. But other than that you can still write C pretty cleanly. It's not like you have to break the language in a way that only BASIC-like spaghetti code is left.
So, yeah, mostly clean C with a few unclean things is still better than Assembly, unless you're someone who has written Assembly his whole life.


You're still fighting something that's imposing additional resource limit, because of your own resistance to learn something more efficient. In that respect, I see FaTony's situation really no different than your own; neither of you want to bite the bullet and learn assembly so you don't have fight with design in order to get efficiency.
Re: Making a NES game in C++
by on (#182405)
tomaitheous wrote:
You're still fighting something that's imposing additional resource limit, because of your own resistance to learn something more efficient. In that respect, I see FaTony's situation really no different than your own; neither of you want to bite the bullet and learn assembly so you don't have fight with design in order to get efficiency.

Not to mention, as I covered in this unrelated thread... well, I'll just quote myself and bold/underline the relevant part:

Quote:
I see this constantly around here now (and in tech in general, but it's prolific as hell around the NES and SNES): people trying to instil crybaby beliefs about PLs into the console. "Wah, I don't like assembly language, but I really want to make something on this archaic system that I'm sporting a major hard on for. I'm familiar with Javascript and Ruby. Why can't the NES run these? This is stupid!" Yes I'm overdramatising (or am I?), but it's a bizarre mindset: wanting to develop software for a 30-year-old system (NES/FC; for SNES/SFC, make that ~24 years), but having present-day ideologies and beliefs and habits (esp. about PLs) somehow take precedence. I'm not talking down anyone on here, honest, but many of us who have been doing 65xxx assembly for a long time know that the architecture does not "mesh well" with C. That said: is it possible to use C on the NES? Yes, and either in this thread or another (I forget), there have been several titles done that way. I think that's awesome. But doing so still requires that you know and use assembly at some point, especially for debugging/troubleshooting. In other words, you cannot avoid the reality of needing to know and use assembly on these platforms.

There is no avoiding assembly on an architecture like those of classic consoles. You are going to have to deal with it at some point. If anyone who's done NES work exclusively in a higher level language and has managed to entirely avoid assembly, then they are an embodiment of living perfection and deserve gold statues made in their honour.
Re: Making a NES game in C++
by on (#182407)
tomaitheous wrote:
What you're arguing is trivial stuff.

Not if you have 1000 places where this "trivial" stuff is used. I mean, I admire people who can write in Assembly as quickly as I can write in high level languages. But I don't think that I would have ever finished my game if I had to do it in Assembly only.

tomaitheous wrote:
You're still fighting something that's imposing additional resource limit, because of your own resistance to learn something more efficient. In that respect, I see FaTony's situation really no different than your own

Yes, it is a different situation. Assembly and C are completely different. But C and C++ are basically the same abstraction layer. Especially since C is a subset of C++.

Also, I didn't resist to learn Assembly. A good bunch of my code is written in Assembly: Initializing the NES, updating the sprites, doing the NMI stuff (scrolling, background updates), controller reading, general purpose functions like CopyArray, CompareArray, ShiftArray, the randomizer.
So, I had to work with Assembly and had to write code for it. Don't assume that I have never touched Assembly at all. I know well enough how the language works.

But I'm glad that I was able to use C for everything else because writing and then checking those Assembly functions was a real pain in the ass. It takes me much longer to write Assembly. And when I check the code for correctness after writing it (especially if it isn't just general checking, but if something actually doesn't work), I really have to concentrate hard to know exacly which value is in which state in what line. To me, it's much easier reading C.
Re: Making a NES game in C++
by on (#182408)
tomaitheous wrote:
You don't have to spend your whole life with Assembly in order to write efficient code.

You don't even have to be older than 17 either. :lol:

tomaitheous wrote:
You're still fighting something that's imposing additional resource limit, because of your own resistance to learn something more efficient.

The only thing I have to say is that maybe he can afford the resource hit so why not, but considering how concerned I've been with the speed of the SCPU, I couldn't even imagine getting anywhere on the NES without very optimized code. I've always assumed C to be only half as fast as optimal assembly on the NES or SNES, but I have nothing to base this on and are probably exaggerating.

DRW wrote:
It takes me much longer to write Assembly. And when I check the code for correctness after writing it (especially if it isn't just general checking, but if something actually doesn't work)

Why would you check it if it works?
Re: Making a NES game in C++
by on (#182411)
DRW wrote:
Yes, it is a different situation. Assembly and C are completely different. But C and C++ are basically the same abstraction layer. Especially since C is a subset of C++.

No, the differences between C++ and C are really quite tremendous. This is why I thought the demonstration this thread was started to talk about was a really cool thing. We're not just looking at a path that might produce a better C compiler than CC65, we're looking at all the functionality that a robust C++ implementation can bring to the table.

I also think it would be nice if we could talk more about the cool demo, and a lot less about whether DRW thinks he's right about C++ being stupid and unnecessary compared to C, but I know he's incapable of not having the last word, so I fully expect his 8 page reply to this comment.
Re: Making a NES game in C++
by on (#182415)
Espozo wrote:
I've always assumed C to be only half as fast as optimal assembly on the NES or SNES, but I have nothing to base this on and are probably exaggerating.

It's not as bad as you think.
My game is a jump'n'run platformer. I.e. the move functions are much more complex than simply four times "if direction was pressed then check for obstacle in this direction, if no obstacle then update x/y position".
And there are almost always four characters on the screen at once (five if you count the main character's taser when she attacks, which is its own meta sprite).

And still, I have plenty of rendering time left (the non-red parts):
Attachment:
Screenshot.png
Screenshot.png [ 3.63 KiB | Viewed 1747 times ]

(A little explanation: Since I use parallax scrolling by checking for the sprite overflow flag and the sprite 0 flag, I split my game logic into three parts, so that the time until the lowest split is reached isn't wasted. That's why you see three separate stripes of red: The first is the update of scrolling and the status bar, the second is the main logic and the third is putting the sprites in the correct locations, so that NMI can put them on screen.)

So, C is more than capable of doing quick games. Despite what I have heard by some people, you aren't limited to "Pac-Man"- or "Zooming Secretary"-like complexity. If anybody wants to do his action platformer in C, there's no reason why he should be dicouraged from it.

Yes, Assembly is needed, but only for the low level stuff, like initialization, NMI, sprite updates and maybe a handful of general purpose functions, like CopyArray and the like. But I'm proud to say that no game logic stuff, like UpdateStatusBar, MoveAmy, MoveGoon, CheckCollision etc. needed any handwritten Assembly code. It's all pure C.

Espozo wrote:
DRW wrote:
It takes me much longer to write Assembly. And when I check the code for correctness after writing it (especially if it isn't just general checking, but if something actually doesn't work)

Why would you check it if it works?

Because I always check code again, to see what can be optimized and if really everything is correct and doesn't include a bug that just didn't surface until now.

rainwarrior wrote:
No, the differences between C++ and C are really quite tremendous.

Of course it's different. C++ = C + some more stuff.
But it's the same layer of abstraction. In both languages you have the same control structures, the same way of assigning values to an integer etc. It's not a difference in the same way C and Assembly are different.
And even if you might have problems grasping classes when only knowing C, that's not the issue here. Since the OP already knows C++, he could easily work with C.

rainwarrior wrote:
This is why I thought the demonstration this thread was started to talk about was a really cool thing. We're not just looking at a path that might produce a better C compiler than CC65, we're looking at all the functionality that a robust C++ implementation can bring to the table.

Yeah, great. Tell me when you have written that great compiler where you can do a clean object-oriented approach for an NES game, including a nice class hierarchy with data encapsulation, inheritence, templates and dynamic arrays and still have the code be as good as using C code with a bunch of global variables and a few simple fixed-sized arrays and macros. Go ahead. I'm waiting.

rainwarrior wrote:
I also think it would be nice if we could talk more about the cool demo, and a lot less about whether DRW thinks he's right about C++ being stupid and unnecessary compared to C, but I know he's incapable of not having the last word, so I fully expect his 8 page reply to this comment.

You know what? Bite me!

The thread is called "Making a NES game in C++" and I explained that it's perfectly possible to do a game in C, but that the things that C++ has that C doesn't have wouldn't really be fitting for a good-performance NES game, so my answer to the original poster was definitely a positive one: "Yes, my friend, you don't need to do major Assembly stuff. You can indeed write a good game in C."
But he insisted: "Nah, I don't like C, I want C++" and I explained him why the C++ extensions wouldn't really help him anyway and why the stuff that he doesn't like about C wouldn't be used in an NES game either, so he would be good with C either way.

I never said C++ is stupid and unnecessary. In fact, for the PC I would never write a game in pure C, but always use C++.
I just said that the features that you should use for an NES game would be mostly a subset of only the C features anyway, so he should just go ahead and do a game and not let the unavailability of C++ stop him. In how far is this a bad thing again?

But yeah, just complain again about me being negative, as you always do. I, the person who wants to encourage the original poster to try doing his game and clear up his misunderstandings about C, I'm the asshole again, while your chat about a hypothetical C++ compiler that will probably never exist is a valuable part of the discussion.

And then in the end, after calling me out in this public thread, will you write a message again, admitting that you have been an ass and apologizing to me secretly and in private, as you did last time?

But you know what? You can have your last word now. I'm out of this thread.
(Edit: As far as the topic at hand (C++ vs. C etc.) is concerned. If people talk about something totally unrelated, like general homebrew chatting, I might comment on it.)
Go on talking about some fantasy C++ compiler for the NES that would be 10 times more effective than CC65 and could optimize away everything. I bet that's going to produce high quality NES games: Talking about a non-existant piece of software.
I, who wants to encourage the original poster to do NES games with actually existing and proven-to-work tools, I'm just a dogmatic piece of shit who wants to have the last word.
No, no, my posts are dick posts. Philosophising about how a certain hypothetical compiler could optimize things to make it work for the NES, that's the way to go for the original poster's request to make an NES game in a high level language.
Re: Making a NES game in C++
by on (#182420)
DRW wrote:
And there are almost always four characters on the screen at once

So you have 90% of SNES games beat already. :lol:
Re: Making a NES game in C++
by on (#182423)
koitsu wrote:
There is no avoiding assembly on an architecture like those of classic consoles. You are going to have to deal with it at some point. If anyone who's done NES work exclusively in a higher level language and has managed to entirely avoid assembly, then they are an embodiment of living perfection and deserve gold statues made in their honour.
I've also finished a couple NES games entirely in C. They do use neslib, but I did not write a single line of asm in them. Do I get a statue? ;)

My current projects push the hw more, and there I do a lot of hand-optimization in asm.
Re: Making a NES game in C++
by on (#182428)
Espozo wrote:
DRW wrote:
And there are almost always four characters on the screen at once

So you have 90% of SNES games beat already. :lol:


I never want to design an entire game around using only 4 objects at once. The hardware is already limited as it is, why make it even more limited? Lets say you have an enemy who shoots arrows. If you have 2 of them onscreen they have to take turns shooting an arrow, because they both can't shoot at the same time.
Re: Making a NES game in C++
by on (#182429)
"Never" is a strong word. Street Fighter has about four objects at once: Ken, Guile, Hadouken, and Sonic Boom.
Re: Making a NES game in C++
by on (#182430)
I think I had about 20 objects, if you count bullets, in my Spacy Shooty example game. In C. Although most of the time consuming parts were written in ASM. (collision detection) (object rendering).
Re: Making a NES game in C++
by on (#182431)
psycopathicteen wrote:
I never want to design an entire game around using only 4 objects at once. The hardware is already limited as it is, why make it even more limited?

The game wasn't consciously designed around it. It just happened to be this way.
I guess I could easily add another character and still not run into a lag, but the game is already hard enough as it is.
(The game uses auto-scrolling, so you cannot just stop. Most of my testers told me that it's very hard. Adding another opponent would make it even harder.)
As you can see in the screenshot, there's still tons of rendering time, so no, the game is not at the NES's limits in the slightest. I could add more stuff if I wanted. If I removed the digitized voice sample, I would even still have tons of ROM space for completely new enemy movement patterns.

Besides, are there ever more than three opponents on screen at once in "Super Mario Bros."? Even "Ninja Gaiden" usually doesn't have more than four opponents at once, so it's not like commercial platformers are usually crowded with enemies while having only three screams "amateur".

dougeff wrote:
Although most of the time consuming parts were written in ASM. (collision detection).

What's time-consuming with collision detection?
Code:
collision =
   !(
         playerLeft > npcRight
      || playerRight < npcLeft
      || playerTop > npcBottom
      || playerBottom < npcTop
   );
Re: Making a NES game in C++
by on (#182433)
Because collision checks increase exponentially as objects increase.

I have 20 objects, but I'm actually doing 80 collision checks per frame. Thus, it is prudent to optimize this function.
Re: Making a NES game in C++
by on (#182435)
Well, it's exponential if everything can collide with everything, anyway. If you have a player and enemies, and enemies don't collide with each other, it would only grow linearly, since you'd only need to check for collisions between the player and each enemy.
Re: Making a NES game in C++
by on (#182436)
Oh, right. I see that your spaceship can shoot six bullets at a time. Yeah, that makes it a bit more CPU-intensive.
I only have up to six checks: Amy with every opponent. And Amy's taser with every opponent.
Re: Making a NES game in C++
by on (#182437)
dougeff wrote:
Because collision checks increase exponentially as objects increase.

I have 20 objects, but I'm actually doing 80 collision checks per frame. Thus, it is prudent to optimize this function.

In the worst case where everything can collide with everything it's not exponential increase but quadratic increase. We could put a 2-dimentional array showing all object slots and ticking a box when object collide. Adding one more object adds one row and one column, and there's n^2 boxes.

If it was exponential increase, it'd double (or triple, or whathever) the amount of checks for each added object, but here it's not the case obviously.

In practice it's rare to have everything collide with everything, just everything with the player and some type of bullet with the enemies.
Re: Making a NES game in C++
by on (#182438)
DRW wrote:
psycopathicteen wrote:
I never want to design an entire game around using only 4 objects at once. The hardware is already limited as it is, why make it even more limited?

The game wasn't consciously designed around it. It just happened to be this way.
I guess I could easily add another character and still not run into a lag, but the game is already hard enough as it is.
(The game uses auto-scrolling, so you cannot just stop. Most of my testers told me that it's very hard. Adding another opponent would make it even harder.)


At least you're game isn't hardcoded to 4 objects, where it wouldn't spawn a power up if there happens to be 3 enemies onscreen.
Re: Making a NES game in C++
by on (#182439)
psycopathicteen wrote:
At least you're game isn't hardcoded to 4 objects, where it wouldn't spawn a power up if there happens to be 3 enemies onscreen.

Actually, it is hardcoded to three enemies.
But it would be a relatively mundane issue to increase the NumberOfCharacters constant. (O.k., some small adjustments would still be necessary, but not many.)

By the way, once per level, Amy's best friend Rachel appears to give her an item and when it's time for Rachel to appear, she won't do so until all the opponents have left the screen.
Because Rachel, her extended arm and the item use the array slots that are usually used for the three opponents.
Although in this case it also has to do with the fact that Rachel and the item have their own color combinations, so we need to remove the opponents before Rachel appears, so that the values in two of the four sprite palettes can be changed and opponents don't spontaneously change colors.

If my game had randomly appearing items throughout the levels, I probably would have given them their own slots in the array. Or I would have used a completely separate array.

But yeah, all in all, NES games written in C can have a good bunch of more opponents, even non-munane ones.
I once heard that "Super Mario Bros. 3" couldn't be done by using C. I haven't tried it out, but I tend to believe this is not the case.
Re: Making a NES game in C++
by on (#182441)
psycopathicteen wrote:
At least you're game isn't hardcoded to 4 objects, where it wouldn't spawn a power up if there happens to be 3 enemies onscreen.

Haunted: Halloween '85 has NUM_ACTORS=6 (Donny plus five enemies), with an additional eight-object late entry queue. Health restoration candy dropped after defeating an enemy goes to the entry queue, meaning it may pop in only once an enemy's faint animation completes. And if too many enemies are lured into one place, they can keep other enemies, candy, etc. from spawning. As soon as something is defeated, however, the next thing in the entry queue spawns. This limit is there for two reasons: checking all walkers against terrain is time-consuming, especially when I have to continuously reference terrain in ROM because there's so little RAM, and it helps the level designer avoid situations that might lead to excessive flicker.

Its sequel The Curse of Possum Hollow has the same structure for the most part, but "bullets" (small, lightweight objects) are in a separate pool. And there are more things that can keep things from spawning, mostly if enemies would have more different 1K sprite sheets than MMC3 can handle at once.

As for making Super Mario Bros. 3 in C, you may be right that no existing 6502 compiler would make that practical on the NES. But most Game Boy Advance games were made in C, and I'm inclined to believe that Super Mario Advance 4: Super Mario Bros. 3 was written in C, though I haven't traced through it myself. But given the attitude toward "inefficient programming languages", "HLL code", and "HLL-programmers" expressed in GBATEK, I bet nocash could rattle off a bunch of suboptimal instruction sequences that the C-to-Thumb compilers of the time emitted.
Re: Making a NES game in C++
by on (#182546)
If you use CC65, you give up lots of optimization that can be dome with gcc. If you watch the video a few pages back, you'll see that CC65 couldn't even optimize Hello world.
Re: Making a NES game in C++
by on (#182554)
FaTony wrote:
If you use CC65, you give up lots of optimization that can be dome with gcc. If you watch the video a few pages back, you'll see that CC65 couldn't even optimize Hello world.

So? Has anybody ever said anything different?

As far as I remember, all I said was that C++-specific features are, for the most part, stuff that shouldn't be used in an NES game. This statement is true regardless of the compiler. Even in gcc for the NES, I would suggest against using stuff like object orientation or templates.

So, yeah, if somebody manages to do a gcc version that can work with NES games, I might use it. But this fact tells nothing about the things that I said about object orientation being ineffective for NES games since there are things that no compiler could ever optimize away. Stuff like the fact that an object's address in a dynamic array is only known at runtime and each member is therefore used with a pointer.

The fact that your most recent post totally misses the point (since it was never an argument about which compiler is better and since my statements about C++ count equally for every compiler) confirms that you really have no idea what you're talking about.
Re: Making a NES game in C++
by on (#182556)
FaTony wrote:
If you watch the video a few pages back, you'll see that CC65 couldn't even optimize Hello world.

What video? If you're talking about https://www.youtube.com/watch?v=CdJOSfK1ja0, cc65 produced close to optimal code (when optimizations were enabled) for the simple "hello world" of setting the border color on C64.
Re: Making a NES game in C++
by on (#182557)
DRW wrote:
Stuff like the fact that an object's address in a dynamic array is only known at runtime and each member is therefore used with a pointer.

I mentioned before, but there are plenty of practical ways to reduce overhead. Here's a simple example:
Code:
const int NUM_OBJECTS = 16;

class Object
{
protected:
   uint8 index;
   
   static uint8 used[NUM_OBJECTS] = {0};
   static uint8 member_data[NUM_OBJECTS];
   
public:
   Object()
   {
      // generate an index (probably would do this differently, just using as example)
      for (uint8 i=0; i<NUM_OBJECTS; ++i)
      {
         if (!used[i])
         {
            used[i] = 1;
            index = i;
            return;
         }
      }
      throw OutOfObjects(); // or handle the error however you think is appropriate
   }
   
   ~Object()
   {
      used[index] = 0;
   }
   
   void do_something(uint8 blah)
   {
      // only non-static overhead is fetching the index, and possibly virtual function calls
      member_data[index] = blah;
   }
};

Depending on how much work you can do with that index in a single function, the overhead doesn't have to be much of an issue at all.

Again, if the class instance is static, the overhead would be optimized away entirely (zero overhead). Classes don't have to be dynamically allocated to be useful, and they don't have to be pointers to be useful either. Static inheritence is still useful and the problem you're making up doesn't apply to it. We could derive from Object in this example, and the only potential overhead added would be a lookup for virtual functions, if and when you need them, and again optimized to zero overhead if it's statically allocated.

Whole program A.K.A. link time optimization was mentioned, but it's also sometimes practical to just build the entire program in a single translation unit. (This doesn't just apply to small games, I worked on a PS3 project using Unreal 3 where we did this.) Lots of NES developers seem to work this way with C or assembly already, but for a different reason than assisting compiler optimizations.

Even if your instances have to by dynamic and passed only by pointer, the overhead in practical terms is occupation of 2 bytes on ZP for the pointer, and constant use of the Y register for indirect access. Even this doesn't have to be a terrible burden if your compiler has a good optimizer. CC65 is atrocious with pointers, but this is not something that has to be an inherent problem with other compilers, or C++, or objects.

Even if the overhead is bad, as always, many choices are a tradeoff, and it depends what you want to do with them. There's tons of stuff that can be useful even if inefficient. You're insisting on a specific solution for a nonspecific problem.

DRW wrote:
I would suggest against using stuff like object orientation or templates.

Why templates? Templates are typically used to trade code space either for better performance, or for smaller source code. Why do you think this is a bad trade? Sometimes it's bad, but space is often not the primary constraint. There's a lot of other features of C++ besides classes and templates too. FaTony mentioned constexpr and it has huuuuuuuuuuge applications for this kind of thing (and it gets mentioned a lot in the OP's video).
Re: Making a NES game in C++
by on (#182559)
I've seen the video now as well.

The only reason why CC65 created the additional stuff (LDX, TXA) is because the function is the main function which is called int main(). Therefore, those two additional commands are the return value. Of course you wouldn't have this in a function with return type void. Therefore, this argument is already invalid regarding CC65's optimization.
Maybe someone with a YouTube account should tell this guy, so that clueless people like FaTony don't jump to conclusions.

The other stuff about the CC65 not being able to inline functions: So what? If you really have a function that literally only consist of one line anyway, you can simply write it as a macro.

So, I'm still open to a better compiler. But the only thing that this example showed is the ability to inline functions by the compiler. Hm. Alright. But still nothing revolutionary. Most of my functions have more than one line.

rainwarrior wrote:
Code:
const int NUM_OBJECTS = 16;

class Object
{
protected:
   uint8 index;
   
   static uint8 used[NUM_OBJECTS] = {0};
   static uint8 member_data[NUM_OBJECTS];
   
public:
   Object()
   {
      for (uint8 i=0; i<NUM_OBJECTS; ++i)
      {
         if (!used[i])
         {
            used[i] = 1;
            index = i;
            return;
         }
      }
      throw OutOfObjects();
   }
   
   ~Object()
   {
      used[index] = 0;
   }
   
   void do_something(uint8 blah)
   {
      // only non-static overhead is fetching the index, and possibly virtual function calls
      member_data[index] = blah;
   }
};

Wow! A class with only one member that is nothing but an integer for a general array. I'm sure that's really a reason to use object oriented programming. :roll:

Besides, your do_something function will be ineffective. Because unless all your objects of this class are global (which defeats the purpose of the constructor, since you could just assign the values directly in this case), the access to index will be by a pointer: this->index. If you have 20 other functions using index, this will be a whole bunch of indirect accesses.

And even if your class objects are all global (i.e. the adress of each instance of index is known at runtime), then optimizing the indirect access away would mean to copy all of your member functions in ROM, one batch of functions for each object. So, if your class has five functions and you declare five global instances of the class and optimize for direct access of the members, then you now have 25 functions in your ROM code.

So, no, sorry, this example doesn't disprove any of my objections at all.

rainwarrior wrote:
Why templates? Templates are typically used to trade code space either for better performance, or for smaller source code.

Exactly. And you don't have that much ROM space in an NES game.
Besides, what important use could you have for a template in an NES game anyway?

rainwarrior wrote:
There's a lot of other features besides classes and templates. FaTony mentioned constexpr and it has huuuuuuuuuuge applications for this kind of thing (and it gets mentioned a lot in the OP's video).

You mean stuff like that?
Code:
constexpr int multiply (int x, int y)
{
    return x * y;
}
 
const int val = multiply(10, 10);

O.k., it's a neat little feature. Introduced a mere five years ago, after C++ existing since 1979. So, yeah, that's really a feature that we just can't live without. :roll: All of that is surely useful, but seriously, if someone rejects programming a game because he doesn't have that, there's nothing I can do to help him anymore.
Code:
#define multiply(x, y) ((x) * (y))
Re: Making a NES game in C++
by on (#182560)
DRW wrote:
Wow! A class with only one member that is nothing but an integer for a general array. I'm sure that's really a reason to use object oriented programming. :roll:

Wow! :mrgreen:

thefox wrote:
What video? If you're talking about https://www.youtube.com/watch?v=CdJOSfK1ja0, cc65 produced close to optimal code (when optimizations were enabled) for the simple "hello world" of setting the border color on C64.

The first example in that video gets optimized by CC65 fine, but there's a second example near the end that doesn't and I think is actually a good demonstration of one kind of thing it can't do that GCC can.
Re: Making a NES game in C++
by on (#182561)
rainwarrior wrote:
thefox wrote:
What video? If you're talking about https://www.youtube.com/watch?v=CdJOSfK1ja0, cc65 produced close to optimal code (when optimizations were enabled) for the simple "hello world" of setting the border color on C64.

The first example in that video gets optimized by CC65 fine, but there's a second example near the end that doesn't and I think is actually a good demonstration of one kind of thing it can't do that GCC can.

Yeah, that one is valid.
Re: Making a NES game in C++
by on (#182562)
rant:

WTF is with the current kids making videos for everything that would be better as text. No, I'm not wasting 13 minutes watching that.

end rant
Re: Making a NES game in C++
by on (#182563)
rainwarrior wrote:
The first example in that video gets optimized by CC65 fine, but there's a second example near the end that doesn't and I think is actually a good demonstration of one kind of thing it can't do that GCC can.

Wasn't that just the fact that the compiler was able to inline the function?
(I'm talking about the 13 minute video. Not about that video from the original post that's over an hour long.)

If my functionality in an NES game only consists of one line where a variable is assigned, then I wouldn't use this as a function and rely on the optimization abilities of the compiler anyway. This simple assigment, when needed more than once, would be a macro since I avoid functions that need parameters as much as possible when doing a game for the NES.

As I said: All this is neat and I'm actually for these kind of features. If anybody can make a usable version of the gcc or a converter that can produce worakble 6502 code that has the quality of professional optimized code, then this is good. I would use this compiler if it was in a workable state.

But if anybody who wants to do an NES game rejects the idea because of this video or because he doesn't want to use pure C instead of C++, then this is really exaggerated. The example wasn't that convincing and C++ either has features over C that you cannot really effectively use anyway (object orientation), or it's pure syntax sugar that you can easily do without (inline functions, const expressions).
Re: Making a NES game in C++
by on (#182565)
DRW wrote:
Wasn't that just the fact that the compiler was able to inline the function?

The specific example is an inlined function (and all the static analysis and optimization around it), but it's just an example. If he were to throw hundreds of lines of code at it, it would still make many valid optimizations, and it would take hours to explain everything that it's doing. That wouldn't be the point of the video, he's just trying to illustrate that it can do useful things.

DRW wrote:
As I said: All this is neat and I'm actually for these kind of features. If anybody can make a usable version of the gcc or a converter that can produce worakble 6502 code that has the quality of professional optimized code, then this is good. I would use this compiler if it was in a workable state.

It's right here if you want to try it: https://github.com/lefticus/x86-to-6502

I do plan to give it a shake when I'm finished my current project. I don't know how good it is yet, but I like what I've seen a lot.

DRW wrote:
The example wasn't that convincing and C++ either has features over C that you cannot really effectively use anyway (object orientation), or it's pure syntax sugar that you can easily do without (inline functions, const expressions).

There's a problem with examples that they have to be simple and small to be understood and explained during a 5 minute or 1 hour lecture. He wrote Pong with it, and demonstrated a bunch of interesting C++ features in that lecture linked in OP.

Demonstrating the actual utility of a language like C++ takes big, practical examples, and a lot of time and experience. Quite frankly I don't see why FaTony or anyone else in this thread needs to justify to you why C++ is worthwhile. Some of us have already started from the premise that it is worthwhile, and would like to discuss this new development from that standpoint. (Or at least, I would, and I said so.)

If I could give you a one paragraph explanation of why I think C++ features are very useful and practical and a worthwhile advantage over C, I would, but it's a complicated subject and you seem hostile to the idea anyway, so why would I want to? Normally if you wanted to learn this stuff you might take a class or an internship and have a professional to teach you these things over many months. I can't really imagine doing this in a forum post for you.

DRW wrote:
But if anybody who wants to do an NES game rejects the idea because of this video or because he doesn't want to use pure C instead of C++, then this is really exaggerated.

The reason I mocked you in my earlier post is because I don't think anybody but you really wants to argue about this. It's not an informative or interesting argument to have. You made your point about that in post 3, I don't really get why you keep bringing it up. I responded to your claim about the efficiency of classes not to argue with you, but for the benefit of anyone else reading because I think you're spreading misinformation.

If you're someone who has been using C++ for years and has seen its utility, the ability to use it with such a limited processor like the 6502 might be a really interesting prospect. Do you really think I owe you a lesson on why C++ is useful? Can't you just let others talk about it even if you don't understand why they're interested? It's quite exhausting to have you belligerently demand that we justify our interest in it.
Re: Making a NES game in C++
by on (#182567)
DRW, you seem to be willing to discount the fact that it is easier to write and develop modern C++ than old C (which is the type of C you necessarily have to write using cc65).

IDE support is way better. You get true namespaces, you get templating (which can make for writing more readable code). If your goals for a project are long term, you often spend time building tooling to help you write aspects of the project more quickly and easily. The tooling around C++ is incredibly mature and makes writing it not only less painful but occasionally joyful. You gain access to Static Analysis as well as better linting - tools that profession game developers rely on to create fast and correct code.

I have not completed a full NES game, so my opinion is one of a professional software developer, rather than a hobby game developer. I do believe my points stand, though.
Re: Making a NES game in C++
by on (#182569)
I'll give the converter a try.

rainwarrior wrote:
I responded to your claim about the efficiency of classes not to argue with you, but for the benefit of anyone else reading because I think you're spreading misinformation.

But your example wasn't something that you would find in real object orientation. It was just an example of using classes for the sake of using classes.

A real object oriented approach would be something like this (written in pseudo code):
Code:
class Character
{
    int X;
    int Y;
    bool IsActive;

    virtual void Move();
}

class Player : Character
{
    int Energy;

    override void Move();
}

class WalkingOpponent : Character
{
    Direction CurrentWalkingDirection;

    override void Move();
}

class WalkingOpponent : Character
{
    int JumpVelocity;

    override void Move();
}


// Somewhere else:

Player Player;

Character *Opponents[5] = { null, ... };


This is how you use object orientation.

Your example wasn't even valid as a simple example since it wasn't object orientation at all. As your member variables, you basically used a bunch of global variables (encapsulated into the class, but nevertheless a bunch of glorified global variables) and the only thing that your class objects actually included was an index to reference these global arrays. That's not what object orientation is about.

Why did you even declare a member in the class to begin with? If the class has only one member (and will only ever have this one member since the actual data is supposed to be in the static arrays) and this member is nothing but the index/ID/slot for all the arrays, then why don't you declare each index that you need as a simple global integer to begin with? Just so that you can call myObject.DoStuff() instead of DoStuff(myIndex)?

Sorry, your code is not clean object orientation. That's a bunch of static arrays and a bunch of indices to use the static arrays. The class is basically nothing but a glorified namespace here.


rainwarrior wrote:
If you're someone who has been using C++ for years and has seen its utility, the ability to use it with such a limited processor like the 6502 might be a really interesting prospect. Do you really think I owe you a lesson on why C++ is useful? Can't you just let others talk about it even if you don't understand why they're interested? It's quite exhausting to have you belligerently demand that we justify our interest in it.

You seem to be under the impression that I haven't used it myself. I'm not a hobby programmer, I do programming for a living. While I haven't used C++ itself for a few years now (after working with it for three years), I work with C# which has a similar object oriented approach, so I know what I'm talking about.

And I can tell you this:

If you use a clean object oriented approach as my example above, my argument still stands: With this approach you have indirect access for everything, meaning it is unsuitable for an NES game. And nobody has tried to question this until now.

If you use the approach from your example, then this has nothing to do with object orientation anymore. The class as a container for static arrays with the only non-static member being an index for said static arrays is still pure functional C-like programming with the only difference being that the class serves as a namespace for the arrays and the index doesn't need to be passed to the function (instead, you have to precede the object name in front of every function call).

So, no, your example is not a description how C++ can still be useful for the NES without any overhead. Your example just shows that you can put a bunch of global stuff into a class just for the sake of using a class.


pstalcup wrote:
DRW, you seem to be willing to discount the fact that it is easier to write and develop modern C++ than old C (which is the type of C you necessarily have to write using cc65).

That's total nonsense. I never said that.
In fact, the contrary is true: It is much, much easier and cleaner to develop stuff with a good object oriented model, occasional templates, a good standard framework and variables as local and as temporary as necessary.
And this is what I have been doing almost every workday in the past nine years or so.

What I want to say regarding the NES is: All the neat things that make your programming life easier in a modern environment just cannot be used on the NES without a huge performance hit. If you want to write a game for the NES, you need to do a bunch of dirty stuff like:
- using macros
- declaring a struct with a bunch of arrays instead of an array of a struct
- functions whose "parameters" are global variables that are set before each function call (with macros, so you don't forget to set any variable) instead of actual parameters.

And keeping this in mind, a clean object oriented character and level model will be totally unfitting for an NES game.
The only way you could efficiently use classes on the NES is by filling it with a bunch of static variables which defeats to purpose of using classes in the first place.

Yes, C++ has some features that don't produce any overhead: Inline functions, const expressions, reference variables, local variables that can be put anywhere into a function instead of just the start of a block.

But firstly, these little things are barely enough to demand a full C++ compiler since it's basically just syntax sugar.
And secondly, the usefulness of these things is mundane anyway. There's nothing to "talk" or "discuss" about it. Yes, inline functions are better than macros. Period.

But what is worthy of discussion: Is it worth creating a full C++ compiler for the NES in the first place? Or will the things that really define C++ over C be the stuff that cannot be used in an NES game efficiently anyway, so we should rather spend time improving the existing C compiler instead?

And I think: The really strong, defining features of C++, which are object orientation, templates and the C++ standard library, are all things that you couldn't (or shouldn't) really use in an NES game anyway.

(If we're talking about a tool that converts x86 Assembly to 6502 Assembly, so that you can use a professional compiler that already exists: Now, that's a totally different topic of course since the C++ features are not an issue here because GCC already has them while the conversion tools only needs to convert raw Assembly into another form of raw Assembly. But a new C++ compiler for the NES: Waste of time.)

That's my standpoint. My standpoint is not "I discount the fact that it is easier to write and develop modern C++ than old C."

pstalcup wrote:
I have not completed a full NES game, so my opinion is one of a professional software developer, rather than a hobby game developer. I do believe my points stand, though.

Well, my opinion is that of a professional software developer who also has completed a full NES game as a hobby project. And I must say: All your statements about modern programming are of course true, but you misunderstood what I wanted to say.

Rainwarrior's example doesn't justify object orientation to begin with since he just uses the class as a simple container for static variables.

And my above object oriented example would be unfitting for an NES game since the compiler would have no choice but to use indirect access every time a member is referenced. Do you disagree with this?
Re: Making a NES game in C++
by on (#182581)
DRW wrote:
Why did you even declare a member in the class to begin with? If the class has only one member (and will only ever have this one member since the actual data is supposed to be in the static arrays) and this member is nothing but the index/ID/slot for all the arrays, then why don't you declare each index that you need as a simple global integer to begin with? Just so that you can call myObject.DoStuff() instead of DoStuff(myIndex)?

I was demonstrating that by reorganizing your data into static arrays, you can reduce or eliminate the overhead of indirection. Any member function now requires only a single index lookup, and the rest of the data can be accessed with absolute indexed instructions (e.g. LDA abs, X). You can access the static arrays 30 or 50 times in a function call, but you only need the indirection once. (Possibly shared over multiple subsequent member function calls automatically too, if inlining and static analysis works well.)

It removes that one specific overhead, and it retains all the other features of a class. You can still use inheritance, and everything that goes along with it. It acts the same as any other class, it just stores its data externally.

Inheritance is the defining feature of a class, and it has nothing to do with where or how the data that class uses is stored. Inheritance is about where your code goes and how it is reused.

And I'll try not to beat this dead horse much further, but you don't have to dynamically allocate classes for them to be useful as classes.

DRW wrote:
Rainwarrior's example doesn't justify object orientation to begin with since he just uses the class as a simple container for static variables.

It's an example of how to reduce the overhead of indirection, and nothing more.

If you want a justification for why classes are useful, I dunno, go read a book or something? I'm not interested in that debate. Do you think anyone else here is?

DRW wrote:
You seem to be under the impression that I haven't used it myself. ... I know what I'm talking about.

I'm merely under the impression that you have a very narrow opinion of what C++ is useful for. I was trying to honestly explain why I think otherwise, the reasons for which have to do with my own personal experiences with it. I don't know how to convince you otherwise, because what convinced me did take years. I also have no desire to convince you of my opinion. What I do desire is to be able to discuss this new development with people who share that opinion, without you interjecting aggressive and uninformative rants about why you think it's useless. That seems to be impossible though.
Re: Making a NES game in C++
by on (#182583)
I think part of the problem is a memory layout quirk that C++ inherits from C: even if objects are part of a statically allocated pool, all data members of a class/struct must be contiguous in memory except for alignment padding. As far as I'm aware, the C++ language has no built-in facility for the sort of striped arrays that the 6502 can access efficiently. The same is true for variable allocation, as the language offers no way to prove statically that a particular set of functions with automatic variables lack recursion.

I use polymorphism in my assembly programs. The Curse of Possum Hollow has at last count 47 different actor classes, each with its own override for the move method, though they all use the same draw implementation.
Re: Making a NES game in C++
by on (#182585)
rainwarrior wrote:
I was demonstrating that by reorganizing your data into static arrays, you can reduce or eliminate the overhead of indirection.

Yeah, fine, but by doing this, you're not using object orientation anymore. You're using the equivalent of an index variable in combination with global array, i.e. you're using what C is capable of as well. Your only difference is that you put it into two brackets with the word "class" at the top.

You don't need to explain me the technical backgrounds. I know fully well what your code does. The only thing I'm saying is: This is not object oriented programming. It's pure functional C-like programming, with some globally valid arrays, some indices for access and all the stuff that you know from typical C programs. Only that you build a class around it.

Whoever dislikes C because he wants object orientation would dislike this attempt as well.
And whoever is o.k. with this construct can just as well use global arrays and doesn't need this class.

Whatever your code is, it is not an overhead-less version of object orientation. It is a functional programming method, put into a class that serves no real purpose other than serve as a namespace.

rainwarrior wrote:
You can still use inheritance, and everything that goes along with it.

How do you want to use inheritance if your only member ever is just the index? You do realize that static variables and functions are basically independent from inheritance, i.e. it makes absolutely no difference if you write:
Code:
class A
{
    static int B;
}

class C
{
    static int D;
}

or if you simply write:
Code:
class A
{
    static int B;
    static int D;
}

B and D, at heart, are global anyway, so it makes no difference which of the two classes-as-namespaces you use to store these otherwise global variables.
O.k., with the private/public/protected keywords, you may be able to hide them from each other in the same way separate source files can hide variables from each other, so that class A cannot accidentally access anything from class C. But that's about it.

As I said: A glorified namespace.
The static members don't really belong to the object that uses it.

If you have only one object, you still have 16 slots for every piece of data.
And if you create the 17th object, your game would go boom.
And nothing prevents object 1 from accessing array item 2.

There's no built-in correlation between the index and the array. It's all hand-made, just like in a purely functional programming language.

Your example might use a class without much indirect access, but:

Your. Example. Is. Not. Object. Orientation.

rainwarrior wrote:
If you want a justification for why classes are useful, I dunno, go read a book or something? I'm not interested in that debate.

:shock: Are you kidding me? Are you FUCKING kidding me?

After all those texts where I layed out my stance in details, you still believe that my message is: "Classes are not useful"? That's what you think my argumentation boils down to?

Especially after the stuff I said to pstalcup, you have the nerve to summarize my statements as if I thought classes aren't useful and I'd have to read a book about what classes are?

You know what? Screw you!

Seriously, I have no problem with people disagreeing with me. But if you're not even willing to grasp my arguments on a purely informational level, i.e. the things that you disagree with are not even what I fucking said or believe, then screw you!

Damn!
Re: Making a NES game in C++
by on (#182587)
I don't really feel like playing a game of "define object oriented" with you.

You seem to have two main points here:
  • 1. Some C++ features have overhead.
  • 2. Features of C++ that have overhead aren't worth using.

Do you think this is an unfair summary?

My summarized counter-argument is:
  • 1. Causes of overhead are manageable. It can be reduced, or even eliminated.
  • 2. The features can be useful, and sometimes overhead is a desirable trade.

I think that's about as direct as I can make it.
Re: Making a NES game in C++
by on (#182589)
rainwarrior wrote:
You seem to have two main points here:
  • 1. Some C++ features have overhead.
  • 2. Features of C++ that have overhead aren't worth using.

Do you think this is an unfair summary?

Yes because I'm only talking about NES games, dammit! I was never talking about programs in general, i.e. stuff that you write for modern PCs or even for an 80s DOS computer.
So, your whole argument about reading a book about classes is total bullshit, unless you're actually thinking of a book about object oriented programming in C++ for the NES. Which you probably did not, did you?

You know that, when working with CC65 for the NES, it is good practice to do some things that otherwise would be considered bad style?
Like when you use an item of an array a few times: It's better to save the item to a temporary variable and to use this variable, then save it back at the end.
Remember this tip? I guess you do.

Now think about it: How much more would this count for indirect access?
An array is already bad enough as it is with its LDA array, X. The version LDA (pointer), Y is even worse. And that's the way member variables are always used with classes (since the function cannot know from which object it's called), making a clean object oriented approach basically unusable for a productive NES game.

rainwarrior wrote:
My summarized counter-argument is:
  • 1. Causes of overhead are manageable. It can be reduced, or even eliminated.

That's true. But by doing this, your code is not object oriented anymore. You're just encapsuling a bunch of global variables and functions into a class.
If you're not using the paradigms of object orientation and simply use the class as a container for otherwise global functions, it's not object orientation anymore.
But if it's not object orientation, there's no real need to have a C++ compiler. Because static arrays with a fixed size and a bunch of index variables can be used in basically the same way without a class at all. The class brings you no other advantage except for some encapsulating name, but you don't use it the way a class is intended.

rainwarrior wrote:
  • 2. The features can be useful, and sometimes overhead is a desirable trade.

Yes, sometimes. But not in the case where every single member variable of every single object would always be accessed by indirect access.
I saved a ton of ROM space by storing into temp variables whenever something like valueArray[index] was used more often. How much worse is it if you use objectArray[index].value and not storing this into temp variables?
You cannot tell me that this is an acceptable overhead if even something like arrays of a struct are too bad for an NES game and you should use a struct with a bunch of arrays instead where only one instance of the struct is ever created.

And don't give me that inlining functions bullshit for this case. In a clean object oriented approach, your class would probably have a good bunch of functions. Copying all these functions for all the 16 objects that you delcare, so that you can use them with direct variable access is just as bad because now you need 16 times as much ROM space as before.


So, this is my summary:

If you do object orientation in C++ in a clean way, in the way it is intended, where each object actually has only the members that belong to it, you will get large and slow code.

If you circumvent these problems by using static arrays in the class, then you defeat the purpose of using classes in the first place. The static arrays would have no correlation to class objects. Consistency would only be guaranteed because you made sure that each object only accesses the part of the array that it is supposed to. Now, your code is not object oriented, it is function-based. And for this, a C++ compiler isn't necessary. You can simply use regular functional C code right away.
Re: Making a NES game in C++
by on (#182591)
NES has been a context for everything I have said in this thread, but yes I do think things people write or say about C++ in other contexts still apply to NES.

That's kind of the whole point of the x86 to 6502 converter, actually. It applies something that was appropriate for the 386 to the 6502.
Re: Making a NES game in C++
by on (#182593)
At some point it becomes less effort to try it and see how well it can be made to work than argue it to death.
Re: Making a NES game in C++
by on (#182594)
rainwarrior wrote:
NES has been a context for everything I have said in this thread, but yes I do think things people write or say about C++ in other contexts still apply to NES.

If that was true then this would also count for C: "What people write about C in other contexts still applies for the NES."
But if that was true, then why do lists exist that explain which features of C you should not use specifically when writing NES games?
http://shiru.untergrund.net/articles/programming_nes_games_in_c.htm wrote:
Avoid local variables as much as possible, make them static at least

Is this true or false? If it's true, why do you think generic C++ talk automatically applies to the NES if that's obviously not the case for C?

rainwarrior wrote:
That's kind of the whole point of the x86 to 6502 converter, actually. It applies something that was appropriate for the 386 to the 6502.

The x86 to 6502 converter is something completely different yet again. As far as I understand it, doesn't care at all if you write code in C oder C++ or Pascal or Cobol. It only converts x86 Assembly to 6502 Assembly. So, this converter is neither a positive, nor a negative example for our discussion:

If you write C++ with a lot of classes, you would still have x86 Assembly code with a lot of indirect access. A PC doesn't care, but if you try to convert this Assembly to the NES's Assembly, you would run into all the problems I listed.
If you write C or C++ without classes and without local variables, you will get pretty fast x86 Assembly code, which doesn't matter since a PC is fast enough either way, and if you convert it to 6502 Assembly for the NES, it will work as well.

So, this converter is totally out of the scope of this discussion since no C++ compiler was written for it anyway.
The thing I'm talking about is: Does it make sense to write a C++ compiler for the NES or would this be a waste of time since you already have a C compiler and you shouldn't use most of the C++-specific features anyway?

That an x86 to 6502 converter always makes sense because it can convert code from any x86 programming language from any x86 compiler, so you can let your code even be optimized by Visual Studio and turn the Assembly output into an NES game, that's of course something we don't need to argue about. Of course that makes sense. But this has nothing to do with "Should you use C++ classes for NES games" or "Should you take the time to write a C++ compiler for the NES?"

snarfblam wrote:
At some point it becomes less effort to try it and see how well it can be made to work than argue it to death.

Well, yes, of course. This is a forum after all. This is about talking. If you want to try it, you wouldn't do this in a forum anyway, but in your IDE. So, talking about it here prevents no-one from trying it out on his computer.
Re: Making a NES game in C++
by on (#182601)
It would be nice for this thread to not get bogged down by combative posts arguing in extremes.
DRW's strawman wrote:
"What people write about C in other contexts still applies for the NES."

General tips for best-practice C absolutely still apply, including those about program organization. There may be additional architecture-specific notes, like what Shiru wrote. What exactly is there to be misunderstood here? The same argument applies to C++, and as usual there would be notes on which features would be better used or not used, and how. Even when writing C targeting 68000, I know the compiler is going to push every argument to the stack, so in some cases I will pass a reference to struct(s) instead of many properties. A good example could be collision detection between two objects.

DRW wrote:
Does it make sense to write a C++ compiler for the NES or would this be a waste of time since you already have a C compiler and you shouldn't use most of the C++-specific features anyway?

It's not a waste of time - it would be educational, and if nothing else, you could at least have first-class support for basic polymorphism, instead of typecasting from generic object_t * structs to more specific object classes. Type safety never hurt anybody.*

While I have many reservations about the feasibility of creating a reasonably efficient C++ compiler that supports the features a "modern C++ user" would expect, it would be interesting, and I don't see why at least the most basic class features couldn't be done.
Re: Making a NES game in C++
by on (#182602)
mikejmoffitt wrote:
General tips for best-practice C absolutely still apply, including those about program organization. There may be additional architecture-specific notes, like what Shiru wrote. What exactly is there to be misunderstood here?

I don't know what's to be misunderstood here. The things you say are what I have said the whole time.
Re: Making a NES game in C++
by on (#182612)
This might be a strange opinion but I'd rather use a fake ASM language to 6502 converter than use a C compiler. Like a RISC CPU with 256 registers mapped to the direct page.

Obviously it would need peephole optimizations.
Re: Making a NES game in C++
by on (#182617)
There is alot of action in this (interesting!) thread to keep up, so even though it happened in the morning it's already two pages ago. Hell, how you guys find the time to repeatedly write lengthy, time-consuming posts? Anyway, this grabbed my attention:
rainwarrior wrote:
Whole program A.K.A. link time optimization was mentioned, but it's also sometimes practical to just build the entire program in a single translation unit. (This doesn't just apply to small games, I worked on a PS3 project using Unreal 3 where we did this.)

Wait, what?!?

A PS3 game, in one translation unit?

Like what, there's one main file, and every other file is included in it, like tens of #includes at the top that each includes tens of other files that includes tens of other files... That does take a heck of a time to compile, even for a tiny change, doesn't it?

Also, in rainwarrior's small C++ example, I'm much more concerned with the throw statement, which is a big memory bloat in embedded systems that I've experienced many times on different targets. I carefully "dummy-out" some functions that occupy (directly or indirectly) many kB of code (in particular, some function that print the exception object when an unhandled exception is thrown...), but you're still left with all those typeinfo structures into rodata, and exception handling needs RTTI, so you can't really get rid of them.

Those members access, this->index, are much more benign in comparison. While indirect access may hinder some optimisations, by itself it only cost an average of one more CPU cycle, and one less byte, than an indexed access. this can already be in zeropage, if parameters are passed by zeropage. There are situations where these extra cycles aren't that critical, but now you can reuse more of your code, and save space. Remember folks that Shiru always said that memory, not speed, is the main concern when coding NES games in C, and I expect this would be the case for C++ as well.
Re: Making a NES game in C++
by on (#182618)
Jarhmander wrote:
Like what, there's one main file, and every other file is included in it, like tens of #includes at the top that each includes tens of other files that includes tens of other files... That does take a heck of a time to compile, even for a tiny change, doesn't it?

I've heard this being called a "unity build" (not to be confused with the game engine of the same name) if you want to google for more information. I've heard it can actually speed up the builds in some cases due to various factors (e.g. less disk I/O), although I have never tried it myself.
Re: Making a NES game in C++
by on (#182620)
Jarhmander wrote:
Like what, there's one main file, and every other file is included in it, like tens of #includes at the top that each includes tens of other files that includes tens of other files... That does take a heck of a time to compile, even for a tiny change, doesn't it?

Sounds like a job for precompiled headers. If implemented correctly (the big complication stems from correct Makefile creation, i.e. making sure a modified header gets re-precompiled (heh) if changed), and depending on project size, these can *massively* speed up compilation times.
Re: Making a NES game in C++
by on (#182621)
Yes, "unity build" was the technique, and it was to keep compile times lower, which it did. Unreal 3 had a big problem with header dependencies, and on the GCC PS3 build (PowerPC target, circa 2008) it helped.
koitsu wrote:
Sounds like a job for precompiled headers. If implemented correctly (the big complication stems from correct Makefile creation, i.e. making sure a modified header gets re-precompiled (heh) if changed), and depending on project size, these can *massively* speed up compilation times.

Precompiled headers were thoroughly investigated, and unable to do an adequate job by themselves in this particular case. A lot of time (and money-- for hardware and distributed build software) was invested addressing the build time problem on that project. (Also, we only did this for the PS3/GCC build. On the 360/PC/MSVC platforms, the unity build idea was tried too but ultimately a more conventional build style worked better. In all cases the build times were horrible, the unity build was a desperate attempt to make it less so.)

Anyhow, I only brought it up to point out that alternative build solutions are not uncommon, and often very appropriate on specific platforms.

Jarhmander wrote:
Also, in rainwarrior's small C++ example, I'm much more concerned with the throw statement

I used "throw" there just to indicate conceptually that you have to deal with that problem in some way. I wasn't suggesting that you'd actually want to use a throw, or even to use that particular method for assigning an index. (In the OP's video the guy just shrugged a question about exceptions with "it didn't come up".)

Jarhmander wrote:
Those members access, this->index, are much more benign in comparison. While indirect access may hinder some optimisations, by itself it only cost an average of one more CPU cycle, and one less byte, than an indexed access. this can already be in zeropage, if parameters are passed by zeropage.

It's not just one CPU cycle. You'd need to set Y differently for every different member (which also takes back that saved byte). The index suggestion lets you use striped arrays, which would share the same index value for every member. (Pretty similar to how I'd probably organize and use the data in assembly.)

It also can use X or Y, so it frees up Y to be used with a different pointer at the same time. I don't know if x86 to 6502 conversion could be put up to this task, but it might be really interesting to see a real 6502 C++ optimizer try to automatically reorder accesses to be contiguous so it could use mostly INY / DEY to adjust Y.

Anyhow, the point wasn't to suggest this was universally better, just that there are ways you can manage indirect accesses, and use 6502-friendly data structures, even with dynamically used C++ classes. (It was also an argument against DRW's incessantly repeated premise that indirection is a problem. It's not necessarily a problem.)

Jarhmander wrote:
There are situations where these extra cycles aren't that critical, but now you can reuse more of your code, and save space. Remember folks that Shiru always said that memory, not speed, is the main concern when coding NES games in C, and I expect this would be the case for C++ as well.

I'd say that's generally true for NES games once they get large enough, not just in C, but in assembly too. Performance is usually easier to budget than ROM or RAM if you're keeping an eye on it. There are still lots of cases in my large project where I've felt the need to trade space for speed. Size of everything is an issue, and code's not even the biggest fish there. Data space has been the much more important constraint as my game got large.

Depending on the needs of your project, the bottleneck really doesn't have to be ROM space. Part of the reason it's a problem with CC65 is that it can't very easily put C code in more than one bank, mostly due to its heavy dependency on CRT functions for generated code (and inability to have multiple copies of it at link time). If you can solve this problem, code space could be a much smaller issue. All of Shiru's games are NROM, aren't they? Even using banks for just data would open a lot of space for code in the main bank.

Since we're talking about GCC, the -Os (optimize for size) flag seems more appropriate than the -O3 (optimize for speed) used by the author in that video.
Re: Making a NES game in C++
by on (#182636)
Thanks for the answers regarding so-called "unity builds".

After a quick read on the net it seems it can speed-up build by some large factor, but I noticed too that those blogs praising those "unity builds" are quite old, over 5 years. In 2008, there weren't quad-core (or more cores) available in PCs, right? Because I don't think the "unity build" is fast anymore now, because with over 4 cores you can build translation units in parallel effectively; doing the "unity build" way OTOH prevent parallelization.

Every time I hear people complaining that build time (for an iteration, not a full rebuild) is too long, I invariably think either they have way too much coupling between "modules" (at least, at the file level) or they fucked up their build systems real hard.

EDIT: I thought of some compiler extension that could be used and implemented into a C compiler to implement splitted array of structures. I really do not miss the near and far keywords from back in the day (though they are still used in some C compilers targeting 8-bit microcontrollers), but it could be useful to convey an optimization to the compiler, nevertheless.

A near pointer is 8 bit in size, it can fit in an index register; an array of near objects have their members split, and these kind of array cannot hold more than 256 objects.

Code:
// file: object.h

// (include guard and #include's)

#define NUM_OBJECTS 8

typedef struct
{
    uint8_t x;
    uint8_t y;
    uint8_t type;
    uint8_t state;
} Object;

extern near Object objects[NUM_OBJECTS];

// functions
void Object_move(near Object *object, uint8_t delta_x, uiunt8_t delta_y);
...

// ------
// file : object.c

near Object objects[NUM_OBJECTS];

// functions
void Object_move(near Object *object, uint8_t delta_x, uiunt8_t delta_y)
{
    object->x += delta_x;
    object->y += delta_y;
}

...


a near pointer is absolutely impossible to convert back to a normal pointer, because the memory layout of the near structure is different.

The fun part is not the syntax, but the implementation. One way to do it is to have member access be resolved by the linker; the offset cannot be known by the compiler because it depends on how much objects of this type exists in the final executable. Each near object would have to be output in special sections, one encoding its type for near objects and the other the layout of this type. The linker would ensure that for each section representing a type, offsets do not exceed 256 bytes. The limitation is you could not create e.g. 2 distinct arrays of near Object, each one having 256 objects, because of how the algorithm I described works, even though we all know we can do it in assembly easily.
Re: Making a NES game in C++
by on (#182647)
Yeah I haven't used near and far since learning C++ on my 286 in the early 90s. I don't think I'd want to reuse the keyword to mean something different like that, but it would be nice if there was some sort of type modifier keyword that could be used to stripe a structure for arrays.

In C++ you can do a lot of stuff by passing by reference and overloading operators, like have a native-looking array class that can store its members internally in striped 8 bit arrays.

I dunno about viability of unity builds for compile performance today. I haven't tried to use one for that purpose since then. Though link time optimization is a lot stronger now, I think a single translation unit approach can still result in a runtime performance gain, and it might very well be applicable to an NES target.
Re: Making a NES game in C++
by on (#182743)
rainwarrior wrote:
Yeah I haven't used near and far since learning C++ on my 286 in the early 90s. I don't think I'd want to reuse the keyword to mean something different like that, but it would be nice if there was some sort of type modifier keyword that could be used to stripe a structure for arrays.

Yeah I do not really miss these keywords, buuuuut they could be useful, and these keywords, well, they are not random keywords, (some) programmers are familiar with them, they know what they mean. And the meaning is technically not different: it is merely an optimization to reduce the size of the pointers as well as potentially reduce the code size dealing with them. I understand It might be more "orthodox" to define a near pointer as a pointer into zeropage, but that's not nearly as useful.

I'm just not sure yet if doing this would broke the type system or do something non-conformant (safe the fact that it's a non standard keyword and thus has no standard-defined semantics).

rainwarrior wrote:
In C++ you can do a lot of stuff by passing by reference and overloading operators, like have a native-looking array class that can store its members internally in striped 8 bit arrays.


I can't see how it could be done:
  • operator. cannot be overloaded;
  • using operator-> won't work, because that operator should return an actual pointer to struct/class or an object with operator-> overloaded. This won't let you a chance to trick the address generated for the member;
  • even if you did manage to generate an address to get that member, you'll be in trouble for multi-byte members, because the C++ will naturally assume that all bytes of the accessed member are contiguous (as with any other object on any storage), so you'll be forced to generate a fancy proxy object that convert into the underlying type and implement operator= that write the rhs back into the splitted array. I can't figure out how the compiler would generate efficient code for this.

But I would like to hear what you had in mind, I possibly missed other options that may make it possible, and I can't realistically prove that it cannot be done.
Re: Making a NES game in C++
by on (#182745)
Hmm, I meant you can make a class that "feels" like a 16 or 32 bit integer array but internally stores its data to 8 bit stripes via overloaded operators (operator () and = but also ++, +=, or any other modifying ones too). I've definitely done this before, but honestly it's not a solution I can really rattle off the top of my head (a lot of tricky little ramifications to remember and deal with). I'd have to do some digging to find a practical working implementation.

I wasn't really thinking about doing it for all members of a structure, but maybe you could make a structure out of such classes? I'm not sure.