Charas-Project
Off-Topic => Really Old Stuff => Archive => General programming => Topic started by: Kilyle on October 16, 2010, 11:00:48 AM
-
So hey, I've taken up the study of 6502 assembly code, the basis for NES ROMs. A three-second search didn't find 6502 on this forum (maybe I should've been less lazy and tried other keywords too), so here's a topic to cover it, because I'm hoping to get some help.
Through various internet resources I have managed to get a handle on Hex code and Assembly opcodes and how to switch between them; it'll take more study, but my notes are pretty good and I expect no problem there. I've found some info on I/O as far as user input, and I'm ignoring music for the moment. The main thing I'm interested in but having problems understanding is graphics.
My studies have left me with this understanding:
Somewhere in the code, possibly the D000 block, there are two databases for sprites: one holds characters, the other holds background and foreground objects. (So far, this is sounding a lot like RPGMaker, which cheers me.) I have a list of D000-block codes that should allow me to set the graphics themselves, the colors used, and so forth. Then when the program's ready to put them onscreen, I set 8 pointers to 8 sprites (the max char sprites onscreen at any one time) and use associated settings to control their location, visibility, priority over foreground objects, etc.
Things I haven't run across yet, or need clarification on:
- Is the D000 block the correct spot for NES, or is it for a different form of 6502 code (or perhaps another code entirely)? I'm confused because it's part of the larger PRG-ROM upper bank area that I understood was half of my personal playground (where I'd put all my code).
- How do I properly set up a font in 1BPP (one bit per pixel)? The sprite database apparently uses 64 bytes to hold each sprite, but using that much data for each character seems wasteful, so I assume I'm missing something about either graphical compression or the usual way of doing things.
- How do I handle animation? Does the 8-sprite limit mean that (e.g.) if I had a 2-frame animation I could only have 4 sprites onscreen at a time?
- Does the 8-sprite limit apply to duplicate sprites (e.g., I couldn't have 1 guard duplicated a dozen times)?
- How do I go about creating a tiled background? I assume the 8-sprite limit doesn't apply to backgrounds, but I'm not sure where to begin either. Are there implicit routines for bg and fg or will I have to craft my own from scratch?
I think I had more questions on this matter, but they slip my mind. Here's hoping someone with some experience can help me through some of these early hurdles (or at least put my mind at ease regarding whether or not my studies are causing me to mix up different codes.
-
Are you using the NintendoAge tutorials?
If not, I suggest you give them a shot. I don't remember much about it since I gave it up until I can finally get to a quiet location. But I'm sure some of your questions could be answered there.
-
So hey, I've taken up the study of 6502 assembly code, the basis for NES ROMs. A three-second search didn't find 6502 on this forum (maybe I should've been less lazy and tried other keywords too), so here's a topic to cover it, because I'm hoping to get some help.
Through various internet resources I have managed to get a handle on Hex code and Assembly opcodes and how to switch between them; it'll take more study, but my notes are pretty good and I expect no problem there. I've found some info on I/O as far as user input, and I'm ignoring music for the moment. The main thing I'm interested in but having problems understanding is graphics.
My studies have left me with this understanding:
Somewhere in the code, possibly the D000 block, there are two databases for sprites: one holds characters, the other holds background and foreground objects. (So far, this is sounding a lot like RPGMaker, which cheers me.) I have a list of D000-block codes that should allow me to set the graphics themselves, the colors used, and so forth. Then when the program's ready to put them onscreen, I set 8 pointers to 8 sprites (the max char sprites onscreen at any one time) and use associated settings to control their location, visibility, priority over foreground objects, etc.
Things I haven't run across yet, or need clarification on:
- Is the D000 block the correct spot for NES, or is it for a different form of 6502 code (or perhaps another code entirely)? I'm confused because it's part of the larger PRG-ROM upper bank area that I understood was half of my personal playground (where I'd put all my code).
- How do I properly set up a font in 1BPP (one bit per pixel)? The sprite database apparently uses 64 bytes to hold each sprite, but using that much data for each character seems wasteful, so I assume I'm missing something about either graphical compression or the usual way of doing things.
- How do I handle animation? Does the 8-sprite limit mean that (e.g.) if I had a 2-frame animation I could only have 4 sprites onscreen at a time?
- Does the 8-sprite limit apply to duplicate sprites (e.g., I couldn't have 1 guard duplicated a dozen times)?
- How do I go about creating a tiled background? I assume the 8-sprite limit doesn't apply to backgrounds, but I'm not sure where to begin either. Are there implicit routines for bg and fg or will I have to craft my own from scratch?
I think I had more questions on this matter, but they slip my mind. Here's hoping someone with some experience can help me through some of these early hurdles (or at least put my mind at ease regarding whether or not my studies are causing me to mix up different codes.
By sheer coincidence, my senior design project is making a Super Nintendo Emulator; the SNES uses the 16-bit cousin of the 6502 processor, the 65c816. At the very least I'm in the right mind to help.
First of all, if you haven't been to it already, the best place to find docs on coding NES assembly (and rom hacking / development in general) is http://www.romhacking.net/ (http://www.romhacking.net/). There's tons of docs there, especially for the NES.
I'm working mainly off of this document (http://www.romhacking.net/docs/%5B120%5Dnestech.txt), so keep in mind I really am not an expert at this.
- Dunno
- I don't know about this, but using sprites for text seems like a bad idea anyway/ Consider that you can only have 8 on a single scanline (see below) and 64 total on the screen. That's 64 letters if the screen has nothing else. The better option here is to use background tiles for your text. This is why text usually appears in a box in an NES game; because the text is a background tile, it cannot appear "over" the background, so the box is drawn and the text has a background of the same color as the box, making it appear as if the box is over other things. (Don't quote me on that, but that seems like a decent reason to me :P).
- To do animation, you'd simply update the tile# for each sprite when you want it to animate to the next frame. Say you had a sprite of a soldier with three frames: the frames would be three seperate entries in the pattern and name tables in VRAM. Say they were tiles 1, 2, and 3. Your soldier sprite would start at 1, and then when you wanted to switch frames you'd set it to 2, then 3, then back to 1 again.
The 8-sprite limit is a limit on the number of sprites per-scanline, not on-screen. Because animation is simply changing the tile of an existing sprite, you don't "use up" sprites with an animation. What it means is that each scanline can only contain 8 sprite entities. So if you had nine 8x8 sprites that all shared at least one scanline (ie their x values were all within 8 pixels of each other) then one of them would not be drawn. At least that's what I can tell from the docs - Multiple guards being drawn on the screen, though sharing the same tile, would require separate sprites as they have separate x/y and attribute values. But again, the 8-sprite limit is per-scanline, so as long as the guards don't converge on a scanline, you can have up to 64 of them (notwithstanding that your hero needs a sprite as do other things.
- I couldn't easily find a good answer to this. Sorry.
That's the best I can do with the small amount of info I have. I'd be of much better help if you had questions about the 6502 CPU itself (graphics and music and such are not related to the CPU but the NES; the 6502 is just a processor and thus doesn't "know" about whether it's even in an NES or a remote controller).
-
Thanks for the info so far; I'm looking forward to more of it as the discussion moves along.
(ETA: WOW! The NintendoAge tutorials look to be just what I need right now. Awesome!!)
From another forum, I got the answer that the D000 block is for the Commodore 64 6502 code, and that the NES might have them in like the 0300 block or something; I'm waiting on more info (or for my brain to make better sense of the info I have gotten so far).
A lot of the theory, I understand pretty well; it's the specific details that so far elude me. What I'm hoping for is more of a "go to this block and use such-and-such instruction." For example, I got told how to make the NES header (the letters N, E, and S, followed by (don't have my notes with me) 0A, and a couple somethings, and the number of PRG-ROM blocks, and so forth, and then eight filler 00's), so now I feel pretty confident that I could make a header that an emulator would identify as a header.
Similarly, I understand the opcodes enough to start putting them together into routines, like simple math equations. Once I find the correct NES-style input data, I think it won't be difficult to craft "if you press the B button, execute this subroutine" and such.
So I guess what I'm after right now is this:
- The correct location for the character sprites.
- The correct location for the background and foreground sprites.
- Any specifications of things I have to do to make them work.
I imagine I'll figure part of this out as I hack more ROMs with easy (not crazy-compressed) graphics, but then again my understanding is that some of the ROMs didn't follow what I might call "normal NES ROM procedure" and therefore have things all higgledy-piggledy all over the place. (One of my mentors even told me that if I was having trouble understanding why they'd program in what seems an unwieldy manner, I was to assume the programmers had been on drugs in the time.)
Anyway, I have made use of what info I could find at Romhacking.net and several others sites. I joined Oldskool Gaming or something like that two days ago just to get this question answered (before I thought to check here). I've pretty much exhausted the keywords I can think up to search for this stuff on Bing, and I'm never sure of my results, because I can't yet tell the difference between different 6502 codes (which is how I got hung up trying to integrate the Commodore 64 code there).
I'm looking forward to making my own animation routines now (thanks for that info) and we'll see how far I can go with that.
Oh, and your link to that document just sends me to an "Oops! This link appears to be broken" page.
-
Okay, those tutorials did answer a number of the questions I had. They also raised a couple more.
These PPU Ports (in the I/O registers in areas $2000-2007 and $4000-401F)... I guess the NES understands the address and I don't have to do anything special except read them and write to them? In my code, do I just set these areas to 00 or am I supposed to put something else there? Same question with the mirrors in $2008-3FFF. Also, anyone have a link to a simple listing of what each of these ports does and how to use it?
It says the PPU has internal memory for 64 sprites of 8x8 pixels... is the program going to be rewriting this periodically, or does this operation only happen once, or...?
Wait, if the sprites get transfered to the PPU, why do you change their position and stuff back in the $0200 block they were in originally? Is the PPU now watching these areas and keeping track of the changes there??
Several times the tutorials use code with a .db start. Coming from a BASIC background, this looks like a vague memory I have of a command code for "send out these values, in order, when you're called upon" (like a "NextLetterInAlphabet" function with the data "abcdefg" etc. and internal incrementation). I haven't seen this in my studies of Hex codes; is there a Hex code for this operation, or is it a lot more complex in Hex than in Assembly mneumonics?
Similar question about subroutine titles: How do you (in Hex) include stuff like "this subroutine is called LoadSprites"?
The controllers are really set up to return a byte with a single bit 8 times to tell you whether their buttons are pressed or not???? What are the other bits doing that they can't tell you this info with the use of a single read of a single byte?? (I assume there's a reason, but on the face of it it sounds like crazy talk. So I'd like to know.)
-
Again, all info is given without guarantee of correctness. :P
These PPU Ports (in the I/O registers in areas $2000-2007 and $4000-401F)... I guess the NES understands the address and I don't have to do anything special except read them and write to them?
Yes. In the actual physical NES, when the CPU accesses a memory address, it doesn't go straight to the memory. It has to first pass through a mapper, which is a piece of hardware that determines where an address will go. For example, some different machine with a 6502 processor might make the memory addresses $0000-$7FFF go to some RAM, and the addresses $8000-$FFFF go to a hard drive. The mapper is what allows different memory addresses to go to different places.
The PPU ports are called "memory-mapped registers". What this basically means is that the mapper will redirect those specific memory addresses to input ports on the PPU. So when you write and read memory to and from those locations, you're actually communicating with some pins on the PPU chip.
Also, anyone have a link to a simple listing of what each of these ports does and how to use it?
This list seems to have some decent info (http://en.labs.wikimedia.org/wiki/NES_Programming#Hardware_I.2FO_Ports). Searching for things like "nes hardware registers" or "new hardware ports" should help find more info on them.
... PPU internal memory ... is the program going to be rewriting this periodically, or does this operation only happen once, or...?
If the NES is anything like the SNES (which it is), sort've. The program will be rewriting the video memory, but it doesn't have to re-write the entire thing. The VRAM that contains your sprite info is basically the place where "what stuff is on the screen currently" is stored. In order to make your graphics move around on the screen, you'll have to edit the X and Y values for these sprites, as well as which tile is being used by each sprite.
Be clear on this distinction: a sprite here is NOT the graphic being used. A sprite is an object of sorts, that has an X/Y position. A tile is the graphic being used. Each sprite refers to the tile that holds it's sprite data. Thus, when you want to animate or move something on the screen, you edit the sprite. But you don't have to replace all of VRAM every time you do this, you can just edit the X/Y values and tile values and leave the rest.
Wait, if the sprites get transfered to the PPU, why do you change their position and stuff back in the $0200 block they were in originally? Is the PPU now watching these areas and keeping track of the changes there??
I don't know about changing their position in the block they were in originally, but I can guarantee you that the PPU doesn't "watch" memory areas like that. Can you link to where you saw this?
Several times the tutorials use code with a .db start. Coming from a BASIC background, this looks like a vague memory I have of a command code for "send out these values, in order, when you're called upon" (like a "NextLetterInAlphabet" function with the data "abcdefg" etc. and internal incrementation). I haven't seen this in my studies of Hex codes; is there a Hex code for this operation, or is it a lot more complex in Hex than in Assembly mneumonics?
Having done other assembly languages, this sounds like it's just denoting a block of memory in the program that will store either data or code. In x86 assembly, for example, most assemblers let you define a "data" block that holds all your variables, and then a "code" block containing the actual program code.
Note that this is still kind've a guess, but I'm pretty sure it has nothing to do with your BASIC command.
As for how you would achieve that function, you'd probably have stored the data somewhere in memory beforehand. You'd use one of the read operations that uses an indexed form of addressing that adds an index register (X or Y are the usual names for these registers) to the memory address before accessing it. Then you just do the read operation, increment the index register, and repeat; it will read the next byte in the sequence because the address was incremented by 1.
Similar question about subroutine titles: How do you (in Hex) include stuff like "this subroutine is called LoadSprites"?
In hex? You don't. Assemblers have the luxury of subroutines and named jump points because they do the math beforehand to work it out, or they just copy-paste the code of the subroutine to the point where you use it. In the actual hex code (which is called machine code), the best thing you have is something like a "Jump to subroutine" instruction.
Typically, a Jump to Subroutine Function has one argument (IE it has the opcode and is followed by a byte representing the argument) does the following:
- Store the status register on the stack
- Store the current instruction address on the stack
- Add the argument to the current instruction address and store it back
- Begin executing code at the current instruction address
Jump to Subroutine is most often accompanied by a Return from Subroutine instruction, which simply loads the old instruction address off of the stack and continues execution at that address (and replaces the status register with the value stored on the stack as well). Assemblers that use subroutines or labels often use this in lieu of having an actual way to call a subroutine with a name.
The controllers are really set up to return a byte with a single bit 8 times to tell you whether their buttons are pressed or not?Huh? What are the other bits doing that they can't tell you this info with the use of a single read of a single byte?? (I assume there's a reason, but on the face of it it sounds like crazy talk. So I'd like to know.)
Probably some hardware limitation with how they laid out the NES controller or somesuch.