Aren't you afraid that NES Maker would just bring lazy noobs

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
aa-dav
Posts: 106
Joined: Tue Apr 14, 2020 9:45 pm
Location: Russia

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by aa-dav » Sun May 03, 2020 10:41 pm

It's interesting for me to compare optimized C code.
Let's rewrite it as mentioned above: pass parameters as pointers in zero-page:

Code: Select all

extern char *src, *dst;
#pragma zpsym( "src" )
#pragma zpsym( "dst" )

void str_cpy()
{
	while ( *dst++ = *src++ );
}
cc65 -Oirs produces next asm:

Code: Select all

	.importzp	_src
	.importzp	_dst
...
.proc	_str_cpy: near
.segment	"CODE"
L0004:	lda     _dst
	ldx     _dst+1
	sta     regsave
	stx     regsave+1
	clc
	adc     #$01
	bcc     L0008
	inx
L0008:	sta     _dst
	stx     _dst+1
	lda     regsave
	ldx     regsave+1
	jsr     pushax
	lda     _src
	ldx     _src+1
	sta     regsave
	stx     regsave+1
	clc
	adc     #$01
	bcc     L000A
	inx
L000A:	sta     _src
	stx     _src+1
	ldy     #$00
	lda     (regsave),y
	jsr     staspidx
	tax
	bne     L0004
	rts
.endproc
Well, it's far from next ideal (written by me):

Code: Select all

.proc str_cpy
	ldy # 0
loop:	lda (src),y
	sta (dst),y
	beq exit
	inc dst
	bne skip1
	inc dst+1
skip1:	inc src
	bne loop
	inc src+1
	jmp loop
exit:	rts
.endproc
Maybe not ten times, but 3-5 times seems real. Hm... Any ideas how to help compiler?
I rewrite C code to this:

Code: Select all

extern char *src, *dst;
#pragma zpsym( "src" )
#pragma zpsym( "dst" )

void str_cpy()
{
	while ( 1 )
	{
		*dst = *src;
		if ( !*dst )
			break;
		dst++;
		src++;
	}
}
trying to help compiler make inplace incrementing, but no way. Things seems to get worse.

User avatar
DRW
Posts: 1984
Joined: Sat Sep 07, 2013 2:59 pm

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by DRW » Mon May 04, 2020 2:41 am

Here are some hints:

Compile only with -O, not -Oirs. The code will be much shorter.

As you already recognized: Don't use local variables. Use global ones.

Yes, I admit, that's the big weakness of cc65: Accessing pointer values through an index (pointer[index]) or via dereferencing (*pointer).
Even if your own pointer is in zeropgage, cc65 will always copy it to its internal temp variable ptr1 before doing anything with it. And it will do it for every single expression. So, if you really must use pointers, you should write yourself a bunch of inline assembly macros for copying stuff from and to pointers.

The same problem doesn't exist for normal arrays, unless you try to copy from one array to another. (Because cc65 only uses the Y register as an index. And it doesn't realize when the index variable is the same for both arrays, so it does some pointer stuff again.) But stuff like array[index] = value is done pretty well.

Arrays indices can be as large as an unsigned integer allows it. So, the compiler always has to work with integer indices. Restrict the access to a byte index.

Never use variable++, only ++variable. array[index++] is much larger than array[index]; ++index.

Have a look at this code:

Code: Select all

char str1[100];
char str2[100];
unsigned char index;
char temp;

void Copy(void)
{
	index = 0;

	while (1)
	{
		temp = str2[index];
		str1[index] = temp;

		if (temp == '\0')
			break;

		++index;
	}	
}
This produces this:

Code: Select all

.segment	"CODE"

.proc	_Copy: near

.segment	"CODE"

	lda     #$00
	sta     _index
L0007:	ldy     _index
	lda     _str2,y
	sta     _temp
	ldy     _index
	lda     _temp
	sta     _str1,y
	lda     _temp
	beq     L0008
	inc     _index
	jmp     L0007
L0008:	rts

.endproc
Still not perfect, but far from 10 times worse.
My game "City Trouble": www.denny-r-walter.de/city.htm

Bananmos
Posts: 532
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by Bananmos » Mon May 04, 2020 3:16 am

