汇编语言学习笔记-5

0x00 直接定址表

 如何有效合理的组织数据,以及相关编程。

  • 地址标号:在后面加:,只能在代码段中使用,不能在其他段中使用。

  • 数据标号:后面没有:,表示内存单元的地址与内存单元的长度。

 下面给出两个标号的例子,都是将a段中每个数据相加,并放到b中。

  • 地址标号的程序如下:

image-20230306145525766

  • 数据标号的程序如下:

image-20230306145726854

 可以看到,数据标号的程序更类似于C语言中数组的用法,而数据标号看上去更简洁。

Q&A

image-20230306150043325

1
2
3
4
5
6
s:
mov ax, a[si]
add word ptr b[0], ax
adc word ptr b[2], 0
add si, 2
loop s

 数据标号可以用到其他段中(但是要将所在段与段寄存器联系起来,否则编译器在编译时无法确定标号的段地址在哪一个寄存器中),数据标号可以描述存储数据的单元的地址和长度

 也可以将标号当作数据来定义,如下所示:

image-20230306150846583

image-20230306150952509

注:offset a指的是取a的偏移地址,seg a指的是取a的段地址。

Q&A

image-20230306151103669

1
2
3
start:
mov ax, data
mov ds, ax

image-20230306151204012

分析:byte型数据为1F(8位),那么在屏幕中间就输出字符串”1F”。字符转换关系如下:

image-20230306151633708

 可以看到,转换关系不一致,如何实现这种转换,就要用查找表(可以在两个集合之间建立映射关系)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
assume cs:code, ds:data
data segment
table db "0123456789ABCDEF"
data ends

code segment
start:
; 初始化data段
mov ax, data
mov ds, ax

s0:
; 获取键盘输入,如果是Esc则退出
in al, 60H
cmp al, 1H
je showend

; 转换成字符
mov ah, al
mov cl, 4
shr ah, cl
mov bl, ah
mov bh, 0
mov ah, table[bx] ; 注意table[xx]中的xx要求是1个字
and al, 00001111B
mov bl, al
mov al, table[bx]

; 展示在屏幕中间
mov bx, 0B800H
mov es, bx
mov es:[160*12+40*2], ah
mov es:[160*12+40*2+2], al ; +1 表示改变颜色

loop s0

showend:
; 程序结束
mov ax, 4c00H
int 21H
code ends
end start

Q&A

image-20230306160245684

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
assume cs:code, ds:data, ss:stack
data segment
table dw offset ag0, offset ag30, offset ag60, offset ag90, offset ag120, offset ag150, offset ag180
ag0 db '0', 0
ag30 db '0.5', 0
ag60 db '0.866', 0
ag90 db '1', 0
ag120 db '0.866', 0
ag150 db '0.5', 0
ag180 db '0', 0
strNotLegal db 'not legal', 0
strLegal db 'legal', 0
table2 db "0123456789ABCDEF", 0
data ends

stack segment
db 128 dup(0)
stack ends

code segment
; 判断输入bx是否是30的倍数且在[0,180]之间,如果不是返回bx=0FFFFH
legalDetect:
push ax
push cx
push dx

mov dx, 0000H
mov ax, bx
and ax, 00FFH
cmp ax, 180
ja short notLegal
mov cx, 52
div cx
cmp dx, 0
je short legalEnd
notLegal:
mov bx, 00FFH
legalEnd:
and bx, 00FFH
pop dx
pop cx
pop ax
ret

; 输入函数
input:
push ax
push dx
push cx

mov bl, 0 ; 最后结果
mov ah, 0
mov dl, 10
mov cx, 0 ; 循环次数
s0:
in al, 60H
cmp al, 80H ; 是否是断码
jae short s0

cmp al, 1H ; 输入结束符Esc
je short inputEsc

sub al, 1 ; 输入0-9,其扫描码分别为0B,2,3,4,5,6,7,8,9,0A
cmp al, 0AH
jne inputNotEqual0
mov al, 0H
inputNotEqual0:
mov bh, al ; 结果=结果*10+当前输入
mov al, bl
mul dl
mov bl, al
add bl, bh

