VOGONS


UniPCemu's 80286 emulation bug help?

Topic actions

Reply 40 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

The description of the checks executed when loading the TSS data segments (ES, DS, FS, GS) and their checks are a little strange.

https://pdos.csail.mit.edu/6.828/2014/reading … i386/s07_05.htm

13 DS, ES, FS, GS selectors are valid GP Segment 14 DS, ES, FS, GS se […]
Show full quote

13 DS, ES, FS, GS selectors are
valid GP Segment
14 DS, ES, FS, GS segments
are readable GP Segment
15 DS, ES, FS, GS segments
are present NP Segment
16 DS, ES, FS, GS segment DPL
>= CPL (unless these are
conforming segments) GP Segment

- What does it mean with the segments being readable? That it's not paged out of RAM? Segments are always readable? Also, it can't check, since each segment can use different limit configurations combined with paging?
- How can you even check present after readable? If the segment isn't present, you can even load it for checking(since it can't even be looked at for anything(when the register points to a non-present gate descriptor or non-present data/code descriptor)).
- Are the segments really checked in that strange order? Why would you load all segment descriptors(and follow non-present descriptors) and check CPL afterwards?
- Data segments can't be conforming? Only executable code segments can?

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

Reply 41 of 69, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Keep in mind that the Landmark BIOS is only aimed at IBM PC 5170 which has a 286 processor and not 386, so it would not expect your emulator to deal with paging. The description for the link you posted above is specific to 386 (FS, GS, paging) and I expect behavior and error conditions on loading the TSS segments to be different between 286 and 386. Pick one CPU and emulate that.

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 42 of 69, by crazyc

User metadata
Rank Member
Rank
Member
superfury wrote:

- What does it mean with the segments being readable? That it's not paged out of RAM? Segments are always readable? Also, it can't check, since each segment can use different limit configurations combined with paging?

Readability here just means they aren't execute only code segments.

superfury wrote:

- Are the segments really checked in that strange order? Why would you load all segment descriptors(and follow non-present descriptors) and check CPL afterwards?

I think that's actually wrong as SS should be loaded before CS as the DPL of SS is defined as CPL. In fact this later Pentium manual on page 342 says just that http://www.intel.com/design/pentium/MANUALS/24143004.pdf.

superfury wrote:

- Data segments can't be conforming? Only executable code segments can?

Only code segments can be conforming but a readable conforming code segment can be loaded into a data segment register.

Reply 43 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++
crazyc wrote:
Readability here just means they aren't execute only code segments. […]
Show full quote
superfury wrote:

- What does it mean with the segments being readable? That it's not paged out of RAM? Segments are always readable? Also, it can't check, since each segment can use different limit configurations combined with paging?

Readability here just means they aren't execute only code segments.

superfury wrote:

- Are the segments really checked in that strange order? Why would you load all segment descriptors(and follow non-present descriptors) and check CPL afterwards?

I think that's actually wrong as SS should be loaded before CS as the DPL of SS is defined as CPL. In fact this later Pentium manual on page 342 says just that http://www.intel.com/design/pentium/MANUALS/24143004.pdf.

superfury wrote:

- Data segments can't be conforming? Only executable code segments can?

Only code segments can be conforming but a readable conforming code segment can be loaded into a data segment register.

OK. But isn't the CPL the lowest 2 bits of the CS segment selector? Or is it the currently loaded CS DPL in the descriptor cache? Since any app can simply load CS with a different CPL by means of a JMP instruction to it's own segment selector? (CS&0xFFFC)

Last edited by superfury on 2017-01-24, 14:23. 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 44 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've modified the CPL to use the CPU's internal CPL value, which is:
- Reset when switched to Real mode (by resetting the PM flag).
- Reset when switching from Real mode to Protected mode.
- Set to the new segment selector low 2 bits(RPL of the segment selector?) when loading a non-conforming CS selector(both gated and without gate). When loading a conforming CS selector, it's loaded with the DPL of the new CS descriptor.
- The low 2 bits of CS being the requesting RPL to use in (CPL+RPL)<=DPL checks.

I now notice that during the task switch, it loads ES with the value 0000h, raising a #GP fault because of that(which the BIOS doesn't like, during switching the task to itself)?

Edit: Looking at the Task Switching again, it stores all segment registers (CS, (E)IP, DS, (FS, GS on 32-bit tasks) and SS), but I've forgotten to add the ES register. Now it properly stores and loads ES again.

Edit: It now tries to execute an IRET, which executes a normal IRET, since the NT flag is cleared during the first JMP 0080:0000 instruction. Is that correct? It then proceeds to real an invalid return address from memory(a bunch of AA55h values being read from the segment descriptor in memory).

Filename
debugger_protectedmode_diagnostics_20170124_invalid_unnested_IRET.zip
File size
1.58 MiB
Downloads
66 downloads
File comment
Log of the protected mode test executing an invalid IRET, causing a #GP fault.
File license
Fair use/fair dealing exception

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

Reply 45 of 69, by crazyc

User metadata
Rank Member
Rank
Member
superfury wrote:

OK. But isn't the CPL the lowest 2 bits of the CS segment selector? Or is it the currently loaded CS DPL in the descriptor cache? Since any app can simply load CS with a different CPL by means of a JMP instruction to it's own segment selector? (CS&0xFFFC)

The RPL of CS should always be equal to CPL but the DPL of CS could be different if CS is a conforming code segment. The DPL of SS on earlier CPUs is CPL though. In http://www.rcollins.org/ddj/May97/May97.html he uses SMM to change the DPL of SS which causes it to be at CPL0 in VM86 mode (changing the DPL of CS didn't work). Likely the same could be done with loadall on the 386.

When loading a conforming CS selector, it's loaded with the DPL of the new CS descriptor.

Wait, you change CPL when calling a conforming code segment (without going though a gate)? That's not right. CPL should be the same as the calling code segment (or to belabor the point a bit, the DPL of SS).

Reply 46 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

