Complete C standard library
Posté le 19/08/2018 19:31
Motivation
Until now there was no complete C standard library (aka
libc) available for the Casio calculators. Although some parts of this library have been provided by fxlib and
gint, there was no libc implementation complying with the standard and compatible with the sh3eb architecture ready to use.
To change that, I decided to port
newlib to the Casio CPU. Newlib is an alternative libc implementation intended for use on embedded systems.
Alpha
Follow this link and click the download button in the top right corner:
>>> v1.1 <<<
Instructions on how to install newlib alongside with gcc (big shout-out to Lephé):
Compiler sous Linux avec GCC
Features for Casio fx9860g calculators:
* C standard library
libc
→
printf implementation to print text to the display
→ Dynamic allocation of memory using
malloc and
free
→ Memory manipulation using
memcpy,
memcmp,
memset etc.
→ String manipulation using
strcpy,
strcmp,
strstr,
strtok
→ ...
* Math library
libm
→ Floating point arithmetics
→ ...
* Automatic library and include path recognition after installation
* Basic Casio features:
→ implementation of
GetKey,
Bdisp_AllClr_DDVRAM,
Bdisp_PutDisp_DD,
Print and
locate without fxlib (but you can use it if you want)
Code
To contribute or get all those bleeding edge features, see the code including all further information:
libc (my GitLab repository)
The project you find in my repository is a fork of
the official newlib repository. To make it easier for everyone to follow, I try to keep a clean
git history. That means that all my changes are located on a dedicated branch with meaningful commits.
I also try to keep the changes to the upstream library minimal. That increases maintainability a lot.
Contributing
If you have a ideas, feature request, found a bug or simply want to contribute, just send me a message and you're in! You can also create
Issues and
Merge Requests directly in the repository. As in every OpenSource project: merge requests welcome!
Citer : Posté le 04/09/2018 00:15 | #
Ok, so what I formulated in 2) was not quite correct. What I wanted to say is:
Halting the processor is very suboptimal since it produces all the problems you described. It's only of use if you really need to save energy.
There are some justified applications such as:
setTimerTo(5000); // 5000 ms
sleep();
However, usually, if you need to work with interrupts, you would choose busy waiting:
while (1) {
setTimerTo(1000);
// do sth
while (!timeIsUp); // busy waiting (instead of sleep)
// do sth else
}
Citer : Posté le 04/09/2018 08:10 | #
Oh, I see. Sleeping and separating the clock signal from the processor were the same thing for you (which may be factual, I don't know the details tbh).
Well, if you need a few figures to be convinced of the benefits of sleeping; I once wrote a simple clock application (here), it was at a time when no SH4 clock was available. This one would busily wait for the RTC to tick at each second; and it would exhaust the calculator's batteries in a few hours' time.
Though I have not found a proof in the code, I am fairly sure that GetKey() sleeps. This function is what runs 95% of the time when using the system apps and the longevity of the batteries is already a very strong hint.
-
There are other solutions to the "sleeping problem". For example, if you stop considering that there is only one interrupt, or that you can only disable all interrupts at once, you can do the following:
1. Consider two timers regular (which you want to sleep for) and timeout (a helper timer). At the beginning regular is set up with a user-chosen delay and its interrupt is enabled; timeout is disabled.
2. The first code extract from my article is used.
3. Before checking the condition, disable the interrupt for regular.
4. If the condition is false, leave; regular has been disabled automatically already.
5. If the condition holds, configure timeout to fire once, very quickly (≤ 1 ms).
6. Go to sleep.
7. After a short delay, timeout fires; use this to enable the interrupt for regular.
8. Now you're sleeping with the interrupt enabled.
This works only if the interrupt for timeout does not wake up the userland program from its sleep. If think it can be configured to depend on the interrupt priority. I would need to check.
Citer : Posté le 04/09/2018 17:27 | #
Oh, I see. Sleeping and separating the clock signal from the processor were the same thing for you (which may be factual, I don't know the details tbh)
They are definitely not the same and I was just plain wrong.
Though I have not found a proof in the code, I am fairly sure that GetKey() sleeps. This function is what runs 95% of the time when using the system apps and the longevity of the batteries is already a very strong hint.
I did not expect that and in this case I take back my argument.
It would be fun discussing this topic in a chat. Apropos, is there a chat beside general and discussion?
Citer : Posté le 04/09/2018 19:14 | #
No, there's not; in fact the separation between general and discussion is already recent.
Now that you mention it, there was actually a funny situation with more channels before I set up special urls like /shoutbox/general and /shoutbox/discussion; back then the url was on the form /shoutbox/?channel=x, and any x worked for the script because it served as a channel indicator in the database. When Xavier59 asked whether there were more channels, I said "no" without suspecting a thing, and he quickly proved me wrong by sending messages to a hidden channel no. 63057 whose existence I had not even planned. xD
Since it would be an interesting discussion, you'll want it to be archived, so the chat is not a very good idea. The forum should be more suitable for that, don't you agree?
Citer : Posté le 04/09/2018 19:51 | #
Well, I think sometimes it is easier to find out more about a certain topic if a topic is so new, that you do not even know really what questions to ask.
Ajouté le 04/09/2018 à 22:15 :
For example I have a question about the interrupts:
So I imagine there is an interrupt routine for each interrupt which is executed by the CPU even if it was halted before. What happens afterwards? Does the CPU go to sleep again? Can the interrupt know if the CPU was sleeping or not?
Citer : Posté le 04/09/2018 22:27 | #
I got to check it and you're nowhere close to wrong! This is in fact as good as you can get: documentation says "Sleep mode: Supply of the clock to the CPU core is stopped.".
So when I suggested you may not wake up from that state, I was actually plain wrong.
There is only one interrupt routine for all interrupt types; usually this master routine branches to some specialized interrupt handler by checking the INTEVT or INTEVT2 register that uniquely identifies interrupt sources. So from the point of view of the hardware there is only one ISR.
It depends on how deep the chip is sleeping. In normal sleep mode where the CPU clock is halted, any interrupt or reset will cause the processor to restart execution. In complete standby mode (something like hibernation), you need a reset or one of a few select interrupts such as NMI or keyboard. In our clock configuration, the deepest U-standby mode can only be exited by a reset.
I don't know of a way.
Citer : Posté le 05/09/2018 17:33 | #
That's actually very interesting! Since there is no official documentation, where can I find information about this topic?
Citer : Posté le 05/09/2018 17:39 | #
Have a look at the SH7724 documentation. You can conveniently find a copy in TeamFX's gathering of doc, which includes MPU, RAM, Flash, display, MoBo photos, and more. This reference has been called "TeamFX's bible" for quite some time, which is why the following link is to bible.planet-casio.com. Look in the mpu directory.
https://bible.planet-casio.com/hardware/
Now there are other directories and contributed documentation in there, mainly sorted by contributor, don't hesitate to have a look if something piques your interest.
Citer : Posté le 05/09/2018 18:04 | #
Wow, that is quite some information, thanks!
Ajouté le 05/09/2018 à 20:07 :
Implementing scanf is much harder than I thought... not because the stdlib is so complicated, but because this calculator has too few keys! ...Oh and because the fxlib syscall is really not what I wished it was (key combinations e.g. SHIFT+5)
Citer : Posté le 05/09/2018 20:34 | #
What could be the problem with the keys? You mean not enough keys to input various ASCII characters to the virtual terminal? Well, you have the SHIFT and ALPHA modifiers for this purpose.
Could you explain more precisely what you wished and what it is? Maybe I can point you to something more appropriate.
Citer : Posté le 05/09/2018 20:38 | #
If you want to add keys to the keyboard (shift+5 as you said, for example) then indeed, GetKey() is not enough. Though I don't know why you'd want to bypass GetKey(), it allows entering all necessary characters (and you can make a [CHAR] menu on F6 for other characters).
What I do with CasioPy is using PutKey(OPTN) to reset the modifier, then keep track of modifiers myself. See https://github.com/Zezombye/casiopy/blob/8330d4de3ba3647b073885d863b3f919ab8b4082/ports/minimal/console.c#L73
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 05/09/2018 20:40 | #
I don't know if you own a Graph 75+ Zezombye, but on this machine SHIFT + OPTN toggles the backlight so you might want to use a key that doesn't have a special behavior on SHIFT... except there's none. >_<
Citer : Posté le 05/09/2018 21:07 | #
I actually i have two problems:
1) The technical problem is that I want to use combinations like SHIFT+5 or SHIFT+7 which can not be used via GetKey(). [@Zezombye] Your PutKey() hack is actually a good idea! That might enable me to track all combinations.
2) Another practical problem is that I basically want to provide an intuitive and well usable way of entering all common ascii chars. This is very hard for chars which are not written on the keyboard (especially lower case letters and special symbols: !"§$%&/()=?,\n,\r,\t,...)
The menu for special symbols is a great idea, however I have a very generic function (scanf) and basically no way of explaining to the user, that F6 opens the special menu they need. I consider using the key CATALOG or List, but to be honest I do not like these options either
Citer : Posté le 05/09/2018 21:13 | #
Well, to be honest I do think that performing keyboard and screen input/output in the standard library is a bit harsh. It'd be easier if you had the support of the runtime library.
Have you considered using syscall 0x24a ?
This one returns a matrix code, very similar to gint's, so it will definitely not bother you with ALPHA/SHIFT combinations. Feels better than injecting keys to me. Although you'll have to do the waiting yourself but a call to the sleep instruction should be more than enough.
Citer : Posté le 06/09/2018 13:05 | #
Hmm, that would be even better... but I cannot make it work
Is the signature really the following?
int Keyboard_GetPressedKey(short*);
Unfortunately, my convenient list of fx9860g syscalls does not include the function signature
Citer : Posté le 06/09/2018 13:09 | #
You just need to follow the link (and fix the case typo in it): http://media.taricorp.net/prizm/simon_chm/fx_legacy_keyboard.htm
Now this is indeed the correct signature; I have never used this specific syscall in an add-in but I disassembled various versions of it and I don't remember any mismatch between SimLo's prototype and the actual code.
What exactly are you getting?
Citer : Posté le 06/09/2018 13:24 | #
Oh thank you.
There is my (minimal) code:
typedef int (*_get_pressed_key_type)(short* matrixcode);
const unsigned int _get_pressed_key_address[] = { 0xD201D002, 0x422B0009, 0x80010070, 0x24A };
const _get_pressed_key_type _get_pressed_key_ptr = (_get_pressed_key_type) _get_pressed_key_address;
typedef void (*_sleep_type)(unsigned int ms);
const unsigned int _sleep_address[] = { 0xD201D002, 0x422B0009, 0x80010070, 0x420 };
const _sleep_type _sleep_ptr = (_sleep_type) _sleep_address;
int main(void) {
int i = 0;
unsigned int key = 0;
short matrix_key;
char out[20] = "Test";
while (1) {
Bdisp_AllClr_DDVRAM();
sprintf(out, "[%d] %x", i++, matrix_key);
locate(3, 4);
Print("Key: ");
Print(out);
Bdisp_PutDisp_DD();
_get_pressed_key_ptr(&matrix_key);
_sleep_ptr(1000);
}
return 0;
}
Citer : Posté le 06/09/2018 13:44 | #
Ok so I checked my annotated disassembly of this syscall and against all odds, it seems to check that the provided keycode in matrix_code is pressed.
As far as I could test it returns 1 when AC/ON is pressed and 0 otherwise. This corresponds neither to SimLo's description nor to my disassembly, and it's quite weird.
Citer : Posté le 06/09/2018 14:30 | #
Hmm, I think this is not usable for me... I actually do not want to check every key every x milliseconds.
@Zezombye Your solution works like a charm! That's actually a brilliant idea!
@Lephé Switching on the backlight still works.
Citer : Posté le 06/09/2018 15:07 | #
This clearly needs investigation. As a very last suggestion, have you tried using the two first parameters of GetKeyWait() ?
Edit: Not that I don't like Zezombye's method, but... well, it's a hack.
Citer : Posté le 06/09/2018 15:24 | #
Okok, using the dedicated function works and is actually much cleaner. I'll use GetKeyWait()
Thanks for being the reasonable one here, Lephé.
@Zezombye Your solution is still clever though
Ajouté le 07/09/2018 à 14:31 :
I'm at implementing scanf when suddenly... A very strange problem appears!
I use logic... It's not very effective!
Here is the error I get. Note that I get this error instantly, i.e. without pressing any key before.
System ERROR
REBOOT :[EXIT]
INITIALIZE:[EXE]
ADDRESS(R)
TARGET=0030080E
PC =003002E4
Ok, I probably dereference an invalid pointer (which points to ROM)... should be easy to find.
Since I did not press any key before the error appeared, the error must be caused by something before the first call of GetKey(), non?
Let's look at my main:
int main(void) {
unsigned int key = 0;
char out[20] = "Test";
while (1) {
Bdisp_AllClr_DDVRAM();
locate(1, 4);
Print("Out: ");
Bdisp_PutDisp_DD();
GetKey(&key);
_read(0, out, 5); // <-- when I comment this line out, it works!
}
return 0;
}
Ok, first I thought, the new implementation of _read() cannot be the problem. But when I comment it out, it works o.O.
However, _read() only calls _console_read() which instantly returns.
I think the problem is that the linker links much more code if I call _read(). As a consequence, one of the previous calls (locate, Print etc.) fails. That's just a guess, however, and I have not the slightest idea on how to solve such a problem