I think that's the core issue I have with C on the 6502. The language is designed to assume a 16-bit CPU with local stack storage. To get anywhere near good performance, it appears you have to use generally poor practices (globals, excessive use of typecasts to avoid implicit int-conversion etc). And even then you'll end up in a situation where you'll be fighting the compiler to not produce its default output. The element of fun in retro homebrew just goes away for me when it seems you're fighting the tool so much to get the end result you want.

And it doesn't help that whereas other C ocmpilers like the PIC took liberties in diverging from the language spec, cc65 strived for compatibility with the spec at all cost, even going as far as to implement a software stack for automatic variables. At the expense of performance.

But if you see that challenge as part of the element of fun, then C on 6502 can be a viable option... just not for me.

There's a few quite interesting high-level languages purpose-built for the 6502 / other limited 8-bit CPUs. I really like that concept, but the only one I ever tried years ago (Atalan) was just too unstable to use... but I might try Millfork at some point, to see if the experience is less frustrating.

User avatar
aa-dav
Posts: 106
Joined: Tue Apr 14, 2020 9:45 pm
Location: Russia

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by aa-dav » Mon May 04, 2020 3:19 am

DRW wrote:
Mon May 04, 2020 2:41 am
...
Never use variable++, only ++variable.
...
Oh, thanks for hints, especially for this one. I forgot about post-increment complexity completely. This really helps compiler to do inplace pointer increments. I could not understand why so simple thing isn't implemented yet.
However, yes, pointer actions have a giant potential for improvements. Giant.

By the way couple of weeks ago I run this test (str_cpy from previous page) on official Zilog modern C compiler for descendant of Z80 - eZ80.
eZ80 is 8-bit processor core for modern Zilog microcontrollers. It's backward compatible with Z80, but adds new 24-bit functionality.
Yep, 24 bit. Every Z80 register pair has (invisible in asm syntax) upper extended byte in eZ80. So, instruction
ADD HL, BC
in LONG MODE of eZ80 adds 24 bit values. And
LD (HL), B
saves register B to 24-bit address.
So, let's go back to str_cpy, this is that I get on modern Z80 desendant in modern Zilog optimizing C compiler:

Code: Select all

 void StrCpy( char *dst, char *src )
 {
000250 DDE5 PUSH IX
000252 DD21000000 LD   IX,%0
000257 DD39 ADD  IX,SP
000259 C5  PUSH BC
   while ( *dst++ = *src++ );
00025A DD0706 LD   BC,(IX+%6)
00025D DD0FFD LD   (IX+%FD),BC
000260 DD3109 LD   IY,(IX+%9)
000263 DD2706 LD   HL,(IX+%6)
000266 FD7E00 LD   A,(IY+%0)
000269 77  LD   (HL),A
00026A DD0709 LD   BC,(IX+%9)
00026D 03  INC  BC
00026E DD0F09 LD   (IX+%9),BC
000271 DD0706 LD   BC,(IX+%6)
000274 03  INC  BC
000275 DD0F06 LD   (IX+%6),BC
000278 DD27FD LD   HL,(IX+%FD)
00027B 7E  LD   A,(HL)
00027C B7  OR   A,A
00027D 20DB JR   NZ,%25A
 }
00027F DDF9 LD   SP,IX
000281 DDE1 POP  IX
000283 C9  RET
Well, it doesn't call system procedures at least, but... it definitely doesn't optimized well enough for me.
So, I think it's common troubles for 8-bit compilers.

User avatar
DRW
Posts: 1984
Joined: Sat Sep 07, 2013 2:59 pm

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by DRW » Mon May 04, 2020 5:50 am

Bananmos wrote:
Mon May 04, 2020 3:16 am
To get anywhere near good performance, it appears you have to use generally poor practices (globals, excessive use of typecasts to avoid implicit int-conversion etc). And even then you'll end up in a situation where you'll be fighting the compiler to not produce its default output. The element of fun in retro homebrew just goes away for me when it seems you're fighting the tool so much to get the end result you want.
If you're able to write handwritten Assembly code fluently, then I would always suggest to do so. But for me, I know that I would have never even started writing a game if I had been forced to use Assembly only.

