汇编-一段代码掌握所有汇编知识

汇编语言是任何一种用于电子计算机、微处理器、微控制器,或其他可编程器件的低级语言。

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
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168

; 汇编语言 assembly

;;;

;;;

; ax bx cx dx [通用寄存器]
; h (high) 高位 l (low) 低位

; ax (accumulator) 累加寄存器
; bx (base) 基址寄存器
; cx (count) 计数寄存器
; dx (data) 数据寄存器

; sp (stack pointer) [堆栈指针寄存器]

; bp (base pointer) [基址指针寄存器]

; si (source pointer) [源变址寄存器]
; di (destination index) [目的变址寄存器]

; 只有 bx bp si di 可以用在[...]中来进行内存单元的寻址
; 4中正确组合 [bx+si],[bx+di],[bp+si],[bp+di]

; [bp] 表示当前堆栈段所在的实际物理内存地址
; 将堆栈段的值赋值给ax `mov ax,[bp]`
; 即 (ax)=((ss)*16 + (bp))

;;;

; 段寄存器之间不能相互赋值

; ds (data segment) [数据段寄存器] 存放数据段的段基值
; ds:[13abh] 或 [bx] 表示当前数据段所在的实际物理内存地址
; 将数据段的值赋值给ax `mov ax,ds:[13abh]`
; 将数据段的值赋值给ax `mov bx,13abh` `mov ax,[bx]`
; 即 (ax)=((ds)*16 + (bx))
; [bx+5]=[5+bx]=5[bx]=[bx].5

; cs (code segment) [代码段寄存器] 存放当前正在运行的程序代码所在段的段基值
; ip (instruction pointer) [指令指针寄存器]
; cs:ip 表示要执行的代码片段所在的实际物理内存地址

; ss (stack segment) [堆栈段寄存器] 存放堆栈段的段基值
; sp (stack pointer) [堆栈指针寄存器] 通用寄存器
; ss:sp 表示当前堆栈所指的实际物理内存地址

; es (extra segment) [附加段寄存器]

;;;


;;;

;;;

; 十进制 85 = 原码 01010101B = 反码 01010101B = 补码 01010101B
; 负数补码=原码(除符号位)按位取反再+1
; 负数原码=补码(除符号位)按位取反再+1
; 十进制 -85 = 原码 11010101B = 反码 10101010B = 补码 10101011B

;;;

; 指令在执行前 所要处理的数据在三个地方
; CPU内部 内存 端口

; [idata] 直接寻址
; [bx],[si],[di],[bp] 寄存器间接寻址
; [bx+idata],[si+idata],[di+idata],[bp+idata] 寄存器相对寻址
; [bx+si],[bx+di],[bp+si],[bp+di] 基址变址寻址
; [bx+si+idata],[bx+di+idata],[bp+si+idata],[bp+di+idata] 相对基址变址寻址

;;;

; MASM 基本命令
; R 显示寄存器内容
; R [寄存器名] 修改指定寄存器值
; A 开始汇编指令
; A [内存地址] 从指定地址开始汇编指令
; U [内存地址] 对指定内存块进行反汇编
; D [内存地址] 显示指定内存块内容 以字节单位读取
; T 单步执行指令
; P 直接跳到循环结束
; G [偏移地址] 直接执行到偏移地址
; Q 退出DEBUG

; AB 21000H
; CD 21001H
; EF 21002H
; 00 21003H
; 00 21004H
; 00 21005H
; 00 21006H
; 00 21007H
; 00 21008H
; 00 21009H
; 00 2100AH

;;;

; word ptr 指明字类型数据
; `mov word ptr ds[0],1` 0001H
; `inc word ptr [bx]`
; byte ptr 指明字节类型数据
; `mov byte ptr ds:[0],1` 01H
; `inc byte ptr [bx]`

;;;
; ASCII码 一个字节 (1个字节(Byte)= 8位 bit)
; a=61H=01100001B b=62H=01100010B
; A=41H=01000001B B=42H=01000010B
;;;

; 伪指令 编译器处理
; 汇编指令 编译为机器码

; 用T命令调试属于单步中断 它会把cs,ip值等入栈 但不改变sp值
;;;

; assume 伪指令
; 除cs以外段其它段寄存器 仅仅是声明了这个段的名字
; data stack 并没有将段地址装入段寄存器 需要在运行中执行装载代码
assume ds:data,es:table,ss:stack,cs:code

; db定义字节类型变量 一个字节类型数据占1个字节单元 一个字节16*16大小 读完一个 偏移量加1
; dw定义字类型变量 一个字类型数据占2个字节单元 读完一个 偏移量加2
; dd定义双字类型变量 一个双字类型数据占4个字节单元 读完一个 偏移量加4

