Page 1 of 1

On serialRaw and buttonState

Posted: 20 Jul 2019, 18:51
by marcelk
There are two system variables in page zero that appear to have the same function. At least, you may be fooled into believing that if you simply observe them in a simulator: [$000f] serialRaw and [$0011] buttonState. In reality they have a somewhat different function, and this difference is about to become significant.

In the README they're described as follows:

Code: Select all

000f      serialRaw     New raw serial read
0011      buttonState   Edge-triggered and resettable input bits
serialRaw

serialRaw is the raw value of the 74HC595 shift register U39 as sampled 60 times per second. It reads 255 when idle or when there's no device connected. It reads an ASCII code when a key is pressed. Game controllers send byte values that are normally different from ASCII codes (but not always: the classic counterexample is that action button [A] is sent as 127, and that is also ASCII Delete).

The BabelFish software in Pluggy McPlugface normally sends ASCII codes 3 times in succession, so that even slower software doesn't have to miss a key while auto-repeat still works. It maps cursor keys to game controller bit patterns, and it sends those for as long as those keys are pressed down. BabelFish has a few more such tricks so that PS/2 keyboards not only work properly, but also act as a functional game controller replacement.

The game controller buttons are normally mapped to bits as follows:
  • bit 0: buttonRight
  • bit 1: buttonLeft
  • bit 2: buttonDown
  • bit 3: buttonUp
  • bit 4: buttonStart
  • bit 5: buttonSelect
  • bit 6: buttonB
  • bit 7: buttonA
These bits are normally 0 when pressed down, and 1 when idle. But this is not universally true anymore as far as serialRaw is concerned! Enter buttonState:

buttonState

First of all, buttonState typically follows serialRaw, but all the way back since ROM v1 it had an extra featue. [$0010] is an internal variable that hints at what's going on:

Code: Select all

0010      (serialLast)  Previous serial read (used for edge detection)
In short: buttonState responds only to button press changes: a bit becomes 0 if the corresponding button was just pressed down. And it becomes 1 again once it is released. The intent is that with this you can reset the bits yourself as soon as you've processed them. This extra feature is handy if you only want to take a single action each time a button is pressed down (instead of a continuous action while pressed):
  1. you detect a 0 bit, indicating a button was just pressed down
  2. you take your action for that button
  3. you reset only that bit to 1
  4. re-enter your I/O processing and handle the other bits
  5. even if that button is still pressed down, you see it as released until the next press down
With that there's no need to do edge-detection yourself!

New game controller types...

Second, and the main reason for posting this now: as of recently we're supporting a second type of game controller. These controllers look and feel exactly the same, except they have a different chip inside that sends different raw signals. The vastly oversimplified story is: to keep software compatible, the input handler recognises these game controllers automatically, and converts their values to the "old" values in buttonState. But it doesn't do that for serialRaw, because then keyboards wouldn't work anymore. And with that, the bits in serialRaw and buttonState don't have the same meaning anymore...

Take-home message:
  • If you want to process game controller buttons: look at buttonState: peek(17) or LD $11
  • If you want to process ASCII input: look at serialRaw: peek(15) or LD $0f
P.S: Tetronis and Bricks were already using buttonState, so they remain compatible. But two of our own programs needed a fix.

Re: On serialRaw and buttonState

Posted: 09 Sep 2019, 18:56
by DavidHK
With the button reset I encountered a small problem when the button happens to be the start button, this would also disable the reboot. (At least it appeared to me that way). So if I'd write an application to use that button, I should probably keep track of the state myself instead of resetting the buttonState.

Regarding the second controller type...unfortunately it does not feel the same to me. While holding down a direction key any change in the A button for example is not registered. As I already wrote in the kit assembly result board, playing Racer with that is quite a challenge. ;-) I suspect that the distributor that used to send you controllers silently swapped them? But I guess if I were in your place, I would probably have done the same, add some code to get them at least partially functional instead of having a bunch of "wrong" controllers to get rid of.