I still use pure Assembly in some general-purpose cases, like array copy functions, NMI and the rendering of the hardware sprites based on the game's meta sprites.
But for probably 95 % of the game, I have the comfort of writing C.

Also, in the end, it's not so bad. You don't really have to fight the compiler. Most of the stuff can still be done regularly like in typical C. And many other things still look close enough, so that the C code really doesn't look like an ugly abomination.


Local variables can be replaced with one of two things:

Either use static local variables if the RAM is sufficient for you. This way, you don't really have much difference from actual local variables, but internally it's a global one.

Or use a bunch of generic variables that are declared in some common code file and whose local names you define and undefine for your function:

Code: Select all

void __fastcall__ MyFunction(void)
{
#define i Byte1

    for (i = 0; i < 10; ++i)
        ...

#undef i
}

Type conversions are rarely needed. If your index variable is a byte (unsigned char), the compiler will know that it can copy it directly to a register.


Function parameters: That's what macros are for:

Code: Select all

#define MyFunction(parameter) \
{ \
    Byte1 = (parameter); \
    MyFunction_();
}

void __fastcall__ MyFunction_(void)
{
#define parameter Byte1
#define temp Byte2

    temp = parameter * 2 + 1;

    ...

#undef parameter
#undef temp
}

void __fastcall__ SomeOtherFunction(void)
{
    MyFunction(MainCharacter.Energy);
}

The only stuff that can get a bit bothersome is the handlung of arrays through pointers because of the constant copying into ptr1 and the Y register.
But again, macros can be used with relative comfort:

Code: Select all

#define AsmSetVarFromPtrAtIdxVar(variable, pointer, indexVariable) \
{ \
	__asm__("LDY %v", indexVariable); \
	__asm__("LDA (%v), %s", pointer, Y); \
	__asm__("STA %v", variable); \
}

-->

temp = ptr[i];
becomes
AsmSetVarFromPtrAtIdxVar(temp, ptr, i);
(Feel free to shorten the macro name to your liking.)

Not ideal. But if I have to choose between hand-written Assembly for the entirety of my code instead of just 5 % of my code, or between normal C code with crappy output. Or the workarounds above. Then I'll take those workarounds.


By the way:
Bananmos wrote:
Mon May 04, 2020 3:16 am
To get anywhere near good performance,
Even without the optimizations, it's not as bad. When I programmed "City Trouble" (link in my signature), I didn't know yet that pointer access is so expensive, so that game didn't even have those inline Assembly macros. And still, look at the game: It's a platformer that constantly has five entities on screen: The main character, the taser weapon, two regular opponents and one simple flying opponent. And this game has zero lags.
Last edited by DRW on Mon May 04, 2020 6:06 am, edited 1 time in total.
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
DRW
Posts: 1984
Joined: Sat Sep 07, 2013 2:59 pm

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by DRW » Mon May 04, 2020 5:51 am

My game "City Trouble": www.denny-r-walter.de/city.htm

Bananmos
Posts: 532
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by Bananmos » Fri May 08, 2020 5:04 pm

DRW wrote:
Mon May 04, 2020 5:51 am
And here are a few more hints:

https://shiru.untergrund.net/articles/p ... s_in_c.htm
As much as I respect Shiru's work (all done in C, I know) the "time saving" example looks like total nonsense to me. No one in their right mind would "hand-write" assembly code like that.
To begin with, trying to index a 32x32 map is a fundamentally wrong approach. To start off with, you wouldn't / shouldn't be storing a map without metatiling in the first place, but use metatiles to get things addressable on a byte size. The saying goes that premature optimisation is the root of all evil... but on the 6502, you need to optimise from the start to avoid 16-bit maths in the first place, and premature implementation is the bigger evil. Most of the time I do 6502 coding, I will often spend 90% of the time working on toolsets just to get data in the right format. It's a necessary evil in nesdev, and something I personally think you need to enjoy the process of to really enjoy the homebrew.

But *if* you are determined to use 32x32 uncompressed maps as your format, then you'd at least use a lookup table to just fetch two bytes from in ROM, into a zp pointer and omit the addition of my altogether. Sure, lookup tables wastes a lot of space. But if you're already wasting space by using a uncompressed map format, then there's little arguments to be made for trying to conserve size anyway.

