【汇编】汇编的helloWorld

过程

汇编源代码 -> 编译 -> 链接 -> 执行exe。

编译:将源码翻译成机器代码,生成目标(obj)文件。有 x 个源码文件就会生成 x 个目标文件。
链接:将目标文件进行链接,生成exe文件。

在dos中,shell是一直运行的进程,当执行汇编exe时,shell会将exe加载进内存,shell中断暂停运行,并设置CPU的CS:IP指向入口。exe执行结束后中断返回至shell,shell继续运行。

exe

exe中包含两部分文件,程序(exe)和数据;相关的描述信息(map),比如程序有多大,占用的内存空间等。

在操作系统中,操作系统依照可执行文件中的描述信息,加载机器码和数据,并进行相关初始化(比如设置cs:ip指向第一条要执行的指令),然后CPU执行程序。

dos加载exe的过程

  1. 找到一段起始地址SA:0,起始地址的偏移地址为0的容量足够的空闲内存区。
  2. 在这个内存去的前256个字节,创建一个数据区(PSP),DOS利用PSP来和家在程序进行通信。
  3. 程序被加载在PSP后面,程序地址被设为SA+10H:0。
  4. 将该内存区(程序起始内存地址,不是程序入口地址,不是CS:IP,这里包含了PSP区域)的段地址存入ds,初始化其他寄存器,设置CS:IP指向程序的入口。

需要特别注意的是:PSP区域是属于程序内存区域的,相当于ds:0这里既是PSP区域也是程序内的内存地址,从ds:100后开始是程序指令的入口地址。
而不是说PSP区域不是程序内存,他们不是分开的。

访问psp区域:ds:0

为什么是 SA+10H:0

程序加载后,ds存放着程序所在内存区的段地址,这个内存区的偏移地址为0,即地址为ds:0。

这个内存区的前256个字节是PSP,256之后是程序。

所以,PSP的段地址是SA,PSP偏移地址是0,所以物理地址是SA*16 + 0。
因为PSP占用256字节,所以程序物理地址是:

SA * 16 + 0 + 256 = SA * 16 + 16 * 16 + 0 = (SA+16)*16 + 0
简化乘法分配律,即得SA + 10H:0。

SA * 16 + 0 是起始地址,加上 PSP 的大小后就是程序的物理地址。

PSP区域相关

PSP是dos和程序通信的模块,比如有一些命令行参数等信息会保存在这里。
头字节是 CD 20,是指令int 20H码,作用是退出程序,返回DOS。

int 21h 和 int 20h 是 DOS(Disk Operating System)中用于系统调用的软中断指令。

int 21h 用于调用 DOS 提供的各种服务程序,如读写文件、创建进程、获取系统时间等。根据不同的功能需要,在 AH 寄存器中传入对应的功能号,同时还可以传入其他必要的参数和数据。
例如,AH=02h 表示显示一个字符,需要将待显示的字符存放在 DL 寄存器中。通过 int 21h,程序可以利用 DOS 提供的功能实现文件的读写、屏幕输出等操作。

int 20h 用于终止程序的执行,其作用相当于执行 return 语句。当程序执行到 int 20h 指令时,程序将停止执行并返回操作系统,释放占用的内存资源等。

需要注意的是,在现代操作系统中,int 21h 和 int 20h 已经不再是常用的系统调用方式,取而代之的是更为先进、高效的 API 接口。

INT 21H 功能号码表
https://blog.51cto.com/u_15127637/4342756

源码分析

assume cs:code, ds: data, ss: stack

data segment
         dw 1, 2, 3, 4
data ends

stack segment stack
          dw 0H,0H,0H,0H

stack ends

code segment
             
    start:   mov  ax, stack
             mov  ss, ax
             mov  sp, 16
             
             mov  ax, data
             mov  ds, ax
             mov  cx, 8

             mov  ax, 4c00h
             int  21h

code ends

end start

伪指令

汇编指令有对应的机器码指令,可以被编译为机器指令,CPU最终执行。
伪指令是编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。

XXX segemnt / XXX ends

这是一对指令伪指令,作用是定义一个段,segment说明一个段开始,ends说明一个段结束。

段名 segment
  ...
段名 ends

这里的段名可以任意取名。

end

这里的end是end start中的end,不是段的ends,代表了对源程序的编译,写完程序要加上end,不然编译器不知道在何时结束。

start

start是一个标号,代表程序入口在此。程序加载到内存后,CS:IP会指向这个标号,从start指向的指令开始运行。

start换成main也行,start不一定在程序代码最前面,也有可能在中间,前面是可以有指令或者数据的,但这样很不规范,在代码段里放置变量。

assume

定义标记符,将segement...end的段和某一寄存器相关联。

assume cs:code
定义了一个代码段,使用了assume来关联了cs寄存器。

程序的返回

当程序运行结束之后就要返回,返回程序段如下。

mov ax, 4c00H
int 21H

调用 INT 21H 的 4CH 号中断,安全退出程序。

posted @ 2023-08-01 17:36  阿初  阅读(58)  评论(0编辑  收藏  举报