VBCC Optimizing C-compiler now supports NES

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

Moderator: Moderators

Post Reply
User avatar
Lazycow
Posts: 105
Joined: Tue Jun 11, 2013 1:04 pm
Contact:

VBCC Optimizing C-compiler now supports NES

Post by Lazycow » Wed Jun 24, 2020 11:35 am

I'm posting here on Volker Barthelmann's behalf, whos accound hasn't been approved, yet. (admin: hint, hint) :wink:
---8<---
Hello,

I have just released the second version of a port of the vbcc compiler to the 6502 at:
http://www.compilers.de/vbcc.html

Thanks to Matthias "Lazycow" Bock, the NES is now a supported target system, and this distribution also includes the lazyNES library with all crucial functionality to write NES games in C (see samples/lazynes).

It contains a C compiler, assembler, linker and a very rushed port of a C Library for the C64 and NES.

A few of the good things:

- compiler is under active development
- supports C99 (variable-length arrays, designated initializers etc.)
- generates optimized code (see dhrystones in sample directory)
- supports banked memory and far-pointers
- (limited) floating point support based on Steve Wozniaks code
- (pretty good) 32/64bit IEEE floating point support based on SANE
- support for writing interrupt handlers
- attributes for putting variables into zero page
- supports stack-frames > 256 bytes

More details in the included pdf.

Image

User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 501
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: VBCC Optimizing C-compiler now supports NES

Post by Jarhmander » Thu Jun 25, 2020 4:51 am

Wait, two C compilers released less than a week apart? What's this, the year of the C compilers? That being said, that's awesome.

I know some members here would like to see some code examples, like what the equivalent of a hand coded memcpy in C looks like in assembly once compiled.
((λ (x) (x x)) (λ (x) (x x)))

User avatar
Nikku4211
Posts: 67
Joined: Sun Dec 15, 2019 1:28 pm
Location: Bronx, New York
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by Nikku4211 » Sat Jun 27, 2020 10:57 am

Nice. Do you plan to support SNES? I know there's already PVSNESLib, but it's not really great to be honest...
I have an ASD, so empathy is not natural for me. If I hurt you, I apologise.

vbc
Posts: 44
Joined: Sun Jun 21, 2020 5:03 pm

Re: VBCC Optimizing C-compiler now supports NES

Post by vbc » Mon Jun 29, 2020 7:49 am

Jarhmander wrote:
Thu Jun 25, 2020 4:51 am
Wait, two C compilers released less than a week apart? What's this, the year of the C compilers? That being said, that's awesome.

I know some members here would like to see some code examples, like what the equivalent of a hand coded memcpy in C looks like in assembly once compiled.
My registration seems to work now.

Code: Select all

void memcpy(char *d,char *s,unsigned int n)
{
  while(n){*d++=*s++;n--;}
}
with vc -O3 compiles to:

Code: Select all

;vcprmin=10000                                                                  
        section text
        global  _memcpy
_memcpy:
        lda     r5
        bne     l22
        lda     r4
        beq     l9
l22:
l8:
        ldy     #0
        lda     (r2),y
        sta     (r0),y
        inc     r2
        bne     l23
        inc     r3
l23:
        inc     r0
        bne     l24
        inc     r1
l24:
        lda     r4
        bne     l25
        dec     r5
l25:
        dec     r4
        lda     r5
        bne     l8
        lda     r4
        bne     l8
l9:
        rts
cc65 -Oisr generates:

Code: Select all

.proc	_memcpy: near

.segment	"CODE"

	jsr     pushax
	jmp     L0004
L0002:	ldy     #$05
	lda     (sp),y
	tax
	dey
	lda     (sp),y
	sta     regsave
	stx     regsave+1
	clc
	adc     #$01
	bcc     L0007
	inx
L0007:	jsr     staxysp
	lda     regsave
	ldx     regsave+1
	jsr     pushax
	ldy     #$05
	lda     (sp),y
	tax
	dey
	lda     (sp),y
	sta     regsave
	stx     regsave+1
	clc
	adc     #$01
	bcc     L0009
	inx
