王爽<<汇编语言>>第 3 版 关于 jmp short 和 jmp near ptr 的区别

jmp short 标号 === (ip)=(ip)+8 位位移
8 位位移的意思是, 下图中的EB0A 的0A 就是 8 位长度表示的位移数据, 表示向后跳 10 个字节

2024-06-28T06:57:40.png2024-06-28T06:57:40.png

jmp near ptr 标号 === (ip)=(ip)+16 位位移
我原以为 16 位位移是按 2 个字节一跳, 我还想那要是要跳 17 个字节怎么办?
16 位位移的意思是, 下图中的 E92201 中的 2201 就是 16 位长度表示的位移数据, 表示向后跳 290 个字节

2024-06-28T07:00:28.png2024-06-28T07:00:28.png

如果用 jmp near ptr 替代 jmp short 呢, 编译器会编译出一条 NOP 的指令
2024-06-28T07:03:57.png2024-06-28T07:03:57.png

王爽《汇编语言》第 3 版 实验 课程设计 1

assume cs:codesg

stacksg segment stack
    dw 0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0
stacksg ends

datasg segment
    db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
    db '1984','1985','1986','1987','1988','1989','1990', '1991','1992'
    db '1993','1994','1995'
    dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
    dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
    dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
    dw 11542,14430,15257,17800
datasg ends

dataview segment
    db 16 dup (0)
    db 16 dup (0)
dataview ends


table segment
    db 21 dup ('????????????????'),0
table ends

