Esempio n. 1
0
/* 16-bit real mode DOS or 16-bit protected mode Windows */
void probe_dpmi() {
#if defined(TARGET_OS2)
	/* OS/2 apps do not run under DPMI */
#elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
	/* Win32 apps do not bother with DPMI */
#else
	if (!dpmi_init) {
		/* BUGFIX: WINE (Wine Is Not an Emulator) can run Win16 applications
		 *         but does not emulate the various low level interrupts one
		 *         can call. To avoid crashing under WINE we must not use
		 *         direct interrupts. */
		if (windows_emulation == WINEMU_WINE) {
			dpmi_present = 0;
			dpmi_init = 1;
			return;
		}

		{
			unsigned char present=0,proc=0;
			uint16_t version=0,privv=0,flags=0;
			uint32_t entry=0;

			__asm {
				push	es
				mov	ax,0x1687
				int	2Fh
				or	ax,ax
				jnz	err1
				mov	present,1
				mov	flags,bx
				mov	proc,cl
				mov	version,dx
				mov	privv,si
				mov	word ptr [entry+0],di
				mov	word ptr [entry+2],es
				pop	es
err1:
			}

			dpmi_flags = flags;
			dpmi_present = present;
			dpmi_version = version;
			dpmi_entry_point = entry;
			dpmi_processor_type = proc;
			dpmi_private_data_segment = 0xFFFF;
			dpmi_private_data_length_paragraphs = privv;
		}

#if TARGET_MSDOS == 32 || defined(TARGET_WINDOWS)
		/* when we ask for the "entry point" we mean we want the real-mode entrypoint.
		 * apparently some DPMI servers like Windows XP+NTVDM.EXE translate ES:DI coming
		 * back to a protected mode entry point, which is not what we're looking for.
		 *
		 * Interesting fact: When compiled as a Win16 app, the DPMI call actually works,
		 * but returns an entry point appropriate for Win16 apps. So.... apparently we
		 * can enter DPMI protected mode from within Win16? Hmm.... that might be something
		 * fun to experiment with :) */
		if (dpmi_present) {
			struct dpmi_realmode_call rc={0};
			rc.eax = 0x1687;
			mux_realmode_2F_call(&rc);
			if ((rc.eax&0xFFFF) == 0) {
				dpmi_flags = rc.ebx&0xFFFF;
				dpmi_present = 1;
				dpmi_version = rc.edx&0xFFFF;
				dpmi_entry_point = (((uint32_t)rc.es << 16UL) & 0xFFFF0000UL) + (uint32_t)(rc.edi&0xFFFFUL);
				dpmi_processor_type = rc.ecx&0xFF;
				dpmi_private_data_segment = 0xFFFF;
				dpmi_private_data_length_paragraphs = rc.esi&0xFFFF;
			}
			else {
				dpmi_present = 0;
			}
		}
#endif

#if TARGET_MSDOS == 32 && !defined(TARGET_OS2)
		dpmi_no_0301h = 0;
#endif
		dpmi_init = 1;

#if TARGET_MSDOS == 32 && !defined(TARGET_OS2)
		if (dpmi_present) {
			/* Thanks to bullshit like DOS4/GW we have to test the extender to know
			   whether or not core routines we need are actually there or not. If they
			   are not, then alternative workarounds are required. The primary reason
			   for this test is to avoid HIMEM.SYS API code returning nonsensical values
			   caused by DOS4/GW not supporting such vital functions as DPMI 0301H:
			   Call far real-mode procedure. Knowing this should also fix the VESA BIOS
			   test bug where the protected-mode version is unable to use the BIOS's
			   direct-call window bank-switching function.

			   At least, this code so far can rely on DPMI function 0300H: call real-mode
			   interrupt.*/

			/* test #1: allocate a 16-bit region, put a RETF instruction there,
			   and ask the DPMI server to call it (0301H test).

				Success:
				Registers unchanged
				CF=0

				Failure (DOS4/GW):
				CF=1
				AX=0301H  (wait wha?) */
			{
				uint16_t sel = 0;
				struct dpmi_realmode_call rc={0};
				unsigned char *proc = dpmi_alloc_dos(16,&sel);
				if (proc != NULL) {
					*proc = 0xCB; /* <- RETF */

					rc.cs = ((size_t)proc) >> 4UL;
					rc.ip = ((size_t)proc) & 0xFUL;
					if (dpmi_test_rm_entry_call(&rc) != 0)
						dpmi_no_0301h = 1;

					dpmi_free_dos(sel);
				}
			}
		}
#endif
	}
#endif
}
Esempio n. 2
0
int find_isa_pnp_bios() {
#if TARGET_MSDOS == 32
	uint8_t *scan = (uint8_t*)0xF0000;
#else
	uint8_t far *scan = (uint8_t far*)MK_FP(0xF000U,0x0000U);
#endif
	unsigned int offset,i;

	free_isa_pnp_bios();
	isa_pnp_bios_offset = (uint16_t)(~0U);
	memset(&isa_pnp_info,0,sizeof(isa_pnp_info));

	/* NTS: Stop at 0xFFE0 because 0xFFE0+0x21 >= end of 64K segment */
	for (offset=0U;offset != 0xFFE0U;offset += 0x10U,scan += 0x10) {
		if (scan[0] == '$' && scan[1] == 'P' && scan[2] == 'n' &&
			scan[3] == 'P' && scan[4] >= 0x10 && scan[5] >= 0x21) {
			uint8_t chk=0;
			for (i=0;i < scan[5];i++)
				chk += scan[i];

			if (chk == 0) {
				isa_pnp_bios_offset = (uint16_t)offset;
				_fmemcpy(&isa_pnp_info,scan,sizeof(isa_pnp_info));
				isa_pnp_rm_call = (void far*)MK_FP(isa_pnp_info.rm_ent_segment,
					isa_pnp_info.rm_ent_offset);

#if TARGET_MSDOS == 32
				isa_pnp_rm_call_area = dpmi_alloc_dos(ISA_PNP_RM_DOS_AREA_SIZE,&isa_pnp_rm_call_area_sel);
				if (isa_pnp_rm_call_area == NULL) {
					fprintf(stderr,"WARNING: Failed to allocate common area for DOS realmode calling\n");
					goto fail;
				}

				/* allocate descriptors to make calling the BIOS from pm mode */
				if ((isa_pnp_pm_code_seg = dpmi_alloc_descriptor()) != 0) {
					if (!dpmi_set_segment_base(isa_pnp_pm_code_seg,isa_pnp_info.pm_ent_segment_base)) {
						fprintf(stderr,"WARNING: Failed to set segment base\n"); goto fail; }
					if (!dpmi_set_segment_limit(isa_pnp_pm_code_seg,0xFFFFUL)) {
						fprintf(stderr,"WARNING: Failed to set segment limit\n"); goto fail; }
					if (!dpmi_set_segment_access(isa_pnp_pm_code_seg,0x9A)) {
						fprintf(stderr,"WARNING: Failed to set segment access rights\n"); goto fail; }
				}

				if ((isa_pnp_pm_data_seg = dpmi_alloc_descriptor()) != 0) {
					if (!dpmi_set_segment_base(isa_pnp_pm_data_seg,isa_pnp_info.pm_ent_data_segment_base)) {
						fprintf(stderr,"WARNING: Failed to set segment base\n"); goto fail; }
					if (!dpmi_set_segment_limit(isa_pnp_pm_data_seg,0xFFFFUL)) {
						fprintf(stderr,"WARNING: Failed to set segment limit\n"); goto fail; }
					if (!dpmi_set_segment_access(isa_pnp_pm_data_seg,0x92)) {
						fprintf(stderr,"WARNING: Failed to set segment access rights\n"); goto fail; }
				}

				/* allocate code selector for realmode area */
				if ((isa_pnp_rm_call_area_code_sel = dpmi_alloc_descriptor()) != 0) {
					if (!dpmi_set_segment_base(isa_pnp_rm_call_area_code_sel,(uint32_t)isa_pnp_rm_call_area)) {
						fprintf(stderr,"WARNING: Failed to set segment base\n"); goto fail; }
					if (!dpmi_set_segment_limit(isa_pnp_rm_call_area_code_sel,0xFFFFUL)) {
						fprintf(stderr,"WARNING: Failed to set segment limit\n"); goto fail; }
					if (!dpmi_set_segment_access(isa_pnp_rm_call_area_code_sel,0x9A)) {
						fprintf(stderr,"WARNING: Failed to set segment access rights\n"); goto fail; }
				}

				isa_pnp_pm_use = 1;
#endif

				return 1;
#if TARGET_MSDOS == 32
fail:				free_isa_pnp_bios();
				return 0;
#endif
			}
		}
	}

	return 0;
}