王爽《汇编语言》第四版 课程设计2

使用工具

VMware虚拟机

个人使用上 VMware 比 VirtualBox 要稳定很多,特别是 VMware 15。

Windows xp系统

xp 系统使用内置的 ie 不能连接网络,我这里选择火狐浏览器 xp 版本。

对于汇编文本的编辑这里选用 sublime text3 加 x86-assembly-textmate-bundle-master 高亮插件。

win xp以及所用工具资源

适用于本系统的密钥实测有效:YBVJB-YV2JW-7FHPT-6D8XG-RT83G 。

x86高亮插件的安装

点击箭头指示,将资源中 x86-assembly-textmate-bundle-master 解压然后放至该目录下,再点击上图右下角切换为如上图显示的 x86 and x86_64 assembly 主题。

实验流程

在dosbox中的实验

这里推荐先完成 mini_OS 程序的子功能1,4,5,因为这三个功能基本可以在 dosbox 下完成。在每个功能开头都可以清一次屏幕,具体原理参看《汇编语言》实验十六。

列出功能项

由于要列出的字符串比较多,故使用了直接定址表方便直接调用。

strs dw l0,l1,l2,l3,l4,l5,l6,l7,date,date_form
		l0	db 'Please choose the following options',0
		l1	db '(1) RESET PC',0
		l2	db '(2) BOOT OS',0
		l3	db '(3) SHOW CLOCK',0
		l4  db '(4) SET CLOCK',0
		l5 	db '---Copyright@kctaig---',0
		l6 	db 'Enter ESC to return or S to change colors',0
		l7 	db 'Please enter the date in the following format',0		
		date db "--/--/-- --:--:--",0
		date_form db 9,8,7,4,2,0	
		table dw str_push,str_pop,str_show
		top   dw 0

这一功能实现比较简单,主要使用了《汇编语言》中的字符串输出。这里为了后续输出方便,将字符串输出写为一个子程序。代码如下:

;----------------- 字符串输出 ---------------------
;es:di表示屏幕显示位置
;bx表示字符串在strs表中的位置
;cx表示要连续输出的字符串个数
	str_output:
			push si
			
			push di
			add bx,bx               ;strs中是dw大小,这里计算偏移量
	s0:		mov si,strs[bx]			;将定址表strs中第(bx)个的偏移地址传递到si中
	s1:		cmp	byte ptr cs:[si],0			
			je 	next_raw			;若等于零则结束该字符串
			mov al,cs:[si]
			mov es:[di],al
			inc si
			add di,2
			jmp short s1
	next_raw:	
			add bx,2				;下一个字符串,注意是加2
			pop di
			add di,160				;屏幕下一行
			push di
			loop s0	
			
			pop di					;将屏幕显示的位置pop掉
			
			pop si
			ret

所以具体功能的实现代码如下:

;------------ (1)列出功能项 ------------ 
	list_show:
			call clear			;清屏
			push ax
			push bx
			push cx
			push es
			push di
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,0
			mov cx,1
			call str_output        ;显示l0
			
			mov di,160*11+60
			mov bx,1
			mov cx,4
			call str_output        ;显示l1~l4
			
			mov di,160*15+50
			mov bx,5
			mov cx,1
			call str_output        ;显示l5
			
			pop di
			pop es
			pop cx
			pop bx
			pop ax
			ret	

动态显示当前时间

对于时间的显示参考实验《汇编语言》实验十四,这里我自己因为粗心循环调用的时候用的是 call而不是 jmp 导致程序出错浪费了一个下午,希望读者能引以为戒。

当按下 s 键后,改变现实颜色;按下 ESC 键后,返回到主选单。这里改为s键是因为我的 F1 键不管在加不加 Fn 键都无法使用,后来就放弃了,每次操作软键盘太麻烦;而且没有修改 int 9h 也不会对其他功能造成影响。

我参考的是这位仁兄的方法,我原先使用的是修改 int 9h 键盘中断,但是在调试的时候出现问题。这个问题在《汇编语言》实验十五中也出现了,就是在修改中断向量表时 debug 卡死,但是程序本身又能运行。如果非要用 int 9h 可以参考这位的实验。我偶然看到这个博客,但本人未验证过,有兴趣的朋友可以试试。

