If this does indeed cause a more uniform distribution as opposed to what before could be described as 12.5% (0-63), 37.5% (64-127), 37.5% (128-191), 12.5% (192-255), then it should solve the problem outright. I can't imagine it will harm any other mechanics in the game, as RNG should aim to be uniform anyway in this game's combat systems. Would you like to keep this under review as per your notes about avoiding that free space, or do you want me to slap it on the first post?
The actual distribution was 43/256 (16.8%), 85/256 (33.2%), 85/256 (33.2%), 43/256 (16.8%). Scanning the disassembly for uses of $42 (the RNG LUT index), I see a lot of places where the game sets it to a specific value (e.g. LDA #$08 STA $42), which kind of makes the lookup table not so random anymore, so it's possible there might be deeper RNG issues that simply fixing the overall distribution wouldn't address. Might as well add it to the first post for now; if I get around to analyzing the couple of calls where I can't easily verify that X <= A and find out that assuming the upper bound really is not smaller than the lower bound is safe, then I can make a significant code size reduction later.
Meanwhile, I decided to take a look at all the blocks of free space available in ROM before we start sticking our patches in them.
Caveat emptor: the only reason those blocks are marked as "free" is because I saw a run of identical bytes that hadn't been logged by taotao's CDL file and didn't have any immediately obvious pointers into them. It is in no way a guarantee that they are actually unused, just an educated guess.
Is everyone happy with ca65 or is there another preferred assembler I should shoot for?
For no particularly strong reason, I've been using Asar for generating patches, which I hear is not one of ca65's strong points. Asar's not the greatest for 6502 as it doesn't complain about me accidentally mixing 65816 with 6502 ("INC A" and "BRA" are my weaknesses
) and disabling its default SNES memory mapping has some unwanted side effects like also disabling bounds checking, so I'm not super attached to it.
P.S. Welcome back!
Which reminds me, did anyone weigh in on shifting that code up three spaces and adjusting pointers accordingly?
If you can find all the pointers, sure, go right ahead. I've automatically labelled all the explicit direct control flow sources/targets and manually identified hopefully most/all of the indirect jumps, but there are some calculated and otherwise sneaky ones in there such as dynamically changing the NMI vector at $0100, pushing a return address before JMP to some routine that ends with RTS, or just plain writing the bytes for JMP $addr to RAM and executing that.
Curious issue... [...] with spells at least, it appears some other spell counters are raising besides the one being used.
One thing to note is that you aren't saving and restoring the registers and processor flags that you're modifying in your new routine. Is that a problem? Well, it depends. When the original code does stuff like this:
0x031E68|$0C:$9E58:FE F7 7C INC $7CF7,X ; Character #1 spell slot #1 battle use counter
0x031E6B|$0C:$9E5B:A6 9E LDX $9E
0x031E6D|$0C:$9E5D:A0 2A LDY #$2A
0x031E6F|$0C:$9E5F:B1 80 LDA ($80),Y
0x031E71|$0C:$9E61:C9 15 CMP #$15
then it doesn't matter what state you leave A, X, Y, or C/Z in, but when the original code does stuff like this:
0x031E75|$0C:$9E65:FE 3F 7D INC $7D3F,X ; Character #1 black magic use counter
0x031E78|$0C:$9E68:D0 03 BNE $9E6D
then it makes a difference what state you leave Z in. Similarly, when the original code does stuff like this:
0x03261D|$0C:$A60D:FE 3B 7D INC $7D3B,X ; Character #1 counter for times magically attacked by enemy
; control flow target (from $A537, $A56E, $A5A2, $A5AB, $A5CA, $A609)
0x032620|$0C:$A610:A0 2B LDY #$2B
0x032622|$0C:$A612:91 44 STA ($44),Y
then returning from your new routine with a different value in A than when you entered results in unintentional changes to other things in the game.