汇编语言学习-课程设计


课程设计

在整个课程中,我们一共有两个课程设计,编写两个比较综合的程序,这是第一个。任务:将实验7中的power idea公司的数据按照图10.2所示的格式在屏幕上显示出来。

效果
20211018160247

难点

  • 从数字转换成字符串–通过除以10来不断获取余数,在加上30,最后再逆向输出
  • 因为程序要显示的数据有些已经大于65535,应该编写一个新的数据到字符串转换的子程序,完成dword型到字符串的转换
  • 在循环中如何选择寄存器(组合)来实现对有规律数据的复制
  • 对数据存储结构和控制移位的寄存器值的变化的控制

总结

虽然实现了效果,但是在思路上还是比较凌乱,尤其是在整体的思路上面.还是改不了边写边改的毛病.

  • 后面还是要不断的把代码再优化优化

整理

寄存器寻址组合

已知的是 di+bp / bx+si / bx+di 这几种可以用来在内存中遍历数据,在用之前,需要搞清楚,数据元素的确定最少需要几个变化的值来定位。

条件指令的转移范围

条件指令的转移范围只能是从(-128-127),如果想要跳转的位置超过了这个范围,可以先跳到附近的位置,再在附近的位置利用无条件指令跳到想跳到的位置。

汇编语言清除屏幕输出

mov ah,15
int 10h
mov ah,0
int 10h

编码

  • 哪些寄存器在循环的过程中需要变化,哪些寄存器需要在下一次循环或者调用子程序之前初始化,这些都要搞清楚
  • 再利用div等有范围和大小限制的指令时,考虑一下是否会有溢出的情况
  • jmp和je等条件跳转指令如何实现循环的过程

代码


;TODO 
;1.现在 char段前面四个字节的年份复制完成了 √
;2.着手将收入转换位为字符串并存在char段中  √
;3.下一步是 将雇员数转换为字符串存在char段中,这个应该不用divdw了 √
;4.最后一步就是显示了,不过应该也是比较头疼的把 √

;整理TODO
;1.整理一下到底哪几个寄存器可以组合起来用来寻址,我快疯了~~~
;已知的是di+bp/bx+si/bx+di
assume cs:codesg,ds:data
data segment
    ;0
    db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
    db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
    db '1993','1994','1995'

    ;54H
    dd 16,22,382,1356,2390,8000,160000,24486,50065,97479,140417,197514
    dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
    
    ;A8H
    dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
    dw 11542,14430,15257,17800
data ends

table segment
    db 21 dup ('year summ ne ?? ')
table ends

char segment
    db 672 dup(0) ;672 = 32*21 每一年的数据占23个字节(取个整数刚好是32个字节-16的倍数), 一共有21年
    ;00 XXXX
    ;03 XXXX
    ;06 XXXX
    ;09 XXXX
    ;年份 1997          4字节
    ;空格               1字节-5
    ;收入 max = 5937000 7个字节
    ;空格               1字节-13    
    ;雇员数 max = 17800 5个字节
    ;空格               1字节-19
    ;人均收入 max = 333  3个字节
    ;空格               1字节
char ends

testsg segment
    db 1680 dup(0) 
testsg segment

codesg segment

start:
    mov ah,15
    int 10h
    mov ah,0
    int 10h

    mov ax,data
    mov ds,ax

    mov ax,table
    mov es,ax

    call calculate_income

    mov ax,table
    mov ds,ax

    mov ax,char
    mov es,ax
    call duplicate_year
    
    call num_char
    call employees_char
    call average_salary_char
    call show_str
    
    mov ax,4c00h
    int 21h
