www.riscos.com Technical Support:Programmer's Reference Manual

Memory Management

Introduction

This chapter describes the memory management in RISC OS. This covers memory allocation by a program or module as well as using the MEMC chip to handle how memory is mapped.

In many environments, such as BASIC and C, you can use the language's intrinsic memory allocation routines, which use the calls described in this chapter transparently. For example, refer to Wimp_SlotSize on Wimp_SlotSize of the chapter entitled The Window Manager.

Similarly, small, transiently loaded utilities may not require any memory over the 1024 bytes they are automatically allocated. Some programs and modules, however, will require arbitrary amounts of memory, which can be freed after use. For example, filing systems, specialised VDU drivers such as the font manager and so on. The memory manager provides simple allocation and deallocation facilities. Relocatable modules can use this manager either directly, to manipulate their own private heap, or indirectly using the module support calls.

A block of memory can be set up as a heap. This is a structure that allows arbitrary parts of the block to be allocated and freed. A program simply requests a block of a given size and is given a pointer to it by the heap manager. This block can be expanded or contracted or freed by using this pointer as a reference.

The part of the screen RAM that is not visible on the screen is also available as a temporary buffer. This memory is temporarily available because of the way that vertical scrolling is done.

One of the other memory resources available is the battery-backed CMOS RAM. This is used to hold default system parameters while the power is off. Some spare locations in CMOS RAM are reserved for users' own purposes, and for the use of expansion cards; application authors wishing to use CMOS RAM should ask Acorn for an allocation (although this will not be given if options can instead be saved to file).

The MEMC chip controls how logical addresses (those used by programs or modules) are mapped into the physical memory location to use. Numerous calls are used to control how it does this, though generally this is something that most programs would not want to do.

Overview

Heap manager

RISC OS contains a heap management system. This is used by the operating system to allocate space within the relocatable module area and also to maintain the system heap. A heap is just an area of memory from which bytes may be allocated, then deallocated for later use. An area can also be reallocated, meaning that its size changes.

The heap manager is also available to the user. You provide an area of memory which is to be used for the heap, which can be any size you require. If you are a module, then the heap would be a block within the RMA, and if you are a program, then it would be within the application space.

Thus, it would be a heap within a heap; for example a block in the RMA would be allocated by a module, and then declared as a heap. In theory, this process could continue indefinitely, but in practice this is as far as you need to go.

At the start of a heap, the heap manager sets up the heap descriptor, which is a block containing information on the limits of the heap, etc. This descriptor is updated by the heap manager when necessary.

When a block within this heap is required, a request is made to the heap manager, which returns a pointer to a suitable block of memory. The heap manager keeps a record of the total amount of memory which is free in the heap and the largest individual block which is available.

Heap fragmentation

The heap management system does not provide free-space collation. This is the technique of moving blocks of allocated memory around so as to maximise the contiguous free space and avoiding excessive fragmentation of the heap.

Also, the heap management system will never attempt to move a block within the heap, since it has no knowledge of whether the block contains pointers that need to be relocated, or whether there are any pointers to the block which need updating. Hence, unless an area of contiguous free space of the size requested is available, a request for a block will fail.

MEMC control

The MEMC chip maps logical onto physical addresses. To do this, it maintains a table of entries that map a given memory block to a particular address. Generally, the system will take care of the operation of this mapping for you. Calls are provided to allow you to read this mapping and alter it, but you should have a very good reason to do so, and be certain of what you are doing.

Screen memory

The vertical scrolling technique used under RISC OS is to change the memory location that the screen starts at. This means that part of the screen memory may be unused, depending on the screen mode and the amount of memory reserved. You can use this memory temporarily, as long as you don't cause any output that may scroll the screen. Also remember that this memory is limited to one program using it at a time, so it may not be available every time you request it. Consequently, you cannot rely on it being there when writing a module or application.

Battery-backed CMOS RAM

A block of 240 bytes of battery-backed CMOS RAM is available under RISC OS. Each location has a specific meaning and should not be directly modified unless you are sure of the meaning of the value. Many of these locations are changed indirectly using the *Configure commands. These can be found throughout this manual, in the chapter appropriate to their function.

Some bytes are not allocated, and are reserved for users and applications to use. If you want to use one or more of the application bytes, you should request a location in writing from Acorn Computers. This is so that different applications don't accidentally use the same location.

Technical Details

Guidelines on using memory efficiently

This section provides basic information on memory management by RISC OS applications. It is intended to provide some specialist knowledge to help you write efficient programs for RISC OS, and to provide some practical hints and tips.

All the information in this chapter relating to programs written in C refers to Acorn's Desktop C product.

You should follow the guidelines in this section to make the best use of available memory. The guidelines are explained in more detail on the following pages.

Use recovery procedures - Your program should keep the machine operational. Don't allow your program to lock up when memory runs out; your program should indicate that it has run out of memory (with an error or warning message) and only stop subsequent actions that use more memory. Ideally, ensure that actions which free up memory have enough reserved memory to run in.

