VOGONS


80186 and NEC V20/V30 DIV0 exception return IP?

Topic actions

First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

Where does the IP on the stack point to after a Divide by 0 exception? On 808x it's supposed to point to the next instruction. But on the 286 and up at least it's pointing to the DIV instruction (including any prefixes)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member

Correct. On the 808X division by zero/divison overflow was a "type-0 interrupt" whereas on later CPUs it was an exception, so the return semantics changed.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 2 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2024-08-06, 17:37:

Correct. On the 808X division by zero/divison overflow was a "type-0 interrupt" whereas on later CPUs it was an exception, so the return semantics changed.

So both the 80186 and NEC V20/V30 chips push the IP of the (I)DIV instruction (like 80286 and newer)? Although I can't find anything about it in the official documentation.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 3 of 29, by mkarcher

User metadata
Rank l33t
Rank
l33t
superfury wrote on 2024-08-06, 17:43:

So both the 80186 and NEC V20/V30 chips push the IP of the (I)DIV instruction (like 80286 and newer)? Although I can't find anything about it in the official documentation.

I have an 80186 datasheet published by AMD that contains the following information:

80186 datasheet wrote:

Interrupts

... Instruction exceptions occur when an unusual condition, which prevents any further instruction processing, is detected while attempting to execute an instruction. If the exception was caused by executing an ESC instruction with the ESC trap bit set in the relocation register, the return instruction will point to the ESC instruction, or to the segment override prefix immediately preceding the ESC instruction if the prefix was present. In all other cases, the return address will point at the instruction immediately following the instruction causing the exception.

If this is actually correct, and not just mistakenly pasted from an 8086 datasheet, the 80186 will behave in the same way as the 8086 for divide error - and it will be the only CPU in which the BOUND exception will push the return address of the subsequent instruction instead of the address of the BOUND instruction.

Reply 4 of 29, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

I would have presumed the v20 behaves like the 8086. There is nothing about exceptions in the v20 pdf i have or that divide by zero operates differently. singlestep is explained by div/0 is not.

i saw your fuzzing thread on vcf, pretty cool stuff.

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--

Reply 5 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm running the EPC 80186 testsuite right now (trying to iron out some CPU bugs): https://forum.osdev.org/viewtopic.php?f=13&t=23739&start=15, http://orbides.1gb.ru/80186_tests.zip
It seems to display some interesting behaviour in it's testsuite:
- Divide by zero exception points to the DIV instruction with prefixes included, not after the DIV instruction(so not a trap but an exception behaviour, like 80286+).
- Top 4 bits of EFLAGS seem to be cleared always. But doesn't that conflict with 80286+ CPU automatic detection software, which always assume pre-286 sets these bits to non-zero?

For now I've modified the 'NEC V30' (actually a 80186 implementation only, no extra functionality implemented (like 8080 mode etc.)) emulation to:
- Divide by 0 like a 286+ (return point is the (I)DIV instruction with prefixes).
- Top 4 of EFLAGS cleared when running the testsuite specifically (hack), otherwise set as per official 80(1)86 documentation to properly detect.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 7 of 29, by jakethompson1

User metadata
Rank Oldbie
Rank
Oldbie

In practice since people don't attempt to recover from a divide by zero, would this 8086 vs. 286 real mode disparity have only affected debugger authors?

Reply 8 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member
jakethompson1 wrote on 2024-08-06, 18:55:

In practice since people don't attempt to recover from a divide by zero, would this 8086 vs. 286 real mode disparity have only affected debugger authors?

One infamous consequence is that the original Microsoft Flight Simulator stalls on 286+ as it triggers a divide exception, and no handler exists capable of recovering the situation, whereas on 8088 it just merrily continues on its way.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 9 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++

Oddly enough, using the EPC testsuite after fixing some issues with the test emulator (which is basically a stripped version of the normal emulator without any additional hardware, just CPU and RAM installed), there's a handful of tests going wrong now (mostly flags being weird somehow?):
- shifts
- bcdcnv
- jmpmov
- mul

