求一个汇编语言课程设计的MASM32时钟的程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求一个汇编语言课程设计的MASM32时钟的程序相关的知识,希望对你有一定的参考价值。

edit的 8086的 谢谢 使程序运行后屏幕显示器成为一台电子钟。首先屏幕上显示提示符,要求从键盘上输入当前时间,然后每隔一秒使显示的秒值加1,达到60秒时使分值加1,秒值清零;达到60分时使小时值加1,分值清零;达到24小时则小时值清零。上述过程一直进行下去,当键入Ctrl+C时退出“电子钟”状态,返回DOS。

/*电子时钟源代码*/
#include<graphics.h>
#include<stdio.h>
#include<math.h>
#include<dos.h>
#define PI 3.1415926 /*定义常量*/
#define UP 0x4800 /*上移↑键:修改时间*/
#define DOWN 0x5000 /*下移↓键:修改时间*/
#define ESC 0x11b /*ESC键 : 退出系统*/
#define TAB 0xf09 /*TAB键 : 移动光标*/
/*函数声明*/
int keyhandle(int,int); /*键盘按键判断,并调用相关函数处理*/
int timeupchange(int); /*处理上移按键*/
int timedownchange(int); /*处理下移按键*/
int digithour(double); /*将double型的小时数转换成int型*/
int digitmin(double); /*将double型的分钟数转换成int型*/
int digitsec(double); /*将double型的秒钟数转换成int型*/
void digitclock(int,int,int ); /*在指定位置显示时钟或分钟或秒钟数*/
void drawcursor(int); /*绘制一个光标*/
void clearcursor(int);/*消除前一个光标*/
void clockhandle(); /*时钟处理*/

double h,m,s; /*全局变量:小时,分,秒*/
double x,x1,x2,y,y1,y2; /*全局变量:坐标值*/
struct time t[1];/*定义一个time结构类型的数组*/
main()

int driver, mode=0,i,j;
driver=DETECT; /*自动检测显示设备*/
initgraph(&driver, &mode, "");/*初始化图形系统*/
setlinestyle(0,0,3); /*设置当前画线宽度和类型:设置三点宽实线*/
setbkcolor(0);/*用调色板设置当前背景颜色*/
setcolor(9); /*设置当前画线颜色*/
line(82,430,558,430);
line(70,62,70,418);
line(82,50,558,50);
line(570,62,570,418);
line(70,62,570,62);
line(76,56,297,56);
line(340,56,564,56); /*画主体框架的边直线*/
/*arc(int x, int y, int stangle, int endangle, int radius)*/
arc(82,62,90,180,12);
arc(558,62,0,90,12);
setlinestyle(0,0,3);
arc(82,418,180,279,12);
setlinestyle(0,0,3);
arc(558,418,270,360,12); /*画主体框架的边角弧线*/
setcolor(15);
outtextxy(300,53,"CLOCK"); /*显示标题*/
setcolor(7);
rectangle(342,72,560,360); /*画一个矩形,作为时钟的框架*/

setwritemode(0); /*规定画线的方式。mode=0, 则表示画线时将所画位置的原来信息覆盖*/
setcolor(15);
outtextxy(433,75,"CLOCK");/*时钟的标题*/
setcolor(7);
line(392,310,510,310);
line(392,330,510,330);
arc(392,320,90,270,10);
arc(510,320,270,90,10); /*绘制电子动画时钟下的数字时钟的边框架*/
/*绘制数字时钟的时分秒的分隔符*/
setcolor(5);
for(i=431;i<=470;i+=39)
for(j=317;j<=324;j+=7)
setlinestyle(0,0,3);
circle(i,j,1); /*以(i, y)为圆心,1为半径画圆*/

setcolor(15);
line(424,315,424,325); /*在运行电子时钟前先画一个光标*/
/*绘制表示小时的圆点*/
for(i=0,m=0,h=0;i<=11;i++,h++)
x=100*sin((h*60+m)/360*PI)+451;
y=200-100*cos((h*60+m)/360*PI);
setlinestyle(0,0,3);
circle(x,y,1);

/*绘制表示分钟或秒钟的圆点*/
for(i=0,m=0;i<=59;m++,i++)
x=100*sin(m/30*PI)+451;
y=200-100*cos(m/30*PI);
setlinestyle(0,0,1);
circle(x,y,1);

/*在电子表的左边打印帮助提示信息*/
setcolor(4);
outtextxy(184,125,"HELP");
setcolor(15);
outtextxy(182,125,"HELP");
setcolor(5);
outtextxy(140,185,"TAB : Cursor move");
outtextxy(140,225,"UP : Time ++");
outtextxy(140,265,"DOWN: Time --");
outtextxy(140,305,"ESC : Quit system!");
outtextxy(140,345,"Version : 2.0");
setcolor(12);
outtextxy(150,400,"Nothing is more important than time!");
clockhandle();/*开始调用时钟处理程序*/
closegraph(); /*关闭图形系统*/
return 0; /*表示程序正常结束,向操作系统返回一个0值*/


void clockhandle()