Return unwanted memory - You should return any memory you have no further use for. Claiming memory then not returning it can tie up memory unnecessarily until the machine is re-booted. RISC OS has no garbage collection, so once you have asked for memory RISC OS assumes that you want it until you explicitly return it, even if your program terminates execution. Language libraries often provide you with protection from this, as long as memory is claimed from them.

Don't waste memory - You should avoid wasting memory. It is a finite resource, often wasted in two ways:

by permanently claiming memory for infrequent operations

by fragmenting it, so that although there is enough unused memory, it is either in the wrong place, or it is not in large enough blocks to use.

Recovery from lack of memory

An important consideration when designing programs for RISC OS is the recovery process, not just from user errors, but also from lack of system resources.

An example of a technique that can be designed into an application is to make an algorithm more disc-based and less RAM-based on detection of lack of memory. This could allow you to continue using an application on a small machine (especially one with a hard disc) at the expense of some speed.

When implementing your code, expect the unexpected and program defensively. Be sure that when the system resources you need (memory, windows, files etc) are not available, your program can cope. Make sure that, when a document managed by your program expands and memory runs out, the document is still valid and can be saved. Don't just check that your main document expansion routines work; check that all routines which require memory (or in fact any system resource) fail gracefully when there is no more.

Centralising access to system resources can help: write your program as if every operating system interface is likely to return an error.

Avoiding permanent loss of memory

Permanent loss of memory is mainly a problem for applications or modules written entirely in assembly language. When interworking assembler routines with C or another high level language you should use memory handed to you by the high level language library (eg use malloc to get a memory area from C and pass a pointer to it as an argument to your assembler routine). The language library automatically returns such areas to RISC OS on program exit. Additional types of program requiring care to avoid memory loss are those expected to run for a long time (eg a printer spooler) and those making use of RMA directly through SWI calls.

When using the RMA for storage directly through SWI calls, especially for items in linked lists, consider using the first word as a check word containing four characters of text to identify it as belonging to your program. When a block of RMA is deallocated, the heap manager puts it back into a list of free blocks, and in so doing overwrites the first word of the block.

This technique therefore serves two purposes:

after your program has been run and exited, your check word can be searched for, showing up any blocks you have failed to deallocate

it avoids problems when accidentally referencing deallocated memory.

A typical problem of referencing deallocated blocks results from using the first word as a pointer to your program's next block, then accidentally referencing a wild pointer when it is overwritten.

You can use the following BASIC routine to search for any lost blocks:

When writing relocatable module initialisation code you should check that memory and other system resources are returned if initialisation is unable to complete and is going to return with V set. It is often useful to construct module finalisation code as a mirror image of initialisation code so that it can be jumped to when initialisation is going to return an error and cleaned up. A typical algorithm is:

Initialisation

Claim main workspace: If error then keep this error and goto Exit3

Claim secondary workspace: If error then keep this error and goto Exit2

Claim tertiary workspace: If error then keep this error and goto Exit1

Return

Finalisation

Set kept error to null

Release tertiary workspace

Exit1

Release secondary workspace

Exit2

Release main workspace

Exit3

Get kept error (if there was one)

Return

Avoiding memory wastage

The key factor in writing programs that use memory efficiently and don't waste it is understanding the following:

how SWI XOS_Module and SWI XOS_Heap work if you are constructing a relocatable module or are using the RMA from an application

how C flex and malloc work when writing a C program (parts of which may be written in assembler).

This understanding will lead you to writing programs that will work in harmony with the storage allocator. See the following section for a description of C memory allocation.

The C storage manager

Understanding the C storage manager is obviously useful to writers of C. But it may also be useful to writers of assembly language for two reasons: to assist in constructing part C and part assembler programs; to assist in constructing their own memory allocation routines, both as an example algorithm and as an allocator that may be running for other applications at the same time as their own.

Normal C applications (ie those not running as modules) claim memory blocks in two main ways:

from malloc

from flex.

The malloc heap storage manager is the standard interface from which to claim small areas of memory. It is tuned to give good performance to the widest variety of programs.

In the following sections, the word heap refers to the section of memory currently under the control of the storage manager (usually referred to as malloc, or the malloc heap).

The flex facility is available as part of RISC_OSLib, and can be useful for claiming large areas of data space. It manages a shifting set of areas, so its operation can be slow, and address-dependent data cannot be stored in it. However, it has the following advantages:

it doesn't waste memory by fragmenting free space

it returns deallocated memory to RISC OS for use by other applications.

Allocation of malloc blocks

All block sizes allocated are in bytes and are rounded up to a multiple of four bytes. All blocks returned to the user are word-aligned. All blocks have an overhead of eight bytes (two words). One word is used to hold the block's length and status, the other contains a guard constant which is used to detect heap corruptions. The guard word may not be present in future releases of the ANSI C library. When the stack needs to be extended, blocks are allocated from the malloc heap.

When an allocation request is received by the storage manager, it is categorised into one of three sizes of blocks

small - 0 64 bytes

medium - 65 512 bytes

large - 513 16777216 bytes.

