From f7e93fbeb3ba7c6c9da8bcbde426f7c1f371d303 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Tue, 2 Jun 2015 14:09:59 -0700 Subject: [PATCH] Finally, some progress with the IBM VGA ROM --- blog/2015/06/01/README.md | 227 +++++++++++ .../deskpro386/1988-01-28/1988-01-28.nasm | 5 + devices/pc/video/ibm/vga/ibm-vga.nasm | 74 +++- modules/pcjs/lib/bus.js | 3 +- modules/pcjs/lib/video.js | 351 ++++++++++++++---- modules/pcjs/lib/x86cpu.js | 2 +- 6 files changed, 574 insertions(+), 88 deletions(-) create mode 100644 blog/2015/06/01/README.md diff --git a/blog/2015/06/01/README.md b/blog/2015/06/01/README.md new file mode 100644 index 000000000..f3b5c5d27 --- /dev/null +++ b/blog/2015/06/01/README.md @@ -0,0 +1,227 @@ +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 we'll still have to check for backward-compatibility issues with older ROMs. + +The VGA ROM programs the card for the first time here: + + EAX=00000000 EBX=00001642 ECX=00000004 EDX=00004AE8 + ESP=000000EA EBP=FFFF00F0 ESI=00006000 EDI=000003D4 + SS=0030 DS=0000 ES=C000 FS=0000 GS=0304 PS=00000246 V0 D0 I1 T0 S0 Z1 A0 P1 C0 + C000:00CB E85910 CALL 1127 ;cycles=5 + videoVGA.outPort(0x03C4,SEQ.INDX,0x00) @C000:112F + videoVGA.outPort(0x03C5,SEQ.RESET,0x01) @C000:112F + videoVGA.outPort(0x03C4,SEQ.INDX,0x01) @C000:1204 + videoVGA.outPort(0x03C5,SEQ.CLK,0x29) @C000:1204 + videoVGA.outPort(0x03C4,SEQ.INDX,0x02) @C000:1204 + videoVGA.outPort(0x03C5,SEQ.MAPMASK,0x0F) @C000:1204 + videoVGA.outPort(0x03C4,SEQ.INDX,0x03) @C000:1204 + videoVGA.outPort(0x03C5,SEQ.CHARMAP,0x00) @C000:1204 + videoVGA.outPort(0x03C4,SEQ.INDX,0x04) @C000:1204 + videoVGA.outPort(0x03C5,SEQ.MODE,0x06) @C000:1204 + videoVGA.outPort(0x03C2,MISC,0x63) @C000:1143 + videoVGA.outPort(0x03C4,SEQ.INDX,0x00) @C000:1149 + videoVGA.outPort(0x03C5,SEQ.RESET,0x03) @C000:1149 + videoVGA.outPort(0x03D4,CRTC.INDX,0x11) @C000:1151 + videoVGA.outPort(0x03D5,CRTC.VERT_RETRACE_END,0x00) @C000:1151 + videoVGA.outPort(0x03D4,CRTC.INDX,0x00) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.HORZ_TOTAL,0x5F) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x01) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.HORZ_DISP_END,0x4F) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x02) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.HORZ_BLANK_START,0x50) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x03) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.HORZ_BLANK_END,0x82) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x04) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.HORZ_RETRACE_START,0x55) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x05) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.HORZ_RETRACE_END,0x81) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x06) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.VERT_TOTAL,0xBF) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x07) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.CRTC_OVERFLOW,0x1F) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x08) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.PRESET_ROW_SCAN,0x00) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x09) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.MAX_SCAN_LINE,0x40) @C000:1204 + removeCursor(): removed from 0,0 + videoVGA.outPort(0x03D4,CRTC.INDX,0x0A) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.CURSOR_START,0x00) @C000:1204 + checkCursor(): cursor moved from -1 to 0 + videoVGA.outPort(0x03D4,CRTC.INDX,0x0B) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.CURSOR_END,0x00) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x0C) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.START_ADDR_HI,0x00) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x0D) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.START_ADDR_LO,0x00) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x0E) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.CURSOR_ADDR_HI,0x00) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x0F) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.CURSOR_ADDR_LO,0x00) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x10) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.VERT_RETRACE_START,0x9C) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x11) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.VERT_RETRACE_END,0x8E) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x12) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.VERT_DISP_END,0x8F) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x13) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.OFFSET,0x28) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x14) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.UNDERLINE,0x1F) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x15) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.VERT_BLANK_START,0x96) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x16) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.VERT_BLANK_END,0xB9) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x17) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.MODE_CTRL,0xE3) @C000:1204 + videoVGA.outPort(0x03D4,CRTC.INDX,0x18) @C000:1204 + videoVGA.outPort(0x03D5,CRTC.LINE_COMPARE,0xFF) @C000:1204 + videoVGA.inPort(0x03DA,STATUS1): 0x00 @C000:1167 + videoVGA.outPort(0x03C0,ATC.INDX,0x10) @C000:116C + videoVGA.outPort(0x03C0,ATC.MODE,0x01) @C000:1171 + videoVGA.outPort(0x03C0,ATC.INDX,0x12) @C000:1174 + videoVGA.outPort(0x03C0,ATC.PLANES,0x0F) @C000:1179 + videoVGA.outPort(0x03C0,ATC.INDX,0x13) @C000:117C + videoVGA.outPort(0x03C0,ATC.HORZPAN,0x00) @C000:1181 + videoVGA.outPort(0x03CE,GRC.INDX,0x00) @C000:1204 + videoVGA.outPort(0x03CF,GRC.SRESET,0x00) @C000:1204 + videoVGA.outPort(0x03CE,GRC.INDX,0x01) @C000:1204 + videoVGA.outPort(0x03CF,GRC.ESRESET,0x00) @C000:1204 + videoVGA.outPort(0x03CE,GRC.INDX,0x02) @C000:1204 + videoVGA.outPort(0x03CF,GRC.COLRCMP,0x0F) @C000:1204 + videoVGA.outPort(0x03CE,GRC.INDX,0x03) @C000:1204 + videoVGA.outPort(0x03CF,GRC.DATAROT,0x00) @C000:1204 + videoVGA.outPort(0x03CE,GRC.INDX,0x04) @C000:1204 + videoVGA.outPort(0x03CF,GRC.READMAP,0x00) @C000:1204 + videoVGA.outPort(0x03CE,GRC.INDX,0x05) @C000:1204 + videoVGA.outPort(0x03CF,GRC.MODE,0x08) @C000:1204 + setAccess(0x0210) + videoVGA.outPort(0x03CE,GRC.INDX,0x06) @C000:1204 + videoVGA.outPort(0x03CF,GRC.MISC,0x05) @C000:1204 + setMode(0x0010) + setMode(16): removing 0x00008000 bytes from 0x000B8000 + setMode(16): adding 0x00010000 bytes to 0x000A0000 + videoVGA.outPort(0x03CE,GRC.INDX,0x07) @C000:1204 + videoVGA.outPort(0x03CF,GRC.COLRDC,0x0F) @C000:1204 + videoVGA.outPort(0x03CE,GRC.INDX,0x08) @C000:1204 + videoVGA.outPort(0x03CF,GRC.BITMASK,0xFF) @C000:1204 + + EAX=0000FF09 EBX=00001642 ECX=00000000 EDX=000003DA + ESP=000000EA EBP=FFFF00F0 ESI=00006000 EDI=000003D4 + SS=0030 DS=0000 ES=C000 FS=0000 GS=0304 PS=00000246 V0 D0 I1 T0 S0 Z1 A0 P1 C0 + C000:00CE 32C0 XOR AL,AL ;cycles=1031 + +Now that the card was programmed for the first time, I used the `d video` Debugger command to dump the video +card's state: + + BIOSMODE: 0x10 + CRTC[0x00]: HORZ_TOTAL 0x5F + CRTC[0x01]: HORZ_DISP_END 0x4F + CRTC[0x02]: HORZ_BLANK_START 0x50 + CRTC[0x03]: HORZ_BLANK_END 0x82 + CRTC[0x04]: HORZ_RETRACE_START 0x55 + CRTC[0x05]: HORZ_RETRACE_END 0x81 + CRTC[0x06]: VERT_TOTAL 0xBF + CRTC[0x07]: CRTC_OVERFLOW 0x1F + CRTC[0x08]: PRESET_ROW_SCAN 0x00 + CRTC[0x09]: MAX_SCAN_LINE 0x40 + CRTC[0x0A]: CURSOR_START 0x00 + CRTC[0x0B]: CURSOR_END 0x00 + CRTC[0x0C]: START_ADDR_HI 0x00 + CRTC[0x0D]: START_ADDR_LO 0x00 + CRTC[0x0E]: CURSOR_ADDR_HI 0x00 + CRTC[0x0F]: CURSOR_ADDR_LO 0x00 + CRTC[0x10]: VERT_RETRACE_START 0x9C + CRTC[0x11]: VERT_RETRACE_END 0x8E + CRTC[0x12]: VERT_DISP_END 0x8F + CRTC[0x13]: OFFSET 0x28 + CRTC[0x14]: UNDERLINE 0x1F + CRTC[0x15]: VERT_BLANK_START 0x96 + CRTC[0x16]: VERT_BLANK_END 0xB9 + CRTC[0x17]: MODE_CTRL 0xE3 + CRTC[0x18]: LINE_COMPARE 0xFF* + STATUS1: 0x00 + ATCDATA: false + ATC[0x00]: PAL00 0x?? + ATC[0x01]: PAL01 0x?? + ATC[0x02]: PAL02 0x?? + ATC[0x03]: PAL03 0x?? + ATC[0x04]: PAL04 0x?? + ATC[0x05]: PAL05 0x?? + ATC[0x06]: PAL06 0x?? + ATC[0x07]: PAL07 0x?? + ATC[0x08]: PAL08 0x?? + ATC[0x09]: PAL09 0x?? + ATC[0x0A]: PAL0A 0x?? + ATC[0x0B]: PAL0B 0x?? + ATC[0x0C]: PAL0C 0x?? + ATC[0x0D]: PAL0D 0x?? + ATC[0x0E]: PAL0E 0x?? + ATC[0x0F]: PAL0F 0x?? + ATC[0x10]: MODE 0x01 + ATC[0x11]: OVRSCAN 0x?? + ATC[0x12]: PLANES 0x0F + ATC[0x13]: HORZPAN 0x00* + GRC[0x00]: SRESET 0x00 + GRC[0x01]: ESRESET 0x00 + GRC[0x02]: COLRCMP 0x0F + GRC[0x03]: DATAROT 0x00 + GRC[0x04]: READMAP 0x00 + GRC[0x05]: MODE 0x08 + GRC[0x06]: MISC 0x05 + GRC[0x07]: COLRDC 0x0F + GRC[0x08]: BITMASK 0xFF* + SEQ[0x00]: RESET 0x03* + SEQ[0x01]: CLK 0x29 + SEQ[0x02]: MAPMASK 0x0F + SEQ[0x03]: CHARMAP 0x00 + SEQ[0x04]: MODE 0x06 + FEAT: 0x00 + MISC: 0x63 + STATUS0: 0x00 + LATCHES: 0x00 + ACCESS: 0x0210 + Use 'dump video buffer' to dump video memory + +If you've noticed that none of the ATC palette registers were programmed, well, that's apparently by design. + +*[@jeffpar](http://twitter.com/jeffpar)* +*June 1, 2015* diff --git a/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.nasm b/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.nasm index 190437185..35c29c417 100644 --- a/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.nasm +++ b/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.nasm @@ -4805,6 +4805,11 @@ xa9e9: cs rep movsw ; 0000A9E9 F32EA5 '...' mov al,0x3b ; 0000A9F9 B03B '.;' out 0x84,al ; 0000A9FB E684 '..' call xc259 ; 0000A9FD E85918 '.Y.' + ; + ; Set bit 2 (reserved?) and bit 4 (memory size?) of ROM BIOS Video Mode Options byte @40:0087. + ; It would appear that Compaq had a different conception of this byte; however, in the case of an + ; adapter like the IBM VGA, it doesn't matter, since the IBM VGA ROM will rewrite this byte anyway. + ; or byte [0x87],0x14 ; 0000AA00 800E870014 '.....' mov al,0x8e ; 0000AA05 B08E '..' call xb544 ; 0000AA07 E83A0B '.:.' diff --git a/devices/pc/video/ibm/vga/ibm-vga.nasm b/devices/pc/video/ibm/vga/ibm-vga.nasm index 498f08fd1..592d0e9e8 100644 --- a/devices/pc/video/ibm/vga/ibm-vga.nasm +++ b/devices/pc/video/ibm/vga/ibm-vga.nasm @@ -24,8 +24,31 @@ mov [0x10e],cs ; 00000083 8C0E0E01 '....' sti ; 00000087 FB '.' mov byte [0x489],0x11 ; 00000088 C606890411 '.....' - mov byte [0x487],0x60 ; 0000008D C606870460 '....`' - call x100 ; 00000092 E86B00 '.k.' + + ; + ; 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 +; +; From the "IBM Personal System/2 Model 50 and 60 Technical Reference: I/O Controllers, Video Subsystem", p.4-29: +; +; When in setup mode (I/O address hex 0094, bit 5 equals 0), the VGA responds to a single option select byte +; at I/O address hex 0102 and treats the LSB (bit 0) of that byte as the VGA sleep bit. When the LSB equals 0, +; the VGA does not respond to commands, addresses, or data, on the data bus. When the LSB equals 1, the VGA +; responds. If the VGA was set up and is generating video output when the LSB is set to 0, the output is still +; generated. +; +; The VGA responds only to address hex 0102 when in the setup mode. No other addresses are valid at that time. +; Conversely, the VGA ignores address hex 0102 when in the enabled mode (I/O address hex 0094, bit 5 equals 1), +; and decodes normal I/O and memory addresses. +; cli ; 00000095 FA '.' mov dx,0x46e8 ; 00000096 BAE846 '..F' mov ax,0x16 ; 00000099 B81600 '...' @@ -42,14 +65,35 @@ sti ; 000000B1 FB '.' push cs ; 000000B2 0E '.' pop es ; 000000B3 07 '.' + + ; + ; Default to mono settings + ; mov di,0x3b4 ; 000000B4 BFB403 '...' mov bx,0x1602 ; 000000B7 BB0216 '...' + + ; + ; Test bit 1 of ROM BIOS Video Mode Options byte @40:0087 (set if mono, clear if color) + ; test byte [0x487],0x2 ; 000000BA F606870402 '.....' jnz xc7 ; 000000BF 7506 'u.' + + ; + ; Choose color settings + ; mov di,0x3d4 ; 000000C1 BFD403 '...' mov bx,0x1642 ; 000000C4 BB4216 '.B.' + + ; + ; Set the CRTC base port address (0x3B4 for mono, 0x3D4 for color) + ; xc7: mov [0x463],di ; 000000C7 893E6304 '.>c.' + + ; + ; Program the card for a default mode + ; call x1127 ; 000000CB E85910 '.Y.' + xor al,al ; 000000CE 32C0 '2.' out dx,al ; 000000D0 EE '.' call x547 ; 000000D1 E87304 '.s.' @@ -2105,7 +2149,19 @@ x1122: mov dx,0x3c8 ; 00001122 BAC803 '...' out dx,al ; 00001125 EE '.' x1126: ret ; 00001126 C3 '.' -x1127: mov dx,0x3c4 ; 00001127 BAC403 '...' +; +; Programs the card using data from [BX]; CRTC port address is in DI (0x3B4 or 0x3D4). +; +; Writes 0x01 to SEQ register 0x00 (SEQ.RESET). +; Writes values starting at [BX+0x05] to SEQ registers 0x01-0x04. +; Writes [BX+0x09] to Miscellaneous Output register (MISC). +; Writes 0x03 to SEQ register 0x00 (SEQ.RESET). +; Writes 0x00 to CRTC register 0x11 (CRTC.VERT_RETRACE_END). +; Writes values starting at [BX+0x0A] to CRTC registers 0x00-0x18. +; Writes values starting at [BX+0x33] to ATC registers 0x10, 0x12 and 0x13 +; Writes values starting at [BX+0x37] to GRC registers 0x00-0x08. +; +x1127: mov dx,0x3c4 ; (DX) == SEQ port address (0x3C4) mov ax,0x100 ; 0000112A B80001 '...' pushf ; 0000112D 9C '.' cli ; 0000112E FA '.' @@ -2116,14 +2172,14 @@ x1127: mov dx,0x3c4 ; 00001127 BAC403 '...' mov al,0x1 ; 00001137 B001 '..' call x1201 ; 00001139 E8C500 '...' pop bx ; 0000113C 5B '[' - mov dl,0xc2 ; 0000113D B2C2 '..' + mov dl,0xc2 ; (DX) == MISC port address (0x3C2) mov al,[es:bx+0x9] ; 0000113F 268A4709 '&.G.' out dx,al ; 00001143 EE '.' - mov dl,0xc4 ; 00001144 B2C4 '..' + mov dl,0xc4 ; (DX) == SEQ port address (0x3C4) mov ax,0x300 ; 00001146 B80003 '...' out dx,ax ; 00001149 EF '.' popf ; 0000114A 9D '.' - mov dx,di ; 0000114B 8BD7 '..' + mov dx,di ; (DX) == CRTC port address (0x3B4 or 0x3D4) mov al,0x11 ; 0000114D B011 '..' xor ah,ah ; 0000114F 32E4 '2.' out dx,ax ; 00001151 EF '.' @@ -2134,12 +2190,12 @@ x1127: mov dx,0x3c4 ; 00001127 BAC403 '...' call x1201 ; 0000115B E8A300 '...' pop bx ; 0000115E 5B '[' mov dx,di ; 0000115F 8BD7 '..' - add dl,0x6 ; 00001161 80C206 '...' + add dl,0x6 ; (DX) == STATUS1 port address (0x3BA or 0x3DA) push dx ; 00001164 52 'R' pushf ; 00001165 9C '.' cli ; 00001166 FA '.' in al,dx ; 00001167 EC '.' - mov dl,0xc0 ; 00001168 B2C0 '..' + mov dl,0xc0 ; (DX) == ATC port address (0x3C0) mov al,0x10 ; 0000116A B010 '..' out dx,al ; 0000116C EE '.' mov al,[es:bx+0x33] ; 0000116D 268A4733 '&.G3' @@ -2154,7 +2210,7 @@ x1127: mov dx,0x3c4 ; 00001127 BAC403 '...' out dx,al ; 00001181 EE '.' popf ; 00001182 9D '.' push bx ; 00001183 53 'S' - mov dl,0xce ; 00001184 B2CE '..' + mov dl,0xce ; (DX) == GRC port address (0x3CE) add bx,byte +0x37 ; 00001186 83C337 '..7' mov cx,0x9 ; 00001189 B90900 '...' xor al,al ; 0000118C 32C0 '2.' diff --git a/modules/pcjs/lib/bus.js b/modules/pcjs/lib/bus.js index 58f2ec1e6..386bcae7e 100644 --- a/modules/pcjs/lib/bus.js +++ b/modules/pcjs/lib/bus.js @@ -1375,7 +1375,8 @@ Bus.prototype.checkPortInputNotify = function(port, addrLIP) } if (aNotify !== undefined) { if (aNotify[1]) { - bIn = aNotify[1].call(aNotify[0], port, addrLIP); + var b = aNotify[1].call(aNotify[0], port, addrLIP); + if (b !== undefined) bIn = b; } if (DEBUGGER && this.dbg && this.fPortInputBreakAll != aNotify[2]) { this.dbg.checkPortInput(port, bIn); diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index b430d8a18..3c3874433 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -462,6 +462,42 @@ Video.CARD = { } }; +/* + * Supported Modes + * + * Although this component is designed to be a video hardware emulation, not a BIOS simulation, we DO + * look for changes to the hardware state that correspond to standard BIOS mode settings, so our internal + * mode setting will normally match the current BIOS mode setting; however, this a debugging convenience, + * not an attempt to monitor or emulate the BIOS. + * + * We do have some BIOS awareness (eg, when loading ROM-based fonts, and some special code to ensure all + * the BIOS diagnostics pass), but for the most part, we treat the BIOS like any other application code. + * + * As we expand support to include more programmable cards like the EGA, it becomes quite easy for the card + * to enter a "mode" that has no BIOS counterpart (eg, non-standard combinations of frame buffer address, + * memory access modes, fonts, display regions, etc). Our hardware emulation routines will cope with those + * situations as best they can (and when they don't, it should be considered a bug if some application is + * broken as a result), but realistically, our hardware emulation is never likely to be 100% accurate. + */ +Video.MODE = { + CGA_40X25_BW: 0, + CGA_40X25: 1, + CGA_80X25_BW: 2, + CGA_80X25: 3, + CGA_320X200: 4, + CGA_320X200_BW: 5, + CGA_640X200: 6, + MDA_80X25: 7, + EGA_320X200: 0x0D, // mapped at A000:0000 + EGA_640X200: 0x0E, // mapped at A000:0000 + EGA_640X350_MONO: 0x0F, // mapped at A000:0000, monochrome + EGA_640X350: 0x10, // mapped at A000:0000, color + VGA_640X480_MONO: 0x11, // mapped at A000:0000, monochrome + VGA_640X480: 0x12, // mapped at A000:0000, color + VGA_320X200: 0x13, // mapped at A000:0000, color + UNKNOWN: 0xFF +}; + /* * Supported Monitors * @@ -604,42 +640,6 @@ Video.aEGAMonitorSwitches = { 0x05: [ChipSet.MONITOR.MONO, ChipSet.MONITOR.COLOR, false] // "0101" }; -/* - * Supported Modes - * - * Although this component is designed to be a video hardware emulation, not a BIOS simulation, we DO - * look for changes to the hardware state that correspond to standard BIOS mode settings, so our internal - * mode setting will normally match the current BIOS mode setting; however, this a debugging convenience, - * not an attempt to monitor or emulate the BIOS. - * - * We do have some BIOS awareness (eg, when loading ROM-based fonts, and some special code to ensure all - * the BIOS diagnostics pass), but for the most part, we treat the BIOS like any other application code. - * - * As we expand support to include more programmable cards like the EGA, it becomes quite easy for the card - * to enter a "mode" that has no BIOS counterpart (eg, non-standard combinations of frame buffer address, - * memory access modes, fonts, display regions, etc). Our hardware emulation routines will cope with those - * situations as best they can (and when they don't, it should be considered a bug if some application is - * broken as a result), but realistically, our hardware emulation is never likely to be 100% accurate. - */ -Video.MODE = { - CGA_40X25_BW: 0, - CGA_40X25: 1, - CGA_80X25_BW: 2, - CGA_80X25: 3, - CGA_320X200: 4, - CGA_320X200_BW: 5, - CGA_640X200: 6, - MDA_80X25: 7, - EGA_320X200: 0x0D, // mapped at A000:0000 - EGA_640X200: 0x0E, // mapped at A000:0000 - EGA_640X350_MONO: 0x0F, // mapped at A000:0000, monochrome - EGA_640X350: 0x10, // mapped at A000:0000, color - VGA_640X480_MONO: 0x11, // mapped at A000:0000, monochrome - VGA_640X480: 0x12, // mapped at A000:0000, color - VGA_320X200: 0x13, // mapped at A000:0000, color - UNKNOWN: 0xFF -}; - /** * @class Font * @property {number} cxCell @@ -1121,7 +1121,7 @@ Card.CRTC = { HR: 0x80 // Hardware Reset }, LINE_COMPARE: 0x18, - TOTAL_REGS: 0x19 // total CRT registers on EGA + TOTAL_REGS: 0x19 // total CRT registers on EGA/VGA }, ADDR_HI_MASK: 0x3F }; @@ -1134,7 +1134,7 @@ if (DEBUGGER) { Card.CRTC.EGA_REGS = ["HORZ_TOTAL","HORZ_DISP_END","HORZ_BLANK_START","HORZ_BLANK_END","HORZ_RETRACE_START","HORZ_RETRACE_END", "VERT_TOTAL","CRTC_OVERFLOW","PRESET_ROW_SCAN","MAX_SCAN_LINE","CURSOR_START","CURSOR_END", - "START_ADDR_HI","START_ADDR_LO","CURSOR_ADDR_HI","CURSOR_ADDR_LO","LIGHT_PEN_HI","LIGHT_PEN_LO", + "START_ADDR_HI","START_ADDR_LO","CURSOR_ADDR_HI","CURSOR_ADDR_LO","VERT_RETRACE_START","VERT_RETRACE_END", "VERT_DISP_END","OFFSET","UNDERLINE","VERT_BLANK_START","VERT_BLANK_END","MODE_CTRL","LINE_COMPARE"]; } @@ -1352,7 +1352,8 @@ if (DEBUGGER) Card.SEQ.REGS = ["RESET","CLK","MAPMASK","CHARMAP","MODE"]; */ Card.DAC = { MASK: { - PORT: 0x3C6 // initialized to 0xFF and should not be changed + PORT: 0x3C6, // initialized to 0xFF and should not be changed + DEFAULT: 0xFF }, STATE: { PORT: 0x3C7, @@ -1365,15 +1366,16 @@ Card.DAC = { }, DATA: { PORT: 0x3C9 - } + }, + TOTAL_REGS: 0x100 }; /* * EGA/VGA Graphics Controller Registers (regGRCIndx and regGRCData) */ Card.GRC = { - POS1_PORT: 0x3CC, - POS2_PORT: 0x3CA, + POS1_PORT: 0x3CC, // EGA only, write-only + POS2_PORT: 0x3CA, // EGA only, write-only INDX: { PORT: 0x3CE, MASK: 0x0F @@ -1903,7 +1905,13 @@ Card.prototype.initEGA = function(data, nMonitorType) /*21*/ 0xffffffff|0, /*22*/ 0, /*23*/ 0, - /*24*/ 0 + /*24*/ 0, + /*25*/ Card.VGA_ENABLE.ENABLED, + /*26*/ Card.DAC.MASK.DEFAULT, + /*27*/ 0, + /*28*/ 0, + /*29*/ Card.DAC.STATE.MODE_WRITE, + /*30*/ new Array(Card.DAC.TOTAL_REGS) ]; } @@ -1969,7 +1977,15 @@ Card.prototype.initEGA = function(data, nMonitorType) this.nSetMapBits = data[22]; this.nColorCompare = data[23]; this.nColorDontCare = data[24]; - this.regVGAEnable = data[25]; + + if (this.nCard == Video.CARD.VGA) { + this.regVGAEnable = data[25]; + this.regDACMask = data[26]; + this.regDACAddr = data[27]; + this.regDACShift = data[28]; + this.regDACState = data[29]; + this.regDACData = data[30]; + } }; /** @@ -2030,7 +2046,15 @@ Card.prototype.saveEGA = function() data[22] = this.nSetMapBits; data[23] = this.nColorCompare; data[24] = this.nColorDontCare; - data[25] = this.regVGAEnable; + + if (this.nCard == Video.CARD.VGA) { + data[25] = this.regVGAEnable; + data[26] = this.regDACMask; + data[27] = this.regDACAddr; + data[28] = this.regDACShift; + data[29] = this.regDACState; + data[30] = this.regDACData; + } return data; }; @@ -4016,7 +4040,16 @@ Video.prototype.checkMode = function(fForce) // we've already defaulted to 0x0F or 0x10, so determine if it's 0x0D or 0x0E (ie, a 200-row mode) // and then which one (ie, 320 wide or 640 wide). // - if (nCRTCVertTotal < 350) nMode = (fSEQDotClock? Video.MODE.EGA_320X200 : Video.MODE.EGA_640X200); + if (nCRTCVertTotal > 480) { + nMode = Video.MODE.VGA_640X480; + } + else if (nCRTCVertTotal < 350) { + nMode = (fSEQDotClock? Video.MODE.EGA_320X200 : Video.MODE.EGA_640X200); + } + if (DEBUG && this.messageEnabled()) { + this.printMessage("checkMode(): nCRTCVertTotal=" + nCRTCVertTotal + ", mode=" + str.toHexByte(nMode)); + this.cpu.stopCPU(); + } } } } @@ -4074,7 +4107,7 @@ Video.prototype.setMode = function(nMode, fForce) if (nMode != null && (nMode != this.nMode || fForce)) { if (DEBUG && this.messageEnabled()) { - this.printMessage("setMode(" + str.toHexWord(nMode) + (fForce? ",force" : "") + ")"); + this.printMessage("setMode(" + str.toHexByte(nMode) + (fForce? ",force" : "") + ")"); } this.cUpdates = 0; // count updateScreen() calls as a means of driving blink updates @@ -4100,7 +4133,7 @@ Video.prototype.setMode = function(nMode, fForce) if (this.addrBuffer) { if (DEBUG && this.messageEnabled()) { - this.printMessage("setMode(" + nMode + "): removing " + str.toHexLong(this.sizeBuffer) + " bytes from " + str.toHexLong(this.addrBuffer)); + this.printMessage("setMode(" + str.toHexByte(nMode) + "): removing " + str.toHexLong(this.sizeBuffer) + " bytes from " + str.toHexLong(this.addrBuffer)); } if (!this.bus.removeMemory(this.addrBuffer, this.sizeBuffer)) { @@ -4119,7 +4152,7 @@ Video.prototype.setMode = function(nMode, fForce) this.sizeBuffer = card.sizeBuffer; if (DEBUG && this.messageEnabled()) { - this.printMessage("setMode(" + nMode + "): adding " + str.toHexLong(this.sizeBuffer) + " bytes to " + str.toHexLong(this.addrBuffer)); + this.printMessage("setMode(" + str.toHexByte(nMode) + "): adding " + str.toHexLong(this.sizeBuffer) + " bytes to " + str.toHexLong(this.addrBuffer)); } var controller = (card === this.cardEGA? card : null); @@ -4692,11 +4725,11 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit) * @this {Video} * @param {number} port (0x3B4) * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) - * @return {number} + * @return {number|undefined} */ Video.prototype.inMDAIndx = function(port, addrFrom) { - return this.inCRTCIndx(this.cardMono, addrFrom); + return this.inCRTCIndx(this.cardMono, port, addrFrom); }; /** @@ -4709,7 +4742,7 @@ Video.prototype.inMDAIndx = function(port, addrFrom) */ Video.prototype.outMDAIndx = function(port, bOut, addrFrom) { - this.outCRTCIndx(this.cardMono, bOut, addrFrom); + this.outCRTCIndx(this.cardMono, port, bOut, addrFrom); }; /** @@ -4722,7 +4755,7 @@ Video.prototype.outMDAIndx = function(port, bOut, addrFrom) */ Video.prototype.inMDAData = function(port, addrFrom) { - return this.inCRTCData(this.cardMono, addrFrom); + return this.inCRTCData(this.cardMono, port, addrFrom); }; /** @@ -4735,7 +4768,7 @@ Video.prototype.inMDAData = function(port, addrFrom) */ Video.prototype.outMDAData = function(port, bOut, addrFrom) { - this.outCRTCData(this.cardMono, bOut, addrFrom); + this.outCRTCData(this.cardMono, port, bOut, addrFrom); }; /** @@ -4982,6 +5015,138 @@ Video.prototype.outSEQData = function(port, bOut, addrFrom) } }; +/** + * inDACMask(port, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C6) + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + * @return {number} + */ +Video.prototype.inDACMask = function(port, addrFrom) +{ + var b = this.cardEGA.regDACMask; + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.MASK.PORT, null, addrFrom, "DAC.MASK", b); + } + return b; +}; + +/** + * outDACMask(port, bOut, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C6) + * @param {number} bOut + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + */ +Video.prototype.outDACMask = function(port, bOut, addrFrom) +{ + if (Video.TRAPALL || this.cardEGA.regDACMask !== bOut) { + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.MASK.PORT, bOut, addrFrom, "DAC.MASK"); + } + this.cardEGA.regDACMask = bOut; + } +}; + +/** + * inDACState(port, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C7) + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + * @return {number} + */ +Video.prototype.inDACState = function(port, addrFrom) +{ + var b = this.cardEGA.regDACState; + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.STATE.PORT, null, addrFrom, "DAC.STATE", b); + } + return b; +}; + +/** + * outDACRead(port, bOut, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C7) + * @param {number} bOut + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + */ +Video.prototype.outDACRead = function(port, bOut, addrFrom) +{ + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.ADDR.PORT_READ, bOut, addrFrom, "DAC.READ"); + } + this.cardEGA.regDACAddr = bOut; + this.cardEGA.regDACState = Card.DAC.STATE.MODE_READ; + this.cardEGA.regDACShift = 0; +}; + +/** + * outDACWrite(port, bOut, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C8) + * @param {number} bOut + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + */ +Video.prototype.outDACWrite = function(port, bOut, addrFrom) +{ + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.ADDR.PORT_WRITE, bOut, addrFrom, "DAC.WRITE"); + } + this.cardEGA.regDACAddr = bOut; + this.cardEGA.regDACState = Card.DAC.STATE.MODE_WRITE; + this.cardEGA.regDACShift = 0; +}; + +/** + * inDACData(port, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C9) + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + * @return {number} + */ +Video.prototype.inDACData = function(port, addrFrom) +{ + var b = (this.cardEGA.regDACData[this.cardEGA.regDACAddr] >> this.cardEGA.regDACShift) & 0x3f; + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.DATA.PORT, null, addrFrom, "DAC.DATA[" + str.toHexByte(this.cardEGA.regDACAddr) + "][" + str.toHexByte(this.cardEGA.regDACShift) + "]", b); + } + this.cardEGA.regDACShift += 6; + if (this.cardEGA.regDACShift > 12) { + this.cardEGA.regDACShift = 0; + this.cardEGA.regDACAddr = (this.cardEGA.regDACAddr + 1) & (Card.DAC.TOTAL_REGS-1); + } + return b; +}; + +/** + * outDACData(port, bOut, addrFrom) + * + * @this {Video} + * @param {number} port (0x3C9) + * @param {number} bOut + * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) + */ +Video.prototype.outDACData = function(port, bOut, addrFrom) +{ + var dw = this.cardEGA.regDACData[this.cardEGA.regDACAddr]; + if (this.messageEnabled()) { + this.printMessageIO(Card.DAC.DATA.PORT, bOut, addrFrom, "DAC.DATA[" + str.toHexByte(this.cardEGA.regDACAddr) + "][" + str.toHexByte(this.cardEGA.regDACShift) + "]"); + } + this.cardEGA.regDACData[this.cardEGA.regDACAddr] = (dw & ~(0x3f << this.cardEGA.regDACShift)) | ((bOut & 0x3f) << this.cardEGA.regDACShift); + this.cardEGA.regDACShift += 6; + if (this.cardEGA.regDACShift > 12) { + this.cardEGA.regDACShift = 0; + this.cardEGA.regDACAddr = (this.cardEGA.regDACAddr + 1) & (Card.DAC.TOTAL_REGS-1); + } +}; + /** * inVGAFeat(port, addrFrom) * @@ -5154,11 +5319,11 @@ Video.prototype.outGRCData = function(port, bOut, addrFrom) * @this {Video} * @param {number} port (0x3D4) * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) - * @return {number} + * @return {number|undefined} */ Video.prototype.inCGAIndx = function(port, addrFrom) { - return this.inCRTCIndx(this.cardColor, addrFrom); + return this.inCRTCIndx(this.cardColor, port, addrFrom); }; /** @@ -5171,7 +5336,7 @@ Video.prototype.inCGAIndx = function(port, addrFrom) */ Video.prototype.outCGAIndx = function(port, bOut, addrFrom) { - this.outCRTCIndx(this.cardColor, bOut, addrFrom); + this.outCRTCIndx(this.cardColor, port, bOut, addrFrom); }; /** @@ -5184,7 +5349,7 @@ Video.prototype.outCGAIndx = function(port, bOut, addrFrom) */ Video.prototype.inCGAData = function(port, addrFrom) { - return this.inCRTCData(this.cardColor, addrFrom); + return this.inCRTCData(this.cardColor, port, addrFrom); }; /** @@ -5197,7 +5362,7 @@ Video.prototype.inCGAData = function(port, addrFrom) */ Video.prototype.outCGAData = function(port, bOut, addrFrom) { - this.outCRTCData(this.cardColor, bOut, addrFrom); + this.outCRTCData(this.cardColor, port, bOut, addrFrom); }; /** @@ -5238,7 +5403,7 @@ Video.prototype.inCGAColor = function(port, addrFrom) { var b = this.cardColor.regColor; if (this.messageEnabled()) { - this.printMessageIO(this.cardColor.port + 5, null, addrFrom, this.cardColor.type + ".COLOR", b); + this.printMessageIO(port /* this.cardColor.port + 5 */, null, addrFrom, this.cardColor.type + ".COLOR", b); } return b; }; @@ -5254,7 +5419,7 @@ Video.prototype.inCGAColor = function(port, addrFrom) Video.prototype.outCGAColor = function(port, bOut, addrFrom) { if (this.messageEnabled()) { - this.printMessageIO(this.cardColor.port + 5, bOut, addrFrom, this.cardColor.type + ".COLOR"); + this.printMessageIO(port /* this.cardColor.port + 5 */, bOut, addrFrom, this.cardColor.type + ".COLOR"); } if (this.cardColor.regColor !== bOut) { this.cardColor.regColor = bOut; @@ -5280,67 +5445,92 @@ Video.prototype.inCGAStatus = function(port, addrFrom) }; /** - * inCRTCIndx(card, addrFrom) + * inCRTCIndx(card, port, addrFrom) * * @this {Video} * @param {Object} card + * @param {number} port * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) - * @return {number} + * @return {number|undefined} */ -Video.prototype.inCRTCIndx = function(card, addrFrom) +Video.prototype.inCRTCIndx = function(card, port, addrFrom) { - var b = card.regCRTIndx; - this.printMessageIO(card.port, null, addrFrom, "CRTC.INDX", b); + var b; + /* + * The IBM VGA ROM makes some hardware determinations based on how the CRTC controller responds when + * the IO_SELECT bit in the Miscellaneous Output Register is cleared; normally, that would mean ports + * 0x3B? are decoded and ports 0x3D? are ignored. We didn't used to bother ignoring them, but the + * VGA ROM's logic requires it, so now we also check fActive. However, we ignore only CTRC reads; + * we retain any writes in case that information proves useful later. + * + * Note that returning an undefined value now signals the Bus component to return whatever default value + * it prefers (normally 0xff). + */ + if (card.fActive) b = card.regCRTIndx; + this.printMessageIO(port, null, addrFrom, "CRTC.INDX", b); return b; }; /** - * outCRTCIndx(card, bOut, addrFrom) + * outCRTCIndx(card, port, bOut, addrFrom) * * @this {Video} * @param {Object} card + * @param {number} port * @param {number} bOut * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) */ -Video.prototype.outCRTCIndx = function(card, bOut, addrFrom) +Video.prototype.outCRTCIndx = function(card, port, bOut, addrFrom) { card.regCRTPrev = card.regCRTIndx; card.regCRTIndx = bOut & Card.CGA.CRTC.INDX.MASK; - this.printMessageIO(card.port, bOut, addrFrom, "CRTC.INDX"); + this.printMessageIO(port /* card.port */, bOut, addrFrom, "CRTC.INDX"); }; /** - * inCRTCData(card, addrFrom) + * inCRTCData(card, port, addrFrom) * * @this {Video} * @param {Object} card + * @param {number} port * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) * @return {number|undefined} */ -Video.prototype.inCRTCData = function(card, addrFrom) +Video.prototype.inCRTCData = function(card, port, addrFrom) { var b; - if (card.regCRTIndx < card.nCRTCRegs) b = card.regCRTData[card.regCRTIndx]; + /* + * The IBM VGA ROM makes some hardware determinations based on how the CRTC controller responds when + * the IO_SELECT bit in the Miscellaneous Output Register is cleared; normally, that would mean ports + * 0x3B? are decoded and ports 0x3D? are ignored. We didn't used to bother ignoring them, but the + * VGA ROM's logic requires it, so now we also check fActive. However, we ignore only CTRC reads; + * we retain any writes in case that information proves useful later. + * + * Note that returning an undefined value now signals the Bus component to return whatever default value + * it prefers (normally 0xff). + */ + if (card.fActive && card.regCRTIndx < card.nCRTCRegs) b = card.regCRTData[card.regCRTIndx]; if (this.messageEnabled()) { - this.printMessageIO(card.port + 1, null, addrFrom, "CRTC." + card.asCRTCRegs[card.regCRTIndx], b); + this.printMessageIO(port /* card.port + 1 */, null, addrFrom, "CRTC." + card.asCRTCRegs[card.regCRTIndx], b); } return b; }; /** - * outCRTCData(card, bOut, addrFrom) + * outCRTCData(card, port, bOut, addrFrom) * * @this {Video} * @param {Object} card + * @param {number} port * @param {number} bOut * @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port) */ -Video.prototype.outCRTCData = function(card, bOut, addrFrom) +Video.prototype.outCRTCData = function(card, port, bOut, addrFrom) { if (card.regCRTIndx < card.nCRTCRegs) { if (Video.TRAPALL || card.regCRTData[card.regCRTIndx] !== bOut) { if (this.messageEnabled()) { - this.printMessageIO(card.port + 1, bOut, addrFrom, "CRTC." + card.asCRTCRegs[card.regCRTIndx]); + this.printMessageIO(port /* card.port + 1 */, bOut, addrFrom, "CRTC." + card.asCRTCRegs[card.regCRTIndx]); } card.regCRTData[card.regCRTIndx] = bOut; } @@ -5571,12 +5761,19 @@ Video.aEGAPortOutput = { Video.aVGAPortInput = { 0x3C3: Video.prototype.inVGAEnable, + 0x3C6: Video.prototype.inDACMask, + 0x3C7: Video.prototype.inDACState, + 0x3C9: Video.prototype.inDACData, 0x3CA: Video.prototype.inVGAFeat, 0x3CC: Video.prototype.inVGAMisc }; Video.aVGAPortOutput = { - 0x3C3: Video.prototype.outVGAEnable + 0x3C3: Video.prototype.outVGAEnable, + 0x3C6: Video.prototype.outDACMask, + 0x3C7: Video.prototype.outDACRead, + 0x3C8: Video.prototype.outDACWrite, + 0x3C9: Video.prototype.outDACData }; /** diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index 16538ea4b..aaffccf36 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -2114,7 +2114,7 @@ X86CPU.prototype.rewindIP = function(dec) X86CPU.prototype.getSP = function() { if (I386) { - this.assert(!((this.regLSP - this.segSS.base) & ~this.segSS.addrMask)); + // assert(!((this.regLSP - this.segSS.base) & ~this.segSS.addrMask)); return (this.regESP & ~this.segSS.addrMask) | (this.regLSP - this.segSS.base); } return (this.regLSP - this.segSS.base)|0;