COURS203.TXT: Difference between revisions

From Atari Wiki
Jump to navigation Jump to search
mNo edit summary
mNo edit summary
 
Line 188: Line 188:
 
END
 
END
   
This program is of course bogus. However, I hope you have scrupulously typed it. Launch it... bang 4 bombs!!!!!
+
This program is of course bogus. I hope, however, that you
  +
have typed it scrupulously. Launch it... boom, 4 bombs!!!!!
   
  +
Let's examine it closely to understand why... The start
Let's observe it closely to understand why... The start is well set up and when we debug it's performed without any incident. We then place $AAAA in the variable THING. Activate window 3 and point to THING (just do Alternate A and type the name of the label in uppercase so here THING). Move on step by step, we see THING receive AAAA. Keep moving: we jump into SOMETHING with the address of return saved in the stack on the way (by the way, you can point the window 3 on STACK and look above the stack of data). Then we will jump into STUFF but there, stop!!!!!!! Follow the explanations very carefully. Just before executing the BSR STUFF, scroll down window 1 with the 'down arrow' key. Indeed, according to the size of the listing and that of the window, you should not see THING. So go down by 7 lines. Normally, the first line of the window should now be BSR STUFF with the little arrow in front indicating that it's this instruction that will be performed. At the bottom of the window, you must see THING with DC.W $AAAA in front since that's what we deposited there. Below the ORI.B#0,D0. Indeed, we are in a window that tries to show us the disassembly of what is in the tube. But at this place, there is 0 in the tube and it corresponds to ORI.B #0,d0; which explains these instructions. But these ORI.Bs correspond to what? Where are they located? Well, they are in our stack since it consists of a block of 124 bytes between THING and STACK. So now watch THING very attentively and move the program forward by one step. Now we are on the line MOVEM.L of the subroutine STUFF. Let's execute this line...
 
  +
is properly set up and when we debug it is executed
  +
without incident. We then place $AAAA in the variable BIDULE.
  +
Activate window 3 and point to BIDULE (just do Alternate A and type the label name in uppercase so
  +
here BIDULE). Move step by step, we see BIDULE
  +
receive AAAA. Continue advancing: we jump into TRUCMUCHE
  +
with the return address saved in the stack (about
  +
this, you can point window 3 to STACK and
  +
watch the data being stacked above). Then we are going
  +
to jump into MACHIN but there, stop!!!!!!! Follow the
  +
explanations carefully. Just before executing the BSR MACHIN, scroll
  +
window 1 down with the 'down arrow' key. Indeed,
  +
according to the size of the listing and the window, you should not
  +
see BIDULE. So scroll down 7 lines. Normally, the
  +
first line of the window should now be BSR MACHIN with
  +
the small arrow facing it indicating that this instruction is
  +
going to be executed. At the bottom of the window, you should see BIDULE
  +
with DC.W $AAAA in front since that's what we placed there.
  +
Below are the ORI.B#0,D0. Indeed, we are in a
  +
window trying to show us the disassembly of what is in the memory. At this location, there is 0 in the memory which
  +
corresponds to ORI.B #0,d0; this explains these instructions. But
  +
what do these ORI.B correspond to? Where are they located? Well, they
  +
are in our stack since it consists of a 124-byte block between
  +
BIDULE and STACK. So now scrutinize BIDULE very
  +
carefully and advance the program by one step. Here we are
  +
now on the MOVEM.L line of the subroutine MACHIN. Execute
  +
this line...
   
  +
Astonishment, BIDULE is overwritten, as well as the RTS of the subroutine
</pre>
 
  +
MACHIN which is just above!!! And now, if we
  +
continue, after the second MOVEM the 68000 will not encounter
  +
the RTS since it has just been overwritten, and our program will
  +
crash! Why? Well, because we tried to stack
  +
128 bytes (MOVEM from trucmuche=15 registers so 15*4=60 bytes,
  +
the same for the MOVEM from machin, plus the return address for
  +
trucmuche and machin, total 128 bytes!) whereas
  +
