1.显示字符串
思想:字有点难看但是这是我编写这个程序时的基础思想
;time:2022/3/16 name:feng
;刚开始编写的时候头脑有点不清晰,导致没有思路也是因为跳转指令太难理解搞得我很烦躁,经过一天后重新思考这个问题很其实很简单,
;我感觉最难的是确定显存的起始地址,因为刚开始我是想直接将行设为b8000+460h的后来才发现这个是不能实现的只能将其放在偏移地址中存放
;我感觉我编写的这个程序最好的地方是我没有定义cx的值,直接用跳转指令实现控制。其实刚开始我受到loop指令的思想想为cx设置循环值,但是后面
;想到如果这样写还没我写的9_2.asm写的好,后面看到书本上的指令结合方式想到不需要去理会cx的值,因为只有当将字符串0存入cl时才会导致循环结束
assume cs:code
data segment
db 'Welcome to masm!',0
data ends
code segment
start: mov dh,08h ;行号
mov dl,03h ;列号
mov cl,2 ;颜色
mov ax,data
mov ds,ax
mov si,0
call show_str ;相当于push ip,jmp near ptr 标号
mov ax,4c00h
int 21h
show_str:
sub dh,1h ;显存为25*80,所以要算显存起始地址b800+(8-1)*160+4
mov al,dh ;将行号存入al等待乘法使用
mov bl,0a0h ;注意字母前面一定要加0,一直忘记,0a0h=160d
mul bl
mov di,ax ;之所以将后面的部分存入di是因为不能存入es中会发生错误:b800+460h,而我想要的是b8000+460h
add di,4h
mov ax,0b800h
mov es,ax
mov ah,cl ;将颜色存入ah
s: mov cl,[si] ;将字符存入cl
mov ch,0
jcxz ok ;判断字符是否为0,因为字符串以0为结束
mov al,cl
mov es:[di],ax
inc si
add di,2
jmp short s ;跳转到s继续执行,直到字符是0时结束
ok: ret
code ends
end start
2.解决除法溢出的问题
;time:2022/3/18 name:feng
;这个程序真的不是很难,但是它困扰了我两三天。原因是书本上提供的公式有点简略,导致我需要将数值带进公式去检查一下
;并且去理解一下这个公式的原理,比如按我理解的是dx存放的是溢出为的值,ax和cx存放的是公式后半部分的除法的值(32/16)
;但是书本上却没有讲的很清楚。然而当我理解并写出这个程序时却真的很开心,虽然这个程序简单,但是却让我意识到所有的编程
;其实难的不是如何去编程,而是思想,一旦有了想法,写出程序的方法有千万种。
assume cs:code,ss:stack
stack segment ;创建栈来存储会受影响的值
dw 8 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,10h
mov ax,4240h ;被除数的低16位
mov dx,000fh ;被除数的高16位(H)
mov cx,0ah ;除数(N)
call divdw
mov ax,4c00h
int 21h
divdw: push ax ;将被除数的低16位(L)压栈,因为公式的后半部分需要使用,而在计算前半部分时ax的值会变。
mov ax,dx ;将被除数的高16位赋给ax计算公式的前半部分。
div cl ;al存放商,也就是溢出位的值;ah存放余数后面需要使用(rem(H/N))
mov dl,ah ;将余数赋给dl后面需要使用,当作被除数的高16位
mov bl,al ;将商赋给bl后面将其压栈,因为后面需要将栈中存放的ax取出,会影响所以用bl暂时存储
pop ax ;ax=4240H
push bx
div cx
mov cx,dx ;cx存放余数
pop dx
ret
code ends
end start
3.数值显示
;time:2022/3/17 name:feng
;在做这个实验前我刚开始是想使用栈将data段中的数据逆序重新放置的,因为在这个程序中我使用了前面所写的show_str,
;但是后面想到栈存取是以字为单位,而我存储在data中的是以字节为单位的
;最终放弃逆序的想法,使用从后往前将数据存入显存中的方法,然后将show_str微调使其能够将数字在屏幕上显示,
;在编写这个程序中这个问题是困扰我许久的问题,编写dtoc其实很简单,但是想办法
;使其如何在屏幕上按正常的顺序显示是真的有一点困难。但是解决这个问题之后是真的很舒服。
;注:最后在写注释的时候show_str中的第一个jcxz我忘记了将其删除,应该是我编写的时候没注意到它会影响程序的运行结果
;后面发现如果我要将以0为尾数的数值显示出来那么这个jcxz将会提前导致程序的结束,果然编写注释是真的好,既能加强理解也能检查错误
assume cs:code
data segment
db 10 dup (0)
data ends
code segment
start: mov ax,317ah ;存放12666这个数
mov bx,data
mov ds,bx
mov si,0 ;注意这个si在这个程序中至关重要
call dtoc
mov dh,8h ;行号
mov dl,3h ;列号
mov cl,2h ;颜色
call show_str
mov ax,4c00h
int 21h
dtoc: mov bx,0ah ;将除数放在bx中 (之所以使用32/16位的除法是因为第一次做除法得到的商为1266>255,不能使用八位的位置存储)
mov dx,0 ;被除数的最高位
div bx
mov cx,ax ;将商赋给cx来判断算法是否结束
add dx,30h ;数字+30H正好对应数字的ASCII值
mov [si],dl ;将所得余数存放在data段中
inc si
jcxz okk ;判断时候结束
jmp short dtoc ;如果没结束就继续
okk: ret
show_str:
sub dh,1h ;这个show_str与10_1.asm所编写的相差不打,所以只注释微调的指令
mov al,dh
mov bl,0a0h
mul bl
mov di,ax
add di,4h
mov ax,0b800h
mov es,ax
mov ah,cl
s: mov cl,-1[si] ;-1[si]=[-1+si],注意汇编语言中不能[si-1],之所以这样是因为所要显示的字符是逆序放置的如‘66621’而我要显示的是‘12666’
mov ch,0
;jcxz ok 之所以将这行放到下面的原因是我已经不是从前往后存入了,而且如果数字末尾是0的话如果这行不删除将无法运行
mov al,cl
mov es:[di],ax
sub si,1
mov cx,si
jcxz ok ;以si是否为0来判断是否结束(注意jcxz只能判断cx的值我这里写si是更好理解结束的位置)
add di,2
jmp short s
ok: ret
code ends
end start
总结
我是自学的,如果写的不好请指出。我个人感觉这三个实验真的不是很难,最难的是思想而不是编程,我在编写第二个程序的时候真的没有搞懂那个公式,导致最简单的程序反而是最后写出来的。这个是我的个人理解,可能你们比我更聪明觉得那个公式很简单,感觉写的还行的点个赞吧。(我的字很难看我知道,勿喷)