It is currently Wed Dec 13, 2017 5:49 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 132 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next
Author Message
PostPosted: Fri Aug 07, 2015 10:40 am 
Offline

Joined: Thu Aug 12, 2010 3:43 am
Posts: 1589
Reading the whole initialization stuff... Something I'd like to remark: you should never rely on startup values from a mapper because it's likely they won't get restored when the Reset button is pressed. It's annoying when a game doesn't respond properly to the Reset button =P


Top
 Profile  
 
PostPosted: Fri Aug 07, 2015 11:34 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19342
Location: NE Indiana, USA (NTSC)
Sik wrote:
It's annoying when a game doesn't respond properly to the Reset button =P

Even more annoying is when the game uses the Reset button to exit a particular level, as in that X-Men game for Sega Genesis. If you're in that level, it goes to the next level. If you're in any other level, it starts the game over.

Then the other problem is in a multicart like Super Mario Bros./Duck Hunt. Should Reset go back to the game select or to the game's own menu? In SMB/DH, it goes back to game select. In Action 52, it goes back to an unskippable cut scene followed by game select. In Action 53, it's a bit inconsistent: NROM games go back to game select, while bigger games go back to the game's title screen and are expected to have "quit" on the menu.


Top
 Profile  
 
PostPosted: Fri Aug 07, 2015 12:06 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1868
Location: DIGDUG
Quote:
And when you're writing with extra program banks, how are you supposed to write code in memory spaces that are identical addresses to ones you've already written in the same program? Wouldn't the assembler throw up a flag? It's not like it understands that you're using a mapper. Do you write it in a different file and include it as a bin?


I was going to answer this, but I think I'll leave this to the more experienced people here. Do you guys compile each bank separately and have a final file with a bunch of 'incbin' statements, or do you have each bank in separate ASM files and have a final file with a bunch of 'include' statements?

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Fri Aug 07, 2015 12:26 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19342
Location: NE Indiana, USA (NTSC)
I have .asm files dedicated to each need (player, metasprite, sprite cel loading, background loading, menus, etc.), and each file places data into the segments associated with separate banks as needed.


Top
 Profile  
 
PostPosted: Fri Aug 07, 2015 1:25 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1046
Quote:
It tells you to put that code at the end of each 16384 bank. That's the only code it tells you to put there. It doesn't say to put the NMI handler or anything like that there.

Well, because it doesn't need the NMI handler at the end of every bank. What is guaranteed to happen when the console turns on? The game starts executing from the RESET vector's address.

The very first instruction SEI makes it so an IRQ won't fire. Yes, it might be possible for an NMI to fire in the very few instructions (probably less than 4) before you disable them, and then the game might crash. But usually the user would just think, "Oh, I need to blow on my cartridge." :lol:
Quote:
So i imagine you couldn't .org $FFFA, because it might be in the first bank. So how would you make sure that what you're writing actually occupies the last 16 bytes without a .org? And when you're writing with extra program banks, how are you supposed to write code in memory spaces that are identical addresses to ones you've already written in the same program? Wouldn't the assembler throw up a flag? It's not like it understands that you're using a mapper. Do you write it in a different file and include it as a bin?

Most of this isn't terribly important if you're not using MMC1, and a lot of solutions to the potential problems you lay out are assembler specific. But the one question to answer is, "How are you supposed to write code in memory spaces that are identical addresses to ones you've already written in the same program?"

And the answer is, you just do. .org $8000 in multiple banks and just write code. How the assembler knows whether or not that's a new bank varies a lot between assemblers.
Quote:
Now, I know that name is only for coding and is interpreted by the assembler and doesn't make it to the actual program. but won't the assembler say, "You have two labels for reset_stub and I can't put both of them at $FFFC."?

Depends on the assembler.
Quote:
3. I'm going to guess that when you write a bank, even if you can't know for sure if it will be booted on startup, then you'll know for sure that if it will be loaded into the top or bottom. That's gotta be true, right? Well, you said that this could actually happen.

