COURS205.TXT

From Atari Wiki
Revision as of 00:14, 17 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 Fierce Rabbit (from 44E) *
  • *
  • Second series *
  • *
  • Lesson number 5 *
  After this course on macros, we will move on to the use of
  arrays in assembler. I hope that after each course you
  conduct your own research and exercises, and that you
  don't just rush straight onto the next lesson. Even
  if the courses now seem short compared to those
  from the first series, they nonetheless lift the veil on many
  topics. You now have the level to deepen them and I hope you do!
  ARRAYS
  The use of arrays is not very common in assembler among
  beginner programmers. Indeed, the system may
  seem quite perilous to handle but proves to be very
  powerful and very convenient!
  Just like the principle of the stack, that of arrays is simple
  but requires a lot of logic to function
  properly. Let's start with a simple example: we
  will press the F1 to F10 function keys and
  each time we will display a letter. Let's start with an
  example without an array.
  See Listing number 2.
  Initially INCLUDE the program start-up routine studied
  at the beginning of the second series. Introduction with a small message,
  and then waiting for a key press. As we want to test
  the function keys and since they do not have an ASCII code,
  we take advantage of the fact that Gemdos 7 function returns 
  in the low order of D0 the ASCII code but also in the high order the scan code. There is more than just that though, because
  if you consult the ST bible or simply the description of the
  functions in the last pages of the GFA 3.00 doc, you learn
  that Gemdos 7 returns in bits 0-7 the ASCII code,
  16-23 the keyboard code and 24-31 the keyboard toggle keys state.
  Quick reminder about ASCII codes and scan codes. ASCII
  codes (American Standard Code for Information Interchange) are
  7-bit codes that follow the alphabetical order. This
  standard is also known as International Telegraph Code
  number 5. The 8th bit is a parity bit, that is to say it only
  serves to verify the correct transmission of the code. Since
  this system is American, the ASCII encoding does not
  take into account accents or characters like the c
  cedilla. However, our computer does not need this
  eighth bit to check transmissions, so it is
  used to increase the number of possible combinations. Returning
  to the course 2 of series 1, on magic numbers: with 7 bits
  we can count from 0 to 127, one more bit allows us to
  count from 0 to 255. Consult a chart of ASCII codes (bible,
  developer's book, GFA doc, etc...) and you will realize
  that all 'strange' signs are coded on 8 bits and therefore have
  codes higher than 127.
  However, ASCII codes are not sufficient. Imagine that I press
  the key A on my keyboard. In ASCII code I will receive
  65. But if an Englishman presses the same key, for him it will be
  a Q and he will receive the ASCII code 81. The ASCII code therefore
  does not depend on the key, mechanically speaking, but rather on the letter
  of this key. There is another coding that corresponds to keys
  from a mechanical point of view. This is called the
  scan-code.
  In terms of the scan-code, a key has a number, totally
  independent of the letter that will be assigned to this key. Thus
  the key A on your keyboard returns the scan-code $10, and the same
  goes for all STs in the world, whether this key is
  marked "A", or "Q". It's the placement on the keyboard that
  counts.
  We therefore test the scan-code of the key we
  pressed. If it's Escape, we exit the program.
  Otherwise, we will test if it's a function key. The key F1 has a scan-code $3B, F2->$3C, F3->$3D
  etc... up to F10->$44. Since scan-codes are consecutive, we therefore test
  our result relative to the scan-code of F1, if it's
  lower it's therefore not valid, likewise relative to the
  scan-code of F10: if it's higher it's not valid either.
  These tests being done, we are therefore in the presence of a scan-code
  located between $3B and $44, these 2 numbers included. $3B, that's 59
  in decimal. To get 65 (ASCII code of A) just add
  6. That's what we do next. We thus obtain the ASCII
  code of A, B, C etc... depending on the function key that was
  pressed. Only the letter remains to be displayed!
  Let's imagine now that we want to display "key F1"
  when we press this key, "key F2", etc....
  Several solutions come to mind. Here is the first one that
  comes to mind, and I will just unveil it because it's not
  related to arrays. In order not to complicate, we
  will not display "key F10", in fact we will only take into account
  F1-F9 keys. Remember one of the listings from
  series 1. The one that displayed a sentence by making the letters appear
  like on the displays of train stations or airports. Take up that listing again (it was
  number 3) and remember what happened to the sentence
  located at the address TXT. We displayed this sentence
  but beforehand it was modified at the column address and the
  letter address.
  In the present case it is enough to do about the same thing.
  Prepare a sentence like this:
  TXT     DC.B    "KEY F"
  NUMBER  DC.B    "  ",13,10,0
  For each press on a function key, we subtract 10 (in
  decimal) from the scan-code, and we put the result at the
  address NUMBER. Thus the scan-code of the key F1 ($3B thus 59 in
  decimal) will become 49 which is the ASCII code of the letter '1'.
  So we will see displayed 'KEY F1'.
  Execute this program before continuing, it will make an
  excellent exercise!!!!
  Let's now move to the array, modifying the display slightly.
  A press on:                   will display:
  F1                      A
  F2                      Z
  F2                      E
  F4                      R
  F5                      T
  F6                      Y
  F7                      U
  F8                      I
  F9                      O
  F10                     P
  First observation: if the scan-codes of the keys still follow each other,
  it can be said that the logical link between the displayed letters is a bit weak...
  Take listing number 3, and let's begin to study it. The beginning,
  up to the comment 'the key is valid', is
  strictly identical to the previous listing. Then we begin
  the part using the array. The address of this array is passed
  in A0 (Load Effective Address), then $3B is subtracted from D0 so
  that it has a value from 0 to 9. The array we
  use is composed of Words. However, a word is 2 bytes,
  and the memory is composed of bytes. To move within an
  array in words when our unit is the byte, we must therefore
  move by 2's. Our 'counter', which
  is here D0, shouldn't then be taking a value such as 0,1,2,3,4 etc...
  but rather a value like 0,2,4,6,8...
  Since in the course of our operations we have D0 with a value
  of the type 0,1,2,3,4... we now need to multiply it by 2. This
  is done by the MULU operation which is read Multiply Unsigned. Indeed
  it is an unsigned multiplication, it does not
  take into account the sign of what is multiplied,
  unlike the MULS operation (Multiply signed).
  Now, let's closely observe this instruction:
  MOVE.W    0(A0,D0.W),D0
  It is a MOVE so it's a transfer operation. It takes place over a word
  since we have .W This MOVE will take the D0th word from A0
  to put it in D1. So, if we press F3, we
  get $3D. We subtract $3B and we get 2, we
  multiply by 2, and so D0 now equals 4. We will therefore
  point to the 4th byte of A0 and take a word starting from
  that place. The transfer is indeed always counted with a
  number of bytes, whether the table is in bytes, words, or longs.
  It's a bit like if you were moving down a street with small
  houses, medium or large ones, the transfer will always be measured in meters.
  But what does the 0 mean in front of the parenthesis? Well, it’s
  the value of a fixed shift to add. Let's take an example: We
  have an array in which we 'tap' according to a number that
  is provided to us by a key press. Only we need to
  take different things depending on whether the key is pressed while
  SHIFT is pressed down. It is then possible to say: if
  shift is not pressed then it will be the first elements of the
  array that will be taken into account, but with shift it will be the
  elements at the end. We can then do:
  MOVE.W    0(A0,D0.W),D1 or if shift is pressed,
  MOVE.W    18(A0,D0.W),D1. This amounts to taking the D0th word
  from A0, starting to count 18 bytes from the beginning of A0.
  Nevertheless, we must be careful with several things concerning arrays. First of all, paying close attention to the type of data in the
  array to properly modify the 'counter' accordingly.
  Also, be very careful that the first element is element 0 and not 1. We had already seen in the very
  first courses of series 1 the problems that can arise
  when counting, sometimes forgetting the 0. This problem is
  all the more annoying with arrays because, if instead of subtracting $3B in my example to get a number from 0 to 9, I had only
  subtracted $3A and thus obtained 1 to 10, my program would have
  worked perfectly. It would have simply displayed anything after a press on F10. However, if you have an array of
  200 elements that you call with the keys, key+shifts, +control etc... the verification key by key could
  be left aside... In our example, we used words in our table. It would have been perfectly possible to use bytes.
  Modify the program a bit: delete the line with MULU, and
  modify the datas. Instead of putting DC.W at the address TABLE,
  put DC.B. Finally, since our array is now
  in bytes and not in words, the addressing
  allowing to pick from it must be modified. Instead of MOVE.W 0(A0,D0.W),D1 it
  is now necessary to put MOVE.B 0(A0,D0.W),D1
  However we must be careful because we have talked about the impossibility of using odd addresses. However, in this last case,
  as our table is in bytes, if D0 is worth 3, we find
  ourselves with an odd address, and yet it works! Indeed
  it works because we take a byte. In fact, the 68000
  can perfectly take a byte at an odd address,
  however, what it cannot do is take a larger piece of data (word or long) that starts on an odd address and
  therefore spans 'normal' places. Let's modify the program once
  more. Put the table in word mode, and return to word
  addressing (MOVE.W 0(A0,D0.W),D1). So, the mistake will come
  from the fact that we forgot MULU, and therefore our
  counter will sometimes be odd while our table and our
  addressing mode requires an even counter.
  Assemble and launch. Pressing F1: everything goes well! Normal,
  D0 after subtracting $3B, is valued at 0 which is therefore even. Press
  F3: same thing because D0 is worth 2. However, pressing F2 results
  in 3 bombs and a return to DEVPACK. Let's debug our program: alternate+D, and scroll down to the line:
  MOVE.W 0(A0,D0.W),D1
  Place this line at the top of window 1 and press control+B
  A breakpoint is set there. Launch with control+R, and press
  the F2 key. Breakpoint, here we are under MONST. Looking at the
  value of A0 we know the address of our table, which is
  an even address. However, if you pressed F2,
  you should have 1 as the value of D0, therefore an odd value.
  Move a step over MOVE.W 0(A0,D0.W),D1 using Control+Z.
  Address error! You just need to exit with Control+C.
  Okay, we've seen how to take a word or a byte from an
  array. With a little intelligence you should be able to take a long word (instead of doing a MULU by 2
  you do one by 4). Let's take a step back and remember
  from the previous courses: we studied the principle of this
  'tube', of that memory which we are beginning to use
  abundantly. If you have a bit of memory actually, you should
  remember a remark made at the very beginning, stating that it was necessary
  to be careful not to confuse the contents of the tube with the address of
  this content. Indeed, it is completely possible to have
  IMAGE         incbin         "A:\HOUSE.PI1"
  PTN_IMAGE     DC.L           IMAGE
  At the address IMAGE, we find in the tube the image itself,
  but at the address PTN_IMAGE, we find a long word, which turns
  out to be the image's address. With a little imagination, we
  can therefore imagine an array composed of long words, these long
  words being addresses of images, texts, but also (why
  not!) routines!!!!!!
  Here is the skeleton of a program performing such a thing: In the beginning, same as before, waiting for key press,
  verifying the validity of the key, we manipulate to have
  a code like 0,1,2,3,4... then we MULU it by 4 as our
  table will consist of long words.
           LEA       TABLE,A0
           MOVE.L    0(A0,D0.W),A0 
           JSR       (A0)
           BRA       START               and we start over
  We perform a JSR (jump subroutine) instead of a BSR. Why?
  try, and look at the appendix on instructions to see the
  differences between the two!!!
  But what is our table made of? Well, for example
  TABLE DC.L EVERYTHING_GREEN
   DC.L    ALL_BLUE
   DC.L    QUIT
   DC.L    DRING
   DC.L    HELLO
  etc....
  All these entries being the addresses of the routines. For example
  HELLO move.l #message,-(sp)
   move.w #9,-(sp)
   trap #1
   addq.l #6,sp
   rts
  The EVERYTHING_GREEN routine sets the entire palette to green etc....
  It is likewise possible to put in an array the addresses of
  phrases and pass the "picked" address to a routine that displays
  with gemdos(9), for example.
  One last thing, which is closer to the system of the list than to that of the array, but which is also very useful. We studied here
  possibilities always stemming from the same evidence:
  the data that we use to point in the array, follow each other! Unfortunately, in many cases, they do not...
  Here is another method: Imagine the case of a text editor, with several possible actions (erase the text, save
  the text, print it, load, overwrite, scroll etc...) called
  by combinations of keys. To be in line with the Wordstart
  norm (it is the keyboard norm used by Devack: ALT+W=print for
  example), I first collected with a very small program the
  codes returned by the key combinations Then I made
  a list of these codes, a list in words because in the case of key combinations (it is possible to build the combination pressed key/-
  control key).
  TAB_CODE dc.w $1519,$1615,$1312,$2e03,$FFFF
  Then I made a list with the addresses of my routines.
  As I didn't have any done at the start, I made a
  'fake' one, called JRTS and that does.... only RTS!
  TAB_ROUTINE dc.l JRTS,JRTS,JRTS,JRTS
  Then I looped to read TAB_CODE, comparing, every
  time, the value found in the table with the one from the
  key. At the same time I walk through TAB_ROUTINE so that when
  I read the 3rd element of TAB_CODE, I am in front of the 3rd element
  of TAB_ROUTINE.
  Here is the module. D7 contains the word corresponding to the key or
  the key combination.
   LEA TAB_CODE,A0
   LEA TAB_ROUTINE,A1
  .HERE MOVE.W (A0)+,D0
   CMP.W #$FFFF,D0
   BEQ START
   MOVE.L (A1)+,A2
   CMP.W D0,D7
   BNE .HERE
   JSR (A2)
   BRA START
  The address of the list of codes is put in A0 and that of the
  routine addresses in A1. We take a