UI improvements
There were a lot of User Interface (UI) improvements in 2022. Mostly these focused on the WxWidgets UI, but some of the improvements have been applied to the GTK UI as well. Part of the original intent of this was to make the interface more usable as a debug and diagnostic tool. The goal is to be able to use the UI to step through a program with breakpoints, just like you would with a proper debugger.
Not all of the changes are meeting that goal, but they meet the goal of "having fun".
Data tables
Early on, I made some changes to make it possible to see information about parts of the running system. A list of the graphics variables, modules, dynamic areas and system variables was added to the View menu. How these were managed changed over time so that they became easier to add to the system, and over the year the types of information grew to include most of the resources available in the system.
The data tables are essentially simple tables that can show any amount of information in rows. The later developments also allow it to be copied to the clipboard, saved to a file and refreshed with live data. Towards the end of October, the View menu contained information from nine different sources, in addition to the custom displays like the GPIO pins, keyboard lights and NVRAM information.
Data tables like the AMB information used Unicode characters to make the state of certain features clearer, as well:
OS_Confirm
and Wimp_ReportError
It is rare to see an OS_Confirm
request in a modern RISC OS system. This is because operations are generally performed within the desktop. The request is used for the command line multi-file operations (for example *Wipe
, *Copy
, *Count
) and others. The implementation in RISC OS Pyromaniac had been to just ask the user for a key press, read with OS_ReadC
.
However, if you're using the UI it makes sense to take advantage of this and open a dialogue box to get the confirmation from the user. The OS_Confirm
call was changed so that it can do this, adding in a new implementation that allows us to get a yes or no from the user with a button click. This makes the system more integrated and whilst it might seem a little gimmicky, the system does feel more like another application rather than a separate OS running under emulation.
Originally this used a standard WxWidgets dialogue, looking like this:
With the confirmation dialogue being available in the host UI, the Wimp error box seemed like a similar candidate to be made into a host dialogue box as well. The internet error box handling had been abstracted to allow just this behaviour, and with the knowledge gained from implementing the OS_Confirm
interfaces, it wasn't that hard to create a dialogue that looked like a RISC OS Wimp_ReportError
box but was entirely running on the host.
Once the report error box had been made work, the greater understanding of how to do it well was fed back into the OS_Confirm
implementation, to make the confirmation box look more like the error box. That made the style much more consistent:
Those error boxes have little icons for the category of error that is being shown. The category is actually a PNG (or GIF) on the host side, as they're effectively fixed. But the application icon is a sprite - the sprite is converted as needed to a bitmap that can be displayed in the dialogue. This isn't so hard as we already do this for plotting sprites, but instead of plotting the sprite to a Cairo surface, we're now retaining it to use as a bitmap in a dialogue.
TextEditor
The Twin module provides a mechanism for BASIC to open an editor on the program, before returning to the command line to run the program again. This had been created 3 years ago to make it possible to edit files within the RISC OS environment. The Twin interface had been extended to allow it to be invoked through different implementations - the command implementation, which can launch an editor like nano
and the wxwidgets
implementation which opens a window in the host system.
As some of the structure was already present for different windows after the work on Wimp_ReportError
, creating a separate window for editing content and returning it to the user was relatively easy. Making sure that the content was encoded properly, and that the different error cases and close conditions were all working was a little harder. Then a little later there were problems with OSX deciding to apply smart quotes to the content, which made a mess of things until the option was found to turn that magic off.
Together with this, a new *-command was created to allow the editing of files through this mechanism. A simple *Edit
would edit the file and when it was saved, write it back. There is documentation on how Twin functions on the Pyromaniac website.
This video shows the simple example of opening the Wimp_ReportError
window from BASIC, then editing the program in the host editor window, and running the program to see that the change has been made.
Memory dumps
As I mentioned, one goal was to eventually be able to use the UI as a proper debugger. To do that, one thing which would be needed was a display of the memory being executed, and ways to examine other memory. Obviously, there was code that displayed memory information implemented in the *Memory
code inside Debugger - code that had been reused as part of the trace system which reports warnings and information about the execution of the system.
However, that code was focused on textual output, and to look good in a window, each data item would need to be a table cell. The dumping code was refactored so that it could be used in two different ways - either as a textual output or as structured data. The code was taken out of RISC OS Pyromaniac and made into its own library so that it could be updated, tested and generally messed with without having to use the whole of RISC OS Pyromaniac. Plus it is open source so anyone else can use it.
The memory dump was then attached to new dynamic menu items which allow the modules, dynamic areas and AMBs to be displayed.
Because these are displays of memory, we can include the NVRAM as a region to display as well. Of course, the NVRAM is a little more confusing as it contains lots of different information and doesn't generally contain text. So the window has a status bar at the bottom which shows the meaning of the cell that the pointer is over.
Later, the options were expanded so that we can jump to a given address in the region, save the data to a file and even search for strings.
This video shows the menus and different forms of content in the modules, dynamic areas and the NVRAM. This is contemporary to its development so doesn't include the later features in the menu.
As we expect to use these memory windows for debugging, it is also necessary to disassemble the contents of the words. The disassembly code built into Pyromaniac uses the Capstone engine to perform its disassembly, with several modifications to the results to make it more like the familiar Classic Debugger.
The disassembly library was mostly separate from the trace system because it needed to be used by the Debugger module for *MemoryI
, but there were still a few bits that were hooked into the rest of the system. These remaining links were severed and moved to a separate library. Like the memory dumping code, this allowed it to be updated independently of RISC OS Pyromaniac.
An option for disassembly was wired into the memory display code to allow it to be shown in that form in addition to the regular bytes and words views. Because the components of the disassembly are known, it's possible to colour the instructions in different ways as well. I styled these to match the Zap configuration.
Of course, with the core disassembly now able to give colour information, this also meant that this could be used from the RISC OS system as well. The Debugger module was updated so that if Debugger$Options
includes a C
, we get colour output (as a video):
The Debugger module itself now has documentation for the new options, and the other interfaces that it provides, on the Pyromaniac website.
Request Input
To implement authentication with a nice interface, there is an internal 'Request Input' system in Pyromaniac. This can be used by any part of the system, and when configured to use the 'console' implementation it will just prompt with OS_ReadLine
. But, if it has been configured to use the 'wxwidgets' implementation, a prompt is shown in a host window. Like the other integrations which provide host dialogue boxes, this isn't completely necessary but it makes for a much nicer experience in the host system.
This video demonstrates the input requesting a username and then a password (which is hidden) for Git:
File explorer
One thing I wanted to be able to do with the system was to be able to examine and edit files from outside the RISC OS system, whilst it was running. This is going to be a relatively dangerous operation. Accessing files from outside RISC OS requires running code in RISC OS that may not expect to be called. It could corrupt the filing system state. But most of the time you'll be just fine. However, to be able to do that I needed to create a means of viewing the files on the filesystem.
No such system existed in RISC OS Pyromaniac, so I began with a clean library which was able to browse a virtual filesystem. The library began with just simple file browsing but expanded to include other operations as I needed them, with different view modes and sizes supported.
For the icons, I used the vector icons that I had created for Select 5, but as they had been a work in progress, they were somewhat incomplete. In particular, I either never got around to drawing (or lost over time) the !Edit and text file icons. I had never drawn a JPEG icon either, as its graduated face was likely to be difficult to reproduce as a vector. Without these icons, the explorer window wasn't going to look very good, so I created a new text file icon, and a JPEG icon, complete with its graduated face.
It was somewhat strange using !Draw after all these years, but it's still nice. It's not great, but it does the job.
This video shows how the system looked with the explorer. It was recorded before the text and JPEG icons were created. It demonstrates the integration with the memory dumps, showing that the files can be viewed as words and bytes, just like the memory regions. Then we demonstrate how the files can use the text editor window, showing that the files can be displayed in the same way as the earlier editing operations. The absolute file is shown as a disassembly, with the colouring appearing on some of the instructions and operands, and the file information is displayed for a file. Finally, the three different display modes are demonstrated.
Although the explorer only supports read operations on files - you cannot edit them as yet - there were options added for creating directories and deleting them. These can be seen in this video:
For comparison, the RISC OS Filer and the equivalent file explorer look like this, with the menus having a similar structure.
Window resizing and border
The UI window had been fixed in size, according to the size of the RISC OS mode that was being displayed. There was a minimum size that it would maintain, beyond which it would scale up the content. This meant that if you used a small mode the window wouldn't get too small - pixels would appear larger - but you couldn't manually scale it up beyond that.
That wasn't a problem most of the time, but occasionally it would be useful to scale the window up so that you could either see things better or so that they filled the screen. The WxWidgets UI was extended so that the display window can be resized to be larger, keeping the aspect ratio the same.
This had the side effect of allowing the application run in full screen mode. This had a similar effect to adding the flashing VDU cursor, making the system seem a lot more like a 'real' OS.
And here's The Great Escape, running in full screen in RISC OS, with the WxWidgets window maximised to fill the screen:
With the window being resizeable, the border could become more prominent, and so the graphics system was updated to support changing the screen border from within RISC OS. This might not seem like an important detail, but the border configuration was one of the major ways of communicating system failures in the early versions of RISC OS. This was also useful in later with with hardware displays to change their rendition.
Here are both the WxWidgets (left) and GTK (right) versions of RISC OS Pyromaniac with a configured border colour:
This has the obvious effect that you can now have a very big window, scaling up as necessary, in addition to having whatever eigen factors you would like. Here's a 2200x2600 mode, using EX0 EY0
, spanning multiple physical monitors:
SysRqs: Behind the scenes
All the operations performed by the user interfaces are passed through a 'system requests' (SysRqs) interface. This is a command-oriented interface such as you might use at a command line, which allows clients to communicate with the operating system. The principle is that Pyromaniac is a debugging system, and can be controlled through these operations. The user interface uses them to ensure that the operations it performs are safe and standardised - and so that it isn't poking around inside the system whilst it might be running. It provides thread safety through a defined and serialised interface.
For every feature in the user interface, there is a corresponding SysRq which provides the information or performs the operation. So, for example, the operation to read the dynamic areas list is dynamicareas-info
which returns a list of the properties of all the areas.
Another reason for this is that this interface can then be used to plug the OS into other debuggers in the future.
Testing
Possibly surprisingly, RISC OS Pyromaniac had some good opportunities for being used for testing. The Jan Vibe graphical programs provided loads of little fixes, and improvements to parts of the system that weren't quite right.
Code Coverage
One of the goals for the year was to try to push the code coverage up higher, to try to get above 70% of the code in RISC OS Pyromaniac being exercised by the integration tests. This was largely achieved within the year by attacking the components whose coverage was low and adding in tests that explicitly exercised segments of the code that hadn't been hit. Similarly, any new code that was added tried to get a high coverage for the new modules and libraries so that they pulled the average up.
There was some host-specific code in interfaces used by the Portable module, the network system and later other USB devices. These interfaces were not tested at all because they depended on the host having certain libraries installed or responding in particular ways. In general, they wouldn't work across different platforms. A new type of Python library was added called 'preloads' which were always loaded before any other configuration. These libraries could then mock the host-specific code so that the responses would look like the real thing.
For example, the Resolver module has mock code that pretends that certain hosts exist with particular parameters so that we never go out to the network. This meant that the code still used the proper interfaces and we could test that everything around it worked fine.
Eventually, with these and other tests added, the coverage reached about 71%. Whereas from 2020 to 2021 there were just over 550 tests added to the project, in the 2021 to 2022 period there were 1300 tests added. That's a huge difference in the focus and one that I'm very pleased with.
Unfortunately, towards the end of the year, a large amount of code was added to support the WxWidgets explorer and memory displays. Because WxWidgets is a massive interface and would require huge amounts to be mocked, it just isn't exercised at all at the moment. The new UI code dragged the coverage percentage back down to just shy of 69%. That's a little sad, but I'm planning to introduce some UI testing at some point - and when that happens we should almost immediately jump to the mid-70% region. So that's something to look forward to.
On the other hand, the test time for the CI runs has jumped significantly. It's pretty obvious that if the number of tests in the system has nearly doubled, the time taken to run them will as well. But still, it's becoming a bit long, with the Linux tests jumping from ~20 minutes in 2021 to ~46 minutes in 2022. And the macOS testing now takes ~81 minutes.
Obviously, these are long times and that's frustrating, so there might be some work to try to reduce the test time to something more manageable in the future.
Internet 7
The RISC OS Developments Internet module - Internet 7 - had been tested throughout the year in fits and starts. When I originally received it for beta-test there were a few problems that prevented my doing much. For a start, the modules were compressed, which is something that isn't supported by the RISC OS Pyromaniac system at present. I started looking at how the compression worked and whether I could write a decompressor for the system.
However, I quickly realised that this would take a while and I didn't really need it. In the modern RISC OS systems, it doesn't really make sense. The usual reason to compress was so that the files didn't take space on the disc, and loaded faster. With modern discs, this isn't really necessary. The only real use would be to place the module into ROM because it isn't linked statically. But that's a niche area that the modules don't fall into.
So instead I wrote a small Obey script that just uses unmodsqz
to decompress the module files. Eventually, I might get around to writing a decompressor, but this bypassed the problem neatly.
One of the earliest problems I found with the Internet module was that it used sparse dynamic areas. These were not yet implemented within the RISC OS Pyromaniac dynamic area system, so the modules failed to load. RISC OS Pyromaniac is pretty easy to modify, however, and adding some small changes to make it look like the areas were sparse was pretty easy. In reality, the areas were fully mapped, but as a quick workaround it allowed me to continue. It isn't ideal, but it means that the modules can load and I can at least test things. Eventually, they will be implemented correctly.
Another problem with the module was that it was using invalid values for its memory allocations through OS_DynamicArea
and crashing badly. This was pretty silly, and I reported it to the authors as clearly it was broken. They responded quickly, saying that they were confused because the interface they were using was correct and it was working for them on the real systems. I checked the documentation and it was indeed correct, and the RISC OS Pyromaniac dynamic area handling code was passing the wrong values back. Whilst I have tried to get the implementation right, it's easy to miss things. This case provided another nice fix to Pyromaniac due to doing this testing.
To get some of the modules to load, I needed to implement a fake ResourceFS, which could make the modules believe that they had registered files. I described this earlier - this was the reason it exists.
Of course, the fixes went the other way too. Whilst running the stack, I saw very, very slow load times for the Internet module. When it starts up, it tries to generate some random data using the CryptRandom (I created a RISC OS Pyromaniac CryptRandom module late last year so that the Internet module didn't have to load a separate module). However, it creates this 128K of data 4 bytes at a time - 32768 calls to the CryptRandom SWI to generate a word of data. This made the initialisation cripplingly slow. It was reasonably easy to show this with a dump of the SWI timings statistics, which includes the number of calls to each SWI.
Another interesting case was the number of file operations performed by some of the modules and tools. I added some new diagnostics to the filesystem so that the system could report statistics about the number of file operations that were performed. This gives us statistics in a table when the OS exits, like this (full video):
Filesystem statistics: open rdata getext Filename load close getptr $.files.hosts 0 6 6 16 16 6 $.route-test 0 1 1 0 0 0 $.files.services 0 8 8 280 280 8 $.rm.Internet 1 0 0 0 0 0 $.rm.Resolver 1 0 0 0 0 0 $.files.ipid_seed 1 0 0 0 0 0 $.resfs.Resources.Internet.Messages 0 1 1 3 4 0 $.bin.ifconfig 1 0 0 0 0 0 $.resfs.Resources.Resolver.Messages 0 1 1 3 4 0 $.bin.route 1 0 0 0 0 0
This makes it easy to see that there were a lot of accesses to the Hosts file and the Services file, which you wouldn't expect for this operation. As you can from see the statistics of what's being done, it's easy to decide where to focus attention. Using the statistics, you can see that the files were accessed more than you might expect - you might expect that once read, the services file data would be cached and wouldn't be repeatedly reopened and parsed.
Of course, you can also use these statistics to show how other operations are performed as well:
Interesting things showed up in the Resolver module, with it failing to remove its transient callbacks properly when it exited. Tracing all the SWI calls for OS_AddCallback
and OS_RemoveCallback
made it easy to see that the system had exited by the time the calls were made. Because RISC OS Pyromaniac can clear memory as RMA is freed, we can find problems more quickly, and tracing every single SWI call makes this even easier to do.
Whilst using the Internet stack with debugging enabled, there were a lot more ticker and callback events happening than I would have expected for an idle system. In some cases, the ticker events were taking so long on RISC OS Pyromaniac that they almost immediately re-triggered, resulting in almost all of the time being spent in the ticker code and only progressing the non-ticker code very slowly. To make things more manageable I added a scaler value for the ticker speed so that Ticker events could be made to occur much more slowly. This allowed some parts of the system to work better, but wasn't really a good solution.
I added new commands to list the tickers and to produce statistics about their use. This means that we can see how many ticker events are used by the system and whether they are OS_CallAfter
or OS_CallEvery
.
Some tracing of the execution found that invalid file handles were used, which was pretty easy to see in the SWI argument tracing output:
7034c84: SWI XOS_Args => r0 = &00000002 2 constant 2 r1 = &07039008 117673992 file [&00000000, &00000000, &00000000, &00010008] <= Error returned: (&000000de) Bad file handle (handle 117673992 is out of range) r2 = &00000000 0 ext
There were lots of other little comments on behaviour as well, and I made an update to the Internet tests that I had written for Pyromaniac so that they could be used by the Internet 7 modules. These are in the GitHub repository so that anyone can use them to check the behaviour does not regress.
The developers were responsive to the reports that I gave - either explaining what was going on, or fixing the problems that had been found. It was quite nice to be using the diagnostics to be able to report what was happening, and I think that it was useful to them. One of the early problems that we had was that when the module crashed, the result was a list of anonymous functions in the backtrace. This was due to not having the function name prefixes present in the module. All I could report is that 'it's 6 calls down the stack in a function that seems to do something with time'. The function names were added, and suddenly it became a lot clearer what was being done and how the code had reached that point.
Providing what I hoped was good feedback, and having a issues fixed or at least considered was a good experience all around. I'm hopeful that there will be a similar experience when the RISC OS Open Internet module is made available - it will be interesting to do testing for them as well.
OvationPro
David Pilling released the Ovation Pro sources, and a free version, this year. I wanted to play with the sources a little but never really got around to it, but I did try out the free version of Ovation Pro. As Pipedream and Fireworkz work pretty well, I was keen to see how badly Ovation Pro ran within the system.
The answer was that it failed before even starting. The !Run
file had several *Set
commands in it which were nicely lined up so that they looked tidy in the file. However, the Pyromaniac implementation of *Set
didn't trim the spaces properly, and so the value that was being assigned to the system variable was prefixed by spaces - and that didn't work at all. Small changes to the command processing for the variants of *Set
(and more tests added to exercise them) let me get a little further with running Ovation Pro.
A little further in the file, there was a problem with the OS_EvaluateExpression
algorithm not handling unary plus and minus properly. These operations were known to be broken, and there were some 'FIXME' marks around that code, but they had not been a problem up to now. Again, more fixes to the code (and tests to go with them) let me get even further with the application.
The application reports some errors during its startup, which I believe are related to some of the plugins expecting other modules to be present, but it ran. And not just that - it worked pretty well.
It shouldn't be too much of a surprise to me that it worked, but I still find it impressive. There are still problems with the caret and how some font operations happen, but the fact that there weren't huge bugs to fix is a good sign that the system is pretty accurate to the original and reliable.
LineEditor
Back in 2020, I added automated builds to the LineEditor module on GitHub. These were pretty reliable and meant that every change that was made could be built so that a version of the module might be made available automatically. There was a fix to the LineEditor source in October 2022 which caused the LineEditor module to fail to build catastrophically - the entire operating system crashed.
Not only had the machine run out of disc space fatally, but the change managed to tickle problems with the OS_Heap
implementation. Resizing the heap down by more than the current size wouldn't actually do the resize properly. Previously it would return saying that it had done so, but wouldn't update the internal heap headers. The result was an inconsistent state in the OS_Heap
header which LineEditor became confused by and crashed.
There were more tests added for OS_Heap
to check these cases of resizing. The parameters that were returned and the header details are now being checked more carefully. With these fixes made, LineEditor's automated builds worked again.
Resolver
In a rare instance of actually being directly useful for debugging, I applied RISC OS Pyromaniac to the RISC OS 5 Resolver module, to investigate a problem that a user reported in the RISC OS Open forums. Their report claimed that there were problems with the hosts
file being case-sensitive, which was curious but should have been easy to determine.
However, when I came to look at the module that was being used, it shouldn't ever have worked as it was trying to open the wrong files. Instead of trying to read InetDBase:hosts
, which is the standard local hosts file, it was trying to read /etc/hosts
. Although that is a valid filename, it isn't the name of a file that you would expect on RISC OS.
Using the debug options to turn on the OS_File
debugging gave output like this:
*resolverconfig Read catalogue info with path Filename = '/etc/hosts' Path = None DirEntry = <DirectoryEntry('/etc/hosts', 0, &00000000, &00000000, 0, &0)>
which showed that it just wasn't reading the right file at all.
It was very reassuring to see that RISC OS Pyromaniac could find the plainly broken code, but I cannot be smug about that - as the tests for the Pyromaniac Resolver module also didn't include checks whether the correct file was being read, so it was just as vulnerable to that sort of regression. Worse, the tests that did exist didn't check for case-sensitive behaviour at all. So had the user's original bug report been something I could reproduce, my Resolver would have fallen foul of that too.
I explained what I found for the RISC OS 5 Resolver module, on the forums. Then I added tests for, and fixed the problems that I found, with my own Resolver module. Although it was a small problem, it produced a few fixes for me, in addition to finding the bug in their module as well.
Where next...
Resources
-
Part 1: Introduction
An introduction to Pyromaniac. -
Part 2: Changes by month
Summary of the changes broken down by month. -
Part 3: Details (1)
In depth discussion about some quick hacks, Jan Vibe's graphical ditties, Git and others. -
Part 4: Details (2)
In depth discussion about the user interface changes and testing. -
Part 5: Details (3)
In depth discussion about the hardware interfaces. -
Part 6: Conclusion
Some final words about the year. -
Appendix: Summary of the changes
A quick summary of the changes in 2022. -
Main Pyromaniac Site
Main pyromaniac site with resources, examples and other links.