Jump to content
IGNORED

keypads and batari basic


toiletunes

Recommended Posts

Does Batari Basic currently support keypad functions?

I'd love to learn how to use them.

At present there are no builtin functions or statements to support the keypad controllers, the way there are for the joysticks and paddles. However, you can read the keypad on your own in batari Basic, and it's probably just a matter of time before commands for the keypad get added. I've been interested in the keypad controllers, too, so I'll see if I can write a routine or some example code.

 

While I'm asking stuff, does Batari Basic work with the driving controllers?

I've never had any driving controllers, and to be honest, I don't understand the difference between the driving controllers and the paddles. Can somebody explain how they're different?

 

Michael

Link to comment
Share on other sites

I've never had any driving controllers, and to be honest, I don't understand the difference between the driving controllers and the paddles. Can somebody explain how they're different?

 

Aside from the fact that they're both "round", there is no similarity between the driving controllers and the paddles.

 

The driving controllers operate by using two on-off contacts to indicate rotation. If the contacts are called A and B (they correspond to the "left" and "right" inputs on the joystick, but I'm not sure which is which) rotating the knob one click clockwise will cause contact A to get the value of B while B gets the opposite of value A (thus going through the sequence "off off / off on / on on / on off". Rotating the knob one clock counter-clockwise will cause contact B to get the value of A, while A gets the opposite of value B. Software can watch the sequence of transitions and count how many clicks the knob is turned in either direction.

 

The driving controller does not have to be read as often as the paddles, nor does it have to be read with any particularly accurate or consistent timing, provided that the maximum time between readings never exceeds the time between adjacent clicks in the same direction. If a game only reads the controller once per frame, a user who turns the knob very quickly will exceed the software's ability to accurately count it. If the screen is divided into zones that are at most 30 or so scan lines high, however, checking the controller once per zone may suffice. If RAM is available, each check need only take 7-8 cycles in the kernel (LDA SWCHA / STA temp [or temp,x]); you can check the fetched data later. If RAM is more precious than that, I'm not sure the best approach.

Link to comment
Share on other sites

Aside from the fact that they're both "round", there is no similarity between the driving controllers and the paddles.

Thank you for the explanation. I'm sure coding examples must be out there in the Stella archives, but since I've never had any driving controllers, I've never studied how to read them. :)

 

Michael

Link to comment
Share on other sites

Yes quite different. A paddle is giving you a analog voltage level in

digital form. The paddle is a potentiometer. 0- MAX resistance, that

the VCS circuitry converts to digital data. The driving controller

uses switches and either grey or quadrature encoding to determine

which direction the knob is being turned and even how fast.

 

The other MAJOR difference is a paddle is finite....it has an upper

and lower limit. With quadrature encoding its an eternal loop(if

you so desire it to be)and makes for a much nicer and smoother

analog stick....I hope to prove that with one of my Jaguar releases.

 

Also the other advantage of quadrature and encoders is you do not

need an A to D converter but only two input bits. All game systems

have at least two input bits on their controller ports and all can use

an encoder to simulate a paddle or analog device. The newer xgen

consoles are using USB like controller ports but no problem. Mice

and USB work great together. Oh a mouse is two encoders, one for

x axis and one for y axis. It's like two high rez driving controllers.

 

Here...some extra tidbits.

 

http://en.wikipedia.org/wiki/Rotary_encoder

 

 

:D

Edited by Gorf
Link to comment
Share on other sites

I've been interested in the keypad controllers, too, so I'll see if I can write a routine or some example code.

 

I've considered writing a Keypad routine as well. My thought was to stick the reading code in the VBLANK routine, and store the results in the variables used by the paddle routine to be read by the program. Don't know when I'll get to it though.

 

Or we could collaborate...

Link to comment
Share on other sites

I've been interested in the keypad controllers, too, so I'll see if I can write a routine or some example code.

 

I've considered writing a Keypad routine as well. My thought was to stick the reading code in the VBLANK routine, and store the results in the variables used by the paddle routine to be read by the program. Don't know when I'll get to it though.

 

Or we could collaborate...

The "Stella Programmer's Guide" says that a delay of 400 microseconds is needed between sending a signal to SWCHA and reading INPT0, INPT1, and INPT4 to see which key is being pressed. If I did my math correctly, I think that translates into a delay of about 6.28 scan lines, or setting TIM8T to 60 intervals. Since there are 4 rows that need to be checked, that means it would take slightly more than 25 scan lines to check all 4 rows-- let's say about 26 or 27 scan lines, since it takes time to set the timer, check the timer, read the INPT0/1/4 registers, and store the results. So it will take most of the "VBLANK" time to read one keypad controller. And since there are 12 buttons, it will take at least 2 bytes to store the results. Thus, only one keypad controller per frame could be checked this way, which should be okay-- so that sounds like a plan!

 

Michael

Link to comment
Share on other sites

And since there are 12 buttons, it will take at least 2 bytes to store the results. Thus, only one keypad controller per frame could be checked this way, which should be okay-- so that sounds like a plan!

 

Why check only one at a time? Start out by setting all rows low on both controllers and leave them "idle" in that configuration. Then you should be able to do something like:

 lda INPT0
 and INPT1
 and INPT4
 bpl got_left_key
 lda INPT2
 and INPT3
 and INPT5
 bpl got_right_key

Link to comment
Share on other sites

Why check only one at a time? Start out by setting all rows low on both controllers and leave them "idle" in that configuration. Then you should be able to do something like:

 lda INPT0
 and INPT1
 and INPT4
 bpl got_left_key
 lda INPT2
 and INPT3
 and INPT5
 bpl got_right_key

I didn't realize you could do that. The "Stella Programmer's Guide" seems to say that you write to SWCHA to set the row you want to read, and then check INPT0, INPT1, and INPT4 to see which key was pressed in that row. It doesn't say anything about reading INPT2, INPT3, and INPT5. :ponder:

 

The SPG is short on examples, so I guess the thing to do is disassemble a program that uses the keypad controllers, and see how it reads them.

 

Michael

Link to comment
Share on other sites

Cool! In 2600 terms, this may indeed be inefficient but it still a far cry simpler than a Jaguar DSP version I wrote which was kind of a PITA.

However it works quite well. Reading the Jaguar Ports is an operating system in itself. You have to read one row of inputs at a time. So that is 6 reads, then you have to rotate the read and on and on. I miss the old days when one read was usually enough to get the info you needed.

Link to comment
Share on other sites

Here's a project I didn't get very far on in 2004 - Dragon Defense Squad - I did get the keyboard to work.

Thank you, thank you, thank you! I already see something I was doing wrong. When you write to SWCHA for the row you want to read, I was writing a value of 1 to the bit, but apparently you're supposed to write 0 to that bit and 1s to the other bits? (As I said, the SPG is short on examples-- as in, zip, zilch, nada, nosirree-- and in some cases the explanations are so vague that they don't actually "explain" anything! :ponder:)

 

