Users browsing this thread: 1 Guest(s)
Event Script Assembler Macros

#1
Posts: 181
Threads: 3
Thanks Received: 26
Thanks Given: 4
Joined: Apr 2015
Reputation: 18
Status
None
I've been working on a set of assembler macros for writing and modifying FF6 events using an assembler. Essentially this allows you to write events in an easily readable text file and then convert them to binary automatically (just like how an assembler converts asm files into machine code). It's still a WIP, but it's now integrated into my ff6 repo on GitHub. It can also be used to generate standalone events without building the whole ROM. src/event/README.md contains more details and an incomplete list of event command descriptions, include/event_cmd.inc contains the macro definitions that are used to convert the event script commands into binary using the assembler, and src/event/event_main.asm contains the actual event script code. Currently, only the beginning and end of the event script get generated using the new macros. The rest is still imported as a raw binary that gets ripped from a vanilla ROM. Eventually, I'd like to generate the entire event script using just assembly files (like how the CPU code is generated).

As an example, here's the script for the event that occurs when you use a Warp Stone or cast Warp (at CA/0108). The indentations are for readability only, they're not required.

Code:
; ca/0108
.proc Warp
       sfx 76
       loop 2
               obj_script SLOT_1
                       dir RIGHT
                       end
               obj_script SLOT_1
                       dir UP
                       end
               obj_script SLOT_1
                       dir LEFT
                       end
               obj_script SLOT_1
                       dir DOWN
                       end
               end_loop
       fade_out
       loop 2
               obj_script SLOT_1
                       dir RIGHT
                       end
               obj_script SLOT_1
                       dir UP
                       end
               obj_script SLOT_1
                       dir LEFT
                       end
               obj_script SLOT_1
                       dir DOWN
                       end
               end_loop
       wait_fade
       switch $01cc=0
       switch $01cd=0
       switch $02bc=0
       if_switch $02bf=1, phoenix_cave_warp
       if_switch $02be=1, kefkas_tower_warp
       call reset_turtles
       load_map $01ff, 0, 0, UP, $24, 0
       set_script_mode WORLD
       end
       set_script_mode EVENT

; warp event when in kefka's tower
kefkas_tower_warp:
       call $cc0ff6
       return

; warp event when in phoenix cave
phoenix_cave_warp:
       call $cc1001
       return

; reset some turtles when in darill's tomb
reset_turtles:
       switch $02b4=0
       switch $02b6=0
       return
.endproc  ; Warp

Compare to Imzogelmo's event script dump, which has a bit more detail, but can't be easily converted back into binary.

Code:
CA/0108: F4    Play sound effect 76
CA/010A: B0    Execute the following commands until $B1 2 times
CA/010C: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/010E: CD            Turn vehicle/entity right
CA/010F: FF            End queue
CA/0110: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/0112: CC            Turn vehicle/entity up
CA/0113: FF            End queue
CA/0114: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/0116: CF            Turn vehicle/entity left
CA/0117: FF            End queue
CA/0118: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/011A: CE            Turn vehicle/entity down
CA/011B: FF            End queue
CA/011C: B1        End block of repeating commands
CA/011D: 97    Fade screen to black
CA/011E: B0    Execute the following commands until $B1 2 times
CA/0120: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/0122: CD            Turn vehicle/entity right
CA/0123: FF            End queue
CA/0124: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/0126: CC            Turn vehicle/entity up
CA/0127: FF            End queue
CA/0128: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/012A: CF            Turn vehicle/entity left
CA/012B: FF            End queue
CA/012C: 31        Begin action queue for character $31 (Party Character 0), 2 bytes long (Wait until complete)
CA/012E: CE            Turn vehicle/entity down
CA/012F: FF            End queue
CA/0130: B1        End block of repeating commands
CA/0131: 5C    Pause execution until fade in or fade out is complete
CA/0132: D3    Clear event bit $1E80($1CC) [$1EB9, bit 4]
CA/0134: D3    Clear event bit $1E80($1CD) [$1EB9, bit 5]
CA/0136: D5    Clear event bit $1E80($2BC) [$1ED7, bit 4]
CA/0138: C0    If ($1E80($2BF) [$1ED7, bit 7] is set), branch to $CA0154
CA/013E: C0    If ($1E80($2BE) [$1ED7, bit 6] is set), branch to $CA014F
CA/0144: B2    Call subroutine $CA0159
CA/0148: 6B    Load map $01FF (<unknown or out-of-range map index>) instantly, (upper bits $2400), place party at (0, 0), facing down
CA/014E: FF        End map script

CA/014F: B2    Call subroutine $CC0FF6
CA/0153: FE    Return

CA/0154: B2    Call subroutine $CC1001
CA/0158: FE    Return

CA/0159: D5    Clear event bit $1E80($2B4) [$1ED6, bit 4]
CA/015B: D5    Clear event bit $1E80($2B6) [$1ED6, bit 6]
CA/015D: FE    Return

So, why make this? The main reason is so that the assembler has access to event addresses/labels for my ff6 disassembly, but it can also be used to write shorter standalone events. Hackers often write events using an event editor built into one of the ROM editors (ZoneDoctor and FF6Tools have one) or by generating the raw binary code manually. These methods have issues of their own, but more importantly for me there's no easy way to reference event addresses in the CPU code, event triggers, NPCs, etc. I also think writing events this way is easier to read and understand.

