运行环境

操作系统:windows10 调试环境:DOSBox 0.74-3 最后的运行环境:VMware Workstation Pro15(低版本的虚拟机开机功能会报错,因为我使用的是VMware Workstation Pro12所以最后开机功能没有测试成功,假如使用15是可以成功的) 安装的虚拟机:ms-dos6.22 注意:需要修改虚拟机设置,添加软驱

安装ms-dos

我使用的vmware workstation pro 12, 这里选择ms-dos系统会报错,因此我改为选择freeBSD解决了这一问题。然后进入BIOS的方法没有必要像他这样弄得这么复杂,只需要在关机状态下,右键电源,然后打开电源时进入固件即可。安装教程

这里是dos6.22_bootdisk.iso的下载地址

实验要求

阅读下面的材料:

开机后, CPU 自动进入到 FFF0:0 单元处执行,此处有一条跳转指令。 CPU 执行该指令后,转去执行 BIOS 中的硬件系统检测和初始化程序。

初始化程序将建立 BIOS 所支持的中断向量,即将 BIOS 提供的中断历程的入口地址登记在中断向量表中。

硬件系统检测和初始化完成后,调用 INT 19H 进行操作系统的引导。 如果设为从软盘启动操作系统,则 INT 19H 将主要完成一下工作。 (1) 控制 0 号软驱,读取软盘 0 道 0 面 1 扇区的内容到 0:7c00 ; (2) 将 CS:IP 指向 0:7c00 。 软盘的 0 道 0 面 1 扇区中装有操作系统引导程序。int 19h将其装到 0:7c00 处后,设置 CPU 从 0:7c00 开始执行此处的引导程序,操作系统被激活,控制计算机。 如果在 0 号软驱中没有软盘,或发生软盘 I/O 错误,则 int 19h将主要完成以下工作。 (1) 读取硬盘 C 的 0 道 0 面 1 扇区的内容到 0:7c00 ; (2) 将 CS:IP 指向 0:7c00 。

软盘的0道0面1扇区中装有操作系统引导程序。int 19h讲其装道0:7c00处后,设置CPU从0:7c00开始执行此处的引导程序,操纵系统被激活,控制计算机。

如果在0号软驱中没有软盘,或发生软盘I/O错误,则int 19h将主要完成以下工作。

  1. 读取硬盘C的0道0面1扇区的内容到0:7c00;

  2. 将CS:IP指向0:7c00。

这次课程设计的任务是编写一个可以自行启动计算机,不需要在现有操作系统环境中运行的程序。 改程序的功能如下: (1) 列出功能选项,让用户通过键盘进行选择,界面如下: 1) reset pc ; 重新启动计算机 2) start system ; 引导现有的操作系统 3) clock ; 进入时钟程序 4) set clock ; 设置时间 (2) 用户输入"1"后重新启动计算机。(提示:考虑 ffff:0 ) (3) 用户输入"2" 后引导现有的操作系统。(提示:考虑硬盘 C 的 0 道 0 面 1 扇区) (4) 用户输入"3"后,执行动态现实当前日期,时间的程序。 现实格式如下:年/月/日 时:分:秒

进入此项功能后,一直动态显示当前的时间,在屏幕上将出现时间按秒变化的效果(提示:循环读取CMOS)。

当按下F1键后,改变显示颜色;按下Esc键后,返回到主选单(提示:利用键盘中断)。

  1. 用户输入"4"后可更改当前的日期、时间,更改后返回到主选单(提示:输入字符串)。

下面给出几点建议:

  1. 在DOS下编写 安装程序,在安装程序中包含任务程序;

  2. 运行安装程序,将任务程序写到软盘上;

  3. 若要任务程序可以在开机后自行执行,要将它写道软盘的0道0面1扇区上。如果程序长度大于512个字节,则需要用多个扇区存放,这种情况下,处于软盘0道0面1扇区中的程序就必须负责将其他扇区中的内容读入内存。

这个程序较为复杂, 它用到了我们所学到的所有技术,需要进行仔细地分析和耐心地调试。这个程序对于我们的整个学习过程是具有总结性的,希望读者能够尽力完成。

实验过程

sysboot

开头第一句话先跳转到启动代码

