Calling C code from vectors
Dave Higton (1515) 3526 posts |
I would like a module, written in C, to have some code called regularly and frequently. This suggests using either OS_CallEvery, or hanging on the Ticker vector. (I suspect the latter, as it’s dubious whether 20 ms is fast enough.) But is either of these possible from pure C, or is an assember shim required? I can’t see how to meet the entry requirements in C, e.g. the value of R12, the stack frame, the variables, etc. If here are examples to point me at in the RISC OS sources, I would be grateful. The applications I have in mind are joysticks, gamepads, graphics tablets and Bluetooth dongles. |
Rick Murray (539) 13840 posts |
Pure C will work. Look at CMHG. If you CallEvery or Claim a vector, you will get a workspace pointer in your module init routine. Pass that as the value to R12. The CMHG defines two entry points for a vector handler. One is called “blahblah” (you can name it) and the other is called “blahblah_handler”. The first (define it external in a header) is generated by CMHG. You pass this as the routine to call on the callback or event. This will set up the C environment and then call your C function, the one with the _handler suffix. Sorry this is vague (and probably incorrect). I’m sitting in bed wondering why I can’t sleep. ;-) It is however, all described in the C/C++ user guide under CMHG (though the examples are about as much gibberish as this post is!). If you don’t figure it out for yourself, prod me over the weekend and I’ll copy’paste some code. Scheduling callbacks is useful. Just wish there was a systick that was faster than a centisecond. |
Dave Higton (1515) 3526 posts |
Agreed. Quite a few USB devices want servicing more frequently than that. Though the simple way is to declare a buffer big enough for several transactions, and, every centisecond, deal with all the transactions that have happened in that centisecond courtesy of the USB stack. |
Rick Murray (539) 13840 posts |
Mmm, let’s put it like this – the centisecond tick was too slow for MIDI in ‘89. It is exactly a quarter century later and the OS still doesn’t offer anything faster itself… Let me know how you get on with your vector handler. |
Dave Higton (1515) 3526 posts |
Well, I’ve managed to hook into TickerV. All I’m doing is incrementing an int so that I can see that it’s working. One thing I don’t understand: I had to insert an extern declaration for the veneer, otherwise it wouldn’t link, but I don’t see any such declarations in the examples from the RISC OS sources that I have (briefly) looked at. Any suggestions as to what I might be doing differently would be welcomed. The next question is what exactky to do on each tick. In many cases there won’t be any new input. One thing I could do is to look whether there is new input, and add a callback if there is. This would solve the problem of the processing on the tick taking a long time, which the PRM says is an issue (perhaps not a major one, I don’t know). But how long might elapse before the callback gets called? I have no idea how timely it is. |
Jeffrey Lee (213) 6048 posts |
If you use CMHG to generate a header then the veneer will be extern’d for you in there (the OS sources will all be using CMHG generated headers)
How much processing do you need to do? If you just need to read from a (non-empty) USB buffer and stuff it into a data structure somewhere then that should be fine. It’s only if you need to do something which will block (e.g. wait for a USB transfer to complete) that performing the processing in a callback might be required. Callbacks are generally pretty timely, but there might be some situations under which they’re delayed for “long” periods of time. |
Rick Murray (539) 13840 posts |
As Jeffrey said, the function is a part of the CMHG code chunk – you’re only defining it so you can compile your code without error. ;-) I do stuff on a centisecond tick. Do you know how many instructions are executed in 100th sec at a ~700MHz clock rate? Provided you don’t wait for something, it should be okay. If responsiveness is less important, schedule a callback. Even if it takes 10cs, would the average user notice? |
Dave Higton (1515) 3526 posts |
I am using CMHG, which makes the veneer visible globally; but my code that wants to call it needs to have an extern declaration so that it knows to look for the veneer outside itself. Or maybe I’m misunderstanding what you’re saying.
That’s the bit that I hadn’t thought about until lunchtime today. There’s a call to OS_Args 2 to see if any bytes are available, then, if they are, a call to OS_GBPB 4 to get them (8 bytes per update). Then it’s minimal arithmetic and… that’s all. Shouldn’t take many microseconds. I now have a GM-1300 Joystick module working from TickerV, and I can drive the RC car around with it. I can apply the same techniques to a USBSerial_FTDI module that I last played with a year ago, and a module for the cheap graphics tablet that I bought years ago. Thanks for the help, guys, it’s been invaluable. |
Jeffrey Lee (213) 6048 posts |
If you use CMHG to generate a header then the veneer will be extern’d for you in there There are two files CMHG can output – an object file containing the module header + veneer code (use |
Rick Murray (539) 13840 posts |
To elaborate on what Jeffrey said (I posted that from my phone, hence the terseness)… C will not compile anything until it is happy that all references have been satisfied. Therefore… The object file created by CMHG contains a number of things (such as the module header and entry points, plus the veneer code to convert a system call environment into a C-style environment). Which is why you have the extern definition. It is “extern” to satisfy C – anything that is not in the current file is “extern”. In this case it is in a prebuild object file (the one created by CMHG), but if not, it would be the same for object files built from other C sources. To give an example to (hopefully!) make this clearer – my MIDI module contains three C sources which create four object files (the 4th is the CMHG one). It is all symbiotic, everything depends on everything else being present. It could be stuck into one source, but splitting it up makes things more logical. To play a note, you call SWI MIDI_PlayNote (IIRC!). The SWI call is picked up by the CMHG code and transformed into a call to the wrapper code that deals with SWI dispatch. The big switch() block will notice what SWI is being called, and pass the information over to the note playing function in the midi code, which will look at what is wanted to be played and construct the appropriate data to generate a MIDI Tx packet, which is then passed to the usb code to send. So it works like this: User app → SWI (CMHG) → SWI (wrapper) → playnote (midi) → note data (usb) → keyboard makes a sound All of the functions not in the current bit of code being compiled need to be defined as “extern”. Luckily C will accept having everything defined as extern. It isn’t entirely legal, but it works, so I can just have the one global header that defines all of the functions (and #define values) and just #include that into each source. Makes life simpler.
I do more than that each MIDI time, doesn’t clobber the system.
Ooooh! YouTube video?
If I can find my serial module, it would be interesting to try it. It was a dirt-cheap Chinese clone and the official driver hated it. I managed to bodge it into loading, but it just kept returning junk data. I don’t even know if I still have the thing. Somebody got it for me, cost them about two pounds (inc. carrier pidgeon).
I have one of them (well, I have the tablet, not sure where the pen is – needed some ridiculously tiny (AAAA) battery). Wonder if it’d work on RISC OS? Do you know the “pressure” API? The last time I looked at PhotoDesk, there was a configuration for a pressure sensitive tablet, though I don’t know how this is implemented. |
Dave Higton (1515) 3526 posts |
I’m pleased to be able to report that the Nisis/Aiptek graphics tablet module is well on its way. I have movement of the pointer. I know how to handle the F-keys and the buttons, so it’s only a matter of more handle-cranking to get them done. The pressure feels non-linear. The pad outputs a value in the range 0 to 511, but 256 feels like way less than half the pressure required for 511. I have seen the pressure API, and I probably have electronic copy somewhere. |
Rick Murray (539) 13840 posts |
Could be a logarithmic pressure – to give more finesse on lighter touches. |
Dave Higton (1515) 3526 posts |
I’m not going to pursue the Nisis/Aiptek module after all. Further experimentation has shown that it is impossible, on my T-6000U at least, to tell the state of the pen’s buttons. |