BASIC programs will stop by the way when you press the down button on a controller of this type. (Yes, I was so brave and hot-swapped the keyboard and the controller -- at one time the Gigatron rebooted when I did that, I hope I didn't damage anything...appears to work fine still).

Since I've got the Pluggy interface, I can't really complain. But I am curious, was there another application those controllers were originally meant for? I tried searching for those controllers on the web but didn't find anything.

I hope that didn't sound too negative, I am still very happy with the kit!

(Maybe I can hide a bigger microcontroller like an ATmega inside of the game controller case? ;-) )

Re: On serialRaw and buttonState

Posted: 09 Sep 2019, 19:35
by marcelk
Indeed we've kept [Select] and [Start] effectively reserved for user-kernel communication and we don't really expect applications to use them as well. They are handled at a low level, which I believe adds to the system's consistency and reliability. This way the user is always in control of basic operation such as how to get back to the main menu and what video mode he prefers to look at.

The game controllers are "new-old stock" from vintage Nintendo knock-offs we believe originally produced for the Chinese market. They come with all kinds of different chips inside. Racer is just hard, regardless of the game controller type...

And 8 buttons should be enough for everybody :D ! But if indeed you need to give a meaning to a brief push of [Start], it might need some suboptimal hack. In reality, with the keyboard interface now available for more than a year, the game controllers have become a bit of a neglected side gadget. They have never been that great. Tiny BASIC is too slow for nice games anyway, and BASIC + game controller is an odd combination in the current setup: with the game controller plugged in, there's no way to get a BASIC program in as well... Having said that, at one point I want to start writing games for the game controller again... There's so much entertainment potential there. But FAT32 must come first...

Re: On serialRaw and buttonState

Posted: 11 Sep 2019, 17:22
by DavidHK
Well, that is kind of my point, currently the reset is not handled at such a low level as it still seems to use the buttonState variable -- which I mistakenly first believed was free for my own reset purposes. I tried to save some instructions while developing the Sprint Paint app and set the entire buttonState to $ff instead of individual bits -- then I realized I had turned off the soft reset functionality by doing so.

Regarding the video mode selection -- I am personally a bit torn on this question, on one hand the button is really nice for demonstrating what happens, on the other hand I believe that the author of the app should know best which video mode works and which doesn't. For example the Loader doesn't work at all in the full mode, Tetronis struggles with the music, Racer gets really slow while Snake is perfectly happy in the full mode. In the two apps I have written so far I changed between the modes to speed up initialization of the screen and in the Sprite Paint app I even used it as a graphical effect to darken the screen while the app is saving the picture.

8 buttons would be enough, but that count would include Start and Select. ;-) I know, I am probably dreaming big, but I am imagining "top tier" 8-bit games like Pokemon, Legend of Zelda or Ultima...

Re: On serialRaw and buttonState

Posted: 12 Sep 2019, 18:26
by marcelk
DavidHK wrote: the author of the app should know best which video mode works and which doesn't
My thinking is that if we trust the owner to assemble the system from 100+ parts, we can surely trust him to select the video mode that works best for him :lol:. Video mode turns out to be a deeply personal thing, we didn't see that coming. For example, I can't stand the full mode, while others absolutely love it and are willing to accept compromised performance for it. So in general, we let the owner decide: the kit is a thing to tinker with after all. We only try to avoid hangups (to some extent), hence the very minor update in TinyBASIC when we added MODE 1975. (We applied a similar reasoning when we decided to make the romType a RAM variable: it gives a bit more control to the owner.)

On the other hand: this is a toy, a gadget for entertainment purposes, and no "rule" should be taken too seriously... Everyone enjoys it in his/her own way and that's all that matters :-).

Re: On serialRaw and buttonState

Posted: 28 Sep 2019, 23:15
by marcelk
DavidHK wrote: I tried to save some instructions while developing the Sprint Paint app and set the entire buttonState to $ff instead of individual bits -- then I realized I had turned off the soft reset functionality by doing so.
So far I only set buttonState's individual bits, not the whole bunch. But a quick test in BASIC makes me believe that the soft reset function keeps working if you restore buttonState in one go with $ef (or $cf) instead of $ff.