代码语言:javascript复制
1 assume cs:code,ss:stack
2 stack segment
3 db 128 dup (0)
4 stack ends
5 code segment
6 start:
7 mov ax,stack
8 mov ss,ax
9 mov sp,128
10
11 call copy_boot
12
13 ;设置CS:IP为0:7e00h
14 mov ax,0
15 push ax
16 mov ax,7e00h
17 push ax
18 retf
19
20 mov ax,4c00h
21 int 21h
22 ;org 7e00h
23 ;引导程序
24 boot:
25 jmp boot_begin
26 func0 db 'Hk_Mayfly----XIUXIUXIU~',0
27 func1 db '1) reset pc',0
28 func2 db '2) start system',0
29 func3 db '3) clock',0
30 func4 db '4) set clock',0
31 ;相减得到的是标号的相对位置, 7e00h得到的绝对位置
32 func_pos dw offset func0-offset boot 7e00h
33 dw offset func1-offset boot 7e00h
34 dw offset func2-offset boot 7e00h
35 dw offset func3-offset boot 7e00h
36 dw offset func4-offset boot 7e00h
37 time db 'YY/MM/DD hh:mm:ss',0
38 cmos db 9,8,7,4,2,0
39 clock1 db 'F1----change the color ESC----return menu',0
40 clock2 db 'Please input Date and Time,(YY MM DD hh mm ss):',0
41 change db 12 dup (0),0
42
43 boot_begin:
44 call init_boot
45 call cls_screen
46 call show_menu
47 jmp choose
48 mov ax,4c00h
49 int 21h
50
51 choose:
52 call clear_kb_buffer
53 ;获取我们输入的操作,跳转到对于函数
54 mov ah,0
55 int 16h
56 cmp al,'1'
57 je choose_func1
58 cmp al,'2'
59 je choose_func2
60 cmp al,'3'
61 je choose_func3
62 cmp al,'4'
63 je choose_func4
64
65 jmp choose
66
67 ;在题中提到了,开机后进入到ffff:0处执行指令
68 ;那我们也可以把重启理解为,跳转到ffff:0执行指令
69 ;所以我们利用jmp dword跳转到ffff:0地址,模拟重启
70 choose_func1:
71 mov bx,0ffffh
72 push bx
73 mov bx,0
74 push bx
75 retf
76
77 jmp choose
78
79 ;题中对引导现有的操作系统的描述是调用int 19,这里为了方便就直接写成函数了
80 choose_func2:
81 mov bx,0
82 mov es,bx
83 mov bx,7c00h
84
85 mov al,1;扇区数
86 mov ch,0
87 mov cl,1;扇区
88 mov dl,0
89 mov dh,0
90 mov ah,2;读取
91 int 13h
92
93 mov bx,0
94 push bx
95 mov bx,7c00h
96 push bx
97 retf
98
99 jmp choose
100
101 ;获取时间
102 choose_func3:
103 call show_time
104
105 jmp choose
106
107 show_time:
108 call init_boot
109 call cls_screen
110 ;显示按键信息
111 mov si,offset clock1-offset boot 7e00h
112 mov di,160*14 10*2;在14行10列显示
113 call show_line
114 show_time_start:
115 ;获取时间信息,并显示(将time中的未知字符替换为当前时间)
116 call get_time_info
117 mov di,160*10 30*2;屏幕显示的偏移地址
118 mov si,offset time-offset boot 7e00h;time标号的偏移地址
119 call show_line
120
121 ;获取键盘缓存区的数据
122 mov ah,1
123 int 16h
124 ;没有数据就跳回show_time_start
125 jz show_time_start
126 ;判断是否按下F1
127 cmp ah,3bh
128 je change_color
129 ;判断是否按下ESC
130 cmp ah,1
131 je Return_Main
132 ;有数据,但是是无用的键盘中断,清除
133 cmp al,0
134 jne clear_kb_buffer2
135 ;返回开始,重复之前的操作,达到刷新时间的效果。
136 jmp show_time_start
137
138 change_color:
139 call change_color_show
140 clear_kb_buffer2:
141 call clear_kb_buffer
142 jmp show_time_start
143 Return_Main:
144 ;返回到开始,重新打印菜单
145 jmp boot_begin
146 ret
147
148 choose_func4:
149 call set_time
150 jmp boot_begin
151
152 set_time:
153 call init_boot
154 call cls_screen
155 call clear_stack
156
157 ;设置提示信息显示位置
158 mov di,160*10 13*2
159 mov si,offset clock2-offset boot 7e00h
160 call show_line
161 ;显示修改后change中的内容
162 mov di,160*12 26*2
163 mov si,offset change-offset boot 7e00h
164 call show_line
165
166 call get_string
167
168 get_string:
169 mov si,offset change - offset boot 07e00H
170 mov bx,0
171 getstring:
172 ;获取键盘输入的时间信息
173 mov ah,0
174 int 16h
175
176 ;输入的时间为数字0~9
177 cmp al,'0'
178 jb error_input
179 cmp al,'9'
180 ja error_input
181 ;将我们输入的时间字符入栈
182 call char_push
183 ;不能超过输入的数量
184 cmp bx,12
185 ja press_ENTER
186 mov di,160*12 26*2
187 call show_line
188 jmp getstring
189 error_input:
190 ;判断是不是按下退格或回车键
191 cmp ah,0eh
192 je press_BS
193 cmp ah,1ch
194 je press_ENTER
195
196 jmp getstring
197 ;按下回车
198 press_BS:
199 call char_pop
200 mov di,160*12 26*2
201 call show_line
202 jmp getstring
203 ;按下enter就退出
204 press_ENTER:
205 ret
206
207 char_push:
208 ;只能最多输入12个梳子
209 cmp bx,12
210 ja char_push_end
211 ;将数值移动到对应位置
212 mov ds:[si bx],al
213 inc bx;表示我们输入了多少个字符
214 char_push_end:
215 ret
216
217 char_pop:
218 ;判断是否输入了设置时间的数值,没有就相当于删完了
219 cmp bx,0
220 je char_pop_end
221 ;否则用星号替换,相当于删除
222 dec bx
223 mov byte ptr ds:[si bx],'*'
224 char_pop_end:
225 ret
226
227 clear_stack:
228 push bx
229 push cx
230
231 mov bx,offset change-offset boot 7e00h
232 mov cx,12
233 cls_stack:
234 ;替换change段中内容
235 mov byte ptr ds:[bx],'*'
236 inc bx
237 loop cls_stack
238
239 pop cx
240 pop bx
241 ret
242
243
244 ;获取时间
245 get_time_info:
246 ;从cmos ram获取年月日,时分秒6个数据
247 mov cx,6
248 ;获取存放单元地址
249 mov bx,offset cmos - offset boot 7e00H
250 ;通过替换来显示
251 mov si,offset time - offset boot 7e00H
252 next_point:
253 push cx
254 ;获取单元号
255 mov al,ds:[bx]
256 ;向70h端口写入要访问的单元地址,并从71h端口读取数据
257 out 70H,al
258 in al,71H
259 ;右移4位获取十位
260 mov ah,al
261 mov cl,4
262 shr al,cl
263 and ah,00001111b
264 ;将BCD码转换为ASCII码
265 add ax,3030H
266 ;写入time中
267 mov word ptr ds:[si],ax
268 ;下一单元号
269 inc bx
270 ;每个数据之间距离都是3
271 add si,3
272 pop cx
273 loop next_point
274 ret
275
276 ;改变颜色
277 change_color_show:
278 push bx
279 push cx
280
281 mov cx,2000
282 mov bx,1
283 next:
284 ;属性值 1,改变颜色
285 add byte ptr es:[bx],1
286 ;当超出字体颜色的数值(0~111h)时,将数值重置
287 cmp byte ptr es:[bx],00001000b
288 jne change_end
289 ;因为背景是黑色,所以文字颜色就不设置成黑色了
290 mov byte ptr es:[bx],1
291 change_end:
292 add bx,2
293 loop next
294
295 pop cx
296 pop bx
297 ret
298
299 clear_kb_buffer:
300 ;1号程序,用来检测键盘缓冲区是否有数据
301 ;如果有的话ZF!=0,没有,ZF=0
302 mov ah,1
303 int 16h
304 ;通过ZF判断减缓缓冲区是否有数据,没有就跳出
305 jz clear_kb_bf_end
306 mov ah,0
307 int 16h
308 jmp clear_kb_buffer
309 clear_kb_bf_end:
310 ret
311
312 init_boot:
313 ;基本设置,注意:程序的直接定址表默认段地址是CS
314 ;当程序转移到7c00h时,代码中CS值未发生改变,
315 ;所以需要我们指明段地址
316 mov bx,0b800h
317 mov es,bx
318 mov bx,0
319 mov ds,bx
320 ret
321
322 ;清屏
323 cls_screen:
324 mov bx,0
325 mov cx,2000
326 mov dl,' '
327 mov dh,2;字体为绿色,不设置的话,在我们显示菜单时,字体和背景颜色相同
328 s: mov es:[bx],dx
329 add bx,2
330 loop s
331 sret:
332 ret
333
334 ;展示界面
335 show_menu:
336 ;在10行,30列显示菜单
337 mov di,160*10 30*2
338 ;保存在直接定址表的绝对位置
339 mov bx,offset func_pos-offset boot 7e00h
340 ;菜单有5行
341 mov cx,5
342 s1:
343 ;这里相当于外循环,每次一行
344 ;获取func_pos中每行的保存位置的偏移地址
345 mov si,ds:[bx]
346 ;调用内循环函数,输出一行的每个字符
347 call show_line
348 ;下一行偏移地址
349 add bx,2
350 ;下一行显示
351 add di,160
352 loop s1
353 ret
354
355 show_line:
356 push ax
357 push di
358 push si
359 show_line_start:
360 ;获取这一行的第si 1个字符
361 mov al,ds:[si]
362 ;判断是否到末尾
363 cmp al,0
364 je show_line_end
365 ;保存字符到显示缓冲区
366 mov es:[di],al
367 add di,2
368 inc si
369 jmp show_line_start
370 show_line_end:
371 pop si
372 pop di
373 pop ax
374 ret
375
376 boot_end:nop
377
378 ;转存引导程序
379 copy_boot:
380 ;将引导程序储存到指定位置
381 mov ax,0
382 mov es,ax
383 mov di,7e00h
384
385 mov ax,cs
386 mov ds,ax
387 mov si,offset boot
388 mov cx,offset boot_end-offset boot
389 cld
390 rep movsb
391
392 ret
393
394 code ends
395 end start
具体的在注释中都说明了。
jz指令:https://zhidao.baidu.com/question/564008138.html
int 16的1号程序:https://zhidao.baidu.com/question/511189643.html
总结
汇编的难度并不大,我认为在有编程的基础上,学习汇编要做到细致,细致的理解计算机编程的编译过程,对于我理解其他编程语言也有很大的帮助。欢迎大家关注,一起交流。