int k=0,count;
setcolor(15);
gettime(t);/*取得系统时间,保存在time结构类型的数组变量中*/
h=t[0].ti_hour;
m=t[0].ti_min;
x=50*sin((h*60+m)/360*PI)+451; /*时针的x坐标值*/
y=200-50*cos((h*60+m)/360*PI); /*时针的y坐标值*/
line(451,200,x,y);/*在电子表中绘制时针*/

x1=80*sin(m/30*PI)+451; /*分针的x坐标值*/
y1=200-80*cos(m/30*PI); /*分针的y坐标值*/
line(451,200,x1,y1); /*在电子表中绘制分针*/

digitclock(408,318,digithour(h)); /*在数字时钟中,显示当前的小时值*/
digitclock(446,318,digitmin(m)); /*在数字时钟中,显示当前的分钟值*/
setwritemode(1);
/*规定画线的方式,如果mode=1,则表示画线时用现在特性的线
与所画之处原有的线进行异或(XOR)操作,实际上画出的线是原有线与现在规定
的线进行异或后的结果。因此, 当线的特性不变, 进行两次画线操作相当于没有
画线,即在当前位置处清除了原来的画线*/
for(count=2;k!=ESC;) /*开始循环,直至用户按下ESC键结束循环*/
setcolor(12);/*淡红色*/
sound(500);/*以指定频率打开PC扬声器,这里频率为500Hz*/
delay(700);/*发一个频率为500Hz的音调,维持700毫秒*/
sound(200);/*以指定频率打开PC扬声器,这里频率为200Hz*/
delay(300);
/*以上两种不同频率的音调,可仿真钟表转动时的嘀哒声*/
nosound(); /*关闭PC扬声器*/
s=t[0].ti_sec;
m=t[0].ti_min;
h=t[0].ti_hour;

x2=98*sin(s/30*PI)+451; /*秒针的x坐标值*/
y2=200-98*cos(s/30*PI); /*秒针的y坐标值*/
line(451,200,x2,y2);
/*绘制秒针*/

/*利用此循环,延时一秒*/
while(t[0].ti_sec==s&&t[0].ti_min==m&&t[0].ti_hour==h)
gettime(t);/*取得系统时间*/
if(bioskey(1)!=0)
k=bioskey(0);
count=keyhandle(k,count);
if(count==5) count=1;


setcolor(15);
digitclock(485,318,digitsec(s)+1);/*数字时钟增加1秒*/

setcolor(12); /*淡红色*/
x2=98*sin(s/30*PI)+451;
y2=200-98*cos(s/30*PI);
line(451,200,x2,y2);
/*用原来的颜色在原来位置处再绘制秒针,以达到清除当前秒针的目的*/

/*分钟处理*/
if(t[0].ti_min!=m) /*若分钟有变化*/
/*消除当前分针*/
setcolor(15); /*白色*/
x1=80*sin(m/30*PI)+451;
y1=200-80*cos(m/30*PI);
line(451,200,x1,y1);
/*绘制新的分针*/
m=t[0].ti_min;
digitclock(446,318,digitmin(m)); /*在数字时钟中显示新的分钟值*/
x1=80*sin(m/30*PI)+451;
y1=200-80*cos(m/30*PI);
line(451,200,x1,y1);


/*小时处理*/
if((t[0].ti_hour*60+t[0].ti_min)!=(h*60+m)) /*若小时数有变化*/
/*消除当前时针*/
setcolor(15); /*白色*/
x=50*sin((h*60+m)/360*PI)+451;/*50:时钟的长度(单位:像素),451:圆心的x坐标值*/
y=200-50*cos((h*60+m)/360*PI);
line(451,200,x,y);
/*绘制新的时针*/
h=t[0].ti_hour;
digitclock(408,318,digithour(h));
x=50*sin((h*60+m)/360*PI)+451;
y=200-50*cos((h*60+m)/360*PI);
line(451,200,x,y);




int keyhandle(int key,int count) /*键盘控制 */
switch(key)
case UP: timeupchange(count-1); /*因为count的初始值为2,所以此处减1*/
break;
case DOWN:timedownchange(count-1); /*因为count的初始值为2,所以此处减1*/
break;
case TAB:setcolor(15);
clearcursor(count); /*清除原来的光标*/
drawcursor(count); /*显示一个新的光标*/
count++;
break;

return count;


int timeupchange(int count) /*处理光标上移的按键*/

if(count==1)
t[0].ti_hour++;
if(t[0].ti_hour==24) t[0].ti_hour=0;
settime(t); /*设置新的系统时间*/


if(count==2)
t[0].ti_min++;
if(t[0].ti_min==60) t[0].ti_min=0;
settime(t); /*设置新的系统时间*/


if(count==3)
t[0].ti_sec++;
if(t[0].ti_sec==60) t[0].ti_sec=0;
settime(t); /*设置新的系统时间*/



int timedownchange(int count) /*处理光标下移的按键*/

if(count==1)
t[0].ti_hour--;
if(t[0].ti_hour==0) t[0].ti_hour=23;
settime(t);/*设置新的系统时间*/

if(count==2)
t[0].ti_min--;
if(t[0].ti_min==0) t[0].ti_min=59;
settime(t);/*设置新的系统时间*/


