Gerph, November 2021
Good evening. This presentation is about the few things I've done on Pyromaniac over the last year. I hope you all find it interesting.
There's going to be a lot of talk about what goes on inside RISC OS. I suspect that most people won't have thought about what happens behind the scenes, so I'll try to explain this as well as I can.
The presentation is split into a few sections. Between some of the sections, I'm going to have a short break to answer questions on that section. This should give us a chance to talk about that section’s topics without getting too far ahead and people being completely lost.
Please feel free to use the chat to ask questions as we go. I will try to pay attention to it and answer anything that comes up as we go.
At the end of the presentation, these slides and a bunch of links to resources will be made available.
There are quite a few sections this time.
There will be three demonstrations - one demo of the VNC systems, a some examples of the documentation, and a longer system demonstration at the end.
And after my conslusion, I'll take questions for as long as people want.
Let's start by giving some background before we get into the meat of the presentation...
I'm a RISC OS architect, and in my day job I'm a software engineer. I've worked with all of RISC OS from chip initialisation through to the applications and development tools. I've written about my previous work in much detail in my RISC OS Rambles on my website.
RISC OS development is something that I enjoy, and I returned to working on RISC OS because I find it to be fun and challenging.
If you missed the presentation last year, or have just forgotten, I'll summarise what was shown...
Last year I talked about and demonstrated the RISC OS build service, and explained how it worked behind the scenes. The build service itself provides a cloud based way of building and testing RISC OS software without a RISC OS machine. It's available for free to everyone.
I talked about the system that powered it - an entirely new implementation of RISC OS from scratch in Python. RISC OS Pyromaniac provides a command line shell with some graphical capabilities, and runs on Windows, macOS and Linux.
It functioned well enough that the entire slide presentation was running on it - the presentation system that you're watching right now.
I developed the slide system using Pyromaniac and only tested it occassionally on RISC OS Classic - the term RISC OS Classic is how I refer to the original ARM implementation of RISC OS.
I introduced a web site that provides an interactive demonstration of the operating system. And for the presentation the site pyromaniac.riscos.online includes media generated to show off the system, and links to software that has been released.
At the end of the presentation I said there were a few things that I would like to look at...
I'd like to say that I've got them all done. But no.
I'll talk about the things that I have got done, which isn't too far from this. I mostly attacked this year in the same way as the previous year - by doing what seemed fun, and what grabbed me at the time.
Let's talk about some stuff!
One of the things I wanted to do was to make the shell server a little more accessible. At the moment there's one shell server and it shares its filing system with all the users. I wanted something that was a bit more flexible so that it can be used by more people as a general online RISC OS server.
It probably wouldn't be that hard, I reasoned, to make the display from the Cairo graphics system available through the browser. Or even to stream the graphics operations to the browser directly, without Cairo.
That would be really interesting and quite cool. But it's a lot of work, and I'm not that interested in it. But, I thought, I've got a Cairo surface, so surely I could present that through a VNC server rather than to the desktop?
I looked. There wasn't a simple library that bridges Cairo to VNC. So I wrote one. You'll
find it on GitHub as
A VNC server has, traditionally, been something I thought 'gosh that's hard to do'. But actually it's really not that hard. The devil is in the detail as always, but getting something that works isn't actually that hard.
Although I wanted the VNC server inside Pyromaniac, I don't believe in building things for one purpose. That's a key requirement when you're working with an operating system. You should never think 'how do I solve this', but instead think 'what is the class of problem I want to solve'. Because you aren't writing something for a specific purpose, but so that others can use it to do things that you hadn't thought of.
Plus, of course, that makes it easier to test. You can run things in isolation and get rid of the bugs without subjecting it to the mess that is your real application - in this case Pyromaniac.
So I started out by writing a server that could be attached to a Cairo surface, without any real knowledge of what the surface shows or the application that runs it. That way I can build it in to Pyromaniac, but I can make it available to other people if they want to use it.
The library was built up from the specification, just stepping through the different sections and implementing the request/response handling as I went. I had a list of requirements for things I wanted it to be able to do.
Once I'd implemented the initial negotiations and security checks, I had to handle actual pixel data. There are a lot of encodings available to the VNC servers, but the bitmap data is only handled as RGB in my server - no JPEG encoding here. It does handle arbitrary bit depths up to 8 bit per channel and has a conversion that handles different shifts.
If you want 2 bits of red, 4 bits of green and 2 bits of blue, in the order blue, green, red it should handle it.
The VNC protocol allows a number of ways of encoding the changed data - you can give rectangles of arbitrary size, or copy regions, and you can compress the data with ZLib if you want. There's a bunch of things you can do. But my server only supports the raw encoding.
It also only delivers whole rows at a time. Because that's a lot easier to handle, and I really don't need to do any more. It does optimise out any rows that are unchanged though. But that's about the only efficiency we apply here.
The animator - that's the name I give to the application that's being served - can change the size of their display. On RISC OS this would happen if you changed the mode and the resolution was different. So the server supports this behaviour - not all clients handle it but that cannot be helped.
Input from the user is buffered until the animator is ready for it - so you can interact with what's being displayed. Each client is able to be configured so that it can be read only (cannot give input) or a standard client (which can give input). The animator retrieves the events and can handle them as it sees fit. There's currently 3 events that are available - mouse click/release, key press/release and mouse movement.
Deciding which types of clients are read only or standard is purely down to which password was given. There's one for read only and one for standard. I would expect that you would use this as a way to present a single session to multiple users.
I created small examples to go with it, showing how you run your application on one thread, with the VNC server on another thread handling connections.
Assuming you have some Cairo rendering in a simple class called
Screen, this is
the sort of code you could use for a basic animation. You create your animation
object, and you run it on a thread. Then you create a
CairoVNCServer with a bunch
of configuration parameters, and you tell it to serve stuff.
In a real program you need a bit more than this, but the examples show how to do this stuff. The image shows what one of those example programs looks like in a VNC client. The bezier curve moves around and the red square moves up and down.
It's very exciting.
Ok, so having got a library, how was easy was it to integrate with Pyromaniac?
Not especially hard really. It's just another graphics implementation around Cairo. So in that respect it's just a case of taking some of the existing code, stripping out all the desktop UI specific bits, and replacing them with VNC specific bits.
The rendering needed to use a locking system to ensure that we don't try to render whilst the library is grabbing the content for a VNC client. This required an update to the library as I had forgotten that feature.
But the entire VNC graphics implementation in Pyromaniac is about 320 lines - and a third of those lines are just the key mapping from VNC to internal key numbers. You'll find the source to the implementation on the pyromaniac site.
Whilst I'd been working on the VNC server, I'd also been trying to add the ability to plot sprites. Because surely that's not so hard?
Let's look at what happens when you put a sprite on the screen in RISC OS.
Work out what the sprite is.
OS_SpriteOpto find the sprite address, although you could just give its name.
Ask for a colour translation table.
Finally you request a plot of the sprite.
PlotScaledis called, passing in the translation table so that the sprite is rendered with the correct colours.
SpriteOptakes every pixel that's being rendered, looks up the correct value in the translation table, transforms it into the right position and stores that resulting value on the screen.
Knowing what happens to draw a sprite to the screen, what parts of RISC OS do we need to work in order to do this?
There are quite a few areas which need to work in order to get a sprite on to the screen. Some are pretty simple, but others cover more of the system. It would be nice to think that sprite rendering was just about putting something on the screen, but that's just the pinacle that sits on quite a few structural foundations.
I'll talk about some of these and how they're handled inside Pyromaniac.
Graphics primitives like being able to select colours to draw with, and then drawing lines and text, may not seem like they're necessary for sprite plotting, but they're needed for a few things...
Next on the list of requirements was having shallow and deep screen modes.
The mode system had been designed with the intention of making it possible to add deep modes later - but last year, only numbered modes were supported. This mattered because the interfaces that sprites and colour translation uses allow for 'modes' to be specified in a number of different forms:
Of these all the formats need to be handled, but only the first two were supported last year. The details of this are documented in PRM 5a, but I've created new documentation to combine all the mode specification details into one place. You'll find this in the API documentation.
Internally modes are described with a class called
Mode. This class describes
all the properties that the mode has - it's width, height, depth, density
and a bunch of other parameters. Basically everything you would normally
Mode can also return its palette,
and the default palette for that mode - because each mode could have had
its palette changed.
That was fine when we only had numbered modes, but to introduce the other
ways of handling modes we need to be able to create new
to represent (say) sprites.
So we now have a new class
ModeSelector, which can handle the different
forms of mode specification and turn them into a
In some cases, the specification is incomplete so we don't know
all the information - the sprite mode word, for example, doesn't give any
information about width and height, but does contain depth and density.
All the mode handling passes through a
getmodedef function which takes
one of the mode specifiers, and makes a
Mode using the
Here you can see an example of the
getmodedef function in use. This is
a section of
ColourTrans with some complexity cut out.
Because I use a single function to get a
Mode, it is possible
to use the
Service_ModeExtension interface to provide new numbered modes.
This interface was already present last year, but only using the RISC OS 3.1
style of mode extensions. It now also supports the RISC OS 3.5 form.
Fortunately the differences between these are largely related to hardware,
and as Pyromaniac doesn't have any hardware, we can completely ignore those
parameters. It's only the mode variables we really care about, and we just
use them to create the
Because it's easy to create new modes in this way, there's now a configuration option that allows modes to be defined on the command line.
Inside Pyromaniac, we need to be able to find out where a sprite is before we can operate on it - we need to find the sprite's address. Even if the user gives the sprite's name, internally the OS still has to do this look up.
Is that hard? Not per se, but it requires that all the machinery to look up sprites be handled properly, in approximately the same way as it did in RISC OS Classic.
This means that all the sprite calls need to be able to create python objects that reference the sprites in the sprite area so that we can look them up. But only if the sprite operation being performed need a sprite.
For example, some sprite operations don't need a sprite to be passed to them. Others can be passed a value of 0 for the sprite pointer. And of course, some look up the sprite by name and some look up the sprite by address.
Every time you reference a sprite in a sprite area, the Python code will build a
SpriteArea object that
manages the area, and then builds a list of objects which represent the sprites in that area.
The sprite object for a given operation is passed to the function that will operate on it -
which in our case will be the plot operation.
So all of that machinery for decoding sprite areas and sprites had to be built, and the mechanisms for dispatching the handling of each sprite operation. That had to happen just to be able to operate on the sprite area and find details about the sprites.
The code you can see here handles reading the sprite details. Because it has been passed the object representing the sprite it can update the registers with the correct values and return.
But before that we need to get a translation table.
Getting a translation table means calling ColourTrans to process the sprite palette and the screen palette to produce the mapping of the pixel values. However the process is more general than that.
The source we're generating a translation for might not be the sprite and its palette, but could be a regular screen mode number, or the current mode. Or it could be a sprite mode word and a palette pointer. Or even a mode descriptor and a marker to use the default palette. Or a mixture of those pairs.
And the destination might be an equivalent set of those combinations.
Additionally, there's a 'transfer function' which can change the colours. This is
a pointer to code that
ColourTrans will call to change the colours as they are being
translated. This allows you to do effects like inverting the sprite, or tinting them
a particular colour. The WindowManager uses these for selections.
So let's take an example. Let's say we've got a 4 colour sprite and we want to plot it in a 16 colour mode.
Here's what we want to draw. It's a red letter 'G' which is being drawn from a 4 colour mode with the standard palette. It's going to be plotted in a 16 colour mode with the desktop palette.
So we call ColourTrans_GenerateTable. This call will create a translation table which we can use to translate the colours in the original sprite to the colours on the screen. ColourTrans reads the palette from the sprite and from the current destination.
It then steps through each of the colours in the source palette and finds the closest match in the destination palette. This is actually done with some weightings to allow for more sensitivity to certain colours.
These colours are then written into the caller's translation table. In this example we're using a 4 colour source and a 16 colour destination, but in a 256 colour mode, this can result in a lot of comparisons. Classic ColourTrans has optimisations for this, but these aren't present in Pyromaniac at present.
Here you can see that the translation table has the original colours mapped to colour 7, 11, 14, and 0.
Having obtained the translation table, we now call
OS_SpriteOp to actually render
the sprite with that table.
OS_SpriteOp is given the sprite, and the translation
table, and it has to put that sprite on the screen. The procedure is
very similar to that on RISC OS Classic, but there is only one implementation inside
Pyromaniac - whereas in RISC OS Classic there are two. One implementation, for the
simpler operations is in the Kernel, and one for the more complex operations is in
To explain how the sprite is stored, I've included a table showing the bit pattern that's stored in the sprite and the decimal values of those bytes. In reality, all the sprites are aligned to 4-byte words, but I'm trying to keep this simple.
You should be able to see that the 'G' is flipped horizontally. This is because the left most pixel's value is stored in the lowest bits of the byte, and the right most pixel is in the highest bits of the byte.
Now let's do some actual processing on this data. The first thing that the Pyromaniac code does is to build a table that converts from the byte values to the pixel values - essentially undoing what I did to explain how it was stored in the previous slide. This is actually a static table for each depth of mode.
Because there are only 4 distinct values used in our example sprite, I've only shown these 4 entries in the lookup table.
As you can see, the byte 215 in the sprite actually means colours 3, 1, 1 and 3 in a 4 colour mode. It doesn't matter what those colours are. That's why we use the translation table - ColourTrans has already done that work to map those colours to the new values.
Next we apply the translation table to the lookup table. This means replacing each of the colour numbers from our lookup with the corresponding number from the translation table.
This new table tells us what colours we should use in the destination mode for a given byte in the source sprite. That would be fine if we were writing to a RISC OS mode, but we're not - Cairo only ever uses a 24 bit per pixel output so we need to convert to its format.
We look at the current destination's palette and we apply a mapping to the translated palette, giving the actual colours that will be used on the screen. So now we have a table that maps from the byte that was stored in the sprite to the colour that will be placed on the screen.
At this point you might wonder why we can't skip the translation table entirely and just map the colours directly to those that will be put on the screen from the source palette. The reason is pretty simple - the translation table doesn't have to map colours directly.
They could be made darker, lighter, or inverted. Or the user could be doing some colour cycling - hiding some colours to make a simple animation. This means that you have to honour what the user supplied to you.
There are special options to some of the sprite plotting calls to plot directly from the palette, and this does offer some opimisation opportunities.
Up to now, we're not actually been concerned with the sprite data. All we've been manipulating is the table of lookups that will be performed on each byte of data.
Finally we can put this information together. For every byte in the source sprite data we look up what this will be on the screen in the paletted lookup table. This produces a table of colour values which can then be written to a Cairo surface and rendered on the screen.
Hopefully you can see in the colour values that the image data is of a 'G'.
There are a lot of things I've glossed over here:
All of these translation table and processing operations happen for every single sprite plot. In Pyromaniac it's all implemented in Python. You might think that that means that it's going to be slow. And yu'd be absolutely right. But it's not quite as bad as you might expect.
The graphics system in Pyromaniac has a number of coordinate spaces to deal with. Each of the graphics operations needs to convert between these coordinates spaces in order to put something on the screen. The first three spaces are common with RISC OS Classic.
These coordinate conversions aren't really restricted to sprites, as they apply to all the graphics primitives, and Draw and Font rendering.
The first space is the user space with an origin. In this example, the origin has been placed at the centre of the screen.
As I step through these spaces, the changing parameters are highlighted in bold.
The space is converted to normalised user space by adding the origin to all the coordinates.
The origin is now in the bottom left, and the coordinates are affected accordingly.
Then we convert to internal coordinates by scaling by the eigenfactors. The eigenfactors basically describe the shape of a pixel - how rectangular it is.
This means that every addressable coordinate now maps to a pixel, and the top-right is now the size of the screen in pixels.
Finally the Cairo implementation has to turn everything upside down.
Like most other graphics systems, Cairo uses an origin at the top left, with Y-coordinates increasing down the screen.
This means subtracting the coordinates from the height of the screen.
Every graphics operation has to go through these transformations to get to the position that they will appear on the Cairo surface.
The user can say that they want to plot sprites with a transformation matrix. They might do this to scale things up or down, or skew the image. Working with matrices can be pretty unfun, especially when you have to convert between different coordinate spaces.
Alternatively, the user can specify a scaling ratio. In this case they only give
the ratio as a multiplier and a divider for both
the X and Y dimensions. Inside Pyromaniac the
Matrix objects descend
from a common
Transform object, which means that applying them to coordinates
can be done through common functions.
This is the generic form of a transformation, using a matrix to calculate the new
coordinates. Only the 6 values
f are supplied to RISC OS.
This is what those variables mean.
If you need to do matrix calculationsthere's a lot of information online to help.
Now that I've got the
Matrix object working I use it to do all the calculations
for transformations because the maths is easy to get wrong.
This is an example of scaling a sprite up to twice its original size. Two of the values in the matrix are set to 2, to scale both dimensions.
And if you wanted to stretch the graphic you'd give these different values.
There's actually a lot more you can do with this, from skewing the graphic at an angle, to rotating the graphic around an origin.
The matrix operations in Pyromaniac are processed methodically, and then when we get to Cairo they have to be modified to account for the coordinate space being upside down in relation to RISC OS. It's kinda frustrating, but now that it's working it's pretty nice.
Tiling of sprites isn't used that often in RISC OS, but the one place it is used is quite prominent - the mottled sprite on window backgrounds. Traditionally this was drawn by finding the region that the sprite needed to fill, and repeatedly rendering the same sprite at offsets so that it covers the entire graphics window.
You can see that many sprite operations might be needed to fill a region here. This isn't such a big deal when you're plotting to the screen with direct screen access, but if you've got an accelerated graphics system this can matter.
In Select I introduced a new call to request that a sprite be plotted tiled into the graphics window.
This made it easier for applications wishing to tile sprites, but more importantly it made it possible for accelerated interface to perform this tiling operation efficiently. The software implementation still uses the same algorithm when there is no acceleration, but when there is acceleration available, this can run much faster.
This acceleration was provided in the ViewFinder driver to make its tiling faster than it would have been when drawn normally.
The interface is very simple - plot the sprite at a single location, and it will be automatically
repeated into the surrounding space. Only 1
SpriteOp call is made here, and the whole area
How does that related to Pyromaniac? In modern graphics system - and I'm using modern to mean 'the last 30 years or so' - it has been common for this operation to be provided by the hardware system. Instead of bounding a given sprite to its own extent, it is repeated within the region it is drawn. This is commonly just a single configuration change when plotting a sprite for whether it should repeat or not.
Cairo provides such an option, and so being able to call the Cairo rendering once and have it fill the whole region was very simple.
This example is the tail end of the sprite plotting code, where the tiling was inserted. This happens after the RISC OS sprite data has been converted to a Cairo surface, and all the matrix calculations have been performed.
The changes for the tiling are very simple:
REPEATacross the page.
As you can see the extra code needed to tile the sprite is pretty simple and easy to understand.
This is what it looks like when you tile the cog sprite. You can see some red marks towards the bottom left of the image, which show where the actual plot of the sprite was performed.
The Pyromaniac implementation was kinda fun when I first ran it because it just worked. Adding the single option to repeat the image was all it took and tiled sprites were then accelerated.
It's time for our first demonstration. This demonstration builds upon what I've discussed up to now. If you have a VNC client, you can connect to the server at address vnc.railpro.riscos.online on display 8. You'll be playing a different game to the one I'm displaying - each connection is a separate game.
I'm going to connect to the server here, and I'll show you the VNC session running.
<launch the VNC server and then share its screen in Zoom>
This is the game RailPro, by Jos Keijzers. The idea is to guide the trains to platforms and out again. That's it. No ray guns and explosions. Well, there might be explosions if you crash the trains. We try not to do that.
It takes a little while to start because it reads the map using BGET operations, which aren't very fast.
It's a mouse based game, which uses the cursor keys to move around the map. As you can see here we've got a load of tracks in grey, and a number of signals in red and green. Those are all masked sprites being rendered. The background is drawn in different colours - there was some fun getting the correct set of colours to appear.
I can scroll left and right to see the two parts of the map. <cursor right>
Let's start the game. <press S>
There's a train coming in which is meant to go to which platform it's being sent to.
As you can see, the train is moving slowly towards our region and we need to get it there. It's necessary to change the signals and the points to make the train reach its destination. Although the train is moving slowly, another one will be along soon.
Although Pyromaniac is not fast itself, this sort of game is practical because it does not require rapid redraw. The gameplay like this is almost the same as the original game when I played it on my A5000.
<adlib as necessary - there's not much to say, but a minute or two of gameplay won't hurt>
The game continues until you cause catastrophic crashes and they fire you, or... well, I'm not sure. I always get fired, although I have managed to last for over 20 minutes in one game.
The actual server is providing a separate invocation of the RISC OS Pyromaniac system inside its own docker container. This means that each session has its own workspace and is running mostly independantly. Changing the program from RailPro to something else would be pretty trivial.
I'll take any questions about the VNC system, sprites system, or the game demo we just saw, now.
<restore the shared screen to the slides, then quit the VNC session>
Ok, let's talk about the revamp that the font system has had over the past year...
I'd mentioned in the prior presentation about how the font system was painful.
The FontManager allows strings to be passed to it which are to be plotted on to the screen. Usually those strings are just plain text, and that's commonly what you'd expect. But some parts of the system - the WindowManager particularly - use control codes that are embedded in the font string to change its behaviour. For example, you can change the font mid-string, move the baseline, change the colours or even change the transformation matrix.
Little bits of this were implemented last year, but only inside the
call - and wasn't implemented at all in any of the other font SWIs.
These control codes needed to be handled properly, mostly for the WindowManager. Without them, there would be no 'shift' characters on menus and the positioning of the right aligned text would not work.
The first attempt to get the FontManager working - which was demonstrated last year as part of the presentation - was to just take the string and stop as soon as a 0 byte was encountered. For the presentation system this was fine because there wouldn't be any anything control codes in there.
To render fonts, RISC OS Pyromaniac has two major parts:
The Pyromaniac graphics system has primitives to select font faces, size text, and to draw text in a colour with a transformation. When the FontManager wants to perform operations on the fonts, it calls these primitives.
This means that what the operations do can change - and it does depending on the graphics implementation. Under Cairo, this is converted to the 'Toy' text interface. If a different graphics back end was used then only the font primitives need to change - FontManager is exactly the same.
In RISC OS terms this would be equivalent to keeping the SWI interface but switching out the Font Outlines parsing and rendering for a different system like FreeType. RISC OS Pyromaniac is intended for debugging and trying things out, so my intention was to give the system a structure that might then be applied to a RISC OS module in the future.
Anyhow, the graphics system's font primitives are pretty much sufficient to do everything that FontManager requires. But FontManager has to use them properly if the SWIs are to work as expected, and in this initial implementation the FontManager just took a string and threw it at the graphics system to be rendered.
That's fine for the presentation system, but when it comes to the WindowManager, there's a lot more going on. At that time, the WindowManager didn't work very well - but well enough that the Wimp_ReportError call could function.
That's what happens if you don't process any of the control codes before you try rendering it. The leading control characters appear as squares in the output.
You can also see that at that time the graphics system was working well enough to draw the rest of the window - the outline and 3D border are working, and the IconBorderRound module is able to draw its buttons just fine.
Aside from colour selection, those are all operations that could have run on the BBC so they're really not that special.
This wasn't working out right, so I needed to handle the control strings better. The control strings are styled to be like the VDU codes with the same numbers, which is kinda cute and makes understanding them a lot easier.
What you can't tell from this list is that few of these calls are used, even in the WindowManager.
The codes are clearly important to getting the interface correct, but just to get
things working, it wasn't necessary to process them all. Those squares preceding the
text were actually a
26, <font handle> sequence that the WindowManager inserts before
every icon render.
I replaced the bare string read with a parser that would read the bytes from the string and just throw away all the control codes entirely - except the first one. The first one was always processed because that's how the WindowManager selected a font. Not ideal, but it gets past the problem of those squares turning up.
There are other wrinkles, too. Menu items in the desktop are usually described with simple strings. In your application you include a string that has the shortcut on the end, like the example.
And you expect the
^F3 shortcut to be right aligned. This worked when the desktop used
the monospaced system font, but with proportional fonts those spaces won't give the
right alignment. What the
WindowManager does is to reduce the trailing spaces to just one space and any other
spaces to hardspaces. Why ? Because then it can use a trick to move the text. It could
9 sequence to move the text, but it doesn't - I'm not sure why, but instead
it changes the word spacing when it plots the text.
Changing the word spacing is commonly used to provide a cheap form of justification, and that's essentially what's being done here.
The WindowManager calls the
Font_ScanString to get the size of its modified string.
It then subtracts that from the space it's has available in the menu, and uses that as the word
spacing it supplied to
Because it has ensured that only a single space is present
and that this is just before the shortcut, the
^F3 becomes right aligned.
In the Pyromaniac FontManager at that time, if a word spacing was supplied, the string would be split on spaces and it would just repeat the rendering on each fragment, adding the word spacing to the baseline.
This was good enough, and menus would show their shortcuts right aligned.
The FontManager still didn't handle the inter-character spacing, or any of the control codes, but it was good enough.
There is a font used by the WindowManager which is used to render special characters.
WIMPSymbol font provides a few characters which were previously used in the system
font for window furniture - the close icon, back icon and the arrows. The Window Manager
replaces these if you use them in strings with a reference to the font.
So when you use character 139 for scroll up, it gets converted to the sequence you see.
With my code that stripped out the control characters, you'd be left with a regular character 139 in the desktop font, so that didn't look great. But even if I did process the control characters, code 139 isn't an up arrow in any font encoding - it's only present in the system font because the WindowManager reprograms the characters when it starts.
Fortunately, this is what font encodings are for. Normally when you select a font you tell it what encoding you're going to use, or use the default usually Latin 1. These regular encodings were already implemented.
However, fonts can have their own encodings that they use in preference to the encoding you supply. This is used for symbol fonts. The fonts Selwyn and Sidney use this to ensure that they always look the same regardless of your current alphabet.
So I had to add the ability of fonts to override their supplied encoding and always
use their own - and to define what that encoding is for each of those fonts. Fortunately,
defining the encoding was pretty easy because I can just add them to the encodings provided
If you look at the GitHub repository, you'll see that there specific encodings for the Selwyn, Sidney, and WIMPSymbol fonts. Those are used as font encodings so that the fonts are rendered as expected.
This is Selwyn, being rendered with its own font encoding. Internally this turns the byte codes into unicode characters with a suitable font under macOS. Most of the characters are exactly as you'd expect.
With the encoding problem solved, we still need to be able to change fonts in the middle of the line, or do any of the other control codes.
So I wrote a parser which would step through the bytes that you gave it and return a list of either control codes to process or the string to render. If you gave it a string with 'Replace shift-F5', it would give you a list like this.
As you can see it's broken down into segments that use the different fonts.
Font_Paint code would then step through the list and process each operation.
When it got a string, it would call the rendering code, and when it got a font change
it would update the font handle. And the other control codes were also processed to
move the baseline, change colours or whatever.
And that worked pretty well.
With that in place it was also possible to handle inter-character spacing as well by processing each character in the string separately.
This is what it looks like when you run my manual tests. Each of the lines tests
one or more of the features of
The program that creates this can be found in the
And here you can see the output of a slightly earlier version alongside RPCEmu rendering the same content. Pyromaniac is on the right.
There's some differences in the font metrics which mean that things are spaced slightly differently, and the font is a little bolder in Pyromaniac. But basically, it's looking like they do the same thing.
It's still not right though. And the reason it's not right is that it's still a bit
of a bodge. Not only is it not processing the codes properly, the processing is performed
Font_Paint code. Because
Font_ScanString is still using the old method of
stripping all the control codes out, it's not actually compatible. Adding the same code
Font_ScanString would be difficult, so... it's time to do it right.
The code that was parsing the strings was separated but a bit ugly, and the code that
acted on it was built into the
Font_Paint code. The code needed to be reworked to be
able to be reused in the
Font_ScanString implementation and able to be tested well.
So, I started again, from scratch - well, using the principles of what I'd done in the previous version. The new implementation has a lot more structure, and is designed to be used without needing Pyromaniac. I did this so that I could develop and test it without having to worry about the rest of the system.
The non-Pyromaniac version takes a set of bytes containing the font string to be processed. In Pyromaniac the functions that access the bytes of the string instead read from the emulated memory. This ensures that we only access bytes as we need to.
So what makes up the parser?
Despite being separated from the Pyromaniac, I'm still using the graphics structure
Bounds. These aren't really related to the memory access
so they're reusable.
FontContext object which has the state stored in it, and is how we can control
the sizing and rendering operations. It has functions which allow it to convert between
GCOL and RGB values and to perform operations on font handles. Outside of Pyromaniac
these are just simple operations that return dummy values. Inside Pyromaniac it is
subclassed so that those functions are overridden by the ones that talk to the real
FontContext also performs the processing on a list of operations to either render
or size the provided strings.
FontControlParser takes the font string and parses it into a list of
operations. It can give more information on what it has processed, but is largely used
to create a
FontControlSequence which holds all the information about the string that
was processed. In Pyromaniac, the
FontControlParser is subclassed to replace the
memory reading, allowing it to access emulated memory.
FontControlSequence is a list of objects which describe each of the operations
in the string. The
FontControlSequence can also create a new list of operations which has injected
movements for the word spacing or character spacing. This allows us to process the
basics of the string and then split up it up with the spacing as necessary.
The sequence contains objects which are based on the
Each object is an instance of a class which can perform the changes
FontContext, or the necessary rendering for that operation when methods are called.
That's a lot of description so here's what actually happens when you try to render
Replace <shift>F5 string for a menu. When the FontManager initialises, it creates
FontContext which holds the current colours, current font, and other information.
This will be updated when a
Font_Paint is performed, colours are selected or a font
Here we can see that the FontContext contains the requested parameters - we're going
to plot with the current font (which is unset), we have black on white, and we're plotting
at 640, 480. The
FontControlParser is called to generate a list of objects
FontControlSequence from the input string.
This is what you can see here - there's font selection operations and strings to be
rendered. To render this, the
Font_Paint SWI asks the
The first thing it does is to expand the sequence to include an extra movement after the space for the word spacing.
You can see the extra movement has been added, as the
FontControlMoveSpace. This is a variant of the regular
control code, which will also add an underline in between, if an underline was required.
A regular move control code would not produce an extra underline. It's this different
type of object that will avoid the problem with the underline being split up as we saw
in the earlier example.
FontContext painter steps through each of the objects and asks it to perform
paint operation. On the right hand side, I've drawn what is present as after the
operation completes. At this point, there's still nothing on the screen. However, the
context has been updated to reflect the new font that we need to use.
We draw the word
Replace, and a space following it, and the baseline position in the
context is incremented by the size of that string.
Then we move a little further - just the baseline x position is updated.
Switch to the WIMPSymbol font by updating the context.
Draw the shift character, and advance the baseline position.
Switch back to the desktop font.
Finally draw the
At this point the
FontContext returns, and
Font_Paint can return to the user. For
shear simplicity, I've omitted the bounding box from these diagrams, but after each operation
the bounding box of the covered area is updated in the context. The
will be updated if they have been configured to do track updated regions.
What does this mean for
Font_ScanString and its friends?
Well, it does exactly the same process, but instead of calling
paint, it calls
This does almost exactly the same as
paint, but doesn't render, and if a terminal condition like
stepping past the limits is reached, the scan ends. The diagram above omits it, but each
of the objects also contains a property which gives the index of the end of the operation
in the original string. This is used to return the correct offset when the
needs to say where the scan ended.
Font_ScanString also does something slightly different with the
FontContext. Instead of
operating on the current context, it copies the current context to a
operates on that instead. It is this
future_context that will be queried when the user
This was all implemented as a standalone test first, and then updated in the FontManager code. This meant that the complexities of different types of operations could be experimented with, and avoided problems caused by the rest of the system.
This is what the new parser's rendering looks like. It's looks the same as the old
parser, except that the underline is no longer broken up. Plus, it can now support the
Font_ScanString operations as well.
It is slightly different though. Because
Font_ScanString honours these control
codes and returns data in a slightly different way, some of the bounding boxes are
very slightly different. However, it's more consistent now, so that's alright.
I think that the structure I've implemented is probably similar to the Classic FontManager, but my recollection is that the assembler in the FontManager was a lot more adhoc with its use of data structures.
If you're interested, there is a repository on GitHub which contains the entire parser and tests for the font control code system.
Font_ScanString has a lot of code in it, mostly related to working out what you want it
to do. But the core of the code looks like this.
We set up the
future_context as a copy of the current context - so that we can work
on it without affecting the current context.
We apply the parser to the string that was supplied. This gives us a sequence of operations
as above. And then we do the
size operation on it - that updates all the properties in
The properties from the
future_context are then returned to the user in the registers
or blocks as required.
The font caret shows where your text will be entered. It's a pretty simple rendering, but one that the Classic FontManager drew by directly writing to the screen. In RISC OS Pyromaniac we draw the caret using the graphics primitives.
It changes its shape based on the height requested. You can see here what the caret looks like at different heights.
The documentation actually says that the caret will be drawn with loops at the top and bottom. To the best of my knowledge this has never actually been the case. But it is an option that you can enable in Pyromaniac. And this is what it looks like.
I really don't like it. Perhaps spending some more time would help, but the point of Pyromaniac is to make it easy to try things - this one isn't quite right, but it wasn't too hard to try out.
Whilst discussing the changes for the sprites, I talked a little bit about the use of mode selectors, and how the system needs to handle them wherever we have a mode supplied. This went hand in hand with adding support for deep modes - 15 bit per pixel and 24 bit per pixel modes.
Adding those modes was surprisingly easy. The palette system needed to be updated so that they didn't have to be indexed. Places where the system iterated over the whole palette were updated to be aware of the new depths. Each of the palette objects has a number of common methods which can be used to operate on the palette.
The modes that had a palette would be able to access the individual paletted entries. To find the closest colour they need to do a search. The deep modes, however, could just use simple calculations to return the closest colours.
The 32K tables take a long time to generate - they are essentially 32 * 32 * 32
calls to the
find_closest() function. Because of this, the
keeps a cache of translation tables for common palettes. This means that we do not need
to waste time building them at run time. This is very similar to how the Classic
ColourTrans module handled translation tables.
These palette operations are obviously at the heart of drawing anything onto the display and handling sprites. They could be optimised in the future if they are found to be a bottleneck, but right now the performance isn't terrible.
There was a fun bug introduced when the palettes were created. In the 15 bit
per pixel modes, the selection of the colours in the
lookup function was a
The problem was the the red component was mapped into green as well - the shift in the green assignment should be moving the value by 16 bits, not 8. It meant that the colours in that mode had no green component at all.
Because the deep modes were harder to test automatically, this wasn't caught for a month. Another reminder of why it's important to have automated tests, even when it's hard.
When modes were just numbered, it was easy to select them. Using
VDU 22 or
OS_ScreenMode 0 was all that was necessary. However, the mode selection can
also be performed by a mode string. There are 3 reason codes in
which process the mode strings into mode specifiers.
These calls allow the specification of mode as a string and are required for
BASIC's to mode selection. This was surprisingly easy to add. The
ModeSelector class had already been created which was able to hold all the
details about a mode.
The string decoding and encoding was implemented within this class as methods, and a simple interface used to write the selector to memory. It's great when the code needed to implement interfaces is really obvious. The code needed to implement the Select mode by mode string interface was very readable.
The memory size could probably do with a bit more commentary, but it's still pretty readable.
One of the reasons that the mode strings parsing was moved into the Kernel, was that it had previously been inside the WindowManager. BASIC had been modified to call into the WindowManager when a mode string had been used. This is clearly insanity - the WindowManager should not need to be present for a language interpreter to work.
However, it introduced an interesting problem. The selection of greyscale modes was a WindowManager thing. There was no way to express in a mode selector that the mode should be greyscale. The WindowManager didn't care - it knew that that was the type of mode requested and reset the palette after it changed mode.
But how was that to translate to the 'decode mode string, select mode' sequence so that when you specified a greyscale mode, the mode selected would actually have a greyscale palette? The solution was to introduce a new mode flag for greyscale which allows this information to be passed through.
That's what Select introduced, and of course the same problem exists here in RISC OS Pyromaniac, so the mode flag for greyscale modes was passed through. When the flag is seen by the mode information code, it changes the default palette for the mode to a greyscale palette.
With RISC OS 3.5, the system brought in a system of monitor specific modes which
could be enumerated by a call to
OS_ScreenMode. The list of modes that were
available in this way were generally serviced by the ScreenModes module.
This module manages the list of modes available by using a Monitor Definition File to describe what the monitor can handle. There isn't any real reason why that has to be the case, and indeed on modern systems it's unlikely that you'd have any monitor definition except to override when the monitor provided incorrect information.
RISC OS Pyromaniac doesn't have any hardware, and so very few of the restrictions that it would bring. The enumeration call is now issued by the kernel when requested, and it can be configured to respond with the details from the numbered modes. This means that any numbered modes that are defined through the Mode Extension interface will appear as options to be selected. You can still select any other resolution you like manually. But the modes that are known to the system through the numbers will appear in the enumerated list.
RISC OS Select introduced the multiple display interfaces, allowing you to switch between display drivers at will. Eventually, RISC OS Pyromaniac will support this, but right now, there's only one display.
However, the calls that read information about the display drivers are now supported.
OS_ScreenMode 12 will return details about the current graphics implementation as its
details. This means that anything which tries to read the information about the
displays will get back useful information.
Providing multiple display drivers in RISC OS Pyromaniac is probably pretty easy. There's a bunch of entangled information about the graphics system and its presentation, but that's the same as was the case on Select. The harder part will be convincing the interface implementation like WxWidgets to redraw the different displays independently. They just weren't written with that in mind.
It'll be fun, but it will be later.
Before I move on to talk about documentation, are there any questions about the font system of screen modes in Pyromaniac?
Let's move on to talk about documentation.
One of the very last things that was done on Pyromaniac before the presentation last year was to begin creating API documentation. This would go alongside the main product documentation to explain some of the differences in its the Operating System. I had, over the previous year, begun to resurrect the PRM-in-XML documentation project. This was a project started in around 2001 and which was made public at that time to very little interest.
Essentially the project intended to replace all the documentation in its disparate forms with structured documentation specially designed for describing interfaces used by RISC OS. I described this in much more detail in an article on Iconbar earlier this year, so if you're interested in the rationale and some more detail about it, you can find out more there.
For Pyromaniac, I needed to describe the details of how the interfaces
were different where it mattered. Primarily this was for new APIs, or places
where existing documentation didn't exist. There are a few new APIs, but the
one that caused me to need some documentation was the
This SWI manages 'Application Memory Blocks' - the memory that lives in application space.
It had no real documentation, and it was a vital call necessary for the WindowManager to function. Not only was there no documentation, it would be necessary for parts of the behaviour to differ from the Classic version. The reasons for the deviation were largely related to simplifying the behaviour. In some cases, the interface does not translate well to a modern system.
PRM-in-XML was an ideal way to do this, and the documentation was added for a few pages. Last year there were 3 API documents in Pyromaniac. Now there are 8 API documents. Not a huge number for an Operating System, but it's a step in the right direction.
As I mentioned, I'd rebuilt the original PRM documentation so that it could be used as the basis for full documents. I intended to talk to some people about getting this released because it was some good work and there had been very little useful effort towards documenting RISC OS properly. In my opinion.
After writing the article about documentation, there was some good interest from Alan Robertson to try it out. I gave him access to the tools and a skeleton guide that I'd written for 'how to convert documents to PRM-in-XML'.
He very quickly found a number of problems, and asked a lot of useful questions. The problems ranged from the tools not working on RISC OS, to bugs in my documentation and the transformation. After a little while, I set up a little staging area for documents that were worth capturing but weren't ready for the PRMs yet. This was also a little playground to try things out.
Alan has converted a number of functional specifications to the PRM-in-XML format. This showed a number of problems with the way in which it was structured. The tool was designed for PRM-like documentation. The manner in which Acorn wrote its functional specifications didn't translate directly - but you could definitely make a good showing with it.
This is an example of one of the conversions that Alan made. It updates the Acorn-era functional specification for the Nested Wimp to be documented in the PRM-in-XML format.
The content is largely unchanged and that's the point. The conversion hasn't changed the meaning; only the presentation. Updating the content to be written more like the PRMs would be relatively trivial, and then the documentation could be dropped into the regular manual.
In this case that's not quite true because this is one of the special
extended reason codes for
Wimp_ForceRedraw. But it's a lot easier to work
with in this form - and in a more familiar form.
Alan has converted a number of documents, and they're going quite well. The biggest problem is my having time to review them.
Meanwhile, I updated the tools to add some new features and make them more reliable for users - myself and Alan at this stage. There was a lot of progress over a few months, and then I gave him access to the partially converted PRMs.
He's been updating those and adding new bits. It's going well, but there's still a lot to do. I've done a lot of the ground work and I don't see myself converting these documents, so it makes sense for someone to come at it with fresh eyes, and I can do some of the tooling work.
Back when the original PRM-in-XML documents were created, Andrew Hill did a lot of work in converting them based off the original HTML versions that David Thomas produced. Working with Alan has been really good fun so far, and I'm hopeful that this work will become available to other people sometime in the new year.
It's been a broad collaboration amongst a number of people over a long period of time - it feels like it's coming together now.
There were a lot of little things that Alan has suggested, and some things that I've brought in. I'm going to just give one example that was very effective.
In the original PRMs back in the early 2000s I wanted to allow people to include key input that looked like the RISC OS documentation - using the upward arrow for shift, and caret for ctrl, and labelled use of the cursor keys. I wanted this to be flexible so I added some entities for these keys.
The problem with that is that it's not actually flexible enough. In some cases you want the keys to be displayed differently, and you cannot easily do this with entities. Alan had asked the best way to describe key presses and I wanted to make things more flexible.
At first I suggested a simple representation that allowed modifiers.
Which would be simple to write, and would allow the representation to be decided by the transformation. However, this implies that you can only combine some modifiers with keys - pre-defined ones.
I set out a number of things that we need to be able to represent, like presses, modifiers, mouse button actions, and sequences. From this I suggested a number of different ways of describing them, and Alan pulled in some examples from how DocBook and Wikipedia handle this.
A number of different ideas were tried, and some are shown here.
We finally settled on a grouping
input element, with some more
support for mouse input type (scroll wheels and the like), which looks like this:
Once we'd settled on a way of representing the key sequences in the XML,
the transformation could be updated to present it in a nice way. The
nice thing is that it can change the style relatively easily when it's
been structured correctly. One of the reasons for using the well known names
ctrl was that those could be translated into their
common forms of
^, but if we decide in the future that
we want to change that presentation, we can do so in the transformation
without changing every single document.
The transformation that creates HTML 5 output is quite nice for these input sequences, and even has a special option to make function keys red. The example picture shows what these keys look like in the current development version. It's just CSS, so if you don't like it you can restyle it however you like.
As always it's a matter of time and energy that decides what you can do. I spent a lot of time working on fixes and updates to make the PRM-in-XML tranformations HTML 5 and CSS compliant... and then making them look good.
'Looking good' is a pretty subjective thing, but the goal with the PRM-in-XML tool is that you should be able to re-style or restructure things if you want, so what is really needed is a good baseline that people can work from. The style used by the PRM is the baseline I'd chosen, and I think what I've produced matches it pretty well.
However, I wasn't happy with just one variation. There was an excessive amount of focus on getting the fonts right in some of the discussions online. This is, to me, a typical example of bike-shedding - discussing the trivialities to the exclusion of what's actually important. What's important about manuals is conveying information, and whilst it is important to get a good font, it's by no means the most important thing.
That said, people have strong opinions about it, so obviously it's useful to be able to accomodate them. The PRM-in-XML variations allows different styles to be chosen, either from a pre-configured set that I've put together, or which you choose yourself.
The font is one thing, but actually there have been quite different styles used for the manuals over the years. The RISC OS 3 PRMs, and the supplement for 3.5, are the best known, although the supplement itself has a slightly different style. However, this differs considerably from the style used by RISC OS 2's PRMs.
This is an example of a PRM-in-XML formatted page compared to same page from the the RISC OS 3 manuals.
OS_ClaimSWI from the paper manuals.
The spacing is a little different and the fonts are slightly different for the headings, but it's a pretty fair version of the documentation. More importantly, it contains the information you need.
This is the same page, but using the RISC OS 2 PRM style.
Again, the original printed version is on the left, and on the right is the PRM-in-XML transformed manual, but using the RISC OS 2 variant.
As you can see the style differs quite a bit from the RISC OS 3 manual. Obviously there's a big difference in how the headings are laid out, and the dividing line which separates the content. This style was a little harder to generate because it doesn't fit quite so easily with the CSS, but the reproduction is pretty good.
As you can see there, the tooling can produce these different styles from the same source. Not only that, but using the CSS paged media styles, it's possible to produce good quality documents.
Those of you who are in the Pub will be able to see a couple of example documents I've produced. These just have a few chapters in them, but they show the type of output that can be produced when printed. There's two styles there - the RISC OS 2 style and the RISC OS 3 style.
I'll quickly flick through the pages of the documents so that those of you not in the pub can see what they're like.
<Load up the prm-networking-example.pdf and share its window> <move to the contents page>
The RISC OS 3 variation is similar to what you might expect, and there's not a lot to say about the content. However, you should be able to see that the contents page covers all the chapter details. The page numbers are all linked, just like you'd expect and although there are only 3 chapters in this example, you can see that they cover a few different areas.
<Click on the Resolver chapter>
If we have a look at the Resolver chapter, we can see that it's structured in the usual way - the Resolver document was written based on earlier documentation the described the interface, but formatted into the correct style.
<scroll to the technical details section>
You can see that SWI references have page numbers listed beside them, just like the contents.
<Scroll to the structure definition for Host entries>
The host entries here are described with a structured offset table. These structured tables mean that they can be restyled if we need to. If we go to a SWI we can see how the interfaces are structured.
<click on the SWI Resolver_GetHost entry>
Again, the SWIs are presented as you'd expect, with all the usual information available. All the fields are defined in a structured way so that they can be reorganised or restyled as necessary. In theory we can produce StrongHelp manuals directly from the content, although it might be a bit wordy for StrongHelp.
If we look down here you'll see a big gap and the 'Use' section is on the next page. That's a small bug - the entire 'Use' block is marked as being kept together, so they're pushed to the next page. I haven't got around to fixing that one yet.
<Return to the top (Home) and scroll to the Indexes>
If we return to the contents page we can see that there are indexes at the end of the document. All the indexes are generated automatically from the content of the chapters.
<click on the Commands>
This is a simple index of all the commands in this example document.
<scroll down to SWIs>
And these are the SWIs in the document, sorted by name. Beside each, the description of the interface is given. They don't have to be, but it seemed to make sense at the time.
Let's have a little look at the RISC OS 2 version.
<open the RISC OS 2 version prm-networking-example.pdf and share its window>
<Scroll to the contents>
The RISC OS 2 version has a vertical dividing line between headings and content. This means that there's a lot more white space but it also means that there's a lot more focus on delivering information in a way that's easy to see at a glance. Is it effective? I'm not sure, and maybe it's the fact that it's different to what I'm used to that makes it interesting.
If we look at the Resolver documentation here...
<click on the Resolver chapter>
... we can see the same content, but just formatted differently.
<scroll down to technical details section>
Again, the same information in this technical details section. And finally a look at how the SWI is laid out.
<click the SWI Resolver_GetHost entry>
I'm actually quite partial to the RISC OS 2 style, but really it's a matter of taste.
The content of these example documents will be up on the resources site as PDFs.
I'd like to be clear though... I really don't think that printed manuals are the way to go. It's fine that some people might want to print them out, but economically (and ecologically) they're not a good answer - and what's more important is that the documentation is available online and can be searchable easily. HTML is great for that, and that's where the bulk of the effort was spent.
The reasons for being able to produce paged media like these PDFs is really two-fold:
Pyromaniac will continue to use the PRM-in-XML documentation for its APIs, because it's the right thing to do, and I'm going to keep updating the tools to make them available to people. It's a bit of a slog, but it is fun and it would be good if maybe one of my long term projects actually came to fruition.
<return to sharing the Pyromaniac window>
Before I move on to the next section, does anyone have any questions about the Font system or the documentation project.
You might remember that one of the things I care about is testing, so let's talk about that...
In my recent presentation about software testing I talked a little about how you can test operating system features. Essentially I have a lot of expectation testing of the commands and system calls, using the text output to track whether things work as needed. That's fine when you're exercising the *Commands, or simple SWI interfaces, but for graphics it gets a bit harder.
But really it's only a bit harder.
In other places I've done some work with producing diffs of images, and presenting the results nicely, but here I'm dealing with simple images, in paletted modes. The simplest way to handle it is to save the screen to a textual image format and compare the results in the normal way.
The textual image format I use is PBM, because it's simple. Obviously, such output files are viewable by the RISC OS thumbnail system through ImageFileConvert, but I'm using macOS... and macOS supports them through its preview system too.
Of course, saving the whole screen to display simple graphics would be overkill in most modes, so the graphics tests use a custom mode which is just 80 pixels square. That's not a lot, but it is enough to exercise the tests.
This is what the expectation files look like on disc. Actually this is slightly cut down to fit on the slide. The PBM format describes the width and height of the image at the header of the file, and the maximum colour number that is used.
Then each of the colours is listed. Depending on how well you spot patterns you may be able to see a chequer-board like pattern of different colour numbers in the middle.
There 3s, then 1s, then 2s, then 0s.
As that's the colours that are in the original sprite, that's correct.
Because the system compares the different text files, any time that the graphics system puts the wrong colours down, we get a difference in the expectations and the tests fail.
ColourTrans is quite frustrating to test. We can ignore the fact that
it's dealing with colour, generally - it's just manipulating arrays
of numbers. The simple calls like
ColourTrans_ReturnGCOL will return the
colours being used, and we can compare them to some simple expectation.
This is an example for that SWI in a standard 16 colour mode. It's testing both the conversion to a GCOL number and to the opposite colour number.
The same sort of test is also run in other modes with different depths to check that their behaviour too. And then you get to the 256 colour modes which have crazy colour numbers and tints.
I've compared my results with those from RISC OS Classic and I think I'm getting it to do the right thing.
ColourTrans_GenerateTable is another matter though. There are many
combinations of parameters that can be supplied to the call, and I
only have 24 combinations that I am testing at the moment.
It was interesting working with the 256 colour modes and finding that the colour numbers returned differed from Classic ColourTrans. The reason was largely that I hadn't been accounting for the ColourTrans weightings. With the weightings added, it was still slightly different, but good enough.
Testing is great, but it would be really good to be able to test the user interfaces. I've got 3 interfaces - WxWidgets, GTK and VNC.
And I have no testing for them at all. I'd like to test them but user interface testing is more tricky. I've got a simple environment set up under Linux which allows me to test applications in a desktop environment, but getting that into the testing in a structured way will need more thought.
Similarly, I'm producing a couple of applications for different platforms, but they aren't tested either. There's a Windows application, and a macOS application - both of which contain a bundled Python, all the packages and the sources. But they're never tested.
And that's a problem, because it turned out that both those applications had been broken for about a year. Fortunately they're now fixed, but it would be good to add some testing for them soon!
So having just said how there's some big holes in the testing, how much do I actually have?
Some of that code coverage difference is in code that's not exercised in the user interface and platform code. And some is due to the large amounts of debug code that's conditional and never hit in normal tests.
If you like statistics, you'll find more information on the resource site.
That's enough about how I test the OS... let's talk about its test features have improved.
The disassembly produced by the tracing system has been improved in a lot of little ways. The code generated is more like RISC OS disassembly in some places, with more special cases to turn the more modern assembly format to things that RISC OS users are familiar with.
This includes the FPA floating point instructions - there's a bunch of dedicated code that handles decoding those instructions into the mnemonics we expect. This was added largely because some floating point code turned up in a module I was debugging and I wanted to understand what it did.
There's also some better logic in place for decoding the register values when the disassembly is using live registers. This means that some values from the registers will be shown as numbers where they weren't before, and some places will include bit numbers. This can make it easier to follow the code's execution.
Together with the function name decoding as we enter new regions of code, and the special decoding of a dispatch table entry, it's a lot easier to see what code is doing.
The status register can be decoded in a number of places, which means that it's easier to see what flags will be selected. The example shows the regular condition flags being updated explicitly, and only the flags in the PSR which are changed are shown.
There are a few more SWIs that now use the trace system's 'warning' to report cases where the interface has been used within invalid values.
This includes the LegacyBBC module which can now report when you use calls that it provides. This could help replace code that uses those ancient interfaces.
When a watchpoint or tracepoint or SWI trap is triggered, the state of the registers are listed as locations if the memory exists. This includes referencing the function name if one is known. If the register refers to the private word of a module, that's also described.
There are new options to the SWI traps which allow the trace state to be changed dynamically when the SWIs are executed. This means that if you know that a particular SWI is operating badly you can turn on the tracing just whilst it's executing.
Or if you know that a failure happens after a particular SWI call you can turn on the tracing from that point.
In the future, this might be extended to have other types of triggers, but I haven't needed them yet.
Sometimes when running the system you want to interact with it out of band of the actual system. By that I mean the system's running and you just want to see what it's doing. But you can't get to the command line to turn on the debug.
You can sometimes restart the system, enable debug on the command line, and get back to where you were, but that's not a nice option even when you can do that.
So in the user interface there's an extra menu item to control which debug options are enabled whilst the system is running. This makes it a lot easier to check what's going on when you get to the right place.
When we come to the system demo, we'll see that in use.
There are a few bits and bobs that are important, so I'll talk about them and then we'll move on to the system demo.
When I implemented the sound system, which was present in the version last year, I'd wired the SoundChannel system to the host's MIDI. And I tested it mostly with the simplest of things - sample code from old books on the BBC sound system.
Hall Of The Mountain King is probably relatively familiar to people, although maybe not in its BASIC form here.
That was the way I tested much of the original sound system. However, this meant that
only some of the
SOUND calls were actually being tested.
In particular, Arthur had introduced a different form of pitch specification which allowed much more granular setting of the frequency. This wasn't supported at all - and some of the newer sound tests that I'd been using needed to use this format.
So I had to do a bit of maths to convert the pitch down from the new pitch format to the BBC format - because really that's all we support internally at the moment.
There were also a bunch of bug fixes for different interfaces - it hadn't been possible to select channel 8 at one point, and some interfaces had off-by-one errors so would select the wrong instruments. It produced some very ugly sounds at times.
The SoundScheduler is the next layer up the stack from SoundChannels. Its not actually used that often, but it provides an arbitrary scheduler for SWI calls. This actually matters because some applications do use it to play music.
The SoundScheduler, under RISC OS Pyromaniac, is semi-faked. It doesn't use a clock from the sound system, but instead uses the regular monotonic timer. Because of how the system is configured this is approximately the same thing.
When it was first being tested, the scheduler was playing really slowly and I spent some time trying to work out where the problems were. In the end it turned out that I'd left an excessive level of debug enabled - turn that off and it worked pretty well.
There's also an implementation of SoundDMA on a branch, but I've not touched it since last year. It has a number of lazy assumptions that really need fixing.
The sound system is a complex and badly documented beast, but I think the implementation is pretty close to the original, at least in the main functions.
In the middle of the year I decided that I'd make the text cursor - the flashing line where you're going to input text - actually appear. I'd had stub code that tracked the state of whether it should be on the screen or not, but hadn't implemented the actual rendering of the cursor. I'd been somewhat reluctant to add in the flashing cursor because it wastes processing time and serves no diagnostic and testing function.
With all the other graphics changes that had been added, I'd been trying to include the necessary calls around them to ensure that if the cursor were there it would be removed. As you can see, the way it's invoked makes it very simple to be sure that the cursors are handled properly.
But there was no way easy way to know whether that was right or not except by actually having a real cursor.
Adding the cursor was a little fiddly because the way it's usually implemented is that you find the scanline in the screen memory, read a character's worth of bytes, invert them, and write them back.
However, in the implementation of the
I'd added a means by which a horizontal line can be read from the screen buffer.
It can return each pixel from the screen as a colour number,
just like you'd have on RISC OS Classic. Creating a similar function to
write a horizontal line of pixels to the screen was actually quite simple. It's not quite
the same routine backwards but it's pretty close.
The actual body of the cursor code looks like this.
I hope you can tell that - in comparison to how this would have been implemented in assembler - it's relatively simple.
My reluctance to add the cursor as a default was still there, and I settled on some simple logic. If you're using one of UI implementations, the cursor is on by default, and in other cases it be off. But you can explicitly override that, if you really want it on, even if you can't see it.
Once the flashing cursor was added the system really began to feel like a real OS. That was genuinely surprising to me. Obviously I'd had a cursor when I was using it in the terminal, but seeing the cursor flashing at a Star prompt made it somehow more like a real system. I know that not much has changed much by adding that, but it felt much more like a real RISC OS after that point.
One of the earliest things that was implemented in Pyromaniac - probably the earliest -
was the output stream which was used by the
OS_Write* SWIs. However, this just went straight
into the VDU system. In RISC OS Classic, characters pass through
the write character vector.
This allowed for some efficiencies for Pyromaniac - strings could be written out in one go,
rather than being broken up. However, this meant that some things didn't work as you might
expect. You've probably not encountered it, but the
Wimp_CommandWindow traps output so that
it can insert the drawing of the command window when your task starts writing output.
And on RISC OS Select, the BootLog module would capture all the output before the desktop was entered so that errors could be diagnosed more easily.
The problem is that breaking up the output so that it goes to the vector, one character at a time, would make everything a lot slower. So as with the flashing cursor, I made this a configurable option. Two options that you have are to always use the vector, or to never use it - but the default is to use the vector 'as required'. If someone has claimed the vector, it will split everything up and pass all the characters to the vector one by one. But nobody has claimed the vector, the fast internal path using whole strings will be used.
This made the command window work properly, and the BootLog recorded things just like you'd expect.
There are two other interfaces that weren't using the vectors which could.
The Draw module was expected to be passed through
DrawV. This was pretty easy to do, but
it looked like there was no way for the vector to report an error, so I've changed the
interface slightly to allow unclaimed SWIs to report errors.
Similarly, the FontManager module was meant to use
FontV, but (for some reason) this was
disabled in '92, and had never been added back again as far as I can see. Pyromaniac can
be configured to support using
FontV if you want, with an identical interface to
The hourglass isn't very exciting - last year, I created the scripts that build different hourglass modules and published them up on GitHub. They look ok, but nothing special, but I do like the little spinning cog. You should see it pop up now and then whilst I'm demonstrating.
I created an experimental branch for the hourglass maker that allows percentages to be displayed, either as digits below the hourglass, or as a little bar indicator. Or with both. It's up on GitHub if anyone wants it.
The point of creating the percentages was that the data generated by the maker can be dropped into Pyromaniac, and with some small tweaks to the Python code, we have percentages in Pyromaniac as well.
We'll see the hourglass percentages in the demo shortly.
Originally when I created the Cairo graphics implementation I didn't think it would
be possible to do all the
OS_Plot actions. Fortunately, most of the actions are
pretty useless - so they would rarely be needed.
But the 'Exclusive OR' operation is commonly used to provide simple animation effects by plotting and unplotting shapes.
Whilst it won't work quite the same way in paletted modes, the effect can be
simulated using the
DIFFERENCE operator to draw shapes. In paletted modes it
won't do the right thing - the colours drawn will not match the exclusive OR'd
colour number. But it works well enough some of the time.
OS_Plot can also be used to draw lines with a pattern of dots. It's configured
VDU 23, 6 and
OS_Byte 163, and it also allows a continuation of the
pattern, so that you can draw a pattern which continues around corners.
Cairo can be configured with a dot pattern to use, but it is a bit fiddly - converting from the bit pattern used by RISC OS to a similar pattern used by Cairo was surprisingly difficult.
In the end it was just 66 lines, but it took quite a few hours to get it right. And it still doesn't support those continuations.
There will be more work on the
OS_Plot interfaces but there are fewer interfaces
that need to work now.
At one point I had some problems trying to get mouse input with the UI. The mouse clicks worked fine usually. But if I double clicked I wouldn't get any events being delivered. I decided that it was probably due to the system running a lot slower than it was expecting. If the system took longer to process the mouse click than the double click delay, then that would mean that we missed out on detecting the second click.
So went to add the code to print out the time that each event was received and when it was delivered... and found that the code wasn't quite right. Instead of the mouse event time being a value from the monotonic timer, it had been given as the unix epoch time times 100.
Whilst that meant that it still counted up in centiseconds, it wasn't comparable
to the value from
OS_ReadMonotonicTime, so could mean that the click events
weren't detected properly. I changed that code so that reading the time returned
correct values for the timestamp.
Still double clicks didn't work.
The mouse has a few ways of being read. Some of them go through the mouse buffer. Some of them don't. I'd not used the mouse buffer at all in my implementation, and everything was being read directly. Maybe because the clicks weren't buffered, the problem with the implementation still being slow was still reasonable.
So I added some code to debug what was being delivered and started implementing the mouse buffer - it's pretty simple as it's just a matter of pushing some data into a queue on one side and pulling it out at the other.
However... I noticed in testing it that when I double clicked there weren't any events delivered from the interface for the second click.
If there are no events being delivered, there's no way RISC OS could report them. A little more debugging and it turned out that the WxWidgets implementation handles double clicks separately, and I hadn't requested they be delivered.
A few changes to the event registration inside the window handler and double clicks now worked! I finished the mouse buffer code, and so we have a nice mouse buffer and the events are delivered properly now.
A few modules were implemented and finished...
python-zipinfo-riscoslibrary has been updated to make it easier to create RISC OS style archives from non-RISC OS systems.
Next up is the system demo, and that's likely to generate its own questions. So before we move on to that, I'd like to invite anyone to ask questions about the last sections - the testing and miscellaneous bits. Or about anything else up to now.
I suspect this will very quickly become a very boring demo as what you're seeing will be very familiar. However, I'll be explaining what things are and why they matter as we go, so hopefully this will make it more interesting.
For this demo, please feel free to jump in and ask questions about the things that I'm demonstrating. I don't mind if it's a question about what I'm demonstrating or how it works under the hood - there is a lot going on in the system so if I'm glossing over something, speak up!
<Start running the demo with
pyrodev --config-file wimp.pyro 2> trace.txt. Ensure the terminal window is on the left of the screen and the Pyromaniac window is on the right. Ensure that desktop icons are hidden.>
<Share the new window>
This is the boot menu. It was kinda working last year, but now it's not using any special changes to work correctly. There's a sprite for the cog, and the graphics operations all work properly. If I cursor down the page it scrolls properly. We can exit to the command line just like you might expect.
<select the 'exit to command line'>
The only reason I've done that is to show that the flashing cursor works. There's not a lot else to say about that.
The desktop banner here is one of the examples of a squashed sprite that's decompressed. Using the native decompression code it's reasonably fast.
And we have a desktop. There are only a few tasks running at the moment - we can see Task Manager, Display Manager and the Resource filer on the iconbar. And there's Pinboard running in the background with a nice gradient.
<Open TaskManager menu>
The TaskManager menu is built using
MessageTrans_MakeMenus, so you can see
this is working properly. You can also see here that there's a little shift
character on the Shutdown item - that's the font change control character
working properly. And of course, the shortcuts are right aligned, so we can
see that the inter-word spacing is working too.
<Click the Task display>
The Task Display item flashing is an effect caused by the Exclusive OR plot action.
The TaskManager window is pretty much as you'd expect. There are a couple of points of interest though. Firstly the window background is the tiled desktop sprite. This is the same as the iconbar, but it's more noticeable here. It's fast to redraw because it's almost entirely handled by the native graphics system.
There's no Font cache shown and no RAM disc shown. Neither of these dynamic areas exist, so TaskManager greys them out. Similarly the free application space is greyed out. The free pool does not exist on RISC OS Pyromaniac, so it isn't shown.
<close window and click on DisplayManager>
Display manager works, and it shows the resolutions we have available.
<Click resolution display>
I could change mode but this mode is fine for the demo.
<Menu on the DisplayManager iconbar icon and go to the Devices menu>
Display manager shows that the multi-display support is exposing the display device name. You can't install other displays yet, but it knows about the one we have.
<Click Cancel and open the demo filer window>
So we have a Filer window. It shows files and applications just like you'd expect... so let's look at what's going on here. Obviously we've managed to list all the files, and their types so the filesystem is working. It's improved since last year but that's not especially new. All the sprites are plotted properly, with masks. They're in a variety of modes so those are working too.
<Drag a selection box around !Chars down to memnow; hold the box to see the marching ants>
I'm selecting a number of icons using a drag box, which uses the dotted plot operation. The pattern of dots is updated by the WindowManager so that it gives the appearance of the pattern moving. Actually it's even more clever than that. It's using the dotted pattern with an Exclusive OR to change the dots only where they have been updated.
There's no 'undraw' operation whilst it's animating and it looks more smooth. That means that both those operations - dotted plot and the Exclusive OR need to work exactly like they did in RISC OS Classic. And they do.
<release the button if you haven't already>
Now we have 4 icons selected, and they're highlighted in green. Back when I explained about how ColourTrans performed its translation, I mentioned it could use a transfer function to change the colours. The green tint is the result of a transfer function highlighting it as configured.
<Click off the icons>
Right lets have a look at some debug.
<Change the sharing to the whole screen>
We can see the RISC OS desktop and the terminal that launched it here. The terminal shows any text that was output from the system, and any debug.
The menu for Pyromaniac has a Debug option which allows the debug settings in the OS to be toggled at runtime. I'm going to turn on a few of the font options.
<turn on: font, fontmanager, fontparser>
With these font settings enabled we can now see what's going on when we interact with the desktop. I'm going to click on Maestro...
<click on Maestro>
And we see that there were a bunch of font operations performed. You should be able to see that it's getting the size for the text of each icon in the window. Presumably it's doing a bounds check on the text for the click, although it could have been more efficient about it by just checking whether the pointer was in the whole file's bounding box.
So we've learnt something that can be improved just by turning on one set of debug options.
Finally at the bottom here we can see that it selects the colour for
the text and plots the
Let's turn on the sprite debugging.
<turn on: spriteop, spriterender, sprites. turn off font, fontmanager, fontparser>
Now whenever there's any sprite operations we'll get extra debug for them.
<Click off the maestro icon>
Deselecting the icon caused a redraw of the icon, and the sprite operations are now visible.
At the top we can see that there are two sprite operations- 228 and 234 -
which read the sprite size and the plot the
Then there is a call to plot the desktop tile sprite, SpriteOp &241. And then we can see the details of the sprite tile operation.
At the end we can see that the pointer was reset back to its original shape, and there's a diagram of the new pointer shape.
When something goes wrong, or it's not clear why things are happening, it's relatively easy to change the debug settings.
<turn off: spriteop, spriterender, sprites; return to sharing just the window>
So, back to the Filer. We can select icons and perform operations on them, just like you might expect.
<select !Chars, and choose Selection->Count>
So that's Filer_Action working. That's also demonstrating, behind the scenes, that module instatiation is working properly. It's not used that often, and getting it right is fiddly.
So let's run an application.
<double click on Chars>
As you'd expect the !Chars application is pretty simple, and works fine.
<click Menu over it>
This is a FontMenu created by Font_ListFonts. You can see it works too.
The fonts work pretty much as expected. This shows the same characters as we saw earlier in the presentation for Selwyn, showing the font encoding works properly.
One thing I've not demonstrated to now is the window sizing.
<resize the filer window to the right to get an extra column>
As you can see we use the dotted drag boxes for the resizing. This is a lot faster than the live resizing operation, and you can see that the window resizes as expected.
<double click !clock>
Clock has some problems. Something about how it selects the colours to draw with means that the hands don't undraw properly. And the big and little hand look weird. I've got something wrong, but I'm not sure what yet.
<close clock, double click on Friends>
Let's look at Draw... As I mentioned in the presentation last year, the Draw module is implemented using Cairo so it's working well. It's drawn the file just fine.
<select the black background, click menu and go to Select->Fill colour>
Draw uses the ColourPicker module to select the colours you want to use. As you can see it works, although it's a little slow drawing the colour selector.
<choose a dark red and click ok>
The colour selection from ColourPicker works, just as you'd expect.
<Quit the application and click Discard when the dialogue appears. Load Edit.>
Edit works - you can edit files.
<Enter 'Hello world', Press F3 and drag to the demo window>
You can save files, but this shows up one unimplemented feature - filesystem upcalls aren't implemented yet, so the Filer doesn't redraw.
<Click Menu and refresh on the window to show the text file>
<Double click on the rougol sprite to load it>
Paint works - it loads sprites. And the scaling buttons work.
<zoom up to 4:1>
It's pretty much what you'd expect - except you cannot edit the sprites at all. None of those operations have been implemented yet.
<close the sprite and sprite file>
<go into Music and load Entertainer>
Let's try some music. There's a bunch of Maestro music about, and although it's not had a lot of love, it's been something of a fun application to make work.
Maestro takes a while to load files because it uses BGET for reading the file, which means that it is quite a slow path to get the data.
<Resize the window to full. Use the menu to select Play. Wait for bar 5>
Hopefully, you can hear that it's playing. And you should be able to see that it's scrolling in time with the music. I think it's quite impressive - it's a multi-channel track, and it's keeping up with the playback quite well.
<Let it get to about bar 15-20 before stopping it with the Play menu item>
<Quit maestro and other applications that are running>
We've seen RailPro earlier, and this time here's a desktop game - MineHunt.
<Load MineHunt, and click on it>
It uses sprites as its entire interface and it works reasonably well - although showing the empty regions sometimes stresses it.
<Click a few times until you get a largeish region uncovered. Get blown up>
<Quit it, and open the PE directory. Load Tinct and then Privateye>
Dave Thomas' PrivatEye works - it can load Drawfiles.
<Drag More Than Meets The Eye on to it>
And it can draw JPEGs.
<Double click the rougol/jpg file>
There's something up that makes it upset with PNGs, but I've not looked into that just yet.
Ok, this is all pretty regular fare, so let's try something a bit more complex.
<open the More directory and double click on Fireworkz>
Fireworkz is a bit more complex, and somehow it tickles a bug in my heap handling which causes a Dynamic area to resize badly.
<click on the iconbar icon>
It's not a problem though. We can ignore it for now - I'm investigating what's up there.
<select Letter and OK>
It runs, and I can enter text.
<Enter 'Hello world'. Press ctrl-a to select all. Press ctrl-i to make it italic. Click off>
It's styled in italic. You may have noticed the caret doesn't redraw properly and the font isn't quite updating right. We're at the edges of what's working and if I do too much we'll fall off one of those edges and crash. But it's running a decent sized application.
<click quit, 'Continue' the error, and discard the document>
Pipedream works too, but it's a lot slower in use.
Finally, it's time for another game. Dave Thomas disassembled the entirity of The Great Escape for the ZX Spectrum. He then rewrote it in C.
<Open TGE, and double click on the !GtEscape icon>
So this is a ZX Spectrum game for RISC OS running on macOS.
<click on the iconbar icon and then click on the window>
<Enter 0 to start the game, then L P, Z, X, and Space. Confirm with Y. Walk outside into the yard. Walk straight down and right.>
It's not terrible, for a desktop game being emulated. And it runs in full screen too.
<Switch to full screen with menu, view, full screen>
<Walk around a bit more to show that you can>
It's a bit faster in full screen, and we can return to the desktop with Escape.
Right that's everything in the desktop, so let's shut the machine down.
<Go to TaskManager, click Menu and Shutdown. Select Shutdown from the dialogue that appears>
And turn it off- this also shows the final thing... the hourglass percentage appears when it shuts down.
<Click the Off button>
<Reshare the slides>
Let's wrap this up with a conclusion...
So what did I get done over the last year?
Lots of graphics system improvements. There were a lot of little fixes to the graphics and VDU system. The new ColourTrans and Sprite handling is now pretty reliable. Deep modes are usable, and 256 colour modes are now reliable. The new VNC implementation means that more doors are opened for using the system in the cloud. The hourglass finally has a percentage.
Lots of filesystem improvements for how many FSControl and GBPB operations work,
and commands like
*Wipe are now implemented. The encoding used by filenames
is now handled much better.
Input now works much better, with keyboard scans being properly supported and the mouse buffer being used properly.
A bunch of debug features were added to make it easier to trace when certain calls were made, and to improve the disassembly and reports. FPA instructions are now disassembled.
Improved SoundChannels, and new SoundScheduler module for timed playback. A bunch of new modules added to round out the use of the system a little more.
Many fixes to parts of the system, including problems with creating module instances, filing system commands, and application space.
And improvements to the PRM-in-XML documentation system to make it much more useful.
There were a bunch of other things that I have worked on which weren't discussed here - a server for the documentation project, the FanController system, URL fetcher, a project called PRIDE, some experiments with different architectures, and a few build tool changes. None of those are at a point they're ready to be shown yet, but maybe next year they will be.
The shell server was updated at the beginning (and had been updated about every other month). Although there's been no website changes for the RISC OS build server over the last year, the back end has been updated equally regularly, taking that month's release.
I've updated the information site at https://pyromaniac.riscos.online/ with more resources from the collection of images that I created over the last year, and updated documentation.
I would recommend people take a look at the
FEATURES documentation on the site
to see what is and isn't supported. And take a look at the
CHANGELOG for the last
Whilst you might expect that I'm pleased with the work that I've done on the operating system, I'm also rather proud of the fact that it's pretty well documented and the changes are well recorded.
I guess for most people writing notes about what they've done and keeping a decent change log isn't that fun. But I think it's a general part of what I enjoy. Not only does it show me what I've achieved, but it's the right thing to do. So what if I've got no users and nobody's shown much interest...
So yeah, RISC OS development is still pretty fun, and I'm still enjoying the work that I'm doing.
Working with Alan Robertson to develop parts of the documentation system has been a lot of fun. It is one of those areas that collaboration really works well. I suggest something, Alan gives his opinion, we decide on the right way forward - or vice-versa. Similarly Alan reports bugs and we work together until we find what's wrong. That's been great.
My small diversion in the middle of the year to discuss software testing was largely due to comments by a RISC OS developer about poor testing being a reason for a bug they'd found - something I'd previously commented on and written about. That work was born of frustration, but realising that I could address it with some education was good - and I may not be able to meet all my desires, but I can do that. That's fun.
David Thomas reported a bizarre problem with PrivateEye crashing which I attempted to reproduce within RISC OS Pyromaniac. I had to implement a lot of things, and fix a lot more before I could even run PrivateEye. And then I still couldn't reproduce the problem. But it was a lot of fun to get to that point, and I did find a few issues in PrivateEye that wouldn't have been seen otherwise.
I expect to find other things that have similar problems and will address deficiencies in Pyromaniac as time goes on.
I've really looked forward to doing this presentation and I've spent a bit of time trying to get it right. It's made me really stressed at times, but I've had some help from my girlfriend which has made the process easier. I'll do another one next year, but I'm not sure that leaving a whole year between them is useful - there's just been so many things I've had to drop and not talk about.
I've listed a few things that I'd like to look at here. Going by previous experience, I might get about a third of those looked at, and do other things for the rest of the time.
I know there's a bunch of problems with the system that are only exposed when they come to be used in anger. Fixing those and getting some tests to check their behaviour would really help - the crash at the start of Fireworkz seemed simple enough, but behind it there were some bad assumptions that need a little restructure to fix properly, for example.
But essentially, I'll do what seems fun as I come to it.
Info site: https://pyromaniac.riscos.online/