💾 Archived View for mirrors.apple2.org.za › active › 4am › images › games › action › arkanoid › Arka… captured on 2024-08-19 at 03:41:15.

View Raw

More Information

-=-=-=-=-=-=-

----------------Arkanoid---------------
A 4am crack                  2015-03-03
---------------------------------------

Name: Arkanoid
Genre: arcade
Year: 1988
Publisher: Taito America Corp.
Media: single-sided 5.25-inch floppy
OS: custom
Other versions: an uncredited crack on
  Asimov

       _________________________
      {                         }
      { "Brave Clarice. You     }
      {  will let me know when  }
      {  those lambs stop       }
      {  screaming, won't you?" }
      {                         }
      {    Silence of the Lambs }
      {_________________________}

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no read errors, but copy squeals and
  crashes

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  ditto

Disk Fixer
  T00 -> custom bootloader
  T11 -> DOS 3.3 disk catalog with one
    file ("HELLO") which is blank
  no other signs of DOS, so I assume
    this is just a diversion

Why didn't any of my copies work?
  Probably a nibble check during boot.
  It's definitely intentional code
  detecting that the disk is a copy.
  (Seriously, it *squeals*.)

Next steps:

  1. Boot trace
  2. Find and disable the nibble check
  3. Record the squeal for posterity

                   ~

               Chapter 1
    In Which We Wake Up In The Dark


[S6,D1=original disk]
[S5,D1=my work disk]

]PR#5
CAPTURING BOOT0
...reboots slot 6...
...reboots slot 5...
SAVING BOOT0

]BLOAD BOOT0,A$800
]CALL -151


; create denibblization table (copied
; verbatim from drive controller ROM
; routine at $C602..$C620)
0801-   A0 00       LDY   #$00
0803-   A2 03       LDX   #$03
0805-   86 3C       STX   $3C
0807-   8A          TXA
0808-   0A          ASL
0809-   24 3C       BIT   $3C
080B-   F0 10       BEQ   $081D
080D-   05 3C       ORA   $3C
080F-   49 FF       EOR   #$FF
0811-   29 7E       AND   #$7E
0813-   B0 08       BCS   $081D
0815-   4A          LSR
0816-   D0 FB       BNE   $0813
0818-   98          TYA
0819-   9D 80 09    STA   $0980,X
081C-   C8          INY
081D-   E8          INX
081E-   10 E5       BPL   $0805

; probably an address ($BC00)
0820-   A2 00       LDX   #$00
0822-   86 80       STX   $80
0824-   A2 BC       LDX   #$BC
0826-   86 81       STX   $81

; maybe a sector count?
0828-   A2 05       LDX   #$05
082A-   8E FB 08    STX   $08FB

; dunno
082D-   A2 D8       LDX   #$D8
082F-   86 47       STX   $47

; assume slot 6 and get drive ready
0831-   A2 60       LDX   #$60
0833-   BD 8E C0    LDA   $C08E,X
0836-   BD 8A C0    LDA   $C08A,X

; find and parse next address field
0839-   20 56 08    JSR   $0856

; is it the sector we wanted?
083C-   AC FB 08    LDY   $08FB
083F-   B9 EB 08    LDA   $08EB,Y
0842-   C5 2D       CMP   $2D

; nope
0844-   D0 F3       BNE   $0839

; match data field prologue
0846-   20 8F 08    JSR   $088F

; read sector data, store it in ($80)
0849-   20 D3 08    JSR   $08D3

; decrement page
084C-   C6 81       DEC   $81

; decrement sector count
084E-   CE FB 08    DEC   $08FB
0851-   D0 DA       BNE   $082D

; jump to boot1
0853-   4C 00 BC    JMP   $BC00

That is probably the least obfuscated
custom bootloader I've ever seen.

I can interrupt it at $0853 and capture
the boot1 code in $B800..$BCFF.



; set up callback at $0853 instead of
; continuing to $BC00
96F8-   A9 05       LDA   #$05
96FA-   8D 54 08    STA   $0854
96FD-   A9 97       LDA   #$97
96FF-   8D 55 08    STA   $0855

; start the boot
9702-   4C 01 08    JMP   $0801

; callback is here -- relocate boot1 to
; graphics page so it survives a reboot
9705-   A2 05       LDX   #$05
9707-   A0 00       LDY   #$00
9709-   B9 00 B8    LDA   $B800,Y
970C-   99 00 28    STA   $2800,Y
970F-   C8          INY
9710-   D0 F7       BNE   $9709
9712-   EE 0B 97    INC   $970B
9715-   EE 0E 97    INC   $970E
9718-   CA          DEX
9719-   D0 EE       BNE   $9709

; turn off slot 6 drive motor
971B-   AD E8 C0    LDA   $C0E8

; reboot to my work disk
971E-   4C 00 C5    JMP   $C500


