GBASIC and MONitor Keywords

Whither the great unknown? Plunge in henceforth, brave adventurer. There be dragons here.

Updates and corrections are gratefully accepted. Last modify 13 April 2016.

Back to "How To Program In GBASIC" | Back to "Programming the Tomy Tutor" | Back to the main page

[Tutti GBASIC]
Tomy GBASIC, as shown in the original Tutti.

Historically, the major problem has been no surviving significant documentation of GBASIC (that we know of), so there's no comprehensive description of its keywords and capabilities available. This incomplete treatise is based upon experimentation and derivation from the manual's sparse GBASIC programming examples as well as careful bytewise analysis of tape dumps from XTOMYDEV.

MONitor Commands

I guess you could call this direct mode ... ?

All MONitor commands are four characters long. Any trailing characters are ignored (e.g., GBASIC == GBAS).

Command Description
BASI Switches to regular Tomy BASIC mode (on those machines [American Tutors and upgraded Grandstand Tutors] that support it).
DIRC Undocumented and does not seem to work on English GBASIC systems, but early Japanese GBASIC had a katakana command 'oshiete' ('listing'). Junya notes this command in his table, but it does not work on either of my American systems.
EDIT Enters the GBASIC editor, keeping any program in memory resident.
GBAS Enters the GBASIC editor and clears any program in memory.
GRAP Exits the monitor. This seems to clear any program in memory, so save it first.
GRUN Starts a program in memory (if one does not exist, it generates a COMD ERR).
LOAD Loads a file from tape. You are prompted for the filename.
MENU Returns to the main menu.
SAVE Saves a file to tape. You are prompted for the filename. This saves screen, sprite and GBASIC data, all in one piece.
STEP Undocumented. Enables step-by-step traced execution. Each line is displayed in green one line up from the bottom. Step through the code with the RT key.
VERI Verifies a file on tape. You are prompted for the filename.

GBASIC Program Structure

In these descriptions, LSB and MSB refer to least and most significant byte (low and high byte to you 6502 freaks) of a 16-bit quantity, respectively. The 9995 is big-endian, so the MSB is the lower of the two byte addresses of a 16-bit word.

GBASIC has an rather unusual memory format. Program lines are encoded in VDP RAM as groups of 8-byte octets. Each octet has a two-byte header, either the line number (a 16-bit big endian short with the high bit set) or two nulls indicating a "continuation" from the previous octet. The variable table starts at byte offset 1600 in the 1960 bytes available for GBASIC programs, so there can be a maximum of 200 octets and thus (at least in theory) 200 lines. Lines are padded out to the full octet multiple with nulls, which is terribly inefficient for storage, but seems to be a concession to allow faster line editing.

Each GBASIC line has a "leader token" which immediately follows the line number in the first octet. Only certain keywords are allowed in this position. The low nybble is the actual token; the high nybble is the total number of octets in the line. If the high nybble is greater than 1, then various continuation octets (with null line numbers) follow. Multiple statements per line are not permitted. The last line is always an END token.

There is no explicit line delimiter since the end of a program line can always be determined from its internal structure.

The program variable table starts at offset 1600 (0x640). It consists of six byte entries, with the four byte variable name (space padded) followed by a 16-bit reference count. The actual variable type is encoded in the program text. All variables are referenced by index into this variable table, which saves valuable space within the octets. Under this scheme, 60 unique variable names are available.

Keyword List

Leader tokens are shown as $xy where y is the salient portion and x is the variable number of octets, except for those where the octet number is fixed (STOP, END, etc.). Other tokens are displayed as the entire byte. Some tokens mean different things depending on the context: $1A as the leader token is END, but $1A in the left side or right side of a LET statement is ANIM.

Many keywords share a common 16(?)-byte work area in VDP RAM for their manipulations. This is important not only from an interference point of view, but it also indirectly allows more direct control over screen data; with a little thought, clever ordering of keyword sequences can allow arbitrary bytes to be written to VDP RAM (see CELL for an example).

In general, entering an expression or an unexpected or out-of-range value where a literal is expected causes an entry error.

Keyword Token Description
ANIM a$1A When used on the right side of an assignment (i.e., LET ...=ANIM ...), it returns a 16-bit quantity corresponding to the X (LSB) and Y (MSB) position of sprite number a (1-4). When used on the left, it acts as a variable reference to one of the same four sprites. (ANIM expr is illegal and causes an entry error; a must be an integer literal.)

This can be used to grab a sprite location e.g. LET X=ANIM 3 or to put two sprites at the same place e.g. LET ANIM 3=ANIM 4 or to put a sprite at a particular position using the POST operator e.g. LET ANIM 4=POST(Y,X) to specify an ordered pair. This program moves sprites 3 and 4 together with controller 1 (based on page 44 of the manual):