we only planned a stack of 124 bytes.
   
  +
For this program to work without a problem, we must therefore put at
  +
minimum a stack of 128 bytes. Be very careful about this, because if
  +
we had put a stack of 124 bytes, the program would not have
  +
crashed because there would not have been overwriting of the RTS but
  +
there would have been overwriting of BIDULE and I am certain that you
  +
would have looked for a long time asking yourself "but what can
  +
possibly overwrite BIDULE" especially since this overwriting occurs at
  +
a moment when, precisely, we do not use BIDULE!!!
  +
  +
Always keep in mind the principle of the memory tube,
  +
without forgetting that it is full of instructions, variable contents and that nothing prevents them from being overwritten!
  +
  +
Another small remark about the Start. It is entirely
  +
possible to test D0 on return from Mshrink(). If it is
  +
negative, there has been an error. If you know that systematically you put the BYE_BYE label in front of the GEMDOS(0) which ends your program, you can add at the end of the start:
  +
  +
TST.W D0
  +
BMI BYE_BYE
  +
  +
One last clarification on inclusions. There are other
  +
ways to achieve such things, for example by assembling the
  +
pieces and then linking them with a LINKER. This solution is
  +
interesting when programs start to become gigantic.
  +
  +
Otherwise, it's more hassle than anything else!!!! Even if
  +
some purists prefer linking:
  +
  +
I edit, I assemble, I quit, I link, I launch, it crashes, I
  +
debug, I edit, etc...)
  +
  +
I prefer, for my part, the method of "include". It allows
  +
possibly direct access to the source. For example,
  +
if your palette save routine crashes, place the cursor
  +
of GENST on the line INCLUDE "A:\SAUV_PAL.S" then choose
  +
the Insert File option in the file menu. Your routine is
  +
now in front of your eyes. Once this block is finalized,
  +
delimit it with F1 and F2 then save it with F3, and there you go!
  +
  +
End of the course on inclusions! Start building your
  +
library and don't hesitate to make backup copies,
  +
and beware of viruses! On this disk, there is IMUN.PRG
  +
You put it in the Auto folder on your boot disk, and it
  +
checks all the disks you insert into the
  +
drive!
  +
 
</pre>
  +
Back to [[ASM_Tutorial]]
   
 
[[Category: 68000 ASSEMBLY ON ATARI ST Part 1 ]]
 
[[Category: 68000 ASSEMBLY ON ATARI ST Part 1 ]]

Latest revision as of 00:09, 17 December 2023

******************************************************************
*                                                                *
*             68000 ASSEMBLER COURSE ON ATARI ST                 *
*                                                                *
*                 by The Ferocious Rabbit (from 44E)             *
*                                                                *
*                         Second series                          *
*                                                                *
*                         Course number 3                        *
******************************************************************

Before continuing to study these courses, I think it's time for
you to study all the 68000 instructions. You should now have
enough basics to realize small programs and especially you
must have become accustomed to the system that consists in
typing only 2 or 3 lines of programs and then visualize their function with MONST.

So, take the 68000 book you normally acquired, and tour all the 
instructions with this method. Observe well the results of the 
shift operations (ASL, ASR, etc.). It's not about knowing them 
by heart but at least to know they exist to find them easily, if needed,
and especially to be able to understand what they do. The number of 
instructions is small, but each performs a very specific operation that
should be known in the finest details. It's not enough to know 
that ROR performs a rotation to the right, you must also know that the 
bit ejected on one side is put back on the other and, in addition, it 
is copied into the C bit of the SR. It should be noted also that ROR #4,D0 
is accepted but not ROR #9,D0. In that case, you have to do 2 RORs 
or MOVE.W #9,D1 then ROR D1,D0. It's this kind of little tricks that will block you later 
if you haven’t spent a few hours dissecting them!

FILE INCLUSIONS

