汇编语言(王爽)P200课程设计的啰嗦答案.

 %include “protect.inc” ; 常量, 宏, 以及一些说明

# 常见汇编代码 #

这个程序是我看这本书第五天时编的.
我刚学汇编5天—-纯粹的一个初学者,编写这个程序不是一帆风顺,遇到问题没有人去咨询没有人去商量,只能自己一点点的摸索,真的痛苦不堪!.
两个不眠之夜,多少次想放弃汇编不去学了,但是最后还是坚持的做下去,最后结果是我成功了!!!!!!!我很自豪.

org  0x0500

  1. 编写程序:比较AX,BX,CX中带符号数的大小,将最大的数放在AX中

相当高兴,现在将这个东西放在这里和大家分享.里边有很多注释,话语是我一贯的作风—-啰嗦!

; 实模式

        code segment

还有一个啰嗦就是程序比较啰嗦,有很多地方可以简化,但是我还要继续看后边的中断部分这里就不在进行了.但是我在下边提出了一些关于简化的想法,只要有机会我还是会按照我的想法重新做的.毕竟做一个纯粹的程序员不是通过编程序而去混饭吃,而是见程序当做音乐的音符去编写不留杂音的.好的程序员他编写的程序在具有同等功能的其他程序中他的程序最”安全”!最”精炼”

 jmp SetUp_seg

        assume cs:code

好了不不再啰嗦了,有兴趣的继续看下边的程序吧!

;——————————————————————————————
times  0x10-($-$$) db 0
KernelSize equ 512

        mov ax,32

下边的源代码我用masm for
windows调试的,希望对初学者有帮助,同时希望高手提出宝贵的意见,谢谢!!!

; 此处地址: $ -> 0x500

        mov bx,74

 

MCRNumberDW:   dd 0
MemChkBuf: times 256 db 0
MemSizeDW:   dd 0

        mov cx,23

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MemInfo1  db “________________________Memory 
Allocation_______________________”
MemInfoLen1 equ $ – MemInfo1
MemInfo2  db ” BaseAddrLow   BaseAddrHigh   LengthLow     LengthHigh    
Type”
MemInfoLen2 equ $ – MemInfo2
;
SetupMessage db “Running Setup … “
SMLength equ $ – SetupMessage

        sort: cmp ax,bx

;对下边程序的文字说明

KernelAddr equ 12*1024  ;将kernel加载到这里( 物理地址:12*1024 )
CylindTotalNum equ 80  ;软盘正反面都面有80个柱面
SectorTotalNum  equ 18  ;软盘的每个磁道有18个扇区
SectorByte equ 512  ;软盘的每个扇区有512 byte

              jge X          ;如果ax大于等于bx就跟cx比较

;data2—->a seg
;data3—->b seg
;c1—->d 运算之后的数据存放在d
;c1 最后所有的数据都汇总的c1,当然也可以声调这个步骤直接写入显存.
;在a.b.d.中重新写入的数据是有格式的,数据的第一位是数据的长度(字节数),这个位的生成是在dtoc中
;        
最后一位是结束位表示这个字节结束了,结束位的标志是’00’,当然这个字节可以省略
;show_str代码中也有关于字符结束的认定,就是当它读入一个字符为’00’时认为已经读完数据开始ret
;在strcls中设置的全部输出为空’20h’,在2001中设置字节值为’00’
;data2/data3—–>c1  c1仅仅作为一个中间方.
;strnote 一个字作为一个记录,低位为长度,高位为起始地址,
;screndata 每次用完数据要整理一次全部变成1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DriveNo  db 0
HeadNo  db 0
CylindNo db 0
SectorNo db 1
SectorNum db 0
LoadSeg  dd 0

              xchg ax,bx

assume cs:li
data1 segment
 db ‘1975’,’1976′,’1977′,’1978′,’1979′,’1980′,’1981′,’1982′,’1983′
 db ‘1984’,’1985′,’1986′,’1987′,’1988′,’1989′,’1990′,’1991′,’1992′
 db ‘1993’,’1994′,’1995′
 ;21个字符串     合计 84 byte          54h
data1 ends

;——————————————————————————————

            X:cmp ax,cx      ;如果ax大于cx

data2 segment
 dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
 dd
345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
 ;21个dword数据  合计 84 byte          54h
data2 ends

PrintBlank:
 push ax
 push bx
 mov bx, 000Ch
 mov al, ‘ ‘
 mov ah, 0Eh
 int 10h
 pop bx
 pop ax
 ret
;——————————
PrintRL:
 push ax
 push bx
 push cx
 push dx
 mov ah, 03h
 int 10h
 inc dh
 mov dl, 0
 dec ah
 int 10h 
 pop dx
 pop cx
 pop bx
 pop ax
 ret
;—————————— 

              jge Y

data3 segment
 dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
 dw 11542,14430,15257,17800
 ;21个word数据   合计 42 byte 
data3 ends
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;栈
stack segment
 dd 64 dup(0ffffh)
stack ends
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;栈
a segment
 db 160 dup(0)
a ends

PrintMesg:    ; 输出字符串函数
 push bx
 push es
 mov ax, 00h
 mov es, ax
 mov ax, 1301h  ; AH = 13,  AL = 01h
 mov bx, 000Ch  ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
 int 10h   ; 10h 号中断
 pop es
澳门新葡萄京官网注册 , pop bx
 ret
