Alrighty... I'd like to get started in the world of NES programming. I've done x86 assembly (yeah, I know it's different), and want to give this a go to see if I can at least do something that would run on Nintendulator.
I'd like to make a basic program that would have a few simple sprites, one that could recognize controller input and do some changes, and possibly display text. Sound and such I'd look into later.
One person recommended CA65. I don't know if any assembler is really considered superior.
So, what should I do and where to start?
I like Loopy's ASM6 as an assembler. To properly use CA65 you have to set it up, which is something I'd always rather not do.
I assume you're familiar with simple programming concepts, so that's not a problem. What you want to do is get a graphics editor (I personally like YY-Chr), and you do want an assembler that's commonly used (ASM6, CA65, or WLA-DX) so people can help you more easily when you have a problem. I use WLA-DX, but I hate trying to set up new things. I usually just use the same setup for every new project.
You'll want to read some of the technical documents, such as Yoshi's NEStech document, and you'll want to learn the 6502. There are plenty of places to learn 6502. It's fairly simple, there aren't that many instructions.
A default NES game without any mappers or anything added on to it is 40k: 8k of graphical data, and 32k of program data. The default setting is known as NROM. So if you're just making simple sprite data, you'll want to set up everything to make an NROM demo.
So yeah, if you read NEStech, and MAYBE GBAGuy's tutorials, you'll be at a good start.
I might sound like a scratched up record here, but learn 6502 ASM & just completely skip learning anything about it's Binary-Coded Decimal mode. Other than the fact that it's been stripped from the NES/Famicom's CPU, to my knowledge very few people use it aside for scorekeeping.
& remember, when you start developing, if you're not using the IRQ interrupt for anything, remember to set it to a RTI instruction. I don't know HOW long that mistake crippled me!
Well, from my reading thus far, I've figured out that # means the value of the number and not a memory location. $ in front means a hex number, % means binary, and no prefix means decimal. I also apparently set up banks for graphics and code I guess... It's a start for now.
I found a bunch of the commands and made up a Notepad++ styler for them, so I got colorful 6502! I'm sure I don't have all though.
The guides I've been looking at state nesasm as the assembler, although looking at the forums, it seems to be unacceptable to use that.
Some of the commands look specific to nesasm, and not to other assemblers... like:
.inesprg 1
.inesmap 0
.... etc.
Anyway, I'll keep looking things up. It'll be great when I can compile something with no errors.
You can start off with nesasm if you want, while you wait for
somebody to set up you the CA65 config files.
I got something to compile with nesasm. A simple sprite that moves around. It's a start!
You can learn a lot from a simple program like that. You seem to catch on quickly. At least much more than I did. But I didn't have any programming experience when I started.
Well, I've been fiddling some more.
I've gotten the controller mappings into a subroutine now and it can return values for the various buttons.
One thing that's really weird is that the sprite moves down and right faster than it moves up and left, even though I'm just changing each value by 1 pixel at a time... I also like how going off screen to the right brings it back in on the left!
I'm trying to write some code to have it so that you need to release a button before it can function again.
I'd also like to get into background graphics, though that's a little fuzzy.
Your sprite shouldn't be going up/down at a different rate. There may be something wrong with the way you are checking things...
Doing what you said about the controller is quite simple. You just need to keep track of the button status somewhere in RAM.
I'm thinking it has something to do with the Vblank checking. I have read that I should (need to???) check for 2 Vblanks and I wasn't sure if the demo code was any good.
I'm not sure if that could be it or what. Also, I tried adding some values to the palette to change colors and when I add 1, it adds 2 instead...
the vblank checked twice thing should only be used in your init code, before anything else. This gives time for caps to fill, etc if I remember correctly.
Remember to use CLC before ADC and SEC before SBC if you want them to behave like ADD/SUB.
Ok. I've done well so far. I've gotten sprites to move, palettes to be modified, though I've hit a jam.
I want to load in the BG data. Now, I can get some of it, but I need an index that'll allow me to go to $ffff, but that's obviously not possible.
Code:
GetBG:
LDA #$20
STA $2006
STA $2006
LDX #$00
loadBG1:
LDY #$00
loadBG2:
LDA titledata, Y ;Too bad we can't do X, Y!!!!
INY
sta $2007
CPY #$FF
BNE loadBG2
INX
CPX #$03
BNE loadBG1
RTS
That's what I'm doing. Someone said to use indirect addressing??? I am bad with pointers.
I do have another question: If I ever want to modify the BG in-game, do I need to do another full write to $2007?
You'll have to use a pointer. Here is the canonical way to copy 1 KiB of data to a port:
Code:
PPUDATA = $2007
.proc loadNT
; Put a pointer to title screen data in zero page locations 0-1
lda #<titledata
sta 0
lda #>titledata
sta 1
; Loop through four 256-byte "pages", using register Y to keep
; track of where we are in the page and register X to keep track
; of how many pages remain to be copied.
ldx #4
ldy #0
loop:
lda (0),y
sta PPUDATA
iny
bne loop
; If y = 0, we've finished a page, so increment the high byte of the pointer.
inc 1
; Count how many pages remain
dex
bne loop
rts
.endproc
I wrote this macro a while back to copy data onto a portion of a nametable using nesasm (I'm using $20-$24 as temporaries):
Code:
copy_nametable_data .macro
lda #(\1 / 256)
sta <$21
lda #(\1 % 256)
sta <$20
lda #\4
sta <$22
lda #(\2 % 256)
sta <$23
lda #(\2 / 256)
sta <$24
.y\@:
lda <$21
sta $2006
lda <$20
sta $2006
ldx #\3
ldy #0
.x\@:
lda [$23],y
sta $2007
iny
dex
bne .x\@
dec <$22
beq .z\@
lda <$20
clc
adc #$20
sta <$20
lda <$21
adc #0
sta <$21
lda <$23
clc
adc #\3
sta <$23
lda <$24
adc #0
sta <$24
jmp .y\@
.z\@:
.endm
Then you can write stuff like
copy_nametable_data $214A,my_nt_data,12,10
to set a block of 12x10 tiles at the center of nametable 0.
Sivak wrote:
If I ever want to modify the BG in-game, do I need to do another full write to $2007?
If your game is "screen-by-screen" (like Zelda), yeah, this would be the easiest approach. But in a game that scrolls, you typically just update rows and/or (depending on the type of scrolling) columns of tiles, with the contents that are just entering the viewing area.
The NES has a drawing area of 512x480 pixels, but half of that is replicated from the other half (because the built-in memory can only hold that much), and you get to choose if this mirroring is vertical or horizontal, so the drawing area is actually 512x240 pixels or 256x480 pixels. By using the scrolling registers you can have the viewing area start anywhere in this space, so games just slide the viewing area over the name tables, updating rows and columns of tiles as necessary.
Updating tiles is the easy part... handling the attribute tables is the most frustrating part, specially when scrolling in both directions.
I'm not using any scrolling elements (yet), but for this first project I'm going to avoid that. I'll look more into that maybe for a second project.
So if I wanted to just, say, change some text in the BG after the player presses a button, could I just place a certain string of hex into the memory location $2007 + X where X is the spot the string starts?
I'm also having some issues getting the BG attribute table setup... Although I'll have to look into that later. Right now I tried just writing some stuff into $23C0 to see if the colors would change, but they didn't... Must be a trick to it! Heh.
Thanks for all the help so far. If I can manipulate the BG and understand how to do that, I can probably get a full-blown project going. Audio will come later...
The 2 attribute bits select which out of the 4 palettes that a tile is using. You don't have 2 bits for every tile though, they are shared on a 2x2 tile basis. So each attribute table byte contains attributes for 4x4 tiles (2x2 + 2x2 + 2x2 + 2x2).
Sivak wrote:
So if I wanted to just, say, change some text in the BG after the player presses a button, could I just place a certain string of hex into the memory location $2007 + X where X is the spot the string starts?
No, this is the whole point of $2006. You must point to a certain location in the PPU. If you did the whole $2007,x thing, you'd end up with things you really don't want. Say X was $10. You'd end up writing to $2017.
To point to a tile with $2006, you must write to it with the high byte of the Name Table address, and then write to it again with the low byte. So this:
lda #$21
sta $2006
lda #$C9
sta $2006
Is telling the PPU that when I write something to $2007, it will show up at location $21C9. Depending on the status of a certain bit in $2000, the PPU will either move on to the tile right next to it, or the one right below it.
Yeah, PPU memory is not directly accessible. It's always accessed by 2 registers, mapped to the memory locations $2006 and $2007. The first is the address register. Whenever you want to write something to the PPU, you must tell it where you want to write it, and you do that by writing the address to $2006. $2007 is the data register. After the address is set, whatever is written to $2007 will go to the address you specified, and the address will auto-increment, by 1 or 32 depending on how the PPU is set.
The increments of 32 are useful for writing columns of tiles, since the name tables are 32 tiles wide, adding 32 to the address makes the PPU point to the tile below the one that was just written.
So, to calculate the starting position of a string the correct formula is: NTBase + (Y * 32) + X
"NTBase" is the base address of the name table you are using. In the case of the first name table, it's $2000. X and Y are the tile coordinates (0 to 31 for X, 0 to 29 for Y). If you write the result of this calculation to $2006 (high byte first), you can then just write the string to $2007, one character at a time. Remember to set the PPU to auto-increment by 1 first.
Remember that you can only use $2006 and $2007 when rendering is disabled. So, if you want to update something as the program runs, be sure to do it during VBlank (inside your NMI routine). Be careful not to spend much time on updates, or you might go beyond VBlank time and screw the display up.
As for the attributes not working... well, first make sure that you have set different background palettes. If the palettes are not different, it doesn't matter which one is used and the screen will look the same. Also make sure that the are you are trying to modify contains non-transparent tiles. If a tile is transparent, what you get is color 0, which is the same across all palettes. Last, make sure to understand what bytes affect what areas of the screen, so that you know exactly what address to manipulate.
Well, I've had luck getting the attributes to work, so I'm good there. Pretty easy stuff with that. The only thing I can't seem to do now is change the BG after it's loaded to something different...
I tried calling the subroutine that draws the whole BG again, but it doesn't seem to like that. I also tried turning off the PPU, doing that, and turning it back on with no luck...
On another note, I did finish a program that was for test purposes. Basically, it's my own palette tester showing colors from $00 to $0C and their +$10, $20, and $30 counterparts. $0D-$0F are just black colors. Plus I heard $0D is bad for TV's.
http://www.nintendoage.com/_usermedia// ... ster%2Ezip
That's pretty good! The first demos I made were sloppily put together, and were really dumb. I like that actually. You should try making one that displays all the colors at once. I actually could use a test ROM like that... Maybe I'll try making one.
Thanks for the comments.
Celius wrote:
You should try making one that displays all the colors at once. I actually could use a test ROM like that... Maybe I'll try making one.
There's one on the front page of this site, although I still wanted to do my own to see if I could do it.
Hey again. I actually have gotten background mods working, and I'm now taking a different approach to loading them.
Basically, instead of using a file with everything, I break it down into smaller parts. For instance, if I have a bunch of blank space, I just use a loop and write $00 to $2007 a bunch of times. This way I think takes less space too rather than having all that data and a whole bunch of $00 in a list.
Anyway, I have two questions:
1. Is this a good method?
2. Should my BG writing start at $2000 or $2020? Some guides seem to say either or. I'm using $2020 right now and ending at... $23C0
Basically, I have read that $3C0 of tile data gets written and one should start at $2020 since $2000-$2020 is never seen.
I'm getting fine results with this, but I want to know if it's okay. Thanks.
Start at $2000. The top row is seen on some displays and if you ever scroll the screen around.
Sivak wrote:
1. Is this a good method?
You described RLE (run-length encoding), which is good. It can easily save space on any amount more than 3 repeated bytes.
Quote:
2. Should my BG writing start at $2000 or $2020? Some guides seem to say either or. I'm using $2020 right now and ending at... $23C0
If it won't be displayed on NTSC TVs (and a lot of emulator default settings) it won't matter, but I would say to just avoid putting garbage in there at least.
I put stuff up there no matter what. My first attempt at a Final Fantasy style map engine had me putting stuff starting at $2020 and ending before the last row. It was a total disaster. On my NTSC TV, you can just barely see the middle of the top row (I can see the updates on FF1 if I look at the very top of the screen). So I would definitely start at $2000.
Writing $00 to the screen is good to do at your reset routine, because if someone does a soft reset while playing your game, you'll most likely want to have a clear screen before you do anything to it.
I'd say you should draw the full name table. Just because most TV's will not show those lines this is no reason to leave crap there. If, during a game, you don't want to write anything there in order to save time, at least clear them at the start and never touch those tiles again. At least you will be sure they are blank.
Celius wrote:
Writing $00 to the screen is good to do at your reset routine, because if someone does a soft reset while playing your game, you'll most likely want to have a clear screen before you do anything to it.
Personal opinion: I never clear blocks of memory, I'd rather write clean code that does not assume values in RAM that was not initialized. And I initialize EVERYTHING before using. In a complex program, it's hard to be sure what was in the name tables before, so you'd better clear it all when redrawing it, and not risk displaying crap by accident.