;-------------- (4)动态显示当前时间 --------------	
	show_clock:
			call clear
			call clear_input	;清理键盘缓存区
			
			push ax
			push bx
			push cx
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,6
			mov cx,1
			call str_output		;显示l6字符串
			
			mov ax,1;			;设置颜色
			push ax
			
	rep_show:
			mov ax,cs
			mov ds,ax
			mov bx,0
			
			mov ax,0b800h
			mov es,ax
			mov di,160*12+60	;es:di输出屏幕位置
			
			mov cx,6
	s:		push cx
			mov al,date_form[bx]
			push ax
			out 70h,al
			in al,71h			;此时从端口中获取到数据
			
			mov ah,al
			mov cl,4
			shr ah,cl
			and al,00001111b
			
			add ah,30h
			add al,30h			;此时ah为十位,al为个位
			
			mov es:[di],ah
			mov es:[di+2],al	;输出
			add di,4
			
			pop ax
			cmp al,9
			jne ok1
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok1:	cmp al,8
			jne ok2
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok2:	cmp al,7
			jne ok3
			add di,2
			jmp ok
			
	ok3:	cmp al,4
			jne ok4
			mov byte ptr es:[di],':'
			add di,2
			jmp ok
			
	ok4:	cmp al,2
			jne ok
			mov byte ptr es:[di],':'
			add di,2			;输出间隔符号
			
	ok:		inc bx
			pop cx	
			loop s
			
			mov ah,1
			int 16h				;利用16h中断的1号功能,非阻塞读取
			jz rep_call			;如果无缓存字符,继续循环
			mov ah,0
			int 16h				;将一号功能读取的字符从缓存中清除
			cmp al,1bh			;判段是否是esc
			je cret
			cmp ah,1fh			;用s键来改变颜色
			je change_color
			
			jmp rep_call		;若是其他键则继续显示
			
	change_color:
			pop ax
			inc ax
			cmp ax,7			;设置颜色数量上线
			je reset_col
			jmp next_col
	reset_col:					;重置颜色
			mov ax,1
	next_col:
			push ax				;将下一个颜色入栈	
			call set_color
			
	rep_call:
			jmp rep_show		;循环显示时间
			
	cret:	mov ax,7			;将前景色设置为白色
			call set_color
			
			pop ax				;将颜色出栈
			pop es
			pop ds
			pop di
			pop cx
			pop bx
			pop ax
			ret	

修改日期

先建立一个字符串 date ,利用《汇编语言》实验第十七章的 17.3 的程序(会根据需求略作修改)将输入的日期写入 date 。

再将 date 中的年月日等提取出来改写为 BCD CMOS 中。

注:在 dosbox 中修改的日期会无法显示,具体效果只能在裸机上看到。(这个坑真的浪费我好长时间,很无语)

get_str 调用十七章的 17.3,由于太长且书上有就不贴这部分代码了,所有代码会贴在最后面。

;--------------- (5)修改日期 ----------------
;先将日期写入date字符串中,保证只输入日期和格式正确
;再将字符串中的数字写入cmos中
	set_clock:
			call clear				;清除屏幕
		
			push ax
			push dx
			push si
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,7
			mov cx,1
			call str_output			;显示l7字符串
			
			mov di,160*12+64
			mov bx,8
			mov cx,1
			call str_output			;显示日期格式
			
			mov dh,12
			mov dl,32				;设置字符串在屏幕上的行列位置
			mov ax,cs
			mov ds,ax
			mov si,offset date		;ds:si指向字符栈空间
			call get_str			;输入新日期
			
			call set_cmos			;将新日期写入cmos中
			
			pop es
			pop ds
			pop di
			pop si
			pop dx
			pop ax
			
			ret
;--------------- 修改cmos中71端口的内容 ---------------
	set_cmos:
			push ax
			push bx
			push cx
			push si
			
			mov bx,0
			mov cx,6	
	cmos_s:	call get_date_cut		;此时al中为日期
			mov ah,al

			mov al,date_form[bx]
			out 70h,al			;将日期对应地址写入70h端口
			mov al,ah
			out 71h,al				;修改cmos中的内容
			
			inc bx
			loop cmos_s
			
			pop cx
			pop bx
			pop ax
			ret			
