News:

11 March 2016 - Forum Rules

Main Menu

SNES architecture quirk?

Started by Gideon Zhi, October 24, 2013, 04:45:23 AM

Previous topic - Next topic

Gideon Zhi

So one of the games I'm working on has compressed code, which is unpacked and executed from RAM. Most of this is text pointers, but I've uncovered a much more complex bit of code that needs some major retooling - in this case, tilemapping code. Rather than have to rebuild the compressed binary into the game every time I change the code, stuck the code at DBFA00 (lorom addr) in the game binary, and a JML $DBFA00 in the compressed binary. The JML gets loaded into RAM at $7E8D4C.

The issue is that while the code works in SNES9X, it's crashing the game in ZSNES and I don't have immediate access to that area of the game in BSNES.

Here's the code I wrote, with the original code commented out above it. Note that if I remove the comments at the top of the file, essentially replicating the original functionality just with the JML hooks, the code STILL crashes ZSNES, so it's not necessarily any of my actual code. My guess is that it's an architecture quirk with JMLs out of and back into RAM.

(Just as a bit of background, the text was originally 16-bit. My version is 8-bit with DTE support at A>=0x50. The adjustments to the tilemapping reflect that.)

lorom
;$7E/8D4C: B7 A0        LDA [$A0],Y [$7E:7A86]    A:75BE X:004B Y:0006 D:0200 DB:00 S:01FA P:envMXdizc HC:776 VC:239
;$7E/8D4E: C9 20        CMP #$20                  A:755D X:004B Y:0006 D:0200 DB:00 S:01FA P:envMXdizc HC:864 VC:239
;$7E/8D50: F0 31        BEQ $31 [$8D83]           A:755D X:004B Y:0006 D:0200 DB:00 S:01FA P:envMXdizC HC:920 VC:239
;$7E/8D52: 5A           PHY                       A:755D X:004B Y:0006 D:0200 DB:00 S:01FA P:envMXdizC HC:976 VC:239
;$7E/8D53: AD F2 0F     LDA $0FF2 [$00:0FF2]      A:755D X:004B Y:0006 D:0200 DB:00 S:01F9 P:envMXdizC HC:1038 VC:239
;$7E/8D56: EB           XBA                       A:7521 X:004B Y:0006 D:0200 DB:00 S:01F9 P:envMXdizC HC:1110 VC:239
;$7E/8D57: AD F9 0F     LDA $0FF9 [$00:0FF9]      A:2175 X:004B Y:0006 D:0200 DB:00 S:01F9 P:envMXdizC HC:1170 VC:239
;$7E/8D5A: C2 20        REP #$20                  A:2100 X:004B Y:0006 D:0200 DB:00 S:01F9 P:envMXdiZC HC:1242 VC:239
;$7E/8D5C: 87 3D        STA [$3D] [$7F:75BE]      A:2100 X:004B Y:0006 D:0200 DB:00 S:01F9 P:envmXdiZC HC:1328 VC:239
;$7E/8D5E: 1A           INC A                     A:2100 X:004B Y:0006 D:0200 DB:00 S:01F9 P:envmXdiZC HC:056 VC:240
;$7E/8D5F: A0 02        LDY #$02                  A:2101 X:004B Y:0006 D:0200 DB:00 S:01F9 P:envmXdizC HC:110 VC:240
;$7E/8D61: 97 3D        STA [$3D],Y [$7F:75C0]    A:2101 X:004B Y:0002 D:0200 DB:00 S:01F9 P:envmXdizC HC:190 VC:240
;$7E/8D63: 1A           INC A                     A:2101 X:004B Y:0002 D:0200 DB:00 S:01F9 P:envmXdizC HC:286 VC:240
;$7E/8D64: A0 40        LDY #$40                  A:2102 X:004B Y:0002 D:0200 DB:00 S:01F9 P:envmXdizC HC:340 VC:240
;$7E/8D66: 97 3D        STA [$3D],Y [$7F:75FE]    A:2102 X:004B Y:0040 D:0200 DB:00 S:01F9 P:envmXdizC HC:420 VC:240
;$7E/8D68: 1A           INC A                     A:2102 X:004B Y:0040 D:0200 DB:00 S:01F9 P:envmXdizC HC:516 VC:240
;$7E/8D69: A0 42        LDY #$42                  A:2103 X:004B Y:0040 D:0200 DB:00 S:01F9 P:envmXdizC HC:570 VC:240
;$7E/8D6B: 97 3D        STA [$3D],Y [$7F:7600]    A:2103 X:004B Y:0042 D:0200 DB:00 S:01F9 P:envmXdizC HC:650 VC:240
;$7E/8D6D: E2 20        SEP #$20                  A:2103 X:004B Y:0042 D:0200 DB:00 S:01F9 P:envmXdizC HC:746 VC:240
;$7E/8D6F: 1A           INC A                     A:2103 X:004B Y:0042 D:0200 DB:00 S:01F9 P:envMXdizC HC:808 VC:240
;$7E/8D70: 8D F9 0F     STA $0FF9 [$00:0FF9]      A:2104 X:004B Y:0042 D:0200 DB:00 S:01F9 P:envMXdizC HC:862 VC:240
;$7E/8D73: A5 3D        LDA $3D [$00:023D]        A:2104 X:004B Y:0042 D:0200 DB:00 S:01F9 P:envMXdizC HC:934 VC:240
;$7E/8D75: 18           CLC                       A:21BE X:004B Y:0042 D:0200 DB:00 S:01F9 P:eNvMXdizC HC:998 VC:240
;$7E/8D76: 69 04        ADC #$04                  A:21BE X:004B Y:0042 D:0200 DB:00 S:01F9 P:eNvMXdizc HC:1052 VC:240
;$7E/8D78: 90 02        BCC $02 [$8D7C]           A:21C2 X:004B Y:0042 D:0200 DB:00 S:01F9 P:eNvMXdizc HC:1108 VC:240
;$7E/8D7A: E6 3E        INC $3E [$00:023E]        A:2100 X:003F Y:0042 D:0200 DB:00 S:01F9 P:envMXdiZC HC:940 VC:244
;$7E/8D7C: 85 3D        STA $3D [$00:023D]        A:21C2 X:004B Y:0042 D:0200 DB:00 S:01F9 P:eNvMXdizc HC:1170 VC:240
;$7E/8D7E: 7A           PLY                       A:21C2 X:004B Y:0042 D:0200 DB:00 S:01F9 P:eNvMXdizc HC:1234 VC:240
;$7E/8D7F: C8           INY                       A:21C2 X:004B Y:0006 D:0200 DB:00 S:01FA P:envMXdizc HC:1302 VC:240
;$7E/8D80: C8           INY                       A:21C2 X:004B Y:0007 D:0200 DB:00 S:01FA P:envMXdizc HC:1356 VC:240
;$7E/8D81: 80 C9        BRA $C9 [$8D4C]           A:21C2 X:004B Y:0008 D:0200 DB:00 S:01FA P:envMXdizc HC:042 VC:241

