Shadow of the Beast (port)

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

Moderator: Moderators

User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Shadow of the Beast (port)

Post by rainwarrior »

Ah, I see the problem. Wasn't quite where I was suggesting, though related:

You set up your first IRQ here in your main thread code. This happens directly after the NMI returns:

Code: Select all

 00:9BB8: 20 C0 E3  JSR $E3C0 ; wait for NMI
>00:9BBB: A9 01     LDA #$01 ; setup first interrupt
 00:9BBD: 8D 00 E0  STA $E000
 00:9BC0: 8D 00 C0  STA $C000
 00:9BC3: A9 00     LDA #$00
 00:9BC5: 8D 01 C0  STA $C001
 00:9BC8: A9 01     LDA #$01
 00:9BCA: 8D 01 E0  STA $E001
 00:9BCD: A9 00     LDA #$00
 00:9BCF: 85 74     STA $74
The problem is that the music routine delays when you will return from NMI. On PAL there is plenty of time for it to execute before rendering begins, but on NTSC it will overlap the beginning of the frame sometimes. If this first IRQ setup doesn't happen before scanline 0, the whole screen is thrown off. (Seems to be about 10-15 scanlines late on every other frame.)

The solution should be to put this inside your NMI handler. See my last suggestion from my previous post, you should still add that CLI and RTI, but right there above the suggested CLI do this first IRQ setup right there, when you know you're still in vblank (i.e. before scanline 0) and you haven't yet run the music routine. This will allow that first IRQ to fire on scanline 0 (which will sometimes be within the music routine, but it is perfectly okay to interrupt that).


Alternative option is to put the music playback call in your main thread just after that STA $74. This will get your NMI handler returning earlier again.

Ideally both of these things should be within the NMI, though. Having the IRQs set off by the NMI will prevent them from failling if your main thread ever runs too long (slowdown), and similar with music having it in the NMI makes it slowdown resitant. However, if you can guarantee the main thread will never run long it doesn't matter either way.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Shadow of the Beast (port)

Post by Diskover »

rainwarrior wrote:My assumption is that you're running your music within the NMI.

The reason this is a problem is that NMI implicitly does a SEI (cleared by the RTI popping the processor flags). So if your music update happens in your NMI routine, it will overlap and block your interrupts if it takes too long, which it does on NTSC.

Easy fix: just put a CLI in the NMI routine just before it calls the music playback (assuming it is the last thing that happens in your NMI handler). Otherwise if that's not feasible, you can just move the music playback outside of the NMI. It doesn't really matter too much where it gets called as long as it's once per frame, preferably at a consistent time (which is why NMI is usually convenient).


Edit: Actually, peeking at it in a debugger, it looks like your NMI is missing an RTI at the end? It seems to roll right into the IRQ handler after finishing. So in this case, I'd recommend putting a CLI before the music playback there, and also an RTI before the IRQ handler begins.

Code: Select all

 03:E224: A9 00     LDA #$00
 03:E226: 85 02     STA $02 = #$03
 03:E228: 20 4C E8  JSR $E84C ; music playback? place a CLI right before this line
 03:E22B: 68        PLA
 03:E22C: A8        TAY
 03:E22D: 68        PLA
 03:E22E: AA        TAX
 03:E22F: 68        PLA ; shouldn't an RTI follow this line?
>03:E230: 48        PHA ; IRQ handler starts here
 03:E231: 8A        TXA
 03:E232: 48        PHA
 03:E233: 98        TYA
 03:E234: 48        PHA
...
 03:E260: 40        RTI
rainwarrior wrote:Ah, I see the problem. Wasn't quite where I was suggesting, though related:

You set up your first IRQ here in your main thread code. This happens directly after the NMI returns:

Code: Select all

 00:9BB8: 20 C0 E3  JSR $E3C0 ; wait for NMI
>00:9BBB: A9 01     LDA #$01 ; setup first interrupt
 00:9BBD: 8D 00 E0  STA $E000
 00:9BC0: 8D 00 C0  STA $C000
 00:9BC3: A9 00     LDA #$00
 00:9BC5: 8D 01 C0  STA $C001
 00:9BC8: A9 01     LDA #$01
 00:9BCA: 8D 01 E0  STA $E001
 00:9BCD: A9 00     LDA #$00
 00:9BCF: 85 74     STA $74
The problem is that the music routine delays when you will return from NMI. On PAL there is plenty of time for it to execute before rendering begins, but on NTSC it will overlap the beginning of the frame sometimes. If this first IRQ setup doesn't happen before scanline 0, the whole screen is thrown off. (Seems to be about 10-15 scanlines late on every other frame.)

