1870 字
9 分钟
从零开始的汇编语言笔记(3)

这里应该就是一些比较大的代码记录,其实就是课本题品鉴一下自己写一下。

[6.1] 编写一个子程序嵌套结构的程序模块,分别从键盘输入姓名及8个字符的电话号码,并以一定的格式显示出来。

主程序TELIST

· 显示提示符INPUT NAME:

· 调用子程序INPUT_NAME输入姓名;

· 显示提示符INPUT A TELEPHONE NUMBER:

· 调用子程序INPHONE输入电话号码;

· 调用子程序PRINTLINE显示姓名及电话号码。

子程序INPUT_NAME

· 调用键盘输入子程序GETCHAR,把输入的姓名存放在INBUF缓冲区中;

· 把INBUF中的姓名移入输出行OUTNAME

子程序INPHONE

· 调用键盘输入子程序GETCHAR,把输入的8位电话号码存放在INBUF缓冲区中;

· 把INBUF中的号码移入输出行OUTPHONE

子程序PRINTLINE

显示姓名及电话号码,格式为:

NAME TEL.
X X X XXXXXXXX

这是课本答案的代码:

;******************************************
; 数据段定义
DSEG SEGMENT
INBUF DB 12 DUP (' ') ; 输入缓冲区,初始化为空格,长度12个字节
OUTNAME DB 16 DUP (' ') ; 姓名输出行,初始化为空格,长度16个字节
OUTPHONE DB 12 DUP (' '), 0DH, 0AH, '$' ; 电话号码输出行,初始化为空格,后跟回车换行符和字符串结束符
MESG1 DB 'INPUT NAME:', '$' ; 提示输入姓名的字符串
MESG2 DB 'INPUT A TELEPHONE NUMBER:', '$' ; 提示输入电话号码的字符串
MESG3 DB 'NAME', 12 DUP (' '), 'TEL.', 0DH, 0AH, '$' ; 显示标题行的字符串
CRLF DB 0DH, 0AH, '$' ; 回车换行符字符串
DSEG ENDS ; 数据段结束
;******************************************
;******************************************
; 堆栈段定义
STACK SEGMENT
DW 100 DUP (?) ; 堆栈空间,100个字(200字节),未初始化
TOS LABEL WORD ; 堆栈顶(Top of Stack)的标签
STACK ENDS ; 堆栈段结束
;******************************************
;******************************************
; 代码段定义
CSEG SEGMENT
; 主程序TELIST,远过程(FAR PROC)
TELIST PROC FAR
ASSUME CS: CSEG, DS: DSEG, ES: DSEG, SS: STACK ; 假设段寄存器的关联关系
; 初始化堆栈段寄存器SS和堆栈指针SP
START: MOV AX, STACK ; 将堆栈段的段地址加载到AX
MOV SS, AX ; 将AX的值(STACK段地址)赋给SS寄存器
MOV SP, OFFSET TOS ; 将堆栈顶的偏移地址赋给SP寄存器
; 保存原有的数据段寄存器DS
PUSH DS ; 将DS压入堆栈,保存原始DS
SUB AX, AX ; 将AX清零
PUSH AX ; 将AX(现在为0)压入堆栈,可能用于设置段寄存器
MOV AX, DSEG ; 将数据段地址加载到AX
MOV DS, AX ; 设置DS寄存器为DSEG
MOV ES, AX ; 设置ES寄存器为DSEG
; 显示提示符“INPUT NAME:”
BEGIN: LEA DX, MESG1 ; 将MESG1的地址加载到DX
MOV AH, 09H ; 设置AH为09H,DOS显示字符串功能
INT 21H ; 调用DOS中断,显示字符串
CALL INPUT_NAME ; 调用子程序INPUT_NAME,输入姓名
; 显示提示符“INPUT A TELEPHONE NUMBER:”
LEA DX, MESG2 ; 将MESG2的地址加载到DX
MOV AH, 09H ; 设置AH为09H,DOS显示字符串功能
INT 21H ; 调用DOS中断,显示字符串
CALL INPHONE ; 调用子程序INPHONE,输入电话号码
CALL PRINTLINE ; 调用子程序PRINTLINE,显示姓名及电话号码
RET ; 远返回,结束主程序
TELIST ENDP ; 主程序结束
;--------------------------------------------------------------------------
; 子程序INPUT_NAME,近过程(NEAR PROC)
INPUT_NAME PROC NEAR
CALL GETCHAR ; 调用子程序GETCHAR,输入姓名
LEA SI, INBUF ; 将INBUF的地址加载到SI(源索引寄存器)
LEA DI, OUTNAME ; 将OUTNAME的地址加载到DI(目的索引寄存器)
MOV CX, 12 ; 设置循环计数器CX为12
CLD ; 清除方向标志,字符串操作向前移动
REP MOVSB ; 重复移动字节,将INBUF的数据复制到OUTNAME
RET ; 返回
INPUT_NAME ENDP ; INPUT_NAME子程序结束
;--------------------------------------------------------------------------
; 子程序INPHONE,近过程(NEAR PROC)
INPHONE PROC NEAR
CALL GETCHAR ; 调用子程序GETCHAR,输入电话号码
LEA SI, INBUF ; 将INBUF的地址加载到SI(源索引寄存器)
LEA DI, OUTPHONE ; 将OUTPHONE的地址加载到DI(目的索引寄存器)
MOV CX, 12 ; 设置循环计数器CX为12
CLD ; 清除方向标志,字符串操作向前移动
REP MOVSB ; 重复移动字节,将INBUF的数据复制到OUTPHONE
RET ; 返回
INPHONE ENDP ; INPHONE子程序结束
;--------------------------------------------------------------------------
; 子程序GETCHAR,近过程(NEAR PROC)
GETCHAR PROC NEAR
MOV AL, 20H ; 将空格字符(20H)加载到AL
MOV CX, 12 ; 设置循环计数器CX为12
LEA DI, INBUF ; 将INBUF的地址加载到DI
CLD ; 清除方向标志,字符串操作向前移动
REP STOSB ; 重复存储字节,将20H填充到INBUF
MOV CX, 12 ; 设置循环计数器CX为12,用于输入字符
MOV DI, 0 ; 将DI清零,用于索引INBUF
INPUT: MOV AH, 1 ; 设置AH为1,DOS读取字符功能
INT 21H ; 调用DOS中断,读取一个字符
CMP AL, 0DH ; 比较输入字符是否为回车符(0DH)
JZ QUIT ; 如果是回车符,跳转到QUIT
MOV [INBUF + DI], AL ; 将输入的字符存入INBUF[DI]
INC DI ; 增加DI,指向下一个位置
LOOP INPUT ; 循环,直到CX为0
QUIT: CALL DISP_CRLF ; 调用子程序DISP_CRLF,显示回车换行
RET ; 返回
GETCHAR ENDP ; GETCHAR子程序结束
;--------------------------------------------------------------------------
; 子程序PRINTLINE,近过程(NEAR PROC)
PRINTLINE PROC NEAR
LEA DX, MESG3 ; 将MESG3的地址加载到DX
MOV AH, 09H ; 设置AH为09H,DOS显示字符串功能
INT 21H ; 调用DOS中断,显示字符串
LEA DX, OUTNAME ; 将OUTNAME的地址加载到DX
MOV AH, 09H ; 设置AH为09H,DOS显示字符串功能
INT 21H ; 调用DOS中断,显示姓名
LEA DX, OUTPHONE ; 将OUTPHONE的地址加载到DX
MOV AH, 09H ; 设置AH为09H,DOS显示字符串功能
INT 21H ; 调用DOS中断,显示电话号码
RET ; 返回
PRINTLINE ENDP ; PRINTLINE子程序结束
;--------------------------------------------------------------------------
; 子程序DISP_CRLF,近过程(NEAR PROC)
DISP_CRLF PROC NEAR
LEA DX, CRLF ; 将CRLF的地址加载到DX
MOV AH, 09H ; 设置AH为09H,DOS显示字符串功能
INT 21H ; 调用DOS中断,显示回车换行
RET ; 返回
DISP_CRLF ENDP ; DISP_CRLF子程序结束
;--------------------------------------------------------------------------
CSEG ENDS ; 代码段结束
;******************************************
END START ; 程序入口点

