mirror of
https://github.com/everything8215/ff6.git
synced 2024-10-07 02:23:31 +00:00
Can build banks C0 and EE
No battle, menu, sound, or cutscene code. Game is playable up to Sabin's scenario, but softlocks because Gau can't be recruited on the Veldt.
This commit is contained in:
parent
8cc165843c
commit
2b18ca7e8e
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
*.sfc
|
||||
*.o
|
||||
*.lst
|
||||
*.map
|
||||
node_modules/*
|
||||
ff6-*-data.json
|
97
Makefile
Normal file
97
Makefile
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
# exported variables used by module makefiles
|
||||
export ASM = ca65
|
||||
export ASMFLAGS =
|
||||
export VERSION_EXT
|
||||
|
||||
# the linker
|
||||
LINK = ld65
|
||||
LINKFLAGS =
|
||||
|
||||
# list of ROM versions
|
||||
VERSIONS = ff6-jp ff6-en ff6-en1
|
||||
ROM_DIR = rom
|
||||
ROMS = $(foreach V, $(VERSIONS), $(ROM_DIR)/$(V).sfc)
|
||||
|
||||
# list of modules
|
||||
MODULES = field menu btlgfx battle sound cutscene world
|
||||
|
||||
.PHONY: all rip encode-jp encode-en clean $(VERSIONS) $(MODULES)
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
# make all versions
|
||||
all: $(VERSIONS)
|
||||
|
||||
# rip data from ROMs
|
||||
rip:
|
||||
node tools/decode-ff6.js
|
||||
|
||||
# encode-jp: ff6-jp-data.json
|
||||
# node tools/encode-ff6.js ff6-jp-data.json
|
||||
#
|
||||
# encode-en: ff6-en-data.json
|
||||
# node tools/encode-ff6.js ff6-en-data.json
|
||||
|
||||
# clean module subdirectories
|
||||
MODULES_CLEAN = $(foreach M, $(MODULES), $(M)_clean)
|
||||
|
||||
%_clean:
|
||||
$(MAKE) -C $* clean
|
||||
|
||||
clean: $(MODULES_CLEAN)
|
||||
$(RM) -r $(ROM_DIR)
|
||||
|
||||
# ROM filenames
|
||||
FF6_JP_PATH = $(ROM_DIR)/ff6-jp.sfc
|
||||
FF6_EN_PATH = $(ROM_DIR)/ff6-en.sfc
|
||||
FF6_EN1_PATH = $(ROM_DIR)/ff6-en1.sfc
|
||||
|
||||
ff6-jp: $(FF6_JP_PATH)
|
||||
ff6-en: $(FF6_EN_PATH)
|
||||
ff6-en1: $(FF6_EN1_PATH)
|
||||
|
||||
# set up target-specific variables
|
||||
ff6-jp: VERSION_EXT = jp
|
||||
ff6-jp: ASMFLAGS += -D ROM_VERSION=0
|
||||
|
||||
ff6-en: VERSION_EXT = en
|
||||
ff6-en: ASMFLAGS += -D LANG_EN=1 -D ROM_VERSION=0
|
||||
|
||||
ff6-en1: VERSION_EXT = en1
|
||||
ff6-en1: ASMFLAGS += -D LANG_EN=1 -D ROM_VERSION=1
|
||||
|
||||
# target-specific object filenames
|
||||
OBJ_FILES_JP = $(foreach M, $(MODULES), $(M)/obj/$(M)_jp.o)
|
||||
OBJ_FILES_EN = $(foreach M, $(MODULES), $(M)/obj/$(M)_en.o)
|
||||
OBJ_FILES_EN1 = $(foreach M, $(MODULES), $(M)/obj/$(M)_en1.o)
|
||||
|
||||
# rules for making ROM files
|
||||
$(FF6_JP_PATH): ff6-jp.lnk encode-jp $(OBJ_FILES_JP)
|
||||
@mkdir -p rom
|
||||
$(LINK) $(LINKFLAGS) -m $(@:sfc=map) -o $@ -C $< $(OBJ_FILES_JP)
|
||||
node tools/calc-checksum.js $@
|
||||
|
||||
$(FF6_EN_PATH): ff6-en.lnk encode-en $(OBJ_FILES_EN)
|
||||
@mkdir -p rom
|
||||
$(LINK) $(LINKFLAGS) -m $(@:sfc=map) -o $@ -C $< $(OBJ_FILES_EN)
|
||||
node tools/calc-checksum.js $@
|
||||
|
||||
$(FF6_EN1_PATH): ff6-en.lnk encode-en $(OBJ_FILES_EN1)
|
||||
@mkdir -p rom
|
||||
$(LINK) $(LINKFLAGS) -m $(@:sfc=map) -o $@ -C $< $(OBJ_FILES_EN1)
|
||||
node tools/calc-checksum.js $@
|
||||
|
||||
# run sub-make to create object files for each module
|
||||
$(OBJ_FILES_JP): $(MODULES)
|
||||
$(OBJ_FILES_EN): $(MODULES)
|
||||
$(OBJ_FILES_EN1): $(MODULES)
|
||||
|
||||
# rules for making modules in subdirectories
|
||||
define MAKE_MODULE
|
||||
$1/obj/$1_%.o:
|
||||
$$(MAKE) -C $1
|
||||
endef
|
||||
|
||||
$(foreach M, $(MODULES), $(eval $(call MAKE_MODULE,$(M))))
|
225
README.md
225
README.md
@ -1,2 +1,223 @@
|
||||
# ff6
|
||||
Disassembly and ROM info for Final Fantasy VI
|
||||
# Final Fantasy VI Disassembly
|
||||
|
||||
This is a disassembly of Final Fantasy VI for the Super Famicom (i.e. Final
|
||||
Fantasy III for the SNES). It is a work in progress which partially builds
|
||||
the following ROMs:
|
||||
|
||||
- Final Fantasy III 1.0 (U), CRC32: `0xA27F1C7A`
|
||||
- Final Fantasy III 1.1 (U), CRC32: `0xC0FA0464`
|
||||
|
||||
The Japanese version is not currently supported.
|
||||
|
||||
## Build Instructions
|
||||
|
||||
You will need a Unix-like shell to build the ROM. If you are on a Mac or
|
||||
Linux, simply open a terminal. If you are using Windows, you will need to
|
||||
use a Unix-like runtime environment such as Cygwin: https://www.cygwin.com.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
First, install the following dependencies if you don't have them already.
|
||||
|
||||
- GNU Make: https://www.gnu.org/software/make/
|
||||
- cc65: https://cc65.github.io
|
||||
- node: https://nodejs.org
|
||||
|
||||
Here is a very nice tutorial explaining how to set up Cygwin and cc65 on
|
||||
a Windows machine: https://github.com/SlithyMatt/x16-hello-cc65
|
||||
|
||||
### Clone the Repo
|
||||
|
||||
If you have git installed, run `git clone
|
||||
https://github.com/everything8215/ff4.git`. Otherwise, click on "Code" in
|
||||
GitHub and select "Download ZIP" to copy the repo to your computer.
|
||||
|
||||
### Install Node.js Modules
|
||||
|
||||
In the root directory, run `npm install` to install Node.js modules needed
|
||||
to build the ROMs.
|
||||
|
||||
### Rip ROM Data
|
||||
|
||||
Copy an unmodified FF6 ROM file into the "vanilla" directory. If you want to
|
||||
build both the Japanese version and the English version, you will need to copy
|
||||
both ROMs. If your ROMs have a 512-byte copier header, you will need to remove
|
||||
if. There are many tools available on romhacking.net that can detect and
|
||||
remove a copier header from a ROM file.
|
||||
|
||||
All of the data can be extracted from either a v1.0 ROM or a v1.1 ROM. For
|
||||
this step there is no difference between the two versions.
|
||||
|
||||
Next, run `make rip` in the root directory to extract all of the required data
|
||||
from your ROMs. You should only need to do this once. The extracted data will
|
||||
be saved in the module directories. If you later wish to revert any of these
|
||||
files to their original state, simply delete those files and run `make rip`
|
||||
again, as it will only create files that do not exist and will not affect
|
||||
existing files.
|
||||
|
||||
All ROM data will also be decoded and saved to a json file in the root
|
||||
directory called either ff6-en-data.json or ff6-jp-data.json. Data in these
|
||||
files can be modified and then encoded into source files when a ROM is
|
||||
assembled. Editing the data file will eventually be done with the FFTools
|
||||
editor, which is currently in development
|
||||
(https://github.com/everything8215/fftools).
|
||||
It is also possible to change simple things like text and monster HP
|
||||
by editing the json file. A top-level object called `"obj"` contains all of
|
||||
the data objects. After editing an object, find the corresponding entry in the
|
||||
top-level object called `"assembly"` and add the following property:
|
||||
`"isDirty": true`. This will notify the encoding script that the assembly
|
||||
file containing this object's data needs to be updated. This procedure will
|
||||
be automated by FFTools, but for now it needs to be done manually.
|
||||
|
||||
### Assemble and Link ROM File
|
||||
|
||||
Run `make <version>` to make the version of the ROM that you want, where
|
||||
`<version>` is one of the following values:
|
||||
|
||||
- `ff6-en`: Final Fantasy III 1.0 (U)
|
||||
- `ff6-en1`: Final Fantasy III 1.1 (U)
|
||||
|
||||
The ROM will be created in the `rom` directory. If you have ripped data from
|
||||
both the Japanese and English versions, you can also run `make all` to make
|
||||
all four ROMs.
|
||||
|
||||
After building the vanilla ROMs, you are free to modify the code and data as
|
||||
you like, then run make again to rebuild the ROM. Some switchable config
|
||||
options can be found in the file `include/const.inc`. This includes several
|
||||
bugfixes and options to skip the intro and disable random battles.
|
||||
|
||||
## Distributing ROM Hacks
|
||||
|
||||
To avoid legal troubles, I believe that it's important to avoid distributing
|
||||
copyrighted intellectual property. This repository does not contain any such
|
||||
material, and instead allows you to extract all necessary data from your ROM
|
||||
files.
|
||||
|
||||
ROM hacks are typically distributed by generating a patch file which
|
||||
modifies a few bytes when applied to a ROM file. However, reassembling an
|
||||
entire ROM from scratch can cause large blocks of data to be shuffled around,
|
||||
resulting in patch files which contain copyrighted data when using the IPS
|
||||
patch format. To avoid this, ROM hacks made from this code should be
|
||||
distributed by creating forks of this repository or by using more sophisticated
|
||||
patch formats which are able to differentiate data that has been modified from
|
||||
data that has simply been relocated (i.e. XDelta or Delta BPS).
|
||||
|
||||
## Format and Organization
|
||||
|
||||
In order to create a somewhat cohesive and standardized disassembly, I try to
|
||||
follow a set of rules for how files in the repo are formatted and organized.
|
||||
|
||||
### Assembler and Linker
|
||||
|
||||
The code for Final Fantasy games is typically split into several distinct
|
||||
modules. The field or map module and the battle module are always present.
|
||||
The battle code is often split into two parts, one for battle mechanics and
|
||||
the other for battle graphics. There is also a menu module, though in some of
|
||||
the early games the menu code was mixed in with the field code. The music and
|
||||
sound code is also always in a separate module. Other modules can include
|
||||
cutscenes, intro/ending credits, special effects, and the 3D world map for
|
||||
FF6.
|
||||
|
||||
Because of this modular structure, I find it convenient to assemble each
|
||||
module as a single object file and then link the modules together to create
|
||||
the ROM file. These two steps are done using ca65 and ld65, respectively.
|
||||
This strategy leads to a reasonable number of import/export commands, as the
|
||||
separate modules only interact with one another via a small number of external
|
||||
subroutines and data locations.
|
||||
|
||||
### File Formats, Names, and Extensions
|
||||
|
||||
Assembly files have the extension '.asm'. This includes files which define ROM
|
||||
data, scripts, and memory labels but contain no actual code. In most cases,
|
||||
assembly files should only be assembled once. The only exception is when
|
||||
the ROM contains multiple identical copies of the same subroutine or data.
|
||||
|
||||
Include files have the extension '.inc'. These files should are meant to be
|
||||
included multiple times and should not output any bytes to the assembler or
|
||||
reserve any memory addresses. Examples include macro definitions and hardware
|
||||
address definitions.
|
||||
|
||||
Assembly and include files should not have lines longer than 80 characters.
|
||||
|
||||
### File Organization
|
||||
|
||||
Each of the modules described above is in a separate directory. This mimics
|
||||
my best guess as to how the original source code was organized based on e.g.
|
||||
the Playstation releases where each module had a directory named after the
|
||||
main programmer for that module ('NARITA', 'YOSHII', etc.). Each directory
|
||||
contains all of the source code and data as well as a GNU Makefile to assemble
|
||||
everything into a single object file. The root directory contains a Makefile
|
||||
to link all of the object files together to create the ROM.
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
As a naming convention for symbols, I've chosen to follow the example of the
|
||||
Pokémon reverse engineering team (https://github.com/pret). Subroutine names
|
||||
and labels for data in the ROM use PascalCase. Acronyms like RAM appear in all
|
||||
capitals (i.e. InitRAM). This differs from conventional camel-case
|
||||
capitalization rules.
|
||||
|
||||
External subroutines (which can be called by other modules) get the special
|
||||
suffix '_ext'. Also, dummy subroutines called by another bank (typically a
|
||||
jsr followed by rtl or jsl followed by rts on the 65c816) get the special
|
||||
suffix '_far' or '_near', respectively.
|
||||
|
||||
Labels for unknown subroutines are the 6-digit ROM address of the subroutine
|
||||
(including the bank) preceded by an underscore, e.g. `_c28566`.
|
||||
|
||||
Local labels inside subroutines use mixed case with a prepended '@' symbol,
|
||||
which is ca65's default symbol to identify a local label. Most local labels
|
||||
are unnamed, and instead use the 4-digit ROM address (excluding the bank). I
|
||||
also typically include a local label at the start of each subroutine so that
|
||||
it can be compared to the original ROM file, but these are just for convenience
|
||||
and can be removed eventually.
|
||||
|
||||
WRAM and SRAM labels begin with a lowercase 'w' or 's' followed by a
|
||||
descriptive name in PascalCase case, e.g. `wSpriteData`.
|
||||
|
||||
Hardware registers are a slight exception to the rule. I chose to use the
|
||||
official register names from the SNES development manual in all caps,
|
||||
prepended with a lowercase 'h' (i.e. `$2100` is `hINIDISP`).
|
||||
|
||||
Instruction mnemonics and macro names are in all lowercase. Macro names can
|
||||
include underscores to improve readability. Constants are in all uppercase
|
||||
with underscores between words.
|
||||
|
||||
To shorten subroutine and label names, the following shortened words may be
|
||||
used:
|
||||
|
||||
- anim: animation
|
||||
- btm: bottom
|
||||
- char: character
|
||||
- cmd: command
|
||||
- ctrl: control or controller
|
||||
- dec: decrement
|
||||
- div: divide
|
||||
- dlg: dialogue
|
||||
- dur: duration
|
||||
- elem: element
|
||||
- exec: execute
|
||||
- gfx: graphics
|
||||
- grp: group
|
||||
- inc: increment
|
||||
- init: initialize
|
||||
- mod: modify or modifier
|
||||
- msg: message
|
||||
- mult: multiply or multiplier
|
||||
- obj: object
|
||||
- qty: quantity
|
||||
- pal: palette
|
||||
- prop: properties
|
||||
- ptr: pointer
|
||||
- rand: random
|
||||
- reg: register
|
||||
- sfx: sound effect
|
||||
- tbl: table
|
||||
|
||||
### Tabs, Spaces, and Comments
|
||||
|
||||
Never use tabs. Always use spaces. In assembly files, labels begin in column 1,
|
||||
instructions and macros begin in column 9, operands begin in column 17, and
|
||||
comments can begin in column 41. Long comments that would extend beyond the
|
||||
80-character limit should be placed on their own line before the assembly
|
||||
code that they describe.
|
||||
|
26
battle/Makefile
Normal file
26
battle/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = battle
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
65
battle/battle.asm
Normal file
65
battle/battle.asm
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: battle.asm |
|
||||
; | |
|
||||
; | description: battle program |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.p816
|
||||
|
||||
.include "const.inc"
|
||||
.include "hardware.inc"
|
||||
.include "macros.inc"
|
||||
|
||||
.include "battle_data.asm"
|
||||
|
||||
.export Battle_ext, UpdateBattleTime_ext
|
||||
.export UpdateEquip_ext, CalcMagicEffect_ext
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "battle_code"
|
||||
.a8
|
||||
.i16
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
Battle_ext:
|
||||
@0000: jmp BattleMain
|
||||
|
||||
UpdateBattleTime_ext:
|
||||
@0003: jmp UpdateBattleTime
|
||||
|
||||
UpdateEquip_ext:
|
||||
@0006: jmp UpdateEquip
|
||||
|
||||
CalcMagicEffect_ext:
|
||||
@0009: jmp CalcMagicEffect
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
BattleMain:
|
||||
@000c: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
UpdateEquip:
|
||||
@0e77: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
UpdateBattleTime:
|
||||
@111b: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
CalcMagicEffect:
|
||||
@4730: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
29
battle/battle_data.asm
Normal file
29
battle/battle_data.asm
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: battle_data.asm |
|
||||
; | |
|
||||
; | description: data for battle module |
|
||||
; | |
|
||||
; | created: 8/13/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.export WorldBattleRate, SubBattleRate
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "battle_data"
|
||||
|
||||
; cf/0000
|
||||
.res $5800
|
||||
|
||||
; cf/5800
|
||||
.include "data/world_battle_rate.asm"
|
||||
|
||||
; cf/5880
|
||||
.include "data/sub_battle_rate.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
26
btlgfx/Makefile
Normal file
26
btlgfx/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = btlgfx
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
26
btlgfx/btlgfx.asm
Normal file
26
btlgfx/btlgfx.asm
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: btlgfx.asm |
|
||||
; | |
|
||||
; | description: battle graphics program |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.p816
|
||||
|
||||
.include "const.inc"
|
||||
.include "hardware.inc"
|
||||
.include "macros.inc"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "btlgfx_code"
|
||||
.a8
|
||||
.i16
|
||||
|
||||
; ------------------------------------------------------------------------------
|
26
cutscene/Makefile
Normal file
26
cutscene/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = cutscene
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
65
cutscene/cutscene.asm
Normal file
65
cutscene/cutscene.asm
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: cutscene.asm |
|
||||
; | |
|
||||
; | description: cutscene program |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.p816
|
||||
|
||||
.include "const.inc"
|
||||
.include "hardware.inc"
|
||||
.include "macros.inc"
|
||||
|
||||
.export OpeningCredits_ext, TitleScreen_ext
|
||||
.export FloatingIslandScene_ext, WorldOfRuinScene_ext
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "cutscene_code"
|
||||
.a8
|
||||
.i16
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
OpeningCredits_ext:
|
||||
@6800: jmp OpeningCredits
|
||||
|
||||
TitleScreen_ext:
|
||||
@6803: jmp TitleScreen
|
||||
|
||||
FloatingIslandScene_ext:
|
||||
@6806: jmp FloatingIslandScene
|
||||
|
||||
WorldOfRuinScene_ext:
|
||||
@6809: jmp WorldOfRuinScene
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
TitleScreen:
|
||||
@680c: lda #$ff ; set return code to start a new game
|
||||
sta $0200
|
||||
rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
OpeningCredits:
|
||||
@6813: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
FloatingIslandScene:
|
||||
@681a: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
WorldOfRuinScene:
|
||||
@6821: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
73
ff6-en.lnk
Normal file
73
ff6-en.lnk
Normal file
@ -0,0 +1,73 @@
|
||||
memory {
|
||||
ram: start = $000000, size = $2000, type = rw;
|
||||
wram: start = $7e2000, size = $01e000, type = rw;
|
||||
sram: start = $306000, size = $2000, type = rw;
|
||||
bank_c0: start = $c00000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c1: start = $c10000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c2: start = $c20000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c3: start = $c30000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c4: start = $c40000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c5: start = $c50000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c6: start = $c60000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c7: start = $c70000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c8: start = $c80000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_c9: start = $c90000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_ca: start = $ca0000, size = $2e600, type = ro, fill = yes, fillval = $ff;
|
||||
bank_cc: start = $cce600, size = $1a00, type = ro, fill = yes, fillval = $ff;
|
||||
bank_cd: start = $cd0000, size = $20000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_cf: start = $cf0000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d0: start = $d00000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d1: start = $d10000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d2: start = $d20000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d3: start = $d30000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d4: start = $d40000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d5: start = $d50000, size = $40000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_d9: start = $d90000, size = $50000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_de: start = $de0000, size = $7fa00, type = ro, fill = yes, fillval = $ff;
|
||||
bank_e5: start = $e5f400, size = $0600, type = ro, fill = yes, fillval = $ff;
|
||||
bank_e6: start = $e60000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_e7: start = $e70000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_e8: start = $e80000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_e9: start = $e90000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_ea: start = $ea0000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_eb: start = $eb0000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_ec: start = $ec0000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_ed: start = $ed0000, size = $10000, type = ro, fill = yes, fillval = $ff;
|
||||
bank_ee: start = $ee0000, size = $20000, type = ro, fill = yes, fillval = $ff;
|
||||
}
|
||||
|
||||
segments {
|
||||
# wram: load = wram, type = bss;
|
||||
# sram: load = sram, type = bss;
|
||||
|
||||
field_code: load = bank_c0, type = ro;
|
||||
field_data: load = bank_c0, type = ro, start = $c0dfa0;
|
||||
interrupt: load = bank_c0, type = ro, start = $c0ff00;
|
||||
snes_header_ext: load = bank_c0, type = ro, start = $c0ffb0;
|
||||
snes_header: load = bank_c0, type = ro, start = $c0ffc0;
|
||||
vectors: load = bank_c0, type = ro, start = $c0ffe0;
|
||||
btlgfx_code: load = bank_c1, type = ro;
|
||||
battle_code: load = bank_c2, type = ro;
|
||||
cutscene_code: load = bank_c2, type = ro, start = $c26800;
|
||||
menu_code: load = bank_c3, type = ro;
|
||||
event_triggers: load = bank_c4, type = ro;
|
||||
font_gfx: load = bank_c4, type = ro, start = $c487c0;
|
||||
sound_code: load = bank_c5, type = ro;
|
||||
dialogue_ptrs: load = bank_cc, type = ro, start = $cce600;
|
||||
dialogue: load = bank_cd, type = ro;
|
||||
event_script: load = bank_ca, type = ro;
|
||||
battle_data: load = bank_cf, type = ro;
|
||||
map_init_event: load = bank_d1, type = ro, start = $d1fa00;
|
||||
world_pal: load = bank_d2, type = ro, start = $d2ec00;
|
||||
map_sprite_gfx: load = bank_d5, type = ro;
|
||||
mode7_cutscene_data: load = bank_d5, type = ro, start = $d8dd00;
|
||||
map_tile_data: load = bank_d9, type = ro, start = $d9a800;
|
||||
map_tile_data2: load = bank_de, type = ro, start = $de0000;
|
||||
map_gfx: load = bank_de, type = ro, start = $dfda00;
|
||||
map_gfx2: load = bank_e6, type = ro;
|
||||
window_gfx: load = bank_ed, type = ro;
|
||||
map_data: load = bank_ed, type = ro, start = $ed8f00;
|
||||
world_code: load = bank_ee, type = ro;
|
||||
world_data: load = bank_ee, type = ro, start = $eeb200;
|
||||
world_sine: load = bank_ee, type = ro, start = $effef0;
|
||||
}
|
26
field/Makefile
Normal file
26
field/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = field
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
24888
field/field.asm
Normal file
24888
field/field.asm
Normal file
File diff suppressed because it is too large
Load Diff
265
field/field_data.asm
Normal file
265
field/field_data.asm
Normal file
@ -0,0 +1,265 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: field_data.asm |
|
||||
; | |
|
||||
; | description: data for field module |
|
||||
; | |
|
||||
; | created: 8/9/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.export RNGTbl
|
||||
.export ShortEntrancePtrs, EventTriggerPtrs
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "field_data"
|
||||
|
||||
; c0/dfa0
|
||||
.include "data/dte_table.asm"
|
||||
|
||||
; c0/e0a0
|
||||
.include "data/init_npc_switch.asm"
|
||||
|
||||
; c0/e120
|
||||
.res $0180
|
||||
|
||||
; c0/e2a0
|
||||
.include "gfx/overlay_gfx.asm"
|
||||
.res $0c00+OverlayGfx-*
|
||||
|
||||
; c0/eea0
|
||||
.include "data/overlay_tilemap.asm"
|
||||
|
||||
; c0/f4a0
|
||||
OverlayPropPtrs:
|
||||
make_ptr_tbl_rel OverlayProp, 45
|
||||
.res $60+OverlayPropPtrs-*
|
||||
|
||||
; c0/f500
|
||||
.include "data/overlay_prop.asm"
|
||||
.res $0800+OverlayProp-*
|
||||
|
||||
; c0/fd00
|
||||
.include "data/rng_tbl.asm"
|
||||
|
||||
; c0/fe00
|
||||
.include "data/map_color_math.asm"
|
||||
.res $40+MapColorMath-*
|
||||
|
||||
; c0/fe40
|
||||
.include "data/map_parallax.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "event_triggers"
|
||||
|
||||
; c4/0000
|
||||
EventTriggerPtrs:
|
||||
make_ptr_tbl_rel EventTrigger, 416, EventTriggerPtrs
|
||||
.addr EventTriggerEnd - EventTrigger
|
||||
|
||||
; c4/0342
|
||||
.include "data/event_trigger.asm"
|
||||
EventTriggerEnd:
|
||||
.res $1a10+EventTriggerPtrs-*
|
||||
|
||||
; c4/1a10
|
||||
NPCPropPtrs:
|
||||
make_ptr_tbl_rel NPCProp, 416, NPCPropPtrs
|
||||
.addr NPCPropEnd - NPCProp
|
||||
|
||||
; c4/1d52
|
||||
.include "data/npc_prop.asm"
|
||||
NPCPropEnd:
|
||||
.res $50b0+NPCPropPtrs-*
|
||||
|
||||
; c4/6ac0
|
||||
.res $0e00
|
||||
|
||||
; c4/78c0
|
||||
.include "text/char_name_en.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "font_gfx"
|
||||
|
||||
; c4/87c0
|
||||
.include "gfx/font_gfx_small.asm"
|
||||
|
||||
; c4/8fc0
|
||||
.include "data/font_width.asm"
|
||||
|
||||
; c4/90c0
|
||||
.include "gfx/font_gfx_large.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "event_script"
|
||||
|
||||
; ca/0000
|
||||
.include "data/event_script.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "dialogue_ptrs"
|
||||
|
||||
; cc/e600
|
||||
DlgBankInc:
|
||||
.word $0626
|
||||
|
||||
; cc/e602
|
||||
DlgPtrs:
|
||||
make_ptr_tbl_rel Dlg1, 1574
|
||||
make_ptr_tbl_rel Dlg2, 1510, Dlg1+$10000
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "dialogue"
|
||||
|
||||
; cd/0000
|
||||
.include "text/dlg1.asm"
|
||||
.include "text/dlg2.asm"
|
||||
.res $01f100+Dlg1-*
|
||||
|
||||
; ce/f100
|
||||
.include "text/map_title_en.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_init_event"
|
||||
|
||||
; d1/fa00
|
||||
.include "data/map_init_event.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_sprite_gfx"
|
||||
|
||||
; d5/0000
|
||||
.include "gfx/map_sprite_gfx.asm"
|
||||
|
||||
; d8/3000
|
||||
.include "gfx/map_vehicle_gfx.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_tile_data"
|
||||
|
||||
; d9/a800
|
||||
.include "data/map_tile_prop.asm"
|
||||
MapTilePropEnd:
|
||||
.res $2510+MapTileProp-*
|
||||
|
||||
; d9/cd10
|
||||
MapTilePropPtrs:
|
||||
make_ptr_tbl_rel MapTileProp, 42
|
||||
.addr MapTilePropEnd - MapTileProp
|
||||
.res $80+MapTilePropPtrs-*
|
||||
|
||||
; d9/cd90
|
||||
SubTilemapPtrs:
|
||||
make_ptr_tbl_far SubTilemap, 350
|
||||
.faraddr SubTilemapEnd - SubTilemap
|
||||
.res $420+SubTilemapPtrs-*
|
||||
|
||||
; d9/d1b0
|
||||
.include "data/sub_tilemap.asm"
|
||||
SubTilemapEnd:
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_tile_data2"
|
||||
|
||||
; de/0000
|
||||
.include "data/map_tileset.asm"
|
||||
.res $01b400+MapTileset-*
|
||||
|
||||
; df/b400
|
||||
.res $0200
|
||||
|
||||
; df/b600
|
||||
.res $0400
|
||||
|
||||
; df/ba00
|
||||
MapTilesetPtrs:
|
||||
make_ptr_tbl_far MapTileset, 75
|
||||
.res $0100+MapTilesetPtrs-*
|
||||
|
||||
; df/bb00
|
||||
ShortEntrancePtrs:
|
||||
make_ptr_tbl_rel ShortEntrance, 416, ShortEntrancePtrs
|
||||
.addr ShortEntranceEnd - ShortEntrancePtrs
|
||||
|
||||
; df/bf02
|
||||
.include "data/short_entrance.asm"
|
||||
ShortEntranceEnd:
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_gfx"
|
||||
|
||||
; df/da00
|
||||
MapGfxPtrs:
|
||||
make_ptr_tbl_far MapGfx, 82
|
||||
.res $0100+MapGfxPtrs-*
|
||||
|
||||
; df/db00
|
||||
.include "gfx/map_gfx.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_gfx2"
|
||||
|
||||
; e6/0000
|
||||
.include "gfx/map_anim_gfx.asm"
|
||||
|
||||
; e6/8000
|
||||
.include "gfx/map_sprite_pal.asm"
|
||||
|
||||
; e6/8400
|
||||
MapTitlePtrs:
|
||||
make_ptr_tbl_rel MapTitle, 73
|
||||
.res $0380+MapTitlePtrs-*
|
||||
|
||||
; e6/8780
|
||||
.include "gfx/map_gfx_bg3.asm"
|
||||
.res $45E0+MapGfxBG3-*
|
||||
|
||||
; e6/cd60
|
||||
MapGfxBG3Ptrs:
|
||||
make_ptr_tbl_far MapGfxBG3, 18
|
||||
.res $40+MapGfxBG3Ptrs-*
|
||||
|
||||
; e6/cda0
|
||||
MapAnimGfxBG3Ptrs:
|
||||
make_ptr_tbl_far MapAnimGfxBG3, 6
|
||||
.res $20+MapAnimGfxBG3Ptrs-*
|
||||
|
||||
; e6/cdc0
|
||||
.include "gfx/map_anim_gfx_bg3.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "map_data"
|
||||
|
||||
; ed/8f00
|
||||
.include "data/map_prop.asm"
|
||||
.res 1
|
||||
|
||||
; ed/c480
|
||||
.include "gfx/map_pal.asm"
|
||||
|
||||
; ed/f480
|
||||
LongEntrancePtrs:
|
||||
make_ptr_tbl_rel LongEntrance, 416, LongEntrancePtrs
|
||||
.addr LongEntranceEnd - LongEntrancePtrs
|
||||
|
||||
; ed/f882
|
||||
.include "data/long_entrance.asm"
|
||||
LongEntranceEnd:
|
||||
|
||||
; ------------------------------------------------------------------------------
|
91
field/header.asm
Normal file
91
field/header.asm
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: header.asm |
|
||||
; | |
|
||||
; | description: snes cartridge header |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.segment "interrupt"
|
||||
|
||||
JmpReset:
|
||||
@ff00: sei
|
||||
clc
|
||||
xce
|
||||
jml Reset
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ nmi ]
|
||||
|
||||
.align $10
|
||||
|
||||
JmpNMI:
|
||||
@ff10: jml $001500
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ irq ]
|
||||
|
||||
JmpIRQ:
|
||||
@ff14: jml $001504
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.if LANG_EN
|
||||
|
||||
.segment "snes_header_ext"
|
||||
|
||||
SnesHeaderExt:
|
||||
@ffb0:
|
||||
.byte "C3" ; publisher: squaresoft
|
||||
.byte "F6 " ; game code
|
||||
.byte 0,0,0,0,0,0,0,0,0,0
|
||||
.endif
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "snes_header"
|
||||
|
||||
SnesHeader:
|
||||
@ffc0:
|
||||
.if LANG_EN
|
||||
.byte "FINAL FANTASY 3 " ; rom title (U)
|
||||
.else
|
||||
.byte "FINAL FANTASY 6 " ; rom title (J)
|
||||
.endif
|
||||
.byte $31 ; HiROM, FastROM
|
||||
.byte $02 ; rom + ram + sram
|
||||
.byte $0c ; rom size: 48 Mbit
|
||||
.byte $03 ; sram size: 64 kbit
|
||||
.if LANG_EN
|
||||
.byte $01 ; destination: north america
|
||||
.byte $33 ; use extended header
|
||||
.else
|
||||
.byte $00 ; destination: japan
|
||||
.byte $c3 ; publisher: squaresoft
|
||||
.endif
|
||||
.byte ROM_VERSION ; revision number (0 or 1)
|
||||
.word 0 ; checksum (calculate later)
|
||||
HeaderChecksum:
|
||||
.word $ffff ; inverse checksum
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "vectors"
|
||||
|
||||
Vectors:
|
||||
@ffe0: .res 10
|
||||
@ffea: .addr JmpNMI
|
||||
.res 2
|
||||
@ffee: .addr JmpIRQ
|
||||
.res 12
|
||||
@fffc: .addr JmpReset
|
||||
.res 2
|
||||
|
||||
; ------------------------------------------------------------------------------
|
34
include/const.inc
Normal file
34
include/const.inc
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: const.inc |
|
||||
; | |
|
||||
; | description: global constant definitions |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; | |
|
||||
; | author: everything8215@gmail.com |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.list off ; disable listing
|
||||
|
||||
; define a config constant if it's not already defined
|
||||
.macro def_config _const, _value
|
||||
.ifndef _const
|
||||
_const = _value
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
def_config DEBUG,0
|
||||
def_config LANG_EN,0
|
||||
|
||||
.if LANG_EN
|
||||
.define LANG_SUFFIX "en"
|
||||
.else
|
||||
.define LANG_SUFFIX "jp"
|
||||
.endif
|
||||
|
||||
.list on
|
241
include/hardware.inc
Normal file
241
include/hardware.inc
Normal file
@ -0,0 +1,241 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: hardware.inc |
|
||||
; | |
|
||||
; | description: snes hardware register definitions |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; | |
|
||||
; | author: everything8215@gmail.com |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.list off
|
||||
|
||||
; [ ppu registers ]
|
||||
|
||||
hINIDISP := $2100
|
||||
hOBJSEL := $2101
|
||||
hOAMADDL := $2102
|
||||
hOAMADDH := $2103
|
||||
hOAMDATA := $2104
|
||||
hBGMODE := $2105
|
||||
hMOSAIC := $2106
|
||||
hBG1SC := $2107
|
||||
hBG2SC := $2108
|
||||
hBG3SC := $2109
|
||||
hBG4SC := $210a
|
||||
hBG12NBA := $210b
|
||||
hBG34NBA := $210c
|
||||
hBG1HOFS := $210d
|
||||
hBG1VOFS := $210e
|
||||
hBG2HOFS := $210f
|
||||
hBG2VOFS := $2110
|
||||
hBG3HOFS := $2111
|
||||
hBG3VOFS := $2112
|
||||
hBG4HOFS := $2113
|
||||
hBG4VOFS := $2114
|
||||
hVMAINC := $2115
|
||||
hVMADDL := $2116
|
||||
hVMADDH := $2117
|
||||
hVMDATAL := $2118
|
||||
hVMDATAH := $2119
|
||||
hM7SEL := $211a
|
||||
hM7A := $211b
|
||||
hM7B := $211c
|
||||
hM7C := $211d
|
||||
hM7D := $211e
|
||||
hM7X := $211f
|
||||
hM7Y := $2120
|
||||
hCGADD := $2121
|
||||
hCGDATA := $2122
|
||||
hW12SEL := $2123
|
||||
hW34SEL := $2124
|
||||
hWOBJSEL := $2125
|
||||
hWH0 := $2126
|
||||
hWH1 := $2127
|
||||
hWH2 := $2128
|
||||
hWH3 := $2129
|
||||
hWBGLOG := $212a
|
||||
hWOBJLOG := $212b
|
||||
hTM := $212c
|
||||
hTS := $212d
|
||||
hTMW := $212e
|
||||
hTSW := $212f
|
||||
hCGSWSEL := $2130
|
||||
hCGADSUB := $2131
|
||||
hCOLDATA := $2132
|
||||
hSETINI := $2133
|
||||
hMPYL := $2134
|
||||
hMPYM := $2135
|
||||
hMPYH := $2136
|
||||
hSLHV := $2137
|
||||
hROAMDATA := $2138
|
||||
hRVMDATAL := $2139
|
||||
hRVMDATAH := $213a
|
||||
hRCGDATA := $213b
|
||||
hOPHCT := $213c
|
||||
hOPVCT := $213d
|
||||
hSTAT77 := $213e
|
||||
hSTAT78 := $213f
|
||||
hAPUIO0 := $2140
|
||||
hAPUIO1 := $2141
|
||||
hAPUIO2 := $2142
|
||||
hAPUIO3 := $2143
|
||||
hWMDATA := $2180
|
||||
hWMADDL := $2181
|
||||
hWMADDM := $2182
|
||||
hWMADDH := $2183
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ cpu registers ]
|
||||
|
||||
hNMITIMEN := $4200
|
||||
hWRIO := $4201
|
||||
hWRMPYA := $4202
|
||||
hWRMPYB := $4203
|
||||
hWRDIVL := $4204
|
||||
hWRDIVH := $4205
|
||||
hWRDIVB := $4206
|
||||
hHTIMEL := $4207
|
||||
hHTIMEH := $4208
|
||||
hVTIMEL := $4209
|
||||
hVTIMEH := $420a
|
||||
hMDMAEN := $420b
|
||||
hHDMAEN := $420c
|
||||
hMEMSEL := $420d
|
||||
hRDNMI := $4210
|
||||
hTIMEUP := $4211
|
||||
hHVBJOY := $4212
|
||||
hRDIO := $4213
|
||||
hRDDIVL := $4214
|
||||
hRDDIVH := $4215
|
||||
hRDMPYL := $4216
|
||||
hRDMPYH := $4217
|
||||
hSTDCNTRL1L := $4218
|
||||
hSTDCNTRL1H := $4219
|
||||
hSTDCNTRL2L := $421a
|
||||
hSTDCNTRL2H := $421b
|
||||
hSTDCNTRL3L := $421c
|
||||
hSTDCNTRL3H := $421d
|
||||
hSTDCNTRL4L := $421e
|
||||
hSTDCNTRL4H := $421f
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ dma registers ]
|
||||
|
||||
hDMAP0 := $4300
|
||||
hDMAB0 := $4301
|
||||
hDMAAL0 := $4302
|
||||
hDMAAH0 := $4303
|
||||
hDMAAB0 := $4304
|
||||
hDMADL0 := $4305
|
||||
hDMADH0 := $4306
|
||||
hDMADB0 := $4307
|
||||
hDMATL0 := $4308
|
||||
hDMATH0 := $4309
|
||||
hDMAL0 := $430a
|
||||
hDMAP1 := $4310
|
||||
hDMAB1 := $4311
|
||||
hDMAAL1 := $4312
|
||||
hDMAAH1 := $4313
|
||||
hDMAAB1 := $4314
|
||||
hDMADL1 := $4315
|
||||
hDMADH1 := $4316
|
||||
hDMADB1 := $4317
|
||||
hDMATL1 := $4318
|
||||
hDMATH1 := $4319
|
||||
hDMAL1 := $431a
|
||||
hDMAP2 := $4320
|
||||
hDMAB2 := $4321
|
||||
hDMAAL2 := $4322
|
||||
hDMAAH2 := $4323
|
||||
hDMAAB2 := $4324
|
||||
hDMADL2 := $4325
|
||||
hDMADH2 := $4326
|
||||
hDMADB2 := $4327
|
||||
hDMATL2 := $4328
|
||||
hDMATH2 := $4329
|
||||
hDMAL2 := $432a
|
||||
hDMAP3 := $4330
|
||||
hDMAB3 := $4331
|
||||
hDMAAL3 := $4332
|
||||
hDMAAH3 := $4333
|
||||
hDMAAB3 := $4334
|
||||
hDMADL3 := $4335
|
||||
hDMADH3 := $4336
|
||||
hDMADB3 := $4337
|
||||
hDMATL3 := $4338
|
||||
hDMATH3 := $4339
|
||||
hDMAL3 := $433a
|
||||
hDMAP4 := $4340
|
||||
hDMAB4 := $4341
|
||||
hDMAAL4 := $4342
|
||||
hDMAAH4 := $4343
|
||||
hDMAAB4 := $4344
|
||||
hDMADL4 := $4345
|
||||
hDMADH4 := $4346
|
||||
hDMADB4 := $4347
|
||||
hDMATL4 := $4348
|
||||
hDMATH4 := $4349
|
||||
hDMAL4 := $434a
|
||||
hDMAP5 := $4350
|
||||
hDMAB5 := $4351
|
||||
hDMAAL5 := $4352
|
||||
hDMAAH5 := $4353
|
||||
hDMAAB5 := $4354
|
||||
hDMADL5 := $4355
|
||||
hDMADH5 := $4356
|
||||
hDMADB5 := $4357
|
||||
hDMATL5 := $4358
|
||||
hDMATH5 := $4359
|
||||
hDMAL5 := $435a
|
||||
hDMAP6 := $4360
|
||||
hDMAB6 := $4361
|
||||
hDMAAL6 := $4362
|
||||
hDMAAH6 := $4363
|
||||
hDMAAB6 := $4364
|
||||
hDMADL6 := $4365
|
||||
hDMADH6 := $4366
|
||||
hDMADB6 := $4367
|
||||
hDMATL6 := $4368
|
||||
hDMATH6 := $4369
|
||||
hDMAL6 := $436a
|
||||
hDMAP7 := $4370
|
||||
hDMAB7 := $4371
|
||||
hDMAAL7 := $4372
|
||||
hDMAAH7 := $4373
|
||||
hDMAAB7 := $4374
|
||||
hDMADL7 := $4375
|
||||
hDMADH7 := $4376
|
||||
hDMADB7 := $4377
|
||||
hDMATL7 := $4378
|
||||
hDMATH7 := $4379
|
||||
hDMAL7 := $437a
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ joypad button masks ]
|
||||
|
||||
JOY_A = %10000000
|
||||
JOY_X = %01000000
|
||||
JOY_L = %00100000
|
||||
JOY_R = %00010000
|
||||
|
||||
JOY_B = %10000000
|
||||
JOY_Y = %01000000
|
||||
JOY_SELECT = %00100000
|
||||
JOY_START = %00010000
|
||||
JOY_UP = %00001000
|
||||
JOY_DOWN = %00000100
|
||||
JOY_LEFT = %00000010
|
||||
JOY_RIGHT = %00000001
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.list on
|
343
include/macros.inc
Normal file
343
include/macros.inc
Normal file
@ -0,0 +1,343 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: macros.inc |
|
||||
; | |
|
||||
; | description: macro definitions |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; | |
|
||||
; | author: everything8215@gmail.com |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.list off
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ clr_a/x/y ]
|
||||
|
||||
; clear accumulator, x, y registers
|
||||
|
||||
.macro clr_a
|
||||
tdc
|
||||
.endmacro
|
||||
|
||||
.macro clr_ax
|
||||
tdc
|
||||
tax
|
||||
.endmacro
|
||||
|
||||
.macro clr_ay
|
||||
tdc
|
||||
tay
|
||||
.endmacro
|
||||
|
||||
.macro clr_axy
|
||||
tdc
|
||||
tax
|
||||
tay
|
||||
.endmacro
|
||||
|
||||
.macro clr_ayx
|
||||
tdc
|
||||
tay
|
||||
tax
|
||||
.endmacro
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ set the size of accumulator/index registers ]
|
||||
|
||||
PSW_A := %00100000
|
||||
PSW_I := %00010000
|
||||
PSW_C := %00000001
|
||||
|
||||
.macro longa
|
||||
.a16
|
||||
rep #PSW_A
|
||||
.endmacro
|
||||
|
||||
.macro shorta
|
||||
.a8
|
||||
sep #PSW_A
|
||||
.endmacro
|
||||
|
||||
.macro shorta0 ; battle code almost always clears
|
||||
clr_a ; accumulator before a shorta
|
||||
shorta
|
||||
.endmacro
|
||||
|
||||
.macro longi
|
||||
.i16
|
||||
rep #PSW_I
|
||||
.endmacro
|
||||
|
||||
.macro shorti
|
||||
.i8
|
||||
sep #PSW_I
|
||||
.endmacro
|
||||
|
||||
.macro longai
|
||||
.a16
|
||||
.i16
|
||||
rep #PSW_A|PSW_I
|
||||
.endmacro
|
||||
|
||||
.macro shortai
|
||||
.a8
|
||||
.i8
|
||||
sep #PSW_A|PSW_I
|
||||
.endmacro
|
||||
|
||||
.macro longac
|
||||
.a16
|
||||
rep #PSW_A|PSW_C
|
||||
.endmacro
|
||||
|
||||
.macro shortac
|
||||
.a8
|
||||
sep #PSW_A|PSW_C
|
||||
.endmacro
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated no operation ]
|
||||
|
||||
.macro nop_n n
|
||||
.repeat n
|
||||
nop
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define nop2 nop_n 2
|
||||
.define nop3 nop_n 3
|
||||
.define nop4 nop_n 4
|
||||
.define nop5 nop_n 5
|
||||
.define nop6 nop_n 6
|
||||
.define nop7 nop_n 7
|
||||
.define nop8 nop_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated shift left ]
|
||||
|
||||
.macro asl_n n
|
||||
.repeat n
|
||||
asl
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define asl2 asl_n 2
|
||||
.define asl3 asl_n 3
|
||||
.define asl4 asl_n 4
|
||||
.define asl5 asl_n 5
|
||||
.define asl6 asl_n 6
|
||||
.define asl7 asl_n 7
|
||||
.define asl8 asl_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated shift right ]
|
||||
|
||||
.macro lsr_n n
|
||||
.repeat n
|
||||
lsr
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define lsr2 lsr_n 2
|
||||
.define lsr3 lsr_n 3
|
||||
.define lsr4 lsr_n 4
|
||||
.define lsr5 lsr_n 5
|
||||
.define lsr6 lsr_n 6
|
||||
.define lsr7 lsr_n 7
|
||||
.define lsr8 lsr_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated rotate left ]
|
||||
|
||||
.macro rol_n n
|
||||
.repeat n
|
||||
rol
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define rol2 rol_n 2
|
||||
.define rol3 rol_n 3
|
||||
.define rol4 rol_n 4
|
||||
.define rol5 rol_n 5
|
||||
.define rol6 rol_n 6
|
||||
.define rol7 rol_n 7
|
||||
.define rol8 rol_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated rotate right ]
|
||||
|
||||
.macro ror_n n
|
||||
.repeat n
|
||||
ror
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define ror2 ror_n 2
|
||||
.define ror3 ror_n 3
|
||||
.define ror4 ror_n 4
|
||||
.define ror5 ror_n 5
|
||||
.define ror6 ror_n 6
|
||||
.define ror7 ror_n 7
|
||||
.define ror8 ror_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated increment ]
|
||||
|
||||
.macro inc_n n
|
||||
.repeat n
|
||||
inc
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define inc2 inc_n 2
|
||||
.define inc3 inc_n 3
|
||||
.define inc4 inc_n 4
|
||||
.define inc5 inc_n 5
|
||||
.define inc6 inc_n 6
|
||||
.define inc7 inc_n 7
|
||||
.define inc8 inc_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated decrement ]
|
||||
|
||||
.macro dec_n n
|
||||
.repeat n
|
||||
dec
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define dec2 dec_n 2
|
||||
.define dec3 dec_n 3
|
||||
.define dec4 dec_n 4
|
||||
.define dec5 dec_n 5
|
||||
.define dec6 dec_n 6
|
||||
.define dec7 dec_n 7
|
||||
.define dec8 dec_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated increment x ]
|
||||
|
||||
.macro inx_n n
|
||||
.repeat n
|
||||
inx
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define inx2 inx_n 2
|
||||
.define inx3 inx_n 3
|
||||
.define inx4 inx_n 4
|
||||
.define inx5 inx_n 5
|
||||
.define inx6 inx_n 6
|
||||
.define inx7 inx_n 7
|
||||
.define inx8 inx_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated decrement x ]
|
||||
|
||||
.macro dex_n n
|
||||
.repeat n
|
||||
dex
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define dex2 dex_n 2
|
||||
.define dex3 dex_n 3
|
||||
.define dex4 dex_n 4
|
||||
.define dex5 dex_n 5
|
||||
.define dex6 dex_n 6
|
||||
.define dex7 dex_n 7
|
||||
.define dex8 dex_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated increment y ]
|
||||
|
||||
.macro iny_n n
|
||||
.repeat n
|
||||
iny
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define iny2 iny_n 2
|
||||
.define iny3 iny_n 3
|
||||
.define iny4 iny_n 4
|
||||
.define iny5 iny_n 5
|
||||
.define iny6 iny_n 6
|
||||
.define iny7 iny_n 7
|
||||
.define iny8 iny_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ repeated decrement y ]
|
||||
|
||||
.macro dey_n n
|
||||
.repeat n
|
||||
dey
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
.define dey2 dey_n 2
|
||||
.define dey3 dey_n 3
|
||||
.define dey4 dey_n 4
|
||||
.define dey5 dey_n 5
|
||||
.define dey6 dey_n 6
|
||||
.define dey7 dey_n 7
|
||||
.define dey8 dey_n 8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ make pointer table (absolute) ]
|
||||
|
||||
.macro make_ptr_tbl_abs label, length
|
||||
.repeat length, i
|
||||
.addr .ident(.sprintf("%s_%04x", .string(label), i))
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ make pointer table (relative) ]
|
||||
|
||||
.macro make_ptr_tbl_rel label, length, offset
|
||||
.repeat length, i
|
||||
.ifblank offset
|
||||
.addr .ident(.sprintf("%s_%04x", .string(label), i)) - label
|
||||
.else
|
||||
.addr .ident(.sprintf("%s_%04x", .string(label), i)) - offset
|
||||
.endif
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ make pointer table (far) ]
|
||||
|
||||
.macro make_ptr_tbl_far label, length, offset
|
||||
.repeat length, i
|
||||
.ifblank offset
|
||||
.faraddr .ident(.sprintf("%s_%04x", .string(label), i)) - label
|
||||
.else
|
||||
.faraddr .ident(.sprintf("%s_%04x", .string(label), i)) - offset
|
||||
.endif
|
||||
.endrep
|
||||
.endmacro
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.list on
|
26
menu/Makefile
Normal file
26
menu/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = menu
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
808
menu/menu.asm
Normal file
808
menu/menu.asm
Normal file
@ -0,0 +1,808 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: menu.asm |
|
||||
; | |
|
||||
; | description: menu program |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.p816
|
||||
|
||||
.include "const.inc"
|
||||
.include "hardware.inc"
|
||||
.include "macros.inc"
|
||||
|
||||
.include "menu_data.asm"
|
||||
|
||||
.import ExecSound_ext
|
||||
|
||||
.export OpenMenu_ext, UpdateCtrlBattle_ext, InitCtrl_ext, UpdateCtrlField_ext
|
||||
.export IncGameTime_ext, LoadSavedGame_ext, EndingCutscene_ext
|
||||
.export OptimizeEquip_ext, EndingAirshipScene_ext
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "menu_code"
|
||||
.a8
|
||||
.i16
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
OpenMenu_ext:
|
||||
@0000: jmp OpenMenu
|
||||
|
||||
UpdateCtrlBattle_ext:
|
||||
@0003: jmp UpdateCtrlBattle
|
||||
|
||||
InitCtrl_ext:
|
||||
@0006: jmp InitCtrl
|
||||
|
||||
UpdateCtrlField_ext:
|
||||
@0009: jmp UpdateCtrlField
|
||||
|
||||
IncGameTime_ext:
|
||||
@000c: jmp IncGameTime
|
||||
|
||||
LoadSavedGame_ext:
|
||||
@000f: jmp LoadSavedGame
|
||||
|
||||
EndingCutscene_ext:
|
||||
@0012: jmp EndingCutscene
|
||||
|
||||
OptimizeEquip_ext:
|
||||
@0015: jmp OptimizeEquip
|
||||
|
||||
EndingAirshipScene_ext:
|
||||
@0018: jmp EndingAirshipScene
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ open menu ]
|
||||
|
||||
OpenMenu:
|
||||
@001b: longi
|
||||
shorta
|
||||
lda #$00 ; set data bank
|
||||
pha
|
||||
plb
|
||||
ldx #$0000 ; set direct page
|
||||
phx
|
||||
pld
|
||||
ldx #$0000 ; set $00
|
||||
stx $00
|
||||
lda #$7e
|
||||
sta $2183 ; set wram bank
|
||||
jsr InitInterrupts
|
||||
lda $0200
|
||||
cmp #$02
|
||||
bne @003f ; branch if not opening game load menu
|
||||
jsr InitSaveSlot
|
||||
@003f: jsr InitMenu
|
||||
jsr OpenMenuType
|
||||
jsl InitCtrl
|
||||
lda #$8f
|
||||
sta $2100
|
||||
stz $4200
|
||||
stz $420b
|
||||
stz $420c
|
||||
lda $0200
|
||||
cmp #$02
|
||||
bne @006f ; branch if not restoring a saved game
|
||||
lda $0205
|
||||
bpl @006f ; branch if tent/warp/warp stone was used
|
||||
lda $1d4e
|
||||
and #$20
|
||||
beq @006f ; branch if stereo mode
|
||||
; lda #$ff
|
||||
; jsr $3e3f ; set mono/stereo mode
|
||||
@006f: lda $0200
|
||||
; bne @00bf ; branch if not main menu
|
||||
; lda $0205
|
||||
; bpl @00bf ; return if return code is positive
|
||||
; cmp #$fe
|
||||
; bne @009e ; branch if not using rename card
|
||||
;
|
||||
; ; rename card
|
||||
; lda #$01
|
||||
; sta $0200
|
||||
; lda $0201
|
||||
; sta $020f
|
||||
; ldy $0206
|
||||
; sty $0201
|
||||
; jsl OpenMenu
|
||||
; lda $020f
|
||||
; sta $0201
|
||||
; stz $0200
|
||||
; jmp @001b
|
||||
;
|
||||
; ; swdtech renaming (ff6j)
|
||||
; @009e: lda #$06
|
||||
; sta $0200
|
||||
; lda $0201
|
||||
; sta $020f
|
||||
; lda $0206
|
||||
; sta $0201
|
||||
; jsl OpenMenu
|
||||
; lda $020f
|
||||
; sta $0201
|
||||
; stz $0200
|
||||
; jmp @001b
|
||||
|
||||
; return from menu
|
||||
@00bf: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ set up interrupt jump code ]
|
||||
|
||||
InitInterrupts:
|
||||
@00c0: lda #$5c
|
||||
sta $1500
|
||||
sta $1504
|
||||
ldx #.loword(MenuNMI)
|
||||
stx $1501
|
||||
ldx #.loword(MenuIRQ)
|
||||
stx $1505
|
||||
lda #^MenuNMI
|
||||
sta $1503
|
||||
sta $1507
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ open menu (type) ]
|
||||
|
||||
OpenMenuType:
|
||||
@00dd: tdc
|
||||
lda $0200
|
||||
asl
|
||||
tax
|
||||
jmp (.loword(MenuTypeTbl),x)
|
||||
|
||||
; menu type jump table
|
||||
MenuTypeTbl:
|
||||
.addr MainMenu
|
||||
.addr NameChangeMenu
|
||||
.addr GameLoadMenu
|
||||
.addr ShopMenu
|
||||
.addr PartySelectMenu
|
||||
.addr ItemDetailsMenu
|
||||
.addr BushidoNameMenu
|
||||
.addr ColosseumMenu
|
||||
.addr FinalBattleOrderMenu
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ init menu ]
|
||||
|
||||
InitMenu:
|
||||
@00f8: ; jsr $68fa ; init menu ram
|
||||
; jsr $6915 ; init party character data
|
||||
; jsr $69a9 ;
|
||||
; jsl $d4cd48 ; init menu hardware registers
|
||||
jsl InitCtrl
|
||||
tdc
|
||||
; jsl $d4ca1d ; init hdma for menu window gradient bars
|
||||
; jsr $69dc ; init window 1 position hdma
|
||||
; jsr $1114 ; init threads
|
||||
; jsr $6a87 ; init menu graphics
|
||||
; jmp @3a87 ; init custom menu window
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $00: main menu ]
|
||||
|
||||
MainMenu:
|
||||
@011a: lda #$04 ; menu state $04 (main menu init)
|
||||
sta $26
|
||||
jmp MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $03: shop ]
|
||||
|
||||
ShopMenu:
|
||||
@0121: ; jsr $3f99 ; init font color
|
||||
lda #$24 ; menu state $24 (shop init)
|
||||
sta $26
|
||||
jmp MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $04: party select ]
|
||||
|
||||
PartySelectMenu:
|
||||
@012b: ; jsr $3f99 ; init font color
|
||||
; jsr $0138 ; clear previous cursor position
|
||||
lda #$2c ; menu state $2c (party select init)
|
||||
sta $26
|
||||
jmp MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ clear previous cursor position ]
|
||||
|
||||
@0138: tdc
|
||||
tay
|
||||
sty $8e
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $05: item details ??? (unused) ]
|
||||
|
||||
ItemDetailsMenu:
|
||||
@013d: ; jsr $3f99 ; init font color
|
||||
; jsr $0138 ; clear previous cursor position
|
||||
lda #$2f ; menu state $2f (item details)
|
||||
sta $26
|
||||
bra MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $06: swdtech renaming (ff6j) ]
|
||||
|
||||
BushidoNameMenu:
|
||||
@0149: ; jsr $3f99 ; init font color
|
||||
lda #$3f ; menu state $3f
|
||||
sta $26
|
||||
bra MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $07: colosseum ]
|
||||
|
||||
ColosseumMenu:
|
||||
@0152: ; jsr $3f99 ; init font color
|
||||
stz $79
|
||||
stz $7a
|
||||
stz $7b
|
||||
lda #$ff
|
||||
sta $0205
|
||||
lda #$71 ; menu state $71 (colosseum item select init)
|
||||
sta $26
|
||||
bra MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $08: final battle order ]
|
||||
|
||||
FinalBattleOrderMenu:
|
||||
; @0166: jsr $3f99 ; init font color
|
||||
lda #$73 ; menu state $73 (final battle order init)
|
||||
sta $26
|
||||
bra MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $02: restore game ]
|
||||
|
||||
GameLoadMenu:
|
||||
@016f: lda $307ff1 ; increment random number seed
|
||||
inc
|
||||
sta $307ff1
|
||||
jsl InitCtrl
|
||||
jsr CheckSRAM
|
||||
; bcc @019f ; branch if sram is invalid
|
||||
; lda #$01 ; song $01 (the prelude)
|
||||
; sta $1301
|
||||
; lda #$10
|
||||
; sta $1300
|
||||
; lda #$80
|
||||
; sta $1302
|
||||
; jsl ExecSound_ext
|
||||
; lda #$ff
|
||||
; sta $0205
|
||||
; lda #$20 ; menu state $20 (restore game init)
|
||||
; sta $26
|
||||
; bra MenuLoop
|
||||
|
||||
; sram invalid
|
||||
; @019f: jsr $2a21 ; clear game time
|
||||
lda #1
|
||||
sta $0224 ; current saved game slot = 1
|
||||
stz $021f ; don't load a saved game
|
||||
lda #$ff
|
||||
sta $26 ; terminate menu
|
||||
stz $0205 ; clear return code
|
||||
bra MenuLoop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu type $01: name change ]
|
||||
|
||||
NameChangeMenu:
|
||||
@01b3: ; jsr $3f99 ; init font color
|
||||
lda #$5d ; menu state $5d (name change init)
|
||||
sta $26
|
||||
; fall through
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu state loop ]
|
||||
|
||||
MenuLoop:
|
||||
; @01ba: jsr $1412 ; update hardware registers
|
||||
@01bd: tdc
|
||||
; lda $26 ; return if menu state is $ff
|
||||
; cmp #$ff
|
||||
; beq @01d8
|
||||
; longa
|
||||
; asl
|
||||
; tax
|
||||
; shorta
|
||||
; jsr ($01db,x) ; do menu state code
|
||||
; jsr $11b0 ; execute threads
|
||||
; jsr $134d ; wait for next frame
|
||||
; jsr $02db ; check timer 0
|
||||
; bra @01bd
|
||||
@01d8: stz $43 ; disable hdma
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu nmi ]
|
||||
|
||||
MenuNMI:
|
||||
@1387: rti
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ menu irq ]
|
||||
|
||||
MenuIRQ:
|
||||
@13c7: rti
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ increment game time ]
|
||||
|
||||
IncGameTime:
|
||||
@13c8: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
LoadSavedGame:
|
||||
@14fe: lda $021f ; branch if not loading a saved game
|
||||
; beq @1514
|
||||
; jsr $1566 ; load saved game data
|
||||
; jsr $19d1 ; calculate saved game data checksum
|
||||
; jsr $19eb ; check saved game data checksum
|
||||
; beq @1514 ; branch if checksum invalid
|
||||
; jsr $1595 ; restore timer data
|
||||
; tdc ; return $00
|
||||
; bra @1518
|
||||
@1514: shorta
|
||||
lda #$ff ; return $ff
|
||||
@1518: sta $0205
|
||||
tdc
|
||||
rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ load window palette ]
|
||||
|
||||
LoadWindowPal:
|
||||
@6bbc: ldx #8
|
||||
stx $e7
|
||||
ldx #0
|
||||
txy
|
||||
longa
|
||||
@6bc7: lda #7
|
||||
sta $e9
|
||||
@6bcc: lda f:WindowPal+2,x ; load wallpaper palettes
|
||||
sta $1d57,y
|
||||
inx2
|
||||
iny
|
||||
iny
|
||||
dec $e9
|
||||
bne @6bcc
|
||||
txa
|
||||
clc
|
||||
adc #$0012
|
||||
tax
|
||||
dec $e7
|
||||
bne @6bc7
|
||||
shorta
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ check if sram is valid ]
|
||||
|
||||
CheckSRAM:
|
||||
@7023: longa
|
||||
lda #$e41b ; fixed value
|
||||
cmp $307ff8
|
||||
beq @7047
|
||||
cmp $307ffa
|
||||
beq @7047
|
||||
cmp $307ffc
|
||||
beq @7047
|
||||
cmp $307ffe
|
||||
beq @7047
|
||||
shorta
|
||||
jsr ClearSRAM
|
||||
clc
|
||||
rts
|
||||
@7047: shorta
|
||||
sec
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ clear sram ]
|
||||
|
||||
ClearSRAM:
|
||||
@704b: phb
|
||||
lda #$30
|
||||
pha
|
||||
plb
|
||||
ldx #0
|
||||
longa
|
||||
@7055: stz $6000,x ; clear 30/6000-30/7fff
|
||||
inx
|
||||
inx
|
||||
stz $6000,x
|
||||
inx2
|
||||
cpx #$2000
|
||||
bne @7055
|
||||
shorta
|
||||
plb
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ init saved menu cursor positions ]
|
||||
|
||||
_c37068:
|
||||
@7068: ldx #0
|
||||
@706b: stz $022b,x ; clear saved menu cursor positions
|
||||
inx
|
||||
cpx #$001f
|
||||
bne @706b
|
||||
lda #$01 ; character skills cursors default to magic
|
||||
sta $0237
|
||||
sta $0239
|
||||
sta $023b
|
||||
sta $023d
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ make sram valid ]
|
||||
|
||||
ValidateSRAM:
|
||||
@7083: longa
|
||||
lda #$e41b ; set fixed value
|
||||
sta $307ff8
|
||||
sta $307ffa
|
||||
sta $307ffc
|
||||
sta $307ffe
|
||||
shorta
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ init saved game data ]
|
||||
|
||||
InitSaveSlot:
|
||||
@709b: jsr _c37068
|
||||
ldy #$7fff ; set default font color
|
||||
sty $1d55
|
||||
lda #$12 ; set default button mappings
|
||||
sta $1d50
|
||||
lda #$34
|
||||
sta $1d51
|
||||
lda #$56
|
||||
sta $1d52
|
||||
lda #$06
|
||||
sta $1d53
|
||||
lda #$2a ;
|
||||
sta $1d4d
|
||||
tdc
|
||||
tay
|
||||
sty $1dc7 ;
|
||||
stz $1d54
|
||||
stz $1d4e
|
||||
stz $1d4f
|
||||
sty $1863 ; clear game time
|
||||
stz $1865
|
||||
sty $1860 ; clear gp
|
||||
stz $1862
|
||||
sty $1866 ; clear steps
|
||||
stz $1868
|
||||
sty $021b ; clear menu game time
|
||||
sty $021d
|
||||
jsr LoadWindowPal
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
OptimizeEquip:
|
||||
@96d2: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ init controller ]
|
||||
|
||||
InitCtrl:
|
||||
@a424: lda #$08 ; set direction button repeat delay (8 frames)
|
||||
sta $0229
|
||||
lda #$03 ; set button repeat rate (3 frames/repeat)
|
||||
sta $022a
|
||||
sta $0226
|
||||
lda #$20 ; set a button repeat delay (32 frames)
|
||||
sta $0225
|
||||
lda $1d54 ; branch if no special button configuration
|
||||
and #$40
|
||||
beq @a441
|
||||
jsr SetCustomBtnMap
|
||||
rtl
|
||||
@a441: jsr SetDefaultBtnMap
|
||||
rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ update controller (battle) ]
|
||||
|
||||
UpdateCtrlBattle:
|
||||
@a445: lda $1d54
|
||||
bpl @a458
|
||||
tdc
|
||||
lda $0201
|
||||
tax
|
||||
lda $1d4f
|
||||
and f:_c3a53d,x
|
||||
bne @a45d
|
||||
@a458: ldx hSTDCNTRL1L
|
||||
bra @a462
|
||||
@a45d: ldx hSTDCNTRL2L
|
||||
bra @a462
|
||||
@a462: jsr _c3a483
|
||||
jsr _c3a4bd
|
||||
jsr _c3a4f6
|
||||
jmp _c3a527
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ update controller (menu) ]
|
||||
|
||||
UpdateCtrlMenu:
|
||||
@a46e: ldx hSTDCNTRL1L
|
||||
jsr _c3a483
|
||||
jsr _c3a4bd
|
||||
jmp _c3a527
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ update controller (field/world) ]
|
||||
|
||||
UpdateCtrlField:
|
||||
@a47a: ldx hSTDCNTRL1L
|
||||
jsr _c3a483
|
||||
jmp _c3a527
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ ]
|
||||
|
||||
joy_getsub:
|
||||
_c3a483:
|
||||
@a483: ldy $e0 ; push dp variables
|
||||
sty $0213
|
||||
ldy $e7
|
||||
sty $0215
|
||||
ldy $e9
|
||||
sty $0217
|
||||
ldy $eb
|
||||
sty $0219
|
||||
stx $eb
|
||||
longa
|
||||
lda $0c
|
||||
and #$fff0
|
||||
sta $e0
|
||||
jsr _c3a541
|
||||
lda $0c
|
||||
eor #$ffff
|
||||
and $06
|
||||
sta $08
|
||||
ldy $06
|
||||
sty $0c
|
||||
lda hSTDCNTRL1L
|
||||
ora hSTDCNTRL2L
|
||||
sta $04
|
||||
shorta
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ ]
|
||||
|
||||
_c3a4bd:
|
||||
@a4bd: longa
|
||||
lda $06
|
||||
and #$fff0
|
||||
cmp $e0
|
||||
shorta
|
||||
bne @a4e5
|
||||
lda $0227
|
||||
beq @a4d4
|
||||
dec $0227
|
||||
bne @a4f1
|
||||
@a4d4: dec $0228
|
||||
bne @a4f1
|
||||
lda $022a
|
||||
sta $0228
|
||||
ldy $06
|
||||
sty $0a
|
||||
bra @a4f5
|
||||
@a4e5: lda $0229
|
||||
sta $0227
|
||||
lda $022a
|
||||
sta $0228
|
||||
@a4f1: ldy $08
|
||||
sty $0a
|
||||
@a4f5: rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ ]
|
||||
|
||||
_c3a4f6:
|
||||
@a4f6: lda $06
|
||||
bit #$80
|
||||
beq @a517
|
||||
lda $0225
|
||||
beq @a506
|
||||
dec $0225
|
||||
bne @a522
|
||||
@a506: dec $0226
|
||||
bne @a522
|
||||
lda $022a
|
||||
sta $0226
|
||||
lda #$80
|
||||
tsb $0a
|
||||
bra @a526
|
||||
@a517: lda #$20
|
||||
sta $0225
|
||||
lda $022a
|
||||
sta $0226
|
||||
@a522: lda $08
|
||||
sta $0a
|
||||
@a526: rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ pop dp variables used for joypad ]
|
||||
|
||||
joy_sub3:
|
||||
_c3a527:
|
||||
@a527: ldy $0213
|
||||
sty $e0
|
||||
ldy $0215
|
||||
sty $e7
|
||||
ldy $0217
|
||||
sty $e9
|
||||
ldy $0219
|
||||
sty $eb
|
||||
tdc
|
||||
rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
_c3a53d:
|
||||
@a53d: .byte $01,$02,$04,$08
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ ]
|
||||
|
||||
.a16
|
||||
|
||||
_c3a541:
|
||||
@a541: lda $eb
|
||||
and #$0f00
|
||||
sta $06
|
||||
lda #$0080
|
||||
sta $e7
|
||||
lda #$8000
|
||||
sta $e9
|
||||
ldy $00
|
||||
jsr _c3a581
|
||||
lda #$0040
|
||||
sta $e7
|
||||
lda #$4000
|
||||
sta $e9
|
||||
iny
|
||||
jsr _c3a581
|
||||
lda #$0020
|
||||
sta $e7
|
||||
lda #$0010
|
||||
sta $e9
|
||||
iny
|
||||
jsr _c3a581
|
||||
lda #$1000
|
||||
sta $e7
|
||||
lda #$2000
|
||||
sta $e9
|
||||
iny
|
||||
jmp _c3a581
|
||||
|
||||
.a8
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ ]
|
||||
|
||||
_c3a581:
|
||||
@a581: lda $eb
|
||||
bit $e7
|
||||
beq @a59b
|
||||
tdc
|
||||
shorta
|
||||
lda $0220,y
|
||||
and #$f0
|
||||
longa
|
||||
lsr3
|
||||
tax
|
||||
lda f:_c3a5b4,x
|
||||
tsb $06
|
||||
@a59b: lda $eb
|
||||
bit $e9
|
||||
beq @a5b3
|
||||
tdc
|
||||
shorta
|
||||
lda $0220,y
|
||||
and #$0f
|
||||
longa
|
||||
asl
|
||||
tax
|
||||
lda _c3a5b4,x
|
||||
tsb $06
|
||||
@a5b3: rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
_c3a5b4:
|
||||
@a5b4: .word $1000,$0080,$8000,$0040,$4000,$0020,$0010,$2000
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ set custom button mapping ]
|
||||
|
||||
SetCustomBtnMap:
|
||||
@a5c4: ldy $1d50
|
||||
sty $0220
|
||||
ldy $1d52
|
||||
sty $0222
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; [ set default button mapping ]
|
||||
|
||||
SetDefaultBtnMap:
|
||||
@a5d1: ldy #$3412
|
||||
sty $0220
|
||||
ldy #$0656
|
||||
sty $0222
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
EndingCutscene:
|
||||
@c51c: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
EndingAirshipScene:
|
||||
@c51c: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
26
menu/menu_data.asm
Normal file
26
menu/menu_data.asm
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: menu_data.asm |
|
||||
; | |
|
||||
; | description: data for menu module |
|
||||
; | |
|
||||
; | created: 8/12/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.export WindowGfx, WindowPal
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "window_gfx"
|
||||
|
||||
; ed/0000
|
||||
.include "gfx/window_gfx.asm"
|
||||
|
||||
; ed/1c00
|
||||
.include "gfx/window_pal.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
File diff suppressed because it is too large
Load Diff
@ -303,7 +303,7 @@ EF9D17-EFB630 Serpent Trench Map Formation (compressed)
|
||||
EFB631-EFC623 Serpent Trench Graphics (compressed)
|
||||
EFC624-EFCE76 Some Chocobo Graphics (compressed)
|
||||
EFCE77-EFCE96 Vector Approach Palette
|
||||
EFCE97-EFCEB6 Data for Esper Terra
|
||||
EFCE97-EFCEB6 Esper Terra Palette
|
||||
EFCEB7-EFCFB8 Airship Shadow and Perspective Graphics (compressed)
|
||||
EFCFB9-EFDC4B Various Sprites (ship, esper terra, figaro, etc., compressed)
|
||||
EFDC4C-EFE49A More Chocobo Graphics (world map, compressed)
|
@ -22,34 +22,34 @@ $0000-$00FF direct page
|
||||
l: Left direction down
|
||||
r: Right direction down
|
||||
|
||||
$19
|
||||
$19
|
||||
|
||||
+$1E
|
||||
$20
|
||||
+$1E
|
||||
$20
|
||||
|
||||
$22 target screen brightness
|
||||
$23 current screen brightness (0-F)
|
||||
$24 vblank flag
|
||||
|
||||
|
||||
+$26 forward movement speed
|
||||
|
||||
|
||||
++$29 rotation speed
|
||||
|
||||
|
||||
+$2F altitude
|
||||
$31
|
||||
$31
|
||||
|
||||
+$34 current X position (in pixels)
|
||||
|
||||
|
||||
+$38 current Y position (in pixels)
|
||||
++$3A rotation angle (high 16-bits active)
|
||||
+$3D m7a
|
||||
+$3F m7b
|
||||
+$41 m7c
|
||||
+$43 m7d
|
||||
|
||||
+$44
|
||||
+$46
|
||||
|
||||
|
||||
+$44
|
||||
+$46
|
||||
|
||||
$58-$64 scratchpad
|
||||
|
||||
$60 fdd?????
|
||||
@ -60,7 +60,7 @@ $58-$64 scratchpad
|
||||
u: vehicle is moving up
|
||||
l: vehicle is turning left
|
||||
r: vehicle is turning right
|
||||
|
||||
|
||||
+$6A scratchpad
|
||||
|
||||
+$73 rotation frame counter
|
||||
@ -69,14 +69,14 @@ $58-$64 scratchpad
|
||||
+$79 bg1 vertical scroll
|
||||
+$7B map horizontal scroll position, or maybe an offset based on the map sector ???
|
||||
+$7D map vertical scroll position ???
|
||||
|
||||
+$83
|
||||
+$85
|
||||
+$87
|
||||
+$89
|
||||
$8B
|
||||
|
||||
+$83
|
||||
+$85
|
||||
+$87
|
||||
+$89
|
||||
$8B
|
||||
$8C zoom level ??
|
||||
|
||||
|
||||
$9F HDMA #4 (M7A) line count 1 (124 lines, no repeat)
|
||||
+$A0 HDMA #4 (M7A) data pointer 1
|
||||
$A2 HDMA #4 (M7A) line count 2 (100 lines, no repeat)
|
||||
@ -103,14 +103,23 @@ $58-$64 scratchpad
|
||||
+$C4 current tile index
|
||||
+$C6 current X position (in pixels)
|
||||
+$C8 current Y position (in pixels)
|
||||
$CA character 1 graphic (00 = none, 01 = falcon, 02 = chocobo ???, 03 = character, 04 = ???, 05 = ship, 0C = esper terra, 12 = bird, 16 = smoking airship)
|
||||
$CA character 1 graphic
|
||||
00: none
|
||||
01: falcon
|
||||
02: chocobo ???
|
||||
03: character
|
||||
04: ???
|
||||
05: ship
|
||||
0C: esper terra
|
||||
12: bird
|
||||
16: smoking airship
|
||||
$CB character 2 graphic
|
||||
$CC character 3 graphic
|
||||
$CD character 4 graphic
|
||||
|
||||
|
||||
++$D2 decompression source address
|
||||
++$D5 decompression destination address
|
||||
|
||||
|
||||
+$DF X position (in pixels * 256)
|
||||
+$E1 Y position (in pixels * 256)
|
||||
+$E3 X movement speed (signed)
|
||||
@ -131,30 +140,30 @@ $58-$64 scratchpad
|
||||
$EF movement distance/pause counter
|
||||
$F0 current event command
|
||||
$F1 frame counter for pause event command
|
||||
$F2
|
||||
$F2
|
||||
$F3 character movement speed
|
||||
+$F4 destination map index ???
|
||||
$F6 facing direction (0 = up, 1 = right, 2 = down, 3 = left)
|
||||
$F7 graphical action
|
||||
|
||||
|
||||
$FA 4 frame counter (magitek train ride)
|
||||
+$FA origin X
|
||||
+$FC origin Y
|
||||
|
||||
|
||||
$0140-$09FF mode 7 HDMA data
|
||||
$0A00-$0AFF dp stack
|
||||
$0B00-$0B0F
|
||||
$0B00-$0B0F
|
||||
|
||||
$11F0
|
||||
|
||||
$11F2
|
||||
$11F3
|
||||
+$11F4
|
||||
$11F0
|
||||
|
||||
$11F2
|
||||
$11F3
|
||||
+$11F4
|
||||
+$11F6 ???????? ?????gbm
|
||||
g: map graphics are already loaded
|
||||
b: enable battle
|
||||
m: hide mini-map
|
||||
$11F8
|
||||
$11F8
|
||||
$11F9 battle bg index
|
||||
$11FA vehicle index (0 = none, 1 = airship, 2 = chocobo)
|
||||
$11FB showing character's graphic index
|
||||
@ -179,7 +188,7 @@ $1F81-$1FA0 saved object map indexes
|
||||
$1FA2 random number pointer for monster formation
|
||||
$1FA3 random number counter for monster formation
|
||||
$1FA4 random number counter for random monster battle
|
||||
$1FA5
|
||||
$1FA5
|
||||
+$1FA6 pointer to current showing character's object data
|
||||
$1FA8-$1FBF saved counter data
|
||||
+$1FC0 party XY position
|
||||
@ -201,10 +210,10 @@ $9350-$93DF map tile palette assignments
|
||||
|
||||
$B5D0-$B64F
|
||||
$B650 walking animation position (0-3)
|
||||
|
||||
|
||||
$B750-$B84F water tile graphics
|
||||
$B850-$B85F water tile movement counters (1 per line)
|
||||
$B860
|
||||
$B860
|
||||
|
||||
$E000-$E200 color palettes
|
||||
|
116
package-lock.json
generated
Normal file
116
package-lock.json
generated
Normal file
@ -0,0 +1,116 @@
|
||||
{
|
||||
"name": "ff6",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"crc-32": "^1.2.2",
|
||||
"is-number": "^7.0.0",
|
||||
"is-string": "^1.0.7",
|
||||
"isarray": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-string": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
|
||||
"integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
|
||||
"dependencies": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||
},
|
||||
"has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"requires": {
|
||||
"has-symbols": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"is-string": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
|
||||
"integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
|
||||
"requires": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
|
||||
}
|
||||
}
|
||||
}
|
8
package.json
Normal file
8
package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"crc-32": "^1.2.2",
|
||||
"is-number": "^7.0.0",
|
||||
"is-string": "^1.0.7",
|
||||
"isarray": "^2.0.5"
|
||||
}
|
||||
}
|
26
sound/Makefile
Normal file
26
sound/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = sound
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
66
sound/sound.asm
Normal file
66
sound/sound.asm
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: sound.asm |
|
||||
; | |
|
||||
; | description: sound program |
|
||||
; | |
|
||||
; | created: 8/2/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.p816
|
||||
|
||||
.include "const.inc"
|
||||
.include "hardware.inc"
|
||||
.include "macros.inc"
|
||||
|
||||
.export InitSound_ext, ExecSound_ext, TfrSong_ext, TfrSamples_ext
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "sound_code"
|
||||
.a8
|
||||
.i16
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
InitSound_ext:
|
||||
@0000: jmp InitSound
|
||||
nop
|
||||
|
||||
ExecSound_ext:
|
||||
@0004: jmp ExecSound
|
||||
nop
|
||||
|
||||
TfrSong_ext:
|
||||
@0008: jmp TfrSong
|
||||
nop
|
||||
|
||||
TfrSamples_ext:
|
||||
@000c: jmp TfrSamples
|
||||
nop
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
InitSound:
|
||||
@0034: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
ExecSound:
|
||||
@0131: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
TfrSong:
|
||||
@031c: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
TfrSamples:
|
||||
@0374: rtl
|
||||
|
||||
; ------------------------------------------------------------------------------
|
50
tools/calc-checksum.js
Normal file
50
tools/calc-checksum.js
Normal file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const hexString = require('./romtools/hex-string');
|
||||
|
||||
function calcSum(data) {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < data.length; i++) sum += data[i];
|
||||
return sum & 0xFFFF;
|
||||
}
|
||||
|
||||
function mirrorSum(data, mask) {
|
||||
mask = mask || 0x800000;
|
||||
while (!(data.length & mask)) mask >>= 1;
|
||||
|
||||
const part1 = calcSum(data.slice(0, mask));
|
||||
let part2 = 0;
|
||||
|
||||
let nextLength = data.length - mask;
|
||||
if (nextLength) {
|
||||
part2 = mirrorSum(data.slice(mask), nextLength, mask >> 1);
|
||||
|
||||
while (nextLength < mask) {
|
||||
nextLength += nextLength;
|
||||
part2 += part2;
|
||||
}
|
||||
}
|
||||
return (part1 + part2) & 0xFFFF;
|
||||
}
|
||||
|
||||
// load the ROM file
|
||||
const romPath = process.argv[2];
|
||||
const romBuffer = fs.readFileSync(romPath);
|
||||
const romData = new Uint8Array(romBuffer);
|
||||
|
||||
// calculate the SNES checksum
|
||||
const checksum = mirrorSum(romData);
|
||||
const checksumInverse = checksum ^ 0xFFFF;
|
||||
|
||||
console.log(`SNES Checksum: ${hexString(checksum, 4)}`);
|
||||
|
||||
// set the checksum in the SNES header and write to the ROM file
|
||||
romData[0xFFDC] = checksumInverse & 0xFF;
|
||||
romData[0xFFDD] = checksumInverse >> 8;
|
||||
romData[0xFFDE] = checksum & 0xFF;
|
||||
romData[0xFFDF] = checksum >> 8;
|
||||
|
||||
fs.writeFileSync(romPath, romData);
|
||||
|
||||
process.exit(0);
|
65
tools/decode-ff6.js
Normal file
65
tools/decode-ff6.js
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const CRC32 = require('crc-32');
|
||||
const ROMDataCodec = require('./romtools/data-codec');
|
||||
const ROMDecoder = require('./romtools/rom-decoder');
|
||||
const ROMRange = require('./romtools/range');
|
||||
const hexString = require('./romtools/hex-string');
|
||||
|
||||
const romInfoListFF4 = {
|
||||
0x45EF5AC8: {
|
||||
name: 'Final Fantasy VI 1.0 (J)',
|
||||
ripPath: 'vanilla/ff6-jp-rip.json',
|
||||
dataPath: 'ff6-jp-data.json'
|
||||
},
|
||||
0xA27F1C7A: {
|
||||
name: 'Final Fantasy III 1.0 (U)',
|
||||
ripPath: 'vanilla/ff6-en-rip.json',
|
||||
dataPath: 'ff6-en-data.json'
|
||||
},
|
||||
0xC0FA0464: {
|
||||
name: 'Final Fantasy III 1.1 (U)',
|
||||
ripPath: 'vanilla/ff6-en-rip.json',
|
||||
dataPath: 'ff6-en-data.json'
|
||||
}
|
||||
}
|
||||
|
||||
// search the vanilla directory for valid ROM files
|
||||
const files = fs.readdirSync('vanilla');
|
||||
|
||||
let foundOneROM = false;
|
||||
for (let filename of files) {
|
||||
const filePath = `vanilla/${filename}`;
|
||||
if (fs.statSync(filePath).isDirectory()) continue;
|
||||
|
||||
const fileBuf = fs.readFileSync(filePath);
|
||||
const crc32 = CRC32.buf(fileBuf) >>> 0;
|
||||
const romInfo = romInfoListFF4[crc32];
|
||||
if (!romInfo) continue;
|
||||
|
||||
console.log(`Found ROM: ${romInfo.name}`);
|
||||
console.log(`File: ${filePath}`);
|
||||
|
||||
// load the definition file
|
||||
const ripDefinitionFile = fs.readFileSync(romInfo.ripPath);
|
||||
const ripDefinition = JSON.parse(ripDefinitionFile);
|
||||
|
||||
const decoder = new ROMDecoder(ripDefinition);
|
||||
|
||||
// load the ROM file
|
||||
const romData = new Uint8Array(fs.readFileSync(filePath));
|
||||
const romObj = decoder.decodeROM(romData, ripDefinition);
|
||||
romObj.crc32 = hexString(crc32, 8);
|
||||
|
||||
fs.writeFileSync(romInfo.dataPath, JSON.stringify(romObj, null, 2));
|
||||
foundOneROM = true;
|
||||
}
|
||||
|
||||
if (!foundOneROM) {
|
||||
console.log('No valid ROM files found!\nPlease copy your valid FF6 ROM ' +
|
||||
'file(s) into the "vanilla" directory.\nIf your ROM has a header, ' +
|
||||
'please remove it first.');
|
||||
}
|
||||
|
||||
process.exit(0);
|
18
tools/encode-ff6.js
Normal file
18
tools/encode-ff6.js
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const ROMEncoder = require('./romtools/rom-encoder');
|
||||
const ROMMemoryMap = require('./romtools/memory-map');
|
||||
const ROMRange = require('./romtools/range');
|
||||
const ROMDataCodec = require('./romtools/data-codec');
|
||||
|
||||
// load the data file
|
||||
const dataPath = process.argv[2];
|
||||
const romDefinitionFile = fs.readFileSync(dataPath);
|
||||
const romDefinition = JSON.parse(romDefinitionFile);
|
||||
|
||||
const encoder = new ROMEncoder(romDefinition);
|
||||
|
||||
encoder.encodeROM(romDefinition);
|
||||
|
||||
process.exit(0);
|
1321
tools/romtools/data-codec.js
Normal file
1321
tools/romtools/data-codec.js
Normal file
File diff suppressed because it is too large
Load Diff
36
tools/romtools/data-manager.js
Normal file
36
tools/romtools/data-manager.js
Normal file
@ -0,0 +1,36 @@
|
||||
// data-manager.js
|
||||
|
||||
const ROMTextCodec = require('./text-codec');
|
||||
const ROMMemoryMap = require('./memory-map');
|
||||
|
||||
class ROMDataManager {
|
||||
constructor(definition) {
|
||||
|
||||
this.definition = definition;
|
||||
|
||||
// create the memory mapper
|
||||
const mapMode = definition.mode || ROMMemoryMap.MapMode.none;
|
||||
this.memoryMap = new ROMMemoryMap(mapMode);
|
||||
|
||||
// create text codecs
|
||||
this.textCodec = {};
|
||||
for (let key in definition.textEncoding) {
|
||||
const encodingDef = definition.textEncoding[key];
|
||||
this.textCodec[key] = new ROMTextCodec(encodingDef, definition.charTable);
|
||||
}
|
||||
|
||||
// create string tables
|
||||
|
||||
}
|
||||
|
||||
getDefinition(key) {
|
||||
return this.definition.assembly[key];
|
||||
}
|
||||
|
||||
getObject(key) {
|
||||
return this.definition.obj[key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ROMDataManager;
|
1829
tools/romtools/gfx.js
Normal file
1829
tools/romtools/gfx.js
Normal file
File diff suppressed because it is too large
Load Diff
22
tools/romtools/hex-string.js
Normal file
22
tools/romtools/hex-string.js
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
const isNumber = require('is-number');
|
||||
|
||||
function hexString(num, pad, prefix) {
|
||||
if (prefix === undefined) prefix = '0x';
|
||||
if (num < 0) num = 0xFFFFFFFF + num + 1;
|
||||
let hex = num.toString(16).toUpperCase();
|
||||
if (isNumber(pad)) {
|
||||
hex = hex.padStart(pad, '0');
|
||||
} else if (num < 0x0100) {
|
||||
hex = hex.padStart(2, '0');
|
||||
} else if (num < 0x010000) {
|
||||
hex = hex.padStart(4, '0');
|
||||
} else if (num < 0x01000000) {
|
||||
hex = hex.padStart(6, '0');
|
||||
} else {
|
||||
hex = hex.padStart(8, '0');
|
||||
}
|
||||
return (prefix + hex);
|
||||
}
|
||||
|
||||
module.exports = hexString;
|
138
tools/romtools/memory-map.js
Normal file
138
tools/romtools/memory-map.js
Normal file
@ -0,0 +1,138 @@
|
||||
|
||||
const ROMRange = require('./range');
|
||||
|
||||
class ROMMemoryMap {
|
||||
constructor(mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
bankSize() {
|
||||
switch (this.mode) {
|
||||
case ROMMemoryMap.MapMode.mmc1: return 0x4000;
|
||||
case ROMMemoryMap.MapMode.mmc3: return 0x2000;
|
||||
case ROMMemoryMap.MapMode.loROM: return 0x8000;
|
||||
case ROMMemoryMap.MapMode.hiROM: return 0x10000;
|
||||
case ROMMemoryMap.MapMode.x16: return 0x2000;
|
||||
case ROMMemoryMap.MapMode.wsc: return 0x10000;
|
||||
default: return 0x10000;
|
||||
}
|
||||
}
|
||||
|
||||
// convert CPU address to ROM file address
|
||||
mapAddress(address) {
|
||||
switch (this.mode) {
|
||||
case ROMMemoryMap.MapMode.mmc1:
|
||||
var bank = address & 0xFF0000;
|
||||
return (bank >> 2) + (address & 0x3FFF) + 0x10;
|
||||
|
||||
case ROMMemoryMap.MapMode.mmc3:
|
||||
var bank = address & 0xFF0000;
|
||||
return (bank >> 3) + (address & 0x1FFF) + 0x10;
|
||||
|
||||
case ROMMemoryMap.MapMode.loROM:
|
||||
var bank = address & 0xFF0000;
|
||||
return (bank >> 1) + (address & 0x7FFF);
|
||||
|
||||
case ROMMemoryMap.MapMode.hiROM:
|
||||
if (address >= 0xC00000) {
|
||||
return address - 0xC00000;
|
||||
} else if (address >= 0x800000) {
|
||||
return address - 0x800000;
|
||||
} else {
|
||||
return address;
|
||||
}
|
||||
|
||||
case ROMMemoryMap.MapMode.gba:
|
||||
if (address >= 0x08000000) {
|
||||
return address - 0x08000000;
|
||||
} else {
|
||||
return address;
|
||||
}
|
||||
|
||||
case ROMMemoryMap.MapMode.x16:
|
||||
var bank = address & 0xFF0000;
|
||||
bank -= 0x010000;
|
||||
return (bank >> 3) + (address & 0x1FFF) + 2;
|
||||
|
||||
case ROMMemoryMap.MapMode.wsc:
|
||||
if (address < 0x10000000) return address; // raw address
|
||||
var bank = (address >> 12) & 0xFF0000;
|
||||
return bank + (address & 0xFFFF);
|
||||
|
||||
case ROMMemoryMap.MapMode.None:
|
||||
default:
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
// convert ROM file address to CPU address
|
||||
unmapAddress(address) {
|
||||
switch (this.mode) {
|
||||
case ROMMemoryMap.MapMode.mmc1:
|
||||
address -= 0x10; // iNES header
|
||||
var bank = (address << 2) & 0xFF0000;
|
||||
return bank | (address & 0x3FFF) | 0x8000;
|
||||
|
||||
case ROMMemoryMap.MapMode.mmc3:
|
||||
address -= 0x10; // iNES header
|
||||
var bank = (address << 3) & 0xFF0000;
|
||||
if (bank & 0x010000) {
|
||||
return bank | (address & 0x1FFF) | 0xA000;
|
||||
} else {
|
||||
return bank | (address & 0x1FFF) | 0x8000;
|
||||
}
|
||||
|
||||
case ROMMemoryMap.MapMode.loROM:
|
||||
var bank = (address << 1) & 0xFF0000;
|
||||
return bank | (address & 0x7FFF) | 0x8000;
|
||||
|
||||
case ROMMemoryMap.MapMode.hiROM:
|
||||
return address + 0xC00000;
|
||||
|
||||
case ROMMemoryMap.MapMode.gba:
|
||||
return address | 0x08000000;
|
||||
|
||||
case ROMMemoryMap.MapMode.x16:
|
||||
address -= 2; // header
|
||||
var bank = (address << 3) & 0xFF0000;
|
||||
bank += 0x010000;
|
||||
return bank | (address & 0x1FFF) | 0xA000;
|
||||
|
||||
case ROMMemoryMap.MapMode.wsc:
|
||||
if (address > 0x0F0000) return address; // raw address
|
||||
var bank = (address & 0x0F0000) << 12;
|
||||
return bank | (address & 0xFFFF);
|
||||
|
||||
case ROMMemoryMap.MapMode.None:
|
||||
default:
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
// convert CPU address (exclusive) to ROM file address (inclusive)
|
||||
mapRange(range) {
|
||||
const begin = this.mapAddress(range.begin);
|
||||
const end = this.mapAddress(range.end);
|
||||
return new ROMRange(begin, end);
|
||||
}
|
||||
|
||||
unmapRange(range) {
|
||||
const begin = this.unmapAddress(range.begin);
|
||||
const end = this.unmapAddress(range.end);
|
||||
return new ROMRange(begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
ROMMemoryMap.MapMode = {
|
||||
none: "none",
|
||||
mmc1: "mmc1",
|
||||
mmc3: "mmc3",
|
||||
loROM: "loROM",
|
||||
hiROM: "hiROM",
|
||||
gba: "gba",
|
||||
x16: "x16",
|
||||
psx: "psx",
|
||||
wsc: "wsc"
|
||||
}
|
||||
|
||||
module.exports = ROMMemoryMap;
|
115
tools/romtools/range.js
Normal file
115
tools/romtools/range.js
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
const hexString = require('./hex-string');
|
||||
const isNumber = require('is-number');
|
||||
const isString = require('is-string');
|
||||
|
||||
class ROMRange {
|
||||
|
||||
constructor(begin, end) {
|
||||
|
||||
// beginning of range (must be positive)
|
||||
this.begin = 0;
|
||||
// end of range (included in range)
|
||||
this.end = -1;
|
||||
|
||||
if (isNumber(begin)) {
|
||||
// assume single value for now
|
||||
this.begin = begin;
|
||||
this.end = begin;
|
||||
} else if (isString(begin)) {
|
||||
const bounds = begin.split('-');
|
||||
if (bounds.length == 1) {
|
||||
// single value
|
||||
this.begin = Number(begin);
|
||||
if (!isNumber(this.begin)) {
|
||||
console.log(`Invalid range: ${begin}`);
|
||||
this.begin = 0;
|
||||
}
|
||||
this.end = this.begin;
|
||||
} else if (bounds.length == 2) {
|
||||
// multiple values
|
||||
this.begin = Number(bounds[0]);
|
||||
this.end = Number(bounds[1]);
|
||||
if (!isNumber(this.begin) || !isNumber(this.end)) {
|
||||
console.log(`Invalid range: ${begin}`);
|
||||
this.begin = 0;
|
||||
this.end = -1;
|
||||
}
|
||||
} else {
|
||||
console.log(`Invalid range: ${begin}`);
|
||||
}
|
||||
}
|
||||
|
||||
// specified end value supercedes
|
||||
if (isNumber(end)) {
|
||||
this.end = end;
|
||||
} else if (isString(end)) {
|
||||
this.end = Number(end);
|
||||
if (!isNumber(this.end)) {
|
||||
console.log(`Invalid range: ${end}`);
|
||||
this.end = this.begin - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// validate range
|
||||
// if (this.begin < 0) {
|
||||
// console.log(`Invalid range begin: ${this.begin}`);
|
||||
// this.begin = 0;
|
||||
// }
|
||||
// if (this.end < this.begin) {
|
||||
// console.log(`Invalid range: ${this.begin}-${this.end}`);
|
||||
// this.end = this.begin;
|
||||
// }
|
||||
}
|
||||
|
||||
toString(pad) {
|
||||
|
||||
if (pad) {
|
||||
pad = Number(pad);
|
||||
} else if (this.end < 0x0100) {
|
||||
return `${this.begin}-${this.end}`;
|
||||
} else if (this.end < 0x010000) {
|
||||
pad = 4;
|
||||
} else if (this.end < 0x01000000) {
|
||||
pad = 6;
|
||||
} else {
|
||||
pad = 8;
|
||||
}
|
||||
return `${hexString(this.begin, pad)}-${hexString(this.end, pad)}`;
|
||||
}
|
||||
|
||||
contains(i) {
|
||||
// true if this range includes i
|
||||
return (i >= this.begin && i <= this.end);
|
||||
}
|
||||
|
||||
offset(offset) {
|
||||
// return a new range offset from this range
|
||||
return new ROMRange(this.begin + offset, this.end + offset);
|
||||
}
|
||||
|
||||
intersection(range) {
|
||||
// return a new range that includes the intersection of this range
|
||||
// with another range (can be empty)
|
||||
const begin = Math.max(range.begin, this.begin);
|
||||
const end = Math.min(range.end, this.end);
|
||||
return new ROMRange(begin, end);
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
// true if the array is empty
|
||||
return (this.end < this.begin);
|
||||
}
|
||||
|
||||
get length() {
|
||||
// return the length of the range
|
||||
return (this.end - this.begin + 1);
|
||||
}
|
||||
|
||||
set length(newLength) {
|
||||
// set the length of the range by changing end
|
||||
this.end = this.begin + newLength - 1;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ROMRange;
|
369
tools/romtools/rom-decoder.js
Normal file
369
tools/romtools/rom-decoder.js
Normal file
@ -0,0 +1,369 @@
|
||||
// rom-decoder.js
|
||||
|
||||
const fs = require('fs');
|
||||
const getDirName = require('path').dirname;
|
||||
const isString = require('is-string');
|
||||
const ROMDataCodec = require('./data-codec');
|
||||
const ROMMemoryMap = require('./memory-map');
|
||||
const ROMRange = require('./range');
|
||||
const ROMTextCodec = require('./text-codec');
|
||||
const hexString = require('./hex-string');
|
||||
|
||||
class ROMDecoder {
|
||||
|
||||
constructor(definition) {
|
||||
|
||||
// create a memory mapper
|
||||
const mapMode = definition.mode || ROMMemoryMap.MapMode.none;
|
||||
this.memoryMap = new ROMMemoryMap(mapMode);
|
||||
|
||||
// create text codecs
|
||||
this.textCodec = {};
|
||||
for (let key in definition.textEncoding) {
|
||||
const encodingDef = definition.textEncoding[key];
|
||||
this.textCodec[key] = new ROMTextCodec(encodingDef, definition.charTable);
|
||||
}
|
||||
|
||||
this.currentIndex = 0;
|
||||
this.indexStack = [];
|
||||
}
|
||||
|
||||
decodeROM(data, definition) {
|
||||
// set the ROM file
|
||||
this.romData = data;
|
||||
|
||||
definition.obj = {};
|
||||
for (let key in definition.assembly) {
|
||||
const objDefinition = definition.assembly[key];
|
||||
if (!objDefinition.file) continue;
|
||||
definition.obj[key] = this.decodeParentObject(objDefinition);
|
||||
definition.assembly[key].isDirty = false;
|
||||
}
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
decodeParentObject(definition) {
|
||||
|
||||
// calculate the appropriate ROM range using the mapper
|
||||
const unmappedRange = new ROMRange(definition.range);
|
||||
const mappedRange = this.memoryMap.mapRange(unmappedRange);
|
||||
const asmSymbol = definition.asmSymbol;
|
||||
console.log(`${unmappedRange} ${asmSymbol} -> ${definition.file}`);
|
||||
|
||||
// extract the array data
|
||||
const objData = this.romData.slice(mappedRange.begin, mappedRange.end + 1);
|
||||
|
||||
// make a list of pointers
|
||||
let pointers = [];
|
||||
this.currentIndex = 0;
|
||||
|
||||
if (definition.type !== 'array') {
|
||||
// single object
|
||||
pointers.push(0);
|
||||
|
||||
} else if (definition.pointerTable) {
|
||||
|
||||
// extract the pointer table
|
||||
const ptrDefinition = definition.pointerTable;
|
||||
const isMapped = ptrDefinition.isMapped;
|
||||
let ptrOffset = Number(ptrDefinition.offset);
|
||||
if (!isMapped) {
|
||||
// map pointer offset first, then add pointers
|
||||
ptrOffset = this.memoryMap.mapAddress(ptrOffset);
|
||||
}
|
||||
|
||||
// extract the pointer table data
|
||||
let ptrRange = new ROMRange(ptrDefinition.range);
|
||||
ptrRange = this.memoryMap.mapRange(ptrRange);
|
||||
const ptrData = this.romData.subarray(ptrRange.begin, ptrRange.end + 1);
|
||||
const pointerSize = ptrDefinition.pointerSize || 2;
|
||||
|
||||
for (let i = 0; i < (ptrData.length / pointerSize); i++) {
|
||||
let pointer = 0;
|
||||
pointer |= ptrData[i * pointerSize];
|
||||
pointer |= ptrData[i * pointerSize + 1] << 8;
|
||||
if (pointerSize > 2) {
|
||||
pointer |= ptrData[i * pointerSize + 2] << 16;
|
||||
}
|
||||
pointer += ptrOffset;
|
||||
if (isMapped) {
|
||||
// map pointer after adding to pointer offset
|
||||
pointer = this.memoryMap.mapAddress(pointer);
|
||||
}
|
||||
pointers.push(pointer - mappedRange.begin);
|
||||
}
|
||||
|
||||
} else if (definition.itemRanges) {
|
||||
for (let i = 0; i < definition.arrayLength; i++) {
|
||||
const range = new ROMRange(definition.itemRanges[i]);
|
||||
const pointer = this.memoryMap.mapAddress(range.begin);
|
||||
pointers.push(pointer - mappedRange.begin);
|
||||
}
|
||||
|
||||
} else if (definition.terminator !== undefined) {
|
||||
// terminated items
|
||||
const terminator = Number(definition.terminator);
|
||||
pointers.push(0);
|
||||
for (let p = 0; p < (objData.length - 1); p++) {
|
||||
if (objData[p] === terminator) {
|
||||
pointers.push(p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (definition.itemLength) {
|
||||
// fixed length items
|
||||
const length = definition.itemLength;
|
||||
for (let i = 0; i < definition.arrayLength; i++) {
|
||||
pointers.push(i * length);
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplicates and sort pointers
|
||||
const sortedPointers = [...new Set(pointers)].sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
// create a list of pointer ranges (these may not correspond
|
||||
// with item ranges in some cases)
|
||||
let pointerRanges = {};
|
||||
for (let p = 0; p < sortedPointers.length; p++) {
|
||||
const begin = sortedPointers[p];
|
||||
let end = objData.length - 1;
|
||||
if (p !== (sortedPointers.length - 1)) {
|
||||
end = sortedPointers[p + 1] - 1;
|
||||
}
|
||||
pointerRanges[begin] = new ROMRange(begin, end);
|
||||
}
|
||||
|
||||
const itemRanges = [];
|
||||
const labelOffsets = {};
|
||||
for (let i = 0; i < definition.arrayLength; i++) {
|
||||
const begin = pointers[i];
|
||||
if (definition.terminator !== undefined) {
|
||||
let end = begin;
|
||||
const terminator = Number(definition.terminator);
|
||||
while (end < objData.length) {
|
||||
if (objData[end] === terminator) break;
|
||||
end++;
|
||||
}
|
||||
const range = new ROMRange(begin, end);
|
||||
itemRanges.push(range);
|
||||
|
||||
} else if (definition.isSequential) {
|
||||
let end = objData.length - 1;
|
||||
if (i !== definition.arrayLength - 1) {
|
||||
end = pointers[i + 1] - 1;
|
||||
}
|
||||
const range = new ROMRange(begin, end);
|
||||
itemRanges.push(range);
|
||||
|
||||
} else {
|
||||
itemRanges.push(pointerRanges[begin]);
|
||||
}
|
||||
|
||||
if (definition.type === 'array') {
|
||||
// add a label offset
|
||||
if (labelOffsets[begin]) {
|
||||
labelOffsets[begin].push(i);
|
||||
} else {
|
||||
labelOffsets[begin] = [i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(definition.file)) {
|
||||
let asmString = '';
|
||||
asmString += '.list off\n';
|
||||
asmString += '\n';
|
||||
asmString += `.define ${asmSymbol}Size`;
|
||||
asmString += ` ${hexString(mappedRange.length, 4, '$')}\n`;
|
||||
if (definition.type === 'array') {
|
||||
asmString += `.define ${asmSymbol}ArrayLength`;
|
||||
asmString += ` ${itemRanges.length}\n`;
|
||||
}
|
||||
|
||||
asmString += `\n${definition.asmSymbol}:\n`;
|
||||
|
||||
for (let pointer of sortedPointers) {
|
||||
// skip if pointer is out of range
|
||||
if (pointer < 0 || pointer > objData.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemList = labelOffsets[pointer] || [];
|
||||
const range = pointerRanges[pointer];
|
||||
// print the label
|
||||
for (let i = 0; i < itemList.length; i++) {
|
||||
const indexString = hexString(itemList[i], 4, '').toLowerCase();
|
||||
asmString += `\n${definition.asmSymbol}_${indexString}:`;
|
||||
}
|
||||
|
||||
// print the data
|
||||
const pointerData = objData.subarray(range.begin, range.end + 1);
|
||||
for (let b = 0; b < pointerData.length; b++) {
|
||||
if (b % 16 == 0) {
|
||||
asmString += '\n .byte ';
|
||||
} else {
|
||||
asmString += ',';
|
||||
}
|
||||
asmString += hexString(pointerData[b], 2, '$').toLowerCase();
|
||||
}
|
||||
asmString += '\n';
|
||||
}
|
||||
asmString += '\n.list on\n';
|
||||
|
||||
const asmPath = definition.file;
|
||||
fs.mkdirSync(getDirName(asmPath), { recursive: true });
|
||||
fs.writeFileSync(asmPath, asmString);
|
||||
}
|
||||
|
||||
if (definition.type === 'array') {
|
||||
// create each array item
|
||||
const array = [];
|
||||
for (let i = 0; i < itemRanges.length; i++) {
|
||||
const itemRange = itemRanges[i];
|
||||
const itemData = objData.slice(itemRange.begin, itemRange.end + 1);
|
||||
array[i] = this.decodeObject(itemData, definition.assembly);
|
||||
this.currentIndex++;
|
||||
}
|
||||
return array;
|
||||
} else {
|
||||
return this.decodeObject(objData, definition);
|
||||
}
|
||||
}
|
||||
|
||||
decodeObject(data, definition) {
|
||||
|
||||
// default definition is raw data
|
||||
definition = definition || { type: 'data' };
|
||||
|
||||
// decode the data
|
||||
if (definition.format) {
|
||||
const dataCodec = new ROMDataCodec(definition.format);
|
||||
data = dataCodec.decode(data);
|
||||
}
|
||||
|
||||
if (definition.type === 'text') {
|
||||
const encodingKey = definition.encoding;
|
||||
const textCodec = this.textCodec[encodingKey];
|
||||
const begin = definition.begin || 0;
|
||||
const textData = data.slice(begin);
|
||||
return textCodec.decode(textData);
|
||||
|
||||
} else if (definition.type === 'assembly') {
|
||||
let obj = {};
|
||||
for (let key in definition.assembly) {
|
||||
const subDefinition = definition.assembly[key];
|
||||
|
||||
// skip category names
|
||||
if (isString(subDefinition)) continue;
|
||||
|
||||
// skip external properties
|
||||
if (subDefinition.external) continue;
|
||||
|
||||
obj[key] = this.decodeObject(data, subDefinition);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
} else if (definition.type === 'array') {
|
||||
return this.decodeArray(data, definition);
|
||||
|
||||
} else if (definition.type === 'property') {
|
||||
return this.decodeProperty(data, definition);
|
||||
|
||||
} else {
|
||||
return Buffer.from(data).toString('base64');
|
||||
}
|
||||
}
|
||||
|
||||
decodeProperty(data, definition) {
|
||||
|
||||
// calculate the index of the first bit
|
||||
let mask = Number(definition.mask) || 0xFF;
|
||||
let bitIndex = 0;
|
||||
let firstBit = 1;
|
||||
while (!(firstBit & mask)) {
|
||||
bitIndex++;
|
||||
firstBit <<= 1;
|
||||
}
|
||||
|
||||
let value = 0;
|
||||
const begin = Number(definition.begin) || 0;
|
||||
const end = Math.min(begin + 4, data.length - 1);
|
||||
for (let i = end; i >= begin; i--) {
|
||||
value <<= 8;
|
||||
value |= data[i];
|
||||
}
|
||||
value = (value & mask) >> bitIndex;
|
||||
if (definition.bool) return (value !== 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
decodeArray(data, definition) {
|
||||
let array = [];
|
||||
this.indexStack.push(this.currentIndex);
|
||||
this.currentIndex = 0;
|
||||
|
||||
if (definition.terminator === '\\0') {
|
||||
// null-terminated text
|
||||
if (!definition.assembly || !definition.assembly.encoding) {
|
||||
console.log('Invalid null-terminated text definition');
|
||||
return array;
|
||||
}
|
||||
const encodingKey = definition.assembly.encoding;
|
||||
const textCodec = this.textCodec[encodingKey];
|
||||
let begin = 0;
|
||||
while (begin < data.length) {
|
||||
const end = begin + textCodec.textLength(data.subarray(begin));
|
||||
const itemData = data.slice(begin, end);
|
||||
const itemObj = this.decodeObject(itemData, definition.assembly);
|
||||
this.currentIndex++;
|
||||
array.push(itemObj);
|
||||
begin = end;
|
||||
}
|
||||
|
||||
} else if (definition.terminator !== undefined) {
|
||||
// terminated items
|
||||
const terminator = Number(definition.terminator);
|
||||
let begin = 0;
|
||||
let end = 0;
|
||||
while (end < data.length) {
|
||||
if (data[end] === terminator) {
|
||||
const itemData = data.slice(begin, end + 1);
|
||||
const itemObj = this.decodeObject(itemData, definition.assembly);
|
||||
this.currentIndex++;
|
||||
array.push(itemObj);
|
||||
begin = end + 1;
|
||||
}
|
||||
end++;
|
||||
}
|
||||
|
||||
// data at end with no terminator
|
||||
if (begin !== end) {
|
||||
console.log(`Invalid terminated array item`);
|
||||
}
|
||||
|
||||
} else if (definition.itemLength) {
|
||||
// fixed length items
|
||||
const length = definition.itemLength;
|
||||
let begin = 0;
|
||||
while (begin < data.length) {
|
||||
const itemData = data.slice(begin, begin + length);
|
||||
const itemObj = this.decodeObject(itemData, definition.assembly);
|
||||
this.currentIndex++;
|
||||
array.push(itemObj);
|
||||
begin += length;
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('Invalid sub-array');
|
||||
}
|
||||
|
||||
this.currentIndex = this.indexStack.pop();
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ROMDecoder;
|
348
tools/romtools/rom-encoder.js
Normal file
348
tools/romtools/rom-encoder.js
Normal file
@ -0,0 +1,348 @@
|
||||
// encode-rom.js
|
||||
|
||||
const fs = require('fs');
|
||||
const getDirName = require('path').dirname;
|
||||
const isArray = require('isarray');
|
||||
const ROMDataCodec = require('./data-codec');
|
||||
const ROMRange = require('./range');
|
||||
const ROMTextCodec = require('./text-codec');
|
||||
const hexString = require('./hex-string');
|
||||
|
||||
class ROMEncoder {
|
||||
constructor(definition) {
|
||||
|
||||
// create text codecs
|
||||
this.textCodec = {};
|
||||
for (let key in definition.textEncoding) {
|
||||
const encodingDef = definition.textEncoding[key];
|
||||
this.textCodec[key] = new ROMTextCodec(encodingDef, definition.charTable);
|
||||
}
|
||||
|
||||
this.currentIndex = 0;
|
||||
this.indexStack = [];
|
||||
}
|
||||
|
||||
encodeROM(definition) {
|
||||
for (let key in definition.assembly) {
|
||||
const objDefinition = definition.assembly[key];
|
||||
if (!objDefinition.file || !objDefinition.isDirty) continue;
|
||||
|
||||
const obj = definition.obj[key];
|
||||
|
||||
console.log(`Encoding ${objDefinition.asmSymbol}`);
|
||||
this.encodeParentObject(obj, objDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
encodeParentObject(obj, definition) {
|
||||
// const definition = this.dataMgr.getDefinition(key);
|
||||
// const obj = this.dataMgr.getObject(key);
|
||||
if (isArray(obj) ^ (definition.type === 'array')) {
|
||||
console.log(`Object/definition mismatch: ${definition.asmSymbol}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let objData;
|
||||
let pointers = [];
|
||||
this.currentIndex = 0;
|
||||
if (definition.type !== 'array') {
|
||||
// single object
|
||||
objData = this.encodeObject(obj, definition, null);
|
||||
pointers.push(0);
|
||||
|
||||
} else if (definition.isSequential || (definition.terminator !== undefined)) {
|
||||
// sequential array items
|
||||
let totalLength = 0;
|
||||
let dataArray = [];
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
pointers.push(totalLength);
|
||||
const itemData = this.encodeObject(obj[i], definition.assembly, null);
|
||||
this.currentIndex++;
|
||||
dataArray.push(itemData);
|
||||
totalLength += itemData.length;
|
||||
}
|
||||
objData = new Uint8Array(totalLength);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
objData.set(dataArray[i], pointers[i]);
|
||||
}
|
||||
|
||||
} else if (definition.itemLength) {
|
||||
// fixed-length array items
|
||||
const length = definition.itemLength;
|
||||
let totalLength = length * obj.length;
|
||||
objData = new Uint8Array(totalLength);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
const begin = i * length;
|
||||
pointers.push(begin);
|
||||
let itemData = new Uint8Array(length);
|
||||
itemData = this.encodeObject(obj[i], definition.assembly, itemData);
|
||||
this.currentIndex++;
|
||||
objData.set(itemData, begin);
|
||||
}
|
||||
|
||||
} else {
|
||||
// shared array items
|
||||
objData = new Uint8Array(0);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
const itemData = this.encodeObject(obj[i], definition.assembly, null);
|
||||
this.currentIndex++;
|
||||
let offset = findSubarray(objData, itemData);
|
||||
if (offset === -1) {
|
||||
// data not found
|
||||
offset = objData.length;
|
||||
const newData = new Uint8Array(offset + itemData.length);
|
||||
newData.set(objData);
|
||||
newData.set(itemData, offset);
|
||||
objData = newData;
|
||||
}
|
||||
pointers.push(offset);
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplicates and sort pointers
|
||||
const sortedPointers = [...new Set(pointers)].sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
// create a list of pointer ranges (these may not correspond
|
||||
// with item ranges in some cases)
|
||||
let pointerRanges = {};
|
||||
for (let p = 0; p < sortedPointers.length; p++) {
|
||||
const begin = sortedPointers[p];
|
||||
let end = objData.length - 1;
|
||||
if (p !== (sortedPointers.length - 1)) {
|
||||
end = sortedPointers[p + 1] - 1;
|
||||
}
|
||||
pointerRanges[begin] = new ROMRange(begin, end);
|
||||
}
|
||||
|
||||
const itemRanges = [];
|
||||
const labelOffsets = {};
|
||||
for (let i = 0; i < definition.arrayLength; i++) {
|
||||
const begin = pointers[i];
|
||||
if (definition.terminator !== undefined) {
|
||||
let end = begin;
|
||||
const terminator = Number(definition.terminator);
|
||||
while (end < objData.length) {
|
||||
if (objData[end] === terminator) break;
|
||||
end++;
|
||||
}
|
||||
const range = new ROMRange(begin, end);
|
||||
itemRanges.push(range);
|
||||
} else {
|
||||
itemRanges.push(pointerRanges[begin]);
|
||||
}
|
||||
|
||||
if (definition.type === 'array') {
|
||||
// add a label offset
|
||||
if (labelOffsets[begin]) {
|
||||
labelOffsets[begin].push(i);
|
||||
} else {
|
||||
labelOffsets[begin] = [i];
|
||||
}
|
||||
}
|
||||
}
|
||||
const asmSymbol = definition.asmSymbol;
|
||||
|
||||
let asmString = '';
|
||||
asmString += '.list off\n';
|
||||
asmString += '\n';
|
||||
asmString += `.define ${asmSymbol}Size`;
|
||||
asmString += ` ${hexString(objData.length, 4, '$')}\n`;
|
||||
if (definition.arrayLength) {
|
||||
asmString += `.define ${asmSymbol}ArrayLength`;
|
||||
asmString += ` ${definition.arrayLength}\n`;
|
||||
}
|
||||
// if (definition.itemLength) {
|
||||
// asmString += `.define ${asmSymbol}ItemLength`;
|
||||
// asmString += ` ${definition.itemLength}\n`;
|
||||
// }
|
||||
|
||||
asmString += `\n${definition.asmSymbol}:\n`;
|
||||
|
||||
for (let pointer of sortedPointers) {
|
||||
// skip if pointer is out of range
|
||||
if (pointer < 0 || pointer > objData.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemList = labelOffsets[pointer] || [];
|
||||
const range = pointerRanges[pointer];
|
||||
// print the label
|
||||
for (let i = 0; i < itemList.length; i++) {
|
||||
const indexString = hexString(itemList[i], 4, '').toLowerCase();
|
||||
asmString += `\n${definition.asmSymbol}_${indexString}:`;
|
||||
}
|
||||
|
||||
// print the data
|
||||
const pointerData = objData.subarray(range.begin, range.end + 1);
|
||||
for (let b = 0; b < pointerData.length; b++) {
|
||||
if (b % 16 == 0) {
|
||||
asmString += '\n .byte ';
|
||||
} else {
|
||||
asmString += ',';
|
||||
}
|
||||
asmString += hexString(pointerData[b], 2, '$').toLowerCase();
|
||||
}
|
||||
asmString += '\n';
|
||||
}
|
||||
asmString += '\n.list on\n';
|
||||
|
||||
const asmPath = definition.file;
|
||||
fs.mkdirSync(getDirName(asmPath), { recursive: true });
|
||||
fs.writeFileSync(asmPath, asmString);
|
||||
}
|
||||
|
||||
encodeObject(obj, definition, data) {
|
||||
|
||||
// default definition is raw data
|
||||
definition = definition || { type: 'data' };
|
||||
|
||||
if (definition.type === 'text') {
|
||||
const encodingKey = definition.encoding;
|
||||
const textCodec = this.textCodec[encodingKey];
|
||||
const begin = definition.begin || 0;
|
||||
const textData = textCodec.encode(obj);
|
||||
if (data) {
|
||||
const padValue = textCodec.getPadValue();
|
||||
data.fill(padValue, begin);
|
||||
data.set(textData, begin);
|
||||
} else {
|
||||
data = textData;
|
||||
}
|
||||
|
||||
} else if (definition.type === 'assembly') {
|
||||
for (let key in obj) {
|
||||
const subDefinition = definition.assembly[key];
|
||||
|
||||
// skip invalid assemblies
|
||||
const index = this.currentIndex;
|
||||
const invalid = subDefinition.invalid;
|
||||
if (invalid && eval(invalid)) continue;
|
||||
|
||||
data = this.encodeObject(obj[key], subDefinition, data);
|
||||
}
|
||||
|
||||
} else if (definition.type === 'array') {
|
||||
data = this.encodeArray(obj, definition, data);
|
||||
|
||||
} else if (definition.type === 'property') {
|
||||
data = this.encodeProperty(obj, definition, data);
|
||||
|
||||
} else {
|
||||
data = new Uint8Array(Buffer.from(obj, 'base64'));
|
||||
}
|
||||
|
||||
if (definition.format) {
|
||||
const dataCodec = new ROMDataCodec(definition.format);
|
||||
data = dataCodec.encode(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
encodeProperty(obj, definition, data) {
|
||||
|
||||
let value = obj;
|
||||
|
||||
// modify the value if needed
|
||||
if (definition.bool) value = (value ? 1 : 0);
|
||||
|
||||
// calculate the index of the first bit
|
||||
let mask = Number(definition.mask) || 0xFF;
|
||||
let bitIndex = 0;
|
||||
let firstBit = 1;
|
||||
while (!(firstBit & mask)) {
|
||||
bitIndex++;
|
||||
firstBit <<= 1;
|
||||
}
|
||||
|
||||
// shift and mask the value
|
||||
value = (value << bitIndex) & mask;
|
||||
|
||||
// find the beginning and end of this property
|
||||
const begin = Number(definition.begin) || 0;
|
||||
let end = begin;
|
||||
let byteMask = 0xFF;
|
||||
while (byteMask & mask) {
|
||||
byteMask <<= 8;
|
||||
end++;
|
||||
}
|
||||
|
||||
// validate the data length
|
||||
if (!data) {
|
||||
data = new Uint8Array(end);
|
||||
} else if (end > data.length) {
|
||||
let newData = new Uint8Array(end);
|
||||
newData.set(data);
|
||||
data = newData;
|
||||
}
|
||||
|
||||
// copy property value to data
|
||||
let unmask = (~mask) >>> 0;
|
||||
for (let i = begin; i < end; i++) {
|
||||
data[i] &= (unmask & 0xFF);
|
||||
data[i] |= (value & 0xFF);
|
||||
unmask >>= 8;
|
||||
value >>= 8;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
encodeArray(obj, definition, data) {
|
||||
|
||||
this.indexStack.push(this.currentIndex);
|
||||
this.currentIndex = 0;
|
||||
let pointers = [];
|
||||
if (definition.itemLength) {
|
||||
// fixed-length array items
|
||||
const length = definition.itemLength;
|
||||
let totalLength = length * obj.length;
|
||||
data = new Uint8Array(totalLength);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
const begin = i * length;
|
||||
pointers.push(begin);
|
||||
let itemData = new Uint8Array(length);
|
||||
itemData = this.encodeObject(obj[i], definition.assembly, itemData);
|
||||
this.currentIndex++;
|
||||
data.set(itemData, begin);
|
||||
}
|
||||
|
||||
} else {
|
||||
// sequential array items
|
||||
let totalLength = 0;
|
||||
let dataArray = [];
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
pointers.push(totalLength);
|
||||
const itemData = this.encodeObject(obj[i], definition.assembly, null);
|
||||
this.currentIndex++;
|
||||
dataArray.push(itemData);
|
||||
totalLength += itemData.length;
|
||||
}
|
||||
data = new Uint8Array(totalLength);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
data.set(dataArray[i], pointers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.currentIndex = this.indexStack.pop();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
function findSubarray(arr, subarr) {
|
||||
|
||||
for (let i = 0; i < 1 + (arr.length - subarr.length); i++) {
|
||||
let found = true;
|
||||
for (let j = 0; j < subarr.length; j++) {
|
||||
if (arr[i + j] !== subarr[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
module.exports = ROMEncoder;
|
196
tools/romtools/text-codec.js
Normal file
196
tools/romtools/text-codec.js
Normal file
@ -0,0 +1,196 @@
|
||||
const fs = require('fs');
|
||||
const isString = require('is-string');
|
||||
const isNumber = require('is-number');
|
||||
const isArray = require('isarray');
|
||||
const hexString = require('./hex-string');
|
||||
|
||||
class ROMTextCodec {
|
||||
constructor(definition, charTables) {
|
||||
|
||||
this.encodingTable = {};
|
||||
this.decodingTable = [];
|
||||
|
||||
if (!isArray(definition.charTable)) {
|
||||
console.log('Invalid text encoding');
|
||||
return;
|
||||
}
|
||||
for (let charTableKey of definition.charTable) {
|
||||
const charTable = charTables[charTableKey];
|
||||
if (!charTable) {
|
||||
console.log('Character table not found: ${charTableKey}');
|
||||
continue;
|
||||
}
|
||||
const keys = Object.keys(charTable.char);
|
||||
for (let c = 0; c < keys.length; c++) {
|
||||
const key = keys[c];
|
||||
const value = charTable.char[key];
|
||||
this.decodingTable[Number(key)] = value;
|
||||
this.encodingTable[value] = Number(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decode(data) {
|
||||
let text = '';
|
||||
let i = 0;
|
||||
let b1, b2, c;
|
||||
|
||||
while (i < data.length) {
|
||||
c = null;
|
||||
b1 = data[i++];
|
||||
b2 = data[i++];
|
||||
if (b1) c = this.decodingTable[(b1 << 8) | b2];
|
||||
if (!c) {
|
||||
c = this.decodingTable[b1];
|
||||
i--;
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
// unknown character
|
||||
text += hexString(b1, 2, '\\x');
|
||||
} else if (c == '\\0') {
|
||||
// string terminator
|
||||
break;
|
||||
} else if (c.endsWith('[[')) {
|
||||
// 2-byte parameter
|
||||
text += c;
|
||||
b1 = data[i++] || 0;
|
||||
b2 = data[i++] || 0;
|
||||
text += hexString(b1 | (b2 << 8), 4);
|
||||
text += ']]';
|
||||
} else if (c.endsWith('[')) {
|
||||
// 1-byte parameter
|
||||
text += c;
|
||||
b1 = data[i++] || 0;
|
||||
text += hexString(b1, 2);
|
||||
text += ']';
|
||||
} else {
|
||||
// no parameter
|
||||
text += c;
|
||||
}
|
||||
}
|
||||
|
||||
// remove padding from the end of the string
|
||||
while (text.endsWith('\\pad')) {
|
||||
text = text.substring(0, text.length - 4);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
encode(text) {
|
||||
let data = [];
|
||||
let i = 0;
|
||||
const keys = Object.keys(this.encodingTable);
|
||||
|
||||
while (i < text.length) {
|
||||
var remainingText = text.substring(i);
|
||||
var matches = keys.filter(function(s) {
|
||||
return remainingText.startsWith(s);
|
||||
});
|
||||
|
||||
if (!matches.length && remainingText.startsWith('\\x')) {
|
||||
const parameter = `0${remainingText.substring(1, 4)}`;
|
||||
i += 4;
|
||||
const n = Number(parameter);
|
||||
if (!isNumber(n)) {
|
||||
console.log(`Invalid value: ${parameter}`);
|
||||
} else {
|
||||
data.push(n);
|
||||
}
|
||||
continue;
|
||||
|
||||
} else if (!matches.length) {
|
||||
console.log(`Invalid character: ${remainingText[0]}`);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const match = matches.reduce(function (a, b) {
|
||||
return a.length > b.length ? a : b;
|
||||
});
|
||||
|
||||
// end of string
|
||||
if (match === '\\0') break;
|
||||
|
||||
let value = this.encodingTable[match];
|
||||
i += match.length;
|
||||
|
||||
if (match.endsWith('[[')) {
|
||||
// 2-byte parameter
|
||||
let end = text.indexOf(']]', i);
|
||||
const parameter = text.substring(i, end);
|
||||
let n = Number(parameter);
|
||||
if (!isNumber(n) || n > 0xFFFF) {
|
||||
console.log(`Invalid parameter: ${parameter}`);
|
||||
n = 0;
|
||||
end = i;
|
||||
}
|
||||
i = end + 2;
|
||||
value <<= 16;
|
||||
value |= n;
|
||||
} else if (match.endsWith('[')) {
|
||||
// 1-byte parameter
|
||||
let end = text.indexOf(']', i);
|
||||
const parameter = text.substring(i, end);
|
||||
let n = Number(parameter);
|
||||
if (!isNumber(n) || n > 0xFF) {
|
||||
console.log(`Invalid parameter: ${parameter}`);
|
||||
n = 0;
|
||||
end = i;
|
||||
}
|
||||
i = end + 1;
|
||||
value <<= 8;
|
||||
value |= n;
|
||||
}
|
||||
|
||||
if (value > 0xFF) {
|
||||
data.push(value >> 8);
|
||||
data.push(value & 0xFF);
|
||||
} else {
|
||||
data.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
const terminator = this.encodingTable['\\0'];
|
||||
if (isNumber(terminator) && data[data.length - 1] !== terminator) {
|
||||
data.push(terminator);
|
||||
}
|
||||
|
||||
return Uint8Array.from(data);
|
||||
}
|
||||
|
||||
textLength(data) {
|
||||
var i = 0;
|
||||
var b1, b2, c;
|
||||
|
||||
while (i < data.length) {
|
||||
c = null;
|
||||
b1 = data[i++];
|
||||
b2 = data[i++];
|
||||
if (b1) c = this.decodingTable[(b1 << 8) | b2];
|
||||
if (!c) {
|
||||
c = this.decodingTable[b1];
|
||||
i--;
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
continue;
|
||||
} else if (c === '\\0') {
|
||||
break; // string terminator
|
||||
} else if (c.endsWith('[[')) {
|
||||
i += 2;
|
||||
} else if (c.endsWith('[')) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(i, data.length);
|
||||
}
|
||||
|
||||
getPadValue() {
|
||||
return this.encodingTable['\\pad'] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ROMTextCodec;
|
8077
vanilla/ff6-en-rip.json
Normal file
8077
vanilla/ff6-en-rip.json
Normal file
File diff suppressed because it is too large
Load Diff
26
world/Makefile
Normal file
26
world/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
NAME = world
|
||||
OBJ_DIR = obj
|
||||
TARGET = $(OBJ_DIR)/$(NAME)_$(VERSION_EXT).o
|
||||
|
||||
SRC_MAIN = $(NAME).asm
|
||||
SRC_FILES = $(wildcard *.asm)
|
||||
INC_DIR = ../include
|
||||
INC_FILES = $(wildcard $(INC_DIR)/*.inc)
|
||||
DATA_FILES = $(wildcard data/*)
|
||||
GFX_FILES = $(wildcard gfx/*)
|
||||
TEXT_FILES = $(wildcard text/*)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# disable default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -r $(OBJ_DIR) data gfx text
|
||||
|
||||
$(TARGET): $(SRC_FILES) $(INC_FILES) $(DATA_FILES) $(GFX_FILES) $(TEXT_FILES)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(ASM) $(ASMFLAGS) -I $(INC_DIR) -l $(@:o=lst) $(SRC_MAIN) -o $@
|
17295
world/world.asm
Normal file
17295
world/world.asm
Normal file
File diff suppressed because it is too large
Load Diff
234
world/world_data.asm
Normal file
234
world/world_data.asm
Normal file
@ -0,0 +1,234 @@
|
||||
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | |
|
||||
; | FINAL FANTASY VI |
|
||||
; | |
|
||||
; +----------------------------------------------------------------------------+
|
||||
; | file: world_data.asm |
|
||||
; | |
|
||||
; | description: data for world module |
|
||||
; | |
|
||||
; | created: 8/15/2022 |
|
||||
; +----------------------------------------------------------------------------+
|
||||
|
||||
.segment "world_pal"
|
||||
|
||||
; d2/ec00
|
||||
.include "gfx/world_bg_pal1.asm"
|
||||
; d2/ed00
|
||||
.include "gfx/world_bg_pal2.asm"
|
||||
; d2/ee00
|
||||
.include "gfx/world_sprite_pal1.asm"
|
||||
; d2/ef00
|
||||
.include "gfx/world_sprite_pal2.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "mode7_cutscene_data"
|
||||
|
||||
; d8/dd00 Pointers to Magitek Train Ride Tile Graphics (29 items, 12x2 bytes each, +$7E0000)
|
||||
.res $02b8
|
||||
|
||||
; d8/dfb8 Vector Panorama Graphics (compressed)
|
||||
.res $0607
|
||||
|
||||
; d8/e5bf Vector Panorama Tilemap (compressed)
|
||||
.res $00fb
|
||||
|
||||
; d8/e6ba
|
||||
.include "gfx/snake_road_pal.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "world_data"
|
||||
|
||||
|
||||
; unused
|
||||
@b200: .faraddr WorldBackdropGfx
|
||||
@b203: .faraddr WorldBackdropGfx
|
||||
|
||||
WorldBackdropGfxPtr:
|
||||
@b206: .faraddr WorldBackdropGfx
|
||||
|
||||
WorldBackdropTilesPtr:
|
||||
@b209: .faraddr WorldBackdropTiles
|
||||
|
||||
AirshipGfx1Ptr:
|
||||
@b20c: .faraddr AirshipGfx1
|
||||
|
||||
WorldTilemap1Ptr:
|
||||
@b20f: .faraddr WorldTilemap1
|
||||
|
||||
WorldGfx1Ptr:
|
||||
@b212: .faraddr WorldGfx1
|
||||
|
||||
TrainGfxPtr:
|
||||
@b215: .faraddr TrainGfx
|
||||
|
||||
; unused
|
||||
@b218: .faraddr TrainPal
|
||||
|
||||
TrainPalPtr:
|
||||
@b21b: .faraddr TrainPal
|
||||
|
||||
WorldGfx2Ptr:
|
||||
@b21e: .faraddr WorldGfx2
|
||||
@b221: .faraddr WorldTilemap2
|
||||
|
||||
WorldTilemap2Ptr:
|
||||
@b224: .faraddr WorldTilemap2
|
||||
|
||||
WorldTilemap3Ptr:
|
||||
@b227: .faraddr WorldTilemap3
|
||||
|
||||
WorldGfx3Ptr:
|
||||
@b22a: .faraddr WorldGfx3
|
||||
|
||||
; unused
|
||||
@b22d: .faraddr WorldChocoGfx1
|
||||
|
||||
WorldChocoGfx1Ptr:
|
||||
@b230: .faraddr WorldChocoGfx1
|
||||
|
||||
; unused
|
||||
@b233: .faraddr VectorApproachPal
|
||||
|
||||
VectorApproachPalPtr:
|
||||
@b236: .faraddr VectorApproachPal
|
||||
|
||||
; unused
|
||||
@b239: .faraddr WorldEsperTerraPal
|
||||
|
||||
WorldEsperTerraPalPtr:
|
||||
@b23c: .faraddr WorldEsperTerraPal
|
||||
|
||||
; unused
|
||||
@b23f: .faraddr WorldSpriteGfx1
|
||||
|
||||
WorldSpriteGfx1Ptr:
|
||||
@b242: .faraddr WorldSpriteGfx1
|
||||
|
||||
WorldSpriteGfx2Ptr:
|
||||
@b245: .faraddr WorldSpriteGfx2
|
||||
|
||||
WorldChocoGfx2Ptr:
|
||||
@b248: .faraddr WorldChocoGfx2
|
||||
|
||||
MinimapGfx1Ptr:
|
||||
@b24b: .faraddr MinimapGfx1
|
||||
|
||||
MinimapGfx2Ptr:
|
||||
@b24e: .faraddr MinimapGfx2
|
||||
|
||||
AirshipGfx2Ptr:
|
||||
@b251: .faraddr AirshipGfx2
|
||||
|
||||
; ending airship palette
|
||||
EndingAirshipScenePalPtr:
|
||||
@b254: .faraddr EndingAirshipScenePal
|
||||
|
||||
.res 3*3
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; ee/b260
|
||||
WorldModDataPtrs:
|
||||
.res 3*3
|
||||
|
||||
; ee/b269
|
||||
VehicleEvent_00:
|
||||
.faraddr $000068
|
||||
|
||||
VehicleEvent_01:
|
||||
.faraddr $00004f
|
||||
|
||||
VehicleEvent_02:
|
||||
.faraddr $000059
|
||||
|
||||
VehicleEvent_03:
|
||||
.faraddr $000088
|
||||
|
||||
VehicleEvent_04:
|
||||
.faraddr $00007f
|
||||
|
||||
VehicleEvent_05:
|
||||
.faraddr $00008f
|
||||
|
||||
VehicleEvent_06:
|
||||
.faraddr $000096
|
||||
|
||||
.res 6*3
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; ee/b290 World Map Clouds Graphics (compressed)
|
||||
.include "gfx/world_backdrop_gfx.asm"
|
||||
|
||||
; ee/c295 World Map Clouds Tile Formation (compressed)
|
||||
.include "gfx/world_backdrop_tiles.asm"
|
||||
|
||||
; ee/c702 Blackjack Graphics (compressed)
|
||||
.include "gfx/airship_gfx1.asm"
|
||||
|
||||
; ee/d434 World of Balance Map Formation (compressed)
|
||||
.include "data/world_tilemap1.asm"
|
||||
|
||||
; ef/114f World of Balance Graphics (compressed)
|
||||
.include "gfx/world_gfx1.asm"
|
||||
|
||||
; ef/3250 Magitek Train Ride Graphics (variable size tiles, compressed)
|
||||
.include "gfx/train_gfx.asm"
|
||||
|
||||
; ef/4846 Magitek Train Ride Palettes
|
||||
.include "gfx/train_pal.asm"
|
||||
|
||||
; ef/4a46 World of Ruin Graphics (compressed)
|
||||
.include "gfx/world_gfx2.asm"
|
||||
|
||||
; ef/6a56 World of Ruin Map Formation (compressed)
|
||||
.include "data/world_tilemap2.asm"
|
||||
|
||||
; ef/9d17 Serpent Trench Map Formation (compressed)
|
||||
.include "data/world_tilemap3.asm"
|
||||
|
||||
; ef/b631 Serpent Trench Graphics (compressed)
|
||||
.include "gfx/world_gfx3.asm"
|
||||
|
||||
; ef/c624 Some Chocobo Graphics (compressed)
|
||||
.include "gfx/world_choco_gfx1.asm"
|
||||
|
||||
; ef/ce77 Vector Approach Palette
|
||||
.include "gfx/vector_approach_pal.asm"
|
||||
|
||||
; ef/ce97 Esper Terra Palette
|
||||
.include "gfx/world_esper_terra_pal.asm"
|
||||
|
||||
; ef/ceb7 Airship Shadow and Perspective Graphics (compressed)
|
||||
.include "gfx/world_sprite_gfx1.asm"
|
||||
|
||||
; ef/cfb9 Various Sprites (ship, esper terra, figaro, etc., compressed)
|
||||
.include "gfx/world_sprite_gfx2.asm"
|
||||
|
||||
; ef/dc4c More Chocobo Graphics (world map, compressed)
|
||||
.include "gfx/world_choco_gfx2.asm"
|
||||
|
||||
; ef/e49b World of Balance Minimap Graphics (compressed)
|
||||
.include "gfx/minimap_gfx1.asm"
|
||||
|
||||
; ef/e8b3 World of Ruin Minimap Graphics (compressed)
|
||||
.include "gfx/minimap_gfx2.asm"
|
||||
|
||||
; ef/ed26 Falcon Graphics (compressed)
|
||||
.include "gfx/airship_gfx2.asm"
|
||||
|
||||
; ef/fac8 Palettes for Ending Airship Scene
|
||||
.include "gfx/ending_airship_scene_pal.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
.segment "world_sine"
|
||||
|
||||
; ef/fef0
|
||||
.include "data/world_sine.asm"
|
||||
|
||||
; ------------------------------------------------------------------------------
|
Loading…
Reference in New Issue
Block a user