/* MvC1 six-button palette selector and six-block body-palette reader.
 *
 * Linked at $3F4000 in stock $FF space inside mvc.10.  The entry points are
 * reached by absolute JMP hooks in the encrypted first program ROM.
 * GNU as, Motorola syntax, 68000 only.
 */

        .cpu 68000
        .section .text
        .even

        .equ STOCK_SELECTOR_TAIL,  0x00C448
        .equ STOCK_SIDEART_ADDA,   0x0B7696
        .equ STOCK_SIDEART_AFTER,  0x0B7698
        .equ SIDEART_ARENA,        0x3F5000
        .equ SHADOW_DYNAMIC_BASE,  0x92FC00
        .equ SHADOW_SIX_BASE,      0x186AC0
        .equ MEGA_MAN_SIX_BASE,    0x1861C0
        .global selector_decode
        .global duplicate_next_color
        .global body_a6_a4bc
        .global body_a4_a5b2
        .global body_a4_a62e
        .global body_d0_a682
        .global body_d0_a6c2
        .global body_a4_a7b2
        .global body_a4_a82e
        .global body_d0_a882
        .global body_d0_a8c0
        .global select_sideart_six_color
        .global shadow_palette_guard
        .global mega_row2_guard
        .global mega_row0_guard
        .global mega_row1_guard
        .global select_animation_six_color

/* Entry replaces $00C434..$00C439, after stock has selected P1/P2's held word
 * into d2.  Keeping $C426..$C433 intact also preserves the original calling
 * convention at the smallest possible hook.
 *
 * MvC retains the standard CPS-2 six-button word even though its stock code
 * collapses $0100/$0200/$0400 into one "kick" result.  Live MAME traces prove:
 *
 *   Button 1/LP $0010 -> 0       Button 4/LK $0100 -> 3
 *   Button 2/MP $0020 -> 1       Button 5/MK $0200 -> 4
 *   Button 3/HP $0040 -> 2       Button 6/HK $0400 -> 5
 *
 * Lowest numbered button wins if more than one is held.  No attack button
 * falls back to palette 0, matching stock's punch-side default.
 */
selector_decode:
        moveq   #0,%d1
        btst    #4,%d2
        bne.s   7f
        moveq   #1,%d1
        btst    #5,%d2
        bne.s   7f
        moveq   #2,%d1
        btst    #6,%d2
        bne.s   7f
        moveq   #3,%d1
        btst    #8,%d2
        bne.s   7f
        moveq   #4,%d1
        btst    #9,%d2
        bne.s   7f
        moveq   #5,%d1
        btst    #10,%d2
        bne.s   7f
        moveq   #0,%d1
7:
        jmp     STOCK_SELECTOR_TAIL

/* Entry replaces $00C440..$00C445.
 *
 * The stock duplicate-character path receives the already-used palette in
 * d2 and boolean-inverts it.  Advance modulo six instead: 0->1 ... 5->0.
 */
duplicate_next_color:
        moveq   #0,%d1
        move.b  %d2,%d1
        addq.w  #1,%d1
        cmpi.w  #6,%d1
        bcs.s   8f
        moveq   #0,%d1
8:
        jmp     STOCK_SELECTOR_TAIL

/* The $A4xx family has nine body-palette readers.  Four keep the base pointer
 * in a0 with the flag on a fighter object; four keep the pointer in d0 with
 * the flag at a5-relative globals; $A4BC is the direct a6 form.  Each wrapper
 * skips its complete stock boolean sequence and rejoins at its exact tail.
 * $A920 is deliberately not here: it selects an assist/effect palette table.
 */

body_a6_a4bc:
        moveq   #0,%d0
        move.b  0x277(%a6),%d0
        bsr.s   scale_a0
        jmp     0x00A4C6

body_a4_a5b2:
        moveq   #0,%d0
        move.b  0x277(%a4),%d0
        bsr.s   scale_a0
        jmp     0x00A5BC

body_a4_a62e:
        moveq   #0,%d0
        move.b  0x277(%a4),%d0
        bsr.s   scale_a0
        jmp     0x00A638

body_a4_a7b2:
        moveq   #0,%d0
        move.b  0x277(%a4),%d0
        bsr.s   scale_a0
        jmp     0x00A7BC

body_a4_a82e:
        moveq   #0,%d0
        move.b  0x277(%a4),%d0
        bsr.s   scale_a0
        jmp     0x00A838

scale_a0:
        bsr.w   normalize_a0
        mulu.w  #0x0060,%d0
        adda.l  %d0,%a0
        rts
        nop
        nop

body_d0_a682:
        moveq   #0,%d1
        move.b  -0x4D89(%a5),%d1
        bsr.s   scale_d0
        jmp     0x00A68E

