实验一 软中断实验-DOS功能调用 (2)

XDU计组实验一 软中断实验-DOS功能调用


原文链接,观看更丝滑:(实验一 软中断实验-DOS功能调用)


  • 实验目的
    1. 掌握汇编语言编程工具。
    2. 掌握中断基本功能调用方法。
    3. 掌握汇编语言编写、编译、调试方法。
  • 实验内容
    1. 在屏幕上显示示“Hello World!”字符串。
    2. 从键盘输人自己的姓名和学号,并回显输出到屏幕。
    3. 从键盘输入循环读取英文字符,并将其对应的ASCII码输出到屏幕。真至键盘输入“Q”或“q”
    4. 注:1、2、3写成一个源程序。
  • 实验原理
    1. 英文字符显示为 ASCII 码算法见后图。
    2. INT 21H中断 01、02、09、0A 等功能调用见后表。
  • 实验步骤
    1. 熟悉INT 21H软中断
    2. 编写源程序
    3. 运行、调试

前置知识

参考文章:第一章 基础知识 - Mer_curiail - 博客园


TASM汇编语言的基本格式


MYSTACK SEGMENT PARA ‘STACK’
; DW	  100 DUP(?)
MYSTACK  ENDS 
 
DATA  SEGMENT PARA ‘DATA’
; DATA DEFINE
DATA  ENDS
 
CODE  SEGMENT PARA ‘CODE’
      ASSUME DS:DATA,SS:MYSTACK,CS:CODE
START:
;INSERT YOUR OWN CODES
CODE  ENDS
END START

汇编语言基础知识


