Welcome to Data Crystal's new home! Data Crystal is now part of the TCRF family (sort of).
The wiki has recently moved; please report any issues in Discord. Pardon the dust.

Castlevania II: Belmont's Revenge/ROM map

From Data Crystal
Jump to navigation Jump to search

This is a sub-page of Castlevania II: Belmont's Revenge.

Chip tiny.png The following article is a ROM map for Castlevania II: Belmont's Revenge.

Format for PRG-ROM is Bank:RAM address.

(r:symbol) marks the start of a callable subroutine. (RSTxy:symbol) marks a callable subroutine which can be called with the special quick-access RST commands. (t:symbol) marks the start of a data table.

Note that many functions take as an input the current entity in register d, and the entity property in register e. As a rule, entities are $20 bytes long and the first $20 bytes of every each page ($100 bytes) compose an entity.

X:7FFF all swappable banks store list their bank number as the last byte of the bank.
0:0000 (RST00:jumptable_entity_state) jumptable (indirect jump by entry in table following call to RST00 according to current entity's state variable)
0:0003 (r:jumptable) jumptable by A.
0:0008 (RST08:ld_from_table) hl <- (hl + 2a)
0:000C (r:ld_hl_hl) hl <- (hl)
0:0010 (RST10:entity_set_image) sets entity's image (see "Images" section below)
0:0018 (RST18:entity_set_timer) sets entity property $08 to A. This seems to be the entity's timer, such as how long until fire despawns or belmont's hitstun.
0:0020 (RST20:entity_get_x) e <- $17, a <- x position of entity.
0:0028 (RST28:add_hl_a) hl += a, then a <- l.
0:0030 (RST30:entity_inc_state) increments the state variable (D:01) of the current entity.
0:0038 (RST38:entity_get_y) get Y position of current entity.
0:01D9 (r:longcall_2_438D) stores current bank (read from $7FFF) on stack, calls routine 2:438D (write screen's worth of tiles to vram), then returns to caller bank.
0:031b This routine does a lot, but the first thing it does is check if the player is holding a+b+start+select and if so restarts the game.
0:0D23 (r:entity_set_state_and_substate) entity state <- b, substate (attacking) <- c
0:0D2A (r:belmont_apply_velocity) adds belmont's velocity to belmont's x position
0:2838 (r:standard_scanline_effect) ($dec1) <- 01, ($c898) <- 7e
0:283B (r:store_bc_scanline_effect) ($dec1) <- b, ($c898) <
0:2868 (r:load_substage_word_from_table) hl <- ((hl + 2*stage) + substage*2)
0:286D (r:load_substage_byte_from_table) a <- ((hl + 2*stage) + substage)
0:2873 (r:load_stage_data) hl <- (hl + 2*stage); a <- substage.
0:2881 (r:add_bc_a) bc += a, then a <- c.
0:2886 (r:add_de_a) de += a, then a <- e
0:2934 (r:clear_all_entities) zeros-out all entities from C100-D700 inclusive. Only Belmont (C000) is spared.
0:2958 (r:memcpy) directly copies bc+1 bytes from hl to de. After this, hl and de point to the end of their respective buffers. Before calling, if de=hl+1, smears bc copies of [hl]. This can be a handy way to zero out a large buffer.
0:2978 (r:negative) a <- 0x100 - a
0:297B (r:leftshift_bc_4) leftshifts bc by 4.
0:2986 (r:lda_00) a <- $00
0:2989 (r:lda_06) a <- $06
0:298C (r:lda_7F) a <- $7F
0:298F (r:lda_06) a <- $80
0:2AC7 (r:wait_for_blank) Seems to wait to the end of the current blanking period (if applicable) and then to the start of the next.
0:2ca4 change this 2-byte word from 41c0 to 4280 to free up some extra space behind Belmont's head for sprite-hacking. Specifically, this means the map screen blank tile will be loaded from somewhere else. (You'll want to do the same at 0:2ce4 for the map screen as well.)
0:2E24 (r:transfer_buffers_to_vram) Transfers multiple buffers to vram. Before calling, ($ca81) should be set to the transfer scheme (see 0:2E57, below), and hl should point to a zero-terminated meta-buffer containing structs as follows:
 - 2 bytes (Big-Endian): destination vram address divided by 0x10
 - 1 byte: destination length in units of 0x10 (source length depends on this and $ca81 scheme)
 - 1 byte: source bank
 - 2 bytes (Little-Endian) source address
0:2E57 (r:transfer_buffer_to_vram) transfers bc from buffer:hl into vram:de, using one of four schemes as selected by ($ca81):
  - 0: copy bytes from hl 1:1 (e.g. 0, 1, 2, 3, 4, 5...)
  - 1: duplicate every byte in hl (e.g. 0, 0, 1, 1, 2, 2, ...)
  - 2: even bytes from hl, odd bytes 0 (e.g. 0, 0, 1, 0, 2, 0, ...)
  - 3: odd bytes from hl, even bytes 0 (e.g 0, 0, 0, 1, 0, 2, ...)
0:2FE7 (r:2fe7) Runs during vblank. transfers $c980 buffer to vram (see ram map)
0:305A (r:305A) Runs during vblank. transfers ($cac0) bytes from bank ($cac1), address ($cac2.w) to vram ($cac4.w)
0:3366 (r:copy_32_bytes) copies 32 bytes from [bc...bc+32] to [hl...hl+32]. As a result, hl and bc are incremented 32 times.
0:3369 (r:copy_16_bytes) copies 16 bytes from [bc...bc+16] to [hl...hl+16]. As a result, hl and bc are incremented 16 times.
0:3553 (r:play_music) plays music in argument a. If a is 0, stops music?
0:35A9 (r:mbc_bankswap_1) loads swappable bank 1.
0:35AA (r:mbc_bankswap) loads swappable bank from cpu register a.
0:35AF (r:mbc_bankswap_2) loads swappable bank 2.
0:35B5 (r:mbc_bankswap_6) loads swappable bank 6.
0:35BB (r:mbc_bankswap_7) loads swappable bank 7.
0:35C1 (r:mbc_bankswap_3) loads swappable bank 3.
0:36B5 (r:get_tilechunk_address_from_de_plus_4ea2) hl <- 4x4 tile-chunk address (points to bank 2), indexed from [de+$(4ea2)] in bank 6
0:3372 (r:transfer_tile_to_vram) Runs during vblank. Transfers 8x8 tile from (bc) to (hl). If ($cacb) is non-zero, the source format is in reduplicated format (each source byte results in two subsequence destination bytes).
0:38E0 (r:entity_set_animation) Set animation (prop 0C,0B,0A) to (bc):0:(bc+1) (bc):0:(bc+1)
0:3B42 (r:entity_set_y_velocity_from_table) Entity y velocity <- word [hl + 2*a]
0:3B80 (r:entity_decrement_hitstun) decrements hitstun if positive
0:3B82 (r:entity_decrement_variable) decrements Entity variable [dl] if positive.
0:3E02 (r:entity_set_state) sets Entity variable state [d:01] <- a
0:3DAC (r:entity_set_x_velocity_0) Entity x velocity <- 00.
0:3DAF (r:entity_set_x_velocity) Entity x velocity <- cb.
0:3DB4 (r:entity_set_y_velocity_0) Entity y velocity <- 00.
0:3DB7 (r:entity_set_y_velocity) Entity y velocity <- cb.
0:3DBA (r:write_word) (hl) <- cb
0:3DBE (r:entity_get_x_velocity) cb <- Entity x velocity.
0:3DC5 (r:entity_get_y_velocity) cb <- Entity x velocity.
0:3DC8 (r:read_word) cb <- (hl)
0:3DEC (r:entity_reset_13b0) reset bit 0 of Entity var 13
2:43ae (r:get_pointer_to_tiledata) de <- a*20 + (tiledata_ptr / ca92:ca93).
2:4407 (r:transfer_4x4_tiles_to_vram) input: bc points to top-left of 4x4 vram tiles, hl points to length-16 array. Also copies the tiles to to bc+$3C00 (i.e. $D000-$D800)
3:5242 (t:substage_misc_entities) (index via load_substage_data): copied (with some modification) to $d240/$d340. 
3:58AC (t:substage_enemies) (index via load_substage_data): copied (with some modification) to $d440/$d540. 
3:5D25 (t:substage_items) (index via load_substage_data): copied (with some modification) to $d640/$d740. 
3:6bdc (r:clear_entity) clears the current entity (d:00-d:20)
3:6C49 (r:screen_coords_arithmetic) bc <- a plus bc in screen-radix. "screen-radix" here means that b represents screen dimensions ($A0 or $80 depending on vertical or horizontal), and c ranges between 0 and the screen dimension. Before calling, store screen dimension ($80 or $A0) in $(CBC3).
3:6D6B (r:inc_CBC4) increments $(CBC4)
3:702C (r:load_substage_sprites)
3:703D (r:load_substage_quadref_from_table) hl <- [hl + 2*level] + 4*sublevel
3:7048 (r:load_sprites) copy *b* bytes from [hl...] to [$df00 + 2*a]
6:421D (r:entity_update) Belmont frame update routine. Called from 0:05FA.
6:4235-4241 Belmont update jumptable
6:427F Belmont state 0 (standing) routine
6:4289 Belmont state 2 (crouching) routine
6:42E9 Belmont state 1 (walking) routine
6:4293 Belmont state 3 (jumping) routine
6:42A1 Belmont state 4 (??) routine
6:44A6 Belmont state 5 (??) routine
6:44D2 Belmont state 6 (??) routine
6:4510 Belmont state 7 (??) routine
6:461B (r:input_A_pressed) A gets zero or 10 depending on if the A button was pressed this frame. Status flags set
6:4621 (r:input_up_down_held) A and status flags
6:4627 (r:input_left_right_held) A and status flags
6:462D (r:input_B_pressed) A and status flags
6:4633 (r:input_B_held) A and status flags
6:4639 (r:input_down_held) A and status flags
6:464B (r:input_up_held) A and status flags
6:4651 (r:input_any_held) A and status flags
6:4801 (r:belmont_set_walk) Read left/right input and set Belmont to be walking left or right accordingly.
6:4817 (r:entity_move_face_right) Entity moves and faces right at velocity  0x90
6:4817 (r:entity_move_face_left)  Entity moves and faces left at velocity -0x90
7:4000 (r:memcpy7) do ([de++] = [hl++]) while (--bc)

Bank swap routines:

- All are in bank 0, and all write to $2180 to change the bank. For some reason, bank 5 is always swapped inline rather than by function call.

Sprites

Sprites comprise a set of 8x16 tiles with x/y offsets and vram object flags. They are stored in the following format:

 - 1 byte: number of 8x16 tiles in the sprite
 - (for each sprite:)
   - 1 byte [s] y offset
   - 1 byte [s] x offset
   - 1 byte     high 7 bits: vram tile index. 1 bit: object flags
   - (optional) 1 byte: object flags. (Only if previous byte low bit is set.)

- Entities' sprites are stored in property 0A (for example, Belmont's sprite is at 0xC00A—see RAM map). The value is looked up in a table in RAM starting at address 0xD00 to get a pointer to a sprite as defined above. Editing this value will change the entity's image—for example, the axe might appear as a torch, at least until the axe's animation restores the image to the next frame of axe animation. Below is a partially-complete table of images (Please contribute!). Note that the sprite will look like garbage if the correct vram tiles aren't loaded yet.

