Sending the RISC OS boot messages to an I2C device
Phil Pemberton (7989) 71 posts |
I’ve been building a rack-mounted A4000 from a spare motherboard I picked up, and finally got around to building the “version 2” soft power on/off board with LCD/VFD interface this weekend. At the moment it displays a static message, but can also display text sent over I2C. I’ve written a module which listens for the “shutdown complete” service call and turns the power off – and this works great. Now I’d like to send the “RISC OS 4096K, Acorn ADFS” boot messages and the output from !Boot to the display — but I’m having trouble with this. I figured the easy way was to use OS_Claim to claim the WrchV vector, then send an I2C byte with IIC_Control every time a character is printed to the screen – but when I call the IIC_Control SWI from the vector, the machine locks up (only the cursor moves). Is there a way to do what I want? Thanks |
Rick Murray (539) 13850 posts |
I’m not an expert, but… low level VDU stuff is a bit finicky, and it may not like working in such a manner. |
Paolo Fabio Zaino (28) 1882 posts |
@ Phil Just a thought, have you tried to buffer the characters (instead of send them as soon as you receive them) and then sending them at regular intervals (or later time), maybe via call backs or setting up an OS event to trigger periodically (using OS_Byte or similar) and check if there are characters in the buffer to send over I2C? Basically avoid doing the call from the vector. Just my 0.2c |
Rick Murray (539) 13850 posts |
🤷 Am I talking to myself again? 😉 |
Stuart Swales (8827) 1357 posts |
I’d suggest that summats wrong with IIC_Control if it can’t be called from WrchV. Double-check that vector code! |
Paolo Fabio Zaino (28) 1882 posts |
Oh you should see when I am coding on RO lol ROOL may have removed the swearing from the sources, but here the old habits are still going strong! XD |
Rick Murray (539) 13850 posts |
You did remember to stash R14 around the call to IIC_Control, right? You’re in SVC mode… ;) |
Phil Pemberton (7989) 71 posts |
Addressing all of these …
That was my first idea, but I figured I could send from the vector to start with, then add the buffering later.
I’m pretty sure it’s right:
Saved it right at the start of the function! |
Stuart Swales (8827) 1357 posts |
Why buffer at all?
You meant ADD R1, R12, #w_txbuf surely? I would paranoically clear V after the XIIC_Control too. |
Phil Pemberton (7989) 71 posts |
I2C packet overheads. Each packet needs a start sequence, addressing byte, and stop sequence. For a single-byte transfer, that’s slightly under 50% efficiency – which improves the more bytes you send. Also buffering the data from WrchV means it’ll return faster, and the I2C transaction can be done somewhere less time critical (e.g. some kind of timer tick vector)
I tried that, and it earned me a “Bad immediate constant” error … and now I’ve just realised why that doesn’t work. I borrowed a chunk of code to allocate the workspace from a post by Steve Fryatt (https://www.riscosopen.org/forum/forums/11/topics/16298?page=3#posts-119636) and it’s got a bug which makes it return wrong addresses if you allocate a 4-byte block, then a 64-byte one like I did. I was going to post a bit about the wrapper code from Chapter 10 “Interrupts” under “Calling SWIs” (http://www.riscos.com/support/developers/prm/interrupts.html) but it seems that’s unnecessary. Now I’m getting the text sent to the display perfectly at the F12 prompt, but when the WIMP is running it prints absolute heaps of garbage when I interact with the desktop. Ideally I need to filter out the noise from the WIMP and only display what’s printed at the star-prompt. … It also slows the desktop to an absolute crawl, but that’s not a huge surprise given how much noise it’s printing when I move windows around. |
Rick Murray (539) 13850 posts |
Yup, it’s amazing how much UI stuff goes through WrchV. It turns up in Spool files as well.
What comes to mind is to hammer OS_Byte 1. Specifically OS_Byte 117, which will return the VDU status flags in R1, and if bit 5 is set then you’re in VDU 5 mode. In that case, probably best to just discard the data as I can’t imagine stuff printed in VDU5 mode would make much sense. Obviously this won’t work with TaskWindows (but then mine use ZapRedraw so that isn’t even an issue). 1 Even that will be faster than spewing unwanted junk through IIC. |
Stuart Swales (8827) 1357 posts |
If actually gets worse if you sit on WrchV! OS_Plot has to send the VDU sequence, etc. |
Phil Pemberton (7989) 71 posts |
I thought I had it working, but it seems like it works intermittently. Sometimes it crashes, sometimes it runs and works. I can’t figure out the pattern. Would anyone mind taking a look at this and seeing if any errors stand out? I’m happy for suggestions for a better way to do this — I started implementing buffering (there are some placeholders for this) but stopped when I couldn’t think of somewhere to print out the contents of the buffer.
Also, DarkLord on the Stardot Discord has provided a better implementation of FNworkspace which aligns to 4 bytes:
|
Rick Murray (539) 13850 posts |
870 ; Save the workspace 880 STR R2, [R12] 890 900 ; Make R12 point to the workspace 910 MOV R12, R2 Aren’t these two back to front? |
Stuart Swales (8827) 1357 posts |
You will of course get R0=4 and R0=5 in the middle of other VDU sequences! If you’re going to do it this way, you’d have to have a table of VDU control sequence lengths so you can skip that number of subsequent values. |
Simon Willcocks (1499) 519 posts |
At a glance, that looks OK; the allocated address gets stored in the private word, then r12 becomes the wsptr. |
Rick Murray (539) 13850 posts |
That’s why I said to spam OS_Byte 117 to read the VDU5 status, it saves having to try to make assumptions about what passes through WrchV. |
Phil Pemberton (7989) 71 posts |
I’ve changed the code to hit OS_Byte 117 … and it seems like that crashes it too! After bisecting it, I found that ending the vector with:
crashes the machine. The fix is to change it to:
Which restores LR then returns, clearing the V-bit as part of the return. Still two instructions but no crash. Doing that and adding code to check OS_Byte 117 seems to be doing mostly what I need, but for two issues:
I think I need to have a ponder about how to buffer the incoming characters and send them in larger blocks. The obvious answer is to do the send in WrchV, but that just means it’ll delay at the end of the line, or whenever the buffer fills up. I’d rather do it somewhere less time-critical. |
nemo (145) 2552 posts |
Sorry I didn’t notice this earlier. There are many problems.
If you want to filter out VDU sequences (OS_Plots, graphics and text windows, cursor movements, clear screens, beeps etc) then you need to do a bit more work. It’s not just the VDU5 state that you need to look at – crucially you need to check the VDUQueueDepth read via OS_Byte,218,0,-1. So read VDU Status and if that says VDU4 (if that’s what you want) then read the VDUQueueDepth and if that’s zero you can redirect your byte if it’s a printable (and maybe LF) but you’ll probably also want to map VDU9 to space and perhaps even be clever about 8 and 127 if you’re buffering. Finally, WrchV doesn’t need flags preserved, so to cope with a SWI returning V just do So you’ll want to do something more like:
Which you’ll notice uses no workspace and is CPU agnostic. The Knuthian Defence applies – proceed with caution. [Yes, UTF-16 is a RISC OS Alphabet. Yes it means R0 isn’t a byte in WrchV. Yes there’s a way to cope in general. I’ll write that up elsewhere.] |
nemo (145) 2552 posts |
You may be thinking that this is a lot of stuff to do to work out if you should be doing something with WrchV, and you’d be right. eg all of these have to do this kind of thing: It’s almost like VDUStatus and VDUQueueDepth should have been part of the WrchV API! Those of you with sharp eyes will note that some of those modules have set their R12 sensibly. RO5 broke the API for reading the MOSVars address (fix module on my site), but the VDU Workspace must be inferred from what you are allowed to read – an exercise for the reader. |
nemo (145) 2552 posts |
To cope with Alphabets that aren’t 8bit, use Service_International,332: I wrote about the complexities of the ill-defined WrchV in the ReadMe for my ArthurASCII module:
The Kernel WrchV handler treats R0 as 8bit and simply ignores the rest. This was correct (but undefined) for the Alphabets available in 1987. However, it is not correct in general, and is certainly wrong for Alphabet 117 – “UTF16”. For the record, WrchV claimants MUST preserve R0 for their caller; IGNORE extraneous bits when recognising codepoints; and NEED NOT pass those on to the vector tail. Out-of-band codes such as -1 MUST NOT be special-cased, as there may one day be a fully 32bit Alphabet for which this is a valid codepoint. For stateful Alphabets such as UTF-8, control codes 0 and 27 are DEFINED to act as flush and reset respectively without other side-effects (ie 192,0 will treat 192 as a Fallback character, whereas 192,27 discards the 192 undisplayed). It is intolerable having to read and check the current Alphabet number for every character code that passes down WrchV (though it is strictly necessary for correct character code interpretation), so some code intercepts ByteV in order to spot Alphabet changes to cache locally. In light of this inefficient API, the UTF8Alphabet module introduced an Alphabet Change Service (&43,&10C) to push Alphabet (and Fallback) changes to interested modules. This module relies on that API to spot changes to the Alphabet after initialisation. Having said that, I advise against the use of UTF-16 as an Alphabet because it has significant implications for many APIs, not least OS_ReadC and ReadLineV. Having OS_ASCII recognise CR correctly when in UTF-16 is therefore an exercise in errant pedantry – I know you would expect no less. |
Rick Murray (539) 13850 posts |
Good example – the Wimp and how it deals with function keys and the way that UTF-8 is sent as multiple keypresses instead of just a word-sized value.
Thank <Deity$Name> that something now broadcasts a Service Call for this now. |
nemo (145) 2552 posts |
A better example than you think, because originally
If it were a word-sized value it wouldn’t be UTF-8. It would just be Unicode. Which is what it ought to be for Alphabet 118 “Unicode” (unofficial – I’m just not cruel enough it appears). So you may find it awkward, but it’s actually correct that a single “character” be delivered over multiple Wimp_Poll events, eg ‘🏴’ is 28 events, and ‘👩🏼👩🏼👧🏼👦🏼’ is 41 in UTF-8, and they’d still be 7 and 11 events respectively if delivered as 21b Unicode (whether they display as single “characters” depends on font support in your device).
Just call me nemo. |
nemo (145) 2552 posts |
BTW, the biggest problem with dealing with 41 (or whatever) KeyPress events isn’t the stitching the bytes together, but that the entire sequence is probably atomic (as in the above cases), so one should either insert all of it or none of it – only inserting the first n bytes that fit in the writeable (for example) will result in broken sequences and, if one’s display system supports Fallback, unintended characters (mojibake). This is why I coined the StyledText message, which carries not only a text fragment but also a “Font Name” (in the API sense – it doesn’t have to contain a Typeface name). Here’s the Emoji Panel using StyledText to deliver an emoji to an application that implements the message to ensure the content is treated atomically. Then if there isn’t room (or the encoding is inappropriate, whatever) it can complain with a tooltip: If StyledText isn’t supported, the EmojiPanel has to guess whether the sequence will fit, which isn’t so good. [StyledText is 25 years old, but I wish I’d put the fontname first — it can’t be used with UTF-16 for example because of the termination of the first string. D’oh. Not that “\EUTF16” does anything sensible anyway thanks to the frankly insane behaviour for “\EUTF8”, but that’s another story] |