Claim set · data/claims/simons-quest-save.yaml

Castlevania II - Simon's Quest (USA), the password, read in the ROM

Game: Castlevania II: Simon's Quest. Concept: Password and battery save.

Simon's Quest's password packs 10 bytes: 7 of state, the byte FoxCunning labels Simon's level, the day count in BCD, the laurel and garlic counts in 4 bits each (the encoder writes a count above 8 as 8), the bytes labeled body parts, misc items 1, and misc items 2, and the whip level; then the 16-bit sum of those 7 as a check value; then a scramble byte. Hearts are not kept: accepting a password zeroes the bytes FoxCunning labels hearts and experience, sets HP to 48 and lives to 3, and zeroes $30 and $50-$57 (labeled the map type, scene, sub-room, and scroll position), $82 (night), $84, and $85 (the time wait counter and the minute). simons-quest-save/layout

Value
{
 "bytes": 10,
 "fields": [
  {
   "byte": 0,
   "what": "Simon's level (CurrentLevel, $8B); the decoder takes 0-6"
  },
  {
   "byte": 1,
   "what": "day count, two BCD digits (CurrentDayBCD, $83)"
  },
  {
   "byte": 2,
   "bits": "0-3",
   "what": "laurels (InventoryNumLaurels, $4C), at most 8"
  },
  {
   "byte": 2,
   "bits": "4-7",
   "what": "garlics (InventoryNumGarlics, $4D), at most 8"
  },
  {
   "byte": 3,
   "what": "body parts (InventoryBodyParts1, $91); the decoder takes below $80"
  },
  {
   "byte": 4,
   "what": "misc items 1 (InventoryMiscItems1, $92); the decoder takes below $10"
  },
  {
   "byte": 5,
   "what": "misc items 2 (InventoryMiscItems2, $4A); the decoder takes below $80"
  },
  {
   "byte": 6,
   "what": "whip level (CurrentWhipLevel, $0434); the decoder takes 0-4"
  },
  {
   "byte": "7-8",
   "what": "check value, the 16-bit sum of bytes 0-6, high byte first"
  },
  {
   "byte": 9,
   "bits": "4-7",
   "what": "the add, 0-4"
  },
  {
   "byte": 9,
   "bits": "0-3",
   "what": "the XOR table's number, 0-15"
  }
 ]
}
CodeCastlevania II - Simon's Quest (USA) · 0x12F31-0x12F71 — bank 4 $AF21-$AF61, EncodePassword_Compress: $8B, $83, $4C and $4D (each ANDed with $0F, then 8 if 8 or more, garlics shifted into the high nibble), $91, $92, $4A, and $0434 copied to $0520-$0526Castlevania II - Simon's Quest (USA) · 0x12E4E-0x12E6D — bank 4 $AE3E-$AE5D, EncodePassword_CreateChecksum: $0520-$0526 summed with carry into $0527 (high) and $0528 (low)Castlevania II - Simon's Quest (USA) · 0x12F0B-0x12F30 — bank 4 $AEFB-$AF20, EncodePassword_CreateAddSub and its table: the add shifted into the high nibble of $0529, the XOR number in the lowCastlevania II - Simon's Quest (USA) · 0x12D57-0x12DC5 — bank 4 $AD47-$ADB5, the accepted path: $0434 kept on the stack across a JSR to $C5A1, then a JSR to $C419, $90, $4F, $33, $1E, $82, $30, $50-$57, $84, and $85 zeroed, $80 = $30, $31 = 3Castlevania II - Simon's Quest (USA) · 0x1C429-0x1C433 — bank 7 $C419-$C423, ResetHeartsAndExp: zero to $48, $49, $46, $47Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L48-L51 — the labels for the bytes the accepted path zeroesEdigits0011 = $46 ; $46 … HeartsDigits1100 = $49 ; $49Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L33-L34CurrentLevelMapType = $30 ; $30 RemainingLives = $31 ; $31Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L52-L63InventoryMiscItems2 = $4A ; $4A … InventoryNumLaurels = $4C ; $4C InventoryNumGarlics = $4D ; $4D … CurrentLevelSceneNumber = $50 ; $50 CurrentLevelSubRoomNumber = $51 ; $51 CurrentXScrollingPositionPixels = $53 ; $53 CurrentXScrollingPositionScreens = $54 ; $54 CurrentYScrollingPositionPixels_Mod240 = $56 ; $56 Current_WhatUnknown57 = $57 ; $57Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L88-L93CurrentHP = $80 ; $80 … IsNightTime = $82 ; $82 CurrentDayBCD = $83 ; $83 CurrentTimeWaitCounter = $84 ; $84 CurrentMinuteBCD = $85 ; $85Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L102-L103InventoryBodyParts1 = $91 ; $91 InventoryMiscItems1 = $92 ; $92Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L258CurrentWhipLevel = $0434 ; $0434Castlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L97CurrentLevel = $8B ; $8BCastlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L301-L310PasswordLevel = $0520 ; $0520 PasswordCurrentDayBCD = $0521 ; $0521 PasswordLaurelsGarlics = $0522 ; $0522 PasswordBodyparts = $0523 ; $0523 PasswordMiscItems1 = $0524 ; $0524 PasswordMiscItems2 = $0525 ; $0525 PasswordWhipLevel = $0526 ; $0526 PasswordChecksumHi = $0527 ; $0527 PasswordChecksumLo = $0528 ; $0528 PasswordXorAdd = $0529 ; $0529Checked by claude, 2026-10-22 — disassembled each range in the pinned ROM; FoxCunning's bank_4.asm, a hack, led to them, and a script matched its password module ($AABE-$B327) against the ROM instruction by instruction, immediates and labeled addresses included, with no difference, so its names apply to the original here

The field names are FoxCunning's labels; the atlas hasn't read which bit of the item bytes is which item. Maximum HP is set from the level, 48 below level 2, 64 below 4, 80 below 6, and 96 at level 6 and above (computed from the same range), while HP starts at 48.

Cited by: Castlevania II: Simon's Quest, Password and battery save

Simon's Quest writes the 10 bytes, 80 bits, as 16 characters of 5 bits each, high bit first, so the scramble byte fills the last 8 bits; the screen draws a value n as tile n + 1 of CHR bank 16, where by the atlas's reading of the tiles 1-36 are A-Z and 0-9. Since the scramble adds up to 4 to characters 1-14, they can show all 36 (computed), and characters 15 and 16 only A-Z and 0-5. On the entry grid, pressing A on cells 0-35 enters that symbol, cells 36 and 37 step the cursor back and forward, and a later cell submits. simons-quest-save/characters

Value
{
 "characters": 16,
 "bitsPerCharacter": 5,
 "alphabet": "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
}
CodeCastlevania II - Simon's Quest (USA) · 0x12FD7-0x130E9 — bank 4 $AFC7-$B0D9, EncodePassword_Convert8_to_6: $0520-$0529 shifted and masked, high bits first, into 5-bit values at $0530-$053FCastlevania II - Simon's Quest (USA) · 0x12B85-0x12BB2 — bank 4 $AB75-$ABA2: the tile sent to the screen is the cursor cell ($04F4) plus 1, kept in $0F; a cleared character sends 0Castlevania II - Simon's Quest (USA) · 0x12B4F-0x12B76 — bank 4 $AB3F-$AB66, A pressed: a cell below $24 enters a character, $24 and $25 decrement and increment the character position, any other advances the screen's state to the checkCastlevania II - Simon's Quest (USA) · 0x12C09-0x12C36 — bank 4 $ABF9-$AC26: entering stores $0F at $0540 + position; clearing stores 0Castlevania II - Simon's Quest (USA) · 0x1C3A4-0x1C3AC — bank 7 $C394-$C39C, before the entry screen: $10 and $11 written to $0101 and $0102, FoxCunning's Mapper1reg_reg1 and reg2, the 4 KB CHR banksCastlevania II - Simon's Quest (USA) · 0x30020-0x3027F — CHR ROM bank 16, tiles $01-$26: the glyphs A to Z, 0 to 9, a left arrow, and a right arrowChecked by claude, 2026-10-22 — disassembled each PRG range in the pinned ROM and drew CHR bank 16's tiles $01-$26 as text; a script built from these routines and the ROM's tables round-tripped 20,000 states and produced values up to 35

Cells 36 and 37 draw tiles $25 and $26, a left and a right arrow.

Cited by: Password and battery save

To make a password, Simon's Quest sums the 7 state bytes into a 16-bit check value and takes a scramble from the frame counter: its low 4 bits number one of 16 tables of 14 XOR values, and its low 3 bits pick an add from 0, 1, 2, 3, 4, 1, 2, 3. After packing, characters 1-14 are XORed with the table and then have the add added; characters 15 and 16 carry the add and the table number unscrambled. Since the add follows from the table number, one state has at most 16 passwords (computed). simons-quest-save/scramble

Value
{
 "check": "16-bit sum",
 "tables": 16,
 "scrambled": 14,
 "adds": [
  0,
  1,
  2,
  3,
  4,
  1,
  2,
  3
 ],
 "passwordsPerState": 16
}
CodeCastlevania II - Simon's Quest (USA) · 0x12E34-0x12E4D — bank 4 $AE24-$AE3D, EncodePassword: compress, checksum, choose the scramble, pack, XOR, addCastlevania II - Simon's Quest (USA) · 0x12E4E-0x12E6D — bank 4 $AE3E-$AE5D: 16-bit sum of $0520-$0526Castlevania II - Simon's Quest (USA) · 0x12F0B-0x12F30 — bank 4 $AEFB-$AF20: FrameCounter ($1D) AND 7 indexes the 8-entry add table, AND $0F gives the XOR numberCastlevania II - Simon's Quest (USA) · 0x13215-0x13337 — bank 4 $B205-$B327, Password_DoXorring: 16 pointers to 14-byte tables, each value below $20, XORed into $0530-$053DCastlevania II - Simon's Quest (USA) · 0x12EF9-0x12F0A — bank 4 $AEE9-$AEFA, EncodePassword_DoAdding: the add added to $0530-$053DChecked by claude, 2026-10-22 — disassembled each range in the pinned ROM; a script encoding one state at all 256 frame-counter values found 16 distinct passwords

The add table's 1, 2, and 3 appear twice, so the frame counter picks them twice as often as 0 or 4. The XOR values are 5-bit, so a scrambled character stays below 36.

Cited by: Castlevania II: Simon's Quest, Password and battery save

On entry Simon's Quest rejects a password with any of its 16 characters blank. Otherwise it subtracts the add from characters 1-14, XORs them, unpacks the 10 bytes, and accepts only when both bytes of the sum match the 7 state bytes again, the level is below 7, the body-parts and misc-items-2 bytes are below $80, misc items 1 is below $10, the whip level is below 5, both of the day's digits are 0-9, and laurels and garlics are each at most 8. A rejection writes MISS and ENTRY to the screen, plays sound $24, and after 128 frames brings back the ENTER YOUR PASSWORD prompt and returns to input, writing nothing to the entered characters on the way; an acceptance writes ENTRY COMPLETED. simons-quest-save/reject

CodeCastlevania II - Simon's Quest (USA) · 0x12D26-0x12D56 — bank 4 $AD16-$AD46, DecodePassword_VerifyIt: any 0 at $0540-$054F goes to the error; each value less 1 into $0530-$053F; the error sends string $59, sets the delay to $80, plays $24Castlevania II - Simon's Quest (USA) · 0x12E25-0x12E33 — bank 4 $AE15-$AE23, DecodePassword: read the scramble, subtract, XOR, unpack, checkCastlevania II - Simon's Quest (USA) · 0x12FB6-0x12FD6 — bank 4 $AFA6-$AFC6, DecodePassword_LastByte: XOR number from $053F's low 4 bits, add from $053F bit 4 and $053E bits 0-2Castlevania II - Simon's Quest (USA) · 0x12FA4-0x12FB5 — bank 4 $AF94-$AFA5, DecodePassword_DoSubbing: the add subtracted from $0530-$053DCastlevania II - Simon's Quest (USA) · 0x130EA-0x13214 — bank 4 $B0DA-$B204, DecodePassword_Convert6_to_8: the 5-bit values back into $0520-$0528Castlevania II - Simon's Quest (USA) · 0x12E6E-0x12EF0 — bank 4 $AE5E-$AEE0, DecodePassword_VerifyChecksum and its maxima $07, $FF, $FF, $80, $10, $80, $05: the sum recomputed and both bytes compared, level below 7, bytes 3-6 below their maxima, each day nibble below $0A, each laurel and garlic nibble below 9; on a pass, string $5A and sound $25Castlevania II - Simon's Quest (USA) · 0x12DC6-0x12DD5 — bank 4 $ADB6-$ADC5: count the delay down, send string $5B, set state 2Castlevania II - Simon's Quest (USA) · 0x12E0E-0x12E11 — bank 4 $ADFE-$AE01, where the error jumps: increment the state ($04EF), 3 to 4Castlevania II - Simon's Quest (USA) · 0x12AB2-0x12ACD — bank 4 $AAA2-$AABD, the screen's dispatch on $04EF: state 2 is $AB0C, the input handler, 3 is $AD16, the check, 4 is $ADB6, the waitCastlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · globals.inc#L327PasswordInputtedCode0 = $0540 ; $0540Castlevania II - Simon's Quest (USA) · 0x1C9FA-0x1CA13 — bank 7 $C9EA-$CA03, string $59: tiles spelling MISS, then ENTRY and two punctuation tilesCastlevania II - Simon's Quest (USA) · 0x1CA14-0x1CA2D — bank 7 $CA04-$CA1D, string $5A: ENTRY, then COMPLETEDCastlevania 2 - Bloodlust: a full disassembly and hack of Castlevania II: Simon's Quest · bank_7.asm#L1434-L1436 — the string labels, read here with the font in CHR bank 16 (characters).word (PPU_Text_StringsList_59_bank7_PasswordError) ;C9EA (1C9EA) ([8:8][A:9]) .word (PPU_Text_StringsList_5A_bank7_EntryCompleted) ;CA04 (1CA04) ([8:8][A:9]) .word (PPU_Text_StringsList_5B_bank7_EnterYourPassword) ;CA1E (1CA1E) ([8:8][A:9])Checked by claude, 2026-10-22 — disassembled each range in the pinned ROM and read the strings' tile numbers against CHR bank 16; from $AD35 to state 2 the path ($AD35-$AD46, $ADFE, $ADB6-$ADC5) has no store to $0540-$054F

The check reads only the decoded bytes: the decoder takes any add, 0-15, from characters 15 and 16, though the encoder writes only 0-4. A sum's high byte is at most 6, since 7 bytes sum to at most 1,785.

Cited by: Castlevania II: Simon's Quest, Password and battery save