Game stuck at credits, except during soft reset
Moderator: Moderators
Game stuck at credits, except during soft reset
Today, I tried out the cartridge of my game and I saw that the game gets suck during the credits screen and you cannot advance.
If you press Reset or if you press Power in a very short amount of time, it works.
The game does use sprite 0 split and nine sprites overflow split, even in the credits screen where it is not necessary, but I didn't want to change the logic for it.
However, I'm not sure why this works after a reset, but not during power on.
Can this have anything to do with the PPU not being ready yet or something like this? In a way that the background is already enabled, but sprites are not (despite me enabling both at the same time) and the game goes into an infinity loop waiting for sprite 0?
None of the popular emulators, fceux, Nestopia and Nintendulator, shows this behavior with the ROM. And it was fine when I last checked it with a PowerPak.
If there's no known way how I could reproduce the error on an emulator, does anybody on this forum have the means to test the game on an actual cartridge?
PowerPak or Everdrive would not suffice. It has to be an actual standalone 32 KB NROM cartridge with vertical mirroring.
If you press Reset or if you press Power in a very short amount of time, it works.
The game does use sprite 0 split and nine sprites overflow split, even in the credits screen where it is not necessary, but I didn't want to change the logic for it.
However, I'm not sure why this works after a reset, but not during power on.
Can this have anything to do with the PPU not being ready yet or something like this? In a way that the background is already enabled, but sprites are not (despite me enabling both at the same time) and the game goes into an infinity loop waiting for sprite 0?
None of the popular emulators, fceux, Nestopia and Nintendulator, shows this behavior with the ROM. And it was fine when I last checked it with a PowerPak.
If there's no known way how I could reproduce the error on an emulator, does anybody on this forum have the means to test the game on an actual cartridge?
PowerPak or Everdrive would not suffice. It has to be an actual standalone 32 KB NROM cartridge with vertical mirroring.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
Is there any memory or PPU/APU register that you didn't set to a known value in your powerup routine?
Re: Game stuck at credits, except during soft reset
I don't know. Actually, I did all the stuff that needs to be done, according to the tutorials and the wiki.
Maybe not enough wait for vblank?
However, here's the (hopefully) relevant code:
Maybe not enough wait for vblank?
However, here's the (hopefully) relevant code:
Code: Select all
.segment "CODE"
; Wait for vblank is needed more than once, so we declare it as a macro.
.macro WAIT_FOR_VBLANK
.local @waitForVBlank
; Check the vblank flag to be sure that we wait for a vertical blank.
BIT PpuStatus
@waitForVBlank:
; The vblank flag is cleared and we wait while the resulting value is positive.
BIT PpuStatus
BPL @waitForVBlank
.endmacro
; There are several PPU statuses that we need to wait for,
; so we declare it as a macro with a parameter.
.macro WAIT_FOR_PPU_STATUS value
.local @waitForNotPpuStatus, @waitForPpuStatus
; Wait while the PPU status hasn't happened.
@waitForNotPpuStatus:
; Checks if the status has happened.
LDA PpuStatus
AND #value
BNE @waitForNotPpuStatus
; Wait until the status has happened.
@waitForPpuStatus:
; Checks if the status has happened.
LDA PpuStatus
AND #value
BEQ @waitForPpuStatus
; The status has happened now.
.endmacro
; Due to parallax scrolling, we set the scrolling position more than once.
.macro SET_SCROLLING_POSITION scrollingPosition, nameTable
; The horizontal scrolling position is set while vertical scrolling is always 0.
LDA scrollingPosition
STA PpuScroll
LDA #0
STA PpuScroll
; We set the name table by taking the horizontal PPU control register value
; and OR-connect it with the currently set name table value.
; In the PPU control register, the name tables are set at bit 1 and 0, so the OR works.
LDA #PpuCtrlHorizontal
ORA nameTable
STA PpuCtrl
.endmacro
; The start of the reset function.
Reset:
; At first, general system initialization has to be done.
; Disable interrupt requests (IRQs).
SEI
; Disable decimal mode since it isn't available on the NES anyway.
CLD
; Disable APU frame IRQ.
LDX #$40
STX ApuFrameCounter
; Set up the stack.
LDX #$FF
TXS
; Set X to 0.
INX
; Disable NMI, rendering and DMC IRQs.
STX PpuCtrl
STX PpuMask
STX DmcFreq
; The PPU needs two waits for vblank to be ready.
; This is the first one.
WAIT_FOR_VBLANK
; In the meantime, set the whole RAM memory (i.e. all variables) to zero.
; The RAM goes from address $0000 to $07FF.
; The current address value goes into a two bytes pointer.
; The values are set in two loops.
; X is still 0.
; The X and Y registers are used as counters for the loops and initialized with 0.
TXA
TAY
; A, X and Y all have the value 0 now.
; Pointer always remains 0.
; The address is calculated by Pointer + 1 and Y.
STA Pointer
STA Pointer + 1
@initializeRamOuterLoop:
@initializeRamInnerLoop:
; There is a certain RAM area that shall not be initialized with zeroes
; because this area shall be persistent when the Reset button is pressed.
; We check if we reached that RAM area.
; The high byte of the address is checked.
LDA Pointer + 1
CMP #>(__NO_RESET_LOAD__)
BNE @noNoResetSkip
; The low byte is checked.
CPY #<(__NO_RESET_LOAD__)
BNE @noNoResetSkip
; If we reached the reset-persistent area,
; we change the address to the first value after the area.
; This way, the reset-persistent area doesn't get changed.
LDY #<(__NO_RESET_LOAD__ + __NO_RESET_SIZE__)
LDA #>(__NO_RESET_LOAD__ + __NO_RESET_SIZE__)
STA Pointer + 1
@noNoResetSkip:
; Set the value of 0 to the current address.
LDA #0
STA (Pointer), Y
; Increment the low byte part of the address.
INY
; If it is set back to zero, increment the high byte part
; and, of course, the counter.
; Otherwise, continue with the inner loop.
BNE @initializeRamInnerLoop
INC Pointer + 1
INX
; The outer loop ends just before address $0800.
CPX #$08
BNE @initializeRamOuterLoop
; The RAM has now been initialized with all zeroes.
; The second wait for vblank.
WAIT_FOR_VBLANK
; From now on, the system is properly initialized.
; We want to check whether the console is NTSC or PAL.
; This is needed only for the music.
; The gameplay isn't adjusted to this value.
; We set counters.
LDX #52
LDY #24
@ntscPalLoop:
DEX
BNE @ntscPalLoop
DEY
BNE @ntscPalLoop
; Now we AND-combine PpuStatus with $80.
; This tells us whether we have NTSC or PAL.
; 0 means the console is PAL, non-zero means it's NTSC.
LDA PpuStatus
AND #$80
; The FamiTone sound library gets initialized.
; The value in A is necessary for setting NTSC or PAL.
JSR InitializeSound
; The argument stack pointer for CC65 is initialized.
; From now on, we can call functions written in C.
LDA #<(__STACK_START__ + __STACK_SIZE__)
STA sp
LDA #>(__STACK_START__ + __STACK_SIZE__)
STA sp + 1
; The reset-persistent data is checked and set to an initial value
; if the console was started with the Power button.
JSR InitializeNoResetData
; General initializations of graphics, game logic and everything else.
JSR Initialize
; The first thing that is supposed to run is the NMI.
; Therefore, we set the WaitForNmi variable to true.
LDA #true
STA WaitForNmi
; The PPU control register is set with its horizontal value.
; This means the NMI is enabled now.
; From now on, NMI should never be switched off again.
; The various bits in the value can change,
; but NMI itself should always be on.
LDA #PpuCtrlHorizontal
STA PpuCtrl
; The general game logic.
@gameLogic:
; When the program is waiting for the NMI to hit
; after the game logic has run already,
; then the game logic is not allowed to run again and is skipped.
; Only when NMI is done is the game logic actually entered.
LDA WaitForNmi
BNE @gameLogicEnd
; Next, the buttons of the controller are read.
; Before we read the controller input, we save the status from the previous frame.
LDA ControllerInput
STA PreviousControllerInput
; Latch the buttons of the controller.
LDA #1
STA ControllerPort
LDA #0
STA ControllerPort
; X is the counter for each button.
LDX #8
@readControllerLoop:
PHA
; Each button is read.
LDA ControllerPort
; The lowest two bytes are masked off.
AND #%00000011
; Compare to 1 which sets the carry flag
; to check if either or both bits are set.
CMP #1
; Rotate the carry flag into the top of A.
PLA
; Shift all other buttons to the right.
ROR A
; There are eight buttons to read for the controller.
; The counter is decremented until the loop is over.
DEX
BNE @readControllerLoop
; The controller input is saved.
STA ControllerInput
; Controller reading has ended.
; The next frame is calculated based on the current frame and the controller input.
; Due to the parallax scrolling, we have three frame processing functions.
; This is the first one.
; It needs to be finished until the status bar has been drawn.
JSR ProcessNextFrameTop
; If PpuMaskValue is 0, we skip the top scrolling.
LDA PpuMaskValue
BEQ @skipScrollingTop
; Wait for the first sprite overflow (more than eight sprites on one scanline).
WAIT_FOR_PPU_STATUS %00100000
; The scrolling position for the middle screen is done.
SET_SCROLLING_POSITION ScrollingPositionMiddle, NameTableMiddle
@skipScrollingTop:
; The second frame processing function.
; This is the one that has the most time.
; It needs to be finished until the point for parallax scrolling is reached.
JSR ProcessNextFrameMiddle
; If PpuMaskValue is 0, we skip the middle scrolling.
LDA PpuMaskValue
BEQ @skipScrollingBottom
; Wait for sprite 0 hit.
WAIT_FOR_PPU_STATUS %01000000
; The scrolling position for the lower screen is done.
; If midframe scrolling is disabled,
; this is the only scrolling that is done at all.
SET_SCROLLING_POSITION ScrollingPositionBottom, NameTableBottom
@skipScrollingBottom:
; The third frame processing function.
JSR ProcessNextFrameBottom
; The next thing that needs to be executed is the NMI.
LDA #true
STA WaitForNmi
@gameLogicEnd:
; The program goes into an infinite loop where it processes the game logic whenever it's allowed.
JMP @gameLogic
Last edited by DRW on Fri Mar 31, 2017 12:02 pm, edited 1 time in total.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
Dynamic random access memory (DRAM) decays if not refreshed. OAM in the PPU is DRAM, and rendering refreshes it. Not rendering for longer than about a couple dozen scanlines (the length of vblank) causes OAM to decay to unpredictable values, including the position and identity of sprite 0. So if you disable rendering for a while (to set up your credit screen) and then wait for sprite 0 hit, you need to keep uploading display lists to OAM using $4014.
Re: Game stuck at credits, except during soft reset
I indeed do a bunch of stuff before rendering is turned on again before a game section:
However, nothing sprite-related writes to the actual PPU in this case.
ClearSprites and UpdateSystemSprites do nothing but writing stuff into a plain and simple array:
which is located at $0200. (The location is forced by the NES.cfg file and putting the array into a specific segment.)
The actual sprites in the PPU are only ever accessed in the NMI, and only in this way:
So, can this still be the issue?
What about too few wait for vblank? I once heard of someone who uses a third one to be sure. Can this be the problem?
Code: Select all
TempNameTableMiddle = NameTableMiddle =
TempScrollingPositionMiddle = ScrollingPositionMiddle =
TempNameTableBottom = NameTableBottom =
TempScrollingPositionBottom = ScrollingPositionBottom =
0;
/* The system sprites for the scrolling split
are set to their correct locations. */
UpdateSystemSprites();
ClearSprites(FirstNonSystemSpriteIndex);
ClearPpu();
if (GameSectionIsLevel())
InitializeLevel();
else
InitializeTextScreen();
GameSectionIsInitialized = true;
/* The PPU mask variable is set
with its default value.
This means rendering is enabled
once the value is written
to the actual PPU mask.
Enabling and disabling the PPU mask
should be done only inside NMI.
Otherwise, rendering the current data
might happen mid-frame. */
PpuMaskValue = Bin(0,0,0,1,1,1,1,0);
ClearSprites and UpdateSystemSprites do nothing but writing stuff into a plain and simple array:
Code: Select all
byte Sprites[256];
The actual sprites in the PPU are only ever accessed in the NMI, and only in this way:
Code: Select all
; The low byte of the sprites address gets sent to the PPU port.
LDA #<Sprites
STA OamAddr
; The high byte gets sent and we specify that the transfer is done immediately.
LDA #>Sprites
STA OamDma
What about too few wait for vblank? I once heard of someone who uses a third one to be sure. Can this be the problem?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
Besides, naturally, I never wait for sprite 0 and sprite overflow when rendering is disabled:
Code: Select all
; The next frame is calculated based on the current frame and the controller input.
; Due to the parallax scrolling, we have three frame processing functions.
; This is the first one.
; It needs to be finished until the status bar has been drawn.
JSR ProcessNextFrameTop
; If PpuMaskValue is 0, we skip the top scrolling.
LDA PpuMaskValue
BEQ @skipScrollingTop
; Wait for the first sprite overflow (more than eight sprites on one scanline).
WAIT_FOR_PPU_STATUS %00100000
; The scrolling position for the middle screen is done.
SET_SCROLLING_POSITION ScrollingPositionMiddle, NameTableMiddle
@skipScrollingTop:
; The second frame processing function.
; This is the one that has the most time.
; It needs to be finished until the point for parallax scrolling is reached.
JSR ProcessNextFrameMiddle
; If PpuMaskValue is 0, we skip the middle scrolling.
LDA PpuMaskValue
BEQ @skipScrollingBottom
; Wait for sprite 0 hit.
WAIT_FOR_PPU_STATUS %01000000
; The scrolling position for the lower screen is done.
; If midframe scrolling is disabled,
; this is the only scrolling that is done at all.
SET_SCROLLING_POSITION ScrollingPositionBottom, NameTableBottom
@skipScrollingBottom:
; The third frame processing function.
JSR ProcessNextFrameBottom
; The next thing that needs to be executed is the NMI.
LDA #true
STA WaitForNmi
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
And in case it's necessary, here's my NMI function:
Code: Select all
.segment "CODE"
Nmi:
; The registers are put to the stack.
; This is done because NMI can trigger any time,
; even if the program is right in the middle of the game logic.
; In this case, we have to be able
; to restore the current status when NMI is left again.
PHA
TXA
PHA
TYA
PHA
; If it's not time for the NMI to trigger
; because the game logic has still not finished,
; NMI is left again.
; Otherwise we go on.
LDA WaitForNmi
BEQ @end
; After the NMI was processed, the game logic may start again
; and NMI is disallowed until the game logic is over.
; Setting this value right here at the start
; would also prevent a second NMI to do anything
; in case the first NMI ever takes too long.
; (Which shouldn't happen, but it's still safe this way.)
LDA #false
STA WaitForNmi
; The actual PPU mask is set with the value that was saved for it.
LDA PpuMaskValue
STA PpuMask
; If it is 0, NMI is left again.
BEQ @end
; Rendering the sprites.
; The NMI gets the sprites per direct memory access (DMA).
; So, we tell it where in memory they can be found:
; The low byte of the sprites address gets sent to the PPU port.
LDA #<Sprites
STA OamAddr
; The high byte gets sent and we specify that the transfer is done immediately.
LDA #>Sprites
STA OamDma
; Drawing the background, the attributes and the palettes.
; There is not enough time to draw the whole background every frame.
; That's why the following code may only draw whatever has changed.
; Important: Palette updates should ALWAYS be in the NMI, never outside it.
; Otherwise they might change the screen's color when rendering is turned off.
; So, don't call UpdatePpu outside of NMI when it contains palettes data.
; Updating the background and the attributes outside of NMI is o.k. though.
JSR UpdatePpu
; Setting the scrolling position.
; At first, the status bar is drawn.
; So, the horizontal and vertical scrolling position are set to 0.
LDA #0
STA PpuScroll
STA PpuScroll
; The status bar appears in nametable 0, so we set the horizontal value.
LDA #PpuCtrlHorizontal
STA PpuCtrl
; The rest of the scrolling is done outside NMI
; when the corresponding places
; (status bar end, parallax scrolling line)
; are reached.
@end:
; The sound update is done always,
; so that the sound wouldn't lag
; even if the game logic does.
JSR FamiToneUpdate
; The registers are pulled from the stack,
; so that they get the same values as when NMI hit.
PLA
TAY
PLA
TAX
PLA
; The NMI has ended and the program returns
; to the code that was interrupted by the NMI.
RTI
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
I see a classic mistake in the OAM DMA:
This code probably works, but the label usage and comments hints that the assumptions how these registers works are incorrect.
OamAddr ($2003) isn't used for the low byte of the source address, it's the start address of the destination in the OAM (OAM is only 256 byte so only one byte is needed for the address). OamDma ($4014) takes the high byte of the starting source address which is normally the page number of a RAM page (only full RAM pages can be used for shadow OAM as I understand it), so there is no need to specify the low part of the source address.
Before starting an OAM DMA you always write 0 to OamAddr to make sure the OAM DMA writes from the beginning of OAM. Then you write the RAM page number you are using as shadow OAM to OamDma to invoke the DMA.
If "Sprite" is changed to something that doesn't have $00 in it's upper byte, the code won't work as expected.
So the code should probably look something like this:
Code: Select all
; The low byte of the sprites address gets sent to the PPU port.
LDA #<Sprites
STA OamAddr
; The high byte gets sent and we specify that the transfer is done immediately.
LDA #>Sprites
STA OamDma
OamAddr ($2003) isn't used for the low byte of the source address, it's the start address of the destination in the OAM (OAM is only 256 byte so only one byte is needed for the address). OamDma ($4014) takes the high byte of the starting source address which is normally the page number of a RAM page (only full RAM pages can be used for shadow OAM as I understand it), so there is no need to specify the low part of the source address.
Before starting an OAM DMA you always write 0 to OamAddr to make sure the OAM DMA writes from the beginning of OAM. Then you write the RAM page number you are using as shadow OAM to OamDma to invoke the DMA.
If "Sprite" is changed to something that doesn't have $00 in it's upper byte, the code won't work as expected.
So the code should probably look something like this:
Code: Select all
; Ensure OAM address $00 is selected as destination start address.
LDA #$00
STA OamAddr
; Set the RAM page to be used as source address and invoke OAM-DMA
LDA #>Sprites
STA OamDma
Re: Game stuck at credits, except during soft reset
This mistake comes straight out of Nerdy Nights.
Re: Game stuck at credits, except during soft reset
Indeed, it is out of Nerdy Nights.
However, this cannot be the specific mistake in my case because I'm fully aware that my sprite array has to be aligned to $100. And I took means that this is always guaranteed:
NES.cfg (only the relevant stuff):
Declaration in the code:
So, thanks for the hint. I'll correct it in the future and write a better comment.
But binary-wise, it makes no difference because the low byte of the sprites array is always 0.
Otherwise, my sprite clearing function wouldn't work anyway since it goes through the array until the counter reaches 0 again.
So, unfortunately, this was not relevant to the specific issue I'm having.
However, this cannot be the specific mistake in my case because I'm fully aware that my sprite array has to be aligned to $100. And I took means that this is always guaranteed:
NES.cfg (only the relevant stuff):
Code: Select all
MEMORY
{
ZP: type = rw, start = $0001, size = $00FF, file = "";
RAM: type = rw, start = $0200, size = $0400, file = "";
}
SEGMENTS
{
ZEROPAGE: load = ZP, type = zp;
SPRITES: load = RAM, type = bss, align = $0100;
FAMITONE: load = RAM, type = bss, align = $0100;
BSS: load = RAM, type = bss;
NO_RESET: load = RAM, type = bss, define = yes;
}
Code: Select all
.segment "SPRITES"
Sprites: .res $100
.export Sprites
.export _Sprites = Sprites
But binary-wise, it makes no difference because the low byte of the sprites array is always 0.
Otherwise, my sprite clearing function wouldn't work anyway since it goes through the array until the counter reaches 0 again.
So, unfortunately, this was not relevant to the specific issue I'm having.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
Yeah, this makes no difference for the final binary. I haven't looked at all the code posted, but the suspicion is that rendering is disabled long enough for the OAM data to decay and corrupt the sprite data, so when rendering is enabled again the sprites aren't properly arranged to trigger the sprite 0 hit and/or the sprite overflow. The quick fix in this case would be to do an OAM DMA before enabling rendering again, to make sure the sprite data is valid.
I don't think any emulators simulate OAM decay, so it actually makes a lot of sense that they wouldn't exhibit this problem. But like I said, I didn't read all of the code in this thread, so I'm sorry if this theory has already been debunked.
I don't think any emulators simulate OAM decay, so it actually makes a lot of sense that they wouldn't exhibit this problem. But like I said, I didn't read all of the code in this thread, so I'm sorry if this theory has already been debunked.
Re: Game stuck at credits, except during soft reset
Hmm. O.k., I'll try it. (Memblers is checking the ROMs on real hardware for me.)
However, I'm not sure whether this can actually be the issue.
Here, let me show you a shortened version of the code:
At first, I set WaitForNmi to true, then I enable the rendering, then comes the game logic.
If WaitForNmi is still true, the game logic skips right to the end where it jumps back to the beginning.
Only if WaitForNmi is false is the game logic done and the sprite 0 split actually checked.
Therefore, no wait for sprite 0 or for sprite overflow can appear unless NMI has run. Also, it's only checked if the PpuMask is actually on.
Furthermore, this is a shortened version of Nmi:
As you see, the NMI is the only place where the PPUMASK register is actually set.
And right after this, it's always a sprite update (except if PPUMASK was set to 0 of course, but in this case, the game logic will skip sprite 0 and sprite overflow check anyway).
So, after seeing this, can the decaying still be the issue?
However, I'm not sure whether this can actually be the issue.
Here, let me show you a shortened version of the code:
Code: Select all
JSR Initialize
; Sets PpuMaskValue to %00011110 in C.
LDA #true
STA WaitForNmi
LDA #%10010000
STA PpuCtrl
@gameLogic:
LDA WaitForNmi
BNE @gameLogicEnd
;;; ReadController ;;;
JSR ProcessNextFrameTop
LDA PpuMaskValue
BEQ @skipScrollingTop
WAIT_FOR_PPU_STATUS %00100000
SET_SCROLLING_POSITION ScrollingPositionMiddle, NameTableMiddle
@skipScrollingTop:
JSR ProcessNextFrameMiddle
LDA PpuMaskValue
BEQ @skipScrollingBottom
WAIT_FOR_PPU_STATUS %01000000
SET_SCROLLING_POSITION ScrollingPositionBottom, NameTableBottom
@skipScrollingBottom:
JSR ProcessNextFrameBottom
LDA #true
STA WaitForNmi
@gameLogicEnd:
JMP @gameLogic
If WaitForNmi is still true, the game logic skips right to the end where it jumps back to the beginning.
Only if WaitForNmi is false is the game logic done and the sprite 0 split actually checked.
Therefore, no wait for sprite 0 or for sprite overflow can appear unless NMI has run. Also, it's only checked if the PpuMask is actually on.
Furthermore, this is a shortened version of Nmi:
Code: Select all
Nmi:
;;;;; PHA TXA PHA TYA PHA ;;;;;
LDA WaitForNmi
BEQ @end
LDA #false
STA WaitForNmi
LDA PpuMaskValue
STA PpuMask
BEQ @end
LDA #0
STA OamAddr
LDA #>Sprites
STA OamDma
JSR UpdatePpu
LDA #0
STA PpuScroll
STA PpuScroll
LDA #%10010000
STA PpuCtrl
@end:
JSR FamiToneUpdate
;;;;; PLA TAY PLA TAX PLA ;;;;;
And right after this, it's always a sprite update (except if PPUMASK was set to 0 of course, but in this case, the game logic will skip sprite 0 and sprite overflow check anyway).
So, after seeing this, can the decaying still be the issue?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
- infiniteneslives
- Posts: 2104
- Joined: Mon Apr 04, 2011 11:49 am
- Location: WhereverIparkIt, USA
- Contact:
Re: Game stuck at credits, except during soft reset
Have you tried simply waiting more vblanks before making any PPU writes?
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers
Re: Game stuck at credits, except during soft reset
Yes, I compiled a version where I wait three instead of two and one where a loop lets the game wait 10 vblanks.
I sent all of those to Memblers, including the ones with the additional OAMDMA. It remains to be seen which version still has the error.
I sent all of those to Memblers, including the ones with the additional OAMDMA. It remains to be seen which version still has the error.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Game stuck at credits, except during soft reset
O.k., the publisher tested my test ROMs. I tried all of the things you told me and some more, each thing in a separate ROM:
Setting the "system sprites" and manual OAMDMA at startup.
10 vblanks before doing anything else.
Checking PPUMASK directly instead of my temporary variable.
etc.
The only thing that helped was completely disabling the wait for the "nine sprites overflow" flag. When I did this, the game always worked.
Sprite 0 flag wait is fine. This one doesn't freeze anything.
But nine sprites overflow causes the freezing.
Now my question:
Is it possible that the overflow flag doesn't hit at the beginning, even though sprite 0 already does?
I mean, it's not that the game stops midway during playing. Also, it almost always works after a soft reset.
(So, a mundame error like "Did you actually position the sprites correctly" is also pretty much out of the question.)
But it looks like you cannot check for nine sprites at the beginning, even though you can already check for nine sprites overflow.
Alternately, can you please check whether my sprites overflow wait is even correct?
Setting the "system sprites" and manual OAMDMA at startup.
10 vblanks before doing anything else.
Checking PPUMASK directly instead of my temporary variable.
etc.
The only thing that helped was completely disabling the wait for the "nine sprites overflow" flag. When I did this, the game always worked.
Sprite 0 flag wait is fine. This one doesn't freeze anything.
But nine sprites overflow causes the freezing.
Now my question:
Is it possible that the overflow flag doesn't hit at the beginning, even though sprite 0 already does?
I mean, it's not that the game stops midway during playing. Also, it almost always works after a soft reset.
(So, a mundame error like "Did you actually position the sprites correctly" is also pretty much out of the question.)
But it looks like you cannot check for nine sprites at the beginning, even though you can already check for nine sprites overflow.
Alternately, can you please check whether my sprites overflow wait is even correct?
Code: Select all
.macro WAIT_FOR_PPU_STATUS value
.local @waitForNotPpuStatus, @waitForPpuStatus
@waitForNotPpuStatus:
LDA PpuStatus
AND #value
BNE @waitForNotPpuStatus
@waitForPpuStatus:
LDA PpuStatus
AND #value
BEQ @waitForPpuStatus
.endmacro
;;; Do some stuff ;;;
WAIT_FOR_PPU_STATUS %00100000
;;; Do some stuff ;;;
WAIT_FOR_PPU_STATUS %01000000
;;; Do some stuff ;;;
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html