+  RHDN Forum Archive
|-+  Romhacking
| |-+  ROM Hacking Discussion
| | |-+  Stack Manipulation, RTS, and a Whole Lotta Weirdness
Pages: [1]
Author Topic: Stack Manipulation, RTS, and a Whole Lotta Weirdness  (Read 463 times)
RedComet
Guest
« on: May 18, 2007, 07:07:37 pm »

First some code:

Code:
$87/82F2 BF 88 88 87 LDA $878888,x[$87:88BA] A:0032 X:0032 Y:89F3 D:0000 DB:92 S:1FDF P:envmxdizcHC:1218 VC:000 00 FL:65106
$87/82F6 FA          PLX                     A:83B3 X:0032 Y:89F3 D:0000 DB:92 S:1FDF P:eNvmxdizcHC:1274 VC:000 00 FL:65106
$87/82F7 C8          INY                     A:83B3 X:0000 Y:89F3 D:0000 DB:92 S:1FE1 P:envmxdiZcHC:1318 VC:000 00 FL:65106
$87/82F8 48          PHA                     A:83B3 X:0000 Y:89F4 D:0000 DB:92 S:1FE1 P:eNvmxdizcHC:1340 VC:000 00 FL:65106
$87/82F9 60          RTS                     A:83B3 X:0000 Y:89F4 D:0000 DB:92 S:1FDF P:eNvmxdizcHC:0010 VC:001 00 FL:65106
$87/83B4 B9 00 00    LDA $0000,y[$92:89F4]   A:83B3 X:0000 Y:89F4 D:0000 DB:92 S:1FE1 P:eNvmxdizcHC:0060 VC:001 00 FL:65105

My understanding here is that the 16-bit value that's loaded from $8788BA ($83B3) is being pushed on the stack  to be used as the return address. That I follow perfectly, but when the RTS at $8782F9 is hit, instead of returning to $8783B3 like I thought it would, it returns to $8783B4. What the hell? :huh:

I checked the stack and the data at $8788BA and in both places it's $83B3, not $83B4. Just to clarify, I have not modified ANYTHING in this ROM. The thing that's really baffling is that there is an RTS at $8783B3, but if that were being executed the stack pointer should reflect that, which, as you can see above, it doesn't. Undecided\

Anyone have any ideas? Or some esoteric knowledge to share that might be of use?
Ryusui
Guest
« Reply #1 on: May 18, 2007, 07:16:39 pm »

The address on the stack is always incremented by one. I just checked the stack while running my Patlabor WIP: the address stored on the stack is 1 less than the address that is actually returned to.
RedComet
Guest
« Reply #2 on: May 18, 2007, 07:28:51 pm »

D'oh! I feel really dumb now.  :laugh:

Thanks, Ryusui. Smiley

EDIT: Double d'oh! It's Ryusui not Ryusei, isn't it? Tongue
« Last Edit: May 18, 2007, 09:11:21 pm by RedComet »
Ryusui
Guest
« Reply #3 on: May 18, 2007, 08:06:07 pm »

If it's not a bug, then it's a feature. :3

Like how TheCheat 2.0 will throw an error whenever you try to take advantage of its convenient "save project before exiting?" function, which it currently throws at you whenever you try to exit the program. ^_^;

byuu
Guest
« Reply #4 on: May 18, 2007, 09:25:30 pm »

Quote
The address on the stack is always incremented by one.

A little ambiguous. I'll clarify with the actual behavior for reference.

Here are the three JSR functions:

Code:
jsr_addr(0x20) {
1:aa.l = op_readpc();
2:aa.h = op_readpc();
3:op_io();
4:regs.pc.w--;
  op_writestack(regs.pc.h);
5:last_cycle();
  op_writestack(regs.pc.l);
  regs.pc.w = aa.w;
}

jsr_long(0x22) {
1:aa.l = op_readpc();
2:aa.h = op_readpc();
3:op_writestackn(regs.pc.b);
4:op_io();
5:aa.b = op_readpc();
6:regs.pc.w--;
  op_writestackn(regs.pc.h);
7:last_cycle();
  op_writestackn(regs.pc.l);
  regs.pc.d = aa.d & 0xffffff;
  if(regs.e)regs.s.h = 0x01;
}

