Skip to navigation

Landscape: CheckSecretCode (Part 1 of 2)

Name: CheckSecretCode (Part 1 of 2) [Show more] Type: Subroutine Category: Landscape Summary: Generate the secret code for this landscape and optionally check it against the entered code in the keyboard input buffer
Context: See this subroutine in context in the source code References: No direct references to this subroutine in this source file

At this point we have generated the landscape and populated it with enemies, the player and trees, all using the landscape's sequence of seed numbers. This sequence will be different for each individual level, but will be exactly the same sequence every time we generate a specific level. We now keep generating the landscape's sequence of seed numbers to get the landscape's secret code, as follows: * Generate another 38 numbers from the sequence * The next four numbers in the sequence form the secret code To get a secret code of the form 12345678, we take the last four numbers and convert them into binary coded decimal (BCD) by using the GetNextSeedAsBCD routine. These four two-digit pairs then form the secret code, with each of the four numbers producing a pair of digits, building up the secret code from left to right (so in the order that they are written down). If we are displaying the landscape number on-screen at the end of a level, then the last four numbers are generated in the SpawnSecretCode3D routine, but if we are checking the secret code entered by the player, then we generate the last four numbers in this routine (plus one extra number that is ignored). This behaviour is controlled by the doNotPlayLandscape variable.
Arguments: doNotPlayLandscape Controls how we generate the secret code and how we return from the subroutine: * If bit 7 is set, return from part 1 of the routine after generating the secret code sequence up to, but not including, the last four BCD numbers (i.e. the secret code itself), so these can be generated and drawn by the SpawnSecretCode3D routine * If bit 7 is clear, jump to part 2 after generating the whole secret code sequence, plus one more code, checking the generated code against the code entered into the input buffer as we go The first one is used when displaying a landscape number for a completed level, while the second is used to check an entered secret code before playing the landscape
.CheckSecretCode LDX #170 \ Set X = 170 to use as a loop counter for when we \ calculate the secret code, so the following loop \ counts down from 170 to 128 (inclusive, so that's a \ total of 38 + 4 + 1 iterations) \ \ This is the value for when bit 7 of doNotPlayLandscape \ is clear, so that's when we are about to play the game \ and we need to generate the secret code in the \ following loop so we can check it against the code \ entered by the player (which is still in the keyboard \ input buffer from when they typed it in) LDY stashOffset-170,X \ Set Y = stashOffset \ \ The value of stashOffset is set in the SetSecretStash \ routine during the landscape drawing process, where it \ is set to a value that is unique and consistent for \ each individual landscape \ \ We use it as an offset into the secretCodeStash list \ below, so the stash moves around in memory depending \ on the landscape number, making this whole process \ harder to follow (and therefore harder to crack) BIT doNotPlayLandscape \ If bit 7 of doNotPlayLandscape is clear then the next BPL srct1 \ step is to play the landscape, so skip the following \ to leave X set to 170 LDX #165 \ Set X = 165 to use as a loop counter for when we \ calculate the secret code, so the following loop \ counts down from 165 to 128 (inclusive, so that's a \ total of 38 iterations) \ \ This is the value for when bit 7 of doNotPlayLandscape \ is set, so that's when we are not going to play the \ game but are just generating the secret code, in which \ case we stop iterating just before the secret code is \ generated so the SpawnSecretCode3D can finish the job \ We now loop through a number of iterations, counting \ X down towards 128 \ \ On each iteration we do three things: \ \ * We generate the next number from the landscape's \ sequence of seed numbers, converted to BCD \ \ * We test this against the contents of memory from \ either &0D19 or &0D14 down to &0CEF and rotate the \ result into bit 0 of secretCodeChecks (the result \ is only relevant if we are checking the secret \ code against an entered code) \ \ * We add the objectFlags for the Sentinel (which is \ simply a way of incorporating a known value, in \ this case %01111111) and store the results in the \ table at secretCodeStash, from the offset in Y \ onwards, i.e. from offset stashOffset onwards \ \ If we are checking the code against an entered code, \ then the second step is the important part, and this \ is how it works \ \ The inputBuffer is at &0CF0, and it still holds the \ code that the player entered in &0CF0 to &0CF3, with \ the first two digits of the code in &0CF3 and the last \ two digits in &0CF0 \ \ This means that when X = 170, the last five checks in \ each iteration test against the four BCD numbers in \ the entered code, in the correct order, with one extra \ generation and check that is ignored (presumably to \ make this whole process harder to follow) \ \ So if bits 1 to 4 of secretCodeChecks are set by the \ end of the process, the secret code in the keyboard \ input buffer matches the secret code that we just \ generated for this level \ \ The third step is only relevant if we are going on to \ play the game, as this feeds into a second secret code \ check that is performed in the GetRowVisibility \ routine, which is only run during gameplay .srct1 JSR GetNextSeedAsBCD \ Set A to the next number from the landscape's sequence \ of seed numbers, converted to a binary coded decimal \ (BCD) number \ We now compare this generated number with the contents \ of memory, working our way down towards the keyboard \ input buffer (towards the end of the iterations) \ \ X counts down to 128, and for each iteration we check \ the generated number against a location in memory \ \ For the checks to work, we need the last five bytes \ to be the four secret code numbers in inputBuffer, \ plus one more, so: \ \ * When X > 132, we are checking against memory that \ comes after the inputBuffer, and we can safely \ ignore the results \ \ * When X = 132, 131, 130 and 129 we need to be \ checking against the four numbers in inputBuffer \ \ * When X = 128, we are doing the very last check, \ which we can also ignore \ \ The comparison is done by subtracting the contents of \ the memory location we are checking from the BCD \ number we just generated \ \ This is done with a SBC byteToCheck,X instruction \ \ To work out what byteToCheck should be, consider that: \ \ * When X = 129, we check the byte at inputBuffer \ (i.e. the last 2 digits of the code \ when written down or typed in) \ \ * When X = 130, we check the byte at inputBuffer+1 \ \ * When X = 131, we check the byte at inputBuffer+2 \ \ * When X = 132, we check the byte at inputBuffer+3 \ (i.e. the first 2 digits of the code \ when written down or typed in) \ \ To make this work, then, we need this instruction: \ \ SBC inputBuffer-129,X \ \ Note that for BeebAsm to parse this properly, we need \ to wrap the inputBuffer-129 part in brackets, and we \ have to use square brackets so it doesn't look like an \ indirect address instruction SEC \ Subtract the byte from memory that we are checking SBC [inputBuffer-129],X \ from the generated number BEQ srct2 \ If A = 0 then we have a match between the number in \ memory and the generated number, so jump to srct2 to \ keep the C flag set, so we can rotate this into \ secretCodeChecks to indicate a success CLC \ Otherwise we do not have a match, so clear the C flag \ and rotate this into secretCodeChecks to indicate a \ failure .srct2 ROL secretCodeChecks \ Rotate the C flag into bit 0 of secretCodeChecks, so \ secretCodeChecks contains a record of the last eight \ matches between memory and the generated sequence of \ numbers \ \ We only care about the last five comparisons, of which \ we ignore the very last, as the preceding four results \ are for the four BCD numbers in the keyboard input \ buffer (i.e. the entered number) \ We now move on to populate the secret code stash, \ which contains the result of each of the comparisons \ with %01111111 added to them \ \ The stash is checked in the GetRowVisibility routine \ and will abort the game if the values aren't correct, \ so this enables a second secret code check once the \ game has started \ \ The secret stash adds a known value into the mix, by \ fetching the value of objectFlags, which contains the \ object flags for object #0 \ \ Object #0 is always the Sentinel, and the Sentinel \ is always placed on top of the Sentinel's tower, so \ the object flags for the Sentinel are constructed as \ follows: \ \ * Bits 0-5 = the number of the object beneath this \ one \ \ * Bit 6 = set to indicate that this object is on top \ of another object \ \ * Bit 7 = clear to indicate that this object number \ is allocated to an object \ \ The Sentinel's tower is always the first object to be \ spawned, and object numbers are allocated from 63 and \ down, so this means the tower is always object #63, or \ %111111 \ \ The Sentinel's object flags are therefore %01111111 \ \ See the PlaceObjectOnTile routine for details of how \ the Sentinel's object flags are constructed CLC \ Set A = A + %01111111 ADC objectFlags STA secretCodeStash,Y \ Store A in the Y-th entry in the secretCodeStash list \ \ The addition above means that an entry of %01111111 in \ that stash indicates that A was zero before the \ addition, which also indicates a match \ \ If the entered code matches the generated sequence of \ numbers (i.e. it matches the landscape's secret code) \ then the four corresponding entries in secretCodeStash \ will be %01111111 \ \ See the GetRowVisibility routine to see this in action INY \ Increment the index in Y so we build the stash upwards \ in memory DEX \ Decrement the loop counter so the comparisons move \ down in memory, towards inputBuffer BMI srct1 \ Look back to compare the next byte until we have \ compared the bytes all the way down to X = 128 ASL doNotPlayLandscape \ Set the C flag to bit 7 of doNotPlayLandscape and \ clear bit 7 of doNotPlayLandscape, so from this point \ on any calls to GenerateLandscape will preview and \ play the game BCC srct4 \ If bit 7 of doNotPlayLandscape was clear then jump to \ part 2 to check the secret code and either show the \ "WRONG SECRET CODE" error or play the game \ Otherwise bit 7 of doNotPlayLandscape was set, so we \ return from the subroutine normally without playing \ the game .srct3 RTS \ Return from the subroutine \ \ We get to this point by calling the SpawnPlayer \ routine from one of two places: \ \ * PreviewLandscape \ \ * FinishLandscape \ \ and then either failing the secret code checks or \ finishing the current landscape \ \ If we got here from PreviewLandscape, then the next \ instruction jumps to SecretCodeError to display the \ "WRONG SECRET CODE" error, wait for a key press and \ rejoin the main title loop \ \ If we got here from FinishLandscape, then the next \ instructions display the landscape's secret code on \ completion of the level