Game project help and progress thread

A place where you can keep others updated about your NES-related projects through screenshots, videos or information in general.

Moderator: Moderators

tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Game project help and progress thread

Post by tepples »

Espozo wrote:wouldn't it be possible to change out chrrom graphics for the BG every couple of frames to actually animate the fire without a special mapper?
Like the fire in the first stage of Teenage Mutant Ninja Turtles II: The Arcade Game, which uses plain old MMC3?
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Game project help and progress thread

Post by rainwarrior »

tokumaru wrote:
Yet another 16bit forest fire:
I'm sure there are plenty of examples, this idea isn't exactly the most original idea ever. Characters jumping around in blocky worlds have also been done to death, but we're still doing that, aren't we? :wink:
The original plans for my own in-progress game included a forest fire. ;) Eventually I moved the forest away from the volcano, though as I revised my plans.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Game project help and progress thread

Post by tokumaru »

dougeff wrote:What about DMC music channel IRQs?
They can be used for raster effects to some extent, but it's quite tricky. You'd think that if you started the same sample at the exact same time every time, that an IRQ would fire exactly the same number of cycles later every time, right? You'd be wrong. The APU has its own rhythm and as far as we know there are only 2 ways to use it for raster effects:

1- Have the IRQ fire a safe number of scanlines (a number larger than the maximum error) before a sprite 0 hit or sprite overflow that will be used to properly sync the CPU with the display.

2- Set a preliminary IRQ and measure how long it takes for it to fire (wasting CPU time), so you can measure the error and compensate for it (wasting more CPU time).
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Game project help and progress thread

Post by rainwarrior »

tokumaru wrote:The APU has its own rhythm...
What do you mean by this? I think the APU is very deterministic?

