首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 嵌入开发 > 汇编语言 >

王爽汇编的课程设计对菜鸟来说是不是难了点?写了两天修改了一天还是搞不定,感觉已经精疲力尽了.

2013-09-06 
王爽汇编的课程设计对初学者来说是不是难了点?写了两天修改了一天还是搞不定,感觉已经精疲力尽了...本帖最

王爽汇编的课程设计对初学者来说是不是难了点?写了两天修改了一天还是搞不定,感觉已经精疲力尽了...
本帖最后由 u011536790 于 2013-08-23 22:02:31 编辑
运行后闪一下就没了,看了很久找不到原因。而且很可能不止一个问题,初学汇编没多久,搞不定可以直接跳过往下学习么?

;使数据显示为四列,每列占40个字节
assume cs:code,ds:data,ss:stack,es:data0

data segment
;以下数据段为年份
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
;至此共占84字节
;以下数据段为总资产
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514,345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;至此共占85+88=168字节
;以下数据段为人数
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226,11452,14430,15257,17800
;至此共占85+88+44=210字节
;以下数据段为平均资产
dd 21 dup (0)

data ends

data0 segment  ;字符串段,暂存转化的字符
db 10 dup (0)
data0 ends

stack segment  ;栈段
db 32 dup (0)
stack ends

code segment

start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov ax,data0
mov es,ax   ;关联各段
mov di,0    ;双字单元起始偏移地址为0
mov si,0    ;字单元起始偏移地址为0

call pj     ;调用子程序:计算平均资产


mov cx,21;
l:          ;循环显示21行数据


;显示年份:(参数:数据段中年份起始偏移地址ds:[di]、显存单元中对应的年份起始偏移地址bx)
call nf     ;调用子程序:显示年份

;显示总资产:(参数:数据段中总资产起始偏移地址ds:85[di]、显存单元中对应的总资产起始偏移地址bx)
mov ax,84[di]   ;总资产数值低位赋给ax作为被除数低位
mov dx,84[di+2] ;总资产数值高位赋给ax作为被除数高位
call dtoc       ;调用子程序:双字数值转化为字符。总资产数值转化为字符
mov bx,160+40   ;总资产字符串显示的起始偏移地址
mov dx,0
mov dl,02h      ;设置字体颜色
call show_str   ;调用子程序:显示字符串。在第二列显示总资产字符串


;显示人数:(参数:数据段中人数起始偏移地址ds:173[si]、显存单元中对应的人数起始偏移地址bx)
mov ax,168[si] ;人数赋给ax作为被除数低位
mov dx,0       ;人数被除数高位为0
call dtocz     ;调用子程序:字数值转化为字符。人数转化为字符
mov bx,160+80   ;人数字符串显示的起始偏移地址
mov dx,0
mov dl,02h      ;设置字体颜色
call show_str   ;调用子程序:显示字符串。在第三列显示人数字符串


;显示平均资产:(参数:数据段中平均资产起始偏移地址ds:217[si]、显存单元中对应的平均资产起始偏移地址bx)
mov ax,210[si] ;平均资产数值赋给ax作为被除数低位
mov dx,0       ;平均资产被除数高位为0
call dtocz     ;调用子程序:字数值转化为字符。平均资产数值转化为字符
mov bx,160+120   ;平均资产字符串显示的起始偏移地址


mov dx,0
mov dl,02h      ;设置字体颜色
call show_str   ;调用子程序:显示字符串。在第四列显示平均资产字符串


add si,2        ;字单元后移2个
add di,4        ;双字单元后移4个
add bx,160      ;显存单元后移160个,换行输出显示
loop l          ;如循环次数不为0则返回到l处,重复循环


mov ax,4c00h
int 21h

pause proc near  ;子程序定义(起退出缓冲作用)

jmp short exitbegin

exitstr db 0ah,0dh,'Press any key to exit...$'

