COURS203.TXT
Jump to navigation
Jump to search
****************************************************************** * * * 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