News: 11 March 2016 - Forum Rules, Mobile Version
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia

Author Topic: Final Fantasy 1 Magic Upgrade Idea  (Read 5035 times)

KingMike

  • Forum Moderator
  • Hero Member
  • *****
  • Posts: 6175
  • *sigh* A changed avatar. Big deal.
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #20 on: March 31, 2017, 12:28:41 pm »
Note that AND doesn't do what you might be thinking it would.
It's commonly used to limit values to a certain range, but it's only useful when that range happens to be a power-of-2 range.

ie. AND #$1F to limit to 32 values (0 to 1F)
Code: [Select]
1F is 00011111Note that the resulting value is all 0s starting from the left and 1s starting from the right. AND only works for limiting to a range when the value follows this pattern.
(alternatively it can be used with binary values of 1s on the left and 0s to the right to round down, ie AND #$C0 (11000000) to round down to the lower nearest 0x20 due to the bits through the 0x20 bit being forced to 0)

What AND #$14 will actually do is keep the 0x10 and 0x4 bits of the value and set the rest of the bits to 0

Code: [Select]
76 01110110
14 00010100
   --------
   00010100 = 14. It is not modulus, the answer is not 0x12 (18 dec, the remainder) or 100.
The corresponding result bit is 1 if the corresponding bits in the compared values are both 1, 0 otherwise.
« Last Edit: March 31, 2017, 12:34:40 pm by KingMike »
Quote
Sir Howard Stringer, chief executive of Sony, on Christmas sales of the PS3:
"It's a little fortuitous that the Wii is running out of hardware."

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #21 on: March 31, 2017, 07:21:33 pm »
Hah, that's exactly what I did! I just didn't think it was working because the other dumb stuff I was trying to do at the same time... That's a very good and useful lesson though! So @labels are like footnotes in books, almost... Every page can have its own¹ or², but that's wiped out on the next page...

That's an interesting way to think about it.  Haha.  Yeah you get the idea.
"Scope" is a concept that exists in some form in pretty much every programming language -- so I just think of it in those terms.  But if you're not familiar with it... that's a fine way to think about it  :)


Quote
Gotta admit, I'm not a big fan of the random aspect with healing.

Well you are changing the code.  If you don't want it, you can easily cut it out.  ;)

Quote
I noticed that it chooses the effectivity before even asking if you want to use it, so maybe I can slip in the "hp_recovery" byte into the text, so you know how much it'll heal before you commit... That might be too much effort, if I can simply take it out entirely.

Yeah mucking with drawing code is a whole 'nother beast.  I'm not going to say "don't do it", but if you decide to... just know that it'll be a lot of work.

Quote
And I THINK I worked out the formula here... How's this look?

See KingMike's reply --- AND doesn't quite do what you're hoping.

If you do AND #$14, the only possible values you'll get are $00, $04, $10, or $14.  Not quite the full 0-20

Apart from that it looks great to me.  =)

Quote
Hm, I don't think its getting the character level, though. The debugger says: ADC $6126, X @ $61AE = #$00
Character's in the third slot, X is 88.

Ack!!!  Then I was wrong about X!!!

You want X to be either $00, $40, $80, or $C0.  That's how out-of-battle character stats are meant to be indexed.  Lemme look at the code again...

*looks*

Okay -- yeah I was totally wrong.  X isn't prepped like I thought it was.  I saw it indexed further down, but I missed an LDX.  Whooops!

So you'll have to get the character index if you want to get the caster's stats.  You can pull it out of 'mp_required', which has the index to the caster's MP slot.  Basically you can just chop off everything but the high 2 bits:

Code: [Select]
LDA mp_required
AND #$C0
    ; A is now your index (00, 40, 80, or C0)
    ; from here you can TAX it or whatever


Quote
So what exactly is the framecounter? I see it popping up all over but haven't found the description of how it works just yet.

It's just a byte that is incremented every frame.  It has no actual significance other than it's constantly changing (and therefore can be used to simulate randomness).

For example... in the HEAL code, what frame you decide to cast the spell on is factored into how much you'll get healed.  But the user doesn't KNOW what frame they're casting on.... and the frames move by so fast that it's unlikely a human being would be able to press the button at the exact right time even if they knew when that time was.  Therefore, to the player, it appears as though the number is randomized.


Quote
And, does spell accuracy have any effect?

Out of battle?  No.

In battle?  For damage spells I think it's effectively a critical hit chance.  For insta-kill or status spells I think it impacts their chance to be successful.  I can't remember exactly.

Quote
If I wanted negative effects to have a higher chance of working, would increasing this byte work, or is it only about enemy's HP or something?

It depends on the spell.  If I remember correctly, spells like XXXX don't care about anything other than elemental resistance and remaining HP.  But spells like RUB I think have a randomized chance of working that is based on that value.

If you want to see for yourself, search for "@jumptable_MagicEffect:" in Bank C.  That's the name of all the routines that actually perform in-battle magic effects.  You can examine each of those routines to see how the accuracy factors in (if at all)


Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #22 on: April 03, 2017, 01:33:48 pm »
Quote
Note that AND doesn't do what you might be thinking it would.
Wow, yeah... that hurts my brain. But I think I get it...! I could make a fourth LUT for this, so 0-1 orbs is 16, 2-3 orbs is 32, and all 4 is 64... Thanks for catching that!

Quote
So you'll have to get the character index if you want to get the caster's stats.  You can pull it out of 'mp_required', which has the index to the caster's MP slot.  Basically you can just chop off everything but the high 2 bits:
I didn't see that page 2 of this thread existed, so I kept editing my last post. Is simply doing "ADC ch_level-8, X" a better way? Save some bytes?

For my own sanity, I think now's a time to make a list of what I want to change so far...

1. Effectivity boost for damage and healing in battles -- Pretty much complete
2. Effectivity boost for healing in the menu -- Pretty much complete
3. Accuracy boost for status effects in battle -- Framework in place, should be simple
4. Fix TMPR/SABR bug, make XFER work on enemies
5. Make LIFE and SOFT work in battle
6. Fever dream idea: Take the space freed up by consolidating CURE and HEAL magic to make a spell that disables random battles for a short period??? Or one to swap dungeon palettes so it acts as a light source???
7. Remove SFX from dialogue box (whoop! wheeww...), maybe keep dungeon music from resetting too.
8. Finish map sprites... (at this rate it might be easier for me to make the whole bloody thing look like FF6!)
9. Replace Marsh Cave and Floating Castle music with my own compositions. They always bugged me.
10. Start re-working the dialogue and maps...

Phew.