;-------------- 截取date中年、月、日等 ----------------
;参数bx表示截取的位置
;al存储截取出的日期
	get_date_cut:
			push bx
			push cx
			push di
			push es
		
			mov al,3
			mul bl
			mov bl,al		;计算偏移量
			
			mov ax,cs
			mov es,ax
			mov di,offset date
			add di,bx		;es:di指向date字符串中日期的位置
			
			mov ax,es:[di]
			sub al,30h
			sub ah,30h
			mov cl,4
			shl al,cl		;输入的时候先输入的十位在al中
			and al,11110000b;保留al中的前四位
			add al,ah		;al中为两位数日期的的BCD码
			
			pop es
			pop di
			pop cx
			pop bx
			ret	

以上基本完成了在 dosbox 中完成的 mini_OS 中的操作,在 dobox 中还会涉及 loader 程序(即安装boot 和 mini_OS 到软盘中)。

在裸机中的实验

机器开机后执行流程

1、开机后 CPU 进入到 FFFF:0000单元处执行,此处为一条跳转指令

2、CPU 执行该跳转指令后,转去执行 BIOS 中的硬件系统检测和初始化程序

3、之后调用 int 19h 中断进行操作系统引导,这里从软盘A启动操作系统

4、int 19h 从软盘启动操作系统将完成以下工作

  1. 控制0号软驱,去读软盘的0道0面1扇区的内容(本实验中即 boot )到 0:7c00h
  2. 将 cs:ip 指向 0:7c00h

5、本实验中 loader 将 boot 写入 0:7c00h,故此处执行 boot

6、再执行 mini_OS ,一个能自行启动计算机,无需在现有操作系统中运行的程序

本实验三个子程序

loader 程序:在 dosbox 中执行,功能如下

  1. 将 boot 写入0道0面1扇区
  2. 将 mini_OS 写入之后的其他扇区

boot 程序:由 int19h 中断将之加载进入内存 0:7c00h,功能如下:

  1. 将 mini_OS 从软盘A中加载进内存 0:7e00h
  2. 将 cs:ip 指向 0:7e00h

mini_OS 程序:小型循环操作系统,功能如下:

  1. 列出功能项
  2. 用户输入1后重新启动计算机
  3. 用户输入2后引导现有操作系统( c 盘中 win xp)
  4. 用户输入3后动态显示当前时间
  5. 用户输入4后修改日期 

注:用 loader 与 boot 的目的是能够扩展引导启动程序。因为 int 19h 只能读取一个扇区,而boot 可以占据多个之后的扇区。

软盘处理

这里参考了在VMWare中软盘的安装

个人觉得这里是实验最难顶的部分,因为软盘启动黑屏无法调试,但是软盘中确实写入了 boot 和 init_OS程序。而VMware 中不好调试裸机;VirtualBox 中虽然有内置调试工具,但我用了发现无法调试引导程序,也就是 Boot 程序;Bochs 需要用 NASM ,并且对于以及写好的软盘在 Windows 写 bochsrc配置文件我没有发现中文文档有具体的方法(看了一圈都是讲bin写进 img 中生成镜像,而软盘本身就可以自己当作启动盘,如果改程序变为 NASM 格式也很麻烦。),当我打算看 Bochs 原文档时,修改了一下返回指令成功了,原因后面会详细说的。总之,这里对于调试非常不友好,在没有人带的情况下很难顶。

安装程序

即 loader,目的是将 boot(系统引导程序)放入软盘0道0面1扇区,并且将 mini_OS 放入之后的3个扇区。具体代码如下:

;------------- loader程序 --------------
	loader:	mov ax,cs
			mov es,ax
			mov bx,offset boot
			
			mov al,1
			mov ch,0
			mov cl,1
			mov dl,0
			mov dh,0
			mov ah,3	
			int 13h			;将boot写入软盘0道0面1扇区
			
			mov bx,offset mini_OS
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,3
			int 13h			;将mini_OS写入软盘之后三个扇区中
			ret

boot 程序

将 mini_OS 从软盘的0道0面2扇区及之后的2个扇区中导入内存中的 0:7E00h 中,并将 cs:ip 指向 0:7E00h。

我原本是用的 iret,但是在 dosbox 中可以用 iret 完成功能1,却无法实现引导,修改为使用 retf 后可以成功,同时还要修改栈,具体参考《汇编语言》第十章。