; segment 伪指令 段定义
data segment
; 定义字类型数据 8字类型数据 16字节类型数据 偏移 data:0-15
; [0000H],[0001H],...,[000EH],[000FH]
; 23 01 56 04 89 07 BC 0A EF 0D 00 00 FF FF FF FF
dw 0123h,0456h,0789h,0abch,0defh,0,0ffffh,65535
; 定义字节类型数据 偏移 data:16-20
; [0010H],[0011H],[0012H],[0013H],[0014H]
; 61 53 63 49 69
db 'aScIi'
; 定义字节类型数据 偏移 data:21-25
; [0015H],[0016H],[0017H],[0018H],[0019H]
; 61 53 63 49 69
db 'aScIi'
; 定义一串字节类型数据 偏移 data:26-36
; [001AH],[001BH],...,[0023H],[0024H]
; 77 65 6C 63 6F 6D 65 20 74 6F 21
db 'welcome to!'
; 定义一串字节类型数据 偏移 data:37-47
; [0025H],[0026H],...,[002EH],[002FH]
; 68 65 6C 6C 6F 20 32 30 32 32 21
db 'hello 2022!'
; 定义一串字节类型数据 偏移 data:48-63
; [0030H],[0031H],...,[003EH],[003FH]
; 20 20 ... 20 20
db ' first blood '
; 定义一串字节类型数据 偏移 data:64-79
; [0040H],[0041H],...,[004EH],[004FH]
; 20 20 ... 20 20
db ' wx LoveCoder '
; 定义一串字节类型数据 偏移 data:80-95
; [0050H],[0051H],...,[005EH],[005FH]
; 20 20 ... 20 20
db ' triple kill '
; 定义一串字节类型数据 偏移 data:96-111
; [0060H],[0061H],...,[006EH],[006FH]
; 20 20 ... 20 20
db ' quadra kill '
; 定义一串字节类型数据 偏移 data:112-127
; [0070H],[0071H],...,[007EH],[007FH]
; 20 20 ... 20 20
db ' penta kill '
; 定义一串字节类型数据 偏移 data:128-143
; [0080H],[0081H],...,[008EH],[008FH]
; 20 20 ... 20 20
db ' aced '
; 定义字类型数据 16字节类型数据 偏移 data:144-159
; 用来存放临时数据
; [0090H],[0091H],...,[009EH],[009FH]
; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
dw 0000H,0,0,0,0,0,0,0
; dd定义双字类型变量 4字节类型数据 偏移 data:160-163 高位存储高字类型 低位存储低字类型
; [00A0H],[00A1H],[00A2H],[00A3H]
; A1 86 01 00
dd 100001
; 定义字类型数据 2字节类型数据 偏移 data:164-165 高位存储高字节 低位存储低字节
; [00A4H],[00A5H]
; 64 00
dw 100
; 定义字类型数据 2字节类型数据 偏移 data:166-167 高位存储高字节 低位存储低字节
; [00A6H],[00A7H]
; 00 00
dw 0
; dup 操作符 用来进行数据的重复 4个字节重复2次 偏移 data:168-175
; [00A8H],[00A9H],...,[00AFH]
; 61 62 41 42 61 62 41 42
db 2 dup ('ab', 'AB')
; 定义一串字节类型数据 每4个字节一组 表示32年的年份 偏移 data:176-303
; [00B0H],...,[012FH]
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995','1996','1997','1998','1999','2000','2001','2002','2003','2004','2005','2006'
; dd定义双字类型变量 每4个字节一组 表示32年的公司总收入 偏移 data:304-431
; [0130H],...,[01AFH]
; 8A 1C 00 00 B1 1D 00 00 ... E7 11 01 00
dd 7306,7601,55059,30943,1491,58205,7129,66990,87201,82752,22266,4119,82599,12471,6257,37992,48528,7603,28639,39070,66092,2401,2786,6236,699,49097,36424,10204,4545,3207,6570,70119
; 定义字类型数据 每2个字节一组 表示32年的公司雇员人数 偏移 data:432-495
; [01B0H],...,[01EFH]
dw 73,40,8,44,20,701,985,3,444,539,524,115,106,106,162,123,575,329,284,583,782,576,515,553,624,796,479,272,32,871,597,674
; 定义一串字节类型数据 偏移 data:496-511
; [01F0H],...,[01FFH]
; 77 65 6C 63 6F 6D 65 20 74 6F 20 6D 61 73 6D 21
db 'wx LoveCoder !'
; 定义一串字节类型数据 偏移 data:512-514
; [0200H],...,[0202H]
; 02 21 CA
db 02H,21H,0CAH
; 定义字类型数据 每2个字节一组 偏移 data:515-530
; [0203H],...,[0212H]
; 01 00 02 00 ... 60 EA
dw 60000,2,3,4,5,6,7,60000
; dd定义双字类型变量 每4字节一组 偏移 data:531-562
; [0213H],...,[0232H]
; 00 00 00 00 ... 00 00
dd 0,0,0,0,0,0,0,0
; 定义一串字节类型数据 偏移 data:563-579
; [0233H],...,[0243H]
; 77 65 6C 63 6F 6D 65 20 74 6F 20 6D 61 73 6D 21 00
db 'wx LoveCodera !',0
; 定义一串字节类型数据 偏移 data:580-595
; [0244H],...,[0253H]
; 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88
db 88H,88H,88H,88H,88H,88H,88H,88H,88H,88H,88H,88H,88H,88H,88H,88H
; 定义一串字节类型数据 偏移 data:596-611
; [0254H],...,[0263H]
; 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
db 11H,11H,11H,11H,11H,11H,11H,11H,11H,11H,11H,11H,11H,11H,11H,11H
; 定义一串字节类型数据 偏移 data:612-627
; [0264H],...,[0x273H]
; 57 65 6C 63 6F 6D 65 20 74 6F 20 6D 61 73 6D 21
db 'wx LoveCodera !'
; 定义一串字节类型数据 偏移 data:628-643
; [0274H],...,[0283H]
; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
db 16 dup(0)
; 定义一串字节类型数据 偏移 data:644-
; [0284H],...,[]
; 77 65 6C 63 ... 21 00
db 'LoveCoder 7ch!',0

