-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathelite-checksum.asm
325 lines (262 loc) · 13.4 KB
/
elite-checksum.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
; ******************************************************************************
;
; COMMODORE 64 ELITE ENCRYPTION SOURCE
;
; Commodore 64 Elite was written by Ian Bell and David Braben and is copyright
; D. Braben and I. Bell 1985
;
; The code in this file is identical to the source disks released on Ian Bell's
; personal website at http://www.elitehomepage.org/ (it's just been reformatted
; to be more readable)
;
; The commentary is copyright Mark Moxon, and any misunderstandings or mistakes
; in the documentation are entirely my fault
;
; The terminology and notations used in this commentary are explained at
; https://elite.bbcelite.com/terminology
;
; The deep dive articles referred to in this commentary can be found at
; https://elite.bbcelite.com/deep_dives
;
; ------------------------------------------------------------------------------
;
; The following routines from the S.BCODES and S.COMLODS BBC BASIC source files
; are implemented in the elite-checksum.py script. This file is purely for
; reference and is not used in the build process.
;
; ******************************************************************************
; ******************************************************************************
;
; Name: S.BCODES encryption
; Type: Subroutine
; Category: Copy protection
; Summary: Encrypts LOCODE and HICODE
;
; ******************************************************************************
ZP = $70 ; ZP(1 0) stores the source address for the encryption
ZP2 = $72 ; ZP2(1 0) stores the end address
KEY1 = $36 ; The seed for encrypting LOCODE from G% to R%, where
; LOCODE = ELTA-C
KEY2 = $49 ; The seed for encrypting HICODE from R% to F%, where
; HICODE = ELTD-K
; These four variables come from the build process:
;
; G% is the address in the main game code of the ELTA
; file, just after the end of the decryption routine
;
; B% is the address in the main game code of the start
; of the LOCODE file
;
; R% is the address in the main game code of the first
; byte of ELTD
;
; F% is the address in the main game code of the last
; byte of ELTK (i.e. after the C.COMUDAT music data but
; before the C.THEME music data)
;
; These three variables are calculated by S.BCODES when
; loading the binary files into memory in order to
; encrypt them
;
; L% is the address in local memory in S.BCODES where we
; load the ELTA-K files
;
; U% is the address in local memory in S.BCODES of the
; first byte of ELTD
;
; V% is the address in local memory in S.BCODES of the
; last byte of ELTK
;
; We can therefore convert an in-game address to a local
; S.BCODES address by adding L%-B%
;
; This code encrypts as follows:
;
; G% to R% with KEY1 (i.e. L%+G%-B% to U% in local
; memory)
;
; R% to F% with KEY2 (i.e. U% to V% in local memory)
LDA U% ; Store the contents of the byte at U% on the stack,
PHA ; so we can restore it below
LDA #KEY1 ; Set the byte at U% to KEY1, so we can work up from
STA U% ; L% to U% to encrypt LOCODE, ending with the correct
; seed (which we can then use to "unzip" the encrypted
; data in the opposite direction, from U% to L%)
LDA #0 ; Set ZP = 0, so (ZP+1 Y) will equal ZP(1 0) + Y
STA ZP
LDY #LO(L%+G%-B%) ; Set (ZP+1 Y) to the address in local memory of the
LDA #HI(L%+G%-B%) ; start of the code that we want to encrypt (i.e. G% in
STA ZP+1 ; the main game code, or the first byte of ELTA)
LDA #LO(U%) ; Set ZP2(1 0) to U%, the address in local memory of the
STA ZP2 ; end of the code that we want to encrypt (i.e. R% in
LDA #HI(U%) ; the main game code, or the first byte of ELTD)
STA ZP2+1
JSR WUMP ; Encrypt from (ZP+1 Y) up to ZP2(1 0), i.e. from G% up
; to R% in the main game code
PLA ; Restore the byte at U%, which we corrupted above
STA U%
LDA #KEY2 ; Set the byte at V% to KEY2, so we can work up from
STA V% ; U% to V% to encrypt HICODE, ending with the correct
; seed (which we can then use to "unzip" the encrypted
; data in the opposite direction, from V% to U%)
LDY #LO(U%) ; Set (ZP+1 Y) to U%, the address in local memory of the
LDA #HI(U%) ; start of the code that we want to encrypt (i.e. R% in
STA ZP+1 ; the main game code, or the first byte of ELTD)
LDA #LO(V%) ; Set ZP2(1 0) to V%, the address in local memory of the
STA ZP2 ; end of the code that we want to encrypt (i.e. F% in
LDA #HI(V%) ; the main game code, or the byte after C.COMUDAT)
STA ZP2+1
JSR WUMP ; Encrypt from (ZP+1 Y) up to ZP2(1 0), i.e. from R% up
; to F% in the main game code
LDA #0 ; Zero the byte at V% to hide the seed
STA V%
RTS ; Return from the subroutine
.WUMP
; This routine encrypts from (ZP+1 Y) up to ZP2(1 0)
;
; This is the same as ZP2(1 0) down to ZP(1 0) + Y
LDA (ZP),Y ; If Y = 255, jump to WUMP2 to add the bytes across the
CLC ; page boundary and jump back to WUMP3
INY ;
BEQ WUMP2 ; Otherwise add byte Y+1 of ZP(1 0) to byte Y of ZP(1 0)
ADC (ZP),Y
DEY
STA (ZP),Y
.WUMP3
INY ; Increment Y to point to the next byte to encrypt
CPY ZP2 ; If we haven't reached ZP2(1 0), loop back to WUMP to
BNE WUMP ; encrypt the next byte
LDA ZP+1
CMP ZP2+1
BNE WUMP
RTS ; Otherwise we have reached ZP2(1 0), so return from the
; subroutine
.WUMP2
; If we get here then Y has just been incremented from
; 255 to 0
INC ZP+1 ; Increment ZP(1 0) to point to the next page
ADC (ZP),Y ; Add byte 0 of the new page to byte 255 of the old page
DEC ZP+1 ; (C is still clear from above)
DEY
STA (ZP),Y
INC ZP+1
BNE WUMP3 ; Jump to WUMP3 to continue encrypting the new page
; ******************************************************************************
;
; Name: S.COMLODS encryption
; Type: Subroutine
; Category: Copy protection
; Summary: Encrypts COMLOD
;
; ******************************************************************************
KEY3 = $8E ; The seed for encrypting COMLOD from U% to V%, which is
; the second block of data, after the decryption routine
KEY4 = $6C ; The seed for encrypting COMLOD from W% to X%, which is
; the first block of data, before the decryption routine
; These four variables come from the build process:
;
; U% is the address in the COMLOD code of the start of
; the second data block, just after the decryption
; routine
;
; V% is the address of the end of the COMLOD code
;
; W% is the of the start of the COMLOD code
;
; X% is the address in the COMLOD code of the end of the
; first data block, just after the decryption routine
;
; These two variables are calculated by S.COMLODS when
; loading the binary files into memory in order to
; encrypt them
;
; O2% is the address of the COMLOD file when loaded into
; local memory by S.COMLODS
;
; P2% is the address of the COMLOD file when loaded by
; the game
;
; We can therefore convert an in-game address to a local
; S.COMLODS address by adding O2%-P2%
;
; This code encrypts as follows:
;
; U% to V% with KEY3 (i.e. U%+O2%-P2% to V%+O2%-P2%
; in local memory)
;
; W% to X% with KEY4 (i.e. W%+O2%-P2% to X%+O2%-P2%
; in local memory)
.ZP
LDA #KEY3 ; Set A = KEY3 to use below, and label this instruction
; and the next one to use as temporary storage, which
; has the side effect of overwriting the key number as
; we do the encryption, thus erasing a very big clue
.ZP2
STA V%+O2%-P2% ; Set the byte at V% to KEY3, so we can work up from
; U% to V% to encrypt the second block of data after the
; decryption routine, ending with the correct seed
; (which we can then use to "unzip" the encrypted data
; in the opposite direction, from V% to U%)
LDA #0 ; Set ZP = 0, so (ZP+1 Y) will equal ZP(1 0) + Y
STA ZP
LDY #LO(U%+O2%-P2%) ; Set (ZP+1 Y) to the address in local memory of the
LDA #HI(U%+O2%-P2%) ; start of the code that we want to encrypt (i.e. U% in
STA ZP+1 ; the loader, or the first byte after the decryption
; routine)
LDA #LO(V%+O2%-P2%) ; Set ZP2(1 0) to the address in local memory of the
STA ZP2 ; end of the code that we want to encrypt (i.e. V% at
LDA #HI(V%+O2%-P2%) ; the end of the loader)
STA ZP2+1
JSR WUMP ; Encrypt from (ZP+1 Y) up to ZP2(1 0), i.e. from U% up
; to V% in the loader
LDA X%+O2%-P2% ; Store the contents of the byte at X% on the stack,
PHA ; so we can restore it below
LDA #KEY4 ; Set the byte at X% to KEY4, so we can work up from
STA X%+O2%-P2% ; W% to X% to encrypt the first block of data before the
; decryption routine, ending with the correct seed
; (which we can then use to "unzip" the encrypted data
; in the opposite direction, from X% to W%)
LDA #0 ; Set ZP = 0, so (ZP+1 Y) will equal ZP(1 0) + Y
STA ZP
LDY #LO(W%+O2%-P2%) ; Set (ZP+1 Y) to the address in local memory of the
LDA #HI(W%+O2%-P2%) ; start of the code that we want to encrypt (i.e. W% in
STA ZP+1 ; the loader, or the first byte of the loader)
LDA #LO(X%+O2%-P2%) ; Set ZP2(1 0) to the address in local memory of the
STA ZP2 ; end of the code that we want to encrypt (i.e. X% just
LDA #HI(X%+O2%-P2%) ; before the decryption routine)
STA ZP2+1
JSR WUMP ; Encrypt from (ZP+1 Y) up to ZP2(1 0), i.e. from W% up
; to X% in the loader
PLA ; Restore the byte at X%, which we corrupted above
STA X%+O2%-P2%
RTS ; Return from the subroutine
.WUMP
; This routine encrypts from (ZP+1 Y) up to ZP2(1 0)
;
; This is the same as ZP2(1 0) down to ZP(1 0) + Y
LDA (ZP),Y ; If Y = 255, jump to WUMP2 to add the bytes across the
CLC ; page boundary and jump back to WUMP3
INY ;
BEQ WUMP2 ; Otherwise add byte Y+1 of ZP(1 0) to byte Y of ZP(1 0)
ADC (ZP),Y
DEY
STA (ZP),Y
.WUMP3
INY ; Increment Y to point to the next byte to encrypt
CPY ZP2 ; If we haven't reached ZP2(1 0), loop back to WUMP to
BNE WUMP ; encrypt the next byte
LDA ZP+1
CMP ZP2+1
BNE WUMP
RTS ; Otherwise we have reached ZP2(1 0), so return from the
; subroutine
.WUMP2
; If we get here then Y has just been incremented from
; 255 to 0
INC ZP+1 ; Increment ZP(1 0) to point to the next page
ADC (ZP),Y ; Add byte 0 of the new page to byte 255 of the old page
DEC ZP+1 ; (C is still clear from above)
DEY
STA (ZP),Y
INC ZP+1
BNE WUMP3 ; Jump to WUMP3 to continue encrypting the new page