;-------------- boot程序 ----------------
	boot:	mov ax,0    	;修改栈顶为0:7c00
    		mov ss,ax   
    		mov sp,7c00h 

			mov ax,0
			mov es,ax
			mov bx,7e00h	;es:bx为要读入的内存地址
			
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,2
			int 13h			;将mini_OS从软盘加载进内存0:7e00h
			
			mov ax,0   		;cs
		    push ax
		    mov ax,7e00h	;ip
		    push ax
			retf            ;通过retf设置cs:ip

系统重启

最简单的功能,直接将 cs:ip 设置为 FFFF:0000

;----------- (2)重新启动计算机 ------------
	rep_system:
			mov ax,0ffffh
			push ax
			mov ax,0
			push ax
			retf

引导现有操作系统

将 C 盘中的 xp 系统的引导写入 0:7C00h,然后跳转此处执行。

这里也要用 retf 。

;----------- (3)引导现有操作系统 ------------
	start_system: 	
		
			mov ax,0	
			mov es,ax
			mov bx,7c00h		;es:bx指向要写入的位置
			
			mov al,1			;读取一个扇区
			mov ch,0			;磁道号0
			mov cl,1			;扇区号1
			mov dh,0
			mov dl,80h			;硬盘c
			mov ah,2
			int 13h

			mov ax,0
			push ax
			mov ax,7c00h
			push ax
			retf

完结

综上,本次实验就完成基本完成了,在这个框架上可以完善很多功能,希望大家可以做得更好。

同时感谢我引用的链接的博主们,他们使我少走了不少弯路。

实验代码

本程序要在 cmd 中执行才能写入软盘中,在 dosbox 中无效。

mini_OS 中开头的栈顶设置必须是 0:7c00h,如果设置 0:7e00 会影响引导现有os时的512B内容从而使功能2失效,7c00h + 512b = 7e00h。

 全部代码如下:

assume cs:code	

stack segment
	db 128 dup (0)
stack ends

code segment	

	start: 	call loader		;在cmd中运行安装程序,将任务程序写到软盘上
	
			mov ax,4c00h
			int 21h

;------------- loader程序 --------------
	loader:	mov ax,cs
			mov es,ax
			mov bx,offset boot
			
			mov al,1
			mov ch,0
			mov cl,1
			mov dl,0
			mov dh,0
			mov ah,3	
			int 13h			;将boot写入软盘0道0面1扇区
			
			mov bx,offset mini_OS
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,3
			int 13h			;将mini_OS写入软盘之后3个扇区中
			ret
;-------------- boot程序 ----------------
	boot:	mov ax,0    	
    		mov ss,ax   
    		mov sp,7c00h 

			mov ax,0
			mov es,ax
			mov bx,7e00h	;es:bx为要读入的内存地址
			
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,2
			int 13h			;将mini_OS从软盘加载进内存0:7e00h
			
			mov ax,0   		
		    push ax
		    mov ax,7e00h	
		    push ax
			retf            
;-------------------------------------------------

 org 7e00h	        ;这一步定位mini_OS

;---------------- mini_OS操作系统 ----------------
	mini_OS:
			mov ax,0    	
    		mov ss,ax   	
    		mov sp,7c00h             ;这里必须是7c00

			jmp mini_OS_start
		
		strs dw l0,l1,l2,l3,l4,l5,l6,l7,date,date_form
		l0	db 'Please choose the following options',0
		l1	db '(1) RESET PC',0
		l2	db '(2) BOOT OS',0
		l3	db '(3) SHOW CLOCK',0
		l4  db '(4) SET CLOCK',0
		l5 	db '---Copyright@kctaig---',0
		l6 	db 'Enter ESC to return or S to change colors',0
		l7 	db 'Please enter the date in the following format',0		
		date db "--/--/-- --:--:--",0
		date_form db 9,8,7,4,2,0	
		table dw str_push,str_pop,str_show
		top   dw 0

	mini_OS_start:

			call list_show			;列出功能选项
			call check_option		;检测键盘输入
			jmp mini_OS_start
			ret
			