;将数据存入显存当中
show_str:
    mov ax,char
    mov ds,ax
    mov ax,0B800H
    mov es,ax
    
    mov dh,4;行号
    mov dl,0;列号
    mov cl,2;颜色
    mov bx,0
    show_str_year:
        push dx
        ;找行号对应的内存地址
        mov ah,00
        mov al,160
        mul dh
        mov bp,ax
        ;找列对应的内存地址
        mov ah,00
        mov al,2
        mul dl
        mov di,ax
        
        ; ;找行号对应的内存地址--数据段
        ; mov ah,00
        ; mov al,10H
        ; mov dl,dh
        ; mov dh,0
        ; mul dx
        ; mov bx,ax
        ;把颜色转移一下
        mov al,cl
        push cx
        mov si,0
        show_str_year_loop:
            mov cl,ds:[bx+si]
            mov ch,0
            jcxz show_str_year_loop_ok
            mov es:[bp+di],cl
            mov es:[bp+di+1],al

            add di,2
            add si,1
            jmp short  show_str_year_loop
        show_str_year_loop_ok:
            pop cx
            mov dl,10;列号
            ;更新下一个列地址
            mov ah,00
            mov al,2
            mul dl
            mov di,ax
            ;把颜色再设置回来
            mov al,cl
        
        push cx
        mov si,0
        show_str_income_loop:
            mov cl,ds:[bx+si+5]
            mov ch,0
            jcxz show_str_income_loop_ok
            mov es:[bp+di],cl
            mov es:[bp+di+1],al

            add di,2
            add si,1
            jmp short  show_str_income_loop
        show_str_income_loop_ok:
            pop cx
            mov dl,20;列号
            ;更新下一个列地址
            mov ah,00
            mov al,2
            mul dl
            mov di,ax
            ;把颜色再设置回来
            mov al,cl
        
        push cx
        mov si,0
        show_str_employees_loop:
            mov cl,ds:[bx+si+13]
            mov ch,0
            jcxz show_str_employees_loop_ok
            mov es:[bp+di],cl
            mov es:[bp+di+1],al

            add di,2
            add si,1
            jmp short  show_str_employees_loop
        show_str_employees_loop_ok:
            pop cx
            mov dl,30;列号
            ;更新下一个列地址
            mov ah,00
            mov al,2
            mul dl
            mov di,ax
            ;把颜色再设置回来
            mov al,cl
        push cx
        mov si,0
        show_str_average_loop:
            mov cl,ds:[bx+si+19]
            mov ch,0
            jcxz show_str_average_loop_ok
            mov es:[bp+di],cl
            mov es:[bp+di+1],al

            add di,2
            add si,1
            jmp short  show_str_average_loop
        show_str_average_loop_ok:
            pop cx
            mov dl,40;列号
            ;更新下一个列地址
            mov ah,00
            mov al,2
            mul dl
            mov di,ax
            ;把颜色再设置回来
            mov al,cl       
        
        pop dx
        add bx,20H
        add dh,1;行号加一
        mov dl,0;列号清零
        mov ax,21
        sub al,dh
        cmp byte ptr al,0
        ; cmp byte ptr dh,21
        ;je show_str_ok;这很明显不相等啊,为什么还跳转了?
        ;你应该是跳转到循环里才对,如果不是跳到循环里,它其实没有发生跳转,只是按顺序往下执行刚好到下面了
        je show_str_ok;这样写也跳转了。。??
        show_str_year_relay:
            jmp show_str_year
    show_str_ok:
        ret
;把人均工资部分转换成字符串
average_salary_char:
    mov bx,0
    mov di,0
    mov cx,21
    average_salary_char_all:
        push cx
        mov ax,ds:[bx+0DH]
        mov dx,0
        mov si,0
        mov bp,10
        average_salary_char_one:    
            div bp
            cmp ax,0
            je average_salary_char_one_ok
            push dx
            mov dx,0
            inc si
            jmp average_salary_char_one
        average_salary_char_one_ok:
            push dx
            inc si            
            mov cx,si
            mov si,0
            mov bp,0
            average_salary_char_one_ok_loop:
                pop ax
                add al,30H
                mov es:[di+bp+19],al
                inc bp
                loop average_salary_char_one_ok_loop
        pop cx
        add bx,10H
        add di,20H
        loop average_salary_char_all
    ret
;把雇员数部分转换成字符串
employees_char:
    mov bx,0
    mov di,0
    mov cx,21
    employees_char_all:
        push cx
        mov ax,ds:[bx+0AH]
        mov dx,0
        mov si,0
        mov bp,10
        employees_char_one:    
            div bp
            cmp ax,0
            je employees_char_one_ok
            push dx
            mov dx,0
            inc si
            jmp employees_char_one
        employees_char_one_ok:
            push dx
            inc si            
            mov cx,si
            mov si,0
            mov bp,0
            employees_char_one_ok_loop:
                pop ax
                add al,30H
                mov es:[di+bp+13],al
                inc bp
                loop employees_char_one_ok_loop
        pop cx
        add bx,10H
        add di,20H
        loop employees_char_all
    ret
