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

rNES - a NES library for cc65 - version 0.1 released

rNES - a NES library for cc65 - version 0.1 released
by on (#42668)
Well, here's the quick and dirty first release of rNES, a library for NES apps and games development, written in C for cc65.

Hope some newbie can benefit from this

Contributions, criticism and ideas are welcome!

http://tachdaun.com.ar/rnes/rNES-0.1.zip

Image

Image

by on (#42673)
It's good to see some effort done for the C version of CC65. It may help some developers that doesn't have any knowledge about assembler to be able to do some tinkering on the nes.

Good work!

by on (#42686)
Looks promising. However you should learn some assembly. Also I'd have to go to GBA dev seeing how they do games in C, and maybe try to do similar libraries for the NES, so that they are suitable for game use.

by on (#42692)
- My emu displays wrong colors:
Image

- Any help?

by on (#42693)
Fx3 wrote:
- My emu displays wrong colors:
[img]
- Any help?


:(
Letters' colors are right. The earth seems to have a problem.
What emulator and version is it?


Fx3 wrote:
Image


OH! now that I see your screenshot, your emulator shows the first and last rows of tiles...
And I can see now what I supposed, as I zero-out page 2 ( $0200 ) to write it to OAM, then all sprites use tile 0, which is one pixel, used for sprite 0 hit, and all are at 0,1.

I should make them all off-screen.

Thanks for the double feedback :D

by on (#42696)
Banshaku wrote:
It's good to see some effort done for the C version of CC65...Good work!

Thanks!

Bregalad wrote:
Looks promising. However you should learn some assembly. Also I'd have to go to GBA dev seeing how they do games in C, and maybe try to do similar libraries for the NES, so that they are suitable for game use.

Thanks! Good advice, I'll give it a look

by on (#42712)
Petruza wrote:
What emulator and version is it?

He is the author of RockNES, so he probably meant the lastest version.

Quote:
OH! now that I see your screenshot, your emulator shows the first and last rows of tiles...

You should configure your emulator like this when developing too. Not being able to see many scanlines can easily make you miss errors, like in this case.

Quote:
I should make them all off-screen.

Yeah, that's the correct way to hide unused sprites. For better performance, you don't even have to clear the whole page, simply setting the Y coordinates to $F0 or more will hide them, you don't even have to touch the other bytes, so don't waste precious CPU time with that.

by on (#42715)
tokumaru wrote:
He is the author of RockNES, so he probably meant the lastest version.

Oh, that was he meant by "My" emu. =D

tokumaru wrote:
Yeah, that's the correct way to hide unused sprites. For better performance, you don't even have to clear the whole page, simply setting the Y coordinates to $F0 or more will hide them, you don't even have to touch the other bytes, so don't waste precious CPU time with that.

Very true.
it's worth a void rNES_offscreen_sprites( byte page ); which puts every 4th byte to $F0 in the given page

by on (#42721)
- Probably a bug in my emulator (RockNES), I still need to trace the code. Well, it works on Nintendulator and Nestopia, but not on FCEUX.

EDIT: hmm, it's buggy from beta 8, works fine on beta <= 7... Interesting.

by on (#42727)
Fx3 wrote:
- Probably a bug in my emulator (RockNES), I still need to trace the code. Well, it works on Nintendulator and Nestopia, but not on FCEUX.

Yes, it's strange, my progs run perfectly on FCEUXD but not at all on FCEUX.

Fx3 wrote:
EDIT: hmm, it's buggy from beta 8, works fine on beta <= 7... Interesting.

Well, it has to be something with the background pallete. And one pallete only, because the two other palletes are just like the other emus.

by on (#42742)
Some things that I noticed:

- rNES_get_joy: both controllers are strobed by $4016; $4017 controls the APU. Also, you could accept the joypad selector as an enumeration, for better type-safety. But it makes more sense to me to select it as an index, 0 or 1.

- rNES_waitvblank() should read rNES_PPU_status once before the loop, in case you're already in vblank. Without this clearing, if you were already in vblank right near the end, rNES_waitvblank() wouldn't delay and your code might run outside it unexpectedly. Also, there's no reason to make this a macro, as efficiency isn't an issue for a routine that waits for something in a loop.

- The macro addr should cast its result to a volatile unsigned char*, to ensure that none of the accesses are optimized out.

- times2: does cc65 really not optimize n*2 to n+n, n*4 to n<<2, etc.?

- In C, "void rNES_init();" declares a function that can accept any arguments. Better to make it "void rNES_init( void );"

- Why do many of the files have lots of newlines at the end?

- For your bin macro, you could use this instead, which is easier to use and does checking:
Code:
/* Compile error if a has too many bits or any are not 0 or 1.
Always evaluates to 0. */
#define BIN8_VALID_(a)  (0 * sizeof (char [(01##a & 0111111111) == 01##a]))

#define BIN8_(n)        ((n       & 0x01) |\
                         (n >>  2 & 0x02) |\
                         (n >>  4 & 0x04) |\
                         (n >>  6 & 0x08) |\
                         (n >>  8 & 0x10) |\
                         (n >> 10 & 0x20) |\
                         (n >> 12 & 0x40) |\
                         (n >> 14 & 0x80))

#define BIN8(a)         (BIN8_( 0##a ) + BIN8_VALID_( a ))

Examples: BIN8(10000000) = 0x80. BIN8(100) = 7, BIN8(100000000) is error (too many bits), BIN8(200) is error (invalid bit).

by on (#42750)
blargg wrote:
Some things that I noticed:
- rNES_get_joy: both controllers are strobed by $4016; $4017 controls the APU. Also, you could accept the joypad selector as an enumeration, for better type-safety. But it makes more sense to me to select it as an index, 0 or 1.
ooops, never actually tested 2nd controller
Quote:
- rNES_waitvblank() should read rNES_PPU_status once before the loop, in case you're already in vblank. Without this clearing, if you were already in vblank right near the end, rNES_waitvblank() wouldn't delay and your code might run outside it unexpectedly. Also, there's no reason to make this a macro, as efficiency isn't an issue for a routine that waits for something in a loop.
Sorry, I don't get it. "near the end" of what?
Quote:
- times2: does cc65 really not optimize n*2 to n+n, n*4 to n<<2, etc.?
Don't have any idea. I'll check.
Quote:
- In C, "void rNES_init();" declares a function that can accept any arguments. Better to make it "void rNES_init( void );"
Never heard of that, do you mean the empty parenthesis mean the function can accept any number and type of arguments??
Quote:
- Why do many of the files have lots of newlines at the end?
:D I have a problem with coding at the bottom of the screen, so I put them to make the last written line in the middle of the screen. Anf forgot to take them out.
Quote:
- For your bin macro, you could use this instead, which is easier to use and does checking:
Code:
/* Compile error if a has too many bits or any are not 0 or 1.
Always evaluates to 0. */
#define BIN8_VALID_(a)  (0 * sizeof (char [(01##a & 0111111111) == 01##a]))

#define BIN8_(n)        ((n       & 0x01) |\
                         (n >>  2 & 0x02) |\
                         (n >>  4 & 0x04) |\
                         (n >>  6 & 0x08) |\
                         (n >>  8 & 0x10) |\
                         (n >> 10 & 0x20) |\
                         (n >> 12 & 0x40) |\
                         (n >> 14 & 0x80))

#define BIN8(a)         (BIN8_( 0##a ) + BIN8_VALID_( a ))

Examples: BIN8(10000000) = 0x80. BIN8(100) = 7, BIN8(100000000) is error (too many bits), BIN8(200) is error (invalid bit).

Wow that was twisted.

Thanks for all the contributions, I'll stack'em in my to do bucket. :P

by on (#42754)
- I still don't know what's broken here. From what blargg said, probably nothing :), but I can confirm it's not the palette (2007 r/w), since both dumps (of 3F00-1F) have identical data. It's quite difficult to "undo" changes.

- Since I have no other game/demo with this problem, I suspect it's not with RockNES after all...

by on (#42756)
blargg wrote:
- In C, "void rNES_init();" declares a function that can accept any arguments. Better to make it "void rNES_init( void );"


Is this a C89 thing? I always thought empty parenthesis meant no aruments, and an elipsis was any number of arguments. But maybe that was just C++ (and C99?).

I suppose putting (void) couldn't hurt. Better safe than sorry.

by on (#42763)
Disch wrote:
I always thought empty parenthesis meant no aruments, and an elipsis was any number of arguments.

Me too.

by on (#42772)
The "void func();" being equivalent to "void func( ... );" is a C89/C99 thing, but not a C++ thing (there, () always means ( void )). See section 3.5.4.3 of the C89 standard:
C99 standard wrote:
An empty list in a function declarator that is part of a function definition specifies that the function has no parameters. The empty list in a function declarator that is not part of a function definition specifies that no information about the number or types of the parameters is supplied.

That is, a function definition with () specifies no parameters, but () in a declaration gives no information about them.

Petruza wrote:
blargg wrote:
- rNES_waitvblank() should read rNES_PPU_status once before the loop, in case you're already in vblank. Without this clearing, if you were already in vblank right near the end, rNES_waitvblank() wouldn't delay and your code might run outside it unexpectedly.

Sorry, I don't get it. "near the end" of what?

Near the end of vblank. Let's say the entire frame takes 100 units of time, including vblank. That means that vblank takes about 8 units of time. If someone calls rNES_waitvblank(), he expects to have about 8 units of time to do PPU operations. If you don't clear the flag before waiting in the loop, and rNES_waitvblank() is called in near the end of vblank, it will return immediately, and the caller will have far fewer than 8 units of time; worst-case, he could have no time, with vblank ending occurring just as rNES_waitvblank() finishes.
Quote:
Wow that [binary macro] was twisted.

Really? All it does is convert the input to octal by prefixing it with 0 (the 0##a part), then shifts each bit into the proper position and mask it. The valid part is a bit more subtle.

by on (#42779)
Any idea what's causing the Earth colors glitch? :(

by on (#42781)
Fx3 wrote:
Any idea what's causing the Earth colors glitch? :(

Nope. Works fine in FCEXUD, Jnes and a portable media player.

by on (#42782)
Petruza wrote:
Works fine in FCEXUD, Jnes and a portable media player.

What about actual accurate emulators such as Nestopia and Nintendulator? Nobody should ever release a NES program without trying those out.

by on (#42785)
tokumaru wrote:
Petruza wrote:
Works fine in FCEXUD, Jnes and a portable media player.

What about actual accurate emulators such as Nestopia and Nintendulator? Nobody should ever release a NES program without trying those out.


It works on both emus. The Earth uses attribute color value 0, but it isn't occuring here. Looks like an obscure thing... :(

by on (#42786)
Fx3 wrote:
It works on both emus. The Earth uses attribute color value 0, but it isn't occuring here. Looks like an obscure thing... :(


I tried it in RockNES X 2.0 and colors show ok. Although it's a mod of your emu, I think emulation shouldn't be different to the DOS version, right?

Strange...

by on (#42789)
Nope, all the current releases are for Windows. :)

by on (#42804)
Can we get back to the topic please, that of a NES library in C? If a particular emulator has bugs, start a separate thread.

by on (#42805)
Blargg, about your suggestion for rNES_waitvblank(), this function does what its name says, if you are actually at vblank, and you want to "wait for vblank", well, the result is you wait nothing.

Anyway, I think the programmer won't be just waiting for vblank, usually what he'll do is a loop that does anything else while waiting for vblank, as decompressing some data or calculating something, right?

For those people who actually made or are making some game for NES, do you usually wait for vblank doing nothing, or use that time to do something else?

by on (#42807)
I agree with Blargg. waitvblank() should wait for the start of vblank. If you're already in vblank it should wait until the start of the next vblank, rather than return immediately.

The programmer will wait for VBlank for two reasons:

1) It regulates the logic flow of the game. Since VBlank comes 60 times a second, you do one logic step -- then wait for another frame (vblank), then do another logic step, then wait for a frame, etc. This produces a steady gameflow.

One problem with waitvblank returning immediately is -- what if the game doesn't have very much logic to do? Say for example the game is waiting for user input before continuing. Like at a title screen or something where it's waiting for the Start button to be pressed. And for kicks, say there's a little sprite that animates on the screen.

Code might look something like:

Code:
while(!start_button_pressed)
{
  rNES_waitvblank();
  animate_and_draw_sprite();
  update_joy();
}


This is very practical (and typical in a game). The game would expect animate_and_draw_sprite to be called once per frame, which would result in a steady animation framerate of 60 FPS. If it's called more often than that, the animation would be faster than expected.

So what happens if animate_and_draw_sprite() and update_joy() are shorter than the VBlank period? This would result in rNES_waitvblank returning immediately rather than waiting for the next frame, which would disrupt the 60 FPS animation speed.


2) The other reason to wait for VBlank is to wait for a time where you can draw stuff to the screen. This is the reason blargg was talking about.

Once VBlank happens, you know you have X amount of time to get drawing done.

Code:
rNES_waitvblank();
DoABunchOfDrawing();


DoABunchOfDrawing might assume that it has X time to get all its drawing done -- however if rNES_waitvblank returns immediately (in the middle of or near the end of vblank), you'll be hosed... since you don't have the expected X time to finish drawing -- you have much less. As a result, drawing code might spill outside of vblank, resulting in all kinds of horrible graphic glitches.

Quote:
For those people who actually made or are making some game for NES, do you usually wait for vblank doing nothing, or use that time to do something else?


The whole point of calling a wait() funciton is that you have nothing else to do. You can't do something else while waiting.. or else you're not waiting.

That said -- there are times you might want NMI/Vblank to interrupt code that's running. For example, if an NMI occurs while you're updating game logic, you'll know your code is running long and you have to do some kind of slowdown to keep up.

Therefore games might be interested in VBlank notification outside of waiting for vblank. However they would not use a wait() function for this.

by on (#42829)
Disch wrote:
I agree with Blargg. waitvblank() should wait for the start of vblank[...]
So what happens if animate_and_draw_sprite() and update_joy() are shorter than the VBlank period? This would result in rNES_waitvblank returning immediately rather than waiting for the next frame, which would disrupt the 60 FPS animation speed.

rNES_waitvblank() reads $2002 for bit 7, and that read clears the bit until the next vblank.
Suppose one loop's logic takes too long, so you call rNES_waitvblank() inside the vblank time, and the next loop takes less time, so the interval between the two calls to rNES_waitvblank() would be less than one 60th of a second, making the animation go faster than it should, but that is only for one frame.
If rNES_waitvblank() checked $2002 once before making the loop to wait for another vblank, and it was called inside the vblank period, then the program would be inactive for more than a 60th of a second, which will also make the animation out of sync.

Anyway, there's also this:
rNES_VBlank( PPU_status )
which checks for the vblank flag, but doesn't wait for it, so you can use it in your own loop.

by on (#42831)
Petruza wrote:
rNES_waitvblank() reads $2002 for bit 7


In that case, I would recommend removing the function completely, as this is a very unreliable way to wait for vblank and might be the source of many glitches and errors in games as a result of them using it.

The only time you need to (or should) examine $2002.7 in a game would be at startup when you're waiting for the PPU to warm up -- and a rNES_waitforwarmup() function or something of that sort seems more practical for this kind of thing.

by on (#42833)
The replacement I'd recommend is to increment a variable in the NMI. waitvblank waits for it to become non-zero, then resets it to zero again. That's the usual way of doing it.

by on (#42840)
Well, this is a library, so the programmer could choose to use its functions or code their own. For some reason, the NES, appart from firing the NMI each vblank, sets $2002.7, it's there for you to read it, if you want, so my library makes it available with a macro.

I might as well remove rNES_waitvblank() as it's one of the first things I coded and just does the same as waitvblank() from the cc65 NES library.

In the demo, what actually takes place is this:
Code:
      while( true )
      {
         PPU_status = rNES_PPU_status();
         
         if( rNES_sprite0hit( PPU_status ) )
            rNES_scroll(sx,0);
         
         if( rNES_VBlank( PPU_status ) )
            break;
      }

I could add a $2002 read before the loop as sugested.
But checking for sprite 0 hit or doing some other things before vblank, make rNES_waitvblank() pretty useless anyway.

I'm aware that the better way to do this is NMI, but I haven't yet figured out how to hook a routine to an interrupt in C.
If someone has any clue, please tell me. Anyway, using NMI would be something the programmer should take care about, the library couldn't handle it in any useful way I think.

by on (#42842)
- I'm sure you must make the things more friendly. Make PPUSTATUS a global variable, and with easy C, your code would become this:

Code:
while( true )
      {
         if( PPUSTATUS & 0x40 )
            rNES_scroll(sx,0);
         
         if( PPUSTATUS & 0x80 )
            break;
      }


...or:
Code:
while( 0 == (PPUSTATUS & 0x80) )
      {
         if( PPUSTATUS & 0x40 )
            rNES_scroll(sx,0);
      }


- What's the reason of creating a function to every single thing?

by on (#42846)
Well they're not functions, they're macro functions, so there's no actual function call if that's what you mean.
In fact, rNES_sprite0hit() is exactly what you proposed:
Code:
#define rNES_sprite0hit( PPU_status )      ( PPU_status & bin(0,1,0,0,0,0,0,0) )

I read the PPU status once and then check it twice for different flags, as reading it clears both flags, so if you do this:
Code:
if( PPUSTATUS & 0x40 )
            rNES_scroll(sx,0);

That read could just cleared the vblank bit also, and you miss it.

by on (#42854)
Petruza wrote:
Well, this is a library, so the programmer could choose to use its functions or code their own.


I guess what I'm getting at is your function would give the impression that it does a specific job -- however it does that job in a broken way and would thus be detrimental to anyone who uses it.

It's like saying "here, use this function", but the function is poison for the program and ultimately wrecks it. The user is better off not even being presented the option for it.

Quote:
I'm aware that the better way to do this is NMI, but I haven't yet figured out how to hook a routine to an interrupt in C.


I recall reading in the documentation many many moons ago that you had to have at least minimal 6502 to handle the interrupt -- it couldn't be done solely in C.

However I think with a lib, it could be done in C if you can provide a callback (in the form of a function pointer) which the library could call from its NMI handler. The library itself might have to have a touch of 6502 to make it work, but the end user won't need to.

Quote:
Anyway, using NMI would be something the programmer should take care about, the library couldn't handle it in any useful way I think.


I think there could be ways to make it work. If I were to design it, I'd probably do something like....

Code:
void rNES_setnmicallback( NMIProc callbackproc );

void rNES_enablenmi( int enable ); // 0 disables, 1 enables NMI


rNES_setnmicallback would need to be called once at program startup to set the function pointer to which the program would call on NMI. The NMI handler code in the lib could be something like

Code:
NMI:
  PHA
  TXA
  PHA
  TYA
  PHA

  JSR __do_whatever_to_call_callback_proc__

  PLA
  TAY
  PLA
  TAX
  PLA

  RTI


I think that a setup like this would clash with the idea of a waitvblank function that utilizes NMIs -- and therefore stick to my recommendation of omitting the waitvblank function from the API completely.


EDIT -- apparenly you would want to use char or unsigned char instead of int, though (after having read the documentation). I always thought 'int' was to use whatever data size was fastest on the platform -- strange.


EDIT2 -- Here's where I read that about interrupts... in the FAQ of all places.

http://www.cc65.org/faq.php#IntHandlers

by on (#42858)
Disch wrote:
However I think with a lib, it could be done in C if you can provide a callback (in the form of a function pointer) which the library could call from its NMI handler. The library itself might have to have a touch of 6502 to make it work, but the end user won't need to.

Thanks, I'll note this down and re-read it when I dive into 6502 assembler.
By now I'll spend time making a game before improving the library, from that, more improvements will surely come out as well.

Quote:
I think that a setup like this would clash with the idea of a waitvblank function that utilizes NMIs -- and therefore stick to my recommendation of omitting the waitvblank function from the API completely.

Yes, as I said in my previous post, it'll be out in the next version.

Quote:
EDIT -- apparenly you would want to use char or unsigned char instead of int, though (after having read the documentation). I always thought 'int' was to use whatever data size was fastest on the platform -- strange.

Well C's definition of as I recall it is that int should hold values at least between -32768 and 32767 and up to but not more than long int, whatever size the specific compiler assigns to long int.


edit: When using NMI routine, do you do some sort of signaling between the rendering routine and the program's logic?

by on (#42860)
Disch wrote:
EDIT2 -- Here's where I read that about interrupts... in the FAQ of all places.
http://www.cc65.org/faq.php#IntHandlers

Well, there. In other words, Interrupt handling in C is a pain in the ass.
I don't even know if all that values stacking and all won't render the interrupt handling unusable.

As for now, my library's users ( which would be like... about... just me ) will have to conform with reading $2002.

by on (#42861)
Petruza wrote:
As for now, my library's users ( which would be like... about... just me ) will have to conform with reading $2002.


Polling $2002 works for sprite #0 hit, but that's about it. I forget the exact details, but there are times where the flag will be reset faster than it can be polled. I remember one old demo I made with music in it, when I saw it running on a real NES for the first time, the music kept randomly speeding up because of it.

I'm guessing that's why NES games do the vblank wait loop twice, because it doesn't always work.

The most simple NMI routine I've ever used was just this:
Code:
NMI:
 inc nmi_count
 rti


And that works just as well as anything I normally do.

by on (#42863)
Memblers wrote:
The most simple NMI routine I've ever used was just this:
Code:
NMI:
 inc nmi_count
 rti


And that works just as well as anything I normally do.


And then you would loop in your code, after the program's logic, waiting for nmi_count to become non zero, then start writing to VRAM?


edit:
Now, looking the source for cc65's NES runtime, I figure out there's already an NMI routine:
Code:
nmi:    pha
        tya
        pha
        txa
        pha

        lda     #1
        sta     VBLANK_FLAG

        inc     tickcount
        bne     @s
        inc     tickcount+1

@s:     jsr     ppubuf_flush

        ; reset the video counter
        lda     #$20
        sta     PPU_VRAM_ADDR2
        lda     #$00
        sta     PPU_VRAM_ADDR2

        ; reset scrolling
        sta     PPU_VRAM_ADDR1
        sta     PPU_VRAM_ADDR1

        pla
        tax
        pla
        tay
        pla

; Interrupt exit

irq2:
irq1:
timerirq:
irq:
        rti

I think I may have to really learn asm sometime and possibly rewrite all this to fit gamedev's needs.
All this code seems to be written having in mind only writing characters to the screen, in fact it originally had all the background tile section occupied by the charset.


edit:
As I can see here, the zero page variable 'tickcount' is incremented each NMI, and I can use it for vblank, as Memblers suggested.
I know that this variable is at offset $6B so I can access it.

Do you say I use this method? waiting for $6B to become non zero?
Wouldn't that be the same as reading $2002.7 ?

by on (#42908)
Petruza wrote:
Memblers wrote:
The most simple NMI routine I've ever used was just this:
Code:
NMI:
 inc nmi_count
 rti


And that works just as well as anything I normally do.


And then you would loop in your code, after the program's logic, waiting for nmi_count to become non zero, then start writing to VRAM?


Yep, exactly.


Quote:
As I can see here, the zero page variable 'tickcount' is incremented each NMI, and I can use it for vblank, as Memblers suggested.
I know that this variable is at offset $6B so I can access it.

Do you say I use this method? waiting for $6B to become non zero?


That would work. It looks like VBLANK_FLAG might be usable for it also.
tickcount would also be where you'd get a delay, to wait a certain amount of frames.

Quote:
Wouldn't that be the same as reading $2002.7 ?

It might work the same on any emulators (I know of) right now, but definitely won't be the same on NES (and l'd bet most clone systems too).

by on (#42929)
I might as well replace all the NMI routine for the one that you suggested:
NMI:
inc nmi_count
rti

All the other code just does things for functions like cprintf() and such.

I yet have to figure out a way to access zp vars defined in an asm inc file, other than reading the memory address directly, which works, but it´s not very neat.

by on (#43077)
I just spent a while whittling the runtime library down to the minimum, and removing that awful NES shell they provide. But my conclusion is that the compiled code is way to inefficient for any useful NES development. Also, if one wants to code a NES-like platform in C, one could just use the guts of a NES emulator and compile as a regular PC executable. It could be made virtually identical to what you'd get with cc65, except you'd have unlimited time per frame, and it wouldn't run on a NES.

by on (#43148)
blargg wrote:
I just spent a while whittling the runtime library down to the minimum, and removing that awful NES shell they provide.

Can I have it? I was willing to do the same.

blargg wrote:
But my conclusion is that the compiled code is way to inefficient for any useful NES development.

I'll prove you wrong with a game. Gimme some time. :wink:
Besides, the metronome I programmed is very useful to me.

by on (#43162)
OK, I'm game. There is something oddly interesting about programming the NES in C.

by on (#43163)
blargg wrote:
OK, I'm game. There is something oddly interesting about programming the NES in C.


</ironic> ? ;)

by on (#43167)
In fact, I only plan to code the game logic in C.
Things like NMI handling, VRAM writing and in general all access to registers I will write them in assembler.

by on (#43170)
Petruza wrote:
In fact, I only plan to code the game logic in C.
Things like NMI handling, VRAM writing and in general all access to registers I will write them in assembler.


Not a bad plan, actually. It's only bad if you have little time to handle lots of enemies. This is actually my case, so I'll have to optimize as much as possible.

by on (#43231)
Here's an archive with the improved nes driver and a simple demo:
cc65_nes.zip
This nes driver does minimal handling, unlike the standard one which tries to implement a text console. If you add CUSTOM_VECTORS=1 in your chars.s file before including ines.s, you can take over reset, nmi, and irq handling.

by on (#43260)
Thanks! it's a good source of inspiration
awesome! I use cc65
by on (#45180)
I mainly use ca65 (the assembler) because it has a lot of nice features and the linker is really powerful in the cc65 package. I haven't gotten far enough to do anything with the C compiler in it yet, and I did notice some NES package on the cc65 website, but it seemed large and undocumented.

I wasn't able to download your rNES link because the hostname doesn't resolve to anything. I suspect it's dynamic DNS and you're not online?
"Resolving petruza.com.ar... failed: Name or service not known."

I am a big fan of mixing C and assembly, because C is quite a bit easier for handling data structures and state machines than assembly.

We should all know at least enough assembly to debug our C code is my belief, but knowing more is not absolutely necessary, despite what some of the other people have been saying. We should choose our battles carefully, we can spend a lot of time trying to make a compact and optimal image, or we can be "sloppy" and use the time for things like testing, design and improving game play.
Re: awesome! I use cc65
by on (#45182)
Jon wrote:
I wasn't able to download your rNES link because the hostname doesn't resolve to anything. I suspect it's dynamic DNS and you're not online?
"Resolving petruza.com.ar... failed: Name or service not known."


Thanks for reminding me, recently my hosting provider realized I was hosting many sites in one account and made me delete all the dns redirections, so now it's accesible at:
http://tachdaun.com.ar/rnes/

I'll update the link in the first post.

And thanks for the interest, I'm rewriting the library in ca65 assembler so if you want to help, you're welcome!

by on (#45184)
the folks at playpower should check this out http://groups.google.com/group/playpowertech

by on (#45194)
frantik wrote:
the folks at playpower should check this out http://groups.google.com/group/playpowertech


I never heard of this before you mentioned it, but it sounds like a noble project! I'm just coming back to 8-bit very recently, so I probably can't contribute anything. But I'll have to bring it up at the next SVFIG meeting (silicon valley forth interest group) because there are a lot of old timers there who used to program 8-bit systems professionally. Assuming someone hasn't spread the word already.

by on (#45199)
frantik wrote:
the folks at playpower should check this out http://groups.google.com/group/playpowertech


They did :D

by on (#45211)
cool :D

i'm actually working on a similar type of library but for ASM6, so i've been checking out your code for some ideas on how to implement the the lowerlevel stuff. :)

one thing I noticed is it looks like youre polling $2002 for vblank? which is Not Good (tm) according to this thread

by on (#45228)
frantik wrote:
one thing I noticed is it looks like youre polling $2002 for vblank? which is Not Good (tm)


Yes thanks, I'm aware of that now, and will fix it in v0.2

Hey, I'd like to see your code too, as I'm an ultra newbie in assembler I could steal, I mean get inspired, by your code.

by on (#45252)
Petruza wrote:
Hey, I'd like to see your code too, as I'm an ultra newbie in assembler I could steal, I mean get inspired, by your code.


sure.. once it's finished i'll be sure to post it up :)

by on (#49848)
The demo nes run out nothing in FCEUX 2.1
What's wrong?

by on (#49859)
What is wrong is that I'm new to assembler, new to the NES and I developed it testing only with emulators.
But in fact it works on other emulators I don't remember now. jNES, I think. And in some version of FCEUX.

by on (#68444)
Is it possible to download rnes library? Links in the topic are broken

by on (#69029)
Buba wrote:
Is it possible to download rnes library? Links in the topic are broken

I would also like to know the same thing. I came here from Google, while searching for information on cc65 for NES, and came across the rNES library. I would like to try it out, but the website linked in the first post doesn't exist anymore.

by on (#69042)
Hi, I've received some requests for this since it's not online anymore, so here it is:

http://www.megaupload.com/?d=Z7CXE0NG

I'm afraid I won't be doing any more development on rNES but I invite and encourage anyone to take it, complete it and make it better.
My idea was coding it again from scratch in assembler, due to NES's memory and cpu cycles constraints, but still providing a C binding to use the library.
Anyway, if anyone wants to continue work on this, let me know!

by on (#69059)
Megaupload says "File temporary unavailable" :( Let's wait for some time, maybe it is just hoster problem

Update. It was turned off Flash in my browser.

by on (#69533)
Cant compile examples. It seems that nes-chr-ram.cfg is buggy. Linker says
Code:
ld65: Warning: Duplicate external identifier: `_exit'
ld65: Warning: Memory area overflow in `HEADER', segment `HEADER' (16 bytes)
ld65: Warning: Memory area overflow in `ROMV', segment `VECTORS' (12 bytes)
ld65: Error: Cannot generate output due to memory area overflows

by on (#69534)
Unfortunately I can't help you, since my understanding of ld65's memory blocks was already weak at its peak, and now it's completely gone due to months not even looking at the code. :(

If you can fix it, let us know, though.

by on (#69543)
You're linking something more than once. What's your makefile/build script look like?

by on (#69548)
Copypasted from readme and add map file option
Code:
ld65 -C ../tools/nes_chr-ram.cfg  NEStronome.o  ../rNES.o  ../tools/nes_chr-ram.o  nes.lib  atmos.lib -o NEStronome.nes -m map

I dont know if it really need atmos.lib. Remove it have no effect
From map file:
Code:
Segment list:
-------------
Name                  Start   End     Size
--------------------------------------------
CHARS                 000000  000FFF  001000
HEADER                000000  00001F  000020
ZEROPAGE              000002  00001B  00001A
DATA                  006000  0064B7  0004B8
BSS                   0064B8  0064FD  000046
STARTUP               008000  0080B9  0000BA
INIT                  0080BA  0080E9  000030
CODE                  0080EA  0093AC  0012C3
RODATA                0093AD  009524  000178
VECTORS               00FFF6  01000D  000018


Anyway, rNES itself doesnt want to compile:

Code:
rNES.c(97): Error: Invalid right operand for binary operator `+='
rNES.c(209): Error: Invalid right operand for binary operator `+='

Code like this:
Code:
void rNES_copy_vram( byte *source, word length, word target )
{
   byte *top = source;
   top += (byte*)length;

Error in the last line. Is (byte*) really need in this place?

by on (#69550)
Try removing each of the .o files from the ld65 line to find which one(s) are responsible for the duplicate _exit error and HEADER overflow error. And yeah, that cast is erroneous. Maybe an older version of cc65 was more tolerant of errors like this.

by on (#69552)
There are _exit in nes.lib and nes-chr-ram.o . I turned off exporting _exit in nes-chr-ram and reassembled it. This warning is gone, but other errors and warnings still in place

by on (#75916)
It seems that i managed to compile helloworld with rNES. I used cc65 ver. 2.13.2 and rNES 0.1. Build system of 0.2 is too complex for me now. How to do it:
Use local copy of nes.lib and remove crt0.o from it:
Code:
ar65 d nes.lib crt0.o

Add line
Code:
.export         __STARTUP__ : absolute = 1
to the export part of crt0_chr-ram.s and recompile it:
Code:
ca65 crt0_chr-ram.s

Compile and assemble your program as write in the manual:
Code:
cc65 hello.c
ca65 hello.s

Link it with changed command. I presume that all needed files in the same directory
Code:
ld65 -C nes_chr-ram.cfg  crt0_chr-ram.o  hello.o  rNES.o ./nes.lib -o hello.nes
Re: rNES - a NES library for cc65 - version 0.1 released
by on (#124376)
Hi, it's been around 5 years since I stopped development on rNES, so have that in mind, I barely remember any technical details about it.
But some people asked about it since the download links stopped working, so I put it up again in case anyone wants to take a look.
The source is a bit of a mess, you should look for the folder "rNES-0.1", don't let "rNES-0.2" deceive you, it's a work in progress, I don't think it even compiles.

Here:
https://drive.google.com/file/d/0B6N3jOtfqZeJbVA4cnA2Y1lkc0k/edit?usp=sharing