;——————————
PrintHex:    ; 以十六进制的形式输出 AL
 push bx
 push cx
 push dx
 mov cx, 2
 mov dh, al   ; 用 dh 保存 al 的值
 shr al, 4   ; 先输出高位
 mov ah, 0Eh
   .hex1:
 and al, 0Fh
 add al, ‘0’
 cmp al, ‘9’
 jbe .hex2
 add al, 7   ; al 中的值大于 9 ,此时 al 已经 加了48
   .hex2:
 mov bx, 000Ch  ; 字符属性
 int 10h
 mov al, dh
 dec cx
 jnz .hex1
 pop dx
 pop cx
 pop bx
 ret
;——————-
ReadFloppy:
 push es
.1 
 mov ax, word [LoadSeg]
 mov es, ax
 mov bx, 0
 mov ah, 02h
 mov al, [SectorNum]
 mov dl, [DriveNo]
 mov dh, [HeadNo]
 mov ch, [CylindNo]
 mov cl, [SectorNo]
 int 13h
 jc .1

              xchg ax,cx

b segment
 db 112 dup(0)
b ends

 pop es
 ret
;—————————————————————————-

            Y:int 21h

d segment
 db 160 dup(0)
d ends

SetUp_seg:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ax, 8fc0h
 mov ss, ax
 mov sp, 0FFFFh

              mov ax,4c00h

c1 segment
 db 2001 dup(11h)
c1 ends

 mov dx, 0200h  ; dh -> 行号  dl -> 列号
 mov bp, SetupMessage ; ES:BP = 串地址(ES == 0000h)
 mov cx, SMLength
 call PrintMesg

        code ends

;;;li主程序的开始;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
li segment
start:
 mov bx,data1
 mov bx,data2
 mov bx,data3
 mov bx,a
 mov bx,b
 mov bx,c1
 mov bx,d
 mov bx,0   
;;上边这几段可以省略的,这些是为了在程序开始的时候记录存储空间的

 ; 调用int 15h 中断获得内存使用情况,保存到 MemChkBu 中
 mov ebx, 0
 mov di, MemChkBuf
Chk_loop:
 mov eax, 0E820h
 mov ecx, 20
 mov edx, 0534D4150h
 int 15h
 jc Chk_fail
 add di, 20
 inc dword [MCRNumberDW]
 cmp ebx, 0
 jne Chk_loop
 jmp Mem_display
Chk_fail:
 mov byte [MCRNumberDW], 0

            end sort

                  ;;各个段地址,以便在出现问题的时候进行内存数据查询.
 mov ax,stack
 mov ss,ax
 mov sp,120
;;;;;;;data2—->a seg begin
;ax 传递要转换的数低16位
;dx 传递要转换的数高16位
;ds:si 传递转换后的数据存放首地址
;字符返回直接存储到制定的地址空间,存储方式为第一位指定的了字符的长度,后边为字符,字符最后一位用0结束.
;si 返回输出字符的最后一位的下一位.
;后面还有一段排序用的函数.

 ; 显示内存可用情况
Mem_display:
; mov al ,byte [MCRNumberDW]    ;memory info 个数
; call  PrintHex
 call PrintRL

2.要求对键盘输入的小写字母用大写字母显示出来

;bp 仅仅作为偏移地址计算
 mov ax,a
 mov ds,ax
 mov si,0
 mov di,0
 mov cx,21
data2a:
 push ds          ;;;push ds in 1
 mov bx,data2
 mov ds,bx
 mov ax,ds:[di]
 add di,2
 mov dx,ds:[di]  ;;传递ax,dx数值
 add di,2
 pop ds           ;;;pop ds out 1
 call dtoc
 loop data2a
;;;;;;;data2—->a seg转换排序整理结束;;;;;;;;;;;;;;;;;;;;;;;ok
;;;;;;;;data3—->b seg begin
 mov ax,b
 mov ds,ax
 mov si,0
 mov di,0
 mov cx,21
data3b:
 push ds          ;;;push ds in 2
 mov bx,data3
 mov ds,bx
 mov ax,ds:[di]
 add di,2
 mov dx,0   ;;传递ax,dx:00数值
 pop ds           ;;;;push ds in 2
 call dtoc
 loop data3b
;;;;;;;;data3—->b seg转换排序整理结束; 
;;;;;;;;data2/data3—–>c1进行除法运算
;ax 被除数dword L16bit
;dx 被除数dword H16bit
;cs 除数
;返回值ax 商dword L16bit
;返回值dx 商dword H16bit
;返回值cs 余数
 mov cx,21
 mov di,0
 mov si,0
d23c1:
 push cx                     ;push cx1    
 mov bx,data2
 mov ds,bx
 mov ax,ds:[si]
 add si,2
 mov dx,ds:[si]            ;;;传递AX,DX,CX数据进行除法运算
 add si,2
 mov bx,data3
 mov ds,bx
 mov cx,ds:[di]
 call divdw
 mov bx,c1
 mov ds,bx
 mov ds:[di],ax            ;;传递ax数据进行类型变换
 add di,2
 pop cx         ;pop cx1
 loop d23c1
;;;;;;;;data2/data3—–>c1进行除法运算结束 ;;;;;;;;;;;;;;;ok
;;;;;;;;c1—->d seg begin
;ax 传递要转换的数低16位
;dx 传递要转换的数高16位
;ds:si 传递转换后的数据存放首地址
 mov cx,0ffffh
 mov bx,d
 mov ds,bx
 mov si,0
 mov di,0
 mov cx,21
ok1:
 push ds     ;push ds 1
 mov bx,c1
 mov ds,bx
 mov ax,ds:[di]
 add di,2
 mov dx,0
 pop ds       ;pop ds 1
 call dtoc
 loop ok1 
;;;;;;;c1—->d seg转换排序整理结束;
;;;;;;;;;;;;; c1 数据整理begin;;;;;;;;;;;;;;;;;;;;;;;;;
 mov ax,c1
 mov ds,ax
 mov si,0
 mov al,20h     ;;输入空格屏幕没有显示
 mov cx,2000