I think I can get my bB keypad controller routine working now. :)

 

Michael

Link to comment
Share on other sites

Here's a project I didn't get very far on in 2004 - Dragon Defense Squad - I did get the keyboard to work.

Thank you, thank you, thank you! I already see something I was doing wrong. When you write to SWCHA for the row you want to read, I was writing a value of 1 to the bit, but apparently you're supposed to write 0 to that bit and 1s to the other bits? (As I said, the SPG is short on examples-- as in, zip, zilch, nada, nosirree-- and in some cases the explanations are so vague that they don't actually "explain" anything! :ponder:)

 

I think I can get my bB keypad controller routine working now. :)

 

Michael

Cool :cool:

 

In order to maximize the utility of such a routine in the bB environment, I think it would be wise to write the routine as a bB function, taking the controller number as an argument (0 or 1 for L/R) and have it return the number pressed (0-9 for their respective numbers, 10 for *, 11 for #, and 12 for nothing pressed.)

 

This would make it ideal to use a data table or "on ... goto" for the value returned, and/or allow the number to be used for a calculation of some kind.

Link to comment
Share on other sites

Okay, I finally got my keypad controller routine to work. I'm sure it could be done a lot better, and I intend to optimize it before putting it into a routine that could be included in a batari Basic program-- e.g., by adding "include keypads.asm" and "dim keypads = a" (or some other user variable) to the program. One way it can be optimized is by replacing the "sleep" statements with less ROM-hungry methods of wasting 476 cycles.

 

Right now, the routine reads both keypad controllers during the vblank period-- so that rules out any possibility of using the vblank period for other things. And even if someone wanted to customize the routine so they can squeeze other tasks into the four lengthy "sleep" periods, the routine uses the accumulator and the Y register for storing the left and right key presses, so any other tasks that are squeezed into the "sleep" periods would be able to use only the X register-- unless the routine were rewritten to use X and Y, such that only the accumulator could be used for other tasks. Anyway, the key presses that are stored in A and Y get combined at the end of the routine, and are stored in the variable "keypads" (which must be set to one of the user variables using a "dim" statement). When the routine ends, the low nybble of "keypads" will contain the key pressed on the left keypad, and the high nybble will contain the key pressed on the right keypad, as follows:

 

$00 = nothing pressed

$01_$02_$03__________$10_$20_$30
$04_$05_$06__________$40_$50_$60
$07_$08_$09__________$70_$80_$90
$0A_$0B_$0C__________$A0_$B0_$C0

If two keys are pressed on the same controller, the higher-valued key will take priority-- e.g., if 1 and 7 are pressed at the same time, then 7 will override 1. However, a key pressed on one keypad will *not* override a key pressed on the other keypad-- e.g., if 1 is pressed on the left keypad, and 7 is pressed on the right keypad, then both key presses will be returned.

 

When I optimize the routine and put it into an includes file, I'll also add a function that can be called to more easily identify the key that was pressed on a given keypad, so the includes file will contain both the vblank routine and the function.

 

The "keypad_test.bas" program displays two 3-by-4 grids of squares that represent the keys on the left and right keypads. As you press a key on either keypad controller, the appropriate square will light up to show which key you're pressing.

 

Don't forget that when you run the program in an emulator, you need to tell the emulator that the program uses the keypad controllers in the left and right ports. In the Stella emulator, the keys on the left and right keypads are controlled (or emulated) by the following keys on the keyboard:

 

1_2_3__________8_9_0
Q_W_E__________I_O_P
A_S_D__________K_L_;
Z_X_C__________,_._/

I want to extend sincere gratitude to the following people:

 

-- tolietunes, for asking the question that prompted me to finally try this;

-- CurtisP, for suggesting that the keypads be read during vblank;

-- supercat, for pointing out that both keypads can be read at the same time;

-- SpiceWare, for providing a working example of actual code; and

-- batari, for suggesting the idea of a function (which I haven't done yet).

 

And to answer atari2600land's question-- yes, I think the kids' controllers are identical to the keypad controllers as far as functionality and programming are concerned.

 

If anyone can see ways of improving the vblank routine, *please* post them, so I can modify the routine before posting it as a "keypads.asm" includes file.

 

Michael

 

post-7456-1177653287_thumb.png

 

post-7456-1177653319_thumb.png

keypad_test.bas

keypad_test.bas.bin

  • Like 1
Link to comment
Share on other sites

$00 = nothing pressed

$01_$02_$03__________$10_$20_$30
$04_$05_$06__________$40_$50_$60
$07_$08_$09__________$70_$80_$90
$0A_$0B_$0C__________$A0_$B0_$C0

Combining both keyboards into a single byte is a clever idea as it requires just one pass of the function to get two reads. For that matter, it kind of obviates the need for a function altogether, as you don't need to pass any arguments. It might not be obvious to beginners how to separate the nybbles, so that would be covered in the documentation.

 

Though I'm not sure about the way the keys are encoded. 1-9 map to their respective keys but $0B maps to the 0 key here. While it does make sense to map $00 to nothing pressed, it also makes sense to map $00 to key 0. So what do others think is better?

Link to comment
Share on other sites

I remember using those keyboards to input the one and only

MagiCard program i ever used that thing for. I think those could

have served a game like 'Adventure' pretty well.

For many years, I've been planning an adventure game (or trilogy) called "Quest for the Lost Pyramids of Atlantis," and I think the ideal situation would be to use a joystick in the left port for controlling the player's movement, and a keypad in the right port (or vice versa) for selecting items in the inventory strip, or selecting actions (e.g., pick up, drop, buy, sell, talk, etc.) in the action strip.

 

However, I'd also like to use the AtariVox or MemCard/SaveKey for saving games, so I'm not sure how that could be worked out, since there's no way to have a keypad and the SaveKey plugged into a port at the same time. :( I guess one solution would be to switch to a "save game" or "load game" menu screen using the keypad controller (or perhaps using the "select" console switch), and then letting the player unplug the keypad, plug in the SaveKey, save or load the game, and then plug in the keypad controller again before switching back to the game screen. :ponder:

 

Michael

Link to comment
Share on other sites

Combining both keyboards into a single byte is a clever idea as it requires just one pass of the function to get two reads. For that matter, it kind of obviates the need for a function altogether, as you don't need to pass any arguments. It might not be obvious to beginners how to separate the nybbles, so that would be covered in the documentation.

 

Though I'm not sure about the way the keys are encoded. 1-9 map to their respective keys but $0B maps to the 0 key here. While it does make sense to map $00 to nothing pressed, it also makes sense to map $00 to key 0. So what do others think is better?

I was thinking that a function is still a good idea, because it makes it easier for beginners to use the keypads without having to worry about bit operations. And the function could return different values than the ones that the vblank routine uses, such as changing $0B to a returned value of 0. I'd even like to make the return values "programmable," probably using some sort of user-defined lookup tables that convert the "internal" values into "returned" values. For example, having keys 1, 2, and 3 on the top row may make sense for a program that uses a "telephone buttons" type of arrangement, but it does *not* make sense for a calculator type of program. :)

 

Michael

Link to comment
Share on other sites

Right now, the routine reads both keypad controllers during the vblank period-- so that rules out any possibility of using the vblank period for other things. And even if someone wanted to customize the routine so they can squeeze other tasks into the four lengthy "sleep" periods, the routine uses the accumulator and the Y register for storing the left and right key presses, so any other tasks that are squeezed into the "sleep" periods would be able to use only the X register-- unless the routine were rewritten to use X and Y, such that only the accumulator could be used for other tasks.

I was just thinking that I could code this in such as way that four "mini-vblank" routines could be performed (if defined in the program) while the keypad-reading routine is waiting during the four lengthy "sleep" periods. And I could store A, X, and Y in temp variables before JSR-ing to the four mini-vblank routines, and restore them upon returning to the keypad routine, so that all three could be used by the mini-vblank routines. This is going to be fun to do! :)

 

Michael

Link to comment
Share on other sites

Okay, I finally got my keypad controller routine to work. I'm sure it could be done a lot better, and I intend to optimize it before putting it into a routine that could be included in a batari Basic program-- e.g., by adding "include keypads.asm" and "dim keypads = a" (or some other user variable) to the program. One way it can be optimized is by replacing the "sleep" statements with less ROM-hungry methods of wasting 476 cycles.

 

One of my ideas was to use the paddle variables for the keyboard. However, since they are shared with missile0, this would require the kernel_option no_blank_lines to be set. I think I like the idea of dimming the variable better.

 

The way I would have used vblank was to spread the reading of the controller out over multiple vblanks. Set the output register in one and read it in the next. The drawscreen in between would give you plenty of microseconds. This would require two variables, one for a counter and one for the results. The results variable would just stay 0 until the keypad read was done.

 

Another possible issue is that this code forces both controllers to be a keypad, whereas some people would like to use a keypad plus a joystick.

 

All that said, it's fantastic that you got the reading code to work!

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...