void play_sfx(const pcspkt_ent_list *pl) { struct pcspk_ent *p; t8254_time_t pc,cc; t8254_time_t delta; int32_t tim = 0; p = *(pl++); cc = read_8254(0); while (p != NULL) { while (p->delay != 0) { if (p->freq != 0) { #ifdef TARGET_PC98 /* Double the counter to convert IBM PC timer delay (1193182Hz) to PC-98 timer delay (2457600Hz) */ write_8254_pc_speaker(p->freq << 9u); #else write_8254_pc_speaker(p->freq << 8u); #endif t8254_pc_speaker_set_gate(PC_SPEAKER_GATE_ON); } else { t8254_pc_speaker_set_gate(PC_SPEAKER_GATE_OFF); } #ifdef TARGET_PC98 /* Double the delay to convert IBM PC timer delay (1193182Hz) to PC-98 timer delay (2457600Hz) */ tim += (int32_t)((unsigned long)p->delay << 9u); #else tim += (int32_t)((unsigned int)p->delay << 8u); #endif while (tim >= 0l) { pc = cc; cc = read_8254(0); delta = (pc - cc) & 0xFFFFul; /* counts DOWN */ tim -= delta; } p++; } p = *(pl++); } t8254_pc_speaker_set_gate(PC_SPEAKER_GATE_OFF); }
void pulse_width_test() { int fd; unsigned char cc; unsigned int play; unsigned char plan_b=0; unsigned int patience = 10000; _cli(); write_8254_system_timer(0xFFFF); /* BUGFIX: Personal experience tells me BIOSes would fail reading the floppy if the IRQ 0 timer isn't ticking along at 18Hz */ _sti(); fd = open("..\\test1_22.wav",O_RDONLY|O_BINARY); if (fd < 0) fd = open("test1_22.wav",O_RDONLY|O_BINARY); if (fd < 0) { fprintf(stderr,"Cannot open test WAV\n"); return; } lseek(fd,44,SEEK_SET); read(fd,tmp,sizeof(tmp)); for (play=0;play < sizeof(tmp);play++) tmp[play] = (((tmp[play]) * 53) / 255) + 1; /* add "noise" to dither */ close(fd); /* set timer 0 to 54 ticks (1.191MHz / 54 ~ 22050Hz) */ _cli(); t8254_pc_speaker_set_gate(0); write_8254_pc_speaker(1); write_8254_system_timer(54); _sti(); _cli(); { outp(T8254_CONTROL_PORT,(0 << 6) | (0 << 4) | 0); /* latch counter N, counter latch read */ do { if (--patience == 1) break; cc = inp(T8254_TIMER_PORT(0)); inp(T8254_TIMER_PORT(0)); } while (cc < (54/2)); do { if (--patience == 0) break; cc = inp(T8254_TIMER_PORT(0)); inp(T8254_TIMER_PORT(0)); } while (cc >= (54/2)); if (patience <= 2) { write_8254_system_timer(0xFFFF); /* BUGFIX: on very old slow machines, the 54-count tick can easily cause the printf() below to absolutely CRAWL... */ _sti(); fprintf(stderr,"Oops! Either your CPU is too fast or the timer countdown trick doesn't work.\n"); _cli(); write_8254_system_timer(54); plan_b = 1; } } _sti(); while (1) { if (kbhit()) { int c = getch(); if (c == 27) break; } if (plan_b) { /* run with interrupts enabled, use IRQ0 to know when to tick */ _cli(); counter = 0; t8254_pc_speaker_set_gate(3); for (play=0;play < sizeof(tmp);) { outp(T8254_CONTROL_PORT,(2 << 6) | (1 << 4) | (T8254_MODE_0_INT_ON_TERMINAL_COUNT << 1)); /* MODE 0, low byte only, counter 2 */ outp(T8254_TIMER_PORT(2),tmp[play]); _sti(); while (counter == 0); _cli(); play += counter; counter = 0; } _sti(); } else { _cli(); t8254_pc_speaker_set_gate(3); outp(T8254_CONTROL_PORT,(0 << 6) | (0 << 4) | 0); /* latch counter N, counter latch read */ for (play=0;play < sizeof(tmp);play++) { outp(T8254_CONTROL_PORT,(2 << 6) | (1 << 4) | (T8254_MODE_0_INT_ON_TERMINAL_COUNT << 1)); /* MODE 0, low byte only, counter 2 */ outp(T8254_TIMER_PORT(2),tmp[play]); do { cc = inp(T8254_TIMER_PORT(0)); inp(T8254_TIMER_PORT(0)); } while (cc < (54/2)); do { cc = inp(T8254_TIMER_PORT(0)); inp(T8254_TIMER_PORT(0)); } while (cc >= (54/2)); } _sti(); } } t8254_pc_speaker_set_gate(0); }
int main() { struct t8254_readback_t readback; t8254_time_t tick[3]; unsigned int i; printf("8254 library test program\n"); if (!probe_8254()) { printf("Chip not present. Your computer might be 2010-era hardware that dropped support for it.\n"); return 1; } if (!probe_8259()) { printf("8259 interrupt controller not present. Your computer might be 2010-era hardware that dropped support for it.\n"); return 1; } printf("8254 base clock: %luHz\n",T8254_REF_CLOCK_HZ); speaker_rate = T8254_REF_CLOCK_HZ / 400UL; /* 400Hz */ prev_irq0 = _dos_getvect(T8254_IRQ+0x08); _dos_setvect(T8254_IRQ+0x8,irq0); _cli(); write_8254_pc_speaker(speaker_rate); write_8254_system_timer(max); _sti(); #ifdef TARGET_PC98 /* PC-98 does not have IRQ0 running by default */ p8259_unmask(T8254_IRQ); #endif while (1) { if (kbhit()) { int c = getch(); if (c == 27) break; else if (c == '-') { max -= 80; if (max > (0xFFFF-80)) max = 0xFFFF; _cli(); write_8254_system_timer(max); _sti(); } else if (c == '=') { max += 110; if (max < 110 || max > (0xFFFF-110)) max = 0xFFFF; _cli(); write_8254_system_timer(max); _sti(); } /* play with timer 2 and the PC speaker gate */ else if (c == 'p') { unsigned char on = (t8254_pc_speaker_read_gate() != 0) ? 1 : 0; if (on) t8254_pc_speaker_set_gate(0); else t8254_pc_speaker_set_gate(3); } else if (c == '1') { #ifndef TARGET_PC98 unsigned char v = t8254_pc_speaker_read_gate(); t8254_pc_speaker_set_gate(v ^ PC_SPEAKER_OUTPUT_TTL_AND_GATE); #endif } else if (c == '2') { #ifndef TARGET_PC98 unsigned char v = t8254_pc_speaker_read_gate(); t8254_pc_speaker_set_gate(v ^ PC_SPEAKER_COUNTER_2_GATE); #endif } else if (c == '[') { speaker_rate += 110; if (speaker_rate > (0xFFFF-110) || speaker_rate < 110) speaker_rate = 0xFFFF; write_8254_pc_speaker(speaker_rate); } else if (c == ']') { speaker_rate -= 110; if (speaker_rate > (0xFFFF-110)) speaker_rate = 0; write_8254_pc_speaker(speaker_rate); } else if (c == 'w') { printf("\n"); pulse_width_test(); _cli(); write_8254_system_timer(max); _sti(); printf("\n"); } else if (c == 'z') { /* sleep-wait loop test */ unsigned long delay_ticks; unsigned long z; unsigned int c,cmax; printf("\nDelay interval in us? "); z = 1000000; scanf("%lu",&z); delay_ticks = t8254_us2ticks(z); printf(" %lu = %lu ticks\n",z,delay_ticks); if (delay_ticks == 0UL) cmax = (unsigned int)(T8254_REF_CLOCK_HZ / 20UL); else cmax = (unsigned int)(T8254_REF_CLOCK_HZ / 20UL / (unsigned long)delay_ticks); if (cmax == 0) cmax = 1; write_8254_pc_speaker(T8254_REF_CLOCK_HZ / 400UL); /* tick as fast as possible */ while (1) { if (kbhit()) { if (getch() == 27) break; } for (c=0;c < cmax;c++) { t8254_pc_speaker_set_gate(3); t8254_wait(delay_ticks); t8254_pc_speaker_set_gate(0); t8254_wait(delay_ticks); } } } else if (c == 'd') { printf("\n \nDetail mode, hit 'd' again to exit: [WARNING: 8254 only]\n"); while (1) { if (kbhit()) { int c = getch(); if (c == 'd') { break; } } _cli(); readback_8254(T8254_READBACK_ALL,&readback); _sti(); printf("\x0D"); for (i=0;i <= 2;i++) { printf("[%u] stat=%02x count=%04x ",i, readback.timer[i].status, readback.timer[i].count); } fflush(stdout); } printf("\n"); } } for (i=0;i <= 2;i++) tick[i] = read_8254(i); /* BUG: DOSBox/DOSBox-X appear to have a bug where the PC speaker readback toggles * regardless of the GATE input to Counter 2. Bring GATE low (setting bit 1 * of port 61h to 0) is supposed to cause Counter 2 to stop. The AND gate * after the output (bit 0 of port 61h) is not supposed to affect the readback. */ printf("\x0D %04x %04x %04x max=%04x count=%04x SPKR=%u",tick[0],tick[1],tick[2], max,counter,read_8254_pc_speaker_output()!=0?1:0); fflush(stdout); } printf("\n"); #ifdef TARGET_PC98 /* PC-98 does not have IRQ0 running by default */ p8259_mask(T8254_IRQ); #endif _cli(); write_8254_pc_speaker(0); t8254_pc_speaker_set_gate(0); _dos_setvect(T8254_IRQ+0x8,prev_irq0); _sti(); write_8254_system_timer(0xFFFF); /* restore normal function to prevent BIOS from going crazy */ return 0; }