If the SS DPL is the CPL, why is the stack loaded AFTER the Code Segment? How can the code segment be checked against CPL if SS DPL isn't loaded yet?

6 CS selector is valid TS Code segment 7 Code segment is present NP Code […]
Show full quote

6 CS selector is valid TS Code segment
7 Code segment is present NP Code segment
8 Code segment DPL matches
CS RPL TS Code segment
9 Stack segment is valid GP Stack segment
10 Stack segment is present SF Stack segment
11 Stack segment DPL = CPL SF Stack segment
12 Stack-selector RPL = CPL GP Stack segment

If SS DPL is the CPL, then the CS CPL checks won't work? Since the SS register is checked and loaded at a later step(containing the selector of the previous task)? Also, step 11 will ALWAYS be true? SS DPL == SS DPL(=CPL)?

Also, quoting https://pdos.csail.mit.edu/6.828/2014/reading … i386/s07_05.htm :

The new task begins executing at the privilege level indicated by the RPL of the CS selector value that is loaded from the TSS.

Thus matching step 8 and 11 to make sense? The CPL IS the CS RPL for the checks to make sense?

Edit: Thinking about this is making my brain hurt. CPL can't be the SS DPL: Step 11 won't make sense. CPL cannot be CS RPL: Privilege check(MAX(CPL,RPL)>=DPL) won't make sense. CPL cannot be CS DPL: see previous. So when is the 'CPL' loaded and what is it based on? Some hidden register? When is that register loaded? And what value is it based on?

Edit: Also:
http://duartes.org/gustavo/blog/post/cpu-ring … and-protection/
That confirms that the low 2 bits of the CS segment selector is CPL.

Edit: Also:
http://stackoverflow.com/questions/33229626/x … privilege-level

So CPL is assigned from the lowest two bits of CS when loaded in protected mode. Real mode loads it with 0 when switched to/reset, Virtual 8086 mode with 3 when switched to.

So what does the CPL become with conforming code segments?

Edit: Some information found: http://stackoverflow.com/questions/31074904/p … ng-code-segment

So CS DPL IS the CPL to use?

Last edited by superfury on 2017-01-24, 22:06. 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 47 of 69, by crazyc

User metadata
Rank Member
Rank
Member

Look at the list from the Pentium manual above, or the 486 manual, SS is checked before CS. I believe the 80386 manual you linked is wrong and they corrected it in the 80486 manual and there more more mistakes than that. Look at https://pdos.csail.mit.edu/6.828/2014/readings/i386/IRET.htm under "STACK-RETURN-TO-V86" and tell me why it would do privilege checks on a v86 mode segment register value (everything between "Examine return CS" and "IF instruction pointer" is gone in the 80486 manual).

Table 7-1. Checks Made during a Task Switch […]
Show full quote

Table 7-1. Checks Made during a Task Switch

Step
Condition Checked
Exception
Error Code Reference

1
TSS descriptor is present in memory
NP
New Task's TSS

2
TSS descriptor is not busy
GP
New Task's TSS

3
TSS segment limit greater than or equal to 103
TS
New Task's TSS

4
Registers are loaded from the values in the TSS

5
LDT selector of new task is valid
TS
New Task's TSS

6
Code segment DPL matches selector RPL
TS
New Code Segment

7
SS selector is valid
GP
New Stack Segment

8
Stack segment is present in memory
SF
New Stack Segment

9
Stack segment DPL matches CPL
SF
New Stack Segment

10
LDT of new task is present in memory
TS
New Task's TSS

11
CS selector is valid
TS
New Code Segment

12
Code segment is present in memory
NP
New Code Segment

13
Stack segment DPL matches selector RPL
GP
New Stack Segment

14
DS, ES, FS, and GS selectors are valid
GP
New Data Segment

15
DS, ES, FS, and GS segments are readable
GP
New Data Segment

16
DS, ES, FS, and GS segments are present in memory
NP
New Data Segment

17
DS, ES, FS, and GS segment DPL greater than or equal to CPL (unless these are conforming segments)
GP
New Data Segment

Reply 48 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

So CS DPL is checked against CS RPL and SS DPL is checked against SS RPL. But what about CPL? What is it based on and when is it reloaded?

Also, why does the IRET in the latest dump fail? What's going wrong? Is it expecting NT to be set? Why would NT(&First word of TSS's TSS segment selector) be set without CALL? The task is activated through JMP 0080:0000, which switches tasks without being able to return(No nested task switch)? It pops strange data from the normal IRET?

Edit: Hmmm... https://lists.ubuntu.com/archives/kernel-team … ber/049401.html

So CPL is CS.RPL until the stack is valid. Then CPL is set to SS.DPL? Is that correct? So it's a hidden 2-bit 'register'?

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

Reply 49 of 69, by crazyc

User metadata
Rank Member
Rank
Member
superfury wrote:

Edit: Hmmm... https://lists.ubuntu.com/archives/kernel-team … ber/049401.html

So CPL is CS.RPL until the stack is valid. Then CPL is set to SS.DPL? Is that correct? So it's a hidden 2-bit 'register'?

That's how I did it in the MAME 286 emulation (other than the hidden register part) and it works with the one program I could find that uses 286 TSSs. I'm not certain that's how it really works but close enough.

superfury wrote:

Also, why does the IRET in the latest dump fail? What's going wrong? Is it expecting NT to be set? Why would NT(&First word of TSS's TSS segment selector) be set without CALL? The task is activated through JMP 0080:0000, which switches tasks without being able to return(No nested task switch)? It pops strange data from the normal IRET?

I believe that jmp shouldn't clear the nt flag if it's set. Imagine if you used int to call a task then jumped to a different one then returned, if jmp cleared the nt flag the iret wouldn't work.

Reply 50 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

And there's a problem: that first JMP 0080:0000 'clears' the NT flag, because it's switching to a different task that doesn't have NT set in it's flags, nor the first word of the TSS. So the IRET tries to return from a parallel task instead of from a nested task?

