Jet Set Willy Tape Loader

Everything about programming, including VDP and Sound programming.
Post Reply
User avatar
thewiz
Posts: 137
Joined: 12 Aug 2012 16:08

Jet Set Willy Tape Loader

Post by thewiz »

Hi,

Been meaning to post this for sometime. Looking at the file date stamps about 3 years :D

Most tape loaders tended to copy the ROM code and change the lead-in from 256 zeros to 50 zeros. Others would send bytes directly to the VDP for the loading screen.

But the most complicated I came across was for JSW. It had two encrypted loaders, played music while loading, and send the load screen directly to VDP a byte at a time.

The initial Basic loader looks like this:

Code: Select all

CTRBADR:
	
0xFA52 00 F2 F8 02 00 00 00 00 00 00 00 0A F9 02 00 00 	................
0xFA62 00 00 00 00 00 22 F9 02 00 00 00 00 00 00 00 3A 	.....".........:
0xFA72 F9 02 00 00 00 00 00 00                         	........        

0xFA7A LSTPG:   00		; Number of 32kb pages -1
0xFA7B VARNAM:  C000		; Bottom of variable names (Normally 0xC000)
0xFA7D VALBOT:  C003		; Bottom of variable values (Normally 0xC001)
0xFA7F CALCBOT: C00D		; Bottom of calculator stack (Normally 0xC001)
0xFA81 CALCST:  C00D		; Top of calculator stack (Normally 0xC001)
0xFA83 KBDBUF:  FB4B		; Addr of keyboard buffer (Normally 0xFB4B)
0xFA85 USYNT:   00 00 00 07     ; USER cmd word syntax bytes
0xFA89 USER:    21 00 81        ; User cmd word jump sv_address
		LD HL, 0x8100
0xFA8C 0xFA8C:  3A 7A FA        ; Unused
		LD A, (FA7A)
0xFA8F IOPL:    18		; List Device
0xFA90 REALBY:  06		; PANEL breakpoint byte
		JR +0x06
0xFA91 KBFLAG:  80		; Key Lock statuses
                                ; (2:Numlock,5:Scroll Lock,7:capslock)
0xFA92 STKLIM:  F8F2		; Top of free space (Normally 0xF8F2)
0xFA94 SYSTOP:  FB4B		; Top of sysvars to save (Normally 0xFB4B)
0xFA96 SSTACK:  0048		; Addr of machine stack (Z80 stack pointer)
		LD C, B
0xFA98 USERINT: A7 28 02        ; Jump sv_addr used by user interrupt routine
                                ; (See INTFFF (0xFD5E))
		AND A
		JR Z, +0x02
0xFA9B NODLOC:  26 41 7E        ; Jump sv_addr used for NODE expansion
		LD HL, 0x41
		LD A, (HL)
0xFA9E FEXPAND: 07 77 2C        ; Jump sv_addr used for PANEL expansion
		RLCA
		LD (HL), A
		INC L
0xFAA1 USERNOD  20 FA E9        ; Jump sv_addr used for Noddy expansion
		JR NZ, +0xFA
		JP (HL)
0xFAA4 NBTOP:   80A8		; Top of Noddy
0xFAA6 IOPL:    00		; Top of Noddy page
0xFAA7 BASTOP:  80A8		; Top of current Basic
0xFAA9 BASTPG:  00		; Top of current Basic page
0xFAAA BASBOT:  4000		; Bottom of Basic

BASTOP:
	
0xFAAC A8 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFABC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................

0xFACC ARRTOP:  022C		; Top of arrays
0xFACE ARRTPG:  00		; Top of arrays page
0xFACF BASELIN: 8000		; 
0xFAD1 BASLNP:  00		; 
0xFAD2 PAGE:    00		; Current page configuration
0xFAD3 CRNTPG:  00		; Current Basic page
0xFAD4 PGN1:    00		; Temp variable used by BASIC
0xFAD5 PGN2:    00		; Temp variable used by BASIC
0xFAD6 PGTOP:   80A8		; 