But even ignoring that, why on earth would you make the final lookup, with a y register of zero and an additional addition of the "mx" variable? Just put the x variable into the Y register and let the 8-bit addressing feature of the CPU solve the problem for you.

It just looks to me like the most contrived example ever, designed to try to argue a point through a straw man argument...

I do however agree that assembly isn't best tool in all stages of development. This especially goes for gameplay logic which you need to tweak and refine a lot of times, such as player control and enemy AI. It's very important to get these things right, and a quicker test cycle helps immensely. But I'm not convinced that C is the best solution here. You might be far better off working on this tweaking in a dedicated modern game making tool / engine, and then bring that over to the NES once you're happy with how it works. There you have scripts which often reload and change object behavior instanteneously, giving you a time-saver people back in the 80s / 90s would be dying to have.

One thing that comes to my mind, is that too few of us - myself included - realise we've already got a pretty decent scripting language to do initial experimental nesdev in: Lua. It's generally used for adding hacks to exiting commercial games, but makes for a pretty good tool for prototyping / debugging your actual game as well.
Sure, Lua might have some awkward features, but is a decent scripting language that a lot of successful PC games have shipped with. If one is losing too much time trying to prototype stuff in asm, then I'd argue saving that time using the Lua scripting features in emulators such as FCEU or Mesen will get you there sooner than having to recompile C code and trying to debug all the low-level issues you still don't get rid of by switching from asm to C.

Lua scripts reload instantly in emulators, does bounds checking, is interpreted - so avoids the whole compilation step. So it allows you to prototype your code in stages: using simple floating point numbers to start with - not even needing to care about storage. Then add quantization to fixed-point numbers and store things to your actual NES RAM object data... and only once you're satisfied with the game dynamics start converting all those operations to their final assembly form. The best form of writing is re-writing...

It would also be cool to see a hardware implementation for Lua scripts. I think possibly an Everdrive N8 Pro might just be powerful enough to run a softcore capable of a Lua interpreter. But frame buffer access obviously wouldn't work - at least not without a lot more complicated hardware setup. And you'd suddenly have race conditions between the NES code and the Lua code that an emulator won't have. Still, it:)

Finally, I would also say that Movax's high-level macros for ca65 is my own personal favorite for when logic gets a bit too complicated to maintain in standard-assembly code. They've been around for long but are still great and not known to everyone:
http://mynesdev.blogspot.com/2012/10/ca ... again.html

I find that they give you just that one-step-up-from-pure-asm to be more productive when it comes to things like gameplay logic, while keeping you more connected and aware of what the final code output will be. The way they are an add-on to bring some structure to your asm code really appeals to me. But like I said, trying some 6502-dedicated language such as Millfork has long been on my todo-list... :)

User avatar
Controllerhead
Posts: 169
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by Controllerhead » Fri May 08, 2020 6:43 pm

Bananmos wrote:
Fri May 08, 2020 5:04 pm
One thing that comes to my mind, is that too few of us - myself included - realise we've already got a pretty decent scripting language to do initial experimental nesdev in: Lua.
Hold that thought. I am utilizing Lua in Mesen for Much More than that. Stay tuned!
Image

User avatar
aa-dav
Posts: 106
Joined: Tue Apr 14, 2020 9:45 pm
Location: Russia

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by aa-dav » Fri May 08, 2020 8:03 pm

I would add in support of assembler another thing.
IMHO, one of the main reasons it's hard to program in assembler is hardness of revisiting old code.
While you work on the current task it looks easy because you keep in mind that every instruction in series is supposed to do.
But after a while it's really hard to recall meaning of instructions. They all are the same.
And I think main tool to deal with it is comments and macros.
For example it's hard (for me) to read two lined idiom:

Code: Select all

lda some
sta somewhere
So, I just made macro and write

Code: Select all

store somewhere, some
And wow, many chunks of code shrink twice and become more clear.
Also I see many times here examples of code with things like "sta $2000". Why not "sta PPUCTRL"? And so on.
And comments, comments which really do explain why this instruction is here. Comments which you say in the head while write this instruction. Why not? You never ever have to reconstruct this thought later.
I saw very few examples of asembler code with enough comments (in my opinion). Very often it is just wall of raw instructions without any visible idea behind them.