The storage manager keeps track of the free sections of the heap in two ways. The medium and large sized blocks are chained together into a linked list (overflow list) and small blocks of the same size are chained together into linked lists (bins). The overflow list is ordered by ascending block address, while the bins have the most recently freed block at the start of the list.

When a small block is requested, the bin which contains the blocks of the required size is checked, and, if the bin is not empty, the first block in the list is returned to the user. If there was not a block of the exact size available, the bin containing blocks of the next size up is checked, and so on until a block is found. If a block is not found in the bins, the last block (highest address) on the overflow list is taken. If the block is large enough to be split into two blocks, and the remainder is a usable size (> 12 including the overhead) then the block is split, the top section returned to the user and the remainder, depending on its size, is either put in the relevant bin at the front of the list or left in the overflow list.

When a medium block is requested, the search ignores the bins and starts with the overflow list. This is searched in reverse order for a block of usable size, in the same way as for small blocks.

When a large block is requested, the overflow list is searched in increasing address order, and the first block in the list which is large enough is taken. If the block is large enough to be split into two blocks, and the size of the remainder is larger than a small block (> 64) then the block is split, the top section is returned to the overflow list, and bottom section given to the user.

Should there not be a block of the right size available, the C storage manager has two options:

Take all the free blocks on the heap and join adjacent free blocks together (coalescing) in the hope that a block of the right size will be created which can then be used

Ask the operating system for more heap, put the block returned in the overflow list, and try again.

The heap will only be coalesced if there is at least enough free memory in it to make it worthwhile (ie four times the size of the requested block, and at least one sixth of the total heap size) or if the request for more heap was denied. Coalescing causes the following:

the bins and overflow list are emptied;

the heap is scanned;

adjacent free blocks are merged;

the free blocks are scattered into the bins and overflow list in increasing address order.

Deallocation of malloc blocks

When a block is freed, if it will fit in a bin then it is put at the start of the relevant bin list, otherwise it is just marked as being free and effectively taken out of the heap until the next coalesce phase, when it will be put in the overflow list. This is done because the overflow list is in ascending block address order, and it would have to be scanned to be able to insert the freed block at the correct position. Fragmentation is also reduced if the block is not reusable until after the next coalesce phase. It is worth noting that deallocating a block and then reallocating a block of the same size can not be relied upon to deliver the original block.

Reallocation of malloc blocks

You should be cautious when using realloc. Reallocating a block to a larger size will usually require another block of memory to be used and the data to be copied into it. This means that you cannot use the whole of the heap as both blocks need to be present at the same time.

If consecutive calls keep increasing the block size until all memory is used up, then only about a third of the heap is likely to be available in one block. A typical course of events is:

The first block is present (block A).

It is extended to a larger sized block (block B). Block A must still be present (see above).

It is again extended to a larger sized block (block C). Block B must still be present (see above). However, block A also still exists because it is too small to use, and cannot be coalesced with another block because block B is in the way.

Wimp slots and the C flex system

A typical C application running under the Wimp has a single contiguous application area (wimp slot) into which are placed the following:

program image

stack

static data

malloc data.

The initial wimp slot size is set by the size of the Next slot (in the Task display window) when the application is started, or by *WimpSlot commands in the !Run file associated with the C application. If the malloc heap is full and the operating system has free memory, the wimp slot grows, raising its highest address. Once enlarged by malloc, the wimp slot never reduces again until program termination.

The application area is used as follows:

low memory:

the application image

the static data

high memory:

the malloc heap

The stack is allocated on the heap, in 4K (or as big as needed) chunks: the ARM procedure call standard means that disjoint extension of the stack is possible. The only other use that the ANSI C library makes of the malloc heap is in allocating file buffers, but even this usage can be prevented by making the appropriate calls to the ANSI C library buffer handling facilities (setvbuf). The operation of the malloc heap is described above and is designed to provide good performance under heavy use. Its design is such that small blocks can be allocated and freed rapidly.

Any malloc heap tends to fragment over time. This is particularly serious in the following circumstances:

no virtual memory

multitasking - if memory is not in use, it should be handed to other applications

if a program runs out of memory it must not crash, but must recover and continue.

These are just the conditions under which a desktop application operates!

Because of this, the flex facilities are available as part of RISC_OSLib (the RISC OS-specific C library provided with Desktop C). These provide a shifting heap, intended for the allocation of large blocks of memory which might otherwise destroy the structure of a malloc-style heap.

Flex works by increasing the size of the application area, using space above that reserved for use by malloc. When the malloc heap grows, flex areas are shifted. The benefits of using flex can be seen in Draw, Paint and Edit, which are all written in C using early versions of RISC_OSLib. Their application areas expand when new files are added, contract when files are discarded, and do not suffer from needless incremental application area growth over time.

The implementation of flex is quite simple. There is no free list as memory is shifted whenever a block is destroyed or changed in size. New blocks are always allocated at the top. When blocks are deallocated or resized, those above are moved. This means that deallocating or changing the size of a block can take quite a long time (proportional to the sum of the sizes of the blocks above it in memory). Flex is also not recommended for allocation of small blocks. Its other limitation is that as flex blocks can be shifted, you should not use them for address-dependent data (eg pointers or indirected icon data).