exitbegin:

        push ax

        push ds

        push dx

        ;以上3条push指令的作用是保存现场

        mov ax,seg pause ;取子程序名pause所在的段地址

        mov ds,ax

        lea dx,exitstr

        mov ah,9  ;显示字符串exitstr的内容

        int 21h

        mov ah,7 ;等待输入一个[无回显]字符

        int 21h

        pop dx

        pop ds

        pop ax

        ;以上3条pop指令的作用是恢复现场

        ret

pause endp  ;子程序定义结束


nf:          ;进入子程序:显示年份。相关寄存器入栈
push ax
push bx
push di
push es

mov ax,0b800h
mov es,ax    ;设置显存段地址es
mov ax,[di]  
mov es:[bx],ax   ;年份的前半部分存入显存单元
mov ax,[di+2]
mov es:[bx+2],ax ;年份的后半部分存入显存单元

pop es    ;相关寄存器出栈
pop di
pop bx
pop ax
ret       ;结束子程序,返回主程序


show_str:    ;进入子程序:显示字符串。相关寄存器入栈
push ax
push bx
push cx
push dx
push ds
push es
push di
push si

mov ax,es
mov ds,ax    ;暂存字符串的es段改为ds段
mov ax,0b800h
mov es,ax    ;设置显存段地址es



j3:
mov cl,[si]
mov ch,0          ;[si]赋给cx,检测是否已到字符串末尾即[si]是否为0
jcxz r3           ;cx不为0就继续往下执行,为0则跳转到r3
mov al,[si]       ;[si]中的字符赋给al
mov ah,dl         ;字体颜色赋给ah
mov es:[bx+di],ax ;字符存入对应的显存单元
inc si            ;字符串内存单元后移1个
add di,2          ;显存单元后移2个
jmp j3            ;跳转到j3重复循环

r3:               ;相关寄存器出栈
pop si
pop di
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret               ;结束子程序,返回主程序


divdw:        ;进入子程序:解决除法溢出的除法运算。相关寄存器入栈
push ax
push dx
push cx
push bx
push di
push si

mov si,ax   ;存放被除数低位到si
mov ax,dx   ;被除数高位赋给ax作为被除数低位
mov dx,0    ;被除数低位为0
div cx    ;除法运算,商高位在ax中,余数高位在dx中
mov bx,ax ;bx存放商高位
mov ax,si ;被除数低位赋给ax,余数高位dx作为被除数高位
div cx     ;除法运算,余数在dx中,商低位在ax中
mov cx,dx  ;余数存放在cx中
mov dx,bx  ;商高位存放在dx中

pop si     ;相关寄存器出栈
pop di
pop bx
pop cx
pop dx
pop ax
ret        ;结束子程序,返回子程序dtoc


dtoc:   ;进入子程序:双字数值转化为字符。调用子程序divdw来解决除法溢出的问题。相关寄存器入栈
push ax
push bx
push cx
push dx
push di
push si

j2:
mov cx,10
call divdw     ;调用子程序:解决除法溢出的除法运算
add cx,30h     ;余数转为对应的字符
push cx        ;字符入栈
inc di         ;字符个数计数
mov cx,ax      ;商赋给cx
jcxz r         ;检测低位商是否为0,不为0继续往下执行,为0则跳转到r
jmp short j2   ;跳转到j2,重复循环

r:mov cx,dx
jcxz r2        ;检测高位商是否为0,不为0继续往下执行,为0则跳转到r2
jmp short j2   ;跳转到j2,重复循环


r2:            ;把栈段中的字符存放到es段中


mov cx,di      ;字符个数作为循环次数
l1:pop dx      ;字符出栈
mov es:[si],dl ;dl中的字符赋给es的内存单元
inc si         ;es的内存单元后移1位
loop l1        ;如果循环次数不为0则返回到l0处重复循环,为0则继续往下执行
mov ax,0
mov es:[si],ax ;使字符串的末尾为0

pop si         ;相关寄存器出栈
pop di
pop dx
pop cx
pop bx
pop ax
ret      ;结束子程序,返回主程序


dtocz:   ;进入子程序:字数值转化为字符。相关寄存器入栈
push ax
push bx
push cx
push dx
push di
push si