body_d0_a6c2:
        moveq   #0,%d1
        move.b  -0x4589(%a5),%d1
        bsr.s   scale_d0
        jmp     0x00A6D0

body_d0_a882:
        moveq   #0,%d1
        move.b  -0x4989(%a5),%d1
        bsr.s   scale_d0
        jmp     0x00A88E

body_d0_a8c0:
        moveq   #0,%d1
        move.b  -0x4189(%a5),%d1
        bsr.s   scale_d0
        jmp     0x00A8CC

scale_d0:
        bsr.w   normalize_d0
        mulu.w  #0x0060,%d1
        add.l   %d1,%d0
        rts
        nop
        nop

/* The large character-select figure has a separate three-row palette loader.
 * Stock chooses its first source only for index 0 and one alternate source for
 * every non-zero index, visibly collapsing MP/HP/LK/MK/HK together even though
 * the fighter object already stores 0..5.
 *
 * Entry replaces $0B7690..$0B7695 after stock has resolved the old P/K source
 * offset in d0.  The select presentation object identifies player in +$02 and
 * point/bench in +$62.  Read the corresponding fighter +$277 byte, map the 21
 * vanilla-selectable ids to a compact arena, and skip stock's base+offset add.
 * Unsupported ids and malformed indices reproduce the original instruction.
 */
        .balign 16
select_sideart_six_color:
        move.w  %d0,-(%sp)
        moveq   #0,%d1
        tst.b   0x0002(%a6)
        bne.s   11f
        tst.b   0x0062(%a6)
        bne.s   12f
        move.b  -0x4D89(%a5),%d1
        bra.s   15f
12:
        move.b  -0x4589(%a5),%d1
        bra.s   15f
11:
        tst.b   0x0062(%a6)
        bne.s   13f
        move.b  -0x4989(%a5),%d1
        bra.s   15f
13:
        move.b  -0x4189(%a5),%d1
15:
        cmpi.w  #5,%d1
        bhi.s   19f

        moveq   #0,%d0
        move.w  0x0052(%a6),%d0
        cmpi.w  #0x0002,%d0
        bcs.s   19f
        cmpi.w  #0x0020,%d0
        bls.s   16f
        cmpi.w  #0x0024,%d0
        bcs.s   19f
        cmpi.w  #0x002C,%d0
        bhi.s   19f
        lsr.w   #1,%d0
        subq.w  #2,%d0
        bra.s   17f
16:
        lsr.w   #1,%d0
        subq.w  #1,%d0
17:
        addq.l  #2,%sp
        mulu.w  #0x0240,%d0
        mulu.w  #0x0060,%d1
        add.l   %d1,%d0
        movea.l #SIDEART_ARENA,%a0
        adda.l  %d0,%a0
        jmp     STOCK_SIDEART_AFTER
19:
        move.w  (%sp)+,%d0
        movea.l #0x001D2A24,%a0
        jmp     STOCK_SIDEART_ADDA

/* Shadow Lady builds only two stock body blocks in slot-local RAM.  Redirect
 * that dynamic base to her identity-preserving six-block ROM arena before the
 * common index scaling; every other fighter keeps its original base pointer.
 * These helpers live after all externally hooked entry points so their added
 * bytes cannot move any encrypted JMP target.
 */
normalize_a0:
        cmpa.l  #SHADOW_DYNAMIC_BASE,%a0
        bne.s   20f
        bsr.w   dynamic_owner_a0
        nop
20:
        cmpi.w  #5,%d0
        bls.s   21f
        moveq   #0,%d0
21:
        rts

normalize_d0:
        cmpi.l  #SHADOW_DYNAMIC_BASE,%d0
        bne.s   22f
        bsr.w   dynamic_owner_d0
        nop
22:
        cmpi.w  #5,%d1
        bls.s   23f
        moveq   #0,%d1
23:
        rts

/* Shadow Lady's stock animation worker overwrites body row 0 from a two-color
 * dynamic table every few frames.  That erases indices 2..5 even after the
 * common body loader selected the correct ROM block.  Skip only that live-row
 * overwrite for Shadow Lady; every other fighter and all surrounding worker
 * state continue through the stock path.
 */
shadow_palette_guard:
        cmpi.w  #0x002A,0x0052(%a6)
        beq.s   24f
        tst.b   0x79B7(%a5)
        bne.s   24f
        jmp     0x0069882
24:
        jmp     0x00698A0

