VENTURESPEAK
ZX Computing, October, November & December 1986


Part 1: October 1986


Alan Davis, recently returned from the Realms of Interaction,
sets out once more on a quest to produce a superior command
analysis system.


It's often said that the quality of an adventure game is predominantly
determined by the originality and imaginative scope of its plot, and
really it would be hard to deny this. Virtuosity of programming, text
compression, complex language analysis, and similar sophistications
have been loudly trumpeted by many a software company, and equally
loudly hailed by many a reviewer; yet these count for nothing if the
heart of the game - its plot and atmosphere - is dull and uninspired.
All this is true, but of course there are limits It's equally possible
for an adventure to be wonderfully imaginative in concept, but
rendered almost unplayable by inordinately long response times, shabby
on-screen presentation, or sheer "unfriendliness".

It's not difficult to find examples of this second category even among
recent commercial adventures: the grotesque "Lord of the Rings" comes
most readily to mind, with its excruciatingly slow responses coupled
to a keyboard input routine whose sluggishness makes accurate entry of
commands a sore test of patience. Or how about those hordes of Quilled
adventures, essentially rapid in response, but so often suffering from
stereotyped presentation, unfriendly vocabulary and the rigid
restriction of the standard "verb/noun" input requirement.

Faced with this situation, the BASIC programmer with a yen to write
adventures may well be put off for good. Even if he opts for the
simple "verb/noun" input format, the need for a large, friendly
vocabulary necessarily implies long delays in response whenever the
program decodes the player's input and scans the vocabulary lists.
Even then, if this limitation is accepted, there are many occasions
when the "verb/noun" type of command analyser is inadequate - for the
fact remains that you just can't write an interactive "Hobbit"-style
adventure without some capability for dealing with more complex
commands.


Instant response

The obvious solution to the problem is to resort to machine code
programming in order to deal with the command analysis aspects of
adventure writing, since this has many advantages. First, program
responses can be virtually instant, regardless of size of vocabulary.
Second, complex and multiple commands (eg. PUT THE JEWEL IN THE CHEST
AND LEAVE THE ROOM) can be dealt with, again at no cost in response
time. And finally, extra facilities may be incorporated into the
command analyser to permit, say, detailed editing facilities to
improve "friendliness", or the option of "real time" operation in the
completed adventure if required.

Such a solution in turn raises its own difficulties - not least that
of interfacing the machine code routines with the rest of a
predominantly BASIC program. We must also face the problem of
(preferably) writing code which offers enough flexibility for us to
use it for any adventure we may care to write. This, in a nutshell, is
what the Venturespeak command analysis system has been designed for.
Even if you have no understanding of machine code programming, you can
use Venturespeak yourself to write sophisticated adventures which
retain the flexibility and ease of BASIC, yet which are largely free
from its speed limitations.

Venturespeak essentially consists of a single "package" which can be
conveniently divided into 3 sections as follows:

1) A keyboard input routine which accepts typed commands up to 63
   characters long, complete with single key- press facilities for
   deleting / editing commands, and a real- time option.

2) A Parser routine which decodes complex commands containing up to
   three verbs and four nouns, such as "ASK SAM TO PUT THE COIN IN THE
   PURSE", as well as permitting several commands to be strung together
   in sequence using commas, full stops, or "AND".

3) The menu-driven Venturespeak Editor which offers comprehensive and
   easy-to-use facilities for adding, deleting, or changing vocabulary.
   It is capable of generating a single block of code which incorporates
   both keyboard and parsing routines together with the vocabulary needed
   for any particular adventure.


Assembly points

This month we'll be dealing with the input routine, and the other
aspects of Venturespeak will be tackled in succeeding articles, but
before we begin I'd like to make a strong recommendation that you
equip yourself with a decent assembler program (if you don't have one
already). You can poke in the machine code routines from BASIC of
course - and I'll provide decimal dumps for those who wish to do so -
but we'll be dealing with over 1K of machine code altogether, and the
scope for errors when typing in the long lists of numbers is
considerable, even with checksums. An assembler is well worth the
investment - I use Hisoft's "DEVPAC" and find it excellent.

Now that the hors-d'oeuvres are out of the way, we can get on with
this month's main course. Before you start, type CLEAR 59999 to lower
RAMTOP so that our code can be stored safely at address 60000 and
above. Incidentally, I've chosen this address so that the Venturespeak
routines will be compatible with those from the previous "Realms of
Interaction" series, should you wish to combine them. (The code is not
relocatable).

Listing 1 gives the assembly language program for the keyboard input
routine. The code is organised at address 60000, and is 391 bytes
long. For those assemblerless individuals, Listing 2 provides the
(decimal) bytes you'll need in conjunction with the BASIC loader given
in Listing 3. Type in Listing 3, and then RUN. Just follow the prompts
the program gives you, entering the individual bytes from Listing 2
one at a time, including the checksum after every fifth byte. If a
mistake is found, the program will ask you to enter the whole of the
offending line of 5 bytes again. The screen display which builds up
should be identical to the layout of Listing 2, and this should help
you to check everything as you go along. Once the whole thing has been
entered without errors, the program will save your code to tape as
"KEYBOARD" CODE 60000,391.

What, then, does this routine do when you call it with LET m=USR
60000? Well, basically it prints a small "prompt" symbol at the bottom
of the screen and waits for you to start typing a command. Each
character you type is stored as a single byte above RAMTOP, the bytes
being stored sequentially from address 60318 (START) onwards up to a
maximum of 63 characters. When you finally press ENTER, the routine
does an immediate return to BASIC, with the total length of your typed
command held at address 60315 (COUNT). All is then ready for the
parser (which of course we don't yet have) to take over and analyse
your command. However, this is by no means all that the routine can do
for you. The following facilities are available:

1) CAPS SHIFT/0 acts just like the usual Spectrum "DELETE" key,
   erasing the last character typed.
2) CAPS SHIFT/1 deletes everything you've typed so far.
3) CAPS SHIFT/2 enables you to recall the whole of the last command
   you entered, either so that you can enter it again unchanged, or for
   editing using the "DELETE" key.


Zero time

The routine also has a built-in real time facility which operates as
follows: if nothing has been typed, then after about 10 seconds a
return to BASIC occurs with the address 60383 (TIME) holding the value
zero. If on the other hand a return to BASIC has occurred due to the
player entering a command, the address 60383 holds the value 1. This
means that on exit from the routine we can test PEEK 60383, and
execute appropriate subroutines (say for independent action of
characters) if that memory location contains zero. If on the other
hand it contains the value 1, we know that a command analysis must be
carried out first.

