COURS 6.TXT

From Atari Wiki
Revision as of 22:19, 16 December 2023 by Olivier.jan (talk | contribs) (Replacing content of with translated version)
Jump to navigation Jump to search
  ******************************************************************
  *                                                                *
  *             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