******************************************************************
* *
* 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