# 第 10 章：CALL 和 RET 指令

## 检测点 10.1

补全程序 ，实现从内存 1000:0000 处开始执行指令。

```
assume cs:code

stack segment
    db 16 dup (0)
stack ends

code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov ax,1000h    ; CS，先入后出
    push ax
    mov ax,0000h    ; IP
    push ax
    retf
code ends

end start
```

## 检测点 10.2

下面的程序执行后，ax 中的数值为多少？

| 内存地址    | 机器码      | 汇编指令     | ax        | IP    |
| ------- | -------- | -------- | --------- | ----- |
| 1000: 0 | b8 00 00 | mov ax,0 | 0000h     | 0003h |
| 1000: 3 | e8 01 00 | call s   |           | 0006h |
| 1000: 6 | 40       | inc ax   | （该行未执行）   |       |
| 1000: 7 | 58       | s:pop ax | **0006h** | 0008h |

## 检测点 10.3

下面的程序执行后，ax 中的数值为多少？

| 内存地址    | 机器码            | 汇编指令           | ax        | bx        | CS    | IP    | 栈                        |
| ------- | -------------- | -------------- | --------- | --------- | ----- | ----- | ------------------------ |
| 1000: 0 | b8 00 00       | mov ax,0       | 0000h     |           |       |       |                          |
| 1000: 3 | 9A 09 00 00 10 | call far ptr s |           |           | 1000h | 0008h | <p>0008h</p><p>1000h</p> |
| 1000: 8 | 40             | inc ax         | （该行未执行）   |           |       |       |                          |
| 1000: 9 | 58             | s: pop ax      | 0008h（IP） |           |       |       | 1000h                    |
|         |                | add ax , ax    | 0010h     |           |       |       |                          |
|         |                | pop bx         |           | 1000h（CS） |       |       |                          |
|         |                | add ax,bx      | **1010h** |           |       |       |                          |

## 检测点 10.4

下面的程序执行后，ax 中的数值为多少？

| 内存地址    | 机器码      | 汇编指令          | ax          | IP    | 栈     |
| ------- | -------- | ------------- | ----------- | ----- | ----- |
| 1000: 0 | b8 06 00 | mov ax,6      | 0006h       | 0003h |       |
| 1000: 3 | ff d0    | call ax       | （跳转到 0006h） | 0005h | 0005h |
| 1000: 5 | 40       | inc ax        |             |       |       |
| 1000: 6 |          | mov bp,sp     |             |       |       |
|         |          | add ax, \[bp] | **000Bh**   |       |       |

## 检测点 10.5

（1）下面的程序执行后，ax 中的数值为多少？（注意：用 call 指令的原理来分析，不要在 Debug 中单步跟踪来验证你的结论。对于此程序，在 Debug 中单步跟踪的结果，不能代表 CPU 的实际执行结果。）

```
assume cs:code

stack segment
    dw 8 dup (0)
stack ends

code segment
start:
    mov ax,stack            ; ax 存储 stack 的段地址
    mov ss,ax               ; ss 指向 stack 段
    mov sp,16               ; sp = 16，而 stack 是 8 字 == 16 字节，正好指向栈顶
    mov ds,ax               ; ds 指向 stack 段
    mov ax,0                ; ax = 0000h
    call word ptr ds:[0EH]  ; 将 CS 和 IP 分别压栈，此时 IP 为该指令后的第一个字节
                            ; 跳转地址为 ds:0E ~ ds:0F，而 ds 指向 stack 段，且栈顶恰好为第 16 个字节
                            ; 因此该 call 指令即跳转到栈顶存储的 CS 和 IP 指向的位置
                            ; 而栈顶存储的 IP 恰好指向的是下一条指令（inc ax）的地址
                            ; 故这条指令执行完，直接执行下面的指令
    inc ax                  ; ax = 0001h
    inc ax                  ; ax = 0002h
    inc ax                  ; ax = 0003h <- ax 最后的值
    mov ax,4c00h
    int 21h
code ends

end start
```

为什么不让使用 debug 来逐步调试？因为在执行到 CALL 指令时，显示 ds:\[0eh]＝065D（在我的环境中debug 后），ds:\[0eh] 中的内容是中断的信息，并不是我们程序所希望的 0011H，它一下就 jmp 到 065D 的地址去了，而不是 CS:11 地址执行。

（2）下面的程序执行后， ax 和 bx 中的数值为多少？

```
assume cs:code

data segment
    dw 8 dup (0)
data ends

code segment
start:
    mov ax,data                     ; ax = data 段地址
    mov ss,ax                       ; ss 指向 data 段
    mov sp,16                       ; sp = 16
    mov word ptr ss:[0] , offset s  ; ss:[0] 指向 s 的偏移地址
    mov ss:[2],cs                   ; ss:[2] 指向段地址 CS
    call dword ptr ss:[0]           ; 分别将 CS 和 IP 压栈，sp = 12 = 0cH
                                    ; CS 位于 ss:[0eH]，IP 位于 ss:[0cH]
                                    ; 入栈的 IP 为该指令后的第一个字节的偏移地址
                                    ；也即下面 nop 对应的地址
                                    ; 执行完跳转至 ss:[0]，也即 s
    nop                             ; 机器码 90，占据一字节
s:
    mov ax,offset s                 ; ax = s 的偏移地址
    sub ax,ss:[0cH]                 ; ax = ax - ss:[0cH]，也即：
                                    ; ax = s 的偏移地址 - nop 的偏移地址，而 nop 为 1字节
                                    ; 故 ax = 0001h
    mov bx,cs                       ; bx = CS 存储的段地址
    sub bx,ss:[0eH]                 ; bx = CS 存储的段地址 - ss:[0eH]
                                    ; 而 ss:[0eH] 中恰好为 CS 地址
                                    ; 故 bx = 0000h
    mov ax,4c00h
    int 21h
code ends

end start
```

### 参考链接

* 汇编语言（王爽第三版）检测点10 - 筑基2017 - 博客园&#x20;
  * <https://www.cnblogs.com/Base-Of-Practice/articles/6883922.html>

## 实验 10 编写子程序

### 一、显示字符串

显示字符串是现实工作中经常要用到的功能，应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口，使调用者可以决定显示的位置（行、列）、内容和颜色。

|      | 描述                                                                                       |
| ---- | ---------------------------------------------------------------------------------------- |
| 名称   | show\_str                                                                                |
| 功能   | 在指定的位置，用指定的颜色，显示一个用 0 结束的字符串。                                                            |
| 参数   | <p>(dh)＝行号（取值范围 0~~24）</p><p>(di)＝列号（取值范围 0~~79）</p><p>(cl)＝颜色</p><p>ds:si 指向字符串的首地址</p> |
| 返回   | 无                                                                                        |
| 应用举例 | 在屏幕的 8 行 3 列，用绿色显示 data 段中的字符串。                                                          |

```
```

### 参考链接

* 汇编语言（王爽第三版）实验10：编写子程序 - 筑基2017 - 博客园&#x20;
  * <https://www.cnblogs.com/Base-Of-Practice/articles/6883929.html>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hansimov.gitbook.io/asm-lang/solutions/ch10-call-and-ret-instruction.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
