I'm currently learning from this site:
http://shiru.untergrund.net/articles/pr ... s_in_c.htmWould anyone be able to tell me how to use more than one tile set within one of these examples?
Thanks,
Jake
How many tile sets do you plan to use in a single project, and how big are they?
You can use two tilesets with NROM, assigning one of them to the background and/or sprites with the bank_bg and bank_spr functions. If you need more than two, you will have to use a mapper.
CNROM is very easy to do and is cheap in manufacturing. It only switches graphics, so you can choose between 4 different banks.
To add it to Shirus library, add this to neslib.s:
Code:
.export _bankswitch
_bankswitch:
tax
sta bankBytes,x
rts
bankBytes:
.byte $00,$01,$02,$03
Then add this in neslib.h:
Code:
void __fastcall__ bankswitch(unsigned char bank);
Now you can have 4 different sets of character graphics imported in crt0.s like this:
Code:
.segment "CHARS"
.incbin "bank1.chr"
.incbin "bank2.chr"
.incbin "bank3.chr"
.incbin "bank4.chr"
Don't forget to change NES_MAPPER to 3 and NES_CHR_BANKS to 4 (or as many as you have)
I think that should be it. To use it in your C game, just do:
Code:
bankswitch(x);
Nioreh wrote:
...and NES_CHR_BANKS to 4 (or as many as you have)
Try to use powers of 2 though, since emulators have a hard time dealing with weird ROM sizes (understandably so, since real chips always come in powers of 2). In the case of CNROM you can safely have 1 (pointless, because that'd be the same as NROM), 2 or 4 CHR-ROM banks.
@tepples I'm only looking to use 2. One as a background and one with text so i can write text to the screen. I'm not sure of the sizes, how would I check?
@ Shiru, thats what I was planning, still trying to figure out how to bring two chr files into the game.
@Nioreh Thanks for the code, I'm currently only looking to use two in the way Shiru stated above. However this looks like something I'd use in the future.
@tokumaru, will make note, thanks.
Alright, so when I try adding another chr I get this:
.segment "CHARS"
.incbin "tileset.chr"
.incbin "tileset2.chr"
also made NES_CHR_BANKS a larger number.
Gives me:
ld65.exe: Warning: Memory area overflow in `CHR', segment `CHARS' (4096 bytes)
ld65.exe: Error: Cannot generate output due to memory area overflow
Press any key to continue . . .
In my examples there are two banks already, combined into one 8K CHR file.
Note that you can't easily use tiles from two banks on a single screen. If you want to have background graphics in one bank, font graphics in another, and display text as the background, you'll have to do a mid-screen bank change, without mixing graphics from two banks on the same scanline.
jshift wrote:
@tepples I'm only looking to use 2. One as a background and one with text so i can write text to the screen.
The problem is that on the NES, text *IS* part of the background. Since there can only be 8 sprites in a row (any more than that are not displayed), you can't really write much with them.
Some games do however have separate tilesets for text, but they use a "trick" that consists in bankswitching CHR-ROM halfway through the rendering of the screen. This is common in RPGs for example, where text boxes use a different tileset from the rest of the screen. This is a bit hard to pull off though, because it requires timed code or sprite 0 hits or mapper IRQs, or even combinations of these.
Quote:
I'm not sure of the sizes, how would I check?
Check the sizes of the CHR files. In windows you can right-click the a file and ask for its properties, then it will tell you the size of the file in bytes. The NES can see 8KB (8192 bytes) of tiles at a time, usually half of this is for the background and half is for sprites (but when using 8x16 sprites the sprites can use tiles from the background side).
@Shiru, that explains the error. I was including one chr was was 4kbs and another from one of your examples which was 8
So if I'm using two 4kb chrs I need to use the technique your describing to show them both on the screen at once? Is there any examples of this or can you give me some function names I should take a look at?
I guess to explain what I'm doing is making a card for someone special, just a static picture for the background with text below it. The background took up all the space in the first chr that I need another for the text.
Here is a pic:
The white below is where I'd like the text.
Thanks for bearing with me, although I am a programmer I'm not to familiar with any of this... I can say I am having fun though
You can make a sprite 0 hit, and then when it triggers, switch the 4KB page used by the background to the next graphics set.
jshift wrote:
The background took up all the space in the first chr
Did you remove the redundant black tiles?
If you really don't have space for text in the same 4KB as the picture, you should do what 3gengames said. After the sprite hit, in ASM you just change a bit in the $2000 register, pretty simple. I have no idea how to do it in C though! =P
Anyway, you don't need a mapper for this, NROM is fine.
tokumaru wrote:
jshift wrote:
The background took up all the space in the first chr
Did you remove the redundant black tiles?
If you really don't have space for text in the same 4KB as the picture, you should do what 3gengames said. After the sprite hit, in ASM you just change a bit in the $2000 register, pretty simple. I have no idea how to do it in C though! =P
Anyway, you don't need a mapper for this, NROM is fine.
Yes I removed them, no space
Not sure what 3gengames is describing. I'm thinking I need to do something like Shiru describes thought. I'm just not sure how to.
No, you need to display more tiles, there's many ways, but sprite 0 is the easiest. You do this by changing them as the PPU is rendering. Sprite 0 is a flag on the PPU that when a sprite0 non-zero pixel overlaps another non-zero pixel from the background, it will set the flag, allowing for you to then go and change the graphics used by the PPU in $2000, resulting in having more graphics available for the background. Or you can just change the 4KB graphics page if you're using a mapper, which also works.
jshift wrote:
Not sure what 3gengames is describing. I'm thinking I need to do something like Shiru describes thought.
They are both saying the same thing. After the picture is displayed, switch the tileset so that text can be displayed. The easiest way to detect *when* to make that change is a sprite 0 hit.
Quote:
I'm just not sure how to.
How do you write to the PPU ports in C? The PPU has this control register ($2000) that you can use to configure some aspects of the PPU, one of them being which pattern table is used for backgrounds and which is used for sprites. The idea is that during VBlank (i.e. before rendering starts) you set the background to use the pattern table that contains the picture. Also during VBlank, you position the first sprite (sprite index 0) right below the picture, making sure that a non-transparent pixel in it overlaps a non-transparent pixel in the background (that's the only way the PPU will detect a "hit"). Then you wait for the hit to happen, by reading the PPU status register ($2002) repeatedly until it indicates that a hit has happened. At that point, you make another write to the control register and make the background use the other pattern table, the one that contains the font. Hopefully Shiru will provide details on how to do this in C.
tokumaru, the whole point of my library is that is high level, so it frees the user from thinking about ports.
It is possible to write and read ports by a pointer, though, but as the screen splits are picky to the timings, I think a better idea is to just write a function in assembly code, a simple loop that waits for sprite 0 hit. There is an explaination of how to add an assembly function to C program in my article and in this thread.
Shiru wrote:
tokumaru, the whole point of my library is that is high level, so it frees the user from thinking about ports.
I see... I wasn't sure how much abstraction it had.
Quote:
but as the screen splits are picky to the timings
Not so much in this case though... there is a whole bunch of nothing between the picture and the text.
Waiting for the sprite 0 hit with C code it would be just a single iine then:
Code:
while(!((*(unsigned char*)0x2002)&0x40));
(I was about to suggest to add "volatile" in the cast, but I just remembered that CC65 doesn't optimise memory accesses anyway, and do the obvious thing expected here.)
That line should be OK, but be careful with CC65, some variations of how you write that line could generate
very poor code. The volatile keyword is a good idea, semantically speaking, though as stated it doesn't do anything in CC65. I put it in a macro to keep myself from having inconsistent syntax:
Code:
#define RAW_BUS(x) (*((volatile unsigned char*)x))
However I wouldn't really recommend doing your sprite 0 detection in a C loop like that.
There will be extra code around the loop that will reduce its accuracy greatly.(Apparently it's okay, see below.) Here's the routine I use, you can put it in an assembly file used by your project:
Code:
.export _ppu_sprite_0_hit
.proc _ppu_sprite_0_hit
:
bit $2002
bmi :- ; wait for vblank to end if in vblank
:
bit $2002
bvc :- ; wait for sprite 0 hit
rts
.endproc
Then just define it like this in your C file and it should be ready to use:
Code:
void ppu_sprite_0_hit();
Note the assembly version is preceded by an underscore (_); C functions have this automatically added when they are compiled to assembly.
The code with greatly reduced accuracy is in fact this:
Code:
;
; while(!((*(unsigned char*)0x2002)&0x40));
;
L0281: lda $2002
and #$40
beq L0281
Not too bad, better than I thought.
rainwarrior wrote:
There will be extra code around the loop that will reduce its accuracy greatly.
Again, not that it will matter in this particular case. There are a number of "empty" scanlines between the picture and the text, it's not like there's any urgency to react to the sprite hit, and it will not make a difference if it varies greatly from frame to frame.
jshift wrote:
Alright, so when I try adding another chr I get this:
.segment "CHARS"
.incbin "tileset.chr"
.incbin "tileset2.chr"
also made NES_CHR_BANKS a larger number.
Gives me:
ld65.exe: Warning: Memory area overflow in `CHR', segment `CHARS' (4096 bytes)
ld65.exe: Error: Cannot generate output due to memory area overflow
Press any key to continue . . .
Yeah, I did miss one thing when explaining how to use CNROM. In nes.cfg, there is a line describing the layout of the chr memory. It looks like:
Code:
CHR: start = $0000, size = $2000, file = %O, fill = yes;
You will have to change the size to the correct value. ($4000 for 2 8k-banks, $8000 for 4). But you only need to change this if you go for a mapper like CNROM.
Shiru wrote:
The code with greatly reduced accuracy is in fact this:
Code:
;
; while(!((*(unsigned char*)0x2002)&0x40));
;
L0281: lda $2002
and #$40
beq L0281
Not too bad, better than I thought.
Well, that code turned out better than expected! I should have checked it. Thanks for following up.