Page 1 of 1

IntelHexFile stuff

Posted: Thu Aug 05, 2010 1:39 pm
by infratec
Hi,

I've done it several times now.
But the first time in PureBASIC.

IntelHex.pbi

Code: Select all

;
; IntelHex include file V1.01
;
; if ExtendedSegmentAddresseRecords (02) is set to #True, than
; this type is used instead of ExtendedLinearAddressRecords (04)
; when creating large hex files
; 

Enumeration
  #INTELHEX_NOT_ABLE_TO_LOAD_FILE = 1
  #INTELHEX_NOT_ABLE_TO_WRITE_FILE
  #INTELHEX_UNABLE_TO_ALLOCATE_MEMORY
  #INTELHEX_ADDRESS_OUT_OF_RANGE
  #INTELHEX_CHECKSUM_WRONG
EndEnumeration

Enumeration
  #INTELHEX_DataRecord
  #INTELHEX_EndOfFileRecord
  #INTELHEX_ExtendedSegmentAddressRecord
  #INTELHEX_StartSegmentAddressRecord
  #INTELHEX_ExtendedLinearAddressRecord
  #INTELHEX_StartLinearAddressRecord
EndEnumeration


Structure IntelHexFileStr
  HexFilename$
  BinFilename$
  BufferSize.l
  *Buffer
  StartAddress.l
  EndAddress.l
  Offset.l
  ExtendedSegmentAddressesRecords.a
  Error.i
  ErrorLine.i
EndStructure



Procedure.s IntelHexError(*Info.IntelHexFileStr)
  
  Result$ = ""
  
  Select *Info\Error
    Case #INTELHEX_NOT_ABLE_TO_LOAD_FILE
      Result$ = "Not able to open the file"
    Case #INTELHEX_NOT_ABLE_TO_WRITE_FILE
      Result$ = "Not able to create the file"
    Case #INTELHEX_UNABLE_TO_ALLOCATE_MEMORY
      Result$ = "Unabel to allocate memory"
    Case #INTELHEX_ADDRESS_OUT_OF_RANGE
      Result$ = "Address is larger than the buffer size (" + Str(*Info\BufferSize) + ") in line " + Str(*Info\ErrorLine)
    Case #INTELHEX_CHECKSUM_WRONG
      Result$ = "Wrong checksum in line " + Str(*Info\ErrorLine)
  EndSelect
  
  ProcedureReturn Result$
  
EndProcedure



Procedure.a IntelHexCalcChecksum(Line$)
  
  Length = (Len(Line$) - 3) >> 1
    
  Checksum.a = 0
  For i = 0 To Length - 1
    Checksum + Val("$" + Mid(Line$, 2 + i * 2, 2))
  Next i
  
  ProcedureReturn ~Checksum + 1
EndProcedure




Procedure IntelHexDecodeLine(*Info.IntelHexFileStr, Line$)
  
  If Left(Line$, 1) = ":"
    
    If IntelHexCalcChecksum(Line$) = Val("$" + Right(Line$, 2))
      
      Type = Val("$" + Mid(Line$, 8, 2))
      
      Select Type
        Case #INTELHEX_DataRecord
          Length = Val("$" + Mid(Line$, 2, 2))
          Start = *Info\Offset + Val("$" + Mid(Line$, 4, 4))
          
          If Start + Length <= *Info\BufferSize
            
            For i = 1 To Length
              PokeA(*Info\Buffer + Start + (i - 1), Val("$" + Mid(Line$, 8 + i * 2, 2)))
            Next i
            If *Info\StartAddress > Start : *Info\StartAddress = Start : EndIf
            If *Info\EndAddress < (Start + Length) : *Info\EndAddress = Start + Length : EndIf
      
          Else
            *Info\Error = #INTELHEX_ADDRESS_OUT_OF_RANGE
          EndIf
      
        Case #INTELHEX_EndOfFileRecord
          ; no need to detect it
          
        Case #INTELHEX_ExtendedSegmentAddressRecord
          *Info\Offset = Val("$" + Mid(Line$, 10, 4)) << 4
          Debug Hex(*Info\Offset)
          
        Case #INTELHEX_StartSegmentAddressRecord
          ; no need for CS:IP here (start address for X86)
          
        Case #INTELHEX_ExtendedLinearAddressRecord
          *Info\Offset = Val("$" + Mid(Line$, 10, 4)) << 16
          
        Case #INTELHEX_StartLinearAddressRecord
          ; no need for EIP here (start address for X86 protected mode)
          
     EndSelect
     
   Else
     *Info\Error = #INTELHEX_CHECKSUM_WRONG
   EndIf
  EndIf
    
EndProcedure