j1:
mov bx,10
div bx         ;除法运算,商存放在ax中,余数存放在dx中
add dx,30h     ;余数转为对应的字符
push dx        ;字符入栈
inc di         ;字符个数计数
mov cx,ax      ;商赋给cx
jcxz r1        ;检测商是否为0,不为0继续往下执行,为0则跳转到r1
jmp short j1   ;跳转到j1,重复循环

r1:            ;把栈段中的字符存放到es段中
mov cx,di      ;字符个数作为循环次数
l0:pop dx      ;字符出栈
mov es:[si],dl ;dl中的字符赋给es的内存单元
inc si         ;es的内存单元后移1位
loop l0        ;如果循环次数不为0则返回到l0处重复循环,为0则继续往下执行
mov ax,0
mov es:[si],ax ;使字符串的末尾为0

pop si         ;相关寄存器出栈
pop di
pop dx
pop cx
pop bx
pop ax
ret      ;结束子程序,返回主程序


pj:      ;进入子程序:计算平均资产。相关寄存器入栈
push ax
push cx
push dx
push di
push si


mov cx,21
lp:
push cx
mov cx,168[si] ;将除数赋给cx
jcxz r0         ;如果除数为0,跳转到r0处
mov ax,84[di]  ;被除数低位赋给ax;
mov dx,84[di+2];被除数高位赋给dx;
div cx        ;除法运算,平均资产存放在ax中
mov 210[si],ax ;平均资产从ax存放到平均资产的内存单元
add di,4     ;总资产的内存单元后移4个
add si,2     ;存放平均资产的内存单元后移2个

pop cx
loop lp          ;跳转到lp处重复循环
r0:           ;相关寄存器出栈


pop si
pop di
pop dx
pop cx
pop ax
ret          ;结束子程序,返回主程序


code ends
end start

汇编
[解决办法]



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

; 数据段
data segment

        db '1975','1976','1977','1978','1979','1980','1981','1982','1983';第一个偏移值:0

        db '1984','1985','1986','1987','1988','1989','1990','1991','1992'

        db '1993','1994','1995';最后一个偏移值:53H(83D)

        ;以上是表示21年的21个字符串

 

        dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514;第一个偏移值:54H(84D)

        dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000;最后一个偏移值:A7H(167D)

        ;以上是表示21年公司总收入的21个dword型数据

 

        dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226;第一个偏移值是:A8H(168D)

        dw 11542,14430,15257,17800;最后一个偏移值是:D2(210D)

        ;以上是表示21年公司雇员人数的21个word型数据

dw 64 dup(?); 因为最后转换成字符串的空间不够,所以需要再多申请这么多空间

data ends



; 栈段
stack segment

db 256 dup (?)

stack ends


; 计算后的数据存放段
table segment

        db 21 dup ('year sunm ne ?? ')

dw 32 dup(?); 因为最后转换成字符串的空间不够,所以需要再多申请这么多空间

table ends



; 代码段
code segment

 start:

; 设置栈段地址
mov ax , stack
mov ss , ax
mov sp , 256



; 计算数据段中的数据(计算人均收入,也就是收入除以雇员数)


mov ax , data
mov ds , ax
mov bx , 0
call Calculate




; 循环,将双字节的数据的整型数转换成字符型
mov bx , 0
mov si , 0; 设置字符串输出的首地址
mov cx , 21; 设置循环次数
 again: 
; 复制年份到ds段
mov ax , es:[ bx ]
mov ds:[ si ] , ax
mov ax , es:[ bx + 2 ]
mov ds:[ si + 2 ] , ax
mov word ptr ds:[ si + 4 ] , 0; 存入0,表示间隔
add bx , 5; 因为只需跳过一个空格,空格占一个字节,再加上前面读取的4个字节
add si , 6


