参考文献
x86 Assembly/GAS Syntax – Wikibooks, open books for an open world[……]
「GAS」- GNU Assembler
「MASM」- 汇编语法(学习笔记)
该笔记记录 MASM 汇编器语法,但是不包含汇编指令。
需要学习的内容
ML and ML64 command-line option ML error messages
Directives reference Symbols Reference Operators Reference
MASM for x64 (ml64.exe) Microsoft Macro Assembler BNF Grammar
MASM Operators reference
x86 and x64 assembler/x86 and x64 assembler reference/Operators reference
OFFSET
是由汇编器处理的指令,功能是取得标号处的偏移地址。
参考文献
MASM – Microsoft Macro Assembler reference[……]
「NASM」- Netwide Assembler
相关链接
Website: https://www.nasm.us/ Manual / NASM – The Netwide Assembler version 2.15.02[……]
「NASM」- attempt to reserve non-constant quantity of BSS space
问题描述
在执行 nasm 命令时,产生如下错误:
# nasm ipl.nas
ipl.nas:52: error: attempt to reserve non-constant quantity of BSS space
问题原因
汇编语法错误,参考 NASM 手册。
$,当前位置;$$,当前段位置;$ – $$,当前位置距离段开始的偏移位置
参考文献
assembly – How does $ work in NASM, exactly? – Stack Overflow Ubuntu 18.04で30日OS自作入門をやりたい[1日目] – free(malloc(sizeof(MRM))); 「30天自制操作系统」Day1 – 从计算机结构到汇编程序入门 | Poeroz’s blog[……]
「Assembly」- 从 ATA 中,读取扇区数据
在个人计算机中,主硬盘控制器(ATA)被分配 8 个端口,0x01F0-0x01FF:
端口的作用及使用方法
0x01F0
作用:数据端口,用于从中读取数据。
端口长度:该端口为 16 bits
0x01F1
作用:错误寄存器,保存硬盘驱动器在最后一次执行命令后的状态(错误原因)。
0x01F2
作用:设置要读取的扇区数
该端口 8 bits,写入 0 表示读取 256 个扇区。
每成功读取一个扇区,该数值将减一。该端口的最终数值,为读取失败的扇区数。
0x01F3 / 0x01F4 / 0x01F5 / 0x01F6
作用:设置起始的 LBA28 扇区号
端口长度:每个端口为 8 bits
其中 0x01F3-0x01F5 保存 LBA 的第 00-23 位,而 LBA 的第 24-27 位保存在 0x01F6 的 00-03 位;
端口 0x01F6 的第 04 位,用于指定要读取主盘(0)还是从盘(1)。(旧的 PATA/IDE 接口,可以挂接两块硬盘;新的 SATA 接口,只能挂接一块硬盘)
端口 0x01F6 的高 05-07 位,为 111 表示使用 LBA 寻址,为 101 表示 CHS 寻址。
0x01F7
作用:既是命令端口,也是状态端口。
端口长度:该端口为 8 bits
写入 0x0020 表示读取请求,然后硬盘便开始工作,进行读取数据。
端口 0x01F7 的第 07 位为 1 表示磁盘正在工作,此时我们还不能读取数据(需要我们在程序中处理,即等待)。
端口 0x01F7 的第 07 位为 0 表示工作结束,并且第 03 位为 1 表示准备完成,此时我们可以读取数据。
端口 0x01F7 的第 00 位表示前个命令执行是否出现错误,错误原因保存在 0x00F1 端口中。
从磁盘中读取数据(汇编实现)
参考文献
ATA (Advanced Technology Attachment) Definition ATA read/write sectors – OSDev Wiki 《x86 汇编语言 从实模式到保护模式》[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 11.1
参考答案
sub al,al al=0h ZF=1 PF=1 SF=0
mov al,1 al=1h ZF=1 PF=1 SF=0
push ax ax=1h ZF=1 PF=1 SF=0
pop bx bx=1h ZF=1 PF=1 SF=0
add al,bl al=2h ZF=0 PF=0 SF=0
add al,10 al=12h ZF=0 PF=1 SF=0
mul al ax=144h ZF=0 PF=1 SF=1
指令 add、sub、mul、div 、inc、or、and 为运算指令,在执行后,影响标志寄存器
指令 mov、push、pop 为传送指令,在执行后,不会影响标志寄存器
关于 mul al 指令
经过实验,不管最开始在 AX 中存储的是什么,指令 mul al 的 Sign Flag 都为 NG 即负数。
参考文献
王爽《汇编语言(第三版)》检测点11.1 汇编语言检测点11.1详细解析[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 11.2
参考答案
AL CF OF SF ZF PF
sub al, al 0h 00000000b 0/NC 0/NV 0/PL 1/ZR 1/PE
mov al, 10h 10h 00100000b 0/NC 0/NV 0/PL 1/ZR 1/PE
add al, 90h a0h 10100000b 0/NC 0/NV 1/NG 0/NZ 1/PE
mov al, 80h 80h 10000000b 0/NC 0/NV 1/NG 0/NZ 1/PE
add al, 80h 0h 00000000b 1/CY 1/OV 0/PL 1/ZR 1/PE
mov al, 0fch 0fch 11111100b 1/CY 1/OV 0/PL 1/ZR 1/PE
add al, 05h 1h 00000001b 1/CY 0/NV 0/PL 0/NZ 0/PO
mov al, 7dh 7dh 11111101b 1/CY 0/NV 0/PL 0/NZ 0/PO
add al, 0bh 88h 10001000b 0/NC 1/OV 1/NG 0/NZ 1/PE
add al, 90h
如果当作无符号计算,结果没有发生进位,所以 CF = 0
如果当作有符号计算,结果为 -96 (=16-112),也没有发生溢出,所以 OF = 0
=> 当我们将计算视为无符号计算时,只需关注 CF 标志位; => 当我们将计算视为有符号计算时,只需关注 OF 标志位;
add al, 05h
如果当作无符号计算,结果为 101H,发生进位,所以 CF = 1
如果当作有符号计算,结果为 1 (=-4+5),没有发生溢出,所以 OV = 0
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 11.3
参考答案
1)补全下面的程序,统计F000:0处32个字节中,大小在[32,128]的数据个数。
mov ax,0f000h
mov ds,ax
mov bx,0 ;ds:bx指向第一个字节
mov dx,0 ;初始化累加器
mov cx,32
s: mov al,[bx]
cmp al,32 ;和32进行比较
jb s0 ;如果低于al转到s0,继续循环
cmp al,128 ;和128进行比较
ja s0 ;如果高于al转到s0,继续循环
inc dx
s0: inc bx
loop s
[32,128]是闭区间,包括两端点的值
2)补全下面的程序,统计F000:0处32个字节中,大小在(32,128)的数据个数。
mov ax,0f000h
mov ds,ax
mov bx,0 ;ds:bx指向第一个字节
mov dx,0 ;初始化累加器
mov cx,32
s: mov al,[bx]
cmp al,32
jna s0 ;如果不高于al转到s0,继续循环
cmp al,128
jnb s0 ;如果不低于al转到s0,继续循环
inc dx
s0: inc bx
loop s
(32,128)是开区间,不包括两端点的值
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 11.4
参考答案
(ax)=45
推算过程
在 popf 后,标志寄存器中,本章节介绍的那些标志位都为零(没学过的位置用 * 先代替) 那么 pushf 将计算后的当时状态的标志寄存器入栈,然后 pop 给 ax 寄存器 接下来进行 and 操作,结果如下:
mov ax,0
push ax
popf
mov ax, 0fff0h ; -16
add ax, 0010h ; +16
pushf
pop ax ; | 0 0 0 0 of df if tf | sf zf 0 af 0 pf 0 cf |
; | 0 0 0 0 0 0 * * | 0 1 0 * 0 1 0 1 |
; ax = 000000** 010*0101b
and al,11000101B ; al = 01000101b = 45h
and ah,00001000B ; ah = 00000000b = 0h
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 12.1
参考答案
1) 用 debug 查看内存,情况如下: 0000:0000 68 10 A7 00 8B 01 70 00-16 00 9D 03 8B 01 70 00 则 3 号中断源对应的中断处理程序入口的偏移地址的内存单位的地址为 ( 0070:018b )
题目解析: 壹个表项存放壹个中断向量,壹个中断向量包括段地址和偏移地址,所以占用两个字,高位保存段地址,低位保存偏移地址。 另外由于内存单元是字节单元,数据以字节为单位进行存储。
2) 存储 N 号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为: 4N 存储 N 号中断源对应的中断处理程序入口的段地址的内存单元的地址为: 4N+2
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 13.1
参考答案
1) 最大位移是 FFFF
2) 下面是测试程序,用于测试在 7ch 中的中断例程,由原教材提供:
assume cs:code
data segment
db ‘conversation’,0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
mov ax, 0b800h
mov es, ax
mov di, 12*160
s: cmp byte ptr [si],0
je ok
mov al, [si]
mov es:[di], al
inc si
add di, 2
mov bx, offset s – offset ok
int 7ch
ok: mov ax, 4c00h
int 21h
code ends
end start
下面是中断例程的程序及安装程序:
assume cs:code
code segment
jmp_near_ptr_s:
push si
mov si, sp
add ss:[si+2], bx
pop si
iret
start:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 安装中断程序到内存
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 复制的起始地址
mov ax, cs
mov ds, ax
mov si, offset jmp_near_ptr_s ; mov si, 0
; 复制的目的地址
mov ax, 0000H
mov es, ax
mov di, 0200H
; 复制长度
mov cx, offset start – offset jmp_near_ptr_s
; 复制方向
cld
; 开始复制
rep movsb
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 设置中断向量表
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, 0000H
mov ds, ax
mov word ptr ds:[7CH * 4], 0200H
mov word ptr ds:[7CH * 4 + 2], 0000H
mov ax, 4C00H
int 21H
code ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 13.2
参考答案
1) 错误,在 FFFF:0 处的内容无法改变,并且在我们的程序执行之前就已经存在。
2) 错误,先调用 int 19h 中断里程,然后启动 DOS 系统。
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 14.1
参考答案
1)编程,读取 CMOS RAM 的 2 号单元内容
assume cs:code
code segment
start:
mov al, 2 ; 只能使用 ax 或 al 来存放端口读入或写入的数据
out 70h, al ; 将 al 送入 70h 端口,以指定操作单元
in al, 71h ; 从端口 71h 读出单元内容
mov ax, 4c00h
int 21h
code ends
end start
2)编程,向 CMOS RAM 的 2 号单元写入 0
assume cs:code
code segment
start:
mov al, 2 ; 只能使用 ax 或 al 来存放端口读入或写入的数据
out 70h, al ; 将 al 送入 70h 端口,以指定操作单元
mov al, 0 ; 只能使用 ax 或 al 来存放端口读入或写入的数据
out 71h, al ; 从端口 71h 读出单元内容
mov ax, 4c00h
int 21h
code ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 14.2
参考答案
编程,用加法和移位指令计算(ax)=(ax)*10 提示:(ax)*10=(ax)*2+(ax)*8
assume cs:codeseg
codeseg segment
start:
mov bx, ax
shl bx, 1 ; 计算加号左边
mov cl, 3 ; 计算加号左边
shl ax, cl
add ax, bx ; 合并计算结果
mov ax, 4C00H
int 21H
codeseg ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 15.1
参考答案
1) 由于这段程序是在中断例程中执行的,因此在进入中断例程时,标志位 IF TF 已经设置为零,无需再次设置:
pushf
call dword ptr ds:[0]
2) 问题出现在 mov word ptr es:[9*4], offset int9 与 mov es:[9*4+2], cs 之间
当执行 mov word ptr es:[9*4], offset int9 以后,发生中断,
此时中断向量表的 9 号中断的偏移地址发生变化,但是段地址还没有修改,因此会去该地址执行未知指令。
核心问题就是 中断向量表的条目改了壹半。解决方法如下:
……
cif
mov word ptr es:[9*4]
offset int9 与 mov es:[9*4+2], cs
sif
……
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 16.1
参考答案
assume cs:code
code segment
a dw 1,2,3,4,5,6,7,8
b dd 0
start: mov si, 0
mov cx, 8
s: mov ax, a[si]
add word ptr b[0], ax
adc word ptr b[2], 0
add si, 2
loop s
mov ax,4c00h
int 21h
code ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽) 汇编语言(第三版)– 检测点16.1[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 16.2
参考答案
assume cs:code, es:data
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
start: mov ax, data
mov es, ax
mov si, 0
mov cx, 8
s: mov al, a[si]
mov ah, 0
add b, ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:检测点 17.1
参考答案
“在 int 16h 中断例程中,一定有设置 IF=1 的指令。”,这种说法是对的吗?
这种说法是正确的。因为 int 16h 中断例程会检测键盘缓冲区是否有数据,如果没有数据将循环检测。如果设置 IF = 0 则不处理可屏蔽中断,那么键盘按键就会被忽略,导致按键无法写入键盘缓冲区。当键盘缓冲区都没有数据,将永远都没有数据,进而进入死循环。
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽) 汇编语言(第三版)– 检测点16.1[……]
「汇编语言 第 3 版 王爽」- 参考答案:课程设计 1
第一步、调整 dtoc 程序
在实验 10 中编写 dtoc 程序能够显示数值,但是存在以下问题: 1)原有 dtoc 只能支持 16 位被除数,但是收入数据是 32 位,需要添加支持; 2)除法存在溢出问题,并且原有 dtoc 程序直接使用 DIV 指令,因此可能溢出;
首先调整 dtoc 程序,使其能处理除法溢出问题,这只需使用我们之前编写的 divdw 函数即可。为了清晰问题,我们去掉原有注释:
; 将数值转化为字符换(使用函数 divdw 处理溢出)
; @desc 参数:dx => 数据高位,ax => 数据低位,ds:si => 数据写入地址
; @desc 结果:保存到 ds:si 中,并以 0 结尾
dtoc:
push ax
push bx
push cx
push dx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 计算数值字符串(重点调整的部分 开始)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov bx, 0
push bx ; 无需理解此步骤,往下看就知道了
_do_some_stuff_start:
mov dx, dx ; 调用 divdw 函数,高位 dx
mov ax, ax ; 调用 divdw 函数,地位 ax
mov cx, 10 ; 调用 divdw 函数,除数 cx
call divdw ; 调用 divdw 函数
add cx, 30H ; 余数加 30H 得到对应的字符
push cx ; 计算结果与显示结果是相反的,所以先入栈,以后再弹出
mov cx, 0 ; 开始判断商是否为零,以决定是否进入下轮
or cx, ax
or cx, dx
jcxz _do_some_stuff_end ; 如果 cx 为零,则表示 ax dx 都为 零,即商为零
jmp short _do_some_stuff_start
_do_some_stuff_end: ; 至此,数字对应的字符串都在栈中。“栈底”为零,作为 pop 边界
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 计算数值字符串(重点调整的部分 结束)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov bx, 0
save_to_dateseg:
pop cx
jcxz _append_zero_to_sting
mov ds:[bx], cl
inc bx
jmp short save_to_dateseg
_append_zero_to_sting:
mov b[……]
「汇编语言 第 3 版 王爽」- 参考答案:课程设计 2
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 7 寻址方式在结构化数据访问中的应用
assume cs:codesg
datasg segment
; 年份 21×4=84, 0-83
db ‘1975’,’1976′,’1977′,’1978′,’1979′,’1980′,’1981′,’1982′,’1983′
db ‘1984’,’1985′,’1986′,’1987′,’1988′,’1989′,’1990′,’1991′,’1992′
db ‘1993’,’1994′,’1995′
; 收入 21×4=84, 84-167
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
; 人数 21×2=42, 168-20H9
dw 3,7,9,13,28,38,130,220H,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
datasg ends
table segment
db 21 dup(‘year summ ne ?? ‘)
table ends
codesg segment
start:
; 21 条数据,21 次循环
mov cx, 21
; 用做下标,获取数据
mov si, 0
mov di, 0
; 数据写入 table 段
mov ax, table
mov es, ax
; 每次循环都要取年份、收入、人数数据
mov ax, datasg
mov ds, ax ; 年份 => ds:[0],收入 => ds:[84],人数 => ds:[168]
loop_01:
; 写入年份
mov ax, ds:0[si]
mov es:[0], ax
mov ax, ds:0[si+2]
mov es:[2], ax
; 写入空格
mov al, 20H
mov es:[4], al
; 写入收入
; 与计算收入时,一起写入
; 写入空格
mov al, 20H
mov es:[9], al
; 写入人数
; 与计算收入时,一起写入
; 写入空格
mov al, 20H
mov es:[0CH], al
; 写入收入、人数、平均收入
mov ax, ds:84[si]
mov es:[5], ax
mov dx, ds:84[si+2]
mov es:[7], dx[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 10 编写子程序
1)显示字符串
assume cs:codeseg, ds:data
data segment
db ‘Welcome to masm!’, 0
data ends
codeseg segment
start:
mov dh, 8 ; 行
mov dl, 3 ; 列
mov cl, 2 ; 颜色
; 数据开始
mov ax, data
mov ds, ax
mov si, 0
call show_str
mov ax, 4c00h
int 21h
; 显示字符串
; @desc 参数:dh => 行,dl => 列,cl => 颜色,ds:si => 字符串地址,以要 0 结尾
show_str:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 保存寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
show_str_start:
push ax
push bx
push dx
push cx
push es
push si
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 开始实现功能
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 显存 B8000 – BFFFF,我们使用 B800 作为段地址
; 段空间位 64K,而显存只有 32K,所以不会超过段
; 第一步、设置段地址
mov ax, 0B800H
mov es, ax ; es => 写入字符的段地址
; 第二步、计算偏移量
; 每行的长度:0 – 9F | 每列的宽度:两字节,高位颜色,低位字符
; 写入字符的开始地址 = (行号 – 1) x 0A0H + 列号 x 2
sub dh, 1
mov al, dh
mov bl, 0A0H
mul bl ; ax
add dl, dl
mov dh, 0 ; dx
add dx, ax ; ax => 写入字符的开始地址
mov bx, dx
; 第三步、开始写入
mov ah, cl ; 颜色
copy_char:
mov cl, ds:[si] ; 判断字符是否为零
mov ch, 0
jcxz show_str_end
mov al, cl
mov es:[bx], ax ; 将字符写入显存
inc si
add bx, 2
jmp short copy_char
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 恢复寄存器
;;;;;;;;;;;;;;;;;[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 11 编写子程序
参考答案
assume cs:codeseg
dataseg segment
db “Beginner’s All-purpose Symbolic Instruction Code.”,0
dataseg ends
codeseg segment
start:
mov ax, dataseg
mov ds, ax
mov si, 0
call letterc
mov ax, 4c00H
int 21H
letterc:
push cx
push si
_letterc_start:
mov cl, ds:[si] ; 读取字母
mov ch, 0 ; 判断当前字符是否为零
jcxz _letterc_end ; 如果为零,直接结束
cmp cl, 97 ; 判断是否为小写 [97, 122]
jb _letterc_next_char ; 不为小写则跳走
cmp cl, 122
ja _letterc_next_char
sub cl, 20H ; 将字母转化为大写
mov ds:[si], cl
_letterc_next_char:
inc si
jmp short _letterc_start
_letterc_end:
pop si
pop cx
ret
codeseg ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 12 编写 0 号中断的处理程序
第一步、准备溢出程序
我们要准备溢出程序,用于后面的测试:
assume cs:codeseg
codeseg segment
_start:
mov ax, 1000H
mov bl, 1
div bl
mov ax, 4c00H
int 21H
codeseg ends
end _start
第二步、编写 0 号中断的处理程序
assume cs:codeseg
codeseg segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 编写程序
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
do0:
jmp short _do0_start
db “divide error!” ; 13
_do0_start:
mov ax, cs ; 设置数据地址
mov ds, ax
mov si, 0202H
mov ax, 0B800H ; 设置显存地址
mov es, ax
mov di, 12*160 + 33*2
mov cx, 13 ; 将字符串写入显存
s: mov al, ds:[si]
mov ah, 2
mov es:[di], ax
inc si
add di, 2
loop s
mov ax, 4c00H ; 结束程序,不需要使用 iret 返回
int 21H
_do0_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 复制程序(安装)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:
mov ax, codeseg ; 原始地址
mov ds, ax
mov si, offset do0
mov ax, 0 ; 目的地址
mov es, ax
mov di, 200H
mov cx, offset _do0_end – offset do0 ; 复制长度
cld ; 复制方向
rep movsb ; 执行复制
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 设置中断向量表
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 中断程序,段地址:0000H,偏移地址:0200H
; 中断程序的 段地址 写入 => 0000:6-7
; 中断程序的 偏移地址 写入 => 0000:4-5
mov ax, 0
mov es, ax
mo[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 13 编写、应用中断例程
显示壹个用零结束的字符串
调用中断例程的程序
assume cs:code
data segment
db ‘Welcome to masm!’, 0
data ends
code segment
start: mov dh, 10
mov dl, 10
mov cl, 2
mov ax, data
mov ds, ax
mov si, 0
int 7ch
mov ax, 4c00h
int 21h
code ends
end start
中断例程及安装程序
assume cs:code
code segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 编写中断例程
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
show_str:
push ax
push es
push dx
push di
push cx
push si
; 显存的写入地址
mov ax, 0B800H
mov es, ax
; mov di, (dh – 1) x 80 x 2 + (dl – 1) x 2
sub dh, 1
mov al, 160
mul dh ; ax
sub dl, 1
add dl, dl
mov dh, 0
add ax, dx
sub ax, 2
mov di, ax ; di = ax
; 向显存写入字符串
mov ch, cl ; 颜色
_loop_s0_start:
mov cl, ds:[si] ; 字符
cmp cl, 0
je _loop_s0_end ; 为零则结束
mov es:[di], cx ; 写入显存
inc si
add di, 2
jmp short _loop_s0_start
_loop_s0_end:
pop si
pop cx
pop di
pop dx
pop es
pop ax
iret
show_str_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 复制中断例程
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:
; 起始地址
mov ax, cs
mov ds, ax
mov si, offset show_str ; 0
; 目的地址
mov ax, 0
mov es, ax
mov di, 0200H
; 复制长度
mov cx, offset show_str_end – offset show_str
; 复制[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 14 访问 CMOS RAM
参考答案
assume cs:codeseg
codeseg segment
separator:
db ‘/’, ‘/’, ‘ ‘, ‘:’, ‘:’
date_index:
db 9, 8, 7, 4, 2, 0 ; 这几个位置没啥规律,耽误我写循环
start:
mov ax, cs
mov ds, ax
mov si, offset date_index
mov bp, offset separator
mov ax, 0B800H
mov es, ax
mov di, 0
mov cx, 6
loop_s0_start:
; 提取日期的高位与低位
push cx
mov al, ds:[si]
out 70H, al
in al, 71H
mov ah, al
mov cl, 4
shr ah, cl ; 高位
add ah, 30H
and al, 00001111B ; 地位
add al, 30H
pop cx
; 显示日期
mov dh, 2 ; 用绿色,辨识度高
mov dl, ah ; 显示十位
mov es:[di], dx
mov dl, al ; 显示各位
mov es:[di+2], dx
; 显示分割符号
cmp cx, 1 ; 最后循环没有分割符号显示,直接完成即可
je loop_s0_end
mov dl, ds:[bp]
mov es:[di+4], dx
add si, 1
add bp, 1
add di, 6
loop loop_s0_start
loop_s0_end:
mov ax, 4C00H
int 21H
codeseg ends
end start
参考文献
CSDN/汇编语言王爽第三版答案 百度文库/汇编语言实验答案 (王爽)[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 15 安装新的 int 9 中断例程
参考答案
实验内容
安装一个新的 int 9 中断例程,功能:在 DOS 下,按下”A”键后,除非不再松开,如果松开,就显示满屏幕的”A”,其他的键照常处理。
提示:按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。断码=通码+80h
实验分析
使用老套路: 1)旧中断例程地址复制到别处,设置新中断例程地址。 2)新中断例程调用旧中断例程,另外如果是 A 的断码,就写入显存全变 A
汇编实现
assume cs:codeseg
codeseg segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 新的中断程序
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
print_aaa:
push ax
push ds
push si
push cx
; 执行原有中断例程,其他的键照常处理
pushf
call dword ptr cs:[0200H]
in al, 60H ; 读取键盘扫描码
cmp al, 80H + 1EH ; 判断是否为 A 的断码
jne print_aaa_iret ; 如果不是 A 的断码,则返回
; 向显存写入 A,我们只写入第一页
mov ax, 0B800H
mov ds, ax
mov si, 0
mov cx, 80 * 25 ; 只写入第一页,= 80 * 2 * 25 / 2
loop_s0:
mov byte ptr ds:[si], ‘A’
add si, 2
loop loop_s0
print_aaa_iret:
pop cx
pop si
pop ds
pop ax
iret
print_aaa_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 复制中断程序
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:
mov ax, cs ; 设置起始地址
mov ds, ax
mov si, offset print_aaa
mov ax, 0000H ; 设置目的地址
mov es, ax
mov di, 0204H
mov cx, offset print_aaa_end – offset print_aaa
cld
rep movsb
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 设置中断向量表
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, 0
mov es[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 16 编写包含多个功能子程序的中断例程
参考答案
第一步、编写多功能的中断例程
assume cs:codeseg
codeseg segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 新的中断程序
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
setscreen:
jmp short select_function
table dw offset clear_screen + 0200H, offset set_fg_color + 0200H, offset set_bg_color + 0200H, offset line_up + 0200H
; 实现子程序调用,用于跳转到不同的子程序
select_function:
push bx
cmp ah, 3 ; 检查子程序序号是否超过范围
ja _select_function_iret
mov bh, 0
mov bl, ah
add bx, bx
call word ptr table[bx + 0200H]
_select_function_iret:
pop bx
iret
select_function_end:
; 实现清屏功能
clear_screen:
push ax
push ds
push si
push cx
mov ax, 0B800h ; 设置显存地址
mov ds, ax
mov si, 0
mov cx, 80 * 25 ; 总计写入次数 = 80 * 2 * 25 / 2
_loop_s0:
mov byte ptr ds:[si], ‘ ‘
add si, 2
loop _loop_s0
pop cx
pop si
pop ds
pop ax
ret
clear_screen_end:
; 设置前景色
set_fg_color:
push bx
push ds
push si
push cx
mov bx, 0B800H
mov ds, bx
mov si, 1
mov cx, 80 * 2 * 25 / 2
loop_s1:
and byte ptr ds:[si], 11111000b ; 抹掉原来前景
or ds:[si], al ; 设置前景
add si, 2
loop loop_s1
pop cx
pop si
pop ds
pop bx
ret
set_fg_color_end:
; 设置背景色
set_bg_color:
push b[……]
「汇编语言 第 3 版 王爽」- 参考答案:实验 17 编写包含多个功能子程序的中断例程
参考答案
第一步、编写中断例程
assume cs:codeseg
codeseg segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 编写中断例程
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 参数:ah 功能号;dx 逻辑扇区号;es:bx 存放数据的内存区;al 扇区数;cl 驱动器号;
int_13h:
push bx
push cx
push dx
mov si, dx ; 寄存器不够用,先让出来
;;;;;;; 计算 ah => 功能号
cmp ah, 2 ; 参数检查
ja int_13h_return
add ah, 2 ; 将我们的功能号直接映射到 int 13h 的功能号
;;;;;;; 计算 al => 扇区数
; 原题没有要求写入的扇区数,那么我们假设与 int 7Ch 保持一直,即使用 al
; 无需设置
;;;;;;; 计算 dh => 磁头号,面号
push ax
push bx
mov ax, si
mov dx, 0000h
mov bx, 1140
div bx
mov dh, al ; 取商
pop bx
pop ax
;;;;;;; 计算 dl => 驱动器号
; 原题也没有说如何指定驱动器号,因此我们假设使用 bl
mov dl, cl
;;;;;;; 计算 ch => 磁道号
push ax
push dx
mov ax, si
mov dx, 0
mov cx, 1440
div cx ; 余数用于下一步计算
mov ax, dx
mov cl, 18
div cl
mov ch, al ; 磁道号保存在扇中,因此传送到 ch 中
pop dx
pop ax
;;;;;;; 计算 cl => 扇区号 = rem( 逻辑扇区号 / 18 ) + 1
push bx
push ax
mov ax, si
mov bl, 18
div bl ; 余数用于下一步计算
inc ah
mov cl, ah
pop ax
pop bx
;;;;;;; 计算 es:bx => 存放数据的内存区
; 无需设置
;;;;;;; 调用中断
int 13h
;;;;;;; 返回
int_13h_return:
pop dx
pop cx
pop bx
iret
int_13h_end:
start:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 复制中断例程
;;;;;;;;;[……]