|
|
IDENTIFICATION SERVEUR : 10.14.20.133 - CLIENT : 3.237.97.64 |
|
Voir le sujet précédent :: Voir le sujet suivant |
Auteur |
Message |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 22 Aoû 2012, 0:28 Sujet du message: Choplifter (Brøderbund, 1982) |
|
|
Choplifter is a famous Broderbund title, using the same protection method as Operation Apocalypse (a very rare title that I never saw anywhere except on my old drawer).
Already interesting protection - even the bootsector is protected by omitting the tail (DEAAEB).
This is possible because the boot PROM does not check for it due to space limitations
Let's grab the bootsector:
Code: |
8600<C600.C6FCM
86F9:69 FF
8600G
|
Stage 0 loader:
Code: |
0800 .BYTE 1
;copy self to $200, continue at $20F
0801 LDX #0
0803 loc_803:
0803 LDA $800,X
0806 STA $200,X
0809 INX
080A BNE loc_803
080C JMP $20F
080F LDY #$AB
0811 loc_811:
0811 TYA
0812 STA $3C
0814 LSR A
0815 ORA $3C
0817 CMP #$FF
0819 BNE loc_824
081B CPY #$D5
081D BEQ loc_824
081F TXA
0820 STA $800,Y
0823 INX
0824 loc_824:
0824 INY
0825 BNE loc_811
0827 STY $3D
0829 STY $26
082B LDA #3
082D STA $27
082F LDX $2B
0831 JSR $25D
0834 JSR $2D1
0837 JMP $301
083A .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
084A .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
085A .BYTE 0, 0, 0
085D loc_85D:
085D CLC
085E loc_85E:
085E PHP
085F loc_85F:
085F LDA $C08C,X
0862 BPL loc_85F
0864 loc_864:
0864 EOR #$D5
0866 BNE loc_85F
0868 loc_868:
0868 LDA $C08C,X
086B BPL loc_868
086D CMP #$AA
086F BNE loc_864
0871 NOP
0872 loc_872:
0872 LDA $C08C,X
0875 BPL loc_872
0877 CMP #$B5
0879 BEQ loc_884
087B PLP
087C BCC loc_85D
087E EOR #$AD
0880 BEQ loc_8A1
0882 BNE loc_85D
0884 loc_884:
0884 LDY #3
0886 STY $2A
0888 loc_888:
0888 LDA $C08C,X
088B BPL loc_888
088D ROL A
088E STA $3C
0890 loc_890:
0890 LDA $C08C,X
0893 BPL loc_890
0895 AND $3C
0897 DEY
0898 BNE loc_888
089A PLP
089B CMP $3D
089D BNE loc_85D
089F BCS loc_85E
08A1 loc_8A1:
08A1 LDY #$9A
08A3 loc_8A3:
08A3 STY $3C
08A5 loc_8A5:
08A5 LDY $C08C,X
08A8 BPL loc_8A5
08AA EOR $800,Y
08AD LDY $3C
08AF DEY
08B0 STA $800,Y
08B3 BNE loc_8A3
08B5 loc_8B5:
08B5 STY $3C
08B7 loc_8B7:
08B7 LDY $C08C,X
08BA BPL loc_8B7
08BC EOR $800,Y
08BF LDY $3C
08C1 STA ($26),Y
08C3 INY
08C4 BNE loc_8B5
08C6 loc_8C6:
08C6 LDY $C08C,X
08C9 BPL loc_8C6
08CB EOR $800,Y
08CE BNE loc_85D
08D0 RTS
08D1 TAY
08D2 loc_8D2:
08D2 LDX #0
08D4 loc_8D4:
08D4 LDA $800,Y
08D7 LSR A
08D8 ROL $3CC,X
08DB LSR A
08DC ROL $399,X
08DF STA $3C
08E1 LDA ($26),Y
08E3 ASL A
08E4 ASL A
08E5 ASL A
08E6 ORA $3C
08E8 STA ($26),Y
08EA INY
08EB INX
08EC CPX #$33 ; '3'
08EE BNE loc_8D4
08F0 DEC $2A
08F2 BNE loc_8D2
08F4 CPY $300
08F7 BNE loc_8FC
08F9 RTS
08FA .BYTE 0, 0
08FC loc_8FC:
08FC JMP $FF2D
08FF .BYTE 0
|
Trace to stage 0.1:
Code: |
86F8:A9 5 8D D 8 A9 87 8D E 8 4C 1 8 4C 69 FF
8600G
|
Now we have code at $200, let's look at $20F.
Code: |
;build 5-and-3 nibbilisation table
020F loc_20F:
020F LDY #$AB
0211 loc_211:
0211 TYA
0212 STA $3C
0214 LSR A
0215 ORA $3C
0217 CMP #$FF
0219 BNE loc_224
021B CPY #$D5
021D BEQ loc_224
021F TXA
0220 STA $800,Y
0223 INX
0224 loc_224:
0224 INY
0225 BNE loc_211
;prepare to read another sector 0 but different address marker so it's okay
;read to $300
0227 STY $3D
0229 STY $26
022B LDA #3
022D STA $27
022F LDX $2B
0231 JSR loc_25D ;read sector
0234 JSR loc_2D1 ;decode sector
0237 JMP $301 ;continue boot at stage 1
023A .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
024A .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
025A .BYTE 0, 0, 0
;read a sector, D5AAB5 and D5AAAD
025D loc_25D:
025D CLC
025E loc_25E:
025E PHP
025F loc_25F:
025F LDA $C08C,X
0262 BPL loc_25F
0264 loc_264:
0264 EOR #$D5
0266 BNE loc_25F
0268 loc_268:
0268 LDA $C08C,X
026B BPL loc_268
026D CMP #$AA
026F BNE loc_264
0271 NOP
0272 loc_272:
0272 LDA $C08C,X
0275 BPL loc_272
0277 CMP #$B5
0279 BEQ loc_284
027B PLP
027C BCC loc_25D
027E EOR #$AD
0280 BEQ loc_2A1
0282 BNE loc_25D
0284 loc_284:
0284 LDY #3
0286 STY $2A
0288 loc_288:
0288 LDA $C08C,X
028B BPL loc_288
028D ROL A
028E STA $3C
0290 loc_290:
0290 LDA $C08C,X
0293 BPL loc_290
0295 AND $3C
0297 DEY
0298 BNE loc_288
029A PLP
029B CMP $3D
029D BNE loc_25D
029F BCS loc_25E
02A1 loc_2A1:
02A1 LDY #$9A
02A3 loc_2A3:
02A3 STY $3C
02A5 loc_2A5:
02A5 LDY $C08C,X
02A8 BPL loc_2A5
02AA EOR $800,Y
02AD LDY $3C
02AF DEY
02B0 STA $800,Y
02B3 BNE loc_2A3
02B5 loc_2B5:
02B5 STY $3C
02B7 loc_2B7:
02B7 LDY $C08C,X
02BA BPL loc_2B7
02BC EOR $800,Y
02BF LDY $3C
02C1 STA ($26),Y
02C3 INY
02C4 BNE loc_2B5
02C6 loc_2C6:
02C6 LDY $C08C,X
02C9 BPL loc_2C6
02CB EOR $800,Y
02CE BNE loc_25D
02D0 RTS
02D1 loc_2D1:
02D1 TAY
02D2 loc_2D2:
02D2 LDX #0
02D4 loc_2D4:
02D4 LDA $800,Y
02D7 LSR A
02D8 ROL $3CC,X
02DB LSR A
02DC ROL $399,X
02DF STA $3C
02E1 LDA ($26),Y
02E3 ASL A
02E4 ASL A
02E5 ASL A
02E6 ORA $3C
02E8 STA ($26),Y
02EA INY
02EB INX
02EC CPX #$33
02EE BNE loc_2D4
02F0 DEC $2A
02F2 BNE loc_2D2
02F4 CPY $300
02F7 BNE loc_2FC
02F9 RTS
02FA .BYTE 0, 0
02FC loc_2FC:
02FC JMP $FF2D
|
(continued)
Dernière édition par qkumba le Ven 19 Oct 2012, 20:53; édité 2 fois |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 22 Aoû 2012, 0:32 Sujet du message: boot-tracing Choplifter |
|
|
Trace to stage 1 loader:
Code: |
8705:A9 12 8D 38 2 A9 87 8D 39 2 4C F 2 4C 69 FF
8600G
|
Now we have code at $300, let's look at $301.
Code: |
0300 .BYTE $99 ;checksum
0301 LDX #$2E
0303 TXS
0304 STY $48
0306 LDX #0
0308 loc_308:
0308 LDY $300,X
030B DEY
030C LDA #$EA
030E JSR loc_326
0311 INY
0312 LDA $48
0314 JSR loc_326
0317 DEY
0318 LDA $48
031A JSR loc_326
031D DEY
031E TYA
031F loc_31F:
031F STA $100,X ;nasty stack usage
0322 INX
0323 BNE loc_308
0325 RTS
0326 loc_326:
0326 STA 0
0328 TYA
0329 EOR 0 ;a decoder!
032B TAY
032C RTS
;encoded bytes
032D .BYTE $EA, $FF, $C6, $EF, $4C, $B6, $68, $E9, $EF, $76, $16, $4C, $EA, $6A, 6, $70
033D .BYTE $1F, $74, $47, $76, $7F, 6, $2E, $26, $15, $10, $1F, $33, $1F, $27, $E6, $66
034D .BYTE $1F, $4B, $6F, $56, $4B, $6F, $56, $47, $26, $53, $1F, $5F, 6, $7F, 6, $2E
035D .BYTE $26, $17, $10, $1F, $26, 3, $33, $C5, $A4, $A4, $A4, $A4, $E7, $56, $62, $12
036D .BYTE $E9, $6B, $1B, $E9, $62, $1C, $ED, $6B, $1D, $ED, $A7, $33, $6B, $1A, $ED, $4B
037D .BYTE $6C, $56, $4B, $BF, $56, $4B, $A3, $56, $4B, $96, $56, $F6, $66, $EF, $47, $EA
038D .BYTE $AE, $AE, $B6, $4C, $EA, $76, $16, $6A, $11, $70, $1E, $70, $14, $50, $C5, $F6
039D .BYTE 3, $EF, $27, $23, $26, $17, $F6, 3, $EF, $27, $3B, $26, 3, $F6, 3, $EF
03AD .BYTE $27, $23, $26, 3, $5B, $62, $56, $E6, $15, $C4, $53, $17, $5B, $62, $56, $E6
03BD .BYTE $15, $B3, $17, $7F, $11, $2E, $26, 2, $E8, $16, $56, $5B, $62, $56, $E6, $15
03CD .BYTE $27, $3B, $26, $29, $10, $1E, $30, $14, $26, $34, $F6, 3, $EF, $53, $1C, $F6
03DD .BYTE 3, $EF, $53, $1F, $F6, 3, $EF, $53, 6, $F6, 3, $EF, $53, $1D, $F6, 3
03ED .BYTE $EF, $13, 6, 6, $E7, $82, $1C, $ED, $5B, $62, $56, $E6, $15, $B6, $48, $73
03FD .BYTE $D3, $74, $16
|
This is a particularly tricky one because we can't jump to the monitor anymore without destroying the decoded code.
We also don't have enough bytes to patch a jump after the decoding finishes.
It might seem like we can just redirect the write at $31F to another memory location so that we can intercept the end of the decoding and examine the code...
but this is Broderbund, you know that we can't do that, either. The data on the stack are checksummed later, if we patch the routine, the checksum will change.
Let's copy it to $6300 instead, and redirect the load at $308.
Code: |
8712:A2 0 BD 0 3 9D 0 63 E8 D0 F7 A9 63 8D A 3 A9 4C 8D 1F 3 A9 34 8D 20 3 A9 87 8D 21 3 4C 1 3
8734:9D 0 61 E8 F0 3 4C 8 3 4C 69 FF
8600G
|
Now we have our decoded code at $6100 instead of $100, but we'll use the original address to that the routines match.
Code: |
0100 .BYTE $6F, $4C, $C8, $74, $6A, $AE, $4C, $16, $52, $16, $F5, $6E, $3F, 4, $F6, $D0
0110 .BYTE $F5, $2E, $4B, $AE, $F6, $D0, $F5, $6E, $4B, $AE, $F6, $D0, $F5, $6E, $7E, $73
0120 .BYTE $16, $E7, $E, $26, $15, $B6, $6B, $16, $7E, $AB, $16, $4E, $B6, 4, $11, $30
0130 .BYTE 1, $A2, $60, $8E, $FF, 1, $A0, 0, $A2, 4, $84, $F0, $86, $F1, $9A, $A9
0140 .BYTE $A0, $91, $F0, $C8, $D0, $FB, $E6, $F1, $A5, $F1, $C9, $10, $90, $F1, $AD, $81
0150 .BYTE $C0, $AD, $81, $C0, $A9, $D0, $85, $F1, $B1, $F0, $91, $F0, $C8, $D0, $F9, $E6
0160 .BYTE $F1, $D0, $F5, $A5, $2B, $4A, $4A, $4A, $4A, 9, $C0, $8C, $FC, $FF, $8D, $FD
0170 .BYTE $FF, $8C, $F2, 3, $8D, $F3, 3, $49, $A5, $8D, $F4, 3, $AD, $82, $C0, $AD
0180 .BYTE $51, $C0, $AD, $55, $C0, $AD, $80, $C0, $20, $90, 1, $A9, 4, $48, $48, $60
0190 .BYTE $A2, 4, $A0, 0, $84, $F7, $86, $F8, $86, $FA, $A6, $2B, $20, $F5, 1, $C9
01A0 .BYTE $D5, $D0, $F9, $20, $F5, 1, $C9, $DD, $D0, $F5, $20, $F5, 1, $C9, $D5, $D0
01B0 .BYTE $F5, $BD, $8C, $C0, $10, $FB, $2A, $85, $F9, $BD, $8C, $C0, $10, $FB, $25, $F9
01C0 .BYTE $91, $F7, $C8, $D0, $EC, $E, 0, $C0, $BD, $8C, $C0, $10, $FB, $C9, $DD, $D0
01D0 .BYTE $BF, $E6, $F8, $C6, $FA, $D0, $DA, $20, $F5, 1, $85, $F2, $20, $F5, 1, $85
01E0 .BYTE $F1, $20, $F5, 1, $85, $F0, $20, $F5, 1, $85, $F3, $20, $F5, 1, $C5, $F0
01F0 .BYTE $F0, 9, $6C, $F2, 3, $BD, $8C, $C0, $10, $FB, $60, $AE, $65, 5, $9A, 0
|
Stack pointer was #$2E, which is 1 less than the address that's fetched during RTS, so we read $12F to see where to go next.
So return address is $130, which is also 1 less than the address that's used, so we continue from $131.
Code: |
0131 LDX #$60
0133 STX byte_1FF ;place RTS because the decoder couldn't do it
0136 LDY #0
0138 LDX #4
013A STY $F0
013C STX $F1
013E TXS
013F loc_13F:
013F LDA #$A0
0141 loc_141:
0141 STA ($F0),Y ;clear text screens and some other memory
0143 INY
0144 BNE loc_141
0146 INC $F1
0148 LDA $F1
014A CMP #$10
014C BCC loc_13F
014E LDA $C081
0151 LDA $C081
0154 LDA #$D0
0156 STA $F1
0158 loc_158:
0158 LDA ($F0),Y
015A STA ($F0),Y
015C INY
015D BNE loc_158
015F INC $F1
0161 BNE loc_158 ;enable 64k support
0163 LDA $2B
0165 LSR A
0166 LSR A
0167 LSR A
0168 LSR A
0169 ORA #$C0
016B STY $FFFC
016E STA $FFFD
0171 STY $3F2
0174 STA $3F3
0177 EOR #$A5
0179 STA $3F4 ;point reset and break vectors to boot PROM
017C LDA $C082
017F LDA $C051
0182 LDA $C055 ;select second text screen to hide first one
0185 LDA $C080
0188 JSR loc_190
018B LDA #4
018D PHA
018E PHA
018F RTS
;prepare to 4x large sector to $400-7FF
0190 loc_190:
0190 LDX #4
0192 LDY #0
0194 STY $F7
0196 STX $F8
0198 STX $FA
019A LDX $2B
;read address marker (D5DDD5)
;address marker identifies the sector
019C loc_19C:
019C JSR sub_1F5
019F loc_19F:
019F CMP #$D5
01A1 BNE loc_19C
01A3 JSR sub_1F5
01A6 loc_1A6:
01A6 CMP #$DD
01A8 BNE loc_19F
01AA JSR sub_1F5
01AD CMP #$D5
01AF BNE loc_1A6
;4-and-4 read/decode
01B1 loc_1B1:
01B1 LDA $C08C,X
01B4 BPL loc_1B1
01B6 ROL A
01B7 STA $F9
01B9 loc_1B9:
01B9 LDA $C08C,X
01BC BPL loc_1B9
01BE AND $F9
01C0 STA ($F7),Y
01C2 INY
01C3 BNE loc_1B1
01C5 ASL $C000
;tail check and select next sector if okay
01C8 loc_1C8:
01C8 LDA $C08C,X
01CB BPL loc_1C8
01CD CMP #$DD
01CF BNE loc_190
01D1 INC $F8
01D3 DEC $FA
01D5 BNE loc_1B1
;read next address marker and tail comparison bytes
;this means that each sector is depending on the previous one
;so if one sector is bad then entire disk is useless
01D7 JSR sub_1F5
01DA STA $F2
01DC JSR sub_1F5
01DF STA $F1
01E1 JSR sub_1F5
01E4 STA $F0
01E6 JSR sub_1F5
01E9 STA $F3
01EB JSR sub_1F5
01EE CMP $F0
01F0 BEQ loc_1FB
01F2 JMP ($3F2)
01F5 loc_1F5:
01F5 LDA $C08C,X
01F8 BPL loc_1F5
01FA RTS
01FB loc_1FB:
01FB LDX $565
01FE TXS
01FF byte_1FF: .BYTE 0 ;replaced by RTS from above
|
A successful read sets a new stack pointer from the value at $565.
A wrong value at that location will cause the RTS at $1FF to jump to somewhere unexpected.
So the load at $1FB would be a good place to patch next.
(continued) |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 22 Aoû 2012, 0:35 Sujet du message: boot-tracing Choplifter |
|
|
First we undo the redirection so the stack will be written as intended:
Then we have to redirect the next read so that it doesn't go to the screen anymore. Let's use $6400.
We have to set the number of sectors ourselves. Then we add add our new patch.
Code: |
873D:A9 64 8D 91 1 A9 EA 8D 98 1 8D 99 1 A9 4 85 FA A9 4C 8D FB 1 A9 60 8D FC 1 A9 87 8D FD 1 4C 31 1 4C 60 87
8600G
|
We can't jump to the monitor anymore, so we just hang the system after the read.
If we insert a blank disk and hit ctrl-reset, we can reboot and keep the read memory.
Stage 2 loader:
Code: |
0400 loc_400:
0400
0400 PLA
0401 CLC
0402 ADC $48
0404 STA $48
0406 DEX
0407 BNE loc_400
0409 LDA #$14
040B JSR loc_772
040E LDX #1
0410 LDA #5
0412 JSR loc_700
0415 LDA #$16
0417 JSR loc_772
041A LDA $48
041C EOR $195
041F TAX
0420 TXS
0421 JMP ($200)
0424 loc_424:
0424 LDA $C08C,X
0427 BPL loc_424
0429 CMP #$D5
042B BNE loc_424
042D loc_42D:
042D LDA $C08C,X
0430 BPL loc_42D
0432 CMP #$FF
0434 BNE loc_424
0436 RTS
0437 JSR loc_424
043A JMP loc_424
043D JMP ($200)
0440 LDX $2B
0442 STX $202
0445 LDX #$20
0447 LDA #0
0449 loc_449:
0449
0449 STA $2000
044C INC loc_449+1
044F BNE loc_449
0451 INC loc_449+2
0454 DEX
0455 BNE loc_449
0457 LDA $C052
045A LDA $C057
045D LDA $C054
0460 LDA $C050
0463 NOP
0464 NOP
0465 NOP
0466 NOP
0467 STX $F4
0469 LDA #$20
046B loc_46B:
046B PHA
046C LDA #2
046E JSR loc_7F0
0471 PLA
0472 PHA
0473 loc_473:
0473 LDX #8
0475 JSR loc_700
0478 PLA
0479 CLC
047A ADC loc_473+1
047D CMP #$60
047F BCC loc_46B
0481 LDA #2
0483 JSR loc_7F0
0486 LDA #$60
0488 LDX #4
048A loc_48A:
048A JSR loc_700
048D DEC loc_48A+2
0490 DEC loc_48A+2
0493 DEC loc_48A+2
0496 LDX #0
0498 JMP (loc_48A+1)
049B LDX loc_599
049E LDY #$9A
04A0 LDA #$FF
04A2 STA $A0
04A5 loc_4A5:
04A5 LDA $500,Y
04A8 STA $200,Y
04AB CPY #$F0
04AD BCS loc_4B5
04AF LDA $600,Y
04B2 STA $300,Y
04B5 loc_4B5:
04B5 INY
04B6 BNE loc_4A5
04B8 LDA #3
04BA LDX #$20
04BC JSR $3A3
04BF LDX #$26
04C1 STX $36
04C3 STA $37
04C5 STX $38
04C7 STA $39
04C9 STX $3F0
04CC STA $3F1
04CF LDX #$FF
04D1 JSR loc_4E2
04D4 JSR loc_4E0
04D7 LDA $185
04DA AND #$5F
04DC TAX
04DD JMP loc_61D
04E0 loc_4E0:
04E0 LDX #0
04E2 loc_4E2:
04E2 LDY #0
04E4 LDA #$C
04E6 STY 0
04E8 STA 1
04EA loc_4EA:
04EA TXA
04EB loc_4EB:
04EB STA (0),Y
04ED CMP (0),Y
04EF BNE loc_4FD
04F1 INY
04F2 BPL loc_4EB
04F4 INC 1
04F6 LDA 1
04F8 CMP #$C0
04FA BCC loc_4EA
04FC RTS
04FD loc_4FD:
04FD JMP $323
0500 .WORD $6300
0502 .WORD $4060
0504 .WORD $42
0506 .BYTE 0
0507 LDX $202
050A LDA $C089,X
050D LDA #0
050F JSR $311
0512 LDA #$42
0514 JSR $2CD
0517 LDA #$40
0519 STA $203
051C loc_51C:
051C DEY
051D BNE loc_527
051F DEC $203
0522 BNE loc_527
0524 JMP $2B8
0527 loc_527:
0527 LDA $C08C,X
052A BPL loc_527
052C loc_52C:
052C CMP #$D5
052E BNE loc_51C
0530 loc_530:
0530 LDA $C08C,X
0533 BPL loc_530
0535 loc_535:
0535 CMP #$FF
0537 BNE loc_52C
0539 loc_539:
0539 LDA $C08C,X
053C BPL loc_539
053E CMP #$DD
0540 BNE loc_535
0542 LDA #$44
0544 JSR $2CD
0547 LDY #$FF
0549 LDA $C08D,X
054C LDA $C08E,X
054F STA $C08F,X
0552 ORA $C08C,X
0555 LDA #$80
0557 JSR $311
055A JSR $311
055D LDA $C08D,X
0560 LDA $C08E,X
0563 TYA
0564 STA $C08F,X
0567 ORA $C08C,X
056A PHA
056B PLA
056C loc_56C:
056C CMP (0,X)
056E CMP (0,X)
0570 NOP
0571 INY
0572 STA $C08D,X
0575 ORA $C08C,X
0578 LDA $2BE,Y
057B BNE loc_56C
057D TAY
057E NOP
057F NOP
0580 loc_580:
0580 LDA 0,Y
0583 PHA
0584 LSR A
0585 ORA #$AA
0587 STA $C08D,X
058A CMP $C08C,X
058D CMP (0,X)
058F NOP
0590 NOP
0591 PHA
0592 PLA
0593 PLA
0594 ORA #$AA
0596 STA $C08D,X
0599 loc_599:
0599 CMP $C08C,X
059C PHA
059D PLA
059E INY
059F BNE loc_580
05A1 LDA #$D5
05A3 CMP (0,X)
05A5 NOP
05A6 NOP
05A7 STA $C08D,X
05AA ORA $C08C,X
05AD LDA #8
05AF JSR $311
05B2 LDA $C08E,X
05B5 LDA $C08C,X
05B8 LDA $C088,X
05BB NOP
05BC NOP
05BD RTS
05BE .WORD $3FFF,$F3CF,$FFFC,$CF3F,$FCF3,$D5FF,$DDFF
05CC .BYTE 0
05CD STA $205
05D0 loc_5D0:
05D0 LDA $204
05D3 STA $206
05D6 SEC
05D7 SBC $205
05DA BEQ locret_60E
05DC BCS loc_5E3
05DE INC $204
05E1 BCC loc_5E6
05E3 loc_5E3:
05E3 DEC $204
05E6 loc_5E6:
05E6 JSR $301
05E9 JSR $30F
05EC LDA $206
05EF AND #3
05F1 ASL A
05F2 ORA $202
05F5 TAY
05F6 LDA $C080,Y
05F9 JSR $30F
05FC BEQ loc_5D0
05FE JSR $30F
0601 LDA $204
0604 AND #3
0606 ASL A
0607 ORA $202
060A TAY
060B LDA $C081,Y
060E locret_60E:
060E RTS
060F LDA #$30
0611 SEC
0612 loc_612:
0612 PHA
0613 loc_613:
0613 SBC #1
0615 BNE loc_613
0617 PLA
0618 SBC #1
061A BNE loc_612
061C RTS
061D loc_61D:
061D JMP loc_6FC
0620 LDA #$D2
0622 .BYTE $2C
0623 LDA #$A1
0625 .BYTE $2C
0626 LDA #$D0
0628 .BYTE $2C
0629 LDA #$CC
062B .BYTE $2C
062C LDA #$C4
062E .BYTE $2C
062F LDA #$CD
0631 STA $205
0634 LDX #4
0636 LDY #0
0638 STY 0
063A STX 1
063C LDA #$A0
063E loc_63E:
063E STA (0),Y
0640 INY
0641 BNE loc_63E
0643 INC 1
0645 DEX
0646 BNE loc_63E
0648 LDA $205
064B STA loc_400
064E JSR $FB2F
0651 LDA #0
0653 loc_653:
0653 STA $800
0656 INC $354
0659 BNE loc_653
065B INC $355
065E LDY $355
0661 CPY #$50
0663 BNE loc_66E
0665 loc_665:
0665 BIT $C030
0668 loc_668:
0668 DEX
0669 BNE loc_668
066B DEY
066C BNE loc_665
066E loc_66E:
066E CPY #$C0
0670 BCC loc_653
0672 loc_672:
0672 STA 0,X
0674 STA $102,X
0677 STA $204,X
067A INX
067B BNE loc_672
067D LDA $202
0680 LSR A
0681 LSR A
0682 LSR A
0683 LSR A
0684 ORA #$C0
0686 STA $3A2
0689 JSR $3A3
068C LDY #$F5
068E TXA
068F loc_68F:
068F STA $300,Y
0692 INY
0693 CPY #$90
0695 BNE loc_68F
0697 JSR $FE89
069A JSR $FE93
069D JSR $FE84
06A0 JMP $C600
06A3 LDY $C083
06A6 LDY $C083
06A9 STX $3F2
06AC STA $3F3
06AF STX $FFFC
06B2 STA $FFFD
06B5 PHA
06B6 EOR #$A5
06B8 STA $3F4
06BB PLA
06BC RTS
06BD .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
06CD .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
06DD .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
06ED .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
06FC loc_6FC:
06FC DEX
06FD DEX
06FE TXS
06FF RTS
0700 loc_700:
0700 STX $FA
0702 STA $F7
0704 loc_704:
0704 LDX $FA
0706 STX $FC
0708 LDY #0
070A LDA $F7
070C STY $F8
070E STA $F9
0710 LDX $2B
0712 loc_712:
0712 JSR loc_76C
0715 loc_715:
0715 CMP $F0
0717 BNE loc_712
0719 JSR loc_76C
071C loc_71C:
071C CMP $F1
071E BNE loc_715
0720 JSR loc_76C
0723 CMP $F2
0725 BNE loc_71C
0727 loc_727:
0727 LDA $C08C,X
072A BPL loc_727
072C ROL A
072D STA $FB
072F loc_72F:
072F LDA $C08C,X
0732 BPL loc_72F
0734 AND $FB
0736 STA ($F8),Y
0738 INY
0739 BNE loc_727
073B ASL $C000
073E loc_73E:
073E LDA $C08C,X
0741 BPL loc_73E
0743 CMP $F3
0745 BNE loc_704
0747 INC $F9
0749 DEC $FC
074B BNE loc_727
;read address marker and tail comparison bytes for next sector
074D JSR loc_76C
0750 STA $F2
0752 JSR loc_76C
0755 STA $F1
0757 JSR loc_76C
075A STA $F0
075C JSR loc_76C
075F STA $F3
0761 JSR loc_76C
0764 CMP $F0
0766 BNE loc_769
0768 RTS
0769 loc_769:
0769 JMP $32C
076C loc_76C:
076C LDA $C08C,X
076F BPL loc_76C
0771 RTS
0772 loc_772:
0772 STA $FD
0774 CMP $F4
0776 BEQ locret_7C7
0778 LDA #0
077A STA $F5
077C loc_77C:
077C LDA $F4
077E STA $F6
0780 SEC
0781 SBC $FD
0783 BEQ loc_7B6
0785 BCS loc_78D
0787 EOR #$FF
0789 INC $F4
078B BCC loc_791
078D loc_78D:
078D ADC #$FE
078F DEC $F4
0791 loc_791:
0791 CMP $F5
0793 BCC loc_797
0795 LDA $F5
0797 loc_797:
0797 CMP #$C
0799 BCS loc_79C
079B TAY
079C loc_79C:
079C SEC
079D JSR loc_7BA
07A0 LDA byte_7D3,Y
07A3 JSR loc_7C8
07A6 LDA $F6
07A8 CLC
07A9 JSR loc_7BC
07AC LDA byte_7DF,Y
07AF JSR loc_7C8
07B2 INC $F5
07B4 BNE loc_77C
07B6 loc_7B6:
07B6 JSR loc_7C8
07B9 CLC
07BA loc_7BA:
07BA LDA $F4
07BC loc_7BC:
07BC AND #3
07BE ROL A
07BF ORA $2B
07C1 TAX
07C2 LDA $C080,X
07C5 LDX $2B
07C7 locret_7C7:
07C7 RTS
07C8 loc_7C8:
07C8 LDX #$13
07CA loc_7CA:
07CA DEX
07CB BNE loc_7CA
07CD SEC
07CE SBC #1
07D0 BNE loc_7C8
07D2 RTS
07D3 byte_7D3: .BYTE 1, $30, $28, $24, $20, $1E, $1D, $1C, $1C, $1C, $1C, $1C
07DF byte_7DF: .BYTE $70, $2C, $26, $22, $1F, $1E, $1D, $1C, $1C, $1C, $1C, $1C
07EB .BYTE 0, 0, 0, 0, 0
07F0 loc_7F0:
07F0 CLC
07F1 ADC $F4
07F3 JMP loc_772
07F6 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
Let's check that stack pointer again. $565 is #$8F, which is 1 less than the address that's fetched during RTS, so we read $190 to see where to go next.
That's the location of our redirection, so it's in the $64xx range instead of the $4xx range, but the effect is the same.
So return address is really $4A2, which is also 1 less than the address that's used, so we continue at $4A3.
If you look in the original disassembly, you won't see 04A3 there, only 04A2 and 04A5.
That's because there's an anti-disassembly trick, which is hiding the LDY instruction. :-)
Code: |
;copy miscellaneous routines to $200-3EF
04A3 LDY #0
04A5 loc_4A5:
04A5 LDA $500,Y
04A8 STA $200,Y
04AB CPY #$F0
04AD BCS loc_4B5
04AF LDA $600,Y
04B2 STA $300,Y
04B5 loc_4B5:
04B5 INY
04B6 BNE loc_4A5
;point reset and break vectors to $320 ($6620)
04B8 LDA #3
04BA LDX #$20
04BC JSR $3A3
;point CSW, KSW, and ROM break vectors to $326 ($6626)
04BF LDX #$26
04C1 STX $36
04C3 STA $37
04C5 STX $38
04C7 STA $39
04C9 STX $3F0
04CC STA $3F1
;write memory pattern
;this is a particular problem because it will destroy our code
;unless... there's a weakness that we can exploit
;and of course there is
;the pattern is written only to the first #$80 bytes
;so if we write our code in the address range $xx80-xxff
;then we won't be touched
04CF LDX #$FF
04D1 JSR loc_4E2
04D4 JSR loc_4E0
04D7 LDA $185
04DA AND #$5F
04DC TAX
04DD JMP loc_61D
04E0 loc_4E0:
04E0 LDX #0
04E2 loc_4E2:
04E2 LDY #0
04E4 LDA #$C
04E6 STY 0
04E8 STA 1
04EA loc_4EA:
04EA TXA
04EB loc_4EB:
04EB STA (0),Y
04ED CMP (0),Y
04EF BNE loc_4FD
04F1 INY
04F2 BPL loc_4EB
04F4 INC 1
04F6 LDA 1
04F8 CMP #$C0
04FA BCC loc_4EA
04FC RTS
04FD loc_4FD:
04FD JMP $323
...
061D loc_61D:
061D JMP loc_6FC
...
06FC loc_6FC:
06FC DEX
06FD DEX
06FE TXS
06FF RTS
|
Regardless of the memory pattern problem, we are interested in the value at $185 (from $4D7) to know where to go next.
$185 is #$AD, then there's the AND #$5F, which gives us #$0D. The jump at $4DD reaches $6FC which decrements the value to #$0B.
As before, that's 1 less than the address that's fetched during RTS, so we read $10C to see where to go next.
As usual, it's 1 less than the address that's used, so we continue at $440.
Code: |
0440 LDX $2B
0442 STX $202
;erase first graphics screen
0445 LDX #$20
0447 LDA #0
0449 loc_449:
0449
0449 STA $2000
044C INC loc_449+1
044F BNE loc_449
0451 INC loc_449+2
0454 DEX
0455 BNE loc_449
;enable graphics mode
0457 LDA $C052
045A LDA $C057
045D LDA $C054
0460 LDA $C050
0463 NOP
0464 NOP
0465 NOP
0466 NOP
0467 STX $F4
;prepare to read to $2000-5FFF (title screen and other code)
0469 LDA #$20
046B loc_46B:
046B PHA
;seek to next half track
046C LDA #2
046E JSR loc_7F0
0471 PLA
0472 PHA
;read 8 sectors
0473 loc_473:
0473 LDX #8
0475 JSR loc_700
0478 PLA
0479 CLC
047A ADC loc_473+1
047D CMP #$60
047F BCC loc_46B
;seek to next half track
0481 LDA #2
0483 JSR loc_7F0
;read 4 sectors to $6000-63FF
;the loaded routine is a misguided attempt to detect mods
0486 LDA #$60
0488 LDX #4
048A loc_48A:
048A JSR loc_700
;adjust next pointer and jump
048D DEC loc_48A+2
0490 DEC loc_48A+2
0493 DEC loc_48A+2
0496 LDX #0
0498 JMP (loc_48A+1)
...
0772 loc_772:
0772 STA $FD
0774 CMP $F4
0776 BEQ locret_7C7
0778 LDA #0
077A STA $F5
077C loc_77C:
077C LDA $F4
077E STA $F6
0780 SEC
0781 SBC $FD
0783 BEQ loc_7B6
0785 BCS loc_78D
0787 EOR #$FF
0789 INC $F4
078B BCC loc_791
078D loc_78D:
078D ADC #$FE
078F DEC $F4
0791 loc_791:
0791 CMP $F5
0793 BCC loc_797
0795 LDA $F5
0797 loc_797:
0797 CMP #$C
0799 BCS loc_79C
079B TAY
079C loc_79C:
079C SEC
079D JSR loc_7BA
07A0 LDA byte_7D3,Y
07A3 JSR loc_7C8
07A6 LDA $F6
07A8 CLC
07A9 JSR loc_7BC
07AC LDA byte_7DF,Y
07AF JSR loc_7C8
07B2 INC $F5
07B4 BNE loc_77C
07B6 loc_7B6:
07B6 JSR loc_7C8
07B9 CLC
07BA loc_7BA:
07BA LDA $F4
07BC loc_7BC:
07BC AND #3
07BE ROL A
07BF ORA $2B
07C1 TAX
07C2 LDA $C080,X
07C5 LDX $2B
07C7 locret_7C7:
07C7 RTS
07C8 loc_7C8:
07C8 LDX #$13
07CA loc_7CA:
07CA DEX
07CB BNE loc_7CA
07CD SEC
07CE SBC #1
07D0 BNE loc_7C8
07D2 RTS
07D3 byte_7D3: .BYTE 1, $30, $28, $24, $20, $1E, $1D, $1C, $1C, $1C, $1C, $1C
07DF byte_7DF: .BYTE $70, $2C, $26, $22, $1F, $1E, $1D, $1C, $1C, $1C, $1C, $1C
07EB .BYTE 0, 0, 0, 0, 0
07F0 loc_7F0:
07F0 CLC
07F1 ADC $F4
07F3 JMP loc_772
|
The decrements at $48D change the 7 to a 4, so the jump at $498 goes to $400.
Code: |
;checksum routine
;sums the entire stack
0400 loc_400:
0400
0400 PLA
0401 CLC
0402 ADC $48
0404 STA $48
0406 DEX
0407 BNE loc_400
;seek track 10
0409 LDA #$14
040B JSR loc_772
;read 1 sector to $500
040E LDX #1
0410 LDA #5
0412 JSR loc_700
;seek track 11
0415 LDA #$16
0417 JSR loc_772
;use the checksum as the stack pointer
041A LDA $48
041C EOR $195
041F TAX
0420 TXS
0421 JMP ($200)
|
The value at $200 (from $421) comes from $500 because it was copied by the routine at $4A3.
The read value is $6300.
So we need to patch the jump at $421 so that we can look at the routine at $6300.
We also need to patch the address at $410 so that can see what was read there. We'll use $6500 for that.
We don't need the screen redirection anymore, so we remove that.
However, are affected by the memory pattern at $4CF, so we have to start writing at $8780 to protect future code.
(continued) |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 22 Aoû 2012, 0:39 Sujet du message: boot-tracing Choplifter |
|
|
Code: |
873D:A9 4C 8D FB 1 A9 4F 8D FC 1 A9 87 8D FD 1 4C 31 1
874F:A9 AE 8D FB 1 A9 65 8D FC 1 A9 5 8D FD 1 A9 4C 8D 21 4 A9 80 8D 22 4 A9 87 8D 23 4 A9 65 8D 11 4 4C FB 1
8780:8E 0 64 4C 80 87
8600G
|
Once again, we just hang the system after the read.
Insert a blank disk and hit ctrl-reset, so we can see what was loaded.
We need to know the stack pointer for later, it's stored at $6400 right now.
Here's the routine that was loaded to $6000-63FF.
Code: |
;format is address, count, data
;0 terminates
6000 loc_6000:
6000 .WORD $FA62
6002 .BYTE $38
6003 .BYTE $D8, $20, $84, $FE, $20, $2F, $FB, $20, $93, $FE, $20, $89, $FE, $AD, $58, $C0
6013 .BYTE $AD, $5A, $C0, $AD, $5D, $C0, $AD, $5F, $C0, $AD, $FF, $CF, $2C, $10, $C0, $D8
6023 .BYTE $20, $3A, $FF, $AD, $F3, 3, $49, $A5, $CD, $F4, 3, $D0, $17, $AD, $F2, 3
6033 .BYTE $D0, $F, $A9, $E0, $CD, $F3, 3, $D0, 8
603C .WORD $FAA3
603E .BYTE 2
603F .BYTE $6C, $F2, 3
6042 .WORD $FB2F
6044 .BYTE $10
6045 .BYTE $A9, 0, $85, $48, $AD, $56, $C0, $AD, $54, $C0, $AD, $51, $C0, $A9, 0, $F0
6055 .BYTE $B
6056 .WORD $FB4B
6058 .BYTE $14
6059 .BYTE $85, $22, $A9, 0, $85, $20, $A9, $28, $85, $21, $A9, $18, $85, $23, $A9, $17
6069 .BYTE $85, $25, $4C, $22, $FC
606E .WORD $FBC1
6070 .BYTE $2E
6071 .BYTE $48, $4A, $29, 3, 9, 4, $85, $29, $68, $29, $18, $90, 2, $69, $7F, $85
6081 .BYTE $28, $A, $A, 5, $28, $85, $28, $60, $C9, $87, $D0, $12, $A9, $40, $20, $A8
6091 .BYTE $FC, $A0, $C0, $A9, $C, $20, $A8, $FC, $AD, $30, $C0, $88, $D0, $F5, $60
60A0 .WORD $FBFD
60A2 .BYTE $12
60A3 .BYTE $C9, $A0, $B0, $EF, $A8, $10, $EC, $C9, $8D, $F0, $5A, $C9, $8A, $F0, $5A, $C9
60B3 .BYTE $88, $D0, $C9
60B6 .WORD $FC22
60B8 .BYTE 9
60B9 .BYTE $A5, $25, $20, $C1, $FB, $65, $20, $85, $28, $60
60C3 .WORD $FCA8
60C5 .BYTE $B
60C6 .BYTE $38, $48, $E9, 1, $D0, $FC, $68, $E9, 1, $D0, $F6, $60
60D2 .WORD $FDED
60D4 .BYTE $B
60D5 .BYTE $6C, $36, 0, $C9, $A0, $90, 2, $25, $32, $84, $35, $48
60E1 .WORD $FE84
60E3 .BYTE $29
60E4 .BYTE $A0, $FF, $84, $32, $60, $A9, 0, $85, $3E, $A2, $38, $A0, $1B, $D0, 8, $A9
60F4 .BYTE 0, $85, $3E, $A2, $36, $A0, $F0, $A5, $3E, $29, $F, $F0, 6, 9, $C0, $A0
6104 .BYTE 0, $F0, 2, $A9, $FD, $94, 0, $95, 1, $60
610E .WORD $FF3A
6110 .BYTE 4
6111 .BYTE $A9, $87, $4C, $ED, $FD
6116 .WORD $FBB4
6118 .BYTE 0
6119 .BYTE 8, $78, $2C, $15, $C0, 8, $8D, 7, $C0, $4C, 0, $C1
6125 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
6135 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
6145 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
6150 loc_6150:
6150 .WORD $FB2F
6152 .BYTE $10
6153 .BYTE $A9, 0, $85, $48, $AD, $56, $C0, $AD, $54, $C0, $AD, $51, $C0, $A9, 0, $F0
6163 .BYTE $B
6164 .WORD $FB4B
6166 .BYTE $14
6167 .BYTE $85, $22, $A9, 0, $85, $20, $A9, $28, $85, $21, $A9, $18, $85, $23, $A9, $17
6177 .BYTE $85, $25, $4C, $22, $FC
617C .WORD $FBC1
617E .BYTE $2E
617F .BYTE $48, $4A, $29, 3, 9, 4, $85, $29, $68, $29, $18, $90, 2, $69, $7F, $85
618F .BYTE $28, $A, $A, 5, $28, $85, $28, $60, $C9, $87, $D0, $12, $A9, $40, $20, $A8
619F .BYTE $FC, $A0, $C0, $A9, $C, $20, $A8, $FC, $AD, $30, $C0, $88, $D0, $F5, $60
61AE .WORD $FBFD
61B0 .BYTE $12
61B1 .BYTE $C9, $A0, $B0, $EF, $A8, $10, $EC, $C9, $8D, $F0, $5A, $C9, $8A, $F0, $5A, $C9
61C1 .BYTE $88, $D0, $C9
61C4 .WORD $FC22
61C6 .BYTE 9
61C7 .BYTE $A5, $25, $20, $C1, $FB, $65, $20, $85, $28, $60
61D1 .WORD $FCA8
61D3 .BYTE $B
61D4 .BYTE $38, $48, $E9, 1, $D0, $FC, $68, $E9, 1, $D0, $F6, $60
61E0 .WORD $FD0C
61E2 .BYTE $30
61E3 .BYTE $A4, $24, $B1, $28, $48, $29, $3F, 9, $40, $91, $28, $68, $6C, $38, 0, $E6
61F3 .BYTE $4E, $D0, 2, $E6, $4F, $2C, 0, $C0, $10, $F5, $91, $28, $AD, 0, $C0, $2C
6203 .BYTE $10, $C0, $60, $20, $C, $FD, $20, $2C, $FC, $20, $C, $FD, $C9, $9B, $F0, $F3
6213 .BYTE $60
6214 .WORD $FD64
6216 .BYTE $13
6217 .BYTE $20, $ED, $FD, $20, $8E, $FD, $A5, $33, $20, $ED, $FD, $A2, 1, $8A, $F0, $F3
6227 .BYTE $CA, $20, $35, $FD
622B .WORD $FDED
622D .BYTE $12
622E .BYTE $6C, $36, 0, $C9, $A0, $90, 2, $25, $32, $84, $35, $48, $20, $FD, $FB, $68
623E .BYTE $A4, $35, $60
6241 .WORD $FE84
6243 .BYTE $29
6244 .BYTE $A0, $FF, $84, $32, $60, $A9, 0, $85, $3E, $A2, $38, $A0, $1B, $D0, 8, $A9
6254 .BYTE 0, $85, $3E, $A2, $36, $A0, $F0, $A5, $3E, $29, $F, $F0, 6, 9, $C0, $A0
6264 .BYTE 0, $F0, 2, $A9, $FD, $94, 0, $95, 1, $60
626E .WORD $FF3A
6270 .BYTE 4
6271 .BYTE $A9, $87, $4C, $ED, $FD
6276 .WORD $FF59
6278 .BYTE $16
6279 .BYTE $20, $84, $FE, $20, $2F, $FB, $20, $93, $FE, $20, $89, $FE, $D8, $20, $3A, $FF
6289 .BYTE $A9, $AA, $85, $33, $20, $67, $FD
6290 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
;detect ROM version and check integrity
62A0 sub_62A0:
62A0 JSR sub_6380
;select appropriate table first
;$6000 if $FFFC-FFFD=$FA62, $6150 if $FFFC-FFFD=$FF59, else reboot
62A3 LDA $FFFD
62A6 LDX #$60
62A8 CPY #$62
62AA BNE loc_62B2
62AC EOR #$FA
62AE BNE loc_62F5
62B0 BEQ loc_62BD
62B2 loc_62B2:
62B2 CPY #$59
62B4 BNE loc_62F5
62B6 CMP #$FF
62B8 BNE loc_62F5
62BA INX
62BB LDA #$50
62BD loc_62BD:
62BD STA 0
62BF STX 1
;perform check of ROM code
;Choplifter does not work on some systems because of this
;we'll patch it so that it works everywhere
62C1 loc_62C1:
62C1 LDY #2
62C3 loc_62C3:
62C3 LDA (0),Y
62C5 STA 2,Y
62C8 DEY
62C9 BPL loc_62C3
62CB LDA #3
62CD CLC
62CE ADC 0
62D0 STA 0
62D2 BCC loc_62D6
62D4 INC 1
62D6 loc_62D6:
62D6 LDY 4
62D8 BEQ locret_62F3
62DA loc_62DA:
62DA LDA (0),Y
62DC CMP (2),Y
62DE BNE loc_62F4
62E0 DEY
62E1 BPL loc_62DA
62E3 LDY 4
62E5 INY
62E6 TYA
62E7 CLC
62E8 ADC 0
62EA STA 0
62EC BCC loc_62F0
62EE INC 1
62F0 loc_62F0:
62F0 CLC
62F1 BCC loc_62C1
62F3 locret_62F3:
62F3 RTS
62F4 loc_62F4:
62F4 NOP
62F5 loc_62F5:
62F5 LDX $13
62F7 LDA $C081,X
62FA JMP $32F
62FD .BYTE 0, 0, 0
;probe slots for cards
6300 loc_6300:
6300 JSR sub_6366
6303 LDY #0
6305 LDA #$F8
6307 STY $11
6309 STA $12
630B loc_630B:
630B LDA ($11),Y
630D STA ($11),Y
630F INY
6310 BNE loc_630B
6312 INC $12
6314 BNE loc_630B
6316 JSR sub_6366
;check for known ROM
6319 JSR sub_62A0
631C LDY #7
631E loc_631E:
631E STY $10
6320 JSR sub_633B
6323 BCC loc_6332
6325 STX $13
6327 LDA $C080,X
632A JSR sub_62A0
632D LDX $13
632F LDA $C081,X
6332 loc_6332:
6332 LDY $10
6334 DEY
6335 BPL loc_631E
6337 LDA $C080
633A RTS
633B sub_633B:
633B TYA
633C PHA
633D LDX #0
633F ORA #$C0
6341 STX $11
6343 STA $12
6345 ASL A
6346 ASL A
6347 ASL A
6348 ASL A
6349 TAX
634A BEQ loc_6362
634C LDY #0
634E loc_634E:
634E LDA ($11),Y
6350 STA $B000,Y
6353 INY
6354 BNE loc_634E
6356 loc_6356:
6356 LDA $B000,Y
6359 CMP ($11),Y
635B BNE loc_6362
635D INY
635E BNE loc_6356
6360 CLC
6361 .BYTE $24
6362 loc_6362:
6362 SEC
6363 PLA
6364 TAY
6365 RTS
;probe each slot looking for cards
6366 loc_6366:
6366 LDY #7
6368 loc_6368:
6368 JSR sub_633B
636B BCC loc_6373
636D LDA $C081,X
6370 LDA $C081,X
6373 loc_6373:
6373 DEY
6374 BPL loc_6368
6376 RTS
6377 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0
;apply version-specific patches
;select appropriate table first
;$63E0 if ROM "A" ($FBB5=#$78), else $63C0
6380 loc_6380:
6380 LDX #$C0
6382 LDA $FBB5
6385 EOR #$78
6387 BNE loc_638B
6389 LDX #$E0
638B loc_638B:
638B LDY #2
638D loc_638D:
638D LDA $6300,X
6390 STA 0,Y
6393 INX
6394 DEY
6395 BPL loc_638D
6397 LDY #0
6399 loc_6399:
6399 DEC 2
639B BMI loc_63A6
639D LDA $6300,X
63A0 STA (0),Y
63A2 INX
63A3 INY
63A4 BNE loc_6399
63A6 loc_63A6:
63A6 BIT 1
63A8 BPL loc_638B
63AA LDY $FFFC
63AD RTS
63AE .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
63BE .BYTE 0, 0
;format is count, big-endian address, data
;FF terminates the list
63C0 loc_63C0:
63C0 .BYTE 1
63C1 .BYTE $61, $18
63C3 .BYTE 0
63C4 .BYTE 6
63C5 .BYTE $60, $16
63C7 .BYTE $AD, $5D, $C0, $AD, $5F, $C0
63CD .BYTE 4
63CE .BYTE $60, $5F
63D0 .BYTE $A9, $28, $85, $21
63D4 .BYTE $FF
63D5 .BYTE $FF, $FF
63D7 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0
63E0 loc_63E0:
63E0 .BYTE 1
63E1 .BYTE $61, $18
63E3 .BYTE $B
63E4 .BYTE 6
63E5 .BYTE $60, $16
63E7 .BYTE $A0, 5, $20, $B4, $FB, $EA
63ED .BYTE 4
63EE .BYTE $60, $5F
63F0 .BYTE $A0, 8, $D0, $5F
63F4 .BYTE $FF
63F5 .BYTE $FF, $FF
63F7 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0
|
The return at $633A is using the stack pointer that comes from the checksum routine at $400.
We stored it at $6400 so that we can see it.
The value is #$FB, which is 1 less than the address that's fetched during RTS, so we read $1FC to see where to go next.
So return address is $565, which is also 1 less than the address that's used, so we continue from $566.
Here's the routine that was loaded to $500.
Code: |
0500 loc_500:
0500 LDA #$FF
0502 JSR loc_537
;read sector (DDF5D5)
0505 loc_505:
0505 CMP #$DD
0507 BNE loc_500
0509 JSR loc_539
050C loc_50C:
050C CMP #$F5
050E BNE loc_505
0510 JSR loc_539
0513 CMP #$D5
0515 BNE loc_50C
;tricky routine here
;reads 4-and-4 data twice for an address and once for a value
;writes value to address, can be anywhere in memory
;one of those writes replaces the jump at $544
0517 loc_517:
0517 JSR loc_530
051A STA loc_526+1
051D JSR loc_530
0520 STA loc_526+2
0523 JSR loc_530
0526 loc_526:
0526 STA $FFFF
0529 CMP #$EA
052B BNE loc_517
052D JMP loc_541
0530 loc_530:
0530 LDA $C08C,X
0533 BPL loc_530
0535 SEC
0536 ROL A
0537 loc_537:
0537 STA $FF
0539 loc_539:
0539 LDA $C08C,X
053C BPL loc_539
053E AND $FF
0540 RTS
0541 loc_541:
0541 LDA $C088,X
0544 JMP ($3F2)
0547 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0
;point reset and break vectors to $320
0550 loc_550:
0550 LDX #$20
0552 LDA #3
0554 JSR $3A3
0557 LDX $2B
0559 JMP loc_500
055C .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
;clear stack
0566 loc_566:
0566 LDX #0
0568 TXA
0569 loc_569:
0569 PHA
056A DEX
056B BNE loc_569
056D LDX #$FF
056F TXS
0570 JMP loc_550
0573 LDX $2B
0575 LDA $C089,X
0578 LDY #$80
057A loc_57A:
057A DEX
057B BNE loc_57A
057D DEY
057E BNE loc_57A
0580 LDA #$17
0582 JSR $772
0585 loc_585:
0585 LDA #8
0587 loc_587:
0587 PHA
0588 CLC
0589 LDA $F4
058B ADC #1
058D JSR $772
0590 LDX #4
0592 PLA
0593 PHA
0594 JSR $700
0597 PLA
0598 CLC
0599 ADC #4
059B loc_59B:
059B CMP #$20
059D BCC loc_587
059F BIT loc_59B+1
05A2 BMI loc_5B0
05A4 LDA #$40
05A6 STA loc_585+1
05A9 LDA #$C0
05AB STA loc_59B+1
05AE BMI loc_585
05B0 loc_5B0:
05B0 LDA #$40
05B2 JSR $772
05B5 LDX #1
05B7 LDA #6
05B9 JSR $700
05BC LDA #5
05BE PHA
05BF LDA #$FF
05C1 PHA
05C2 RTS
05C3 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
05D3 .BYTE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
Now we want to know where $544 points to after the read. We also have to disable the ROM check.
We'll write the result to $6000-6001 and then hang the system.
Code: |
8600<C600.C6FCM
86F8:A9 5 8D D 8 A9 87 8D E 8 4C 1 8
8705:A9 12 8D 38 2 A9 87 8D 39 2 4C F 2
8712:A2 0 BD 0 3 9D 0 63 E8 D0 F7 A9 63 8D A 3 A9 4C 8D 1F 3 A9 34 8D 20 3 A9 87 8D 21 3 4C 1 3
8734:9D 0 1 E8 F0 3 4C 8 3
873D:A9 4C 8D FB 1 A9 4F 8D FC 1 A9 87 8D FD 1 4C 31 1
874F:A9 AE 8D FB 1 A9 65 8D FC 1 A9 5 8D FD 1 A9 4C 8D 21 4 A9 80 8D 22 4 A9 87 8D 23 4 4C FB 1
8780:A9 0 8D DF 62 A9 92 8D 2E 5 A9 87 8D 2F 5 4C 0 63
8792:AD 45 5 8D 0 60 AD 46 5 8D 1 60 4C 9E 87
8600G
|
And here's our answer:
When did $4000 get loaded? At the same time as the title screen ($469). What's there?
Code: |
4000 LDX #$40
4002 TXS
4003 RTS
|
Ha! Oh, and some text, perhaps a bit of source code from memory.
Code: |
4004 .BYTE ':',$D
4006 .BYTE 2
4007 .BYTE ';',$D
4009 .BYTE $1E
400A .BYTE '; START LIKE NEW GAME, BUT NO',$D
4028 .BYTE $19
4029 .BYTE '; CONTR',0
|
Okay, back to our stack.
#$40 is 1 less than the address that's fetched during RTS, so we read $141 to see where to go next...
but wait, isn't the stack is empty from $566? No, it was written by $517. Here are all of the values:
Code: |
;address marker and tail comparison bytes for the first sector to read
0020:D5
0021:AA
0022:D4
0023:B5
;update addresses to use $20-23 instead of $F0-F3
0716:20
075B:20
0765:20
071D:21
0756:21
0724:22
0751:22
0744:23
0760:23
;change jump
0544:4C
0545:00
0546:40
;disable read for second round
0570:E8
0571:E8
0572:E8
;set new return address
0141:65
0142:05
;end
0000:EA
|
So...
So return address is $565, which is also 1 less than the address that's used, so we continue from $566.
Since $544-546 were altered, we reach $573 this time.
Code: |
;turn on the drive and wait
0573 LDX $2B
0575 LDA $C089,X
0578 LDY #$80
057A loc_57A:
057A DEX
057B BNE loc_57A
057D DEY
057E BNE loc_57A
;seek track 11.5
0580 LDA #$17
0582 JSR $772
;prepare to read to $800-1FFF
0585 loc_585:
0585 LDA #8
0587 loc_587:
0587 PHA
0588 CLC
;seek to next half track
0589 LDA $F4
058B ADC #1
058D JSR $772
;read 4 sectors
0590 LDX #4
0592 PLA
0593 PHA
0594 JSR $700
0597 PLA
0598 CLC
0599 ADC #4
059B loc_59B:
059B CMP #$20
059D BCC loc_587
059F BIT loc_59B+1
05A2 BMI loc_5B0
;then $4000-BFFF
05A4 LDA #$40
05A6 STA loc_585+1
05A9 LDA #$C0
05AB STA loc_59B+1
05AE BMI loc_585
;seek to track 32
05B0 loc_5B0:
05B0 LDA #$40
05B2 JSR $772
;read 1 sector to $600-6FF
05B5 LDX #1
05B7 LDA #6
05B9 JSR $700
;jmp to $600
05BC LDA #5
05BE PHA
05BF LDA #$FF
05C1 PHA
05C2 RTS
|
Almost done! We want to know what was read to $600 by $5B5, so we will redirect $5B7 to $6600 and then hang the system again.
Code: |
8600<C600.C6FCM
86F8:A9 5 8D D 8 A9 87 8D E 8 4C 1 8
8705:A9 12 8D 38 2 A9 87 8D 39 2 4C F 2
8712:A2 0 BD 0 3 9D 0 63 E8 D0 F7 A9 63 8D A 3 A9 4C 8D 1F 3 A9 34 8D 20 3 A9 87 8D 21 3 4C 1 3
8734:9D 0 1 E8 F0 3 4C 8 3
873D:A9 4C 8D FB 1 A9 4F 8D FC 1 A9 87 8D FD 1 4C 31 1
874F:A9 AE 8D FB 1 A9 65 8D FC 1 A9 5 8D FD 1 A9 4C 8D 21 4 A9 80 8D 22 4 A9 87 8D 23 4 4C FB 1
8780:A9 0 8D DF 62 A9 92 8D 2E 5 A9 87 8D 2F 5 4C 0 63
8792:A9 66 8D B8 5 A9 BB 8D C0 5 4C 41 5
8600G
|
Unfortunately, at this point, my disk does not read properly, and I was unable to find a working .nib image.
If someone has a working .nib image to share, I would be happy to finish this work.
However, at this point, we have the entire game in memory.
The code at $600 sets some zero-page entries and then jumps to $800, which is the main game code. |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 22 Aoû 2012, 0:40 Sujet du message: boot-tracing Choplifter |
|
|
So that's it for the boot-trace. In the 80s, such a thing would have taken me several days. Now I can do it in several hours. ;-)
Next time: cracking the protection using a minimal number of changes. |
|
Revenir en haut de page |
|
 |
toinet Site Admin
Inscrit le: 15 Juin 2007 Messages: 3074 Localisation: Le Chesnay, France
|
Posté le: Lun 27 Aoû 2012, 21:59 Sujet du message: |
|
|
Wouah! Congratulations, Sir! Your explanations rock, I am flabbergasted
If only I had a NIB generator...
antoine |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Lun 22 Oct 2012, 21:58 Sujet du message: |
|
|
So, now we know how to capture the image in memory. We have two options.
One is to modify the disk to make it workable. The other is to save it to a file.
Let's look at the disk modification version first.
The first change that we make is to copy ourselves to $BC00 as well as $200.
The reason will be clear later.
We go from this:
Code: | 0800 .BYTE 1
0801 LDX #0
0803 loc_803:
0803 LDA $800,X
0806 STA $200,X
0809 INX
080A BNE loc_803
080C JMP $20F |
to this:
Code: | 0800 byte_800: .BYTE 1
0801 LDX #0
0803 loc_803:
0803 LDA byte_800,X
0806 STA $200,X
0809 STA $BC00,X
080C INX
080D BNE loc_803
080F JMP $212 |
Now we want to copy the 6-and-2 nibblisation table that the boot PROM created, so that we don't have to do it ourselves.
We go from this:
Code: | 080F LDY #$AB
...
081D BEQ loc_824
081F TXA
0820 STA $800,Y
...
0825 BNE loc_811
0827 STY $3D
0829 STY $26
...
0834 JSR $2D1 |
to this:
Code: | 0212 LDY #$95
...
0220 LDA byte_2D6,Y
0223 STA $BAD6,Y
...
0228 BNE loc_220
022A NOP
0234 LDA byte_2D1 |
Just a few bytes, no additional space needed.
We don't have any post-nibble routine at $2D1 anymore, so the JSR was changed to a LDA.
All we needed was to replace the read routine at $25D. There are some spare bytes at $240-25C which we use, too.
The code is the regular boot PROM code, restructured so that the addresses match, and to take care of some special cases.
Of particular interest is that when the previous read was #$BA, the next reads will overwrite our critical data (nibblisation table and read routine), so we have to relocate them first, and redirect the calls to the new location.
That's the check at $25D.
Code: | 0240 loc_240:
0240 LDY #3
0242 STY $2A
0244 loc_244:
0244 LDA $C08C,X
0247 BPL loc_244
0249 ROL A
024A STA $3C
024C loc_24C:
024C LDA $C08C,X
024F BPL loc_24C
0251 AND $3C
0253 DEY
0254 BNE loc_244
0256 PLP
0257 CMP $3D
0259 BNE loc_289
025B BCS loc_28A
025D loc_25D:
025D CMP #$BA
025F BNE loc_289
0261 loc_261:
0261 LDA $BB00,Y
0264 STA $200,Y
0267 LDA $BC00,Y
026A STA $400,Y
026D INY
026E BNE loc_261
0270 INY
0271 STY byte_4BB
0274 STY byte_4CD
0277 STY byte_4DC
027A INY
027B STY byte_4C1
027E STY byte_4E9
0281 STY byte_4ED
0284 LDY #4
0286 STY byte_73D
0289 loc_289:
0289 CLC
028A loc_28A:
028A PHP
028B loc_28B:
028B LDA $C08C,X
028E BPL loc_28B
0290 loc_290:
0290 EOR #$D5
0292 BNE loc_28B
0294 loc_294:
0294 LDA $C08C,X
0297 BPL loc_294
0299 CMP #$AA
029B BNE loc_290
029D NOP
029E loc_29E:
029E LDA $C08C,X
02A1 BPL loc_29E
02A3 CMP #$96
02A5 BEQ loc_240
02A7 PLP
02A8 BCC loc_289
02AA EOR #$AD
02AC BNE loc_289
02AE LDA #0
02B0 LDY #$56
02B2 loc_2B2:
02B2 STY $3C
02B4 loc_2B4:
02B4 LDY $C08C,X
02B7 BPL loc_2B4
02B9 EOR $BAD6,Y
02BC LDY $3C
02BE DEY
02BF STA $BB00,Y
02C2 BNE loc_2B2
02C4 loc_2C4:
02C4 STY $3C
02C6 loc_2C6:
02C6 LDY $C08C,X
02C9 BPL loc_2C6
02CB EOR $BAD6,Y
02CE LDY $3C
02D0 loc_2D0:
02D0 STA ($26),Y
02D2 INY
02D3 BNE loc_2C4
02D5 loc_2D5:
02D5 LDY $C08C,X
02D8 BPL loc_2D5
02DA EOR $BAD6,Y
02DD BNE loc_28B
02DF TAY
02E0 loc_2E0:
02E0 LDX #$56
02E2 loc_2E2:
02E2 DEX
02E3 BMI loc_2E0
02E5 LDA ($26),Y
02E7 LSR $BB00,X
02EA ROL A
02EB LSR $BB00,X
02EE ROL A
02EF STA ($26),Y
02F1 INY
02F2 BNE loc_2E2
02F4 LDY #$99
02F6 LDX $2B
02F8 RTS |
|
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Lun 22 Oct 2012, 22:00 Sujet du message: |
|
|
Now we move to the stack code.
We make a minor change for compatibility with our read routine, and disable the header checks because we will read the whole sector ourselves, from this:
Code: | 0194 STY $F7
0196 STX $F8
...
01A1 BNE loc_19C
...
01A8 BNE loc_19F
...
01AF BNE loc_1A6
...
01C3 BNE loc_1B1
01C5 ASL $C000
...
01CF BNE loc_190
01D1 INC $F8
...
01EE CMP $F0
01F0 BEQ loc_1FB |
to this:
Code: | 0194 STY $27
0196 STX $3D
...
01A1 BNE loc_1A3
...
01A8 BNE loc_1AA
...
01AF BNE loc_1B1
...
01C3 BNE loc_1C5
01C5 JSR $025D ;read whole sector
...
01CF INC $27
01D1 INC $3D
...
01EE CMP $F0
01F0 BVC loc_1FB ;always taken |
We are lucky with the code at $400, because we're aligned to a whole track, so the seeks and reads didn't need to change.
All we needed is to fix the stack checksum, from this:
Code: | 041A LDA $48
041C EOR $195 |
to this:
Code: | 041A LDA #$DC
041C EOR $195 |
The current value at $48 can't be used with the value at $195, because neither of them can produce the required value.
There also isn't any value in the $1xx range that can be used instead, so we hardcode the required value (#$DC).
This bring us to the code at $6000, which we patch to allow all ROMs. This was described already.
Then we jump back to the code at $573, which needs a few changes.
Here's the routine for reading the game code.
Code: | ;seek track 11.5
0580 LDA #$17
...
058B ADC #1 ;the comment should have been "seek by half track"
...
;read 4 sectors
0590 LDX #4
...
0599 ADC #4
...
059F BIT loc_59B+1
05A2 BMI loc_5B0
;then $4000-BFFF
05A4 LDA #$40
05A6 STA loc_585+1
05A9 LDA #$C0
05AB STA loc_59B+1
05AE BMI loc_585 |
which we change like this:
Code: | ;seek track 11
0580 LDA #$16
...
058B ADC #2 ;seek by whole track
...
;read 8 sectors at a time, otherwise we run out of disk
0590 LDX #8
...
0599 ADC #8
...
;double next read address and size
;very conveniently same size code
059F PHA
05A0 ASL $0591
05A3 ASL $059A
05A6 LDA #$C0
05A8 STA loc_59B+1
05AB PLA
05AC ASL
05AD BPL loc_587 |
And here's the new code at $600. I never did find a working .nib.
Code: | 0600 LDA #$9B
0602 STA $4E
0604 LDA #$30
0606 STA $FC
0608 LDA #6
060A STA $FD
060C LDY #0
060E STY $FF
0610 LDX #$B
0612 STX $4F
0614 LDA #$20
0616 JSR sub_625
0619 LDX #3
061B LDA #$2E
061D JSR sub_625
0620 LDA #$31
0622 JMP ($20)
0625 sub_625:
0625 STA $FE
0627 loc_627:
0627 LDA ($FC),Y
0629 STA ($FE),Y
062B INY
062C DEX
062D BPL loc_627
062F RTS
0630 .BYTE 0, 8, 0, 0, $92, $C, 0, 0, $1F, 8, $C7, 9
063C .BYTE $5F, $B, $13, $C |
All done. We have a working disk.
Next time, we'll look at converting to a file. |
|
Revenir en haut de page |
|
 |
toinet Site Admin
Inscrit le: 15 Juin 2007 Messages: 3074 Localisation: Le Chesnay, France
|
Posté le: Mar 23 Oct 2012, 3:07 Sujet du message: |
|
|
Very good.
Now that "i'm fEDD up" is available and as I own the original disk of Choplifter, I can try to generate to NIB of it.
Which parameters do you want me to set to make a NIB image: number of tracks, step?
Antoine |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 24 Oct 2012, 20:47 Sujet du message: |
|
|
First thing is for you to test if your disk actually works. :-)
This tutorial was created using the .nib that you sent to me at the start of the year. |
|
Revenir en haut de page |
|
 |
