VOGONS


Emu386 - 386 emulator for 286

Topic actions

Reply 60 of 88, by matze79

User metadata
Rank l33t
Rank
l33t

I used this years ago to run Creative Soundblaster Drivers using some 386 Instruction (CTCM etc)

Reply 61 of 88, by igully

User metadata
Rank Newbie
Rank
Newbie
analog_programmer wrote on 2025-09-01, 07:59:
I'm not sure that this confirms 100% the Emu386 can turn a 80186 into a real mode 80386, but managed to run "Dos-J Plus" driver […]
Show full quote

I'm not sure that this confirms 100% the Emu386 can turn a 80186 into a real mode 80386, but managed to run "Dos-J Plus" driver (requires 386 CPU) from this Jo22's post in DOSXbox-X with emulated 80186 + loaded Emu386 (well, the J-thing complains for no XMS memory, but at least it doesn't hang as it does on 286 or 186 without 386 emulation):

The attachment Dos-J Plus on 80186+Emu386.jpg is no longer available

I think this gives some hope, that EMU386S.EXE will be usable on a real 186 hardware. I'm still very disappointed from NEC's V30/20 "gems" with their missing INT6.

I don't know if it is worth to make a patch for proper 186+286 CPUs detection in Emu386 as I don't know someone who has XT-class machine with the weird 80186 CPU. Maybe I'll try something just for fun in DOSBox-X emulator.

P.S. igully's CPUDET.EXE reports it was running on 80188 while the emulation is for 80186 - maybe some minor bug. Jan's CHKCPU.EXE gives proper report for 80186.

The problem is that you are running a CPU emulator that is not accurate enough, so the CPU input prefetch queue which is used to measure the bus width gives inaccurate results. This is self-modifying code and the way DOSBox was designed, was to be fast, but not accurate. On a real machine or a more precise emulator it should work.

I suggest that for stability/reliability tests you never use inaccurate emulators or dynamic recompilation mode on any of them. Don't get me wrong, this is a trade-off developers make for a reason: precision vs emulation speed, which makes sense in "many" circumstances, but certainly not all.

Reply 62 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
igully wrote on 2025-09-01, 16:26:

The problem is that you are running a CPU emulator that is not accurate enough, so the CPU input prefetch queue which is used to measure the bus width gives inaccurate results. This is self-modifying code and the way DOSBox was designed, was to be fast, but not accurate. On a real machine or a more precise emulator it should work.

I suggest that for stability/reliability tests you never use inaccurate emulators or dynamic recompilation mode on any of them. Don't get me wrong, this is a trade-off developers make for a reason: precision vs emulation speed, which makes sense in "many" circumstances, but certainly not all.

I fully agree with this. But my oldest working PC-system is with 80286 CPU and currently it's fully disassembled. And I don't know someone with 80186 system. So, the only option for testing is DOSBox-X.

I'll suppose, that Emu386 can be used on 80186 system. I wish I could make a patch for missing 80186 CPU detection, but unfortunately I barely understand what's happening in this piece of code:

loc_392:			;  xref 26C6 <- start of function to check for 8086/88 and 80186/88 vs 286 or newer ***
push sp ; ???
pop ax ; ???
cmp sp,ax ; ???
je loc_393 ; Jump if equal <- processor is 286 or newer, jump to loc_393 ***
mov dx,2AC9h ; <- (2AC9='PC/AT with 80186/80286 processor required')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: PC/AT with 80186/80286 processor required...' and exit

loc_393: ; xref 26D1 <- start of function to check if processor is 286 only ***
pushf ; ???
or bx,0F000h ; ???
push bx ; ???
popf ; ???
pushf ; ???
pop ax ; ???
popf ; ???
and ax,0F000h ; ???
jz loc_394 ; Jump if zero <- processor is 286, jump to loc_394 ***
mov dx,2AF5h ; <- (2AF5='Processor is 386 or above')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: Processor is 386 or above...' and exit

loc_394: ; xref 26E5

EDIT: *** - mkarcher proved, that this original code is logically improper combination of 8086/88 and 80186/88 vs 286 or newer CPUs check + 286 only CPU check, if the Emu386 should work on 80186 CPU too, so I corrected some code comments.

Last edited by analog_programmer on 2025-09-04, 04:53. Edited 3 times in total.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 63 of 88, by mkarcher

User metadata
Rank l33t
Rank
l33t

Here are the missing comments:

loc_392:			;  xref 26C6 <- start of function to check for 80286 processor only
push sp ; push the value of the current stack pointer to the stack. This modifies the current stack pointer
pop ax ; pop the values pushed to the stack. This will return the stack pointer to the original value
cmp sp,ax ; compare whether the value pushed by "push sp" is the "old" or the "new" value. The 8086 and 80186 push
; the value after modification, while all newer processors push the value before modification.
je loc_393 ; Jump if equal <- processor is 80286, jump to loc_393
mov dx,2AC9h ; <- (2AC9='PC/AT with 80186/80286 processor required')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: PC/AT with 80186/80286 processor required...' and exit

loc_393: ; xref 26D1 <- start of function to check if processor is 386 or newer
pushf ; push the flags to the stack (save them for later recovery)
or bx,0F000h ; ensure the top 4 bits of BX are set
push bx ; push the value of BX to the stack
popf ; pop the value from the stack into the flags register (into all bits that are actually writeable)
pushf ; push the contents of the current flag register back to the stack
pop ax ; pop the value of the current flags register into AX
popf ; restore the flags
and ax,0F000h ; isolate the top 4 bits of AX. On a 286 processor in real mode, these bits are
; fixed at zero.
jz loc_394 ; Jump if zero <- processor is not 386 or newer, jump to loc_394
mov dx,2AF5h ; <- (2AF5='Processor is 386 or above')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: Processor is 386 or above...' and exit

loc_394: ; xref 26E5

This code is bad, because the test used to detect whether the CPU is at least 80186 actually tests for 80286. The "push sp" behaviour changed with the 286, not with the 186, as described in https://wiki.osdev.org/User:ChosenOreo/CPU_De … r_Microcode_Bug .

A suitable test for 80186 would be shift count limiting: Try

loc_392:
mov cl,021h
shl cl, cl
jne loc_393

The idea is that an 8086 will shift CL left by 21 hex (33 decimal) bits, which will clear it, so the zero flags get set. On the 80186 and later, only the low 5 bits of CL are used for the shift count, so CL is shifted by a single bit, making it 42h / 66 decimal. Obviously, the zero flag will not be set in this case.

Reply 64 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
mkarcher wrote on 2025-09-01, 20:49:
Here are the missing comments: […]
Show full quote

Here are the missing comments:

loc_392:			;  xref 26C6 <- start of function to check for 80286 processor only
push sp ; push the value of the current stack pointer to the stack. This modifies the current stack pointer
pop ax ; pop the values pushed to the stack. This will return the stack pointer to the original value
cmp sp,ax ; compare whether the value pushed by "push sp" is the "old" or the "new" value. The 8086 and 80186 push
; the value after modification, while all newer processors push the value before modification.
je loc_393 ; Jump if equal <- processor is 80286, jump to loc_393
mov dx,2AC9h ; <- (2AC9='PC/AT with 80186/80286 processor required')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: PC/AT with 80186/80286 processor required...' and exit

loc_393: ; xref 26D1 <- start of function to check if processor is 386 or newer
pushf ; push the flags to the stack (save them for later recovery)
or bx,0F000h ; ensure the top 4 bits of BX are set
push bx ; push the value of BX to the stack
popf ; pop the value from the stack into the flags register (into all bits that are actually writeable)
pushf ; push the contents of the current flag register back to the stack
pop ax ; pop the value of the current flags register into AX
popf ; restore the flags
and ax,0F000h ; isolate the top 4 bits of AX. On a 286 processor in real mode, these bits are
; fixed at zero.
jz loc_394 ; Jump if zero <- processor is not 386 or newer, jump to loc_394
mov dx,2AF5h ; <- (2AF5='Processor is 386 or above')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: Processor is 386 or above...' and exit

loc_394: ; xref 26E5

This code is bad, because the test used to detect whether the CPU is at least 80186 actually tests for 80286. The "push sp" behaviour changed with the 286, not with the 186, as described in https://wiki.osdev.org/User:ChosenOreo/CPU_De … r_Microcode_Bug .

A suitable test for 80186 would be shift count limiting: Try

loc_392:
mov cl,021h
shl cl, cl
jne loc_393

The idea is that an 8086 will shift CL left by 21 hex (33 decimal) bits, which will clear it, so the zero flags get set. On the 80186 and later, only the low 5 bits of CL are used for the shift count, so CL is shifted by a single bit, making it 42h / 66 decimal. Obviously, the zero flag will not be set in this case.

WOW, this is great! Thank you very much or all this information and the detailed explanation of the code! Now everything makes sense, especially after I read the info from the link and your new code comments 😉

I think there will be no need for V30/20 (in)validation, so your code will work:

  • check if 186 or newer is 'false' -> exit
  • check if 186 or newer is 'true' -> check if 386 or newer is 'true' -> exit
  • check if 186 or newer is 'true' -> check if 386 or newer is 'false' -> proceed

And this must be the new patched assembly code (correct me, if I've mistaken something):

loc_392:			;  xref 26C6 <- start of function to check for 80186/88 or newer CPU
mov cl,021h ; <- (21h = 33)
shl cl, cl ; Shift left counter by 33 places
jne loc_393 ; Jump if not equal <- CPU is 80186 or newer, jump to loc_393 for 386 or newer CPU check
mov dx,2AC9h ; <- (2AC9='PC/AT with 80186/80286 processor required')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: PC/AT with 80186/80286 processor required...' and exit

loc_393: ; xref 26D1 <- start of function to check if processor is 286 only ***
pushf ; push the flags to the stack (save them for later recovery)
or bx,0F000h ; ensure the top 4 bits of BX are set
push bx ; push the value of BX to the stack
popf ; pop the value from the stack into the flags register (into all bits that are actually writable)
pushf ; push the contents of the current flag register back to the stack
pop ax ; pop the value of the current flags register into AX
popf ; restore the flags
and ax,0F000h ; isolate the top 4 bits of AX. On a 286 CPU in real mode, these bits are
; fixed at zero.
jz loc_394 ; Jump if zero <- processor is 286, jump to loc_394 ***
mov dx,2AF5h ; <- (2AF5='Processor is 386 or above')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: Processor is 386 or above...' and exit

loc_394: ; xref 26E5

EDIT: *** - mkarcher proved, that the original code is logically improper combination of 8086/88 and 80186/88 vs 286 or newer CPUs check + 286 only CPU check, if the Emu386 should work on 80186 CPU too, so I corrected some code comments. The code above is still improper for 80186 or 286 check!

The original part, that should be patched occupies 4 bytes:

push	sp			; push the value of the current stack pointer to the stack. This modifies the current stack pointer
pop ax ; pop the values pushed to the stack. This will return the stack pointer to the original value
cmp sp,ax ; compare whether the value pushed by "push sp" is the "old" or the "new" value. The 8086 and 80186 push
; the value after modification, while all newer processors push the value before modification.

And DEBUG.EXE gave me 4 bytes from the new code, which is a perfect fit:

mov	cl,021h			; <- (21h = 33)
shl cl, cl ; Shift left counter by 33 places

And of course I have to change the condition "je loc_393" to "jne loc_393" which is the 5th byte for the patch.

I don't know from where you got the code for 80186 or newer CPUs check, but if it works - it works 😀

Patching away... I'll report back later, if the test with the new CPU patch was (un)successful.

Last edited by analog_programmer on 2025-09-04, 04:54. Edited 3 times in total.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 65 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie

Ok or rather not Ok, after the new CPU validation checks patch, there's still the problem with DOSBox-X in 80186 mode. For sure 80186 emulation in DOSBox-X it's not 100% correct and the new code for 80186 check gives results as for 8086 CPU.

8086 emulation - works as expected, Emu386 refuses to load:

The attachment 8086_OK.jpg is no longer available

80186 emulation... Bummer! Emu386 refuses to load. DOSBox-X emulated 80186 is again recognized by the patched Emu386 as non-186 (which is incorrect) and also not as 286, 386 or newer (at least this is correct):

The attachment 80186_NOOOOOOOOO_freakin'_DOSBox.jpg is no longer available

80286 emulation - works as expected, Emu386 loads:

The attachment 80286_OK.jpg is no longer available

80386 emulation - works as expected, Emu386 refuses to load:

The attachment 80386_OK.jpg is no longer available

80486 emulation - works as expected, Emu386 refuses to load:

The attachment 80486_OK.jpg is no longer available

Anyone with real 80186/80188 PC here? Or is there some proper 80186 emulator? In 86Box I don't see any 80186 option.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 66 of 88, by mkarcher

User metadata
Rank l33t
Rank
l33t
analog_programmer wrote on 2025-09-02, 05:36:

This code is bad, because the test used to detect whether the CPU is at least 80186 actually tests for 80286. The "push sp" behaviour changed with the 286, not with the 186, as described in https://wiki.osdev.org/User:ChosenOreo/CPU_De … r_Microcode_Bug .

A suitable test for 80186 would be shift count limiting

WOW, this is great! Thank you very much or all this information and the detailed explanation of the code! Now everything makes sense, especially after I read the info from the link and your new code comments 😉

I think there will be no need for V30/20 (in)validation, so your code will work:

In fact, this code also does the correct thing on V20/V30: It treats them like the 8086, so it rejects them.

analog_programmer wrote on 2025-09-02, 05:36:
[…]
Show full quote
  • check if 186 or newer is 'false' -> exit
  • check if 186 or newer is 'true' -> check if 386 or newer is 'true' -> exit
  • check if 186 or newer is 'true' -> check if 386 or newer is 'false' -> proceed

Actually, the adjustment I suggested is incomplete. The second part doesn't check if the processor is "386 or newer", but it specifically checks for "286". On the 80286, the top 4 bits are always zero in the flags register in real mode. On the 386 and up, some (or all?) of these bits are writeable in real mode, and not all of those bits are zero in V86 mode. But on the 8086 and 80186, all those bits are always 1. So the test for the top 4 bits being "always 0" only matches the 286!

Writing a test that detects a 386 either in V86 (EMM386 active, Windows DOS box) or real mode (plain DOS), but does not falsely respond to an 8086 or 80186, without writing it as two tests (one for "286" and a second one for "386") is challenging, so I thought about it for a while, to still make it fit in the original space. Actually, a slight variation should work. The code tries to write 1 into all top 4 bits. On the 80186, these bits are always 1 anyway, so the value read-back will be "all bits one" again. On the 286 in real mode, all these bits are zero all the time, so the readback is "all bits zero". On the 80386, in real mode, bit 15 is always zero, bit bits 14-12 are writeable, so the readback will be "0111". On the 386 in VM86 mode, bit 15 is still always zero, but other bits are writeable or forced nonzero, depending on the virtualization environment. A virtualization environment that forces all these bits to zero is possible to write (I think), but unlikely to exist, as it will break many CPU detection routines. Note that "virtualization environment" in this context does not mean VMWare, Bochs or HyperV, but software like EMM386, QEMM and the Windows 3.x kernel in "enhanced 386 mode", so this is about software that actually is typically run on 386-class machines, and not just some hypothetical rambling. So we can expect a readback of something between 0001 and 0111 on a 386 or better in any sane operating environment.

Summary:

  • AX contains Fxxx on 186
  • AX contains 0xxx on 286
  • AX contains 1xxx to 7xxx on 386 or better

You can actually test for that with one instruction and one conditional jump: You subtract 1000h and test for the sign flag:

  • After subtracting 1000, AX contains Exxx on 186, sign flag is set
  • After subtracting 1000, AX contains Fxxx on 286, sign flag is set
  • After subtracting 1000, AX contains 0xxx to 6xxx on 386 or newer, sign flag is clear

So this is my suggested change:

popf				; unchanged instruction just for reference
sub ax,1000h ; replaces the AND AX,0F000h instruction, no size change
js loc_394 ; replaces the JZ instruction, no size change
mov dx,2AF5h ; unchanged instruction just for reference
analog_programmer wrote on 2025-09-02, 05:36:

The original part, that has to be patched occupies 4 bytes:

And DEBUG.EXE gave me 4 bytes from the new code, which is a perfect fit:

This was intentional. That's why I am shifting CL, because this avoids needing an extra instruction that makes a different register nonzero (which is the more typical approach).

analog_programmer wrote on 2025-09-02, 05:36:

Patching away... I'll report back later, if the test with the new CPU patch was (un)successful.

It won't work without the second patch. I'm sorry I missed that part.

Oh, I just saw your reply. While I first suspected DOSBox-X 80186 emulation to be buggy at the moment, just as you did, I checked the source code of DOSBox-X, and https://github.com/joncampbell123/dosbox-x/bl … ructions.h#L860 clearly allows the count of 33 to "get through" only on 8086 emulation, and not on 80186 emulation. While the explanation of the requirement for shift count limiting is written in a FIXME style, the code actually implements the limiting in https://github.com/joncampbell123/dosbox-x/bl … ructions.h#L381 , and returns zero if the shift count exceeds 8. This is for the "normal" core emulation. I didn't check the "dynamic recompilation" source code as well. If you currently have dynamic recompilatin enabled, try disabling it (I don't even know whether "dynrec" works for 80186 emulation). If you are already using the "normal" core emulation, which is interpreting each opcode just-in-time, double-check that you actually did apply the patch. The behaviour you show in your screenshots is identical to the expected behaviour of the unpatched version.

EDIT: This is the corresponding code for the dynrec code: https://github.com/joncampbell123/dosbox-x/bl … risc_x86.h#L682 - This just generates a native SHL instruction, which will limit the count to 31. But the consequence of this would be that all processors look like 286 and up, so I don't think your issue is due to use of the dynamic recompilation core emulator.

Reply 67 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
mkarcher wrote on 2025-09-02, 08:42:

In fact, this code also does the correct thing on V20/V30: It treats them like the 8086, so it rejects them.

I've assumed that 😉

mkarcher wrote on 2025-09-02, 08:42:
Actually, the adjustment I suggested is incomplete. The second part doesn't check if the processor is "386 or newer", but it spe […]
Show full quote

Actually, the adjustment I suggested is incomplete. The second part doesn't check if the processor is "386 or newer", but it specifically checks for "286". On the 80286, the top 4 bits are always zero in the flags register in real mode. On the 386 and up, some (or all?) of these bits are writeable in real mode, and not all of those bits are zero in V86 mode. But on the 8086 and 80186, all those bits are always 1. So the test for the top 4 bits being "always 0" only matches the 286!

Writing a test that detects a 386 either in V86 (EMM386 active, Windows DOS box) or real mode (plain DOS), but does not falsely respond to an 8086 or 80186, without writing it as two tests (one for "286" and a second one for "386") is challenging, so I thought about it for a while, to still make it fit in the original space. Actually, a slight variation should work. The code tries to write 1 into all top 4 bits. On the 80186, these bits are always 1 anyway, so the value read-back will be "all bits one" again. On the 286 in real mode, all these bits are zero all the time, so the readback is "all bits zero". On the 80386, in real mode, bit 15 is always zero, bit bits 14-12 are writeable, so the readback will be "0111". On the 386 in VM86 mode, bit 15 is still always zero, but other bits are writeable or forced nonzero, depending on the virtualization environment. A virtualization environment that forces all these bits to zero is possible to write (I think), but unlikely to exist, as it will break many CPU detection routines. Note that "virtualization environment" in this context does not mean VMWare, Bochs or HyperV, but software like EMM386, QEMM and the Windows 3.x kernel in "enhanced 386 mode", so this is about software that actually is typically run on 386-class machines, and not just some hypothetical rambling. So we can expect a readback of something between 0001 and 0111 on a 386 or better in any sane operating environment.

Summary:

  • AX contains Fxxx on 186
  • AX contains 0xxx on 286
  • AX contains 1xxx to 7xxx on 386 or better

You can actually test for that with one instruction and one conditional jump: You subtract 1000h and test for the sign flag:

  • After subtracting 1000, AX contains Exxx on 186, sign flag is set
  • After subtracting 1000, AX contains Fxxx on 286, sign flag is set
  • After subtracting 1000, AX contains 0xxx to 6xxx on 386 or newer, sign flag is clear

So this is my suggested change:

popf				; unchanged instruction just for reference
sub ax,1000h ; replaces the AND AX,0F000h instruction, no size change
js loc_394 ; replaces the JZ instruction, no size change
mov dx,2AF5h ; unchanged instruction just for reference

Thanks, I have to think on this. And of course I can try your new suggestion, but... There's some kind of misunderstanding. I didn't remove or skip the 386 and newer check (I was wrong and misled about the original code logic. It contains no 386 or newer check, but 286 only chek) after your code - see the assembly "jump" conditions again.

analog_programmer wrote on 2025-09-02, 05:36:
And DEBUG.EXE gave me 4 bytes from the new code, which is a perfect fit: […]
Show full quote

And DEBUG.EXE gave me 4 bytes from the new code, which is a perfect fit:

mov	cl,021h			; <- (21h = 33)
shl cl, cl ; Shift left counter by 33 places

And of course I have to change the condition "je loc_393" to "jne loc_393" which is the 5th byte for the patch.

Also you can see from the screenshots tests for 386 and 486 CPUs, that original code for this still works perfectly fine.

The attachment how_the_things_are_working_now.jpg is no longer available

EDIT: mkarcher proved, that the original code is logically improper combination of 8086/88 and 80186/88 vs 286 or newer CPUs check + 286 only CPU check, if the Emu386 should work on 80186 CPU too. The checks in the yellow frame are for 286 only, so here the logic is still broken for 80186 or 286 check only.

mkarcher wrote on 2025-09-02, 08:42:

This was intentional. That's why I am shifting CL, because this avoids needing an extra instruction that makes a different register nonzero (which is the more typical approach).

Ok, but I also have 56 free bytes from the entirely skipped "nag screen" subroutine + at least 322 usable bytes from now redundant message strings for the "nag screen", so for all the needed CPU validations the patch limit size is not only 4 bytes from the original 286-only check (or 31 bytes - from the entire original code with CPU checks) 😉 If I "inject" new CPU check functions, there will be just more manually added "jumps"in the patched code. We'll play "h4ck3r5", heh 😀

mkarcher wrote on 2025-09-02, 08:42:

It won't work without the second patch. I'm sorry I missed that part.

For the second time - the "second part" is there and fully functional.

mkarcher wrote on 2025-09-02, 08:42:

Oh, I just saw your reply. While I first suspected DOSBox-X 80186 emulation to be buggy at the moment, just as you did, I checked the source code of DOSBox-X, and https://github.com/joncampbell123/dosbox-x/bl … ructions.h#L860 clearly allows the count of 33 to "get through" only on 8086 emulation, and not on 80186 emulation. While the explanation of the requirement for shift count limiting is written in a FIXME style, the code actually implements the limiting in https://github.com/joncampbell123/dosbox-x/bl … ructions.h#L381 , and returns zero if the shift count exceeds 8. This is for the "normal" core emulation. I didn't check the "dynamic recompilation" source code as well. If you currently have dynamic recompilatin enabled, try disabling it (I don't even know whether "dynrec" works for 80186 emulation). If you are already using the "normal" core emulation, which is interpreting each opcode just-in-time, double-check that you actually did apply the patch. The behaviour you show in your screenshots is identical to the expected behaviour of the unpatched version.

EDIT: This is the corresponding code for the dynrec code: https://github.com/joncampbell123/dosbox-x/bl … risc_x86.h#L682 - This just generates a native SHL instruction, which will limit the count to 31. But the consequence of this would be that all processors look like 286 and up, so I don't think your issue is due to use of the dynamic recompilation core emulator.

I don't compile anything, as I only have a text file with disasseembled code generated from Sourcer. I patch the things with hex-editor by hand. I have no idea how is compiled the original EMU386S.EXE on which I'm applying the patches.

I may try something else as I have enough space for additional code. From the link you gave I can use this check for 8086/86 vs 80186/88 (if CPU is 8086/88 - exit) and the original check for 386 and newer (if CPU is 386 or newer - exit) and this way the Emu386 will be loaded only on remaining 80186 and 286 CPUs. Eventually I can also add V20/V30/V40/V50 check (if 8086/88 check is not enough to exclude these), but the code for this one is given as "This is questioned!". And I think this variant will also solve any potential DOSBox-X's 80186 emulation incompatibility recognition problems.

I don't want to publish/attach my work here, even if it's not for the commercial version of this "386 on 286" emulator (which I don't have). You know because of who 😉 But if you want to see it or you have some questions - send me a PM.

P.S. I've to learn x86 assembly to become a "l337", but I'm already too old and too lazy for this 😁

Last edited by analog_programmer on 2025-09-04, 04:58. Edited 2 times in total.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 68 of 88, by Jo22

User metadata
Rank l33t++
Rank
l33t++
analog_programmer wrote on 2025-09-01, 07:59:

I'm not sure that this confirms 100% the Emu386 can turn a 80186 into a real mode 80386, but managed to run "Dos-J Plus" driver (requires 386 CPU) from this Jo22's post in DOSXbox-X with emulated 80186 + loaded Emu386 (well, the J-thing complains for no XMS memory, but at least it doesn't hang as it does on 286 or 186 without 386 emulation):

The attachment Dos-J Plus on 80186+Emu386.jpg is no longer available

Hi, I vaguely remember that the old version of PCE emulator I was using had mentioned support for XMS on 8086/80186.
The CPU can be selected in the configuration file (8086/8088/80186/80188), but 808x vs 8018x perhaps requieres a recompile.

Edit: Correction. Statement taken from the old PCE home page:

A complete 8086/80186 emulator.
Switching between 8086 and 80186 can be done at runtime.
witching between x86 and x88 can be done at compile time.

Anyway, it just comes to mind. If Emu386 could be made run on PCE then it would enhance PCE's application compatibility quite a bit.
Also because there's no speed cap, I vaguely remember; PCE will try to run at max. cycles.

Edit: Real PCs using 80186 that come to mind: RM Nimbus PC-186/Siemens PC-D, PC-X/Tandy 2000/BBC Master 512/The Mindset

Last edited by Jo22 on 2025-09-02, 13:48. Edited 1 time in total.

"Time, it seems, doesn't flow. For some it's fast, for some it's slow.
In what to one race is no time at all, another race can rise and fall..." - The Minstrel

//My video channel//

Reply 69 of 88, by mkarcher

User metadata
Rank l33t
Rank
l33t
analog_programmer wrote on 2025-09-02, 10:45:

Thanks, I have to think on this. And of course I can try your new suggestion, but... There's some kind of misunderstanding. I didn't remove or skip the 386 and newer check after your code - see the assembly "jump" conditions again.

I didn't assume so. But as your "80186_NOOOO" screenshot shows, the 386&newer check is actually not reached on the 186 emulation, so it doesn't matter what the "386 and newer" check does. The emulated 186 already failed on the "186 or newer" check, which is expected for the "PUSH SP" based test, but not expected for the "SHL" based check. That's why I suggested you re-check that you actually tested a correctly patched executable.

analog_programmer wrote on 2025-09-02, 10:45:

Also you can see from the screenshots tests for 386 and 486 CPUs, that original code for this still works perfectly fine.

Of course it works fine on the 386 and 486. It always had and it always will. But it would not work fine if it were actually executed on an 186 processor. While we treat that check as an "386 or better" test, its actually a tests that accepts only 286 processors as suitable for Emu386. So it will consider the 80186 processor as "80386 or newer", which is not fine at all.

So, what we want is:

  • 8086/V20: fail first check ("186/286 required"), because that processor does not have INT 6.
  • 80386 and newer: pass first check, but fail the second check ("386 or above"), because no 386 emulation is needed on those processors.
  • 80186/80286: pass both checks.

What the original EMU386 does instead:

  • 8086/V20: fail the first check (OK)
  • 80386 and neer: pass the first check, fail the second check (OK)
  • 80286: pass the first check, pass the second check (OK)
  • 80186: fail the first check (BAD); if the first check would pass, it would also fail the second check (ALSO BAD)

The screenshots you show match this behaviour. My first suggestion was supposed to fix the first "BAD" thing, but forgot to fix the second "BAD" thing as well.

analog_programmer wrote on 2025-09-02, 10:45:
mkarcher wrote on 2025-09-02, 08:42:

Having it fit in 4 bytes was intentional. That's why I am shifting CL, because this avoids needing an extra instruction that makes a different register nonzero (which is the more typical approach).

Ok, but I also have 56 free bytes from ...

Well, maybe you have more free bytes, but I am not talking about a version that has modifications made to code that tries to implement some kind of copyright enforcement. I am talking about (and I will only be talking about) EMU386S as it is distributed as shareware. I recommend you to limit this thread to changes that relate to 186 support, and not mention any changes that can be interpreted as "changes that could technically motivate people to not register the software".

analog_programmer wrote on 2025-09-02, 10:45:
mkarcher wrote on 2025-09-02, 08:42:

It won't work without the second patch. I'm sorry I missed that part.

For the second time - the "second part" is there and fully functional.

How do you know that? You did not execute it on the 80186 now. Well, actually you did execute the second part on an 80186 processor in your very first post in this thread, and the result was, that the second part error out on everything that is not a 80286 processor:

analog_programmer wrote on 2025-08-30, 15:03:

The DOS version check is simple and clear, and out of the scope. I'm not sure if there's any 186 CPU check along with 286 CPU check condition - rather there's none. I tried to skip 286 check failing by changing "je loc_393" to "jmp loc_393", but then all the CPUs (8088, 8086, 186 etc.) excluding 286 are recognized as "386 or newer" CPU

(note that this quote links to the opening post it is taken from, not the post I am replying to otherwise).

analog_programmer wrote on 2025-09-02, 10:45:

I don't compile anything, as I only have a text file with disasseembled code generated from Sourcer. I patch the things with hex-editor by hand. I have no idea how is compiled the original EMU386S.EXE on which I'm applying the patches.

I know that very well. But DOSBox-X is able to either re-compile the DOS code to protected mode 32-bit or protected mode 64-bit code ("dynamic recompiling core"), or to execute it instruction by instruction ("normal core"). See https://dosbox-x.com/wiki/Guide%3ACPU-setting … E2%80%90X#_core . I was talking about the fact, that I verified that shift count limiting should work perfectly with core=normal according to the source code, but I wouldn't understand how the missing shift count limit of the 8086 would be emulated with core=dynrec.

Reply 70 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
Jo22 wrote on 2025-09-02, 12:07:

Hi, I vaguely remember that the old version of PCE emulator I was using had mentioned support for XMS on 8086/80186.
The CPU can be selected in the configuration file (8086/8088/80186/80188), but 808x vs 8018x perhaps requieres a recompile.

Hi, Jo22! What PCE means? This thing?

Jo22 wrote on 2025-09-02, 12:07:

Anyway, it just comes to mind. If Emu386 could be made run on PCE then it would enhance PCE's application compatibility quite a bit.
Also because there's no speed cap, I vaguely remember; PCE will try to run at max. cycles.

Edit: Real PCs using 80186 that come to mind: RM Nimbus PC-186/Siemens PC-D, PC-X/Tandy 2000/BBC Master 512/The Mindset

I'm sure with the help from mkarcher I'll manage to produce a proper CPU validation patch for Emu386 shareware/free version 😉 I don't like broken/unfinished code (the original one is also unfinished - no 80186 check at all), so I don't like the stupid patch for skipping all the CPU checks. And if it runs on emulated DOSBox-X emulated 80186 it will run on real 80186/88 too.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 71 of 88, by Jo22

User metadata
Rank l33t++
Rank
l33t++

^Hi, yes. That's the current PCE home page. The PCE version I was using was an older version, the IBM PC 5150 emulator, ca. 2006.
The old home page maybe is available via wayback machine.

"Time, it seems, doesn't flow. For some it's fast, for some it's slow.
In what to one race is no time at all, another race can rise and fall..." - The Minstrel

//My video channel//

Reply 72 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
mkarcher wrote on 2025-09-02, 12:55:

I didn't assume so. But as your "80186_NOOOO" screenshot shows, the 386&newer check is actually not reached on the 186 emulation, so it doesn't matter what the "386 and newer" check does. The emulated 186 already failed on the "186 or newer" check, which is expected for the "PUSH SP" based test, but not expected for the "SHL" based check. That's why I suggested you re-check that you actually tested a correctly patched executable.

Yes, It is obvious, that 386 check was not reached in this case and that's why I have doubts about the DOSBox-X 80186 emulation. Or you gave me non-working 4 bytes check for 186+286, because I changed the 5th byte for jump condition according to your code from "je loc_393" to "jne loc_393". Or maybe we "read and write on different languages" here - yeah, assembly is not mu thing 😁

EDIT: I was wrong and misled about the original code logic. It contains no 386 or newer check, but 286 only check instead.

mkarcher wrote on 2025-09-02, 12:55:
Of course it works fine on the 386 and 486. It always had and it always will. But it would not work fine if it were actually exe […]
Show full quote

Of course it works fine on the 386 and 486. It always had and it always will. But it would not work fine if it were actually executed on an 186 processor. While we treat that check as an "386 or better" test, its actually a tests that accepts only 286 processors as suitable for Emu386. So it will consider the 80186 processor as "80386 or newer", which is not fine at all.

So, what we want is:

  • 8086/V20: fail first check ("186/286 required"), because that processor does not have INT 6.
  • 80386 and newer: pass first check, but fail the second check ("386 or above"), because no 386 emulation is needed on those processors.
  • 80186/80286: pass both checks.

What the original EMU386 does instead:

  • 8086/V20: fail the first check (OK)
  • 80386 and neer: pass the first check, fail the second check (OK)
  • 80286: pass the first check, pass the second check (OK)
  • 80186: fail the first check (BAD); if the first check would pass, it would also fail the second check (ALSO BAD)

The screenshots you show match this behaviour. My first suggestion was supposed to fix the first "BAD" thing, but forgot to fix the second "BAD" thing as well.

I posted a picture (and the new piece of fixed assembly code post before the one with the picture) how the things now work.

EDIT: mkarcher proved, that the original code is logically improper combination of 8086/88 and 80186/88 vs 286 or newer CPUs check + 286 only CPU check, if the Emu386 should work on 80186 CPU too.

mkarcher wrote on 2025-09-02, 12:55:

How do you know that? You did not execute it on the 80186 now. Well, actually you did execute the second part on an 80186 processor in your very first post in this thread, and the result was, that the second part error out on everything that is not a 80286 processor:

Define what you mean by "second part". 'Cause again "we're talking different languages". "Second part" for me is the original 386 check (sorry, I've mistaken, it is actually 286 check only), "first part" is your 80186+286 check + 5th byte for inverted jump condition according to your code. And I always tripple-check the things before and after I'm doing something - on each step.

mkarcher wrote on 2025-09-02, 12:55:

I know that very well. But DOSBox-X is able to either re-compile the DOS code to protected mode 32-bit or protected mode 64-bit code ("dynamic recompiling core"), or to execute it instruction by instruction ("normal core"). See https://dosbox-x.com/wiki/Guide%3ACPU-setting … E2%80%90X#_core . I was talking about the fact, that I verified that shift count limiting should work perfectly with core=normal according to the source code, but I wouldn't understand how the missing shift count limit of the 8086 would be emulated with core=dynrec.

I'll not start to fix anything in DOSBox-X. There's an entire team for this.

Last edited by analog_programmer on 2025-09-04, 04:59. Edited 4 times in total.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 73 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
Jo22 wrote on 2025-09-02, 13:41:

^Hi, yes. That's the current PCE home page. The PCE version I was using was an older version, the IBM PC 5150 emulator, ca. 2006.
The old home page maybe is available via wayback machine.

But those archive sites also hosts "copyright/left infridge...refrigerator" things 🤣 Don't go there!

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 74 of 88, by Jo22

User metadata
Rank l33t++
Rank
l33t++

..

"Time, it seems, doesn't flow. For some it's fast, for some it's slow.
In what to one race is no time at all, another race can rise and fall..." - The Minstrel

//My video channel//

Reply 75 of 88, by Jo22

User metadata
Rank l33t++
Rank
l33t++

In case you're refering to DOS Plus and GEM.. They've used to be owned by Digital Research.
GEM has become Open Source (FreeGEM, OpenGEM), as did CP/M.
DOS Plus in turn is based on CP/M-86, DOS compatibility is being provided through PC-MODE compatibility layer.

https://www.theregister.com/2001/11/26/cp_m_c … ection_is_back/
https://en.wikipedia.org/wiki/FreeGEM#OpenGEM

"Time, it seems, doesn't flow. For some it's fast, for some it's slow.
In what to one race is no time at all, another race can rise and fall..." - The Minstrel

//My video channel//

Reply 76 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
Jo22 wrote on 2025-09-02, 14:09:

..

What? "Copy...cats" were mentioned. And I can't resist to make some reference to some(one's) "double standards" interpretations 😉

Jo22 wrote on 2025-09-02, 14:16:
In case you're refering to DOS Plus and GEM.. They've used to be owned by Digital Research. GEM has become Open Source (FreeGEM, […]
Show full quote

In case you're refering to DOS Plus and GEM.. They've used to be owned by Digital Research.
GEM has become Open Source (FreeGEM, OpenGEM), as did CP/M.
DOS Plus in turn is based on CP/M-86, DOS compatibility is being provided through PC-MODE compatibility layer.

https://www.theregister.com/2001/11/26/cp_m_c … ection_is_back/
https://en.wikipedia.org/wiki/FreeGEM#OpenGEM

Nope, I'm not referring to any of these. I just mean some basic principles.

For example, even if I'm all out of capriciossa or bolognese, I'll never try to advertise a BETA (unfinished and/or untested) version of a paid software product through a freeware version (dubbed by me a "shareware") 😉 Selling or even trying to sell a beta version to your (potential) customers doesn't seems like a honest thing.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 77 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
mkarcher wrote on 2025-09-02, 08:42:
So this is my suggested change: […]
Show full quote

So this is my suggested change:

popf				; unchanged instruction just for reference
sub ax,1000h ; replaces the AND AX,0F000h instruction, no size change
js loc_394 ; replaces the JZ instruction, no size change
mov dx,2AF5h ; unchanged instruction just for reference

If this is the correct "second part" addition to your "first part", I've just tried it in this combination (with your "first part"):

loc_392:			;  xref 26C6 <- start of function to check for 186 or newer CPU
mov cl,021h ; <- (21h = 33)
shl cl, cl ; Shift left counter by 33 places
jne loc_393 ; Jump if not equal <- CPU is 80186 or newer, jump to loc_393 for 386 or newer CPU check
mov dx,2AC9h ; <- (2AC9='PC/AT with 80186/80286 processor required')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: PC/AT with 80186/80286 processor required...' and exit

loc_393: ; xref 26D1 <- start of function to check if CPU is 80386 or newer
pushf ; push the flags to the stack (save them for later recovery)
or bx,0F000h ; ensure the top 4 bits of BX are set
push bx ; push the value of BX to the stack
popf ; pop the value from the stack into the flags register (into all bits that are actually writeable)
pushf ; push the contents of the current flag register back to the stack
pop ax ; pop the value of the current flags register into AX
popf ; restore the flags
sub ax,01000h ; subtracts 1000h
js loc_394 ; Jump if signed <- CPU is not 386 or newer, jump to loc_394
mov dx,2AF5h ; <- (2AF5='Processor is 386 or above')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: Processor is 386 or above...' and exit

loc_394: ; xref 26E5

Another 5 4 bytes patched (my mistake - 5 bytes are for the new 186/286 test, 4 bytes are for fixed 386+ test) and the results in DOSBox-X are exactly the same as for the original "second part".... and the stupid me had re-tested previous non-working version without the correction for 386+ test, so the thing is actually working with this code!

EDIT: mkarcher proved, that the original code is logically improper combination of 8086/88 and 80186/88 vs 286 or newer CPUs check + 286 only CPU check, if the Emu386 should work on 80186 CPU too. So, finally this is a proper code for 80186 or 286 check only!

Last edited by analog_programmer on 2025-09-04, 04:46. Edited 2 times in total.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.

Reply 78 of 88, by mkarcher

User metadata
Rank l33t
Rank
l33t
analog_programmer wrote on 2025-09-02, 15:34:
If this is the correct "second part" addition to your "first part", I've just tried it in this combination (with your "first par […]
Show full quote

If this is the correct "second part" addition to your "first part", I've just tried it in this combination (with your "first part"):

loc_392:			;  xref 26C6 <- start of function to check for 186 or newer CPU
mov cl,021h ; <- (21h = 33)
shl cl, cl ; Shift left counter by 33 places
jne loc_393 ; Jump if not equal <- CPU is 80186 or newer, jump to loc_393 for 386 or newer CPU check
mov dx,2AC9h ; <- (2AC9='PC/AT with 80186/80286 processor required')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: PC/AT with 80186/80286 processor required...' and exit

loc_393: ; xref 26D1 <- start of function to check if CPU is 80386 or newer
pushf ; push the flags to the stack (save them for later recovery)
or bx,0F000h ; ensure the top 4 bits of BX are set
push bx ; push the value of BX to the stack
popf ; pop the value from the stack into the flags register (into all bits that are actually writeable)
pushf ; push the contents of the current flag register back to the stack
pop ax ; pop the value of the current flags register into AX
popf ; restore the flags
sub ax,01000h ; subtracts 1000h
js loc_394 ; Jump if signed <- CPU is not 386 or newer, jump to loc_394
mov dx,2AF5h ; <- (2AF5='Processor is 386 or above')
jmp short loc_390 ; (269C) <- jump to loc_390 to print 'ERROR: Processor is 386 or above...' and exit

loc_394: ; xref 26E5

Another 5 bytes patched and the results in DOSBox-X are exactly the same as for the original "second part".

So, the results in DOSBox-X are still what you reported in Re: Emu386 - 386 emulator for 286 ? In that case, I really have to reproduce it on my own machine. I do not understand how this code can print "80186/80286 required" on an 80186 processor, but not on an 80286 processor, given that DOSBox-X uses a variant of the 286 emulation core if you select 186. Possibly it is a DOSBox issue after all. Can you point me to the specific DOSBox revision you are using?

Reply 79 of 88, by analog_programmer

User metadata
Rank Oldbie
Rank
Oldbie
mkarcher wrote on 2025-09-02, 17:50:

So, the results in DOSBox-X are still what you reported in Re: Emu386 - 386 emulator for 286 ?

Yep! Absolutely 100% exactly the same results. I don't even bothered to take new screenshots as they're like the last ones you linked.

EDIT: Stupid me. I'd re-tested the wrong thing.

mkarcher wrote on 2025-09-02, 17:50:

In that case, I really have to reproduce it on my own machine.

I still don't want to discuss the hex-patches in public. And not because someone may still want to try to pay for the non-free beta version (good luck!!! Emu386 site is long time dead just as the project itself 😉 ), but because of all the "double standards" here, when it comes to something, that may look like a "copy... cats in refrigerator" to someone.

mkarcher wrote on 2025-09-02, 17:50:

I do not understand how this code can print "80186/80286 required" on an 80186 processor, but not on an 80286 processor, given that DOSBox-X uses a variant of the 286 emulation core if you select 186. Possibly it is a DOSBox issue after all.

I'm wondering the same. The patched assembly code seems Ok to me, at least for the logic in the conditional jumps. As for these assembly CPU bugs and exploits used - it's not my busyness to dig in them.

EDIT: Now I'm wondering how to be more organized, so not to the mess things up, when I'm working with couple of files simultaneously...

mkarcher wrote on 2025-09-02, 17:50:

Can you point me to the specific DOSBox revision you are using?

EDIT: It doesn't matter anymore. The problem was not in the DOSBox-X 80186 emulation, but in the "device" behind the keyboard.

Last edited by analog_programmer on 2025-09-03, 05:25. Edited 1 time in total.

The word Idiot refers to a person with many ideas, especially stupid and harmful ideas.
This world goes south since everything's run by financiers and economists.
This isn't voice chat, yet some people overusing online communications talk and hear voices.