Hello World

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

You mean how the wall overlaps the characters? The best way to do that on the NES would be to use mask sprites... sprites with the same shape as the wall, with higher priority (lower OAM index) than the character sprites and with the "behind background" bit set.

When your character sprites and the mask sprites overlap, the PPU will first check which sprite has higher priority, and since the mask sprites appear first in the OAM they will win. Then, the PPU will try to draw the mask, but since it's configured to be behind the background, the background is displayed instead.

The only problem with this technique is that the sprite count raises quite quickly. You obviously wouldn't keep the masks in place at all times, only when necessary, but even then the limit of 8 sprites is reached fairly easily, so there will be some flickering.
I understand. That method wouldn't require having more tiles either, since the top of the top of the character and the bottom of the wall would already be present. But can't colors only be distributed per 16x16 blocks? That'd mean either the character or the wall would have to change its palette to the other's, which would look bad. I wouldn't mind the sprites per line limit so much, since one can tell fceux to ignore that anyways. What's the limit on sprites per screen though?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero wrote:I understand. That method wouldn't require having more tiles either, since the top of the top of the character and the bottom of the wall would already be present.
There's no need for any extra sprites for the player, but the mask must have the same shape of the wall. If the wall tiles themselves can't be used as a mask (it will happen depending on your use of color 0), you will need extra graphics.
But can't colors only be distributed per 16x16 blocks? That'd mean either the character or the wall would have to change its palette to the other's, which would look bad.
This is irrelevant. The background will be drawn as normal. The player will use its normal palette(s) also, and it doesn't matter what palette is used for the mask, as it will be hidden behind the background and will not be visible at all.

Maybe you didn't understand how this works exactly, so I'll try to explain a little better. Here's the background and sprite in place:

Image

The player is in front of the background, which you don't want. The easiest solution, if that grass is drawn exclusively with color 0, is to set the "behind the background" bit of the character's sprites, so he'll show up in front of the grass but behind the wall. That's hardly the case though, because grass often isn't flat, and has pixels of other colors on it. Also, you might want to apply the effect in some other areas where the floor is not green at all. The solution that works for all of these cases, is a mask:

Image

The black sprite is a mask (I only used black for the example, the actual color doesn't matter, it will not be seen), with the exact shape of the wall (this is important!). It is successfully hiding the character, because it has higher priority, so it's drawn on top. The only thing left to do is set the "behind" bit of the mask's sprites, which will cause the PPU to bring the background pixels to the front, achieving the desired result:

Image

I hope you can see that there's no issue with palettes whatsoever. The only thing that makes this technique complex is that you will probably need some logic to detect when the player is "touching" the wall in order to put the mask(s) in place only when necessary, to minimize sprite use.
I wouldn't mind the sprites per line limit so much, since one can tell fceux to ignore that anyways.
But then you are not making NES programs anymore, but FCEUX programs, don't you agree? If you are going to deliberately ignore a system limitation because an inaccurate copy of it doesn't have the same limitation you could just as well throw all the limitations out the window and making a PC game instead. save yourself the trouble of learning how to code for an ancient machine.
What's the limit on sprites per screen though?
64 sprites per screen, because that's how many entries fit in OAM. You can have more if you interrupt rendering mid-frame for 5 scanlines or so to perform more sprite DMAs. Few games ever did that, because unless you have a reason to split your screen (like, there are 2 gameplay windows) you wouldn't want blank scanlines in the middle of the screen.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

[tokumaru's text]
Oh, you're describing the same thing that SMB3 does for pipes. I'm not sure why I wasn't able to realize that one could use that for walls too...
But then you are not making NES programs anymore, but FCEUX programs, don't you agree? If you are going to deliberately ignore a system limitation because an inaccurate copy of it doesn't have the same limitation you could just as well throw all the limitations out the window and making a PC game instead. save yourself the trouble of learning how to code for an ancient machine.
I suppose. Thinking about it further, I realize it wouldn't actually add any more sprites per line though, because the PPU would never get to drawing the one's with the lower priority.
64 sprites per screen, because that's how many entries fit in OAM. You can have more if you interrupt rendering mid-frame for 5 scanlines or so to perform more sprite DMAs. Few games ever did that, because unless you have a reason to split your screen (like, there are 2 gameplay windows) you wouldn't want blank scanlines in the middle of the screen.
I see.

-----

Another question:
1) DW3 and DW4 had a day/night cycle, and the people available to talk/interact with, and sometimes even the town itself, changed according to the time. How is this done? I assume every time one enters a time, the time is checked, and then the appropriate people (and there sprites) are added? But what about the town itself? Must a whole different map be stored in rom?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

The basics of day/night can be done by screwing with the palette and/or the tint bits. Look at the difference in SMB1 between 1-1 and 3-1 for a crude idea of how to do this.

Zelda: ALTTP had a light and dark world with mostly the same outdoor map, and there was some sort of differential coding: objects in both worlds, objects in light world only, and objects in dark world only.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero wrote:1) DW3 and DW4 had a day/night cycle, and the people available to talk/interact with, and sometimes even the town itself, changed according to the time. How is this done?
Once the time is entered, you have the responsibility of updating it. If you have NMIs enabled, like you should, you can update the time 60 times per second (NTSC).
I assume every time one enters a time, the time is checked, and then the appropriate people (and there sprites) are added? But what about the town itself? Must a whole different map be stored in rom?
You should check the time every frame, to decide whether it's time to change to day or night. Usually, modifying just the palette is enough to switch between day and night, but if you really need more extensive changes, nothing prevents you from changing the pattern tables or even the map.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