data ends
;; (G :0012)

; segment 伪指令 段定义
table segment
; dup 操作符 用来进行数据的重复 偏移 data:0-335
; 定义一张32行的员工表格
; 年份4字节 空格 总收入4字节 空格 人数2字节
; [0000H],[0001H],...,[0014EH],[0014FH]
; ...
db 32 dup ('year summ ne ?? ')
table ends
;; (G :0012)

; segment 伪指令 段定义
stack segment
; 定义8个字类型数据 16字节类型数据 偏移 stack:0-15
; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
dw 0,0,0,0,0,0,0,0
stack ends

; segment 伪指令 段定义
; cs [代码段寄存器] 存放当前正在运行的程序代码所在段的段基值
code segment
; 指令标号 程序开始
start:
;; (U :0000,:000F)
;; 初始化数据
; 设置数据段 将data段装入数据段寄存器
; ds [数据段寄存器] 存放数据段的段基值
; 定义该空间当作数据段使用
mov ax,data
mov ds,ax
; 设置数据段 将table段装入附加段寄存器
; es [附加段寄存器]
; 定义该空间当作附加段使用
mov ax,table
mov es,ax
; 设置堆栈段 将stack段装入堆栈段寄存器
; ss [堆栈段寄存器] 存放堆栈段的段基值
; 定义该空间当作堆栈段使用
mov ax,stack
mov ss,ax
; 将栈顶指向16
; mov sp,16
mov sp,10h
;; (G :0012)

;; (U :0012,:0020)
;; 累加ds:0-15的值赋值给ax
; ax 通用寄存器
mov ax,0
; bx 通用寄存器 这里作为ds代码段寄存器的偏移地址值
mov bx,0
; cx 计数寄存器 循环次数
mov cx,8
sum1:
; [bx]表示数据段的偏移地址值即ds:[bx]
add ax,[bx]
add bx,2
; loop 执行循环
loop sum1
;; ax = 25ABH
;; (G :0022)

;; (U :0022,:003D)
;; 将ds:0-15倒序
mov ax,0
mov bx,0
mov cx,8
; 指令标号 s1循环开始
s1:
; 入栈 先把指针向上移动 然后把数据放进去
; [bx] 表示偏移地址值为bx
push [bx]
; bx增加 2个8字节
add bx,2
; loop 执行循环
loop s1
mov bx,0
; cx 计数寄存器 循环次数
mov cx,8
; 指令标号 s2循环开始
s2:
; 出栈 先把数据拿出来 然后把指针向下移动
pop [bx]
; bx增加 2个8字节
add bx,2
; loop 执行循环
loop s2
;; ds FF FF FF FF 00 00 00 00 EF CD AB 89 67 45 23 01
;; (G :003F)

;; (U :003F,:0057)
;; 将ds:16-20的字母变成大写 将ds:21-25的字母变成小写
mov ax,0
mov bx,16
mov cx,5
s3:
; 将ASCII码从ds:[bx]所指向的单元中取出
mov al,0[bx]
; 将al中的ASCII码转化为二进制的第五位置为0 变为大写字母
and al,11011111B
; 转变后的ASCII码写回原单元
mov 0[bx],al

; 将ASCII码从ds:[bx+5]所指向的单元中取出
mov al,5[bx]
; 将al中的ASCII码转化为二进制的第五位置为1 变为小写字母
or al,00100000B
; 转变后的ASCII码写回原单元
mov 5[bx],al

; bx加1 ds:bx指向下一个字母
inc bx
loop s3
;; ds ASCIIascii
;; (G :0059)

;; (U :0059,:006B)
;; 将ds:26-36的数据迁移到ds:37-47
mov ax,0
mov bx,26
mov si,0
mov cx,11
s4:
mov al,[bx+si]
mov 11[bx+si],al
inc si
loop s4
;; (G :006D)

;; (U :006D,:0081)
;; 将ds:48-95的数组首字母大写
mov ax,0
mov bx,48
; 三行字符串
mov cx,3
s5:
; 首字母在每一行的第三位
mov al,[bx+2]
; 将al中的ASCII码转化为二进制的第五位置为0 变为大写字母
and al,11011111B
mov [bx+2],al
; 每组字符串16个字节
add bx,16
loop s5
;; (G :0083)

