C时钟与键盘中断混合
程序代码:#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <mem.h>
#include <dos.h>
/* 以下是 PC 机的 DMA 控制器端口的常量定义 */
#define DMA8_CMD_PORT 0x08 /* 8位DMA写命令寄存器端口 */
#define DMA8_STATU_PORT 0x08 /* 8位DMA独状态寄存器端口 */
#define DMA8_REQUEST_PORT 0x09 /* 8位DMA写请求寄存器端口 */
#define DMA8_MASK_PORT 0x0A /* 8位DMA屏蔽寄存器端口(只写)*/
#define DMA8_MODE_PORT 0x0B /* 8位DMA写模式寄存器端口 */
#define DMA8_CLRPTR_PORT 0x0C /* 8位DMA清先后状态寄存器端口 */
#define DMA8_RESET_PORT 0x0D /* 8位DMA写复位命令端口 */
/* 以下是 PC 机 DMA 的 7 个通道的地址寄存器、计数寄存器和页面寄存器的端口常量定义 */
/* 其中通道 0-3 用于 8 位的 DMA,通道 4-7 用于 16 位 DMA */
/* PC机中,规定通道 2 用于进行软盘的 DMA 传输,其余通道可供用户使用 */
#define DMA0_ADDR_PORT 0x00 /* 通道0的地址寄存器 */
#define DMA0_COUNT_PORT 0x01 /* 通道0的计数寄存器 */
#define DMA0_PAGE_PORT 0x87 /* 通道0的页面寄存器 */
#define DMA1_ADDR_PORT 0x02 /* 通道1的地址寄存器 */
#define DMA1_COUNT_PORT 0x03 /* 通道1的计数寄存器 */
#define DMA1_PAGE_PORT 0x83 /* 通道1的页面寄存器 */
#define DMA3_ADDR_PORT 0x06 /* 通道3的地址寄存器 */
#define DMA3_COUNT_PORT 0x07 /* 通道3的计数寄存器 */
#define DMA3_PAGE_PORT 0x82 /* 通道3的页面寄存器 */
#define DMA5_ADDR_PORT 0xC4 /* 通道5的地址寄存器 */
#define DMA5_COUNT_PORT 0xC6 /* 通道5的计数寄存器 */
#define DMA5_PAGE_PORT 0x8B /* 通道5的页面寄存器 */
#define DMA6_ADDR_PORT 0xC8 /* 通道6的地址寄存器 */
#define DMA6_COUNT_PORT 0xCA /* 通道6的计数寄存器 */
#define DMA6_PAGE_PORT 0x89 /* 通道6的页面寄存器 */
#define DMA7_ADDR_PORT 0xCC /* 通道7的地址寄存器 */
#define DMA7_COUNT_PORT 0xCE /* 通道7的计数寄存器 */
#define DMA7_PAGE_PORT 0x8A /* 通道7的页面寄存器 */
/* DSP 定义 */
#define DSP_RESET_DELAY 10
#define DSP_READY 0xAA
#define DSP_GET_VERSION 0xE1
#define DSP_SET_BLK_SIZE 0x48
#define DSP_START_DMA8 0x1C
#define DSP_PAUSE_DMA8 0xD0
#define DSP_SET_SAM_RATE 0x40
#define PIC_PORT_21H 0x21
#define PIC_PORT_20H 0x20
#define PIC_EOI 0x20
/*数据块大小必须<51199*/
#define DMA_BUFFER_SIZE 51198
#define KEY_A 0x1E
#define KEY_B 0x30
#define TRUE 1
#define FALSE 0
typedef struct
{
unsigned short wavetype; /* WAVE的类别 */
unsigned short channel; /* 通道数 */
unsigned short samplerate; /* 采样频率 */
unsigned short samplebits; /* 采样位数 */
long int datalen; /* 数据长度 */
long int leftdatalen; /* 剩余数据长度 */
int loops; /* 播放次数 */
FILE *fp;
} WAVE, *PWAVE;
typedef struct
{
char dspenvstr[128];
unsigned short dspversion;
unsigned short dspbaseioport;
unsigned short resetport;
unsigned short writedataport;
unsigned short writestatusport;
unsigned short readdataport;
unsigned short readstatusport;
unsigned short mixerbaseioport;
unsigned short mpu401baseioport;
unsigned char dspirqnum;
unsigned char dspdma8;
unsigned char dspdma16;
} DSP, *PDSP;
/* WAVE 文件结构定义 */
typedef struct
{
char RIFF[4]; /* RIFF */
unsigned long int filelen; /* 文件长度 */
char WAVEfmt[8]; /* WAVEfmt */
unsigned long int reserved; /* 保留 */
unsigned short wavetype; /* WAVE的类别 */
unsigned short channel; /* 通道数目 */
unsigned short sampling; /* 采样频率 */
unsigned long int transpeed; /* 数据传输速率 */
unsigned short blkalign; /* 调整数据块 */
unsigned short sampbits; /* 采样位数 */
char data[4]; /* data */
unsigned long int datalen; /* 语音数据长度 */
unsigned char *pdata; /* 数据区 */
} WAVEFILE, *PWAVEFILE;
int initsound(void);
void closesound(void);
int loadwave(PWAVE pwave, char *file);
void destroywave(PWAVE pwave);
void playwave(PWAVE pwave);
void stopwave(PWAVE pwave);
void pausewave(PWAVE pwave);
int initpic(int irqnum);
void closepic(int irqnum);
void picintdone(void);
int initdsp(PDSP pdsp);
void writedsp(PDSP pdsp, unsigned char byte);
void setdspblocksize(PDSP pdsp, unsigned short blksize);
void dspstartdma8(PDSP pdsp);
void dsppausedma8(PDSP pdsp);
void dspsetsamplerate(PDSP pdsp, unsigned short rate);
int initdma8(int channel, unsigned char far *addr, int size);
void closedma8(int channel);
void interrupt (*old_dsp_int_handle)(void) = NULL;
void interrupt new_zhong_duan(void);
void an_zhuang_zhong_duan(void);
void del_zhong_duan(void);
DSP cursbdsp = {0};
PWAVE pcurwave = NULL;
int dma_buf_DSP_flag = NULL;
unsigned char far *sound_dma_buf = NULL;
int parse_sb_envstr(char *envstr, char id);
int DSP_flag=1;
char key_state[128],key_pressed[128];
void interrupt (*oldInt9Handler)(void)=0; /*OldInt9Handler将保存原有键盘中断值*/
void far interrupt newInt9();
//可参考JIG大拿的DOS播放WAV音乐的解说,很详细
int initdma8(int channel, unsigned char far *addr, int size)
{
unsigned long phyaddr = FP_SEG(addr) * 0x10L + FP_OFF(addr);
unsigned char page = (unsigned char)(phyaddr >> 16);
unsigned short offset = (unsigned short)(phyaddr >> 0);
outportb(DMA8_MASK_PORT, channel | (1 << 2)); /* 屏蔽该通道 */
outportb(DMA8_MODE_PORT, channel | (1 << 4) | (2 << 2)); /* 请求方式+自动初始化+读传送 */
outportb(DMA8_CLRPTR_PORT, 0);
outportb(DMA1_COUNT_PORT, (size - 1)& 0x00FF);
outportb(DMA1_COUNT_PORT, (size - 1) >> 8);
outportb(DMA1_ADDR_PORT, (offset)& 0x00FF);
outportb(DMA1_ADDR_PORT, (offset) >> 8);
outportb(DMA1_PAGE_PORT, page);
outportb(DMA8_MASK_PORT, channel);
return TRUE;
}
void closedma8(int channel)
{
if (channel > 3 || channel == 2) return;
outportb(DMA8_MASK_PORT, channel | (1 << 2)); /* 屏蔽该通道 */
}
//这里是应用函数getenv()读取环境变量进行的初始化.
//可以:printf("%s",getenv("BLASTER"));测试.可以得到 A220 I7 D1 H6 P330 T6等.关于此数据说明的文档放在附件里.
int parse_sb_envstr(char *envstr, char id)
{
char buf[32] = "0x";
int i;
int j;
for (i = 0; envstr[i] != id && envstr[i] != '\0' && i < 128; i++);
if (envstr[i] == '\0' || i == 128) return 0;
else i++;
for (j = 2; j < 32 && envstr[i] != ' '; j++) buf[j] = envstr[i++];
return (int)strtoul(buf, NULL, 0);
}
int initdsp(PDSP pdsp)
{
if (!getenv("BLASTER")) return FALSE;
strupr(strcpy(pdsp->dspenvstr, getenv("BLASTER")));
pdsp->dspbaseioport = parse_sb_envstr(pdsp->dspenvstr, 'A');
pdsp->resetport = pdsp->dspbaseioport + 0x06; /*复位端口*/
pdsp->writedataport = pdsp->dspbaseioport + 0x0C; /*写数据端口*/
pdsp->writestatusport = pdsp->dspbaseioport + 0x0C; /*写状态端口*/
pdsp->readdataport = pdsp->dspbaseioport + 0x0A; /*读数据端口*/
pdsp->readstatusport = pdsp->dspbaseioport + 0x0E; /*读状态端口*/
pdsp->dspirqnum = parse_sb_envstr(pdsp->dspenvstr, 'I');
pdsp->dspdma8 = parse_sb_envstr(pdsp->dspenvstr, 'D');
pdsp->dspdma16 = parse_sb_envstr(pdsp->dspenvstr, 'H');
pdsp->mixerbaseioport = parse_sb_envstr(pdsp->dspenvstr, 'M');
pdsp->mpu401baseioport = parse_sb_envstr(pdsp->dspenvstr, 'P');
writedsp(pdsp, DSP_GET_VERSION);
return TRUE;
}
void writedsp(PDSP pdsp, unsigned char byte)
{
outportb(pdsp->writedataport, byte);
}
void setdspblocksize(PDSP pdsp, unsigned short blksize)
{
writedsp(pdsp, DSP_SET_BLK_SIZE);
writedsp(pdsp, (blksize - 1) &0x00FF);
writedsp(pdsp, (blksize - 1) >> 8);
}
void dspstartdma8(PDSP pdsp)
{
writedsp(pdsp, DSP_START_DMA8);
}
void dsppausedma8(PDSP pdsp)
{
writedsp(pdsp, DSP_PAUSE_DMA8);
}
void dspsetsamplerate(PDSP pdsp, unsigned short rate)
{
unsigned short timeconst = (unsigned short)(65536L - (256000000L / rate));
writedsp(pdsp, DSP_SET_SAM_RATE);
writedsp(pdsp, timeconst >> 8);
}
//中断的屏蔽与打开.可以使某位=0或1即可.
int initpic(int irqnum)
{
unsigned char mask;
mask = inportb(PIC_PORT_21H);
mask &= ~(1 << irqnum);
outportb(PIC_PORT_21H, mask);
}
void closepic(int irqnum)
{
unsigned char mask;
mask = inportb(PIC_PORT_21H);
mask |= (1 << irqnum);
outportb(PIC_PORT_21H, mask);
}
int initsound(void)
{
sound_dma_buf = (unsigned char far *)malloc(DMA_BUFFER_SIZE); //分配内存空间存放WAV音乐数据
if (!sound_dma_buf) return FALSE;
initdsp(&cursbdsp); //初始化DSP
setdspblocksize(&cursbdsp, DMA_BUFFER_SIZE / 2); //设置时钟频率
initdma8(cursbdsp.dspdma8, sound_dma_buf, DMA_BUFFER_SIZE); //初始化DMA
initpic(cursbdsp.dspirqnum); //开中断
an_zhuang_zhong_duan(); //安装中断
return TRUE;
}
void closesound(void)
{
del_zhong_duan();
closepic(cursbdsp.dspirqnum);
closedma8(cursbdsp.dspdma8);
if (sound_dma_buf) free((void*)sound_dma_buf);
}
/* 装载WAVE 文件到 WAVE 对象 */
int loadwave(PWAVE pw, char *file)
{
WAVEFILE wf;
pw->fp = fopen(file,"rb"); //打开WAV文件
if (!pw->fp) return FALSE;
fread(&wf, sizeof(wf), 1, pw->fp); //读取WAV文件头,不知道的可以分析WAV文件结构
pw->wavetype = wf.wavetype;
pw->channel = wf.channel;
pw->samplerate = wf.sampling;
pw->samplebits = wf.sampbits;
pw->datalen = wf.filelen - sizeof(wf) - 14;
pw->leftdatalen = wf.filelen - sizeof(wf) - 14;
fseek(pw->fp, 14, SEEK_CUR);//pw->fp指向的是WAV声音保存的地方
return TRUE;
}
/* 销毁 WAVE 对象 */
void destroywave(PWAVE pw)
{
fclose(pw->fp);
pw->fp = NULL;
}
void playwave(PWAVE pwave)
{
pcurwave = pwave;
dma_buf_DSP_flag = FALSE;
fread((void*)sound_dma_buf, DMA_BUFFER_SIZE, 1, pcurwave->fp);//读取数据
initdma8(cursbdsp.dspdma8, sound_dma_buf, DMA_BUFFER_SIZE); //初始化DMA
dspsetsamplerate(&cursbdsp, pcurwave->samplerate*pcurwave->channel); //设置DSP播放频率
dspstartdma8(&cursbdsp);//DMA开始传送数据
}
void stopwave(PWAVE pwave)
{
dsppausedma8(&cursbdsp);
fseek(pcurwave->fp, sizeof(WAVEFILE) + 14, SEEK_SET);
pwave->leftdatalen = pwave->datalen;
}
void pausewave()
{
dsppausedma8(&cursbdsp);
}
/* 内部函数实现 */
void interrupt new_zhong_duan(void)
{ DSP_flag=0;
pcurwave->leftdatalen -= DMA_BUFFER_SIZE / 2;
//这句代码意思是每回只读取一数据块大小的一半.实现,播放前面一半的时候中断COPY后面一半进内存.播放后面一半的时候中断COPY
//前面一半进内存
fread((void*)(sound_dma_buf + dma_buf_DSP_flag * DMA_BUFFER_SIZE / 2),1, DMA_BUFFER_SIZE / 2, pcurwave->fp);
dma_buf_DSP_flag = ! dma_buf_DSP_flag;
outportb(0x20,0x20); /*复位中断控制器8259,向端口20h写一个20h*/
DSP_flag=1;
}
void an_zhuang_zhong_duan(void)
{
old_dsp_int_handle = getvect(cursbdsp.dspirqnum + 8);
setvect(cursbdsp.dspirqnum + 8, new_zhong_duan);
}
void del_zhong_duan(void)
{
setvect(cursbdsp.dspirqnum + 8, old_dsp_int_handle);
old_dsp_int_handle = NULL;
}
void installKeyboard(void) /*安装新键盘中断程序的函数*/
{
int i;
for(i=0;i<128;i++)
key_state[i]=key_pressed[i]=0; /*初始化键盘,无任何键被按下*/
oldInt9Handler=getvect(9); /*保存原09h中断*/
setvect(9,newInt9); /*设置新的中断*/
}
void shutDownKeyboard(void) /*恢复原键盘中断值*/
{
setvect(9,oldInt9Handler);
}
void far interrupt newInt9(void) /*新的键盘中断程序*/
{
unsigned char ScanCode,temp;
ScanCode=inportb(0x60); /*从键盘I/O端口读入一个按键码*/
temp=inportb(0x61); /*读取控制寄存器61H*/
outportb(0x61,temp | 0x80); /*并用82h完成一个OR操作*/
/*在控制寄存器上用7fh完成一个AND操作,以便复位键盘触发器,告诉硬件一个按键已被处理,可以读下一个键了*/
outportb(0x61,temp & 0x7f);
if(ScanCode&0x80) /*闭合状态*/
{
ScanCode&=0x7f;
key_state[ScanCode]=0;
}
else /*断开状态*/
{
key_state[ScanCode]=1;
key_pressed[ScanCode]=1;
}
outportb(0x20,0x20); /*复位中断控制器8259,向端口20h写一个20h*/
}
int getKey(int ScanCode) /*读取按键*/
{
int res;
res=key_state[ScanCode]|key_pressed[ScanCode]; /*求出所有按键的键码和*/
while(key_state[ScanCode])return 0; /* 如果按键未松开 */
key_pressed[ScanCode]=0; /*读取完后,恢复未按状态*/
return res;
}
main()
{
WAVE mywave = {0};
initsound();
loadwave(&mywave, "test.wav");
mywave.loops = -1;
playwave(&mywave);
installKeyboard();
while(1)
{
if(DSP_flag!=0&&getKey(KEY_A))
fseek(pcurwave->fp, sizeof(WAVEFILE) + 14, SEEK_SET);
if(DSP_flag!=0&&getKey(KEY_B)) break;
}
destroywave(&mywave);
closesound();
shutDownKeyboard();
}说明:参考了本论坛某牛人代码.名字忘了= =!心中感激.他的代码也贴上供参考学习!!
按字母A键从头重新播放.B键退出.
讨论:1.某些地方不需要读取是否可写也是可以的.如:
readdsp(PDSP pdsp)
{
while(!(inportb(pdsp->readstatusport) & (1 << 7)));
return(inportb(pdsp->readdataport));
}
实际上while(!(inportb(pdsp->readstatusport) & (1 << 7)));删掉完全没关系.存在有啥必要的呢?
2.如何同时播放两首音乐.如一个朗诵与一段背景音乐同时播放?
我的思考是声音的混合,这东西,一点接触都没- -!有没说明更好的思路?别说多线程.DOS还真木有这玩意.
PS:自己都没分了= =!大家友情探讨下... ...
PS:他的名字:RockCarry!!~!膜拜!!~!
下载它的代码学习~
[ 本帖最后由 暗留香 于 2010-6-2 02:58 编辑 ]






