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键退出.
声霸卡环境变量.rar
(1.07 KB)
SRC.rar
(6.4 KB)
讨论: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 编辑 ]