The primary problem with it as an IRQ timer is that it has a very coarse resolution. You can only change the sample length in 128 sample (16 byte) increments, and there are only 16 available samplerates. This makes it rather difficult to choose a sample-length + speed combination that lands where you want it, but once you choose one it should fire in a rather predictable location. (edit: lidnariq explains why this won't work, below.)

Picking an arbitrary scanline would be difficult. (Probably would require a lookup table of sample length + speed + extra CPU wait time?)
Last edited by rainwarrior on Thu Dec 24, 2015 11:07 am, edited 1 time in total.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Game project help and progress thread

Post by lidnariq »

Tokumaru seems to be referring to that the bit phase of the DMC is in a semi-random state when you write to the registers, isn't changed by writing to the registers, and the only way you can know its value it to wait for an APU DMC interrupt.
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Game project help and progress thread

Post by rainwarrior »

Isn't that only true if you're interrupting a running sample? If you let a sample finish (or reach the IRQ) it should be in a predictable state, shouldn't it? If you're using it for a raster split, presumably it's still empty from the previous frame.

You can also just set the sample length to 1, and wait enough cycles for the last byte to drain out, if you really need to.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Game project help and progress thread

Post by lidnariq »

No, the APU always run 8 bits at a time... when there's no data, rather than sitting idle it clocks through 8 bits of "do nothing"
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Game project help and progress thread

Post by tokumaru »

I won't pretend I know a lot about the internals of the APU, but from trying to use DMC IRQs for scanline timing I realized IRQs simply won't fire a constant amount of time after you set them up. They'll vary by several scanlines, and I can only assume this is because the APU has its own pipeline of operation, much like the PPU repeats the whole process of generating video frames over and over. Think about the PPU: if rendering is off and you turn it on, the PPU doesn't immediately start a new frame just because a register was written to... the PPU will finish the current frame, and only then it'll start a new one, because the chip was made that way. I can only assume that something similar happens inside the APU, it does things in a certain order and will only start playing samples at certain times.
rainwarrior wrote:I think the APU is very deterministic?
Oh, I'm sure it's quite deterministic in its own isolated universe, but it runs in parallel with the PPU while not in sync with it in any way. The CPU can sync with the PPU though NMIs or the status register, and it can sync with the DMC channel by waiting for a DMC IRQ to fire, but in order to use APU IRQs for timing raster effects, you kinda have to sync the CPU to both of them, and that's the hard part.
The primary problem with it as an IRQ timer is that it has a very coarse resolution.
I wish! If this was the case I would long ago have assembled a table with parameters (sample length, rate and padding, like you mentioned) for all 240 scanlines and forever forget the NES doesn't have a built-in scanline counter.
lidnariq wrote:Tokumaru seems to be referring to that the bit phase of the DMC is in a semi-random state when you write to the registers, isn't changed by writing to the registers, and the only way you can know its value it to wait for an APU DMC interrupt.
Exactly. You can sync with the APU by setting up a preliminary IRQ, and by counting how much time passes until it fires you can tell how "off" the APU is compared to how you wanted it to be. Once you're synced with the APU, you can then predict when subsequent interrupts will fire, but since those will be relative to the "wrong" time when the first interrupt fired, you have to compensate for this error using the difference you encountered when waiting for that first IRQ, in order to find the correct scanline. I never manged to get this working correctly though... I did get a stable enough split that would jitter by up to 4 tiles (good enough for simple raster effects), but every few seconds it would jump several tiles and I never found out why.
rainwarrior wrote:Isn't that only true if you're interrupting a running sample? If you let a sample finish (or reach the IRQ) it should be in a predictable state, shouldn't it? If you're using it for a raster split, presumably it's still empty from the previous frame.
Again, I wish!
lidnariq wrote:No, the APU always run 8 bits at a time... when there's no data, rather than sitting idle it clocks through 8 bits of "do nothing"
Thanks for clarifying that.
User avatar
Tsutarja
Posts: 123
Joined: Sun Oct 12, 2014 11:06 am
Location: Finland

Re: Game project help and progress thread

Post by Tsutarja »

So many replies :o
For the sprite issue, I believe that the off screen sprites are not moved down to the off-screen area correctly (if I even have remembered to do that). I need to check the code once more.

For the fire effect discussed, I was planning on using the MMC3's scanline IRQs, because its the easiest method. DMC IRQs are out of question, because the DMC channel is used in music. Another thing about the effect is that every other frame the fire will display and every other the regular forest background. The wobble is only active every other frame, and I wasn't planning it begin very intensive anyway. Another thing I need to make sure is that the stage ends on even screen number, so I can disable the status bar IRQ on the frames the wobble happens, making it a bit easier and decreasing the amount of IRQs required for that frame, since the status bar is drawn on the left nametable. The right one will have the forest background and since there is no wobble for that, the status bar IRQ won't cause any problems.
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Game project help and progress thread

Post by rainwarrior »

lidnariq wrote:No, the APU always run 8 bits at a time... when there's no data, rather than sitting idle it clocks through 8 bits of "do nothing"
Ah! I did not know this. Thank you.

Yes, that would explain everything Tokumaru said.

I should think about implementing DPCM start jitter in my NSF player then...
User avatar
Tsutarja
Posts: 123
Joined: Sun Oct 12, 2014 11:06 am
Location: Finland

Re: Game project help and progress thread

Post by Tsutarja »

The sprites are moving to the off screen area now when the sprite goes off screen, though there random is amount of delay for the sprites on the right side of the object, which confuses me :/
It probably has something to do with the OAM clear not working correctly, but since I'm not sure, I added sprite assembler code just in case, since I have modified it.

Sprite Assembler:

Code: Select all

SpriteAssembler:
 LDA oam_full             ; Make sure OAM isn't full
 BEQ ContinueSpriteAssembly
 RTS

ContinueSpriteAssembly:
 LDA oam_offset              ; Find where to store sprites based on offset and pseudo random starting point
 CLC
 ADC oam_start
 TAX

SpriteAssemblyLoop:
 CLC                                     ; Calculate low x byte for sprite's position
 LDA [obj_spr_hi], y
 EOR obj_h_flip
 BPL AddPositiveOffsetX
AddNegativeOffsetX:
 ADC spr_x_lo
 STA $0203, x
 LDA #$FF
 BNE AddHighByteX
AddPositiveOffsetX:
 ADC spr_x_lo
 STA $0203, x
 LDA #$00
AddHighByteX:
 ADC spr_x_hi                                     ; Calculate high x byte for sprite's position
 BEQ XCoordinateReady
 LDA #$F0                           ; Move sprite off screen if high byte is not #$00 (on screen)
 STA $0200, x
 INY
 INY
 INY
 INY
 BNE TestLoopEnd
XCoordinateReady:
 INY

 CLC                                     ; Calculate low y byte for sprite's position
 LDA [obj_spr_hi], y
 EOR obj_v_flip
 BPL AddPositiveOffsetY
AddNegativeOffsetY:
 ADC spr_y_lo
 STA $0200, x
 LDA #$FF
 BNE AddHighByteY
AddPositiveOffsetY:
 ADC spr_y_lo
 STA $0200, x
 LDA #$00
AddHighByteY:
 ADC spr_y_hi                                     ; Calculate high y byte for sprite's position
 BEQ YCoordinateReady
 INY
 INY
 INY
 BNE TestLoopEnd
YCoordinateReady:
 INY

 LDA [obj_spr_hi], y                             ; Get palette and attribute for sprite
 EOR spr_flip_mask
 STA $0202, x
 INY

 LDA [obj_spr_hi], y
 STA $0201, x
 INY

 INX
 INX
 INX
 INX
 CPX oam_start                       ; Test to see if OAM filled up
 BNE TestLoopEnd
 LDA #$01
 STA oam_full

TestLoopEnd:
 DEC obj_sprite_count                       ; Decrement amount of sprites to process for object
 BNE SpriteAssemblyLoop
 STX oam_offset
 RTS
OAM Clear:

Code: Select all

OAMClear:
 LDA oam_offset            ; Clear unused OAM
 CMP oam_start
 BEQ OAMFull
 LDX oam_offset
 LDA #$FF
 STA $0200, x
 INX
 STX oam_offset
 JMP OAMClear
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Game project help and progress thread

Post by tokumaru »

Code: Select all

LDA [obj_spr_hi], y
Shouldn't this be LDA [obj_spr_lo], y?
User avatar
Tsutarja
Posts: 123
Joined: Sun Oct 12, 2014 11:06 am
Location: Finland

Re: Game project help and progress thread

Post by Tsutarja »

tokumaru wrote:

Code: Select all

LDA [obj_spr_hi], y
Shouldn't this be LDA [obj_spr_lo], y?
Yes it should. I had to fiddle around with the byte endianess (?) a bit so they just happened to stay as 'hi' instead of 'lo'. It doesn't affect how the assembler works, since the variables in the list are the other way around.

I will correct there later to make sure I don't get confused when I reuse code.
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Game project help and progress thread

Post by tokumaru »

It looks like the OAM isn't getting cleared when the sprite is offscreen. Apparently it's because of this:

Code: Select all

LDA oam_offset
CMP oam_start
BEQ OAMFull
If the offset is the same as the start you consider the OAM full and don't clear anything, but these values will also be the same if the OAM is empty, which it is when the one object you have is off screen. Since the OAM isn't getting cleared, you get garbage sprites. What you need is some other method to detect when the OAM is actually full.

Alternatively, you can simply hide all sprites before drawing any metasprites. It may be a little wasteful, but is conceptually simpler.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Game project help and progress thread

Post by tepples »

You could initialize the starting index to 4 instead of 0 so that when you do need sprite 0 for something, it's ready for you.
Post Reply