strdatacls: 
 mov ds:[si],al
 inc si
 loop strdatacls
 mov ds:[si],00h
;;;;;;;;;;;;; c1 数据整理end ;;;;;;;;;;;;;;;;;;;;;;;;;;ok
;;;;;;;;;;;;;;;;data1中数据写入c1中;;;;;;;;;;;;;;;;;;;begin
 mov ax,data1
 mov ds,ax
 mov si,0
 mov ax,c1
 mov es,ax
 mov di,0ffb2h      
;;初始位置应该是0ffb0h,这里的0ffb4h多了4字节为了让显示的位置好看些
 mov cx,21           ;初始化
data1toc1: 
 push cx
 mov cx,4
data1toc1in: 
 mov al,ds:[si]
 inc si
 mov es:[di+80],al   
;;这里用的是di+80,由于前边的初始化我们可以这么做,要不读取每段数据的第一值需要

 mov dx, 0300h  ; dh -> 行号  dl -> 列号
 mov bp, MemInfo1  ; ES:BP = 串地址(ES == 0000h)
 mov cx, MemInfoLen1
 call PrintMesg
 mov dx, 0500h  ; dh -> 行号  dl -> 列号
 mov bp, MemInfo2  ; ES:BP = 串地址(ES == 0000h)
 mov cx, MemInfoLen2
 call PrintMesg

        code segment

                              ;;再重新编写一段代码然后再进入loop循环.
 inc di
 loop data1toc1in
 add di,76
 pop cx
 loop data1toc1
;;;;;;;;;;;;;;;;data1中数据写入c1中;;;;;;;;;;;;;;;;;;;;;;ok
;;;;;;;;;;;;;;;; a 中数据写入c1 中 ;;;;;;;;;;;;;;;;;;;;;;
 mov ax,a
 mov ds,ax
 mov si,0
 mov ax,c1
 mov es,ax
 mov di,0ffc8h
 mov cx,21           ;初始化
atoc1: 
 push cx
 mov cx,ds:[si]
 mov ch,0
 mov bx,cx
 inc si
atoc1in: 
 mov al,ds:[si]
 inc si
 mov es:[di+80],al
 inc di
 loop atoc1in
 inc si
 sub di,bx
 add di,80
 pop cx
 loop atoc1
 
;;;;;;;;;;;;;;;; a 中数据写入c1 中 ;;;;;;;;;;;;;;;;;;;;;;ok
;;;;;;;;;;;;;;;; b 中数据写入c1 中 ;;;;;;;;;;;;;;;;;;;;;;
 mov ax,b
 mov ds,ax
 mov si,0
 mov ax,c1
 mov es,ax
 mov di,0ffdch
 mov cx,21           ;初始化
btoc1: 
 push cx
 mov cx,ds:[si]
 mov ch,0
 mov bx,cx
 inc si
btoc1in: 
 mov al,ds:[si]
 inc si
 mov es:[di+80],al
 inc di
 loop btoc1in
 inc si
 sub di,bx
 add di,80
 pop cx
 loop btoc1 
;;;;;;;;;;;;;;;; b 中数据写入c1 中 ;;;;;;;;;;;;;;;;;;;;;;ok
;;;;;;;;;;;;;;;; d 中数据写入c1 中 ;;;;;;;;;;;;;;;;;;;;;;
 mov ax,d
 mov ds,ax
 mov si,0
 mov ax,c1
 mov es,ax
 mov di,0fff0h
 mov cx,21           ;初始化
dtoc1: 
 push cx
 mov cx,ds:[si]
 mov ch,0
 mov bx,cx
 inc si
dtoc1in: 
 mov al,ds:[si]
 inc si
 mov es:[di+80],al
 inc di
 loop dtoc1in
 inc si
 sub di,bx
 add di,80
 pop cx
 loop dtoc1 
;;;;;;;;;;;;;;;; b 中数据写入c1 中 ;;;;;;;;;;;;;;;;;;;;;;ok

 mov dx, 0
 mov dl, byte [MCRNumberDW]
 cmp dl , 0
 je Read_floppy
 mov si, MemChkBuf
 add si,4
  Mem_L1:
 call PrintRL
 call PrintBlank
 call PrintBlank
 mov cx, 0
  Mem_L2:
 dec si
 mov al, byte [si] 
 call PrintHex 
 inc cx
 test cx, 0003h
 jne Mem_L2

        assume cs:code

;;;;;;;;;;;;;;;;;;调用写入显存数据程序;;;;;;;;;;;;;;;;;;;;;;;;;
 mov ax,c1
 mov ds,ax
 mov si,0
 mov dh,5
 mov dl,1
 mov cx,0
 mov cl,2
 call show_str
;;;;;;;;;;;;;;;;;;调用写入显存数据程序;;;;;;;;;;;;;;;;;;;;;;;;;ok

 add si,8
 call PrintBlank
 call PrintBlank
 call PrintBlank
 call PrintBlank
 call PrintBlank
 call PrintBlank
 cmp cx, 14h
 jne Mem_L2
 dec dx
 jnz Mem_L1

        start: mov ah,1

 mov ax,4c00h
 int 21h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 ;从软盘加载Kernel模块; 读内核到内存中 12*1024 处
Read_floppy:
 mov [LoadSeg], word (KernelAddr-4*1024)/16
 mov [SectorNum],byte 60   ;  13
 mov [SectorNo],byte 6
 call ReadFloppy
 ;软盘控制寄存器的I/O地址为3F0 – 3F7
 mov dl, 0  ; 停止驱动器
 mov dx, 0x3f2
 mov al, 0x0c
 out dx, al
  
 ;关中断
 cli

            int 21h

