Type-In Books
  • Home
  • Theme
    • Re:Code >
      • Re:Code Downhill Ski Jump
      • Re:Code Downhill Ski Jump Part 2
      • Re:Code Downhill Ski Jump Part 3
      • Re:Code Downhill Ski Jump Part 4
      • Re:Code Downhill Ski Jump Part 5
      • Re:Code Downhill Ski Jump Part 6
      • Re:Code Downhill Ski Jump Part 7
    • Usborne >
      • Usborne Computer Spy Games
    • Commodore 64 >
      • C64 Programmer's Reference Guide
      • Programming the Commodore 64
      • Machine Language for the Absolute Beginner
      • C64 Games Book
      • C64 Games Book 2
  • About
  • Retro Links

Re:Code Downhill Ski Jump

RE:CODE - Converting BASIC games to 6502 assembly
Article #4: Breaking down the BASIC Part 2

Previous article: Breaking down the BASIC Part 1

We covered the core initialisation and game flow in Part 1 and now all we have left are the subroutines for each of the stages, and to look at the player crash/game over sequence.

As the game was nice enough to split each stage into a manageable subroutine, we'll look through these one at a time. The stages aren't in chronological order in the code, but we'll step them in that order rather than the order they appear in the game.

Stage 1: Trees

Picture
2000 IFTI$>"000006"ANDI=1THENH=3:S=1+INT(RND(0)*32):RETURN
2010 PRINT"{purple}!"TAB(S)A$(I)TAB(38)"{purple}!":RD=RD+1:I=I+1:IFI<5THEN2023
2021
I=1:S=1+INT(RND(0)*34):POKEM4,17:POKEM5,30:POKEM6,100
2023
IFRD<120THENRETURN
​2030
IFI=1THENH=2:RD=0:GOTO4000
There are a number of things going on in this short routine and a similar pattern is followed for each of the stages so it's worth taking the time to properly understand this first one.

There are a number of variables in use which would be useful to understand. Looking across the routine it appears that I is used for the index on our character arrays. If you remember from the first part we had A$, B$ etc. and these seemed to be string arrays set up to hold the chars for the houses, trees, ants etc. that appear on the screen. It would appear then they are arranged in rows as they appear on screen and I is used to step through these rows.

S is a random number which is then used in a TAB statement - so clearly this is used to randomise the X position of the character object on the screen. RD looks like a counter that increases as we progress through the level and indicates when we've reached the end of a stage.

So with this in mind, let's step through line by line:

The first line is looking at the internal timer to check that 6 seconds has passed and also make sure we're on character index 1 (i.e. we're not part way through printing a graphic block). If so the stage is changed to 3 and the tab space (S) is randomised. This seems a little odd at first, but if we glance at the routine for H=3 and use what we know from the image above we can see that this line is effectively printing a house every 6 seconds (instead of a tree). The mechanics of how it is doing this we can look into in more detail when we get to stage 3.

If we're not printing a house then the routine drops into its normal process. It prints the purple barriers at the side of the screen and then in the middle of that at the random tab position S it prints the current row of our tree graphic (A$). Our stage counter (RD) is incremented and then we check to see if we've finished printing our tree (I=5), if not we skip the next line.

If we have finished printing the tree a new tab position is randomised and stored in S. We switch on the triangle waveform in $D404 with a frequency hi/lo of 30/100. This makes the intermittent 'pip' noise as we progress through the stage.

​The next line is checking RD to see if we've reached the end of the current stage - if not, we return to our main game loop. The final line then points H to the next stage and resets the progress counter before jumping to an end of level routine in 4000 (which we'll look at later).

Stage 4: Forest

Picture
2500 IFTI$>"000006"ANDI=1THENH=3:S=1+INT(RND(0)*32):RETURN
2510 PRINT"{purple}!"TAB(S)A$(I)TAB(S1)A$(I)TAB(38)"{purple}!":RD=RD+1:I=I+1:IFI<5THEN2533
2531
S=1+INT(RND(0)*15):S1=19+INT(RND(0)*15):I=1:POKEM4,33:POKEM5,40:POKEM6,100
2533
IFRD<100THENRETURN
​2535
IFI=1THENH=5:S=5:I=2:RD=0:POKE53281,15:POKE53280,15:POKEM4,33
​2540
RETURN
This routine is similar to stage 1 in that it prints a house every 6 seconds between the trees. The second line is very similar also, except this time we are printing two trees at tab space S and S1. S1 is initialised at the beginning of the game.

The third line creates our random tab positions for the next two trees to be printed, but the values of S and S1 are set so that they never overlap. This time the sawtooth waveform is switched on with frequency hi/lo of 40/100 - again used to make the short sound as we progress through the level.