Procedure IntelHexLoadHexFile(*Info.IntelHexFileStr)
  
  *Info\StartAddress = *Info\BufferSize
  *Info\Offset = 0
  *Info\EndAddress = 0
  *Info\Error = 0
  *Info\ErrorLine = 0
  
  If ReadFile(0, *Info\HexFilename$)
    
    If *Info\Buffer : FreeMemory(*Info\Buffer) : EndIf
    *Info\Buffer = AllocateMemory(*Info\BufferSize)
    If *Info\Buffer    
      LineNo = 0
      While Not Eof(0) And Not *Info\Error
        LineNo + 1
        IntelHexDecodeLine(*Info, ReadString(0))      
      Wend
      If *Info\Error : *Info\ErrorLine = LineNo : EndIf
    Else
      *Info\Error = #INTELHEX_UNABLE_TO_ALLOCATE_MEMORY
    EndIf
    CloseFile(0)
  Else
    *Info\Error = #INTELHEX_NOT_ABLE_TO_LOAD_FILE
  EndIf
  
  ProcedureReturn *Info\Error
  
EndProcedure




Procedure IntelHexLoadBinFile(*Info.IntelHexFileStr)
  
  *Info\Error = 0
  
  If ReadFile(0, *Info\BinFilename$)
    *Info\BufferSize = Lof(0)
    If *Info\Buffer : FreeMemory(*Info\Buffer) : EndIf
    *Info\Buffer = AllocateMemory(*Info\BufferSize)
    If *Info\Buffer
      If ReadData(0, *Info\Buffer, *Info\BufferSize) = *Info\BufferSize
        *Info\StartAddress = 0
        *Info\EndAddress = *Info\BufferSize
      EndIf
    Else
      *Info\Error = #INTELHEX_UNABLE_TO_ALLOCATE_MEMORY
    EndIf
    CloseFile(0)
  Else
    *Info\Error = #INTELHEX_NOT_ABLE_TO_LOAD_FILE
  EndIf
  
  ProcedureReturn *Info\Error
  
EndProcedure




Procedure.s IntelHexEncodeBytes(*Ptr, Index, Length)
  
  Line$ = ":"
  Line$ + RSet(Hex(Length), 2, "0")
  Line$ + RSet(Hex(Index & $FFFF), 4, "0")
  Line$ + "00"
  CalculatedChecksum.a = Length + (Index >> 8) + (Index & $FF)
  For i = 0 To Length - 1
    Byte = PeekA(*Ptr + Index + i)
    Line$ + RSet(Hex(Byte), 2, "0")
    CalculatedChecksum + Byte
  Next i
  
  CalculatedChecksum = ~CalculatedChecksum + 1
  Line$ + RSet(Hex(CalculatedChecksum), 2, "0")
  
  ProcedureReturn Line$
  
EndProcedure




Procedure IntelHexWriteHexFile(*Info.IntelHexFileStr)
  
  If CreateFile(0, *Info\HexFilename$)
    Offset.l = 0
    Help.l = (*Info\EndAddress & $FFFFFFF0) - 1
    For i = *Info\StartAddress To Help Step 16
      If i >> 16 > Offset        
        Offset = i >> 16
        If *Info\ExtendedSegmentAddressesRecords
          HelpLine$ = ":02000002" + RSet(Hex(Offset << 12), 4, "0") + "00"
        Else
          HelpLine$ = ":02000004" + RSet(Hex(Offset), 4, "0") + "00"
        EndIf
        HelpLine$ = Left(HelpLine$, 13) + RSet(Hex(IntelHexCalcChecksum(HelpLine$)), 2, "0")
        WriteStringN(0, HelpLine$)
      EndIf
      WriteStringN(0, IntelHexEncodeBytes(*Info\Buffer, i, 16))
    Next i
    
    If i < *Info\EndAddress
      WriteStringN(0, IntelHexEncodeBytes(*Info\Buffer, i, *Info\EndAddress - i))
    EndIf
    
    WriteStringN(0, ":00000001FF")
    CloseFile(0)
  Else
    *Info\Error = #INTELHEX_NOT_ABLE_TO_WRITE_FILE
  EndIf
  
  ProcedureReturn *Info\Error
  
EndProcedure




Procedure IntelHexWriteBinFile(*Info.IntelHexFileStr)
  
  If CreateFile(0, *Info\BinFilename$)
    WriteData(0, *Info\Buffer, *Info\EndAddress)
    CloseFile(0)
  Else
    *Info\Error = #INTELHEX_NOT_ABLE_TO_WRITE_FILE
  EndIf
  
  ProcedureReturn *Info\Error
  
EndProcedure




Procedure IntelHexWriteFile(*Info.IntelHexFileStr)
  
  Result = 0
  
  If *Info\HexFilename$
    If *Info\BinFilename$ = ""
      *Info\BinFilename$ = Left(*Info\HexFilename$, FindString(*Info\HexFilename$, ".hex", 1)) + "bin"
    EndIf
    Result = IntelHexWriteBinFile(*Info)
  ElseIf *Info\BinFilename$
    If *Info\HexFilename$ = ""
      *Info\HexFilename$ = Left(*Info\BinFilename$, FindString(*Info\BinFilename$, ".bin", 1)) + "hex"
    EndIf
    Result = IntelHexWriteHexFile(*Info)
  EndIf
  
  ProcedureReturn Result
  
EndProcedure