The problem with basing a game around a day/night cycle is that no NES mapper has a real-time clock. So when you save the game, turn off the power, and turn the power back on, the program has no idea how much time elapsed while the power was off. It's one of the reasons why the Animal Crossing concept never occurred to Nintendo developers during the NES era. (Here are the others.)
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

The basics of day/night can be done by screwing with the palette and/or the tint bits. Look at the difference in SMB1 between 1-1 and 3-1 for a crude idea of how to do this.

Zelda: ALTTP had a light and dark world with mostly the same outdoor map, and there was some sort of differential coding: objects in both worlds, objects in light world only, and objects in dark world only.
I've already watched the PPU of DW3 when it changes time (I left the PPU window open while playing through most of the game, actually.), and have seen how palette changes do it.
The problem with basing a game around a day/night cycle is that no NES mapper has a real-time clock. So when you save the game, turn off the power, and turn the power back on, the program has no idea how much time elapsed while the power was off. It's one of the reasons why the Animal Crossing concept never occurred to Nintendo developers during the NES era. (Here are the others.)
It's not a realtime clock though; Instead, taking a step advances the time (maybe; sometimes it doesn't). Standing in place doesn't. I think it works well enough for a game like DW3.
Zelda: ALTTP had a light and dark world with mostly the same outdoor map, and there was some sort of differential coding: objects in both worlds, objects in light world only, and objects in dark world only.
But did they store 2 different versions of the map? Or just the original and where the second deviated from it?
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

So, I've mustered the strength and moral courage to try and do this again. I have some free time before university begins again in the fall.

* * *

So, here's a new round of questions:

1) The registers are only a byte large, right? How does one do/simulate math with 2 or more bytes?

2) What happens if you use JSR when the stack's empty?

3) What happens if you use JSR when the stack's already full?

4) What's the point of SEI and CLI? Are they used when games are paused and unpaused? How does BRK fit into this?

* * *

I'll post the code I'm trying to get to work soon.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

FinalZero wrote:1) The registers are only a byte large, right? How does one do/simulate math with 2 or more bytes?
With the carry flag.

Code: Select all

  clc
  lda a_lo
  adc b_lo
  sta result_lo
  lda a_hi
  adc b_hi
  sta result_hi
2) What happens if you use JSR when the stack's empty?
The return address (minus one) is pushed, and the new address is loaded into the program counter.
3) What happens if you use JSR when the stack's already full?
The same, except that the stack pointer wraps around within the $0100-$01FF page.
4) What's the point of SEI and CLI? Are they used when games are paused and unpaused?
Pause is a game state controlled by one of the game's variables. Each frame, the game moves the game pieces only if the game is not paused. It isn't necessarily related to anything in the hardware. SEI and CLI are used to change the CPU's interrupt priority level (CLI: IRQ and NMI; SEI: NMI only). This sort of interrupt is more connected with mappers.
How does BRK fit into this?
BRK always performs a syscall to the IRQ vector, regardless of the CPU's interrupt priority level.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

The return address (minus one) is pushed, and the new address is loaded into the program counter.
I made a mistake. I meant RTS, not JSR...
The same, except that the stack pointer wraps around within the $0100-$01FF page.
That sounds bad.

* * *

Thank you for you timely response.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

FinalZero wrote:I meant RTS, not JSR...
RTS on an empty stack pulls 16 bits of undefined data from the stack pointer (which again wraps around), adds one, and jumps there. I guess they didn't have enough transistors back in the late 1970s when the 6502 was designed to raise an interrupt on an attempt to wrap the stack.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

A couple more questions, this time about cc65.

1)

Code: Select all

#pragma charmap('\0', 0xFF)
is illegal, so C doesn't barf. I suppose there's no way to tell C to terminate strings with something other than '\0' (0x00), is there? I realize that it isn't that big of an issue anyways; Just move your font around so 0x00 is null, instead of something else.

2) Why is

Code: Select all

#pragma charmap('0', 0xF0)
illegal though? Why does it think '0' is 0x00? Isn't it 0x30?

3) Is there a way to include a ca65 file in cc65 code? How about a binary?
Shiru
Posts: 1161
Joined: Sat Jan 23, 2010 11:41 pm

Post by Shiru »

cc65 itself generates an assembly file, so there is no problem to add an assembly file. Just assemble it and then give the linker object file. You can include binary files into an assembly file as usual.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

I realized the problem with 2) in my previous post. My code actually had:

Code: Select all

#pragma charmap('0', 0x00)
It was complaining about the 0x00. Apparently, one can't map anything from NOR to 0.
cc65 itself generates an assembly file, so there is no problem to add an assembly file. Just assemble it and then give the linker object file. You can include binary files into an assembly file as usual.
But what if I have code that depends on the assembly file? I mean, I have an asm file that has ".include "something.s"", and it uses variables/locations declared/defined there.
Shiru
Posts: 1161
Joined: Sat Jan 23, 2010 11:41 pm

Post by Shiru »

I don't understand your problem from this explaination. There is .import and .export to pass symbols between modules.
Post Reply