OMF Analyzer

(C) 2013-2014 by Antoine VIGNAU and Olivier ZARDINI

> What is OMF Analyzer ?

OMFAnalyzer is a command-line tool for Windows to analyze OMF Files we can found on 16 bits Apple IIgs operating systems like Prodos 16 or GS/OS.

Because OMF files are the core of any executable code on the Apple IIgs system
(S16, Exe, CDA, NDA, FST, PIF, Library, Tool...), OMF Analyzer will be helpful for anyone wanting to write an Assembler, a Compiler, a Linker or a Disasembler...

OMF Analyzer 
is part of the Brutal Deluxe's Cross Development Tools Project, a full set of utilities available on Windows (and other) platforms to enable the creation of new Apple IIgs software : 65c816 Assembler, 65c816 Disassembler, 65c816 Simulator, Graphic File Converter, Resource Catcher...


> Relocating Code anywhere in memory / Dynamic loading

If the object code is supposed to be executed from a pre-defined address in the Apple IIgs memory space, you can assemble your source code using the ORG directive (Merlin 16+ syntax) to define the starting address, including the Bank number. In the following example, the source code is assembled to run from $03/8000 :

+--------+-----------------------+----------------------------------------------------------
|        |                       |             ORG   $038000
|        |                       |
|        | 03/8000 : 18          |             CLC          ; 8 bit

|        | 03/8001 : FB          |             XCE
|        | 03/8002 : E2 30       |             SEP   #$30
|        |                       |
| 1      | 03/8004 : A9 2E       |             LDA   #VAR1  ; 1 byte address : LL
| 1 >> 8 | 03/8006 : A9 80       |             LDA   #>VAR1 ; 1 byte address : HH
| 1      | 03/8008 : A9 2E       |             LDA   #<VAR1 ; 1 byte address : LL
| 1 >>16 | 03/800A : A9 03       |             LDA   #^VAR1 ; 1 byte address : BB
|        |                       |
|        | 03/800C : 18          |             CLC          ; 16 bit
|        | 03/800D : FB          |             XCE
|        | 03/800E : C2 30       |             REP   #$30
|        |                       |
| 2      | 03/8010 : A9 2E 80    |             LDA   #VAR1  ; 2 bytes address : HHLL
| 2 >> 8 | 03/8013 : A9 80 03    |             LDA   #>VAR1 ; 2 bytes address : BBHH
| 2      | 03/8016 : A9 2E 80    |             LDA   #<VAR1 ; 2 bytes address : HHLL
| 1 >>16 | 03/8019 : A9 03 00    |             LDA   #^VAR1 ; 2 bytes address : 00BB
|        |                       |
| 2      | 03/801C : AD 2E 80    |             LDA   VAR1   ; 2 bytes address : HHLL
| 3      | 03/801F : AF 2E 80 03 |             LDAL  VAR1   ; 3 bytes address : BBHHLL
| 2 >> 8 | 03/8023 : AF 80 03 00 |             LDA   >VAR1  ; 3 bytes address : 00BBHH
| 2      | 03/8027 : AD 2E 80    |             LDA   <VAR1  ; 2 bytes address : HHLL
| 1 >>16 | 03/802A : F4 03 00    |             PEA   ^VAR1  ; 2 bytes address : 00BB
|        |                       |
|        | 03/802D : 60          |             RTS
|        |                       |
|        | 03/802E : 00 00       | VAR1        HEX   0000   ; Address is
00BB/HHLL=0003/802E
|        |                       |
| 2      | 03/8030 : 2E 80       | TABLE2      DA    VAR1   ; 2 bytes address : HHLL
| 3      | 03/8032 : 2E 80 03    | TABLE3      ADR   VAR1   ; 3 bytes address : BBHHLL
| 3      | 03/8035 : 2E 80 03 00 | TABLE4      ADRL  VAR1   ; 4 bytes address : 00BBHHLL
+--------+-----------------------+----------------------------------------------------------

A 16 bit variable named VAR1 is embedded in the source code. Its address is $03/802E if the ORG value is $03/8000. There are many ways to manipulate the variable address, depending on the addressing modes and the Registers size. An address can go from 1 byte to 4 bytes. Because the 65c816 uses 24 bits address, the higher byte of the 32 bit address will be always 0x00. We define a 32 bit generic address as 00BBHHLL, with 00 for the highest byte, BB for the Bank byte, HH for the High Byte of the address (within the Bank) and LL for the Low Byte. The previous example shows how the assembler encode the variable address ($03/802E) on various modes (Merlin 16+ syntax). Beware about the ^VAR1 address mode (00BB), it is valid only with PEA opcode (mostly used for Tool calls). See comment area for explanations.

If we want the same code to be used anywhere in the Apple IIgs memory, we have to use the REL (RELocatable code) directive at the beginning of the source code. This time, the ORG is virtually set to $00/0000 and the address of VAR1 is now $00/002E. Because the Bank Byte is 00, like the HH Byte or the higher Byte of the 32 bit address, the result of the assembly process is harder to read than previous one (check comment area to properly identify each byte), but the logic is the same :

+--------+-----------------------+----------------------------------------------------------
|        |                       |             REL          ; Relocatable Code

|        |                       |
|        | 00/0000 : 18          |             CLC          ; 8 bit
|        | 00/0001 : FB          |             XCE
|        | 00/0002 : E2 30       |             SEP   #$30
|        |                       |
| 1      | 00/0004 : A9 2E       |             LDA   #VAR1  ; 1 byte address : LL
| 1 >> 8 | 00/0006 : A9 00       |             LDA   #>VAR1 ; 1 byte address : HH
| 1      | 00/0008 : A9 2E       |             LDA   #<VAR1 ; 1 byte address : LL
| 1 >>16 | 00/000A : A9 00       |             LDA   #^VAR1 ; 1 byte address : BB
|        |                       |
|        | 00/000C : 18          |             CLC          ; 16 bit
|        | 00/000D : FB          |             XCE
|        | 00/000E : C2 30       |             REP   #$30
|        |                       |
| 2      | 00/0010 : A9 2E 00    |             LDA   #VAR1  ; 2 bytes address : HHLL
| 2 >> 8 | 00/0013 : A9 00 00    |             LDA   #>VAR1 ; 2 bytes address : BBHH
| 2      | 00/0016 : A9 2E 00    |             LDA   #<VAR1 ; 2 bytes address : HHLL
| 1 >>16 | 00/0019 : A9 00 00    |             LDA   #^VAR1 ; 2 bytes address : 00BB
|        |                       |
| 2      | 00/001C : AD 2E 00    |             LDA   VAR1   ; 2 bytes address : HHLL
| 3      | 00/001F : AF 2E 00 00 |             LDAL  VAR1   ; 3 bytes address : BBHHLL
| 2 >> 8 | 00/0023 : AF 2E 00 00 |             LDA   >VAR1  ; 3 bytes address : 00BBHH
| 2      | 00/0027 : AD 2E 00    |             LDA   <VAR1  ; 2 bytes address : HHLL
| 1 >>16 | 00/002A : F4 00 00    |             PEA   ^VAR1  ; 2 bytes address : 00BB
|        |                       |
|        | 00/002D : 60          |             RTS
|        |                       |
|        | 00/002E : 00 00       | VAR1        HEX   0000   ; Address is
00BB/HHLL=0003/802E
|        |                       |
| 2      | 00/0030 : 2E 00       | TABLE2      DA    VAR1   ; 2 bytes address : HHLL
| 3      | 00/0032 : 2E 00 00    | TABLE3      ADR   VAR1   ; 3 bytes address : BBHHLL
| 3      | 00/0035 : 2E 00 00 00 | TABLE4      ADRL  VAR1   ; 4 bytes address : 00BBHHLL
+--------+-----------------------+----------------------------------------------------------

