Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/bios_disk.h2
-rw-r--r--include/dos_inc.h24
-rw-r--r--include/drives.h170
-rw-r--r--include/logging.h1
-rw-r--r--include/pci_bus.h6
-rw-r--r--include/sdlmain.h11
-rw-r--r--include/vga.h2
-rw-r--r--include/video.h25
-rw-r--r--src/debug/debug_gui.cpp2
-rw-r--r--src/dos/dos.cpp12
-rw-r--r--src/dos/dos_devices.cpp2
-rw-r--r--src/dos/dos_ioctl.cpp617
-rw-r--r--src/dos/drive_fat.cpp369
-rw-r--r--src/dos/program_imgmount.cpp110
-rw-r--r--src/dosbox.cpp30
-rw-r--r--src/gui/sdlmain.cpp223
-rw-r--r--src/hardware/ide.cpp31
-rw-r--r--src/hardware/memory.cpp3
-rw-r--r--src/hardware/meson.build5
-rw-r--r--src/hardware/pci_bus.cpp63
-rw-r--r--src/hardware/pci_devices.h234
-rw-r--r--src/hardware/vga_draw.cpp15
-rw-r--r--src/hardware/voodoo.cpp188
-rw-r--r--src/hardware/voodoo.h42
-rw-r--r--src/hardware/voodoo_data.h2238
-rw-r--r--src/hardware/voodoo_def.h559
-rw-r--r--src/hardware/voodoo_emu.cpp3758
-rw-r--r--src/hardware/voodoo_emu.h61
-rw-r--r--src/hardware/voodoo_interface.cpp321
-rw-r--r--src/hardware/voodoo_interface.h65
-rw-r--r--src/hardware/voodoo_opengl.cpp1925
-rw-r--r--src/hardware/voodoo_opengl.h94
-rw-r--r--src/hardware/voodoo_types.h381
-rw-r--r--src/hardware/voodoo_vogl.cpp468
-rw-r--r--src/hardware/voodoo_vogl.h129
-rw-r--r--src/ints/bios_disk.cpp170
-rw-r--r--vs/dosbox.vcxproj5
-rw-r--r--vs/dosbox.vcxproj.filters15
38 files changed, 12180 insertions, 196 deletions
diff --git a/include/bios_disk.h b/include/bios_disk.h
index 12c3753cb..a0ae32824 100644
--- a/include/bios_disk.h
+++ b/include/bios_disk.h
@@ -73,7 +73,7 @@ public:
uint32_t sector_size;
uint32_t heads,cylinders,sectors;
private:
- uint32_t current_fpos;
+ uint64_t current_fpos;
enum { NONE,READ,WRITE } last_action;
};
diff --git a/include/dos_inc.h b/include/dos_inc.h
index caddb9b06..7a06ebfc7 100644
--- a/include/dos_inc.h
+++ b/include/dos_inc.h
@@ -267,6 +267,22 @@ static inline uint16_t long2para(uint32_t size) {
else return (uint16_t)(size>>4);
}
+/* fopen64, ftello64, fseeko64 */
+#if defined(__APPLE__)
+ #define fopen64 fopen
+ #define ftello64 ftell
+ #define fseeko64 fseek
+#elif defined (_MSC_VER)
+ #define fopen64 fopen
+ #if (_MSC_VER >= 1400)
+ #define ftello64 _ftelli64
+ #define fseeko64 _fseeki64
+ #else
+ #define ftello64 ftell
+ #define fseeko64 fseek
+ #endif
+#endif
+
/* Dos Error Codes */
#define DOSERR_NONE 0
#define DOSERR_FUNCTION_NUMBER_INVALID 1
@@ -577,8 +593,8 @@ public:
void SetDirID(uint16_t id) { SSET_WORD(sDTA, dirID, id); }
uint16_t GetDirID() const { return SGET_WORD(sDTA, dirID); }
- void SetDirIDCluster(uint16_t cl) { SSET_WORD(sDTA, dirCluster, cl); }
- uint16_t GetDirIDCluster() const { return SGET_WORD(sDTA, dirCluster); }
+ void SetDirIDCluster(uint32_t cl) { SSET_DWORD(sDTA, dirCluster, cl); }
+ uint32_t GetDirIDCluster() const { return SGET_DWORD(sDTA, dirCluster); }
private:
#ifdef _MSC_VER
@@ -590,8 +606,8 @@ private:
uint8_t sext[3]; /* The Search pattern for the extension */
uint8_t sattr; /* The Attributes that need to be found */
uint16_t dirID; /* custom: dir-search ID for multiple searches at the same time */
- uint16_t dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */
- uint8_t fill[4];
+ uint32_t dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */
+ uint8_t fill[2];
uint8_t attr;
uint16_t time;
uint16_t date;
diff --git a/include/drives.h b/include/drives.h
index 51f5ff543..b13f06ed2 100644
--- a/include/drives.h
+++ b/include/drives.h
@@ -128,6 +128,157 @@ struct bootstrap {
uint8_t magic2; /* 0xaa */
} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct FAT_BPB_MSDOS20 {
+ uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */
+ uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */
+ uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */
+ uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */
+ uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */
+ uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */
+ uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */
+ uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */
+} GCC_ATTRIBUTE(packed); /* ==> 0x018 size 0x00D total */
+
+struct FAT_BPB_MSDOS30 {
+ uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */
+ uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */
+ uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */
+ uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */
+ uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */
+ uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */
+ uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */
+ uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */
+ uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */
+ uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */
+ uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */
+} GCC_ATTRIBUTE(packed); /* ==> 0x020 size 0x015 total */
+ /* ==== ADDITIONAL NOTES (Wikipedia) */
+ /* offset 0x01C size 0x002 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.0) */
+ /* offset 0x01E size 0x002 Total sectors including hidden (?) if BPB_TotSec16 != 0 (MS-DOS 3.20) */
+
+struct FAT_BPB_MSDOS331 {
+ uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */
+ uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */
+ uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */
+ uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */
+ uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */
+ uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */
+ uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */
+ uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */
+ uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */
+ uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */
+ uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */
+ uint32_t BPB_TotSec32; /* offset 0x020 size 0x004 Total sectors of volume if count >= 0x10000 or FAT32, or 0 if not. totalsecdword */
+} GCC_ATTRIBUTE(packed); /* ==> 0x024 size 0x019 total */
+
+struct FAT_BPB_MSDOS40 { /* FAT12/FAT16 only */
+ uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */
+ uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */
+ uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */
+ uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */
+ uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */
+ uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */
+ uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */
+ uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */
+ uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */
+ uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */
+ uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */
+ uint32_t BPB_TotSec32; /* offset 0x020 size 0x004 Total sectors of volume if count >= 0x10000 or FAT32, or 0 if not. totalsecdword */
+ uint8_t BPB_DrvNum; /* offset 0x024 size 0x001 Physical (INT 13h) drive number */
+ uint8_t BPB_Reserved1; /* offset 0x025 size 0x001 Reserved? */
+ uint8_t BPB_BootSig; /* offset 0x026 size 0x001 Extended boot signature. 0x29 or 0x28 to indicate following members exist. */
+ uint32_t BPB_VolID; /* offset 0x027 size 0x004 Volume ID, if BPB_BootSig is 0x28 or 0x29. */
+ uint8_t BPB_VolLab[11]; /* offset 0x02B size 0x00B Volume label, if BPB_BootSig is 0x28 or 0x29. */
+ uint8_t BPB_FilSysType[8]; /* offset 0x036 size 0x008 File system type, for display purposes if BPB_BootSig is 0x29. */
+} GCC_ATTRIBUTE(packed); /* ==> 0x03E size 0x033 total */
+
+struct FAT_BPB_MSDOS710_FAT32 { /* FAT32 only */
+ uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */
+ uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */
+ uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */
+ uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */
+ uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */
+ uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */
+ uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */
+ uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */
+ uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */
+ uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */
+ uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */
+ uint32_t BPB_TotSec32; /* offset 0x020 size 0x004 Total sectors of volume if count >= 0x10000 or FAT32, or 0 if not. totalsecdword */
+ uint32_t BPB_FATSz32; /* offset 0x024 size 0x004 Sectors per fat (FAT32). */
+ uint16_t BPB_ExtFlags; /* offset 0x028 size 0x002 Bitfield: [7:7] 1=one fat active 0=mirrored [3:0]=active FAT if mirroring disabled */
+ uint16_t BPB_FSVer; /* offset 0x02A size 0x002 Version number. Only 0.0 is defined now. Do not mount if newer version beyond what we support */
+ uint32_t BPB_RootClus; /* offset 0x02C size 0x004 Starting cluster number of the root directory (FAT32) */
+ uint16_t BPB_FSInfo; /* offset 0x030 size 0x002 Sector number in volume of FAT32 FSInfo structure in reserved area */
+ uint16_t BPB_BkBootSec; /* offset 0x032 size 0x002 Sector number in volume of FAT32 backup boot sector */
+ uint8_t BPB_Reserved[12]; /* offset 0x034 size 0x00C Reserved for future expansion */
+ uint8_t BS_DrvNum; /* offset 0x040 size 0x001 BPB_DrvNum but moved for FAT32 */
+ uint8_t BS_Reserved1; /* offset 0x041 size 0x001 BPB_Reserved1 but moved for FAT32 */
+ uint8_t BS_BootSig; /* offset 0x042 size 0x001 Extended boot signature. 0x29 or 0x28 to indicate following members exist. */
+ uint32_t BS_VolID; /* offset 0x043 size 0x004 Volume ID, if BPB_BootSig is 0x28 or 0x29. */
+ uint8_t BS_VolLab[11]; /* offset 0x047 size 0x00B Volume label, if BPB_BootSig is 0x28 or 0x29. */
+ uint8_t BS_FilSysType[8]; /* offset 0x052 size 0x008 File system type, for display purposes if BPB_BootSig is 0x29. */
+} GCC_ATTRIBUTE(packed); /* ==> 0x05A size 0x04F total */
+
+typedef struct FAT_BPB_MSDOS40 FAT_BPB_MSDOS; /* what we use internally */
+typedef struct FAT_BPB_MSDOS710_FAT32 FAT32_BPB_MSDOS; /* what we use internally */
+
+struct FAT_BootSector {
+ /* --------- Common fields: Amalgam of Wikipedia documentation with names from Microsoft's FAT32 whitepaper */
+ uint8_t BS_jmpBoot[3]; /* offset 0x000 size 0x003 Jump instruction to boot code. Formerly nearjmp[3] */
+ uint8_t BS_OEMName[8]; /* offset 0x003 size 0x008 OEM string. Formerly oemname[8] */
+ /* --------- BIOS Parameter Block (converted in place from existing DOSBox-X code) */
+ union bpb_union_t {
+ struct FAT_BPB_MSDOS20 v20; /* offset 0x00B size 0x00D MS-DOS 2.0 BPB */
+ struct FAT_BPB_MSDOS30 v30; /* offset 0x00B size 0x015 MS-DOS 3.0 BPB */
+ struct FAT_BPB_MSDOS331 v331; /* offset 0x00B size 0x019 MS-DOS 3.31 BPB */
+ struct FAT_BPB_MSDOS40 v40; /* offset 0x00B size 0x039 MS-DOS 4.0 BPB (FAT12/FAT16) */
+ struct FAT_BPB_MSDOS710_FAT32 v710_32;/* offset 0x00B size 0x04F MS-DOS 7.10 BPB (FAT32) */
+
+ FAT_BPB_MSDOS v; /* offset 0x00B ... */
+ FAT32_BPB_MSDOS v32; /* offset 0x00B ... */
+
+ inline bool is_fat32(void) const {
+ return (v.BPB_RootEntCnt == 0 && v.BPB_TotSec16 == 0 && v.BPB_FATSz16 == 0); /* all fields are "must be set to 0" for FAT32 */
+ }
+ } bpb;
+ /* --------- The rest of the sector ---------- */
+ uint8_t bootcode[512 - 2/*magic*/ - sizeof(bpb_union_t) - 8/*OEM*/ - 3/*JMP*/];
+ uint8_t magic1; /* 0x55 */
+ uint8_t magic2; /* 0xaa */
+#define SECTOR_SIZE_MAX 2048
+#if SECTOR_SIZE_MAX > 512
+ uint8_t extra[SECTOR_SIZE_MAX - 512];
+#endif
+} GCC_ATTRIBUTE(packed);
+static_assert(offsetof(FAT_BootSector,bpb.v20) == 0x00B,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v30) == 0x00B,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v331) == 0x00B,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v40) == 0x00B,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v) == 0x00B,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v20.BPB_TotSec16) == 0x013,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v30.BPB_TotSec16) == 0x013,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v331.BPB_TotSec16) == 0x013,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_TotSec16) == 0x013,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v.BPB_TotSec16) == 0x013,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v331.BPB_TotSec32) == 0x020,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v30.BPB_HiddSec) == 0x01C,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_TotSec32) == 0x020,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_VolLab) == 0x02B,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v710_32.BS_FilSysType) == 0x052,"Oops");
+static_assert(sizeof(FAT_BootSector) == SECTOR_SIZE_MAX,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v331.BPB_TotSec32) == 0x020,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_TotSec32) == 0x020,"Oops");
+static_assert(offsetof(FAT_BootSector,bpb.v710_32.BPB_TotSec32) == 0x020,"Oops");
+static_assert(sizeof(FAT_BootSector::bpb.v20) == 0x00D,"Oops");
+static_assert(sizeof(FAT_BootSector::bpb.v30) == 0x015,"Oops");
+static_assert(sizeof(FAT_BootSector::bpb.v331) == 0x019,"Oops");
+static_assert(sizeof(FAT_BootSector::bpb.v40) == 0x033,"Oops");
+static_assert(sizeof(FAT_BootSector::bpb.v710_32) == 0x04F,"Oops");
+
struct direntry {
uint8_t entryname[11];
uint8_t attrib;
@@ -141,6 +292,13 @@ struct direntry {
uint16_t modDate;
uint16_t loFirstClust;
uint32_t entrysize;
+ inline uint32_t Cluster32(void) const {
+ return ((uint32_t)hiFirstClust << (uint32_t)16) + loFirstClust;
+ }
+ inline void SetCluster32(const uint32_t v) {
+ loFirstClust = (uint16_t)v;
+ hiFirstClust = (uint16_t)(v >> (uint32_t)16);
+ }
} GCC_ATTRIBUTE(packed);
struct partTable {
@@ -198,12 +356,18 @@ public:
uint32_t appendCluster(uint32_t startCluster);
void deleteClustChain(uint32_t startCluster, uint32_t bytePos);
uint32_t getFirstFreeClust(void);
+ unsigned long GetSerial();
+ uint32_t GetPartitionOffset();
bool directoryBrowse(uint32_t dirClustNumber, direntry *useEntry, int32_t entNum, int32_t start=0);
bool directoryChange(uint32_t dirClustNumber, direntry *useEntry, int32_t entNum);
std::shared_ptr<imageDisk> loadedDisk;
bool created_successfully;
uint32_t partSectOff;
-
+ FAT_BootSector::bpb_union_t GetBPB() {return BPB;}
+ void SetBPB(const FAT_BootSector::bpb_union_t &bpb);
+ virtual uint32_t getSectSize(void);
+ uint32_t sector_size = 0;
+ bool readonly = false;
private:
uint32_t getClusterValue(uint32_t clustNum);
void setClusterValue(uint32_t clustNum, uint32_t clustValue);
@@ -215,9 +379,9 @@ private:
void zeroOutCluster(uint32_t clustNumber);
bool getEntryName(char *fullname, char *entname);
- bootstrap bootbuffer;
+ FAT_BootSector::bpb_union_t BPB = {}; // BPB in effect (translated from on-disk BPB as needed)
+ FAT_BootSector bootbuffer = {};
bool absolute;
- bool readonly;
uint8_t fattype;
uint32_t CountOfClusters;
uint32_t firstDataSector;
diff --git a/include/logging.h b/include/logging.h
index ee594f090..1fc035282 100644
--- a/include/logging.h
+++ b/include/logging.h
@@ -35,6 +35,7 @@ enum LOG_TYPES {
LOG_MOUSE,LOG_BIOS,LOG_GUI,LOG_MISC,
LOG_IO,
LOG_PCI,
+ LOG_VOODOO,
LOG_MAX
};
diff --git a/include/pci_bus.h b/include/pci_bus.h
index 667b78b2d..d53c52b26 100644
--- a/include/pci_bus.h
+++ b/include/pci_bus.h
@@ -19,7 +19,7 @@
#ifndef DOSBOX_PCI_H
#define DOSBOX_PCI_H
-//#define PCI_FUNCTIONALITY_ENABLED 0
+#define PCI_FUNCTIONALITY_ENABLED 1
#if defined PCI_FUNCTIONALITY_ENABLED
@@ -74,6 +74,7 @@ public:
virtual bool OverrideReadRegister(uint8_t regnum, uint8_t* rval, uint8_t* rval_mask)=0;
virtual Bits ParseWriteRegister(uint8_t regnum,uint8_t value)=0;
virtual bool InitializeRegisters(uint8_t registers[256])=0;
+ virtual ~PCI_Device() = 0;
};
@@ -83,4 +84,7 @@ RealPt PCI_GetPModeInterface(void);
#endif
+void PCI_AddSST_Device(Bitu type);
+void PCI_RemoveSST_Device(void);
+
#endif
diff --git a/include/sdlmain.h b/include/sdlmain.h
index cbf17c200..18c569b42 100644
--- a/include/sdlmain.h
+++ b/include/sdlmain.h
@@ -55,14 +55,6 @@ enum MouseControlType {
NoMouse = 1 << 3
};
-enum SCREEN_TYPES {
- SCREEN_SURFACE,
- SCREEN_TEXTURE,
-#if C_OPENGL
- SCREEN_OPENGL
-#endif
-};
-
enum class FRAME_MODE {
UNSET,
CFR, // constant frame rate, as defined by the emulated system
@@ -118,6 +110,7 @@ struct SDL_Block {
struct {
int width = 0;
int height = 0;
+ int flags = 0;
double scalex = 1.0;
double scaley = 1.0;
double pixel_aspect = 1.0;
@@ -154,6 +147,8 @@ struct SDL_Block {
uint8_t bpp = 0;
double dpi_scale = 1.0;
bool fullscreen = false;
+ bool lazy_fullscreen = false;
+ bool lazy_fullscreen_req = false;
// This flag indicates, that we are in the process of switching
// between fullscreen or window (as oppososed to changing
diff --git a/include/vga.h b/include/vga.h
index 63e604cac..e5a1676cd 100644
--- a/include/vga.h
+++ b/include/vga.h
@@ -190,6 +190,7 @@ struct VGA_Draw {
double custom_refresh_hz = REFRESH_RATE_DOS_DEFAULT;
VGA_RATE_MODE dos_rate_mode = VGA_RATE_MODE::DEFAULT;
double aspect_ratio = 0;
+ bool vga_override = false;
bool double_scan = false;
bool doublewidth = false;
bool doubleheight = false;
@@ -530,6 +531,7 @@ void VGA_SetCGA2Table(uint8_t val0,uint8_t val1);
void VGA_SetCGA4Table(uint8_t val0,uint8_t val1,uint8_t val2,uint8_t val3);
void VGA_ActivateHardwareCursor(void);
void VGA_KillDrawing(void);
+void VGA_SetOverride(bool vga_override);
void VGA_LogInitialization(const char *adapter_name,
const char *ram_type,
diff --git a/include/video.h b/include/video.h
index 456574acd..5757187b7 100644
--- a/include/video.h
+++ b/include/video.h
@@ -22,6 +22,7 @@
#include <string>
#include "types.h"
+#include <SDL.h>
#define REDUCE_JOYSTICK_POLLING
@@ -31,6 +32,14 @@ typedef enum {
GFX_CallBackRedraw
} GFX_CallBackFunctions_t;
+enum SCREEN_TYPES {
+ SCREEN_SURFACE,
+ SCREEN_TEXTURE,
+#if C_OPENGL
+ SCREEN_OPENGL
+#endif
+};
+
typedef void (*GFX_CallBack_t)( GFX_CallBackFunctions_t function );
#define GFX_CAN_8 0x0001
@@ -74,16 +83,30 @@ void GFX_ResetScreen(void);
void GFX_RequestExit(const bool requested);
void GFX_Start(void);
void GFX_Stop(void);
+bool GFX_IsFullscreen(void);
void GFX_SwitchFullScreen(void);
+void GFX_SwitchFullscreenNoReset(void);
+bool GFX_LazyFullscreenRequested(void);
+void GFX_RestoreMode(void);
bool GFX_StartUpdate(uint8_t * &pixels, int &pitch);
void GFX_EndUpdate( const uint16_t *changedLines );
void GFX_GetSize(int &width, int &height, bool &fullscreen);
void GFX_UpdateMouseState();
-void GFX_LosingFocus();
void GFX_RegenerateWindow(Section *sec);
void GFX_SetMouseRawInput(const bool raw_input);
void GFX_MouseCaptureAfterMapping();
bool GFX_MouseIsAvailable();
+void GFX_LosingFocus(void);
+void SetTransparency(void);
+void OpenGL_On();
+void OpenGL_Off();
+bool OpenGL_using(void);
+SDL_Window* GFX_GetSDLWindow(void);
+SDL_Window *SetWindowMode(SCREEN_TYPES screen_type,
+ int width,
+ int height,
+ bool fullscreen,
+ bool resizable);
#if defined (REDUCE_JOYSTICK_POLLING)
void MAPPER_UpdateJoysticks(void);
diff --git a/src/debug/debug_gui.cpp b/src/debug/debug_gui.cpp
index c67490cca..9bb3b917b 100644
--- a/src/debug/debug_gui.cpp
+++ b/src/debug/debug_gui.cpp
@@ -274,6 +274,8 @@ void LOG_StartUp(void) {
loggrp[LOG_IO].front="IO";
loggrp[LOG_PCI].front="PCI";
+ loggrp[LOG_VOODOO].front="SST";
+
/* Register the log section */
Section_prop * sect=control->AddSection_prop("log",LOG_Init);
Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,"");
diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp
index 6081899ec..46cf79472 100644
--- a/src/dos/dos.cpp
+++ b/src/dos/dos.cpp
@@ -1267,6 +1267,18 @@ static Bitu DOS_21Handler(void) {
}
CALLBACK_SCF(false);
break;
+ case 0x23: /* Determine if character represents yes/no response */
+ {
+ unsigned int c = reg_dl;
+ if (tolower(c) == 'y')
+ reg_ax = 1;/*yes*/
+ else if (tolower(c) == 'n')
+ reg_ax = 0;/*no*/
+ else
+ reg_ax = 2;/*neither*/
+ }
+ CALLBACK_SCF(false);
+ break;
default:
E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al);
};
diff --git a/src/dos/dos_devices.cpp b/src/dos/dos_devices.cpp
index 050653ca4..885c83f88 100644
--- a/src/dos/dos_devices.cpp
+++ b/src/dos/dos_devices.cpp
@@ -205,7 +205,7 @@ uint32_t DOS_CheckExtDevice(const char *name, bool already_flag)
no = real_readw(seg, off + 4);
next_seg = real_readw(seg, off + 2);
next_off = real_readw(seg, off);
- if (next_seg == 0xffff && next_off == 0xffff) {
+ if ((next_seg == 0xffff && next_off == 0xffff) || (no == 0 && next_seg == 0 && next_off == 0)) {
break;
}
if (no & 0x8000) {
diff --git a/src/dos/dos_ioctl.cpp b/src/dos/dos_ioctl.cpp
index 960465b89..9a7ad4be3 100644
--- a/src/dos/dos_ioctl.cpp
+++ b/src/dos/dos_ioctl.cpp
@@ -22,7 +22,570 @@
#include "callback.h"
#include "mem.h"
#include "regs.h"
+#include "bios_disk.h"
#include "dos_inc.h"
+#include "drives.h"
+
+bool DOS_IOCTL_AX440D_CH08(uint8_t drive,bool query) {
+ PhysPt ptr = SegPhys(ds)+reg_dx;
+ switch (reg_cl) {
+ case 0x40: /* Set device parameters */
+ {
+ if (Drives[drive]->GetType() != DosDriveType::Fat) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL || fdp->readonly) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ if (query) break;
+
+ FAT_BootSector::bpb_union_t bpb=fdp->GetBPB();
+ if (fdp->loadedDisk != NULL)
+ fdp->loadedDisk->cylinders = mem_readw(ptr+4); // number of cylinders
+
+ if (mem_readw(ptr+0xd) == 0 && mem_readw(ptr+0xf) == 0 && mem_readw(ptr+0x12) == 0) { // FAT32 BPB?
+ bpb.v32.BPB_BytsPerSec = mem_readw(ptr+7); // bytes per sector (Win3 File Mgr. uses it)
+ bpb.v32.BPB_SecPerClus = mem_readb(ptr+9); // sectors per cluster
+ bpb.v32.BPB_RsvdSecCnt = mem_readw(ptr+0xa); // number of reserved sectors
+ bpb.v32.BPB_NumFATs = mem_readb(ptr+0xc); // number of FATs
+ bpb.v32.BPB_RootEntCnt = mem_readw(ptr+0xd); // number of root entries (Fake, the real BPB value is zero)
+ bpb.v32.BPB_TotSec16 = mem_readw(ptr+0xf); // number of small sectors (always zero on BPB and returned by Win98)
+ bpb.v32.BPB_Media = mem_readb(ptr+0x11); // media type
+ bpb.v32.BPB_FATSz32 = (uint16_t)mem_readw(ptr+0x12); // sectors per FAT (FIXME: What does Win98 do if this value > 0xFFFF?)
+ bpb.v32.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14); // sectors per track
+ bpb.v32.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16); // number of heads
+ bpb.v32.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18); // number of hidden sectors
+ bpb.v32.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c); // number of big sectors
+ } else {
+ bpb.v.BPB_BytsPerSec = mem_readw(ptr+7); // bytes per sector (Win3 File Mgr. uses it)
+ bpb.v.BPB_SecPerClus = mem_readb(ptr+9); // sectors per cluster
+ bpb.v.BPB_RsvdSecCnt = mem_readw(ptr+0xa); // number of reserved sectors
+ bpb.v.BPB_NumFATs = mem_readb(ptr+0xc); // number of FATs
+ bpb.v.BPB_RootEntCnt = mem_readw(ptr+0xd); // number of root entries
+ bpb.v.BPB_TotSec16 = mem_readw(ptr+0xf); // number of small sectors
+ bpb.v.BPB_Media = mem_readb(ptr+0x11); // media type
+ bpb.v.BPB_FATSz16 = (uint16_t)mem_readw(ptr+0x12); // sectors per FAT
+ bpb.v.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14); // sectors per track
+ bpb.v.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16); // number of heads
+ bpb.v.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18); // number of hidden sectors
+ bpb.v.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c); // number of big sectors
+ }
+ fdp->SetBPB(bpb);
+ break;
+ }
+ case 0x60: /* Get device parameters */
+ if (query) break;
+ {
+ //mem_writeb(ptr+0,0); // special functions (call value)
+ mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
+ mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
+ mem_writew(ptr+4,(drive>=2)?0x3FF:0x50);// number of cylinders
+ mem_writeb(ptr+6,0x00); // media type (00=other type)
+ // bios parameter block following
+ fatDrive *fdp;
+ FAT_BootSector::bpb_union_t bpb;
+ bool usereal=false;
+ if (Drives[drive]->GetType() == DosDriveType::Fat) {
+ fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp != NULL) {
+ bpb=fdp->GetBPB();
+ if (bpb.v.BPB_BytsPerSec && bpb.v.BPB_Media)
+ usereal=true;
+ }
+ }
+ if (usereal) {
+ if (fdp->loadedDisk != NULL)
+ mem_writew(ptr+4,fdp->loadedDisk->cylinders); // number of cylinders
+
+ if (bpb.is_fat32()) {
+ /* Windows 98 behavior: Some of the FAT32 BPB fields are translated into FAT16 BPB fields even though those fields are zero in the actual BPB */
+ mem_writew(ptr+7,bpb.v32.BPB_BytsPerSec); // bytes per sector (Win3 File Mgr. uses it)
+ mem_writeb(ptr+9,bpb.v32.BPB_SecPerClus); // sectors per cluster
+ mem_writew(ptr+0xa,bpb.v32.BPB_RsvdSecCnt); // number of reserved sectors
+ mem_writeb(ptr+0xc,bpb.v32.BPB_NumFATs); // number of FATs
+ mem_writew(ptr+0xd,0x200); // number of root entries (Fake, the real BPB value is zero)
+ mem_writew(ptr+0xf,0); // number of small sectors (always zero on BPB and returned by Win98)
+ mem_writeb(ptr+0x11,bpb.v32.BPB_Media); // media type
+ mem_writew(ptr+0x12,(uint16_t)bpb.v32.BPB_FATSz32); // sectors per FAT (FIXME: What does Win98 do if this value > 0xFFFF?)
+ mem_writew(ptr+0x14,(uint16_t)bpb.v32.BPB_SecPerTrk); // sectors per track
+ mem_writew(ptr+0x16,(uint16_t)bpb.v32.BPB_NumHeads); // number of heads
+ mem_writed(ptr+0x18,(uint32_t)bpb.v32.BPB_HiddSec); // number of hidden sectors
+ mem_writed(ptr+0x1c,(uint32_t)bpb.v32.BPB_TotSec32); // number of big sectors
+ }
+ else {
+ mem_writew(ptr+7,bpb.v.BPB_BytsPerSec); // bytes per sector (Win3 File Mgr. uses it)
+ mem_writeb(ptr+9,bpb.v.BPB_SecPerClus); // sectors per cluster
+ mem_writew(ptr+0xa,bpb.v.BPB_RsvdSecCnt); // number of reserved sectors
+ mem_writeb(ptr+0xc,bpb.v.BPB_NumFATs); // number of FATs
+ mem_writew(ptr+0xd,bpb.v.BPB_RootEntCnt); // number of root entries
+ mem_writew(ptr+0xf,bpb.v.BPB_TotSec16); // number of small sectors
+ mem_writeb(ptr+0x11,bpb.v.BPB_Media); // media type
+ mem_writew(ptr+0x12,(uint16_t)bpb.v.BPB_FATSz16); // sectors per FAT
+ mem_writew(ptr+0x14,(uint16_t)bpb.v.BPB_SecPerTrk); // sectors per track
+ mem_writew(ptr+0x16,(uint16_t)bpb.v.BPB_NumHeads); // number of heads
+ mem_writed(ptr+0x18,(uint32_t)bpb.v.BPB_HiddSec); // number of hidden sectors
+ mem_writed(ptr+0x1c,(uint32_t)bpb.v.BPB_TotSec32); // number of big sectors
+ }
+ } else {
+ mem_writew(ptr+7,0x0200); // bytes per sector (Win3 File Mgr. uses it)
+ mem_writew(ptr+9,(drive>=2)?0x20:0x01); // sectors per cluster
+ mem_writew(ptr+0xa,0x0001); // number of reserved sectors
+ mem_writew(ptr+0xc,0x02); // number of FATs
+ mem_writew(ptr+0xd,(drive>=2)?0x0200:0x00E0); // number of root entries
+ mem_writew(ptr+0xf,(drive>=2)?0x0000:0x0B40); // number of small sectors
+ mem_writew(ptr+0x11,(drive>=2)?0xF8:0xF0); // media type
+ mem_writew(ptr+0x12,(drive>=2)?0x00C9:0x0009); // sectors per FAT
+ mem_writew(ptr+0x14,(drive>=2)?0x003F:0x0012); // sectors per track
+ mem_writew(ptr+0x16,(drive>=2)?0x10:0x02); // number of heads
+ mem_writed(ptr+0x18,0); // number of hidden sectors
+ mem_writed(ptr+0x1c,(drive>=2)?0x31F11:0x00); // number of big sectors
+ }
+ for (int i=0x20; i<0x22; i++)
+ mem_writeb(ptr+i,0);
+ break;
+ }
+ case 0x42: /* Format and verify logical device track (FORMAT.COM) */
+ {
+ /* 01h WORD number of disk head
+ * 03h WORD number of disk cylinder
+ * ---BYTE 00h bit 1 set---
+ * 05h WORD number of tracks to format */
+ uint8_t flags = mem_readb(ptr+0);
+ uint16_t head = mem_readw(ptr+1);
+ uint16_t cyl = mem_readw(ptr+3);
+ uint16_t ntracks = (flags & 0x1) ? mem_readw(ptr+5) : 1;
+ uint16_t sect = 0;
+
+ fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL || fdp->readonly) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ if (query) break;
+
+ /* BUT: These are C/H/S values relative to the partition!
+ * FIXME: MS-DOS may not adjust sector value, or maybe it does...
+ * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
+ {
+ uint32_t adj = fdp->GetPartitionOffset();
+ sect += adj % fdp->loadedDisk->sectors;
+ adj /= fdp->loadedDisk->sectors;
+ head += adj % fdp->loadedDisk->heads;
+ adj /= fdp->loadedDisk->heads;
+ cyl += adj;
+
+ while (sect >= fdp->loadedDisk->sectors) {
+ sect -= fdp->loadedDisk->sectors;
+ head++;
+ }
+ while (head >= fdp->loadedDisk->heads) {
+ head -= fdp->loadedDisk->heads;
+ cyl++;
+ }
+
+ /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
+ sect++;
+ }
+
+ // STUB!
+ LOG_INFO("DOS:IOCTL Call 0D:42 Drive %2X pretending to format device track C/H/S=%u/%u/%u ntracks=%u",drive,cyl,head,sect,ntracks);
+ }
+ break;
+ case 0x62: /* Verify logical device track (FORMAT.COM) */
+ {
+ /* 01h WORD number of disk head
+ * 03h WORD number of disk cylinder
+ * 05h WORD number of tracks to verify */
+ uint16_t head = mem_readw(ptr+1);
+ uint16_t cyl = mem_readw(ptr+3);
+ uint16_t ntracks = mem_readw(ptr+5);
+ uint16_t sect = 0;
+
+ fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ if (query) break;
+
+ /* BUT: These are C/H/S values relative to the partition!
+ * FIXME: MS-DOS may not adjust sector value, or maybe it does...
+ * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
+ {
+ uint32_t adj = fdp->GetPartitionOffset();
+ sect += adj % fdp->loadedDisk->sectors;
+ adj /= fdp->loadedDisk->sectors;
+ head += adj % fdp->loadedDisk->heads;
+ adj /= fdp->loadedDisk->heads;
+ cyl += adj;
+
+ while (sect >= fdp->loadedDisk->sectors) {
+ sect -= fdp->loadedDisk->sectors;
+ head++;
+ }
+ while (head >= fdp->loadedDisk->heads) {
+ head -= fdp->loadedDisk->heads;
+ cyl++;
+ }
+
+ /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
+ sect++;
+ }
+
+ // STUB!
+ LOG_INFO("DOS:IOCTL Call 0D:62 Drive %2X pretending to verify device track C/H/S=%u/%u/%u ntracks=%u",drive,cyl,head,sect,ntracks);
+ }
+ break;
+ case 0x46: /* Set volume serial number */
+ if (query) break;
+ {
+ fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL || fdp->readonly) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ FAT_BootSector::bpb_union_t bpb=fdp->GetBPB();
+ unsigned long serial_number=mem_readd(ptr+2)?mem_readd(ptr+2):0x1234;
+ if (bpb.is_fat32())
+ bpb.v32.BS_VolID=serial_number;
+ else
+ bpb.v.BPB_VolID=serial_number;
+ fdp->SetBPB(bpb);
+ }
+ break;
+ case 0x66: /* Get volume serial number */
+ if (query) break;
+ {
+ char const* bufin=Drives[drive]->GetLabel();
+ char buffer[11];memset(buffer,' ',11);
+
+ char const* find_ext=strchr(bufin,'.');
+ if (find_ext) {
+ Bitu size=(Bitu)(find_ext-bufin);
+ if (size>8) size=8;
+ memcpy(buffer,bufin,size);
+ find_ext++;
+ memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext));
+ } else {
+ memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin));
+ }
+
+ char buf2[8]={ 'F','A','T','1','6',' ',' ',' '};
+ if(drive<2) buf2[4] = '2'; //FAT12 for floppies
+
+ //mem_writew(ptr+0,0); //Info level (call value)
+ unsigned long serial_number=0x1234;
+ if (Drives[drive]->GetType() == DosDriveType::Fat) {
+ fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp != NULL) serial_number=fdp->GetSerial();
+ }
+ mem_writed(ptr+2,serial_number);//Serial number
+ MEM_BlockWrite(ptr+6,buffer,11);//volumename
+ MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem
+ }
+ break;
+ case 0x41: /* Write logical device track */
+ {
+ fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL || fdp->readonly) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ uint8_t sectbuf[SECTOR_SIZE_MAX];
+
+ if (fdp->loadedDisk == NULL) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ if (query) break;
+
+ /* (RBIL) [http://www.ctyme.com/intr/rb-2896.htm]
+ * Offset Size Description (Table 01562)
+ * 00h BYTE special functions (reserved, must be zero)
+ * 01h WORD number of disk head
+ * 03h WORD number of disk cylinder
+ * 05h WORD number of first sector to read/write
+ * 07h WORD number of sectors
+ * 09h DWORD transfer address */
+ uint16_t head = mem_readw(ptr+1);
+ uint16_t cyl = mem_readw(ptr+3);
+ uint16_t sect = mem_readw(ptr+5);
+ uint16_t nsect = mem_readw(ptr+7);
+ uint32_t xfer_addr = mem_readd(ptr+9);
+ PhysPt xfer_ptr = ((xfer_addr>>16u)<<4u)+(xfer_addr&0xFFFFu);
+ uint16_t sectsize = fdp->loadedDisk->getSectSize();
+
+ /* BUT: These are C/H/S values relative to the partition!
+ * FIXME: MS-DOS may not adjust sector value, or maybe it does...
+ * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
+ {
+ uint32_t adj = fdp->GetPartitionOffset();
+ sect += adj % fdp->loadedDisk->sectors;
+ adj /= fdp->loadedDisk->sectors;
+ head += adj % fdp->loadedDisk->heads;
+ adj /= fdp->loadedDisk->heads;
+ cyl += adj;
+
+ while (sect >= fdp->loadedDisk->sectors) {
+ sect -= fdp->loadedDisk->sectors;
+ head++;
+ }
+ while (head >= fdp->loadedDisk->heads) {
+ head -= fdp->loadedDisk->heads;
+ cyl++;
+ }
+
+ /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
+ sect++;
+ }
+
+ LOG_INFO("DOS:IOCTL Call 0D:41 Write Logical Device Track from Drive %2X C/H/S=%u/%u/%u num=%u from %04x:%04x sz=%u",
+ drive,cyl,head,sect,nsect,xfer_addr >> 16,xfer_addr & 0xFFFF,sectsize);
+
+ while (nsect > 0) {
+ MEM_BlockRead(xfer_ptr,sectbuf,sectsize);
+
+ uint8_t status = fdp->loadedDisk->Write_Sector(head,cyl,sect,sectbuf);
+ if (status != 0) {
+ LOG_INFO("IOCTL 0D:61 write error at C/H/S %u/%u/%u",cyl,head,sect);
+ DOS_SetError(DOSERR_ACCESS_DENIED);//FIXME
+ return false;
+ }
+
+ xfer_ptr += sectsize;
+ nsect--;
+ sect++;
+ }
+ }
+ break;
+ case 0x61: /* Read logical device track */
+ {
+ fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ uint8_t sectbuf[SECTOR_SIZE_MAX];
+
+ if (fdp->loadedDisk == NULL) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ if (query) break;
+
+ /* (RBIL) [http://www.ctyme.com/intr/rb-2896.htm]
+ * Offset Size Description (Table 01562)
+ * 00h BYTE special functions (reserved, must be zero)
+ * 01h WORD number of disk head
+ * 03h WORD number of disk cylinder
+ * 05h WORD number of first sector to read/write
+ * 07h WORD number of sectors
+ * 09h DWORD transfer address */
+ uint16_t head = mem_readw(ptr+1);
+ uint16_t cyl = mem_readw(ptr+3);
+ uint16_t sect = mem_readw(ptr+5);
+ uint16_t nsect = mem_readw(ptr+7);
+ uint32_t xfer_addr = mem_readd(ptr+9);
+ PhysPt xfer_ptr = ((xfer_addr>>16u)<<4u)+(xfer_addr&0xFFFFu);
+ uint16_t sectsize = fdp->loadedDisk->getSectSize();
+
+ /* BUT: These are C/H/S values relative to the partition!
+ * FIXME: MS-DOS may not adjust sector value, or maybe it does...
+ * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
+ {
+ uint32_t adj = fdp->GetPartitionOffset();;
+ sect += adj % fdp->loadedDisk->sectors;
+ adj /= fdp->loadedDisk->sectors;
+ head += adj % fdp->loadedDisk->heads;
+ adj /= fdp->loadedDisk->heads;
+ cyl += adj;
+
+ while (sect >= fdp->loadedDisk->sectors) {
+ sect -= fdp->loadedDisk->sectors;
+ head++;
+ }
+ while (head >= fdp->loadedDisk->heads) {
+ head -= fdp->loadedDisk->heads;
+ cyl++;
+ }
+
+ /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
+ sect++;
+ }
+
+ LOG_INFO("DOS:IOCTL Call 0D:61 Read Logical Device Track from Drive %2X C/H/S=%u/%u/%u num=%u to %04x:%04x sz=%u",
+ drive,cyl,head,sect,nsect,xfer_addr >> 16,xfer_addr & 0xFFFF,sectsize);
+
+ while (nsect > 0) {
+ uint8_t status = fdp->loadedDisk->Read_Sector(head,cyl,sect,sectbuf);
+ if (status != 0) {
+ LOG_INFO("IOCTL 0D:61 read error at C/H/S %u/%u/%u",cyl,head,sect);
+ DOS_SetError(DOSERR_ACCESS_DENIED);//FIXME
+ return false;
+ }
+
+ MEM_BlockWrite(xfer_ptr,sectbuf,sectsize);
+ xfer_ptr += sectsize;
+ nsect--;
+ sect++;
+ }
+ }
+ break;
+ case 0x4A:
+ case 0x4B:
+ case 0x6A:
+ case 0x6B:
+ if (query) break;
+ LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X volume/drive locking IOCTL, faking it",reg_cl,drive);
+ break;
+ case 0x67: /* Get access flag (whether allowed by driver) */
+ if (query) break;
+ /* In DOSBox-X, disk access is always allowed.
+ * Real MS-DOS might be more restrictive, especially Windows 95 which requires volume locking before disk access is allowed */
+ /* FDISK.EXE needs this IOCTL to determine whether it can read the partition and therefore whether the "system type" is "FAT16", "FAT12", "unknown" etc. */
+ /* ptr+0 = special function (always zero)
+ * ptr+1 = return whether access allowed */
+ mem_writeb(ptr+1,0x01);
+ break;
+ default:
+ LOG_WARNING("DOS:IOCTL %s %2X:%2X Drive %2X unhandled (CH=08h)",query?"Query":"Call",reg_al,reg_cl,drive);
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ reg_ax=0;
+ return true;
+}
+
+bool DOS_IOCTL_AX440D_CH48(uint8_t drive,bool query) {
+ PhysPt ptr = SegPhys(ds)+reg_dx;
+ switch (reg_cl) {
+ case 0x40: /* Set device parameters */
+ {
+ if (Drives[drive]->GetType() != DosDriveType::Fat) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp == NULL || fdp->readonly) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ if (query) break;
+
+ FAT_BootSector::bpb_union_t bpb=fdp->GetBPB();
+ if (fdp->loadedDisk != NULL)
+ fdp->loadedDisk->cylinders = mem_readw(ptr+4); // number of cylinders
+
+ if (mem_readw(ptr+0xd) == 0 && mem_readw(ptr+0xf) == 0 && mem_readw(ptr+0x12) == 0) { // FAT32 BPB?
+ bpb.v.BPB_BytsPerSec = mem_readw(ptr+7); // bytes per sector (Win3 File Mgr. uses it)
+ bpb.v.BPB_SecPerClus = mem_readb(ptr+9); // sectors per cluster
+ bpb.v.BPB_RsvdSecCnt = mem_readw(ptr+0xa); // number of reserved sectors
+ bpb.v.BPB_NumFATs = mem_readb(ptr+0xc); // number of FATs
+ bpb.v.BPB_RootEntCnt = mem_readw(ptr+0xd); // number of root entries
+ bpb.v.BPB_TotSec16 = mem_readw(ptr+0xf); // number of small sectors
+ bpb.v.BPB_Media = mem_readb(ptr+0x11); // media type
+ bpb.v.BPB_FATSz16 = (uint16_t)mem_readw(ptr+0x12); // sectors per FAT
+ bpb.v.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14); // sectors per track
+ bpb.v.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16); // number of heads
+ bpb.v.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18); // number of hidden sectors
+ bpb.v.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c); // number of big sectors
+ bpb.v32.BPB_FATSz32 = (uint32_t)mem_readd(ptr+0x20); // sectors per FAT
+ bpb.v32.BPB_ExtFlags = (uint16_t)mem_readw(ptr+0x24);
+ bpb.v32.BPB_FSVer = (uint16_t)mem_readw(ptr+0x26);
+ bpb.v32.BPB_RootClus = (uint32_t)mem_readd(ptr+0x28);
+ bpb.v32.BPB_FSInfo = (uint16_t)mem_readw(ptr+0x2C);
+ bpb.v32.BPB_BkBootSec = (uint16_t)mem_readw(ptr+0x2E);
+ fdp->SetBPB(bpb);
+ } else {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ break;
+ }
+ case 0x60: /* Get device parameters */
+ if (query) break;
+ {
+ //mem_writeb(ptr+0,0); // special functions (call value)
+ mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
+ mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
+ mem_writew(ptr+4,(drive>=2)?0x3FF:0x50);// num of cylinders
+ mem_writeb(ptr+6,0x00); // media type (00=other type)
+ // bios parameter block following
+ fatDrive *fdp;
+ FAT_BootSector::bpb_union_t bpb;
+ bool usereal=false;
+ if (Drives[drive]->GetType() == DosDriveType::Fat) {
+ fdp = dynamic_cast<fatDrive*>(Drives[drive]);
+ if (fdp != NULL) {
+ bpb=fdp->GetBPB();
+ if (bpb.v.BPB_BytsPerSec && bpb.v.BPB_Media)
+ usereal=true;
+ }
+ }
+ if (usereal) {
+ if (fdp->loadedDisk != NULL)
+ mem_writew(ptr+4,fdp->loadedDisk->cylinders); // num of cylinders
+
+ if (bpb.is_fat32()) {
+ mem_writew(ptr+7,bpb.v.BPB_BytsPerSec); // bytes per sector (Win3 File Mgr. uses it)
+ mem_writeb(ptr+9,bpb.v.BPB_SecPerClus); // sectors per cluster
+ mem_writew(ptr+0xa,bpb.v.BPB_RsvdSecCnt); // number of reserved sectors
+ mem_writeb(ptr+0xc,bpb.v.BPB_NumFATs); // number of FATs
+ mem_writew(ptr+0xd,bpb.v.BPB_RootEntCnt); // number of root entries
+ mem_writew(ptr+0xf,bpb.v.BPB_TotSec16); // number of small sectors
+ mem_writeb(ptr+0x11,bpb.v.BPB_Media); // media type
+ mem_writew(ptr+0x12,(uint16_t)bpb.v.BPB_FATSz16); // sectors per FAT
+ mem_writew(ptr+0x14,(uint16_t)bpb.v.BPB_SecPerTrk); // sectors per track
+ mem_writew(ptr+0x16,(uint16_t)bpb.v.BPB_NumHeads); // number of heads
+ mem_writed(ptr+0x18,(uint32_t)bpb.v.BPB_HiddSec); // number of hidden sectors
+ mem_writed(ptr+0x1c,(uint32_t)bpb.v.BPB_TotSec32); // number of big sectors
+ mem_writed(ptr+0x20,(uint32_t)bpb.v32.BPB_FATSz32); // sectors per FAT
+ mem_writew(ptr+0x24,(uint16_t)bpb.v32.BPB_ExtFlags);
+ mem_writew(ptr+0x26,(uint16_t)bpb.v32.BPB_FSVer);
+ mem_writed(ptr+0x28,(uint32_t)bpb.v32.BPB_RootClus);
+ mem_writew(ptr+0x2C,(uint16_t)bpb.v32.BPB_FSInfo);
+ mem_writew(ptr+0x2E,(uint16_t)bpb.v32.BPB_BkBootSec);
+ }
+ else {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ } else {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ break;
+ }
+ case 0x42:
+ case 0x46:
+ case 0x4A:
+ case 0x4B:
+ case 0x61:
+ case 0x62:
+ case 0x66:
+ case 0x6A:
+ case 0x6B:
+ return DOS_IOCTL_AX440D_CH08(drive,query);
+ default:
+ LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call %02X:%2X Drive %2X unhandled (CH=48h)",reg_al,reg_cl,drive);
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ reg_ax=0;
+ return true;
+}
bool DOS_IOCTL(void) {
// LOG(LOG_IOCTL,LOG_WARN)("%X %X %X %X",reg_ax,reg_bx,reg_cx,reg_dx);
@@ -156,60 +719,28 @@ bool DOS_IOCTL(void) {
}
return true;
case 0x0D: /* Generic block device request */
+ case 0x11: /* query generic ioctl capability */
{
if (drive < 2 && !Drives[drive]) {
DOS_SetError(DOSERR_ACCESS_DENIED);
return false;
}
- if (reg_ch != 0x08 || Drives[drive]->isRemovable()) {
+ if (Drives[drive]->isRemovable()) {
+ LOG_INFO("Attempt IOCTL AX=%04x CX=%04x on removable drive",reg_ax,reg_cx);
DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
return false;
}
- PhysPt ptr = SegPhys(ds)+reg_dx;
- switch (reg_cl) {
- case 0x60: /* Get Device parameters */
- //mem_writeb(ptr+0,0); // special functions (call value)
- mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
- mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
- mem_writew(ptr+4,0x0000); // num of cylinders
- mem_writeb(ptr+6,0x00); // media type (00=other type)
- // bios parameter block following
- mem_writew(ptr+7,0x0200); // bytes per sector (Win3 File Mgr. uses it)
- break;
- case 0x46: /* Set volume serial number */
- break;
- case 0x66: /* Get volume serial number */
- {
- char const* bufin=Drives[drive]->GetLabel();
- char buffer[11];memset(buffer,' ',11);
-
- char const* find_ext=strchr(bufin,'.');
- if (find_ext) {
- Bitu size=(Bitu)(find_ext-bufin);
- if (size>8) size=8;
- memcpy(buffer,bufin,size);
- find_ext++;
- memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext));
- } else {
- memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin));
- }
-
- char buf2[8]={ 'F','A','T','1','6',' ',' ',' '};
- if(drive<2) buf2[4] = '2'; //FAT12 for floppies
-
- //mem_writew(ptr+0,0); //Info level (call value)
- mem_writed(ptr+2,0x1234); //Serial number
- MEM_BlockWrite(ptr+6,buffer,11);//volumename
- MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem
- }
- break;
- default :
- LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled",reg_cl,drive);
+ if (reg_ch == 0x08) {
+ return DOS_IOCTL_AX440D_CH08(drive,reg_al==0x11);
+ }
+ else if (reg_ch == 0x48) {
+ return DOS_IOCTL_AX440D_CH48(drive,reg_al==0x11); // Same functions as CH=08h but for FAT32 drives
+ }
+ else {
+ LOG_INFO("Attempt IOCTL AX=%04x CX=%04x",reg_ax,reg_cx);
DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
return false;
}
- reg_ax=0;
- return true;
}
case 0x0E: /* Get Logical Drive Map */
if (drive < 2) {
diff --git a/src/dos/drive_fat.cpp b/src/dos/drive_fat.cpp
index 796060261..b7d58abe8 100644
--- a/src/dos/drive_fat.cpp
+++ b/src/dos/drive_fat.cpp
@@ -246,7 +246,10 @@ bool fatFile::Write(uint8_t * data, uint16_t *size) {
finalizeWrite:
myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex);
tmpentry.entrysize = filelength;
- tmpentry.loFirstClust = (uint16_t)firstCluster;
+ if (myDrive->GetBPB().is_fat32())
+ tmpentry.SetCluster32(firstCluster);
+ else
+ tmpentry.loFirstClust = (uint16_t)firstCluster;
myDrive->directoryChange(dirCluster, &tmpentry, dirIndex);
*size =sizecount;
@@ -301,7 +304,7 @@ bool fatFile::UpdateDateTimeFromHost(void) {
}
uint32_t fatDrive::getClustFirstSect(uint32_t clustNum) {
- return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector;
+ return ((clustNum - 2) * BPB.v.BPB_SecPerClus) + firstDataSector;
}
uint32_t fatDrive::getClusterValue(uint32_t clustNum) {
@@ -321,8 +324,8 @@ uint32_t fatDrive::getClusterValue(uint32_t clustNum) {
fatoffset = clustNum * 4;
break;
}
- fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
- fatentoff = fatoffset % bootbuffer.bytespersector;
+ fatsectnum = BPB.v.BPB_RsvdSecCnt + (fatoffset / BPB.v.BPB_BytsPerSec) + partSectOff;
+ fatentoff = fatoffset % BPB.v.BPB_BytsPerSec;
if(curFatSect != fatsectnum) {
/* Load two sectors at once for FAT12 */
@@ -368,8 +371,8 @@ void fatDrive::setClusterValue(uint32_t clustNum, uint32_t clustValue) {
fatoffset = clustNum * 4;
break;
}
- fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
- fatentoff = fatoffset % bootbuffer.bytespersector;
+ fatsectnum = BPB.v.BPB_RsvdSecCnt + (fatoffset / BPB.v.BPB_BytsPerSec) + partSectOff;
+ fatentoff = fatoffset % BPB.v.BPB_BytsPerSec;
if(curFatSect != fatsectnum) {
/* Load two sectors at once for FAT12 */
@@ -403,11 +406,11 @@ void fatDrive::setClusterValue(uint32_t clustNum, uint32_t clustValue) {
var_write((uint32_t *)&fatSectBuffer[fatentoff], clustValue);
break;
}
- for(int fc=0;fc<bootbuffer.fatcopies;fc++) {
- writeSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]);
+ for(unsigned int fc=0;fc<BPB.v.BPB_NumFATs;fc++) {
+ writeSector(fatsectnum + (fc * (BPB.is_fat32() ? BPB.v32.BPB_FATSz32 : BPB.v.BPB_FATSz16)), &fatSectBuffer[0]);
if (fattype==FAT12) {
- if (fatentoff>=511)
- writeSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]);
+ if (fatentoff >= (BPB.v.BPB_BytsPerSec-1U))
+ writeSector(fatsectnum+1u+(fc * (BPB.is_fat32() ? BPB.v32.BPB_FATSz32 : BPB.v.BPB_FATSz16)), &fatSectBuffer[BPB.v.BPB_BytsPerSec]);
}
}
}
@@ -447,6 +450,10 @@ bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry,
safe_strcpy(dirtoken, filename);
findFile=dirtoken;
+ if (BPB.is_fat32()) {
+ /* Set to FAT32 root directory */
+ currentClust = BPB.v32.BPB_RootClus;
+ }
/* Skip if testing in root directory */
if ((len>0) && (filename[len-1]!='\\')) {
//LOG_MSG("Testing for filename %s", filename);
@@ -470,7 +477,10 @@ bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry,
findDir = findNext;
}
- currentClust = foundEntry.loFirstClust;
+ if (BPB.is_fat32())
+ currentClust = foundEntry.Cluster32();
+ else
+ currentClust = foundEntry.loFirstClust;
}
} else {
/* Set to root directory */
@@ -497,6 +507,10 @@ bool fatDrive::getDirClustNum(char *dir, uint32_t *clustNum, bool parDir) {
/* Skip if testing for root directory */
if ((len>0) && (dir[len-1]!='\\')) {
+ if (BPB.is_fat32()) {
+ /* Set to FAT32 root directory */
+ currentClust = BPB.v32.BPB_RootClus;
+ }
//LOG_MSG("Testing for dir %s", dir);
findDir = strtok(dirtoken,"\\");
while(findDir != NULL) {
@@ -512,10 +526,15 @@ bool fatDrive::getDirClustNum(char *dir, uint32_t *clustNum, bool parDir) {
imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
}
- currentClust = foundEntry.loFirstClust;
-
+ if (BPB.is_fat32())
+ currentClust = foundEntry.Cluster32();
+ else
+ currentClust = foundEntry.loFirstClust;
}
*clustNum = currentClust;
+ } else if (BPB.is_fat32()) {
+ /* Set to FAT32 root directory */
+ *clustNum = BPB.v32.BPB_RootClus;
} else {
/* Set to root directory */
*clustNum = 0;
@@ -532,11 +551,11 @@ uint8_t fatDrive::readSector(uint32_t sectnum, void * data) {
if (absolute) {
return loadedDisk->Read_AbsoluteSector(sectnum, data);
}
- uint32_t cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack;
+ uint32_t cylindersize = (unsigned int)BPB.v.BPB_NumHeads * (unsigned int)BPB.v.BPB_SecPerTrk;
uint32_t cylinder = sectnum / cylindersize;
sectnum %= cylindersize;
- uint32_t head = sectnum / bootbuffer.sectorspertrack;
- uint32_t sector = sectnum % bootbuffer.sectorspertrack + 1L;
+ uint32_t head = sectnum / BPB.v.BPB_SecPerTrk;
+ uint32_t sector = sectnum % BPB.v.BPB_SecPerTrk + 1L;
return loadedDisk->Read_Sector(head, cylinder, sector, data);
}
@@ -549,38 +568,42 @@ uint8_t fatDrive::writeSector(uint32_t sectnum, void * data) {
if (absolute) {
return loadedDisk->Write_AbsoluteSector(sectnum, data);
}
- uint32_t cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack;
+ uint32_t cylindersize = (unsigned int)BPB.v.BPB_NumHeads * (unsigned int)BPB.v.BPB_SecPerTrk;
uint32_t cylinder = sectnum / cylindersize;
sectnum %= cylindersize;
- uint32_t head = sectnum / bootbuffer.sectorspertrack;
- uint32_t sector = sectnum % bootbuffer.sectorspertrack + 1L;
+ uint32_t head = sectnum / BPB.v.BPB_SecPerTrk;
+ uint32_t sector = sectnum % BPB.v.BPB_SecPerTrk + 1L;
return loadedDisk->Write_Sector(head, cylinder, sector, data);
}
+uint32_t fatDrive::getSectSize(void) {
+ return sector_size;
+}
+
uint32_t fatDrive::getSectorCount()
{
- if (bootbuffer.totalsectorcount != 0)
- return check_cast<uint32_t>(bootbuffer.totalsectorcount);
+ if (BPB.v.BPB_TotSec16 != 0)
+ return check_cast<uint32_t>(BPB.v.BPB_TotSec16);
else
- return bootbuffer.totalsecdword;
+ return BPB.v.BPB_TotSec32;
}
uint32_t fatDrive::getSectorSize(void)
{
- return bootbuffer.bytespersector;
+ return BPB.v.BPB_BytsPerSec;
}
uint32_t fatDrive::getClusterSize(void) {
- return bootbuffer.sectorspercluster * bootbuffer.bytespersector;
+ return (unsigned int)BPB.v.BPB_SecPerClus * (unsigned int)BPB.v.BPB_BytsPerSec;
}
uint32_t fatDrive::getAbsoluteSectFromBytePos(uint32_t startClustNum, uint32_t bytePos) {
- return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector);
+ return getAbsoluteSectFromChain(startClustNum, bytePos / BPB.v.BPB_BytsPerSec);
}
uint32_t fatDrive::getAbsoluteSectFromChain(uint32_t startClustNum, uint32_t logicalSector) {
- int32_t skipClust = logicalSector / bootbuffer.sectorspercluster;
- uint32_t sectClust = logicalSector % bootbuffer.sectorspercluster;
+ int32_t skipClust = logicalSector / BPB.v.BPB_SecPerClus;
+ uint32_t sectClust = logicalSector % BPB.v.BPB_SecPerClus;
uint32_t currentClust = startClustNum;
uint32_t testvalue;
@@ -733,9 +756,9 @@ fatDrive::fatDrive(const char *sysFilename,
: loadedDisk(nullptr),
created_successfully(true),
partSectOff(0),
- bootbuffer{{0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, 0},
- absolute(false),
readonly(roflag),
+ bootbuffer{},
+ absolute(false),
fattype(0),
CountOfClusters(0),
firstDataSector(0),
@@ -758,12 +781,17 @@ fatDrive::fatDrive(const char *sysFilename,
created_successfully = (diskfile != nullptr);
if (!created_successfully)
return;
- fseek(diskfile, 0L, SEEK_END);
- filesize = (uint32_t)ftell(diskfile) / 1024L;
+ fseeko64(diskfile, 0L, SEEK_END);
+ filesize = (uint32_t)(ftello64(diskfile) / 1024L);
is_hdd = (filesize > 2880);
/* Load disk image */
loadedDisk.reset(new imageDisk(diskfile, sysFilename, filesize, is_hdd));
+ if (loadedDisk->getSectSize() > sizeof(bootbuffer)) {
+ LOG_MSG("Disk sector/bytes (%u) is too large, not attempting FAT filesystem access",loadedDisk->getSectSize());
+ created_successfully = false;
+ return;
+ }
if(is_hdd) {
/* Set user specified harddrive parameters */
@@ -804,27 +832,38 @@ fatDrive::fatDrive(const char *sysFilename,
loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
- bootbuffer.bytespersector = host_to_le(bootbuffer.bytespersector);
- bootbuffer.reservedsectors = host_to_le(bootbuffer.reservedsectors);
- bootbuffer.rootdirentries = host_to_le(bootbuffer.rootdirentries);
- bootbuffer.totalsectorcount = host_to_le(bootbuffer.totalsectorcount);
- bootbuffer.sectorsperfat = host_to_le(bootbuffer.sectorsperfat);
- bootbuffer.sectorspertrack = host_to_le(bootbuffer.sectorspertrack);
- bootbuffer.headcount = host_to_le(bootbuffer.headcount);
- bootbuffer.hiddensectorcount = host_to_le(bootbuffer.hiddensectorcount);
- bootbuffer.totalsecdword = host_to_le(bootbuffer.totalsecdword);
+ void* var = &bootbuffer.bpb.v.BPB_BytsPerSec;
+ bootbuffer.bpb.v.BPB_BytsPerSec = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_RsvdSecCnt;
+ bootbuffer.bpb.v.BPB_RsvdSecCnt = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_RootEntCnt;
+ bootbuffer.bpb.v.BPB_RootEntCnt = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_TotSec16;
+ bootbuffer.bpb.v.BPB_TotSec16 = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_FATSz16;
+ bootbuffer.bpb.v.BPB_FATSz16 = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_SecPerTrk;
+ bootbuffer.bpb.v.BPB_SecPerTrk = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_NumHeads;
+ bootbuffer.bpb.v.BPB_NumHeads = var_read((uint16_t*)var);
+ var = &bootbuffer.bpb.v.BPB_HiddSec;
+ bootbuffer.bpb.v.BPB_HiddSec = var_read((uint32_t*)var);
+ var = &bootbuffer.bpb.v.BPB_TotSec32;
+ bootbuffer.bpb.v.BPB_TotSec32 = var_read((uint32_t*)var);
+ var = &bootbuffer.bpb.v.BPB_VolID;
+ bootbuffer.bpb.v.BPB_VolID = var_read((uint32_t*)var);
if (!is_hdd) {
/* Identify floppy format */
- if ((bootbuffer.nearjmp[0] == 0x69 || bootbuffer.nearjmp[0] == 0xe9 ||
- (bootbuffer.nearjmp[0] == 0xeb && bootbuffer.nearjmp[2] == 0x90)) &&
- (bootbuffer.mediadescriptor & 0xf0) == 0xf0) {
+ if ((bootbuffer.BS_jmpBoot[0] == 0x69 || bootbuffer.BS_jmpBoot[0] == 0xe9 ||
+ (bootbuffer.BS_jmpBoot[0] == 0xeb && bootbuffer.BS_jmpBoot[2] == 0x90)) &&
+ (bootbuffer.bpb.v.BPB_Media & 0xf0) == 0xf0) {
/* DOS 2.x or later format, BPB assumed valid */
- if ((bootbuffer.mediadescriptor != 0xf0 && !(bootbuffer.mediadescriptor & 0x1)) &&
- (bootbuffer.oemname[5] != '3' || bootbuffer.oemname[6] != '.' || bootbuffer.oemname[7] < '2')) {
+ if ((bootbuffer.bpb.v.BPB_Media != 0xf0 && !(bootbuffer.bpb.v.BPB_Media & 0x1)) &&
+ (bootbuffer.BS_OEMName[5] != '3' || bootbuffer.BS_OEMName[6] != '.' || bootbuffer.BS_OEMName[7] < '2')) {
/* Fix pre-DOS 3.2 single-sided floppy */
- bootbuffer.sectorspercluster = 1;
+ bootbuffer.bpb.v.BPB_SecPerClus = 1;
}
} else {
/* Read media descriptor in FAT */
@@ -834,30 +873,30 @@ fatDrive::fatDrive(const char *sysFilename,
if (mdesc >= 0xf8) {
/* DOS 1.x format, create BPB for 160kB floppy */
- bootbuffer.bytespersector = 512;
- bootbuffer.sectorspercluster = 1;
- bootbuffer.reservedsectors = 1;
- bootbuffer.fatcopies = 2;
- bootbuffer.rootdirentries = 64;
- bootbuffer.totalsectorcount = 320;
- bootbuffer.mediadescriptor = mdesc;
- bootbuffer.sectorsperfat = 1;
- bootbuffer.sectorspertrack = 8;
- bootbuffer.headcount = 1;
+ bootbuffer.bpb.v.BPB_BytsPerSec = 512;
+ bootbuffer.bpb.v.BPB_SecPerClus = 1;
+ bootbuffer.bpb.v.BPB_RsvdSecCnt = 1;
+ bootbuffer.bpb.v.BPB_NumFATs = 2;
+ bootbuffer.bpb.v.BPB_RootEntCnt = 64;
+ bootbuffer.bpb.v.BPB_TotSec16 = 320;
+ bootbuffer.bpb.v.BPB_Media = mdesc;
+ bootbuffer.bpb.v.BPB_FATSz16 = 1;
+ bootbuffer.bpb.v.BPB_SecPerTrk = 8;
+ bootbuffer.bpb.v.BPB_NumHeads = 1;
bootbuffer.magic1 = 0x55; // to silence warning
bootbuffer.magic2 = 0xaa;
if (!(mdesc & 0x2)) {
/* Adjust for 9 sectors per track */
- bootbuffer.totalsectorcount = 360;
- bootbuffer.sectorsperfat = 2;
- bootbuffer.sectorspertrack = 9;
+ bootbuffer.bpb.v.BPB_TotSec16 = 360;
+ bootbuffer.bpb.v.BPB_FATSz16 = 2;
+ bootbuffer.bpb.v.BPB_SecPerTrk = 9;
}
if (mdesc & 0x1) {
/* Adjust for 2 sides */
- bootbuffer.sectorspercluster = 2;
- bootbuffer.rootdirentries = 112;
- bootbuffer.totalsectorcount *= 2;
- bootbuffer.headcount = 2;
+ bootbuffer.bpb.v.BPB_SecPerClus = 2;
+ bootbuffer.bpb.v.BPB_RootEntCnt = 112;
+ bootbuffer.bpb.v.BPB_TotSec16 *= 2;
+ bootbuffer.bpb.v.BPB_NumHeads = 2;
}
} else {
/* Unknown format */
@@ -867,43 +906,110 @@ fatDrive::fatDrive(const char *sysFilename,
}
}
- if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
+ /* accept BPB.. so far */
+ BPB = bootbuffer.bpb;
+
+ /* DEBUG */
+ LOG_MSG("FAT: BPB says %u sectors/track %u heads %u bytes/sector",
+ BPB.v.BPB_SecPerTrk,
+ BPB.v.BPB_NumHeads,
+ BPB.v.BPB_BytsPerSec);
+
+ if (fatDrive::getSectSize() == 512 && (bootbuffer.magic1 != 0x55 || bootbuffer.magic2 != 0xaa)) {
/* Not a FAT filesystem */
LOG_MSG("Loaded image has no valid magicnumbers at the end!");
}
+ if (BPB.v.BPB_SecPerTrk == 0 || (BPB.v.BPB_SecPerTrk > ((filesize <= 3000) ? 40 : 255)) ||
+ (BPB.v.BPB_NumHeads > ((filesize <= 3000) ? 64 : 255))) {
+ LOG_MSG("Rejecting image, boot sector has weird values not consistent with FAT filesystem");
+ created_successfully = false;
+ return;
+ }
+ sector_size = loadedDisk->getSectSize();
+
/* Sanity checks */
- if ((bootbuffer.sectorsperfat == 0) || // FAT32 not implemented yet
- (bootbuffer.bytespersector != 512) || // non-standard sector sizes not implemented
- (bootbuffer.sectorspercluster == 0) ||
- (bootbuffer.rootdirentries == 0) ||
- (bootbuffer.fatcopies == 0) ||
- (bootbuffer.headcount == 0) ||
- (bootbuffer.headcount > headscyl) ||
- (bootbuffer.sectorspertrack == 0) ||
- (bootbuffer.sectorspertrack > cylsector)) {
+ if ((BPB.v.BPB_SecPerClus == 0) ||
+ (BPB.v.BPB_NumFATs == 0) ||
+ (BPB.v.BPB_NumHeads == 0) ||
+ (BPB.v.BPB_SecPerTrk > cylsector)) {
created_successfully = false;
+ LOG_MSG("here");
return;
}
+ /* Sanity check: Root directory count is nonzero if FAT16/FAT12, or is zero if FAT32 */
+ if (BPB.is_fat32()) {
+ if (BPB.v.BPB_RootEntCnt != 0) {
+ LOG_MSG("Sanity check fail: Root directory count != 0 and not FAT32");
+ created_successfully = false;
+ return;
+ }
+ }
+ else {
+ if (BPB.v.BPB_RootEntCnt == 0) {
+ LOG_MSG("Sanity check fail: Root directory count == 0 and not FAT32");
+ created_successfully = false;
+ return;
+ }
+ }
+
/* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */
- absolute = ((bootbuffer.headcount == headscyl) && (bootbuffer.sectorspertrack == cylsector));
+ absolute = ((BPB.v.BPB_NumHeads == headscyl) && (BPB.v.BPB_SecPerTrk == cylsector));
/* Determine FAT format, 12, 16 or 32 */
/* Get size of root dir in sectors */
- uint32_t RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector;
+ uint32_t RootDirSectors;
uint32_t DataSectors;
- if(bootbuffer.totalsectorcount != 0) {
- DataSectors = bootbuffer.totalsectorcount - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
- } else {
- DataSectors = bootbuffer.totalsecdword - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
-
- }
- CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
- firstDataSector = (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors) + partSectOff;
- firstRootDirSect = bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + partSectOff;
+ if (BPB.is_fat32()) {
+ /* FAT32 requires use of TotSec32, TotSec16 must be zero. */
+ if (BPB.v.BPB_TotSec32 == 0) {
+ LOG_MSG("BPB_TotSec32 == 0 and FAT32 BPB, not valid");
+ created_successfully = false;
+ return;
+ }
+ if (BPB.v32.BPB_RootClus < 2) {
+ LOG_MSG("BPB_RootClus == 0 and FAT32 BPB, not valid");
+ created_successfully = false;
+ return;
+ }
+ if (BPB.v.BPB_FATSz16 != 0) {
+ LOG_MSG("BPB_FATSz16 != 0 and FAT32 BPB, not valid");
+ created_successfully = false;
+ return;
+ }
+ if (BPB.v32.BPB_FATSz32 == 0) {
+ LOG_MSG("BPB_FATSz32 == 0 and FAT32 BPB, not valid");
+ created_successfully = false;
+ return;
+ }
+
+ RootDirSectors = 0; /* FAT32 root directory has it's own allocation chain, instead of a fixed location */
+ DataSectors = (Bitu)BPB.v.BPB_TotSec32 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v32.BPB_FATSz32) + (Bitu)RootDirSectors);
+ CountOfClusters = DataSectors / BPB.v.BPB_SecPerClus;
+ firstDataSector = ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v32.BPB_FATSz32) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
+ firstRootDirSect = 0;
+ }
+ else {
+ if (BPB.v.BPB_FATSz16 == 0) {
+ LOG_MSG("BPB_FATSz16 == 0 and not FAT32 BPB, not valid");
+ created_successfully = false;
+ return;
+ }
+
+ RootDirSectors = ((BPB.v.BPB_RootEntCnt * 32u) + (BPB.v.BPB_BytsPerSec - 1u)) / BPB.v.BPB_BytsPerSec;
+
+ if (BPB.v.BPB_TotSec16 != 0)
+ DataSectors = (Bitu)BPB.v.BPB_TotSec16 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors);
+ else
+ DataSectors = (Bitu)BPB.v.BPB_TotSec32 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors);
+
+ CountOfClusters = DataSectors / BPB.v.BPB_SecPerClus;
+ firstDataSector = ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
+ firstRootDirSect = (Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)partSectOff;
+ }
if(CountOfClusters < 4085) {
/* Volume is FAT12 */
@@ -939,7 +1045,7 @@ bool fatDrive::AllocationInfo(uint16_t *_bytes_sector, uint8_t *_sectors_cluster
loadedDisk->Get_Geometry(&hs, &cy, &sect, &sectsize);
*_bytes_sector = (uint16_t)sectsize;
- *_sectors_cluster = bootbuffer.sectorspercluster;
+ *_sectors_cluster = BPB.v.BPB_SecPerClus;
if (CountOfClusters<65536) {
*_total_clusters = (uint16_t)CountOfClusters;
@@ -1025,7 +1131,7 @@ bool fatDrive::FileCreate(DOS_File **file, char *name, uint16_t attributes) {
/* Empty file created, now lets open it */
/* TODO: check for read-only flag and requested write access */
- *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
+ *file = new fatFile(name, BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust, fileEntry.entrysize, this);
(*file)->flags=OPEN_READWRITE;
((fatFile *)(*file))->dirCluster = dirClust;
((fatFile *)(*file))->dirIndex = subEntry;
@@ -1051,7 +1157,7 @@ bool fatDrive::FileOpen(DOS_File **file, char *name, uint32_t flags) {
uint32_t dirClust, subEntry;
if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
/* TODO: check for read-only flag and requested write access */
- *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
+ *file = new fatFile(name, BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust, fileEntry.entrysize, this);
(*file)->flags = flags;
((fatFile *)(*file))->dirCluster = dirClust;
((fatFile *)(*file))->dirIndex = subEntry;
@@ -1093,7 +1199,10 @@ bool fatDrive::FileUnlink(char * name) {
fileEntry.entryname[0] = 0xe5;
directoryChange(dirClust, &fileEntry, subEntry);
- if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
+ {
+ const uint32_t chk = BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust;
+ if(chk != 0) deleteClustChain(chk, 0);
+ }
return true;
}
@@ -1119,7 +1228,7 @@ bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
return false;
}
dta.SetDirID(0);
- dta.SetDirIDCluster((uint16_t)(cwdDirCluster&0xffff));
+ dta.SetDirIDCluster(cwdDirCluster);
return FindNextInternal(cwdDirCluster, dta, &dummyClust);
}
@@ -1181,7 +1290,8 @@ nextfile:
entryoffset = dirPos % 16;
if(dirClustNumber==0) {
- if(dirPos >= bootbuffer.rootdirentries) {
+ if (BPB.is_fat32()) return false;
+ if(dirPos >= BPB.v.BPB_RootEntCnt) {
DOS_SetError(DOSERR_NO_MORE_FILES);
return false;
}
@@ -1291,6 +1401,13 @@ bool fatDrive::SetFileAttr(const char *name, const uint16_t attr)
return true;
}
+unsigned long fatDrive::GetSerial() {
+ if (BPB.is_fat32())
+ return BPB.v32.BS_VolID?BPB.v32.BS_VolID:0x1234;
+ else
+ return BPB.v.BPB_VolID?BPB.v.BPB_VolID:0x1234;
+}
+
bool fatDrive::directoryBrowse(uint32_t dirClustNumber, direntry *useEntry, int32_t entNum, int32_t start/*=0*/) {
direntry sectbuf[16]; /* 16 directory entries per sector */
uint32_t logentsector; /* Logical entry sector */
@@ -1307,7 +1424,8 @@ bool fatDrive::directoryBrowse(uint32_t dirClustNumber, direntry *useEntry, int3
entryoffset = dirPos % 16;
if(dirClustNumber==0) {
- if(dirPos >= bootbuffer.rootdirentries) return false;
+ assert(!BPB.is_fat32());
+ if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
tmpsector = firstRootDirSect+logentsector;
readSector(tmpsector,sectbuf);
} else {
@@ -1341,7 +1459,8 @@ bool fatDrive::directoryChange(uint32_t dirClustNumber, direntry *useEntry, int3
entryoffset = dirPos % 16;
if(dirClustNumber==0) {
- if(dirPos >= bootbuffer.rootdirentries) return false;
+ assert(!BPB.is_fat32());
+ if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
tmpsector = firstRootDirSect+logentsector;
readSector(tmpsector,sectbuf);
} else {
@@ -1379,7 +1498,8 @@ bool fatDrive::addDirectoryEntry(uint32_t dirClustNumber, direntry useEntry) {
entryoffset = dirPos % 16;
if(dirClustNumber==0) {
- if(dirPos >= bootbuffer.rootdirentries) return false;
+ assert(!BPB.is_fat32());
+ if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
tmpsector = firstRootDirSect+logentsector;
readSector(tmpsector,sectbuf);
} else {
@@ -1414,7 +1534,7 @@ void fatDrive::zeroOutCluster(uint32_t clustNumber) {
memset(&secBuffer[0], 0, 512);
int i;
- for(i=0;i<bootbuffer.sectorspercluster;i++) {
+ for(i=0;i<BPB.v.BPB_SecPerClus;i++) {
writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
}
}
@@ -1467,8 +1587,14 @@ bool fatDrive::MakeDir(char *dir) {
/* [..] entry */
memset(&tmpentry,0, sizeof(direntry));
memcpy(&tmpentry.entryname, ".. ", 11);
- tmpentry.loFirstClust = (uint16_t)(dirClust & 0xffff);
- tmpentry.hiFirstClust = (uint16_t)(dirClust >> 16);
+ if (BPB.is_fat32() && dirClust == BPB.v32.BPB_RootClus) {
+ tmpentry.loFirstClust = (uint16_t)0;
+ tmpentry.hiFirstClust = (uint16_t)0;
+ }
+ else {
+ tmpentry.loFirstClust = (uint16_t)(dirClust & 0xffff);
+ tmpentry.hiFirstClust = (uint16_t)(dirClust >> 16);
+ }
tmpentry.attrib = DOS_ATTR_DIRECTORY;
addDirectoryEntry(dummyClust, tmpentry);
@@ -1494,6 +1620,7 @@ bool fatDrive::RemoveDir(char *dir) {
/* Can't remove root directory */
if(dummyClust == 0) return false;
+ if(BPB.is_fat32() && dummyClust==BPB.v32.BPB_RootClus) return false;
/* Get parent directory starting cluster */
if(!getDirClustNum(dir, &dirClust, true)) return false;
@@ -1579,3 +1706,53 @@ bool fatDrive::TestDir(char *dir) {
uint32_t dummyClust;
return getDirClustNum(dir, &dummyClust, false);
}
+
+uint32_t fatDrive::GetPartitionOffset(void) {
+ return partSectOff;
+}
+
+void fatDrive::SetBPB(const FAT_BootSector::bpb_union_t &bpb) {
+ if (readonly) return;
+ BPB.v.BPB_BytsPerSec = bpb.v.BPB_BytsPerSec;
+ BPB.v.BPB_SecPerClus = bpb.v.BPB_SecPerClus;
+ BPB.v.BPB_RsvdSecCnt = bpb.v.BPB_RsvdSecCnt;
+ BPB.v.BPB_NumFATs = bpb.v.BPB_NumFATs;
+ BPB.v.BPB_RootEntCnt = bpb.v.BPB_RootEntCnt;
+ BPB.v.BPB_TotSec16 = bpb.v.BPB_TotSec16;
+ BPB.v.BPB_Media = bpb.v.BPB_Media;
+ BPB.v.BPB_FATSz16 = bpb.v.BPB_FATSz16;
+ BPB.v.BPB_SecPerTrk = bpb.v.BPB_SecPerTrk;
+ BPB.v.BPB_NumHeads = bpb.v.BPB_NumHeads;
+ BPB.v.BPB_HiddSec = bpb.v.BPB_HiddSec;
+ BPB.v.BPB_TotSec32 = bpb.v.BPB_TotSec32;
+ if (!bpb.is_fat32() && (bpb.v.BPB_BootSig == 0x28 || bpb.v.BPB_BootSig == 0x29))
+ BPB.v.BPB_VolID = bpb.v.BPB_VolID;
+ if (bpb.is_fat32() && (bpb.v32.BS_BootSig == 0x28 || bpb.v32.BS_BootSig == 0x29))
+ BPB.v32.BS_VolID = bpb.v32.BS_VolID;
+ if (bpb.is_fat32()) {
+ BPB.v32.BPB_BytsPerSec = bpb.v32.BPB_BytsPerSec;
+ BPB.v32.BPB_SecPerClus = bpb.v32.BPB_SecPerClus;
+ BPB.v32.BPB_RsvdSecCnt = bpb.v32.BPB_RsvdSecCnt;
+ BPB.v32.BPB_NumFATs = bpb.v32.BPB_NumFATs;
+ BPB.v32.BPB_RootEntCnt = bpb.v32.BPB_RootEntCnt;
+ BPB.v32.BPB_TotSec16 = bpb.v32.BPB_TotSec16;
+ BPB.v32.BPB_Media = bpb.v32.BPB_Media;
+ BPB.v32.BPB_FATSz32 = bpb.v32.BPB_FATSz32;
+ BPB.v32.BPB_SecPerTrk = bpb.v32.BPB_SecPerTrk;
+ BPB.v32.BPB_NumHeads = bpb.v32.BPB_NumHeads;
+ BPB.v32.BPB_HiddSec = bpb.v32.BPB_HiddSec;
+ BPB.v32.BPB_TotSec32 = bpb.v32.BPB_TotSec32;
+ BPB.v32.BPB_FATSz32 = bpb.v32.BPB_FATSz32;
+ BPB.v32.BPB_ExtFlags = bpb.v32.BPB_ExtFlags;
+ BPB.v32.BPB_FSVer = bpb.v32.BPB_FSVer;
+ BPB.v32.BPB_RootClus = bpb.v32.BPB_RootClus;
+ BPB.v32.BPB_FSInfo = bpb.v32.BPB_FSInfo;
+ BPB.v32.BPB_BkBootSec = bpb.v32.BPB_BkBootSec;
+ }
+
+ FAT_BootSector bootbuffer = {};
+ loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
+ if (BPB.is_fat32()) bootbuffer.bpb.v32=BPB.v32;
+ bootbuffer.bpb.v=BPB.v;
+ loadedDisk->Write_AbsoluteSector(0+partSectOff,&bootbuffer);
+}
diff --git a/src/dos/program_imgmount.cpp b/src/dos/program_imgmount.cpp
index 77e9b58f2..af37be64a 100644
--- a/src/dos/program_imgmount.cpp
+++ b/src/dos/program_imgmount.cpp
@@ -37,6 +37,69 @@
#include "string_utils.h"
#include "../ints/int10.h"
+bool DetectMFMsectorPartition(uint8_t buf[], uint32_t fcsize, uint16_t sizes[]) {
+ // This is used for plain MFM sector format as created by IMGMAKE
+ // It tries to find the first partition. Addressing is in CHS format.
+ /* Offset | Length | Description
+ * +0 | 1 byte | 80 hex = active, 00 = inactive
+ * +1 | 3 bytes | CHS of first sector in partition
+ * +4 | 1 byte | partition type
+ * +5 | 3 bytes | CHS of last sector in partition
+ * +8 | 4 bytes | LBA of first sector in partition
+ * +C | 4 bytes | Number of sectors in partition. 0 may mean, use LBA
+ */
+ uint8_t starthead = 0; // start head of partition
+ uint8_t startsect = 0; // start sector of partition
+ uint16_t startcyl = 0; // start cylinder of partition
+ //uint8_t ptype = 0; // Partition Type
+ uint16_t endcyl = 0; // end cylinder of partition
+ uint8_t heads = 0; // heads in partition
+ uint8_t sectors = 0; // sectors per track in partition
+ uint32_t pe1_size = host_readd(&buf[0x1ca]);
+ if ((uint32_t)host_readd(&buf[0x1fa]) != 0) { // DOS 2.0-3.21 partition table
+ pe1_size = host_readd(&buf[0x1fa]);
+ starthead = buf[0x1ef];
+ startsect = (buf[0x1f0] & 0x3fu) - 1u;
+ startcyl = (unsigned char)buf[0x1f1] | (unsigned int)((buf[0x1f0] & 0xc0) << 2u);
+ endcyl = (unsigned char)buf[0x1f5] | (unsigned int)((buf[0x1f4] & 0xc0) << 2u);
+ //ptype = buf[0x1f2];
+ heads = buf[0x1f3] + 1u;
+ sectors = buf[0x1f4] & 0x3fu;
+ } else if (pe1_size != 0) { // DOS 3.3+ partition table, starting at 0x1BE
+ starthead = buf[0x1bf];
+ startsect = (buf[0x1c0] & 0x3fu) - 1u;
+ startcyl = (unsigned char)buf[0x1c1] | (unsigned int)((buf[0x1c0] & 0xc0) << 2u);
+ endcyl = (unsigned char)buf[0x1c5] | (unsigned int)((buf[0x1c4] & 0xc0) << 2u);
+ //ptype = buf[0x1c2];
+ heads = buf[0x1c3] + 1u;
+ sectors = buf[0x1c4] & 0x3fu;
+ }
+ if (pe1_size != 0) {
+ uint32_t part_start = startsect + sectors * starthead +
+ startcyl * sectors * heads;
+ uint32_t part_end = heads * sectors * endcyl;
+ uint32_t part_len = part_end - part_start;
+ // partition start/end sanity check
+ // partition length should not exceed file length
+ // real partition size can be a few cylinders less than pe1_size
+ // if more than 1023 cylinders see if first partition fits
+ // into 1023, else bail.
+ if (/*(part_len<0) always false because unsigned || */(part_len > pe1_size) || (pe1_size > fcsize) ||
+ ((pe1_size - part_len) / (sectors*heads)>2u) ||
+ ((pe1_size / (heads*sectors))>1023u)) {
+ //LOG_MSG("start(c,h,s) %u,%u,%u",startcyl,starthead,startsect);
+ //LOG_MSG("endcyl %u heads %u sectors %u",endcyl,heads,sectors);
+ //LOG_MSG("psize %u start %u end %u",pe1_size,part_start,part_end);
+ } else {
+ sizes[0] = 512; sizes[1] = sectors;
+ sizes[2] = heads; sizes[3] = (uint16_t)(fcsize / (heads*sectors));
+ if (sizes[3]>1023) sizes[3] = 1023;
+ return true;
+ }
+ }
+ return false;
+}
+
void IMGMOUNT::ListImgMounts(void)
{
const std::string header_drive = MSG_Get("PROGRAM_MOUNT_STATUS_DRIVE");
@@ -162,7 +225,9 @@ void IMGMOUNT::Run(void) {
if (wants_ide) {
IDE_Get_Next_Cable_Slot(ide_index, is_second_cable_slot);
}
- }
+ } else if (type=="hdd" && wants_ide) {
+ IDE_Get_Next_Cable_Slot(ide_index, is_second_cable_slot);
+ }
cmd->FindString("-size",str_size,true);
if ((type=="hdd") && (str_size.size()==0)) {
@@ -322,11 +387,40 @@ void IMGMOUNT::Run(void) {
return;
}
Bitu sectors=(Bitu)(fcsize/(16*63));
- if (sectors*16*63!=fcsize) {
+ if (sectors*16*63>fcsize) {
WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY"));
return;
}
- sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors;
+ bool assume_lba = false;
+ if (!DetectMFMsectorPartition(buf, fcsize, sizes)) {
+ uint8_t ptype = buf[0x1c2]; // Location of DOS 3.3+ partition type
+ if (ptype == 0x0C/*FAT32+LBA*/ || ptype == 0x0E/*FAT16+LBA*/) {
+ assume_lba = true;
+ LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on first partition type (FAT with LBA)");
+ }
+ if (!assume_lba && (ptype == 0x01 || ptype == 0x04 || ptype == 0x06 || ptype == 0x0B || ptype == 0x0C || ptype == 0x0E)) {
+ /* buf[] still contains MBR */
+ unsigned int i=0;
+ while (i < 0x20 && buf[i] == 0) i++;
+ if (i == 0x20) {
+ assume_lba = true;
+ LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on first partition type (FAT-related) and lack of executable code in the MBR");
+ }
+ }
+ if (!assume_lba && fcsize >= ((4ull*1024ull*1024ull*1024ull)/512ull)) {
+ assume_lba = true;
+ LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on size");
+ }
+ if (assume_lba) {
+ sizes[0] = 512;
+ sizes[1] = 63;
+ sizes[2] = 255;
+ const Bitu d = sizes[1]*sizes[2];
+ sizes[3] = (fcsize + d - 1) / d; /* round up */
+ } else {
+ sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors;
+ }
+ }
LOG_MSG("autosized image file: %d:%d:%d:%d",sizes[0],sizes[1],sizes[2],sizes[3]);
}
@@ -370,6 +464,16 @@ void IMGMOUNT::Run(void) {
drive_index(drive) * 9,
mediaid);
+ // If instructed, attach to IDE controller as IDE device
+ if (wants_ide) {
+ if (ide_index >= 0) {
+ if (imgDisks.size() && !imageDiskList[drive_index(drive)]) imageDiskList[drive_index(drive)] = static_cast<fatDrive*>(imgDisks[0])->loadedDisk;
+ IDE_Hard_Disk_Attach(ide_index, is_second_cable_slot, drive_index(drive));
+ } else {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_IDE_CONTROLLERS_UNAVAILABLE"));
+ }
+ }
+
/* Command uses dta so set it to our internal dta */
RealPt save_dta = dos.dta();
dos.dta(dos.tables.tempdta);
diff --git a/src/dosbox.cpp b/src/dosbox.cpp
index 318124188..e2dcd753d 100644
--- a/src/dosbox.cpp
+++ b/src/dosbox.cpp
@@ -54,6 +54,7 @@
#include "timer.h"
#include "tracy.h"
#include "video.h"
+#include "pci_bus.h"
bool shutdown_requested = false;
MachineType machine;
@@ -85,6 +86,7 @@ void HARDWARE_Init(Section*);
#if defined(PCI_FUNCTIONALITY_ENABLED)
void PCI_Init(Section*);
+void VOODOO_Init(Section*);
#endif
void KEYBOARD_Init(Section*); //TODO This should setup INT 16 too but ok ;)
@@ -728,9 +730,33 @@ void DOSBOX_Init()
secprop->AddInitFunction(&VGA_Init);
secprop->AddInitFunction(&KEYBOARD_Init);
-
#if defined(PCI_FUNCTIONALITY_ENABLED)
- secprop=control->AddSection_prop("pci",&PCI_Init,false); //PCI bus
+ secprop=control->AddSection_prop("3dfx",&PCI_Init,false); //PCI bus
+
+ secprop->AddInitFunction(&VOODOO_Init,true);
+ const char* voodoo_settings[] = {
+ "false",
+ "software",
+#if C_OPENGL
+ "opengl",
+#endif
+ "auto",
+ 0
+ };
+ Pstring = secprop->Add_string("voodoo_card",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(voodoo_settings);
+ Pstring->Set_help("Enable VOODOO card support.");
+
+ const char* voodoo_memory[] = {
+ "standard",
+ "max",
+ 0
+ };
+ Pstring = secprop->Add_string("voodoo_mem",Property::Changeable::OnlyAtStart,"standard");
+ Pstring->Set_values(voodoo_memory);
+ Pstring->Set_help("Specify VOODOO card memory size.\n"
+ " 'standard' 4MB card (2MB front buffer + 1x2MB texture unit)\n"
+ " 'max' 12MB card (4MB front buffer + 2x4MB texture units)");
#endif
// Configure mouse
diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp
index b85932fd0..931d7739b 100644
--- a/src/gui/sdlmain.cpp
+++ b/src/gui/sdlmain.cpp
@@ -72,6 +72,10 @@
#include "video.h"
#if C_OPENGL
+#include "../hardware/voodoo.h"
+#include "../hardware/voodoo_types.h"
+#include "../hardware/voodoo_data.h"
+#include "../hardware/voodoo_opengl.h"
//Define to report opengl errors
//#define DB_OPENGL_ERROR
@@ -272,11 +276,6 @@ static bool isDebuggerEvent(const SDL_Event &event)
}
return false;
}
-
-SDL_Window *GFX_GetSDLWindow(void)
-{
- return sdl.window;
-}
#endif
#if C_OPENGL
@@ -1155,7 +1154,14 @@ static void NewMouseScreenParams()
#endif
}
-static SDL_Window *SetWindowMode(SCREEN_TYPES screen_type,
+void SetTransparency() {
+ Section_prop *section = static_cast<Section_prop *>(control->GetSection("sdl"));
+ const auto transparency = clamp(section->Get_int("transparency"), 0, 90);
+ const auto alpha = static_cast<float>(100 - transparency) / 100.0f;
+ SDL_SetWindowOpacity(sdl.window, alpha);
+}
+
+SDL_Window *SetWindowMode(SCREEN_TYPES screen_type,
int width,
int height,
bool fullscreen,
@@ -1198,6 +1204,7 @@ static SDL_Window *SetWindowMode(SCREEN_TYPES screen_type,
LOG_ERR("SDL: Failed to create window: %s", SDL_GetError());
return nullptr;
}
+ SetTransparency();
if (screen_type == SCREEN_TEXTURE) {
if (sdl.renderer) {
@@ -1313,6 +1320,167 @@ finish:
return sdl.window;
}
+SDL_Window* GFX_GetSDLWindow(void) {
+ return sdl.window;
+}
+
+bool OpenGL_using(void) {
+#if C_OPENGL
+ return (sdl.desktop.want_type==SCREEN_OPENGL?true:false);
+#else
+ return false;
+#endif
+}
+
+static std::string prev_output = "";
+void OpenGL_On() {
+ if (OpenGL_using()) {
+ prev_output.clear();
+ }
+ Section* tsec = control->GetSection("sdl");
+ prev_output = static_cast<Section_prop *>(tsec)->Get_string("output");
+ tsec->HandleInputline("output=opengl");
+ GFX_RegenerateWindow(tsec);
+}
+
+void OpenGL_Off() {
+ if (!OpenGL_using() || !prev_output.size()) {
+ prev_output.clear();
+ return;
+ }
+ Section* tsec = control->GetSection("sdl");
+ tsec->HandleInputline("output="+prev_output);
+ GFX_RegenerateWindow(tsec);
+ prev_output.clear();
+}
+
+SDL_Window* GFX_SetSDLWindowMode(uint16_t width, uint16_t height, SCREEN_TYPES screen_type)
+{
+ static SCREEN_TYPES lastType = SCREEN_SURFACE;
+ if (sdl.renderer) {
+ SDL_DestroyRenderer(sdl.renderer);
+ sdl.renderer=0;
+ }
+ if (sdl.texture.pixelFormat) {
+ SDL_FreeFormat(sdl.texture.pixelFormat);
+ sdl.texture.pixelFormat = 0;
+ }
+ if (sdl.texture.texture) {
+ SDL_DestroyTexture(sdl.texture.texture);
+ sdl.texture.texture=0;
+ }
+ int currWidth, currHeight;
+ if (sdl.window) {
+ //SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
+ if (sdl.resizing_window) {
+ SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
+ sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
+
+ return sdl.window;
+ }
+ }
+
+#if C_OPENGL
+ if (sdl.opengl.context) {
+ SDL_GL_DeleteContext(sdl.opengl.context);
+ sdl.opengl.context=0;
+ }
+#endif
+
+ /* If we change screen type, recreate the window. Furthermore, if
+ * it is our very first time then we simply create a new window.
+ */
+ if (!sdl.window
+ || (lastType != screen_type)
+// || (currWidth != width) || (currHeight != height)
+// || (glwindow != (0 != (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_OPENGL)))
+// || (fullscreen && (0 == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN)))
+// || (fullscreen != (SDL_WINDOW_FULLSCREEN == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN)))
+// || (fullscreen && ((width != currWidth) || (height != currHeight)))
+ ) {
+ lastType = screen_type;
+ int x = -1, y = -1;
+ if (sdl.window) {
+ SDL_GetWindowPosition(sdl.window, &x, &y);
+ SDL_DestroyWindow(sdl.window);
+ }
+
+ uint32_t flags = opengl_driver_crash_workaround(screen_type);
+#if C_OPENGL
+ if (screen_type == SCREEN_OPENGL)
+ flags |= SDL_WINDOW_OPENGL;
+#endif
+ if (!sdl.desktop.window.show_decorations) {
+ flags |= SDL_WINDOW_BORDERLESS;
+ }
+
+ const auto default_val = SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.display_number);
+ const auto pos = get_initial_window_position_or_default(default_val);
+ sdl.window = SDL_CreateWindow("",
+ x>-1?x:pos.x,
+ y>-1?y:pos.y,
+ width,
+ height,
+ flags);
+ if (!sdl.window) {
+ LOG_ERR("SDL: %s", SDL_GetError());
+ return nullptr;
+ }
+ SetTransparency();
+ SDL_SetWindowResizable(sdl.window, SDL_TRUE);
+ GFX_SetTitle(-1, -1, false); //refresh title.
+ SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
+ sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
+
+#if C_OPENGL
+ if (screen_type == SCREEN_OPENGL) {
+ sdl.opengl.context = SDL_GL_CreateContext(sdl.window);
+ if (sdl.opengl.context == NULL) LOG_MSG("WARNING: SDL2 unable to create GL context");
+ if (SDL_GL_MakeCurrent(sdl.window, sdl.opengl.context) != 0) LOG_MSG("WARNING: SDL2 unable to make current GL context");
+ }
+#endif
+ sdl.surface = SDL_GetWindowSurface(sdl.window);
+
+ return sdl.window;
+ }
+ /* Fullscreen mode switching has its limits, and is also problematic on
+ * some window managers. For now, the following may work up to some
+ * level. On X11, SDL_VIDEO_X11_LEGACY_FULLSCREEN=1 can also help,
+ * although it has its own issues.
+ * Suggestion: Use the desktop res if possible, with output=surface
+ * if one is not interested in scaling.
+ * On Android, desktop res is the only way.
+ */
+ SDL_SetWindowResizable(sdl.window, SDL_TRUE);
+ if (GFX_IsFullscreen()) {
+ SDL_DisplayMode displayMode;
+ SDL_GetWindowDisplayMode(sdl.window, &displayMode);
+ displayMode.w = width;
+ displayMode.h = height;
+ SDL_SetWindowDisplayMode(sdl.window, &displayMode);
+
+ SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+ } else {
+ SDL_SetWindowFullscreen(sdl.window, 0);
+
+ SDL_SetWindowSize(sdl.window, width, height);
+ }
+ /* Maybe some requested fullscreen resolution is unsupported? */
+ SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
+ sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
+ sdl.surface = SDL_GetWindowSurface(sdl.window);
+
+#if C_OPENGL
+ if (screen_type == SCREEN_OPENGL) {
+ sdl.opengl.context = SDL_GL_CreateContext(sdl.window);
+ if (sdl.opengl.context == NULL) LOG_MSG("WARNING: SDL2 unable to create GL context");
+ if (SDL_GL_MakeCurrent(sdl.window, sdl.opengl.context) != 0) LOG_MSG("WARNING: SDL2 unable to make current GL context");
+ }
+#endif
+
+ return sdl.window;
+}
+
// Used for the mapper UI and more: Creates a fullscreen window with desktop res
// on Android, and a non-fullscreen window with the input dimensions otherwise.
SDL_Window * GFX_SetSDLSurfaceWindow(uint16_t width, uint16_t height)
@@ -1415,7 +1583,7 @@ static SDL_Point restrict_to_viewport_resolution(const int w, const int h)
static SDL_Rect calc_viewport_fit(int win_width, int win_height);
-static SDL_Window *SetupWindowScaled(SCREEN_TYPES screen_type, bool resizable)
+SDL_Window *SetupWindowScaled(SCREEN_TYPES screen_type, bool resizable)
{
if (sdl.scaling_mode == SCALING_MODE::PERFECT)
return setup_window_pp(screen_type, resizable);
@@ -2365,6 +2533,29 @@ static void SwitchFullScreen(bool pressed)
GFX_SwitchFullScreen();
}
+void GFX_SwitchFullscreenNoReset(void) {
+ sdl.desktop.fullscreen=!sdl.desktop.fullscreen;
+}
+
+void GFX_SwitchLazyFullscreen(bool lazy) {
+ sdl.desktop.lazy_fullscreen = lazy;
+ sdl.desktop.lazy_fullscreen_req = false;
+}
+
+bool GFX_LazyFullscreenRequested(void) {
+ if (sdl.desktop.lazy_fullscreen) return sdl.desktop.lazy_fullscreen_req;
+ return false;
+}
+
+void GFX_TearDown(void) {
+ if (sdl.updating)
+ GFX_EndUpdate( 0 );
+}
+
+void GFX_RestoreMode(void) {
+ GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback,1.0);
+}
+
// This function returns write'able buffer for user to draw upon. Successful
// return depends on properly initialized SDL_Block structure (which generally
// can be achieved via GFX_SetSize call), and specifically - properly initialized
@@ -3710,6 +3901,15 @@ void GFX_RegenerateWindow(Section *sec) {
static void HandleVideoResize(int width, int height)
{
+#if C_OPENGL
+ if (sdl.desktop.lazy_fullscreen) {
+ new_height = height;
+ new_width = width;
+ voodoo_ogl_update_dimensions();
+ return;
+ }
+#endif
+
/* Maybe a screen rotation has just occurred, so we simply resize.
There may be a different cause for a forced resized, though. */
if (sdl.desktop.full.display_res && sdl.desktop.fullscreen) {
@@ -4560,6 +4760,12 @@ void GFX_GetSize(int &width, int &height, bool &fullscreen)
fullscreen = sdl.desktop.fullscreen;
}
+bool GFX_IsFullScreenSetting()
+{
+ return control->cmdline->FindExist("-fullscreen") ||
+ static_cast<Section_prop *>(control->GetSection("sdl"))->Get_bool("fullscreen");
+}
+
extern "C" int SDL_CDROMInit(void);
int sdl_main(int argc, char *argv[])
{
@@ -4739,8 +4945,7 @@ int sdl_main(int argc, char *argv[])
/* Some extra SDL Functions */
Section_prop * sdl_sec=static_cast<Section_prop *>(control->GetSection("sdl"));
- if (control->cmdline->FindExist("-fullscreen") ||
- sdl_sec->Get_bool("fullscreen")) {
+ if (GFX_IsFullScreenSetting()) {
if(!sdl.desktop.fullscreen) { //only switch if not already in fullscreen
GFX_SwitchFullScreen();
}
diff --git a/src/hardware/ide.cpp b/src/hardware/ide.cpp
index ec7f6b3c8..9370a495e 100644
--- a/src/hardware/ide.cpp
+++ b/src/hardware/ide.cpp
@@ -2491,6 +2491,37 @@ static void IDE_SelfIO_Out(IDEController * /* ide */, io_port_t port, io_val_t v
ide_baseio_w(port, val, width);
}
+/* Get physical (not logical INT 13h) geometry */
+bool IDE_GetPhysGeometry(unsigned char disk,uint32_t &heads,uint32_t &cyl,uint32_t &sect,uint32_t &size) {
+ IDEController *ide;
+ IDEDevice *dev;
+ Bitu idx,ms;
+
+ for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) {
+ ide = GetIDEController(idx);
+ if (ide == NULL) continue;
+
+ /* for master/slave device... */
+ for (ms=0;ms < 2;ms++) {
+ dev = ide->device[ms];
+ if (dev == NULL) continue;
+
+ if (dev->type == IDE_TYPE_HDD) {
+ IDEATADevice *ata = (IDEATADevice*)dev;
+ if (ata->bios_disk_index == disk && ata->phys_heads != 0 && ata->phys_sects != 0 && ata->phys_cyls != 0) {
+ heads = ata->phys_heads;
+ sect = ata->phys_sects;
+ cyl = ata->phys_cyls;
+ size = 512;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
/* INT 13h extensions */
void IDE_EmuINT13DiskReadByBIOS_LBA(uint8_t disk, uint64_t lba)
{
diff --git a/src/hardware/memory.cpp b/src/hardware/memory.cpp
index 0d9b0a0e0..a6a25da8c 100644
--- a/src/hardware/memory.cpp
+++ b/src/hardware/memory.cpp
@@ -26,6 +26,7 @@
#include "paging.h"
#include "regs.h"
#include "support.h"
+#include "voodoo.h"
#define PAGES_IN_BLOCK ((1024*1024)/MEM_PAGE_SIZE)
#define SAFE_MEMORY 32
@@ -143,6 +144,8 @@ PageHandler * MEM_GetPageHandler(Bitu phys_page) {
} else if ((phys_page>=memory.lfb.start_page+0x01000000/4096) &&
(phys_page<memory.lfb.start_page+0x01000000/4096+16)) {
return memory.lfb.mmiohandler;
+ } else if (VOODOO_PCI_CheckLFBPage(phys_page)) {
+ return VOODOO_GetPageHandler();
}
return &illegal_page_handler;
}
diff --git a/src/hardware/meson.build b/src/hardware/meson.build
index 4843b8e0e..ecfcf048a 100644
--- a/src/hardware/meson.build
+++ b/src/hardware/meson.build
@@ -66,6 +66,11 @@ libhardware_sources = files(
'vga_seq.cpp',
'vga_tseng.cpp',
'vga_xga.cpp',
+ 'voodoo.cpp',
+ 'voodoo_emu.cpp',
+ 'voodoo_interface.cpp',
+ 'voodoo_opengl.cpp',
+ 'voodoo_vogl.cpp',
)
libhardware = static_library(
diff --git a/src/hardware/pci_bus.cpp b/src/hardware/pci_bus.cpp
index 96640743c..62daa206b 100644
--- a/src/hardware/pci_bus.cpp
+++ b/src/hardware/pci_bus.cpp
@@ -21,12 +21,12 @@
#include "inout.h"
#include "mem.h"
#include "pci_bus.h"
+#include "support.h"
#include "setup.h"
#include "debug.h"
#include "callback.h"
#include "regs.h"
-
#if defined(PCI_FUNCTIONALITY_ENABLED)
static uint32_t pci_caddress=0; // current PCI addressing
@@ -44,7 +44,7 @@ static PCI_Device* pci_devices[PCI_MAX_PCIDEVICES]; // registered PCI devices
// 10- 8 - subfunction number (0x00000700)
// 7- 2 - config register # (0x000000fc)
-static void write_pci_addr(io_port_t port, io_val_t val, io_width_t)
+static void write_pci_addr([[maybe_unused]] io_port_t port, io_val_t val, io_width_t)
{
LOG(LOG_PCI, LOG_NORMAL)("Write PCI address :=%x", val);
pci_caddress = val;
@@ -105,19 +105,19 @@ static void write_pci(io_port_t port, io_val_t value, io_width_t width)
// WORD and DWORD are never used
case io_width_t::word:
write_pci_register(dev, regnum + 0, (uint8_t)(val & 0xff));
- write_pci_register(dev, regnum + 1, (uint8_t)((val >> 8) & 0xff));
+ write_pci_register(dev, regnum + 1, (uint8_t)((val >> 7 >> 1) & 0xff));
break;
case io_width_t::dword:
write_pci_register(dev, regnum + 0, (uint8_t)(val & 0xff));
- write_pci_register(dev, regnum + 1, (uint8_t)((val >> 8) & 0xff));
- write_pci_register(dev, regnum + 2, (uint8_t)((val >> 16) & 0xff));
- write_pci_register(dev, regnum + 3, (uint8_t)((val >> 24) & 0xff));
+ write_pci_register(dev, regnum + 1, (uint8_t)((val >> 7 >> 1) & 0xff));
+ write_pci_register(dev, regnum + 2, (uint8_t)((val >> 7 >> 7 >> 2) & 0xff));
+ write_pci_register(dev, regnum + 3, (uint8_t)((val >> 7 >> 7 >> 7 >> 3) & 0xff));
break;
}
}
}
-static uint32_t read_pci_addr(io_port_t port, io_width_t))
+static uint32_t read_pci_addr([[maybe_unused]] io_port_t port, io_width_t)
{
LOG(LOG_PCI, LOG_NORMAL)("Read PCI address -> %x", pci_caddress);
return pci_caddress;
@@ -168,15 +168,15 @@ static uint8_t read_pci(io_port_t port, io_width_t width)
uint8_t devnum = (uint8_t)((pci_caddress >> 11) & 0x1f);
uint8_t fctnum = (uint8_t)((pci_caddress >> 8) & 0x7);
uint8_t regnum = (uint8_t)((pci_caddress & 0xfc) + (port & 0x03));
- if (devnum>=pci_devices_installed) return 0xffffffff;
+ if (devnum>=pci_devices_installed) return 0xff;
LOG(LOG_PCI,LOG_NORMAL)(" Read from device %x register %x (function %x); addr %x",
devnum,regnum,fctnum,pci_caddress);
PCI_Device *selected_device = pci_devices[devnum];
if (selected_device == nullptr)
- return 0xffffffff;
+ return 0xff;
if (fctnum > selected_device->NumSubdevices())
- return 0xffffffff;
+ return 0xff;
PCI_Device *dev = selected_device->GetSubdevice(fctnum);
@@ -190,7 +190,7 @@ static uint8_t read_pci(io_port_t port, io_width_t width)
case io_width_t::word: {
uint16_t val16 = read_pci_register(dev, regnum);
val16 |= (read_pci_register(dev, regnum + 1) << 8);
- return val16;
+ return (uint8_t)val16;
}
case io_width_t::dword: {
uint32_t val32 = read_pci_register(dev, regnum);
@@ -203,7 +203,7 @@ static uint8_t read_pci(io_port_t port, io_width_t width)
}
}
}
- return 0xffffffff;
+ return 0xff;
}
static Bitu PCI_PM_Handler() {
@@ -261,6 +261,7 @@ PCI_Device* PCI_Device::GetSubdevice(Bits subfct) {
return NULL;
}
+PCI_Device::~PCI_Device() {}
// queued devices (PCI device registering requested before the PCI framework was initialized)
static const Bitu max_rqueued_devices=16;
@@ -436,6 +437,33 @@ public:
static PCI* pci_interface=NULL;
+void PCI_AddSST_Device(Bitu type) {
+ Bitu ctype = 1;
+ switch (type) {
+ case 1:
+ case 2:
+ ctype = type;
+ break;
+ default:
+ LOG_MSG("PCI:SST: Invalid board type %x specified", (unsigned int)type);
+ break;
+ }
+ PCI_Device* voodoo_dev=new PCI_SSTDevice(ctype);
+ if (pci_interface!=NULL) {
+ pci_interface->RegisterPCIDevice(voodoo_dev);
+ } else {
+ if (num_rqueued_devices<max_rqueued_devices)
+ rqueued_devices[num_rqueued_devices++]=voodoo_dev;
+ }
+}
+
+void PCI_RemoveSST_Device(void) {
+ if (pci_interface!=NULL) {
+ uint16_t vendor=PCI_SSTDevice::VendorID();
+ pci_interface->RemoveDevice(vendor,1);
+ pci_interface->RemoveDevice(vendor,2);
+ }
+}
PhysPt PCI_GetPModeInterface(void) {
if (pci_interface) {
@@ -449,8 +477,7 @@ bool PCI_IsInitialized() {
return false;
}
-
-void PCI_ShutDown(Section* sec){
+void PCI_ShutDown(Section *){
delete pci_interface;
pci_interface=NULL;
}
@@ -460,4 +487,12 @@ void PCI_Init(Section* sec) {
sec->AddDestroyFunction(&PCI_ShutDown,false);
}
+#else
+
+void PCI_AddSST_Device(Bitu type) {
+}
+
+void PCI_RemoveSST_Device(void) {
+}
+
#endif
diff --git a/src/hardware/pci_devices.h b/src/hardware/pci_devices.h
index 317cd23b3..ab9463cc8 100644
--- a/src/hardware/pci_devices.h
+++ b/src/hardware/pci_devices.h
@@ -16,3 +16,237 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "../ints/int10.h"
+
+#define VOODOO_INITIAL_LFB 0xd0000000
+
+void VOODOO_PCI_InitEnable(Bitu val);
+void VOODOO_PCI_Enable(bool enable);
+void VOODOO_PCI_SetLFB(uint32_t lfbaddr);
+
+class PCI_VGADevice:public PCI_Device {
+private:
+ static const uint16_t vendor=0x5333; // S3
+ static const uint16_t device=0x8811; // trio64
+// static const uint16_t device=0x8810; // trio32
+public:
+ PCI_VGADevice():PCI_Device(vendor,device) {
+ }
+
+ static uint16_t VendorID(void) { return vendor; }
+ static uint16_t DeviceID(void) { return device; }
+
+ Bits ParseReadRegister(uint8_t regnum) {
+ return regnum;
+ }
+
+ bool OverrideReadRegister(uint8_t, uint8_t*, uint8_t*) {
+ return false;
+ }
+
+ Bits ParseWriteRegister(uint8_t regnum, uint8_t value) {
+ if ((regnum>=0x18) && (regnum<0x28)) return -1; // base addresses are read-only
+ if ((regnum>=0x30) && (regnum<0x34)) return -1; // expansion rom addresses are read-only
+ switch (regnum) {
+ case 0x10:
+ return (pci_cfg_data[this->PCIId()][this->PCISubfunction()][0x10]&0x0f);
+ case 0x11:
+ return 0x00;
+ case 0x12:
+// return (value&0xc0); // -> 4mb addressable
+ return (value&0x00); // -> 16mb addressable
+ case 0x13:
+ return value;
+ case 0x14:
+ return (pci_cfg_data[this->PCIId()][this->PCISubfunction()][0x10]&0x0f);
+ case 0x15:
+ return 0x00;
+ case 0x16:
+ return value; // -> 64kb addressable
+ case 0x17:
+ return value;
+ default:
+ break;
+ }
+ return value;
+ }
+
+ bool InitializeRegisters(uint8_t registers[256]) {
+ // init (S3 graphics card)
+// registers[0x08] = 0x44; // revision ID (s3 trio64v+)
+ registers[0x08] = 0x00; // revision ID
+ registers[0x09] = 0x00; // interface
+ registers[0x0a] = 0x00; // subclass type (vga compatible)
+// registers[0x0a] = 0x01; // subclass type (xga device)
+ registers[0x0b] = 0x03; // class type (display controller)
+ registers[0x0c] = 0x00; // cache line size
+ registers[0x0d] = 0x00; // latency timer
+ registers[0x0e] = 0x00; // header type (other)
+
+ // reset
+ registers[0x04] = 0x23; // command register (vga palette snoop, ports enabled, memory space enabled)
+ registers[0x05] = 0x00;
+ registers[0x06] = 0x80; // status register (medium timing, fast back-to-back)
+ registers[0x07] = 0x02;
+
+// registers[0x3c] = 0x0b; // irq line
+// registers[0x3d] = 0x01; // irq pin
+
+// uint32_t gfx_address_space=(((uint32_t)S3_LFB_BASE)&0xfffffff0) | 0x08; // memory space, within first 4GB, prefetchable
+ uint32_t gfx_address_space=(((uint32_t)S3_LFB_BASE)&0xfffffff0); // memory space, within first 4GB
+ registers[0x10] = (uint8_t)(gfx_address_space&0xff); // base addres 0
+ registers[0x11] = (uint8_t)((gfx_address_space>>8)&0xff);
+ registers[0x12] = (uint8_t)((gfx_address_space>>16)&0xff);
+ registers[0x13] = (uint8_t)((gfx_address_space>>24)&0xff);
+
+ uint32_t gfx_address_space_mmio=(((uint32_t)S3_LFB_BASE+0x1000000)&0xfffffff0); // memory space, within first 4GB
+ registers[0x14] = (uint8_t)(gfx_address_space_mmio&0xff); // base addres 0
+ registers[0x15] = (uint8_t)((gfx_address_space_mmio>>8)&0xff);
+ registers[0x16] = (uint8_t)((gfx_address_space_mmio>>16)&0xff);
+ registers[0x17] = (uint8_t)((gfx_address_space_mmio>>24)&0xff);
+
+ return true;
+ }
+};
+
+
+class PCI_SSTDevice:public PCI_Device {
+private:
+ static const uint16_t vendor=0x121a; // 3dfx
+ uint16_t oscillator_ctr;
+ uint16_t pci_ctr;
+public:
+ PCI_SSTDevice(Bitu type):PCI_Device(vendor,(type==2)?0x0002:0x0001) {
+ oscillator_ctr=0;
+ pci_ctr=0;
+ }
+
+ static uint16_t VendorID(void) { return vendor; }
+
+ Bits ParseReadRegister(uint8_t regnum) {
+// LOG_MSG("SST ParseReadRegister %x",regnum);
+ switch (regnum) {
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ LOG_MSG("SST ParseReadRegister STATUS %x",regnum);
+ break;
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ if (DeviceID() >= 2) return -1;
+ break;
+ default:
+ break;
+ }
+ return regnum;
+ }
+
+ bool OverrideReadRegister(uint8_t regnum, uint8_t* rval, uint8_t* rval_mask) {
+ switch (regnum) {
+ case 0x54:
+ if (DeviceID() >= 2) {
+ oscillator_ctr++;
+ pci_ctr--;
+ *rval=(oscillator_ctr | ((pci_ctr<<16) & 0x0fff0000)) & 0xff;
+ *rval_mask=0xff;
+ return true;
+ }
+ break;
+ case 0x55:
+ if (DeviceID() >= 2) {
+ *rval=((oscillator_ctr | ((pci_ctr<<16) & 0x0fff0000)) >> 8) & 0xff;
+ *rval_mask=0xff;
+ return true;
+ }
+ break;
+ case 0x56:
+ if (DeviceID() >= 2) {
+ *rval=((oscillator_ctr | ((pci_ctr<<16) & 0x0fff0000)) >> 16) & 0xff;
+ *rval_mask=0xff;
+ return true;
+ }
+ break;
+ case 0x57:
+ if (DeviceID() >= 2) {
+ *rval=((oscillator_ctr | ((pci_ctr<<16) & 0x0fff0000)) >> 24) & 0xff;
+ *rval_mask=0x0f;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ Bits ParseWriteRegister(uint8_t regnum, uint8_t value) {
+// LOG_MSG("SST ParseWriteRegister %x:=%x",regnum,value);
+ if ((regnum>=0x14) && (regnum<0x28)) return -1; // base addresses are read-only
+ if ((regnum>=0x30) && (regnum<0x34)) return -1; // expansion rom addresses are read-only
+ switch (regnum) {
+ case 0x10:
+ return (pci_cfg_data[this->PCIId()][this->PCISubfunction()][0x10]&0x0f);
+ case 0x11:
+ return 0x00;
+ case 0x12:
+ return (value&0x00); // -> 16mb addressable (whyever)
+ case 0x13:
+ VOODOO_PCI_SetLFB(value<<24);
+ return value;
+ case 0x40:
+ VOODOO_PCI_InitEnable(value&7);
+ break;
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ return -1;
+ case 0xc0:
+ VOODOO_PCI_Enable(true);
+ return -1;
+ case 0xe0:
+ VOODOO_PCI_Enable(false);
+ return -1;
+ default:
+ break;
+ }
+ return value;
+ }
+
+ bool InitializeRegisters(uint8_t registers[256]) {
+ // init (3dfx voodoo)
+ registers[0x08] = 0x02; // revision
+ registers[0x09] = 0x00; // interface
+// registers[0x0a] = 0x00; // subclass code
+ registers[0x0a] = 0x00; // subclass code (video/graphics controller)
+// registers[0x0b] = 0x00; // class code (generic)
+ registers[0x0b] = 0x04; // class code (multimedia device)
+ registers[0x0e] = 0x00; // header type (other)
+
+ // reset
+ registers[0x04] = 0x02; // command register (memory space enabled)
+ registers[0x05] = 0x00;
+ registers[0x06] = 0x80; // status register (fast back-to-back)
+ registers[0x07] = 0x00;
+
+ registers[0x3c] = 0xff; // no irq
+
+ // memBaseAddr: size is 16MB
+ uint32_t address_space=(((uint32_t)VOODOO_INITIAL_LFB)&0xfffffff0) | 0x08; // memory space, within first 4GB, prefetchable
+ registers[0x10] = (uint8_t)(address_space&0xff); // base addres 0
+ registers[0x11] = (uint8_t)((address_space>>8)&0xff);
+ registers[0x12] = (uint8_t)((address_space>>16)&0xff);
+ registers[0x13] = (uint8_t)((address_space>>24)&0xff);
+
+ if (DeviceID() >= 2) {
+ registers[0x40] = 0x00;
+ registers[0x41] = 0x40; // voodoo2 revision ID (rev4)
+ registers[0x42] = 0x01;
+ registers[0x43] = 0x00;
+ }
+
+ return true;
+ }
+};
diff --git a/src/hardware/vga_draw.cpp b/src/hardware/vga_draw.cpp
index 8e60f8e47..368fee5bf 100644
--- a/src/hardware/vga_draw.cpp
+++ b/src/hardware/vga_draw.cpp
@@ -972,7 +972,7 @@ static void VGA_VerticalTimer(uint32_t /*val*/)
//Check if we can actually render, else skip the rest (frameskip)
++vga.draw.cursor.count; // Do this here, else the cursor speed depends
// on the frameskip
- if (!RENDER_StartUpdate())
+ if (vga.draw.vga_override || !RENDER_StartUpdate())
return;
vga.draw.address_line = vga.config.hlines_skip;
@@ -1868,3 +1868,16 @@ void VGA_KillDrawing(void) {
vga.draw.lines_done = ~0;
RENDER_EndUpdate(true);
}
+
+void VGA_SetOverride(bool vga_override) {
+ if (vga.draw.vga_override!=vga_override) {
+ if (vga_override) {
+ VGA_KillDrawing();
+ vga.draw.vga_override=true;
+ } else {
+ vga.draw.vga_override=false;
+ vga.draw.width=0; // change it so the output window gets updated
+ VGA_SetupDrawing(0);
+ }
+ }
+}
diff --git a/src/hardware/voodoo.cpp b/src/hardware/voodoo.cpp
new file mode 100644
index 000000000..1f21a6eb4
--- /dev/null
+++ b/src/hardware/voodoo.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "dosbox.h"
+#include "config.h"
+#include "setup.h"
+#include "cross.h"
+
+#include "paging.h"
+#include "video.h"
+#include "mem.h"
+
+#include "voodoo.h"
+#include "pci_bus.h"
+#include "voodoo_interface.h"
+
+
+class VOODOO;
+static VOODOO* voodoo_dev;
+
+static uint32_t voodoo_current_lfb=(VOODOO_INITIAL_LFB&0xffff0000);
+
+
+class VOODOO:public Module_base{
+private:
+ Bits emulation_type;
+public:
+ VOODOO(Section* configuration):Module_base(configuration){
+ emulation_type=-1;
+
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ std::string voodoo_type_str(section->Get_string("voodoo_card"));
+ if (voodoo_type_str=="false") {
+ emulation_type=0;
+ } else if (voodoo_type_str=="software" || (voodoo_type_str=="auto" && !OpenGL_using())) {
+ emulation_type=1;
+#if C_OPENGL
+ } else if (voodoo_type_str=="opengl" || voodoo_type_str=="auto") {
+ emulation_type=2;
+#else
+ } else if (voodoo_type_str=="auto") {
+ emulation_type=1;
+#endif
+ } else {
+ emulation_type=0;
+ }
+
+ if (emulation_type<=0) return;
+
+
+ Bits card_type = 1;
+ bool max_voodoomem;
+ std::string voodoo_mem_str(section->Get_string("voodoo_mem"));
+ if (voodoo_mem_str=="max") {
+ max_voodoomem = true;
+ } else {
+ max_voodoomem = false;
+ }
+
+ bool needs_pci_device = false;
+
+ switch (emulation_type) {
+ case 1:
+ case 2:
+ Voodoo_Initialize(emulation_type, card_type, max_voodoomem);
+ needs_pci_device = true;
+ break;
+ default:
+ break;
+ }
+
+ if (needs_pci_device) PCI_AddSST_Device(card_type);
+ }
+
+ ~VOODOO(){
+ PCI_RemoveSST_Device();
+
+ if (emulation_type<=0) return;
+
+ switch (emulation_type) {
+ case 1:
+ case 2:
+ Voodoo_Shut_Down();
+ break;
+ default:
+ break;
+ }
+
+ emulation_type=-1;
+ }
+
+ void PCI_InitEnable(Bitu val) {
+ switch (emulation_type) {
+ case 1:
+ case 2:
+ Voodoo_PCI_InitEnable(val);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void PCI_Enable(bool enable) {
+ switch (emulation_type) {
+ case 1:
+ case 2:
+ Voodoo_PCI_Enable(enable);
+ break;
+ default:
+ break;
+ }
+ }
+
+ PageHandler* GetPageHandler() {
+ switch (emulation_type) {
+ case 1:
+ case 2:
+ return Voodoo_GetPageHandler();
+ default:
+ break;
+ }
+ return NULL;
+ }
+
+};
+
+
+void VOODOO_PCI_InitEnable(Bitu val) {
+ if (voodoo_dev!=NULL) {
+ voodoo_dev->PCI_InitEnable(val);
+ }
+}
+
+void VOODOO_PCI_Enable(bool enable) {
+ if (voodoo_dev!=NULL) {
+ voodoo_dev->PCI_Enable(enable);
+ }
+}
+
+
+void VOODOO_PCI_SetLFB(uint32_t lfbaddr) {
+ voodoo_current_lfb=(lfbaddr&0xffff0000);
+}
+
+bool VOODOO_PCI_CheckLFBPage(Bitu page) {
+ if ((page>=(voodoo_current_lfb>>12)) &&
+ (page<(voodoo_current_lfb>>12)+VOODOO_PAGES))
+ return true;
+ return false;
+}
+
+PageHandler* VOODOO_GetPageHandler() {
+ if (voodoo_dev!=NULL) {
+ return voodoo_dev->GetPageHandler();
+ }
+ return NULL;
+}
+
+
+void VOODOO_Destroy(Section* /*sec*/) {
+ delete voodoo_dev;
+ voodoo_dev=NULL;
+}
+
+void VOODOO_Init(Section* sec) {
+ voodoo_current_lfb=(VOODOO_INITIAL_LFB&0xffff0000);
+ voodoo_dev = new VOODOO(sec);
+ sec->AddDestroyFunction(&VOODOO_Destroy,true);
+}
diff --git a/src/hardware/voodoo.h b/src/hardware/voodoo.h
new file mode 100644
index 000000000..8c48f00a5
--- /dev/null
+++ b/src/hardware/voodoo.h
@@ -0,0 +1,42 @@
+ /*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VOODOO_H
+#define DOSBOX_VOODOO_H
+
+
+#define VOODOO_INITIAL_LFB 0xd0000000
+#define VOODOO_REG_PAGES 1024
+#define VOODOO_LFB_PAGES 1024
+#define VOODOO_TEX_PAGES 2048
+#define VOODOO_PAGES (VOODOO_REG_PAGES+VOODOO_LFB_PAGES+VOODOO_TEX_PAGES)
+
+
+#define VOODOO_EMU_TYPE_OFF 0
+#define VOODOO_EMU_TYPE_SOFTWARE 1
+#define VOODOO_EMU_TYPE_ACCELERATED 2
+
+
+void VOODOO_PCI_InitEnable(Bitu val);
+void VOODOO_PCI_Enable(bool enable);
+void VOODOO_PCI_SetLFB(uint32_t lfbaddr);
+bool VOODOO_PCI_CheckLFBPage(Bitu page);
+PageHandler* VOODOO_GetPageHandler();
+
+#endif
diff --git a/src/hardware/voodoo_data.h b/src/hardware/voodoo_data.h
new file mode 100644
index 000000000..802f2cdc3
--- /dev/null
+++ b/src/hardware/voodoo_data.h
@@ -0,0 +1,2238 @@
+/***************************************************************************/
+/* Portion of this software comes with the following license: */
+/***************************************************************************/
+/*
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*************************************************************************
+
+ 3dfx Voodoo Graphics SST-1/2 emulator
+
+ emulator by Aaron Giles
+
+**************************************************************************/
+
+
+#ifndef DOSBOX_VOODOO_DATA_H
+#define DOSBOX_VOODOO_DATA_H
+
+
+/*************************************
+ *
+ * Misc. constants
+ *
+ *************************************/
+
+/* enumeration specifying which model of Voodoo we are emulating */
+enum
+{
+ VOODOO_1,
+ VOODOO_1_DTMU,
+ VOODOO_2,
+ MAX_VOODOO_TYPES
+};
+
+
+/* maximum number of TMUs */
+#define MAX_TMU 2
+
+/* maximum number of rasterizers */
+#define MAX_RASTERIZERS 1024
+
+/* size of the rasterizer hash table */
+#define RASTER_HASH_SIZE 97
+
+/* flags for LFB writes */
+#define LFB_RGB_PRESENT 1
+#define LFB_ALPHA_PRESENT 2
+#define LFB_DEPTH_PRESENT 4
+#define LFB_DEPTH_PRESENT_MSW 8
+
+/* flags for the register access array */
+#define REGISTER_READ 0x01 /* reads are allowed */
+#define REGISTER_WRITE 0x02 /* writes are allowed */
+#define REGISTER_PIPELINED 0x04 /* writes are pipelined */
+#define REGISTER_FIFO 0x08 /* writes go to FIFO */
+#define REGISTER_WRITETHRU 0x10 /* writes are valid even for CMDFIFO */
+
+/* shorter combinations to make the table smaller */
+#define REG_R (REGISTER_READ)
+#define REG_W (REGISTER_WRITE)
+#define REG_WT (REGISTER_WRITE | REGISTER_WRITETHRU)
+#define REG_RW (REGISTER_READ | REGISTER_WRITE)
+#define REG_RWT (REGISTER_READ | REGISTER_WRITE | REGISTER_WRITETHRU)
+#define REG_RP (REGISTER_READ | REGISTER_PIPELINED)
+#define REG_WP (REGISTER_WRITE | REGISTER_PIPELINED)
+#define REG_RWP (REGISTER_READ | REGISTER_WRITE | REGISTER_PIPELINED)
+#define REG_RWPT (REGISTER_READ | REGISTER_WRITE | REGISTER_PIPELINED | REGISTER_WRITETHRU)
+#define REG_RF (REGISTER_READ | REGISTER_FIFO)
+#define REG_WF (REGISTER_WRITE | REGISTER_FIFO)
+#define REG_RWF (REGISTER_READ | REGISTER_WRITE | REGISTER_FIFO)
+#define REG_RPF (REGISTER_READ | REGISTER_PIPELINED | REGISTER_FIFO)
+#define REG_WPF (REGISTER_WRITE | REGISTER_PIPELINED | REGISTER_FIFO)
+#define REG_RWPF (REGISTER_READ | REGISTER_WRITE | REGISTER_PIPELINED | REGISTER_FIFO)
+
+/* lookup bits is the log2 of the size of the reciprocal/log table */
+#define RECIPLOG_LOOKUP_BITS 9
+
+/* input precision is how many fraction bits the input value has; this is a 64-bit number */
+#define RECIPLOG_INPUT_PREC 32
+
+/* lookup precision is how many fraction bits each table entry contains */
+#define RECIPLOG_LOOKUP_PREC 22
+
+/* output precision is how many fraction bits the result should have */
+#define RECIP_OUTPUT_PREC 15
+#define LOG_OUTPUT_PREC 8
+
+
+
+/*************************************
+ *
+ * Dithering tables
+ *
+ *************************************/
+
+static const UINT8 dither_matrix_4x4[16] =
+{
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+};
+
+static const UINT8 dither_matrix_2x2[16] =
+{
+ 2, 10, 2, 10,
+ 14, 6, 14, 6,
+ 2, 10, 2, 10,
+ 14, 6, 14, 6
+};
+
+
+
+/*************************************
+ *
+ * Macros for extracting pixels
+ *
+ *************************************/
+
+#define EXTRACT_565_TO_888(val, a, b, c) \
+ (a) = (((val) >> 8) & 0xf8) | (((val) >> 13) & 0x07); \
+ (b) = (((val) >> 3) & 0xfc) | (((val) >> 9) & 0x03); \
+ (c) = (((val) << 3) & 0xf8) | (((val) >> 2) & 0x07); \
+
+#define EXTRACT_x555_TO_888(val, a, b, c) \
+ (a) = (((val) >> 7) & 0xf8) | (((val) >> 12) & 0x07); \
+ (b) = (((val) >> 2) & 0xf8) | (((val) >> 7) & 0x07); \
+ (c) = (((val) << 3) & 0xf8) | (((val) >> 2) & 0x07); \
+
+#define EXTRACT_555x_TO_888(val, a, b, c) \
+ (a) = (((val) >> 8) & 0xf8) | (((val) >> 13) & 0x07); \
+ (b) = (((val) >> 3) & 0xf8) | (((val) >> 8) & 0x07); \
+ (c) = (((val) << 2) & 0xf8) | (((val) >> 3) & 0x07); \
+
+#define EXTRACT_1555_TO_8888(val, a, b, c, d) \
+ (a) = ((INT16)(val) >> 15) & 0xff; \
+ EXTRACT_x555_TO_888(val, b, c, d) \
+
+#define EXTRACT_5551_TO_8888(val, a, b, c, d) \
+ EXTRACT_555x_TO_888(val, a, b, c) \
+ (d) = ((val) & 0x0001) ? 0xff : 0x00; \
+
+#define EXTRACT_x888_TO_888(val, a, b, c) \
+ (a) = ((val) >> 16) & 0xff; \
+ (b) = ((val) >> 8) & 0xff; \
+ (c) = ((val) >> 0) & 0xff; \
+
+#define EXTRACT_888x_TO_888(val, a, b, c) \
+ (a) = ((val) >> 24) & 0xff; \
+ (b) = ((val) >> 16) & 0xff; \
+ (c) = ((val) >> 8) & 0xff; \
+
+#define EXTRACT_8888_TO_8888(val, a, b, c, d) \
+ (a) = ((val) >> 24) & 0xff; \
+ (b) = ((val) >> 16) & 0xff; \
+ (c) = ((val) >> 8) & 0xff; \
+ (d) = ((val) >> 0) & 0xff; \
+
+#define EXTRACT_4444_TO_8888(val, a, b, c, d) \
+ (a) = (((val) >> 8) & 0xf0) | (((val) >> 12) & 0x0f); \
+ (b) = (((val) >> 4) & 0xf0) | (((val) >> 8) & 0x0f); \
+ (c) = (((val) >> 0) & 0xf0) | (((val) >> 4) & 0x0f); \
+ (d) = (((val) << 4) & 0xf0) | (((val) >> 0) & 0x0f); \
+
+#define EXTRACT_332_TO_888(val, a, b, c) \
+ (a) = (((val) >> 0) & 0xe0) | (((val) >> 3) & 0x1c) | (((val) >> 6) & 0x03); \
+ (b) = (((val) << 3) & 0xe0) | (((val) >> 0) & 0x1c) | (((val) >> 3) & 0x03); \
+ (c) = (((val) << 6) & 0xc0) | (((val) << 4) & 0x30) | (((val) << 2) & 0xc0) | (((val) << 0) & 0x03); \
+
+
+
+/*************************************
+ *
+ * Misc. macros
+ *
+ *************************************/
+
+/* macro for clamping a value between minimum and maximum values */
+#define CLAMP(val,min,max) do { if ((val) < (min)) { (val) = (min); } else if ((val) > (max)) { (val) = (max); } } while (0)
+
+/* macro to compute the base 2 log for LOD calculations */
+#define LOGB2(x) (log((double)(x)) / log(2.0))
+
+
+
+/*************************************
+ *
+ * Macros for extracting bitfields
+ *
+ *************************************/
+
+#define INITEN_ENABLE_HW_INIT(val) (((val) >> 0) & 1)
+#define INITEN_ENABLE_PCI_FIFO(val) (((val) >> 1) & 1)
+#define INITEN_REMAP_INIT_TO_DAC(val) (((val) >> 2) & 1)
+#define INITEN_ENABLE_SNOOP0(val) (((val) >> 4) & 1)
+#define INITEN_SNOOP0_MEMORY_MATCH(val) (((val) >> 5) & 1)
+#define INITEN_SNOOP0_READWRITE_MATCH(val) (((val) >> 6) & 1)
+#define INITEN_ENABLE_SNOOP1(val) (((val) >> 7) & 1)
+#define INITEN_SNOOP1_MEMORY_MATCH(val) (((val) >> 8) & 1)
+#define INITEN_SNOOP1_READWRITE_MATCH(val) (((val) >> 9) & 1)
+#define INITEN_SLI_BUS_OWNER(val) (((val) >> 10) & 1)
+#define INITEN_SLI_ODD_EVEN(val) (((val) >> 11) & 1)
+#define INITEN_SECONDARY_REV_ID(val) (((val) >> 12) & 0xf) /* voodoo 2 only */
+#define INITEN_MFCTR_FAB_ID(val) (((val) >> 16) & 0xf) /* voodoo 2 only */
+#define INITEN_ENABLE_PCI_INTERRUPT(val) (((val) >> 20) & 1) /* voodoo 2 only */
+#define INITEN_PCI_INTERRUPT_TIMEOUT(val) (((val) >> 21) & 1) /* voodoo 2 only */
+#define INITEN_ENABLE_NAND_TREE_TEST(val) (((val) >> 22) & 1) /* voodoo 2 only */
+#define INITEN_ENABLE_SLI_ADDRESS_SNOOP(val) (((val) >> 23) & 1) /* voodoo 2 only */
+#define INITEN_SLI_SNOOP_ADDRESS(val) (((val) >> 24) & 0xff) /* voodoo 2 only */
+
+#define FBZCP_CC_RGBSELECT(val) (((val) >> 0) & 3)
+#define FBZCP_CC_ASELECT(val) (((val) >> 2) & 3)
+#define FBZCP_CC_LOCALSELECT(val) (((val) >> 4) & 1)
+#define FBZCP_CCA_LOCALSELECT(val) (((val) >> 5) & 3)
+#define FBZCP_CC_LOCALSELECT_OVERRIDE(val) (((val) >> 7) & 1)
+#define FBZCP_CC_ZERO_OTHER(val) (((val) >> 8) & 1)
+#define FBZCP_CC_SUB_CLOCAL(val) (((val) >> 9) & 1)
+#define FBZCP_CC_MSELECT(val) (((val) >> 10) & 7)
+#define FBZCP_CC_REVERSE_BLEND(val) (((val) >> 13) & 1)
+#define FBZCP_CC_ADD_ACLOCAL(val) (((val) >> 14) & 3)
+#define FBZCP_CC_INVERT_OUTPUT(val) (((val) >> 16) & 1)
+#define FBZCP_CCA_ZERO_OTHER(val) (((val) >> 17) & 1)
+#define FBZCP_CCA_SUB_CLOCAL(val) (((val) >> 18) & 1)
+#define FBZCP_CCA_MSELECT(val) (((val) >> 19) & 7)
+#define FBZCP_CCA_REVERSE_BLEND(val) (((val) >> 22) & 1)
+#define FBZCP_CCA_ADD_ACLOCAL(val) (((val) >> 23) & 3)
+#define FBZCP_CCA_INVERT_OUTPUT(val) (((val) >> 25) & 1)
+#define FBZCP_CCA_SUBPIXEL_ADJUST(val) (((val) >> 26) & 1)
+#define FBZCP_TEXTURE_ENABLE(val) (((val) >> 27) & 1)
+#define FBZCP_RGBZW_CLAMP(val) (((val) >> 28) & 1) /* voodoo 2 only */
+#define FBZCP_ANTI_ALIAS(val) (((val) >> 29) & 1) /* voodoo 2 only */
+
+#define ALPHAMODE_ALPHATEST(val) (((val) >> 0) & 1)
+#define ALPHAMODE_ALPHAFUNCTION(val) (((val) >> 1) & 7)
+#define ALPHAMODE_ALPHABLEND(val) (((val) >> 4) & 1)
+#define ALPHAMODE_ANTIALIAS(val) (((val) >> 5) & 1)
+#define ALPHAMODE_SRCRGBBLEND(val) (((val) >> 8) & 15)
+#define ALPHAMODE_DSTRGBBLEND(val) (((val) >> 12) & 15)
+#define ALPHAMODE_SRCALPHABLEND(val) (((val) >> 16) & 15)
+#define ALPHAMODE_DSTALPHABLEND(val) (((val) >> 20) & 15)
+#define ALPHAMODE_ALPHAREF(val) (((val) >> 24) & 0xff)
+
+#define FOGMODE_ENABLE_FOG(val) (((val) >> 0) & 1)
+#define FOGMODE_FOG_ADD(val) (((val) >> 1) & 1)
+#define FOGMODE_FOG_MULT(val) (((val) >> 2) & 1)
+#define FOGMODE_FOG_ZALPHA(val) (((val) >> 3) & 3)
+#define FOGMODE_FOG_CONSTANT(val) (((val) >> 5) & 1)
+#define FOGMODE_FOG_DITHER(val) (((val) >> 6) & 1) /* voodoo 2 only */
+#define FOGMODE_FOG_ZONES(val) (((val) >> 7) & 1) /* voodoo 2 only */
+
+#define FBZMODE_ENABLE_CLIPPING(val) (((val) >> 0) & 1)
+#define FBZMODE_ENABLE_CHROMAKEY(val) (((val) >> 1) & 1)
+#define FBZMODE_ENABLE_STIPPLE(val) (((val) >> 2) & 1)
+#define FBZMODE_WBUFFER_SELECT(val) (((val) >> 3) & 1)
+#define FBZMODE_ENABLE_DEPTHBUF(val) (((val) >> 4) & 1)
+#define FBZMODE_DEPTH_FUNCTION(val) (((val) >> 5) & 7)
+#define FBZMODE_ENABLE_DITHERING(val) (((val) >> 8) & 1)
+#define FBZMODE_RGB_BUFFER_MASK(val) (((val) >> 9) & 1)
+#define FBZMODE_AUX_BUFFER_MASK(val) (((val) >> 10) & 1)
+#define FBZMODE_DITHER_TYPE(val) (((val) >> 11) & 1)
+#define FBZMODE_STIPPLE_PATTERN(val) (((val) >> 12) & 1)
+#define FBZMODE_ENABLE_ALPHA_MASK(val) (((val) >> 13) & 1)
+#define FBZMODE_DRAW_BUFFER(val) (((val) >> 14) & 3)
+#define FBZMODE_ENABLE_DEPTH_BIAS(val) (((val) >> 16) & 1)
+#define FBZMODE_Y_ORIGIN(val) (((val) >> 17) & 1)
+#define FBZMODE_ENABLE_ALPHA_PLANES(val) (((val) >> 18) & 1)
+#define FBZMODE_ALPHA_DITHER_SUBTRACT(val) (((val) >> 19) & 1)
+#define FBZMODE_DEPTH_SOURCE_COMPARE(val) (((val) >> 20) & 1)
+#define FBZMODE_DEPTH_FLOAT_SELECT(val) (((val) >> 21) & 1) /* voodoo 2 only */
+
+#define LFBMODE_WRITE_FORMAT(val) (((val) >> 0) & 0xf)
+#define LFBMODE_WRITE_BUFFER_SELECT(val) (((val) >> 4) & 3)
+#define LFBMODE_READ_BUFFER_SELECT(val) (((val) >> 6) & 3)
+#define LFBMODE_ENABLE_PIXEL_PIPELINE(val) (((val) >> 8) & 1)
+#define LFBMODE_RGBA_LANES(val) (((val) >> 9) & 3)
+#define LFBMODE_WORD_SWAP_WRITES(val) (((val) >> 11) & 1)
+#define LFBMODE_BYTE_SWIZZLE_WRITES(val) (((val) >> 12) & 1)
+#define LFBMODE_Y_ORIGIN(val) (((val) >> 13) & 1)
+#define LFBMODE_WRITE_W_SELECT(val) (((val) >> 14) & 1)
+#define LFBMODE_WORD_SWAP_READS(val) (((val) >> 15) & 1)
+#define LFBMODE_BYTE_SWIZZLE_READS(val) (((val) >> 16) & 1)
+
+#define CHROMARANGE_BLUE_EXCLUSIVE(val) (((val) >> 24) & 1)
+#define CHROMARANGE_GREEN_EXCLUSIVE(val) (((val) >> 25) & 1)
+#define CHROMARANGE_RED_EXCLUSIVE(val) (((val) >> 26) & 1)
+#define CHROMARANGE_UNION_MODE(val) (((val) >> 27) & 1)
+#define CHROMARANGE_ENABLE(val) (((val) >> 28) & 1)
+
+#define FBIINIT0_VGA_PASSTHRU(val) (((val) >> 0) & 1)
+#define FBIINIT0_GRAPHICS_RESET(val) (((val) >> 1) & 1)
+#define FBIINIT0_FIFO_RESET(val) (((val) >> 2) & 1)
+#define FBIINIT0_SWIZZLE_REG_WRITES(val) (((val) >> 3) & 1)
+#define FBIINIT0_STALL_PCIE_FOR_HWM(val) (((val) >> 4) & 1)
+#define FBIINIT0_PCI_FIFO_LWM(val) (((val) >> 6) & 0x1f)
+#define FBIINIT0_LFB_TO_MEMORY_FIFO(val) (((val) >> 11) & 1)
+#define FBIINIT0_TEXMEM_TO_MEMORY_FIFO(val) (((val) >> 12) & 1)
+#define FBIINIT0_ENABLE_MEMORY_FIFO(val) (((val) >> 13) & 1)
+#define FBIINIT0_MEMORY_FIFO_HWM(val) (((val) >> 14) & 0x7ff)
+#define FBIINIT0_MEMORY_FIFO_BURST(val) (((val) >> 25) & 0x3f)
+
+#define FBIINIT1_PCI_DEV_FUNCTION(val) (((val) >> 0) & 1)
+#define FBIINIT1_PCI_WRITE_WAIT_STATES(val) (((val) >> 1) & 1)
+#define FBIINIT1_MULTI_SST1(val) (((val) >> 2) & 1) /* not on voodoo 2 */
+#define FBIINIT1_ENABLE_LFB(val) (((val) >> 3) & 1)
+#define FBIINIT1_X_VIDEO_TILES(val) (((val) >> 4) & 0xf)
+#define FBIINIT1_VIDEO_TIMING_RESET(val) (((val) >> 8) & 1)
+#define FBIINIT1_SOFTWARE_OVERRIDE(val) (((val) >> 9) & 1)
+#define FBIINIT1_SOFTWARE_HSYNC(val) (((val) >> 10) & 1)
+#define FBIINIT1_SOFTWARE_VSYNC(val) (((val) >> 11) & 1)
+#define FBIINIT1_SOFTWARE_BLANK(val) (((val) >> 12) & 1)
+#define FBIINIT1_DRIVE_VIDEO_TIMING(val) (((val) >> 13) & 1)
+#define FBIINIT1_DRIVE_VIDEO_BLANK(val) (((val) >> 14) & 1)
+#define FBIINIT1_DRIVE_VIDEO_SYNC(val) (((val) >> 15) & 1)
+#define FBIINIT1_DRIVE_VIDEO_DCLK(val) (((val) >> 16) & 1)
+#define FBIINIT1_VIDEO_TIMING_VCLK(val) (((val) >> 17) & 1)
+#define FBIINIT1_VIDEO_CLK_2X_DELAY(val) (((val) >> 18) & 3)
+#define FBIINIT1_VIDEO_TIMING_SOURCE(val) (((val) >> 20) & 3)
+#define FBIINIT1_ENABLE_24BPP_OUTPUT(val) (((val) >> 22) & 1)
+#define FBIINIT1_ENABLE_SLI(val) (((val) >> 23) & 1)
+#define FBIINIT1_X_VIDEO_TILES_BIT5(val) (((val) >> 24) & 1) /* voodoo 2 only */
+#define FBIINIT1_ENABLE_EDGE_FILTER(val) (((val) >> 25) & 1)
+#define FBIINIT1_INVERT_VID_CLK_2X(val) (((val) >> 26) & 1)
+#define FBIINIT1_VID_CLK_2X_SEL_DELAY(val) (((val) >> 27) & 3)
+#define FBIINIT1_VID_CLK_DELAY(val) (((val) >> 29) & 3)
+#define FBIINIT1_DISABLE_FAST_READAHEAD(val) (((val) >> 31) & 1)
+
+#define FBIINIT2_DISABLE_DITHER_SUB(val) (((val) >> 0) & 1)
+#define FBIINIT2_DRAM_BANKING(val) (((val) >> 1) & 1)
+#define FBIINIT2_ENABLE_TRIPLE_BUF(val) (((val) >> 4) & 1)
+#define FBIINIT2_ENABLE_FAST_RAS_READ(val) (((val) >> 5) & 1)
+#define FBIINIT2_ENABLE_GEN_DRAM_OE(val) (((val) >> 6) & 1)
+#define FBIINIT2_ENABLE_FAST_READWRITE(val) (((val) >> 7) & 1)
+#define FBIINIT2_ENABLE_PASSTHRU_DITHER(val) (((val) >> 8) & 1)
+#define FBIINIT2_SWAP_BUFFER_ALGORITHM(val) (((val) >> 9) & 3)
+#define FBIINIT2_VIDEO_BUFFER_OFFSET(val) (((val) >> 11) & 0x1ff)
+#define FBIINIT2_ENABLE_DRAM_BANKING(val) (((val) >> 20) & 1)
+#define FBIINIT2_ENABLE_DRAM_READ_FIFO(val) (((val) >> 21) & 1)
+#define FBIINIT2_ENABLE_DRAM_REFRESH(val) (((val) >> 22) & 1)
+#define FBIINIT2_REFRESH_LOAD_VALUE(val) (((val) >> 23) & 0x1ff)
+
+#define FBIINIT3_TRI_REGISTER_REMAP(val) (((val) >> 0) & 1)
+#define FBIINIT3_VIDEO_FIFO_THRESH(val) (((val) >> 1) & 0x1f)
+#define FBIINIT3_DISABLE_TMUS(val) (((val) >> 6) & 1)
+#define FBIINIT3_FBI_MEMORY_TYPE(val) (((val) >> 8) & 7)
+#define FBIINIT3_VGA_PASS_RESET_VAL(val) (((val) >> 11) & 1)
+#define FBIINIT3_HARDCODE_PCI_BASE(val) (((val) >> 12) & 1)
+#define FBIINIT3_FBI2TREX_DELAY(val) (((val) >> 13) & 0xf)
+#define FBIINIT3_TREX2FBI_DELAY(val) (((val) >> 17) & 0x1f)
+#define FBIINIT3_YORIGIN_SUBTRACT(val) (((val) >> 22) & 0x3ff)
+
+#define FBIINIT4_PCI_READ_WAITS(val) (((val) >> 0) & 1)
+#define FBIINIT4_ENABLE_LFB_READAHEAD(val) (((val) >> 1) & 1)
+#define FBIINIT4_MEMORY_FIFO_LWM(val) (((val) >> 2) & 0x3f)
+#define FBIINIT4_MEMORY_FIFO_START_ROW(val) (((val) >> 8) & 0x3ff)
+#define FBIINIT4_MEMORY_FIFO_STOP_ROW(val) (((val) >> 18) & 0x3ff)
+#define FBIINIT4_VIDEO_CLOCKING_DELAY(val) (((val) >> 29) & 7) /* voodoo 2 only */
+
+#define FBIINIT5_DISABLE_PCI_STOP(val) (((val) >> 0) & 1) /* voodoo 2 only */
+#define FBIINIT5_PCI_SLAVE_SPEED(val) (((val) >> 1) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_OUTPUT_WIDTH(val) (((val) >> 2) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_17_OUTPUT(val) (((val) >> 3) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_18_OUTPUT(val) (((val) >> 4) & 1) /* voodoo 2 only */
+#define FBIINIT5_GENERIC_STRAPPING(val) (((val) >> 5) & 0xf) /* voodoo 2 only */
+#define FBIINIT5_BUFFER_ALLOCATION(val) (((val) >> 9) & 3) /* voodoo 2 only */
+#define FBIINIT5_DRIVE_VID_CLK_SLAVE(val) (((val) >> 11) & 1) /* voodoo 2 only */
+#define FBIINIT5_DRIVE_DAC_DATA_16(val) (((val) >> 12) & 1) /* voodoo 2 only */
+#define FBIINIT5_VCLK_INPUT_SELECT(val) (((val) >> 13) & 1) /* voodoo 2 only */
+#define FBIINIT5_MULTI_CVG_DETECT(val) (((val) >> 14) & 1) /* voodoo 2 only */
+#define FBIINIT5_SYNC_RETRACE_READS(val) (((val) >> 15) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_RHBORDER_COLOR(val) (((val) >> 16) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_LHBORDER_COLOR(val) (((val) >> 17) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_BVBORDER_COLOR(val) (((val) >> 18) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_TVBORDER_COLOR(val) (((val) >> 19) & 1) /* voodoo 2 only */
+#define FBIINIT5_DOUBLE_HORIZ(val) (((val) >> 20) & 1) /* voodoo 2 only */
+#define FBIINIT5_DOUBLE_VERT(val) (((val) >> 21) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_16BIT_GAMMA(val) (((val) >> 22) & 1) /* voodoo 2 only */
+#define FBIINIT5_INVERT_DAC_HSYNC(val) (((val) >> 23) & 1) /* voodoo 2 only */
+#define FBIINIT5_INVERT_DAC_VSYNC(val) (((val) >> 24) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_24BIT_DACDATA(val) (((val) >> 25) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_INTERLACING(val) (((val) >> 26) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_18_CONTROL(val) (((val) >> 27) & 1) /* voodoo 2 only */
+#define FBIINIT5_RASTERIZER_UNIT_MODE(val) (((val) >> 30) & 3) /* voodoo 2 only */
+
+#define FBIINIT6_WINDOW_ACTIVE_COUNTER(val) (((val) >> 0) & 7) /* voodoo 2 only */
+#define FBIINIT6_WINDOW_DRAG_COUNTER(val) (((val) >> 3) & 0x1f) /* voodoo 2 only */
+#define FBIINIT6_SLI_SYNC_MASTER(val) (((val) >> 8) & 1) /* voodoo 2 only */
+#define FBIINIT6_DAC_DATA_22_OUTPUT(val) (((val) >> 9) & 3) /* voodoo 2 only */
+#define FBIINIT6_DAC_DATA_23_OUTPUT(val) (((val) >> 11) & 3) /* voodoo 2 only */
+#define FBIINIT6_SLI_SYNCIN_OUTPUT(val) (((val) >> 13) & 3) /* voodoo 2 only */
+#define FBIINIT6_SLI_SYNCOUT_OUTPUT(val) (((val) >> 15) & 3) /* voodoo 2 only */
+#define FBIINIT6_DAC_RD_OUTPUT(val) (((val) >> 17) & 3) /* voodoo 2 only */
+#define FBIINIT6_DAC_WR_OUTPUT(val) (((val) >> 19) & 3) /* voodoo 2 only */
+#define FBIINIT6_PCI_FIFO_LWM_RDY(val) (((val) >> 21) & 0x7f) /* voodoo 2 only */
+#define FBIINIT6_VGA_PASS_N_OUTPUT(val) (((val) >> 28) & 3) /* voodoo 2 only */
+#define FBIINIT6_X_VIDEO_TILES_BIT0(val) (((val) >> 30) & 1) /* voodoo 2 only */
+
+#define FBIINIT7_GENERIC_STRAPPING(val) (((val) >> 0) & 0xff) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_ENABLE(val) (((val) >> 8) & 1) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_MEMORY_STORE(val) (((val) >> 9) & 1) /* voodoo 2 only */
+#define FBIINIT7_DISABLE_CMDFIFO_HOLES(val) (((val) >> 10) & 1) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_READ_THRESH(val) (((val) >> 11) & 0x1f) /* voodoo 2 only */
+#define FBIINIT7_SYNC_CMDFIFO_WRITES(val) (((val) >> 16) & 1) /* voodoo 2 only */
+#define FBIINIT7_SYNC_CMDFIFO_READS(val) (((val) >> 17) & 1) /* voodoo 2 only */
+#define FBIINIT7_RESET_PCI_PACKER(val) (((val) >> 18) & 1) /* voodoo 2 only */
+#define FBIINIT7_ENABLE_CHROMA_STUFF(val) (((val) >> 19) & 1) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_PCI_TIMEOUT(val) (((val) >> 20) & 0x7f) /* voodoo 2 only */
+#define FBIINIT7_ENABLE_TEXTURE_BURST(val) (((val) >> 27) & 1) /* voodoo 2 only */
+
+#define TEXMODE_ENABLE_PERSPECTIVE(val) (((val) >> 0) & 1)
+#define TEXMODE_MINIFICATION_FILTER(val) (((val) >> 1) & 1)
+#define TEXMODE_MAGNIFICATION_FILTER(val) (((val) >> 2) & 1)
+#define TEXMODE_CLAMP_NEG_W(val) (((val) >> 3) & 1)
+#define TEXMODE_ENABLE_LOD_DITHER(val) (((val) >> 4) & 1)
+#define TEXMODE_NCC_TABLE_SELECT(val) (((val) >> 5) & 1)
+#define TEXMODE_CLAMP_S(val) (((val) >> 6) & 1)
+#define TEXMODE_CLAMP_T(val) (((val) >> 7) & 1)
+#define TEXMODE_FORMAT(val) (((val) >> 8) & 0xf)
+#define TEXMODE_TC_ZERO_OTHER(val) (((val) >> 12) & 1)
+#define TEXMODE_TC_SUB_CLOCAL(val) (((val) >> 13) & 1)
+#define TEXMODE_TC_MSELECT(val) (((val) >> 14) & 7)
+#define TEXMODE_TC_REVERSE_BLEND(val) (((val) >> 17) & 1)
+#define TEXMODE_TC_ADD_ACLOCAL(val) (((val) >> 18) & 3)
+#define TEXMODE_TC_INVERT_OUTPUT(val) (((val) >> 20) & 1)
+#define TEXMODE_TCA_ZERO_OTHER(val) (((val) >> 21) & 1)
+#define TEXMODE_TCA_SUB_CLOCAL(val) (((val) >> 22) & 1)
+#define TEXMODE_TCA_MSELECT(val) (((val) >> 23) & 7)
+#define TEXMODE_TCA_REVERSE_BLEND(val) (((val) >> 26) & 1)
+#define TEXMODE_TCA_ADD_ACLOCAL(val) (((val) >> 27) & 3)
+#define TEXMODE_TCA_INVERT_OUTPUT(val) (((val) >> 29) & 1)
+#define TEXMODE_TRILINEAR(val) (((val) >> 30) & 1)
+#define TEXMODE_SEQ_8_DOWNLD(val) (((val) >> 31) & 1)
+
+#define TEXLOD_LODMIN(val) (((val) >> 0) & 0x3f)
+#define TEXLOD_LODMAX(val) (((val) >> 6) & 0x3f)
+#define TEXLOD_LODBIAS(val) (((val) >> 12) & 0x3f)
+#define TEXLOD_LOD_ODD(val) (((val) >> 18) & 1)
+#define TEXLOD_LOD_TSPLIT(val) (((val) >> 19) & 1)
+#define TEXLOD_LOD_S_IS_WIDER(val) (((val) >> 20) & 1)
+#define TEXLOD_LOD_ASPECT(val) (((val) >> 21) & 3)
+#define TEXLOD_LOD_ZEROFRAC(val) (((val) >> 23) & 1)
+#define TEXLOD_TMULTIBASEADDR(val) (((val) >> 24) & 1)
+#define TEXLOD_TDATA_SWIZZLE(val) (((val) >> 25) & 1)
+#define TEXLOD_TDATA_SWAP(val) (((val) >> 26) & 1)
+#define TEXLOD_TDIRECT_WRITE(val) (((val) >> 27) & 1) /* Voodoo 2 only */
+
+#define TEXDETAIL_DETAIL_MAX(val) (((val) >> 0) & 0xff)
+#define TEXDETAIL_DETAIL_BIAS(val) (((val) >> 8) & 0x3f)
+#define TEXDETAIL_DETAIL_SCALE(val) (((val) >> 14) & 7)
+#define TEXDETAIL_RGB_MIN_FILTER(val) (((val) >> 17) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_RGB_MAG_FILTER(val) (((val) >> 18) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_ALPHA_MIN_FILTER(val) (((val) >> 19) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_ALPHA_MAG_FILTER(val) (((val) >> 20) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_SEPARATE_RGBA_FILTER(val) (((val) >> 21) & 1) /* Voodoo 2 only */
+
+#define TREXINIT_SEND_TMU_CONFIG(val) (((val) >> 18) & 1)
+
+
+/*************************************
+ *
+ * Core types
+ *
+ *************************************/
+
+typedef struct _voodoo_state voodoo_state;
+typedef struct _poly_extra_data poly_extra_data;
+
+typedef UINT32 rgb_t;
+
+typedef struct _rgba rgba;
+#define LSB_FIRST
+struct _rgba
+{
+#ifdef LSB_FIRST
+ UINT8 b, g, r, a;
+#else
+ UINT8 a, r, g, b;
+#endif
+};
+
+
+typedef union _voodoo_reg voodoo_reg;
+union _voodoo_reg
+{
+ INT32 i;
+ UINT32 u;
+ float f;
+ rgba rgb;
+};
+
+
+typedef voodoo_reg rgb_union;
+
+
+/* note that this structure is an even 64 bytes long */
+typedef struct _stats_block stats_block;
+struct _stats_block
+{
+ INT32 pixels_in; /* pixels in statistic */
+ INT32 pixels_out; /* pixels out statistic */
+ INT32 chroma_fail; /* chroma test fail statistic */
+ INT32 zfunc_fail; /* z function test fail statistic */
+ INT32 afunc_fail; /* alpha function test fail statistic */
+ INT32 clip_fail; /* clipping fail statistic */
+ INT32 stipple_count; /* stipple statistic */
+ INT32 filler[64/4 - 7]; /* pad this structure to 64 bytes */
+};
+
+
+typedef struct _fifo_state fifo_state;
+struct _fifo_state
+{
+ INT32 size; /* size of the FIFO */
+};
+
+
+typedef struct _pci_state pci_state;
+struct _pci_state
+{
+ fifo_state fifo; /* PCI FIFO */
+ UINT32 init_enable; /* initEnable value */
+ bool op_pending; /* true if an operation is pending */
+};
+
+
+typedef struct _ncc_table ncc_table;
+struct _ncc_table
+{
+ bool dirty; /* is the texel lookup dirty? */
+ voodoo_reg * reg; /* pointer to our registers */
+ INT32 ir[4], ig[4], ib[4]; /* I values for R,G,B */
+ INT32 qr[4], qg[4], qb[4]; /* Q values for R,G,B */
+ INT32 y[16]; /* Y values */
+ rgb_t * palette; /* pointer to associated RGB palette */
+ rgb_t * palettea; /* pointer to associated ARGB palette */
+ rgb_t texel[256]; /* texel lookup */
+};
+
+
+typedef struct _tmu_state tmu_state;
+struct _tmu_state
+{
+ UINT8 * ram; /* pointer to our RAM */
+ UINT32 mask; /* mask to apply to pointers */
+ voodoo_reg * reg; /* pointer to our register base */
+ bool regdirty; /* true if the LOD/mode/base registers have changed */
+
+ UINT32 texaddr_mask; /* mask for texture address */
+ UINT8 texaddr_shift; /* shift for texture address */
+
+ INT64 starts, startt; /* starting S,T (14.18) */
+ INT64 startw; /* starting W (2.30) */
+ INT64 dsdx, dtdx; /* delta S,T per X */
+ INT64 dwdx; /* delta W per X */
+ INT64 dsdy, dtdy; /* delta S,T per Y */
+ INT64 dwdy; /* delta W per Y */
+
+ INT32 lodmin, lodmax; /* min, max LOD values */
+ INT32 lodbias; /* LOD bias */
+ UINT32 lodmask; /* mask of available LODs */
+ UINT32 lodoffset[9]; /* offset of texture base for each LOD */
+ INT32 detailmax; /* detail clamp */
+ INT32 detailbias; /* detail bias */
+ UINT8 detailscale; /* detail scale */
+
+ UINT32 wmask; /* mask for the current texture width */
+ UINT32 hmask; /* mask for the current texture height */
+
+ UINT8 bilinear_mask; /* mask for bilinear resolution (0xf0 for V1, 0xff for V2) */
+
+ ncc_table ncc[2]; /* two NCC tables */
+
+ rgb_t * lookup; /* currently selected lookup */
+ rgb_t * texel[16]; /* texel lookups for each format */
+
+ rgb_t palette[256]; /* palette lookup table */
+ rgb_t palettea[256]; /* palette+alpha lookup table */
+};
+
+
+typedef struct _tmu_shared_state tmu_shared_state;
+struct _tmu_shared_state
+{
+ rgb_t rgb332[256]; /* RGB 3-3-2 lookup table */
+ rgb_t alpha8[256]; /* alpha 8-bit lookup table */
+ rgb_t int8[256]; /* intensity 8-bit lookup table */
+ rgb_t ai44[256]; /* alpha, intensity 4-4 lookup table */
+
+ rgb_t rgb565[65536]; /* RGB 5-6-5 lookup table */
+ rgb_t argb1555[65536]; /* ARGB 1-5-5-5 lookup table */
+ rgb_t argb4444[65536]; /* ARGB 4-4-4-4 lookup table */
+};
+
+
+typedef struct _setup_vertex setup_vertex;
+struct _setup_vertex
+{
+ float x, y; /* X, Y coordinates */
+ float a, r, g, b; /* A, R, G, B values */
+ float z, wb; /* Z and broadcast W values */
+ float w0, s0, t0; /* W, S, T for TMU 0 */
+ float w1, s1, t1; /* W, S, T for TMU 1 */
+};
+
+
+typedef struct _fbi_state fbi_state;
+struct _fbi_state
+{
+ UINT8 * ram; /* pointer to frame buffer RAM */
+ UINT32 mask; /* mask to apply to pointers */
+ UINT32 rgboffs[3]; /* word offset to 3 RGB buffers */
+ UINT32 auxoffs; /* word offset to 1 aux buffer */
+
+ UINT8 frontbuf; /* front buffer index */
+ UINT8 backbuf; /* back buffer index */
+
+ UINT32 yorigin; /* Y origin subtract value */
+
+ UINT32 width; /* width of current frame buffer */
+ UINT32 height; /* height of current frame buffer */
+// UINT32 xoffs; /* horizontal offset (back porch) */
+// UINT32 yoffs; /* vertical offset (back porch) */
+// UINT32 vsyncscan; /* vertical sync scanline */
+ UINT32 rowpixels; /* pixels per row */
+ UINT32 tile_width; /* width of video tiles */
+ UINT32 tile_height; /* height of video tiles */
+ UINT32 x_tiles; /* number of tiles in the X direction */
+
+ UINT8 vblank; /* VBLANK state */
+ bool vblank_dont_swap; /* don't actually swap when we hit this point */
+ bool vblank_flush_pending;
+
+ /* triangle setup info */
+ INT16 ax, ay; /* vertex A x,y (12.4) */
+ INT16 bx, by; /* vertex B x,y (12.4) */
+ INT16 cx, cy; /* vertex C x,y (12.4) */
+ INT32 startr, startg, startb, starta; /* starting R,G,B,A (12.12) */
+ INT32 startz; /* starting Z (20.12) */
+ INT64 startw; /* starting W (16.32) */
+ INT32 drdx, dgdx, dbdx, dadx; /* delta R,G,B,A per X */
+ INT32 dzdx; /* delta Z per X */
+ INT64 dwdx; /* delta W per X */
+ INT32 drdy, dgdy, dbdy, dady; /* delta R,G,B,A per Y */
+ INT32 dzdy; /* delta Z per Y */
+ INT64 dwdy; /* delta W per Y */
+
+ stats_block lfb_stats; /* LFB-access statistics */
+
+ UINT8 sverts; /* number of vertices ready */
+ setup_vertex svert[3]; /* 3 setup vertices */
+
+ fifo_state fifo; /* framebuffer memory fifo */
+
+ UINT8 fogblend[64]; /* 64-entry fog table */
+ UINT8 fogdelta[64]; /* 64-entry fog table */
+ UINT8 fogdelta_mask; /* mask for for delta (0xff for V1, 0xfc for V2) */
+
+// rgb_t clut[512]; /* clut gamma data */
+};
+
+
+typedef struct _dac_state dac_state;
+struct _dac_state
+{
+ UINT8 reg[8]; /* 8 registers */
+ UINT8 read_result; /* pending read result */
+};
+
+
+typedef struct _raster_info raster_info;
+struct _raster_info
+{
+ struct _raster_info *next; /* pointer to next entry with the same hash */
+ poly_draw_scanline_func callback; /* callback pointer */
+ bool is_generic; /* true if this is one of the generic rasterizers */
+ UINT8 display; /* display index */
+ UINT32 hits; /* how many hits (pixels) we've used this for */
+ UINT32 polys; /* how many polys we've used this for */
+ UINT32 eff_color_path; /* effective fbzColorPath value */
+ UINT32 eff_alpha_mode; /* effective alphaMode value */
+ UINT32 eff_fog_mode; /* effective fogMode value */
+ UINT32 eff_fbz_mode; /* effective fbzMode value */
+ UINT32 eff_tex_mode_0; /* effective textureMode value for TMU #0 */
+ UINT32 eff_tex_mode_1; /* effective textureMode value for TMU #1 */
+
+ bool shader_ready;
+ UINT32 so_shader_program;
+ UINT32 so_vertex_shader;
+ UINT32 so_fragment_shader;
+ INT32* shader_ulocations;
+};
+
+
+struct _poly_extra_data
+{
+ voodoo_state * state; /* pointer back to the voodoo state */
+ raster_info * info; /* pointer to rasterizer information */
+
+ INT16 ax, ay; /* vertex A x,y (12.4) */
+ INT32 startr, startg, startb, starta; /* starting R,G,B,A (12.12) */
+ INT32 startz; /* starting Z (20.12) */
+ INT64 startw; /* starting W (16.32) */
+ INT32 drdx, dgdx, dbdx, dadx; /* delta R,G,B,A per X */
+ INT32 dzdx; /* delta Z per X */
+ INT64 dwdx; /* delta W per X */
+ INT32 drdy, dgdy, dbdy, dady; /* delta R,G,B,A per Y */
+ INT32 dzdy; /* delta Z per Y */
+ INT64 dwdy; /* delta W per Y */
+
+ INT64 starts0, startt0; /* starting S,T (14.18) */
+ INT64 startw0; /* starting W (2.30) */
+ INT64 ds0dx, dt0dx; /* delta S,T per X */
+ INT64 dw0dx; /* delta W per X */
+ INT64 ds0dy, dt0dy; /* delta S,T per Y */
+ INT64 dw0dy; /* delta W per Y */
+ INT32 lodbase0; /* used during rasterization */
+
+ INT64 starts1, startt1; /* starting S,T (14.18) */
+ INT64 startw1; /* starting W (2.30) */
+ INT64 ds1dx, dt1dx; /* delta S,T per X */
+ INT64 dw1dx; /* delta W per X */
+ INT64 ds1dy, dt1dy; /* delta S,T per Y */
+ INT64 dw1dy; /* delta W per Y */
+ INT32 lodbase1; /* used during rasterization */
+
+ UINT16 dither[16]; /* dither matrix, for fastfill */
+
+ Bitu texcount;
+ Bitu r_fbzColorPath;
+ Bitu r_fbzMode;
+ Bitu r_alphaMode;
+ Bitu r_fogMode;
+ INT32 r_textureMode0;
+ INT32 r_textureMode1;
+};
+
+
+/* typedef struct _voodoo_state voodoo_state; -- declared above */
+struct _voodoo_state
+{
+ UINT8 type; /* type of system */
+ UINT8 chipmask; /* mask for which chips are available */
+
+ voodoo_reg reg[0x400]; /* raw registers */
+ const UINT8 * regaccess; /* register access array */
+ const char *const * regnames; /* register names array */
+ bool alt_regmap; /* enable alternate register map? */
+
+ pci_state pci; /* PCI state */
+ dac_state dac; /* DAC state */
+
+ fbi_state fbi; /* FBI states */
+ tmu_state tmu[MAX_TMU]; /* TMU states */
+ tmu_shared_state tmushare; /* TMU shared state */
+
+ stats_block * thread_stats; /* per-thread statistics */
+
+ int next_rasterizer; /* next rasterizer index */
+ raster_info rasterizer[MAX_RASTERIZERS]; /* array of rasterizers */
+ raster_info * raster_hash[RASTER_HASH_SIZE]; /* hash table of rasterizers */
+
+ bool send_config;
+ UINT32 tmu_config;
+
+ bool ogl;
+ bool ogl_dimchange;
+ bool clock_enabled;
+ bool output_on;
+ bool active;
+};
+
+
+
+/*************************************
+ *
+ * Inline FIFO management
+ *
+ *************************************/
+
+INLINE INT32 fifo_space(fifo_state *f)
+{
+ INT32 items = 0;
+ if (items < 0)
+ items += f->size;
+ return f->size - 1 - items;
+}
+
+
+INLINE UINT8 count_leading_zeros(UINT32 value)
+{
+ INT32 result;
+
+#if defined _MSC_VER && defined(_M_IX86)
+ __asm
+ {
+ bsr eax,value
+ jnz skip
+ mov eax,63
+ skip:
+ xor eax,31
+ mov result,eax
+ }
+#else
+ result = 32;
+ while(value > 0) {
+ result--;
+ value >>= 1;
+ }
+#endif
+
+ return (UINT8)result;
+}
+
+/*************************************
+ *
+ * Computes a fast 16.16 reciprocal
+ * of a 16.32 value; used for
+ * computing 1/w in the rasterizer.
+ *
+ * Since it is trivial to also
+ * compute log2(1/w) = -log2(w) at
+ * the same time, we do that as well
+ * to 16.8 precision for LOD
+ * calculations.
+ *
+ * On a Pentium M, this routine is
+ * 20% faster than a 64-bit integer
+ * divide and also produces the log
+ * for free.
+ *
+ *************************************/
+
+INLINE INT64 fast_reciplog(INT64 value, INT32 *log2)
+{
+ extern UINT32 voodoo_reciplog[];
+ UINT32 temp, rlog;
+ UINT32 interp;
+ UINT32 *table;
+ UINT64 recip;
+ bool neg = false;
+ int lz, exp = 0;
+
+ /* always work with unsigned numbers */
+ if (value < 0)
+ {
+ value = -value;
+ neg = true;
+ }
+
+ /* if we've spilled out of 32 bits, push it down under 32 */
+ if (value & LONGTYPE(0xffff00000000))
+ {
+ temp = (UINT32)(value >> 16);
+ exp -= 16;
+ }
+ else
+ temp = (UINT32)value;
+
+ /* if the resulting value is 0, the reciprocal is infinite */
+ if (GCC_UNLIKELY(temp == 0))
+ {
+ *log2 = 1000 << LOG_OUTPUT_PREC;
+ return neg ? 0x80000000 : 0x7fffffff;
+ }
+
+ /* determine how many leading zeros in the value and shift it up high */
+ lz = count_leading_zeros(temp);
+ temp <<= lz;
+ exp += lz;
+
+ /* compute a pointer to the table entries we want */
+ /* math is a bit funny here because we shift one less than we need to in order */
+ /* to account for the fact that there are two UINT32's per table entry */
+ table = &voodoo_reciplog[(temp >> (31 - RECIPLOG_LOOKUP_BITS - 1)) & ((2 << RECIPLOG_LOOKUP_BITS) - 2)];
+
+ /* compute the interpolation value */
+ interp = (temp >> (31 - RECIPLOG_LOOKUP_BITS - 8)) & 0xff;
+
+ /* do a linear interpolatation between the two nearest table values */
+ /* for both the log and the reciprocal */
+ rlog = (table[1] * (0x100 - interp) + table[3] * interp) >> 8;
+ recip = (table[0] * (0x100 - interp) + table[2] * interp) >> 8;
+
+ /* the log result is the fractional part of the log; round it to the output precision */
+ rlog = (rlog + (1 << (RECIPLOG_LOOKUP_PREC - LOG_OUTPUT_PREC - 1))) >> (RECIPLOG_LOOKUP_PREC - LOG_OUTPUT_PREC);
+
+ /* the exponent is the non-fractional part of the log; normally, we would subtract it from rlog */
+ /* but since we want the log(1/value) = -log(value), we subtract rlog from the exponent */
+ *log2 = ((exp - (31 - RECIPLOG_INPUT_PREC)) << LOG_OUTPUT_PREC) - rlog;
+
+ /* adjust the exponent to account for all the reciprocal-related parameters to arrive at a final shift amount */
+ exp += (RECIP_OUTPUT_PREC - RECIPLOG_LOOKUP_PREC) - (31 - RECIPLOG_INPUT_PREC);
+
+ /* shift by the exponent */
+ if (exp < 0)
+ recip >>= -exp;
+ else
+ recip <<= exp;
+
+ /* on the way out, apply the original sign to the reciprocal */
+ return neg ? -(INT64)recip : (INT64)recip;
+}
+
+
+
+/*************************************
+ *
+ * Float-to-int conversions
+ *
+ *************************************/
+
+INLINE INT32 float_to_int32(UINT32 data, int fixedbits)
+{
+ int exponent = ((data >> 23) & 0xff) - 127 - 23 + fixedbits;
+ INT32 result = (data & 0x7fffff) | 0x800000;
+ if (exponent < 0)
+ {
+ if (exponent > -32)
+ result >>= -exponent;
+ else
+ result = 0;
+ }
+ else
+ {
+ if (exponent < 32)
+ result <<= exponent;
+ else
+ result = 0x7fffffff;
+ }
+ if (data & 0x80000000)
+ result = -result;
+ return result;
+}
+
+
+INLINE INT64 float_to_int64(UINT32 data, int fixedbits)
+{
+ int exponent = ((data >> 23) & 0xff) - 127 - 23 + fixedbits;
+ INT64 result = (data & 0x7fffff) | 0x800000;
+ if (exponent < 0)
+ {
+ if (exponent > -64)
+ result >>= -exponent;
+ else
+ result = 0;
+ }
+ else
+ {
+ if (exponent < 64)
+ result <<= exponent;
+ else
+ result = LONGTYPE(0x7fffffffffffffff);
+ }
+ if (data & 0x80000000)
+ result = -result;
+ return result;
+}
+
+
+
+/*************************************
+ *
+ * Rasterizer inlines
+ *
+ *************************************/
+
+INLINE UINT32 normalize_color_path(UINT32 eff_color_path)
+{
+ /* ignore the subpixel adjust and texture enable flags */
+ eff_color_path &= ~((1 << 26) | (1 << 27));
+
+ return eff_color_path;
+}
+
+
+INLINE UINT32 normalize_alpha_mode(UINT32 eff_alpha_mode)
+{
+ /* always ignore alpha ref value */
+ eff_alpha_mode &= ~(0xff << 24);
+
+ /* if not doing alpha testing, ignore the alpha function and ref value */
+ if (!ALPHAMODE_ALPHATEST(eff_alpha_mode))
+ eff_alpha_mode &= ~(7 << 1);
+
+ /* if not doing alpha blending, ignore the source and dest blending factors */
+ if (!ALPHAMODE_ALPHABLEND(eff_alpha_mode))
+ eff_alpha_mode &= ~((15 << 8) | (15 << 12) | (15 << 16) | (15 << 20));
+
+ return eff_alpha_mode;
+}
+
+
+INLINE UINT32 normalize_fog_mode(UINT32 eff_fog_mode)
+{
+ /* if not doing fogging, ignore all the other fog bits */
+ if (!FOGMODE_ENABLE_FOG(eff_fog_mode))
+ eff_fog_mode = 0;
+
+ return eff_fog_mode;
+}
+
+
+INLINE UINT32 normalize_fbz_mode(UINT32 eff_fbz_mode)
+{
+ /* ignore the draw buffer */
+ eff_fbz_mode &= ~(3 << 14);
+
+ return eff_fbz_mode;
+}
+
+
+INLINE UINT32 normalize_tex_mode(UINT32 eff_tex_mode)
+{
+ /* ignore the NCC table and seq_8_downld flags */
+ eff_tex_mode &= ~((1 << 5) | (1 << 31));
+
+ /* classify texture formats into 3 format categories */
+ if (TEXMODE_FORMAT(eff_tex_mode) < 8)
+ eff_tex_mode = (eff_tex_mode & ~(0xf << 8)) | (0 << 8);
+ else if (TEXMODE_FORMAT(eff_tex_mode) >= 10 && TEXMODE_FORMAT(eff_tex_mode) <= 12)
+ eff_tex_mode = (eff_tex_mode & ~(0xf << 8)) | (10 << 8);
+ else
+ eff_tex_mode = (eff_tex_mode & ~(0xf << 8)) | (8 << 8);
+
+ return eff_tex_mode;
+}
+
+
+INLINE UINT32 compute_raster_hash(const raster_info *info)
+{
+ UINT32 hash;
+
+ /* make a hash */
+ hash = info->eff_color_path;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_fbz_mode;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_alpha_mode;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_fog_mode;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_tex_mode_0;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_tex_mode_1;
+
+ return hash % RASTER_HASH_SIZE;
+}
+
+
+
+/*************************************
+ *
+ * Dithering macros
+ *
+ *************************************/
+
+/* note that these equations and the dither matrixes have
+ been confirmed to be exact matches to the real hardware */
+#define DITHER_RB(val,dith) ((((val) << 1) - ((val) >> 4) + ((val) >> 7) + (dith)) >> 1)
+#define DITHER_G(val,dith) ((((val) << 2) - ((val) >> 4) + ((val) >> 6) + (dith)) >> 2)
+
+#define DECLARE_DITHER_POINTERS \
+ const UINT8 *dither_lookup = NULL; \
+ const UINT8 *dither4 = NULL; \
+ [[maybe_unused]] const UINT8 *dither = NULL \
+
+#define COMPUTE_DITHER_POINTERS(FBZMODE, YY) \
+do \
+{ \
+ /* compute the dithering pointers */ \
+ if (FBZMODE_ENABLE_DITHERING(FBZMODE)) \
+ { \
+ dither4 = &dither_matrix_4x4[((YY) & 3) * 4]; \
+ if (FBZMODE_DITHER_TYPE(FBZMODE) == 0) \
+ { \
+ dither = dither4; \
+ dither_lookup = &dither4_lookup[(YY & 3) << 11]; \
+ } \
+ else \
+ { \
+ dither = &dither_matrix_2x2[((YY) & 3) * 4]; \
+ dither_lookup = &dither2_lookup[(YY & 3) << 11]; \
+ } \
+ } \
+} \
+while (0)
+
+#define APPLY_DITHER(FBZMODE, XX, DITHER_LOOKUP, RR, GG, BB) \
+do \
+{ \
+ /* apply dithering */ \
+ if (FBZMODE_ENABLE_DITHERING(FBZMODE)) \
+ { \
+ /* look up the dither value from the appropriate matrix */ \
+ const UINT8 *dith = &DITHER_LOOKUP[((XX) & 3) << 1]; \
+ \
+ /* apply dithering to R,G,B */ \
+ (RR) = dith[((RR) << 3) + 0]; \
+ (GG) = dith[((GG) << 3) + 1]; \
+ (BB) = dith[((BB) << 3) + 0]; \
+ } \
+ else \
+ { \
+ (RR) >>= 3; \
+ (GG) >>= 2; \
+ (BB) >>= 3; \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Clamping macros
+ *
+ *************************************/
+
+#define CLAMPED_ARGB(ITERR, ITERG, ITERB, ITERA, FBZCP, RESULT) \
+do \
+{ \
+ INT32 r = (INT32)(ITERR) >> 12; \
+ INT32 g = (INT32)(ITERG) >> 12; \
+ INT32 b = (INT32)(ITERB) >> 12; \
+ INT32 a = (INT32)(ITERA) >> 12; \
+ \
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0) \
+ { \
+ r &= 0xfff; \
+ RESULT.rgb.r = r; \
+ if (r == 0xfff) \
+ RESULT.rgb.r = 0; \
+ else if (r == 0x100) \
+ RESULT.rgb.r = 0xff; \
+ \
+ g &= 0xfff; \
+ RESULT.rgb.g = g; \
+ if (g == 0xfff) \
+ RESULT.rgb.g = 0; \
+ else if (g == 0x100) \
+ RESULT.rgb.g = 0xff; \
+ \
+ b &= 0xfff; \
+ RESULT.rgb.b = b; \
+ if (b == 0xfff) \
+ RESULT.rgb.b = 0; \
+ else if (b == 0x100) \
+ RESULT.rgb.b = 0xff; \
+ \
+ a &= 0xfff; \
+ RESULT.rgb.a = a; \
+ if (a == 0xfff) \
+ RESULT.rgb.a = 0; \
+ else if (a == 0x100) \
+ RESULT.rgb.a = 0xff; \
+ } \
+ else \
+ { \
+ RESULT.rgb.r = (r < 0) ? 0 : (r > 0xff) ? 0xff : (UINT8)r; \
+ RESULT.rgb.g = (g < 0) ? 0 : (g > 0xff) ? 0xff : (UINT8)g; \
+ RESULT.rgb.b = (b < 0) ? 0 : (b > 0xff) ? 0xff : (UINT8)b; \
+ RESULT.rgb.a = (a < 0) ? 0 : (a > 0xff) ? 0xff : (UINT8)a; \
+ } \
+} \
+while (0)
+
+
+#define CLAMPED_Z(ITERZ, FBZCP, RESULT) \
+do \
+{ \
+ (RESULT) = (INT32)(ITERZ) >> 12; \
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0) \
+ { \
+ (RESULT) &= 0xfffff; \
+ if ((RESULT) == 0xfffff) \
+ (RESULT) = 0; \
+ else if ((RESULT) == 0x10000) \
+ (RESULT) = 0xffff; \
+ else \
+ (RESULT) &= 0xffff; \
+ } \
+ else \
+ { \
+ CLAMP((RESULT), 0, 0xffff); \
+ } \
+} \
+while (0)
+
+
+#define CLAMPED_W(ITERW, FBZCP, RESULT) \
+do \
+{ \
+ (RESULT) = (INT16)((ITERW) >> 32); \
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0) \
+ { \
+ (RESULT) &= 0xffff; \
+ if ((RESULT) == 0xffff) \
+ (RESULT) = 0; \
+ else if ((RESULT) == 0x100) \
+ (RESULT) = 0xff; \
+ (RESULT) &= 0xff; \
+ } \
+ else \
+ { \
+ CLAMP((RESULT), 0, 0xff); \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Chroma keying macro
+ *
+ *************************************/
+
+#define APPLY_CHROMAKEY(VV, STATS, FBZMODE, COLOR) \
+do \
+{ \
+ if (FBZMODE_ENABLE_CHROMAKEY(FBZMODE)) \
+ { \
+ /* non-range version */ \
+ if (!CHROMARANGE_ENABLE((VV)->reg[chromaRange].u)) \
+ { \
+ if (((COLOR.u ^ (VV)->reg[chromaKey].u) & 0xffffff) == 0) \
+ { \
+ (STATS)->chroma_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ \
+ /* tricky range version */ \
+ else \
+ { \
+ INT32 low, high, test; \
+ int results = 0; \
+ \
+ /* check blue */ \
+ low = (VV)->reg[chromaKey].rgb.b; \
+ high = (VV)->reg[chromaRange].rgb.b; \
+ test = COLOR.rgb.b; \
+ results = (test >= low && test <= high); \
+ results ^= CHROMARANGE_BLUE_EXCLUSIVE((VV)->reg[chromaRange].u); \
+ results <<= 1; \
+ \
+ /* check green */ \
+ low = (VV)->reg[chromaKey].rgb.g; \
+ high = (VV)->reg[chromaRange].rgb.g; \
+ test = COLOR.rgb.g; \
+ results |= (test >= low && test <= high); \
+ results ^= CHROMARANGE_GREEN_EXCLUSIVE((VV)->reg[chromaRange].u); \
+ results <<= 1; \
+ \
+ /* check red */ \
+ low = (VV)->reg[chromaKey].rgb.r; \
+ high = (VV)->reg[chromaRange].rgb.r; \
+ test = COLOR.rgb.r; \
+ results |= (test >= low && test <= high); \
+ results ^= CHROMARANGE_RED_EXCLUSIVE((VV)->reg[chromaRange].u); \
+ \
+ /* final result */ \
+ if (CHROMARANGE_UNION_MODE((VV)->reg[chromaRange].u)) \
+ { \
+ if (results != 0) \
+ { \
+ (STATS)->chroma_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ else \
+ { \
+ if (results == 7) \
+ { \
+ (STATS)->chroma_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ } \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Alpha masking macro
+ *
+ *************************************/
+
+#define APPLY_ALPHAMASK(VV, STATS, FBZMODE, AA) \
+do \
+{ \
+ if (FBZMODE_ENABLE_ALPHA_MASK(FBZMODE)) \
+ { \
+ if (((AA) & 1) == 0) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Alpha testing macro
+ *
+ *************************************/
+
+#define APPLY_ALPHATEST(VV, STATS, ALPHAMODE, AA) \
+do \
+{ \
+ if (ALPHAMODE_ALPHATEST(ALPHAMODE)) \
+ { \
+ UINT8 alpharef = (VV)->reg[alphaMode].rgb.a; \
+ switch (ALPHAMODE_ALPHAFUNCTION(ALPHAMODE)) \
+ { \
+ case 0: /* alphaOP = never */ \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ \
+ case 1: /* alphaOP = less than */ \
+ if ((AA) >= alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 2: /* alphaOP = equal */ \
+ if ((AA) != alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 3: /* alphaOP = less than or equal */ \
+ if ((AA) > alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 4: /* alphaOP = greater than */ \
+ if ((AA) <= alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 5: /* alphaOP = not equal */ \
+ if ((AA) == alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 6: /* alphaOP = greater than or equal */ \
+ if ((AA) < alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 7: /* alphaOP = always */ \
+ break; \
+ } \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Alpha blending macro
+ *
+ *************************************/
+
+#define APPLY_ALPHA_BLEND(FBZMODE, ALPHAMODE, XX, DITHER, RR, GG, BB, AA) \
+do \
+{ \
+ if (ALPHAMODE_ALPHABLEND(ALPHAMODE)) \
+ { \
+ int dpix = dest[XX]; \
+ int dr = (dpix >> 8) & 0xf8; \
+ int dg = (dpix >> 3) & 0xfc; \
+ int db = (dpix << 3) & 0xf8; \
+ int da = (FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) && depth) ? depth[XX] : 0xff; \
+ int sr = (RR); \
+ int sg = (GG); \
+ int sb = (BB); \
+ int sa = (AA); \
+ int ta; \
+ \
+ /* apply dither subtraction */ \
+ if ((FBZMODE_ALPHA_DITHER_SUBTRACT(FBZMODE)) && DITHER) \
+ { \
+ /* look up the dither value from the appropriate matrix */ \
+ int dith = DITHER[(XX) & 3]; \
+ \
+ /* subtract the dither value */ \
+ dr = ((dr << 1) + 15 - dith) >> 1; \
+ dg = ((dg << 2) + 15 - dith) >> 2; \
+ db = ((db << 1) + 15 - dith) >> 1; \
+ } \
+ \
+ /* compute source portion */ \
+ switch (ALPHAMODE_SRCRGBBLEND(ALPHAMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* AZERO */ \
+ (RR) = (GG) = (BB) = 0; \
+ break; \
+ \
+ case 1: /* ASRC_ALPHA */ \
+ (RR) = (sr * (sa + 1)) >> 8; \
+ (GG) = (sg * (sa + 1)) >> 8; \
+ (BB) = (sb * (sa + 1)) >> 8; \
+ break; \
+ \
+ case 2: /* A_COLOR */ \
+ (RR) = (sr * (dr + 1)) >> 8; \
+ (GG) = (sg * (dg + 1)) >> 8; \
+ (BB) = (sb * (db + 1)) >> 8; \
+ break; \
+ \
+ case 3: /* ADST_ALPHA */ \
+ (RR) = (sr * (da + 1)) >> 8; \
+ (GG) = (sg * (da + 1)) >> 8; \
+ (BB) = (sb * (da + 1)) >> 8; \
+ break; \
+ \
+ case 4: /* AONE */ \
+ break; \
+ \
+ case 5: /* AOMSRC_ALPHA */ \
+ (RR) = (sr * (0x100 - sa)) >> 8; \
+ (GG) = (sg * (0x100 - sa)) >> 8; \
+ (BB) = (sb * (0x100 - sa)) >> 8; \
+ break; \
+ \
+ case 6: /* AOM_COLOR */ \
+ (RR) = (sr * (0x100 - dr)) >> 8; \
+ (GG) = (sg * (0x100 - dg)) >> 8; \
+ (BB) = (sb * (0x100 - db)) >> 8; \
+ break; \
+ \
+ case 7: /* AOMDST_ALPHA */ \
+ (RR) = (sr * (0x100 - da)) >> 8; \
+ (GG) = (sg * (0x100 - da)) >> 8; \
+ (BB) = (sb * (0x100 - da)) >> 8; \
+ break; \
+ \
+ case 15: /* ASATURATE */ \
+ ta = (sa < (0x100 - da)) ? sa : (0x100 - da); \
+ (RR) = (sr * (ta + 1)) >> 8; \
+ (GG) = (sg * (ta + 1)) >> 8; \
+ (BB) = (sb * (ta + 1)) >> 8; \
+ break; \
+ } \
+ \
+ /* add in dest portion */ \
+ switch (ALPHAMODE_DSTRGBBLEND(ALPHAMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* AZERO */ \
+ break; \
+ \
+ case 1: /* ASRC_ALPHA */ \
+ (RR) += (dr * (sa + 1)) >> 8; \
+ (GG) += (dg * (sa + 1)) >> 8; \
+ (BB) += (db * (sa + 1)) >> 8; \
+ break; \
+ \
+ case 2: /* A_COLOR */ \
+ (RR) += (dr * (sr + 1)) >> 8; \
+ (GG) += (dg * (sg + 1)) >> 8; \
+ (BB) += (db * (sb + 1)) >> 8; \
+ break; \
+ \
+ case 3: /* ADST_ALPHA */ \
+ (RR) += (dr * (da + 1)) >> 8; \
+ (GG) += (dg * (da + 1)) >> 8; \
+ (BB) += (db * (da + 1)) >> 8; \
+ break; \
+ \
+ case 4: /* AONE */ \
+ (RR) += dr; \
+ (GG) += dg; \
+ (BB) += db; \
+ break; \
+ \
+ case 5: /* AOMSRC_ALPHA */ \
+ (RR) += (dr * (0x100 - sa)) >> 8; \
+ (GG) += (dg * (0x100 - sa)) >> 8; \
+ (BB) += (db * (0x100 - sa)) >> 8; \
+ break; \
+ \
+ case 6: /* AOM_COLOR */ \
+ (RR) += (dr * (0x100 - sr)) >> 8; \
+ (GG) += (dg * (0x100 - sg)) >> 8; \
+ (BB) += (db * (0x100 - sb)) >> 8; \
+ break; \
+ \
+ case 7: /* AOMDST_ALPHA */ \
+ (RR) += (dr * (0x100 - da)) >> 8; \
+ (GG) += (dg * (0x100 - da)) >> 8; \
+ (BB) += (db * (0x100 - da)) >> 8; \
+ break; \
+ \
+ case 15: /* A_COLORBEFOREFOG */ \
+ (RR) += (dr * (prefogr + 1)) >> 8; \
+ (GG) += (dg * (prefogg + 1)) >> 8; \
+ (BB) += (db * (prefogb + 1)) >> 8; \
+ break; \
+ } \
+ \
+ /* blend the source alpha */ \
+ (AA) = 0; \
+ if (ALPHAMODE_SRCALPHABLEND(ALPHAMODE) == 4) \
+ (AA) = sa; \
+ \
+ /* blend the dest alpha */ \
+ if (ALPHAMODE_DSTALPHABLEND(ALPHAMODE) == 4) \
+ (AA) += da; \
+ \
+ /* clamp */ \
+ CLAMP((RR), 0x00, 0xff); \
+ CLAMP((GG), 0x00, 0xff); \
+ CLAMP((BB), 0x00, 0xff); \
+ CLAMP((AA), 0x00, 0xff); \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Fogging macro
+ *
+ *************************************/
+
+#define APPLY_FOGGING(VV, FOGMODE, FBZCP, XX, DITHER4, RR, GG, BB, ITERZ, ITERW, ITERAXXX) \
+do \
+{ \
+ if (FOGMODE_ENABLE_FOG(FOGMODE)) \
+ { \
+ rgb_union fogcolor = (VV)->reg[fogColor]; \
+ INT32 fr, fg, fb; \
+ \
+ /* constant fog bypasses everything else */ \
+ if (FOGMODE_FOG_CONSTANT(FOGMODE)) \
+ { \
+ fr = fogcolor.rgb.r; \
+ fg = fogcolor.rgb.g; \
+ fb = fogcolor.rgb.b; \
+ } \
+ \
+ /* non-constant fog comes from several sources */ \
+ else \
+ { \
+ INT32 fogblend = 0; \
+ \
+ /* if fog_add is zero, we start with the fog color */ \
+ if (FOGMODE_FOG_ADD(FOGMODE) == 0) \
+ { \
+ fr = fogcolor.rgb.r; \
+ fg = fogcolor.rgb.g; \
+ fb = fogcolor.rgb.b; \
+ } \
+ else \
+ fr = fg = fb = 0; \
+ \
+ /* if fog_mult is zero, we subtract the incoming color */ \
+ if (FOGMODE_FOG_MULT(FOGMODE) == 0) \
+ { \
+ fr -= (RR); \
+ fg -= (GG); \
+ fb -= (BB); \
+ } \
+ \
+ /* fog blending mode */ \
+ switch (FOGMODE_FOG_ZALPHA(FOGMODE)) \
+ { \
+ case 0: /* fog table */ \
+ { \
+ INT32 delta = (VV)->fbi.fogdelta[wfloat >> 10]; \
+ INT32 deltaval; \
+ \
+ /* perform the multiply against lower 8 bits of wfloat */ \
+ deltaval = (delta & (VV)->fbi.fogdelta_mask) * \
+ ((wfloat >> 2) & 0xff); \
+ \
+ /* fog zones allow for negating this value */ \
+ if (FOGMODE_FOG_ZONES(FOGMODE) && (delta & 2)) \
+ deltaval = -deltaval; \
+ deltaval >>= 6; \
+ \
+ /* apply dither */ \
+ if (FOGMODE_FOG_DITHER(FOGMODE)) \
+ if (DITHER4) \
+ deltaval += DITHER4[(XX) & 3]; \
+ deltaval >>= 4; \
+ \
+ /* add to the blending factor */ \
+ fogblend = (VV)->fbi.fogblend[wfloat >> 10] + deltaval; \
+ break; \
+ } \
+ \
+ case 1: /* iterated A */ \
+ fogblend = ITERAXXX.rgb.a; \
+ break; \
+ \
+ case 2: /* iterated Z */ \
+ CLAMPED_Z((ITERZ), FBZCP, fogblend); \
+ fogblend >>= 8; \
+ break; \
+ \
+ case 3: /* iterated W - Voodoo 2 only */ \
+ CLAMPED_W((ITERW), FBZCP, fogblend); \
+ break; \
+ } \
+ \
+ /* perform the blend */ \
+ fogblend++; \
+ fr = (fr * fogblend) >> 8; \
+ fg = (fg * fogblend) >> 8; \
+ fb = (fb * fogblend) >> 8; \
+ } \
+ \
+ /* if fog_mult is 0, we add this to the original color */ \
+ if (FOGMODE_FOG_MULT(FOGMODE) == 0) \
+ { \
+ (RR) += fr; \
+ (GG) += fg; \
+ (BB) += fb; \
+ } \
+ \
+ /* otherwise this just becomes the new color */ \
+ else \
+ { \
+ (RR) = fr; \
+ (GG) = fg; \
+ (BB) = fb; \
+ } \
+ \
+ /* clamp */ \
+ CLAMP((RR), 0x00, 0xff); \
+ CLAMP((GG), 0x00, 0xff); \
+ CLAMP((BB), 0x00, 0xff); \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Texture pipeline macro
+ *
+ *************************************/
+
+#define TEXTURE_PIPELINE(TT, XX, DITHER4, TEXMODE, COTHER, LOOKUP, LODBASE, ITERS, ITERT, ITERW, RESULT) \
+do \
+{ \
+ INT32 blendr, blendg, blendb, blenda; \
+ INT32 tr, tg, tb, ta; \
+ INT32 s, t, lod, ilod; \
+ INT64 oow; \
+ INT32 smax, tmax; \
+ UINT32 texbase; \
+ rgb_union c_local; \
+ \
+ /* determine the S/T/LOD values for this texture */ \
+ if (TEXMODE_ENABLE_PERSPECTIVE(TEXMODE)) \
+ { \
+ oow = fast_reciplog((ITERW), &lod); \
+ s = (INT32)((oow * (ITERS)) >> 29); \
+ t = (INT32)((oow * (ITERT)) >> 29); \
+ lod += (LODBASE); \
+ } \
+ else \
+ { \
+ s = (INT32)((ITERS) >> 14); \
+ t = (INT32)((ITERT) >> 14); \
+ lod = (LODBASE); \
+ } \
+ \
+ /* clamp W */ \
+ if (TEXMODE_CLAMP_NEG_W(TEXMODE) && (ITERW) < 0) \
+ s = t = 0; \
+ \
+ /* clamp the LOD */ \
+ lod += (TT)->lodbias; \
+ if (TEXMODE_ENABLE_LOD_DITHER(TEXMODE)) \
+ if (DITHER4) \
+ lod += DITHER4[(XX) & 3] << 4; \
+ if (lod < (TT)->lodmin) \
+ lod = (TT)->lodmin; \
+ if (lod > (TT)->lodmax) \
+ lod = (TT)->lodmax; \
+ \
+ /* now the LOD is in range; if we don't own this LOD, take the next one */ \
+ ilod = lod >> 8; \
+ if (!(((TT)->lodmask >> ilod) & 1)) \
+ ilod++; \
+ \
+ /* fetch the texture base */ \
+ texbase = (TT)->lodoffset[ilod]; \
+ \
+ /* compute the maximum s and t values at this LOD */ \
+ smax = (TT)->wmask >> ilod; \
+ tmax = (TT)->hmask >> ilod; \
+ \
+ /* determine whether we are point-sampled or bilinear */ \
+ if ((lod == (TT)->lodmin && !TEXMODE_MAGNIFICATION_FILTER(TEXMODE)) || \
+ (lod != (TT)->lodmin && !TEXMODE_MINIFICATION_FILTER(TEXMODE))) \
+ { \
+ /* point sampled */ \
+ \
+ UINT32 texel0; \
+ \
+ /* adjust S/T for the LOD and strip off the fractions */ \
+ s >>= ilod + 18; \
+ t >>= ilod + 18; \
+ \
+ /* clamp/wrap S/T if necessary */ \
+ if (TEXMODE_CLAMP_S(TEXMODE)) \
+ CLAMP(s, 0, smax); \
+ if (TEXMODE_CLAMP_T(TEXMODE)) \
+ CLAMP(t, 0, tmax); \
+ s &= smax; \
+ t &= tmax; \
+ t *= smax + 1; \
+ \
+ /* fetch texel data */ \
+ if (TEXMODE_FORMAT(TEXMODE) < 8) \
+ { \
+ texel0 = *(UINT8 *)&(TT)->ram[(texbase + t + s) & (TT)->mask]; \
+ c_local.u = (LOOKUP)[texel0]; \
+ } \
+ else \
+ { \
+ texel0 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s)) & (TT)->mask]; \
+ if (TEXMODE_FORMAT(TEXMODE) >= 10 && TEXMODE_FORMAT(TEXMODE) <= 12) \
+ c_local.u = (LOOKUP)[texel0]; \
+ else \
+ c_local.u = ((LOOKUP)[texel0 & 0xff] & 0xffffff) | \
+ ((texel0 & 0xff00) << 16); \
+ } \
+ } \
+ else \
+ { \
+ /* bilinear filtered */ \
+ \
+ UINT32 texel0, texel1, texel2, texel3; \
+ UINT8 sfrac, tfrac; \
+ INT32 s1, t1; \
+ \
+ /* adjust S/T for the LOD and strip off all but the low 8 bits of */ \
+ /* the fraction */ \
+ s >>= ilod + 10; \
+ t >>= ilod + 10; \
+ \
+ /* also subtract 1/2 texel so that (0.5,0.5) = a full (0,0) texel */ \
+ s -= 0x80; \
+ t -= 0x80; \
+ \
+ /* extract the fractions */ \
+ sfrac = (UINT8)(s & (TT)->bilinear_mask); \
+ tfrac = (UINT8)(t & (TT)->bilinear_mask); \
+ \
+ /* now toss the rest */ \
+ s >>= 8; \
+ t >>= 8; \
+ s1 = s + 1; \
+ t1 = t + 1; \
+ \
+ /* clamp/wrap S/T if necessary */ \
+ if (TEXMODE_CLAMP_S(TEXMODE)) \
+ { \
+ CLAMP(s, 0, smax); \
+ CLAMP(s1, 0, smax); \
+ } \
+ if (TEXMODE_CLAMP_T(TEXMODE)) \
+ { \
+ CLAMP(t, 0, tmax); \
+ CLAMP(t1, 0, tmax); \
+ } \
+ s &= smax; \
+ s1 &= smax; \
+ t &= tmax; \
+ t1 &= tmax; \
+ t *= smax + 1; \
+ t1 *= smax + 1; \
+ \
+ /* fetch texel data */ \
+ if (TEXMODE_FORMAT(TEXMODE) < 8) \
+ { \
+ texel0 = *(UINT8 *)&(TT)->ram[(texbase + t + s) & (TT)->mask]; \
+ texel1 = *(UINT8 *)&(TT)->ram[(texbase + t + s1) & (TT)->mask]; \
+ texel2 = *(UINT8 *)&(TT)->ram[(texbase + t1 + s) & (TT)->mask]; \
+ texel3 = *(UINT8 *)&(TT)->ram[(texbase + t1 + s1) & (TT)->mask]; \
+ texel0 = (LOOKUP)[texel0]; \
+ texel1 = (LOOKUP)[texel1]; \
+ texel2 = (LOOKUP)[texel2]; \
+ texel3 = (LOOKUP)[texel3]; \
+ } \
+ else \
+ { \
+ texel0 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s)) & (TT)->mask]; \
+ texel1 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s1)) & (TT)->mask];\
+ texel2 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t1 + s)) & (TT)->mask];\
+ texel3 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t1 + s1)) & (TT)->mask];\
+ if (TEXMODE_FORMAT(TEXMODE) >= 10 && TEXMODE_FORMAT(TEXMODE) <= 12) \
+ { \
+ texel0 = (LOOKUP)[texel0]; \
+ texel1 = (LOOKUP)[texel1]; \
+ texel2 = (LOOKUP)[texel2]; \
+ texel3 = (LOOKUP)[texel3]; \
+ } \
+ else \
+ { \
+ texel0 = ((LOOKUP)[texel0 & 0xff] & 0xffffff) | \
+ ((texel0 & 0xff00) << 16); \
+ texel1 = ((LOOKUP)[texel1 & 0xff] & 0xffffff) | \
+ ((texel1 & 0xff00) << 16); \
+ texel2 = ((LOOKUP)[texel2 & 0xff] & 0xffffff) | \
+ ((texel2 & 0xff00) << 16); \
+ texel3 = ((LOOKUP)[texel3 & 0xff] & 0xffffff) | \
+ ((texel3 & 0xff00) << 16); \
+ } \
+ } \
+ \
+ /* weigh in each texel */ \
+ c_local.u = rgba_bilinear_filter(texel0, texel1, texel2, texel3, sfrac, tfrac);\
+ } \
+ \
+ /* select zero/other for RGB */ \
+ if (!TEXMODE_TC_ZERO_OTHER(TEXMODE)) \
+ { \
+ tr = COTHER.rgb.r; \
+ tg = COTHER.rgb.g; \
+ tb = COTHER.rgb.b; \
+ } \
+ else \
+ tr = tg = tb = 0; \
+ \
+ /* select zero/other for alpha */ \
+ if (!TEXMODE_TCA_ZERO_OTHER(TEXMODE)) \
+ ta = COTHER.rgb.a; \
+ else \
+ ta = 0; \
+ \
+ /* potentially subtract c_local */ \
+ if (TEXMODE_TC_SUB_CLOCAL(TEXMODE)) \
+ { \
+ tr -= c_local.rgb.r; \
+ tg -= c_local.rgb.g; \
+ tb -= c_local.rgb.b; \
+ } \
+ if (TEXMODE_TCA_SUB_CLOCAL(TEXMODE)) \
+ ta -= c_local.rgb.a; \
+ \
+ /* blend RGB */ \
+ switch (TEXMODE_TC_MSELECT(TEXMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* zero */ \
+ blendr = blendg = blendb = 0; \
+ break; \
+ \
+ case 1: /* c_local */ \
+ blendr = c_local.rgb.r; \
+ blendg = c_local.rgb.g; \
+ blendb = c_local.rgb.b; \
+ break; \
+ \
+ case 2: /* a_other */ \
+ blendr = blendg = blendb = COTHER.rgb.a; \
+ break; \
+ \
+ case 3: /* a_local */ \
+ blendr = blendg = blendb = c_local.rgb.a; \
+ break; \
+ \
+ case 4: /* LOD (detail factor) */ \
+ if ((TT)->detailbias <= lod) \
+ blendr = blendg = blendb = 0; \
+ else \
+ { \
+ blendr = ((((TT)->detailbias - lod) << (TT)->detailscale) >> 8);\
+ if (blendr > (TT)->detailmax) \
+ blendr = (TT)->detailmax; \
+ blendg = blendb = blendr; \
+ } \
+ break; \
+ \
+ case 5: /* LOD fraction */ \
+ blendr = blendg = blendb = lod & 0xff; \
+ break; \
+ } \
+ \
+ /* blend alpha */ \
+ switch (TEXMODE_TCA_MSELECT(TEXMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* zero */ \
+ blenda = 0; \
+ break; \
+ \
+ case 1: /* c_local */ \
+ blenda = c_local.rgb.a; \
+ break; \
+ \
+ case 2: /* a_other */ \
+ blenda = COTHER.rgb.a; \
+ break; \
+ \
+ case 3: /* a_local */ \
+ blenda = c_local.rgb.a; \
+ break; \
+ \
+ case 4: /* LOD (detail factor) */ \
+ if ((TT)->detailbias <= lod) \
+ blenda = 0; \
+ else \
+ { \
+ blenda = ((((TT)->detailbias - lod) << (TT)->detailscale) >> 8);\
+ if (blenda > (TT)->detailmax) \
+ blenda = (TT)->detailmax; \
+ } \
+ break; \
+ \
+ case 5: /* LOD fraction */ \
+ blenda = lod & 0xff; \
+ break; \
+ } \
+ \
+ /* reverse the RGB blend */ \
+ if (!TEXMODE_TC_REVERSE_BLEND(TEXMODE)) \
+ { \
+ blendr ^= 0xff; \
+ blendg ^= 0xff; \
+ blendb ^= 0xff; \
+ } \
+ \
+ /* reverse the alpha blend */ \
+ if (!TEXMODE_TCA_REVERSE_BLEND(TEXMODE)) \
+ blenda ^= 0xff; \
+ \
+ /* do the blend */ \
+ tr = (tr * (blendr + 1)) >> 8; \
+ tg = (tg * (blendg + 1)) >> 8; \
+ tb = (tb * (blendb + 1)) >> 8; \
+ ta = (ta * (blenda + 1)) >> 8; \
+ \
+ /* add clocal or alocal to RGB */ \
+ switch (TEXMODE_TC_ADD_ACLOCAL(TEXMODE)) \
+ { \
+ case 3: /* reserved */ \
+ case 0: /* nothing */ \
+ break; \
+ \
+ case 1: /* add c_local */ \
+ tr += c_local.rgb.r; \
+ tg += c_local.rgb.g; \
+ tb += c_local.rgb.b; \
+ break; \
+ \
+ case 2: /* add_alocal */ \
+ tr += c_local.rgb.a; \
+ tg += c_local.rgb.a; \
+ tb += c_local.rgb.a; \
+ break; \
+ } \
+ \
+ /* add clocal or alocal to alpha */ \
+ if (TEXMODE_TCA_ADD_ACLOCAL(TEXMODE)) \
+ ta += c_local.rgb.a; \
+ \
+ /* clamp */ \
+ RESULT.rgb.r = (tr < 0) ? 0 : (tr > 0xff) ? 0xff : (UINT8)tr; \
+ RESULT.rgb.g = (tg < 0) ? 0 : (tg > 0xff) ? 0xff : (UINT8)tg; \
+ RESULT.rgb.b = (tb < 0) ? 0 : (tb > 0xff) ? 0xff : (UINT8)tb; \
+ RESULT.rgb.a = (ta < 0) ? 0 : (ta > 0xff) ? 0xff : (UINT8)ta; \
+ \
+ /* invert */ \
+ if (TEXMODE_TC_INVERT_OUTPUT(TEXMODE)) \
+ RESULT.u ^= 0x00ffffff; \
+ if (TEXMODE_TCA_INVERT_OUTPUT(TEXMODE)) \
+ RESULT.rgb.a ^= 0xff; \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Pixel pipeline macros
+ *
+ *************************************/
+
+#define PIXEL_PIPELINE_BEGIN(VV, XX, YY, FBZCOLORPATH, FBZMODE, ITERZ, ITERW) \
+do \
+{ \
+ INT32 depthval, wfloat; \
+ INT32 prefogr, prefogg, prefogb; \
+ INT32 r, g, b, a; \
+ \
+ /* apply clipping */ \
+ /* note that for perf reasons, we assume the caller has done clipping */ \
+ \
+ /* handle stippling */ \
+ if (FBZMODE_ENABLE_STIPPLE(FBZMODE)) \
+ { \
+ /* rotate mode */ \
+ if (FBZMODE_STIPPLE_PATTERN(FBZMODE) == 0) \
+ { \
+ (VV)->reg[stipple].u = ((VV)->reg[stipple].u << 1) | ((VV)->reg[stipple].u >> 31);\
+ if (((VV)->reg[stipple].u & 0x80000000) == 0) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ } \
+ \
+ /* pattern mode */ \
+ else \
+ { \
+ int stipple_index = (((YY) & 3) << 3) | (~(XX) & 7); \
+ if ((((VV)->reg[stipple].u >> stipple_index) & 1) == 0) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ } \
+ } \
+ \
+ /* compute "floating point" W value (used for depth and fog) */ \
+ if ((ITERW) & LONGTYPE(0xffff00000000)) \
+ wfloat = 0x0000; \
+ else \
+ { \
+ UINT32 temp = (UINT32)(ITERW); \
+ if ((temp & 0xffff0000) == 0) \
+ wfloat = 0xffff; \
+ else \
+ { \
+ int exp = count_leading_zeros(temp); \
+ wfloat = ((exp << 12) | ((~temp >> (19 - exp)) & 0xfff)); \
+ if (wfloat < 0xffff) wfloat++; \
+ } \
+ } \
+ \
+ /* compute depth value (W or Z) for this pixel */ \
+ if (FBZMODE_WBUFFER_SELECT(FBZMODE) == 0) \
+ CLAMPED_Z(ITERZ, FBZCOLORPATH, depthval); \
+ else if (FBZMODE_DEPTH_FLOAT_SELECT(FBZMODE) == 0) \
+ depthval = wfloat; \
+ else \
+ { \
+ if ((ITERZ) & 0xf0000000) \
+ depthval = 0x0000; \
+ else \
+ { \
+ UINT32 temp = (ITERZ) << 4; \
+ if ((temp & 0xffff0000) == 0) \
+ depthval = 0xffff; \
+ else \
+ { \
+ int exp = count_leading_zeros(temp); \
+ depthval = ((exp << 12) | ((~temp >> (19 - exp)) & 0xfff)); \
+ if (depthval < 0xffff) depthval++; \
+ } \
+ } \
+ } \
+ \
+ /* add the bias */ \
+ if (FBZMODE_ENABLE_DEPTH_BIAS(FBZMODE)) \
+ { \
+ depthval += (INT16)(VV)->reg[zaColor].u; \
+ CLAMP(depthval, 0, 0xffff); \
+ } \
+ \
+ /* handle depth buffer testing */ \
+ if (FBZMODE_ENABLE_DEPTHBUF(FBZMODE)) \
+ { \
+ INT32 depthsource; \
+ \
+ /* the source depth is either the iterated W/Z+bias or a */ \
+ /* constant value */ \
+ if (FBZMODE_DEPTH_SOURCE_COMPARE(FBZMODE) == 0) \
+ depthsource = depthval; \
+ else \
+ depthsource = (UINT16)(VV)->reg[zaColor].u; \
+ \
+ /* test against the depth buffer */ \
+ switch (FBZMODE_DEPTH_FUNCTION(FBZMODE)) \
+ { \
+ case 0: /* depthOP = never */ \
+ goto skipdrawdepth; \
+ \
+ case 1: /* depthOP = less than */ \
+ if (depth) \
+ if (depthsource >= depth[XX]) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 2: /* depthOP = equal */ \
+ if (depth) \
+ if (depthsource != depth[XX]) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 3: /* depthOP = less than or equal */ \
+ if (depth) \
+ if (depthsource > depth[XX]) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 4: /* depthOP = greater than */ \
+ if (depth) \
+ if (depthsource <= depth[XX]) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 5: /* depthOP = not equal */ \
+ if (depth) \
+ if (depthsource == depth[XX]) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 6: /* depthOP = greater than or equal */ \
+ if (depth) \
+ if (depthsource < depth[XX]) \
+ { \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 7: /* depthOP = always */ \
+ break; \
+ } \
+ }
+
+
+#define PIXEL_PIPELINE_MODIFY(VV, DITHER, DITHER4, XX, FBZMODE, FBZCOLORPATH, ALPHAMODE, FOGMODE, ITERZ, ITERW, ITERAXXX) \
+ \
+ /* perform fogging */ \
+ prefogr = r; \
+ prefogg = g; \
+ prefogb = b; \
+ APPLY_FOGGING(VV, FOGMODE, FBZCOLORPATH, XX, DITHER4, r, g, b, \
+ ITERZ, ITERW, ITERAXXX); \
+ \
+ /* perform alpha blending */ \
+ APPLY_ALPHA_BLEND(FBZMODE, ALPHAMODE, XX, DITHER, r, g, b, a);
+
+
+#define PIXEL_PIPELINE_FINISH(VV, DITHER_LOOKUP, XX, dest, depth, FBZMODE) \
+ \
+ /* write to framebuffer */ \
+ if (FBZMODE_RGB_BUFFER_MASK(FBZMODE)) \
+ { \
+ /* apply dithering */ \
+ APPLY_DITHER(FBZMODE, XX, DITHER_LOOKUP, r, g, b); \
+ dest[XX] = (UINT16)((r << 11) | (g << 5) | b); \
+ } \
+ \
+ /* write to aux buffer */ \
+ if (depth && FBZMODE_AUX_BUFFER_MASK(FBZMODE)) \
+ { \
+ if (FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) == 0) \
+ depth[XX] = (UINT16)depthval; \
+ else \
+ depth[XX] = (UINT16)a; \
+ }
+
+#define PIXEL_PIPELINE_END(STATS) \
+ \
+ /* track pixel writes to the frame buffer regardless of mask */ \
+ (STATS)->pixels_out++; \
+ \
+skipdrawdepth: \
+ ; \
+} \
+while (0)
+
+#endif
diff --git a/src/hardware/voodoo_def.h b/src/hardware/voodoo_def.h
new file mode 100644
index 000000000..90cd7019f
--- /dev/null
+++ b/src/hardware/voodoo_def.h
@@ -0,0 +1,559 @@
+/***************************************************************************/
+/* Portion of this software comes with the following license: */
+/***************************************************************************/
+/*
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*************************************************************************
+
+ 3dfx Voodoo Graphics SST-1/2 emulator
+
+ emulator by Aaron Giles
+
+**************************************************************************/
+
+
+#ifndef DOSBOX_VOODOO_DEF_H
+#define DOSBOX_VOODOO_DEF_H
+
+
+/*************************************
+ *
+ * Register constants
+ *
+ *************************************/
+
+/* Codes to the right:
+ R = readable
+ W = writeable
+ P = pipelined
+ F = goes to FIFO
+*/
+
+/* 0x000 */
+#define status (0x000/4) /* R P */
+#define intrCtrl (0x004/4) /* RW P -- Voodoo2/Banshee only */
+#define vertexAx (0x008/4) /* W PF */
+#define vertexAy (0x00c/4) /* W PF */
+#define vertexBx (0x010/4) /* W PF */
+#define vertexBy (0x014/4) /* W PF */
+#define vertexCx (0x018/4) /* W PF */
+#define vertexCy (0x01c/4) /* W PF */
+#define startR (0x020/4) /* W PF */
+#define startG (0x024/4) /* W PF */
+#define startB (0x028/4) /* W PF */
+#define startZ (0x02c/4) /* W PF */
+#define startA (0x030/4) /* W PF */
+#define startS (0x034/4) /* W PF */
+#define startT (0x038/4) /* W PF */
+#define startW (0x03c/4) /* W PF */
+
+/* 0x040 */
+#define dRdX (0x040/4) /* W PF */
+#define dGdX (0x044/4) /* W PF */
+#define dBdX (0x048/4) /* W PF */
+#define dZdX (0x04c/4) /* W PF */
+#define dAdX (0x050/4) /* W PF */
+#define dSdX (0x054/4) /* W PF */
+#define dTdX (0x058/4) /* W PF */
+#define dWdX (0x05c/4) /* W PF */
+#define dRdY (0x060/4) /* W PF */
+#define dGdY (0x064/4) /* W PF */
+#define dBdY (0x068/4) /* W PF */
+#define dZdY (0x06c/4) /* W PF */
+#define dAdY (0x070/4) /* W PF */
+#define dSdY (0x074/4) /* W PF */
+#define dTdY (0x078/4) /* W PF */
+#define dWdY (0x07c/4) /* W PF */
+
+/* 0x080 */
+#define triangleCMD (0x080/4) /* W PF */
+#define fvertexAx (0x088/4) /* W PF */
+#define fvertexAy (0x08c/4) /* W PF */
+#define fvertexBx (0x090/4) /* W PF */
+#define fvertexBy (0x094/4) /* W PF */
+#define fvertexCx (0x098/4) /* W PF */
+#define fvertexCy (0x09c/4) /* W PF */
+#define fstartR (0x0a0/4) /* W PF */
+#define fstartG (0x0a4/4) /* W PF */
+#define fstartB (0x0a8/4) /* W PF */
+#define fstartZ (0x0ac/4) /* W PF */
+#define fstartA (0x0b0/4) /* W PF */
+#define fstartS (0x0b4/4) /* W PF */
+#define fstartT (0x0b8/4) /* W PF */
+#define fstartW (0x0bc/4) /* W PF */
+
+/* 0x0c0 */
+#define fdRdX (0x0c0/4) /* W PF */
+#define fdGdX (0x0c4/4) /* W PF */
+#define fdBdX (0x0c8/4) /* W PF */
+#define fdZdX (0x0cc/4) /* W PF */
+#define fdAdX (0x0d0/4) /* W PF */
+#define fdSdX (0x0d4/4) /* W PF */
+#define fdTdX (0x0d8/4) /* W PF */
+#define fdWdX (0x0dc/4) /* W PF */
+#define fdRdY (0x0e0/4) /* W PF */
+#define fdGdY (0x0e4/4) /* W PF */
+#define fdBdY (0x0e8/4) /* W PF */
+#define fdZdY (0x0ec/4) /* W PF */
+#define fdAdY (0x0f0/4) /* W PF */
+#define fdSdY (0x0f4/4) /* W PF */
+#define fdTdY (0x0f8/4) /* W PF */
+#define fdWdY (0x0fc/4) /* W PF */
+
+/* 0x100 */
+#define ftriangleCMD (0x100/4) /* W PF */
+#define fbzColorPath (0x104/4) /* RW PF */
+#define fogMode (0x108/4) /* RW PF */
+#define alphaMode (0x10c/4) /* RW PF */
+#define fbzMode (0x110/4) /* RW F */
+#define lfbMode (0x114/4) /* RW F */
+#define clipLeftRight (0x118/4) /* RW F */
+#define clipLowYHighY (0x11c/4) /* RW F */
+#define nopCMD (0x120/4) /* W F */
+#define fastfillCMD (0x124/4) /* W F */
+#define swapbufferCMD (0x128/4) /* W F */
+#define fogColor (0x12c/4) /* W F */
+#define zaColor (0x130/4) /* W F */
+#define chromaKey (0x134/4) /* W F */
+#define chromaRange (0x138/4) /* W F -- Voodoo2/Banshee only */
+#define userIntrCMD (0x13c/4) /* W F -- Voodoo2/Banshee only */
+
+/* 0x140 */
+#define stipple (0x140/4) /* RW F */
+#define color0 (0x144/4) /* RW F */
+#define color1 (0x148/4) /* RW F */
+#define fbiPixelsIn (0x14c/4) /* R */
+#define fbiChromaFail (0x150/4) /* R */
+#define fbiZfuncFail (0x154/4) /* R */
+#define fbiAfuncFail (0x158/4) /* R */
+#define fbiPixelsOut (0x15c/4) /* R */
+#define fogTable (0x160/4) /* W F */
+
+/* 0x1c0 */
+#define cmdFifoBaseAddr (0x1e0/4) /* RW -- Voodoo2 only */
+#define cmdFifoBump (0x1e4/4) /* RW -- Voodoo2 only */
+#define cmdFifoRdPtr (0x1e8/4) /* RW -- Voodoo2 only */
+#define cmdFifoAMin (0x1ec/4) /* RW -- Voodoo2 only */
+#define colBufferAddr (0x1ec/4) /* RW -- Banshee only */
+#define cmdFifoAMax (0x1f0/4) /* RW -- Voodoo2 only */
+#define colBufferStride (0x1f0/4) /* RW -- Banshee only */
+#define cmdFifoDepth (0x1f4/4) /* RW -- Voodoo2 only */
+#define auxBufferAddr (0x1f4/4) /* RW -- Banshee only */
+#define cmdFifoHoles (0x1f8/4) /* RW -- Voodoo2 only */
+#define auxBufferStride (0x1f8/4) /* RW -- Banshee only */
+
+/* 0x200 */
+#define fbiInit4 (0x200/4) /* RW -- Voodoo/Voodoo2 only */
+#define clipLeftRight1 (0x200/4) /* RW -- Banshee only */
+#define vRetrace (0x204/4) /* R -- Voodoo/Voodoo2 only */
+#define clipTopBottom1 (0x204/4) /* RW -- Banshee only */
+#define backPorch (0x208/4) /* RW -- Voodoo/Voodoo2 only */
+#define videoDimensions (0x20c/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit0 (0x210/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit1 (0x214/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit2 (0x218/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit3 (0x21c/4) /* RW -- Voodoo/Voodoo2 only */
+#define hSync (0x220/4) /* W -- Voodoo/Voodoo2 only */
+#define vSync (0x224/4) /* W -- Voodoo/Voodoo2 only */
+#define clutData (0x228/4) /* W F -- Voodoo/Voodoo2 only */
+#define dacData (0x22c/4) /* W -- Voodoo/Voodoo2 only */
+#define maxRgbDelta (0x230/4) /* W -- Voodoo/Voodoo2 only */
+#define hBorder (0x234/4) /* W -- Voodoo2 only */
+#define vBorder (0x238/4) /* W -- Voodoo2 only */
+#define borderColor (0x23c/4) /* W -- Voodoo2 only */
+
+/* 0x240 */
+#define hvRetrace (0x240/4) /* R -- Voodoo2 only */
+#define fbiInit5 (0x244/4) /* RW -- Voodoo2 only */
+#define fbiInit6 (0x248/4) /* RW -- Voodoo2 only */
+#define fbiInit7 (0x24c/4) /* RW -- Voodoo2 only */
+#define swapPending (0x24c/4) /* W -- Banshee only */
+#define leftOverlayBuf (0x250/4) /* W -- Banshee only */
+#define rightOverlayBuf (0x254/4) /* W -- Banshee only */
+#define fbiSwapHistory (0x258/4) /* R -- Voodoo2/Banshee only */
+#define fbiTrianglesOut (0x25c/4) /* R -- Voodoo2/Banshee only */
+#define sSetupMode (0x260/4) /* W PF -- Voodoo2/Banshee only */
+#define sVx (0x264/4) /* W PF -- Voodoo2/Banshee only */
+#define sVy (0x268/4) /* W PF -- Voodoo2/Banshee only */
+#define sARGB (0x26c/4) /* W PF -- Voodoo2/Banshee only */
+#define sRed (0x270/4) /* W PF -- Voodoo2/Banshee only */
+#define sGreen (0x274/4) /* W PF -- Voodoo2/Banshee only */
+#define sBlue (0x278/4) /* W PF -- Voodoo2/Banshee only */
+#define sAlpha (0x27c/4) /* W PF -- Voodoo2/Banshee only */
+
+/* 0x280 */
+#define sVz (0x280/4) /* W PF -- Voodoo2/Banshee only */
+#define sWb (0x284/4) /* W PF -- Voodoo2/Banshee only */
+#define sWtmu0 (0x288/4) /* W PF -- Voodoo2/Banshee only */
+#define sS_W0 (0x28c/4) /* W PF -- Voodoo2/Banshee only */
+#define sT_W0 (0x290/4) /* W PF -- Voodoo2/Banshee only */
+#define sWtmu1 (0x294/4) /* W PF -- Voodoo2/Banshee only */
+#define sS_Wtmu1 (0x298/4) /* W PF -- Voodoo2/Banshee only */
+#define sT_Wtmu1 (0x29c/4) /* W PF -- Voodoo2/Banshee only */
+#define sDrawTriCMD (0x2a0/4) /* W PF -- Voodoo2/Banshee only */
+#define sBeginTriCMD (0x2a4/4) /* W PF -- Voodoo2/Banshee only */
+
+/* 0x2c0 */
+#define bltSrcBaseAddr (0x2c0/4) /* RW PF -- Voodoo2 only */
+#define bltDstBaseAddr (0x2c4/4) /* RW PF -- Voodoo2 only */
+#define bltXYStrides (0x2c8/4) /* RW PF -- Voodoo2 only */
+#define bltSrcChromaRange (0x2cc/4) /* RW PF -- Voodoo2 only */
+#define bltDstChromaRange (0x2d0/4) /* RW PF -- Voodoo2 only */
+#define bltClipX (0x2d4/4) /* RW PF -- Voodoo2 only */
+#define bltClipY (0x2d8/4) /* RW PF -- Voodoo2 only */
+#define bltSrcXY (0x2e0/4) /* RW PF -- Voodoo2 only */
+#define bltDstXY (0x2e4/4) /* RW PF -- Voodoo2 only */
+#define bltSize (0x2e8/4) /* RW PF -- Voodoo2 only */
+#define bltRop (0x2ec/4) /* RW PF -- Voodoo2 only */
+#define bltColor (0x2f0/4) /* RW PF -- Voodoo2 only */
+#define bltCommand (0x2f8/4) /* RW PF -- Voodoo2 only */
+#define bltData (0x2fc/4) /* W PF -- Voodoo2 only */
+
+/* 0x300 */
+#define textureMode (0x300/4) /* W PF */
+#define tLOD (0x304/4) /* W PF */
+#define tDetail (0x308/4) /* W PF */
+#define texBaseAddr (0x30c/4) /* W PF */
+#define texBaseAddr_1 (0x310/4) /* W PF */
+#define texBaseAddr_2 (0x314/4) /* W PF */
+#define texBaseAddr_3_8 (0x318/4) /* W PF */
+#define trexInit0 (0x31c/4) /* W F -- Voodoo/Voodoo2 only */
+#define trexInit1 (0x320/4) /* W F */
+#define nccTable (0x324/4) /* W F */
+
+
+
+/*************************************
+ *
+ * Alias map of the first 64
+ * registers when remapped
+ *
+ *************************************/
+
+static const UINT8 register_alias_map[0x40] =
+{
+ status, 0x004/4, vertexAx, vertexAy,
+ vertexBx, vertexBy, vertexCx, vertexCy,
+ startR, dRdX, dRdY, startG,
+ dGdX, dGdY, startB, dBdX,
+ dBdY, startZ, dZdX, dZdY,
+ startA, dAdX, dAdY, startS,
+ dSdX, dSdY, startT, dTdX,
+ dTdY, startW, dWdX, dWdY,
+
+ triangleCMD,0x084/4, fvertexAx, fvertexAy,
+ fvertexBx, fvertexBy, fvertexCx, fvertexCy,
+ fstartR, fdRdX, fdRdY, fstartG,
+ fdGdX, fdGdY, fstartB, fdBdX,
+ fdBdY, fstartZ, fdZdX, fdZdY,
+ fstartA, fdAdX, fdAdY, fstartS,
+ fdSdX, fdSdY, fstartT, fdTdX,
+ fdTdY, fstartW, fdWdX, fdWdY
+};
+
+
+
+/*************************************
+ *
+ * Table of per-register access rights
+ *
+ *************************************/
+
+static const UINT8 voodoo_register_access[0x100] =
+{
+ /* 0x000 */
+ REG_RP, 0, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x040 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x080 */
+ REG_WPF, 0, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x0c0 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x100 */
+ REG_WPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWF, REG_RWF, REG_RWF, REG_RWF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, 0, 0,
+
+ /* 0x140 */
+ REG_RWF, REG_RWF, REG_RWF, REG_R,
+ REG_R, REG_R, REG_R, REG_R,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x180 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x1c0 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x200 */
+ REG_RW, REG_R, REG_RW, REG_RW,
+ REG_RW, REG_RW, REG_RW, REG_RW,
+ REG_W, REG_W, REG_W, REG_W,
+ REG_W, 0, 0, 0,
+
+ /* 0x240 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x280 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x2c0 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x300 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x340 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x380 */
+ REG_WF
+};
+
+
+static const UINT8 voodoo2_register_access[0x100] =
+{
+ /* 0x000 */
+ REG_RP, REG_RWPT, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x040 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x080 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x0c0 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x100 */
+ REG_WPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWF, REG_RWF, REG_RWF, REG_RWF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x140 */
+ REG_RWF, REG_RWF, REG_RWF, REG_R,
+ REG_R, REG_R, REG_R, REG_R,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x180 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x1c0 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_RWT, REG_RWT, REG_RWT, REG_RWT,
+ REG_RWT, REG_RWT, REG_RWT, REG_RW,
+
+ /* 0x200 */
+ REG_RWT, REG_R, REG_RWT, REG_RWT,
+ REG_RWT, REG_RWT, REG_RWT, REG_RWT,
+ REG_WT, REG_WT, REG_WF, REG_WT,
+ REG_WT, REG_WT, REG_WT, REG_WT,
+
+ /* 0x240 */
+ REG_R, REG_RWT, REG_RWT, REG_RWT,
+ 0, 0, REG_R, REG_R,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x280 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x2c0 */
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_WPF,
+
+ /* 0x300 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x340 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x380 */
+ REG_WF
+};
+
+
+
+/*************************************
+ *
+ * Register string table for debug
+ *
+ *************************************/
+
+static const char *const voodoo_reg_name[] =
+{
+ /* 0x000 */
+ "status", "{intrCtrl}", "vertexAx", "vertexAy",
+ "vertexBx", "vertexBy", "vertexCx", "vertexCy",
+ "startR", "startG", "startB", "startZ",
+ "startA", "startS", "startT", "startW",
+ /* 0x040 */
+ "dRdX", "dGdX", "dBdX", "dZdX",
+ "dAdX", "dSdX", "dTdX", "dWdX",
+ "dRdY", "dGdY", "dBdY", "dZdY",
+ "dAdY", "dSdY", "dTdY", "dWdY",
+ /* 0x080 */
+ "triangleCMD", "reserved084", "fvertexAx", "fvertexAy",
+ "fvertexBx", "fvertexBy", "fvertexCx", "fvertexCy",
+ "fstartR", "fstartG", "fstartB", "fstartZ",
+ "fstartA", "fstartS", "fstartT", "fstartW",
+ /* 0x0c0 */
+ "fdRdX", "fdGdX", "fdBdX", "fdZdX",
+ "fdAdX", "fdSdX", "fdTdX", "fdWdX",
+ "fdRdY", "fdGdY", "fdBdY", "fdZdY",
+ "fdAdY", "fdSdY", "fdTdY", "fdWdY",
+ /* 0x100 */
+ "ftriangleCMD", "fbzColorPath", "fogMode", "alphaMode",
+ "fbzMode", "lfbMode", "clipLeftRight","clipLowYHighY",
+ "nopCMD", "fastfillCMD", "swapbufferCMD","fogColor",
+ "zaColor", "chromaKey", "{chromaRange}","{userIntrCMD}",
+ /* 0x140 */
+ "stipple", "color0", "color1", "fbiPixelsIn",
+ "fbiChromaFail","fbiZfuncFail", "fbiAfuncFail", "fbiPixelsOut",
+ "fogTable160", "fogTable164", "fogTable168", "fogTable16c",
+ "fogTable170", "fogTable174", "fogTable178", "fogTable17c",
+ /* 0x180 */
+ "fogTable180", "fogTable184", "fogTable188", "fogTable18c",
+ "fogTable190", "fogTable194", "fogTable198", "fogTable19c",
+ "fogTable1a0", "fogTable1a4", "fogTable1a8", "fogTable1ac",
+ "fogTable1b0", "fogTable1b4", "fogTable1b8", "fogTable1bc",
+ /* 0x1c0 */
+ "fogTable1c0", "fogTable1c4", "fogTable1c8", "fogTable1cc",
+ "fogTable1d0", "fogTable1d4", "fogTable1d8", "fogTable1dc",
+ "{cmdFifoBaseAddr}","{cmdFifoBump}","{cmdFifoRdPtr}","{cmdFifoAMin}",
+ "{cmdFifoAMax}","{cmdFifoDepth}","{cmdFifoHoles}","reserved1fc",
+ /* 0x200 */
+ "fbiInit4", "vRetrace", "backPorch", "videoDimensions",
+ "fbiInit0", "fbiInit1", "fbiInit2", "fbiInit3",
+ "hSync", "vSync", "clutData", "dacData",
+ "maxRgbDelta", "{hBorder}", "{vBorder}", "{borderColor}",
+ /* 0x240 */
+ "{hvRetrace}", "{fbiInit5}", "{fbiInit6}", "{fbiInit7}",
+ "reserved250", "reserved254", "{fbiSwapHistory}","{fbiTrianglesOut}",
+ "{sSetupMode}", "{sVx}", "{sVy}", "{sARGB}",
+ "{sRed}", "{sGreen}", "{sBlue}", "{sAlpha}",
+ /* 0x280 */
+ "{sVz}", "{sWb}", "{sWtmu0}", "{sS/Wtmu0}",
+ "{sT/Wtmu0}", "{sWtmu1}", "{sS/Wtmu1}", "{sT/Wtmu1}",
+ "{sDrawTriCMD}","{sBeginTriCMD}","reserved2a8", "reserved2ac",
+ "reserved2b0", "reserved2b4", "reserved2b8", "reserved2bc",
+ /* 0x2c0 */
+ "{bltSrcBaseAddr}","{bltDstBaseAddr}","{bltXYStrides}","{bltSrcChromaRange}",
+ "{bltDstChromaRange}","{bltClipX}","{bltClipY}","reserved2dc",
+ "{bltSrcXY}", "{bltDstXY}", "{bltSize}", "{bltRop}",
+ "{bltColor}", "reserved2f4", "{bltCommand}", "{bltData}",
+ /* 0x300 */
+ "textureMode", "tLOD", "tDetail", "texBaseAddr",
+ "texBaseAddr_1","texBaseAddr_2","texBaseAddr_3_8","trexInit0",
+ "trexInit1", "nccTable0.0", "nccTable0.1", "nccTable0.2",
+ "nccTable0.3", "nccTable0.4", "nccTable0.5", "nccTable0.6",
+ /* 0x340 */
+ "nccTable0.7", "nccTable0.8", "nccTable0.9", "nccTable0.A",
+ "nccTable0.B", "nccTable1.0", "nccTable1.1", "nccTable1.2",
+ "nccTable1.3", "nccTable1.4", "nccTable1.5", "nccTable1.6",
+ "nccTable1.7", "nccTable1.8", "nccTable1.9", "nccTable1.A",
+ /* 0x380 */
+ "nccTable1.B"
+};
+
+
+#endif
diff --git a/src/hardware/voodoo_emu.cpp b/src/hardware/voodoo_emu.cpp
new file mode 100644
index 000000000..4445cc2e0
--- /dev/null
+++ b/src/hardware/voodoo_emu.cpp
@@ -0,0 +1,3758 @@
+/***************************************************************************/
+/* Portion of this software comes with the following license: */
+/***************************************************************************/
+/*
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+ 3dfx Voodoo Graphics SST-1/2 emulator
+
+ emulator by Aaron Giles
+
+ --------------------------
+
+ Specs:
+
+ Voodoo 1 (SST1):
+ 2,4MB frame buffer RAM
+ 1,2,4MB texture RAM
+ 50MHz clock frequency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ 64 entry PCI FIFO
+ memory FIFO up to 65536 entries
+
+ Voodoo 2:
+ 2,4MB frame buffer RAM
+ 2,4,8,16MB texture RAM
+ 90MHz clock frquency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ ultrafast clears @ 16 pixels/clock
+ 128 entry PCI FIFO
+ memory FIFO up to 65536 entries
+
+ --------------------------
+
+
+iterated RGBA = 12.12 [24 bits]
+iterated Z = 20.12 [32 bits]
+iterated W = 18.32 [48 bits]
+
+**************************************************************************/
+
+
+#include <stdlib.h>
+#include <math.h>
+#include <cstring>
+
+#include "dosbox.h"
+#include "cross.h"
+
+#include "voodoo_emu.h"
+#include "voodoo_opengl.h"
+
+#include "voodoo_def.h"
+
+
+voodoo_state *v;
+
+
+#define LOG_VBLANK_SWAP (0)
+#define LOG_REGISTERS (0)
+#define LOG_LFB (0)
+#define LOG_TEXTURE_RAM (0)
+#define LOG_RASTERIZERS (0)
+
+
+/* fast dither lookup */
+static UINT8 dither4_lookup[256*16*2];
+static UINT8 dither2_lookup[256*16*2];
+
+/* fast reciprocal+log2 lookup */
+UINT32 voodoo_reciplog[(2 << RECIPLOG_LOOKUP_BITS) + 2];
+
+
+
+/*************************************
+ *
+ * Prototypes
+ *
+ *************************************/
+
+/* command handlers */
+static void fastfill(voodoo_state *v);
+static void swapbuffer(voodoo_state *v, UINT32 data);
+static void triangle(voodoo_state *v);
+static void begin_triangle(voodoo_state *v);
+static void draw_triangle(voodoo_state *v);
+
+/* triangle helpers */
+static void setup_and_draw_triangle(voodoo_state *v);
+static void triangle_create_work_item(voodoo_state *v, UINT16 *drawbuf, int texcount);
+
+/* rasterizer management */
+static raster_info *add_rasterizer(voodoo_state *v, const raster_info *cinfo);
+static raster_info *find_rasterizer(voodoo_state *v, int texcount);
+
+/* generic rasterizers */
+static void raster_fastfill(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata);
+
+
+/***************************************************************************
+ RASTERIZER MANAGEMENT
+***************************************************************************/
+
+void raster_generic(UINT32 TMUS, UINT32 TEXMODE0, UINT32 TEXMODE1, void *destbase,
+ INT32 y, const poly_extent *extent, const void *extradata)
+{
+ const poly_extra_data *extra = (const poly_extra_data *)extradata;
+ voodoo_state *v = extra->state;
+ stats_block *stats = &v->thread_stats[0];
+ DECLARE_DITHER_POINTERS;
+ INT32 startx = extent->startx;
+ INT32 stopx = extent->stopx;
+ INT32 iterr, iterg, iterb, itera;
+ INT32 iterz;
+ INT64 iterw, iterw0 = 0, iterw1 = 0;
+ INT64 iters0 = 0, iters1 = 0;
+ INT64 itert0 = 0, itert1 = 0;
+ UINT16 *depth;
+ UINT16 *dest;
+ INT32 dx, dy;
+ INT32 scry;
+ INT32 x;
+
+ /* determine the screen Y */
+ scry = y;
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* compute the dithering pointers */
+ if (FBZMODE_ENABLE_DITHERING(v->reg[fbzMode].u))
+ {
+ dither4 = &dither_matrix_4x4[(y & 3) * 4];
+ if (FBZMODE_DITHER_TYPE(v->reg[fbzMode].u) == 0)
+ {
+ dither = dither4;
+ dither_lookup = &dither4_lookup[(y & 3) << 11];
+ }
+ else
+ {
+ dither = &dither_matrix_2x2[(y & 3) * 4];
+ dither_lookup = &dither2_lookup[(y & 3) << 11];
+ }
+ }
+
+ /* apply clipping */
+ if (FBZMODE_ENABLE_CLIPPING(v->reg[fbzMode].u))
+ {
+ INT32 tempclip;
+
+ /* Y clipping buys us the whole scanline */
+ if (scry < (INT32)((v->reg[clipLowYHighY].u >> 16) & 0x3ff) ||
+ scry >= (INT32)(v->reg[clipLowYHighY].u & 0x3ff))
+ {
+ stats->pixels_in += stopx - startx;
+ stats->clip_fail += stopx - startx;
+ return;
+ }
+
+ /* X clipping */
+ tempclip = (v->reg[clipLeftRight].u >> 16) & 0x3ff;
+ if (startx < tempclip)
+ {
+ stats->pixels_in += tempclip - startx;
+ startx = tempclip;
+ }
+ tempclip = v->reg[clipLeftRight].u & 0x3ff;
+ if (stopx >= tempclip)
+ {
+ stats->pixels_in += stopx - tempclip;
+ stopx = tempclip - 1;
+ }
+ }
+
+ /* get pointers to the target buffer and depth buffer */
+ dest = (UINT16 *)destbase + scry * v->fbi.rowpixels;
+ depth = (v->fbi.auxoffs != (UINT32)(~0)) ? ((UINT16 *)(v->fbi.ram + v->fbi.auxoffs) + scry * v->fbi.rowpixels) : NULL;
+
+ /* compute the starting parameters */
+ dx = startx - (extra->ax >> 4);
+ dy = y - (extra->ay >> 4);
+ iterr = extra->startr + dy * extra->drdy + dx * extra->drdx;
+ iterg = extra->startg + dy * extra->dgdy + dx * extra->dgdx;
+ iterb = extra->startb + dy * extra->dbdy + dx * extra->dbdx;
+ itera = extra->starta + dy * extra->dady + dx * extra->dadx;
+ iterz = extra->startz + dy * extra->dzdy + dx * extra->dzdx;
+ iterw = extra->startw + dy * extra->dwdy + dx * extra->dwdx;
+ if (TMUS >= 1)
+ {
+ iterw0 = extra->startw0 + dy * extra->dw0dy + dx * extra->dw0dx;
+ iters0 = extra->starts0 + dy * extra->ds0dy + dx * extra->ds0dx;
+ itert0 = extra->startt0 + dy * extra->dt0dy + dx * extra->dt0dx;
+ }
+ if (TMUS >= 2)
+ {
+ iterw1 = extra->startw1 + dy * extra->dw1dy + dx * extra->dw1dx;
+ iters1 = extra->starts1 + dy * extra->ds1dy + dx * extra->ds1dx;
+ itert1 = extra->startt1 + dy * extra->dt1dy + dx * extra->dt1dx;
+ }
+
+ /* loop in X */
+ for (x = startx; x < stopx; x++)
+ {
+ rgb_union iterargb = { 0 };
+ rgb_union texel = { 0 };
+
+ /* pixel pipeline part 1 handles depth testing and stippling */
+ PIXEL_PIPELINE_BEGIN(v, x, y, v->reg[fbzColorPath].u, v->reg[fbzMode].u, iterz, iterw);
+
+ /* run the texture pipeline on TMU1 to produce a value in texel */
+ /* note that they set LOD min to 8 to "disable" a TMU */
+
+ if (TMUS >= 2 && v->tmu[1].lodmin < (8 << 8))
+ TEXTURE_PIPELINE(&v->tmu[1], x, dither4, TEXMODE1, texel,
+ v->tmu[1].lookup, extra->lodbase1,
+ iters1, itert1, iterw1, texel);
+
+ /* run the texture pipeline on TMU0 to produce a final */
+ /* result in texel */
+ /* note that they set LOD min to 8 to "disable" a TMU */
+ if (TMUS >= 1 && v->tmu[0].lodmin < (8 << 8)) {
+ if (!v->send_config) {
+ TEXTURE_PIPELINE(&v->tmu[0], x, dither4, TEXMODE0, texel,
+ v->tmu[0].lookup, extra->lodbase0,
+ iters0, itert0, iterw0, texel);
+ } else { /* send config data to the frame buffer */
+ texel.u=v->tmu_config;
+ }
+ }
+
+ /* colorpath pipeline selects source colors and does blending */
+ CLAMPED_ARGB(iterr, iterg, iterb, itera, v->reg[fbzColorPath].u, iterargb);
+
+
+ INT32 blendr, blendg, blendb, blenda;
+ rgb_union c_other;
+ rgb_union c_local;
+
+ /* compute c_other */
+ switch (FBZCP_CC_RGBSELECT(v->reg[fbzColorPath].u))
+ {
+ case 0: /* iterated RGB */
+ c_other.u = iterargb.u;
+ break;
+ case 1: /* texture RGB */
+ c_other.u = texel.u;
+ break;
+ case 2: /* color1 RGB */
+ c_other.u = v->reg[color1].u;
+ break;
+ default: /* reserved */
+ c_other.u = 0;
+ break;
+ }
+
+ /* handle chroma key */
+ APPLY_CHROMAKEY(v, stats, v->reg[fbzMode].u, c_other);
+
+ /* compute a_other */
+ switch (FBZCP_CC_ASELECT(v->reg[fbzColorPath].u))
+ {
+ case 0: /* iterated alpha */
+ c_other.rgb.a = iterargb.rgb.a;
+ break;
+ case 1: /* texture alpha */
+ c_other.rgb.a = texel.rgb.a;
+ break;
+ case 2: /* color1 alpha */
+ c_other.rgb.a = v->reg[color1].rgb.a;
+ break;
+ default: /* reserved */
+ c_other.rgb.a = 0;
+ break;
+ }
+
+ /* handle alpha mask */
+ APPLY_ALPHAMASK(v, stats, v->reg[fbzMode].u, c_other.rgb.a);
+
+ /* handle alpha test */
+ APPLY_ALPHATEST(v, stats, v->reg[alphaMode].u, c_other.rgb.a);
+
+ /* compute c_local */
+ if (FBZCP_CC_LOCALSELECT_OVERRIDE(v->reg[fbzColorPath].u) == 0)
+ {
+ if (FBZCP_CC_LOCALSELECT(v->reg[fbzColorPath].u) == 0) /* iterated RGB */
+ c_local.u = iterargb.u;
+ else /* color0 RGB */
+ c_local.u = v->reg[color0].u;
+ }
+ else
+ {
+ if (!(texel.rgb.a & 0x80)) /* iterated RGB */
+ c_local.u = iterargb.u;
+ else /* color0 RGB */
+ c_local.u = v->reg[color0].u;
+ }
+
+ /* compute a_local */
+ switch (FBZCP_CCA_LOCALSELECT(v->reg[fbzColorPath].u))
+ {
+ default:
+ case 0: /* iterated alpha */
+ c_local.rgb.a = iterargb.rgb.a;
+ break;
+ case 1: /* color0 alpha */
+ c_local.rgb.a = v->reg[color0].rgb.a;
+ break;
+ case 2: /* clamped iterated Z[27:20] */
+ {
+ int temp;
+ CLAMPED_Z(iterz, v->reg[fbzColorPath].u, temp);
+ c_local.rgb.a = (UINT8)temp;
+ break;
+ }
+ case 3: /* clamped iterated W[39:32] */
+ {
+ int temp;
+ CLAMPED_W(iterw, v->reg[fbzColorPath].u, temp); /* Voodoo 2 only */
+ c_local.rgb.a = (UINT8)temp;
+ break;
+ }
+ }
+
+ /* select zero or c_other */
+ if (FBZCP_CC_ZERO_OTHER(v->reg[fbzColorPath].u) == 0)
+ {
+ r = c_other.rgb.r;
+ g = c_other.rgb.g;
+ b = c_other.rgb.b;
+ }
+ else
+ r = g = b = 0;
+
+ /* select zero or a_other */
+ if (FBZCP_CCA_ZERO_OTHER(v->reg[fbzColorPath].u) == 0)
+ a = c_other.rgb.a;
+ else
+ a = 0;
+
+ /* subtract c_local */
+ if (FBZCP_CC_SUB_CLOCAL(v->reg[fbzColorPath].u))
+ {
+ r -= c_local.rgb.r;
+ g -= c_local.rgb.g;
+ b -= c_local.rgb.b;
+ }
+
+ /* subtract a_local */
+ if (FBZCP_CCA_SUB_CLOCAL(v->reg[fbzColorPath].u))
+ a -= c_local.rgb.a;
+
+ /* blend RGB */
+ switch (FBZCP_CC_MSELECT(v->reg[fbzColorPath].u))
+ {
+ default: /* reserved */
+ case 0: /* 0 */
+ blendr = blendg = blendb = 0;
+ break;
+ case 1: /* c_local */
+ blendr = c_local.rgb.r;
+ blendg = c_local.rgb.g;
+ blendb = c_local.rgb.b;
+ break;
+ case 2: /* a_other */
+ blendr = blendg = blendb = c_other.rgb.a;
+ break;
+ case 3: /* a_local */
+ blendr = blendg = blendb = c_local.rgb.a;
+ break;
+ case 4: /* texture alpha */
+ blendr = blendg = blendb = texel.rgb.a;
+ break;
+ case 5: /* texture RGB (Voodoo 2 only) */
+ blendr = texel.rgb.r;
+ blendg = texel.rgb.g;
+ blendb = texel.rgb.b;
+ break;
+ }
+
+ /* blend alpha */
+ switch (FBZCP_CCA_MSELECT(v->reg[fbzColorPath].u))
+ {
+ default: /* reserved */
+ case 0: /* 0 */
+ blenda = 0;
+ break;
+ case 1: /* a_local */
+ blenda = c_local.rgb.a;
+ break;
+ case 2: /* a_other */
+ blenda = c_other.rgb.a;
+ break;
+ case 3: /* a_local */
+ blenda = c_local.rgb.a;
+ break;
+ case 4: /* texture alpha */
+ blenda = texel.rgb.a;
+ break;
+ }
+
+ /* reverse the RGB blend */
+ if (!FBZCP_CC_REVERSE_BLEND(v->reg[fbzColorPath].u))
+ {
+ blendr ^= 0xff;
+ blendg ^= 0xff;
+ blendb ^= 0xff;
+ }
+
+ /* reverse the alpha blend */
+ if (!FBZCP_CCA_REVERSE_BLEND(v->reg[fbzColorPath].u))
+ blenda ^= 0xff;
+
+ /* do the blend */
+ r = (r * (blendr + 1)) >> 8;
+ g = (g * (blendg + 1)) >> 8;
+ b = (b * (blendb + 1)) >> 8;
+ a = (a * (blenda + 1)) >> 8;
+
+ /* add clocal or alocal to RGB */
+ switch (FBZCP_CC_ADD_ACLOCAL(v->reg[fbzColorPath].u))
+ {
+ case 3: /* reserved */
+ case 0: /* nothing */
+ break;
+ case 1: /* add c_local */
+ r += c_local.rgb.r;
+ g += c_local.rgb.g;
+ b += c_local.rgb.b;
+ break;
+ case 2: /* add_alocal */
+ r += c_local.rgb.a;
+ g += c_local.rgb.a;
+ b += c_local.rgb.a;
+ break;
+ }
+
+ /* add clocal or alocal to alpha */
+ if (FBZCP_CCA_ADD_ACLOCAL(v->reg[fbzColorPath].u))
+ a += c_local.rgb.a;
+
+ /* clamp */
+ CLAMP(r, 0x00, 0xff);
+ CLAMP(g, 0x00, 0xff);
+ CLAMP(b, 0x00, 0xff);
+ CLAMP(a, 0x00, 0xff);
+
+ /* invert */
+ if (FBZCP_CC_INVERT_OUTPUT(v->reg[fbzColorPath].u))
+ {
+ r ^= 0xff;
+ g ^= 0xff;
+ b ^= 0xff;
+ }
+ if (FBZCP_CCA_INVERT_OUTPUT(v->reg[fbzColorPath].u))
+ a ^= 0xff;
+
+
+ /* pixel pipeline part 2 handles fog, alpha, and final output */
+ PIXEL_PIPELINE_MODIFY(v, dither, dither4, x,
+ v->reg[fbzMode].u, v->reg[fbzColorPath].u, v->reg[alphaMode].u, v->reg[fogMode].u,
+ iterz, iterw, iterargb);
+ PIXEL_PIPELINE_FINISH(v, dither_lookup, x, dest, depth, v->reg[fbzMode].u);
+ PIXEL_PIPELINE_END(stats);
+
+ /* update the iterated parameters */
+ iterr += extra->drdx;
+ iterg += extra->dgdx;
+ iterb += extra->dbdx;
+ itera += extra->dadx;
+ iterz += extra->dzdx;
+ iterw += extra->dwdx;
+ if (TMUS >= 1)
+ {
+ iterw0 += extra->dw0dx;
+ iters0 += extra->ds0dx;
+ itert0 += extra->dt0dx;
+ }
+ if (TMUS >= 2)
+ {
+ iterw1 += extra->dw1dx;
+ iters1 += extra->ds1dx;
+ itert1 += extra->dt1dx;
+ }
+ }
+}
+
+
+/***************************************************************************
+ RASTERIZER MANAGEMENT
+***************************************************************************/
+
+void raster_generic_0tmu(void *destbase, INT32 y, const poly_extent *extent, const void *extradata) {
+ raster_generic(0, 0, 0, destbase, y, extent, extradata);
+}
+
+void raster_generic_1tmu(void *destbase, INT32 y, const poly_extent *extent, const void *extradata) {
+ raster_generic(1, v->tmu[0].reg[textureMode].u, 0, destbase, y, extent, extradata);
+}
+
+void raster_generic_2tmu(void *destbase, INT32 y, const poly_extent *extent, const void *extradata) {
+ raster_generic(2, v->tmu[0].reg[textureMode].u, v->tmu[1].reg[textureMode].u, destbase, y, extent, extradata);
+}
+
+
+
+/*************************************
+ *
+ * Common initialization
+ *
+ *************************************/
+
+void init_fbi(voodoo_state *v, fbi_state *f, int fbmem)
+{
+ if (fbmem <= 1) E_Exit("VOODOO: invalid frame buffer memory size requested");
+ /* allocate frame buffer RAM and set pointers */
+ f->ram = (UINT8*)malloc(fbmem);
+ f->mask = (UINT32)(fbmem - 1);
+ f->rgboffs[0] = f->rgboffs[1] = f->rgboffs[2] = 0;
+ f->auxoffs = (UINT32)(~0);
+
+ /* default to 0x0 */
+ f->frontbuf = 0;
+ f->backbuf = 1;
+ f->width = 640;
+ f->height = 480;
+// f->xoffs = 0;
+// f->yoffs = 0;
+
+// f->vsyncscan = 0;
+
+ /* init the pens */
+/* for (UINT8 pen = 0; pen < 32; pen++)
+ v->fbi.clut[pen] = MAKE_ARGB(pen, pal5bit(pen), pal5bit(pen), pal5bit(pen));
+ v->fbi.clut[32] = MAKE_ARGB(32,0xff,0xff,0xff); */
+
+ /* allocate a VBLANK timer */
+ f->vblank = false;
+
+ /* initialize the memory FIFO */
+ f->fifo.size = 0;
+
+ /* set the fog delta mask */
+ f->fogdelta_mask = (v->type < VOODOO_2) ? 0xff : 0xfc;
+
+ f->yorigin = 0;
+
+ f->sverts = 0;
+
+ memset(&f->lfb_stats, 0, sizeof(f->lfb_stats));
+ memset(&f->fogblend, 0, sizeof(f->fogblend));
+ memset(&f->fogdelta, 0, sizeof(f->fogdelta));
+}
+
+
+void init_tmu_shared(tmu_shared_state *s)
+{
+ int val;
+
+ /* build static 8-bit texel tables */
+ for (val = 0; val < 256; val++)
+ {
+ int r, g, b, a;
+
+ /* 8-bit RGB (3-3-2) */
+ EXTRACT_332_TO_888(val, r, g, b);
+ s->rgb332[val] = MAKE_ARGB(0xff, r, g, b);
+
+ /* 8-bit alpha */
+ s->alpha8[val] = MAKE_ARGB(val, val, val, val);
+
+ /* 8-bit intensity */
+ s->int8[val] = MAKE_ARGB(0xff, val, val, val);
+
+ /* 8-bit alpha, intensity */
+ a = ((val >> 0) & 0xf0) | ((val >> 4) & 0x0f);
+ r = ((val << 4) & 0xf0) | ((val << 0) & 0x0f);
+ s->ai44[val] = MAKE_ARGB(a, r, r, r);
+ }
+
+ /* build static 16-bit texel tables */
+ for (val = 0; val < 65536; val++)
+ {
+ int r, g, b, a;
+
+ /* table 10 = 16-bit RGB (5-6-5) */
+ EXTRACT_565_TO_888(val, r, g, b);
+ s->rgb565[val] = MAKE_ARGB(0xff, r, g, b);
+
+ /* table 11 = 16 ARGB (1-5-5-5) */
+ EXTRACT_1555_TO_8888(val, a, r, g, b);
+ s->argb1555[val] = MAKE_ARGB(a, r, g, b);
+
+ /* table 12 = 16-bit ARGB (4-4-4-4) */
+ EXTRACT_4444_TO_8888(val, a, r, g, b);
+ s->argb4444[val] = MAKE_ARGB(a, r, g, b);
+ }
+}
+
+
+void init_tmu(voodoo_state *v, tmu_state *t, voodoo_reg *reg, int tmem)
+{
+ if (tmem <= 1) E_Exit("VOODOO: invalid texture buffer memory size requested");
+ /* allocate texture RAM */
+ t->ram = (UINT8*)malloc(tmem);
+ t->mask = (UINT32)(tmem - 1);
+ t->reg = reg;
+ t->regdirty = true;
+ t->bilinear_mask = (v->type >= VOODOO_2) ? 0xff : 0xf0;
+
+ /* mark the NCC tables dirty and configure their registers */
+ t->ncc[0].dirty = t->ncc[1].dirty = true;
+ t->ncc[0].reg = &t->reg[nccTable+0];
+ t->ncc[1].reg = &t->reg[nccTable+12];
+
+ /* create pointers to all the tables */
+ t->texel[0] = v->tmushare.rgb332;
+ t->texel[1] = t->ncc[0].texel;
+ t->texel[2] = v->tmushare.alpha8;
+ t->texel[3] = v->tmushare.int8;
+ t->texel[4] = v->tmushare.ai44;
+ t->texel[5] = t->palette;
+ t->texel[6] = (v->type >= VOODOO_2) ? t->palettea : NULL;
+ t->texel[7] = NULL;
+ t->texel[8] = v->tmushare.rgb332;
+ t->texel[9] = t->ncc[0].texel;
+ t->texel[10] = v->tmushare.rgb565;
+ t->texel[11] = v->tmushare.argb1555;
+ t->texel[12] = v->tmushare.argb4444;
+ t->texel[13] = v->tmushare.int8;
+ t->texel[14] = t->palette;
+ t->texel[15] = NULL;
+ t->lookup = t->texel[0];
+
+ /* attach the palette to NCC table 0 */
+ t->ncc[0].palette = t->palette;
+ if (v->type >= VOODOO_2)
+ t->ncc[0].palettea = t->palettea;
+
+ /* set up texture address calculations */
+ t->texaddr_mask = 0x0fffff;
+ t->texaddr_shift = 3;
+
+ t->lodmin=0;
+ t->lodmax=0;
+}
+
+
+/*************************************
+ *
+ * VBLANK management
+ *
+ *************************************/
+
+void voodoo_swap_buffers(voodoo_state *v)
+{
+// if (LOG_VBLANK_SWAP) LOG(LOG_VOODOO,LOG_WARN)("--- swap_buffers @ %d\n", video_screen_get_vpos(v->screen));
+
+ if (v->ogl && v->active) {
+ voodoo_ogl_swap_buffer();
+ return;
+ }
+
+ /* keep a history of swap intervals */
+ v->reg[fbiSwapHistory].u = (v->reg[fbiSwapHistory].u << 4);
+
+ /* rotate the buffers */
+ if (v->type < VOODOO_2 || !v->fbi.vblank_dont_swap)
+ {
+ if (v->fbi.rgboffs[2] == (UINT32)(~0))
+ {
+ v->fbi.frontbuf = (UINT8)(1 - v->fbi.frontbuf);
+ v->fbi.backbuf = (UINT8)(1 - v->fbi.frontbuf);
+ }
+ else
+ {
+ v->fbi.frontbuf = (v->fbi.frontbuf + 1) % 3;
+ v->fbi.backbuf = (v->fbi.frontbuf + 1) % 3;
+ }
+ }
+}
+
+
+
+/*************************************
+ *
+ * Chip reset
+ *
+ *************************************/
+
+void reset_counters(voodoo_state *v)
+{
+ v->reg[fbiPixelsIn].u = 0;
+ v->reg[fbiChromaFail].u = 0;
+ v->reg[fbiZfuncFail].u = 0;
+ v->reg[fbiAfuncFail].u = 0;
+ v->reg[fbiPixelsOut].u = 0;
+}
+
+
+void soft_reset(voodoo_state *v)
+{
+ reset_counters(v);
+ v->reg[fbiTrianglesOut].u = 0;
+}
+
+
+
+/*************************************
+ *
+ * Recompute video memory layout
+ *
+ *************************************/
+
+void recompute_video_memory(voodoo_state *v)
+{
+ UINT32 buffer_pages = FBIINIT2_VIDEO_BUFFER_OFFSET(v->reg[fbiInit2].u);
+ UINT32 fifo_start_page = FBIINIT4_MEMORY_FIFO_START_ROW(v->reg[fbiInit4].u);
+ UINT32 fifo_last_page = FBIINIT4_MEMORY_FIFO_STOP_ROW(v->reg[fbiInit4].u);
+ UINT32 memory_config;
+ int buf;
+
+ /* memory config is determined differently between V1 and V2 */
+ memory_config = FBIINIT2_ENABLE_TRIPLE_BUF(v->reg[fbiInit2].u);
+ if (v->type == VOODOO_2 && memory_config == 0)
+ memory_config = FBIINIT5_BUFFER_ALLOCATION(v->reg[fbiInit5].u);
+
+ /* tiles are 64x16/32; x_tiles specifies how many half-tiles */
+ v->fbi.tile_width = (v->type < VOODOO_2) ? 64 : 32;
+ v->fbi.tile_height = (v->type < VOODOO_2) ? 16 : 32;
+ v->fbi.x_tiles = FBIINIT1_X_VIDEO_TILES(v->reg[fbiInit1].u);
+ if (v->type == VOODOO_2)
+ {
+ v->fbi.x_tiles = (v->fbi.x_tiles << 1) |
+ (FBIINIT1_X_VIDEO_TILES_BIT5(v->reg[fbiInit1].u) << 5) |
+ (FBIINIT6_X_VIDEO_TILES_BIT0(v->reg[fbiInit6].u));
+ }
+ v->fbi.rowpixels = v->fbi.tile_width * v->fbi.x_tiles;
+
+// logerror("VOODOO.%d.VIDMEM: buffer_pages=%X fifo=%X-%X tiles=%X rowpix=%d\n", v->index, buffer_pages, fifo_start_page, fifo_last_page, v->fbi.x_tiles, v->fbi.rowpixels);
+
+ /* first RGB buffer always starts at 0 */
+ v->fbi.rgboffs[0] = 0;
+
+ /* second RGB buffer starts immediately afterwards */
+ v->fbi.rgboffs[1] = buffer_pages * 0x1000;
+
+ /* remaining buffers are based on the config */
+ switch (memory_config)
+ {
+ case 3: /* reserved */
+ LOG(LOG_VOODOO,LOG_WARN)("VOODOO.ERROR:Unexpected memory configuration in recompute_video_memory!\n");
+ [[fallthrough]];
+
+ case 0: /* 2 color buffers, 1 aux buffer */
+ v->fbi.rgboffs[2] = (UINT32)(~0);
+ v->fbi.auxoffs = 2 * buffer_pages * 0x1000;
+ break;
+
+ case 1: /* 3 color buffers, 0 aux buffers */
+ v->fbi.rgboffs[2] = 2 * buffer_pages * 0x1000;
+ v->fbi.auxoffs = (UINT32)(~0);
+ break;
+
+ case 2: /* 3 color buffers, 1 aux buffers */
+ v->fbi.rgboffs[2] = 2 * buffer_pages * 0x1000;
+ v->fbi.auxoffs = 3 * buffer_pages * 0x1000;
+ break;
+ }
+
+ /* clamp the RGB buffers to video memory */
+ for (buf = 0; buf < 3; buf++)
+ if (v->fbi.rgboffs[buf] != (UINT32)(~0) && v->fbi.rgboffs[buf] > v->fbi.mask)
+ v->fbi.rgboffs[buf] = v->fbi.mask;
+
+ /* clamp the aux buffer to video memory */
+ if (v->fbi.auxoffs != (UINT32)(~0) && v->fbi.auxoffs > v->fbi.mask)
+ v->fbi.auxoffs = v->fbi.mask;
+
+ /* compute the memory FIFO location and size */
+ if (fifo_last_page > v->fbi.mask / 0x1000)
+ fifo_last_page = v->fbi.mask / 0x1000;
+
+ /* is it valid and enabled? */
+ if (fifo_start_page <= fifo_last_page && FBIINIT0_ENABLE_MEMORY_FIFO(v->reg[fbiInit0].u))
+ {
+ v->fbi.fifo.size = (fifo_last_page + 1 - fifo_start_page) * 0x1000 / 4;
+ if (v->fbi.fifo.size > 65536*2)
+ v->fbi.fifo.size = 65536*2;
+ }
+ else /* if not, disable the FIFO */
+ {
+ v->fbi.fifo.size = 0;
+ }
+
+ /* reset our front/back buffers if they are out of range */
+ if (v->fbi.rgboffs[2] == (UINT32)(~0))
+ {
+ if (v->fbi.frontbuf == 2)
+ v->fbi.frontbuf = 0;
+ if (v->fbi.backbuf == 2)
+ v->fbi.backbuf = 0;
+ }
+}
+
+
+
+/*************************************
+ *
+ * NCC table management
+ *
+ *************************************/
+
+static bool palette_changed = false;
+
+void ncc_table_write(ncc_table *n, UINT32 regnum, UINT32 data)
+{
+ /* I/Q entries reference the palette if the high bit is set */
+ if (regnum >= 4 && (data & 0x80000000) && n->palette)
+ {
+ UINT32 index = ((data >> 23) & 0xfe) | (regnum & 1);
+
+ rgb_t palette_entry = 0xff000000 | data;
+
+ if (n->palette[index] != palette_entry) {
+ /* set the ARGB for this palette index */
+ n->palette[index] = palette_entry;
+ palette_changed = true;
+ }
+
+ /* if we have an ARGB palette as well, compute its value */
+ if (n->palettea)
+ {
+ UINT32 a = ((data >> 16) & 0xfc) | ((data >> 22) & 0x03);
+ UINT32 r = ((data >> 10) & 0xfc) | ((data >> 16) & 0x03);
+ UINT32 g = ((data >> 4) & 0xfc) | ((data >> 10) & 0x03);
+ UINT32 b = ((data << 2) & 0xfc) | ((data >> 4) & 0x03);
+ n->palettea[index] = MAKE_ARGB(a, r, g, b);
+ }
+
+ /* this doesn't dirty the table or go to the registers, so bail */
+ return;
+ }
+
+ /* if the register matches, don't update */
+ if (data == n->reg[regnum].u)
+ return;
+ n->reg[regnum].u = data;
+
+ /* first four entries are packed Y values */
+ if (regnum < 4)
+ {
+ regnum *= 4;
+ n->y[regnum+0] = (data >> 0) & 0xff;
+ n->y[regnum+1] = (data >> 8) & 0xff;
+ n->y[regnum+2] = (data >> 16) & 0xff;
+ n->y[regnum+3] = (data >> 24) & 0xff;
+ }
+
+ /* the second four entries are the I RGB values */
+ else if (regnum < 8)
+ {
+ regnum &= 3;
+ n->ir[regnum] = (INT32)(data << 5) >> 23;
+ n->ig[regnum] = (INT32)(data << 14) >> 23;
+ n->ib[regnum] = (INT32)(data << 23) >> 23;
+ }
+
+ /* the final four entries are the Q RGB values */
+ else
+ {
+ regnum &= 3;
+ n->qr[regnum] = (INT32)(data << 5) >> 23;
+ n->qg[regnum] = (INT32)(data << 14) >> 23;
+ n->qb[regnum] = (INT32)(data << 23) >> 23;
+ }
+
+ /* mark the table dirty */
+ n->dirty = true;
+}
+
+
+void ncc_table_update(ncc_table *n)
+{
+ int r, g, b, i;
+
+ /* generte all 256 possibilities */
+ for (i = 0; i < 256; i++)
+ {
+ int vi = (i >> 2) & 0x03;
+ int vq = (i >> 0) & 0x03;
+
+ /* start with the intensity */
+ r = g = b = n->y[(i >> 4) & 0x0f];
+
+ /* add the coloring */
+ r += n->ir[vi] + n->qr[vq];
+ g += n->ig[vi] + n->qg[vq];
+ b += n->ib[vi] + n->qb[vq];
+
+ /* clamp */
+ CLAMP(r, 0, 255);
+ CLAMP(g, 0, 255);
+ CLAMP(b, 0, 255);
+
+ /* fill in the table */
+ n->texel[i] = MAKE_ARGB(0xff, r, g, b);
+ }
+
+ /* no longer dirty */
+ n->dirty = false;
+}
+
+
+
+/*************************************
+ *
+ * Faux DAC implementation
+ *
+ *************************************/
+
+void dacdata_w(dac_state *d, UINT8 regnum, UINT8 data)
+{
+ d->reg[regnum] = data;
+}
+
+
+void dacdata_r(dac_state *d, UINT8 regnum)
+{
+ UINT8 result = 0xff;
+
+ /* switch off the DAC register requested */
+ switch (regnum)
+ {
+ case 5:
+ /* this is just to make startup happy */
+ switch (d->reg[7])
+ {
+ case 0x01: result = 0x55; break;
+ case 0x07: result = 0x71; break;
+ case 0x0b: result = 0x79; break;
+ }
+ break;
+
+ default:
+ result = d->reg[regnum];
+ break;
+ }
+
+ /* remember the read result; it is fetched elsewhere */
+ d->read_result = result;
+}
+
+
+
+/*************************************
+ *
+ * Texuture parameter computation
+ *
+ *************************************/
+
+void recompute_texture_params(tmu_state *t)
+{
+ int bppscale;
+ UINT32 base;
+ int lod;
+
+ /* extract LOD parameters */
+ t->lodmin = TEXLOD_LODMIN(t->reg[tLOD].u) << 6;
+ t->lodmax = TEXLOD_LODMAX(t->reg[tLOD].u) << 6;
+ t->lodbias = (INT8)(TEXLOD_LODBIAS(t->reg[tLOD].u) << 2) << 4;
+
+ /* determine which LODs are present */
+ t->lodmask = 0x1ff;
+ if (TEXLOD_LOD_TSPLIT(t->reg[tLOD].u))
+ {
+ if (!TEXLOD_LOD_ODD(t->reg[tLOD].u))
+ t->lodmask = 0x155;
+ else
+ t->lodmask = 0x0aa;
+ }
+
+ /* determine base texture width/height */
+ t->wmask = t->hmask = 0xff;
+ if (TEXLOD_LOD_S_IS_WIDER(t->reg[tLOD].u))
+ t->hmask >>= TEXLOD_LOD_ASPECT(t->reg[tLOD].u);
+ else
+ t->wmask >>= TEXLOD_LOD_ASPECT(t->reg[tLOD].u);
+
+ /* determine the bpp of the texture */
+ bppscale = TEXMODE_FORMAT(t->reg[textureMode].u) >> 3;
+
+ /* start with the base of LOD 0 */
+ if (t->texaddr_shift == 0 && (t->reg[texBaseAddr].u & 1))
+ LOG(LOG_VOODOO,LOG_WARN)("Tiled texture\n");
+ base = (t->reg[texBaseAddr].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[0] = base & t->mask;
+
+ /* LODs 1-3 are different depending on whether we are in multitex mode */
+ /* Several Voodoo 2 games leave the upper bits of TLOD == 0xff, meaning we think */
+ /* they want multitex mode when they really don't -- disable for now */
+ if (0)//TEXLOD_TMULTIBASEADDR(t->reg[tLOD].u))
+ {
+ base = (t->reg[texBaseAddr_1].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[1] = base & t->mask;
+ base = (t->reg[texBaseAddr_2].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[2] = base & t->mask;
+ base = (t->reg[texBaseAddr_3_8].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[3] = base & t->mask;
+ }
+ else
+ {
+ if (t->lodmask & (1 << 0))
+ base += (((t->wmask >> 0) + 1) * ((t->hmask >> 0) + 1)) << bppscale;
+ t->lodoffset[1] = base & t->mask;
+ if (t->lodmask & (1 << 1))
+ base += (((t->wmask >> 1) + 1) * ((t->hmask >> 1) + 1)) << bppscale;
+ t->lodoffset[2] = base & t->mask;
+ if (t->lodmask & (1 << 2))
+ base += (((t->wmask >> 2) + 1) * ((t->hmask >> 2) + 1)) << bppscale;
+ t->lodoffset[3] = base & t->mask;
+ }
+
+ /* remaining LODs make sense */
+ for (lod = 4; lod <= 8; lod++)
+ {
+ if (t->lodmask & (1 << (lod - 1)))
+ {
+ UINT32 size = ((t->wmask >> (lod - 1)) + 1) * ((t->hmask >> (lod - 1)) + 1);
+ if (size < 4) size = 4;
+ base += size << bppscale;
+ }
+ t->lodoffset[lod] = base & t->mask;
+ }
+
+ /* set the NCC lookup appropriately */
+ t->texel[1] = t->texel[9] = t->ncc[TEXMODE_NCC_TABLE_SELECT(t->reg[textureMode].u)].texel;
+
+ /* pick the lookup table */
+ t->lookup = t->texel[TEXMODE_FORMAT(t->reg[textureMode].u)];
+
+ /* compute the detail parameters */
+ t->detailmax = TEXDETAIL_DETAIL_MAX(t->reg[tDetail].u);
+ t->detailbias = (INT8)(TEXDETAIL_DETAIL_BIAS(t->reg[tDetail].u) << 2) << 6;
+ t->detailscale = TEXDETAIL_DETAIL_SCALE(t->reg[tDetail].u);
+
+ /* no longer dirty */
+ t->regdirty = false;
+
+ /* check for separate RGBA filtering */
+ if (TEXDETAIL_SEPARATE_RGBA_FILTER(t->reg[tDetail].u))
+ E_Exit("Separate RGBA filters!");
+}
+
+
+INLINE INT32 prepare_tmu(tmu_state *t)
+{
+ INT64 texdx, texdy;
+ INT32 lodbase;
+
+ /* if the texture parameters are dirty, update them */
+ if (t->regdirty)
+ {
+ recompute_texture_params(t);
+
+ /* ensure that the NCC tables are up to date */
+ if ((TEXMODE_FORMAT(t->reg[textureMode].u) & 7) == 1)
+ {
+ ncc_table *n = &t->ncc[TEXMODE_NCC_TABLE_SELECT(t->reg[textureMode].u)];
+ t->texel[1] = t->texel[9] = n->texel;
+ if (n->dirty)
+ ncc_table_update(n);
+ }
+ }
+
+ /* compute (ds^2 + dt^2) in both X and Y as 28.36 numbers */
+ texdx = (INT64)(t->dsdx >> 14) * (INT64)(t->dsdx >> 14) + (INT64)(t->dtdx >> 14) * (INT64)(t->dtdx >> 14);
+ texdy = (INT64)(t->dsdy >> 14) * (INT64)(t->dsdy >> 14) + (INT64)(t->dtdy >> 14) * (INT64)(t->dtdy >> 14);
+
+ /* pick whichever is larger and shift off some high bits -> 28.20 */
+ if (texdx < texdy)
+ texdx = texdy;
+ texdx >>= 16;
+
+ /* use our fast reciprocal/log on this value; it expects input as a */
+ /* 16.32 number, and returns the log of the reciprocal, so we have to */
+ /* adjust the result: negative to get the log of the original value */
+ /* plus 12 to account for the extra exponent, and divided by 2 to */
+ /* get the log of the square root of texdx */
+ (void)fast_reciplog(texdx, &lodbase);
+ return (-lodbase + (12 << 8)) / 2;
+}
+
+
+INLINE INT32 round_coordinate(float value)
+{
+ INT32 result = (INT32)floor(value);
+ return result + (value - (float)result > 0.5f);
+}
+
+void poly_render_triangle(void *dest, poly_draw_scanline_func callback, const poly_vertex *v1, const poly_vertex *v2, const poly_vertex *v3, poly_extra_data *extra)
+{
+ float dxdy_v1v2, dxdy_v1v3, dxdy_v2v3;
+ const poly_vertex *tv;
+ INT32 curscan, scaninc=1;
+
+ INT32 v1yclip, v3yclip;
+ INT32 v1y, v3y;
+
+ /* first sort by Y */
+ if (v2->y < v1->y)
+ {
+ tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+ if (v3->y < v2->y)
+ {
+ tv = v2;
+ v2 = v3;
+ v3 = tv;
+ if (v2->y < v1->y)
+ {
+ tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+ }
+
+ /* compute some integral X/Y vertex values */
+ //v1x = round_coordinate(v1->x);
+ v1y = round_coordinate(v1->y);
+ v3y = round_coordinate(v3->y);
+
+ /* clip coordinates */
+ v1yclip = v1y;
+ v3yclip = v3y;// + ((poly->flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ if (v3yclip - v1yclip <= 0)
+ return;
+
+ /* compute the slopes for each portion of the triangle */
+ dxdy_v1v2 = (v2->y == v1->y) ? 0.0f : (v2->x - v1->x) / (v2->y - v1->y);
+ dxdy_v1v3 = (v3->y == v1->y) ? 0.0f : (v3->x - v1->x) / (v3->y - v1->y);
+ dxdy_v2v3 = (v3->y == v2->y) ? 0.0f : (v3->x - v2->x) / (v3->y - v2->y);
+
+ poly_extent *extent = new poly_extent;
+ int extnum=0;
+ for (curscan = v1yclip; curscan < v3yclip; curscan += scaninc)
+ {
+ {
+ float fully = (float)(curscan + extnum) + 0.5f;
+ float startx = v1->x + (fully - v1->y) * dxdy_v1v3;
+ float stopx;
+ INT32 istartx, istopx;
+
+ /* compute the ending X based on which part of the triangle we're in */
+ if (fully < v2->y)
+ stopx = v1->x + (fully - v1->y) * dxdy_v1v2;
+ else
+ stopx = v2->x + (fully - v2->y) * dxdy_v2v3;
+
+ /* clamp to full pixels */
+ istartx = round_coordinate(startx);
+ istopx = round_coordinate(stopx);
+
+ /* force start < stop */
+ if (istartx > istopx)
+ {
+ INT32 temp = istartx;
+ istartx = istopx;
+ istopx = temp;
+ }
+
+ /* set the extent and update the total pixel count */
+ if (istartx >= istopx)
+ istartx = istopx = 0;
+
+ extent->startx = istartx;
+ extent->stopx = istopx;
+ (callback)(dest,curscan,extent,extra);
+ }
+ }
+
+ delete extent;
+}
+
+
+
+void poly_render_triangle_custom(void *dest, int startscanline, int numscanlines, const poly_extent *extents, poly_extra_data *extra)
+{
+ INT32 curscan, scaninc;
+ INT32 v1yclip, v3yclip;
+
+ v1yclip = startscanline;
+ v3yclip = startscanline + numscanlines;
+
+ if (v3yclip - v1yclip <= 0)
+ return;
+
+ for (curscan = v1yclip; curscan < v3yclip; curscan += scaninc)
+ {
+ tri_work_unit *unit = new tri_work_unit;
+ int extnum=0;
+
+ /* determine how much to advance to hit the next bucket */
+ scaninc = 1;
+
+ {
+ const poly_extent *extent = &extents[(curscan + extnum) - startscanline];
+ INT32 istartx = extent->startx, istopx = extent->stopx;
+
+ /* force start < stop */
+ if (istartx > istopx)
+ {
+ INT32 temp = istartx;
+ istartx = istopx;
+ istopx = temp;
+ }
+
+ /* set the extent and update the total pixel count */
+ unit->extent[extnum].startx = (INT16)istartx;
+ unit->extent[extnum].stopx = (INT16)istopx;
+ raster_fastfill(dest,curscan,extent,extra);
+ }
+ delete unit;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Statistics management
+ *
+ *************************************/
+
+static void accumulate_statistics(voodoo_state *v, const stats_block *stats)
+{
+ /* apply internal voodoo statistics */
+ v->reg[fbiPixelsIn].u += stats->pixels_in;
+ v->reg[fbiPixelsOut].u += stats->pixels_out;
+ v->reg[fbiChromaFail].u += stats->chroma_fail;
+ v->reg[fbiZfuncFail].u += stats->zfunc_fail;
+ v->reg[fbiAfuncFail].u += stats->afunc_fail;
+}
+
+static void update_statistics(voodoo_state *v, bool accumulate)
+{
+ /* accumulate/reset statistics from all units */
+ if (accumulate)
+ accumulate_statistics(v, &v->thread_stats[0]);
+ memset(&v->thread_stats[0], 0, sizeof(v->thread_stats[0]));
+
+ /* accumulate/reset statistics from the LFB */
+ if (accumulate)
+ accumulate_statistics(v, &v->fbi.lfb_stats);
+ memset(&v->fbi.lfb_stats, 0, sizeof(v->fbi.lfb_stats));
+}
+
+
+
+/*************************************
+ *
+ * Voodoo register writes
+ *
+ *************************************/
+
+void register_w(UINT32 offset, UINT32 data) {
+ voodoo_reg reg;
+ UINT32 regnum = (offset) & 0xff;
+ UINT32 chips = (offset>>8) & 0xf;
+ reg.u = data;
+
+ INT64 data64;
+
+// LOG(LOG_VOODOO,LOG_WARN)("V3D:WR chip %x reg %x value %08x(%s)", chips, regnum<<2, data, voodoo_reg_name[regnum]);
+
+ if (chips == 0)
+ chips = 0xf;
+ chips &= v->chipmask;
+
+ /* the first 64 registers can be aliased differently */
+ if ((offset & 0x800c0) == 0x80000 && v->alt_regmap)
+ regnum = register_alias_map[offset & 0x3f];
+ else
+ regnum = offset & 0xff;
+
+ /* first make sure this register is readable */
+ if (!(v->regaccess[regnum] & REGISTER_WRITE))
+ {
+ if (regnum <= 0xe0) LOG(LOG_VOODOO,LOG_WARN)("VOODOO.ERROR:Invalid attempt to write %s\n", v->regnames[regnum]);
+ else LOG(LOG_VOODOO,LOG_WARN)("VOODOO.ERROR:Invalid attempt to write #%x\n", regnum);
+ return;
+ }
+
+ /* switch off the register */
+ switch (regnum)
+ {
+ /* Vertex data is 12.4 formatted fixed point */
+ case fvertexAx:
+ data = float_to_int32(data, 4);
+ [[fallthrough]];
+ case vertexAx:
+ if (chips & 1) v->fbi.ax = (INT16)(data&0xffff);
+ break;
+
+ case fvertexAy:
+ data = float_to_int32(data, 4);
+ [[fallthrough]];
+ case vertexAy:
+ if (chips & 1) v->fbi.ay = (INT16)(data&0xffff);
+ break;
+
+ case fvertexBx:
+ data = float_to_int32(data, 4);
+ [[fallthrough]];
+ case vertexBx:
+ if (chips & 1) v->fbi.bx = (INT16)(data&0xffff);
+ break;
+
+ case fvertexBy:
+ data = float_to_int32(data, 4);
+ [[fallthrough]];
+ case vertexBy:
+ if (chips & 1) v->fbi.by = (INT16)(data&0xffff);
+ break;
+
+ case fvertexCx:
+ data = float_to_int32(data, 4);
+ [[fallthrough]];
+ case vertexCx:
+ if (chips & 1) v->fbi.cx = (INT16)(data&0xffff);
+ break;
+
+ case fvertexCy:
+ data = float_to_int32(data, 4);
+ [[fallthrough]];
+ case vertexCy:
+ if (chips & 1) v->fbi.cy = (INT16)(data&0xffff);
+ break;
+
+ /* RGB data is 12.12 formatted fixed point */
+ case fstartR:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case startR:
+ if (chips & 1) v->fbi.startr = (INT32)(data << 8) >> 8;
+ break;
+
+ case fstartG:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case startG:
+ if (chips & 1) v->fbi.startg = (INT32)(data << 8) >> 8;
+ break;
+
+ case fstartB:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case startB:
+ if (chips & 1) v->fbi.startb = (INT32)(data << 8) >> 8;
+ break;
+
+ case fstartA:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case startA:
+ if (chips & 1) v->fbi.starta = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdRdX:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dRdX:
+ if (chips & 1) v->fbi.drdx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdGdX:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dGdX:
+ if (chips & 1) v->fbi.dgdx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdBdX:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dBdX:
+ if (chips & 1) v->fbi.dbdx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdAdX:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dAdX:
+ if (chips & 1) v->fbi.dadx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdRdY:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dRdY:
+ if (chips & 1) v->fbi.drdy = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdGdY:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dGdY:
+ if (chips & 1) v->fbi.dgdy = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdBdY:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dBdY:
+ if (chips & 1) v->fbi.dbdy = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdAdY:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dAdY:
+ if (chips & 1) v->fbi.dady = (INT32)(data << 8) >> 8;
+ break;
+
+ /* Z data is 20.12 formatted fixed point */
+ case fstartZ:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case startZ:
+ if (chips & 1) v->fbi.startz = (INT32)data;
+ break;
+
+ case fdZdX:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dZdX:
+ if (chips & 1) v->fbi.dzdx = (INT32)data;
+ break;
+
+ case fdZdY:
+ data = float_to_int32(data, 12);
+ [[fallthrough]];
+ case dZdY:
+ if (chips & 1) v->fbi.dzdy = (INT32)data;
+ break;
+
+ /* S,T data is 14.18 formatted fixed point, converted to 16.32 internally */
+ case fstartS:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].starts = data64;
+ if (chips & 4) v->tmu[1].starts = data64;
+ break;
+ case startS:
+ if (chips & 2) v->tmu[0].starts = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].starts = (INT64)(INT32)data << 14;
+ break;
+
+ case fstartT:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].startt = data64;
+ if (chips & 4) v->tmu[1].startt = data64;
+ break;
+ case startT:
+ if (chips & 2) v->tmu[0].startt = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].startt = (INT64)(INT32)data << 14;
+ break;
+
+ case fdSdX:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dsdx = data64;
+ if (chips & 4) v->tmu[1].dsdx = data64;
+ break;
+ case dSdX:
+ if (chips & 2) v->tmu[0].dsdx = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dsdx = (INT64)(INT32)data << 14;
+ break;
+
+ case fdTdX:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dtdx = data64;
+ if (chips & 4) v->tmu[1].dtdx = data64;
+ break;
+ case dTdX:
+ if (chips & 2) v->tmu[0].dtdx = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dtdx = (INT64)(INT32)data << 14;
+ break;
+
+ case fdSdY:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dsdy = data64;
+ if (chips & 4) v->tmu[1].dsdy = data64;
+ break;
+ case dSdY:
+ if (chips & 2) v->tmu[0].dsdy = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dsdy = (INT64)(INT32)data << 14;
+ break;
+
+ case fdTdY:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dtdy = data64;
+ if (chips & 4) v->tmu[1].dtdy = data64;
+ break;
+ case dTdY:
+ if (chips & 2) v->tmu[0].dtdy = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dtdy = (INT64)(INT32)data << 14;
+ break;
+
+ /* W data is 2.30 formatted fixed point, converted to 16.32 internally */
+ case fstartW:
+ data64 = float_to_int64(data, 32);
+ if (chips & 1) v->fbi.startw = data64;
+ if (chips & 2) v->tmu[0].startw = data64;
+ if (chips & 4) v->tmu[1].startw = data64;
+ break;
+ case startW:
+ if (chips & 1) v->fbi.startw = (INT64)(INT32)data << 2;
+ if (chips & 2) v->tmu[0].startw = (INT64)(INT32)data << 2;
+ if (chips & 4) v->tmu[1].startw = (INT64)(INT32)data << 2;
+ break;
+
+ case fdWdX:
+ data64 = float_to_int64(data, 32);
+ if (chips & 1) v->fbi.dwdx = data64;
+ if (chips & 2) v->tmu[0].dwdx = data64;
+ if (chips & 4) v->tmu[1].dwdx = data64;
+ break;
+ case dWdX:
+ if (chips & 1) v->fbi.dwdx = (INT64)(INT32)data << 2;
+ if (chips & 2) v->tmu[0].dwdx = (INT64)(INT32)data << 2;
+ if (chips & 4) v->tmu[1].dwdx = (INT64)(INT32)data << 2;
+ break;
+
+ case fdWdY:
+ data64 = float_to_int64(data, 32);
+ if (chips & 1) v->fbi.dwdy = data64;
+ if (chips & 2) v->tmu[0].dwdy = data64;
+ if (chips & 4) v->tmu[1].dwdy = data64;
+ break;
+ case dWdY:
+ if (chips & 1) v->fbi.dwdy = (INT64)(INT32)data << 2;
+ if (chips & 2) v->tmu[0].dwdy = (INT64)(INT32)data << 2;
+ if (chips & 4) v->tmu[1].dwdy = (INT64)(INT32)data << 2;
+ break;
+
+ /* setup bits */
+ case sARGB:
+ if (chips & 1)
+ {
+ v->reg[sAlpha].f = (float)RGB_ALPHA(data);
+ v->reg[sRed].f = (float)RGB_RED(data);
+ v->reg[sGreen].f = (float)RGB_GREEN(data);
+ v->reg[sBlue].f = (float)RGB_BLUE(data);
+ }
+ break;
+
+ /* mask off invalid bits for different cards */
+ case fbzColorPath:
+ if (v->type < VOODOO_2)
+ data &= 0x0fffffff;
+ if (chips & 1) v->reg[fbzColorPath].u = data;
+ break;
+
+ case fbzMode:
+ if (v->type < VOODOO_2)
+ data &= 0x001fffff;
+ if (chips & 1) {
+ if (v->ogl && v->active && (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u)!=FBZMODE_Y_ORIGIN(data))) {
+ v->reg[fbzMode].u = data;
+ voodoo_ogl_set_window(v);
+ } else {
+ v->reg[fbzMode].u = data;
+ }
+ }
+ break;
+
+ case fogMode:
+ if (v->type < VOODOO_2)
+ data &= 0x0000003f;
+ if (chips & 1) v->reg[fogMode].u = data;
+ break;
+
+ /* triangle drawing */
+ case triangleCMD:
+ triangle(v);
+ break;
+
+ case ftriangleCMD:
+ triangle(v);
+ break;
+
+ case sBeginTriCMD:
+// E_Exit("begin tri");
+ begin_triangle(v);
+ break;
+
+ case sDrawTriCMD:
+// E_Exit("draw tri");
+ draw_triangle(v);
+ break;
+
+ /* other commands */
+ case nopCMD:
+ if (data & 1)
+ reset_counters(v);
+ if (data & 2)
+ v->reg[fbiTrianglesOut].u = 0;
+ break;
+
+ case fastfillCMD:
+ fastfill(v);
+ break;
+
+ case swapbufferCMD:
+ swapbuffer(v, data);
+ break;
+
+ /* gamma table access -- Voodoo/Voodoo2 only */
+ case clutData:
+/* if (chips & 1)
+ {
+ if (!FBIINIT1_VIDEO_TIMING_RESET(v->reg[fbiInit1].u))
+ {
+ int index = data >> 24;
+ if (index <= 32)
+ {
+// v->fbi.clut[index] = data;
+ }
+ }
+ else
+ LOG(LOG_VOODOO,LOG_WARN)("clutData ignored because video timing reset = 1\n");
+ } */
+ break;
+
+ /* external DAC access -- Voodoo/Voodoo2 only */
+ case dacData:
+ if (chips & 1)
+ {
+ if (!(data & 0x800))
+ dacdata_w(&v->dac, (data >> 8) & 7, data & 0xff);
+ else
+ dacdata_r(&v->dac, (data >> 8) & 7);
+ }
+ break;
+
+ /* vertical sync rate -- Voodoo/Voodoo2 only */
+ case hSync:
+ case vSync:
+ case backPorch:
+ case videoDimensions:
+ if (chips & 1)
+ {
+ v->reg[regnum].u = data;
+ if (v->reg[hSync].u != 0 && v->reg[vSync].u != 0 && v->reg[videoDimensions].u != 0)
+ {
+ int htotal = ((v->reg[hSync].u >> 16) & 0x3ff) + 1 + (v->reg[hSync].u & 0xff) + 1;
+ int vtotal = ((v->reg[vSync].u >> 16) & 0xfff) + (v->reg[vSync].u & 0xfff);
+ int hvis = v->reg[videoDimensions].u & 0x3ff;
+ int vvis = (v->reg[videoDimensions].u >> 16) & 0x3ff;
+ int hbp = (v->reg[backPorch].u & 0xff) + 2;
+ int vbp = (v->reg[backPorch].u >> 16) & 0xff;
+// attoseconds_t refresh = video_screen_get_frame_period(v->screen).attoseconds;
+ attoseconds_t refresh = 0;
+ attoseconds_t stdperiod, medperiod, vgaperiod;
+ attoseconds_t stddiff, meddiff, vgadiff;
+ rectangle visarea;
+
+ /* create a new visarea */
+ visarea.min_x = hbp;
+ visarea.max_x = hbp + hvis - 1;
+ visarea.min_y = vbp;
+ visarea.max_y = vbp + vvis - 1;
+
+ /* keep within bounds */
+ visarea.max_x = MIN(visarea.max_x, htotal - 1);
+ visarea.max_y = MIN(visarea.max_y, vtotal - 1);
+
+ /* compute the new period for standard res, medium res, and VGA res */
+ stdperiod = HZ_TO_ATTOSECONDS(15750) * vtotal;
+ medperiod = HZ_TO_ATTOSECONDS(25000) * vtotal;
+ vgaperiod = HZ_TO_ATTOSECONDS(31500) * vtotal;
+
+ /* compute a diff against the current refresh period */
+ stddiff = stdperiod - refresh;
+ if (stddiff < 0) stddiff = -stddiff;
+ meddiff = medperiod - refresh;
+ if (meddiff < 0) meddiff = -meddiff;
+ vgadiff = vgaperiod - refresh;
+ if (vgadiff < 0) vgadiff = -vgadiff;
+
+ LOG(LOG_VOODOO,LOG_WARN)("hSync=%08X vSync=%08X backPorch=%08X videoDimensions=%08X\n",
+ v->reg[hSync].u, v->reg[vSync].u, v->reg[backPorch].u, v->reg[videoDimensions].u);
+ LOG(LOG_VOODOO,LOG_WARN)("Horiz: %d-%d (%d total) Vert: %d-%d (%d total) -- ", visarea.min_x, visarea.max_x, htotal, visarea.min_y, visarea.max_y, vtotal);
+
+ /* configure the screen based on which one matches the closest */
+ if (stddiff < meddiff && stddiff < vgadiff)
+ {
+// video_screen_configure(v->screen, htotal, vtotal, &visarea, stdperiod);
+ LOG(LOG_VOODOO,LOG_WARN)("Standard resolution, %f Hz\n", ATTOSECONDS_TO_HZ(stdperiod));
+ }
+ else if (meddiff < vgadiff)
+ {
+// video_screen_configure(v->screen, htotal, vtotal, &visarea, medperiod);
+ LOG(LOG_VOODOO,LOG_WARN)("Medium resolution, %f Hz\n", ATTOSECONDS_TO_HZ(medperiod));
+ }
+ else
+ {
+// video_screen_configure(v->screen, htotal, vtotal, &visarea, vgaperiod);
+ LOG(LOG_VOODOO,LOG_WARN)("VGA resolution, %f Hz\n", ATTOSECONDS_TO_HZ(vgaperiod));
+ }
+
+ /* configure the new framebuffer info */
+ UINT32 new_width = (hvis+1) & ~1;
+ UINT32 new_height = (vvis+1) & ~1;
+ if ((v->fbi.width != new_width) || (v->fbi.height != new_height)) {
+ v->fbi.width = new_width;
+ v->fbi.height = new_height;
+ v->ogl_dimchange = true;
+ }
+// v->fbi.xoffs = hbp;
+// v->fbi.yoffs = vbp;
+// v->fbi.vsyncscan = (v->reg[vSync].u >> 16) & 0xfff;
+
+ /* recompute the time of VBLANK */
+// adjust_vblank_timer(v);
+
+ /* if changing dimensions, update video memory layout */
+ if (regnum == videoDimensions)
+ recompute_video_memory(v);
+
+ Voodoo_UpdateScreenStart();
+ }
+ }
+ break;
+
+ /* fbiInit0 can only be written if initEnable says we can -- Voodoo/Voodoo2 only */
+ case fbiInit0:
+ if ((chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ Voodoo_Output_Enable(FBIINIT0_VGA_PASSTHRU(data));
+ v->reg[fbiInit0].u = data;
+ if (FBIINIT0_GRAPHICS_RESET(data))
+ soft_reset(v);
+ recompute_video_memory(v);
+ }
+ break;
+
+ /* fbiInit5-7 are Voodoo 2-only; ignore them on anything else */
+ case fbiInit5:
+ case fbiInit6:
+ if (v->type < VOODOO_2)
+ break;
+ /* else fall through... */
+
+ /* fbiInitX can only be written if initEnable says we can -- Voodoo/Voodoo2 only */
+ /* most of these affect memory layout, so always recompute that when done */
+ case fbiInit1:
+ case fbiInit2:
+ case fbiInit4:
+ if ((chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ v->reg[regnum].u = data;
+ recompute_video_memory(v);
+ }
+ break;
+
+ case fbiInit3:
+ if ((chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ v->reg[regnum].u = data;
+ v->alt_regmap = (FBIINIT3_TRI_REGISTER_REMAP(data) > 0);
+ v->fbi.yorigin = FBIINIT3_YORIGIN_SUBTRACT(v->reg[fbiInit3].u);
+ recompute_video_memory(v);
+ }
+ break;
+
+ /* nccTable entries are processed and expanded immediately */
+ case nccTable+0:
+ case nccTable+1:
+ case nccTable+2:
+ case nccTable+3:
+ case nccTable+4:
+ case nccTable+5:
+ case nccTable+6:
+ case nccTable+7:
+ case nccTable+8:
+ case nccTable+9:
+ case nccTable+10:
+ case nccTable+11:
+ if (chips & 2) ncc_table_write(&v->tmu[0].ncc[0], regnum - nccTable, data);
+ if (chips & 4) ncc_table_write(&v->tmu[1].ncc[0], regnum - nccTable, data);
+ break;
+
+ case nccTable+12:
+ case nccTable+13:
+ case nccTable+14:
+ case nccTable+15:
+ case nccTable+16:
+ case nccTable+17:
+ case nccTable+18:
+ case nccTable+19:
+ case nccTable+20:
+ case nccTable+21:
+ case nccTable+22:
+ case nccTable+23:
+ if (chips & 2) ncc_table_write(&v->tmu[0].ncc[1], regnum - (nccTable+12), data);
+ if (chips & 4) ncc_table_write(&v->tmu[1].ncc[1], regnum - (nccTable+12), data);
+ break;
+
+ /* fogTable entries are processed and expanded immediately */
+ case fogTable+0:
+ case fogTable+1:
+ case fogTable+2:
+ case fogTable+3:
+ case fogTable+4:
+ case fogTable+5:
+ case fogTable+6:
+ case fogTable+7:
+ case fogTable+8:
+ case fogTable+9:
+ case fogTable+10:
+ case fogTable+11:
+ case fogTable+12:
+ case fogTable+13:
+ case fogTable+14:
+ case fogTable+15:
+ case fogTable+16:
+ case fogTable+17:
+ case fogTable+18:
+ case fogTable+19:
+ case fogTable+20:
+ case fogTable+21:
+ case fogTable+22:
+ case fogTable+23:
+ case fogTable+24:
+ case fogTable+25:
+ case fogTable+26:
+ case fogTable+27:
+ case fogTable+28:
+ case fogTable+29:
+ case fogTable+30:
+ case fogTable+31:
+ if (chips & 1)
+ {
+ int base = 2 * (regnum - fogTable);
+ v->fbi.fogdelta[base + 0] = (data >> 0) & 0xff;
+ v->fbi.fogblend[base + 0] = (data >> 8) & 0xff;
+ v->fbi.fogdelta[base + 1] = (data >> 16) & 0xff;
+ v->fbi.fogblend[base + 1] = (data >> 24) & 0xff;
+ }
+ break;
+
+ /* texture modifications cause us to recompute everything */
+ case textureMode:
+ case tLOD:
+ case tDetail:
+ case texBaseAddr:
+ case texBaseAddr_1:
+ case texBaseAddr_2:
+ case texBaseAddr_3_8:
+ if (chips & 2)
+ {
+ v->tmu[0].reg[regnum].u = data;
+ v->tmu[0].regdirty = true;
+ }
+ if (chips & 4)
+ {
+ v->tmu[1].reg[regnum].u = data;
+ v->tmu[1].regdirty = true;
+ }
+ break;
+
+ case trexInit1:
+ /* send tmu config data to the frame buffer */
+ v->send_config = (TREXINIT_SEND_TMU_CONFIG(data) > 0);
+ goto default_case;
+ break;
+
+ case clipLowYHighY:
+ case clipLeftRight:
+ if (chips & 1) v->reg[0x000 + regnum].u = data;
+ if (v->ogl) {
+ voodoo_ogl_clip_window(v);
+ }
+ break;
+
+ /* these registers are referenced in the renderer; we must wait for pending work before changing */
+ case chromaRange:
+ case chromaKey:
+ case alphaMode:
+ case fogColor:
+ case stipple:
+ case zaColor:
+ case color1:
+ case color0:
+ /* fall through to default implementation */
+
+ /* by default, just feed the data to the chips */
+ default:
+default_case:
+ if (chips & 1) v->reg[0x000 + regnum].u = data;
+ if (chips & 2) v->reg[0x100 + regnum].u = data;
+ if (chips & 4) v->reg[0x200 + regnum].u = data;
+ if (chips & 8) v->reg[0x300 + regnum].u = data;
+ break;
+ }
+
+}
+
+
+
+/*************************************
+ *
+ * Voodoo LFB writes
+ *
+ *************************************/
+
+void lfb_w(UINT32 offset, UINT32 data, UINT32 mem_mask) {
+ LOG(LOG_VOODOO,LOG_WARN)("V3D:WR LFB offset %X value %08X", offset, data);
+ UINT16 *dest, *depth;
+ UINT32 destmax, depthmax;
+
+ int sr[2], sg[2], sb[2], sa[2], sw[2];
+ int x, y, scry, mask;
+ int pix, destbuf;
+
+ /* byte swizzling */
+ if (LFBMODE_BYTE_SWIZZLE_WRITES(v->reg[lfbMode].u))
+ {
+ data = FLIPENDIAN_INT32(data);
+ mem_mask = FLIPENDIAN_INT32(mem_mask);
+ }
+
+ /* word swapping */
+ if (LFBMODE_WORD_SWAP_WRITES(v->reg[lfbMode].u))
+ {
+ data = (data << 16) | (data >> 16);
+ mem_mask = (mem_mask << 16) | (mem_mask >> 16);
+ }
+
+ /* extract default depth and alpha values */
+ sw[0] = sw[1] = v->reg[zaColor].u & 0xffff;
+ sa[0] = sa[1] = v->reg[zaColor].u >> 24;
+
+ /* first extract A,R,G,B from the data */
+ switch (LFBMODE_WRITE_FORMAT(v->reg[lfbMode].u) + 16 * LFBMODE_RGBA_LANES(v->reg[lfbMode].u))
+ {
+ case 16*0 + 0: /* ARGB, 16-bit RGB 5-6-5 */
+ case 16*2 + 0: /* RGBA, 16-bit RGB 5-6-5 */
+ EXTRACT_565_TO_888(data, sr[0], sg[0], sb[0]);
+ EXTRACT_565_TO_888(data >> 16, sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*1 + 0: /* ABGR, 16-bit RGB 5-6-5 */
+ case 16*3 + 0: /* BGRA, 16-bit RGB 5-6-5 */
+ EXTRACT_565_TO_888(data, sb[0], sg[0], sr[0]);
+ EXTRACT_565_TO_888(data >> 16, sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+
+ case 16*0 + 1: /* ARGB, 16-bit RGB x-5-5-5 */
+ EXTRACT_x555_TO_888(data, sr[0], sg[0], sb[0]);
+ EXTRACT_x555_TO_888(data >> 16, sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*1 + 1: /* ABGR, 16-bit RGB x-5-5-5 */
+ EXTRACT_x555_TO_888(data, sb[0], sg[0], sr[0]);
+ EXTRACT_x555_TO_888(data >> 16, sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*2 + 1: /* RGBA, 16-bit RGB x-5-5-5 */
+ EXTRACT_555x_TO_888(data, sr[0], sg[0], sb[0]);
+ EXTRACT_555x_TO_888(data >> 16, sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*3 + 1: /* BGRA, 16-bit RGB x-5-5-5 */
+ EXTRACT_555x_TO_888(data, sb[0], sg[0], sr[0]);
+ EXTRACT_555x_TO_888(data >> 16, sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+
+ case 16*0 + 2: /* ARGB, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_1555_TO_8888(data, sa[0], sr[0], sg[0], sb[0]);
+ EXTRACT_1555_TO_8888(data >> 16, sa[1], sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+ case 16*1 + 2: /* ABGR, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_1555_TO_8888(data, sa[0], sb[0], sg[0], sr[0]);
+ EXTRACT_1555_TO_8888(data >> 16, sa[1], sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+ case 16*2 + 2: /* RGBA, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_5551_TO_8888(data, sr[0], sg[0], sb[0], sa[0]);
+ EXTRACT_5551_TO_8888(data >> 16, sr[1], sg[1], sb[1], sa[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+ case 16*3 + 2: /* BGRA, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_5551_TO_8888(data, sb[0], sg[0], sr[0], sa[0]);
+ EXTRACT_5551_TO_8888(data >> 16, sb[1], sg[1], sr[1], sa[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+
+ case 16*0 + 4: /* ARGB, 32-bit RGB x-8-8-8 */
+ EXTRACT_x888_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+ case 16*1 + 4: /* ABGR, 32-bit RGB x-8-8-8 */
+ EXTRACT_x888_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+ case 16*2 + 4: /* RGBA, 32-bit RGB x-8-8-8 */
+ EXTRACT_888x_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+ case 16*3 + 4: /* BGRA, 32-bit RGB x-8-8-8 */
+ EXTRACT_888x_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+
+ case 16*0 + 5: /* ARGB, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sa[0], sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+ case 16*1 + 5: /* ABGR, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sa[0], sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+ case 16*2 + 5: /* RGBA, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sr[0], sg[0], sb[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+ case 16*3 + 5: /* BGRA, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sb[0], sg[0], sr[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+
+ case 16*0 + 12: /* ARGB, 32-bit depth+RGB 5-6-5 */
+ case 16*2 + 12: /* RGBA, 32-bit depth+RGB 5-6-5 */
+ sw[0] = data >> 16;
+ EXTRACT_565_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*1 + 12: /* ABGR, 32-bit depth+RGB 5-6-5 */
+ case 16*3 + 12: /* BGRA, 32-bit depth+RGB 5-6-5 */
+ sw[0] = data >> 16;
+ EXTRACT_565_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+
+ case 16*0 + 13: /* ARGB, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_x555_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*1 + 13: /* ABGR, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_x555_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*2 + 13: /* RGBA, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_555x_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*3 + 13: /* BGRA, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_555x_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+
+ case 16*0 + 14: /* ARGB, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_1555_TO_8888(data, sa[0], sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*1 + 14: /* ABGR, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_1555_TO_8888(data, sa[0], sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*2 + 14: /* RGBA, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_5551_TO_8888(data, sr[0], sg[0], sb[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*3 + 14: /* BGRA, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_5551_TO_8888(data, sb[0], sg[0], sr[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+
+ case 16*0 + 15: /* ARGB, 16-bit depth */
+ case 16*1 + 15: /* ARGB, 16-bit depth */
+ case 16*2 + 15: /* ARGB, 16-bit depth */
+ case 16*3 + 15: /* ARGB, 16-bit depth */
+ sw[0] = data & 0xffff;
+ sw[1] = data >> 16;
+ mask = LFB_DEPTH_PRESENT | (LFB_DEPTH_PRESENT << 4);
+ offset <<= 1;
+ break;
+
+ default: /* reserved */
+ return;
+ }
+
+ /* compute X,Y */
+ x = (offset << 0) & ((1 << 10) - 1);
+ y = (offset >> 10) & ((1 << 10) - 1);
+
+ /* adjust the mask based on which half of the data is written */
+ if (!ACCESSING_BITS_0_15)
+ mask &= ~(0x0f - LFB_DEPTH_PRESENT_MSW);
+ if (!ACCESSING_BITS_16_31)
+ mask &= ~(0xf0 + LFB_DEPTH_PRESENT_MSW);
+
+ /* select the target buffer */
+ destbuf = LFBMODE_WRITE_BUFFER_SELECT(v->reg[lfbMode].u);
+// LOG(LOG_VOODOO,LOG_WARN)("destbuf %X lfbmode %X",destbuf, v->reg[lfbMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ dest = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ destmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.frontbuf]) / 2;
+ break;
+
+ case 1: /* back buffer */
+ dest = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ destmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.backbuf]) / 2;
+ break;
+
+ default: /* reserved */
+ E_Exit("reserved lfb write");
+ return;
+ }
+ depth = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs);
+ depthmax = (v->fbi.mask + 1 - v->fbi.auxoffs) / 2;
+
+ /* simple case: no pipeline */
+ if (!LFBMODE_ENABLE_PIXEL_PIPELINE(v->reg[lfbMode].u))
+ {
+ DECLARE_DITHER_POINTERS;
+ UINT32 bufoffs;
+
+ if (LOG_LFB) LOG(LOG_VOODOO,LOG_WARN)("VOODOO.LFB:write raw mode %X (%d,%d) = %08X & %08X\n", LFBMODE_WRITE_FORMAT(v->reg[lfbMode].u), x, y, data, mem_mask);
+
+ /* determine the screen Y */
+ scry = y;
+ if (LFBMODE_Y_ORIGIN(v->reg[lfbMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* advance pointers to the proper row */
+ bufoffs = scry * v->fbi.rowpixels + x;
+
+ /* compute dithering */
+ COMPUTE_DITHER_POINTERS(v->reg[fbzMode].u, y);
+
+ /* loop over up to two pixels */
+ for (pix = 0; mask; pix++)
+ {
+ /* make sure we care about this pixel */
+ if (mask & 0x0f)
+ {
+ bool has_rgb = (mask & LFB_RGB_PRESENT) > 0;
+ bool has_alpha = ((mask & LFB_ALPHA_PRESENT) > 0) && (FBZMODE_ENABLE_ALPHA_PLANES(v->reg[fbzMode].u) > 0);
+ bool has_depth = ((mask & (LFB_DEPTH_PRESENT | LFB_DEPTH_PRESENT_MSW)) && !FBZMODE_ENABLE_ALPHA_PLANES(v->reg[fbzMode].u));
+ if (v->ogl && v->active) {
+ if (has_rgb || has_alpha) {
+ // if enabling dithering: output is 565 not 888 anymore
+// APPLY_DITHER(v->reg[fbzMode].u, x, dither_lookup, sr[pix], sg[pix], sb[pix]);
+ voodoo_ogl_draw_pixel(x, scry+1, has_rgb, has_alpha, sr[pix], sg[pix], sb[pix], sa[pix]);
+ }
+ if (has_depth) {
+#if C_OPENGL
+ voodoo_ogl_draw_z(x, scry+1, sw[pix]);
+#endif
+ }
+ } else {
+ /* write to the RGB buffer */
+ if (has_rgb && bufoffs < destmax)
+ {
+ /* apply dithering and write to the screen */
+ APPLY_DITHER(v->reg[fbzMode].u, x, dither_lookup, sr[pix], sg[pix], sb[pix]);
+ dest[bufoffs] = (UINT16)((sr[pix] << 11) | (sg[pix] << 5) | sb[pix]);
+ }
+
+ /* make sure we have an aux buffer to write to */
+ if (depth && bufoffs < depthmax)
+ {
+ /* write to the alpha buffer */
+ if (has_alpha)
+ depth[bufoffs] = (UINT16)sa[pix];
+
+ /* write to the depth buffer */
+ if (has_depth)
+ depth[bufoffs] = (UINT16)sw[pix];
+ }
+ }
+
+ /* track pixel writes to the frame buffer regardless of mask */
+ v->reg[fbiPixelsOut].u++;
+ }
+
+ /* advance our pointers */
+ bufoffs++;
+ x++;
+ mask >>= 4;
+ }
+ }
+
+ /* tricky case: run the full pixel pipeline on the pixel */
+ else
+ {
+ DECLARE_DITHER_POINTERS;
+
+ if (LOG_LFB) LOG(LOG_VOODOO,LOG_WARN)("VOODOO.LFB:write pipelined mode %X (%d,%d) = %08X & %08X\n", LFBMODE_WRITE_FORMAT(v->reg[lfbMode].u), x, y, data, mem_mask);
+
+ /* determine the screen Y */
+ scry = y;
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* advance pointers to the proper row */
+ dest += scry * v->fbi.rowpixels;
+ if (depth)
+ depth += scry * v->fbi.rowpixels;
+
+ /* compute dithering */
+ COMPUTE_DITHER_POINTERS(v->reg[fbzMode].u, y);
+
+ INT32 blendr = 0, blendg = 0, blendb = 0, blenda = 0;
+
+ /* loop over up to two pixels */
+ for (pix = 0; mask; pix++)
+ {
+ /* make sure we care about this pixel */
+ if (mask & 0x0f)
+ {
+ stats_block *stats = &v->fbi.lfb_stats;
+ INT64 iterw = sw[pix] << (30-16);
+ INT32 iterz = sw[pix] << 12;
+ rgb_union color;
+
+ /* apply clipping */
+ if (FBZMODE_ENABLE_CLIPPING(v->reg[fbzMode].u))
+ {
+ if (x < (INT32)((v->reg[clipLeftRight].u >> 16) & 0x3ff) ||
+ x >= (INT32)(v->reg[clipLeftRight].u & 0x3ff) ||
+ scry < (INT32)((v->reg[clipLowYHighY].u >> 16) & 0x3ff) ||
+ scry >= (INT32)(v->reg[clipLowYHighY].u & 0x3ff))
+ {
+ stats->pixels_in++;
+ stats->clip_fail++;
+ goto nextpixel;
+ }
+ }
+
+ /* pixel pipeline part 1 handles depth testing and stippling */
+ // TODO: in the v->ogl case this macro doesn't really work with depth testing
+ PIXEL_PIPELINE_BEGIN(v, x, y, v->reg[fbzColorPath].u, v->reg[fbzMode].u, iterz, iterw);
+
+ color.rgb.r = sr[pix];
+ color.rgb.g = sg[pix];
+ color.rgb.b = sb[pix];
+ color.rgb.a = sa[pix];
+
+ /* apply chroma key */
+ APPLY_CHROMAKEY(v, stats, v->reg[fbzMode].u, color);
+
+ /* apply alpha mask, and alpha testing */
+ APPLY_ALPHAMASK(v, stats, v->reg[fbzMode].u, color.rgb.a);
+ APPLY_ALPHATEST(v, stats, v->reg[alphaMode].u, color.rgb.a);
+
+
+ if (FBZCP_CC_MSELECT(v->reg[fbzColorPath].u) != 0) LOG_MSG("lfbw fpp mselect %8x",FBZCP_CC_MSELECT(v->reg[fbzColorPath].u));
+ if (FBZCP_CCA_MSELECT(v->reg[fbzColorPath].u) > 1) LOG_MSG("lfbw fpp mselect alpha %8x",FBZCP_CCA_MSELECT(v->reg[fbzColorPath].u));
+
+ if (FBZCP_CC_REVERSE_BLEND(v->reg[fbzColorPath].u) != 0) {
+ if (FBZCP_CC_MSELECT(v->reg[fbzColorPath].u) != 0) LOG_MSG("lfbw fpp rblend %8x",FBZCP_CC_REVERSE_BLEND(v->reg[fbzColorPath].u));
+ }
+ if (FBZCP_CCA_REVERSE_BLEND(v->reg[fbzColorPath].u) != 0) {
+ if (FBZCP_CC_MSELECT(v->reg[fbzColorPath].u) != 0) LOG_MSG("lfbw fpp rblend alpha %8x",FBZCP_CCA_REVERSE_BLEND(v->reg[fbzColorPath].u));
+ }
+
+ rgb_union c_local;
+
+ /* compute c_local */
+ if (FBZCP_CC_LOCALSELECT_OVERRIDE(v->reg[fbzColorPath].u) == 0)
+ {
+ if (FBZCP_CC_LOCALSELECT(v->reg[fbzColorPath].u) == 0) /* iterated RGB */
+ {
+// c_local.u = iterargb.u;
+ c_local.rgb.r = sr[pix];
+ c_local.rgb.g = sg[pix];
+ c_local.rgb.b = sb[pix];
+ }
+ else /* color0 RGB */
+ c_local.u = v->reg[color0].u;
+ }
+ else
+ {
+ LOG_MSG("lfbw fpp FBZCP_CC_LOCALSELECT_OVERRIDE set!");
+/* if (!(texel.rgb.a & 0x80)) // iterated RGB
+ c_local.u = iterargb.u;
+ else // color0 RGB
+ c_local.u = v->reg[color0].u; */
+ }
+
+ /* compute a_local */
+ switch (FBZCP_CCA_LOCALSELECT(v->reg[fbzColorPath].u))
+ {
+ default:
+ case 0: /* iterated alpha */
+// c_local.rgb.a = iterargb.rgb.a;
+ c_local.rgb.a = sa[pix];
+ break;
+ case 1: /* color0 alpha */
+ c_local.rgb.a = v->reg[color0].rgb.a;
+ break;
+ case 2: /* clamped iterated Z[27:20] */
+ {
+ int temp;
+ CLAMPED_Z(iterz, v->reg[fbzColorPath].u, temp);
+ c_local.rgb.a = (UINT8)temp;
+ break;
+ }
+ case 3: /* clamped iterated W[39:32] */
+ {
+ int temp;
+ CLAMPED_W(iterw, v->reg[fbzColorPath].u, temp); /* Voodoo 2 only */
+ c_local.rgb.a = (UINT8)temp;
+ break;
+ }
+ }
+
+ /* select zero or c_other */
+ if (FBZCP_CC_ZERO_OTHER(v->reg[fbzColorPath].u) == 0) {
+ r = sr[pix];
+ g = sg[pix];
+ b = sb[pix];
+ } else {
+ r = g = b = 0;
+ }
+
+ /* select zero or a_other */
+ if (FBZCP_CCA_ZERO_OTHER(v->reg[fbzColorPath].u) == 0) {
+ a = sa[pix];
+ } else {
+ a = 0;
+ }
+
+ /* subtract c_local */
+ if (FBZCP_CC_SUB_CLOCAL(v->reg[fbzColorPath].u))
+ {
+ r -= c_local.rgb.r;
+ g -= c_local.rgb.g;
+ b -= c_local.rgb.b;
+ }
+
+ /* subtract a_local */
+ if (FBZCP_CCA_SUB_CLOCAL(v->reg[fbzColorPath].u))
+ a -= c_local.rgb.a;
+
+ /* blend RGB */
+ switch (FBZCP_CC_MSELECT(v->reg[fbzColorPath].u))
+ {
+ default: /* reserved */
+ case 0: /* 0 */
+ blendr = blendg = blendb = 0;
+ break;
+ case 1: /* c_local */
+ blendr = c_local.rgb.r;
+ blendg = c_local.rgb.g;
+ blendb = c_local.rgb.b;
+ LOG_MSG("blend RGB c_local");
+ break;
+ case 2: /* a_other */
+// blendr = blendg = blendb = c_other.rgb.a;
+ LOG_MSG("blend RGB a_other");
+ break;
+ case 3: /* a_local */
+ blendr = blendg = blendb = c_local.rgb.a;
+ LOG_MSG("blend RGB a_local");
+ break;
+ case 4: /* texture alpha */
+// blendr = blendg = blendb = texel.rgb.a;
+ LOG_MSG("blend RGB texture alpha");
+ break;
+ case 5: /* texture RGB (Voodoo 2 only) */
+/* blendr = texel.rgb.r;
+ blendg = texel.rgb.g;
+ blendb = texel.rgb.b; */
+ LOG_MSG("blend RGB texture RGB");
+ break;
+ }
+
+ /* blend alpha */
+ switch (FBZCP_CCA_MSELECT(v->reg[fbzColorPath].u))
+ {
+ default: /* reserved */
+ case 0: /* 0 */
+ blenda = 0;
+ break;
+ case 1: /* a_local */
+ blenda = c_local.rgb.a;
+// LOG_MSG("blend alpha a_local");
+ break;
+ case 2: /* a_other */
+// blenda = c_other.rgb.a;
+ LOG_MSG("blend alpha a_other");
+ break;
+ case 3: /* a_local */
+ blenda = c_local.rgb.a;
+ LOG_MSG("blend alpha a_local");
+ break;
+ case 4: /* texture alpha */
+// blenda = texel.rgb.a;
+ LOG_MSG("blend alpha texture alpha");
+ break;
+ }
+
+ /* reverse the RGB blend */
+ if (!FBZCP_CC_REVERSE_BLEND(v->reg[fbzColorPath].u))
+ {
+ blendr ^= 0xff;
+ blendg ^= 0xff;
+ blendb ^= 0xff;
+ }
+
+ /* reverse the alpha blend */
+ if (!FBZCP_CCA_REVERSE_BLEND(v->reg[fbzColorPath].u))
+ blenda ^= 0xff;
+
+ /* do the blend */
+ r = (r * (blendr + 1)) >> 8;
+ g = (g * (blendg + 1)) >> 8;
+ b = (b * (blendb + 1)) >> 8;
+ a = (a * (blenda + 1)) >> 8;
+
+ /* add clocal or alocal to RGB */
+ switch (FBZCP_CC_ADD_ACLOCAL(v->reg[fbzColorPath].u))
+ {
+ case 3: /* reserved */
+ case 0: /* nothing */
+ break;
+ case 1: /* add c_local */
+ r += c_local.rgb.r;
+ g += c_local.rgb.g;
+ b += c_local.rgb.b;
+ break;
+ case 2: /* add_alocal */
+ r += c_local.rgb.a;
+ g += c_local.rgb.a;
+ b += c_local.rgb.a;
+ break;
+ }
+
+ /* add clocal or alocal to alpha */
+ if (FBZCP_CCA_ADD_ACLOCAL(v->reg[fbzColorPath].u))
+ a += c_local.rgb.a;
+
+ /* clamp */
+ CLAMP(r, 0x00, 0xff);
+ CLAMP(g, 0x00, 0xff);
+ CLAMP(b, 0x00, 0xff);
+ CLAMP(a, 0x00, 0xff);
+
+ /* invert */
+ if (FBZCP_CC_INVERT_OUTPUT(v->reg[fbzColorPath].u))
+ {
+ r ^= 0xff;
+ g ^= 0xff;
+ b ^= 0xff;
+ }
+ if (FBZCP_CCA_INVERT_OUTPUT(v->reg[fbzColorPath].u))
+ a ^= 0xff;
+
+ if (v->ogl && v->active) {
+ if (FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u)) {
+// APPLY_DITHER(FBZMODE, XX, DITHER_LOOKUP, r, g, b);
+ voodoo_ogl_draw_pixel_pipeline(x, scry+1, r, g, b);
+ }
+/* if (depth && FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u)) {
+ if (FBZMODE_ENABLE_ALPHA_PLANES(v->reg[fbzMode].u) == 0)
+ voodoo_ogl_draw_z(x, y, depthval&0xffff, depthval>>16);
+// else
+// depth[XX] = a;
+ } */
+ } else {
+ /* pixel pipeline part 2 handles color combine, fog, alpha, and final output */
+ PIXEL_PIPELINE_MODIFY(v, dither, dither4, x, v->reg[fbzMode].u, v->reg[fbzColorPath].u, v->reg[alphaMode].u, v->reg[fogMode].u, iterz, iterw, v->reg[zaColor]);
+
+ PIXEL_PIPELINE_FINISH(v, dither_lookup, x, dest, depth, v->reg[fbzMode].u);
+ }
+
+ PIXEL_PIPELINE_END(stats);
+ }
+nextpixel:
+ /* advance our pointers */
+ x++;
+ mask >>= 4;
+ }
+ }
+}
+
+
+
+/*************************************
+ *
+ * Voodoo texture RAM writes
+ *
+ *************************************/
+
+INT32 texture_w(UINT32 offset, UINT32 data) {
+ int tmunum = (offset >> 19) & 0x03;
+ LOG(LOG_VOODOO,LOG_WARN)("V3D:write TMU%x offset %X value %X", tmunum, offset, data);
+
+ tmu_state *t;
+
+ /* point to the right TMU */
+ if (!(v->chipmask & (2 << tmunum)))
+ return 0;
+ t = &v->tmu[tmunum];
+
+ if (TEXLOD_TDIRECT_WRITE(t->reg[tLOD].u))
+ E_Exit("Texture direct write!");
+
+ /* update texture info if dirty */
+ if (t->regdirty)
+ recompute_texture_params(t);
+
+ /* swizzle the data */
+ if (TEXLOD_TDATA_SWIZZLE(t->reg[tLOD].u))
+ data = FLIPENDIAN_INT32(data);
+ if (TEXLOD_TDATA_SWAP(t->reg[tLOD].u))
+ data = (data >> 16) | (data << 16);
+
+ /* 8-bit texture case */
+ if (TEXMODE_FORMAT(t->reg[textureMode].u) < 8)
+ {
+ int lod, tt, ts;
+ UINT32 tbaseaddr;
+ UINT8 *dest;
+
+ /* extract info */
+ lod = (offset >> 15) & 0x0f;
+ tt = (offset >> 7) & 0xff;
+
+ /* old code has a bit about how this is broken in gauntleg unless we always look at TMU0 */
+ if (TEXMODE_SEQ_8_DOWNLD(v->tmu[0].reg/*t->reg*/[textureMode].u))
+ ts = (offset << 2) & 0xfc;
+ else
+ ts = (offset << 1) & 0xfc;
+
+ /* validate parameters */
+ if (lod > 8)
+ return 0;
+
+ /* compute the base address */
+ tbaseaddr = t->lodoffset[lod];
+ tbaseaddr += tt * ((t->wmask >> lod) + 1) + ts;
+
+ if (LOG_TEXTURE_RAM) LOG(LOG_VOODOO,LOG_WARN)("Texture 8-bit w: lod=%d s=%d t=%d data=%08X\n", lod, ts, tt, data);
+
+ /* write the four bytes in little-endian order */
+ dest = t->ram;
+ tbaseaddr &= t->mask;
+
+ bool changed = false;
+ if (dest[BYTE4_XOR_LE(tbaseaddr + 0)] != ((data >> 0) & 0xff)) {
+ dest[BYTE4_XOR_LE(tbaseaddr + 0)] = (data >> 0) & 0xff;
+ changed = true;
+ }
+ if (dest[BYTE4_XOR_LE(tbaseaddr + 1)] != ((data >> 8) & 0xff)) {
+ dest[BYTE4_XOR_LE(tbaseaddr + 1)] = (data >> 8) & 0xff;
+ changed = true;
+ }
+ if (dest[BYTE4_XOR_LE(tbaseaddr + 2)] != ((data >> 16) & 0xff)) {
+ dest[BYTE4_XOR_LE(tbaseaddr + 2)] = (data >> 16) & 0xff;
+ changed = true;
+ }
+ if (dest[BYTE4_XOR_LE(tbaseaddr + 3)] != ((data >> 24) & 0xff)) {
+ dest[BYTE4_XOR_LE(tbaseaddr + 3)] = (data >> 24) & 0xff;
+ changed = true;
+ }
+
+ if (changed && v->ogl && v->active) {
+ voodoo_ogl_texture_clear(t->lodoffset[lod],tmunum);
+ voodoo_ogl_texture_clear(t->lodoffset[t->lodmin],tmunum);
+ }
+ }
+
+ /* 16-bit texture case */
+ else
+ {
+ int lod, tt, ts;
+ UINT32 tbaseaddr;
+ UINT16 *dest;
+
+ /* extract info */
+ tmunum = (offset >> 19) & 0x03;
+ lod = (offset >> 15) & 0x0f;
+ tt = (offset >> 7) & 0xff;
+ ts = (offset << 1) & 0xfe;
+
+ /* validate parameters */
+ if (lod > 8)
+ return 0;
+
+ /* compute the base address */
+ tbaseaddr = t->lodoffset[lod];
+ tbaseaddr += 2 * (tt * ((t->wmask >> lod) + 1) + ts);
+
+ if (LOG_TEXTURE_RAM) LOG(LOG_VOODOO,LOG_WARN)("Texture 16-bit w: lod=%d s=%d t=%d data=%08X\n", lod, ts, tt, data);
+
+ /* write the two words in little-endian order */
+ dest = (UINT16 *)t->ram;
+ tbaseaddr &= t->mask;
+ tbaseaddr >>= 1;
+
+ bool changed = false;
+ if (dest[BYTE_XOR_LE(tbaseaddr + 0)] != ((data >> 0) & 0xffff)) {
+ dest[BYTE_XOR_LE(tbaseaddr + 0)] = (data >> 0) & 0xffff;
+ changed = true;
+ }
+ if (dest[BYTE_XOR_LE(tbaseaddr + 1)] != ((data >> 16) & 0xffff)) {
+ dest[BYTE_XOR_LE(tbaseaddr + 1)] = (data >> 16) & 0xffff;
+ changed = true;
+ }
+
+ if (changed && v->ogl && v->active) {
+ voodoo_ogl_texture_clear(t->lodoffset[lod],tmunum);
+ voodoo_ogl_texture_clear(t->lodoffset[t->lodmin],tmunum);
+ }
+ }
+
+ return 0;
+}
+
+
+
+/*************************************
+ *
+ * Handle a register read
+ *
+ *************************************/
+
+UINT32 register_r(UINT32 offset)
+{
+ UINT32 regnum = (offset) & 0xff;
+
+// LOG(LOG_VOODOO,LOG_WARN)("Voodoo:read chip %x reg %x (%s)", chips, regnum<<2, voodoo_reg_name[regnum]);
+
+ /* first make sure this register is readable */
+ if (!(v->regaccess[regnum] & REGISTER_READ))
+ {
+ return 0xffffffff;
+ }
+
+ UINT32 result;
+
+ /* default result is the FBI register value */
+ result = v->reg[regnum].u;
+
+ /* some registers are dynamic; compute them */
+ switch (regnum)
+ {
+ case status:
+
+ /* start with a blank slate */
+ result = 0;
+
+ /* bits 5:0 are the PCI FIFO free space */
+ result |= 0x3f << 0;
+
+ /* bit 6 is the vertical retrace */
+ //result |= v->fbi.vblank << 6;
+ result |= (Voodoo_GetRetrace() ? 0x40 : 0);
+
+
+ /* bit 7 is FBI graphics engine busy */
+ if (v->pci.op_pending)
+ result |= 1 << 7;
+
+ /* bit 8 is TREX busy */
+ if (v->pci.op_pending)
+ result |= 1 << 8;
+
+ /* bit 9 is overall busy */
+ if (v->pci.op_pending)
+ result |= 1 << 9;
+
+ /* bits 11:10 specifies which buffer is visible */
+ result |= v->fbi.frontbuf << 10;
+
+ /* bits 27:12 indicate memory FIFO freespace */
+ result |= 0xffff << 12;
+
+ /* bits 30:28 are the number of pending swaps */
+ result |= 0 << 28;
+
+ /* bit 31 is not used */
+
+ break;
+
+ case hvRetrace:
+ if (v->type < VOODOO_2)
+ break;
+
+ /* start with a blank slate */
+ result = 0;
+
+ result |= ((uint32_t)(Voodoo_GetVRetracePosition() * 0x1fff)) & 0x1fff;
+ result |= (((uint32_t)(Voodoo_GetHRetracePosition() * 0x7ff)) & 0x7ff) << 16;
+
+ break;
+
+ /* bit 2 of the initEnable register maps this to dacRead */
+ case fbiInit2:
+ if (INITEN_REMAP_INIT_TO_DAC(v->pci.init_enable))
+ result = v->dac.read_result;
+ break;
+
+/* case fbiInit3:
+ if (INITEN_REMAP_INIT_TO_DAC(v->pci.init_enable))
+ result = 0;
+ break;
+
+ case fbiInit6:
+ if (v->type < VOODOO_2)
+ break;
+ result &= 0xffffe7ff;
+ result |= 0x1000;
+ break; */
+
+ /* all counters are 24-bit only */
+ case fbiPixelsIn:
+ case fbiChromaFail:
+ case fbiZfuncFail:
+ case fbiAfuncFail:
+ case fbiPixelsOut:
+ update_statistics(v, true);
+ [[fallthrough]];
+ case fbiTrianglesOut:
+ result = v->reg[regnum].u & 0xffffff;
+ break;
+
+ }
+
+ return result;
+}
+
+
+
+/*************************************
+ *
+ * Handle an LFB read
+ *
+ *************************************/
+UINT32 lfb_r(UINT32 offset)
+{
+ LOG(LOG_VOODOO,LOG_WARN)("Voodoo:read LFB offset %X", offset);
+ UINT16 *buffer;
+ UINT32 bufmax;
+ UINT32 bufoffs;
+ UINT32 data;
+ int x, y, scry;
+ UINT32 destbuf;
+
+ /* compute X,Y */
+ x = (offset << 1) & 0x3fe;
+ y = (offset >> 9) & 0x3ff;
+
+ /* select the target buffer */
+ destbuf = LFBMODE_READ_BUFFER_SELECT(v->reg[lfbMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ bufmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.frontbuf]) / 2;
+ break;
+
+ case 1: /* back buffer */
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ bufmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.backbuf]) / 2;
+ break;
+
+ case 2: /* aux buffer */
+ if (v->fbi.auxoffs == (UINT32)(~0))
+ return 0xffffffff;
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs);
+ bufmax = (v->fbi.mask + 1 - v->fbi.auxoffs) / 2;
+ break;
+
+ default: /* reserved */
+ return 0xffffffff;
+ }
+
+ /* determine the screen Y */
+ scry = y;
+ if (LFBMODE_Y_ORIGIN(v->reg[lfbMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ if (v->ogl && v->active) {
+ data = voodoo_ogl_read_pixel(x, scry+1);
+ } else {
+ /* advance pointers to the proper row */
+ bufoffs = scry * v->fbi.rowpixels + x;
+ if (bufoffs >= bufmax)
+ return 0xffffffff;
+
+ /* compute the data */
+ data = buffer[bufoffs + 0] | (buffer[bufoffs + 1] << 16);
+ }
+
+ /* word swapping */
+ if (LFBMODE_WORD_SWAP_READS(v->reg[lfbMode].u))
+ data = (data << 16) | (data >> 16);
+
+ /* byte swizzling */
+ if (LFBMODE_BYTE_SWIZZLE_READS(v->reg[lfbMode].u))
+ data = FLIPENDIAN_INT32(data);
+
+ if (LOG_LFB) LOG(LOG_VOODOO,LOG_WARN)("VOODOO.LFB:read (%d,%d) = %08X\n", x, y, data);
+ return data;
+}
+
+
+void voodoo_w(UINT32 offset, UINT32 data, UINT32 mask) {
+ if ((offset & (0xc00000/4)) == 0)
+ register_w(offset, data);
+ else if ((offset & (0x800000/4)) == 0)
+ lfb_w(offset, data, mask);
+ else
+ texture_w(offset, data);
+}
+
+UINT32 voodoo_r(UINT32 offset) {
+ if ((offset & (0xc00000/4)) == 0)
+ return register_r(offset);
+ else if ((offset & (0x800000/4)) == 0)
+ return lfb_r(offset);
+
+ return 0xffffffff;
+}
+
+
+
+/***************************************************************************
+ DEVICE INTERFACE
+***************************************************************************/
+
+/*-------------------------------------------------
+ device start callback
+-------------------------------------------------*/
+
+void voodoo_init(int type) {
+ v->active = false;
+
+ v->type = VOODOO_1;
+
+ switch (type) {
+ case VOODOO_1:
+ break;
+ case VOODOO_1_DTMU:
+ v->type = VOODOO_1_DTMU;
+ break;
+ case VOODOO_2:
+ v->type = VOODOO_2;
+ break;
+ default:
+ LOG_MSG("invalid voodoo card type initialization [%x]",type);
+ break;
+ }
+
+ memset(v->reg, 0, sizeof(v->reg));
+
+ v->fbi.vblank_flush_pending = false;
+ v->pci.op_pending = false;
+ v->dac.read_result = 0;
+
+ v->output_on = false;
+ v->clock_enabled = false;
+ v->ogl_dimchange = true;
+ v->send_config = false;
+
+ memset(v->dac.reg, 0, sizeof(v->dac.reg));
+
+ v->next_rasterizer = 0;
+ for (UINT32 rct=0; rct<MAX_RASTERIZERS; rct++)
+ v->rasterizer[rct] = raster_info();
+
+ v->thread_stats = new stats_block[1];
+ v->thread_stats[0].pixels_in = 0;
+ v->thread_stats[0].pixels_out = 0;
+ v->thread_stats[0].chroma_fail = 0;
+ v->thread_stats[0].zfunc_fail = 0;
+ v->thread_stats[0].afunc_fail = 0;
+ v->thread_stats[0].clip_fail = 0;
+ v->thread_stats[0].stipple_count = 0;
+
+ v->alt_regmap = false;
+ v->regnames = voodoo_reg_name;
+
+ /* create a table of precomputed 1/n and log2(n) values */
+ /* n ranges from 1.0000 to 2.0000 */
+ for (UINT32 val = 0; val <= (1 << RECIPLOG_LOOKUP_BITS); val++)
+ {
+ UINT32 value = (1 << RECIPLOG_LOOKUP_BITS) + val;
+ voodoo_reciplog[val*2 + 0] = (1 << (RECIPLOG_LOOKUP_PREC + RECIPLOG_LOOKUP_BITS)) / value;
+ voodoo_reciplog[val*2 + 1] = (UINT32)(LOGB2((double)value / (double)(1 << RECIPLOG_LOOKUP_BITS)) * (double)(1 << RECIPLOG_LOOKUP_PREC));
+ }
+
+ for (UINT32 val = 0; val < RASTER_HASH_SIZE; val++)
+ v->raster_hash[val] = NULL;
+
+ /* create dithering tables */
+ for (UINT32 val = 0; val < 256*16*2; val++)
+ {
+ int g = (val >> 0) & 1;
+ int x = (val >> 1) & 3;
+ int color = (val >> 3) & 0xff;
+ int y = (val >> 11) & 3;
+
+ if (!g)
+ {
+ dither4_lookup[val] = (UINT8)(DITHER_RB(color, dither_matrix_4x4[y * 4 + x]) >> 3);
+ dither2_lookup[val] = (UINT8)(DITHER_RB(color, dither_matrix_2x2[y * 4 + x]) >> 3);
+ }
+ else
+ {
+ dither4_lookup[val] = (UINT8)(DITHER_G(color, dither_matrix_4x4[y * 4 + x]) >> 2);
+ dither2_lookup[val] = (UINT8)(DITHER_G(color, dither_matrix_2x2[y * 4 + x]) >> 2);
+ }
+ }
+
+ v->tmu_config = 0x11; // revision 1
+
+ UINT32 fbmemsize = 0;
+ UINT32 tmumem0 = 0;
+ UINT32 tmumem1 = 0;
+
+ /* configure type-specific values */
+ switch (v->type)
+ {
+ case VOODOO_1:
+ v->regaccess = voodoo_register_access;
+ fbmemsize = 2;
+ tmumem0 = 2;
+ break;
+
+ case VOODOO_1_DTMU:
+ v->regaccess = voodoo_register_access;
+ fbmemsize = 4;
+ tmumem0 = 4;
+ tmumem1 = 4;
+ break;
+/*
+ case VOODOO_2:
+ v->regaccess = voodoo2_register_access;
+ fbmemsize = 4;
+ tmumem0 = 4;
+ tmumem1 = 4;
+ v->tmu_config |= 0x800;
+ break;
+*/
+ default:
+ E_Exit("Unsupported voodoo card in voodoo_start!");
+ break;
+ }
+
+ if (tmumem1 != 0)
+ v->tmu_config |= 0xc0; // two TMUs
+
+ v->chipmask = 0x01;
+
+ /* set up the PCI FIFO */
+ v->pci.fifo.size = 64*2;
+
+ /* set up frame buffer */
+ init_fbi(v, &v->fbi, fbmemsize << 20);
+
+ v->fbi.rowpixels = v->fbi.width;
+
+ v->tmu[0].ncc[0].palette = NULL;
+ v->tmu[0].ncc[1].palette = NULL;
+ v->tmu[1].ncc[0].palette = NULL;
+ v->tmu[1].ncc[1].palette = NULL;
+ v->tmu[0].ncc[0].palettea = NULL;
+ v->tmu[0].ncc[1].palettea = NULL;
+ v->tmu[1].ncc[0].palettea = NULL;
+ v->tmu[1].ncc[1].palettea = NULL;
+
+ v->tmu[0].ram = NULL;
+ v->tmu[1].ram = NULL;
+ v->tmu[0].lookup = NULL;
+ v->tmu[1].lookup = NULL;
+
+ /* build shared TMU tables */
+ init_tmu_shared(&v->tmushare);
+
+ /* set up the TMUs */
+ init_tmu(v, &v->tmu[0], &v->reg[0x100], tmumem0 << 20);
+ v->chipmask |= 0x02;
+ if (tmumem1 != 0)
+ {
+ init_tmu(v, &v->tmu[1], &v->reg[0x200], tmumem1 << 20);
+ v->chipmask |= 0x04;
+ v->tmu_config |= 0x40;
+ }
+
+ /* initialize some registers */
+ v->pci.init_enable = 0;
+ v->reg[fbiInit0].u = (UINT32)((1 << 4) | (0x10 << 6));
+ v->reg[fbiInit1].u = (UINT32)((1 << 1) | (1 << 8) | (1 << 12) | (2 << 20));
+ v->reg[fbiInit2].u = (UINT32)((1 << 6) | (0x100 << 23));
+ v->reg[fbiInit3].u = (UINT32)((2 << 13) | (0xf << 17));
+ v->reg[fbiInit4].u = (UINT32)(1 << 0);
+
+ /* do a soft reset to reset everything else */
+ soft_reset(v);
+
+ recompute_video_memory(v);
+}
+
+void voodoo_shutdown() {
+ if (v->ogl)
+ voodoo_ogl_shutdown(v);
+
+ if (v!=NULL) {
+ free(v->fbi.ram);
+ if (v->tmu[0].ram != NULL) {
+ free(v->tmu[0].ram);
+ v->tmu[0].ram = NULL;
+ }
+ if (v->tmu[1].ram != NULL) {
+ free(v->tmu[1].ram);
+ v->tmu[1].ram = NULL;
+ }
+ delete[] v->thread_stats;
+ v->active=false;
+ }
+}
+
+
+/***************************************************************************
+ COMMAND HANDLERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ fastfill - execute the 'fastfill'
+ command
+-------------------------------------------------*/
+
+void fastfill(voodoo_state *v)
+{
+ int sx = (v->reg[clipLeftRight].u >> 16) & 0x3ff;
+ int ex = (v->reg[clipLeftRight].u >> 0) & 0x3ff;
+ int sy = (v->reg[clipLowYHighY].u >> 16) & 0x3ff;
+ int ey = (v->reg[clipLowYHighY].u >> 0) & 0x3ff;
+
+ poly_extent extents[64];
+ UINT16 dithermatrix[16];
+ UINT16 *drawbuf = NULL;
+ int extnum, x, y;
+
+ /* if we're not clearing either, take no time */
+ if (!FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u) && !FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u))
+ return;
+
+ /* are we clearing the RGB buffer? */
+ if (FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u))
+ {
+ /* determine the draw buffer */
+ int destbuf = FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ break;
+
+ case 1: /* back buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ break;
+
+ default: /* reserved */
+ break;
+ }
+
+ /* determine the dither pattern */
+ for (y = 0; y < 4; y++)
+ {
+ DECLARE_DITHER_POINTERS;
+ COMPUTE_DITHER_POINTERS(v->reg[fbzMode].u, y);
+ for (x = 0; x < 4; x++)
+ {
+ int r = v->reg[color1].rgb.r;
+ int g = v->reg[color1].rgb.g;
+ int b = v->reg[color1].rgb.b;
+
+ APPLY_DITHER(v->reg[fbzMode].u, x, dither_lookup, r, g, b);
+ dithermatrix[y*4 + x] = (UINT16)((r << 11) | (g << 5) | b);
+ }
+ }
+ }
+
+ /* fill in a block of extents */
+ extents[0].startx = sx;
+ extents[0].stopx = ex;
+ for (extnum = 1; extnum < (int)ARRAY_LENGTH(extents); extnum++)
+ extents[extnum] = extents[0];
+
+ poly_extra_data *extra = new poly_extra_data;
+
+ if (v->ogl && v->active) {
+ voodoo_ogl_fastfill();
+ } else {
+
+ /* iterate over blocks of extents */
+ for (y = sy; y < ey; y += ARRAY_LENGTH(extents))
+ {
+ int count = MIN(ey - y, (int)ARRAY_LENGTH(extents));
+
+ extra->state = v;
+ memcpy(extra->dither, dithermatrix, sizeof(extra->dither));
+
+ poly_render_triangle_custom(drawbuf, y, count, extents, extra);
+ }
+ }
+ delete extra;
+}
+
+
+/*-------------------------------------------------
+ swapbuffer - execute the 'swapbuffer'
+ command
+-------------------------------------------------*/
+
+void swapbuffer(voodoo_state *v, UINT32 data)
+{
+ /* set the don't swap value for Voodoo 2 */
+ v->fbi.vblank_dont_swap = ((data >> 9) & 1)>0;
+
+ voodoo_swap_buffers(v);
+}
+
+
+/*-------------------------------------------------
+ triangle - execute the 'triangle'
+ command
+-------------------------------------------------*/
+
+void triangle(voodoo_state *v)
+{
+ int texcount = 0;
+ UINT16 *drawbuf;
+ int destbuf;
+
+ /* determine the number of TMUs involved */
+ texcount = 0;
+ if (!FBIINIT3_DISABLE_TMUS(v->reg[fbiInit3].u) && FBZCP_TEXTURE_ENABLE(v->reg[fbzColorPath].u))
+ {
+ texcount = 1;
+ if (v->chipmask & 0x04)
+ texcount = 2;
+ }
+
+ /* perform subpixel adjustments */
+ // ????????
+ if (!v->ogl && FBZCP_CCA_SUBPIXEL_ADJUST(v->reg[fbzColorPath].u))
+// if (FBZCP_CCA_SUBPIXEL_ADJUST(v->reg[fbzColorPath].u))
+ {
+ INT32 dx = 8 - (v->fbi.ax & 15);
+ INT32 dy = 8 - (v->fbi.ay & 15);
+
+ /* adjust iterated R,G,B,A and W/Z */
+ v->fbi.startr += (dy * v->fbi.drdy + dx * v->fbi.drdx) >> 4;
+ v->fbi.startg += (dy * v->fbi.dgdy + dx * v->fbi.dgdx) >> 4;
+ v->fbi.startb += (dy * v->fbi.dbdy + dx * v->fbi.dbdx) >> 4;
+ v->fbi.starta += (dy * v->fbi.dady + dx * v->fbi.dadx) >> 4;
+ v->fbi.startw += (dy * v->fbi.dwdy + dx * v->fbi.dwdx) >> 4;
+ v->fbi.startz += mul_32x32_shift(dy, v->fbi.dzdy, 4) + mul_32x32_shift(dx, v->fbi.dzdx, 4);
+
+ /* adjust iterated W/S/T for TMU 0 */
+ if (texcount >= 1)
+ {
+ v->tmu[0].startw += (dy * v->tmu[0].dwdy + dx * v->tmu[0].dwdx) >> 4;
+ v->tmu[0].starts += (dy * v->tmu[0].dsdy + dx * v->tmu[0].dsdx) >> 4;
+ v->tmu[0].startt += (dy * v->tmu[0].dtdy + dx * v->tmu[0].dtdx) >> 4;
+
+ /* adjust iterated W/S/T for TMU 1 */
+ if (texcount >= 2)
+ {
+ v->tmu[1].startw += (dy * v->tmu[1].dwdy + dx * v->tmu[1].dwdx) >> 4;
+ v->tmu[1].starts += (dy * v->tmu[1].dsdy + dx * v->tmu[1].dsdx) >> 4;
+ v->tmu[1].startt += (dy * v->tmu[1].dtdy + dx * v->tmu[1].dtdx) >> 4;
+ }
+ }
+ }
+
+ /* determine the draw buffer */
+ destbuf = FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ break;
+
+ case 1: /* back buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ break;
+
+ default: /* reserved */
+ return;
+ }
+
+ /* find a rasterizer that matches our current state */
+ triangle_create_work_item(v, drawbuf, texcount);
+
+ /* update stats */
+ v->reg[fbiTrianglesOut].u++;
+}
+
+
+/*-------------------------------------------------
+ begin_triangle - execute the 'beginTri'
+ command
+-------------------------------------------------*/
+
+static void begin_triangle(voodoo_state *v)
+{
+ setup_vertex *sv = &v->fbi.svert[2];
+
+ /* extract all the data from registers */
+ sv->x = v->reg[sVx].f;
+ sv->y = v->reg[sVy].f;
+ sv->wb = v->reg[sWb].f;
+ sv->w0 = v->reg[sWtmu0].f;
+ sv->s0 = v->reg[sS_W0].f;
+ sv->t0 = v->reg[sT_W0].f;
+ sv->w1 = v->reg[sWtmu1].f;
+ sv->s1 = v->reg[sS_Wtmu1].f;
+ sv->t1 = v->reg[sT_Wtmu1].f;
+ sv->a = v->reg[sAlpha].f;
+ sv->r = v->reg[sRed].f;
+ sv->g = v->reg[sGreen].f;
+ sv->b = v->reg[sBlue].f;
+
+ /* spread it across all three verts and reset the count */
+ v->fbi.svert[0] = v->fbi.svert[1] = v->fbi.svert[2];
+ v->fbi.sverts = 1;
+}
+
+
+/*-------------------------------------------------
+ draw_triangle - execute the 'DrawTri'
+ command
+-------------------------------------------------*/
+
+static void draw_triangle(voodoo_state *v)
+{
+ setup_vertex *sv = &v->fbi.svert[2];
+
+ /* for strip mode, shuffle vertex 1 down to 0 */
+ if (!(v->reg[sSetupMode].u & (1 << 16)))
+ v->fbi.svert[0] = v->fbi.svert[1];
+
+ /* copy 2 down to 1 regardless */
+ v->fbi.svert[1] = v->fbi.svert[2];
+
+ /* extract all the data from registers */
+ sv->x = v->reg[sVx].f;
+ sv->y = v->reg[sVy].f;
+ sv->wb = v->reg[sWb].f;
+ sv->w0 = v->reg[sWtmu0].f;
+ sv->s0 = v->reg[sS_W0].f;
+ sv->t0 = v->reg[sT_W0].f;
+ sv->w1 = v->reg[sWtmu1].f;
+ sv->s1 = v->reg[sS_Wtmu1].f;
+ sv->t1 = v->reg[sT_Wtmu1].f;
+ sv->a = v->reg[sAlpha].f;
+ sv->r = v->reg[sRed].f;
+ sv->g = v->reg[sGreen].f;
+ sv->b = v->reg[sBlue].f;
+
+ /* if we have enough verts, go ahead and draw */
+ if (++v->fbi.sverts >= 3)
+ setup_and_draw_triangle(v);
+}
+
+
+/*-------------------------------------------------
+ setup_and_draw_triangle - process the setup
+ parameters and render the triangle
+-------------------------------------------------*/
+
+static void setup_and_draw_triangle(voodoo_state *v)
+{
+ float dx1, dy1, dx2, dy2;
+ float divisor, tdiv;
+
+ /* grab the X/Ys at least */
+ v->fbi.ax = (INT16)(v->fbi.svert[0].x * 16.0);
+ v->fbi.ay = (INT16)(v->fbi.svert[0].y * 16.0);
+ v->fbi.bx = (INT16)(v->fbi.svert[1].x * 16.0);
+ v->fbi.by = (INT16)(v->fbi.svert[1].y * 16.0);
+ v->fbi.cx = (INT16)(v->fbi.svert[2].x * 16.0);
+ v->fbi.cy = (INT16)(v->fbi.svert[2].y * 16.0);
+
+ /* compute the divisor */
+ divisor = 1.0f / ((v->fbi.svert[0].x - v->fbi.svert[1].x) * (v->fbi.svert[0].y - v->fbi.svert[2].y) -
+ (v->fbi.svert[0].x - v->fbi.svert[2].x) * (v->fbi.svert[0].y - v->fbi.svert[1].y));
+
+ /* backface culling */
+ if (v->reg[sSetupMode].u & 0x20000)
+ {
+ int culling_sign = (v->reg[sSetupMode].u >> 18) & 1;
+ int divisor_sign = (divisor < 0);
+
+ /* if doing strips and ping pong is enabled, apply the ping pong */
+ if ((v->reg[sSetupMode].u & 0x90000) == 0x00000)
+ culling_sign ^= (v->fbi.sverts - 3) & 1;
+
+ /* if our sign matches the culling sign, we're done for */
+ if (divisor_sign == culling_sign)
+ return;
+ }
+
+ /* compute the dx/dy values */
+ dx1 = v->fbi.svert[0].y - v->fbi.svert[2].y;
+ dx2 = v->fbi.svert[0].y - v->fbi.svert[1].y;
+ dy1 = v->fbi.svert[0].x - v->fbi.svert[1].x;
+ dy2 = v->fbi.svert[0].x - v->fbi.svert[2].x;
+
+ /* set up R,G,B */
+ tdiv = divisor * 4096.0f;
+ if (v->reg[sSetupMode].u & (1 << 0))
+ {
+ v->fbi.startr = (INT32)(v->fbi.svert[0].r * 4096.0f);
+ v->fbi.drdx = (INT32)(((v->fbi.svert[0].r - v->fbi.svert[1].r) * dx1 - (v->fbi.svert[0].r - v->fbi.svert[2].r) * dx2) * tdiv);
+ v->fbi.drdy = (INT32)(((v->fbi.svert[0].r - v->fbi.svert[2].r) * dy1 - (v->fbi.svert[0].r - v->fbi.svert[1].r) * dy2) * tdiv);
+ v->fbi.startg = (INT32)(v->fbi.svert[0].g * 4096.0f);
+ v->fbi.dgdx = (INT32)(((v->fbi.svert[0].g - v->fbi.svert[1].g) * dx1 - (v->fbi.svert[0].g - v->fbi.svert[2].g) * dx2) * tdiv);
+ v->fbi.dgdy = (INT32)(((v->fbi.svert[0].g - v->fbi.svert[2].g) * dy1 - (v->fbi.svert[0].g - v->fbi.svert[1].g) * dy2) * tdiv);
+ v->fbi.startb = (INT32)(v->fbi.svert[0].b * 4096.0f);
+ v->fbi.dbdx = (INT32)(((v->fbi.svert[0].b - v->fbi.svert[1].b) * dx1 - (v->fbi.svert[0].b - v->fbi.svert[2].b) * dx2) * tdiv);
+ v->fbi.dbdy = (INT32)(((v->fbi.svert[0].b - v->fbi.svert[2].b) * dy1 - (v->fbi.svert[0].b - v->fbi.svert[1].b) * dy2) * tdiv);
+ }
+
+ /* set up alpha */
+ if (v->reg[sSetupMode].u & (1 << 1))
+ {
+ v->fbi.starta = (INT32)(v->fbi.svert[0].a * 4096.0);
+ v->fbi.dadx = (INT32)(((v->fbi.svert[0].a - v->fbi.svert[1].a) * dx1 - (v->fbi.svert[0].a - v->fbi.svert[2].a) * dx2) * tdiv);
+ v->fbi.dady = (INT32)(((v->fbi.svert[0].a - v->fbi.svert[2].a) * dy1 - (v->fbi.svert[0].a - v->fbi.svert[1].a) * dy2) * tdiv);
+ }
+
+ /* set up Z */
+ if (v->reg[sSetupMode].u & (1 << 2))
+ {
+ v->fbi.startz = (INT32)(v->fbi.svert[0].z * 4096.0);
+ v->fbi.dzdx = (INT32)(((v->fbi.svert[0].z - v->fbi.svert[1].z) * dx1 - (v->fbi.svert[0].z - v->fbi.svert[2].z) * dx2) * tdiv);
+ v->fbi.dzdy = (INT32)(((v->fbi.svert[0].z - v->fbi.svert[2].z) * dy1 - (v->fbi.svert[0].z - v->fbi.svert[1].z) * dy2) * tdiv);
+ }
+
+ /* set up Wb */
+ tdiv = divisor * 65536.0f * 65536.0f;
+ if (v->reg[sSetupMode].u & (1 << 3))
+ {
+ v->fbi.startw = v->tmu[0].startw = v->tmu[1].startw = (INT64)(v->fbi.svert[0].wb * 65536.0f * 65536.0f);
+ v->fbi.dwdx = v->tmu[0].dwdx = v->tmu[1].dwdx = (INT64)(((v->fbi.svert[0].wb - v->fbi.svert[1].wb) * dx1 - (v->fbi.svert[0].wb - v->fbi.svert[2].wb) * dx2) * tdiv);
+ v->fbi.dwdy = v->tmu[0].dwdy = v->tmu[1].dwdy = (INT64)(((v->fbi.svert[0].wb - v->fbi.svert[2].wb) * dy1 - (v->fbi.svert[0].wb - v->fbi.svert[1].wb) * dy2) * tdiv);
+ }
+
+ /* set up W0 */
+ if (v->reg[sSetupMode].u & (1 << 4))
+ {
+ v->tmu[0].startw = v->tmu[1].startw = (INT64)(v->fbi.svert[0].w0 * 65536.0f * 65536.0f);
+ v->tmu[0].dwdx = v->tmu[1].dwdx = (INT64)(((v->fbi.svert[0].w0 - v->fbi.svert[1].w0) * dx1 - (v->fbi.svert[0].w0 - v->fbi.svert[2].w0) * dx2) * tdiv);
+ v->tmu[0].dwdy = v->tmu[1].dwdy = (INT64)(((v->fbi.svert[0].w0 - v->fbi.svert[2].w0) * dy1 - (v->fbi.svert[0].w0 - v->fbi.svert[1].w0) * dy2) * tdiv);
+ }
+
+ /* set up S0,T0 */
+ if (v->reg[sSetupMode].u & (1 << 5))
+ {
+ v->tmu[0].starts = v->tmu[1].starts = (INT64)(v->fbi.svert[0].s0 * 65536.0f * 65536.0f);
+ v->tmu[0].dsdx = v->tmu[1].dsdx = (INT64)(((v->fbi.svert[0].s0 - v->fbi.svert[1].s0) * dx1 - (v->fbi.svert[0].s0 - v->fbi.svert[2].s0) * dx2) * tdiv);
+ v->tmu[0].dsdy = v->tmu[1].dsdy = (INT64)(((v->fbi.svert[0].s0 - v->fbi.svert[2].s0) * dy1 - (v->fbi.svert[0].s0 - v->fbi.svert[1].s0) * dy2) * tdiv);
+ v->tmu[0].startt = v->tmu[1].startt = (INT64)(v->fbi.svert[0].t0 * 65536.0f * 65536.0f);
+ v->tmu[0].dtdx = v->tmu[1].dtdx = (INT64)(((v->fbi.svert[0].t0 - v->fbi.svert[1].t0) * dx1 - (v->fbi.svert[0].t0 - v->fbi.svert[2].t0) * dx2) * tdiv);
+ v->tmu[0].dtdy = v->tmu[1].dtdy = (INT64)(((v->fbi.svert[0].t0 - v->fbi.svert[2].t0) * dy1 - (v->fbi.svert[0].t0 - v->fbi.svert[1].t0) * dy2) * tdiv);
+ }
+
+ /* set up W1 */
+ if (v->reg[sSetupMode].u & (1 << 6))
+ {
+ v->tmu[1].startw = (INT64)(v->fbi.svert[0].w1 * 65536.0f * 65536.0f);
+ v->tmu[1].dwdx = (INT64)(((v->fbi.svert[0].w1 - v->fbi.svert[1].w1) * dx1 - (v->fbi.svert[0].w1 - v->fbi.svert[2].w1) * dx2) * tdiv);
+ v->tmu[1].dwdy = (INT64)(((v->fbi.svert[0].w1 - v->fbi.svert[2].w1) * dy1 - (v->fbi.svert[0].w1 - v->fbi.svert[1].w1) * dy2) * tdiv);
+ }
+
+ /* set up S1,T1 */
+ if (v->reg[sSetupMode].u & (1 << 7))
+ {
+ v->tmu[1].starts = (INT64)(v->fbi.svert[0].s1 * 65536.0f * 65536.0f);
+ v->tmu[1].dsdx = (INT64)(((v->fbi.svert[0].s1 - v->fbi.svert[1].s1) * dx1 - (v->fbi.svert[0].s1 - v->fbi.svert[2].s1) * dx2) * tdiv);
+ v->tmu[1].dsdy = (INT64)(((v->fbi.svert[0].s1 - v->fbi.svert[2].s1) * dy1 - (v->fbi.svert[0].s1 - v->fbi.svert[1].s1) * dy2) * tdiv);
+ v->tmu[1].startt = (INT64)(v->fbi.svert[0].t1 * 65536.0f * 65536.0f);
+ v->tmu[1].dtdx = (INT64)(((v->fbi.svert[0].t1 - v->fbi.svert[1].t1) * dx1 - (v->fbi.svert[0].t1 - v->fbi.svert[2].t1) * dx2) * tdiv);
+ v->tmu[1].dtdy = (INT64)(((v->fbi.svert[0].t1 - v->fbi.svert[2].t1) * dy1 - (v->fbi.svert[0].t1 - v->fbi.svert[1].t1) * dy2) * tdiv);
+ }
+
+ /* draw the triangle */
+ triangle(v);
+}
+
+
+/*-------------------------------------------------
+ triangle_create_work_item - finish triangle
+ setup and create the work item
+-------------------------------------------------*/
+
+void triangle_create_work_item(voodoo_state *v, UINT16 *drawbuf, int texcount)
+{
+ poly_extra_data *extra = new poly_extra_data;
+ raster_info *info = find_rasterizer(v, texcount);
+ poly_vertex vert[3];
+
+ /* fill in the vertex data */
+ vert[0].x = (float)v->fbi.ax * (1.0f / 16.0f);
+ vert[0].y = (float)v->fbi.ay * (1.0f / 16.0f);
+ vert[1].x = (float)v->fbi.bx * (1.0f / 16.0f);
+ vert[1].y = (float)v->fbi.by * (1.0f / 16.0f);
+ vert[2].x = (float)v->fbi.cx * (1.0f / 16.0f);
+ vert[2].y = (float)v->fbi.cy * (1.0f / 16.0f);
+
+ /* fill in the extra data */
+ extra->state = v;
+ extra->info = info;
+
+ /* fill in triangle parameters */
+ extra->ax = v->fbi.ax;
+ extra->ay = v->fbi.ay;
+ extra->startr = v->fbi.startr;
+ extra->startg = v->fbi.startg;
+ extra->startb = v->fbi.startb;
+ extra->starta = v->fbi.starta;
+ extra->startz = v->fbi.startz;
+ extra->startw = v->fbi.startw;
+ extra->drdx = v->fbi.drdx;
+ extra->dgdx = v->fbi.dgdx;
+ extra->dbdx = v->fbi.dbdx;
+ extra->dadx = v->fbi.dadx;
+ extra->dzdx = v->fbi.dzdx;
+ extra->dwdx = v->fbi.dwdx;
+ extra->drdy = v->fbi.drdy;
+ extra->dgdy = v->fbi.dgdy;
+ extra->dbdy = v->fbi.dbdy;
+ extra->dady = v->fbi.dady;
+ extra->dzdy = v->fbi.dzdy;
+ extra->dwdy = v->fbi.dwdy;
+
+ /* fill in texture 0 parameters */
+ if (texcount > 0)
+ {
+ extra->starts0 = v->tmu[0].starts;
+ extra->startt0 = v->tmu[0].startt;
+ extra->startw0 = v->tmu[0].startw;
+ extra->ds0dx = v->tmu[0].dsdx;
+ extra->dt0dx = v->tmu[0].dtdx;
+ extra->dw0dx = v->tmu[0].dwdx;
+ extra->ds0dy = v->tmu[0].dsdy;
+ extra->dt0dy = v->tmu[0].dtdy;
+ extra->dw0dy = v->tmu[0].dwdy;
+ extra->lodbase0 = prepare_tmu(&v->tmu[0]);
+
+ /* fill in texture 1 parameters */
+ if (texcount > 1)
+ {
+ extra->starts1 = v->tmu[1].starts;
+ extra->startt1 = v->tmu[1].startt;
+ extra->startw1 = v->tmu[1].startw;
+ extra->ds1dx = v->tmu[1].dsdx;
+ extra->dt1dx = v->tmu[1].dtdx;
+ extra->dw1dx = v->tmu[1].dwdx;
+ extra->ds1dy = v->tmu[1].dsdy;
+ extra->dt1dy = v->tmu[1].dtdy;
+ extra->dw1dy = v->tmu[1].dwdy;
+ extra->lodbase1 = prepare_tmu(&v->tmu[1]);
+ }
+ }
+
+ extra->texcount = texcount;
+ extra->r_fbzColorPath = v->reg[fbzColorPath].u;
+ extra->r_fbzMode = v->reg[fbzMode].u;
+ extra->r_alphaMode = v->reg[alphaMode].u;
+ extra->r_fogMode = v->reg[fogMode].u;
+ extra->r_textureMode0 = v->tmu[0].reg[textureMode].u;
+ if (v->tmu[1].ram != NULL) extra->r_textureMode1 = v->tmu[1].reg[textureMode].u;
+
+ info->polys++;
+
+ if (palette_changed && v->ogl && v->active) {
+ voodoo_ogl_invalidate_paltex();
+ palette_changed = false;
+ }
+
+ if (v->ogl && v->active) {
+ if (extra->info==NULL) {
+ delete extra;
+ return;
+ }
+ voodoo_ogl_draw_triangle(extra);
+ } else {
+ poly_render_triangle(drawbuf, info->callback, &vert[0], &vert[1], &vert[2], extra);
+ }
+
+ delete extra;
+}
+
+/***************************************************************************
+ RASTERIZER MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ add_rasterizer - add a rasterizer to our
+ hash table
+-------------------------------------------------*/
+
+static raster_info *add_rasterizer(voodoo_state *v, const raster_info *cinfo)
+{
+ raster_info *info = &v->rasterizer[v->next_rasterizer++];
+ int hash = compute_raster_hash(cinfo);
+
+ if (v->next_rasterizer > MAX_RASTERIZERS)
+ E_Exit("Out of space for new rasterizers!");
+
+ /* make a copy of the info */
+ *info = *cinfo;
+
+ /* fill in the data */
+ info->hits = 0;
+ info->polys = 0;
+
+ /* hook us into the hash table */
+ info->next = v->raster_hash[hash];
+ v->raster_hash[hash] = info;
+
+ if (LOG_RASTERIZERS)
+ LOG_MSG("Adding rasterizer @ %p : %08X %08X %08X %08X %08X %08X (hash=%d)\n",
+ (void *)info->callback,
+ info->eff_color_path, info->eff_alpha_mode, info->eff_fog_mode, info->eff_fbz_mode,
+ info->eff_tex_mode_0, info->eff_tex_mode_1, hash);
+
+ return info;
+}
+
+
+/*-------------------------------------------------
+ find_rasterizer - find a rasterizer that
+ matches our current parameters and return
+ it, creating a new one if necessary
+-------------------------------------------------*/
+
+static raster_info *find_rasterizer(voodoo_state *v, int texcount)
+{
+ raster_info *info, *prev = NULL;
+ raster_info curinfo;
+ int hash;
+
+ /* build an info struct with all the parameters */
+ curinfo.eff_color_path = normalize_color_path(v->reg[fbzColorPath].u);
+ curinfo.eff_alpha_mode = normalize_alpha_mode(v->reg[alphaMode].u);
+ curinfo.eff_fog_mode = normalize_fog_mode(v->reg[fogMode].u);
+ curinfo.eff_fbz_mode = normalize_fbz_mode(v->reg[fbzMode].u);
+ curinfo.eff_tex_mode_0 = (texcount >= 1) ? normalize_tex_mode(v->tmu[0].reg[textureMode].u) : 0xffffffff;
+ curinfo.eff_tex_mode_1 = (texcount >= 2) ? normalize_tex_mode(v->tmu[1].reg[textureMode].u) : 0xffffffff;
+
+ /* compute the hash */
+ hash = compute_raster_hash(&curinfo);
+
+ /* find the appropriate hash entry */
+ for (info = v->raster_hash[hash]; info; prev = info, info = info->next)
+ if (info->eff_color_path == curinfo.eff_color_path &&
+ info->eff_alpha_mode == curinfo.eff_alpha_mode &&
+ info->eff_fog_mode == curinfo.eff_fog_mode &&
+ info->eff_fbz_mode == curinfo.eff_fbz_mode &&
+ info->eff_tex_mode_0 == curinfo.eff_tex_mode_0 &&
+ info->eff_tex_mode_1 == curinfo.eff_tex_mode_1)
+ {
+ /* got it, move us to the head of the list */
+ if (prev)
+ {
+ prev->next = info->next;
+ info->next = v->raster_hash[hash];
+ v->raster_hash[hash] = info;
+ }
+
+ /* return the result */
+ return info;
+ }
+
+ /* generate a new one using the generic entry */
+ curinfo.callback = (texcount == 0) ? raster_generic_0tmu : (texcount == 1) ? raster_generic_1tmu : raster_generic_2tmu;
+ curinfo.is_generic = true;
+ curinfo.display = 0;
+ curinfo.polys = 0;
+ curinfo.hits = 0;
+ curinfo.next = 0;
+ curinfo.shader_ready = false;
+
+ return add_rasterizer(v, &curinfo);
+}
+
+
+/***************************************************************************
+ GENERIC RASTERIZERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ raster_fastfill - per-scanline
+ implementation of the 'fastfill' command
+-------------------------------------------------*/
+
+static void raster_fastfill(void *destbase, INT32 y, const poly_extent *extent, const void *extradata)
+{
+ const poly_extra_data *extra = (const poly_extra_data *)extradata;
+ voodoo_state *v = extra->state;
+ stats_block *stats = &v->thread_stats[0];
+ INT32 startx = extent->startx;
+ INT32 stopx = extent->stopx;
+ int scry, x;
+
+ /* determine the screen Y */
+ scry = y;
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* fill this RGB row */
+ if (FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u))
+ {
+ const UINT16 *ditherow = &extra->dither[(y & 3) * 4];
+ UINT64 expanded = *(UINT64 *)ditherow;
+ UINT16 *dest = (UINT16 *)destbase + scry * v->fbi.rowpixels;
+
+ for (x = startx; x < stopx && (x & 3) != 0; x++)
+ dest[x] = ditherow[x & 3];
+ for ( ; x < (stopx & ~3); x += 4)
+ *(UINT64 *)&dest[x] = expanded;
+ for ( ; x < stopx; x++)
+ dest[x] = ditherow[x & 3];
+ stats->pixels_out += stopx - startx;
+ }
+
+ /* fill this dest buffer row */
+ if (FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u) && v->fbi.auxoffs != (UINT32)(~0))
+ {
+ UINT16 color = (UINT16)(v->reg[zaColor].u & 0xffff);
+ UINT64 expanded = ((UINT64)color << 48) | ((UINT64)color << 32) | (color << 16) | color;
+ UINT16 *dest = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs) + scry * v->fbi.rowpixels;
+
+ if (v->fbi.auxoffs + 2 * (scry * v->fbi.rowpixels + stopx) >= v->fbi.mask) {
+ stopx = (v->fbi.mask - v->fbi.auxoffs) / 2 - scry * v->fbi.rowpixels;
+ if ((stopx < 0) || (stopx < startx)) return;
+ }
+
+ for (x = startx; x < stopx && (x & 3) != 0; x++)
+ dest[x] = color;
+ for ( ; x < (stopx & ~3); x += 4)
+ *(UINT64 *)&dest[x] = expanded;
+ for ( ; x < stopx; x++)
+ dest[x] = color;
+ }
+}
+
+
+void voodoo_vblank_flush(void) {
+ if (v->ogl)
+ voodoo_ogl_vblank_flush();
+ v->fbi.vblank_flush_pending=false;
+}
+
+void voodoo_set_window(void) {
+ if (v->ogl && v->active) {
+ voodoo_ogl_set_window(v);
+ }
+}
+
+void voodoo_leave(void) {
+ if (v->ogl) {
+#if C_OPENGL
+ voodoo_ogl_leave(true);
+#endif
+ }
+ v->active = false;
+}
+
+void voodoo_activate(void) {
+ v->active = true;
+
+ if (v->ogl) {
+ if (voodoo_ogl_init(v)) {
+ voodoo_ogl_clear();
+ } else {
+ v->ogl = false;
+ LOG_MSG("VOODOO: acceleration disabled");
+ }
+ }
+}
+
+void voodoo_update_dimensions(void) {
+ v->ogl_dimchange = false;
+
+#if C_OPENGL
+ if (v->ogl) {
+ voodoo_ogl_update_dimensions();
+ }
+#endif
+}
diff --git a/src/hardware/voodoo_emu.h b/src/hardware/voodoo_emu.h
new file mode 100644
index 000000000..46871e39e
--- /dev/null
+++ b/src/hardware/voodoo_emu.h
@@ -0,0 +1,61 @@
+ /*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VOODOO_EMU_H
+#define DOSBOX_VOODOO_EMU_H
+
+#include <stdlib.h>
+
+#include "dosbox.h"
+
+#include "voodoo_types.h"
+#include "voodoo_data.h"
+
+
+extern voodoo_state *v;
+
+void voodoo_w(UINT32 offset, UINT32 data, UINT32 mask);
+UINT32 voodoo_r(UINT32 offset);
+
+void voodoo_init(int type);
+void voodoo_shutdown();
+void voodoo_leave(void);
+
+void voodoo_activate(void);
+void voodoo_update_dimensions(void);
+void voodoo_set_window(void);
+
+void voodoo_vblank_flush(void);
+void voodoo_swap_buffers(voodoo_state *v);
+
+
+extern void Voodoo_UpdateScreenStart();
+extern void Voodoo_Output_Enable(bool enabled);
+extern bool Voodoo_GetRetrace();
+extern double Voodoo_GetVRetracePosition();
+extern double Voodoo_GetHRetracePosition();
+
+void GFX_TearDown(void);
+void GFX_SetResizeable(bool enable);
+void GFX_SwitchLazyFullscreen(bool lazy);
+
+//extern void CPU_Core_Dyn_X86_SaveDHFPUState(void);
+//extern void CPU_Core_Dyn_X86_RestoreDHFPUState(void);
+
+#endif
diff --git a/src/hardware/voodoo_interface.cpp b/src/hardware/voodoo_interface.cpp
new file mode 100644
index 000000000..bafe296f6
--- /dev/null
+++ b/src/hardware/voodoo_interface.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2002-2021 The DOSBox Team
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "dosbox.h"
+#include "cross.h"
+#include "logging.h"
+#include "vga.h"
+#include "pic.h"
+#include "paging.h"
+#include "render.h"
+
+#include "voodoo_interface.h"
+#include "voodoo_emu.h"
+
+
+static voodoo_draw vdraw;
+
+
+Voodoo_PageHandler * voodoo_pagehandler;
+
+
+uint8_t Voodoo_PageHandler::readb(PhysPt addr) {
+ (void)addr;//UNUSED
+// LOG_MSG("voodoo readb at %x",addr);
+ return (uint8_t)-1;
+}
+void Voodoo_PageHandler::writeb(PhysPt addr,uint8_t val) {
+ (void)addr;//UNUSED
+ (void)val;//UNUSED
+// LOG_MSG("voodoo writeb at %x",addr);
+}
+
+uint16_t Voodoo_PageHandler::readw(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr);
+ if (addr&1) {
+ LOG_MSG("voodoo readw unaligned");
+ return (uint16_t)-1;
+ }
+
+ uint32_t retval = voodoo_r((addr>>2)&0x3FFFFF);
+ if (addr&3)
+ retval >>= 16;
+ else
+ retval &= 0xffff;
+
+ return (uint16_t)retval;
+}
+
+void Voodoo_PageHandler::writew(PhysPt addr,uint16_t val) {
+ addr = PAGING_GetPhysicalAddress(addr);
+ if (addr&1u) {
+ LOG_MSG("voodoo writew unaligned");
+ return;
+ }
+
+ if (addr&3u)
+ voodoo_w((addr>>2u)&0x3FFFFFu,(UINT32)(val<<16u),0xffff0000u);
+ else
+ voodoo_w((addr>>2u)&0x3FFFFFu,val,0x0000ffffu);
+}
+
+uint32_t Voodoo_PageHandler::readd(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr);
+ if (!(addr&3)) {
+ return voodoo_r((addr>>2)&0x3FFFFF);
+ } else {
+ if (!(addr&1)) {
+ uint32_t low = voodoo_r((addr>>2)&0x3FFFFF);
+ uint32_t high = voodoo_r(((addr>>2)+1)&0x3FFFFF);
+ return (low>>16) | (high<<16);
+ } else {
+ LOG_MSG("voodoo readd unaligned");
+ }
+ }
+ return 0xffffffff;
+}
+
+void Voodoo_PageHandler::writed(PhysPt addr,uint32_t val) {
+ addr = PAGING_GetPhysicalAddress(addr);
+ if (!(addr&3)) {
+ voodoo_w((addr>>2)&0x3FFFFF,val,0xffffffff);
+ } else {
+ if (!(addr&1)) {
+ voodoo_w((addr>>2)&0x3FFFFF,val<<16,0xffff0000);
+ voodoo_w(((addr>>2)+1)&0x3FFFFF,val>>16,0x0000ffff);
+ } else {
+ uint32_t val1 = voodoo_r((addr>>2)&0x3FFFFF);
+ uint32_t val2 = voodoo_r(((addr>>2)+1)&0x3FFFFF);
+ if ((addr&3)==1) {
+ val1 = (val1&0xffffff) | ((val&0xff)<<24);
+ val2 = (val2&0xff000000) | (val>>8);
+ } else if ((addr&3)==3) {
+ val1 = (val1&0xff) | ((val&0xffffff)<<8);
+ val2 = (val2&0xffffff00) | (val>>24);
+ } else E_Exit("???");
+ voodoo_w((addr>>2)&0x3FFFFF,val1,0xffffffff);
+ voodoo_w(((addr>>2)+1)&0x3FFFFF,val2,0xffffffff);
+ }
+ }
+}
+
+
+static void Voodoo_VerticalTimer(uint32_t /*val*/) {
+ vdraw.frame_start = PIC_FullIndex();
+ PIC_AddEvent( Voodoo_VerticalTimer, vdraw.vfreq );
+
+ if (v->fbi.vblank_flush_pending) {
+ voodoo_vblank_flush();
+ if (GFX_LazyFullscreenRequested()) {
+ v->ogl_dimchange = true;
+ }
+ }
+
+ if (!v->ogl) {
+ if (!RENDER_StartUpdate()) return; // frameskip
+
+ rectangle r;
+ r.min_x = r.min_y = 0;
+ r.max_x = (int)v->fbi.width;
+ r.max_y = (int)v->fbi.height;
+
+ // draw all lines at once
+ uint16_t *viewbuf = (uint16_t *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ for(Bitu i = 0; i < v->fbi.height; i++) {
+ RENDER_DrawLine((uint8_t*) viewbuf);
+ viewbuf += v->fbi.rowpixels;
+ }
+ RENDER_EndUpdate(false);
+ } else {
+ // ???
+ voodoo_set_window();
+ }
+}
+
+bool Voodoo_GetRetrace() {
+ // TODO proper implementation
+ double time_in_frame = PIC_FullIndex() - vdraw.frame_start;
+ double vfreq = vdraw.vfreq;
+ if (vfreq <= 0.0) return false;
+ if (v->clock_enabled && v->output_on) {
+ if ((time_in_frame/vfreq) > 0.95) return true;
+ } else if (v->output_on) {
+ double rtime = time_in_frame/vfreq;
+ rtime = fmod(rtime, 1.0);
+ if (rtime > 0.95) return true;
+ }
+ return false;
+}
+
+double Voodoo_GetVRetracePosition() {
+ // TODO proper implementation
+ double time_in_frame = PIC_FullIndex() - vdraw.frame_start;
+ double vfreq = vdraw.vfreq;
+ if (vfreq <= 0.0) return 0.0;
+ if (v->clock_enabled && v->output_on) {
+ return time_in_frame/vfreq;
+ } else if (v->output_on) {
+ double rtime = time_in_frame/vfreq;
+ rtime = fmod(rtime, 1.0);
+ return rtime;
+ }
+ return 0.0;
+}
+
+double Voodoo_GetHRetracePosition() {
+ // TODO proper implementation
+ double time_in_frame = PIC_FullIndex() - vdraw.frame_start;
+ double hfreq = vdraw.vfreq*100.0;
+ if (hfreq <= 0.0) return 0.0;
+ if (v->clock_enabled && v->output_on) {
+ return time_in_frame/hfreq;
+ } else if (v->output_on) {
+ double rtime = time_in_frame/hfreq;
+ rtime = fmod(rtime, 1.0);
+ return rtime;
+ }
+ return 0.0;
+}
+
+static void Voodoo_UpdateScreen(void) {
+ // abort drawing
+ RENDER_EndUpdate(true);
+
+ if ((!v->clock_enabled || !v->output_on) && vdraw.override_on) {
+ // switching off
+ PIC_RemoveEvents(Voodoo_VerticalTimer);
+ voodoo_leave();
+
+ VGA_SetOverride(false);
+ vdraw.override_on=false;
+ }
+
+ if ((v->clock_enabled && v->output_on) && !vdraw.override_on) {
+ // switching on
+ PIC_RemoveEvents(Voodoo_VerticalTimer); // shouldn't be needed
+
+ // TODO proper implementation of refresh rates and timings
+ vdraw.vfreq = 1000.0f/60.0f;
+ VGA_SetOverride(true);
+ vdraw.override_on=true;
+
+ vdraw.width=v->fbi.width;
+ vdraw.height=v->fbi.height;
+
+ voodoo_activate();
+
+ if (v->ogl) {
+ v->ogl_dimchange = false;
+ } else {
+ RENDER_SetSize(v->fbi.width, v->fbi.height, 16, 1000.0f / vdraw.vfreq, 4.0/3.0, vga.draw.doublewidth, vga.draw.doubleheight);
+ }
+
+ Voodoo_VerticalTimer(0);
+ }
+
+ if ((v->clock_enabled && v->output_on) && v->ogl_dimchange) {
+ voodoo_update_dimensions();
+ }
+
+ vdraw.screen_update_requested = false;
+}
+
+static void Voodoo_CheckScreenUpdate(uint32_t /*val*/) {
+ vdraw.screen_update_pending = false;
+ if (vdraw.screen_update_requested) {
+ vdraw.screen_update_pending = true;
+ Voodoo_UpdateScreen();
+ PIC_AddEvent(Voodoo_CheckScreenUpdate, 100.0f);
+ }
+}
+
+void Voodoo_UpdateScreenStart() {
+ vdraw.screen_update_requested = true;
+ if (!vdraw.screen_update_pending) {
+ vdraw.screen_update_pending = true;
+ PIC_AddEvent(Voodoo_CheckScreenUpdate, 0.0f);
+ }
+}
+
+void Voodoo_Output_Enable(bool enabled) {
+ if (v->output_on != enabled) {
+ v->output_on = enabled;
+ Voodoo_UpdateScreenStart();
+ }
+}
+
+void Voodoo_Initialize(Bits emulation_type, Bits card_type, bool max_voodoomem) {
+ if ((emulation_type <= 0) || (emulation_type > 2)) return;
+
+ int board = VOODOO_1;
+
+ switch (card_type) {
+ case 1:
+ if (max_voodoomem) board = VOODOO_1_DTMU;
+ else board = VOODOO_1;
+ break;
+ case 2:
+// if (max_voodoomem) board = VOODOO_2_DTMU;
+// else
+ board = VOODOO_2;
+ break;
+ default:
+ E_Exit("invalid voodoo card type specified");
+ break;
+ }
+
+ voodoo_pagehandler = new Voodoo_PageHandler(0);
+
+ v = new voodoo_state;
+ v->ogl = false;
+ if (emulation_type == 2) v->ogl = true;
+
+ vdraw.vfreq = 1000.0f/60.0f;
+
+ voodoo_init(board);
+}
+
+void Voodoo_Shut_Down() {
+ voodoo_shutdown();
+
+ if (v != NULL) {
+ delete v;
+ v = NULL;
+ }
+ if (voodoo_pagehandler != NULL) {
+ delete voodoo_pagehandler;
+ voodoo_pagehandler = NULL;
+ }
+}
+
+void Voodoo_PCI_InitEnable(Bitu val) {
+ v->pci.init_enable = (UINT32)val;
+}
+
+void Voodoo_PCI_Enable(bool enable) {
+ v->clock_enabled = enable;
+ Voodoo_UpdateScreenStart();
+}
+
+PageHandler* Voodoo_GetPageHandler() {
+ return voodoo_pagehandler;
+}
diff --git a/src/hardware/voodoo_interface.h b/src/hardware/voodoo_interface.h
new file mode 100644
index 000000000..cf2a660c7
--- /dev/null
+++ b/src/hardware/voodoo_interface.h
@@ -0,0 +1,65 @@
+ /*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VOODOO_IF_H
+#define DOSBOX_VOODOO_IF_H
+
+typedef struct vdraw {
+ Bitu width;
+ Bitu height;
+ Bitu bpp;
+ float vfreq;
+ double frame_start;
+ bool doublewidth;
+ bool doubleheight;
+
+ bool override_on;
+
+ bool screen_update_requested;
+ bool screen_update_pending;
+} voodoo_draw;
+
+
+class Voodoo_PageHandler : public PageHandler {
+public:
+ Voodoo_PageHandler(HostPt /*addr*/){
+ flags=PFLAG_NOCODE;
+ }
+
+ ~Voodoo_PageHandler() {
+ }
+
+ uint8_t readb(PhysPt addr);
+ void writeb(PhysPt addr,uint8_t val);
+ uint16_t readw(PhysPt addr);
+ void writew(PhysPt addr,uint16_t val);
+ uint32_t readd(PhysPt addr);
+ void writed(PhysPt addr,uint32_t val);
+};
+
+
+void Voodoo_Initialize(Bits emulation_type, Bits card_type, bool max_voodoomem);
+void Voodoo_Shut_Down();
+
+void Voodoo_PCI_InitEnable(Bitu val);
+void Voodoo_PCI_Enable(bool enable);
+
+PageHandler* Voodoo_GetPageHandler();
+
+#endif
diff --git a/src/hardware/voodoo_opengl.cpp b/src/hardware/voodoo_opengl.cpp
new file mode 100644
index 000000000..18da2a781
--- /dev/null
+++ b/src/hardware/voodoo_opengl.cpp
@@ -0,0 +1,1925 @@
+/*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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 <stdlib.h>
+#include <math.h>
+#include <map>
+
+#include "dosbox.h"
+#include "video.h"
+
+#include "voodoo_emu.h"
+#include "voodoo_opengl.h"
+
+
+#if C_OPENGL
+
+#include "voodoo_def.h"
+
+
+SDL_Surface* ogl_surface = NULL;
+
+INT32 cached_line_front_y=-1;
+INT32 cached_line_front_width = -1;
+INT32 cached_line_front_length=-1;
+INT32 cached_line_front_pixels=-1;
+UINT32* cached_line_front_data=NULL;
+
+INT32 cached_line_back_y=-1;
+INT32 cached_line_back_width = -1;
+INT32 cached_line_back_length=-1;
+INT32 cached_line_back_pixels=-1;
+UINT32* cached_line_back_data=NULL;
+
+
+static UINT32 last_clear_color=0;
+
+static UINT32 last_width=0;
+static UINT32 last_height=0;
+static INT32 last_orientation=-1;
+UINT32 new_width = 0;
+UINT32 new_height = 0;
+
+static UINT32 ogl_texture_index = 1;
+
+
+/* texture cache buffer */
+UINT32 texrgb[256*256];
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wint-to-void-pointer-cast"
+#endif
+
+/* texture address map */
+std::map <const UINT32, ogl_texmap> textures[2];
+
+bool GFX_IsFullScreenSetting();
+SDL_Window* GFX_GetSDLWindow(void);
+SDL_Window* GFX_SetSDLWindowMode(uint16_t width, uint16_t height, SCREEN_TYPES screenType);
+
+static void ogl_get_depth(voodoo_state* VV, INT32 ITERZ, INT64 ITERW, INT32 *depthval, INT32 *out_wfloat)
+{
+ INT32 wfloat;
+ UINT32 FBZMODE = VV->reg[fbzMode].u;
+ UINT32 FBZCOLORPATH = VV->reg[fbzColorPath].u;
+
+ /* compute "floating point" W value (used for depth and fog) */
+ if ((ITERW) & LONGTYPE(0xffff00000000))
+ wfloat = 0x0000;
+ else
+ {
+ UINT32 temp = (UINT32)(ITERW);
+ if ((temp & 0xffff0000) == 0)
+ wfloat = 0xffff;
+ else
+ {
+ int exp = count_leading_zeros(temp);
+ wfloat = ((exp << 12) | ((~temp >> (19 - exp)) & 0xfff));
+ if (wfloat < 0xffff) wfloat++;
+ }
+ }
+
+ /* compute depth value (W or Z) for this pixel */
+ if (FBZMODE_WBUFFER_SELECT(FBZMODE) == 0)
+ CLAMPED_Z(ITERZ, FBZCOLORPATH, *depthval);
+ else if (FBZMODE_DEPTH_FLOAT_SELECT(FBZMODE) == 0)
+ *depthval = wfloat;
+ else
+ {
+ if ((ITERZ) & 0xf0000000)
+ *depthval = 0x0000;
+ else
+ {
+ UINT32 temp = (ITERZ) << 4;
+ if ((temp & 0xffff0000) == 0)
+ *depthval = 0xffff;
+ else
+ {
+ int exp = count_leading_zeros(temp);
+ *depthval = ((exp << 12) | ((~temp >> (19 - exp)) & 0xfff));
+ if (*depthval < 0xffff) (*depthval)++;
+ }
+ }
+ }
+
+ /* add the bias */
+ if (FBZMODE_ENABLE_DEPTH_BIAS(FBZMODE))
+ {
+ *depthval += (INT16)(VV)->reg[zaColor].u;
+ CLAMP(*depthval, 0, 0xffff);
+ }
+
+ *out_wfloat = wfloat;
+}
+
+void ogl_get_fog_blend(voodoo_state* v, INT32 wfloat, INT32 ITERZ, INT64 ITERW, INT32 *fogblend)
+{
+ UINT32 FOGMODE = v->reg[fogMode].u;
+ UINT32 FBZCP = v->reg[fbzColorPath].u;
+
+ *fogblend = 0;
+ if (FOGMODE_ENABLE_FOG(FOGMODE) && !FOGMODE_FOG_CONSTANT(FOGMODE))
+ {
+ /* fog blending mode */
+ switch (FOGMODE_FOG_ZALPHA(FOGMODE))
+ {
+ case 0: /* fog table */
+ {
+ INT32 delta = v->fbi.fogdelta[wfloat >> 10];
+ INT32 deltaval;
+
+ /* perform the multiply against lower 8 bits of wfloat */
+ deltaval = (delta & v->fbi.fogdelta_mask) *
+ ((wfloat >> 2) & 0xff);
+
+ /* fog zones allow for negating this value */
+ if (FOGMODE_FOG_ZONES(FOGMODE) && (delta & 2))
+ deltaval = -deltaval;
+ deltaval >>= 6;
+
+ /* apply dither */
+// if (FOGMODE_FOG_DITHER(FOGMODE))
+// deltaval += DITHER4[(XX) & 3];
+ deltaval >>= 4;
+
+ /* add to the blending factor */
+ *fogblend = v->fbi.fogblend[wfloat >> 10] + deltaval;
+ break;
+ }
+
+ case 1: /* iterated A */
+// fogblend = ITERAXXX.rgb.a;
+ break;
+
+ case 2: /* iterated Z */
+ CLAMPED_Z((ITERZ), FBZCP, *fogblend);
+ *fogblend >>= 8;
+ break;
+
+ case 3: /* iterated W - Voodoo 2 only */
+ CLAMPED_W((ITERW), FBZCP, *fogblend);
+ break;
+ }
+
+ }
+}
+
+void ogl_get_vertex_data(INT32 x, INT32 y, const void *extradata, ogl_vertex_data *vd) {
+ const poly_extra_data *extra = (const poly_extra_data *)extradata;
+ voodoo_state *v=(voodoo_state*)extra->state;
+ INT32 iterr, iterg, iterb, itera;
+ INT32 iterz;
+ INT64 iterw;
+ INT32 dx, dy;
+ INT32 d;
+
+ dx = x - extra->ax;
+ dy = y - extra->ay;
+ iterr = extra->startr + ((dy * extra->drdy)>>4) + ((dx * extra->drdx)>>4);
+ iterg = extra->startg + ((dy * extra->dgdy)>>4) + ((dx * extra->dgdx)>>4);
+ iterb = extra->startb + ((dy * extra->dbdy)>>4) + ((dx * extra->dbdx)>>4);
+ itera = extra->starta + ((dy * extra->dady)>>4) + ((dx * extra->dadx)>>4);
+ iterz = extra->startz + ((dy * extra->dzdy)>>4) + ((dx * extra->dzdx)>>4);
+ iterw = extra->startw + ((dy * extra->dwdy)>>4) + ((dx * extra->dwdx)>>4);
+
+ for (Bitu i=0; i<extra->texcount; i++) {
+ INT64 iters,itert,iterw;
+ UINT32 smax,tmax;
+ INT64 s, t;
+ INT32 lod=0;
+
+ UINT32 texmode=v->tmu[i].reg[textureMode].u;
+ UINT32 TEXMODE = texmode;
+ INT32 LODBASE = (i==0) ? extra->lodbase0 : extra->lodbase1;
+
+ INT64 oow;
+
+ UINT32 ilod = v->tmu[i].lodmin >> 8;
+ if (!((v->tmu[i].lodmask >> ilod) & 1))
+ ilod++;
+
+ smax = (v->tmu[i].wmask >> ilod) + 1;
+ tmax = (v->tmu[i].hmask >> ilod) + 1;
+
+ iterw = v->tmu[i].startw + ((dy * v->tmu[i].dwdy)>>4) + ((dx * v->tmu[i].dwdx)>>4);
+ iters = v->tmu[i].starts + ((dy * v->tmu[i].dsdy)>>4) + ((dx * v->tmu[i].dsdx)>>4);
+ itert = v->tmu[i].startt + ((dy * v->tmu[i].dtdy)>>4) + ((dx * v->tmu[i].dtdx)>>4);
+
+ /* determine the S/T/LOD values for this texture */
+ if (TEXMODE_ENABLE_PERSPECTIVE(texmode))
+ {
+ oow = fast_reciplog((iterw), &lod);
+ s = (oow * (iters)) >> 29;
+ t = (oow * (itert)) >> 29;
+ lod += LODBASE;
+ }
+ else
+ {
+ s = (iters) >> 14;
+ t = (itert) >> 14;
+ lod = LODBASE;
+ }
+
+ /* clamp the LOD */
+ lod += v->tmu[i].lodbias;
+// if (TEXMODE_ENABLE_LOD_DITHER(TEXMODE))
+// lod += DITHER4[(XX) & 3] << 4;
+ if (lod < v->tmu[i].lodmin)
+ lod = v->tmu[i].lodmin;
+ if (lod > v->tmu[i].lodmax)
+ lod = v->tmu[i].lodmax;
+
+ /* clamp W */
+ if (TEXMODE_CLAMP_NEG_W(texmode) && (iterw) < 0)
+ s = t = 0;
+
+ if (s != 0) vd->m[i].s = (float)((float)s/(float)(smax*(1<<(18+ilod))));
+ else vd->m[i].s = 0.0f;
+ if (t != 0) vd->m[i].t = (float)((float)t/(float)(tmax*(1<<(18+ilod))));
+ else vd->m[i].t = 0.0f;
+ if (iterw != 0) vd->m[i].w = (float)((float)iterw/(float)(0xffffff));
+ else vd->m[i].w = 0.0f;
+
+ vd->m[i].sw = vd->m[i].s * vd->m[i].w;
+ vd->m[i].tw = vd->m[i].t * vd->m[i].w;
+ vd->m[i].z = 0.0f;
+
+ INT32 lodblend=0;
+
+ if ((TEXMODE_TC_MSELECT(TEXMODE)==4) || (TEXMODE_TCA_MSELECT(TEXMODE)==4))
+ {
+ if (v->tmu[i].detailbias <= lod)
+ lodblend = 0;
+ else
+ {
+ lodblend = (((v->tmu[i].detailbias - lod) << v->tmu[i].detailscale) >> 8);
+ if (lodblend > v->tmu[i].detailmax)
+ lodblend = v->tmu[i].detailmax;
+ }
+ } else if ((TEXMODE_TC_MSELECT(TEXMODE)==5) || (TEXMODE_TCA_MSELECT(TEXMODE)==5))
+ lodblend = lod & 0xff;
+
+ vd->m[i].lodblend = (float)lodblend/255.0f;
+ }
+
+ vd->r = (float)((float)iterr/(float)(1<<20));
+ vd->g = (float)((float)iterg/(float)(1<<20));
+ vd->b = (float)((float)iterb/(float)(1<<20));
+ vd->a = (float)((float)itera/(float)(1<<20));
+ vd->z = (float)((float)iterz/(float)(1<<20));
+ vd->w = (float)((float)iterw/(float)(0xffffff));
+
+ INT32 wfloat;
+ ogl_get_depth(v,iterz,iterw,&d,&wfloat);
+ vd->d = (float)((float)d/(float)(0xffff));
+
+ INT32 fogblend;
+ ogl_get_fog_blend(v,wfloat,iterz,iterw,&fogblend);
+ vd->fogblend = (float)fogblend/255.0f;
+
+// vd->x = (float)x / (16.0f);
+// vd->y = (float)y / (16.0f);
+
+ // OpenGL-correction for Blood/ShadowWarrior
+ vd->x = (((float)x) - (1.0f/16.0f)) / 16.0f;
+ vd->y = (((float)y) - (1.0f/16.0f)) / 16.0f;
+}
+
+static UINT32 crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
+0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+UINT32 calculate_palsum(UINT32 tmunum) {
+ UINT32 csum = 0;
+ for (Bitu pct=0; pct<256; pct++) {
+ UINT32 pval = v->tmu[tmunum].palette[pct];
+ csum = crc_32_tab[(csum ^ pval) & 0xff] ^ (csum>>8);
+ csum = crc_32_tab[(csum ^ (pval>>8)) & 0xff] ^ (csum>>8);
+ csum = crc_32_tab[(csum ^ (pval>>16)) & 0xff] ^ (csum>>8);
+ csum = crc_32_tab[(csum ^ (pval>>24)) & 0xff] ^ (csum>>8);
+ }
+ return csum;
+}
+
+void ogl_cache_texture(const poly_extra_data *extra, ogl_texture_data *td) {
+ voodoo_state *v=(voodoo_state*)extra->state;
+ UINT32 texbase;
+
+ INT32 smax, tmax;
+ UINT32 *texrgbp;
+ GLuint texID;
+
+ UINT32 TEXMODE;
+
+ UINT32 num_tmus = 1;
+ if (v->tmu[1].ram != NULL) num_tmus++;
+
+ for (UINT32 j=0; j<num_tmus; j++) {
+ TEXMODE = j==0 ? extra->r_textureMode0 : extra->r_textureMode1;
+
+ UINT32 ilod = v->tmu[j].lodmin >> 8;
+ if (!((v->tmu[j].lodmask >> ilod) & 1))
+ ilod++;
+
+ texbase = v->tmu[j].lodoffset[ilod];
+
+ if ( extra->texcount && (extra->texcount >= j) && (v->tmu[j].lodmin < (8 << 8)) ) {
+ bool valid_texid = true;
+ std::map<const UINT32, ogl_texmap>::iterator t;
+ t = textures[j].find(texbase);
+ bool reuse_id = false;
+ if (t != textures[j].end()) {
+ if (t->second.valid_pal) {
+ texID = t->second.current_id;
+ if (!t->second.valid_data) {
+ valid_texid = false;
+ reuse_id = true;
+ t->second.valid_data = true;
+ }
+ } else {
+ UINT32 psum = calculate_palsum(j);
+ std::map<const UINT32, GLuint>::iterator u = t->second.ids->find(psum);
+ if (u != t->second.ids->end()) {
+ t->second.valid_pal = true;
+ t->second.current_id = u->second;
+ texID = u->second;
+ } else {
+ valid_texid = false;
+// LOG_MSG("texture removed... size %d",t->second.ids->size());
+ if (t->second.ids->size() > 8) {
+ std::map<const UINT32, GLuint>::iterator u;
+ for (u=t->second.ids->begin(); u!=t->second.ids->end(); u++) {
+ glDeleteTextures(1,&u->second);
+ }
+ t->second.ids->clear();
+ }
+ t->second.valid_pal = true;
+ }
+ }
+ } else {
+ valid_texid = false;
+ }
+ if (!valid_texid) {
+ smax = (v->tmu[j].wmask >> ilod) + 1;
+ tmax = (v->tmu[j].hmask >> ilod) + 1;
+
+ UINT32 texboffset = texbase;
+ texrgbp = (UINT32 *)&texrgb[0];
+ memset(texrgbp,0,256*256*4);
+
+ if (!reuse_id) {
+ texID = ogl_texture_index++;
+ glGenTextures(1, &texID);
+ }
+
+ if (TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u) < 8) {
+ if (TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u) != 5) {
+ for (int i=0; i<(smax*tmax); i++) {
+ UINT8 *texptr8 = (UINT8 *)&v->tmu[j].ram[(texboffset) & v->tmu[j].mask];
+ UINT32 data = v->tmu[j].lookup[*texptr8];
+ *texrgbp = data;
+ texboffset++;
+ texrgbp++;
+ }
+ } else {
+ for (int i=0; i<(smax*tmax); i++) {
+ UINT8 *texptr8 = (UINT8 *)&v->tmu[j].ram[(texboffset) & v->tmu[j].mask];
+ UINT8 texel = *texptr8;
+ UINT32 data = v->tmu[j].lookup[texel];
+ *texrgbp = data;
+ texboffset++;
+ texrgbp++;
+ }
+ }
+ } else if (TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u) >= 10 && TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u) <= 12) {
+ for (int i=0; i<(smax*tmax); i++) {
+ UINT16 *texptr16 = (UINT16 *)&v->tmu[j].ram[(texboffset) & v->tmu[j].mask];
+ UINT32 data = v->tmu[j].lookup[*texptr16];
+ *texrgbp = data;
+ texboffset+=2;
+ texrgbp++;
+ }
+ } else if (TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u) != 14) {
+ for (int i=0; i<(smax*tmax); i++) {
+ UINT16 *texptr16 = (UINT16 *)&v->tmu[j].ram[(texboffset) & v->tmu[j].mask];
+ UINT32 data = (v->tmu[j].lookup[*texptr16 & 0xFF] & 0xFFFFFF) | ((*texptr16 & 0xff00) << 16);
+ *texrgbp = data;
+ texboffset+=2;
+ texrgbp++;
+ }
+ } else {
+ for (int i=0; i<(smax*tmax); i++) {
+ UINT16 *texptr16 = (UINT16 *)&v->tmu[j].ram[(texboffset) & v->tmu[j].mask];
+ UINT16 texel1 = *texptr16 & 0xFF;
+ UINT32 data = (v->tmu[j].lookup[texel1] & 0xFFFFFF) | ((*texptr16 & 0xff00) << 16);
+ *texrgbp = data;
+ texboffset+=2;
+ texrgbp++;
+ }
+ }
+
+ texrgbp = (UINT32 *)&texrgb[0];
+// LOG_MSG("texid %d format %d -- %d x %d",
+// texID,TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u),smax,tmax);
+
+ glBindTexture(GL_TEXTURE_2D, texID);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,TEXMODE_CLAMP_S(TEXMODE)?GL_CLAMP_TO_EDGE:GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,TEXMODE_CLAMP_T(TEXMODE)?GL_CLAMP_TO_EDGE:GL_REPEAT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smax, tmax, 0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, texrgbp);
+ extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT;
+ glGenerateMipmapEXT(GL_TEXTURE_2D);
+ UINT32 palsum=0;
+ if ((TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u)==0x05) || (TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u)==0x0e)) {
+ palsum = calculate_palsum(j);
+ if (t == textures[j].end()) {
+ std::map<const UINT32, GLuint>* ids = new std::map<const UINT32, GLuint>();
+ (*ids)[palsum] = texID;
+ ogl_texmap tex = { true, true, TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u),
+ texID, ids };
+ textures[j][texbase] = tex;
+ } else {
+ (*textures[j][texbase].ids)[palsum] = texID;
+ textures[j][texbase].current_id = texID;
+ }
+ } else {
+ ogl_texmap tex = { true, true, TEXMODE_FORMAT(v->tmu[j].reg[textureMode].u),
+ texID, NULL };
+ textures[j][texbase] = tex;
+ }
+ }
+
+ td[j].texID = texID;
+ td[j].enable = true;
+ } else {
+ td[j].enable = false;
+ }
+ }
+}
+
+void voodoo_ogl_invalidate_paltex(void) {
+ std::map<const UINT32, ogl_texmap>::iterator t;
+ for (int j=0; j<2; j++) {
+ for (t=textures[j].begin(); t!=textures[j].end(); t++) {
+ if ((t->second.format == 0x05) || (t->second.format == 0x0e)) {
+ t->second.valid_pal = false;
+ }
+ }
+ }
+}
+
+
+GLhandleARB m_hProgramObject = (GLhandleARB)NULL;
+
+
+void ogl_printInfoLog(GLhandleARB obj)
+{
+ int infologLength = 0;
+ int charsWritten = 0;
+ char *infoLog;
+
+ glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
+
+ if (infologLength > 0)
+ {
+ infoLog = (char *)malloc(infologLength);
+ if (infoLog) {
+ glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog);
+ LOG_MSG("%s\n",infoLog);
+ free(infoLog);
+ }
+ }
+}
+
+void ogl_sh_tex_combine(std::string *strFShader, const int TMU, const poly_extra_data *extra) {
+ voodoo_state *v=(voodoo_state*)extra->state;
+
+ UINT32 TEXMODE = v->tmu[TMU].reg[textureMode].u;
+
+ if (!TEXMODE_TC_ZERO_OTHER(TEXMODE))
+ *strFShader += " tt.rgb = cother.rgb;\n";
+ else
+ *strFShader += " tt.rgb = vec3(0.0);\n";
+
+ if (!TEXMODE_TCA_ZERO_OTHER(TEXMODE))
+ *strFShader += " tt.a = cother.a;\n";
+ else
+ *strFShader += " tt.a = 0.0;\n";
+
+ if (TEXMODE_TC_SUB_CLOCAL(TEXMODE))
+ *strFShader += " tt.rgb -= clocal.rgb;\n";
+ if (TEXMODE_TCA_SUB_CLOCAL(TEXMODE))
+ *strFShader += " tt.a -= clocal.a;\n";
+
+ switch (TEXMODE_TC_MSELECT(TEXMODE))
+ {
+ default:
+ case 0:
+ *strFShader += " blend.rgb = vec3(0.0);\n";
+ break;
+ case 1:
+ *strFShader += " blend.rgb = clocal.rgb;\n";
+ break;
+ case 2:
+ *strFShader += " blend.rgb = vec3(cother.a);\n";
+ break;
+ case 3:
+ *strFShader += " blend.rgb = vec3(clocal.a);\n";
+ break;
+ case 4:
+ // detail //
+ case 5:
+ // LOD //
+ *strFShader += " blend.rgb = vec3(f_lodblend";
+ *strFShader += TMU==0?"0":"1";
+ *strFShader += ");\n";
+ break;
+ }
+
+ switch (TEXMODE_TCA_MSELECT(TEXMODE))
+ {
+ default:
+ case 0:
+ *strFShader += " blend.a = 0.0;\n";
+ break;
+ case 2:
+ *strFShader += " blend.a = cother.a;\n";
+ break;
+ case 1:
+ // fall through
+ case 3:
+ *strFShader += " blend.a = clocal.a;\n";
+ break;
+ case 4:
+ // detail //
+ case 5:
+ // LOD //
+ *strFShader += " blend.a = f_lodblend";
+ *strFShader += TMU==0?"0":"1";
+ *strFShader += ";\n";
+ break;
+ }
+
+ if (!TEXMODE_TC_REVERSE_BLEND(TEXMODE))
+ *strFShader += " blend.rgb = vec3(1.0) - blend.rgb;\n";
+
+ if (!TEXMODE_TCA_REVERSE_BLEND(TEXMODE))
+ *strFShader += " blend.a = 1.0 - blend.a;\n";
+
+ *strFShader += " tt *= blend;\n";
+
+ switch (TEXMODE_TC_ADD_ACLOCAL(TEXMODE))
+ {
+ case 3:
+ case 0:
+ break;
+ case 1:
+ *strFShader += " tt.rgb += clocal.rgb;\n";
+ break;
+ case 2:
+ *strFShader += " tt.rgb += vec3(clocal.a);\n";
+ break;
+ }
+
+ if (TEXMODE_TCA_ADD_ACLOCAL(TEXMODE))
+ *strFShader += " tt.a += clocal.a;\n";
+
+ *strFShader += " clocal = tt;\n";
+
+ if (TEXMODE_TC_INVERT_OUTPUT(TEXMODE))
+ *strFShader += " clocal.rgb = vec3(1.0)-clocal.rgb;\n";
+
+ if (TEXMODE_TCA_INVERT_OUTPUT(TEXMODE))
+ *strFShader += " clocal.a = 1.0 - clocal.a;\n";
+
+}
+
+void ogl_sh_color_path(std::string *strFShader, const poly_extra_data *extra) {
+ voodoo_state *v=(voodoo_state*)extra->state;
+
+ UINT32 FBZCOLORPATH = v->reg[fbzColorPath].u;
+ UINT32 FBZMODE = v->reg[fbzMode].u;
+ UINT32 ALPHAMODE = v->reg[alphaMode].u;
+
+ switch (FBZCP_CC_RGBSELECT(FBZCOLORPATH))
+ {
+ case 0:
+ *strFShader += " cother = gl_Color;\n";
+ break;
+ case 1:
+ *strFShader += " cother = texel;\n";
+ break;
+ case 2:
+ *strFShader += " cother = color1;\n";
+ break;
+ default:
+ *strFShader += " cother = vec4(0.0);\n";
+ }
+
+ // TODO fix chroma key //
+ if (FBZMODE_ENABLE_CHROMAKEY(FBZMODE)) {
+// if (!CHROMARANGE_ENABLE(v->reg[chromaRange].u))
+ *strFShader += " if (distance (cother.rgb , chromaKey.rgb) < 0.0001) discard;\n";
+// else {
+// *strFShader += " if ((cother.rgb >= (chromaKey.rgb-0.01)) && (cother.rgb <= (chromaRange.rgb+0.01))) discard;";
+// }
+ }
+
+
+ switch (FBZCP_CC_ASELECT(FBZCOLORPATH))
+ {
+ case 0:
+ *strFShader += " cother.a = gl_Color.a;\n";
+ break;
+ case 1:
+ *strFShader += " cother.a = texel.a;\n";
+ break;
+ case 2:
+ *strFShader += " cother.a = color1.a;\n";
+ break;
+ default:
+ *strFShader += " cother.a = 0.0;\n";
+ break;
+ }
+
+ // TODO check alpha mask //
+// if (FBZMODE_ENABLE_ALPHA_MASK(FBZMODE))
+// *strFShader += " if (cother.a > 0.0) discard;";
+
+ if (ALPHAMODE_ALPHATEST(ALPHAMODE))
+ switch (ALPHAMODE_ALPHAFUNCTION(ALPHAMODE)) {
+ case 0:
+ *strFShader += " discard;\n";
+ break;
+ case 1:
+ *strFShader += " if (cother.a >= alphaRef) discard;\n";
+ break;
+ case 2:
+// *strFShader += " if (cother.a != alphaRef) discard;\n";
+ *strFShader += " if (distance(cother.a , alphaRef) > 0.0001) discard;\n";
+ break;
+ case 3:
+ *strFShader += " if (cother.a > alphaRef) discard;\n";
+ break;
+ case 4:
+ *strFShader += " if (cother.a <= alphaRef) discard;\n";
+ break;
+ case 5:
+// *strFShader += " if (cother.a == alphaRef) discard;\n";
+ *strFShader += " if (distance(cother.a , alphaRef) < 0.0001) discard;\n";
+ break;
+ case 6:
+ *strFShader += " if (cother.a < alphaRef) discard;\n";
+ break;
+ case 7:
+ break;
+ }
+
+ if (FBZCP_CC_LOCALSELECT_OVERRIDE(FBZCOLORPATH) == 0)
+ {
+ if (FBZCP_CC_LOCALSELECT(FBZCOLORPATH) == 0)
+ *strFShader += " clocal = gl_Color;\n";
+ else
+ *strFShader += " clocal = color0;\n";
+ }
+ else
+ {
+ *strFShader += " if (texel.a < 0.5) {\n"
+ " clocal = gl_Color;\n"
+ " } else {\n"
+ " clocal = color0;\n"
+ " }\n";
+ }
+
+ switch (FBZCP_CCA_LOCALSELECT(FBZCOLORPATH))
+ {
+ default:
+ case 1:
+ *strFShader += " clocal.a = color0.a;\n";
+ break;
+ case 0:
+ // fall through
+ case 2:
+ *strFShader += " clocal.a = gl_Color.a;\n"; // TODO CLAMPED_Z
+ break;
+ case 3:
+ // voodoo2 only
+ break;
+ }
+
+ if (FBZCP_CC_ZERO_OTHER(FBZCOLORPATH) == 0)
+ *strFShader += " tt.rgb = cother.rgb;\n";
+ else
+ *strFShader += " tt.rgb = vec3(0.0);\n";
+
+ if (FBZCP_CCA_ZERO_OTHER(FBZCOLORPATH) == 0)
+ *strFShader += " tt.a = cother.a;\n";
+ else
+ *strFShader += " tt.a = 0.0;\n";
+
+ if (FBZCP_CC_SUB_CLOCAL(FBZCOLORPATH))
+ *strFShader += " tt.rgb -= clocal.rgb;\n";
+
+ if (FBZCP_CCA_SUB_CLOCAL(FBZCOLORPATH))
+ *strFShader += " tt.a -= clocal.a;\n";
+
+ switch (FBZCP_CC_MSELECT(FBZCOLORPATH))
+ {
+ default:
+ case 0:
+ *strFShader += " blend.rgb = vec3(0.0);\n";
+ break;
+ case 1:
+ *strFShader += " blend.rgb = clocal.rgb;\n";
+ break;
+ case 2:
+ *strFShader += " blend.rgb = vec3(cother.a);\n";
+ break;
+ case 3:
+ *strFShader += " blend.rgb = vec3(clocal.a);\n";
+ break;
+ case 4:
+ *strFShader += " blend.rgb = vec3(texel.a);\n";
+ break;
+ case 5:
+ // voodoo2 only
+ *strFShader += " blend.rgb = texel.rgb;\n";
+ break;
+ }
+
+ switch (FBZCP_CCA_MSELECT(FBZCOLORPATH))
+ {
+ default:
+ case 0:
+ *strFShader += " blend.a = 0.0;\n";
+ break;
+ case 2:
+ *strFShader += " blend.a = cother.a;\n";
+ break;
+ case 1:
+ // fall through
+ case 3:
+ *strFShader += " blend.a = clocal.a;\n";
+ break;
+ case 4:
+ *strFShader += " blend.a = texel.a;\n";
+ break;
+ }
+
+ if (!FBZCP_CC_REVERSE_BLEND(FBZCOLORPATH))
+ *strFShader += " blend.rgb = vec3(1.0) - blend.rgb;\n";
+
+ if (!FBZCP_CCA_REVERSE_BLEND(FBZCOLORPATH))
+ *strFShader += " blend.a = 1.0 - blend.a;\n";
+
+ *strFShader += " tt *= blend;\n";
+
+ switch (FBZCP_CC_ADD_ACLOCAL(FBZCOLORPATH))
+ {
+ case 3:
+ case 0:
+ break;
+ case 1:
+ *strFShader += " tt.rgb += clocal.rgb;\n";
+ break;
+ case 2:
+ *strFShader += " tt.rgb += vec3(clocal.a);\n";
+ break;
+ }
+
+ if (FBZCP_CCA_ADD_ACLOCAL(FBZCOLORPATH))
+ *strFShader += " tt.a += clocal.a;\n";
+
+ // clamp ?? //
+
+ *strFShader += " pixel = tt;\n";
+
+ if (FBZCP_CC_INVERT_OUTPUT(FBZCOLORPATH))
+ *strFShader += " pixel.rgb = vec3(1.0) - tt.rgb;\n";
+
+ if (FBZCP_CCA_INVERT_OUTPUT(FBZCOLORPATH))
+ *strFShader += " pixel.a = 1.0 - tt.a;\n";
+}
+
+void ogl_sh_fog(std::string *strFShader, const poly_extra_data *extra) {
+ voodoo_state *v=(voodoo_state*)extra->state;
+
+ UINT32 FOGMODE = v->reg[fogMode].u;
+
+ *strFShader += " vec4 ff;\n";
+
+ /* constant fog bypasses everything else */
+ if (FOGMODE_FOG_CONSTANT(FOGMODE))
+ *strFShader += " ff = fogColor;\n";
+
+ /* non-constant fog comes from several sources */
+ else {
+ /* if fog_add is zero, we start with the fog color */
+ if (FOGMODE_FOG_ADD(FOGMODE) == 0)
+ *strFShader += " ff = fogColor;\n";
+ else
+ *strFShader += " ff = vec4(0.0);\n";
+
+ /* if fog_mult is zero, we subtract the incoming color */
+ if (FOGMODE_FOG_MULT(FOGMODE) == 0)
+ *strFShader += " ff -= pixel;\n";
+
+ *strFShader += " float fogblend;\n";
+ /* fog blending mode */
+ switch (FOGMODE_FOG_ZALPHA(FOGMODE))
+ {
+ case 1: /* iterated A */
+ *strFShader += " fogblend = gl_Color.a;\n";
+ break;
+ case 0: /* fog table */
+ // blend factor calculated in ogl_get_fog_blend //
+ // fall through
+ case 2: /* iterated Z */
+ // fall through
+ case 3: /* iterated W - Voodoo 2 only */
+ *strFShader += " fogblend = f_fogblend;\n";
+ break;
+ }
+
+ /* perform the blend */
+ *strFShader += " ff *= fogblend;\n";
+
+ /* if fog_mult is 0, we add this to the original color */
+ if (FOGMODE_FOG_MULT(FOGMODE) == 0)
+ *strFShader += " pixel.rgb += ff.rgb;\n";
+ /* otherwise this just becomes the new color */
+ else
+ *strFShader += " pixel.rgb = ff.rgb;\n";
+
+ }
+}
+
+
+void ogl_shaders(const poly_extra_data *extra) {
+ voodoo_state *v=(voodoo_state*)extra->state;
+
+ GLint res;
+ std::string strVShader, strFShader;
+
+ /* shaders extensions not loaded */
+ if (!glCreateShaderObjectARB) return;
+
+ //UINT32 FBZMODE = extra->r_fbzMode;
+ UINT32 FOGMODE = (UINT32)extra->r_fogMode;
+ UINT32 texcount = (UINT32)extra->texcount;
+
+ /* build a new shader program */
+ if (!extra->info->shader_ready) {
+
+ /* create vertex shader */
+ int fcount=0;
+ while (glGetError()!=0) {
+ fcount++;
+ if (fcount>1000) E_Exit("opengl error");
+ }
+
+ GLhandleARB m_hVertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
+
+ strVShader =
+ "attribute float v_fogblend;\n"
+ "varying float f_fogblend;\n"
+ "attribute float v_lodblend0;\n"
+ "varying float f_lodblend0;\n"
+ "attribute float v_lodblend1;\n"
+ "varying float f_lodblend1;\n"
+ "\n"
+ "void main()"
+ "{\n"
+ " gl_TexCoord[0] = gl_MultiTexCoord0;\n"
+ " gl_TexCoord[1] = gl_MultiTexCoord1;\n"
+ " gl_FrontColor = gl_Color;\n"
+ " f_fogblend = v_fogblend;\n"
+ " f_lodblend0 = v_lodblend0;\n"
+ " f_lodblend1 = v_lodblend1;\n"
+ " gl_Position = ftransform();\n"
+ "}\n";
+
+ const char *szVShader = strVShader.c_str();
+ glShaderSourceARB(m_hVertexShader, 1, &szVShader, NULL);
+ glCompileShaderARB(m_hVertexShader);
+ glGetObjectParameterivARB(m_hVertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &res);
+ if(res == 0) {
+ char infobuffer[1000];
+ int infobufferlen = 0;
+ glGetInfoLogARB(m_hVertexShader, 999, &infobufferlen, infobuffer);
+ infobuffer[infobufferlen] = 0;
+ ogl_printInfoLog(m_hVertexShader);
+ E_Exit("ERROR: Error compiling vertex shader");
+ return;
+ }
+
+ /* create fragment shader */
+ GLhandleARB m_hFragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
+ strFShader =
+ "varying float f_fogblend;\n"
+ "varying float f_lodblend0;\n"
+ "varying float f_lodblend1;\n"
+ "uniform sampler2D tex0;\n"
+ "uniform sampler2D tex1;\n"
+ "uniform vec4 color0;\n"
+ "uniform vec4 color1;\n"
+ "uniform vec4 chromaKey;\n"
+ "uniform vec4 chromaRange;\n"
+ "uniform float alphaRef;\n"
+ "uniform float zaColor;\n"
+ "uniform vec4 fogColor;\n"
+ "\n"
+ "void main()"
+ "{\n"
+ " vec4 pixel = vec4(0.0);\n"
+ " vec4 texel = vec4(1.0);\n"
+ " vec4 clocal = vec4(1.0);\n"
+ " vec4 cother = vec4(0.0);\n"
+ " vec4 tt = vec4(0.0);\n"
+ " vec4 blend = vec4(0.0);\n";
+
+ // TODO glsl depth test? //
+
+ if (texcount >= 2 && v->tmu[1].lodmin < (8 << 8))
+ {
+ strFShader += " clocal = texture2DProj(tex1,gl_TexCoord[1]);\n";
+ ogl_sh_tex_combine(&strFShader, 1, extra);
+ strFShader += " cother = clocal;\n";
+ strFShader += " texel = clocal;\n";
+ }
+
+ if (texcount >= 1 && v->tmu[0].lodmin < (8 << 8))
+ {
+ strFShader += " clocal = texture2DProj(tex0,gl_TexCoord[0]);\n";
+ ogl_sh_tex_combine(&strFShader, 0, extra);
+ strFShader += " texel = clocal;\n";
+ }
+
+ // TODO Clamped ARGB //
+
+ ogl_sh_color_path(&strFShader, extra);
+
+ // fogging //
+ if (FOGMODE_ENABLE_FOG(FOGMODE))
+ ogl_sh_fog(&strFShader, extra);
+
+ strFShader += " gl_FragColor = pixel;\n";
+
+ strFShader +=
+ "}";
+
+ const char *szFShader = strFShader.c_str();
+ glShaderSourceARB(m_hFragmentShader, 1, &szFShader, NULL);
+
+ glCompileShaderARB(m_hFragmentShader);
+ glGetObjectParameterivARB(m_hFragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &res);
+ if(res == 0) {
+ ogl_printInfoLog(m_hFragmentShader);
+ E_Exit("ERROR: Error compiling fragment shader");
+ return;
+ }
+
+
+ /* create program object */
+ m_hProgramObject = glCreateProgramObjectARB();
+
+ glAttachObjectARB(m_hProgramObject, m_hVertexShader);
+ glAttachObjectARB(m_hProgramObject, m_hFragmentShader);
+
+ glLinkProgramARB(m_hProgramObject);
+
+ glGetObjectParameterivARB(m_hProgramObject, GL_OBJECT_LINK_STATUS_ARB, &res);
+ if(res == 0) {
+ ogl_printInfoLog(m_hProgramObject);
+ E_Exit("ERROR: Error linking program");
+ return;
+ }
+
+ /* use this shader */
+ glUseProgramObjectARB(m_hProgramObject);
+ extra->info->so_shader_program=(uintptr_t)m_hProgramObject;
+ extra->info->so_vertex_shader=(uintptr_t)m_hVertexShader;
+ extra->info->so_fragment_shader=(uintptr_t)m_hFragmentShader;
+
+ extra->info->shader_ready=true;
+
+ GLenum glerr=glGetError();
+ if (glerr!=0) {
+ E_Exit("create shader start glError->%x",glerr);
+ }
+
+ int* locations=new int[12];
+ locations[0]=glGetUniformLocationARB(m_hProgramObject, "chromaKey");
+ locations[1]=glGetUniformLocationARB(m_hProgramObject, "chromaRange");
+ locations[2]=glGetUniformLocationARB(m_hProgramObject, "color0");
+ locations[3]=glGetUniformLocationARB(m_hProgramObject, "color1");
+ locations[4]=glGetUniformLocationARB(m_hProgramObject, "alphaRef");
+ locations[5]=glGetUniformLocationARB(m_hProgramObject, "zaColor");
+ locations[6]=glGetUniformLocationARB(m_hProgramObject, "tex0");
+ locations[7]=glGetUniformLocationARB(m_hProgramObject, "tex1");
+ locations[8]=glGetUniformLocationARB(m_hProgramObject, "fogColor");
+
+ locations[9] = glGetAttribLocationARB(m_hProgramObject, "v_fogblend");
+ locations[10] = glGetAttribLocationARB(m_hProgramObject, "v_lodblend0");
+ locations[11] = glGetAttribLocationARB(m_hProgramObject, "v_lodblend1");
+ extra->info->shader_ulocations=locations;
+ } else {
+ /* use existing shader program */
+ if (m_hProgramObject != (GLhandleARB)extra->info->so_shader_program) {
+ glUseProgramObjectARB((GLhandleARB)extra->info->so_shader_program);
+ m_hProgramObject = (GLhandleARB)extra->info->so_shader_program;
+ }
+ }
+
+ if (extra->info->shader_ulocations[0]>=0) glUniform4fARB(extra->info->shader_ulocations[0], v->reg[chromaKey].rgb.r/255.0f, v->reg[chromaKey].rgb.g/255.0f, v->reg[chromaKey].rgb.b/255.0f,0);
+ if (extra->info->shader_ulocations[1]>=0) glUniform4fARB(extra->info->shader_ulocations[1], v->reg[chromaRange].rgb.r/255.0f, v->reg[chromaRange].rgb.g/255.0f, v->reg[chromaRange].rgb.b/255.0f,0);
+ if (extra->info->shader_ulocations[2]>=0) glUniform4fARB(extra->info->shader_ulocations[2], v->reg[color0].rgb.r/255.0f, v->reg[color0].rgb.g/255.0f, v->reg[color0].rgb.b/255.0f, v->reg[color0].rgb.a/255.0f);
+ if (extra->info->shader_ulocations[3]>=0) glUniform4fARB(extra->info->shader_ulocations[3], v->reg[color1].rgb.r/255.0f, v->reg[color1].rgb.g/255.0f, v->reg[color1].rgb.b/255.0f, v->reg[color1].rgb.a/255.0f);
+ if (extra->info->shader_ulocations[4]>=0) glUniform1fARB(extra->info->shader_ulocations[4], v->reg[alphaMode].rgb.a/255.0f);
+ if (extra->info->shader_ulocations[5]>=0) glUniform1fARB(extra->info->shader_ulocations[5], (float)((UINT16)v->reg[zaColor].u)/65535.0f);
+ if (extra->info->shader_ulocations[8]>=0) glUniform4fARB(extra->info->shader_ulocations[8], v->reg[fogColor].rgb.r/255.0f, v->reg[fogColor].rgb.g/255.0f, v->reg[fogColor].rgb.b/255.0f,1.0f);
+
+}
+
+
+void voodoo_ogl_draw_triangle(poly_extra_data *extra) {
+ voodoo_state *v=extra->state;
+ ogl_texture_data td[2];
+ ogl_vertex_data vd[3];
+
+ VOGL_ClearBeginMode();
+
+ td[0].enable = false;
+ td[1].enable = false;
+
+ UINT32 ALPHAMODE = (UINT32)extra->r_alphaMode;
+ UINT32 FBZMODE = (UINT32)extra->r_fbzMode;
+
+
+ ogl_get_vertex_data(v->fbi.ax, v->fbi.ay, (void*)extra, &vd[0]);
+ ogl_get_vertex_data(v->fbi.bx, v->fbi.by, (void*)extra, &vd[1]);
+ ogl_get_vertex_data(v->fbi.cx, v->fbi.cy, (void*)extra, &vd[2]);
+
+ for (int i = 0; i < 3; i++)
+ {
+ vd[i].x = (vd[i].x * new_width) / v->fbi.width;
+ vd[i].y = (vd[i].y * new_height) / v->fbi.height;
+ }
+
+ if (FBZMODE_DEPTH_SOURCE_COMPARE(FBZMODE) && VOGL_CheckFeature(VOGL_HAS_STENCIL_BUFFER)) {
+ if (m_hProgramObject != 0) {
+ glUseProgramObjectARB(0);
+ m_hProgramObject = 0;
+ }
+
+ if ((FBZMODE_ENABLE_DEPTHBUF(FBZMODE)) && (FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) == 0)) {
+ VOGL_SetDepthMode(1,FBZMODE_DEPTH_FUNCTION(FBZMODE));
+ } else {
+ VOGL_SetDepthMode(0,0);
+ }
+
+ VOGL_SetDepthMaskMode(false);
+ VOGL_SetColorMaskMode(false, false);
+
+ VOGL_SetAlphaMode(0, 0,0,0,0);
+
+ if (FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u)==0) {
+ VOGL_SetDrawMode(true);
+ } else {
+ VOGL_SetDrawMode(false);
+ }
+
+ glEnable(GL_STENCIL_TEST);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glStencilFunc(GL_ALWAYS, 1, 1);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+
+ glBegin(GL_TRIANGLES);
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ float depth = (float)(v->reg[zaColor].u&0xffff)/(float)0xffff;
+ for (int i=0;i<3;i++)
+ glVertex3f(vd[i].x, vd[i].y, depth);
+
+ glEnd();
+ }
+
+ ogl_cache_texture(extra,td);
+ ogl_shaders(extra);
+
+ if (extra->texcount > 0) {
+ for (int t=0; t<2; t++)
+ if ( td[t].enable ) {
+ UINT32 TEXMODE = v->tmu[t].reg[textureMode].u;
+ glActiveTexture(GL_TEXTURE0_ARB+t);
+ glBindTexture (GL_TEXTURE_2D, td[t].texID);
+ if (!extra->info->shader_ready) {
+ glEnable (GL_TEXTURE_2D);
+ // TODO proper fixed-pipeline combiners
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ } else {
+ if (extra->info->shader_ulocations[6+t] >= 0)
+ glUniform1iARB(extra->info->shader_ulocations[6+t],t);
+ }
+
+ GLint minFilter;
+ minFilter = GL_NEAREST + TEXMODE_MINIFICATION_FILTER(TEXMODE);
+ if (v->tmu[t].lodmin != v->tmu[t].lodmax)
+ minFilter += 0x0100 + TEXMODE_TRILINEAR(TEXMODE)*2;
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,minFilter);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST+TEXMODE_MAGNIFICATION_FILTER(TEXMODE));
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,TEXMODE_CLAMP_S(TEXMODE)?GL_CLAMP_TO_EDGE:GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,TEXMODE_CLAMP_T(TEXMODE)?GL_CLAMP_TO_EDGE:GL_REPEAT);
+ }
+ }
+
+ if (FBZMODE_DEPTH_SOURCE_COMPARE(FBZMODE) && VOGL_CheckFeature(VOGL_HAS_STENCIL_BUFFER)) {
+ glStencilFunc(GL_EQUAL, 1, 1);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ if (FBZMODE_ENABLE_DEPTHBUF(FBZMODE)) {
+ VOGL_SetDepthMode(1,GL_ALWAYS-GL_NEVER);
+ } else {
+ VOGL_SetDepthMode(0,0);
+ }
+ } else {
+ if (FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) == 0) {
+ if (FBZMODE_ENABLE_DEPTHBUF(FBZMODE)) {
+ VOGL_SetDepthMode(1,FBZMODE_DEPTH_FUNCTION(FBZMODE));
+ } else {
+ if (FBZMODE_AUX_BUFFER_MASK(FBZMODE) > 0) {
+ VOGL_SetDepthMode(1,GL_ALWAYS-GL_NEVER);
+ } else {
+ VOGL_SetDepthMode(0,0);
+ }
+ }
+ } else {
+ VOGL_SetDepthMode(1,GL_ALWAYS-GL_NEVER);
+ }
+ }
+
+ bool color_mask = (FBZMODE_RGB_BUFFER_MASK(FBZMODE) > 0);
+
+ if (FBZMODE_AUX_BUFFER_MASK(FBZMODE) > 0) {
+ VOGL_SetDepthMaskMode(FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) == 0);
+ VOGL_SetColorMaskMode(color_mask, true);
+ } else {
+ VOGL_SetDepthMaskMode(false);
+ VOGL_SetColorMaskMode(color_mask, false);
+ }
+
+ if (ALPHAMODE_ALPHABLEND(ALPHAMODE)) {
+ VOGL_SetAlphaMode(1, ogl_sfactor[ALPHAMODE_SRCRGBBLEND(ALPHAMODE)], ogl_dfactor[ALPHAMODE_DSTRGBBLEND(ALPHAMODE)],
+ (ALPHAMODE_SRCALPHABLEND(ALPHAMODE)==4)?GL_ONE:GL_ZERO,
+ (ALPHAMODE_DSTALPHABLEND(ALPHAMODE)==4)?GL_ONE:GL_ZERO);
+ } else {
+ VOGL_SetAlphaMode(0, 0,0,0,0);
+ }
+
+ if (FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u)==0) {
+ VOGL_SetDrawMode(true);
+ v->fbi.vblank_flush_pending=true;
+ cached_line_front_y=-1;
+ } else {
+ VOGL_SetDrawMode(false);
+ cached_line_back_y=-1;
+ }
+
+
+ glBegin(GL_TRIANGLES);
+
+ for (int i=0;i<3;i++) {
+ glColor4fv(&vd[i].r);
+
+ for (int t=0;t<2;t++)
+ if (td[t].enable) {
+ glMultiTexCoord4fv(GL_TEXTURE0_ARB+t,&vd[i].m[t].sw);
+ if (extra->info->shader_ulocations[10+t] >= 0)
+ glVertexAttrib1fARB(extra->info->shader_ulocations[10+t],vd[i].m[t].lodblend);
+ }
+
+ if (extra->info->shader_ulocations[9] >= 0)
+ glVertexAttrib1fARB(extra->info->shader_ulocations[9],vd[i].fogblend);
+
+ glVertex3fv(&vd[i].x);
+ }
+
+ glEnd();
+
+ if (FBZMODE_DEPTH_SOURCE_COMPARE(FBZMODE) && VOGL_CheckFeature(VOGL_HAS_STENCIL_BUFFER)) {
+ glDisable(GL_STENCIL_TEST);
+ }
+
+ if (!extra->info->shader_ready) {
+ glDisable (GL_TEXTURE_2D);
+ }
+}
+
+
+void voodoo_ogl_swap_buffer() {
+ if (GFX_LazyFullscreenRequested()) {
+ v->ogl_dimchange = true;
+ }
+
+ VOGL_ClearBeginMode();
+
+ SDL_GL_SwapWindow(GFX_GetSDLWindow());
+
+ cached_line_front_y=-1;
+ cached_line_back_y=-1;
+}
+
+
+void voodoo_ogl_texture_clear(UINT32 texbase, int TMU) {
+ std::map<const UINT32, ogl_texmap>::iterator t;
+ t=textures[TMU].find(texbase);
+ if (t != textures[TMU].end()) {
+ VOGL_ClearBeginMode();
+ if (t->second.ids != NULL) {
+ std::map<const UINT32, GLuint>::iterator u;
+ for (u=t->second.ids->begin(); u!=t->second.ids->end(); u++) {
+ glDeleteTextures(1,&u->second);
+ }
+ delete t->second.ids;
+ t->second.ids = NULL;
+ } else {
+ t->second.valid_data = false;
+ }
+ glDeleteTextures(1, (GLuint*)&t->second.current_id);
+ textures[TMU].erase(t);
+ }
+}
+
+void voodoo_ogl_draw_pixel(int x, int y, bool has_rgb, bool has_alpha, int r, int g, int b, int a) {
+ if (m_hProgramObject != 0) {
+ glUseProgramObjectARB(0);
+ m_hProgramObject = 0;
+ }
+
+ if (LFBMODE_WRITE_BUFFER_SELECT(v->reg[lfbMode].u)==0) {
+ VOGL_SetDrawMode(true);
+ v->fbi.vblank_flush_pending=true;
+ } else {
+ VOGL_SetDrawMode(false);
+ }
+
+ VOGL_SetDepthMode(0,0);
+
+ VOGL_SetDepthMaskMode(false);
+ VOGL_SetColorMaskMode(has_rgb, has_alpha);
+
+ VOGL_SetAlphaMode(0, 0,0,0,0);
+
+ VOGL_BeginMode(GL_POINTS);
+ glColor4ub((GLubyte)(r&0xff), (GLubyte)(g&0xff), (GLubyte)(b&0xff), (GLubyte)(a&0xff));
+ float dx = (float)new_width / (float)v->fbi.width;
+ float dy = (float)new_height / (float)v->fbi.height;
+ int startx = (int)(x*dx);
+ int endx = (int)((x + 1)*dx);
+ int starty = (int)(y*dy);
+ int endy = (int)((y + 1)*dy);
+
+ for (int nx = startx; nx < endx; nx++)
+ {
+ for (int ny = starty; ny < endy; ny++)
+ {
+ glVertex2i(nx, ny);
+ }
+ }
+}
+
+void voodoo_ogl_draw_z(int x, int y, int z) {
+// VOGL_ClearBeginMode();
+
+ if (m_hProgramObject != 0) {
+ glUseProgramObjectARB(0);
+ m_hProgramObject = 0;
+ }
+
+ if (LFBMODE_WRITE_BUFFER_SELECT(v->reg[lfbMode].u)==0) {
+ VOGL_SetDrawMode(true);
+ v->fbi.vblank_flush_pending=true;
+ } else {
+ VOGL_SetDrawMode(false);
+ }
+
+ VOGL_SetDepthMode(1,GL_ALWAYS-GL_NEVER);
+
+ VOGL_SetDepthMaskMode(true);
+ VOGL_SetColorMaskMode(false, false);
+
+ VOGL_SetAlphaMode(0, 0,0,0,0);
+
+ VOGL_BeginMode(GL_POINTS);
+// glBegin(GL_POINTS);
+ float dx = (float)new_width / (float)v->fbi.width;
+ float dy = (float)new_height / (float)v->fbi.height;
+ int startx = (int)(x*dx);
+ int endx = (int)((x + 1)*dx);
+ int starty = (int)(y*dy);
+ int endy = (int)((y + 1)*dy);
+
+ for (int nx = startx; nx < endx; nx++)
+ {
+ for (int ny = starty; ny < endy; ny++)
+ {
+ glVertex3i(nx, ny, z); // z adjustment??
+ }
+ }
+// glEnd();
+}
+
+void voodoo_ogl_draw_pixel_pipeline(int x, int y, int r, int g, int b) {
+// VOGL_ClearBeginMode();
+
+ // TODO redo everything //
+ if (m_hProgramObject != 0) {
+ glUseProgramObjectARB(0);
+ m_hProgramObject = 0;
+ }
+
+ if (LFBMODE_WRITE_BUFFER_SELECT(v->reg[lfbMode].u)==0) {
+ VOGL_SetDrawMode(true);
+ v->fbi.vblank_flush_pending=true;
+ } else {
+ VOGL_SetDrawMode(false);
+ }
+
+ VOGL_SetDepthMode(0,0);
+
+ VOGL_SetDepthMaskMode(false);
+ if (FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u) > 0) {
+ VOGL_SetColorMaskMode(true, true);
+ } else {
+ VOGL_SetColorMaskMode(true, false);
+ }
+
+ if (ALPHAMODE_ALPHABLEND(v->reg[alphaMode].u)) {
+ VOGL_SetAlphaMode(1, ogl_sfactor[ALPHAMODE_SRCRGBBLEND(v->reg[alphaMode].u)], ogl_dfactor[ALPHAMODE_DSTRGBBLEND(v->reg[alphaMode].u)],
+ (ALPHAMODE_SRCALPHABLEND(v->reg[alphaMode].u)==4)?GL_ONE:GL_ZERO,
+ (ALPHAMODE_DSTALPHABLEND(v->reg[alphaMode].u)==4)?GL_ONE:GL_ZERO);
+ } else {
+ VOGL_SetAlphaMode(0, 0,0,0,0);
+ }
+
+ VOGL_BeginMode(GL_POINTS);
+// glBegin(GL_POINTS);
+// glColor3f((float)r/255.0f, (float)g/255.0f, (float)b/255.0f);
+ glColor3ub((GLubyte)(r&0xff), (GLubyte)(g&0xff), (GLubyte)(b&0xff));
+ float dx = (float)new_width / (float)v->fbi.width;
+ float dy = (float)new_height / (float)v->fbi.height;
+ int startx = (int)(x*dx);
+ int endx = (int)((x + 1)*dx);
+ int starty = (int)(y*dy);
+ int endy = (int)((y + 1)*dy);
+
+ for (int nx = startx; nx < endx; nx++)
+ {
+ for (int ny = starty; ny < endy; ny++)
+ {
+ glVertex2i(nx, ny);
+ }
+ }
+// glEnd();
+}
+
+
+void voodoo_ogl_clip_window(voodoo_state *) {
+/* VOGL_ClearBeginMode();
+
+ int sx = (v->reg[clipLeftRight].u >> 16) & 0x3ff;
+ int ex = (v->reg[clipLeftRight].u >> 0) & 0x3ff;
+ int sy = (v->reg[clipLowYHighY].u >> 16) & 0x3ff;
+ int ey = (v->reg[clipLowYHighY].u >> 0) & 0x3ff;
+
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u)) {
+ sy = (v->fbi.yorigin+1 - sy) & 0x3ff;
+ ey = (v->fbi.yorigin+1 - ey) & 0x3ff;
+ }
+
+ if ((sx>0) || (sy>0) || (ex<v->fbi.width) || (ey<v->fbi.height))
+ {
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(sx,sy,ex-sx,ey-sy);
+ } else {
+ glScissor(0,0,v->fbi.width,v->fbi.height);
+ glDisable(GL_SCISSOR_TEST);
+ } */
+}
+
+
+void voodoo_ogl_fastfill(void) {
+ VOGL_ClearBeginMode();
+
+ VOGL_SetDepthMaskMode(true);
+
+ int sx = (v->reg[clipLeftRight].u >> 16) & 0x3ff;
+ int ex = (v->reg[clipLeftRight].u >> 0) & 0x3ff;
+ int sy = (v->reg[clipLowYHighY].u >> 16) & 0x3ff;
+ int ey = (v->reg[clipLowYHighY].u >> 0) & 0x3ff;
+
+// if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ {
+ int tmp = (v->fbi.yorigin+1 - ey) & 0x3ff;
+ ey = (v->fbi.yorigin+1 - sy) & 0x3ff;
+ sy = tmp;
+ }
+
+ bool scissors_needed = true;
+ if ((sx == 0) && (sy == 0)) {
+ if ((ex == (int)v->fbi.width) && (ey == (int)v->fbi.height)) scissors_needed = false;
+ }
+
+ sx = (sx* new_width) / v->fbi.width;
+ ex = (ex* new_width) / v->fbi.width;
+ sy = (sy* new_height) / v->fbi.height;
+ ey = (ey* new_height) / v->fbi.height;
+
+ if (scissors_needed) {
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(sx,sy,ex-sx,ey-sy);
+ }
+
+
+ uint32_t clear_mask=0;
+ if (FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u)) {
+ clear_mask|=GL_COLOR_BUFFER_BIT;
+
+// if (FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u) && v->fbi.auxoffs != (UINT32)(~0)) ...
+ VOGL_SetColorMaskMode(true, true);
+
+ if (last_clear_color!=v->reg[color1].u) {
+ glClearColor((float)v->reg[color1].rgb.r/255.0f,
+ (float)v->reg[color1].rgb.g/255.0f,
+ (float)v->reg[color1].rgb.b/255.0f,
+ (float)v->reg[color1].rgb.a/255.0f);
+ last_clear_color=v->reg[color1].u;
+ }
+ if (FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u)==0) {
+ VOGL_SetDrawMode(true);
+ v->fbi.vblank_flush_pending=true;
+ cached_line_front_y=-1;
+ } else {
+ VOGL_SetDrawMode(false);
+ cached_line_back_y=-1;
+ }
+ }
+ if (FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u) && v->fbi.auxoffs != (UINT32)(~0)) {
+// if (FBZMODE_ENABLE_DEPTHBUF(v->reg[fbzMode].u)) {
+ clear_mask|=GL_DEPTH_BUFFER_BIT;
+ glClearDepth((float)((UINT16)v->reg[zaColor].u)/65535.0f);
+ }
+
+ if (clear_mask) glClear(clear_mask);
+
+ if (scissors_needed) {
+ glScissor(0,0,new_width,new_height);
+ glDisable(GL_SCISSOR_TEST);
+ }
+}
+
+void voodoo_ogl_clear(void) {
+ VOGL_ClearBeginMode();
+
+ VOGL_SetDrawMode(false);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ last_clear_color = 0;
+ glClearDepth(1.0f);
+ glClearStencil(0);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ voodoo_ogl_swap_buffer();
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+
+UINT32 voodoo_ogl_read_pixel(int x, int y) {
+ UINT32 data[2];
+ if ((x < 0) || (y < 0) || (x >= (INT32)v->fbi.width) || (y >= (INT32)v->fbi.height)) return 0xffffffff;
+ x = (x * new_width) / v->fbi.width;
+ y = (y * new_height) / v->fbi.height;
+
+ UINT32 mode=GL_RGBA;
+ switch (LFBMODE_READ_BUFFER_SELECT(v->reg[lfbMode].u)) {
+ case 0: /* front buffer */
+ VOGL_SetReadMode(true);
+ if ((cached_line_front_y != y) || (x+1 >= cached_line_front_width)) {
+ if (cached_line_front_length<(INT32)new_width) {
+ if (cached_line_front_data!=NULL) free(cached_line_front_data);
+ size_t span_length=((new_width+64)&(~15));
+ cached_line_front_data=(UINT32*)malloc(sizeof(UINT32)*span_length);
+ cached_line_front_length=(INT32)span_length;
+ }
+ glReadPixels(0,new_height-y,new_width,1,mode,GL_UNSIGNED_BYTE,cached_line_front_data);
+ cached_line_front_y=y;
+ cached_line_front_width = (INT32)new_width;
+ }
+ data[0]=cached_line_front_data[x];
+ data[1]=cached_line_front_data[x+1];
+ break;
+ case 1: /* back buffer */
+ VOGL_SetReadMode(false);
+ if ((cached_line_back_y != y) || (x+1 >= cached_line_back_width)) {
+ if (cached_line_back_length<(INT32)new_width) {
+ if (cached_line_back_data!=NULL) free(cached_line_back_data);
+ size_t span_length=((new_width+64)&(~15));
+ cached_line_back_data=(UINT32*)malloc(sizeof(UINT32)*span_length);
+ cached_line_back_length=(INT32)span_length;
+ }
+ glReadPixels(0,new_height-y,new_width,1,mode,GL_UNSIGNED_BYTE,cached_line_back_data);
+ cached_line_back_y=y;
+ cached_line_back_width = (INT32)new_width;
+ }
+ data[0]=cached_line_back_data[x];
+ data[1]=cached_line_back_data[x+1];
+ break;
+ case 2: /* aux buffer */
+ mode=GL_DEPTH_COMPONENT;
+ VOGL_SetReadMode(false);
+ glReadPixels(x,new_height-y,2,1,mode,GL_UNSIGNED_INT,&data);
+ return ((data[0]>>16)&0xffff) | (data[1] & 0xffff0000);
+ default:
+ E_Exit("read from invalid buf %x",LFBMODE_READ_BUFFER_SELECT(v->reg[lfbMode].u));
+ break;
+ }
+
+ return ((RGB_BLUE(data[0])>>3)<<11) | ((RGB_GREEN(data[0])>>2)<<5) | (RGB_RED(data[0])>>3) |
+ ((RGB_BLUE(data[1])>>3)<<27) | ((RGB_GREEN(data[1])>>2)<<21) | ((RGB_RED(data[1])>>3)<<16);
+}
+
+
+void voodoo_ogl_vblank_flush(void) {
+ VOGL_ClearBeginMode();
+ glFlush();
+}
+
+
+void voodoo_ogl_set_window(voodoo_state *v) {
+ VOGL_ClearBeginMode();
+
+ // matrix mode GL_PROJECTION assumed
+ bool size_changed=false;
+ if ((new_width!=last_width) || (new_height!=last_height)) size_changed=true;
+ if (size_changed || (last_orientation != (INT32)FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))) {
+ glLoadIdentity( );
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ glOrtho( 0, new_width, 0, new_height, 0.0f, -1.0f );
+ else
+ glOrtho( 0, new_width, new_height, 0, 0.0f, -1.0f );
+ if (last_orientation != (INT32)FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ last_orientation = FBZMODE_Y_ORIGIN(v->reg[fbzMode].u);
+ }
+ if (size_changed) {
+ glViewport( 0, 0, new_width, new_height );
+ last_width = new_width;
+ last_height = new_height;
+ }
+}
+
+void voodoo_ogl_reset_videomode(void) {
+ last_clear_color=0;
+
+ last_width=0;
+ last_height=0;
+ last_orientation=-1;
+
+ VOGL_Reset();
+
+ GFX_TearDown();
+
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+
+ bool has_alpha = true;
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+
+ bool has_stencil = true;
+ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+#if !defined (WIN32)
+ // broken on windows (longstanding SDL bug), may help other platforms to force hardware acceleration
+ SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );
+#endif
+
+ if (ogl_surface != NULL) {
+ SDL_FreeSurface(ogl_surface);
+ ogl_surface = NULL;
+ }
+
+ if (new_height<v->fbi.height) new_height = v->fbi.height;
+ if (new_width < v->fbi.width) new_width = v->fbi.width;
+
+ if (GFX_LazyFullscreenRequested()) GFX_SwitchFullscreenNoReset();
+
+ int w = -1, h = -1;
+ SDL_GetWindowSize(GFX_GetSDLWindow(), &w, &h);
+ if (w > -1 && h > -1 && ((int)new_width != w || (int)new_height != h)) {
+ new_height = h;
+ new_width = w;
+ }
+ SDL_Window *window = GFX_SetSDLWindowMode(new_width, new_height, SCREEN_OPENGL);
+ if (window != NULL)
+ ogl_surface = SDL_GetWindowSurface(window);
+ if (ogl_surface == NULL)
+ E_Exit("VOODOO: opengl init error");
+
+ if (GFX_IsFullScreenSetting() && !GFX_IsFullscreen())
+ GFX_SwitchFullScreen();
+ GFX_SwitchLazyFullscreen(true);
+
+ //GFX_UpdateSDLCaptureState();
+
+ int value;
+
+ bool few_colors = false;
+ if (SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &value) == 0) {
+ if (value < 8) few_colors = true;
+ }
+ if (SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &value) == 0) {
+ if (value < 8) few_colors = true;
+ }
+ if (SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &value) == 0) {
+ if (value < 8) few_colors = true;
+ }
+ if (few_colors) LOG_MSG("opengl: warning: graphics mode with insufficient color depth");
+
+ if (SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &value) == 0) {
+ if (value < 24) LOG_MSG("opengl: warning: depth buffer with insufficient resolution");
+ }
+
+ if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &value) == 0) {
+ if (value < 1) has_stencil = false;
+ }
+ if (SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &value) == 0) {
+ if (value < 8) has_alpha = false;
+ }
+
+ if (has_stencil) VOGL_FlagFeature(VOGL_HAS_STENCIL_BUFFER);
+ if (has_alpha) VOGL_FlagFeature(VOGL_HAS_ALPHA_PLANE);
+
+ GLint depth_csize;
+ glGetIntegerv(GL_DEPTH_BITS, &depth_csize);
+ if (depth_csize == 24) {
+ } else if (depth_csize == 16) {
+ } else if (depth_csize < 16) {
+ LOG_MSG("VOODOO: OpenGL: invalid depth size %d",depth_csize);
+ }
+
+ LOG_MSG("VOODOO: OpenGL: mode set, resolution %d:%d", v->fbi.width, v->fbi.height);
+}
+
+void voodoo_ogl_update_dimensions(void) {
+ voodoo_ogl_leave(false);
+
+ voodoo_ogl_reset_videomode();
+
+ glMatrixMode( GL_PROJECTION );
+ voodoo_ogl_set_window(v);
+
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity( );
+ glMatrixMode( GL_PROJECTION );
+}
+
+bool voodoo_ogl_init(voodoo_state *v) {
+ OpenGL_On();
+ voodoo_ogl_reset_videomode();
+
+ if (!VOGL_Initialize()) {
+ VOGL_Reset();
+ OpenGL_Off();
+ // reset video mode etc.
+ return false;
+ }
+
+/* std::string features = "";
+ if (VOGL_CheckFeature(VOGL_HAS_SHADERS)) features += " shader";
+ if (VOGL_CheckFeature(VOGL_HAS_ALPHA_PLANE)) features += " alpha-plane";
+ if (VOGL_CheckFeature(VOGL_HAS_STENCIL_BUFFER)) features += " stencil-buffer";
+
+ if (features == "") features = " none";
+
+ LOG_MSG("VOODOO: OpenGL: features enabled:%s",features.c_str()); */
+
+
+ glMatrixMode( GL_PROJECTION );
+ voodoo_ogl_set_window(v);
+
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity( );
+ glMatrixMode( GL_PROJECTION );
+
+ return true;
+}
+
+void voodoo_ogl_leave(bool leavemode) {
+ VOGL_ClearBeginMode();
+
+ std::map<const UINT32, ogl_texmap>::iterator t;
+ for (int j=0; j<2; j++) {
+ for (t=textures[j].begin(); t!=textures[j].end(); t++) {
+ if (t->second.ids != NULL) {
+ std::map<const UINT32, GLuint>::iterator u;
+ for (u=t->second.ids->begin(); u!=t->second.ids->end(); u++) {
+ glDeleteTextures(1,&u->second);
+ }
+ if (!t->second.ids->empty()) t->second.ids->clear();
+ delete t->second.ids;
+ t->second.ids = NULL;
+ } else {
+ glDeleteTextures(1,&t->second.current_id);
+ }
+ }
+ if (!textures[j].empty()) textures[j].clear();
+ }
+
+
+ if (m_hProgramObject != 0) {
+ glUseProgramObjectARB(0);
+ m_hProgramObject = 0;
+ }
+
+ for (int hct=0; hct<RASTER_HASH_SIZE; hct++) {
+ raster_info *info = v->raster_hash[hct];
+ for (; info; info = info->next) {
+ if (info->shader_ready) {
+ delete[] info->shader_ulocations;
+ info->shader_ulocations=NULL;
+
+ if (info->so_shader_program > 0) {
+ if (info->so_vertex_shader > 0) glDetachObjectARB((GLhandleARB)info->so_shader_program, (GLhandleARB)info->so_vertex_shader);
+ if (info->so_fragment_shader > 0) glDetachObjectARB((GLhandleARB)info->so_shader_program, (GLhandleARB)info->so_fragment_shader);
+ if (info->so_vertex_shader > 0) glDeleteObjectARB((GLhandleARB)info->so_vertex_shader);
+ if (info->so_fragment_shader > 0) glDeleteObjectARB((GLhandleARB)info->so_fragment_shader);
+ glDeleteObjectARB((GLhandleARB)info->so_shader_program);
+ }
+
+ info->shader_ready=false;
+ }
+ }
+ }
+
+
+ cached_line_front_y=-1;
+ cached_line_back_y=-1;
+
+ if (leavemode) {
+ LOG_MSG("VOODOO: OpenGL: quit");
+
+ GFX_SwitchLazyFullscreen(false);
+ if (GFX_IsFullScreenSetting() && !GFX_IsFullscreen())
+ GFX_SwitchFullScreen();
+ if (ogl_surface != NULL) {
+ SDL_FreeSurface(ogl_surface);
+ ogl_surface = NULL;
+ }
+ GFX_RestoreMode();
+ OpenGL_Off();
+ }
+}
+
+void voodoo_ogl_shutdown(voodoo_state *v) {
+ // TODO revert to previous video mode //
+ new_width = new_height = 0;
+ voodoo_ogl_leave(false);
+
+ v->active = false;
+}
+
+#else
+
+bool voodoo_ogl_init(voodoo_state *v) {
+ return false;
+}
+
+void voodoo_ogl_leave(void) {
+}
+
+void voodoo_ogl_shutdown(voodoo_state *v) {
+}
+
+void voodoo_ogl_set_window(voodoo_state *v) {
+ E_Exit("invalid call to voodoo_ogl_set_window");
+}
+
+void voodoo_ogl_swap_buffer(void) {
+ E_Exit("invalid call to voodoo_ogl_swap_buffer");
+}
+
+void voodoo_ogl_vblank_flush(void) {
+ E_Exit("invalid call to voodoo_ogl_vblank_flush");
+}
+
+void voodoo_ogl_clear(void) {
+ E_Exit("invalid call to voodoo_ogl_clear");
+}
+
+void voodoo_ogl_fastfill(void) {
+ E_Exit("invalid call to voodoo_ogl_fastfill");
+}
+
+void voodoo_ogl_clip_window(voodoo_state *v) {
+ E_Exit("invalid call to voodoo_ogl_clip_window");
+}
+
+void voodoo_ogl_texture_clear(UINT32 texbase, int TMU) {
+ E_Exit("invalid call to voodoo_ogl_texture_clear");
+}
+
+void voodoo_ogl_invalidate_paltex(void) {
+ E_Exit("invalid call to voodoo_ogl_invalidate_paltex");
+}
+
+void voodoo_ogl_draw_pixel(int x, int y, bool has_rgb, bool has_alpha, int r, int g, int b, int a) {
+ E_Exit("invalid call to voodoo_ogl_draw_pixel");
+}
+
+void voodoo_ogl_draw_z(int x, int y, int z1, int z2) {
+ E_Exit("invalid call to voodoo_ogl_draw_z");
+}
+
+void voodoo_ogl_draw_pixel_pipeline(int x, int y, int r, int g, int b) {
+ E_Exit("invalid call to voodoo_ogl_draw_pixel_pipeline");
+}
+
+UINT32 voodoo_ogl_read_pixel(int x, int y) {
+ E_Exit("invalid call to voodoo_ogl_read_pixel");
+
+ return 0;
+}
+
+void voodoo_ogl_draw_triangle(poly_extra_data *extra) {
+ E_Exit("invalid call to voodoo_ogl_draw_triangle");
+}
+
+
+#endif \ No newline at end of file
diff --git a/src/hardware/voodoo_opengl.h b/src/hardware/voodoo_opengl.h
new file mode 100644
index 000000000..80a5004e1
--- /dev/null
+++ b/src/hardware/voodoo_opengl.h
@@ -0,0 +1,94 @@
+ /*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VOODOO_OPENGL_H
+#define DOSBOX_VOODOO_OPENGL_H
+
+#include <stdlib.h>
+#include <math.h>
+#include <map>
+
+#include "dosbox.h"
+#include "cross.h"
+
+
+#if C_OPENGL
+
+#include "voodoo_vogl.h"
+#include "voodoo_types.h"
+
+extern UINT32 new_width;
+extern UINT32 new_height;
+
+/* texture data */
+struct ogl_texture_data {
+ GLuint texID;
+ bool enable;
+};
+
+/* triangle vertex map data */
+struct ogl_vertex_map_data {
+ float sw,tw,z,w;
+ float s,t;
+ float lodblend;
+};
+
+/* triangle vertex data */
+struct ogl_vertex_data {
+ float x,y,d,w,z;
+ float r,g,b,a;
+ float fogblend;
+ ogl_vertex_map_data m[2];
+};
+
+struct ogl_texmap {
+ bool valid_data;
+ bool valid_pal;
+ UINT32 format;
+ UINT32 current_id;
+
+ std::map<const UINT32, GLuint>* ids;
+};
+
+#endif
+
+bool voodoo_ogl_init(voodoo_state *v);
+void voodoo_ogl_leave(bool leavemode);
+void voodoo_ogl_shutdown(voodoo_state *v);
+
+void voodoo_ogl_set_window(voodoo_state *v);
+void voodoo_ogl_swap_buffer(void);
+void voodoo_ogl_vblank_flush(void);
+
+void voodoo_ogl_update_dimensions(void);
+void voodoo_ogl_clear(void);
+void voodoo_ogl_fastfill(void);
+
+void voodoo_ogl_clip_window(voodoo_state *v);
+void voodoo_ogl_texture_clear(UINT32 texbase, int TMU);
+void voodoo_ogl_invalidate_paltex(void);
+
+void voodoo_ogl_draw_pixel(int x, int y, bool has_rgb, bool has_alpha, int r, int g, int b, int a);
+void voodoo_ogl_draw_z(int x, int y, int z);
+void voodoo_ogl_draw_pixel_pipeline(int x, int y, int r, int g, int b);
+UINT32 voodoo_ogl_read_pixel(int x, int y);
+
+void voodoo_ogl_draw_triangle(poly_extra_data *extra);
+
+#endif
diff --git a/src/hardware/voodoo_types.h b/src/hardware/voodoo_types.h
new file mode 100644
index 000000000..1875cf22b
--- /dev/null
+++ b/src/hardware/voodoo_types.h
@@ -0,0 +1,381 @@
+/***************************************************************************/
+/* Portion of this software comes with the following license: */
+/***************************************************************************/
+/*
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+
+#ifndef DOSBOX_VOODOO_TYPES_H
+#define DOSBOX_VOODOO_TYPES_H
+
+#include "cross.h"
+
+#ifdef _MSC_VER
+ #define INLINE __forceinline
+#elif defined(__GNUC__)
+ #define INLINE inline __attribute__((always_inline))
+#elif defined(__WATCOMC__)
+ #define INLINE __inline
+#else
+ #define INLINE
+#endif
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+
+/* 8-bit values */
+typedef uint8_t UINT8;
+typedef int8_t INT8;
+
+/* 16-bit values */
+typedef uint16_t UINT16;
+typedef int16_t INT16;
+
+/* 32-bit values */
+#ifndef _WINDOWS_
+typedef uint32_t UINT32;
+typedef int32_t INT32;
+#endif
+
+/* 64-bit values */
+#ifndef _WINDOWS_
+typedef uint64_t UINT64;
+typedef int64_t INT64;
+#endif
+
+/* core components of the attotime structure */
+typedef INT64 attoseconds_t;
+typedef INT32 seconds_t;
+
+
+/* the attotime structure itself */
+typedef struct _attotime attotime;
+struct _attotime
+{
+ seconds_t seconds;
+ attoseconds_t attoseconds;
+};
+
+#define ATTOSECONDS_PER_SECOND_SQRT ((attoseconds_t)1000000000)
+#define ATTOSECONDS_PER_SECOND (ATTOSECONDS_PER_SECOND_SQRT * ATTOSECONDS_PER_SECOND_SQRT)
+
+/* convert between hertz (as a double) and attoseconds */
+#define ATTOSECONDS_TO_HZ(x) ((double)ATTOSECONDS_PER_SECOND / (double)(x))
+#define HZ_TO_ATTOSECONDS(x) ((attoseconds_t)(ATTOSECONDS_PER_SECOND / (x)))
+
+
+/* poly_param_extent describes information for a single parameter in an extent */
+typedef struct _poly_param_extent poly_param_extent;
+struct _poly_param_extent
+{
+ float start; /* parameter value at starting X,Y */
+ float dpdx; /* dp/dx relative to starting X */
+};
+
+
+#define MAX_VERTEX_PARAMS 6
+
+/* poly_extent describes start/end points for a scanline, along with per-scanline parameters */
+typedef struct _poly_extent poly_extent;
+struct _poly_extent
+{
+ INT32 startx; /* starting X coordinate (inclusive) */
+ INT32 stopx; /* ending X coordinate (exclusive) */
+ poly_param_extent param[MAX_VERTEX_PARAMS]; /* starting and dx values for each parameter */
+};
+
+
+/* an rgb_t is a single combined R,G,B (and optionally alpha) value */
+typedef UINT32 rgb_t;
+
+/* an rgb15_t is a single combined 15-bit R,G,B value */
+typedef UINT16 rgb15_t;
+
+/* macros to assemble rgb_t values */
+#define MAKE_ARGB(a,r,g,b) ((((rgb_t)(a) & 0xff) << 24) | (((rgb_t)(r) & 0xff) << 16) | (((rgb_t)(g) & 0xff) << 8) | ((rgb_t)(b) & 0xff))
+#define MAKE_RGB(r,g,b) (MAKE_ARGB(255,r,g,b))
+
+/* macros to extract components from rgb_t values */
+#define RGB_ALPHA(rgb) (((rgb) >> 24) & 0xff)
+#define RGB_RED(rgb) (((rgb) >> 16) & 0xff)
+#define RGB_GREEN(rgb) (((rgb) >> 8) & 0xff)
+#define RGB_BLUE(rgb) ((rgb) & 0xff)
+
+/* common colors */
+#define RGB_BLACK (MAKE_ARGB(255,0,0,0))
+#define RGB_WHITE (MAKE_ARGB(255,255,255,255))
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ rgb_to_rgb15 - convert an RGB triplet to
+ a 15-bit OSD-specified RGB value
+-------------------------------------------------*/
+
+INLINE rgb15_t rgb_to_rgb15(rgb_t rgb)
+{
+ return ((RGB_RED(rgb) >> 3) << 10) | ((RGB_GREEN(rgb) >> 3) << 5) | ((RGB_BLUE(rgb) >> 3) << 0);
+}
+
+
+/*-------------------------------------------------
+ rgb_clamp - clamp an RGB component to 0-255
+-------------------------------------------------*/
+
+INLINE UINT8 rgb_clamp(INT32 value)
+{
+ if (value < 0)
+ return 0;
+ if (value > 255)
+ return 255;
+ return (UINT8)value;
+}
+
+
+/*-------------------------------------------------
+ pal1bit - convert a 1-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal1bit(UINT8 bits)
+{
+ return (bits & 1) ? 0xff : 0x00;
+}
+
+
+/*-------------------------------------------------
+ pal2bit - convert a 2-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal2bit(UINT8 bits)
+{
+ bits &= 3;
+ return (bits << 6) | (bits << 4) | (bits << 2) | bits;
+}
+
+
+/*-------------------------------------------------
+ pal3bit - convert a 3-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal3bit(UINT8 bits)
+{
+ bits &= 7;
+ return (bits << 5) | (bits << 2) | (bits >> 1);
+}
+
+
+/*-------------------------------------------------
+ pal4bit - convert a 4-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal4bit(UINT8 bits)
+{
+ bits &= 0xf;
+ return (bits << 4) | bits;
+}
+
+
+/*-------------------------------------------------
+ pal5bit - convert a 5-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal5bit(UINT8 bits)
+{
+ bits &= 0x1f;
+ return (bits << 3) | (bits >> 2);
+}
+
+
+/*-------------------------------------------------
+ pal6bit - convert a 6-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal6bit(UINT8 bits)
+{
+ bits &= 0x3f;
+ return (bits << 2) | (bits >> 4);
+}
+
+
+/*-------------------------------------------------
+ pal7bit - convert a 7-bit value to 8 bits
+-------------------------------------------------*/
+
+INLINE UINT8 pal7bit(UINT8 bits)
+{
+ bits &= 0x7f;
+ return (bits << 1) | (bits >> 6);
+}
+
+
+/* rectangles describe a bitmap portion */
+typedef struct _rectangle rectangle;
+struct _rectangle
+{
+ int min_x; /* minimum X, or left coordinate */
+ int max_x; /* maximum X, or right coordinate (inclusive) */
+ int min_y; /* minimum Y, or top coordinate */
+ int max_y; /* maximum Y, or bottom coordinate (inclusive) */
+};
+
+/* Standard MIN/MAX macros */
+#ifndef MIN
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+
+inline float u2f(UINT32 v)
+{
+ union {
+ float ff;
+ UINT32 vv;
+ } u;
+ u.vv = v;
+ return u.ff;
+}
+
+
+/* Macros for normalizing data into big or little endian formats */
+#define FLIPENDIAN_INT16(x) (((((UINT16) (x)) >> 8) | ((x) << 8)) & 0xffff)
+#define FLIPENDIAN_INT32(x) ((((UINT32) (x)) << 24) | (((UINT32) (x)) >> 24) | \
+ (( ((UINT32) (x)) & 0x0000ff00) << 8) | (( ((UINT32) (x)) & 0x00ff0000) >> 8))
+#define FLIPENDIAN_INT64(x) \
+ ( \
+ (((((UINT64) (x)) >> 56) & ((UINT64) 0xFF)) << 0) | \
+ (((((UINT64) (x)) >> 48) & ((UINT64) 0xFF)) << 8) | \
+ (((((UINT64) (x)) >> 40) & ((UINT64) 0xFF)) << 16) | \
+ (((((UINT64) (x)) >> 32) & ((UINT64) 0xFF)) << 24) | \
+ (((((UINT64) (x)) >> 24) & ((UINT64) 0xFF)) << 32) | \
+ (((((UINT64) (x)) >> 16) & ((UINT64) 0xFF)) << 40) | \
+ (((((UINT64) (x)) >> 8) & ((UINT64) 0xFF)) << 48) | \
+ (((((UINT64) (x)) >> 0) & ((UINT64) 0xFF)) << 56) \
+ )
+
+#define ACCESSING_BITS_0_15 ((mem_mask & 0x0000ffff) != 0)
+#define ACCESSING_BITS_16_31 ((mem_mask & 0xffff0000) != 0)
+
+// constants for expression endianness
+enum endianness_t
+{
+ ENDIANNESS_LITTLE,
+ ENDIANNESS_BIG
+};
+const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_LITTLE;
+#define ENDIAN_VALUE_LE_BE(endian,leval,beval) (((endian) == ENDIANNESS_LITTLE) ? (leval) : (beval))
+#define NATIVE_ENDIAN_VALUE_LE_BE(leval,beval) ENDIAN_VALUE_LE_BE(ENDIANNESS_NATIVE, leval, beval)
+#define BYTE4_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,3))
+
+#define BYTE_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,1))
+
+#define profiler_mark_start(x) do { } while (0)
+#define profiler_mark_end() do { } while (0)
+
+
+/* Highly useful macro for compile-time knowledge of an array size */
+#define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
+
+INLINE INT32 mul_32x32_shift(INT32 a, INT32 b, INT8 shift)
+{
+ INT32 result;
+
+#if defined _MSC_VER && defined(_M_IX86)
+ __asm
+ {
+ mov eax,a
+ imul b
+ mov cl,shift
+ shrd eax,edx,cl
+ mov result,eax
+ }
+#else
+ INT64 tmp = (INT64)a * (INT64)b;
+ tmp >>= shift;
+ result = (INT32)tmp;
+#endif
+
+ return result;
+}
+
+
+typedef void (*poly_draw_scanline_func)(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata);
+
+INLINE rgb_t rgba_bilinear_filter(rgb_t rgb00, rgb_t rgb01, rgb_t rgb10, rgb_t rgb11, UINT8 u, UINT8 v)
+{
+ UINT32 ag0, ag1, rb0, rb1;
+
+ rb0 = (rgb00 & 0x00ff00ff) + ((((rgb01 & 0x00ff00ff) - (rgb00 & 0x00ff00ff)) * u) >> 8);
+ rb1 = (rgb10 & 0x00ff00ff) + ((((rgb11 & 0x00ff00ff) - (rgb10 & 0x00ff00ff)) * u) >> 8);
+ rgb00 >>= 8;
+ rgb01 >>= 8;
+ rgb10 >>= 8;
+ rgb11 >>= 8;
+ ag0 = (rgb00 & 0x00ff00ff) + ((((rgb01 & 0x00ff00ff) - (rgb00 & 0x00ff00ff)) * u) >> 8);
+ ag1 = (rgb10 & 0x00ff00ff) + ((((rgb11 & 0x00ff00ff) - (rgb10 & 0x00ff00ff)) * u) >> 8);
+
+ rb0 = (rb0 & 0x00ff00ff) + ((((rb1 & 0x00ff00ff) - (rb0 & 0x00ff00ff)) * v) >> 8);
+ ag0 = (ag0 & 0x00ff00ff) + ((((ag1 & 0x00ff00ff) - (ag0 & 0x00ff00ff)) * v) >> 8);
+
+ return ((ag0 << 8) & 0xff00ff00) | (rb0 & 0x00ff00ff);
+}
+
+typedef struct _poly_vertex poly_vertex;
+struct _poly_vertex
+{
+ float x; /* X coordinate */
+ float y; /* Y coordinate */
+ float p[MAX_VERTEX_PARAMS]; /* interpolated parameter values */
+};
+
+
+typedef struct _tri_extent tri_extent;
+struct _tri_extent
+{
+ INT16 startx; /* starting X coordinate (inclusive) */
+ INT16 stopx; /* ending X coordinate (exclusive) */
+};
+
+/* tri_work_unit is a triangle-specific work-unit */
+typedef struct _tri_work_unit tri_work_unit;
+struct _tri_work_unit
+{
+ tri_extent extent[8]; /* array of scanline extents */
+};
+
+#endif
diff --git a/src/hardware/voodoo_vogl.cpp b/src/hardware/voodoo_vogl.cpp
new file mode 100644
index 000000000..030bd698a
--- /dev/null
+++ b/src/hardware/voodoo_vogl.cpp
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2002-2021 The DOSBox Team
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+// Tell Mac OS X to shut up about deprecated OpenGL calls
+#ifndef GL_SILENCE_DEPRECATION
+#define GL_SILENCE_DEPRECATION
+#endif
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include <map>
+
+#include "dosbox.h"
+#include "dos_inc.h"
+#include "logging.h"
+
+#if C_OPENGL
+
+#include "voodoo_types.h"
+#include "voodoo_vogl.h"
+
+/* NTS: This causes errors in Linux because MesaGL already defines these */
+#ifdef WIN32
+PFNGLMULTITEXCOORD4FVARBPROC __glMultiTexCoord4fvARB = NULL;
+PFNGLMULTITEXCOORD4FARBPROC __glMultiTexCoord4fARB = NULL;
+PFNGLACTIVETEXTUREARBPROC __glActiveTextureARB = NULL;
+#endif
+
+PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB = NULL;
+PFNGLSHADERSOURCEARBPROC glShaderSourceARB = NULL;
+PFNGLCOMPILESHADERARBPROC glCompileShaderARB = NULL;
+PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB = NULL;
+PFNGLATTACHOBJECTARBPROC glAttachObjectARB = NULL;
+PFNGLLINKPROGRAMARBPROC glLinkProgramARB = NULL;
+PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB = NULL;
+PFNGLUNIFORM1IARBPROC glUniform1iARB = NULL;
+PFNGLUNIFORM1FARBPROC glUniform1fARB = NULL;
+PFNGLUNIFORM2FARBPROC glUniform2fARB = NULL;
+PFNGLUNIFORM3FARBPROC glUniform3fARB = NULL;
+PFNGLUNIFORM4FARBPROC glUniform4fARB = NULL;
+PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB = NULL;
+PFNGLDETACHOBJECTARBPROC glDetachObjectARB = NULL;
+PFNGLDELETEOBJECTARBPROC glDeleteObjectARB = NULL;
+PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB = NULL;
+PFNGLGETINFOLOGARBPROC glGetInfoLogARB = NULL;
+PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT = NULL;
+PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = NULL;
+PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB = NULL;
+PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB = NULL;
+
+
+static int32_t opengl_version = -1;
+
+static bool has_shaders = false;
+static bool has_stencil = false;
+static bool has_alpha = false;
+
+
+static INT32 current_begin_mode = -1;
+
+static int32_t current_depth_mode = -1;
+static int32_t current_depth_func = -1;
+
+static int32_t current_alpha_enabled = -1;
+static int32_t current_src_rgb_fac = -1;
+static int32_t current_dst_rgb_fac = -1;
+static int32_t current_src_alpha_fac = -1;
+static int32_t current_dst_alpha_fac = -1;
+
+static bool depth_masked = false;
+static bool color_masked = false;
+static bool alpha_masked = false;
+
+// buffer read/write defaults are back-buffer for double buffered contexts
+static bool draw_to_front_buffer = false;
+static bool read_from_front_buffer = false;
+
+
+void VOGL_Reset(void) {
+ opengl_version = -1;
+ has_shaders = false;
+ has_stencil = false;
+ has_alpha = false;
+
+ current_depth_mode=-1;
+ current_depth_func=-1;
+
+ current_alpha_enabled=-1;
+ current_src_rgb_fac=-1;
+ current_dst_rgb_fac=-1;
+ current_src_alpha_fac=-1;
+ current_dst_alpha_fac=-1;
+
+ depth_masked = false;
+ color_masked = false;
+ alpha_masked = false;
+
+ draw_to_front_buffer = false;
+ read_from_front_buffer = false;
+}
+
+void VOGL_InitVersion(void) {
+ opengl_version = -1;
+
+ char gl_verstr[16];
+ const GLubyte* gl_verstr_ub = glGetString(GL_VERSION);
+ strncpy(gl_verstr, (const char*)gl_verstr_ub, 16);
+ gl_verstr[15] = 0;
+ char* gl_ver_minor = strchr(gl_verstr, '.');
+ if (gl_ver_minor != NULL) {
+ gl_ver_minor++;
+ char* skip = strchr(gl_ver_minor, '.');
+ if (skip != NULL) *skip = 0;
+ }
+
+ int ogl_ver = 100;
+ if (gl_verstr[0] != 0) {
+ int major = 1;
+ int minor = 0;
+ if (strchr(gl_verstr, '.') != NULL) {
+ if (sscanf(gl_verstr,"%d.%d", &major, &minor) != 2) major = 0;
+ } else {
+ if (sscanf(gl_verstr, "%d", &major) != 1) major = 0;
+ }
+ if (major > 0) {
+ ogl_ver = major*100;
+ if (minor >= 0) {
+ if (minor < 10) ogl_ver += minor*10;
+ else ogl_ver += minor;
+ }
+ }
+ }
+
+ if (ogl_ver > 0) opengl_version = ogl_ver;
+}
+
+void VOGL_ClearShaderFunctions(void) {
+ glShaderSourceARB = NULL;
+ glCompileShaderARB = NULL;
+ glCreateProgramObjectARB = NULL;
+ glAttachObjectARB = NULL;
+ glLinkProgramARB = NULL;
+ glUseProgramObjectARB = NULL;
+ glUniform1iARB = NULL;
+ glUniform1fARB = NULL;
+ glUniform2fARB = NULL;
+ glUniform3fARB = NULL;
+ glUniform4fARB = NULL;
+ glGetUniformLocationARB = NULL;
+ glDetachObjectARB = NULL;
+ glDeleteObjectARB = NULL;
+ glGetObjectParameterivARB = NULL;
+ glGetInfoLogARB = NULL;
+}
+
+bool VOGL_Initialize(void) {
+ VOGL_ClearShaderFunctions();
+
+ VOGL_InitVersion();
+
+#ifdef WIN32
+ __glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glActiveTextureARB"));
+ if (!__glActiveTextureARB) {
+ LOG_MSG("opengl: glActiveTextureARB extension not supported");
+ return false;
+ }
+
+ __glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glMultiTexCoord4fARB"));
+ if (!__glMultiTexCoord4fARB) {
+ LOG_MSG("opengl: glMultiTexCoord4fARB extension not supported");
+ return false;
+ }
+
+ __glMultiTexCoord4fvARB = (PFNGLMULTITEXCOORD4FVARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glMultiTexCoord4fvARB"));
+ if (!__glMultiTexCoord4fvARB) {
+ LOG_MSG("opengl: glMultiTexCoord4fvARB extension not supported");
+ return false;
+ }
+#endif
+
+ glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC)((uintptr_t)SDL_GL_GetProcAddress("glBlendFuncSeparateEXT"));
+ if (!glBlendFuncSeparateEXT) {
+ LOG_MSG("opengl: glBlendFuncSeparateEXT extension not supported");
+ return false;
+ }
+
+ glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)((uintptr_t)SDL_GL_GetProcAddress("glGenerateMipmapEXT"));
+ if (!glGenerateMipmapEXT) {
+ LOG_MSG("opengl: glGenerateMipmapEXT extension not supported");
+ return false;
+ }
+
+ if (VOGL_CheckFeature(VOGL_ATLEAST_V20)) {
+ const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
+ if (strstr(extensions, "GL_ARB_shader_objects") && strstr(extensions, "GL_ARB_vertex_shader") &&
+ strstr(extensions, "GL_ARB_fragment_shader")) {
+
+ glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glCreateShaderObjectARB"));
+ if (!glCreateShaderObjectARB) {
+ LOG_MSG("opengl: shader extensions not supported. Using fixed pipeline");
+ } else {
+ glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glShaderSourceARB"));
+ if (!glShaderSourceARB) LOG_MSG("opengl: glShaderSourceARB extension not supported");
+
+ glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glCompileShaderARB"));
+ if (!glCompileShaderARB) LOG_MSG("opengl: glCompileShaderARB extension not supported");
+
+ glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glCreateProgramObjectARB"));
+ if (!glCreateProgramObjectARB) LOG_MSG("opengl: glCreateProgramObjectARB extension not supported");
+
+ glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glAttachObjectARB"));
+ if (!glAttachObjectARB) LOG_MSG("opengl: glAttachObjectARB extension not supported");
+
+ glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glLinkProgramARB"));
+ if (!glLinkProgramARB) LOG_MSG("opengl: glLinkProgramARB extension not supported");
+
+ glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glUseProgramObjectARB"));
+ if (!glUseProgramObjectARB) LOG_MSG("opengl: glUseProgramObjectARB extension not supported");
+
+ glUniform1iARB = (PFNGLUNIFORM1IARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glUniform1iARB"));
+ if (!glUniform1iARB) LOG_MSG("opengl: glUniform1iARB extension not supported");
+
+ glUniform1fARB = (PFNGLUNIFORM1FARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glUniform1fARB"));
+ if (!glUniform1fARB) LOG_MSG("opengl: glUniform1fARB extension not supported");
+
+ glUniform2fARB = (PFNGLUNIFORM2FARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glUniform2fARB"));
+ if (!glUniform2fARB) LOG_MSG("opengl: glUniform2fARB extension not supported");
+
+ glUniform3fARB = (PFNGLUNIFORM3FARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glUniform3fARB"));
+ if (!glUniform3fARB) LOG_MSG("opengl: glUniform3fARB extension not supported");
+
+ glUniform4fARB = (PFNGLUNIFORM4FARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glUniform4fARB"));
+ if (!glUniform4fARB) LOG_MSG("opengl: glUniform4fARB extension not supported");
+
+ glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glGetUniformLocationARB"));
+ if (!glGetUniformLocationARB) LOG_MSG("opengl: glGetUniformLocationARB extension not supported");
+
+ glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glDetachObjectARB"));
+ if (!glDetachObjectARB) LOG_MSG("opengl: glDetachObjectARB extension not supported");
+
+ glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glDeleteObjectARB"));
+ if (!glDeleteObjectARB) LOG_MSG("opengl: glDeleteObjectARB extension not supported");
+
+ glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glGetObjectParameterivARB"));
+ if (!glGetObjectParameterivARB) LOG_MSG("opengl: glGetObjectParameterivARB extension not supported");
+
+ glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glGetInfoLogARB"));
+ if (!glGetInfoLogARB) LOG_MSG("opengl: glGetInfoLogARB extension not supported");
+
+ glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glGetAttribLocationARB"));
+ if (!glGetAttribLocationARB) LOG_MSG("opengl: glGetAttribLocationARB extension not supported");
+
+ glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC)((uintptr_t)SDL_GL_GetProcAddress("glVertexAttrib1fARB"));
+ if (!glVertexAttrib1fARB) LOG_MSG("opengl: glVertexAttrib1fARB extension not supported");
+
+ if (glShaderSourceARB && glCompileShaderARB && glCreateProgramObjectARB &&
+ glAttachObjectARB && glLinkProgramARB && glUseProgramObjectARB &&
+ glUniform1iARB && glUniform1fARB && glUniform2fARB && glUniform3fARB &&
+ glUniform4fARB && glGetUniformLocationARB && glDetachObjectARB &&
+ glDeleteObjectARB && glGetObjectParameterivARB && glGetInfoLogARB) {
+ VOGL_FlagFeature(VOGL_HAS_SHADERS);
+// LOG_MSG("opengl: shader functionality enabled");
+ } else {
+ VOGL_ClearShaderFunctions();
+ }
+ }
+ }
+ }
+
+ LOG_MSG("opengl: I am able to use OpenGL to emulate Voodoo graphics");
+ return true;
+}
+
+
+bool VOGL_CheckFeature(uint32_t feat) {
+ switch (feat) {
+ case VOGL_ATLEAST_V20:
+ if (opengl_version >= 200) return true;
+ break;
+ case VOGL_ATLEAST_V21:
+ if (opengl_version >= 210) return true;
+ break;
+ case VOGL_ATLEAST_V30:
+ if (opengl_version >= 300) return true;
+ break;
+ case VOGL_HAS_SHADERS:
+ if (has_shaders) return true;
+ break;
+ case VOGL_HAS_STENCIL_BUFFER:
+ if (has_stencil) return true;
+ break;
+ case VOGL_HAS_ALPHA_PLANE:
+ if (has_alpha) return true;
+ break;
+ default:
+ LOG_MSG("opengl: unknown feature queried: %x",feat);
+ break;
+ }
+
+ return false;
+}
+
+void VOGL_FlagFeature(uint32_t feat) {
+ switch (feat) {
+ case VOGL_HAS_SHADERS:
+ has_shaders = true;
+ break;
+ case VOGL_HAS_STENCIL_BUFFER:
+ has_stencil = true;
+ break;
+ case VOGL_HAS_ALPHA_PLANE:
+ has_alpha = true;
+ break;
+ default:
+ LOG_MSG("opengl: unknown feature: %x",feat);
+ break;
+ }
+}
+
+
+void VOGL_BeginMode(INT32 new_mode) {
+ if (current_begin_mode > -1) {
+ if (new_mode != current_begin_mode) {
+ glEnd();
+ if (new_mode > -1) glBegin((GLenum)new_mode);
+ current_begin_mode = new_mode;
+ }
+ } else {
+ if (new_mode > -1) {
+ glBegin((GLenum)new_mode);
+ current_begin_mode = new_mode;
+ }
+ }
+}
+
+void VOGL_ClearBeginMode(void) {
+ if (current_begin_mode > -1) {
+ glEnd();
+ current_begin_mode = -1;
+ }
+}
+
+
+void VOGL_SetDepthMode(int32_t mode, int32_t func) {
+ if (current_depth_mode!=mode) {
+ if (mode!=0) {
+ VOGL_ClearBeginMode();
+ glEnable(GL_DEPTH_TEST);
+ current_depth_mode=1;
+ if (current_depth_func!=func) {
+ glDepthFunc((GLenum)(GL_NEVER+func));
+ current_depth_func=func;
+ }
+ } else {
+ VOGL_ClearBeginMode();
+ glDisable(GL_DEPTH_TEST);
+ current_depth_mode=0;
+ }
+ } else {
+ if ((mode!=0) && (current_depth_func!=func)) {
+ VOGL_ClearBeginMode();
+ glDepthFunc((GLenum)(GL_NEVER+func));
+ current_depth_func=func;
+ }
+ }
+}
+
+
+void VOGL_SetAlphaMode(int32_t enabled_mode,GLuint src_rgb_fac,GLuint dst_rgb_fac,
+ GLuint src_alpha_fac,GLuint dst_alpha_fac) {
+ if (current_alpha_enabled!=enabled_mode) {
+ VOGL_ClearBeginMode();
+ if (enabled_mode!=0) {
+ glEnable(GL_BLEND);
+ current_alpha_enabled=1;
+ if ((current_src_rgb_fac!=(int32_t)src_rgb_fac) || (current_dst_rgb_fac!=(int32_t)dst_rgb_fac) ||
+ (current_src_alpha_fac!=(int32_t)src_alpha_fac) || (current_dst_alpha_fac!=(int32_t)dst_alpha_fac)) {
+ glBlendFuncSeparateEXT(src_rgb_fac, dst_rgb_fac, src_alpha_fac, dst_alpha_fac);
+ current_src_rgb_fac=(int32_t)src_rgb_fac;
+ current_dst_rgb_fac=(int32_t)dst_rgb_fac;
+ current_src_alpha_fac=(int32_t)src_alpha_fac;
+ current_dst_alpha_fac=(int32_t)dst_alpha_fac;
+ }
+ } else {
+ glDisable(GL_BLEND);
+ current_alpha_enabled=0;
+ }
+ } else {
+ if (current_alpha_enabled!=0) {
+ if ((current_src_rgb_fac!=(int32_t)src_rgb_fac) || (current_dst_rgb_fac!=(int32_t)dst_rgb_fac) ||
+ (current_src_alpha_fac!=(int32_t)src_alpha_fac) || (current_dst_alpha_fac!=(int32_t)dst_alpha_fac)) {
+ VOGL_ClearBeginMode();
+ glBlendFuncSeparateEXT(src_rgb_fac, dst_rgb_fac, src_alpha_fac, dst_alpha_fac);
+ current_src_rgb_fac=(int32_t)src_rgb_fac;
+ current_dst_rgb_fac=(int32_t)dst_rgb_fac;
+ current_src_alpha_fac=(int32_t)src_alpha_fac;
+ current_dst_alpha_fac=(int32_t)dst_alpha_fac;
+ }
+ }
+ }
+}
+
+
+void VOGL_SetDepthMaskMode(bool masked) {
+ if (depth_masked!=masked) {
+ VOGL_ClearBeginMode();
+ if (masked) {
+ glDepthMask(GL_TRUE);
+ depth_masked=true;
+ } else {
+ glDepthMask(GL_FALSE);
+ depth_masked=false;
+ }
+ }
+}
+
+
+void VOGL_SetColorMaskMode(bool cmasked, bool amasked) {
+ if ((color_masked!=cmasked) || (alpha_masked!=amasked)) {
+ color_masked=cmasked;
+ alpha_masked=amasked;
+ GLboolean cm = (color_masked ? GL_TRUE : GL_FALSE);
+ GLboolean am = (alpha_masked ? GL_TRUE : GL_FALSE);
+ glColorMask(cm,cm,cm,am);
+ }
+}
+
+
+void VOGL_SetDrawMode(bool front_draw) {
+ if (draw_to_front_buffer!=front_draw) {
+ VOGL_ClearBeginMode();
+ if (front_draw) glDrawBuffer(GL_FRONT);
+ else glDrawBuffer(GL_BACK);
+ draw_to_front_buffer=front_draw;
+ }
+}
+
+
+void VOGL_SetReadMode(bool front_read) {
+ VOGL_ClearBeginMode();
+
+ if (read_from_front_buffer!=front_read) {
+ if (front_read) glReadBuffer(GL_FRONT);
+ else glReadBuffer(GL_BACK);
+ read_from_front_buffer=front_read;
+ }
+}
+
+#endif
diff --git a/src/hardware/voodoo_vogl.h b/src/hardware/voodoo_vogl.h
new file mode 100644
index 000000000..8b39fb3d0
--- /dev/null
+++ b/src/hardware/voodoo_vogl.h
@@ -0,0 +1,129 @@
+ /*
+ * Copyright (C) 2002-2011 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VOODOO_VOGL_H
+#define DOSBOX_VOODOO_VOGL_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+#include "SDL.h"
+#include "SDL_opengl.h"
+
+/* opengl extensions */
+#ifdef WIN32
+extern PFNGLACTIVETEXTUREARBPROC __glActiveTextureARB;
+extern PFNGLMULTITEXCOORD4FARBPROC __glMultiTexCoord4fARB;
+extern PFNGLMULTITEXCOORD4FVARBPROC __glMultiTexCoord4fvARB;
+# define glMultiTexCoord4fv __glMultiTexCoord4fvARB
+# define glActiveTexture __glActiveTextureARB
+#endif
+
+extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
+extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
+extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
+extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
+extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
+extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
+extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
+extern PFNGLUNIFORM1IARBPROC glUniform1iARB;
+extern PFNGLUNIFORM1FARBPROC glUniform1fARB;
+extern PFNGLUNIFORM2FARBPROC glUniform2fARB;
+extern PFNGLUNIFORM3FARBPROC glUniform3fARB;
+extern PFNGLUNIFORM4FARBPROC glUniform4fARB;
+extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
+extern PFNGLDETACHOBJECTARBPROC glDetachObjectARB;
+extern PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
+extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
+extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
+extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT;
+extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT;
+extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB;
+extern PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB;
+
+
+#define VOGL_ATLEAST_V20 0x00000001
+#define VOGL_ATLEAST_V21 0x00000002
+#define VOGL_ATLEAST_V30 0x00000004
+#define VOGL_HAS_SHADERS 0x00000010
+#define VOGL_HAS_STENCIL_BUFFER 0x00000100
+#define VOGL_HAS_ALPHA_PLANE 0x00000200
+
+
+static const GLuint ogl_sfactor[16] = {
+ GL_ZERO,
+ GL_SRC_ALPHA,
+ GL_DST_COLOR,
+ GL_DST_ALPHA,
+ GL_ONE,
+ GL_ONE_MINUS_SRC_ALPHA,
+ GL_ONE_MINUS_DST_COLOR,
+ GL_ONE_MINUS_DST_ALPHA,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_SRC_ALPHA_SATURATE
+};
+
+static const GLuint ogl_dfactor[16] = {
+ GL_ZERO,
+ GL_SRC_ALPHA,
+ GL_SRC_COLOR,
+ GL_DST_ALPHA,
+ GL_ONE,
+ GL_ONE_MINUS_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_COLOR,
+ GL_ONE_MINUS_DST_ALPHA,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_ZERO,
+ GL_SRC_COLOR /* A_COLORBEFOREFOG */
+};
+
+
+void VOGL_Reset(void);
+
+bool VOGL_Initialize(void);
+
+bool VOGL_CheckFeature(uint32_t feat);
+void VOGL_FlagFeature(uint32_t feat);
+
+void VOGL_BeginMode(INT32 new_mode);
+void VOGL_ClearBeginMode(void);
+
+void VOGL_SetDepthMode(int32_t mode, int32_t func);
+void VOGL_SetAlphaMode(int32_t enabled_mode,GLuint src_rgb_fac,GLuint dst_rgb_fac,
+ GLuint src_alpha_fac,GLuint dst_alpha_fac);
+
+void VOGL_SetDepthMaskMode(bool masked);
+void VOGL_SetColorMaskMode(bool cmasked, bool amasked);
+
+void VOGL_SetDrawMode(bool front_draw);
+void VOGL_SetReadMode(bool front_read);
+
+#endif
diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp
index f1313f4f2..39775d285 100644
--- a/src/ints/bios_disk.cpp
+++ b/src/ints/bios_disk.cpp
@@ -164,10 +164,9 @@ uint8_t imageDisk::Read_Sector(uint32_t head,uint32_t cylinder,uint32_t sector,v
uint8_t imageDisk::Read_AbsoluteSector(uint32_t sectnum, void *data)
{
- const uint32_t bytenum = sectnum * sector_size;
-
+ const uint64_t bytenum = sectnum * sector_size;
if (last_action == WRITE || bytenum != current_fpos) {
- if (fseek(diskimg, bytenum, SEEK_SET) != 0) {
+ if (fseeko64(diskimg, bytenum, SEEK_SET) != 0) {
LOG_ERR("BIOSDISK: Could not seek to sector %u in file '%s': %s",
sectnum, diskname, strerror(errno));
return 0xff;
@@ -190,16 +189,16 @@ uint8_t imageDisk::Write_Sector(uint32_t head,uint32_t cylinder,uint32_t sector,
uint8_t imageDisk::Write_AbsoluteSector(uint32_t sectnum, void *data) {
- uint32_t bytenum;
+ uint64_t bytenum;
- bytenum = sectnum * sector_size;
+ bytenum = (uint64_t)sectnum * sector_size;
//LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);
if (last_action == READ || bytenum != current_fpos) {
- if (fseek(diskimg, bytenum, SEEK_SET) != 0) {
- LOG_ERR("BIOSDISK: Could not seek to byte %u in file '%s': %s",
- bytenum, diskname, strerror(errno));
+ if (fseeko64(diskimg, bytenum, SEEK_SET) != 0) {
+ LOG_ERR("BIOSDISK: Could not seek to byte %llu in file '%s': %s",
+ (long long unsigned int)bytenum, diskname, strerror(errno));
return 0xff;
}
}
@@ -318,19 +317,49 @@ static bool driveInactive(uint8_t driveNum) {
return false;
}
+static struct {
+ uint8_t sz;
+ uint8_t res;
+ uint16_t num;
+ uint16_t off;
+ uint16_t seg;
+ uint32_t sector;
+} dap;
+
+static void readDAP(uint16_t seg, uint16_t off) {
+ dap.sz = real_readb(seg,off++);
+ dap.res = real_readb(seg,off++);
+ dap.num = real_readw(seg,off); off += 2;
+ dap.off = real_readw(seg,off); off += 2;
+ dap.seg = real_readw(seg,off); off += 2;
+
+ /* Although sector size is 64-bit, 32-bit 2TB limit should be more than enough */
+ dap.sector = real_readd(seg,off); off +=4;
+
+ if (real_readd(seg,off)) {
+ E_Exit("INT13: 64-bit sector addressing not supported");
+ }
+}
+
template<typename T, size_t N>
static bool has_image(const std::array<T, N> &arr) {
auto to_bool = [](const T &x) { return bool(x); };
return std::any_of(std::begin(arr), std::end(arr), to_bool);
}
+void IDE_ResetDiskByBIOS(unsigned char disk);
+void IDE_EmuINT13DiskReadByBIOS_LBA(uint8_t disk, uint64_t lba);
+void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect);
+bool IDE_GetPhysGeometry(unsigned char disk,uint32_t &heads,uint32_t &cyl,uint32_t &sect,uint32_t &size);
static Bitu INT13_DiskHandler(void) {
uint16_t segat, bufptr;
uint8_t sectbuf[512];
uint8_t drivenum;
- Bitu t;
+ Bitu i, t;
last_drive = reg_dl;
drivenum = GetDosDriveNumber(reg_dl);
+ if (!drivenum && imageDiskList[2] && (reg_ah == 0x41 || reg_ah == 0x42)) drivenum = 2;
+ std::fill_n(sectbuf, 512, 0);
const bool any_images = has_image(imageDiskList);
// unconditionally enable the interrupt flag
@@ -360,6 +389,7 @@ static Bitu INT13_DiskHandler(void) {
return CBRET_NONE;
}
if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
+ if (reg_dl >= 0x80) IDE_ResetDiskByBIOS(reg_dl);
last_status = 0x00;
CALLBACK_SCF(false);
}
@@ -412,6 +442,8 @@ static Bitu INT13_DiskHandler(void) {
bufptr = reg_bx;
for (Bitu i = 0; i < reg_al; i++) {
last_status = imageDiskList[drivenum]->Read_Sector((uint32_t)reg_dh, (uint32_t)(reg_ch | ((reg_cl & 0xc0)<< 2)), (uint32_t)((reg_cl & 63)+i), sectbuf);
+ /* IDE emulation: simulate change of IDE state that would occur on a real machine after INT 13h */
+ IDE_EmuINT13DiskReadByBIOS(reg_dl, (uint32_t)(reg_ch | ((reg_cl & 0xc0)<< 2)), (uint32_t)reg_dh, (uint32_t)((reg_cl & 63)+i));
if((last_status != 0x00) || (killRead)) {
LOG_MSG("Error in disk read");
killRead = false;
@@ -580,6 +612,126 @@ static Bitu INT13_DiskHandler(void) {
reg_ah = 0x00;
CALLBACK_SCF(false);
break;
+ case 0x41: /* Check Extensions Present */
+ if ((reg_bx == 0x55aa) && !(driveInactive(drivenum))) {
+ LOG_MSG("INT13: Check Extensions Present for drive: 0x%x", reg_dl);
+ reg_ah=0x1; /* 1.x extension supported */
+ reg_bx=0xaa55; /* Extensions installed */
+ reg_cx=0x1; /* Extended disk access functions (AH=42h-44h,47h,48h) supported */
+ CALLBACK_SCF(false);
+ break;
+ }
+ LOG_MSG("INT13: AH=41h, Function not supported 0x%x for drive: 0x%x", reg_bx, reg_dl);
+ CALLBACK_SCF(true);
+ break;
+ case 0x42: /* Extended Read Sectors From Drive */
+ /* Read Disk Address Packet */
+ readDAP(SegValue(ds),reg_si);
+
+ if (dap.num==0) {
+ reg_ah = 0x01;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ if (!any_images) {
+ // Inherit the Earth cdrom (uses it as disk test)
+ if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
+ reg_ah = 0;
+ CALLBACK_SCF(false);
+ return CBRET_NONE;
+ }
+ }
+ if (driveInactive(drivenum)) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+
+ segat = dap.seg;
+ bufptr = dap.off;
+ for(i=0;i<dap.num;i++) {
+ last_status = imageDiskList[drivenum]->Read_AbsoluteSector(dap.sector+(uint32_t)i, sectbuf);
+
+ IDE_EmuINT13DiskReadByBIOS_LBA(reg_dl,dap.sector+i);
+
+ if((last_status != 0x00) || (killRead)) {
+ LOG_MSG("Error in disk read");
+ killRead = false;
+ reg_ah = 0x04;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ for(t=0;t<512;t++) {
+ real_writeb(segat,bufptr,sectbuf[t]);
+ bufptr++;
+ }
+ }
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x43: /* Extended Write Sectors to Drive */
+ if(driveInactive(drivenum)) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+
+ /* Read Disk Address Packet */
+ readDAP(SegValue(ds),reg_si);
+ bufptr = dap.off;
+ for(i=0;i<dap.num;i++) {
+ for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
+ sectbuf[t] = real_readb(dap.seg,bufptr);
+ bufptr++;
+ }
+
+ last_status = imageDiskList[drivenum]->Write_AbsoluteSector(dap.sector+(uint32_t)i, &sectbuf[0]);
+ if(last_status != 0x00) {
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ }
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x48: { /* get drive parameters */
+ uint16_t bufsz;
+
+ if(driveInactive(drivenum)) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+
+ segat = SegValue(ds);
+ bufptr = reg_si;
+ bufsz = real_readw(segat,bufptr+0);
+ if (bufsz < 0x1A) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ if (bufsz > 0x1E) bufsz = 0x1E;
+ else bufsz = 0x1A;
+
+ tmpheads = tmpcyl = tmpsect = tmpsize = 0;
+ if (!IDE_GetPhysGeometry(drivenum,tmpheads,tmpcyl,tmpsect,tmpsize))
+ imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
+
+ real_writew(segat,bufptr+0x00,bufsz);
+ real_writew(segat,bufptr+0x02,0x0003); /* C/H/S valid, DMA boundary errors handled */
+ real_writed(segat,bufptr+0x04,tmpcyl);
+ real_writed(segat,bufptr+0x08,tmpheads);
+ real_writed(segat,bufptr+0x0C,tmpsect);
+ real_writed(segat,bufptr+0x10,tmpcyl*tmpheads*tmpsect);
+ real_writed(segat,bufptr+0x14,0);
+ real_writew(segat,bufptr+0x18,512);
+ if (bufsz >= 0x1E)
+ real_writed(segat,bufptr+0x1A,0xFFFFFFFF); /* no EDD information available */
+
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ } break;
default:
LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum);
reg_ah=0xff;
diff --git a/vs/dosbox.vcxproj b/vs/dosbox.vcxproj
index 70e5b568c..c8c5f993c 100644
--- a/vs/dosbox.vcxproj
+++ b/vs/dosbox.vcxproj
@@ -632,6 +632,11 @@ IF %ERRORLEVEL% LSS 8 SET ERRORLEVEL = 0</Command>
<ClCompile Include="..\src\hardware\vga_seq.cpp" />
<ClCompile Include="..\src\hardware\vga_tseng.cpp" />
<ClCompile Include="..\src\hardware\vga_xga.cpp" />
+ <ClCompile Include="..\src\hardware\voodoo.cpp" />
+ <ClCompile Include="..\src\hardware\voodoo_emu.cpp" />
+ <ClCompile Include="..\src\hardware\voodoo_interface.cpp" />
+ <ClCompile Include="..\src\hardware\voodoo_opengl.cpp" />
+ <ClCompile Include="..\src\hardware\voodoo_vogl.cpp" />
<ClCompile Include="..\src\ints\bios.cpp" />
<ClCompile Include="..\src\ints\bios_disk.cpp" />
<ClCompile Include="..\src\ints\bios_keyboard.cpp" />
diff --git a/vs/dosbox.vcxproj.filters b/vs/dosbox.vcxproj.filters
index f999c4834..f70373393 100644
--- a/vs/dosbox.vcxproj.filters
+++ b/vs/dosbox.vcxproj.filters
@@ -322,6 +322,21 @@
<ClCompile Include="..\src\hardware\vga_xga.cpp">
<Filter>src\hardware</Filter>
</ClCompile>
+ <ClCompile Include="..\src\hardware\voodoo.cpp">
+ <Filter>src\hardware</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\hardware\voodoo_emu.cpp">
+ <Filter>src\hardware</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\hardware\voodoo_interface.cpp">
+ <Filter>src\hardware</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\hardware\voodoo_opengl.cpp">
+ <Filter>src\hardware</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\hardware\voodoo_vogl.cpp">
+ <Filter>src\hardware</Filter>
+ </ClCompile>
<ClCompile Include="..\src\ints\bios.cpp">
<Filter>src\ints</Filter>
</ClCompile>