急需一份基于8086最小模式的汇编语言的数字钟,时间显示通过时间中断子程序实现,不用8086时钟频

急需一份基于8086最小模式的汇编语言的数字钟,时间显示通过时间中断子程序实现,不用8086时钟频,第1张

可以放一个中断程序到1C中断,这样会每秒18.2次被调用

里面放一个简单的敏册手显示程序,调用中断10,定位当时的鼠标位置,保存,然后可以到屏幕右上角显示当时的时间,姿做时钟数据直接调用21中断取得电脑主机时间数据。

程序主体就是置换原有1C中断地址为新中断地址,记得保存原有中断地址,在新中断运行的时候调用一次原有中断。然后可以考虑驻留内存,或者是遇桥嫌到中断按键后退出程序以便于调试

/*电子时钟源代码*/

#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=431i<=470i+=39)

for(j=317j<=324j+=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=0i<=11i++,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=0i<=59m++,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=2k!=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=0i<=23i++)

{if(h==i) return i}

}

int digitmin(double m)/*将double型的分钟数转换成int型*/

{int i

for(i=0i<=59i++)

{if(m==i) return i}

}

int digitsec(double s) /*将double型的秒钟数转换成int型*/

{int i

for(i=0i<=59i++)

{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

}

}

8086/8088微处理器

8086是Inter系列的16位微处理器,芯片上有2.9万个晶体管,采用 HMOS工

艺制造,用单一的+5V电源,时钟频率为5MHz~10MHz。

8086有16根数据线和20根地址线,它既能处理16位数据,也能处理8位数据。可

寻址的内存空间为1MB.

Inter公司在推出8086的同时,还推出了一种准16位微处理器8088,8088的内部寄存器,运算部件及内部数据总线都是按16位设计的,单外部数据总线只有8条。推出8086的主要目的是为了与当时已有的一套Inter外部设备接口芯片直接兼容使用。8086与8088在寄存器结构,编程结构,存储器组织及I/O端口组织方面是完全一样的或稍有差别,在本节中,对其差别之做出说明。

1.3.1 8086/8088的寄存器结构

图1-3示出了8086/8088的寄存器结构

1. 数据寄存器

数据寄存器为图中最上边所示的4个寄存器AX,BX,CX,DX。这些寄存器用以暂时保存计算过程中所得到的 *** 作数及结果。他能处理16位数,也能处理8位数,当处理8位数时,这4个16位寄存器作为8个8为寄存器AH,AL,BH,BL,CH,CL,DH,DL来使用。

这4个数据寄存器除了作为通用寄存器以外,还有各自的专门用途:

AX(accumulator)做累加器用,是算术运算的主要寄存器。AX还用在字乘和字除法中,此外,所有的I/O指令都是以AX为中心与外部设备进行信息传送;

BX(base)在基裤计算寄存器地址时,常用做基值寄存器;

CX(count)再串 *** 作指令及循环中用做计数器;

DX(data)在字乘法,字除法运算中,将DX,AX组合成一个双字长数,DX用来存放高16位数。另外,在间接的I/O指令中,DX用来指定I/O端口地址

2. 指针寄存器及变址寄存器

指针寄存器包括堆栈寄存器SP(stack pointer)和基数指针寄存器BP(base pointer),变

值寄存器包括源变址寄存器SI(source index)和目的变值寄存器搏伍简DI(destination index)。这

4个寄存器都是16位寄存器,这些寄存器在运算过程中也可以用来存放 *** 作数(只能

以字为单位),但经常的用途是在段内寻址时提供偏移地址,SP,BP一般与段寄存器SS

联用,以确定堆栈寄存器橘凳中某一单元的地址,SP用以指示栈顶的偏移地址,而BP可

作为堆栈区中的一个基地址,用以确定在堆栈中的 *** 作数地址。SI,DI一般与段寄存器

DS联用,以确定数据段中某一存储单元的地址,SI,DI具有自动增量和自动减量的功能,

