Skip to content

Commit 394452b

Browse files
more
1 parent eaf478a commit 394452b

File tree

4 files changed

+416
-1
lines changed

4 files changed

+416
-1
lines changed

cxx/8254.cpp

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
/* test.c
2+
*
3+
* 8254 programmable interrupt timer test program.
4+
* (C) 2008-2012 Jonathan Campbell.
5+
* Hackipedia DOS library.
6+
*
7+
* This code is licensed under the LGPL.
8+
* <insert LGPL legal text here>
9+
*
10+
* Compiles for intended target environments:
11+
* - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box] */
12+
13+
/* NTS: As of 2011/02/27 the 8254 routines no longer do cli/sti for us, we are expected
14+
* to do them ourself. This is for performance reasons as well as for sanity reasons
15+
* should we ever need to use the subroutines from within an interrupt handler */
16+
17+
#include <stdio.h>
18+
#include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
19+
#include <stdlib.h>
20+
#include <unistd.h>
21+
#include <fcntl.h>
22+
#include <dos.h>
23+
24+
#include <hw/8254/8254.h>
25+
26+
#include <hw/dos/dos.h>
27+
#include <hw/dos/doswin.h>
28+
#include <hw/dos/dosbox.h>
29+
#include <hw/dos/dosntvdm.h>
30+
31+
#ifdef TARGET_WINDOWS
32+
# define WINFCON_STOCK_WIN_MAIN
33+
# include <hw/dos/winfcon.h>
34+
#endif
35+
36+
#ifdef TARGET_WINDOWS
37+
/* no interrupts */
38+
#elif defined(__cplusplus)
39+
/* why are dos_getvect() and dos_setvect() not available in C++ mode? */
40+
#else
41+
# define HOOK_IRQ
42+
#endif
43+
44+
#ifdef HOOK_IRQ
45+
# include <hw/8259/8259.h>
46+
#endif
47+
48+
static volatile unsigned int counter = 0;
49+
static unsigned int speaker_rate = 0;
50+
static unsigned int max = 0xFFFF;
51+
52+
#ifdef HOOK_IRQ
53+
void (__interrupt __far *prev_irq0)() = NULL;
54+
static void __interrupt __far irq0() {
55+
counter++;
56+
57+
#if T8254_IRQ >= 8
58+
p8259_OCW2(8,P8259_OCW2_NON_SPECIFIC_EOI);
59+
#endif
60+
p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
61+
}
62+
#endif
63+
64+
#if TARGET_MSDOS == 32
65+
/* 32-bit DOS flat mode doesn't impose restrictions */
66+
unsigned char tmp[238617];
67+
#else
68+
/* we're probably limited by 16-bit real mode and near pointers */
69+
unsigned char tmp[32768];
70+
#endif
71+
72+
void pulse_width_test() {
73+
int fd;
74+
unsigned char cc;
75+
unsigned int play;
76+
unsigned char plan_b=0;
77+
unsigned int patience = 10000;
78+
79+
_cli();
80+
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 */
81+
_sti();
82+
83+
fd = open("..\\test1_22.wav",O_RDONLY|O_BINARY);
84+
if (fd < 0) fd = open("test1_22.wav",O_RDONLY|O_BINARY);
85+
if (fd < 0) {
86+
printf("Cannot open test WAV\n");
87+
return;
88+
}
89+
lseek(fd,44,SEEK_SET);
90+
read(fd,tmp,sizeof(tmp));
91+
for (play=0;play < sizeof(tmp);play++) tmp[play] = (((tmp[play]) * 53) / 255) + 1; /* add "noise" to dither */
92+
close(fd);
93+
94+
/* set timer 0 to 54 ticks (1.191MHz / 54 ~ 22050Hz) */
95+
_cli();
96+
t8254_pc_speaker_set_gate(0);
97+
write_8254_pc_speaker(1);
98+
write_8254_system_timer(54);
99+
_sti();
100+
101+
_cli();
102+
{
103+
outp(T8254_CONTROL_PORT,(0 << 6) | (0 << 4) | 0); /* latch counter N, counter latch read */
104+
do {
105+
if (--patience == 1) break;
106+
cc = inp(T8254_TIMER_PORT(0));
107+
inp(T8254_TIMER_PORT(0));
108+
} while (cc < (54/2));
109+
do {
110+
if (--patience == 0) break;
111+
cc = inp(T8254_TIMER_PORT(0));
112+
inp(T8254_TIMER_PORT(0));
113+
} while (cc >= (54/2));
114+
if (patience <= 2) {
115+
write_8254_system_timer(0xFFFF); /* BUGFIX: on very old slow machines, the 54-count tick can easily cause the printf() below to absolutely CRAWL... */
116+
_sti();
117+
printf("Oops! Either your CPU is too fast or the timer countdown trick doesn't work.\n");
118+
_cli();
119+
write_8254_system_timer(54);
120+
plan_b = 1;
121+
}
122+
}
123+
_sti();
124+
125+
while (1) {
126+
if (kbhit()) {
127+
int c = getch();
128+
if (c == 27) break;
129+
}
130+
131+
if (plan_b) {
132+
/* run with interrupts enabled, use IRQ0 to know when to tick */
133+
_cli();
134+
counter = 0;
135+
t8254_pc_speaker_set_gate(3);
136+
for (play=0;play < sizeof(tmp);) {
137+
outp(T8254_CONTROL_PORT,(2 << 6) | (1 << 4) | (T8254_MODE_0_INT_ON_TERMINAL_COUNT << 1)); /* MODE 0, low byte only, counter 2 */
138+
outp(T8254_TIMER_PORT(2),tmp[play]);
139+
_sti();
140+
while (counter == 0);
141+
_cli();
142+
play += counter;
143+
counter = 0;
144+
}
145+
_sti();
146+
}
147+
else {
148+
_cli();
149+
t8254_pc_speaker_set_gate(3);
150+
outp(T8254_CONTROL_PORT,(0 << 6) | (0 << 4) | 0); /* latch counter N, counter latch read */
151+
for (play=0;play < sizeof(tmp);play++) {
152+
outp(T8254_CONTROL_PORT,(2 << 6) | (1 << 4) | (T8254_MODE_0_INT_ON_TERMINAL_COUNT << 1)); /* MODE 0, low byte only, counter 2 */
153+
outp(T8254_TIMER_PORT(2),tmp[play]);
154+
155+
do {
156+
cc = inp(T8254_TIMER_PORT(0));
157+
inp(T8254_TIMER_PORT(0));
158+
} while (cc < (54/2));
159+
160+
do {
161+
cc = inp(T8254_TIMER_PORT(0));
162+
inp(T8254_TIMER_PORT(0));
163+
} while (cc >= (54/2));
164+
}
165+
_sti();
166+
}
167+
}
168+
169+
t8254_pc_speaker_set_gate(0);
170+
}
171+
172+
int main() {
173+
struct t8254_readback_t readback;
174+
t8254_time_t tick[3];
175+
unsigned int i;
176+
177+
#if defined(TARGET_WINDOWS) && TARGET_WINDOWS == 32 && !defined(WIN386)
178+
/* As a 32-bit Windows application, we *can* talk directly to the 8254 but only if running under Windows 3.1/95/98/ME.
179+
* Windows NT would never permit us to directly talk to IO.
180+
*
181+
* However if we're a Win16 program or Watcom 386 application, Windows NT will trap and emulate the 8254 through NTVDM.EXE */
182+
detect_windows();
183+
if (!(windows_mode == WINDOWS_REAL || windows_mode == WINDOWS_STANDARD || windows_mode == WINDOWS_ENHANCED)) {
184+
printf("This test program is only applicable to Windows 3.x/9x\n");
185+
return 1;
186+
}
187+
#endif
188+
189+
printf("8254 library test program\n");
190+
if (!probe_8254()) {
191+
printf("Chip not present. Your computer might be 2010-era hardware that dropped support for it.\n");
192+
return 1;
193+
}
194+
#ifdef HOOK_IRQ
195+
if (!probe_8259()) {
196+
printf("8259 interrupt controller not present. Your computer might be 2010-era hardware that dropped support for it.\n");
197+
return 1;
198+
}
199+
#endif
200+
201+
printf("8254 base clock: %luHz\n",T8254_REF_CLOCK_HZ);
202+
203+
speaker_rate = T8254_REF_CLOCK_HZ / 400UL; /* 400Hz */
204+
205+
#ifdef HOOK_IRQ
206+
prev_irq0 = _dos_getvect(T8254_IRQ+0x08);
207+
_dos_setvect(T8254_IRQ+0x8,irq0);
208+
#endif
209+
210+
_cli();
211+
write_8254_pc_speaker(speaker_rate);
212+
write_8254_system_timer(max);
213+
_sti();
214+
215+
#ifdef HOOK_IRQ
216+
#ifdef TARGET_PC98
217+
/* PC-98 does not have IRQ0 running by default */
218+
p8259_unmask(T8254_IRQ);
219+
#endif
220+
#endif
221+
222+
while (1) {
223+
if (kbhit()) {
224+
int c = getch();
225+
if (c == 27)
226+
break;
227+
else if (c == '-') {
228+
max -= 80;
229+
if (max > (0xFFFF-80))
230+
max = 0xFFFF;
231+
232+
_cli();
233+
write_8254_system_timer(max);
234+
_sti();
235+
}
236+
else if (c == '=') {
237+
max += 110;
238+
if (max < 110 || max > (0xFFFF-110))
239+
max = 0xFFFF;
240+
241+
_cli();
242+
write_8254_system_timer(max);
243+
_sti();
244+
}
245+
/* play with timer 2 and the PC speaker gate */
246+
else if (c == 'p') {
247+
unsigned char on = (t8254_pc_speaker_read_gate() != 0) ? 1 : 0;
248+
if (on) t8254_pc_speaker_set_gate(0);
249+
else t8254_pc_speaker_set_gate(3);
250+
}
251+
else if (c == '1') {
252+
#ifndef TARGET_PC98
253+
unsigned char v = t8254_pc_speaker_read_gate();
254+
t8254_pc_speaker_set_gate(v ^ PC_SPEAKER_COUNTER_2_GATE);
255+
#endif
256+
}
257+
else if (c == '2') {
258+
#ifndef TARGET_PC98
259+
unsigned char v = t8254_pc_speaker_read_gate();
260+
t8254_pc_speaker_set_gate(v ^ PC_SPEAKER_OUTPUT_TTL_AND_GATE);
261+
#endif
262+
}
263+
else if (c == '[') {
264+
speaker_rate += 110;
265+
if (speaker_rate > (0xFFFF-110) || speaker_rate < 110)
266+
speaker_rate = 0xFFFF;
267+
268+
write_8254_pc_speaker(speaker_rate);
269+
}
270+
else if (c == ']') {
271+
speaker_rate -= 110;
272+
if (speaker_rate > (0xFFFF-110))
273+
speaker_rate = 0;
274+
275+
write_8254_pc_speaker(speaker_rate);
276+
}
277+
else if (c == 'w') {
278+
printf("\n");
279+
pulse_width_test();
280+
_cli();
281+
write_8254_system_timer(max);
282+
_sti();
283+
printf("\n");
284+
}
285+
else if (c == 'z') {
286+
/* sleep-wait loop test */
287+
unsigned long delay_ticks;
288+
unsigned long z;
289+
unsigned int c,cmax;
290+
291+
printf("\nDelay interval in us? ");
292+
z = 1000000; scanf("%lu",&z);
293+
294+
delay_ticks = t8254_us2ticks(z);
295+
printf(" %lu = %lu ticks\n",z,delay_ticks);
296+
297+
if (delay_ticks == 0UL) cmax = (unsigned int)(T8254_REF_CLOCK_HZ / 20UL);
298+
else cmax = (unsigned int)(T8254_REF_CLOCK_HZ / 20UL / (unsigned long)delay_ticks);
299+
if (cmax == 0) cmax = 1;
300+
301+
write_8254_pc_speaker(T8254_REF_CLOCK_HZ / 400UL); /* tick as fast as possible */
302+
while (1) {
303+
if (kbhit()) {
304+
if (getch() == 27) break;
305+
}
306+
307+
for (c=0;c < cmax;c++) {
308+
t8254_pc_speaker_set_gate(3);
309+
t8254_wait(delay_ticks);
310+
t8254_pc_speaker_set_gate(0);
311+
t8254_wait(delay_ticks);
312+
}
313+
}
314+
}
315+
else if (c == 'd') {
316+
printf("\n \nDetail mode, hit 'd' again to exit: [WARNING: 8254 only]\n");
317+
while (1) {
318+
if (kbhit()) {
319+
int c = getch();
320+
if (c == 'd') {
321+
break;
322+
}
323+
}
324+
325+
_cli();
326+
readback_8254(T8254_READBACK_ALL,&readback);
327+
_sti();
328+
printf("\x0D");
329+
for (i=0;i <= 2;i++) {
330+
printf("[%u] stat=%02x count=%04x ",i,
331+
readback.timer[i].status,
332+
readback.timer[i].count);
333+
}
334+
fflush(stdout);
335+
}
336+
printf("\n");
337+
}
338+
}
339+
340+
for (i=0;i <= 2;i++) tick[i] = read_8254(i);
341+
342+
/* BUG: DOSBox/DOSBox-X appear to have a bug where the PC speaker readback toggles
343+
* regardless of the GATE input to Counter 2. Bring GATE low (setting bit 1
344+
* of port 61h to 0) is supposed to cause Counter 2 to stop. The AND gate
345+
* after the output (bit 0 of port 61h) is not supposed to affect the readback. */
346+
printf("\x0D %04x %04x %04x max=%04x count=%04x SPKR=%u",tick[0],tick[1],tick[2],
347+
max,counter,read_8254_pc_speaker_output()!=0?1:0);
348+
fflush(stdout);
349+
}
350+
printf("\n");
351+
352+
#ifdef HOOK_IRQ
353+
#ifdef TARGET_PC98
354+
/* PC-98 does not have IRQ0 running by default */
355+
p8259_mask(T8254_IRQ);
356+
#endif
357+
#endif
358+
359+
_cli();
360+
write_8254_pc_speaker(0);
361+
t8254_pc_speaker_set_gate(0);
362+
#ifdef HOOK_IRQ
363+
_dos_setvect(T8254_IRQ+0x8,prev_irq0);
364+
#endif
365+
_sti();
366+
367+
write_8254_system_timer(0xFFFF); /* restore normal function to prevent BIOS from going crazy */
368+
return 0;
369+
}
370+

0 commit comments

Comments
 (0)