jmp short表示跳转的目标是一个在+128或者-127bytes的标签(label)

jmp near表示跳转的目标在同一个段(segment)内

jmp far表示跳转的目标标签在不同的段中

这里的sp设置的比较巧妙,参考别人的方法,将stack作为数据放在数据区,将ss设置为0,然后将sp设置为数据区的结尾处的值,但是这样做需要注意的是不能过多的使用栈,因为假如过多压栈超过分配的栈空间,将会覆盖低地址中存放的其他值。

安装int 9h

先解释一下movsb指令,这里就照搬百度百科了

MOVSB 的英文是 move string byte,意思是搬移一个字节,它是把 DS:SI 所指地址的一个字节搬移到 ES:DI 所指的地址上,搬移后原来的内容不变,但是原来 ES:DI 所指的内容会被覆盖而且在搬移之后 SI 和 DI 会自动地指向下一个要搬移的地址。 一般而言,通常程序设计师一般并不会只搬一个字节,通常都会重复许多次,如果要重复的话,就得把重复次数 ( 也就是字串长度 ) 先记录在 cx寄存器,并且在 MOVSB 之前加上 REP 指令,REP 是重复 (repeat) 的意思。

键盘输入相当于一个外中断:

键盘输入到达60h端口后,就会引发9号中断,CPU则去执行int 9h中断例程。

要让其他的键照常处理:

调用原始的int 9h中断来对其他的输入进行处理,原来的int 9h中断在处理程序的入口地址0:[4*9]

这里的思路是将原始的int 9h中断向量保存在数据区中的oldint9addr, 然后将新的int 9h写入到0:[4*9]

键盘扫描码的具体对应参考这里

使用数据区的sign来保存当前的界面状态

keycapture

keycapture为在调用原始int 9h中断之后,获得保存在al中的键盘扫描码,并对键盘扫描码进行解析。首先将sign的值保存在ah中,然后先判断是哪个界面,在不同的界面中只接受对应的部分键码。

这里使用一个技巧使用cmp与ja、jb的组合来判断键码是否在某一连续区间内,因为cmp实际上是做减操作,因此CF=1 则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时oprd1<oprd2,此时使用jb则会跳转;CF=0 则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时oprd1>oprd2,此时使用ja则会跳转。

跳转命令ja、jb

微内核loopcore

这里使用一个不断循环的子程序来控制键盘对应的操作。使用dl来保存旧的界面状态,然后检测界面是否发生改变,若改变则做出相应的反应。

boot

使用int 13h来读取磁盘,之后跳转到sysboot,相当于载入kernel。

reset pc

设置cs=0,ip=0ffffh,具体方式是使用push入栈然后使用retf对cs和ip进行设置。计算机启动的时候首先会进入0:0ffffh,这一段是bios的代码,然后控制权会交给bios,它会进行一些自检工作,然后将软盘上我写好的boot载入到内存,跳转到0:7c00h之后由boot进行引导。

startsys

此处将c盘中的boot读入到内存中再跳转到0:7c00h处运行,启动dos系统(因为dos系统安装在c盘中)。

clock

1
2
3
4
5
timedata     db 20h,5h,26h,15h,51h,0h
timeaddr db 9,8,7,4,2,0
timestr db '00/00/00 00:00:00',0
timelimitb db 0, 1, 1, 0, 0,0
timelimita db 99h,12h,31h,23h,60h,60h

timedata是6字节内存,用来保存时间数据,设置时钟的数据不直接放入timestr中,而是用时间数据最大允许值timelimita,和最小允许值timelimitb,检测一下是否是时间设置的允许值,符合要求的时间数字放入timedata中,会看到该数字被显示,不符合要求的数字不放入timedata中,就不会被显示,也不会被保存。

调试

由于涉及到大量数据复制操作,因此会使用rep movsb命令,假如在dosbox中使用debug进行调试,直接按t进入下一步则会反复执行movsb,因此为了跳过它,我们可以使用u,显示之后的汇编命令,然后g+地址直接让程序执行到我们需要的地方。

测试时,程序运行流程

回顾一个小知识点,使用来寄存器间接寻址时,只可以使用 BX, BP, SI, DI 这四个寄存自器百中的一个,不可以使用其它寄存器。

