IntelHexFile stuff

Share your advanced PureBasic knowledge/code with the community.
infratec
Always Here
Always Here
Posts: 7623
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

IntelHexFile stuff

Post 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
Last edited by infratec on Sat Aug 07, 2010 4:29 pm, edited 4 times in total.
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Re: Load an IntelHexFile

Post by flaith »

I'm using an Arduino, so it will be very useful
Thanks :mrgreen:
“Fear is a reaction. Courage is a decision.” - WC
User avatar
Rook Zimbabwe
Addict
Addict
Posts: 4322
Joined: Tue Jan 02, 2007 8:16 pm
Location: Cypress TX
Contact:

Re: Load an IntelHexFile

Post by Rook Zimbabwe »

What is an Intel Hex File? :D
Binarily speaking... it takes 10 to Tango!!!

Image
http://www.bluemesapc.com/
infratec
Always Here
Always Here
Posts: 7623
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Load an IntelHexFile

Post 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 ...
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Re: Load an IntelHexFile

Post 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!
Last edited by Joakim Christiansen on Fri Aug 06, 2010 7:00 am, edited 1 time in total.
I like logic, hence I dislike humans but love computers.
User avatar
Rook Zimbabwe
Addict
Addict
Posts: 4322
Joined: Tue Jan 02, 2007 8:16 pm
Location: Cypress TX
Contact:

Re: Load an IntelHexFile

Post by Rook Zimbabwe »

Oh damn I didn't catch the ardunio thing!!! programmable chip! :oops:
Binarily speaking... it takes 10 to Tango!!!

Image
http://www.bluemesapc.com/
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Re: Load an IntelHexFile

Post 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
Anthony Jordan
infratec
Always Here
Always Here
Posts: 7623
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Load an IntelHexFile

Post 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
infratec
Always Here
Always Here
Posts: 7623
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Load an IntelHexFile

Post 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
Post Reply