if(count==3)
t[0].ti_sec--;
if(t[0].ti_sec==0) t[0].ti_sec=59;
settime(t);/*设置新的系统时间*/



int digithour(double h)/*将double型的小时数转换成int型*/
int i;
for(i=0;i<=23;i++)
if(h==i) return i;


int digitmin(double m)/*将double型的分钟数转换成int型*/
int i;
for(i=0;i<=59;i++)
if(m==i) return i;


int digitsec(double s) /*将double型的秒钟数转换成int型*/
int i;
for(i=0;i<=59;i++)
if(s==i) return i;


void digitclock(int x,int y,int clock)/*在指定位置显示数字时钟:时\分\秒*/
char buffer1[10];
setfillstyle(0,2);
bar(x,y,x+15,328);
if(clock==60) clock=0;
sprintf(buffer1,"%d",clock);
outtextxy(x,y,buffer1);


void drawcursor(int count) /*根据count的值,画一个光标*/
switch(count)

case 1:line(424,315,424,325);break;
case 2:line(465,315,465,325);break;
case 3:line(505,315,505,325);break;



void clearcursor(int count) /*根据count的值,清除前一个光标*/
switch(count)

case 2:line(424,315,424,325);break;
case 3:line(465,315,465,325);break;
case 1:line(505,315,505,325);break;

参考技术A ;*********************************************************
; 带有时间设置和秒显示的数字闹钟
; Date : 2007.10.26 12MHZ晶振
; Create by :星星 缘木求鱼
; P3.2设置键 P3.3小时调整键 P3.5分钟调整键 P3.7定时输出指示
;**************************************************************
;*********************************************************
; 变量地址分配
;*********************************************************
SwDelay equ 2; 设置按键时去抖动时间
DisplayBuffer equ 30h; 设置显示缓冲区的地址为30h-35h共6个字节
BeepVal equ 38h; 蜂鸣时间长短存储器地址
OneSecondCounter equ 39h; 设置1秒计数器的地址,1秒计数器是用来计数1秒内计时器的中断次数
Hour equ 3ah; 设置小时计数器的地址
Minute equ 3bh; 设置分钟计数器的地址
Second equ 3ch; 设置秒计数器的地址
Year equ 3dh; 设置月日年计数器的地址
Month equ 3eh;
Day equ 3fh;
P1Val equ 40h; 设置数码管位驱动值的地址
ClockMode equ 20h.0; 模式(正常走时/闹时)设置寄存器地址,值为0时正常走时,为1时闹时设定
AlarmOnOff equ 20h.1; 闹钟开启/关闭标志,为0关闭,为1开启
AlarmTimeOn equ 20h.2; 此位为1时表示闹时时间到
DataMode equ 20h.3
DispHour equ 21h; 设置小时显示寄存器的地址
DispMinute equ 22h; 设置分钟显示寄存器的地址
DispSecond equ 23h; 设置秒显示寄存器的地址
Dnum equ 24h
AlarmHour equ 2eh; 设置闹时小时计数器的地址
AlarmMinute equ 2fh; 设置闹时分钟计数器的地址
AlarmSetKey bit P3.2; 闹钟设置键
MinuteKey bit P3.3; 定义分设置键
HourKey bit P3.4; 定义小时设置键
DataSetKey bit p3.5;
RelayOut equ P3.7; 定义输出引脚
;**********************************************************************
; 程序开始
;**********************************************************************
org 00h
ajmp Reset ;程序开始
org 0bh ;Timer0中断向量地址
ajmp TimeInt ;跳到中断处理程序
org 0020h
Reset: ;以下为初始化程序,为各个变量赋初值
mov sp,#70h;
setb RelayOut
mov OneSecondCounter,#125
;
mov Hour,#23
mov Minute,#59
mov Second,#30 ;设置上电时时钟显示的初值
;
mov Year,#07
mov Month,#12
mov Day,#27
;
mov AlarmHour,#00
mov AlarmMinute,#00 ;设置上电时闹时时间的初值
;
clr AlarmOnOff ;上点复位后闹时功能处于关闭状态
clr ClockMode ;正常走时模式
clr AlarmTimeOn
setb RelayOut ;清闹时输出
clr DataMode
;
mov 36h,#10
mov 37h,#11
;
;************************************
; Use Timer 0 Mode 1
; 400us interrupt
;************************************
mov tmod,#00000001b
mov th0,#0E3h
mov tl0,#5Dh
mov ie, #82h ;开全局中断
SETB EA
SETB ET0
setb tr0 ;开定时中断