Now before I do anything more, I need to find a better debugger. FCEUX is great for play-testing, but its a pain in the neck when I want to find and compare the hex code of my stuff versus the original game versus my Hackster-ravaged game... Is there a nice program, preferably for Windows 64-bit (not a deal-breaker, I'll be using a 32-bit laptop soon), that can show me the ASM code of the raw game files? Like some kind of... DTE Table, I think the term is? Yeah, just a .tbl file. I think I can whip one up actually...

http://www.e-tradition.net/bytes/6502/disassembler.html A non-browser version of this would be great, with the ability to compare files. Notepad++ is very finicky when it comes to comparing in hex, and I couldn't figure out how to load a table to see the game's text data properly. I prefer HxD, but that goes byte by byte when comparing differences. And I can't figure out how to load tables with it either.

So far, to find the code I've changed with FCEUX, I've been adding '.byte "MARKER" ' kind of things before compiling, so I can search that out, snip the hex from between the markers...

But then there's the problem of injecting that into my Hackster-ravaged game, right? If I snip out all that useless code in Bank C, in a file that's already hard-coded to look at certain places for things, its gonna look in the wrong place unless I place the code I make perfectly into the space I cut out?
« Last Edit: April 03, 2017, 02:17:44 pm by Jiggers »

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #23 on: April 03, 2017, 09:04:45 pm »
Is simply doing "ADC ch_level-8, X" a better way? Save some bytes?

If you're getting X from mp_required, no -- don't do that.  The "off by" amount will be different depending on what level spell it is.  IE, you'll need -8 for a L1 spell, but -9 for a L2 spell.


Quote
4. Fix TMPR/SABR bug, make XFER work on enemies

FWIW, every time I found a bug when commenting that code, I labelled it with 'BUG' or 'BUGGED'... and gave notes about what was bugged as well as a potential way to fix it.  So if you want, you can do a grep or a find-in-files for 'BUG' to hunt some of this stuff down.

Quote
Now before I do anything more, I need to find a better debugger. FCEUX is great for play-testing, but its a pain in the neck when I want to find and compare the hex code of my stuff versus the original game versus my Hackster-ravaged game... Is there a nice program, preferably for Windows 64-bit (not a deal-breaker, I'll be using a 32-bit laptop soon), that can show me the ASM code of the raw game files? Like some kind of... DTE Table, I think the term is? Yeah, just a .tbl file. I think I can whip one up actually...

So... what you're looking for sounds like a 6502 disassembler (not a debugger).

There are several available on this site.  I doubt there's much difference between any of them.

I use this one:  https://www.dropbox.com/s/qzqpvbnznkypngq/6502d.zip?dl=0

Though I wouldn't call it great.  I mostly use it out of habit just because I haven't bothered to try anything else since I found it.

Quote
with the ability to compare files.

I use Beyond Compare for this.  It compares binary files, text, directories, you name it.  Only downside is it's not free.  But if you're willing to shell out a few bucks, it's a great program.  I use it all the time -- but that's mostly because of emu dev stuff where I have to compare tons of tracelogs.  You probably won't get as much use out of it as I do.

I'm sure there are free alternatives, too, but I can't speak to any of them.  Search around for 'text comparison' program and look for ones that have binary file support.

Quote
I prefer HxD, but that goes byte by byte when comparing differences. And I can't figure out how to load tables with it either.

Sadly, HxD does not have table file support, which is the only thing stopping it from being hands-down the best hex editor I've ever used.   =P


Quote
But then there's the problem of injecting that into my Hackster-ravaged game, right? If I snip out all that useless code in Bank C, in a file that's already hard-coded to look at certain places for things, its gonna look in the wrong place unless I place the code I make perfectly into the space I cut out?

Yeah, that's definitely a potential problem.  I don't have a good solution other than "insert as many bytes as you remove so you don't move things around".  =/

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #24 on: April 05, 2017, 05:24:34 pm »
If you're getting X from mp_required, no -- don't do that.  The "off by" amount will be different depending on what level spell it is.  IE, you'll need -8 for a L1 spell, but -9 for a L2 spell.
Hm. I thought I tested it with a Level 1 and a Level 3 spell, and both worked? Trying it again... So Heal at Level 3 works, but Cure at level 1 doesn't.

For Cure: $611E, X @ $61DE = #$00
For Heal: $611E, X @ $61E6 = #$01 (Character is level 2)

-8 works for Heal, then...

But also, I don't think it gets X from mp_required until after. First it does this:

 
Code: [Select]
@HaveMP:
    LDA submenu_targ       ; get character ID
    LSR A
    ROR A
    ROR A                  ; shift to get usable character index
    ORA cursor             ; ORA with cursor to get index to spell
    TAX           

Then the magic-list jumping, my code pops up immediately once a healing spell is selected, then it does another TAX thing for "LDA ch_ailments, X      ; get target's OB ailments", then it does mp_required with X.

If I take out the -8 for Cure: $611E, X @ $61A6 = #$01

So I think... because this is the hard-coded part anyway, I just have to make sure it'll match. Currently, in my game, the two healing spells are Level 1 and Level 2, so I'll have to experiment to find out how much to subtract for the level 2 spells, and leave Cure's code alone...

Quote
FWIW, every time I found a bug when commenting that code, I labelled it with 'BUG' or 'BUGGED'... and gave notes about what was bugged as well as a potential way to fix it.  So if you want, you can do a grep or a find-in-files for 'BUG' to hunt some of this stuff down.


Yep! This was the plan. :D

Quote
So... what you're looking for sounds like a 6502 disassembler (not a debugger).
I use this one:  https://www.dropbox.com/s/qzqpvbnznkypngq/6502d.zip?dl=0
Oh, I tried that... I wanted something with its own GUI for editing, but--yeah, this works fine for what I wanted to do with things, actually! Then I can use Notepad++ for the comparing.

Quote
Yeah, that's definitely a potential problem.  I don't have a good solution other than "insert as many bytes as you remove so you don't move things around".  =/

I think I settled on the crazy idea of disassembling my Hackster work, then copying the changes over, and re-assembling it all. That way I don't have to worry about shifting things around, and can hack and slash as much as I want, knowing all my own stuff will be safe. I'll just need to make sure I'm 100% done with what Hackster can do for me...

Already I ran into a confusion, though. Looks like its in bank_00.dat's area of stuff?

Original game:
000003B0: 16 16 12 17 27 12 16 16 30 30 27 12 16 16 16 16 27 12 16 16 16 30 27 13
My game:
000003B0: 1C 1C 1C 1C 1A 1A 1A 1A 0A 0A 1C 1C 1A 1A 1C 1C 1A 1A 1A 1A 07 07 07 07

I JUST ran into this and haven't really dug into what information is stashed away there, though. Off the top of my head I'm guessing it might be related to choosing 12 classes at the start, the patch that fixes the graphics...? Or else some other data table that Hackster poked... I don't remember reading anywhere about what the .dat files are.

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #25 on: April 05, 2017, 05:39:54 pm »
First it does this:

So... it looks like it's getting the TARGET character's stats?  Not the casting character?

I'll leave it up to you to figure out  ;P

Quote
000003B0

That's the palette entries for the "mapman" graphics.

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #26 on: April 08, 2017, 09:32:36 am »
Palettes! Good, good. I figured out the next big block of changes was starting stats! But around 0000087C to 0000090C, there's a lot of 03s in the original, which in mine, are all 02s. Some of them are still 03, but the majority are not. Still stumped about that.

Meanwhile, taking a short break from the code stuff to flesh out my design documents a bit more. I should probably start a thread in the Personal Projects, huh?

I just have one or two or three more big questions... For small maps like Matoya's cave, does filling in the blank spaces take up more bytes? Or are all the maps set to take up the same amount of bytes? And if I were to make it so the edges of the ocean warped you back to a fixed point on the ocean, would that 1: put the character sprite on the ocean, or warp the whole ship, and 2: would each warp tile take up more bytes? Or should I just find another way to keep the world map from looping over? (I don't need the airship, so that won't be an issue. I just want to keep the scope of the game small, one continent and a few islands.)


Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #27 on: April 08, 2017, 10:22:50 am »
But around 0000087C to 0000090C, there's a lot of 03s in the original, which in mine, are all 02s. Some of them are still 03, but the majority are not. Still stumped about that.

This is a minor bug in Hackster.

The bytes in question are tile properties:
00 = Normal tile
01 = Can't walk on this tile
02 = This tile is a door/shop
03 = Both (can't walk on it & it's a door/shop)

Now... you'd think that it wouldn't make sense to have a door that you couldn't walk on... because then it'd just be a wall, right?  So Hackster never writes '03' for this value.

HOWEVER, the way the game is coded, the player can walk on doors *even if* the '01' bit is set.  NPCs, however, obey the 01 bit and ignore the 02 bit entirely.

So it ends up working like this:
00 = Normal tile
01 = Nobody can walk on this tile
02 = Door/shop that anyone can walk on
03 = Door/shop that the player can walk on, but NPCs cannot.


So in FF1 hacks, you might see NPCs walk into shop doorways, or occasionally walk though a closed door on a map -- where this never happened in the original game.  That's Hackster's fault.



Quote
Meanwhile, taking a short break from the code stuff to flesh out my design documents a bit more. I should probably start a thread in the Personal Projects, huh?

*shrug*

FWIW I pay less attention to that forum so I'm less likely to notice questions posted there.  Though you could have a project page in there, but still post questions in this board or the ROM-hacking board.

Quote
For small maps like Matoya's cave, does filling in the blank spaces take up more bytes?

Maps are compressed with basic RLE in "book order".  I don't think "book order" is an official term... but I mean it's ordered how you'd read a book:  top to bottom, left to right.  The way RLE works, but more identical tiles you fit next to each other, the smaller it will compress.

Code: [Select]
AAAAAABBBBBB   <- this

AAABBBAAABBB   <- will compress better than this

ABABABABABAB   <- and this will not compress at all

Since it's "book order"... this:
Code: [Select]
AAAAAAAAAAAA
AAAAAAAAAAAA
AAAAAAAAAAAA
BBBBBBBBBBBB
BBBBBBBBBBBB
BBBBBBBBBBBB
Will compress better than this:
Code: [Select]
AAAAAABBBBBB
AAAAAABBBBBB
AAAAAABBBBBB
AAAAAABBBBBB
AAAAAABBBBBB
AAAAAABBBBBB


So that first floor of Bahamut's cave that's a long vertical stretch of nothing?  If that was a long horizontal stretch of nothing it would compress to a smaller size.


In general, this basic RLE is a pretty lousy compression method, though.  Pretty much the more detail you add the worse it gets.

Quote
And if I were to make it so the edges of the ocean warped you back to a fixed point on the ocean, would that 1: put the character sprite on the ocean, or warp the whole ship, and 2: would each warp tile take up more bytes?

Well there are no overworld->overworld teleports.  And even if there were, I'm pretty sure the game doesn't check for teleports when you're in the ship.

But if you changed it to make both of those things happen -- you'd still be in the ship... and no, the tiles wouldn't take up any additional bytes.


Quote
Or should I just find another way to keep the world map from looping over? (I don't need the airship, so that won't be an issue. I just want to keep the scope of the game small, one continent and a few islands.)

My approach would be to keep land at least a "half-screen" away from the edge of the world.  That way if the user ever gets to the very edge, they will always have a screen full of water.

Then, in the player movement code, check for when your X/Y coordinates wrap.. and simply prevent them from wrapping.

So if the player tries to just go left forever --- it'll appear as though they're going left forever, but really they won't actually be moving any further than the left-edge of the world.


The "screen full of water" is important, as putzing around with the character position would normally mess up drawing routines and cause the wrong tiles to be drawn.  However, if everything on-screen is water, then it'll look perfectly normal.

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #28 on: April 20, 2017, 08:05:08 pm »
I'm back!
Ahhh, I'm so glad I asked about that. Good to know I can ignore it.

I found pretty much every other change I've made (thanks to Anomie's other disassembly stuff!), and am working on how to insert it into the Disassembly. I made my own .bin for character and weapon sprites! So I can just snip them from the rom after editing with Hackster, and copy-paste, no hassle.

There's still a lot of stuff confusing me, but I'm muddling through, and passing on what I'll need to ask about later. This one, I felt was worth asking about now: NewGame_LoadStartingStats  [$C76D :: 0x3C77D]

So I took out all that CMP stuff where it only gives starting mana to RM, WM, and BM, and used the padding in the lut_ClassStartingStats to tell it how much mana to give each class.

Code: [Select]
LDA lut_ClassStartingStats+$B, Y
    STA ch_curmp, X
    STA ch_maxmp, X

This works great! But I also want to grant some level 2 mana at start, so I copy that again...

Code: [Select]
LDA lut_ClassStartingStats+$C, Y
    STA ch_curmp+9, X
    STA ch_maxmp+9, X

Only that just gives the Max MP into Level 2, not the Current MP... I'll keep fiddling!

Oh, yeah, the other thing. I want to have 12 classes at start, and I got this working through pure hex magic thanks to everyone else's work on it, but I'm not sure how to convert the hex changes to assembly... As far as I could tell at the time I was doing the hex comparisons with my hack and the original, this chunk is the only part that's different, everything around it was the same.

Code: [Select]
Original:
A9 09 20 03 FE AD 02 20
A9 10 8D 06 20 A9 00 8D
06 20 A9 90 85 11 A9 00
85 10 A2 0C 20 65 E9 A9
AC 85 11 A2 04 4C 13 EB
A9 09 20 03 FE AD 02 20
A9 10 8D 06 20 A9 00 8D
06 20 85 10 AD 00 61 20
1E EB AD 40 61 20 1E EB
AD 80 61 20 1E EB AD C0
61 20 1E EB A9 A8 85 11
A2 08 20 67 E9 20 99 EB
A9 0E 4C 03 FE 0A 18 69
90 85 11 A2 02 4C 65 E9
Code: [Select]
My Game::
20 17 EB A9 90 85 11 A9
00 85 10 A2 01 20 65 E9
E6 11 A5 11 C9 A8 D0 F3
A9 AC 85 11 A2 04 4C 01
EB EA EA 20 17 EB A9 00
85 10 AD 00 61 20 0C EB
AD 40 61 20 0C EB AD 80
61 20 0C EB AD C0 61 20
0C EB A9 A8 85 11 A2 08
20 67 E9 20 99 EB A9 0E
4C 03 FE 0A 18 69 90 85
11 A2 02 4C 65 E9 AD 02
20 A9 10 8D 06 20 A9 00
8D 06 20 A9 09 4C 03 FE

I'm not sure where to begin... Could I just make an INCBIN line for it, and nevermind the ASM, or would that get wonky without the fancy labels the rest uses?

_________

Ahhh, and one last thing for now... Wanted to make going in and out of the menu faster, so I commented out all this palette cycling stuff at [$D946 :: 0x3D956]. The new version just does this:

Code: [Select]
CyclePalettes:
LDA #$0
STA $400C ; hopefully silences noise track
JSR WaitVBlank_NoSprites  ; wait for VBlank, and kill all sprites
LDA #BANK_MENUS   ; swap to menus bank
JMP SwapPRG_L     ; and exit

The problem is, there's a slight clicky, thuddy noise, but only when you're on the ship and its been playing the ocean wave sfx. I haven't tried with the airship, but my guess is it'd be there, too. Any ideas? Am I muting the noise track correctly, or not in the right place?
« Last Edit: April 21, 2017, 12:52:36 am by Jiggers »

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #29 on: April 21, 2017, 01:07:50 am »
Only that just gives the Max MP into Level 2, not the Current MP... I'll keep fiddling!

You want +1, not +9.

ch_curmp+0 is L1
ch_curmp+1 is L2
ch_curmp+2 is L3
etc


Quote
Oh, yeah, the other thing. I want to have 12 classes at start, and I got this working through pure hex magic thanks to everyone else's work on it, but I'm not sure how to convert the hex changes to assembly... As far as I could tell at the time I was doing the hex comparisons with my hack and the original, this chunk is the only part that's different, everything around it was the same.
[snip]
I'm not sure where to begin... Could I just make an INCBIN line for it, and nevermind the ASM, or would that get wonky without the fancy labels the rest uses?

This is a popular change.

So... actually allowing 12 classes at start is a single byte change.  In Bank E, 'DoPartyGen_OnCharacter', it does a CMP #6 to wrap the character.  Just change that 6 to however many classes you want.

HOWEVER, there will be graphics glitches for the extra classes in the party creation screen, since only the graphics for the first 6 classes have been loaded.

The patch I made to fix those glitches is here:  https://www.dropbox.com/s/4frno4jzuraykmv/FixForTwelveChars.ips?dl=0
But I assume you already have that and are just looking at how to recreate it.

So here's the changes, with explanation:

What this does overall

If you look at the pattern tables (PPU viewer in FCEUX) during the party creation screen, you'll see all 6 [starting] classes loaded into memory.  Each class has two rows of tiles.  However, the only graphic that's drawn during party creation is the "standing" graphic, which is the first few tiles.  The rest of the tiles go unused.

So instead of loading 2 rows of tiles for 6 classes, this hack makes it load 1 row of tiles for 12 classes.

Change in Bank E:

In PtyGen_DrawChars, there are a bunch of 'ASL A's at the end of the routine.  Replace one of them with a NOP.  This changes a '*$20' to a '*$10'... resulting in each class using 1 row of tiles instead of 2.


Change in Bank F:

In LoadBatSprChrPalettes_NewGame and LoadBatSprCHRPalettes (which immediately follows it)... replace both of those routines with this code:

Code: [Select]

LoadBatSprCHRPalettes_NewGame:
    JSR LoadBatSprCHR_SetPpuAddrAndSwap     ; Set PPU address to $1000, swap to page containing char graphics

    LDA #>lut_BatSprCHR    ;  set source pointer
    STA tmp+1
    LDA #<lut_BatSprCHR
    STA tmp

    @loop:
        LDX #1          ; Only load 1 row of tile data for this class
        JSR CHRLoad     ; Load up the CHR
        INC tmp+1       ; Increase high byte of source pointer (skip the 2nd row of tiles)
        LDA tmp+1
        CMP #>lut_BatSprCHR + (12*2)  ; See if we've passed over 24 rows (all 12 classes, 2 rows per class)
        BNE @loop                     ; If not, keep looping until we do
   
    LDA #>(lut_BatObjCHR + $400)  ; change source pointer to bottom half of cursor and related CHR
    STA tmp+1
    LDX #$04                      ; load 4 rows (bottom half)
    JMP :+                        ; skip ahead to the part of the next routine that loads cursor CHR
   
    NOP     ; extra space
    NOP

LoadBatSprCHRPalettes:
    JSR LoadBatSprCHR_SetPpuAddrAndSwap ; common code
    LDA #$00
    STA tmp           ; clear low byte of source pointer
    LDA ch_class      ; get character 1's class
    JSR @LoadClass    ;  load it
    LDA ch_class+$40  ; character 2's
    JSR @LoadClass
    LDA ch_class+$80  ; character 3's
    JSR @LoadClass
    LDA ch_class+$C0  ; character 4's
    JSR @LoadClass
    LDA #>lut_BatObjCHR  ; once all character's class graphics are loaded
    STA tmp+1            ;   change source pointer to $A800  (start of cursor and related battle CHR)
    LDX #$08             ; signal to load 8 rows

       ; above two routines both merge here

:     JSR CHRLoad_Cont   ; load cursor and other battle related CHR
      JSR LoadBattleSpritePalettes  ; load palettes for these sprites
      LDA #BANK_MENUS
      JMP SwapPRG_L      ; and swap to bank E on exit

@LoadClass:
    ASL A               ; double class index (each class has 2 rows of tiles)
    CLC
    ADC #>lut_BatSprCHR ; add high byte of the source pointer
    STA tmp+1
    LDX #$02            ; signal to load 2 rows
    JMP CHRLoad         ; and load them!
   
LoadBatSprCHR_SetPpuAddrAndSwap:
    LDA $2002               ; Common code moved here to free up some bytes
    LDA #$10
    STA $2006
    LDA #$00
    STA $2006
    LDA #BANK_BTLCHR
    JMP SwapPRG_L


Quote
Am I muting the noise track correctly, or not in the right place?

You're muting it incorrectly.  Write $10 to $400C instead of zero.

Audio regs are tricky.
« Last Edit: April 21, 2017, 05:29:47 pm by Disch »

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #30 on: April 22, 2017, 12:27:54 pm »
Woo! Gosh, I wasn't expecting that much help. :D One of my questions was going to be "What is the PPU", too...

I found what some of the unknown.bins for Bank B were, too. One has the menu orb graphics, one has shop prices... I need to find pointers for things next, 'cos making room in that bank for my bloated intro text (123 bytes over limit, eesh) screws up a lot of stuff...

Otherwise, making good progress again! Even wrote up a new tidbit of code for key items in chests, so it plays the special music, while disabling the normal item jingle. So no more resetting the music track every time you grab a new chest.

Code: [Select]
LDA dlg_itemid               ; get the item ID again
    CMP #item_qty_start - items  ; see if it's a qty item (normal item -- not key item)
    BCC @KeyItem
@NotKeyItem:
    LDA #DLGID_TCGET             ; put the treasure chest dialogue ID in A before exiting!
    RTS

  @KeyItem:
INC dlgsfx
JMP @NotKeyItem
I'll figure out a better way when I understand the different instructions better. I was just so happy this worked.

Also sped up the black up/down wipe, the dialogue box (and no more wewww sfx!), and turned off the palette cycling for going into the menu. Fancy things that just slow down gameplay. It's been a lot of fun!

Just a bit grumpy that FFHackster's map editing isn't working on my laptop. The images are aligned weirdly... http://imgur.com/a/2boBq And I can't get FF Improvement working with Dosbox at all. Guess that'll wait 'til I get back to my desktop.

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #31 on: April 22, 2017, 01:14:55 pm »
One of my questions was going to be "What is the PPU", too...

The PPU is the "Picture Processing Unit" -- it's the graphics processor on the NES.  You can think of it like the NES's graphics card.

Code that interacts with the PPU is generally either doing something drawing related, setting the scroll point for the screen, or is enabling/disabling NMIs (which are triggered at the start of the frame -- or end, depending on how you look at it).  PPU regs are in the $2000-2007 range, so any code that touches those addresses is doing something graphic or NMI related.

Quote
I found what some of the unknown.bins for Bank B were, too. One has the menu orb graphics, one has shop prices... I need to find pointers for things next, 'cos making room in that bank for my bloated intro text (123 bytes over limit, eesh) screws up a lot of stuff...

Yeah I probably could have gone through those and figured them out  =x.  Ah well.

Code that accesses stuff in those bin files usually has a label in constants.inc.  There's a whole section at the bottom of "labels that couldn't be inserted in data".  I'm sure most of them refer to some of those bin files.

Quote
I'll figure out a better way when I understand the different instructions better. I was just so happy this worked.

Very nice!  =D
Though rather than jumping forward to an INC, only to jump back... you could just jump over the INC:

Code: [Select]
  LDA dlg_itemid
  CMP #item_qty_start - items
  BCS :+
      INC dlgsfx   ; will get skipped over if C was set (not key item)
: LDA #DLGID_TCGET
  RTS


Glad to see you're enjoying it!

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #32 on: April 22, 2017, 06:53:49 pm »
I figured jumping over was the way to go, but I didn't know which branching instruction to use. BEQ didn't work, and that's the only one I've ever used before! So now I know how BCS works. XD Kinda. Still gotta know what's setting C and all that.

Having a very annoying issue with price corruption, now.

In Constants.inc:
lut_ItemPrices     = $BC00

I can see that $BC00 in Anomie's disassembly notation is matching up with my .bin contents. So everything should be matching up normally?

I made a lot of space in Bank F (not on purpose, just clearing out unused stuff and things I didn't need, like the palette cycling) so I tried to include the .bin there, and there was space! So I put the lut_ItemPrices right up in there by LoadPrice, and took out the bank jump.

Code: [Select]
LoadPrice:
    ASL A        ; double item index (2 bytes per price)
    STA tmp+2    ; store low byte in $12

    LDA #>(lut_ItemPrices>>1) ; high byte of pointer, but load it right-shifted by 1
    ROL A                      ; and rotate it left by 1 in order to catch carry from above shifting
    STA tmp+3                  ; store as high byte of pointer at tmp+3

    ;LDA #BANK_ITEMPRICES
    ;JSR SwapPRG_L  ; swap to bank D (for item prices)

    LDY #0         ; zero Y (our source index)
    LDA (tmp+2), Y ; get low byte of price
    STA tmp
    INY
    LDA (tmp+2), Y ; and high byte
    STA tmp+1

    LDA #0
    STA tmp+2       ; 3rd byte is always 0 (no item costs more that 65535)

    LDA #BANK_MENUS ; swap back to bank E
    JMP SwapPRG_L   ; and return

lut_ItemPrices:
.INCBIN "bin/0F_ItemPrices.bin"

I'm guessing it has something to do with the pointer shifting stuff, but no matter what I try, I can't get it right. Disabling the ROL, removing the >>1, removing the ( ) around it... Blegh. And half the time, when I compile a new rom, the music is scratchy and increasing emulation speed only effects the music, not the walking speed? But the only thing I changed between this build and the last was that "LDA #>(lut_ItemPrices>>1)" line... (I think this is just an emulator setting I messed up... Edit 2: Not entirely convinced its the emulator, though...)

Ah, I think its a page boundary thing. I just don't know... where the page boundaries are? If the original LUT was at $BC00, and a page is 256 bytes... And $BC00 is 48128, then it'd be on the 188th page? So all I gotta do is find the nearest--or best--area to put the LUT and it'll be okay, right?

Edit 3: I DID IT!!!

April 25, 2017, 01:26:15 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
Wee, a new frustration...

Fiddling with enemy Regeneration code. Wanted the game to tell the players that the enemy was, in fact, regenerating HP. I got it mostly working! There's just two smallish problems... Or one problem that has a side effect.

Once it draws the enemy's name, that Attacker message box never undraws. Then, after the end of the turn, graphical glitches start to pop up. For every turn that box stays undrawn, the game gets more corrupt, until the palette turns grey and it freezes. The side effect is that if you do kill everything, the character who lands the killing blow, their name stays in the box while they do their little victory dance.

Code: [Select]
; Now that we have RAM and ROM pointers for this enemy...
LDA #en_enemyid ; get enemy name and save it for btl_attacker message box later...
STA btl_attacker
    LDY #ENROMSTAT_CATEGORY     ; Get the enemy category, and see if they are
    LDA (@rom), Y               ;  regenerative
    AND #CATEGORY_REGEN
    BEQ @Next                   ; If not, skip them -- go to next iteration
   
LDY #en_hp                  ; move their HP to the math buffer
    LDA (@ram), Y
    CMP #$00 ; Check if the enemy's HP is 0??
BEQ @Next ; if 0, and they're dead, skip regenerating their HP
STA btl_mathbuf
    INY
    LDA (@ram), Y
    STA btl_mathbuf+1

LDA #$00
    LDX #$0A
    JSR MathBuf_Add             ; regenerative enemies recover 3 HP (Jiggers: changed to 10; may make a LUT for some enemies to regen higher amounts...?)
   
    LDY #ENROMSTAT_HPMAX        ; put HP max in mathbuf2
    LDA (@rom), Y               ;  ... because a normal CMP would be too simple
    STA btl_mathbuf+2           ;  ...
    INY                         ;  Jiggers: Gotta find out how to use a CMP instead...
    LDA (@rom), Y
    STA btl_mathbuf+3
   
    LDY #$00                    ; compare HP and MaxHP buffers
    LDX #$01
    JSR MathBuf_Compare         ; C will be set if HP >= HPMax
   
    BCC :+                      ; cap at Max HP
      LDA btl_mathbuf+2
      STA btl_mathbuf+0
      LDA btl_mathbuf+3
      STA btl_mathbuf+1
     
  : LDY #en_hp                  ; move HP back to RAM stats
    LDA btl_mathbuf
    STA (@ram), Y
    INY
    LDA btl_mathbuf+1
    STA (@ram), Y
   
JSR DrawCombatBox_Attacker ; BUGGED: this is not undrawing, and will gradually corrupt the game the more enemies regen HP
LDA #BTLMSG_REGEN             
JSR DrawBtlMsg_ClearCombatBoxes ; Draw the message that the enemy regenerates HP!

I've been over the box stuff for hours and somehow I keep missing how DrawCombatBox_Attacker is getting undrawn normally. I've tried adding a JSR to every Undraw routine I could find... Maybe if I step away a couple days again, come back with a clearer mind, I can find it... But I also wanted to ask how the CMP would work, instead of the Math Buffer stuff! I think I get the general idea, but I'm not sure how much of the math buffer code should go in the garbage. All of it? Stop at MathBuf_Compare and leave the BCC :+ alone?

I don't think I said thanks in my last few posts so... thanks again. XD
« Last Edit: April 25, 2017, 01:26:16 pm by Jiggers »

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #33 on: April 26, 2017, 01:12:24 am »
Sorry for slow reply -- for some reason I just didn't see that this thread had new posts  =x

Glad you got your first problem solved!  =D

CMP does a "compare", which is effectively a subtraction.  It will take whatever is in A and subtract whatever other number you give it.  The N/Z/C flags are set according to the result ... but the actual number is discarded (A remains unchanged).  You can then examine N/Z/C flags (usually just Z/C... and usually with branches) to see which number was larger or if the numbers were equal.

The end result is:
if A > value:  C=1, Z=0
if A = value:  C=1, Z=1
if A < value:  C=0, Z=0

So the Z flag (BNE/BEQ) effectively checks if the two values were equal.  And the C flag (BCC/BCS) effectively checks if A was >= the given value.

Example:
Code: [Select]
LDA #$10
CMP #$08
BCS This_Will_Branch_Because_C_Is_Set  ; A is $10, and $10>$08, so C is set after the CMP


This is a bit trickier with the section of code you're looking at because HP spans two bytes, so you'd have to do a 16-bit compare.  So effectively you'd compare the high bytes first, and only compare the low bytes if the high bytes were equal.  Replacing the math buffer code will save you a few bytes of space... but other than that it's not really worth it unless you want to practice your 6502 skills.

As for checking to see if the Enemy's HP is zero, there are some problems with that.
1)  You're only checking the low byte.  So if an enemy has 256 HP (or any multiple of 256), your code will think they have zero HP.
2)  I think there's a better way to check if an enemy is dead... I don't think HP is reliable (might not be zero'd if the enemy ran or was insta-killed).  A quick check of the code seems to suggest that each enemy has an entry in 'btl_enemyIDs' that is replaced with the value 'FF' upon their death or otherwise removal from battle... so a better way to check would be with this:

Code: [Select]
LDX $id
LDA btl_enemyIDs, X
CMP #$FF
BEQ the_enemy_is_dead_or_doesnt_exist



Also.... DrawCombatBox_Attacker assumes the enemy ID has been written to btl_attacker.  So before calling that, you'll want to copy the ID to there.

As for the undraw problem.... groooaaaan.  FF stupidly has two separate counters to keep track of how many combat boxes are drawn (btl_combatboxcount and btl_combatboxcount_alt).  The problem you're experiencing is because DrawCombatBox_Attacker increments the "alt" counter ... whereas DrawBtlMsg_ClearCombatBoxes looks at the "non-alt" counter to figure out how many boxes to undraw.

Easiest fix here would be this:
Code: [Select]
JSR DrawCombatBox_Attacker   ; draw the attacker box
DEC btl_combatboxcount_alt   ; -1 to the "alt" counter
INC btl_combatboxcount       ; +1 to the "non-alt" counter
  ; now DrawBtlMsg_ClearCombatBoxes should properly wipe the attacker box


I am reasonably certain that the in-game corruption you're seeing is due to the combat box not being undrawn.

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #34 on: May 18, 2017, 04:51:44 pm »
No worries about slow replies. I'm on vacation! Yet I chose the hottest day to come back to this...

I spent a little bit trying to get my head around CMP and how I might shorten the comparison for that bit of code, but in the end I went with moving a chunk of it past the @Next, and doing a JMP back. Doing the attack box drawing stuff pushed the BNE @MainLoop line too far away and it was getting a range error. (Another reason I thought it was so clever to use the HP for checking to see if an enemy was there; the HP stuff was already being loaded for other stuff, saving some bytes.)

Maybe I'll come back to it if I get really pressed for space. Edit 1: Looks like I'm 5 bytes over in Bank C, so this will be my next priority...!) But yay, no more glitching and crashing now!  :D