汇编语言的组成

  • 汇编指令:机器码的助记符,有对应的机器码
  • 伪指令:没有对应的机器码,由编译器执行,计算机并不执行
  • 其他符号:如+,-,*,/`等,由编译器识别,没有对应的机器码
存储器、指令、数据和存储单元

  • 存储器

存储器是指令和数据存放的地方,也就是我们平时所说的内存。

磁盘不同于内存,磁盘上的数据或程序如果不读到内存中,就无法被CPU利用。

  • 指令和数据

指令和数据只是应用上的概念。在内存或磁盘上。没有任何区别,都是二进制的信息。

  • 存储单元

存储器被划分为多个存储单元,存储单元从0开始编号。

一个存储单元可以存储一个Byte,即一个字节,也就是 8 b i t 8bit 8bit

微机存储器的容量是以字节为最小单位来计算。对于拥有128个存储单元的存储器,它的容量就是128个字节。

同时有以下计量单位:

1 K B = 1024 B 1 M B = 1024 K B 1 G B = 1024 M B 1 T B = 1024 G B 1KB=1024B \quad 1MB=1024KB \quad 1GB=1024MB \quad 1TB=1024GB 1KB=1024B1MB=1024KB1GB=1024MB1TB=1024GB

汇编指令复习

CPU和其他芯片的导线,通常被称为总线。总线从物理上来讲,是一根根导线的集合。根据传送消息的不同,总线又从逻辑上分为:地址总线,控制总线,数据总线。

程序结构

汇编程序通常由几个段(Segment)组成:

  • 数据段(Data Segment):存储数据的地方,定义变量、字符串、缓冲区等。
  • 栈段(Stack Segment):用于存储函数调用时的数据(如返回地址、局部变量等)。
  • 代码段(Code Segment):存储程序的指令部分。
段(Segment)和寄存器(Register)

  • 每个段都有一个寄存器指向它:
    • CS:代码段寄存器
    • DS:数据段寄存器
    • SS:栈段寄存器
    • ES:附加段寄存器
  • MOV AX, data:将数据段的起始地址加载到 AX 寄存器中。
  • MOV DS, AX:将 AX 中的值(即数据段的地址)加载到 DS 中,设置数据段寄存器。
各个寄存器及其用途

寄存器全称名称用途
AXAccumulator累加寄存器算术运算、I/O 操作
BXBase基址寄存器数据指针、索引指针
CXCount计数寄存器循环计数器、字符串操作计数
DXData数据寄存器算术运算、I/O 操作
SPStack Pointer栈指针寄存器指向栈顶,管理堆栈数据
BPBase Pointer基址指针寄存器指向栈帧基址,在函数调用时用于指向当前函数的栈帧
SISource Index源索引寄存器字符串或数组操作中的源地址
DIDestination Index目标索引寄存器字符串或数组操作中的目标地址
DSData Segment数据段寄存器指向数据段,访问程序中的数据
CSCode Segment代码段寄存器指向当前执行代码的地址
SSStack Segment堆栈段寄存器指向栈段,管理堆栈数据
ESExtra Segment额外段寄存器用于字符串和其他特殊内存操作
FSFS Segment源段寄存器用于线程局部存储(TLS)或操作系统的特殊功能
GSGS Segment目标段寄存器用于线程局部存储(TLS)或操作系统的特殊功能
FLAGSFlags Register标志寄存器存储算术运算结果的状态标志

备注:

  • AXBXCXDXSPBPSIDI 是 16 位寄存器,现代 CPU 还提供了对应的 32 位和 64 位扩展寄存器(如 EAXEBXECXEDX 等)。
  • 段寄存器DSCSSSESFSGS 用于访问不同内存段,影响数据和代码的访问。
  • FLAGS** 寄存器**:包含程序执行状态的标志,影响条件跳转和决策操作。
标签

在汇编语言中,标签(Label是程序中的一个标识符,用来标记代码的某个位置。它通常用于与跳转指令(如 JMPJE 等)配合使用,指示程序跳转到某个特定的位置。

  • 标签的概念:
    • 标签是一个标识符,后跟冒号(:)。它通常出现在代码行的开头,用来表示某个特定位置。
    • 标签本身并不占用任何内存,它只是一个符号,用于指示代码位置,便于程序的控制流跳转。
  • 标签的使用:
    • 当程序执行到某个跳转指令时,跳转指令会根据标签来确定程序跳转到哪一行。
    • 标签可以放在代码段的任何地方,通常用来标记循环的开始、结束或特定的条件分支。
start:          ; 标签 start,表示程序的开始位置
    MOV AX, 5    ; 将 5 存入 AX 寄存器
    JMP next     ; 跳转到 next 标签

next:           ; 标签 next,表示跳转后的代码位置
    MOV BX, 10   ; 将 10 存入 BX 寄存器
    ; 程序继续执行...
- `start` 和 `next` 是标签。
- 程序会执行到 `JMP next`,然后跳转到 `next` 标签所在的地方,继续执行后续指令。
常用指令

  • 数据传送指令
    • MOV destination, source:将源数据传送到目标位置(寄存器、内存等)。
    • LEA destination, source:将源地址(指针)加载到目标寄存器中。(load effective address)
  • 算术运算指令
    • ADD destination, source:将源值加到目标值上。
    • SUB destination, source:从目标值中减去源值。
    • MULDIV:乘法和除法。
  • 条件跳转
    • CMP operand1, operand2:比较两个操作数(会影响标志寄存器)。
    • JE (Jump if Equal):如果两个操作数相等,跳转。
    • JNE (Jump if Not Equal):如果两个操作数不相等,跳转。
    • JL (Jump if Less):如果第一个操作数小于第二个操作数,跳转。
    • JLE (Jump if Less or Equal):如果第一个操作数小于或等于第二个操作数,跳转。
    • JG (Jump if Greater):如果第一个操作数大于第二个操作数,跳转.
    • JGE (Jump if Greater or Equal):如果第一个操作数大于或等于第二个操作数,跳转.
    • JMP (Jump):无条件跳转。
  • 中断(Interrupt)
    • INT 21H:调用 DOS 中断,执行操作系统提供的服务。例如:
      • AH=09H:显示字符串。
      • AH=0AH:输入字符串。
      • AH=4CH:程序退出。
      • …(其他)
字符串处理

字符串通常以 $ 作为结束符。DOS 中断 INT 21H 可以用于字符串的显示和输入。

  • MOV AH, 09H:显示字符串(要求字符串以 $ 结尾)。
  • MOV AH, 0AH:接收用户输入的字符串(以 $ 结尾,输入前两个字节存储长度信息)。
栈操作

栈用于函数调用和局部变量存储,栈操作通常包括:

  • PUSH:将数据压入栈。
  • POP:从栈中弹出数据。
循环和过程

  • 循环:使用 CMP 和条件跳转指令(如 JNZJE)来控制程序的循环。
  • 过程(Procedure):程序中的功能块,可以重复调用。
位操作

位操作指令在处理字符、二进制数据时非常有用。比如:

  • ROL DL, CL:将寄存器 DL 左移 CL 位(旋转),常用于处理十六进制字符。
  • ANDORXORNOT:这些都是位运算指令。
程序退出

INT 21H 中断 AH = 4CH 用于终止程序,AL = 0 表示正常退出。

INT 21H中断


集成环境软件支持的软中断.pdf

程序思路

由于字符编码问题,本人统一用英文写代码注释了


实验目标总结:


  1. 显示"Hello World!" 字符串。
  2. 从键盘输入姓名和学号,并将它们回显到屏幕上。
  3. 从键盘输入字符,输出对应的ASCII码,直到输入“Q”或“q”时退出程序。

数据段


  • 就是一些要打印出来的东西,放到数据段里
  • 代码:
data SEGMENT PARA 'DATA'
    helloworld  DB  'Hellow, world!', 0DH, 0AH, 24H ; String: "Hellow, world!" , carriage return(CR), newline, and '$'(end of string)
    nextline    DB  0DH, 0AH, 24H                   ; CR and newline string, ends with '$'
    buffer      DB  0100H dup('$')                  ; 256-byte(16*16) buffer initialized with '$', dup means duplicate, dup('$') means 256 copies of '$'
    nm          DB  'Please input your name: ', '$' ; Prompt to input name, ends with '$'
    id          DB  'Please input your id: ', '$'   ; Prompt to input ID, ends with '$'
    chr         DB  'Please input a char:', '$'     ; Prompt to input a character, ends with '$'
data ENDS

代码段


  • 代码:
code SEGMENT PARA 'CODE'
ASSUME CS: code, DS: data; CS and DS are the segments for code and data respectively

BEGIN:
    ;concrete code
  
code ENDS
END BEGIN

换行代码


  • 由于经常需要打印换行,所以弄成个过程,方便重复调用
  • 代码:
; Print a newline procedure
PrintNewLine PROC NEAR
    LEA     DX, nextline                            ; Load the address of newline characters into DX
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    RET                                             ; Return from the procedure
PrintNewLine ENDp

初始化,载入数据(数据段初始化)


  • 代码:
    ; Initialize data segment
    MOV     AX, data                                ; Load data segment address into AX
    MOV     DS, AX                                  ; Set DS to point to the data segment

显示 “Hello World!” 字符串


  • 思路:
    • 首先,我们要在屏幕上输出固定的字符串 Hello, world!。汇编语言通过调用 DOS 中断(INT 21H)来实现这一点。
    • 使用 DOS 中断 21h 的 09H 功能来显示一个以 $ 结束的字符串(这是 DOS 中断规定的字符串结束符)。我们将字符串 "Hellow, world!" 存储在数据段中,并通过 LEA 指令将它的地址加载到 DX 寄存器,然后调用 INT 21H 来显示它。
  • 代码:
    ; Display "Hellow, World!"
    LEA     DX, helloworld                          ; Load the address of the "Hellow, world!" string into DX
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     ; Call interrupt

    ; Print a newline using the procedure
    CALL    PrintNewLine
  • 结果(用的是TASM的官方GUI软件来演示,学校的星研集成环境其实编译器用的也是TASM):

从键盘输入姓名和学号,并回显


  • 思路:
    • 程序需要提示用户输入姓名和学号,然后回显用户输入的内容。
    • 我们通过调用 DOS 中断 21H 的功能 09H 来显示提示信息(比如“Please input your name: ”)。
    • 输入姓名和学号使用 DOS 中断 21H 的功能 0AH。该功能会将输入的字符串存储在 buffer 中,其中 buffer 的前两个字节分别存储最大输入长度和实际输入的长度。我们跳过前两个字节,直接输出用户输入的部分。
    • 需要注意的是,输入字符串后我们需要在屏幕上显示用户输入的内容,回显功能就是将 buffer 中的数据再次输出。
  • 代码:
    ; name
    LEA     DX, nm                                  ; Prompt for name
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    LEA     DX, buffer                              ; Get name input into buffer
    MOV     AH, 0AH                                 ; DOS interrupt to input a string 
    INT     21H                                     
    CALL    PrintNewLine
    LEA     DX, buffer+2                            ; Display name of input. Skip the first two bytes (length info) to display the input
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    CALL    PrintNewLine

    ; ID
    LEA     DX, id                                  ; Prompt for ID
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    LEA     DX, buffer                              ; Get ID input into buffer
    MOV     AH, 0AH                                 ; DOS interrupt to input a string 
    INT     21H                                     
    CALL    PrintNewLine
    LEA     DX, buffer+2                            ; Display ID of input. Skip the first two bytes (length info) to display the input
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    CALL    PrintNewLine
  • 结果:

输入字符并输出对应的 ASCII 码


  • 思路:
    • 老师给的算法流程图:
    • 注意!!!提取高/低位的时候,原始读入的数据一定要用另一个寄存器保存,不然会出错!我这里用**MOV BL, AL**来保存输入的**AL**中的数据。
    • 程序进入一个循环,不断从键盘读取字符,并输出该字符的 ASCII 码。
    • 每次输入字符后,我们会使用 MOV AH, 02H 来输出字符本身。
    • 然后,我们通过位移操作(将字符按 4 位分成两部分)将字符的 ASCII 码转换为十六进制并显示。
    • 通过 CMP AL, 'Q'CMP AL, 'q' 判断是否输入了 Qq,如果是则退出程序。
  • 代码:

; ASCII input loop
ascii_loop:
    LEA     DX, chr                                 ; Prompt for a character
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    MOV     AH, 01H                                 ; DOS interrupt to input a single character
    INT     21H                                     
    CMP     AL, 'Q'                                 ; Compare input character with 'Q'
    JE      exit_prog                               ; If input is 'Q', exit the program
    CMP     AL, 'q'                                 ; Compare input character with 'q'
    JE      exit_prog                               ; If input is 'q', exit the program
    CALL    PrintNewLine

    ; Display ASCII value in hexadecimal
    MOV     BL, AL                                  ; Save input character in BL
    SHR     AL, 4                                   ; Extract the high nibble (high 4 bits)
    CALL    ConvertToHex                            ; Convert and display high nibble
    MOV     AL, BL                                  ; Restore original character to AL
    AND     AL, 0FH                                 ; Mask out high nibble to get the low nibble
    CALL    ConvertToHex                            ; Convert and display low nibble
    CALL    PrintNewLine

    JMP     ascii_loop                              ; Loop back to prompt for another character

exit_prog:
    ; Exit program
    MOV     AH, 4CH                                 ; DOS interrupt to terminate the program
    MOV     AL, 0                                   ; Exit code 0 (normal exit)
    INT     21H                                     

; Convert a single nibble to hexadecimal and display it
ConvertToHex PROC NEAR
    CMP     AL, 0AH                                 ; Check if nibble is >= 10
    JL      HexNumber                               ; If less than 10, jump to HexNumber
    ADD     AL, 07H                                 ; If >= 10, add 7 to convert to 'A'-'F'
HexNumber:
    ADD     AL, 30H                                 ; Convert to ASCII ('0'-'9' or 'A'-'F')
    MOV     DL, AL                                  ; Move the ASCII character to DL
    MOV     AH, 02H                                 ; DOS interrupt to display a single character
    INT     21H
    RET
ConvertToHex ENDp
  • 结果(不知道为什么,在DOS中需要按两次大小写转换键才能转换,没去深入研究了,有兴趣的自行研究。):

程序流程:


  1. 程序启动后首先显示 “Hello World!”。
  2. 提示用户输入姓名,并回显输入的内容。
  3. 提示用户输入学号,并回显输入的内容。
  4. 进入字符输入循环,显示输入的字符和对应的 ASCII 码,直到用户输入 Qq 时退出程序。

这样就实现了实验要求的功能,涵盖了字符串显示、输入输出处理、字符转换及循环控制。

白嫖党最喜欢的部分——完整代码


还是希望大家能看完上面的解析后再cv代码,验收也方便。第一次试验刚好也能复习下汇编知识呀!

data SEGMENT PARA 'DATA'
    helloworld  DB  'Hellow, world!', 0DH, 0AH, 24H ; String: "Hellow, world!" , carriage return(CR), newline, and '$'(end of string)
    nextline    DB  0DH, 0AH, 24H                   ; CR and newline string, ends with '$'
    buffer      DB  0100H dup('$')                  ; 256-byte(16*16) buffer initialized with '$', dup means duplicate, dup('$') means 256 copies of '$'
    nm          DB  'Please input your name: ', '$' ; Prompt to input name, ends with '$'
    id          DB  'Please input your id: ', '$'   ; Prompt to input ID, ends with '$'
    chr         DB  'Please input a char:', '$'     ; Prompt to input a character, ends with '$'
data ENDS

code SEGMENT PARA 'CODE'
ASSUME CS: code, DS: data                           ; CS and DS are the segments for code and data respectively

BEGIN:
    ; Initialize data segment
    MOV     AX, data                                ; Load data segment address into AX
    MOV     DS, AX                                  ; Set DS to point to the data segment

    ; Display "Hellow, World!"
    LEA     DX, helloworld                          ; Load the address of the "Hellow, world!" string into DX
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     ; Call interrupt
    CALL    PrintNewLine

    ; name
    LEA     DX, nm                                  ; Prompt for name
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    LEA     DX, buffer                              ; Get name input into buffer
    MOV     AH, 0AH                                 ; DOS interrupt to input a string 
    INT     21H                                     
    CALL    PrintNewLine
    LEA     DX, buffer+2                            ; Display name of input. Skip the first two bytes (length info) to display the input
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    CALL    PrintNewLine

    ; ID
    LEA     DX, id                                  ; Prompt for ID
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    LEA     DX, buffer                              ; Get ID input into buffer
    MOV     AH, 0AH                                 ; DOS interrupt to input a string 
    INT     21H                                     
    CALL    PrintNewLine
    LEA     DX, buffer+2                            ; Display ID of input. Skip the first two bytes (length info) to display the input
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    CALL    PrintNewLine

; ASCII input loop
ascii_loop:
    LEA     DX, chr                                 ; Prompt for a character
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    MOV     AH, 01H                                 ; DOS interrupt to input a single character
    INT     21H                                     
    CMP     AL, 'Q'                                 ; Compare input character with 'Q'
    JE      exit_prog                               ; If input is 'Q', exit the program
    CMP     AL, 'q'                                 ; Compare input character with 'q'
    JE      exit_prog                               ; If input is 'q', exit the program
    CALL    PrintNewLine

    ; Display ASCII value in hexadecimal
    MOV     BL, AL                                  ; Save input character in BL
    SHR     AL, 4                                   ; Extract the high nibble (high 4 bits)
    CALL    ConvertToHex                            ; Convert and display high nibble
    MOV     AL, BL                                  ; Restore original character to AL
    AND     AL, 0FH                                 ; Mask out high nibble to get the low nibble
    CALL    ConvertToHex                            ; Convert and display low nibble
    CALL    PrintNewLine

    JMP     ascii_loop                              ; Loop back to prompt for another character

exit_prog:
    ; Exit program
    MOV     AH, 4CH                                 ; DOS interrupt to terminate the program
    MOV     AL, 0                                   ; Exit code 0 (normal exit)
    INT     21H                                     

; Convert a single nibble to hexadecimal and display it
ConvertToHex PROC NEAR
    CMP     AL, 0AH                                 ; Check if nibble is >= 10
    JL      HexNumber                               ; If less than 10, jump to HexNumber
    ADD     AL, 07H                                 ; If >= 10, add 7 to convert to 'A'-'F'
HexNumber:
    ADD     AL, 30H                                 ; Convert to ASCII ('0'-'9' or 'A'-'F')
    MOV     DL, AL                                  ; Move the ASCII character to DL
    MOV     AH, 02H                                 ; DOS interrupt to display a single character
    INT     21H
    RET
ConvertToHex ENDp

; Print a newline procedure
PrintNewLine PROC NEAR
    LEA     DX, nextline                            ; Load the address of newline characters into DX
    MOV     AH, 09H                                 ; DOS interrupt to display a string
    INT     21H                                     
    RET                                             ; Return from the procedure
PrintNewLine ENDp

code ENDS
END BEGIN
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值