COURS202.TXT
****************************************************************** * * * 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