Development Languages

Discussion of development of software for any "obsolete" computer or video game system. See the WSdev wiki and ObscureDev wiki for more information on certain platforms.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: Development Languages

Post by Garth »

Bregalad wrote:
Is it only concern about speed that prevented people from using higher-level languages? or the lack of tools, perhaps? I remember seeing some game for the NES here was made with cc65's C compiler, and it didn't (seem) to have speed issues.
Are you kidding ? I tried to make a program that basically writes text on the screen, and even that was too much to handle, it lagged.
The 6502's architecture does not seem to lend itself well to C. Fortunately, the same traits that cause this also make the '02 easy to envision solutions in assembly language. Here's an example of compiled C code, an empty count-to-100 loop, the line

Code: Select all

for (i = 0; i < 100; i++);
with i defined as a char (1 byte). The equivalent BASIC line would be

Code: Select all

FOR I = 0 to 99 : NEXT I
The popular CC65 C compiler, which admittedly is not optimizing, produces the following 6502 code from it. The comments were added by hand, not by the compiler.

Code: Select all

                                jsr     decsp1    ; make 1 byte space on the stack

000003r 1  A2 00                ldx     #$00
000005r 1  A9 00                lda     #$00
000007r 1  A0 00                ldy     #$00
000009r 1  91 rr                sta     (sp),y    ; initialize i to 0
00000Br 1  A0 00        L0003:  ldy     #$00
00000Dr 1  A2 00                ldx     #$00
00000Fr 1  B1 rr                lda     (sp),y
000011r 1  C9 64                cmp     #$64      ; cmp i to 100    
000013r 1  20 rr rr             jsr     boolult   ; do a less than comparison
000016r 1  F0 03 4C rr          jne     L0005     ; if less than
00001Ar 1  rr
00001Br 1  4C rr rr             jmp     L0004     ; if equal to 100
00001Er 1  A0 00        L0005:  ldy     #$00
000020r 1  A2 00                ldx     #$00
000022r 1  B1 rr                lda     (sp),y    ; get i again
000024r 1  48                   pha
000025r 1  18                   clc
000026r 1  69 01                adc     #$01      ; increment i     
000028r 1  A0 00                ldy     #$00
00002Ar 1  91 rr                sta     (sp),y    ; store i
00002Cr 1  68                   pla                                        
00002Dr 1  4C rr rr             jmp     L0003
000030r 1  20 rr rr     L0004:  jsr     incsp1    ; restore stack
all for what can be done in assembly language with only:

Code: Select all

        LDX  #0
L0003:  INX
        CPX  #$64
        BNE  L0003
which, besides being less than one-seventh as many bytes, has no subroutine calls to eat up even more time like the compiled C version did.

Since the index value is not used inside the loop, we could further shorten the code by starting at 100 and counting down, to zero, to get the same number of loop iterations. We'll use DEX, which like many other instructions, has an automatic, implied, compare-to-zero instruction built in, so we can omit the CPX #0:

Code: Select all

        LDX  #$64
L0003:  DEX
        BNE  L0003
Clearly the assembly-language version will absolutely fly compared to the compiled C-language version.

I'm sure a much better C compiler could be written for the '02. Perhaps there is one and I'm just not aware of it. The above is from the middle of my web page, "Assembly Language: Still Relevant Today." An experienced programmer came across it last week and emailed me saying he was working on a 68020 around 1990 and they were looking for the C compiler for it that put out the fastest-performing code. One thing they tried was an empty count-to-10,000 loop, and timed it. Zero. What? So they tried 10,000,000 instead. Still zero. It turned out that the compiler was smart enough to see that the loop did nothing, so it compiled no code for it. Unless the desired delay is super short, a loop is usually a very poor way to get it; but if a programmer knew what the compiler should put out, he might have a very good reason for the empty "for" loop, and wouldn't want the compiler to delete it.

I remember reading, back then, about compilers trying to optimize things to make their compiler look better than the competition's. They watched for various things you might want to do, particularly well known benchmarks and tests, and replaced them with the hand-optimized version strenuously written by the few programmers who knew the processor a lot better than the customer would. My reader commented that that was "a bit naughty."
http://WilsonMinesCo.com/ lots of 6502 resources
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Development Languages

Post by rainwarrior »

Garth wrote:The popular CC65 C compiler, which admittedly is not optimizing, produces the following 6502 code from it. The comments were added by hand, not by the compiler.
If you add -O to the command line it will do a bit better job than that.

You can to still better with a few very minor changes to your code:
  • use an unsigned char instead of an int (always a good idea to use 8-bit unsigned when you can)
  • use a static variable instead of a stack based variable (cc65's simulated stack is slow)
  • use ++i instead of i++ (i++ has a delayed side effect that carries a slight penalty)
  • use -O flag in command line (yes it actually has an optimization flag, and it helps)
The result:

Code: Select all

// static unsigned char i;
// for (i=0; i<100; ++i);

	lda     #$00
	sta     L30D1
L3767:	lda     L30D1
	cmp     #$64
	bcs     L30D3
	inc     L30D1
	jmp     L3767
L30D3:	rts
So, yes your naive C code compiled to something pretty horrible, but with a few hints to the compiler it gets a lot better. It's never going to get as good as assembly, but it's usually not that hard to get it to behave a lot better than what you were looking at.
calima
Posts: 1745
Joined: Tue Oct 06, 2015 10:16 am

Re: Development Languages

Post by calima »

Garth, you necro 5-year old topics regularly...
User avatar
gauauu
Posts: 779
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Development Languages

Post by gauauu »

What I keep wondering is why does cc65 produce such inefficient code without the O flag. Is there any reason whatsoever that somebody might want to compile without that flag with cc65?

(It's not like some compilers where optimizing produces weird hard to understand assembly... It seems the opposite here, the unoptimized is weird and hard to make sense of, and the optimized produces normal looking code)
adam_smasher
Posts: 271
Joined: Sun Mar 27, 2011 10:49 am
Location: Victoria, BC

Re: Development Languages

Post by adam_smasher »

calima wrote:Garth, you necro 5-year old topics regularly...
Nothing wrong with that IMO, so long as he's adding useful things to the discussion. Anyway, we necro 30+ year old consoles :beer:
gauauu wrote:What I keep wondering is why does cc65 produce such inefficient code without the O flag. Is there any reason whatsoever that somebody might want to compile without that flag with cc65?

(It's not like some compilers where optimizing produces weird hard to understand assembly... It seems the opposite here, the unoptimized is weird and hard to make sense of, and the optimized produces normal looking code)
From the perspective of the compiler writer, I imagine the transformation without "-O" is more straightforward and thus less likely to be buggy. If there's an issue with some generated code, it's helpful to know whether the problem is in your optimizer or somewhere earlier in the compilation chain.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Development Languages

Post by rainwarrior »

Yeah, I think that's it. With CC65 there's no reason not to use -O, really, unless you're trying to debug the compiler.
Post Reply