No, you don't necessarily even know that at startup for some mappers. So that reset stub really does need to be in all banks.

That said, when you design your bank layout, you do design it so that it would always be swapped in at a particular place. (Whereever it's .org'd to.)

Quote:
It says that reset (the rest of the reset instruction not in the stub) must be located in $C000-$FFED. What about the nmi instructions and the irq instructions? Won't those have to be at the same point in all four banks in case any of those banks landed to where the vector addresses are pointing? If you could put a bank in the top that goes in the bottom, it seems like you're going to have to put copies of all of your interrupt instructions in every single bank. Why would you ever want to switch a bank between the top and bottom of your code? I can't imagine a situation in which a program would benefit from flipping the top and bottom halves in a way that couldn't be better achieved through branching and subroutines.

This may make more sense if you read the MMC1 documentation, specifically load: http://wiki.nesdev.com/w/index.php/MMC1 ... .24FFFF.29
When you write a set high bit to $8000, it ensures that mapped to $C000-$FFFF is the last 16 kilobytes of your PRG ROM. So if you've got sixteen 16KB banks, bank15 now occupies $C000-$FFFF. Another way to say it is the reset label needs to be in that bank. But that initialize code only ensures $C000-$FFFF. Nothing is guaranteed about $8000-$BFFF, but reset should take care of that. And yes, it's possible for an NMI to fire but typically reset would just disable it. You could put a disable NMI under reset_stub, but because that's duplicated everywhere, it becomes a waste. reset_stub is designed the do the absolute minimum to ensure the rest of the reset is only done once. THAT should set up $8000-$BFFF and then NMIs could be reenabled and everything would be cool.
Quote:
If I could hear that there is no way a person could ever want to switch a bank between the top and the bottom and it's just a glitch in the mapper that you have to account for then I'd feel decent at least about getting it.

There are reasons you might want to do that. And even if not, some people would prefer to have their code fixed at $8000 all the time, and some people would prefer their code fixed at $C000 all the time so the mapper supports both. And people aren't responsible for which of those two behaviors the mapper boots with. It's "random". The reset_stub ensures that $C000 is fixed to the last bank. Less random. Then reset would set it up exactly how you like.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Sat Aug 08, 2015 2:00 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
I could swear I saved the draft I typed earlier. Ugh. I'll get back to the off topic things later. You guys did actually answer my questions, as convoluted as they were. Thank you for taking the time to help a confused n00b.

So, I'm just going to see if I can break down what I need to get MMC3 functionality and how to start coding CHR bank switches. I'll start with the iNES header.

tokumaru wrote:
It's not so much "changing" as it is "creating" the iNES header. Anyway, I don't remember you mentioning which assembler you're using, so I can't say much about how to set things up.


I am using ASM6. I hear people say CA65 is better. I'm honestly not opposed to switching, I just started with ASM6 because I found a version of the Nerdy Nights tutorial for ASM6 so I had a place to start there. I did find more documentation on CA65 however, and I can see that it has a more robust directives list, so that's one benefit I can see from it, but other than that I'm not sure how the assemblers differ.

So from the iNES NESDEV Wiki Page, I see:

Quote:
An iNES file consists of the following sections, in order:
Header (16 bytes)
Trainer, if present (0 or 512 bytes)
PRG ROM data (16384 * x bytes)
CHR ROM data, if present (8192 * y bytes)
PlayChoice INST-ROM, if present (0 or 8192 bytes)
PlayChoice PROM, if present (16 bytes Data, 16 bytes CounterOut) (this is often missing, see PC10 ROM-Images for details)


So, on my program, taken from Nerdy Nights, I have:

Code:
byte "NES",$1a                          ; basically "NES" plus a terminator
byte $01                                ; 1x16 PRG-ROM block ($c000)
byte $01                                ; 1 CHR-ROM block
byte $00                                ; dontcare
byte $00                                ; dontcare
dsb 8                                   ; 8 bytes padding


