Understanding 6502 assembly on the Commodore 64 - (17) Stack and CPU Register in depth

     We need to stop for a minute with our programming, and discuss the registers that the 6502 uses.  We could then continue where we left off, perhaps, EXACTLY where we left off, having personally gained new information about the processor functionality, and continue on our merry way.  In fact, we could state that we are INTERRUPTING our normally scheduled programming to bring you this important information, and then upon completion return you to where you were.






     No, this is not a lesson on interrupts, no IRQ's or NMI's today.  We can't discuss them until the register is fully understood.  The register, as displayed with the R command in the monitor, is the Dashboard of the processor. This is where the processor is RIGHT NOW.  RIGHT NOW is important, because if we had to do something completely different, we would want to save RIGHT NOW somehow so we could load RIGHT NOW later.  In which case it would then be referred to as RIGHT WHERE WE LEFT OFF.

     I know this sounds confusing as first, but eventually that magical moment will happen when it all makes sense. Consider the screenshot below.  Forever static, and never to change, we see all of the values that I've attained with the R command.  If tomorrow, you ever wondered what the registers were when I pressed R, just have a look at the screenshot.  It is in fact visually recorded for you.




    So we know that somehow we can save the status of the processor, do something else, and reload the status as if nothing happened.  We first need to know what everything means, or our information would be a bit useless.  You might be surprised that you know already a good portion of the contents. We will start off with what we know first, to get it out of the way.

     A quick note here, IRQ is NOT a processor register Neither is BK (CURRENT BANK), its added by TFC3 as an added bonus.  We will not be discussing this as it is beyond the scope of this chapter, pretend it is not there.

PC     AC     XR     YR     SP     NV#BDIZC



PC - This is our program counter

     When we started our program with SYS 49152 what we were telling the computer to do was to go to memory location C000 and start executing our program from that location.  the PC tells us at the very moment you are looking at the registers, which line of code in memory is next to be executed.  When we stepped through a program in chapter Chapter 13 Watching the 6502 Think, we saw the program running step by step.  Remember, the instruction is the OPCODE, and there may be some data behind it.

r PC=C000 brought us to our NOP, it was 1 byte, you see our scheduled instruction at C001.  I do this to see the code I started at without having to subtract 1 [HEX] from the total

     We see below LDA (A9) on C001 : That is an instruction,  #$55 (55) : C002 is the data,  the next instruction is at C003, STA (8D), followed by more data.  This is the PC, simply stated, where the current code we are executing is.




AC,XR,YR - This is our Accumulator, X reg and Y reg

     This one is easy to understand. What are the current contents of A X and Y.  Looking above at the same picture.


We see at line C001:

AC:00 XR:00 YR:0A

We LDA #$55

after this instruction we would see

AC:55 XR:00 YR:0A

Thats it.  It simply shows what is currently in A,X and Y


SP -  This is the STACK POINTER

     This one will take a little bit of time, first we have to discuss what the stack is.   The stack is a temporary storage area for the processor.  It is mapped to memory just after the ZERO PAGE starting at 0100 to 01FF.   Be advised, this does not function like regular memory, there are rules and procedures for engaging it, any why, up till now, I've avoided using it in any demonstration.

     Stack isn't just a pretty name, it is in fact a literal stack.


Imagine the stack was empty.


-----------------------


and we add a byte to it, lets say 3F


            3F
-----------------------



and we add a byte to it, lets say 55


            55
            3F
-----------------------



and we add a byte to it, lets say FF


            FF
            55
            3F
-----------------------



and we add a byte to it, lets say 09

            09
            FF
            55
            3F
-----------------------

We are in fact stacking bytes, one on top of the other.

Here comes Rule 1:

We can only add to the top of the stack, and we can only pull off from the top of the stack.

Ill repeat myself here

We can only add to the top of the stack, and we can only pull off from the top of the stack.

LAST IN.......... FIRST OUT!!!!!!

We do not use our regular storage procedures for the stack, that is to say we would not do an STA $0103 to get something located within the stack,  this would no doubt cause the computer to crash.


No, in fact we have OPCODES entirely devoted to the stack
PHA, PLA, TXS, TSX, PHP, PLP