jsr_iaddrx(0xfc) {
1:aa.l = op_readpc();
2:op_writestackn(regs.pc.h);
3:op_writestackn(regs.pc.l);
4:aa.h = op_readpc();
5:op_io();
6:rd.l = op_readpbr(aa.w + regs.x.w);
7:last_cycle();
  rd.h = op_readpbr(aa.w + regs.x.w + 1);
  regs.pc.w = rd.w;
  if(regs.e)regs.s.h = 0x01;
}

As you can see, the address pushed onto the stack inside the JSR is actually decremented by one.

Here are the return functions:

Code:
rti(0x40) {
1:op_io();
2:op_io();
3:regs.p = op_readstack();
  if(regs.e)regs.p |= 0x30;
  if(regs.p.x) {
    regs.x.h = 0x00;
    regs.y.h = 0x00;
  }
4:rd.l = op_readstack();
5:if(regs.e)last_cycle();
  rd.h = op_readstack();
  if(regs.e) {
    regs.pc.w = rd.w;
    end;
  }
6:last_cycle();
  rd.b = op_readstack();
  regs.pc.d = rd.d & 0xffffff;
}

rts(0x60) {
1:op_io();
2:op_io();
3:rd.l = op_readstack();
4:rd.h = op_readstack();
5:last_cycle();
  op_io();
  regs.pc.w = rd.w;
  regs.pc.w++;
}

rtl(0x6b) {
1:op_io();
2:op_io();
3:rd.l = op_readstackn();
4:rd.h = op_readstackn();
5:last_cycle();
  rd.b = op_readstackn();
  regs.pc.d = rd.d & 0xffffff;
  regs.pc.w++;
  if(regs.e)regs.s.h = 0x01;
}

The RTS / RTL opcodes increment the value pulled off of the stack. But take a look at RTI, this increment does not happen. Why?

Code:
void sCPU::op_irq() {
  op_read(regs.pc.d);
  op_io();
  if(!regs.e)op_writestack(regs.pc.b);
  op_writestack(regs.pc.h);
  op_writestack(regs.pc.l);
  op_writestack(regs.e ? (regs.p & ~0x10) : regs.p);
  rd.l = op_read(event.irq_vector + 0);
  regs.pc.b = 0x00;
  regs.p.i  = 1;
  regs.p.d  = 0;
  rd.h = op_read(event.irq_vector + 1);
  regs.pc.w = rd.w;
}

Because the code that's executed when an interrupt triggers does not perform the decrement. This is an important difference, as you can just as easily use rti as a plp + rtl combo if you keep that one little difference in mind.

As for why the SNES does this, I'm really not 100% certain. I believe it is due to the implementation of jsr (addr,x). They likely modified the other two jsr routines so that the same rts / rtl opcodes could be used for all three jsr opcodes. Or maybe it's just to screw with people. It's unfortunately not like the branching opcodes where it's immediately clear why bra $00 branches to the end of that opcode, rather than back to itself. The PC is incremented each time you read in an opcode byte.

---

Now, though Wikipedia will tell you that bsnes is not open source ... if in fact you stop drinking Bruce Perens' Kool-Aid, you'll see that it, in fact, is. I'd strongly recommend downloading it and taking a look at the CPU core when you have questions like this. Not that we don't mind answering, but it'd be a neat way to see how things are really working behind the scenes. I wrote it really cleanly so you should be able to figure out what it's doing without having to study it for a long time or anything.
« Last Edit: May 18, 2007, 09:34:13 pm by byuu »
RedComet
Guest
« Reply #5 on: May 19, 2007, 10:41:27 am »

I think byuu just called me a n00b. Tongue

N00bness aside, that was quite informative and helpful byuu. Thanks for taking the time to write such a detailed reply. Smiley
Pages: [1]  


Powered by SMF 1.1.4 | SMF © 2006-2007, Simple Machines LLC