Hopefully now I'm ready to come back to this part and understand it. This is all in the first 16 bytes so obviously it's just the header. From the wiki, I see:

Quote:
The format of the header is as follows:
0-3: Constant $4E $45 $53 $1A ("NES" followed by MS-DOS end-of-file)
4: Size of PRG ROM in 16 KB units
5: Size of CHR ROM in 8 KB units (Value 0 means the board uses CHR RAM)
6: Flags 6
7: Flags 7
8: Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility; see PRG RAM circuit)
9: Flags 9
10: Flags 10 (unofficial)
11-15: Zero filled


So 0-3 are ALWAYS "NES",$1a because that's what it expects. That's easy enough to remember. I don't think there's any practical reason why I'd need to understand that better.

4- So, at this point, I have just the basic non-switch PRG ROM, but I feel I need to understand this. My program has $01 as the entry, so what is 00? Just a non-existent program? I guess the big question for this is, "If my banks are switchable but not loaded, are they included in this total?" Instinctually I would think no, because they're not part of the program loaded by the CPU as $8000-$FFFF, however after a little more thought, I'm thinking the answer is yes, since this is just telling an emulator how large of ROM space it has to draw PRG code from, so I'm going to guess yes. Is that correct? If I had a total of 64KB of program data between all of my banks, would the answer be 07?

5- This seems to be the largely same formula as PRG ROM except with 8 KB, but this is where I'll actually be making adjustments for what I'm doing. Now with my MMC3 individual banks will be smaller than 8KB each, but it looks like I need to add them to my program in 8K chunks. Seems there's no option to do otherwise. So does this mean that I need to create 2 2K blocks for every 4 1K blocks? And would I need to order them a certain way so that they would be loaded properly? I'm getting ahead of myself from the header here, but I'll certainly need to know that much.

I'm imagining that I have to structure my CHR-ROM in 2 x 2KB blocks followed by 4 x 1KB block. So, if I just needed to add more 1KB blocks, I'd have to add an extra 4KB at the beginning for filler. Is that correct, or could I dedicate an entire 8K chunk to 1K character animation blocks?

