VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

When I try to run software other than the basic IBM AT BIOS starting up or running 80(1)86 MS-DOS software, it will end up at strange points in the executable code. Windows 95 setup(on the 80386+ emulation) will simply end up with infinite General Protection faults on an REP MOVSD instruction. The same kind of errors happen when I try to run Day of The Tentacle(DOTT.EXE, when starting the game from it's MS-DOS launcher).

Memory accesses itself:
MMU: https://bitbucket.org/superfury/unipcemu/src/ … mmu.c?at=master

Support for modr/m data and flags:
ModR/M: https://bitbucket.org/superfury/unipcemu/src/ … drm.c?at=master
Flags: https://bitbucket.org/superfury/unipcemu/src/ … ags.c?at=master

Opcodes themselves:
80286 opcodes: https://bitbucket.org/superfury/unipcemu/src/ … 286.c?at=master
80386 opcodes: https://bitbucket.org/superfury/unipcemu/src/ … 386.c?at=master
80386 0F opcodes: https://bitbucket.org/superfury/unipcemu/src/ … 386.c?at=master

Remaining protected mode functionality:
Paging: https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master
Protected mode debugger: https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master
Protected mode basics: https://bitbucket.org/superfury/unipcemu/src/ … ion.c?at=master
Multitasking(task switching itself to be exact): https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master

The main functions involved in protected mode are:
Paging: is_paging, isvalidpage, mappage for paging translation and exception checking.
Multitasking: CPU_switchtask, CPU_TSSFault for task switching.
Protected mode debuggger: checkProtectedModeDebugger, checkProtectedModeDebuggerAfter for Protected Mode debugging using the debugger registers.
Protected mode basics: segmentWritten(for all segment register loading), CPU_MMU_start(for the start address of a segment register in linear memory), CPU_MMU_checklimit for memory checks in protected or other memory modes(when accessing memory using segments), checkSTICLI, disallowPOPFI, checkPortRights, CPU_ProtectedModeInterrupt(for protected mode interrupt execution).

The remaining given units handle different part of normal execution within the CPU(flags, opcodes, ModR/M decoding and access using the BIU).

Anyone can see if there are any errors with my handling of the protected-mode specific features? Up until the 80286 have been adjustted to use the BIU to access memory and BUS. The 80386 opcodes and extensions still uses them directly(without taking care of any BUS timings by using the BIU, atm).

Last edited by superfury on 2017-05-12, 14:24. 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 1 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just implemented proper support for 16-bit and 32-bit BIU. Now the SuperSoft/Landmark BIOS(from minuszerodegrees) halts during the protected mode test(passing all other tests, except before display is filled it still gives audio tones signifying hardware problems not showed on screen. Those are probably the faulty error codes being reported(which shouldn't be errors at all)). The Protected mode test keeps reporting a high/low tone infinitely(instead of high/low, low etc...).

The message on the start is:
6 high/low, low(cannot initialize monitor).
6 high/low, 2 low(unknown according to manual, maybe see above).
screen enabled
5 high low, 8 low(cannot initialize monitor).
starts running logic tests(U74 CPU registers and logic), passing all tests until protected mode CPU test
Protected mode gives infinite high low tests, no passed or any error code is displayed.

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

Reply 2 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

Anyone can see what's going wrong with the protected mode emulation? Mainly look at Protected mode basics and maybe Paging.

Anyone can see what's going wrong? Jepael?

Edit: The AT BIOS crashes on this now. It reports POST codes up until 1E, 1F, 20, then resets completely, starting at 01, 02 and then(in 02) H(A)LTs permanently. So it detects memory in protected mode, then crashes after resetting(whether intentional or not. No triple fault is generated, so it should be intentional).

Edit: The CMOS is cleared and memory reduced to 4MB(to prevent the cause of corrupted CMOS and too much memory). The problem with it resetting up until it reaches POST code 02h still persists?

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

Reply 3 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

After running the 80186 test suite again, I've found at least one problem with the flags handling: The Auxiliary flag isn't correctly updated? The emulation sets it when it's supposed to be cleared, according to the result file? This at least happens with the GRP2 SHL/SHR opcodes?

8086 opcodes

general flag calculations

Edit: Looking at Dosbox's Auxiliary/Adjust flags:
- SHL/SHR/SAR always set with non-zero shift, cleared otherwise.
- NEG sets when result low 4 bits not zero, cleared otherwise(essentially negated zero flag for low 4 bits).
- INC sets when result low 4 bits is zero, cleared otherwise.
- DEC sets when result low 4 bits is 1111b(0xF), cleared otherwise.

All other instructions use a simple source XOR dest XOR result AND 10h operation to get the Auxiliary flag?

Is that behaviour correct?

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

Reply 4 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

It seems still some problems occur with the shift/rotate instructions. Anyone can see what's going wrong?

8086+ 8/16-bit version:

byte op_grp2_8(byte cnt, byte varshift) {
//word d,
INLINEREGISTER word s, shift, oldCF, msb;
word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
s = oper1b;
oldCF = FLAG_CF;
if (EMULATED_CPU >= CPU_NECV30) cnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
switch (thereg) {
case 0: //ROL r/m8
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | FLAG_CF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 7) & 1));
break;

case 1: //ROR r/m8
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | (FLAG_CF << 7);
}
if (cnt==1) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1));
break;

