8-Bit Software Online Conversion

                      By C.J.Richardson. An article in the September issue of 8-Bit by M.T.Farnworth 15A, prompted me to start considering how I actually find cheats for games myself. The tools that I find indispensable for this are: DISCDOCTOR, my printer and a list of the opcodes. Before anything else, may I suggest that you refresh yourself by reading M.T.Farnworth's article again, it is imperative that you use a backup and rename the original file something like: CODEOR so that it is obvious which is the original unbutchered version. I will mention a couple of machine code instructions in the following text. JSR - Jump Sub Routine similar to gosub in BASIC. RTS Return from Sub Routine. NOP No OPeration. Handy blank out. RTS and NOP take up only one memory location. JSR takes up 3.        There are a couple of methods mentioned here which can be applied without reading the code of a game at all. However, a disassembler is a real must. If you haven't got one, there is a simple one of mine included on this issue disc. Firstly, play the game and decide if it is worth attempting to find a cheat for it. Then decide on the things that you would like to have: Infinite lives? Immortality? Walk through walls? Disable the nasties? Infinite weapons or shields? See the passwords? Read the text? The list can grow endlessly depending on the type of game. For instance, in an adventure game you might want to see all the game locations. This can be done in a number of ways. Sometimes the number of the present location is stored in zero page, it is a matter of changeing this number and calling the game repeatedly. To carry this out you have to find out where the main loop of the game is and slip an RTS (&60) into the loop. Change the memory location, then call the game loop again. Sometimes you can spot the main loop as a large number of JSR instructions followed by a JMP instruction pointing back to the start of all the JSRs. This is a dead giveaway. You can clap 3 NOPs over the JSR instruction, or an RTS at the address the JSR instruction points to. At this particular point in the game either method will have the same end effect, i.e. the routine will be avoided. So if it is a collision detect routine, then a collision will not be detected! However, if you put the RTS at the address pointed to rather than just removing the instruction to jump there, then the routine will be totally disabled, if it is called from other parts of the program, the same thing will happen, a collision will not be detected. If you think about this, the RTS method on occasions will not have the desired effect, because if the same routine is used to detect if a nasty has been hit, then it will not die either. So you must have an idea of what you want to do before deciding on the method to use. An idea is to write a short loop such as this with the game in memory, a direct command so that you don't overwrite anything that may be there: FOR L%=&1000TO&5000:IF?L%=&20 PRINT"At &";~L%;" JSR&";~?(L%+2);~?(L%+1):NEXT ELSE NEXT This line will print out all the occurrences of JSR instructions in an area of memory from &1000 to &5000. It may also print out some rogue ones, as the number &20 could occur for other reasons. The line gives the memory location of the instruction along with the address of the subroutine. So you can put 3 NOP instructions starting at the first number, or an RTS at the address pointed to. A line like that can produce quite a lot of output. This is where the printer comes in. Type the above line then, before pressing return press CTRL +B to switch the printer on. I use a till roll! Remember to type CTRL+C to switch the printer off when you have finished. If you haven't a clue where to start, try just working your way from one end of memory to the other in stages of say &300 bytes changeing all occurences of JSR to NOPs. This can have quite alarming effects sometimes! Using that method you can put a cheat in without reading the program. Using trial and error, you can gradually find the one routine you need to cut out. Something like: FORL%=&1000TO&1100:IF?L%=&20 ?L%=&EA: ?(L%+1)=&EA:?(L%+2)=&EA:NEXT ELSE NEXT Then call the start of the program. Change the value of the FOR NEXT loop when the program crashes to: FORL%=&1100 TO &1200 etc and try again. Instead of putting the RTS instruction at the address pointed to by the JSR instruction, you could put it a bit further on, so that a part of the routine is used. At the start of the game, there will generally be an area which repeatedly saves values to zero page locations, this could be the initialisation of scores, shields, weapons, lives, etc. Try changeing the value saved. The program may look something like this: LDA#0 STA&76 STA&77 STA&78 LDA#3 STA&79 Something like this looks really promising.The LDA#3 instruction could be changed to LDA#&FF, so instead of starting with 3 lives, you may start with 255. Sometimes that may cause display problems though, as the program tries to print 255 elves at the top of the screen instead of the 3 you should have had. Sometimes it is best not to be too greedy. Try altering the LDA#0 as well, and see what happens, one of those locations may refer to the start position for instance. If you change the 0 to 4, you may find yourself on level 5 of the game. At this point you may now have discovered where the number of lives are stored, let us assume that location &79 holds the number of lives. You have proved this by altering the number 3 to 255 and getting loads of lives. So now you can find out where the memory location is altered. For instance if somewhere in memory is the instruction DEC&79 (&C6 &79) this is where your lives are depleted. You could find it like this: FORL%=&1100TO&3000:IF?L%=&C6:IF?(L%+1)= &79 PRINT"Change ?&";~L%;" and ?&";~L%+ 1;" to &EA":NEXT ELSE NEXT It's then a simple matter of replacing these 2 bytes with NOPs. Then lo and behold, you will still get killed, but you have infinite lives. Taking this a stage further, you may now have found the area that detects a kill, so read backwards until you find a JMP or RTS instruction (it could be others), and put an RTS just after that. That brings me to another heavy handed method. Use the previously described method of finding JSRs to find RTSs instead: FOR L%=&1900TO&5000:IF?L%=&60 P."RTS at &";~L%:N. ELSE N. Then try putting an RTS just after that. The principle behind this (albeit based on shaky ground) is that sometimes an RTS represents the end of a routine, therefore it follows that where one finishes another must start. This will therefore sometimes disable an important routine such as collision detection. An extension of this idea is another really easy method to attempt without even reading the coding: *LOAD in the game Type as a direct command: FORL%=&1A00TO&1900STEP-1:IF?L%=&60?(L%+ 1)=&60 :NEXT ELSE NEXT Then CALL the start address. When that crashes, repeat the operation but increase the FOR NEXT loop values by another &100. Or shorten the loop to home in on the instruction that is doing the trick. Here are some assembler opcodes to look for. "n" and "nn" represent the number of bytes that follow the instruction. ...........HEX DEC. OPCODE............. ...........&10 16.. BPL.&n............. ...........&20 32.. JSR.&nn............ ...........&30 48.. BMI.&n............. ...........&4C 76.. JMP.&nn............ ...........&50 80.. BVC &n............. ...........&60 96.. RTS................ ...........&61 97.. ADC (&n),X......... ...........&65 101. ADC.&n............. ...........&69 105. ADC.#&n............ ...........&6C 108. JMP(&nn)........... ...........&6D 109. ADC.&nn............ ...........&71 113. ADC.(&n),Y......... ...........&74 116. STZ.&n,X........... ...........&76 118. ADC.&n,X........... ...........&79 121. ADC.&nn,Y.......... ...........&81 129. STA.(&n,X)......... ...........&84 132. STY.&n............. ...........&85 133. STA.&n............. ...........&86 134. STX.&n............. ...........&88 136. DEY................ ...........&8C 140. STY.&nn............ ...........&8D 141. STA.&nn............ ...........&8E 142. STX.&nn............ ...........&90 144. BCC.&n............. ...........&9D 157. STA.&nn,X.......... ...........&A0 170. LDY.#&n............ ...........&A2 162. LDX.#&n............ ...........&A4 164. LDY.&n............. ...........&A5 165. LDA.&n............. ...........&A6 166. LDX.&n............. ...........&AC 172. LDY.&nn............ ...........&AD 173. LDA.&nn............ ...........&AE 174. LDX.&nn............ ...........&B0 176. BCS.&n............. ...........&C5 197. CMP.&n............. ...........&C6 198. DEC.&n............. ...........&C8 200. INY................ ...........&CA 202. DEX................ ...........&D6 214. DEC.&n,X........... ...........&DE 222. DEC.&nn,X.......... ...........&E4 228. CPX.&n............. ...........&E5 229. SBC.&n............. ...........&E6 230. INC.&n............. ...........&E9 233. SBC.#&n............ If you want a full list of the opcodes, see the program "DISM".          Dism is a basic program which reads machine code in memory or on disc and converts it to something a little more readable. The machine code MNEMONICS. First, get the machine code you want to read into memory or decide on the file you want to read from disc. If you are reading from memory, *LOAD the machine code into memory. Then load DISM, be careful not to load DISM in at a place that will overwrite the part you want to read. Change page to above or below the code. If you can't do this, use the read from disc option mentioned below. Run the program. It will ask these questions: List opcodes? Answering "Y" to this will send a list of the mnemonics to the printer. Read from Disc or memory? Printout. Answer "Y" and all output goes to the printer as well as screen. Jump Single or Relative? If you answer "S" to this, memory read will be in jumps of one location at a time. Use this option to find your way around a program. The option you will normally choose is "R" for relative jumps. If an instruction takes up 3 bytes, the next instruction disassembled will be taken from 3 bytes further on. Mode 7 or 0. Choose the mode. If you are reading from disc the program will now ask for the name of the file you are going to read. Read from where? The program is now asking for the position in memory or on disc that you want to start reading from. You can enter the address in decimal, or precede the number with "&" for hexadecimal. If when using the disc option you enter a number less than 1 or larger than the file, the program will stop. Instructions are read in blocks of ten, press any key but shift to read more. Press "J" to Jump to somewhere else in memory. Enter the memory location in either decimal, or precede the number with "&" to enter it in hexadecimal. The program will convert itself to 6502 by following the instructions on the title page. Type SA.FNS to save the converted file. All that the conversion does is remove the extra instructions available on the 65C02. You do not need to convert the program unless using the data to write a program. Here is an idea to get you going: Chain Dism. Press N M N R 0. Then type &900 and press RETURN. You should now be reading one of the mag routines.                    