Virtual Memory
Virtual Memory
This chapter describes the Virtual Memory Manager, the part of the Operating System
that allows memory to be extended beyond the limits of the physical address space
provided by the available RAM. A user can select (in the Memory control panel) whether
to enable this larger or “virtual” address space.
Most applications are completely unaffected by the operation of the Virtual Memory
Manager and have no need to know whether any virtual memory is available.
You might, however, need to intervene in the otherwise automatic workings of
the Virtual Memory Manager if your application has critical timing requirements,
executes code at interrupt time, or performs debugging operations.
The Virtual Memory Manager also offers services that might be of use to software
components even if virtual memory is not enabled on a particular computer. On some
Macintosh computers, the physical address space is discontiguous and is therefore not
identical with the logical address space. In normal operations, the Operating System uses
the MMU coprocessor to map logical addresses to their corresponding physical
addresses. In some cases, however, you might need to perform this address mapping 3
yourself. For example, if you are writing software that runs in the Macintosh Operating
and work with larger amounts of data than would be possible if the logical address
space were limited to the available RAM. Instead of equipping a computer with amounts
of RAM large enough to handle all possible needs, the user can install only enough RAM
to meet average needs. Then, during those occasional times when more memory is
needed for large tasks or many applications, the user can take advantage of virtual
memory. When virtual memory is present, the perceived amount of RAM can be
extended to as much as 14 MB on systems with 24-bit addressing and as much as 1 GB
on systems with 32-bit addressing.
The Virtual Memory Manager also provides a number of routines that your software can
use to modify or get information about its operations. You can use the Virtual Memory
Manager to
■ hold portions of the logical address space in physical RAM
■ lock portions of the logical address space in their physical RAM locations
■ determine whether a particular portion of the logical address space is currently in
physical RAM
■ determine, from a logical address, the physical address of a block of memory
This section describes how the Virtual Memory Manager provides virtual memory. It
also explains why you might need to use certain Virtual Memory Manager routines even
when virtual memory is not available.
Virtual Memory 3
The Virtual Memory Manager extends the logical address space by using part of the
available secondary storage (such as a hard disk) to hold portions of applications and
data that are not currently in use in physical memory. When an application needs to
operate on portions of memory that have been transferred to disk, the Virtual Memory
Manager loads those portions back into physical memory by making them trade places
with other, unused segments of memory. This process of moving portions (or pages) of
memory between physical RAM and the hard disk is called paging.
For the most part, the Virtual Memory Manager operates invisibly to applications and to
the user. Most applications do not need to know whether virtual memory is installed
unless they have critical timing requirements, execute code at interrupt time, or perform
debugging operations. The only time that users need to know about virtual memory is
when they configure it in the Memory control panel. One visible cost of this extra
memory is the use of an equivalent amount of storage on a storage device, such as a SCSI
hard disk. Another cost of using virtual memory is a possible perception of sluggishness
as paged-out segments of memory are pulled back into physical memory. Performance
degradation due to the use of virtual memory ranges from unnoticeable to severe,
depending on the ratio of virtual memory to physical RAM and the behavior of the
actual applications running.
There are two main requirements for running virtual memory. First, the computer must
be running system software version 7.0 or later. Second, the computer must be equipped
with an MMU or PMMU coprocessor. Apple’s 68040- and 68030-based machines have
an MMU built into the CPU and are ready to run virtual memory with no additional
hardware. A Macintosh II (68020-based) computer can take advantage of virtual memory
if it has the 68851 PMMU coprocessor on its main logic board in place of the standard
Address Management Unit (AMU). (The PMMU is the same coprocessor needed to run
A/UX.) Apple’s 68000-based machines cannot take advantage of virtual memory.
Users control and configure virtual memory through the Memory control panel. Controls
in this panel allow the user to turn virtual memory on or off, set the size of virtual
memory, and set the volume on which the invisible backing-store file resides. (The
backing-store file is the file in which the Operating System stores the contents of
nonresident pages of memory.) Other memory-related user controls appear in this
control panel. These include settings for the disk cache and for 24-bit or 32-bit Memory
Manager addressing. If users change the virtual memory, addressing, or disk cache
3
settings, they must restart the computer for the changes to take effect.
24-Bit Addressing 3
When running with 24-bit addressing, the Memory Manager can address at most
224 bytes, or 16 MB. Of these 16 MB, at most 8 MB can be used to address physical RAM.
The remaining 8 MB are devoted to ROM addresses, I/O device addresses, and NuBus
slot addresses. Figure 3-1 illustrates the logical address space mapping used by the 24-bit
Memory Manager.
Note
In some Macintosh computers, the ROM is mapped to the address range
$01000000 to $010FFFFF (indicated as belonging to slot $A in Figure 3-1).
In these computers, the maximum amount of physical RAM is 10 MB
instead of 8 MB. The remainder of this section describes the original
layout of the 24-bit logical address space only. ◆
High memory
$01000000
I/O devices
$00F00000
Slot $E
Slot $D
Slot $C
NuBus
Slot $B addresses
Slot $A
Slot $9
$00900000
ROM
$00800000
RAM
addresses
$00000000
Low memory
When 24-bit addressing is in operation and virtual memory is available, the Virtual
Memory Manager uses, as part of the addressable application memory, any 1 MB
segments not assigned to a NuBus card. For example, if a Macintosh computer has three
NuBus expansion cards installed, that computer can address at most 11 MB of virtual
memory. The maximum amount of virtual memory possible in a 24-bit environment is
14 MB (that is, 8 MB of physical RAM + 6 MB of additional space previously reserved for
the NuBus); this maximum is achievable only on a computer with no NuBus expansion
cards installed.
Notice in Figure 3-1 that addresses from $00800000 to $008FFFFF are reserved for ROM.
In other words, the largest contiguous block of space that an application can allocate
when virtual memory is available is somewhat less than 8 MB, even though the total
amount of virtual memory available can be as large as 14 MB. The rest of the virtual
memory can be in a contiguous block as large as 4 or 5 MB, unless the user has
fragmented the NuBus space by making a poor choice of slots in which to install
expansion cards. To maximize the amount of contiguous virtual memory, users should
3
place cards in consecutive slots at either end of the expansion bus. A haphazard
placement of NuBus cards may result in a number of 1 MB or 2 MB “islands” in the
Note
Some Macintosh computers have fewer than six NuBus slots, and the
numbering of the slots is not consistent across different models. In a
Macintosh IIcx, the three available slots are numbered $9 through $B, so
expansion cards should be grouped toward the lowest-numbered slot
(contiguous with the ROM space). In a Macintosh IIci, the slots are
numbered $C through $E, so expansion cards should be grouped toward
the highest-numbered slot (contiguous with the I/O space). However,
the RAM-based video on the Macintosh IIci occupies addresses reserved
for slot $B; as a result, it is impossible to avoid some degree of
fragmentation of the virtual address space when you use the
RAM-based video option on that computer. ◆
32-Bit Addressing 3
When running with 32-bit addressing, the Memory Manager can address at most
232 bytes, or 4 GB. Of these 4 GB, at most 1 GB can be used to address physical RAM. The
remaining 3 GB are devoted to ROM addresses, I/O device addresses, and NuBus slot
addresses. Figure 3-2 illustrates the logical address space mapping used by the 32-bit
Memory Manager.
High memory
$FFFFFFFF
NuBus
$F1000000
Reserved
$F0000000
NuBus
addresses
$60000000
ROM ROM
addresses
$40000000
RAM
addresses
$00000000
Low memory
Note
The fragmentation of the virtual address space that sometimes occurs
when 24-bit addressing is in operation is never a problem when 32-bit
addressing is in operation. In the 32-bit address space, virtual memory
and the NuBus slots do not share space. ◆
Figure 3-3 The physical address space on a Macintosh IIci with 8 MB of RAM
High memory
$04400000
(68 MB)
Bank B
$04000000
(64 MB)
$00400000
(4 MB)
Bank A
$00000000
Low memory
In most cases, a discontiguous physical address space causes no problems, because the
Operating System uses the MMU coprocessor to map the available physical memory into
a single contiguous logical address space. All memory addresses returned to your
application by the Memory Manager (for instance, when you allocate a new block by
calling NewHandle) are logical addresses. When you read or write a logical memory
address, the Operating System uses the MMU coprocessor to determine the physical
address corresponding to your logical address. This address translation is completely
transparent to your application. For example, if you read the system global variable
located at address $10C, it doesn’t matter that the CPU actually looks at the physical
address $0400010C.
In some cases, however, you can run into problems if you don’t account for the
possibility that the logical address space and the physical address space might differ.
Suppose, for instance, that you are developing a driver that passes addresses to NuBus
master hardware. In this case, you need to take care to pass it physical addresses only,
because NuBus hardware does not use the MMU to translate logical addresses into
physical addresses. If your driver passes a logical address, the NuBus hardware cannot
translate it into a physical address because it does not have access to the MMU’s
address-mapping tables. If your hardware then attempts to write data to that address, it
is likely to overwrite some other portion of physical memory.
To prevent this problem, you need to make certain that you always convert logical
addresses to their corresponding physical addresses before you pass those addresses to
any alternate bus master. You can do this by calling the GetPhysical function, as
described later in “Mapping Logical to Physical Addresses,” which begins on page 3-16.
The GetPhysical function is implemented in ROM on all machines that have a
discontiguous physical address space—whether or not virtual memory is available.
3
Accordingly, before you pass addresses to an alternate bus master, you should check for
the availability of the GetPhysical call; if it’s available, you should use it to translate
Note
Passive or slave NuBus cards (such as video cards) that do not read or
write physical RAM are not likely to be affected by the presence of
virtual memory or by a discontiguous physical address space. ◆
Page Faults 3
When an application or other software component tries to access data in a page of
memory that is not currently resident in RAM, the Operating System issues a special
kind of bus error known as a page fault. The Virtual Memory Manager intercepts page
faults and tries to load the affected page or pages into memory. It does so by executing
its own internal page-fault handler, which handles page faults and passes other bus
errors to the standard bus-error vector in low memory.
To load the required pages into memory, the Virtual Memory Manager’s page-fault
handler takes over the SCSI bus and makes calls directly to the driver of the
backing-store file. While the Virtual Memory Manager is handling a page fault, it is
essential that no other page faults occur. If a page fault did occur during page-fault
handling—a condition known as a double page fault—the Virtual Memory Manager
would have to interrupt the driver of the paging device to make a further request to load
the needed page. Unless the driver of the paging device is concurrent (that is, able to
handle several requests at once), the driver cannot handle this second request.
Unfortunately, current versions of most SCSI disk drivers are not concurrent. As a result,
a double page fault results in a system crash.
The Virtual Memory Manager takes special steps to avoid double page faults caused by
user code (that is, code that is not executed as the result of an exception). It defers all
user code while the driver of the paging device is busy. In particular, the Virtual Memory
Manager defers until a safe time the following types of code:
■ VBL tasks
■ Slot-based VBL tasks
■ Time Manager tasks
■ I/O completion routines
Note
Because these types of tasks may be deferred under virtual memory,
any application or device driver that uses them to achieve real-time
performance might be adversely affected by the operation of the Virtual
Memory Manager. ◆
Other software components must take care not to cause page faults at interrupt time. In
particular, device drivers, which commonly run at interrupt time, should make certain
that any data structures or buffers that they reference at interrupt time are in physical
memory at that time. You can make sure that this happens by holding the required data
in physical memory, as described in “Holding and Releasing Memory” on page 3-14.
In an effort to maintain compatibility with existing drivers, the Operating System
automatically keeps the entire system heap in physical memory at all times. Therefore,
if your device driver and its associated data structures are loaded into the system heap,
you do not need to worry about causing page faults at interrupt time.
▲ WA R N I N G
Future versions of the system software are not guaranteed to keep the
entire system heap in physical memory. To be safe, you should explicitly
hold in physical memory any code or data that you know might be
accessed at interrupt time. ▲
The Virtual Memory Manager provides this further level of protection against page
faults caused by device drivers at interrupt time: it automatically holds in physical
memory any buffers used by the Device Manager _Read and _Write operations. Any
driver that uses the _Read and _Write calls to move data between main memory and
the driver’s associated hardware device is therefore automatically compatible with
virtual memory. If, however, you use _Status or _Control calls to move data at
interrupt time, you must explicitly hold or lock all buffers that are referenced in the
_Status or _Control parameter block. If possible, you should rewrite your driver
so that it uses _Read and _Write calls instead of _Status and _Control calls to
move data.
The Virtual Memory Manager provides one other routine that you can use to help
prevent double page faults. If your application or other code installs interrupt routines
other than those handled automatically by the Virtual Memory Manager (such as VBL
tasks, Time Manager tasks, and Device Manager completion calls), you can explicitly
defer the execution of the routine by calling it via the function DeferUserFn. See
“Deferring User Interrupt Handling” on page 3-20 for details on calling DeferUserFn.
Note
The vast majority of applications do not need to use these
routines. They are used primarily by drivers, debuggers, and other
interrupt-servicing code. ◆
If necessary, your software can request that a range of memory be held in physical
memory. Holding means that the specified memory range cannot be paged out to disk,
although it might be moved within physical RAM. As a result, no page faults can result 3
from reading or writing memory addresses of pages that are held in memory.
Note
Sometimes you don’t need to check whether virtual memory is actually
available before calling some Virtual Memory Manager routines. For
example, you might need to call the GetPhysical function even if
virtual memory is not enabled. Instead of calling Gestalt to see
whether virtual memory is available, you should simply test whether
the appropriate trap is available. In the case of the GetPhysical
function, you should check that the _MemoryDispatchA0Result trap
is available. ◆
You can also use the Gestalt function to obtain information about the memory
configuration of the system, in particular, information about the amount of physical
memory installed in a computer, the amount of logical memory available in a computer,
the version of virtual memory installed (if any), and the size of a logical page. By
obtaining this information from Gestalt, you can help insulate your applications or
drivers from possible future changes in the details of the virtual memory
implementation.
Note
If you use the device-level _Read and _Write functions when doing
data transfers, the Virtual Memory Manager automatically ensures that
the data buffers and parameter blocks are held before the transfer
of data. ◆
The following sample code instructs the Virtual Memory Manager to hold in RAM an
8192-byte range of memory starting at address $32500:
myAddress := $32500;
myLength := 8192;
myErr := HoldMemory(myAddress, myLength);
Note that whole pages of the virtual address space are held, regardless of the starting
address and length parameters you supply. If the starting address parameter supplied to
the HoldMemory function is not on a page boundary, then it is rounded down to the
nearest page boundary. Similarly, if the specified range does not end on a page boundary,
the length parameter is rounded up so that one or more whole pages are held. This
rounding might result in the holding of several pages of physical memory, even if the
specified range is less than a page in length.
To release memory held as a result of a call to HoldMemory, you must use the
UnholdMemory function, which simply reverses the effects of the HoldMemory
function. For example, the page or pages held in memory in the previous example can be
released as follows:
Like holding, releasing applies to whole pages of the virtual address space. Similar
rounding of the address and length parameters is performed, as required, to make the 3
range begin and end on page boundaries.
Note
Don’t confuse locking address ranges in RAM (using LockMemory)
with locking a handle (using HLock). A locked handle can still be
paged out. ◆
Note
It might not be possible to make a range physically contiguous if any of
the pages in the range are already locked. Because a call to
LockMemoryContiguous is not guaranteed to return the desired
results, you must include in your code an alternate method for locking
the necessary ranges of memory. In general, you should avoid calling
LockMemoryContiguous if at all possible. If you must call it, do so as
early as possible—preferably at system startup time—to increase the
likelihood of finding enough contiguous memory. ◆
To unlock a range of previously locked pages, use the UnlockMemory function. This
function reverses the effects of LockMemory or LockMemoryContiguous. Unlocked
pages are marked as cacheable.
Locking, contiguous locking, and unlocking operations are applied to ranges of the
logical address space. If necessary to force the ranges onto page boundaries, the Virtual
Memory Manager performs rounding of addresses and sizes, as described in “Holding
and Releasing Memory” on page 3-14.
The GetPhysical function allows you to obtain the physical addresses that correspond
to any logically addressable range of main memory. To specify the logical address
range to be translated, you use a memory-block record, defined by the MemoryBlock
data type.
TYPE MemoryBlock =
RECORD
address: Ptr; {start of block}
count: LongInt; {size of block}
END;
Note
Don’t confuse the blocks of memory defined by the MemoryBlock data 3
type with memory blocks as manipulated by the Memory Manager. The
TYPE LogicalToPhysicalTable =
RECORD
logical: MemoryBlock; {a logical block}
physical: ARRAY[0..defaultPhysicalEntryCount-1] OF
MemoryBlock; {equivalent physical blocks}
END;
To call GetPhysical, you need to pass a translation table whose logical field
specifies the logical address range you want to translate. You also need to specify how
many contiguous physical address ranges you want returned. In this way, you can adjust
the number of elements in the array to suit your own needs. By default, a translation
table contains enough space for eight physical memory blocks.
CONST defaultPhysicalEntryCount = 8;
The algorithm used here to calculate the number of physical entries returned (myCount)
allows you to change the size (and hence the type) of the myTable variable to include
more or fewer memory blocks. The default size of the translation table is sufficient for
most purposes. Before you do the translation, you can determine how many physical
blocks you need to accommodate the entire logical address space specified in the table’s
logical parameter. To determine this, you pass a variable whose initial value is 0:
If the value of its second parameter is 0, GetPhysical returns in that parameter the
total number of physical blocks that would be required to translate the entire logical
address range. In this case, both the logical and physical fields of the translation
table are unchanged.
If the value of its second parameter is not 0, GetPhysical returns in the physical
field of the translation table an array specifying the physical blocks that correspond to
the logical address specified in the logical field. The GetPhysical function returns
in its second parameter the number of entries in that array (which may be fewer than
were asked for). If the translation table was not large enough to contain all the physical
blocks corresponding to the logical block, GetPhysical updates the fields of the
logical memory block to reflect the remaining number of bytes in the logical range left
to translate (count field) and the next address in the logical address range to translate
(start field).
Note
You must lock (using LockMemory) the address range passed to
GetPhysical to guarantee that the translation data returned are
accurate (that is, that the logical pages do not move around in physical
memory and that paging activity has not invalidated the translation
data). An error is returned if you call GetPhysical on an address
range that is not locked. ◆
Recall that you sometimes need to call GetPhysical even if virtual memory is not
available. (See “The Physical Address Space” on page 3-9 for details.) In general, if
GetPhysical is available in the operating environment, then you should call it
any time your software exports addresses to a NuBus expansion card that can read or
write physical RAM directly. Listing 3-1 defines a general algorithm for implementing
driver calls to a generic NuBus master card. To maximize compatibility with virtual
memory, make sure that your hardware and device drivers support this method of
issuing driver calls.
PROGRAM GetPhysicalUsage;
USES Types, Traps, Memory, Utilities;
CONST
kTestPtrSize = $100000;
VAR
myPtr: Ptr;
myPtrSize: LongInt;
hasGetPhysical: Boolean; {does this machine have GetPhysical?}
lockOK: Boolean; {was the block successfully locked?}
myErr: OSErr;
myTable: LogicalToPhysicalTable;
myCount: LongInt;
index: Integer; 3
physical[index].count)
ELSE
BEGIN
{Handle GetPhysical error indicated by myErr.}
{Loop will terminate unless myErr is reset to noErr.}
END;
END; {WHILE}
{Always unlock a range you locked; ignore any error here.}
myErr := UnlockMemory(myPtr, myPtrSize);
END
ELSE {not lockOK}
BEGIN
{handle LockMemory error indicated by myErr}
END;
END
ELSE {GetPhysical not available}
SendDMACmd(myPtr, myPtrSize);
END; {IF myPtr}
END.
If the GetPhysical function is not available, the program defined in Listing 3-1 simply
calls your routine to send a DMA command to the NuBus hardware. In that case, no
address translation is necessary. If, however, GetPhysical is available, you need to
lock the logical address range whose physical addresses you want to get. If you
successfully lock the range, you can call GetPhysical as illustrated earlier. Be sure to
unlock the range you previously locked before exiting the program.
▲ WA R N I N G
Some Macintosh computers contain the _MemoryDispatch trap in
ROM, even though they do not contain an MMU coprocessor. In this
case, the system software patches the _MemoryDispatch trap to make
it appear unimplemented. However, software that executes before
system patches are installed cannot use this as a test of whether to call
GetPhysical or not. If your code is executed before the installation of
system patches, you should use the Gestalt function to test directly for
the existence of an MMU coprocessor. ▲
The use of procedure pointers (variables of type ProcPtr) in specifying I/O completion
routines, socket listeners, and so forth makes it impossible for drivers to know the exact
location and size of all code or buffers that might be referenced when these routines are
invoked. However, these routines must still be called only at a safe time, when paging is
not currently in progress. Because the locations of all needed pages cannot be known, an
alternate strategy is used to prevent a fatal double page fault.
The DeferUserFn function is provided to allow interrupt service routines to defer, until
a safe time, code that might cause page faults. This function determines whether the call
can be made immediately and, if it is safe, makes the call. If a page fault is in progress,
the address of the service routine and its parameter are saved, and the routine is deferred
until page faults are again permitted.
Bus-Error Vectors 3
The Operating System needs to intercept page faults and do the necessary paging. In
addition, various applications and pieces of system software need to handle other kinds
of bus errors. Virtual memory takes care of the complications of bus-error handling by
providing two bus-error vectors. The vector that applications and other system software
see is the one in low memory (at address $8). The vector that virtual memory uses (the
one actually used by the processor) is in virtual memory’s private storage and is pointed
to by the Vector Base Register (VBR). Virtual memory’s bus-error handler handles page
faults and passes other bus errors to the vector in low memory at address $8.
When a debugger wants the contents of a page to be loaded into memory, it can read a
byte from that page. The Operating System detects the page fault and loads the
appropriate page (perhaps swapping another page to disk).
Note that a debugger will probably temporarily replace one or both of the bus-error
vectors while it is executing. A debugger that wants virtual memory to continue paging
while the debugger runs can put a handler only in the low-memory bus-error vector. A
debugger that displays memory without allowing virtual memory to continue paging
can put a handler in the virtual memory’s bus-error vector (at VBR + $8).
Because the current version of virtual memory is not reentrant, there are times when
trying to load a page into memory would be fatal. To allow for this, you can use the
PageFaultFatal function to determine whether a page fault would be fatal at that
time. If this function returns TRUE, the debugger should not allow the virtual memory’s
bus-error handler to detect any page faults. Thus, you should always replace the virtual
memory’s bus-error vector if the PageFaultFatal function returns TRUE.
Unfortunately, the LockMemory function is intended for use by device drivers and
automatically disables data caching for the locked pages. Because this is not desirable for
the debugger, the functions DebuggerLockMemory and DebuggerUnlockMemory
lock pages without inhibiting the caching of those pages. Note that both stack, code, and
other storage used by the debugger might need to be locked in this way.
Supervisor Mode 3
Because a debugger is typically activated through one of the processor vectors, it usually
executes in supervisor mode, allowing it access to all of memory and all processor
registers. When the debugger is entered in another way—for example, through
the _Debugger or _DebugStr trap or when it is first loaded—it is necessary to enter
supervisor mode. You can accomplish this with the following assembly-language
instructions:
MOVEQ #EnterSupervisorMode,D0 3
_DebugUtil ;OS trap to DebugUtils
The code switches the caller into supervisor mode, and the previous status register is
returned in register D0. Thus, when the debugger returns to the interrupted code, you
can restore the previous interrupt level, condition codes, and so forth. When the
debugger is ready to return to user mode, it simply loads the status register with the
result returned in D0. Entering supervisor mode also switches the stack pointer from the
user stack pointer (USP) to the interrupt stack pointer (ISP); reentering user mode
changes the stack pointer back to the user stack pointer.
Keyboard Input 3
A debugger can obtain the user’s keyboard input by calling the DebuggerPoll
procedure. This routine can obtain keyboard input even when interrupts are disabled.
After you call this service, you must then obtain keyboard events through the normal
event-queue mechanism.
Page States 3
Debuggers need a way to display the contents of memory without paging or to display
the contents of pages currently on disk. The GetPageState function returns one of
these values to specify the state of a page containing a virtual address:
CONST
kPageInMemory = 0; {page is in RAM}
kPageOnDisk = 1; {page is on disk}
kNotPaged = 2; {address is not paged}
A debugger can use this information to determine whether certain memory addresses
should be referenced. Note that ROM and I/O space are not pageable and therefore are
considered not paged.
Data Structures 3
The Virtual Memory Manager defines two data structures for use with the
GetPhysical function, the memory-block record and the translation table.
Memory-Block Record 3
The GetPhysical function uses a memory-block record to hold information about a
block of memory, either logical or physical. The memory-block record is a data structure
of type MemoryBlock.
TYPE MemoryBlock =
RECORD
address: Ptr; {start of block}
count: LongInt; {size of block}
END;
Field descriptions
address A pointer to the beginning of a block of memory.
count The number of bytes in the block of memory.
Translation Table 3
The GetPhysical function uses a translation table to hold information about a logical
address range and its corresponding physical addresses. A translation table is defined by
the data type LogicalToPhysicalTable.
TYPE LogicalToPhysicalTable =
RECORD
logical: MemoryBlock; {a logical block}
physical: ARRAY[0..defaultPhysicalEntryCount-1] OF
MemoryBlock; {equivalent physical blocks}
END;
Field descriptions
logical A logical block of memory whose corresponding physical blocks are
to be determined. 3
physical A physical translation table that identifies the blocks of physical
Routines 3
This section describes the routines you can use to control virtual memory. The section
“Virtual Memory Management” describes the routines that allow you to control pages in
physical memory, and the section “Virtual Memory Debugger Support Routines”
describes the routines that only programmers implementing debuggers need to use.
HoldMemory 3
To make a portion of the address space resident in physical memory and ineligible for
paging, use the HoldMemory function.
DESCRIPTION
The HoldMemory function makes the portion of the address space beginning at
address and having a size of count bytes resident in physical memory and ineligible
for paging.
If the address parameter supplied to the HoldMemory function is not on a page
boundary, then it is rounded down to the nearest page boundary. Similarly, if the
specified range does not end on a page boundary, the count parameter is rounded up so
that the entire range of memory is held.
SPECIAL CONSIDERATIONS
Even though HoldMemory does not move or purge memory, you should not call it at
interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the HoldMemory function are
Trap macro Selector
_MemoryDispatch $0000
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
A0 Starting address
A1 Number of bytes to hold
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notEnoughMemoryErr –620 Insufficient physical memory
interruptsMaskedErr –624 Called with interrupts masked
UnholdMemory 3
To make a currently held range of memory eligible for paging again, use the
UnholdMemory function.
DESCRIPTION
The UnholdMemory function makes the portion of the address space beginning at
address and having a size of count bytes eligible for paging.
If the address parameter supplied to the UnholdMemory function is not on a page 3
boundary, then it is rounded down to the nearest page boundary. Similarly, if the
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the UnholdMemory function are
Trap macro Selector
_MemoryDispatch $0001
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
A0 Starting address
A1 Number of bytes to release
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notHeldErr –621 Specified range of memory is not held
interruptsMaskedErr –624 Called with interrupts masked
LockMemory 3
To make a portion of the address space immovable in physical memory and ineligible for
paging, use the LockMemory function.
DESCRIPTION
The LockMemory function makes the portion of the address space beginning at
address and having a size of count bytes immovable in physical memory and
ineligible for paging.
If the address parameter supplied to the LockMemory function is not on a page
boundary, it is rounded down to the nearest page boundary. Similarly, if the specified
range does not end on a page boundary, the count parameter is rounded up so that the
entire range of memory is locked.
The CPU marks locked pages as noncacheable. On Macintosh computers containing the
Macintosh IIci ROM, all physical RAM is marked noncacheable.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the LockMemory function are
Trap macro Selector
_MemoryDispatch $0002
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
A0 Starting address
A1 Number of bytes to lock
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notEnoughMemoryErr –620 Insufficient physical memory
interruptsMaskedErr –624 Called with interrupts masked
LockMemoryContiguous 3
DESCRIPTION
The LockMemoryContiguous function makes the portion of the address space 3
beginning at address and having a size of count bytes immovable in physical memory
and ineligible for paging. The function attempts to obtain a contiguous block of physical
SPECIAL CONSIDERATIONS
Because a call to LockMemoryContiguous is not guaranteed to succeed,
all code that uses LockMemoryContiguous must have an alternate method
for locking the necessary ranges of memory. In general, you should avoid using
LockMemoryContiguous if at all possible. If you must call it, do so as early as
possible—preferably at system startup time—to increase the likelihood that enough
contiguous memory can be found.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the LockMemoryContiguous function are
Trap macro Selector
_MemoryDispatch $0004
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notEnoughMemoryErr –620 Insufficient physical memory
cannotMakeContiguousErr –622 Cannot make specified range contiguous
interruptsMaskedErr –624 Called with interrupts masked
UnlockMemory 3
DESCRIPTION
The UnlockMemory function makes the portion of the address space beginning at
address and having a size of count bytes movable in real memory and eligible for
paging again.
If the address parameter supplied to the UnlockMemory function is not on a page
boundary, then it is rounded down to the nearest page boundary. Similarly, if the
specified range does not end on a page boundary, the count parameter is rounded up so
that the entire range of memory is unlocked.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the UnlockMemory function are
Trap macro Selector
_MemoryDispatch $0003
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notLockedErr –623 Specified range of memory is not locked
interruptsMaskedErr –624 Called with interrupts masked 3
To translate logical addresses into their corresponding physical addresses, use the
GetPhysical function.
addresses A translation table. On entry, set the logical field of this record to the
block of memory to translate. On exit, the physical field of this record
holds the corresponding physical address blocks.
physicalEntryCount
The number of physical entries to translate. On entry, set this field to 0 if
you want GetPhysical to return the number of table entries needed to
translate the entire logical address range.
DESCRIPTION
The GetPhysical function translates a logical address range into its corresponding
physical address ranges. The logical field of the addresses translation table specifies
the logical address range to be translated. GetPhysical translates up to the size of the
physical table or until it completes the translation, whichever occurs first.
If you call GetPhysical with the physicalEntryCount parameter set to 0, it returns
in physicalEntryCount the number of table entries needed to translate the entire
address range. In this case, the translation table specified by the addresses parameter
is unchanged.
Note
The logical address range must be locked to ensure validity of the
translation data. ◆
SPECIAL CONSIDERATIONS
The GetPhysical function as currently implemented under virtual memory supports
only logical RAM. You cannot use GetPhysical to translate addresses in the address
spaces of the ROM, I/O devices, or NuBus slots. Some Macintosh computers map a
portion of the physical RAM into NuBus space, to simulate the presence of a video
expansion card. GetPhysical returns the result code paramErr if you attempt to read
that memory.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the GetPhysical function are
Trap macro Selector
_MemoryDispatchA0Result $0005
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
A0 Pointer to a translation table
A1 physicalEntryCount in table
Registers on exit
A0 physicalEntryCount translated
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notLockedErr –623 Specified range of memory is not locked
interruptsMaskedErr –624 Called with interrupts masked
SEE ALSO
See “Mapping Logical to Physical Addresses,” beginning on page 3-16, for a method of
calling GetPhysical to translate addresses to be sent to a NuBus master card.
DeferUserFn 3
To determine whether code that might cause page faults can safely be called
immediately, use the DeferUserFn function.
userFunction
The address of the routine to run. 3
argument A pointer to the argument to pass to the specified routine.
ASSEMBLY-LANGUAGE INFORMATION
The registers on entry and exit for the DeferUserFn function are
Registers on entry
A0 Address of function
D0 Argument for function
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
cannotDeferErr –625 Unable to defer additional user functions
DebuggerGetMax 3
The Memory Manager includes a special routine that debuggers use, instead of the
Gestalt function, to determine which debugger functions are present.
DESCRIPTION
The DebuggerGetMax function returns the highest selector number of the debugger
routines that are defined in terms of the _DebugUtil trap. The numbers correspond to
the following routines:
Selector Routine
$0000 DebuggerGetMax
$0001 DebuggerEnter
$0002 DebuggerExit
$0003 DebuggerPoll
$0004 GetPageState
$0005 PageFaultFatal
$0006 DebuggerLockMemory
$0007 DebuggerUnlockMemory
$0008 EnterSupervisorMode
Of course, you should use the Gestalt function to check whether virtual memory
is available at all before you call the DebuggerGetMax function.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the DebuggerGetMax function are
Trap macro Selector
_DebugUtil $0000
Registers on exit
D0 Highest available selector
DebuggerEnter 3
PROCEDURE DebuggerEnter;
3
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the DebuggerEnter procedure are
Trap macro Selector
_DebugUtil $0001
The registers on entry for this routine are
Registers on entry
D0 Selector code
DebuggerExit 3
PROCEDURE DebuggerExit;
DESCRIPTION
The DebuggerExit procedure allows the _DebugUtil trap to clean up after all
debugging calls are completed.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the DebuggerExit procedure are
Trap macro Selector
_DebugUtil $0002
The registers on entry for this routine are
Registers on entry
D0 Selector code
PageFaultFatal 3
DESCRIPTION
The PageFaultFatal function returns TRUE if the debugger should not allow the
virtual memory’s bus-error handler to detect any page faults.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the PageFaultFatal function are
Trap macro Selector
_DebugUtil $0005
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
Registers on exit
D0 Returned value
DebuggerLockMemory 3
To lock a portion of the address space (as the LockMemory function does) while leaving
data caching enabled on the affected pages, use the DebuggerLockMemory function.
address The start address of the range of memory that is to be locked in RAM.
count The size in bytes of the range of memory that is to be locked in RAM.
DESCRIPTION
The DebuggerLockMemory function makes the portion of the address space beginning
at address and having a size of count bytes immovable in physical memory and 3
ineligible for paging. The function leaves data caching enabled on the affected pages.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the DebuggerLockMemory function are
Trap macro Selector
_DebuggerLockMemory $0006
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
A0 Starting address
A1 Number of bytes to hold
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notEnoughMemoryErr –620 Insufficient physical memory
DebuggerUnlockMemory 3
DESCRIPTION
The DebuggerUnlockMemory function makes the portion of the address space
beginning at address and having a size of count bytes movable in real memory and
eligible for paging again.
If the address parameter supplied to the DebuggerUnlockMemory function is not on
a page boundary, then it is rounded down to the nearest page boundary. Similarly, if the
specified range does not end on a page boundary, the count parameter is rounded up so
that the entire range of memory is unlocked.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the DebuggerUnlockMemory function are
Trap macro Selector
_DebugUtil $0007
The registers on entry and exit for this routine are
Registers on entry
D0 Selector code
A0 Starting address
A1 Number of bytes to hold
Registers on exit
D0 Result code
RESULT CODES
noErr 0 No error
paramErr –50 Error in parameter list
notLockedErr –623 Specified range of memory is not locked
DebuggerPoll 3
PROCEDURE DebuggerPoll;
DESCRIPTION
Call the DebuggerPoll procedure, which you can use even if interrupts are disabled,
to poll for keyboard input.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the DebuggerPoll procedure are
Trap macro Selector 3
_DebugUtil $0003
Registers on exit
D0 Result code
GetPageState 3
To obtain the state of a page of logical memory, use the GetPageState function.
DESCRIPTION
The GetPageState function returns the page state of the page containing the address
passed in the address parameter. The returned value is one of these constants:
CONST
kPageInMemory = 0; {page is in RAM}
kPageOnDisk = 1; {page is on disk}
kNotPaged = 2; {address is not paged}
ASSEMBLY-LANGUAGE INFORMATION
The trap macro and routine selector for the GetPageState function are
Trap macro Selector
_DebugUtil $0004
The registers on entry and exit for this routine are
Registers on entry
A0 Address in the page whose state is to be determined
D0 Selector code
Registers on exit
D0 Page state
Pascal Summary 3
Constants 3
CONST
{Gestalt constants}
gestaltVMAttr = 'vm '; {virtual memory attributes}
3
gestaltVMPresent = 0; {bit set if virtual memory present}
{page states}
kPageInMemory = 0; {page is in RAM}
kPageOnDisk = 1; {page is on disk}
kNotPaged = 2; {address is not paged}
Data Types 3
TYPE
PageState = Integer;
Routines 3
C Summary 3
Constants 3
/*Gestalt constants*/
#define gestaltVMAttr 'vm '; /*virtual memory attributes*/
#define gestaltVMPresent 0; /*bit set if virtual memory present*/
/*page states*/
enum {
kPageInMemory = 0, /*page is in RAM*/
kPageOnDisk = 1, /*page is on disk*/
kNotPaged = 2 /*address is not paged*/
};
Data Types 3
Routines 3
Assembly-Language Summary 3
Data Types 3
Trap Macros 3
Selector Routine
$0000 HoldMemory
$0001 UnholdMemory
$0002 LockMemory
$0003 UnlockMemory
$0004 LockMemoryContiguous
_MemoryDispatchA0Result
Selector Routine
$0005 GetPhysical
_DebugUtil
Selector Routine
$0000 DebuggerGetMax
$0001 DebuggerEnter
$0002 DebuggerExit
$0003 DebuggerPoll
$0004 GetPageState
$0005 PageFaultFatal
$0006 DebuggerLockMemory 3
$0007 DebuggerUnlockMemory
Result Codes 3
noErr 0 No error
paramErr –50 Error in parameter list
notEnoughMemoryErr –620 Insufficient physical memory
notHeldErr –621 Specified range of memory is not held
cannotMakeContiguousErr –622 Cannot make specified range contiguous
notLockedErr –623 Specified range of memory is not locked
interruptsMaskedErr –624 Called with interrupts masked
cannotDeferErr –625 Unable to defer additional user functions