Fix EGA scrolling for modes 0x0D and 0x0E
This commit is contained in:
parent
aa98f63f47
commit
38f16fcc50
13 changed files with 3322 additions and 3077 deletions
180
blog/2015/06/05/README.md
Normal file
180
blog/2015/06/05/README.md
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
The Strange Case of the EGA Graphics Scroll Bug
|
||||||
|
---
|
||||||
|
|
||||||
|
For reasons I've forgotten, I was playing with different video modes in this
|
||||||
|
[IBM PC AT w/EGA](/devices/pc/machine/5170/ega/640kb/rev1/) machine, and I discovered some odd problems.
|
||||||
|
|
||||||
|
For example, run this code:
|
||||||
|
|
||||||
|
A>b:debug
|
||||||
|
-a
|
||||||
|
0CE0:0100 mov ax,e
|
||||||
|
0CE0:0103 int 10
|
||||||
|
0CE0:0105 int 3
|
||||||
|
0CE0:0106
|
||||||
|
-g
|
||||||
|
|
||||||
|
and the following "text" will appear 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
|
||||||
|
-
|
||||||
|
|
||||||
|
If I typed "q", then "cls" and finally "dir", the screen would properly fill with DOS directory contents.
|
||||||
|
However, 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 arrives as 0xC0 (192), which is the number of scan-lines to move up, and BX is 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 reference 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, GRC[MODE] has been 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 back farther 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, whenver 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 always supposed to 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
|
||||||
|
if 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,12 +1,18 @@
|
||||||
IBM PC Machine Configurations
|
IBM PC Machine Configurations
|
||||||
---
|
---
|
||||||
|
|
||||||
Here you'll find sample Machine Configurations for all the IBM PC models that PCjs currently supports.
|
PCjs supports the following IBM PC models:
|
||||||
|
|
||||||
* [Model 5150](/devices/pc/machine/#model-5150-machine-configurations)
|
* [Model 5150](/devices/pc/machine/#model-5150-machine-configurations)
|
||||||
* [Model 5160](/devices/pc/machine/#model-5160-machine-configurations)
|
* [Model 5160](/devices/pc/machine/#model-5160-machine-configurations)
|
||||||
* [Model 5170](/devices/pc/machine/#model-5170-machine-configurations)
|
* [Model 5170](/devices/pc/machine/#model-5170-machine-configurations)
|
||||||
|
|
||||||
|
Configurations for the [Challenger 1P](/devices/c1p/machine/) and other non-IBM machines are available [here](/devices/).
|
||||||
|
|
||||||
|
Support is also being added for a few IBM PC-compatible machines, starting with the [Compaq DeskPro 386](compaq/deskpro386/) series.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Model 5150 Machine Configurations
|
### Model 5150 Machine Configurations
|
||||||
|
|
||||||
* [IBM PC, MDA, 64K](/devices/pc/machine/5150/mda/64kb/)
|
* [IBM PC, MDA, 64K](/devices/pc/machine/5150/mda/64kb/)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -240,6 +240,8 @@ var asFilesNonServed = [
|
||||||
"web.config" // Azure/IISNode-specific
|
"web.config" // Azure/IISNode-specific
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var nBlogExcerpts = 20;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTMLOut()
|
* HTMLOut()
|
||||||
*
|
*
|
||||||
|
|
@ -1167,10 +1169,10 @@ HTMLOut.prototype.getBlog = function(sToken, sIndent, aParms)
|
||||||
* strings) that can hold the excerpts. We'll plug the excerpts into aExcerpts as they
|
* strings) that can hold the excerpts. We'll plug the excerpts into aExcerpts as they
|
||||||
* come in, and then we'll assemble them all at the end.
|
* come in, and then we'll assemble them all at the end.
|
||||||
*
|
*
|
||||||
* We also take this opportunity to cap the number of (most recent) excerpts at 10.
|
* We also take this opportunity to cap the number of (most recent) excerpts.
|
||||||
*/
|
*/
|
||||||
var i;
|
var i;
|
||||||
var cExcerpts = Math.min(asPaths.length, 10);
|
var cExcerpts = Math.min(asPaths.length, nBlogExcerpts);
|
||||||
var aExcerpts = new Array(cExcerpts);
|
var aExcerpts = new Array(cExcerpts);
|
||||||
var cExcerptsRemaining = aExcerpts.length;
|
var cExcerptsRemaining = aExcerpts.length;
|
||||||
for (i = 0; i < cExcerpts; i++) {
|
for (i = 0; i < cExcerpts; i++) {
|
||||||
|
|
|
||||||
|
|
@ -4499,7 +4499,7 @@ if (DEBUGGER) {
|
||||||
this.println("\ninput commands:");
|
this.println("\ninput commands:");
|
||||||
this.println("\ti [p]\tread port [p]");
|
this.println("\ti [p]\tread port [p]");
|
||||||
/*
|
/*
|
||||||
* NOTE: Regarding this warning, it might be nice if we had an "unchecked" version of
|
* TODO: Regarding this warning, consider adding an "unchecked" version of
|
||||||
* bus.checkPortInputNotify(), since all Debugger memory accesses are unchecked, too.
|
* bus.checkPortInputNotify(), since all Debugger memory accesses are unchecked, too.
|
||||||
*
|
*
|
||||||
* All port I/O handlers ARE aware when the Debugger is calling (addrFrom is undefined),
|
* All port I/O handlers ARE aware when the Debugger is calling (addrFrom is undefined),
|
||||||
|
|
@ -4796,7 +4796,7 @@ if (DEBUGGER) {
|
||||||
this.println("\noutput commands:");
|
this.println("\noutput commands:");
|
||||||
this.println("\to [p] [b]\twrite byte [b] to port [p]");
|
this.println("\to [p] [b]\twrite byte [b] to port [p]");
|
||||||
/*
|
/*
|
||||||
* NOTE: Regarding this warning, it might be nice if we had an "Unchecked" version of
|
* TODO: Regarding this warning, consider adding an "unchecked" version of
|
||||||
* bus.checkPortOutputNotify(), since all Debugger memory accesses are unchecked, too.
|
* bus.checkPortOutputNotify(), since all Debugger memory accesses are unchecked, too.
|
||||||
*
|
*
|
||||||
* All port I/O handlers ARE aware when the Debugger is calling (addrFrom is undefined),
|
* All port I/O handlers ARE aware when the Debugger is calling (addrFrom is undefined),
|
||||||
|
|
|
||||||
|
|
@ -1342,7 +1342,7 @@ Card.SEQ = {
|
||||||
SELB_HIGH: 0x10, // VGA only
|
SELB_HIGH: 0x10, // VGA only
|
||||||
SELA_HIGH: 0x20 // VGA only
|
SELA_HIGH: 0x20 // VGA only
|
||||||
},
|
},
|
||||||
MODE: {
|
MEMMODE: {
|
||||||
INDX: 0x04, // Sequencer Memory Mode Register
|
INDX: 0x04, // Sequencer Memory Mode Register
|
||||||
ALPHA: 0x01, // set for alphanumeric (A/N) mode, clear for graphics (APA or "All Points Addressable") mode (EGA only)
|
ALPHA: 0x01, // set for alphanumeric (A/N) mode, clear for graphics (APA or "All Points Addressable") mode (EGA only)
|
||||||
EXT: 0x02, // set if memory expansion installed, clear if not installed
|
EXT: 0x02, // set if memory expansion installed, clear if not installed
|
||||||
|
|
@ -1352,7 +1352,7 @@ Card.SEQ = {
|
||||||
TOTAL_REGS: 0x05
|
TOTAL_REGS: 0x05
|
||||||
};
|
};
|
||||||
|
|
||||||
if (DEBUGGER) Card.SEQ.REGS = ["RESET","CLOCKING","MAPMASK","CHARMAP","MODE"];
|
if (DEBUGGER) Card.SEQ.REGS = ["RESET","CLOCKING","MAPMASK","CHARMAP","MEMMODE"];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* VGA Digital-to-Analog Converter (DAC) Registers (regDACMask, regDACState, regDACAddr, and regDACData)
|
* VGA Digital-to-Analog Converter (DAC) Registers (regDACMask, regDACState, regDACAddr, and regDACData)
|
||||||
|
|
@ -1507,8 +1507,8 @@ if (DEBUGGER) Card.GRC.REGS = ["SRESET","ESRESET","COLORCMP","DATAROT","READMAP"
|
||||||
*
|
*
|
||||||
* Even/Odd Memory Access Functions
|
* Even/Odd Memory Access Functions
|
||||||
*
|
*
|
||||||
* The "EVENODD" functions deal with the EGA's default text-mode addressing, where every EVEN address is mapped to
|
* The "EVENODD" functions deal with the EGA's default text-mode addressing, where EVEN addresses are mapped to
|
||||||
* plane 0 (and plane 2) and every ODD address is mapped to plane 1 (and plane 3). This occurs when SEQ.MODE.SEQUENTIAL
|
* plane 0 (and 2) and ODD addresses are mapped to plane 1 (and 3). This occurs when SEQ.MEMMODE.SEQUENTIAL
|
||||||
* is clear (and GRC.MODE.EVENODD is set), turning address bit 0 (A0) into a "plane select" bit. Whether A0 is
|
* is clear (and GRC.MODE.EVENODD is set), turning address bit 0 (A0) into a "plane select" bit. Whether A0 is
|
||||||
* also used as a memory address bit depends on CRTC.MODE_CTRL.BM: if it's set, then we're in "Byte Mode" and A0 is
|
* also used as a memory address bit depends on CRTC.MODE_CTRL.BM: if it's set, then we're in "Byte Mode" and A0 is
|
||||||
* used as-is; if it's clear, then we're in "Word Mode", and either A15 (when CRTC.MODE_CTRL.AW is set) or A13
|
* used as-is; if it's clear, then we're in "Word Mode", and either A15 (when CRTC.MODE_CTRL.AW is set) or A13
|
||||||
|
|
@ -1599,6 +1599,11 @@ Card.ACCESS.readByteMode0 = function readByteMode0(off, addr)
|
||||||
*/
|
*/
|
||||||
Card.ACCESS.readByteMode0EvenOdd = function readByteMode0EvenOdd(off, addr)
|
Card.ACCESS.readByteMode0EvenOdd = function readByteMode0EvenOdd(off, addr)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* TODO: As discussed in getAccess(), we need to run some tests on real EGA/VGA hardware to determine
|
||||||
|
* exactly what gets latched (ie, from which address) when EVENODD is in effect. Whatever we learn may
|
||||||
|
* also dictate a special EVENODD function for Read Mode 1 as well.
|
||||||
|
*/
|
||||||
off += this.offset;
|
off += this.offset;
|
||||||
var idw = off & ~0x1;
|
var idw = off & ~0x1;
|
||||||
var dw = this.controller.latches = this.adw[idw];
|
var dw = this.controller.latches = this.adw[idw];
|
||||||
|
|
@ -1821,6 +1826,10 @@ Card.ACCESS.writeByteMode1 = function writeByteMode1(off, b, addr)
|
||||||
*/
|
*/
|
||||||
Card.ACCESS.writeByteMode1EvenOdd = function writeByteMode1EvenOdd(off, b, addr)
|
Card.ACCESS.writeByteMode1EvenOdd = function writeByteMode1EvenOdd(off, b, addr)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* TODO: As discussed in getAccess(), we need to run some tests on real EGA/VGA hardware to determine
|
||||||
|
* exactly where latches are written (ie, to which address) when EVENODD is in effect.
|
||||||
|
*/
|
||||||
off += this.offset;
|
off += this.offset;
|
||||||
//
|
//
|
||||||
// When even/odd addressing is enabled, nWriteMapMask must be cleared for planes 1 and 3 if
|
// When even/odd addressing is enabled, nWriteMapMask must be cleared for planes 1 and 3 if
|
||||||
|
|
@ -2225,9 +2234,9 @@ Card.prototype.dumpCard = function()
|
||||||
this.dumpRegs(" FEAT", this.regFeat);
|
this.dumpRegs(" FEAT", this.regFeat);
|
||||||
this.dumpRegs(" MISC", this.regMisc);
|
this.dumpRegs(" MISC", this.regMisc);
|
||||||
this.dumpRegs(" STATUS0", this.regStatus0);
|
this.dumpRegs(" STATUS0", this.regStatus0);
|
||||||
this.dumpRegs(" LATCHES", this.latches);
|
this.dbg.println(" LATCHES: 0x" + str.toHex(this.latches));
|
||||||
this.dbg.println(" ACCESS: " + str.toHexWord(this.nAccess));
|
this.dbg.println(" ACCESS: " + str.toHexWord(this.nAccess));
|
||||||
this.dbg.println("Use 'dump video buffer' to dump video memory");
|
this.dbg.println("Use 'dump video [addr]' to dump video memory");
|
||||||
/*
|
/*
|
||||||
* There are few more EGA regs we could dump, like GRCPos1, GRCPos2, but does anyone care?
|
* There are few more EGA regs we could dump, like GRCPos1, GRCPos2, but does anyone care?
|
||||||
*/
|
*/
|
||||||
|
|
@ -3836,9 +3845,38 @@ Video.prototype.getAccess = function()
|
||||||
if (regGRCMode & Card.GRC.MODE.READ_MODE1) {
|
if (regGRCMode & Card.GRC.MODE.READ_MODE1) {
|
||||||
nReadAccess = Card.ACCESS.READ.MODE1;
|
nReadAccess = Card.ACCESS.READ.MODE1;
|
||||||
}
|
}
|
||||||
if (regGRCMode & Card.GRC.MODE.EVENODD) {
|
/*
|
||||||
nReadAccess |= Card.ACCESS.READ.EVENODD;
|
* I discovered that when the IBM EGA ROM scrolls the screen in graphics modes 0x0D and 0x0E, it
|
||||||
nWriteAccess |= Card.ACCESS.WRITE.EVENODD;
|
* reprograms this register for WRITE_MODE1 (which is fine) *and* EVENODD (which is, um, very odd).
|
||||||
|
* Moreover, it does NOT make the complementary change to the SEQ.MEMMODE.SEQUENTIAL bit; under
|
||||||
|
* "normal" circumstances, those two bits are always supposed to programmed oppositely.
|
||||||
|
*
|
||||||
|
* Until I can perform some tests on real hardware, I have to assume that the EGA scroll operation
|
||||||
|
* is supposed to actually WORK in modes 0x0D and 0x0E, so I've decided to tie the trigger for my own
|
||||||
|
* EVENODD functions to SEQ.MEMMODE.SEQUENTIAL being clear, instead of GRC.MODE.EVENODD being set.
|
||||||
|
*
|
||||||
|
* It's also possible that my EVENODD read/write functions are not implemented properly; when EVENODD
|
||||||
|
* is in effect, which addresses get latched by a read, and to which addresses are latches written?
|
||||||
|
* If EVENODD has no effect on the effective address used with the latches, then I should change the
|
||||||
|
* EVENODD read/write functions accordingly.
|
||||||
|
*
|
||||||
|
* However, I've also done some limited testing with an emulated VGA running in text mode, and I've
|
||||||
|
* discovered that toggling the GRC.MODE.EVENODD bit *alone* doesn't seem to affect the delivery of
|
||||||
|
* text mode attributes from plane 1. So maybe this is the wiser change after all.
|
||||||
|
*
|
||||||
|
* TODO: Perform some tests on actual EGA/VGA hardware, to determine the proper course of action.
|
||||||
|
*
|
||||||
|
* if (regGRCMode & Card.GRC.MODE.EVENODD) {
|
||||||
|
* nReadAccess |= Card.ACCESS.READ.EVENODD;
|
||||||
|
* nWriteAccess |= Card.ACCESS.WRITE.EVENODD;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
var regSEQMode = card.regSEQData[Card.SEQ.MEMMODE.INDX];
|
||||||
|
if (regSEQMode != null) {
|
||||||
|
if (!(regSEQMode & Card.SEQ.MEMMODE.SEQUENTIAL)) {
|
||||||
|
nReadAccess |= Card.ACCESS.READ.EVENODD;
|
||||||
|
nWriteAccess |= Card.ACCESS.WRITE.EVENODD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nAccess = nReadAccess | nWriteAccess;
|
nAccess = nReadAccess | nWriteAccess;
|
||||||
}
|
}
|
||||||
|
|
@ -5157,8 +5195,15 @@ Video.prototype.outSEQData = function(port, bOut, addrFrom)
|
||||||
}
|
}
|
||||||
this.cardEGA.regSEQData[this.cardEGA.regSEQIndx] = bOut;
|
this.cardEGA.regSEQData[this.cardEGA.regSEQIndx] = bOut;
|
||||||
}
|
}
|
||||||
if (this.cardEGA.regSEQIndx == Card.SEQ.MAPMASK.INDX) {
|
switch(this.cardEGA.regSEQIndx) {
|
||||||
|
case Card.SEQ.MAPMASK.INDX:
|
||||||
this.cardEGA.nWriteMapMask = Video.aEGAByteToDW[bOut & Card.SEQ.MAPMASK.MAPS];
|
this.cardEGA.nWriteMapMask = Video.aEGAByteToDW[bOut & Card.SEQ.MAPMASK.MAPS];
|
||||||
|
break;
|
||||||
|
case Card.SEQ.MEMMODE.INDX:
|
||||||
|
this.setAccess(this.getAccess());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li><a href="/apps/pc/">Apps</a></li>
|
<li><a href="/apps/pc/">Apps</a></li>
|
||||||
<li><a href="/disks/pc/">Disks</a></li>
|
<li><a href="/disks/pc/">Disks</a></li>
|
||||||
<li><a href="/devices/">Machines</a></li>
|
<li><a href="/devices/pc/machine/">Machines</a></li>
|
||||||
<li><a href="/docs/">Docs</a></li>
|
<li><a href="/docs/">Docs</a></li>
|
||||||
<li><a href="/pubs/">Pubs</a></li>
|
<li><a href="/pubs/">Pubs</a></li>
|
||||||
<li><a href="/blog/">Blog</a></li>
|
<li><a href="/blog/">Blog</a></li>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li><a href="/apps/pc/">Apps</a></li>
|
<li><a href="/apps/pc/">Apps</a></li>
|
||||||
<li><a href="/disks/pc/">Disks</a></li>
|
<li><a href="/disks/pc/">Disks</a></li>
|
||||||
<li><a href="/devices/">Machines</a></li>
|
<li><a href="/devices/pc/machine/">Machines</a></li>
|
||||||
<li><a href="/docs/">Docs</a></li>
|
<li><a href="/docs/">Docs</a></li>
|
||||||
<li><a href="/pubs/">Pubs</a></li>
|
<li><a href="/pubs/">Pubs</a></li>
|
||||||
<li><a href="/blog/">Blog</a></li>
|
<li><a href="/blog/">Blog</a></li>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li><a href="/apps/pc/">Apps</a></li>
|
<li><a href="/apps/pc/">Apps</a></li>
|
||||||
<li><a href="/disks/pc/">Disks</a></li>
|
<li><a href="/disks/pc/">Disks</a></li>
|
||||||
<li><a href="/devices/">Machines</a></li>
|
<li><a href="/devices/pc/machine/">Machines</a></li>
|
||||||
<li><a href="/docs/">Docs</a></li>
|
<li><a href="/docs/">Docs</a></li>
|
||||||
<li><a href="/pubs/">Pubs</a></li>
|
<li><a href="/pubs/">Pubs</a></li>
|
||||||
<li><a href="/blog/">Blog</a></li>
|
<li><a href="/blog/">Blog</a></li>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li><a href="/apps/pc/">Apps</a></li>
|
<li><a href="/apps/pc/">Apps</a></li>
|
||||||
<li><a href="/disks/pc/">Disks</a></li>
|
<li><a href="/disks/pc/">Disks</a></li>
|
||||||
<li><a href="/devices/">Machines</a></li>
|
<li><a href="/devices/pc/machine/">Machines</a></li>
|
||||||
<li><a href="/docs/">Docs</a></li>
|
<li><a href="/docs/">Docs</a></li>
|
||||||
<li><a href="/pubs/">Pubs</a></li>
|
<li><a href="/pubs/">Pubs</a></li>
|
||||||
<li><a href="/blog/">Blog</a></li>
|
<li><a href="/blog/">Blog</a></li>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue