COURS 6.TXT
****************************************************************** * * * 68000 ASSEMBLER COURSE ON ATARI ST * * * * by The Ferocious Rabbit (from 44E) * * * * Lesson number 6 * * * ******************************************************************
THE STACK We have already used the concept of a 'tube' when it comes to memory. We can store different things in it, and if we remember the address, we can come back later to that place to retrieve what we left there.
Try with this small program:
MOVE.L #$12345678,D0 MOVE.L D0,$493E0 MOVE.L #0,D0 MOVE.L $493E0,D0
Assemble and then use MONST to step through. D0 is initially filled with $12345678, then the contents of D0 are transferred to address $493E0. Note that there is no # in front of $493E0, to indicate that it is indeed an address. Once this line is executed, activate window 3 ([Alternate+3]) and set the start of it to address $493E0 ([Alternate+A] then type 493E0) You can clearly see 12345678 at this location in the 'tube'. If I have chosen this address it's because it's located 300 Kilobytes from the beginning of memory. It is therefore accessible even on a 520, and it is far enough away not to be in GENST or MONST. Indeed there is only one memory 'tube'! So we are writing into memory while some of it is occupied by GENST and MONST! Writing within the areas oc- cupied by these programs is possible, which will very likely cause some crashes!
Let’s continue to step through; we set D0 to 0 then the contents of address $493E0 (without #) are moved back into D0.
The stack is a part of this tube, but that we will manage in a slightly different way. Indeed, instead of placing the data in the tube and noting their addresses, this time we will stack them and to retrieve them, unstack them. The advantage is the time saved (no need to wonder at which address we stored the data) and a space saving (if it's for temporarily storing data, no need to keep a portion of 'tube' just for that).
On the other hand, the disadvantage is that management must be ri- gorous. Let's imagine that I stack one first number then 10 others on top of it. Next, I unstack, but, mistake on my part, I only unstack 9 numbers! When I unstack once more, thinking I will find the first stacked number, I will actually re- cover the first of the series of 10.
We conclude 2 things from this: first, the stack is a sim- ple way to save data, but then it is a potential source of annoyance, such that some programmers he- sitate to use it. It's usually due to a lack of ri- gor, which I hope will not happen to you.
Another note: the last element placed on the stack will al- ways be the first to come out. This is the same principle as that of a stack of plates: Look at your home, there's certainly a huge stack of plates, but simply because storing after washing is done by stacking and setting the table is done by unstacking, you are actually always eating in the same plates... (hence the interest of doing the dishes well!)
This stack structure is called LIFO structure, that is Last In First Out. This structure is different from another structure frequently encountered in computing, that of the queue, also called struc- ture FIFO (First In First Out), the queue being similar to a waiting line in front of a counter: the first in line will be the first to leave.
But concretely, what is the stack for? We will see with an example. Type the following program:
MOVE.L #$12345678,D0 MOVE.L #$BD88,D1 MOVE.L #$BD88,A0 BSR ADD MOVE.L #0,D0 MOVE.L D2,D0
ADD MOVE.L #$11112222,D2
ADD.L D1,D2 RTS
First note: this program differs from the previous ones in that we are using a label, a label called ADD.
This word, 'ADD', must be found all the way to the left, against the edge of the editor window. It is not something to place IN the tube but rather a mark BESIDE the tube.
Another note, assembler listings, unlike lis- tings in other languages, are quite free in terms of presen- tation. It is quite possible to skip lines, which is done here to separate the 2 parts. Assembler sources are often very long, and even if it wastes some lines, spacing the modules allows to find one's way easier.
Assemble and then debug. Step through with Control+Z. The first 3 lines are familiar to us but not the fourth. This line reads BRANCH SUB ROUTINE ADD, meaning bran- ching to a subroutine called ADD. To specify which su- broutine one wants to go to, its label is specified. Here in this case it's ADD but the name doesn't matter much. It is all quite possible to put fairly long names and I can only advise you to avoid names like X Y, Z or AX1 etc... which are still less explicit than IMAGE_START, NEW_PALETTE or END_GAME.
Now be very attentive: upon reading this instruction many things will happen. The command therefore asks the 68000 to continue reading its instructions in a sub- program whose start is located in the tube, opposite the la- bel ADD. However, this is indeed a subroutine. This means that once completed, the 68000 will go back up to execute the following line of BSR ADD, namely MOVE.L #0,D0. Ques- tion: how will the 68000 know where to go back to? Indeed the nature of a subroutine is to be able to be called multiple times and from several different places and to be able to return each time to the very place that called it.
Well the 68000 will use the stack to note this place of return. This stack certainly has an address, where is it noted? In A7. Yes, this somewhat special register corresponds to the stack.
But A7' then? Well, it's also a stack, but reserved for the Supervisor mode. So if we were running 2 programs at the same time, one in user mode and the other in supervisor, each would have their own stack.
Before executing the line BSR ADD, observe carefully the address registers and the data registers.
We have seen that the registers, whether they are for data or address, can contain numbers coded over 32 bits. We have also seen that there are 2 types of numbers for the machine: those inside the 'tube' and those outside, AGAINST this tube, and indicating a sort of distance from the start of that tube.
This second type of number is called an address. However, it is quite possible to store a number representing an address in a data register (D0-D7). Let's imagine now that we needed to store a player's score in the game we are programming. This score will for example be placed in memory (in the 'tube') at address $80792.
But what will happen if we transfer this address to use with A1 for example? well A1 will take the value $80792. That's nice, but that's not what interests us. What we want to modify, check etc... it's what is IN the tube at this address.
Well our debugger anticipates a bit this request. Indeed, considering that the numbers stored in D0-D7 or A0-A6 can represent address values, it indicates next to the regis- ters, what is in the tube at the address indicated in the register.
For the data registers, MONST displays to their right the value of 8 bytes found in the tube at the address indicated in the register. For address registers, it is 10 bytes which are indicated. You have certainly noticed that in front of D0 register (which should contain $12345678 if you have done the program advance correctly), MONST has only displayed stars. This is normal because the number $12345678 corresponds to an em- placement that would only be accessible with 305 megabytes of memory!!! MONST therefore indicates that it cannot reach this memory area by displaying stars.
Now look at D1 and A0. The numbers on their right show the same thing, which is normal since both registers D1 and A0 are filled with the same number. We say they point to address $BD88. Let's check the memory just to verify the display. Activate window 3 with Alternate+3. This one dis- plays the contents of memory, but we are far from $BD88!
Let's ask that this address be the one at the top of window 3, with Alternate+A. We type this address (BD88). Window 3 is re-displayed with $BD88 at the top. On the right column we see the contents of memory, which we had already a peak with the display to the right of D1 and A0. Is it clear?
Reactivate window 1 (alternate+1). Normally the small ar- row should always be in front of BSR ADD. Note the number in register A7 (thus the address of the Stack) and watch carefully the numbers on the right of this register, while making Control+Z.
The numbers have changed! First the A7 register no longer contains the same number. The one currently there is indeed smaller than the previous one. Note that this difference is 4. The stack address has therefore been decremented by 4. Plus nu- mbers have been placed on the stack (they are seen to the right of the A7 register). Now, look at the number that is to the left of the instruction MOVE.L #0,D0 in our program, that is the address where the 68000 should return once the subroutine is complete: it is indeed this number that has been placed on the stack. So there is stacking of the return address, which also explains the change in the stack address by 4. Indeed an address is coded on 4 bytes!
Note: since we are talking about a stack, it is more common to say that the data are placed on the stack and less often in the stack.
Continue our program with Control+Z. We are now in the subroutine. Stop just before RTS. This ins- truction will make us "go back up". It reads RETURN FROM SUB ROUTINE.
Observe A7 (its value but also the contents of the 'tube' at this address) and take a step (Control+Z). The return address has been unstacked, A7 has resumed its old address and we now point to MOVE.L #0,D0.
Quit this program with Control+C, erase it and type this one.
MOVE.L #$12345678,D0 MOVE.L #$AAAAAAAA,D1 BSR ADD MOVE.W D2,D3
ADD MOVE.W #$EEEE,D1
MOVE.W #$1111,D2 ADD.W D1,D2 RTS
Assemble and then debug. Step through: D0 takes the value $12345678 D1 the value AAAAAAAA, then we depart towards the subrou- tine ADD.
Unfortunately this one uses D1 and on return we note that this one no longer contains AAAAAAAA. Indeed jumping to a subroutine only saves the return address, and in assembly local variables and other languages evol- ved features do not exist! It is therefore up to us to save the regis- ters, and this is what we will do now.
Note: the A7 register containing the address of the stack pointer (this address varying of course with the stacking and unstacking), we can consider this address as a finger permanently pointing towards the top of the stack. For this reason the A7 register is also called the stack pointer. As always we will use the English vocabulary, and we will say Stack Pointer, abbreviated as SP. For this reason and because it is customary, we will henceforth replace A7 with SP (which is not read "ess-pee" but STACK POINTER!!!).
Suppose we wanted to save D0 at the entry to the su- broutine:
We must not forget to retrieve it at the end! Let’s move the contents of D0 to the stack. Let's try MOVE.L D0,SP and think about it: This will put the contents of D0 into A7, unfortunately that's not what we want to do. Indeed we want to put the contents of D0 INSIDE the tube, at the point indicated by A7 (so SP).
This will be done with MOVE.L D0,(SP), the parentheses indicating that the source of the operation is the inside of the tube.
Erase the current program and type the following one.
MOVE.L #$12345678,D0 MOVE.L D0,(A0) MOVE.W D0,(A1)
Assemble and then debug as usual. D0 takes the value $12345678, then D0 is transferred in its entirety (due to the .L indicating that the operation is on a long word) to the address noted in A0, then the lower half of D0 is transferred into the tube at the address noted in A1. To verify this, you can activate window 3 and ask to place the address noted in A0 at the top of this window, and you will see that indeed the value of D0 is in the 'tube'.
We are therefore going to use this type of transfer to save D0
But let's think a little. MOVE.L D0,(SP) will indeed place the contents of the long word D0 in the tube, but if we want to pla- ce another value on the stack, it is going to overwrite our first value because with MOVE.L D0,(SP) the address indicated by SP (thus A7) will not be modified, which should be the case.
We are therefore going to perform the transfer differently (in fact we are going to further improve our vocabulary, since we are going to talk about addressing type or mode now).
We are going to do MOVE.L D0,-(SP)
This is the addressing mode with pre-decrement. Behind this vo- cabulary hides a whole series of events. In a single instruction, we decrease the address of the stack pointer by 4 (since in our example we wanted to transfer a long word therefore 4 bytes), and we place the long word D0 in memory at this address.
To recover D0, that is to say unstack, we will have to:
MOVE.L (SP)+,D0
As we decremented the stack pointer to then de- posit D0 at this address, we then recover D0 without for- getting to modify the stack pointer in the other direction, to make it find its old position again. Note that in this case, and if we limit ourselves to think very summarily, it would have been possible to save D0 with MOVE.L D0,(SP) and to reco- ver it with MOVE.L (SP),D0. It's without counting that the stack is a common reservoir for many things. It is therefore bet- ter to play each time the game of a correct stacking and a re- flection but also an unstacking 'sticking' perfectly with the pre- vious stacking.
Let's verify all this with the following example: MOVE.L #$12345678,D0 value in D0 MOVE.W #$AAAA,D1 value in D1 MOVE.L D0,-(SP) saves D0.L on the stack MOVE.W D1,-(SP) same with D1 but in word MOVE.L #0,D0 sets D0 to 0 MOVE.W #0,D1 and D1 too MOVE.W (SP)+,D1 retrieves D1 (word) MOVE.L (SP)+,D0 then D0
Assemble and go through this program step by step under MONST. Note several things: first of all comments have been added to the source. It is enough that they are separated from the ope- rands for the assembler to know that it's about comments. If you want to type a line of comments (that is to say that on it there will be nothing else than this comment), you must precede it with an asterisk or a semicolon.
Second thing, we had stacked D0 then D1, then we un- stacked D1 then D0. Indeed, we must be very careful with the order and the sizes of what we stack, in order to unstack the same sizes, in the reverse order of stacking.
Here is one last example. MOVE.L #$12345678,D0 BSR ADD jump to subroutine MOVE.L D0,D1 transfer
ADD MOVE.L D0,-(SP) saves d0.l on the stack
MOVE.W #8,D0 MOVE.W #4,D1 ADD.W D0,D1 MOVE.L (SP)+,D0 RTS
Assemble and follow the unfolding under MONST carefully