One of the biggest problems with ASSEMBLER is related to the size of listings. If, in BASIC, one line is enough for a sometimes very complex operation, we have seen that this is not the case in ASSEMBLER. On the other hand, we also saw that writing in ASSEMBLER consists in typing the right number of machine instructions, while the BASIC, C, or PASCAL compiler manages to do a 'working' translation, which is therefore not necessarily the most economical in terms of size and/or speed.

On the other hand, our ASSEMBLER sources quickly make pages and pages where a programmer working with an 'advanced' language would only have a few dozen lines. However, there are some methods to reduce significantly the size of the listing we're working on.

Two directives will mainly serve us. Beware, these are not ASSEMBLER instructions, but orders interpreted by the ASSEMBLER. So these are, in our case, 'Devpack' instructions and not '68000' instructions.

The first one, INCBIN, allows to incorporate a binary file into 
the program. A binary file can be an image, a digit, sprites, etc... or a piece of program that has been assembled in the form of a binary file. Here is an example with an image. (listing 1 series 2)

First, we transfer the image address in A6, we skip the header to point to the colors, which are set with Xbios(6), then we search for the screen address with the Xbios(3) function and, after having skipped the color palette of our image, we transfer this image on the screen. A screen is 32000 bytes. If we transfer it long word by long word, we only have to transfer 32000 divided by 4, i.e., 8000 long words. As we are using a DBF loop that counts up to 0 included (the loop stops when the counter reaches -1), we must therefore initialize the counter to 7999. Then wait for a key press and bye-bye.

Small educational exercise. Once this program is assembled, follow it under MONST. When Xbios(3) has given the screen address, place window 3 on this address then continue to advance the program step by step to see the copy happen. From time to time press the V key to see the screen instead of MONST, you can thus follow the display 'live'!!!

Another small exercise: The ST has the particularity of having somehow 2 screens: the screen on which we work (Xbios(2)) and the one we can see (Xbios(3)). In most cases, it's the same but it is totally possible to place them at different memory locations and thus prepare a display in xbios2 while showing Xbios3. It will then be possible to quickly display Xbios2 by transferring it to Xbios3, thus, making fast animations. Try to change the program listing a bit and put MOVE.W #2,-(SP) instead of 3 to search for the screen. Debug the program and see!

But this takes us a bit far from the subject which was inclusion. To make this, we just provided a positioning label which is here IMAGE then the instruction INCBIN followed by the path to find this image. In the example it's a PI3 image, but nothing prevents you from putting a PI2 or PI1 image! We could very well have put 2 images back to back, without putting a label to spot the second. To point to it, knowing that a DEGAS image makes 32066 bytes, we would have done:

            LEA       IMAGE,A6
            ADDA.L    #32066,A6
I remind you this reads: load effective address IMAGE in A6 
add address long...

Small exercise: make a program that runs in medium resolution, switches to low, displays a low resolution image, waits for a key press then switches back to medium. Knowing how to use Xbios functions and loops, you should be able to make one routine to save the palette and another to restore it.

It is entirely possible to include very diverse files this way. However, there are several problems. First, the size of the resulting program. Indeed, our DEGAS image, due to its size, has grown our program by 32Ko! Of course, now there is the advantage of being able to prevent tinkers from coming to put their feet there! Even though a DEGAS image is always the same size, it is possible to include a compacted image (putting of course a decompression routine in our program!).

Another problem, time! Indeed, if the display itself is faster because there is no need to fetch the image from the floppy disk since this image is already in memory with the program, it's during assembly that it drags!!! It is therefore necessary to go through well-ordered work mechanisms. Set up a virtual disk (preferably resistant to Reset) copy the images into it and then start working. You will quickly understand why 520 owners have every interest to have them expanded to a minimum of one mega and why an external drive or better a hard drive becomes essential quickly!!!