codesg segment
start:

    mov ax,datasg
    mov ds,ax
    mov ax,table
    mov es,ax
    mov ax,stacksg
    mov ss,ax
    mov sp,64

    mov cx,21 ;设置循环次数为 21 次
    mov bx,0 ;每个循环加 4
    mov si,0 ;每个循环加 2
    mov di,0 ;每个循环加 16

    s1:
        ;1  处理年份
        mov ax,ds:[bx]
        mov dx,ds:[bx+2]
        mov es:[di],ax
        mov es:[di+2],dx

        ;2 处理收入
        mov ax,ds:[bx+84]
        mov dx,ds:[bx+86]
        mov es:[di+5],ax
        mov es:[di+7],dx

        ;3 处理雇员数量
        mov ax,ds:[si+168]
        mov es:[di+10],ax

        ;4 处理人均收入
        mov ax,es:[di+5]
        mov dx,es:[di+7]
        div word ptr es:[di+10]
        mov es:[di+13],ax


        ;处理 4 个 空格
        mov al,' '
        mov es:[di+4],al
        mov es:[di+9],al
        mov es:[di+12],al
        mov es:[di+15],al

        ;循环计数器累加
        add si,2
        add bx,4
        add di,16
    loop s1


    mov cx,21 ;设置循环次数为 21 次
    mov bx,0 ;每个循环加 16
    mov ax,table
    mov es,ax

    mov ax,dataview
    mov ds,ax

    mov dh,4
    mov dl,1

    s2:
        mov bp,cx ;备份 cx
        mov dl,1

        ;打印年份
        mov ax,es:[bx+0]
        mov ds:[0],ax
        mov ax,es:[bx+2]
        mov ds:[2],ax
        mov byte ptr ds:[4],0h

        mov cl,2
        mov si,0
        call show_str

        ;打印 收入
        mov cx,dx
        mov ax,es:[bx+5]
        mov dx,es:[bx+7]
        call fn_dtoc

        mov dx,cx
        add dl,16
        mov si,0
        mov cl,2
        call show_str

        ;打印 雇员数量
        mov cx,dx
        mov ax,es:[bx+10]
        mov dx,0h
        call fn_dtoc
        mov dx,cx
        add dl,16
        mov si,0
        mov cl,2
        call show_str

        ;打印 人均收入
        mov cx,dx
        mov ax,es:[bx+13]
        mov dx,0h
        call fn_dtoc
        mov dx,cx
        add dl,16
        mov si,0
        mov cl,2
        call show_str


        ;循环计数器累加
        add bx,16

        inc dh
        mov cx,bp ;还原 cx
    loop s2


    ;call show_str
    mov ax,4c00H
    int 21H



    ;把给定的字符串交换位置 比如: 1234 变成 4321
    ;段地址放 ds 起始地址放 si 长度放 cx
    fn_chang_str:

        ;如果字符串长度为 1 就返回
        sub cx,1
        jcxz label_fn_chang_str_return_now
        inc cx

        push ax
        push dx
        push bx
        push bp

        mov ax,cx ;这里做除法用
        mov dx,cx ;这里把长度放的 bx 中后面要用
        mov ch,0
        mov cl,2
        div cl ;这时 al=商 ah = 余数

        sub dx,1
        mov cl,al
        mov bp,0
        label_fn_chang_str_3:
            mov ah,ds:[bp+si]
            mov bx,dx
            sub bx,bp
            mov al,ds:[si+bx]
            mov ds:[bp+si],al
            mov ds:[si+bx],ah
            inc bp
            loop label_fn_chang_str_3

        label_fn_chang_str_return:
            pop bp
            pop bx
            pop dx
            pop ax

        label_fn_chang_str_return_now:
            ret

    ;把一个数值变成可以显示的 ascii 码
    ;高位放 dx
    ;低位放 ax
    ;无返回, 结果直接写到 dataview:[0]
    fn_dtoc:

        push cx
        push bx
        push ds
        push si

        mov bx,dataview
        mov ds,bx
        mov si,0


        label_fn_dtoc_j:
            mov cx,10
            call fn_divdw
            mov bx,cx
            add bx,30h
            mov ds:[si],bx
            add si,1
            mov cx,ax
            jcxz label_fn_dtoc_return
            jmp short label_fn_dtoc_j


        label_fn_dtoc_return:
            ;翻转
            mov cx,si
            mov byte ptr ds:[si],0
            mov si,0
            call fn_chang_str


            pop si
            pop bx
            mov ds,bx
            pop bx
            pop cx

            ret

    ;不会溢出的除法
    ; 被除数低位放 ax, 被除数高位放 dx,除数放 cx
    ; 结果: 高16 位放 dx,低 16 位放 ax, 余数放 cx
    fn_divdw:
        ;备份 reg
        push bp
        push bx

        ;1 算 H/N
        ;备份 被除数低 16 位放到 bp 中
        mov bp,ax
        mov ax,dx
        div cl
        mov bx,ax  ;这时候 bl 是 H/N 的商,bh 是 H/N 的余数

        ;算 [rem(h/n)*65536+L]/N
        mov dh,0
        mov dl,bh
        mov ax,bp
        div cx  ; ax=商,dx=余

        ;把结果放到相关寄存器中
        mov cx,dx

        mov dh,0
        mov dl,bl

        pop bx
        pop bp
        ret

    ; 在指定的位置打印指定的字符串
    ;  dh = 行号  dl= 列号 cl = 颜色 si = 要打印的字符串地址
    show_str:
        ;1 暂存子程序用到的寄存器
        push ax ;备份 ax
        push bx ;备份 bx
        push cx ;备份 cx
        push dx ;备份 dx
        push es    ;备份 es

        ;2 设置显存地址,段地址放入 es 中
        mov ax,0b800h
        mov es,ax ;设置显存段地址

        ;3 设置指定的行和列的显示偏移地址放入 bx 中
        push cx ;因为后面 loop 要用到 cx ,所以再备份一次
        mov ch,0
        mov cl,dh ;把参数行号放入 cx 中准备 loop
        mov bx,0 ;要把实际的行偏移地址放在 bx 中,所以清理 bx 准备放数据
        sub cx,1 ;cx-1 是因为要在第 8 行显示的话,地址加到第 7 行就行了
        setrows:
            add bx,160 ;这里加 160 是一行显示 80 个字符,每个字符占用 1 个字
            loop setrows
        mov ch,0
        mov cl,dl
        sub cx,1
        setcols:
            add bx,2
            loop setcols
        pop cx
        mov dh,cl
        mov ch,0

        mov ax,16 ; 如果不够 20 个字符就用空格补齐

        ;3 读取 data 中的字符, 直到读取到 0 为止
        readdata:
            mov cl,ds:[si]
            jcxz next_01
            mov dl,cl
            mov es:[bx],dx
            inc si
            add bx,2
            sub ax,1
            jmp short readdata

        next_01:
            mov cx,ax
            mov dl,20h
        addspace:
            jcxz return_show_str
            add bx,2
            mov es:[bx], dx
            sub cx,1
            jmp short addspace

        ;返回
        return_show_str:

            pop es
            pop dx
            pop cx
            pop bx
            pop ax
            ret
