《汇编语言(第四版)》课程设计二

本文详细展示了如何在MASM2015环境下编写和修改引导程序及主程序的代码,包括安装引导程序、主程序到软盘,以及处理中断例程。作者在分析他人代码的基础上,解决了401行的编译问题,并分享了完整的代码示例。
摘要由CSDN通过智能技术生成

前后研究了几天,也看了别人的代码,最终发现怎么写也没有别人写的好。干脆就不献丑了,只是把一些想法记录下来。

别人写的代码:小杰玩编程HkMayfly,Apollon_krj,Risuss

看来看去,个人觉得“小杰玩编程”写的那个代码看起来最好,只是我遇到个问题:

401行有个table-my_main+7e00h[bx],我把它改成[table-my_main+7e00h+bx]之后才能在MASM2015环境里面编译通过,而且在虚拟机里能正常用。可惜TA的网站上没有留言功能,我也没办法请教一下。
为了防止哪天TA的网站不在了,代码没了,我就把TA的代码改一下贴在下面:

; 实现了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];这是第401行,原先的代码是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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值