Another cool thing is that I'm pretty sure this is how the original devs wrote these scripts. The editors they used were probably pretty primitive, so they would have relied heavily on macro assemblers to do most of the script and data generation when developing the game.

Like with anything new, there's going to be a learning curve if you want to write events with this. If you're already proficient with other methods for writing event code, this might not be worth learning. Also, Because ff6 contains such a large amount of code and data, the more familiar assemblers like Asar and xkas aren't really suitable. Instead, I've been using ca65 which is better suited to large projects, but nonetheless has issues of its own and is less familiar to most hacking folks.

My next goals are to implement the remaining event commands, finish the documentation, convert the rest of the event script into this format, and then do lots of testing with custom events. I don't have as much time to spend on this stuff as I used to so if anyone would like to contribute or help out in any way I would really appreciate it. Let me know here or on GitHub and sorry in advance if I'm slow to reply.
  Find
Quote  
[-] The following 1 user says Thank You to Everything for this post:
  • madsiur (04-05-2024)

#2
Posts: 3,970
Threads: 279
Thanks Received: 236
Thanks Given: 58
Joined: Oct 2011
Reputation: 65
Status
Tissue-aware
Awesome!!

Edit: (I might have some time in the future to contribute again)
  Find
Quote  

#3
Posts: 181
Threads: 3
Thanks Received: 26
Thanks Given: 4
Joined: Apr 2015
Reputation: 18
Status
None
Big update - I finished making a full disassembly of the event script. The ff6 repo now contains an assembly file (src/event/event_main.asm) which can be assembled into a byte-for-byte copy of the vanilla event script, with all event addresses being properly linked to allow for insertion and deletion of event commands, as well as relocation of the entire script within the ROM. What's still missing is properly linking all of the event triggers, NPCs, and map startup event pointers to the event script, so the game will probably still crash if the script is modified. This was a big milestone though, and more updates will hopefully be coming soon.

One thing I could use some help with is to separate the event script into multiple assembly files, which means I need to map out where the events for different parts of the game are located in the script. I started a list below, and if anyone can help fill out the rest of this by parsing through the script it would be very helpful.

Code:
CA/0000-CA/004E system events
CA/004F-CA/0489 vehicle events
CA/048A-CA/3F82 final battle / ending
CA/3F83-CA/48C0 daryl's tomb
CA/48C1-CA/5E22 floating island collapse / solitary island
CA/5E23-CA/5EB3 game start
CA/5EB4-        world map events
CA/5F8A-        figaro castle
CA/75EE-        cave to south figaro
CA/77AD-        south figaro
CA/80BF-        sabin's house
CA/820F-        mt. kolts
CA/84AB-        locke's scenario
CA/89ED-        edgar & sabin at figaro castle
CA/8AE3-        serpent trench / nikeah
CA/9337-        setzer intro
CA/94FF-        zozo / esperville
CA/AD4C-        3 scenarios
CA/ADF1-        opera house
CA/C5C1-        common subroutines

CA/????-        floating island (collapse)
CA/E8F4-        map startup events
CA/????-        floating island (statues)
CA/F64B-CB/0468 returner's hideout
CB/0469-CB/0A33 lete river
CB/0A34-        sabin's scenario (imperial camp)
CB/1B0E-        blackjack, setzer joins
CB/2562-        imperial base

CB/2A5B-        cave to the sealed gate

CB/43E4-CB/5F64 jidoor
CB/5F65-        crazy old man's house

CB/6E4B-CB/7853 ebot's rock / thamasa (wor)
CB/7854-CB/7981 colosseum
CB/7982-CB/7DBD cave in the veldt
CB/7DBE-CB/8273 gogo's cave
CB/8274-CB/A411 doma castle / cyan's dream
CB/A412-CB/BEF0 phantom train
CB/BEF1-CB/C218 baren falls
CB/C219-CB/C879 crescent mountain
CB/C87A-        albrook -> thamasa -> esper mountain
CC/0A??-        narshe (picking locks)
CC/0B8A-        duncan's house / bum rush
CC/0F7B-CC/19EE kefka's tower
CC/19EF-CC/1F9E ancient castle
CC/1F9F-CC/206D 8 dragons / crusader
CC/206E-CC/3303 phoenix cave
CC/3304-CC/339B chocobo stable
CC/339C-        beginner's house
CC/36BD-        narshe (wor)

CC/3AFC-        kohlingen (wor)

CC/3D73-        cyan & lola events (mt. zozo)

CC/43E2-CC/5172 mobliz (wor)
CC/5173-CC/56D9 fanatics' tower
CC/56DA-CC/5B00 tzen
CC/5B01-CC/6367 albrook
CC/6368-        maranda
CC/65C6-        mobliz (wob)
CC/6935-        kohlingen (wob)
CC/7083-        narshe (after vector)
CC/72C9-        magitek factory
CC/8320-        vector / imperial castle
CC/985B-        narshe (wob)
CC/E499-        common subroutines
  Find
Quote  



Forum Jump:

Users browsing this thread: 1 Guest(s)


Theme by Madsiur2017Custom Graphics by JamesWhite