Though that might be due to it using 808x logic or universal logic (386 testsuite-tested) for those instructions?
Though jmpmov's test failing is weird, as it's not supposed to be able to, unless going very wrong (it contains just jmp instructions, mov instructions and a single out instruction (don't now why though)).
Edit: Fixed jmpmov's test. There was an issue with juggling multiple CPU cores (in this case their lock behaviour, which is required for XCHG instructions and LOCK prefixes to behave properly without hanging the CPU). Those were leaving the test core on an invalid CPU core (out of valid range (buffer overflow) or on a CPU (structure) that's not actively emulated).

So that just leaves those shifts, bcdcnv and mul that are going wrong with the newly (hacked special case FLAGS register) 'NEC V30' emulation (actually a plain 80186 implementation).

Edit: Just tried the Generic Super PC/Turbo XT BIOS 3.1 again. On a XT with NEC V30 CPU it detects a 'V20 CPU', while erroring out somehow with "System error: 02" on VGA. On CGA it somehow managed to get itself into a loop waiting for the video base port +6 (the input status register to detect retrace), but instead managed to use base port of 0, thus reading the DMA controller's register which isn't setup at all, always reading 0 while waiting for it to set (and the wrong port of course) on using the CGA BIOS provided by the BIOS itself.
It's obvious there's something going very wrong there somehow.
System error 02 apparently means bad RAM?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 11 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member

Are you masking off undefined flags? Those can and do change between generations.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 12 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2024-08-12, 18:06:

Are you masking off undefined flags? Those can and do change between generations.

The odd thing is that they seem to be all kinds of flags going 'wrong' in the 186 flags. Pretty much all flags with results from bit 0 through bit 11 of the pushed flags on the stack by the tests. 2 times with MUL tests, pretty much all with shift/bcd ones, somehow. Test386.asm otoh passes all it's tests, and those should be most, if not all opcodes (though using limited modr/m types from what I remember).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 13 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member

Is there a particular reason you do not wish to use my test suite?

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 14 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2024-08-12, 18:41:

Is there a particular reason you do not wish to use my test suite?

The main issue is that it would add some weird dependency or even multiple. I'd need some kind of rust (if I remember correctly) parser to be added into my own emulator (which is written in C entirely (not C++ due to compiler issues on some platforms it supports)).
And I don't want to write an entire parser for just that myself.

Other than that, it's kind of odd that those 2 MUL instructions (and ALL BCD instructions) have flags errors, just on 80(1)8x apparently? Do they differ from 286+ flags changed somehow? UniPCemu uses the found 286+ flags always. Oddly enough those testsuites used to pass always, just not since the recent CPU improvements (INTA/NMI/SMI moved to pre-instruction phase, core prefetching moved/changed to be split between prefetch and instruction phases(immediate), instruction state commit adjusted (adding end of instruction EIP, partial instruction EBP) as well as fault/paging using the saved EIP/EBP by default (preventing stuff like INT or traps to point to the instruction) and paging not resetting EIP/EBP when locking the bus).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 15 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member
superfury wrote on 2024-08-13, 09:28:

The main issue is that it would add some weird dependency or even multiple. I'd need some kind of rust (if I remember correctly) parser to be added into my own emulator (which is written in C entirely (not C++ due to compiler issues on some platforms it supports)).
And I don't want to write an entire parser for just that myself.

The tests are written in JSON. JSON is a standard data interchange format and parsers are available for every language.
Nearly a dozen emulator authors have used these tests so far, including emulators written in C as well. I hear that often cJSON is used: https://github.com/DaveGamble/cJSON
An alternative is to use python to convert the test data like register state into a binary format. You could emit raw bytes of 'initial' register state and raw bytes of 'final' register state and this would be sufficient for many instructions.
At least one emulator that used these tests was written in assembly language.

superfury wrote on 2024-08-13, 09:28:

Other than that, it's kind of odd that those 2 MUL instructions (and ALL BCD instructions) have flags errors, just on 80(1)8x apparently? Do they differ from 286+ flags changed somehow? UniPCemu uses the found 286+ flags always. Oddly enough those testsuites used to pass always, just not since the recent CPU improvements (INTA/NMI/SMI moved to pre-instruction phase, core prefetching moved/changed to be split between prefetch and instruction phases(immediate), instruction state commit adjusted (adding end of instruction EIP, partial instruction EBP) as well as fault/paging using the saved EIP/EBP by default (preventing stuff like INT or traps to point to the instruction) and paging not resetting EIP/EBP when locking the bus).

If you could supply the starting register state and instruction bytes, I could tell you what is the the correct result on a real 8088 or V20 CPU.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 16 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2024-08-13, 13:27:
The tests are written in JSON. JSON is a standard data interchange format and parsers are available for every language. Nearly a […]
Show full quote
superfury wrote on 2024-08-13, 09:28:

The main issue is that it would add some weird dependency or even multiple. I'd need some kind of rust (if I remember correctly) parser to be added into my own emulator (which is written in C entirely (not C++ due to compiler issues on some platforms it supports)).
And I don't want to write an entire parser for just that myself.

The tests are written in JSON. JSON is a standard data interchange format and parsers are available for every language.
Nearly a dozen emulator authors have used these tests so far, including emulators written in C as well. I hear that often cJSON is used: https://github.com/DaveGamble/cJSON
An alternative is to use python to convert the test data like register state into a binary format. You could emit raw bytes of 'initial' register state and raw bytes of 'final' register state and this would be sufficient for many instructions.
At least one emulator that used these tests was written in assembly language.

superfury wrote on 2024-08-13, 09:28:

Other than that, it's kind of odd that those 2 MUL instructions (and ALL BCD instructions) have flags errors, just on 80(1)8x apparently? Do they differ from 286+ flags changed somehow? UniPCemu uses the found 286+ flags always. Oddly enough those testsuites used to pass always, just not since the recent CPU improvements (INTA/NMI/SMI moved to pre-instruction phase, core prefetching moved/changed to be split between prefetch and instruction phases(immediate), instruction state commit adjusted (adding end of instruction EIP, partial instruction EBP) as well as fault/paging using the saved EIP/EBP by default (preventing stuff like INT or traps to point to the instruction) and paging not resetting EIP/EBP when locking the bus).

If you could supply the starting register state and instruction bytes, I could tell you what is the the correct result on a real 8088 or V20 CPU.

The odd thing is that logging used to work when I implemented the validator. Somehow it isn't logging anything right now (with the validator executor only, not with the normal runtime for debugging normal apps (which is running fine)).
It does finish the tests and report the different bytes in memory vs what the testsuite reports the finishing state is (when HLT is entered), so it's actually executing instructions (just somehow not triggering logging on the debugger for some unknown reason).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 17 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member
superfury wrote on 2024-08-13, 19:31:

The odd thing is that logging used to work when I implemented the validator. Somehow it isn't logging anything right now (with the validator executor only, not with the normal runtime for debugging normal apps (which is running fine)).
It does finish the tests and report the different bytes in memory vs what the testsuite reports the finishing state is (when HLT is entered), so it's actually executing instructions (just somehow not triggering logging on the debugger for some unknown reason).

Okay, but there's nothing I can really do to help you with that. If you get it sorted and want my help, let me know.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 18 of 29, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. Somehow it seems to run the debugger now.

The 80186 shift tests keep setting the auxilliary carry flag (bit 4) for all results somehow? All other flags seem fine.
BCDCNV seems to have it's flags wrong all over?

With the MUL tests only 3 sets of flags are incorrect. I see auxilliary carry erroring(stack offset 8C), then overflow flag with auxilliary carry erroring out at stack offset 88h.

Oddly enough, looking at the shifts execution log, somehow it logs the result of a MOV etc. in register state before the intruction is even started somehow?

Edit: Found a bug with flag filtering.... Recreating logs...
Edit: OK. Might be my bad. The logs themselves seem generated fine from what I can see (wasn't seeing the ^-character before the instructions indicating they belong to the state that's dumped above it instead of below it (the instruction already finished executing before it's logged in that case. If following the register dump (which is always captured before an instruction starts executing and logged when an instruction finishes), it means that the dump immediately before(above) it (and T-states if logging is enabled) are the state of said instruction before it started fetching the instruction and executing it).
So it's actually setting the auxilliary carry flag when it might not supposed to be?
Edit: Hmmm... Running it in https://carlosrafaelgn.com.br/Asm86/ seems to set EFLAGS to 18h? (overflow flag set in the high byte)?
Ignoring bit 3(undefined, probably supposed to be bit 1 instead, so an online emulator bug), it also sets the auxilliary carry flag.
Edit: Hmmm... According to the source code it's a SAL instruction? Is there a difference between SAL and SHL?
Edit: That first instruction is on SETMO(C) instruction on a 808x CPU though (opcode D0/D1 /6 to be exact)?

Last edited by superfury on 2024-08-15, 16:36. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 19 of 29, by GloriousCow

User metadata
Rank Member
Rank
Member
superfury wrote on 2024-08-15, 14:50:

OK. Somehow it seems to run the debugger now.

The 80186 shift tests keep setting the auxilliary carry flag (bit 4) for all results somehow? All other flags seem fine.

aux carry flag is undefined after SHL and SHR. Behavior may differ between CPUs. An 8088 can set AF on a SHL, a V20 will always clear AF.
There's also no guarantee that the tests you're looking at matched actual hardware behavior of a 186.

superfury wrote on 2024-08-15, 14:50:

With the MUL tests only 3 sets of flags are incorrect. I see auxilliary carry erroring(stack offset 8C), then overflow flag with auxilliary carry erroring out at stack offset 88h.

aux carry flag is undefined after MUL. An 8088 will clear it. I believe the overflow flag should be set to the same state as the carry flag.

if you're going to implement undefined flags, you have to first decide which CPU you are going to implement them for.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc