diff options
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, §, §size); *_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 §,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 §,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, §buf[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> |