...reboots slot 6...
...reboots slot 5...
]BSAVE BOOT1 B800-BCFF,A$2800,L$500

                   ~

               Chapter 2
       In Which We Open The Gate


]CALL -151


; 80STOREOFF
BC00-   8D 00 C0    STA   $C000

; read/write language card, RAM bank 1
BC03-   AD 8B C0    LDA   $C08B
BC06-   AD 8B C0    LDA   $C08B

; clear hi-res screen 1
BC09-   A9 00       LDA   #$00
BC0B-   A8          TAY
BC0C-   85 00       STA   $00
BC0E-   A2 20       LDX   #$20
BC10-   86 01       STX   $01
BC12-   91 00       STA   ($00),Y
BC14-   C8          INY
BC15-   D0 FB       BNE   $BC12
BC17-   E6 01       INC   $01
BC19-   CA          DEX
BC1A-   D0 F6       BNE   $BC12

; and show it
BC1C-   AD 57 C0    LDA   $C057
BC1F-   AD 52 C0    LDA   $C052
BC22-   AD 54 C0    LDA   $C054
BC25-   AD 50 C0    LDA   $C050

; loop
BC28-   A9 00       LDA   #$00
BC2A-   85 82       STA   $82
BC2C-   A2 00       LDX   #$00
BC2E-   8E 9C BC    STX   $BC9C
BC31-   20 50 BC    JSR   $BC50
BC34-   EE 9C BC    INC   $BC9C
BC37-   AE 9C BC    LDX   $BC9C
BC3A-   E0 04       CPX   #$04
BC3C-   D0 09       BNE   $BC47

; when X hits 4, switch to RAM bank 2
BC3E-   AD 83 C0    LDA   $C083
BC41-   AD 83 C0    LDA   $C083
BC44-   4C 2E BC    JMP   $BC2E

; when X hits 9, we're done
BC47-   E0 09       CPX   #$09
BC49-   D0 E3       BNE   $BC2E

; jump to game code
BC4B-   A2 60       LDX   #$60
BC4D-   4C 00 40    JMP   $4000

The main action happens in $BC50, which
is a multi-sector read loop driven by
four data tables.



; starting phase (track x2)
BC50-   BD 78 BC    LDA   $BC78,X
BC53-   A8          TAY

; starting memory page
BC54-   BD 8A BC    LDA   $BC8A,X
BC57-   85 81       STA   $81

; sector count
BC59-   BD 93 BC    LDA   $BC93,X
BC5C-   8D 9D BC    STA   $BC9D

; starting sector
BC5F-   BD 81 BC    LDA   $BC81,X
BC62-   AA          TAX

; read a sector
BC63-   20 00 B8    JSR   $B800

; decrement sector count
BC66-   CE 9D BC    DEC   $BC9D
BC69-   F0 0C       BEQ   $BC77

; decrement memory page
BC6B-   C6 81       DEC   $81

; decrement sector
BC6D-   CA          DEX
BC6E-   10 F3       BPL   $BC63

; decrement track
BC70-   A2 0F       LDX   #$0F
BC72-   88          DEY
BC73-   88          DEY
BC74-   4C 63 BC    JMP   $BC63
BC77-   60          RTS

Here's the raw data:

BC78-14 12 12 04 04 02 00 00 40 ;phase
BC81-0D 0F 07 02 04 0F 0F 0B 03 ;sector
BC8A-ED BF B7 DE DD 1F 0C 06 43 ;page
BC93-0E 02 58 03 02 10 04 05 04 ;count

And here's how it translates into code:

1. read $0E sectors into $E000..$EDFF
   starting from T0A,S0D
2. read $02 sectors into $BE00..$BFFF
   starting from T09,S0F
3. read $58 sectors into $6000..$B7FF
   starting from T09,S07
4. read $03 sectors into $DC00..$DEFF
   starting from T02,S02

(main loop switches to RAM bank 2)

5. read $02 sectors into $DC00..$DDFF
   starting from T02,S04
6. read $10 sectors into $1000..$1FFF
   starting from T01,S0F
7. read $04 sectors into $0900..$0CFF
   starting from T00,S0F
8. read $05 sectors into $0200..$06FF
   starting from T00,S0B
9. read $04 sectors into $4000..$43FF
   starting from T20,S03

My non-working copy gets this far.
Still no evidence of copy protection.

                   ~

               Chapter 3
           In Which We Ask,
       Of Each Particular Thing,
         What Is It In Itself?


I need to interrupt the boot at $BC4D,
before it jumps to the game code at
$4000. This will be a little tricky
since my normal boot tracer (starting
at $9600) will be overwritten by the
loader. It looks like it doesn't touch
the stack page though, so I'll relocate
my callback there.