;*****************************************
; 以下为主程序
;*****************************************
MainLoop:
jb AlarmSetKey,CheckMinuteKey ;闹时设置键按下了吗?没有则转去检测秒设置键
call Delay
jb AlarmSetKey,CheckMinuteKey ;按下的时间超过500ms吗?
setb ClockMode ;置为闹时设置模式
call AlarmSet
CheckMinuteKey:
jb MinuteKey,CheckHourKey ;分设置键按下了吗?没有则转去检测小时设置键
;如按下调用蜂鸣器发音程序
mov a,Minute
add a,#1; 如果按下则将分钟加一 十进制调整
mov Minute,a;
cjne a,#3ch,NotOver1; 到60分钟了吗?
mov Minute,#0; 到60分钟则将分钟清0
NotOver1: ;以下等待按键释放及防抖动
jnb MinuteKey,$
CheckHourKey:
jb HourKey,CheckDataKey;
; 如按下调用蜂鸣器发音程序
mov a,Hour
add a,#1; 如果按下则将小时加1
mov Hour,a
cjne a,#18h,NotOver2
mov Hour,#0; 到24小时则将小时清0
NotOver2: ;以下等待按键释放及防抖动
jnb HourKey,$
CheckDataKey:
jb DataSetKey,CheckAlarm
call Delay
jb DataSetKey,CheckAlarm
setb DataMode
call Dataset
CheckAlarm:
jnb AlarmTimeOn,ToReturn
call AlarmProcess
ToReturn:
ajmp MainLoop
;**********************************************
; 定时器Timer0中断服务程序(此程序每8ms执行一次)
;**********************************************
TimeInt:
mov th0,#0E3h; 重新加载定时参数
mov tl0,#5Dh;
push acc
push psw; 保护累加器及程序状态字的内容
setb rs0; 选择工作寄存器组1,
clr rs1; 这样可保护原工作寄存器组(0组)的内容
djnz OneSecondCounter,NotoneSecond; 中断了125次了吗?即够1秒了吗?
mov OneSecondCounter,#125; 如够1秒则重新设置"OneSecondCounter"计数器
call Clock ; 调用将时钟内容加1秒的子程序
call Daynum
call ConvertoBuffer ; 调用将时钟内容转换到显示缓冲区子程序
NotoneSecond:
call ScanDisplay ; 调用扫描显示子程序
pop psw
pop acc ; 恢复累加器及程序状态字的内容
reti ; 中断返回
;*********************************************
; 扫描显示子程序
;*********************************************
ScanDisplay:
MOV R1,#DisplayBuffer ;指向显示数据首址
MOV R5,#0FEH ;扫描控制字初值
PLAY:
MOV A,R5 ;扫描字放入A
MOV P2,A ;从P2口输出
MOV A,@R1 ;取显示数据到A
MOV DPTR,#TAB ;取段码表地址
MOVC A,@A+DPTR ;查显示数据对应段码
MOV P1,A ;段码放入P1口

LCALL Delay

INC R1 ;指向下一地址
MOV A,R5 ;扫描控制字放入A
JNB ACC.7,ENDOUT ;扫到第六位时结束
RL A ;A中数据循环左移
MOV R5,A ;放回R5内
AJMP PLAY ;跳回PLAY循环
ENDOUT: MOV P2,#0FFH ;一次显示结束,P2口复位
MOV P1,#00H ;P1口复位
RET ;子程序返回
TAB: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,40H,39h
;共阴段码表 "0""1""2" "3""4""5""6""7" "8""9""-""c"
;****************************************************
; 时钟内容加1秒的子程序
;****************************************************
Clock:
mov a,Second; 将原秒值送入a
add a,#1; 加1秒

mov Second,a
cjne a,#3cH,NotOverFlow; 够60秒了吗?
mov Second,#0; 够了则将秒值清0
;
mov a,Minute
add a,#1
mov Minute,a; 分钟加1
cjne a,#3cH,NotOverFlow; 够60分了吗?
mov Minute,#0; 够了则将分值清0
;
mov a,Hour
add a,#1
mov Hour,a; 小时加1
cjne a,#18H,NotOverFlow; 够24小时吗?
mov Hour,#0; 够了则将小时值清0
;
mov a,Day
add a,#1
mov Day,a
cjne a,Dnum,NotAlarm
mov Day,#1
;
mov a,Month
add a,#1
mov Month,a
cjne a,#13,NotAlarm
mov Month,#1
;
mov a,Year
add a,#1
mov Year,a
cjne a,#11,NotAlarm
mov Year,#0
NotOverFlow:
jnb AlarmOnOff,NotAlarm; 闹钟开启了吗?如没有开启则无需理会是否到闹时时间
mov a,Second
jnz NotAlarm; 秒为零吗?
mov a,Minute
cjne a,AlarmMinute,NotAlarm; 时间分钟值和闹时设置分钟值相等吗?
mov a,Hour
cjne a,AlarmHour,NotAlarm; 时间小时值和闹时设置小时值相等吗?
setb AlarmTimeOn; 到了闹时时间则将“闹时时间到”标志设为1
NotAlarm:
ret

