EUG PD


Assembly Adventure Game

 
Published in EUG #24

I hope my last contribution was reasonably understandable and not too overwhelming! Like most of my programs, it just grew and grew! I have a program that grew too big for 32k and hope to shorten it by M/coding it.

That said, before I describe the promised adventure program, I want to comment on two routines which were used in the programs given last time as these will be often used in M/code.

In the Mr. Miner program, when you move around, the play area is re-drawn from the data, each time starting at a different address. This address is calculated by adding or subtracting an amount depending on which way you move.

For addition, the carry flag must be cleared first then the low byte values added followed by the high byte values. Thus:

      CLC:LDAaddr:ADC#xlow:STAaddr
      LDAaddr+1:ADC#xhigh:STAaddr

adds the two byte value x to the address held at addr and stores it at this address.

For subtraction, the routine is the same except the carry flag must be set first.

In the MINsrc1 program, line 650, the value to be added is first stored in &90. This is then added after loading the value from data and stored again. As there is no high byte value to add, the ADC#0 in the line adds 1 go the high byte if the carry flag is set by the addition of the low byte. Lines 660 and 670 are similar for changing the address held in addr.

The other routine is for transferring blocks of data from one memory area to another. This is done with a loop similar to those I have already given. In line 680 of the program referred to, data from the address in &80 is loaded into A register and stored into the address in addr with the commands LDA(&80),Y:STA(addr),Y. As Y register is loaded with 17 at the start, this is added to the addresses and each time round the loop Y is decreased until the value goes negative when the value in Y falls below zero. In this way, 18 bytes are transferred.

Another loop often used is this:

      LDY#0
      .loop
      <transfer commands>
      INY:BNEloop
      RTS

This transfers 256 bytes, i.e. a page of data and can be extended to transfer several pages by increasing the high bytes of the addresses each time the loop is completed and either counting the number of pages using X register as a counter or comparing the high byte of one of the addresses.

Now for the game, such as it is! It was based on a series of articles in Let's Compute, the kids' magazine. It is far from complete as the series was unfinished when the magazine stopped publication. It was also written in rather peculiar BASIC as it was to be adapted for other machines and I thought it a challenge to rewrite it in machine code. I thought also that there was sufficient to give you some ideas on writing an adventure program in machine code and also to show some further routines. If anyone has ideas on how to complete the game or to write another, I would be glad to help.

First, I will give a breakdown of the program referring to the lines of the assembler which produce the code, as I did last time.

      Line(s) 120 - 1090 . are the subroutines
             2000 - 2240 . are the main loop
             5000 ........ the buffer for osword (&FFF1) for the input.
             5010 ........ title data
             5020 - 5030 . data for printing the directions
             5040 - 5240 . message data
             5250 - 5280 . table of message addresses
             5290 ........ flag for the messages
             5300 - 5550 . words for COMMANDS
             5310 ........ directions - single letters
             5320 ........ directions - words
             5340 ........ actions
             5400 ........ useful objects to take
             5470 ........ other objects
             5550 ........ inventory
             5570 ........ stores the current lacation number
             5580 - 5780 . locations
             5790 - 5820 . table of location addresses
             5830 - 6030 . movement data
             6040 - 6180 . object descriptions
             6190 - 6210 . table of addresses for these
             6220 ........ data for object positions
             6230 ........ data for objects held
             6240 ........ two bytes for holding the action numbers

Below I describe the working of the program from the start line 200) and explain the subroutines as they are used again referring to the lines in the assembler listing.

At the start you are in location 1 - The Control Room, as the value in 'loc' (line 5570) is 1.

Line 2010 selects MODE 6 followed by two OSNEWLs.

Line 2020 uses the routine 'rep' to print twelve spaces <TAB(12)>, and prints the title routine 'prt' and an OSNEWL.

Line 2030 prints the underline.

The main loop starts at line 2040.

