DRAUGHTS PART ONE | ||||||||||||||||||||||||
DRAUGHTSNow that we can enter and edit machine code, it's about time we started using it for something useful, and hopefully interesting. Draughts is a program we have to be very careful with. Here's what it will look like in BASIC:
As you can see, the vast, vast majority of it will be entirely in machine code. The machine code will begin immediately after the BASIC program ends. However, in order that we may edit it we shall temporarily store it a little higher up in memory than that - in the fourth K. Also, in order that we may have the BASIC part of the program at the right location it will be necessary to MOVE the machine code part of HEXLD3. NEW ROM users should start typing POKE 16389,74 and then NEW, and then load the program HEXLD3. Now, to move it, write the following program to the few spare characters at the end of the REM statement:
Now for the tedious bit. Every address used in the machine code part either begins with 40 or 41. You'll have to go through the listing and change each 40 to a 4A, and each 41 to a 4B (the changes are to be made in the copied version, not the original version). That done change every address in the BASIC parts that calls a USR routine. To make a change you must add 2560 to each number. Now delete line one by typing its line number. The program should still work, but you'll need to type RUN 400 in order to SAVE it. Make sure that the variable BEGIN (now at 4A3C or 4A93) contains a value of 4A00. New ROM users change line 500 to:
[Thunor: The above NEW ROM modifications were discussed earlier in chapter 9's addendum.] Download available for 16K ZX81 -> chapter11-hexld3d.p.[This is a version of HEXLD3 which includes all of the modifications outlined above i.e. BEGIN (at 4A93) contains the value of 4A00 and HPRINT is at 4A82 etc.][Thunor: OLD ROM users, please read this before proceeding: DIM is limited to 255 (try DIM O(256) and see for yourself) which equates to 256 elements * 2 bytes = 512 bytes maximum. The last instruction in this chapter is at 4DF0 and with HEXLD3 starting at 4A1A means that 983 bytes must be catered for. Therefore the version that the author is presenting in this book cannot be saved with HEXLD3.] Now type RUN 100 to start the WRITE routine and re-enter the board printing routine. Again you'll need to load it to address 4C09. The listing is the same as it was before. Turn to chapter seven and simply retype the whole thing. The instruction RAND USR 19477 should now print a picture of a draughts board in the top left hand corner of the screen. Try it and see. Now each part of this program will be explained in great detail, so don't worry if a program this size seems a daunting prospect. Right now we are only going to input the first part. It starts off with some data.
[Thunor: The "4C97:" on the left is the memory address to write to and is a convention used throughout the remainder of the book.] This represents the directions in which we are about to allow moves. The numbers in the data are -6, -5, 6 and 5, which, in the board numbering system the computer will use, are simply the numbers we add to one square to reach another. The first, and simplest thing to do, is to make a copy of the board as it appears on the screen. The copy is called WKBOARD, for it is the part of RAM on which the computer will do its working out. The address of WKBOARD is to be 403C. That's not a misprint, it really does say four zero three C. For OLD ROM users this is just beyond the end of the BASIC part of the program, but for NEW ROM users it is slap bang in the middle of the system variables. Is this wise? We will in fact be overwriting the 33 byte area PRBUFF and part of the calculating store MEMBOT. This doesn't matter since we will not be using LPRINT, not be attempting to use floating point calculations, and in fact not using this area at all. This will not cause a crash. During the construction of this program, OLD ROM users should use the address 4B3C instead, since 403C is in mid-program. You can always change it when your program is complete. Here's the copying routine. You should load this to address 4CE4.
Notice the way LDI was used instead of LDIR. This is a very useful way of saving space. What we are doing is incrementing HL each time round, so that only the black squares are copied, not the white ones. This loop is repeated 2A (forty-two) times, since in addition to the squares on the board, one or two characters from the border are also copied. Notice that although LDI decrements BC, it is C that is decremented, not B, so that the DJNZ instruction will still count correctly. OLD ROM users can easily check that the routine is working by listing from 4B3C using HEXLD3, after the program is run. NEW ROM users can check by replacing the RET instruction The next part of the program is just as simple. If you take a look at the board printing program, you'll see that the last thing printed is a row of fourteen spaces. What this is is a "window" in which our machine code program can display messages to the user, so the next thing to do is to fill this window with spaces in order to wipe out any error message that may have been there.
Notice that we have actually overwritten the previous routine's RET instruction, so that it will automatically continue into this one.
The next part is for NEW ROM users only. OLD ROM users please ignore it. The following will cause line one (that is BASIC line one) to be re-executed as soon as the next RET instruction is received. Note that this overwrites the six NOPs in the previous section.
This fools the ROM into thinking that the next line to be executed begins at address 407D, which is the first byte of the program. It doesn't return to BASIC immediately however, it will continue with draughts until a RET instruction is reached. Now the program seriously starts. We assume that a move has been input as A$, which is the first item in the variable store. Here's how to input a move. Look at the diagram of the board. There are sixty-four squares, but only thirty-two of them are playable. Each square has a coordinate from 11 to 88. Notice that these are printed without seperation. The first digit refers to the number down the left (and right) hand side of the board, and the second digit refers to the number along the top or bottom of the board. There are four different directions you may move in. These are called A (up-left), B (up-right), C (down-right) and D (down-left). This is indicated on the diagram. To input a move simply type in the coordinates and a letter (A, B, C or D). There should be no spaces in this input. For instance, to move from square 61 to 52 you should input "61B". Now for a program to interpret this input. Follow this carefully:
A small amount of additional explanation concerning the input here, which applies to OLD ROM users only. To input a simple move, such as from 61 to 52, you in fact need to input "shift W space 61B". A simple move must always be preceded by shift W space, and this also applies to single jumps. Double jumps, triple jumps etc. are a little different, and we shall cover them later. As I have said, this is for OLD ROM users only. The above routine initially loads A with the length of the input string, and then subtracts three, so that for an ordinary move A ends up as zero, for a double jump A ends up as one, for a triple jump A ends up as two, and so on. Then IF A is 00 it is changed to FF. This is so that we can check up on whether or not a player has made a move, or a jump, later on in the game. This quantity, which is ordinarily FF, is stored in the register E. We then continue.
In the above routine the first coordinate is multiplied by eleven, by making use of registers B and C, and then the second coordinate is added. Note that if you input "12" as your square then because of the Sinclair character codes the program thinks that the first coordinate is actually 1D, and that the second coordinate is 1E. This actually leads to a result of 5D. Rotating right gives 2E, together with a carry indicating that the player has not cheated by giving a white square instead of a black one. The next five bytes deal with what happens if the player has cheated. These are:
The subroutine ERROR, which requires one byte of data (here the byte 1D, the character code of "1") looks like this:
Notice what happens. The message "ILLEGAL MOVE 1" appears on the screen, and no piece is moved. The player is then required to re-input [his/]her move which will then be checked in exactly the same way. If no error is found (yet) the program continues.
0E is simply the required factor to exactly match A to the low part of the address of the working-board square. For instance, adding 0E to 2E gives 3C, and 403C is the start of WKBOARD.
A brief explanation of the last six lines here. A is loaded
At this stage the program will jump or move as the case may be (in other words it will decide for itself - you don't need a special input) and will so far check for three types of error. These are:
This will copy the computer's working-board back onto the screen so that you can see what has happened. You can also alter the data for the board-print routine, and so set up a board in mid-game in order to test some of the error checks if you want. To make the program run, add the following BASIC program lines:
The program is activated by the command RUN 4. Don't forget you can still use HEXLD3 to list, but you must now use RUN 10 to bring this into operation. If you type RUN on its own accidentally you will get an input prompt. Break out immediately! If you don't the results will be unpredictable. I don't think it will crash, but just to be on the safe side.... And now a check to determine whether or not the human player has reached the other end of the board. If so, this next routine will automatically change [his/]her piece into a king.
Notice the new error check. If a player attempts to make a king in mid-move, that is, if [he/]she jumps to the back row and intends to jump out again in the same go, then an error will be detected and "ILLEGAL MOVE 4" [will be] printed to the screen. This is because according to official rules a piece does not become a king until after you remove your fingers from it. Of course in this game your fingers are never on the piece in the first place, but we presume that this is what the rules are intended to mean. Remember that E contains FF for a single jump, and 01 for a double jump. LD A,E/INC A/CP 02 will only give an error if E is one or more. If E is 00 (which if you've input a multiple jump it will eventually be), the move will go ahead successfully.
Well, all of the possible error checks have been made, and the program contains a loop which will allow for the inputting of multiple jumps. Here's how a multiple jump should be input. To jump from square 63 first in direction A, then in direction B, then finally in direction C, just input "63ABC" - it's that simple. OLD ROM users need to note the following convention though:
And so on... The sequence is W, E, R, D, F, S, A, T, G. I doubt very much whether you'll ever need a 4-ply jump though. Even using a triple jump seems rather unlikely. The next thing that should happen is that the computer should make a move in response, but we'll leave that to another chapter, since it has a bit of decision making to do. But there is one question to be answered first. What if it now has no pieces left to move? What if the player's last move removed its last piece? This has to be checked for. If this is the case then the player has won, and we must somehow indicate this. Here is the final check:
And the subroutine....
Now we reach the exciting bit. What happens if the player HAS won? I'm not actually going to tell you - just input it and find out. To test it you'll have to alter the data that sets up the initial board, and arrange it so that you can take all of the computer's pieces.
Notice how, in the last two lines the return address is removed from the stack, so that the next item on the stack is the return to BASIC address. The next RET will of course do just that. Download available for 16K ZX81 -> chapter11-draughts1.p.[Thunor: Unfortunately due to the BASIC program and the O$ array expanding in size there isn't enough space remaining below 4A00 to include REM based instructions, therefore I'm printing them here:
|