mov ax , es:[ bx ]; 获取公司总收入的dword型数据的低位
mov dx , es:[ bx + 2 ];; 获取公司总收入的dword型数据的高位
call dtoc; 调用子程序,将dword型数据转换成数字符串型,放到ds:si指定的位置
add bx , 5; 跳过已经读取的字节再加上一个空格
inc si; 如果去掉这个自增1,则存放他们之间的间隔0为一个字节,没有去掉这个自增就为一个字

mov ax , es:[ bx ]; 获取公司雇员人数的word型数据
mov dx , 0; 因为是word型数据,所以高位不需要,为0
call dtoc; 调用子程序,将dword型数据转换成数字符串型,放到ds:si指定的位置
add bx , 3; 跳过已经读取的字节再加上一个空格
inc si; 如果去掉这个自增1,则存放他们之间的间隔0为一个字节,没有去掉这个自增就为一个字

mov ax , es:[ bx ]; 获取公司雇员的人均收入
mov dx , 0; 因为是word型数据,所以高位不需要,为0
call dtoc; 调用子程序,将dword型数据转换成数字符串型,放到ds:si指定的位置
add bx , 3; 跳过已经读取的字节再加上一个空格
inc si; 如果去掉这个自增1,则存放他们之间的间隔0为一个字节,没有去掉这个自增就为一个字

  loop again



; 循环,用于显示所有的字符串
mov dh , 0; 第0行
mov dl , 2; 第2列
mov si , 0; 设置指向字符串首地址偏移量
mov cx , 22; 外循环共循环21次(因为第一次为了直接跳到循环处而一次性执行循环,所以需要多加一次循环)

jmp show_start; 跳到开始出,以便能直接使用loop循环一次性输出

 show_again1:

push cx; 现场保护
mov cx , 4; 内循环共循环4次

   show_again2:

push cx; 因为CX还要用来设置颜色,所以还要保护
mov ch , 0
mov cl , 01h; 设置颜色(蓝色)
call show_str
add si , 2; 加上间隔符,进行下一组字符串的输出
add dl , 10; 每输出一串字符列就空开几列
pop cx; 恢复内循环的循环次数值

   loop show_again2

add dh , 1; 输出完一组就要换下一行
mov dl , 2; 输出完一组列就要归位
pop cx; 恢复外循环的循环次数值
show_start:
 loop show_again1



; 结束程序


mov ax , 4c00h
int 21h



; 子程序
; 计算数据段中的数据(计算平均收入,也就是收入除以雇员数)
; 入口参数:ds:bx为待计算的数据段
; 出口参数:计算好后的数据将放在table数据段中,也可以用 es:0 来直接获取这个table数据段的首地址
Calculate:

mov ax , table
mov es , ax;设置要写入数据的段寄存器

mov bp , 0;设置table段的偏移地址

mov di , 0;设置雇员数每次增加的值(因为与其他值相比只有雇员数是2字节,所以要做特殊处理)

mov cx , 21;设置循环的值为21次

again_1:

mov ax , ds:[bx];复制年份的低字
mov es:[bp] , ax

mov ax , ds:[bx + 2];复制年份的高字
mov es:[bp + 2] , ax



mov ax , ds:0A8H.[di];复制雇员数(十进制168)

mov es:[bp + 0AH] , ax;加10是因为复制了2个字节,而且写入的时候要输入一个空格,
;并且这个位置是在复制了4个字节后的再加5个字节的位置


mov ax , ds:54H.[bx];复制收入的低字节(十进制84)
mov es:[bp + 5] , ax

mov dx , ds:54H.[bx + 2];复制收入的高字节(十进制84)
mov es:[bp + 7] , dx



div word ptr ds:0A8H.[di];因为除数是2个字节(16位)的,所以也用di(di每次循环增加2)

mov es:[bp + 0DH] , ax;复制算好的人均收入



add bp , 16;进行下一组写入

add di , 2;用于计算雇员数的值加2,因为他一次值复制了2个字节

add bx , 4;复制了一次后要加上4字节,以便下一次读出和计算

loop again_1

  ret; Calculate子程序返回




