/* Compute table of pointers to look for directory entry of a file. */ static int init_dirent_table (void) { short get_sda_func; unsigned short dirent_offset; unsigned short true_dos_version; unsigned short dos_major, dos_minor; __dpmi_regs regs; if (dirent_count == -1) /* we already tried and found we can't */ return 0; /* Compute INT 21h function number and offset of directory entry from start of SDA. These depend on the DOS version. We need exact knowledge about DOS internals, so we need the TRUE DOS version (not the simulated one by SETVER), if that's available. */ true_dos_version = _get_dos_version(1); dos_major = true_dos_version >> 8; dos_minor = true_dos_version & 0xff; if ((dos_major == 3) && (dos_minor >= 10)) { get_sda_func = 0x5d06; dirent_offset = 0x1a7; } else if (dos_major == 4) { /* According to ``Undocumented DOS, 2nd edition'', I could have used 5d06 here, as for DOS 5 and above, but I like to be defensive. In fact, the above book itself uses 5d0b, contrary to its own recommendation. */ get_sda_func = 0x5d0b; dirent_offset = 0x1b3; } else if (dos_major >= 5) { get_sda_func = 0x5d06; dirent_offset = 0x1b3; } else { _djstat_fail_bits |= _STFAIL_OSVER; dirent_count = -1; return 0; } _djstat_fail_bits &= ~_STFAIL_OSVER; /* version is OK */ /* Get the pointer to SDA by calling undocumented function 5dh of INT 21. */ regs.x.ax = get_sda_func; __dpmi_int(0x21, ®s); if (regs.x.flags & 1) { _djstat_fail_bits |= _STFAIL_SDA; dirent_count = -1; /* if the call failed, never try this later */ return 0; } _djstat_fail_bits &= ~_STFAIL_SDA; /* Get SDA succeeded */ /* DOS 4.x might have several SDA's, which means we might have more than one place to look into. (It is typical of DOS 4 to complicate things.) Compute all the possible addresses where we will have to look. */ if (dos_major == 4) { /* The pointer returned by INT 21h, AX=5D0b points to a header which holds a number of SDA's and then an array of that number of records each one of which includes address of an SDA (DWORD) and its length and type (encoded in a WORD). While walking this list of SDA's, we add to each pointer the offset of directory entry and stash the resulting address in our table for later use. */ int sda_list_walker = MK_FOFF(regs.x.ds, regs.x.si); int i; int *tbl; dirent_count = _farpeekw(_dos_ds, sda_list_walker); /* number of SDA's */ /* Allocate storage for table. */ tbl = dirent_table = (int *)malloc(dirent_count*sizeof(int)); if (!dirent_table) { /* If malloc() failed, maybe later it will succeed, so don't store -1 in dirent_count. */ dirent_count = 0; _djstat_fail_bits |= _STFAIL_DCOUNT; return 0; } memset(dirent_table, 0, dirent_count*sizeof(int)); _djstat_fail_bits &= ~_STFAIL_DCOUNT; /* dirent_count seems OK */ /* Walk the array of pointers, computing addresses of directory entries and stashing them in our table. */ _farsetsel(_dos_ds); for (i = dirent_count, sda_list_walker += 2; i--; sda_list_walker += 6) { int sda_start = _farnspeekl(sda_list_walker); unsigned short sda_len = _farnspeekw(sda_list_walker + 4) & 0x7fff; /* Let's be defensive here: if this SDA is too short to have place for directory entry, we won't use it. */ if (sda_len > dirent_offset) *tbl++ = sda_start + dirent_offset; else dirent_count--; } } /* DOS 3.1 and 5.0 or later. We have only one SDA pointed to by whatever INT 21h, AH=5d returns. */ else { dirent_count = 1; dirent_place = MK_FOFF(regs.x.ds, regs.x.si) + dirent_offset; dirent_table = &dirent_place; } return 1; }
static boolean __wss_detect() { /* First find the port number */ if (!wss.port) { static unsigned int wss_ports[] = { 0x32c, 0x530, 0x604, 0xE80, 0xF40 }; int i; for (i = 0; i < 5; i++) { wss.port = wss_ports[i]; if (__wss_ping()) break; } if (i < 0) { wss.port = 0; return FALSE; } } /* Now disable output */ wss_output(FALSE); /* Detect the DMA channel */ if (!wss.dma) { static int __dma[] = { 0, 1, 3 }; int i; /* Enable playback IRQ */ __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); /* Start a short DMA transfer and check if DMA count is zero */ for (i = 0; i < 3; i++) { unsigned int timer, status, freq = 44100; wss.dma = __dma[i]; dma_disable(wss.dma); dma_set_mode(wss.dma, DMA_MODE_WRITE); dma_clear_ff(wss.dma); dma_set_count(wss.dma, 10); dma_enable(wss.dma); /* Clear IRQ status */ outportb(WSS_STATUS, 0); __wss_setformat(__wss_getrate(&freq)); __wss_outreg(WSSR_COUNT_LOW, 1); __wss_outreg(WSSR_COUNT_HIGH, 0); /* Tell codec to start transfer */ __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); _farsetsel(_dos_ds); timer = _farnspeekl(0x46c); while (_farnspeekl(0x46c) - timer <= 2) if (dma_get_count(wss.dma) == 0) break; __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); dma_disable(wss.dma); /* Now check if DMA transfer count is zero and an IRQ is pending */ status = inportb(WSS_STATUS); outportb(WSS_STATUS, 0); if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT)) break; wss.dma = 0; } if (!wss.dma) return FALSE; } /* Now detect the IRQ number */ if (!wss.irq) { unsigned int i, irqmask, freq = 5510; unsigned long timer, delta = 0x7fffffff; /* IRQ can be one of 2,3,5,7,10 */ irq_detect_start(0x04ac, __wss_irq_irqdetect); dma_disable(wss.dma); dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); dma_clear_ff(wss.dma); dma_set_count(wss.dma, 1); dma_enable(wss.dma); __wss_setformat(__wss_getrate(&freq)); /* Clear IRQ status */ outportb(WSS_STATUS, 0); __wss_outreg(WSSR_COUNT_LOW, 0); __wss_outreg(WSSR_COUNT_HIGH, 0); /* Prepare timeout counter */ _farsetsel(_dos_ds); timer = _farnspeekl(0x46c); while (timer == _farnspeekl(0x46c)); timer = _farnspeekl(0x46c); /* Reset all IRQ counters */ irq_detect_clear(); /* Tell codec to start transfer */ __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); /* Now wait 1/18 seconds */ while (timer == _farnspeekl(0x46c)); __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); dma_disable(wss.dma); /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval of 1/18.2 second, we should have received about 302 interrupts */ for (i = 2; i <= 10; i++) { int count = abs(302 - irq_detect_get(i, &irqmask)); if (count < delta) wss.irq = i, delta = count; } if (delta > 150) wss.irq = 0; irq_detect_end(); if (!wss.irq) return FALSE; } return TRUE; }