Files
86Box/src/video/vid_pgc.c

2733 lines
60 KiB
C
Raw Normal View History

Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* This implements just enough of the Professional Graphics
* Controller to act as a basis for the Vermont Microsystems
* IM-1024.
*
* PGC features implemented include:
* > The CGA-compatible display modes
* > Switching to and from native mode
* > Communicating with the host PC
*
* Numerous features are implemented partially or not at all,
* such as:
* > 2D drawing
* > 3D drawing
* > Command lists
* Some of these are marked TODO.
*
* The PGC has two display modes: CGA (in which it appears in
* the normal CGA memory and I/O ranges) and native (in which
* all functions are accessed through reads and writes to 1K
* of memory at 0xC6000).
*
* The PGC's 8088 processor monitors this buffer and executes
* instructions left there for it. We simulate this behavior
* with a separate thread.
*
* **NOTE** This driver is not finished yet:
*
* - cursor will blink at very high speed if used on a machine
* with clock greater than 4.77MHz. We should "scale down"
* this speed, to become relative to a 4.77MHz-based system.
*
* - pgc_plot() should be overloaded by clones if they support
* modes other than WRITE and INVERT, like the IM-1024.
*
* - test it with the Windows 1.x driver?
*
* This is expected to be done shortly.
*
2020-03-25 00:46:02 +02:00
*
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* John Elliott, <jce@seasip.info>
*
* Copyright 2019 Fred N. van Kempen.
* Copyright 2019 John Elliott.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <ctype.h>
#include <math.h>
#define HAVE_STDARG_H
2020-02-29 19:12:23 +01:00
#include "86box.h"
#include "86box_io.h"
#include "mem.h"
#include "rom.h"
#include "timer.h"
#include "device.h"
#include "pit.h"
#include "plat.h"
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
#include "video.h"
#include "vid_cga.h"
#include "vid_pgc.h"
#define PGC_CGA_WIDTH 640
#define PGC_CGA_HEIGHT 400
#define HWORD(u) ((u) >> 16)
#define LWORD(u) ((u) & 0xffff)
#define WAKE_DELAY (TIMER_USEC * 500)
static const char *pgc_err_msgs[] = {
"Range \r",
"Integer \r",
"Memory \r",
"Overflow\r",
"Digit \r",
"Opcode \r",
"Running \r",
"Stack \r",
"Too long\r",
"Area \r",
2020-01-15 04:58:28 +01:00
"Missing \r",
"Unknown \r"
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
};
/* Initial palettes */
static const uint32_t init_palette[6][256] = {
#include "vid_pgc_palette.h"
};
static video_timings_t timing_pgc = {VIDEO_ISA, 8, 16, 32, 8, 16, 32};
#ifdef ENABLE_PGC_LOG
int pgc_do_log = ENABLE_PGC_LOG;
static void
pgc_log(const char *fmt, ...)
{
va_list ap;
if (pgc_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define pgc_log(fmt, ...)
#endif
static inline int
is_whitespace(char ch)
{
return (ch != 0 && strchr(" \r\n\t,;()+-", ch) != NULL);
}
/*
* Write a byte to the output buffer.
*
* If buffer is full will sleep until it is not. Returns 0 if
* a PGC reset has been triggered by a write to 0xC63FF.
*/
static int
output_byte(pgc_t *dev, uint8_t val)
{
/* If output buffer full, wait for it to empty. */
while (!dev->stopped && dev->mapram[0x302] == (uint8_t)(dev->mapram[0x303] - 1)) {
pgc_log("PGC: output buffer state: %02x %02x Sleeping\n",
dev->mapram[0x302], dev->mapram[0x303]);
dev->waiting_output_fifo = 1;
pgc_sleep(dev);
}
if (dev->mapram[0x3ff]) {
/* Reset triggered. */
pgc_reset(dev);
return 0;
}
dev->mapram[0x100 + dev->mapram[0x302]] = val;
dev->mapram[0x302]++;
pgc_log("PGC: output %02x: new state: %02x %02x\n", val,
dev->mapram[0x302], dev->mapram[0x303]);
return 1;
}
/* Helper to write an entire string to the output buffer. */
static int
output_string(pgc_t *dev, const char *s)
{
while (*s) {
if (! output_byte(dev, *s)) return 0;
s++;
}
return 1;
}
/* As output_byte, for the error buffer. */
static int
error_byte(pgc_t *dev, uint8_t val)
{
/* If error buffer full, wait for it to empty. */
while (!dev->stopped && dev->mapram[0x304] == dev->mapram[0x305] - 1) {
dev->waiting_error_fifo = 1;
pgc_sleep(dev);
}
if (dev->mapram[0x3ff]) {
/* Reset triggered. */
pgc_reset(dev);
return 0;
}
dev->mapram[0x200 + dev->mapram[0x304]] = val;
dev->mapram[0x304]++;
return 1;
}
/* As output_string, for the error buffer. */
static int
error_string(pgc_t *dev, const char *s)
{
while (*s) {
if (! error_byte(dev, *s)) return 0;
s++;
}
return 1;
}
/*
* Read next byte from the input buffer.
*
* If no byte available will sleep until one is. Returns 0 if
* a PGC reset has been triggered by a write to 0xC63FF.
*/
static int
input_byte(pgc_t *dev, uint8_t *result)
{
/* If input buffer empty, wait for it to fill. */
while (!dev->stopped && (dev->mapram[0x300] == dev->mapram[0x301])) {
dev->waiting_input_fifo = 1;
pgc_sleep(dev);
}
if (dev->stopped)
return 0;
if (dev->mapram[0x3ff]) {
/* Reset triggered. */
pgc_reset(dev);
return 0;
}
*result = dev->mapram[dev->mapram[0x301]];
dev->mapram[0x301]++;
return 1;
}
/*
* Read a byte and interpret as ASCII.
*
* Ignore control characters other than CR, LF or tab.
*/
static int
input_char(pgc_t *dev, char *result)
{
uint8_t ch;
while (1) {
if (! dev->inputbyte(dev, &ch)) return 0;
ch &= 0x7f;
if (ch == '\r' || ch == '\n' || ch == '\t' || ch >= ' ') {
*result = toupper(ch);
return 1;
}
}
}
/*
* Read in the next command.
*
* This can be either as hex (1 byte) or ASCII (up to 6 characters).
*/
static int
read_command(pgc_t *dev)
{
if (dev->stopped)
return 0;
if (dev->clcur)
return pgc_clist_byte(dev, &dev->hex_command);
if (dev->ascii_mode) {
char ch;
int count = 0;
while (count < 7) {
if (dev->stopped) return 0;
if (! input_char(dev, &ch)) return 0;
if (is_whitespace(ch)) {
/* Pad to 6 characters */
while (count < 6)
dev->asc_command[count++] = ' ';
dev->asc_command[6] = 0;
return 1;
}
dev->asc_command[count++] = toupper(ch);
}
return 1;
}
return dev->inputbyte(dev, &dev->hex_command);
}
/* Read in the next command and parse it. */
static int
parse_command(pgc_t *dev, const pgc_cmd_t **pcmd)
{
const pgc_cmd_t *cmd;
char match[7];
*pcmd = NULL;
dev->hex_command = 0;
memset(dev->asc_command, ' ', 6);
dev->asc_command[6] = 0;
if (! read_command(dev)) {
/* PGC has been reset. */
return 0;
}
/*
* Scan the list of valid commands.
*
* dev->commands may be a subclass list (terminated with '*')
* or the core list (terminated with '@')
*/
for (cmd = dev->commands; cmd->ascii[0] != '@'; cmd++) {
/* End of subclass command list, chain to core. */
if (cmd->ascii[0] == '*')
cmd = dev->master;
/* If in ASCII mode match on the ASCII command. */
if (dev->ascii_mode && !dev->clcur) {
sprintf(match, "%-6.6s", cmd->ascii);
if (! strncmp(match, dev->asc_command, 6)) {
*pcmd = cmd;
dev->hex_command = cmd->hex;
break;
}
} else {
/* Otherwise match on the hex command. */
if (cmd->hex == dev->hex_command) {
sprintf(dev->asc_command, "%-6.6s", cmd->ascii);
*pcmd = cmd;
break;
}
}
}
return 1;
}
/*
* Beginning of a command list.
*
* Parse commands up to the next CLEND, storing
* them (in hex form) in the named command list.
*/
static void
hndl_clbeg(pgc_t *dev)
{
const pgc_cmd_t *cmd;
uint8_t param = 0;
pgc_cl_t cl;
if (! pgc_param_byte(dev, &param)) return;
pgc_log("PGC: CLBEG(%i)\n", param);
memset(&cl, 0x00, sizeof(pgc_cl_t));
while (1) {
if (! parse_command(dev, &cmd)) {
/* PGC has been reset. */
return;
}
if (!cmd) {
pgc_error(dev, PGC_ERROR_OPCODE);
return;
} else if (dev->hex_command == 0x71) {
/* CLEND */
dev->clist[param] = cl;
return;
} else {
if (! pgc_cl_append(&cl, dev->hex_command)) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
return;
}
if (cmd->parser) {
if (! (*cmd->parser)(dev, &cl, cmd->p))
return;
}
}
}
}
static void
hndl_clend(pgc_t *dev)
{
/* Should not happen outside a CLBEG. */
}
/*
* Execute a command list.
*
* If one was already executing, remember
* it so we can return to it afterwards.
*/
static void
hndl_clrun(pgc_t *dev)
{
pgc_cl_t *clprev = dev->clcur;
uint8_t param = 0;
if (! pgc_param_byte(dev, &param)) return;
dev->clcur = &dev->clist[param];
dev->clcur->rdptr = 0;
dev->clcur->repeat = 1;
dev->clcur->chain = clprev;
}
/* Execute a command list multiple times. */
static void
hndl_cloop(pgc_t *dev)
{
pgc_cl_t *clprev = dev->clcur;
uint8_t param = 0;
int16_t repeat = 0;
if (! pgc_param_byte(dev, &param)) return;
if (! pgc_param_word(dev, &repeat)) return;
dev->clcur = &dev->clist[param];
dev->clcur->rdptr = 0;
dev->clcur->repeat = repeat;
dev->clcur->chain = clprev;
}
/* Read back a command list. */
static void
hndl_clread(pgc_t *dev)
{
uint8_t param = 0;
uint32_t n;
if (! pgc_param_byte(dev, &param)) return;
for (n = 0; n < dev->clist[param].wrptr; n++) {
if (! pgc_result_byte(dev, dev->clist[param].list[n]))
return;
}
}
/* Delete a command list. */
static void
hndl_cldel(pgc_t *dev)
{
uint8_t param = 0;
if (! pgc_param_byte(dev, &param)) return;
memset(&dev->clist[param], 0, sizeof(pgc_cl_t));
}
/* Clear the screen to a specified color. */
static void
hndl_clears(pgc_t *dev)
{
uint8_t param = 0;
uint32_t y;
if (! pgc_param_byte(dev, &param)) return;
for (y = 0; y < dev->screenh; y++)
memset(dev->vram + y * dev->maxw, param, dev->screenw);
}
/* Select drawing color. */
static void
hndl_color(pgc_t *dev)
{
uint8_t param = 0;
if (! pgc_param_byte(dev, &param)) return;
pgc_log("PGC: COLOR(%i)\n", param);
dev->color = param;
}
/*
* Set drawing mode.
*
* 0 => Draw
* 1 => Invert
*/
static void
hndl_linfun(pgc_t *dev)
{
uint8_t param = 0;
if (! pgc_param_byte(dev, &param)) return;
pgc_log("PGC: LINFUN(%i)\n", param);
if (param < 2)
dev->draw_mode = param;
else
pgc_error(dev, PGC_ERROR_RANGE);
}
/* Set the line drawing pattern. */
static void
hndl_linpat(pgc_t *dev)
{
uint16_t param = 0;
if (! pgc_param_word(dev, (int16_t *)&param)) return;
pgc_log("PGC: LINPAT(0x%04x)\n", param);
dev->line_pattern = param;
}
/* Set the polygon fill mode (0=hollow, 1=filled, 2=fast fill). */
static void
hndl_prmfil(pgc_t *dev)
{
uint8_t param = 0;
if (! pgc_param_byte(dev, &param)) return;
pgc_log("PGC: PRMFIL(%i)\n", param);
if (param < 3)
dev->fill_mode = param;
else
pgc_error(dev, PGC_ERROR_RANGE);
}
/* Set the 2D drawing position. */
static void
hndl_move(pgc_t *dev)
{
int32_t x = 0, y = 0;
if (! pgc_param_coord(dev, &x)) return;
if (! pgc_param_coord(dev, &y)) return;
pgc_log("PCG: MOVE %x.%04x,%x.%04x\n",
HWORD(x), LWORD(x), HWORD(y), LWORD(y));
dev->x = x;
dev->y = y;
}
/* Set the 3D drawing position. */
static void
hndl_move3(pgc_t *dev)
{
int32_t x = 0, y = 0, z = 0;
if (! pgc_param_coord(dev, &x)) return;
if (! pgc_param_coord(dev, &y)) return;
if (! pgc_param_coord(dev, &z)) return;
dev->x = x;
dev->y = y;
dev->z = z;
}
/* Relative move (2D). */
static void
hndl_mover(pgc_t *dev)
{
int32_t x = 0, y = 0;
if (! pgc_param_coord(dev, &x)) return;
if (! pgc_param_coord(dev, &y)) return;
dev->x += x;
dev->y += y;
}
/* Relative move (3D). */
static void
hndl_mover3(pgc_t *dev)
{
int32_t x = 0, y = 0, z = 0;
if (! pgc_param_coord(dev, &x)) return;
if (! pgc_param_coord(dev, &y)) return;
if (! pgc_param_coord(dev, &z)) return;
dev->x += x;
dev->y += y;
dev->z += z;
}
/* Given raster coordinates, find the matching address in PGC video RAM. */
uint8_t *
pgc_vram_addr(pgc_t *dev, int16_t x, int16_t y)
{
int offset;
/* We work from the bottom left-hand corner. */
if (y < 0 || (uint32_t)y >= dev->maxh ||
x < 0 || (uint32_t)x >= dev->maxw) return NULL;
offset = (dev->maxh - 1 - y) * (dev->maxw) + x;
pgc_log("PGC: vram_addr(x=%i,y=%i) = %i\n", x, y, offset);
if (offset < 0 || (uint32_t)offset >= (dev->maxw * dev->maxh))
return NULL;
return &dev->vram[offset];
}
/*
* Write a screen pixel.
* X and Y are raster coordinates, ink is the value to write.
*/
void
pgc_write_pixel(pgc_t *dev, uint16_t x, uint16_t y, uint8_t ink)
{
uint8_t *vram;
/* Suppress out-of-range writes; clip to viewport. */
if (x < dev->vp_x1 || x > dev->vp_x2 || x >= dev->maxw ||
y < dev->vp_y1 || y > dev->vp_y2 || y >= dev->maxh) {
pgc_log("PGC: write_pixel clipped: (%i,%i) "
"vp_x1=%i vp_y1=%i vp_x2=%i vp_y2=%i "
"ink=0x%02x\n",
x, y, dev->vp_x1, dev->vp_y1, dev->vp_x2, dev->vp_y2, ink);
return;
}
vram = pgc_vram_addr(dev, x, y);
if (vram)
*vram = ink;
}
/* Read a screen pixel (x and y are raster coordinates). */
uint8_t
pgc_read_pixel(pgc_t *dev, uint16_t x, uint16_t y)
{
uint8_t *vram;
/* Suppress out-of-range reads. */
if (x >= dev->maxw || y >= dev->maxh)
return 0;
vram = pgc_vram_addr(dev, x, y);
if (vram)
return *vram;
return 0;
}
/*
* Plot a point in the current color and draw mode. Raster coordinates.
*
* FIXME: this should be overloaded by clones if they support
* modes other than WRITE and INVERT, like the IM-1024.
*/
void
pgc_plot(pgc_t *dev, uint16_t x, uint16_t y)
{
uint8_t *vram;
/* Only allow plotting within the current viewport. */
if (x < dev->vp_x1 || x > dev->vp_x2 || x >= dev->maxw ||
y < dev->vp_y1 || y > dev->vp_y2 || y >= dev->maxh) {
pgc_log("PGC: plot clipped: (%i,%i) %i <= x <= %i; %i <= y <= %i; "
"mode=%i ink=0x%02x\n", x, y,
dev->vp_x1, dev->vp_x2, dev->vp_y1, dev->vp_y2,
dev->draw_mode, dev->color);
return;
}
vram = pgc_vram_addr(dev, x, y);
if (! vram) return;
/* TODO: Does not implement the PGC plane mask (set by MASK). */
switch (dev->draw_mode) {
default:
case 0: /* WRITE */
*vram = dev->color;
break;
case 1: /* INVERT */
*vram ^= 0xff;
break;
case 2: /* XOR color */
//FIXME: see notes
*vram ^= dev->color;
break;
case 3: /* AND color */
//FIXME: see notes
*vram &= dev->color;
break;
}
}
/*
* Draw a line (using raster coordinates).
*
* Bresenham's Algorithm from:
* <https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C>
*
* The line pattern mask to use is passed in. Return value is the
* line pattern mask, rotated by the number of points drawn.
*/
uint16_t
pgc_draw_line_r(pgc_t *dev, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t linemask)
{
int32_t dx, dy, sx, sy, err, e2;
dx = abs(x1 - x0);
dy = abs(y1 - y0);
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = (dx > dy ? dx : -dy) / 2;
for (;;) {
if (linemask & 0x8000) {
pgc_plot(dev, x0, y0);
linemask = (linemask << 1) | 1;
} else
linemask = (linemask << 1);
if (x0 == x1 && y0 == y1) break;
e2 = err;
if (e2 > -dx) {
err -= dy;
x0 += sx;
}
if (e2 < dy) {
err += dx;
y0 += sy;
}
}
return linemask;
}
/* Draw a line (using PGC fixed-point coordinates). */
uint16_t
pgc_draw_line(pgc_t *dev, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t linemask)
{
pgc_log("pgc_draw_line: (%i,%i) to (%i,%i)\n",
x0 >> 16, y0 >> 16, x1 >> 16, y1 >> 16);
/* Convert from PGC fixed-point to device coordinates */
x0 >>= 16;
y0 >>= 16;
pgc_ito_raster(dev, &x0, &y0);
x1 >>= 16;
y1 >>= 16;
pgc_ito_raster(dev, &x1, &y1);
return pgc_draw_line_r(dev, x0, y0, x1, y1, linemask);
}
/*
* Draw a horizontal line in the current fill pattern
* (using raster coordinates).
*/
void
pgc_fill_line_r(pgc_t *dev, int32_t x0, int32_t x1, int32_t y0)
{
int32_t mask = 0x8000 >> (x0 & 0x0f);
int32_t x;
if (x0 > x1) {
x = x1;
x1 = x0;
x0 = x;
}
for (x = x0; x <= x1; x++) {
if (dev->fill_pattern[y0 & 0x0F] & mask)
pgc_plot(dev, x, y0);
mask = mask >> 1;
if (mask == 0) mask = 0x8000;
}
}
/* For sorting polygon nodes. */
static int
compare_double(const void *a, const void *b)
{
const double *da = (const double *)a;
const double *db = (const double *)b;
if (*da < *db) return 1;
if (*da > *db) return -1;
return 0;
}
/* Draw a filled polygon (using PGC fixed-point coordinates). */
void
pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y)
{
double *nodex;
double *dx;
double *dy;
unsigned n, nodes, i, j;
double ymin, ymax, ypos;
pgc_log("PGC: fill_polygon(%i corners)\n", corners);
2020-01-15 02:34:50 +01:00
if (!x || !y || (corners < 2))
return; /* Degenerate polygon */
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
nodex = (double *)malloc(corners * sizeof(double));
dx = (double *)malloc(corners * sizeof(double));
dy = (double *)malloc(corners * sizeof(double));
2020-01-14 22:12:02 +01:00
if (!nodex || !dx || !dy) {
if (nodex) {
free(nodex);
nodex = NULL;
}
if (dx) {
free(dx);
dx = NULL;
}
if (dy) {
free(dy);
dy = NULL;
}
return;
}
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
ymin = ymax = y[0] / 65536.0;
for (n = 0; n < corners; n++) {
/* Convert from PGC fixed-point to native floating-point. */
dx[n] = x[n] / 65536.0;
dy[n] = y[n] / 65536.0;
if (dy[n] < ymin) ymin = dy[n];
if (dy[n] > ymax) ymax = dy[n];
}
/* Polygon fill. Based on <http://alienryderflex.com/polygon_fill/> */
/* For each row, work out where the polygon lines intersect with
* that row. */
for (ypos = ymin; ypos <= ymax; ypos++) {
nodes = 0;
j = corners - 1;
for (i = 0; i < corners; i++) {
if ((dy[i] < ypos && dy[j] >= ypos) ||
(dy[j] < ypos && dy[i] >= ypos)) /* Line crosses */ {
nodex[nodes++] = dx[i] + (ypos-dy[i])/(dy[j]-dy[i]) * (dx[j] - dx[i]);
}
j = i;
}
/* Sort the intersections. */
if (nodes)
qsort(nodex, nodes, sizeof(double), compare_double);
/* And fill between them. */
for (i = 0; i < nodes; i += 2) {
int16_t x1 = (int16_t)nodex[i], x2 = (int16_t)nodex[i + 1],
y1 = (int16_t)ypos, y2 = (int16_t)ypos;
pgc_sto_raster(dev, &x1, &y1);
pgc_sto_raster(dev, &x2, &y2);
pgc_fill_line_r(dev, x1, x2, y1);
}
}
free(nodex);
free(dx);
free(dy);
}
/* Draw a filled ellipse (using PGC fixed-point coordinates). */
void
pgc_draw_ellipse(pgc_t *dev, int32_t x, int32_t y)
{
/* Convert from PGC fixed-point to native floating-point. */
double h = y / 65536.0;
double w = x / 65536.0;
double y0 = dev->y / 65536.0;
double x0 = dev->x / 65536.0;
double ypos, xpos;
double x1;
double xlast = 0.0;
int16_t linemask = dev->line_pattern;
pgc_log("PGC: ellipse(color=%i drawmode=%i fill=%i)\n",
dev->color, dev->draw_mode, dev->fill_mode);
pgc_dto_raster(dev, &x0, &y0);
for (ypos = 0; ypos <= h; ypos++) {
if (ypos == 0) {
if (dev->fill_mode)
pgc_fill_line_r(dev, (uint16_t)(x0 - w),
(uint16_t)(x0 + w), (uint16_t)y0);
if (linemask & 0x8000) {
pgc_plot(dev, (uint16_t)(x0 + w), (uint16_t)y0);
pgc_plot(dev, (uint16_t)(x0 - w), (uint16_t)y0);
linemask = (linemask << 1) | 1;
} else
linemask = linemask << 1;
xlast = w;
} else {
x1 = sqrt((h * h) - (ypos * ypos)) * w / h;
if (dev->fill_mode) {
pgc_fill_line_r(dev, (uint16_t)(x0 - x1),
(uint16_t)(x0 + x1),
(uint16_t)(y0 + ypos));
pgc_fill_line_r(dev, (uint16_t)(x0 - x1),
(uint16_t)(x0 + x1),
(uint16_t)(y0 - ypos));
}
/* Draw border. */
for (xpos = xlast; xpos >= x1; xpos--) {
if (linemask & 0x8000) {
pgc_plot(dev, (uint16_t)(x0 + xpos),
(uint16_t)(y0 + ypos));
pgc_plot(dev, (uint16_t)(x0 - xpos),
(uint16_t)(y0 + ypos));
pgc_plot(dev, (uint16_t)(x0 + xpos),
(uint16_t)(y0 - ypos));
pgc_plot(dev, (uint16_t)(x0 - xpos),
(uint16_t)(y0 - ypos));
linemask = (linemask << 1) | 1;
} else
linemask = linemask << 1;
}
xlast = x1;
}
}
}
/* Handle the ELIPSE (sic) command. */
static void
hndl_ellipse(pgc_t *dev)
{
int32_t x = 0, y = 0;
if (! pgc_param_coord(dev, &x)) return;
if (! pgc_param_coord(dev, &y)) return;
pgc_draw_ellipse(dev, x, y);
}
/* Handle the POLY command. */
static void
hndl_poly(pgc_t *dev)
{
uint8_t count;
int32_t x[256];
int32_t y[256];
int32_t n;
if (! pgc_param_byte(dev, &count)) return;
pgc_log("PGC: POLY (%i)\n", count);
for (n = 0; n < count; n++) {
if (! pgc_param_coord(dev, &x[n])) return;
if (! pgc_param_coord(dev, &y[n])) return;
}
}
/* Parse but don't execute a POLY command (for adding to a command list) */
static int
parse_poly(pgc_t *dev, pgc_cl_t *cl, int c)
{
uint8_t count;
#ifdef ENABLE_PGC_LOG
pgc_log("PCG: parse_poly\n");
#endif
if (! pgc_param_byte(dev, &count)) return 0;
pgc_log("PCG: parse_poly: count=%02x\n", count);
if (! pgc_cl_append(cl, count)) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
return 0;
}
pgc_log("PCG: parse_poly: parse %i coords\n", 2 * count);
return pgc_parse_coords(dev, cl, 2 * count);
}
/* Handle the DISPLAY command. */
static void
hndl_display(pgc_t *dev)
{
uint8_t param;
if (! pgc_param_byte(dev, &param)) return;
pgc_log("PGC: DISPLAY(%i)\n", param);
if (param > 1)
pgc_error(dev, PGC_ERROR_RANGE);
else
pgc_setdisplay(dev, param);
}
/* Handle the IMAGEW command (memory to screen blit). */
static void
hndl_imagew(pgc_t *dev)
{
int16_t row, col1, col2;
uint8_t v1, v2;
if (! pgc_param_word(dev, &row)) return;
if (! pgc_param_word(dev, &col1)) return;
if (! pgc_param_word(dev, &col2)) return;
if ((uint32_t)row >= dev->screenh ||
(uint32_t)col1 >= dev->maxw || (uint32_t)col2 >= dev->maxw) {
pgc_error(dev, PGC_ERROR_RANGE);
return;
}
/* In ASCII mode, what is written is a stream of bytes. */
if (dev->ascii_mode) {
while (col1 <= col2) {
if (! pgc_param_byte(dev, &v1)) return;
pgc_write_pixel(dev, col1, row, v1);
col1++;
}
return;
}
/* In hex mode, it's RLE compressed. */
while (col1 <= col2) {
if (! pgc_param_byte(dev, &v1)) return;
if (v1 & 0x80) {
/* Literal run. */
v1 -= 0x7f;
while (col1 <= col2 && v1 != 0) {
if (! pgc_param_byte(dev, &v2)) return;
pgc_write_pixel(dev, col1, row, v2);
col1++;
v1--;
}
} else {
/* Repeated run. */
if (! pgc_param_byte(dev, &v2)) return;
v1++;
while (col1 <= col2 && v1 != 0) {
pgc_write_pixel(dev, col1, row, v2);
col1++;
v1--;
}
}
}
}
/* Select one of the built-in palettes. */
static void
init_lut(pgc_t *dev, int param)
{
if (param >= 0 && param < 6)
memcpy(dev->palette, init_palette[param], sizeof(dev->palette));
else if (param == 0xff)
memcpy(dev->palette, dev->userpal, sizeof(dev->palette));
else
pgc_error(dev, PGC_ERROR_RANGE);
}
/* Save the current palette. */
static void
hndl_lutsav(pgc_t *dev)
{
memcpy(dev->userpal, dev->palette, sizeof(dev->palette));
}
/* Handle LUTINT (select palette). */
static void
hndl_lutint(pgc_t *dev)
{
uint8_t param;
if (! pgc_param_byte(dev, &param)) return;
init_lut(dev, param);
}
/* Handle LUTRD (read palette register). */
static void
hndl_lutrd(pgc_t *dev)
{
uint8_t param;
uint32_t col;
if (! pgc_param_byte(dev, &param)) return;
col = dev->palette[param];
pgc_result_byte(dev, (col >> 20) & 0x0f);
pgc_result_byte(dev, (col >> 12) & 0x0f);
pgc_result_byte(dev, (col >> 4) & 0x0f);
}
/* Handle LUT (write palette register). */
static void
hndl_lut(pgc_t *dev)
{
uint8_t param[4];
int n;
for (n = 0; n < 4; n++) {
if (! pgc_param_byte(dev, &param[n])) return;
if (n > 0 && param[n] > 15) {
pgc_error(dev, PGC_ERROR_RANGE);
param[n] &= 0x0f;
}
}
dev->palette[param[0]] = makecol((param[1] * 0x11),
(param[2] * 0x11),
(param[3] * 0x11));
}
/*
* LUT8RD and LUT8 are extensions implemented by several PGC clones,
* so here are functions that implement them even though they aren't
* used by the PGC.
*/
void
pgc_hndl_lut8rd(pgc_t *dev)
{
uint8_t param;
uint32_t col;
if (! pgc_param_byte(dev, &param)) return;
col = dev->palette[param];
pgc_result_byte(dev, (col >> 16) & 0xff);
pgc_result_byte(dev, (col >> 8) & 0xff);
pgc_result_byte(dev, col & 0xff);
}
void
pgc_hndl_lut8(pgc_t *dev)
{
uint8_t param[4];
int n;
for (n = 0; n < 4; n++)
if (! pgc_param_byte(dev, &param[n])) return;
dev->palette[param[0]] = makecol((param[1]), (param[2]), (param[3]));
}
/* Handle AREAPT (set 16x16 fill pattern). */
static void
hndl_areapt(pgc_t *dev)
{
int16_t pat[16];
int n;
for (n = 0; n < 16; n++)
if (! pgc_param_word(dev, &pat[n])) return;
pgc_log("PGC: AREAPT(%04x %04x %04x %04x...)\n",
pat[0] & 0xffff, pat[1] & 0xffff, pat[2] & 0xffff, pat[3] & 0xffff);
memcpy(dev->fill_pattern, pat, sizeof(dev->fill_pattern));
}
/* Handle CA (select ASCII mode). */
static void
hndl_ca(pgc_t *dev)
{
dev->ascii_mode = 1;
}
/* Handle CX (select hex mode). */
static void
hndl_cx(pgc_t *dev)
{
dev->ascii_mode = 0;
}
/*
* CA and CX remain valid in hex mode; they are handled
* as command 0x43 ('C') with a one-byte parameter.
*/
static void
hndl_c(pgc_t *dev)
{
uint8_t param;
if (! dev->inputbyte(dev, &param)) return;
if (param == 'A')
dev->ascii_mode = 1;
if (param == 'X')
dev->ascii_mode = 0;
}
/* RESETF resets the PGC. */
static void
hndl_resetf(pgc_t *dev)
{
pgc_reset(dev);
}
/* TJUST sets text justify settings. */
static void
hndl_tjust(pgc_t *dev)
{
uint8_t param[2];
if (! dev->inputbyte(dev, &param[0])) return;
if (! dev->inputbyte(dev, &param[1])) return;
if (param[0] >= 1 && param[0] <= 3 && param[1] >= 1 && param[1] <= 3) {
dev->tjust_h = param[0];
dev->tjust_v = param[1];
} else
pgc_error(dev, PGC_ERROR_RANGE);
}
/* TSIZE controls text horizontal spacing. */
static void
hndl_tsize(pgc_t *pgc)
{
int32_t param = 0;
if (! pgc_param_coord(pgc, &param)) return;
pgc_log("PGC: TSIZE %i\n", param);
pgc->tsize = param;
}
/*
* VWPORT sets up the viewport (roughly, the clip rectangle) in
* raster coordinates, measured from the bottom left of the screen.
*/
static void
hndl_vwport(pgc_t *dev)
{
int16_t x1, x2, y1, y2;
if (! pgc_param_word(dev, &x1)) return;
if (! pgc_param_word(dev, &x2)) return;
if (! pgc_param_word(dev, &y1)) return;
if (! pgc_param_word(dev, &y2)) return;
pgc_log("PGC: VWPORT %i,%i,%i,%i\n", x1,x2,y1,y2);
dev->vp_x1 = x1;
dev->vp_x2 = x2;
dev->vp_y1 = y1;
dev->vp_y2 = y2;
}
/* WINDOW defines the coordinate system in use. */
static void
hndl_window(pgc_t *dev)
{
int16_t x1, x2, y1, y2;
if (! pgc_param_word(dev, &x1)) return;
if (! pgc_param_word(dev, &x2)) return;
if (! pgc_param_word(dev, &y1)) return;
if (! pgc_param_word(dev, &y2)) return;
pgc_log("PGC: WINDOW %i,%i,%i,%i\n", x1,x2,y1,y2);
dev->win_x1 = x1;
dev->win_x2 = x2;
dev->win_y1 = y1;
dev->win_y2 = y2;
}
/*
* The list of commands implemented by this mini-PGC.
*
* In order to support the original PGC and clones, we support two lists;
* core commands (listed below) and subclass commands (listed in the clone).
*
* Each row has five parameters:
* ASCII-mode command
* Hex-mode command
* Function that executes this command
* Function that parses this command when building a command list
* Parameter for the parse function
*
* TODO: This list omits numerous commands present in a genuine PGC
* (ARC, AREA, AREABC, BUFFER, CIRCLE etc etc).
* TODO: Some commands don't have a parse function (for example, IMAGEW)
*
* The following ASCII entries have special meaning:
* ~~~~~~ command is valid only in hex mode
* ****** end of subclass command list, now process core command list
* @@@@@@ end of core command list
*
*/
static const pgc_cmd_t pgc_commands[] = {
{ "AREAPT", 0xe7, hndl_areapt, pgc_parse_words, 16 },
{ "AP", 0xe7, hndl_areapt, pgc_parse_words, 16 },
{ "~~~~~~", 0x43, hndl_c, NULL, 0 },
{ "CA", 0xd2, hndl_ca, NULL, 0 },
{ "CLBEG", 0x70, hndl_clbeg, NULL, 0 },
{ "CB", 0x70, hndl_clbeg, NULL, 0 },
{ "CLDEL", 0x74, hndl_cldel, pgc_parse_bytes, 1 },
{ "CD", 0x74, hndl_cldel, pgc_parse_bytes, 1 },
{ "CLEND", 0x71, hndl_clend, NULL, 0 },
{ "CLRUN", 0x72, hndl_clrun, pgc_parse_bytes, 1 },
{ "CR", 0x72, hndl_clrun, pgc_parse_bytes, 1 },
{ "CLRD", 0x75, hndl_clread, pgc_parse_bytes, 1 },
{ "CRD", 0x75, hndl_clread, pgc_parse_bytes, 1 },
{ "CLOOP", 0x73, hndl_cloop, NULL, 0 },
{ "CL", 0x73, hndl_cloop, NULL, 0 },
{ "CLEARS", 0x0f, hndl_clears, pgc_parse_bytes, 1 },
{ "CLS", 0x0f, hndl_clears, pgc_parse_bytes, 1 },
{ "COLOR", 0x06, hndl_color, pgc_parse_bytes, 1 },
{ "C", 0x06, hndl_color, pgc_parse_bytes, 1 },
{ "CX", 0xd1, hndl_cx, NULL, 0 },
{ "DISPLA", 0xd0, hndl_display, pgc_parse_bytes, 1 },
{ "DI", 0xd0, hndl_display, pgc_parse_bytes, 1 },
{ "ELIPSE", 0x39, hndl_ellipse, pgc_parse_coords, 2 },
{ "EL", 0x39, hndl_ellipse, pgc_parse_coords, 2 },
{ "IMAGEW", 0xd9, hndl_imagew, NULL, 0 },
{ "IW", 0xd9, hndl_imagew, NULL, 0 },
{ "LINFUN", 0xeb, hndl_linfun, pgc_parse_bytes, 1 },
{ "LF", 0xeb, hndl_linfun, pgc_parse_bytes, 1 },
{ "LINPAT", 0xea, hndl_linpat, pgc_parse_words, 1 },
{ "LP", 0xea, hndl_linpat, pgc_parse_words, 1 },
{ "LUTINT", 0xec, hndl_lutint, pgc_parse_bytes, 1 },
{ "LI", 0xec, hndl_lutint, pgc_parse_bytes, 1 },
{ "LUTRD", 0x50, hndl_lutrd, pgc_parse_bytes, 1 },
{ "LUTSAV", 0xed, hndl_lutsav, NULL, 0 },
{ "LUT", 0xee, hndl_lut, pgc_parse_bytes, 4 },
{ "MOVE", 0x10, hndl_move, pgc_parse_coords, 2 },
{ "M", 0x10, hndl_move, pgc_parse_coords, 2 },
{ "MOVE3", 0x12, hndl_move3, pgc_parse_coords, 3 },
{ "M3", 0x12, hndl_move3, pgc_parse_coords, 3 },
{ "MOVER", 0x11, hndl_mover, pgc_parse_coords, 2 },
{ "MR", 0x11, hndl_mover, pgc_parse_coords, 2 },
{ "MOVER3", 0x13, hndl_mover3, pgc_parse_coords, 3 },
{ "MR3", 0x13, hndl_mover3, pgc_parse_coords, 3 },
{ "PRMFIL", 0xe9, hndl_prmfil, pgc_parse_bytes, 1 },
{ "PF", 0xe9, hndl_prmfil, pgc_parse_bytes, 1 },
{ "POLY", 0x30, hndl_poly, parse_poly, 0 },
{ "P", 0x30, hndl_poly, parse_poly, 0 },
{ "RESETF", 0x04, hndl_resetf, NULL, 0 },
{ "RF", 0x04, hndl_resetf, NULL, 0 },
{ "TJUST", 0x85, hndl_tjust, pgc_parse_bytes, 2 },
{ "TJ", 0x85, hndl_tjust, pgc_parse_bytes, 2 },
{ "TSIZE", 0x81, hndl_tsize, pgc_parse_coords, 1 },
{ "TS", 0x81, hndl_tsize, pgc_parse_coords, 1 },
{ "VWPORT", 0xb2, hndl_vwport, pgc_parse_words, 4 },
{ "VWP", 0xb2, hndl_vwport, pgc_parse_words, 4 },
{ "WINDOW", 0xb3, hndl_window, pgc_parse_words, 4 },
{ "WI", 0xb3, hndl_window, pgc_parse_words, 4 },
{ "@@@@@@", 0x00, NULL, NULL, 0 }
};
/* When the wake timer expires, that's when the drawing thread is actually
* woken */
static void
wake_timer(void *priv)
{
pgc_t *dev = (pgc_t *)priv;
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: woke up\n");
#endif
thread_set_event(dev->pgc_wake_thread);
}
/*
* The PGC drawing thread main loop.
*
* Read in commands and execute them ad infinitum.
*/
static void
pgc_thread(void *priv)
{
pgc_t *dev = (pgc_t *)priv;
const pgc_cmd_t *cmd;
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: thread begins\n");
#endif
for (;;) {
if (! parse_command(dev, &cmd)) {
/* Are we shutting down? */
if (dev->stopped) {
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: Thread stopping...\n");
#endif
dev->stopped = 0;
break;
}
/* Nope, just a reset. */
continue;
}
pgc_log("PGC: Command: [%02x] '%s' found = %i\n",
dev->hex_command, dev->asc_command, (cmd != NULL));
if (cmd) {
dev->result_count = 0;
(*cmd->handler)(dev);
} else
pgc_error(dev, PGC_ERROR_OPCODE);
}
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: thread stopped\n");
#endif
}
/* Parameter passed is not a number: abort. */
static int
err_digit(pgc_t *dev)
{
uint8_t asc;
do {
/* Swallow everything until the next separator */
if (! dev->inputbyte(dev, &asc)) return 0;
} while (! is_whitespace(asc));
pgc_error(dev, PGC_ERROR_DIGIT);
return 0;
}
/* Output a byte, either as hex or ASCII depending on the mode. */
int
pgc_result_byte(pgc_t *dev, uint8_t val)
{
char buf[20];
if (! dev->ascii_mode)
return output_byte(dev, val);
if (dev->result_count) {
if (! output_byte(dev, ',')) return 0;
}
sprintf(buf, "%i", val);
dev->result_count++;
return output_string(dev, buf);
}
/* Output a word, either as hex or ASCII depending on the mode. */
int
pgc_result_word(pgc_t *dev, int16_t val)
{
char buf[20];
if (! dev->ascii_mode) {
if (! output_byte(dev, val & 0xFF)) return 0;
return output_byte(dev, val >> 8);
}
if (dev->result_count) {
if (! output_byte(dev, ',')) return 0;
}
sprintf(buf, "%i", val);
dev->result_count++;
return output_string(dev, buf);
}
/* Report an error, either in ASCII or in hex. */
int
pgc_error(pgc_t *dev, int err)
{
if (dev->mapram[0x307]) {
/* Errors enabled? */
if (dev->ascii_mode) {
if (err >= PGC_ERROR_RANGE && err <= PGC_ERROR_MISSING)
return error_string(dev, pgc_err_msgs[err]);
return error_string(dev, "Unknown error\r");
} else {
return error_byte(dev, err);
}
}
return 1;
}
/* Initialize RAM and registers to default values. */
void
pgc_reset(pgc_t *dev)
{
int n;
memset(dev->mapram, 0x00, sizeof(dev->mapram));
/* The 'CGA disable' jumper is not currently implemented. */
dev->mapram[0x30b] = dev->cga_enabled = 1;
dev->mapram[0x30c] = dev->cga_enabled;
dev->mapram[0x30d] = dev->cga_enabled;
dev->mapram[0x3f8] = 0x03; /* minor version */
dev->mapram[0x3f9] = 0x01; /* minor version */
dev->mapram[0x3fb] = 0xa5; /* } */
dev->mapram[0x3fc] = 0x5a; /* PGC self-test passed */
dev->mapram[0x3fd] = 0x55; /* } */
dev->mapram[0x3fe] = 0x5a; /* } */
dev->ascii_mode = 1; /* start off in ASCII mode */
dev->line_pattern = 0xffff;
memset(dev->fill_pattern, 0xff, sizeof(dev->fill_pattern));
dev->color = 0xff;
dev->tjust_h = 1;
dev->tjust_v = 1;
/* Reset panning. */
dev->pan_x = 0;
dev->pan_y = 0;
/* Reset clipping. */
dev->vp_x1 = 0;
dev->vp_y1 = 0;
dev->vp_x2 = dev->visw - 1;
dev->vp_y2 = dev->vish - 1;
/* Empty command lists. */
for (n = 0; n < 256; n++) {
dev->clist[n].wrptr = 0;
dev->clist[n].rdptr = 0;
dev->clist[n].repeat = 0;
dev->clist[n].chain = 0;
}
dev->clcur = NULL;
/* Select CGA display. */
dev->cga_selected = -1;
pgc_setdisplay(dev, dev->cga_enabled);
/* Default palette is 0. */
init_lut(dev, 0);
hndl_lutsav(dev);
}
/* Switch between CGA mode (DISPLAY 1) and native mode (DISPLAY 0). */
void
pgc_setdisplay(pgc_t *dev, int cga)
{
pgc_log("PGC: setdisplay(%i): cga_selected=%i cga_enabled=%i\n",
cga, dev->cga_selected, dev->cga_enabled);
if (dev->cga_selected != (dev->cga_enabled && cga)) {
dev->cga_selected = (dev->cga_enabled && cga);
dev->displine = 0;
if (dev->cga_selected) {
mem_mapping_enable(&dev->cga_mapping);
dev->screenw = PGC_CGA_WIDTH;
dev->screenh = PGC_CGA_HEIGHT;
} else {
mem_mapping_disable(&dev->cga_mapping);
dev->screenw = dev->visw;
dev->screenh = dev->vish;
}
pgc_recalctimings(dev);
}
}
/*
* When idle, the PGC drawing thread sleeps. pgc_wake() awakens it - but
* not immediately. Like the Voodoo, it has a short delay so that writes
* can be batched.
*/
void
pgc_wake(pgc_t *dev)
{
if (!timer_is_enabled(&dev->wake_timer))
timer_set_delay_u64(&dev->wake_timer, WAKE_DELAY);
}
/* Wait for more input data, or for output to drain. */
void
pgc_sleep(pgc_t *dev)
{
pgc_log("PGC: sleeping on %i %i %i %i 0x%02x 0x%02x\n",
dev->stopped,
dev->waiting_input_fifo, dev->waiting_output_fifo,
dev->waiting_error_fifo, dev->mapram[0x300], dev->mapram[0x301]);
/* Avoid entering waiting state. */
if (dev->stopped) {
dev->waiting_input_fifo = 0;
dev->waiting_output_fifo = 0;
return;
}
/* Race condition: If host wrote to the PGC during the that
* won't be noticed */
if (dev->waiting_input_fifo &&
dev->mapram[0x300] != dev->mapram[0x301]) {
dev->waiting_input_fifo = 0;
return;
}
/* Same if they read. */
if (dev->waiting_output_fifo &&
dev->mapram[0x302] != (uint8_t)(dev->mapram[0x303] - 1)) {
dev->waiting_output_fifo = 0;
return;
}
thread_wait_event(dev->pgc_wake_thread, -1);
thread_reset_event(dev->pgc_wake_thread);
}
/* Pull the next byte from the current command list. */
int
pgc_clist_byte(pgc_t *dev, uint8_t *val)
{
if (dev->clcur == NULL) return 0;
if (dev->clcur->rdptr < dev->clcur->wrptr)
*val = dev->clcur->list[dev->clcur->rdptr++];
else
*val = 0;
/* If we've reached the end, reset to the beginning and
* (if repeating) run the repeat */
if (dev->clcur->rdptr >= dev->clcur->wrptr) {
dev->clcur->rdptr = 0;
dev->clcur->repeat--;
if (dev->clcur->repeat == 0)
dev->clcur = dev->clcur->chain;
}
return 1;
}
/*
* Read in a byte, either as hex (1 byte) or ASCII (decimal).
* Returns 0 if PGC reset detected while the value is being read.
*/
int
pgc_param_byte(pgc_t *dev, uint8_t *val)
{
int32_t c;
if (dev->clcur)
return pgc_clist_byte(dev, val);
if (! dev->ascii_mode)
return dev->inputbyte(dev, val);
if (! pgc_param_coord(dev, &c)) return 0;
c = (c >> 16); /* drop fractional part */
if (c > 255) {
pgc_error(dev, PGC_ERROR_RANGE);
return 0;
}
*val = (uint8_t)c;
return 1;
}
/*
* Read in a word, either as hex (2 bytes) or ASCII (decimal).
* Returns 0 if PGC reset detected while the value is being read.
*/
int
pgc_param_word(pgc_t *dev, int16_t *val)
{
uint8_t lo, hi;
int32_t c;
if (dev->clcur) {
if (! pgc_clist_byte(dev, &lo)) return 0;
if (! pgc_clist_byte(dev, &hi)) return 0;
*val = (((int16_t)hi) << 8) | lo;
return 1;
}
if (! dev->ascii_mode) {
if (! dev->inputbyte(dev, &lo)) return 0;
if (! dev->inputbyte(dev, &hi)) return 0;
*val = (((int16_t)hi) << 8) | lo;
return 1;
}
if (! pgc_param_coord(dev, &c)) return 0;
c = (c >> 16);
if (c > 0x7fff || c < -0x7fff) {
pgc_error(dev, PGC_ERROR_RANGE);
return 0;
}
*val = (int16_t)c;
return 1;
}
typedef enum {
PS_MAIN,
PS_FRACTION,
PS_EXPONENT
} parse_state_t;
/*
* Read in a PGC coordinate.
*
* Either as hex (4 bytes) or ASCII (xxxx.yyyyEeee)
*
* Returns 0 if PGC reset detected while the value is being read.
*/
int
pgc_param_coord(pgc_t *dev, int32_t *value)
{
uint8_t asc;
int sign = 1;
int esign = 1;
int n;
uint16_t dp = 1;
uint16_t integer = 0;
uint16_t frac = 0;
uint16_t exponent = 0;
uint32_t res;
parse_state_t state = PS_MAIN;
uint8_t encoded[4];
/* If there is a command list running, pull the bytes out of that
* command list */
if (dev->clcur) {
for (n = 0; n < 4; n++)
if (! pgc_clist_byte(dev, &encoded[n])) return 0;
integer = (((int16_t)encoded[1]) << 8) | encoded[0];
frac = (((int16_t)encoded[3]) << 8) | encoded[2];
*value = (((int32_t)integer) << 16) | frac;
return 1;
}
/* If in hex mode, read in the encoded integer and fraction parts
* from the hex stream */
if (! dev->ascii_mode) {
for (n = 0; n < 4; n++)
if (! dev->inputbyte(dev, &encoded[n])) return 0;
integer = (((int16_t)encoded[1]) << 8) | encoded[0];
frac = (((int16_t)encoded[3]) << 8) | encoded[2];
*value = (((int32_t)integer) << 16) | frac;
return 1;
}
/* Parsing an ASCII value; skip separators. */
do {
if (! dev->inputbyte(dev, &asc)) return 0;
if (asc == '-') sign = -1;
} while (is_whitespace(asc));
/* There had better be a digit next. */
if (! isdigit(asc)) {
pgc_error(dev, PGC_ERROR_MISSING);
return 0;
}
do {
switch (asc) {
/* Decimal point is acceptable in 'main' state
* (start of fraction) not otherwise */
case '.':
if (state == PS_MAIN) {
if (! dev->inputbyte(dev, &asc)) return 0;
state = PS_FRACTION;
continue;
} else {
pgc_error(dev, PGC_ERROR_MISSING);
return err_digit(dev);
}
break;
/* Scientific notation. */
case 'd':
case 'D':
case 'e':
case 'E':
esign = 1;
if (! dev->inputbyte(dev, &asc)) return 0;
if (asc == '-') {
sign = -1;
if (! dev->inputbyte(dev, &asc)) return 0;
}
state = PS_EXPONENT;
continue;
/* Should be a number or a separator. */
default:
if (is_whitespace(asc)) break;
if (! isdigit(asc)) {
pgc_error(dev, PGC_ERROR_MISSING);
return err_digit(dev);
}
asc -= '0'; /* asc is digit */
switch (state) {
case PS_MAIN:
integer = (integer * 10)+asc;
if (integer & 0x8000) {
/* Overflow */
pgc_error(dev, PGC_ERROR_RANGE);
integer = 0x7fff;
}
break;
case PS_FRACTION:
frac = (frac * 10) + asc;
dp *= 10;
break;
case PS_EXPONENT:
exponent = (exponent * 10)+asc;
break;
}
}
if (! dev->inputbyte(dev, &asc)) return 0;
} while (! is_whitespace(asc));
res = (frac << 16) / dp;
pgc_log("PGC: integer=%u frac=%u exponent=%u dp=%i res=0x%08lx\n",
integer, frac, exponent, dp, res);
res = (res & 0xffff) | (integer << 16);
if (exponent) {
for (n = 0; n < exponent; n++) {
if (esign > 0)
res *= 10;
else
res /= 10;
}
}
*value = sign*res;
return 1;
}
/*
* Add a byte to a command list.
*
* We allow command lists to be arbitrarily large.
*/
int
pgc_cl_append(pgc_cl_t *list, uint8_t v)
{
uint8_t *buf;
if (list->listmax == 0 || list->list == NULL) {
list->list = (uint8_t *)malloc(4096);
if (!list->list) {
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: out of memory initializing command list\n");
#endif
return 0;
}
list->listmax = 4096;
}
while (list->wrptr >= list->listmax) {
buf = (uint8_t *)realloc(list->list, 2 * list->listmax);
if (!buf) {
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: out of memory growing command list\n");
#endif
return 0;
}
list->list = buf;
list->listmax *= 2;
}
list->list[list->wrptr++] = v;
return 1;
}
/* Parse but don't execute a command with a fixed number of byte parameters. */
int
pgc_parse_bytes(pgc_t *dev, pgc_cl_t *cl, int count)
{
uint8_t *param = (uint8_t *)malloc(count);
int n;
if (! param) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
return 0;
}
for (n = 0; n < count; n++) {
if (! pgc_param_byte(dev, &param[n])) {
free(param);
return 0;
}
if (! pgc_cl_append(cl, param[n])) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
free(param);
return 0;
}
}
free(param);
return 1;
}
/* Parse but don't execute a command with a fixed number of word parameters. */
int
pgc_parse_words(pgc_t *dev, pgc_cl_t *cl, int count)
{
int16_t *param = (int16_t *)malloc(count * sizeof(int16_t));
int n;
if (! param) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
return 0;
}
for (n = 0; n < count; n++) {
if (! pgc_param_word(dev, &param[n])) {
free(param);
return 0;
}
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
if (!pgc_cl_append(cl, param[n] & 0xff) ||
!pgc_cl_append(cl, param[n] >> 8)) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
free(param);
return 0;
}
}
free(param);
return 1;
}
/* Parse but don't execute a command with a fixed number of coord parameters */
int
pgc_parse_coords(pgc_t *dev, pgc_cl_t *cl, int count)
{
int32_t *param = (int32_t *)malloc(count * sizeof(int32_t));
int n;
if (! param) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
return 0;
}
2020-01-15 03:04:59 +01:00
for (n = 0; n < count; n++) {
if (! pgc_param_coord(dev, &param[n])) {
free(param);
return 0;
}
}
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
/* Here is how the real PGC serializes coords:
*
* 100.5 -> 64 00 00 80 ie 0064.8000
* 100.3 -> 64 00 CD 4C ie 0064.4CCD
*/
for (n = 0; n < count; n++) {
/* Serialize integer part. */
if (!pgc_cl_append(cl, (param[n] >> 16) & 0xff) ||
!pgc_cl_append(cl, (param[n] >> 24) & 0xff) ||
/* Serialize fraction part. */
!pgc_cl_append(cl, (param[n] ) & 0xff) ||
!pgc_cl_append(cl, (param[n] >> 8) & 0xff)) {
pgc_error(dev, PGC_ERROR_OVERFLOW);
free(param);
return 0;
}
}
free(param);
return 1;
}
/* Convert coordinates based on the current window / viewport to raster
* coordinates. */
void
pgc_dto_raster(pgc_t *dev, double *x, double *y)
{
#ifdef ENABLE_PGC_LOG
double x0 = *x, y0 = *y;
#endif
*x += (dev->vp_x1 - dev->win_x1);
*y += (dev->vp_y1 - dev->win_y1);
pgc_log("PGC: coords to raster: (%f, %f) -> (%f, %f)\n", x0, y0, *x, *y);
}
/* Overloads that take ints. */
void
pgc_sto_raster(pgc_t *dev, int16_t *x, int16_t *y)
{
double xd = *x, yd = *y;
pgc_dto_raster(dev, &xd, &yd);
*x = (int16_t)xd;
*y = (int16_t)yd;
}
void
pgc_ito_raster(pgc_t *dev, int32_t *x, int32_t *y)
{
double xd = *x, yd = *y;
pgc_dto_raster(dev, &xd, &yd);
*x = (int32_t)xd;
*y = (int32_t)yd;
}
void
pgc_recalctimings(pgc_t *dev)
{
double disptime, _dispontime, _dispofftime;
double pixel_clock = (cpuclock * (double)(1ull << 32)) / (dev->cga_selected ? 25175000.0 : dev->native_pixel_clock);
/* Use a fixed 640x400 display. */
disptime = dev->screenw + 11;
_dispontime = dev->screenw * pixel_clock;
_dispofftime = (disptime - dev->screenw) * pixel_clock;
dev->dispontime = (uint64_t)(_dispontime);
dev->dispofftime = (uint64_t)(_dispofftime);
}
/* Write to CGA registers are copied into the transfer memory buffer. */
void
pgc_out(uint16_t addr, uint8_t val, void *priv)
{
pgc_t *dev = (pgc_t *)priv;
pgc_log("PGC: out(%04x, %02x)\n", addr, val);
switch(addr) {
case 0x03d0: /* CRTC Index register */
case 0x03d2:
case 0x03d4:
case 0x03d6:
dev->mapram[0x03d0] = val;
break;
case 0x03d1: /* CRTC Data register */
case 0x03d3:
case 0x03d5:
case 0x03d7:
if (dev->mapram[0x03d0] < 18)
dev->mapram[0x03e0 + dev->mapram[0x03d0]] = val;
break;
case 0x03d8: /* CRTC Mode Control register */
dev->mapram[0x03d8] = val;
break;
case 0x03d9: /* CRTC Color Select register */
dev->mapram[0x03d9] = val;
break;
}
}
/* Read back the CGA registers. */
uint8_t
pgc_in(uint16_t addr, void *priv)
{
pgc_t *dev = (pgc_t *)priv;
uint8_t ret = 0xff;
switch(addr) {
case 0x03d0: /* CRTC Index register */
case 0x03d2:
case 0x03d4:
case 0x03d6:
ret = dev->mapram[0x03d0];
break;
case 0x03d1: /* CRTC Data register */
case 0x03d3:
case 0x03d5:
case 0x03d7:
if (dev->mapram[0x03d0] < 18)
ret = dev->mapram[0x03e0 + dev->mapram[0x03d0]];
break;
case 0x03d8: /* CRTC Mode Control register */
ret = dev->mapram[0x03d8];
break;
case 0x03d9: /* CRTC Color Select register */
ret = dev->mapram[0x03d9];
break;
case 0x03da: /* CRTC Status register */
ret = dev->mapram[0x03da];
break;
}
pgc_log("PGC: in(%04x) = %02x\n", addr, ret);
return ret;
}
/* Memory write to the transfer buffer. */
/* TODO: Check the CGA mapping repeat stuff. */
void
pgc_write(uint32_t addr, uint8_t val, void *priv)
{
pgc_t *dev = (pgc_t *)priv;
/*
* It seems variable whether the PGC maps 1K or 2K at 0xc6000.
*
* Map 2K here in case a clone requires it.
*/
if (addr >= 0xc6000 && addr < 0xc6800) {
addr &= 0x7ff;
/* If one of the FIFOs has been updated, this may cause
* the drawing thread to be woken */
if (dev->mapram[addr] != val) {
dev->mapram[addr] = val;
switch (addr) {
case 0x300: /* input write pointer */
if (dev->waiting_input_fifo &&
dev->mapram[0x300] != dev->mapram[0x301]) {
dev->waiting_input_fifo = 0;
pgc_wake(dev);
}
break;
case 0x303: /* output read pointer */
if (dev->waiting_output_fifo &&
dev->mapram[0x302] != (uint8_t)(dev->mapram[0x303] - 1)) {
dev->waiting_output_fifo = 0;
pgc_wake(dev);
}
break;
case 0x305: /* error read pointer */
if (dev->waiting_error_fifo &&
dev->mapram[0x304] != (uint8_t)(dev->mapram[0x305] - 1)) {
dev->waiting_error_fifo = 0;
pgc_wake(dev);
}
break;
case 0x306: /* cold start flag */
/* XXX This should be in IM-1024 specific code */
dev->mapram[0x306] = 0;
break;
case 0x30c: /* display type */
pgc_setdisplay(priv, dev->mapram[0x30c]);
dev->mapram[0x30d] = dev->mapram[0x30c];
break;
case 0x3ff: /* reboot the PGC */
pgc_wake(dev);
break;
}
}
}
if (addr >= 0xb8000 && addr < 0xc0000 && dev->cga_selected) {
addr &= 0x3fff;
dev->cga_vram[addr] = val;
}
}
/* TODO: Check the CGA mapping repeat stuff. */
uint8_t
pgc_read(uint32_t addr, void *priv)
{
pgc_t *dev = (pgc_t *)priv;
uint8_t ret = 0xff;
if (addr >= 0xc6000 && addr < 0xc6800) {
addr &= 0x7ff;
ret = dev->mapram[addr];
} else if (addr >= 0xb8000 && addr < 0xc0000 && dev->cga_selected) {
addr &= 0x3fff;
ret = dev->cga_vram[addr];
}
return ret;
}
/* Draw the display in CGA (640x400) text mode. */
void
pgc_cga_text(pgc_t *dev, int w)
{
int x, c;
uint8_t chr, attr;
int drawcursor = 0;
uint32_t cols[2];
int pitch = (dev->mapram[0x3e9] + 1) * 2;
uint16_t sc = (dev->displine & 0x0f) % pitch;
uint16_t ma = (dev->mapram[0x3ed] | (dev->mapram[0x3ec] << 8)) & 0x3fff;
uint16_t ca = (dev->mapram[0x3ef] | (dev->mapram[0x3ee] << 8)) & 0x3fff;
uint8_t *addr;
uint32_t val;
int cw = (w == 80) ? 8 : 16;
addr = &dev->cga_vram[((ma + ((dev->displine / pitch) * w)) * 2) & 0x3ffe];
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
ma += (dev->displine / pitch) * w;
for (x = 0; x < w; x++) {
chr = *addr++;
attr = *addr++;
/* Cursor enabled? */
if (ma == ca && (dev->cgablink & 8) &&
(dev->mapram[0x3ea] & 0x60) != 0x20) {
drawcursor = ((dev->mapram[0x3ea] & 0x1f) <= (sc >> 1)) &&
((dev->mapram[0x3eb] & 0x1f) >= (sc >> 1));
} else
drawcursor = 0;
if (dev->mapram[0x3d8] & 0x20) {
cols[1] = (attr & 15) + 16;
cols[0] = ((attr >> 4) & 7) + 16;
if ((dev->cgablink & 8) && (attr & 0x80) && !drawcursor)
cols[1] = cols[0];
} else {
cols[1] = (attr & 15) + 16;
cols[0] = (attr >> 4) + 16;
}
for (c = 0; c < cw; c++) {
if (drawcursor)
val = cols[(fontdatm[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ 0x0f;
else
val = cols[(fontdatm[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0];
buffer32->line[dev->displine][(x * cw) + c] = val;
}
ma++;
}
}
/* Draw the display in CGA (320x200) graphics mode. */
void
pgc_cga_gfx40(pgc_t *dev)
{
int x, c;
uint32_t cols[4];
int col;
uint16_t ma = (dev->mapram[0x3ed] | (dev->mapram[0x3ec] << 8)) & 0x3fff;
uint8_t *addr;
uint16_t dat;
cols[0] = (dev->mapram[0x3d9] & 15) + 16;
col = ((dev->mapram[0x3d9] & 16) ? 8 : 0) + 16;
/* From John Elliott's site:
On a real CGA, if bit 2 of port 03D8h and bit 5 of port 03D9h are both set,
the palette used in graphics modes is red/cyan/white. On a PGC, it's
magenta/cyan/white. You still get red/cyan/white if bit 5 of port 03D9h is
not set. This is a firmware issue rather than hardware. */
if (dev->mapram[0x3d9] & 32) {
cols[1] = col | 3;
cols[2] = col | 5;
cols[3] = col | 7;
} else if (dev->mapram[0x3d8] & 4) {
cols[1] = col | 3;
cols[2] = col | 4;
cols[3] = col | 7;
} else {
cols[1] = col | 2;
cols[2] = col | 4;
cols[3] = col | 6;
}
for (x = 0; x < 40; x++) {
addr = &dev->cga_vram[(ma + 2 * x + 80 * (dev->displine >> 2) + 0x2000 * ((dev->displine >> 1) & 1)) & 0x3fff];
dat = (addr[0] << 8) | addr[1];
dev->ma++;
for (c = 0; c < 8; c++) {
buffer32->line[dev->displine][(x << 4) + (c << 1)] =
buffer32->line[dev->displine][(x << 4) + (c << 1) + 1] = cols[dat >> 14];
dat <<= 2;
}
}
}
/* Draw the display in CGA (640x200) graphics mode. */
void
pgc_cga_gfx80(pgc_t *dev)
{
int x, c;
uint32_t cols[2];
uint16_t ma = (dev->mapram[0x3ed] | (dev->mapram[0x3ec] << 8)) & 0x3fff;
uint8_t *addr;
uint16_t dat;
cols[0] = 16;
cols[1] = (dev->mapram[0x3d9] & 15) + 16;
for (x = 0; x < 40; x++) {
addr = &dev->cga_vram[(ma + 2 * x + 80 * (dev->displine >> 2) + 0x2000 * ((dev->displine >> 1) & 1)) & 0x3fff];
dat = (addr[0] << 8) | addr[1];
dev->ma++;
for (c = 0; c < 16; c++) {
buffer32->line[dev->displine][(x << 4) + c] = cols[dat >> 15];
dat <<= 1;
}
}
}
/* Draw the screen in CGA mode. */
void
pgc_cga_poll(pgc_t *dev)
{
uint32_t cols[2];
if (! dev->linepos) {
timer_advance_u64(&dev->timer, dev->dispofftime);
dev->mapram[0x03da] |= 1;
dev->linepos = 1;
if (dev->cgadispon) {
if (dev->displine == 0)
video_wait_for_buffer();
if ((dev->mapram[0x03d8] & 0x12) == 0x12)
pgc_cga_gfx80(dev);
else if (dev->mapram[0x03d8] & 0x02)
pgc_cga_gfx40(dev);
else if (dev->mapram[0x03d8] & 0x01)
pgc_cga_text(dev, 80);
else
pgc_cga_text(dev, 40);
} else {
cols[0] = ((dev->mapram[0x03d8] & 0x12) == 0x12) ? 0 : ((dev->mapram[0x03d9] & 15) + 16);
hline(buffer32, 0, dev->displine, PGC_CGA_WIDTH, cols[0]);
}
if (++dev->displine == PGC_CGA_HEIGHT) {
dev->mapram[0x3da] |= 8;
dev->cgadispon = 0;
}
if (dev->displine == PGC_CGA_HEIGHT + 32) {
dev->mapram[0x3da] &= ~8;
dev->cgadispon = 1;
dev->displine = 0;
}
} else {
if (dev->cgadispon)
dev->mapram[0x3da] &= ~1;
timer_advance_u64(&dev->timer, dev->dispontime);
dev->linepos = 0;
if (dev->displine == PGC_CGA_HEIGHT) {
if (PGC_CGA_WIDTH != xsize || PGC_CGA_HEIGHT != ysize) {
xsize = PGC_CGA_WIDTH;
ysize = PGC_CGA_HEIGHT;
set_screen_size(xsize, ysize);
if (video_force_resize_get())
video_force_resize_set(0);
}
video_blit_memtoscreen_8(0, 0, 0, ysize, xsize, ysize);
frames++;
/* We have a fixed 640x400 screen for CGA modes. */
video_res_x = PGC_CGA_WIDTH;
video_res_y = PGC_CGA_HEIGHT;
switch (dev->mapram[0x3d8] & 0x12) {
case 0x12:
video_bpp = 1;
break;
case 0x02:
video_bpp = 2;
break;
default:
video_bpp = 0;
break;
}
dev->cgablink++;
}
}
}
/* Draw the screen in CGA or native mode. */
void
pgc_poll(void *priv)
{
pgc_t *dev = (pgc_t *)priv;
uint32_t x, y;
if (dev->cga_selected) {
pgc_cga_poll(dev);
return;
}
/* Not CGA, so must be native mode. */
if (! dev->linepos) {
timer_advance_u64(&dev->timer, dev->dispofftime);
dev->mapram[0x3da] |= 1;
dev->linepos = 1;
if (dev->cgadispon && (uint32_t)dev->displine < dev->maxh) {
if (dev->displine == 0)
video_wait_for_buffer();
/* Don't know why pan needs to be multiplied by -2, but
* the IM1024 driver uses PAN -112 for an offset of
* 224. */
y = dev->displine - 2 * dev->pan_y;
for (x = 0; x < dev->screenw; x++) {
if (x + dev->pan_x < dev->maxw)
buffer32->line[dev->displine][x] = dev->palette[dev->vram[y * dev->maxw + x]];
else
buffer32->line[dev->displine][x] = dev->palette[0];
}
} else {
hline(buffer32, 0, dev->displine, dev->screenw, dev->palette[0]);
}
if (++dev->displine == dev->screenh) {
dev->mapram[0x3da] |= 8;
dev->cgadispon = 0;
}
if (dev->displine == dev->screenh + 32) {
dev->mapram[0x3da] &= ~8;
dev->cgadispon = 1;
dev->displine = 0;
}
} else {
if (dev->cgadispon)
dev->mapram[0x3da] &= ~1;
timer_advance_u64(&dev->timer, dev->dispontime);
dev->linepos = 0;
if (dev->displine == dev->screenh) {
if (dev->screenw != xsize || dev->screenh != ysize) {
xsize = dev->screenw;
ysize = dev->screenh;
set_screen_size(xsize, ysize);
if (video_force_resize_get())
video_force_resize_set(0);
}
video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize);
frames++;
video_res_x = dev->screenw;
video_res_y = dev->screenh;
video_bpp = 8;
dev->cgablink++;
}
}
}
void
pgc_speed_changed(void *priv)
{
pgc_t *dev = (pgc_t *)priv;
pgc_recalctimings(dev);
}
void
pgc_close(void *priv)
{
pgc_t *dev = (pgc_t *)priv;
/*
* Close down the worker thread by setting a
* flag, and then simulating a reset so it
* stops reading data.
*/
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: telling thread to stop...\n");
#endif
dev->stopped = 1;
dev->mapram[0x3ff] = 1;
if (dev->waiting_input_fifo || dev->waiting_output_fifo) {
/* Do an immediate wake-up. */
wake_timer(priv);
}
/* Wait for thread to stop. */
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: waiting for thread to stop...\n");
#endif
// while (dev->stopped);
thread_wait(dev->pgc_thread, -1);
#ifdef ENABLE_PGC_LOG
pgc_log("PGC: thread stopped, closing up.\n");
#endif
if (dev->cga_vram)
free(dev->cga_vram);
if (dev->vram)
free(dev->vram);
free(dev);
}
/*
* Initialization code common to the PGC and its subclasses.
*
* Pass the 'input byte' function in since this is overridden in
* the IM-1024, and needs to be set before the drawing thread is
* launched.
*/
void
pgc_init(pgc_t *dev, int maxw, int maxh, int visw, int vish,
int (*inpbyte)(pgc_t *, uint8_t *), double npc)
{
int i;
/* Make it a 16k mapping at C4000 (will be C4000-C7FFF),
because of the emulator's granularity - the original
mapping will conflict with hard disk controller BIOS'es. */
mem_mapping_add(&dev->mapping, 0xc4000, 16384,
pgc_read,NULL,NULL, pgc_write,NULL,NULL,
NULL, MEM_MAPPING_EXTERNAL, dev);
mem_mapping_add(&dev->cga_mapping, 0xb8000, 32768,
pgc_read,NULL,NULL, pgc_write,NULL,NULL,
NULL, MEM_MAPPING_EXTERNAL, dev);
io_sethandler(0x03d0, 16,
pgc_in,NULL,NULL, pgc_out,NULL,NULL, dev);
dev->maxw = maxw;
dev->maxh = maxh;
dev->visw = visw;
dev->vish = vish;
dev->vram = (uint8_t *)malloc(maxw * maxh);
memset(dev->vram, 0x00, maxw * maxh);
dev->cga_vram = (uint8_t *)malloc(16384);
memset(dev->cga_vram, 0x00, 16384);
/* Create and initialize command lists. */
dev->clist = (pgc_cl_t *)malloc(256 * sizeof(pgc_cl_t));
memset(dev->clist, 0x00, 256 * sizeof(pgc_cl_t));
for (i = 0; i < 256; i++) {
dev->clist[i].list = NULL;
dev->clist[i].listmax = 0;
dev->clist[i].wrptr = 0;
dev->clist[i].rdptr = 0;
dev->clist[i].repeat = 0;
dev->clist[i].chain = NULL;
}
dev->clcur = NULL;
dev->native_pixel_clock = npc;
pgc_reset(dev);
dev->inputbyte = inpbyte;
dev->master = dev->commands = pgc_commands;
dev->pgc_wake_thread = thread_create_event();
dev->pgc_thread = thread_create(pgc_thread, dev);
timer_add(&dev->timer, pgc_poll, dev, 1);
timer_add(&dev->wake_timer, wake_timer, dev, 0);
}
static void *
pgc_standalone_init(const device_t *info)
{
pgc_t *dev;
dev = (pgc_t *)malloc(sizeof(pgc_t));
memset(dev, 0x00, sizeof(pgc_t));
dev->type = info->local;
/* Framebuffer and screen are both 640x480. */
pgc_init(dev, 640, 480, 640, 480, input_byte, 25175000.0);
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_pgc);
return(dev);
}
const device_t pgc_device = {
"PGC",
DEVICE_ISA, 0,
pgc_standalone_init,
pgc_close,
NULL,
NULL,
pgc_speed_changed,
NULL,
NULL
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
};