In addition to the relocatable object code, we have to provide a Relocation Dictionary used to remap all the address located in the object code :

+-----------+-------------+----------+-------------+
|  # Bytes  |  Bit Shift  |  Offset  |  Reference  |
+-----------+-------------+----------+-------------+
|    01     |             |   0005   |    002E     |
|    01     |    >> 8     |   0007   |    002E     |
|    01     |             |   0009   |    002E     |
|    01     |    >> 16    |   000B   |    002E     |
+-----------+-------------+----------+-------------+
|    02     |             |   0011   |    002E     |
|    02     |    >> 8     |   0014   |    002E     |
|    02     |             |   0017   |    002E     |
|    01     |    >> 16    |   001A   |    002E     |
+-----------+-------------+----------+-------------+
|    02     |             |   001D   |    002E     |
|    03     |
    >> 8     |   0020   |    002E     |
|    02     |             |   0024   |    002E     |
|    02     |             |   0028   |    002E     |
|    01     |
    >> 16    |   002B   |    002E     |
+-----------+-------------+----------+-------------+
|    02     |             |   0030   |    002E     |
|    03     |             |   0032   |    002E     |
|    03     |             |   0035   |    002E     |
+-----------+-------------+----------+-------------+

Each Directory Entry contains 4 information  :
        - Number of bytes to relocate (from 1 to 3)
        - Bit Shift : logical operation to peform on the 32 bit address. We use here a C Language syntax (>> 8 = shift the address 8 times on the right)
        - Offset, in the object code, of the bytes to relocate. Because the code is divided into 64 KB segments, the offset is a 16 bit value.
        - Reference : Address value (lower 16 bits value of the 32 bit address).

In our previous example, the only address we used if the VAR1 variable address, to all the Reference are set to 002E, the address of the VAR1 variable in the relocatable object code. We use this address 16 times in the source code, so the number of entries in the dictionary is 16. The Offset column contains the offset of each usage of the VAR1 address in the relocatable object code (0005, 0007 , 0009...), shown in blue in the object code. The number of bytes to relocate depends on the address mode (1 byte / 2 bytes or 3 bytes). Because the highest Byte of the 32 bit address is always 00, we never have to modify / relocate it, so the maximum number of bytes to relocate is 3. With a Register size equal to 8 or 16 bit, we have to manipulate the 32 bits address to define which part if the address we want to get in the Register (Lower part, higher part, middle one...).  We use the Bit Shift information to  encode the operation performed on the 32 bits address :

    | 1 >>16 | 00/002A : F4 00 00    |         PEA   ^VAR1  ; 2 bytes address : 00BB

The address mode used with PEA is ^VAR1. We want to get a 16 bit value of the 2 highest bytes of the 32 bit address. We have 00 00 in the object code because the Bank number is 00 and another 00 because the highest byte of a 32 bit address is always 00. In the dictionary, we have to indicate than 1 byte has to be relocated (and not 2 because the highest byte of a 32 bit address is always 00). For the byte to relocate (the Bank byte), we have to shift the 32 bit address (00BBHHLL) for 16 positions to the right (00BBHHLL >> 16 = 000000BB). We get 1 one byte from this value (the lowest one). The first column of the source code 1 >>16 describes this (1 byte to relocate, >>16 bits shift to the right).

The main job of the Prodos 16 or GS/OS loader is to find a free area in memory to copy the relocatable object code and to patch all the address defined in the Relocation Dictionary. The code is now ready to be executed.


> Commands List


If you do not provide any parameter on the command line, OMFAnalyzer displays a quick reminder of existing commands :

C:\AppleIIgs>OMFAnalyzer.exe
OMFAnalyzer.exe v 1.0 (c) Brutal Deluxe 2013-2014

   Usage : 
OMFAnalyzer.exe  DUMP        <omf_file_path>      <output_file_path>.
           OMFAnalyzer.exe  EXTRACT     <omf_file_path>      <segment_number>       <output_file_path>.
           OMFAnalyzer.exe  COMPARE     <omf_file1_path>     <omf_file2_path>       <diff_file_path>.
           OMFAnalyzer.exe  COMPAREBIN  <binary_file1_path>  <binary_file2_path>    <diff_file_path>.