这一点使在串 *** 作指令中用做变址非常方便,SI作为隐含的源变址DS联用,DI作为

隐含的目的变址和ES连用,从而达到在数据段和附加段中寻址的目的

3. 段寄存器

一共有4个段地址寄存器,他们是:

CS(code segment register)16位代码段寄存器

DS(data segment register)16位数据段寄存器

SS(stack segment register)16位堆栈段寄存器

ES(extra segment register )16为附加段寄存器

下面将要讲到,在IBM PC机中采用存储器地址分段的办法,使8086/8088能寻址1MB的内存。而段寄存器就是用来存放段地址的,CS段寄存器用来存放当前正在运行的程序;DS段寄存器用来存放当前运行的数据,若程序中使用了段 *** 作指令,源 *** 作数也

存放在数据段中,SS段寄存器规定了堆栈所处的区域;ES段寄存器用来存放辅助数据

,因ES是一个附加的数据段,在执行串 *** 作指令时,目的 *** 作数也一般存放在ES段中。

4. 控制寄存器

IP(instruction pointer)是指令指针寄存器,是一个16位寄存器,用来存放代码段中的偏移地址。他与CS连用才能确定下一条指令的地址,根据这一地址,控制器从指定的存储器中,取出下一条要执行的指令,并修改IP,以便指向下一条要执行的指令。可见IP

寄存器是用来控制指令系列的执行流程的。

PSW(processor status word)是状态标志寄存器,也是一个16位寄存器,我们将在本节后面加以介绍。

上面介绍的这些寄存器在计算机中有非常重要的作用,在运算过程中,这些寄存器起着存储器的作用,但存取速度比存储器快得多。

1.3.2 8086/8088的编程结构

所谓编程结构是指从使用者看到的结构,这是一种按功能划分的结构,这种结构与CPU内部的实际物理结构当然是有区别的。

8086的编程结构见图1-4。他分两部分。即总线接口部分BIU(bus interface unit)和执行部件EU(execution unit).

总线接口部分负责与存储器,外设端口传送数据。具体讲,总线接口部分从内存中取出指令送到指令队列时,CPU执行指令时,所需的 *** 作数也由总线接口部分从指定的内存单元或外设端口取来,传送给执行部分去执行,反过来,执行部分的 *** 作结果也通过

总线接口传送到指定的内存单元或外设端口中去。

总线接口部件由下面4部分组成:4个段寄存器,指令指针寄存器IP,20位的地址加法器及6个字节的指令队列。

地址加法器的作用是产生20个地址。上面提到,8086/8088内部所有的寄存器都是16位的,8086/8088可用20位地址去寻址1MB的内存空间,这就需要地址加法器根据16

寄存器提供的信息,计算出20位物理地址,具体算法将在本节后面讲述存储器组织时加以介绍。

对总线接口部分需说明的一点是,8086的指令队列为6个字节,而8088的指令队列为4个字节。不管是8086,还是8088,都会在执行指令的同时,从内存中取出下面一条或几条指令,取来的指令依次放在指令队列中,按顺序放,并按顺序到EU中去执行。执行部分EU的功能负责指令的执行。

执行部件包括:4个数据寄存器,2个指针寄存器,2个变值寄存器,1个状态标志寄存器和一个算术逻辑单元。

从编程结构可看出,由于总线接口部分和执行部分是分开的,每当EU执行一条指令时,造成指令队列空出2个或空出一个指令字节时 ,BIU马上从内存中取出下面一条或几条指令,以添满他的指令队列。这样,一般情况下,CPU在执行完一条指令后,便可马上执行下一条指令,不像以往8位CPU那样,执行完一条指令后,需等待下一条指令

1.3.3 8086/8088的存储器组织

1. 存储单元的地址和内容

2. 在计算机中用以存储信息的基本单位是一个二进制位,每8个组成一个字节

参考资料:网上论坛


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12312688.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存