This is the (6502) assembler algorithm that handles the checksum for Lufia & The Fortress of Doom (thanks to KingMike over at RHDN again for his help):
$00/9429 A0 FC 03 LDY #$03FC A:0070 X:616E Y:0000 $00/942C A6 1F LDX $1F [$00:001F] A:0070 X:616E Y:03FC $00/942E A9 02 65 LDA #$6502 A:0070 X:0000 Y:03FC $00/9431 18 CLC A:6502 X:0000 Y:03FC $00/9432 7D 08 00 ADC $0008,x[$70:0008] A:6502 X:0000 Y:03FC $00/9435 E8 INX A:4AE7 X:0000 Y:03FC $00/9436 E8 INX A:4AE7 X:0001 Y:03FC $00/9437 88 DEY A:4AE7 X:0002 Y:03FC $00/9438 D0 F7 BNE $F7 [$9431] A:4AE7 X:0002 Y:03FB $00/943A AA TAX A:592C X:07F8 Y:0000 $00/943B 7A PLY A:592C X:592C Y:0000 $00/943C AB PLB A:592C X:592C Y:0000 $00/943D 28 PLP A:592C X:592C Y:0000 $00/943E 60 RTS A:592C X:592C Y:0000Here is the same code again, with some comments I have added to make it a bit more readable (sorry, my 6502 assembler isn't very good, so feel free to let me know of any mistakes I have made):
A0 FC 03 LDY #$03FC ; load Y immediate with value 0x03FC A6 1F LDX $1F [$00:001F] ; load X with value at address 0x1F A9 02 65 LDA #$6502 ; load Accumulator with 0x6502 18 CLC ; clear the carry flag 7D 08 00 ADC $0008,x[$70:0008] ; add with carry starting at address 0x0008 E8 INX ; increment the X register E8 INX ; increment the X register (again) 88 DEY ; decrement the Y register D0 F7 BNE $F7 [$9431] ; repeat until Y hits 0 (branch not equal) AA TAX ; transfer the accumulator into X 7A PLY ; pull the Y register off of the stack AB PLB ; pulls a byte off of stack into data bank 28 PLP ; pull processor status off of the stack 60 RTS ; return from subroutineAnd I tell you that to tell you this...
I have written this small program below to give you a better feeling for how the checksum algorithm works for this game. I have included comments to try and make it as straight forward as possible (and yes, the while loop should be placed in a separate function with the proper variables just passed in to it -- I have coded it this way on purpose to show the program and logic flow here).
/* begin file -- main.cpp */// File name: main.cpp// Author: Vegetaman// Date: February 24, 2011// Purpose: Lufia Checksum#include <iostream>#include <fstream>#define SIZE_OF_SRAM 0x2000 // SRAM is 8K large#define LUFIA_SRAM_FILE "C:\\Lufia.srm" // SRAM file location#define HALF_OF_2K 0x03FC // half of 2048 - 8#define FILE00_OFFSET 0x0008 // 8 bytes in#define FILE01_OFFSET 0x0808 // 2K + 8 bytes in#define FILE02_OFFSET 0x1008 // 4K + 8 bytes in#define COUNTER_ROLLOVER_VALUE 0xFFFF // max 16-bit valueusing namespace std;int main(int argc, char *argv[]){ FILE *filePtr; // file pointer unsigned char ArrayOfSRAM[SIZE_OF_SRAM]; // array of char or bytes unsigned short accumulator; // is a 16-bit unsigned integer unsigned short register_x; // will be just like the register X unsigned short register_y; // will be just like the register Y filePtr = fopen(LUFIA_SRAM_FILE, "r"); // open file Lufia.srm -- read only // NOTE: I am reading the SRAM into an array so I don't have to do all of my // operations from the file, which would be really slow and wasteful... fread(ArrayOfSRAM, sizeof(char), SIZE_OF_SRAM, filePtr); // load the array fclose(filePtr); // always close your file handle // begin the routine of calculating the first 16-bit little endian checksum register_x = FILE00_OFFSET; // load the first offset into register X register_y = HALF_OF_2K; // load 0x03FC into register Y accumulator = 0x6502; // load the accumulator with the base value 6502 while(register_y != 0) // while Y does not equal 0 (will run 1020 times) { accumulator += ArrayOfSRAM[register_x]; // get the first/low byte register_x++; // increment X accumulator += ArrayOfSRAM[register_x] << 8; // get the second/high byte register_x++; // increment X accumulator &= 0xFFFF; // discarding the carry flag register_y--; // decrement Y } // some code to throw the checksum up on the console so that it can be seen cout << "Checksum for FILE00: " << dec << accumulator << endl; cout << "Little Endian HEX: " << hex << (accumulator & 0xFF) << " "; cout << hex << ((accumulator >> 8) & 0xFF) << endl << endl; // prepare to calculate the second little endian 16-bit checksum register_x = FILE01_OFFSET; // load the first offset into register X register_y = HALF_OF_2K; // load 0x03FC into register Y accumulator = 0x6502; // load the accumulator with the base value 6502 while(register_y != 0) // while Y does not equal 0 (will run 1020 times) { accumulator += ArrayOfSRAM[register_x]; // get the first/low byte register_x++; // increment X accumulator += ArrayOfSRAM[register_x] << 8; // get the second/high byte register_x++; // increment X accumulator &= 0xFFFF; // discarding the carry flag register_y--; // decrement Y } // some code to throw the checksum up on the console so that it can be seen cout << "Checksum for FILE01: " << dec << accumulator << endl; cout << "Little Endian HEX: " << hex << (accumulator & 0xFF) << " "; cout << hex << ((accumulator >> 8) & 0xFF) << endl << endl; // prepare to calculate the third and final little endian 16-bit checksum register_x = FILE02_OFFSET; // load the first offset into register X register_y = HALF_OF_2K; // load 0x03FC into register Y accumulator = 0x6502; // load the accumulator with the base value 6502 while(register_y != 0) // while Y does not equal 0 (will run 1020 times) { accumulator += ArrayOfSRAM[register_x]; // get the first/low byte register_x++; // increment X accumulator += ArrayOfSRAM[register_x] << 8; // get the second/high byte register_x++; // increment X accumulator &= 0xFFFF; // discarding the carry flag register_y--; // decrement Y } // now throw the last checksum up on the console so it can also be seen cout << "Checksum for FILE02: " << dec << accumulator << endl; cout << "Little Endian HEX: " << hex << (accumulator & 0xFF) << " "; cout << hex << ((accumulator >> 8) & 0xFF) << endl << endl; // now, hold the program up until the user is done reading... cout << "Press ENTER to continue..."; // prompt user getchar(); // wait for "ENTER" key return 0; // exit with success}/* end of file -- main.cpp */If you wish to run this code on your own platform (it should compile with Visual Studio/Visual C++.NET and run as a console program, work with Bloodshed Dev-C++, and gcc/gpp -- for all you Windows, Mac, and Linux fans out there respectively), make sure you make the following line of code match your file path for where your Lufia SRAM (.srm) file is located (tested with the [!] version of the game):
#define LUFIA_SRAM_FILE "C:\\Lufia.srm" // SRAM file locationHave a suggestion? Drop me a line at: vegetaman@gmail.com