
/*********************************************************************
rNES - a NES library for cc65 - version 0.1

(c) 2009 Ernesto Borio A.K.A. Petruza
mail:	petruzanautico@yahoo.com.ar
web:	petruza.com.ar/rnes

This library was made while learning to program the NES, therefore
it's incomplete and not very efficient. Hopefully it will be more
accurate and efficient in future versions, including a total re-
write in assembler, some day. (while retaining the capability of
being linked to C code)

Use this software in any way you like, modify it, distribute it
and use it on your free or commercial projects. Keeping this header 
and mentioning the author would be great though.

Special thanks to the guys at cc65 mailing list and nesdev forum

*********************************************************************/

#ifndef _rNES_h
	#define _rNES_h

//--------------------------------------------------------------------- General

typedef unsigned char		byte;
typedef unsigned int		word;

#define true	1
#define false	0

// Trick to write binary. example: bin(0,0,0,0,1,0,0,1) = bin4(1,0,0,1) = 9
#define  bin(h,g,f,e,d,c,b,a)	  (a|b<<1|c<<2|d<<3|e<<4|f<<5|g<<6|h<<7)
#define bin8(h,g,f,e,d,c,b,a)		(a|b<<1|c<<2|d<<3|e<<4|f<<5|g<<6|h<<7)
#define bin7(g,f,e,d,c,b,a)			(a|b<<1|c<<2|d<<3|e<<4|f<<5|g<<6)
#define bin6(f,e,d,c,b,a)				(a|b<<1|c<<2|d<<3|e<<4|f<<5)
#define bin5(e,d,c,b,a)					(a|b<<1|c<<2|d<<3|e<<4)
#define bin4(d,c,b,a)						(a|b<<1|c<<2|d<<3)
#define bin3(c,b,a)							(a|b<<1|c<<2)
#define bin2(b,a)								(a|b<<1)

// multiplications and divides
#define times2( num )			( num <<1 )
#define times4( num )			( num <<2 )
#define times8( num )			( num <<3 )
#define times16( num )		( num <<4 )
#define half( num )				( num >>1 )

#define highbyte( value )		((byte)(value>>8))
#define lowbyte( value )		((byte)value)

#define addr(_addr)		(*(unsigned char*) (_addr))
/* Macro for direct memory access. use it both as r-value and l-value
	Example: addr(0x0200) = 5; sets memory address $200 to value 5. */
	
void rNES_init();
/* Right now, it only waits two vblanks. Call it at start */

void rNES_fill_ram( word address, word length, byte fill );
/* Fills an area of Program RAM with the byte 'fill', starting at address
	'address' and of size 'length' in bytes. */

//---------------------------------------------------------------------- Joypad
	
byte rNES_get_joy( word joypad );
/* reads the state of the joypads
	-joypad: supply rNES_JOYPAD_1 or rNES_JOYPAD_2 and none other.
	-returns: a byte whose bits represent the joypad state. Check against
		rNES_*_Down() to test if a certain button is pressed or not */

// Bitmasks to test for buttons on get_joy's return value
#define rNES_A			0x80
#define rNES_B			0x40
#define rNES_SELECT	0x20
#define rNES_START	0x10
#define rNES_UP			0x08
#define rNES_DOWN		0x04
#define rNES_LEFT		0x02
#define rNES_RIGHT	0x01

// Macro functions to check if a certain button is pressed or not
#define rNES_A_Down( joy_state ) 				( joy_state & rNES_A )
#define rNES_B_Down( joy_state ) 				( joy_state & rNES_B )
#define rNES_SELECT_Down( joy_state ) 	( joy_state & rNES_SELECT )
#define rNES_START_Down( joy_state ) 		( joy_state & rNES_START )
#define rNES_UP_Down( joy_state ) 			( joy_state & rNES_UP )
#define rNES_DOWN_Down( joy_state ) 		( joy_state & rNES_DOWN )
#define rNES_LEFT_Down( joy_state ) 		( joy_state & rNES_LEFT )
#define rNES_RIGHT_Down( joy_state ) 		( joy_state & rNES_RIGHT )

// Joypads' read & write memory address. Supply them to rNES_get_joy()
#define rNES_JOYPAD_1		0x4016
#define rNES_JOYPAD_2		0x4017

//----------------------------------------------------------------------- Sound