After having seen the mechanism of file inclusion, we will now look at listing inclusions. It is indeed possible to take a piece of the listing, save it to a floppy disk, and ask the assembler to include it during assembly. There, too, there is a waste of time assembling but a very significant time saving when creating the program. For example, your palette saving routine: carefully made, it works well and in fact you need it in all your programs. It is therefore necessary each time to retype it and especially consume a few dozen lines with this routine. Time waste, possibility of typing error, and unnecessary extension of the listing. When you start browsing 10 or 20 pages of sources to look for a variable, you will start to understand what I'm talking about, knowing that 20 pages are just a tiny assembler source...

Let's see a concrete example, with the palette saving.

SAUVE_PALETTE
            MOVEM.L   D0-D4/A0-A4,-(SP)
            LEA       ANC_PAL,A4            point to the save location
            MOVE.W    #15,D4                16 colors

   .ICI     MOVE.W    #-1,-(SP)             color request
            MOVE.W    D4,-(SP)              color number
            MOVE.W    #7,-(SP)              Setcolor()
            TRAP      #14                   Xbios
            ADDQ.L    #6,SP
            MOVE.W    D0,(A4)+              saving the color
            DBF       D4,.ICI               and we move to the next one
            MOVEM.L   (SP)+,D0-D4/A0-A4
            RTS
ANC_PAL  DC.L      0,0,0,0,0,0,0,0
REMETS_PALETTE
            MOVEM.L   D0-D4/A0-A4,-(SP)
            LEA       ANC_PAL,A4            point to the save location
            MOVE.W    #15,D4                16 colors

   .ICI     MOVE.W    (A4)+,-(SP)           color request
            MOVE.W    D4,-(SP)              color number
            MOVE.W    #7,-(SP)              Setcolor()
            TRAP      #14                   Xbios
            ADDQ.L    #6,SP
            DBF       D4,.ICI               and we move to the next one
            MOVEM.L   (SP)+,D0-D4/A0-A4
            RTS

Here are therefore 2 routines. First of all, knowing that a call to Xbios does not save the D0-D3 and A0-A3 registers, we use D4 and A4 for the color counter and the storage address, which also explains saving on the stack at the start of both routines. The first routine saves the palette and puts it at the ANC_PAL address. We notice that this address is between the 2 routines. Indeed, several solutions are available to us. First, the most economical in size is to put this reservation for the palette in the BSS section of our program by doing ANC_PAL DS.W 16. We have already seen that the BSS section does not take up space on the floppy disk. However, we made these routines with the idea of including them, to gain in ease of programming. By placing this reservation between the routines, it becomes an integral part of the file. It's not possible to put it in DS therefore we are obliged to put it in DC. The danger would be that our program tries to read this part. Doing a BSR ANC_PAL, for example, would be fatal but we are serious enough not to do it, so no problem...

Once you have typed this small listing, save it for example under the name SAUV_PAL.S. Then modify the program of listing 1 (the one we just saw with the included image). Just after the end of the program by MOVE.W #0,-(SP), TRAP #1 (so just before including the image), put

            INCLUDE   "A:\SAUV_PAL.S"

Do not put a label on the left margin since the first label is SAUVE_PALETTE and it is in our routine. Then at the very beginning of the program, put BSR SAUVE_PALETTE and at the end just before leaving, put BSR REMET_PALETTE. Size-wise, your listing is therefore simply increased by 3 lines:

   (BSR SAUVE_PALETTE, BSR REMET_PALETTE, and INCLUDE "A:\SAUV_PAL.S")

and yet it's 24 lines that are added!!!

We are therefore in the process of creating a library. It's a very big part of the assembler programmer's job because many things often come back: initialization by saving the palette, switching to low resolution and passing to Supervisor, restoration by doing the opposite, decompression of images, etc.

Similarly, if you are in the process of creating a large program, during development, entire pages can be included decreasing the size of the listing and allowing for much easier movement.

