王爽《汇编语言》课程设计2
2016-08-23 23:46
357 查看
;关于程序编写过程中一些问题的总结和说明,见文章末尾
ASSUME
CS:SETUP
;============================================================================================
;安装程序
;将引导所需的程序写入软盘
SETUP
SEGMENT
START:
;复制装载程序到软盘0面0道1扇区
MOV
AX,LOAD
MOV
ES,AX
MOV
BX,0
;ES:BX指向要写的单元
MOV
DX,0
MOV
CX,1
MOV
AL,1
MOV
AH,3
INT
13H
;复制任务程序到软盘0面0道2~3扇区
MOV
AX,SYS
MOV
ES,AX
;BX没变
MOV
CX,2
;DX没变
MOV
AL,2
MOV
AH,3
INT
13H
MOV
AX,4C00H
INT
21H
SETUP
ENDS
;============================================================================================
;============================================================================================
;引导程序
LOAD
SEGMENT
;装载任务程序到指定内存处(此处2000:0)
;此处程序应该按照0:7C00的观点来看
;注意,这里是用软盘启动计算机,并没有DOS中断可用
ASSUME
CS:LOAD
;读任务程序到2000:0
MOV
AX,2000H
MOV
ES,AX
MOV
BX,0
PUSH
AX
PUSH
BX
MOV
DX,0
MOV
CX,2
MOV
AL,2
MOV
AH,2
INT
13H
;将CS:IP指向任务程序的首地址2000:0
RETF
LOAD
ENDS
;============================================================================================
;============================================================================================
;任务程序
SYS
SEGMENT
ASSUME
CS:SYS
;隐藏光标
MOV
AH,01H
MOV
CX,3030H
INT
10H
MOV
CX,OFFSET
ENDSHOW-OFFSET
SYS_START
SYS_START:
JMP
SHOW_MENU
DATE DB
"00/00/00 00:00:00",0
;日期字符串
DATE_INPUT DB
"00/00/0000:00:00",0
;用户输入的日期字符串,当输入不正确或者退出输入时,
;可以避免改变原始日期
DATE_POS DB
0,2,4,7,8,9
;定位
CMOS RAM 中日期各项的地址
DATE_CHAR_POS DB
15,12,9,6,3,0
;定位日期字符串中各项的位置
DATE_COLOR DB
2
;提示字符串
PRESS_ESC DB
"PRESS ESC TORETURN MENU",0
PRESS_F1 DB
"PRESS F1 TOCHANGE COLOR",0
INPUT_DATE DB
"INPUT A DATEIN THE FORM OF YY/MM/DD HH:MM:SS,PRESS ENTER TO END INPUT",0
OPTION1 DB
"1) RESET PC",0
OPTION2 DB
"2) START SYSTEM",0
OPTION3 DB
"3) CLOCK",0
OPTION4 DB
"4) SET CLOCK",0
;直接定址表
TABLE DW
SUB1,SUB2,SUB3,SUB4
;列出功能选项
SHOW_MENU:
CALL
CLS
PUSH
CS
POP
DS
MOV
SI,OFFSET
OPTION1
MOV
DX,0
MOV
CL,2
CALL
SHOW_STR
INC
DH
MOV
SI,OFFSET
OPTION2
CALL
SHOW_STR
INC
DH
MOV
SI,OFFSET
OPTION3
CALL
SHOW_STR
INC
DH
MOV
SI,OFFSET
OPTION4
CALL
SHOW_STR
GET_INPUT:
MOV
AH,0
;获取键盘输入
INT
16H
SUB
AL,30H
CMP
AL,1
JB
GET_INPUT
CMP
AL,4
JA
GET_INPUT
MOV
BL,AL
DEC
BL
;1~4转化为0~3
MOV
BH,0
SHL
BX,1
CALL
WORD
PTR
CS:TABLE[BX]
;进入子程序
JMP
SHOW_MENU
;重新启动计算机
SUB1:
MOV
AX,0FFFFH
PUSH
AX
MOV
AX,0H
PUSH
AX
RETF
;引导硬盘现有的操作系统
SUB2:
CALL
CLS
MOV
AX,0
MOV
ES,AX
MOV
BX,7C00H
PUSH
AX
PUSH
BX
MOV
DL,80H
MOV
DH,0
MOV
CH,0
MOV
CL,1
MOV
AL,1
MOV
AH,2
INT
13H
;读入硬盘0面0道1扇区的内容到0:7C00H
RETF
;CS:IP指向0:7C00H
;循环显示时间
SUB3:
;清屏,显示提示字符串,设置读取日期的循环次数及各项的起始地址
CALL
CLS
PRE_SHOW_DATE:
;输出提示信息
MOV
SI,OFFSET
PRESS_ESC
MOV
DX,0
MOV
CL,DATE_COLOR
CALL
SHOW_STR
MOV
SI,OFFSET
PRESS_F1
MOV
DX,0100H
CALL
SHOW_STR
MOV
CX,6
;循环次数=6
MOV
DI,OFFSET
DATE_POS ;DI用来定位需要读取的CMOS
ROM的单元
MOV
BX,OFFSET
DATE_CHAR_POS ;BX用来定位日期各项的位置
;读取字符串
READ_DATE:
PUSH
CX
;保存循环次数
;读取日期的一项,放置到日期字符串相应的位置
MOV
AL,[DI]
;CMOS ROM
日期的某单元
OUT
70H,AL
;向端口70H写入要访问的单元的地址
IN
AL,71H
;从端口71H获得要写入AL的数据
CALL
BCD2ASCII;BCD码转化为数字的ASCII码,(AH)=十位的ASCII码,(AL)=个位的ASCII码
PUSH
AX
;压入(AX),因为后面还要传送到DATE,而紧接着改变了(AX)
MOV
AL,[BX]
;(AL)=日期某项的位置
MOV
AH,0
;(AH)置零
MOV
SI,AX
;SI指向日期字符串某项的位置
POP
AX
;恢复(AX)
MOV
BYTE
PTR DATE[SI],AH
;传送
MOV
BYTE
PTR DATE[SI+1],AL<
577a2
/span>
INC
DI
;DI+1,定位到下一个单元
INC
BX
;BX+1,定位到DATE字符串下一个位置
POP
CX
;恢复循环次数
LOOP
READ_DATE ;继续传送日期的下一项到DATE
;显示日期字符串,DS:SI指向字符串首地址
MOV
DX,0300H
MOV
CL,DATE_COLOR
MOV
SI,OFFSET
DATE
CALL
SHOW_STR
SUB3_INPUT:
MOV
AH,1
;获取键盘输入,非阻塞
INT
16H
JZ
PRE_SHOW_DATE ;缓冲区无按键
CMP
AL,1BH
;ESC
JE
SUB3_RET
CMP
AH,3BH
;F1
JE
CHG_COLOR
JMP
CLEAR_BUFFER
CHG_COLOR:
INC
DATE_COLOR
CLEAR_BUFFER:
MOV
AH,0
;16H中断的1号功能不会清除键盘缓冲区,下次读取还会读出
INT
16H
;调用0号功能清除一次
CALL
DELAY
JMP
PRE_SHOW_DATE
SUB3_RET:
MOV
AH,0
;16H中断的1号功能不会清除键盘缓冲区,下次读取还会读出
INT
16H
;调用0号功能清除一次
MOV
CL,2
MOV
DATE_COLOR,CL
RET
;修改当前日期、时间
SUB4:
CALL
CLS
;输出提示信息
MOV
SI,OFFSET
PRESS_ESC
MOV
DX,0
MOV
CL,2
CALL
SHOW_STR
MOV
SI,OFFSET
INPUT_DATE
MOV
DX,0100H
CALL
SHOW_STR
;设置字符栈的位置,获取日期输入前的准备
MOV
SI,OFFSET
DATE_INPUT
MOV
DX,0300H
;显示的位置
GET_DATE:
MOV
AH,0
INT
16H
CMP
AL,20H
;空格
JE
PUSH_CHAR
CMP
AL,3AH
;冒号
JE
PUSH_CHAR
CMP
AL,2FH
JB
NO_DATE ;ASCII码小于2FH,说明不是数字或斜杠'/'
CMP
AL,39H
JA
NO_DATE ;ASCII码大于39H,说明不是数字或斜杠'/'
PUSH_CHAR:
MOV
AH,0
CALL
CHARSTACK ;字符入栈
MOV
AH,2
CALL
CHARSTACK ;字符串显示
JMP
GET_DATE
NO_DATE:
CMP
AH,0EH
;退格
JE
BACKSPACE
CMP
AH,1CH
;ENTER
JE
PRESS_ENTER
CMP
AL,1BH
;ESC
JE
ESC_SUB4
JMP
GET_DATE
BACKSPACE:
MOV
AH,1
CALL
CHARSTACK ;字符出栈
MOV
AH,2
CALL
CHARSTACK ;字符串显示
JMP
GET_DATE
ESC_SUB4:
;退出SUB4,DATE字符串的内容未被改变
MOV
AH,0
CALL
CHARSTACK
JMP
SUB4_RET
PRESS_ENTER:
MOV
AL,0
MOV
AH,0
CALL
CHARSTACK ;0入栈
MOV
AH,2
CALL
CHARSTACK ;字符串显示
;完成输入,复制
DATE_INPUT 的内容到 DATE
MOV
SI,OFFSET
DATE_INPUT
PUSH
CS
POP
ES
MOV
DI,OFFSET
DATE
MOV
CX,18
REP
MOVSB
MOV
CX,6
;循环次数=6
MOV
DI,OFFSET
DATE_POS ;DI用来定位需要读取的CMOS
ROM的单元
MOV
BX,OFFSET
DATE_CHAR_POS ;BX用来定位日期各项的位置
SET_DATE:
PUSH
CX
;保存循环次数
;读取日期的一项,放置到日期字符串相应的位置
MOV
AL,[DI]
;CMOS ROM
日期的某单元
OUT
70H,AL
;向端口70H写入要访问的单元的地址
MOV
AL,[BX]
MOV
AH,0
MOV
SI,AX
MOV
AH,DATE[SI]
;传送日期项的内容
MOV
AL,DATE[SI+1]
CALL
ASCII2BCD ;两位数字的ASCII码转化为BCD码(AH的高4位=十位,AH的低4位=个位)
OUT
71H,AL
;将日期项的BCD码写入到CMOS
ROM
INC
DI
;DI+1,定位到下一个单元
INC
BX
;BX+1,定位到DATE字符串下一个位置
POP
CX
;恢复循环次数
LOOP
SET_DATE ;继续传送日期的下一项到DATE
SUB4_RET:
RET
;--------------------------------------------------------------------------------------------
;功能:在指定的位置,用指定的颜色,显示一个用0结尾的字符串。
;参数:(DH)=行号(0~24),(DL)=列号(0~79),(CL)=颜色,DS:SI指向字符串的首地址。
;返回:无
SHOW_STR:
PUSH
AX
;保护现场
PUSH
ES
PUSH
DX
PUSH
DI
PUSH
SI
MOV
AX,0B800H
;显示缓冲区段地址
MOV
ES,AX
;(ES)=显示缓冲区段地址
MOV
AL,0A0H
;以下计算初始字符的偏移地址
MUL
DH
;行数×0A0H(160个字节)
MOV
DI,AX
;转移到DI中
MOV
AL,2
;显示缓冲区中一个字符占两个字节空间
MUL
DL
;2×列号
ADD
DI,AX
;获得初始字符的偏移地址
S:
MOV
AX,DS:[SI]
;输出字符到显示缓冲区
MOV
ES:[DI],AX
INC
DI
;准备写入颜色信息
MOV
ES:[DI],CL
;写入颜色信息
INC
SI
;准备输出下一个字符
PUSH
CX
;保存颜色=(CL)
MOV
CX,DS:[SI]
;(CX)=下一个字符
MOV
CH,0
;!!!若DS:[SI]的低位字节为零,但其高位字节不为零,
;!!!则程序不能如期望的那样跳转到END_SHOW
JCXZ
END_SHOW ;不为零则继续输出,为零则结束子程序
POP
CX
;恢复颜色=(CL)
INC
DI
;准备写入下一个字符
JMP
S ;输出下一个字符
END_SHOW:
POP
CX
;!!!如果(CX)≠0,就会跳转到这里,此时(CX)在栈中还没有弹出
;!!!如果不弹出就会引发错误
POP
SI
;恢复现场
POP
DI
POP
DX
POP
ES
POP
AX
RET
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:清屏
;参数:无
;返回:无
CLS:
PUSH
AX
PUSH
CX
PUSH
DI
PUSH
ES
MOV
AX,0B800H
MOV
ES,AX
MOV
DI,0
MOV
CX,2000
CLS_S:
MOV
BYTE PTR
ES:[DI],0
;为什么把BYTE改成WORD会引发崩溃?
;然而等我写好了程序再把
BYTE 改成 WORD
,发现并不会崩溃,但
;一开始最精简的程序又会崩溃,显示不出来东西,这又是为什么呢?
;问题待查
INC
DI
INC
DI
LOOP
CLS_S
;恢复颜色
MOV
DI,1
MOV
CX,2000
RESET_COLOR:
MOV
BYTE
PTR
ES:[DI],7
INC
DI
INC
DI
LOOP
RESET_COLOR
POP
ES
POP
DI
POP
CX
POP
AX
RET
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:把一个byte、两位数的BCD码转变成2个byte的ASCII码
;参数:(AL)=十进制两位数的BCD码(十位=高4位的BCD码,个位=低4位的BCD码)
;返回:(AH)=十位的ASCII码,(AL)=个位的ASCII码
BCD2ASCII:
PUSH
CX
;保存用到的寄存器
MOV
AH,AL
;AL中的BCD码复制一份到AH中
MOV
CL,4
;右移4位,取得月份十位的值
SHR
AH,CL
;(AH)=月份十位的值
AND
AL,00001111B;(AL)=月份个位的值
ADD
AH,30H
;(AH)=月份十位的ASCII码
ADD
AL,30H
;(AL)=月份个位的ASCII码
POP
CX
;恢复用到的寄存器
RET
;返回
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:把2个byte的ASCII码转变成一个byte、两位数的BCD码
;参数:(AH)=十位的ASCII码,(AL)=个位的ASCII码
;返回:(AL)=十进制两位数的BCD码(十位=高4位的BCD码,个位=低4位的BCD码)
ASCII2BCD:
PUSH
CX
;保存用到的寄存器
SUB
AH,30H
;(AH的低4位)=月份十位的BCD码
SUB
AL,30H
;(AL的低4位)=月份个位的BCD码
MOV
CL,4
;左移4位
SHL
AH,CL
;(AH的高4位)=月份十位的BCD码
OR
AH,00001111B
;(AH低4位)置1
OR
AL,11110000B
;(AL高4位)置1
AND
AL,AH
;(AL)=十进制两位数的BCD码
POP
CX
;恢复用到的寄存器
RET
;返回
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:产生延时
;参数:无
;返回:无
DELAY:
PUSH
AX
PUSH
DX
MOV
DX,1000
MOV
AX,0
DELAY_S:
SUB
AX,1
SBB
DX,0
CMP
AX,0
JNE
DELAY_S
CMP
DX,0
JNE
DELAY_S
POP
DX
POP
AX
RET
;返回
;--------------------------------------------------------------------------------------------
;字符栈
;--------------------------------------------------------------------------------------------
;功能:0号:字符入栈
1号:字符出栈
2号:显示栈中的字符
;参数:
(AH)=功能选择,(AL)=入栈的字符,DS:SI指向字符栈空间
;
(DH)=显示的行位置,(DL)=显示的列位置
;返回:(AL)=出栈的字符
CHARSTACK:
JMP
CHARSTART
CHAR_TABLE DW
CHARPUSH,CHARPOP,CHARSHOW
TOP DW
0
CHARSTART:
PUSH
BX
CMP
AH,2
JA
SRET ;没有对应的功能号,结束子程序
;调用相应的子功能
MOV
BL,AH
MOV
BH,0
SHL
BX,1
CALL
WORD
PTR CHAR_TABLE[BX]
;限制输入的长度,如果超过了日期字符串的长度,将TOP置为0
CMP
TOP,18
JNE
SRET
MOV
TOP,0
SRET:
POP
BX
RET
;字符入栈,(AL)=入栈的字符
CHARPUSH:
CMP
AL,1BH
;如果是ESC
JE
ESC_INPUT ;跳转到ESC_INPUT
PUSH
BX
MOV
BX,TOP
MOV
[BX][SI],AL
;字符入栈
MOV
BYTE
PTR
1[BX][SI],0
INC
TOP ;TOP指向新的栈顶
POP
BX
JMP
CHARPUSH_RET
ESC_INPUT:
MOV
TOP,0
;TOP置为0
CHARPUSH_RET:
RET
;字符出栈,(AL)=出栈的字符
CHARPOP:
PUSH
BX
CMP
TOP,0
;是否已到栈底
JE
CHARPOPRET ;是则不出栈,结束子功能
DEC
TOP ;TOP指向要出栈的字符
MOV
BX,TOP
MOV
AL,[BX][SI]
;字符出栈
MOV
BYTE
PTR
[BX][SI],'
'
CHARPOPRET:
POP
BX
RET
;DS:SI指向字符栈空间,(DH)=显示的行位置,(DL)=显示的列位置
CHARSHOW:
PUSH
AX
PUSH
BX
PUSH
DX
PUSH
DI
PUSH
ES
;使ES:SI指向显示的位置,DS:SI指向字符栈空间
MOV
BX,0B800H
MOV
ES,BX
MOV
AL,160
MUL
DH
MOV
DI,AX
MOV
AL,DL
SHL
AL,1
MOV
AH,0
ADD
DI,AX
MOV
BX,0
CHARSHOWS:
CMP
TOP,0
JE
SET_CURSOR ;处理删除最后一个字符的事件
CMP
BX,TOP
JE
ENDSHOW
MOV
AL,[BX][SI]
MOV
ES:[DI],AL
MOV
ES:[DI+2],BYTE
PTR
' '
INC
BX
INC
DI
INC
DI
JMP
CHARSHOWS
SET_CURSOR:
MOV
BYTE
PTR
ES:[DI],'
'
ENDSHOW:
POP
ES
POP
DI
POP
DX
POP
BX
POP
AX
RET
;--------------------------------------------------------------------------------------------
SYS
ENDS
;============================================================================================
;安装过程的第一行指令
END
START
1.
采用怎样的引导方式?是把所有任务程序复制到0:7C00处,还是把引导程序复制到0:7C00处,再执行引导程序把任务程序复制到别处内存?采用后一种方式。原因:程序的子功能2是从硬盘引导现有的操作系统,这需要把硬盘0面0道1扇区的内容复制到0:7C00处,也就覆盖了原来0:7C00处的内容,谁知道这时0:7C00处是什么内容呢?又怎样来重新定位CS:IP呢?
2.
没有检测用户输入日期的格式(但是只可以输入数字、空格、英文冒号’:’、斜杠’/’和ENTER),日期显示仍有闪烁,其余完美。
3. 其他内容可以参见注释。
ASSUME
CS:SETUP
;============================================================================================
;安装程序
;将引导所需的程序写入软盘
SETUP
SEGMENT
START:
;复制装载程序到软盘0面0道1扇区
MOV
AX,LOAD
MOV
ES,AX
MOV
BX,0
;ES:BX指向要写的单元
MOV
DX,0
MOV
CX,1
MOV
AL,1
MOV
AH,3
INT
13H
;复制任务程序到软盘0面0道2~3扇区
MOV
AX,SYS
MOV
ES,AX
;BX没变
MOV
CX,2
;DX没变
MOV
AL,2
MOV
AH,3
INT
13H
MOV
AX,4C00H
INT
21H
SETUP
ENDS
;============================================================================================
;============================================================================================
;引导程序
LOAD
SEGMENT
;装载任务程序到指定内存处(此处2000:0)
;此处程序应该按照0:7C00的观点来看
;注意,这里是用软盘启动计算机,并没有DOS中断可用
ASSUME
CS:LOAD
;读任务程序到2000:0
MOV
AX,2000H
MOV
ES,AX
MOV
BX,0
PUSH
AX
PUSH
BX
MOV
DX,0
MOV
CX,2
MOV
AL,2
MOV
AH,2
INT
13H
;将CS:IP指向任务程序的首地址2000:0
RETF
LOAD
ENDS
;============================================================================================
;============================================================================================
;任务程序
SYS
SEGMENT
ASSUME
CS:SYS
;隐藏光标
MOV
AH,01H
MOV
CX,3030H
INT
10H
MOV
CX,OFFSET
ENDSHOW-OFFSET
SYS_START
SYS_START:
JMP
SHOW_MENU
DATE DB
"00/00/00 00:00:00",0
;日期字符串
DATE_INPUT DB
"00/00/0000:00:00",0
;用户输入的日期字符串,当输入不正确或者退出输入时,
;可以避免改变原始日期
DATE_POS DB
0,2,4,7,8,9
;定位
CMOS RAM 中日期各项的地址
DATE_CHAR_POS DB
15,12,9,6,3,0
;定位日期字符串中各项的位置
DATE_COLOR DB
2
;提示字符串
PRESS_ESC DB
"PRESS ESC TORETURN MENU",0
PRESS_F1 DB
"PRESS F1 TOCHANGE COLOR",0
INPUT_DATE DB
"INPUT A DATEIN THE FORM OF YY/MM/DD HH:MM:SS,PRESS ENTER TO END INPUT",0
OPTION1 DB
"1) RESET PC",0
OPTION2 DB
"2) START SYSTEM",0
OPTION3 DB
"3) CLOCK",0
OPTION4 DB
"4) SET CLOCK",0
;直接定址表
TABLE DW
SUB1,SUB2,SUB3,SUB4
;列出功能选项
SHOW_MENU:
CALL
CLS
PUSH
CS
POP
DS
MOV
SI,OFFSET
OPTION1
MOV
DX,0
MOV
CL,2
CALL
SHOW_STR
INC
DH
MOV
SI,OFFSET
OPTION2
CALL
SHOW_STR
INC
DH
MOV
SI,OFFSET
OPTION3
CALL
SHOW_STR
INC
DH
MOV
SI,OFFSET
OPTION4
CALL
SHOW_STR
GET_INPUT:
MOV
AH,0
;获取键盘输入
INT
16H
SUB
AL,30H
CMP
AL,1
JB
GET_INPUT
CMP
AL,4
JA
GET_INPUT
MOV
BL,AL
DEC
BL
;1~4转化为0~3
MOV
BH,0
SHL
BX,1
CALL
WORD
PTR
CS:TABLE[BX]
;进入子程序
JMP
SHOW_MENU
;重新启动计算机
SUB1:
MOV
AX,0FFFFH
PUSH
AX
MOV
AX,0H
PUSH
AX
RETF
;引导硬盘现有的操作系统
SUB2:
CALL
CLS
MOV
AX,0
MOV
ES,AX
MOV
BX,7C00H
PUSH
AX
PUSH
BX
MOV
DL,80H
MOV
DH,0
MOV
CH,0
MOV
CL,1
MOV
AL,1
MOV
AH,2
INT
13H
;读入硬盘0面0道1扇区的内容到0:7C00H
RETF
;CS:IP指向0:7C00H
;循环显示时间
SUB3:
;清屏,显示提示字符串,设置读取日期的循环次数及各项的起始地址
CALL
CLS
PRE_SHOW_DATE:
;输出提示信息
MOV
SI,OFFSET
PRESS_ESC
MOV
DX,0
MOV
CL,DATE_COLOR
CALL
SHOW_STR
MOV
SI,OFFSET
PRESS_F1
MOV
DX,0100H
CALL
SHOW_STR
MOV
CX,6
;循环次数=6
MOV
DI,OFFSET
DATE_POS ;DI用来定位需要读取的CMOS
ROM的单元
MOV
BX,OFFSET
DATE_CHAR_POS ;BX用来定位日期各项的位置
;读取字符串
READ_DATE:
PUSH
CX
;保存循环次数
;读取日期的一项,放置到日期字符串相应的位置
MOV
AL,[DI]
;CMOS ROM
日期的某单元
OUT
70H,AL
;向端口70H写入要访问的单元的地址
IN
AL,71H
;从端口71H获得要写入AL的数据
CALL
BCD2ASCII;BCD码转化为数字的ASCII码,(AH)=十位的ASCII码,(AL)=个位的ASCII码
PUSH
AX
;压入(AX),因为后面还要传送到DATE,而紧接着改变了(AX)
MOV
AL,[BX]
;(AL)=日期某项的位置
MOV
AH,0
;(AH)置零
MOV
SI,AX
;SI指向日期字符串某项的位置
POP
AX
;恢复(AX)
MOV
BYTE
PTR DATE[SI],AH
;传送
MOV
BYTE
PTR DATE[SI+1],AL<
577a2
/span>
INC
DI
;DI+1,定位到下一个单元
INC
BX
;BX+1,定位到DATE字符串下一个位置
POP
CX
;恢复循环次数
LOOP
READ_DATE ;继续传送日期的下一项到DATE
;显示日期字符串,DS:SI指向字符串首地址
MOV
DX,0300H
MOV
CL,DATE_COLOR
MOV
SI,OFFSET
DATE
CALL
SHOW_STR
SUB3_INPUT:
MOV
AH,1
;获取键盘输入,非阻塞
INT
16H
JZ
PRE_SHOW_DATE ;缓冲区无按键
CMP
AL,1BH
;ESC
JE
SUB3_RET
CMP
AH,3BH
;F1
JE
CHG_COLOR
JMP
CLEAR_BUFFER
CHG_COLOR:
INC
DATE_COLOR
CLEAR_BUFFER:
MOV
AH,0
;16H中断的1号功能不会清除键盘缓冲区,下次读取还会读出
INT
16H
;调用0号功能清除一次
CALL
DELAY
JMP
PRE_SHOW_DATE
SUB3_RET:
MOV
AH,0
;16H中断的1号功能不会清除键盘缓冲区,下次读取还会读出
INT
16H
;调用0号功能清除一次
MOV
CL,2
MOV
DATE_COLOR,CL
RET
;修改当前日期、时间
SUB4:
CALL
CLS
;输出提示信息
MOV
SI,OFFSET
PRESS_ESC
MOV
DX,0
MOV
CL,2
CALL
SHOW_STR
MOV
SI,OFFSET
INPUT_DATE
MOV
DX,0100H
CALL
SHOW_STR
;设置字符栈的位置,获取日期输入前的准备
MOV
SI,OFFSET
DATE_INPUT
MOV
DX,0300H
;显示的位置
GET_DATE:
MOV
AH,0
INT
16H
CMP
AL,20H
;空格
JE
PUSH_CHAR
CMP
AL,3AH
;冒号
JE
PUSH_CHAR
CMP
AL,2FH
JB
NO_DATE ;ASCII码小于2FH,说明不是数字或斜杠'/'
CMP
AL,39H
JA
NO_DATE ;ASCII码大于39H,说明不是数字或斜杠'/'
PUSH_CHAR:
MOV
AH,0
CALL
CHARSTACK ;字符入栈
MOV
AH,2
CALL
CHARSTACK ;字符串显示
JMP
GET_DATE
NO_DATE:
CMP
AH,0EH
;退格
JE
BACKSPACE
CMP
AH,1CH
;ENTER
JE
PRESS_ENTER
CMP
AL,1BH
;ESC
JE
ESC_SUB4
JMP
GET_DATE
BACKSPACE:
MOV
AH,1
CALL
CHARSTACK ;字符出栈
MOV
AH,2
CALL
CHARSTACK ;字符串显示
JMP
GET_DATE
ESC_SUB4:
;退出SUB4,DATE字符串的内容未被改变
MOV
AH,0
CALL
CHARSTACK
JMP
SUB4_RET
PRESS_ENTER:
MOV
AL,0
MOV
AH,0
CALL
CHARSTACK ;0入栈
MOV
AH,2
CALL
CHARSTACK ;字符串显示
;完成输入,复制
DATE_INPUT 的内容到 DATE
MOV
SI,OFFSET
DATE_INPUT
PUSH
CS
POP
ES
MOV
DI,OFFSET
DATE
MOV
CX,18
REP
MOVSB
MOV
CX,6
;循环次数=6
MOV
DI,OFFSET
DATE_POS ;DI用来定位需要读取的CMOS
ROM的单元
MOV
BX,OFFSET
DATE_CHAR_POS ;BX用来定位日期各项的位置
SET_DATE:
PUSH
CX
;保存循环次数
;读取日期的一项,放置到日期字符串相应的位置
MOV
AL,[DI]
;CMOS ROM
日期的某单元
OUT
70H,AL
;向端口70H写入要访问的单元的地址
MOV
AL,[BX]
MOV
AH,0
MOV
SI,AX
MOV
AH,DATE[SI]
;传送日期项的内容
MOV
AL,DATE[SI+1]
CALL
ASCII2BCD ;两位数字的ASCII码转化为BCD码(AH的高4位=十位,AH的低4位=个位)
OUT
71H,AL
;将日期项的BCD码写入到CMOS
ROM
INC
DI
;DI+1,定位到下一个单元
INC
BX
;BX+1,定位到DATE字符串下一个位置
POP
CX
;恢复循环次数
LOOP
SET_DATE ;继续传送日期的下一项到DATE
SUB4_RET:
RET
;--------------------------------------------------------------------------------------------
;功能:在指定的位置,用指定的颜色,显示一个用0结尾的字符串。
;参数:(DH)=行号(0~24),(DL)=列号(0~79),(CL)=颜色,DS:SI指向字符串的首地址。
;返回:无
SHOW_STR:
PUSH
AX
;保护现场
PUSH
ES
PUSH
DX
PUSH
DI
PUSH
SI
MOV
AX,0B800H
;显示缓冲区段地址
MOV
ES,AX
;(ES)=显示缓冲区段地址
MOV
AL,0A0H
;以下计算初始字符的偏移地址
MUL
DH
;行数×0A0H(160个字节)
MOV
DI,AX
;转移到DI中
MOV
AL,2
;显示缓冲区中一个字符占两个字节空间
MUL
DL
;2×列号
ADD
DI,AX
;获得初始字符的偏移地址
S:
MOV
AX,DS:[SI]
;输出字符到显示缓冲区
MOV
ES:[DI],AX
INC
DI
;准备写入颜色信息
MOV
ES:[DI],CL
;写入颜色信息
INC
SI
;准备输出下一个字符
PUSH
CX
;保存颜色=(CL)
MOV
CX,DS:[SI]
;(CX)=下一个字符
MOV
CH,0
;!!!若DS:[SI]的低位字节为零,但其高位字节不为零,
;!!!则程序不能如期望的那样跳转到END_SHOW
JCXZ
END_SHOW ;不为零则继续输出,为零则结束子程序
POP
CX
;恢复颜色=(CL)
INC
DI
;准备写入下一个字符
JMP
S ;输出下一个字符
END_SHOW:
POP
CX
;!!!如果(CX)≠0,就会跳转到这里,此时(CX)在栈中还没有弹出
;!!!如果不弹出就会引发错误
POP
SI
;恢复现场
POP
DI
POP
DX
POP
ES
POP
AX
RET
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:清屏
;参数:无
;返回:无
CLS:
PUSH
AX
PUSH
CX
PUSH
DI
PUSH
ES
MOV
AX,0B800H
MOV
ES,AX
MOV
DI,0
MOV
CX,2000
CLS_S:
MOV
BYTE PTR
ES:[DI],0
;为什么把BYTE改成WORD会引发崩溃?
;然而等我写好了程序再把
BYTE 改成 WORD
,发现并不会崩溃,但
;一开始最精简的程序又会崩溃,显示不出来东西,这又是为什么呢?
;问题待查
INC
DI
INC
DI
LOOP
CLS_S
;恢复颜色
MOV
DI,1
MOV
CX,2000
RESET_COLOR:
MOV
BYTE
PTR
ES:[DI],7
INC
DI
INC
DI
LOOP
RESET_COLOR
POP
ES
POP
DI
POP
CX
POP
AX
RET
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:把一个byte、两位数的BCD码转变成2个byte的ASCII码
;参数:(AL)=十进制两位数的BCD码(十位=高4位的BCD码,个位=低4位的BCD码)
;返回:(AH)=十位的ASCII码,(AL)=个位的ASCII码
BCD2ASCII:
PUSH
CX
;保存用到的寄存器
MOV
AH,AL
;AL中的BCD码复制一份到AH中
MOV
CL,4
;右移4位,取得月份十位的值
SHR
AH,CL
;(AH)=月份十位的值
AND
AL,00001111B;(AL)=月份个位的值
ADD
AH,30H
;(AH)=月份十位的ASCII码
ADD
AL,30H
;(AL)=月份个位的ASCII码
POP
CX
;恢复用到的寄存器
RET
;返回
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:把2个byte的ASCII码转变成一个byte、两位数的BCD码
;参数:(AH)=十位的ASCII码,(AL)=个位的ASCII码
;返回:(AL)=十进制两位数的BCD码(十位=高4位的BCD码,个位=低4位的BCD码)
ASCII2BCD:
PUSH
CX
;保存用到的寄存器
SUB
AH,30H
;(AH的低4位)=月份十位的BCD码
SUB
AL,30H
;(AL的低4位)=月份个位的BCD码
MOV
CL,4
;左移4位
SHL
AH,CL
;(AH的高4位)=月份十位的BCD码
OR
AH,00001111B
;(AH低4位)置1
OR
AL,11110000B
;(AL高4位)置1
AND
AL,AH
;(AL)=十进制两位数的BCD码
POP
CX
;恢复用到的寄存器
RET
;返回
;--------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------
;功能:产生延时
;参数:无
;返回:无
DELAY:
PUSH
AX
PUSH
DX
MOV
DX,1000
MOV
AX,0
DELAY_S:
SUB
AX,1
SBB
DX,0
CMP
AX,0
JNE
DELAY_S
CMP
DX,0
JNE
DELAY_S
POP
DX
POP
AX
RET
;返回
;--------------------------------------------------------------------------------------------
;字符栈
;--------------------------------------------------------------------------------------------
;功能:0号:字符入栈
1号:字符出栈
2号:显示栈中的字符
;参数:
(AH)=功能选择,(AL)=入栈的字符,DS:SI指向字符栈空间
;
(DH)=显示的行位置,(DL)=显示的列位置
;返回:(AL)=出栈的字符
CHARSTACK:
JMP
CHARSTART
CHAR_TABLE DW
CHARPUSH,CHARPOP,CHARSHOW
TOP DW
0
CHARSTART:
PUSH
BX
CMP
AH,2
JA
SRET ;没有对应的功能号,结束子程序
;调用相应的子功能
MOV
BL,AH
MOV
BH,0
SHL
BX,1
CALL
WORD
PTR CHAR_TABLE[BX]
;限制输入的长度,如果超过了日期字符串的长度,将TOP置为0
CMP
TOP,18
JNE
SRET
MOV
TOP,0
SRET:
POP
BX
RET
;字符入栈,(AL)=入栈的字符
CHARPUSH:
CMP
AL,1BH
;如果是ESC
JE
ESC_INPUT ;跳转到ESC_INPUT
PUSH
BX
MOV
BX,TOP
MOV
[BX][SI],AL
;字符入栈
MOV
BYTE
PTR
1[BX][SI],0
INC
TOP ;TOP指向新的栈顶
POP
BX
JMP
CHARPUSH_RET
ESC_INPUT:
MOV
TOP,0
;TOP置为0
CHARPUSH_RET:
RET
;字符出栈,(AL)=出栈的字符
CHARPOP:
PUSH
BX
CMP
TOP,0
;是否已到栈底
JE
CHARPOPRET ;是则不出栈,结束子功能
DEC
TOP ;TOP指向要出栈的字符
MOV
BX,TOP
MOV
AL,[BX][SI]
;字符出栈
MOV
BYTE
PTR
[BX][SI],'
'
CHARPOPRET:
POP
BX
RET
;DS:SI指向字符栈空间,(DH)=显示的行位置,(DL)=显示的列位置
CHARSHOW:
PUSH
AX
PUSH
BX
PUSH
DX
PUSH
DI
PUSH
ES
;使ES:SI指向显示的位置,DS:SI指向字符栈空间
MOV
BX,0B800H
MOV
ES,BX
MOV
AL,160
MUL
DH
MOV
DI,AX
MOV
AL,DL
SHL
AL,1
MOV
AH,0
ADD
DI,AX
MOV
BX,0
CHARSHOWS:
CMP
TOP,0
JE
SET_CURSOR ;处理删除最后一个字符的事件
CMP
BX,TOP
JE
ENDSHOW
MOV
AL,[BX][SI]
MOV
ES:[DI],AL
MOV
ES:[DI+2],BYTE
PTR
' '
INC
BX
INC
DI
INC
DI
JMP
CHARSHOWS
SET_CURSOR:
MOV
BYTE
PTR
ES:[DI],'
'
ENDSHOW:
POP
ES
POP
DI
POP
DX
POP
BX
POP
AX
RET
;--------------------------------------------------------------------------------------------
SYS
ENDS
;============================================================================================
;安装过程的第一行指令
END
START
1.
采用怎样的引导方式?是把所有任务程序复制到0:7C00处,还是把引导程序复制到0:7C00处,再执行引导程序把任务程序复制到别处内存?采用后一种方式。原因:程序的子功能2是从硬盘引导现有的操作系统,这需要把硬盘0面0道1扇区的内容复制到0:7C00处,也就覆盖了原来0:7C00处的内容,谁知道这时0:7C00处是什么内容呢?又怎样来重新定位CS:IP呢?
2.
没有检测用户输入日期的格式(但是只可以输入数字、空格、英文冒号’:’、斜杠’/’和ENTER),日期显示仍有闪烁,其余完美。
3. 其他内容可以参见注释。
相关文章推荐
- 王爽《汇编语言》第十章 课程设计1 解答
- 王爽《汇编语言》课程设计二续(包含软盘操作)
- 王爽《汇编语言》课程设计1
- 王爽 《汇编语言》课程设计1
- 王爽《汇编语言》P211页课程设计
- 王爽《汇编语言》课程设计1
- 王爽《汇编语言》课程设计二(只包含CMOS RAM操作不包含软盘操作)
- 《汇编语言》(第三版)王爽第十章 课程设计1 个人方法记录
- 王爽《汇编语言》课程设计1
- 王爽 《汇编语言》课程设计2
- 王爽著《汇编语言》课程设计1(Page211)源码答案
- 王爽老师《汇编语言》课程设计2
- 王爽版《汇编语言》课程设计1
- 《汇编语言(第3版)》王爽 课程设计2
- 《汇编语言》王爽 课程设计1
- 《汇编语言(第3版)》王爽 课程设计1
- 王爽《汇编语言》第10章 课程设计1
- 王爽汇编语言课程设计一
- 王爽《汇编》课程设计一
- 王爽 -- 汇编语言课程设计2一些问题记录