case 2: //RCL r/m8
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | oldCF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 7) & 1));
break;

case 3: //RCR r/m8
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
FLAGW_CF(s & 1);
s = (s >> 1) | (oldCF << 7);
}
if (cnt==1) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1));
break;

case 4: case 6: //SHL r/m8
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
}
if (cnt==1) FLAGW_OF((FLAG_CF ^ (s >> 7)));
flag_szp8((uint8_t)(s&0xFF)); break;

case 5: //SHR r/m8
if (cnt == 1) FLAGW_OF((s & 0x80) ? 1 : 0); else FLAGW_OF(0);
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
Show last 151 lines
			backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
flag_szp8((uint8_t)(s & 0xFF)); break;

case 7: //SAR r/m8
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!cnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (cnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_s8(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (cnt) //Anything shifted at all?
{
flag_s8(s); //Affect sign as well!
}
if ((EMULATED_CPU>=CPU_NECV30) && cnt) //NECV20+ affected?
{
flag_p8(s); //Affect parity as well!
}
break;
}
op_grp2_cycles(cnt, varshift);
return(s & 0xFF);
}

word op_grp2_16(byte cnt, byte varshift) {
//uint32_t d,
INLINEREGISTER uint_32 s, shift, oldCF, msb;
//if (cnt>0x10) return(oper1); //NEC V20/V30+ limits shift count
if (EMULATED_CPU >= CPU_NECV30) cnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
word backup;
s = oper1;
oldCF = FLAG_CF;
switch (thereg) {
case 0: //ROL r/m16
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | FLAG_CF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 15) & 1));
break;

case 1: //ROR r/m16
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | (FLAG_CF << 15);
}
if (cnt==1) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1));
break;

case 2: //RCL r/m16
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | oldCF;
//oldCF = ((s&0x8000)>>15)&1; //Save FLAG_CF!
//s = (s<<1)+FLAG_CF;
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 15) & 1));
break;

case 3: //RCR r/m16
if (cnt==1) FLAGW_OF(((s >> 15) & 1) ^ FLAG_CF);
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
FLAGW_CF(s & 1);
s = (s >> 1) | (oldCF << 15);
//oldCF = s&1;
//s = (s<<1)+(FLAG_CF<<16);
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1));
break;

case 4: case 6: //SHL r/m16
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if ((cnt) && (FLAG_CF == (s >> 15))) FLAGW_OF(0); else FLAGW_OF(1);
flag_szp16(s); break;

case 5: //SHR r/m16
if (cnt) FLAGW_OF((s & 0x8000) ? 1 : 0);
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
flag_szp16(s); break;

case 7: //SAR r/m16
msb = s & 0x8000; //Read the MSB!
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp16(s);*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!cnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (cnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_s16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (cnt) //Anything shifted at all?
{
flag_s16(s); //Affect sign as well!
}
if ((EMULATED_CPU>=CPU_NECV30) && cnt) //NECV20+ affected?
{
flag_p16(s); //Affect parity as well!
flag_s16(s); //Affect sign as well!
}
break;
}
op_grp2_cycles(cnt, varshift|4);
return(s & 0xFFFF);
}

32-bit version of the 16-bit version:

uint_32 op_grp2_32(byte cnt, byte varshift) {
//uint32_t d,
INLINEREGISTER uint_32 s, shift, oldCF, msb;
//if (cnt>0x10) return(oper1d); //NEC V20/V30+ limits shift count
if (EMULATED_CPU >= CPU_NECV30) cnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
s = oper1d;
oldCF = FLAG_CF;
switch (thereg) {
case 0: //ROL r/m32
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | FLAG_CF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 31) & 1));
break;

case 1: //ROR r/m32
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | (FLAG_CF << 31);
}
if (cnt==1) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | oldCF;
//oldCF = ((s&0x8000)>>15)&1; //Save FLAG_CF!
//s = (s<<1)+FLAG_CF;
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 31) & 1));
break;

case 3: //RCR r/m32
if (cnt==1) FLAGW_OF(((s >> 31) & 1) ^ FLAG_CF);
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
FLAGW_CF(s & 1);
s = (s >> 1) | (oldCF << 31);
//oldCF = s&1;
//s = (s<<1)+(FLAG_CF<<32);
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 4: case 6: //SHL r/m32
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
s = (s << 1) & 0xFFFFFFFF;
//FLAGW_AF(1); //Auxiliary carry?
}
if ((cnt==1) && (FLAG_CF == (s >> 31))) FLAGW_OF(0); else FLAGW_OF(1);
flag_szp32(s); break;
Show last 46 lines

case 5: //SHR r/m32
if (cnt==1) FLAGW_OF((s & 0x80000000) ? 1 : 0);
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = s >> 1;
//FLAGW_AF(1); //Auxiliary carry?
}
flag_szp32(s); break;