User avatar
gauauu
Posts: 706
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by gauauu » Fri May 08, 2020 8:24 pm

aa-dav wrote:
Fri May 08, 2020 8:03 pm

While you work on the current task it looks easy because you keep in mind that every instruction in series is supposed to do.
But after a while it's really hard to recall meaning of instructions. They all are the same.
That's the main reason that I do use C in a lot of cases. (I tend to mix and match, using C in some places, asm in others). But the beauty of C is not in the speed of writing it (which isn't all THAT much faster once you have to think about to convince it to emit efficient code), but it's MUCH easier to read later when you've forgotten what you're doing.

bngrybt
Posts: 27
Joined: Tue May 09, 2017 5:03 am

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by bngrybt » Mon May 11, 2020 11:17 am

I wrote Mall Brawl primarily in C, and I consider it to be pretty comparable with other games of its genre from the retail period. I'm not really concerned with what's considered to be "good" or "bad" practice according to modern programming standards when working with the NES. I just go with whatever is optimal.

User avatar
rainwarrior
Posts: 7878
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by rainwarrior » Mon May 11, 2020 3:17 pm

aa-dav wrote:
Sun May 03, 2020 9:16 pm

Code: Select all

void str_cpy( char *dst, char *src )
{
	while ( *dst++ = *src++ );
}
Iterating many times over a tight loop, especially with pointers, is basically worst case for the CC65 compiler, and that's precisely the kind of thing you should rewrite in assembly if you need it. Unless you're being extremely ideological about never touching assembly, this really doesn't have to be a problem, though.

And of course you know this, but for this example there's already a standard library function that does exactly that. It's generally not a great idea to roll your own low level highly-iterative functions like that. Even making a wrapper instead of calling the library function directly would be a lot more efficient:

Code: Select all

#include <string.h>
void str_cpy( char *dst, char *src )
{
	return strcpy(dst, src);
}
Where C is more useful, and also less subject to inefficiency, is writing complex logic. A function like this one actually compiles extremely well, and is very easy to both write and read in C:

Code: Select all

void weather_fade()
{
	if (weather_wind_timeout == 0)
		weather_wind_random();
	else
		--weather_wind_timeout;
	
	// smoothly change wind
	if (weather_wind_dir != weather_wind_fade_dir)
	{
		if (weather_wind_p == 0) weather_wind_dir = weather_wind_fade_dir;
		else --weather_wind_p;
	}
	else
	{
		if      (weather_wind_p < weather_wind_fade_p) ++weather_wind_p;
		else if (weather_wind_p > weather_wind_fade_p) --weather_wind_p;
	}

	// smoothly change particles drop rate

	if (frame_count & 15) return;

	if (set_w[field_set])
	{
		if (weather_rate_min == 0)
		{
			weather_rate_min = 64;
			weather_fade_automask();
		}
		else if (weather_rate_min > 4)
		{
			--weather_rate_min;
			weather_fade_automask();
		}
	}
	else
	{
		if (weather_rate_min != 0)
		{
			++weather_rate_min;
			weather_fade_automask();
			if (weather_rate_min >= 65) weather_rate_min = 0;
		}
	}
}

User avatar
aa-dav
Posts: 106
Joined: Tue Apr 14, 2020 9:45 pm
Location: Russia

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by aa-dav » Tue May 12, 2020 4:58 am

rainwarrior wrote:
Mon May 11, 2020 3:17 pm
Iterating many times over a tight loop, especially with pointers, is basically worst case for the CC65 compiler, and that's precisely the kind of thing you should rewrite in assembly if you need it. Unless you're being extremely ideological about never touching assembly, this really doesn't have to be a problem, though.
I think the main reason C language won battle of programming languages is pointers.
If you don't use pointers you never was programming in C.
You never ever can be called C-programmer if you do not use pointers.

Pointers were way to really speed up programs in C in comparison to another languages.
Pointers made array addressing as simple as incrementing pointer. Removing multiplication in array access was a huge way of "real programming language".
In C++ pointers were updated to concept of iterators. And so on.
I think it's the main reason this language won the battle.

