COURS 3.TXT

From Atari Wiki
Revision as of 13:46, 16 December 2023 by Olivier.jan (talk | contribs)
Jump to navigation Jump to search
   ******************************************************************
   *                                                                *
   *               ASSEMBLER COURSE 68000 ON ATARI ST               *
   *                                                                *
   *                   by Le Féroce Lapin (from 44E)                 *
   *                                                                *
   *                            Course number 3                      *
   *                                                                *
   ******************************************************************

   If  you have studied the first two lessons correctly, you
   should now have a little more order than at the beginning, and binary
   and hexadecimal should no longer hold any secrets for you.

   Before starting, I must remind you of something essential: It is
   tempting to think in terms of numbers whereas very often it would be
   better to remember that a number is only a sequence of bits. So imagine a game in which you have to
   code data related to characters. By reading this data, you will know 
   which character it is, and how many hit points they have left. Let's say there are 4 characters.
   How many bits do you need to count from 0 to 3 (i.e. for 4 possibilities)? Only 2 bits. My characters can 
   have a maximum of 63 hit points (from 0 to 63 because at 0 they are dead), so I need 6 bits to code this vitality. 
   So I can have on a single byte (octet) 2 completely different things: with bits 0 and 1 (the rightmost bit is bit 0, the leftmost bit for a byte is therefore 7) 
   I code my character's type, and with bits 2 to 7 his vitality.

   Thus the number 210 in itself does not mean anything. It is the fact
   of putting it in binary: 11010010 and thinking in terms of 
   bit grouping that will make it clearer. Let us separate the 2 right bits: 10 which gives 3 in decimal, so I am in the presence of a character of type 3.

   Now let's take the 6 left bits: 110100 and convert.

   We get 52. So we are in the presence of a character of type 3, with 52 hit points.

   This should now be clear, let's move on to a brief explanation
   concerning memory, before we approach our first program.

   MEMORY STRUCTURE

   Memory is a very thin and very long tube. We must distinguish 2 things:

   1) What is in the tube.
   2) The distance from the beginning of the tube.

   ATTENTION, this notion must be perfectly understood because it is perpetually a source of error.
   Thanks to the distance, we can easily find what we put in the tube. This distance is called 'address'.

   The tube has a diameter of 1 byte (octet). When I talk about the address $6F00 (28416 in decimal), it is a location. At this location I can put a byte. 
   If the data I want to put fits on a word (so 2 bytes because 1 word is indeed 2 bytes glued together), this data will occupy the address $6F00 and the address $6F01.

   Let's imagine that I load an image (32000 bytes) from the address $12C52. I will loop 32000 times to deposit my image, increasing my address each time.

   Now let's imagine that I want to note this address. I will for example note it at address $6F00.

   So if I walk along the tube to address $6F00 and look at this level in the tube, I see the number
   $12C52 coded in a long word (addresses are encoded in long words). So, what does this number represent: an address, that of my image!!!! I hope it's perfectly clear...

   A program, therefore, for the 68000 is a series of readings of the contents of the tube. It will find numbers which it will interpret 
   as instructions (remember lesson 2). With these instructions, we will tell it for example to continue reading at another point of the tube, to go back, to 
   take the content of the tube and to go and deposit it elsewhere (always in this same tube of course) etc... To know at what point the 68000 is reading 
   the instructions it is executing, there is a counter. As this counter is used for the program, it is called the Program Counter, abbreviated PC.

   The 68000 has a 24-bit PC, which means it can take values between 0 and 16777215. As each value of the PC corresponds to an address and in front 
   of this address (so in the tube) we can only put a byte, a machine equipped with a 68000 can therefore work with 16777215 bytes, which is 16 Megs. 
   For information, the 80286 from Intel which equips 'large' PC compatibles, only has a 20-bit PC, which restricts its space to 1 meg...

   Note that memory is intended to receive bytes but what these bytes represent (text, program, image...) is of absolutely no importance.

   FIRST PROGRAM
   We will now illustrate our point right away. So let's launch GENST. Those who have a color screen should launch it at average resolution, it is preferable for better comfort 
   of work.

   Even if you have a 520, choose in the 'preferences' of GENST (in the 'Options' menu) an automatic loading of MONST (Load MONST 'YES') put a Tab Setting of 11 and auto-indent on
   YES.

   If MONST is already loaded, its option in the 'program' menu should be available, otherwise it is grayed out. If that's the case, after saving the preferences, exit GENST and 
   restart it.

   Now, let's create the following program:

       Put the number $12345678 in register D0
       Put the number $00001012 in register D1
       Add the content of register D0 with the content of register D1

   First of all, you should know that these instructions will be put in the tube, and that we will sometimes need to locate these places. For this purpose, we will use labels, which we will 
   put next to the tube. These labels are to be written all the way to the left in our listing while the instructions (what goes INTO the tube) will be written after a space or better for
   legibility, after a tabulation.

   So our program becomes:

   MOVE.L     #$12345678,D0
   MOVE.L     #$00001012,D1
   ADD.L      D0,D1

   Notice the # sign before the numbers. The $ sign indicates that these numbers are written in hexadecimal. The # sign indicates that it is the value $12345678 that we want to put in D0.

   If we had done MOVE.L   $12345678,D0, it is the value at address $12345678 that we would have put in D0.

   Why is there .L after the MOVE and the ADD? We will see that in a few minutes.

   Meanwhile, let's assemble while holding [ALTERNATE] and then pressing A.

   Normally, everything went well or else it's because you didn't faithfully copy this 'program'. 

   Now, let's debug our program, by holding down [ALTERNATE] and pressing D.

   Voila, we find ourselves in MONST which, being called from GENST, has automatically loaded our program.

   First of all let's take a look at this jumble of numbers...

   At the top we find our data registers D0 to D7 as well as our address registers A0 to A7 with A7'. Below the data registers, we see SR and underneath PC. 
   We notice that PC shows an address and the first line of our program. So the PC indicates what is going to be executed.

   The window below (number 2) shows our program. On the left of this window we see the addresses. Symbolically we can say that the right part of this window shows our instructions in the tube and 
   the left figures tell us the location, the address from the beginning of the tube.

   The right window (number 3) actually shows the same thing as number 2, but with the view of the 68000. We saw in lesson 2 that for the machine 
   our sequence of orders was just a series of numbers.

   When we assembled, the assembler simply converted our program line by line into numbers.

   Normally in window 2 you should see our program with a small arrow in front of the first instruction. Look at the address of this instruction (i.e. the left figure, indicating where in the tube 
   this order is located). With Atari 1040ST, this revolves around $61BF0.

   NOTE: The 68000 allows a program to be placed anywhere.
   On some micro-processors, programs must imperatively all be placed in the same location, but not for us, so if my program is in $61BF0, it may not be the same for you: that's normal.

   Now look at window 3 and find the same address that you read in window 2 in front of our first program line. Normally if you haven't touched anything, this address should normally be the first.

   You should see 203C12345678. This is how the microprocessor receives MOVE.L #$12345678,D0!!!

   Now return to window 2. Note the address of the second line of our program and subtract this number from the address of the first line. We get 6. 
   So we deduce that:

   MOVE.L   #$12345678,D0 takes 6 bytes in memory.

   Now let's move our program forward. For this, hold down [CONTROL] and press Z once. The little arrow has jumped to the second line, and now this same line is indicated by the PC and our 
   register D0 now contains the value $12345678. MONST indicates all the figures in hexadecimal, you start to understand the interest of the calculator...

   Let's continue by doing Control+Z again. It's now the third line of our program that is indicated by the PC while D1 has been filled with $00001012.

   Let's continue with Control+Z. The addition between D0 and D1 has been carried out. As we had seen in lesson 2, the possibilities are minimal 
   because the result has crushed the old value of D1. To realize D0+D1=D2, it would have been necessary to first transfer D1 to D2 and then do ADD.L  D0,D2.

   In our case, D1 now contains the value $1234668A.

   Since our program does not really have an end, let's artificially exit it by typing Control+C.


   SECOND PROGRAM

   Erase the first program (alternate C) and type the following:

   MOVE.L     #$12345678,D0
   MOVE.W     D0,D1
   MOVE.B     D1,D2

   We have seen in Monst that D0-D7 are quite large registers. We succeeded in putting $12345678 in D0, which gives 305419896 in decimal! 
   Indeed, the 68000 is a 16/32-bit microprocessor, which means that these registers are not coded on 16 bits but on 32.

   32 bits, that's a long word. In our first program, we wanted the MOVE instruction to act on the entire register, 
   so on a long word, that's why we had specified .L after the move.

   NOTE: The vocabulary is very important and requires a little effort at the beginning. So MOVE.L doesn’t mean anything. 
   It is best to read this mnemonic MOVE LONG. Moreover, the term mnemonic (this is what the assembler instructions are called) must be related to mnemonic 
   (able to help memory by mental association, which facilitates the acquisition and retrieval of memories /CF Le Robert dictionary). So 
   it is best to read the instructions in English, which will greatly facilitate comprehension.

   Now let's assemble and debug it.

   Now execute the first line. The result is the same as for the first program: the PC indicates the second line, while D0 has received the value $12345678.

   Now execute the second line. What does it say? 
   MOVE.W    D0,D1 

   That is, move the content of D0 to put it into D1. But be careful, the move must be done on a word (specified by .W after the move. This reads MOVE WORD). 
   However, operations are always done on the least significant bits. So the MOVE will take the least significant word of D0 and put it into the least 
   significant word of D1. This is why it receives $5678.

   Let's continue by executing the third line. This one says:
   MOVE.B    D1,D2 (move byte d1 d2)

   So transfer the least significant byte of D1 to the least significant byte of D2. Pay close attention to the registers and the values they receive!

   Now exit the program using CONTROL+C


   THIRD PROGRAM

   Erase the previous program, and type the following, then assemble and debug it:

   MOVE.L     #$12345678,D0
   MOVE.L     #$AAAAAAAA,D1
   MOVE.W     D0,D1
   SWAP       D0
   MOVE.W     D0,D2

   Execution of the first and second line should no longer pose a problem.

   We should get
   D0=12345678
   D1=AAAAAAAA

   Now execute the third line. There is indeed a transfer of the least significant word of D0 to the least significant word of D1. 
   We note that the most significant word of D1 is NOT AFFECTED by this transfer, and that it remains quite independent from the least significant word.

   Execute now the fourth line. This mnemonic 'SWAP' (To swap) will swap the 16 least significant bits with the 16 most
   significant bits. D0 will therefore become 56781234.

   Finally, the last line. Transfer of the least significant word of D0 (which is now 1234 and not 5678) to the least significant word of D2.

   We have seen that in fact, D0 contained 2 data and that these data are totally independent. This allows great flexibility in work but also requires great rigor because if instead of 
   doing MOVE.W D0,D1, it was just a typo by typing MOVE.L D0,D1, 
   I would have overwritten the most significant word of D1 and then I would have been surprised to find 1234 in D1 where I should still have found AAAA.

   We immediately see the enormous advantages of this system. We only have 8 'pockets' of data (D0 to D7), but if we want to keep only words, we can put 2 in a pocket, 
   that's to say 16 in all. Likewise, if our coding is only on bytes, we can keep 32 bytes (4 per pocket). This may seem quite obvious but for example on the Archimedes, this is not possible. 
   On this machine, a register contains a long word or nothing!

   SUMMARY OF VOCABULARY

   MOVE.L = move long
   MOVE.W = move word
   MOVE.B = move byte


   TIPS
   Take your time, read this tutorial calmly and the previous ones again. You see, assembler isn't that difficult!

Back to ASM_Tutorial