ugBASIC User Manual

I/O control support

This section clarifies all aspects of controlling a program by hardware. So we are exploiting the joystick and the keyboard in your programs.

Using the joystick

A joystick can be used to control movement around the screen by pushing its handle in the desired direction, and to trigger all sorts of actions by pressing one or more buttons built in to its mechanism. Either of the two (or four) joystick sockets at the back or side of your computer will happily accept a joystick plug.

If two/four users want to control one joystick each for specially written programs, all ports can be used.

To make a joystick interact with your programs, the computer must be able to read its movements and actions. The ugBASIC language offers a number of useful functions to do just that.

The JOY() command inspects what is happening with the joystick and makes a report. The maximum number of joysticks installed in the system can be retrieved using the JOY COUNT constant. If the joystick you are interested in is plugged into any joystick port, the computer must be told to look at that port number. For example:

DO
   j=JOY(1)
   PRINT BIN$(j,5),j
LOOP

By running that routine, reports are given about the movements of the joystick and the status of the fire-button in the form of binary numbers. The pattern of ones and zeros in the report can then be inspected. Binary bits shown as zero indicate that nothing is happening, whereas if any of the bits in the report is shown as a one, it means that the joystick has been moved in the direction that relates to that bit.

Here is a list of those bits along with their meanings:

  • UP (0) - joystick has been moved up;
  • DOWN (1) - joystick has been moved down;
  • LEFT (2) - joystick has been moved left;
  • RIGHT (3) - joystick has been moved right;
  • FIRE (4) - fire-button has been pressed.

In ugBASIC bits can be checked in various way:

REM functional style
left = BIT( JOY(1), LEFT )
PRINT "LEFT IS: ";left

REM declarative style
up = BIT UP OF JOY(1)
PRINT "UP IS: ";up

REM conditional style
IF JOY(1) HAS BIT UP THEN : PRINT "UP" : ENDIF
IF JOY(1) IS UP THEN : PRINT "UP" : ENDIF

REM (negative) conditional style
IF JOY(1) HAS NOT BIT UP THEN : PRINT "NOT UP" : ENDIF
IF JOY(1) IS NOT UP THEN : PRINT "NOT UP" : ENDIF

Each of those aspects of the joystick status can also be accessed individually, using the following functions:
  • JLEFT - test for joystick movement towards the left;
  • JRIGHT - test for joystick movement towards the right;
  • JUP - test for joystick movement upwards;
  • JDOWN - test for joystick movement downwards;
  • JFIRE (or FIRE()) - test for joystick fire button pressed.
Every function will returns a value of TRUE (meaning-1) if the joystick connected to the given port number has been pushed to the directiong requested (or the button has been pressed), otherwise a value of 0 is returned (meaning false).

These functions can be demonstrated by the following example:

DO
   IF JLEFT(1) THEN PRINT "WEST"
   IF JRIGHT(1) THEN PRINT "EAST"
   IF JUP(1) THEN PRINT "NORTH"
   IF JDOWN(1) THEN PRINT "SOUTH"
   IF JFIRE(1) THEN CENTRE "BANG!"
LOOP

In addition to these commands, there are some very useful instructions to implement rather frequent mechanisms. For example, to wait for the FIRE button to be pressed on the joystick, you can use the WAIT FIRE command.

Asynchronous vs syncronous readings

Available on (both, async, sync):
atari atarixl c128 c128z c64 coco coco3 d32 d64 coleco cpc msx1 sc3000 sg1000 vic20 zx

The ugBASIC language generally provides two ways to read the joystick position.

The first, which is the most precise and timely, is the so-called synchronous readings. In this case, the value of the joystick position is read at the exact moment in which it is requested. The advantage of this technique is that you get the instantaneous position of the joystick, and it is less demanding on the computer. The disadvantage is that, if you are not regular in the readings, you can get irregular movements.

On the contrary, the asynchronous readings uses the so-called "interrupts" to perform the reading. In other words, at regular intervals the execution is interrupted and the position of the joystick at that moment is read. The program, when it requests the joystick position, actually gets the last known position. The major disadvantage of this technique is that it can be demanding on the computer, depending on the target. However, it has the undoubted advantage of ensuring regularity in the readings over time, and so you can get regular movements.

Both modes can be selected with a specific pragma:

Using the Keyboard

The keyboard can be used to interact with your routines once they are running. This is vital for any sort of arcade game, adventure gaming or for more practical items such as word processing.

The INKEY$ function checks to see if a key has been pressed, and reports back its value in a string. For example:

DO
   k = INKEY$
   IF k<>"" THEN : PRINT "YOU PRESSED A KEY!" : ENDIF
LOOP

The INKEY$ function does not wait for you to input anything from the keyboard, so if a character is not entered an empty string is returned. INKEY$ can only register a key-press from one of the keys that carries its own ASCII code, and the ASCII code numbers that represent the characters which can be printed on the screen.

It has also been explained that special keys and the function keys do not carry as ASCII code at all, and if INKEY$ detects that this type of key has been pressed, a character with a value of zero will be returned. When this happens, the internal “scan codes” of these keys can be found.

The function SCANCODE returns the internal scan code of a key that has already been entered using the INKEY$ function. The next example may be tested by pressing the function keys, F1 and F2.

DO
   WHILE k == ""
      k = INKEY$
   WEND
   IF ASC(k)==0 THEN PRINT "NO ASCII CODE"
   PRINT "THE SCAN CODE IS ";SCANCODE
   k = ""
LOOP

To determine if keys are pressed at the same time as either or both of the SHIFT keys, the SCANSHIFT function returns the following values:

  • NO SHIFT (0) - if no SHIFT key pressed;
  • LEFT SHIFT (1) - if the left SHIFT pressed;
  • RIGHT SHIFT (2) - if the right SHIFT pressed;
  • BOTH SHIFTS (3) - if both keys pressed.
LEFT SHIFT and RIGHT SHIFT are bitmask, you can use the previus syntax:

REM functional style
left = BIT( SCANSHIFT, LEFT SHIFT )
PRINT "LEFT IS: ";left

REM declarative style
left = BIT LEFT SHIFT OF SCANSHIFT
PRINT "LEFT IS: ";up

REM conditional style
IF SCANSHIFT HAS BIT LEFT SHIFT THEN : PRINT "LEFT" : ENDIF
IF SCANSHIFT IS LEFT SHIFT THEN : PRINT "LEFT" : ENDIF

REM (negative) conditional style
IF SCANSHIFT HAS NOT BIT LEFT SHIFT THEN : PRINT "NOT LEFT" : ENDIF
IF SCANSHIFT IS NOT LEFT SHIFT THEN : PRINT "NOT LEFT" : ENDIF

Try out the following example by pressing various keys, in combination with the SHIFT keys:

DO
   a = INKEY
   s = SCANSHIFT
   IF s <> 0 THEN
      PRINT s
   END IF
LOOP

Use the KEY STATE function to check whether or not a specific key has been pressed. The relevant scan code should be enclosed in brackets, and when the associated key is being pressed KEY STATE will return a value of TRUE (-1), otherwise the result will be given as FALSE (0). For example:

DO
   IF KEY STATE(KEY F1) == TRUE THEN : PRINT "F1!" : ENDIF
   IF KEY STATE(KEY RUNSTOP) == TRUE THEN : PRINT "RUN STOP!" : ENDIF
LOOP

On the same way, KEY SHIFT is used to report the current status of those keys which cannot be detected by either INKEY$ or SCANCODE because they do not carry the relevant codes. These control keys cannot be tested individually, or a test can be set up for any combination of such keys pressed together. A single call to the KEY SHIFT function can test for all eventualities, by examining a bit map in the following format:

  • LEFT SHIFT (0) - if the left SHIFT is pressed;
  • RIGHT SHIFT (1) - if the right SHIFT is pressed;
  • CAPS LOCK (2) - if it is on or off
  • CTRL (3) - if the CTRL is pressed;
  • LEFT ALT (4) - if the left ALT is pressed;
  • RIGHT ALT (5) - if the right ALT is pressed;
If the report reveals that a bit is set to 1, then the associated key has been held down by the user, otherwise a 0 is given. Here is a practical example:

CENTRE "PLEASE PRESS SOME CONTROL KEYS"
CURS OFF
DO
   LOCATE 14,4: PRINT BIN$(KEY SHIFT, 8)
LOOP

When an appropriate character is entered from the keyboard, its ASCII code is placed in an area of memory called the keyboard buffer. This buffer is then examined by the INKEY$ function in order to report on key presses. CLEAR KEY completely erases this buffer and re-sets the keyboard, making it a very useful command at the beginning of a program when the keyboard buffer may be filled with unwanted information. CLEAR KEY can also be called immediately before a WAIT KEY command, to make sure that the program waits for a fresh key-press before proceeding.

The WAIT KEY command waits for a single key-press before acting on the next instruction. For example:

PRINT "PLEASE PRESS A KEY" : WAIT KEY : PRINT "THANK YOU!"

The INPUT$ function loads a given number of characters into a string variable, waiting for the user to enter each character in turn. Although characters will not appear on the screen, similar to INKEY$, the two instructions are totally different.

Here is an example:

CLEAR KEY : PRINT "PLEASE TYPE IN TEN CHARACTERS"
v=INPUT$(10) : PRINT "YOU TYPED: ";

The INPUT command is used to enter information into one or more variables. Any variable may be used, as well as any set of variables, providing they are separated by commas. A question mark will automatically appear at the current cursor position as a prompt for your input.

REM ASK FOR A NUMBER AND A STRING
INPUT A, K$

If your own “prompt string” is included, it will be printed out before your information is entered. Please note that a semi-colon must be used between your prompt text and the variable list, a comma is not allowed for this purpose. You may also use an optional semi-colon at the end of your variable list, to specify that the text cursor is not to be affected by the INPUT command, and will retain its original position after your data has been entered. When INPUT is executed, the program will wait for the required information to be entered via the keyboard, and each variable in the list must be matched by a single value entered by the user. These values must be of exactly the same type as the original variables, and should be separated by commas.

For example:

PRINT "TYPE IN A NUMBER"
INPUT a
PRINT "YOUR NUMBER WAS ";a
INPUT "WHAT'S YOUR NAME?";name$
LOCATE 23, : PRINT "HELLO ";name$

LINE INPUT is identical in usage to INPUT, except that is uses a press of the RETURN key to separate each value you enter via the keyboard instead of a comma. Try this:

LINE INPUT "TYPE IN THREE NUMBER";a,b,c
PRINT a,b,c

The PUT KEY command loads a string of characters directly into the keyboard buffer, and it is most commonly used to set up defaults for your INPUT routines. Note that end of line returns can be included using a CHR$(13) character. In the next example, NO is assigned to the default INPUT string.

DO
   PUT KEY "NO"
   INPUT "DO YOU WANT TO CONTINUE, YES OR NO: ";a$
   b$ = UPPER$(a$)
   IF b$ == "NO" THEN BOOM : WAIT 50 : EXIT
LOOP

Asynchronous vs syncronous readings

Available on (both, async, sync):
atari atarixl c128 c128z c64 c64reu coco coco3 d32 d64 cpc mo5 msx1 pc128op sc3000 vic20 zx

The ugBASIC language provides also for keyboard two ways to read the keys.

The first, which is the most precise and timely, is the so-called synchronous readings. In this case, the value of keyboard is read at the exact moment in which it is requested. The advantage of this technique is that you get the instantaneous key pressed, and it is less demanding on the computer. The disadvantage is that, if you are not regular in the readings, you can miss keystrokes.

On the contrary, the asynchronous readings uses the so-called "interrupts" to perform the reading. In other words, at regular intervals the execution is interrupted and the key pressed on keyboard at that moment is read. The program, when it requests any key, actually gets the last pressed. The major disadvantage of this technique is that it can be demanding on the computer, depending on the target. However, it has the undoubted advantage of ensuring regularity in the readings over time, and so you can avoid to lose keystrokes.

Both modes can be selected with a specific pragma:

Any problem?

If you have found a problem, if you think there is a bug or, more simply, you would like something to be improved, write a topic on the official forum, or open an issue on GitHub.

Thank you!