key_down() bugs out GetKey()
Posté le 06/06/2019 14:54
I'm having a problem with key_down() messing with getkey()
I've tested usefill.c and input.c keydown commands
The problem is when that command is run, while there is no key currently being held down, when the next time GetKey() is run it only works when a key on a certain row is pushed (Like only keys on the 2nd to top row work)
Run the code
press a random key
wait for timer
press another random key
it should restart the timer, but it doesn't
any key on the 2nd to top row worked for me, but the rest didn't
while (1) {
PrintXY(10, 10, "Press any key", 0);
GetKey(&key);
Bdisp_AllClr_DDVRAM();
sprintf(&string, "%d", key);
PrintXY(10, 20, string, 0);
for (k = 0; k < 10; k++) {
sprintf(&string, "%d", k);
PrintXY(60, 30, string, 0);
Bdisp_PutDisp_DD();
Sleep(100);
}
key_down(28);
}
This is part of the code for the keydown() from usefill.c / input.c
memcpy(&key, keyboardregister, sizeof(unsigned short) << 3);
row = code % 10;
return (0 != (key[row >> 1] & 1 << code / 10 - 1 + ((row & 1) << 3)));
Sorry; I don't know french, I'm using google translate.
Citer : Posté le 06/06/2019 15:06 | #
Is that Simon Lothar's CheckKeyRow() function by any chance? I've seen such a problem when experimenting with my keyboard driver, and honestly it's pretty bad.
Anyway, GetKey() uses interrupts to be notified of events, whereas key_down() reads I/O lines on-the-fly. The two just can't mix, and you probably should use GetKey() everywhere, especially if you have a timer.
If you need a GetKey() with a timeout, see if GetKeyWait() could fit your needs. This function is buggy and will never return the pressed key code. This can be solved by calling the related syscall, just don't be surprised if you try it directly in the SDK.
Citer : Posté le 06/06/2019 22:59 | #
Probably is, I got different libs from
https://www.planet-casio.com/Fr/forums/topic9901-1-[C]Easy-Coding.html
https://www.planet-casio.com/Fr/forums/topic13167-1-[Librairie-C]-Controle-avance-du-clavier-V2-corrigee.html
https://www.planet-casio.com/Fr/forums/topic13183-1-[bibliotheque-C]usefull.h--plein-de-fonctions-utiles-!.html
Im trying to make a 2 player snake game, which requires me to be able to test to see if theres 2 different keys pushed down at the same time.
I need a non-pausing KeyDown() while the game is running and I was using GetKey() for menu stuff
Could I do something like test to see if anykey is down then run KeyDown() or should I just use KeyDown() for everything?
How would I get [Menu] key to exit to menu then?
Citer : Posté le 07/06/2019 00:16 | #
Okay, so two simultaneous key presses is where you can't use GetKey() anymore. You have to use key_down() at least in this section of the program, and you need to do it "properly".
Your input loop must be either all-GetKey() or all-key_down(). In the section where you use key_down(), you should rely on the timer to introduce delays and regularly check whether one or more of the keys are pressed. (A major advantage of that over using GetKey() is that GetKey() may not have the delays you need. Its delays are irregular.)
In this situation, you can't return to menu directly. You could use a trick but there'd be trouble when you come back into the app. The safest way, I suppose, is to have a pause menu driven by GetKey().
Citer : Posté le 07/06/2019 01:00 | #
If key_down(54) is run when no key is currently being pressed down (54 is char 9) then if GetKey(&key) is run afterwards, only keys in the same row as char_9 work, every other key just doesn't do anything
Im using GetKey() to pause the game, when the user presses any button it unpauses, but if the menu key is paused it should go to the menu
So I can replace GetKey(&key) with GetKeyWait(0,0,0,&key)?
A work around is to use GetKeyWait(1,0,0,&key) to check if any key is pressed, then run key_down() - this is to stop key_down() being run when no keys are pressed at that moment- doesn't work, was able to still break itThe code above is not my snake game, it was just to show that GetKey() gets stuck after running key_down()
Citer : Posté le 07/06/2019 01:15 | #
If key_down(54) is run when no key is currently being pressed down (54 is char 9) then GetKey(&key) is run afterwards only keys in the same row as char_9 work, every other key just doesn't do anything
The KeyDown() function from EasyCoding is Simon's routine and I have experienced bugs with it before, such as the one you described. Don't use it as is. Also, this is a function for SH3-based models, which are extremely rare today. How old is the calculator you're targeting?
Ninestar's input.c library is also using Simon's CheckKeyRow(), so you ought to avoid that as well on SH4.
And... it's the same for usefull. All of them use Simon's incomplete routine.
Oh, now I remember why it is buggy! The data port is not restored after the call. You need to save the value of *PORTB and restore it at the end of CheckKeyRow().
But really, this method of reading the keyboard is for SH3 calculators. Check your OS version, if it's 02.xx.xxx1 then you have an SH4 calculator and you should remember that it is not the canonical way to do it.
You just need to call GetKey(). If the user presses the menu key, then GetKey() will go to the menu but the call will not terminate. Only if the user returns in the application and presses a new key will GetKey() return. Menu management is transparent for you.
This is likely not going to work, and it's still pretty bad taste if it does. Using the timer to schedule your event loop would give you regular intervals, FPS control and rid you of many inputs GetKey() could wrongly interpret for you.
Let's sum up: to solve this you need to save *PORTB and restore it in CheckKeyRow(). This will possibly make your code work, and the rest is up to you. But I strongly suggest getting rid of the GetKey()/key_down() combo which has been bad in the past.
Citer : Posté le 07/06/2019 01:28 | #
I have a 9750GII, OS upgraded to SH4/02.04.0201
Who else has a 'IsKeyDown() routine that works 100% without bugs on my calc? (I dont care about SH3)
How would I save and restore *PORTB?
Like this?
[code]
unsigned char CheckKeyRow(unsigned char code) {
tempPORTB = *PORTB;
//row, column and key code stuff
*PORTB = tempPORTB;
}
[/code]
Citer : Posté le 07/06/2019 01:32 | #
Yes, that would be the way. But on SH4 it is much faster to use the following scan procedure:
volatile uint16_t *KEYSC = (void *)0xa44b0000;
for(int i = 0; i < 6; i++) scan[i] = KEYSC[i];
And that's all. Every key corresponds to a certain bit in the array, which is organized with one byte per row (although it is read in words) in pretty much the same way as on SH3.
Citer : Posté le 07/06/2019 07:20 | #
Sorry but I dont know how or where to put that code
Do you have an example program that has this code in it?
Just so I can see how its implemented and used
IDK how to fix these errors
(83) : C2225 (E) Undeclared name "KEYSC"
(83) : C2233 (E) Illegal array reference
Citer : Posté le 07/06/2019 13:47 | #
Oh, sorry ! The scan array contains a complete copy of the keyboard state. The idea is to execute the two last lines regularly (generally you want to do this with a timer) and then read scan when you want to check if a key is pressed. So you have two choices :
1. Either you use a lot of functions from input/usefull/EasyCoding and you should edit them; replace their key array with scan and the CheckKeyRow() function with the two other lines. I can help for this is you indicate which library you're interested in;
2. Or you just declare scan in your own program and you wrap the other two lines in a timer callback like this:
void scan_keyboard(void)
{
volatile unsigned short *KEYSC = (void *)0xa44b0000;
for(int i = 0; i < 6; i++) scan[i] = KEYSC[i];
}
int key_down(int row, int col)
{
unsigned char *data = scan;
return (data[row] >> col) & 1;
}
SetTimer(ID_USER_TIMER1, 50, scan_keyboard);
The key codes are replaced with "matrix codes" which tell the row and column of the key; it is not unusual to define keycodes as "10*row+column" (or the converse), this is the case in Casio Basic for instance.
The errors you get is because I had left a uint16_t in the code. This is a type for 16-bit integers but the SDK does not use this name, so I replaced it with unsigned short. Sorry for this!
Citer : Posté le 08/06/2019 06:36 | # | Fichier joint
error reading nonexisting memory - attached file
oh and the SDK doesn't support 'int i' inside the for loop
I just had to declare it outside the function
Citer : Posté le 08/06/2019 09:16 | #
Oh, now I remember why it is buggy! The data port is not restored after the call. You need to save the value of *PORTB and restore it at the end of CheckKeyRow().
It seems to be able to fix the freeze bug of Getkey1 of C.Basic on SH3.
Thanks very much!
Overclocking utilitaire Ftune/Ptune2/Ptune3 est également disponible.
Si vous avez des questions ou un rapport de bogue, n'hésitez pas à me le faire savoir.
Citer : Posté le 08/06/2019 11:38 | #
Does the [Menu] key still get frozen?
Ajouté le 08/06/2019 à 16:15 :
Still getting this error:
Execution has stopped due to an error! Nonexisting memory by data read access at A44B0000
when this line gets run
What am I not doing? or is it just a tiny spelling error?
Citer : Posté le 08/06/2019 16:19 | #
It seems to be able to fix the freeze bug of Getkey1 of C.Basic on SH3.
Thanks very much!
You're very welcome!
Execution has stopped due to an error! Nonexisting memory by data read access at A44B0000
The emulator runs an SH3 system, and there is no robust keyboard access method that works on both SH3 and SH4...
I personally have two drivers, one for the port-driver interface of the SH3, similar to CheckKeyRow() after it is fixed, and one for the key matrix of the SH4, which I just gave you. The SH4 can use ports but it's less reliable and it's not the same ports as the SH3.
I realize this is getting complicated for you, I'm sorry about that. >_>
You should not have this error if you try on-calc.
Citer : Posté le 08/06/2019 16:23 | #
Ahh I thought the emulator was on SH4, but that makes sense since it has never been updated
Ok I think I know what to do now... I think
Ajouté le 08/06/2019 à 16:54 :
I think I got it working
Emulator is fine, cant check calc until later tomorrow morning
GetKey() is the same across both SH4 and SH3 right?
'isOS2' stands for SH4?
Citer : Posté le 08/06/2019 17:19 | #
Yes GetKey() is the same.
I dont' know about isOS2(). That seems likely SH3 vs SH4. If you can point me the source I will be able to tell.
Citer : Posté le 09/06/2019 11:25 | #
@Lephenixnoir
isOS2 is like this.
'isOS2' stands for SH4?
This is a judgment used in C.Basic.
https://www.planet-casio.com/Fr/forums/topic14525-25-windmill-moteur-graphique-3d-new-demo.html#164573
Overclocking utilitaire Ftune/Ptune2/Ptune3 est également disponible.
Si vous avez des questions ou un rapport de bogue, n'hésitez pas à me le faire savoir.
Citer : Posté le 09/06/2019 11:31 | #
I dont remember where exalty I got this code from, might be one of the libs said above or I copy/pasted it off some code in one of the forums on this website
Code is pretty much the same as all the libs
top of part detects if SH3 or SH4 I think
#ifndef OS2Change
#define OS2Change
#ifndef OS2Change_GetOS2
#define OS2Change_GetOS2
typedef int(*sc_i2cp2sip)(char*, char*, short int*, short int*);
const unsigned int sc0015[] = { 0xD201D002, 0x422B0009, 0x80010070, 0x0015 };
#define GlibGetOSVersionInfo (*(sc_i2cp2sip)sc0015)
int OSVersionAsInt(void) {
unsigned char mainversion;
unsigned char minorversion;
unsigned short release;
unsigned short build;
GlibGetOSVersionInfo(&mainversion, &minorversion, &release, &build);
return ((mainversion << 24) & 0xFF000000) | ((minorversion << 16) & 0x00FF0000) | (release & 0x0000FFFF);
}
#define isOS2 (OSVersionAsInt() >= 0x02020000)
#define OS2(x,y) ((OSVersionAsInt() >= 0x02020000)?y:x)
#endif
#ifndef OS2Change_Keyboard
#define OS2Change_Keyboard
void delay(void) {
char i;
for (i = 0; i < 5; i++) {};
}
unsigned char CheckKeyRow(unsigned char code) {
unsigned char result = 0;
short* PORTB_CTRL = (void*)0xA4000102;
short* PORTM_CTRL = (void*)0xA4000118;
char* PORTB = (void*)0xA4000122;
char* PORTM = (void*)0xA4000138;
char* PORTA = (void*)0xA4000120;
short smask;
char cmask;
unsigned char column, row;
char tempPORTB = *PORTB;
column = code >> 4;
row = code & 0x0F;
smask = 0x0003 << ((row % 8) * 2);
cmask = ~(1 << (row % 8));
if (row < 8) {
*PORTB_CTRL = 0xAAAA ^ smask;
*PORTM_CTRL = (*PORTM_CTRL & 0xFF00) | 0x00AA;
delay();
*PORTB = cmask;
*PORTM = (*PORTM & 0xF0) | 0x0F;
}
else {
*PORTB_CTRL = 0xAAAA;
*PORTM_CTRL = ((*PORTM_CTRL & 0xFF00) | 0x00AA) ^ smask;
delay();
*PORTB = 0xFF;
*PORTM = (*PORTM & 0xF0) | cmask;
}
delay();
result = (~(*PORTA)) >> column & 1;
delay();
*PORTB_CTRL = 0xAAAA;
*PORTM_CTRL = (*PORTM_CTRL & 0xFF00) | 0x00AA;
delay();
*PORTB_CTRL = 0x5555;
*PORTM_CTRL = (*PORTM_CTRL & 0xFF00) | 0x0055;
delay();
*PORTB = tempPORTB;
return result;
}
unsigned char KeyDown(unsigned char keycode) {
unsigned short key[8];
const unsigned short* keyboardregister = (unsigned short*)0xA44B0000;
if (isOS2) {
unsigned char row = keycode % 10;
memcpy(key, keyboardregister, sizeof(unsigned short) << 3);
return (0 != (key[row >> 1] & 1 << keycode / 10 - 1 + ((row & 1) << 3)));
//scan code goes here?
//unsigned short scan[16] = { 0 };
//volatile unsigned short *KEYSC = (void *)0xa44b0000;
//for(int i = 0; i < 6; i++) scan[i] = KEYSC[i];
}
else {
return CheckKeyRow((keycode % 10) + ((keycode / 10 - 1) << 4));
}
}
unsigned char GetKeyMod(unsigned int* key) {
unsigned char x, ret;
ret = GetKey(key);
for (x = 0; x < 80; x++)
{
if (KeyDown(x))
{
*key = x;
break;
}
}
return ret;
}
#define GetKey(x) GetKeyMod(x)
#define IsKeyDown(x) KeyDown(x)
#define IsKeyUp(x) !KeyDown(x)
#endif
#endif
Citer : Posté le 09/06/2019 19:32 | #
If you want to detect SH3 and SH4 using the OS version, isOS2() is not reliable. It was at some point I think, but no longer.
There is a more reliable way to know. For 02.xx.xxxy OSes, the last digit y is 0 for SH3 and 1 for SH4. For 03.xx.xxxx OSes, the target is always SH4.
Or you have a hardware-based detection which is the only reliable thing in case the OS has been tweaked, as in C.Basic.
Regarding the code you posted above, you have the keyboard scan code commented with "scan code goes here?". This is not exactly the place. You should scan the keyboard regularly with a timer, and do it often. I think you can go down to 25ms with SetTimer() and this is a good start. In the timer callback you fill the scan array and then the KeyDown() procedure can read the scan array.
The reason is that you have to control the frequency of hardware access. If it's too high, you'll waste power on hardware I/O. If it's too low, the keyboard will behave strangely. I don't know why this happens specifically on SH4 but I have experienced and documented that a bit.
Citer : Posté le 18/06/2019 11:44 | #
so I have this code
I've tested SH3 (emulator) and SH4 (my calc) and it seems to work perfectly between both OS's
its just some test code, I'm gonna clean it all up
Why does the scanKeyboard() need to be in a timer that runs all the time?
It seems to work fine when it only gets run when the KeyDown() is run?
What sort of issues are you talking about when its not working correctly?
is (void*)0xa44b0000 a memory address which the memory for the keys are located or is it a 'function' which gets the memory address of the keys?
#include "stdio.h"
#ifndef OS
#define OS (3 + !(*(volatile unsigned short*)0xFFFFFF80 || *(volatile unsigned short*)0xFFFFFF84)) //3 for SH3: 4 for SH4
void delay(void) {
unsigned char i;
for (i = 0; i < 5; i++);
}
unsigned char CheckKeyRow(unsigned char col, unsigned char row) {
unsigned char result = 0;
short* PORTB_CTRL = (void*)0xA4000102;
short* PORTM_CTRL = (void*)0xA4000118;
char* PORTB = (void*)0xA4000122;
char* PORTM = (void*)0xA4000138;
char* PORTA = (void*)0xA4000120;
short smask;
char cmask;
char tempPORTB;
smask = 0x0003 << ((row % 8) * 2);
cmask = ~(1 << (row % 8));
tempPORTB = *PORTB;
if (row < 8) {
*PORTB_CTRL = 0xAAAA ^ smask;
*PORTM_CTRL = (*PORTM_CTRL & 0xFF00) | 0x00AA;
delay();
*PORTB = cmask;
*PORTM = (*PORTM & 0xF0) | 0x0F;
}
else {
*PORTB_CTRL = 0xAAAA;
*PORTM_CTRL = ((*PORTM_CTRL & 0xFF00) | 0x00AA) ^ smask;
delay();
*PORTB = 0xFF;
*PORTM = (*PORTM & 0xF0) | cmask;
}
delay();
result = (~(*PORTA)) >> col & 1;
delay();
*PORTB_CTRL = 0xAAAA;
*PORTM_CTRL = (*PORTM_CTRL & 0xFF00) | 0x00AA;
delay();
*PORTB_CTRL = 0x5555;
*PORTM_CTRL = (*PORTM_CTRL & 0xFF00) | 0x0055;
delay();
*PORTB = tempPORTB;
return result;
}
unsigned char KeyDown(unsigned char col, unsigned char row) {
if (OS == 3)
return CheckKeyRow(col - 1, row);
else {
unsigned char i;
unsigned char* data;
unsigned short scan[5];
volatile unsigned short* KEYSC = (void*)0xa44b0000;
for (i = 0; i < 5; i++)
scan[4 - i] = KEYSC[i];
data = scan;
return (data[9 - row] >> (col - 1)) & 1;
}
}
#endif //OS
unsigned char string[1];
unsigned int key;
unsigned int i;
unsigned char row;
unsigned char col;
int AddIn_main(int isAppli, unsigned short OptionNum) {
sprintf(&string, "SH%d", OS);
PrintXY(0, 30, string, 0);
for (i = 0; i < 10000; i++) {
for (col = 1; col <= 7; col++) {
for (row = 0; row <= 9; row++) {
if (KeyDown(col, row)) {
sprintf(&string, "%d", col * 10 + row);
PrintXY(5, 50, string, 0);
sprintf(&string, "%c", '-');
}
else
sprintf(&string, "%c", '0');
PrintMini(90 - col * 4, 55 - row * 6, string, 0);
}
}
Bdisp_PutDisp_DD();
if (KeyDown(6, 6))
break;
}
if (OS == 4)
KillTimer(1);
while (1)
GetKey(&key);
return 1;
}
#pragma section _BR_Size
unsigned long BR_Size;
#pragma section
#pragma section _TOP
int InitializeSystem(int isAppli, unsigned short OptionNum) {
return INIT_ADDIN_APPLICATION(isAppli, OptionNum);
}
#pragma sectionction
Citer : Posté le 18/06/2019 13:23 | #
This looks like a new detection method, but I'm not very confident. Both of these addresses ought to be address errors on SH4. Plus, although FRQCR will certainly not be 0, the WDT could run on SH3. How did you find this one?
The delay()'s going to be optimized out.
I fought with that for a long time, my conclusion was that if you don't read the keyboard registers often enough, it will eventually return glichty data. Your example does not invalidate this conclusion yet because you run a lot of KeyDown() in your critical loop, so there's not delay between them. But any more information is welcome. See this topic (casiopeia.net) for more details.
It's the address of a peripheral module whose 6 first registers are 16-bit and hold the key state of the keyboard. There's definitely "something" backing up this module, but I don't know what. Maybe a fairly complicated circuit.