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 #5: Breaking down the BASIC Part 3

Previous article: Breaking down the BASIC Part 2

So that's all the levels sorted along with the main game flow routine which just leaves the crash and game over routines and two curious end of level routines which we'll look at first.

​First up is a subroutine at line 4000

End of Stage 1

Picture
4000 POKE53281,14:POKE53280,14:I=1:S=13:PRINT"{white}!!!!!!!!!!!!!!{space*8}!!!!!!!!!!!!!!!!!":POKEM4,17:

​
FORU=100TO10STEP-1:POKEM5,U:POKEM6,UL:NEXT:FORU=50TO80:POKEM5,U:POKEM6,UL:NEXT:RETURN
The first thing to note is that this routine is only called once - and that is at the end of stage 1. The routine sets the background and border colour to light blue, resets the character index (I) and sets the first tab position (S) to 13. Then it prints the opening to the 'mountain pass' stage as can be seen in the image above.

The second part of this routine plays the strange little noise you get at the end of the stage. This is achieved by setting the frequency high byte ($D401) to firstly a decreasing value, then an increasing value. The low byte is set to the variable UL here, but UL is never initialised in the game, so this value will always be zero.

What's curious about this routine is that it is only used once at the end of stage one. It's unclear whether it was originally written to be used at the end of every stage and then was subsequently removed from other stages, or if the programmer just forgot to call the routine at the end of other stages.

Either way, it probably would be good to have a consistent end of stage routine. As it stands, this routine could just be part of the first stage routine. This same pattern is followed for the routine at 4100 below...

End of stage 7

Picture
4100 POKE53280,13:POKE53281,13:I=1:POKEV+39,2:POKEV+40,2:

​L=L-
1:IFH=6THENPRINT"{blue}!!!!!!!!!!!!!!!!!{space*6}!!!!!!!!!!!!!!!!":RETURN
Again, this routine is called once - at the end pf stage 7 (the first mushroom stage). The border and background are set to light green and the character index (I) is reset. Then, for some strange reason, the colour for sprite 0 and 1 are set to red - this hints that maybe the original game had additional sprites in it, or there were plans for additional sprites. You'll remember from earlier articles that our player sprite uses sprite 2 and all other sprites are switched off. So this code is redundant.

On the next line the variable L is decreased by 1. We thought before that this was maybe something to do with player lives - but why it's decreasing is unclear. The crash routine should help us understand this better.

Finally, we check if the stage number (H) is 6. Well, given that the line that calls this routine sets H to 6 then this is always true. Again, this suggests a routine that was going to be called at the end of more than one stage, and with those extra sprite settings maybe there was going to be some sort of end of stage baddie.

The last part displays the blue barrier that can be seen in the image above, before we return to our main game routine.
​​

CRASH!

Picture
5000 POKEV+41,11:UM=200:POKEM1,15:POKEM2,10:POKEM3,200:POKEM4,17:
POKE2042,195:FORU=100TO200STEP1:IFAX<>0THENPOKEV+16,4

5036
POKEM5,UM:POKEM6,100:POKEV+4,AM:POKEV+5,U:UM=UM-2:NEXTU:POKEM4,129:
FORU1=15TO1STEP-.1:POKEM1,U1:POKEM5,30:POKEM6,100:NEXT:POKEM5,0:POKEM6,0:
​
FORU1=1TO500:NEXT:POKEV+21,0:L=L+1:IFL<3THENPOKEV+21,0:POKEV+16,0:GOTO25
 So, you hit a tree...

The crash routine starts by setting our skier sprite colour to dark grey and initialises UM which is used in the following line for the frequency high byte. The next part initialises the sound registers setting volume to full, the attack/decay register to 10, sustain/release to 200 and triggers the triangle waveform gate. The sprite pointer (2042) is changed to point at our crashing skier sprite (see image above) before we start an increasing loop on U from 100 to 200 which is used in the next line to set the sprite Y position.

