Indoor Sports - After the Basic LOADER program, a file called SCREEN at &A00 decrypts and runs itself using a fancy EOR method, which is discussed in detail.
1. SCREEN Disassembly
0A00 LDA #15 0A02 LDX #0 0A04 JSR OSbyte \*FX15,0 clear keyboard 0A07 LDX #0 0A09 SEI \disable interrupts
Location &A0B contains the low byte of the data after the LDA instruction (i.e. offset into page &B) and is INCremented at &A49.
0A0A LDA &B00 \Load encrypted byte from page &B. 0A0D CLC
Location &A0F initially contains 33, being the data after the ADC instruction at &A0E, and is DECremented at &A46.
0A0E ADC #33 0A10 EOR &0A00,X
Location &A14 initially contains 78, being the data after the EOR instruction at &A13, and is INCremented at &A43.
0A13 EOR #78 0A15 SEC 0A16 SBC &0A14 0A19 EOR &0A00,X 0A1C EOR #43 0A1E CLC 0A1F ADC #33 0A21 EOR #102 0A23 SEC 0A24 SBC #13 0A26 EOR &0A00,X 0A29 INX 0A2A BNE &0A0D
Location &A2D contains the low byte of the data after the STA instruction, i.e. offset into page &B.
0A2C STA &0B00 \Store decrypted byte in page &B
This section puts the instruction JMP &0A5B at locations &287-9. When the BREAK key is pressed, if location &287 contains a JMP instruction (opcode &4C) the computer jumps to that address, which is &0A5B in this case.
0A2F LDA #1 0A31 STA &0258 0A34 LDA #&4C \JMP 0A36 STA &0287 0A39 LDA #&5B 0A3B STA &0288 0A3E LDA #&0A \&0A5B 0A40 STA &0289
The next section increments the low bytes of the page of memory (page &B) being decrypted.
0A43 INC &0A14 \Increment EOR value 0A46 DEC &0A0F \Decrement ADC value 0A49 INC &0A0B \Increment load address low byte 0A4C INC &0A2D \Increment store address high byte 0A4F BNE &0A0A \Loop till done.
A checksum is performed and if the byte at &0BFF is not 69 then the code branches to &A5B, otherwise execution continues at &B00.
0A51 LDA &0BFF 0A54 CMP #69 0A56 BNE &0A5B 0A58 JMP &0B00
If the checksum fails and/or the Break key is pressed, the computer goes to &A5B which is a memory-clearing lock-up routine. The RAM &0B00 to &7FFF is cleared, then cleared again ad infinitum.
0A5B LDA #0 0A5D STA &70 0A5F LDA #&0B 0A61 STA &71 \Set address to &B00 0A63 LDY #0 0A65 LDA #0 0A67 STA (&70),Y \Store zero in memory 0A69 INY 0A6A BNE &0A65 \until done one page 0A6C INC &71 0A6E LDA &71 \increment page number 0A70 CMP #&80 \until at end of RAM 0A78 BNE &0A65 0A74 BEQ &0A5B \Loop forever
2. Zapping the tape protection
It is not possible to poke an RTS into the code to return to Basic once the decryption is done, that is, we can't do ?&A58=96:CALL&A 00. This is because this would modify a byte whose value is essential to the decryption. The solution is to make a copy of the relevant piece of code and add some instructions to enable it run at its new location, and execute the copy. (Compare with hacking note on Boulderdash.)
10 *L.SCREEN A00 30 REM Make copy of decryption code 40 FOR X%=0 TO &42:X%?&1100=X%?&A00:NEXT 50 P%=&1143:[INC&A14:INC&1114 60 DEC&A0F:DEC&110F 70 INC&A0B:INC&110B 80 INC&A2D:INC&112D 90 BNE&110A:RTS 90 ]CALL&1100
You can now inspect the decrypted page &B in a disassembler, or save it to disc for future reference:
*SAVE SCREENZ A00+200
Note that some further checks are made at &B00:
0B00 LDA &0258 \Check if Escape key has been disabled 0B03 CMP #1 \by examining location &258 0B05 BEQ &0B0A \If it has, continue 0B07 JMP &0A5B \else jump to memory clearing lock-up routine
The next section switches off all foreign ROMs in case the user has installed their own one, perhaps containing a utility to take a snapshot of the memory.
0B0A LDX #0 0B0C LDA #0 0B0E STA &02A1,X 0B11 INX 0B12 CPX #15 0B14 BNE &0B0C
Now we see a commonly-used piece of code to restore the default vectors, in case the user has fiddled with any of them and thus potentially enabling them to gain access to the game code. The address of the default vector table - the table that holds the default ve ctor addresses - is at &FFB7/8 in the operating system ROM, and location &FFB6 contains the number of default vectors.
\Restore default vectors. 0B16 LDY &FFB6 0B19 LDA &FFB7 \Default vector table lo 0B1C STA &70 0B1E LDA &FFB8 \Default vector table hi 0B21 STA &71 0B23 LDA (&70),Y \Load vector addresses 0B25 STA &200,Y \Store in page 2 0B28 DEY 0B29 BNE &8023 \loop until done all of them.
Finally we reach the actual program which goes into Mode 4, turns foreground colour green, and prints some messages. CHR$6 marks the end of the VDU sequence.
0B2B LDX #0 0B2D LDA &0B7F,X \Get VDU code from &B7F 0B30 JSR OSwrch \print it 0B33 INX 0B34 CMP #6 \Was ASCII code 6? 0B36 BNE &0B2D \No so continue with next VDU code. (&B38-7E is discussed below) 0B7F EQUB 22,4 \MODE 4 0B81 EQUB 19,1,2,0,0,0 \Foreground colour green 0B87 EQUB 31,0,2 0B8A EQUS" TYNESOFT SOFTWARE 1988 PRESENTS ..." 0BB0 EQUB 31,0,7 0BB3 EQUS " ...=== INDOOR SPORTS ===..." 0BD4 EQUB 31,0,12 0BD7 EQUS " ELECTRON TAPE VERSION" 0BF5 EQUB 31,0,17 0BF8 EQUB " THE GAME WILL FOLLOW SHORTLY" 0C19 EQUB 28,5,31,18,20 \Text window 0C1E EQUB 6 \End of sequence marker
The file -TyneSoft- is *LOADed to &1100, decrypted and executed by JuMPing to &1208.
0B38 LDX #&45 0B3A LDY #&0B \CLI at &B45 0B3C JSR OScli 0B3F JSR &0B57 \Decrypt and 0B42 JMP &1208 \Execute it \Filename terminated by Carriage Return 0B45 EQUS "L.-TyneSoft- 1100" 0B56 EQUB &0D
Here is the decryption routine, simple this time as it just EORs every byte of the code the two bytes of a 16-bit seed, initially &3 398 and decremented by 2 for each byte decoded.
0B57 LDA #&00 0B59 STA &70 0B5B TAY 0B5C LDA #&11 \Start address=&1100 0B5E STA &71 0B60 LDA (&70),Y 0B62 EOR #&33 \Decryption seed hi 0B64 EOR #&98 \Decryption seed lo 0B66 STA (&70),Y 0B68 DEC &0B65 0B6E DEC &0B65 \ 0B6E BNE &0B73 \ 0B70 DEC &0B63 \Subtract 2 from decryption seed 0B73 INY 0B74 BNE &0B60 0B76 INC &71 0B78 LDA &71 0B7A CMP #&27 \Have we reached &2700? 0B7C BNE &0B60 \no, carry on. 0B7E RTS \Yes, return.
3. Zapping the Other Loader Files
The loader for Bowling is a simple unencrypted file and will not be discussed here. However, the loaders for the other three games - Darts, Tennis, and Airball - employ the same method described in section 1 and the code is identical except (A) it runs at &E00 and (B) JMP &287 instead of JMP &A5B is poked into locations &287-9, i.e. a jump instruction to the address of itself. Here is a disassembly for completeness:
0E00 LDA #15 0E02 LDX #0 0E04 JSR OSbyte \*FX15,0 clear keyboard 0E07 LDX #0 0E09 SEI \disable interrupts 0E0A LDA &F00 \Load encrypted byte from page &B. 0E0D CLC 0E0E ADC #33 0E10 EOR &0E00,X 0E13 EOR #78 0E15 SEC 0E16 SBC &0E14 0E19 EOR &0E00,X 0E1C EOR #43 0E1E CLC 0E1F ADC #33 0E21 EOR #102 0E23 SEC 0E24 SBC #13 0E26 EOR &0E00,X 0E29 INX 0E2A BNE &0E0D 0E2C STA &0F00 0E2F LDA #1 0E31 STA &0258 \Disable Escape 0E34 LDA #&4C \JMP opcode 0E36 STA &0287 0E39 LDA #&87 \to &287 0E3B STA &0288 0E3E LDA #&02 0E40 STA &0289 0E43 INC &0E14 \Increment EOR value 0E46 DEC &0E0F \Decrement ADC value 0E49 INC &0E0B \Increment load address low byte 0E4C INC &0E2D \Increment store address high byte 0E4F BNE &0E0A \Loop till done. 0E51 JMP &0F00
The code at &F00 varies according to the game being loaded. An example for Darts is given below.
0F00 LDA &0258 0F03 CMP #1 0F05 BEQ &0F0A \Check that Escape has been disabled 0F07 JMP &0E54 \If not, jump to loop-forever routine. \Switch out any foreign ROMs (as discussed previously) 0F0A LDX #0 0F0C LDA #0 0F0E STA &02A1,X 0F11 INX 0F12 CPX #15 0F14 BNE &0F0C \Restore default vectors (as discussed previously) 0F16 LDY &FFB6 0F19 LDA &FFB7 0F1C STA &70 0F1E LDA &FFB8 0F21 STA &71 0F23 LDA (&70),Y 0F25 STA &0200,Y 0F28 DEY 0F29 BNE &0F23 \Load in the game files 0F2B LDX #&49 0F2D LDY #&0F \string at &F49 0F2F JSR OScli \*L.Dart1 800 0F32 LDX #&55 0F34 LDY #&0F 0F36 JSR OScli \*L.Dart2 5630 0F39 JSR &0B71 0F3C LDX #&62 0F3E LDY #&0F \*L.Dart3 1100 0F40 JSR OScli 0F43 JSR &0F70 \Decrypt 0F46 JMP &3F1C \and finally execute 0F49 EQUS"L.Dart1 800"+CHR$&0D 0F55 EQUS"L.Dart2 5630"+CHR$&0D 0F62 EQUS"L.Dart3 1100"+CHR$&0D \Routine to decrypt code at &1100-&4F00 0F70 LDA #&00 0F72 STA &70 0F74 TAY 0F75 LDA #&11 \Start address &1100. 0F77 STA &71 0F79 LDA (&70),Y 0F7B EOR #&04 \16-bit decryption seed 0F7D EOR #&75 \with initial value of &475. 0F7F STA (&70),Y 0F81 DEC &0F7E 0F84 DEC &0F7E 0F87 BNE &0F8C 0F89 DEC &0F7C 0F8C INY 0F8D BNE &0F79 0F8F INC &71 0F91 LDA &71 0F93 CMP #&4F \Have we reached &4F00? 0F95 BNE &0F79 \No, continue. 0F97 RTS \Yes, return.
For disc systems where PAGE is higher than &E00, it is advisable to load in DART3 from tape, decrypt and re-save the DART3 file. Assuming the 'zapped' DARTS file is still in memory:
*L.DART3 1100 CALL&F70 *DISC *SAVE DART3 1100 4F00 3F1C
The game can now be run by typing:
*L.Dart1 800 *L.Dart2 5630 */Dart3
Or a disc loader could be written (in machine code) to perform the above tasks automatically.
Mr Spock 7 Feb 2005