;—————-dtoc begin——————-
;ax 传递要转换的数低16位
;dx 传递要转换的数高16位
;ds:si 传递转换后的数据存放首地址
;字符返回直接存储到指定的地址空间,存储方式为第一位指定的了字符的长度,后边为字符,字符最后一位用0结束.
;si 返回输出字符的最后一位的下一位.
;后面还有一段排序用的函数.

 ;将在 Gdtr 放置的GDT 表入口(32位基址 + 16位界限)加载到—->
gdtr(共48位)中
 lgdt [Gdtr]

            cmp al,’a’     

;bp 仅仅作为偏移地址计算
dtoc:
 push cx
 push bx
 push bp

 ; 将GDT前八项到物理地址 0x000000 ,共 4 * 8byte = 32 byte
 mov esi, Gdt_begin
 mov edi, 0h
 mov ecx, 10*8
 cld
 rep movsb

            jz error

;入栈保护
 mov bp,1 
ndiv:
 mov cx,10
 mov bx,ax
 mov ax,dx
 mov dx,0
 div cx
 push bp   ;bp push 1
 mov bp,ax
 mov ax,bx
 div cx
 mov cx,ax
 mov bl,dl
 mov dx,bp
 pop bp   ;bp pop 1 
 add bl,30h
 mov ds:[si+bp],bl
 inc bp
 add cx,1
 loop ndiv  
 sub bp,1
;———– ;此时转换完毕但是这是还是倒序,需要重新排列顺序;
 push ax
 push dx
 push cx
 push si
 push bp
 push bx
 mov bx,1
 mov ax,bp
 mov dx,0
 mov cx,2
 call divdw
 mov cx,ax
number: 
 mov al,ds:[si+bx]
 mov ah,ds:[si+bp]
 mov ds:[si+bp],al
 mov ds:[si+bx],ah
 inc bx
 sub bp,1
 jcxz cxz
 loop number
cxz:
 pop bx
 pop bp
 pop si
 pop cx
 pop dx
 pop ax
;———– ;这上边一小段函数将倒序变成顺序的;-
 mov bx,bp
 mov ds:[si],bl
 add si,bp
 inc si       ;用来在结束添加’00’确定结束地址
 mov al,0
 mov ds:[si],al
 inc si
;出栈保护
 pop bp
 pop bx
 pop cx
 ret
;———– ;—————————-divdw 
begin——————————-
;ax 被除数dword L16bit
;dx 被除数dword H16bit
;cs 除数
;返回值
;ax 商dword L16bit
;dx 商dword H16bit
;cs 余数
divdw:
 push bx
 push si
 push di
;入栈保护

 ;打开地址线 A20
 in al, 92h
 or al, 02h
 out 92h, al

            cmp al,’z’

 mov si,ax
 mov ax,dx
 mov dx,0
 div cx
 mov bx,ax  ;ax  dword H16bit商,dx dword H16bit余数 
 mov ax,si
 div cx   ;ax  dword L16bit商,dx dword L16bit余数 
 mov cx,dx
 mov dx,bx

 ;准备切换到保护模式
 mov eax, cr0
 or eax, 1
 mov cr0, eax

            ja error

;出栈返回     
 pop di
 pop si
 pop bx
 ret
;———– ;—————————-divdw 
end——————————- 
;—————-dtoc end ——————–
;
;————-show_str begin—————-
;dh 显示器显示的行数
;dl 显示器显示的列数
;cl 显示器显示的字体颜色
;ds 程序中要显示的数据地址
show_str:
 push bp
 push ss
 push cx
 push ax
 push bx
 push ds
 push es
;上边为入栈保护 ax,es,dx,bp,bx,ds,cx
 mov ax,0b800h
 mov es,ax       ;es:bp 显示器的  段地址:偏移地址 
 mov ax,160
 sub dh,1
 mul dh
 mov bp,ax       ;显示器显示的初始地址定位行
 sub dl,1
 mov ax,2
 mul dl
 add bp,ax       ;显示器显示的初始地址定位: {行*每行列数}+列
 mov bx,0
 mov ah,cl       ;ah 存储数据颜色 cl 使用结束,由ah代替cl,
 mov cx,0
sjp1:
 mov al,ds:[si+bx] ;将数据读出
 mov cl,al
 jcxz okc
 inc bx
 mov es:[bp],al   ;将数据放入显存
 add bp,1
 mov es:[bp],ah
 add bp,1
 jmp short sjp1
;下边为出栈释放
okc:
 add si,bx       ;返回源文件所读数据的下一个数据的指针
 pop es
 pop ds
 pop bx
 pop ax
 pop cx
 pop ss
 pop bp 
 ret
;————-show_str end—————-

 ; 将 Sel_code 装入 cs , 并入跳转到 KCode
 jmp  dword Sel_Kenel:KCode_start

            sub al,20h      ;’A’~’Z’:41~5AH    ‘a’~’z’:61~7ah

 

 

            mov dl,al

li ends
end start

;  保护模式

            mov ah,2

 

[bits 32]

            int 21h

 

KCode_start :
 ;初始化堆栈段 SS、SP 和 DS、ES
 mov ax, Sel_KData     ; Sel_KData 基址为 0000h
 mov  ds, ax
 mov  es, ax
 mov ss, ax
 mov esp, 140*1024-4
 
 ; 下面显示一个字符串:  Running in Protect Mode now
 mov  ah, 0Ch     ; 0000: 黑底    1100: 红字
 mov esi, PMMessage     ; 源数据偏移
 mov edi, (80 * 12 + 0) * 2    ; 目的数据偏移。屏幕第 3 行, 第 0
