Call Register Routines
Volume Number: | | 1
|
Issue Number: | | 5
|
Column Tag: | | Undocumented procedure
|
Call register-based Toolbox/OS routines
By Steve Brecher
Macintosh Pascal provides a built-in procedure named Generic that is not documented. Generic permits you to call register-based Toolbox/OS routines. It can also be used to execute any machine language code that you have stored in a Pascal data structure. This article describes the use of the Generic procedure.
In effect, the following procedure is pre-declared:
procedure Generic(InstructionWord :integer;
VAR Registers : RegRcd);
RegRcd denotes a data structure consisting of 13 32-bit values -- five address register values (A0..A4), followed by eight data register values (D0..D7). The exact type of Registers is immaterial, i.e., you could declare:
var
Registers : record
A : array[0..4] of longint;
D : array[0..7] of longint
end;
Or in some cases you might prefer something like:
var
Registers : record
A0, A1, A2, A3, A4 : ^char;
D0, D1, D2, D3, D4, D5, D6, D7 : longint
end;
The register values that you pass to Generic are written to the MC68000 registers. Then the one-word instruction denoted by the InstructionWord argument is executed. Finally, your Registers structure is updated with the (possibly) new values of the MC68000 registers before Generic returns to your program.
Any registers that are not used by the machine language routine youre invoking do not have to have their corresponding elements in the Registers data structure initialized. The machine language routine will have garbage in those registers (but, since its not using them, it wont care).
Usually, Generic will be used to execute a register-based Toolbox/OS trap. In such cases the value you pass to Generic via the InstructionWord argument is the trap value. Heres an example routine that resets the modem output port to establish new communications parameters:
type
DataBitsT = (Five, Seven, Six, Eight);
{ ^--sic--^ }
ParityT = (OddParity, NoParity, EvenParity);
StopBitsT = (One, OnePointFive, Two);
function ResetSer (Baud : longint;
DataBits : DataBitsT;
Parity : ParityT;
StopBits : StopBitsT) : boolean;
{returns true if no error, false if modem}
{port hasnt been opened yet}
{}
const
PBControl = $A004; {trap value}
noErr = 0;
ModemOutRefNum = -7;
SerReset = 8; {CScode}
{}
var
ParamBlockRec : record
Filler : array[0..11] of integer;
ioRefNum : integer;
csCode : integer;
csParam : integer
end;
Registers : record
A : array[0..4] of longint;
D : array[0..7] of longint
end;
serConfig : longint;
begin {ResetSer}
with ParamBlockRec do
begin
ioRefNum := ModemOutRefNum;
csCode := SerReset;
serConfig := trunc(114571.7 / baud -
1.338395)
+ 1024 * ord(DataBits)
+ 4096 * (ord(Parity) + 1)
+ 16384 * (ord(StopBits) + 1);
csParam := loword(serConfig);
end;
Registers.A[0] := ord(@ParamBlockRec);
Generic(PBControl, Registers);
ResetSer := (Registers.D[0] = noErr)
end; {ResetSer}
The InstructionWord argument to Generic does not have to be a trap value, however. It can be any 16-bit MC68000 instruction, as shown in the following example:
procedure CallCode(VAR Result :integer);
{}
{This example assumes a global integer }
{array named Code which contains a }
{machine language subroutine that takes}
{one VAR (address) argument on the }
{stack. CallCode calls the routine, }
{passing it the address of the Result }
{parameter. }
{}
const
JsrIndirectA0 = $4E90; { Jsr (A0) }
var
Registers : record
A : array[0..4] of ^integer;
D : array[0..7] of longint
end;
Glue : array[1..4] of integer;
begin
{ MoveA.L (SP),A0 ;return addr }
Glue[1] := $2057;
{ Move.L A2,(SP) ;ptr to Result }
Glue[2] := $2E8A;
{ Move.L A0,-(SP) ;return addr }
Glue[3] := $2F08;
{ Jmp (A1) ;to subr in Code array }
Glue[4] := $4ED1;
with Registers do
begin
A[0] := @Glue[1];
A[1] := @Code[1];
A[2] := @Result;
end;
{Call Glue routine, which invokes Code}
{routine...}
Generic(JsrIndirectA0, Registers);
end; {CallCode}
Scroll Rectangle Notes
(NOTE: did not appear with above article in MacTutor)
ScrollRect is a QuickDraw routine which scrolls a rectangle. Bits (pixels) scrolled out of the rectangle are lost, and the area near the side of the rectangle opposite to the scroll direction is filled with the current Grafports background pattern. [The rectangle defining the area to be scrolled must be larger than the screen object you wish to be moved by this procedure to allow room to be lost at the top.]
A Performance Note
ScrollRect will slow down by a factor of 3 or 4 if the scrolled rectangle includes one or more borders of the current Grafports portrect. For fast scrolling, assure that each side of the rectangle passed to ScrollRect is at least two pixels away from a border of the portrect.
A Usage Note
The UpdateRgn parameter (a region handle) passed to ScrollRect enables it to return to you the specification of the region that was vacated by scrolled bits and which ScrollRect filled with the background pattern. (Usually the vacated area will be a sub-rectangle of the scrolled rectangle; but it may be other than rectangular if an odd-shapped visRgn or clipRgn affects the ScrollRect.) The previous contents of the region (i.e., the data structure) are destroyed. If you dont need to know the specification of the area filled with the background pattern (or if you already know), just allocate a dummy region (using NewRgn) and pass ScrollRect a handle to the dummy region. You can allocate the dummy region once at the beginning of your program; or you can allocate it before each call to ScrollRect and then use DisposRgn after each call to free the memory used by the region data structure.