This level is shorter than stage 1 with a line checking for stage completion at RD=100. If we have completed the level the stage is set to H=5, but the index is set to 2 which must be required for stage 5 (we'll check what this is doing when we reach that point). The border and background are set to light grey and we switch the sawtooth waveform on.​

Stage 3: House

Picture
3000 POKEM4,17:POKEM5,(I*10):POKEM6,(I*10):PRINT"{purple}!"TAB(S)"{dark gray}"B$(I)TAB(38)"{purple}!":I=I+1:IFI<6THENRETURN
​3040 H=H2:TI$="000000":I=1:S=10:S1=20:RETURN
We can see now that this isn't really its own stage. In fact, the level descriptor in the NN$ array bizarrely refers to index 3 as "SOD". Instead this routine is called by the two stages that require a house to be printed intermittently.

On the first line we're switching on the triangle waveform and the frequency hi/lo is set based on the value of our character index. If you remember from the play through this makes that little rising tone as a house appears. The next bit of code prints the barriers at the sides of the stage and prints a row of our house graphic at the random tab position S before incrementing the character index.

On the next line we can finally see the purpose of H2. It is used as a holding position for the current stage so that once we've printed a house we return back to the correct stage (i.e. 1=Trees, 4=Forest). The timer is reset here also so that we don't get another house for at least another 6 seconds. The tab positions S and S1 are then fixed (rather than randomised) presumably to ensure we don't end up with trees too close to the house.

Stage 2: Mountain Pass

Picture
3100 PRINTTAB(S)"{blue}%%{space*7}%%":RD=RD+1:I=I+1:IFI>SXTHENIX=-IX:SX=INT(RND(0)*15):I=0
3140
S=S+IX:IFS<3ORS>24THENIX=-IX
3143 IFRD<100THENRETURN
​3145 H=4:H2=4:POKE53281,3:POKE53280,3:RD=0:S=4:S1=33:I=1:TI$="000000":RETURN
This level takes a slightly different approach - as should be obvious from the screenshot. S is used to determine the X position of the left most barrier, with a constant 7 spaces between the right barrier. SX and IX are initialised to 5 and 0.5 respectively at the beginning of the game.

​In our first line we incrementing the stage counter (RD) as normal but the character index (I) performs a slightly different function. In this case it increments by 1 until it is greater than SX. Once this happens SX is randomised between 0 and 14 and I is reset. Alongside this we invert IX (which switches between 0.5 and -0.5).

What this code is doing is setting a random number (SX) for the length of time the barriers slope in a particular direction (left/right). Then our index counter (I) counts up to this value and we print our sloping barrier, before setting a new random length and changing direction.

The second line adds our current offset (0.5 or -0.5) to the tab position S - of course, a tab can't be 0.5 of a char but BASIC will just use the integer part and this size offset just means the gradient isn't too steep. There's also a check in case our tab position is getting too far outside a desired range on the screen - if it is we switch direction with IX.

Finally we're checking for the end of level, and if so we set our stage pointer to 4 change the border and background to red and initialise our stage variables ready for the next stage.

Stage 5: Ice Cracks

Picture
3200 IFI>1THEN3220
3211
PRINT"{purple}!"TAB(S)"{white}"E$TAB(38)"{purple}!":GOTO3225
3220
PRINT"{purple}!"TAB(38)"{purple}!":POKEM1,15-I
3225
POKEM1,15-I:POKEM4,33:POKEM5,20:POKEM6,100:RD=RD+1:I=I+1:IFI>10THENS=INT(RND(0)*27):I=1
3236
IFRD>80ANDI=8THENH=7:H2=7:RD=0:I=0:POKE53281,12:POKE53280,12:VL=15
​3240
RETURN
A slightly different level again. Our first line this time skips printing the 'ice crack' if our character index isn't 1 - in that case it just prints the left and right edge barriers (and the preceding routine set I=2 to avoid an immediate ice crack at the beginning of the stage).

M1 is the reference to the SID volume register - so we are setting the volume inversely proportional to our character index I (i.e. as it counts up, the volume reduces). On line 3225 we're switching the sawtooth waveform on with a frequency hi/lo of 20/100. We then increase our stage counter (RD) and our character index. Every time I passes 10 we reset it and create a new random tab position for the next ice shelf.

Finally we check if our stage counter has passed 80 and the index char is 8 (presumably to create a bit of space between the last ice crack and the next level). If we're at the end of the stage, we move on to stage 7 setting the background and border to medium grey and initialising our stage variables.

Stage 6: Tunnel

Picture
3300 RD=RD+1:IFRD>=85ANDRD<100THENPRINT"{purple}!"TAB(38)"{purple}!":RETURN
3306
POKEM4,33:POKEM5,10:POKEM6,100:POKEM1,VL:VL=VL-2:IFRD>=100THENRD=0:H=8:H2=8:I=1:S=15:
​
POKE53281,3:POKE53280,3:POKEM1,15:RETURN
3310
IFI<=7THENPRINT"{space*12}{gray}%{green}%%%%{space*6}{gray}%":VL=15:GOTO3350
3320
IF(I>7ANDI<=13)OR(I>20ANDI<27)THENPRINT"{space*12}{gray}%{space*10}%":GOTO3350
3330
IFI>13ANDI<=20THENPRINT"{space*12}{gray}%{space*6}{green}%%%%{gray}%":VL=15
3350
I=I+1:IFI=26THENI=1
3360
RETURN
The tunnel stage is another stage variant and handles differently. It's worth noting at this point that our level name index (NN) seems out of kilter with the order of the actual levels. This stage has the name 'mushrooms' in the data statements, but is clearly in the wrong order. Something to fix!

This routine starts by incrementing the stage counter and if it's between 85 and 100 it just prints barriers at the edges of the screen, basically to create a bit of run-off time before the next level.

In the next line we switch the sawtooth waveform on with a frequency hi/lo of 10/100. The voice volume is set to decrease by 2 on each run of the loop. We test for the end of level earlier in this routine and progress to stage 8 if at the end (with the usual initialise of the stage variables). Border and background are set to red for the following stage.

The next few lines are used to create the green/non-green spaces in the tunnel. If our character index (I) is 7 or less then we display grass on the left and space on the right, between 7 and 13 and we're just displaying space, between 13 and 20 we have space on the left and grass on the right (I'm assuming the green is grass anyway!). For values of I between 21 and 25 we don't print anything at all