org $DBFA00
  ;LDA [$A0],Y
  ;CMP #$20
  ;JMP $7E8D50

RomAddr:
  LDA [$A0],Y
  CMP #$50
  BCS TilemapDTE
  CMP #$20
  BNE TilemapRegular
  BRL Exit
 
TilemapDTE:
  PHY
  LDA $0FF2
  XBA
  LDA $0FF9
  REP #$20
  STA [$3D]
  INC A
  LDY #$40
  STA [$3D],Y
  INC A
  LDY #$02
  STA [$3D],Y
  INC A
  LDY #$42
  STA [$3D],Y
  SEP #$20
  INC A
  STA $0FF9
  LDA $3D
  CLC
  ADC #$04
  BCC $02
  INC $3D
  STA $3D
  PLY
  INY
  BRA RomAddr
 
TilemapRegular:
  PHY
  LDA $0FF2
  XBA
  LDA $0FF9
  REP #$20
  STA [$3D]
  INC A
  LDY #$40
  STA [$3D],Y
  SEP #$20
  INC A
  STA $0FF9
  LDA $3D
  CLC
  ADC #$02
  BCC $02
  INC $3D
  STA $3D
  PLY
  INY
  BRA RomAddr

Exit:
  JMP $7E8D83

assassin

- to be sure, it's not one of the games that needs special steps or packages to run with ZSNES in the first place, is it?
- i notice the commented out code will "INC $3E" if the addition to $3D overflows.  in contrast, the new code (in two spots) will "INC $3D".  that seems superfluous, with an "STA $3D" right after it.  is this intended?  yes, i realize it has nothing to do with crashing.
- following your theory about the dual JMLs, if you change the "JML $DBFA00" to "JSL $DBFA00", and the "JMP $7E8D83" to "RTL", will it run right?  alternatively, keep the "JML $DBFA00", and change the "JMP $7E8D83" to "PEA $8D82 / LDA #$7E / PHA / RTL" (err, i hope that pushing order is right).

lytron

Is a JMP with a 24-bit-address compiled correct? I only know it in combination with a JML, but it depends on which compiler you use.

denim

Quote from: lytron on October 24, 2013, 09:36:52 AM
Is a JMP with a 24-bit-address compiled correct? I only know it in combination with a JML, but it depends on which compiler you use.

