From d689753f3793b71fb28d62897e5b7742afe99a61 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Sun, 5 Jul 2015 10:24:32 -0700 Subject: [PATCH] More DAC support --- blog/2015/06/01/README.md | 122 +++++++++++----------- modules/pcjs/lib/video.js | 82 +++++++++------ tests/pc/vga/L31-1.ASM | 2 + tests/pc/vga/L32-1.ASM | 211 ++++++++++++++++++++++++++++++++++++++ tests/pc/vga/L32-2.C | 201 ++++++++++++++++++++++++++++++++++++ 5 files changed, 525 insertions(+), 93 deletions(-) create mode 100644 tests/pc/vga/L32-1.ASM create mode 100644 tests/pc/vga/L32-2.C diff --git a/blog/2015/06/01/README.md b/blog/2015/06/01/README.md index f1f42fb57..94de37d44 100644 --- a/blog/2015/06/01/README.md +++ b/blog/2015/06/01/README.md @@ -75,68 +75,68 @@ Another problem: during IBM VGA ROM initialization, a "missing readByte handler" Before debugging this though, I decided to first make a table of all the register values for most of the modes that the IBM VGA supports: - INT 0x10 Mode Requested: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x0D 0x0E 0x10 0x12 0x13 + INT 0x10 Mode Requested: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x0D 0x0E 0x10 0x12 0x13 - BIOSMODE: 0x01 0x01 0x03 0x03 0x04 0x04 0x06 0x12 0x12 0x12 0x12 0x13 - CRTC[0x00]: HORZ_TOTAL 0x2D 0x2D 0x5F 0x5F 0x2D 0x2D 0x5F 0x2D 0x5F 0x5F 0x5F 0x5F - CRTC[0x01]: HORZ_DISP_END 0x27 0x27 0x4F 0x4F 0x27 0x27 0x4F 0x27 0x4F 0x4F 0x4F 0x4F - CRTC[0x02]: HORZ_BLANK_START 0x28 0x28 0x50 0x50 0x28 0x28 0x50 0x28 0x50 0x50 0x50 0x50 - CRTC[0x03]: HORZ_BLANK_END 0x90 0x90 0x82 0x82 0x90 0x90 0x82 0x90 0x82 0x82 0x82 0x82 - CRTC[0x04]: HORZ_RETRACE_START 0x2B 0x2B 0x55 0x55 0x2B 0x2B 0x54 0x2B 0x54 0x54 0x54 0x54 - CRTC[0x05]: HORZ_RETRACE_END 0xA0 0xA0 0x81 0x81 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 - CRTC[0x06]: VERT_TOTAL 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_SCAN 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 - CRTC[0x09]: MAX_SCAN_LINE 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]: VERT_RETRACE_START 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x83 0xEA 0x9C - CRTC[0x11]: VERT_RETRACE_END 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x85 0x8C 0x8E - CRTC[0x12]: VERT_DISP_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 (bit 6 selects double-word mode) - CRTC[0x15]: VERT_BLANK_START 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x63 0xE7 0x96 - CRTC[0x16]: VERT_BLANK_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 (bit 6 selects byte mode) - 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]: HORZPAN 0x08 0x08 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + BIOSMODE: 0x01 0x01 0x03 0x03 0x04 0x04 0x06 0x12 0x12 0x12 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 (bit 6 selects double-word mode) + 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 (bit 6 selects byte mode) + 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 the process of building this table, I also discovered that INT 0x10 mode 0x0D display is garbled, and that all graphics modes >= 0x0D are misdetected as BIOSMODE 0x12. diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index 3e70f80c2..38bf52cf7 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -180,9 +180,9 @@ function Video(parmsVideo, canvas, context, textarea, container) /* * Instead of (re)allocating a new color array every time getCardColors() is called, we preallocate - * an array now and simply update the entries as needed. Note that for an EGA (or a VGA operating - * in an EGA-compatible mode), only the first 16 entries get used (derived from the ATC); only when - * a VGA is operating in an 8bpp mode are 256 entries used (derived from the DAC rather than the ATC). + * an array and simply update the entries as needed. Note that for an EGA (or a VGA operating in an + * EGA-compatible mode), only the first 16 entries get used (derived from the ATC); only when a VGA + * is operating in an 8bpp mode are 256 entries used (derived from the DAC rather than the ATC). */ this.aRGB = new Array(this.nCard == Video.CARD.VGA? 256 : 16); this.fRGBValid = false; // whenever this is false, it signals getCardColors() to rebuild aRGB @@ -1243,6 +1243,7 @@ Card.ATC = { INDX_PAL_ENABLE: 0x20, // must be clear when loading palette registers PALETTE: { INDX: 0x00, // 16 registers: 0x00 - 0x0F + MASK: 0x3f, BLUE: 0x01, GREEN: 0x02, RED: 0x04, @@ -1261,7 +1262,7 @@ Card.ATC = { RESERVED: 0x10, // bit 4: reserved PANCOMPAT: 0x20, // bit 5: set for pixel-panning compatibility PELWIDTH: 0x40, // bit 6: set for 256-color modes, clear for all other modes - COLORSEL: 0x80 // bit 7: set for P5,P4 mapped to bits 1,0 of the Color Select register + COLORSEL_ALL: 0x80 // bit 7: set to enable all COLORSEL bits (ie, COLORSEL.DAC_BIT5 and COLORSEL.DAC_BIT4) }, OVERSCAN: { INDX: 0x11 // ATC Overscan Color Register @@ -1272,16 +1273,16 @@ Card.ATC = { MUX: 0x30, RESERVED: 0xC0 }, - HORZPAN: { + HPAN: { INDX: 0x13, // ATC Horizontal PEL Panning Register SHIFT_LEFT: 0x0F // bits 0-3 indicate # of pixels to shift left }, COLORSEL: { INDX: 0x14, // ATC Color Select Register (VGA only) - S_COLOR_7: 0x08, // selects bit 7 of 8-bit color values sent to DAC (except 256-color modes) - S_COLOR_6: 0x04, // selects bit 6 of 8-bit color values sent to DAC (except 256-color modes) - S_COLOR_5: 0x02, // selects bit 5 of 8-bit color values sent to DAC - S_COLOR_4: 0x01 // selects bit 4 of 8-bit color values sent to DAC + DAC_BIT7: 0x08, // specifies bit 7 of DAC values (ignored in 256-color modes) + DAC_BIT6: 0x04, // specifies bit 6 of DAC values (ignored in 256-color modes) + DAC_BIT5: 0x02, // specifies bit 5 of DAC values (if ATC.MODE.COLORSEL_ALL is set; ignored in 256-color modes) + DAC_BIT4: 0x01 // specifies bit 4 of DAC values (if ATC.MODE.COLORSEL_ALL is set; ignored in 256-color modes) }, TOTAL_REGS: 0x14 }; @@ -1289,7 +1290,7 @@ Card.ATC = { if (DEBUGGER) { Card.ATC.REGS = ["PAL00","PAL01","PAL02","PAL03","PAL04","PAL05","PAL06","PAL07", "PAL08","PAL09","PAL0A","PAL0B","PAL0C","PAL0D","PAL0E","PAL0F", - "MODE","OVERSCAN","PLANES","HORZPAN"]; + "MODE","OVERSCAN","PLANES","HPAN"]; } /* @@ -1791,7 +1792,8 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b, addr) * because we would be using sequential video buffer locations, instead of multiples of 4, and would match how * pixels are stored in "Mode X". However, I don't think that's how CHAIN4 modes operate (although that still * needs to be confirmed, because multiple sources conflict on this point). TODO: Confirm CHAIN4 operation on - * actual VGA hardware. + * actual VGA hardware, including the extent to which ALU and other writeByteMode0() operations need to be + * folded into this. * * It probably doesn't matter that much, as long as both the read and write CHAIN4 functions decode their * addresses in exactly the same manner; we'd only get into trouble with software that "unchained" or @@ -1821,8 +1823,8 @@ Card.ACCESS.writeByteMode0Chain4 = function writeByteMode0Chain4(off, b, addr) * writeByteMode0Chain1(off, b, addr) * * TODO: Although this function is similar to writeByteMode0(), it's used only for 8bpp modes, so it remains to - * be seen how much of the former will need to be folded into this function; if it's everything, then we can eliminate - * this function and map CHAIN1 support to writeByteMode0(). + * be seen how much of the former will need to be folded into this; if it's everything, then we can eliminate this + * function and map CHAIN1 support to writeByteMode0(). * * @this {Memory} * @param {number} off @@ -3547,8 +3549,8 @@ Video.prototype.getCardColors = function(nBitsPerPixel) * Brown (0x16) White (0x17) * * The numbers in parentheses are the EGA ATC palette register values that the EGA BIOS uses for each - * color set; on an EGA, I synthesize a fake CGA regColor value, until I figure out exactly how the EGA - * simulates the CGA color palette. TODO: Figure it out. + * color set; on an EGA, I synthesize a fake CGA regColor value, until I (TODO:) figure out exactly how + * the EGA simulates the CGA color palette. */ var regColor = this.cardActive.regColor; if (this.cardActive === this.cardEGA) { @@ -3580,40 +3582,56 @@ Video.prototype.getCardColors = function(nBitsPerPixel) } if (!this.fRGBValid) { - var aRegs, i, bRed, bGreen, bBlue; + + var card = this.cardEGA; + var aDAC = card.regDACData; + var aRegs, i, dw, b, bRed, bGreen, bBlue; + if (nBitsPerPixel == 8) { /* - * The card must be a VGA, and it's using a (8bpp) mode that bypasses the ATC, so we need + * The card must be a VGA, and it's using an (8bpp) mode that bypasses the ATC, so we need * to pull RGB data exclusively from the 256-entry DAC; each entry contains 6-bit red, green, * and blue values packed into bits 0-5, 6-11, and 12-17, respectively, each of which we - * effectively shift left 2 bits, for a crude 6-to-8-bit conversion. + * effectively shift left 2 bits: a crude 6-to-8-bit color conversion. */ - aRegs = this.cardEGA.regDACData; for (i = 0; i < 256; i++) { - var dw = aRegs[i] || 0; + dw = aDAC[i] || 0; + this.assert(dw >= 0 && dw <= 0x3ffff); bRed = (dw << 2) & 0xfc; bGreen = (dw >> 4) & 0xfc; bBlue = (dw >> 10) & 0xfc; this.aRGB[i] = [bRed, bGreen, bBlue, 0xff]; } - this.fRGBValid = true; } else { /* * We need to pull RGB data from the ATC; moreover, if the ATC hasn't been initialized yet, * we go with a default EGA-compatible 16-color palette. - * - * TODO: If the card is really a VGA, the DAC plays a role as well; need to update this code. */ - aRegs = (this.cardEGA.regATCData[15] != null? this.cardEGA.regATCData : Video.aEGAPalDef); + var fDAC = (aDAC && aDAC[255]); + aRegs = (card.regATCData[15] != null? card.regATCData : Video.aEGAPalDef); for (i = 0; i < 16; i++) { - var b = aRegs[i] || 0; - bRed = (((b & 0x04)? 0xaa : 0) | ((b & 0x20)? 0x55 : 0)); - bGreen = (((b & 0x02)? 0xaa : 0) | ((b & 0x10)? 0x55 : 0)); - bBlue = (((b & 0x01)? 0xaa : 0) | ((b & 0x08)? 0x55 : 0)); + b = aRegs[i] & Card.ATC.PALETTE.MASK; + if (fDAC) { + b |= (card.regATCData[Card.ATC.COLORSEL.INDX] & (Card.ATC.COLORSEL.DAC_BIT7 | Card.ATC.COLORSEL.DAC_BIT6)) << 4; + if (card.regATCData[Card.ATC.MODE.INDX] & Card.ATC.MODE.COLORSEL_ALL) { + b &= ~0x30; + b |= (card.regATCData[Card.ATC.COLORSEL.INDX] & (Card.ATC.COLORSEL.DAC_BIT5 | Card.ATC.COLORSEL.DAC_BIT4)) << 4; + } + this.assert(b >= 0 && b <= 255); + dw = aDAC[b]; + this.assert(dw >= 0 && dw <= 0x3ffff); + bRed = (dw << 2) & 0xfc; + bGreen = (dw >> 4) & 0xfc; + bBlue = (dw >> 10) & 0xfc; + } else { + bRed = (((b & 0x04)? 0xaa : 0) | ((b & 0x20)? 0x55 : 0)); + bGreen = (((b & 0x02)? 0xaa : 0) | ((b & 0x10)? 0x55 : 0)); + bBlue = (((b & 0x01)? 0xaa : 0) | ((b & 0x08)? 0x55 : 0)); + } this.aRGB[i] = [bRed, bGreen, bBlue, 0xff]; } - this.fRGBValid = true; } + this.fRGBValid = true; } return this.aRGB; @@ -5174,7 +5192,7 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit) var x = 0, y = 0; var xDirty = this.nCols, xMaxDirty = 0, yDirty = this.nRows, yMaxDirty = 0; - var iPixelFirst = this.cardActive.regATCData[Card.ATC.HORZPAN.INDX] & Card.ATC.HORZPAN.SHIFT_LEFT; + var iPixelFirst = this.cardActive.regATCData[Card.ATC.HPAN.INDX] & Card.ATC.HPAN.SHIFT_LEFT; /* * TODO: What should happen if the card is programmed such that nColsLogical is LESS THAN nCols? */ @@ -5298,7 +5316,7 @@ Video.prototype.updateScreenGraphicsVGA = function(addrScreen, addrScreenLimit) var xDirty = this.nCols, xMaxDirty = 0, yDirty = this.nRows, yMaxDirty = 0; var cbInc = (this.cardActive.regSEQData[Card.SEQ.MEMMODE.INDX] & Card.SEQ.MEMMODE.CHAIN4)? 4 : 1; - var iPixelFirst = this.cardActive.regATCData[Card.ATC.HORZPAN.INDX] & Card.ATC.HORZPAN.SHIFT_LEFT; + var iPixelFirst = this.cardActive.regATCData[Card.ATC.HPAN.INDX] & Card.ATC.HPAN.SHIFT_LEFT; /* * TODO: What should happen if the card is programmed such that nColsLogical is LESS THAN nCols? */ @@ -5632,7 +5650,7 @@ Video.prototype.inStatus0 = function(port, addrFrom) * db 0x14,0x14,0x2D,0x00 * db 0x2D,0x2D,0x2D,0x00 * - * So I ensure happiness by setting SWSENSE unless any of the three 6-bit DAC values contain 0x2D. + * I ensure much happiness by setting SWSENSE unless any of the three 6-bit DAC values contain 0x2D. * * This hard-coded behavior assumes a color monitor. If you really want to simulate a monochrome monitor, * then the 1st array (above) must mismatch, and a different set of arrays must all match: diff --git a/tests/pc/vga/L31-1.ASM b/tests/pc/vga/L31-1.ASM index e2aa3072a..86620191c 100644 --- a/tests/pc/vga/L31-1.ASM +++ b/tests/pc/vga/L31-1.ASM @@ -199,6 +199,7 @@ Set320By400Mode proc near ; line twice. ; mov dx,CRTC_INDEX + IF 1 ; the following code can be disabled to test memory access in 320x200 mode -JP mov al,MAX_SCAN_LINE out dx,al inc dx @@ -206,6 +207,7 @@ Set320By400Mode proc near and al,not 1fh ;set maximum scan line = 0 out dx,al dec dx + ENDIF ; ; Change CRTC scanning from doubleword mode to byte mode, allowing ; the CRTC to scan more than 64K of video data. diff --git a/tests/pc/vga/L32-1.ASM b/tests/pc/vga/L32-1.ASM new file mode 100644 index 000000000..029b2e3b3 --- /dev/null +++ b/tests/pc/vga/L32-1.ASM @@ -0,0 +1,211 @@ +; C tiny/small/medium model-callable assembler +; subroutines to: +; * Set 360x480 256-color VGA mode +; * Draw a dot in 360x480 256-color VGA mode +; * Read the color of a dot in 360x480 256-color VGA mode +; +; Assembled with TASM +; +; The 360x480 256-color mode set code and parameters were provided +; by John Bridges, who has placed them into the public domain. +; +VGA_SEGMENT equ 0a000h ;display memory segment +SC_INDEX equ 3c4h ;Sequence Controller Index register +GC_INDEX equ 3ceh ;Graphics Controller Index register +MAP_MASK equ 2 ;Map Mask register index in SC +READ_MAP equ 4 ;Read Map register index in GC +SCREEN_WIDTH equ 360 ;# of pixels across screen +WORD_OUTS_OK equ 1 ;set to 0 to assemble for + ; computers that can't handle + ; word outs to indexed VGA registers +; +_DATA segment public byte 'DATA' +; +; 360x480 256-color mode CRT Controller register settings. +; (Courtesy of John Bridges.) +; +vptbl dw 06b00h ; horz total + dw 05901h ; horz displayed + dw 05a02h ; start horz blanking + dw 08e03h ; end horz blanking + dw 05e04h ; start h sync + dw 08a05h ; end h sync + dw 00d06h ; vertical total + dw 03e07h ; overflow + dw 04009h ; cell height + dw 0ea10h ; v sync start + dw 0ac11h ; v sync end and protect cr0-cr7 + dw 0df12h ; vertical displayed + dw 02d13h ; offset + dw 00014h ; turn off dword mode + dw 0e715h ; v blank start + dw 00616h ; v blank end + dw 0e317h ; turn on byte mode +vpend label word +_DATA ends +; +; Macro to output a word value to a port. +; +OUT_WORD macro +if WORD_OUTS_OK + out dx,ax +else + out dx,al + inc dx + xchg ah,al + out dx,al + dec dx + xchg ah,al +endif + endm +; +_TEXT segment byte public 'CODE' + assume cs:_TEXT, ds:_DATA +; +; Sets up 360x480 256-color mode. +; (Courtesy of John Bridges.) +; +; Call as: void Set360By480Mode() +; +; Returns: nothing +; + public _Set360x480Mode +_Set360x480Mode proc near + push si ;preserve C register vars + push di + mov ax,12h ; start with mode 12h + int 10h ; let the bios clear the video memory + + mov ax,13h ; start with standard mode 13h + int 10h ; let the bios set the mode + + mov dx,3c4h ; alter sequencer registers + mov ax,0604h ; disable chain 4 + out dx,ax + + mov ax,0100h ; synchronous reset + out dx,ax ; asserted + mov dx,3c2h ; misc output + mov al,0e7h ; use 28 mHz dot clock + out dx,al ; select it + mov dx,3c4h ; sequencer again + mov ax,0300h ; restart sequencer + out dx,ax ; running again + + mov dx,3d4h ; alter crtc registers + + mov al,11h ; cr11 + out dx,al ; current value + inc dx ; point to data + in al,dx ; get cr11 value + and al,7fh ; remove cr0 -> cr7 + out dx,al ; write protect + dec dx ; point to index + cld + mov si,offset vptbl + mov cx,((offset vpend)-(offset vptbl)) shr 1 +@b: lodsw + out dx,ax + loop @b + pop di ;restore C register vars + pop si + ret +_Set360x480Mode endp +; +; Draws a pixel in the specified color at the specified +; location in 360x480 256-color mode. +; +; Call as: void Draw360x480Dot(int X, int Y, int Color) +; +; Returns: nothing +; +DParms struc + dw ? ;pushed BP + dw ? ;return address +DrawX dw ? ;X coordinate at which to draw +DrawY dw ? ;Y coordinate at which to draw +Color dw ? ;color in which to draw (in the + ; range 0-255; upper byte ignored) +DParms ends +; + public _Draw360x480Dot +_Draw360x480Dot proc near + push bp ;preserve caller's BP + mov bp,sp ;point to stack frame + push si ;preserve C register vars + push di + mov ax,VGA_SEGMENT + mov es,ax ;point to display memory + mov ax,SCREEN_WIDTH/4 + ;there are 4 pixels at each address, so + ; each 360-pixel row is 90 bytes wide + ; in each plane + mul [bp+DrawY] ;point to start of desired row + mov di,[bp+DrawX] ;get the X coordinate + shr di,1 ;there are 4 pixels at each address + shr di,1 ; so divide the X coordinate by 4 + add di,ax ;point to the pixel's address + mov cl,byte ptr [bp+DrawX] ;get the X coordinate again + and cl,3 ;get the plane # of the pixel + mov ah,1 + shl ah,cl ;set the bit corresponding to the plane + ; the pixel is in + mov al,MAP_MASK + mov dx,SC_INDEX + OUT_WORD ;set to write to the proper plane for + ; the pixel + mov al,byte ptr [bp+Color] ;get the color + stosb ;draw the pixel + pop di ;restore C register vars + pop si + pop bp ;restore caller's BP + ret +_Draw360x480Dot endp +; +; Reads the color of the pixel at the specified +; location in 360x480 256-color mode. +; +; Call as: int Read360x480Dot(int X, int Y) +; +; Returns: pixel color +; +RParms struc + dw ? ;pushed BP + dw ? ;return address +ReadX dw ? ;X coordinate from which to read +ReadY dw ? ;Y coordinate from which to read +RParms ends +; + public _Read360x480Dot +_Read360x480Dot proc near + push bp ;preserve caller's BP + mov bp,sp ;point to stack frame + push si ;preserve C register vars + push di + mov ax,VGA_SEGMENT + mov es,ax ;point to display memory + mov ax,SCREEN_WIDTH/4 + ;there are 4 pixels at each address, so + ; each 360-pixel row is 90 bytes wide + ; in each plane + mul [bp+DrawY] ;point to start of desired row + mov si,[bp+DrawX] ;get the X coordinate + shr si,1 ;there are 4 pixels at each address + shr si,1 ; so divide the X coordinate by 4 + add si,ax ;point to the pixel's address + mov ah,byte ptr [bp+DrawX] + ;get the X coordinate again + and ah,3 ;get the plane # of the pixel + mov al,READ_MAP + mov dx,GC_INDEX + OUT_WORD ;set to read from the proper plane for + ; the pixel + lods byte ptr es:[si];read the pixel + sub ah,ah ;make the return value a word for C + pop di ;restore C register vars + pop si + pop bp ;restore caller's BP + ret +_Read360x480Dot endp +_TEXT ends + end diff --git a/tests/pc/vga/L32-2.C b/tests/pc/vga/L32-2.C new file mode 100644 index 000000000..c9197adeb --- /dev/null +++ b/tests/pc/vga/L32-2.C @@ -0,0 +1,201 @@ +/* + * Sample program to illustrate VGA line drawing in 360x480 + * 256-color mode. + * + * Compiled with Borland C/C++. + * + * Must be linked with Listing 32.1 with a command line like: + * + * bcc l32-2.c l32-1.asm + * + * By Michael Abrash + */ +#include /* contains geninterrupt */ + +#define TEXT_MODE 0x03 +#define BIOS_VIDEO_INT 0x10 +#define X_MAX 360 /* working screen width */ +#define Y_MAX 480 /* working screen height */ + +extern void Draw360x480Dot(); +extern void Set360x480Mode(); + +/* + * Draws a line in octant 0 or 3 ( |DeltaX| >= DeltaY ). + * |DeltaX|+1 points are drawn. + */ +void Octant0(X0, Y0, DeltaX, DeltaY, XDirection, Color) +unsigned int X0, Y0; /* coordinates of start of the line */ +unsigned int DeltaX, DeltaY; /* length of the line */ +int XDirection; /* 1 if line is drawn left to right, + -1 if drawn right to left */ +int Color; /* color in which to draw line */ +{ + int DeltaYx2; + int DeltaYx2MinusDeltaXx2; + int ErrorTerm; + + /* Set up initial error term and values used inside drawing loop */ + DeltaYx2 = DeltaY * 2; + DeltaYx2MinusDeltaXx2 = DeltaYx2 - (int) ( DeltaX * 2 ); + ErrorTerm = DeltaYx2 - (int) DeltaX; + + /* Draw the line */ + Draw360x480Dot(X0, Y0, Color); /* draw the first pixel */ + while ( DeltaX-- ) { + /* See if it's time to advance the Y coordinate */ + if ( ErrorTerm >= 0 ) { + /* Advance the Y coordinate & adjust the error term + back down */ + Y0++; + ErrorTerm += DeltaYx2MinusDeltaXx2; + } else { + /* Add to the error term */ + ErrorTerm += DeltaYx2; + } + X0 += XDirection; /* advance the X coordinate */ + Draw360x480Dot(X0, Y0, Color); /* draw a pixel */ + } +} + +/* + * Draws a line in octant 1 or 2 ( |DeltaX| < DeltaY ). + * |DeltaY|+1 points are drawn. + */ +void Octant1(X0, Y0, DeltaX, DeltaY, XDirection, Color) +unsigned int X0, Y0; /* coordinates of start of the line */ +unsigned int DeltaX, DeltaY; /* length of the line */ +int XDirection; /* 1 if line is drawn left to right, + -1 if drawn right to left */ +int Color; /* color in which to draw line */ +{ + int DeltaXx2; + int DeltaXx2MinusDeltaYx2; + int ErrorTerm; + + /* Set up initial error term and values used inside drawing loop */ + DeltaXx2 = DeltaX * 2; + DeltaXx2MinusDeltaYx2 = DeltaXx2 - (int) ( DeltaY * 2 ); + ErrorTerm = DeltaXx2 - (int) DeltaY; + + Draw360x480Dot(X0, Y0, Color); /* draw the first pixel */ + while ( DeltaY-- ) { + /* See if it's time to advance the X coordinate */ + if ( ErrorTerm >= 0 ) { + /* Advance the X coordinate & adjust the error term + back down */ + X0 += XDirection; + ErrorTerm += DeltaXx2MinusDeltaYx2; + } else { + /* Add to the error term */ + ErrorTerm += DeltaXx2; + } + Y0++; /* advance the Y coordinate */ + Draw360x480Dot(X0, Y0,Color); /* draw a pixel */ + } +} + +/* + * Draws a line on the EGA or VGA. + */ +void EVGALine(X0, Y0, X1, Y1, Color) +int X0, Y0; /* coordinates of one end of the line */ +int X1, Y1; /* coordinates of the other end of the line */ +unsigned char Color; /* color in which to draw line */ +{ + int DeltaX, DeltaY; + int Temp; + + /* Save half the line-drawing cases by swapping Y0 with Y1 + and X0 with X1 if Y0 is greater than Y1. As a result, DeltaY + is always > 0, and only the octant 0-3 cases need to be + handled. */ + if ( Y0 > Y1 ) { + Temp = Y0; + Y0 = Y1; + Y1 = Temp; + Temp = X0; + X0 = X1; + X1 = Temp; + } + + /* Handle as four separate cases, for the four octants in which + Y1 is greater than Y0 */ + DeltaX = X1 - X0; /* calculate the length of the line + in each coordinate */ + DeltaY = Y1 - Y0; + if ( DeltaX > 0 ) { + if ( DeltaX > DeltaY ) { + Octant0(X0, Y0, DeltaX, DeltaY, 1, Color); + } else { + Octant1(X0, Y0, DeltaX, DeltaY, 1, Color); + } + } else { + DeltaX = -DeltaX; /* absolute value of DeltaX */ + if ( DeltaX > DeltaY ) { + Octant0(X0, Y0, DeltaX, DeltaY, -1, Color); + } else { + Octant1(X0, Y0, DeltaX, DeltaY, -1, Color); + } + } +} + +/* + * Subroutine to draw a rectangle full of vectors, of the + * specified length and in varying colors, around the + * specified rectangle center. + */ +void VectorsUp(XCenter, YCenter, XLength, YLength) +int XCenter, YCenter; /* center of rectangle to fill */ +int XLength, YLength; /* distance from center to edge + of rectangle */ +{ + int WorkingX, WorkingY, Color = 1; + + /* Lines from center to top of rectangle */ + WorkingX = XCenter - XLength; + WorkingY = YCenter - YLength; + for ( ; WorkingX < ( XCenter + XLength ); WorkingX++ ) + EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++); + + /* Lines from center to right of rectangle */ + WorkingX = XCenter + XLength - 1; + WorkingY = YCenter - YLength; + for ( ; WorkingY < ( YCenter + YLength ); WorkingY++ ) + EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++); + + /* Lines from center to bottom of rectangle */ + WorkingX = XCenter + XLength - 1; + WorkingY = YCenter + YLength - 1; + for ( ; WorkingX >= ( XCenter - XLength ); WorkingX-- ) + EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++); + + /* Lines from center to left of rectangle */ + WorkingX = XCenter - XLength; + WorkingY = YCenter + YLength - 1; + for ( ; WorkingY >= ( YCenter - YLength ); WorkingY-- ) + EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++); +} + +/* + * Sample program to draw four rectangles full of lines. + */ +void main() +{ + char temp; + + Set360x480Mode(); + + /* Draw each of four rectangles full of vectors */ + VectorsUp(X_MAX / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 1); + VectorsUp(X_MAX * 3 / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 2); + VectorsUp(X_MAX / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 3); + VectorsUp(X_MAX * 3 / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 4); + + /* Wait for the enter key to be pressed */ + scanf("%c", &temp); + + /* Back to text mode */ + _AX = TEXT_MODE; + geninterrupt(BIOS_VIDEO_INT); +}