codesg ends
end start

1719309261605.png1719309261605.png

寄存器太少, 很多还指定寄存器, 要不停的备份,还原

王爽《汇编语言》第 3 版 实验 10 编写子程序 作业

assume cs:code,ss:stack,ds:data

stack segment
    dw 0,0,0,0,0,0,0,0
stack ends

data segment
    db 'Welcome to masm!',0
data ends



code segment
    start:
        mov ax,stack
        mov ss,ax
        mov sp,16

        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

    show_str:
        ;1 暂存子程序用到的寄存器
        push ax ;备份 ax
        push bx ;备份 bx
        push cx ;备份 cx
        push dx ;备份 dx
        push es    ;备份 es

        ;2 设置显存地址,段地址放入 es 中
        mov ax,0b800h
        mov es,ax ;设置显存段地址

        ;3 设置指定的行和列的显示偏移地址放入 bx 中
        push cx ;因为后面 loop 要用到 cx ,所以再备份一次
        mov ch,0
        mov cl,dh ;把参数行号放入 cx 中准备 loop
        mov bx,0 ;要把实际的行偏移地址放在 bx 中,所以清理 bx 准备放数据
        sub cx,1 ;cx-1 是因为要在第 8 行显示的话,地址加到第 7 行就行了
        setrows:
            add bx,160 ;这里加 160 是一行显示 80 个字符,每个字符占用 1 个字
            loop setrows    
        mov ch,0
        mov cl,dl
        sub cx,1
        setcols:
            add bx,2
            loop setcols
        pop cx
        mov dh,cl
        mov ch,0

        ;3 读取 data 中的字符
        readdata:
            mov cl,ds:[si]
            jcxz return_show_str    
            mov dl,cl
            mov es:[bx],dx
            inc si
            add bx,2
            jmp short readdata

        ;返回
        return_show_str:
            pop es
            pop dx
            pop cx
            pop bx
            pop ax
            ret



code ends

end start

王爽《汇编语言》第3版 实验 9 根据材料编程

assume ds:datasg,cs:codesg

datasg segment
    db 'welcome to masm!'
    db 02h,24h,71h ;分别代表绿字,绿地红字,白底蓝字
datasg ends

codesg segment
    
    start:
    mov ax,0B878h 
    mov ds,ax ;设置要写入的内存地址为显存区域大概 13 行位置 160*12=1920=780h

    mov cx,16 ;16 个字符,所以要 loop 16 次
    mov bx,60 ;设置列位置

    mov ax,datasg
    mov es,ax ;把字符串数据放到 es
    mov bp,0  ;记数,用于读取 welcome....
    mov si,0  ;记数,用于读取 02h,24h,71h;

      s16:

        mov al,es:[bp] ;依次读取 welcome... 放入 al

        mov dx,cx ;备份好 cx
        mov ch,0
        mov cl,al
        sub cl,20h ;如果当前读的字符是空格就换显示的样式
        jcxz changstyle

        mov ah,es:[si+16] ;把显示样式写入ah
        jmp notspace ;如果当前读取的字符不是空格,就跳过更换样式的代码

      changstyle:
          inc si ;更换字体的显示样式
          mov ah,00h ;如果是空格就没有样式

      notspace:          
          mov [bx],ax ; 显示到屏幕上
        add bx,2
        inc bp
        mov cx,dx ;还原 cx 继续进行 loop 循环

    loop s16


    mov ax,4c00h
    int 21h
codesg ends
end start

1718959974877.png1718959974877.png