208 lines
10 KiB
Markdown
208 lines
10 KiB
Markdown
---
|
|
layout: post
|
|
title: Windows 95
|
|
date: 2015-07-17 11:00:00
|
|
category: Windows 95
|
|
permalink: /blog/2015/07/17/
|
|
machines:
|
|
- id: deskpro386
|
|
type: pcx86
|
|
debugger: true
|
|
state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
|
|
config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/debugger/machine.xml
|
|
drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
|
|
autoMount:
|
|
A:
|
|
name: None
|
|
B:
|
|
name: None
|
|
---
|
|
|
|
This week (July 14, 2015) was the 20th anniversary of Windows 95 RTM ("Release To Manufacturing"). So I decided to
|
|
throw a PCjs party and try running Windows 95 Setup inside a PCx86 machine for the first time.
|
|
|
|
Sadly, it immediately failed:
|
|
|
|
Please wait while Setup initializes.
|
|
Windows requires a computer with an 80386 processor or higher.
|
|
|
|
The failing code:
|
|
|
|
0E36:08FD 06 PUSH ES
|
|
0E36:08FE 1E PUSH DS
|
|
0E36:08FF 9C PUSHF
|
|
0E36:0900 33C0 XOR AX,AX
|
|
0E36:0902 50 PUSH AX
|
|
0E36:0903 9D POPF
|
|
0E36:0904 9C PUSHF
|
|
0E36:0905 58 POP AX
|
|
0E36:0906 A90080 TEST AX,8000
|
|
0E36:0909 7517 JNZ 0922
|
|
0E36:090B B80070 MOV AX,7000
|
|
0E36:090E 50 PUSH AX
|
|
0E36:090F 9D POPF
|
|
0E36:0910 FB STI
|
|
0E36:0911 9C PUSHF
|
|
0E36:0912 58 POP AX
|
|
0E36:0913 A90070 TEST AX,7000
|
|
0E36:0916 7405 JZ 091D
|
|
0E36:0918 B88603 MOV AX,0386
|
|
0E36:091B EB08 JMP 0925
|
|
0E36:091D B88602 MOV AX,0286
|
|
0E36:0920 EB03 JMP 0925
|
|
0E36:0922 B88600 MOV AX,0086
|
|
0E36:0925 9D POPF
|
|
0E36:0926 1F POP DS
|
|
0E36:0927 07 POP ES
|
|
0E36:0928 C3 RET
|
|
|
|
was easily fixed with a change to [x86cpu.js](/modules/pcx86/lib/x86cpu.js), allowing the IOPL bits to be
|
|
modified in real-mode on an 80386. When I had previously tweaked setPS() to accomodate 80286/80386 discrimination
|
|
logic in OS/2 1.0, there was no 80386 support in PCx86 at that time, so it was sufficient to *never* allow the
|
|
IOPL bits to be altered in real-mode.
|
|
|
|
The next problem was triggered by Setup's CAB ("Diamond") decompression code, which uses all 32 bits
|
|
of the 80386's 32-bit registers. That in itself was not a problem, but by leaving stray bits in the upper halves of
|
|
registers like EDX, it exposed a bug in the PCx86 I/O instruction handlers, which neglected to mask EDX with 0xFFFF before
|
|
performing port lookups, causing mysterious I/O failures that usually manifested themselves as hard disk I/O errors.
|
|
|
|
Then PCx86 crashed in the middle of the decompression of the first CAB file (MINI.CAB). It turned out the stack
|
|
had been improperly adjusted because a "RETF n" instruction mistakenly believed that a stack switch had occurred.
|
|
This was, in fact, a left-over condition from a protected-mode stack-switch. That was easily cleared.
|
|
|
|
Next, I discovered that I had never finished updating a handful of instructions to full 32-bit operation;
|
|
namely, INC, DEC, NEG, NOT, TEST, MOVSB and MOVSW. Those are done now.
|
|
|
|
Then I ran into a couple of Windows 95 oddities. First, some kernel initialization code deliberately
|
|
executed an invalid opcode (0x0F,0xFF) and expected its DPMI exception (0x06) handler to field the exception; that was
|
|
fixed by flagging the opcode as genuinely invalid.
|
|
|
|
> SIDEBAR
|
|
|
|
> By default, PCx86 marks opcodes as invalid *only* if they have been confirmed invalid. PCx86 considers the vast
|
|
majority of unused/undocumented opcodes to be merely "undefined" until I've seen them in the wild. An instruction will
|
|
be marked invalid if I either discover that real hardware treats it as an Invalid Opcode (by throwing the exception)
|
|
*or* that real software expects it to. For opcode 0x0F,0xFF, it was the latter.
|
|
|
|
> Don't be confused that Intel also refers to the Invalid Opcode exception (0x06) as the #UD ("Undefined")
|
|
opcode exception. Every opcode that triggers that exception is, by definition, defined: it's defined as invalid.
|
|
I consider it a misnomer to refer to any invalid instruction as "undefined".
|
|
|
|
The other recent Windows 95 oddity I ran into was an instruction with multiple address-override (0x67) prefixes; the
|
|
first prefix changed the instruction's addressing mode from 16-bit to 32-bit, and the second prefix changed it back to
|
|
16-bit. PCx86 should have simply ignored the second prefix.
|
|
|
|
With all of the above changes in place, PCx86 v1.18.4 is able to run Windows 95 Setup a bit farther, but still far from
|
|
completion. If you want to give it a spin yourself, start the machine below (click the **Run** button) and once it has
|
|
finished booting, run SETUP from drive B, where the first Windows 95 diskette is already loaded.
|
|
|
|
If, when it crashes (and it will), you're interested in examining the instructions that were executed prior to the
|
|
crash, you can dump the instruction history buffer using the Debugger's "dh" command.
|
|
|
|
NOTE: The diskette images contain a pre-release version of Windows 95, as I don't currently have the RTM version on
|
|
diskette.
|
|
|
|
---
|
|
|
|
August 13, 2015 Update
|
|
---
|
|
|
|
PCx86 v1.18.8 has made a little more progress running Windows 95 Setup, but CAB decompression still fails almost
|
|
immediately. To monitor DOS calls until the first 36-byte read of PRECOPY1.CAB, try setting the following
|
|
breakpoint and then starting the machine, using the PCx86 Debugger *input* field next to the **Enter** button:
|
|
|
|
m dos off
|
|
bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"
|
|
g
|
|
|
|
Alternatively, you can hard-code those commands into the Debugger component of the machine.xml file; eg:
|
|
|
|
```xml
|
|
<debugger id="debugger" messages="fault|tss|int" commands='m dos off;bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"'/>
|
|
```
|
|
|
|
Once the 36-byte read is hit, you'll probably want to stop on the next instruction that examine those bytes,
|
|
by using a memory read breakpoint:
|
|
|
|
br ds:dx
|
|
|
|
and you also might want to change the first breakpoint to stop on *any* file read, and add a second breakpoint
|
|
to dump the initial contents of those reads:
|
|
|
|
bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f"
|
|
bp FDC8:422A "if fn==3f;di ds:dx;db ds:dx;h;else"
|
|
|
|
In the current machine, `1ED4:16B4` is the DOS INT 0x21 entry point and `FDC8:422A` is the corresponding IRET.
|
|
The first breakpoint sets an internal variable, `fn`, to the value of the **AH** register on entry, so that the
|
|
second breakpoint can check the value on exit.
|
|
|
|
Regarding the other Debugger commands shown above, the `dos` command describes the current DOS operation
|
|
(alternatively, you could use the `m int on; m dos on` commands to turn on DOS interrupt messages). The `di`
|
|
command dumps PCx86 BACKTRACK(tm) information, to help you visually confirm which INT 0x21 calls are reading
|
|
PRECOPY1.CAB. Finally, the `if` command evaluates the given expression, and if the result is non-zero ("true"),
|
|
all subsequent commands are executed, up to to any `else` command; otherwise, only commands after the `else`
|
|
command will be executed, and if there is no `else` command, execution will stop.
|
|
|
|
Debugger expressions may contain the usual variety of arithmetic, bitwise and logical binary operators, and they
|
|
are evaluated using traditional operator precedence (ie, the same as C or JavaScript); any other operators, such as
|
|
parentheses, assignment operators, and unary or ternary operators, are not supported in expressions.
|
|
|
|
This test machine below has been updated to load WDEB386.EXE prior to starting B:SETUP.EXE, if you prefer using
|
|
WDEB386. Make sure the machine is running (ie, click the **Run** button, or use the PCx86 Debugger "g" command),
|
|
and then click on the Debugger *output* control to give it focus and press CTRL-C to trigger WDEB386.
|
|
|
|
The Debugger *input* field is used exclusively for PCx86 Debugger commands, whereas the *output* textarea combines
|
|
all Debugger output *and* WDEB386 COM2 serial port I/O. You can even use the PCx86 Debugger to debug
|
|
the WDEB386 debugger; just make sure the appropriate text control has focus before typing a command.
|
|
|
|
To help reduce confusion, the PCx86 Debugger displays a double-character command prefix, to differentiate its commands
|
|
from WDEB386's single-character command prompt -- but it's still easy to get confused.
|
|
|
|
A quick recap of those command prefixes (which you won't see until AFTER you've typed a PCx86 command):
|
|
|
|
* `>>` indicates real-mode
|
|
* `##` indicates protected-mode
|
|
* `--` indicates V86-mode
|
|
|
|
80386 Debug register (DR0-DR7) support was recently added, so even WDEB386 read/write breakpoints should work now.
|
|
|
|
---
|
|
|
|
August 21, 2015 Update
|
|
---
|
|
|
|
PCx86 v1.19.1 has finally solved a number of nagging bugs. The CAB decompression code itself was running
|
|
fine; it would crash after loading a 32-bit value into EAX *and* a timer interrupt occurred. A path
|
|
through the interrupt handler was trashing the upper bits of EAX. The culprit: any of the MOV instructions
|
|
that move an immediate value into one of the "high" 8-bit registers (AH, BH, CH, or DH). Those instructions
|
|
were failing to preserve the upper 16 bits of the entire register. Further proof that the most exasperating
|
|
bugs sometimes have the most mundane causes.
|
|
|
|
Also recently fixed: the annoying VGA video glitch that would occur whenever the mouse was moved, improper
|
|
updates to the ACCESSED bit in a selector's descriptor table entry, improper error codes when a selector load
|
|
generated a fault, and the RETF instruction's failure to properly restart when the return address referred
|
|
to a not-present segment.
|
|
|
|
The next problem appears to be timer-related. When Windows 95 Setup begins its hardware analysis, it
|
|
gets "stuck" in code that's reading and writing timer ports (0x40 and 0x43). Turning on timer port messages
|
|
with the `"m port on;m timer on"` Debugger commands reveals that the problem maybe an unsupported timer
|
|
command:
|
|
|
|
chipset.outPort(0x0043,PIT1_CTRL,0xD2) @1847:DD02
|
|
PIT1_CTRL: Read-Back command not supported (yet)
|
|
|
|
---
|
|
|
|
September 12, 2015 Update
|
|
---
|
|
|
|
Lots of bugs have been squashed in the past few weeks -- not enough to finish setting up Windows 95, but it's getting
|
|
closer. More details on recent releases can be found [here](https://github.com/jeffpar/pcjs/releases).
|
|
|
|
The machine below has been reconfigured with a hard disk image containing all the Windows 95 Setup files. The machine
|
|
is at the point where Windows 95 SETUP has just rebooted.
|
|
|
|
{% include machine.html id="deskpro386" %}
|
|
|
|
*[@jeffpar](http://twitter.com/jeffpar)*
|
|
*July 17, 2015 (updated September 12, 2015)*
|