73d255e1091175dd3e1f865745fc7ade93c45c85
6 // (C) 2010 Underground Software
8 // JLH = James Hammons <jlhamm@acm.org>
9 // JPM = Jean-Paul Mari <djipi.mari@gmail.com>
12 // --- ---------- ------------------------------------------------------------
13 // JLH 01/16/2010 Created this log ;-)
14 // JLH 02/28/2010 Added functions to look inside .ZIP files and handle contents
15 // JLH 06/01/2012 Added function to check ZIP file CRCs against file DB
16 // JPM June/2016 Visual Studio support, ELF format support and Soft debugger support
17 // JPM 07/15/2016 DWARF format support
18 // JPM 04/06/2019 Added ELF sections check
19 // JPM 03/12/2020 Added ELF section types check and new error messages
24 #include "_MSC_VER/config.h"
34 #include "universalhdr.h"
37 #include "libelf/libelf.h"
38 #include "libelf/gelf.h"
40 #include "debugger/ELFManager.h"
41 #include "debugger/DBGManager.h"
45 // Private function prototypes
47 static int gzfilelength(gzFile gd
);
48 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
49 static bool CheckExtension(const uint8_t *filename
, const char *ext
);
51 //static bool CheckExtension(const char * filename, const char * ext);
53 //static int ParseFileType(uint8_t header1, uint8_t header2, uint32_t size);
55 // Private variables/enums
59 // Generic ROM loading
61 uint32_t JaguarLoadROM(uint8_t * &rom
, char * path
)
63 // We really should have some kind of sanity checking for the ROM size here to prevent
64 // a buffer overflow... !!! FIX !!!
66 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
68 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
72 WriteLog("FILE: JaguarLoadROM attempting to load file '%s'...", path
);
73 char * ext
= strrchr(path
, '.');
75 // No filename extension == YUO FAIL IT (it is loading the file).
76 // This is naive, but it works. But should probably come up with something a little
77 // more robust, to prevent problems with dopes trying to exploit this.
80 WriteLog("FAILED!\n");
84 WriteLog("\nFILE: Succeeded in finding extension (%s)!\n", ext
);
85 WriteLog("FILE: Loading \"%s\"...", path
);
87 if (strcasecmp(ext
, ".zip") == 0)
89 // Handle ZIP file loading here...
90 WriteLog("(ZIPped)...");
92 // uint8_t * buffer = NULL;
93 // romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
94 romSize
= GetFileFromZIP(path
, FT_SOFTWARE
, rom
);
98 WriteLog("Failed!\n");
102 // memcpy(rom, buffer, romSize);
107 // Handle gzipped files transparently [Adam Green]...
109 gzFile fp
= gzopen(path
, "rb");
113 WriteLog("Failed!\n");
117 romSize
= gzfilelength(fp
);
118 rom
= new uint8_t[romSize
];
119 gzseek(fp
, 0, SEEK_SET
);
120 gzread(fp
, rom
, romSize
);
124 WriteLog("OK (%i bytes)\n", romSize
);
131 // Jaguar file loading
132 // We do a more intelligent file analysis here instead of relying on (possible
133 // false) file extensions which people don't seem to give two shits about
136 bool JaguarLoadFile(char * path
)
139 GElf_Ehdr ElfEhdr
, *PtrGElfEhdr
;
141 Elf_Data
*PtrElfData
;
142 GElf_Shdr GElfShdr
, *PtrGElfShdr
;
144 uint8_t *buffer
= NULL
;
146 size_t ElfSectionNameType
;
147 int DBGType
= DBG_NO_TYPE
;
151 jaguarROMSize
= JaguarLoadROM(buffer
, path
);
153 if (jaguarROMSize
== 0)
155 // It's up to the GUI to report errors, not us. :-)
156 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path
);
160 jaguarMainROMCRC32
= crc32_calcCheckSum(buffer
, jaguarROMSize
);
161 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32
);
162 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
163 // directory, copy the one from the ZIP file, if it exists.
165 jaguarRunAddress
= 0x802000; // For non-BIOS runs, this is true
166 int fileType
= ParseFileType(buffer
, jaguarROMSize
);
167 jaguarCartInserted
= false;
170 if (fileType
== JST_ROM
)
172 jaguarCartInserted
= true;
173 memcpy(jagMemSpace
+ 0x800000, buffer
, jaguarROMSize
);
174 // Checking something...
175 jaguarRunAddress
= GET32(jagMemSpace
, 0x800404);
176 WriteLog("FILE: Cartridge run address is reported as $%X...\n", jaguarRunAddress
);
180 else if (fileType
== JST_ALPINE
)
182 // File extension ".ROM": Alpine image that loads/runs at $802000
183 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize
);
184 memset(jagMemSpace
+ 0x800000, 0xFF, 0x2000);
185 memcpy(jagMemSpace
+ 0x802000, buffer
, jaguarROMSize
);
188 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
189 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
190 // This kludge works! Yeah!
191 SET32(jaguarMainRAM
, 0x10, 0x00001000);
192 SET16(jaguarMainRAM
, 0x1000, 0x60FE); // Here: bra Here
195 else if (fileType
== JST_ELF32
)
199 char *PtrELFExe
= (char *)ELFManager_ExeCopy(buffer
, jaguarROMSize
);
201 if (PtrELFExe
!= NULL
)
203 if ((elf_version(EV_CURRENT
) != EV_NONE
) && (ElfMem
= ELFManager_MemOpen(PtrELFExe
, jaguarROMSize
)))
205 if (ELFManager_DwarfInit(ElfMem
))
207 DBGType
|= DBG_ELFDWARF
;
210 if (!elf_getshdrnum(ElfMem
, &NbrSect
))
212 if (((PtrGElfEhdr
= gelf_getehdr(ElfMem
, &ElfEhdr
)) != NULL
) && ((PtrElfScn
= elf_getscn(ElfMem
, 0)) != NULL
))
214 for (error
= false; (PtrElfScn
!= NULL
) && (error
== false); PtrElfScn
= elf_nextscn(ElfMem
, PtrElfScn
))
218 if ((PtrGElfShdr
= gelf_getshdr(PtrElfScn
, &GElfShdr
)) == NULL
)
224 NameSection
= elf_strptr(ElfMem
, PtrGElfEhdr
->e_shstrndx
, (size_t)PtrGElfShdr
->sh_name
);
225 WriteLog("FILE: ELF Section %s found\n", NameSection
);
227 if (((ElfSectionNameType
= ELFManager_GetSectionType(NameSection
)) == ELF_NO_TYPE
) && vjs
.ELFSectionsCheck
)
229 WriteLog("FILE: ELF Section %s not recognized\n", NameSection
);
234 switch (PtrGElfShdr
->sh_type
)
240 if ((PtrGElfShdr
->sh_flags
& (SHF_ALLOC
| SHF_WRITE
| SHF_EXECINSTR
)))
242 if (PtrGElfShdr
->sh_addr
>= 0x800000)
244 memcpy(jagMemSpace
+ PtrGElfShdr
->sh_addr
, buffer
+ PtrGElfShdr
->sh_offset
, PtrGElfShdr
->sh_size
);
249 memcpy(jaguarMainRAM
+ PtrGElfShdr
->sh_addr
, buffer
+ PtrGElfShdr
->sh_offset
, PtrGElfShdr
->sh_size
);
254 switch (ElfSectionNameType
)
257 case ELF_debug_abbrev_TYPE
:
258 case ELF_debug_aranges_TYPE
:
259 case ELF_debug_frame_TYPE
:
260 case ELF_debug_info_TYPE
:
261 case ELF_debug_line_TYPE
:
262 case ELF_debug_loc_TYPE
:
263 case ELF_debug_macinfo_TYPE
:
264 case ELF_debug_pubnames_TYPE
:
265 case ELF_debug_pubtypes_TYPE
:
266 case ELF_debug_ranges_TYPE
:
267 case ELF_debug_str_TYPE
:
268 case ELF_debug_types_TYPE
:
274 case ELF_comment_TYPE
:
278 WriteLog("FILE: ELF section %s is not recognized\n", NameSection
);
290 while ((error
== false) && ((PtrElfData
= elf_getdata(PtrElfScn
, PtrElfData
)) != NULL
))
292 if (!ELFManager_AddTab(PtrElfData
, ElfSectionNameType
))
294 WriteLog("FILE: ELF tab cannot be allocated\n");
301 WriteLog("FILE: ELF SHT type %i not recognized\n", PtrGElfShdr
->sh_type
);
309 // Set the executable address
310 jaguarRunAddress
= (uint32_t)PtrGElfEhdr
->e_entry
;
311 WriteLog("FILE: Setting up ELF 32bits... Run address: %08X\n", jaguarRunAddress
);
320 WriteLog("FILE: Cannot get the number of the ELF sections\n");
327 WriteLog("FILE: libelf version is not recognized or libelf memory cannot be opened\n");
333 WriteLog("FILE: ELFManager cannot allocate memory\n");
340 WriteLog("FILE: ELF parsing error\n");
342 if ((err
= elf_errno()))
344 WriteLog("FILE: ELF error: %s\n", elf_errmsg(err
));
351 DBGManager_SetType(DBGType
);
355 else if (fileType
== JST_ABS_TYPE1
)
357 // For ABS type 1, run address == load address
358 uint32_t loadAddress
= GET32(buffer
, 0x16),
359 codeSize
= GET32(buffer
, 0x02) + GET32(buffer
, 0x06);
360 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress
, codeSize
);
361 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0x24, codeSize
);
363 jaguarRunAddress
= loadAddress
;
366 else if (fileType
== JST_ABS_TYPE2
)
368 uint32_t loadAddress
= GET32(buffer
, 0x28), runAddress
= GET32(buffer
, 0x24),
369 codeSize
= GET32(buffer
, 0x18) + GET32(buffer
, 0x1C);
370 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress
, codeSize
);
371 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0xA8, codeSize
);
373 jaguarRunAddress
= runAddress
;
376 // NB: This is *wrong*
378 Basically, if there is no "JAG" at position $1C, then the long there is the load/start
379 address in LITTLE ENDIAN.
380 If "JAG" is present, the the next character ("R" or "L") determines the size of the
381 JagServer command (2 bytes vs. 4). Following that are the commands themselves;
382 typically it will either be 2 (load) or 3 (load & run). Command headers go like so:
401 10: (Poll for commands)
403 12: (Load & run user program)
404 filname, terminated with NULL
409 else if (fileType
== JST_JAGSERVER
)
411 // This kind of shiaut should be in the detection code below...
412 // (and now it is! :-)
413 // if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
415 // Still need to do some checking here for type 2 vs. type 3. This assumes 3
416 // Also, JAGR vs. JAGL (word command size vs. long command size)
417 uint32_t loadAddress
= GET32(buffer
, 0x22), runAddress
= GET32(buffer
, 0x2A);
418 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress
, jaguarROMSize
- 0x2E);
419 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0x2E, jaguarROMSize
- 0x2E);
421 jaguarRunAddress
= runAddress
;
423 // Hmm. Is this kludge necessary?
424 SET32(jaguarMainRAM
, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
425 SET16(jaguarMainRAM
, 0x1000, 0x60FE); // Here: bra Here
429 // else // Special WTFOMGBBQ type here...
431 // uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
432 // WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
433 // memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
435 // jaguarRunAddress = loadAddress;
439 else if (fileType
== JST_WTFOMGBBQ
)
441 uint32_t loadAddress
= (buffer
[0x1F] << 24) | (buffer
[0x1E] << 16) | (buffer
[0x1D] << 8) | buffer
[0x1C];
442 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress
, jaguarROMSize
- 0x20);
443 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0x20, jaguarROMSize
- 0x20);
445 jaguarRunAddress
= loadAddress
;
449 // We can assume we have JST_NONE at this point. :-P
450 WriteLog("FILE: Failed to load headerless file.\n");
456 // "Debugger" file loading
457 // To keep the things separate between "Debugger" and "Alpine" loading until usage clarification has been done
459 bool DebuggerLoadFile(char * path
)
461 return (AlpineLoadFile(path
));
466 // "Alpine" file loading
467 // Since the developers were coming after us with torches and pitchforks, we
468 // decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U
471 bool AlpineLoadFile(char * path
)
473 uint8_t * buffer
= NULL
;
474 jaguarROMSize
= JaguarLoadROM(buffer
, path
);
476 if (jaguarROMSize
== 0)
478 // It's up to the GUI to deal with failure, not us. ;-)
479 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path
);
483 jaguarMainROMCRC32
= crc32_calcCheckSum(buffer
, jaguarROMSize
);
484 WriteLog("FILE: CRC is %08X\n", (unsigned int)jaguarMainROMCRC32
);
487 jaguarRunAddress
= 0x802000;
489 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize
);
491 memset(jagMemSpace
+ 0x800000, 0xFF, 0x2000);
492 memcpy(jagMemSpace
+ 0x802000, buffer
, jaguarROMSize
);
495 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
496 // Try setting the vector to say, $1000 and putting an instruction there
497 // that loops forever:
498 // This kludge works! Yeah!
499 SET32(jaguarMainRAM
, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
500 SET16(jaguarMainRAM
, 0x1000, 0x60FE); // Here: bra Here
507 // Get the length of a (possibly) gzipped file
509 static int gzfilelength(gzFile gd
)
511 int size
= 0, length
= 0;
512 unsigned char buffer
[0x10000];
518 // Read in chunks until EOF
519 size
= gzread(gd
, buffer
, 0x10000);
534 // Compare extension to passed in filename. If equal, return true; otherwise false.
536 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
537 static bool CheckExtension(const uint8_t *filename
, const char *ext
)
539 //static bool CheckExtension(const char * filename, const char * ext)
542 // Sanity checking...
543 if ((filename
== NULL
) || (ext
== NULL
))
546 const char * filenameExt
= strrchr((const char *)filename
, '.'); // Get the file's extension (if any)
548 if (filenameExt
== NULL
)
551 return (strcasecmp(filenameExt
, ext
) == 0 ? true : false);
556 // Get file from .ZIP
557 // Returns the size of the file inside the .ZIP file that we're looking at
558 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
559 // Which means we have to deallocate it later.
561 uint32_t GetFileFromZIP(const char * zipFile
, FileType type
, uint8_t * &buffer
)
563 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
564 // size of the Jaguar console.
565 #if defined(_MSC_VER)
566 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
568 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
570 const char ftStrings
[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
571 // ZIP * zip = openzip(0, 0, zipFile);
572 FILE * zip
= fopen(zipFile
, "rb");
576 WriteLog("FILE: Could not open file '%s'!\n", zipFile
);
584 // The order is here is important: If the file is found, we need to short-circuit the
585 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
586 // while (!found && readzip(zip))
587 while (!found
&& GetZIPHeader(zip
, ze
))
591 // Here we simply rely on the file extension to tell the truth, but we know
592 // that extensions lie like sons-a-bitches. So this is naive, we need to do
593 // something a little more robust to keep bad things from happening here.
594 #if defined(_MSC_VER)
595 #pragma message("Warning: !!! Checking for image by extension can be fooled !!!")
597 #warning "!!! Checking for image by extension can be fooled !!!"
599 if ((type
== FT_LABEL
) && (CheckExtension(ze
.filename
, ".png") || CheckExtension(ze
.filename
, ".jpg") || CheckExtension(ze
.filename
, ".gif")))
602 WriteLog("FILE: Found image file '%s'.\n", ze
.filename
);
605 if ((type
== FT_SOFTWARE
) && (CheckExtension(ze
.filename
, ".j64")
606 || CheckExtension(ze
.filename
, ".rom") || CheckExtension(ze
.filename
, ".abs")
607 || CheckExtension(ze
.filename
, ".cof") || CheckExtension(ze
.filename
, ".coff")
608 || CheckExtension(ze
.filename
, ".jag") || CheckExtension(ze
.filename
, ".elf")))
611 WriteLog("FILE: Found software file '%s'.\n", ze
.filename
);
614 if ((type
== FT_EEPROM
) && (CheckExtension(ze
.filename
, ".eep") || CheckExtension(ze
.filename
, ".eeprom")))
617 WriteLog("FILE: Found EEPROM file '%s'.\n", ze
.filename
);
621 fseek(zip
, ze
.compressedSize
, SEEK_CUR
);
624 uint32_t fileSize
= 0;
628 WriteLog("FILE: Uncompressing...");
629 // Insert file size sanity check here...
630 buffer
= new uint8_t[ze
.uncompressedSize
];
632 // if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)
633 // if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)
634 if (UncompressFileFromZIP(zip
, ze
, buffer
) == 0)
636 fileSize
= ze
.uncompressedSize
;
637 WriteLog("success! (%u bytes)\n", fileSize
);
643 WriteLog("FAILED!\n");
647 // Didn't find what we're looking for...
648 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings
[type
]);
656 uint32_t GetFileDBIdentityFromZIP(const char * zipFile
)
658 FILE * zip
= fopen(zipFile
, "rb");
662 WriteLog("FILE: Could not open file '%s'!\n", zipFile
);
668 // Loop through all files in the zip file under consideration
669 while (GetZIPHeader(zip
, ze
))
671 // & loop through all known CRC32s in our file DB to see if it's there!
674 while (romList
[index
].crc32
!= 0xFFFFFF)
676 if (romList
[index
].crc32
== ze
.crc32
)
685 // We didn't find it, so skip the compressed data...
686 fseek(zip
, ze
.compressedSize
, SEEK_CUR
);
690 return (uint32_t )-1;
694 bool FindFileInZIPWithCRC32(const char * zipFile
, uint32_t crc
)
696 FILE * zip
= fopen(zipFile
, "rb");
700 WriteLog("FILE: Could not open file '%s'!\n", zipFile
);
706 // Loop through all files in the zip file under consideration
707 while (GetZIPHeader(zip
, ze
))
715 fseek(zip
, ze
.compressedSize
, SEEK_CUR
);
724 // Parse the file type based upon file size and/or headers.
726 uint32_t ParseFileType(uint8_t * buffer
, uint32_t size
)
728 // Check headers first...
731 if (buffer
[EI_CLASS
] == ELFCLASS32
)
733 if (((BigToLittleEndian16(((Elf32_Ehdr
*)buffer
)->e_machine
) & 0xFF) == EM_68K
) && (BigToLittleEndian16(((Elf32_Ehdr
*)buffer
)->e_type
) == ET_EXEC
) && (buffer
[0] == ELFMAG0
) && (buffer
[1] == ELFMAG1
) && (buffer
[2] == ELFMAG2
) && (buffer
[3] == ELFMAG3
))
738 if (buffer
[0] == 0x60 && buffer
[1] == 0x1B)
739 return JST_ABS_TYPE1
;
742 if (buffer
[0] == 0x01 && buffer
[1] == 0x50)
743 return JST_ABS_TYPE2
;
745 // Jag Server & other old shite
746 if (buffer
[0] == 0x60 && buffer
[1] == 0x1A)
748 if (buffer
[0x1C] == 'J' && buffer
[0x1D] == 'A' && buffer
[0x1E] == 'G')
749 return JST_JAGSERVER
;
751 return JST_WTFOMGBBQ
;
754 // And if that fails, try file sizes...
756 // If the file size is divisible by 1M, we probably have an regular ROM.
757 // We can also check our CRC32 against the internal ROM database to be sure.
758 // (We also check for the Memory Track cartridge size here as well...)
759 if ((size
% 1048576) == 0 || size
== 131072)
762 // If the file size + 8192 bytes is divisible by 1M, we probably have an
763 // Alpine format ROM.
764 if (((size
+ 8192) % 1048576) == 0)
772 // Check for universal header
774 bool HasUniversalHeader(uint8_t * rom
, uint32_t romSize
)
780 for(int i
=0; i
<8192; i
++)
781 if (rom
[i
] != universalCartHeader
[i
])
791 Stubulator ROM vectors...
792 handler 001 at $00E00008
793 handler 002 at $00E008DE
794 handler 003 at $00E008E2
795 handler 004 at $00E008E6
796 handler 005 at $00E008EA
797 handler 006 at $00E008EE
798 handler 007 at $00E008F2
799 handler 008 at $00E0054A
800 handler 009 at $00E008FA
801 handler 010 at $00000000
802 handler 011 at $00000000
803 handler 012 at $00E008FE
804 handler 013 at $00E00902
805 handler 014 at $00E00906
806 handler 015 at $00E0090A
807 handler 016 at $00E0090E
808 handler 017 at $00E00912
809 handler 018 at $00E00916
810 handler 019 at $00E0091A
811 handler 020 at $00E0091E
812 handler 021 at $00E00922
813 handler 022 at $00E00926
814 handler 023 at $00E0092A
815 handler 024 at $00E0092E
816 handler 025 at $00E0107A
817 handler 026 at $00E0107A
818 handler 027 at $00E0107A
819 handler 028 at $00E008DA
820 handler 029 at $00E0107A
821 handler 030 at $00E0107A
822 handler 031 at $00E0107A
823 handler 032 at $00000000
825 Let's try setting up the illegal instruction vector for a stubulated jaguar...
827 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
828 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
829 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
830 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
833 ABS Format sleuthing (LBUGDEMO.ABS):
835 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
836 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
839 DRI-format file detected...
840 Text segment size = 0x0000050c bytes
841 Data segment size = 0x000462c0 bytes
842 BSS Segment size = 0x00000428 bytes
843 Symbol Table size = 0x000012a6 bytes
844 Absolute Address for text segment = 0x00802000
845 Absolute Address for data segment = 0x0080250c
846 Absolute Address for BSS segment = 0x00004000
849 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
850 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
851 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
853 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
854 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
855 000050 00 00 00 00 00 00 00 20
856 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
857 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
858 000078 00 00 00 00 00 00 00 40
859 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
860 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
861 0000a0 00 00 00 00 00 00 00 80
863 Header size is $A8 bytes...
865 BSD/COFF format file detected...
867 Symbol Table offset = 230160 ($00038310)
868 Symbol Table contains 1339 symbol entries ($0000053B)
869 The additional header size is 28 bytes ($001C)
870 Magic Number for RUN_HDR = 0x00000107
871 Text Segment Size = 7632 ($00001DD0)
872 Data Segment Size = 222360 ($00036498)
873 BSS Segment Size = 428928 ($00068B80)
874 Starting Address for executable = 0x00802000
875 Start of Text Segment = 0x00802000
876 Start of Data Segment = 0x00803dd0