;**************************************************************************
; 将时钟内容或闹时设置值转换到显示缓冲区子程序
;**************************************************************************
ConvertoBuffer:
mov r1,#DisplayBuffer
jnb dataMode,TimeDisp
mov a,Day
mov DispSecond,a
mov a,Month
mov DispMinute,a
mov a,Year
mov DispHour,a
ajmp Convert
TimeDisp: jb ClockMode,DispAlarmSet; 判断时钟模式,以决定是显示实时时间还是闹时时间
mov a,Second;
mov DispSecond,a;
mov a,Minute;
mov Dispminute,a;
mov a,Hour;
mov DispHour,a; 显示实时时间
;
ajmp Convert
DispAlarmSet:
jb AlarmOnOff,AlarmOn
mov DispSecond,#00h
ajmp Next
AlarmOn:
mov DispSecond,#11; 显示闹时时间及显示闹钟状态:显示“00”表示关闭闹钟,
Next: ;“11”表示开启闹钟
mov a,AlarmMinute;
mov Dispminute,a;
mov a,AlarmHour;
mov DispHour,a;
;
Convert:
mov a,DispSecond; 取秒值
mov b,#10
div ab
mov @r1,b;
inc r1; 缓冲寄存器的地址加1
mov @r1,a; 将秒值的十位值存入缓冲区
;
inc r1
mov a,DispMinute
mov b,#10
div ab
mov @r1,b;
inc r1; 缓冲寄存器的地址加1
mov @r1,a; 将秒值的十位值存入缓冲区
;
inc r1
mov a,DispHour
mov b,#10
div ab
mov @r1,b;
inc r1; 缓冲寄存器的地址加1
mov @r1,a; 将秒值的十位值存入缓冲区
ret;
;*******************************************************
;天数判断,平年,闰年
;*******************************************************
Daynum: mov a,Month
mov dptr,#TABL
movc a,@a+dptr
mov Dnum,a
mov a,Year
mov b,#4
div ab
mov a,b
cjne a,#0,BB
mov a,Month
cjne a,#2,BB
inc Dnum
BB: RET
TABL: DB 31,32,29,32,31,32,31,32,32,31,32,31,32

;**************************************************
; 闹时设置子程序
;**************************************************
AlarmSet:
jnb AlarmSetKey,$
call Delay; 等待“AlarmSetKey”键释放
CheckArmMinuteKey: ;
jb MinuteKey,CheckArmHourKey; 分设置键按下了吗?没有则转去检测小时设置键
setb AlarmOnOff
mov 37h,#0
mov a,AlarmMinute
add a,#1; 如果按下则将分钟加1 十进制调整
mov AlarmMinute,a;
cjne a,#3ch,ArmNotOver1; 到60分钟了吗?
mov AlarmMinute,#0; 到60分钟则将分钟清0
ArmNotOver1: ;以下等待按键释放及防抖动
jnb MinuteKey,$
CheckArmHourKey:
jb HourKey,AlarmSetEnd; 小时设置键按下了吗?没有则返回反复检测
setb AlarmOnOff
mov a,AlarmHour
add a,#1; 如果按下则将小时加1
mov AlarmHour,a
cjne a,#18h,ArmNotOver2
mov AlarmHour,#0; 到24小时则将小时清0
ArmNotOver2: ;以下等待按键释放及防抖动
jnb HourKey,$
AlarmSetEnd:
jb AlarmSetKey,AlarmSet; 设置完毕了吗?
jnb AlarmSetKey,$;
clr ClockMode; 从设置模式转为走时模式
ret
;*********************************************
; 日期调整
;*********************************************
DataSet:
jnb DataSetKey,$
call Delay
CheckDataYearKey:
jb HourKey,CheckDataMonthKey
mov a,Year
add a,#1
mov Year,a
cjne a,#11,DataNotOver1
mov Year,#0
DataNotOver1:
jnb HourKey,$
CheckDataMonthKey:
jb MinuteKey,CheckDataDayKey
mov a,Month
add a,#1
mov Month,a
cjne a,#13,DataNotOver2
mov Month,#1
DataNotOver2:
jnb MinuteKey,$
CheckDataDayKey:
jb AlarmSetKey,DataSetEnd
mov a,day
add a,#1
mov Day ,a
cjne a,Dnum,DataNotOver3
mov Day,#1
DataNotOver3:
jnb AlarmSetKey,$
DataSetEnd:
jb DataSetKey,DataSet
jnb DataSetKey,$
clr DataMode
ret

;**************************************************
; 闹时服务子程序
;**************************************************
AlarmProcess:
clr RelayOut;
jb AlarmSetKey,AlarmReturn; 停止闹时键(即闹时设置键)按下了吗?
clr AlarmOnOff
jnb AlarmSetKey,$;
setb RelayOut; 如停止闹时键按下则停止闹时
clr AlarmTimeOn;
mov 37h,#11
mov AlarmMinute,#00
mov AlarmHour,#00
AlarmReturn:
ret
;**************************************************
; 延时子程序
;**************************************************
Delay:
mov r6,#2
Del:
mov r7,#124
djnz r7,$
djnz r6,Del
ret
DL1s: mov r3,#4
dd: call Delay
djnz r3,dd
ret
end
参考技术B 安装完成以后请将本光盘根目录下的环境设置批处理文件
Var.bat
拷贝到
Masm32\bin
目录下,并根据
MASM32
的安装位置编辑修改
Var.bat
文件中的相关目录名称。
2.
代码维护工具
每个例子都包括了描述编译、链接方法的
makefile
文件,
使用
nmake
工具可以自动根据此文件进行编译链接,nmake

具可以从
Visual
C++