The solution should be to put this inside your NMI handler. See my last suggestion from my previous post, you should still add that CLI and RTI, but right there above the suggested CLI do this first IRQ setup right there, when you know you're still in vblank (i.e. before scanline 0) and you haven't yet run the music routine. This will allow that first IRQ to fire on scanline 0 (which will sometimes be within the music routine, but it is perfectly okay to interrupt that).


Alternative option is to put the music playback call in your main thread just after that STA $74. This will get your NMI handler returning earlier again.

Ideally both of these things should be within the NMI, though. Having the IRQs set off by the NMI will prevent them from failling if your main thread ever runs too long (slowdown), and similar with music having it in the NMI makes it slowdown resitant. However, if you can guarantee the main thread will never run long it doesn't matter either way.

Sorry for taking a long time to answer. Busy.

We'll see. I do not have much idea of ​​assembler, but let's see if I can fix this.

The routine that NMI controls is part of the NESlib library. According to the instructions that you indicate, I should put a CLI control before the JSR FamiToneUpdate line and it would look like this:

Code: Select all


;NMI handler

nmi:

...
...
...

@skipNtsc:

	cli
	jsr FamiToneUpdate

	
	pla
	tay
	pla
	tax
	pla
This is correct?
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Shadow of the Beast (port)

Post by rainwarrior »

Well, there are three things:

1. Put the IRQ setup in your NMI handler before jsr FamitomeUpdate so that it is guaranteed to happen before the end of vblank.
2. Right after the IRQ setup, put cli (also before jsr FamitoneUpdate) so that if an IRQ happens during the music routine it won't be blocked.
3. Put rti on the end of your NMI handler (after that ...tax, pla). Without this, the code just keeps running into the IRQ handler directly below.

The main thing I'm not entirely certain of, without looking at much more of the code, is whether it's appropriate to set up the IRQ every NMI or just on screens that need it. Maybe it should have a flag that turns it on and off? (When I say IRQ setup I mean that code including lines $9BBB to $9BCF in that disassembly.)