void rNES_sound_init( byte channels );
/* supply all channels that will be used, OR'ed. For now, only rectangular wave
	channels 1 & 2 are supported.
	Example: rNES_sound_init( rNES_snd_chn_1 | rNES_snd_chn_2 ) */

void rNES_sound( byte channel, byte length, byte volume, word wavelength );
/* makes a sound in the given channel, with the given parameters.
	-length: use only the macros rNES_sndlen_*
	-volume: only 4 lower bits used, range: [0..15]
	-wavelength: only 11 lower bits used, range: [0..2047] The higher this value,
		the lower the pitch of the sound. (inversely proportional to frequency) */

// Sound channels
#define rNES_snd_chn_1		1 // rectangular pulse wave channel #1
#define rNES_snd_chn_2		2 // rectangular pulse wave channel #2

// use these for length in rNES_sound(). Don't supply any other values
#define rNES_sndlen_1   bin(0,0,0,1,1 ,0,0,0)  
#define rNES_sndlen_2   bin(0,0,1,0,1 ,0,0,0)  
#define rNES_sndlen_3   bin(0,0,1,1,1 ,0,0,0)  
#define rNES_sndlen_4   bin(0,1,0,0,1 ,0,0,0)  
#define rNES_sndlen_5   bin(0,1,0,1,1 ,0,0,0)  
#define rNES_sndlen_6   bin(0,1,1,0,1 ,0,0,0)  
#define rNES_sndlen_7   bin(0,1,1,1,1 ,0,0,0)  
#define rNES_sndlen_8   bin(1,0,0,0,1 ,0,0,0)  
#define rNES_sndlen_9   bin(1,0,0,1,1 ,0,0,0)  
#define rNES_sndlen_10  bin(1,0,1,0,1 ,0,0,0)  
#define rNES_sndlen_11  bin(1,0,1,1,1 ,0,0,0)  
#define rNES_sndlen_12  bin(1,1,0,0,1 ,0,0,0)  
#define rNES_sndlen_13  bin(1,1,0,1,1 ,0,0,0)  
#define rNES_sndlen_14  bin(1,1,1,0,1 ,0,0,0)  
#define rNES_sndlen_15  bin(1,1,1,1,1 ,0,0,0)  
#define rNES_sndlen_16  bin(1,1,1,1,0 ,0,0,0)  
#define rNES_sndlen_20  bin(0,0,1,0,0 ,0,0,0)  
#define rNES_sndlen_22  bin(1,0,1,0,0 ,0,0,0)  
#define rNES_sndlen_36  bin(1,1,0,1,0 ,0,0,0)  
#define rNES_sndlen_40  bin(0,0,1,1,0 ,0,0,0)  
#define rNES_sndlen_48  bin(1,0,1,1,0 ,0,0,0)  
#define rNES_sndlen_80  bin(0,1,0,0,0 ,0,0,0)  
#define rNES_sndlen_96  bin(1,1,0,0,0 ,0,0,0)  
#define rNES_sndlen_127	bin(0,0,0,0,0 ,0,0,0)

//-------------------------------------------------------------------- Graphics

#define rNES_PPU_status()								addr(0x2002)
// returns the PPU status into one byte

#define rNES_VBlank( PPU_status )				( PPU_status & bin(1,0,0,0,0,0,0,0) )
// Checks bit 7 of the PPU status, if true, the vertical blank is ocurring

#define rNES_sprite0hit( PPU_status )		( PPU_status & bin(0,1,0,0,0,0,0,0) )
// Checks bit 6 of the PPU status, if true, the sprite 0 hit occured

#define rNES_waitvblank()		while( ! rNES_VBlank( rNES_PPU_status() ) )
// equivalent to cc65's waitvblank() but without a function call

#define rNES_scroll( horizontal, vertical )		addr(0x2005) = horizontal; addr(0x2005) = vertical;
// sets horizontal and vertical scroll

void rNES_disable_graphics();
// Turns off background and sprite rendering, among other things

void rNES_enable_graphics();
// Turns them on again

// use these for 'start_to' in rNES_copy_tiles()
#define rNES_back_tiles		0x0000
#define rNES_sprite_tiles	0x0100

