-
Notifications
You must be signed in to change notification settings - Fork 4
Unorganized Dev Notes
15 days 9-15 days 0-8 days
$3B7-$3B9 weapon index $8C67 = 0xB9 = 185 damage
When weapon index is set to 0x80 on last frame, it points to the 185 damage
====PPU Instruction Format==== Byte 1 - Sets PPU_CONTROL value Bytes 2-3 - 2 bytes written to PPU_ADDRESS (high byte first) Byte 4-N - Writes byte 4 to PPU_DATA (i.e., adding sprite/tile to screen)
Instructions are followed by 0xFF, signaling the end of the PPU instruction
Marsh does 1 HP of damage every 16 frames, or more specifically, any time you are in the marsh and the global counter ($1D) & 0x0F == 0.
Damage calculated at 03:8031
Damage applied at: 07:D354 - spikes/marsh (accumulator holds damage value and gets set in $08) 07:D356 - all other damage ($08 already set to damage specific to enemy)
$0C == 0x03 when taking taking from marsh/spikes
$500-$6FF used for environment collision detection
- AA = water
- FF = marsh/spikes
$8F used for marsh and spikes
- 00 = town
- 01 = town room
- 41 = marsh
- 81 = spikes
01:8D1C
execute breakpoint with X==#7
$32A death y position $32B-$32D death scythe y position $34E death x position $34F-$351 death scythe x position $372 Death y delta $373 holds the next x pos increment of the scythe $384 Death y delta???? $396 Death's x delta $397-$399 Death scythes' x delta $426 - 1 if death mirrored, 0 if not
07:E1B7
,1E18B
code where value is added to scythe x position
sprite | location |
---|---|
death drop (golden knife) | $29518-$2952B |
camilla drop (cross, 1st half) | $2A618-$2A619 |
camilla drop (cross, 2nd half) | $2A71E-$2A71F |
name | RAM | ROM | $7F | notes |
---|---|---|---|---|
merchant (weapon/item) | EDD8 | 1EDE8 | see below | |
merchant (whip) | EDF4 | 1EE04 | see below | |
crystal dude (blue) | 906F | 507F | 0x55 | |
crystal dude (red) | 9088 | 5098 | 0x56 | |
orb | 8794 | 47A4 | 0x18-0x1C | |
laurel dude (laruba) | 9347 | 5357 | 0x78 | |
flame whip dude | 8C72 | 4C82 | 0x0E | |
diamond dude | AA3A | 6A4A | 0x12 | |
secret merchant (silver knife) | AE12 | 6E22 | 0x10 | |
secret merchant (silk bag) | AE07 | 6E17 | 0x0F | |
Death | 87C7 | 47D7 | 0x77 | still appears as knife no matter what item it actually gives you |
Camilla | 87BF | 47CF | 0x26 | still appears as cross no matter what item it actually gives you |
sacred flame | 87CD | 47DD | 0x76 | still appears as flame no matter what item it actually gives you |
merchant | $7F |
---|---|
thorn whip | 0x33 |
chain whip | 0x34 |
morning star | 0x35 |
white crystal | 0x32 |
holy water | 0x37 |
dagger | 0x36 |
garlic | 0x2F |
laurels | 0x30 |
oak stake | 0x1D |
; if (($7F == 0x26) && ($7F < 0x1D || $7F > 0x37)) { ; $600E = 0x01 ; }
We use SRAM values ($6000-$7FFF) to track progressive whip and crystal upgrades throughout the game to ensure you can't get the same progression item from the same actor more than once. It's also used for other state and temporary variables when necessary. Here's how we use those values.
memory | usage |
---|---|
$6000 | Temporary variable used to store original X register when loading the correct (with respect to progression) crystal sale icon for merchants |
$6006 | Diamond warp patch flag, set to $FF if enabled |
$6007 - $600B | Used for warp |
$600C | Used by is-checked. A value of 1 means the current actor has already been checked. 0 means it has not and an item can still be acquired. |
$600D | Temporary variable used to store original memory bank when bank switching is necessary, specifically in the game state checks for jovah warp and inventory deselect |
$600E | If set to a non-zero value, it indicates that an actor should use its normal, non-refuse text despite having been marked as checked. This is necessary because most non-merchant actors add the item to your inventory before they display their text. |
$600F | If set to 0x01 a jovah warp is currently in progress |
$6010-$601F | Tracks whip progression. When you acquire a whip, it adds an entry to this range which marks the actor as "checked". The range consists of 4 entries, 4 bytes in size each. The 4 bytes, in order, are: objset($30), area($50), submap($51 & 0x7), actor identifier($7F) |
$6020-$602B | Tracks crystal progression. When you acquire a crystal, it adds an entry to this range which marks the actor as "checked". The range consists of 3 entries, 4 bytes in size each. The 4 bytes, in order, are: objset($30), area($50), submap($51 & 0x7), actor identifier($7F) |
$6030-$6031 | Death count |
$6032-$6033 | Kill count |
$6034-$6035 | Hearts picked up |
$6100 | Stores objset ($30) when entering a door, used for door rando. Value is $FF when not set. |
0xCC84
is where all the text starts in the ROM. First one is "What a horrible night to have a curse".
Char | Value |
---|---|
(space) | 0x00 |
A | 0x01 |
B | 0x02 |
C | 0x03 |
D | 0x04 |
E | 0x05 |
F | 0x06 |
G | 0x07 |
H | 0x08 |
I | 0x09 |
J | 0x0A |
K | 0x0B |
L | 0x0C |
M | 0x0D |
N | 0x0E |
O | 0x0F |
P | 0x10 |
Q | 0x11 |
R | 0x12 |
S | 0x13 |
T | 0x14 |
U | 0x15 |
V | 0x16 |
W | 0x17 |
X | 0x18 |
Y | 0x19 |
Z | 0x1A |
. | 0x1B |
' | 0x1C |
^ | 0x1D |
, | 0x1E |
0 | 0x36 |
1 | 0x37 |
2 | 0x38 |
3 | 0x39 |
4 | 0x3A |
5 | 0x3B |
6 | 0x3C |
7 | 0x3D |
8 | 0x3E |
9 | 0x3F |
(new line) | 0xFE |
(end) | 0xFF |
-
Dead River - Part 2 and Dead River to Brahm share the same actors, so this needs to be accounted for during any randomizing that involves the actors in those screens. Essentially, unless otherwise handled, Dead River - Part 2 will take precedence since it is defined after Dead River to Brahm in the
core.js
definition file. -
npc.crystalDude
is the same as the (secret) merchant in the non-town pattern tables
I had to store new 1 byte values. High 3 bits are zero (but could be bg table), low 5 bits are sprite table for every screen in the game. These needed to stored in a space that could be accessed mathematically based on 3 values: objset ($30), area ($50), and submap ($51 AND #$0xF). Those 3 values together compose a unique reference to every screen. The table below shows how I allocated free bytes in the ROM to essentially store a multi-dimensional array.
objset | # of areas | # of submaps | map storage |
---|---|---|---|
0x00 | 24 | 2 | 48 |
0x01 | 11 | 4 | 44 |
0x02 | 10 | 4 | 40 |
0x03 | 5 | 5 | 25 |
0x04 | 4 | 2 | 8 |
0x05 | 1 | 2 | 2 |
So I'm mapping 167 bytes, only using 93 of those bytes in total, thereby wasting 74 bytes as unused space between the valid values of the mult-dimensional array. The pseduo-code calculation below shows roughly how, stored in this manner, I can access any bg/sprite table index at any time with the objset, area, and submap values:
// OBJ_OFFSET is 0x30, as there's equal space between each objset in the stored bytes.
(objset * OBJ_OFFSET) + (area * num_of_submaps_for_this_area) + submap
With the values stored, we then hijack the typical rudimentary pattern table mapping code. We write to unused ROM space at 0x7860
the compiled opcodes of the 6502 asm contained in the file at data/pattern.asm
. This code is responsible for executing the calculation above and then properly selecting a sprite table index based on it. All we need to do after that is overwrite 3 particular bytes of code to point to our new code. Essentially we replace the code at 0x1CCDF
with the following:
JSR $B850: 20 50 B8
and voila! Each screen now has it's own pattern table mapping! Here's the original mapping, which shows there were only 6 options (instead of the 93 possible options we have now). Here we can see what background and sprite table indexes correspond to what enemy groupings.
objset | pattern pointers | bg | sprite | name |
---|---|---|---|---|
0 | 0x1CCF9 | 0x00 | 0x01 | town |
1 | 0x1CCFB | 0x08 | 0x09 | mansion |
2 | 0x1CCFD | 0x02 | 0x03 | woods 1 |
3 | 0x1CCFF | 0x04 | 0x05 | woods 2 |
4 | 0x1CD01 | 0x06 | 0x07 | graveyard |
5 | 0x1CD03 | 0x0B | 0x0C | castlevania |
animation code at 01:AAF9
, 6B09
01:89F5
, 4A05
is oak hitting orb
$B = #01 from oak pointer
$303 = #AD for golden knife
$B1-$B6 are sound ids
sound | high byte | low byte | notes |
---|---|---|---|
B1 | BD | B7 | |
B2 | BE | B8 | |
B3 | BF | B9 | |
B4 | C0 | BA | |
B5 | C1 | BB | simon sfx |
B6 | C2 | BC |
07:C0FB process audio
#30 at NOISE_VOL ($400C) plus 8 NOPs #7F at $400D plus 8 NOPs
$9D-9E mirrors $bb,$C1
$164 determines which $4000+ range value gets set
af = 1 169 = a b5 = a bb = d6 c1 = 82 df = 0
$AA = 0 $164 = 3 $168 = 0 $169 = a $16a = 00 $16b = 4 $16c = APU_STATUS & #10
$DF = 0
$8137 where sound start?
- whip (leather)
- $00AF = 0x01 (trigger sound)
- $00B5 = 0x0A (whip sound id)
- $00BB = 0xD6 (pointer?)
- $00C1 = 0x82 (pointer?)
- jump landing
- $00AF = 0x01 (trigger sound)
- $00B5 = 0x07 (jump sound id)
- $00BB = 0xBE (pointer?)
- $00C1 = 0x82 (pointer?)
$00A3 = 0x55 (?????? is this needed for timing?)
$B4 CLC or SEC before sound set
00:82D6
, 2E6
whip sound?
$B5 and $169 write #A
00:A3B9
169 = 0a 3ea = 80 3b5 = ff // triggers whip animation
16a = ff // trigger sound? af = 1 // trigger sound?
00:A2CE:AD 99 A2 LDA $A299 = #$37 00:A2D1:85 A3 STA $00A3 = #$37 00:A2D3:AD 9A A2 LDA $A29A = #$81 00:A2D6:85 A4 STA $00A4 = #$81
00:9D9E:B5 B7 LDA $B7,X @ $00BB = #$D6 00:9DA0:85 A1 STA $00A1 = #$D6 00:9DA2:B5 BD LDA $BD,X @ $00C1 = #$82 00:9DA4:85 A2 STA $00A2 = #$82
445 457 wgi pcount down
457 whip stuff?
3f???
In RAM, 7:E373
is a subroutine that does 4 LSRs. 7:E378
does 4 ASLs.
red crystal tornado at 01:A956 ram, 6966 rom core function 07:C0E7
// merchant purchasable items: 0x1ee22-0x1ee39
// code for blue crystal guy at 1:906F(RAM) 0x507F(ROM)
// code for red crystal guy at 1:9088(RAM) 0x5098(ROM)
// 07:C08A function for red crystal
// F7 is dpad
// 0x1F6AD is where the dialog starts, 0x0E 0x00 means 0x00 14 times
// 0x83CC code specific to merchants? // 0x58,0x59 map -- 0x5E,0x5F ??? // 0x80EC - code that stores npc "type", value at 0x9140 for thorn
// 0x3ba is 0x2E which is the lower 7 bits of 0xAE which is the actor "type" // 0x4da is the actor "value" 0x08 // 0x34e is 0xC0 // 0x32a is 0xC0 // 0x3de is 0x08 // 0x438 is 0x01
// $1F6F7 is the price line in dialog ROM
// 0x414 is 0xOB after a pointer chek on 0x3ba when entering room // 0x414 times 3 (0x21) goes into 0x94, // then ised to point to 0xDDC3 = (01 1e 0c) // 1e goes into 0x306, 0c goes into 3f0 //
// $40 is 0x28, gets set to 0x93 // 0x888D is where whip is checked for merchant chat
// $1ED3D in ROM is 0x5B, which is the whip icon, gets stored at $703 // the format is 3 bytes for each entry. The first byte is the // icon for sale (index for background PPU table). The second and third // bytes are the price. PRICE RANDO! // prices: 0x1ED25-0x1ED45 // price high byte gets stored at $29, $93 // price low byte gets stored at $28
// 0xE09C is where 0x3de AND 0xf7, which excludes only values with the 4th bit // set, meaning merchants that sell whips. This gets put in 0x3de, or zero
// $f6e7 for line in dialog for dashes and hearts // 0x1EFE2 is where yes/no dialog choice happens (0xEFD2 RAM)
// $E08C (RAM) watch me whip?
// $40 and $93 determine icon for sale // $1D is 0x2E, $18 is 0x05
// $7A // * 0-3 == presenting dialog background // * 4 == printing dialog text // * 5 == non-interactive text end, waiting for B press // * 6 == waiting for yes/no choice
// $7E // * 0 == start menu // * 1 == no interaction dialog // * 2 == yes/no dialog
// $F1 is dpad
// ECC7 is where we move to the whip icon, C5D0 is where pointer is set // EE9D is the dialog text loop
// 7A increments after dialog completes (5), i think it managed dialog state // does $164 hold this value at any stage?
// C058 checks $1A, if Z flag not set, skips all merchant logic!
// $7A needs to be 0x01 for normal dialog
// $103???? at C)FB
// Change $3BA (or whatever near location has the merchant 0x2E) to the value 0x35. // The merchant then moves like a normal NPC and will deliver an npc dialog with no // yes/no interaction. Dialog now points to the unused "you level has increased..." // messages, which should be easy to change.
// Using $7F as a unique ID for actors doesn't work because the multiple // laurel and garlic merchants share the same value. Additionally, laurel // merchants have a $7F value of 0x00, which is intended to be an empty value // in the current progressive whip upgrade logic, so that will have to // change.
// whip sale code 0xEDF4 RAM, 0x1EE04 ROM
// palette hacking // $82 day/night? 1/0 // $C7E7 is where day/night is checked
800E, 401e (rom) for the area checks to determine which actor to load
3d and 3e are used as a pointer to the actors for a particular area
Join the CV2R crew in the Discord to discuss game play, races, and development of CV2R!