OS_ReadLine, OS_ReadLine32 and ReadLineV, a history
nemo (145) 2546 posts |
Why are even the simplest things complicated? I happened to look at the documentation for OS_ReadLine, which I thought looked wrong. I checked the other place, and it looked wrong there too. It’s a long time since I’ve seen a physical PRM, so maybe it’s always been wrong everywhere. Anyway, the situation has got sufficiently complicated that I thought I’d investigate properly, so for your initial pleasure and gradually increasing dread, I present: A history of OS_ReadLine, ReadLineV and OS_ReadLine32 |
||||||||||||||||||||||
nemo (145) 2546 posts |
The question I had asked myself was
There isn’t one. “See OS_ReadLine” it says. That’s not good enough. That claims that R0 and R2-R3 are corrupted. I know they’re not, but to make sure I wasn’t misremembering or looking at an implementation detail, I have gone through every version of ReadLine back to the ARM evaluation board. Strap in, it gets bumpy. |
||||||||||||||||||||||
nemo (145) 2546 posts |
ARM Evaluation Board OS 0.30 => R0 unaligned buffer (32bit) R1 length R2 min chr code R3 max chr code <= R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 This is where it started, and it’s all perfectly reasonable. Then Arthur messed it up… |
||||||||||||||||||||||
nemo (145) 2546 posts |
Arthur 0.30, and everything up to RISC OS 4.23 => R0 unaligned buffer (26bit) + flags b31,b30 (b26-29 are reserved) R1 length R2 min chr code R3 max chr code R4 password chr code IFF R0b30 <= R0 buffer WITHOUT flags R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 The desire to suppress the display of chrs outside the acceptable range (b31) and the addition of a password chr (b30) required flags adding, in particular to signal that R4 was not to be ignored. WHY weren’t they added to R1, R2 or R3, all of which have oodles of spare bits? This is the cause of all the ensuing difficulty. Important: From 0.30 to 4.23, R0 is a 26bit address with two flags. |
||||||||||||||||||||||
nemo (145) 2546 posts |
RISC OS 4.24 and later 4.xx => R0 unaligned buffer (30bit) + flags b31,b30 (b26-29 are reserved) R1 length R2 min chr code R3 max chr code R4 password chr code IFF R0b30 <= R0 buffer WITHOUT flags R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 Increasing memory sizes result in the address expanding to use 30bits of R0. |
||||||||||||||||||||||
nemo (145) 2546 posts |
RISC OS 6.20 (and earlier 6.xx probably ) => R0 buffer [(26bit) + flags b31,b30] IFF ‘old API’, else 32bit R1 length (31bit) + *‘new API’ flag in b31* R2 min chr code R3 max chr code R4 password chr code IFF (‘new API’ OR R0b30) + (flags IFF ‘new API’) <= R0 buffer WITHOUT flags R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 OS_ReadLine32 => R0 buffer (32bit) R1 length (31bit) (b31 superfluous but not cleared) R2 min chr code R3 max chr code R4 password chr code (b0-7) + flags b31,b30 *(b8-25 are reserved)* <= R0 buffer R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 This is now properly complicated, and as far as I can tell, completely undocumented. Despite that, I think it’s nearly the right approach: The introduction of OS_ReadLine32 raises an important ambiguity – OS_ReadLine and OS_ReadLine32 are different APIs, so what is the API of ReadLineV? This change introduces a flag into R1 that documents which API is being used on the vector (and indeed via OS_ReadLine, which can now be operated in 32bit mode). Otherwise it is impossible to interpret R0 – is it a high address with flags in R4, or is it a low address with flags in the top bits? Using OS_ReadLine32 causes that bit to be set if its parameters are not compatible with the ‘old API’ – and this includes setting any of the reserved bits in R4 or calling it in 32bit mode. However, use of OS_ReadLine32, and hence the ‘new API’ bit, is NOT compatible with earlier handlers, which will interpret it as a negative max length and hence not allow anything to be typed, or will treat it as a HUGE unsigned length and allow the buffer to be overrun. NOT GOOD. A subtle difference concerns the password code in R4 – to date it had always been passed down ReadLineV unchanged, but OS_ReadLine32 reduces it to 8bits (This affects the upcoming UTF-8 support). Bizarrely, when in ‘old API’ mode, the address reverts to being 26bit instead of the 30bit introduced in the previous API version. Handlers written against this version of the API will work in previous versions. Earlier handlers will choke on the ‘new API’ bit. Callers that do not use OS_ReadLine32 or the ‘new API’ bit are compatible with earlier versions. And then there’s RO5… |
||||||||||||||||||||||
nemo (145) 2546 posts |
RISC OS 5.19 (I’ve not checked later versions yet) => R0 buffer (32bit) R1 length R2 min chr code R3 max chr code R4 password chr code + (flags IFF ‘new API’) <= R0 buffer R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 OS_ReadLine NOTE! => R0 buffer (32bit) *NOTE!* R1 length R2 min chr code R3 max chr code R4 ignored *NOTE!* <= R0 buffer R1 bytes read other regs PRESERVED C Escape pressed V Error, report in R0 RO5 takes the (defensible but undocumented) decision to change the API of ReadLineV. As of RO5 it is now identical to OS_ReadLine32. Sadly OS_ReadLine is a mess. The fear that an inadverdent high address in R0 would be mistaken for flags results in actual flags in R0 being mistaken for an address. This might cause an exception, or it might cause high memory to be written by the ReadLineV handler that is running in SVC mode. Since there are no flags, there’s no way of knowing if R4 is useful, so it is always set to 0 before calling ReadLineV. Passwords are therefore broken. The RO6 ‘new API’ bit is not supported (who knew?!), and it is now not possible to write a single handler that works on all versions of ReadLineV. |
||||||||||||||||||||||
nemo (145) 2546 posts |
Conclusions
|
||||||||||||||||||||||
nemo (145) 2546 posts |
Olly Betts’ LineEditor module Probably the most significant ReadLineV handler outside the OS, this maintains the API I describe above (as of the RO4 version), preserving all other regs. The one difference is that it also preserves the flags in R0 on exit, whereas the OS does not. This is a mistake. The fact that Olly went to the trouble of duplicating the undocumented behaviour really does suggest it should not be undocumented. |
||||||||||||||||||||||
Steve Pampling (1551) 8170 posts |
703 “more important things”…
I believe code can be enclosed with a dot bc and a dot p although use of the textile interface tends to be akin to using The Force in being amazing when it works. 1 "Indeed. As part of the team that created the print system for Windows 7 and later, " :) |
||||||||||||||||||||||
Chris Mahoney (1684) 2165 posts |
Conversely I don’t know how to make it double-space in the first place! But <pre><code> might be what you need if plain old <pre> doesn’t do it. |
||||||||||||||||||||||
Colin (478) 2433 posts |
If the code doesn’t have a blank line then at the beginning of the line you put
If the code has blank lines you put.
Like this. Note bc and p must be at the beginning of a paragraph ie at the beginning of a line and with a blank line above – if it’s not the first paragraph. |
||||||||||||||||||||||
nemo (145) 2546 posts |
It turns out that failing to leave a blank line before |
||||||||||||||||||||||
Rick Murray (539) 13840 posts |
The list of Textile oddities is long and varied. |
||||||||||||||||||||||
Rick Murray (539) 13840 posts |
Can anybody explain why two different (albeit related) SWIs with differing parameters are filtered through the same vector? If it was that simple we’d have simply patched a magic word or two into the original OS_ReadLine SWI. My personal opinion here is to forget Six. It’s different in an odd way (one of the joys of two implementations going their own way) and isn’t going to be developed any further, and as you note, apparently undocumented. |
||||||||||||||||||||||
nemo (145) 2546 posts |
My point, ill made amongst the information overload, is that if you’re going to change an API, at least change it in a way that can be detected. That way you only break things once, temporarily.
Note that it is not sufficient to check whether you are running in 32bit mode, that’s orthogonal. |
||||||||||||||||||||||
Rick Murray (539) 13840 posts |
Or, better yet, document the damn API and which OS version this applies from. Of course, this is one of the reasons SIX p**ses me off. Things get tricky when working with OS version numbering because some twat wanted to make a “statement”. :-/ But, mostly, document it.
My usual way of detecting RISC OS 5-like, that is hopefully future proof, is XOS_ReadSysInfo 8, looking for a reply of 5. |
||||||||||||||||||||||
nemo (145) 2546 posts |
Oh bother. I’ve updated the documentation accordingly, but the OS_ReadLine32 documentation reminds me that:
So, there’s no way of detecting which version of the API is being used on ReadLineV. That’s completely broken. OS_ReadLine32 needs to change the way it calls ReadLineV – either by calling a new vector (ReadLine32V) or by adding a new flag, as Six did. |
||||||||||||||||||||||
nemo (145) 2546 posts |
Rick ranted
I want to make this completely clear: This is not SIX’s fault, this is RO5’s fault. Thanks to RO5’s change of the ReadLineV API, with the CallASWI module retrofitting that change to earlier OSes, it is not possible to detect whether a call to ReadLineV has come from OS_ReadLine or OS_ReadLine32 (in general) and hence it is not possible to work out which version of the API is in use. Since the functionality of this API call is to cause stuff to be written to memory and since it is not possible to detect what that address is intended to be, the ReadLineV API is completely broken. |
||||||||||||||||||||||
edwardx (1628) 37 posts |
This is documented. The public RO6 documentation was released shortly before the first version of RO6, and hasn’t been updated to cover later releases. Given that, I think we can conclude it’s in all versions of RO6.
This was also documented, but has subsequently fallen off the web. web archive
The RO6 documentation includes example code to do just this. Looks fine to me. |
||||||||||||||||||||||
nemo (145) 2546 posts |
edwardx found
Marvellous. I didn’t find that when I looked.
And will work fine on RO6, RO4 and all earlier versions… as long as CallASWI isn’t in use. For example, if CallASWI is in use on RO4 and OS_ReadLine32 is called, then the ‘new’ API is in use, but there’s no R1 flag to indicate it. Equally, if code attempts to use that bit on RO5, the call will fail to input anything. All in all, I think the SIX solution to this problem is pretty good, and should be adopted. |
||||||||||||||||||||||
Rick Murray (539) 13840 posts |
Slow handclap. :-) Would a flag work, or would the differing data potentially confuse older users of ReadLineV1? Maybe ReadLine32V might be more logical, and stop older software touching stuff it clearly cannot cope with2. 1 That it’s taken until now for this to be noticed3 makes me think that it’s not oft used.3 No excuse, but still… 2 Like earlier versions of DOSFS that would be happy to mount, and trash, volumes too large for it. 3 Like the obscure little never-used SWI that I found a bug in: https://www.riscosopen.org/viewer/view/castle/RiscOS/Sources/Kernel/s/Middle?only_with_tag=Kernel-5_79 |
||||||||||||||||||||||
nemo (145) 2546 posts |
Rick bugged
Oh that’s nothing. RO4.39 (very common) literally has the end of the BreakPt handler missing, so instead of calling OS_Exit as it should, it stomps off into a text message. Collateral damage from the Module All The Things nonsense.
Yes. And it ought to be Six’s flag, because it’s as good a solution as any and it already exists. Maybe someone has even used it. Maybe. As for confusing existing code… only if the flag is used (and it can be suppressed when unnecessary), and then it’s a 50:50 whether it causes nothing to be input (safe) or allows far too much to be input (unlikely). |
||||||||||||||||||||||
Rick Murray (539) 13840 posts |
It looks to me, from a brief reading, that the kernels with OS_ReadLine32 will always call the 32 bit incarnation of ReadLineV; therefore:
This means you ought to have two scenarios:
It isn’t ideal, certainly, and the ROLtd way of actually specifying the API in use is questionable – note that bit 31 is set on a 26 bit system to indicate full addressing is in use, it is not set on a 32 bit system as the API is always specified as 32 bit (the same as RO5). Why bother setting a bit here? If it is always 32 bit on a 32 bit system, then R0 is only an address on a 32 bit machine. And as for a 26 bit system, an address greater than 64MB is invalid, so those bits being set are clearly flags. That’s my interpretation of the specs linked by edwardx. |
||||||||||||||||||||||
nemo (145) 2546 posts |
Update. The CallASWI module allows OS_ReadLine32 to be used on older OSes. It calls ReadLineV (on that older OS) with the original API, erroring if given a high address. So, up to and including RO4, ReadLineV has the original OS_ReadLine API.
Incorrect. R1b31 (on ReadLineV) indicates the OS_ReadLine32 API is in use, otherwise the OS_ReadLine API is in use. That is its purpose. There are many more than two scenarios:
Except it might not be. Are you asserting that b30+valid_RAM_address, b31+valid_RAM_address and b31+b30+valid_RAM_address are always invalid addresses? I don’t think that’s true. |