I saw another thread about the XFER and TMPR bugs, so I went ahead and made those changes, too. I was going to ask about that next. Instead, I'll ask about the IB/OB stuff, for ailments and HP.

As I understand it, IB is RAM, OB is ROM. You point out a few times that IB is never used for these things, even though its there in the code... and using OB is outright bugged in a few places? You know what I mean--so if I were to fix this, what should I do? Replace all the OB with IB stuff? But not Max HP? (It seems like you don't rant about it being used for Max HP, which makes sense; battle stuff shouldn't be changing that stat except on level-up.)

Or should I leave most of it be?

Blegh, its so hot, I know I'm not wording this post very well. I'll try to come back to it with a clearer head.

Edit 1 Again:

So I've worked out a bunch of the music code and have got my first song in, replacing the Castle theme. I just have no idea how to loop it properly. The first track works great, because the loop point points to the right spot, since I haven't changed any code before it. Tracks 2 and 3, not so lucky.

Going by Gil Galad's Music Driver assembly file I found here, there looks to be a way to loop without using hardcoded pointers? But its in a completely different style... Here's the triangle track for the original Castle music, for instance.

Code: [Select]
   .DB $FD
   CLOOP2
   .DB $F8,$08,$EC,$DA,$21,$21,$11,$11,$D9,$B1,$B1
   .DB $A1,$A1,$71,$71,$DA,$21,$D9,$B1,$DA,$41,$41,$D9,$91,$91
   .DB $D0
   .DW CLOOP2 ; $E5,$8D

