Sprite Plotting - part 3

First, I just want to bring you the answer about the four extra entries in the palette data in 16 colour-modes that puzzled me in Part 2 (13.11 p75). This one comes from an Archive reader, Tony Still - they bear information about the screen border colour and the three mouse pointer colours for the desktop mode. That makes four, thanks Tony!

In this article, I will prepare the ground (maybe I should say the background!) for plotting sprites onto sprites located in a user sprite area. But first I need to dwell on the features of user sprites area, use of which will be necessary to achieve our ultimate challenging target - plotting sprites on sprites located in the user sprite areas. I think I should have started the whole series with this topic, but it's never too late.

Why use user sprite areas?

Sprite data must be located in a properly structured memory area if you want to perform some sprite operations with "OS_SpriteOp" commands. RISC OS makes use of its own memory buffer called the system sprite area. You know that, eventually, plotting sprites will be done in the video memory area. In the first two articles, I explained how to proceed, using a specific user sprite area that I will call hereafter USA. Basically, it allowed you to store sprite data and to perform some basic operations on sprites by means of specific "OS_SpriteOp" commands. As a matter of fact, you can handle as many USA's (e.g. USA_1, USA_2, ... USA_i) as you want for sprite operations. For example, USA_i can be used for window sprites if you use: WimpBlock!64 = USAPtr_i% (pointer on USA_i) when creating the window.

Within a window, you can even have sprite-only Icon_i referring to Sprite_j in USA_k if you do the following:

SpriteIconData_i%!20 = SpritePtr_j%

(or SpriteName_jPtr% = pointer to Sprite_j name depending on SpriteData_i%!28 value hereafter),

SpriteIconData_i%!24 = USAPtr_k%,
SpriteIconData_i%!28 = 0 then SpriteIconData_i%!20 contains a pointer to

Sprite_j, or Sprite_jLength_char% (length of name in character) if SpriteIconData_i%!20 contains a pointer to the Sprite_j name.

These comments are slightly beyond this article's range of interest because the OS itself normally takes care of plotting window sprite icons, but I think they are worth mentioning.

Of course, you can directly handle sprites through a series of OS_SpriteOp commands to achieve some specific sprite operations. This is now entirely within my scope and I will focus on this from here on.

OS_SpriteOp commands

The OS provides a collection of OS_SpriteOp commands to operate on sprites located in USAs. The number placed in R0 sets the sprite action.

If you add &100 to this number, R1 and R2 designate respectively USAPtr% and the pointer to the SpriteNamePtr% (pointer to sprite name).

If you add &200 to this number, R1 and R2 designate respectively USAPtr% and SpritePtr%.

Wherever possible, I recommend the latter option as it's more efficient in terms of speed, but unfortunately not available all the time as some sprite operations may invalidate sprite pointers.

I personally regret that the modern handle option wasn't retained by Acorn as this could efficiently have replaced absolute pointers and be fully transparent for users as entirely managed by the OS, whatever sprite operation was executed.

USA free memory space allocation

To be able to use a USA, the user must first allocate the proper memory space and then initialise this area prior to using it. The latter is achieved by command "OS_SpriteOp 9". Unfortunately, for some obscure reason, the OS doesn't give any help with memory allocation. There are basically two cases:

  1. The USA has no free space.
    This is what happens when you entirely fill the USA by loading a sprite file. In this case, you just have to read the number of bytes of the sprite file prior to loading it, then add four bytes to get the memory space to be allocated. You then initialise the USA with command "OS_SpriteOp 9" after setting two words in the USA header:
    • !USAPtr% = total number of bytes occupied by the USA,
    • USAPtr%!8 = 16 (offset to the first sprite assuming the Extension Area is null).
    Then the USA buffer is ready to be filled by copying the sprite file into it with command "OS_SpriteOp 10".
    From then on, you can safely use all "OS_SpriteOp" commands that operate on sprites contained in the USA and that don't require extra free space.
  2. The USA requires some free memory space.
    Let's consider the following simple problem - after doing what is described above, you want to create a new sprite in the USA by:
    • grabbing a piece of screen display with command "OS_SpriteOp 16", or:
    • simply initialising a blank sprite with command "OS_SpriteOp 15".
    In both cases, the system won't help you to determine the number of extra bytes necessary to accommodate the new sprite data. This is very strange as the Paint application, for example, keeps doing the job and probably has specific routines built in. However, these commands will be executed after the OS has checked whether the USA size as specified in its first word is globally compatible with the required space for the operation. When it isn't possible, the message "Not enough memory to create sprite" is displayed.
    If you want to modify your USA memory allocation to enable this operation, you can use Tony Houghton's very good module "THHeap" to resize your USA buffer by doing:
    SYS "THHeap_Extend",HeapHandle%,ExtraBufferLength_byte%, USAPtr% TO ,,USAPtr%
    

    Should the block be moved for some reason to another memory location a new USAPtr% pointer is returned. Accordingly, you need to update !USAPtr% by doing:
    !USAPtr%+ = ExtraBufferLength_byte%
    