Xkas 0.14 doesn't allow the use of JMP with 24-bit address. But maybe he is using another compiler.

Lenophis

If xkas v0.14 won't let you use a legitimate SNES instruction, there's a problem. Good thing it works in 0.06. :p This sounds more like a Zsnes issue, though.


https://ff6randomizer.codeplex.com/ - Randomize your FF6 experience!

Gideon Zhi

Quote from: assassin on October 24, 2013, 08:19:12 AM
- to be sure, it's not one of the games that needs special steps or packages to run with ZSNES in the first place, is it?
No. In fact, the reason I'm so concerned about this is that the original code works in ZSNES, it's just the new stuff that doesn't.

Quote
- i notice the commented out code will "INC $3E" if the addition to $3D overflows.  in contrast, the new code (in two spots) will "INC $3D".  that seems superfluous, with an "STA $3D" right after it.  is this intended?  yes, i realize it has nothing to do with crashing.
This is a typo, probably mostly arising from the fact that I wrote the code after midnight :p Thanks for pointing it out! (stupid 8-bit adders...)

Quote
- following your theory about the dual JMLs, if you change the "JML $DBFA00" to "JSL $DBFA00", and the "JMP $7E8D83" to "RTL", will it run right?  alternatively, keep the "JML $DBFA00", and change the "JMP $7E8D83" to "PEA $8D82 / LDA #$7E / PHA / RTL" (err, i hope that pushing order is right).

It still crashes in ZSNES if I use JSL, but it also still works right in SNES9X.

assassin

- ZSNES has a debugger (sort of).  are you able to pinpoint at what instruction the game stops running in it?

-
QuoteThe JML gets loaded into RAM at $7E8D4C.

how soon is this happening before the JML is executed?  maybe there's some (Z)SNES issue of it expecting a little wait for the RAM modify to be "recognized" before the new values there can be utilized.  i know nothing about SNES or ZSNES internals, and haven't heard of any "pre-fetching" here, so this is just a theory.  if you're running the JML very shortly after writing it, maybe put several NOPs in between the two acts.  (or more blatantly yet, a couple NOPs, a call to a dummy function in a far, non-RAM bank, that returns with RTL, and a couple more NOPs after return, _then_ execute the JML to RAM.)

try both of these countermeasures if you can, though the first step (ideally) is to glean what you can about the crash from ZSNES' debugger.  i say "ideally" because i've never been able to accomplish a damned thing in that debugger.

Gideon Zhi

#7
Quote from: assassin on October 24, 2013, 03:11:55 PM
- ZSNES has a debugger (sort of).  are you able to pinpoint at what instruction the game stops running in it?

try both of these countermeasures if you can, though the first step (ideally) is to glean what you can about the crash from ZSNES' debugger.  i say "ideally" because i've never been able to accomplish a damned thing in that debugger.

I can't even remember the last time I used ZSNES's debugger. I'm not even sure if ZSNES DOS works in Windows 7!

Any case, the JML goes loaded in a *long* time before it runs. It's part of the data that gets dumped into RAM when the game zones-in to this particular area, and there are about a dozen lines of dialog before that happening and the game running this particular display code.

Edit: Apparently, it works in ZSNES 1.50+ (I was testing the code using ZSNES 1.42). And since 1.42 doesn't have the debugger even if 1.50+ does, I can't test it any further, so. I guess that's the end of that? I'm going to have someone else test the game in bsnes to make sure it works.

KingMike

Since ZSNES is the least accurate of the three, I'd have let it be broken as long as SNES9x supports it, and wait until I could test to that point in bsnes. Does the game have saving (in which case you could make a regular save in SNES9x then transplant to bsnes)?
"My watch says 30 chickens" Google, 2018

lytron

#9
Quote from: Gideon Zhi on October 24, 2013, 04:45:23 AM
lorom
  LDA $3D
  CLC
  ADC #$04
  BCC $02
  INC $3D
  STA $3D


Just one short question: Does it make sense that you INC $3D right before you store the value in A in $3D? Doesn't it get replaced? Do you probably rather mean "INC A"?

EDIT: Sorry, just saw that you already were pointed to it.

furrykef

Quote from: denim on October 24, 2013, 12:08:00 PM
Xkas 0.14 doesn't allow the use of JMP with 24-bit address. But maybe he is using another compiler.

Assemblers aren't compilers. Compilers translate high-level languages to another language (often assembly or pure object code). An assembler just provides a convenient human-readable syntax for binary code. (Of course, real assemblers usually also provide convenience features such as macros, but whatever.)

Bisqwit

The line between assemblers and compilers is rather narrow really, especially considering features such as jump size optimization, aligning and so on.
I would not pedantically uphold the difference. Although I wouldn't ever call a C++ compiler an assembler.