; set up callback #1
96F8-   A9 05       LDA   #$05
96FA-   8D 54 08    STA   $0854
96FD-   A9 97       LDA   #$97
96FF-   8D 55 08    STA   $0855

; start the boot
9702-   4C 01 08    JMP   $0801

; callback #1 is here -- set up
; callback #2 at $0100
9705-   A9 00       LDA   #$00
9707-   8D 4E BC    STA   $BC4E
970A-   A9 01       LDA   #$01
970C-   8D 4F BC    STA   $BC4F

; copy callback #2 code to $0100
970F-   A2 10       LDX   #$10
9711-   BD 1D 97    LDA   $971D,X
9714-   9D 00 01    STA   $0100,X
9717-   CA          DEX
9718-   10 F7       BPL   $9711

; continue the boot
971A-   4C 00 BC    JMP   $BC00

; callback #2 is here (running at $100)
; turn off language card
971D-   AD 82 C0    LDA   $C082

; turn off slot 6 drive motor
9720-   AD E8 C0    LDA   $C0E8

; jump to monitor
9723-   4C 59 FF    JMP   $FF59


...reboots slot 6...
<beep>



; slow down IIgs to 1 Mhz (does nothing
; on older machines)
4000-   AD 36 C0    LDA   $C036
4003-   29 7F       AND   #$7F
4005-   8D 36 C0    STA   $C036

; here we go
4008-   A9 00       LDA   #$00
400A-   8D F7 40    STA   $40F7
400D-   20 90 40    JSR   $4090



; turn on slot 6 drive motor manually
; (always suspicious)
4090-   A2 60       LDX   #$60
4092-   BD 8E C0    LDA   $C08E,X
4095-   BD 8A C0    LDA   $C08A,X

; position drive head on track $21
4098-   A9 42       LDA   #$42
409A-   85 31       STA   $31
409C-   A2 60       LDX   #$60
409E-   20 40 B8    JSR   $B840

; zero some memory
40A1-   A9 00       LDA   #$00
40A3-   8D F9 40    STA   $40F9
40A6-   8D F8 40    STA   $40F8
40A9-   A0 0F       LDY   #$0F
40AB-   99 FA 40    STA   $40FA,Y
40AE-   88          DEY
40AF-   10 FA       BPL   $40AB
40B1-   20 B5 40    JSR   $40B5
40B4-   60          RTS

; just a memory move
40B5-   20 0B 41    JSR   $410B

40B8-   EE F8 40    INC   $40F8
40BB-   A9 19       LDA   #$19
40BD-   8D 7B 41    STA   $417B
40C0-   8D 89 41    STA   $4189
40C3-   20 19 41    JSR   $4119



4119-   A2 60       LDX   #$60
411B-   DD 8E C0    CMP   $C08E,X

; wait loop
411E-   A9 C0       LDA   #$C0
4120-   8D 9F 41    STA   $419F
4123-   8D A0 41    STA   $41A0
4126-   EE 9F 41    INC   $419F
4129-   D0 08       BNE   $4133
412B-   EE A0 41    INC   $41A0
412E-   D0 03       BNE   $4133
4130-   4C 97 41    JMP   $4197

; look for address prologue
4133-   BD 8C C0    LDA   $C08C,X
4136-   10 FB       BPL   $4133
4138-   C9 D5       CMP   #$D5
413A-   D0 EA       BNE   $4126
413C-   BD 8C C0    LDA   $C08C,X
413F-   10 FB       BPL   $413C
4141-   C9 AA       CMP   #$AA
4143-   D0 E1       BNE   $4126
4145-   BD 8C C0    LDA   $C08C,X
4148-   10 FB       BPL   $4145
414A-   C9 96       CMP   #$96
414C-   D0 D8       BNE   $4126

414E-   20 B0 41    JSR   $41B0
4151-   20 B0 41    JSR   $41B0



; get 4-4 encoded value from address
; field
41B0-   BD 8C C0    LDA   $C08C,X
41B3-   10 FB       BPL   $41B0
41B5-   38          SEC
41B6-   2A          ROL
41B7-   8D C0 41    STA   $41C0
41BA-   BD 8C C0    LDA   $C08C,X
41BD-   10 FB       BPL   $41BA
41BF-   29 00       AND   #$00
41C1-   8D C0 41    STA   $41C0
41C4-   60          RTS

; keep trying until we find track $21
4154-   C9 21       CMP   #$21
4156-   8D F7 40    STA   $40F7
4159-   D0 CB       BNE   $4126

; keep trying until we find sector $0F
415B-   20 B0 41    JSR   $41B0
415E-   C9 0F       CMP   #$0F
4160-   D0 C4       BNE   $4126

