汇编:史上最全注释,王爽汇编语言,课程设计2源码
Posted 小杰666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了汇编:史上最全注释,王爽汇编语言,课程设计2源码相关的知识,希望对你有一定的参考价值。
课程设计2
题目见 汇编语言 第四版 作者王爽 p312
概述
1、vm虚拟机中安装win98se,以下称为win98
。
2、成功安装系统后,虚拟机添加一个新软盘(需要创建软盘映像.img文件)。
3、启动win98系统,将下文源码编译,然后执行程序(需要masm汇编器)。
4、关闭win98系统,在虚拟机中,设置软盘
为启动盘。
5、启动虚拟机,我们自己写的程序就能执行了。
如果用两个硬盘,即添加新硬盘代替软盘,然后设新硬盘为启动盘后,启动失败(暂不管此问题)!
程序结构
软盘:第1扇区存放我们的引导程序,第2-3扇区存放主程序,实现4个功能。
硬盘1:是win98系统盘,第1扇区有mbr引导程序。
开机后,软盘为启动盘,加载我们自己的引导程序,执行引导程序,然后加载2-3扇区的主程序。
内存结构
汇编源码
源码:
; 课程设计2
; 实现了4个功能的程序以下称为主程序
assume cs:code
code segment
start:
; 安装我们自己的引导程序到软盘a,占1个扇区
call inst_my_boot
; 安装主程序到软盘a,占2个扇区
call inst_my_main
mov ax,4c00h
int 21h
;---------------- 安装程序 ----------------
inst_my_boot:
mov bx,cs
mov es,bx
mov bx,offset my_boot ; es:bx指向my_boot
; 写入内容到 软盘A,0面,0道,1扇区
mov dl,0 ; 软盘A
mov dh,0 ; 0面
mov ch,0 ; 0道
mov cl,1 ; 第1扇区
mov al,1 ; 写1个扇区
mov ah,3 ; 写
int 13h
ret
inst_my_main:
mov bx,cs
mov es,bx
mov bx,offset my_main ; es:bx指向my_main
; 写入内容到 软盘A,0面,0道,2扇区
mov dl,0 ; 软盘A
mov dh,0 ; 0面
mov ch,0 ; 0道
mov cl,2 ; 第2扇区
mov al,2 ; 写2个扇区
mov ah,3 ; 写
int 13h
ret
;---------------- 引导程序 ----------------
my_boot:
; 设栈
cli
mov ax,0
mov ss,ax
mov sp,7c00h
sti
; 装载新int9h中断例程
call load_newint9
; 将主程序拷贝到7e00h
mov bx,0
mov es,bx
mov bx,7e00h
; 读取 软盘A,0面,0道,2扇区 开始的2个扇区 到0:7e00h
mov dl,0 ; 软盘A
mov dh,0 ; 0面
mov ch,0 ; 0道
mov cl,2 ; 第2扇区
mov al,2 ; 复制2个扇区
mov ah,2 ; 读取
int 13h
mov bx,0
push bx
mov bx,7e00h
push bx
retf ; 从栈中取2字 设CS:IP=0:7e00h 并从此处开始执行
; 装载新int9h中断例程
load_newint9:
push ds
push si
push es
push di
push cx
push cs ; 引导程序执行时,cs:ip=0:7c00h
pop ds
mov cx,0
mov es,cx
mov si,newint9-my_boot+7c00h ; ds:si指向新int9例程
mov di,204h ; es:di指向新int9例程装载位置
mov cx,newint9end-newint9
cld
rep movsb
; 保存旧的int9h中断向量
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
pop cx
pop di
pop es
pop si
pop ds
ret
newint9:
push ax
push bx
push cx
push es
in al,60h
pushf
call dword ptr cs:[200h] ; 此newint9中断放在0:204h处,此中断执行时cs=0
cmp al,3bh ; f1扫描码
je chgcolor
cmp al,01h ; esc键扫描码
je int9ret
jmp newint9ret
backmain:
pop es
pop cx
pop bx
pop ax
add sp,4 ; 跳过cs:ip
popf
mov bx,0
push bx
mov bx,7e00h
push bx
retf
int9ret:
; 恢复原int9中断向量,原中断向量保存在0:200h, 0:202h
mov ax,0
mov es,ax
cli
push es:[200h]
pop es:[9*4]
push es:[202h]
pop es:[9*4+2]
sti
jmp backmain
chgcolor:
mov ax,0b800h
mov es,ax
mov bx,1
mov cx,17
chgcolor_s1:
inc byte ptr es:[bx]
add bx,2
loop chgcolor_s1
newint9ret:
pop es
pop cx
pop bx
pop ax
iret
newint9end:
nop
; 因为引导程序占512字节,这里填充512字节0,作为扇区结束,
; 防止引导程序不够512字节,而复制下面的代码到1扇区内
db 512 dup (0)
;--------------------- 主程序 ---------------------
my_main:
jmp main_start
menu1 db '1) reset pc',0
menu2 db '2) start system',0
menu3 db '3) clock',0
menu4 db '4) set clock',0
menu_addr dw menu1-my_main+7e00h, menu2-my_main+7e00h, menu3-my_main+7e00h, menu4-my_main+7e00h
timestr db 'yy/mm/dd hh:mm:ss',0
timeaddr db 9,8,7,4,2,0
strbuffer db 100 dup (0) ; 输入字符串缓冲区
main_start:
; 初始化数据段寄存器
mov ax,0
mov ds,ax
call clr_src
call show_menu
call choose_item
choose_item: ; 选择菜单项
mov si,strbuffer-my_main+7e00h
call getstr ; 输入字符串,回车确认,ds:si指向字符串缓冲区
cmp byte ptr [si],'1'
je item1
cmp byte ptr [si],'2'
je item2
cmp byte ptr [si],'3'
je item3
cmp byte ptr [si],'4'
je item4
jmp main_start ; 其他输入时,重新显示菜单
item1:
mov bx,0ffffh
push bx
mov bx,0
push bx
retf ; 将从ffff:0处开始执行,会重启系统
item2: ; 引导现有系统
; 将原mbr拷贝到7c00h
mov bx,0
mov es,bx
mov bx,7c00h
; 读取 硬盘c,0面,0道,1扇区 到0:7c00h
mov dl,80h ; 盘c
mov dh,0 ; 0面
mov ch,0 ; 0道
mov cl,1 ; 第1扇区
mov al,1 ; 复制1个扇区
mov ah,2 ; 读取
int 13h
mov bx,0
push bx
mov bx,7c00h
push bx
retf
item3:
call setnewint9
call show_dt
jmp main_start
item4:
call clr_src
mov si,strbuffer-my_main+7e00h
call getstr
call set_dt ; ds:si指向字符串缓冲区
jmp main_start
show_menu: ; 显示主程序菜单
push bx
push es
push si
push cx
push di
mov bx,0b800h
mov es,bx
mov bx,160*10+32*2 ; 中间位置显示菜单,第10行第32列
mov di,menu_addr-my_main+7e00h ; 直接定址表
mov cx,4
show_menu_s1:
mov si,[di] ; ds:di定位直接定址表
call show_str
add di,2
add bx,160 ; 跳到下一行
loop show_menu_s1
pop di
pop cx
pop si
pop es
pop bx
ret
clr_src: ; 清屏
push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0 ; 显存的偶地址单元为字符
mov cx,2000
clr_src_s1:
mov byte ptr es:[bx],' ' ; 空格填充
add bx,2
loop clr_src_s1
pop es
pop cx
pop bx
ret
; 显示字符串,字符串以0结束
; ds:si指向字符串开头
; es:bx指向显存开始,对应屏幕上的位置
show_str:
push si
push cx
push es
push bx
show_str_s1:
mov cl,[si]
cmp cl,0
je show_str_ret
mov es:[bx],cl
add bx,2
inc si
jmp show_str_s1
show_str_ret:
pop bx
pop es
pop cx
pop si
ret
; 接受字符串输入
; ds:si指向字符栈空间
getstr:
push ax
push dx
push es
push di
mov ax,0b800h
mov es,ax
mov di,160*14+32*2 ; es:di为光标初始位置、字符串输入位置
;mov di,0
call setcur
mov dx,0 ; 字符栈栈指针
getstrs:
mov ah,0
int 16h ; 获取键盘缓冲区内容
cmp al,20h
jb nochar
cmp al,7eh
ja nochar ; 只能输入可见字符
mov ah,0
call charstack ; 字符入栈
mov ah,2
call charstack ; 显示栈中字符
jmp getstrs
nochar:
cmp ah,0eh ; 退格键扫描码
je backspace
cmp ah,1ch ; 回车键扫描码
je enter
jmp getstrs
backspace:
mov ah,1
call charstack ; 字符出栈
mov ah,2
call charstack
jmp getstrs
enter:
mov al,0 ; 把0入栈,作为字符串结束
mov ah,0
call charstack
mov ah,2
call charstack
pop di
pop es
pop dx
pop ax
ret
; 字符入栈、出栈、显示功能
; ah=功能号,0入栈,1出栈,2显示
; ds:si指向字符栈空间
; dx=字符栈栈指针
; 0号功能,al=入栈字符
; 1号功能,al=返回的字符
; 2号功能,es:di指向屏幕位置
charstack: jmp short charstart
table dw charpush-my_main+7e00h,charpop-my_main+7e00h,charshow-my_main+7e00h
charstart:
push bx
push di
push es
;dx为charstack非局部变量,不要入栈
;push dx
cmp ah,2 ; 功能号判断
ja sret
mov bl,ah
mov bh,0
add bx,bx ; 根据功能号取得对应的偏移地址
jmp word ptr table-my_main+7e00h[bx]
charpush:
mov bx,dx
mov [si][bx],al
inc dx
jmp sret
charpop:
cmp dx,0
je sret
dec dx
mov bx,dx
mov al,[si][bx]
jmp sret
charshow:
; es:di指向屏幕位置,由调用者传递
mov bx,0
charshows:
cmp bx,dx
jne noempey
mov byte ptr es:[di],' '
call setcur
jmp sret
noempey:
mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2],' ' ; 清空后一个字符
inc bx
add di,2
jmp charshows
sret:
pop es
pop di
pop bx
ret
setcur: ; 设置光标到es:di位置
push ax
push dx
mov ax,di
mov dh,160
div dh
mov dh,al ; 行号
mov al,ah
mov ah,0
mov dl,2
div dl
mov dl,al ; 列号
mov ah,2 ; 设置光标位置
mov bh,0 ; 第0页
int 10h
pop dx
pop ax
ret
; 显示时间
show_dt:
call get_dt
call delay
jmp show_dt
ret
; 设置新的int9h中断向量
setnewint9:
push es
push bx
mov bx,0
mov es,bx
cli
mov word ptr es:[9*4],204h
mov word ptr es:[9*4+2],0
sti
pop bx
pop es
ret
; 获取CMOS中的系统时间
; ds:si从cmos对应地址取日期时间,ds:di转换后的日期时间字符串
get_dt:
push si
push di
push cx
push ax
push es
push bx
mov si,timeaddr-my_main+7e00h
mov di,timestr-my_main+7e00h
mov cx,6
get_dt_s1:
mov bx,cx
mov al,[si]
out 70h,al ; 70h为地址端口
in al,71h ; 71h为数据端口
mov ah,al
mov cl,4
shr ah,cl ; 右移4位,ah为十进制的十位数
and al,00001111b ; al为十进制的个位数
add ah,30h
add al,30h ; 数值转字符形式
xchg ah,al
mov [di],ax
add di,3
inc si
mov cx,bx
loop get_dt_s1
mov bx,0b800h
mov es,bx
mov bx,0
mov si,timestr-my_main+7e00h
call show_str
pop bx
pop es
pop ax
pop cx
pop di
pop si
ret
; 延时
delay:
push ax
push dx
mov dx,6000h ; 循环6000000h次
mov ax,0
delays1:
sub ax,1
sbb dx,0
cmp ax,0
jne delays1
cmp dx,0
jne delays1
pop dx
pop ax
ret
; 设置系统时间
set_dt:
push si
push di
push cx
push ax
push bx
mov di,timeaddr-my_main+7e00h
mov cx,6
set_dt_s1:
mov ax,[si] ; al为十位数,ah为个位数
sub ah,30h
sub al,30h
and ah,00001111b ; 取个位数
mov bx,cx
mov cl,4
shl al,cl
or al,ah
mov cx,bx
mov ah,al
mov al,[di]
out 70h,al
mov al,ah
out 71h,al
inc di
add si,3
loop set_dt_s1
pop bx
pop ax
pop cx
pop di
pop si
ret
db 1024 dup (0) ; 填充1k字节,原因同上
code ends
end start
执行结果:
以上是关于汇编:史上最全注释,王爽汇编语言,课程设计2源码的主要内容,如果未能解决你的问题,请参考以下文章
汇编语言(第3版)王爽试验7 寻址方式在接过话数据访问中的应用(代码+注释)