7e9fb3479dcfa857cd12d0cce7723aa3f0119a07
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
16 // JLH 06/01/2012 Added function to check ZIP file CRCs against file DB
17 // JPM 06/06/2016 Visual Studio support
18 // JPM 06/15/2016 ELF format support
19 // JPM 06/19/2016 Soft debugger support
20 // JPM 07/15/2016 DWARF format support
25 #include "_MSC_VER/config.h"
35 #include "universalhdr.h"
38 #include "libelf/libelf.h"
39 #include "libelf/gelf.h"
42 #include "debugger/ELFManager.h"
43 #include "debugger/DBGManager.h"
45 #include "debugger\ELFManager.h"
46 #include "debugger\DBGManager.h"
50 // Private function prototypes
52 static int gzfilelength(gzFile gd
);
53 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
54 static bool CheckExtension(const uint8_t *filename
, const char *ext
);
56 //static bool CheckExtension(const char * filename, const char * ext);
58 //static int ParseFileType(uint8_t header1, uint8_t header2, uint32_t size);
60 // Private variables/enums
64 // Generic ROM loading
66 uint32_t JaguarLoadROM(uint8_t * &rom
, char * path
)
68 // We really should have some kind of sanity checking for the ROM size here to prevent
69 // a buffer overflow... !!! FIX !!!
71 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
73 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
77 WriteLog("FILE: JaguarLoadROM attempting to load file '%s'...", path
);
78 char * ext
= strrchr(path
, '.');
80 // No filename extension == YUO FAIL IT (it is loading the file).
81 // This is naive, but it works. But should probably come up with something a little
82 // more robust, to prevent problems with dopes trying to exploit this.
85 WriteLog("FAILED!\n");
89 WriteLog("\nFILE: Succeeded in finding extension (%s)!\n", ext
);
90 WriteLog("FILE: Loading \"%s\"...", path
);
92 if (strcasecmp(ext
, ".zip") == 0)
94 // Handle ZIP file loading here...
95 WriteLog("(ZIPped)...");
97 // uint8_t * buffer = NULL;
98 // romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
99 romSize
= GetFileFromZIP(path
, FT_SOFTWARE
, rom
);
103 WriteLog("Failed!\n");
107 // memcpy(rom, buffer, romSize);
112 // Handle gzipped files transparently [Adam Green]...
114 gzFile fp
= gzopen(path
, "rb");
118 WriteLog("Failed!\n");
122 romSize
= gzfilelength(fp
);
123 rom
= new uint8_t[romSize
];
124 gzseek(fp
, 0, SEEK_SET
);
125 gzread(fp
, rom
, romSize
);
129 WriteLog("OK (%i bytes)\n", romSize
);
136 // Jaguar file loading
137 // We do a more intelligent file analysis here instead of relying on (possible
138 // false) file extensions which people don't seem to give two shits about
141 bool JaguarLoadFile(char * path
)
144 GElf_Ehdr ElfEhdr
, *PtrGElfEhdr
;
146 Elf_Data
*PtrElfData
;
147 GElf_Shdr GElfShdr
, *PtrGElfShdr
;
149 uint8_t *buffer
= NULL
;
151 size_t ElfSectionNameType
;
152 int DBGType
= DBG_NO_TYPE
;
156 jaguarROMSize
= JaguarLoadROM(buffer
, path
);
158 if (jaguarROMSize
== 0)
160 // It's up to the GUI to report errors, not us. :-)
161 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path
);
165 jaguarMainROMCRC32
= crc32_calcCheckSum(buffer
, jaguarROMSize
);
166 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32
);
167 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
168 // directory, copy the one from the ZIP file, if it exists.
170 jaguarRunAddress
= 0x802000; // For non-BIOS runs, this is true
171 int fileType
= ParseFileType(buffer
, jaguarROMSize
);
172 jaguarCartInserted
= false;
175 if (fileType
== JST_ROM
)
177 jaguarCartInserted
= true;
178 memcpy(jagMemSpace
+ 0x800000, buffer
, jaguarROMSize
);
179 // Checking something...
180 jaguarRunAddress
= GET32(jagMemSpace
, 0x800404);
181 WriteLog("FILE: Cartridge run address is reported as $%X...\n", jaguarRunAddress
);
185 else if (fileType
== JST_ALPINE
)
187 // File extension ".ROM": Alpine image that loads/runs at $802000
188 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize
);
189 memset(jagMemSpace
+ 0x800000, 0xFF, 0x2000);
190 memcpy(jagMemSpace
+ 0x802000, buffer
, jaguarROMSize
);
193 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
194 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
195 // This kludge works! Yeah!
196 SET32(jaguarMainRAM
, 0x10, 0x00001000);
197 SET16(jaguarMainRAM
, 0x1000, 0x60FE); // Here: bra Here
200 else if (fileType
== JST_ELF32
)
204 char *PtrELFExe
= (char *)ELFManager_ExeCopy(buffer
, jaguarROMSize
);
206 if (PtrELFExe
!= NULL
)
208 if ((elf_version(EV_CURRENT
) != EV_NONE
) && (ElfMem
= ELFManager_MemOpen(PtrELFExe
, jaguarROMSize
)))
210 if (ELFManager_DwarfInit(ElfMem
))
212 DBGType
|= DBG_ELFDWARF
;
215 if (!elf_getshdrnum(ElfMem
, &NbrSect
))
217 if (((PtrGElfEhdr
= gelf_getehdr(ElfMem
, &ElfEhdr
)) != NULL
) && ((PtrElfScn
= elf_getscn(ElfMem
, 0)) != NULL
))
219 for (error
= false; (PtrElfScn
!= NULL
) && (error
== false); PtrElfScn
= elf_nextscn(ElfMem
, PtrElfScn
))
223 if ((PtrGElfShdr
= gelf_getshdr(PtrElfScn
, &GElfShdr
)) == NULL
)
229 NameSection
= elf_strptr(ElfMem
, PtrGElfEhdr
->e_shstrndx
, (size_t)PtrGElfShdr
->sh_name
);
230 WriteLog("FILE: ELF Section name: %s\n", NameSection
);
232 if ((ElfSectionNameType
= ELFManager_GetSectionType(NameSection
)) == ELF_NO_TYPE
)
234 WriteLog("FILE: ELF Section not listed\n");
239 switch (PtrGElfShdr
->sh_type
)
245 if ((PtrGElfShdr
->sh_flags
& (SHF_ALLOC
| SHF_WRITE
| SHF_EXECINSTR
)))
247 if (PtrGElfShdr
->sh_addr
>= 0x800000)
249 memcpy(jagMemSpace
+ PtrGElfShdr
->sh_addr
, buffer
+ PtrGElfShdr
->sh_offset
, PtrGElfShdr
->sh_size
);
254 memcpy(jaguarMainRAM
+ PtrGElfShdr
->sh_addr
, buffer
+ PtrGElfShdr
->sh_offset
, PtrGElfShdr
->sh_size
);
259 switch (ElfSectionNameType
)
261 case ELF_debug_aranges_TYPE
:
262 case ELF_debug_info_TYPE
:
263 case ELF_debug_abbrev_TYPE
:
264 case ELF_debug_line_TYPE
:
265 case ELF_debug_frame_TYPE
:
266 case ELF_debug_ranges_TYPE
:
267 case ELF_debug_str_TYPE
:
268 case ELF_debug_loc_TYPE
:
271 case ELF_comment_TYPE
:
286 while ((error
== false) && ((PtrElfData
= elf_getdata(PtrElfScn
, PtrElfData
)) != NULL
))
288 if (!ELFManager_AddTab(PtrElfData
, ElfSectionNameType
))
303 jaguarRunAddress
= (uint32_t)PtrGElfEhdr
->e_entry
;
304 WriteLog("FILE: Setting up ELF 32bits... Run address: %08X\n", jaguarRunAddress
);
319 WriteLog("FILE: libelf version is not recognized or libelf memory cannot be opened\n");
325 WriteLog("FILE: ELFManager cannot allocate memory\n");
332 WriteLog("FILE: ELF parsing error\n");
334 if ((err
= elf_errno()))
336 WriteLog("FILE: ELF error: %s\n", elf_errmsg(err
));
343 DBGManager_SetType(DBGType
);
347 else if (fileType
== JST_ABS_TYPE1
)
349 // For ABS type 1, run address == load address
350 uint32_t loadAddress
= GET32(buffer
, 0x16),
351 codeSize
= GET32(buffer
, 0x02) + GET32(buffer
, 0x06);
352 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress
, codeSize
);
353 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0x24, codeSize
);
355 jaguarRunAddress
= loadAddress
;
358 else if (fileType
== JST_ABS_TYPE2
)
360 uint32_t loadAddress
= GET32(buffer
, 0x28), runAddress
= GET32(buffer
, 0x24),
361 codeSize
= GET32(buffer
, 0x18) + GET32(buffer
, 0x1C);
362 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress
, codeSize
);
363 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0xA8, codeSize
);
365 jaguarRunAddress
= runAddress
;
368 // NB: This is *wrong*
370 Basically, if there is no "JAG" at position $1C, then the long there is the load/start
371 address in LITTLE ENDIAN.
372 If "JAG" is present, the the next character ("R" or "L") determines the size of the
373 JagServer command (2 bytes vs. 4). Following that are the commands themselves;
374 typically it will either be 2 (load) or 3 (load & run). Command headers go like so:
393 10: (Poll for commands)
395 12: (Load & run user program)
396 filname, terminated with NULL
401 else if (fileType
== JST_JAGSERVER
)
403 // This kind of shiaut should be in the detection code below...
404 // (and now it is! :-)
405 // if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
407 // Still need to do some checking here for type 2 vs. type 3. This assumes 3
408 // Also, JAGR vs. JAGL (word command size vs. long command size)
409 uint32_t loadAddress
= GET32(buffer
, 0x22), runAddress
= GET32(buffer
, 0x2A);
410 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress
, jaguarROMSize
- 0x2E);
411 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0x2E, jaguarROMSize
- 0x2E);
413 jaguarRunAddress
= runAddress
;
415 // Hmm. Is this kludge necessary?
416 SET32(jaguarMainRAM
, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
417 SET16(jaguarMainRAM
, 0x1000, 0x60FE); // Here: bra Here
421 // else // Special WTFOMGBBQ type here...
423 // uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
424 // WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
425 // memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
427 // jaguarRunAddress = loadAddress;
431 else if (fileType
== JST_WTFOMGBBQ
)
433 uint32_t loadAddress
= (buffer
[0x1F] << 24) | (buffer
[0x1E] << 16) | (buffer
[0x1D] << 8) | buffer
[0x1C];
434 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress
, jaguarROMSize
- 0x20);
435 memcpy(jagMemSpace
+ loadAddress
, buffer
+ 0x20, jaguarROMSize
- 0x20);
437 jaguarRunAddress
= loadAddress
;
441 // We can assume we have JST_NONE at this point. :-P
442 WriteLog("FILE: Failed to load headerless file.\n");
448 // "Debugger" file loading
449 // To keep the things separate between "Debugger" and "Alpine" loading until usage clarification has been done
451 bool DebuggerLoadFile(char * path
)
453 return (AlpineLoadFile(path
));
458 // "Alpine" file loading
459 // Since the developers were coming after us with torches and pitchforks, we
460 // decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U
463 bool AlpineLoadFile(char * path
)
465 uint8_t * buffer
= NULL
;
466 jaguarROMSize
= JaguarLoadROM(buffer
, path
);
468 if (jaguarROMSize
== 0)
470 // It's up to the GUI to deal with failure, not us. ;-)
471 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path
);
475 jaguarMainROMCRC32
= crc32_calcCheckSum(buffer
, jaguarROMSize
);
476 WriteLog("FILE: CRC is %08X\n", (unsigned int)jaguarMainROMCRC32
);
479 jaguarRunAddress
= 0x802000;
481 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize
);
483 memset(jagMemSpace
+ 0x800000, 0xFF, 0x2000);
484 memcpy(jagMemSpace
+ 0x802000, buffer
, jaguarROMSize
);
487 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
488 // Try setting the vector to say, $1000 and putting an instruction there
489 // that loops forever:
490 // This kludge works! Yeah!
491 SET32(jaguarMainRAM
, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
492 SET16(jaguarMainRAM
, 0x1000, 0x60FE); // Here: bra Here
499 // Get the length of a (possibly) gzipped file
501 static int gzfilelength(gzFile gd
)
503 int size
= 0, length
= 0;
504 unsigned char buffer
[0x10000];
510 // Read in chunks until EOF
511 size
= gzread(gd
, buffer
, 0x10000);
526 // Compare extension to passed in filename. If equal, return true; otherwise false.
528 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
529 static bool CheckExtension(const uint8_t *filename
, const char *ext
)
531 //static bool CheckExtension(const char * filename, const char * ext)
534 // Sanity checking...
535 if ((filename
== NULL
) || (ext
== NULL
))
538 const char * filenameExt
= strrchr((const char *)filename
, '.'); // Get the file's extension (if any)
540 if (filenameExt
== NULL
)
543 return (strcasecmp(filenameExt
, ext
) == 0 ? true : false);
548 // Get file from .ZIP
549 // Returns the size of the file inside the .ZIP file that we're looking at
550 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
551 // Which means we have to deallocate it later.
553 uint32_t GetFileFromZIP(const char * zipFile
, FileType type
, uint8_t * &buffer
)
555 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
556 // size of the Jaguar console.
557 #if defined(_MSC_VER)
558 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
560 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
562 const char ftStrings
[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
563 // ZIP * zip = openzip(0, 0, zipFile);
564 FILE * zip
= fopen(zipFile
, "rb");
568 WriteLog("FILE: Could not open file '%s'!\n", zipFile
);
576 // The order is here is important: If the file is found, we need to short-circuit the
577 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
578 // while (!found && readzip(zip))
579 while (!found
&& GetZIPHeader(zip
, ze
))
583 // Here we simply rely on the file extension to tell the truth, but we know
584 // that extensions lie like sons-a-bitches. So this is naive, we need to do
585 // something a little more robust to keep bad things from happening here.
586 #if defined(_MSC_VER)
587 #pragma message("Warning: !!! Checking for image by extension can be fooled !!!")
589 #warning "!!! Checking for image by extension can be fooled !!!"
591 if ((type
== FT_LABEL
) && (CheckExtension(ze
.filename
, ".png") || CheckExtension(ze
.filename
, ".jpg") || CheckExtension(ze
.filename
, ".gif")))
594 WriteLog("FILE: Found image file '%s'.\n", ze
.filename
);
597 if ((type
== FT_SOFTWARE
) && (CheckExtension(ze
.filename
, ".j64")
598 || CheckExtension(ze
.filename
, ".rom") || CheckExtension(ze
.filename
, ".abs")
599 || CheckExtension(ze
.filename
, ".cof") || CheckExtension(ze
.filename
, ".coff")
600 || CheckExtension(ze
.filename
, ".jag") || CheckExtension(ze
.filename
, ".elf")))
603 WriteLog("FILE: Found software file '%s'.\n", ze
.filename
);
606 if ((type
== FT_EEPROM
) && (CheckExtension(ze
.filename
, ".eep") || CheckExtension(ze
.filename
, ".eeprom")))
609 WriteLog("FILE: Found EEPROM file '%s'.\n", ze
.filename
);
613 fseek(zip
, ze
.compressedSize
, SEEK_CUR
);
616 uint32_t fileSize
= 0;
620 WriteLog("FILE: Uncompressing...");
621 // Insert file size sanity check here...
622 buffer
= new uint8_t[ze
.uncompressedSize
];
624 // if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)
625 // if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)
626 if (UncompressFileFromZIP(zip
, ze
, buffer
) == 0)
628 fileSize
= ze
.uncompressedSize
;
629 WriteLog("success! (%u bytes)\n", fileSize
);
635 WriteLog("FAILED!\n");
639 // Didn't find what we're looking for...
640 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings
[type
]);
648 uint32_t GetFileDBIdentityFromZIP(const char * zipFile
)
650 FILE * zip
= fopen(zipFile
, "rb");
654 WriteLog("FILE: Could not open file '%s'!\n", zipFile
);
660 // Loop through all files in the zip file under consideration
661 while (GetZIPHeader(zip
, ze
))
663 // & loop through all known CRC32s in our file DB to see if it's there!
666 while (romList
[index
].crc32
!= 0xFFFFFF)
668 if (romList
[index
].crc32
== ze
.crc32
)
677 // We didn't find it, so skip the compressed data...
678 fseek(zip
, ze
.compressedSize
, SEEK_CUR
);
682 return (uint32_t )-1;
686 bool FindFileInZIPWithCRC32(const char * zipFile
, uint32_t crc
)
688 FILE * zip
= fopen(zipFile
, "rb");
692 WriteLog("FILE: Could not open file '%s'!\n", zipFile
);
698 // Loop through all files in the zip file under consideration
699 while (GetZIPHeader(zip
, ze
))
707 fseek(zip
, ze
.compressedSize
, SEEK_CUR
);
716 // Parse the file type based upon file size and/or headers.
718 uint32_t ParseFileType(uint8_t * buffer
, uint32_t size
)
720 // Check headers first...
723 if (buffer
[EI_CLASS
] == ELFCLASS32
)
725 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
))
730 if (buffer
[0] == 0x60 && buffer
[1] == 0x1B)
731 return JST_ABS_TYPE1
;
734 if (buffer
[0] == 0x01 && buffer
[1] == 0x50)
735 return JST_ABS_TYPE2
;
737 // Jag Server & other old shite
738 if (buffer
[0] == 0x60 && buffer
[1] == 0x1A)
740 if (buffer
[0x1C] == 'J' && buffer
[0x1D] == 'A' && buffer
[0x1E] == 'G')
741 return JST_JAGSERVER
;
743 return JST_WTFOMGBBQ
;
746 // And if that fails, try file sizes...
748 // If the file size is divisible by 1M, we probably have an regular ROM.
749 // We can also check our CRC32 against the internal ROM database to be sure.
750 // (We also check for the Memory Track cartridge size here as well...)
751 if ((size
% 1048576) == 0 || size
== 131072)
754 // If the file size + 8192 bytes is divisible by 1M, we probably have an
755 // Alpine format ROM.
756 if (((size
+ 8192) % 1048576) == 0)
764 // Check for universal header
766 bool HasUniversalHeader(uint8_t * rom
, uint32_t romSize
)
772 for(int i
=0; i
<8192; i
++)
773 if (rom
[i
] != universalCartHeader
[i
])
783 Stubulator ROM vectors...
784 handler 001 at $00E00008
785 handler 002 at $00E008DE
786 handler 003 at $00E008E2
787 handler 004 at $00E008E6
788 handler 005 at $00E008EA
789 handler 006 at $00E008EE
790 handler 007 at $00E008F2
791 handler 008 at $00E0054A
792 handler 009 at $00E008FA
793 handler 010 at $00000000
794 handler 011 at $00000000
795 handler 012 at $00E008FE
796 handler 013 at $00E00902
797 handler 014 at $00E00906
798 handler 015 at $00E0090A
799 handler 016 at $00E0090E
800 handler 017 at $00E00912
801 handler 018 at $00E00916
802 handler 019 at $00E0091A
803 handler 020 at $00E0091E
804 handler 021 at $00E00922
805 handler 022 at $00E00926
806 handler 023 at $00E0092A
807 handler 024 at $00E0092E
808 handler 025 at $00E0107A
809 handler 026 at $00E0107A
810 handler 027 at $00E0107A
811 handler 028 at $00E008DA
812 handler 029 at $00E0107A
813 handler 030 at $00E0107A
814 handler 031 at $00E0107A
815 handler 032 at $00000000
817 Let's try setting up the illegal instruction vector for a stubulated jaguar...
819 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
820 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
821 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
822 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
825 ABS Format sleuthing (LBUGDEMO.ABS):
827 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
828 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
831 DRI-format file detected...
832 Text segment size = 0x0000050c bytes
833 Data segment size = 0x000462c0 bytes
834 BSS Segment size = 0x00000428 bytes
835 Symbol Table size = 0x000012a6 bytes
836 Absolute Address for text segment = 0x00802000
837 Absolute Address for data segment = 0x0080250c
838 Absolute Address for BSS segment = 0x00004000
841 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
842 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
843 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
845 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
846 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
847 000050 00 00 00 00 00 00 00 20
848 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
849 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
850 000078 00 00 00 00 00 00 00 40
851 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
852 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
853 0000a0 00 00 00 00 00 00 00 80
855 Header size is $A8 bytes...
857 BSD/COFF format file detected...
859 Symbol Table offset = 230160 ($00038310)
860 Symbol Table contains 1339 symbol entries ($0000053B)
861 The additional header size is 28 bytes ($001C)
862 Magic Number for RUN_HDR = 0x00000107
863 Text Segment Size = 7632 ($00001DD0)
864 Data Segment Size = 222360 ($00036498)
865 BSS Segment Size = 428928 ($00068B80)
866 Starting Address for executable = 0x00802000
867 Start of Text Segment = 0x00802000
868 Start of Data Segment = 0x00803dd0