.DW is not a recognized control command for the ASM we're using though...

I'll need to figure out some way to manage the first LUT too. I've stripped it down into my assembly file like this:

Code: [Select]
.ALIGN $100
 lut_ScoreData:

.BYTE $C0,$80 ; Square 1
.BYTE $BD,$81 ; Square 2
.BYTE $BC,$82 ; Triangle
.BYTE $00,$00 ; ^^ Prelude

If I could somehow make these into variables or something... so the pointers can move around to labels, things can shift as data is re-arranged... Is that possible?

EDIT AGAIN: .WORD is what I was looking for. I knew I saw it somewhere before, and wondered what it was! So I can make my own shiftable LUT for the songs and loops within them. :D

https://drive.google.com/open?id=0B7rOlPhS8WdsMWdtTnVZVVRFOVk -- The sprite for this character is desperately in need of someone who knows what they're doing, but here's the new castle song! I'm so giddy with pride...
« Last Edit: May 19, 2017, 12:00:21 am by Jiggers »

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #35 on: May 19, 2017, 12:12:31 am »
As I understand it, IB is RAM, OB is ROM. You point out a few times that IB is never used for these things, even though its there in the code... and using OB is outright bugged in a few places? You know what I mean--so if I were to fix this, what should I do? Replace all the OB with IB stuff? But not Max HP? (It seems like you don't rant about it being used for Max HP, which makes sense; battle stuff shouldn't be changing that stat except on level-up.)

