Game stuck at credits, except during soft reset

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Game stuck at credits, except during soft reset

Post by DRW » Fri Mar 31, 2017 11:30 am

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.
My game "City Trouble": www.denny-r-walter.de/city.htm

lidnariq
Posts: 9692
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Game stuck at credits, except during soft reset

Post by lidnariq » Fri Mar 31, 2017 11:50 am

Is there any memory or PPU/APU register that you didn't set to a known value in your powerup routine?

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Fri Mar 31, 2017 11:57 am

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:

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": www.denny-r-walter.de/city.htm

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

Re: Game stuck at credits, except during soft reset

Post by tepples » Fri Mar 31, 2017 12:00 pm

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.

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Fri Mar 31, 2017 12:18 pm

I indeed do a bunch of stuff before rendering is turned on again before a game section:

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);
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:

Code: Select all

byte Sprites[256];
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:

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
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?
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Fri Mar 31, 2017 12:23 pm

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": www.denny-r-walter.de/city.htm

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Fri Mar 31, 2017 12:56 pm

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": www.denny-r-walter.de/city.htm

Pokun
Posts: 1512
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Game stuck at credits, except during soft reset

Post by Pokun » Sat Apr 01, 2017 9:51 am

I see a classic mistake in the OAM DMA:

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
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

   ; 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

User avatar
tokumaru
Posts: 11866
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Game stuck at credits, except during soft reset

Post by tokumaru » Sat Apr 01, 2017 10:05 am

This mistake comes straight out of Nerdy Nights.

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Sat Apr 01, 2017 11:23 am

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):

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;
}
Declaration in the code:

Code: Select all

.segment "SPRITES"

	Sprites: .res $100
	.export Sprites
	.export _Sprites = Sprites
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.
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
tokumaru
Posts: 11866
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Game stuck at credits, except during soft reset

Post by tokumaru » Sat Apr 01, 2017 11:44 am

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.

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Sat Apr 01, 2017 1:17 pm

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:

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
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:

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 ;;;;;
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?
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: Game stuck at credits, except during soft reset

Post by infiniteneslives » Sat Apr 01, 2017 11:07 pm

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

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Sun Apr 02, 2017 12:50 am

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.
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
DRW
Posts: 1982
Joined: Sat Sep 07, 2013 2:59 pm

Re: Game stuck at credits, except during soft reset

Post by DRW » Mon Apr 03, 2017 10:45 am

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?

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": www.denny-r-walter.de/city.htm

Post Reply