Before JMP: At task 0048(Nested task w/ NT&Backlink)
After JMP: At task 0058(Non-nested task)

Why does it try to interrupt return from a non-nested task? Or is task 0048 supposed to modify the TSS of task 0058(Task 0058 flags&Backlink=0 when reaching the JMP)?

Last edited by superfury on 2017-01-25, 07:20. Edited 2 times in total.

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

Reply 51 of 69, by SoftPCMuseum_

User metadata
Rank Newbie
Rank
Newbie
crazyc wrote:
superfury wrote:

Edit: Hmmm... https://lists.ubuntu.com/archives/kernel-team … ber/049401.html

So CPL is CS.RPL until the stack is valid. Then CPL is set to SS.DPL? Is that correct? So it's a hidden 2-bit 'register'?

That's how I did it in the MAME 286 emulation (other than the hidden register part) and it works with the one program I could find that uses 286 TSSs. I'm not certain that's how it really works but close enough.

What program was it that used 80286-style "Task State Segment" descriptors? Was it an operating system running in Virtual 8086 Mode or Protected Mode (such as Windows/386 and higher or OS/2), or was it something else (such as a debugger)?

Reply 52 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just implemented the functionality mentioned here: http://blog.world4engineers.com/privilege-lev … 0386-and-80486/

So CPL becomes the lowest privilege(>) of RPL and CPL when tasks are switched.

Edit: Now the application crashes during the first CALL 0078:0000?
Edit: It seems I've used the 32-bit TSS CS when starting task switching instead of the 16-bit one(thus loading uninitialized data). With this fixed, it runs again.

Edit:

00:01:13:88.08712: 0004:0018 (75FB)JNZ 0015
00:01:13:88.08736: EU&BIU cycles: 9, Operation cycles: 4, HW interrupt cycles: 0, Prefix cycles: 0, Exception cycles: 0, MMU read cycles: 0, MMU write cycles: 0, I/O bus cycles: 0, Prefetching cycles: 9, BIU prefetching cycles: 3
00:01:13:88.08736: Registers:
00:01:13:88.08744: AX: 0000, BX: 0000, CX: 0000, DX: 0000
00:01:13:88.08752: CS: 0004, DS: 000C, ES: 0020, SS: 0020, TR: 0048, LDTR:0038
00:01:13:88.08760: SP: 0800, BP: 1000, SI: 0000, DI: 0000
00:01:13:88.08760: IP: 0018, FLAGS: 4046
00:01:13:88.08760: CR0: FFF9
00:01:13:88.08768: GDTR: 0000000004A40098, IDTR: 00000000053C0180
00:01:13:88.08776: FLAGSINFO:c1P0a0Zstido00N0
00:01:13:88.08784: Interrupt status: 0000000000000000
00:01:13:88.08784: VGA@566,45(CRT:678,67)
00:01:13:88.08792: Display=832,246