But MOS 6502 is the real unique thing because common techniques to speed up things in 8-bit world become grave slowness on this machine.

BUT!

It's amazing.
It's interesting how this machine is made.
My first computer of childhood was Z80-based.
And Z80 has big set of registers and the main problem with compilers is ignoring of register set.
C compilers emits code load/saving memory for just incrementing and so on.
But in the case of 6502 there is no complex register set and everything ends up in the memory cells.

I love this beast.

Bananmos
Posts: 532
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by Bananmos » Tue May 12, 2020 7:06 am

aa-dav wrote:
Tue May 12, 2020 4:58 am
BUT!

It's amazing.
It's interesting how this machine is made.
My first computer of childhood was Z80-based.
And Z80 has big set of registers and the main problem with compilers is ignoring of register set.
C compilers emits code load/saving memory for just incrementing and so on.
But in the case of 6502 there is no complex register set and everything ends up in the memory cells.

I love this beast.
It's very true that C and 6502 don't blend well. And IIRC, many features of the 65C816 were put in place to make the CPU architecture handle C code more gracefully.

But it is totally possible to make a C compiler that's efficient. The older PIC processors (which have an architecture even more limited than the 6502, with a single 8-bit accumulator and just a small hardware stack for subroutines) are primarily running C code. But from what I remember, the compiler Microchip provided did not make much effort to be compliant with any C standard.

In contrast, cc65's main goal appears to be high compatibility with existing C code. That's actually a noble effort - but a bit futile for the use case most of the folks on this forum have, where we are unlikely to re-use generic C code from other systems. The biggest performance impact of this tradeoff seems to be adding a software stack using (indirect),y in place of the stack pointer and just using TSX to get at automatic variables.

Even on the c64 (which I think cc65 originally targeted) with its whopping 64kB of RAM I would still question this trade-off... but on the NES which only comes with 2kB RAM as standard, it's just mad to have this overhead just because you might have a deeper depth than 256 bytes.
The largest stack deoth use I've ever used in asm was 240 entries for a floodfill-like - and even then I took the effort to re-write the JSR into a pha / pla logic, saving just the metatile X,Y position in a single byte.

But going even further, for 99.9% of all code targeting the NES you wouldn't use recursion anyway, but have all your functions use zeropage. The problem is you need to avoid collisions between different variables. But this is easily done by assuming no recursion / call-by-pointer and creating a "compiled stack" of zeropage variables. In fact, this is exactly what Microchip's PIC C compiler appears to do: https://stackoverflow.com/questions/579 ... iled-stack.

It's a shame this killer feature of automatic ZP temp storage isn't one of the bonuses you get from moving from asm to a higher-level language. Conceptually, I don't think it would be a huge effort to implement this in cc65, assuming you sacrifice recursion and call-by-pointer. The difficult thing is that with cc65's compile / link separation, you potentially would need to change that to an architecture that supports link-time-optimisation. But perhaps not... if the compiled objects just contains zeropage data that the linker can redirect at its leisure.

But I wouldn't be the one to do it, as I don't even enjoy coding in C all that much in the first place. I have enough C in my life as it is when coding to make a living... I consider NES coding to be an escape from the widespread beast of C, which - while being the most popular middle-ground solution for software - does not really excel in anything. (all in my subjective opinion of course... ;)

User avatar
gauauu
Posts: 706
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Aren't you afraid that NES Maker would just bring lazy noobs

Post by gauauu » Tue May 12, 2020 7:10 am

rainwarrior wrote:
Mon May 11, 2020 3:17 pm

Iterating many times over a tight loop, especially with pointers, is basically worst case for the CC65 compiler, and that's precisely the kind of thing you should rewrite in assembly if you need it.
.....

Where C is more useful, and also less subject to inefficiency, is writing complex logic.
Yes, 100% this. This is exactly my experience, both the tight loop and the big block of complex logic.

I've found that C can be great for writing a main character's logic, as that often has a lot of complexity of different moves/attacks/etc, and isn't as likely to iterate through a loop of pointers/arrays. C is a lot worse for iterating over your enemies or other entities, as it does a terrible job of preserving X or Y, so wastes a ton of time reloading them.

Post Reply