;; (U :0083,:00A0)
;; 双重循环实现每行(3-7)4个字节类型数据变成大写字母
;; 将ds:96-143的数组字母大写
mov ax,0
; 用bx定位每行首个字符串位置
mov bx,96
; 三行字符串
mov cx,3
s6:
; 双重循环
; 将外层循环的cx值暂存
; ; [90H] 作为临时存放一个字类型数据的内存位置
; mov [90H],cx
; 入栈
push cx
; 每一行首字母的第三位开始 定位字符串中的每个元素
mov si,2
; 内层循环的次数 改变4个字节类型数据变成大写字母
mov cx,4
s7:
; 每一行首字母的第三位开始 共四个字节
mov al,[bx+si]
; 将al中的ASCII码转化为二进制的第五位置为0 变为大写字母
and al,11011111B
mov [bx+si],al
inc si
loop s7
; 换行 每组字符串16个字节 切换到下一行首字母位置
add bx,16
; 还原外层循环的cx值
; ; [90H] 作为临时存放一个字类型数据的内存位置
; mov cx,[90H]
; 出栈
pop cx
loop s6
;; (G :00A2)

;; (U :00A2,:00AB)
;; div 除法运算 10/4=2余1 10是被除数 4是除数 2是商 1是余数
;; 除数8位 一个字节 被除数16位(AX) 结果 商(AL) 余数(AH)
;; 除数16位 一个字型 被除数32位(DX+AX) 结果 商(AX) 余数(DX)
; dx 存储被除数高位字型数据
mov dx,01h
; ax 存储被除数高位字型数据
mov ax,05AEH
; bx 存储除数 字型数据(默认) 所以被除数为32位数据
; 除数不能为0,1 内存会溢出
mov bx,2
div bx
; (ax) = [(dx)*10000H+(ax)]/(bx)的商
; (dx) = [(dx)*10000H+(ax)]/(bx)的余数
;; ax 03E8H
;; dx 0001H
;; (G :00AD)

;; (U :00AD,:0109)
;; 企业员工平均工资计算
; 年份4字节 空格 总收入4字节 空格 人数2字节
; 年份的偏移基址 176 [B0H]
; 每年份+4个字节数据 每年总收入+4个字节数据
mov bx,0
; 每年人数+2个字节数据
mov si,0
; table表格换行的累加基址 +16
mov di,0
; 32次循环
mov cx,32
s8:
; 存放年份 每4个字节类型一年份数据
; 年份总数据量 32条*4字节 每4个字节一年份数据 共32个年份
mov al,[0B0H+bx]
mov es:0H[di],al
mov al,[0B1H+bx]
mov es:1H[di],al
mov al,[0B2H+bx]
mov es:2H[di],al
mov al,[0B3H+bx]
mov es:3H[di],al
; 存放公司总收入 每双字类型一份总收入数据
; 总收入总数据量 32条*4字节 每双字类型即4个字节 共32个年份
mov ax,[130H+bx]
mov dx,[132H+bx]
; 4H 存放空格
; 高位存储高字类型 低位存储低字类型
mov es:5H[di],ax
mov es:7H[di],dx
; 9H 存放空格
; 总收入总数据量 32条*2字节 每字类型即2个字节 共32个年份
mov ax,[1B0H+si]
mov es:0AH[di],ax
; 0CH 存放空格
; div 除法运算 10/4=2余1 10是被除数 4是除数 2是商 1是余数
; 除数16位 一个字型 被除数32位(DX+AX) 结果 商(AX) 余数(DX)
; 被除数是总收入 低位存放在ax 高位存放在dx
mov ax,[130H+bx]
mov dx,[132H+bx]
; 结果 商存放在ax
div word ptr [1B0H+si]
mov es:0DH[di],ax
; 下个年份指标移动
add bx,4
add si,2
add di,16
loop s8
;; (G :010B)

;; (U :010B,:0120)
;; offset 标号的偏移地址
mov ax,0001H ; [:010B +3] B80100
mov bx,0001H ; [:010E +3] BB0100
; 标号 mov ax,bx 占两个字节
s9:
add bx,bx ; 03DB
; 将s9的内存偏移地址存储在si s9标记的偏移值为0111
mov si,offset s9 ; [:0113 +3] BE1101
; 将s10的内存偏移地址存储在di s10标记的偏移值为011F
mov di,offset s10 ; [:0116 +3] BE1F01
; 将s9的内存偏移地址上的指令内容存储到s10的内存偏移地址上
mov ax,cs:[si]
mov cs:[di],ax
; 标号 nop 占一个字节
s10:
nop ; 90
nop ; 90
;; bx = 0004H
;; (G :0121)

;; (U :0121,:0129)
;; jmp 无条件转移指令
; jmp short 标号
; 段内短转移 对IP的修改范围 -128~127
mov ax,0 ; [:0121 +3] B80000
; 二进制解析 EB跳转 03偏移量 最后跳转到(:0124+2+3=:0129)
jmp short s11 ; [:0124 +2] EB03
add ax,1 ; [:0126 +3] 83C001
s11:
inc ax ; [:0129 +1] 40
;; ax = 0001H
;; (G :012A)

