3
votes

I have to answer the following question about the 6502 assembly language:

"On the stack, there are the following values (top element first): 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 At address 0xc000 is the instruction jsr 0xABCD. Which value will be stored in the program counter after the fetch/execute cycle and what will be the top element of the stack?"

I know that the program counter will be 0xABCD, but I'm confused about the stack elements. I know that in the 6502 architecture, the stack grows from the top to the bottom (from 0x01FF to 0x0100). So, I assume, the stack pointer is pointing to the element 0x01, right?

Now, the return address should be program counter + 3, because there is the next command, so I would say, 0xc003 will be pushed on the stack, but in little endian order, so c0 will be the top element. Is that correct?

2
I would call the starting point of the stack (0x1ff) the bottom and 0x100 the top. In LE machine the consistent model would be to store '03' in a lesser address.Aki Suihkonen
Just a warning: In 6502, JSR pushes (return address - 1) to the stack. That is, RTS increments it after pulling.cyco130
@cyco130 is there any rationale behind such a design? I find it quite weird to store the address-1 instead of the actual address of the next instruction the execution will continue from.BarbaraKwarc
@BarbaraKwarc I don't have a definitive answer, I can only speculate: I know the 6502 always fetches two bytes on the first two cycles of each instruction. For 1-byte opcodes, it throws the extra one away and doesn't further increment PC (which was already incremented to fetch the thrown away second byte). For 2-byte opcodes, it uses both bytes and does increment PC. And for 3-byte ones, it increments PC, fetches the final byte, uses it, and increments the PC once more. In JSR (a 3-byte opcode), it must be pushing the program counter to the stack before incrementing PC for the last time.cyco130
Yeah, my guess was that incrementing the PC is the last stage of every machine cycle, hard coded, so it has to do it anyway (after popping it from the stack), and to compensate for that, it pushes the address 1 less (which isn't that hard, if it pushes it before incrementing the PC in the last stage). But it would be nice to have it backed up by some official documentation. And the fact that the conditional jumps use the address of the next instruction for calculating the offsets, kinda puts me off track with this speculation :qBarbaraKwarc

2 Answers

5
votes

Start with the S register equal to $F9, meaning everything after that in the $0100 page is the stack. Memory contents are as follows:

$01FA: 01 02 03 04 05 06

$ABCD: A6 23       LDX $23
; rest of the body of the subroutine
$AC03: 60          RTS

$C000: 20 CD AB    JSR $ABCD
$C003: BD 40 06    LDA $0640,X

The JSR instruction pushes the address of the last byte of the instruction. In this case, the address of the last byte is $C002. The high byte is pushed first so that the low byte is in the lower address: push $C0, then push $02, then jump to $ABCD. After this, the stack looks like this, with $C002 in little-endian byte order at the top, and S has become $F7.

$01F8: 02 C0 01 02 03 04 05 06

The subroutine at $ABCD will end with the RTS instruction, here shown at $AC03. This instruction pulls the low and high bytes of the program counter. And then because the return address points at the last byte of the previous instruction, it adds 1. $C002 plus one is $C003, the address of the first byte of the next instruction in the caller.

1
votes

I believe what happens on a jsr is

stack[stack_pointer] = return_high
stack_pointer--
stack[stack_pointer] = return_low
stack_pointer--
pc = jsr address

so if you are claiming that your stack pointer is pointing at the 0x01 and 0x02 is a lower/smaller address then 0x01 and 0x02 will be overwritten and when you hit your subroutine the stack will be pointing at 0x03.