Like the fire in the first stage of Teenage Mutant Ninja Turtles II: The Arcade Game, which uses plain old MMC3?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?
Game project help and progress thread
Moderator: Moderators
Re: Game project help and progress thread
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Game project help and progress thread
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.tokumaru wrote: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?Yet another 16bit forest fire:
Re: Game project help and progress thread
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:dougeff wrote:What about DMC music channel IRQs?
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).
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Game project help and progress thread
What do you mean by this? I think the APU is very deterministic?tokumaru wrote:The APU has its own rhythm...
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.
Re: Game project help and progress thread
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.
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Game project help and progress thread
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.
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.
Re: Game project help and progress thread
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"
Re: Game project help and progress thread
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.
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.rainwarrior wrote:I think the APU is very deterministic?
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.The primary problem with it as an IRQ timer is that it has a very coarse resolution.
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.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.
Again, I wish!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.
Thanks for clarifying that.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"
Re: Game project help and progress thread
So many replies
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.
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
L R L R STOP & DASH & UP & TALK Ijou nashi
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Game project help and progress thread
Ah! I did not know this. Thank you.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"
Yes, that would explain everything Tokumaru said.
I should think about implementing DPCM start jitter in my NSF player then...
Re: Game project help and progress thread
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:
OAM Clear:
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
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
L R L R STOP & DASH & UP & TALK Ijou nashi
Re: Game project help and progress thread
Code: Select all
LDA [obj_spr_hi], y
Re: Game project help and progress thread
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.tokumaru wrote:Shouldn't this be LDA [obj_spr_lo], y?Code: Select all
LDA [obj_spr_hi], y
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
L R L R STOP & DASH & UP & TALK Ijou nashi
Re: Game project help and progress thread
It looks like the OAM isn't getting cleared when the sprite is offscreen. Apparently it's because of this:
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.
Code: Select all
LDA oam_offset
CMP oam_start
BEQ OAMFull
Alternatively, you can simply hide all sprites before drawing any metasprites. It may be a little wasteful, but is conceptually simpler.
Re: Game project help and progress thread
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.