The messages are printed by loading the A register with the number of the message and then calling the routine 'prtmess'. The value in A is doubled by ASL A, 2 subtracted and the value transferred to Y register. Thus Y is loaded with 0, 2, 4 ... according to which message is to be printed. This is then used as an index to the two bytes or the address of the message. These two values are then loaded into X and Y registers before calling the routine 'prt'.

The routine 'prtloc' works in the same way except that A register is loaded with the value of the current location held in 'loc' so that you are told where you are.

This method of referring to an address in a table is often used in M/code. In this program, it is used to set up the printing of the data and what you can do according to the location you are since the value in 'loc' is used for the index.

Line 2080 prints message 2 and the routine 'ldobs' is called. This routine searches the data 'obsdat' (Line 6220) - Note that each four bytes are in reverse order as they are entered using EQUD.

The data is compared with the location number and, if they are equal, the object number, which is the value in Y register, is printed with the routine 'prtob' (Line 440). The 'obflag' (&75) is set to -1 (&FF) to show that an object has been found.

On completion of the search, the program jumps to 'ret' and the obflag is compared with &FF. If this is equal, the routine ends. If no object is found, then the obflag is zero and message 15 is printed (Line 580).

Message 3 is now printed and the routine 'prtdirs' called. This uses the data 'movdat' in lines 5840 - 6030. Each line refers to the corresponding location and allows for eight directions though only four are used in the program.

These lines define the 'map' of the game. The line 350 multiplies the value in 'loc' by eight and subtracts 8 to select the line of data and then compares each value in turn with &FF. If this is equal, the direction is not printed as this move is not allowed.

Now message 4 is printed asking you to enter your command. I have used page &900 to take your entry and this is cleared of any previous entry at lines 2160 and then a CR character entered.

OSWORD is then called with A register 0 and X and Y the address of the buffer. The five buffer bytes are: the address to be written to (&900), the length of the input allowed (255 chars) and the characters allowed (32-127). This enters the string input at &900.

The input required is the usual VERB NOUN. The words may be the first four letters of the complete word. The directions can also be single letters.

Line 2190 allows you to return to BASIC if you press ESCAPE.

The next two routines find the two words of the command and, if found, put the two action numbers in 'actn' (Line 6240).

'fndwd1' is in two parts. The first (Lines 600-640) check for a single letter command. Register X is used as an index to point to the address in the list of words. Initially X is set to 1 to point to the first letter in 'words1'. This is loaded into the A register and compared with the first byte of the input and if not equal, X is increased by two (Line 640) to point to the next letter. If none are equal, the byte &FF is reached and the program will branch to the second part at line 650.

If an equal is found, the program goes to 'fd' (Line 630). This will clear the second byte of 'actn' and since X is decreased, the action number will be read and then stored in the first byte. The routine ends and since no second word is added, the program runs through 'fndwd2'.

If a single letter command has not been found, the second part of the routine uses a loop to compare the four bytes of the words and X is increased five times when a word is not found. The value in X is stored in 'tempx' so this value always points to the first letter of the word and can then be reloaded and decreased to point to the action number which is then loaded into 'actn'.

'fndwd2' finds the second word in the same way as part two of the first routine so that the second action number is entered in the second byte of 'actn'.

The first action number is then compared at line 2220. If this is zero, no action is taken and message 9 is printed at line 2240 and the program returns to 'mm' at line 2140.

If the action is then less than 9, the action is a direction and so jumps to line 2230 and then to 'mov' line 830. This checks the relevant line of data as in 'prtloc'. If the value is &FF a jump is made to 'nomov' and message 8 is printed and then a return to 'mm'. If the number is for a valid move, the value read from the data is stored in 'loc' as this is the room number to which you have moved. A return to 'mvd' is made (Line 2230) and a line of s printed before returning to 'm' in line 2040 to print the new location information.