00:01:13:88.08800: Reading from RAM: 00000524=00 ( )
00:01:13:88.08800: Reading from RAM: 00000525=00 ( )
00:01:13:88.08808: Reading from RAM: 00000526=58 (X)
00:01:13:88.08808: Reading from RAM: 00000527=00 ( )
00:01:13:88.08816: Reading from RAM: 00000528=00 ( )
00:01:13:88.08824: Reading from RAM: 00000529=85 (…)
00:01:13:88.08824: Reading from RAM: 0000052A=00 ( )
00:01:13:88.08832: Reading from RAM: 0000052B=00 ( )
00:01:13:88.08832: Reading from RAM: 000004FC=68 (h)
00:01:13:88.08840: Reading from RAM: 000004FD=00 ( )
00:01:13:88.08848: Reading from RAM: 000004FE=4C (L)
00:01:13:88.08848: Reading from RAM: 000004FF=04 ()
00:01:13:88.08856: Reading from RAM: 00000500=00 ( )
00:01:13:88.08856: Reading from RAM: 00000501=81 ()
00:01:13:88.08864: Reading from RAM: 00000502=00 ( )
00:01:13:88.08872: Reading from RAM: 00000503=00 ( )
00:01:13:88.08872: Switching task to task 0058
00:01:13:88.08880: Preparing outgoing task 0048 for transfer
00:01:13:88.08888: Reading from RAM: 000004EC=68 (h)
00:01:13:88.08888: Reading from RAM: 000004ED=00 ( )
00:01:13:88.08896: Reading from RAM: 000004EE=20 ( )
00:01:13:88.08904: Reading from RAM: 000004EF=04 ()
00:01:13:88.08904: Reading from RAM: 000004F0=00 ( )
00:01:13:88.08912: Reading from RAM: 000004F1=83 (ƒ)
00:01:13:88.08912: Reading from RAM: 000004F2=00 ( )
00:01:13:88.08920: Reading from RAM: 000004F3=00 ( )
00:01:13:88.08928: Reading from RAM: 000004EC=68 (h)
00:01:13:88.08928: Reading from RAM: 000004ED=00 ( )
00:01:13:88.08936: Reading from RAM: 000004EE=20 ( )
00:01:13:88.08960: Reading from RAM: 000004EF=04 ()
00:01:13:88.08968: Reading from RAM: 000004F0=00 ( )
00:01:13:88.08968: Reading from RAM: 000004F1=83 (ƒ)
00:01:13:88.08976: Reading from RAM: 000004F2=00 ( )
00:01:13:88.08984: Reading from RAM: 000004F3=00 ( )
00:01:13:88.08984: Writing to RAM: 000004EC=68 (h)
00:01:13:88.08992: Writing to RAM: 000004ED=00 ( )
00:01:13:88.08992: Writing to RAM: 000004EE=20 ( )
00:01:13:88.09000: Writing to RAM: 000004EF=04 ()
00:01:13:88.09024: Writing to RAM: 000004F0=00 ( )
00:01:13:88.09024: Writing to RAM: 000004F1=81 ()
00:01:13:88.09032: Writing to RAM: 000004F2=00 ( )
00:01:13:88.09032: Writing to RAM: 000004F3=00 ( )
00:01:13:88.09040: Saving outgoing task 0048 to memory
00:01:13:88.09048: Writing to memory: 00000420=00 ( )
00:01:13:88.09048: Writing to RAM: 00000420=00 ( )
00:01:13:88.09056: Writing to memory: 00000421=00 ( )
Show last 271 lines
00:01:13:88.09064: Writing to RAM: 00000421=00 ( )
00:01:13:88.09064: Writing to memory: 0000042E=1F ()
00:01:13:88.09072: Writing to RAM: 0000042E=1F ()
00:01:13:88.09072: Writing to memory: 0000042F=00 ( )
00:01:13:88.09080: Writing to RAM: 0000042F=00 ( )
00:01:13:88.09088: Writing to memory: 00000430=46 (F)
00:01:13:88.09088: Writing to RAM: 00000430=46 (F)
00:01:13:88.09096: Writing to memory: 00000431=40 (@)
00:01:13:88.09096: Writing to RAM: 00000431=40 (@)
00:01:13:88.09104: Writing to memory: 00000432=00 ( )
00:01:13:88.09112: Writing to RAM: 00000432=00 ( )
00:01:13:88.09112: Writing to memory: 00000433=00 ( )
00:01:13:88.09120: Writing to RAM: 00000433=00 ( )
00:01:13:88.09120: Writing to memory: 00000434=00 ( )
00:01:13:88.09128: Writing to RAM: 00000434=00 ( )
00:01:13:88.09136: Writing to memory: 00000435=00 ( )
00:01:13:88.09136: Writing to RAM: 00000435=00 ( )
00:01:13:88.09144: Writing to memory: 00000436=00 ( )
00:01:13:88.09144: Writing to RAM: 00000436=00 ( )
00:01:13:88.09152: Writing to memory: 00000437=00 ( )
00:01:13:88.09152: Writing to RAM: 00000437=00 ( )
00:01:13:88.09160: Writing to memory: 00000438=00 ( )
00:01:13:88.09168: Writing to RAM: 00000438=00 ( )
00:01:13:88.09168: Writing to memory: 00000439=00 ( )
00:01:13:88.09176: Writing to RAM: 00000439=00 ( )
00:01:13:88.09176: Writing to memory: 0000043A=00 ( )
00:01:13:88.09184: Writing to RAM: 0000043A=00 ( )
00:01:13:88.09192: Writing to memory: 0000043B=08 ()
00:01:13:88.09192: Writing to RAM: 0000043B=08 ()
00:01:13:88.09200: Writing to memory: 0000043C=00 ( )
00:01:13:88.09200: Writing to RAM: 0000043C=00 ( )
00:01:13:88.09208: Writing to memory: 0000043D=10 ()
00:01:13:88.09216: Writing to RAM: 0000043D=10 ()
00:01:13:88.09216: Writing to memory: 0000043E=00 ( )
00:01:13:88.09224: Writing to RAM: 0000043E=00 ( )
00:01:13:88.09224: Writing to memory: 0000043F=00 ( )
00:01:13:88.09232: Writing to RAM: 0000043F=00 ( )
00:01:13:88.09240: Writing to memory: 00000440=00 ( )
00:01:13:88.09240: Writing to RAM: 00000440=00 ( )
00:01:13:88.09248: Writing to memory: 00000441=00 ( )
00:01:13:88.09248: Writing to RAM: 00000441=00 ( )
00:01:13:88.09256: Writing to memory: 00000442=20 ( )
00:01:13:88.09256: Writing to RAM: 00000442=20 ( )
00:01:13:88.09264: Writing to memory: 00000443=00 ( )
00:01:13:88.09272: Writing to RAM: 00000443=00 ( )
00:01:13:88.09272: Writing to memory: 00000444=04 ()
00:01:13:88.09280: Writing to RAM: 00000444=04 ()
00:01:13:88.09280: Writing to memory: 00000445=00 ( )
00:01:13:88.09288: Writing to RAM: 00000445=00 ( )
00:01:13:88.09296: Writing to memory: 00000446=20 ( )
00:01:13:88.09296: Writing to RAM: 00000446=20 ( )
00:01:13:88.09304: Writing to memory: 00000447=00 ( )
00:01:13:88.09304: Writing to RAM: 00000447=00 ( )
00:01:13:88.09312: Writing to memory: 00000448=0C ()
00:01:13:88.09320: Writing to RAM: 00000448=0C ()
00:01:13:88.09320: Writing to memory: 00000449=00 ( )
00:01:13:88.09328: Writing to RAM: 00000449=00 ( )
00:01:13:88.09328: Switching active TSS to segment selector 0058
00:01:13:88.09336: Reading from RAM: 000004FC=68 (h)
00:01:13:88.09344: Reading from RAM: 000004FD=00 ( )
00:01:13:88.09344: Reading from RAM: 000004FE=4C (L)
00:01:13:88.09352: Reading from RAM: 000004FF=04 ()
00:01:13:88.09352: Reading from RAM: 00000500=00 ( )
00:01:13:88.09368: Reading from RAM: 00000501=81 ()
00:01:13:88.09376: Reading from RAM: 00000502=00 ( )
00:01:13:88.09376: Reading from RAM: 00000503=00 ( )
00:01:13:88.09384: Reading from RAM: 000004FC=68 (h)
00:01:13:88.09384: Reading from RAM: 000004FD=00 ( )
00:01:13:88.09392: Reading from RAM: 000004FE=4C (L)
00:01:13:88.09400: Reading from RAM: 000004FF=04 ()
00:01:13:88.09400: Reading from RAM: 00000500=00 ( )
00:01:13:88.09408: Reading from RAM: 00000501=81 ()
00:01:13:88.09408: Reading from RAM: 00000502=00 ( )
00:01:13:88.09416: Reading from RAM: 00000503=00 ( )
00:01:13:88.09424: Reading from RAM: 000004FC=68 (h)
00:01:13:88.09424: Reading from RAM: 000004FD=00 ( )
00:01:13:88.09432: Reading from RAM: 000004FE=4C (L)
00:01:13:88.09440: Reading from RAM: 000004FF=04 ()
00:01:13:88.09440: Reading from RAM: 00000500=00 ( )
00:01:13:88.09448: Reading from RAM: 00000501=81 ()
00:01:13:88.09448: Reading from RAM: 00000502=00 ( )
00:01:13:88.09456: Reading from RAM: 00000503=00 ( )
00:01:13:88.09464: Writing to RAM: 000004FC=68 (h)
00:01:13:88.09464: Writing to RAM: 000004FD=00 ( )
00:01:13:88.09472: Writing to RAM: 000004FE=4C (L)
00:01:13:88.09472: Writing to RAM: 000004FF=04 ()
00:01:13:88.09480: Writing to RAM: 00000500=00 ( )
00:01:13:88.09480: Writing to RAM: 00000501=83 (ƒ)
00:01:13:88.09488: Writing to RAM: 00000502=00 ( )
00:01:13:88.09496: Writing to RAM: 00000503=00 ( )
00:01:13:88.09496: Loading incoming TSS 0058 state
00:01:13:88.09504: Reading from RAM: 0000044C=00 ( )
00:01:13:88.09504: Read from memory: 0000044C=00 ( )
00:01:13:88.09512: Reading from RAM: 0000044D=00 ( )
00:01:13:88.09520: Read from memory: 0000044D=00 ( )
00:01:13:88.09520: Reading from RAM: 0000044E=00 ( )
00:01:13:88.09528: Read from memory: 0000044E=00 ( )
00:01:13:88.09528: Reading from RAM: 0000044F=06 ()
00:01:13:88.09536: Read from memory: 0000044F=06 ()
00:01:13:88.09544: Reading from RAM: 00000450=20 ( )
00:01:13:88.09544: Read from memory: 00000450=20 ( )
00:01:13:88.09552: Reading from RAM: 00000451=00 ( )
00:01:13:88.09552: Read from memory: 00000451=00 ( )
00:01:13:88.09560: Reading from RAM: 00000452=00 ( )
00:01:13:88.09568: Read from memory: 00000452=00 ( )
00:01:13:88.09568: Reading from RAM: 00000453=00 ( )
00:01:13:88.09576: Read from memory: 00000453=00 ( )
00:01:13:88.09576: Reading from RAM: 00000454=00 ( )
00:01:13:88.09584: Read from memory: 00000454=00 ( )
00:01:13:88.09592: Reading from RAM: 00000455=00 ( )
00:01:13:88.09592: Read from memory: 00000455=00 ( )
00:01:13:88.09600: Reading from RAM: 00000456=00 ( )
00:01:13:88.09600: Read from memory: 00000456=00 ( )
00:01:13:88.09608: Reading from RAM: 00000457=00 ( )
00:01:13:88.09608: Read from memory: 00000457=00 ( )
00:01:13:88.09616: Reading from RAM: 00000458=00 ( )
00:01:13:88.09624: Read from memory: 00000458=00 ( )
00:01:13:88.09624: Reading from RAM: 00000459=00 ( )
00:01:13:88.09632: Read from memory: 00000459=00 ( )
00:01:13:88.09632: Reading from RAM: 0000045A=00 ( )
00:01:13:88.09640: Read from memory: 0000045A=00 ( )
00:01:13:88.09648: Reading from RAM: 0000045B=00 ( )
00:01:13:88.09648: Read from memory: 0000045B=00 ( )
00:01:13:88.09656: Reading from RAM: 0000045C=00 ( )
00:01:13:88.09656: Read from memory: 0000045C=00 ( )
00:01:13:88.09664: Reading from RAM: 0000045D=00 ( )
00:01:13:88.09672: Read from memory: 0000045D=00 ( )
00:01:13:88.09672: Reading from RAM: 0000045E=00 ( )
00:01:13:88.09680: Read from memory: 0000045E=00 ( )
00:01:13:88.09680: Reading from RAM: 0000045F=00 ( )
00:01:13:88.09688: Read from memory: 0000045F=00 ( )
00:01:13:88.09704: Reading from RAM: 00000460=00 ( )
00:01:13:88.09704: Read from memory: 00000460=00 ( )
00:01:13:88.09712: Reading from RAM: 00000461=00 ( )
00:01:13:88.09712: Read from memory: 00000461=00 ( )
00:01:13:88.09720: Reading from RAM: 00000462=00 ( )
00:01:13:88.09728: Read from memory: 00000462=00 ( )
00:01:13:88.09728: Reading from RAM: 00000463=00 ( )
00:01:13:88.09736: Read from memory: 00000463=00 ( )
00:01:13:88.09736: Reading from RAM: 00000464=00 ( )
00:01:13:88.09744: Read from memory: 00000464=00 ( )
00:01:13:88.09744: Reading from RAM: 00000465=00 ( )
00:01:13:88.09752: Read from memory: 00000465=00 ( )
00:01:13:88.09760: Reading from RAM: 00000466=00 ( )
00:01:13:88.09760: Read from memory: 00000466=00 ( )
00:01:13:88.09768: Reading from RAM: 00000467=06 ()
00:01:13:88.09768: Read from memory: 00000467=06 ()
00:01:13:88.09776: Reading from RAM: 00000468=00 ( )
00:01:13:88.09784: Read from memory: 00000468=00 ( )
00:01:13:88.09784: Reading from RAM: 00000469=10 ()
00:01:13:88.09792: Read from memory: 00000469=10 ()
00:01:13:88.09792: Reading from RAM: 0000046A=00 ( )
00:01:13:88.09800: Read from memory: 0000046A=00 ( )
00:01:13:88.09808: Reading from RAM: 0000046B=00 ( )
00:01:13:88.09808: Read from memory: 0000046B=00 ( )
00:01:13:88.09816: Reading from RAM: 0000046C=00 ( )
00:01:13:88.09824: Read from memory: 0000046C=00 ( )
00:01:13:88.09824: Reading from RAM: 0000046D=00 ( )
00:01:13:88.09832: Read from memory: 0000046D=00 ( )
00:01:13:88.09832: Reading from RAM: 0000046E=20 ( )
00:01:13:88.09840: Read from memory: 0000046E=20 ( )
00:01:13:88.09840: Reading from RAM: 0000046F=00 ( )
00:01:13:88.09848: Read from memory: 0000046F=00 ( )
00:01:13:88.09856: Reading from RAM: 00000470=04 ()
00:01:13:88.09856: Read from memory: 00000470=04 ()
00:01:13:88.09864: Reading from RAM: 00000471=00 ( )
00:01:13:88.09864: Read from memory: 00000471=00 ( )
00:01:13:88.09872: Reading from RAM: 00000472=20 ( )
00:01:13:88.09880: Read from memory: 00000472=20 ( )
00:01:13:88.09880: Reading from RAM: 00000473=00 ( )
00:01:13:88.09888: Read from memory: 00000473=00 ( )
00:01:13:88.09888: Reading from RAM: 00000474=0C ()
00:01:13:88.09896: Read from memory: 00000474=0C ()
00:01:13:88.09904: Reading from RAM: 00000475=00 ( )
00:01:13:88.09904: Read from memory: 00000475=00 ( )
00:01:13:88.09912: Reading from RAM: 00000476=40 (@)
00:01:13:88.09912: Read from memory: 00000476=40 (@)
00:01:13:88.09920: Reading from RAM: 00000477=00 ( )
00:01:13:88.09928: Read from memory: 00000477=00 ( )
00:01:13:88.09928: Checking for backlink to TSS 0048
00:01:13:88.09936: Marking incoming TSS 0058 busy if needed
00:01:13:88.09944: Reading from RAM: 000004FC=68 (h)
00:01:13:88.09944: Reading from RAM: 000004FD=00 ( )
00:01:13:88.09952: Reading from RAM: 000004FE=4C (L)
00:01:13:88.09952: Reading from RAM: 000004FF=04 ()
00:01:13:88.09960: Reading from RAM: 00000500=00 ( )
00:01:13:88.09968: Reading from RAM: 00000501=83 (ƒ)
00:01:13:88.09968: Reading from RAM: 00000502=00 ( )
00:01:13:88.09976: Reading from RAM: 00000503=00 ( )
00:01:13:88.09976: Writing to RAM: 000004FC=68 (h)
00:01:13:88.09984: Writing to RAM: 000004FD=00 ( )
00:01:13:88.09984: Writing to RAM: 000004FE=4C (L)
00:01:13:88.09992: Writing to RAM: 000004FF=04 ()
00:01:13:89.00000: Writing to RAM: 00000500=00 ( )
00:01:13:89.00000: Writing to RAM: 00000501=83 (ƒ)
00:01:13:89.00008: Writing to RAM: 00000502=00 ( )
00:01:13:89.00008: Writing to RAM: 00000503=00 ( )
00:01:13:89.00016: Loading incoming TSS 0058 state into the registers.
00:01:13:89.00024: Loading incoming TSS LDT 0040
00:01:13:89.00024: Reading from RAM: 000004E4=10 ()
00:01:13:89.00032: Reading from RAM: 000004E5=00 ( )
00:01:13:89.00048: Reading from RAM: 000004E6=10 ()
00:01:13:89.00048: Reading from RAM: 000004E7=04 ()
00:01:13:89.00056: Reading from RAM: 000004E8=00 ( )
00:01:13:89.00056: Reading from RAM: 000004E9=82 (‚)
00:01:13:89.00064: Reading from RAM: 000004EA=00 ( )
00:01:13:89.00072: Reading from RAM: 000004EB=00 ( )
00:01:13:89.00072: Setting Task Switched flag in CR0
00:01:17:86.06776: Loading incoming TSS CS register
00:01:17:86.06792: Reading from RAM: 00000400=29 ())
00:01:17:86.06792: Reading from RAM: 00000401=00 ( )
00:01:17:86.06800: Reading from RAM: 00000402=F1 (ñ)
00:01:17:86.06808: Reading from RAM: 00000403=A4 (¤)
00:01:17:86.06808: Reading from RAM: 00000404=0F ()
00:01:17:86.06816: Reading from RAM: 00000405=9A (š)
00:01:17:86.06824: Reading from RAM: 00000406=00 ( )
00:01:17:86.06824: Reading from RAM: 00000407=00 ( )
00:01:21:34.01888: Loading incoming TSS Stack address
00:01:21:34.01904: Reading from RAM: 000004C4=FF (ÿ)
00:01:21:34.01912: Reading from RAM: 000004C5=FF (ÿ)
00:01:21:34.01912: Reading from RAM: 000004C6=00 ( )
00:01:21:34.01920: Reading from RAM: 000004C7=10 ()
00:01:21:34.01920: Reading from RAM: 000004C8=00 ( )
00:01:21:34.01928: Reading from RAM: 000004C9=92 (’)
00:01:21:34.01944: Reading from RAM: 000004CA=00 ( )
00:01:21:34.01944: Reading from RAM: 000004CB=00 ( )
00:01:23:96.05264: Loading remaining TSS segment registers
00:01:23:96.05272: Reading from RAM: 00000408=03 ()
00:01:23:96.05280: Reading from RAM: 00000409=00 ( )
00:01:23:96.05288: Reading from RAM: 0000040A=00 ( )
00:01:23:96.05288: Reading from RAM: 0000040B=08 ()
00:01:23:96.05296: Reading from RAM: 0000040C=00 ( )
00:01:23:96.05296: Reading from RAM: 0000040D=92 (’)
00:01:23:96.05304: Reading from RAM: 0000040E=00 ( )
00:01:23:96.05312: Reading from RAM: 0000040F=00 ( )
00:01:23:96.05312: Reading from RAM: 000004C4=FF (ÿ)
00:01:23:96.05320: Reading from RAM: 000004C5=FF (ÿ)
00:01:23:96.05328: Reading from RAM: 000004C6=00 ( )
00:01:23:96.05328: Reading from RAM: 000004C7=10 ()
00:01:23:96.05344: Reading from RAM: 000004C8=00 ( )
00:01:23:96.05352: Reading from RAM: 000004C9=92 (’)
00:01:23:96.05360: Reading from RAM: 000004CA=00 ( )
00:01:23:96.05368: Reading from RAM: 000004CB=00 ( )
00:01:23:96.05376: New task ready for execution.
00:01:23:96.05384: 0004:001A (EA00008000)JMP 0080:0000
00:01:23:96.05424: EU&BIU cycles: 369, Operation cycles: 15, HW interrupt cycles: 0, Prefix cycles: 0, Exception cycles: 0, MMU read cycles: 132, MMU write cycles: 45, I/O bus cycles: 0, Prefetching cycles: 30, BIU prefetching cycles: 18
00:01:23:96.05432: Registers:
00:01:23:96.05432: AX: 0000, BX: 0000, CX: 0000, DX: 0000
00:01:23:96.05440: CS: 0004, DS: 000C, ES: 0020, SS: 0020, TR: 0048, LDTR:0038
00:01:23:96.05448: SP: 0800, BP: 1000, SI: 0000, DI: 0000
00:01:23:96.05456: IP: 001A, FLAGS: 4046
00:01:23:96.05456: CR0: FFF9
00:01:23:96.05464: GDTR: 0000000004A40098, IDTR: 00000000053C0180
00:01:23:96.05472: FLAGSINFO:c1P0a0Zstido00N0
00:01:23:96.05472: Interrupt status: 0000000000000000
00:01:23:96.05480: VGA@587,45(CRT:699,67)
00:01:23:96.05480: Display=832,246