;---------------- 输入检查 ----------------
	check_option:
			call clear_input	;清除键盘缓存		
			push ax
			mov ah,0
			int 16h
			cmp al,'1'
			je fun1
			cmp al,'2'
			je fun2
			cmp al,'3'
			je fun3
			cmp al,'4'
			je fun4
			call option_error
			
			call chret
	
	fun1:	call rep_system		;调用重启程序
			jmp chret
	fun2:	call start_system
			jmp chret
	fun3:	call show_clock		;显示时钟
			jmp chret
	fun4:	call set_clock
			jmp chret
	
	chret:	pop ax
			ret

;------------ (1)列出功能项 ------------ 
	list_show:
			call clear			;清屏
			push ax
			push bx
			push cx
			push es
			push di
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,0
			mov cx,1
			call str_output
			
			mov di,160*11+60
			mov bx,1
			mov cx,4
			call str_output
			
			mov di,160*15+50
			mov bx,5
			mov cx,1
			call str_output
			
			pop di
			pop es
			pop cx
			pop bx
			pop ax
			ret	

;----------- (2)重新启动计算机 ------------
	rep_system:
			mov ax,0ffffh
			push ax
			mov ax,0
			push ax
			retf

;----------- (3)引导现有操作系统 ------------
	start_system: 	
		
			mov ax,0	
			mov es,ax
			mov bx,7c00h		;es:bx指向要写入的位置
			
			mov al,1			;读取一个扇区
			mov ch,0			;磁道号0
			mov cl,1			;扇区号1
			mov dh,0
			mov dl,80h			;硬盘c
			mov ah,2
			int 13h

			mov ax,0
			push ax
			mov ax,7c00h
			push ax
			retf
			
;-------------- (4)动态显示当前时间 --------------	
	show_clock:
			call clear
			call clear_input	;清理键盘缓存区
			
			push ax
			push bx
			push cx
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,6
			mov cx,1
			call str_output		;显示l6字符串
			
			mov ax,1;			;设置颜色
			push ax
			
	rep_show:
			mov ax,cs
			mov ds,ax
			mov bx,0
			
			mov ax,0b800h
			mov es,ax
			mov di,160*12+60	;es:di输出屏幕行
			
			mov cx,6
	s:		push cx
			mov al,date_form[bx]
			push ax
			out 70h,al
			in al,71h			;此时从端口中获取到数据
			
			mov ah,al
			mov cl,4
			shr ah,cl
			and al,00001111b
			
			add ah,30h
			add al,30h			;此时ah为十位,al为个位
			
			mov es:[di],ah
			mov es:[di+2],al	;输出
			add di,4
			
			pop ax
			cmp al,9
			jne ok1
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok1:	cmp al,8
			jne ok2
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok2:	cmp al,7
			jne ok3
			add di,2
			jmp ok
			
	ok3:	cmp al,4
			jne ok4
			mov byte ptr es:[di],':'
			add di,2
			jmp ok
			
	ok4:	cmp al,2
			jne ok
			mov byte ptr es:[di],':'
			add di,2			;输出间隔符号
			
	ok:		inc bx
			pop cx	
			loop s
			
			mov ah,1
			int 16h				;利用16h中断的1号功能,非阻塞读取
			jz rep_call			;如果无缓存字符,继续循环
			mov ah,0
			int 16h				;将一号功能读取的字符从缓存中清除
			cmp al,1bh			;判段是否是esc
			je cret
			cmp ah,1fh			;用s键来改变颜色
			je change_color
			
			jmp rep_call		;若是其他键则继续显示
			
	change_color:
			pop ax
			inc ax
			cmp ax,7			;设置颜色数量上线
			je reset_col
			jmp next_col
	reset_col:					;重置颜色
			mov ax,1
	next_col:
			push ax				;将下一个颜色入栈	
			call set_color
			
	rep_call:
			jmp rep_show		;循环显示时间
			
	cret:	mov ax,7			;将前景色设置为白色
			call set_color
			
			pop ax				;将颜色出栈
			pop es
			pop ds
			pop di
			pop cx
			pop bx
			pop ax
			ret	