If INVE has been entered, the action number is 99 and a jump to 'inven' calls routine 'inv' followed by a line of s as above. The routine clears the screen and prints message 5. The data line 'obsheld' (Line 6230) is searched and, if an object number if found, the object is printed. If none are found then message 15 is printed.

Action 'verb' numbers may be between 10 and 49 though in this only 11, 12, 20 and 21 are used. These numbers call the routine 'act' in line 890. This compares the number or the routine required and, if not found, jumps to 'noact' to print message 9.

The four routines used so far work follows:

TAKE (Line 930) first compares the second action number. For useful objects that can be carried, the numbers may be 50 to 69. Numbers 50-55 are used here and, if these are not found, the number is compared with zero. If it is equal (no second word entered) the input word together with message 16 are printed. Otherwise message 17 is printed.

If a valid number is found, a jump is made to (tak'. First, 49 is deducted to obtain the object number and this is stored in 'obflag'. The number is transferred into X register and decreased by one and used as an index to compare with the data in 'obsheld'. If this is equal, message 13 is printed (Line 940). If not equal, the data is compared with the location number and, if this is not equal, message 14 is printed at line 950.

DROP (Line 970) works in a similar way but clears the memory address in 'obsheld' and stores the object number is 'obsdat' at the address indexed by the current location.

PUSH (Line 1010) first checks whether you are in location 1 and, if not, prints message 17 (Line 1060). Then if actn+1 is zero, the input is printed and message 16 (Line 1050). If you enter BUTTON (Action no. 75), the input and message 18 are printed (Line 1040). You must enter PUSH RED (or GREEN). If RED is chosen, obsdat+9 is set to zero and obsdat+8 loaded with 8 (the location no. of the red light), see line 1020. For GREEN, the addresses are set the other way at line 1030. The message 19 is then printed.

MEND (Line 1070) checks for location 16 and then for the second word as above. It then checks if the second word is CABLE (Action no. 73) and if so checks that you have object 4 (The SPANNER) and, if you have it, obsdat+12 is cleared and obsdat+13 loaded with 16. This changes the 'loose cable' to 'fixed cable'.

As I have mentioned the messages given by number, here is a list of them now for easy reference:

  1. "You are "
  2. "You can see "
  3. "You can go "
  4. "What next? "
  5. "You are carrying "
  6. "You wanted to go "
  7. "You wanted to "
  8. "You can't move that way"
  9. "- Do what?"
  10. "You take it"
  11. "You drop it"
  12. "You haven't got it"
  13. "You've already got it!"
  14. "It is not here!!"
  15. "Nothing useful"
  16. "What?"
  17. "You can't do that"
  18. " - Which button?"
  19. "Ok"

The original program gave the message 'You wanted to' (Messages 6 and 7) after eah entry but I have not included these as I felt they were not necessary.

Well, so much for the game as far as it goes. I hope that it's of interest and will encourage somebody to have a go at either completing it or writing another adventure game.

The article seems to have grown again and I hope I have explained all of it correctly! It was actually written two or three years ago and I had to refresh my memory on how I had done it! I would also say that it is not a perfect program and there is room for improvement but I thought it showed some routines one can use to write an adventure game.

In conclusion to USING THE ASSEMBLER, I hope the series has encouraged those who have not used the assembler to at least try and use it. I enjoy experimenting and have learnt by trial and error - especially the latter!

Not only does the coding speed up the program, it helps to save memory too. In BASIC, all variables need to be held in memory twice. They are defined in the program and then held in memory above LOMEM. Programs which are entirely in m/code include th variables and also they can use areas of memory below PAGE which cannot be used in BASIC. This is why I am hoping to code the program mentioned earlier.

However, I must finish the series now so I can press on with other ideas I have in mind. If anyone has any problems, or finds something that I have not covered, I shall be pleased to try and explain it. As I have said before, I do not count myself an expert but I am always ready to try and help in any way I can.

Richard Dimond, EUG #24

Richard Dimond