void rNES_copy_tiles( byte* tiles, word start_from, word start_to, word quantity );
/* Copies tiles from Program RAM to Video RAM.
	- tiles: it's a pointer to where the tile data is stored
	- start_from: index of the first tile to be copied. As tiles occupy 16 bytes, for example
			specifing 2 here, would use data starting from ( tiles + 2 * 16 )
	- start_to: index of the first tile in VRAM to write to. Same as above. Use rNES_*_tiles
	- quantity: How many tiles to be copied.
*/

// Macros for use in rNES_fill_vram() and rNES_copy_vram()
#define rNES_back_tiles_addr			0x0000
#define rNES_sprite_tiles_addr		0x1000

#define rNES_back_palette_addr		0x3F00
#define rNES_sprite_palette_addr	0x3F10

#define	rNES_tiles_length					0x0100
#define rNES_all_tiles_length			0x0200

#define	rNES_palette_length				0x0010
#define rNES_all_palettes_length	0x0020

void rNES_fill_vram( word address, word length, byte fill );
/* Fills Video RAM with the value 'fill' starting in 'address' and with as many bytes as 'length' 
	( do this on vblank, or else: glitches! ) */

#define rNES_put_vram( address, value )	\
	addr(0x2006) = highbyte( address );	\
	addr(0x2006) = lowbyte( address );	\
	addr(0x2007) = value;
// Like rNES_fill_vram() but with one byte only

// This struct holds a sprite info on Program RAM, before copying it to Sprite RAM (OAM)
typedef struct
{
	byte y; 		// y coordinate minus 1 ( 0xFF means 0, 0 means 1, and so on )
	byte tile;	// tile index from pattern table
	byte attr;	// attributes defined below
	byte x;			// x coordinate
} rNES_sprite;

// Use these bit masks with rNES_sprite::attr
#define rNES_sprite_vert_flip		bin(1,0,0,0,0,0,0,0)
#define rNES_sprite_horz_flip		bin(0,1,0,0,0,0,0,0)
#define rNES_sprite_behind_bg		bin(0,0,1,0,0,0,0,0)
#define rNES_sprite_palette_id	bin(0,0,0,0,0,0,1,1)

void rNES_sprite_dma( byte page, byte start );
/* Dumps 256 sprites's data from Program RAM to Sprite RAM (OAM)
	- page: For example, if your sprite data starts at PRG-RAM 0x0200, then page is 2.
			as this is a multiple of 0x100, sprite data must start in 0x100 boundaries.
	- start: specifies what sprite to start with. Not tested. usually 0.
*/

// Use these with rNES_copy_vram()
#define rNES_name_table_0			0x2000
#define rNES_attr_table_0			0x23C0
#define rNES_name_table_1			0x2400
#define rNES_attr_table_1			0x27C0
#define rNES_name_table_2			0x2800
#define rNES_attr_table_2			0x2BC0
#define rNES_name_table_3			0x2C00
#define rNES_attr_table_3			0x2FC0
#define rNES_back_palette 		0x3F00
#define rNES_sprite_palette 	0x3F10
#define rNES_name_table_len		0x03C0
#define rNES_attr_table_len		0x0040
#define rNES_palette_len			0x0010

void rNES_copy_vram( byte *source, word length, word target );
/* copies a chunk of data from PRG-RAM to VRAM
	- source: pointer to data in PRG-RAM
	- length: how many bytes to copy
	- target: address in VRAM to copy to
	( do this on vblank, or else: glitches! )
*/

void rNES_print( byte* string, byte x, byte y, word name_table_addr, byte* charset );
/* Prints a string on the background ( nametable )
	- string: The null terminated string to be printed\
	- x, y: nametable coordinates. ( The screen is 32 x 30 )
	- name_table_addr: address of the nametable to write to. Use rNES_name_table_*
	- charset: an array in which the indexes are ASCII values minus 32, and the values
			specify the tile index where the letter graphics is stored.
			For example: index [33] should have the tile index of letter 'A'. ( 33 + 32 = 65, ASCII 'A' )
			The reason for 32 is that ASCII 32, the space, is the first ASCII printable character.
			The charset can be as long as you want, I.E. specifying uppercase and lowercase or only
			uppercase, including numbers or not, but as limits are not checked, you have to make sure
			the strings to be printed don't have characters out of charset's range, otherwise, garbage will
			show on the screen.
			
	The user has to load the graphics for the letters in the background tiles area ( VRAM 0x0000 )
	This letters' colors are obviously affected by attribute tables.
*/

#endif // #ifndef _rNES_h


