inc cx
jmp short s0
inputEsc:
cmp cx, 0
je short inputFirstEsc
jmp short inputLatterEsc
inputFirstEsc:
jmp short s0
inputLatterEsc:
call legalDetect
jmp short inputEnd
inputEnd:
pop cx
pop dx
pop ax
ret

; 将bx的值展示到屏幕上
;----------------------------- show ------------------------------
show:
push ax
push cx
push si
push di

mov cl, 0
mov si, 6
s1:
; 拿出bx的每4位
mov ax, bx
shr ax, cl
add cl, 4
and ax, 000FH
mov di, ax
mov al, table2[di]
mov ch, al

; 放到屏幕中央
mov ax, 0b800H
mov es, ax
mov es:[160*12+40*2+si], ch
sub si, 2

cmp cl, 10H
je short showEnd
jmp short s1
showEnd:
pop di
pop si
pop cx
pop ax
ret
;----------------------------- show ------------------------------
;----------------------------- main ------------------------------
start:
; 初始化data段
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 128

; 进行输入,输入结果放入到bx中,如果输入不对,那么bx=0FFFFH
call input

; 将bx值输出到屏幕中央
call show

; 程序返回
mov ax, 4c00H
int 21H
;----------------------------- main ------------------------------
code ends
end start

 上述程序是有问题的。上述程序想要实现的是:输入一个字符串,字符串是一个角度,然后输出角度对应的sin值。如果用in al, 60H,同一时间,60H端口只保留一个值,如果没有及时取出这个值,那么就会被之后输入的字符所覆盖,那我们就要与empty_cycle结合,且等时间间隔输入字符。因此,几乎不可能使用in al, 60H进行字符串输入。

image-20230307182611984

image-20230307183735624

 注:第(4)条这应该是向下滚动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
assume cs:code
code segment
;---------------------- 子程序1 -------------------------
sub1:
push ax
push cx
push es
push si
mov ax, 0B800H
mov es, ax
mov cx, 2000
mov si, 0
sub1s:
mov byte ptr es:[si], ' '
add si, 2
loop sub1s
pop si
pop es
pop cx
pop ax
ret
;---------------------- 子程序1 -------------------------
;---------------------- 子程序2 -------------------------
sub2:
push ax
push bx
push cx
push es
push si

mov bl, al ; 保存颜色值到bl中
mov ax, 0B800H
mov es, ax
mov cx, 2000
mov si, 0
sub2s:
and byte ptr es:[si+1], 11111000B
or es:[si+1], bl
add si, 2
loop sub2s
pop si
pop es
pop cx
pop bx
pop ax
ret
;---------------------- 子程序2 -------------------------
;---------------------- 子程序3 -------------------------
sub3:
push ax
push bx
push cx
push es
push si

mov bl, al ; 保存颜色值到bl中
shl bl, 1
shl bl, 1
shl bl, 1
shl bl, 1
mov ax, 0B800H
mov es, ax
mov cx, 2000
mov si, 0
sub3s:
and byte ptr es:[si+1], 10001111B
or es:[si+1], bl
add si, 2
loop sub3s
pop si
pop es
pop cx
pop bx
pop ax
ret
;---------------------- 子程序3 -------------------------
;---------------------- 子程序4 -------------------------
sub4:
push ax
push cx
push si
push di
push ds
push es
; ds:[si]->es:[di]
mov ax, 0B800H
mov es, ax
mov ds, ax
mov cx, 23
mov si, 160
mov di, 0
cld
sub4s1:
push cx
mov cx, 160
rep movsb ; 不用刻意改变si与di的值,因为它们会自动变化
pop cx
loop sub4s1

; 清空第24行为空格
mov cx, 80
mov si, 0
sub4s2:
mov byte ptr es:[160*24+si], ' '
add si, 2
loop sub4s2
pop es
pop ds
pop di
pop si
pop cx
pop ax
ret
;---------------------- 子程序4 -------------------------
setScreen:
jmp short set
table dw sub1, sub2, sub3, sub4
set:
push bx
cmp ah, 3
ja sret
; 根据功能号ah调用对应的子程序
mov bl, ah
mov bh, 00H
add bx, bx
call word ptr table[bx]
sret:
pop bx
ret

start:
mov ah, 0 ; 功能号
mov al, 0 ; 颜色值
call setScreen

