Inside BoulderDash

Inside this document:

Demo data

This is the raw data used to play the "demo" that starts up after a certain amount of idle time on the splash screen. It's a recorded demo; Rockford makes the same moves on the same cave (Cave A, 1).

The format of the demo data is as follows. The low nybble of each byte indicates the direction that Rockford is to move ($0 = end of demo, $7 = Right, $B = Left, $D = Down, $E = Up, $F = no movement). The high nybble indicates the number of spaces (number of frames) to apply that movement. The demo finishes when it hits $00. So for example, $FF means no movement for 15 turns, $1E means move up one space, $77 means move right 7 spaces, etc.

FF FF 1E 77 2D 97 4F 2D 47 3E 1B 4F 1E B7 1D 27 
4F 6D 17 4D 3B 4F 1D 1B 47 3B 4F 4E 5B 3E 5B 4D 
3B 5F 3E AB 1E 3B 1D 6B 4D 17 4F 3D 47 4D 4B 2E 
27 3E A7 A7 1D 47 1D 47 2D 5F 57 4E 57 6F 1D 00

Random numbers

There are two different random number generators used in BoulderDash I on the C64. One generates "real", unpredictable random numbers using the CIA timers in the C64.These are used in all the places in the game where a random number is desired but predictability is not.

The other is used to generate "random" numbers in a specific, repeatable sequence, used to generate cave data. This random number generator is written in assembly without reference to any hardware such as timers or the SID chip; it's purely mathmatical manipulations to give a pseudo-random sequence. The reason is that this random number generator must not rely on any hardware is that this way it can be implemented on other computer types other than the C64 and still give the same sequence of random numbers (when seeded specifically) in order to give the same cave layout.

BoulderDash I does not use the random number generator that is built into the SID chip in the C64.

The "real", unpredictable random number generator

A random number generator. Number is returned in A. Note that BoulderDash has two random number generators. This one is a "real" one in that it is based on three timers. There is another one that is portable to other computers and can be seeded to give the same sequence of numbers in a pseudo-random sequence.

6E68 Random.00      LDA $DC04   ;CIA#1  Timer A  Lo byte 
6E6B                EOR $DC05   ;CIA#1  Timer A  Hi byte 
6E6E                EOR $DD04   ;CIA#2  Timer A  Lo byte 
6E71                ADC $DD05   ;CIA#2  Timer A  Hi byte 
6E74                EOR $DD06   ;CIA#2  Timer B  Lo byte 
6E77                EOR $DD07   ;CIA#2  Timer B  Hi byte 
6E7A                RTS         ;

The predictable random number generator (for cave data)

Given RandSeed1 and RandSeed2, this routine generates the next random number in the sequence, returning the result in RandSeed2. This is the second random number generator: it is a machine portable generator that can be seeded to give a consistent sequence of pseudo-random numbers.
void NextRandom(int *RandSeed1,int *RandSeed2)
/* This is the mathematical random number generator from the Commodore 64
   implementation of Boulder Dash I. The 6510 disassembly is given in
   comments, and the C translation follows. */
{
    short TempRand1;
    short TempRand2;
    short carry;
    short result;

/*
            7085 NextRandom.00  LDA RandSeed1
            7087                ROR
            7088                ROR
            7089                AND #$80
            708B                STA TempRand1
        
            Note: ROR on the 6510 works like this:
              7-->6-->5-->4-->3-->2-->1-->0-->C
              ^                               |
              |_______________________________|
            In other words, it's a nine-bit rotate. Thus it takes two RORs to shift
            the low bit (bit zero) into the high bit (bit 7).
*/
    TempRand1 = (*RandSeed1 & 0x0001) * 0x0080;   /* Bugfix! */


        /*
            708E                LDA RandSeed2
            7090                ROR
            7091                AND #$7F
            7093                STA TempRand2
        */
    TempRand2 = (*RandSeed2 >> 1) & 0x007F;


        /*
            7096                LDA RandSeed2
            7098                ROR
            7099                ROR
            709A                AND #$80
            709C                CLC
            709D                ADC RandSeed2
        */
    result = (*RandSeed2) + (*RandSeed2 & 0x0001) * 0x0080;
    carry = (result > 0x00FF);
    result = result & 0x00FF;


        /*
            709F                ADC #$13
            70A1                STA RandSeed2
        */
    result = result + carry + 0x13;
    carry = (result > 0x00FF);
    *RandSeed2 = result & 0x00FF;


        /*
            70A3                LDA RandSeed1
            70A5                ADC TempRand1
        */
    result = *RandSeed1 + carry + TempRand1;
    carry = (result > 0x00FF);
    result = result & 0x00FF;


        /*
            70A8                ADC TempRand2
            70AB                STA RandSeed1
            70AD                RTS
        */
    result = result + carry + TempRand2;
    *RandSeed1 = result & 0x00FF;
}