Stage 7: Mushrooms

Picture
3400 RD=RD+1:IFRD>140ANDI=0THEN3455
3415
IFRD>116THENPRINT"{black}!"TAB(38)"{black}!":RETURN
3420
POKEM4,17:POKEM5,15-(I*2):POKEM6,20:PRINT"{black}!"TAB(S)D$(I)TAB(38)"{black}!":I=I+1:IFI=4THENI=0:S=1+INT(RND(0)*33)
3440
RETURN
3455
I=8:H=6:H2=6:RD=0:GOTO4100
​3490
RETURN
Back to the standard stage type. Nothing too complicated going on here and it runs very similar to the trees level but this time with mushrooms (in the D$ array).

Again there is a run-off area at the end of the level between RD=116 and RD=140 before we start stage 8. Also, each time a mushroom is displayed we trigger the triangle waveform with a frequency hi that alters based on the value of the character index.

​There's a slight change in the end of level routine here in that we jump to a routine at line 4100 - we'll look at this later. Note that line 3490 is never reached so can be ignored.

Stage 8: City

Picture
3500 I=I+1:RD=RD+1:IFRD>100ANDI>5THENH=9:H2=9:RD=0:I=3:RETURN
3530
IFI>5THENI=1:S=1+INT(RND(0)*32)
​3540
PRINT"{purple}!"TAB(S)B$(I)TAB(38)"{purple}!":POKEM4,33:POKEM5,(I*2):POKEM6,20:POKEM2,10:RETURN
Another straightforward level again, this one re-using the house graphic in the B$ array - only this time it's all houses and no trees. The sound to accompany a house uses the sawtooth waveform and as in the previous routine the frequency hi value is changed in relation to the character index. The voice 1 attack/decay register is set to 10 here, but it is already 10 anyway so this is extraneous code.​

Stage 9: Slalom

Picture
3600 IFI=2THENPRINT:GOTO3640
3610
IFI>2THENPRINT"{dark gray}!"TAB(38)"{dark gray}!":GOTO3640
3630
FORU=1TOS:PRINT"{green}%";:NEXT:PRINT"{blue}!{space*7}!";:FORU=1TO39-S-9:PRINT"{green}%";:NEXT
3640
I=I+1:IFI=18THENI=1:S=1+INT(RND(0)*19)
3650
RD=RD+1:IFRD<100THENRETURN
3655
IFRD<130THENI=16:RETURN
​3690
H=10:H2=10:RD=0:I=1:S=10:S1=20:POKE53281,15:POKE53280,15:RETURN
You can't fault this game for its level variation and the slalom level is yet another different level type, although the approach isn't much different to the others.

We start by checking if the character index is 2 - if you remember when this stage is initialised by the stage before it the value for I is set to 3. For index=2 a single print command is called and we skip the other parts of the graphics routine.