L0009:	jsr     staxysp
	ldy     #$00
	lda     (regsave),y
	jsr     staspidx
	ldy     #$01
	lda     (sp),y
	tax
	dey
	lda     (sp),y
	sec
	sbc     #$01
	bcs     L000B
	dex
L000B:	jsr     stax0sp
L0004:	ldy     #$01
	lda     (sp),y
	dey
	ora     (sp),y
	bne     L0002
	jmp     incsp6

.endproc

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

Re: VBCC Optimizing C-compiler now supports NES

Post by aa-dav » Mon Jun 29, 2020 9:44 pm

vbc wrote:
Mon Jun 29, 2020 7:49 am
...
Cool! Could you test this example please:

Code: Select all

void str_cpy(char *d,char *s)
{
  while ( *d++ = *s++ ) {};
}
Looks like your compiler analizes call tree and places parameters in global variables if possible. Is it true? But does it support recursion? If yes, what code will be generated if parameters will be in stack?

vbc
Posts: 44
Joined: Sun Jun 21, 2020 5:03 pm

Re: VBCC Optimizing C-compiler now supports NES

Post by vbc » Tue Jun 30, 2020 3:43 am

aa-dav wrote:
Mon Jun 29, 2020 9:44 pm
vbc wrote:
Mon Jun 29, 2020 7:49 am
...
Cool! Could you test this example please:

Code: Select all

void str_cpy(char *d,char *s)
{
  while ( *d++ = *s++ ) {};
}
vbcc does not like the post-increment in the loop condition too much and makes a copy of the pointers:

Code: Select all

;vcprmin=10000
        section text
        global  _str_cpy
_str_cpy:
        lda     r3
        sta     r7
        lda     r2
        sta     r6
        inc     r2
        bne     l20
        inc     r3
l20:
        lda     r1
        sta     r5
        lda     r0
        sta     r4
        inc     r0
        bne     l21
        inc     r1
l21:
        ldy     #0
        lda     (r6),y
        sta     (r4),y
        cmp     #0
        beq     l7
l6:
        lda     r3
        sta     r7
        lda     r2
        sta     r6
        inc     r2
        bne     l22
        inc     r3
l22:
        lda     r1
        sta     r5
        lda     r0
        sta     r4
        inc     r0
        bne     l23
        inc     r1
l23:
        ldy     #0
        lda     (r6),y
        sta     (r4),y
        cmp     #0
        bne     l6
l7:
        rts
Much better code gets generated when slightly rewriting the function:

Code: Select all

void str_cpy(char *s1,char *s2)
{
    char c;
    do{
        c=*s2++;
        *s1++=c;
    }while(c);
}
compiles to:

Code: Select all

;vcprmin=10000
        section text
        global  _str_cpy
_str_cpy:
l7:
        ldy     #0
        lda     (r2),y
        inc     r2
        bne     l15
        inc     r3
l15:
        sta     (r0),y
        inc     r0
        bne     l16
        inc     r1
l16:
        cmp     #0
        bne     l7
        rts
cc65 -Oisr generates:

Code: Select all

.proc   _str_cpy: near

.segment        "CODE"

        jsr     pushax
        jsr     decsp1
L0002:  ldy     #$02
        lda     (sp),y
        tax
        dey
        lda     (sp),y
        sta     regsave
        stx     regsave+1
        clc
        adc     #$01
        bcc     L0007
        inx
L0007:  jsr     staxysp
        ldy     #$00
        lda     (regsave),y
        sta     (sp),y
        ldy     #$04
        lda     (sp),y
        tax
        dey
        lda     (sp),y
        sta     regsave
        stx     regsave+1
        clc
        adc     #$01
        bcc     L0009
        inx
L0009:  jsr     staxysp
        ldy     #$00
        lda     (sp),y
        sta     (regsave),y
        lda     (sp),y
        bne     L0002
        jmp     incsp5