toinet Site Admin
Inscrit le: 15 Juin 2007 Messages: 3074 Localisation: Le Chesnay, France
|
Posté le: Jeu 25 Oct 2012, 6:30 Sujet du message: |
|
|
qkumba a écrit: | First thing is for you to test if your disk actually works.
This tutorial was created using the .nib that you sent to me at the start of the year. |
oops, bad memory. sorry.
I'll have to test on a 8-bit machine. the program does not load on my IIgs
av |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Jeu 25 Oct 2012, 7:55 Sujet du message: |
|
|
toinet a écrit: | the program does not load on my IIgs |
right, it's the ROM check that's failing.
you could try using the tutorial to boot-trace the code and disable the ROM check, then you can let it run and see what happens. |
|
Revenir en haut de page |
|
 |
qkumba
Inscrit le: 29 Jan 2012 Messages: 176
|
Posté le: Mer 07 Nov 2012, 4:19 Sujet du message: |
|
|
so we know how to create a working disk, but how about a file?
Choplifter is quite a big game ($800-$bfff), which leaves no room for DOS.
Even if we could save such a large file, we could not load it back into memory.
So we need to compress it, but first, we should look a bit closer at the memory map...
Yes, $4000-$5fff is identical to $2000-$3fff. We just saved ourselves 8kb instantly.
This is great, because we can compress in two stages and we don't need to use special memory regions like the stack page or high memory (particularly, the crack should not require more memory than the original).
We can actually compress in one part, but the experience is not good in that case - the title screen would take a long time until it showed, and that's not how the original version behaved.
If we compress in two parts and decompress the low memory part first (and backwards) then the title screen is the first thing to appear, and the user won't mind waiting quite as much. :-)
So, we compress $6000-$bfff first, and then $800-$3fff, and place all that data starting at $4000. The compressed low memory is unpacked first, and that leaves plenty of room for the other region to unpack.
Finally, we jump to $fe2c to copy $2000-$3fff to $4000-$5fff and everything works.
I used aPLib for the purpose, the result is an 18kb file, about half the size of every other version that I have seen (the others use RLE compression). |
|
Revenir en haut de page |
|
 |
|
|
Vous ne pouvez pas poster de nouveaux sujets dans ce forum Vous ne pouvez pas répondre aux sujets dans ce forum Vous ne pouvez pas éditer vos messages dans ce forum Vous ne pouvez pas supprimer vos messages dans ce forum Vous ne pouvez pas voter dans les sondages de ce forum
|
|