列。
 cld
PM_loop:
 lodsb
 test al, al
 jz PM_end
 mov [0xB8000+edi], ax
 add edi, 2
 jmp PM_loop
PM_end:        ; 显示完毕

            jmp start

 ;—————————————–
 
 lidt [Idtr]

        error:ret

 call  SetupPageTbl     ; 建立页表
 
 mov  eax, cr4
 or  eax, 20h
 mov  cr4, eax 

    code ends

 mov  eax, PagePDPEBase     ;PageDirBase
 mov  cr3, eax
 mov  eax, cr0
 or  eax, 80000000h
 mov  cr0, eax
 
 jmp dword Sel_Kenel:KernelAddr

        end start

Stop :
 jmp  $      ; 死循环

3.编写程序:从键盘上接收一个四位数的十进制数,并在终端上显示出与它等值的二进制数。

;
;——————————————————————————————————
; _________ 内存各段定义 _________
;
GDTBase  equ  0
GDTLen  equ  2*1024
;
IDTBase  equ  2*1024
IDTLen  equ  2*1024
;
PagePDPEBase equ  1024
PagePDEBase equ  4*1024
PagePTEBase equ  8*1024
;
PageNum  equ 1     ; 页表个数,暂时为 1 个
(指向前4M内存),此处最多设为(640k-64k*2)/4k-1 = 127

        code segment

;

          assume cs:code

; _________ GDT 定义 _________
;                                  段基址, 段界限,   属性
Gdt_begin :
  Descriptor  0,    0,   0                             ;0x0 空描述符
DesKCode   :  Descriptor  0, 0FFFFFh, Des_DPL0+Des_CR  + Des_32 +
Des_G ;0x8 (内核)非一致代码段, 32
DesKData   : Descriptor  0, 0FFFFFh, Des_DPL0+Des_DRW + Des_32 +
Des_G ;0x10

        begin:xor bx,bx      ;清空bx

Gdt_end
;
Gdtr :
  dw  GDTLen     ; gdt的长度
  dd  GDTBase     ; gdt的物理地址

              mov ch,4

Idtr :
  dw IDTLen-1    ; idt的长度
  dd IDTBase     ; idt的物理地址

              mov cl,4        ;设置内外层的循环次数

Sel_Kenel   equ   DesKCode    – Gdt_begin
Sel_KData   equ   DesKData    – Gdt_begin    ; 数据段描述符
Sel_stack equ   DesKData    – Gdt_begin    ; 堆栈段描述符

        input:shl bx,cl        ;bx中的内容逻辑左移

;
PMMessage db “Running  in  ProtectMode”,0

              mov ah,1

;—————————————————————————————————-
; 初始化页表

              int 21h          ;输入

SetupPageTbl:

              cmp al,30h        ;是否小于0

 mov dword [PagePDPEBase+4] , dword 0x0 
 mov dword [PagePDPEBase], PagePDEBase | PG_P  | PG_USU | PG_RWW 
 mov dword [PagePDEBase+4] , dword 0x0
 mov dword [PagePDEBase] , PagePTEBase | PG_P  | PG_USU | PG_RWW
 
 mov ecx, 512           ; 每个页表有 1024 项, 每项 4 byte
 mov edi, PagePTEBase    ; 此段首地址为 PagePTEBase
 xor eax, eax
 mov eax, PG_P | PG_USU | PG_RWW
.PageTbl:
 mov [es:edi], eax
 add edi,4
 mov [es:edi],  dword 0x0
 add edi,4
 add eax, 4096     ; 每一页指向 4K 的空间
 loop .PageTbl
 
 call deleteHole
 
 ret
 
deleteHole:
 mov esi,  PagePTEBase + 35*8   ;// 140k 对应的页表项  140 = 4k + 4k +
4k + 128k
 mov edi,  PagePTEBase + 160*8  ;// 640k 对应的页表项
 mov ecx, (1024-640)/4
.1 
 mov eax, dword [esi]
 mov ebx, dword [edi]
 mov  dword [esi], ebx
 mov  dword [edi], eax
 add edi,8
 add esi,8
 loop .1
 ret
 
;——————————

              jb  input        ;是就重新输入

times  2048-($-$$) db 0

              cmp al,39h        ;是否大于9

              ja  input   

              and al,0fh        ;转换为相应的二进制

              jmp combine

        combine:or bl,al       

                dec ch

                jnz input

        display:mov cx,16        ;输出的循环次数

          print:mov dl,0

                rol bx,1

                rcl dl,1

                or dl,30h        ;清空dl

                mov ah,2

                int 21h

                loop print

                ret

        code ends

            end begin

4.将内存ffff:0~ffff:d单元中的数据复制到0:200~0:20d单元中。

    code segment

    assume cs:code

            mov bx,0

            mov cx,0fh

    copy:mov ax,0ffffh 

          mov ds,ax

          mov dl,[bx]

          mov ax,0020h

          mov ds,ax

          mov [bx],dl

          inc bx

          loop copy

        mov ax,4c00h

        int 21h

    code  ends

            end copy

5.将AX寄存器中的16位数分成四组,每组四位,然后把这四组数分别放在AL、BL、CL和DL中。

    data segment

      s db 4 dup(?)  ;占位,并清空

    mov ax,0ffffh

    data ends

    ;———————————– 

    progrnam segment

              assume cs:progrnam,ds:data

    begin:mov cl,4    ;ax逻辑右移四次

          mov ch,4   

          lea bx,s

      X: mov dx,ax

        and dx,0fh      ;逻辑与屏蔽高位

        mov [bx],dl  ;以字节形式将dl移入bx

        inc bx

        shr ax,cl      ;逻辑右移

        dec ch

        jnz X

      Y: mov dl,s        ;依次移入相应寄存器内

        mov cl,s+1

        mov bl,s+2

        mov al,s+3

        ret   

    progrnam ends

        end begin

6.从键盘输入一系列字符,以字符‘$’为结束符,然后对其中的非数字字符计数,并显示出计数结果。

    `stack segment

        dw  100h    dup(?)

        top  label  word

    stack ends

    data segment

            h1  db  ‘Please input a string: ‘,’$’

            h2  db ‘The number of the chars that is not digit:’,’$’

          crlf  db 0dh,0ah,24h

    data  ends

    code  segment

          assume  cs:code,ds:data,ss:stack

    main  proc    far

      begin:mov    ax,stack

            mov    ss,ax

            lea    sp,top

            mov    ax,data

            mov    ds,ax

            lea    dx,h1

        mov    ah,9

        int    21h

            mov    cx,0

    input:    mov    ah,1

            int    21h

            cmp    al,’$’

            jz      exit

            cmp    al,30h

            jb      count

            cmp    al,39h

            jnb    count

            jmp    input

    count:  inc    cx

            jmp    input

    exit:  lea    dx,crlf

            mov    ah,9

            int    21h

            lea    dx,h2

            int    21h

            mov    si,0

            mov    bl,10

            mov    ax,cx

    mem:    div    bl

            mov    dl,ah

            mov    dh,0

            push    dx

            inc    si

            mov    ah,0

            cmp    al,0

            jnz    mem

    prin:    pop    dx

            add    dl,30h

            mov    ah,2

            int    21h

            dec    si

            cmp    si,0

            jnz    prin

            mov    ah,4ch

            int    21H

    main    endp

    code    ends

            end  begin

    `

7.已知两个整数变量A和B,试编写完成下述操作的程序:

(1)若两个数中有一个数是奇数,则将奇数存入A中,偶数存入B中

(2)若两个数均为奇数,则两数分别加1,并存回原变量。

(3)若两个数均为偶数,则两变量不变。

    code segment

        assume cs:code

    begin: mov ax,13

          mov bx,12

          mov cx,ax

          mov dx,bx

          xor ax,bx

          test ax,0001h    ;A和B是否同时为奇数或偶数

          jz next          ;是

          test bx,0001h 

          jz return        ;B为偶数,A为奇数,加1

    exchange:mov ax,dx        ;A为偶数,B为奇数,交换

          mov bx,cx

          jmp return

      next:test bx,0001h    ;是否同为奇数

          jz return        ;同为偶数,不变

          inc dx

          inc cx

          jmp exchange

      return:ret

    code ends

          end begin

8.比较两个字符串string1和string2所含的字符是否相同。若相同则显示‘true’,否则显示‘false’。

    data segment

        string1 db ‘i am a student’

        string2 db ‘i am a student’

        string3 db ‘true’,0dh,0ah,’$’

        string4 db ‘false’,0dh,0ah,’$’

    data ends

    ;————————–   

    progrnam  segment

          assume cs:progrnam,ds:data

    start:push ds

          sub ax,ax

          push ax

          mov ax,data

          mov ds,ax

          mov es,ax

    begin:lea si,string1

          lea di,string2

          cld 

          mov cx,string2-string1

          repe cmpsb

          jne Y

          lea dx,string3

          jmp print     

        Y:lea dx,string4

        print: mov ah,9

          int 21h

        mov ax,4c00h

        int 21h

    progrnam ends

        end start

9.编写无溢出除法的汇编子程序 。

这里所说的除法溢出并不是指分母为0而发生溢出的情况。我们以除数为8位的情况来说明,假设我们的被除数为65535(16位的最大值)存储在AX
中,除数为1,存储在CL中,然后执行除法指令: div
CL。根据上面的说法,结果都是放在AX中的,余数为0,当然这没有问题,然而商为65535要放在AL中却是放不下的,因为AL能存放的最大值只为
255,所以此时就会发生溢出。我们可以看到65535/1 =
255,这显然与我位正常的除法结果不符。

如何解决这个溢出问题

既然我们知道了问题的根源,要解
决它就不困难了。为了统一而且不发生溢出,我们可以把所有的除法的商都用32位来保存,即所有的被除数都用32位,所有的除数都用16位,所有的商都用
32位,所有的余数都用16位来保存,就可以解决这个问题,因为一个数除以一个整数后,不可能大于其之前的值。而对于8位的除法,除数和被除数的高位全用
0补全即可。为了达到这个目的,我们就不能使用默认的除法指令div了,而需要我们写代码来实现我们自定义的除法。

自定义除法: X为除数,N为被除数,H代表X的高16位,L代表X的低16位,

int(H/N)和rem(H/N)代表着H除以N的商和余数

X/N = int(H/N)* 2^16 + [rem(H/N)* 2^16+L]/N

    progrnam segment

              assume cs:progrnam

              mov ax,0ffffh

              mov cx,1          ;初始化进行测试

    begin: cmp cx,0

            je return  ;除数不能为0

            push bx

            push ax

            mov ax,dx

            mov dx,0

            div cx  ;执行H/N,商保存在ax中,余数在DX中

            mov bx,ax ;把商保存

            pop ax    ;取出低位,执行rem(H/N)*2^16+L

            div cx    ;执行[rem(H/N)*2^16+L]/N ,商保存在ax中
,余数在dx中

            mov cx,dx ;cx保存余数

            mov dx,bx ;dx中保存高位除法的商

            pop bx

    return:ret

        mov ax,4c00h

        int 21h

    progrnam ends

          end begin