;; (U :012A,:0238)
;; jmp far ptr 标号
; 用标号的段地址和偏移地址修改cs和ip
mov ax,0 ; [:012A +3] B80000
mov bx,0 ; [:012D +3] BB0000
jmp far ptr s12 ; [:0130 +5] EA3502AC07
; 定义 256 个字数据
db 256 dup (0)
s12:
add ax,1 ; [:0235 +4] 83C001
inc ax ; [:0238 +1] 40
;; (G :0239)

;; (U :0239,:0247)
;; jmp 16位寄存器
;; jmp word ptr 内存单元地址(段内转移)
mov ax,0246H ; [:0239 +3] B84602
mov ds:[0],ax
; 跳转到段内:0246偏移地址
jmp word ptr ds:[0]
add ax,1
inc ax ; [:0246 +1] 40
;; ax = 0247H
;; (G :0247)

;; (U :0247,:0259)
; jmp dword ptr ds:[0] 段间转移
; 段偏移地址
mov ax,025AH ; [:0247 +3] B85A02
mov ds:[0],ax ; [:024A +3] A30000
; 段基址
mov ax,cs ; [:024D +2] 8CC8
; 16位数据 字类型数据 赋值给ds:[2]的地址上
mov word ptr ds:[2],ax ; [:024F +3] A30200
; 跳转到 [cs]:025A 处
jmp dword ptr ds:[0] ; [:0252 +4] FF2E0000
add ax,1 ; [:0256 +3] 83C001
inc ax ; [:0259 +1] 40
;; ax = 025BH
;; (G :025A)

;; (U :025A,:0269)
;; jcxz 有条件跳转指令 cx=0则跳转 所有有条件跳转都是短转移 -128~127
mov ax,0
mov bx,0
; 从ds:[0]开始查找 直到找到数据为0的内存地址
s13:
mov ch,0 ; [:0261 +2] B500
mov cl,[bx] ; [:0263 +2] 8A0F
; 检查cx的值 为0则查找结束 跳到s14标号的位置
jcxz s14 ; [:0265 +2] E303
inc bx ; [:0267 +1] 43
jmp short s13 ; [:0268 +2] EBF7
s14:
mov dx,bx ; [:026A +2] 8BD3
;; dx = 0004H
;; (G :026B)

;; (U :026B,:0286)
;; 欺骗跳转
;; 利用 (jmp short 标号) 以偏移跳转的特性
mov ax,0
; 跳转到s16 占用2个字节 偏移+2H
jmp short s16 ; [:026F +2] EB02
s15:
; 跳转到s20 占用2个字节 偏移+15H
jmp short s19 ; [:0271 +2] EB15
s16:
nop ; [:0273 +1] 90
nop ; [:0274 +1] 90
; 将s18的内存偏移地址上的指令内容存储到s16的内存偏移地址上
mov di,offset s16 ; [:0275 +3] BF7302
mov si,offset s18 ; [:0278 +3] BF8402
mov ax,cs:[si]
mov cs:[di],ax
; 此时s16的上的指令是 EBFC EB跳转 -3偏移
jmp short s16 ; [;0281 +2] EBF0
s17:
nop ; [:0283 +1] 90
nop ; [:0284 +1] 90
s18: jmp short s17 ; [:0285 +2] EBFC
s19:
mov ax,1 ; [:0287 +6] B80100
;; ax = 0001H
;; (G :0289)

;; (U :0289,:0292)
;; 修改显示器
; 内存地址空间中 B8000H~BFFFFH 共32KB的空间 为80*25彩色字符模式的显示缓冲区
;
; 行列偏移
; 0000~0001 第1行第1列
; 0002~0003 第1行第2列
; ... 共25行 共80列
; 009E~009F 第1行第80列
; 行列偏移
; 00A0~00A1 第2行第1列
; 00A2~00A3 第2行第2列
; ... 共25行 共80列
; 013E~013F 第2行第80列
;
; 第1行
; 0000 0001 ... 009E 009F
; 第2行
; 00A0 00A1 ... 013E 013F
; ... 共25行
; 第25行
; 0F00 0F01 ... 0F9E 0F9F
;
; 一个像素点对应两个字节 样式+内容 高位放样式 低位放内容
; CA42 = 闪烁红底高亮绿字B
;
; 样式
; 字节对应的格式 二进制8位
; RGB R红色 G绿色 B蓝色
; 7 6 5 4 3 2 1 0
; BL R G B I R G B
; 闪烁 背景 高亮 前景
; 闪烁红底绿字 11000010B = C2H
; 黑底白字 00000111B = 07H
; 闪烁红底高亮绿字 11001010B = CAH
;
; 内容
; ASCII码对应显示内容
;
; 显示缓冲区的基址
mov ax,0B800H
mov es,ax
; 第25行第二列像素点
mov bx,0F02H
; 闪烁红底高亮绿字B
mov es:[bx],0CA42H
;; (G :0296)