If you don't want your adventure to run in real time, this feature can
be suppressed by amending the code with the following POKEs: POKE
60033,0: POKE 60034,0: POKE 60035,0 (You might like to save two
versions of the code - one with real time, and one without).
Alternatively, if you want to keep the real time feature but slow down
the pace of things, try poking different values into 60032. POKE
60032,4 will give exits to BASIC every 20 seconds or so, for instance.

Still, trying all these things for yourself is much more fun than
reading about them, and Listing 4 will enable you to check the routine
out. First CLEAR 59999, load in the bytes, type in the lines of BASIC
from Listing 4 [file: keytest], and then RUN. When you see the prompt
at screen bottom, type in any old command (note that CAPS LOCK is set
automatically) and press ENTER. If all is well the lower screen will
clear, and your command will be exactly reproduced in the upper screen
(confirming that it has been stored correctly in memory).

Try pressing CAPS SHIFT/2 to check that the "recall last command"
facility is working correctly, and in addition check the delete
functions. It's clear, I hope, what the benefits of these facilities
are: suppose your last command was "ASK SAM TO ATTACK THE ORC", and
that on your next turn you'd like Sam to beat the daylights out of a
troll. All you need do is recall your previous command using CS/2,
delete the word "ORC", and add the word "TROLL" before pressing ENTER.

All OK? Fine. Make yourself a well-earned cup of coffee, for you now
have about one third of the complete Venturespeak system safely
stashed away on tape. See you next month, with the second instalment...


Assembler Listing for KEYBOARD
------------------------------

60000 3E08     KEYBD   LD   A,008
60002 326A5C           LD   (23658),A
60005 CDE2EA           CALL CLEAR
60008 CD2EEB           CALL PROMPT
60011 AF       RESET   XOR  A
60012 32785C           LD   (23672),A
60015 32795C           LD   (23673),A
60018 327A5C           LD   (23674),A
60021 329BEB           LD   (COUNT),A
60024 AF       WAIT    XOR  A
60025 32085C           LD   (23560),A
60028 3A795C   WAIT2   LD   A,(23673)
60031 FE02             CP   002
60033 D232EB           JP   NC,TIMEUP
60036 3A085C   CONT    LD   A,(23560)
60039 FE00             CP   000
60041 CA7CEA           JP   Z,WAIT2
60044 FE06             CP   006
60046 CA7BEB           JP   Z,REPEAT
60049 FE0D             CP   013
60051 CCFBEA           CALL Z,CHECKN
60054 FE00             CP   000
60056 CA78EA           JP   Z,WAIT
60059 FE0C             CP   012
60061 CCFBEA           CALL Z,CHECKN
60064 FE00             CP   000
60066 CA78EA           JP   Z,WAIT
60069 FE7B             CP   123
60071 D278EA           JP   NC,WAIT
60074 329DEB           LD   (CHR),A
60077 FE0D             CP   013
60079 CA0AEB           JP   Z,ENTER
60082 FE0C             CP   012
60084 CA55EB           JP   Z,DELETE
60087 3A9DEB           LD   A,(CHR)
60090 FE07             CP   007
60092 CA19EB           JP   Z,WIPE
60095 FE20             CP   032
60097 DA78EA           JP   C,WAIT
60100 3A9BEB           LD   A,(COUNT)
60103 FE3F             CP   063
60105 CA78EA           JP   Z,WAIT
60108 219EEB           LD   HL,START
60111 1600             LD   D,000
60113 5F               LD   E,A
60114 3A9DEB           LD   A,(CHR)
60117 19               ADD  HL,DE
60118 77               LD   (HL),A
60119 13               INC  DE
60120 CD3EEB           CALL CURSOR
60123 7B               LD   A,E
60124 329BEB           LD   (COUNT),A
60127 C378EA           JP   WAIT

60130 AF       CLEAR   XOR  A
60131 CD0116           CALL #1601
60134 CD26EB           CALL SETPR
60137 0640             LD   B,064
60139 3E20     SCAN    LD   A,032
60141 C5               PUSH BC
60142 D7               RST  #10
60143 C1               POP  BC
60144 05               DEC  B
60145 78               LD   A,B
60146 FE00             CP   000
60148 C2EBEA           JP   NZ,SCAN
60151 CD26EB           CALL SETPR
60154 C9               RET

60155 329DEB   CHECKN  LD   (CHR),A
60158 3A9BEB           LD   A,(COUNT)
60161 FE00             CP   000
60163 CA09EB           JP   Z,RESETA
60166 3A9DEB           LD   A,(CHR)
60169 C9       RESETA  RET

60170 CDE2EA   ENTER   CALL CLEAR
60173 3E01             LD   A,001
60175 32DFEB           LD   (TIME),A
60178 3A9BEB           LD   A,(COUNT)
60181 329CEB           LD   (CT2),A
60184 C9               RET

60185 AF       WIPE    XOR  A
60186 329BEB           LD   (COUNT),A
60189 CDE2EA           CALL CLEAR
60192 CD2EEB           CALL PROMPT
60195 C378EA           JP   WAIT

60198 3E16     SETPR   LD   A,022
60200 D7               RST  #10
60201 AF               XOR  A
60202 D7               RST  #10
60203 AF               XOR  A
60204 D7               RST  #10
60205 C9               RET

60206 3E3E     PROMPT  LD   A,062
60208 D7               RST  #10
60209 C9               RET

60210 3A9BEB   TIMEUP  LD   A,(COUNT)
60213 FE00             CP   000
60215 C284EA           JP   NZ,CONT
60218 CDE0EB           CALL QUIT
60221 C9               RET

60222 D5       CURSOR  PUSH DE
60223 3E11             LD   A,017
60225 D7               RST  #10
60226 3E06             LD   A,006
60228 D7               RST  #10
60229 3A9DEB           LD   A,(CHR)
60232 D7               RST  #10
60233 D1               POP  DE
60234 C9               RET

60235 3E11     SPACE   LD   A,017
60237 D7               RST  #10
60238 3E07             LD   A,007
60240 D7               RST  #10
60241 3E20             LD   A,032
60243 D7               RST  #10
60244 C9               RET

60245 3A9BEB   DELETE  LD   A,(COUNT)
60248 FE1F             CP   031
60250 CA70EB           JP   Z,TLINE
60253 3E08             LD   A,008
60255 D7               RST  #10
60256 CD4BEB   HERE    CALL SPACE
60259 3E08             LD   A,008
60261 D7               RST  #10
60262 3A9BEB           LD   A,(COUNT)
60265 3D               DEC  A
60266 329BEB           LD   (COUNT),A
60269 C378EA           JP   WAIT

