list bincom.bas
  Dim Version$/"BINCOM Binary compare program V1.0 (C) 1985 Software Dynamics, In
c."/
! Used to show difference between two binary object files
! Version History:
!     1.0   7/24/85    IDB...built out of desperation
!
  Dim Line$(100),ReferenceRecords[100,2],TargetRecords[100,2]
  Dim Ref/1/,Target/2/,Output/3/
  Dim Byte$(1),Word$(2)

Subroutine CollectLoadRecords(CollectChannel,CollectedRecords[*,*])
  ! Subroutine to scan thru file on CollectChannel to determine all load records
  ! Builds up list of memory addresses, file offsets and lengths
  ! CollectedRecords[0,0] is number of load records containing data
  ! CollectedRecords[0,1] is object file type ($01,$02,$11, etc.)
  ! CollectedRecords[0,2] is start address
  ! CollectedRecords[n,0] is memory address
  ! CollectedRecords[n,1] is load record length
  ! CollectedRecords[n,2] is file offset
  CollectedRecords[0,0]=0\ ! Record number of load records in file
  Read #CollectChannel,Byte$
  If Byte$(1)<>:01 and Byte$(1)<>:02 and Byte$(1)<>:03 and Byte$(1)<>:11
  Then Error 1028\ ! Not an object file
  CollectedRecords[0,1]=Byte$(1) \ ! Remember object file type
  Read #CollectChannel,Word$
  Let CollectedRecords[0,2]=Word$[1]**8+Word$[2]
  Read #CollectChannel,Word$
  If CollectedRecords[0,2]+Word$[1]**8+Word$[2]<>:FFFF
  Then Error 1028 \ ! Not an object file
  FilePosition=5 \ ! We have read 5 bytes
ProcessLoadRecords: ! Inspect next load record
  If Eof(CollectChannel) Then Error 1001 \ ! Propogate error back to user
  Read #CollectChannel,Byte$
  !D! Print "Load record type read = ";Byte$(1)
  If Byte$(1)=2 Then Gosub ProcessLoadRecord\Goto ProcessLoadRecords
  If Byte$(1)=3
  Then
      Gosub ProcessLoadRecord
      Print CollectedRecords[0,0];"Load Records:"
      For LoadRecordIndex=1 to CollectedRecords[0,0]
          Print "Start ";Hex$(CollectedRecords[LoadRecordIndex,0]);
          Print " Length ";Hex$(CollectedRecords[LoadRecordIndex,1]);
          Print " FilePosition ";CollectedRecords[LoadRecordIndex,2]
      Next LoadRecordIndex
      Return Subroutine
  Fi
  If Byte$(1)=0 Then ProcessSkipRecord
  Error 1036 \ ! Bad Load Record

ProcessSkipRecord:
  Read #CollectChannel,Word$ \ ! Get skip count
  Let FilePosition=FilePosition+3+(Word$[1]**8+Word$[2])
  Position #CollectChannel,FilePosition
  Goto ProcessLoadRecords

ProcessLoadRecord:
  Read #CollectChannel,Word$ \ ! Get load record address
  Let Address=Word$[1]**8+Word$[2]
  Read #CollectChannel,Word$ \ ! Get load count
  Let Count=Word$[1]**8+Word$[2]
  Let FilePosition=FilePosition+5 \ ! Remember we read past load record header
  !D! Print "Address ";Hex$(Address);" Count ";Hex$(Count);" FilePosition";FilePo
sition
InsertNewLoadRecord: ...
& Do
    If Count=0 Then Exit InsertNewLoadRecord \ ! Ignore empty load records
    ! Assert: Load records in the list all have nonzero counts
    For LoadRecordIndex=1 to CollectedRecords[0,0]
        ! Load records are in sorted order from low to high
        If CollectedRecords[LoadRecordIndex,0]<=Address
        Then
            ! This load record might contain [Address,Count] range
            If CollectedRecords[LoadRecordIndex,0]+...
&                 CollectedRecords[LoadRecordIndex,1]<=Address
            Then
                ! No overlap between this load record and new load record
                Cycle LoadRecordIndex
            Else
                ! New load record supercedes part of current load record
                If CollectedRecords[LoadRecordIndex,0]+...
&                  CollectedRecords[LoadRecordIndex,1]<=Address+Count
                Then Print "UNFINISHED"
                Else
                     Print "UNFINISHED"
                Fi
            Fi
        Else
            ! Start of current load record is above start of new load record
            If Address+Count<=CollectedRecords[LoadRecordIndex,0]
            Then
                ! Aha! A new load record to insert
                ! First, shuffle rest of load records up
                i=CollectedRecords[0,0] \ ! Index of last load record
                While i>=LoadRecordIndex Do
                   CollectedRecords[i+1,0]=CollectedRecords[i,0]
                   CollectedRecords[i+1,1]=CollectedRecords[i,1]
                   CollectedRecords[i+1,2]=CollectedRecords[i,2]
                   i=i-1
                End
                ! Insert new load record where current one was
                CollectedRecords[LoadRecordIndex,0]=Address
                CollectedRecords[LoadRecordIndex,1]=Count
                CollectedRecords[LoadRecordIndex,2]=FilePosition
                CollectedRecords[0,0]=CollectedRecords[0,0]+1 \ ! Bump Load recor
