PuzZLeR Posted January 25, 2021 Share Posted January 25, 2021 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! Quote Link to comment Share on other sites More sharing options...
+nanochess Posted January 25, 2021 Share Posted January 25, 2021 You need to use something like this. c = CONT1 d = c AND $E0 IF (d = $80) + (d = $40) + (d = $20) THEN ' Just ignore keypad ELSE ' Process anything else END IF 2 Quote Link to comment Share on other sites More sharing options...
intvnut Posted January 25, 2021 Share Posted January 25, 2021 (edited) 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 January 25, 2021 by intvnut improve the code further 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted January 25, 2021 Share Posted January 25, 2021 Bonus points to anyone who figures out how my functions above work. I can post more details later, but it might be fun to work it out for yourselves first. 1 Quote Link to comment Share on other sites More sharing options...
Zendocon Posted January 26, 2021 Share Posted January 26, 2021 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. 1 Quote Link to comment Share on other sites More sharing options...
PuzZLeR Posted January 27, 2021 Author Share Posted January 27, 2021 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. Quote Link to comment Share on other sites More sharing options...
mr_me Posted January 27, 2021 Share Posted January 27, 2021 (edited) 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 January 27, 2021 by mr_me 1 1 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted January 27, 2021 Share Posted January 27, 2021 (edited) 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 January 27, 2021 by DZ-Jay 2 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted January 27, 2021 Share Posted January 27, 2021 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. 1 Quote Link to comment Share on other sites More sharing options...
Peripheral Posted January 27, 2021 Share Posted January 27, 2021 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 1 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted January 27, 2021 Share Posted January 27, 2021 (edited) 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 January 27, 2021 by intvnut very slight improvement to CONT_BUTTON (1 instruction) 3 Quote Link to comment Share on other sites More sharing options...
intvnut Posted January 27, 2021 Share Posted January 27, 2021 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. 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. 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. 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. 3 Quote Link to comment Share on other sites More sharing options...
intvnut Posted January 27, 2021 Share Posted January 27, 2021 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. 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. 2 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted January 28, 2021 Share Posted January 28, 2021 (edited) 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. 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. I always thought it was very good at depicting the bits. Edited January 28, 2021 by DZ-Jay 1 Quote Link to comment Share on other sites More sharing options...
PuzZLeR Posted January 28, 2021 Author Share Posted January 28, 2021 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. Quote Link to comment Share on other sites More sharing options...
+nanochess Posted January 28, 2021 Share Posted January 28, 2021 (edited) 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 January 28, 2021 by nanochess 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.