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.
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.
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.
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:
SYS "THHeap_Extend",HeapHandle%,ExtraBufferLength_byte%, USAPtr% TO ,,USAPtr% |
!USAPtr%+ = ExtraBufferLength_byte% |
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.
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 |