What is the USA structure?

To do the proper memory space allocation, we need to use information describing the USA structure. It can be found in the PRM pages 1-749 and 1-750. Basically:

USALength_byte% = USAControlBlockLength_byte% + ExtensionAreaHeaderLength_byte% +
                  SpriteDataLength_byte% + FreeSpaceLength_byte%

All USAControlBlockLength_byte%, ExtensionAreaHeaderLength_byte% and SpriteDataLength_byte% parameters are supposed to be known, the latter one representing sprite data for all sprites already present in the USA, for example deriving from sprite's file load.

Typically:

As for the FreeSpaceLength_byte% parameter, it's up to the user to determine its value depending on how many extra sprites are to be added. Each sprite occupies a number of bytes called SpriteDataLength_byte%.

Simply,

SpriteDataLength_byte% = SpriteControlBlockLength_byte% + SpritePaletteDataLength_byte% (optional)
                         + SpriteImageDataLength_byte% + SpriteMaskDataLength_byte% (optional)

where: SpriteControlBlockLength_byte% = 44 bytes.

Number of bytes in a sprite?

We're back to a basic problem - how can you determine the number of bytes occupied by a particular sprite?

When you grab a piece of screen, you have to make use of the following information:

When you create a blank sprite or copy a sprite within the same USA (with another name) you have to have access to the following information:

The first case becomes equivalent to the second one when OS units are converted to pixels with the following instructions:

SYS "OS_ReadModeVariable",-1,4 TO ,,XEigFactor%
SpriteWidth_pixel% = SpriteWidth_OSunit% >> XEigFactor%
SYS "OS_ReadModeVariable",-1,5 TO ,,YEigFactor%
SpriteHeight_pixel% = SpriteHeight_OSunit% >> YEigFactor%

XEigFactor% and YEigFactor% parameters are usually equal to 1 or 2, but you can force 0 and 3 values on screen modes by using the "Mode" menu item of the "Display" menu on the iconbar, and change EX and EY values accordingly. Some undesirable but accepted display effects can then be observed on screen. When XEigFactor% and YEigFactor% parameters are different from zero, it means that SpriteWidth_OSunit% and SpriteHeight_OSunit% are rounded off to the next proper value. For example, in 16-colour mode with 1280×1024 resolution as:

SpriteWidth_OSunit% and SpriteHeight_OSunit% values will always be even values. If you use odd values they will be rounded off by the OS.

Basic routine called FNDetermineSpriteSize-FromUserChoice() allows proper calculation of SpriteDataLength_byte% for old sprites or new sprites (OS 3.5+) of any mode with or without palette and mask. You need to provide the mode number (old modes or mode specifiers for new sprites), width and height, either in OS units or pixels, and specify whether palette and mask are to be included or not. You will find the routines on the Archive magazine disc.

If PixelFlag% is true, dimensions are in pixels, otherwise they are in OS units, in which case they will be properly rounded off.

If MaskFlag% is true, the sprite has a mask. For old sprites, sprite mask data has the same size as sprite image data. For new sprites, it is always 1 bpp, which it should have been from the very beginning to save memory. If you create a sprite either by grabbing a piece of screen or creating a blank sprite, it never has a mask. This is added later with command "OS_SpriteOp 29".

If PaletteFlag% is true, the sprite has a palette. For old sprites:

SpritePaletteData_byte% = NColour × 8 bytes

where NColour is the number of logical colours in the palette. For new sprites with 256 colours, there's a very strange exception to this rule:

SpritePaletteData_byte% = NColour × 2 bytes

otherwise it is the same rule as above.

Routine FNDetermineSpriteSizeFromUser-Choice() uses the following sub-routines:

Application !CreateSp uses all these routines once you have selected all necessary sprite parameters for either old sprites or new sprites to tailor the size of a USA in the following sequence:

After completion, it is worth checking the content of the file "USA_1".

Now we know how to prepare a USA for the job. Next time, I will finish talking about USAs. I will further explain how to collect information from existing sprites and describe the impact of VDU driver redirection on and off sprites located in USAs and pursue handling sprites between multiple USAs. Feel free to comment as this will be profitable for everyone.


Source: Archive Magazine 14.1
Publication: Archive Magazine
Contributor: Daniel Moyne