5 LET X=1
6 LET Y=41
210 KEY 1 J,K
220 IF J=0 THEN GOTO 210
230 IF J>5 THEN GOTO 260
240 LET X=X+1
250 GOTO 270
260 LET X=X-1
290 GOTO 210
1000 END
Expressions like ANIM 4=3+(X*3) are legal (though, oddly, ANIM 4=(X*3) causes a runtime error), as is ANIM 4=POST(3,4)+2 (puts the sprite at 6,3), and it can also be used on the right-hand side X=(ANIM 3)+4*5 of an expression as well.

See the POST operator for how the sprite coordinate system works.

CELL(a)$1B When used on the right side of an assignment (i.e., LET ...=CELL(...)), it loads the 16(?)-byte image of 8x8 screen cell a (1-768) into the workspace. The last eight bytes consist of the bit pattern in the cell, and the first eight consist of the colour data. a may be an expression. When used on the left side, it acts as a reference for that same screen cell. (If a is less than one or a is greater than 768, a blank black cell is returned.)

The documented use in the manual is for simple copying of screen data using assignment operations LET CELL(5)=CELL(6), such as this loop which makes all cells the same as cell 1:

10 FOR 30 A=2 TO 768
20 CELL(A)=CELL(1)
1000 END
However, clever manipulation of the workspace means we're not limited to simply copying what's on screen already. Because string and integer operations affect only part of the workspace area, they can be thus used to change portions of a screen cell independently. To wit,
10 X=CELL(5)
30 CELL(6)=E$
1000 END
This code fragment copies the bit pattern from cell 5 (X is just thrown away), but replaces its colour data with the colour set represented by the ASCII values of string E$, and copies the hybrid cell to cell 6.

By the same token (no pun intended), it is also possible to set the bitmap data, using a slightly different idiom:

20 CELL(5)=E$
1000 END
or set them simultaneously:
20 CELL(5)=E$
30 X=CELL(5)
40 CELL(5)=E$
1000 END
Cells cannot be compared in IF-THEN using the CELL operator overtly (e.g., IF CELL(5)=X and IF CELL(5)=CELL(6) both cause entry errors). While using CELL in an expression like A=CELL(85)+3 causes no entry error, it causes a runtime error.
END$1A Standard BASIC, with the added side effect of terminating the editor and returning to the MONitor when this command is entered as a program statement. If you just want to enter a command to halt the program's execution and are not finished in the editor, try STOP.
FOR next var=from TO to STEP step, NEXT$x3, $13, $14, $14 Standard BASIC, with the addition of the next parameter, which tells GBASIC the line number where the NEXT is for this loop, e.g. (same as the initial CELL example),
10 FOR 30 A=2 TO 768
1000 END
Note that the next parameter isn't for compile mode, but rather for entry mode. If you enter a NEXT on a line that hadn't been previously declared as a NEXT in a preceding FOR statement, an entry error results (so NEXT-w/o-FOR is thus handled during entry mode, not runtime mode).

Loops are nestable. Unlike most BASICs, however, NEXT never takes a variable as an argument (entry error). It does, however, have the target line number encoded in it as if it were a GOTO (q.v.).

from, to and step must all be integer literals: expressions generate an entry error.

STEP doesn't seem to be a real keyword, as it never appears in listings. However, it does have an internal token (the same as NEXT's), and when the program is listed out, every FOR has a step value listed at the end (it simply defaults to 1). It is optional during entry.

GOTO$11 Standard BASIC. Serious undocumented crash bug: a GOTO pointing to a non-existent line number causes the computer to lock up during compile mode!!! There is no runtime error for undef'ed statement error because GBASIC never gets that far! This bug is also present in GSUB and THEN.

Calculated branches (GOTO A, GSUB A, etc.) cause entry errors.

The line number is encoded as a big-endian 16-bit integer with the most significant bit set, as is true for all branching instructions.

GSUB, RTN$15, $16 Same as GOSUB and RETURN in standard BASIC. Serious undocumented crash bug in all branching instructions; see GOTO!! RTN-w/o-GSUB causes a runtime error. The line number is encoded as a big-endian 16-bit integer with the most significant bit set, as is true for all branching instructions.
HORZ(a)$1D Undocumented. Returns the X coordinate of a 16-bit sprite position quantity (e.g., HORZ(ANIM 3) gives the X position of sprite 3). See POST for the sprite coordinate system. a can be an expression, and HORZ can be used in expressions too (basically a rapid equivalent to taking the LSB of a 16-bit integer).
IF, THEN$x2 Standard BASIC, but with a limited comparison syntax; IF can only compare variables with variables, or one literal with one variable (IF 5=3, bizarrely, generates an entry error). Worse, you cannot do string comparisons (either literal vs. variable, or variable vs. variable). However, IF is the only keyword that can read the current value of a timer (see TIME). Comparators are limited to = > < <> >= and <= as all others generate an entry error.