bin
目录中找到,也可以从作者的网
站中下载。
3.
编译环境和编译方法
建议使用命令行方式进行编译,以编译
Chapter02\Test
目录中
Test.asm
为例,步骤是:
I.
打开一个“命令提示符”窗口。
II.
进入环境设置批处理文件
Var.bat
所在目录并执行
它,以后就可以使用这个“命令提示符”窗口编译
文件了。
x:
<切换到MASM32安装的驱动器>
cd
\masm32\bin
<进入MASM32的执行目录>
var
<执行Var.bat设置环境变量>
III.
进入源代码目录:
cd
\chapter02\test
IV.
使用
nmake
工具进行编译链接:
nmake
V.
执行编译好的可执行文件。

汇编:史上最全注释,王爽汇编语言,课程设计2源码

课程设计2

题目见 汇编语言 第四版 作者王爽 p312

这篇文章是《汇编语言 第四版》的完结篇。

概述

1、vm虚拟机中安装win98se,以下称为win98

2、成功安装系统后,虚拟机添加一个新软盘(需要创建软盘映像.img文件)。

3、启动win98系统,将下文源码编译,然后执行程序(需要masm汇编器)。

4、关闭win98系统,在虚拟机中,设置软盘为启动盘。

5、启动虚拟机,我们自己写的程序就能执行了。

如果用两个硬盘,即添加新硬盘代替软盘,然后设新硬盘为启动盘后,启动失败(暂不管此问题)!

程序结构

软盘:第1扇区存放我们的引导程序,第2-3扇区存放主程序,实现4个功能。

硬盘1:是win98系统盘,第1扇区有mbr引导程序。

开机后,软盘为启动盘,加载我们自己的引导程序,执行引导程序,然后加载2-3扇区的主程序。

内存结构

汇编源码

源码:

; 课程设计2
; 实现了4个功能的程序以下称为主程序

assume cs:code
code segment
start:

    ; 安装我们自己的引导程序到软盘a,占1个扇区
    call inst_my_boot

    ; 安装主程序到软盘a,占2个扇区
    call inst_my_main

    mov ax,4c00h
    int 21h

;---------------- 安装程序 ----------------
inst_my_boot:
    mov bx,cs
    mov es,bx
    mov bx,offset my_boot ; es:bx指向my_boot

    ; 写入内容到 软盘A,0面,0道,1扇区
    mov dl,0 ; 软盘A
	mov dh,0 ; 0面
    mov ch,0 ; 0道
	mov cl,1 ; 第1扇区
	mov al,1 ; 写1个扇区
	mov ah,3 ; 写
	int 13h

    ret

inst_my_main:
    mov bx,cs
    mov es,bx
    mov bx,offset my_main ; es:bx指向my_main

    ; 写入内容到 软盘A,0面,0道,2扇区
    mov dl,0 ; 软盘A
	mov dh,0 ; 0面
    mov ch,0 ; 0道
	mov cl,2 ; 第2扇区
	mov al,2 ; 写2个扇区
	mov ah,3 ; 写
	int 13h

    ret

;---------------- 引导程序 ----------------
my_boot:
    ; 设栈
    cli
    mov ax,0
    mov ss,ax
    mov sp,7c00h
    sti

    ; 装载新int9h中断例程
    call load_newint9

    ; 将主程序拷贝到7e00h
    mov bx,0
    mov es,bx
    mov bx,7e00h

    ; 读取 软盘A,0面,0道,2扇区 开始的2个扇区 到0:7e00h
    mov dl,0 ; 软盘A
	mov dh,0 ; 0面
    mov ch,0 ; 0道
	mov cl,2 ; 第2扇区
	mov al,2 ; 复制2个扇区
	mov ah,2 ; 读取
	int 13h

    mov bx,0
    push bx
    mov bx,7e00h
    push bx
    retf ; 从栈中取2字 设CS:IP=0:7e00h 并从此处开始执行

; 装载新int9h中断例程
load_newint9:
    push ds
    push si
    push es
    push di
    push cx

    push cs ; 引导程序执行时,cs:ip=0:7c00h
    pop ds

    mov cx,0
    mov es,cx

    mov si,newint9-my_boot+7c00h ; ds:si指向新int9例程
    mov di,204h ; es:di指向新int9例程装载位置
    mov cx,newint9end-newint9
    cld
    rep movsb

    ; 保存旧的int9h中断向量
    push es:[9*4]
    pop es:[200h]
    push es:[9*4+2]
    pop es:[202h]

    pop cx
    pop di
    pop es
    pop si
    pop ds
    ret

newint9:
    push ax
    push bx
    push cx
    push es

    in al,60h

    pushf
    call dword ptr cs:[200h] ; 此newint9中断放在0:204h处,此中断执行时cs=0

    cmp al,3bh ; f1扫描码
    je chgcolor
    cmp al,01h ; esc键扫描码
    je int9ret
    jmp newint9ret

backmain:
    pop es
    pop cx
    pop bx
    pop ax

    add sp,4 ; 跳过cs:ip
    popf

    mov bx,0
    push bx
    mov bx,7e00h
    push bx
    retf