;--------------- (5)修改日期 ----------------
;先将日期写入date字符串中,保证只输入日期和格式正确
;再将字符串中的数字写入cmos中
	set_clock:
			call clear				;清除屏幕
		
			push ax
			push dx
			push si
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,7
			mov cx,1
			call str_output			;显示l7字符串
			
			mov di,160*12+64
			mov bx,8
			mov cx,1
			call str_output			;显示日期格式
			
			mov dh,12
			mov dl,32				;设置字符串在屏幕上的行列位置
			mov ax,cs
			mov ds,ax
			mov si,offset date		;ds:si指向字符栈空间
			call get_str			;输入新日期
			
			call set_cmos			;将新日期写入cmos中
			
			pop es
			pop ds
			pop di
			pop si
			pop dx
			pop ax
			
			ret
;--------------- 修改cmos中71端口的内容 ---------------
	set_cmos:
			push ax
			push bx
			push cx
			push si
			
			mov bx,0
			mov cx,6	
	cmos_s:	call get_date_cut		;此时al中为日期
			mov ah,al

			mov al,date_form[bx]
			out 70h,al			;将日期对应地址写入70h端口
			mov al,ah
			out 71h,al				;修改cmos中的内容
			
			inc bx
			loop cmos_s
			
			pop cx
			pop bx
			pop ax
			ret	
		
;-------------- 截取date中年、月、日等 ----------------
;参数bx表示截取的位置
;al存储截取出的日期
	get_date_cut:
			push bx
			push cx
			push di
			push es
		
			mov al,3
			mul bl
			mov bl,al		;计算偏移量
			
			mov ax,cs
			mov es,ax
			mov di,offset date
			add di,bx		;es:di指向date字符串中日期的位置
			
			mov ax,es:[di]
			sub al,30h
			sub ah,30h
			mov cl,4
			shl al,cl		;输入的时候先输入的十位在al中
			and al,11110000b;保留al中的前四位
			add al,ah		;al中为两位数日期的的BCD码
			
			pop es
			pop di 			;指向date字符串中日期的位置
			pop cx
			pop bx
			ret	

;--------------- 接受字符串的子程序 ---------------
	get_str:call clear_input
			push ax
	get_char:
			mov ah,0
			int 16h				;从缓存区中取字符
			cmp al,20h
			jb not_char			;ASCII码小于20h不是字符
			mov ah,0	
			call str_stack		;添加字符
			mov ah,2			
			call str_stack		;显示栈中字符
			jmp get_char
			
	not_char:
			cmp ah,0eh
			je backspace		;退格键的扫描码
			cmp ah,1ch
			je enter			;回车键的扫描码
			
			jmp get_char
			
	backspace:	
			mov ah,1
			call str_stack		;删除一个字符
			mov ah,2
			call str_stack		;显示栈中字符
			jmp get_char
	enter:		
			mov al,0
			mov ah,0
			call str_stack		;字符串末尾加0
			mov ah,2
			call str_stack
			
			pop ax				;字符串输入完成
			ret		
	
;--------------- 字符栈的入栈、出栈和显示 -----------------
	str_stack:		
			push bx
			push dx
			push di
			push es
			
			cmp ah,2
			ja sret
			mov bl,ah
			mov bh,0
			add bx,bx
			jmp word ptr table[bx]
	str_push:
			mov bx,top
			mov [si][bx],al
			inc top
			jmp sret
	str_pop:	
			cmp top,0
			je sret
			dec top
			mov bx,top
			mov al,[si][bx]
			jmp sret
			
	str_show:	
			mov bx,0b800h
			mov es,bx
			mov al,160
			mov ah,0
			mul dh
			mov di,ax
			add dl,dl
			mov dh,0
			add di,dx				;es:di表示在屏幕中要输入的位置
			
			mov bx,0
			
	char_show:
			cmp bx,top
			jne char_not_empty
			jmp sret
	char_not_empty:	
			mov al,[si][bx]
			mov es:[di],al
			inc bx
			add di,2
			jmp char_show

	sret:	pop es
			pop di
			pop dx
			pop bx
			ret

;----------------- 选项输入错误 -------------------
	option_error:
			call check_option
			ret
;----------------- 字符串输出 ---------------------
;es:di表示屏幕显示位置
;bx表示字符串在strs表中的位置
;cx表示要连续输出的字符串个数
	str_output:
			push si
			
			push di
			add bx,bx
	s0:		mov si,strs[bx]			;将定址表strs中第(bx)个的偏移地址传递到si中
	s1:		cmp	byte ptr cs:[si],0			
			je 	next_raw			;若等于零则结束该字符串
			mov al,cs:[si]
			mov es:[di],al
			inc si
			add di,2
			jmp short s1
	next_raw:	
			add bx,2				;下一个字符串,注意是加2
			pop di
			add di,160				;屏幕下一行
			push di
			loop s0	
			
			pop di					;将屏幕显示的位置pop掉
			
			pop si
			ret

