Post

File Hex (Part 1)

File Hex (Part 1)

🌱 Lời mở đầu

Đây là bài viết đầu tiên trên blog của tôi 😎, và tôi muốn dành nó để giải thích chi tiết về file Hex – một khái niệm quan trọng trong thế giới lập trình và hệ thống nhúng.

Tại sao tôi chọn chủ đề này để bắt đầu?

Bởi vì tôi tin rằng file Hex là “ngôn ngữ chung” giúp máy tính, vi điều khiển, và vi xử lý thực thi các lệnh mà chúng ta lập trình. Dù bạn sử dụng Python, Java, C/C++, Assembly, … thì sau khi qua trình biên dịch, tất cả đều được chuyển hóa thành các con số nhị phân 0 và 1. Đây chính là “nguyên liệu cơ bản” để máy móc có thể hiểu và hoạt động.

I. File hex là gì

- Các file có đuôi *.hex được gọi là file HEX. Hex là 1 tiêu chuẩn do Intel quy định. Tên đầy đủ của hex là Interl HEX. Loại file này thường được sử dụng để truyền các chương trình và dữ liệu sẽ được lưu trữ trong ROM hoặc EEPROM. Nó là một file văn bản ASCII (2 kí tự mã ASCII biểu thị 1 byte Hex) bao gồm các dòng văn bản phù hợp với định dạng file Intel HEX.

- File này chủ yếu được sử dụng để lưu firmware.

II. Đinh dạng file Hex

Dưới đây là hình ảnh minh họa về 1 file.hex

Hex Code Example Hex Code

Mỗi dòng sẽ tuân theo Format sau

Format Code Format Code

  • Start code: mỗi dòng được coi là 1 frame, và được bắt đầu bằng dấu hai chấm “ : “.
  • Byte count: Hai chữ số thập lục phân cho biết số byte trong Data. Số byte tối đa là 255 (0xFF), 16 (0x10)32 (0x20) là các byte thường được sử dụng.
  • Address: Bốn chữ số thập lục phân cho biết độ lệch (offset) địa chỉ. Địa chỉ vật lí của dữ liệu được tính bằng cách thêm phần offset này vào địa chỉ cơ sở.
  • Record type: Hai chữ số thập lục phân từ 0x00 -> 0x05.
CommandDescription 
0x00Data record 
0x01End of File Record 
0x02Extended Segment Address Record 
0x03Start Segment Address Record 
0x04Extended Linear Address Record 
0x05Start Linear Address Record 
  • Data: chuỗi dữ liệu n-byte.
  • Checksum: Hai chữ số thập lục phân được sử dụng để xác minh rằng giá trị tính toán được ghi không có lỗi.

Example 1: :10000000A8990020C1010408830B040839080408DA.

Difference File Bin and File Hex

  • :: Bắt đầu.
  • 0x10: Số byte data = 16.
  • 0x00, 0x00: Địa chỉ offset nơi để lưu dữ liệu. (Ta có thể hiểu là 0x0000)
  • 0x00: Kiểu dữ liệu.
  • 0xA8 , 0x99, 0x00, 0x20, 0xC1, 0x01, 0x04, 0x08, 0x83, 0x0B, 0x04, 0x08, 0x39, 0x08, 0x04, 0x08: Data (Tổng cộng có 16 bytes).
  • Byte Checksum = 0xDA.

Để có thể kiểm tra lỗi trong frame thì:

  • B1: Tổng giá trị các byte có trong frame = 0x10 + 0x00 + 0x00 + 0x00 + 0xA8 + … + 0x04 + 0x08 = 0x326
  • B2: Lấy byte cuối cùng = 0x26.
  • B3: Tổng checksum = (0x100 - byte cuối) & 0xFF = (0x100 - 0x26) & 0xFF = 0xDA -> Ta thấy được ở cuối frame cũng là 0xDA nên frame này không có ERROR.

Example 2: :00000001FF.

Đây là dòng cuối cùng trong hình ảnh bên trên.

  • :: Bắt đầu.
  • 0x00: Số byte data = 0.
  • 0x00, 0x00: Địa chỉ offset nơi để lưu dữ liệu.
  • 0x01: End of File Record –> Kết thúc file HEX.
  • Data: None
  • Byte Checksum = 0xFF

III. Sự khác biệt giữa File.hex và File.bin

1. Cách sử dụng

  • File *.hex: Khi lập trình và tải xuống file Hex, người dùng không cần phải chỉ định địa chỉ vì thông tin bên trong File Hex đã bao gồm địa chỉ.
  • File *.bin: Khi ghi file BIN, người dùng cần phải chỉ định địa chỉ do File Bin chỉ có phần DATA trong file HEX. Difference File Bin and File Hex

Trái là File.hex - Phải là File.bin.

2. Kích thước File

Như đầu bài viết tôi có đề cập: File.hex là một file văn bản ASCII. Mỗi kí tự trong file Hex sẽ có kích thước là 1 byte.

=> File Hex ít nhất phải có kích thước lớn hơn gấp đôi so với File Bin.

Để dễ hiểu hơn thì tôi sẽ lấy ví dụ từ hình so sánh bên trên.

  • 46: Trong file Hex sẽ bao gồm 2 kí tự là 46. Tra bảng mã ASCII ta được: 0x340x36 => Cần 2 bytes để biểu diễn 0x46.
  • 46: Trong file Bin thì chỉ cần 1 byte = 0x46.