PHA - Push value in A to stack
PLA - Pull value off of stack and store it in A

TXS - Push value in X to stack
TSX - Pull value off of stack and store it in X

PHP - Push our processor registers [NV#BDIZC] onto stack
PLP - Pull our processor registers off the stack and store it in A


     The opcodes are straight forward, but we need to see what happens when these instructions are executed.  In order for the computer to keep track of how much is stored in the stack the value SP is incremented and decremented.

     The Stack pointed works backwards from conventional thinking.   If the stack were empty, the value in SP would be FF.  If we PUSH something onto the stack, the value of SP would decrement to FE, a third addition to the stack would bring us to FD.  Like wise, if we PULL a value off, it would increment to FE, and pulling another value off would increment to FF.

     In order to correlate the STACK POINTER with the memory space it occupies, just add $0100 to the SP value to see where the next BYTE pushed to the stack would be stored.  Remember, the stack occupies the space from $0100 to $01FF

If the SP displayed FF and we pushed our first value 3F:
     3F would be stored in (FF + 0100) or 01FF
     The stack pointer would drop to FE

If the SP displayed FE and we pushed our next value 55:
     55 would be stored in (FE + 0100) or 01FE
     The stack pointer would drop to FD

If the SP displayed FD and we pushed our next value FF:
     FF would be stored in (FD + 0100) or 01FD
     The stack pointer would drop to FC

If the SP displayed FC and we pushed our next value 09:
     09 would be stored in (FC + 0100) or 01FC
     The stack pointer would drop to FB


AND THEN......

If the SP displayed FB and we pulled our last value 09 into A:
     09 would be stored in A
     The stack pointer would rise to FC

 If the SP displayed FC and we pulled our last value FF into A:
     FF would be stored in A
     The stack pointer would rise to FD


Ok, I think I've driven this point home.  Heres another important note though, If you use the stack, that is to say, if you PUSH anything to the STACK, you should pull it off.

Here comes Rule 2:

 Do not write code that adds something to the stack that it doesn't ultimately remove.  This will lead to trouble (and wasted cycles and space).


     The primary use for the stack is for interrupts.  Which stop our program and do something entirely different, and then return to our program as if nothing happened.  In case you didn't know, our program is interrupted 60 times per second for the computers housekeeping chores. Refresh screen, blink cursor, that sort of stuff. Each time its interrupted  our current registers are stored to the stack, and restored after the housekeeping is finished. Now its time for a quick and dirty demo


; Quick and dirty stack test, works on
; all 6502 computers
; 64TASS assembler style code for 6502
; Jordan Rubin 2014 http://technocoma.blogspot.com
;
; Load stuff on the stack and pull it off


*=$C000 ; SYS 49152 to begin     


nop


lda #$3F

pha      ; push #$35 to stack
lda #$55
pha      ; push #$55 to stack
lda #$FF
pha      ; push #$FF to stack
lda #$09
pha      ; push #$09 to stack

pla      ; pull off top (#$09)

pla      ; pull off top (#$FF)
pla      ; pull off top (#$55)
pla      ; pull off top (#$09)

brk



     With this, we load our four values one at at time from A, and then pull them back off into A, running this won't show us anything interesting, we need to step it out....



     Notice after each PHA instruction, the next line shows the SP to be decremented by 01.  When we look at the bottom of the program we see that after each PLA instruction the SP is incremented by 1 and the A has the value that was pulled off the stack. As we can see, when the program was started, the stack was NOT empty, it generally isn't, and is used constantly by other operations within the computer.

     In summary, SP indicats where the next value (BYTE) would be stored on the stack


NV#BDIZC - Our processor flags 

     This part of the register deserves its own chapter, in summary, these are the flags that become enabled and disabled through our tests and comparisons that we conduct throughout our program.


     Some examples we've used are CMP CPX CPY.  Its actually a lot more involved than simple comparisons and deserves to be broken down into further detail.

NEXT----->
Understanding 6502 assembly on the Commodore 64 - (18) Status flags NV-BDIZC


Table of contents

No comments:

Post a Comment