Procedure IntelHexLoadFile(*Info.IntelHexFileStr)
  
  *Info\Error = #INTELHEX_NOT_ABLE_TO_LOAD_FILE
  
  If *Info\HexFilename$
    If *Info\BufferSize = 0 : *Info\BufferSize = 64 * 1024 : EndIf
    IntelHexLoadHexFile(*Info)
  ElseIf *Info\BinFilename$
    IntelHexLoadBinFile(*Info)
  EndIf
  
  ProcedureReturn *Info\Error
  
EndProcedure
and a small example to convert hex->bin and bin->hex named Hexer.pb

Code: Select all

IncludeFile "IntelHex.pbi"


Procedure Usage()
  PrintN("")
  PrintN(" Hexer V1.00")
  PrintN("")
  PrintN("  usage: Hexer file [buffer size]")
  PrintN("")
  PrintN("  file       : *.hex or *.bin")
  PrintN("  buffer size: only for hex files (default is 65536)")
  PrintN("")
EndProcedure


Info.IntelHexFileStr

OpenConsole()

If CountProgramParameters() > 0
  
  Filename$ = ProgramParameter(0)
  If FindString(Filename$, ".hex", 1)
    Info\HexFilename$ = Filename$
    If CountProgramParameters() = 2
      Info\BufferSize = Val(ProgramParameter(1))
    EndIf
  ElseIf FindString(Filename$, ".bin", 1)
    Info\BinFilename$ = Filename$
  EndIf
  
  If IntelHexLoadFile(@Info) = 0
    IntelHexWriteFile(@Info)
  EndIf
  
  If Info\Error
    PrintN(IntelHexError(@Info))
  EndIf
  
Else
  Usage()
EndIf
Now also for type 02 and type 04 records :mrgreen:

Best regards,

Bernd

Re: Load an IntelHexFile

Posted: Thu Aug 05, 2010 2:06 pm
by flaith
I'm using an Arduino, so it will be very useful
Thanks :mrgreen:

Re: Load an IntelHexFile

Posted: Thu Aug 05, 2010 3:46 pm
by Rook Zimbabwe
What is an Intel Hex File? :D

Re: Load an IntelHexFile

Posted: Thu Aug 05, 2010 4:03 pm
by infratec
If you don't know it, you don't need it :!:
:mrgreen: :mrgreen: :mrgreen:

It is a file format where binary data is stored in an ASCII text file.
With address field and checksum per line.

Created by Intel to send programs to the 'old' MCS51 microcontrollers.
Or EPROM burners
Or whatever ...

Re: Load an IntelHexFile

Posted: Thu Aug 05, 2010 8:02 pm
by Joakim Christiansen
Funny, because I made one myself not long ago. :D
Actually I made a simple application to upload hex files to the Arduino bootloader.
But nice of you to share it!

Re: Load an IntelHexFile

Posted: Fri Aug 06, 2010 2:29 am
by Rook Zimbabwe
Oh damn I didn't catch the ardunio thing!!! programmable chip! :oops:

Re: Load an IntelHexFile

Posted: Fri Aug 06, 2010 11:05 am
by akj
Please note that infratec's routine and also the one at http://fixunix.com/cp-m/257-hex-bin-file-converter.html only includes record types 0 and 1 out of the six possible defined types 0..5 .

Also the checksum calculation for each record can be slightly simplified by simply summing all the record bytes (except the leading colon) into a PureBasic .a variable, then if the result is zero, the checksum is correct.

The .HEX file format is defined here:
http://en.wikipedia.org/wiki/Intel_HEX
http://microsym.com/editor/assets/intelhex.pdf

Re: Load an IntelHexFile

Posted: Sat Aug 07, 2010 3:18 pm
by infratec
Hi akj,

this was the first step, since I don't need files larger than 64k.
But I extended my version:

Now it also supports type 02 and type 04 records and it also writes IntelHex files.
It also has an error printing procedure now.
It is a little bit larger now :mrgreen:

I also added a small program called Hexer.pb.
It is a console program.

Simply call it with 'Hexer file.hex' or 'Hexer file.bin' it converts it to the oposite.
The default buffer for hex files is 64k, but you can increase it by an additional parameter
'Hexer test.hex 512000'
For bin files the buffer depends on the file size, no need todo something by hand. :D

By default it generates large hex files with 04 records.
But you can change this when you set ExtendedSegmentAddressesRecords in the struct to
#True. Than 02 records will be used.

Oh, and I use the IntelHexCalculateChecksum() procedure not only to verify the checksum,
I also use it now to create a checksum.
So for me it is simple enough :mrgreen: :mrgreen: :mrgreen:

Have fun,

Bernd

Re: Load an IntelHexFile

Posted: Sat Aug 07, 2010 4:19 pm
by infratec
Found a small bug

inside IntelHexDecodeLine()

Was

Code: Select all

If Start + Len <= *Info\BufferSize
Should be

Code: Select all

If Start + Length <= *Info\BufferSize
I have corrected it above.

If you have already downloaded this, please check it.

Bernd