Blog updates

This commit is contained in:
Jeff Parsons 2015-12-10 15:12:21 -08:00
commit abb77fe5c9
8 changed files with 131 additions and 45 deletions

View file

@ -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)*

View file

@ -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*

View file

@ -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)*

View file

@ -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*

View file

@ -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 &lt;cpu&gt; 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*