10.编写一个程序,接受从键盘输入的10个十进制数字,输入回车符则停止输入,然后将这些数字加密后(用XLAT指令变换)存入内存缓冲区BUFFER。加密表为:

  输入数字:0,1,2,3,4,5,6,7,8,9

  密码数字:7,5,9,1,3,6,8,0,2,4

    data segment

        number db 7,5,9,1,3,6,8,0,2,4

        buffer db 10 dup(?)

    data ends

    ;————————————-

    code segment

        assume cs:code,ds:data

        mov si,0

        mov cx,10

        lea bx,number

    input:mov ah,1

          int 21h

          cmp al,0dh    ;是否为回车符

          jz return

          cmp al,30h    ;比较是否是0~9,不是就重新输入

          jb input

          cmp al,39h

          ja input

          xlat          ;变换进行存储

          mov buffer[si],al

          inc si

          loop input

    return:ret

    code ends

    end input

    data segment

        number db 7,5,9,1,3,6,8,0,2,4

        buffer db 10 dup(?)

    data ends

    ;————————————-

    code segment

        assume cs:code,ds:data

        mov si,0

        mov cx,10

        lea bx,number

    input:mov ah,1

          int 21h

          cmp al,0dh    ;是否为回车符

          jz return

          cmp al,30h    ;比较是否是0~9,不是就重新输入

          jb input

          cmp al,39h

          ja input

          xlat          ;变换进行存储

          mov buffer[si],al

          inc si

          loop input

    return:ret

    code ends

        end input

11.编写一个子程序嵌套结构的程序模块,分别从键盘输入姓名及8个字符的电话号码,并以一定的格式显示出来。

    主程序TELIST:

    A.    显示提示符“INPUT  NAME:”;

    B.    调用子程序INPUT_NAME输入姓名;

    C.    显示提示符“INPUT  A  TELEPHONE  NUMBER:”;

    D.    调用子程序INPHONE输入电话号码;

    E.    调用子程序PRINTLINE显示姓名及电话号码。

    子程序INPUT_NAME:

    A.    调用键盘输入子程序GETCHAR,把输入的姓名存放在INBUF缓冲区中;

    B.    把INBUF中的姓名移入输出行OUTNAME。

    子程序INPHONE:

    A.   
调用键盘输入子程序GETCHAR,把输入的8位电话号码存放在INBUF缓冲区中;

    B.    把INBUF中的号码移入输出行OUTPHONE。

    子程序PRINTLINE:

    显示姓名及电话号码,格式为:

      NAME    TEL.

      X X X        XXXXXXXX

代码如下:

    data segment

        tipname db ‘input name:’,’$’

        tipnumber db ‘input a telephone number:’,’$’

        inbuf db 12 dup(‘ ‘)

        crlf db 0dh,0ah,’$’

        outname db 16 dup(‘ ‘)

        outphone db 12 dup(‘ ‘),0dh,0ah,’$’

        info db ‘name’,12 dup(‘ ‘),’tel’,0dh,0ah,’$’

    data ends

    ;————————————

    stack segment

          dw 100 dup(?)      ;伪定义使得同一个变量具有不同的属性

        string label word 

    stack ends

    ;—————————————-

    progrnam segment

    main proc far

          assume cs:progrnam,ds:data,es:data,ss:stack

      start:mov ax,stack

            mov ss,ax

            mov sp,offset string

            push ds

            xor ax,ax

            push ax

            mov ax,data

            mov ds,ax

            mov es,ax

      begin:             

            mov ah,09          ;输出提示字符串

            mov dx,seg tipname

            mov ds,dx

            mov dx,offset tipname

            int 21h

            call input_name    ;输入姓名

            mov ah,09          ;输出提示字符串

            mov dx,seg tipnumber

            mov ds,dx

            mov dx,offset tipnumber

            int 21h

            call input_number

            call printline   

            ret

    main endp

    ;——————————————

    input_name proc near

                call getchar

                lea si,inbuf

                lea di,outname

                mov cx,12      ;设置计数器

                cld            ;设置方向为自增

    ;movsb 与rep结合是传送字节,相当于 movs es:byte ptr[di],ds:[si]

                rep movsb     

                ret

    input_name endp

    ;———————————————–

    input_number proc near

            call getchar

            lea si,inbuf

            lea di,outphone

            mov cx,12

            cld

            rep movsb

            ret

    input_number endp

    ;————————————————

    getchar proc near

            mov al,20h

            mov cx,12

            lea di,inbuf

            cld

    ;stosb这里是为了每一次输入一个字节存入相应的缓冲区

            rep stosb

            mov cx,12

            mov di,0

      input:mov ah,1

            int 21h

            cmp al,0dh

            jz return

            mov inbuf[di],al

            inc di

            loop input

        return: call disp_crlf          ;输出回车符

                ret

    getchar endp

    ;————————————–

    printline proc near            ;这里均是将所得的字符串进行输出

              mov ah,09

              mov dx,seg info

              mov ds,dx

              mov dx,offset info

              int 21h

              mov ah,09

              mov dx,seg outname

              mov ds,dx

              mov dx,offset outname

              int 21h

              ret

    printline endp

    ;—————————————

    disp_crlf proc near

              mov ah,09

              mov dx,seg crlf

              mov ds,dx

              mov dx,offset crlf

              int 21h

              ret

    disp_crlf endp

    ;—————————————–

    progrnam ends

            end start