Where our character index isn't 1 (or 2) we just print the barriers to the left and right of the screen. Otherwise S is used to determine the 'opening' of the gate and a barrier is printed across the screen. You'll spot the semi-colon at the end of the print statement - this is so that when we PRINT at I=2 we don't print a blank line. It's not entirely clear why this little workaround was put in but we can assume there was some issue that meant it was added in.

Our character index goes up to 17 this time to give adequate space between each slalom gate. The second to last line fixes the value of the index at 16 to recreate a run-off area at the end of the level. Once the level is complete we progress on to stage 10 and initialise our stage variables.

Stage 10: Ants

Picture
3700 RD=RD+1:IFRD<160THEN3715
3712
H=11:H2=11:RD=0:I=0:S=5:S1=25:POKE53281,12:POKE53280,12:RETURN
3715
IFRD>132THENPRINT"{purple}!"TAB(38)"{purple}!":RETURN
3720
PRINT"{purple}!"TAB(S)F$(I)TAB(S1)F$(I)TAB(38)"{purple}!":I=I+1:IFI<4THENRETURN
​3735
I=1:S=1+INT(RND(0)*15):S1=19+INT(RND(0)*17):POKEM4,129:POKEM5,15-(I*2):POKEM6,10:POKEM2,10:RETURN
Another standard-ish level. Like the 'forest' level this uses S and S1 to create ants at two points across the X position on the screen. The noise waveform is used for ants, again with a changing frequency based on the character index. And again we see the extraneous setting of the attack/decay register.

We can see across these routines that they're a bit messy really and there's essentially a lot of repeated code - so this is definitely something to keep in mind when we start the assembly version. The order is also mixed up across levels and within the routines - so sometimes the end of level is tested at the beginning of the routine and sometimes at the end. We'll want to standardise all of these - we'd want to do that even if we were re-writing this in BASIC to be honest.

​At the end of this stage we move on to stage 11.

Stage 11: Mushroom Field

Picture
3800 RD=RD+1:IFRD>116ANDRD<=140THENPRINT"{purple}!"TAB(38)"{purple}!":RETURN
3810
IFRD>140THEN3890
3815
POKEM4,17:POKEM5,15-(I*2):POKEM6,20:POKEM2,10:PRINT"{purple}!"TAB(S)D$(I)TAB(S1)D$(I)TAB(38)"{purple}!":I=I+1:IFI=4THENI=0:S=1+INT(RND(0)*15):S1=19+INT(RND(0)*15)
3840
RETURN
3890
IFDEMO=1THEN15
​3895
FORU=1TO500:NEXT:H=1:H2=1:P=P+50:RD=0:I=1:S=10:S1=20:L=L-1:RETURN
Another fairly standard level at the end - we're re-using the mushroom graphic, but doubling up to create a 'mushroom field'. This is essentially the same stage type as the 'forest' level which doubled up on trees. This level uses the triangle waveform with a decreasing frequency based on the character index.

The character index resets to 0 on line 3815 which is unusual compared to other routines as there is no value in the D$ array for this. It's either deliberate to create a gap between mushrooms or it's a mistake in the code. Hard to tell with this particular program!

​There's a slight change at the end of the level given we're on the last stage - if we're on the demo mode then we're sent back to the title screen (effectively) on completion, if not the player loops back to stage 1.

Two other variables are changed here: P is increased by 50 and we're not sure what purpose that serves yet, so we'll wait for any mention of it in the end game section. L is decreased by 1 - lives possibly? We'll find out in the next section.

​Those are all of the stages so the only bits of code to look at now are the two end of level routines called after the 'Trees' and 'Mushrooms' stages, and of course the player crash and game over routines.

Given this article is getting quite lengthy again (!) we'll cover those in a separate article where we'll also start to list out all of our variables and elements of the game flow. Thanks for reading.

Next up: Breaking down the BASIC Part 3

github
gamebase

Site powered by Weebly. Managed by 34SP.com
  • Home
  • Theme
    • Re:Code >
      • Re:Code Downhill Ski Jump
      • Re:Code Downhill Ski Jump Part 2
      • Re:Code Downhill Ski Jump Part 3
      • Re:Code Downhill Ski Jump Part 4
      • Re:Code Downhill Ski Jump Part 5
      • Re:Code Downhill Ski Jump Part 6
      • Re:Code Downhill Ski Jump Part 7
    • Usborne >
      • Usborne Computer Spy Games
    • Commodore 64 >
      • C64 Programmer's Reference Guide
      • Programming the Commodore 64
      • Machine Language for the Absolute Beginner
      • C64 Games Book
      • C64 Games Book 2
  • About
  • Retro Links