Bookmark this blog entry!
Last Update: 2022 / 10 / 08
IMPORTANT NOTICE:
If you just want the RAM table, visit the Castlevania 3 page on the Data Crystal Wiki! This is still my notes repository, but it's a chaotic mess.
Just in case my computer dies and I lose the RTF file I've been keeping
my notes in, I will post my notes on the address indexing in CV3. That
is, where Trevor's and enemy's coordinates are stored in the RAM, where
the bounding box offsets (so-to-speak, since there aren't really
bounding boxes) are stored, as well as image speeds and other alarms.
All that fun stuff will be noted here.
There are up to 22 instances addressed at a time. Trevor always has the
lowest address, then enemies, then 5 torches or lanterns, then 5
candles. Item drops use the same address as the enemy, torch or candle
they were dropped from. The whip is at X:$13, then the three subweapons.
I think X:$17 is reserved for the ally swap, while X:$18 is reserved
for the subweapon HUD display.
RAM pages 4 through 7 are used, most of the data being stored in pages 4
and 5. Page 6 is frequently used for alarms or -- in the case of
sinusoidal movement -- storing xstart and ystart. Pages 6 and 7 are used
for the tile map(s).
Some non-ranged addresses will be included as well. Anything in page 0
is likely unique. An (R) denotes a ranged set of values. A (C) denotes a
cascading set of values, meaning they are updated multiple times each
step, usually for PPU purposes. All addresses listed as "Sprite index"
or "Image index" relate in one way or another to the sprite displayed.
Addresses listed below use the lowest index in the ranges (see above).
The US version has many offsets changed due to changes in coding. ROM
offsets are often different between versions. RAM offsets in the Zero
Page are one byte lower in the US version.
Instance Mapping in the X Register
Up to 28 (0x00 - 0x1B) instances can be mapped out into any room.
Certain instance mappings are reserved for specific types. Addresses
after 0x17 are used for special cases and as such some indirect
addresses exclude them. Below is a list of each mapping.
0x00 Trevor or Ally
In many situations, especially when another instance is already
mapped into the X register, this mapping is ignored and variables are
referenced directly, such as $0438 instead of $0438,X. If you do see a variable referenced with a 0x00 X register, you're probably within a loop, such as a drawing loop.
0x01-0x0C Hostiles
Most everything is spawned within free range. Most projectiles spawn
within the range of 0x0A to 0x0D, limiting the number of projectiles on
screen typically to 4 at a time. Axe Knights are an exception to the
rule.
0x0D-0x12 Luminaries
These higher values are reserved for candles, torches and such.
0x13 Primary Attack
This is reserved for Trevor's whip or an ally's primary attack.
0x14-0x16 Item Attacks and Spells
Subweapons and Alucard's orbs occupy this range.
0x17 On-Hit Spark
The spark that appears over enemies or candles when hit.
0x18 Ally Icon
The sprite info for the ally icon is stored here (used in the draw phase).
0x19 Item Sprite
Secondary attack icon info is stored here (used in the draw phase).
0x1A-0x1B Breakable Blocks
For double=blocks, refers to the lower block.
PROM Bank Allocation
Bank #04 Audio Engine; Background Music
Bank #05 Background Music;
Bank #0C Audio Engine; Sound Effects
Bank #0F System Routines; Common Subroutines
Bank #10 Graphics
PRAM Addressing
$E249(U:$E25F) Sound Play
The value in the accumulator specifies a sound or music to play
$E2D0 PRAM Bank
Set the current bank to the value in the accumulator.
$F543 Step Beginning
Essentially the start of each step
iteration as far as anyone attempting to hack the game would really even
care about. Everything before it is technical stuff and drawing code,
pretty much.
$FCDE Trevor's Collision Detection
The accumulator holds the relative x-offset to check, while the
Y-register checks the relative y-offset to check for collisions with the
tile map.
WRAM Addressing
$0011 Attack height (and miscellaneous)
Used for lots of temporary variables, but especially useful when trying to watch death sequences.
$0012 Temporary sprite_index
Used to keep track of $007F and $0565. Adds #$08 to Trevor's y when ducking ($0565==#$1C).
$0015 Temporary y
Used to keep track of $0041C when attacking.
$0017(U) Repeat Counter
Tracks number of loops in some repeated calls.
$0018(U:$0018) System State
Controls menus, screen transitions, and stuff. This is the byte you need to mess with in order to access the debug tools.
$00 Intro & Title Screen
$01 (unused?)
$02 New Game Black Screen (goto $0A)
$03 Gameplay Black Screen (goto $04)
$04 Gameplay Screen
$05 Game Over Black Screen (goto $06)
$06 Game Over Screen
$07 Stage Select Screen
$08 Map Screen
$09 Prayer Screen
$0A Name Entry Screen
$0B Password Entry Screen
$0C Epilogue Screen
$0D Credits Screen
$0E Respawn Black Screen (goto $04)
$0F Sound Test Screen
$0019(U:$0019) System Substate
See above. This is why changing $18 directly will likely cause artifacting, unless this is set to #$00 first.
$001B(U:$001A) Step Counter
Incremented by 1 every step. Typical step counter found in most
games. Frequently used for incremental timing or simple randomization.
$001D(U:$001C) Blackout Timer
Set to #02 whenever a new tileset is loaded, then counts down after
$0061 counts down. Forces the game to clear bits 3 and 4 of PPUMASK,
resulting in nothing being drawn. Also prevents player pausing.
$0020(U:$001F) Randomizer
True randomizer in the game, although infrequently used. Incremented
by 1 and then added to $001B while waiting for the system interrupt.
$0021 PPU Bank Mode
Can probably ignore, unless you mess with the VRC6 mapper stuff.
$0022(U:$0020) Unknown
Increases by #44 every frame. Don't know what it's used for.
$0025(U) Nametable Mapping
Write to register $5105.
$0028 Controller press (R)
Used to check if a new button on the controller is pressed.
$002A Controller hold (R)
Each button press adds a specific value to this. Right adds #01, left
adds #02, down adds #04, up adds #08, Start adds #10, Select adds #20, B
adds #40, and A adds #80.
$002C(U:$002A) Game State
As opposed to System State, used during gameplay. By default it's
#$03. Set to #$08 when walking through a door. Set to #$06 when climbing
stairs to another room, then to #$07, then to #$01, then to #$04 if
it's a vertical room next, then increased so it's #$05 in the next room;
if it's a horizontal room it's just increased from #$01 twice so it's
#$03 again. In a new stage it's set to #$00 then increased, then set to
#$03 or #$05.
Gets set temporarily to #$19 when whip upgrade is collected and then reset back to #$03.
Set to #$11 when soul crystal is retrieved or at Clocktower exit -- a value of #$11 at least is for converting time and hearts to score.
Set to #$18 when walking out of the tower and when at the auto-walk door in the forest. So a value of #$18 is for auto-walking.
Set to #$1E when fleeing Grant's tower or exiting the Clocktower,
then to #$17, then to #$1F, then normal stage start settings -- this
appears to be normal behavior for a scripted automatic stage transition.
Set to #$09 when climbing stairs to the cloudy areas in Sypha's stage
and then set to #$0A so it's actually set at #$0A throughout that room.
Set to #$0B when changing characters.
Set to #$0F for entering a room with auto-climb stairs (Forest, Main Hall). The next stairs out (the auto-climb) set it to #$10.
Set to #$1A when Sypha casts her ice spell in freezable water. After
the water freezes over, this gets set to #$1B and is used to prevent
conveyor tiles from moving.
$002D(U:$002B) Player Paused
Set when player pauses.
$002E Stage clear
Set when you defeat the boss and the soul shard drops. Prevents player pausing.
$0030 Password Checkpoint
Used by password verifier. Determines what stage and path is generated by the password.
$0031 Grant Defeated
Set when Grant is defeated.
$0032(U:$0030) Effect Timer (Word)
Timer for start menu effect, prayer scene timing, and whip upgrade timing.
$0034(U:$0032) Stage
This is the first value found in reVamp's room editor. Ex., 02:01.02. This gets displayed plus 1.
$0035(U:$0033) Block
This is the middle value found in reVamp's room editor. Ex.,
02:01.02. This gets displayed directly. It is increased by 1 when
walking through a door.
$0036(U:$0034) Sub-room
This is the third value found in reVamp's room editor. Ex., 02:01.02.
This never gets displayed. These are not sequential. Walking down
stairs decreases it. A safety check is in place to make sure if it's
#$00 before going down stairs that it becomes #$01, yet most of the time
the rooms are set so that doesn't happen.
$0037(U:$0035) Lives
Remaining lives. Note: the UI does not update until the tileset changes.
$0038(U:$0046) CHR 0
This is the first set of sprites loaded. This cannot be changed in
reVamp. This affects Trevor's sprites. Spirit partners have different
offsets, so you can't just change this to make Trevor look like Grant
with a whip. In the PPU, this is the top set of tiles loaded on the
left.
$0039(U:$0047) CHR 1
This is the second set of sprites loaded. This cannot be changed in
reVamp. This affects Trevor's whip. In the PPU, this is the second set
of tiles loaded on the left. Each of the CHR selects can be "cleared" by
setting them to $7F.
$003A(U:$0048) CHR 2
This is the third set of sprites loaded. This can be changed in
reVamp. This affects certain enemies and moving platforms. In the PPU,
this is the third set of tiles loaded on the left.
$003B(U:$0049) CHR 3
This is the fourth set of sprites loaded. This can be changed in
reVamp. This affects certain enemies and moving platforms. In the PPU,
this is the bottom set of tiles loaded on the left.
$003C(U:$004A) CHR 4
This is the first set of tiles loaded. This cannot be changed in
reVamp. In the PPU, this is the top set of tiles loaded on the right.
$003D(U:$004B) CHR 5
This is the second set of tiles loaded. This can be changed in
reVamp. In the PPU, this is the second set of tiles loaded on the right.
$003E(U:$004C) CHR 6
This is the third set of tiles loaded. This can be changed in reVamp.
In the PPU, this is the third set of tiles loaded on the right.
$003F(U:$004D) CHR 7
This is the last set of tiles loaded. This cannot be changed in
reVamp. In the PPU, this is the bottom set of tiles loaded on the right.
$0040(J) IRQ Control
Write value to $F001 in VRC6 mapper.
$0040(U) Scanline IRQ Status
Read from register $5204.
$0041(J) IRQ Latch HUD
Write value to $F000 in VRC6 mapper. Negated before writes. Lower y-coordinate for HUD.
$0041(U) Scanline Target
Write to register $5203.
$0042 IRQ Latch Ally Swap
Write
value to $F000 in VRC6 mapper. Negated before writes. Set to 72 pixels
above player, then alternates by 1 every step to create the ally
swapping effect.
$0043(U:$003F) Default Drawing State
Default value written to $006A each step.
$0044 Score (R)
Bytes $0044 through $0046 hold the score formatted in decimal.
$0048(U:$003A) Spirit Partner
Sypha is #01, Grant is #02, Alucard is #03, Trevor solo is #FF.
$0049(U:$003B) Spirit Swapped
This is set when Trevor swaps places with his partner.
$004A(U:$003C) PC Health
Max value is #40. One bar is equal to 4 HP.
$004B(U:$003D) Boss' health
During boss fights, this is the same as $067C, otherwise it's #40.
$004C(U:$003E) Score Target
Target value for $0046 to get an extra life.
$004E(U only) Alternate CHR Bank
Alternate CHR4 to use in Hard Mode.
$0053(U:$0056) View Position (Word)
This is the view's coordinates in the room. In horizontal stages, this is the xview, but in vertical stages it's yview.
$0055(U:$0058) View Position Subpixel
Used for when Trevor's hspeed isn't 1 or 2 exactly.
$0056(U:$0059) Spawn Area Left
This is essentially every 32 pixels in the view, but set outside the
view. So when xview is #00, this is #FE. Only gets checked when walking
left.
$0057(U:$005A) Spawn Area Right
Same as above, but this one is for the right.
$0059(U:$005C) TSA Row Iterator
Used to track which row of tile assemblies is being loaded.
$005A(U:$005D) Tile Assembly Definitions (W)
Prefectched ddress of current tile assembly definitions.
$005C(U:$005F) Tile Assembly Pallets (W)
Prefetched
address of current tile assembly pallets. Pallet definitions themselves
are single byte values. Least significant nybble is top-left tile and
most signficant nybble is bottom-right tile.
$005E(U:$0061) Tile Assembly Nametable Address (W)
Nametable address for next row of tiles to be written to PPU.
$0060(U:$0063) Tile Assembly Value
Fetched tile assembly value. Multiplied by 16 and added to $005A.
$0061(U:$0064) Transition Load Delay
Set to #0C whenever a new tileset is loaded, then counts down.
$0062 Movement
This is set according to Trevor's movement. If he walks left, this
gets set to 0; if he walks right, it's set to 1. It will constantly
toggle back to 2, possibly at the start of each step.
$0063 View Edge Previous
Previous edge of view. Used to track tile and spawner refresh times.
$0064 Update View
Set whenever the view has moved.
$0065(U:$0068) Room orientation
Setting this to anything less than $80 means the room is oriented
horizontally. Setting this to $80 or greater means the room is oriented
vertically. The chapel in stage 1 is set to $80. (More details in ROM
addresses below.) Since there are no doors in vertical stages, this also
affects how close to the edge of the screen Trevor can walk to.
$0066 Stair Mapping (Word)
The PRG-ROM address for the index of values for any stairs in the current room is stored here.
$0068 Transition State
In door-to-door room transitions, this is the substate. 0 sets up the
transition, 1 is a slight delay before the transition progresses, 2 is
the first scroll, 3 is a slight pause, 4 is the opening of the door, 5
is Trevor walking, 6 is the closing of the door, 7 is another pause, and
8 is the final scroll.
$0068 Trevor's phase counter
Appears to be a phase counter used during partner changes. Cleared when walking through a door.
$0069 Event object
I think this keeps track of which instance is currently being
handled. Since X will change when a projectile is thrown, for example,
the program needs to keep track of which value of X points to the
instance that created said projectile. When the room starts, this is
used to keep track of how many interactive terrains are in the room and
is stored for each of those terrain objects.
$006A(U:$006D?) Active Drawing State
Appears to be a state machine for handling some drawing. Does not affect status bar, though.
$006B Object View Scroll Speed
This is dependent upon the room orientation. If the room is oriented
horizontally, this value is subtracted from the x-coordinates of all
instances except Trevor. If the room is oriented vertically, this value
is added to the y-coordinates of all instances except Trevor. If
Trevor's in the middle of the view, of a room with multiple screens,
this value is added to his x-coordinate in a horizontal stage.
$006C View Offset (W)
The game has to tell the NES how far to shift the tiles over. This is
how it does it. When Trevor moves, it increases or decreases and the
value is passed to register $2005.
$006E Room Size
Signifies number of screens a room spans. One screen is technically
256px x 192px, or 48 tile assemblies. Compared against xview for
horizontal stages or yview for vertical stages.
$006F CHR Bank Mode
If
set to #00 or #03, will load all CHR banks as set in $0038 thru $003F.
If set to #01 or #02, will force tilesets #41 and #42 into banks 5 and 6
for the status bar, and will also clear banks 2 and 3 (rendering NPCs
invisible) if set to #02.
$0070 Previous Orientation
The previous room's orientation is stored here. This is used in room
transitions. Roundabout method. If it's negative, the previous room was
vertical, which apparently affects what the next room is. Negative $0065
overrides this always.
$0071 Music Loaded
Set when stage music starts playing.
$0072
Unknown.
$0073 Horizontal Spawner Block
Initially set to the lowest spawner block (those 64px wide divisions
that can only hold one spawner), which is determined by the view
position. The value is used as an index pointer for the spawner
database. This is also the ID of enemy spawners.
$0074 First Spawner Position
Coordinate of leftmost spawner block. Checked after view moves 64px.
$0075 (U:$78) Boss lock
This will freeze screen movement when a boss is present.
$0076 Vertical Spawner Block
Next
section of a vertical room to load enemies in. If moving into the
specified section, loads the next section of the vertical room.
$0077 Vertical Spawn Reset?
No
idea what this is for. In some rooms it goes up along with $0076 as
things are spawned in, but in other rooms it stays at 0. In
auto-scrolling rooms, $0076 is set to this after spawning and it is
never referenced again.
$0078 Horizontal Spawn Count
There are 8 spawn sections per screen in a horizontal room, so this
value is $0063*8+8. This is used to prevent the spawner algorithm from
trying to read beyond the right edge of the room.
$007A Animated backgrounds
Set for stages with animated backgrounds. The value denotes parallax
scrolling or which tiles to change. Seems to hold two values (suggested
by &#$F0 operation).
NOTE: STAGES WITH PARALLAX BACKGROUNDS (i.e., clouds) CANNOT HAVE ANY
INTERACTIVE TERRAIN OBJECTS IN THEM BECAUSE PARALLAX CLOUDS OCCUPY THE
SAME RAM OFFSETS AS TERRAIN OBJECTS.
Known values:
$10 Stormy Clouds (3-02)
$11 Wide Clouds (Forest)
$12 High Clouds (Castle Keep)
$13 Low Clouds (Castle Keep)
$20 Auto Stairs (Forest Ruins)
$21 Auto Door (Forest Exit)
$30 River + Waterfall (Causeway)
$31 River + Waterfalls (Castle Tower)
$40 Clockwork (Dracula's Tower)
$41 Noisy Clockwork (Clocktower)
$50 Watery Death (Sunken City)
$51 Watery Death (Sunken City)
$52 Watery Death (Sunken City)
$60 Watery Death (Sunken City, multiple pools)
$71 Rising Water (Sunken City)
$80 Collapsing Bridge (Causeway)
$81 Collapsing Bridge (Main Hall)
For the Watery Deaths, the lower bits set the depth at which the
splash occurs. For #$5X range, these values are adjustable at PROM
address 0x38C5D to 0x38C60. For the #$6X range, these are hard-coded,
with #$60 being at depth #$C0 as defined in PROM address 0x38C2F,
whereas any other value is the default screen depth of #$E0 (in other
words, #$61 to #$6F are junk ranges and I have no idea why they are
there other than as remnants of previous code).
$007B(U:$007E) Timer (Word)
Converted into decimal and stored here. What you see is what you get.
$007D(U:$0080) Wounded timer
Set when Trevor takes damage and becomes invincible. It counts down
each step if set. While set, every other step toggles Trevor's
visibility state.
$007E(U:$0081) Damage received
When an enemy hits Trevor, the damage level is temporarily stored here.
$007F(U:$0082) PC Height
This is the player's object index,
increased by 4 when ducking, but not jumping. Set to #08 when Alucard is
in Bat form. Used to determine where the top of the hitbox will be for
each character.
$0081(U:$0084) Hearts
The game converts the heart count to decimal format before storing to this address. The highest this can be is 99 (hard-coded).
$0082(U:$0085) Trevor's Subweapon
Axe is $01, Cross is $02, Dagger is $03, Holy Water is $04, Sypha's
fire is $05, Sypha's ice is $06, Sypha's water is $07, Grant's dagger is
$08, Grant's axe is $09, Alucard's fireball is $0A, Stopwatch is $0B.
$0083(U:$0086) Ally's Subweapon
Same as above, but for the current ally.
$0084(U:$0087) Trevor's Multiplier
Subweapon multiplier level for Trevor.
$0085(U:$0088) Grant's Multiplier
Subweapon multiplier level for Grant.
$0089(U) Status Bar Cutoff
Scanline value to start drawing room view.
$0088(U:$008B) Conveyance
When terrain (conveyor or mud) hinders Trevor's movement, this gets set
$01 Riding platforms or frozen enemies
$02 Can only walk right
$03 Uknown/No effect
$04 Unknown/No effect
$05 Teeter-Totter
$06 Gears
$07 Mud
$08 Waterfall
$09 Convey right under waterfall
$0A Convey left
$0B Convey right
$0D Petrified
$0E Auto-Walk
$0089(U$:008C) Spark Timer
Controls when the spark from hitting an enemy disappears.
$008A(U:$008D) Rooom Initialized/Load Tileset
Set when (re)starting room, lets program know to load tileset.
$008B(U:$008E) Whip level
$008C(U:$008F) Alucard Orb Level
$008D Knockback direction
Set to determine direction Trevor gets knocked back when wounded.
$008E Conveyor
When Trevor's movement get affected by some object (enemy or
terrain), this gets set to the spawn ID and used to knock back or lift
Trevor. Only used by a few objects.
$0090 Gear Tooth Proximity
When on a gear, this gets set based on Belmont's proximity to the
nearest gear tooth. A value of 1 means the tooth is either to the right
or below. A value of 2 means the tooth is to the right and below. A
value of 0 means there are no teeth in proximity. This is meant to
dictate how the gear actually affects player movement.
$0091 Gear Tooth ID
For gears, this is the value of the tooth closest to Trevor.
$0092 Gear Size
Denotes if on a small gear (0) or big gear (1) when riding gears.
$0099 Subweapon Count
Counts how many times a subweapon is
used on candles and enemies. Small hearts become multipliers when this
gets high enough. Resets whenever a weapon or multiplier is grabbed.
$009A Grant Jump Cutoff
Tells the game when to cut off Grant's jump height.
$009B Subweapon Iterator
Used by system to loop across all subweapons checking for collisions. Ignore it.
$009C Game State Previous
Temporary holder for $002C when collecting whip upgrade.
$009D PC Direction Recent
Temporary holder for $009E when collecting whip upgrade.
$009E PC Direction Previous
Temporary holder for $0062 when collecting whip upgrade. Yeah, it gets confusing.
$00A0 In-Game Step Counter
Similar to $001B, except only incremented while game is not paused.
$00A2 Odd Tile Check
Set if the right tile is checked in a tile map read.
$00A3 thru $00A6
Temporary holder for $0040 thru $0043 when changing characters.
$00A7
Temporary holder for $2C when changing characters.
$00A8 Stopwatch status
Set when the Stopwatch is used. Prevents player pausing.
$00A9 Stopwatch timer
Counts down when Stopwatch used. Also affects sound effects.
$00AA Potion Timer
Invinicibility Potion timer.
$00AB Bat Timer
Set
to 1 when Alucard turns into a bat. Counts down while Alucard is a bat.
When it reaches 0, decreases hearts by 1 and resets to #3C (1 second).
$00AC Landing Delay
When falling off a frozen enemy or when walking off a floating
platform, this timer gets set and decremented. Prevents simply walking
across floating platforms or frozen enemies.
$00AD Force Doppelganger Sprites
Forces sprite CHR bank write for Doppelganger.
$00AE Reference Cache
Used to save X register or PROM page pointers temporarily.
$00AF
Unknown
$00B0
Related to the Stopwatch, it seems.
$00B4
Gets set momentarily when colliding with a whip powerup.
On the Name Entry and Password screens, this is a counter for the
fade-in and fade-out transition codes. The corresponding values added to
each palette entry are: #$00, #$F0, #$E0, #$D0, #$C0, and #$FF. The
#$FF marks the break. All this does is shift the palette entries up one
row to darken the colors. If a value becomes negative, the entry is set
to $0F (black).
$00B5 Platform Current
Set when landing on a floating platform or frozen enemy. Used to prevent walking across elevators or frozen enemies.
$00B5
On the Name Entry and Password screens, this is the timer between steps in the fade-in and fade-out transition codes.
$00B6 Rough landing
If Trevor falls from too high up, this gets set when he lands, preventing movement until it ticks down.
$00B7 Boss fight
This gets set depending on which boss is fought.
$00BC Death pause
When Trevor dies, this gets set to #$80 to prevent damage
calculations and pause the game while Trevor goes through his death
animation and the room restarts.
$00BD
Unknown
$00BE
Unknown
$00BF
Unknown
$00C1-$00C2 Swapping Disabled(?) (REMOVED?)
Disables partner swapping when set. Gets reset to 0 when changing rooms. I haven't found code that sets it to anything other than 0.
$00C5 Horizontal lock
When this is set, the view will not move left, preventing Trevor from
going less than 16 pixels from the left edge. This is set when a
crumbling bridge is encountered, for example. This is not set by bosses.
$00C7 Flood Height
Height of water in flooding rooms.
$00C9
$00CA
$00CB Enemy drop counter
Incremented and checked when random check passes on enemy death
($001B & $07). When less than $05, enemies that pass the check
drop a small heart. Every fifth enemy that passes the check has a 50%
chance of dropping an Axe, 25% chance of dropping a Dagger, and 25%
chance of dropping a Stopwatch. If Trevor already has the subweapon, a
small heart is dropped instead. Potential drops are stored in PRG-ROM
$04:BE93 thru $04:BE96 (see Item drop below).
$00CC Conveyance Previous
Holds previous value of $0088.
$00CD Vertical room ceiling
For vertical stages, this is the height at which Trevor will bump his
head. Tile collisions will always return #$00 in that case.
$00EF Audio Sample
Temporarily holds currently passed audio value, which could be a track or DCM sample.
$00F0 Broken Luminary Tracker (R)
Tracks which luminaries have been broken in the current room. Prevents respawning luminaries.
$00F4 Breakable wall status (R)
This gets set to FF for each breakable wall in a block that gets
destroyed. The variable that determines if a wall can be destroyed is
set based on this. Along with the other variables between $00F0 and
$00F7, this gets reset when changing rooms obviously.
$00F8 Controller Press Previous (R)
Supposed to hold previous
inputs for detecting when buttons are released. Instead, just treated as
a duplicate used when pausing or unpausing the game.
$00FA Controller Hold Previous (R)
Same as $002A, but used to set $0028.
$00FC(U:$006F) PPU Scroll Value (Word)
Value written to PPUSCROLL register.
$00FE PPUMASK Value
Value written to PPUMASK register. Handles color emphasis, edge clipping, and layer visibility. See NESdev.org for more info.
$00FF PPUCTRL Value
Value written to PPUCTRL register. Handles some of the stuff you shouldn't mess with. See NESdev.org for more info.
$0300 Tilemap (R) (C)
The TSA map is dumped here. The tile map is generated on the fly. The
TSA map for the current stage is loaded into the PRG-ROM bank and
evaluated on a tile-by-tile basis. The tile values correspond to their
relative offset in the PPU. Tiles of value #$C0 or higher fall under the
"universal interactive" category (in other words, spikes). This range
of tiles is actually reserved for common sprites, but stationary spikes
are included in this subset. Below is a list of value ranges used to
determine the tile map.
$DC - $DD Ceiling Spike
$DE - $DF Floor Spike
$E4 - $E5 Trap Door (counts as empty)
$0347 Palette (R) (C)
The background palette is stored here.
$0400 Image single (R)
Just making up names here. This is the actual frame displayed,
whereas the image index is the offset for the address this points to. So
sprite displayed is offset by image_single+image_index.
$0418 Subweapon Icon Image Single
Subweapon icon image.
$0419 Multiplier Icon Image_single
Subweapon multiplier image.
$041A (Word)
Unknown. Gets cleared when $002C==#$08 or #$1B.
$041C Y-coordinate (R)
$042F Whip Y-coordinate
This gets increased by 8 when ducking (but not jumping, since Trevor stands when jump-attacking).
$0434 Y-coordinate of subweapon icon
$0436 Y-coordinate of Breakable Blocks (W)
$0438 X-coordinate (R)
$0438 X-coordinate of Breakable Blocks (W)
$0450 X-coordinate of subweapon icon
$0454 Palette Offset (R)
Not the actual palette. Used for palette cycling.
$046C
$0470 Attributes (R)
Care when editing these. For Trevor, $00 is visible and $80 is
invisible. Collecting a whip powerup will force this to #$00 so Trevor
is visible during the shimmer effect. For luminaries, this seems to just
be the type. For enemies, this determines if the enemy will move or
stand still, be visible, or even interact with Trevor at all. Each bit
serves a specific function:
$HGFEDCBA
A Destroyed
B Frozen
C Unknown
D Forces byte H
E Illusion
F Unanimated
G Moving
H Invisible
Clearing the lower nybble seems to have no effect for enemies. The
lowest bit of the lower nybble should never be set (i.e., no odd
numbered values), as this will instantly destroy the enemy. The lowest
bit of the upper nybble will render the enemy illusionary -- Trevor will
not be able to attack it, nor will it be able to attack Trevor. For
some reason, enemies like Sword Skeletons set bit F when attacking,
probably some unknown mechanic that made it necessary. Not sure what bit
C is for. It's set for most enemies. It's cleared for Medusa Heads.
$048B
Unknown
$048C Sprite Page (R)
This determines which row in the PPU to read. Probably shouldn't mess with this.
$04A5
$04A8 X-scale (R)
Sprites face left in the ROM, so a value of 0 flips the sprite so it
faces right. This is unnoticeable with torches and lanterns, but does
have a slight effect on candles.
$04C0
$04C4 Fractional X-coordinate (R)
When integral speed is added to the X-coordinate, the X-coordinate
increases, but not when a fractional speed is added (e.g., X=0;
X+1/4=0). So what happens to that fractional speed? It gets added to
this instead, which rolls over to the X-coordinate when the carry bit is
set..
$04DB Fractional Y-coordinate (R)
$04F2 Integral horizontal speed (R)
$0505
Part of a partner swap counter, it seems. Adds #$10 to the hit alarm when it reaches 8.
$0509 Fractional horizontal speed (R)
Any speed value less than 1 is stored here as an 8-bit floating point
(e.g., a speed of 1/4 will set this to #40, since #100 / 4=#40).
$051C
Denominator of partner swap counter (see $0505).
$0520 Integral vertical speed (R)
Very important. Not only does it move the instance, it is checked when climbing stairs.
$0537 Fractional vertical speed (R)
$054E Object index (R)
This is the same as $0048 for spirit partners. Always keep luminary
values between #$80 and #$82; all inappropriate values pose a risk of
actually crashing the game. This variable determines object-specific
data, such as which sprite to use.
$0561 Whip level
This is the number of whip upgrades collected.
$0565 Action state(R)
Don't mess with it, as it more than likely freeze up on you. Very
important variable, at least for Trevor. It can have the following
values:
#02 Stand Idle
#04 Walking
#06 Jump Start (unique for Grant)
#08 Jumping (unique for Grant)
#0A Ducking (unique for Grant)
#0C Falling (unique for Grant)
#0E Walk To Stair
#10 Start Climb
#12 Stair Idle
#14 Stair Climb
#16 Stair Transition
#18 Stand Attack (ignored by Grant & Alucard)
#1A Jump Attack (ignored by Grant & Alucard)
#1C Duck Attack (unique for Grant & Alucard)
#1E Stair Attack (ignored by Grant & Alucard)
#20 Stand Item (Attack for Grant & Alucard)
#22 Jump Item (Attack for Grant & Alucard)
#24 Stair Item (Attack for Grant & Alucard)
#26 Knocked Back
#28 Petrified
#2A (same as $28)
#2C Pitfalling
#2E Dead Animation
#30 Alucard Henshin! / Grant Wall
#32 Alucard POOMF! / Grant Ceiling
#34 Alucard Bat / Grant Reach Ceiling-to-Wall
#36 Alucard Bat Crash / Grant Hug Ceiling-to-Wall
#38 Alucard Bat Bump / Grant Reach Wall-to-Ceiling
#3A Alucard Ascent / Grant Hug Wall-to-Ceiling
#3C Alucard Revert / Grant Reach Wall-to-Floor
#3E Alucard POP! / Grant Hug Wall-to-Floor
#40 Alucard River Push / Grant Reach Floor-to-Wall
#42 Alucard River Drown / Grant Hug Floor-to-Wall
#44 Grant Wall Hop
#46 Grant Wall Attack
#48 Grant Ceiling Attack
#80
. Any above value ORed with #$80
. will freeze Trevor in that action
.
Values $06 and $10 are logical. When he jumps, Trevor doesn't apply
horizontal movement on the first frame, so it's not until $08 that he
starts moving. When transitioning from the ground to stairs, certain
variables are set, so this transitional phase is handled by $10. Value
$0C is only applied when falling off a ledge. For jumps, it's always $08
(or $1A for jump attacks or $22 for jump items).
For bosses, this determines their next action, usually.
$0578 (R) Weapon phase
Same role as $0565 for Trevor, this tells the weapon what to do. For
Trevor's whip and Sypha's staff, it just tells them to update their
coordinates.
$057C Image speed (R)
Counts down to 0 then changes image index upon rolling over.
$0593 Image index (R)
This one is most definitely close to image_index. When Trevor walks, it cycles {0,2,4,6}.
$05AA Sprite offset? (R)
No clue what this is for, but it is set at the same time the PPU
address is set for instances, so clearly it's related to sprites. Since
it's usually greater than 8, I think it's a sprite definition offset.
$05C1 (R) Fall switch / Enemy State
Although this is a range, this specific use applies only to Trevor.
When he falls, this is set to 1. This is so the game knows whether to
increase the jump offset or decrease it (see below).
For enemies and lanterns, this is the current state (cf. finite state machines).
$05D4 PC State Previous
Holds Trevor's action state temporarily when hit or his x when changing partners.
$05D8 PC Jump State/ Fall height
For PC, this
is set to 9 when jumping and increased each step, then stored in the Y
register. This is used to retrieve the integral vertical speed. When the
value retrieved for speed is $81, it decreases and the integral speed
is inverted. When the value retrieved is $80, the speed is set to $06
and this address no longer changes. Upon landing, if this is $00 (in
other words, integral speed is $06), the rough landing flag is set.
For Grant, this gets set to #1E at the start of a jump and increases by $61D every step. Once it equals #39, Grant abruptly falls.
$05D9 Item drop (R)
This is where candle drops are stored. Any value below $83 seems to
crash the game, while values over $A0 cause graphical glitches. Here are
the known useable values:
$83 Axe
$84 Cross
$85 Dagger
$86 Holy Water
$87 Stopwatch
$88 Sypha's fire
$89 Sypha's ice
$8A Sypha's water
$8B Grant's Dagger
$8C Grant's Axe
$8D Upgrade 1 (upgrades whip to 1)
$8E Upgrade 2 (upgrades whip to 2)
$8F Nothing? (looks like whip upgrade but no effect)
$90 Mystery Meat
$91 Invincibility Potion (duration = $B4)
$92 Rosary
$93 100 Point Bag
$94 200 Point Bag
$95 400 Point Bag
$96 700 Point Bag
$97 1 k Point Bag
$98 2 k Point Bag
$99 4 k Point Bag
$9A 7 k Point Bag
$9B 10G Point Bag
$9C 1UP
$9D Big Heart
$9E Small Heart
$9F 2x Multiplier
$A0 3x Multiplier
$A1 Fake Candle (can be useful glitch)
$05D9 Boss timer (R)
This is set when a boss appears and then counts down before starting the boss battle.
$05EB Hit alarm
Set to #$20 when Trevor is hit. Reverts to previous action when zeroed. Used as numerator in partner swap counter (see $0505).
$05EF Various / Enemy Class
Somehow affects room transitions for Trevor. On stairs, this is the
direction he's facing, basically. Additionally, this is Grant's factional jump frame and is incremented by $606 when jumping. Since $606 is always 0, this is never used.
For enemies, this is actually a
pointer to the set of routines and subroutines to call every step, vis a
vis $5C1,X. See notes about Enemy State Addressing.
$0602 Hit On Stairs
Cleared when PC is hit on ground, set when hit on stairs. Stuns PC until $5EB counts down.
$0606 Alarm / Starting Y (R)
Some enemies use this as an alarm, others use it to store the starting x or y coordinate for use in sinusoidal movement.
$0619 Ally Swap Y-Offset
This is used for the IRQ pass in conjunction with $0630. Don't sweat it.
$061A Subweapon Damage (R)
Similar to $0630, but used by subweapons.
$061D Stair Timer / Grant Jump Counter
This is the alarm that determines how long Trevor climbs stairs until the next input check. When Grant jumps, this is set to #01 while A is held, otherwise it is #02.
$061E Enemy timer (R)
This one is almost always a dedicated timer for enemies.
$0630 Whip damage
How much damage the whip deals, shifted 4 bits.
$0630 Ally Swap IRQ Toggle
When swapping allies, this toggles and is subtracted from $0619 to create the ally swap effect.
$0633 Counter (R)
For enemies like crows, this counts how many dives they attempt.
$0645 Spawn ID (R)
For candles, this is the object index. For enemies, this is the spawner ID that spawned it.
For Death, this is his appearance timer (instead of using $7F3).
$0657 Damage (R)
This determines the level of damage, (light, normal, heavy) an enemy
or projectile deals. Hard Mode and any stage from the Abandoned Mines
upward will typically increase damage. Note that $0657 itself belongs to
the $0645 set, as it was seemingly not needed for anything by PCs.
$0669 Damage received (R)
This is how much damage an enemy has taken from Trevor. Note that
$0669 itself belongs to the $0657 set, as it was seemingly not needed
for anything by PCs.
$067C Health (R)
This is duplicated for bosses. All enemies have health, even
"indestructible" ones. A blood skeleton can be slain if enough damage
is dealt to it, but time will probably run out before it dies.
$068D Weapon Hit Tracker (R)
Bitmasked for enemies when hit by the whip or subweapons. Ensures
no weapon will hit the enemy twice in too short of a time span.
$06A0 Audio Stuff? (R)
I think this section is all audio stuff.
$06DC BGM Paused
Set when stopwatch activated. Prevents music from playing.
$06DD Boss Scream Cutoff
Counts up to #F0, then stops boss scream DPCM.
$06E0 Tile map (Word) (R)
The tile collision map is read by columns, starting with the leftmost
column on the screen. Only the top-left tile of each metatile is read.
It is refreshed so only the tiles in the vicinity are registered. The
map is very simplistic. One Word is equal to one TSA. Each Word reads
left-to-right, top-to-bottom in the TSA. The address is retrieved for
y+16 (less 32 to account for the status bar). Words can be visualized
like so:
$ABCD
corresponds to
AB
CD
The following values exist:
$0 Empty
$1 Mud
$2 Conveyor right
$3 Conveyor left
$4 Crumbling ledge
$5 Ceiling Spike
$6 Solid
$7 Floor Spike
$C Crumbling (increments to $F)
Each of these are treated as solids when approached from the side.
Because of this, you cannot have mud on top of a conveyor and expect the
conveyor to pull you through it, but it will pull you out if you sink
to the conveyor at just the right spot. ("Pull out, Peggy! You hit an
artery!") The conveyor flags apply to the blocks under the water, not
the water itself (which is $0). Because of this, you cannot have water
and crumbling ledges at the same time (as nifty as that would be).
That's not to say you couldn't program such a gimmick, but it's just not
already incorporated. You can put mud over a collapsing ledge, if you
wanted to for whatever reason. Trevor would have to jump out of the mud
to avoid from falling. Crumbling ledges do not automatically disappear
and falling ledges do not automatically fall. These addresses only
affect the collision states. The spikes are essentially the same. I
don't know why they have two separate values, as touching either of them
from any direction will kill you. Any of the other solids can be placed
over these, although only mud or crumbling blocks would make sense over
a spike.
$0770 Solids definition (R)If a room has special tiles (such as
conveyors or mud), their offsets are stored here. Byte $0776 and $0777
are always #$80. Bytes $077E and $077F are always #$C0. Bytes $0770 thru
$0775 define the lower region of tiles whose PPU offset is less than
$80, while $0778 thru $077D define the lower region of the specific
tiles whose PPU offset is less than $C0. Six instances of a PPU offset
translate to a tile map value of #$6. In other words, if you wanted
three of the tiles to be mud, you'd specify the range of tiles to be
treated as mud in byte $0778. If you wanted no tiles from a specific
half of the PPU, you'd set all corresponding bytes to that max offset.
$0780 Background Animation Frame
Bone Dragon King uses this; don't remember seeing it used outside of
him. There are 3 frames of animation for CHR 5 in the clocktower and
sunken city.
$0781 Background Animation Speed
Bone Dragon King uses this; don't remember seeing it used outside of
him. This is how frequently the CHR 5 (half the background tiles) change
during his fight.
$0782 Epilogue partner
Needs to be set before picking up Dracula's crystal, I think. This is
the value read for determining offsets used in the epilogue. This is
the same as $0048.
$0783 Epilogue State
Castle falls at #00. Trevor's cape flutters at #01. Screen fades and text scrolls at #02.
$0784 Castle Speed
Reverses castle horizontal speed every 2 steps as it crumbles.
$0785 Epilogue text offset
This increases with each pass of the epilogue timer. It is used to
retrieve the index for the next line of text or the end of the epilogue.
$0788 Epilogue Scroll Timer
Counts up to #06 if Trevor is solo, otherwise up to #05, then resets. Shifts the PPU scroll to create the text crawl effect.
$078A Epilogue timer
This is how fast the next chunk of text is loaded into the PPU during
the epilogue. Messing with the scroll speed means you may need to edit
this as well (see ROM Addresses). Also controls time until screen fades after castle falls.
$078B Password Errors
Bitmasked errors during password decryption.
$0790 Interactive terrain index (R)
Seems to hold $0069 for gears and crushers. Maybe other objects as well.
$0790 Path at fork (R)
When the road forks, four variables are set. They determine the sprites to use for the path marker on the mini-map.
$0790 Password Icon Value (R)
On the password entry screen, each cell has a value of 3n and each
icon has a value of 1, 2 or 3. The icon value is added to the cell
value, which is essentially the password.
$079D Byte 8 (R)
I don't know what else to call this. It holds byte 8 of breakable
blocks (see ROM Addresses). Since this is written when X is #$1A or #$1B
(and X cuts off at #$1C), only two values are found here and $7B8.
$07A1 Terrain screen
Seems to be a tracker for the current screen value relative to the first interactive terrain object.
$07A2 Active terrain
This is the number of interactive terrain objects (gears, crushers,
breakable blocks, dripping water, etc.) in the view (horizontal stage)
or the accessible room (vertical stages).
$07A7 Breakable Block Status (W)
Set to #$80 when the block is destroyed.
$07B1 Dual-layer wall (R)
For breakable walls, this is set when the wall occupies two tiles.
$07B3 Broken bottom tile (R)
For breakable walls, this is set to $FF when the lower wall is broken.
$07B5 Broken top wall (R)
For breakable walls, this is set to $FF when the top wall is broken.
$07B7 Wall ID (R)
This is the ID of the wall in the room, used to offset from $00F4.
$07C2 Spawner offset (R)
This stores the ID of the enemy spawners. This is the same value as
seen in reVamp. The value is shifted left 1 bit to retrieve the address
indexed at $028533.
$07C8 Spawner count (R)
This gets incremented each time an enemy is spawned. This gets
retrieved, shifted left, then used to offset the values retrieved by
$7C2,X. For enemies that can only spawn once, this gets set to 1 when
the enemy can be spawned and then cleared when once the enemy is
spawned.
$07CE Spawn timer (R)
So far only used by non-flickering Ghost to delay spawning.
$07D4 Spawn y-coordinate (R)
This is the y-coordinate in the target screen for the spawner. This
is where the enemy will be generated whenever the spawner code is
called. Note that Spiders are hardcoded to start from #$30.
$07DA Spawn x-coordinate (R)
This is the x-coordinate in the target screen for the spawner. This
is where the enemy will be generated whenever the spawner's code is
called.
$07E0 Off-screen status (R)
This is cleared as long as the spawner is on the screen. As soon as
the spawner leaves the screen (via x+screen_shift) this gets set,
preventing its code from running.
$07E6 Spawned X register (R) / Instance value 1 (R)
This stores the X register value used when an enemy was successfully
spawned. When a spawner is first loaded, the first of any special
instance-specific value is loaded here.
$07EC Bone Dragon King Frames (R)
Range is through $07F1. The head is always 1. Each of the 11 ribs has its image_index stored here, 1 nybble per rib.
$07EC Various
This increments slowly for Bone Dragons. It's used to handle movement.
For the Giant Bat, this stores the instance ID of the main bat.
For Big D, this is his form (0=sorceror, 1=floating heads, 2=Pazuzu).
$07ED Various
This controls how many blocks comprise a collapsing bridge.
When a Bone Dragon is present, this controls the xscale of the ribs and fireball.
For the Giant Bat, this is the current state of all bats (not sure why it uses an extra byte).
$07EE Bone Dragon alarm
For Bone Dragons this counts down from $30. When it reaches 0, the
Bone Dragon opens its mouth and this is set to $10. After the Bone
Dragon spits, it's set to $80 and the Bone Dragon closes its mouth.
For Death, this is the number of scythes he can throw.
$07EF Bone Dragon spit
When the Bone Dragon finally attacks, this is set to #$01 briefly and the fireball is generated on the next pass.
$07F3 Boss State
Bit confusing to call it this. It's set to #$80 when the boss first
spawns and while set to this it waits until Trevor is in range of the
boss, at which point it is set to #$00 when the boss is active. When the
boss dies and goes through its death sequence, it's set to #$01. When
the boss is finally destroyed and the soul shard is supposed to drop,
this gets set to #$02. When the soul shard finally appears, it's set to
#$03.
$07F6 Hard mode
This is set when you play in hard mode. It's also used in password generation.
$07F8 Name (R)
Bytes $07F8 thru $07FF store each letter of the name, with A=$50, B=$51, and so on.
Some ROM addresses!
Scene Tiling
For stages, tile square assemblies are used (see below), but for
simple aesthetics, tiles are placed individually row by row. There are
shortcuts, however. When a tile is put in the room repeated 4 or more
times, two values are used. The first value specifies how many tiles to
draw, while the second value specifies which tile to draw. Afterwards,
either another repeating tile is written or the next address in the PPU
is set to a value greater than $80. The difference between that value
and $80 is how many tiles will be specified individually, beyond which
point the next value is a repeated tile. So for example:
05 DE 81 B1 10 00 04 DE 82 8A 8B
In the above example, first tile $DE will be drawn in the room 5
times. After the fifth one is drawn, tile $B1 will be drawn next. After
that, tile $00 will be drawn 16 times. Then tile $DE will be drawn 4
more times. Finally, tiles 8A and 8B will be drawn. Below are some scene
addresses tiled as such. Below each scene's tile map is the CHR data as
well. Loading $7F into a CHR select will result in a blank tile set.
$033651 Intro Castle
$00646F CHR 4
$006473 CHR 5
$006477 CHR 6
$00647B CHR 7
CHR 0 thru CHR 3 are set to #$00 and #$01.
$00B552 Title Screen
$006BC7 CHR 3 (path markers make up the lightning here)
$00647F CHR 4
$006480 CHR 5
$006481 CHR 6
$006482 CHR 7
$00B552 Main Menu (yes, it's the same as Title Screen)
$03E3BE CHR 0
$03E3C2 CHR 1 (CHR_0+1, can't be changed easily)
$03E3C6 CHR 4
$03E3CA CHR 5
$03E3CE CHR 6
$03E3D2 CHR 7
$007BE3 Name Entry
$007414 CHR 0
$007415 CHR 1
$007416 CHR 2
$007417 CHR 3
$007418 CHR 4
$007419 CHR 5
$00741A CHR 6
$00741B CHR 7
$033302 Password
Same CHR set as Name Entry
$00668D Prayer
$006547 CHR 0
$00654B CHR 1
$00654F CHR 2
$006553 CHR 3
$006557 CHR 4
$00655B CHR 5
$00655F CHR 6
$006563 CHR 7
$02BDFB Epilogue
$02B02B CHR 0
$02B02F CHR 1
$02B033 CHR 2
$02B037 CHR 3
$02B03B CHR 4
$02B03F CHR 5
$02B043 CHR 6
$02B047 CHR 7
Text
The prologue handles a little differently than the title screen. The
border around the title screen is drawn. Each line of text is preceded
by $80 for a line break and then the x-position of the first tile in
that line, with $00 meaning no text. So $80080F would mean a new line is
started and tile $0F would be drawn at x:#$08 of that line. At the end
of the code, $8100 denotes a page break. Below each text address is the
CHR 4 thru CHR 7 range loaded at the time. Diacritics (in Akumajou
Densetsu) are drawn on the previous line, but written immediately after
the character they follow.
$03DC1D Prologue Paragraph 1
$006483 thru $006486
$03DD12 Prologue Paragraph 2
$006487 thru $00648A
$03DDEC Prologue Paragraph 3
$00648B thru $00648E (Whip)
$03DEA0 Prologue Paragraph 4
$00648F thru $006492 (Bust of Trevor)
$03DF65 Prologue Paragraph 5 (lots of line breaks here)
$006493 thru $006496 (Trevor overlooking Castlevania)
$006497 thru $00649A (font)
The final 12 bytes of CHR addressing are the same as $006493 thru $006496.
The epilogue text is handled quite differently. This time diacritics
are handled on separate lines. Each line is assigned an absolute PPU
address (written in reverse byte order). When the end of a line is
reached, the value $FE is written. When a pause between lines is needed,
$FF is written (I think). The same CHR values are used throughout the
epilogue.
$02B203 CHR 0
$02B207 CHR 1
$02B20B CHR 2
$02B20F CHR 3
$02B213 CHR 4
$02B217 CHR 5
$02B21B CHR 6
$02B21F CHR 7
$02B95E Epilogue Part 1 (generic)
$02BC2D Epilogue Part 3 (Grant)
TSA Definitions
TSAs are stored in the ROM left to right, top to bottom, row by row.
Solid black is routinely PPU address $00 while solid "sky" is $3D. Each
TSA section is preceded by the tile maps for each stage (see below).
$0182D0 - $187CF Catacombs
$018EDB - $19EDA Abandoned Mines
$01A2FB - $1A8FA Main Hall
$01AC9A - $1B269 Inner Hall
$01B592 - $1BD51 Castle Keep
$01C459 - $1C9B8 Causeway
$01CD94 - $1D143 Swamp
$01D78E - $1DD8D Alucard's Cave
$01E3BE - $1E9BD Sunken City
$01EDFA - $1F3C9 Lookout Tower
??????
$020451 - $20D60 Wallachia
$02141A - $21965 Clocktower
$022120 - $2263F Forest
$022C7D - $232BC Ghost Ship
$023737 - $23BB6 Tower
TSA Maps
Each tile map is preceded by a byte specifying the size of the room.
So $200BA is set to #$02 since the first room takes up 2 extra screens,
$2014B is set to #$00 since the next area doesn't take up any extra
screens. One vertical screen is 64 bytes. One horizontal screen is 48
bytes. Tile maps read left to right, top to bottom, row by row. Stage 1
is expanded here as an example. For all other addresses, refer to
reVamp's room editor (it shows the ROM address for each TSA in the room.
Rooms, like stages, are not mapped out in order.
$020039 Stage 1-02:1 size
$02003A - $200B9 Stage 1-02:1
$0200BA Stage 1-01:0 size
$0200BB - $2014A Stage 1-01:0
$02014B Stage 1-02:0 size
$02014C - $2017B Stage 1-02:0
$02017C Stage 1-02:2 size
$02017D - $201DC Stage 1-02:2
$0201DD Stage 1-02:3 size
$0201DE - $2026D Stage 1-02:3
$02026E Stage 1-03:0 size
$02026F - $202FE Stage 1-03:0
$0202FF Stage 1-03:1 size
$020300 - $2038F Stage 1-03:1
$020390 Stage 1-04:0
$020391 - $20450 Stage 1-04:0
$01802B Catacombs
$01886D Abandoned Mines
$019FF6 Main Hall
Stage CHRs
The CHR for each stage is pretty easy to find in the ROM, as they're
in order according to stage, block, and room. CHR 0 is always set to
#$00 and CHR 1 is simply CHR_0+1, although these values can be easily
modified. CHR 4 is always set to #$40 and CHR 7 is always set to #$43,
but these can be modified too.
$00001F CHR 0
$00001B CHR 1 offset from CHR 0
$000F4F CHR 4
$000F53 CHR 7
$000275 CHR 2 | CHR 3
$00018C CHR 5 | CHR 6
The values of CHR 5 and CHR 6 are indexed as Words, with the upper
byte used for CHR 6. The range from $00018C to $000274 should include
CHR 5/CHR 6 pairs for all levels in order, but this hasn't been tested.
Likewise with CHR 2/CHR 3.
Solids Definitions
Solid tiles are pretty easy to define, but care is needed since the
number of bytes required to define the solids for each room is dependent
on the variety of solids present in that room.
$0003D2 PRG-ROM Index
$00042C Definitions
All the definitions are within the $00:84XX block of the PRG-RAM (by
default). All the definitions follow a simple format of one WORD per
solid. The lower byte specifies how many times the solid type,
determined by the upper byte, should occur in the Solids Definition in
the RAM. It then moves onto the next WORD. The sum of the lower bytes
cannot exceed 8. The game will increment the X register by 1 every time a
value is written to the RAM, which it then ANDs with #$07, branching to
the second set of solids definitions and repeating the process. Special
consideration is given to Pazuzu's lower tile group ($33), a Konami
logo ($36) and a font ($37). The latter two apparently go together, but I
don't know when they're actually used -- possibly the ending credits.
The standard font is $41 and Konami logo is $72.
Unfortunately, the solid definitions for each CHR set are hard-coded, requiring them to always be laoded into CHR Bank 5 EOR CHR bank 6, but never interchangeably. For example, CHR #45 (bank 5 in the first stage) has solids defined at #7e, so if bank 6 is set to #45, all tiles from bank 6 will be solid, since they are all greater than #7E. Similarly, setting bank 5 to #47, which defines solids at #B7, will result in all tiles from bank 5 to be not solid.
The number of actual solid definitions is limited by default, so you should try to restrict yourself to following any of these. Background CHRs start at #44, so these are indexed by
$0003D2 + (CHR_ID - #44) in the CV3j ROM.
$841C #0880 Bank 5, no solids
$841E #06BE02C0 Bank 6, #BE+ solid
$8422 #067E0280 Bank 5, #7E+ solid
$8426 #06B702C0 Bank 6, #B7+ solid
$842A #06B902C0 Bank 6, #B9+ solid
$842E #06B802C0 Bank 6, #B8+ solid
$8432 #04B802B902C0 Bank 6, #B8 collapses, #B9+ solid
$8438 #067A0280 Bank 5, #7A+ solid
$843C #067C0280 Bank 5, #7C+ solid
$8440 #02B601B703B802C0 Bank 6, #B6 right, #B7 left, #B8+ solid
$8448 #06730280 Bank 5, #73+ solid
$844C #01B405B702C0 Bank 6, #B4-#B5 mud, #B7+ solid
$8452 #04B202B402C0 Bank 6, #B2-#B3 collapses, #B4+ solid
$8458 #04BA02BB02C0 Bank 6, #BA collapses, #BB+ solid
$845E #06A002C0 Bank 6, #A0+ solid
$8462 #06B802C0 Bank 6, #B8+ solid same as $842E
$8466 #067C0280 Bank 5, #7C+ solid same as $843C
$846A #04B802B902C0 Bank 6, #B8 collapses, #B9+ solid same as $8432
$8470 #04B202B302C0 Bank 6, #B2 collapses, #B3+ solid
$8476 #04B702B802C0 Bank 6, #B7 collapses, #B8+ solid
$847C #06B802C0 Bank 6, #B8+ solid same as $842E
As you can see, there are no Bank 6 sets with no solids. However, since there are duplicates, you could replace any of them with #08C0. Also, $8452 is an oddball in that it seemingly defines 2 tiles as collapsible, but the second tile is actually blank, as can be seen in set #5D. Similarly, $845E has an expansive set of solid tiles, but 45% of them are blank.
Palette sets
Most palette entries only hold 3 values and the game loads these
three into the placeholders. Editing the background palette placeholder
in reVamp automatically affects all other background colors, but by
editing the ROM data directly, it's possible to set 4 different
background colors. The palette sets for background colors contain 3
palettes per group. Intro, prologue, name, and password screens have
their own separate palettes.
For the preset sprite palettes, sets 1 and 2 are listed first
(Trevor and relics), then #$0F0F0F0F as a placeholder for the
room-specific sets, then set 4 (candles and hearts). Only the three
colors unique to sprite palette entry 3 for each room is stored.
Trevor's first color is the default background color apparently.
After tweaking with background palettes in reVamp and searching for
changes in the ROM, Trevor's palette was the only thing that changed.
That really sucks.
$4BE Palette script
This is the start of the palette loading code.
$00066F Palette set used (R)
This is actually stored in order, addressed incrementally when
rooms changed. The order of palette sets used will be sequential
regardless of room index. Clocktower decrements when climbing back down,
even though reVamp treats them as separate rooms.
$6DE Status bar
This is quadruplicated as a placeholder. Of course the first value is the status bar's palette always.
$0006EF - $0006F5 Set #$00
thru
$000869 - $000871 Set #$2A
$0008EF Trevor
$0008F3 Relics & skeletons
$0008F7 Room palette placeholder
$0008FB Hearts and candles
$000900 Grant
$000926 Set #$00
thru
$000983 Set #$18
Background Music
The track order can be changed in reVamp easily enough, but known
values will get posted here. NSF values will not be here... probably.
Values get all wonky after using reVamp's Change Tracks function. Will
need to test byte values later. Changing more than one or two at a time
causes some weird readings in ExamDiff. These pairs are a map, not the
music itself.
$031010 Wallachia
$031012 Black Forest
$031014 Causeway
$031016 Marsh
$031018 Clock Tower
$03101A Sunken City
$03101C Tower of Terror
$03101E Ship of Fools (2 bytes?)
$031020 Alucard's Cave
$AD92 Beginning
$FD92 Clockwork
$C192 Mad Forest
$D592 Stream
$E992 Dead Beat
$1193 Aquarius
$2593 Rising
$3993 Anxiety
$4D93 Nightmare
Damage Amounts
This is how much damage enemies and traps do. Damage values start at #$00 for 1 damage.
$0382E8 Weak attacks (Trevor)
$0382E9 Normal attacks (Trevor)
$0382EA Strong attacks (Trevor)
$0382EE Dull spikes (Trevor)
$0382F6 Sharp spikes (Trevor)
$0382F8 Weak attacks (partners)
$0382F9 Normal attacks (partners)
$0382FA Strong attacks (partners)
$0382FE Dull spikes (partners)
$038306 Sharp spikes (partners)
Damage levels for each enemy are stored in a list. The specific
damage level is retrieved by subtracting #$38 from the object_index and
then adding #$20 to that value if either Hard Mode is set or the current
stage is #$0A (Abandoned Mines) or greater.
$02E03D Damage (R)
Enemy Spawner
The program will check each enemy instance's object_index
($054E,X). When a match
with the designated object_index is found, local var $000E is
incremented. Upon
verifying each object_index, it compares $000E against the value stored
in $000D (which is hardcoded as #$03). If $000E<$000D, it looks for
an available spot in the object_index list within a(n optionally)
specified range. The program then clears all the
preset RAM offsets within the current X register's range. Next, it sets
the coordinates, object_index, and behavior. It
then retrieves the enemy's health and damage.
$028DD2(U:$028E65) Multiple Object Limit
$028533(U:$028537) Address Map for Spawner Behaviors
Each step the game will retrieve the spawner's offset value and
retrieves an address. It then takes the spawn count and uses it to
retrieve another address offset from the first address then executes
the code there. If no enemy exists, this usually jumps to $8B90,
otherwise it goes to $8BB0, I think. Script $8BB0 simply updates the position of the spawner.
For the most part, spawner data can be edited in reVamp by
right-clicking on a spawner in the room. The first three values can be edited in reVamp, but the
fourth
and fifth values are special values for enemies or spawners that need
them (e.g., Ghosts, Fuzzbusters, Harpies, Bone Dragons, Bone Pillars,
Crows); the fourth byte is for RAM offset $07E6 and the fifth byte is
for RAM offset $07CE. Neither value can be edited in reVamp. In
GameMaker terms, these two bytes are equivalent to assigning instance
creation code to each spawner.
$02A13E(U:$02A217) Spawner Types Data
Byte 0: Spawner ID
Byte 1: Spawner x mod 64
Byte 2: Spawner y
Byte 3: Value for $07E6,X
Byte 4: Value for $07CE,X
Here
is a list of each spawner ID and its corresponding enemy object.
Interactive terrain (e.g. trap) spawners were eventually replaced with
separate terrain code, but their spawner data still exists, albeit with
buggy behavior. A couple alternate enemies (e.g., bat, fuzz buster) also
had their spawners diverted, but I haven't delved deeply enough to
determine if their code was actually deleted, but most of the LUTs
remain, such that setting the spawner state to #02, thus reveals each
spawner's intended application.
#00 Unused address used by diverted spawners
#01 Zombie (slow rate) normal zombie
#02 Zombie (from ground)
#03 Medusa Head (one)
#04 Winged Demon (one)
#05 Skull Knight (Boss)
#06 Cyclops (Boss)
#07 Grant (Boss)
#08 Lightning (bolts) first visual effect
#09 Lightning (Sypha) cinematic visual effect
#0A Lightning (clouds) boss fight visual effect
#0B Hunchback
#0C Bats (flying)[1]
#0D Bats (asleep)
#0E Bone Pillar (3-shot)[1]
#0F Axe Knight
#10 Crow (flying low)
#11 Skeleton (sword)[1]
#12 Fishmen (jumping) bridge variety
#13 Skeleton (whip,red)
#14 Floating Eye
#15 Slime Ball
#16 Skeleton (bones)
#17 Skeleton (whip,blue)
#18 Ghost (no flicker)
#19 Skeleton (red)
#1A Mummies[1]
#1B Giant Bat (Boss)
#1C Alucard (Boss)
#1D Bone Dragon King (Boss)
#1E Medusa (Boss)
#1F Water Dragons (Boss)
#20 Mummies + Cyclops (Boss)
#21 Frankenstein's Monster (Boss)
#22 UNUSED BOSS (no object, spawner position #2A)
#23 Grim Reaper (Boss)
#24 Doppelganger (Boss)
#25 Mummy + Cyclops + Leviathan (Boss)
#26 Dracula (Boss)
#27 Crow (flying high)
#28 Headless Pirate a.k.a. Dhuron
#29 Spider
#2A Bone Pillar (2-shot)
#2B Bats (flying)[2] *unused in CV3j*
#2C Fuzz Buster[1] *unused in CV3j*
#2D Spore (floating)
#2E Spore
#2F Trap Door *unused*
#30 Trap Door (spikes) *unused*
#31 Gear Tooth *unused*
#32 Skeleton (blue)
#33 Pendulum *unused*
#34 Falling Spikes[1] *unused*
#35 Falling Spikes[2] *unused*
#36 Water Current (4 blocks)
#37 Falling Blocks *unused*
#38 Acid Drops *unused*
#39 Water Current (2 blocks)[1]
#3A Zombie (fast rate)[1]
#3B Mudmen
#3C Frog
#3D Fishmen (swimming) aqueduct variety
#3E Fishmen (swim+jump) deep water variety
#3F Fire Man
#40 Knight
#41 Bone Pillar (3-shot)[2]
#42 Auto-Walk (forest)
#43 Bone Dragon
#44 Owl
#45 Fuzz Buster[2]
#46 Harpy
#47 Bats Flying[3]
#48 Medusa Heads (two)[1]
#49 Skeleton (sword)[2]
#4A Medusa Heads (two)[2]
#4B Ghost (w/flicker)
#4C Auto-Walk (caves)
#4D Water Current (6 blocks)
#4E Water Current (2 blocks)[2]
#4F Zombie (fast rate)[2]
#50 Mummies[2]
#51 Flood Controller
#52 Winged Demon (two) *CV3u only*
Enemies and luminaries are not directly placed in the room, but rather a spawner
is. Each spawner occupies a 64px
block (as seen in reVamp) and is the pointer to the subroutine that handles spawning the enemy. You
can access all available spawners by right-clicking the enemy or
luminary list in reVamp. There is a hard limit of spawners -- 224
enemies (U:226) and 244 luminaries (U:254) -- for the entire game,
although in actuality there are far fewer (see warning below). A
skilled, patient hacker should be able to add more.
WARNING: Some spawners point to the same address, identifiable in reVamp as spawners with the same object, x and y. Editing these in reVamp will glitch it out, as the program apparently is designed to only change ROM values, not add them.
All enemy spawners below #80 are reserved strictly for enemies
appearing across both playthroughs. By default enemy spawners #A0 and up
are reserved for the second playthrough, although you can alter this
hard limit by editing $02813A (U: $028130). There is no such
limitation for luminaries. With some skilled hacking, I'm sure you could reprogram the spawner data to allow 16bit spawner quantities. There can only be 6 spawners each loaded at any one time in a horizontal stage. Spawner
indices for each room are stored as WORDs, with the lower byte
corresponding to the luminary and the upper byte corresponding to the
enemy. The address maps are stored in order by stage-block-subroom.
$0292BE(U:$2938F) Address maps for enemies
$02A57B(U:$2A654) Address maps for luminaries
$0293DE First enemy (1-01:0 far left)
$0293DF First luminary (1-01:0 far left)
Luminary
stats are stored as DWORDs, with each byte representing (in order) the
luminary type, item dropped, x-coordinate, and y-coordinate (add #28 to
the y-coordinates listed in reVamp).
Luminaries are stored as follows:
$02A763(U:$02A850) Luminary #00 (Null)
$02AB2B(U:$02AC40) Luminary #$FF
#80(U:#90) Candle
#81(U:#91) Candlestand
#82(U:#92) Lantern
In vertical stages, enemies and luminaries are hard-coded
starting at . The first byte is the screen the enemy or luminary appears
in. The second byte is the y-coordinate in that screen. The third value
is the enemy as can be seen in reVamp or the luminary value. The fourth
byte is the x-coordinate in the screen. The fifth byte is the item
dropped for luminaries, possibly ignored for enemies even though it's
set (otherwise bats would drop big hearts). The sixth and seventh bytes
aren't used as far as I can tell. A value of $FF in the first byte
denotes the end of the list of spawners in that vertical room.
$02AB2F(U:$02AC44) Vertical stage Spawners
Trevor's Starting Coordinates
Trevor's X is (KKKKK & #$F0). Trevor's Y is (KKKKK << 4).
Every other byte corresponds to a block. These can be set in reVamp.
Good for debugging if $36 is frozen.
$037802 1-01:0
$037804 1-02:0
$037806 1-03:0
Scroll type
Vertical stages have a scroll type specified as #$80 (normal), #$82
(automatic), or #$84 (incremental, like the collapsing towers). When the
stage scrolls down, it can take #$81 (normal), #$83 (automatic), or
#$85 (incremental, not sure if implemented). You can set this for each
room, but if a room isn't set up as vertical, it will fail to load.
$03DA01 1-01:0 (starting address)
$03DA03 1-02:1
$03DA11 2-01:0 Up
$03DA15 2-02:1 Up
$03DA18 2-03:1 Up
$03DA1B 2-03:1 Down
$03DA1E 2-02:1 Down
$03DA20 2-01:0 Down
$03D961 - $3DA00 Scroll type map (and other?)
Prayer Values
These are some of the values set during the prayer scene.
$00658d Trevor's mirrored state
$006588 Trevor's x-coordinate
$00658D Trevor's y-coordinate
$00667F Trevor's PPU offset
$006684 Trevor's palette
$006689 Trevor's "image single"
Doors
Each door has 6 bytes of data. The location of this data is located in a series of WORDs offset by the current stage.
$01F606-$01F623 PRG-ROM Address (Word) (R)
$01F624 Door value (R)
Each of these addresses contains 6 bytes of data, offset by the current block.
Subroom
Screen
Side of room (#$00 = left, #$01 = right)
First door y-coordinate
Second door y-coordinate
Subroom of next block
The first byte tells the game which subroom in the block the door is
located. Obviously since there can be only one door per block, there can
be only one subroom. The second byte tells the game which screen the
door is on. The screen is the higher byte of the View X. The third byte
specifies which side of the room the door is on. You can only use one
side of the room, not both (CV2 had different screen transitions
obviously). The next two bytes specify the y-coordinate Trevor needs to
be standing at for either door when walking through. This is how you
have two doors per level in the Alucard's cave. And yes, Trevor must be
actually standing at that y-coordinate. The final byte tells the game
which subroom of the next block to go to.
Stairs
Stairs have quite a bit of code associated with them. The stairs'
stats are indexed by stage, block and room. This means adding new stairs
is tedious. The indexing is as follows:
$01F75F Index by stage (Word) (R)
$01F77D Sub-index by block (Word) (R)
$01F818 Sub-index by room (Word) (R)
$01F828 Stair values and breaks (R)
Breaks are included because the game will cycle through all stair
values if at any time Trevor's stats don't match those of the currently
indexed stair. When a break is encountered, the game stops looking.
Stair stats are stored in the format ABCCDD, with each nybble/byte
corresponding to the following (coordinates of the reVamp icons are
listed below each one):
A = Direction
$0 Up and right
x = left edge
y = bottom edge + #$10
$4 Up and left
x = right edge
y = bottom edge + #$10
$8 Down and left
x = right edge
y = top edge + #$10
$C Down and right
x = left edge
y = top edge + #$10
B = Y-coordinate >> 4 or Screen for vertical rooms
C = X-coordinate or Y-coordinate for vertical rooms
D = Screen or X-coordinate for horizontal rooms
Putting a solid tile directly under the top of a stair will prevent
Trevor from walking down the stair. Climbing stairs makes Trevor ignore
tile collisions normally, but if the tile directly below is a solid, the
collision will register and he will snap out of the stair to the ledge
above.
Special Values
A few stages have special attributes applied to background tiles and
are listed in the ROM. The map is very simple. Every DWORD stores (in
order of ascending byte) the stage, the block, and the room, then the
code for the instructions, which is stored in RAM address $007A.
$001595 Moving clouds (#$10 to #$13)
$0015A5 One-way stairs? (#$20 and #$23)
$0015AD Stream with waterfall (#$30 and #$31)
$0015B5 Grinding clockwork (#$40)
$0015E9 Cascading waterfall (#$41)
$0015F5 Quicksand (#$32 to #$37)
$00160D Water? (#$50 to #$53)
$00161D Split room with water? (#$60)
$001625 Flooding (#$71 and #$72)
$001629 Collapsing bridge (#$80 and #$81)
Obviously #$10 thru #$13 handle the values used to reference the
instructions for animating background clouds. The lower bits correspond
to RAM offsets.
A value of #$20, I have no idea yet.
A value of #$23 will make Trevor climb the stairs up to the next
block automatically. Like doors, this clears all other instances from
the screen.
When using Sypha, #$30 and #$31 handle water that she can freeze.
So no, she can't freeze all water, only the flowing streams with
waterfalls in them. These flags also animate the water.
Values #$40 and #$41 simply handle tile animations. The gears in
the Clocktower use #$40 and the waterfalls in the background of the
castle tower use #$41.
The quicksand flag will make every part of Trevor's sprite that is
completely under the mud flicker. The lower bits map to $00631A offset
by [lower bits]-2. So a value of #$33 would map to $00631B. The values
stored within that array correspond to the screen in which no flickering
should occur (i.e., the screen with no mud at all). If the
corresponding array value is higher than the room size (e.g., $0054
doesn't exceed #$01 but $00631A[n] is set to #$02), the game will render
all screens in the room as full of mud. When the array value matches
the current screen, no flickering will occur below ground-level at all.
Not sure if the water flag is actually for water, but seeing as how
it's only set in the Sunken City, that's a good sign. It has a similar
format to the quicksand flag, but is only used for the lowest subroom.
Perhaps Konami was planning something with it. Possibly only used by
Fishmen. Both #$50 and #$60 are checked in the same subroutine, which
suggests water. Furthermore, the code creates three new objects in the
room, but they never appear (at least on my end). This kind of code only
occurs when particles are created. In other words, there was probably
supposed to be a splash when Trevor hit the water.
Epilogue
Not really something you'll probably ever want or need to change, but
if you think it takes too long for the text to start scrolling in the
epilogue after the castle crumbles, you can change these values.
$02B224 Text Delay for Trevor
$02B225 Text Delay for Sypha
$02B226 Text Delay for Grant
$02B227 Text Delay for Alucard
$02B281 Scroll Speed for Trevor
$02B282 Scroll Speed for Sypha
$02B283 Scroll Speed for Grant
$02B284 Scroll Speed for Alucard
$02B25A Text Index
The index for the text is referenced by RAM address $0785 and
increases with every pass of $078A. When the value retrieved is $FF, the
text stops loading and scrolls to the top of the page. The three or
four indices before the $FF are typically void of text so the epilogue
scrolls off the screen before ending.
Respawn Conditions
Some enemies can be specified to respawn or perform other actions when slain.
$02F553 #$4B
$02F557 #$47
$02F55B #$49
$02F55F #$4D
$02F563 #$38
Some have different displays when slain.
$02F53B #$3D
$02F53F #$3E
$02F543 #$4B
$02F547 #$47
Collision Formula
The formula for horizontal collisions is
abs(ALLY.x-OTHER.x)<=(ALLY.hbound+OTHER.hbound)
The formula for upper vertical collisions is
ALLY.y+ALLY.vbound - (OTHER.y-OTHER.vbound)<#$12
Bounding Box Offsets
Using the object IDs below as Other pointers
$02F683 Ally Vertical
$02FB15 Ally Horizontal
$02FB90 Other Horizontal
$02FC18 Other Vertical
NPC Object IDs $0291FC (add $10 for CV3u)
$01 Skull Knight
$02 Cyclops
$03 Grant
$04 Giant Bat
$05 Alucard Bat / Cape
$06 Bone Dragon King
$07 The Monster
$08 Moat Dragon
$09 Triad Wisp / The Devil
$0A Medusa
$0B Doppelganger
$0C Death
$0D Dracula
$0E Mummy Boss
$0F Doppelganger Weapon
$12 Storm Clouds
$13 Small Lightning
$14 Grant's Knife
$15 Grant's Axe
$16 Soul Shard
$17 Alucard Fireball
$1A Boss Death Effect
$1F Cyclops' Killer Lightning?
$20 Timer for Cyclops' Lightning?
$21 Alucard Head
$23 Devil's Fireball
$24 Dracula's Pyre
$25 Dracula's Head
$27 Mummy Boss Wrap
$29 Legion's Drool / Pazuzu's Laser
$2A Water Splash
$2B Scythe
$30 Normal Fireball
$31 Axe
$32 Teardrop
$33 Bone
$34 Mummy Wrap
$35 Spiderling
$36 Sporeling
$37 Embers
$38 Zombie
$39 Crow
$3A Bats
$3B Medusa Head
$3C Ghost
$3D Skeleton
$3E Skeleton (Whip)
$3F Floating Eye
$40 Spore Float
$41 Winged Demon (U:$51)
$42 Flea Man
$43 Slime
$44 Skeleton (Sword)
$45 Axe Knight
$46 Bone Pillar
$47 Blood Skeleton (Whip)
$48 Merman
$49 Bone Dragon
$4A Mummy
$4B Blood Skeleton
$4C Dhuron
$4D Spider
$4E Spore Bloom
$4F Owl
$50 Mudman
$51 Frog
$52 Fireman
$53 Knight
$54 Fuzzy
$55 Harpy
$58 Spiderweb / Crumbling Mummy
$5C Ice Giblets
$5D Medusa Skull (CV3U only, $6D)
$61 6-Tile Current
$62 2-Tile Current(1)
$63 Auto-Walk (Caves)
$64 Auto-Walk (Forest)
$65 2-Tile Current (2)
$66 4-Tile current (U:Fast Zombie)
$68 Normal Lightning
$69 Collapsing Walkway
$6A Teeter-Totter
$6B Falling Block Spawner
$6C Water Droplet Spawner
$6D Trap Door
$6E Spiked Platform
$6F Clock Pendulum
$70 Gear Teeth
$71 Walkway Crumbles
$72 Spiked Trap Door
$73 Water Droplet/Falling Block
$74 Spike Crusher
$75 Medium Gear
$76 Vertical Platform
$77 Horizontal Platform
$78 Scrolling Platform
$7A Pazuzu Platform
$7B Floating Platform
$80 Candle
$81 Candlestand
$82 Lantern
NPC Class IDs $0291A4
$00 Boss
$01 Zombie (from side, slow)
$02 Zombie (from ground)
$03 Crow (low)
$04 Crow (high)
$05 Bat (flying[1])
$06 Bat (sleeping)
$07 Medusa Head
$08 Ghost (no flicker)
$09 Skeleton (bone)
$0A Skeleton (whip)
$0B Floating Eye
$0C Shroom (floating)
$0D Winged Demon
$0E Flea Man
$0F Slime
$10 Skeleton (sword)
$11 Axe Knight
$12 Bone Pillar (3-shot[1])
$13 Skeleton (normal)
$14 Fishman (Jump only)
$15 <unused?>
$16 Mummy
$17 Skeleton (blood normal)
$18 Enemy Death Spark
$19 Mummy (crumbling)
$1A Skeleton (crumbling)
$1B Splash (ice)
$1C Dhuron
$1D Spider
$1E Bone Pillar (2-shot)
$1F <unused?>
$20 Shroom (bloom floating)
$21 Skeleton (blood whip)
$22 Shroom (grounded)
$23 Shroom (bloom grounded)
$24 Zombie (from side, fast[2])
$25 Mudman
$26 Frog
$27 Fishman (Swim only)
$28 Fireman
$29 Fishman (Swim & Jump)
$2A Splash (water)
$2B Knight
$2C Bone Dragon Head
$2D Bone Dragon Rib
$2E Bone Dragon Base
$2F Bone Pillar (3-shot[2])
$30 Candle
$31 Torch
$32 Lantern
$38 Zombie (from side, fast[1])
$43 Money Bags
...
$4C
$4D
$4E Small Heart
$4F Multiplier (2X)
$50 Multiplier (3X)
$57 Spiderweb
$58 Fireball
$59 Axe
$5A Teardrop
$5B Bone
$5C Mummy Wrap
$5D Spiderling
$5E Sporeling
$5F Embers
$60 Thunderstorm
$63 Owl / Boss Crystal
$64 Fuzzy
$65 Harpy
$66 Bat (flying[3])
$68 Ghost (flicker)
$69 Fleaman Bomb
$6E (U) Bat Flying[2]
$70 (U) Zombie (from side, fast[2], hard)
...
$9
APU Initialization
For
regular audio tracks (#6E and lower), the APU is initialized as
follows: Registers
$4000, $4004, and $400C (Square and Noise volumes) are initialized to
#30. Registers $4001 and $4005 (Square sweeps) are initialized to #7F.
Registers $9000, $A000, and $B000 (VRC6 Pulse control and Saw volume)
are initialized to #00. Register $4015 (APU channels) is initialized to
#0F. For DPCM samples, register $4015 is also initialized to #0F, but a
few lines later it's set to #1F after the DPCM data has been loaded.
If the death jingle (J: #50, U: #4E) is playing, only the percussion noise tracks (#01-#05) are allowed.
DPCM
samples are stored in order of priority. The percussion DPCM samples
(#6F-#73) have no priority, so any DPCM sample will cut off any
percussion sample already playing. Otherwise, a quick glance at the
sample order tells us the Trevor's whip and Sypha's staff have the
lowest priority, then the grunting sounds when taking damage, then the
boss laughs (including that scream from the floating head demon), and
the boss death cry has the highest priority.
Sound Files
These are the values sent to the CPU for sound files via address $E249 (U:$E25F).
SFX
$01 Hi-Hat Pedal
$02 Hi-Hat Closed
$03 Hi-Hat Open/Cymbal Light
$04 Hi-Hat Open/Cymbal Heavy (unused)
$05 Snare Hit
$06 [U:$07] Crumbling Room
$07 [U:$09] Footsteps
$08 [U:$0B] Rough Landing
$09 [U:$0C] Door Slam
$0A [U:$0D] Bouncing Slime Landing
--- [U:$0A] Floating Heads Drool
$0B [U:$0E] Giant Rotating Gear
$0C [U:$0F] Mudman Appearance
$0D [U:$10] Crumbling Ledge
$0E [U:$08] Corrosive Water Corrosion
$0F [U:$11] Ceiling Smash | Ledge Fall
$10 [U:$12] Crumbling Bridge
$11 [U:$13] Axe Weapon Toss
$12 [U:$14] Dagger Toss
$13 [U:$15] Cross Toss
$14 [U:$16] Holy Water Shatter
$15 [U:$17] Invincibility Potion
$16 [U:$18] Get Coins
$17 [U:$19] Timer Tally
$18 [$U:1A] Hearts Tally | Name Letter Select
$19 [U:$1B] Get Heart | Name Letter Enter
$1A [U:$1C] Get Weapon
$1B [U:$1D] ****UNUSED EXPLOSION****
$1C [U:$1E] Water Attack (Lightning in v.US)
$1D [U:$1F] Alucard Fireball
$1E [U:$20] Flowing Water
$1F [U:$21] Ice Spell
$20 [U:$22] Emerge Splash
$21 [U:$23] Submerge Splash
$22 [U:$06] Corrosive Water Silencer
$23 [U:$24] Fire Spell (for Water Dragons also)
$24 Resilient Enemy Hit
$25 Roaring Waterfall
$26 Owl Flap
$27 Enemy Shattered (e.g., skeletons)
$28 Ineffective Attack
$29 Cyclops Pound
$2A Boss Hit
$2B [U:$2C] Mystery Meat
$2C Thunder
$2D Title Screen Thunder
$2E Bat Form
$2F Water Dragon Emerge
$30 Water Dragon Submerge
$31 [U:$32] Bone, Spit, Tears, et al Attack
$32 [U:$33] Object Destroy
$33 [U:$34] Block Break
$34 [U:$35] River Freeze
$35 [U:---] Teeter-Totter (removed)
$36 Trap Door Turn
$37 Collapsing Tower | Leviathan Stomp
$38 Frozen Enemy Shattered
$39 Creature Stomp
$3A [U:$06] Medusa Beam (removed)
$3B [U:$06] Medusa Arrow (removed)
$3C [U:$3A] Clocktower Quake
$3D [U:$3B] Sunken Room Quake
$3E [U:$3C] Stopwatch Tick
$3F [U:$3D] Flooding Sunken Room
$40 [U:$3E] Prayer Thunder
$41 [U:$3F] Big Thunder
$42 [U:$40] Invalid Password
$43 [U:$41] Partner Saved
$44 [U:$42] Flame Pillar
$45 [U:$43] Wisp Explode
$46 [U:$44] Pazuzu Laser
$47 [U:$45] Doppelganger Change
$48 [U:$46] Partner Change
$49 [U:$47] Pazuzu Hit
$4A [U:$48] Crystal Ping
$4B [U:$49] Time's Up
$4C [U:$4A] Rosario
$4D [U:$4B] 1UP
$4E [U:$4C] Health Refill
$4F [U:$4D] Pause
$50 [U:$4E] "Life Loss"
$51 [U:$4F] "Beginning"
$52 [U:$50] "Mad Forest"
$53 [U:$51] "Stream"
$54 [U:$52] "Dead Beat"
$55 [U:$53] "Clockwork"
$56 [U:$54] "Aquarius"
$57 [U:$55] "Rising"
$58 [U:$56] "Anxiety"
$59 [U:$57] "Nightmare"
$5A [U:$58] "Demon Seed"
$5B [U:$59] "Anxiety" (duplicated)
$5C [U:$5A] "Aquarius" (duplicated)
$5D [U:$5B] "Deja Vu"
$5E [U:$5C] "Riddle"
$5F [U:$5D] "Pressure"
$60 [U:$5E] "Overture"
$61 [U:$5F] "Boss Fight"
$62 [U:$60] "Big Battle"
$63 [U:$61] "Big Battle" (fast)
$64 [U:$62] "Epitaph"
$65 [U:$63] "Blk Clear"
$66 [U:$64] "All Clear"
$67 [U:$65] "Game Over"
$68 [U:$66] "Prelude"
$69 [U:$67] "Prayer"
$6A [U:$68] "Evergreen"
$6B [U:$69] "Flash Back"
$6C [U:$6A] "Pressure" (duplicated)
$6D [U:$6B] "Encounter"
$6E [U:$6C] "Destiny"
$6F [U:$6D] Floor Tom (DPCM #00)
$70 [U:$6E] Low Tom (DPCM #01)
$71 [U:$6F] High Tom (DPCM #02)
$72 [U:$70] Snare Drum (DPCM #03)
$73 [U:$71] Bass Drum (DPCM #04)
$74 [U:$72] Attack / Main Menu Select (DPCM #05)
$75 [U:$73] Trevor Hurt
$76 [U:$74] Grant Hurt
$77 [U:$75] Sypha Hurt
$78 [U:$76] Alucard Hurt
$79 [U:$77] Death Laugh (DPCM)
$7A [U:$78] Whip Attack (duplicated)
$7B [U:$79] Ominous Laugh (DPCM)
$7C [U:$7A] Flying Heads Scream (DPCM)
$7D [U:$7B] Boss Scream (DPCM)
Constants
As I find them, I will include the constants (enemy stats, timer
durations, etc.) here. Addresses identified as offsets should only be
modified with extreme caution.
$000F42 Starting Hearts
$000F3D Starting Lives
The above two can be easily changed in reVamp.
$03BA3D Stopwatch Duration
Default is #$B4, which is 180 steps. In other words, it's 3 seconds
if running at 60 fps. Oddly, the timer requires 64 steps to count down 1
second, which I've noticed in other games as well.
$02925E Enemy Health (R)
Every enemy's health is stored in this range, sorted by object index.
Every boss has a unique object index, which means bosses can have
different health, but anything higher than #$40 will not register
properly in the boss' health bar at the top of the screen.
$013FA0 Point Value (R)
$02C3E8 Byte $05AA (R)
The offset is retrieved by object_index-#$38. This is tripled and used as a sprite offset.
Hit detection is performed in Begin Step event (essentially).
Notes To Self
$037724 (50) (7C & $F0 3A)
$3D909 (01 80) address for climbing up stairs to next room based on $0034
next address is $3D909 + $0035 << 1 offset, add #$18010 to find ROM address
0180 0
E28D 1
AF99 2
82A6 3
11B3 4
0180 5
FF89 6
6F91 7
DE9D 8
0180 9
1088 A
0EAA B
CB9F C
4BA9 D
B7B2 E
Above address offset by $0035 << 1
$038613 (R) Map to Trevor's x-coordinate offset when changing rooms via stairs
$038614 (R) Map to Trevor's y-coordinate when changing rooms via stairs
$03862B (R) Map to Trevor's y-coordinate offset (?) based on $054E and
additional x-coordinate offset based on $05EF (whatever that is),
usually +0
Y-values in reVamp vary from game values by 1 whole tile height as follows:
yrevamp = ygame - #$20{status bar} + #$10
When height checked uses that formula, collision is for at instance's
feet. Discrepancy shouldn't ever be more than #$10 or less than #$F0
(above head).
Collisions in front of objects are checked 1 whole tile width (#$10) ahead.
These are the "hit box" settings for tile collision of various objects:
Trevor
In my engine, Trevor's y is $10 pixels higher than in the actual
game, so y+$10-$20, $20 being the height of the status bar, in my engine
would be y+$20 (I think).
(x±8,y±16) Wall collision
( x±5,y+16) Floor collision
(x,y-8 6r y-24) Jumping
( $800C + #$09 + $0036 << 1 + #$01 ) >> #$04 = next subroom when ascending from vertical
x($0A)+#$0C = AB; A = next subroom << 4, B = next screen
x($0A)+$0036 = BG palette offset or palette offset+#$1C
$9378 Trevor's Bounding box addressing
Indexed by $9375+$0565+1
2D95 $02
6796 $04
2496 $06
7797 $08
2099 $0A
7797 $0C
5799 $0E
6B99 $10
A499 $12
AB9A $14
4999 $16
0D94 $18
2F94 $1A
5A94 $1C
9394 $1E
AD94 $20
C994 $22
F494 $24
0783 $26
A693 $28
A693 $2A
588B $2C
DF8A $2E
view_x&$3F[0E]&$20+$C0[09] if ($0062)==0 else view_x&$3F&$20+$00
($0073)+$05(*$0062>0)[73]
$01[0A]
$8410,73 (enemy block)
Interactive Terrain (QWord)Moving objects between screens sometimes glitches in reVamp.
$00A030 Index 4 (Object Map)
$00A054 Index 1 (Word Map)
$00A0F2 Index 2 (Offset Map)
$00A17A Index 3 (Stats Map)
A B C D E F G H
02 E8 00 10 50 00 01 00
Breakable Block
A Screen
B X-coordinate (Y in vertical stages)
(#$8 right from left edge)
D Object (#$10;#$11 in vertical stages)
(used when loading into room) E Y-coordinate (X in vertical stages)
(#$D down from top edge; subtract 8 if G set)
F Item
($00=Meat, $01=Heart, $02=Empty, $03=Multiplier, $04=1UP)
G Dual Layer
H Instance ID (counter)
Horizontal Ledge
C Screen Trigger
(see reVamp)
D Object (#$0C)
G Velocity
(see reVamp)
Minimum is #$01. Setting the highest bit moves ledge left.
H Range (see reVamp)
Vertical Ledge
D Object (#$0B)
F Range
(see reVamp)
G Velocity
(see reVamp)
Minimum is #$01. Setting the highest bit moves ledge up.
Moving Spikes /Crusher
D Object (#$06;#$07)
F Up-Down State?
G Range
(see reVamp)
Values in reVamp don't match values in ROM. Huge variance.
Trap Doors
D Object (#$02;#$03)
F Up-Down State?
G Location
(see reVamp)
Not sure why this is used.
Pendulum
D Object (#$05)
F Location
(see reVamp)
G Velocity
(see reVamp)
Positive for left, negative for right.
Gears
C ??
D Object (#$0A;#$04)
F Direction
Clockwise=#$00; Counterclockwise=#$01)
G Location
(see reVamp)
Tilting Ledge
D Object (#$01)
F Location
(see reVamp)
G Tilt
L-tilt should be #$01, but this seems inconsistent.
Falling Blocks
D Object (#$08)
F Location
(see reVamp)
Acid Drops
D Object (#$09)
F Location
(see reVamp)
Scrolling Ledges
B ??
C ??
D Object (#$0D)
H ??
Breakable Blocks
Breakable blocks occupy X offsets #$1A and #$1B.
Enemy State Addressing
Enemy states can be
thought of as a jumbled mass of scripts (i.e., functions). Each of these
are addressed in the LUT starting at (U: $02FD5F). The LUT is indexed
based on addresses found in another LUT at (U: $02FEB1), sorted by an
enemy's CLASS. This LUT itself points to a list of pointers and
additional argument info starting at $02E5EE (U: $02E548), with an odd
outlier at $02E4B6 (U: $02E41B), sorted by the enemy's STATE. For
example, looking at a normal Zombie, whose CLASS maps to the X register
as #02, $02FEB1,X(u) points to $02E5AC. If our Zombie is in STATE 0, the
values fetched would be #07008000. This translates to script 7, with
arguments #00, #80, and #00. If you look at script 7 (slow zombie state
0), arguments 1 and 2 are used for the horizontal speed (#0080, or 1/2),
while argument 3 is unused. Similar mechanics are seen in the boss
codes. This setup may seem confusing at first, but when you factor in
the amount of repeated code (e.g., scripts #11, #12, #1F, #20), it has
its benefits.
$02FD77 (U:$02FD5F) State Functions (J:#9E, U:#A9)
$02FEB3 (U:$02FEB1) Class Definitions (J:#68, U:#73)
$02E5EE (U:$02E548) State Definitions
Bosses
are handled similarly. There are #66 classes of boss behaviors, with
each class defined at (U: $0249FF). Each class has data pairs sorted by
state ($5C1,X for bosses). The first value of each pair is the function
to run for the state, and the second value is a pointer to a list of
arguments for that function, found at (U: $0250B7). For example, a pair
#0501 means the state will execute function #05 using the second (#01+1)
set of arguments {#00,#00,#00}. The pointers for each function start at
(U: $024681).
(U:$024681) State Functions
(U:$0249FF) Class Definitions
(U:$0250B7) State Definitions
Sprite Definition
Up to 64 sprites can be set each frame, as determined by $0007 in the sprite handling routine within the game. Each sprite takes 4 bytes as outlined on NESDev's PPU wiki. By default, unused sprites will have a y-coordinate of #f4, telling the PPU to not render any of those "sprites". Sprite data is thus pretty straightforward. The first number is always the number of 8x16 sprites comprising the overall sprite. The number of bytes after that relate to each of the bytes in the PPU as follow:
- Y-offset of sprite. ROR the value (set carry to highest bit first) to find y-offset. For example, if value is #f0, the y-offset will be #f8; if value is #04, y-offset will be #08. Added to instance's y-coordinate before write to PPU.
- First sprite in PPU to use, written directly to PPU. The sprite immediately afterward will be drawn below it.
- Sprite attributes. If the image is not flipped, this will be XORed by #40 before writing to PPU. If any sprite attributes are set for the instance, the default palette will be ignored.
- X-offset of sprite minus 8. In other words, #f8 means no offset. If this sets the sprite outside the screen, the sprite is given a y-value of #f4, making it unrendered. Otherwise, added to instance's x-coordinate before write to PPU.
After every 4 bytes written, $0007 decrements. If there are no more sprites allowed, the routine ends. Although this likely would never happen, it may become prudent to ensure the most significant sprites in each instance are rendered first.
Unplaceable Sprites
The following sprites in CV3j have inexplicably failed to get mapped in FCEUX for me, excluding sprites clearly included as a batch, such as alphabet and punctuation, and tiles probably positioned behind the HUD.
$40610 Mirrored Dagger
$40E10 Grant reaching to floor? Somersault? Time-out on wall?
$43410 Armor (alternate CHR)
$44410 Grant Epilogue Cheer (garbage)
$45410 Teeter-Totter (alternate CHR with Fireman)
$47B10 Alucard artifact (looks like eyes?)
$47E10 Alucard lightning (did they copy Sypha's set?)
$49090 Medusa snake belly
$4CBB0 Pazuzu tile
$4CC70 Pazuzu tile
$4CD00 Pazuzu tile
$4CD40 Pazuzu tile
$4CDE0 Pazuzu tile
$4E9A0 Epilogue water tile
$4EB10 Epilogue mountain/tree tile
$4F0D0 Intro whip? (not the one you're thinking of)
$4F330 Intro cloud (probably "covered" by Trevor's head)
$52850 Large gear wheel side
$53710 Crusher tiles (uses sprites, not tiles)
This is an excellent list for programmers like my self. I am hoping in time you list the values that control the map screens. I'd like to program a way to remove them and have the stages play back to back. Keep up the awesome work!
ReplyDeleteThat's a good question... It would appear to be handled by either byte $18 or byte $2C (my money's on $18), but simply freezing one or the other causes all kinds of bugs, so I (or someone) will need to delve deep into the subroutines for both bytes. Setting $18 to #09 immediately goes to the Prayer scene, and #04 keeps it in the normal gameplay, and #08 sends it to the map. You'd think freezing at #04 prevents it from going to the map, but it also prevents the stage from ending.
DeleteWell, in CV3(J), ROM address 0x004158 tells the game to go to the Map Screen between levels. However, the next level isn't actually set until you go to the map screen. Simply changing this address to #03 (stage start) or #05 (block restart) would do you no good because you'd be stuck playing the same room until you die.
DeleteFurthermore, due to how the game is structured, the map screen resets certain variables, most notably $002E (stage cleared). Thus, bypassing the map screen without extensive restructuring of the code would lead to undesirable glitches such as not being able to pause the game.
None of this is to say you couldn't bypass the map screen entirely. To do so would require extensive restructuring of the code in order to address all the lost functionality of the map screen. However, perhaps you could work within the map screen code itself. It is structured by substates. Just loading the map screen and getting to the point where Trevor chooses a path requires 8 substates. If you rewrite the code to bypass those undesired states, including the ones that draw the map, you could fairly easily treat the map screen as just another quick transition.