Let's immediately see another block that should now be part of our library. Up to now, we have typed a lot of small programs without worrying about the memory space. It's time to think about it to start becoming aware of the fact that we need to program cleanly. Let's imagine that our program is in memory but at the same time there are other programs in this memory. It's clear that each one must only appropriate the memory space it needs to leave as much as possible for its neighbors. For this, we need to know that during assembly into an executable file, a header is generated. Thanks to this header, upon the launch of our program, what is called the base page will be created. Here is a description of it. If you want to get the most information on program headers or base pages, refer to the corresponding chapters in the Bible or the Developer's Book. Excellent chapter on this subject in the ATARI official documentation (document on program structure).

* Base page

Address         Description
$00      Base page start address
$04      End address of free memory
$08      Start address of the TEXT section
$0C      Size of the TEXT section
$10      Start address of the DATA section
$14      Size of the DATA section
$18      Start address of the BSS section
$1C      Size of the BSS section

We will therefore get this information and deduce the size that should be enough for our program. Knowing the location of the implantation zone of the program, we will use function 74 of the GEMDOS (Mshrink function) which allows, by giving the desired size and the end address, to shrink a memory area. Indeed, at the launch our program has taken all the available space, we must therefore shrink it. Our program also needs a stack. Instead of taking the one that is already in place, we will replace it with ours, whose size we can set at will.

* PROGRAM START ROUTINE

            MOVE.L    A7,A5                 retrieves stack pointer to take
                                            * parameters
            LEA.L     STACK,A7              imposes our stack
            MOVE.L    4(A5),A5              address of the base page from
                                            * the old stack
            MOVE.L    12(A5),D0             size of the text section
            ADD.L     20(A5),D0             + size data section
            ADD.L     28(A5),D0             + size bss section
            ADD.L     #$100,D0              + length of the base page
                                            * (256 bytes)

* Calling the MShrink() function from GEMDOS (Memory Shrink)

            MOVE.L    D0,-(SP)
            MOVE.L    A5,-(SP)
            MOVE.W    #0,-(SP)
            MOVE.W    #74,-(SP)             M_shrink()
            TRAP      #1
            LEA       12(A7),A7

After this operation, our program only uses the memory space it needs. Do not forget to define the stack in the BSS section like this:

            DS.L      256
STACK    DS.L      1