case 7: //SAR r/m32
msb = s & 0x80000000; //Read the MSB!
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | msb;
//FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp32(s);*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!cnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (cnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_s32(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (cnt) //Anything shifted at all?
{
flag_s32(s); //Affect sign as well!
}
if ((EMULATED_CPU>=CPU_NECV30) && cnt) //NECV20+ affected?
{
flag_p32(s); //Affect parity as well!
flag_s32(s); //Affect sign as well!
}
break;
}
op_grp2_cycles32(cnt, varshift|4);
return(s & 0xFFFFFFFF);
}

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

Reply 5 of 9, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

- SHL/SHR/SAR always set with non-zero shift, cleared otherwise.

Yes.

- NEG sets when result low 4 bits not zero, cleared otherwise(essentially negated zero flag for low 4 bits).

The flags result is equivalent to performing "0 SUB value".

- INC sets when result low 4 bits is zero, cleared otherwise.

The flags result is equivalent to performing "value ADD 1", but clearing CF afterwards.

- DEC sets when result low 4 bits is 1111b(0xF), cleared otherwise.

The flags result is equivalent to performing "value sub 1", but clearing CF afterwards.

Reply 6 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

My code currently doesn't clear CF afterwards with INC/DEC instructions, but instead sets it to the value before the INC/DEC executed(i.e. it's unchanged during execution of the instruction, neither cleared nor set). I've removed the manual 0xF(dec) and 0x0(inc) checks on the low 4 bits, because this is already handled by the ADD/SUB flags calculation it's using.

Is that correct behaviour?

Strangely enough, when I make SHL/SHR/SAR set the Auxiliary carry flag to 1(and clear it when nothing's shifted) with non-zero shifts, the testsuite errors out for some reason, saying the flags result is incorrect? It's the EPC 80186 testsuite I'm using.

Is there a difference between ADD and SUB instructions calculating the Auxiliary carry flag? Or are both supposed to use ((src XOR dest XOR result) AND 0x10), so source xor dest xor result, then take bit 4 as the Auxiliary carry flag?

One difference between the 80286 and 80386 cores currently, is that the 80386 (all 32-bit instructions and new instructions) core still use the old way of directly handling memory and I/O accesses. So they won't run through the BIU to handle all memory accesses, causing incorrect timing when using them. All 80286- instructions should be handled correctly(although there's errors somewhere, causing the IBM AT BIOS to crash when performing the very first CPU reset(directly after detecting extended memory and resetting the CPU, performing stage 01h and 02h(BIOS Diagnostic card output) then hanging at stage 02h for some reason.

The 80386 on XT(emulating the Inboard 386/PC hardware) emulation iNBRDPC.SYS errors out saying all extended memory(3072K, as it has 4MB memory installed, with 640K(correct) base memory) is bad for some reason?

It's emulating a 80386SX to be exact.

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

Reply 7 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've fixed some of the bugs in the 80286/80386 emulation. The trouble starts when using task switching/interrupts in the test BIOS(Diagnostics from Supersoft/Landmark).

It eventually returns to an available task, then executes IRET, which fails due to an #GP fault because all return addresses(normal interrupt return, nothing above the low 8 bits in the EFLAGS being set) from the stack seem to contain invalid data(all 0x55AA values at SS:SP, which is 20h:600h. The same applies to all other values read from the stack(IP, CS, FLAGS).

Can anyone see what's going wrong during task switching? Look at https://bitbucket.org/superfury/unipcemu/src/ … ion.c?at=master segmentWritten, protectedmodeInterrupt, as well as CPU_IRET in https://bitbucket.org/superfury/unipcemu/src/ … pts.c?at=master

What happens during call instructions after a task switch by a call to a task gate/TSS? Is anything pushed on the new stack?

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

Reply 8 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

Checking the documentation again, I might have found the problem: it was storing SS:(E)SP during leaving an outgoing task, but reentering it through a different privilege level task was causing it to load the current privilege level(SS0-2:(E)SP0-2) instead, which will change state incorrectly. This has now been fixed.

Edit: It seems at some point, it wants to return somewhere, but the state is somehow invalid? It's faulting(which it shouldn't) at task 0x58's IRET instruction(returning to 55AA:55AA with FLAGS 55AA on the stack, stack address being 0020:0600 before the POP(16-bit stack, due to being a 80286). The task switching itself has been fixed, but it somehow wants to return to some caller that's apparently not there?

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

Reply 9 of 9, by superfury

User metadata
Rank l33t++
Rank
l33t++

The first task(the main task) seems to be at linear address 0x486 in memory. This seems to be task 0x0068 when initialized. It switches the second task using a CALL instruction. It switches to task 0x0048, marking task 0x0068 idle. Then it sets the Nested Task flag(to indicate it's to return to task 0x0068). That task eventually jumps to task 0x0058(located at linear address 0x42F), leaving task 0x0068 idle, with no way to return to task 0x0048(if I'm not mistaken), except by a knowingly JMP.

https://www.dropbox.com/s/7i71ktnchxqqkc6/deb … invalid.7z?dl=0

Strangely enough, I see no trace of any POPF instruction to change this behaviour, so could it be that it's supposed to have something stored when switching to task 0x0058? Anyone can see what's going wrong(warning: it's a 5GB big text file).

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