;----------- 清屏,将显存中当前屏幕中的字符设为空格符 -----------
	clear:	push bx
			push cx
			push es
			
			mov bx,0b800h
			mov es,bx
			mov bx,0
			mov cx,2000
	clear_char:	
			mov byte ptr es:[bx],' '
			add bx,2
			loop clear_char
			
			pop es
			pop cx
			pop bx
			ret	
		
;---------------- 设置前景色 ----------------
	set_color:	
			push bx
			push cx
			push es
			
			mov bx,0b800h
			mov es,bx
			mov bx,1
			mov cx,2000
	sc:		and byte ptr es:[bx],11111000b
			or es:[bx],al			;改为al中的颜色
			add bx,2
			loop sc
	
			pop es
			pop cx
			pop bx
			ret

;--------------- clear_input_buffer --------------
	clear_input:
			push ax
	clear_word:
			mov ah,1
			int 16h
			jz clear_input_ret		;无读取则退出循环
			
			mov ah,0
			int 16h
			call clear_word			;将非阻塞读取的字符从buffer中删除
	clear_input_ret:
			pop ax
			ret

code ends
end start

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第1章 基础知识   1.1 机器语言   1.2 汇编语言的产生   1.3 汇编语言的组成   1.4 存储器   1.5 指令和数据   1.6 存储单元   1.7 CPU对存储器的读写   1.8 地址总线   1.9 数据总线   1.10 控制总线   1.11 内存地址空间(概述)   1.12 主板   1.13 接口卡   1.14 各类存储器芯片   1.15 内存地址空间  第2章 寄存器  2.1 通用寄存器         2.2 字在寄存器中的存储     2.3 几条汇编指令        2.4 物理地址          2.5 16位结构的CPU       2.6 8086cPu给出物理地址的方法 2.7 “段地址xl6+偏移地址=物理地址”的本质含义         2.8 段的概念          2.9 段寄存器          2.10 CS和IP          2.11 修改CS、IP的指令     2.12 代码段          实验1 查看CPU和内存,用机器指令和汇编指令编程    第3章 寄存器(内存访问)    3.1 内存中字的存储      3.2 DS 31:1[address]         3.3 字的传送         3.4 ITIOV、add、sub指令    3.5 数据段          3.6 栈                3.7 CPU提供的栈机制     3.8 栈顶超界的问题       3.9 puSh、pop指令       3.10 栈段           实验2 用机器指令和汇编指令编程  第4章 第一个程序          4.1 一个源程序从写出到执行的过程  4.2 源程序             4.3 编辑源程序           4.4 编译              4.5 连接              4.6 以简化的方式进行编译和连接   4.7 1.exe的执行           4.8 谁将可执行文件中的程序装载进入内存并使它运行?        4.9 程序执行过程的跟踪       实验3 编程、编译、连接、跟踪   第5章 【BX】和loop指令       5.1 【BX】              5.2 Loop指令            5.3 在Debu9中跟踪用loop指令实现酮循环程序            5.4 Debu9和汇编编译器masm对指令的不同处理         5.5 loop和【bx】的联合应用     …… 第3章 寄存器(内存访问)  第4章 第一个程序  第5章 [BX]和loop指令  第6章 包含多个段的程序  第7章 更灵活的定位内存地址的方法  第8章 数据处理的两个基本问题  第9章 转移指令的原理  第10章 CALL和RET指令  第11章 标志寄存器  第12章 内中断  第13章 int指令  第14章 端口  第15章 外中断  第16章 直接定址表  第17章 使用BIOS进行键盘输入和磁盘读写  综合研究  附注
汇编语言王爽著 经典学习汇编教程 汇编语言是各种CPU提供的机器指令的助记符的集合,人们可以用汇编语言直接控制硬件系统进行工作。汇编语言是很多相关课程(如数据结构、操作系统、微机原理等)的重要基础。为了更好地引导、帮助读者学习汇编语言,作者以循序渐进的思想精心创作了这本书。本书具有如下特点:采用了全新的结构对课程的内容进行组织,对知识进行最小化分割,为读者构造了循序渐进的学习线索;在深入本质的层面上对汇编语言进行讲解;对关键环节进行深入的剖析。 汇编语言是直接在硬件之上工作的编程语言,首先要了解硬件系统的结构,才能有效的应用汇编语言对其编程。 在本章中,对硬件系统结构的问题进行一部分的探讨,以使后续的课程可在一个好的基础上进行。 机器语言是机器指令的集合。机器指令展开来讲就是一台机器可以正确执行的命令。 这个是一本入门的汇编语言教材 关于内容:1、内容真的是做到了深入浅出(书是给人看的,别人看懂了才是最关键的)。2、有的地方看的出来是老师自己的感悟(不像其它国内大多数的书,就知道东抄一点西搬一点。内容到是什么都有,看了就不知道在讲个什么,什么都讲不清楚,就知道赚钱)我是学信息系?的学生,关于计算机科学方面我的这个专业学的不是太多,我就自己学,看了很多书,国内像这样的好书真的是很难得。3、更珍贵的是老师的编排,可以说比国外的还好(当然从知识点来说不如国外的书,但是这是一本入门书) 我的感想:1、计算机科学方面的好书,中国基本上没有(运用方面到是还是有很多不错的),要看就看国外的。翻译的好坏也是一个重点(像我英文不是很好,就只能看翻译过来的,看的时候还要看看翻译的是不是很好,有的翻译真的很烂),翻译的好的一般像潘爱民老师(听说雷迎春翻译的也不错,不过我还没看过他翻译的书).3、看的时候还要看是不是适合自己的情况(1、分清是入门教程还是高级教程;2、看看学过先导课程没有,有的知识是要有另外的知识做前提的) 目录: 第1章 基础知识 1.1 机器语言 1.2 汇编语言的产生 1.3 汇编语言的组成 1.4 存储器 1.5 指令和数据 1.6 存储单元 1.7 CPU对存储器的读写 1.8 地址总线 1.9 数据总线 1.10 控制总线 1.11 内存地址空间(概述) 1.12 主板 1.13 接口卡 1.14 各类存储器芯片 1.15 内存地址空间 第2章 寄存器 2.1 通用寄存器 2.2 字在寄存器中的存储 2.3 几条汇编指令 2.4 物理地址 2.5 16位结构的CPU 2.6 8086cPu给出物理地址的方法 2.7 “段地址xl6+偏移地址=物理地址”的本质含义 2.8 段的概念 2.9 段寄存器 2.10 CS和IP 2.11 修改CS、IP的指令 2.12 代码段 实验1 查看CPU和内存,用机器指令和汇编指令编程 第3章 寄存器(内存访问) 3.1 内存中字的存储 3.2 DS 31:1[address] 3.3 字的传送 3.4 ITIOV、add、sub指令 3.5 数据段 3.6 栈 3.7 CPU提供的栈机制 3.8 栈顶超界的问题 3.9 puSh、pop指令 3.10 栈段 实验2 用机器指令和汇编指令编程 第4章 第一个程序 4.1 一个源程序从写出到执行的过程 4.2 源程序 4.3 编辑源程序 4.4 编译 4.5 连接 4.6 以简化的方式进行编译和连接 4.7 1.exe的执行 4.8 谁将可执行文件中的程序装载进入内存并使它运行? 4.9 程序执行过程的跟踪 实验3 编程、编译、连接、跟踪 第5章 【BX】和loop指令 5.1 【BX】 5.2 Loop指令 5.3 在Debu9中跟踪用loop指令实现酮循环程序 5.4 Debu9和汇编编译器masm对指令的不同处理 5.5 loop和【bx】的联合应用 …… 第3章 寄存器(内存访问) 第4章 第一个程序 第5章 [BX]和loop指令 第6章 包含多个段的程序 第7章 更灵活的定位内存地址的方法 第8章 数据处理的两个基本问题 第9章 转移指令的原理 第10章 CALL和RET指令 第11章 标志寄存器 第12章 内中断 第13章 int指令 第14章 端口 第15章 外中断 第16章 直接定址表 第17章 使用BIOS进行键盘输入和磁盘读写 综合研究 附注

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值