COURS203.TXT
- *
- 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. However, I hope you have scrupulously typed it. Launch it... bang 4 bombs!!!!!
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...
Am