I see some of you are surprised by this reservation!!! In the examples provided with DEVPACK it says "stack go backwards", which I loosely translate as "as I go forward, the stack goes backward, how do you want me to..." A bit of seriousness. We have seen that the use of the stack is done by decrementing it:

   (move.w        #12,-(sp) for example).

So, we must reserve space BEFORE the label. For this reason, we note the label and, above, the real size reserved for the stack.

What size to choose? It's up to you! If your program is full of subroutines calling each other and saving all registers each time, you need to plan big.

Type the following program. It goes without saying that you must have typed beforehand the program start routine which is a little higher in this course. It is included here at the beginning. No branching to it. If, once assembled, you debug this program, you will see the start routine at the beginning. From now on, this routine will always be present at the beginning of our programs but will never be fully copied again, an INCLUDE is much more convenient! Note: It seems that DEVPACK sometimes gets confused when there are numerous inclusions and calls to files contained in folders. Similarly, there are problems with inclusions on drive B when it is taken as drive A in which you put the drive B disk. (on the other hand, I did not encounter any problem with my external drive).

            INCLUDE   "A:\START.S"
            MOVE.W    #$AAAA,THING
            BSR       SOMETHING
            MOVE.W    THING,D6
            MOVE.W    #0,-(SP)
            TRAP      #1
*---------------------------------------------------*
SOMETHING  MOVEM.L  D0-D7/A0-A6,-(SP)
            BSR       STUFF
            MOVEM.L   (SP)+,D0-D7/A0-A6
            RTS

STUFF      MOVEM.L   D0-D7/A0-A6,-(SP)
            MOVE.L    #$12345678,D0
            MOVEM.L   (SP)+,D0-D7/A0-A6
            RTS

            SECTION BSS

THING      DS.W      1
            DS.B      124
STACK      DS.L      1
            END

This program is of course bogus. I hope, however, that you
   have typed it scrupulously. Launch it... boom, 4 bombs!!!!!

   Let's examine it closely to understand why... The start
   is properly set up and when we debug it is executed
   without incident. We then place $AAAA in the variable BIDULE.
   Activate window 3 and point to BIDULE (just do Alternate A and type the label name in uppercase so
   here BIDULE). Move step by step, we see BIDULE
   receive AAAA. Continue advancing: we jump into TRUCMUCHE
   with the return address saved in the stack (about
   this, you can point window 3 to STACK and
   watch the data being stacked above). Then we are going
   to jump into MACHIN but there, stop!!!!!!! Follow the
   explanations carefully. Just before executing the BSR MACHIN, scroll
   window 1 down with the 'down arrow' key. Indeed,
   according to the size of the listing and the window, you should not
   see BIDULE. So scroll down 7 lines. Normally, the
   first line of the window should now be BSR MACHIN with
   the small arrow facing it indicating that this instruction is
   going to be executed. At the bottom of the window, you should see BIDULE
   with DC.W $AAAA in front since that's what we placed there.
   Below are the ORI.B#0,D0. Indeed, we are in a
   window trying to show us the disassembly of what is in the memory. At this location, there is 0 in the memory which
   corresponds to ORI.B #0,d0; this explains these instructions. But
   what do these ORI.B correspond to? Where are they located? Well, they
   are in our stack since it consists of a 124-byte block between
   BIDULE and STACK. So now scrutinize BIDULE very
   carefully and advance the program by one step. Here we are
   now on the MOVEM.L line of the subroutine MACHIN. Execute
   this line...

   Astonishment, BIDULE is overwritten, as well as the RTS of the subroutine
   MACHIN which is just above!!! And now, if we
   continue, after the second MOVEM the 68000 will not encounter
   the RTS since it has just been overwritten, and our program will
   crash! Why? Well, because we tried to stack
   128 bytes (MOVEM from trucmuche=15 registers so 15*4=60 bytes,
   the same for the MOVEM from machin, plus the return address for
   trucmuche and machin, total 128 bytes!) whereas
   we only planned a stack of 124 bytes.

   For this program to work without a problem, we must therefore put at
   minimum a stack of 128 bytes. Be very careful about this, because if
   we had put a stack of 124 bytes, the program would not have
   crashed because there would not have been overwriting of the RTS but
   there would have been overwriting of BIDULE and I am certain that you
   would have looked for a long time asking yourself "but what can
   possibly overwrite BIDULE" especially since this overwriting occurs at
   a moment when, precisely, we do not use BIDULE!!!

   Always keep in mind the principle of the memory tube,
   without forgetting that it is full of instructions, variable contents and that nothing prevents them from being overwritten!

   Another small remark about the Start. It is entirely
   possible to test D0 on return from Mshrink(). If it is
   negative, there has been an error. If you know that systematically you put the BYE_BYE label in front of the GEMDOS(0) which ends your program, you can add at the end of the start:

            TST.W     D0
            BMI       BYE_BYE

   One last clarification on inclusions. There are other
   ways to achieve such things, for example by assembling the
   pieces and then linking them with a LINKER. This solution is
   interesting when programs start to become gigantic.

   Otherwise, it's more hassle than anything else!!!! Even if
   some purists prefer linking:
   
   I edit, I assemble, I quit, I link, I launch, it crashes, I
   debug, I edit, etc...)

   I prefer, for my part, the method of "include". It allows
   possibly direct access to the source. For example,
   if your palette save routine crashes, place the cursor
   of GENST on the line INCLUDE "A:\SAUV_PAL.S" then choose
   the Insert File option in the file menu. Your routine is
   now in front of your eyes. Once this block is finalized,
   delimit it with F1 and F2 then save it with F3, and there you go!

   End of the course on inclusions! Start building your
   library and don't hesitate to make backup copies,
   and beware of viruses! On this disk, there is IMUN.PRG
   You put it in the Auto folder on your boot disk, and it
   checks all the disks you insert into the
   drive!

Back to ASM_Tutorial