21 January, 2018

Simply put

Programming the Casio fx-9750gII

Everything should be made as simple as possible, but no simpler (Einstein)

Writing programs can be pretty rewarding, when everything works right the first time. It can be a complete and utter pain if you get something wrong, and can be even worse if you don't know the language you're supposed to be working in. Thankfully I think I'm finally getting my head around Casio BASIC.

Computer programs (and calculators too) spend 95% of their time waiting for user input. This is certainly true in a program I've been developing since before 2008. I've made several changes in it over the years, yet I'm still striking some of the same issues today that I did when I first started development.

There's bugs in my calculator!

One bug I had in my code relates to just how fast the calculator works, meaning that if I keep it too busy doing other stuff, it's got less time to take notice of something happening when I need it to. Casio BASIC has a function called GetKey that looks to see if a key's been pressed while it's running (that's important), and pass back the value. If it doesn't see that one's been pressed, then it'll simply return 0 and exit, and won't wait around until a key has actually been pressed. It only seems to wait for about a tenth of a second, though I'm not sure if the speed of the calculator matters.

This can have an impact if the program asks you to choose from a list of choices, and you've put the calculator into a busy loop so you can make it actually wait for a key to be pressed. There are a couple of ways to do this. In short, the loop you use should be really short, and shouldn't involve anything else except waiting for a key. Don't draw bits of the screen, then wait for a key, then redraw the screen, wait, etc. Just wait for a key and loop; if one doesn't turn up, go back to the beginning of the wait loop, which simply looks for a key.

I'd done exactly this in most of the rest of my code, but in this one function I'd forgotten that. I didn't click to what the problem was until I was developing the help functions. I ran the help program and was striking exactly the same issues that I'd struck back in my other routine, meaning that I'd made the same mistake.

Waiting Around

The bug I'd had worked a bit like this pseudocode:

LBL 0
DrawScreen() #Takes significant time to execute
GetKey->K
If K=1; Then Goto 9; IfEnd
If K=2; Then Goto 12; IfEnd
If K!=3; Then Goto 0; IfEnd
Lbl 9
...

Because I was including the DrawScreen in the wait cycle, the calculator was doing what I asked. It takes time to draw the screen contents on this calculator (sometimes on the order of a couple of seconds); it's certainly not a HP Prime or TI-84CE in that regard. Because of that, the calculator would wait for a tenth of a second, decide I hadn't pressed a key, then draw the screen again (taking perhaps a fifth of a second or so), then ask if I'd pressed a key, ad infinitum.

This was awkward, because the program was prompting for one of two values, but I was having real problems actually trying to select either value. It's never a good look when the user has to hammer the key they want just to get the calculator to take notice. Put simply, I was expecting the calculator to accept a key while it spent more time (possibly five times as long) to draw the screen than it spent waiting for a key. A calculator's often single-threaded, so can't do multiple things at the same time.

Apply flyspray

The way I eventually fixed the code was to add a label directly before the GetKey function, and jump to that if I hadn't matched anything I wanted:

LBL 0
DrawScreen() #Takes significant time to execute
LBL 1
GetKey->K
If K=1; Then Goto 9; IfEnd #Jump to first function
If K=2; Then Goto 12; IfEnd #Jump to second function
If K!=3; Then Goto 1; IfEnd #Jump to beginning
Lbl 9
...

This sped up the calculator's response to me, because the only thing it was doing was either:

  • Asking what key had been pressed, and returning 0 if none turned up after 0.1 seconds
  • Storing the returned value
  • Checking the value against a list of other values
  • Jumping to a place
and it wasn't spending time repeatedly drawing the screen.

Conclusion

One thing I learned a while ago from a programming manual, though I can't remember which one: if you find and fix a bug in one place, go through the rest of the code to see where else that bug appears, and fix them all at the same time. This works best for small projects that are only written by one or a few people, but really really pays off for large projects too. The final moral of the story is, don't make the calculator do more than it has to. The battery life of your calculator will thank you for it, especially on bigger calculators such as the TI-89 Titanium or Casio fx-9750gII.

No comments: