Claim set · data/claims/dragon-warrior-save.yaml

Dragon Warrior's battery save (nmikstas's disassembly and the US ROM)

Game: Dragon Warrior.

Dragon Warrior saves the hero as a 32-byte record in battery-backed cartridge RAM; bytes 0-1 hold experience, 2-3 gold, 4-7 the inventory, 8 the magic keys (capped at 6), 9 the herbs, 10 the equipment, 11-13 spell, player, and story flags, 14-21 the name, 22 the message speed, 23 HP, 24 MP, 25 whether HP and MP are refilled at the start, 26-29 the filler byte $C8, and 30-31 a check value. dragon-warrior-save/record

About: Dragon Warrior Password and battery save

Value
{
 "bytes": 32,
 "fields": [
  {
   "bytes": "0-1",
   "what": "experience"
  },
  {
   "bytes": "2-3",
   "what": "gold"
  },
  {
   "bytes": "4-7",
   "what": "inventory slots"
  },
  {
   "bytes": "8",
   "what": "magic keys, at most 6"
  },
  {
   "bytes": "9",
   "what": "herbs"
  },
  {
   "bytes": "10",
   "what": "weapon, armor, and shield"
  },
  {
   "bytes": "11",
   "what": "upper spells and flags"
  },
  {
   "bytes": "12",
   "what": "player flags"
  },
  {
   "bytes": "13",
   "what": "story flags"
  },
  {
   "bytes": "14-21",
   "what": "name"
  },
  {
   "bytes": "22",
   "what": "message speed"
  },
  {
   "bytes": "23",
   "what": "HP"
  },
  {
   "bytes": "24",
   "what": "MP"
  },
  {
   "bytes": "25",
   "what": "start status: $78 refill, $AB keep"
  },
  {
   "bytes": "26-29",
   "what": "filler, $C8"
  },
  {
   "bytes": "30-31",
   "what": "CRC"
  }
 ]
}
CodeDragon Warrior (USA) (Rev 1) · 0x6-0x6 — header flags 6, $12: bit 1 set, battery-backed PRG RAM at $6000-$7FFFdragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9836-L9936SaveData: LFA18: LDY #$00 ;Zero out index. … LFA1E: INY ;Save player's experience points. … LFA26: STA (GameDatPtr),Y ;Save player's gold. … LFA46: CMP #$07 ;Does player have more than 6 keys? … LFA4A: LDA #$06 ;6 keys max. … LFA7D: LDA MessageSpeed ;Save player's message speed. …dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L10196-L10198LFBE3: LDY #$1E ;CRC is stored in bytes 31 and 32 of saved game data.dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Dragon_Warrior_Defines.asm#L658-L660.alias StartStatus1 $6045 ;#$78=Full HP and MP on start, #$AB=Do not restore, Save game 1.Checked by claude, 2026-10-22 — read SaveData byte by byte at the pinned commit (bank 3 is byte-identical to Rev 1, per the source record); read the Rev 1 header byte

nmikstas counts the check value's bytes from 1, as bytes 31 and 32; from 0 they are 30 and 31.

Cited by: Password and battery save

Dragon Warrior's check value is a 16-bit CRC of the record's first 30 bytes, read from byte 29 down to byte 0, one bit at a time from the top, with the polynomial $1021 and both bytes seeded with $1D. dragon-warrior-save/crc

About: Dragon Warrior Password and battery save

Value
{
 "width": 16,
 "polynomial": 4129,
 "seed": 7453,
 "bytes": "29 down to 0"
}
Codedragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L10209-L10220DoCRC: LFBEF: LDY #$1D ;30 bytes of saved game data. LFBF1: STY CRCLB ;Seed the LFSR. LFBF3: STY CRCUB ; LFBF5:* LDA (GameDatPtr),Y ;Loop until all saved data bytes are processed. LFBF7: STA GenByte3C ; LFBF9: JSR DoLFSR ;($FC2A)Put data through an LFSR. LFBFC: DEY ;dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L10261-L10284LFC2C: LDY #$08 ; LFC2E:* LDA CRCUB ; LFC30: EOR GenByte3C ; LFC32: ASL CRCLB ; LFC34: ROL CRCUB ; LFC36: ASL GenByte3C ; LFC38: ASL ;Some kind of linear feedback shift register I think. LFC39: BCC + ;The saved data is run though this function and a 16-bit LFC3B: LDA CRCLB ;CRC appears to be generated. LFC3D: EOR #$21 ; LFC3F: STA CRCLB ; LFC41: LDA CRCUB ; …Checked by claude, 2026-10-22 — read DoCRC and DoLFSR at the pinned commit and traced one bit: the top bit of the CRC's high byte XOR the data's top bit decides whether the shifted CRC is XORed with $1021

nmikstas's comment calls it "some kind of linear feedback shift register" that "appears" to make a CRC; the steps are those of the CRC-16 with polynomial $1021 (x^16 + x^12 + x^5 + 1), fed most significant bit first, with no final XOR. Calling it CRC-16-CCITT with a nonstandard seed is the atlas's reading.

Cited by: Dragon Warrior, Password and battery save

Dragon Warrior writes each save 10 times, one record after another, into its slot's 320 bytes; loading checks the copies in order, counts each one whose CRC fails, loads the first that passes, and rewrites all 10 from it, and if none passes the game marks the slot invalid (clearing its valid flag and mask bit, not its data) and says no deeds were recorded. dragon-warrior-save/ten-copies