测试完毕后将testRun7e00h注释掉换成,copyToDiskAsector

1
2
3
4
start:
call copyToDiskAsector
; call testRun7e00h
code ends

测试开机功能时出现以下报错

该问题要将wmware workstation pro12升级成vmware workstation pro15才能解决

最终效果

首先在dos中运行这个程序将内容拷贝到软盘上。 然后关闭电源,进入bios,设置启动顺序,将 放在前面,然后按f10保存修改。 之后进入系统 按1重新启动 按2启动系统 按3显示时间,改变颜色 按4修改时间,可以移动光标,修改数值,但是这里没有捕捉小键盘,所以不能使用数字小键盘,enter保存

源代码

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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
assume cs:code,ds:data
data segment
bootdata db 510 dup(0)
dw 0aa55h
data ends
code segment




;=======================下面是A盘2至4扇区内容===========================

;offset sysbootend-offset sysboot之间的内容是拷贝到A盘2-4扇区的内容。

;=======================下面是A盘2至4扇区内容===========================

sysboot:
jmp near ptr dostart
oldint9addr dw 0,0
;如果不加offset偏移地址会出错
menudata dw offset md0-offset sysboot+7e00h
dw offset md1-offset sysboot+7e00h
dw offset md2-offset sysboot+7e00h
dw offset md3-offset sysboot+7e00h
dw offset md4-offset sysboot+7e00h
md0 db '------ welcome ------',0
md1 db '1) reset pc',0
md2 db '2) start system',0
md3 db '3) clock',0
md4 db '4) set clock',0
timedata db 20h,2h,18h,17h,51h,0h
timeaddr db 9,8,7,4,2,0
timestr db '00/00/00 00:00:00',0
timelimitb db 0, 1, 1, 0, 0,0
timelimita db 99h,12h,31h,23h,60h,60h
focusplaces db 0,1,3,4,6,7,9,10,12,13,15,16
focus db 0
color db 4
functionlist dw offset endloopcore-offset sysboot+7e00h
dw offset showmenu -offset sysboot+7e00h
dw offset reset -offset sysboot+7e00h
dw offset startsys -offset sysboot+7e00h
dw offset showclock -offset sysboot+7e00h
dw offset setclock -offset sysboot+7e00h
sign db 1 ;设为0时退出,1,显示菜单,4、时显示时钟 5、设置时钟
stack db 128 dup (0)

dostart:
mov ax,cs;cs=0
mov ds,ax
mov ss,ax
add ax,offset dostart-offset sysboot+7e00h
mov sp,ax

call setint9 ;捕获键盘,设置9号中断,
jmp loopcore ;循环显示菜单。
;call saveclock ;调试保存时钟
;call showclock ;调试显示时钟

;================调试时钟设置================
;mov al,0ah
;call settime
;call showtimedata
;================调试时钟设置================

mov ax,4c00h
int 21h
;ret
;这几行程序一直在循环,相当于微内核================================
;sign=1时显示菜单,
;sign=2时调用reset
;sign=3时调用硬盘第一扇区
;sign=4时显示时钟
;sign=5时设置时钟
;这几行程序一直在循环,相当于微内核================================
loopcore:
mov bx,offset sign -offset sysboot+7e00h
mov bl,cs:[bx]; 取出菜单选择的数字值
mov bh,0

cmp bl,dl ; 检测选择键是否改变
je loopcore1
call clear

loopcore1:
mov dl,bl; 暂存用于下次比较
mov di,offset functionlist-offset sysboot+7e00h
add bl,bl ;functionlist 每个函数的地址是 2 字节。
add di,bx
call word ptr cs:[di]
jmp loopcore
ret
;=======================这几行程序一直在循环,相当于微内核=================




;sign=2
;重启系统
reset:
mov bx,0ffffh
push bx
mov bx,0
push bx
retf


;sign=3
;开机,将booter从0面0道1扇区中读出
startsys:
mov ax,0
mov es,ax
mov bx,7c00h;es:bx内存数据。

mov dl,80h;硬盘C
; mov dl, 0h ;软盘0
mov dh,0;0面(磁头)
mov ch,0;磁道号
mov cl,1;扇区号
mov al,1;扇区数量
mov ah,2;读扇区
int 13h