OB isn't necessarily ROM -- as character stats are always pulled from RAM.  Though enemy stats might be pulled from ROM.

IB = In Battle
OB = Out of Battle

Certain magic spells have effects on stats, but those stats wear off at the end of battle.  How this works is, when the battle starts, OB stats are copied to IB -- then IB stats are modified in battle.  When the battle is over, any permanent effects would be copied back to OB.  A spell like INVS, for example, modifies IB evade .. but since it's not permanent, IB evade is not copied back to OB evade at the end of battle.

Logically I would expect HP and ailments to behave the same way -- but it appears the game modifies OB HP and ailemnts directly (since its effects are always permanent).  Which ends up making IB HP/ailments useless.  Other IB stuff is definitely used, though.

Quote
Or should I leave most of it be?

My general rule is don't tinker with it unless you have reason to.  If something is bugged you can try to fix it, but if it's working I wouldn't bother.


Quote
Going by Gil Galad's Music Driver assembly file I found here, there looks to be a way to loop without using hardcoded pointers?

Looks like you figured this out in your edit.  =D
« Last Edit: May 19, 2017, 10:34:18 am by Disch »

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #36 on: May 19, 2017, 04:59:56 am »
That makes so much more sense now! Hopefully I can find a way to make some room if there's useless OB/IB stuff to toss. I forget if I deleted the other useless stuff in this version... been so busy tinkering with other things, I never really pieced together my orb damage stuff since I got it working.

