| 12
 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
 
 | ; 课程设计2; 实现了4个功能的程序以下称为主程序
 
 assume cs:code
 code segment
 start:
 
 ; 安装我们自己的引导程序到软盘a,占1个扇区
 call inst_my_boot
 
 ; 安装主程序到软盘a,占2个扇区
 call inst_my_main
 
 mov ax,4c00h
 int 21h
 
 ;---------------- 安装程序 ----------------
 inst_my_boot:
 mov bx,cs
 mov es,bx
 mov bx,offset my_boot ; es:bx指向my_boot
 
 ; 写入内容到 软盘A,0面,0道,1扇区
 mov dl,0 ; 软盘A
 mov dh,0 ; 0面
 mov ch,0 ; 0道
 mov cl,1 ; 第1扇区
 mov al,1 ; 写1个扇区
 mov ah,3 ; 写
 int 13h
 
 ret
 
 inst_my_main:
 mov bx,cs
 mov es,bx
 mov bx,offset my_main ; es:bx指向my_main
 
 ; 写入内容到 软盘A,0面,0道,2扇区
 mov dl,0 ; 软盘A
 mov dh,0 ; 0面
 mov ch,0 ; 0道
 mov cl,2 ; 第2扇区
 mov al,2 ; 写2个扇区
 mov ah,3 ; 写
 int 13h
 
 ret
 
 ;---------------- 引导程序 ----------------
 my_boot:
 ; 设栈
 cli
 mov ax,0
 mov ss,ax
 mov sp,7c00h
 sti
 
 ; 装载新int9h中断例程
 call load_newint9
 
 ; 将主程序拷贝到7e00h
 mov bx,0
 mov es,bx
 mov bx,7e00h
 
 ; 读取 软盘A,0面,0道,2扇区 开始的2个扇区 到0:7e00h
 mov dl,0 ; 软盘A
 mov dh,0 ; 0面
 mov ch,0 ; 0道
 mov cl,2 ; 第2扇区
 mov al,2 ; 复制2个扇区
 mov ah,2 ; 读取
 int 13h
 
 mov bx,0
 push bx
 mov bx,7e00h
 push bx
 retf ; 从栈中取2字 设CS:IP=0:7e00h 并从此处开始执行
 
 ; 装载新int9h中断例程
 load_newint9:
 push ds
 push si
 push es
 push di
 push cx
 
 push cs ; 引导程序执行时,cs:ip=0:7c00h
 pop ds
 
 mov cx,0
 mov es,cx
 
 mov si,newint9-my_boot+7c00h ; ds:si指向新int9例程
 mov di,204h ; es:di指向新int9例程装载位置
 mov cx,newint9end-newint9
 cld
 rep movsb
 
 ; 保存旧的int9h中断向量
 push es:[9*4]
 pop es:[200h]
 push es:[9*4+2]
 pop es:[202h]
 
 pop cx
 pop di
 pop es
 pop si
 pop ds
 ret
 
 newint9:
 push ax
 push bx
 push cx
 push es
 
 in al,60h
 
 pushf
 call dword ptr cs:[200h] ; 此newint9中断放在0:204h处,此中断执行时cs=0
 
 cmp al,3bh ; f1扫描码
 je chgcolor
 cmp al,01h ; esc键扫描码
 je int9ret
 jmp newint9ret
 
 backmain:
 pop es
 pop cx
 pop bx
 pop ax
 
 add sp,4 ; 跳过cs:ip
 popf
 
 mov bx,0
 push bx
 mov bx,7e00h
 push bx
 retf
 
 int9ret:
 ; 恢复原int9中断向量,原中断向量保存在0:200h, 0:202h
 mov ax,0
 mov es,ax
 cli
 push es:[200h]
 pop es:[9*4]
 push es:[202h]
 pop es:[9*4+2]
 sti
 jmp backmain
 
 chgcolor:
 mov ax,0b800h
 mov es,ax
 mov bx,1
 mov cx,17
 chgcolor_s1:
 inc byte ptr es:[bx]
 add bx,2
 loop chgcolor_s1
 
 newint9ret:
 pop es
 pop cx
 pop bx
 pop ax
 iret
 
 newint9end:
 nop
 
 ; 因为引导程序占512字节,这里填充512字节0,作为扇区结束,
 ; 防止引导程序不够512字节,而复制下面的代码到1扇区内
 db 512 dup (0)
 
 ;--------------------- 主程序 ---------------------
 my_main:
 jmp main_start
 menu1     db '1) reset pc',0
 menu2     db '2) start system',0
 menu3     db '3) clock',0
 menu4     db '4) set clock',0
 menu_addr dw menu1-my_main+7e00h, menu2-my_main+7e00h, menu3-my_main+7e00h, menu4-my_main+7e00h
 timestr   db 'yy/mm/dd hh:mm:ss',0
 timeaddr  db 9,8,7,4,2,0
 strbuffer db 100 dup (0) ; 输入字符串缓冲区
 
 main_start:
 ; 初始化数据段寄存器
 mov ax,0
 mov ds,ax
 
 call clr_src
 call show_menu
 call choose_item
 
 choose_item: ; 选择菜单项
 mov si,strbuffer-my_main+7e00h
 call getstr ; 输入字符串,回车确认,ds:si指向字符串缓冲区
 
 cmp byte ptr [si],'1'
 je item1
 cmp byte ptr [si],'2'
 je item2
 cmp byte ptr [si],'3'
 je item3
 cmp byte ptr [si],'4'
 je item4
 jmp main_start ; 其他输入时,重新显示菜单
 
 item1:
 mov bx,0ffffh
 push bx
 mov bx,0
 push bx
 retf ; 将从ffff:0处开始执行,会重启系统
 
 item2: ; 引导现有系统
 ; 将原mbr拷贝到7c00h
 mov bx,0
 mov es,bx
 mov bx,7c00h
 
 ; 读取 硬盘c,0面,0道,1扇区 到0:7c00h
 mov dl,80h ; 盘c
 mov dh,0 ; 0面
 mov ch,0 ; 0道
 mov cl,1 ; 第1扇区
 mov al,1 ; 复制1个扇区
 mov ah,2 ; 读取
 int 13h
 
 mov bx,0
 push bx
 mov bx,7c00h
 push bx
 retf
 
 item3:
 call setnewint9
 call show_dt
 jmp main_start
 
 item4:
 call clr_src
 mov si,strbuffer-my_main+7e00h
 call getstr
 call set_dt ; ds:si指向字符串缓冲区
 jmp main_start
 
 show_menu: ; 显示主程序菜单
 push bx
 push es
 push si
 push cx
 push di
 
 mov bx,0b800h
 mov es,bx
 mov bx,160*10+32*2 ; 中间位置显示菜单,第10行第32列
 mov di,menu_addr-my_main+7e00h ; 直接定址表
 mov cx,4
 show_menu_s1:
 mov si,[di] ; ds:di定位直接定址表
 call show_str
 add di,2
 add bx,160 ; 跳到下一行
 loop show_menu_s1
 
 pop di
 pop cx
 pop si
 pop es
 pop bx
 ret
 
 clr_src: ; 清屏
 push bx
 push cx
 push es
 mov bx,0b800h
 mov es,bx
 mov bx,0 ; 显存的偶地址单元为字符
 mov cx,2000
 clr_src_s1:
 mov byte ptr es:[bx],' ' ; 空格填充
 add bx,2
 loop clr_src_s1
 pop es
 pop cx
 pop bx
 ret
 
 ; 显示字符串,字符串以0结束
 ; ds:si指向字符串开头
 ; es:bx指向显存开始,对应屏幕上的位置
 show_str:
 push si
 push cx
 push es
 push bx
 show_str_s1:
 mov cl,[si]
 cmp cl,0
 je show_str_ret
 mov es:[bx],cl
 add bx,2
 inc si
 jmp show_str_s1
 show_str_ret:
 pop bx
 pop es
 pop cx
 pop si
 ret
 
 ; 接受字符串输入
 ; ds:si指向字符栈空间
 getstr:
 push ax
 push dx
 push es
 push di
 
 mov ax,0b800h
 mov es,ax
 mov di,160*14+32*2 ; es:di为光标初始位置、字符串输入位置
 ;mov di,0
 call setcur
 
 mov dx,0 ; 字符栈栈指针
 getstrs:
 mov ah,0
 int 16h ; 获取键盘缓冲区内容
 cmp al,20h
 jb nochar
 cmp al,7eh
 ja nochar ; 只能输入可见字符
 mov ah,0
 call charstack ; 字符入栈
 mov ah,2
 call charstack ; 显示栈中字符
 jmp getstrs
 
 nochar:
 cmp ah,0eh ; 退格键扫描码
 je backspace
 cmp ah,1ch ; 回车键扫描码
 je enter
 jmp getstrs
 
 backspace:
 mov ah,1
 call charstack ; 字符出栈
 mov ah,2
 call charstack
 jmp getstrs
 
 enter:
 mov al,0 ; 把0入栈,作为字符串结束
 mov ah,0
 call charstack
 mov ah,2
 call charstack
 
 pop di
 pop es
 pop dx
 pop ax
 ret
 
 ; 字符入栈、出栈、显示功能
 ; ah=功能号,0入栈,1出栈,2显示
 ; ds:si指向字符栈空间
 ; dx=字符栈栈指针
 ; 0号功能,al=入栈字符
 ; 1号功能,al=返回的字符
 ; 2号功能,es:di指向屏幕位置
 charstack: jmp short charstart
 table      dw charpush-my_main+7e00h,charpop-my_main+7e00h,charshow-my_main+7e00h
 
 charstart:
 push bx
 push di
 push es
 ;dx为charstack非局部变量,不要入栈
 ;push dx
 
 cmp ah,2 ; 功能号判断
 ja sret
 mov bl,ah
 mov bh,0
 add bx,bx ; 根据功能号取得对应的偏移地址
 jmp word ptr table-my_main+7e00h[bx]
 
 charpush:
 mov bx,dx
 mov [si][bx],al
 inc dx
 jmp sret
 
 charpop:
 cmp dx,0
 je sret
 dec dx
 mov bx,dx
 mov al,[si][bx]
 jmp sret
 
 charshow:
 ; es:di指向屏幕位置,由调用者传递
 mov bx,0
 charshows:
 cmp bx,dx
 jne noempey
 mov byte ptr es:[di],' '
 call setcur
 jmp sret
 
 noempey:
 mov al,[si][bx]
 mov es:[di],al
 mov byte ptr es:[di+2],' ' ; 清空后一个字符
 inc bx
 add di,2
 jmp charshows
 
 sret:
 pop es
 pop di
 pop bx
 ret
 
 setcur: ; 设置光标到es:di位置
 push ax
 push dx
 
 mov ax,di
 mov dh,160
 div dh
 mov dh,al ; 行号
 mov al,ah
 mov ah,0
 mov dl,2
 div dl
 mov dl,al ; 列号
 mov ah,2 ; 设置光标位置
 mov bh,0 ; 第0页
 int 10h
 
 pop dx
 pop ax
 ret
 
 ; 显示时间
 show_dt:
 call get_dt
 call delay
 jmp show_dt
 ret
 
 ; 设置新的int9h中断向量
 setnewint9:
 push es
 push bx
 mov bx,0
 mov es,bx
 cli
 mov word ptr es:[9*4],204h
 mov word ptr es:[9*4+2],0
 sti
 pop bx
 pop es
 ret
 
 ; 获取CMOS中的系统时间
 ; ds:si从cmos对应地址取日期时间,ds:di转换后的日期时间字符串
 get_dt:
 push si
 push di
 push cx
 push ax
 push es
 push bx
 
 mov si,timeaddr-my_main+7e00h
 mov di,timestr-my_main+7e00h
 mov cx,6
 get_dt_s1:
 mov bx,cx
 mov al,[si]
 out 70h,al ; 70h为地址端口
 in al,71h ; 71h为数据端口
 mov ah,al
 mov cl,4
 shr ah,cl ; 右移4位,ah为十进制的十位数
 and al,00001111b ; al为十进制的个位数
 add ah,30h
 add al,30h ; 数值转字符形式
 xchg ah,al
 mov [di],ax
 add di,3
 inc si
 mov cx,bx
 loop get_dt_s1
 
 mov bx,0b800h
 mov es,bx
 mov bx,0
 mov si,timestr-my_main+7e00h
 call show_str
 
 pop bx
 pop es
 pop ax
 pop cx
 pop di
 pop si
 ret
 
 ; 延时
 delay:
 push ax
 push dx
 mov dx,6000h ; 循环6000000h次
 mov ax,0
 delays1:
 sub ax,1
 sbb dx,0
 cmp ax,0
 jne delays1
 cmp dx,0
 jne delays1
 pop dx
 pop ax
 ret
 
 ; 设置系统时间
 set_dt:
 push si
 push di
 push cx
 push ax
 push bx
 
 mov di,timeaddr-my_main+7e00h
 mov cx,6
 set_dt_s1:
 mov ax,[si] ; al为十位数,ah为个位数
 sub ah,30h
 sub al,30h
 and ah,00001111b ; 取个位数
 mov bx,cx
 mov cl,4
 shl al,cl
 or al,ah
 mov cx,bx
 mov ah,al
 mov al,[di]
 out 70h,al
 mov al,ah
 out 71h,al
 inc di
 add si,3
 loop set_dt_s1
 
 pop bx
 pop ax
 pop cx
 pop di
 pop si
 ret
 
 db 1024 dup (0) ; 填充1k字节,原因同上
 
 code ends
 end start
 
 |