d count
                Exit InsertNewLoadRecord
            Else
                ! This load record overlaps tail of [Address,Count] range
                Print "UNFINISHED"
            Fi
        Fi
    Next LoadRecordIndex
    ! Must be a new load record, insert it at end
    CollectedRecords[LoadRecordIndex,0]=Address
    CollectedRecords[LoadRecordIndex,1]=Count
    CollectedRecords[LoadRecordIndex,2]=FilePosition
    CollectedRecords[0,0]=CollectedRecords[0,0]+1 \ ! Bump Load record count
  End \ ! Do InsertNewLoadRecord
  Let FilePosition=FilePosition+Count \ ! Where we are now
  !D! Print "After load record, file position is: "; FilePosition
  Position #CollectChannel,FilePosition-1
  ! Note: can't position to FilePosition, as that might actually be at end of fil
e
  Read #CollectChannel,Byte$ \ ! This should position just before next load recor
d
  If Eof(CollectChannel) Then Error 1001 \ ! Propogate error back to user
  Return
End

Def FetchByte(FetchChannel,FetchRecords[*,*],FetchAddress)
  ! CollectedRecords[n,0] is memory address
  ! CollectedRecords[n,1] is load record length
  ! CollectedRecords[n,2] is file offset
  For LoadRecordIndex=1 to FetchRecords[0,0]
      If FetchRecords[LoadRecordIndex,0]>FetchAddress
      Then Exit LoadRecordIndex
      If FetchRecords[LoadRecordIndex,0]+FetchRecords[LoadRecordIndex,1]...
&        <= Address Then Cycle LoadRecordIndex
      Read #FetchChannel@FetchRecords[LoadRecordIndex,2]+...
&                        (Address-FetchRecords[LoadRecordIndex,0]),Byte$
      Return Byte$(1) \ ! This is desired value
  Next LoadRecordIndex
  Return 256 \ ! Code that says "No byte given in file"
End
! ****** Begin Main program ********
  If col(0)>1
  Then
      Input "" Line$
      Print Version$
      Print "Terse mode not implemented."
      Error 104
  Else
      Print Version$
      Input "Reference File:                   " Line$
      Open #Ref,Line$
      CollectLoadRecords(Ref,ReferenceRecords)
      Input "Compare-to File:                  " Line$
      Open #Target,Line$
      CollectLoadRecords(Target,TargetRecords)
      Input "Send result to (default=CONSOLE:) " Line$
      If Line$=""
      Then Output=0
      Else
          Create #Output,Line$
      Fi
  Fi
  If ReferenceRecords[0,1]<>TargetRecords[0,1]
  Then
      Print #Output,"***** Ref Object Type ";Hex$(ReferenceRecords[0,1])[4,2];
      Print #Output," <> Target Object Type ";Hex$(TargetRecords[0,1])[4,2]
  Fi
  If ReferenceRecords[0,2]<>TargetRecords[0,2]
  Then
      Print #Output,"Ref Start Address ";Hex$(ReferenceRecords[0,2]);
      Print #Output," <> Target Start Address ";Hex$(TargetRecords[0,2])
  Fi
  If ReferenceRecords[0,0]>0
  Then
      ! Some Reference Load records exist
      Let LowestAddress=ReferenceRecords[1,0] \ ! Lowest Reference Address
      Let HighestAddress=ReferenceRecords[ReferenceRecords[0,0],0]+...
&                        ReferenceRecords[ReferenceRecords[0,0],1]-1
  Else
      Let LowestAddress=:0000 \ ! No bounds specified, use complete address space

      Let HighestAddress=:FFFF
  Fi
  If TargetRecords[0,0]>0
  Then
      ! Some Target Load records exist
      If LowestAddress>TargetRecords[1,0]
      Then LowestAddress=TargetRecords[1,0] \ ! Lowest Target Address
      If HighestAddress<TargetRecords[TargetRecords[0,0],0]+...
&                       TargetRecords[TargetRecords[0,0],1]-1
      Then HighestAddress=TargetRecords[TargetRecords[0,0],0]+...
&                        TargetRecords[TargetRecords[0,0],1]-1 Fi
  Fi
  For Address=LowestAddress to HighestAddress
      RefByte=FetchByte(Ref,ReferenceRecords,Address)
      !D! Print "DEBUG: ";Hex$(Address),Hex$(RefByte)
      TargetByte=FetchByte(Target,TargetRecords,Address)
      If RefByte<>TargetByte
      Then
          Print #Output,"Address ";Hex$(Address);" Ref: ";
          Print #Output,If RefByte>255 Then "xx" Else Hex$(RefByte)[4,2] Fi;
          Print #Output," Target: ";
          Print #Output,If TargetByte>255 Then "xx" Else Hex$(TargetByte)[4,2] Fi

      Fi
  Next Address
  Print #Output,"Binary Comparison completed."
  Exit
END
.