.endproc
Looks like your compiler analizes call tree and places parameters in global variables if possible. Is it true?
It uses zero page locations as registers and passes parameters in a number of registers. But the calling convention is using a fixed ABI (unless the function call is inlined in which case the parameters are used directly).
But does it support recursion?
Yes, for example samples/calc.c uses recursion.
If yes, what code will be generated if parameters will be in stack?
Adding some dummy parameters to the memcpy example will force the parameters onto the stack:

Code: Select all

void memcpy_s(int d1,int d2,int d3,int d4,char *d,char *s,unsigned int n)
{
  while(n){*d++=*s++;n--;}
}
The generated code now has to pull the arguments from the software stack into registers:

Code: Select all

;vcprmin=10000
        section text
        global  _memcpy_s
_memcpy_s:
        ldy     #5
        lda     (sp),y
        sta     r5
        dey
        lda     (sp),y
        sta     r4
        dey
        lda     (sp),y
        sta     r3
        dey
        lda     (sp),y
        sta     r2
        dey
        lda     (sp),y
        sta     r1
        dey
        lda     (sp),y
        sta     r0
        lda     r5
        bne     l22
        lda     r4
        beq     l9
l22:
l8:
        ldy     #0
        lda     (r2),y
        sta     (r0),y
        inc     r2
        bne     l23
        inc     r3
l23:
        inc     r0
        bne     l24
        inc     r1
l24:
        lda     r4
        bne     l25
        dec     r5
l25:
        dec     r4
        lda     r5
        bne     l8
        lda     r4
        bne     l8
l9:
        rts[/quote]

vbc
Posts: 44
Joined: Sun Jun 21, 2020 5:03 pm

Re: VBCC Optimizing C-compiler now supports NES

Post by vbc » Tue Jun 30, 2020 3:53 am

Nikku4211 wrote:
Sat Jun 27, 2020 10:57 am
Nice. Do you plan to support SNES? I know there's already PVSNESLib, but it's not really great to be honest...
If there is some demand I might add support for the 65816, but somebody would have to help with target integration and provide SNES-specific support libraries.

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

Re: VBCC Optimizing C-compiler now supports NES

Post by aa-dav » Tue Jun 30, 2020 5:56 am