0: Torch (frame 0)
1: Torch (frame 1)
2: Coin (frame 0)
3: Coin (frame 1)
4: Score Orb
5: Small heart
6: Large Heart
7: Wall meat
08: fire (frame 0)
09: fire (frame 1)
0A: 1-up
0B: holy water (icon)
0C: holy water (projectile)
0D-10: Axe/Cross (frames 0-3)
11-23: Belmont poses
24-29: whip
38-39: Punaguchi
3C: Punaguchi bullet
40-43: Bat
44-45: knight
4D-55: Forneus
57: Moving Platform
5D: Rope Spike Ball
5E-60: Pulley
5D-64: Eyeball
5D-: Giant Bat
61: Raven
75-76: Dagger thrower
76*: Angel Mummy spine

and so on.

Entity indices

(stored in RAM addresses XY00, XY >= $C6) -- range from 0-7F.

00: (no entity)
01: Lantern/Item: Axe/Cross
02: Lantern/Item: Holy Water
03: Lantern/Item: Coin
04: Lantern/Item: Whip Upgrade Orb
05: Lantern/Item: Small Heart
06: Big Heart
07: (debris)
08: (debris with big heart?)
09: Rat?
0A: ?
0B: breakable block?
0C: Punaguchi
0D: Punaguchi
0E: Rat?
0F: ? (explodes when struck)
10-13: ?
14: vertically moving flame?
15-16: ?
17-1B: ? (palette cycles, and turns to flame when struck.)
1C: ? (ascends)
1D: ? (gravity)
1E: ? (descends)
1F: bat?
20: bat?
21: ? (sessile enemy)
22: Lantern/Item: orb that crashes the game
23-24: ?
25: eyeball spawner (right)
26: eyeball spawner (left)
27-29: ?
2A: (immediately explodes)
2B: ? (deals contact damage, but cannot be destroyed)
2C: ? (like 2B but larger and does more damage)
2D: ? (like 2D)
2E-2F: ?
30: ? (sessile enemy)
31: ? (like 2B)
32: ?
33: ? (descending enemy)
34: raven
35: whip upgrade orb
36: ?
37-3A: ?
3B: ? (sessile enemy, explodes)
3C: eyeball spawner (above)
3D-3E: ?
3F: ? (sessile enemy)
40: ?
41: jumping dagger-thrower
42-44: ? (like 2B)
45: Lantern/Item: boss start (Darkside)
46: ?
47: ? (enemy, drops and dies)
48-49: ?
4A: moving platform, contact damage?
4B-4D: ?
4E: background flame
50: moving platform, covers screen?
51: ? (sticks to top of screen)
52: ?
53: Lantern/Item/Controller: boss start (Angel mummy)
54: Angel Mummy Head
55: Angel Mummy Vertebrae
56: ? (moves across screen, leaving trail.)
57: like 56, but in reverse.
58-5A: ?
5B: Night Stalker
5C: ? (contact damage)
5D: ?
60: Lantern/Item: boss start?
61: ?
62: ? (palette cycles, deals damage)
64: ? (like 5C)
65-68: ?
69: Lantern/Item: boss start (Bone Serpent)
6A: Bone Serpent (part)
6B: ? (moves to constant position at top of screen)
6C: Bone Serpent (part)
6D: Bone Serpent (part)
6E: moves to side of screen
6F: Bone Serpent (part)
70: ? (strange glitching tiles)
71: ?
72: Lantern/Item: boss start (Soleil)
73: Lantern/Item: boss start
74: ? (circular movement)
75-78: ?
79-7E: (crashes game)
7F: ?