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
Mỗi dòng sẽ tuân theo Format sau
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)
và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.
Command | Description | |
---|---|---|
0x00 | Data record | |
0x01 | End of File Record | |
0x02 | Extended Segment Address Record | |
0x03 | Start Segment Address Record | |
0x04 | Extended Linear Address Record | |
0x05 | Start 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
.
:
: 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ầnDATA
trong 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à4
và6
. Tra bảng mã ASCII ta được:0x34
và0x36
=> Cần2 bytes
để biểu diễn0x46
.46
: Trong file Bin thì chỉ cần1 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
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ứa81H
(bit cao) và thanh ghi L chứa01H
(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ới8101H
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ới8102H
là03H
với giá trị đã có sẵn trong thanh ghi A05H
=> Giá trị cuối cùng08H
đượ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ố03H
và05H
.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.
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
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