60272 3E16     TLINE   LD   A,022
60274 D7               RST  #10
60275 AF               XOR  A
60276 D7               RST  #10
60277 3E1F             LD   A,031
60279 D7               RST  #10
60280 C360EB           JP   HERE

60283 3A9BEB   REPEAT  LD   A,(COUNT)
60286 FE00             CP   000
60288 C278EA           JP   NZ,WAIT
60291 CDE2EA           CALL CLEAR
60294 CD2EEB           CALL PROMPT
60297 119EEB           LD   DE,START
60300 3A9CEB           LD   A,(CT2)
60303 329BEB           LD   (COUNT),A
60306 0600             LD   B,000
60308 4F               LD   C,A
60309 CD3C20           CALL #203C
60312 C378EA           JP   WAIT

60315 00       COUNT   DEFB 0
60316 00       CT2     DEFB 0
60317 00       CHR     DEFB 0
60318 ..       START   DEFS 65
60383 00       TIME    DEFB 0

60384 32DFEB   QUIT    LD   (TIME),A
60387 CDE2EA           CALL CLEAR
60390 C9               RET


- - - - -

Part 2: November 1986


Part 2: Alan Davis' adventure series looks at command analysis.


This month brings you the second part of the VENTURESPEAK machine code
program, and we'll be taking a look at the gentle art of "parsing the
input" - or "command analysis", if you prefer.

You're going to need the machine code keyboard routine that you saved
last month, as we need to patch this month's program onto it to
produce a single block of code. Listing 1 is the assembly language
program for the "parser" - this is the program which will scan through
a typed input, checking the words against a set of vocabulary data. As
you can see it's a fairly lengthy routine so an assembler will make
error- free entry considerably easier - but if you don't have an
assembler all is not lost, since I've given a decimal dump of the code
in Listing 2. All you need to do is type in Listing 3, RUN it, and
then enter the numbers from Listing 2 in order, including the
checksums after every fifth byte. Take things slowly, checking the
screen display against the listing as you go along, and all should be
well. When you've finished, the program will save the code to tape for
you as "PARSER" CODE 60400,750.


Parsing

So far, so good, We now need to weld the keyboard and parser sections
together as follows:

1) Reset the Spectrum using RANDOMIZE USR 0.
2) Enter CLEAR 59999 as a direct command.
3) Load in the keyboard code you saved last month (LOAD "KEYBOARD" CODE).
4) Load in this month's parser code (LOAD "PARSER" CODE).
5) Now save the whole lot as a single block with
   SAVE "V-SPEAK" CODE 60000,1150 - and keep it safe somewhere until
   next month.

In addition to this single code block, it's probably wise to keep the
two separate parts as well for the present, to facilitate checking in
case you discover errors later.

Of course, you'll be wanting to know just what this new routine does,
and to test it out. We'll certainly be looking at how it works in this
article, but as for testing it - well, I'm going to ask you to be
patient and wait until next month. You see, the problem is that our
parser is actually fairly useless at present because it doesn't yet
possess a vocabulary! (Rather like a chap who, though highly
intelligent, has no background knowledge to draw upon ...) To put
vocabulary into the parser we'll need the VENTURESPEAK EDITOR - and
that's our task for next month.

---------------------------------------------
Figure 1. Vocabulary list, with each verb,
noun and object individually numbered.
         
VERBS             D      10         SHI     5         
                  DOW    10         ARM     6         
SAY     1         DES    10         BOO     7         
TEL     1         GET    11         CHE     8         
ASK     1         TAK    11         BOX     9         
TAL     1         PIC    11         DOO    10         
L       2         DRO    12         
LOO     2         PUT    12         
N       3         EXA    13         PEOPLE         
NOR     3         REA    13         
S       4         GIV    14         SAM     1         
SOU     4         OFF    14         FRE     2         
E       5         FIG    15         PET     3         
EAS     5         KIL    15         JOH     4         
W       6         ATT    15         JAC     5         
WES     6         HIT    15         JOE     6         
IN      7         
ENT     7         
OUT     8         OBJECTS         
LEA     8         
U       9         SWO     1         
UP      9         DAG     2         
CLI     9         SPE     3         
ASC     9         KNI     4         
---------------------------------------------

For the present though, I'm going to explain the basic principles
underlying the parsing system so that when the time comes for you to
use it you'll be familiar with all the main features, We can't discuss
this in a vacuum, so in Figure 1 I've given a very elementary
vocabulary of words which you might expect to find in a typical
adventure (printed out from the EDITOR program, in fact). Figure 1
itself raises a few points which need to be mentioned before we go any
further:

1) Only the first three letters of any word are significant. This can
   occasionally give rise to some confusion because the parser can't
   distinguish between words such as "RAVEN" or "RAVINE", although in
   practice I've never found this to be a serious problem, myself. (My
   game "RUNESTONE" uses only three-letter parsing).

2) Vocabulary is divided into three distinct types: VERBS, OBJECTS and
   PEOPLE. This has advantages over the usual simpler sub-division into
   VERBS and NOUNS because it helps the error-trapping process when you
   write your adventure, as you'll see later.

3) Each item of vocabulary is assigned a number between 1 and 254
   inclusive. Synonyms are catered for by assigning the same number to
   different words, so that the verbs SAY, TELL, ASK, and TALK, for
   example, are all assigned the number 1. This is a good place to make
   an IMPORTANT NOTE: generally you can assign any number you like to any
   verb you like, BUT IT IS ESSENTIAL THAT ALL VERBS IMPLYING SPEECH BE
   ASSIGNED THE NUMBER 1, if the parser routine is to work correctly.


Input

In practice what happens will be something like this. Your BASIC
adventure program will invite input from the player by invoking the
keyboard routine: LET M=USR 60000. The player then types in his
command, presses ENTER when he's finished, and the routine returns to
BASIC having stored the player's input as a string of bytes in the
correct addresses above RAMTOP. We now want the parser to scan this
stored command and decode it, so the next step is to call the parser
routine with LET M=USR 60400. The parser now scans through the
player's command, character by character, checking the words it finds
against those in its vocabulary, ignoring any words it fails to
recognise and skipping on to the next. It eventually stops, either
when it reaches the end, or when it reaches a comma, full stop, or the
word "AND" (these last items signifying that the player has typed in
several commands at one go) and returns to BASIC.

The parser having done its job (almost instantaneously of course), we
need to extract the fruits of its labours by PEEKing certain
addresses. It makes for an easier life if the results of these PEEKs
are assigned immediately to BASIC variables with suitably
memory-jogging names, like this:
LET TELL=PEEK 61124
LET PERS=PEEK 61125
LET VB1=PEEK 61126
LET VB2=PEEK 61127
LET FK1=PEEK 61129
LET OB1=PEEK 61131
LET OB2=PEEK 61132
LET MORE=PEEK 61123