00:01:23:96.08832: 0004:0000 (33DB)XORW BX,BX
00:01:23:96.08864: EU&BIU cycles: 3, Operation cycles: 3, HW interrupt cycles: 0, Prefix cycles: 0, Exception cycles: 0, MMU read cycles: 0, MMU write cycles: 0, I/O bus cycles: 0, Prefetching cycles: 3, BIU prefetching cycles: 3
00:01:23:96.08864: Registers:
00:01:23:96.08872: AX: 0000, BX: 0000, CX: 0000, DX: 0000
00:01:23:96.08880: CS: 0004, DS: 000C, ES: 0020, SS: 0020, TR: 0058, LDTR:0038
00:01:23:96.08880: SP: 0600, BP: 1000, SI: 0000, DI: 0000
00:01:23:96.08888: IP: 0000, FLAGS: 0002
00:01:23:96.08888: CR0: FFF9
00:01:23:96.08896: GDTR: 0000000004A40098, IDTR: 00000000053C0180
00:01:23:96.08904: FLAGSINFO:c1p0a0zstido00n0
00:01:23:96.08912: Interrupt status: 0000000000000000
00:01:23:96.08912: VGA@556,46(CRT:668,68)
00:01:23:96.08920: Display=832,246

This switches from task 0048(which has a Backlink&NT flag set) to task 0058(which loads an empty flags variable, which is forced from 0000h to 0002h(stuck bits)). That causes the NT flag to be 'reset'(actually because flags are reloaded from the TSS, because we're a different task now, initiated by a JMP(=leave current task(busy=0), start new task, not nested(task 0058)). Also, no backlink is set up(together with NT), because it's a JMP instruction that initiated the task switch(it needs to be a CALL instruction in order to do that, according to the documentation).

Could it be that the program expects the task information(Backlink&flags) to be copied over to the new task for some reason? JMP task switches shouldn't do that? Only CALL task switches allow returning by IRET to the previous task(unless already set during a previous task switch)?

Edit: See also, https://xem.github.io/minix86/manual/intel-x8 … 80e0ce-250.html chapter 7.4 TASK LINKING. So 32-bit task gates in the IDT execute task switches like a CALL to the selected task. Just implemented that variant of task switches(thus allowing IRET from an interrupt with task switch to return to the interrupted task).
Although, unfortunately that won't fix the problem in the Supersoft BIOS and unknown problems with Windows 3.0 and Day of The Tentacle. Anyone can see in the source code what's going wrong?

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

Reply 53 of 69, by crazyc

User metadata
Rank Member
Rank
Member
SoftPCMuseum_ wrote:

What program was it that used 80286-style "Task State Segment" descriptors? Was it an operating system running in Virtual 8086 Mode or Protected Mode (such as Windows/386 and higher or OS/2), or was it something else (such as a debugger)?

The Ergo 286 DOS extender used in Turbo Debugger 286 (TD286.EXE).

Reply 54 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Well, the current problem is that I have no idea at all what's going wrong with the 286+ emulation. For some reason the applications just end up in an infinite loop. So maybe there's a (E)FLAGS problem?

CPU flags: https://bitbucket.org/superfury/unipcemu/src/ … ags.c?at=master
ModR/M handling: https://bitbucket.org/superfury/unipcemu/src/ … drm.c?at=master
8086+ instructions: https://bitbucket.org/superfury/unipcemu/src/ … 086.c?at=master
80186+ instructions: https://bitbucket.org/superfury/unipcemu/src/ … V30.c?at=master
80286+ instructions: https://bitbucket.org/superfury/unipcemu/src/ … 286.c?at=master
General CPU core: https://bitbucket.org/superfury/unipcemu/src/ … cpu.c?at=master
CPU timings and instruction parameter handling: https://bitbucket.org/superfury/unipcemu/src/ … ngs.c?at=master
Interrupt handling: https://bitbucket.org/superfury/unipcemu/src/ … pts.c?at=master
Basic protected mode handling: https://bitbucket.org/superfury/unipcemu/src/ … ion.c?at=master
Protected mode multitasking handling: https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master
80386+ paging: https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master

Anyone can see what's going wrong? Afaik Windows 3.0a and DOTT don't even get into protected mode yet, so the problem is still in the real mode emulation.

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

Reply 55 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've improved the 80386 core, by implementing most instructions (all 80186+ and 80286+ opcodes in 32-bits variants) from the older CPUs in 32-bit format. I've also been fixing the timings lookup table to work properly now, instead of messing up CPU generation(186, 286, 386, 486, 586(Pentium)) vs operand size(16-bit vs 32-bit)), now giving priority to 32-bit instructions overriding 32-bit instructions and 16-bit instructions overriding their 16-bit instruction variants, instead of 32-bit instructions overriding 16-bits instructions. Also the 0F jumptable used before has been combined during initialization with the normal jumptable, to lookup faster.