;把收入部分的数字转换为字符串存储到内存当中
num_char:
    mov bx,0
    mov cx,21
    num_char_loop:
        push cx
        
        mov si,0
        mov di,0
        mov ax,ds:[bx+5H]
        mov dx,ds:[bx+7H]
        mov cx,0AH
        ;具体的一个数转换为字符
        num_char_loop_div:
            call divdw
            mov bp,0
            ;如果dx和ax都为零说明该数转换完成
            mov bp,dx
            or bp,ax
            cmp bp,0
            je num_char_loop_div_ok
            push cx;余数入栈
            inc si
            mov cx,0AH;每次跳回去之前要把cx再设置为零
            jmp num_char_loop_div
            num_char_loop_div_ok:
                ;最后一个商小于10,上面没办法入栈了
                push cx;在这里把这个入栈
                inc si

                mov cx,si;si中保留着这个具体数的所有余数
                mov bp,0
                mov ax,2H
                mul bx 
                mov di,ax 
                num_char_loop_div_ok_loop:    
                    pop ax
                    add ax,30H
                    mov es:[di+5H+bp],al
                    add bp,1
                    loop num_char_loop_div_ok_loop
                ; ;填了个空格
                ; mov al,' '
                ; mov es:[di+5H+bp],al
        pop cx
        add bx,10H
        loop num_char_loop
;把data段中的年份复制到char段里面去
duplicate_year:
    mov bx,0
    mov si,0
    mov di,0
    mov cx,21
    duplicate_year_all:
        push cx
        mov cx,2
        mov bx,0
        duplicate_year_year:
            mov ax,ds:[di+bx]
            mov es:[si+bx],ax
            add bx,2
            loop duplicate_year_year
        ; ;添个空格 
        ; mov al,' '
        ; mov es:[si+bx],al

        add si,20H
        add di,10H
        pop cx
        loop duplicate_year_all

    ret

;刚好是之前计算人均收入程序
calculate_income:

    mov bx,0;bx定位data段,bx year和收入都是四个字节可以公用,但是雇员数是两个字节的没办法公用
    mov bp,0;bp定位es段
    mov di,0;di用来在循环中服务于雇员数,因为它是两个字节,没办法跟year和outcome公用一个递增寄存器
    mov cx,21

    overall:
        push cx

        mov cx,2
        mov si,0
        year:
            ;年份的4字节---两个字
            mov ax,ds:[bx+si]
            mov es:[bp+si],ax
            add si,2
            loop year

        mov cx,2
        mov si,0
        outcome:
            ;收入的4字节---两个字
            mov ax,ds:[bx+54H+si]
            mov es:[bp+5H+si],ax
            add si,2
            loop outcome

        ;雇员数---一个字
        mov ax,ds:[di+0A8H]
        mov es:[bp+0AH],ax

        ;计算人均收入
        mov ax,es:[bp+5H];计算人均收入的,要用es的定位,那偏移量就得按照es的来。最开始写成ds的定位了[bx+54H]了
        mov dx,es:[bp+5H+2]
        div word ptr es:[bp+0AH]
        mov es:[bp+0DH],ax

        ;所有的空格
        ;第一次没有考虑到空格是一个字节,这里不应该用16位的,应该用8位的
        mov al,' '
        mov es:[bp+4H],al
        mov es:[bp+9H],al
        mov es:[bp+0FH],al

        pop cx
        add di,2
        add bx,4
        add bp,16
        loop overall
    ret


;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword,除数为word.结果为dword
;参数: ax dword的低16位 | dx dword高16位 | cx除数16位
;返回: dx 结果的高16位, ax 结果的低16位, cx 余数
;应用举例:计算 1000000/10(F4240H/0AH)
;结果: dx = 0001H  ax = 86A0H cx = 0
divdw:
    push si
    push bx

    push ax
    mov ax,dx
    div cl
    mov bl,al
    mov bh,00H
    mov al,ah
    mov ah,00H;bx保留商,ax保留余数

    pop si
    mov dx,si

    mov dx,ax
    mov ax,si
    div cx;32/16 ax余数,dx商

    mov si,bx
    mov bx,dx
    mov dx,si

    mov cx,bx

    pop bx
    pop si
    ret


codesg ends
end start

文章作者: 美食家李老叭
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 美食家李老叭 !
评论
  目录