vbc wrote:
Tue Jun 30, 2020 3:53 am
...
I see. Very good results! Especially if stack is not needed.
Stack variables and parameters are bad thing for 8-bit processors, so I whould recommend to optimize leaf-node functions to do not use stack at all.
Mentioned earlier KickC-compiler (http://forums.nesdev.com/viewtopic.php?f=2&t=20187) brokes compatibility with C by removing of recursion to do not have to deal with software stack. And this is interesting move because manual assembler programming for this machine in fact uses same approach to increase speed and decrease size.
CC65 sometimes generates very long and slow code for compatibility reasons and there are strong recommendations to avoid stack variables.
However leaf-node functions and functions calling only leaf-node functions can be organized in optimal way (not using stack as all) if recursion is not needed and call-stack-tree allows it.
It's really interesting task to speed up things on this machines.
Good luck with your work!

User avatar
Nikku4211
Posts: 67
Joined: Sun Dec 15, 2019 1:28 pm
Location: Bronx, New York
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by Nikku4211 » Wed Jul 01, 2020 11:10 am

vbc wrote:
Tue Jun 30, 2020 3:53 am
If there is some demand I might add support for the 65816, but somebody would have to help with target integration and provide SNES-specific support libraries.
SNES-specific support libraries? Like, ASM libraries?

I know a pretty good SNES-specific library... Behold, Optiroc's LibSFX.
I have an ASD, so empathy is not natural for me. If I hurt you, I apologise.

vbc
Posts: 44
Joined: Sun Jun 21, 2020 5:03 pm

Re: VBCC Optimizing C-compiler now supports NES

Post by vbc » Thu Jul 02, 2020 4:57 am

Nikku4211 wrote:
Wed Jul 01, 2020 11:10 am
vbc wrote:
Tue Jun 30, 2020 3:53 am
If there is some demand I might add support for the 65816, but somebody would have to help with target integration and provide SNES-specific support libraries.
SNES-specific support libraries? Like, ASM libraries?

I know a pretty good SNES-specific library... Behold, Optiroc's LibSFX.
To comfortably support a target, you usually need a linker-script (or possibly several for different cartridge variants), startup-code, and perhaps some system-specific library adaptations (time, I/O, banking support). Additional libraries to access graphics, sound etc. (like lazynes) would be a nice bonus.

I have never used a SNES, so chances for SNES support are best if somebody with good SNES knowledge agrees to contribute in doing such a port. This is pretty much how the NES support was done. Matthias took care of all the nitty gritty NES details, so that I could concentrate on the compiler and in the end we could put it all together.

As I understand it, the SNES uses a 65816. I assume it is usually programmed in 16bit mode or are people also using the 6502 compatbility mode?

tepples
Posts: 22019
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by tepples » Thu Jul 02, 2020 2:32 pm

The 65816 is usually programmed in native mode, switching the widths of A and XY between 8 and 16 bits as needed.

User avatar
Lazycow
Posts: 105
Joined: Tue Jun 11, 2013 1:04 pm
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by Lazycow » Fri Jul 03, 2020 2:28 pm

Hi, I have ported the lazyNES lib from the vbcc6502 examples to the cc65. This means that I can compile the bubbles demo with cc65 now. This demo counts how many bubble sprites could be moved in 60 fps. (so it could be used as a lazy compiler benchmark)
With the cc65, I compiled it with: -Oris -Cl and the demo displays 14 bubbles!
With the vbcc6502 I compiled it with -O. For some reason, -O is faster than -O3, so it seems like vbcc6502's optimizer could need some finetuning. Nevertheless, compiled with vbcc6502, the demo displays whopping 38 bubbles. That's a factor of 2.7 (!!!)
Ok, let's be nice to the cc65 and declare the struct pointer as register variable, then the demo displays 15 bubbles. That's a (probably more realistic) factor of 2.5 (!)

Image Image
(bubbles demo from vbcc6502 examples, left: compiled with cc65, right: compiled with vbcc6502)

User avatar
Banshaku
Posts: 2378
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by Banshaku » Fri Jul 03, 2020 10:56 pm

@lazycow,

It's interesting to see a new C compiler that can be used for the nes but how much stable it is compared to cc65 and how easy would it be to migrate to it?

Sometime cc65 has it own share of issues but you get used to it but migrating a new one, compared to just start from scratch, is sometime not an easy task with the asm code in the back so it would be good to know what is the current status for nes, is porting the code is possible or for now it should be use for experimental only.

User avatar
Lazycow
Posts: 105
Joined: Tue Jun 11, 2013 1:04 pm
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by Lazycow » Sat Jul 04, 2020 1:27 pm

@Banshaku: For me, vbcc6502 is stable and migrating C code itself didn't make any problems at all. If you don't want to use the lazyNES lib, then adapting your assembler code will require some attention. Yes, you're right.

- You have to switch to vasm. (correct me if I'm wrong) At least, I used vasm. The assembler code itself is the same of course, but the vasm directives are different. Copy & paste, copy & paste...

- C interfaces might have to be modified. vbcc6502 supports register A and A/X for 1 parameter functions like cc65, but stack parameters are not compatible.

Yes, that's a bit of work. But hey, 38 bubbles! 8-)

User avatar
Banshaku
Posts: 2378
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: VBCC Optimizing C-compiler now supports NES

Post by Banshaku » Sat Jul 04, 2020 9:00 pm

I see. I'm using my own code so it would be a bad idea to try to adapt to a new library. My current backend is quite ca65 specific (.proc, how it is defined in segments etc) so if everything needs to be changed, then that's quite a big undertaking. I'm not sure I'm ready for that yet.

Maybe for testing new projects it could be a good idea but the backend code would still need to be ported. Right now what I'm doing is testing one project and porting all C code to asm so that I have 2 code base in case the C one becomes too heavy but if possible, I want to keep testing a mix of C/ASM and see how far I can go. On the genesis/md you can write all in C but I gave up about that for nes and adapt the C to make it faster (almost no parameters for functions and use variables in zero page for them instead).

Post Reply