Access to Traps
Volume Number: | | 1
|
Issue Number: | | 6
|
Column Tag: | | MacPascal
|
Access to OS traps
By Alan Wootton
MacPascal is a miracle. Although billed as an educational product, and many serious programmers probably regard it as a toy (just as many consider the Mac to be merely a toy), MacPascal can be used to prototype professional software products.
Since MacPascal is not sold and supported as a development tool, a small amount of homework is needed to assemble a useful system. First you will need MacPascal. Second you will need RMaker, the resource compiler that runs on the Mac. For some an assembler is nice. And a 68000 debugger may be needed (I use MacDB by Bill Duvall). Since there is no syntax check on inline procedures, a debugger is useful to check the stack at the start of the call even if you are relatively bad at assembly language. Eventually one needs a compiler. For now all that is available is the Lisa Pascal Workshop. Hopefully, a Mac compiler will become available. For my purposes it is necessary for the compiler to generate native code; this rules out a p-code type of system (SoftTech Pascal wont do). Last but not least, Inside Macintosh is a must.
MacPascal provides methods to access almost all of the toolbox routines that make the Mac so great. The OS traps, however, are a bit tricky. As Steve Brecher showed us last month in MacTutor with his Advanced Macing column, the undocumented GENERIC procedure is used to call OS traps. However, INLINE can also be used for OS traps, as we shall see this month in this example of using STUFFHEX from Pascal. (See Steves Advanced Macing column in this months issue for an example of using STUFFHEX from Basic.)
TWO TYPES OF TOOLBOX TRAPS
There are two types of toolbox trap; those that are stack based and those that are register based. The stack based calls are supported using the INLINE procedure. The register based traps, of which most of the OS traps are typical, are more difficult to access because there is no way (other than using GENERIC) to set registers from Pascal. Fortunately, virtually all of the register traps use only D0 and A0. Therefore, if we could set D0 and A0 before the trap and read them afterwards, we would have an OS trap call mechanism using the INLINE procedure. I have written a short piece of 68000 code to accomplish this.
As an example, I have prepared a short program to read the date and time from the system. Two calls are made to the toolbox to do this; both are register based. Note that when a pointer is to be passed in A0, the @ operator must be used to set the variable to the correct value before the call. Also note that for any $A0xx calls that return a value to the calling routine, the flag $100 should be added to not preserve all registers.
GLUE ROUTINE FOR PASCAL TO OS
Our machine language routine will pop from the stack, the addresses of the variables which we wish to place in the registers A0 and D0. We will pop these addresses off the stack and use them to load the variables into the two registers. The new values returned in A0 and D0 will then be copied back into those same two Pascal variables. Note that after the inlineP call, all registers are saved so we can use them any way we please. To return to Pascal, we are using an RTS instruction so it will be necessary for our machine language routine to calculate our return address. Fortunately A0 points at the word before where we must return.
ROUTINE TO LOAD A0 AND D0 FOR OS TRAP CALLS FROM MACPASCAL.
126: 2848 MOVE.L A0,A4 ;SAVE RETURN
128: 548C ADDQ.L #$2,A4 ;POINT TO RET
;
; get address where trap will go into A0
;
12A: 41FA 000C LEA *+$000E,A0
;
; pop trap from stack and place into code
;
12E: 309F MOVE.W (A7)+,(A0)
;
; get A0 and D0 from stack
;
130: 245F MOVE.L (A7)+,A2 ;POP A0 VAR
132: 265F MOVE.L (A7)+,A3 ;POP D0 VAR
134: 2052 MOVE.L (A2),A0 ;LOAD A0
136: 2013 MOVE.L (A3),D0 ;LOAD D0
;
; this word gets overwritten by real trap
;
138: FFFF .word $FFFF ;do trap now
;
; pass back A0 and D0 contents
;
13A: 2488 MOVE.L A0,(A2) ;SAVE RET A0
13C: 2680 MOVE.L D0,(A3) ;SAVE RET D0
13E: 4ED4 JMP (A4);RETURN
We will stuff the machine code for our little OS trap caller above into memory by using the STUFFHEX routine from our Pascal program. The above listing is provided to show how the machine language patch works. Two OS traps, ReadDateTime and Secs2Date will be called using our OS trap caller, returning the information in variables of our DateTime record. From this example, it should be obvious how to access other OS type traps using INLINE procedures. (As we mentioned, the undocumented GENERIC can also be used for this purpose.)
program show_date_time;
{ This is a test of a method of accessing }
{ the Mac OS toolbox traps from Mac }
{ Pascal that are register based, using }
{ A0 and D0 only. The call will be: }
{inlineP($4E75,@d0, @a0, trap,@access)}
{ d0 and a0 hold values of registers D0 }
{ and A0 before and after the call. Trap }
{ refers to the trap address being called.}
type
DaTimRec = record
year: integer;
month: integer;
day: integer;
hour: integer;
minute: integer;
second: integer;
dayofweek: integer;
end;
var
currentTime: DatimRec;
procedure GetTime(var date: DaTimRec);
var
access: array[0..12] of integer;
d0,a0, secs: longint;
begin
stuffhex(@access,2848548C41FA000C309F245F265F20522013FFFF248826804ED4');
a0:=ord(@secs);
inlineP($4E75,@d0,@a0,$A139, @access);
{ReadDateTime, pointer to secs in A0}
a0:=ord(@date);
inlineP($4E75,@secs,@a0,$A9C6,@access);
{Secs2Date, secs in d0, pointer to record}
{is in A0.}
end;
begin {main}
GetTime(currentTime);
with currentTime do
begin
writeln(year is, year);
writeln(month is, month);
writeln(day is, day);
writeln(hour is, hour);
writeln(minute is, minute);
writeln(second is, second);
writeln(year is, year);
writeln(day of week is, dayofweek);
end;
end.