int9ret:
    ; 恢复原int9中断向量,原中断向量保存在0:200h, 0:202h
    mov ax,0
    mov es,ax
    cli
    push es:[200h]
    pop es:[9*4]
    push es:[202h]
    pop es:[9*4+2]
    sti
    jmp backmain

chgcolor:
    mov ax,0b800h
    mov es,ax
    mov bx,1
    mov cx,17
chgcolor_s1:
    inc byte ptr es:[bx]
    add bx,2
    loop chgcolor_s1

newint9ret:
    pop es
    pop cx
    pop bx
    pop ax
    iret

newint9end:
    nop

; 因为引导程序占512字节,这里填充512字节0,作为扇区结束,
; 防止引导程序不够512字节,而复制下面的代码到1扇区内
db 512 dup (0)

;--------------------- 主程序 ---------------------
my_main:
    jmp main_start
    menu1     db '1) reset pc',0
    menu2     db '2) start system',0
    menu3     db '3) clock',0
    menu4     db '4) set clock',0
    menu_addr dw menu1-my_main+7e00h, menu2-my_main+7e00h, menu3-my_main+7e00h, menu4-my_main+7e00h
    timestr   db 'yy/mm/dd hh:mm:ss',0
    timeaddr  db 9,8,7,4,2,0
    strbuffer db 100 dup (0) ; 输入字符串缓冲区

main_start:
    ; 初始化数据段寄存器
    mov ax,0
    mov ds,ax

    call clr_src
    call show_menu
    call choose_item

choose_item: ; 选择菜单项
    mov si,strbuffer-my_main+7e00h
    call getstr ; 输入字符串,回车确认,ds:si指向字符串缓冲区

    cmp byte ptr [si],'1'
    je item1
    cmp byte ptr [si],'2'
    je item2
    cmp byte ptr [si],'3'
    je item3
    cmp byte ptr [si],'4'
    je item4
    jmp main_start ; 其他输入时,重新显示菜单

item1:
    mov bx,0ffffh
    push bx
    mov bx,0
    push bx
    retf ; 将从ffff:0处开始执行,会重启系统

item2: ; 引导现有系统
    ; 将原mbr拷贝到7c00h
    mov bx,0
    mov es,bx
    mov bx,7c00h

    ; 读取 硬盘c,0面,0道,1扇区 到0:7c00h
    mov dl,80h ; 盘c
	mov dh,0 ; 0面
    mov ch,0 ; 0道
	mov cl,1 ; 第1扇区
	mov al,1 ; 复制1个扇区
	mov ah,2 ; 读取
	int 13h

    mov bx,0
    push bx
    mov bx,7c00h
    push bx
    retf

item3:
    call setnewint9
    call show_dt
    jmp main_start

item4:
    call clr_src
    mov si,strbuffer-my_main+7e00h
    call getstr
    call set_dt ; ds:si指向字符串缓冲区
    jmp main_start

show_menu: ; 显示主程序菜单
    push bx
    push es
    push si
    push cx
    push di

    mov bx,0b800h
    mov es,bx
    mov bx,160*10+32*2 ; 中间位置显示菜单,第10行第32列
    mov di,menu_addr-my_main+7e00h ; 直接定址表
    mov cx,4
show_menu_s1:
    mov si,[di] ; ds:di定位直接定址表
    call show_str
    add di,2
    add bx,160 ; 跳到下一行
    loop show_menu_s1

    pop di
    pop cx
    pop si
    pop es
    pop bx
    ret

clr_src: ; 清屏
    push bx
    push cx
    push es
    mov bx,0b800h
    mov es,bx
    mov bx,0 ; 显存的偶地址单元为字符
    mov cx,2000
clr_src_s1:
    mov byte ptr es:[bx],' ' ; 空格填充
    add bx,2
    loop clr_src_s1
    pop es
    pop cx
    pop bx
    ret

; 显示字符串,字符串以0结束
; ds:si指向字符串开头
; es:bx指向显存开始,对应屏幕上的位置
show_str:
    push si
    push cx
    push es
    push bx
show_str_s1:
    mov cl,[si]
    cmp cl,0
    je show_str_ret
    mov es:[bx],cl
    add bx,2
    inc si
    jmp show_str_s1
show_str_ret:
    pop bx
    pop es
    pop cx
    pop si
    ret

; 接受字符串输入
; ds:si指向字符栈空间
getstr:
    push ax
    push dx
    push es
    push di

    mov ax,0b800h
    mov es,ax
    mov di,160*14+32*2 ; es:di为光标初始位置、字符串输入位置
    ;mov di,0
    call setcur

    mov dx,0 ; 字符栈栈指针
getstrs:
    mov ah,0
    int 16h ; 获取键盘缓冲区内容
    cmp al,20h
    jb nochar
    cmp al,7eh
    ja nochar ; 只能输入可见字符
    mov ah,0
    call charstack ; 字符入栈
    mov ah,2
    call charstack ; 显示栈中字符
    jmp getstrs

nochar:
    cmp ah,0eh ; 退格键扫描码
    je backspace
    cmp ah,1ch ; 回车键扫描码
    je enter
    jmp getstrs

backspace:
    mov ah,1
    call charstack ; 字符出栈
    mov ah,2
    call charstack
    jmp getstrs