mov bx,offset startsys7c00h-offset sysboot+7e00h
jmp dword ptr cs:[bx]
startsys7c00h dw 7c00h,0
ret


;sign=4
showclock:
call gettimedata
call showtimedata
ret

;sign=5
setclock:
call showtimedata
ret

gettimedata:
push si
push di
push ax
mov si,offset timeaddr - offset sysboot+7e00h
mov di,offset timedata - offset sysboot+7e00h
mov cx,6
gettimedatas0:
mov al,cs:[si]
out 70h,al
in al,71h
mov cs:[di],al
inc si
inc di
loop gettimedatas0
pop ax
pop di
pop si
ret

showtimestr:
push si
push bx
mov si,offset timestr - offset sysboot+7e00h
mov bl,32;列
mov bh,13;行
call showstr
pop si
pop bx
ret

setfocus:
push ax
push bx
push dx
push si
push di
mov si,offset focus - offset sysboot+7e00h
mov bl,cs:[si]
cmp al,4bh
jne rightfocus
cmp bl,0
jne focusdec
mov bl,11
jmp showfocus

focusdec:
dec bl
jmp showfocus


rightfocus:
cmp al,4dh
jne showfocus
cmp bl,11
jne focusinc
mov bl,0
jmp showfocus

focusinc:
inc bl
jmp showfocus


showfocus:
mov cs:[si],bl;保存移动后的光标索引值
mov di,offset focusplaces - offset sysboot+7e00h
mov bh,0
mov dl,cs:[di+bx];取出光标的实际位

mov ah,2
mov bh,0
mov dh,13;行
add dl,32;列

int 10h
;setfocusquit:
pop di
pop si
pop dx
pop bx
pop ax

ret

settime:
push si
push di
push ax
push bx
push cx
push dx
push bp

call scantodigit

mov bp,offset timedata -offset sysboot +7e00h


mov bx,offset focus - offset sysboot+7e00h
mov bl,cs:[bx] ;取出光标索引值
mov bh,0

mov dx,bx
and dx,1b ;如果focus单数时,取出timedata[focus/2],用键入值对应timedata[focus/2+1]

shr bx,1

add bp,bx

cmp dl,0
jne getts1
;如果focus是双数时,键入值对应timedata[focus/2],取出timedata[focus/2+1]
mov dh,cs:[bp]
shl al,1
shl al,1
shl al,1
shl al,1
and dh,1111b
or al,dh
jmp gett2

getts1:
;如果focus是单数时,取timedata[focus/2],用键入值对应timedata[focus/2+1]
mov dh,cs:[bp]
and dh,11110000b
or al,dh
gett2:

mov di,offset timelimitb-offset sysboot +7e00h
mov si,offset timelimita-offset sysboot +7e00h

mov dl,cs:[di+bx];时钟的最小允许值
mov dh,cs:[si+bx];时钟的最大允许值
;用timedata[focus/2][focus/2+1] 与时钟最大允许值,和最小允计值比较
;判断键入的时钟最是否可用,如果可用
;则把timedata[focus/2][focus/2+1]填入timedata
cmp al,dl
jb quitgettime
cmp al,dh
ja quitgettime
;call bcdtochar
mov cs:[bp],al
quitgettime:
pop bp
pop dx
pop cx
pop bx
pop ax
pop di
pop si
ret

showtimedata:
push si
push di
push ax
push bx
push cx
mov si,offset timedata - offset sysboot+7e00h
mov di,offset timestr - offset sysboot+7e00h
mov cx,6
showclocks0:
mov al,cs:[si]
call bcdtochar

mov cs:[di+0],ah
mov cs:[di+1],al
add di,3
inc si
loop showclocks0
mov si,offset timestr - offset sysboot+7e00h
mov bl,32;列
mov bh,13;行
call showstr
showclockquit:
pop cx
pop bx
pop ax
pop di
pop si
ret

newint9:
push ax
push bx
push es
in al,60h

mov bx,0
mov es,bx
pushf
mov bx,offset oldint9addr-offset sysboot+7e00h
call dword ptr es:[bx]
call keycapture
pop es
pop bx
pop ax
iret

setint9:
push di
push ax
push es
push ds
mov ax,0
mov es,ax