;; (U :0296,:02C6)
; 字符串偏移地址 01F0H
; 样式偏移地址 0200H
; 在屏幕中间显示3行不同样式的 wx LoveCodera ! 字符串
; 目标地址屏幕第12行中间的显存的起始位置
mov ax,0B872H
; xor 判断两个值是否不同
; bx清零,用来索引目标列计数
xor bx,bx
; 用来索引来源字符串的起始地址
; 第一个字符存储在ds:[01F0H] 共三行 每行16个字符
mov si,01F0H
; 用来索引来源样式的起始地址
; 第一行样式存储在ds:[0200H] 共三行
mov di,0200H
; s20循环控制行数 显示三行字符串
mov cx,3
s20:
; cx压入堆栈 保存外循环相关寄存器的值
push cx
; ax压入堆栈 将寄存器空出来
push ax
; si压入堆栈 每行来源字符串相同
push si

; 显存位置存储在es段寄存器上 用来访问内存地址
mov es,ax

; s21循环控制列字符串有 16个字节=10H个字节
mov cx,10h
s21:
; 将字符取出 放在字空间的高位字节空间上
mov al,[si]
; 将颜色取出 放在字空间的低位字节空间上
mov ah,[di]
; 在目标列上填充 样式+内容
mov es:[bx],ax
; 来源字符位置加1
add si,1
; 显示下一列位置加2 1行16列 每列2个字节 样式不变
add bx,2
loop s21
; (G: 02BB)
; 下一行来源字符串的起始地址还原
pop si
; 下一行来源样式加1
add di,1
; 目标列增加160位 显示器每行160位
pop ax
; 在段地址中加0AH 相当于在偏移地址中加了0A0H=160字节=160d
add ax,0AH
; 用来索引目标列计数还原
xor bx,bx
; 外层循环计数还原
pop cx
loop s20
;; (G :02C8)

;; (U :02C8,:02DE)
; call 标号
; call word ptr 内存单元地址
; call dword ptr 内存单元地址
; 将当前的IP或CS和IP压入栈中 然后转移
; ret 用栈中的数据 修改IP的内容 从而实现近转移
; retf 用栈中的数据 修改CS和IP的内容 从而实现远转移
mov ax,1
mov bx,1
mov cx,3
; 执行流程1 跳转到s22标号位置 将地址压入堆栈
call s22
; 执行流程4
mov ax,3
; 执行流程5 跳转到s23标号位置
jcxz s23
; 执行流程2 执行循环
s22:
add ax,ax
loop s22
; 执行流程3 返回到call在堆栈中的偏移
ret
; 执行流程6
s23:
mov bx,0
;; ax = 0003H
;; bx = 0000H
;; (G :02E1)

;; (U :02E1,:0308)
;;模块化程序设计
; ds:si 指向第一组word单元
; ds:di 指向第二组dword单元
mov si,0203H
mov di,0213H
mov dx,0
; 每组8条数据 每组单元的立方结果存储在第二组单元里
mov cx,8
s24:
mov bx,[si]
call s25
mov [di],ax
mov 2[di],dx
add si,2
add di,4
loop s24
jmp short s26
; 实现三次方
s25:
mov ax,bx
; 执行 8 位操作数与 AL 寄存器的乘法 乘积 AX
; 执行 16 位操作数与 AX 寄存器的乘法 乘积 DX:AX
; 执行 32 位操作数与 EAX 寄存器的乘法 乘积 EDX:EAX
mul bx
mul bx
ret
s26:
mov ax,ax
;; ax = 8000H
;; dx = 9625H
;; (G :030A)

;; (U :030A,:0343)
;; 定义函数 在屏幕任意位置输出一串带样式的字符串
; 存放行号(1-25)
mov dh,8
; 存放列号(1-80)
mov dl,3
; 存放样式属性
mov cl,00000010B
; 开始
call s27
; 函数开始
s27:
; 每行 80*2 = 160个字节 = 0A0H个字节内容
mov al,0A0H
; dh减1乘al 表示在第dh行的起始单元位置偏移地址 乘积存储在ax
dec dh
mul dh
; bx 存储 定位号的行起始单元位置偏移地址
mov bx,ax
; 每列 每个字符 占2个字节
mov al,2
; dl减1乘al 表示在第dl列上的偏移地址 乘积存储在ax
dec dl
mul dl
; 此时bx存放的是第dh行dl列上的偏移地址
add bx,ax
; es 存放显存开始的地址
mov ax,0B800H
mov es,ax
; 来源字符的偏移单位数
mov si,0
; 目标显示位置的偏移单位数
mov di,0
; 目标样式
mov ah,cl
; ch设置成0 和 ch组合 判断是否跳出程序
mov ch,0
s28:
; 0233H 字符串第一个字母的偏移地址
mov cl,ds:[0233H+si]
mov al,cl
; 当 cl=0 时 cx=0 函数退出跳到 s29
jcxz s29
; ah+al=样式+内容 目标位置上的数据
mov es:[bx+di],ax
; si自增 来源每个字符加1个偏移单位
inc si
; di自增2 目标位置2个偏移单位
add di,2
; 循环执行
jmp short s28
s29:
mov ax,ax
;; (G :0345)