Few remarks about the Parameters required on the Command Line and the sotware behavior :
        - If the Windows File or folder paths contains Space characters, quote the path to avoid conflicts (OMFAnalyzer.exe DUMP "c:\Users and Settings\AppleIIgs\Finder"  c:\Temp\Finder_omf.txt).
        - Any error occuring during the execution of a command is immediately displayed on the Screen.
        - If your are transfering OMF Files from a disk image or FTP server, make sure you transfer the file as a binary file.

DUMP
    Dump the content of an OMF File into a text file. Refer to the Apple IIgs GS/OS Reference book (Appendix F : Object Module Format) for full details about data structure definitions and naming convention.

    The output file contains several sections :
        - File Information
        - Segments Summary
        - Segment #1
        ...
        - Segment #n

    Most of the numeric data (size, offset, numbers...) are displayed using hexadecimal representation and padded with the right number of 0 to fit into its data type (Byte, Word, 3 bytes or Long).  You may sometimes find the decimal value between parenthesis.

File Information

***************************
**   File Information    **
***************************

     - File Name      :  'Finder'
     - Length         :  023DEC (146924)
     - Segment Number :  0E (14)

    The File Information part contains the file Name (here we are analyzing the System 6.01's Finder file), the Length of the file and the Number of segments of the OMF file.

Segment Summary

  An OMF File contains one or more Segments. Each segment consists of one Segment Header and one Segment Body. The Segment Body is a set of Records that contains Code (or Data) and Relocation information.

***************************
**    Segments Summary   **
***************************

       +----------+----------+-----------------+----------------+----------------+-------------+------------+--------------
       |  Offset  |  SegNum  |     SegType     |     SegName    |    LoadName    |  SegLength  |  # Record  |  RecordList
       +----------+----------+-----------------+----------------+----------------+-------------+------------+--------------
       |  000000  |    01    | Data            |  ~ExpressLoad  |  Finder        |    00041C   |    0002    |  LCONST + END
       |  00041C  |    02    | Code            |  FINDER        |                |    01033E   |    0029    |  LCONST + cINTERSEG (21) + RELOC (2) + SUPER (16) + END
       |  01075A  |    03    | Code            |  BUFFERS       |                |    00229B   |    0003    |  LCONST + SUPER + END
       |  0129F5  |    04    | Code            |  VERIFY        |                |    001280   |    0011    |  LCONST + cINTERSEG (2) + RELOC (4) + SUPER (9) + END
       |  013C75  |    05    | Code            |  INFO          |                |    0038F0   |    0032    |  LCONST + cINTERSEG (38) + RELOC + SUPER (9) + END
       |  017565  |    06    | Code            |  CONTROL       |                |    000D2C   |    0005    |  LCONST + SUPER (3) + END
       |  018291  |    07    | Code            |  MATCH         |                |    002313   |    000D    |  LCONST + cINTERSEG (2) + SUPER (9) + END
       |  01A5A4  |    08    | Code            |  ALERT         |                |    000D8B   |    0017    |  LCONST + cINTERSEG (14) + SUPER (7) + END
       |  01B32F  |    09    | Code            |  CODE          |                |    006EB2   |    0026    |  LCONST + RELOC (8) + cINTERSEG (17) + SUPER (11) + END
       |  0221E1  |    0A    | Code            |  DATA          |                |    000AE1   |    0003    |  LCONST + SUPER + END
       |  022CC2  |    0B    | Jump Table      |  ~JumpTable    |                |    0000DF   |    0002    |  LCONST + END
       |  022DA1  |    0C    | Code            |  ABOUT         |                |    0009A1   |    0020    |  LCONST + RELOC (24) + SUPER (6) + END
       |  023742  |    0D    | Code            |  Help          |                |    000221   |    0008    |  LCONST + RELOC (2) + SUPER (4) + END
       |  023963  |    0E    | Code            |  FIFIFIONE     |                |    000489   |    0005    |  LCONST + SUPER (3) + END
       +----------+----------+-----------------+----------------+----------------+-------------+------------+--------------



    The Segment Summary part gives an overview of the OMF file content. We list here all the Segment information including the Number (SegNum, from 1 to N), the Location in the OMF File (Offset, in bytes from the begining of the file), the Length (SegLength in bytes), the Name (SegName)  and the Type (SegType : Data, Code, Jump Table...). If available, we may also find the name of the load segment that will contain the code generated by the linker for this segment (LoadName).

      The last two columns indicate the Number of Records found in the Body part of the Segment (# Record) and the List of Records (RecordList : LCONST, END, SUPER, RELOC, cINTERSEG...). We always find at least one LCONST and one END record in the Segment Body. The LCONST record contains the Code or the Data and the END record indicates the end of the Segment. The other records (SUPER, cINTERSEG, RELOC...) provide code relocation information. Between parenthesis, we indicate the number (in decimal) of each record type found in the Segment, if they appear more than once in the Segment Body. So the line

              LCONST + RELOC (2) + SUPER (4) + END

must be understood as : One LCONST record, Two RELOC records, Four SUPER records and One END record. Notice that the order used to display the record list do not reflect necessarily the Segment Body organization (SUPER records may appear before RELOC records).

Segment #n

***************************
**      Segment 02       **
***************************

  ***  Header  ***

     - Segment Header size + Segment Body size               :  01033E 66366
     - Number of 0x00 to add at the end of the Segment Body  :  000000 0
     - Size of the Segment once loaded in Memory             :  00F9BF 63935
     - Undefined Byte #1 (usually set to 0x00)               :      00
     - Label Length (usually set to 0x00 or 0x0A)            :      00
     - Number Length (usually set to 4 bytes)                :      04
     - Segment OMF Version (should be 0x02 for 2.1)          :      02
     - Bank Size (64 KB for Code, 0-64 KB for Data)          :  010000 65536
     - Segment Type + Segment Attributes                     :    1100 = Code  (Static + Bank Relative)
     - Undefined Byte #2 (usually set to 0x00)               :      00
     - Undefined Byte #3 (usually set to 0x00)               :      00
     - Org Address to load the Segment (0x0000 = anywhere)   :  000000
     - Boundary for Segment Alignment (0, 0x100 or 0x010000) :  000000 = No alignment needed
     - Order of the bytes in a Number (0x00 for the IIgs)    :      00
     - Undefined Byte #4 (usually set to 0x00)               :      00
     - Segment Number (1 to N)                               :    0002 2
     - Entry Point in the Segment                            :  000000 0
     - Load Name                                             :  ''
     - Segment Name                                          :  'FINDER'


    The Segment Header contains general information about the Segment such as Name, Length, Number, Type, OMF Version... It contains also technical information about the Code part (alignment, entry point, org address, order of bytes in a number...). In red, we provide extra information regarding the value found there. When needed, the decimal representation of the numbers are following the hexadecimal one. For exhaustive explanation about the Segment Header structure, please refer to the Apple IIgs GS/OS Reference book, Appendix F, page 446.

  ***  Data or Code  ***

     - Length         :  00F9BF (63935)

       000000   4B AB 09 00 01 8D B3 E0 22 12 00 0B 90 10 A9 00.00 A2 4E 09 A0 4E 09 22 40 0A 08 82 DD 01 22 20
       000020   00 0B 20 31 00 20 A4 06 20 DA 07 20 66 04 4C 81.06 A2 70 00 BD 2F F0 9D 0C EF CA CA 10 F6 AD 2F
       000040   F0 8D 0C EF A2 23 06 9E C1 E0 CA CA 10 F9 9C C1.E0 A2 FE 00 74 00 CA CA 10 FA 7B 8D B5 E0 18 69
       000060   50 00 8D B7 E0 9C 2C 09 9C 2E 09 9C 30 09 9C 32.09 A9 02 04 8F E8 EA 00 8F 49 0D 03 8F E6 E6 00
       000080   A9 80 00 8F 4B 11 03 8F CD 11 03 A2 01 02 22 00.00 E1 48 A2 02 02 22 00 00 E1 68 85 AA 22 A8 00
       0000A0   E1 2A 20 07 02 00 00 AD 09 02 0A 4A C9 02 04 B0.10 A9 00 00 A2 E4 07 A0 E4 07 22 40 0A 08 82 37
       ...
       00F940   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
       00F960   00 00 00 00 00 00 00 47 F9 00 00 00 00 00 00 00.00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
       00F980   00 00 00 00 00 00 00 47 F9 00 00 00 00 00 00 A2.00 00 48 48 F4 00 00 48 D4 00 DA 48 48 A2 02 09
       00F9A0   22 00 00 E1 8D 1D F1 68 FA 60 A9 1E 00 80 03 A9.14 00 FA F4 00 00 48 7B 18 63 01 83 01 DA 60


    We dump here the LCONST record data of the Segment, in hexadecimal (32 bytes / row). This can be 65c816 Code (most of the time) or any kind of Data used by the GS/OS Loader (ExpressLoad, Direct-page / Stack...) or other Segments code. If you want to work with such data (disassemble...), use the EXTRACT command to get them outside of the OMF file as a ready-to-use binay file.

  ***  Relocation Dictionary ***

     - # Address to be patched  :  0101 (257)

       +--------+-----------+-------------+----------+-------------+
       |    #   |  # Bytes  |  Bit Shift  |  Offset  |  Reference  |
       +--------+-----------+-------------+----------+-------------+
       |  0000  |    03     |             |   0075   |    EAE8     |
       |  0001  |    03     |             |   007D   |    E6E6     |
       ...
       |  00FD  |    03     |             |   F8D4   |    F98D     |
       |  00FE  |    03     |             |   F8D9   |    F98B     |
       |  00FF  |    03     |             |   F967   |    F947     |
       |  0100  |    03     |             |   F987   |    F947     |
       +--------+-----------+-------------+----------+-------------+

     - # Address to be patched  :  0469 (1129)

       +--------+-----------+-------------+----------+-------------+------------+-----------+
       |    #   |  # Bytes  |  Bit Shift  |  Offset  |  Reference  |  File Num  |  Seg Num  |
       +--------+-----------+-------------+----------+-------------+------------+-----------+
       |  0000  |    03     |             |   0009   |    0012     |    0001    |    000B   |
       |  0001  |    02     |             |   0012   |    094E     |    0001    |    000A   |
       |  0002  |    02     |     0F      |   0015   |    094E     |    0001    |    000B   |
       |  0003  |    03     |             |   0018   |    0A40     |    0001    |    0008   |
       ...
       |  0466  |    02     |     0F      |   F91B   |    F967     |    0001    |    0003   |
       |  0467  |    03     |             |   F92C   |    26B1     |    0001    |    0009   |
       |  0468  |    04     |             |   F983   |    007C     |    0001    |    0008   |
       +--------+-----------+-------------+----------+-------------+------------+-----------+


    Instead of showing here the relocation records raw data (RELOC, INTERSEG, cRELOC, cINTERSEG, SUPER...), we have splited the relocation dictionary entries into two logical tables :
- one for the inner-segment addresses to be patched (we patch an address in a segment with a reference to another address in the same segment)
- one for the inter-segments addresses to be patched (we patch an address in a segment with a reference to another address in a different segment)

    Both tables have the same mandatory information set :
- # Bytes : number of bytes to be relocated
-
Bit Shift : bit shift operation to perform on the relocated address
-
Offset : offset (relative to the start of the Segment) of the first byte to be relocated
-
Reference : reference address (relative to the start of the Segment)

   For relocation references located in another segment (target segment), we need two extra information :
- File Num : 1 if the target segment is part of the same OMF File, Jump Table segment number of the target segment is locateed in a run-time library
- Seg Num : target segment number (1 - N)
  

    Syntax
OMFAnalyzer.exe DUMP  <omf_file_path>  <output_file_path>

    Example
OMFAnalyzer.exe DUMP  c:\AppleIIgs\Finder  c:\AppleIIgs\Finder_OMF.txt


EXTRACT
    Extract the LCONST part of a Segment to disk. This is useful to retrieve from a OMF file the Code or Data part. We need to provide the OMF file path, the segment number (1 to N) where the LCONST is located and so the path of the binary file where the data will be stored. If you want to get the Relocation Dictionary parts, use the DUMP command and get the RELOC and INTERSEG tables from the output file.
  
    Syntax
OMFAnalyzer.exe EXTRACT   <omf_file_path>   <segment_number>   <output_file_path>

    Example
OMFAnalyzer.exe EXTRACT  c:\AppleIIgs\Merlin\Cogito  1  c:\AppleIIgs\Cogito_code.bin


COMPARE
    Compare two OMF files and create a text file as result with all differencies. This is useful to compare the OMF files built by two differents compiler / assembler (from the same source code) or to compare an original OMF file with a new one rebuilt from the source code. Because each compiler / assembler may organize the relocation dictionary as it wants (pick up among all dfiiferent relocation entry structures), a binary comparison of the OMF file is useless. If we want to check that two OMF files are equivalent (but not necessarily equal in term of bytes organization) , we have to compare the Headers of each segment together and we have to compare Data / Code parts together and so the Relocation Dictionary entries together (for both RELOC and INTERSEG).  If the logical information are the same, they will be declared as equivalent. If some parts are differents, we record them into a diff file. If we find more than 50 errors in one Segment part (Header, Data, Dictionary...) we record only the first 50 ones and we end up there.
  
    Syntax
OMFAnalyzer.exe COMPARE   <omf_file1_path>   <omf_file2_path>    <diff_file_path>

    Example
OMFAnalyzer.exe COMPARE  c:\AppleIIgs\Merlin\Cogito  c:\AppleIIgs\Orca\Cogito  c:\AppleIIgs\Cogito_diff.txt


COMPAREBIN
    Simply compare two files, byte per byte, and search for any difference. Unlike the COMPARE command, there is no intelligence here. This can be useful to compare two binary files (graphic, music...) but also to compare absolute memory image file like Prodos 8 loadable file format. If we find more than 50 differences, we end up there (the diff file will contain information about the the first 50 differences found).

    Syntax
OMFAnalyzer.exe COMPAREBIN   <binary_file1_path>   <binary_file2_path>    <diff_file_path>

    Example
OMFAnalyzer.exe COMPAREBIN  c:\AppleIIgs\Merlin\Cogito  c:\AppleIIgs\Orca\Cogito  c:\AppleIIgs\Cogito_diff.txt


> F.A.Q

Is the Source code available somewhere ?

The Source code is freely available in the Zip file (see download section).

It is currently pakaged as a Visual Studio 2010 Project set of files. The tool is only using C Language, so you can recompile it with any other C ANSI compiler (GCC...).


What about a Macintosh or Linux release ?

Everything has be done to be as independant as possible from the Operating System. The first release has been done using a Windows environment. The Macintosh and Linux ports should be done in the next weeks, as soon as someone is ready to handle it !

The source code is written in C Ansi and the only Operating Systems calls have been isolated in a specific file. So porting the project to Macintosh or Unix systems should take maximum 4 hours to any C programmers having the knowledge of the target platform.


> References

Apple IIgs GS/OS Reference, Appendix F : Object Module Format version 2.1
Merlin 16+
documentation by Glen Bredon, Roger Wagner Publishing
ORCA / M 2.0
documentation by Mike Westerfield


> Download

OMF Analyzer v1.0 for Windows 32 & 64 bits + Source Code                     OMFAnalyzer for Windows