Jump to content
IGNORED

Possible to disable Intellivision keypad keys?


PuzZLeR

Recommended Posts

Hi again. Another question if I may. I am benefitting from the previous answers and I thank you folks. I also hope my questions also end up useful in the future for someone doing a search.

 

I've always found the keypad keys (1-9, 0, CLEAR, ENTER) strange, even on a real Intellivision. In my games, even when I have not programmed them to do anything, they still do funny things, like maybe move my avatar a bit, or shoot, etc. I've heard similar complaints from others too.

 

I would like to completely disable them, or at least choose those that I want disabled, so that if you press on them they will do nothing. (Yes, they may hinder the disc on the same controller, as we know, but that's different.)

 

Are there any IntyBASIC commands that can do this, or is there some sort of procedure, or knowledge regarding this?

 

Thanks so much!

Link to comment
Share on other sites

The following handy functions generate better code, and AFAICT are correct.

 

DEF FN KEY_ON_CONT1 = ((PEEK($1FF) AND (PEEK($1FF) - $20)) + $7FC0) < $8060
DEF FN KEY_ON_CONT2 = ((PEEK($1FE) AND (PEEK($1FE) - $20)) + $7FC0) < $8060
DEF FN NOKEY_ON_CONT1 = ((PEEK($1FF) AND (PEEK($1FF) - $20)) + $7FC0) >= $8060
DEF FN NOKEY_ON_CONT2 = ((PEEK($1FE) AND (PEEK($1FE) - $20)) + $7FC0) >= $8060

Now you can say:

IF KEY_ON_CONT1 THEN GOTO skip_disc1
' decode disc 1
skip_disc1:

Or:

IF NOKEY_ON_CONT1 THEN 
   ' decode disc 1
END 

And, of course, you can make a version that lets you put the controller address in a variable, so that you don't force your user to use controller 1 (which may be the most worn on most systems).

Edited by intvnut
improve the code further
  • Like 1
Link to comment
Share on other sites

Of course, I know that looking at key presses involves looking at Bits 5-7.  But I'm curious why you're putting Bit 15 into these Functions.

 

Does it have to do with the Sign/Carry bits in the CPU?  I know $1ff and $1fe are actually 16-bit, being lumped with the sound chip.

  • Like 1
Link to comment
Share on other sites

Forgive me as I still don't get it. I played around with it.

 

I know you guys know what you're talking about, but I just ask for just a bit more clarification.

 

Particularly, if you can give me a simple example regarding:

 

' Just ignore keypad

' Process anything else

'decode disc 1

 

As an example of what I'm trying to do is that I have implented code to pause the game by hitting zero. It works, but, before it pauses it tends to shoot first (with what a side button is supposed to do) or move the avatar a bit (which is what only the disc is supposed to do).

 

Any insight?

 

Thanks again.

 

 

Link to comment
Share on other sites

That's a mechanical problem with the intellivision controller flex circuit.  Each keypad button grounds two pins.  Those pins are shared with the disc and side buttons.  If pressing a keypad button grounds only one pin you inadvertently get a disc direction.  But it shouldn't trigger a side button action.

Edited by mr_me
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

4 hours ago, PuzZLeR said:

Forgive me as I still don't get it. I played around with it.

 

I know you guys know what you're talking about, but I just ask for just a bit more clarification.

 

Particularly, if you can give me a simple example regarding:

 

' Just ignore keypad

' Process anything else

'decode disc 1

 

As an example of what I'm trying to do is that I have implented code to pause the game by hitting zero. It works, but, before it pauses it tends to shoot first (with what a side button is supposed to do) or move the avatar a bit (which is what only the disc is supposed to do).

 

Any insight?

 

Thanks again.

 

 

 

The Intellivision hand-controller has 31 different input control surfaces (12 keys keypad, 3 action buttons, and 16 disc directions), but it only has 8 wires to transmit the information to the console.  These 8 wires feed into a hardware register which then exposes the input signal as an 8-bit bitmap to the CPU.

 

So ... how do they cram 31 distinct signals into 8 measly wires?  The engineers came up with a clever set of unique 1-, 2-, or 3-wire combinations for each discrete input, such that they can be unambiguously identified provided the program can isolate the combination for each input.  Of course, we still have 8 wires used for 31 combinations, so some of those combinations will obviously reuse the same wires.

 

In practice, that is fine, because the hardware engineers supposed that the disc would be used in two ways:  Either to enter data using a keypad, or to "run-and-shoot" using the disc and action buttons.  With that in mind, the combinations between disc and action buttons were chosen to never overlap.  However, the combinations for the keypad do not overlap within keys themselves, but overlap with some of the wires used by the disc or the action buttons.

 

Still, this works fine as long as you continue the original assumption:  Design your program to accept either a key entry or a disc+action button at a given time.  That doesn't mean that your program must choose one or the other -- it just means that your program must make sure to convey (either in its instructions, its normal mode of operation, or its user interface design, etc.) that the user must never press the disc or a button at the same time that they press a key in the keypad.  If they do, there is a chance you won't be able to tell which one it is.

 

OK, fine, so what does that have to do with what you are trying to do?

 

It turns out that some disc codes use only a single wire for their signals, which happens to be one of those wires re-used in 2- or 3- wire combinations for keys on the keypad.  What this means is that if you are expecting only disc input and a player happens to press a key which triggers one of those disc-looking signals, your program will likely interpret it incorrectly as the disc being pressed.

 

There's also the issue of mechanical "noise" and signal "bounce."  These are electrical artefacts that occur when switches are pressed, in which the contacts may not make complete connection at once.  These are merely transient errors in the signal, but if you happen to read the input as the error is occurring, you will probably read the wrong thing.

 

Perhaps an example would work.  Consider the "7" key, whose 8-bit binary code is 10000100.  Now, compare that to disc position "0," whose code is 00000100.  Note that both of them have the third rightmost bit set.  In the case of the key, there is one more bit set, which makes it unique; but the disc only has the one.

 

So, if you are just reading disc input, and the player pressed the "7" key, your program will see that third bit set and assume it's disc position "0," and react accordingly.

 

How do you avoid this?  Well, that's what the solutions from @nanochess and @intvnut so cleverly do.  Essentially, before checking for disc input, they say, "ignore any code that looks like a keypad key."  That would discard right off the bat something like the "7" key, which is obviously a keypad input.  What's left is only the codes which are exclusively everything else.

 

Their solutions do the same thing, and although they do it in different ways, they both use the same principle:  There are a set of partial signal combinations which only apply to keypad input and never to disc; and when these are seen, they are discarded right off.  This avoids having to compare each input to each possible keypad code.

 

I hope this makes sense.

 

   -dZ.

 

Edited by DZ-Jay
  • Like 2
Link to comment
Share on other sites

On 1/24/2021 at 9:38 PM, PuzZLeR said:

Hi again. Another question if I may. I am benefitting from the previous answers and I thank you folks. I also hope my questions also end up useful in the future for someone doing a search.

 

I've always found the keypad keys (1-9, 0, CLEAR, ENTER) strange, even on a real Intellivision. In my games, even when I have not programmed them to do anything, they still do funny things, like maybe move my avatar a bit, or shoot, etc. I've heard similar complaints from others too.

 

I would like to completely disable them, or at least choose those that I want disabled, so that if you press on them they will do nothing. (Yes, they may hinder the disc on the same controller, as we know, but that's different.)

 

Are there any IntyBASIC commands that can do this, or is there some sort of procedure, or knowledge regarding this?

 

Thanks so much!

 

For reference, here are the codes for each input:

KeyPad:
------- ------- --------
Input   Code    Signal
------- ------- --------
1       0x81    10000001
2       0x41    01000001
3       0x21    00100001
4       0x82    10000010
5       0x42    01000010
6       0x22    00100010
7       0x84    10000100
8       0x44    01000100
9       0x24    00100100
Clear   0x88    10001000
0       0x48    01001000
Enter   0x28    00101000
------- ------- --------

Action:
------- ------- --------
Input   Code    Signal
------- ------- --------
0       0xA0    10100000
1       0x60    01100000
2       0xC0    11000000
------- ------- --------

Disc:
------- ------- --------
Input   Code    Signal
------- ------- --------
0       0x04    00000100
1       0x14    00010100
2       0x16    00010110
3       0x06    00000110
4       0x02    00000010
5       0x12    00010010
6       0x13    00010011
7       0x03    00000011
8       0x01    00000001
9       0x11    00010001
10      0x19    00011001
11      0x09    00001001
12      0x08    00001000
13      0x18    00011000
14      0x1C    00011100
15      0x0C    00001100
------- ------- --------

Notice that pressing "ENTER" (00101000) or "CLEAR" (10001000) or "0" (01001000), all use the fourth rightmost bit, which happens to be the single bit that disc input "12" (00001000) uses.  If you just check for that disc bit and ignore the fact that other bits were set, you will likely misread the input as a disc press instead of a keypad key.

 

So, instead of just checking if the input looks like a disc input (which is what something like "CONT1 AND 8" does), you need to somehow discard all those that are something else but look like a disc code.

 

    -dZ.

  • Like 1
Link to comment
Share on other sites

Curiosity had gotten the better of me last year, when I looked into why both 1+9 and 3+7 were both valid Pause combinations.

 

So I whipped up the attached program which computes all the possible key/button/disc combinations that produce each 8-bit signal.

 

I build and run it on Windows with

 

    cl controller.c

    ./controller.exe > zout

 

For each of the 256 8-bit signals, it lists the set of minimal combinations that produce that signal.

The first few lines of the output are, for example:

 

[  0 = 0x00]
[  1 = 0x01] { S }
[  2 = 0x02] { E }
[  3 = 0x03] { SSE } { E, S }
 

indicating there are no ways to produce 0x00, only one way to produce 0x1 and 0x2, and two ways to produce 0x3.

 

If you're only interested in how many signals can be produced with, say, exactly 6 key presses, invoke it as

 

    ./controller.exe 6

 

Not sure how useful this information is, but it seemed appropriate for this thread.

 

Enjoy.

 

 

 

 

controller.c zout

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

13 hours ago, PuzZLeR said:

Forgive me as I still don't get it. I played around with it.

 

I know you guys know what you're talking about, but I just ask for just a bit more clarification.

 

Particularly, if you can give me a simple example regarding:

 

' Just ignore keypad

' Process anything else

'decode disc 1

 

As an example of what I'm trying to do is that I have implented code to pause the game by hitting zero. It works, but, before it pauses it tends to shoot first (with what a side button is supposed to do) or move the avatar a bit (which is what only the disc is supposed to do).

 

Any insight?

 

Thanks again.

 

 

 

As @mr_me said, none of the keypad bits aliases any of the side action buttons.  If you're seeing that happen, it's because you're using CONTx.BUTTON, and IntyBASIC's taking a shortcut.  IntyBASIC doesn't check whether two bits are set, and so will misinterpret keypad as button.

    ;[1] b = CONT1.BUTTON
    SRCFILE "/tmp/b.bas",1
    MVI 511,R0
    XORI #255,R0
    ANDI #224,R0
    MVO R0,var_B

You can avoid that by testing for B0, B1, B2 explicitly, or maybe trying the following replacement, which is slightly more expensive than CONT1.BUTTON, but way cheaper than testing B0, B1, B2 separately.

DEF FN CONT1_BUTTON = (PEEK($1FF) AND (PEEK($1FF) - $20)) < $20
DEF FN CONT2_BUTTON = (PEEK($1FE) AND (PEEK($1FE) - $20)) < $20
DEF FN CONT_BUTTON  = (PEEK($1FF) AND (PEEK($1FF) - $20) AND PEEK($1FE) AND (PEEK($1FE) - $20)) < $20

 

As for how to use the tools that @nanochess and I offered, you want to do something like this:

 

IF NOKEY_ON_CONT1 THEN
   ' decode action buttons
   IF CONT1.BUTTON THEN '... whatever
   IF CONT1.UP = ... THEN ' ... whatever
   IF CONT1.DOWN = ... then ' ... whatever
ELSE
   ' handle keypad input
   IF CONT1.KEY = 0 THEN '... pause
   '... decode any other keypad keys
END IF

 

That said, as @mr_me points out, sometimes one switch will close before the other, and you don't know whether you're getting a keypad input or a DISC input.  I've tried several strategies over the years.  The best you can do is "good enough."  For example, in Space Patrol, if you press [Enter] on the Select Mission menu really, really slowly, on most controllers it'll close the switch associated with tapping the disc to the left first, and will switch the menu selection on you before recognizing the [Enter] key.  

 

I added about a half-second debounce on that input, so only folks trying to cause problems would see an issue.  "Hurray, you succeeded.  You win a prize!  Your prize today is a wrong menu selection, because you decided to press [Enter] slowly over the course of 2 seconds."  Play stupid games, win stupid prizes, I guess!

 

In a reaction-time driven game, you don't want to add a lot of debouncing on controller inputs as that adds to controller lag.  In the past I've implemented heuristics such as: 

  • If I see two bits asserted in bits 5..7, assume there's no keypad input.  Action buttons assert 2 out of 3 bits among bits 5..7.
  • If I see bit 4 asserted, assume there's no keypad input.  Bit 4 is only ever asserted by the DISC on certain diagonals.
  • If I see an input that looks like exactly N, S, E, W (i.e. exactly 1 bit asserted out of bits 0..3), and no other bit set among 4..7, then:
    • If I haven't seen any inputs at all recently, then wait a small bit of time before deciding that's a DISC input.
    • If it's been stable for a small bit of time, go ahead and assume it's DISC input.
    • If I've seen any other DISC inputs recently, assume it's a DISC input.

So what are "recently" and "small bit of time"?  That's up to you.  Reasonable initial values for "recently" might be "4 or 5 frames", and "small bit of time" might be "1 or 2 frames."

 

If you want to go down this path, then you're basically bypassing the CONTx infrastructure in IntyBASIC and writing your own.  

Edited by intvnut
very slight improvement to CONT_BUTTON (1 instruction)
  • Like 3
Link to comment
Share on other sites

On 1/26/2021 at 7:47 AM, Zendocon said:

Of course, I know that looking at key presses involves looking at Bits 5-7.  But I'm curious why you're putting Bit 15 into these Functions.

 

Does it have to do with the Sign/Carry bits in the CPU?  I know $1ff and $1fe are actually 16-bit, being lumped with the sound chip.

It's possibly simpler than you're probably thinking.  For one thing, IntyBASIC doesn't give you direct access to any of the flag bits.  I have to make due with whatever comparisons it offers me.  That's a hint...

 

Here's my code again for reference:

On 1/25/2021 at 10:12 AM, intvnut said:

DEF FN KEY_ON_CONT1 = ((PEEK($1FF) AND (PEEK($1FF) - $20)) + $7FC0) < $8060 
DEF FN KEY_ON_CONT2 = ((PEEK($1FE) AND (PEEK($1FE) - $20)) + $7FC0) < $8060 
DEF FN NOKEY_ON_CONT1 = ((PEEK($1FF) AND (PEEK($1FF) - $20)) + $7FC0) >= $8060 
DEF FN NOKEY_ON_CONT2 = ((PEEK($1FE) AND (PEEK($1FE) - $20)) + $7FC0) >= $8060

 

 

I'm applying three tricks.

  1. The construct (X AND (X - 1)) gives you the value of X after clearing the rightmost 1 bit. 
    • For example, $AA becomes $A8, and $F0 becomes $E0.  The number 0 remains 0. 
    • If you replace 1 with a power of 2, it removes the rightmost set bit at or above that power of 2.  So, X AND (X - $20) removes the rightmost set bit at bit position 5 or above; the lower bits remain untouched.  $FF becomes $DF.  $E5 becomes $C5.  $1F remains $1F.
  2. I operate on the inverted keypad input: 1 means no input and 0 means input.
    • I know bits 15:8 are always zero.
    • I know bits 7:5 are 111 when nothing is pressed, and one of 001, 010, 100, or 000 when action button(s) are pressed.
    • I know bits 7:5 are 110, 101, or 011 when keypad is pressed.
    • Based on this I was able to construct a truth table, which I'll include below.
  3. You can perform a range comparison with a single subtract and compare, rather than two comparisons.  This cuts the cost of a range comparison almost in half.
    • In C, I would do this using unsigned arithmetic, by subtracting "min" from the value I'm checking so the range effectively starts at 0.  Values below the range become large positive values.  I then compare the value against "max - min", and get both halves of the comparison in one step.  That is, with unsigned arithmetic, I can test min ≤ value < max with (value - min) < (max - min).
    • In IntyBASIC, the comparison is signed, unless I use an unsigned variable; however, I'm not using any variables.  I can do the same trick, but I have to bias all the values by $8000 to make it work with signed arithmetic.  If you add $8000 to $7FC0 and $8060, you'll get -$40 and +$60.  ($FFC0 = -$40).  That means I'm looking for $40 ≤ value < $A0.  (Remember, $40 + $60 = $A0.)

 

So this is the truth table I mentioned above:

 

'Truth table on bits 7:5 of the raw controller input:
'(000 - 001) AND 000 = 000  two buttons?
'(001 - 001) AND 001 = 000  button
'(010 - 001) AND 010 = 000  button
'(011 - 001) AND 011 = 010  keypad
'(100 - 001) AND 100 = 000  button
'(101 - 001) AND 101 = 100  keypad
'(110 - 001) AND 110 = 100  keypad
'(111 - 001) AND 111 = 110  nothing

 

As you can see, among the 8 combinations, only two outputs, 0102 and 1002, correspond to the keypad.  So, numbers in the range 010000002 - 100111112 theoretically could correspond to a keypad key, whereas everything else isn't.  This range is slightly over-broad, but sufficiently tight for our purposes here.  We could always tighten the range.   (Note:  The subscript 2 just means "binary.")

 

So, 010000002 is $40, and 100111112 is $9F.  I have a keypad press if $40 ≤ value ≤ $9F.  Or, equivalently, $40 ≤ value < $A0.  Ta da.

 

  • Like 3
Link to comment
Share on other sites

Adding to the ASCII art and diagrams:   Here's the diagram I personally refer to when I need to look up how bits map to disc and keypad inputs.  This diagram appears in jzintv/examples/task/scanhand.asm, FWIW.

 

;;               DISC    ACTION  KEYPAD                                     ;;
;;       Bit 0:  Down            Row 0 keys --------- 1    2    3           ;;
;;       Bit 1:  Right           Row 1 keys --------- 4    5    6           ;;
;;       Bit 2:  Up              Row 2 keys --------- 7    8    9           ;;
;;       Bit 3:  Left            Row 3 keys --------- C    0    E           ;;
;;       Bit 4:  Corner                               |    |    |           ;;
;;       Bit 5:            T/L   Col 2 keys --------- | -- | ---+           ;;
;;       Bit 6:            L/R   Col 1 keys --------- | ---+                ;;
;;       Bit 7:            T/R   Col 0 keys ----------+                     ;;

 

"Corner" refers to diagonals on the DISC.  It disambiguates between different diagonals.  You can see why I called it that in this image I created eons ago.

 

intv_pad.png.a24e56da7ca4fec1b2190254d93812f7.png

 

 

I think I'm the only person who doesn't think that diagram is ugly.  YMMV.  The four short spans associated with bit 4 are all on diagonals, which is why I called that bit "corner."  Maybe "diagonal" would be a better term?

 

Bit 4 is only set by the DISC, and never set by the keypad or action buttons.

 

The IntyBASIC up/down/left/right test bits 0, 1, 2, 3 directly, and I believe ignore bit 4.

 

 

  • Like 2
Link to comment
Share on other sites

2 hours ago, intvnut said:

"Corner" refers to diagonals on the DISC.  It disambiguates between different diagonals.  You can see why I called it that in this image I created eons ago.

 

intv_pad.png.a24e56da7ca4fec1b2190254d93812f7.png

 

 

I think I'm the only person who doesn't think that diagram is ugly.  YMMV.  The four short spans associated with bit 4 are all on diagonals, which is why I called that bit "corner."  Maybe "diagonal" would be a better term?

 

Bit 4 is only set by the DISC, and never set by the keypad or action buttons.

 

The IntyBASIC up/down/left/right test bits 0, 1, 2, 3 directly, and I believe ignore bit 4.

 

 

I also don't think it's ugly either, so we are two.  I used that diagram myself to understand the bit pattern when building the Christmas Carol disc heuristics. :thumbsup:
 

I always thought it was very good at depicting the bits.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

Wow. Excellent explanation guys. I had to go through it a couple of times, but it's very interesting. ?

 

Thanks as well for the example as I couldn't wrap my head around it at first. I'm still playing around with it and will report back with (hopefully) success news or more questions.

Link to comment
Share on other sites

When I developed IntyBASIC in 2014 I intended to put proper readings of CONT1.BUTTON, CONT1.LEFT and so. But for some reason it keep that way until today (talking about delays! Six years and counting!)

 

I think I can enhance my current implementation without breaking old code (and finally we reached that point where I'm worried about changes to the language to not break compatibility with so many games written!!! XD)

Edited by nanochess
  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...