GOSTACK:
	
0xFAD8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFAE8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFAF8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFB08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFB18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFB28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 	................
0xFB38 00 00 00 00 00 00 00 00 00                      	.........       

0xFB41 GOPTR:   FAD8		; Addr to return to after GOSUB
0xFB43 GOSNUM:  0000		; No of nexted GOSUBs
0xFB45 CTYLST:  00		; Keyboard Configuration
0xFB46 DATAAD:  8000		; Data pointer
0xFB48 DATAPG:  80		; Data pointer page
0xFB49 DESAVE:  801D		; Addr in BASIC when saved

Data After SYSVARS:

Basic Length: 80A8.
Basic:

[4000]: {0053}: 0 POKE 64151,0:SAVE "JET SET WILLY":VS 4:COLOUR 4,1:PAPER 1:INK 1:CLS :INK 4:CSR 4,21:USER "Jet Set Willy is Loading"
[4053]: {0055}: 10 ASSEM 
        {0018}: CODE

0x405A  11 00 01             LD DE,256                                          
0x405D  ED 53 67 FD          LD (#FD67),DE                                      
0x4061  21 00 F0             LD HL,#F000                                        
0x4064  E5                   PUSH HL                                            
0x4065  CD AE 0A             CALL #AAE                                          
0x4068  E1                   POP HL                                             
0x4069  7E          LOOP:    LD A,(HL)                                          
0x406A  ED 67                RRD                                                
0x406C  77                   LD (HL),A                                          
0x406D  2C                   INC L                                              
0x406E  20 F9                JR NZ,LOOP                                         
0x4070  E9                   LD PC,HL                                           
0x4071  C9                   RET                                                

Symbols:
LOOP          4069  

Symbol Table dump:
	
0x4072 00 32 35 B6 00 01 01 00 00 00 23 46 44 36 B7 00 	.25.......#FD6..
0x4082 01 05 00 00 00 23 46 30 30 B0 00 01 08 00 00 00 	.....#F00.......
0x4092 23 41 41 C5 00 01 0C 00 00 02 0F 00 4C 4F 4F D0 	#AA.........LOO.
0x40A2 00 00 01 15 00 FF                               	......          

Number  (00): String:      256
              1 byte ptrs: 0x00 - 
              2 byte ptrs: 0x01 - 0x0001 (0x405b) 
              3 byte ptrs: 0x00 - 
Number  (00): String:      #FD67
              1 byte ptrs: 0x00 - 
              2 byte ptrs: 0x01 - 0x0005 (0x405f) 
              3 byte ptrs: 0x00 - 
Number  (00): String:      #F000
              1 byte ptrs: 0x00 - 
              2 byte ptrs: 0x01 - 0x0008 (0x4062) 
              3 byte ptrs: 0x00 - 
Number  (00): String:      #AAE
              1 byte ptrs: 0x00 - 
              2 byte ptrs: 0x01 - 0x000c (0x4066) 
              3 byte ptrs: 0x00 - 
Label   (02): Offset:      0x000f (0x4069)
              String:      LOOP
              1 byte ptrs: 0x00 - 
              2 byte ptrs: 0x00 - 
              3 byte ptrs: 0x01 - 0x0015 (0x406f) 

Padding:
0x40A8 c0 00 4d 45-4d 4f 54 45-43 48 20 20-20 20 20 20  ..MEMOTE CH......
0x40B8 20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20  ........ ........
0x40C8 20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20  ........ ........
0x40D8 20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20  ........ ........
0x40E8 20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20  ........ ........
0x40F8 20 20 20 20-20 20 20 20

RLCA M/c:
0x4100 88 00 78 80-00 80 f6 58-e1 85 78 92-1f e4 19 d1  ..x....X ..x.....
0x4110 7d e6 ce 7d-eb 4c 25 b2-3a 10 a9 b2-3a 10 ab b4  }..}.L%. :...:...
0x4120 36 36 bc 10-b4 b9 10 26-b7 b0 32 b4-37 b3 90 00  66.....& ..2.7...
0x4130 fa f2 88 00-80 f6 a9 b3-fe e6 57 05-f0 1f bd f6  ........ ..W.....
0x4140 a7 f6 af 57-bb 16 10 fc-f4 00 00 00-00 00 00 00  ...W.... ........
0x4150 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00  ........ ........
0x4160 00 00 00 00-00 00 00 00-00 00 60 00-a6 a2 a6 a7  ........ ..`.....
0x4170 2a a2 a1 24-10 10 10 10-10 10 10 10-10 10 10 10  *..$.... ........
0x4180 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........
0x4190 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........
0x41A0 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........
0x41B0 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........
0x41C0 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........
0x41D0 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........
0x41E0 10 10 10 10-10 10 10 10-10 10 10 10-10 10 10 10  ........ ........

Noddy:

Basic Variables:
Remaining File Dump:
What they did was actually put the code to decrype the M/c loader in the SYS VARs area starting the the USER. This would have the effect of being completely overwritten on a reset. Also the ASSEM code is a complete red herring.

The M/c was stored in the Arrays area after basic, padded so that it existed at 0x4100 - 0x41FF (or 0x8100 to 0x81FF). It was a simple encryption, just RLCA'd each byte.
Last edited by thewiz on 30 Oct 2014 16:02, edited 1 time in total.
THIS is what Memotech is doing now.
User avatar
thewiz
Posts: 137
Joined: 12 Aug 2012 16:08

Re: Jet Set Willy Tape Loader

Post by thewiz »

To decrypt the M/c loader I wrote the following C program:

Code: Select all

#include <stdio.h>

main ()

{

FILE *inpfil;
char inpbyt;

inpfil = fopen ("jetset.ldr", "r");

while (!feof (inpfil)) {
	inpbyt = fgetc (inpfil);
	if (!feof (inpfil)) {
		/* printf ("%02x ", inpbyt & 0xff);
		printf ("%02x ", (((inpbyt << 1) & 0xff) | ((inpbyt & 0x80) >> 7))); */
		putchar (((inpbyt << 1) & 0xff) | ((inpbyt & 0x80) >> 7));
	}
}

fclose (inpfil);

}
The M/c can now be disassembled:

Code: Select all

;
; JSW Loader 1.
;
0x4100 11 00 F0             LD DE,F000					; HL set to 0x4100 or 0x8100
0x4103 01 00 01             LD BC,0x0100
0x4106 ED B0                LDIR					; Loader Moved to 0xF000 - 0xF0FF
0x4108 C3 0B F0             JP JP000					; Jump to moved location
;
; Now running at 0xF00B
;
0x410B 25                   DEC H
0x410C 3E C9                LD A,C9					; Change Basic loader to return
0x410E 32 A3 FA             LD (FAA3),A					; back to here
0x4111 CD 9D FA             CALL CL000					; Call Basic loader to Encrypt Loader 1 again
0x4114 D7                   RST 10					; Output loading message
0x4115 98 4A 65 74 20       CMD output str, 'J', 'e', 't', ' ', 
0x411B 53 65 74 20 57       CMD output str, 'S', 'e', 't', ' ', 'W', 
0x4120 69 6C 6C 79 20       CMD output str, 'i', 'l', 'l', 'y', ' ', 
0x4125 69 73 20 4C 6F       CMD output str, 'i', 's', ' ', 'L', 'o', 
0x412A 61 64 69 6E 67       CMD output str, 'a', 'd', 'i', 'n', 'g', 
;
; Read Loader 2 from tape at 0xF500, length 0x100.
;
0x412E 21 00 F5             LD HL,F500					; Load next loader at 0xF500
0x4131 E5                   PUSH HL
0x4132 11 00 01             LD DE,0100
0x4135 ED 53 67 FD          LD (VERIF),DE
0x4139 CD AE 0A             CALL CL001
;
; Decrypt Loader 2 by XORing with R register
;
0x413C E1                   POP HL
0x413D 3E 7B                LD A,7B
0x413F ED 4F                LD R,A		; 7b
0x4141 ED 5F          LP000 LD A,R		; 7c	01	06
0x4143 AE                   XOR (HL)		; 7d	02	07
0x4144 77                   LD (HL),A		; 7e	03	08
0x4145 2C                   INC L		; 7f	04	09
0x4146 20 F9                JR NZ,LP000		; 00	05	0a
0x4148 E9                   JP (HL)
;
0x4149 00 00 00 00          DS 33
0x416A C0                   RET NZ
0x416B 00                   NOP
;
0x416C 4D 45 4D 4F          DB "MEMOTECH"
0x4170 54 45 43 48
0x4174 20 20 20 20          DB "                    "
0x4184 20 20 20 20          DB "                    "
0x4194 20 20 20 20          DB "                    "
0x41A4 20 20 20 20          DB "                    "
0x41B4 20 20 20 20          DB "                    "
0x41C4 20 20 20 20          DB "                    "
0x41D4 20 20 20 20          DB "                    "
0x41E4 20 20 20 20          DB "                "
It copies itself to 0xF000 and then encrypts the original M/c loader. Next it loads in 256 bytes using the ROM code to 0xF500. Now this is encrypted by XORing with the R register, i.e. it changes each time.
THIS is what Memotech is doing now.
User avatar
thewiz
Posts: 137
Joined: 12 Aug 2012 16:08

Re: Jet Set Willy Tape Loader

Post by thewiz »

The C code used to decrypt this is:

Code: Select all

#include <stdio.h>

main ()

{

FILE	*inpfil;
char	inpbyt;
int	xorbyt;

inpfil = fopen ("jetset_ldr2.xor", "r");

xorbyt = 0x7b;

xorbyt += 2;		/* ld a,r */

while (!feof (inpfil)) {
	inpbyt = fgetc (inpfil);
	if (!feof (inpfil)) {
		/* printf ("%02x ", inpbyt & 0xff);
		printf ("%02x ", (((inpbyt << 1) & 0xff) | ((inpbyt & 0x80) >> 7))); */
		putchar (inpbyt ^ xorbyt);
		xorbyt += 6;
		xorbyt &= 0x7f;
	}
}

fclose (inpfil);

}
The disassembled code looks like this:

Code: Select all

; JSW Loader 2
;
0xF500 21 4C F5             LD HL,F54C
0xF503 E5                   PUSH HL
0xF504 11 00 38             LD DE,0x3800
0xF507 CD 10 0B             CALL CL000		; ROM call
0xF50A AF                   XOR A
0xF50B D3 02                OUT (VDP01 ),A
0xF50D 3E 40                LD A,40
0xF50F D3 02                OUT (VDP01 ),A
;
0xF511 06 00          LP000 LD B,00		; Leadin 0 x 256
0xF513 CD 6E 0A       LP001 CALL CL001		; ROM call
0xF516 38 F9                JR C,LP000
0xF518 10 F9                DJNZ LP001
0xF51A FB             LP002 EI			; Leadin 1
0xF51B AF                   XOR A
0xF51C 3F                   CCF
0xF51D CD 72 0A             CALL CL002		; ROM call
0xF520 30 F8                JR NC,LP002
;
; Next bit is run twice. First time with DE = 0x3800, HL not used. Second time with DE = 0x7400, HL = 0x8007.
;
0xF522 CD 6A F5       LP005 CALL MUSIC		; Play Music
0xF525 CD 60 F5             CALL RDBYT		; Read in a byte to Reg C
0xF528 3A A6 F5             LD A,(F5A6)
0xF52B A7                   AND A		; Check if loop counter 0xF5A6 is 0
0xF52C 79                   LD A,C
0xF52D 20 04                JR NZ,LP003
0xF52F D3 01                OUT (VDP00 ),A	; Write 0x3800 bytes to VRAM.
0xF531 18 02                JR LP004
0xF533 AB             LP003 XOR E		; Write 0x7400 bytes to 0x8007 xor'd with Reg E
0xF534 77                   LD (HL),A
0xF535 1B             LP004 DEC DE
0xF536 23                   INC HL
0xF537 7A                   LD A,D
0xF538 B3                   OR E
0xF539 20 E7                JR NZ,LP005
;
0xF53B 21 A6 F5             LD HL,F5A6		; Check loop counter 0xF5A6 is 1.
0xF53E 35                   DEC (HL)
0xF53F CA 06 0B             JP Z,JP000		; Stack has 0xF54C pushed there at 0xF503
;
0xF542 36 01                LD (HL),01
0xF544 21 07 80             LD HL,0x8007
0xF547 11 00 74             LD DE,0x7400
0xF54A 18 D6                JR LP005
;
0xF54C D7                   RST 10
0xF54D 6C                   CMD virtual scrn 12
0xF54E 8E 1B 42 30 0F       CMD output str, 0x1B, 'B', '0', 0x0F, 
0xF554 00 7E 38 44 9A       CMD output str, 0x00, '~', '8', 'D', 0x9A, 
0xF559 A2 A2 9A 44 38       CMD output str, 0xA2, 0xA2, 0x9A, 'D', '8', 
0xF55D C3 07 80             JP JP001
;
; Read in a byte
;
0xF560 06 08          RDBYT LD B,08
0xF562 CD 6E 0A       LP007 CALL CL001		; ROM call
0xF565 CB 19                RR C
0xF567 10 F9                DJNZ LP007
0xF569 C9                   RET
;
; Play Music
;
0xF56A 7B             MUSIC LD A,E
0xF56B E6 0F                AND 0F
0xF56D C0                   RET NZ		; Run this code every 16 counts of DE
0xF56E E5                   PUSH HL
0xF56F D5                   PUSH DE
0xF570 2A A7 F5             LD HL,(F5A7)	; Pointer to Music Data
0xF573 7B                   LD A,E
0xF574 E6 10                AND 10		; 
0xF576 20 04                JR NZ,LP008		; Only increment HL every other 16 counts of DE
0xF578 23                   INC HL
0xF579 22 A7 F5             LD (F5A7),HL
0xF57C 6E             LP008 LD L,(HL)
0xF57D 2C                   INC L
0xF57E 20 0A                JR NZ,WRTSND	; Check for end of data
0xF580 21 A9 F5             LD HL,F5A9		; Reset pointer
0xF583 23                   INC HL
0xF584 22 A7 F5             LD (F5A7),HL
0xF587 D1                   POP DE
0xF588 E1                   POP HL
0xF589 C9                   RET
;
0xF58A 26 00         WRTSND LD H,00
0xF58C 0F                   RRCA		; Reg A = Reg E & 0x0F
0xF58D 0F                   RRCA
0xF58E 0F                   RRCA
0xF58F 0F                   RRCA
0xF590 4F                   LD C,A
0xF591 85                   ADD L		; Reg A = E >> 4 & (HL)
0xF592 6F                   LD L,A
0xF593 2D                   DEC L
0xF594 3E 0A                LD A,0A
0xF596 22 16 FE             LD (FREQ),HL
0xF599 21 14 FE             LD HL,CHAN
0xF59C 71                   LD (HL),C
0xF59D 32 18 FE             LD (VOL),A
0xF5A0 CD F6 08             CALL CL006		; ROM call
0xF5A3 D1                   POP DE
0xF5A4 E1                   POP HL
0xF5A5 C9                   RET
;
; Variables
;
0xF5A6 00                   DB 0x00		; Loop counter
0xF5A7 A9                   DW 0xF5A9		; Music Pointer
;
; Music Data
;
0xF5A9 00                   NOP
0xF5AA 7B                   LD A,E
0xF5AB 89                   ADC C
0xF5AC 7B                   LD A,E
0xF5AD 89                   ADC C
0xF5AE 92                   SUB D
0xF5AF 92                   SUB D
0xF5B0 B7                   OR A
0xF5B1 B7                   OR A
0xF5B2 B7                   OR A
0xF5B3 B7                   OR A
0xF5B4 92                   SUB D
0xF5B5 89                   ADC C
0xF5B6 7B                   LD A,E
0xF5B7 89                   ADC C
0xF5B8 7B                   LD A,E
0xF5B9 89                   ADC C
0xF5BA 92                   SUB D
0xF5BB 89                   ADC C
0xF5BC 7B                   LD A,E
0xF5BD 6D                   LD L,L
0xF5BE 67                   LD H,A
0xF5BF 6D                   LD L,L
0xF5C0 67                   LD H,A
0xF5C1 6D                   LD L,L
0xF5C2 7B                   LD A,E
0xF5C3 7B                   LD A,E
0xF5C4 7B                   LD A,E
0xF5C5 7B                   LD A,E
0xF5C6 7B                   LD A,E
0xF5C7 7B                   LD A,E
0xF5C8 7B                   LD A,E
0xF5C9 7B                   LD A,E
0xF5CA 5C                   LD E,H
0xF5CB 5C                   LD E,H
0xF5CC 5C                   LD E,H
0xF5CD 5C                   LD E,H
0xF5CE 61                   LD H,C
0xF5CF 61                   LD H,C
0xF5D0 6D                   LD L,L
0xF5D1 6D                   LD L,L
0xF5D2 7B                   LD A,E
0xF5D3 89                   ADC C
0xF5D4 92                   SUB D
0xF5D5 89                   ADC C
0xF5D6 7B                   LD A,E
0xF5D7 7B                   LD A,E
0xF5D8 92                   SUB D
0xF5D9 92                   SUB D
0xF5DA 74                   LD (HL),H
0xF5DB 7B                   LD A,E
0xF5DC 89                   ADC C
0xF5DD 7B                   LD A,E
0xF5DE 74                   LD (HL),H
0xF5DF 74                   LD (HL),H
0xF5E0 89                   ADC C
0xF5E1 89                   ADC C
0xF5E2 5C                   LD E,H
0xF5E3 5C                   LD E,H
0xF5E4 5C                   LD E,H
0xF5E5 5C                   LD E,H
0xF5E6 5C                   LD E,H
0xF5E7 5C                   LD E,H
0xF5E8 5C                   LD E,H
0xF5E9 5C                   LD E,H
0xF5EA FF                   RST 38
The routine loads a screen straight to VDP followed by loading the game proper. While it does this it plays music (badly) which was another first.

Enjoy
THIS is what Memotech is doing now.
Martin A
Posts: 799
Joined: 09 Nov 2013 21:03

Re: Jet Set Willy Tape Loader

Post by Martin A »

That's some lengths to go to protect a game that probably only sold a few hundred copies....

I did wonder if maybe the Einstein version of Elite could be hacked to work on the MTX, but it appears that also uses the R register for encription.
User avatar
thewiz
Posts: 137
Joined: 12 Aug 2012 16:08

Re: Jet Set Willy Tape Loader

Post by thewiz »

I did wonder if converting some of the Einstein games back to the MTX would be fairly straight forward. Sound and Video would only need minimal changes, Keyboard, joystick and interrupts would be the tricky bits.

Thing is when decrypting these files, because the previous loader is known, getting the next loader disassembled is fairly easy to do. At least it was with JSW.

I wondered if the Sord M5 was a candidate for convertion too.
THIS is what Memotech is doing now.
Martin A
Posts: 799
Joined: 09 Nov 2013 21:03

Re: Jet Set Willy Tape Loader

Post by Martin A »

ANYTHING form anywhere!

We're MTX owners, I thing every other Z80 system apart from maybe the Aquarius had more software!
Post Reply