Most of the 80386 instructions have been implemented(CRn/DRn register loads only checks for CPL in protected mode, the bits written to the registers are unchecked currently). Only a few (0F prefixed) instructions are left:
- BT
- SHLD(3 operands?)
- BTS
- SHRD(See SHLD)
- IMUL
- BTR
- opcode BA(which seems to be a BT* GRP opcode? Contains BT, BTS, BTR and BTC)
- BTC
- BSF
- BSR

Those are all that's left to make my 80386 emulation theoretically(according to basic documentation) almost fully 386+ functional(except support for CR4+ registers, Debugger registers(not handled at all) and Test registers).

Edit: The CPU now deadlocks itself(Windows 3.0 setup on 80386 emulation): it seems to execute a HLT instruction with the Interrupt Flag disabled.

No unimplemented 386+ 0F instructions are called.
Register state:

CS:IP=0000:003D; OP:06 ROP:F4(HLT)
DS:0000; ES:0000
SS:1EC9
FS:0000; GS:0000
AX:83BE; BX:A700
CX: 422B; DX:A400
SP:0968; BP:099E
SI:43F4; DI:43F4
CR0:0000
GDTR: FFFF; IDTR: 3FF
Flags: 7806

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

Reply 56 of 69, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

So CS DPL is checked against CS RPL and SS DPL is checked against SS RPL. But what about CPL? What is it based on and when is it reloaded?