Suy ra:

  • File Hex có 10 bytes data thì phải dùng ít nhất 20 byte (chưa kể các bytes để mô tả Address, Byte count, …) để biểu diễn.
  • File Bin thì cũng chỉ cần 10 byte là đủ.

    ✏️ Practice

    ASM 8085 Assembly of 8085

Chương trình trên dùng để cộng 2 số:

  • 8101H: chứa giá trị 05H của số đầu tiên.
  • 8102H: chứa giá trị 03H của số thứ hai.

Giải thích asm:

  • LXI H,8101: nạp giá trị 8101H (16-bit) vào cặp thanh ghi H chứa 81H (bit cao) và thanh ghi L chứa 01H (bit thấp). Thanh ghi HL đang trỏ tới địa chỉ 8101H là nơi chứa giá trị của số đầu tiên.
  • MOV A, M: dùng để di chuyển giá trị của ô nhớ tại địa chỉ mà thanh ghi HL đang trỏ tới 8101H vào thanh ghi A -> Thanh ghi A hiện tại có giá trị = 05H.
  • LNX H: tăng giá trị thanh ghi HL lên 1 => Vị trí tiếp theo mà thanh ghi HL trỏ tới là 8102H nơi chứa giá trị 03H của số thứ 2.
  • ADD M: cộng giá trị tại ví trí thanh ghi HL đang trỏ tới 8102H03H với giá trị đã có sẵn trong thanh ghi A 05H => Giá trị cuối cùng 08H được lưu trong thanh ghi A.
  • STA 8103: lưu nội dung của thanh ghi A vào địa chỉ 8103H. Đây là nơi lưu trữ giá trị 08H kết quả của phép cộng 2 số 03H05H.
  • RST 3: nhảy đến dịa chỉ vector ngắt để thực hiện chương trình con or kết thúc.

Những câu lệnh ASM trên thì cho con người hiểu nhưng để CPU hiểu thì cần phải chuyển ASM sang HEX. Để thực hiện việc này ta dùng Assembler. Và Assembler sẽ dựa vào OPCODE để có thể chuyển từ file.asm sang file.hex or file.bin. (Tham khảo Opcode table của 8085)

  • LXI H,8101 -> Hex Code: 21, 81, 01
    • LXI có opcode: 21H
    • 81, 01: giá trị của lệnh OPCODE này
  • MOV A, M -> Hex Code: 7E
    • MOV có opcode: 7E.

Opcode của các lệnh còn lại bạn tự tìm hiểu.

OPCODE table of 8085 Opcode Table of 8085

IV. Lưu trữ data trong Memory

Ở các mục trước ta đã hiểu được cách nào file *.hex được tạo ra và tác dụng của nó. Nhưng ta có thắc mắc 🤔 file.hex đó sẽ được lưu trữ như thế nào trong bộ nhớ.

File.hex được lưu như thế nào trong memory ?

Trước tiên hãy nhìn vào hình ảnh sau đây, trong ví dụ này tôi dùng bộ nhớ có 32-bits address –> Có tổng cộng 4GB để có thể lưu trữ. Memory of 32-bits Address Memory

Mỗi ô nhớ (8-bit) sẽ có 1 địa chỉ (32-bit) tương ứng. Giá trị của mỗi ô nhớ này là phần DATA trong File.hex. (Mỗi ô nhớ = 8-bit đều đúng trong address 8-bit, 16-bit, 64-bit)

Example:

  • 0x0000 0000: chứa giá trị 0100 1111.
  • 0x0000 0003: chứa giá trị 0110 1011.
  • 0x0000 0008: chứa giá trị 0100 1001.

Example: Lấy hình ảnh từ phần trước, ở đây chỉ xét 3 dòng đầu.

- Dòng 1: Quyết định địa chỉ cơ sở băt đầu để lưu trữ data. Phần DATA = 6000 nghĩa là 0x6000 (16-bits cao trong 32-bits địa chỉ).

- Dòng 2:

  • 16-bits thấp: 0x0000 => Địa chỉ chính xác mà DATA bắt đầu được lưu trữ là: 0x6000 0000.
  • Data gồm 16 bytes: 0x46, 0x43, 0x46, 0x42, 0x00, 0x04, 0x01, 0x56, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x00.

- Dòng 3:

  • 16-bits thấp: 0x0010 => Địa chỉ lưu trữ DATA tiếp theo: 0x6000 0010.
  • Data gồm 16 bytes: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00.

Hi vọng qua hình ảnh này có thể giải thích rõ hơn.

🍂 Lời kết

-Qua bài viết này, tôi hy vọng bạn sẽ hiểu được toàn bộ những nội dung mà tôi muốn truyền đạt. Nếu có bất kỳ thắc mắc nào, đừng ngần ngại để lại bình luận bên dưới, tôi luôn sẵn sàng giải đáp.

-Trong tương lai, tôi sẽ tiếp tục giải thích chi tiết các khái niệm được đề cập trong bài, chẳng hạn như OPCODE, CPU, trình biên dịch (compiler), … và nhiều nội dung thú vị khác.

-Phần tiếp theo của bài viết: Part 2

🌏 Reference

  • https://scienceprog.com/shelling-the-intel-8-bit-hex-file-format/
  • Opcode 8085
  • https://programmedlessons.org/AssemblyTutorial/Chapter-04/ass04_05.html
This post is licensed under CC BY 4.0 by the author.