;; (U :0345 :035E)
;; 标志寄存器 flag寄存器
; of:OV=1 NV=0; sf:NG=1 PL=0; zf:ZR=1 NZ=0; pf:PE=1 PO=0; cf:CY=1 NC=0; df:DN=1 UP=0;
; zf 零标志 最近操作结果为0 运算指令
; pf 奇偶标志 结果的所有二进制位中1的个数是否为偶数
; sf 符号标志 操作是否为负数
sub al,al
;; zf=ZR=1 pf=PE=1 sf=PL=0
; cf 进位标志 对于无符号数sf=PL=0 是否产生了进位
mov al,98H
add al,al
; 98H相加结果溢出 向CF借位
;; al=30H cf=CY=1
add al,al
;; al=60H cf=NC=0
; of 溢出标志 对于有符号数sf=NG=1 是否产生了溢出

;; (U :03 :03)
;; adc 带进位加法指令
; adc ax,bx 表示 ax=(ax)+(bx)+cf
; 计算 1EF0001000H+2010001EF0H 结果放在 ax bx cx 中
mov ax,001EH
mov bx,0F000H
mov cx,1000H
; 最低位相加 没有产生进位
add cx,1EF0H
; cx = 2EF0H cf=NC=0
; 次高位相加 产生进位
adc bx,1000H
; bx = 0000H cf=CY=1
; 高位相加 加上进位cf值
adc ax,0020H
; ax = 003FH cf=NC=0
;; (G :0361)

;; (U :0361 :036B)
;; sbb 带借位减法指令
; sbb ax,bx 表示 ax=(ax)-(bx)-cf
; 计算 003E1000H+00202000H 结果放在 ax bx cx 中
mov bx,1000H
mov ax,003EH
; 低位相减 产生借位
sub bx,2000H
; bx = F000H cf=CY=1
; 高位相减 减去进位cf值
sbb ax,0020H
; ax = 001DH cf=NC=0
;; (G :036E)

;; (U :036E :0391)
;; 测试 两组数据相加
; 第一组数据
mov si,0244H
; 第二组数据
mov di,0254H
; 每组
mov cx,8
call s30
jmp short s32
s30:
push si
push di
push cx
; 将CF设置为0
sub ax,ax
s31:
mov ax,[si]
adc ax,[di]
mov [si],ax
inc si
inc si
inc di
inc di
loop s31
pop cx
pop di
pop si
ret
s32:
nop
; ax = 9999H
;; (G :0392)

;; (U :0392 :03A8)
;; cmp 比较指令 不保存结果 仅影响flag的相关数据
; of:OV=1 NV=0; sf:NG=1 PL=0; zf:ZR=1 NZ=0; pf:PE=1 PO=0; cf:CY=1 NC=0; df:DN=1 UP=0;
mov ax,8
mov bx,8
cmp ax,ax
; of=0 sf=0 zf=1 pf=1 cf=0
mov ax,8
mov bx,3
cmp ax,bx
; of=0 sf=0 zf=0 pf=1 cf=0
mov ax,3
mov bx,8
cmp ax,bx
; of=0 sf=1 zf=0 pf=0 cf=1
;; (G :03AA)

;; (U :03AA :03B8)
;; 条件转移指令
; je 等于则转移 zf=1
; jne 不等于则转移 zf=0
; jb 低于则转移 cf=1
; jnb 不低于则转移 cf=0
; ja 高于则转移 cf=0 zf=0
; jna 不高于则转移 cf=1 zf=1
;; 编程实现 (ah)=(bh) 则 (ah)=(ah)+(ah) 否则 (ah)=(ah)+(bh)
mov ah,1
mov bh,2
cmp ah,bh
je s33
add ah,bh
jmp short s34
s33:
add ah,ah
s34:
nop
;; (G :03B9)

;; (U :03B9 :03C8)
;; 将第一个串(264H)复制到后面的空间中(274H)
;; DF 方向标志
; DF=0 每次操作后 si di 递增
; DF=1 每次操作后 si di 递减
; movsb 串传送操作 以字节单位传递 每次执行完改变di,si的值
; ((es)*16+(di))=((ds)*16+(si))
; movsw 串传送操作 以字单位传递 每次执行完改变di,si的值
; rep movsb 根据cx的值 重复执行后面的串传送指令
; cld 将DF标志寄存器设置为0
; std 将DF标志寄存器设置为1
mov ax,ds
mov es,ax
mov si,264H
mov di,274H
mov cx,16
; 将DF标志寄存器设置为0
cld
; 根据cx的值 重复执行后面的串传送指令
; movsb 串传送操作 以字节单位传递 每次执行完改变di,si的值
; ((es)*16+(di))=((ds)*16+(si))
; ds:[si] 复制到 es:[di] 重复16次
rep movsb
;; (G :03C9)