What this all amounts to is that you can extract from any one command
the code numbers for up to three verbs, two objects, and two people -
so that your BASIC program can then perform the necessary condition
tests appropriate to your adventure. A few examples should make things
clear.


---------------------------------------------
Figure 2. Examples of command analysis
                                
EXAMINE THE DAGGER              PUT THE BOOK IN THE CHEST       
                                
             A                               C
                                
     COMMAND ANALYSIS                COMMAND ANALYSIS           
                                
                                
     TELL:      0                    TELL:      0               
     PERS:      0                    PERS:      0               
                                
     VB1 :      13                   VB1 :      12              
     VB2 :      0                    VB2 :      7               
                                
     FK1 :      0                    FK1 :      0               
                                
     OB1 :      2                    OB1 :      7               
     OB2 :      0                    OB2 :      8               
                                
                                
                                
SAY TO FRED "EXAMINE THE DAGGER"ASK PETER TO GIVE THE KNIFE TO J
                                OHN
             B                               D
                                
     COMMAND ANALYSIS                COMMAND ANALYSIS           
                                
                                
     TELL:      1                    TELL:      1               
     PERS:      2                    PERS:      3               
                                
     VB1 :      13                   VB1 :      14              
     VB2 :      0                    VB2 :      0               
                                
     FK1 :      0                    FK1 :      4               
                                
     OB1 :      2                    OB1 :      4               
     OB2 :      0                    OB2 :      0               
                                
                                
---------------------------------------------


Examining

Figure 2 gives examples of the analysis of a range of commands
involving the basic vocabulary of Figure 1. (You might like to know
that these figures are all screen dumps from the VENTURESPEAK EDITOR
in "test" mode). Let's start with the first and simplest command -
2(a): "EXAMINE THE DAGGER". Only two words are really relevant here -
the verb "EXAMINE", and the noun (in this case an object) "DAGGER".
You'll see in the example that the parser has assigned the value 13 to
VB1, 2 to OB1, and zero to everything else. Now look back at the
vocabulary list in Figure 1, where you'll find that verb number 13 is
"EXA" (for "examine"), and that object number 2 is "DAG" (for
"dagger"). Makes sense? OK, then let's try something a little more
complex.