/* $92FC00 is a shared slot-local dynamic palette address, not a Shadow Lady
 * identity.  Mega Man reaches the same address through the common A5xx body
 * readers.  Resolve the owner before replacing that transient pointer with a
 * six-block ROM base; otherwise Mega Man is accidentally painted from Shadow
 * Lady's arena and never reaches an exact six-color body frame.
 *
 * These routines are deliberately after shadow_palette_guard.  The encrypted
 * guard hook therefore remains fixed at $3F41A2.
 */
dynamic_owner_a0:
        cmpi.w  #0x0020,0x0052(%a4)
        beq.s   25f
        cmpi.w  #0x0020,0x0052(%a6)
        beq.s   25f
        cmpi.w  #0x002A,0x0052(%a4)
        beq.s   26f
        cmpi.w  #0x002A,0x0052(%a6)
        bne.s   27f
26:
        movea.l #SHADOW_SIX_BASE,%a0
        rts
25:
        movea.l #MEGA_MAN_SIX_BASE,%a0
27:
        rts

dynamic_owner_d0:
        cmpi.w  #0x0020,0x0052(%a4)
        beq.s   28f
        cmpi.w  #0x0020,0x0052(%a6)
        beq.s   28f
        cmpi.w  #0x002A,0x0052(%a4)
        beq.s   29f
        cmpi.w  #0x002A,0x0052(%a6)
        bne.s   30f
29:
        move.l  #SHADOW_SIX_BASE,%d0
        rts
28:
        move.l  #MEGA_MAN_SIX_BASE,%d0
30:
        rts

/* Mega Man's intro has three palette-animation writers.  Their stock source
 * scripts contain only punch and kick banks, so indices 1..5 collapse back to
 * the kick costume after the common six-color loader has run.  Keep every
 * sprite/state animation, but source each written 16-pen row from the selected
 * six-color block.  Non-Mega-Man callers reproduce the displaced stock code.
 */
mega_row2_guard:
        cmpi.w  #0x0020,0x0052(%a6)
        bne.s   31f
        moveq   #0,%d0
        move.b  0x0277(%a6),%d0
        cmpi.w  #5,%d0
        bls.s   32f
        moveq   #0,%d0
32:
        mulu.w  #0x0060,%d0
        add.w   %d2,%d0
        movea.l #MEGA_MAN_SIX_BASE,%a0
        adda.l  %d0,%a0
        jmp     0x0064EB4
31:
        tst.b   0x0277(%a6)
        bne.s   38f
        jmp     0x0064EB4
38:
        jmp     0x0064EB0

mega_row0_guard:
        cmpi.w  #0x0020,0x0052(%a6)
        bne.s   33f
        moveq   #0,%d0
        move.b  0x0277(%a6),%d0
        cmpi.w  #5,%d0
        bls.s   34f
        moveq   #0,%d0
34:
        mulu.w  #0x0060,%d0
        add.w   %d2,%d0
        movea.l #MEGA_MAN_SIX_BASE,%a4
        adda.l  %d0,%a4
        jmp     0x0064F16
33:
        tst.b   0x0277(%a6)
        bne.s   39f
        jmp     0x0064F16
39:
        jmp     0x0064F12

mega_row1_guard:
        move.l  %d2,-(%sp)
        cmpi.w  #0x0020,0x0052(%a4)
        bne.s   35f
        moveq   #0,%d2
        move.b  0x0277(%a4),%d2
        cmpi.w  #5,%d2
        bls.s   36f
        moveq   #0,%d2
36:
        mulu.w  #0x0060,%d2
        add.w   %d1,%d2
        movea.l #MEGA_MAN_SIX_BASE,%a0
        adda.l  %d2,%a0
        bra.s   37f
35:
        movea.l (%a0),%a0
37:
        movea.l 0x0288(%a4),%a1
        move.l  (%sp)+,%d2
        jmp     0x007D0C6

/* The small animated fighter on character select has its own two-row palette
 * upload at $07AF36.  Stock reads the already-committed point/bench color byte
 * through a four-slot pointer table, but treats it as a boolean: zero keeps the
 * first block and every non-zero value adds exactly $60.  That makes
 * MP/HP/LK/MK/HK all animate with bank 1 even though the large side-art and
 * match body use banks 1/2/3/4/5 correctly.
 *
 * Entry replaces $07AF6C..$07AF71 with an absolute JMP after a0 has resolved
 * the fighter's relocated six-block base and a1 points at the committed color
 * byte.  Rejoin at $07AF74 with a0 on the selected block; the stock upload,
 * destination selection, priority-bit preservation, and animation remain
 * untouched.
 */
        .balign 16
select_animation_six_color:
        moveq   #0,%d0
        move.b  (%a1),%d0
        cmpi.w  #5,%d0
        bls.s   40f
        moveq   #0,%d0
40:
        mulu.w  #0x0060,%d0
        adda.l  %d0,%a0
        jmp     0x007AF74