;; (U :03C9 :043D)
;; 中断
jmp short s38
;; 0000:0200H处安装中断程序 代码
; 自定义函数开始 在屏幕任意位置输出一串带样式的字符串
s35:
push di ; [:03CB +1] 57 ; [0000:0200 +1] 57
push si ; [:03CC +1] 56
push es ; [:03CD +1] 06
push dx
push cx
push bx
push ax
; 存放行号(1-25)
mov dh,10
; 存放列号(1-80)
mov dl,30
; 存放样式属性 闪烁红底高亮绿字
mov cl,11001010B
; 每行 80*2 = 160个字节 = 0A0H个字节内容
mov al,0A0H
; dh减1乘al 表示在第dh行的起始单元位置偏移地址 乘积存储在ax
dec dh
mul dh
; bx 存储 定位号的行起始单元位置偏移地址
mov bx,ax
; 每列 每个字符 占2个字节
mov al,2
; dl减1乘al 表示在第dl列上的偏移地址 乘积存储在ax
dec dl
mul dl
; 此时bx存放的是第dh行dl列上的偏移地址
add bx,ax
; es 存放显存开始的地址
mov ax,0B800H
mov es,ax
; 来源字符的偏移单位数
mov si,0
; 目标显示位置的偏移单位数
mov di,0
; 目标样式
mov ah,cl
; ch设置成0 和 ch组合 判断是否跳出程序
mov ch,0
s36:
; 0233H 字符串第一个字母的偏移地址
mov cl,ds:[0284H+si]
mov al,cl
; 当 cl=0 时 cx=0 函数退出跳到 s29
jcxz s37
; ah+al=样式+内容 目标位置上的数据
mov es:[bx+di],ax
; si自增 来源每个字符加1个偏移单位
inc si
; di自增2 目标位置2个偏移单位
add di,2
; 循环执行
jmp short s36
s37:
pop ax
pop bx
pop cx
pop dx
pop es
pop si
pop di
iret ; [0000:0244 +1] CF
; 自定义函数结束
s35end:
nop ; [:0410 +1] 90
s38:
; 将自定义函数编码复制到0000:0200H处
; 保存数据段寄存器
push ds
; 要复制的编码基址
mov ax,cs
mov ds,ax
; 要复制的编码偏移地址
mov si,offset s35
; 0:200H 的基址
mov ax,0
mov es,ax
; 0:200H 的偏移地址
mov di,200H
; 传输长度
mov cx,offset s35end - offset s35
; 设置传输方向为正
cld
; 根据cx的值 重复执行后面的串传送指令
; movsb 串传送操作 以字节单位传递 每次执行完改变di,si的值
; ((es)*16+(di))=((ds)*16+(si))
; ds:[si] 复制到 es:[di] 重复16次
rep movsb
; 还原数据段寄存器
pop ds
; 中断向量
mov ax,0 ; [:0428 +3] B80000
mov es,ax
mov word ptr es:[7CH*4],200H
mov word ptr es:[7CH*4+2],0
;; 执行中断 int 当前的 标志寄存器 CS IP 会自动压栈
int 7ch ; [:043B +2] CD7C
nop ; [:043D +1] 90
;; 在屏幕上打印 welcome to 7ch!
;; (G :043E)

;; (U :043E :04)
;; int 10H
;; BIOS中断例程 10号中断例程
;; 10号中断例程的2号子程序
; 光标位置设置
mov ah,2
; 第0页
mov bh,0
; 第5行
mov dh,5
; 第12列
mov dl,12
int 10h
nop ; [:0448 +1] 90
;; 10号中断例程的9号子程序
; 在光标位置显示字符
mov ah,9
; 第0页
mov bh,0
; 样式
mov bl,11001010B
; 字符
mov al,'a'
; 字符重复数
mov cx,3
int 10h
nop ; [:0456 +1] 90
;; (G :0456)

;; (U :0456 :0478)
;; shl 逻辑左移
;; shr 逻辑右移
;; BCD码
; 数码: 0 1 2 3 4
; BCD码: 0000 0001 0010 0011 0100
; 取出70H端口的08H上的月值
mov al,08H
out 70H,al
in al,71H
; BCD码 ah 十位数 al个位数
mov ah,al
mov cl,4
; ah右移4位获得十位数
shr ah,cl
; al and算法清空十位数获得个位数
and al,00001111B
; ASICC码 30H=0
add ah,30H
add al,30H
; 显示器基址
mov bx,0B800H
mov es,bx
; 月份打印到显示器 12行 40列
mov byte ptr es:[160*12+40*2],ah
mov byte ptr es:[160*12+40*2]+2,al
nop
;; 12行 40列 显示当前月份
;; (G :0479)

;; 键盘读取 TODO::
int 16H

; 中断 程序返回
mov ax,4C00H
int 21H

; ends 伪指令 段结束 与segment对应
code ends

; end 伪指令 程序结束
end start
您的支持将鼓励我继续创作