The second example, Figure 2(b), shows the analysis of the command
"SAY TO FRED "EXAMINE THE DAGGER"" and we find VB1 and OB1 assigned
just the same values as before (as we'd expect) but look: this time
the variable TELL takes the value 1I (signifying speech) and the
variable PERS is given the value 2 (the number for Fred) corresponding
to the person being addressed. Note that TELL is really only a flag
which takes the value 1 or 0 according to whether speech is signified
or not; it's the variable PERS which conveys the information about
whom the player is trying to speak to.

So why do we need the extra variables on VB2 and OB2? Well, sometimes
commands may involve two objects - and there may be instances when you
want to distinguish between two slightly different types of action,
such as putting objects ON or IN other subjects. Figure 2(c) shows the
analysis of the command "PUT THE BOOK IN THE CHEST". See how the two
verbs "PUT" (12) and "IN" (7) are picked up here, together with the
two objects in order: "BOOK" (7) and "CHEST" (8).

The final example, 2(d) is of the type where speech to one person
involves some kind of action with yet another person. And so the
command "ASK PETER TO GIVE THE KNIFE TO JOHN" sets the speech flag
(TELL) and sets PERS to 3 ("PETER"). As we'd expect, VB1 is 14
("GIVE") and OB1 is 4 ("KNIFE") but in addition to this, FK1 has
picked up the other person involved in the transaction ("JOHN"=4). We
see here, by the way, how the separation of nouns into PEOPLE and
OBJECTS eases the error checking process needed in every adventure. If
a command is found to contain a verb such as "GIVE" or "FIGHT" (where
some other person must of necessity be specified), it can be rejected
as an unacceptable entry without further enquiry if FK1 is zero.
Example 2(d) also illustrates the essential friendliness of
VENTURESPEAK, particularly where speech commands are concerned. It
matters not a jot what style of entry is used by the player - SAY TO
PETER "GIVE JOHN THE KNIFE" for example, will be decoded with complete
success by the parser.

I haven't mentioned how the system copes with multiple commands yet.
Suppose the command GET THE SWORD AND GO NORTH has been entered. The
parser will analyse the first part (GET THE SWORD) and then return to
BASIC, but it will remember how far along the command it has scanned.
If we LET MORE=PEEK 61123 (see above), the variable MORE will be
assigned the value 1 - telling us in effect that another part of the
command remains to be analysed. (For single commands MORE will be
zero). This means we can arrange for the first command to be processed
by an appropriate BASIC subroutine, and then test the variable MORE to
see whether another analysis should follow, or whether a return to
keyboard control is called for. However, the re-entry point to the
parser routine is different should a continuation of analysis be
necessary - on such occasions the parser is called with LET M=USR
60426. (This is because the usual entry at USR 60000 involves a
tidying-up process which would cause the parser to "forget" the
position it had reached on its previous scan).

A typical programming "flow chart", therefore, would be something like this:

(1) CALL KEYBOARD ROUTINE (USR 60000)
(2) CALL PARSER ROUTINE (USR 60400)
(3) PROCESS COMMAND IN BASIC
(4) IF MORE THEN LET M=USR 60426: GOTO (3)
(5) GO BACK TO (1)

I hope that by now you've begun to get some idea, in principle, of
what VENTURESPEAK can do for your adventure writing. If you're burning
for some "hands-on" experience, don't worry. You'll be able to try out
all these examples (and as many others as you wish) yourself with the
help of the EDITOR program next time, when I'll also be offering more
detailed explanations of how to incorporate the system into your own
programs, Till then ...


Assembler Listing for PARSER
----------------------------

60400 219EEB   INIT    LD   HL,START
60403 22BEEE           LD   (NCOM),HL
60406 22C0EE           LD   (PTR),HL
60409 3A9BEB           LD   A,(COUNT)
60412 5F               LD   E,A
60413 1600             LD   D,000
60415 19               ADD  HL,DE
60416 3EFF             LD   A,255
60418 77               LD   (HL),A
60419 AF               XOR  A
60420 32C4EE           LD   (TELL),A
60423 32C5EE           LD   (PERS),A
60426 AF       RESTRT  XOR  A
60427 32C6EE           LD   (VB1),A
60430 32C7EE           LD   (VB2),A
60433 32C8EE           LD   (VB3),A
60436 32C9EE           LD   (FK1),A
60439 32CAEE           LD   (FK2),A
60442 32CBEE           LD   (OB1),A
60445 32CCEE           LD   (OB2),A
60448 CD74EC           CALL COMMA
60451 2AC0EE   LOOP8   LD   HL,(PTR)
60454 AF               XOR  A
60455 32DAEE           LD   (EXTR),A
60458 CD9AEC           CALL FILTER
60461 3ADAEE           LD   A,(EXTR)
60464 FE01             CP   001
60466 CA4FEC           JP   Z,SORT
60469 2AC0EE           LD   HL,(PTR)
60472 7E               LD   A,(HL)
60473 FEFF             CP   255
60475 CA4FEC           JP   Z,SORT
60478 FE2C             CP   044
60480 CA4BEC           JP   Z,END
60483 FE2E             CP   046
60485 CA4BEC           JP   Z,END
60488 C323EC           JP   LOOP8
60491 23       END     INC  HL
60492 22C0EE           LD   (PTR),HL
60495 3AC6EE   SORT    LD   A,(VB1)
60498 FE01             CP   001
60500 CC58EC           CALL Z,SWAP
60503 C9               RET

60504 32C4EE   SWAP    LD   (TELL),A
60507 3AC7EE           LD   A,(VB2)
60510 32C6EE           LD   (VB1),A
60513 3AC8EE           LD   A,(VB3)
60516 32C7EE           LD   (VB2),A
60519 3AC9EE           LD   A,(FK1)
60522 32C5EE           LD   (PERS),A
60525 3ACAEE           LD   A,(FK2)
60528 32C9EE           LD   (FK1),A
60531 C9               RET

60532 2ABEEE   COMMA   LD   HL,(NCOM)
60535 7E       LOOP    LD   A,(HL)
60536 FEFF             CP   255
60538 CA95EC           JP   Z,ENDCOM
60541 FE2C             CP   044
60543 CA8BEC           JP   Z,SETCOM
60546 FE2E             CP   046
60548 CA8BEC           JP   Z,SETCOM
60551 23               INC  HL
60552 C377EC           JP   LOOP
60555 23       SETCOM  INC  HL
60556 22BEEE           LD   (NCOM),HL
60559 3E01             LD   A,001
60561 32C3EE           LD   (MORE),A
60564 C9               RET
60565 AF       ENDCOM  XOR  A
60566 32C3EE           LD   (MORE),A
60569 C9               RET

60570 7E       FILTER  LD   A,(HL)
60571 FE22             CP   034
60573 CCF6EC           CALL Z,SKIPSP
60576 FE20             CP   032
60578 CCF6EC           CALL Z,SKIPSP
60581 FE3A             CP   058
60583 CCF6EC           CALL Z,SKIPSP
60586 FEFF             CP   255
60588 C8               RET  Z
60589 FE2C             CP   044
60591 C8               RET  Z
60592 FE2E             CP   046
60594 C8               RET  Z
60595 23               INC  HL
60596 7E               LD   A,(HL)
60597 FE20             CP   032
60599 CA0BED           JP   Z,SINGLE
60602 FE3A             CP   058
60604 CA0BED           JP   Z,SINGLE
60607 FE2C             CP   044
60609 CA0BED           JP   Z,SINGLE
60612 FE2E             CP   046
60614 CA0BED           JP   Z,SINGLE
60617 FE22             CP   034
60619 CA0BED           JP   Z,SINGLE
60622 FEFF             CP   255
60624 CA0BED           JP   Z,SINGLE
60627 23               INC  HL
60628 7E               LD   A,(HL)
60629 FE20             CP   032
60631 CA28ED           JP   Z,DOUBLE
60634 FE2C             CP   044
60636 CA28ED           JP   Z,DOUBLE
60639 FE2E             CP   046
60641 CA28ED           JP   Z,DOUBLE
60644 FE22             CP   034
60646 CA28ED           JP   Z,DOUBLE
60649 FE3A             CP   058
60651 CA28ED           JP   Z,DOUBLE
60654 FEFF             CP   255
60656 CA28ED           JP   Z,DOUBLE
60659 C3ACED           JP   TRIPLE
60662 23       SKIPSP  INC  HL
60663 7E               LD   A,(HL)
60664 FE20             CP   032
60666 CAF6EC           JP   Z,SKIPSP
60669 FE22             CP   034
60671 CAF6EC           JP   Z,SKIPSP
60674 FE3A             CP   058
60676 CAF6EC           JP   Z,SKIPSP
60679 22C0EE           LD   (PTR),HL
60682 C9               RET

60683 2AC0EE   SINGLE  LD   HL,(PTR)
60686 7E               LD   A,(HL)
60687 CD81ED           CALL SETPTR
60690 ED5BCDEE         LD   DE,(VCV1)
60694 47               LD   B,A
60695 1A       LOOP1   LD   A,(DE)
60696 FEFF             CP   255
60698 C8               RET  Z
60699 32C2EE           LD   (TPNO),A
60702 13               INC  DE
60703 1A               LD   A,(DE)
60704 B8               CP   B
60705 CA56ED           JP   Z,VBFND
60708 13               INC  DE
60709 C317ED           JP   LOOP1

60712 2AC0EE   DOUBLE  LD   HL,(PTR)
60715 7E               LD   A,(HL)
60716 ED5BCFEE         LD   DE,(VCV2)
60720 47               LD   B,A
60721 23               INC  HL
60722 7E               LD   A,(HL)
60723 4F               LD   C,A
60724 CD81ED           CALL SETPTR
60727 1A       LOOP3   LD   A,(DE)
60728 FEFF             CP   255
60730 C8               RET  Z
60731 32C2EE           LD   (TPNO),A
60734 13               INC  DE
60735 1A               LD   A,(DE)
60736 67               LD   H,A
60737 13               INC  DE
60738 1A               LD   A,(DE)
60739 6F               LD   L,A
60740 78               LD   A,B
60741 BC               CP   H
60742 CA4DED           JP   Z,FND1
60745 13               INC  DE
60746 C337ED           JP   LOOP3
60749 79       FND1    LD   A,C
60750 BD               CP   L
60751 CA56ED           JP   Z,VBFND
60754 13               INC  DE
60755 C337ED           JP   LOOP3
60758 3AC6EE   VBFND   LD   A,(VB1)
60761 FE00             CP   000
60763 C265ED           JP   NZ,VB1DUN
60766 3AC2EE           LD   A,(TPNO)
60769 32C6EE           LD   (VB1),A
60772 C9               RET
60773 3AC7EE   VB1DUN  LD   A,(VB2)
60776 FE00             CP   000
60778 C274ED           JP   NZ,VB2DUN
60781 3AC2EE           LD   A,(TPNO)
60784 32C7EE           LD   (VB2),A
60787 C9               RET
60788 3AC8EE   VB2DUN  LD   A,(VB3)
60791 FE00             CP   000
60793 C0               RET  NZ
60794 3AC2EE           LD   A,(TPNO)
60797 32C8EE           LD   (VB3),A
60800 C9               RET

60801 23       SETPTR  INC  HL
60802 F5               PUSH AF
60803 7E       LOOP2   LD   A,(HL)
60804 FE20             CP   032
60806 CAA6ED           JP   Z,NEWWD
60809 FE22             CP   034
60811 CAA6ED           JP   Z,NEWWD
60814 FE3A             CP   058
60816 CAA6ED           JP   Z,NEWWD
60819 FEFF             CP   255
60821 CAA7ED           JP   Z,LAST
60824 FE2C             CP   044
60826 CAA7ED           JP   Z,LAST
60829 FE2E             CP   046
60831 CAA7ED           JP   Z,LAST
60834 23               INC  HL
60835 C383ED           JP   LOOP2
60838 23       NEWWD   INC  HL
60839 22C0EE   LAST    LD   (PTR),HL
60842 F1               POP  AF
60843 C9               RET

60844 2AC0EE   TRIPLE  LD   HL,(PTR)
60847 7E               LD   A,(HL)
60848 CD64EE           CALL AND
60851 FE01             CP   001
60853 C8               RET  Z
60854 2AC0EE           LD   HL,(PTR)
60857 7E               LD   A,(HL)
60858 32D7EE           LD   (CHR1),A
60861 23               INC  HL
60862 7E               LD   A,(HL)
60863 32D8EE           LD   (CHR2),A
60866 23               INC  HL
60867 7E               LD   A,(HL)
60868 32D9EE           LD   (CHR3),A
60871 CD81ED           CALL SETPTR
60874 ED5BD1EE         LD   DE,(VCV3)
60878 1A       LOOP5   LD   A,(DE)
60879 FEFF             CP   255
60881 CAF5ED           JP   Z,DUNVBS
60884 CD49EE           CALL SHORT1
60887 CAE0ED           JP   Z,VBMCH1
60890 13               INC  DE
60891 13               INC  DE
60892 13               INC  DE
60893 C3CEED           JP   LOOP5
60896 CD54EE   VBMCH1  CALL SHORT2
60899 CAEBED           JP   Z,VBMCH2
60902 13               INC  DE
60903 13               INC  DE
60904 C3CEED           JP   LOOP5
60907 CD5CEE   VBMCH2  CALL SHORT3
60910 CA56ED           JP   Z,VBFND
60913 13               INC  DE
60914 C3CEED           JP   LOOP5

60917 ED5BD3EE DUNVBS  LD   DE,(VCOB)
60921 1A       LOOP6   LD   A,(DE)
60922 FEFF             CP   255
60924 CA20EE           JP   Z,DUNOBJ
60927 CD49EE           CALL SHORT1
60930 CA0BEE           JP   Z,OBMCH1
60933 13               INC  DE
60934 13               INC  DE
60935 13               INC  DE
60936 C3F9ED           JP   LOOP6
60939 CD54EE   OBMCH1  CALL SHORT2
60942 CA16EE           JP   Z,OBMCH2
60945 13               INC  DE
60946 13               INC  DE
60947 C3F9ED           JP   LOOP6
60950 CD5CEE   OBMCH2  CALL SHORT3
60953 CA7EEE           JP   Z,OBFND
60956 13               INC  DE
60957 C3F9ED           JP   LOOP6

60960 ED5BD5EE DUNOBJ  LD   DE,(VCFK)
60964 1A       LOOP7   LD   A,(DE)
60965 FEFF             CP   255
60967 C8               RET  Z
60968 CD49EE           CALL SHORT2
60971 CA34EE           JP   Z,FKMCH1
60974 13               INC  DE
60975 13               INC  DE
60976 13               INC  DE
60977 C324EE           JP   LOOP7
60980 CD54EE   FKMCH1  CALL SHORT2
60983 CA3FEE           JP   Z,FKMCH2
60986 13               INC  DE
60987 13               INC  DE
60988 C324EE           JP   LOOP7
60991 CD5CEE   FKMCH2  CALL SHORT3
60994 CA9EEE           JP   Z,FKFND
60997 13               INC  DE
60998 C324EE           JP   LOOP7

61001 32C2EE   SHORT1  LD   (TPNO),A
61004 13               INC  DE
61005 1A               LD   A,(DE)
61006 47               LD   B,A
61007 3AD7EE           LD   A,(CHR1)
61010 B8               CP   B
61011 C9               RET
61012 13       SHORT2  INC  DE
61013 1A               LD   A,(DE)
61014 47               LD   B,A
61015 3AD8EE           LD   A,(CHR2)
61018 B8               CP   B
61019 C9               RET
61020 13       SHORT3  INC  DE
61021 1A               LD   A,(DE)
61022 47               LD   B,A
61023 3AD9EE           LD   A,(CHR3)
61026 B8               CP   B
61027 C9               RET

61028 FE41     AND     CP   065
61030 C0               RET  NZ
61031 23               INC  HL
61032 7E               LD   A,(HL)
61033 FE4E             CP   078
61035 C0               RET  NZ
61036 23               INC  HL
61037 7E               LD   A,(HL)
61038 FE44             CP   068
61040 C0               RET  NZ
61041 CD8BEC           CALL SETCOM
61044 3E01             LD   A,001
61046 32DAEE           LD   (EXTR),A
61049 2B               DEC  HL
61050 CD81ED           CALL SETPTR
61053 C9               RET

61054 3AC2EE   OBFND   LD   A,(TPNO)
61057 47               LD   B,A
61058 3ACBEE           LD   A,(OB1)
61061 B8               CP   B
61062 C8               RET  Z
61063 FE00             CP   000
61065 C291EE           JP   NZ,OB1DUN
61068 78               LD   A,B
61069 32CBEE           LD   (OB1),A
61072 C9               RET
61073 3ACCEE   OB1DUN  LD   A,(OB2)
61076 B8               CP   B
61077 C8               RET  Z
61078 FE00             CP   000
61080 C0               RET  NZ
61081 78               LD   A,B
61082 32CCEE           LD   (OB2),A
61085 C9               RET

61086 3AC2EE   FKFND   LD   A,(TPNO)
61089 47               LD   B,A
61090 3AC9EE           LD   A,(FK1)
61093 B8               CP   B
61094 C8               RET  Z
61095 FE00             CP   000
61097 C2B1EE           JP   NZ,FK1DUN
61100 78               LD   A,B
61101 32C9EE           LD   (FK1),A
61104 C9               RET
61105 3ACAEE   FK1DUN  LD   A,(FK2)
61108 B8               CP   B
61109 C8               RET  Z
61110 FE00             CP   000
61112 C0               RET  NZ
61113 78               LD   A,B
61114 32CAEE           LD   (FK2),A
61117 C9               RET

60318          START   EQU  60318
60315          COUNT   EQU  60315
61118 0000     NCOM    DEFW 0
61120 0000     PTR     DEFW 0
61122 00       TPNO    DEFB 0
61123 00       MORE    DEFB 0
61124 00       TELL    DEFB 0
61125 00       PERS    DEFB 0
61126 00       VB1     DEFB 0
61127 00       VB2     DEFB 0
61128 00       VB3     DEFB 0
61129 00       FK1     DEFB 0
61130 00       FK2     DEFB 0
61131 00       OB1     DEFB 0
61132 00       OB2     DEFB 0
61133 0000     VCV1    DEFW 0
61135 0000     VCV2    DEFW 0
61137 0000     VCV3    DEFW 0
61139 0000     VCOB    DEFW 0
61141 0000     VCFK    DEFW 0
61143 00       CHR1    DEFB 0
61144 00       CHR2    DEFB 0
61145 00       CHR3    DEFB 0
61146 00       EXTR    DEFB 0


- - - - -

Part 3: December 1986


Alan Davis puts the finishing touches to his Venturespeak Editor.


The story so far:

You are in your living room. You can see a ZX Spectrum, several copies
of ZX Computing Monthly, and a large chunk of VENTURESPEAK machine
code saved on tape. What now?
	WRITE ADVENTURE
You can't do that.
	EXAMINE TAPE
There's something missing.
	HELP
You need the 	VENTURESPEAK editor ...

Indeed you do, and here it is.

Type in Listing 1 and save it to tape with SAVE "EDITOR" LINE 1.
Rewind the tape and verify, and then stop the tape at that point - we
need to save the code from the previous articles after the editor
program so that it can be loaded automatically (see Line 4 of Listing
1). To do this, enter CLEAR 59999 as a direct command, load in the
1150 bytes you stashed away last month, and save it directly after the
EDITOR program with SAVE "V-SPEAK" CODE 60000,1150.


Figure 1 - sample screens from the editor
---------------------------------------------
1(A) Main Menu
                                
            SELECT:             
                                
1: Edit verbs                   
2: Edit objects                 
3: Edit people                  
4: POKE vocabulary into memory  
5: Save vocab. array to tape    
6: Load vocab. array from tape  
7: Hard copy of vocabulary      
8: Test Venturespeak system     

---------------------------------------------
1(B) Verbs Editor, Page 0

         WORD   NO.             
          SAY    1              
          TEL    1              
          ASK    1              
          TAL    1              
          L      2              
          LOO    2              
          N      3              
          NOR    3       VERBS  
          S      4              
       >> SOU    4 <<    Page 0 
          E      5              
          EAS    5              
          W      6              
          WES    6              
          IN     7              
          ENT    7              
          OUT    8              
          LEA    8              
          U      9              
          UP     9              
6=cursor up        7=cursor down
5&8=change page    q=main menu
ENTER=add/edit vocabulary item

---------------------------------------------
1(C) Verbs Editor, Page 1

         WORD   NO.             
          CLI    9              
          ASC    9              
          D     10              
          DOW   10              
          DES   10              
          GET   11              
          TAK   11              
          PIC   11       VERBS  
          DRO   12              
          PUT   12       Page 1 
          EXA   13              
          REA   13              
          GIV   14              
          OFF   14              
          FIG   15              
          KIL   15              
          ATT   15              
          HIT   15              
       >>          <<           
                                
6=cursor up        7=cursor down
5&8=change page    q=main menu
ENTER=add/edit vocabulary item

---------------------------------------------
1(D) Generator of parser code with vocabulary

    POKEing vocabulary bytes    
                                
                                
                                
                                
    61354   255                 
                                
Vocabulary stored from          
61150 to 61354 inclusive        
                                
                                
PRESS "Q" TO RETURN TO MENU OR  
ANY OTHER KEY TO SAVE COMPLETE  
PARSER PLUS VOCABULARY          
(1355 bytes starting at 60000)  
                                
---------------------------------------------


All set? OK - rewind the tape, type LOAD "", and wait for the whole
thing to load in. You should then be greeted on screen with the main
menu, which should look like Figure 1(a). It does? So far, so good.
Press "1" to select the "Edit verbs" option, and you should get a
screen looking like Figure 1(b) - except that of course none of the
verbs will be there since you haven't put any in yet! Press ENTER, and
you'll be asked to type in a verb - reply with the verb TELL. Then
you'll be asked to type in the number of this verb - respond with the
number 1. Hey Presto: the entry TEL#1 will appear on screen, with the
cursor automatically moving down a line ready for your next input.
(Remember that all words are truncated to their first three letters,
regardless of how many you actually type in.)

Try entering other verbs in the same way - use Figures 1(b) and 1(c)
as a guide, if you like - and notice how the editor looks after you
when you reach the bottom of the screen by clearing "Page 0" and
starting afresh at the top of "Page 1". 'Ah yes," you say, "That's all
very well, but I happened to make a mistake on Page 0. What can I do
about that?" Have no fear. At any stage you can add new entries, or
change existing entries, by skipping through them using the 5, 6, 7
and 8 keys. Use keys 5 and 8 to change pages (5 decreases page number
while 8 increases it), and use keys 6 and 7 to move the cursor up and
down the list. Position the cursor at the entry you want to change,
press ENTER, and type in the amendment. The old entry will be deleted,
and the new one inserted in its place. If you want to delete an entry
completely, without replacement, just type a space when the program
prompts you to enter a verb. It's probably obvious (but I'll say it .
anyway), that since entries are always inserted at the current cursor
position, you'll need to skip through to the end of the file to make
new additions after making an amendment - or if you prefer, you can
return to the main menu and select option "1" again, when the program
will automatically position the cursor at the end of the file for you.