; look for a nibble sequence "D5 AA"
; which is the first 2/3 of the data
; prologue, but I'm betting there's
; another one in between the address
; and data fields at the end of T21
4162-   BD 8C C0    LDA   $C08C,X
4165-   10 FB       BPL   $4162
4167-   C9 D5       CMP   #$D5
4169-   D0 F7       BNE   $4162
416B-   BD 8C C0    LDA   $C08C,X
416E-   10 FB       BPL   $416B
4170-   C9 AA       CMP   #$AA
4172-   D0 F3       BNE   $4167

; now do some math on the nibbles that
; follow
4174-   A0 00       LDY   #$00
4176-   BD 8C C0    LDA   $C08C,X
4179-   10 FB       BPL   $4176
417B-   19 C5 41    ORA   $41C5,Y
417E-   99 C5 41    STA   $41C5,Y
4181-   C8          INY
4182-   D0 F2       BNE   $4176
4184-   BD 8C C0    LDA   $C08C,X
4187-   10 FB       BPL   $4184
4189-   19 C5 42    ORA   $42C5,Y
418C-   99 C5 42    STA   $42C5,Y
418F-   C8          INY
4190-   D0 F2       BNE   $4184

; turn off drive motor
4192-   20 AA 41    JSR   $41AA
4195-   18          CLC
4196-   60          RTS
4197-   20 0B 41    JSR   $410B
419A-   20 AA 41    JSR   $41AA
419D-   38          SEC
419E-   60          RTS

Execution continues at $40C6:

; if nibble check failed to find
; T21,S0F then just bail
40C6-   B0 2D       BCS   $40F5

; more checks on the nibbles we read
; after T21,S0F
40C8-   AD 20 43    LDA   $4320
40CB-   C9 99       CMP   #$99
40CD-   D0 26       BNE   $40F5
40CF-   A9 59       LDA   #$59
40D1-   8D 7B 41    STA   $417B
40D4-   8D 89 41    STA   $4189
40D7-   20 19 41    JSR   $4119
40DA-   A2 00       LDX   #$00
40DC-   A0 0F       LDY   #$0F
40DE-   B9 20 43    LDA   $4320,Y
40E1-   F0 01       BEQ   $40E4
40E3-   E8          INX
40E4-   88          DEY
40E5-   10 F7       BPL   $40DE
40E7-   FE FA 40    INC   $40FA,X
40EA-   E0 03       CPX   #$03
40EC-   90 07       BCC   $40F5

; if everything checks out, execution
; falls through to here
; $40F8 was initialized with 0 and only
; incremented once, so this decrements
; it back to 0
40EE-   CE F8 40    DEC   $40F8
40F1-   EE F9 40    INC   $40F9
40F4-   60          RTS
40F5-   60          RTS

Execution continues at $4010:

; check counter set by nibble check
4010-   AD F8 40    LDA   $40F8

; non-zero is bad, jump to The Badlands
4013-   D0 03       BNE   $4018

; success path falls through to here --
; jump to actual game code
4015-   4C 00 02    JMP   $0200

; The Badlands
; switch to text screen (full of
; executable code at this point)
4018-   AD 51 C0    LDA   $C051

; $40F7 contains the track, and this
; prints it to the upper-left corner
; of the text screen (for debugging?)
401B-   AD F7 40    LDA   $40F7
401E-   4A          LSR
401F-   4A          LSR
4020-   4A          LSR
4021-   4A          LSR
4022-   AA          TAX
4023-   BD 80 40    LDA   $4080,X
4026-   8D 00 04    STA   $0400
4029-   AD F7 40    LDA   $40F7
402C-   29 0F       AND   #$0F
402E-   AA          TAX
402F-   BD 80 40    LDA   $4080,X
4032-   8D 01 04    STA   $0401

; and a space -- this is all very
; intentional and deliberate and weird
4035-   A9 A0       LDA   #$A0
4037-   8D 02 04    STA   $0402

; now squeal
403A-   20 65 40    JSR   $4065

; now crash
403D-   00          BRK

; The Squealing routine
4065-   A2 00       LDX   #$00
4067-   A0 00       LDY   #$00
4069-   EE 30 C0    INC   $C030
406C-   A9 FF       LDA   #$FF
406E-   A9 FF       LDA   #$FF
4070-   A9 FF       LDA   #$FF
4072-   A9 FF       LDA   #$FF
4074-   CE 30 C0    DEC   $C030
4077-   E8          INX
4078-   D0 EF       BNE   $4069
407A-   C8          INY
407B-   C0 FF       CPY   #$FF
407D-   90 EA       BCC   $4069
407F-   60          RTS

                   ~

               Chapter 4
       In Which We Take One Byte
    And Run Away As Fast As We Can


It looks like I can just disable the
branch at $4013. Changing the "BNE"
to a "BIT" will give me a one byte
patch:

T20,S00,$13 change "D0" to "24"

Quod erat liberandum.

---------------------------------------
A 4am crack                     No. 237
------------------EOF------------------