------------------------------------------------------------------------------- | Dragon Warrior (NES) SRAM Document 1.0 | by John David Ratliff | | The most recent version of this guide can always be found at | http://games.technoplaza.net/dwsrame/sram-doc.txt | | Copyright (C) 2007 emuWorks (http://games.technoplaza.net/) | Permission is granted to copy, distribute and/or modify this document | under the terms of the GNU Free Documentation License, Version 1.2 | or any later version published by the Free Software Foundation; | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover | Texts. A copy of the license can be found at | http://www.gnu.org/licenses/fdl.html ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- | Table of Contents ------------------------------------------------------------------------------- - 1.0 Introduction - 2.0 Copyright Notice - 3.0 Revision History - 4.0 The Dragon Warrior SRAM - 4.1 SRAM Basics - 4.2 SRAM Offsets - 4.3 The Sanity Algorithm - 4.4 Checksum Bypass Using a Game Genie - 5.0 dwsrame - The Dragon Warrior (NES) SRAM Editor - 6.0 Contact Information ------------------------------------------------------------------------------- | 1.0 Introduction ------------------------------------------------------------------------------- This document is a guide to the SRAM format used by Dragon Warrior for the original Nintendo (NES). SRAM (short for save random access memory, and has many other names and acronyms) was the format for saving data in most NES cartridge games including Dragon Warrior. SRAM was an area of memory that was supplied power by an internal cartridge battery. It's purpose was to preserve information even when the game was not running. In this manner, game progress could be preserved even when the system was not active. SRAM is not the same as emulator save states. SRAM was internal to the game cartridge and thus the format is applicable to any NES emulator (and probably all NES copiers as well). This document aims to discuss the data format used in SRAM to store game progress. There have been many adaptations, remakes, and translations of Dragon Warrior over the years. To be clear, this guide covers the SRAM used by the original Dragon Warrior for the NES (the original English translation of the original Dragon Quest for the Famicom) released by Enix in 1989. ------------------------------------------------------------------------------- | 2.0 Copyright Notice ------------------------------------------------------------------------------- This document is Copyright (C) 2007 emuWorks (http://games.technoplaza.net/) Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at http://www.gnu.org/licenses/fdl.html Basically, it is free documentation in much the same way software under the GNU General Public License is free software. You can modify it, redistribute it, sell it, publish it, etc. ------------------------------------------------------------------------------- | 3.0 Revision History ------------------------------------------------------------------------------- Version 1.0 - Friday, February 2, 2007 - First Public Release ------------------------------------------------------------------------------- | 4.0 Dragon Warrior SRAM ------------------------------------------------------------------------------- This section details the SRAM format used by Dragon Warrior (NES). ------------------------------------------------------------------------------- | 4.1 SRAM Basics ------------------------------------------------------------------------------- The Dragon Warrior cartridge had an SRAM of 0x2000 (8192 decimal) bytes. This was the standard for NES cartridges of the time. There were three possible save slots available in the game. Each of these slots used 0x140 bytes of SRAM, though in reality it is actually just 0x20 bytes repeated 7 times. Because the SRAM was battery backed, and the battery will eventually die, the game also stored certain data to act as a check on the sanity of the preserved data. Two checks were used. The first was the data replication. The 0x20 bytes of game data are replicated 7 times. Secondly, the 0x20 bytes of data also have some embedded sanity data, which I will call the checksum. Finally, there are 4 bytes in the SRAM that determines what save slots are in use. The three games begin at offset 0x68 from the start of the SRAM. From there, the three game's 0x140 bytes of data are stored sequentially. The 4 bytes of save slot usage start at offset 0x35 from the start of SRAM. ------------------------------------------------------------------------------- | 4.2 SRAM Offsets ------------------------------------------------------------------------------- The following are the known offsets within the game data. They will be presented relative to the start of a particular game slot rather than from the start of SRAM. This means an offset of 0 for the first game is actually at the SRAM position 0x68. For the duplication, this means it is also as 0x88, 0xA8, and so on for all 6 duplicate sets. 00-01: Hero's Experience 02-03: Hero's Gold Both of these values are little-endian 16-bit unsigned values. This means they can range from 0 - 65535. It also means the bytes when viewed as hex should be reversed. ex: 34245 decimal = 85C5 hex = C585 little-endian hex. 04-07: Hero's Inventory The hero's inventory is comprised of at most 8 items. Because of the limited number of possible items, each item can be represented in a half-byte (4 bits). This means each byte can represent two numbers. The item values are as follows: 0: No Item 1: Torch 2: Fairy Water 3: Wings 4: Dragon's Scale 5: Fairy Flute 6: Fighter's Ring 7: Erdrick's Token 8: Gwaelin's Love 9: Cursed Belt A: Silver Harp B: Death Necklace C: Stones of Sunlight D: Staff of Rain E: Rainbow Drop F: (unused) displays current number of Herbs, but you cannot use this So, if 04 had the value E3, the hero will have the Rainbow Drop and a set of Wings in his inventory. 08: Hero's Keys 09: Hero's Herbs These two bytes tell how many of these particular items the hero has. Valid values are 0 - 6. Using values outside the valid range is untested. 0A: Equipment Byte The equipment byte detemines the hero's weaponry. To decide what the hero is equipped with, you simply need to add the possible values together. Weapons: Bamboo Pole: 0x20 Club: 0x40 Copper Sword: 0x60 Hand Axe: 0x80 Broad Sword: 0xA0 Flame Sword: 0xC0 Erdrick's Sword: 0xE0 Armor: Clothes: 0x04 Leather Armor: 0x08 Chain Mail: 0x0C Half Plate Armor: 0x10 Full Plate Armor: 0x14 Magic Armor: 0x18 Erdrick's Armor: 0x1C Shields: Small Shield: 0x01 Large Shield: 0x02 Silver Shield: 0x03 So, if you want the best equipment possible, add the value of Erdrick's Sword (0xE0), Erdrick's Armor (0x1C), and the Silver Shield (0x3) to get an equipment byte value of 0xFF. 0B: Quest byte 1 0C: Quest byte 2 0D: Quest byte 3 Quest bytes determine whether certain things have happened in the game or not. Things not covered by the quest bytes are usually determined by the hero's inventory. For example, if you have the staff of rain, the old man who gives it to you will tell you to go away. The quest bytes use individual bits to determine various quest markers. The bits not documented are not known to do anything. Byte 1: bit 2 - Charlock Hidden Stairs Revealed bit 3 - Rainbow Bridge Built bit 4 - Wearing Dragon's Scale bit 5 - Wearing Fighter's Ring bit 6 - Wearing Cursed Belt bit 7 - Wearing Death Necklace Byte 2: bit 0 - Gwaelin Rescued (in Hero's arms) bit 1 - Gwealin on Throne (bits 1 and 0 should be mutually exclusive) bit 3 - Started Quest Byte 3: bit 1 - Golem Killed bit 2 - Dragonlord Killed bit 6 - Green Dragon Guarding Princess Gwaelin Killed 0E-15: Hero's Name The hero's name is stored in a very odd manner. The first four characters 0E-11 are the first four characters of the name, but they are stored in reverse order. In other words, if your characters name is 'Loki', it will be stored ikoL. The second four characters 12-15 are the second four characters of the name, and they too are stored in reverse order. Here are the values for the Dragon Warrior alphabet. 0x00 - 0x09 : The numbers 0-9 0x0A - 0x23 : The lowercase letters 'a' - 'z' 0x24 - 0x3D : The uppercase letters 'A' - 'Z' 0x40 : The apostrophe ' character 0x47 : The period . character 0x48 : The comma , character 0x49 : The dash - character 0x4B : The question mark ? character 0x4C : The exclamation point ! character 0x4E : The close paren ) character 0x4F : The open paren ( character 0x60 : The space ' ' character These are the only valid characters accepted by the name input screen. Using other values may produce other symbols in the name, though this is untested. 16: Message Speed This offset determines the message speed for the game. Valid values are 0 for fast, 1 for normal, and 2 for slow. 17: Hero's HP 18: Hero's MP The HP and MP are the current HP and MP for the hero. The valid range is 0 - 255. 19: (unused) always AB 1A-1D: (unused) always C8 1E-1F: Checksum The checksum is a two-byte value. It will be described in the next section. Finally, the four bytes that determine game slot usage are as follows: 0x35, 0x36, and 0x37 - 0 if the slot is unused, C8 if it is 0x38 - Three bits determine game usage bit 0 - slot 1 bit 1 - slot 2 bit 2 - slot 3 So, if all three slots are in use, you will see 0xC8 0xC8 0xC8 0x07 for these four bytes. These offsets are relative to the start of SRAM, not the start of game data. ------------------------------------------------------------------------------- | 4.3 The Sanity Algorithm ------------------------------------------------------------------------------- As mentioned earlier, the game needs to ensure the data stored in the SRAM is still valid. To do this, it uses data replication and a checksum word. This word must be generated by us if you expect the game to accept modified data. Here is the sanity algorithm, ripped from the Dragon Warrior ROM in 6502 assembly. I have added comments at the end of each line. $FBEF:A0 1D LDY #$1D ; load counter with 0x1D $FBF1:84 94 STY $0094 ; init checksum low byte $FBF3:84 95 STY $0095 ; init checksum high byte $FBF5:B1 22 LDA ($22),Y ; load data[counter] into a $FBF7:85 3C STA $003C ; store to memory $FBF9:20 2A FC JSR $FC2A ; jump to subroutine $FBFC:88 DEY ; y = y - 1 $FBFD:10 F6 BPL $FBF5 ; repeat 0x1D + 1 times $FBFF:60 RTS ; end of checksum algorithm $FC2A:98 TYA ; put counter into a $FC2B:48 PHA ; push counter to stack $FC2C:A0 08 LDY #$08 ; load new counter with 8 $FC2E:A5 95 LDA $0095 ; load checksum high byte $FC30:45 3C EOR $003C ; xor data[counter] with checksum high byte $FC32:06 94 ASL $0094 ; shift left checksum low byte $FC34:26 95 ROL $0095 ; rotate shifted bit onto checksum high byte $FC36:06 3C ASL $003C ; shift left data[counter] $FC38:0A ASL ; shift left original checksum high byte $FC39:90 0C BCC $FC47 ; skip if shifted bit was 0 $FC3B:A5 94 LDA $0094 ; load checksum low byte $FC3D:49 21 EOR #$21 ; xor checksum low byte with 0x21 $FC3F:85 94 STA $0094 ; store into checksum low byte $FC41:A5 95 LDA $0095 ; load checksum high byte $FC43:49 10 EOR #$10 ; xor checksum high with 0x10 $FC45:85 95 STA $0095 ; store checksum high byte $FC47:88 DEY ; y = y - 1 $FC48:D0 E4 BNE $FC2E ; repeat 8 times $FC4A:68 PLA ; pull counter from stack $FC4B:A8 TAY ; restore counter to y $FC4C:60 RTS ; return It is a simple process, though fairly cumbersome for anyone to duplicate by hand. I would recommend either checksum bypass with a game genie or using dwsrame to fix the checksum for you. Here is the C++ conversion of the assembly routine used in dwsrame. wxUint16 SRAMFile::checksum(int game) const { wxASSERT((game >= 0) && (game < 3)); unsigned char cl = 0x1D, ch = 0x1D, carry = 0; unsigned char al, bl, temp; for (int i = 0x1D; i >= 0; --i) { al = sram[GAME_OFFSET + (game * GAME_SIZE) + i]; for (int j = 8; j > 0; --j) { bl = al ^ ch; // asl cl carry = (cl & 0x80) ? 1 : 0; cl <<= 1; // rol ch temp = (ch & 0x80) ? 1 : 0; ch = (ch << 1) | carry; carry = temp; // asl al carry = (al & 0x80) ? 1 : 0; al <<= 1; // asl bl carry = (bl & 0x80) ? 1 : 0; bl <<= 1; if (carry) { cl ^= 0x21; ch ^= 0x10; } } } return (cl | (ch << 8)); } If you know basic arithmetic and binary operations, the algorithm should look pretty straightforwawrd. I will detail the operations real quick. We start with two counters. I will refer to these as checksum high and checksum low. They are unsigned bytes (range 0 - 255) and can be represented in 8 bits. Assign these two counters the initial value 0x1D. Starting at the end of the game data, which is at offset 0x1D from the start of a game slot, we do the following for each game byte from end to beginning. Grab the next byte of game data. I will refer to this as al. Do the following 8 times (once for each bit of the game data byte). Compute the exclusive OR of the game byte with checksum high. Shift the checksum low byte 1 bit left (this may result in a carry if the high bit was 1 - do not discard this bit). Shift the checksum high byte left 1 bit. If a carry resulted from the checksum low shift, that bit should become the new low bit of checksum high. As with the first shift, do not discard the high bit shifted off of checksum high. Now shift left 1 bit the game data byte (al). If a carry results in this shift, it will replace any former carry. Now shift left 1 bit the exclusive OR we calculated earlier. If a carry results, it will replace any former carry. If we have a carry, then we XOR checksum low with 0x21 and XOR checksum high with 0x10. Repeat as directed. It's certainly an annoying process to do by hand, but not a complicated one. I would recommend you avoid doing this by hand though. The next section has a method for bypassing the checksum if you have a game genie or an emulator that supports game genie codes. ------------------------------------------------------------------------------- | 4.4 Checksum Bypass Using a Game Genie ------------------------------------------------------------------------------- If you just want to poke around in the game data and change things without worrying too much about the checksum, one method you can use is a game genie. We can use a game genie code to bypass the sanity check. APVYNUAU will bypass the game sanity check and allow any modified SRAM data to work. What will happen if the data is invalid is unknown. ------------------------------------------------------------------------------- | 5.0 dwsrame - The Dragon Warrior (NES) SRAM Editor ------------------------------------------------------------------------------- If you really want to edit the SRAM, I recommend a program called 'dwsrame', the Dragon Warrior (NES) SRAM Editor. Not surprisingly, I wrote it. It will edit any of the offsets I have outlined in this document an will keep fix the sanity values for you so you don't need to. It's far simpler than trying to edit by hand. If you want to try it out, head over to http://games.technoplaza.net/dwsrame/ It's free software under the GNU GPL, tested in Windows, Linux, and Mac OS X, and is likely to run on almost any unix that support GTK+. ------------------------------------------------------------------------------- | 6.0 Contact Information ------------------------------------------------------------------------------- The author (John Ratliff) can be contacted at webmaster [AT] technoplaza [DOT] net. Replace as necessary. I can also be reached via an online feedback form at http://www.technoplaza.net/feedback.php