mov bx,offset oldint9addr-offset sysboot+7e00h
push es:[9*4]
pop es:[bx]
push es:[9*4+2]
pop es:[bx+2]

cli
mov word ptr es:[9*4],offset newint9-offset sysboot+7e00h
mov word ptr es:[9*4+2],0
sti

pop ds
pop es
pop ax
pop di

ret
;在bh行,bl列,显si开始处的中以0结尾的字符
showstr:;9E
push es
push ax
push di
push cx
push bx
push bp
mov ax,0b800h
mov es,ax

mov al,160;每行显示80个字符,显示一个字要两字节内存1每个字符1字节,颜色1字
mul bh ;dh是行数

mov cx,bx;保存bx
mov bh,0 ;总列数保存在ax

add ax,bx;计算列数,如果用add al,bl会溢位出错
add ax,bx;计算列数,字符1字节,颜色1字节,所以加两次

mov di,ax;目标地址
mov bx,cx;恢复bx
mov bp,offset color-offset sysboot+7e00h
mov ah,cs:[bp];高字节存放颜色
mov ch,0
showstr1:
mov al,cs:[si];低字节是数据
mov es:[di],ax;高字节是颜色

inc si
add di,2
mov cl,al
jcxz showstr2
jmp showstr1
showstr2: ;D7
pop bp
pop bx
pop cx
pop di
pop ax
pop es
ret

;展示界面=================================================
showmenu:
push bx
push bp
;在10行,30列显示菜单
; mov di,160*10+30*2
mov bh,10
mov bl,30
;保存在直接定址表的绝对位置
mov bp,offset menudata-offset sysboot+7e00h
;菜单有5行
mov cx,5
s1:
;这里相当于外循环,每次一行
;获取menudata中每行的保存位置的偏移地址
mov si,ds:[bp]
;调用内循环函数,输出一行的每个字符
call showstr
;下一行偏移地址
add bp,2
;下一行显示
inc bh
loop s1
pop bp
pop bx
ret


clear:
push bx
push dx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov ds,bx

mov bx,0
mov dx,0700h ;清屏中对字符属性设置应该为07h,而不是0
mov cx,2000
clear1:
mov es:[bx],dx
add bx,2
loop clear1
pop es
pop cx
pop dx
pop bx
ret

endloopcore:
mov bx,offset sysboot-offset oldint9addr
push es:[bx]
pop es:[9*4]
push es:[bx+2]
pop es:[9*4+2]
mov ah,4ch
int 21h
ret

saveclock:
push ax
push si
push di
mov di,offset timedata -offset sysboot +7e00h
mov si,offset timeaddr -offset sysboot +7e00h

mov cx,6
saveclocks:
mov al,cs:[si]
out 70h,al
mov al,cs:[di]
out 71h,al
inc di
inc si
loop saveclocks

pop di
pop si
pop ax

ret
scantodigit:
mov ah,al
cmp al,0bh
jne gettnum1
mov al,0
jmp quitscantodigit
gettnum1:
cmp al,2
jb quitscantodigit
cmp al,0bh
ja quitscantodigit
dec al
quitscantodigit:
ret



resetfocus:
push ax
push bx
push dx
mov ah,2
mov bh,0
mov dh,25
mov dl,0
int 10h
pop dx
pop bx
pop ax
ret

;al 是bcd码,返回ah高字符,al低字符
bcdtochar:
push dx
mov dl,al
shr al,1
shr al,1
shr al,1
shr al,1
add al,30h
mov ah,al
mov al,dl
and al,1111b
add al,30h
pop dx
ret

keycapture:
keycheck:
push ax
push bx
push dx
push di


mov bx, offset sign-offset sysboot+7e00h
mov ah,cs:[bx]

cmp al,3bh; F1 改变颜色
jne keyF2
mov bx, offset color-offset sysboot+7e00h
inc byte ptr cs:[bx]
jmp quitkeycheck



keyF2:
cmp al,3ch ; 接F2时设置sign=0,退出循环。
jne keymenu
mov byte ptr cs:[bx],0; endloopcore
jmp quitkeycheck


