08-11-2013, 06:40 PM
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:
As before, you also have to jump to the new code from the old function.
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.)
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