Hi,
I've been writing a SNES demo to be released at Revision 2019, which happens next weekend. Development has been going well so far, but a few days ago I decided to put it in an SA-1 cart and test it on hardware.
For some bizzare reason, despite working perfectly in BSNES, it does not work on hardware. It shows a pink screen, which means that the main CPU code is running, but the main part of the demo does not play.
Over the past few days I've tried everything I could think of to make it work, including copying config code from Jumpin' Derby but the BSNES one still works and the cart still doesn't.
I've come up with one of two options:
1. My SA-1 cart is rubbish. This is the better option. In order to test this I need the help of someone else with a SA-1 cart to test it on their system, and then I can try and fix my own cart. I'm not sure why this would be the case ( the demo starts and shows a pink screen) but is still a possibility.
2. There is an inaccuracy between BSNES and hardware. This is more likely, and the worse option. In this case I'd need to probably send someone my code to try and help me debug it theoretically or something, as I can't rely on BSNES-Plus' tools to be accurate. (My code should technically work in mesen-s, but it doesn't support sa1 yet).
Both of these would need to be done in private preferably, because usually demos are kept secret until the party, but will probably make it public on Thursday if I haven't fixed it by then. Sorry for the short notice.
Thanks so much,
Molive.
I'm afraid this is a sore point for emulation.
It's only been very recently with the 21fx that I've had any ability at all to test the SA1. It's only been very recently that emulators are even remotely accurate with bus conflict timings.
The main difference I've heard of is that only the SNES CPU can access certain I/O registers, and only the SA1 CPU can access other I/O registers. But I don't know for sure which ones are which.
Beyond that, sorry to say, you're in a tough position. If we knew what was wrong, I'd definitely fix it. If you do find out, let me know and I'll fix it.
Yeah, one of the first things I did was make the main cpu run from WRAM, so the SA1 wasn't constantly tripping up over it, but that wasn't the issue.
I might try getting the SA1 to run from IRAM as well, as that might be faster than reading from rom...
Put in an SA-1 cart means booting from other hardware, and then hotswapping carts? EDIT: If yes, then you shouldn't read rom : )
Cart problem might be related to size/presence of BW-RAM (SRAM). And does the cart still work with its original retail ROM?
Software problems might be memory control, memory init, IRQs, or DMAs. Best add simple test codes on SA-1 entrypoint (like writing to IRAM, and then check if the result can be seen on SNES-cpu side) (and do something more meaningful on SNES-cpu side, eg. blink blue+pink, instead of relying on pink only).
For emulation problems, you could try 2-3 other emulators, if your code is unstable then it might behave differently in each emu.
I probably don't have whatever hardware setup that you might need, but I could have a look at the binary in no$sns debugger, maybe I can spot something in the init code or so.
EDIT: To debug main+sa1+apu CPUs in no$sns: Use Ctrl+T to toggle between the different CPUs. There is also an option to warn on invalid memory accesses, if you are lucky then it might instantly tell you what is wrong.
I have an SA1 cart a made a while ago which is game I have removed the ROM from and replaced with an IC socket. It takes over 10 minutes (I haven't timed it) to flash and test a 4MB ROM, which is why I didn't do it sooner. (they take another 10-15 minutes under UV to erase.)
I haven't tested the original game recently, but Kirby's Fun Pak was working a few days ago.
My demo doesn't access BW-RAM, or use SA1 DMA or IRQs (It used to, but the interrupts didn't respond fast enough. Guess they don't like being triggered over 30k times a second).
The SA1 is initialized the same way Jumpin' Derby is.
I am currently writing a test screen to test a few things that the SA1 does.
Quote:
Put in an SA-1 cart means booting from other hardware, and then hotswapping carts? EDIT: If yes, then you shouldn't read rom : )
Will that work on a stock SNES? Pulling the cart out will deadlock it. Breaking pin 4 off the CIC to allow hot-swapping will prevent SA1 games from booting.
But is the SA1 just locking out the ROM, or is it disabling the entire SA1? You couldn't really upload your own code to ROM anyway, so if the rest of the SA1 is functional, then that's the most viable path for most people to test SA1 code on through BWRAM.
If it's not known yet what I/O registers are accessible by what processor, I guess the recently released SA-1 implementation in SD2SNES isn't any more accurate than in bsnes or Higan?
Oh yeah: on SD2SNES it shows only black screen when I tested it.
In no$sns it's pink screen. You can see that for yourself if you have windows (or wine).
With the bad memory access warnings enabled it complains about:
2210h written by main cpu, that's not possible as far I know.
2261h written by main cpu, that's probably from the Jumpin Derby code? The register is undocumented and no other games use it.
Oh, and no$sns is bugging about setting the auto-increment bit in VBD register. That's not a bug, and it might work perfectly fine on hardware (the warning is just there to say that "this is first ever program seen setting that bit").
And, having quick look at SA-1 CPU, the PC seems to sit at what looks like a valid entrypoint. But it does seem to start execution. The state of the CCNT register can be viewed in Window --> Cart Profile (showing coprocessor registers, that 's, yeah, not so elegant, but it works). The CCNT register seems to be 20h (but, as far as I know both bit5 and bit6 should be cleared to "run" code on the SA-1 CPU).
Thanks for your help!
nocash wrote:
2210h written by main cpu, that's not possible as far I know.
Not sure where you got that, I write to TMC using the SA1.
nocash wrote:
2261h written by main cpu, that's probably from the Jumpin Derby code? The register is undocumented and no other games use it.
Yeah, that'll be the config settings.
nocash wrote:
Oh, and no$sns is bugging about setting the auto-increment bit in VBD register. That's not a bug, and it might work perfectly fine on hardware (the warning is just there to say that "this is first ever program seen setting that bit").
Does seem a bit odd that no other program used this feature.
nocash wrote:
The CCNT register seems to be 20h (but, as far as I know both bit5 and bit6 should be cleared to "run" code on the SA-1 CPU).
In order to start the SA1, I run "stz CCNT". I don't write to CCNT anywhere else.
So I rewrote my code to have a few checks at the start, and to display the results to the screen.
I just tested it on hardware, and all of them failed.
All I got was a black screen, which I thought was impossible - and then I realized: right after starting the SA-1 I wait on interrupt for it to get ready. If the interrupt didn't fire it means the SA-1 is running no code whatsoever. It's like I never turned it on (removing the stz CCNT produces identical results).
What on earth?
2210h is written at 01:8CFA, but now that you are saying it... no$sns is somewhat randomly showing "main" or SA-1" as current CPU in lower-right of the debug screen... that looks like a glitch in the no$sns debugger... What seems to be wrong there is: If you reset the emulation while the debugger is in SA-1 view... then the emu seems boot up with SA-1 and SNES cpu's swapped, whoops : )
Okay, but that glitch occurs only if SA-1 view was used before reset. When not doing that:
In that case 2200h is 00h (SA-1 running), that's fine. And the 2210h write is done on SA-1 side, that's fine, too.
APU is in an endless loop at 0284h? Main CPU is also in an endless-loop at 7F:0060 for audio streaming with several NOPs? And IRQs/NMIs are both disabled... hmmm. And SA-1 is somewhere at 01:8C7B.
And, btw. when also enabling the illegal opcode warning... at 00:8036 is that a reserved WDM opcode?
For the VDP auto-increment bit. I am not sure what the bit is doing at all... apart that it is supposedly incrementing something?
I think VDP is solely used by Jumpin Derby, and that one is having auto-increment disabled.
byuu wrote:
Quote:
Put in an SA-1 cart means booting from other hardware, and then hotswapping carts? EDIT: If yes, then you shouldn't read rom : )
Will that work on a stock SNES? Pulling the cart out will deadlock it. Breaking pin 4 off the CIC to allow hot-swapping will prevent SA1 games from booting.
Don't know for sure with SA-1. Hotswapping should work with most carts (as long as the CIC in the console is disabled, of course). And there should be some ways to boot SA-1 carts even the console's CIC is disabled, or if the PAL/NTSC region of the CICs don't match up (something like rewiring CIC CLK to GND or VCC, or installing an external CIC with separate Reset button in the SA-1 cart as last resort).
Which SA-1 register is accessible by which CPU isn't really unknown. It's documented in fullsnes.htm, and probably also in book2.pdf. At worst there might be 1-2 mistakes in those specs (and it lacks specs for undocumented registers like 2261h).
nocash wrote:
APU is in an endless loop at 0284h? Main CPU is also in an endless-loop at 7F:0060 for audio streaming with several NOPs? And IRQs/NMIs are both disabled... hmmm. And SA-1 is somewhere at 01:8C7B.
Yep, this sounds correct.
nocash wrote:
And, btw. when also enabling the illegal opcode warning... at 00:8036 is that a reserved WDM opcode?
BSNES-Plus can use WDM as a software breakpoint on the debugger. LibSFX can insert them with the macro "break" if in debug compile mode.
I wonder if it has something to do with cart headers? Do we know if the SA-1 checks them?
EDIT: it's not
Who on this forum has experience programming for SA-1?
So I've done another test where the SA-1 sends an interrupt as soon as it is turned on, and in software this interrupt fires but on hardware there is no such interrupt.
This confirms that the SA-1 is not even starting, which means it's nothing to do with IRQs or DMA or bus conflicts or anything like that.
This is so weird.
This is in the registers (so SNES-CPU uses ROM-vectors, and SA-1 uses the custom I/O vectors).
Code:
SA-1 Registers (W)
2200 00 80 A0 00 80 07 80 07 80 00 00 F0 00 00 00 00
2210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2220 80 81 82 83 00 00 80 80 04 FF FF 00 00 00 00 00
2230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2250 00 00 00 00 00 00 00 00 8A EE 6C FB 00 00 00 00
SA-1 Registers (R)
2300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SNES-IRQ vector destroys A register (and doesn't push/pop anything that might be changed when using another RAM vector).
The SNES and SA-1 handlers for IRQ don't seem to acknowledge IRQs to the SA-1 hardware (only to the GPU).
But the code that I am looking at seems to have IRQs disabled anyways.
For cases where IRQs should fire, did you make sure that everything is enabled (in SA-1 hardware, and also in CPU's I-flag), and old IRQs were acknowledged before enabling them? And of course, don't forget to acknowledge them before trying to return from the interrupt handler.
The APU streaming code looks as if it's all properly sync'ed to the APU speed, that should work (as long as the APU isn't faster than your SNES/SA-1 code, I haven't counted clock cycles for checking if the timings are too tight).
Other thing: 420Dh is set to Fast ROM? I don't think that SA-1 boards are supporting that.
But it shouldn't matter as long as SNES CPU leaves bank 80-FF unused (and it shouldn't affect any banks on SA-1 CPU).
If you have problems firing an IRQ: Fire a flag in I-RAM/BW-RAM instead. Maybe it will turn out that the SA-1 is running, and it's just the IRQ code that isn't working.
As byuu has said, CIC issues might also trigger some kind of copy protection (if you have a CIC-disable switch in the console, set that to CIC-enabled, at least if cart+console are for same region).
EDIT: To trigger the SA-1 entrypoint/reset vector, one should normally set CCNT bit5 for a moment.
Though the bit may be set automatically at power-up, so maybe you won't need to set it manually in that case.
So, in short: it now works. kinda.
In long:
The issue was in the SA-1 boot. When booting, I leave emulation mode, jump to the correct bank, set up dpage and stack and then jump to a subroutine to set up the rest of the registers. This seems fine at first, until you include register 222Ah - IRAM write enable.
IRAM, by default, starts with no write capabilities. I turn it on in the subroutine where I set the rest of the registers, and I thought I didn't write to IRAM until I did that - except I did, by accident.
When jumping into the subroutine the SA-1 writes a return address to the stack - which is in IRAM. Because write enable is still off, this return address never gets written, and so when returning it jumps to whatever uninitialized memory is in the stack at power on - and crashes. The fix is simple, before jumping into the subroutine enable IRAM writes (I'm actually doing it before leaving emulation mode, because I was getting annoyed.)
I would've come across this earlier - except
BSNES let the write go through. In fact, I removed the code that enabled IRAM writes altogether and it ran fine. I assume that it's just entirely ignoring this register.
The SD2SNES' implementation also must not account for the stack writing to disabled IRAM, as that worked also.
I said earlier that it kinda worked: All of the VBP tests fail, and it just produces nonsense. I will look into this further and relay results in another post to this thread.
Thanks for all your help.
~Molive
So a write protected stack. I guess for emulators, protecting memory from stray writes isn't necessary, and if it's like the MMC1 for NES which there are several versions of with some that do not feature write protection and some that do, and since the header do not specify the version of MMC1 the game is designed for, ignoring the write protection feature is a way to make all games work.
But I guess emulating the write protection is useful for development (to discover bugs like this), or just for accuracy. There could, in theory, be a game that relies on the write protection to work.
Apparently my SA-1's version code is 23h.
Good to know. I'll add the 23h in fullsnes... until/unless somebody discovers chips or boards with other values.
Hmmm, 23h might come from the RF5A123 chip name. The next byte doesn't happen to return A1h, or does it? But theoretically the register should be only 8bit, not 16bit.
Or 23h could be an open-bus effect from reading 230Eh? But that shouldn't happen, at least not when reading the register from SNES-CPU side.
Stack with disabled write access is really nasty. I had looked if your code had initialized stack, and if it had enabled write access, but I didn't thought about checking if that was both done before the first stack write.
Emulating that kind of stuff is uncomfortable... it's rarely needed, and hurts performance... in this case one could probably implement two memory write functions; fast "memory write" code used when all ram is unlocked, and a slower "I/O callback" function used when the ram (or parts of it) are locked.
Did you get the variable-bit-length reads working? And are you stlll UV-erasing after each test? I guess a flash chip would make everything easier, even if it's too small to hold the whole demo. But flash chips with 16bit databus are probably even harder to get than those with 8bit databus... at least in traditional DIP package with 5V supply : /
One distracting thing about the variable-bit feature is that writing to VBD is said to trigger a manual-increment (which wouldn't be desired when using the auto-increment mode) (though that effect wouldn't hurt when re-iniatilizing the address register after the VBD write).
The specs are sounding as if variable-bit ROM address starts at 00:0000 (?) rather than at C0:0000. In that case your code might bug when using address FB:xxxx instead of 33:xxxx (bit23 is probably ignored, but bit22 would be part of the supported 8Mbyte ROM space). Some carts access the upper 4Mbyte via A22=1 (which may be ignored by smaller chips), and some via a separate /CS chipselect signal (which won't work unless you do have two ROM chips). I am not sure how to select 1 or 2 chip mode - there seems to be no I/O bit for that - best guess would be that it's selected by wiring SA-1 pin85 to GND or VCC.
On the other hand, if above is wrong, then 33:xxxx would be probably seen as a LoROM address (so that might make things worse).
EDIT: Just wondering if variable-bit ROM reads are done with 16bit databus... if yes, then the address would be needed to be word-aligned.
EDIT: Looking at Jumpin Derby, that uses D7:xxxx as variable-bit address, so the address isn't a zero-based ROM offset in that game (but that might be a bug in the game).
Quote:
I would've come across this earlier - except BSNES let the write go through.
When I enabled the write protection bits, some games stopped running. There are games that never clear it after boot.
Quote:
Apparently my SA-1's version code is 23h.
It's open bus. If you read from $230e you get $23. If you read from $80230e you get $80.
Vitor ran into that with his speed test:
https://github.com/VitorVilela7/SnesSpeedTestYou raise an interesting point though, is it one CPU's open bus, or does it depend on which CPU reads it? Can one CPU see the others' open bus value?
Going by the specs, the version can be read from SNES-CPU side only. Molive, where did you read it from?
Byuu, do you remember which SA-1 games don't initialize the write-protect bits, and for which memory... I-RAM or BW-RAM?
Just to be sure: Unlocking is done by setting the write-enable bits (not by clearing them).
Or did you mean GSU games? Those do have a write-protect function for "Backup RAM", but none of the existing boards is having that kind of memory installed, so the protect feature is having no function (most or all GSU boards do have normal "Game Pak RAM", but that isn't affected by the "Backup RAM" write protection).
nocash wrote:
Did you get the variable-bit-length reads working? And are you still UV-erasing after each test?
No, Yes. I have a few chips and I can erase 5 at a time, but it takes like 30mins to erase and 10 to flash.
I'm doing some more VBP testing, and it seems something along the lines of "Writing to VDA clears the lowest four bits of VBD" happens:
a = 8, i = 16
lda #%10001010
sta VBD ;VBD is now 8A
lda ...
ldx ...
stx VDAL
sta VDAH ;VBD is now 80
lda VDP ;Now barrelshifted 16bits a time.
You'd need to specifically set VDA first, and then set VBD. This matches the flow diagram in the manual.
I can't confirm this is true behaviour, only that something like this happens, and that swapping them gave me different results on hardware.
Unfortunately, I have no idea what it now means, it almost looks like it's reading from random nearby memory positions. I might just switch to fixed-mode.
nocash wrote:
Molive, where did you read it from?
It was CPU side. When running the test in BSNES it returns "1".
EDIT:
https://github.com/VitorVilela7/SnesSpeedTest also seems to have it as 1 in BSNES and 23h on console, based on the screenshots.
Quote:
It was CPU side. When running the test in BSNES it returns "1".
EDIT:
https://github.com/VitorVilela7/SnesSpeedTest also seems to have it as 1 in BSNES and 23h on console, based on the screenshots.
Read it again CPU side with lda $bf230e.
Also, the SA1 is definitely a case where using forks of my code from 8+ years ago isn't the best idea.
Unlike the people who forked Nestopia et al, my project's very much still active so they are missing out on improvements.
https://gitlab.com/higan/higan/blob/mas ... io.cpp#L17My current SA1 code emulates bus conflict timings, open bus for $230e, and does its best to support CPU vs SA1 only registers.
It should be available in bsnes v107.1 if you're not fond of higan.
Quote:
Byuu, do you remember which SA-1 games don't initialize the write-protect bits, and for which memory... I-RAM or BW-RAM?
Just to be sure: Unlocking is done by setting the write-enable bits (not by clearing them).
Unfortunately not, this was back in 2008 or so. I can re-enable the write protection, but I don't have testers to go through all the SA1 games anymore.
Quote:
Or did you mean GSU games? Those do have a write-protect function for "Backup RAM", but none of the existing boards is having that kind of memory installed, so the protect feature is having no function (most or all GSU boards do have normal "Game Pak RAM", but that isn't affected by the "Backup RAM" write protection).
You know honestly, it may be the case that I got bitten by the GSU and then decided to remove it from the SA1 as well. Seems unlikely but I can't say for certain.
That's a really good point about the write-protect, thank you for that! I could enable write protection conditionally based on the PCB IDs of SuperFX games.
byuu wrote:
Read it again CPU side with lda $bf230e.
Unsurprisingly, it's now BFh. Can the version code even be read?
In other news, it now works. I switched to using fixed-mode, which solved most of it, but then I made all the VBP sections word aligned (like nocash suggested) and that fixed the rest.
I thank you all for your help.
Molive wrote:
I'm doing some more VBP testing, and it seems something along the lines of "Writing to VDA clears the lowest four bits of VBD" happens:
a = 8, i = 16
lda #%10001010
sta VBD ;VBD is now 8A
lda ...
ldx ...
stx VDAL
sta VDAH ;VBD is now 80
lda VDP ;Now barrelshifted 16bits a time.
Okay, that's unexpected.
But it might somewhat make sense technically: The first word is pre-loaded at time when writing Address MSB, and that pre-loaded value shouldn't be affected by the shift-amount... so the hardware might intentionally force the shift-amount to be zero to "disable" the shifting on pre-load (or to shift-in the initial 16bits, as shift value "0" is treated as "16").
In manual mode, something similar might happen: Eg. when setting VBD=0Ah and then writing the address, then VBD might change to 00h or 80h (if it's 80h then you would see auto-increments to happen when repeatedly reading Data, despite of initially having disabled auto-increment by the VBD=0Ah setting).
Molive wrote:
You'd need to specifically set VDA first, and then set VBD. This matches the flow diagram in the manual.
Yeah, looks like so. Then writing VBD with bit7=1 apparently does NOT trigger any unwanted manual increments (I don't remember exactly where that theory came from... probably from snes9x's sa1.cpp source code).
byuu wrote:
Molive wrote:
nocash wrote:
Molive, where did you read it from?
It was CPU side.
Read it again CPU side with lda $bf230e.
Er, yes, sure, but which one, the SNES-CPU in the console, or the SA-1-CPU in the cartridge?
Interpreting the screenshots at
https://github.com/VitorVilela7/SnesSpeedTest isn't so easy.
The first screenshot (with "----" shown as SA-1 version) seems to be from the current source code version, showing [230e] read from both CPU's, and "--" and "--" meaning that both are open-bus (although the open-bus check on SA-1 side looks a bit quirky).
All other screenshots (with a single 2-digit hex value shown as SA-1 version) seem to be from older source code versions, and they are apparently showing [230e] from one CPU only (as for which one: one could probably find out by going through the older source code version).
byuu wrote:
thank you for that! I could enable write protection conditionally based on the PCB IDs of SuperFX games.
What condition? The write-protect register does exist on all GSU boards, but the corresponding "Back-up RAM" memory chip doesn't exist on any boards.
Searching for write protect posts... here:
http://www.smwcentral.net/?p=viewthread ... 54#p579554 you seem to have actually tried to emulate the GSU's "BRAMR" register and breaking various games with it - despite of no single game ever having used "Back-up RAM" chips : )
> What condition?
You mentioned it was two different types of RAM. SA1 has 1L0NxS and 1LxB boards. I presume you mean only one of them have the RAM type that can be protected.
> you seem to have actually tried to emulate the GSU's "BRAMR" register and breaking various games with it - despite of no single game ever having used "Back-up RAM" chips : )
Oh, I see. Well that rules out the SuperFX, but I also had trouble with SA1 games back then.
Okay I emulated SueprFX backup RAM with BRAMR write protection (pointless, but whatever) and SA-1 SWBE + BWPA + SIWP + CIWP write protection.
Kirby's Dream Land 3 fails to run when emulating CWBE.
The SNES CPU sets BWPA = 02 (to protect 400000-4003ff), and then the SA1 CPU sets CWBE = 00 (to disable writes to the protected area.) The SA1 CPU then starts writing to 4001ax and 40032x addresses. Per the documentation, these writes should fail, and yet they must not fail in order for the game to run.
So for now, CWBE isn't emulated.