Quick question... about sound variable thingies...
Code: [Select]
ch_scoreptr   = $0
  ch_envptr     = $2  ; ptr to start of env data
  ch_envpos     = $4  ; pos in env data (00-1F)
  ch_lenctr     = $5  ; counter for score data (note length)
  ch_frqtblptr  = $6  ; pointer to freq table (changes per octave)
  ch_lentblptr  = $8  ; pointer to length table (changes per tempo)
  ch_envrate    = $A  ; rate/speed of env table traversal
  ch_envrem     = $B  ; remaining "fraction" of env data (3 bits of fraction)
  ch_vol        = $C  ; output volume
  ch_loopctr    = $D  ; remaining loop counter
  ch_freq       = $E  ; output freq.  High bit set marks byte has been written (don't rewrite to reset duty)

Is 1, 3, 7, 9, or F used at all? I want to try something crazy.
Since F0 to F7 isn't used in sequence data, but COULD BE used, I want to try making a pitch bend table... Something like:

if f0 then pitch bend starts 2 semitones down, sweeps up a quarter note's length
if f1 then pitch bend starts 2 semitones down, sweeps up an eighth note's length
if f2 then pitch bend starts 1 semitone down, sweeps up a quarter note's length
if f3 then pitch bend starts 1 semitone down, sweeps up an eighth notes's length
if f4 then pitch bend starts 2 semitones up, sweeps down a quarter note's length
if f5 then pitch bend starts 2 semitones up, sweeps down an eighth note's length
if f6 then pitch bend starts 1 semitone up, sweeps down a quarter note's length
if f7 then pitch bend starts 1 semitone up, sweeps down an eighth notes's length


Nevermind, that was a stupid idea from being up until 5 AM... XD I mean, not a stupid idea, just one that's wayyy above what I should be tackling...

I could REALLY use a second loop counter, though. At least for the triangle. Since its not using envelopes (right?), I'm using the F0-F7 codes to use $A as a second loop... It works, just need to see if I can nest the two... Hm... So I've come up with this:

Code: [Select]
Music_LoopCode:
    AND #$0F                ; get low bits (repeat count)
    STA tmp                 ; back it up for later

    LDA ch_loopctr, X       ; check the loop counter
    BEQ @StartLoop          ;  if loop counter is zero, this is a new loop -- start it
    DEC ch_loopctr, X       ; otherwise, dec the loop counter
    BNE ResumeLoop         ;  if it's still nonzero (still looping), resume the loop
                            ; otherwise, this loop is done -- skip past it
      LDA ch_scoreptr, X
      CLC
      ADC #3
      STA ch_scoreptr, X    ; skip past the loop code by adding 3 to the score pointer
      STA mu_scoreptr       ; (3 = 2 byte loop address + 1 byte control code)
      LDA ch_scoreptr+1, X  ; update mu_scoreptr as well to keep it in sync
      ADC #0
      STA ch_scoreptr+1, X
      STA mu_scoreptr+1
      RTS

@StartLoop:             ; to start a new loop...
    LDA tmp               ; get the number to times to repeat this loop
    STA ch_loopctr, X     ; record that as the loop counter
;JMP @ResumeLoop       ;  then resume the loop as normal

  ResumeLoop:
    LDY #1
    LDA (mu_scoreptr), Y    ; to resume the loop, get low byte of loop address from the score
    STA ch_scoreptr, X      ;  record that as the new score pointer for the channel
    STA tmp                 ; store in tmp (don't change mu_scoreptr yet because we still need it
    INY                     ;   to get the high byte)
    LDA (mu_scoreptr), Y
    STA ch_scoreptr+1, X    ; get high byte
    STA mu_scoreptr+1
    LDA tmp
    STA mu_scoreptr         ; then set low byte of mu_scoreptr previously tmp'd
    RTS                     ; channel is now pointing to loop address -- exit
 
   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  Music_LoopCode2  [$B217 :: 0x37227]
;;
;;; F1-7 passes the low bit (1-7) through to Triangle's $A, then decrements itself?
;;; If $A = 0 then it skips through the code until it finds the next F0
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Music_LoopCode2:
  AND #$0F ; get low bits (repeat count)
    STA tmp                 ; back it up for later

LDA ch_loopctr2, X
BEQ @FindNext
DEC ch_loopctr2, X      ; otherwise, dec the loop counter
    BNE ResumeLoop          ;  if it's still nonzero (still looping), resume the loop
                            ; otherwise, this loop is done -- skip past it
      LDA ch_scoreptr, X
      CLC
      ADC #3
      STA ch_scoreptr, X    ; skip past the loop code by adding 3 to the score pointer
      STA mu_scoreptr       ; (3 = 2 byte loop address + 1 byte control code)
      LDA ch_scoreptr+1, X  ; update mu_scoreptr as well to keep it in sync
      ADC #0
      STA ch_scoreptr+1, X
      STA mu_scoreptr+1
      RTS

@FindNext:
LDA ch_scoreptr, X ; Get, um, the point in the song we last left off...?
CMP #$F0 ; if the score data = F0 then
BEQ ResumeLoop ; resume playing
CLC ; if not, cycle through score data until we find F0...?
ADC #3
STA ch_scoreptr, X    ; skip past the loop code by adding 3 to the score pointer
STA mu_scoreptr       ; (3 = 2 byte loop address + 1 byte control code)
LDA ch_scoreptr+1, X  ; update mu_scoreptr as well to keep it in sync
ADC #0
STA ch_scoreptr+1, X
STA mu_scoreptr+1
JMP @FindNext

It ummmm doesn't work! I can see it looping through in the RAM hex view, trying to find the next $F0...

Here's how I picture it working in the song code:

Code: [Select]
   PROWL3CHORUS:
   .BYTE $85,$B5,$DA,$35,$D9,$B5,$85,$B5,$DA,$35,$D9,$B5 ;first bar, fifth bar
   .BYTE $35,$85,$B5,$DA,$35,$85,$35,$D9,$B5,$85 ;second bar, sixth bar
   .BYTE $65,$A5,$DA,$15,$D9,$A5,$65,$A5,$DA,$15,$D9,$A5 ;third bar, fourth bar
   .BYTE $F1
   ; play chorus 1 time
   ; F1 passes the low bit (1) through to Triangle's $A, then decrements itself
   ; If $A = 0 then it skips through the code until it finds the next F0
   .BYTE $65,$A5,$DA,$15,$D9,$A5,$DA,$65,$D9,$A5,$DA,$15,$D9,$A5 ;fourth bar
   .BYTE $D1
   .WORD PROWL3CHORUS
   ;.BYTE $85,$B5,$DA,$35,$D9,$B5,$85,$B5,$DA,$35,$D9,$B5
   ;.BYTE $35,$85,$B5,$DA,$35,$85,$35,$D9,$B5,$85
   ;.BYTE $65,$A5,$DA,$15,$D9,$A5,$65,$A5,$DA,$15,$D9,$A5   
   .BYTE $F0 ; resume point
   .BYTE $65,$A5,$DA,$15,$D9,$A5,$DA,$15,$D9,$A5,$65,$15 ; eighth bar

So what I want: Play the first 4 bars, then loop, and after the 3rd bar again, skip the 4th bar and go to the 8th bar.

It's silly, and I can do without it, but I'm curious if it's possible. I think one spot I'm screwing up is thinking that the first F1 is going to become an F0 in the hard code... I can't tell, my brain's fuzzy.
« Last Edit: May 19, 2017, 10:20:53 pm by Jiggers »

Disch

  • Hero Member
  • *****
  • Posts: 2539
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #37 on: May 19, 2017, 09:22:54 pm »
Is 1, 3, 7, 9, or F used at all? I want to try something crazy.

Yes.  "ch_envptr", being a pointer, needs 2 bytes... so "ch_envptr" is the low byte and "ch_envptr+1" is the high byte.  Same for the others.

Quote
Since F0 to F7 isn't used in sequence data, but COULD BE used, I want to try making a pitch bend table... Something like:

Pitch bends are fun but take a decent chunk of RAM.  Fortunately, you have RAM!

The music engine stores it's data between $B0-DF .. however it actually has space for $A0-EF!  So you can squeeze out 32 more bytes of RAM!

This is what I did for my N163 music driver.  Adding 5 extra sound channels meant I needed as much RAM as I could get my hands on.

Jiggers

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #38 on: May 20, 2017, 06:01:10 pm »
Oop, looks like you posted while I was editing my post (I took so long doing it...) But its all good now.

In Constants, I put: LOOP_MARKERS     = $A0
In Variables, I put:   loop_marker   = $A0 
-- Because I don't know exactly how the ch_xxx figure out what channel they're applying to. That's the next step.

Meanwhile, some tweaks to the loop code:

Code: [Select]
@Code_F0_F7:
CMP #$F0
BCC @Code_F1_F7
  JSR Music_LoopCode2
  JMP Music_DoScore

  @Code_F1_F7:
LDA #1 
JMP Music_DoScore ; _IncByA   ;  just skip over this byte and continue processing

;;;;;;;;;;;;;;;;;;;;;
; Mostly just took out the @s and swapped StartLoop up to avoid needing to JMP
;

Music_LoopCode:
    AND #$0F                ; get low bits (repeat count)
    STA tmp                 ; back it up for later

    LDA ch_loopctr, X       ; check the loop counter
    BEQ StartLoop          ;  if loop counter is zero, this is a new loop -- start it
    DEC ch_loopctr, X       ; otherwise, dec the loop counter
    BNE ResumeLoop         ;  if it's still nonzero (still looping), resume the loop
                           
    SKIPLOOP: ; otherwise, this loop is done -- skip past it
      LDA ch_scoreptr, X
      CLC
      ADC #3
      STA ch_scoreptr, X    ; skip past the loop code by adding 3 to the score pointer
      STA mu_scoreptr       ; (3 = 2 byte loop address + 1 byte control code)
      LDA ch_scoreptr+1, X  ; update mu_scoreptr as well to keep it in sync
      ADC #0
      STA ch_scoreptr+1, X
      STA mu_scoreptr+1
      RTS

StartLoop:             ; to start a new loop...
    LDA tmp               ; get the number to times to repeat this loop
    STA ch_loopctr, X     ; record that as the loop counter
;JMP @ResumeLoop       ;  then resume the loop as normal

  ResumeLoop:
    LDY #1
    LDA (mu_scoreptr), Y    ; to resume the loop, get low byte of loop address from the score
    STA ch_scoreptr, X      ;  record that as the new score pointer for the channel
    STA tmp                 ; store in tmp (don't change mu_scoreptr yet because we still need it
    INY                     ;   to get the high byte)
    LDA (mu_scoreptr), Y
    STA ch_scoreptr+1, X    ; get high byte
    STA mu_scoreptr+1
    LDA tmp
    STA mu_scoreptr         ; then set low byte of mu_scoreptr previously tmp'd
    RTS                     ; channel is now pointing to loop address -- exit
 
   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  Music_LoopCode2 
;;
;;    Called to process a loop/repeat command (code $F0)
;;    F0 is a "go back to X then skip the second time" kind of loop,
;;   allowing a song to play something twice, then have a variation
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Music_LoopCode2:
  LDA loop_marker, X ; if 0 (no loop yet started)
BEQ @StartLoop ; go to start
CMP #$01 ; if 1 (loop started)
BEQ @ResumeLoop ; ResumeLoop
JMP SKIPLOOP        ; Otherwise, ignore it

@ResumeLoop:
LDA #00 ; load nothing
STA loop_marker, X ; write over the loop code so its 0 again for next time
DEC ch_loopctr, X       ; dec the main loop counter, since it won't get to it again
JMP ResumeLoop ; then do the Loop

@StartLoop:             ; to start a new loop...
LDA #01 ; Load 1
STA loop_marker, X      ; Save the RAM byte Triangle $A as 1
JMP SKIPLOOP        ; Skip over F0 and pointer in the score and resume playing

This works great with my new Overworld music, using the Triangle channel. Except the 01 doesn't pop up at A0 in the RAM hex edit view, though it did before when it was using the $A slot of the triangle's...

But when I use it in my new Coneria Castle song, for Square 1, the loop point teleports me into an empty weapons shop. :D And then right back to where I was when I exit the shop. Neat!

Here's the part where it teleports...
Code: [Select]
TEMPLE1:
.BYTE $FC ; Tempo: C
TEMPLE1LOOP:
.BYTE $F8,$08,$EE ; Envelope Data (Speed 08, Pattern E)
TEMPLE1MELODYLOOP1:
.BYTE $D9,$B1,$DA,$43,$D9,$B5,$45
.BYTE $31,$B3,$35,$D8,$B5
.BYTE $D9,$11,$83,$15,$35
.BYTE $F0 ; teleport!
.WORD TEMPLE8THBAR
.BYTE $61,$DA,$13,$D9,$65,$DA,$15 ; 4TH BAR
.BYTE $D1
.WORD TEMPLE1MELODYLOOP1
;.BYTE $D9,$B1,$DA,$43,$D9,$B5,$45
;.BYTE $31,$B3,$35,$D8,$B5
;.BYTE $D9,$11,$83,$15,$35
TEMPLE8THBAR:
.BYTE $62,$A5,$DA,$13,$33 ; 8TH BAR
It only saves 11 bytes, so its not that important to fix this song... but fixing it for other songs might save a lot of space.

It also borks up the battle theme, I'm not sure how yet... more testing required! Just needed to set more .WORD labels. For all the songs!

I went back to check on trimming down the regenerating HP thing, and ... wow, I don't know what I'm doing here. My thought process was like, if we're only regenerating 3 or 10 HP, why do we need to check both bytes? And long story short...

Code: [Select]
LDY #ENROMSTAT_HPMAX+1      ; Get high byte of enemy's max HP
LDA (@rom), Y               ; Use as pointer to find data
STA tmp ; store as tmp
LDY #en_hp+1                ; Load HP pointer into Y
LDA (@ram), Y ; Get high byte (second) HP from enemy RAM info
CMP #tmp ; compare HP to max HP?
BCC :+ ; if HP >= Max HP, then ...
LDA tmp ; reload max HP
STA (@ram), Y ; save max as HP (do I need to do this at all?)
JMP @Next ; and move on to next enemy
: CLC ; if its not already max then...
ADC #10 ; Add 10
STA (@ram), Y ; save

Behold my glorious guesswork! Which takes an imp and makes it have way more than 50 HP, somehow.

You are a saint for putting up with me. XD

Update: Getting a little more sensible...
Code: [Select]
LDY #en_hp                  ; move their HP to the math buffer
    LDA (@ram), Y
    STA btl_mathbuf ; save in mathbuf "0"
    INY ; Add 1 to Y, getting the second byte of HP data?
    LDA (@ram), Y ; Y pointer to RAM info for HP
    STA btl_mathbuf+1 ; save in mathbuf "1" so that HP is "0" and "1"

LDA #$00 ; Load A with emptiness OR Enemy HP from @ram?
    LDX #$0A ; load X with 10
    JSR MathBuf_Add             ; regenerative enemies recover 10 HP (Jiggers)

    LDY #ENROMSTAT_HPMAX        ; Load HP Max pointer for ROM data
INY ; Increment Y (+1 to get high byte of HP Max)
LDA (@rom), Y               ; Load HP Max high byte
CMP btl_mathbuf+1 ; Compare to high byte of current HP
BCC :+ ; if carry clear (what does that mean in this instance...), then goto :
STA btl_mathbuf+1 ; Save HP Max in floating current HP spot (good idea to do that here, or no?)
DEY                     ; Decrement Y back to low byte of max HP data
LDA (@rom), Y ; Load HP Max low byte
CMP btl_mathbuf+0 ; Compare to low byte of current HP
BCC @ADD ; if carry clear, then goto @ADD
JMP :+ ; if not, then never mind...?
@ADD:
STA btl_mathbuf+0 ; Save Max HP low byte into floating HP low byte spot...

  : LDY #en_hp                  ; move floating HP back to RAM stats
    ;LDA btl_mathbuf ; this is already loaded so comment it out...
    STA (@ram), Y
    INY
    LDA btl_mathbuf+1
    STA (@ram), Y

This SEEMS to work, but I'm only testing it on imps with 8 HP, so I don't know if a high byte of more than 00 will break things. Also I need to focus on remembering what the BCCs actually do.
« Last Edit: May 21, 2017, 02:48:32 pm by Jiggers »

SorceressTerra

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #39 on: May 21, 2017, 10:40:57 pm »
Hello! I am new here, and very nervous about posting!

I've been working on an FF1 project, started out just with sprites, branched out into magic, some equipment, and so on, thanks to all the tools that have been made for the game. It was just a goofy little thing for my friends, but the more I work on it, the more I feel serious about really wringing something new out of it! I started poking deeper, reading documents, poking at hex code... The last few days I even started trying to figure out Assembly! It's... scary. And depressing.

I had this idea, though, that I am not sure about: A check that, when the Earth Orb is lit, adds 10-20 effectivity to certain magic spells. So Fire goes from 10 to 30, becoming as strong as Fire 2, and so on.

I'm still blundering through the Disassembly trying to make sense of it, but in the meantime, I thought it best to ask if its even remotely possible. It seems like it should be, if that one INT fix patch is able to make spells more effective based on an even more nebulous variable!

Ideally, every lit orb would give the spells a boost, upgrading the spells throughout the game, so you could free up magic slots for other classes, or more variety of spells that make use of other weaknesses. No more Fire 3, if Fire 2 can eventually get as strong as Nuke!

So far the closest thing I've seen in the Disassembly would be this:

DoClassChange:
    LDA ch_class        ; simply bump up every party member's class ID number
    CLC                 ; to up them to the promoted version of their class
    ADC #6
    STA ch_class

    LDA ch_class+(1<<6)
    CLC
    ADC #6
    STA ch_class+(1<<6)

My next goal is to find out where the magic information is called, and set up a check before spells are cast... then either JMP to a second list of spells (if there's room in the code for it, which I gather there should be? Can we delete the whole minimap thing to free up room?) or... something more complicated that I can't conceive of yet.

Blundering. It's both fun and demoralizing at the same time!

So cool I had this idea once but lacked and still lack any form of knowledge to of done anything with it. I'm really interested in playing this when it's done. So are you planning to like take out the level 5-7 elemental spells as well as NUKE? And instead continually make the Level 1-4 offensive spells get more powerful as more ORBs are lit? That sound pretty dang neat. When I had this idea my plan was to make level Fire, Lit, and Ice to get a 5 effectivity boost after the Earth orb is restored. Then when the Fire Orb is restored it would boost the level 2 spells(magic menu only not the magic items versions) by 10-15 effectivity. Then when the Water Orb is restored it would would make the level 1 elemental spells all multi target spells. Lastly, after the Wind Orb is restored is would boost the level 5-8 offensive spells by 20-30 effectivity. reaching level 50 would give a significant boost to all special property spells as well. Not to mention the White Wizard would get a stronger Thors Hammer well all her Hammers would be stronger and more expensive.(this is because in REAL life war hammers can be significantly stronger than swords. This would make the WW more melee friendly when she is not healing so her good STR is also put to use.

Anyways, I like your idea a lot and look foreward to the results it's to bad I feel unconfident in my ability to learn programming.