CPL is the name of the DPL in SS and CS (and they're supposed to be the same value). It is set when the selector is loaded if checks pass.
Calls check SS first, because stack might be modified before CS is loaded. Jumps check CS alone.
The DPL is the name for other selectors, and is checked on access to that selector.

Reply 57 of 69, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:
Most of the 80386 instructions have been implemented(CRn/DRn register loads only checks for CPL in protected mode, the bits writ […]
Show full quote

Most of the 80386 instructions have been implemented(CRn/DRn register loads only checks for CPL in protected mode, the bits written to the registers are unchecked currently). Only a few (0F prefixed) instructions are left:
- BT
- SHLD(3 operands?)
- BTS
- SHRD(See SHLD)
- IMUL
- BTR
- opcode BA(which seems to be a BT* GRP opcode? Contains BT, BTS, BTR and BTC)
- BTC
- BSF
- BSR

SHLD/SHRD have three parameters - dest register, source r/m (if dest and source are the same register then some disassemblers will hide the source), and count.
BA has all four BT* instructions in /4, /5, /6, and /7. Use of /0-3 triggers exceptions.

Reply 58 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just finished implementing the remaining instructions and their debugging data. Now all 80386+ instructions are emulated and usable. Now the question is: why is the Windows 3.0 setup clearing the interrupt flag and entering halted state(deadlocking the CPU until external reset, which will never happen)?

Edit: Just found a bug: the memory writes of 32-bits size(e.g. the CPU writing 32-bits quantities for any kind of operation to memory(stack writes, normal instructions etc.)) were truncated to 16-bits before calling the used 16-bits handler to write the 16-bit quantities(low 16-bit word and high 16-bit word of the 32-bit word), thus causing the upper 16 bits of any 32-bit value pushed on the stack(or written normally to memory) to be truncated to 16-bits, but written to memory as 32-bits(e.g. zero-extending the low 16-bits to 32-bits). That's probably one of the causes to CPU misdetection(whichcpu detected it as a RapidCAD CPU(which is a 80486, not a 80386)).

Edit: Having fixed this bug, it seems that the Windows 3.0 setup wizard detects a CPU(don't know if it's 80386 yet), checks for LIM EMS, seems to be loading some default descriptor tables(I see values being loaded in adjacent memory locations that resemble the format of the 80286 memory descriptors used by the IBM AT BIOS, with limits of 0xFFFF at a relative address of +0000h and various other data). I also see it using registers like EBX while executing, confirming it's actually detected a 80386+ CPU(otherwise it wouldn't use that instruction).

WhichCPU still detects a RapidCAD for some reason?

CPU found:  : RapidCAD
NPU found : build in

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

Edit: Just tried running the kernel.exe in the Windows SYSTEM directory: my breakpoint that's used during the setup(of Windows 3.0) triggers at the exact same location. So something in kernel.exe is going wrong(Since it's the one that's going wrong when installing Windows 3.0)? Now the question: what's going wrong(the 80386 is using the 80286 instruction timings, except for the new instructions, which take 8 cycles instead. 32-bit timings are currently the same as the 16-bit timings(due to 80286 timing fallback))?

The cause of the RapidCAD detection is apparently that the assumption CMP;JE vs LOOP speed differences, which aren't implemented for the 80386 yet(it's still using 80286 timings after all). Otherwise, it would detect a 80386(SX or DX).

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

Reply 59 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is the log of the kernel.exe(manually started from the MS-DOS prompt) running until the emulator terminates because of the #UD opcodes:
https://www.dropbox.com/s/y9aq35e8idcci2u/deb … 5_1328.zip?dl=0

Anyone can see what's going wrong? Why is the kernel.exe crashing my 80386 emulation(because of undefined instructions, apparently caused by opcode 44h)?

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