VOGONS


UniPCemu's 80286 emulation bug help?

Topic actions

Reply 60 of 69, by Scali

User metadata
Rank l33t
Rank
l33t
superfury wrote:

Why would it even find a NPU(FPU?)? There's none installed(as found by the IBM AT BIOS).

It may assume this because a RapidCAD has an FPU built in, rather than having executed an actual test for an FPU.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 61 of 69, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

A problem is here:

00:05:37:46.09280: 020E:A555 (66C1E010)SHLD AX,10
00:05:37:46.09312: EAX: 0000020e,
...
00:05:37:46.09376: 020E:A559 (B850A6)MOVW AX, A650
00:05:37:46.09408: EAX: 00000000, EBX: 0000a700, ECX: 00000000, EDX: 00000000

->

should be SHLD EAX,10, so EAX=0x020E0000

Here's the other one:

00:05:37:47.04304: 020E:A57D (F36766A5)REP MOVSD

Here is IP:
00:05:38:22.06368: Writing to memory: 0001DDF8=7D (})
00:05:38:22.06368: Writing to RAM: 0001DDF8=7D (})
00:05:38:22.06400: Writing to memory: 0001DDF9=A5 (¥)
00:05:38:22.06400: Writing to RAM: 0001DDF9=A5 (¥)

This is supposed to be CS (20E), but it's zero instead:
00:05:38:22.06400: Writing to memory: 0001DDFA=00 ( )
00:05:38:22.06400: Writing to RAM: 0001DDFA=00 ( )
00:05:38:22.06400: Writing to memory: 0001DDFB=00 ( )
00:05:38:22.06400: Writing to RAM: 0001DDFB=00 ( )
...
00:05:38:22.06432: Interrupt 08=0EBC:0000003C@020E:A57D(A5); ERRORCODE: -1

It appears to think that the CPU mode is 32-bits instead of 16 bits, so it's pushing dwords instead of words, because here is CS:

00:05:38:22.06336: Writing to memory: 0001DDFC=0E ()
00:05:38:22.06336: Writing to RAM: 0001DDFC=0E ()
00:05:38:22.06368: Writing to memory: 0001DDFD=02 ()
00:05:38:22.06368: Writing to RAM: 0001DDFD=02 ()
00:05:38:22.06368: Writing to memory: 0001DDFE=00 ( )
00:05:38:22.06368: Writing to RAM: 0001DDFE=00 ( )
00:05:38:22.06368: Writing to memory: 0001DDFF=00 ( )
00:05:38:22.06368: Writing to RAM: 0001DDFF=00 ( )

Last edited by peterferrie on 2017-02-15, 20:32. Edited 3 times in total.

Reply 62 of 69, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

Cosmetic suggestion:00:05:37:54.09280: Writing to RAM: 00000A31=0D (
)
00:05:37:54.09280: Writing to memory: 00000A32=0A (
)
00:05:37:54.09280: Writing to RAM: 00000A32=0A (
)

Perhaps filter those so that they don't break the listing.

Reply 63 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I just found and fixed a lot of 80386 32-bit code/data conversion bugs:
- results being truncated to 16-bits due to word instead of uint_32 container.
- EIP being truncated to 16-bits during loads of EIP(JMP, CALL(F), RET(F)).
- GRP opcodes for shifts(SHL, RCL, SHR, RCR etc.) being forgotten to convert fully, causing shifting in 32-bit data with half 16-bit logic(bit positions being 14,15 instead of 30,31 and masking down to 16-bits).
- The reported SHLD AX was the debugger being called using modrm_debugger16 instead of modrm_debugger32 to create string representations of the modr/m parameters. Now it creates correct register debugging output again.

This should fix most problems that occurred.

Edit: Fixed another bug: CWDE set/cleared bits 16-31 of EAX based on bit 7 in AL/(E)AX instead of bit 15 of (E)AX.
Edit: Fixed string instructions properly using the Address size attribute to select 32-bit EDI/ESI vs 16-bit SI/DI registers.

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

Reply 64 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

