toinet
Inscrit le: 15 Juin 2007 Messages: 326 Localisation: Paris, France
|
Post� le: Mer 15 Ao� 2007, 21:36 Sujet du message: Leather goddesses of Phobos (Infocom, 1992) |
|
|
A sex-oriented text adventure by Infocom. You'd better speak and understand English (and slang English) very well otherwise you'll get lost quite rapidly. At the time of HackerForce, the game has been distributed with its on-disk protection!
PROTECTION TYPE
On a double-sided diskette, you have:
- a standard 16 sectors disk on side 1
- a special 18 sectors disk on side 2!
BOOT TRACE
As usual:
- 9600<C600.C6FFM
- 96FB: AD E8 C0 60
- 9600G
Bing! The code at $0801 loads next sector at $0900 and then loads the other sectors from $D000 to $DFFF. It ends with a JSR $D505 and JMP $DD48.
We would like to get the data loaded somewhere else:
- 96FB: A9 10 8D 0C 08 A9 4C 8D 3F 08 A9 59 8D 40 08 A9 FF 8D 41 08 4C 01 08
- 9600G
Bing! We now have the next boot process beginning at $1000
The interleaving is set as the following table:
- 00 04 08 0C 01 05 09 0D 02 06 0A 0E 03 07 0B 0F
THE RWTS ADDRESSES
After a code disassembly, we get:
- $D000: RWTS main entry
- $D010: nibblize 256 bytes to 342 6*2 coded nibbles
- $D03A: write sector
- $D0D2: denibblize 342 6*2 coded nibbles to X bytes (X is set at $0D)
- $D154: read header marker and data (volume/track/sector)
- $D1B0: move arm
- $D2E0: the RWTS management routine
- ...: other routines
- $D486: read a 18-sector from disk
- $D505: some inits
- $D51D: translation routine
==> Given two parameters (maybe a page index), the routines translates the values into a track and sector value depending on the side (side 1 = 16 sectors per track, side 2 = 18 sectors per track)
- $D871: please insert side 1 of the disk routine
==> $EB = 1
- $D8A1: please insert side 2 of the disk routine
==> $EB = 2
- $DD48: the main program loop
That is a huge RWTS, using lots of parameters in the zero page. What was fun to discover is the ADC #$12 or SBC #$12 making me understand that the second side was a 18-sector one. There have been several games using that kind of protection: Flight Simulator 2, Wings of Fury, Prince of Persia and maybe others. It appears that the disk scheme was sometimes different. Let's see ours.
A 18-SECTOR TRACK
A track from that game is seen as a header followed by a set of 18 * 343 (342 + 1 as a checksum) nibbles with no separators between each of them.
We therefore have:
- D5 AA AD: the header marker
- XX XX: two nibbles coding the track number
- ...: 343 nibbles (342 +1 as a checksum) for sector 0
- ...: 343 nibbles (342 +1 as a checksum) for sector 1
- and so on...
THE RWTS COMMANDS
As it is a non-standard disk nor crack, I will detail the different commands of the RWTS:
The format of the call is:
Code: |
LDA #command
JSR $D000
|
Where command can be:
$0xxxxxx0 ($00, $02, usw) : read a 16 sector
$0xxxxxx1 ($01, $03, usw) : write a 16 sector
$1xxxxxx0 ($80, $82, etc.) : read a 18 sector
THE RWTS SOURCE CODE
Please find hereafter the long commented source code of the RWTS. Please note that the original code begins at label RWTS, the first part is my copy code (read the 18-sector disk and move to the IIgs memory)
The RWTS source code is not complete, it is available upon request
Code: |
*
* LGOP
*
ORG $001000
MX %11
LST OFF
*
* CONSTANTS
*
SLOT = $60
DRIVE_1 = $01
DRIVE_2 = $02
NB_SECTOR = $12
NB_TRACK = $23
CMD_READ16 = $00
CMD_WRITE = $01
CMD_READ18 = $80
L0A00 = $0A00
L0B00 = $0B00
L0C00 = $0C00
RDMAINRAM EQU $C002
WRMAINRAM EQU $C004
TXTSET EQU $C051
MIXCLR EQU $C052
TXTPAGE1 EQU $C054
*
* ZERO PAGE ADDRESSES
*
RWTS_SLOT = $00
RWTS_OSLOT = $01 ; O MEANS PREVIOUS
RWTS_DRIVE = $02
RWTS_ODRIVE = $03
RWTS_SECTOR = $04
RWTS_TRACK = $05
RWTS_ERROR = $06
RWTS_COMMAND = $07 ; BIT7=1 > 18 SECTOR
PTR_BUFFER = $08
RWTS_0A = $0A
RWTS_PHASESLOT = $0B
RWTS_PHASE = $0C
RWTS_INDEX = $0D
RWTS_OSECTOR = $10
RWTS_OTRACK = $11
RWTS_DELAY1 = $13
RWTS_DELAY2 = $14
RWTS_SECTOR18 = $1A
*
* INIT
*
MY_TRACK = $FE
MY_SECTOR = $FF
LDA #DRIVE_1
STA RWTS_DRIVE
STA RWTS_ODRIVE
LDA #SLOT
STA RWTS_SLOT
STA RWTS_OSLOT
LDA #-1
STA RWTS_0A
LDA #>L0A00
STA PTR_BUFFER+1
LDA #<L0A00
STA PTR_BUFFER
LDA #0
STA MOVE1+2
LDA #$10
STA MOVE1+3
LDA #0
STA MY_TRACK
LDA #0
STA MY_SECTOR
]LP LDA MY_TRACK
STA RWTS_TRACK
LDA MY_SECTOR
STA RWTS_SECTOR
LDA RWTS_TRACK
JSR $FDDA
LDA #"/"
JSR $FDED
LDA RWTS_SECTOR
JSR $FDDA
LDA #"/"
JSR $FDED
LDA #CMD_READ18
JSR RWTS
LDA RWTS_ERROR
JSR $FDDA
LDA #"/"
JSR $FDED
LDA #$8D
JSR $FDED
JSR MOVETOMEMORY
INC MY_SECTOR
LDA MY_SECTOR
CMP #NB_SECTOR
BNE ]LP
LDA #0
STA MY_SECTOR
INC MY_TRACK
LDA MY_TRACK
CMP #NB_TRACK
BNE ]LP
RTS
*
*
*
MOVETOMEMORY LDY #0
TYX
]LP LDA (PTR_BUFFER),Y
MOVE1 STAL $100000,X
INY
INX
BNE ]LP
INC MOVE1+2
BNE MOVE2
INC MOVE1+3
MOVE2 RTS
DS \
*
* RWTS ENTRY
*
RWTS NOP
NOP
NOP
PHP
SEI
JSR RWTS_ENTRY
BCS LD00D
PLP
CLC
RTS
LD00D PLP
SEC
RTS
*
* NIBBLIZE
*
NIBBLIZE LDX #$00
LDY #$02
LD014 DEY
LDA (PTR_BUFFER),Y
LSR
ROL L0C00,X
LSR
ROL L0C00,X
STA L0B00,Y
INX
CPX #$56
BCC LD014
LDX #$00
TYA
BNE LD014
LDX #$55
LD02E LDA L0C00,X
AND #$3F
STA L0C00,X
DEX
BPL LD02E
RTS
*
* WRITE A SECTOR
*
WRITE_SECTOR STX $0E
STX LD474
SEC
LDA $C08D,X
LDA $C08E,X
BMI LD0C4
LDA L0C00
STA RWTS_INDEX
LDA #$FF
STA $C08F,X
ORA $C08C,X
PHA
PLA
NOP
LDY #$04
LD05A PHA
PLA
JSR LD0C9
DEY
BNE LD05A
LDA #$D5
JSR LD0C8
LDA #$AA
JSR LD0C8
LDA #$AD
JSR LD0C8
TYA
LDY #$56
BNE LD079
LD076 LDA L0C00,Y
LD079 EOR $0BFF,Y
TAX
LDA LD236,X
LDX $0E
STA $C08D,X
LDA $C08C,X
DEY
BNE LD076
LDA RWTS_INDEX
NOP
LD08E EOR L0B00,Y
TAX
LDA LD236,X
LDX LD474
STA $C08D,X
LDA $C08C,X
LDA L0B00,Y
INY
BNE LD08E
TAX
LDA LD236,X
LDX $0E
JSR LD0CB
LDA #$DE
JSR LD0C8
LDA #$AA
JSR LD0C8
LDA #$EB
JSR LD0C8
LDA #$FF
JSR LD0C8
LDA $C08E,X
LD0C4 LDA $C08C,X
RTS
LD0C8 CLC
LD0C9 PHA
PLA
LD0CB STA $C08D,X
ORA $C08C,X
RTS
*
* DENIBBLIZE
*
DENIBBLIZE LDY #$00
LD0D4 LDX #$56
LD0D6 DEX
BMI LD0D4
LDA L0B00,Y
LSR L0C00,X
ROL
LSR L0C00,X
ROL
STA (PTR_BUFFER),Y
INY
CPY RWTS_INDEX
BNE LD0D6
RTS
*
* READ SECTOR
*
READ_SECTOR LDY #$20
LD0EE DEY
BEQ LD152
LD0F1 LDA $C08C,X
BPL LD0F1
LD0F6 EOR #$D5
BNE LD0EE
NOP
LD0FB LDA $C08C,X
BPL LD0FB
CMP #$AA
BNE LD0F6
LDY #$56
LD106 LDA $C08C,X
BPL LD106
CMP #$AD
BNE LD0F6
LDA #$00
LD111 DEY
STY RWTS_INDEX
LD114 LDY $C08C,X
BPL LD114
EOR LD1E0,Y
LDY RWTS_INDEX
STA L0C00,Y
BNE LD111
LD123 STY RWTS_INDEX
LD125 LDY $C08C,X
BPL LD125
EOR LD1E0,Y
LDY RWTS_INDEX
STA L0B00,Y
INY
BNE LD123
LD135 LDY $C08C,X
BPL LD135
CMP LD1E0,Y
BNE LD152
LD13F LDA $C08C,X
BPL LD13F
CMP #$DE
BNE LD152
NOP
LD149 LDA $C08C,X
BPL LD149
CMP #$AA
BEQ LD1AE
LD152 SEC
RTS
*
* READ HEADER
*
READ_HEADER LDY #$FC
STY RWTS_INDEX
LD158 INY
BNE LD15F
INC RWTS_INDEX
BEQ LD152
LD15F LDA $C08C,X
BPL LD15F
LD164 CMP #$D5
BNE LD158
NOP
LD169 LDA $C08C,X
BPL LD169
CMP #$AA
BNE LD164
LDY #$03
LD174 LDA $C08C,X
BPL LD174
CMP #$96
BNE LD164
LDA #$00
LD17F STA $0E
LD181 LDA $C08C,X
BPL LD181
ROL
STA RWTS_INDEX
LD189 LDA $C08C,X
BPL LD189
AND RWTS_INDEX
STA |$000F,Y
EOR $0E
DEY
BPL LD17F
TAY
BNE LD152
LD19B LDA $C08C,X
BPL LD19B
CMP #$DE
BNE LD152
NOP
LD1A5 LDA $C08C,X
BPL LD1A5
CMP #$AA
BNE LD152
LD1AE CLC
RTS
*
* MOVE ARM
*
MOVE_ARM STX RWTS_PHASESLOT
STA RWTS_PHASE
CMP LD461
BEQ LD20C
LDA #$00
STA RWTS_INDEX
LD1BD LDA LD461
STA $0E
SEC
SBC RWTS_PHASE
BEQ LD1FA
BCS LD1D0
EOR #$FF
INC LD461
BCC LD1D5
LD1D0 ADC #$FE
DEC LD461
LD1D5 CMP RWTS_INDEX
BCC LD1DB
LDA RWTS_INDEX
LD1DB CMP #$0C
BCS LD1E0
TAY
LD1E0 SEC
JSR LD1FE
LDA LD21E,Y
JSR WAIT
LDA $0E
CLC
JSR LD201
LDA LD22A,Y
JSR WAIT
INC RWTS_INDEX
BNE LD1BD
LD1FA JSR WAIT
CLC
LD1FE LDA LD461
LD201 AND #$03
ROL
ORA RWTS_PHASESLOT
TAX
LDA $C080,X
LDX RWTS_PHASESLOT
LD20C RTS
*
* WAIT
*
WAIT LDX #$11
LD20F DEX
BNE LD20F
INC RWTS_DELAY1
BNE LD218
INC RWTS_DELAY2
LD218 SEC
SBC #$01
BNE WAIT
RTS
LD21E HEX 01302824201E1D1C1C1C1C1C
LD22A HEX 702C26221F1E1D1C1C1C1C1C
LD236 HEX 96979A9B9D9E9FA6A7ABACADAEAFB2B3
HEX B4B5B6B7B9BABBBCBDBEBFCBCDCECFD3
HEX D6D7D9DADBDCDDDEDFE5E6E7E9EAEBEC
HEX EDEEEFF2F3F4F5F6F7F9FAFBFCFDFEFF
LD276 HEX 0001989902039C040506A0A1A2A3A4A5
HEX 0708A8A9AA090A0B0C0DB0B10E0F1011
HEX 1213B81415161718191AC0C1C2C3C4C5
HEX C6C7C8C9CA1BCC1C1D1ED0D1D21FD4D5
HEX 2021D822232425262728E0E1E2E3E429
HEX 2A2BE82C2D2E2F303132F0F133343536
HEX 3738F8393A3B3C3D3E3F
*
* RWTS MAIN ENTRY POINT
*
RWTS_ENTRY STA RWTS_COMMAND
LDA #$02
STA LD475
ASL
STA LD471
*
* SLOT COMPARISON
*
LDX RWTS_SLOT
CPX RWTS_OSLOT
BEQ LD307
LDX RWTS_OSLOT
LDA $C08E,X
LD2F6 LDY #$08
LDA $C08C,X
LD2FB CMP $C08C,X
BNE LD2F6
DEY
BNE LD2FB
LDX RWTS_SLOT
STX RWTS_OSLOT
*
* READ MODE
*
LD307 LDA $C08E,X
LDA $C08C,X
LDY #$08
LD30F LDA $C08C,X
PHA
PLA
PHA
PLA
STX INTERN_SLOT
CMP $C08C,X
BNE LD321
DEY
BNE LD30F
*
* TURN DRIVE ON
*
LD321 PHP
LDA $C089,X
LDA #$D8 ; WAIT DELAY
STA RWTS_DELAY2
*
* SET DRIVE
*
LDA RWTS_DRIVE
CMP RWTS_ODRIVE
BEQ LD335
STA RWTS_ODRIVE
PLP
LDY #$00
PHP
LD335 ROR
BCC LD33D
LDA $C08A,X ; DRIVE 1
BCS LD340
LD33D LDA $C08B,X ; DRIVE 2
LD340 ROR RWTS_0A
PLP
PHP
BNE LD351
LDY #$07
LD348 JSR WAIT
DEY
BNE LD348
LDX INTERN_SLOT
*
* MOVE TO THE RIGHT TRACK
*
LD351 LDA RWTS_TRACK
JSR LD417
PLP
BNE LD36A
LDY RWTS_DELAY2
BPL LD36A
LD35D LDY #$12
LD35F DEY
BNE LD35F
INC RWTS_DELAY1
BNE LD35D
INC RWTS_DELAY2
BNE LD35D
*
* READ/WRITE
* READ : x0000000
* BIT 7=1 => 18 SECTOR
* BIT 7=0 => 16 SECTOR
* WRITE : 00000001
*
LD36A LDA RWTS_COMMAND
ROR
PHP
BCC LD373
JSR NIBBLIZE ; BIT0=1
*
LD373 LDA #$30 ; NB OF READ TRIES
STA INTERN_TRIES
LD378 LDX INTERN_SLOT
LDA RWTS_COMMAND
BPL LD388
STA RWTS_SECTOR18 ; BIT7=1
JSR READ18_SECTOR
BCC LD3B1
BCS LD38D
*
LD388 JSR READ_HEADER ; WE ARE ON A STD DISK
BCC LD3B1
LD38D DEC INTERN_TRIES
BPL LD378
LD392 LDA LD461
PHA
LDA #$60
JSR LD449
DEC LD475
BEQ LD3C8
LDA #$04
STA LD471
LDA #$00
JSR LD417
PLA
LD3AB JSR LD417
JMP LD373
*
*
*
LD3B1 LDY RWTS_OTRACK
CPY LD461
BEQ LD3CF
LDA LD461
PHA
TYA
JSR LD449
PLA
DEC LD471
BNE LD3AB
BEQ LD392
LD3C8 PLA
LDA #$40
PLP
JMP LD407
*
* ARE WE ON THE RIGHT SECTOR ?
*
LD3CF LDA RWTS_COMMAND
BMI LD3DC
LDY RWTS_SECTOR ; BIT7=0
LDA RWTS_INTER,Y
CMP RWTS_OSECTOR
BNE LD38D
LD3DC PLP
BCS LD40E ; GOTO WRITE SECTOR
* READ A SECTOR
LDA RWTS_COMMAND
BPL LD3EF
LDY RWTS_SECTOR ; BIT7=1
STY RWTS_SECTOR18
JSR READ18_SECTOR
BCC LD3F2
SEC
BCS LD3F2
LD3EF JSR READ_SECTOR
LD3F2 BCC LD3F8
CLC
PHP
BCC LD38D
*
* SECTOR HAS BEEN READ, DENIBBLIZE
*
LD3F8 LDX #$00
STX RWTS_INDEX
JSR DENIBBLIZE
LDX INTERN_SLOT
LD402 LDA #$00
CLC
BCC LD408
LD407 SEC
LD408 STA RWTS_ERROR
LDA $C088,X
RTS
*
*
*
LD40E JSR WRITE_SECTOR
BCC LD402
LDA #$10
BNE LD407
*
* PREPARE ARM MOVEMENT
*
LD417 ASL ; A=TRACK
JSR LD41F
LSR LD461
RTS
LD41F STA RWTS_PHASE
JSR LD442
LDA LD461,Y
BIT RWTS_0A
BMI LD42E
LDA LD469,Y
LD42E STA LD461
LDA RWTS_PHASE
BIT RWTS_0A
BMI LD43C
STA LD469,Y
BPL LD43F
LD43C STA LD461,Y
LD43F JMP MOVE_ARM
LD442 TXA ; X=60
LSR
LSR
LSR
LSR
TAY ; Y=6
RTS
LD449 PHA
LDA RWTS_DRIVE
ROR
ROR RWTS_0A
JSR LD442
PLA
ASL
BIT RWTS_0A
BMI LD45D
STA LD469,Y
BPL LD460
LD45D STA LD461,Y
LD460 RTS
LD461 DS $08
LD469 DS $08
LD471 DB $00
INTERN_TRIES DB $00
INTERN_SLOT DB $00
LD474 DB $00
LD475 DB $00
*
* SECTOR INTERLEAVING
*
RWTS_INTER HEX 0004080C0105090D02060A0E03070B0F
*
* READ 18 SECTOR
*
READ18_SECTOR LDA #$20
STA $15
TAY
LD48B LDA #$84
DEC $15
BEQ LD501
LD491 DEY
BEQ LD491
NOP
NOP
LDA $C08C,X
BPL LD491
CMP #$D5
BNE LD491
LD49F LDA $C08C,X
BPL LD49F
CMP #$AA
BNE LD48B
LD4A8 LDA $C08C,X
BPL LD4A8
CMP #$AD
BNE LD48B
SEC
LD4B2 LDA $C08C,X
BPL LD4B2
ROL
STA $17
LD4BA LDA $C08C,X
BPL LD4BA
AND $17
STA RWTS_OTRACK
LDA RWTS_SECTOR18
BMI LD4FD
LD4C7 LDY #$56
LDA #$00
LD4CB DEY
STY RWTS_INDEX
LD4CE LDY $C08C,X
BPL LD4CE
EOR LD1E0,Y
LDY RWTS_INDEX
STA L0C00,Y
BNE LD4CB
LD4DD STY RWTS_INDEX
LD4DF LDY $C08C,X
BPL LD4DF
EOR LD1E0,Y
LDY RWTS_INDEX
STA L0B00,Y
INY
BNE LD4DD
LD4EF LDY $C08C,X
BPL LD4EF
DEC RWTS_SECTOR18
BPL LD4C7 ; NEXT SECTOR PLEASE
CMP LD1E0,Y ; CHECKSUM
BNE LD4FF
LD4FD CLC
RTS
LD4FF LDA #$85
LD501 STA RWTS_ERROR
SEC
RTS
|
DISK COPY
- Copy the front side with Locksmith
- Use my code in the previous section to copy the back side
- Then use a DOS 3.3 RWTS to write back the data on a 16-sector disk
We would have been obliged to rewrite the RWTS if the game would have used the entire $23 tracks of it. We are lucky people, we find data on the first $F tracks of our 16-sector copy
REMOVE THE PROTECTION
We have to tell our beloved goddesses that the back side is no longer a 18-sector ($12) track but a 16-sector ($10) one.
Oops! I believe I am a really lucky guy:
- Remember the RWTS ADDRESSES section and the details about the ADC/SBC #$12?
=> Why not find and replace $12 with $10
- Remember the RWTS commands with bit 7 = 1 if it is to read a 18-sector data?
=> Why not mask bit 7?
- Please launch your favorite disk editor
- Set the interleaving to the one described (so) many lines above
- On T0/S7/48: 12 => 10
- On T0/S7/55: 12 => 10
- On T0/S2/00: EA EA => 29 7F (I am a lucky guy, thanks for the NOPs)
Reboot... Insert side 2... Enjoy your backup copy...
Toinet
Thanks to Deckard, try the $verify command at the prompt. It shall answer 'Okay.' if your disks can be read entirely. |
|