]> Shamusworld >> Repos - virtualjaguar/blob - src/file.cpp
00ef5f3eff749632b11b3d9618784111167bc92c
[virtualjaguar] / src / file.cpp
1 //
2 // FILE.CPP
3 //
4 // File support
5 // by James Hammons
6 // (C) 2010 Underground Software
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  -------------------------------------------------------------
12 // JLH  01/16/2010  Created this log ;-)
13 // JLH  02/28/2010  Added functions to look inside .ZIP files and handle contents
14 // JLH  06/01/2012  Added function to check ZIP file CRCs against file DB
15 //
16
17 #include "file.h"
18
19 #include <stdarg.h>
20 #include <string.h>
21 #include "crc32.h"
22 #include "filedb.h"
23 #include "eeprom.h"
24 #include "jaguar.h"
25 #include "log.h"
26 #include "memory.h"
27 #include "universalhdr.h"
28 #include "unzip.h"
29 #include "zlib.h"
30
31 // Private function prototypes
32
33 static int gzfilelength(gzFile gd);
34 static bool CheckExtension(const char * filename, const char * ext);
35 //static int ParseFileType(uint8_t header1, uint8_t header2, uint32_t size);
36
37 // Private variables/enums
38
39
40 //
41 // Generic ROM loading
42 //
43 uint32_t JaguarLoadROM(uint8_t * &rom, char * path)
44 {
45 // We really should have some kind of sanity checking for the ROM size here to prevent
46 // a buffer overflow... !!! FIX !!!
47 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
48         uint32_t romSize = 0;
49
50         WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
51         char * ext = strrchr(path, '.');
52
53         // No filename extension == YUO FAIL IT (it is loading the file).
54         // This is naive, but it works. But should probably come up with something a little
55         // more robust, to prevent problems with dopes trying to exploit this.
56         if (ext == NULL)
57         {
58                 WriteLog("FAILED!\n");
59                 return 0;
60         }
61
62         WriteLog("Succeeded in finding extension (%s)!\n", ext);
63         WriteLog("VJ: Loading \"%s\"...", path);
64
65         if (strcasecmp(ext, ".zip") == 0)
66         {
67                 // Handle ZIP file loading here...
68                 WriteLog("(ZIPped)...");
69
70 //              uint8_t * buffer = NULL;
71 //              romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
72                 romSize = GetFileFromZIP(path, FT_SOFTWARE, rom);
73
74                 if (romSize == 0)
75                 {
76                         WriteLog("Failed!\n");
77                         return 0;
78                 }
79
80 //              memcpy(rom, buffer, romSize);
81 //              delete[] buffer;
82         }
83         else
84         {
85                 // Handle gzipped files transparently [Adam Green]...
86
87                 gzFile fp = gzopen(path, "rb");
88
89                 if (fp == NULL)
90                 {
91                         WriteLog("Failed!\n");
92                         return 0;
93                 }
94
95                 romSize = gzfilelength(fp);
96                 rom = new uint8_t[romSize];
97                 gzseek(fp, 0, SEEK_SET);
98                 gzread(fp, rom, romSize);
99                 gzclose(fp);
100         }
101
102         WriteLog("OK (%i bytes)\n", romSize);
103
104         return romSize;
105 }
106
107
108 //
109 // Jaguar file loading
110 // We do a more intelligent file analysis here instead of relying on (possible false)
111 // file extensions which people don't seem to give two shits about anyway. :-(
112 //
113 bool JaguarLoadFile(char * path)
114 {
115         uint8_t * buffer = NULL;
116         jaguarROMSize = JaguarLoadROM(buffer, path);
117
118         if (jaguarROMSize == 0)
119         {
120                 // It's up to the GUI to report errors, not us. :-)
121                 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
122                 return false;
123         }
124
125         jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
126         WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
127 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
128 //       directory, copy the one from the ZIP file, if it exists.
129         EepromInit();
130         jaguarRunAddress = 0x802000;                                    // For non-BIOS runs, this is true
131         int fileType = ParseFileType(buffer, jaguarROMSize);
132         jaguarCartInserted = false;
133
134         if (fileType == JST_ROM)
135         {
136                 jaguarCartInserted = true;
137                 memcpy(jagMemSpace + 0x800000, buffer, jaguarROMSize);
138 // Checking something...
139 jaguarRunAddress = GET32(jagMemSpace, 0x800404);
140 WriteLog("FILE: Cartridge run address is reported as $%X...\n", jaguarRunAddress);
141                 delete[] buffer;
142                 return true;
143         }
144         else if (fileType == JST_ALPINE)
145         {
146                 // File extension ".ROM": Alpine image that loads/runs at $802000
147                 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
148                 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
149                 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
150                 delete[] buffer;
151
152 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
153                 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
154                 // This kludge works! Yeah!
155                 SET32(jaguarMainRAM, 0x10, 0x00001000);
156                 SET16(jaguarMainRAM, 0x1000, 0x60FE);           // Here: bra Here
157                 return true;
158         }
159         else if (fileType == JST_ABS_TYPE1)
160         {
161                 // For ABS type 1, run address == load address
162                 uint32_t loadAddress = GET32(buffer, 0x16),
163                         codeSize = GET32(buffer, 0x02) + GET32(buffer, 0x06);
164                 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
165                 memcpy(jagMemSpace + loadAddress, buffer + 0x24, codeSize);
166                 delete[] buffer;
167                 jaguarRunAddress = loadAddress;
168                 return true;
169         }
170         else if (fileType == JST_ABS_TYPE2)
171         {
172                 uint32_t loadAddress = GET32(buffer, 0x28), runAddress = GET32(buffer, 0x24),
173                         codeSize = GET32(buffer, 0x18) + GET32(buffer, 0x1C);
174                 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
175                 memcpy(jagMemSpace + loadAddress, buffer + 0xA8, codeSize);
176                 delete[] buffer;
177                 jaguarRunAddress = runAddress;
178                 return true;
179         }
180         // NB: This is *wrong*
181         /*
182         Basically, if there is no "JAG" at position $1C, then the long there is the load/start
183         address in LITTLE ENDIAN.
184         If "JAG" is present, the the next character ("R" or "L") determines the size of the
185         JagServer command (2 bytes vs. 4). Following that are the commands themselves;
186         typically it will either be 2 (load) or 3 (load & run). Command headers go like so:
187         2:
188         Load address (long)
189         Length (long)
190         payload
191         3:
192         Load address (long)
193         Length (long)
194         Run address (long)
195         payload
196         5: (Reset)
197         [command only]
198         7: (Run at address)
199         Run address (long)
200         [no payload]
201         9: (Clear memory)
202         Start address (long)
203         End address (long)
204         [no payload]
205         10: (Poll for commands)
206         [command only]
207         12: (Load & run user program)
208         filname, terminated with NULL
209         [no payload]
210         $FFFF: (Halt)
211         [no payload]
212         */
213         else if (fileType == JST_JAGSERVER)
214         {
215                 // This kind of shiaut should be in the detection code below...
216                 // (and now it is! :-)
217 //              if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
218 //              {
219                         // Still need to do some checking here for type 2 vs. type 3. This assumes 3
220                         // Also, JAGR vs. JAGL (word command size vs. long command size)
221                         uint32_t loadAddress = GET32(buffer, 0x22), runAddress = GET32(buffer, 0x2A);
222                         WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress, jaguarROMSize - 0x2E);
223                         memcpy(jagMemSpace + loadAddress, buffer + 0x2E, jaguarROMSize - 0x2E);
224                         delete[] buffer;
225                         jaguarRunAddress = runAddress;
226
227 // Hmm. Is this kludge necessary?
228 SET32(jaguarMainRAM, 0x10, 0x00001000);         // Set Exception #4 (Illegal Instruction)
229 SET16(jaguarMainRAM, 0x1000, 0x60FE);           // Here: bra Here
230
231                         return true;
232 //              }
233 //              else // Special WTFOMGBBQ type here...
234 //              {
235 //                      uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
236 //                      WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
237 //                      memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
238 //                      delete[] buffer;
239 //                      jaguarRunAddress = loadAddress;
240 //                      return true;
241 //              }
242         }
243         else if (fileType == JST_WTFOMGBBQ)
244         {
245                 uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
246                 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
247                 memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
248                 delete[] buffer;
249                 jaguarRunAddress = loadAddress;
250                 return true;
251         }
252
253         // We can assume we have JST_NONE at this point. :-P
254         WriteLog("FILE: Failed to load headerless file.\n");
255         return false;
256 }
257
258
259 //
260 // "Alpine" file loading
261 // Since the developers were coming after us with torches and pitchforks, we
262 // decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U
263 // O_O
264 //
265 bool AlpineLoadFile(char * path)
266 {
267         uint8_t * buffer = NULL;
268         jaguarROMSize = JaguarLoadROM(buffer, path);
269
270         if (jaguarROMSize == 0)
271         {
272                 // It's up to the GUI to deal with failure, not us. ;-)
273                 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path);
274                 return false;
275         }
276
277         jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
278         WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
279         EepromInit();
280
281         jaguarRunAddress = 0x802000;
282
283         WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize);
284
285         memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
286         memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
287         delete[] buffer;
288
289 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
290         // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
291         // This kludge works! Yeah!
292         SET32(jaguarMainRAM, 0x10, 0x00001000);         // Set Exception #4 (Illegal Instruction)
293         SET16(jaguarMainRAM, 0x1000, 0x60FE);           // Here: bra Here
294
295         return true;
296 }
297
298
299 //
300 // Get the length of a (possibly) gzipped file
301 //
302 static int gzfilelength(gzFile gd)
303 {
304    int size = 0, length = 0;
305    unsigned char buffer[0x10000];
306
307    gzrewind(gd);
308
309    do
310    {
311       // Read in chunks until EOF
312       size = gzread(gd, buffer, 0x10000);
313
314       if (size <= 0)
315         break;
316
317       length += size;
318    }
319    while (!gzeof(gd));
320
321    gzrewind(gd);
322    return length;
323 }
324
325
326 //
327 // Compare extension to passed in filename. If equal, return true; otherwise false.
328 //
329 static bool CheckExtension(const uint8_t * filename, const char * ext)
330 {
331         // Sanity checking...
332         if ((filename == NULL) || (ext == NULL))
333                 return false;
334
335         const char * filenameExt = strrchr((const char *)filename, '.');        // Get the file's extension (if any)
336
337         if (filenameExt == NULL)
338                 return false;
339
340         return (strcasecmp(filenameExt, ext) == 0 ? true : false);
341 }
342
343
344 //
345 // Get file from .ZIP
346 // Returns the size of the file inside the .ZIP file that we're looking at
347 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
348 //       Which means we have to deallocate it later.
349 //
350 uint32_t GetFileFromZIP(const char * zipFile, FileType type, uint8_t * &buffer)
351 {
352 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
353 //       size of the Jaguar console.
354 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
355         const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
356 //      ZIP * zip = openzip(0, 0, zipFile);
357         FILE * zip = fopen(zipFile, "rb");
358
359         if (zip == NULL)
360         {
361                 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
362                 return 0;
363         }
364
365 //      zipent * ze;
366         ZipFileEntry ze;
367         bool found = false;
368
369         // The order is here is important: If the file is found, we need to short-circuit the
370         // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
371 //      while (!found && readzip(zip))
372         while (!found && GetZIPHeader(zip, ze))
373         {
374 //              ze = &zip->ent;
375
376                 // Here we simply rely on the file extension to tell the truth, but we know
377                 // that extensions lie like sons-a-bitches. So this is naive, we need to do
378                 // something a little more robust to keep bad things from happening here.
379 #warning "!!! Checking for image by extension can be fooled !!!"
380                 if ((type == FT_LABEL) && (CheckExtension(ze.filename, ".png") || CheckExtension(ze.filename, ".jpg") || CheckExtension(ze.filename, ".gif")))
381                 {
382                         found = true;
383                         WriteLog("FILE: Found image file '%s'.\n", ze.filename);
384                 }
385
386                 if ((type == FT_SOFTWARE) && (CheckExtension(ze.filename, ".j64")
387                         || CheckExtension(ze.filename, ".rom") || CheckExtension(ze.filename, ".abs")
388                         || CheckExtension(ze.filename, ".cof") || CheckExtension(ze.filename, ".coff")
389                         || CheckExtension(ze.filename, ".jag")))
390                 {
391                         found = true;
392                         WriteLog("FILE: Found software file '%s'.\n", ze.filename);
393                 }
394
395                 if ((type == FT_EEPROM) && (CheckExtension(ze.filename, ".eep") || CheckExtension(ze.filename, ".eeprom")))
396                 {
397                         found = true;
398                         WriteLog("FILE: Found EEPROM file '%s'.\n", ze.filename);
399                 }
400
401                 if (!found)
402                         fseek(zip, ze.compressedSize, SEEK_CUR);
403         }
404
405         uint32_t fileSize = 0;
406
407         if (found)
408         {
409                 WriteLog("FILE: Uncompressing...");
410 // Insert file size sanity check here...
411                 buffer = new uint8_t[ze.uncompressedSize];
412
413 //              if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)
414 //              if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)
415                 if (UncompressFileFromZIP(zip, ze, buffer) == 0)
416                 {
417                         fileSize = ze.uncompressedSize;
418                         WriteLog("success! (%u bytes)\n", fileSize);
419                 }
420                 else
421                 {
422                         delete[] buffer;
423                         buffer = NULL;
424                         WriteLog("FAILED!\n");
425                 }
426         }
427         else
428                 // Didn't find what we're looking for...
429                 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
430
431 //      closezip(zip);
432         fclose(zip);
433         return fileSize;
434 }
435
436
437 uint32_t GetFileDBIdentityFromZIP(const char * zipFile)
438 {
439         FILE * zip = fopen(zipFile, "rb");
440
441         if (zip == NULL)
442         {
443                 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
444                 return 0;
445         }
446
447         ZipFileEntry ze;
448
449         // Loop through all files in the zip file under consideration
450         while (GetZIPHeader(zip, ze))
451         {
452                 // & loop through all known CRC32s in our file DB to see if it's there!
453                 uint32_t index = 0;
454
455                 while (romList[index].crc32 != 0xFFFFFF)
456                 {
457                         if (romList[index].crc32 == ze.crc32)
458                         {
459                                 fclose(zip);
460                                 return index;
461                         }
462
463                         index++;
464                 }
465
466                 // We didn't find it, so skip the compressed data...
467                 fseek(zip, ze.compressedSize, SEEK_CUR);
468         }
469
470         fclose(zip);
471         return -1;
472 }
473
474
475 bool FindFileInZIPWithCRC32(const char * zipFile, uint32_t crc)
476 {
477         FILE * zip = fopen(zipFile, "rb");
478
479         if (zip == NULL)
480         {
481                 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
482                 return 0;
483         }
484
485         ZipFileEntry ze;
486
487         // Loop through all files in the zip file under consideration
488         while (GetZIPHeader(zip, ze))
489         {
490                 if (ze.crc32 == crc)
491                 {
492                         fclose(zip);
493                         return true;
494                 }
495
496                 fseek(zip, ze.compressedSize, SEEK_CUR);
497         }
498
499         fclose(zip);
500         return false;
501 }
502
503
504 //
505 // Parse the file type based upon file size and/or headers.
506 //
507 uint32_t ParseFileType(uint8_t * buffer, uint32_t size)
508 {
509         // Check headers first...
510
511         // ABS/COFF type 1
512         if (buffer[0] == 0x60 && buffer[1] == 0x1B)
513                 return JST_ABS_TYPE1;
514
515         // ABS/COFF type 2
516         if (buffer[0] == 0x01 && buffer[1] == 0x50)
517                 return JST_ABS_TYPE2;
518
519         // Jag Server & other old shite
520         if (buffer[0] == 0x60 && buffer[1] == 0x1A)
521         {
522                 if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
523                         return JST_JAGSERVER;
524                 else
525                         return JST_WTFOMGBBQ;
526         }
527
528         // And if that fails, try file sizes...
529
530         // If the file size is divisible by 1M, we probably have an regular ROM.
531         // We can also check our CRC32 against the internal ROM database to be sure.
532         // (We also check for the Memory Track cartridge size here as well...)
533         if ((size % 1048576) == 0 || size == 131072)
534                 return JST_ROM;
535
536         // If the file size + 8192 bytes is divisible by 1M, we probably have an
537         // Alpine format ROM.
538         if (((size + 8192) % 1048576) == 0)
539                 return JST_ALPINE;
540
541         // Headerless crap
542         return JST_NONE;
543 }
544
545 //
546 // Check for universal header
547 //
548 bool HasUniversalHeader(uint8_t * rom, uint32_t romSize)
549 {
550         // Sanity check
551         if (romSize < 8192)
552                 return false;
553
554         for(int i=0; i<8192; i++)
555                 if (rom[i] != universalCartHeader[i])
556                         return false;
557
558         return true;
559 }
560
561 #if 0
562 // Misc. doco
563
564 /*
565 Stubulator ROM vectors...
566 handler 001 at $00E00008
567 handler 002 at $00E008DE
568 handler 003 at $00E008E2
569 handler 004 at $00E008E6
570 handler 005 at $00E008EA
571 handler 006 at $00E008EE
572 handler 007 at $00E008F2
573 handler 008 at $00E0054A
574 handler 009 at $00E008FA
575 handler 010 at $00000000
576 handler 011 at $00000000
577 handler 012 at $00E008FE
578 handler 013 at $00E00902
579 handler 014 at $00E00906
580 handler 015 at $00E0090A
581 handler 016 at $00E0090E
582 handler 017 at $00E00912
583 handler 018 at $00E00916
584 handler 019 at $00E0091A
585 handler 020 at $00E0091E
586 handler 021 at $00E00922
587 handler 022 at $00E00926
588 handler 023 at $00E0092A
589 handler 024 at $00E0092E
590 handler 025 at $00E0107A
591 handler 026 at $00E0107A
592 handler 027 at $00E0107A
593 handler 028 at $00E008DA
594 handler 029 at $00E0107A
595 handler 030 at $00E0107A
596 handler 031 at $00E0107A
597 handler 032 at $00000000
598
599 Let's try setting up the illegal instruction vector for a stubulated jaguar...
600
601                 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
602                 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
603                 SET32(jaguar_mainRam, 0x10, 0x00E008E6);        // <-- Should be here (it is)...
604                 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
605
606 /*
607 ABS Format sleuthing (LBUGDEMO.ABS):
608
609 000000  60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
610 000010  12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
611 000020  00 00 40 00
612
613 DRI-format file detected...
614 Text segment size = 0x0000050c bytes
615 Data segment size = 0x000462c0 bytes
616 BSS Segment size = 0x00000428 bytes
617 Symbol Table size = 0x000012a6 bytes
618 Absolute Address for text segment = 0x00802000
619 Absolute Address for data segment = 0x0080250c
620 Absolute Address for BSS segment = 0x00004000
621
622 (CRZDEMO.ABS):
623 000000  01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
624 000010  00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
625 000020  00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
626
627 000030  2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
628 000040  00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
629 000050  00 00 00 00 00 00 00 20
630 000058  2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
631 000068  00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
632 000078  00 00 00 00 00 00 00 40
633 000080  2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
634 000090  00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
635 0000a0  00 00 00 00 00 00 00 80
636
637 Header size is $A8 bytes...
638
639 BSD/COFF format file detected...
640 3 sections specified
641 Symbol Table offset = 230160                            ($00038310)
642 Symbol Table contains 1339 symbol entries       ($0000053B)
643 The additional header size is 28 bytes          ($001C)
644 Magic Number for RUN_HDR = 0x00000107
645 Text Segment Size = 7632                                        ($00001DD0)
646 Data Segment Size = 222360                                      ($00036498)
647 BSS Segment Size = 428928                                       ($00068B80)
648 Starting Address for executable = 0x00802000
649 Start of Text Segment = 0x00802000
650 Start of Data Segment = 0x00803dd0
651 */
652 #endif