6- Okay this is where it gets trickier. My program just says $00 for now. Okay, so bit zero is for mirroring, I get that. If bit 3 is 1, then it looks like it ignores bit 0. Bit 1 means that it contains a battery backed RAM, so I'll skip over that for now. Same with Bit 2 and the trainer. I don't even know what the trainer is. (explains later in the article; don't need it) Then there's the lower nybble of the mapper number. So a nybble is 4 bits. Mapper number is 04, 118, or 119. I'm going to just guess 04 is the most common one. So, the lower nybble of 04 is 0100, right? If that's correct, how would the software distinguish between mappers numbered greater than 0-15? Maybe I'll answer that myself before I get to the end of this post.

7-Alright, another byte. This one looks like a lot of stuff that I don't need at all. Bit 0, VS Unisystem, no, 0. PlayChoice-10, no, 0. NES 2.0, no, 0. Then there's that upper nybble. All zeros for this one. Okay so that's all zeros in this byte.

8- Okay, now the PRG-RAM. I have to admit, I am confused on PRG-RAM. That's where you write your data for battery-backed save games, right? Is there any other reason that you'd use it? I see it would get addressed at $6000-$7FFF. This is completely separate from extra VRAM for 4-screen mirroring isn't it?

9- Basically looks like it should just be zero. Even if designing for PAL it says it doesn't really do much.

10- It says this byte is not part of the official specification. Should I just fill it with 0s or try to answer the questions in it? TV system, NTSC, so 0 for bits 0 and 1, PRG RAM, not present I imagine? As I said, program RAM isn't something that I understand the idea of too well other than for saving games. And as far as bus conflicts on my board, I'm going to hope that my virtual board has so bus conflicts, so I'd go with 0 on that one too.

11-15- Zero filled. Simple. It looks like in ASM6, I just use dsb 5 to fill that space zeroes.

So if I have figured this correctly, this should be the header I need:

Code:
byte "NES",$1a                  ; basically "NES" plus a terminator
byte $01                                    ; 1x16 PRG-ROM block ($c000)
byte $02                                    ; 2 CHR-ROM blocks
byte $%01000001                      ; vertical mirroring, mapper #004
dsb 9                                         ; 9 bytes padding


So as long as I'm correct I feel like I have most of an understanding on how to build the header, although I am confused on a few things like PRG-RAM and arrangement of CHR banks.

Thanks again for all of the assistance!


Top
 Profile  
 
PostPosted: Sat Aug 08, 2015 2:52 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19342
Location: NE Indiana, USA (NTSC)
darryl.revok wrote:
So 0-3 are ALWAYS "NES",$1a because that's what it expects. That's easy enough to remember. I don't think there's any practical reason why I'd need to understand that better.

4- So, at this point, I have just the basic non-switch PRG ROM, but I feel I need to understand this. My program has $01 as the entry, so what is 00?

The meaning of $00 in this byte is not defined in original iNES format.

darryl.revok wrote:
If I had a total of 64KB of program data between all of my banks, would the answer be 07?

A 65536 byte PRG ROM would be $04, as it is measured in 16384 byte units.

Quote:
Now with my MMC3 individual banks will be smaller than 8KB each, but it looks like I need to add them to my program in 8K chunks.

It's actually stricter than that. Existing MMC3 boards require the size of CHR ROM to be a power of 2: 32768 bytes, 65536 bytes, 131072 bytes, or 262144 bytes.

Quote:
Seems there's no option to do otherwise. So does this mean that I need to create 2 2K blocks for every 4 1K blocks?

Not necessarily. It's fine to have a game that only ever uses two 2K blocks and sixty 1K blocks. This would be a 65536 byte CHR ROM: 64 1K blocks, and $08 in byte 5 of the header.

Quote:
And would I need to order them a certain way so that they would be loaded properly?

Other than that each 2K block starts on a 2K boundary (no straddling, say, $07-$08), order doesn't matter in standard MMC3 boards (mapper 4).

Quote:
could I dedicate an entire 8K chunk to 1K character animation blocks?

Yes.

Quote:
6- Okay this is where it gets trickier. My program just says $00 for now. Okay, so bit zero is for mirroring, I get that. If bit 3 is 1, then it looks like it ignores bit 0.

Correct. In fact, bit 0 never matters for existing MMC3 games, as MMC3 games are supposed to configure mirroring at runtime. Only one MMC3 game was ever released with bit 3 turned on: Rad Racer II.

Quote:
8- Okay, now the PRG-RAM. I have to admit, I am confused on PRG-RAM. That's where you write your data for battery-backed save games, right? Is there any other reason that you'd use it?

The battery-less PRG RAM in a TSROM is just an extra block of RAM that a game can use for any purpose. Super Mario Bros. 3 and M.C. Kids use it to cache the decompressed level maps. Some other games were engineered not to need it, as it's an extra cost option at manufacturing time.

Quote:
[PRG RAM] is completely separate from extra VRAM for 4-screen mirroring isn't it?

Yes. PRG RAM is on the CPU side of the bus, and 4-screen VRAM is on the PPU side.

Quote:
And as far as bus conflicts on my board, I'm going to hope that my virtual board has so bus conflicts, so I'd go with 0 on that one too.

MMC3 never has bus conflicts.

Quote:

So if I have figured this correctly, this should be the header I need:

Code:
byte "NES",$1a                  ; basically "NES" plus a terminator
byte $01                                    ; 1x16 PRG-ROM block ($c000)
byte $02                                    ; 2 CHR-ROM blocks
byte $%01000001                      ; vertical mirroring, mapper #004
dsb 9                                         ; 9 bytes padding

This looks reasonable. It gives you 16384 bytes PRG ROM, 16384 bytes CHR ROM, and the most common MMC3 configuration.


Top
 Profile  
 
PostPosted: Sat Aug 08, 2015 5:36 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
tepples wrote:
darryl.revok wrote:
If I had a total of 64KB of program data between all of my banks, would the answer be 07?

A 65536 byte PRG ROM would be $04, as it is measured in 16384 byte units.


My mistake. I got the CHR and PRG mixed up.

Quote:
It's actually stricter than that. Existing MMC3 boards require the size of CHR ROM to be a power of 2: 32768 bytes, 65536 bytes, 131072 bytes, or 262144 bytes.


Okay, I just noticed something I should have asked. 00 infers 8 KB, so does 01 infer 8 KB or 16 KB? I wrote my header in the last post as if it 00 and 01 would both infer 8 KB,

So does this mean there are only 6 valid options; $00, $01, $04, $08, $10, $20, and $40?

Does this restriction also apply to the PRG ROM? If not, what values does the board allow for that?

That would make the header I wrote of $02 invalid, correct?

Quote:
And would I need to order them a certain way so that they would be loaded properly?

Other than that each 2K block starts on a 2K boundary (no straddling, say, $07-$08), order doesn't matter in standard MMC3 boards (mapper 4).

Gotcha. I need to get past the header before I can break down and understand how the bank #s are assigned to a 2K or 1K slot, but since our only options with this one are 1K blocks or 2K blocks, that could be simplified in a sense to say that a 2K block always needs to be on an even # of kilobytes in ROM space, correct?

Like, say you were at 9K position and you need to add another 2K block, you'd have to add 1K block first?

It would make more sense I can imagine to have all of the 2K and then the 1K, but I could see where somebody might not want to go back and change everything if they already built their program and just need another bank.

Quote:
Correct. In fact, bit 0 never matters for existing MMC3 games, as MMC3 games are supposed to configure mirroring at runtime. Only one MMC3 game was ever released with bit 3 turned on: Rad Racer II.


I can understand why this was rarely used during the NES life because of the expense, but I'm curious how expensive it would be to do this on a cart today, with the drop of RAM prices. Just seems like an under utilized feature.

Also, would this be able to share physical memory with PRG-RAM? Like, could you assign part of the RAM to the CPU and part to the PPU?

Quote:
Super Mario Bros. 3 and M.C. Kids use it to cache the decompressed level maps. Some other games were engineered not to need it, as it's an extra cost option at manufacturing time.


So they just needed to store more information than they had room for in CPU memory space?

Quote:
This looks reasonable. It gives you 16384 bytes PRG ROM, 16384 bytes CHR ROM, and the most common MMC3 configuration.

But invalid if used on extra hardware because of the improper CHR ROM size, correct?


Top
 Profile  
 
PostPosted: Sat Aug 08, 2015 6:12 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1046
Quote:
Okay, I just noticed something I should have asked. 00 infers 8 KB

0 refers to 0 KB. Which means the game is using RAM, not ROM for its graphics.
Quote:
That would make the header I wrote of $02 invalid, correct?

No. 2 * 8 = 16. Which is 2^4, so it's a power of 2.
Quote:
Does this restriction also apply to the PRG ROM? If not, what values does the board allow for that?

Max PRG ROM for standard MMC3 boards is 512 KB.
So 16, 32, 64, 128, 256 and 512 are all valid.
Which by extension means
1, 2, 4, 8, 16, and 32 are all valid numbers for the number of 16KB PRG banks.

Max CHR is 256KB.
1, 2, 4, 8, 16, 32 are all valid for the number of CHR Banks. (And 0, which is CHR RAM)
Quote:
Gotcha. I need to get past the header before I can break down and understand how the bank #s are assigned to a 2K or 1K slot, but since our only options with this one are 1K blocks or 2K blocks, that could be simplified in a sense to say that a 2K block always needs to be on an even # of kilobytes in ROM space, correct?

That's pretty much it. So... 256 values are available. For 1KB switching obviously each one corresponds exactly to that block. For 2KB, every two values gets the same results.

Writing 0 to the 2KB side gives you the first two KB. Writing 1 also gives you the first two KB. 2 gives you the next two KB. etc.
Quote:
Like, say you were at 9K position and you need to add another 2K block, you'd have to add 1K block first?

Right. And your logic about putting all 2KB blocks first is also sound design. What I do is have a define for the 2KB blocks end. So I don't have to change anything in my program even if I add more 2KB banks.
Quote:
So they just needed to store more information than they had room for in CPU memory space?

Or they just wanted it. It's possible to design a game that does similar things to those two without it, I think.
Quote:
But invalid if used on extra hardware because of the improper CHR ROM size, correct?

2 is valid. See above.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Sat Aug 08, 2015 7:17 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19342
Location: NE Indiana, USA (NTSC)
darryl.revok wrote:
Okay, I just noticed something I should have asked. 00 infers 8 KB, so does 01 infer 8 KB or 16 KB? I wrote my header in the last post as if it 00 and 01 would both infer 8 KB,

00 means CHR RAM in the typical size for the mapper, and on most mappers this is 8192 bytes. (It's larger on CPROM, Oeka Kids, and RacerMate mappers.)

$01 means 8192 byte CHR ROM. $02 means 16384 byte CHR ROM.

Quote:
So does this mean there are only 6 valid options; $00, $01, $04, $08, $10, $20, and $40?

You forgot $02. For common mappers, this is true.

Quote:
Does this restriction also apply to the PRG ROM?

For common mappers, this is true. There are only a few uncommon boards that allow non-power-of-2 PRG ROM sizes.

Quote:
That would make the header I wrote of $02 invalid, correct?

2 is a power of 2, so 2 is valid. PRG ROM size = 2 means 32768 bytes. CHR ROM size = 2 means 16384 bytes. Tetris by Tengen and Tetris by Nintendo are examples of games with PRG ROM size = 2 and CHR ROM size = 2.

Quote:
a 2K block always needs to be on an even # of kilobytes in ROM space, correct?

Yes.

Quote:
Like, say you were at 9K position and you need to add another 2K block, you'd have to add 1K block first?

Yes.

Quote:
Also, would this be able to share physical memory with PRG-RAM? Like, could you assign part of the RAM to the CPU and part to the PPU?

That would be expensive to put in an actual game, as one memory controller would have to sit in front of a fast memory serving requests from both buses. Nevertheless, kevtris's forthcoming NES flash adapter has an FPGA acting as the memory controller, going to both buses and a big, fast DRAM. This FPGA does the mappers and the Game Boy emulation.


Top
 Profile  
 
PostPosted: Sun Aug 09, 2015 3:19 pm 
Offline

Joined: Thu Aug 12, 2010 3:43 am
Posts: 1589
tepples wrote:
Even more annoying is when the game uses the Reset button to exit a particular level, as in that X-Men game for Sega Genesis. If you're in that level, it goes to the next level. If you're in any other level, it starts the game over.

For the record, originally you were just supposed to do something in-game but playtesters would keep resetting the console instead, so they changed the solution to that level... (doesn't hide the fact it's bullshit and that they should have modified the instructions instead)

At least it doesn't crash...

tepples wrote:
Then the other problem is in a multicart like Super Mario Bros./Duck Hunt. Should Reset go back to the game select or to the game's own menu? In SMB/DH, it goes back to game select. In Action 52, it goes back to an unskippable cut scene followed by game select. In Action 53, it's a bit inconsistent: NROM games go back to game select, while bigger games go back to the game's title screen and are expected to have "quit" on the menu.

Honestly, I'd expect a multicart to always return to the game select (like it was power on).


Top
 Profile  
 
PostPosted: Tue Aug 11, 2015 3:40 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
tokumaru wrote:
Can you just imagine how things were before the internet? These days it seems like people want everything to be handed to them in a silver platter. Well, back in the 80's and 90's people didn't have video tutorials on YouTube detailing every little thing they had to do and somehow people still managed to learn assembly and make games.


I wanted to get back to this when I got a chance. The last thing I want to do is express disrespect for the people who have gone before me and put this information out to make it even possible to learn. If I can't understand the technical sheets, that's my own fault for not being experienced enough, and it will come with time. The information is priceless to those who wish to develop for the console.

Not everybody that wanted to learn 6502 assembly in the 80s and 90s was able to do so. I say this because making games for the NES or SNES was the biggest thing I wanted to do. I was a kid and I honestly had no idea what was required to do so. I started playing with BASIC because it's what was available to me. It was literally on the computer we had so it was what I had. That was just about the only place I could go. There wasn't anywhere a kid like me in Kentucky could go to speak with fellow nerds and be handed the esoteric knowledge passed down from the Atari generation. I really had no clue.

I could go to the book store and pick up a book on BASIC, but I didn't see a book on NES programming or I would have taken that route instead. There could have been a book sitting right there on 6502 Assembly and I wouldn't have had a clue what it was. Was it my fault for not knowing that? Maybe, but at that age, and during that time, it's hard to imagine how I would have found out. Nintendo Power didn't explicitly mention it, not at least that I can recall. At the time, it may not have been feasible anyway. Sure, the code hasn't changed, but there were no emulators, or CHR convertors, at least most likely that weren't proprietary.

I guess I have a somewhat selfish perspective because what I really want are more new games to play and more people making new games. I think the idea of an obsolete video game console is challenged when new games are being developed for them that can be just as entertaining as ones developed for modern consoles, and the development is actual feasible for the "little guy". Once I'm here, and I've taken the plunge, it's my responsibility to learn and understand the more technical aspects. I'm glad the reference material is available. What I mean to say is that I almost didn't think it was possible, and I bet a lot of people who think they might be interested end up feeling the same way. I don't think what I feel is missing is even necessarily tutorials for the first steps, (not to say that more of those wouldn't be great) just like a gateway for the completely inexperienced person to see, yes, it is possible, no, it's not necessarily easy and it's a lot of work, but there are plenty of tools and techniques, here's what you need to know and consider before deciding if you want to go into this hobby, and here's where to go if you decide to do so. I dunno, I guess there's stuff like that, so I don't really know why I was complaining; maybe I just burnt myself out that day and got frustrated.

Quote:
Retro software development is a niche thing, it's not profitable, so there's obviously not gonna be as much material about it as there is for current technologies. Whatever is out there is the work of hobbyists, who don't always have a lot of free time.

Well if there's ever any way I can give back to the community and spend some time to do something that would help get more people involved, I'd like to do so.

Quote:
...and do things according to the book, with help from people in this forum, things will slowly fall into place as you go.


This is where the forum becomes invaluable. If "the book" are some of the beginner tutorials available, I've learned from some people in here that the practices in those aren't always the best. Still, even getting to a point to understand why something might not be the best way takes some work,


Top
 Profile  
 
PostPosted: Tue Aug 11, 2015 9:29 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
darryl.revok wrote:
If "the book" are some of the beginner tutorials available

I actually meant to say "by the book", which means following the most up to date documentation, which is the wiki. Of course the wiki alone is not very helpful for beginners, but whenever you come across a hardware interaction you're not familiar with when using tutorials or going through other peoples' codes, it's a good idea to check what the wiki has to say about it. If something seems off, just ask about it in the forums, that's what I meant.


Top
 Profile  
 
PostPosted: Mon Sep 14, 2015 9:15 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
Okay, so I've got to the point that I have a a very early functional prototype of my game engine, with SMB3 style four-way scrolling, object collision, background collision, rudimentary player controls and some basic sprite flickering. I'm to the point now that I need to add CHR bank switching for sprites in order to add attack moves for my player, more characters, and move forward. I hoped to come back to this thread to get some tips on implementation.

My program is currently set up to use the MMC3 although I'm not using it's features yet. I'm pretty sure I'll stick with the MMC3 and work within it's capabilities for this game. First I want to do CHR switching but I eventually plan to utilize the PRG switching, scanline interrupt, and mirror mode switching as well for this project.

So, I'd like to create a system that can bank switch each frame of an animation, and that will allow objects to share a bank if they use the same one. I think it's a little more complicated than just that though, because some object types will be cycling through CHR banks and only a few in my game might only use one. So, if an object judged whether or not it can be spawned just based on whether or not one of the current CHR banks was compatible, it would run into issues when the other enemy of the same type does a move requiring CHR switches.

So, if I'm correct, I'm thinking it's going to have to work a little like this:

Each object type will have to have a bit defining the number of CHR banks it uses. Any objects that can fit in one 1 KB bank can share a bank, but more than that, and there's no reason to share, even if they could share some frames. So, 0=1 bank and 1 = >1 banks

I believe I would need two bits somewhere to define the number of CHR banks used currently. This is assuming that one is always used, and the player character is always present when using this logic. To allow otherwise, I suppose I would need three bits to allow values 0-4.

Each object will have a byte defining the bank number that it's using, (0-255). When pulling a frame from metasprite data, it will first pull the bank number of that frame, and compare it to the value in the object's RAM. If that's not the same, run bank loading routine.

If the object type uses 1 bank, it could first check bank numbers of active objects and look for that bank. If not present, then check number of used banks for an open slot, if there are none, deny spawning of object.

If the object type uses more than 1 bank, check number of used banks for an open slot, and if there are none, deny spawning of object.

So, I don't know if that logic could be improved, but it seems like that would be a basic framework for determining if an object can access the proper bank, and preventing an object already on screen from losing access to it's sprite data. Now, there's more to do to actually get the bank switching functional, and I'd like to see if my line of thinking on this is correct:

The bank number will be stored in object RAM, but I believe the bank window being occupied will also have to be stored in object RAM since objects should be able to share windows. So, I'm thinking two bits in object data to define which CHR window that particular object is using. If these bits are stored in bits 6 and 7, I can AND out the rest of the byte and have the actual window address.

My metasprite tile numbers will have to change from $00-$FF to $00-$3F.

Then, my bankswitching code might look something like this:

Code:
ChangeCHRBank:

;A = bank number to switch in

  PHA
 
  LDA objectAnimationBankLocation, x
  AND #%11000000
  ASL
  ROL
  ROL
;  CLC
  ADC #$02
  STA $8000
 
  PLA
  STA $8001
 
RTS


Then, my tile number loading changes from this:

Code:
  DEY
  LDA (currentFrameReadLo), y                        ; = 0-255
  STA (currentSpriteTilePreloadSlotLo), y               ; Store tile number


To this:

Code:
  DEY
  LDA objectAnimationBankLocation, x
  AND #%11000000
  CLC
  ADC (currentFrameReadLo), y                        ; = 0-63
  STA (currentSpriteTilePreloadSlotLo), y               ; Store tile number


So, before I go further I wanted to ask if you guys think this is along the right track so far.


Top
 Profile  
 
PostPosted: Tue Sep 15, 2015 3:14 am 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
Well, I got basic CHR switching for sprites functioning.

I ran into one issue that I didn't anticipate, however. Due to the instantaneous nature of bank swapping, switching banks during game logic resulted in the existing sprites on screen being drawn with incorrect tiles momentarily. At first it looked like a delay in the bank swap, but after a little investigating I figured out what was really happening.

Currently I'm solving the issue by swapping my bank during NMI to the bank number saved in object RAM. Right now it's only set up for the player, but after I add functionality for other objects I'll just have to hope that I've still got the spare cycles in NMI for four sprite bank swaps and two background bank swaps.

Any thoughts on how other people have tackled this issue? Are CHR bank swaps often placed in NMI to avoid complications?


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 132 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: LocalH and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group