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 you’re 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 it’s not using them, it won’t 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. Here’s 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 hasn’t 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 Grafport’s 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 Grafport’s 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 don’t 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.