Verbs

This is a good place to remind you that verbs related to speech, such
as TELL, SAY or ASK, must be assigned the number 1. Apart from this
you can allocate any number up to 254 to any verb (allocating the same
number to synonyms, of course). The order in which they occur in the
list is quite irrelevant. I should also point out that any
abbreviation you'd like the parser to accept (such as "N" for "NORTH")
must be entered separately as a synonym in its own right - there are
plenty of examples of this in Figures 1(b) and 1(c).

Once you've become used to the editing system, press "q" to return to
the main menu, and try selecting options 2 and 3. You'll find that the
method of adding entries to the "people" and "objects" vocabulary
files is just the same; the program asks you first for the word, then
for its number, whenever you press ENTER. Easy, isn't it? Don't worry
about making illegal entries by mistake - the editor is very
extensively error-trapped, and it should be virtually impossible to
crash it by accident.

What about those other options on the main menu? Option 4 ("POKE
vocabulary") does require a little explanation. You see, in any of the
3 "Edit" modes, all you're actually doing is manipulating the contents
of a temporary file - In fact it's a BASIC array, v$(). When you're
happy that all the vocabulary you need has been added, selecting
option 4 causes the editor to POKE your vocabulary into memory above
RAMTOP, making the appropriate modifications to the VENTURESPEAK
machine code as necessary. Once this is finished, the program will
offer to save the complete parser code (including vocabulary) to tape,
informing you where in memory it resided, and how long it is. (See
Figure 1(d)). This is the final chunk of code which you'll
subsequently incorporate into your adventure.


Files

Options 5 and 6 on the main menu improve the versatility of the editor
program, allowing you to save and load the temporary vocabulary file,
v$(), to and from tape. Suppose, for example, that you've just spent a
session entering vocabulary, but wish to stop and continue some other
time. All you need to do is save what you've done so far using option
5 and then load it back into the editor at the start of your next
session, using option 6. Actually, I strongly recommend that you save
the temporary file as well as the completed code even when you think
you've finished, just as a matter of course. The reason for this is
that when you come to play-test your adventure game eventually, you're
almost certain to want to make additions or modifications to the
vocabulary at some stage - and there's no way of modifying the code
itself from within the editor. However, if you've saved the temporary
array there's no problem. Just load it into the editor using option 6,
make your amendments, and then generate a fresh batch of code (using
option 4) to replace the old one.

Of the other options on the main menu, number 7 is self- explanatory.
This gives you a hard copy of the entire vocabulary file for future
reference. The only comment worth making here is that the print-out is
produced from the temporary file v$(), rather than from the finished
code - so you can get hard copy whether or not you've previously used
option 4 to POKE in the vocabulary.

The last option, number 8, allows you to test the command analysis
system in situ, but note that the editor won't let you try this unless
you've first POKEd in the vocabulary by using option 4. I went into
details of the method of command analysis last month, and now you can
try out some of those examples for yourself using this facility. Just
type in your command when you see the little "prompt" symbol, and the
system will do a complete analysis for you.

There are one or two other points I should make about the editor
itself. With the program as written, each of the three vocabulary
files can hold up to 200 entries - giving you a maximum vocabulary of
600 words. This will almost certainly give you adequate scope - and
increasing the limit needlessly will merely increase saving and
loading times for the temporary array. However, there's nothing to
stop you increasing the limit if you wish to do so; you simply need to
change the value assigned to the variable "max" in line 10 of listing
1. It should be assigned a value of (1 + some multiple of 20); LET max
= 401, for instance, will give you scope for up to 400 entries in each
file. One further point about the editor is that you can use the POKE
vocabulary option as many times as you like within a session; it will
just overwrite whatever went before.

Now that you have the editor, you're in the happy position of being
able to produce a machine code parser for any adventure you care to
write - but a few tips about incorporating it into a BASIC adventure
program may not go amiss. You'll need to CLEAR 59999, of course, so
that your parser can be safely loaded in at address 60000. If you've
written adventures in BASIC already then you should have no problems,
but I've given in Listing 2 [file: outline] a suggested outline
structure (it is no more than a mere outline) which you might find
helpful. The significance of the various symbols was explained in last
month's article. Essentially the purpose of the parser is to translate
an English sentence into a series of numbers on which condition tests
can be performed, and of course the use you make of it lies entirely
in your own hands. Nevertheless, there are methods of working which
lend themselves well to the VENTURESPEAK system.


Objects

For instance, one sensible approach might be to store all your subject
descriptions in a series of DATA statements in exactly the same order
as they appear in the vocabulary file. Thus we might have lines such as:

9001 DATA "bright sword"
9002 DATA "sharp dagger"
9003 DATA "long spear'
9004 DATA "large chest"
etc.

You'd then arrange your vocabulary so that "SWO" is assigned the
number 1, "DAG" the number 2, "SPE" the number 3, and "CHE" the number
4. In this way, values of ob1 and ob2 extracted from the parser can be
related directly to specific data items for printing messages. Thus a
command like "PUT THE SWORD IN THE CHEST" when analysed would give
ob1=1 and ob2=4. Then in your "putting" subroutine you'd have a line like:
RESTORE (9000+ob1): READ x$: RESTORE (9000+ob2): READ y$:
PRINT "You put the ";x$;" in the ";y$;"."
This would produce the message "You put the bright sword in the large
chest" - assuming of course that the necessary logical tests have been
satisfied.

You can use similar tricks for dealing with more complex commands
involving other characters. Thus you might list characters' names in a
second set of DATA statements, eg

8001 DATA "Sam"
8002 DATA "Fred" etc

ensuring that SAM is allocated the number 1 in the "people" vocabulary
file, and FRE(D) the number 2. A command such as "ASK SAM TO GIVE THE
SWORD TO FRED" can then be conveniently streamlined. The analysis
would give tell=1, pers=1, fk1=2, ob1=1, and vb1=whatever number you
assigned to the verb "GIVE". In the speech subroutine, after the
appropriate checks to ensure that Sam is indeed present, you'd have a
line like:
RESTORE (8000+pers): READ p$: PRINT "You talk to ";p$;"."

Subsequently vb1 is inspected, resulting in the "giving" subroutine
being called. Here would be another series of condition tests (Is the
object available for giving? Is fk1 - Fred - present?) leading
ultimately to a line like this:
RESTORE (9000+ob1): READ x$: RESTORE (8000+fk1): READ y$:
LET p$=(p$ AND tell)+("You" AND NOT tell):
PRINT p$;" give";"s" AND tell;" the ";x$;" to ";y$;"."

In this particular example you'd then see on screen the message: "You
talk to Sam. Sam gives the bright sword to Fred." Of course you could
embellish this as much as you like - writing convincing conversation
routines is an important part of the fun! Notice that the line
suggested above will deal with the situation no matter who is doing
the giving; if the variable "tell" is zero, you'd just get: "You give
the bright sword to Fred".

So there you are - the VENTURESPEAK parser is at your disposal. For
the rest - the creation of that wonderful adventure which will
outshine "Sherlock" - over to you ...