ASCII text strings

Here are the text strings embedded in the program, some used on the title screen, and others used on the status bar during game play. Most of them are 20 characters long, since they are displayed in double-width characters on a 40 column screen.

"  BY PETER LIEPA    " 
"  WITH CHRIS GREY   " 
"1 PLAYER  1 JOYSTICK" 
" PLYR 1      PLYR 2 " 
" 000000 LAST 000000  000000 HIGH 000000 " 
" G A M E   O V E R  " 
"PLAYER 1: 3 MEN A,0 " 
"    OUT OF TIME     " 
" B O N U S  L I F E " 
" CAVE: A  LEVEL: 1  " 
" SPACEBAR TO RESUME " 
"PRESS BUTTON TO PLAY"

Uncovering and covering the screen

When a new cave begins, the cave initially starts off covered with animated steel wall, pieces of which are randomly removed to gradually show the new cave. Here's the process:
loop 69 times
	foreach line in 1..22
		randomly choose a horizontal position on that line
		uncover that position
	end foreach 
end loop 
uncover entire screen
During this time, the cave is actually running as normal (creatures moving around etc) I believe, and the covering wall (while it exists) is animated. The uncover sound is continously played, overring all other sounds.

Timing info

Time's Running Out!

During the last 10 seconds (when the timer shows 9, 8, 7 down to 0 seconds left), a Running out of time ping is played with increasing pitch indicating that time is running out.

Speed of play

In BoulderDash I, the speed of the cave is not set on a cave-by-cave basis as it is in the Construction Kit. Instead, the speed of play remains the same for any given level of difficulty, but increases with level of difficulty.

The speed of play is implemented with a delay loop. Each frame, if the CaveDelay is greater than zero, BoulderDash enters a time-delay loop for 90 cycles per unit of CaveDelay (remembering that the C64 runs at 1 MHz). The actual number of frames per second will vary depending on the objects in the cave; a cave full of boulders takes longer to process than a cave full of dirt.

How long is a second?

A second in BoulderDash is in fact longer than one real second. I guess this might be because of lost IRQ interrupts (BoulderDash does a lot using various IRQ interrupts, and some of BoulderDash I is not as efficiently written as it could be, and as presumably later versions were). I think I found that a second was about 64 ticks, although I can't remember if I actually timed it or whether 64 just seemed like a nicer number than 60. In any case, a game second is more than 60 ticks.

Finishing caves and intermissions

The player can begin a new game on caves A, E, I or M at any of five difficulty levels. The caves are arranged in the sequence: A, B, C, D, Intermission 1, E, F, G, H, Intermission 2, I, J, K, L, Intermission 3, M, N, O, P, Intermission 4. Thus the only way to get to an intermission (bonus life screen) is to complete the four caves preceding it. Note that whether or not the player succeeds in completing the intermission successfully, play continues with the cave that follows. Note also that if the player dies in the intermission, they do not lose a life.

When a player completes intermission 4, they are transported back to Cave A, but with an increased difficulty level. If the player finishes intermission 4 on difficulty level 5, they are transported back to Cave A at difficulty level 5 (again). There is no end of game sequence.


Web page design by Peter Broadribb <peterb@perth.dialix.oz.au>