; 程序结束
mov ax, 4c00H
int 21H
code ends
end start

0x01 使用BIOS进行键盘输入和磁盘读写

 之前说过,BIOS提供9号中断,当键盘发生输入时,会引起9号中断。CPU在9号中断发生后,执行int 9中断例程,此中断例程会从60h端口读出扫描码,并转为相应的ASCII码,并存储在内存的指定空间(键盘缓冲区,可存储16个字,可以存储15个按键的ASCII码与扫描码)中

 对于程序员而言,BIOS提供了int 16h中断例程。int 16h中断例程中包含的一个最重要的功能是从键盘缓冲区中读取一个键盘输入,该功能的编号为0。使用说明:

1
2
3
4
5
mov ah,0
int 16h
结果:
(ah)=扫描码
(al)=ASCII码

int 16h的0号功能具体进行下列操作:

(1)检测键盘缓冲区中是否有数据;

(2)没有则继续做第1 步;

(3)读取缓冲区第一个字单元中的键盘输入;

(4)将读取的扫描码送入ah,ASCII 码送入al;

(5)将己读取的键盘输入从缓冲区中删除。

 可见,BIOS 的int 9中断例程和int 16h中断例程是一对相互配合的程序,int 9中断例程向键盘缓冲区中写入,int 16h中断例程从缓冲区中读出它们写入和读出的时机不同,int 9中断例程(使用了in al, 60H)在有键按下的时候向键盘缓冲区中写入数据,而int 16h中断例程是在应用程序对其进行调用的时候,将数据从键盘缓冲区中读出。

Q&A

image-20230307195357797

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
assume cs:code
code segment

show:
push ax
push bx
push es
mov bl, al
mov ax, 0B800H
mov es, ax
mov es:[160*12+40*2], bl ; 在屏幕上显示字符

and byte ptr es:[160*12+40*2+1], 11111000B
cmp bl, 'r'
je short show1
cmp bl, 'b'
je short show2
cmp bl, 'g'
je short show3 ; 更改相应的颜色
showr:
pop es
pop bx
pop ax
ret
show1:
or byte ptr es:[160*12+40*2+1], 00000111B
jmp short showr
show2:
or byte ptr es:[160*12+40*2+1], 00000110B
jmp short showr
show3:
or byte ptr es:[160*12+40*2+1], 00000101B
jmp short showr

inp:
push ax
inps:
mov ah, 0
int 16H
cmp ah, 1 ; 如果是Esc,则退出
je short inpr
call show
jmp short inps
inpr:
pop ax
ret

start:
call inp
mov ax, 4c00H
int 21H
code ends
end start

0x02 键盘字符串输入

image-20230307201910118

image-20230307201933114

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
assume cs:code, ds:data

data segment
db 128 dup(0)
data ends

code segment

show:
push ax
push bx
push cx
push dx
push si
push di
push es
push ds
mov cx, 128
mov ax, 0B800H
mov es, ax
mov si, dx
mov di, 0
mov bx, 0
shows0:
mov al, ds:[di]
cmp al, 0H
jne short shows1
mov al, 20H
shows1:
mov es:[si+bx], al
inc di
add bx, 2
loop shows0
pop ds
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret

inp:
push ax
push si
push ds
inps0:
mov ah, 0 ; 读取一个字符
int 16H
cmp al, 0DH ; 输入的是回车键
je short inpr
cmp al, 08H ; 输入的是删除键
je short delc
mov byte ptr ds:[si], al ; 输入的是其他字符
inc si
inps1:
call show
jmp short inps0
inpr:
pop ds
pop si
pop ax
ret
delc:
cmp si, 0
je short delcs0
dec si
mov byte ptr ds:[si], 0
delcs0:
jmp short inps1

start:
mov dh, 12 ; 字符串在第12行,40列
mov dl, 40
mov al, 160
mul dh
mov dh, 0
add dl, dl
add ax, dx
mov dx, ax
mov ax, data ; 初始化data段
mov ds, ax
mov si, 0
call inp ; 调用子程序
mov ax, 4c00H
int 21H
code ends
end start

留言

2023-03-06

© 2024 wd-z711

⬆︎TOP