enter:
    mov al,0 ; 把0入栈,作为字符串结束
    mov ah,0
    call charstack
    mov ah,2
    call charstack

    pop di
    pop es
    pop dx
    pop ax
    ret

; 字符入栈、出栈、显示功能
; ah=功能号,0入栈,1出栈,2显示
; ds:si指向字符栈空间
; dx=字符栈栈指针
; 0号功能,al=入栈字符
; 1号功能,al=返回的字符
; 2号功能,es:di指向屏幕位置
charstack: jmp short charstart
table      dw charpush-my_main+7e00h,charpop-my_main+7e00h,charshow-my_main+7e00h

charstart:
    push bx
    push di
    push es
    ;dx为charstack非局部变量,不要入栈
    ;push dx
    
    cmp ah,2 ; 功能号判断
    ja sret
    mov bl,ah
    mov bh,0
    add bx,bx ; 根据功能号取得对应的偏移地址
    jmp word ptr table-my_main+7e00h[bx]

charpush:
    mov bx,dx
    mov [si][bx],al
    inc dx
    jmp sret

charpop:
    cmp dx,0
    je sret
    dec dx
    mov bx,dx
    mov al,[si][bx]
    jmp sret

charshow:
    ; es:di指向屏幕位置,由调用者传递
    mov bx,0
charshows:
    cmp bx,dx
    jne noempey
    mov byte ptr es:[di],' '
    call setcur
    jmp sret

noempey:
    mov al,[si][bx]
    mov es:[di],al
    mov byte ptr es:[di+2],' ' ; 清空后一个字符
    inc bx
    add di,2
    jmp charshows

sret:
    pop es
    pop di
    pop bx
    ret

setcur: ; 设置光标到es:di位置
    push ax
    push dx

    mov ax,di
    mov dh,160
    div dh
    mov dh,al ; 行号
    mov al,ah
    mov ah,0
    mov dl,2
    div dl
    mov dl,al ; 列号
    mov ah,2 ; 设置光标位置
    mov bh,0 ; 第0页
    int 10h

    pop dx
    pop ax
    ret

; 显示时间
show_dt:
    call get_dt
    call delay
    jmp show_dt
    ret

; 设置新的int9h中断向量
setnewint9:
    push es
    push bx
    mov bx,0
    mov es,bx
    cli
    mov word ptr es:[9*4],204h
    mov word ptr es:[9*4+2],0
    sti
    pop bx
    pop es
    ret

; 获取CMOS中的系统时间
; ds:si从cmos对应地址取日期时间,ds:di转换后的日期时间字符串
get_dt:
    push si
    push di
    push cx
    push ax
    push es
    push bx

    mov si,timeaddr-my_main+7e00h
    mov di,timestr-my_main+7e00h
    mov cx,6
get_dt_s1:
    mov bx,cx
    mov al,[si]
    out 70h,al ; 70h为地址端口
    in al,71h ; 71h为数据端口
    mov ah,al
    mov cl,4
    shr ah,cl ; 右移4位,ah为十进制的十位数
    and al,00001111b ; al为十进制的个位数
    add ah,30h
    add al,30h ; 数值转字符形式
    xchg ah,al
    mov [di],ax
    add di,3
    inc si
    mov cx,bx
    loop get_dt_s1

    mov bx,0b800h
    mov es,bx
    mov bx,0
    mov si,timestr-my_main+7e00h
    call show_str

    pop bx
    pop es
    pop ax
    pop cx
    pop di
    pop si
    ret

; 延时
delay:
    push ax
    push dx
    mov dx,6000h ; 循环6000000h次
    mov ax,0
delays1:
    sub ax,1
    sbb dx,0
    cmp ax,0
    jne delays1
    cmp dx,0
    jne delays1
    pop dx
    pop ax
    ret

; 设置系统时间
set_dt:
    push si
    push di
    push cx
    push ax
    push bx

    mov di,timeaddr-my_main+7e00h
    mov cx,6
set_dt_s1:
    mov ax,[si] ; al为十位数,ah为个位数
    sub ah,30h
    sub al,30h
    and ah,00001111b ; 取个位数
    mov bx,cx
    mov cl,4
    shl al,cl
    or al,ah
    mov cx,bx
    mov ah,al
    mov al,[di]
    out 70h,al
    mov al,ah
    out 71h,al
    inc di
    add si,3
    loop set_dt_s1

    pop bx
    pop ax
    pop cx
    pop di
    pop si
    ret

db 1024 dup (0) ; 填充1k字节,原因同上

code ends
end start

执行结果:

以上是关于求一个汇编语言课程设计的MASM32时钟的程序的主要内容,如果未能解决你的问题,请参考以下文章

C语言课程设计 题目为设计一个多功能计算软件实现功能 求完美的代码

《面向对象程序设计》课程设计模拟时钟程序

课程设计数字秒表设计 求高手解答。

汇编:史上最全注释,王爽汇编语言,课程设计2源码

汇编:史上最全注释,王爽汇编语言,课程设计2源码

借同学百度知道问的,求一个C语言课程设计,关于图书馆借阅的程序。