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

NES C programming

NES C programming
by on (#51714)
Alright, so I've done a bit of searching for this, and I've come to the conclusion that no libraries for the NES C compiler have been made.

Compiler can be found here: http://www.cc65.org/

So I'm going to take it upon myself to build my own libraries that will allow:
* Joypad Input
* Sprite Drawing
* Pallette Loading

and much more.

The only problem is, I am not fluent in NES asm and am just beginning, so I will be following the basic NES asm tutorials here: http://patater.com/gbaguy/nesasm.htm

Here is a little bit of test code you folks can try out at home, I've already made the keyboard input part.

Code:
#include <conio.h>
#include <peekpoke.h>
#include <6502.h>

typedef struct joypad
{
   unsigned char a;
   unsigned char b;
   unsigned char select;
   unsigned char start;
   unsigned char up;
   unsigned char down;
   unsigned char left;
   unsigned char right;
} joypad;

void keyprocess(joypad* joy1)
{
   // Reset the Pad
   asm("lda #$01");
   asm("sta $4016");
   asm("lda #$00");
   asm("sta $4016");

   // Read Keys
   joy1->a = PEEK(16406) & 1;
   joy1->b = PEEK(16406) & 1;
   joy1->select = PEEK(16406) & 1;
   joy1->start = PEEK(16406) & 1;
   joy1->up = PEEK(16406) & 1;
   joy1->down = PEEK(16406) & 1;
   joy1->left = PEEK(16406) & 1;
   joy1->right = PEEK(16406) & 1;
}

joypad joy;

void main (void)
{
   clrscr();
   cprintf("hello world");
   while(1)
   {
      clrscr();
                gotoxy(0,0);
      keyprocess(&joy);
      if (joy.a)
      {
         cprintf("win");
      }
      else
      {
         cprintf("fail");
      }
   }
}


Oh and here is the batch file I use to compile (assuming the C file is named test.c):

Code:
SET CC65_INC=\nesC\include
SET CC65_LIB=\nesC\lib
SET PATH=\nesC\bin

cc65.exe -t nes test.c
ca65.exe -t nes test.s
ld65.exe -t nes test.o nes.lib -o test.nes
pause

by on (#51716)
Hi,

It's seems that gbaguy's tutorial is not really good. Some event think that it really sucks.

Anyway, there are other good tutorials, and so far I've been reading them, I can recommend you bunnyboy's one.

by on (#51722)
Yeah, just wanted to echo what Rid said above. Bunnyboy's Nerdy Nights are very good for newbies! Check them out.

by on (#51810)
Yeah thanks guys, I'll refer to bunny boys from now on.

Also I've successfully made palette loading and a little app which converts a file to raw c code.

by on (#51821)
Didn't petruza make a C library for NES development?

by on (#51848)
Ahh, thanks, this will help me develop my library. (His uses pure C code, but mine will use ASM as much as possible to keep it more effecient and optimized)

EDIT: Don't forget about learning experiences :D

EDIT2: Oh guys, I've hit a bit of a roadblock. I was wondering if anyone could give me pointers on compiling a new NES.lib file, as the current one isn't flexible enough to allow NMI vector handling in C and etc.

by on (#51893)
Oh guys, I've hit a bit of a roadblock. I was wondering if anyone could give me pointers on compiling a new NES.lib file, as the current one isn't flexible enough to allow NMI vector handling in C and etc.

by on (#51902)
I don't think many people in this forum uses CC65 in C for making libraries so maybe you will not be able to get your answer here. Most people uses CA65 directly, me included.

I'm sure there must be some help on their forum (CC65) for making libraries.

by on (#51912)
You may want to check out the CC65 mailing list, I haven't looked at it in a very long time but I recall it was very active, and Uz (who wrote/maintains CC65) is really helpful.

by on (#51922)
Alright, I've managed to compile a more dynamic library (without cprint as well, since there is no point in having those functions)

I believe there is a problem with how I'm handling sprites, so I just wanted to check with you guys.

Code:
;
; File generated by cc65 v 2.13.0
;
   .fopt      compiler,"cc65 v 2.13.0"
   .setcpu      "6502"
   .smart      on
   .autoimport   on
   .case      on
   .debuginfo   off
   .importzp   sp, sreg, regsave, regbank, tmp1, ptr1, ptr2
   .macpack   longbranch
   .export      _tile

.segment   "RODATA"

_tile:
   .byte   $03
   .byte   $0F
   .byte   $1F
   .byte   $1F
   .byte   $1C
        <... The rest of the bytes that make up the sprites/tiles... >


So thats how my file looks, and in the C code it uses lowbyte and highbyte of the address to copy it into the DMA. This is done during the NMI loop.

by on (#51923)
I have to admit it scares me a little bit that every person who's worked on NES libraries for CC65 hasn't done any development with NES before, heheh. It's just that most people who write their first asm program ends up finding out about a lot of little quirks and optimal order of operations they didn't know about at first.

I'm little experienced with C, and tried once to fix Groepaz's NES libraries, and had no luck with it really. I suspect a lot of the forum users are the same, better with asm and not as knowledgeable with using C libraries. But don't let that discourage you, I think it'd be great to have a usable mixed C/asm dev environment.

Quote:
So thats how my file looks, and in the C code it uses lowbyte and highbyte of the address to copy it into the DMA. This is done during the NMI loop.


By the DMA you mean the page in RAM that will be DMA'd? You definitely won't want to use the $2004 register, it's too slow. If you're copying it from ROM (RODATA segment), it won't be too useful to copy that every frame. You could copy it once during an initialization, then modify it in RAM to move the sprites.

Also when it gets to more advanced projects, many projects will call for working with animated objects that are made up of multiple sprites. Here's the routine I use, though I'm not sure how easy it is to decypher - http://www.parodius.com/~memblers/nes/animate.asm
It builds the sprites, and depending on the data tables, will handle their animation as well. I believe they are moved by just manually moving the first sprite of the animation. This provides no "OAM cycling" however, they are in fixed positions in RAM, so there's trouble if there are more than 8 sprites on a line.

by on (#51927)
Code:
// NES_LoadSprite_real(address of the tileset)
void NES_LoadSprite_real(unsigned short tileset)
{
   // Load Params
   ++tileset; // fix address
   cTemp = lowbyte(tileset);
   cTemp2 = highbyte(tileset);
   // Set up DMA range
   asm("lda %v",cTemp); // lowbyte
   asm("sta $2003");
   asm("lda %v",cTemp2); // highbyte
   asm("sta $4014");

}


This is the current code for 'loading' the sprite. basically the text '%v' means it will use the value of the variable used afterwards. in the first case, cTemp (aka the lowbyte of the tilesets address)

Is this fine, as is?

by on (#51932)
No.

The DMA always starts at highbyte(tileset)<<8. If lowbyte(tileset) != 0, then this code will not do what you want.

by on (#51933)
So basically if the address where the tile data is stored at, ends up not being 0 for the lowbyte, it won't load?

by on (#51986)
Alright guys, I think I know for sure whats wrong with my code. How do I tell the NES (In asm) to store a value at an address that is held in a variable.

Psuedo Code example:

Code:
lda #01
ldx #$0100
mga


My hypothetical asm code 'mga' would store the value of the Accumulator at the address of the X register.

by on (#51987)
Doogie wrote:
How do I tell the NES (In asm) to store a value at an address that is held in a variable

The registers on the NES are all 8-bit, so they can't represent absolute addresses (and the CPU doesn't combine them in any way to form a 16-bit value), so the only way to do this is using indirect indexed addressing.

To do this, the address in question must be stored in a zero page location (not in a register, like in your example). Unfortunately, the 6502 will always add the Y register to this address, so if you don't plan on using indexes, you have to set it to 0.

Code:
   ;set the destination address
   lda #$3F
   sta $00
   lda #$04
   sta $01
   ldy #$00

   ;write the value
   lda #$55
   sta ($00), y

The code above will store value $55 in memory location $043F. Alternatively, you can keep the low byte of the pointer as zero and have Y hold the lower byte of the address. The following code will do the same as the above:

Code:
   ;set the destination address
   lda #$00
   sta $00
   lda #$04
   sta $01
   ldy #$3F

   ;write the value
   lda #$55
   sta ($00), y

by on (#51988)
Thank you very much, I literally just found out then you have to bracket it for pointers, but you probably saved me a lot of debugging with your in-depth description.

Thank you very much :)

EDIT:

Variable Label:
Code:
_sTemp:
   .res   2,$00


ASM Snippet:
Code:
   ldy     #$00
   lda     #$01
   sta     (_sTemp)
   lda     #$00
   sta     (_sTemp)


Any reason why its throwing errors here for the compiler?

(I tried with the ',y' and it throws RANGE ERRORS. With the code above I get an ILLEGAL ADDRESS MODE)

by on (#51990)
_sTemp needs to be in zeropage, if not that would cause the range error. And all indirect addressing modes have to be indexed with the Y register.

by on (#51991)
Memblers wrote:
_sTemp needs to be in zeropage, if not that would cause the range error. And all indirect addressing modes have to be indexed with the Y register.


Yeah, figured that out before hand, and same with the indirect addressing procedure.

I just need to fix up the zeropage handling so I can actually use it now as the register handling doesn't work for the C compiler.

by on (#52018)
Alright!

I've successfully worked out how to use the zeropage for pointers and such, this is fantastic, thanks heaps for all your help :D

The current joystick code:

Code:
unsigned char NES_ProcessInput_real(unsigned short joypad)
{
   sTemp = joypad;

   // Get Lowbyte
   asm("lda %v",sTemp);
   asm("sta %v",cTemp);

   // Get Highbyte
   asm("lda %v+1",sTemp);
   asm("sta %v",cTemp2);

   // Load the Address of STemp (joypad) into the pointer
   asm("lda %v",cTemp);
   asm("sta regbank");
   asm("lda %v",cTemp2);
   asm("sta regbank+1");

   // Reset Y Register for indirect memory addressing.
   asm("ldy #$00");

   // Reset Strobes for address.

   asm("lda #$01");
   asm("sta (regbank), y");
   asm("lda #$00");
   asm("sta (regbank), y");

   // Read Keys

   // Read A
   asm("lda (regbank), y");
   asm("and #1");
   asm("sta %v",cTemp);
   return cTemp;
}


A Hybrid C/Asm function that uses a mere 24 lines in asm. I just need to work out on how to store all the key bits in one byte.

When outputted to Asm:
Code:
.segment   "CODE"

.proc   _NES_ProcessInput_real: near

.segment   "CODE"

   ldy     #$01
   jsr     ldaxysp
   sta     _sTemp
   stx     _sTemp+1
   lda     _sTemp
   sta     _cTemp
   lda     _sTemp+1
   sta     _cTemp2
   lda     _cTemp
   sta     regbank
   lda     _cTemp2
   sta     regbank+1
   ldy     #$00
   lda     #$01
   sta     (regbank),y
   lda     #$00
   sta     (regbank),y
   lda     (regbank),y
   and     #1
   sta     _cTemp
   ldx     #$00
   lda     _cTemp
   jmp     L0021
L0021:   jsr     incsp2
   rts

.endproc

by on (#52104)
Alright guys, I need a bit of help accessing the DMA. In C, Im not exactly sure on how to store the location of the byte data. (X,Y positions, attributes and tile number). Since this uses a linker, I can't really specify a specific address with .org or else other bits of the program might stuff up.

Also with the ".segment CHARS", does this mean that when I DMA the sprite data, it will automatically know to use the data in the CHR Page?

UPDATE: Found out that my palette's weren't loading properly, now fixed. Im currently working out how to store the DMA data as effectively and effeciently as possible ;)

by on (#52384)
Hello again guys, I've googled a bit but I can't seem to find any documentation on the NES Character format (.chr). I need to see some documentation so I can display CHR files in my Win32 application.

by on (#52386)
Doogie wrote:
Hello again guys, I've googled a bit but I can't seem to find any documentation on the NES Character format (.chr). I need to see some documentation so I can display CHR files in my Win32 application.

16 bytes per tile. The first 8 bytes contain the first bit and the next 8 bytes contain the second bit (tiles are 2bpp). The first byte contains data for the first line of pixels in the tile and so on.

by on (#52389)
Alright, so let me get this straight.

~ 16 bytes per Tile
~ The tile is cut in half vertically (or is it horizontally), LEFT side is byte 1, and right side is byte 2, to represent ONE straight line.

Byte 1 - First four pixels from the left
Byte 2 - First four pixels from the right.

by on (#52390)
Nope:

Byte 1 - First 8 pixel row, bit #0
Byte 2 - Second 8 pixel row, bit #0
...
Byte 8 - Last (8th) 8 pixel row, bit #0
Byte 9 - First 8 pixel row, bit #1
Byte 10 - Second 8 pixel row, bit #1
...
Byte 16 - Last (8th) 8 pixel row, bit #1

Each pixel is two bits.

by on (#52391)
Ok so its more like.

psuedo-code:
Code:
// ReadPixel(), read the bits of both byte1 and byte2 and merge them together to get the color (0-3)

colorvalue[1] = ReadPixel(byte1,byte2,0) // 0 is the bit (0-7)
colorvalue[2] = ReadPixel(byte1,byte2,1) // 0 is the bit (0-7)
...etc...
colorvalue[8] = ReadPixel(byte1,byte2,7) // 0 is the bit (0-7)


So the color value array 1-8 would be the first line.

by on (#52393)
There's a test case for your pattern table reading code on the wiki:
http://wiki.nesdev.com/w/index.php/PPU_pattern_tables

by on (#52445)
Thanks for your help guys, My CHR-Loader works perfectly :)

Unfortunately with the application I use (GameMaker) load times are about 4-5 seconds, but once its done, it runs fine as I save the image on screen as a sprite. :D

Image
You may notice that the palette used is simply the first four in the SMB1 palette.