Assembly MZ(DOS,16位).EXE标头中页数和最后一页大小的重要性
我正在尝试学习如何使用汇编(NASM)创建Dos.EXE文件,手动构造头文件并将文件汇编为二进制文件。我对页面选项(总页数和最后一页的字节数)有问题。无论我设置的初始值有多小,程序都会工作 作为一种极端情况,即使设置1页1字节,以下程序仍能正常工作:Assembly MZ(DOS,16位).EXE标头中页数和最后一页大小的重要性,assembly,dos,16-bit,Assembly,Dos,16 Bit,我正在尝试学习如何使用汇编(NASM)创建Dos.EXE文件,手动构造头文件并将文件汇编为二进制文件。我对页面选项(总页数和最后一页的字节数)有问题。无论我设置的初始值有多小,程序都会工作 作为一种极端情况,即使设置1页1字节,以下程序仍能正常工作: ; ; the smallest possible "Hello, World!" .EXE (DOS MZ) file ; assemble with: ; nasm -f bin -w+all -O0 smallest_hello_exe.as
;
; the smallest possible "Hello, World!" .EXE (DOS MZ) file
; assemble with:
; nasm -f bin -w+all -O0 smallest_hello_exe.asm -o ASM.EXE
;
bits 16
cpu 8086
;
; by setting cs:ip=-10h:100h instead of 0h:0h inside the .EXE header
; (identical assignments), we achieve the following two advantages:
; 1) ds==cs, so no "push cs pop ds" is needed in order for ds:dx
; to point to the message string
; 2) we can exit by int 20h instead of int 21h, thus omitting the
; ah=4ch assignment
; (int 20h requires that cs points to the PSP segment)
;
;
; we do not the address calculations to take the .EXE header into account
; so we must subtract its length (20h) by an "org -20h"
; but, since ip will be 100h, we must also issue an "org 100h"
; and, since 0x100-0x20=0xE0...
org 0xE0 ; 100h for ip value - 20h for header
section .text align=1
;
; the MZ .EXE header structure
; 28 bytes long
; 1 pararaph equals 16 bytes
; 1 page equals 512 bytes
; suggested reading: int 21h,ah=4bh procedure
;
host_exe_header:
.signature: dw 'MZ' ; the 'MZ' characters
.last_page_size: dw 1 ; number of used bytes in the final file page, 0 for all
.page_count: dw 1 ; number of file pages including any last partial page
.reloc: dw 0 ; number of relocation entries after the header
.paragraphs: dw 2 ; size of header + relocation table, in paragraphs
.minalloc: dw 0 ; minimum required additional memory, in paragraphs
.maxalloc: dw 0xFFFF ; maximum memory to be allocated, in paragraphs
.in_ss: dw 0 ; initial relative value of the stack segment
.in_sp: dw 0xF000 ; initial sp value
.checksum: dw 0 ; checksum: 1's complement of sum of all words
.in_ip: dw 100h ; initial ip value
.in_cs: dw -10h ; initial relative value of the text segment
.offset: dw 0 ; offset of the relocation table from start of header
.overlay: dw 0 ; overlay value (0h = main program)
; pad header (its size in bytes must be a multiple of 16)
times (32-$+$$) db 0
mov dx,message
mov ah,09h ; write string ds:dx to stdout
int 21h
int 20h
section .data align=1
message: db 'Hello, World!$'
section .bss align=1
通过试验不同的程序大小,我得出结论,Dos会将每个页面的所有512字节加载到内存中。如果是,最后一页中字节数的用途是什么
它会干扰.bss、堆栈数据和/或动态内存分配吗?总页数绝对不会被忽略,它甚至会被不希望最初加载所有文件的程序使用。他们稍后将自己阅读必要的片段。最后一页中的
字节可以忽略,也可以不忽略,具体取决于操作系统版本。它也可以四舍五入到段落或磁盘扇区边界。你不应该依赖于某个特定的行为并正确地填写它
您的测试代码之所以有效,是因为它很小,而且您的特定操作系统选择将足够的测试代码加载到内存中。如果使程序大于单个页面,但仍在页面计数
字段中指定1
,则代码可能无法完全加载且无法工作。我试过:
times (32-$+$$) db 0
times (512) nop
mov dx,message
mov ah,09h ; write string ds:dx to stdout
int 21h
int 20h
如果页面计数
为1,则此操作失败,但如果页面计数
为2(用于测试dosbox
),则此操作有效。页面计数字段由DOS使用,而不是由Windows NT使用
NT使用的IMAGE\u DOS\u头的仅有两个字段是e\u magic(必须是IMAGE\u DOS\u签名)和e\u lfanew,从IMAGE\u DOS\u头的开始到包含NT加载程序所有信息的IMAGE\u NT\u头结构的偏移量小于4MB