实验代码 《汇编语言》王爽

Assembly Language

Posted by Penistrong on February 3, 2021

《汇编语言》王爽 实验代码

  • 实验10

  • 编写3个子程序
    • 子程序①:在指定位置,用指定颜色,显示一个用0结束的字符串
    • 子程序②:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型
    • 子程序③:将data段中的数据以十进制的形式显示出来
    assume cs:code
    data segment
        db 'Welcome to MASM!', 0
    data ends
    
    code segment
    start:  mov ax, 4240h
            mov dx, 000Fh
            mov cx, 0AH
            call far ptr divdw  ;测试子程序②-DEBUG下查看(dx)=0001H (ax)=86A0H (cx)=0
    
            mov ax, 12666
            mov bx, data
            mov ds, bx
            mov si, 0
            call dtoc           ;测试子程序③-将数值转换成对应的十进制字符串,用子程序①的测试是否转换成功
    
            mov dh, 8
            mov dl, 3
            mov cl, 2
            call show_str       ;测试子程序①-查看第8行第3列是否显示绿色字符串
    
            mov ax, 4c00h
            int 21h
    ;子程序①:显示一个用0结束的字符串 字符串首址在ds:si处
    ;Params: dh=行号(0~24) dl=列号(0~79) cl=颜色
    ;return: null
    show_str:   push dx
                push cx
                push si
    Initialize: mov ax, 0B800h
                mov es, ax
                mov ax, 0A0h
                mul dh              ;计算给出的第(dh)行所在的首址 (dh)*160 byte
                mov bp, ax          ;送偏移地址寄存器bp
                mov ax, 2       
                mul dl              ;计算给出的第(dl)列所在的偏移地址 (dl)*2 byte
                mov di, ax          ;送偏移地址寄存器di
                mov ah, cl          ;将颜色字符串送到ah
                mov si, 0
    s1:         xor cx, cx          ;cx置零
                mov cl, ds:[si]     ;取字符串的字符(8bitASCII码)
                jcxz ret1         ;若为0则跳转至ret处
                  
                mov al, cl          ;此时将cl中存储的字符ASCII码送入al
                mov es:[bp][di], ax ;将字符与其属性的组合送入显示缓冲区
    
                inc si              ;字符串指针+1
                add di, 2           ;显示缓冲区指针+2
                jmp short s1
    ret1:       pop si
                pop cx
                pop dx
                ret
    
    ;子程序②:进行不会产生溢出的除法运算,被除数为dword,除数为word,结果为dword
    ;描述:   使用公式X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
    ;Params: (ax)=被除数低16位 (dx)=被除数高16位 (cx)=除数
    ;return: (ax)=结果低16位 (ax)=结果高16位 (cx)=余数
    divdw:      push ax     ;被除数低16位L入栈
                push dx     ;被除数高16位H入栈
                ;开始计算H/N
    calculate:  pop ax      ;高16位H出栈作32位被除数的低16位
                xor dx, dx  ;32位被除数高16位置零
                div cx      ;计算H/N ax中存放商即int(H/N)部分 dx中存放余数rem(H/N)
                pop bx      ;被除数低16位L出栈 暂存于bx中
                push ax     ;int(H/N)入栈
                ;开始计算[rem(H/N)*65536+L]/N 此时余数作为32位被除数的高16位(直接使用dx) 原被除数的低16位直接使用
                mov ax, bx
                div cx      ;计算[rem(H/N)*65536+L]/N ax中存放dword型结果的低16位
                mov cx, dx  ;余数送入cx中
                pop dx      ;int(H/N)出栈 送入dx(即结果dword型的高16位)
    
                ret
    
    ;子程序③:将word型数据转变为表示十进制数的字符串,字符串以0结尾
    ;描述: 用除法 每次除10 拿到每位上的十进制值(余数) 注意需要逆序输出 使用栈存储每个余数(每个数位)对应的ASCII码
    ;Params: (ax)=word型数据 ds:si指向字符串的首地址
    ;return: null
    dtoc:       push ax         ;要处理的数 入栈
                mov bx, 10      ;除数10
                xor cx, cx      ;cx置零
                xor dx, dx      ;dx置零
    s3:         div bx          ;采用16位除法(若采用8位除法,单步结果不一定小于256) ax存储商 dx存储余数
                add dx, 30H     ;余数(0~9)的实际数值+30H即得到它们对应的ASCII码
                push dx         ;入栈 准备逆序输出
                xor dx, dx      ;dx置零 防止上一步中的余数作为下一步运算的高16位而产生错误
                inc si
    
                mov cx, ax      ;若商为0说明各位的值已全部求出
                jcxz ret2       ;写在inc si后面让si顺便记录循环次数
    
                jmp short s3
    ret2:       mov cx, si      ;准备逆序输出栈中存储的各余数字符
                mov si, 0       ;si复用为偏移地址寄存器
    s4:         pop dx          ;由于栈只能使用16位,因此dl为实际ASCII码
                mov ds:[si], dl
                inc si
                loop s4
    
                pop ax          ;复原ax
                ret
    code ends
    end start
    

  • 实验11

  • 编写一个子程序,将包含任意字符,以0结尾的字符串中的小写字母转换为大写字母
    • 参数: ds:si指向字符串首地址
    assume cs:codesg
    
    datasg segment
        db "Beginner's All-purpose Symbolic Instruction Code.", 0
    datasg ends
    
    codesg segment
    start:  mov ax, datasg
            mov ds, ax
            mov si, 0
            call letterc
    
            mov dh, 8
            mov dl, 3
            mov cl, 2
            call show_str       ;调用子程序显示字符串检查是否将小写转换为大写
    
            mov ax, 4c00h
            int 21h
    
    letterc:push si         ;保存
    s:      mov al, [si]
            cmp al, 0
            je over         ;(al)=0,已读到字符串结束符
            cmp al, 97      
            jb continue     ;小于小写字母a对应的ASCII码97则不处理
            cmp al, 122
            ja continue     ;大于小写字母z对应的ASCII码122则不处理
            sub al, 20H     ;(al)-=32(20H) 得到小写字母其对应大写的ASCII码
            mov [si], al
    continue:inc si         ;偏移地址++, 继续读取下一个字符
            jmp s
    over:   pop si
            ret
    
    ;子程序①:显示一个用0结束的字符串 字符串首址在ds:si处
    ;Params: dh=行号(0~24) dl=列号(0~79) cl=颜色
    ;return: null
    show_str:   ......      ;见上一实验
    
    codesg ends
    
    end start
    

  • 实验12

  • 编写0号中断的处理程序,使得在除法溢出发生之前,在屏幕中间显示字符串“Divide error!”,然后返回到DOS

    assume cs:code
    
    code segment
    start:  mov ax, cs
            mov ds, ax
            mov si, offset do0                  ;指向待安装的中断处理程序所在的源地址
            mov ax, 0
            mov es, ax
            mov di, 0200H                       ;送入中断处理程序的目标内存地址
            mov cx, offset do0end - offset do0  ;通过编译器计算中断处理程序do0所占的字节个数
            cld                                 ;设置串传送指令的传输方向为正
            rep movsb
            mov ax, 0
            mov es, ax
            mov word ptr es:[0*4], 0200H        ;设置中断向量表中0号中断的偏移地址
            mov word ptr es:[0*4+2], 0          ;设置中断向量表中0号中断的段地址
    
            ;测试是否已安装新的除法溢出中断处理程序
            mov ax, 1000H
            mov bh, 1
            div bh
    
            mov ax, 4C00H
            int 21H
    
    do0:        jmp short do0start
                db "Divide error!"
    do0start:   mov ax, cs
                mov ds, ax
                mov si, 0202H                   ;待显示的字符串的地址(在内存中存放中断处理程序的地址)
    
                mov ax, 0B800H
                mov es, ax
                mov di, 12*160+36*2             ;显示在第12行(从第0行开始)第36列
    
                mov cx, 13                      ;待显示字符串的长度
    s:          mov al, [si]                    ;传送字符对应的ascii码
                mov ah, 00001100B               ;设置字符的属性
                mov es:[di], ax                 ;送入显存空间
                inc si
                add di, 2
                loop s
    
                mov ax, 4C00H
                int 21H
    do0end:     nop
    code ends
    end start
    

  • 实验13

  • 编写并安装int 7ch号中断例程,功能为显示一个用0结束的字符串,中断例程安装在0:200处

    assume cs:code
    
    data segment
        db "Welcome to masm", 0
    data ends
    
    code segment
    start:  mov ax, cs
            mov ds, ax
            mov si, offset show_str             ;指向待安装的中断处理程序所在的源地址
            mov ax, 0
            mov es, ax
            mov di, 0200H                       ;送入中断处理程序的目标内存地址 0000:0200 处
            mov cx, offset show_str_end - offset show_str   ;通过编译器计算中断处理程序do0所占的字节个数
            cld                                 ;设置串传送指令的传输方向为正
            rep movsb
    
            mov ax, 0
            mov es, ax
            mov word ptr es:[7ch*4], 0200H      ;设置中断向量表中7ch号中断的偏移地址
            mov word ptr es:[7ch*4+2], 0        ;设置中断向量表中7ch号中断的段地址
    
            ;测试中断是否成功安装
            mov dh, 10
            mov dl, 10
            mov cl, 00000010B                   ;绿色
            mov ax, data
            mov ds, ax
            mov si, 0
            int 7ch
    
            mov ax, 4C00H
            int 21H
    
    ;将实验10中使用的显示字符串的子程序作为中断例程安装
    ;子程序①:显示一个用0结束的字符串 字符串首址在ds:si处
    ;Params: dh=行号(0~24) dl=列号(0~79) cl=颜色
    ;return: null
    show_str:   push dx
                push cx
                push si
    Initialize: mov ax, 0B800h
                mov es, ax
                mov ax, 0A0h
                mul dh              ;计算给出的第(dh)行所在的首址 (dh)*160 byte
                mov bp, ax          ;送偏移地址寄存器bp
                mov ax, 2       
                mul dl              ;计算给出的第(dl)列所在的偏移地址 (dl)*2 byte
                mov di, ax          ;送偏移地址寄存器di
                mov ah, cl          ;将颜色字符串送到ah
                mov si, 0
    s1:         xor cx, cx          ;cx置零
                mov cl, ds:[si]     ;取字符串的字符(8bitASCII码)
                jcxz ret1         ;若为0则跳转至ret处
                  
                mov al, cl          ;此时将cl中存储的字符ASCII码送入al
                mov es:[bp][di], ax ;将字符与其属性的组合送入显示缓冲区
    
                inc si              ;字符串指针+1
                add di, 2           ;显示缓冲区指针+2
                jmp short s1
    ret1:       pop si
                pop cx
                pop dx
                iret
    show_str_end:   nop             ;占位 以计算中断例程部分有用的机器码字节数 便于安装
    
    code ends
    end start
    
  • 编写并安装int 7ch的中断例程,功能为完成loop指令的功能

    assume cs:code
    code segment
    start:  mov ax, cs
            mov ds, ax
            mov si, offset lp                   ;指向待安装的中断处理程序所在的源地址
            mov ax, 0
            mov es, ax
            mov di, 0200H                       ;送入中断处理程序的目标内存地址 0000:0200 处
            mov cx, offset lp_end - offset lp   ;通过编译器计算中断处理程序do0所占的字节个数
            cld                                 ;设置串传送指令的传输方向为正
            rep movsb
    
            mov ax, 0
            mov es, ax
            mov word ptr es:[7ch*4], 0200H      ;设置中断向量表中7ch号中断的偏移地址
            mov word ptr es:[7ch*4+2], 0        ;设置中断向量表中7ch号中断的段地址
    
            ;测试loop中断是否成功安装
            mov ax, 0B800H
            mov es, ax
            mov di, 160*12
            mov bx, offset s - offset se
            mov cx, 80
    s:      mov byte ptr es:[di], '!'
            add di, 2
            int 7ch
    se:     nop
            mov ax, 4c00h
            int 21h
    
    lp:     push bp
            mov bp, sp
            dec cx
            jcxz lpret
            add [bp + 2], bx       ;将堆中存放的此前调用中断时入栈的IP值加上转移位移 中断返回时达到修改IP的目的
    lpret:  pop bp
            iret
    lp_end: nop
    
    code ends
    end start
    
  • 使用系统提供的10号中断和21号中断在屏幕的第2,4,6,8行显示四句英文诗

    assume cs:code
    code segment
    s1: db 'Good,better,best,','$'
    s2: db 'Never let it rest,','$'
    s3: db 'Till good is better,','$'
    s4: db 'And better,best.','$'
    s:  dw offset s1, offset s2, offset s3, offset s4
    row:    db 2, 4, 6, 8
    
    start:  mov ax, cs
            mov ds, ax
            mov bx, offset s
            mov si, offset row
            mov cx, 4
    ok:     mov bh, 0
            mov dh, ds:[si]
            mov dl, 0
            mov ah, 2
            int 10h             ;调用10号中断的2号功能 设置光标位置 (BH)为页码 (DH)为行坐标 (DL)为列坐标
    
            mov dx, [bx]        ;将bx中存放的各诗句所在的偏移地址 使用ds:[bx]取到这些诗句实际的偏移地址送入dx
            mov ah, 9
            int 21h             ;调用21号中断的9号功能 显示字符串 DS:DX为串地址 以'$'结尾
            inc si
            add bx, 2           ;s中存放的是各诗句的偏移地址(word型) 故取下一个偏移地址执行bx+=2即可
            loop ok
    
            mov ax, 4c00h
            int 21h
    
    code ends
    end start
    

  • 实验14

  • 从CMOS RAM中取出时间信息,以“年/月/日 时:分:秒”的格式显示当前日期、时间

    assume cs:code
    
    data segment
    db '??/??/?? ??:??:??','$'
    unit db 9, 8, 7, 4, 2, 0
    data ends
    
    code segment
    start:  mov ax, data
            mov ds, ax
            mov bx, offset unit
            mov si, 0
            mov cx, 6
    s:      mov al, [bx]
            out 70h, al
            in al, 71h
            ;CMOS RAM中这些存储时间的单元均以BCD码形式存储
            mov ah, al
            push cx
            mov cl, 4
            shr ah, cl          ;高4位 十位数码值
            and al, 00001111B   ;低4位 个位数码值
            ;拿到数值对应的ASCII码
            add ah, 30h
            add al, 30h
              
            mov ds:[si], ah     ;十位数码写入日期字符串中
            mov ds:[si + 1], al ;个位数码写入日期字符串中
            add si, 3
            inc bx
            pop cx
            loop s
    
            ;调用21号中断的9号功能显示字符串 DS:DX为串地址
            mov dx, 0
            mov ah, 9
            int 21h
    
            mov ax, 4c00h
            int 21h
    
    code ends
    end start
    

  • 实验15

  • 安装一个新的int 9中断例程
    • 功能:在DOS下,按下“A”键后,除非不再松开,如果松开则满屏幕显示“A”,其他键照常处理
    assume cs:code
    
    stack segment
        db 128 dup (0)
    stack ends
    
    code segment
    start:  mov ax, stack   ;使用自己定义的栈
            mov ss, ax
            mov sp, 128
    
            push cs
            pop ds          ;设置(ds) = (cs)
    
            mov ax, 0
            mov es, ax      ;中断例程待安装的目的段地址
    
            mov si, offset int9
            mov di, 204h    ;在0:200H存放原来的int9中断例程的入口地址 所以0:204H处安装新的int9中断例程
            mov cx, offset int9end - offset int9
            cld
            rep movsb
    
            ;将中断向量表中原来的int9中断的入口地址从0:[9*4]处(dword型)复制到0:[200H]处
            push es:[9*4]
            pop es:[200h]
            push es:[9*4+2]
            pop es:[202h]
    
            ;把新的int9中断例程的入口地址送到中断向量表中存放9号中断入口地址的内存单元中
            ;用cli与sti包裹 防止在执行这段指令时 键盘引发中断 导致中断例程入口错误
            cli
            mov word ptr es:[9*4], 204h
            mov word ptr es:[9*4+2], 0
            sti
    
            mov ax, 4c00h
            int 21h
    
    int9:   push ax
            push bx
            push cx
            push es
    
    press:  in al, 60h  ;从60h端口读出键盘输入
    
            ;调用0:200H处的原始int9中断例程 处理其他硬件细节
            pushf
            call dword ptr cs:[200h]    ;这条指令执行时 该中断例程已成功安装在0:204H处 因此(cs)=0
    
            cmp al, 1Eh     ;“A”的通码为1Eh
            jne int9ret
    
            ;确认"A"已按下 循环等待"A"松开
    release:in al, 60h
            pushf
            call dword ptr cs:[200h]
            cmp al, 9Eh     ;"A"的断码为9Eh 断码=通码+80h
            jne release
    
            ;DOS显示缓冲区第一页全部显示"A"
            mov ax, 0b800h
            mov es, ax
            mov bx, 0
            mov cx, 2000
    s:      mov byte ptr es:[bx], 'A'
            add bx, 2
            loop s
    
    int9ret:pop es
            pop cx
            pop bx
            pop ax
            iret
    
    int9end:nop
    
    code ends
    end start
    

  • 实验16

  • 安装一个新的int 7ch中断例程,为显示输出提供下列功能子程序
    • 子程序0:清屏
    • 子程序1:设置前景色
    • 子程序2:设置背景色
    • 子程序3:向上滚动一行:依次将第n+1行的内容复制到第n行处,最后一行留空
  • 用ah传递中断中使用的子程序功能号:0清屏,1设置前景色,2设置背景色,3向上滚动一行
  • 对于1,2号功能,使用al传递颜色值(RGB) (al)∈{0,1,2,3,4,5,6,7}
  • 最重要指令 ORG 0204H(中断例程的安装地址) 表示以下指令从Origin所示的起始地址开始编译

    assume cs:code
    
    code segment
    start:  mov ax, cs
            mov ds, ax
    
            mov ax, 0
            mov es, ax
    
            mov si, offset int7c    ;ds:si源串地址
            mov di, 0204h           ;es:di目标地址
            mov cx, offset int7cend - offset int7c
            cld
            rep movsb
    
            ;将原始中断向量表中的int 7ch中断例程的入口地址复制到0:200处保存 以期恢复
            push es:[7ch*4]
            pop es:[0200h]
            push es:[7ch*4 + 2]
            pop es:[0202h]
    
            ;将新的int 7ch中断例程的入口地址放入中断向量表中 双字低16位为偏移地址 高16位为段地址
            cli
            mov word ptr es:[7ch*4], 0204h
            mov word ptr es:[7ch*4 + 2], 0
            sti
    
            ;测试新安装的int 7ch中断
            mov ah, 1
            mov al, 1   ;前景色设为蓝
            int 7ch
    
            mov ah, 2
            mov al, 6   ;背景色设为R+B即红+蓝->橙
            int 7ch
    
    delay:  push ax
            push dx
            mov dx,1000h
            mov ax,0
            s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    
            mov ax, 4c00h
            int 21h
    
            ;int 7ch中断
            ;用ah传递中断中使用的子程序功能号:0清屏,1设置前景色,2设置背景色,3向上滚动一行
            ;对于1,2号功能,使用al传递颜色值(RGB) (al)∈{0,1,2,3,4,5,6,7}
            ;注意由于安装时\程序段都被复制到0:0204H处,若是把这些子程序当前标号的地址直接放在sublist里
            ;会导致调用时实际上是找原始的偏移地址,因此现在计算它们相对于实际安装位置的地址即可
            org 0204h   ;ORG伪指令 标识以下的指令都是从偏移地址0204h(即中断例程的安装位置)开始,防止标号地址相对于安装前有改变
    int7c:  jmp short begin
    sublist dw sub0, sub1, sub2, sub3
    begin:  push bx
            push ds
    
            cmp ah, 3
            ja int7cret     ;功能号大于3则中断返回
            mov bl, ah
            mov bh, 0
            add bx, bx      ;功能号*2就是其子程序入口地址存放在sublist中的偏移
              
            push cs
            pop ds
            call word ptr sublist[bx]   ;调用对应功能子程序 2是jmp short begin所占的机器码字节数
    int7cret:   pop ds
                pop bx
                iret
    ;子程序0:清屏
    sub0:   push cx
            push es
            mov bx, 0b800h
            mov es, bx
            mov bx, 0
            mov cx, 2000        ;一页显示80*25=2000个
    sub0s:  mov byte ptr es:[bx], ' '
            add bx, 2
            loop sub0s
            pop es
            pop cx
            ret
    
    ;子程序1:设置前景色
    sub1:   push cx
            push es
            mov bx, 0b800h
            mov es, bx
            mov bx, 1
            mov cx, 2000        ;更改字符属性,以0B800H为段地址的显示缓冲区的奇字节为属性
    sub1s:  and byte ptr es:[bx], 11111000B ;分离代表前景色属性的0,1,2位
            or es:[bx], al                  ;将al中存放的颜色数值同当前前景色相或 达成前5位不变 后3位改为目标RGB值
            add bx, 2
            loop sub1s
            pop es
            pop cx
            ret
    
    ;子程序2:设置背景色
    sub2:   push cx
            push es
            mov cl, 4           ;由于al范围在0~8即3bit二进制表示
            shl al, cl          ;使用逻辑左移将其移至第4,5,6位便于后面直接与字符属性相或处理
            mov bx, 0b800h
            mov es, bx
            mov bx, 1
            mov cx, 2000        ;更改字符属性,以0B800H为段地址的显示缓冲区的奇字节为属性
    sub2s:  and byte ptr es:[bx], 10001111B ;分离代表背景色属性的4,5,6位
            or es:[bx], al                  ;将al中存放的颜色数值同当前字段属性相或,修改字符属性字节中的背景色bit位
            add bx, 2
            loop sub2s
            pop es
            pop cx
            ret
    
    ;子程序3:向上滚动一行:依次将第n+1行的内容复制到第n行处,最后一行留空
    sub3:   push cx
            push si
            push di
            push es
            push ds
    
            mov si, 0b800h      ;ax存储了信息不动用 用si中转
            mov es, si      
            mov ds, si
            mov si, 160
            mov di, 0
            cld
            mov cx, 24          ;共计24行(屏幕一共25行,最后一行留空)
    sub3s:  push cx
            mov cx, 160         ;行内共计80个字符一共160字节
            rep movsb           ;使用串传送不断复制
            pop cx
            loop sub3s
    
            mov cx, 80          ;开始处理最后一行
            mov si, 0
    sub3s1: mov byte ptr [160*24 + si], ' ' ;清空最后一行
            add si, 2
            loop sub3s1
    
            pop ds
            pop es
            pop di
            pop si
            pop cx
            ret
    int7cend:   nop
    code ends
    end start
    

  • 例题17.1

  • 使用int 16h中断,编程,接收用户的键盘输入,输入’r’/’g’/’b’将当前显示缓冲区中的字符设置为对应的颜色

    assume cs:code
    
    code segment
    start:  mov ah, 0
            int 16h
    
            mov ah, 1       ;设置的字符属性字节中前景色RGBbit位在低0,1,2位,这里设置为001表示默认蓝色
            cmp al, 'r'
            je red
            cmp al, 'g'
            je green
            cmp al, 'b'
            je blue
            jmp short sret  ;不是键盘的rgb通码则结束
          
    red:    shl ah, 1       ;逻辑左移一次 同下面执行的Green中的一次左移使字段变为100
    green:  shl ah, 1
    blue:   mov bx, 0b800h
            mov es, bx
            mov bx, 1
            mov cx, 2000
    s:      and byte ptr es:[bx], 11111000B ;前景色RGB位置0
            or es:[bx], ah                  ;使用前述字段对RGB进行设置
            add bx, 2
            loop s
    
    sret:   mov ax, 4c00h
            int 21h
    
    code ends
    end start
    

  • 实验17

  • 安装一个新的int 7ch中断例程,实现通过逻辑扇区号对软盘进行读写
    • ah传递功能号:0读1写
    • dx传递逻辑扇区号(0~2879)
    • es:bx指向存储读出数据或写入数据的内存区
    assume cs:code, es:data
    
    data segment
    destination dw 256 dup(0)   ;一个扇区512字节,即256个字,es:bx指向这段数据段即可
    data ends
    
    code segment
    start:  mov ax, cs
            mov ds, ax
            mov si, offset int7c
    
            mov ax, 0
            mov es, ax
            mov di, 0200h
    
            mov cx, offset int7c_end - offset int7c
            cld
            rep movsb
    
            ;将中断向量表中int 7ch中断的入口地址写入
            cli
            mov word ptr es:[7ch*4], 0200h
            mov word ptr es:[7ch*4 + 2], 0
            sti
              
            ;由于没有软盘,只是写出测试代码
            mov dx, 2879
            mov ax, data
            mov es, ax
            mov bx, destination
            mov ah, 0
            int 7ch
    
            mov ax, 4c00h
            int 21h
    
            ;org 0200h       ;由于并未在新中断中定义标号,故不用使用该伪指令(指明以下程序中的标号,编译器处理时以0200H为偏移起始地址)
    int7c:  push dx
            push cx
            push bx
            push ax
    
            cmp ah, 1           ;若功能号大于1则返回
            ja  int7c_ret
    
            ;调用int 13h时需要复原的寄存器
            push bx
            push ax
    
            ;dx中存放逻辑扇区号
            ;除以1440得到面号,余数继续处理
            mov ax, dx
            mov bx, 1440
            div bx              ;16位除法,AX保存商,DX保存余数
    
            push ax             ;面号压栈,待全部计算完毕后再调用int 13h中断
    
            mov ax, dx          ;把前一步中逻辑扇区号/1440的余数送到存放被除数的ax中
            mov bx, 18
            div bx              ;商为磁道号,余数为扇区号-1
    
            push ax             ;磁道号写入table中
            inc dx              ;余数加1,拿到扇区号
            push dx             ;扇区号写入table中
    
            ;计算完毕后调用int 13h
            pop bx
            mov cl, bl          ;(cl)=扇区号
            pop bx
            mov ch, bl          ;(ch)=磁道号
            mov dl, 0           ;驱动器号,软驱从0开始,硬盘从80h开始
            pop bx
            mov dh, bl          ;(dh)=磁头号 对于软盘就是面号
            pop ax              ;复原ax
            add ah, 2           ;由于该int 7ch中断0表示读而1表示写;int 13h中2表示读而3表示写。加2即可
            mov al, 1           ;默认读/写1个扇区
            pop bx              ;int 13h操作的内存地址也是放在es:bx中,因此复原bx
            int 13h
    int7c_ret:  pop ax
                pop bx
                pop cx
                pop dx
                iret
    int7c_end:  nop
    
    code ends
    end start