12.试编写一个汇编语言子程序,要求将包含任意字符、以0结尾的字符串中的小写字母转换成大写字母。

    data segment

          string db ‘i am A stuDEnt’,0h

    data ends

    ;——————————

    progrnam segment

        assume ds:data,cs:progrnam

    start:push ds

        xor ax,ax   

        mov ax,data

        mov ds,ax

        lea si,string

        push si   

    begin: mov al,[si]      ;判断是否为小写字母并进行转换

          cmp al,0

          je return   

          cmp  al,’a’

          jb print

          cmp  al,’z’

          ja print          ;如果不是小写字母直接进行输出

          sub al,20h

          mov [si],al

    print:push ds          ;将字母进行输出

          ;mov al,[si]

          mov dl,al

          mov ah,2

          int 21h

          pop ds     

        n:inc si

          jmp begin   

    return:

          pop si

          pop ds

        mov ax,4c00h

        int 21h

    progrnam ends

        end start

13.编写程序,将一个包含有20个数据的数组M分成两个数组:正数数组P和负数数组N,并分别把这两个数组中数据的个数显示出来。 

    data segment

        string1 db 0dh,0ah, ‘positive number:’,’$’

        string2 db 0dh,0ah,’negative number:’,’$’

        array dw  1,2,3,4,5,6,7,8,-4,-8,-9,-6,-2,-8,8,12,18,-12,5,64

        array1 dw 20 dup(?)    ;存储正数数组

        array2 dw 20 dup(?)      ;存储负数数组

        count1 db 0

        count2 db 0              ;计数

        CRLF DB 0dh,0ah,’$’

    data ends

    ;———————————-

    progrnam segment

    main proc far

        assume ds:data,cs:progrnam

        s:  push ds

            xor ax,ax

            push ax

            mov ax,data

            mov ds,ax 

        begin:mov cx,20        ;循环次数

              lea bx,array      ;bx相当于数组的首地址

              lea si,array1

              lea di,array2

        start:mov ax,[bx]

                cmp ax,0        ;是否为负数

                js Y            ;是负数

                mov [si],ax      ;存入正数数组

                inc count1

                add si,2

                jmp short next

            Y:  mov [di],ax

                inc count2

                add di,2

            next:add bx,2

                loop start

            lea dx,string1

            mov al,count1

            call print

            lea dx,string2

            mov al,count2       

            call print

            ret

    main endp       

    ;————————————–

    print  proc near

            push ax      ;al中存储计数值,要进行保存

            mov ah,9

            int 21h

            pop ax        ;出栈,进行ASCII码调整

            aam            ;调整方式AH<-AL/10(商),AL<-AL%10

            push ax

            add ah,30h    ;变为0~9的ASCII码

            mov dl,ah

            mov ah,2

            int 21h

            pop ax

            add al,30h

            mov dl,al

            mov ah,2

            int 21h

            lea dx,CRLF

            mov ah,9

            int 21h

            ret

    print endp

    progrnam ends

            end s

14.设有10个学生的成绩分别是76,69,84,90,73,88,99,63,100和80分。试编制一个子程序统计60~69分,70~79分,80~89分,90~99分和100分的人数,分别存放到S6,S7,S8,S9和S10单元中。

    data segment

        grade dw 76,69,84,90,73,88,99,63,100,80

        count1 dw 0

        count2 dw 0

        count3 dw 0

        count4 dw 0

        count5 dw 0

    data ends

    ;—————————————–

    progrnam segment

            assume ds:data,cs:progrnam

              push ds

              xor ax,ax

              push ax

              mov ax,data

              mov ds,ax

        begin:mov cx,10

        mov si,0

        next:mov ax,grade[si]

            mov bx,10

            div bl

            mov bl,al

            sub bx,6

            sal bx,1    ;将(grade/10-6)×2就是

            inc count1[bx]  ;count1是首地址

            add si,2

        loop next

    progrnam ends

        end begin

15.输出空行

    progrnam segment

            assume cs:progrnam

        begin:push cx

              push dx

              mov ax,3    ;这里对ax进行赋值,观察输出的结果

              mov cx,ax

        print:mov dl,0dh

              mov ah,2    ;这是对与输出一个字符的bios的调用

              int 21h  ;print的功能就是输出空行,0dh为回车

            mov dl,0ah ;0ah为换行

            mov ah,2

            int 21h 

            loop print

            pop dx

            pop cx

        ;这一段程序是为了测试输出后的结果,主要母的是输出一个黄色的’*’

            mov ah,9

            mov al,’*’

            mov bh,0

            mov bl,0eh

            mov cx,1

            int 10h

            ret

    progrnam ends

            end begin

16.冒泡排序

ASSUME CS:CODE,DS:DATA

DATA SEGMENT

        BUF DB 12, 32, -2, 4, 0    ; NUMBER ARRAY BUFFER

        CNT DB $-BUF              ; COUNT FOR BUFFER

DATA ENDS

CODE SEGMENT

START:  MOV AX, DATA

        MOV DS, AX                  ; DATA SEGMENT

        MOV SI, OFFSET BUF          ; BUFFER INDEX

        MOV CL, CNT                ; LOOP COUNT

        DEC CL

        MOV CH, 0

OUTER:                              ; OUTER LOOP

        MOV AL, [SI]                ; BUF[SI]

        PUSH CX

        MOV DI, SI                  ; BUFFER INDEX

INNER:                              ; INNER LOOP

        INC DI                      ; DI = SI + 1

        MOV DL, [DI]                ; BUF[DI]

        CMP AL, DL                  ; IF (BUF[SI] > BUF[DI])

        JLE CONT                    ;

        XCHG AX, DX                ; SWAP (BUF[SI], BUF[DI])

        MOV [SI], AL

        MOV [DI], DL

CONT:  LOOP INNER                  ; CONTINUE

        POP CX

        INC SI

        LOOP OUTER

        MOV AH, 4CH

        INT 21H

CODE ENDS

END STA