The latest dump with the bugfixes(also XCHG instruction fixed to check memory better):
https://www.dropbox.com/s/lprdo9zjp8uq7gd/deb … 6_0034.zip?dl=0

The dumps are started at real-mode location 9014:9B5B, which is set as a breakpoint to start logging(The first PUSHFD instruction(actually first 32-bit instruction) executed by the Windows 3.0 setup I'm testing with).

Edit: Implemented the Test Registers(just basic variables currently, as are the debug registers) and fixed DR/TR array sizes to be 8 entries instead of 7(causing a overflow on access). Also CR4 is made unexistant on the 80386. It does exist on 80486+ currently. Is that correct?

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

Reply 65 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm now trying to run the Megarace demo to see if it already works correctly enough to run it (the demo from http://www.dosgamesarchive.com/download/megarace/ ).

The second virtual hard disk checks out at 402Kb/sec, according to the installer(150Kb/sec required). It says it's a super drive, excellent for animations:P

Edit: Then I selected the option to start the game. It fades in the horsedrawn carriage, then crashes due to encountering an #UD(opcode 8F /1-7, since 8F /0 should be defined).

Edit: Disabling the emulator shutdown on #UD, the sound blaster is generating some kind of sound(sounds like a lot of noise. I don't know if it's actually some audio being played with a huge latency or simply the same part of audio being replayed over and over for some reason). The entire animation stops at that point, leaving the application unresponsive, it seems.

Is there any way to verify the 80386 emulation is working correctly and if there's errors, what's going wrong?

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

Reply 66 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm wondering about something. According to what I can find, opcode 8F with the ModR/M reg being 0 is POP modr/m. What about cases where the reg value is 1-7? Are they resulting in a #UD? Or are they counted as a normal POP modr/m instead(the opcode ignoring the reg field completely)?

Just fixed something that's used a lot in my emulator: when encountering instructions documented with /r, I though this meant that the r/m of the modr/m is forced to be a register instead of memory if specified(e.g. by forcing the MOD field of the ModR/M to 3). Looking at the 80386 user manual again, it says about this:

/r: indicates that the ModR/M byte of the instruction contains both a
register operand and an r/m operand

So this is simply in contrast to the /0 to /7 using the modr/m and reg fields to form the instruction? So the MOD and R/M fields function normally, not forcing the MOD field to 11b(or simply forcing the r/m field to point to a register instead of memory)?

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

Reply 67 of 69, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

I'm wondering about something. According to what I can find, opcode 8F with the ModR/M reg being 0 is POP modr/m. What about cases where the reg value is 1-7? Are they resulting in a #UD? Or are they counted as a normal POP modr/m instead(the opcode ignoring the reg field completely)?

They trigger #UD exceptions on access. Only /0 is defined.

superfury wrote:
Just fixed something that's used a lot in my emulator: when encountering instructions documented with /r, I though this meant th […]
Show full quote

Just fixed something that's used a lot in my emulator: when encountering instructions documented with /r, I though this meant that the r/m of the modr/m is forced to be a register instead of memory if specified(e.g. by forcing the MOD field of the ModR/M to 3). Looking at the 80386 user manual again, it says about this:

/r: indicates that the ModR/M byte of the instruction contains both a
register operand and an r/m operand

So this is simply in contrast to the /0 to /7 using the modr/m and reg fields to form the instruction? So the MOD and R/M fields function normally, not forcing the MOD field to 11b(or simply forcing the r/m field to point to a register instead of memory)?

Can you point to an example? I also thought that /r is supposed to indicate that the parameter is register only. /0 to /7 is used to indicate which of the specific groups are defined, when it's only a subset.

Reply 68 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I see it defined here:
http://www.logix.cz/michal/doc/i386/chp17-02.htm

Table 17-2. 16-Bit Addressing Forms with the ModR/M Byte […]
Show full quote

Table 17-2. 16-Bit Addressing Forms with the ModR/M Byte

r8(/r) AL CL DL BL AH CH DH BH
r16(/r) AX CX DX BX SP BP SI DI
r32(/r) EAX ECX EDX EBX ESP EBP ESI EDI
/digit (Opcode) 0 1 2 3 4 5 6 7
REG = 000 001 010 011 100 101 110 111

Effective
+---Address---+ +Mod R/M+ +--------ModR/M Values in Hexadecimal--------+

And

Table 17-3. 32-Bit Addressing Forms with the ModR/M Byte […]
Show full quote

Table 17-3. 32-Bit Addressing Forms with the ModR/M Byte

r8(/r) AL CL DL BL AH CH DH BH
r16(/r) AX CX DX BX SP BP SI DI
r32(/r) EAX ECX EDX EBX ESP EBP ESI EDI
/digit (Opcode) 0 1 2 3 4 5 6 7
REG = 000 001 010 011 100 101 110 111

Effective
+---Address---+ +Mod R/M+ +---------ModR/M Values in Hexadecimal-------+

17.2.2 How to Read the Instruction Set Pages […]
Show full quote

17.2.2 How to Read the Instruction Set Pages

The following is an example of the format used for each 80386 instruction description in this chapter:

CMC -- Complement Carry Flag

Opcode Instruction Clocks Description

F5 CMC 2 Complement carry flag
The above table is followed by paragraphs labelled "Operation," "Description," "Flags Affected," "Protected Mode Exceptions," "Real Address Mode Exceptions," and, optionally, "Notes." The following sections explain the notational conventions and abbreviations used in these paragraphs of the instruction descriptions.

17.2.2.1 Opcode

The "Opcode" column gives the complete object code produced for each form of the instruction. When possible, the codes are given as hexadecimal bytes, in the same order in which they appear in memory. Definitions of entries other than hexadecimal bytes are as follows:

It then says:

/digit: (digit is between 0 and 7) indicates that the ModR/M byte of the instruction uses only the r/m (register or memory) operand. The reg field contains the digit that provides an extension to the instruction's opcode.

/r: indicates that the ModR/M byte of the instruction contains both a register operand and an r/m operand.

Then, in the tables of instructions:
http://www.logix.cz/michal/doc/i386/chp17-a3.htm#17-03-ADC
(ADC instruction description)

ADC -- Add with Carry […]
Show full quote

ADC -- Add with Carry

Opcode Instruction Clocks Description

14 ib ADC AL,imm8 2 Add with carry immediate byte to AL
15 iw ADC AX,imm16 2 Add with carry immediate word to AX
15 id ADC EAX,imm32 2 Add with carry immediate dword to EAX
80 /2 ib ADC r/m8,imm8 2/7 Add with carry immediate byte to r/m
byte
81 /2 iw ADC r/m16,imm16 2/7 Add with carry immediate word to r/m
word
81 /2 id ADC r/m32,imm32 2/7 Add with CF immediate dword to r/m
dword
83 /2 ib ADC r/m16,imm8 2/7 Add with CF sign-extended immediate
byte to r/m word
83 /2 ib ADC r/m32,imm8 2/7 Add with CF sign-extended immediate
byte into r/m dword
10 /r ADC r/m8,r8 2/7 Add with carry byte register to r/m
byte
11 /r ADC r/m16,r16 2/7 Add with carry word register to r/m
word
11 /r ADC r/m32,r32 2/7 Add with CF dword register to r/m dword
12 /r ADC r8,r/m8 2/6 Add with carry r/m byte to byte
register
13 /r ADC r16,r/m16 2/6 Add with carry r/m word to word
register
13 /r ADC r32,r/m32 2/6 Add with CF r/m dword to dword register

But that would mean ADC can only work on registers using opcodes 10-13? That shouldn't be right?

Also, looking at the LXS instructions(LDS, LES, LFS, LGS, LSS), they're /r as well, but that wouldn't make sense, since it's illegal to use those instructions with register parameters afaik?

http://www.logix.cz/michal/doc/i386/chp17-l3.htm#17-03-LGS

LGS/LSS/LDS/LES/LFS -- Load Full Pointer […]
Show full quote

LGS/LSS/LDS/LES/LFS -- Load Full Pointer

Opcode Instruction Clocks Description

C5 /r LDS r16,m16:16 7,p=22 Load DS:r16 with pointer from memory
C5 /r LDS r32,m16:32 7,p=22 Load DS:r32 with pointer from memory
0F B2 /r LSS r16,m16:16 7,p=22 Load SS:r16 with pointer from memory
0F B2 /r LSS r32,m16:32 7,p=22 Load SS:r32 with pointer from memory
C4 /r LES r16,m16:16 7,p=22 Load ES:r16 with pointer from memory
C4 /r LES r32,m16:32 7,p=22 Load ES:r32 with pointer from memory
0F B4 /r LFS r16,m16:16 7,p=25 Load FS:r16 with pointer from memory
0F B4 /r LFS r32,m16:32 7,p=25 Load FS:r32 with pointer from memory
0F B5 /r LGS r16,m16:16 7,p=25 Load GS:r16 with pointer from memory
0F B5 /r LGS r32,m16:32 7,p=25 Load GS:r32 with pointer from memory

So it seems that /r just literally means it uses the entire modr/m byte to form the two parameters?

Also:
http://c9x.me/x86/html/file_module_x86_id_152.html

Protected Mode Exceptions
#UD If source operand is not a memory location.

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

Reply 69 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've reenabled the #UD handling of opcode 8F /1 to /7. So this shouldn't be executed by the Megarace demo executable, unless something's going terribly wrong with executing it on the emulated CPU?

80386+ 32-bit normal instructions: https://bitbucket.org/superfury/unipcemu/src/ … 386.c?at=master
80386+ new 0F instructions: https://bitbucket.org/superfury/unipcemu/src/ … 386.c?at=master
80286+ instructions: https://bitbucket.org/superfury/unipcemu/src/ … 286.c?at=master
80186+ instructions: https://bitbucket.org/superfury/unipcemu/src/ … V30.c?at=master
8086+ instructions: https://bitbucket.org/superfury/unipcemu/src/ … 086.c?at=master
Protection handling for 286+: https://bitbucket.org/superfury/unipcemu/src/ … ion.c?at=master
Basic jumptables: https://bitbucket.org/superfury/unipcemu/src/ … bls.c?at=master
0F opcode jumptables: https://bitbucket.org/superfury/unipcemu/src/ … s0f.c?at=master
Multitasking support: https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master
Interrupt support: https://bitbucket.org/superfury/unipcemu/src/ … pts.c?at=master
ModR/M support: https://bitbucket.org/superfury/unipcemu/src/ … drm.c?at=master
Generic flag calculations(mostly based on fake86 flag handling, with extensions to 32-bit operands): https://bitbucket.org/superfury/unipcemu/src/ … ags.c?at=master
Timings(for 286+(8086 is embedded in the 8086 core). Also instruction formats used for all instruction operand forms(16 vs 32 bit vs emulated CPU)): https://bitbucket.org/superfury/unipcemu/src/ … ngs.c?at=master
CPU main core: https://bitbucket.org/superfury/unipcemu/src/ … cpu.c?at=master

The structure of the opcodes is pretty 'simple' in all cases: the lowest CPU class(8086 at CPU 0 for normal opcodes, 286 for 0F opcodes at CPU 0) provides the basic timings/opcode handlers. Newer CPUs overwrite these settings with their entries or leave the settings alone. This works the same for jumptables(opcode_jmptbl, opcode0F_jmptbl), timings(CPUPMTimings) and instruction format tables(CPUInformation, CPUInformation0F). All of these tables are parsed when emulation of a CPU is started and processed into a more simple one-level lookup table to be used during runtime(256/512(including 0F opcodes) entries(containing up to 8 subentries for CPUPMTimings variants of the same 'instruction'(e.g. different privilege levels vs same privilege level etc. with task switches, stack switches etc.)) times 2 entries for real mode and protected mode timing variants)
Most of the precalcs done is createn by the functions at the bottom of their respective modules, except CPUPMTimings, which is done in the cpu main core itself(which is called by the emulator do do anything(CPU_exec function)).

Can anyone see what's going wrong? What's going wrong in my emulation? Jepael? Reenigne?

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