THEN can only branch (e.g., IF A=5 THEN PRNT 3, "OK" generates an entry error); you can say THEN GOTO but the superfluous GOTO is (perspicaciously) dropped. Interestingly enough, there isn't a THEN token internally -- it's merely implied. There is no ELSE. Serious undocumented crash bug in all branching instructions; see GOTO!!

The line number is encoded as a big-endian 16-bit integer with the most significant bit set, as is true for all branching instructions.

KEY a b,c$x8 Reads controller a (1 or 2) and places the joystick direction value into variable b and the button value into variable c. See ANIM for an example. a must be an integer literal, and unlike BASIC, you cannot use KEY 0 to read the keyboard (bummer); using a variable or using 0 (or 3+) will generate entry errors. With a single joystick, it is read using either controller value.

The directional values are arranged clockwise 1-8, starting with 1 for North, and ending at 8 for NW. The button values are 1 for SR, 2 for SL and 3 for both. Zero in either indicates no button and/or direction.

The keyboard situation is not a total loss, as some keys can be read on the controllers (the number given is the value in the variable):

  • (Controller 1)
    • Button 1: P
    • Button 2: O
    • Direction 1: , (comma)
    • Direction 3: . (period)
    • Direction 5: L
    • Direction 7: ; (semicolon)
  • (Controller 2)
    • Button 1: _ (underscore)
    • Button 2: ° (handaku)
    • Direction 1: / (forward slash)
    • Direction 3: ] (right bracket)
    • Direction 5: : (colon)
    • Direction 7: [ (left bracket)

You can also determine if certain key combinations are pressed (experiment! the values are quite predictable if not, er, logical ;-). This sample program should help:
10 PRNT 1,"STICK 1"
20 PRNT 33, "STICK 2"
30 KEY 1 A,B
40 KEY 2 C,D
50 PRNT 8, A
60 PRNT 16, B
70 PRNT 40, C
80 PRNT 48, D
90 GOTO 30
100 END
LET$x0 Assignment of expressions to variables. LET is optional, but is a true token even though it never appears in listings even when explicitly entered.

For integer variables, LET works fairly close to standard BASIC. For string variables, however, LET only allows direct assignment and no manipulation (either A$="12345678" or A$=B$). There seems to be no concatenation operator (both A$+B$ and A$&B$ cause entry errors). Strangely, A$=A$B$ is accepted, but it sets A$ to B$, not A$+B$.

You can assign integer quantities to string variables, but this doesn't act like BASIC STR$; rather, the 16-bit quantity actually sets the ASCII values of the first two characters of the string. Keep in mind that the 9995 is big-endian, so something like A$=16961 would set A$ to "BA" ($4241, which would be represented as $41 $42 on little-endian CPUs like the 6502). This means a code fragment like B$=65 actually would make the first character of B$ into a null ($0041).

Assigning string literals to integer variables causes an entry error.

OVFL [ON/OFF]$1B [$11/$10] Undocumented. This enables runtime math errors (what a pleasant, uh, feature ...), which default to OFF on each GRUN. When enabled with OVFL ON, math operations that would cause a math overflow (such as 65535+1 or 1/0) now generate a runtime error instead of being silently ignored and/or wrapping. This program generates a runtime error on line 3:
2 A=65535
3 A=A+1
POST(y,x)$1F Turns an ordered pair (x,y) into a 16-bit quantity (note that the Y-coordinate is specified first) which can used directly in calculations, or be passed to ANIM to set a sprite position (see ANIM for an example). x and y may be regular integer expressions, and POST can be used itself in expressions (e.g. ANIM 4=POST(3,4)+2 is legal and executable). POST uses at a minimum the first two bytes of the workspace region as a scratch pad.

As mentioned under ANIM, sprite positions are held in a 16-bit quantity with Y in the MSB and X in the LSB. x and y are not range checked and only their LSBs are used in calculations (the MSBs are thrown away); they are loaded into the first two bytes of the workspace (Y first; remember, the 9995 is big-endian), and then worked with further or assigned. So, POST can also be used to fuse two bytes into a 16-bit number very quickly without an expensive multiply operation.

The sprite coordinate system is a little strange and does not seem to match up fully with documented 9918A behaviour. As expected from 9918A documentation, a Y of 0 is on the second scan line from the top, 1 on the third, 2 on the fourth, etc., down to 192 which is now fully off-screen. 255 is exactly on the top scan line, 254 moves it up one into the border so the top sprite pixels are off-screen, 253 moves it up two, 252 moves it up three, and so on until it is completely obscured at 224 in the border. X, however, is not standard. An X coordinate of zero always hides the sprite, no matter what the Y coordinate is. There is no support for the Early Clock bit, so an X of 1 is at the left hand side, though the sprite may be scrolled into the right hand border (up to 255).

Thus, after all that, in GBASIC the upper left hand corner is at POST(255,1) and the lower right corner is at POST(191,255).

PRNT a,b$x9 Prints b to cell position a (1-761); e.g., PRNT 44,"GBASIC" prints the string GBASIC at cell position 44.

b must either be a string literal (no longer than eight characters), an integer variable, or a string variable (entry error otherwise). Strangely, integer literals are not accepted and cause entry errors, but PRNT 1, ANIM 4 is accepted (! -- but it causes a runtime error). a can be a variable or integer literal, but it cannot be an expression (entry error).

Strings are left justified; integers are right justified.

PRNT always affects eight entire screen cells starting at the specified position. The actual printing is done with white text on a light blue background (no matter what colours were there in the first place, so if colour coordination is important to you, plan accordingly :-). PRNTing to cells 0 or >761 causes no error, but nothing is printed, either.

RAND(a)$1E Returns a random number between 0 and a, inclusive. a may be an expression, and RAND may be used in expressions itself (e.g. A=(4*RAND(Q)-3) is legal). There is no RANDOMIZE.
STOP$17 Standard BASIC, but when entered as a keyword (unlike END), the editor is not terminated. During runtime, when a STOP is found, the line is displayed, and execution halts.
TIME a {ON}$xC [$11] When used with the optional ON parameter, enables one of the built-in timers, of which five are documented but seven appear to exist, specified by a (e.g., TIME 5 ON enables timer 5). a must be a literal (1-7); otherwise, an entry error occurs. Each timer counts up by one every 1/100 sec, up to a maximum value of 4000.

Irritatingly, the actual value of the timer cannot be directly referenced (let alone altered) in expressions (X=TIME 1 generates an entry error); only IF can see the value of a timer e.g., IF TIME 1>20 THEN 50 and nothing else! (IF TIME 1=X, i.e., comparing timers with variables is legal.)

There is no way to reset the timers except to stop the program, and TIME 1 OFF causes an entry error even though the ON token is the same as for OVFL, so OFF should work too, but it doesn't. TIME 1 ON can be specified repeatedly, but it does not reset the timer each time.

TONE NOa$1D ($21, $22, $23, $24) Plays predefined sound a (1-4). Note that there is no space between NO and the a parameter -- it's TONE NO1 and not TONE NO 1 as it occasionally appears or at least looks like in the manual. a must be an integer literal; otherwise, an entry error occurs.

Tone 1 is the key beep; tone 2 is the error buzz; tone 3 is the 'da da da' three-tone acknowledge sound; and tone 4 is the buzz'n'hiss crash sound (like the one you get from biffing it in Traffic Jam). Playback is asynchronous and goes in the background; a new TONE will cancel and replace any currently playing sound.

Even though a could have have been represented as an integer, NO1, NO2, NO3 and NO4 are all separate tokens.

VERT(a)$1C Undocumented. Returns the Y coordinate of a 16-bit sprite position quantity (e.g., VERT(ANIM 3) gives the Y position of sprite 3). See POST for the sprite coordinate system. a can be an expression, and VERT can be used in expressions too (basically a rapid equivalent to taking the MSB of a 16-bit integer).

Symbol Tokens

All tokens have standard BASIC semantics, more or less.

Symbol Token
=, <, >$01, $08, $09
), ($02, $07
+, -, *, /$03, $04, $05, $06

Housekeeping Tokens

This includes variable references and inline integers.

Token Description
$30 Indicates an unsigned 16-bit big-endian integer follows (negative quantities have a unary negation operator instead, prior to the $30 token).
$4x Indicates an integer variable reference. The index into the variable table is stored in the lower 6 bits, but since there are only 60 variable table slots, only indices 0-59 are valid.
$Cx Indicates a string variable reference (a $ token always follows). The index into the variable table is stored in the lower 6 bits, but since there are only 60 variable table slots, only indices 0-59 are valid.

Back to the top | Back to "How To Program In GBASIC" | Back to "Programming The Tomy Tutor"