Patch: allowing use of "reserved" palette colors for player characters
#5
UPDATE

I got inspired, and did a new version of it, with vastly improved efficiency. The most CPU intensive parts of the code have been completely rewritten. I'd say it's now two or three times as fast (based on my subjective judgment -- I didn't do any clocking.)

The "load time" is now much more bearable. I'd say it usually isn't that noticeable. The main circumstance I notice it in is when Terra switches from human to esper form, or back (the animation makes it more obvious than usual). But, even then, though it's noticeable, it isn't bad.

I could probably clean things up just a little bit more, but I consider this close to a "release candidate".

Anyway, I'd like people to test this. I've tested it myself, and I don't notice any bugs, but it's possible there's some circumstance I haven't tried.

I'd also still be happy for any comments on how I can improve the algorithm, though it's already much improved.

I'll wait a while for feedback, and possibly make a few more small changes, before declaring it "done".

Speaking of which, does anyone have any suggestions on how I should "release" this? Should I submit it to romhacking.net or something like that? Considering that it's mainly a patch to help people who are doing their own patches (as opposed to a patch that improves the game in itself), would this site be interested in hosting it or linking to it?

So, here's the new hex, and source:

Code:
paste into 3100FD (F1/00FD)

C9FFD0
016B8514861CAAA5104848DAA61C861A
A5140A186514AAA9C285168BA97F48AB
BF45CEC28512C220BF43CEC28510A945
C78514A61AA90001851AA910008518A7
14C9FFFFD00C7B9D0000E8E8C618D0F7
800EA8B7109D0000E8E8C8C8C618D0F3
E614E614C61AD0D27BE220A61CA94085
12BDC0030A66100A66100A66100A6610
0A66100A66100A66100A6610A5109DC0
03BDC0100A66100A66100A66100A6610
0A66100A66100A66100A6610A5109DC0
10E8C612D0BBABFA680A0A0A0A0A08C2
3048DA5AA9C300851AA21E00E2206419
6418C220A51C186900208510A97F0085
12C220A510290F00D008A51038E91000
8510C610C610A00000E220A9028514A9
FF85158A2514F004A9008002A9FF5710
25158515F0110614C898C912F009C902
D0E1A0100080DCE220A515F016C220A5
1A1F1006F1851A8AC91800300EE220E6
198008C220A510C51CD096C2208AC918
00100CA51A3F1006F1D004E220E618C2
20CACA8AC90000F02CA51A3F1006F1D0
EE8AC918001019E220A518C519D011C2
20A51A1F1006F1851ACACA8A10F18005
C2204CD201A90000E220851385148515
8516C220A20000A51A3F1006F1F00BE8
E88AC91800D0F04C3F039BA21800A51A
3F1006F1D00BE8E88AC92000D0F04C3F
03E230A513D0118A4A0A0A0A0A851398
4A051385134C2703A514D0118A4A0A0A
0A0A8514984A051485144C2703A515D0
118A4A0A0A0A0A8515984A051585154C
2703A516D0298A4A0A0A0A0A8516984A
051685164C2703C230DABBBF1006F145
1A851AFABF1006F1451A851A4CA402E2
30A513D0034CE603C230A51C18690020
8510C230A510290F00D008A51038E910
008510C610C610E230A200A011A9FF85
18A9808519BF130000D0034CDB032519
F004A9008002A9FF57102518F0448518
98C900F00EC910D004A0018001884619
4C7503A000A9018519BF1300002519F0
08B710051897108008A9FF4518371097
10C898C912F00BC902D002A01006194C
A903E88AC904F0034C6B03C230A510C5
1CF0034C5203C2307AFA6828DAAABDAE
2EC90ED012BDC62EC901D00BADA01E29
08F004FA7B8005FABF2BCEC2C2200A0A
0A0A0AAA7BE220680A0A0A0A0AA85AA9
188510BF0063ED99AD81E8C8C610D0F3
08C23048DA5AA51048A51248A51448A5
1648A51848A51A48A51C48A51E488A38
E91800851A9838E91800851CA51329FF
004A4A4A4A0A18651AAAA513290F000A
18651CA8E220BF0063ED99AD81E8C8BF
0063ED99AD81C220A51429FF004A4A4A
4A0A18651AAAA514290F000A18651CA8
E220BF0063ED99AD81E8C8BF0063ED99
AD81C220A51529FF004A4A4A4A0A1865
1AAAA515290F000A18651CA8E220BF00
63ED99AD81E8C8BF0063ED99AD81C220
A51629FF004A4A4A4A0A18651AAAA516
290F000A18651CA8E220BF0063ED99AD
81E8C8BF0063ED99AD81C22068851E68
851C68851A6885186885166885146885
126885107AFA6828FAFEC4616B000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
01000200040008001000200040008000
01000200040008001000200040008000
00010002000400080010002000400080

As before, you also have to jump to the new code from the old function.

Code:
Paste to 013D43 (C1/3D45):

22 FD 00 F1 60

And the ASM.

(By the way: this ASM code includes both my new code, and the disassembly of the original function from C1. I added a significant number of my own comments to the original C1 code. So, if you are interested in understanding/editing this code yourself, those might be helpful to you.)

Code:
;====================================

ADDRESS_BEGIN_DATA .EQU $0600
ADDRESS_BEGIN_DATA_ABSOLUTE .EQU ADDRESS_BEGIN_DATA+$F10000
lbg_data_bitmask .EQU ADDRESS_BEGIN_DATA_ABSOLUTE+0
lbg_data_shifted_bit .EQU lbg_data_bitmask+16

;====================================

; The routine that loads character battle graphics and palettes
C1_3D43:    ;C9FF        CMP #$FF        (from C1_316F, C1_3B1A, C1_3B2A, C1_3B3A, C1_3B4A)
    CMP #$FF                ;Accumulator is expected to hold the character sprite ID.
                        ;if the chracter ID is FF, that means empty
C1_3D45:    ;D001        BNE $3D48
    BNE C1_3D48
C1_3D47:    ;60          RTS
    RTL                    ;rts replaced with rtl

C1_3D48:    ;8514        STA $14        (from only C1_3D45)

    STA $14
C1_3D4A:    ;861C        STX $1C
    STX $1C
C1_3D4C:    ;AA          TAX
    TAX
C1_3D4D:    ;A510        LDA $10
    LDA $10
C1_3D4F:    ;48          PHA
    PHA
C1_3D50:    ;48          PHA
    PHA
C1_3D51:    ;DA          PHX
    PHX
C1_3D52:    ;A61C        LDX $1C
    LDX $1C
C1_3D54:    ;861A        STX $1A
    STX $1A
C1_3D56:    ;A514        LDA $14
    LDA $14
C1_3D58:    ;0A          ASL A
    ASL A
C1_3D59:    ;18          CLC
    CLC
C1_3D5A:    ;6514        ADC $14
    ADC $14
C1_3D5C:    ;AA          TAX
    TAX                    ; X now holds 3 * the character ID passed in in the accumulator. This is to
                        ; offset a 3-byte long pointer.
C1_3D5D:    ;A9C2        LDA #$C2
    LDA #$C2                ; This will be the high byte of a 3-byte pointer (to data in C2)
C1_3D5F:    ;8516        STA $16
    STA $16
C1_3D61:    ;8B          PHB
    PHB
C1_3D62:    ;A97F        LDA #$7F
    LDA #$7F
C1_3D64:    ;48          PHA
    PHA
C1_3D65:    ;AB          PLB
    PLB                    ; We're now using $7f as our data bank
;    .DBREG $7F
C1_3D66:    ;BF45CEC2    LDA $C2CE45,X    (High byte of pointer to start of character battle graphics)
    LDA >$C2CE45,X
C1_3D6A:    ;8512        STA $12
    STA $12
C1_3D6C:    ;C220        REP #$20
    REP #$20                ;set the accumulator to 16 bit
    .LONGA ON
C1_3D6E:    ;BF43CEC2    LDA $C2CE43,X     (Pointer to start of character battle graphics)
    LDA >$C2CE43,X
C1_3D72:    ;8510        STA $10
    STA $10
C1_3D74:    ;A945C7      LDA #$C745
    LDA #$C745                ; Note that we already stored #$C2 in $16
C1_3D77:    ;8514        STA $14
    STA $14                    ; Combinted with the new 16-bit address, $14-$16 now hold a
                        ; 16-bit address. This is a data section in C2. I believe it contains
                        ; The data on how to compose 8x8 tiles into sprite tiles.
C1_3D79:    ;A61A        LDX $1A
    LDX $1A                    ; This re-loads the original value which was stored in X when we entered the function.
                        ; This should be $0000, $2000, $4000, or $6000, depending on whether this is
                        ; battle-character 1, 2, 3 or 4.
C1_3D7B:    ;A90001      LDA #$0100
    LDA #$0100                ; $1A will be a counter for an outer loop, counting down from 256
C1_3D7E:    ;851A        STA $1A
    STA $1A
C1_3D80:    ;A91000      LDA #$0010
    LDA #$0010                ; This begins an inner loop...
C1_3D83:    ;8518        STA $18
    STA $18                    ; And the inner loop counter counts down from 16
C1_3D85:    ;A714        LDA [$14]
    LDA [$14]                ; Load from the pointer stored in DP addresses $14-16. Again, these point to data in
                        ; C2.
C1_3D87:    ;C9FFFF      CMP #$FFFF
    CMP #$FFFF                ; #$FFFF appears to represent a blank tile (or blank line?). Special code handles this
                        ; case.
C1_3D8A:    ;D00C        BNE $3D98
    BNE C1_3D98                ; Otherwise, skip ahead to the normal case.
C1_3D8C:    ;7B          TDC
    TDC                    ; (the =#$FFFF case)
                        ; I think this loads 0 into the accumulator. I'm not sure why they didn't just use
                        ; the literal zero?
C1_3D8D:    ;9D0000      STA $0000,X
    STA |$0000,X                ; Now we write #$0000 16 times (for a total of 32 bytes)
C1_3D90:    ;E8          INX
    INX
C1_3D91:    ;E8          INX
    INX
C1_3D92:    ;C618        DEC $18
    DEC $18
C1_3D94:    ;D0F7        BNE $3D8D
    BNE C1_3D8D                ; loop...
C1_3D96:    ;800E        BRA $3DA6
    BRA C1_3DA6                ; when loop ends, skip over the "normal" code and continue
C1_3D98:    ;A8          TAY
    TAY                    ; Here begins the normal != #$FFFF case
C1_3D99:    ;B710        LDA [$10],Y
    LDA [$10],Y                ; Load into A the long address stored in $10-$12, offset by Y, which contains 3 *
                        ; the character spire ID. (This gives us the pointer to the start of the character
                        ; battle graphics)
C1_3D9B:    ;9D0000      STA $0000,X
    STA |$0000,X                ; Store the loaded graphics in memory
C1_3D9E:    ;E8          INX
    INX
C1_3D9F:    ;E8          INX
    INX
C1_3DA0:    ;C8          INY
    INY
C1_3DA1:    ;C8          INY
    INY                    ; ... and increment the counters by 2 bytes each
C1_3DA2:    ;C618        DEC $18
    DEC $18                    ; loop 16 times
C1_3DA4:    ;D0F3        BNE $3D99
    BNE C1_3D99
C1_3DA6:    ;E614        INC $14
    INC $14
C1_3DA8:    ;E614        INC $14
    INC $14                    ; Incremet this pointer into C2 (tile-related data)
C1_3DAA:    ;C61A        DEC $1A
    DEC $1A                    ; Decrement the larger, outer counter, which had the initial value of 256 (#$0100)
C1_3DAC:    ;D0D2        BNE $3D80
    BNE C1_3D80                ; This ends the outer loop. In total, with the inner and outer loop, we will load data
                        ; 256 * 16 times, * 2 bytes per loop, = 8192 bytes (#$2000).
C1_3DAE:    ;7B          TDC
    TDC                    ; This, again is meant to set accum to 0, I think
C1_3DAF:    ;E220        SEP #$20
    SEP #$20                ; Set the accumulator to 8-bit mode
    .LONGA OFF
C1_3DB1:    ;A61C        LDX $1C
    LDX $1C                    ; Restore X to its original value, viz. #$0000, #$2000, #$4000, or #$6000, depending
                        ; on which battle character this is.
C1_3DB3:    ;A940        LDA #$40
    LDA #$40                ; We're going to repeat this loop 64 times
C1_3DB5:    ;8512        STA $12
    STA $12                    ; Loop counter stored in $12
C1_3DB7:    ;BDC003      LDA $03C0,X
    LDA $03C0,X                ; Starting with byte $03C0 ...
C1_3DBA:    ;0A          ASL A
    ASL A                    ; ... we repeatedly ASL A ...
C1_3DBB:    ;6610        ROR $10
    ROR $10                    ; ... and ROR $10, with carry ...
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A                    ; ... repeated 8 times ...
    ROR $10                    ; ... this has the effect of reversing the order of the bits.
C1_3DD2:    ;A510        LDA $10
    LDA $10                    ; Then we take the reversed-bit result...
C1_3DD4:    ;9DC003      STA $03C0,X
    STA $03C0,X                ; ... and save it back to to this memory location.
C1_3DD7:    ;BDC010      LDA $10C0,X
    LDA $10C0,X                ; Then we repeat the same thing for another memory location.
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10    
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10
    ASL A
    ROR $10    
C1_3DF2:    ;A510        LDA $10
    LDA $10
C1_3DF4:    ;9DC010      STA $10C0,X
    STA $10C0,X                ; ...and save it
C1_3DF7:    ;E8          INX
    INX
C1_3DF8:    ;C612        DEC $12
    DEC $12
C1_3DFA:    ;D0BB        BNE $3DB7
    BNE C1_3DB7                ; Repeat this loop 64 times, for 64 bytes in each of two locations.
C1_3DFC:    ;AB          PLB
    PLB
C1_3DFD:    ;FA          PLX
    PLX
C1_3DFE:    ;68          PLA
    PLA                    ; Restoring a bunch of stuff from the stack
C1_3DFF:    ;0A          ASL A
    ASL A                    ; Shifting bits by 5. I think this has to do with multiplying the character ID by
                        ; 32, in order to get an offset.
C1_3E00:    ;0A          ASL A
    ASL A
C1_3E01:    ;0A          ASL A
    ASL A
C1_3E02:    ;0A          ASL A
    ASL A
C1_3E03:    ;0A          ASL A
    ASL A


load_battle_graphics_new:            ; new code begins, in which we mess with the tile and palette values to make it
                        ; so we can use the last four palette colors for battle PCs.


    PHP                    ; push status register

    REP #$30                ; Make accum/index 16 bit
    .LONGA ON
    .LONGI ON

    PHA                    ; push A
    PHX                    ; and X
    PHY                    ; and Y


; We're going to loop through every byte of graphics data we just stored in memory, checking for each palette value whether that value
; is used. When we're done, we'll have information on which palette values are used anywhere in the sprite data.

; We do this by looping through palette values 0 thru 15, and looping through the four bitplanes associated with a single set
; of pixels. In the end, we'll know whether a palette value was used anywhere in that "row" of 8 pixels.


                        ; $1A-$1B will be 16 bits, each representing a palette entry, telling us which
                        ; palette numbers have been used in any pixel of this sprite data.

                        ; Palette entries 1, 2, 7 & 8 should always be considered used...
    LDA #%0000000011000011            ; (these are clear, black / outline, and two skin colors)
    STA $1A                    ; Since these are manipulated dynamically in battle, we shouldn't mess with them.

    LDX #30                    ; X holds the palette number we are checking for, times two.
                        ; Indexed starting with zero, so #15 (x2 = #30) is the last. We check the last first.

    SEP #$20                ; 8 bit accumulator
    .LONGA OFF

    STZ $19                    ; $19 will represent the number of used colors in the expanded palette which we
                        ; have found, and need to switch in to the main palette.

    STZ $18                    ; $18 will represent the number of definitely unused colors in the main palette,
                        ; which can safely be replaced with expanded colors.

lbg_check_memory_for_palette_loop:
    
    REP #$20                                ; 16 bit accumulator
        .LONGA ON

    LDA $1C                    ; Load into A the value originally stored in X, viz. $0000, $2000... &c
                        ; depending on which of the four battle characters this is.
    CLC
    ADC #$2000                ; Point to the end of the data we just loaded for this character

    STA $10                    ; Store into dp $10-$11, to use as index
    LDA #$7F                ; the third byte of the pointer
    STA $12

load_battle_graphics_outer_byte_loop:

    REP #$20                                ; 16 bit accumulator
        .LONGA ON

    LDA $10
    AND #$000F                ; Check if this is a multiple of 16
    BNE load_battle_graphics_outer_byte_loop_2    ; if not, don't do anything special
    LDA $10
    SEC
    SBC #16                    ; If so, subtract an additional 16 (to account for the weird bitplane format)
    STA $10

load_battle_graphics_outer_byte_loop_2:
    DEC $10
    DEC $10
    LDY #0                    ; Y is the byte offset for the bitplane (0, 1, 16, or 17)

    SEP #$20                ; 8 bit accumulator
    .LONGA OFF

    LDA #%10
    STA $14                    ; $14 is a temporary bitmask, which gives us one bit, and shifts left with each
                        ; iteration of Y. Its purpose is to give us the Nth bit stored in X

    LDA #%11111111                ; $15 is a temporary value, composed of the logical AND of all bitplanes, telling us
    STA $15                    ; Whether each one of them has the "correct" value for the palette value we're
                        ; currently checking. In other words, it tells us if a given palette value is used
                        ; in the current set of pixels.

lbg_palette_check_loop:

    TXA
    AND $14                    ; Find out if the Nth bit of X is a zero (N=1..4, depending on bitplane)
    BEQ lbg_palette_check_not_set

lbg_palette_check_set:                ; If the bit is a 1...
    LDA #%00000000                ; Load all 0's into 8-bit A, so EOR will do nothing
    BRA lbg_palette_check_loop_2

lbg_palette_check_not_set:            ; If the bit is a 0...
    LDA #%11111111                ; Load all 1's into 8-bit A, so EOR will flip all bits

lbg_palette_check_loop_2:
    EOR [$10],Y                ; EOR with the gfx byte, either getting its value or the logical-NOT of its value,
                        ; depending on whether we "want" these bits to be on, or off.

    AND $15                    ; AND with $15. The resulting byte will have 1's for only those bits/pixels which
                        ; have the "right" value across all bitplanes so far tested.
    STA $15                    ; And store back.

    BEQ lbg_palette_check_bitplane_done    ; If all bits are already zero, we don't have to check the rest of the bitplanes.

    ASL $14                    ; shift the bitmask left, so we check the next bit next iteration
    INY                    ; move on to the next bitplane
    TYA
    CMP #18                    ; check if we're done with all four bitplanes
    BEQ lbg_palette_check_bitplane_done

    CMP #2                    ; If Y increments past 1, we want to jump straight to 16, to account for the weird
    BNE lbg_palette_check_loop        ; offsets of the four bitplanes
    LDY #16
    BRA lbg_palette_check_loop

lbg_palette_check_bitplane_done:

    SEP #$20
    .LONGA OFF

    LDA $15                    ; Load the data for this row of pixels
    BEQ lbg_palette_check_bitplane_done_2    ; If all are 0, the palette color was not found in any of these 8 bits, so do not set

                        ; Otherwise, it was found, and we should set it as "used"

    REP #$20                ; 16 bit accumulator
    .LONGA ON
    
    LDA $1A                    ; Load the cumulative "palette used" data, 16 bits
    ORA >lbg_data_shifted_bit,X        ; Use the appropriate bitmask to set the bit
    STA $1A                    ; Store the new value, with the bit turned on

    TXA
    CMP #24                    ; Check if this is one of the expanded palette numbers
    BMI all_gfx_bytes_checked_done        ; If not, go ahead, we're finished here

    SEP #$20
    .LONGA OFF

    INC $19                    ; If so, increment the counter for used expanded colors before continuing

    BRA all_gfx_bytes_checked_done        ; Since we just found this palette #, no need to keep searching

lbg_palette_check_bitplane_done_2:

    REP #$20                ; 16 bit accumulator
    .LONGA ON

    LDA $10                    ; Load the data pointer
    CMP $1C                    ; Have we reached the start of this character's gfx data?
    BNE load_battle_graphics_outer_byte_loop ; If not, then loop

all_gfx_bytes_checked_done:
                        ; After we've checked all bytes for a particular palette value...

    REP #$20                ; 16 bit accumulator
    .LONGA ON

    TXA
    CMP #24                    ; Check if this was an expanded palette color
    BPL all_gfx_bytes_checked_done_2    ; If so, continue....

    LDA $1A                    ; If a main palette color, check if it was set
    AND >lbg_data_shifted_bit,X        ; If so, continue...
    BNE all_gfx_bytes_checked_done_2

    SEP #$20
    .LONGA OFF

    INC $18                    ; If an unused main palette color, increment the number of unused palette numbers

all_gfx_bytes_checked_done_2:
    REP #$20                ; 16 bit accumulator
    .LONGA ON

    DEX                    ; Decrement X twice, so we check the previous palette #
    DEX
    TXA
    CMP #0
    BEQ lbg_palette_check_completely_done    ; If X is 0 (which we don't need to check) we are done.

    LDA $1A                    ; Check if the new X represents a palette color that is already "used"
    AND >lbg_data_shifted_bit,X        ; (This will happen for colors we set to 1 at the beginning, because they are
                        ;  manipulated by the gfx engine and we don't want to mess with them)
    BNE all_gfx_bytes_checked_done_2    ; If so, decrement again

    TXA
    CMP #24                    ; Check if this was an expanded palette color
    BPL all_gfx_bytes_checked_continue_loop    ; If so, just continue....

    SEP #$20
    .LONGA OFF

    LDA $18                    ; If this is a normal palette number...
    CMP $19                    ; Check the used expanded against the unused normal...
    BNE all_gfx_bytes_checked_continue_loop ; If not equal, we can't stop yet.

lbg_palette_check_fill_with_ones:

    REP #$20                ; 16 bit accumulator
    .LONGA ON

    LDA $1A                    ; But if equal, we can stop.
    ORA >lbg_data_shifted_bit,X        ; Mark all remaining colors as "used", since we don't need them.
    STA $1A
    
    DEX
    DEX
    TXA
    BPL lbg_palette_check_fill_with_ones    ; Loop until we reach 0
    BRA lbg_palette_check_completely_done

all_gfx_bytes_checked_continue_loop:
    REP #$20                ; 16 bit accumulator
    .LONGA ON

    JMP lbg_check_memory_for_palette_loop

lbg_palette_check_completely_done:
                        ; Now we're completely done with all bytes of graphics data

; $13, $14, $15 and $16 (single-byte values) will store data on which bitmap / palette values to switch. The low 4-bits should contain
; the original value (found in the first 12 entries, but unused), and the high 4-bits should contain the switched value (found in the
; last 4 entries, but used)

lbg_calculate_replacement_colors:

    LDA #0

    SEP #$20                ;8-bit accumulator
    .LONGA OFF

    STA $13                    ; initialize the four replacements to zero
    STA $14
    STA $15
    STA $16

    REP #$20                ;16-bit accumulator
    .LONGA ON

lbg_find_unused_color_pre:

    LDX #0                    ; X will be used to count 1-12 (word data) for the first 12 palette entries

lbg_find_unused_color:

    LDA $1A
    AND >lbg_data_shifted_bit,X        ; Check against the appropriate bitmask, to see if the color is used
    BEQ lbg_find_expanded_color_pre        ; If zero (unused), look for a replacement in palette numbers 13-16
    
    INX
    INX                    ; If not, move to the next palette entry
    TXA
    CMP #24                    ; Check if we've reached the end of the first 12 (non-expanded colors)
    BNE lbg_find_unused_color        ; If not, continue to check
    JMP lbg_done_replacement_colors        ; If so, there are no more unused colors, and we're done.

lbg_find_expanded_color_pre:
    
    TXY                    ; Store the original (1-12) color in Y temporarily
    LDX #24                    ; now X will be used to index the "expanded" colors

lbg_find_expanded_color:

    LDA $1A
    AND >lbg_data_shifted_bit,X        ; Check against the appropriate bitmask, to see if the color is used
    BNE lbg_set_replacement            ; If non-zero (used), store the replacement

    INX
    INX                    ; else, move to the next palette entry
    TXA
    CMP #32                    ; Check if we've reached the last palette entry
    BNE lbg_find_expanded_color        ; If not, continue to loop through the expanded colors
    JMP lbg_done_replacement_colors        ; If so, there are no more used expanded colors, and we are done

lbg_set_replacement:

    SEP #$30                ;8-bit accumulator/index
    .LONGA OFF
    .LONGI OFF

lbg_replace_in_13:

    LDA $13
    BNE lbg_replace_in_14            ; If it's != 0 (value already set), check the next one

    TXA
    LSR                    ; Divide X (the expanded palette index) by 2, to get the palette number
    ASL
    ASL
    ASL
    ASL                    ; Shift left by 4, because we want to store this in the higher four bits
    STA $13

    TYA
    LSR                    ; Divide Y (the original palette index) by 2, to get the palette number
    ORA $13                    ; Transfer the high bits we already stored
    STA $13                    ; Store the total number
    JMP lbg_replace_done

lbg_replace_in_14:

    LDA $14
    BNE lbg_replace_in_15            ; If it's != 0 (value already set), check the next one

    TXA
    LSR                    ; Divide X (the expanded palette index) by 2, to get the palette number
    ASL
    ASL
    ASL
    ASL                    ; Shift left by 4, because we want to store this in the higher four bits
    STA $14

    TYA
    LSR                    ; Divide Y (the original palette index) by 2, to get the palette number
    ORA $14                    ; Transfer the high bits we already stored
    STA $14                    ; Store the total number
    JMP lbg_replace_done

lbg_replace_in_15:

    LDA $15
    BNE lbg_replace_in_16            ; If it's != 0 (value already set), check the next one

    TXA
    LSR                    ; Divide X (the expanded palette index) by 2, to get the palette number
    ASL
    ASL
    ASL
    ASL                    ; Shift left by 4, because we want to store this in the higher four bits
    STA $15

    TYA
    LSR                    ; Divide Y (the original palette index) by 2, to get the palette number
    ORA $15                    ; Transfer the high bits we already stored
    STA $15                    ; Store the total number
    JMP lbg_replace_done

lbg_replace_in_16:

    LDA $16
    BNE lbg_done_replacement_colors        ; If it's != 0 (value already set), we can't do any more replacements

    TXA
    LSR                    ; Divide X (the expanded palette index) by 2, to get the palette number
    ASL
    ASL
    ASL
    ASL                    ; Shift left by 4, because we want to store this in the higher four bits
    STA $16

    TYA
    LSR                    ; Divide Y (the original palette index) by 2, to get the palette number
    ORA $16                    ; Transfer the high bits we already stored
    STA $16                    ; Store the total number
    JMP lbg_replace_done

lbg_replace_done:

    REP #$30                ;16-bit accumulator/index
    .LONGA ON
    .LONGI ON

    PHX
    TYX

    LDA >lbg_data_shifted_bit,X        ; Get the bitmask for the original color
    EOR $1A                    ; Switch this value from unused to used, so we don't replace the same one
    STA $1A

    PLX

    LDA >lbg_data_shifted_bit,X        ; Get the bitmask for the replacement color
    EOR $1A                    ; Switch this value from used to unused, so we don't make the same replacement
    STA $1A

    JMP lbg_find_unused_color_pre        ; Return to the beginning of the loop

lbg_done_replacement_colors:


;======================
; This part of the code goes through each pixel of the sprite, checks if it is one that we need to change, and changes it
; if necessary.

lbg_pixel_changing_loop_pre:


    SEP #$30                ; 8-bit accumulator/index
    .LONGA OFF
    .LONGI OFF

    LDA $13                    ; First, check if we found ANY colors we need to switch
    BNE lbg_pixel_changing_loop_pre_2    ; If we did, continue
    JMP lbg_bitmap_loop_done        ; Otherwise, skip this whole thing

lbg_pixel_changing_loop_pre_2:

    REP #$30                ; 16-bit accumulator/index
    .LONGA ON
    .LONGI ON

    LDA $1C                    ; Load into A the value originally stored in X, viz. $0000, $2000... &c
                        ; depending on which of the four battle characters this is.
    CLC
    ADC #$2000                ; Point to the end of the data we loaded for this character
    STA $10                    ; Store into dp $10-$11, to use as index

lbg_bitmap_outer_byte_loop:

    REP #$30                ; 16-bit accumulator/index
    .LONGA ON
    .LONGI ON

    LDA $10
    AND #$000F                ; Check if this is a multiple of 16
    BNE lbg_bitmap_outer_byte_loop_2    ; if not, don't do anything special
    LDA $10
    SEC
    SBC #16                    ; If so, subtract an additional 16 (to account for the weird bitplane format)
    STA $10

lbg_bitmap_outer_byte_loop_2:
    DEC $10
    DEC $10

    SEP #$30                ; 8-bit accumulator/index
    .LONGA OFF
    .LONGI OFF
    
    LDX #0                    ; X holds an offset for the "color to switch" information. This information is stored
                        ; in direct page memory $13-$16, and X indexes into these values.

lbg_bitmap_bitplane_loop_start:
    LDY #17                    ; Y holds the bitplane (0, 1, 16, or 17). We start at 17 and decrement, for efficiency
                        ; reasons.

    LDA #%11111111                ; $18 holds a cumulative logical-AND of all the bitplanes we've checked so far,
    STA $18                    ; telling us which bits match the palette number we're looking for

    LDA #%10000000                ; $19 holds a bitmask telling us which bit to check for equality in the "color to
    STA $19                    ; switch" data. Starts at #%10000000 because the "extended" palette number is in the
                        ; high bits. Shifts right when Y decreases.

lbg_bitmap_check_replacement:

    LDA >$13,X                ; Load dp byte 13+X, which tells us colors to switch
    BNE lbg_bitmap_check_replacement_cont    ; Only continue if there is a value set here
    JMP lbg_bitmap_outer_byte_done        ; If it's all zero, there are no more replacements to check

lbg_bitmap_check_replacement_cont:
    AND $19                    ; Use the bitmask to check the value of the Nth bit, which corresponds to the Yth
                        ; bitplane
    BEQ lbg_bitmap_check_replacement_not_set    ; If it's zero, we are looking for bits NOT set

lbg_bitmap_check_replacement_set:
    LDA #%00000000                ; Load all 0's, so an EOR will do nothing
    BRA lbg_bitmap_check_replacement_2

lbg_bitmap_check_replacement_not_set:
    LDA #%11111111                ; Load all 1's, so an EOR will flip all bits

lbg_bitmap_check_replacement_2:

    EOR [$10],Y                ; EOR with the gfx byte, current bitplane. We either get the byte, or the negation
                        ; of the byte. This tells us whether each bit's value is "right".
    AND $18                    ; AND with the cumulative check from previous bitplanes.
    BEQ lbg_bitmap_bitplane_loop_done    ; If all bits are 0, there are no matches in this row of gfx data.
    STA $18                    ; Store this value back

    TYA
    CMP #0
    BEQ lbg_bitmap_match_found        ; If we've reached the lowest bitplane, and there are still 1's in $18, we have
                        ; matching pixels.

lbg_bitmap_check_replacement_3:            ; If we haven't reached the lowest bitplane, continue...
    CMP #16
    BNE lbg_bitmap_check_replacement_4
    LDY #1                    ; If y is currently 16, we want to skip down to 1, due to the format of gfx data
    BRA lbg_bitmap_check_replacement_5

lbg_bitmap_check_replacement_4:
    DEY                    ; Otherwise, just decrement

lbg_bitmap_check_replacement_5:
    LSR $19                    ; Finally, shift the bitmask in $19 right
    JMP lbg_bitmap_check_replacement    ; And reiterate loop

lbg_bitmap_match_found:                ; If some of the bits in $18 are still on after checking all bitplanes, a match
                        ; Now switch the matching bits in all bitplanes

    LDY #0                    ; Y holds the bitplane offset
    LDA #1
    STA $19                    ; $19 holds a bitmask

lbg_bitmap_match_found_loop:
    LDA >$13,X                ; load the "color to replace" data
    AND $19                    ; check the appropriate bit with the bitmask
    BEQ lbg_bitmap_match_found_loop_not_set

lbg_bitmap_match_found_loop_set:        ; If the bit is supposed to be 1
    LDA [$10],Y                ; Load the data for this bitplane
    ORA $18                    ; Turn all the "matched" bits on
    STA [$10],Y                ; Store it back
    BRA lbg_bitmap_match_found_loop_2    ; And continue

lbg_bitmap_match_found_loop_not_set:        ; If the bit is supposed to be 0
    LDA #%11111111
    EOR $18                    ; Load the logical negation of the "matched" bits
    AND [$10],Y                ; AND this with the data, setting all the matched bits to 0
    STA [$10],Y                ; store it back
                        ; and continue
lbg_bitmap_match_found_loop_2:
    INY
    TYA
    CMP #18
    BEQ lbg_bitmap_bitplane_loop_done    ; If we've surpassed 17, we're done

    CMP #2
    BNE lbg_bitmap_match_found_loop_3    ; If we've just surpassed offset 1, skip to offset 16
    LDY #16

lbg_bitmap_match_found_loop_3:
    ASL $19                    ; Shift the bitmask
    JMP lbg_bitmap_match_found_loop        ; continue to loop

lbg_bitmap_bitplane_loop_done:
    INX                    ; Increment the offset of the "color to switch" data
    TXA
    CMP #4                    ; Check if we've done all four bytes of data
    BEQ lbg_bitmap_outer_byte_done        ; If so, we're done with this loop
    JMP lbg_bitmap_bitplane_loop_start    ; Otherwise, continue

lbg_bitmap_outer_byte_done:

    REP #$30                ; 16 bit accumulator/index
    .LONGA ON
    .LONGI ON

    LDA $10                    ; Load the data pointer
    CMP $1C                    ; Have we reached the start of this character's gfx data?
    BEQ lbg_bitmap_loop_done
    JMP lbg_bitmap_outer_byte_loop         ; If not, then loop

lbg_bitmap_loop_done:

    REP #$30                ; 16 bit accumulator/index
    .LONGA ON
    .LONGI ON

    PLY
    PLX
    PLA                    ; Pull everything we pushed onto the stack
    PLP

;======================
; Finally, first section of new code is done, and we pick up where we left off

    .LONGA OFF


C1_3E04:    ;DA          PHX
    PHX
C1_3E05:    ;AA          TAX
    TAX
C1_3E06:    ;BDAE2E      LDA $2EAE,X
    LDA $2EAE,X                ; This checks the character ID...
C1_3E09:    ;C90E        CMP #$0E
    CMP #$0E                ; I believe this section has something to do with handling the palette in the
                        ; special case where the character has imp status, and possibly other special status.
C1_3E0B:    ;D012        BNE $3E1F
    BNE C1_3E1F                ; In any case, we skip it in the general case...
C1_3E0D:    ;BDC62E      LDA $2EC6,X
    LDA $2EC6,X
C1_3E10:    ;C901        CMP #$01
    CMP #$01
C1_3E12:    ;D00B        BNE $3E1F
    BNE C1_3E1F
C1_3E14:    ;ADA01E      LDA $1EA0
    LDA $1EA0
C1_3E17:    ;2908        AND #$08
    AND #$08
C1_3E19:    ;F004        BEQ $3E1F
    BEQ C1_3E1F
C1_3E1B:    ;FA          PLX
    PLX
C1_3E1C:    ;7B          TDC
    TDC
C1_3E1D:    ;8005        BRA $3E24
    BRA C1_3E24
C1_3E1F:    ;FA          PLX
    PLX
C1_3E20:    ;BF2BCEC2    LDA $C2CE2B,X
    LDA >$C2CE2B,X                ; $C2CE2B: Battle Character Palette Assignments (1 byte each)
C1_3E24:    ;C220        REP #$20
    REP #$20                ; Accumulator back to 16 bit
    .LONGA ON
C1_3E26:    ;0A          ASL A
    ASL A                    ; Again ASL 5 times. A is the character battle palette assignment. This means we will
C1_3E27:    ;0A          ASL A        ; multiply that number by 32 when using it as an offset.
    ASL A
C1_3E28:    ;0A          ASL A
    ASL A
C1_3E29:    ;0A          ASL A
    ASL A
C1_3E2A:    ;0A          ASL A
    ASL A
C1_3E2B:    ;AA          TAX         ; store the offset in  X
    TAX
C1_3E2C:    ;7B          TDC
    TDC
C1_3E2D:    ;E220        SEP #$20
    SEP #$20                ; Long A off again
    .LONGA OFF
C1_3E2F:    ;68          PLA
    PLA
C1_3E30:    ;0A          ASL A
    ASL A
C1_3E31:    ;0A          ASL A
    ASL A
C1_3E32:    ;0A          ASL A
    ASL A
C1_3E33:    ;0A          ASL A
    ASL A
C1_3E34:    ;0A          ASL A
    ASL A
C1_3E35:    ;A8          TAY
    TAY
C1_3E36:    ;5A          PHY
    PHY
C1_3E37:    ;A918        LDA #$18
    LDA #$18                ; Load the number #$18 -- 24 in decimal. Probably for 12 palette colors.
C1_3E39:    ;8510        STA $10
    STA $10                    ; $10 is being used as the loop index
C1_3E3B:    ;BF0063ED    LDA $ED6300,X
    LDA >$ED6300,X
C1_3E3F:    ;99AD81      STA $81AD,Y
    STA $81AD,Y
C1_3E42:    ;E8          INX
    INX
C1_3E43:    ;C8          INY
    INY
C1_3E44:    ;C610        DEC $10
    DEC $10
C1_3E46:    ;D0F3        BNE $3E3B
    BNE C1_3E3B

; new code for switching the palette assignments

lbg_new_switch_palette:

    PHP                    ; push status register

    REP #$30
    .LONGA ON
    .LONGI ON

    PHA                    ; push A
    PHX                    ; and X
    PHY                    ; and Y

; X and Y have both been incremented #$18 (24) times, and we want to restore them to their original state
    TXA
    SEC
    SBC #$18
    STA $1A

    TYA
    SEC
    SBC #$18
    STA $1C

    LDA $13                    ; load the first switcheroo, located in $13
    AND #%0000000011111111            ; we want to use only the low byte
    LSR
    LSR
    LSR
    LSR                    ; shift right 4 times, to get the "expanded palette" value stored in the upper bits
    ASL                    ; shift left, since it's a word per color
    CLC
    ADC $1A
    TAX

    LDA $13
    AND #%00001111                ; use only the low bits, which have the "original palette" information
    ASL                    ; again, multiply by 2
    CLC
    ADC $1C
    TAY

    SEP #$20                ; Long A off
    .LONGA OFF
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    INX
    INY
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    REP #$20                ; Long A off again
    .LONGA ON

; Repeat for $14
    LDA $14                    ; now the one located in $14
    AND #%0000000011111111            ; we want to use only the low byte
    LSR
    LSR
    LSR
    LSR                    ; shift right 4 times, to get the "expanded palette" value stored in the upper bits
    ASL                    ; shift left, since it's a word per color
    CLC
    ADC $1A                    ; Add to this the "starting" value of X, stored earlier
    TAX

    LDA $14
    AND #%00001111                ; use only the low bytes, which have the "original palette" information
    ASL                    ; again, multiply by 2
    CLC
    ADC $1C                    ; Add the "starting" value of Y
    TAY

    SEP #$20                ; Long A off
    .LONGA OFF
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    INX
    INY
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    REP #$20                ; Long A off again
    .LONGA ON

; Repeat for $15
    LDA $15                    ; now the one located in $15
    AND #%0000000011111111            ; we want to use only the low byte
    LSR
    LSR
    LSR
    LSR                    ; shift right 4 times, to get the "expanded palette" value stored in the upper bits
    ASL                    ; shift left, since it's a word per color
    CLC
    ADC $1A                    ; Add to this the "starting" value of X, stored earlier
    TAX

    LDA $15
    AND #%00001111                ; use only the low bytes, which have the "original palette" information
    ASL                    ; again, multiply by 2
    CLC
    ADC $1C                    ; Add the "starting" value of Y
    TAY

    SEP #$20                ; Long A off
    .LONGA OFF
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    INX
    INY
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    REP #$20                ; Long A off again
    .LONGA ON

; Repeat for $16
    LDA $16                    ; now the one located in $16
    AND #%0000000011111111            ; we want to use only the low byte
    LSR
    LSR
    LSR
    LSR                    ; shift right 4 times, to get the "expanded palette" value stored in the upper bits
    ASL                    ; shift left, since it's a word per color
    CLC
    ADC $1A                    ; Add to this the "starting" value of X, stored earlier
    TAX

    LDA $16
    AND #%00001111                ; use only the low bytes, which have the "original palette" information
    ASL                    ; again, multiply by 2
    CLC
    ADC $1C                    ; Add the "starting" value of Y
    TAY

    SEP #$20                ; Long A off
    .LONGA OFF
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    INX
    INY
    LDA >$ED6300,X                ; load the expanded color...
    STA $81AD,Y                ; ...and replace the color at the correct location
    REP #$20                ; Long A off again
    .LONGA ON

    PLY
    PLX
    PLA
    PLP

; done with palette switching code

C1_3E48:    ;FA          PLX
    PLX
C1_3E49:    ;FEC461      INC $61C4,X
    INC $61C4,X
C1_3E4C:    ;60          RTS
    RTL                    ; changed to rtl from rts

    .ORG ADDRESS_BEGIN_DATA
;lbg_data_bitmask
    .WORD %0000000000000001, %0000000000000010, %0000000000000100, %0000000000001000, %0000000000010000, %0000000000100000, %0000000001000000, %0000000010000000
;lbg_data_shifted_bit:
    .WORD %1, %10, %100, %1000, %10000, %100000, %1000000, %10000000, %100000000, %1000000000, %10000000000, %100000000000, %1000000000000, %10000000000000, %100000000000000, %1000000000000000
Reply


Messages In This Thread
RE: Patch: allowing use of "reserved" palette colors for player characters - by Eggers - 08-11-2013, 06:40 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Is there a way to change the starting abilities of the player characters in the GBA ? transterra96 3 2,695 09-21-2021, 04:22 PM
Last Post: transterra96
  Glitchy shop sprite (Expanded Palette Patch) Febreeze 1 1,854 08-11-2020, 12:50 PM
Last Post: Gi Nattak
  Disable magic while still allowing Esper equip boxedj 1 2,441 04-24-2020, 04:21 PM
Last Post: C-Dude
Rogue Fix for 2-player Blitz bug (co-op)? Merudo 9 12,786 11-29-2018, 04:26 AM
Last Post: Turbotastic

Forum Jump:


Users browsing this thread: 2 Guest(s)