Need guidance with nes to snes.

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
93143
Posts: 1370
Joined: Fri Jul 04, 2014 9:31 pm

Re: Need guidance with nes to snes.

Post by 93143 » Fri Apr 30, 2021 8:49 pm

$4212 doesn't do anything; it's just information. $4210 should be read during NMI. $4211 should be read during an IRQ.

Patrick FR
Posts: 78
Joined: Tue Jan 19, 2010 10:35 am
Location: Lyon

Re: Need guidance with nes to snes.

Post by Patrick FR » Sat May 01, 2021 6:26 am

Hi
I was just passing by and I can answer about the use of Memblers APU emulatin in upernes.
From what I remember:

* The binary is Memblers emulator to be loaded in the SPC 700 . I took it from his "NSF Player for SNES" rom (https://www.youtube.com/watch?v=cHPgbuM3Yo8). I could not find exactly what to use so I got it by debugging the ROM :wink:

* "setup_spc" routine in the sound assembler file loads the binary to the SPC700, it is the first thing to do. You have to put the binary in a segment of your rom. APUInit is just a variable to remember that this has been done. In fact it may be removed if this is called once at start.

* Then for a basic sound support, you will select a free RAM space and on every write or read to the APU you will replace by a Write or read in the memory area you selected. (I used WRAM then SRAM between 2k and 16K in PRG bank, tried many things). The last version may be in WRAM but you have to decide where to put those contiguous bytes representing the APU registers.

* Set up a timer to update the APU registers in the SPC700 during the drawing of the lines at 3/4 of the picture. Because the game logic runs at vertical blank and then continues during the beginning of the image. But it tends to do nothing during the last part of the drawing, so there you have time to update.

One could directly update the registers every time they are accessed and get rid of knowing what changed. But it may be costly, because of the way data is transferred in a row to the SPC700. I do not know, it may also imply a change in the SP700 code. In my routine I just write in RAM and update later, like Memblers does.

Do not worry too much about the variables, some are just states for upernes, the others are the buffer used for APU registers. You have to set up how to keep the APU accesses, where to check for changes, and where to upload them to the SPC700 during the frame. And I had the same problems as you have. Plus the last commit of upernes is not the best at using sound.

By the way, do you use upernes? A mapper handler for your game mapper could be written in the rom disassembler and then it would be a matter of improving where it stalls of fails at runtime.

Patrick FR
Posts: 78
Joined: Tue Jan 19, 2010 10:35 am
Location: Lyon

Re: Need guidance with nes to snes.

Post by Patrick FR » Sat May 01, 2021 7:23 am

Looking on my last commits, I see that I execute the code from bank $7E and therefore in this bank, we find RAM at $4000. So every access to the APU ends up in RAM. And no need to patch the code, just update it in the SPC700. But it means making a copy of the PRG rom in this bank and jumping to it. It also means no access to HW registers without changing the bank back to $80 or something.
It may be generalised for other registers where a write overwrites the previous value for the current frame without consequence. And therefore, spare emulation accesses and time.
I assume you have the snes official developer manual, it is a must have for those bank descriptions.

Patrick FR
Posts: 78
Joined: Tue Jan 19, 2010 10:35 am
Location: Lyon

Re: Need guidance with nes to snes.

Post by Patrick FR » Sat May 01, 2021 7:37 am

In fact upernes would convert any NROM, it could be there are things to fix but it may work for your intended rom.

infidelity
Posts: 485
Joined: Fri Mar 01, 2013 4:46 am

Re: Need guidance with nes to snes.

Post by infidelity » Sat May 01, 2021 2:08 pm

Hey everyone, so it's been a couple days that i've been screwing with this, and after tightening up code, and changing my method of setting the stack pointer to using the A register now, i fixed the crashing.

But i just now discovered what that slowdown is, i'll explain further in this reply, but, I don't know how to proceed....

First let me explain the stack thing i did. Before, I was getting the value the original game wants/has modified to be inserted into the stack pointer, transfering that low byte of the stack pointer into A, setting A,X,Y to 16bit, adding #$100 to A so that the A value is #$1xx, transferred A to X, then transferred X to the stack pointer, then set A,X,Y back to 8bit.

Pretty messy. The original game has 3 stack locations, they are each given 40 bytes per space.

I was getting crashes around points in the game where I was in the middle of setting 16bit within the NMI, and I'd get an NMI refiring before the code would revert back to 8bit, and it would screw with the stack, crashing the game.

So my new method of setting the stack is this. Here is one example. Credit goes to Oziphantom for originally showing me the xba trick back on page 17. My new method is simply using TCS, bypassing the need to switch to 16bit mode.

Code: Select all

A901	;load 01 for hi byte for stack
EB	;shift 01 over so now A is #$1xx
A97F	;load 7F, now A is #$17F
1B 	;TCS (Transfer 16 bit A to SP), I read a doc that this transfers as 16 bit, regardless if MX flags are set
;the following is for my own piece of mind, wanting the hi byte of A cleared out
A900     ;load 00 
EB 	;shift 00 over so now hi byte of A is 00
With that new method, i no longer get any crashes! However i was still plagued with horrific slowdown during 1 scene of my story mode in the game.

Turns out i was definitely running out of time with my NMI, that is why i have the slowdown currently. When it finally dawned on me regarding "time", I decided to look at a simple routine I have for my sprites.

But first, here is what the original MMC3 game does.

Every frame, the game performs a bankswap of $8000-$9FFF & $A000-$BFFF for the sprite data banks. Obviously it's the MMC3 mapper that does this swap instantaneously. What I have done, is use DMA to load the sprite data banks, and store them to $7E:8000-$7E:BFFF.

What I didn't realize was the original game swaps this every frame. I had no idea about this until I inserted the original games sound engine.

Once I had the sound engine running on top of everything else going on, the slowdown began.

So, once the specific scene of the story that experienced the slowdown occurred, I snapped the bsnes debugger at the start of my NMI, i then EA'd out $420B (initiate DMA transfer), resumed the port/rom, and the entire scene played at full speed!

So, there is my dilemma, if this is supposed to run every frame, i'm in alot of trouble since i'm DMA'ing that many bytes.

Will HDMA solve this issue? If so I'm not familiar with HDMA, I know it uses the same addresses to set it up, but uses $420C for it's initiation.

Also, @Patrick ER, i haven't read your responses yet, but i sort of have the sound engine running, triangle is barely noticeable, but ill come back to this once I can figure out my DMA issue, thank you for writing, I will read it all. :-)
Last edited by infidelity on Sun May 02, 2021 3:05 am, edited 1 time in total.

Myself086
Posts: 75
Joined: Sat Nov 10, 2018 2:49 pm

Re: Need guidance with nes to snes.

Post by Myself086 » Sat May 01, 2021 3:35 pm

infidelity wrote:
Sat May 01, 2021 2:08 pm
Will HDMA solve this issue? If so I'm not familiar with HDMA, I know it uses the same addresses to set it up, but uses $420C for it's initiation.
HDMA is the same as DMA with some minor differences, the most important is that it only transfers 1 unit per active scanline. I use it in my sound data transfer because a delay is required between each write.


It's hard for us to know what you should be doing next because we can't see the whole picture. We'll talk more about it in a private direct conversation, text or voice.

93143
Posts: 1370
Joined: Fri Jul 04, 2014 9:31 pm

Re: Need guidance with nes to snes.

Post by 93143 » Sat May 01, 2021 3:36 pm

HDMA is not faster than regular DMA at anything regular DMA can do. It's a bunch of small transfers in between scanlines, so the overhead associated with a single DMA transfer gets applied multiple times, and the base data transfer rate is the same, so HDMA can only be slower. You also can't assume the transfer has completed once $420C is written, because the transfer is broken up into bits with code running in between, rather than being a single block transfer that happens immediately after writing $420B. Finally, there is an absolute limit of ~7 KB per frame (depends on overscan setting) when using HDMA, because it can only transfer a maximum of 4 bytes per channel per scanline and doesn't run during VBlank.

Like I said before, for minimal code changes it's probably best to have multiple banks of ROM, with everything identical except for the data you want to bankswitch. That way, you can just substitute SNES bankswitching for MMC3 bankswitching, rather than wasting time on DMA. If you're using DMA to VRAM at any point, this does require you to either keep track of the current value of B or do phb; pla or something like that to obtain the source bank byte, but that shouldn't be a big deal compared with using a 16 KB DMA to WRAM every frame...

If you're getting stack corruption because of what the interrupts are doing, it usually means you're not handling the interrupts correctly. You absolutely have to set everything to 16-bit before pushing at the beginning of the interrupt, and if you change the setting during the interrupt you have to set everything to 16-bit again before pulling at the end (honestly, you should probably do that anyway unless you're in an incredible hurry). The processor should push and pull the status register automatically, so the interrupted code will not see a change in the register width settings. There should be no way for this to go wrong if you do it properly.

Now, if you're regularly changing the stack pointer and the interrupt routine expects certain values on the stack, that could cause problems. But that would have nothing to do with register width, and seems like bad code design...
First let me explain the stack thing i did. Before, I was getting the value the original game wants/has modified to be inserted into the stack, transfering that low byte of the stack into A, setting A,X,Y to 16bit, adding #$100 to A so that the A value is #$1xx, transferred A to X, then transferred X to the stack, then set A,X,Y back to 8bit.
You really should try to describe what you're doing better. You can't just expect everybody to read your mind; it makes it nearly impossible to tell what you're saying when you do stuff like quoting important hex values wrong and making no distinction between the stack and the stack pointer.

infidelity
Posts: 485
Joined: Fri Mar 01, 2013 4:46 am

Re: Need guidance with nes to snes.

Post by infidelity » Sat May 01, 2021 4:34 pm

93143 wrote:
Sat May 01, 2021 3:36 pm
HDMA is not faster than regular DMA at anything regular DMA can do. It's a bunch of small transfers in between scanlines, so the overhead associated with a single DMA transfer gets applied multiple times, and the base data transfer rate is the same, so HDMA can only be slower. You also can't assume the transfer has completed once $420C is written, because the transfer is broken up into bits with code running in between, rather than being a single block transfer that happens immediately after writing $420B. Finally, there is an absolute limit of ~7 KB per frame (depends on overscan setting) when using HDMA, because it can only transfer a maximum of 4 bytes per channel per scanline and doesn't run during VBlank.

Like I said before, for minimal code changes it's probably best to have multiple banks of ROM, with everything identical except for the data you want to bankswitch. That way, you can just substitute SNES bankswitching for MMC3 bankswitching, rather than wasting time on DMA. If you're using DMA to VRAM at any point, this does require you to either keep track of the current value of B or do phb; pla or something like that to obtain the source bank byte, but that shouldn't be a big deal compared with using a 16 KB DMA to WRAM every frame...

If you're getting stack corruption because of what the interrupts are doing, it usually means you're not handling the interrupts correctly. You absolutely have to set everything to 16-bit before pushing at the beginning of the interrupt, and if you change the setting during the interrupt you have to set everything to 16-bit again before pulling at the end (honestly, you should probably do that anyway unless you're in an incredible hurry). The processor should push and pull the status register automatically, so the interrupted code will not see a change in the register width settings. There should be no way for this to go wrong if you do it properly.

Now, if you're regularly changing the stack pointer and the interrupt routine expects certain values on the stack, that could cause problems. But that would have nothing to do with register width, and seems like bad code design...
First let me explain the stack thing i did. Before, I was getting the value the original game wants/has modified to be inserted into the stack, transfering that low byte of the stack into A, setting A,X,Y to 16bit, adding #$100 to A so that the A value is #$1xx, transferred A to X, then transferred X to the stack, then set A,X,Y back to 8bit.
You really should try to describe what you're doing better. You can't just expect everybody to read your mind; it makes it nearly impossible to tell what you're saying when you do stuff like quoting important hex values wrong and making no distinction between the stack and the stack pointer.
Understood on the HDMA. I'll come up with an alternative method for these Sprite data banks. I understand the duplication of banks, which I've been doing with $C000-$FFFF, I just liked the idea of swapping banks into the wram, but again I didnt realize this section of the game was running this code every frame.

My NMI does not start and finish in a 16bit state. I've tried implementing that several different time and it just doesn't work for me, I understand the SNES pushes the processor automatically, but the original game deliberately pushes the processor as the first operation when the NMI is initiated. And I've tried a couple different things to get 16bit to work via pushing and pulling, but with this rom I've had a hard time doing so.

I no longer have stack corruption. The corruption was not due to my pushes and pulls, it's due to the NMI not finishing in time, and when the refiring of the NMI occured, it would happen dead smack in the middle of a 16/8bit swap, and that's what was crashing me. But now that I know why I was getting the slowdown and shut that part of the code off, I no longer got crashes or slowdowns.

Sorry for posting hex in the code window, I know how to write it out, its just how I was taught almost 20 years ago, and sorry for not being more detailed.

update - I fixed the slowdown issue. I added a register to determine if the Sprite bank being loaded, is the same as the register i have set aside. Game runs 100% now.

Time to figure out the upernes sound engine[=s]
Last edited by infidelity on Sat May 01, 2021 8:20 pm, edited 2 times in total.

93143
Posts: 1370
Joined: Fri Jul 04, 2014 9:31 pm

Re: Need guidance with nes to snes.

Post by 93143 » Sat May 01, 2021 5:05 pm

infidelity wrote:
Sat May 01, 2021 4:34 pm
My NMI does not start and finish in a 16bit state. I've tried implementing that several different time and it just doesn't work for me
There aren't really any subtle gotchas as far as I'm aware; it should work in every case if you do it right.

The only potential issue here is stack overflow. If you're too close to the end, the extra bytes could push you past the available range and corrupt a different stack or something else in RAM.
I understand the SNES pushes the processor automatically, but the original game deliberately pushes the processor as the first operation when the NMI is initiated.
That shouldn't matter (and you can probably remove it). The key is to set the registers to 16-bit after P is pushed (so what you're pushing is what the interrupted code was using) but before A/X/Y are pushed. After pushing A/X/Y, you can then set them back to 8-bit. At the end, reset the registers to 16-bit before pulling their values off the stack, and then just pull P (or don't, if you removed the explicit push at the start; RTI by itself should do the trick).
I no longer have stack corruption. The corruption was not due to my pushes and pulls, it's due to the NMI not finishing in time, and when the refiring of the NMI occured, it would happen dead smack in the middle of a 16/8bit swap, and that's what was crashing me. But now that I know why I was getting the slowdown and shut that part of the code off, I no longer got crashes or slowdowns.
If I were you, I'd fix the interrupt handling before going any further. Something else might trip over it.

I guess if stack overflow is a risk, you can either (a) move the stacks so you have more space, taking advantage of the extra WRAM, or (b) don't bother fixing the interrupt handling, but make completely 100% sure that you never, ever use 16-bit registers when there's any possibility of an interrupt, for the entire rest of the development of your port. Since you're doing a NES port, (b) might be easier than it sounds...
Sorry for posting hex in the code window, I know how to write it out, its just how I was taught almost 20 years ago, and sorry for not being more detailed.
The hex isn't the problem. It's a small hurdle. I've coded in hex before myself. The issue is when it's wrong (which has happened before when you've used the wrong register address in a post), or when your terminology is misleading (as in the above post where you use "stack" when you apparently mean "stack pointer"). We aren't you, and we can't read your posts with your true intent in mind because we don't know your true intent. Make sure it comes across in hard text.

infidelity
Posts: 485
Joined: Fri Mar 01, 2013 4:46 am

Re: Need guidance with nes to snes.

Post by infidelity » Sat May 01, 2021 8:19 pm

Thank you, 93143. After reading this thoroughly, I've decided to go back from the update I just did earlier, (setting a flag limiting the usage of the DMA routine I wrote) and do as you suggested by reading those Sprite tables in rom, it's late for me and will finish it up in the morning, and I'll report back. I apologize regarding terminology, I respect your criticism regarding my wording/phrasing, I always try be thorough and clear with my intentions, I'll do better.

93143
Posts: 1370
Joined: Fri Jul 04, 2014 9:31 pm

Re: Need guidance with nes to snes.

Post by 93143 » Sat May 01, 2021 8:43 pm

Sorry if I'm a bit short-tempered lately...

I just remembered one more thing. An interrupt causes the program bank to reset to $00 (it's recovered when returning from the interrupt), so if there's any code in the bankswitched sections you may want to do a long jump to the desired bank at the beginning of every interrupt handler.

Oziphantom
Posts: 1163
Joined: Tue Feb 07, 2017 2:03 am

Re: Need guidance with nes to snes.

Post by Oziphantom » Sat May 01, 2021 11:12 pm

depending upon the needs you might be able to do the upper sprite address shift rather than a full bank copy
2101h - OBSEL - Object Size and Object Base (W)

7-5 OBJ Size Selection (0-5, see below) (6-7=Reserved)
Val Small Large
0 = 8x8 16x16 ;Caution:
1 = 8x8 32x32 ;In 224-lines mode, OBJs with 64-pixel height
2 = 8x8 64x64 ;may wrap from lower to upper screen border.
3 = 16x16 32x32 ;In 239-lines mode, the same problem applies
4 = 16x16 64x64 ;also for OBJs with 32-pixel height.
5 = 32x32 64x64
6 = 16x32 32x64 (undocumented)
7 = 16x32 32x32 (undocumented)
(Ie. a setting of 0 means Small OBJs=8x8, Large OBJs=16x16 pixels)
(Whether an OBJ is "small" or "large" is selected by a bit in OAM)
4-3 Gap between OBJ 0FFh and 100h (0=None) (4K-word steps) (8K-byte steps)
2-0 Base Address for OBJ Tiles 000h..0FFh (8K-word steps) (16K-byte steps)
With 4-3 you can shift sprite tiles $100+ up memory by 4KWords. If you can fit the tiles into VRAM and the banks have enough overlap on the animated tiles.
You should also check the "sets" for duplicate tiles, and then not copy them, this will drop the amount you need to DMA each time.
With the crashes and not handling NMI properly, watch the video I linked and make sure your NMI matches the exact order 100% and those should go away. However the 65816 needs more stack so you might want to expand the stack more. i.e make it $100 $200 and $300 not 100 155 1AA etc

calima
Posts: 1373
Joined: Tue Oct 06, 2015 10:16 am

Re: Need guidance with nes to snes.

Post by calima » Sun May 02, 2021 12:04 am

From the description where xba fixed things, you weren't clearing the high A byte. Switching to 8-bit mode does *not* clear high A, so if something else happened to be there, you just added 0x1 (0x100) to a random value.

infidelity
Posts: 485
Joined: Fri Mar 01, 2013 4:46 am

Re: Need guidance with nes to snes.

Post by infidelity » Mon May 03, 2021 2:54 am

Oziphantom wrote:
Sat May 01, 2021 11:12 pm
However the 65816 needs more stack so you might want to expand the stack more. i.e make it $100 $200 and $300 not 100 155 1AA etc
I will definitely keep an extra eye on this, the original rom doesnt seem to reach 20/25 bytes into its stacks. But now that I am beginning coding in the game play, I will pay close attention to the stacks.

Patrick FR
Posts: 78
Joined: Tue Jan 19, 2010 10:35 am
Location: Lyon

Re: Need guidance with nes to snes.

Post by Patrick FR » Mon May 03, 2021 12:09 pm

I also thought about your sprite problem, and it is documented a little in the memory map txt files in upernes. Like said by Oziphantom, you put each sprite bank in different VRAM locations (if you have enough VRAM) and then instead of switching between the banks using a DMA copy, you just change the base address of your sprites instead of the DMA copy (if it is already in VRAM, if not then DMA).

So it goes like this:

SpriteBankSwitching

if (sprite_bank_inVRAM(bank))
{
changeSpriteVramBaseAdress(bank);
}
else
{
DMACopy(bank);
}

But for what I remember it is tight, especially for "Super Mario brothers 2". But it may work for your game. It depends on the number of sprites banks involved.

Post Reply