;================================================================
;1、只在三种状态下接收扫描码 1、sign=1 菜单状态 2、sign =4 显示clock 3、 sign=5 时置clock
;
;在菜单状态下,如果按不是1、2、3、4跳转到结束。
keymenu:
mov ah,cs:[bx]
cmp ah,1
jne keyshowclock
cmp al,2
jb quitkeycheck
cmp al,5
ja quitkeycheck

keymenu1:
mov cs:[bx],al ;由于键码刚好与sign对应所以直接将sign置为al即可

jmp quitkeycheck
;===============================================================
;在显示时钟状态下只检测esc
keyshowclock:
cmp ah,4
jne keysetclock
cmp al,1
jne quitkeycheck
mov cs:[bx],al

jmp quitkeycheck
;===============================================================


;===============================================================
;设置时钟时:
;1、检测四个按键,左箭头,右箭头 ess,enter
;2、检测1,2,3,4,5,6,7,8,9,0
; 4个菜单键检测完成,跳转到结束。
keysetclock:
cmp ah,5
jne quitkeycheck
;假如键码小于2或大于b则说明不是0-9,改为判断是否为方向键
cmp al,2
jb keyleft
cmp al,0bh
ja keyleft

call settime

keyleft:
cmp al,4bh
jne keyright

call setfocus

keyright:
cmp al,4dh
jne keyesc

call setfocus

keyesc:
cmp al,1
jne keyenter

mov cs:[bx],al

keyenter:
cmp al,1ch
jne quitkeycheck

call saveclock

;=================================================================
quitkeycheck:
pop di
pop dx
pop bx
pop ax
ret

sysbootend:nop
;=======================上面是A盘2至4扇区内容===========================

;offset sysbootend-offset sysboot之间的内容是拷贝到A盘2-4扇区的内容。

;=======================上面是A盘2至4扇区内容===========================



;==========================下面是A盘1扇区内容===========================

;boot:磁盘第一扇区被加载到7c00h
;引导扇区的功能是实现把2,3,4,5扇区的
;加载到内7e00h,并把转跳到7e00h.

;=========================下面是A盘1扇区内容=============================
boot:
mov ax,0
mov es,ax
mov bx,7e00h;es:bx内存数据。

mov dl,0;0软区
mov dh,0;0面(磁头)
mov ch,0;磁道号
mov cl,2;扇区号
mov al,3;扇区数量
mov ah,2;读扇区
int 13h

mov bx,offset s7e00-offset boot+7c00h
jmp dword ptr cs:[bx]
s7e00 dw 7e00h,0
endboot:nop
;====================================================================

testRun7e00h:

mov ax,cs
mov ds,ax

mov si,offset sysboot
mov ax,0h
mov es,ax
mov di,7e00h
mov cx,offset sysbootend-offset sysboot
cld
rep movsb


mov bx,offset s7e00h-offset sysboot
jmp dword ptr cs:[bx]
s7e00h dw 7e00h,0
testRun7e00hEnd:nop

ret


copyToDiskAsector:
mov ax,cs
mov ds,ax ;ds:si是安装源
mov si,offset boot

mov ax,data
mov es,ax
mov di,offset bootdata


mov cx,offset endboot-offset boot
cld
rep movsb


mov ax,data
mov es,ax
mov bx,offset bootdata;es:bx内存数据。

mov dl,0;0软区
mov dh,0;0面(磁头)
mov ch,0;磁道号
mov cl,1;扇区号
mov al,1;扇区数量
mov ah,3;写扇区
int 13h

mov ax,cs
mov es,ax
mov bx,offset sysboot;es:bx内存数据。

mov dl,0;0软区
mov dh,0;0面(磁头)
mov ch,0;磁道号
mov cl,2;扇区号
mov al,3;扇区数量
mov ah,3;写扇区
int 13h

mov ax,0
mov es,ax
mov bx,7c00h;es:bx内存数据。

mov dl,0;0软区
mov dh,0;0面(磁头)
mov ch,0;磁道号
mov cl,1;扇区号
mov al,1;扇区数量
mov ah,2;读扇区
int 13h

mov bx,offset s7c00h
jmp dword ptr ds:[bx]
s7c00h dw 7c00h,0

mov ah,4ch
int 21h
copyToDiskAsectorEnd:nop


start:
call copyToDiskAsector
; call testRun7e00h
code ends

end start