As an alternative, just as a quick test, you could just take jsr FamitomeUpdate and move it just under the IRQ setup where it is right now. (Just after where it stores a #0 to your IRQ counter variable at $74.) That would also solve the problem you're having that this music update is happening before you've prepared your IRQs to fire. I wouldn't recommend this as a permanent solution, though, because it's usually appropriate to do music in the NMI handler. If it's not in the NMI handler, music will tend to skip during scene transitions, and also you would need to manually call jsr FamitoneUpdate in each main thread frame loop if there are more than one.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Shadow of the Beast (port)

Post by Diskover »

Ok, let's see. Right now this is like this:

Code: Select all

;NMI handler

nmi:

...
...
...

@skipNtsc:	
	
	LDA #$01 	; turn off MMC3 IRQ
	STA $E000
	STA $C000	; count 20 scanlines, then IRQ
	LDA #$00
	STA $C001
	LDA #$01
	STA $E001	;turn on MMC3 IRQ
	LDA #$00
	STA $74

	cli
	
	jsr FamiToneUpdate

	
	pla
	tay
	pla
	tax
	pla

	rti



irq:
	pha
	txa
	pha
	tya
	pha
	ldy #15				
:	dey					
	bne :- 				

	ldx _Scroll_Index 	
	lda _Scroll, x
	sta $2005			
	lda _scrollY 		
	sta $2005			
	inc _Scroll_Index

	lda #1
	sta $e000 			
	lda #14				
	sta $c000 			
	sta $c001
	lda #1
	sta $e001 			
   
	pla
	tay
	pla
	tax
	pla
	rti

I share the rom. It works in NES PAL and in the emulated NEStopia but now it gives problems in FCEUx and VirtuaNES.
Attachments
Shadow of the Beast (alpha 0.31) [HB].nes
(128.02 KiB) Downloaded 339 times
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Shadow of the Beast (port)

Post by rainwarrior »

I think the problem is right here:

Code: Select all

 00:9BB9: 20 D8 E3  JSR $E3D8 ; wait for NMI to finish
 00:9BBC: A9 00     LDA #$00
>00:9BBE: 85 74     STA $74 = #$00 ; reset raster split index
 00:9BC0: AD C3 04  LDA $04C3 = #$04
You moved the IRQ setup into the NMI (before that CLI) but you left this reset of $74 to 0 in the main loop as soon as it returns from the NMI. I don't know what you call the variable at $74 but it appears to be an index into your table of how many scanlines to skip per split. Leaving this extra $74 = 0 in there causes the splits to start over in the middle of the screen.

Doesn't cause a problem in PAL or without music on NTSC because if NMI finishes before vblank it hasn't run any IRQs yet (resets 0 back to 0). Once you add music in, some IRQs will happen before the music is finished, and this reset ends up mid-screen.

(If I replace that STA $74 with two NOPs it seems to run as correctly as the other modes.)


Edit: Oh, you called $74 _Scroll_Index in your IRQ code, you should use that at @skipNtsc too just in case the variable ever moves. I was only calling it $74 because I was looking at it through a disassembly.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Shadow of the Beast (port)

Post by Diskover »

rainwarrior wrote: Oh, you called $74 _Scroll_Index in your IRQ code, you should use that at @skipNtsc too just in case the variable ever moves. I was only calling it $74 because I was looking at it through a disassembly.
Ok, now I have understood.

According to the dougeff tutorial everything that corresponds to the IRQ configuration, etc ... is written in C, even the variable Scroll_Index I put it to 0 from there.

https://nesdoug.com/2016/01/15/24-mmc3- ... hing-irqs/

When you have indicated the faults that you see from assembler, I have not understood very well, but I let myself be guided by you until I understood it.

Well, what I have done next has been to change those two lines and put them in this way:

Code: Select all

	LDA #$00
	STA _Scroll_Index ; ponemos Scroll_Index a 0
Now the rom seems to work well in the emulators VirtuaNES, NEStopia, FCEU, etc ...

I just need to know if it works well now in an NTSC NES.
Attachments
Shadow of the Beast (alpha 0.32) [HB].nes
(128.02 KiB) Downloaded 342 times
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Shadow of the Beast (port)

Post by rainwarrior »

That looks better in terms of scrolling.

One more issue though: you haven't set up 4 of the CHR banking registers. You can see the problem using Mesen, but it appears the same way on my PowerPak (i.e. the character sprite and moon sprite are using the wrong tiles). This isn't NTSC / PAL related, it is only about fully initializing the MMC3 (would look the same on PAL PowerPak/Mesen).

Of the 8 bank settings on the MMC3 (selected through $8000) I see setup for: 0, 1, 6, 7 (PPU first page, and CPU banks). You are missing any setup for 2, 3, 4, 5 (PPU second page, i.e. all your sprites).

Some emulators, and apparently the Everdrive, pre-initialize these with default values, but on the real MMC3 these are "random" when powered on. (You can see what FCEUX uses instead of randomness, for example. This seems to be a common setup pattern for emulators, Everdrive must have borrowed it.)

Anyhow, really easy to fix, just send 4,5,6,7 to the uninitialized banks 2,3,4,5 in your startup code:

Code: Select all

lda #2
sta $8000
lda #4
sta $8001
lda #3
sta $8000
lda #5
sta $8001
lda #4
sta $8000
lda #6
sta $8001
lda #5
sta $8000
lda #7
sta $8001
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Shadow of the Beast (port)

Post by tepples »

The 0, 2, 4, 5, 6, 7, 0, 1 pattern seen in FCEUX MMC3RegReset() sets up an identity mapping: PPU $0000-$1FFF = CHR ROM $00000-$01FFF. It's also what my stock MMC3 init code sets up.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Shadow of the Beast (port)

Post by Diskover »

rainwarrior wrote:That looks better in terms of scrolling.

One more issue though: you haven't set up 4 of the CHR banking registers. You can see the problem using Mesen, but it appears the same way on my PowerPak (i.e. the character sprite and moon sprite are using the wrong tiles). This isn't NTSC / PAL related, it is only about fully initializing the MMC3 (would look the same on PAL PowerPak/Mesen).

Of the 8 bank settings on the MMC3 (selected through $8000) I see setup for: 0, 1, 6, 7 (PPU first page, and CPU banks). You are missing any setup for 2, 3, 4, 5 (PPU second page, i.e. all your sprites).

Some emulators, and apparently the Everdrive, pre-initialize these with default values, but on the real MMC3 these are "random" when powered on. (You can see what FCEUX uses instead of randomness, for example. This seems to be a common setup pattern for emulators, Everdrive must have borrowed it.)

Anyhow, really easy to fix, just send 4,5,6,7 to the uninitialized banks 2,3,4,5 in your startup code:

Code: Select all

lda #2
sta $8000
lda #4
sta $8001
lda #3
sta $8000
lda #5
sta $8001
lda #4
sta $8000
lda #6
sta $8001
lda #5
sta $8000
lda #7
sta $8001
I have not understood what this really does, but I have tried to fix it.

Is it right now?
Attachments
Shadow of the Beast (alpha 0.33) [HB].nes
(128.02 KiB) Downloaded 337 times
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Shadow of the Beast (port)

Post by rainwarrior »

Yes, this one now works on PowerPak on my (NTSC) NES with music. It also works in Mesen. The only emulator that seems to fail with it now is Nintendulator, but I'm not sure why that is.

There are smaller glitches still, I'm sure you've seen, but the main stuff is working. This is good!!
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Shadow of the Beast (port)

Post by rainwarrior »

The problem this Nintendulator is the same kind of issue as with the sprites, just this time with PRG banking. You haven't initialized the bank at $8000-9FFF (banking register 6) before you use it. Apparently almost all emulators initialize this to 0, and even the PowerPak and Everdrive seem to do this too, but again this would be random on the real MMC3 cart.

Your startup code:

Code: Select all

 03:E005: A9 40     LDA #$40 ; reset begins here
 03:E007: 8D 17 40  STA $4017 = #$00
 03:E00A: 78        SEI
 03:E00B: A2 FF     LDX #$FF
 03:E00D: 9A        TXS
 03:E00E: E8        INX
 03:E00F: 8E 01 20  STX $2001 = #$00
 03:E012: 8E 10 40  STX $4010 = #$00
 03:E015: 8E 00 20  STX $2000 = #$00
 03:E018: 2C 02 20  BIT $2002 = #$00
 03:E01B: 2C 02 20  BIT $2002 = #$00
 03:E01E: 10 FB     BPL $E01B
 03:E020: 2C 02 20  BIT $2002 = #$00
 03:E023: 10 FB     BPL $E020
 03:E025: A9 3F     LDA #$3F
 03:E027: 8D 06 20  STA $2006 = #$00
 03:E02A: 8E 06 20  STX $2006 = #$00
 03:E02D: A9 0F     LDA #$0F
 03:E02F: A2 20     LDX #$20
 03:E031: 8D 07 20  STA $2007 = #$00
 03:E034: CA        DEX
 03:E035: D0 FA     BNE $E031
 03:E037: 8A        TXA
 03:E038: A0 20     LDY #$20
 03:E03A: 8C 06 20  STY $2006 = #$00
 03:E03D: 8D 06 20  STA $2006 = #$00
 03:E040: A0 10     LDY #$10
 03:E042: 8D 07 20  STA $2007 = #$00
 03:E045: E8        INX
 03:E046: D0 FA     BNE $E042
 03:E048: 88        DEY
 03:E049: D0 F7     BNE $E042
 03:E04B: 8A        TXA
 03:E04C: 95 00     STA $00,X @ $0000 = #$F7
 03:E04E: 9D 00 01  STA $0100,X @ $0100 = #$52
 03:E051: 9D 00 02  STA $0200,X @ $0200 = #$B6
 03:E054: 9D 00 03  STA $0300,X @ $0300 = #$B6
 03:E057: 9D 00 04  STA $0400,X @ $0400 = #$4E
 03:E05A: 9D 00 05  STA $0500,X @ $0500 = #$2A
 03:E05D: 9D 00 06  STA $0600,X @ $0600 = #$84
 03:E060: 9D 00 07  STA $0700,X @ $0700 = #$2A
 03:E063: E8        INX
 03:E064: D0 E6     BNE $E04C
 03:E066: A9 04     LDA #$04
 03:E068: 20 0E E3  JSR $E30E
 03:E06B: 20 E3 E2  JSR $E2E3
 03:E06E: 20 39 E3  JSR $E339
>03:E071: 20 91 9F  JSR $9F91 ; crash occurs here!
 03:E074: 20 FD 9E  JSR $9EFD
You need to initialize banking register 6 before that line is reached. These 4 lines would do it:

Code: Select all

lda #6
sta $8000
lda #0
sta $8001
However, it's probably a good idea to initialize all 8 MMC3 bank registers very early on in your startup code. Like I see that you've added the CHR banking initialization that I suggested but it takes place a long time after this startup routine. Might as well do all 8 in here at once. Tepples posted a suggested initial set of values to load earlier on (it's the same as FCEUX and some other emulators use, probably the same as Everdrive's initialization too).
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Shadow of the Beast (port)

Post by Diskover »

rainwarrior wrote:The problem this Nintendulator is the same kind of issue as with the sprites, just this time with PRG banking. You haven't initialized the bank at $8000-9FFF (banking register 6) before you use it. Apparently almost all emulators initialize this to 0, and even the PowerPak and Everdrive seem to do this too, but again this would be random on the real MMC3 cart.

You need to initialize banking register 6 before that line is reached. These 4 lines would do it:

Code: Select all

lda #6
sta $8000
lda #0
sta $8001
However, it's probably a good idea to initialize all 8 MMC3 bank registers very early on in your startup code. Like I see that you've added the CHR banking initialization that I suggested but it takes place a long time after this startup routine. Might as well do all 8 in here at once. Tepples posted a suggested initial set of values to load earlier on (it's the same as FCEUX and some other emulators use, probably the same as Everdrive's initialization too).
Ok, it seems that now it works correctly in Nintendulator.

Yes, this technical demo contains minor errors about the scroll and the nametables.
Attachments
Shadow of the Beast (alpha 0.34) [HB].nes
(128.02 KiB) Downloaded 357 times
Post Reply