In addition to the facilities described above, RISC_OSLib also provides an obsolete malloc-like allocator of non-shifting blocks called heap.

Two facilities are provided, because no one storage manager can solve all problems in the absence of Virtual Memory. A program which works adequately with malloc should feel no compulsion to use anything else. The use of flex, however, particularly in desktop applications such as editors (which are likely to be resident on the desktop for a long period of time) can go a long way towards improving their memory usage.

Using memory from relocatable modules

Relocatable modules should use memory from three sources: the supervisor stack; the RMA; and application workspace. Use of pc-relative written data should be avoided as it makes a module unsuitable to ROM, unsuitable for multiple instantiation, and permanently reserves space, possibly only for occasional use.

The supervisor stack is small and not extendable, so care must be taken to use this resource very economically.

The RMA is the standard source of workspace for any of the non-user mode routines contained in a module. Care must be taken to deallocate unwanted blocks - the marker word hint described earlier in this chapter may be useful. C malloc uses RMA when called from non-user mode.

Application workspace only belongs to a module when referenced from module user mode code running as the sole current application (with RISC OS desktop multitasking halted) or when running as a RISC OS application having dealt with the Service_Memory (&11) service call (sent round by the wimp when your program issues SWI Wimp_Initialise) to keep application workspace.

Never access your application's workspace from an interrupt routine. During interrupts, the state of the application area is effectively random. Since your interrupt routine could execute at any time, it could happen while some other application is switched in. If this did happen, and the interrupt routine updated application space, then some other application could be affected. To get around this problem, allocate some RMA space for your interrupt routine to use when it needs to; this memory will be visible when your application is running. Remember to free up the RMA space when you've finished with it.

Using memory from relocatable modules written in C

There are additional points you should note if you are writing modules in C (although most of the points made above apply equally well - particularly the preceding paragraph).

All memory allocated by malloc comes from the RMA when your program is executing in non-user mode. So remember to free it up when you've finished with it. If your module allocates any RMA blocks by calling SWI XOS_Module directly, the C run-time system does not clear them out when your module finalises, so make sure you do!

There are two sets of atexit() routines, the ones which you registered during initialisation ie before your module was entered via the main() entry point (because the module was RMRun for instance), and the ones you registered after. The ones registered before will be executed when your module is finalised - this is how to clear up after yourself; the ones after will be called when your module exits from being run, ie when main() terminates.

When you are writing a C module, use exit(), not SWI XOS_Exit.

When executing as C module SVC mode code (during initialisation, finalisation, service or interrupt entry) your stack will be small. Also, your stack, unlike when in USR mode (ie running as an application) will not extend dynamically. It is therefore very important to be extremely economical with stack space; eg avoiding large auto arrays, using malloc where larger spaces are required, and freeing claimed memory at the routine end.

Static variables (and arrays etc.) in a C module are extant for the lifetime of the module, ie the entire time it is loaded. If they are only needed when it is running as an application, then they should be claimed using malloc instead.

Heap Manager

The heap is controlled by a single SWI, OS_Heap OS_Heap 0 onwards). This has a reason code and can perform the following operations:

Reason code

Meaning

0

Initialise heap

1

Describe heap

2

Allocate a block from a heap

3

Free a block

4

Change the size of a block

5

Change the size of a heap

6

Read the size of a block

Internal format of the heap

A description of the structure used by the heap manager is given below. It should be noted that this structure is not guaranteed to be preserved between releases of the software and should not be relied upon. It is given purely for advanced programmers who may want to interpret the current state of the heap when testing and debugging their own code.

The heap descriptor is a block of four words:

Format of heap descriptor

The 'special' heap word contains a pattern which distinguishes correct heap descriptors. The pattern is made up of the characters 'Heap' - which is &70616548 in hex.

All other words are offsets into the heap. This means that the heap is relocatable unless you place non-relocatable information in it.

The free list offset is an offset to the first free block in the heap, or zero if there are no free blocks. If the word is non-zero, the first free block is at address:

heap start + free list offset + 4

The other entries are offsets from the start of the heap which refer to boundaries within the heap structure. The heap is delimited as follows:

How the heap is delimited

Blocks in the free list have information in the first two words as follows:

Word 0 is the link to the next free block or 0 if at the end

Word 1 is the size of this block (including these two words)

Allocated blocks start with a word which holds the size of the allocated block. The pointer returned by SWI OS_Heap when a block is allocated actually points to the second word which is the start of the memory available.

Allocation forces the block size to be a multiple of eight, to ensure that no matter what you do, the fragments can always be freed. Therefore, the minimum size of area that can be initialised is 24 bytes (16 for the fixed information and 8 for a block).

Logical memory map

The organisation of the logical address space is currently as follows:

Typical logical memory map

You must not assume that any of the above addresses will remain fixed (save for the base of application workspace). There are defined calls to read any addresses you need, and you must use them.

Setting up the memory map

The memory map is set up on hard reset as follows:

The permanent 32K allocations for system workspace at addresses &0000000 and &1F00000 (31Mbytes) are made, as well as some other fixed allocations (such as an initial part of the system heap).

Then space is allocated to the various adjustable size regions, such as the screen, the system heap, the RMA, etc. Some of these have an absolute configured size, such as the screen. This is allocated in full. For other regions (such as the system heap and RMA), the configured size is the amount of free space that will be left; these only have a minimal allocation made at this stage.

The rest of memory is then allocated to the application workspace, from address &8000 up.

System ROM and expansion card modules are then initialised.

Finally, the regions that have a configured free space get allocated. First they are shrunk as far as possible (to ensure as close to 0 bytes free as possible), then a block of the configured size is requested and freed, so that the heaps contain as close to the configured free space as possible.

Example memory allocation

Here is an example of how memory might be allocated given some typical RAM size allocations on an A310 (8K page size):

Area

Pages

Page size

Total

FontSize

20

4K

80K

RamFsSize

0

8K

0

RMASize

16

8K

128K

ScreenSize

20

8K

160K

SpriteSize

10

8K

80K

SystemSize

4

8K

32K+32K

System workspace

32K

Cursor etc. workspace

32K

Total

576K

Application area

1024K - 576K = 448K

Note that although the FontSize is configured in units of 4K, it is always allocated in multiples of the MEMC page size. A configured screen size of 0 means 'default for this machine', which is 160K on an A310 (see *Configure ScreenSize).

As outlined above, the size of the system area (at 28M) is shrunk as far as possible after all module initialisation and then 'n' extra pages are added. 8K of this is used for the system stack. The rest is for OS variable storage (eg alias variables) and module information. The configured amount is added to the 32K initially allocated.

Altering the memory map

While no application is running (ie in the supervisor prompt), the memory map can be altered as required. For example, if you load a module from disc and the RMA isn't big enough to hold it, the size of the RMA will be increased by an appropriate amount. The OS can only do this when there is no application active, as the extra memory has to be taken from the application workspace. Most programs don't react too kindly to large areas of their memory allocation disappearing.

Under an environment such as the desktop, multiple applications are run concurrently. The currently running application is mapped into memory at &8000. Desktop applications periodically return control to the Window Manager (or Wimp) by calling the SWI Wimp_Poll; at this point the Wimp may decide to swap to another application. In doing so, it maps out the current application, and maps the new application into that space. Thus every application is given the illusion that it is the only one in the system.

Page size

The SWI OS_ReadMemMapInfo returns the page size used in the system and the number of pages present. For more details of page sizes, see the chapter entitled Page size.

Controlling memory allocation

OS_ChangeDynamicArea allows control of the space allocated to the system heap, RMA, screen, sprite area, font cache and RAM filing system. Any space left over is the application space by default. Any of these settings can be read with OS_ReadDynamicArea. OS_ReadRAMFsLimits will read the range of bytes used by the RAM filing system. The size of it can be set in CMOS RAM using *Configure RamFsSize. See also *Configure RMASize and *Configure SystemSize.

Memory protection

