COURS202.TXT

From Atari Wiki
Jump to navigation Jump to search
  

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

   So here we go again for new adventures! This second
   lesson will be about TRAPs and more precisely how to
   program them ourselves. We saw in the first series that
   TRAPs were an excellent way to access the operating system,
   and more generally to access protected spaces
   (only accessible in Supervisor mode). We had also
   studied the passing of parameters on the stack, which we
   used to make subroutines with parameters.

   The first example will consist of changing the background color
   of the screen, with a homemade routine that will be called
   by a TRAP.

   First, the routine:
   Given that a TRAP is always executed in Supervisor, we
   do not hesitate to use system addresses. The ST's color palette
   is located at the address $FF8240. Each color
   is coded on a word, so color 0 is at $FF8240, color 1
   at $FF8242, etc...

   We are going to make 2 routines. One that will set the background to red,
   the other will set the background to green. Here they are:

   RED     MOVE.W  #$700,$FF8240
           RTE

   GREEN   MOVE.W  #$070,$FF8240
           RTE

   A label identifies them (RED and GREEN). Colors are
   coded in RGB (red/green/blue) and the levels range from
   0 to 7. We notice that the routines do not end with
   RTS but by RTE. This means Return from exception. It is indeed
   a return from exception and not the return of a
   classic subroutine.

   Quick reminder: RTE reads "return from exception". I remind you
   that you must read EVERYTHING in English and not just the abbreviation
   whose meaning is often quite elusive.

   Here is the 'entire' program.

           MOVE.L  #MESSAGE,-(SP)  it's always nice to introduce yourself
           MOVE.W  #9,-(SP)
           TRAP    #1              call GEMDOS
           ADDQ.L  #6,SP   

   * Set the exception vector
           MOVE.L  #RED,-(SP)      'routine' address
           MOVE.W  #35,-(SP)       trap #3 vector number
           MOVE.W  #5,-(SP)        Setexec() function
           TRAP    #13             from the bios
           ADDQ.L  #8,SP

           MOVE.L  #GREEN,-(SP)    'routine' address
           MOVE.W  #36,-(SP)       trap #4 vector number
           MOVE.W  #5,-(SP)        Setexec() function
           TRAP    #13             from the bios
           ADDQ.L  #8,SP

   * The routines are now accessible by trap 3 and
   by trap 4.
           BSR     KEY
           TRAP    #3
           BSR     KEY
           TRAP    #4
           BSR     KEY
           TRAP    #3
           BSR     KEY
           
           MOVE.W  #0,-(SP)
           TRAP    #1
   *-------------------------------------*
   RED     MOVE.W  #$700,$FF8240
           RTE
           
   GREEN   MOVE.W  #$070,$FF8240
           RTE
   *-------------------------------------*
   KEY     MOVE.W  #7,-(SP)
           TRAP    #1
           ADDQ.L  #2,SP
           RTS
   *-------------------------------------*
           SECTION DATA
   MESSAGE DC.B    27,"E","TRAPS",0


   Easy, isn't it? And now that you know how to set
   your own routines in TRAP and that you also know how to pass
   parameters to a subroutine, all you have to do
   is to do the same thing. I think you're grown up enough to do
   it yourself and that's why we're not going to
   do it here. You only need to be careful with one thing: A subroutine only needs the return address and
   thus only stacks it. However, a TRAP, since it switches
   to Supervisor, also saves the Status Register. You must
   not forget to take this into account when calculating the jump
   that will allow you to retrieve your parameters passed on the
   stack. The return address is of course coded on 4 bytes and the
   Status Register on 2. There is therefore stacking of 6 bytes by the
   TRAP which automatically unstacks them on return in order to find
   where it comes from and also to restore the Status
   Register as it was before. Do not forget to correct the stack
   on return.

   As usual, take your time and do lots of little
   tests to perfectly understand the system.
   Also look carefully at the Bios function that we have
   used to implement our two routines. If instead of
   supplying the new address for the vector, we pass -1,
   this function will return to us, in D0.L, the current address
   corresponding to this vector. So there is nothing stopping us from requesting
   the address used by TRAP #1 (Gemdos), transfer this
   address to trap #0 (for example) and put our own
   routine in TRAP #1. This can also be useful for
   diverting the TRAP. For example, to automatically generate
   macros. It is possible to imagine a program residing in
   memory, which is placed instead of trap 13 (Bios). Each
   time there is a call to Bios, it is our routine that is
   triggered. Since calls are made with stacking
   of parameters, it is quite possible to know which
   Bios function is being called. It is then possible to react
   differently to certain functions. This allows for example to
   test presses on Alternate+function keys and in this
   case, to write phrases in the keyboard buffer, this in order
   to generate macros!

   Note: A trap cannot call traps placed 'below'
   itself. Thus, in a trap #1, it is quite possible
   to call a trap #13 but the reverse is not possible.

   Curious and interesting example:
           MOVE.W  #"A",-(SP)
           MOVE.W  #2,-(SP)
           TRAP    #1
           ADDQ.L  #4,SP

           MOVE.W  #0,-(SP)
           TRAP    #1

   This short program should not pose any problem. We display A
    and then we quit. Assemble it and then run under MONST.
   Press [Control] + P. You will then choose the preferences
   of MONST. Among them, there is "follow traps", which by default is on "NO". Type Y for YES.
   Once preferences are set, advance your program
   step by step with control+Z. Unlike other times,
   when you arrive on the TRAP you see what happens. Do not be
   surprised, it will be quite long because a lot happens to display a single character on the screen. The most
   amazing thing will be the call to trap #13. Yes indeed, to display a
   character the GEMDOS calls the Bios!!!!!

   Another equally interesting experience:

           MOVE.W  #"A",-(SP)
           MOVE.W  #2,-(SP)
           MOVE.W  #3,-(SP)
           TRAP    #13
           ADDQ.L  #6,SP   

           MOVE.W  #0,-(SP)
           TRAP    #1

   Display of A but this time with the Bconout() function of
   the Bios. Assemble and then run under MONST with trap tracking.
   When you get into the Bios (thus after the walk on
   the TRAP #13 instruction), advance the program step by step
   but from time to time type the letter V. This allows you to
   see the screen. To return to MONST type any
   key. Advance a few more instructions and then type V
   etc... After a while, you will see the letter A appear.
   Reflect on the concept of graphical screen and fonts and you will understand without difficulty what happens. Surprising, isn't it?

   A few more things: follow the traps of the Bios, Xbios,
   GemDos and look at what happens at the beginning. You will
   realize that there is registry saving on the stack. Only
   D3-D7/A3-A6 are saved and thus the content of D2 is potentially
   erasable by a call to the operating system. Caution is therefore
   advised. Also by following the TRAPs you will notice USP.
   This means User Stack Pointer and this is how the user stack is referred to.

   That's it, normally traps should no longer be a secret for you. You
   should know how to pass them parameters, reprogram them, etc...
   You should even realize that by following the operating system functions, you should be able to discover how certain
   things are done, and thus be able to rewrite portions of
   routines. 

Back to ASM_Tutorial