;参数: (ax) = dword型数据的低16位
;      (dx) = dword型数据的高16位
;      (cx) = 除数
;返回: (dx) = 结果的高16位
;      (ax) = 结果的低16位
;      (cx) = 余数
divdw:  ;子程序定义开始

        push ax

        mov ax,dx

        mov dx,0

        div cx

        mov bx,ax

        pop ax

        div cx

        mov cx,dx

        mov dx,bx

        ret   ;子程序定义结束




; 功能:将dword型数据转变为表示十进制数的字符,字符以0为结尾符
; 参数:(ax) = dword型数据的低16位
;      (dx) = dword型数据的高16位


;      ds:si指向字符串的首地址
; 返回:无
  dtoc: push cx; 保护现场
push bx; 保护现场
push si; 保存用来输出结果的字符串的首地址

mov byte ptr ds:[ si ] , 0;先写入结束符(写在首位,以便配合栈结构)

inc si

mov cx , 1

again_3:mov cx , 10; 做除数,每次都除以10,以便得到余数,从而求出每个位的值

call divdw; 调用除法子程序,因为这个除法子程序可以防止数据溢出

add cl , 30H;加上30H成为字符的ASCII码

mov ds:[ si ] , cl;存入到指定的数据段


; 判断商的低位和高位是否都为0如果都为0,则表示结束
mov cx , 0
or cx , ax
or cx , dx

jcxz ok1

inc si
inc cx;因为loop会将cx减少1,所以要先加1

loop again_3

 ok1:   mov byte ptr ds:[ si + 1 ] , 0;尾部也写入结束符

pop si; 取出用来输出结果的首地址
mov bx , si; bx用来做最后的数据存入,所以也是为输出结果的首地址

mov al , ds:[ si ];从得到的第一个数据开始遍历
mov ah , 0;只要低位,高位归零

push ax;结束符先入栈

;把得出的字符倒过来,因为求余得到的数是倒着的
again_4:inc si

mov al , ds:[ si ]
mov ah , 0;只要低位,高位归零

mov cx , ax

jcxz ok2;已经全部临时存入栈中

push ax

inc cx;因为loop会将cx减少1,所以要先加1

loop again_4

  ok2:  pop cx;从栈中取出,正好实现倒叙

mov ds:[ bx ] , cl;只要低位,高位归零

jcxz return

inc bx
inc cx;因为loop会将cx减少1,所以要先加1

loop ok2

return: pop bx; 恢复现场
pop cx; 恢复现场
ret; 返回




;子程序
;功能:在指定的位置 , 用指定的颜色 , 显示一个用0结束的字符串
;参数:(dh) = 行号(取值范围0~24) , (dl) = 列号(取值范围0~79)
;     (cl) = 颜色 , ds:si指向字符串首地址
show_str:

push dx; 现场保护

mov ax , 000ah;转换成行需要乘以a(10)
mul dh;计算写入位置的行
add ax , 0b800h;B800是起始位置,所以加上规定的行数


add dl , dl;计算写入位置的列
mov dh , 0


mov es , ax;设置写入位置的寄存器
mov di , dx

mov al , cl;保存颜色


mov cx , 2
jmp first_start
   again_2:

mov cl , ds:[si];获取要显示的数据
mov ch , 0;不需要高位
jcxz ok;如果是0则返回(结束这个子程序)

mov es:[di] , cl;将字符写入


mov es:[di + 1] , al;写入颜色
add di , 2;指向下一个要写入的位置
inc si;指向下一个要读取字符的位置
mov cl , 2;again循环每次都成立
first_start:
   loop again_2
ok:pop dx; 恢复
ret;返回(结束)这个子程序


code ends


end start




只能告诉楼主大体错误:在dtoc子程序异常退出,退出原因则是因为多次子程序调用而导致的栈结构溢出而且发生程序强制结束(如果把栈定义的够大就会发现无效指令的错误)
附上我以前写的代码,因为在公司,所以没有时间修改代码,而且代码风格较乱,我大学时期写了一本汇编语言编程规范,因为毕业了所以就没时间写下去,代码规范也很重要

.

热点排行