About: Dragon Warrior Password and battery save

Value
{
 "copies": 10,
 "recordBytes": 32,
 "slotBytes": 320,
 "slots": 3
}
Codedragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Dragon_Warrior_Defines.asm#L662-L668.alias SavedGame1 $6068 ;Through $61A7. Save game slot 1. Data repeated 10 times. … .alias CRCFail1 $6432 ;CRC failures for save game 1.dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9798-L9832LF9F9: JSR SaveData ;($FA18)Save player's data to battery backed RAM. LF9FC: JSR GetCRC ;($FBE0)Get CRC for selected game data. … LFA0F: JSR Save10Times ;($FAA3)Save game data 10 times accross different addresses.dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9966-L10035LFAD0: LDX #$0A ;Prepare to copy game data to 10 different locations. … LFAD4: JSR CheckValidCRC ;($FB4A)Check if the CRC for the selected save game is valid. LFAD7: BCS CRCCheckFail ;Is data valid? If not, branch. LFAD9: JSR LoadSavedData ;($FB6B)Load save game data into game engine registers. … LFAE9: JSR Copy10Times ;($FB15)Copy of saved game data 10 times to same addresses. … LFAF3: LDX SaveNumber ;Increment CRC fail counter. LFAF6: INC CRCFail1,X ; … LFB0A: LDA #$FF ;Indicate saved game data cannot be successfully copied.The atlas reads this differently: it renames Copy100Times to CheckSaveCopies.dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9315-L9338LF781: JSR Copy100Times ;($FAC1)Make up to 100 copies of saved game to validate. LF784: CMP #$00 ;Is RAM for saved game valid? LF786: BEQ DoneValidateSave ;If so, branch to check next saved game. LF788: LDA SaveNumber ;Saved game RAM was corrupted. Erase game. LF78B: JSR ClearValidSaves ;($F80F)Clear ValidSave variable for selected game. … LF79E: JSR DoDialogHiBlock ;($C7C5)Unfortunately no deeds were recorded...The atlas reads this differently: Checks the slot's 10 copies in order and loads the first whose CRC passes, then rewrites all 10 from it; if none passes, the slot is marked invalid (its data stays).dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9455-L9460LF834: AND #$06 ;Clear LSB of saved game bit mask. LF836: LDX #$00 ;Clear ValidSave1. LF838: STX ValidSave1 ; … LF83B: STA SaveBitMask ;Update valid save game bit mask.Checked by claude, 2026-10-22 — read SaveCurrentGame, Save10Times, the copy-and-check loop nmikstas labels Copy100Times, and CheckValidSlot at the pinned commit

Despite its label, Copy100Times checks at most 10 copies. Clearing the slot means clearing its valid byte ($C8 marks a slot in use, ValidSave1-3); the 320 bytes are left as they were. The check runs for every slot in use before the list of saved games is shown, and again when a game is continued.

Cited by: Dragon Warrior, Password and battery save

Dragon Warrior marks its battery RAM as initialized with the 10 ASCII bytes KEN MASUTA, written both before and after the three save slots; before showing the saved games it checks the copy after the slots and then the one before, and only if neither matches does it erase all three slots, then rewrites both copies. dragon-warrior-save/signature

About: Dragon Warrior Password and battery save

Value
{
 "signature": "KEN MASUTA",
 "at": [
  24635,
  25640
 ]
}
Codedragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9242-L9300GetValidSaves: LF6F4: LDX #$09 ;Prepare to check for Ken Masuta's name in battery backed RAM. LF6F6:* LDA KenMasuta1,X ; LF6F9: CMP KenMasutaTbl,X ;Check if Ken's name is intact in the battery backed RAM. LF6FC: BNE InitSaves ;Is the name correct? If not, branch to invalidae all saved games. … LF706:* LDA KenMasuta2,X ; … LF70C: BNE ClearSaveGameRAM ;If not, RAM values are corrupt. Branch to rease saved games. … LF71A:* STA SavedGame1,X ;Clear first 256 bytes of save game data for all slots. … …dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L10321-L10323KenMasutaTbl: ; K E N _ M A S U T A LFC76: .byte $4B, $45, $4E, $20, $4D, $41, $53, $55, $54, $41dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Dragon_Warrior_Defines.asm#L657-L665.alias KenMasuta2 $603B ;Through $6044. Stores Ken Masuta's name. … .alias KenMasuta1 $6428 ;Through $6431. Stores Ken Masuta's name.dragon-warrior-disassembly: NES Dragon Warrior software disassembly · source_files/Bank03.asm#L9470-L9480LF853: BRK ;Load BG and sprite palettes for selecting saved game. … LF856: JSR GetValidSaves ;($F6F4)Get valid save game slots.Checked by claude, 2026-10-25 — read GetValidSaves (L9242-L9300) against the two KenMasuta copies and KenMasutaTbl, whose ten bytes are the ASCII codes of KEN MASUTA, and its call before the saved-game menu at LF856

The table's bytes are ASCII codes ($4B $45 $4E ...), not the game's own tile codes. nmikstas reads the string as a name; who Ken Masuta is isn't sourced here. Erasing also zeroes the valid bytes, the start statuses, and the CRC failure counts.

Cited by: Password and battery save