You have read/write access to much of the logically mapped RAM. There are exceptions, such as the 32K system workspace at &1F00000 (31M), the RAM disc, and the font cache. More areas may become protected in future releases of RISC OS. The only areas normal applications should directly access are the application workspace and the RMA. Specialist programs may access other areas of memory; for example a set of extension graphics primitives may write directly to the screen (of course reading the screen's base address using a defined call: in this case OS_ReadVduVariables). In general, though, it is very dangerous to write to these other areas, or rely on certain locations containing given information, as these are subject to change. You should always use OS routines to access operating system workspace.

OS_ValidateAddress will check a range of logical addresses to see if they are mapped into physical memory.

Changing the logical map

The mapping that MEMC maintains from logical to physical address space can be read with OS_ReadMemMapEntries. This gives a list of physical addresses for a matching set of logical page numbers.

The reverse operation, OS_SetMemMapEntries will write the mapping inside MEMC. Note that this is an extremely dangerous operation if you are not sure what you are doing.

OS_UpdateMEMC is a lower level operation that alters the bits in the MEMC control register.

Screen memory

The screen workspace is at the end of logical memory, adjacent to the physical RAM which is mapped onto those addresses. This means that there are two adjacent copies of the screen memory.

Writing to the screen

The display is normally set up by RISC OS's VDU drivers, which write to the logical memory.

You can read various VDU and mode variables to find the addresses used for this. In particular, the ScreenStart VDU variable give the logical address of the base of screen memory, the ScreenSize mode variable gives the amount of memory used by the current mode (and hence the logical address of the top of screen memory), and the TotalScreenSize VDU variable gives the amount of memory allocated to the display.

The screen-size is configurable in units of one page. Hence for a 20K screen on a 400 series machine, 32K will have to be used since it is the next highest multiple of 32K. For an 80K screen, 96K would be used, etc. In addition, if you want to use multiple banks of screen memory (eg for animation), enough memory must be reserved for each bank.

Because the total screen memory is often much more than is required at a given time, the SWI OS_ClaimScreenMemory is provided so you can claim the 'extra' RAM for short periods. It can be used as a buffer, in a data transfer operation, for example.

Displaying the screen

The display is output by MEMC using DMA to access the area of physical memory corresponding to the logical area used by the VDU drivers, and passing this area's contents to VIDC for conversion to a video signal. The area is treated as a circular buffer.

Video DMA is controlled by the physical addresses in various MEMC registers. At the start of a frame the Vptr register (ie the video DMA pointer) is set to the address in the Vinit register - which normally corresponds to the logical address in the ScreenStart VDU variable. Each read Vptr is incremented, unless it has reached the end of the buffer (as delimited by the Vend register), in which case Vptr is reset to the start of the buffer (given by the Vstart register).

Summary and hardware scrolling

This section gives two diagrams to illustrate the above; they also show how hardware scrolling is implemented.

For an unscrolled screen, access to screen memory takes place as follows:

Screen memory

Vertical hardware scrolling is implemented by altering MEMC's Vinit register. At the same time the ScreenStart VDU variable must be altered so that the VDU drivers write to the corresponding location in logical memory. This means that with larger amounts of scrolling, a part of the logical area to which the VDU drivers are writing is in fact an area of physical memory:

Screen memory after hardware scrolling

Normally hardware scrolling is performed automatically, and you don't have to concern yourself with it. However, if you need to implement it yourself - for a game, for instance - you should be in a privileged processor mode, so you can both alter MEMC's Vinit register and write to physical memory.

Non-volatile memory(CMOS RAM)

240 bytes of non-volatile memory are provided. The majority of these bytes are reserved for, or used by Acorn. Some bytes are reserved for each expansion card; before using these, see the chapter entitled CMOS RAM. There are also bytes reserved for the user; you must not use these in any distributed product. Finally, there are bytes reserved for applications; for an allocation, contact Acorn in writing, but see first the CMOS RAM bytes.

CMOS usage is subject to change in different versions of RISC OS, and your application should not assume the location of any particular information.

RISC OS 3

The full usage of CMOS RAM in RISC OS 3 is given below. Locations marked '†' are unused in RISC OS 2, and are therefore reserved for Acorn use. Locations marked '‡' have differing usage in RISC OS 2, and you should see RISC OS 2 for details:

RISC OS 2

Configured screen mode, held in 5 bits, with the fifth bit in bit 1 of byte 133

133

Sync, monitor type, some mode information

Bit 0

0 VERTICAL SYNC_ 1 composite sync

Bit 1

top bit of configured mode (rest held in byte 10)

Bits 2 - 3

monitor type

Bits 4 - 7

reserved for Acorn use

140

PrinterDP state:

Bit 0

print line feeds:0 DO_ 1 don't

Bits 1 - 2

strip control:0 MONOCHROME_ 1 GREY SCALE_ 2 colour, 3 reserved

Bit 3

feed:0 AUTO FEED_ 1 manual feed

Bit 4

print quality:0 DRAFT_ 1 NLQ

Bits 5 - 6

halftone type:0 SMALL_ 1 LARGE_ 2 dithered, 3 reserved

Bit 7

reserved for Acorn use

196

Wimp mode (actual mode EOR &0C)

Service Calls

Service_MemoryMoved(Service Call &4E)

OS_ChangeDynamicArea has just finished

On entry

R1 = &4E (reason code)

On exit

R1 preserved to pass on (do not claim)

Use

This call is made whenever OS_ChangeDynamicArea has just finished. It is used by the Wimp to tidy up and should never be claimed.

Service_ValidateAddress(Service Call &6D)

OS_ValidateAddress has been called with an unrecognised area

On entry

R1 = &6D (reason code)
R2 = start of area (value passed in R0 on entry to OS_ValidateAddress)
R3 = end of area + 1 (value passed in R1 on entry to OS_ValidateAddress)

On exit

R1 = 0 to indicate area is valid; else preserved to pass on

Use

This call is intended for internal use only. Application modules should not need to claim or issue this service.

SWI Calls

OS_Byte 161(SWI &06)

Read battery-backed CMOS RAM

On entry

R0 = 161
R1 = RAM location

On exit

R0, R1 preserved
R2 = contents of location

Interrupts

Interrupt status is undefined
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

Not defined

Use

This call provides read access to any of the locations in the battery-backed CMOS RAM. For example, this call may be used by a module to read a default configuration parameter. Moreover, this parameter could be examined by the user using the *Status command, if the module provides a suitable entry in its command decoding table. See the chapter entitled Help and command keyword table for more details.

Related SWIs

Related vectors

OS_Byte 162(SWI &06)

On entry

On exit

R0, R1 preserved
R2 corrupted

Interrupts

Interrupt status is undefined
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

Not defined

Use

This call writes to any of the locations in the battery backed RAM with the exception of location zero, which is protected. In doing so the CMOS RAM checksum is maintained but not recalculated; ie it will remain in the same state of correctness as before this call. To keep the checksum correct, you should always use this call to write to the CMOS RAM, and never write to the checksum location. This call can take a comparatively long time to return (eg 20ms).

Related vectors

OS_UpdateMEMC(SWI &1A)

On entry

On exit

Interrupts

Interrupts are disabled
Fast interrupts are disabled

Processor Mode

Processor is in SVC mode

Re-entrancy

SWI cannot be re-entered because interrupts are disabled

Use

The memory controller (MEMC) chip is a write-only device. The operating system maintains a software copy of the current state of the control register and OS_UpdateMEMC updates MEMC from the software state. To allow the programming of individual bits the call takes a field and a mask. The new MEMC value is:

newMemC

= (oldMEMC AND NOT R1) OR (R0 AND R1)

R0

= oldMEMC

So to read the contents without altering them, R0 and R1 should both be zero. To set them to n, R0 = n and R1=&FFFFFFFF.

Related SWIs

Related vectors

OS_Heap 0(SWI &1D)

On entry

On exit

R0, R1, R3 preserved

Use

This call checks the given heap pointer, and then writes a valid descriptor into the heap it points at. The heap is then ready for use. The value given for R1 must be word-aligned and less than 32Mbytes (ie must point to an area of logical RAM). R3 must be a multiple of four and less than 16Mbytes.

OS_Heap 1(SWI &1D)

Describe heap

On entry

R0 = 1 (reason code)
R1 = pointer to heap

On exit

R0, R1 preserved
R2 = largest available block size
R3 = total free

Use

This call returns information on the space available in the heap. An error is returned if the heap is invalid. This may be for any of the following reasons:

OS_Heap 2(SWI &1D)

On entry

On exit

Use

This allocates a block from the heap. An error is returned if the allocation failed for any of the following reasons:

there is not a large enough block left in the heap

the heap has been corrupted

R1 does not point to a heap

OS_Heap 3(SWI &1D)

Free heap block

On entry

R0 = 3 (reason code)
R1 = pointer to heap
R2 = pointer to block

On exit

R0 - R2 preserved

Use

This checks that the pointer given refers to an allocated block in the heap, and deallocates it. Deallocation tries to join free blocks together if at all possible, but if the block being freed is not adjacent to any other free block it is just added to the list of free blocks. An error is returned if the deallocation failed which may be because:

On exit

Use

This attempts to enlarge or shrink the given block in its current position if possible, or, if this is not possible, by reallocating and copying it. Note that if the block has to be moved, it is your responsibility to note this (by the fact that R2 has been altered), and to perform any necessary relocation of data within the block.

OS_Heap 5(SWI &1D)

Extend or shrink heap

On entry

On exit

Use

This updates the heap size information to take account of the new size. If the heap cannot shrink as far as requested - because of data that has already been allocated - it will shrink as far as possible, set R3 to the amount by which it shrank, and return an error.

OS_Heap 6(SWI &1D)

Read block size

On entry

R0 = 6 (reason code)
R1 = pointer to heap
R2 = pointer to block

On exit

R0 - R2 preserved
R3 = current block size

Use

This reads the size of a block in the specified heap. This includes any overheads associated with the block, and so will be larger, for example, than the required size of a block newly created with OS_Heap 2. An error is returned if the heap or the block could not be found.

OS_ChangeDynamicArea(SWI &2A)

Alter the space allocation of a dynamic area

On entry

R0 = area to alter
R1 = amount to move in bytes (signed integer)

On exit

R0 = preserved
R1 = number of bytes moved (unsigned integer)

Interrupts

Interrupts are disabled in critical periods, but otherwise in the caller's state
(Under RISC OS 2 interrupts are disabled throughout the call)
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

SWI is re-entrant

Use

OS_ChangeDynamicArea allows the space allocated to an area to be altered in size by removing or adding workspace from the application workspace.

The area to be altered depends on R0 as follows:

Value of R0

Area to alter

0

system heap

1

RMA

2

screen area

3

sprite area

4

font cache

5

RAM filing system

The amount to move is given by the sign and magnitude of R1:

+ve

means enlarge the selected area by at least the given amount

-ve

means shrink the selected area by no more than the given amount

If the amount to be moved is not an exact number of pages, it is rounded up (ie in the +ve direction) to the next number of pages.

Note that normally, this cannot be used while the application work area is being used; for example when BASIC is active outside the RISC OS desktop. An attempt to do so will result in a 'Memory in use' error. (In fact, when this call is made, the OS passes a service call round to modules, which can veto the change if they can't handle it correctly. See Service_Memory (Service Call &11) and Service_MemoryMoved (Service Call &4E) for more details.

Any area size change will fail if the new size is smaller than the current requirements, but will shrink the area as far as it can. If you need to release as much space as possible from an area, try to reduce its size by 16 Mbytes.

Expanding, on the other hand, does nothing if it can't move enough. In this case, if you asked for the extra space you probably need it all; RISC OS assumes that half the job is no use to you.

This SWI also does an UpCall, to enable programs running in application workspace to allow movement of memory. If the UpCall is claimed when the application is running in application workspace, the memory movement is allowed to proceed. For full details see OS_UpCall 257.

An error is returned if not all the bytes were moved, or if application workspace is being used - ie an application is active.

OS_ValidateAddress(SWI &3A)

On entry

On exit

Interrupts

Interrupt status is not altered
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

SWI is re-entrant

Use

This SWI checks the address range between R0 and (R1 - 1) inclusive to see if they are valid. If they are equal, then that single address is checked. Valid addresses are those in logical RAM (0 - 32M) which have memory mapped to them, and also the second mapping of screen RAM at the start of physical memory (32M). From RISC OS 3 onwards, if the area is not recognised as valid Service_ValidateAddress is issued, which if claimed indicates the area is valid, and results in the C flag being cleared on exit from the SWI. See Service_ValidateAddress (Service Call &6D).

On exit

Interrupts

Interrupt status is undefined
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

SWI is not re-entrant

Use

There are several restrictions to the use of screen memory. It can only be claimed by one 'client' at a time, who gets all of it. It can only be claimed if no bank other than bank 1 has been used. You can't claim it, for example, if the shadow bank has been used.

While you have claimed the screen memory, you must not perform any action which might causes the screen to scroll. This means avoiding the use of routines which might cause screen output.

It is important to release the memory after it has been used.

This call is mainly intended for internal use; you should not need to use it.

Related SWIs

None

Related vectors

None

OS_ReadRAMFsLimits(SWI &4A)

Get the current limits of the RAM filing system

On entry

--

On exit

R0 = start address
R1 = end address + 1 byte

Interrupts

Interrupt status is not altered
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

SWI is re-entrant

Use

This reads the start and end addresses of the RAM filing system. This information can also be read from OS_ReadDynamicArea.

If the RamFS is configured to zero size then R0 and R1 have the same value on exit.

The size of the RamFS after a hard reset (ie the difference between the two return values) can be configured using *Configure RamFsSize.

Interrupts

Processor Mode

Re-entrancy

Use

The request list is a series of entries three words long, terminated by a -1 in the first word. The three words are used for:

Word

Meaning

1

page number (from 0 upwards)

2

logical address that it is mapped to

3

protection level:

0

readable and writable by everybody

1

read-only in user mode

2 or 3

inaccessible in user mode

All other values are reserved and must not be used.

All fields must be set on entry.

Any address above 32Mbyte (&2000000) makes that page inaccessible. This also sets the protection level to minimum accessibility. For future compatibility, you should use an address of -1 (&FFFFFFFF) for this.

This SWI assumes you know what you are doing. It will set any page to any address, with no checks at all.

If you are using this call, then you can only use OS_ChangeDynamicArea if the kernel's limits are maintained, and all appropriate areas contain continuous memory.

Related SWIs

Related vectors

OS_FindMemMapEntries(SWI &60)

Read by logical address the logical to physical memory mapping used by MEMC

On entry

R0 = pointer to request list

On exit

R0 preserved

Interrupts

Interrupt status is not altered
Fast interrupts are enabled

Processor Mode

Processor is in SVC mode

Re-entrancy

SWI is re-entrant

Use

This call reads the logical to physical memory mapping used by MEMC. For a given logical address, it finds the corresponding page number and protection level.

The request list is a series of entries three words long, terminated by a -1 in the first word. The three words are used for:

Word

Meaning

1

page number (from 0 upwards)

2

logical address that it is mapped to

3

protection level:

0

readable and writable by everybody

1

read-only in user mode

2 or 3

inaccessible in user mode

All other values are reserved.

On entry, the logical address fields must be set. You may supply probable page numbers, which (if correct) will make this call return more quickly than it might otherwise. If you have no idea what the page number might be, you should set the page number to zero on entry. The protection level is ignored on entry.

If the page number is -1 on exit, then the memory map entry was not found; in this case, the protection level will always be 3. Otherwise the request list has been updated with the page number and protection level for the given logical address.

Related SWIs

Related vectors

None

*Commands

*Configure

Sets the value of a configuration option in the CMOS RAM

Syntax

*Configure [option [value]]

Parameters

option

the name of a configuration option

value

its new value(s)

Use

*Configure sets the value of a configuration option in the CMOS RAM. These are used to permanently store the configuration (or set-up) of the computer. The time they take effect differs; some take effect immediately, whereas some are only made current on initial power-on or after a hard break (Ctrl-Reset).

If no parameters are specified, the available configuration options are listed.

If parameters are specified, the given value is stored in the location in CMOS RAM appropriate for the given option. Some options require more than one value, and some require none at all.

Where a number is required, you may give it in decimal, as a hex number preceded by &, or a number of the form base_num, where base is the base of the number in decimal in the range 2 to 36. For example 2_1010 is another way of saying 10.

Here is a list of the available configuration options, the details of which can be found on the appropriate pages:

Example

Related commands

Related SWIs

Related vectors

*Status

Syntax

Parameters

option

the name of a configuration option

Use

*Status displays the value of a configuration option in the CMOS RAM. If no option is specified, the values of all configuration options are shown.

Because the values of these configuration options are held in non-volatile memory (the battery-backed CMOS RAM) they are preserved even when the computer is switched off, until reset by using either the Configure application from the desktop or the *Configure command from the command line.