The last bit of code checks the skier XMSB value and sets the relevant value in the XMSB register - however, this seems redundant. If the skier is on the right-hand side of the screen at the point of the crash then this register will already be set, there's no need to set it again.

The following line sets the voice frequency with UM used for the high byte and the low byte fixed at 100. The skier sprite X position is set to AM, but again it will already be this value so there seems little point in setting it again. The sprite Y register is then set with our increasing U value - effectively this will move the sprite down the screen. At the same time the frequency high byte is decreased.

Once this loop is finished we switch to a noise waveform (129) and set another loop to create a decreasing volume from 15 to 1 with a frequency high of 30 and a frequency low of 100 (again, already set!). This will make the final 'crash' sound when the sprite reaches the bottom of the screen.

​The final line has a little delay before switching off the player sprite and then increasing our variable L. So we have a sort of reverse lives here - that is, a count of how many times you've died. An interesting approach!

Finally, there's a test to see if the 'number of deaths' is less than three. If it is then we switch off the player sprite (again? Sheesh!) and reset the XMSB register (probably unnecessarily) before returning to the main game loop.

If we've died three times or more, we drop through to the game over routine...​

Game Over

Picture
5100 PRINT"{clear}":AM=20:FORU=1TO30:PRINT"{home}{down*17}"TAB(U)G$(1):
PRINTTAB(U)G$(2):PRINTTAB(U)G$(3):PRINTTAB(U)G$(4):PRINTTAB(U)G$(5):
FORU1=1TO100:NEXT:POKEV+21,0:POKEM1,15:POKEM4,33:POKEM5,AM:POKEM6,100:
IFAM=20THENAM=15:GOTO5180

5170
IFAM=15THENAM=20

5180
NEXT:POKEM5,0:POKEM6,0:POKE53272,21:PRINT"{clear}":POKE53280,15:POKE53281,15:
PRINTCHR$(14):PRINT"{blue}{right*13}W-I-P-E{space*2}O-U-T ":
PRINT"{down*3}{green}You were splatted by the "NS$(H):IFH=3THENH=H2

5230
PRINT"{down*3}{red}You covered:{green}"P"{red}metres.":
PRINT"{purple}{down*3}Longest distance covered is{gray}";:IFHS<PTHENHS=P

5237
PRINTHS"{purple}metres.":FORU=1TO22STEP2:POKEM5,FM(U):POKEM6,100:FORU1=1TOFM(U+1):NEXT:
POKEM5,0:POKEM6,0:NEXT:FORU=1TO3000:NEXT:PRINT"{down*5}{blue}{space*13}{reverse on} Press fire. ":U=0

5260
IFPEEK(56320)=111THEN11

5270
U=U+1:IFU<1500THENGOTO5260 5

280
GOTO11
We know that when we die we get the little ambulance animation before the final game over screen, so it should be relatively obvious what is happening in the next few lines, broadly, but we'll look at the detail.

The first line clears the screen and sets AM to 20 - we should assume this is a different AM used for the skier X position. A quick scan of the code shows this is used for the frequency high byte and alternates between 20 and 15, effectively giving us a nee-naw ambulance sound.

The next section creates a loop around the variable U which is used as a tab stop for the ambulance chars held in the variable G$. It seems strange that a sprite hasn't been used here instead, and that's certainly something we'll look to rectify in the recode.

After the ambulance chars have been printed we disable sprites again - not only has this been done several times before we reach this point but it's also embedded within the loop so we're doing it on every redraw of the ambulance. Aaarrgh!

We then set up our sound effect - volume is set to maximum, frequency high set to AM as mentioned above, frequency low set to 100 (AGAIN!) and a sawtooth waveform enabled​. We then alternate the value of AM, the following line used to switch back to the value of 20.

Line 5180 ends our siren loop and resets the frequency registers. The poke into 53272 is actually switching the VIC bank so that the normal character set is enabled - if you remember, the in game characters were created over the standard character set removing the ability to use the alphabet. We'll fix this by relocating the chars beyond the end of the standard set.

Next the screen is cleared, the border and background are set to grey and we display our game over message. Here we finally use the level name variable NS$ that we set up at the beginning of the game - this seems a waste to just use here and we can maybe display it on the screen during the game. The final bit of code changes the value of H but it looks like this should have been done before the code that prints the level name. If you remember, H=3 has the unusual level name of "sod", but the reality is that it isn't a real stage but a part of a stage (it's the bit that adds houses). This code was meant to switch in the real level name, but an error means that we can actually be shown to have died on level "sod". Oops. Something to fix.

Next we display the total distanced travelled by the player using the variable P - and again, why not display this on the screen during the game? The final section of this routine checks that distance against the previous high score (HS), over-writing if it is higher.

The following line prints the furthest distance travelled and then uses U as a pointer to the notes and duration stored in the array FM which we already assumed was the 'Funeral March' ditty played at the end of the game. The values in FM are pairs of frequency high byte and note duration. We again set the frequency low byte to 100 because, well... why not? Finally we print a 'press fire' message.

The final few lines wait for the fire button to be pressed before returning to the title screen. A counter is used in U and if that reaches 1500 we return to the title screen anyway, regardless of whether fire was pressed or not.

And that is that. All of the code (excepting a few bits of orphaned/redundant code) and what it is doing. So now we know the game logic for our game.

Variables

 The next thing to do is to understand which variables we'll need for our recode - and this isn't necessarily all of the variables used in the original. Let's break it down into themed areas:

Player
playerX - this is controlled by the player using the joystick
playerXMSB - we'll need this for when the skier is to the right of the screen
playerY - this is largely fixed, but comes into play during the crash sequence. Also, an enhancement could be to let the player move in the Y direction slightly to create the effect of 'speed'.
playerLives - we'll start at 3 and decrease. The 'normal' way!
playerStage - we need to keep track of what stage we're on (similar to H used in the original)
playerDistance - this was the variable P in the game, and we'll track this here so we can display it
playerFrame - this will hold the current animation frame for the player sprite (initially just left, right or crashed)
playerDirection - tells us if we're moving left or right
playerColour - this is mostly red or dark grey for the crash, but we'll use a variable in case we want to do more with this

Game
gameStatus - we'll use this in our gameflow so we know if we're on the title screen, alive, crashed etc.
gameCounter - this was the variable RD in the original and checks our progress within each stage
gameDifficulty - this is set from the title screen and controls the granularity of player movement

Sound
voiceFrequencyHi - this usually changes when we're playing a tune or creating a new sound effect
voiceFrequencyLo - this was static in most cases, but there may be cases when it needs to change
voiceDuration - sets how long the note lasts for
voiceWaveform - used to hold the current waveform
voiceVolume - the current volume

All of these, except volume, may be duplicated for each SID voice if we wish to have more than one sound playing at once (we will!)

Objects (trees, houses etc.)
objectIndex - this is the character index (I) which determines which row of the character block to print
objectOffset1 - this is our random tab position (S) which determines the X position of the object on the screen
objectOffset2 - this is S1 where we have 2 objects alongside each other

Other
inputJoy - we'll want to capture joystick input somewhere so we can process the value properly
collision - we'll also want to capture the sprite-data collision register to see if the player has hit an object

There will more than likely be more variables needed once we get into coding, but this should be enough to get us going.

Other design considerations

We've already talked about relocation of characters, and that will be one of the first things we set up before we start to code, alongside ensuring sprites are in place and located appropriately in memory.

There's quite a lot of sound in the game - tunes and effects - and we'll want to include all of these. However, the most sensible approach to handling sound in assembly is via the interrupt. This isn't particularly complex once you get the basics down, but it makes sense to park sound for now and come back to it once we have our core game working. It should be relatively easy to add sound back in at that stage. So we'll create a silent game initially, which lets us focus on the main game mechanics.

So with that in mind and with the knowledge we've taken from unpicking the BASIC, we can start to put in place the framework that will enable us to start to code and getting the game up and running. The fun bit!

​Next up: setting up the framework

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