Blog updates
This commit is contained in:
parent
312c41c68a
commit
abb77fe5c9
8 changed files with 131 additions and 45 deletions
|
|
@ -1,136 +0,0 @@
|
|||
Debugging the IBM VGA ROM
|
||||
---
|
||||
|
||||
The IBM VGA ("Video Graphics Array") standard was introduced as part of the IBM PS/2 line of computers;
|
||||
it was not a feature you could purchase or install in older PC, XT or AT-compatible machines. In fact, full VGA
|
||||
support was not even available in all PS/2 models. Of the first four PS/2 models -- the 8086-based Model 30, the
|
||||
80286-based Model 50 and Model 60, and the 80386-based Model 80 -- VGA support was available only in the three
|
||||
higher-end models. The Model 30 came with MCGA ("Multicolor Graphics Array") video hardware that supported a subset
|
||||
of VGA modes (eg, 640x480 2-color and 320x200 256-color graphics).
|
||||
|
||||
It wasn't until October 1987 that IBM finally introduced an 8-bit ISA card that brought VGA capability to older PCs.
|
||||
The card was called the **IBM PS/2 Display Adapter**. However, I think the name is a bit confusing, since the card
|
||||
could only be used in PC, XT, and AT-compatible systems. I'll refer to it here simply as the IBM VGA.
|
||||
|
||||
The VGA ROM used here is assumed to have come from an original IBM VGA. It's unknown if IBM ever made any
|
||||
revisions to the VGA ROM. With the introduction of the PS/2 family and the VGA, IBM decided to no longer publish
|
||||
the source code for its ROMs, so I've created some assemblable source code from the IBM VGA ROM
|
||||
[here](/devices/pc/video/ibm/vga/ibm-vga.nasm).
|
||||
|
||||
I've finally started debugging a machine configuration that uses the IBM VGA ROM. Since the VGA and the 80386 are
|
||||
contemporaries, I'm using an [80386 machine configuration](/devices/pc/machine/compaq/deskpro386/vga/2048kb/machine.xml).
|
||||
However, I don't expect the IBM VGA ROM to require any 80386 support or PS/2-specific features.
|
||||
|
||||
The first problem I ran into was here:
|
||||
|
||||
;
|
||||
; Initialize the ROM BIOS Video Mode Options byte @40:0087 (default to color and 256Kb of RAM)
|
||||
;
|
||||
mov byte [0x487],0x60 ; 0000008D
|
||||
;
|
||||
; The x100 subroutine alternately enables port 0x3B? and 0x3D? decoding, verifying that there is
|
||||
; no response on opposing ports 0x3D? and 0x3B?, respectively; otherwise, it assumes that another
|
||||
; video card must exist and attempts to select co-existing settings for the VGA. For example, if
|
||||
; there is an unexpected response on the color ports, the VGA ROM will default to mono operation.
|
||||
;
|
||||
call x100 ; 00000092
|
||||
|
||||
The Video component installs I/O port handlers for all possible I/O ranges; when a range isn't being used, the
|
||||
associated I/O operations are redirected to a dummy Card, so that the active Card isn't affected. Here,
|
||||
however, that was insufficient. If the VGA is the only installed video card, the VGA ROM expects *NO RESPONSE*
|
||||
on inactive CRTC ports. So I've changed the CRTC I/O handlers to check the Card's fActive flag. This seems
|
||||
like a safe and logical change, but I still have to check for backward-compatibility issues with older ROMs.
|
||||
|
||||
Other problems included:
|
||||
|
||||
* Some bugs in Read Mode 1 that caused a memory test failure
|
||||
* Horizontal and vertical retrace timing issues (the ROM requires a specific number of intervals per second)
|
||||
* Differences between the EGA and VGA in the SWSENSE bit (bit 4) of Input Status Register 0
|
||||
|
||||
The last problem was the most puzzling, because the ROM programs a series of values into the first DAC register,
|
||||
and expects the SWSENSE bit of Input Status Register 0 to change in very specific ways, depending on the kind
|
||||
of monitor attached.
|
||||
|
||||
I've not found any hardware documentation that explains exactly how this should work. IBM's own Technical Reference
|
||||
material is extremely vague:
|
||||
|
||||
"Bit 4: Switch Sense Bit - This bit allows the system microprocessor to read the switch sense line.
|
||||
This bit allows the power-on self-test to determine if a monochrome or color display is connected to
|
||||
the system."
|
||||
|
||||
I've hard-coded a solution that assumes a color monitor. Support for using a monochrome monitor with an EGA was
|
||||
never completed, and this is another related issue that will have to be resolved for the VGA as well.
|
||||
|
||||
---
|
||||
|
||||
While debugging and fixing assorted IBM VGA issues, I made a table of all the register values for the video
|
||||
modes commonly used on the IBM VGA. Here's that table:
|
||||
|
||||
INT 0x10 Mode Requested: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x0D 0x0E 0x10 0x12 0x13
|
||||
|
||||
BIOSMODE: 0x01 0x01 0x03 0x03 0x04 0x04 0x06 0x0D 0x0E 0x10 0x12 0x13
|
||||
CRTC[0x00]: HTOTAL 0x2D 0x2D 0x5F 0x5F 0x2D 0x2D 0x5F 0x2D 0x5F 0x5F 0x5F 0x5F
|
||||
CRTC[0x01]: HDISP_END 0x27 0x27 0x4F 0x4F 0x27 0x27 0x4F 0x27 0x4F 0x4F 0x4F 0x4F
|
||||
CRTC[0x02]: HBLANK_START 0x28 0x28 0x50 0x50 0x28 0x28 0x50 0x28 0x50 0x50 0x50 0x50
|
||||
CRTC[0x03]: HBLANK_END 0x90 0x90 0x82 0x82 0x90 0x90 0x82 0x90 0x82 0x82 0x82 0x82
|
||||
CRTC[0x04]: HRETRACE_START 0x2B 0x2B 0x55 0x55 0x2B 0x2B 0x54 0x2B 0x54 0x54 0x54 0x54
|
||||
CRTC[0x05]: HRETRACE_END 0xA0 0xA0 0x81 0x81 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80
|
||||
CRTC[0x06]: VTOTAL 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0x0B 0xBF
|
||||
CRTC[0x07]: OVERFLOW 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x3E 0x1F
|
||||
CRTC[0x08]: PRESET_ROW 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x09]: MAX_SCAN 0x4F 0x4F 0x4F 0x4F 0xC1 0xC1 0xC1 0xC0 0xC0 0x40 0x40 0x41
|
||||
CRTC[0x0A]: CURSOR_START 0x0D 0x0D 0x0D 0x0D 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0B]: CURSOR_END 0x0E 0x0E 0x0E 0x0E 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0C]: START_ADDR_HI 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0D]: START_ADDR_LO 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0E]: CURSOR_ADDR_HI 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00
|
||||
CRTC[0x0F]: CURSOR_ADDR_LO 0x19 0x19 0x41 0x41 0x19 0x19 0x41 0x19 0x41 0x41 0xE1 0xA2
|
||||
CRTC[0x10]: VRETRACE_START 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x83 0xEA 0x9C
|
||||
CRTC[0x11]: VRETRACE_END 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x85 0x8C 0x8E
|
||||
CRTC[0x12]: VDISP_END 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x5D 0xDF 0x8F
|
||||
CRTC[0x13]: OFFSET 0x14 0x14 0x28 0x28 0x14 0x14 0x28 0x14 0x28 0x28 0x28 0x28
|
||||
CRTC[0x14]: UNDERLINE 0x1F 0x1F 0x1F 0x1F 0x00 0x00 0x00 0x00 0x00 0x0F 0x00 0x40
|
||||
CRTC[0x15]: VBLANK_START 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x63 0xE7 0x96
|
||||
CRTC[0x16]: VBLANK_END 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xBA 0x04 0xB9
|
||||
CRTC[0x17]: MODE_CTRL 0xA3 0xA3 0xA3 0xA3 0xA2 0xA2 0xC2 0xE3 0xE3 0xE3 0xE3 0xA3
|
||||
CRTC[0x18]: LINE_COMPARE 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
||||
GRC[0x00]: SRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x01]: ESRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x02]: COLORCMP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x03]: DATAROT 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x04]: READMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x05]: MODE 0x10 0x10 0x10 0x10 0x30 0x30 0x00 0x00 0x00 0x00 0x00 0x40
|
||||
GRC[0x06]: MISC 0x0E 0x0E 0x0E 0x0E 0x0F 0x0F 0x0D 0x05 0x05 0x05 0x05 0x05
|
||||
GRC[0x07]: COLORDC 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0F 0x0F 0x0F 0x0F 0x0F
|
||||
GRC[0x08]: BITMASK 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
||||
SEQ[0x00]: RESET 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03
|
||||
SEQ[0x01]: CLOCKING 0x08 0x08 0x00 0x00 0x09 0x09 0x01 0x09 0x01 0x01 0x01 0x01
|
||||
SEQ[0x02]: MAPMASK 0x03 0x03 0x03 0x03 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
|
||||
SEQ[0x03]: CHARMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
SEQ[0x04]: MEMMODE 0x03 0x03 0x03 0x03 0x02 0x02 0x06 0x06 0x06 0x06 0x06 0x0E
|
||||
ATC[0x00]: PAL00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
ATC[0x01]: PAL01 0x01 0x01 0x01 0x01 0x13 0x13 0x17 0x01 0x01 0x01 0x01 0x01
|
||||
ATC[0x02]: PAL02 0x02 0x02 0x02 0x02 0x15 0x15 0x17 0x02 0x02 0x02 0x02 0x02
|
||||
ATC[0x03]: PAL03 0x03 0x03 0x03 0x03 0x17 0x17 0x17 0x03 0x03 0x03 0x03 0x03
|
||||
ATC[0x04]: PAL04 0x04 0x04 0x04 0x04 0x02 0x02 0x17 0x04 0x04 0x04 0x04 0x04
|
||||
ATC[0x05]: PAL05 0x05 0x05 0x05 0x05 0x04 0x04 0x17 0x05 0x05 0x05 0x05 0x05
|
||||
ATC[0x06]: PAL06 0x14 0x14 0x14 0x14 0x06 0x06 0x17 0x06 0x06 0x14 0x14 0x06
|
||||
ATC[0x07]: PAL07 0x07 0x07 0x07 0x07 0x07 0x07 0x17 0x07 0x07 0x07 0x07 0x07
|
||||
ATC[0x08]: PAL08 0x38 0x38 0x38 0x38 0x10 0x10 0x17 0x10 0x10 0x38 0x38 0x08
|
||||
ATC[0x09]: PAL09 0x39 0x39 0x39 0x39 0x11 0x11 0x17 0x11 0x11 0x39 0x39 0x09
|
||||
ATC[0x0A]: PAL0A 0x3A 0x3A 0x3A 0x3A 0x12 0x12 0x17 0x12 0x12 0x3A 0x3A 0x0A
|
||||
ATC[0x0B]: PAL0B 0x3B 0x3B 0x3B 0x3B 0x13 0x13 0x17 0x13 0x13 0x3B 0x3B 0x0B
|
||||
ATC[0x0C]: PAL0C 0x3C 0x3C 0x3C 0x3C 0x14 0x14 0x17 0x14 0x14 0x3C 0x3C 0x0C
|
||||
ATC[0x0D]: PAL0D 0x3D 0x3D 0x3D 0x3D 0x15 0x15 0x17 0x15 0x15 0x3D 0x3D 0x0D
|
||||
ATC[0x0E]: PAL0E 0x3E 0x3E 0x3E 0x3E 0x16 0x16 0x17 0x16 0x16 0x3E 0x3E 0x0E
|
||||
ATC[0x0F]: PAL0F 0x3F 0x3F 0x3F 0x3F 0x17 0x17 0x17 0x17 0x17 0x3F 0x3F 0x0F
|
||||
ATC[0x10]: MODE 0x0C 0x0C 0x0C 0x0C 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x41
|
||||
ATC[0x11]: OVERSCAN 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
ATC[0x12]: PLANES 0x0F 0x0F 0x0F 0x0F 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
|
||||
ATC[0x13]: HPAN 0x08 0x08 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
|
||||
In addition, I've created a [VGA Tests](/tests/pc/vga/) directory to hold VGA test and sample code that PCjs can
|
||||
now successfully run (for the most part). See that directory for more details.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*June 1, 2015 (Updated July 9, 2015)*
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
The Strange Case of the EGA Graphics Scroll Bug
|
||||
---
|
||||
|
||||
I was playing with different video modes using this [IBM PC AT w/EGA](/devices/pc/machine/5170/ega/640kb/rev1/),
|
||||
and I discovered an odd problem.
|
||||
|
||||
For example, when I ran this code:
|
||||
|
||||
A>b:debug
|
||||
-a
|
||||
0CE0:0100 mov ax,e
|
||||
0CE0:0103 int 10
|
||||
0CE0:0105 int 3
|
||||
0CE0:0106
|
||||
-g
|
||||
|
||||
the following "text" correctly appeared at the top of the screen, in 640x200 16-color graphics mode 0x0E:
|
||||
|
||||
AX=0B01 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
|
||||
DS=0CE0 ES=0CE0 SS=0CE0 CS=0CE0 IP=0105 NV UP EI PL NZ NA PO NC
|
||||
0CE0:0105 CC INT 3
|
||||
-
|
||||
|
||||
And when I typed "q", then "cls" and finally "dir", the screen filled with DOS directory contents.
|
||||
|
||||
But as soon as the screen started to scroll, the screen contents became garbled. Other EGA graphics modes,
|
||||
like 640x350 16-color mode 0x10, didn't have this problem.
|
||||
|
||||
To investigate, I set a breakpoint in the IBM EGA ROM where the scrolling starts, at 0xC000:12EA (see p.130 of the
|
||||
"IBM Enhanced Graphics Adapter" Technical Reference document):
|
||||
|
||||
CRANK_A:
|
||||
PUSH CX
|
||||
MOV CL,DL
|
||||
SUB CH,CH
|
||||
PUSH SI
|
||||
PUSH DI
|
||||
REP MOVSB
|
||||
POP DI
|
||||
POP SI
|
||||
ADD SI,BX
|
||||
ADD DI,BX
|
||||
POP CX
|
||||
LOOP CRANK_A
|
||||
|
||||
CX contains 0xC0 (192), which is the number of scan-lines to move up, and BX contains 0x50 (80), the number
|
||||
of bytes per scan-line.
|
||||
|
||||
When the breakpoint was hit, I dumped the video hardware state, using the Debugger's "*d video*" command.
|
||||
For comparison purposes, I've pasted the corresponding video state for mode 0x10 on the right-hand side.
|
||||
|
||||
breakpoint hit: C000:12EA (exec)
|
||||
stopped (175707689 ops, 800060264 cycles, 133470 ms, 5994308 hz)
|
||||
AX=020F BX=0050 CX=00C0 DX=1950 SP=0B58 BP=0511 SI=0280 DI=0000
|
||||
SS=011F DS=A000 ES=A000 PS=0246 V0 D0 I1 T0 S0 Z1 A0 P1 C0
|
||||
C000:12EA 51 PUSH CX
|
||||
|
||||
BIOSMODE: 0x0E BIOSMODE: 0x10
|
||||
CRTC[0x00]: HORZ_TOTAL 0x70 CRTC[0x00]: HORZ_TOTAL 0x5B
|
||||
CRTC[0x01]: HORZ_DISP_END 0x4F CRTC[0x01]: HORZ_DISP_END 0x4F
|
||||
CRTC[0x02]: HORZ_BLANK_START 0x59 CRTC[0x02]: HORZ_BLANK_START 0x53
|
||||
CRTC[0x03]: HORZ_BLANK_END 0x2D CRTC[0x03]: HORZ_BLANK_END 0x37
|
||||
CRTC[0x04]: HORZ_RETRACE_START 0x5E CRTC[0x04]: HORZ_RETRACE_START 0x52
|
||||
CRTC[0x05]: HORZ_RETRACE_END 0x06 CRTC[0x05]: HORZ_RETRACE_END 0x00
|
||||
CRTC[0x06]: VERT_TOTAL 0x04 CRTC[0x06]: VERT_TOTAL 0x6C
|
||||
CRTC[0x07]: OVERFLOW 0x11 CRTC[0x07]: OVERFLOW 0x1F
|
||||
CRTC[0x08]: PRESET_ROW_SCAN 0x00 CRTC[0x08]: PRESET_ROW_SCAN 0x00
|
||||
CRTC[0x09]: MAX_SCAN_LINE 0x00 CRTC[0x09]: MAX_SCAN_LINE 0x00
|
||||
CRTC[0x0A]: CURSOR_START 0x00 CRTC[0x0A]: CURSOR_START 0x00
|
||||
CRTC[0x0B]: CURSOR_END 0x01 CRTC[0x0B]: CURSOR_END 0x01
|
||||
CRTC[0x0C]: START_ADDR_HI 0x00 CRTC[0x0C]: START_ADDR_HI 0x00
|
||||
CRTC[0x0D]: START_ADDR_LO 0x00 CRTC[0x0D]: START_ADDR_LO 0x00
|
||||
CRTC[0x0E]: CURSOR_ADDR_HI 0x07 CRTC[0x0E]: CURSOR_ADDR_HI 0x01
|
||||
CRTC[0x0F]: CURSOR_ADDR_LO 0x80* CRTC[0x0F]: CURSOR_ADDR_LO 0x41*
|
||||
CRTC[0x10]: VERT_RETRACE_START 0xE0 CRTC[0x10]: VERT_RETRACE_START 0x5E
|
||||
CRTC[0x11]: VERT_RETRACE_END 0x23 CRTC[0x11]: VERT_RETRACE_END 0x2B
|
||||
CRTC[0x12]: VERT_DISP_END 0xC7 CRTC[0x12]: VERT_DISP_END 0x5D
|
||||
CRTC[0x13]: OFFSET 0x28 CRTC[0x13]: OFFSET 0x28
|
||||
CRTC[0x14]: UNDERLINE 0x00 CRTC[0x14]: UNDERLINE 0x0F
|
||||
CRTC[0x15]: VERT_BLANK_START 0xDF CRTC[0x15]: VERT_BLANK_START 0x5F
|
||||
CRTC[0x16]: VERT_BLANK_END 0xEF CRTC[0x16]: VERT_BLANK_END 0x0A
|
||||
CRTC[0x17]: MODE_CTRL 0xE3 CRTC[0x17]: MODE_CTRL 0xE3
|
||||
CRTC[0x18]: LINE_COMPARE 0xFF CRTC[0x18]: LINE_COMPARE 0xFF
|
||||
STATUS1: 0x01 STATUS1: 0x01
|
||||
ATCDATA: true ATCDATA: true
|
||||
ATC[0x00]: PAL00 0x00 ATC[0x00]: PAL00 0x00
|
||||
ATC[0x01]: PAL01 0x01 ATC[0x01]: PAL01 0x01
|
||||
ATC[0x02]: PAL02 0x02 ATC[0x02]: PAL02 0x02
|
||||
ATC[0x03]: PAL03 0x03 ATC[0x03]: PAL03 0x03
|
||||
ATC[0x04]: PAL04 0x04 ATC[0x04]: PAL04 0x04
|
||||
ATC[0x05]: PAL05 0x05 ATC[0x05]: PAL05 0x05
|
||||
ATC[0x06]: PAL06 0x06 ATC[0x06]: PAL06 0x14
|
||||
ATC[0x07]: PAL07 0x07 ATC[0x07]: PAL07 0x07
|
||||
ATC[0x08]: PAL08 0x10 ATC[0x08]: PAL08 0x38
|
||||
ATC[0x09]: PAL09 0x11 ATC[0x09]: PAL09 0x39
|
||||
ATC[0x0A]: PAL0A 0x12 ATC[0x0A]: PAL0A 0x3A
|
||||
ATC[0x0B]: PAL0B 0x13 ATC[0x0B]: PAL0B 0x3B
|
||||
ATC[0x0C]: PAL0C 0x14 ATC[0x0C]: PAL0C 0x3C
|
||||
ATC[0x0D]: PAL0D 0x15 ATC[0x0D]: PAL0D 0x3D
|
||||
ATC[0x0E]: PAL0E 0x16 ATC[0x0E]: PAL0E 0x3E
|
||||
ATC[0x0F]: PAL0F 0x17 ATC[0x0F]: PAL0F 0x3F
|
||||
ATC[0x10]: MODE 0x01 ATC[0x10]: MODE 0x01
|
||||
ATC[0x11]: OVERSCAN 0x00 ATC[0x11]: OVERSCAN 0x00
|
||||
ATC[0x12]: PLANES 0x0F ATC[0x12]: PLANES 0x0F
|
||||
ATC[0x13]: HORZPAN 0x00 ATC[0x13]: HORZPAN 0x00
|
||||
GRC[0x00]: SRESET 0x00 GRC[0x00]: SRESET 0x00
|
||||
GRC[0x01]: ESRESET 0x00 GRC[0x01]: ESRESET 0x00
|
||||
GRC[0x02]: COLORCMP 0x00 GRC[0x02]: COLORCMP 0x00
|
||||
GRC[0x03]: DATAROT 0x00 GRC[0x03]: DATAROT 0x00*
|
||||
GRC[0x04]: READMAP 0x00 GRC[0x04]: READMAP 0x00
|
||||
GRC[0x05]: MODE 0x11* GRC[0x05]: MODE 0x00
|
||||
GRC[0x06]: MISC 0x05 GRC[0x06]: MISC 0x05
|
||||
GRC[0x07]: COLORDC 0x0F GRC[0x07]: COLORDC 0x0F
|
||||
GRC[0x08]: BITMASK 0xFF GRC[0x08]: BITMASK 0xFF
|
||||
SEQ[0x00]: RESET 0x03 SEQ[0x00]: RESET 0x03
|
||||
SEQ[0x01]: CLOCKING 0x01 SEQ[0x01]: CLOCKING 0x01
|
||||
SEQ[0x02]: MAPMASK 0x0F* SEQ[0x02]: MAPMASK 0x0F*
|
||||
SEQ[0x03]: CHARMAP 0x00 SEQ[0x03]: CHARMAP 0x00
|
||||
SEQ[0x04]: MEMMODE 0x06 SEQ[0x04]: MEMMODE 0x06
|
||||
FEAT: 0x02 FEAT: 0x02
|
||||
MISC: 0x23 MISC: 0xA7
|
||||
STATUS0: 0x10 STATUS0: 0x10
|
||||
LATCHES: 0x00000000 LATCHES: 0x00000000
|
||||
ACCESS: 0x1411 ACCESS: 0x0400
|
||||
|
||||
One of the apparent oddities is that, for mode 0x0E, the GRC Mode Register was programmed with 0x11, whereas
|
||||
for mode 0x10, it was programmed with 0x00. Why would mode 0x0E want to set the ODDEVEN bit during the scroll,
|
||||
when it hadn't been set during any other writes to the screen?
|
||||
|
||||
At this point, I dumped the instruction history buffer a bit ("*dh 100*"), and noticed this GRC write:
|
||||
|
||||
C000:1582 8BC5 MOV AX,BP ;history=33
|
||||
C000:1584 B603 MOV DH,03 ;history=32
|
||||
C000:1586 B2CE MOV DL,CE ;history=31
|
||||
C000:1588 E88AF7 CALL 0D15 ;history=30
|
||||
C000:0D15 86C4 XCHG AL,AH ;history=29
|
||||
C000:0D17 EE OUT DX,AL ;history=28
|
||||
C000:0D18 42 INC DX ;history=27
|
||||
C000:0D19 86C4 XCHG AL,AH ;history=26
|
||||
C000:0D1B EE OUT DX,AL ;history=25
|
||||
C000:0D1C 4A DEC DX ;history=24
|
||||
|
||||
So I looked farther back and saw where BP was set:
|
||||
|
||||
C000:1522 BA00A0 MOV DX,A000 ;history=97
|
||||
C000:1525 BD1105 MOV BP,0511 ;history=96
|
||||
|
||||
Here's the complete function:
|
||||
|
||||
GR_ST_1:
|
||||
MOV DX,A000
|
||||
MOV BP,0511
|
||||
CMP AH,0F
|
||||
JC 1535
|
||||
CALL 14F7
|
||||
JNC 1535
|
||||
MOV BP,0501
|
||||
RET
|
||||
|
||||
OK, so any (EGA) graphics mode below 0x0F is going to the trigger the use of Write Mode 1 with the ODDEVEN bit set.
|
||||
And sure enough, the scrolling bug also occurs when using 320x200 16-color mode 0x0D.
|
||||
|
||||
It's also worth noting that, whenever the ODDEVEN bit of the GRC Mode Register is set, the SEQUENTIAL bit in the Sequencer
|
||||
Memory Mode Register is supposed to be clear (and vice versa -- those two bits are supposed to always be oppositely set).
|
||||
But here, the IBM EGA BIOS hasn't done that. One wonders if that was a mistake....
|
||||
|
||||
Another bit of trivia: while dumping the frame buffer in a VGA text mode in a different emulator, I discovered that
|
||||
when I turned off the ODDEVEN bit in the GRC Mode Register, odd bytes would still appear from plane 1; it wasn't until I
|
||||
*also* turned off the CHAIN bit in the GRC Miscellaneous Register that the odd bytes would no longer appear. But,
|
||||
that could have just been an idiosyncrasy of that particular emulator.
|
||||
|
||||
As a result of all these observations, and more importantly, to make EGA scrolling work properly in modes 0x0D and 0x0E,
|
||||
I've changed the Video component to use odd/even memory functions *only* when the SEQUENTIAL bit (bit 2) of the
|
||||
Sequencer's Memory Mode Register is clear, instead of relying on the ODDEVEN bit (bit 4) of the Graphics Controller's Mode
|
||||
Register.
|
||||
|
||||
To be continued.... because I've barely scratched the surface of all the side-effects of EGA/VGA odd/even addressing,
|
||||
and I hope to do some testing on real hardware in the near future.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*June 5, 2015*
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
Windows 95
|
||||
---
|
||||
|
||||
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 PCjs 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/pcjs/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 PCjs 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 PCjs 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 PCjs 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, PCjs marks opcodes as invalid *only* if they have been confirmed invalid. PCjs 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. PCjs should have simply ignored the second prefix.
|
||||
|
||||
With all of the above changes in place, PCjs 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
|
||||
---
|
||||
|
||||
PCjs 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 PCjs 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:
|
||||
|
||||
<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 PCjs 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 PCjs 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 PCjs Debugger commands, whereas the *output* textarea combines
|
||||
all Debugger output *and* WDEB386 COM2 serial port I/O. You can even use the PCjs Debugger to debug
|
||||
the WDEB386 debugger; just make sure the appropriate text control has focus before typing a command.
|
||||
|
||||
To help reduce confusion, the PCjs 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 PCjs 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
|
||||
---
|
||||
|
||||
PCjs 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. Click the "Run" button to start the machine.
|
||||
|
||||
[Embedded DeskPro 386](/devices/pc/machine/compaq/deskpro386/vga/4096kb/machine.xml "PCjs:deskpro386:::debugger")
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*July 17, 2015 (updated September 12, 2015)*
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
Windows 95 In Your Web Browser
|
||||
---
|
||||
|
||||
Today, the last serious bug preventing a successful boot of Windows 95 was fixed. I won't bore you with
|
||||
the details.
|
||||
|
||||
OK, I will: three arithmetic instructions (specifically, **AND**, **OR** and **XOR**) include a variation that
|
||||
converts an immediate signed byte into a signed word. Those variations were failing to truncate the result when
|
||||
a 16-bit operand size was in effect, and if the destination was a register, the upper 16 bits of that register
|
||||
could become corrupted.
|
||||
|
||||
The [Windows 95 Test Machine](/devices/pc/machine/compaq/deskpro386/vga/4096kb/) hard disk has been updated
|
||||
with a complete set of Windows 95 files from a "Compact" installation, and first boot has finished, so instead
|
||||
of the initial "Getting ready to run Windows 95 for the first time..." splash screen, you'll see the normal
|
||||
Windows 95 startup screen.
|
||||
|
||||
The machine is still a bit finicky. It easily gets confused about the state of its shift keys if you switch away
|
||||
from the browser and then back again. And Explorer windows don't open in the correct view; for example, both
|
||||
**My Computer** and **Recycle Bin** open the same (incorrect) view. In short, there are still some serious bugs
|
||||
to be resolved, but booting has been achieved.
|
||||
|
||||
The adventure continues.
|
||||
|
||||
[Embedded DeskPro 386](/devices/pc/machine/compaq/deskpro386/vga/4096kb/machine.xml "PCjs:deskpro386")
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*September 21, 2015*
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
Windows 95 and Early 80386 CPUs
|
||||
---
|
||||
|
||||
Every time Windows 95 starts up, its real-mode loader performs the following CPU identification test:
|
||||
|
||||
&0654:121E 9C PUSHF
|
||||
&0654:121F 33C0 XOR AX,AX ; try to clear bit 15 of flags
|
||||
&0654:1221 50 PUSH AX
|
||||
&0654:1222 9D POPF
|
||||
&0654:1223 9C PUSHF
|
||||
&0654:1224 58 POP AX
|
||||
&0654:1225 A90080 TEST AX,8000 ; bit 15 of flags set anyway?
|
||||
&0654:1228 7543 JNZ 126D ; yes (must be an 8086/8086)
|
||||
&0654:122A B80070 MOV AX,7000 ; try to set bits 12-14 of flags
|
||||
&0654:122D 50 PUSH AX
|
||||
&0654:122E 9D POPF
|
||||
&0654:122F FB STI
|
||||
&0654:1230 9C PUSHF
|
||||
&0654:1231 58 POP AX
|
||||
&0654:1232 9D POPF
|
||||
&0654:1233 A90070 TEST AX,7000 ; any of bits 12-14 of flags set?
|
||||
&0654:1236 7436 JZ 126E ; no (must be an 80286)
|
||||
&0654:1238 51 PUSH CX
|
||||
&0654:1239 33C9 XOR CX,CX
|
||||
&0654:123B 8BC4 MOV AX,SP
|
||||
&0654:123D 83E003 AND AX,0003
|
||||
&0654:1240 7404 JZ 1246
|
||||
&0654:1242 8BC8 MOV CX,AX
|
||||
&0654:1244 2BE0 SUB SP,AX
|
||||
&0654:1246 669C PUSHFD
|
||||
&0654:1248 666800000400 PUSH 00040000 ; try to set the AC bit of flags
|
||||
&0654:124E 669D POPFD
|
||||
&0654:1250 669C PUSHFD
|
||||
&0654:1252 6658 POP EAX
|
||||
&0654:1254 669D POPFD
|
||||
&0654:1256 66A900000400 TEST EAX,00040000 ; AC bit set?
|
||||
&0654:125C 7508 JNZ 1266 ; yes (must be an 80486, or newer)
|
||||
&0654:125E 40 INC AX
|
||||
&0654:125F 03E1 ADD SP,CX
|
||||
&0654:1261 59 POP CX
|
||||
&0654:1262 0BC0 OR AX,AX
|
||||
&0654:1264 F9 STC ; return ZF clear and CF set to indicate 80386
|
||||
&0654:1265 C3 RET
|
||||
&0654:1266 03E1 ADD SP,CX
|
||||
&0654:1268 59 POP CX
|
||||
&0654:1269 660BC0 OR EAX,EAX ; return ZF clear and CF clear to indicate 80486
|
||||
&0654:126C C3 RET
|
||||
&0654:126D 9D POPF
|
||||
&0654:126E 33C0 XOR AX,AX ; return ZF set to indicate 80286 or older
|
||||
&0654:1270 C3 RET
|
||||
|
||||
If the above function returns ZF set, then the processor is an 80286 or older, so Windows 95 displays the
|
||||
following message and exits:
|
||||
|
||||
You need an 80386 processor to run Windows.
|
||||
|
||||
If the above function returns CF set, then the processor is an 80386, and if CF is clear, the processor is an
|
||||
80486 or newer.
|
||||
|
||||
When CF is set, Windows 95 proceeds to the following 80386 stepping check, which attempts to execute an
|
||||
XBTS (Extract Bit String) instruction -- an instruction that existed only on B0 and earlier 80386 steppings.
|
||||
|
||||
&0654:1299 53 PUSH BX
|
||||
&0654:129A 51 PUSH CX
|
||||
&0654:129B 52 PUSH DX
|
||||
&0654:129C B80635 MOV AX,3506 ; save the current "invalid opcode" handler (IVT entry #6)
|
||||
&0654:129F CD21 INT 21
|
||||
&0654:12A1 8CC0 MOV AX,ES
|
||||
&0654:12A3 66C1E010 SHL EAX,10
|
||||
&0654:12A7 8BC3 MOV AX,BX
|
||||
&0654:12A9 6650 PUSH EAX
|
||||
&0654:12AB 1E PUSH DS
|
||||
&0654:12AC BAE012 MOV DX,12E0
|
||||
&0654:12AF 8CCB MOV BX,CS
|
||||
&0654:12B1 8EDB MOV DS,BX
|
||||
&0654:12B3 B80625 MOV AX,2506 ; temporarily install a new "invalid opcode" exception handler
|
||||
&0654:12B6 CD21 INT 21
|
||||
&0654:12B8 1F POP DS
|
||||
&0654:12B9 33C0 XOR AX,AX
|
||||
&0654:12BB 8BD0 MOV DX,AX
|
||||
&0654:12BD B900FF MOV CX,FF00
|
||||
&0654:12C0 0FA6CA XBTS CX,DX,AX,CL ; attempt to execute an XBTS instruction
|
||||
&0654:12C3 6658 POP EAX
|
||||
&0654:12C5 8BD0 MOV DX,AX
|
||||
&0654:12C7 66C1E810 SHR EAX,10
|
||||
&0654:12CB 1E PUSH DS
|
||||
&0654:12CC 8ED8 MOV DS,AX
|
||||
&0654:12CE B80625 MOV AX,2506 ; restore the original "invalid opcode" exception handler
|
||||
&0654:12D1 CD21 INT 21
|
||||
&0654:12D3 1F POP DS
|
||||
&0654:12D4 B8B000 MOV AX,00B0
|
||||
&0654:12D7 0BC9 OR CX,CX ; did XBTS work (ie, did CX change)?
|
||||
&0654:12D9 7401 JZ 12DC ; yes, so we must have a B0 stepping or earlier
|
||||
&0654:12DB 40 INC AX ; no, so bump the stepping to B1
|
||||
&0654:12DC 5A POP DX
|
||||
&0654:12DD 59 POP CX
|
||||
&0654:12DE 5B POP BX
|
||||
&0654:12DF C3 RET ; returns AX == 0xB0 if B0 stepping or earlier, 0xB1 if B1 stepping or later
|
||||
|
||||
&0654:12E0 55 PUSH BP ; temporary "invalid opcode" handler
|
||||
&0654:12E1 8BEC MOV BP,SP
|
||||
&0654:12E3 83460203 ADD [BP+02],0003 ; advance IP past the 3-byte XBTS instruction
|
||||
&0654:12E7 5D POP BP
|
||||
&0654:12E8 CF IRET
|
||||
|
||||
If the above function returns 0xB0, then the 80386 is a B0 or earlier stepping, so Windows 95 displays the
|
||||
following message and aborts:
|
||||
|
||||
Windows may not run correctly with the 80386 processor that is installed in this computer.
|
||||
Upgrade your 80386 processor.
|
||||
|
||||
Otherwise, the 80386 is a B1 or later stepping, so Windows 95 next performs a multiplication test (a simplified
|
||||
version of the multiplication tests discussed in "[Early 80386 CPUs](/blog/2015/02/23/)"):
|
||||
|
||||
&0654:12E9 33C9 XOR CX,CX
|
||||
&0654:12EB 66BB81000000 MOV EBX,00000081
|
||||
&0654:12F1 66B800A01704 MOV EAX,0417A000
|
||||
&0654:12F7 66F7E3 MUL EBX
|
||||
&0654:12FA 6683FA02 CMP EDX,00000002
|
||||
&0654:12FE 750B JNZ 130B
|
||||
&0654:1300 663D00A0E70F CMP EAX,0FE7A000
|
||||
&0654:1306 7503 JNZ 130B
|
||||
&0654:1308 E2E1 LOOP 12EB
|
||||
&0654:130A C3 RET
|
||||
|
||||
If any of the 65,536 identical multiplications return an incorrect result, Windows 95 displays the following
|
||||
message:
|
||||
|
||||
WARNING: The 80386 processor in this computer may not reliably execute 32-bit
|
||||
multiplication. Windows may occasionally fail on this computer.
|
||||
|
||||
You may want to replace your 80386 processor.
|
||||
Press any key to continue...Press a key to continue
|
||||
|
||||
A multiplication failure implies that the 80386 stepping is B1, because later steppings resolved the problem.
|
||||
|
||||
You may have heard that Windows 95 [pulled support for the 80386 B1 stepping](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx),
|
||||
and that's true, but only insofar as Windows 95 SETUP is concerned. The following code is executed by WINSETUP.BIN,
|
||||
a 16-bit Windows component that manages the Windows 95 installation process:
|
||||
|
||||
#05C7:69E8 1E PUSH DS
|
||||
#05C7:69E9 07 POP ES
|
||||
#05C7:69EA 6657 PUSH EDI
|
||||
#05C7:69EC FD STD
|
||||
#05C7:69ED 66BF00000000 MOV EDI,00000000
|
||||
#05C7:69F3 678A07 MOV AL,[EDI]
|
||||
#05C7:69F6 67AA STOSB
|
||||
#05C7:69F8 33C0 XOR AX,AX
|
||||
#05C7:69FA 6681FFFFFF0000 CMP EDI,0000FFFF
|
||||
#05C7:6A01 7501 JNZ 6A04
|
||||
#05C7:6A03 40 INC AX
|
||||
#05C7:6A04 FC CLD
|
||||
#05C7:6A05 665F POP EDI
|
||||
#05C7:6A07 C3 RET
|
||||
|
||||
Ths above code checks for B1 stepping [Errata #7](/blog/2015/02/23/): "Wrong Register Size for String Instructions
|
||||
in Mixed 16/32-bit Addressing Systems." It returns AX == 0 if the STOSB instruction updated EDI correctly (0xFFFFFFFF)
|
||||
or AX == 1 if EDI is incorrect (0x0000FFFF).
|
||||
|
||||
If Errata #7 is detected, then Windows 95 SETUP displays the following message and aborts:
|
||||
|
||||
Setup Error B1: Setup has detected an 80386 processor that is not compatible with this version of Windows.
|
||||
Before you can run this version of Windows, you need to upgrade your processor.
|
||||
Contact your computer manufacturer for more information.
|
||||
|
||||
However, if you can get through SETUP, Windows 95 will still run on a B1 stepping. For example, if you installed
|
||||
Windows 95 using a newer 80386, and then later "downgraded" the CPU to a B1, Windows 95 would still run. If your B1
|
||||
suffered from the multiplication flaw, you would see the 32-bit multiplication warning on start-up, but you could
|
||||
still continue to run, and if there was no multiplication problem, you would not see any message at all.
|
||||
|
||||
---
|
||||
|
||||
PCjs v1.20.0 now supports a "stepping" attribute on the <cpu> element, which you can use to simulate specific
|
||||
stepping behavior. For example, a *machine.xml* file with the following CPU definition:
|
||||
|
||||
<cpu id="cpu386" model="80386" stepping="b0"/>
|
||||
|
||||
will cause Windows 95 to abort exactly as described as above. Similarly, selecting a 80386 B1 stepping:
|
||||
|
||||
<cpu id="cpu386" model="80386" stepping="b1"/>
|
||||
|
||||
will cause Windows 95 to display the 32-bit multiplication warning shown above (PCjs deliberately fails the exact
|
||||
multiplication test that Windows 95 performs).
|
||||
|
||||
If you want to simulate a B1 stepping that does *not* have the 32-bit multiplication flaw, set the stepping to B2:
|
||||
|
||||
<cpu id="cpu386" model="80386" stepping="b2"/>
|
||||
|
||||
B2 was not an actual 80386 stepping; it is a *pseudo-stepping* that provides a simple way of specifying a B1 80386 that
|
||||
passes all 32-bit multiplication tests.
|
||||
|
||||
As previously discussed, Windows 95 SETUP will refuse to install on any "A" or "B" stepping, but if it's already been
|
||||
installed, it *will* start up on a B1 stepping.
|
||||
|
||||
PCjs stepping support is extremely limited at this point. Here's a summary:
|
||||
|
||||
1. 80386 steppings A0-B0 provide *limited* support for the short-lived XBTS and IBTS instructions
|
||||
2. 80386 steppings A0-B1 enable [Errata #7](/blog/2015/02/23/) for STOSB (as tested by Windows 95; see above)
|
||||
3. 80386 stepping B1 enables 32-bit multiplication errors (as tested by Windows 95; see above)
|
||||
4. 80386 stepping B2 includes all supported B1 errata, but without 32-bit multiplication errors
|
||||
|
||||
In addition, on 80386 reset, we set the CPU revision number in DX to the appropriate value for the specified stepping.
|
||||
|
||||
Support for additional 80286 and 80386 errata may be added over time, as interesting scenarios or test cases are discovered.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 27, 2015*
|
||||
Loading…
Reference in a new issue