下面是我的代码:

dataseg segment
prompt_name db 'INPUT NAME: ', '$'
prompt_phone db 'INPUT A TELEPHONE NUMBER: ', '$'
INBUF db 20
INBUF_LEN db ?
INBUF_DATA db 20 dup(?)
OUTNAME db 20 dup(?)
OUTPHONE db 20 dup(?)
prompt_title db 'NAME', 8 DUP (' '), 'TEL.', 0DH, 0AH, '$'
dataseg ends
stackseg segment
dw 100 dup(?)
TOS label word
stackseg ends
codeseg segment
assume cs: codeseg, ds: dataseg, ss: stackseg
TELIST proc far
start:
mov ax, stackseg
mov ss, ax
mov sp, offset TOS
push ds
xor ax, ax
push ax
mov ax, dataseg
mov ds, ax
mov es, ax
call INPUT_NAME
call INPHONE
call PRINTLINE
pop ax
pop ds
mov ax, 4C00h
int 21h
TELIST endp
INPUT_NAME proc near
mov ah, 09h
lea dx, prompt_name
int 21h
call GETCHAR
mov cx, 0
lea si, INBUF_DATA
lea di, OUTNAME
mov cl, INBUF_LEN
cld
rep movsb
mov cl, 12
sub cl, INBUF_LEN
mov al, ' '
rep stosb
mov byte ptr [di], '$'
ret
INPUT_NAME endp
INPHONE proc near
mov ah, 09h
lea dx, prompt_phone
int 21h
call GETCHAR
lea si, INBUF_DATA
lea di, OUTPHONE
mov cl, INBUF_LEN
cld
rep movsb
mov cl, 12
sub cl, INBUF_LEN
mov al, ' '
rep stosb
mov byte ptr [di], '$'
ret
INPHONE endp
GETCHAR proc near
mov ah, 0Ah
lea dx, INBUF
int 21h
call newline
ret
GETCHAR endp
newline proc near
push ax
push dx
mov ah, 02h
mov dl, 0Dh
int 21h
mov dl, 0Ah
int 21h
pop dx
pop ax
ret
newline endp
PRINTLINE proc near
mov ah, 09h
lea dx, prompt_title
int 21h
mov ah, 09h
lea dx, OUTNAME
int 21h
mov ah, 09h
lea dx, OUTPHONE
int 21h
ret
PRINTLINE endp
codeseg ends
end start

image-20241202062433154

从零开始的汇编语言笔记(3)
https://fuwari.vercel.app/posts/course/assembly/assembly-note-3/
作者
wegret
发布于
2025-01-02
许可协议
CC BY-NC-SA 4.0